/* Output variables, constants and external declarations, for GNU compiler.
- Copyright (C) 1987-2013 Free Software Foundation, Inc.
+ Copyright (C) 1987-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "stringpool.h"
-#include "gcc-symtab.h"
#include "varasm.h"
#include "flags.h"
+#include "hard-reg-set.h"
#include "function.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "stmt.h"
#include "expr.h"
-#include "hard-reg-set.h"
#include "regs.h"
#include "output.h"
#include "diagnostic-core.h"
-#include "hashtab.h"
-#include "ggc.h"
#include "langhooks.h"
#include "tm_p.h"
#include "debug.h"
#include "target.h"
#include "common/common-target.h"
#include "targhooks.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "basic-block.h"
#include "cgraph.h"
-#include "pointer-set.h"
#include "asan.h"
-#include "basic-block.h"
+#include "rtl-iter.h"
+#include "tree-chkp.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
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 alias_set_type const_alias_set;
-
/* Whether we saw any functions with no_split_stack. */
static bool saw_no_split_stack;
static bool incorporeal_function_p (tree);
#endif
static void decode_addr_const (tree, struct addr_const *);
-static hashval_t const_desc_hash (const void *);
-static int const_desc_eq (const void *, const void *);
static hashval_t const_hash_1 (const tree);
static int compare_constant (const tree, const tree);
-static tree copy_constant (tree);
static void output_constant_def_contents (rtx);
static void output_addressed_constants (tree);
-static unsigned HOST_WIDE_INT array_size_for_constructor (tree);
-static unsigned min_align (unsigned, unsigned);
+static unsigned HOST_WIDE_INT output_constant (tree, unsigned HOST_WIDE_INT,
+ unsigned int);
static void globalize_decl (tree);
static bool decl_readonly_section_1 (enum section_category);
#ifdef BSS_SECTION_ASM_OP
at the cold section. */
bool in_cold_section_p;
+/* The following global holds the "function name" for the code in the
+ cold section of a function, if hot/cold function splitting is enabled
+ and there was actually code that went into the cold section. A
+ pseudo function name is needed for the cold section of code for some
+ debugging tools that perform symbolization. */
+tree cold_function_name = NULL_TREE;
+
/* A linked list of all the unnamed sections. */
static GTY(()) section *unnamed_sections;
/* Return a nonzero value if DECL has a section attribute. */
#define IN_NAMED_SECTION(DECL) \
((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
- && DECL_SECTION_NAME (DECL) != NULL_TREE)
+ && DECL_SECTION_NAME (DECL) != NULL)
+
+struct section_hasher : ggc_ptr_hash<section>
+{
+ typedef const char *compare_type;
+
+ static hashval_t hash (section *);
+ static bool equal (section *, const char *);
+};
/* Hash table of named sections. */
-static GTY((param_is (section))) htab_t section_htab;
+static GTY(()) hash_table<section_hasher> *section_htab;
+
+struct object_block_hasher : ggc_ptr_hash<object_block>
+{
+ typedef const section *compare_type;
+
+ static hashval_t hash (object_block *);
+ static bool equal (object_block *, const section *);
+};
/* A table of object_blocks, indexed by section. */
-static GTY((param_is (struct object_block))) htab_t object_block_htab;
+static GTY(()) hash_table<object_block_hasher> *object_block_htab;
/* The next number to use for internal anchor labels. */
static GTY(()) int anchor_labelno;
/* Helper routines for maintaining section_htab. */
-static int
-section_entry_eq (const void *p1, const void *p2)
+bool
+section_hasher::equal (section *old, const char *new_name)
{
- const section *old = (const section *) p1;
- const char *new_name = (const char *) p2;
-
return strcmp (old->named.name, new_name) == 0;
}
-static hashval_t
-section_entry_hash (const void *p)
+hashval_t
+section_hasher::hash (section *old)
{
- const section *old = (const section *) p;
return htab_hash_string (old->named.name);
}
/* Helper routines for maintaining object_block_htab. */
-static int
-object_block_entry_eq (const void *p1, const void *p2)
+inline bool
+object_block_hasher::equal (object_block *old, const section *new_section)
{
- const struct object_block *old = (const struct object_block *) p1;
- const section *new_section = (const section *) p2;
-
return old->sect == new_section;
}
-static hashval_t
-object_block_entry_hash (const void *p)
+hashval_t
+object_block_hasher::hash (object_block *old)
{
- const struct object_block *old = (const struct object_block *) p;
return hash_section (old->sect);
}
{
section *sect;
- sect = ggc_alloc_section ();
+ sect = ggc_alloc<section> ();
sect->unnamed.common.flags = flags | SECTION_UNNAMED;
sect->unnamed.callback = callback;
sect->unnamed.data = data;
{
section *sect;
- sect = ggc_alloc_section ();
+ sect = ggc_alloc<section> ();
sect->noswitch.common.flags = flags | SECTION_NOSWITCH;
sect->noswitch.callback = callback;
{
section *sect, **slot;
- slot = (section **)
- htab_find_slot_with_hash (section_htab, name,
- htab_hash_string (name), INSERT);
+ slot = section_htab->find_slot_with_hash (name, htab_hash_string (name),
+ INSERT);
flags |= SECTION_NAMED;
if (*slot == NULL)
{
- sect = ggc_alloc_section ();
+ sect = ggc_alloc<section> ();
sect->named.common.flags = flags;
sect->named.name = ggc_strdup (name);
sect->named.decl = decl;
get_block_for_section (section *sect)
{
struct object_block *block;
- void **slot;
if (sect == NULL)
return NULL;
- slot = htab_find_slot_with_hash (object_block_htab, sect,
- hash_section (sect), INSERT);
- block = (struct object_block *) *slot;
+ object_block **slot
+ = object_block_htab->find_slot_with_hash (sect, hash_section (sect),
+ INSERT);
+ block = *slot;
if (block == NULL)
{
- block = ggc_alloc_cleared_object_block ();
+ block = ggc_cleared_alloc<object_block> ();
block->sect = sect;
*slot = block;
}
/* Create the extended SYMBOL_REF. */
size = RTX_HDR_SIZE + sizeof (struct block_symbol);
- symbol = ggc_alloc_rtx_def (size);
+ symbol = (rtx) ggc_internal_alloc (size);
/* Initialize the normal SYMBOL_REF fields. */
memset (symbol, 0, size);
if (name == NULL)
{
gcc_assert (decl && DECL_P (decl) && DECL_SECTION_NAME (decl));
- name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ name = DECL_SECTION_NAME (decl);
}
flags = targetm.section_type_flags (decl, name, reloc);
return get_section (name, flags, decl);
}
+/* Worker for resolve_unique_section. */
+
+static bool
+set_implicit_section (struct symtab_node *n, void *data ATTRIBUTE_UNUSED)
+{
+ n->implicit_section = true;
+ return false;
+}
+
/* If required, set DECL_SECTION_NAME to a unique name. */
void
resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED,
int flag_function_or_data_sections)
{
- if (DECL_SECTION_NAME (decl) == NULL_TREE
+ if (DECL_SECTION_NAME (decl) == NULL
&& targetm_common.have_named_sections
&& (flag_function_or_data_sections
- || DECL_ONE_ONLY (decl)))
+ || DECL_COMDAT_GROUP (decl)))
{
targetm.asm_out.unique_section (decl, reloc);
- DECL_HAS_IMPLICIT_SECTION_NAME_P (decl) = true;
+ if (DECL_SECTION_NAME (decl))
+ symtab_node::get (decl)->call_for_symbol_and_aliases
+ (set_implicit_section, NULL, true);
}
}
hot_function_section (tree decl)
{
if (decl != NULL_TREE
- && DECL_SECTION_NAME (decl) != NULL_TREE
+ && DECL_SECTION_NAME (decl) != NULL
&& targetm_common.have_named_sections)
return get_named_section (decl, NULL, 0);
else
{
if (named_section_suffix)
{
- tree dsn = DECL_SECTION_NAME (decl);
+ const char *dsn = DECL_SECTION_NAME (decl);
const char *stripped_name;
char *name, *buffer;
- name = (char *) alloca (TREE_STRING_LENGTH (dsn) + 1);
- memcpy (name, TREE_STRING_POINTER (dsn),
- TREE_STRING_LENGTH (dsn) + 1);
+ name = (char *) alloca (strlen (dsn) + 1);
+ memcpy (name, dsn,
+ strlen (dsn) + 1);
stripped_name = targetm.strip_name_encoding (name);
buffer = ACONCAT ((stripped_name, named_section_suffix, NULL));
return get_named_section (decl, buffer, 0);
}
- else if (DECL_HAS_IMPLICIT_SECTION_NAME_P (decl))
+ else if (symtab_node::get (decl)->implicit_section)
{
const char *name;
/* Do not try to split gnu_linkonce functions. This gets somewhat
slipperly. */
- if (DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP)
+ if (DECL_COMDAT_GROUP (decl) && !HAVE_COMDAT_GROUP)
return NULL;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
name = targetm.strip_name_encoding (name);
/* Old GNU linkers have buggy --gc-section support, which sometimes
results in .gcc_except_table* sections being garbage collected. */
if (decl
- && DECL_SECTION_NAME (decl)
- && DECL_HAS_IMPLICIT_SECTION_NAME_P (decl))
+ && symtab_node::get (decl)->implicit_section)
return NULL;
#endif
unlikely executed (this happens especially with function splitting
where we can split away unnecessary parts of static constructors. */
if (startup && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED)
- return get_named_text_section (decl, ".text.startup", NULL);
+ {
+ /* If we do have a profile or(and) LTO phase is executed, we do not need
+ these ELF section. */
+ if (!in_lto_p || !flag_profile_values)
+ return get_named_text_section (decl, ".text.startup", NULL);
+ else
+ return NULL;
+ }
/* Similarly for exit. */
if (exit && freq != NODE_FREQUENCY_UNLIKELY_EXECUTED)
case NODE_FREQUENCY_UNLIKELY_EXECUTED:
return get_named_text_section (decl, ".text.unlikely", NULL);
case NODE_FREQUENCY_HOT:
- return get_named_text_section (decl, ".text.hot", NULL);
+ /* If we do have a profile or(and) LTO phase is executed, we do not need
+ these ELF section. */
+ if (!in_lto_p || !flag_profile_values)
+ return get_named_text_section (decl, ".text.hot", NULL);
default:
return NULL;
}
if (decl)
{
- struct cgraph_node *node = cgraph_get_node (decl);
+ struct cgraph_node *node = cgraph_node::get (decl);
if (node)
{
#ifdef USE_SELECT_SECTION_FOR_FUNCTIONS
if (decl != NULL_TREE
- && DECL_SECTION_NAME (decl) != NULL_TREE)
+ && DECL_SECTION_NAME (decl) != NULL)
{
if (targetm.asm_out.function_section)
section = targetm.asm_out.function_section (decl, freq,
else
return targetm.asm_out.select_section
(decl, freq == NODE_FREQUENCY_UNLIKELY_EXECUTED,
- DECL_ALIGN (decl));
+ symtab_node::get (decl)->definition_alignment ());
#else
if (targetm.asm_out.function_section)
section = targetm.asm_out.function_section (decl, freq, startup, exit);
{
if (decl != NULL_TREE && DECL_SECTION_NAME (decl))
{
- const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ const char *name = DECL_SECTION_NAME (decl);
- if (DECL_ONE_ONLY (decl) && HAVE_COMDAT_GROUP)
+ if (DECL_COMDAT_GROUP (decl) && HAVE_COMDAT_GROUP)
{
const char *dot;
size_t len;
return get_section (rname, SECTION_LINKONCE, decl);
}
/* For .gnu.linkonce.t.foo we want to use .gnu.linkonce.r.foo. */
- else if (DECL_ONE_ONLY (decl)
+ else if (DECL_COMDAT_GROUP (decl)
&& strncmp (name, ".gnu.linkonce.t.", 16) == 0)
{
size_t len = strlen (name) + 1;
return readonly_data_section;
}
+/* A subroutine of mergeable_string_section and mergeable_constant_section. */
+
+static const char *
+function_mergeable_rodata_prefix (void)
+{
+ section *s = targetm.asm_out.function_rodata_section (current_function_decl);
+ if (SECTION_STYLE (s) == SECTION_NAMED)
+ return s->named.name;
+ else
+ return targetm.asm_out.mergeable_rodata_prefix;
+}
+
/* Return the section to use for string merging. */
static section *
&& (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
&& TREE_STRING_LENGTH (decl) >= len)
{
- enum machine_mode mode;
+ machine_mode mode;
unsigned int modesize;
const char *str;
HOST_WIDE_INT i;
int j, unit;
- const char *prefix = targetm.asm_out.mergeable_rodata_prefix;
+ const char *prefix = function_mergeable_rodata_prefix ();
char *name = (char *) alloca (strlen (prefix) + 30);
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (decl)));
/* Return the section to use for constant merging. */
section *
-mergeable_constant_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+mergeable_constant_section (machine_mode mode ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED,
unsigned int flags ATTRIBUTE_UNUSED)
{
&& align <= 256
&& (align & (align - 1)) == 0)
{
- const char *prefix = targetm.asm_out.mergeable_rodata_prefix;
+ const char *prefix = function_mergeable_rodata_prefix ();
char *name = (char *) alloca (strlen (prefix) + 30);
sprintf (name, "%s.cst%d", prefix, (int) (align / 8));
char *starred = (char *) alloca (strlen (name) + 2);
starred[0] = '*';
strcpy (starred + 1, name);
- change_decl_assembler_name (decl, get_identifier (starred));
+ symtab->change_decl_assembler_name (decl, get_identifier (starred));
SET_DECL_RTL (decl, NULL_RTX);
}
\f
if (asmspec[0] != 0 && i < 0)
{
i = atoi (asmspec);
- if (i < FIRST_PSEUDO_REGISTER && i >= 0)
+ if (i < FIRST_PSEUDO_REGISTER && i >= 0 && reg_names[i][0])
return i;
else
return -2;
for (i = 0; i < (int) ARRAY_SIZE (table); i++)
if (table[i].name[0]
- && ! strcmp (asmspec, table[i].name))
+ && ! strcmp (asmspec, table[i].name)
+ && reg_names[table[i].number][0])
return table[i].number;
}
#endif /* ADDITIONAL_REGISTER_NAMES */
bss_initializer_p (const_tree decl)
{
return (DECL_INITIAL (decl) == NULL
- || DECL_INITIAL (decl) == error_mark_node
+ /* In LTO we have no errors in program; error_mark_node is used
+ to mark offlined constructors. */
+ || (DECL_INITIAL (decl) == error_mark_node
+ && !in_lto_p)
|| (flag_zero_initialized_in_bss
/* Leave constant zeroes in .rodata so they
can be shared. */
and for code accessing the variable as guaranteed alignment, we
can only increase the alignment if it is a performance optimization
if the references to it must bind to the current definition. */
- if (decl_binds_to_current_def_p (decl))
+ if (decl_binds_to_current_def_p (decl)
+ && !DECL_VIRTUAL_P (decl))
{
#ifdef DATA_ALIGNMENT
unsigned int data_align = DATA_ALIGNMENT (TREE_TYPE (decl), align);
#endif
#ifdef CONSTANT_ALIGNMENT
if (DECL_INITIAL (decl) != 0
- && DECL_INITIAL (decl) != error_mark_node)
+ /* In LTO we have no errors in program; error_mark_node is used
+ to mark offlined constructors. */
+ && (in_lto_p || DECL_INITIAL (decl) != error_mark_node))
{
unsigned int const_align
= CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align);
align = data_align;
#endif
#ifdef CONSTANT_ALIGNMENT
- if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node)
+ if (DECL_INITIAL (decl) != 0
+ /* In LTO we have no errors in program; error_mark_node is used
+ to mark offlined constructors. */
+ && (in_lto_p || DECL_INITIAL (decl) != error_mark_node))
{
unsigned int const_align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl),
align);
{
addr_space_t as = ADDR_SPACE_GENERIC;
int reloc;
+ varpool_node *vnode = varpool_node::get (decl);
+ if (vnode)
+ {
+ vnode = vnode->ultimate_alias_target ();
+ decl = vnode->decl;
+ }
if (TREE_TYPE (decl) != error_mark_node)
as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
+ /* We need the constructor to figure out reloc flag. */
+ if (vnode)
+ vnode->get_constructor ();
+
if (DECL_COMMON (decl))
{
/* If the decl has been given an explicit section name, or it resides
/* There's no point using object blocks for something that is
isolated by definition. */
- if (DECL_ONE_ONLY (decl))
+ if (DECL_COMDAT_GROUP (decl))
return NULL;
}
static bool
use_blocks_for_decl_p (tree decl)
{
+ struct symtab_node *snode;
+
/* Only data DECLs can be placed into object blocks. */
if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL)
return false;
/* If this decl is an alias, then we don't want to emit a
definition. */
- if (lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
+ if (TREE_CODE (decl) == VAR_DECL
+ && (snode = symtab_node::get (decl)) != NULL
+ && snode->alias)
return false;
return targetm.use_blocks_for_decl_p (decl);
}
+/* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS
+ until we find an identifier that is not itself a transparent alias.
+ Modify the alias passed to it by reference (and all aliases on the
+ way to the ultimate target), such that they do not have to be
+ followed again, and return the ultimate target of the alias
+ chain. */
+
+static inline tree
+ultimate_transparent_alias_target (tree *alias)
+{
+ tree target = *alias;
+
+ if (IDENTIFIER_TRANSPARENT_ALIAS (target))
+ {
+ gcc_assert (TREE_CHAIN (target));
+ target = ultimate_transparent_alias_target (&TREE_CHAIN (target));
+ gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target)
+ && ! TREE_CHAIN (target));
+ *alias = target;
+ }
+
+ return target;
+}
+
/* Create the DECL_RTL for a VAR_DECL or FUNCTION_DECL. DECL should
have static storage duration. In other words, it should not be an
automatic variable, including PARM_DECLs.
{
const char *name = 0;
int reg_number;
+ tree id;
rtx x;
/* Check that we are not being given an automatic variable. */
return;
}
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ id = DECL_ASSEMBLER_NAME (decl);
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && cgraph_node::get (decl)
+ && cgraph_node::get (decl)->instrumentation_clone)
+ ultimate_transparent_alias_target (&id);
+ name = IDENTIFIER_POINTER (id);
if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL
&& DECL_REGISTER (decl))
else if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
{
const char *asmspec = name+1;
- enum machine_mode mode = DECL_MODE (decl);
+ machine_mode mode = DECL_MODE (decl);
reg_number = decode_reg_name (asmspec);
/* First detect errors in declaring global registers. */
if (reg_number == -1)
confused with that register and be eliminated. This usage is
somewhat suspect... */
- SET_DECL_RTL (decl, gen_rtx_raw_REG (mode, reg_number));
+ SET_DECL_RTL (decl, gen_raw_REG (mode, reg_number));
ORIGINAL_REGNO (DECL_RTL (decl)) = reg_number;
REG_USERVAR_P (DECL_RTL (decl)) = 1;
/* As a register variable, it has no section. */
return;
}
+ /* Avoid internal errors from invalid register
+ specifications. */
+ SET_DECL_ASSEMBLER_NAME (decl, NULL_TREE);
+ DECL_HARD_REGISTER (decl) = 0;
+ return;
}
/* Now handle ordinary static variables and functions (in memory).
Also handle vars declared register invalidly. */
is called early and it needs to make DECL_RTL to get the name.
we take care of recomputing the DECL_RTL after visibility is changed. */
if (TREE_CODE (decl) == VAR_DECL
- && DECL_SECTION_NAME (decl) != NULL_TREE
+ && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+ && DECL_SECTION_NAME (decl) != NULL
&& DECL_INITIAL (decl) == NULL_TREE
&& DECL_COMMON (decl))
DECL_COMMON (decl) = 0;
x = create_block_symbol (name, get_block_for_decl (decl), -1);
else
{
- enum machine_mode address_mode = Pmode;
+ machine_mode address_mode = Pmode;
if (TREE_TYPE (decl) != error_mark_node)
{
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
{
switch_to_section (sec);
assemble_align (POINTER_SIZE);
- assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+ assemble_integer (symbol, POINTER_SIZE_UNITS, POINTER_SIZE, 1);
}
/* Return the numbered .ctors.N (if CONSTRUCTOR_P) or .dtors.N (if
void
notice_global_symbol (tree decl)
{
- const char **type = &first_global_object_name;
+ const char **t = &first_global_object_name;
if (first_global_object_name
|| !TREE_PUBLIC (decl)
|| DECL_EXTERNAL (decl)
|| !DECL_NAME (decl)
+ || (TREE_CODE (decl) == VAR_DECL && DECL_HARD_REGISTER (decl))
|| (TREE_CODE (decl) != FUNCTION_DECL
&& (TREE_CODE (decl) != VAR_DECL
|| (DECL_COMMON (decl)
&& (DECL_INITIAL (decl) == 0
- || DECL_INITIAL (decl) == error_mark_node))))
- || !MEM_P (DECL_RTL (decl)))
+ || DECL_INITIAL (decl) == error_mark_node)))))
return;
/* We win when global object is found, but it is useful to know about weak
symbol as well so we can produce nicer unique names. */
if (DECL_WEAK (decl) || DECL_ONE_ONLY (decl) || flag_shlib)
- type = &weak_global_object_name;
+ t = &weak_global_object_name;
- if (!*type)
+ if (!*t)
{
- const char *p;
- const char *name;
- rtx decl_rtl = DECL_RTL (decl);
-
- p = targetm.strip_name_encoding (XSTR (XEXP (decl_rtl, 0), 0));
- name = ggc_strdup (p);
-
- *type = name;
+ tree id = DECL_ASSEMBLER_NAME (decl);
+ ultimate_transparent_alias_target (&id);
+ *t = ggc_strdup (targetm.strip_name_encoding (IDENTIFIER_POINTER (id)));
}
}
if (DECL_SECTION_NAME (decl))
{
- struct cgraph_node *node = cgraph_get_node (current_function_decl);
+ struct cgraph_node *node = cgraph_node::get (current_function_decl);
/* Calls to function_section rely on first_function_block_is_cold
being accurate. */
first_function_block_is_cold = (node
in_cold_section_p = first_function_block_is_cold;
}
+/* Get the function's name, as described by its RTL. This may be
+ different from the DECL_NAME name used in the source file. */
+const char *
+get_fnname_from_decl (tree decl)
+{
+ rtx x = DECL_RTL (decl);
+ gcc_assert (MEM_P (x));
+ x = XEXP (x, 0);
+ gcc_assert (GET_CODE (x) == SYMBOL_REF);
+ return XSTR (x, 0);
+}
+
/* Output assembler code for the constant pool of a function and associated
with defining the name of the function. DECL describes the function.
NAME is the function's name. For the constant pool, we use the current
ASM_GENERATE_INTERNAL_LABEL (tmp_label, "LCOLDE", const_labelno);
crtl->subsections.cold_section_end_label = ggc_strdup (tmp_label);
const_labelno++;
+ cold_function_name = NULL_TREE;
}
else
{
if (CONSTANT_POOL_BEFORE_FUNCTION)
output_constant_pool (fnname, decl);
+ align = symtab_node::get (decl)->definition_alignment ();
+
/* Make sure the not and cold text (code) sections are properly
aligned. This is necessary here in the case where the function
has both hot and cold sections, because we don't want to re-set
first_function_block_is_cold = false;
switch_to_section (unlikely_text_section ());
- assemble_align (DECL_ALIGN (decl));
+ assemble_align (align);
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.cold_section_label);
/* When the function starts with a cold section, we need to explicitly
&& BB_PARTITION (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb) == BB_COLD_PARTITION)
{
switch_to_section (text_section);
- assemble_align (DECL_ALIGN (decl));
+ assemble_align (align);
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label);
hot_label_written = true;
first_function_block_is_cold = true;
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.hot_section_label);
/* Tell assembler to move to target machine's alignment for functions. */
- align = floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT);
+ align = floor_log2 (align / BITS_PER_UNIT);
if (align > 0)
{
ASM_OUTPUT_ALIGN (asm_out_file, align);
/* Make function name accessible from other files, if appropriate. */
- if (TREE_PUBLIC (decl))
+ if (TREE_PUBLIC (decl)
+ || (cgraph_node::get (decl)->instrumentation_clone
+ && cgraph_node::get (decl)->instrumented_version
+ && TREE_PUBLIC (cgraph_node::get (decl)->instrumented_version->decl)))
{
notice_global_symbol (decl);
save_text_section = in_section;
switch_to_section (unlikely_text_section ());
+#ifdef ASM_DECLARE_COLD_FUNCTION_SIZE
+ if (cold_function_name != NULL_TREE)
+ ASM_DECLARE_COLD_FUNCTION_SIZE (asm_out_file,
+ IDENTIFIER_POINTER (cold_function_name),
+ decl);
+#endif
ASM_OUTPUT_LABEL (asm_out_file, crtl->subsections.cold_section_end_label);
if (first_function_block_is_cold)
switch_to_section (text_section);
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
#if defined ASM_OUTPUT_ALIGNED_DECL_LOCAL
+ unsigned int align = symtab_node::get (decl)->definition_alignment ();
ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, decl, name,
- size, DECL_ALIGN (decl));
+ size, align);
return true;
#elif defined ASM_OUTPUT_ALIGNED_LOCAL
- ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, DECL_ALIGN (decl));
+ unsigned int align = symtab_node::get (decl)->definition_alignment ();
+ ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, align);
return true;
#else
ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
{
-#if defined ASM_OUTPUT_ALIGNED_BSS
ASM_OUTPUT_ALIGNED_BSS (asm_out_file, decl, name, size,
get_variable_align (decl));
return true;
-#endif
}
#endif
if (!dont_output_data)
{
+ /* Caller is supposed to use varpool_get_constructor when it wants
+ to output the body. */
+ gcc_assert (!in_lto_p || DECL_INITIAL (decl) != error_mark_node);
if (DECL_INITIAL (decl)
&& DECL_INITIAL (decl) != error_mark_node
&& !initializer_zerop (DECL_INITIAL (decl)))
else
/* Leave space for it. */
assemble_zeros (tree_to_uhwi (DECL_SIZE_UNIT (decl)));
+ targetm.asm_out.decl_end ();
}
}
+/* Write out assembly for the variable DECL, which is not defined in
+ the current translation unit. */
+void
+assemble_undefined_decl (tree decl)
+{
+ const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ targetm.asm_out.assemble_undefined_decl (asm_out_file, name, decl);
+}
+
/* Assemble everything that is needed for a variable or function declaration.
Not used for automatic variables, and not used for function definitions.
Should not be called for variables of incomplete structure type.
| SECTION_LINKONCE,
DECL_NAME (decl));
in_section = sect;
+#elif defined (TARGET_PECOFF)
+ /* Neither OBJECT_FORMAT_PE, nor OBJECT_FORMAT_COFF is set here.
+ Therefore the following check is used.
+ In case a the target is PE or COFF a comdat group section
+ is created, e.g. .vtable_map_vars$foo. The linker places
+ everything in .vtable_map_vars at the end.
+
+ A fix could be made in
+ gcc/config/i386/winnt.c: i386_pe_unique_section. */
+ if (TARGET_PECOFF)
+ {
+ char *name;
+
+ if (TREE_CODE (DECL_NAME (decl)) == IDENTIFIER_NODE)
+ name = ACONCAT ((sect->named.name, "$",
+ IDENTIFIER_POINTER (DECL_NAME (decl)), NULL));
+ else
+ name = ACONCAT ((sect->named.name, "$",
+ IDENTIFIER_POINTER (DECL_COMDAT_GROUP (DECL_NAME (decl))),
+ NULL));
+
+ targetm.asm_out.named_section (name,
+ sect->named.common.flags
+ | SECTION_LINKONCE,
+ DECL_NAME (decl));
+ in_section = sect;
+ }
#else
switch_to_section (sect);
#endif
/* Avoid O(external_decls**2) lookups in the pending_assemble_externals
TREE_LIST in assemble_external. */
-static struct pointer_set_t *pending_assemble_externals_set;
+static hash_set<tree> *pending_assemble_externals_set;
/* True if DECL is a function decl for which no out-of-line copy exists.
It is assumed that DECL's assembler name has been set. */
pending_assemble_externals = 0;
pending_assemble_externals_processed = true;
- pointer_set_destroy (pending_assemble_externals_set);
+ delete pending_assemble_externals_set;
#endif
}
Ideally, only final.c would be calling this function, but it is
not clear whether that would break things somehow. See PR 17982
for further discussion. */
- gcc_assert (cgraph_state == CGRAPH_STATE_EXPANSION
- || cgraph_state == CGRAPH_STATE_FINISHED);
+ gcc_assert (state == EXPANSION
+ || state == FINISHED);
#endif
if (!DECL_P (decl) || !DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
return;
}
- if (! pointer_set_insert (pending_assemble_externals_set, decl))
+ if (! pending_assemble_externals_set->add (decl))
pending_assemble_externals = tree_cons (NULL, decl,
pending_assemble_externals);
#endif
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_get_create_node (decl);
+ struct cgraph_node *node = cgraph_node::get_create (decl);
if (!DECL_EXTERNAL (decl)
&& !node->definition)
- cgraph_mark_force_output_node (node);
+ node->mark_force_output ();
}
else if (TREE_CODE (decl) == VAR_DECL)
{
- struct varpool_node *node = varpool_node_for_decl (decl);
+ varpool_node *node = varpool_node::get_create (decl);
/* C++ frontend use mark_decl_references to force COMDAT variables
to be output that might appear dead otherwise. */
node->force_output = true;
}
-/* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at *ALIAS
- until we find an identifier that is not itself a transparent alias.
- Modify the alias passed to it by reference (and all aliases on the
- way to the ultimate target), such that they do not have to be
- followed again, and return the ultimate target of the alias
- chain. */
-
-static inline tree
-ultimate_transparent_alias_target (tree *alias)
-{
- tree target = *alias;
-
- if (IDENTIFIER_TRANSPARENT_ALIAS (target))
- {
- gcc_assert (TREE_CHAIN (target));
- target = ultimate_transparent_alias_target (&TREE_CHAIN (target));
- gcc_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target)
- && ! TREE_CHAIN (target));
- *alias = target;
- }
-
- return target;
-}
-
/* 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
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)
+ if (size > UNITS_PER_WORD && size > POINTER_SIZE_UNITS)
return false;
return op && (assemble_integer_with_op (op, x), true);
}
it into words it if is multi-word, otherwise split it into bytes. */
if (size > 1)
{
- enum machine_mode omode, imode;
+ machine_mode omode, imode;
unsigned int subalign;
unsigned int subsize, i;
enum mode_class mclass;
}
\f
void
-assemble_real (REAL_VALUE_TYPE d, enum machine_mode mode, unsigned int align)
+assemble_real (REAL_VALUE_TYPE d, machine_mode mode, unsigned int align)
{
long data[4] = {0, 0, 0, 0};
int i;
else if (TREE_CODE (target) == MEM_REF
&& TREE_CODE (TREE_OPERAND (target, 0)) == ADDR_EXPR)
{
- offset += mem_ref_offset (target).low;
+ offset += mem_ref_offset (target).to_short_addr ();
target = TREE_OPERAND (TREE_OPERAND (target, 0), 0);
}
else if (TREE_CODE (target) == INDIRECT_REF
value->offset = offset;
}
\f
-
-static GTY((param_is (struct constant_descriptor_tree)))
- htab_t const_desc_htab;
+static GTY(()) hash_table<tree_descriptor_hasher> *const_desc_htab;
static void maybe_output_constant_def_contents (struct constant_descriptor_tree *, int);
/* Constant pool accessor function. */
-htab_t
+hash_table<tree_descriptor_hasher> *
constant_pool_htab (void)
{
return const_desc_htab;
/* Compute a hash code for a constant expression. */
-static hashval_t
-const_desc_hash (const void *ptr)
+hashval_t
+tree_descriptor_hasher::hash (constant_descriptor_tree *ptr)
{
- return ((const struct constant_descriptor_tree *)ptr)->hash;
+ return ptr->hash;
}
static hashval_t
switch (code)
{
case INTEGER_CST:
- p = (char *) &TREE_INT_CST (exp);
- len = sizeof TREE_INT_CST (exp);
+ p = (char *) &TREE_INT_CST_ELT (exp, 0);
+ len = TREE_INT_CST_NUNITS (exp) * sizeof (HOST_WIDE_INT);
break;
case REAL_CST:
break;
case LABEL_REF:
- hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13;
+ hi = (value.offset
+ + CODE_LABEL_NUMBER (LABEL_REF_LABEL (value.base)) * 13);
break;
default:
}
/* Wrapper of compare_constant, for the htab interface. */
-static int
-const_desc_eq (const void *p1, const void *p2)
+bool
+tree_descriptor_hasher::equal (constant_descriptor_tree *c1,
+ constant_descriptor_tree *c2)
{
- const struct constant_descriptor_tree *const c1
- = (const struct constant_descriptor_tree *) p1;
- const struct constant_descriptor_tree *const c2
- = (const struct constant_descriptor_tree *) p2;
if (c1->hash != c2->hash)
return 0;
return compare_constant (c1->value, c2->value);
break;
case LABEL_REF:
- ret = (CODE_LABEL_NUMBER (XEXP (value1.base, 0))
- == CODE_LABEL_NUMBER (XEXP (value2.base, 0)));
+ ret = (CODE_LABEL_NUMBER (LABEL_REF_LABEL (value1.base))
+ == CODE_LABEL_NUMBER (LABEL_REF_LABEL (value2.base)));
break;
default:
gcc_unreachable ();
}
\f
-/* Make a copy of the whole tree structure for a constant. This
- handles the same types of nodes that compare_constant handles. */
-
-static tree
-copy_constant (tree exp)
-{
- switch (TREE_CODE (exp))
- {
- case ADDR_EXPR:
- /* For ADDR_EXPR, we do not want to copy the decl whose address
- is requested. We do want to copy constants though. */
- if (CONSTANT_CLASS_P (TREE_OPERAND (exp, 0)))
- return build1 (TREE_CODE (exp), TREE_TYPE (exp),
- copy_constant (TREE_OPERAND (exp, 0)));
- else
- return copy_node (exp);
-
- case INTEGER_CST:
- case REAL_CST:
- case FIXED_CST:
- case STRING_CST:
- return copy_node (exp);
-
- case COMPLEX_CST:
- return build_complex (TREE_TYPE (exp),
- copy_constant (TREE_REALPART (exp)),
- copy_constant (TREE_IMAGPART (exp)));
-
- case PLUS_EXPR:
- case POINTER_PLUS_EXPR:
- case MINUS_EXPR:
- return build2 (TREE_CODE (exp), TREE_TYPE (exp),
- copy_constant (TREE_OPERAND (exp, 0)),
- copy_constant (TREE_OPERAND (exp, 1)));
-
- CASE_CONVERT:
- case VIEW_CONVERT_EXPR:
- return build1 (TREE_CODE (exp), TREE_TYPE (exp),
- copy_constant (TREE_OPERAND (exp, 0)));
-
- case VECTOR_CST:
- return build_vector (TREE_TYPE (exp), VECTOR_CST_ELTS (exp));
-
- case CONSTRUCTOR:
- {
- tree copy = copy_node (exp);
- vec<constructor_elt, va_gc> *v;
- unsigned HOST_WIDE_INT idx;
- tree purpose, value;
-
- vec_alloc (v, vec_safe_length (CONSTRUCTOR_ELTS (exp)));
- FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, purpose, value)
- {
- constructor_elt ce = {purpose, copy_constant (value)};
- v->quick_push (ce);
- }
- CONSTRUCTOR_ELTS (copy) = v;
- return copy;
- }
-
- default:
- gcc_unreachable ();
- }
-}
-\f
/* Return the section into which constant EXP should be placed. */
static section *
int labelno;
tree decl;
- desc = ggc_alloc_constant_descriptor_tree ();
- desc->value = copy_constant (exp);
+ desc = ggc_alloc<constant_descriptor_tree> ();
+ desc->value = exp;
/* Create a string containing the label name, in LABEL. */
labelno = const_labelno++;
/* Now construct the SYMBOL_REF and the MEM. */
if (use_object_blocks_p ())
{
- section *sect = get_constant_section (exp, DECL_ALIGN (decl));
+ int align = (TREE_CODE (decl) == CONST_DECL
+ || (TREE_CODE (decl) == VAR_DECL
+ && DECL_IN_CONSTANT_POOL (decl))
+ ? DECL_ALIGN (decl)
+ : symtab_node::get (decl)->definition_alignment ());
+ section *sect = get_constant_section (exp, align);
symbol = create_block_symbol (ggc_strdup (label),
get_block_for_section (sect), -1);
}
rtl = gen_const_mem (TYPE_MODE (TREE_TYPE (exp)), symbol);
set_mem_attributes (rtl, exp, 1);
set_mem_alias_set (rtl, 0);
- set_mem_alias_set (rtl, const_alias_set);
/* We cannot share RTX'es in pool entries.
Mark this piece of RTL as required for unsharing. */
{
struct constant_descriptor_tree *desc;
struct constant_descriptor_tree key;
- void **loc;
/* Look up EXP in the table of constant descriptors. If we didn't find
it, create a new one. */
key.value = exp;
key.hash = const_hash_1 (exp);
- loc = htab_find_slot_with_hash (const_desc_htab, &key, key.hash, INSERT);
+ constant_descriptor_tree **loc
+ = const_desc_htab->find_slot_with_hash (&key, key.hash, INSERT);
- desc = (struct constant_descriptor_tree *) *loc;
+ desc = *loc;
if (desc == 0)
{
desc = build_constant_desc (exp);
/* Output the value of EXP. */
output_constant (exp, size, align);
+
+ targetm.asm_out.decl_end ();
}
/* We must output the constant data referred to by SYMBOL; do so. */
{
tree decl = SYMBOL_REF_DECL (symbol);
tree exp = DECL_INITIAL (decl);
- unsigned int align;
bool asan_protected = false;
/* Make sure any other constants whose addresses appear in EXP
place_block_symbol (symbol);
else
{
- align = DECL_ALIGN (decl);
+ int align = (TREE_CODE (decl) == CONST_DECL
+ || (TREE_CODE (decl) == VAR_DECL
+ && DECL_IN_CONSTANT_POOL (decl))
+ ? DECL_ALIGN (decl)
+ : symtab_node::get (decl)->definition_alignment ());
switch_to_section (get_constant_section (exp, align));
if (align > BITS_PER_UNIT)
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
rtx
lookup_constant_def (tree exp)
{
- struct constant_descriptor_tree *desc;
struct constant_descriptor_tree key;
key.value = exp;
key.hash = const_hash_1 (exp);
- desc = (struct constant_descriptor_tree *)
- htab_find_with_hash (const_desc_htab, &key, key.hash);
+ constant_descriptor_tree *desc
+ = const_desc_htab->find_with_hash (&key, key.hash);
return (desc ? desc->rtl : NULL_RTX);
}
tree_output_constant_def (tree exp)
{
struct constant_descriptor_tree *desc, key;
- void **loc;
tree decl;
/* Look up EXP in the table of constant descriptors. If we didn't find
it, create a new one. */
key.value = exp;
key.hash = const_hash_1 (exp);
- loc = htab_find_slot_with_hash (const_desc_htab, &key, key.hash, INSERT);
+ constant_descriptor_tree **loc
+ = const_desc_htab->find_slot_with_hash (&key, key.hash, INSERT);
- desc = (struct constant_descriptor_tree *) *loc;
+ desc = *loc;
if (desc == 0)
{
desc = build_constant_desc (exp);
}
decl = SYMBOL_REF_DECL (XEXP (desc->rtl, 0));
- varpool_finalize_decl (decl);
+ varpool_node::finalize_decl (decl);
return decl;
}
\f
+struct GTY((chain_next ("%h.next"), for_user)) constant_descriptor_rtx {
+ struct constant_descriptor_rtx *next;
+ rtx mem;
+ rtx sym;
+ rtx constant;
+ HOST_WIDE_INT offset;
+ hashval_t hash;
+ machine_mode mode;
+ unsigned int align;
+ int labelno;
+ int mark;
+};
+
+struct const_rtx_desc_hasher : ggc_ptr_hash<constant_descriptor_rtx>
+{
+ static hashval_t hash (constant_descriptor_rtx *);
+ static bool equal (constant_descriptor_rtx *, constant_descriptor_rtx *);
+};
+
/* Used in the hash tables to avoid outputting the same constant
twice. Unlike 'struct constant_descriptor_tree', RTX constants
are output once per function, not once per file. */
It is used on RISC machines where immediate integer arguments and
constant addresses are restricted so that such constants must be stored
in memory. */
- htab_t GTY((param_is (struct constant_descriptor_rtx))) const_rtx_htab;
+ hash_table<const_rtx_desc_hasher> *const_rtx_htab;
/* Current offset in constant pool (does not include any
machine-specific header). */
HOST_WIDE_INT offset;
};
-struct GTY((chain_next ("%h.next"))) constant_descriptor_rtx {
- struct constant_descriptor_rtx *next;
- rtx mem;
- rtx sym;
- rtx constant;
- HOST_WIDE_INT offset;
- hashval_t hash;
- enum machine_mode mode;
- unsigned int align;
- int labelno;
- int mark;
-};
-
/* Hash and compare functions for const_rtx_htab. */
-static hashval_t
-const_desc_rtx_hash (const void *ptr)
+hashval_t
+const_rtx_desc_hasher::hash (constant_descriptor_rtx *desc)
{
- const struct constant_descriptor_rtx *const desc
- = (const struct constant_descriptor_rtx *) ptr;
return desc->hash;
}
-static int
-const_desc_rtx_eq (const void *a, const void *b)
+bool
+const_rtx_desc_hasher::equal (constant_descriptor_rtx *x,
+ constant_descriptor_rtx *y)
{
- const struct constant_descriptor_rtx *const x
- = (const struct constant_descriptor_rtx *) a;
- const struct constant_descriptor_rtx *const y
- = (const struct constant_descriptor_rtx *) b;
-
if (x->mode != y->mode)
return 0;
return rtx_equal_p (x->constant, y->constant);
}
-/* This is the worker function for const_rtx_hash, called via for_each_rtx. */
+/* Hash one component of a constant. */
-static int
-const_rtx_hash_1 (rtx *xp, void *data)
+static hashval_t
+const_rtx_hash_1 (const_rtx x)
{
unsigned HOST_WIDE_INT hwi;
- enum machine_mode mode;
+ machine_mode mode;
enum rtx_code code;
- hashval_t h, *hp;
- rtx x;
+ hashval_t h;
+ int i;
- x = *xp;
code = GET_CODE (x);
mode = GET_MODE (x);
h = (hashval_t) code * 1048573 + mode;
{
case CONST_INT:
hwi = INTVAL (x);
+
fold_hwi:
{
int shift = sizeof (hashval_t) * CHAR_BIT;
const int n = sizeof (HOST_WIDE_INT) / sizeof (hashval_t);
- int i;
h ^= (hashval_t) hwi;
for (i = 1; i < n; ++i)
}
break;
+ case CONST_WIDE_INT:
+ hwi = GET_MODE_PRECISION (mode);
+ {
+ for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++)
+ hwi ^= CONST_WIDE_INT_ELT (x, i);
+ goto fold_hwi;
+ }
+
case CONST_DOUBLE:
- if (mode == VOIDmode)
+ if (TARGET_SUPPORTS_WIDE_INT == 0 && mode == VOIDmode)
{
hwi = CONST_DOUBLE_LOW (x) ^ CONST_DOUBLE_HIGH (x);
goto fold_hwi;
h ^= fixed_hash (CONST_FIXED_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;
case LABEL_REF:
- h = h * 251 + CODE_LABEL_NUMBER (XEXP (x, 0));
+ h = h * 251 + CODE_LABEL_NUMBER (LABEL_REF_LABEL (x));
break;
case UNSPEC:
break;
}
- hp = (hashval_t *) data;
- *hp = *hp * 509 + h;
- return 0;
+ return h;
}
/* Compute a hash value for X, which should be a constant. */
const_rtx_hash (rtx x)
{
hashval_t h = 0;
- for_each_rtx (&x, const_rtx_hash_1, &h);
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, ALL)
+ h = h * 509 + const_rtx_hash_1 (*iter);
return h;
}
{
struct rtx_constant_pool *pool;
- pool = ggc_alloc_rtx_constant_pool ();
- pool->const_rtx_htab = htab_create_ggc (31, const_desc_rtx_hash,
- const_desc_rtx_eq, NULL);
+ pool = ggc_alloc<rtx_constant_pool> ();
+ pool->const_rtx_htab = hash_table<const_rtx_desc_hasher>::create_ggc (31);
pool->first = NULL;
pool->last = NULL;
pool->offset = 0;
and return a MEM rtx to refer to it in memory. */
rtx
-force_const_mem (enum machine_mode mode, rtx x)
+force_const_mem (machine_mode mode, rtx x)
{
struct constant_descriptor_rtx *desc, tmp;
struct rtx_constant_pool *pool;
rtx def, symbol;
hashval_t hash;
unsigned int align;
- void **slot;
+ constant_descriptor_rtx **slot;
/* If we're not allowed to drop X into the constant pool, don't. */
if (targetm.cannot_force_const_mem (mode, x))
tmp.constant = x;
tmp.mode = mode;
hash = const_rtx_hash (x);
- slot = htab_find_slot_with_hash (pool->const_rtx_htab, &tmp, hash, INSERT);
- desc = (struct constant_descriptor_rtx *) *slot;
+ slot = pool->const_rtx_htab->find_slot_with_hash (&tmp, hash, INSERT);
+ desc = *slot;
/* If the constant was already present, return its memory. */
if (desc)
return copy_rtx (desc->mem);
/* Otherwise, create a new descriptor. */
- desc = ggc_alloc_constant_descriptor_rtx ();
+ desc = ggc_alloc<constant_descriptor_rtx> ();
*slot = desc;
/* Align the location counter as required by EXP's data type. */
/* Given a constant pool SYMBOL_REF, return the corresponding constant. */
rtx
-get_pool_constant (rtx addr)
+get_pool_constant (const_rtx addr)
{
return SYMBOL_REF_CONSTANT (addr)->constant;
}
/* Similar, return the mode. */
-enum machine_mode
+machine_mode
get_pool_mode (const_rtx addr)
{
return SYMBOL_REF_CONSTANT (addr)->mode;
in MODE with known alignment ALIGN. */
static void
-output_constant_pool_2 (enum machine_mode mode, rtx x, unsigned int align)
+output_constant_pool_2 (machine_mode mode, rtx x, unsigned int align)
{
switch (GET_MODE_CLASS (mode))
{
case MODE_UFRACT:
case MODE_ACCUM:
case MODE_UACCUM:
+ case MODE_POINTER_BOUNDS:
assemble_integer (x, GET_MODE_SIZE (mode), align, 1);
break;
case MODE_VECTOR_UACCUM:
{
int i, units;
- enum machine_mode submode = GET_MODE_INNER (mode);
+ machine_mode submode = GET_MODE_INNER (mode);
unsigned int subalign = MIN (align, GET_MODE_BITSIZE (submode));
gcc_assert (GET_CODE (x) == CONST_VECTOR);
CODE_LABEL into a NOTE. */
/* ??? This seems completely and utterly wrong. Certainly it's
not true for NOTE_INSN_DELETED_LABEL, but I disbelieve proper
- functioning even with INSN_DELETED_P and friends. */
+ functioning even with rtx_insn::deleted and friends. */
tmp = x;
switch (GET_CODE (tmp))
/* FALLTHRU */
case LABEL_REF:
- tmp = XEXP (tmp, 0);
- gcc_assert (!INSN_DELETED_P (tmp));
+ tmp = LABEL_REF_LABEL (tmp);
+ gcc_assert (!as_a<rtx_insn *> (tmp)->deleted ());
gcc_assert (!NOTE_P (tmp)
|| NOTE_KIND (tmp) != NOTE_INSN_DELETED);
break;
/* Output the label. */
targetm.asm_out.internal_label (asm_out_file, "LC", desc->labelno);
- /* Output the data. */
- output_constant_pool_2 (desc->mode, x, align);
+ /* Output the data.
+ Pass actual alignment value while emitting string constant to asm code
+ as function 'output_constant_pool_1' explicitly passes the alignment as 1
+ assuming that the data is already aligned which prevents the generation
+ of fix-up table entries. */
+ output_constant_pool_2 (desc->mode, x, desc->align);
/* Make sure all constants in SECTION_MERGE and not SECTION_STRINGS
sections have proper size. */
return;
}
-/* Given a SYMBOL_REF CURRENT_RTX, mark it and all constants it refers
- to as used. Emit referenced deferred strings. This function can
- be used with for_each_rtx to mark all SYMBOL_REFs in an rtx. */
+/* Mark all constants that are referenced by SYMBOL_REFs in X.
+ Emit referenced deferred strings. */
-static int
-mark_constant (rtx *current_rtx, void *data ATTRIBUTE_UNUSED)
+static void
+mark_constants_in_pattern (rtx insn)
{
- rtx x = *current_rtx;
-
- if (x == NULL_RTX || GET_CODE (x) != SYMBOL_REF)
- return 0;
-
- if (CONSTANT_POOL_ADDRESS_P (x))
- {
- struct constant_descriptor_rtx *desc = SYMBOL_REF_CONSTANT (x);
- if (desc->mark == 0)
- {
- desc->mark = 1;
- for_each_rtx (&desc->constant, mark_constant, NULL);
- }
- }
- else if (TREE_CONSTANT_POOL_ADDRESS_P (x))
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL)
{
- tree decl = SYMBOL_REF_DECL (x);
- if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl)))
+ const_rtx x = *iter;
+ if (GET_CODE (x) == SYMBOL_REF)
{
- n_deferred_constants--;
- output_constant_def_contents (x);
+ if (CONSTANT_POOL_ADDRESS_P (x))
+ {
+ struct constant_descriptor_rtx *desc = SYMBOL_REF_CONSTANT (x);
+ if (desc->mark == 0)
+ {
+ desc->mark = 1;
+ iter.substitute (desc->constant);
+ }
+ }
+ else if (TREE_CONSTANT_POOL_ADDRESS_P (x))
+ {
+ tree decl = SYMBOL_REF_DECL (x);
+ if (!TREE_ASM_WRITTEN (DECL_INITIAL (decl)))
+ {
+ n_deferred_constants--;
+ output_constant_def_contents (CONST_CAST_RTX (x));
+ }
+ }
}
}
-
- return -1;
}
/* Look through appropriate parts of INSN, marking all entries in the
deferred strings that are used. */
static void
-mark_constants (rtx insn)
+mark_constants (rtx_insn *insn)
{
if (!INSN_P (insn))
return;
/* Insns may appear inside a SEQUENCE. Only check the patterns of
insns, not any notes that may be attached. We don't want to mark
a constant just because it happens to appear in a REG_EQUIV note. */
- if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ if (rtx_sequence *seq = dyn_cast <rtx_sequence *> (PATTERN (insn)))
{
- rtx seq = PATTERN (insn);
- int i, n = XVECLEN (seq, 0);
+ int i, n = seq->len ();
for (i = 0; i < n; ++i)
{
- rtx subinsn = XVECEXP (seq, 0, i);
+ rtx subinsn = seq->element (i);
if (INSN_P (subinsn))
- for_each_rtx (&PATTERN (subinsn), mark_constant, NULL);
+ mark_constants_in_pattern (subinsn);
}
}
else
- for_each_rtx (&PATTERN (insn), mark_constant, NULL);
+ mark_constants_in_pattern (insn);
}
/* Look through the instructions for this function, and mark all the
static void
mark_constant_pool (void)
{
- rtx insn;
+ rtx_insn *insn;
if (!crtl->uses_const_pool && n_deferred_constants == 0)
return;
break;
}
- if (TREE_PUBLIC (tem))
+ if (!targetm.binds_local_p (tem))
reloc |= 2;
else
reloc |= 1;
This includes the pseudo-op such as ".int" or ".byte", and a newline.
Assumes output_addressed_constants has been done on EXP already.
- Generate exactly SIZE bytes of assembler data, padding at the end
- with zeros if necessary. SIZE must always be specified.
+ Generate at least SIZE bytes of assembler data, padding at the end
+ with zeros if necessary. SIZE must always be specified. The returned
+ value is the actual number of bytes of assembler data generated, which
+ may be bigger than SIZE if the object contains a variable length field.
SIZE is important for structure constructors,
since trailing members may have been omitted from the constructor.
ALIGN is the alignment of the data in bits. */
-void
+static unsigned HOST_WIDE_INT
output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align)
{
enum tree_code code;
unsigned HOST_WIDE_INT thissize;
if (size == 0 || flag_syntax_only)
- return;
+ return size;
/* 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
exp = build1 (ADDR_EXPR, saved_type, TREE_OPERAND (exp, 0));
/* Likewise for constant ints. */
else if (TREE_CODE (exp) == INTEGER_CST)
- exp = build_int_cst_wide (saved_type, TREE_INT_CST_LOW (exp),
- TREE_INT_CST_HIGH (exp));
+ exp = wide_int_to_tree (saved_type, exp);
}
&& vec_safe_is_empty (CONSTRUCTOR_ELTS (exp)))
{
assemble_zeros (size);
- return;
+ return size;
}
if (TREE_CODE (exp) == FDESC_EXPR)
#else
gcc_unreachable ();
#endif
- return;
+ return size;
}
/* Now output the underlying data. If we've handling the padding, return.
case REFERENCE_TYPE:
case OFFSET_TYPE:
case FIXED_POINT_TYPE:
+ case POINTER_BOUNDS_TYPE:
case NULLPTR_TYPE:
if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode,
EXPAND_INITIALIZER),
switch (TREE_CODE (exp))
{
case CONSTRUCTOR:
- output_constructor (exp, size, align, NULL);
- return;
+ return output_constructor (exp, size, align, NULL);
case STRING_CST:
thissize
= MIN ((unsigned HOST_WIDE_INT)TREE_STRING_LENGTH (exp), size);
break;
case VECTOR_CST:
{
- enum machine_mode inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
+ machine_mode inner = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
unsigned int nalign = MIN (align, GET_MODE_ALIGNMENT (inner));
int elt_size = GET_MODE_SIZE (inner);
output_constant (VECTOR_CST_ELT (exp, 0), elt_size, align);
case RECORD_TYPE:
case UNION_TYPE:
gcc_assert (TREE_CODE (exp) == CONSTRUCTOR);
- output_constructor (exp, size, align, NULL);
- return;
+ return output_constructor (exp, size, align, NULL);
case ERROR_MARK:
- return;
+ return 0;
default:
gcc_unreachable ();
if (size > thissize)
assemble_zeros (size - thissize);
+
+ return size;
}
\f
tree max_index;
unsigned HOST_WIDE_INT cnt;
tree index, value, tmp;
- double_int i;
+ offset_int i;
/* 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
/* Compute the total number of array elements. */
tmp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (val)));
- i = tree_to_double_int (max_index) - tree_to_double_int (tmp);
- i += double_int_one;
+ i = wi::to_offset (max_index) - wi::to_offset (tmp) + 1;
/* Multiply by the array element unit size to find number of bytes. */
- i *= tree_to_double_int (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val))));
+ i *= wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (val))));
- gcc_assert (i.fits_uhwi ());
- return i.low;
+ gcc_assert (wi::fits_uhwi_p (i));
+ return i.to_uhwi ();
}
/* Other datastructures + helpers for output_constructor. */
if (local->val == NULL_TREE)
assemble_zeros (fieldsize);
else
- output_constant (local->val, fieldsize, align2);
+ fieldsize = output_constant (local->val, fieldsize, align2);
/* Count its size. */
local->total_bytes += fieldsize;
sign-extend the result because Ada has negative DECL_FIELD_OFFSETs
but we are using an unsigned sizetype. */
unsigned prec = TYPE_PRECISION (sizetype);
- double_int idx = tree_to_double_int (local->index)
- - tree_to_double_int (local->min_index);
- idx = idx.sext (prec);
- fieldpos = (tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (local->val)))
- * idx.low);
+ offset_int idx = wi::sext (wi::to_offset (local->index)
+ - wi::to_offset (local->min_index), prec);
+ fieldpos = (idx * wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (local->val))))
+ .to_short_addr ();
}
else if (local->field != NULL_TREE)
fieldpos = int_byte_position (local->field);
Note no alignment needed in an array, since that is guaranteed
if each element has the proper size. */
if ((local->field != NULL_TREE || local->index != NULL_TREE)
- && fieldpos != local->total_bytes)
+ && fieldpos > local->total_bytes)
{
- gcc_assert (fieldpos >= local->total_bytes);
assemble_zeros (fieldpos - local->total_bytes);
local->total_bytes = fieldpos;
}
if (local->val == NULL_TREE)
assemble_zeros (fieldsize);
else
- output_constant (local->val, fieldsize, align2);
+ fieldsize = output_constant (local->val, fieldsize, align2);
/* Count its size. */
local->total_bytes += fieldsize;
this_time = MIN (end_offset - next_offset, BITS_PER_UNIT - next_bit);
if (BYTES_BIG_ENDIAN)
{
- /* On big-endian machine, take the most significant bits
- first (of the bits that are significant)
- and put them into bytes from the most significant end. */
+ /* On big-endian machine, take the most significant bits (of the
+ bits that are significant) first and put them into bytes from
+ the most significant end. */
shift = end_offset - next_offset - this_time;
/* Don't try to take a bunch of bits that cross
- the word boundary in the INTEGER_CST. We can
- only select bits from the LOW or HIGH part
- not from both. */
- if (shift < HOST_BITS_PER_WIDE_INT
- && shift + this_time > HOST_BITS_PER_WIDE_INT)
+ the word boundary in the INTEGER_CST. We can
+ only select bits from one element. */
+ if ((shift / HOST_BITS_PER_WIDE_INT)
+ != ((shift + this_time - 1) / HOST_BITS_PER_WIDE_INT))
{
- this_time = shift + this_time - HOST_BITS_PER_WIDE_INT;
- shift = HOST_BITS_PER_WIDE_INT;
+ const int end = shift + this_time - 1;
+ shift = end & -HOST_BITS_PER_WIDE_INT;
+ this_time = end - shift + 1;
}
/* Now get the bits from the appropriate constant word. */
- if (shift < HOST_BITS_PER_WIDE_INT)
- value = TREE_INT_CST_LOW (local->val);
- else
- {
- gcc_assert (shift < HOST_BITS_PER_DOUBLE_INT);
- value = TREE_INT_CST_HIGH (local->val);
- shift -= HOST_BITS_PER_WIDE_INT;
- }
+ value = TREE_INT_CST_ELT (local->val, shift / HOST_BITS_PER_WIDE_INT);
+ shift = shift & (HOST_BITS_PER_WIDE_INT - 1);
- /* Get the result. This works only when:
+ /* Get the result. This works only when:
1 <= this_time <= HOST_BITS_PER_WIDE_INT. */
local->byte |= (((value >> shift)
& (((HOST_WIDE_INT) 2 << (this_time - 1)) - 1))
}
else
{
- /* On little-endian machines,
- take first the least significant bits of the value
- and pack them starting at the least significant
+ /* On little-endian machines, take the least significant bits of
+ the value first and pack them starting at the least significant
bits of the bytes. */
shift = next_offset - byte_relative_ebitpos;
/* Don't try to take a bunch of bits that cross
- the word boundary in the INTEGER_CST. We can
- only select bits from the LOW or HIGH part
- not from both. */
- if (shift < HOST_BITS_PER_WIDE_INT
- && shift + this_time > HOST_BITS_PER_WIDE_INT)
- this_time = (HOST_BITS_PER_WIDE_INT - shift);
+ the word boundary in the INTEGER_CST. We can
+ only select bits from one element. */
+ if ((shift / HOST_BITS_PER_WIDE_INT)
+ != ((shift + this_time - 1) / HOST_BITS_PER_WIDE_INT))
+ this_time
+ = HOST_BITS_PER_WIDE_INT - (shift & (HOST_BITS_PER_WIDE_INT - 1));
/* Now get the bits from the appropriate constant word. */
- if (shift < HOST_BITS_PER_WIDE_INT)
- value = TREE_INT_CST_LOW (local->val);
- else
- {
- gcc_assert (shift < HOST_BITS_PER_DOUBLE_INT);
- value = TREE_INT_CST_HIGH (local->val);
- shift -= HOST_BITS_PER_WIDE_INT;
- }
+ value = TREE_INT_CST_ELT (local->val, shift / HOST_BITS_PER_WIDE_INT);
+ shift = shift & (HOST_BITS_PER_WIDE_INT - 1);
- /* Get the result. This works only when:
+ /* Get the result. This works only when:
1 <= this_time <= HOST_BITS_PER_WIDE_INT. */
local->byte |= (((value >> shift)
& (((HOST_WIDE_INT) 2 << (this_time - 1)) - 1))
static void
mark_weak (tree decl)
{
+ if (DECL_WEAK (decl))
+ return;
+
+ struct symtab_node *n = symtab_node::get (decl);
+ if (n && n->refuse_visibility_changes)
+ error ("%+D declared weak after being used", decl);
DECL_WEAK (decl) = 1;
if (DECL_RTL_SET_P (decl)
static tree
find_decl (tree target)
{
- symtab_node *node = symtab_node_for_asm (target);
+ symtab_node *node = symtab_node::get_for_asmname (target);
if (node)
return node->decl;
return NULL_TREE;
void
do_assemble_alias (tree decl, tree target)
{
+ tree id;
+
/* Emulated TLS had better not get this var. */
gcc_assert (!(!targetm.have_tls
&& TREE_CODE (decl) == VAR_DECL
if (TREE_ASM_WRITTEN (decl))
return;
+ id = DECL_ASSEMBLER_NAME (decl);
+ ultimate_transparent_alias_target (&id);
+ ultimate_transparent_alias_target (&target);
+
/* We must force creation of DECL_RTL for debug info generation, even though
we don't use it here. */
make_decl_rtl (decl);
TREE_ASM_WRITTEN (decl) = 1;
TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
+ TREE_ASM_WRITTEN (id) = 1;
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
{
- ultimate_transparent_alias_target (&target);
-
if (!TREE_SYMBOL_REFERENCED (target))
weakref_targets = tree_cons (decl, target, weakref_targets);
#ifdef ASM_OUTPUT_WEAKREF
ASM_OUTPUT_WEAKREF (asm_out_file, decl,
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ IDENTIFIER_POINTER (id),
IDENTIFIER_POINTER (target));
#else
if (!TARGET_SUPPORTS_WEAK)
}
#ifdef ASM_OUTPUT_DEF
+ tree orig_decl = decl;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && cgraph_node::get (decl)->instrumentation_clone
+ && cgraph_node::get (decl)->instrumented_version)
+ orig_decl = cgraph_node::get (decl)->instrumented_version->decl;
+
/* Make name accessible from other files, if appropriate. */
- if (TREE_PUBLIC (decl))
+ if (TREE_PUBLIC (decl) || TREE_PUBLIC (orig_decl))
{
globalize_decl (decl);
maybe_assemble_visibility (decl);
#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
if (targetm.has_ifunc_p ())
ASM_OUTPUT_TYPE_DIRECTIVE
- (asm_out_file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ (asm_out_file, IDENTIFIER_POINTER (id),
IFUNC_ASM_TYPE);
else
#endif
ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target);
# else
ASM_OUTPUT_DEF (asm_out_file,
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ IDENTIFIER_POINTER (id),
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));
+ name = IDENTIFIER_POINTER (id);
# ifdef ASM_WEAKEN_DECL
ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target));
# else
/* 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)))
+ if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t))
+ || id == DECL_ASSEMBLER_NAME (TREE_VALUE (t)))
*p = TREE_CHAIN (t);
else
p = &TREE_CHAIN (t);
list, for the same reason. */
for (p = &weakref_targets; (t = *p) ; )
{
- if (DECL_ASSEMBLER_NAME (decl)
- == ultimate_transparent_alias_target (&TREE_VALUE (t)))
+ if (id == ultimate_transparent_alias_target (&TREE_VALUE (t)))
*p = TREE_CHAIN (t);
else
p = &TREE_CHAIN (t);
# if !defined(ASM_OUTPUT_WEAK_ALIAS) && !defined (ASM_WEAKEN_DECL)
error_at (DECL_SOURCE_LOCATION (decl),
"alias definitions not supported in this configuration");
+ TREE_ASM_WRITTEN (decl) = 1;
return;
# else
if (!DECL_WEAK (decl))
else
error_at (DECL_SOURCE_LOCATION (decl),
"only weak aliases are supported in this configuration");
+ TREE_ASM_WRITTEN (decl) = 1;
return;
}
# endif
/* Allow aliases to aliases. */
if (TREE_CODE (decl) == FUNCTION_DECL)
- cgraph_get_create_node (decl)->alias = true;
+ cgraph_node::get_create (decl)->alias = true;
else
- varpool_node_for_decl (decl)->alias = true;
+ varpool_node::get_create (decl)->alias = true;
/* If the target has already been emitted, we don't have to queue the
alias. This saves a tad of memory. */
- if (cgraph_global_info_ready)
+ if (symtab->global_info_ready)
target_decl = find_decl (target);
else
target_decl= NULL;
if ((target_decl && TREE_ASM_WRITTEN (target_decl))
- || cgraph_state >= CGRAPH_STATE_EXPANSION)
+ || symtab->state >= EXPANSION)
do_assemble_alias (decl, target);
else
{
to its transaction aware clone. Note that tm_pure functions are
considered to be their own clone. */
-static GTY((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
- htab_t tm_clone_hash;
+struct tm_clone_hasher : ggc_cache_ptr_hash<tree_map>
+{
+ static hashval_t hash (tree_map *m) { return tree_map_hash (m); }
+ static bool equal (tree_map *a, tree_map *b) { return tree_map_eq (a, b); }
+
+ static int
+ keep_cache_entry (tree_map *&e)
+ {
+ return ggc_marked_p (e->base.from);
+ }
+};
+
+static GTY((cache)) hash_table<tm_clone_hasher> *tm_clone_hash;
void
record_tm_clone_pair (tree o, tree n)
struct tree_map **slot, *h;
if (tm_clone_hash == NULL)
- tm_clone_hash = htab_create_ggc (32, tree_map_hash, tree_map_eq, 0);
+ tm_clone_hash = hash_table<tm_clone_hasher>::create_ggc (32);
- h = ggc_alloc_tree_map ();
+ h = ggc_alloc<tree_map> ();
h->hash = htab_hash_pointer (o);
h->base.from = o;
h->to = n;
- slot = (struct tree_map **)
- htab_find_slot_with_hash (tm_clone_hash, h, h->hash, INSERT);
+ slot = tm_clone_hash->find_slot_with_hash (h, h->hash, INSERT);
*slot = h;
}
in.base.from = o;
in.hash = htab_hash_pointer (o);
- h = (struct tree_map *) htab_find_with_hash (tm_clone_hash,
- &in, in.hash);
+ h = tm_clone_hash->find_with_hash (&in, in.hash);
if (h)
return h->to;
}
} tm_alias_pair;
-/* Helper function for finish_tm_clone_pairs. Dump a hash table entry
- into a VEC in INFO. */
-
-static int
-dump_tm_clone_to_vec (void **slot, void *info)
-{
- struct tree_map *map = (struct tree_map *) *slot;
- vec<tm_alias_pair> *tm_alias_pairs = (vec<tm_alias_pair> *) info;
- tm_alias_pair p = {DECL_UID (map->base.from), map->base.from, map->to};
- tm_alias_pairs->safe_push (p);
- return 1;
-}
-
/* Dump the actual pairs to the .tm_clone_table section. */
static void
{
tree src = p->from;
tree dst = p->to;
- struct cgraph_node *src_n = cgraph_get_node (src);
- struct cgraph_node *dst_n = cgraph_get_node (dst);
+ struct cgraph_node *src_n = cgraph_node::get (src);
+ struct cgraph_node *dst_n = cgraph_node::get (dst);
/* The function ipa_tm_create_version() marks the clone as needed if
the original function was needed. But we also mark the clone as
}
assemble_integer (XEXP (DECL_RTL (src), 0),
- POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+ POINTER_SIZE_UNITS, POINTER_SIZE, 1);
assemble_integer (XEXP (DECL_RTL (dst), 0),
- POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+ POINTER_SIZE_UNITS, POINTER_SIZE, 1);
}
}
to a vector, sort it, and dump the vector. */
/* Dump the hashtable to a vector. */
- htab_traverse_noresize (tm_clone_hash, dump_tm_clone_to_vec,
- (void *) &tm_alias_pairs);
+ tree_map *map;
+ hash_table<tm_clone_hasher>::iterator iter;
+ FOR_EACH_HASH_TABLE_ELEMENT (*tm_clone_hash, map, tree_map *, iter)
+ {
+ tm_alias_pair p = {DECL_UID (map->base.from), map->base.from, map->to};
+ tm_alias_pairs.safe_push (p);
+ }
/* Sort it. */
tm_alias_pairs.qsort (tm_alias_pair_cmp);
/* Dump it. */
dump_tm_clone_pairs (tm_alias_pairs);
- htab_delete (tm_clone_hash);
+ tm_clone_hash->empty ();
tm_clone_hash = NULL;
tm_alias_pairs.release ();
}
};
const char *name, *type;
+ tree id;
+
+ id = DECL_ASSEMBLER_NAME (decl);
+ ultimate_transparent_alias_target (&id);
+ name = IDENTIFIER_POINTER (id);
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
type = visibility_types[vis];
fprintf (asm_out_file, "\t.%s\t", type);
{
enum symbol_visibility vis = DECL_VISIBILITY (decl);
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && cgraph_node::get (decl)
+ && cgraph_node::get (decl)->instrumentation_clone
+ && cgraph_node::get (decl)->instrumented_version)
+ vis = DECL_VISIBILITY (cgraph_node::get (decl)->instrumented_version->decl);
+
if (vis != VISIBILITY_DEFAULT)
{
targetm.asm_out.assemble_visibility (decl, vis);
void
make_decl_one_only (tree decl, tree comdat_group)
{
+ struct symtab_node *symbol;
gcc_assert (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == FUNCTION_DECL);
TREE_PUBLIC (decl) = 1;
+ if (TREE_CODE (decl) == VAR_DECL)
+ symbol = varpool_node::get_create (decl);
+ else
+ symbol = cgraph_node::get_create (decl);
+
if (SUPPORTS_ONE_ONLY)
{
#ifdef MAKE_DECL_ONE_ONLY
MAKE_DECL_ONE_ONLY (decl);
#endif
- DECL_COMDAT_GROUP (decl) = comdat_group;
+ symbol->set_comdat_group (comdat_group);
}
else if (TREE_CODE (decl) == VAR_DECL
- && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
+ && (DECL_INITIAL (decl) == 0
+ || (!in_lto_p && DECL_INITIAL (decl) == error_mark_node)))
DECL_COMMON (decl) = 1;
else
{
void
init_varasm_once (void)
{
- section_htab = htab_create_ggc (31, section_entry_hash,
- section_entry_eq, NULL);
- object_block_htab = htab_create_ggc (31, object_block_entry_hash,
- object_block_entry_eq, NULL);
- const_desc_htab = htab_create_ggc (1009, const_desc_hash,
- const_desc_eq, NULL);
+ section_htab = hash_table<section_hasher>::create_ggc (31);
+ object_block_htab = hash_table<object_block_hasher>::create_ggc (31);
+ const_desc_htab = hash_table<tree_descriptor_hasher>::create_ggc (1009);
- const_alias_set = new_alias_set ();
shared_constant_pool = create_constant_pool ();
#ifdef TEXT_SECTION_ASM_OP
readonly_data_section = text_section;
#ifdef ASM_OUTPUT_EXTERNAL
- pending_assemble_externals_set = pointer_set_create ();
+ pending_assemble_externals_set = new hash_set<tree>;
#endif
}
flags |= SECTION_RELRO;
}
- if (decl && DECL_P (decl) && DECL_ONE_ONLY (decl))
+ if (decl && DECL_P (decl) && DECL_COMDAT_GROUP (decl))
flags |= SECTION_LINKONCE;
if (strcmp (name, ".vtable_map_vars") == 0)
if (strcmp (name, ".bss") == 0
|| strncmp (name, ".bss.", 5) == 0
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0
+ || strcmp (name, ".persistent.bss") == 0
|| strcmp (name, ".sbss") == 0
|| strncmp (name, ".sbss.", 6) == 0
|| strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
if (!(flags & SECTION_DEBUG))
*f++ = 'a';
+#if defined (HAVE_GAS_SECTION_EXCLUDE) && HAVE_GAS_SECTION_EXCLUDE == 1
if (flags & SECTION_EXCLUDE)
*f++ = 'e';
+#endif
if (flags & SECTION_WRITE)
*f++ = 'w';
if (flags & SECTION_CODE)
bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP;
const char *prefix, *name, *linkonce;
char *string;
+ tree id;
switch (categorize_decl_for_section (decl, reloc))
{
gcc_unreachable ();
}
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ id = DECL_ASSEMBLER_NAME (decl);
+ ultimate_transparent_alias_target (&id);
+ name = IDENTIFIER_POINTER (id);
name = targetm.strip_name_encoding (name);
/* If we're using one_only, then there needs to be a .gnu.linkonce
string = ACONCAT ((linkonce, prefix, ".", name, NULL));
- DECL_SECTION_NAME (decl) = build_string (strlen (string), string);
+ set_decl_section_name (decl, string);
}
-/* Like compute_reloc_for_constant, except for an RTX. The return value
- is a mask for which bit 1 indicates a global relocation, and bit 0
- indicates a local relocation. */
+/* Subroutine of compute_reloc_for_rtx for leaf rtxes. */
static int
-compute_reloc_for_rtx_1 (rtx *xp, void *data)
+compute_reloc_for_rtx_1 (const_rtx x)
{
- int *preloc = (int *) data;
- rtx x = *xp;
-
switch (GET_CODE (x))
{
case SYMBOL_REF:
- *preloc |= SYMBOL_REF_LOCAL_P (x) ? 1 : 2;
- break;
+ return SYMBOL_REF_LOCAL_P (x) ? 1 : 2;
case LABEL_REF:
- *preloc |= 1;
- break;
+ return 1;
default:
- break;
+ return 0;
}
-
- return 0;
}
+/* Like compute_reloc_for_constant, except for an RTX. The return value
+ is a mask for which bit 1 indicates a global relocation, and bit 0
+ indicates a local relocation. */
+
static int
-compute_reloc_for_rtx (rtx x)
+compute_reloc_for_rtx (const_rtx x)
{
- int reloc;
-
switch (GET_CODE (x))
{
- case CONST:
case SYMBOL_REF:
case LABEL_REF:
- reloc = 0;
- for_each_rtx (&x, compute_reloc_for_rtx_1, &reloc);
- return reloc;
+ return compute_reloc_for_rtx_1 (x);
+
+ case CONST:
+ {
+ int reloc = 0;
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, ALL)
+ reloc |= compute_reloc_for_rtx_1 (*iter);
+ return reloc;
+ }
default:
return 0;
}
section *
-default_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+default_select_rtx_section (machine_mode mode ATTRIBUTE_UNUSED,
rtx x,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
}
section *
-default_elf_select_rtx_section (enum machine_mode mode, rtx x,
+default_elf_select_rtx_section (machine_mode mode, rtx x,
unsigned HOST_WIDE_INT align)
{
int reloc = compute_reloc_for_rtx (x);
|| resolution == LDPR_RESOLVED_EXEC);
}
-/* Assume ELF-ish defaults, since that's pretty much the most liberal
- wrt cross-module name binding. */
-
-bool
-default_binds_local_p (const_tree exp)
-{
- return default_binds_local_p_1 (exp, flag_shlib);
-}
+/* COMMON_LOCAL_P is true means that the linker can guarantee that an
+ uninitialized common symbol in the executable will still be defined
+ (through COPY relocation) in the executable. */
bool
-default_binds_local_p_1 (const_tree exp, int shlib)
+default_binds_local_p_3 (const_tree exp, bool shlib, bool weak_dominate,
+ bool extern_protected_data, bool common_local_p)
{
- bool local_p;
- bool resolved_locally = false;
- bool resolved_to_local_def = false;
-
- /* With resolution file in hands, take look into resolutions.
- We can't just return true for resolved_locally symbols,
- because dynamic linking might overwrite symbols
- in shared libraries. */
- if (TREE_CODE (exp) == VAR_DECL && TREE_PUBLIC (exp)
- && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
- {
- struct varpool_node *vnode = varpool_get_node (exp);
- if (vnode && resolution_local_p (vnode->resolution))
- resolved_locally = true;
- if (vnode
- && resolution_to_local_definition_p (vnode->resolution))
- resolved_to_local_def = true;
- }
- else if (TREE_CODE (exp) == FUNCTION_DECL && TREE_PUBLIC (exp))
- {
- struct cgraph_node *node = cgraph_get_node (exp);
- if (node
- && resolution_local_p (node->resolution))
- resolved_locally = true;
- if (node
- && resolution_to_local_definition_p (node->resolution))
- resolved_to_local_def = true;
- }
-
/* A non-decl is an entry in the constant pool. */
if (!DECL_P (exp))
- local_p = true;
+ return true;
+
/* Weakrefs may not bind locally, even though the weakref itself is always
static and therefore local. Similarly, the resolver for ifunc functions
might resolve to a non-local function.
FIXME: We can resolve the weakref case more curefuly by looking at the
weakref alias. */
- else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp))
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (exp))
|| (TREE_CODE (exp) == FUNCTION_DECL
&& lookup_attribute ("ifunc", DECL_ATTRIBUTES (exp))))
- local_p = false;
+ return false;
+
/* Static variables are always local. */
- else if (! TREE_PUBLIC (exp))
- local_p = true;
- /* A variable is local if the user has said explicitly that it will
- be. */
- else if ((DECL_VISIBILITY_SPECIFIED (exp)
- || resolved_to_local_def)
- && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
- local_p = true;
- /* Variables defined outside this object might not be local. */
- else if (DECL_EXTERNAL (exp) && !resolved_locally)
- local_p = false;
- /* If defined in this object and visibility is not default, must be
- local. */
- else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT)
- local_p = true;
- /* Default visibility weak data can be overridden by a strong symbol
- in another module and so are not local. */
- else if (DECL_WEAK (exp)
- && !resolved_locally)
- local_p = false;
+ if (! TREE_PUBLIC (exp))
+ return true;
+
+ /* With resolution file in hand, take look into resolutions.
+ We can't just return true for resolved_locally symbols,
+ because dynamic linking might overwrite symbols
+ in shared libraries. */
+ bool resolved_locally = false;
+
+ bool uninited_common = (DECL_COMMON (exp)
+ && (DECL_INITIAL (exp) == NULL
+ || (!in_lto_p
+ && DECL_INITIAL (exp) == error_mark_node)));
+
+ /* A non-external variable is defined locally only if it isn't
+ uninitialized COMMON variable or common_local_p is true. */
+ bool defined_locally = (!DECL_EXTERNAL (exp)
+ && (!uninited_common || common_local_p));
+ if (symtab_node *node = symtab_node::get (exp))
+ {
+ if (node->in_other_partition)
+ defined_locally = true;
+ if (resolution_to_local_definition_p (node->resolution))
+ defined_locally = resolved_locally = true;
+ else if (resolution_local_p (node->resolution))
+ resolved_locally = true;
+ }
+ if (defined_locally && weak_dominate && !shlib)
+ resolved_locally = true;
+
+ /* Undefined weak symbols are never defined locally. */
+ if (DECL_WEAK (exp) && !defined_locally)
+ return false;
+
+ /* A symbol is local if the user has said explicitly that it will be,
+ or if we have a definition for the symbol. We cannot infer visibility
+ for undefined symbols. */
+ if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT
+ && (TREE_CODE (exp) == FUNCTION_DECL
+ || !extern_protected_data
+ || DECL_VISIBILITY (exp) != VISIBILITY_PROTECTED)
+ && (DECL_VISIBILITY_SPECIFIED (exp) || defined_locally))
+ return true;
+
/* If PIC, then assume that any global name can be overridden by
symbols resolved from other modules. */
- else if (shlib)
- local_p = false;
+ if (shlib)
+ return false;
+
+ /* Variables defined outside this object might not be local. */
+ if (DECL_EXTERNAL (exp) && !resolved_locally)
+ return false;
+
+ /* Non-dominant weak symbols are not defined locally. */
+ if (DECL_WEAK (exp) && !resolved_locally)
+ return false;
+
/* Uninitialized COMMON variable may be unified with symbols
resolved from other modules. */
- else if (DECL_COMMON (exp)
- && !resolved_locally
- && (DECL_INITIAL (exp) == NULL
- || DECL_INITIAL (exp) == error_mark_node))
- local_p = false;
+ if (uninited_common && !resolved_locally)
+ return false;
+
/* Otherwise we're left with initialized (or non-common) global data
which is of necessity defined locally. */
- else
- local_p = true;
+ return true;
+}
+
+/* Assume ELF-ish defaults, since that's pretty much the most liberal
+ wrt cross-module name binding. */
+
+bool
+default_binds_local_p (const_tree exp)
+{
+ return default_binds_local_p_3 (exp, flag_shlib != 0, true, false, false);
+}
+
+/* Similar to default_binds_local_p, but common symbol may be local. */
+
+bool
+default_binds_local_p_2 (const_tree exp)
+{
+ return default_binds_local_p_3 (exp, flag_shlib != 0, true, false,
+ !flag_pic);
+}
- return local_p;
+bool
+default_binds_local_p_1 (const_tree exp, int shlib)
+{
+ return default_binds_local_p_3 (exp, shlib != 0, false, false, false);
}
/* Return true when references to DECL must bind to current definition in
definition from different object file) and when resolution info is available
we simply use the knowledge passed to us by linker plugin. */
bool
-decl_binds_to_current_def_p (tree decl)
+decl_binds_to_current_def_p (const_tree decl)
{
gcc_assert (DECL_P (decl));
if (!targetm.binds_local_p (decl))
return false;
if (!TREE_PUBLIC (decl))
return true;
+
/* When resolution is available, just use it. */
- if (TREE_CODE (decl) == VAR_DECL
- && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
- {
- struct varpool_node *vnode = varpool_get_node (decl);
- if (vnode
- && vnode->resolution != LDPR_UNKNOWN)
- return resolution_to_local_definition_p (vnode->resolution);
- }
- else if (TREE_CODE (decl) == FUNCTION_DECL)
+ if (symtab_node *node = symtab_node::get (decl))
{
- struct cgraph_node *node = cgraph_get_node (decl);
- if (node
- && node->resolution != LDPR_UNKNOWN)
+ if (node->resolution != LDPR_UNKNOWN)
return resolution_to_local_definition_p (node->resolution);
}
+
/* Otherwise we have to assume the worst for DECL_WEAK (hidden weaks
binds locally but still can be overwritten), DECL_COMMON (can be merged
with a non-common definition somewhere in the same module) or
return false;
if (DECL_COMMON (decl)
&& (DECL_INITIAL (decl) == NULL
- || DECL_INITIAL (decl) == error_mark_node))
+ || (!in_lto_p && DECL_INITIAL (decl) == error_mark_node)))
return false;
if (DECL_EXTERNAL (decl))
return false;
gcc_assert (DECL_P (decl));
if (!TREE_PUBLIC (decl) || DECL_COMDAT (decl))
return false;
+ if (!flag_semantic_interposition
+ && !DECL_WEAK (decl))
+ return false;
return !decl_binds_to_current_def_p (decl);
}
fputs (ASM_APP_OFF, asm_out_file);
if (targetm.asm_file_start_file_directive)
- output_file_directive (asm_out_file, main_input_filename);
+ {
+ /* LTO produced units have no meaningful main_input_filename. */
+ if (in_lto_p)
+ output_file_directive (asm_out_file, "<artificial>");
+ else
+ output_file_directive (asm_out_file, main_input_filename);
+ }
}
/* This is a generic routine suitable for use as TARGET_ASM_FILE_END
else if (TREE_CONSTANT_POOL_ADDRESS_P (symbol))
{
decl = SYMBOL_REF_DECL (symbol);
+ gcc_checking_assert (DECL_IN_CONSTANT_POOL (decl));
alignment = DECL_ALIGN (decl);
size = get_constant_size (DECL_INITIAL (decl));
if ((flag_sanitize & SANITIZE_ADDRESS)
}
else
{
+ struct symtab_node *snode;
decl = SYMBOL_REF_DECL (symbol);
+
+ snode = symtab_node::get (decl);
+ if (snode->alias)
+ {
+ rtx target = DECL_RTL (snode->ultimate_alias_target ()->decl);
+
+ gcc_assert (MEM_P (target)
+ && GET_CODE (XEXP (target, 0)) == SYMBOL_REF
+ && SYMBOL_REF_HAS_BLOCK_INFO_P (XEXP (target, 0)));
+ target = XEXP (target, 0);
+ place_block_symbol (target);
+ SYMBOL_REF_BLOCK_OFFSET (symbol) = SYMBOL_REF_BLOCK_OFFSET (target);
+ return;
+ }
alignment = get_variable_align (decl);
size = tree_to_uhwi (DECL_SIZE_UNIT (decl));
if ((flag_sanitize & SANITIZE_ADDRESS)
offset = 0;
else
{
- bias = 1 << (GET_MODE_BITSIZE (ptr_mode) - 1);
+ bias = HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (ptr_mode) - 1);
if (offset < 0)
{
delta = -(unsigned HOST_WIDE_INT) offset + max_offset;
if (CONSTANT_POOL_ADDRESS_P (symbol))
{
desc = SYMBOL_REF_CONSTANT (symbol);
+ /* Pass 1 for align as we have already laid out everything in the block.
+ So aligning shouldn't be necessary. */
output_constant_pool_1 (desc, 1);
offset += GET_MODE_SIZE (desc->mode);
}
{
HOST_WIDE_INT size;
decl = SYMBOL_REF_DECL (symbol);
- assemble_constant_contents (DECL_INITIAL (decl), XSTR (symbol, 0),
- DECL_ALIGN (decl));
+ assemble_constant_contents
+ (DECL_INITIAL (decl), XSTR (symbol, 0), DECL_ALIGN (decl));
+
size = get_constant_size (DECL_INITIAL (decl));
offset += size;
if ((flag_sanitize & SANITIZE_ADDRESS)
}
}
-/* A htab_traverse callback used to call output_object_block for
- each member of object_block_htab. */
+/* A callback for qsort to compare object_blocks. */
static int
-output_object_block_htab (void **slot, void *data ATTRIBUTE_UNUSED)
+output_object_block_compare (const void *x, const void *y)
{
- output_object_block ((struct object_block *) (*slot));
- return 1;
+ object_block *p1 = *(object_block * const*)x;
+ object_block *p2 = *(object_block * const*)y;
+
+ if (p1->sect->common.flags & SECTION_NAMED
+ && !(p2->sect->common.flags & SECTION_NAMED))
+ return 1;
+
+ if (!(p1->sect->common.flags & SECTION_NAMED)
+ && p2->sect->common.flags & SECTION_NAMED)
+ return -1;
+
+ if (p1->sect->common.flags & SECTION_NAMED
+ && p2->sect->common.flags & SECTION_NAMED)
+ return strcmp (p1->sect->named.name, p2->sect->named.name);
+
+ unsigned f1 = p1->sect->common.flags;
+ unsigned f2 = p2->sect->common.flags;
+ if (f1 == f2)
+ return 0;
+ return f1 < f2 ? -1 : 1;
}
/* Output the definitions of all object_blocks. */
void
output_object_blocks (void)
{
- htab_traverse (object_block_htab, output_object_block_htab, NULL);
+ vec<object_block *, va_heap> v;
+ v.create (object_block_htab->elements ());
+ object_block *obj;
+ hash_table<object_block_hasher>::iterator hi;
+
+ FOR_EACH_HASH_TABLE_ELEMENT (*object_block_htab, obj, object_block *, hi)
+ v.quick_push (obj);
+
+ /* Sort them in order to output them in a deterministic manner,
+ otherwise we may get .rodata sections in different orders with
+ and without -g. */
+ v.qsort (output_object_block_compare);
+ unsigned i;
+ FOR_EACH_VEC_ELT (v, i, obj)
+ output_object_block (obj);
+
+ v.release ();
}
/* This function provides a possible implementation of the
{
/* We output the name if and only if TREE_SYMBOL_REFERENCED is
set in order to avoid putting out names that are never really
- used. */
+ used. Always output visibility specified in the source. */
if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
- && targetm.binds_local_p (decl))
+ && (DECL_VISIBILITY_SPECIFIED (decl)
+ || targetm.binds_local_p (decl)))
maybe_assemble_visibility (decl);
}
make_debug_expr_from_rtl (const_rtx exp)
{
tree ddecl = make_node (DEBUG_EXPR_DECL), type;
- enum machine_mode mode = GET_MODE (exp);
+ machine_mode mode = GET_MODE (exp);
rtx dval;
DECL_ARTIFICIAL (ddecl) = 1;
to asm_out_file. Instead, add a fake top-level asm statement.
This allows the front ends to use this hook without actually
writing to asm_out_file, to handle #ident or Pragma Ident. */
- if (cgraph_state == CGRAPH_STATE_PARSING)
+ if (symtab->state == PARSING)
{
char *buf = ACONCAT ((ident_asm_op, "\"", ident_str, "\"\n", NULL));
- add_asm_node (build_string (strlen (buf), buf));
+ symtab->finalize_toplevel_asm (build_string (strlen (buf), buf));
}
else
fprintf (asm_out_file, "%s\"%s\"\n", ident_asm_op, ident_str);