}
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-
- if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
+
+ if (name[0] != '*' && TREE_CODE (decl) != FUNCTION_DECL
+ && DECL_REGISTER (decl))
{
- reg_number = decode_reg_name (name);
+ error ("register name not specified for %q+D", 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 ("register name not specified for %q+D", decl);
/* Standard thing is just output label for the function. */
ASM_OUTPUT_LABEL (asm_out_file, fnname);
#endif /* ASM_DECLARE_FUNCTION_NAME */
-
- /* Add NOTE_INSN_SWITCH_TEXT_SECTIONS notes. Don't do this if the current
- function is a thunk, because we don't have a CFG in that case. */
- if (!current_function_is_thunk)
- insert_section_boundary_note ();
}
/* Output assembler code associated with defining the size of the
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)
which do not need to be marked. */
}
+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
id = maybe_get_identifier (real_name);
if (id)
- mark_referenced (id);
+ {
+ mark_referenced (id);
+ ultimate_transparent_alias_target (&id);
+ name = IDENTIFIER_POINTER (id);
+ gcc_assert (! TREE_CHAIN (id));
+ }
assemble_name_raw (file, name);
}
if (value
&& TREE_CODE (value) == FUNCTION_DECL
&& ((decl_function_context (value) && !DECL_NO_STATIC_CHAIN (value))
- || DECL_NON_ADDR_CONST_P (value)))
+ || DECL_DLLIMPORT_P (value)))
return NULL_TREE;
return value;
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));
mark_weak (decl);
}
-/* Emit any pending weak declarations. */
-
-void
-weak_finish (void)
+static void
+weak_finish_1 (tree decl)
{
- tree t;
-
- for (t = weak_decls; t; t = TREE_CHAIN (t))
- {
- tree decl = TREE_VALUE (t);
#if defined (ASM_WEAKEN_DECL) || defined (ASM_WEAKEN_LABEL)
- const char *const name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ const char *const name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
#endif
- if (! TREE_USED (decl))
- continue;
+ if (! TREE_USED (decl))
+ return;
+
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))
+ && lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
+ return;
#ifdef ASM_WEAKEN_DECL
- ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL);
+ ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL);
#else
#ifdef ASM_WEAKEN_LABEL
- ASM_WEAKEN_LABEL (asm_out_file, name);
+ ASM_WEAKEN_LABEL (asm_out_file, name);
#else
#ifdef ASM_OUTPUT_WEAK_ALIAS
- warning (0, "only weak aliases are supported in this configuration");
- return;
+ {
+ static bool warn_once = 0;
+ if (! warn_once)
+ {
+ warning (0, "only weak aliases are supported in this configuration");
+ warn_once = 1;
+ }
+ return;
+ }
#endif
#endif
#endif
+}
+
+/* This TREE_LIST contains weakref targets. */
+
+static GTY(()) tree weakref_targets;
+
+/* Forward declaration. */
+static tree find_decl_and_mark_needed (tree decl, tree target);
+
+/* Emit any pending weak declarations. */
+
+void
+weak_finish (void)
+{
+ tree t;
+
+ for (t = weakref_targets; t; t = TREE_CHAIN (t))
+ {
+ tree alias_decl = TREE_PURPOSE (t);
+ tree target = ultimate_transparent_alias_target (&TREE_VALUE (t));
+
+ if (! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias_decl)))
+ /* Remove alias_decl from the weak list, but leave entries for
+ the target alone. */
+ target = NULL_TREE;
+#ifndef ASM_OUTPUT_WEAKREF
+ else if (! TREE_SYMBOL_REFERENCED (target))
+ {
+# ifdef ASM_WEAKEN_LABEL
+ ASM_WEAKEN_LABEL (asm_out_file, IDENTIFIER_POINTER (target));
+# else
+ tree decl = find_decl_and_mark_needed (alias_decl, target);
+
+ if (! decl)
+ {
+ decl = build_decl (TREE_CODE (alias_decl), target,
+ TREE_TYPE (alias_decl));
+
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ TREE_NOTHROW (decl) = TREE_NOTHROW (alias_decl);
+ TREE_USED (decl) = 1;
+ }
+
+ weak_finish_1 (decl);
+# endif
+ }
+#endif
+
+ {
+ tree *p;
+ tree t2;
+
+ /* Remove the alias and the target from the pending weak list
+ so that we do not emit any .weak directives for the former,
+ nor multiple .weak directives for the latter. */
+ for (p = &weak_decls; (t2 = *p) ; )
+ {
+ if (TREE_VALUE (t2) == alias_decl
+ || target == DECL_ASSEMBLER_NAME (TREE_VALUE (t2)))
+ *p = TREE_CHAIN (t2);
+ else
+ p = &TREE_CHAIN (t2);
+ }
+
+ /* Remove other weakrefs to the same target, to speed things up. */
+ for (p = &TREE_CHAIN (t); (t2 = *p) ; )
+ {
+ if (target == ultimate_transparent_alias_target (&TREE_VALUE (t2)))
+ *p = TREE_CHAIN (t2);
+ else
+ p = &TREE_CHAIN (t2);
+ }
+ }
+ }
+
+ for (t = weak_decls; t; t = TREE_CHAIN (t))
+ {
+ tree decl = TREE_VALUE (t);
+
+ weak_finish_1 (decl);
}
}
else
p = &TREE_CHAIN (t);
}
+
+ /* Remove weakrefs to the same target from the pending weakref
+ list, for the same reason. */
+ for (p = &weakref_targets; (t = *p) ; )
+ {
+ if (DECL_ASSEMBLER_NAME (decl)
+ == ultimate_transparent_alias_target (&TREE_VALUE (t)))
+ *p = TREE_CHAIN (t);
+ else
+ p = &TREE_CHAIN (t);
+ }
+
return;
}
#elif defined(ASM_MAKE_LABEL_LINKONCE)
struct cgraph_node *fnode = NULL;
struct cgraph_varpool_node *vnode = NULL;
- /* C++ thunk emitting code produces aliases late in the game.
- Avoid confusing cgraph code in that case. */
- if (!cgraph_global_info_ready)
+ if (TREE_CODE (decl) == FUNCTION_DECL)
{
- 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);
- }
+ 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)
{
- cgraph_mark_needed_node (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)
tree node is DECL to have the value of the tree node TARGET. */
static void
-do_assemble_alias (tree decl, tree target ATTRIBUTE_UNUSED)
+do_assemble_alias (tree decl, tree target)
{
if (TREE_ASM_WRITTEN (decl))
return;
TREE_ASM_WRITTEN (decl) = 1;
TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 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,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ IDENTIFIER_POINTER (target));
+#else
+ if (!SUPPORTS_WEAK)
+ {
+ error ("%Jweakref is not supported in this configuration", decl);
+ return;
+ }
+#endif
+ return;
+ }
+
#ifdef ASM_OUTPUT_DEF
/* Make name accessible from other files, if appropriate. */
*p = TREE_CHAIN (t);
else
p = &TREE_CHAIN (t);
+
+ /* Remove weakrefs to the same target from the pending weakref
+ list, for the same reason. */
+ for (p = &weakref_targets; (t = *p) ; )
+ {
+ if (DECL_ASSEMBLER_NAME (decl)
+ == ultimate_transparent_alias_target (&TREE_VALUE (t)))
+ *p = TREE_CHAIN (t);
+ else
+ p = &TREE_CHAIN (t);
+ }
}
#endif
}
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))
+ {
+ if (! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
+ error ("%q+D aliased to undefined symbol %qs",
+ p->decl, IDENTIFIER_POINTER (p->target));
+ }
+ else if (DECL_EXTERNAL (target_decl)
+ && ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
error ("%q+D aliased to external symbol %qs",
p->decl, IDENTIFIER_POINTER (p->target));
}
assemble_alias (tree decl, tree target)
{
tree target_decl;
+ bool is_weakref = false;
+
+ if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
+ {
+ tree alias = DECL_ASSEMBLER_NAME (decl);
+
+ is_weakref = true;
+
+ ultimate_transparent_alias_target (&target);
+ if (alias == target)
+ error ("%Jweakref %qD ultimately targets itself", decl, decl);
+ else
+ {
+#ifndef ASM_OUTPUT_WEAKREF
+ IDENTIFIER_TRANSPARENT_ALIAS (alias) = 1;
+ TREE_CHAIN (alias) = target;
+#endif
+ }
+ }
+ else
+ {
#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);
+ 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. */
/* 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;
+ if (! is_weakref)
+ DECL_EXTERNAL (decl) = 0;
/* Allow aliases to aliases. */
if (TREE_CODE (decl) == FUNCTION_DECL)