/* Output variables, constants and external declarations, for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 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 "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;
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 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 BSS_SECTION_ASM_OP
#ifdef ASM_OUTPUT_ALIGNED_BSS
static void asm_output_aligned_bss (FILE *, tree, const char *,
unsigned HOST_WIDE_INT, 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
if (in_section != in_text)
{
in_section = in_text;
+ last_text_section = in_text;
fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
}
}
+/* 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
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);
}
}
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
set of flags for a section to have, so 0 does not mean that the section
has not been seen. */
-unsigned int
+static unsigned int
get_named_section_flags (const char *section)
{
struct in_named_entry **slot;
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 (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.
{
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 ("%J%D causes a section type conflict", decl, decl);
+ 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. */
&& 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
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;
/* 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 (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
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. */
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,
= 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 (tree decl, const char *asmspec)
+make_decl_rtl (tree decl)
{
const char *name = 0;
int reg_number;
rtx x;
/* Check that we are not being given an automatic variable. */
+ gcc_assert (TREE_CODE (decl) != PARM_DECL
+ && TREE_CODE (decl) != RESULT_DECL);
+
/* A weak alias has TREE_PUBLIC set but not the other bits. */
- if (TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL
- || (TREE_CODE (decl) == VAR_DECL
- && !TREE_STATIC (decl)
- && !TREE_PUBLIC (decl)
- && !DECL_EXTERNAL (decl)
- && !DECL_REGISTER (decl)))
- abort ();
+ gcc_assert (TREE_CODE (decl) != VAR_DECL
+ || TREE_STATIC (decl)
+ || TREE_PUBLIC (decl)
+ || DECL_EXTERNAL (decl)
+ || DECL_REGISTER (decl));
+
/* And that we were not given a type or a label. */
- else if (TREE_CODE (decl) == TYPE_DECL
- || TREE_CODE (decl) == LABEL_DECL)
- abort ();
+ gcc_assert (TREE_CODE (decl) != TYPE_DECL
+ && TREE_CODE (decl) != LABEL_DECL);
/* For a duplicate declaration, we can be called twice on the
same DECL node. Don't discard the RTL already made. */
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;
}
- 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);
- change_decl_assembler_name (decl, get_identifier (starred));
+ error ("register name not specified for %q+D", decl);
}
-
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (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 ("%Jregister name not specified for '%D'", decl, decl);
+ error ("register name not specified for %q+D", decl);
else if (reg_number < 0)
- error ("%Jinvalid register name for '%D'", decl, decl);
+ error ("invalid register name for %q+D", decl);
else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
- error ("%Jdata type of '%D' isn't suitable for a register",
- decl, decl);
+ error ("data type of %q+D isn%'t suitable for a register",
+ decl);
else if (! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl))))
- error ("%Jregister specified for '%D' isn't suitable for data type",
- decl, decl);
+ error ("register specified for %q+D isn%'t suitable for data type",
+ decl);
/* Now handle properly declared static register variables. */
else
{
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 ("%Jregister name given for non-register variable '%D'", decl, decl);
+ else if (name[0] == '*')
+ {
+#ifdef REGISTER_PREFIX
+ if (strlen (REGISTER_PREFIX) != 0)
+ {
+ reg_number = decode_reg_name (name);
+ if (reg_number >= 0 || reg_number == -3)
+ error ("register name given for non-register variable %q+D", decl);
+ }
+#endif
+ }
/* Specifying a section attribute on a variable forces it into a
non-.bss section, and thus it cannot be common. */
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.
void
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;
}
between 0 and MAX_INIT_PRIORITY. */
void
-default_stabs_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
+default_stabs_asm_out_destructor (rtx symbol ATTRIBUTE_UNUSED,
+ int priority ATTRIBUTE_UNUSED)
{
+#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO
/* Tell GNU LD that this is part of the static destructor set.
This will work for any system that uses stabs, most usefully
aout systems. */
- fprintf (asm_out_file, "%s\"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP);
- assemble_name (asm_out_file, XSTR (symbol, 0));
- fputc ('\n', asm_out_file);
+ dbxout_begin_simple_stabs ("___DTOR_LIST__", 22 /* N_SETT */);
+ dbxout_stab_value_label (XSTR (symbol, 0));
+#else
+ sorry ("global destructors not supported on this target");
+#endif
}
void
/* Likewise for global constructors. */
void
-default_stabs_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
+default_stabs_asm_out_constructor (rtx symbol ATTRIBUTE_UNUSED,
+ int priority ATTRIBUTE_UNUSED)
{
+#if defined DBX_DEBUGGING_INFO || defined XCOFF_DEBUGGING_INFO
/* Tell GNU LD that this is part of the static destructor set.
This will work for any system that uses stabs, most usefully
aout systems. */
- fprintf (asm_out_file, "%s\"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP);
- assemble_name (asm_out_file, XSTR (symbol, 0));
- fputc ('\n', asm_out_file);
+ dbxout_begin_simple_stabs ("___CTOR_LIST__", 22 /* N_SETT */);
+ dbxout_stab_value_label (XSTR (symbol, 0));
+#else
+ sorry ("global constructors not supported on this target");
+#endif
}
void
|| (DECL_COMMON (decl)
&& (DECL_INITIAL (decl) == 0
|| DECL_INITIAL (decl) == error_mark_node))))
- || GET_CODE (DECL_RTL (decl)) != MEM)
+ || !MEM_P (DECL_RTL (decl)))
return;
- /* We win when global object is found, but it is usefull to know about weak
+ /* We win when global object is found, but it is useful to know about weak
symbol as well so we can produce nicer unique names. */
if (DECL_WEAK (decl) || DECL_ONE_ONLY (decl))
type = &weak_global_object_name;
if (!*type)
{
const char *p;
- char *name;
+ const char *name;
rtx decl_rtl = DECL_RTL (decl);
- p = (* targetm.strip_name_encoding) (XSTR (XEXP (decl_rtl, 0), 0));
- name = xstrdup (p);
+ p = targetm.strip_name_encoding (XSTR (XEXP (decl_rtl, 0), 0));
+ name = ggc_strdup (p);
*type = name;
}
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);
maybe_assemble_visibility (decl);
}
+ if (DECL_PRESERVE_P (decl))
+ targetm.asm_out.mark_decl_preserved (fnname);
+
/* Do any machine/system dependent processing of the function name. */
#ifdef ASM_DECLARE_FUNCTION_NAME
ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl);
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. */
#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++)
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;
rtx decl_rtl;
if (lang_hooks.decls.prepare_assemble_variable)
- (*lang_hooks.decls.prepare_assemble_variable) (decl);
+ lang_hooks.decls.prepare_assemble_variable (decl);
last_assemble_variable_decl = 0;
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 ("%Jstorage size of `%D' isn't known", decl, decl);
+ error ("storage size of %q+D isn%'t known", decl);
TREE_ASM_WRITTEN (decl) = 1;
return;
}
if (! dont_output_data
&& ! host_integerp (DECL_SIZE_UNIT (decl), 1))
{
- error ("%Jsize of variable '%D' is too large", decl, decl);
+ error ("size of variable %q+D is too large", decl);
return;
}
/* Some object file formats have a maximum alignment which they support.
In particular, a.out format supports a maximum alignment of 4. */
-#ifndef MAX_OFILE_ALIGNMENT
-#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT
-#endif
if (align > MAX_OFILE_ALIGNMENT)
{
- warning ("%Jalignment of '%D' is greater than maximum object "
- "file alignment. Using %d", decl, decl,
+ 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 = compute_reloc_for_constant (DECL_INITIAL (decl));
- 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 ("%Jrequested alignment for '%D' is greater than "
- "implemented alignment of %d", decl, decl, 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),
}
}
+/* In unit-at-a-time mode, we delay assemble_external processing until
+ the compilation unit is finalized. This is the best we can do for
+ right now (i.e. stage 3 of GCC 4.0) - the right thing is to delay
+ it all the way to final. See PR 17982 for further discussion. */
+static GTY(()) tree pending_assemble_externals;
+
#ifdef ASM_OUTPUT_EXTERNAL
/* True if DECL is a function decl for which no out-of-line copy exists.
It is assumed that DECL's assembler name has been set. */
}
return false;
}
+
+/* Actually do the tests to determine if this is necessary, and invoke
+ ASM_OUTPUT_EXTERNAL. */
+static void
+assemble_external_real (tree decl)
+{
+ rtx rtl = DECL_RTL (decl);
+
+ if (MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
+ && !SYMBOL_REF_USED (XEXP (rtl, 0))
+ && !incorporeal_function_p (decl))
+ {
+ /* Some systems do require some output. */
+ SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
+ ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
+ }
+}
+#endif
+
+void
+process_pending_assemble_externals (void)
+{
+#ifdef ASM_OUTPUT_EXTERNAL
+ tree list;
+ for (list = pending_assemble_externals; list; list = TREE_CHAIN (list))
+ assemble_external_real (TREE_VALUE (list));
+
+ pending_assemble_externals = 0;
#endif
+}
/* Output something to declare an external symbol to the assembler.
(Most assemblers don't need this, so we normally output nothing.)
main body of this code is only rarely exercised. To provide some
testing, on all platforms, we make sure that the ASM_OUT_FILE is
open. If it's not, we should not be calling this function. */
- if (!asm_out_file)
- abort ();
+ gcc_assert (asm_out_file);
#ifdef ASM_OUTPUT_EXTERNAL
- if (DECL_P (decl) && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl))
- {
- rtx rtl = DECL_RTL (decl);
+ if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
+ return;
- if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
- && !SYMBOL_REF_USED (XEXP (rtl, 0))
- && !incorporeal_function_p (decl))
- {
- /* Some systems do require some output. */
- SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
- ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
- }
- }
+ if (flag_unit_at_a_time)
+ pending_assemble_externals = tree_cons (0, decl,
+ pending_assemble_externals);
+ else
+ assemble_external_real (decl);
#endif
}
if (! SYMBOL_REF_USED (fun))
{
SYMBOL_REF_USED (fun) = 1;
- (*targetm.asm_out.external_libcall) (fun);
+ targetm.asm_out.external_libcall (fun);
}
}
ASM_OUTPUT_LABEL (asm_out_file, name);
}
-/* Set the symbol_referenced flag for ID and notify callgraph code. */
+/* Set the symbol_referenced flag for ID. */
void
mark_referenced (tree id)
{
- if (!TREE_SYMBOL_REFERENCED (id))
+ TREE_SYMBOL_REFERENCED (id) = 1;
+}
+
+/* Set the symbol_referenced flag for DECL and notify callgraph. */
+void
+mark_decl_referenced (tree decl)
+{
+ if (TREE_CODE (decl) == FUNCTION_DECL)
{
- struct cgraph_node *node;
- struct cgraph_varpool_node *vnode;
+ /* 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. */
+}
- if (!cgraph_global_info_ready)
- {
- node = cgraph_node_for_identifier (id);
- if (node)
- cgraph_mark_needed_node (node);
- }
+/* 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). */
- vnode = cgraph_varpool_node_for_identifier (id);
- if (vnode)
- cgraph_varpool_mark_needed_node (vnode);
- }
- TREE_SYMBOL_REFERENCED (id) = 1;
+void
+assemble_name_raw (FILE *file, const char *name)
+{
+ if (name[0] == '*')
+ fputs (&name[1], file);
+ else
+ ASM_OUTPUT_LABELREF (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. */
+/* Like assemble_name_raw, but should be used when NAME might refer to
+ an entity that is also represented as a tree (like a function or
+ variable). If NAME does refer to such an entity, that entity will
+ be marked as referenced. */
void
assemble_name (FILE *file, const char *name)
const char *real_name;
tree id;
- real_name = (* targetm.strip_name_encoding) (name);
+ real_name = targetm.strip_name_encoding (name);
id = maybe_get_identifier (real_name);
if (id)
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
This is done at most once per compilation.
Returns an RTX for the address of the template. */
+static GTY(()) rtx initial_trampoline;
+
#ifdef TRAMPOLINE_TEMPLATE
rtx
assemble_trampoline_template (void)
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
int aligned_p ATTRIBUTE_UNUSED)
{
const char *op = integer_asm_op (size, aligned_p);
+ /* Avoid GAS bugs for large values. Specifically negative values whose
+ absolute value fits in a bfd_vma, but not in a bfd_signed_vma. */
+ if (size > UNITS_PER_WORD && size > POINTER_SIZE / BITS_PER_UNIT)
+ return false;
return op && (assemble_integer_with_op (op, x), true);
}
/* Assemble the integer constant X into an object of SIZE bytes. ALIGN is
the alignment of the integer in bits. Return 1 if we were able to output
- the constant, otherwise 0. If FORCE is nonzero, abort if we can't output
- the constant. */
+ the constant, otherwise 0. We must be able to output the constant,
+ if FORCE is nonzero. */
bool
assemble_integer (rtx x, unsigned int size, unsigned int align, int force)
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
/* 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(())
{
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;
/* 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)))
static hashval_t
const_desc_hash (const void *ptr)
{
- return const_hash_1 (((struct constant_descriptor_tree *)ptr)->value);
+ return ((struct constant_descriptor_tree *)ptr)->hash;
}
static hashval_t
return real_hash (TREE_REAL_CST_PTR (exp));
case STRING_CST:
- if (flag_writable_strings)
- {
- p = (char *) &exp;
- len = sizeof exp;
- }
- else
- {
- p = TREE_STRING_POINTER (exp);
- len = TREE_STRING_LENGTH (exp);
- }
+ p = TREE_STRING_POINTER (exp);
+ len = TREE_STRING_LENGTH (exp);
break;
case COMPLEX_CST:
+ const_hash_1 (TREE_IMAGPART (exp)));
case CONSTRUCTOR:
- if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
- {
- char *tmp;
-
- len = int_size_in_bytes (TREE_TYPE (exp));
- tmp = alloca (len);
- get_set_constructor_bytes (exp, (unsigned char *) tmp, len);
- p = tmp;
- break;
- }
- else
- {
- tree link;
-
- 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;
static int
const_desc_eq (const void *p1, const void *p2)
{
- return compare_constant (((struct constant_descriptor_tree *)p1)->value,
- ((struct constant_descriptor_tree *)p2)->value);
+ const struct constant_descriptor_tree *c1 = p1;
+ const struct constant_descriptor_tree *c2 = p2;
+ if (c1->hash != c2->hash)
+ return 0;
+ return compare_constant (c1->value, c2->value);
}
/* Compare t1 and t2, and return 1 only if they are known to result in
return REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2));
case STRING_CST:
- if (flag_writable_strings)
- return t1 == t2;
-
if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
return 0;
&& 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;
-
- tmp1 = alloca (len);
- tmp2 = alloca (len);
-
- if (get_set_constructor_bytes (t1, tmp1, len) != NULL_TREE)
- return 0;
- if (get_set_constructor_bytes (t2, tmp2, len) != NULL_TREE)
- return 0;
-
- return memcmp (tmp1, tmp2, len) == 0;
- }
- else
- {
- tree l1, l2;
+ {
+ 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;
- 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 (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;
- }
- }
+ v1 = CONSTRUCTOR_ELTS (t1);
+ v2 = CONSTRUCTOR_ELTS (t2);
+ if (VEC_length (constructor_elt, v1)
+ != VEC_length (constructor_elt, v2))
+ 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
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);
}
}
}
struct constant_descriptor_tree *desc;
desc = ggc_alloc (sizeof (*desc));
- if (flag_writable_strings && TREE_CODE (exp) == STRING_CST)
- desc->value = exp;
- else
- desc->value = copy_constant (exp);
+ 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++;
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;
/* 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;
}
/* 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
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
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)
{
/* Output the value of EXP. */
output_constant (exp, size, align);
-}
-
-/* A constant which was deferred in its original location has been
- inserted by the RTL inliner into a different function. The
- current function's deferred constant count must be incremented. */
-void
-notice_rtl_inlining_of_deferred_constant (void)
-{
- n_deferred_constants++;
+ if (flag_mudflap)
+ mudflap_enqueue_constant (exp);
}
/* Look up EXP in the table of constant descriptors. Return the rtl
struct constant_descriptor_tree key;
key.value = exp;
- desc = htab_find (const_desc_htab, &key);
+ key.hash = const_hash_1 (exp);
+ desc = htab_find_with_hash (const_desc_htab, &key, key.hash);
return (desc ? desc->rtl : NULL_RTX);
}
{
const struct constant_descriptor_rtx *x = a;
const struct constant_descriptor_rtx *y = b;
- return x->sym == y->sym;
+ return XSTR (x->sym, 0) == XSTR (y->sym, 0);
}
/* This is the worker function for const_rtx_hash, called via for_each_rtx. */
h ^= real_hash (CONST_DOUBLE_REAL_VALUE (x));
break;
+ case CONST_VECTOR:
+ {
+ int i;
+ for (i = XVECLEN (x, 0); i-- > 0; )
+ h = h * 251 + const_rtx_hash_1 (&XVECEXP (x, 0, i), data);
+ }
+ break;
+
case SYMBOL_REF:
h ^= htab_hash_string (XSTR (x, 0));
break;
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;
/* Lookup the value in the hashtable. */
align = GET_MODE_ALIGNMENT (mode == VOIDmode ? word_mode : mode);
#ifdef CONSTANT_ALIGNMENT
{
- tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
+ tree type = lang_hooks.types.type_for_mode (mode, 0);
if (type != NULL_TREE)
align = CONSTANT_ALIGNMENT (make_tree (type, x), align);
}
/* Insert the descriptor into the symbol cross-reference table too. */
slot = htab_find_slot (pool->const_rtx_sym_htab, desc, INSERT);
- if (*slot)
- abort ();
+ gcc_assert (!*slot);
*slot = desc;
/* Construct the MEM. */
- desc->mem = def = gen_rtx_MEM (mode, symbol);
- set_mem_attributes (def, (*lang_hooks.types.type_for_mode) (mode, 0), 1);
- RTX_UNCHANGING_P (def) = 1;
+ 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);
/* If we're dropping a label to the constant pool, make sure we
don't delete it. */
return find_pool_constant (cfun->varasm->pool, addr)->mode;
}
-enum machine_mode
-get_pool_mode_for_function (struct function *f, rtx addr)
-{
- return find_pool_constant (f->varasm->pool, addr)->mode;
-}
-
-/* Similar, return the offset in the constant pool. */
-
-int
-get_pool_offset (rtx addr)
-{
- return find_pool_constant (cfun->varasm->pool, addr)->offset;
-}
-
/* Return the size of the constant pool. */
int
switch (GET_MODE_CLASS (mode))
{
case MODE_FLOAT:
- if (GET_CODE (x) != CONST_DOUBLE)
- abort ();
- else
- {
- REAL_VALUE_TYPE r;
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- assemble_real (r, mode, align);
- }
- break;
-
+ {
+ 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);
enum machine_mode submode = GET_MODE_INNER (mode);
unsigned int subalign = MIN (align, GET_MODE_BITSIZE (submode));
- if (GET_CODE (x) != CONST_VECTOR)
- abort ();
+ gcc_assert (GET_CODE (x) == CONST_VECTOR);
units = CONST_VECTOR_NUNITS (x);
for (i = 0; i < units; i++)
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
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;
- }
+ gcc_assert (!INSN_DELETED_P (tmp));
+ gcc_assert (!NOTE_P (tmp)
+ || NOTE_LINE_NUMBER (tmp) != NOTE_INSN_DELETED);
break;
default:
}
/* First switch to correct section. */
- (*targetm.asm_out.select_rtx_section) (desc->mode, x, desc->align);
+ targetm.asm_out.select_rtx_section (desc->mode, x, desc->align);
#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, desc->mode,
assemble_align (desc->align);
/* Output the label. */
- (*targetm.asm_out.internal_label) (asm_out_file, "LC", desc->labelno);
+ targetm.asm_out.internal_label (asm_out_file, "LC", desc->labelno);
/* Output the data. */
output_constant_pool_2 (desc->mode, x, desc->align);
/* 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))
{
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
+ case VIEW_CONVERT_EXPR:
reloc = compute_reloc_for_constant (TREE_OPERAND (exp, 0));
break;
case CONSTRUCTOR:
- for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
- if (TREE_VALUE (tem) != 0)
- reloc |= compute_reloc_for_constant (TREE_VALUE (tem));
-
+ {
+ unsigned HOST_WIDE_INT idx;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+ if (tem != 0)
+ reloc |= compute_reloc_for_constant (tem);
+ }
break;
default:
/* 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)
+ /* If we have an initialized CONST_DECL, retrieve the initializer. */
+ if (TREE_CODE (tem) == CONST_DECL && DECL_INITIAL (tem))
+ tem = DECL_INITIAL (tem);
+
+ if (CONSTANT_CLASS_P (tem) || TREE_CODE (tem) == CONSTRUCTOR)
output_constant_def (tem, 0);
break;
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
+ case VIEW_CONVERT_EXPR:
output_addressed_constants (TREE_OPERAND (exp, 0));
break;
case CONSTRUCTOR:
- for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
- if (TREE_VALUE (tem) != 0)
- output_addressed_constants (TREE_VALUE (tem));
-
+ {
+ unsigned HOST_WIDE_INT idx;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+ if (tem != 0)
+ output_addressed_constants (tem);
+ }
break;
default:
{
/* 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))
+ && !VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (value)))
{
+ unsigned HOST_WIDE_INT idx;
tree elt;
bool absolute = true;
- for (elt = CONSTRUCTOR_ELTS (value); elt; elt = TREE_CHAIN (elt))
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt)
{
tree reloc;
- value = TREE_VALUE (elt);
- reloc = initializer_constant_valid_p (value, TREE_TYPE (value));
+ reloc = initializer_constant_valid_p (elt, TREE_TYPE (elt));
if (!reloc)
return NULL_TREE;
if (reloc != null_pointer_node)
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))
- || TREE_CODE (TREE_TYPE (value)) == OFFSET_TYPE)
- && 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);
- /* 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);
+ /* 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 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;
/* 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 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;
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 = 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)
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))
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)
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. */
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 (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 ("%Jweak declaration of '%D' must precede definition",
- newdecl, newdecl);
+ error ("weak declaration of %q+D must precede definition",
+ newdecl);
/* If we've already generated rtl referencing OLDDECL, we may
have done so in a way that will not function properly with
a weak symbol. */
else if (TREE_USED (olddecl)
&& TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl)))
- warning ("%Jweak declaration of '%D' after first use results "
- "in unspecified behavior", newdecl, newdecl);
+ warning (0, "weak declaration of %q+D after first use results "
+ "in unspecified behavior", newdecl);
if (SUPPORTS_WEAK)
{
declare_weak (tree decl)
{
if (! TREE_PUBLIC (decl))
- error ("%Jweak declaration of '%D' must be public", decl, decl);
+ error ("weak declaration of %q+D must be public", decl);
else if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
- error ("%Jweak declaration of '%D' must precede definition", decl, decl);
+ error ("weak declaration of %q+D must precede definition", decl);
else if (SUPPORTS_WEAK)
{
if (! DECL_WEAK (decl))
weak_decls = tree_cons (NULL, decl, weak_decls);
}
else
- warning ("%Jweak declaration of '%D' not supported", decl, decl);
+ warning (0, "weak declaration of %q+D not supported", decl);
mark_weak (decl);
}
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
}
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 (tree decl, tree 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))
+}
+
+/* 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 *p, t;
-#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);
+ 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));
}
- else
- warning ("only weak aliases are supported in this configuration");
+}
-#else
- warning ("alias definitions not supported in this configuration; ignored");
-#endif
+/* Second pass of completing pending aliases. Emit the actual assembly.
+ This happens at the end of compilation and thus it is assured that the
+ target symbol has been emitted. */
+
+void
+finish_aliases_2 (void)
+{
+ unsigned i;
+ alias_pair *p;
+
+ for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
+ do_assemble_alias (p->decl, p->target);
+
+ VEC_truncate (alias_pair, alias_pairs, 0);
+}
+
+/* Emit an assembler directive to make the symbol for DECL an alias to
+ the symbol for TARGET. */
+
+void
+assemble_alias (tree decl, tree target)
+{
+ 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
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
}
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
void
make_decl_one_only (tree decl)
{
- if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL)
- abort ();
+ gcc_assert (TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == FUNCTION_DECL);
TREE_PUBLIC (decl) = 1;
- 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
}
enum tls_model
-decl_tls_model (tree decl)
+decl_default_tls_model (tree decl)
{
enum tls_model kind;
- tree attr = lookup_attribute ("tls_model", DECL_ATTRIBUTES (decl));
bool is_local;
- if (attr)
- {
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (TREE_CODE (attr) != STRING_CST)
- abort ();
- if (!strcmp (TREE_STRING_POINTER (attr), "local-exec"))
- kind = TLS_MODEL_LOCAL_EXEC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "initial-exec"))
- kind = TLS_MODEL_INITIAL_EXEC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "local-dynamic"))
- kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC;
- else if (!strcmp (TREE_STRING_POINTER (attr), "global-dynamic"))
- kind = TLS_MODEL_GLOBAL_DYNAMIC;
- else
- abort ();
- return kind;
- }
-
- is_local = (*targetm.binds_local_p) (decl);
- if (!flag_pic)
+ is_local = targetm.binds_local_p (decl);
+ if (!flag_shlib)
{
if (is_local)
kind = TLS_MODEL_LOCAL_EXEC;
else
kind = TLS_MODEL_INITIAL_EXEC;
}
+
/* Local dynamic is inefficient when we're not combining the
parts of the address. */
else if (optimize && is_local)
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
void
default_no_named_section (const char *name ATTRIBUTE_UNUSED,
- unsigned int flags ATTRIBUTE_UNUSED)
+ unsigned int flags ATTRIBUTE_UNUSED,
+ tree decl ATTRIBUTE_UNUSED)
{
/* Some object formats don't support named sections at all. The
front-end should already have flagged this as an error. */
- abort ();
+ gcc_unreachable ();
}
void
-default_elf_asm_named_section (const char *name, unsigned int flags)
+default_elf_asm_named_section (const char *name, unsigned int flags,
+ tree decl ATTRIBUTE_UNUSED)
{
char flagchars[10], *f = flagchars;
- if (! named_section_first_declaration (name))
+ /* If we have already declared this section, we can use an
+ abbreviated form to switch back to it -- unless this section is
+ part of a COMDAT groups, in which case GAS requires the full
+ declaration every time. */
+ if (!(HAVE_COMDAT_GROUP && (flags & SECTION_LINKONCE))
+ && ! 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 (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 (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)
{
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 (tree, int, int);
-
-static enum section_category
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;
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
void
default_unique_section_1 (tree decl, int reloc, int shlib)
{
- bool one_only = DECL_ONE_ONLY (decl);
+ /* We only need to use .gnu.linkonce if we don't have COMDAT groups. */
+ bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP;
const char *prefix, *name;
size_t nlen, plen;
char *string;
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);
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. */
/* 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)
}
/* 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
}
#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. */
{
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
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)
{