OSDN Git Service

PR other/4372
[pf3gnuchains/gcc-fork.git] / gcc / varasm.c
index 3c1eb11..f9fa0fd 100644 (file)
@@ -60,6 +60,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #endif
 
 /* The (assembler) name of the first globally-visible object output.  */
+extern GTY(()) const char *first_global_object_name;
+extern GTY(()) const char *weak_global_object_name;
+
 const char *first_global_object_name;
 const char *weak_global_object_name;
 
@@ -464,7 +467,7 @@ named_section (tree decl, const char *name, int reloc)
     {
       flags = get_named_section_flags (name);
       if ((flags & SECTION_OVERRIDE) == 0)
-       error ("%J%D causes a section type conflict", decl, decl);
+       error ("%+D causes a section type conflict", decl);
     }
 
   named_section_real (name, flags, decl);
@@ -841,7 +844,8 @@ decode_reg_name (const char *asmspec)
          = ADDITIONAL_REGISTER_NAMES;
 
        for (i = 0; i < (int) ARRAY_SIZE (table); i++)
-         if (! strcmp (asmspec, table[i].name))
+         if (table[i].name[0]
+             && ! strcmp (asmspec, table[i].name))
            return table[i].number;
       }
 #endif /* ADDITIONAL_REGISTER_NAMES */
@@ -919,21 +923,27 @@ make_decl_rtl (tree decl)
     }
 
   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 ("%Jregister name not specified for %qD", decl, decl);
+       error ("register name not specified for %q+D", decl);
       else if (reg_number < 0)
-       error ("%Jinvalid register name for %qD", decl, decl);
+       error ("invalid register name for %q+D", decl);
       else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
-       error ("%Jdata type of %qD isn%'t suitable for a register",
-              decl, decl);
+       error ("data type of %q+D isn%'t suitable for a register",
+              decl);
       else if (! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl))))
-       error ("%Jregister specified for %qD isn%'t suitable for data type",
-               decl, decl);
+       error ("register specified for %q+D isn%'t suitable for data type",
+               decl);
       /* Now handle properly declared static register variables.  */
       else
        {
@@ -983,7 +993,7 @@ make_decl_rtl (tree decl)
       {
        reg_number = decode_reg_name (name);
        if (reg_number >= 0 || reg_number == -3)
-         error ("%Jregister name given for non-register variable %qD", decl, decl);
+         error ("register name given for non-register variable %q+D", decl);
       }
 #endif
   }
@@ -1207,11 +1217,11 @@ notice_global_symbol (tree decl)
   if (!*type)
     {
       const char *p;
-      char *name;
+      const char *name;
       rtx decl_rtl = DECL_RTL (decl);
 
       p = targetm.strip_name_encoding (XSTR (XEXP (decl_rtl, 0), 0));
-      name = xstrdup (p);
+      name = ggc_strdup (p);
 
       *type = name;
     }
@@ -1271,11 +1281,13 @@ assemble_start_function (tree decl, const char *fnname)
       unlikely_text_section ();
       assemble_align (FUNCTION_BOUNDARY);
       ASM_OUTPUT_LABEL (asm_out_file, cfun->cold_section_label);
-      if (BB_PARTITION (ENTRY_BLOCK_PTR->next_bb) == BB_COLD_PARTITION)
+
+      /* When the function starts with a cold section, we need to explicitly
+        align the hot section and write out the hot section label.
+        But if the current function is a thunk, we do not have a CFG.  */
+      if (!current_function_is_thunk
+         && BB_PARTITION (ENTRY_BLOCK_PTR->next_bb) == BB_COLD_PARTITION)
        {
-         /* Since the function starts with a cold section, we need to
-            explicitly align the hot section and write out the hot
-            section label.  */
          text_section ();
          assemble_align (FUNCTION_BOUNDARY);
          ASM_OUTPUT_LABEL (asm_out_file, cfun->hot_section_label);
@@ -1357,8 +1369,6 @@ assemble_start_function (tree decl, const char *fnname)
   /* Standard thing is just output label for the function.  */
   ASM_OUTPUT_LABEL (asm_out_file, fnname);
 #endif /* ASM_DECLARE_FUNCTION_NAME */
-
-  insert_section_boundary_note ();
 }
 
 /* Output assembler code associated with defining the size of the
@@ -1368,6 +1378,9 @@ void
 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)
@@ -1629,7 +1642,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
 
   if (!dont_output_data && DECL_SIZE (decl) == 0)
     {
-      error ("%Jstorage size of %qD isn%'t known", decl, decl);
+      error ("storage size of %q+D isn%'t known", decl);
       TREE_ASM_WRITTEN (decl) = 1;
       return;
     }
@@ -1657,7 +1670,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   if (! dont_output_data
       && ! host_integerp (DECL_SIZE_UNIT (decl), 1))
     {
-      error ("%Jsize of variable %qD is too large", decl, decl);
+      error ("size of variable %q+D is too large", decl);
       return;
     }
 
@@ -1680,8 +1693,8 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
      In particular, a.out format supports a maximum alignment of 4.  */
   if (align > MAX_OFILE_ALIGNMENT)
     {
-      warning (0, "%Jalignment of %qD is greater than maximum object "
-               "file alignment.  Using %d", decl, decl,
+      warning (0, "alignment of %q+D is greater than maximum object "
+               "file alignment.  Using %d", decl,
               MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
       align = MAX_OFILE_ALIGNMENT;
     }
@@ -1716,7 +1729,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   if (DECL_SECTION_NAME (decl) || dont_output_data)
     ;
   /* We don't implement common thread-local data at present.  */
-  else if (DECL_THREAD_LOCAL (decl))
+  else if (DECL_THREAD_LOCAL_P (decl))
     {
       if (DECL_COMMON (decl))
        sorry ("thread-local COMMON data not implemented");
@@ -1744,8 +1757,8 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
 
 #if !defined(ASM_OUTPUT_ALIGNED_COMMON) && !defined(ASM_OUTPUT_ALIGNED_DECL_COMMON) && !defined(ASM_OUTPUT_ALIGNED_BSS)
       if ((unsigned HOST_WIDE_INT) DECL_ALIGN_UNIT (decl) > rounded)
-       warning (0, "%Jrequested alignment for %qD is greater than "
-                 "implemented alignment of %d", decl, decl, rounded);
+       warning (0, "requested alignment for %q+D is greater than "
+                 "implemented alignment of %wu", decl, rounded);
 #endif
 
       /* If the target cannot output uninitialized but not common global data
@@ -1982,6 +1995,23 @@ mark_decl_referenced (tree decl)
      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
@@ -2011,7 +2041,12 @@ assemble_name (FILE *file, const char *name)
 
   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);
 }
@@ -2400,13 +2435,14 @@ const_hash_1 (const tree exp)
 
     case CONSTRUCTOR:
       {
-       tree link;
+       unsigned HOST_WIDE_INT idx;
+       tree value;
        
        hi = 5 + int_size_in_bytes (TREE_TYPE (exp));
        
-       for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-         if (TREE_VALUE (link))
-           hi = hi * 603 + const_hash_1 (TREE_VALUE (link));
+       FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
+         if (value)
+           hi = hi * 603 + const_hash_1 (value);
        
        return hi;
       }
@@ -2517,7 +2553,8 @@ compare_constant (const tree t1, const tree t2)
 
     case CONSTRUCTOR:
       {
-       tree l1, l2;
+       VEC(constructor_elt, gc) *v1, *v2;
+       unsigned HOST_WIDE_INT idx;
        
        typecode = TREE_CODE (TREE_TYPE (t1));
        if (typecode != TREE_CODE (TREE_TYPE (t2)))
@@ -2540,28 +2577,34 @@ compare_constant (const tree t1, const tree t2)
              return 0;
          }
 
-       for (l1 = CONSTRUCTOR_ELTS (t1), l2 = CONSTRUCTOR_ELTS (t2);
-            l1 && l2;
-            l1 = TREE_CHAIN (l1), l2 = TREE_CHAIN (l2))
+       v1 = CONSTRUCTOR_ELTS (t1);
+       v2 = CONSTRUCTOR_ELTS (t2);
+       if (VEC_length (constructor_elt, v1)
+           != VEC_length (constructor_elt, v2))
+           return 0;
+
+       for (idx = 0; idx < VEC_length (constructor_elt, v1); ++idx)
          {
+           constructor_elt *c1 = VEC_index (constructor_elt, v1, idx);
+           constructor_elt *c2 = VEC_index (constructor_elt, v2, idx);
+
            /* Check that each value is the same...  */
-           if (! compare_constant (TREE_VALUE (l1), TREE_VALUE (l2)))
+           if (!compare_constant (c1->value, c2->value))
              return 0;
            /* ... and that they apply to the same fields!  */
            if (typecode == ARRAY_TYPE)
              {
-               if (! compare_constant (TREE_PURPOSE (l1),
-                                       TREE_PURPOSE (l2)))
+               if (!compare_constant (c1->index, c2->index))
                  return 0;
              }
            else
              {
-               if (TREE_PURPOSE (l1) != TREE_PURPOSE (l2))
+               if (c1->index != c2->index)
                  return 0;
              }
          }
        
-       return l1 == NULL_TREE && l2 == NULL_TREE;
+       return 1;
       }
 
     case ADDR_EXPR:
@@ -2645,13 +2688,19 @@ copy_constant (tree exp)
     case CONSTRUCTOR:
       {
        tree copy = copy_node (exp);
-       tree list = copy_list (CONSTRUCTOR_ELTS (exp));
-       tree tail;
-
-       CONSTRUCTOR_ELTS (copy) = list;
-       for (tail = list; tail; tail = TREE_CHAIN (tail))
-         TREE_VALUE (tail) = copy_constant (TREE_VALUE (tail));
-
+       VEC(constructor_elt, gc) *v;
+       unsigned HOST_WIDE_INT idx;
+       tree purpose, value;
+       
+       v = VEC_alloc(constructor_elt, gc, VEC_length(constructor_elt,
+                                                     CONSTRUCTOR_ELTS (exp)));
+       FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, purpose, value)
+         {
+           constructor_elt *ce = VEC_quick_push (constructor_elt, v, NULL);
+           ce->index = purpose;
+           ce->value = copy_constant (value);
+         }
+       CONSTRUCTOR_ELTS (copy) = v;
        return copy;
       }
 
@@ -3476,10 +3525,12 @@ compute_reloc_for_constant (tree exp)
       break;
 
     case CONSTRUCTOR:
-      for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
-       if (TREE_VALUE (tem) != 0)
-         reloc |= compute_reloc_for_constant (TREE_VALUE (tem));
-
+      {
+       unsigned HOST_WIDE_INT idx;
+       FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+         if (tem != 0)
+           reloc |= compute_reloc_for_constant (tem);
+      }
       break;
 
     default:
@@ -3533,10 +3584,12 @@ output_addressed_constants (tree exp)
       break;
 
     case CONSTRUCTOR:
-      for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
-       if (TREE_VALUE (tem) != 0)
-         output_addressed_constants (TREE_VALUE (tem));
-
+      {
+       unsigned HOST_WIDE_INT idx;
+       FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, tem)
+         if (tem != 0)
+           output_addressed_constants (tem);
+      }
       break;
 
     default:
@@ -3567,16 +3620,16 @@ initializer_constant_valid_p (tree value, tree endtype)
       if ((TREE_CODE (TREE_TYPE (value)) == UNION_TYPE
           || TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE)
          && TREE_CONSTANT (value)
-         && CONSTRUCTOR_ELTS (value))
+         && !VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (value)))
        {
+         unsigned HOST_WIDE_INT idx;
          tree elt;
          bool absolute = true;
 
-         for (elt = CONSTRUCTOR_ELTS (value); elt; elt = TREE_CHAIN (elt))
+         FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (value), idx, elt)
            {
              tree reloc;
-             value = TREE_VALUE (elt);
-             reloc = initializer_constant_valid_p (value, TREE_TYPE (value));
+             reloc = initializer_constant_valid_p (elt, TREE_TYPE (elt));
              if (!reloc)
                return NULL_TREE;
              if (reloc != null_pointer_node)
@@ -3610,7 +3663,7 @@ initializer_constant_valid_p (tree value, tree endtype)
       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;
 
@@ -3820,19 +3873,58 @@ output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align)
   if (size == 0 || flag_syntax_only)
     return;
 
+  /* See if we're trying to initialize a pointer in a non-default mode
+     to the address of some declaration somewhere.  If the target says
+     the mode is valid for pointers, assume the target has a way of
+     resolving it.  */
+  if (TREE_CODE (exp) == NOP_EXPR
+      && POINTER_TYPE_P (TREE_TYPE (exp))
+      && targetm.valid_pointer_mode (TYPE_MODE (TREE_TYPE (exp))))
+    {
+      tree saved_type = TREE_TYPE (exp);
+
+      /* Peel off any intermediate conversions-to-pointer for valid
+        pointer modes.  */
+      while (TREE_CODE (exp) == NOP_EXPR
+            && POINTER_TYPE_P (TREE_TYPE (exp))
+            && targetm.valid_pointer_mode (TYPE_MODE (TREE_TYPE (exp))))
+       exp = TREE_OPERAND (exp, 0);
+
+      /* If what we're left with is the address of something, we can
+        convert the address to the final type and output it that
+        way.  */
+      if (TREE_CODE (exp) == ADDR_EXPR)
+       exp = build1 (ADDR_EXPR, saved_type, TREE_OPERAND (exp, 0));
+    }
+
   /* Eliminate any conversions since we'll be outputting the underlying
      constant.  */
   while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
         || TREE_CODE (exp) == NON_LVALUE_EXPR
         || TREE_CODE (exp) == VIEW_CONVERT_EXPR)
-    exp = TREE_OPERAND (exp, 0);
+    {
+      HOST_WIDE_INT type_size = int_size_in_bytes (TREE_TYPE (exp));
+      HOST_WIDE_INT op_size = int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0)));
+
+      /* Make sure eliminating the conversion is really a no-op, except with
+        VIEW_CONVERT_EXPRs to allow for wild Ada unchecked conversions and
+        union types to allow for Ada unchecked unions.  */
+      if (type_size > op_size
+         && TREE_CODE (exp) != VIEW_CONVERT_EXPR
+         && TREE_CODE (TREE_TYPE (exp)) != UNION_TYPE)
+       internal_error ("no-op convert from %wd to %wd bytes in initializer",
+                       op_size, type_size);
+
+      exp = TREE_OPERAND (exp, 0);
+    }
 
   code = TREE_CODE (TREE_TYPE (exp));
   thissize = int_size_in_bytes (TREE_TYPE (exp));
 
   /* Allow a constructor with no elements for any data type.
      This means to fill the space with zeros.  */
-  if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0)
+  if (TREE_CODE (exp) == CONSTRUCTOR
+      && VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (exp)))
     {
       assemble_zeros (size);
       return;
@@ -3942,6 +4034,8 @@ static unsigned HOST_WIDE_INT
 array_size_for_constructor (tree val)
 {
   tree max_index, i;
+  unsigned HOST_WIDE_INT cnt;
+  tree index, value;
 
   /* This code used to attempt to handle string constants that are not
      arrays of single-bytes, but nothing else does, so there's no point in
@@ -3950,10 +4044,8 @@ array_size_for_constructor (tree val)
     return TREE_STRING_LENGTH (val);
 
   max_index = NULL_TREE;
-  for (i = CONSTRUCTOR_ELTS (val); i; i = TREE_CHAIN (i))
+  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (val), cnt, index, value)
     {
-      tree index = TREE_PURPOSE (i);
-
       if (TREE_CODE (index) == RANGE_EXPR)
        index = TREE_OPERAND (index, 1);
       if (max_index == NULL_TREE || tree_int_cst_lt (max_index, index))
@@ -3983,7 +4075,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size,
                    unsigned int align)
 {
   tree type = TREE_TYPE (exp);
-  tree link, field = 0;
+  tree field = 0;
   tree min_index = 0;
   /* Number of bytes output or skipped so far.
      In other words, current position within the constructor.  */
@@ -3991,6 +4083,8 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size,
   /* Nonzero means BYTE contains part of a byte, to be output.  */
   int byte_buffer_in_use = 0;
   int byte = 0;
+  unsigned HOST_WIDE_INT cnt;
+  constructor_elt *ce;
 
   gcc_assert (HOST_BITS_PER_WIDE_INT >= BITS_PER_UNIT);
 
@@ -4010,23 +4104,22 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size,
      There is always a maximum of one element in the chain LINK for unions
      (even if the initializer in a source program incorrectly contains
      more one).  */
-  for (link = CONSTRUCTOR_ELTS (exp);
-       link;
-       link = TREE_CHAIN (link),
-       field = field ? TREE_CHAIN (field) : 0)
+  for (cnt = 0;
+       VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), cnt, ce);
+       cnt++, field = field ? TREE_CHAIN (field) : 0)
     {
-      tree val = TREE_VALUE (link);
+      tree val = ce->value;
       tree index = 0;
 
       /* The element in a union constructor specifies the proper field
         or index.  */
       if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
           || TREE_CODE (type) == QUAL_UNION_TYPE)
-         && TREE_PURPOSE (link) != 0)
-       field = TREE_PURPOSE (link);
+         && ce->index != 0)
+       field = ce->index;
 
       else if (TREE_CODE (type) == ARRAY_TYPE)
-       index = TREE_PURPOSE (link);
+       index = ce->index;
 
 #ifdef ASM_COMMENT_START
       if (field && flag_verbose_asm)
@@ -4089,6 +4182,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size,
             if each element has the proper size.  */
          if ((field != 0 || index != 0) && pos != total_bytes)
            {
+             gcc_assert (pos >= total_bytes);
              assemble_zeros (pos - total_bytes);
              total_bytes = pos;
            }
@@ -4164,6 +4258,7 @@ output_constructor (tree exp, unsigned HOST_WIDE_INT size,
              /* If still not at proper byte, advance to there.  */
              if (next_offset / BITS_PER_UNIT != total_bytes)
                {
+                 gcc_assert (next_offset / BITS_PER_UNIT >= total_bytes);
                  assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes);
                  total_bytes = next_offset / BITS_PER_UNIT;
                }
@@ -4335,16 +4430,16 @@ merge_weak (tree newdecl, tree olddecl)
         declare_weak because the NEWDECL and OLDDECL was not yet
         been merged; therefore, TREE_ASM_WRITTEN was not set.  */
       if (TREE_ASM_WRITTEN (olddecl))
-       error ("%Jweak declaration of %qD must precede definition",
-              newdecl, newdecl);
+       error ("weak declaration of %q+D must precede definition",
+              newdecl);
 
       /* If we've already generated rtl referencing OLDDECL, we may
         have done so in a way that will not function properly with
         a weak symbol.  */
       else if (TREE_USED (olddecl)
               && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl)))
-       warning (0, "%Jweak declaration of %qD after first use results "
-                 "in unspecified behavior", newdecl, newdecl);
+       warning (0, "weak declaration of %q+D after first use results "
+                 "in unspecified behavior", newdecl);
 
       if (SUPPORTS_WEAK)
        {
@@ -4377,49 +4472,135 @@ void
 declare_weak (tree decl)
 {
   if (! TREE_PUBLIC (decl))
-    error ("%Jweak declaration of %qD must be public", decl, decl);
+    error ("weak declaration of %q+D must be public", decl);
   else if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
-    error ("%Jweak declaration of %qD must precede definition", decl, decl);
+    error ("weak declaration of %q+D must precede definition", decl);
   else if (SUPPORTS_WEAK)
     {
       if (! DECL_WEAK (decl))
        weak_decls = tree_cons (NULL, decl, weak_decls);
     }
   else
-    warning (0, "%Jweak declaration of %qD not supported", decl, decl);
+    warning (0, "weak declaration of %q+D not supported", decl);
 
   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);
     }
 }
 
@@ -4450,6 +4631,18 @@ globalize_decl (tree 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)
@@ -4485,27 +4678,28 @@ find_decl_and_mark_needed (tree decl, tree target)
   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)
@@ -4522,7 +4716,7 @@ find_decl_and_mark_needed (tree decl, tree target)
    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;
@@ -4530,6 +4724,27 @@ do_assemble_alias (tree decl, tree target ATTRIBUTE_UNUSED)
   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.  */
 
@@ -4564,6 +4779,17 @@ do_assemble_alias (tree decl, tree target ATTRIBUTE_UNUSED)
        *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
 }
@@ -4583,11 +4809,15 @@ finish_aliases_1 (void)
 
       target_decl = find_decl_and_mark_needed (p->decl, p->target);
       if (target_decl == NULL)
-       error ("%J%qD aliased to undefined symbol %qE",
-              p->decl, p->decl, p->target);
-      else if (DECL_EXTERNAL (target_decl))
-       error ("%J%qD aliased to external symbol %qE",
-              p->decl, p->decl, p->target);
+       {
+         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));
     }
 }
 
@@ -4614,19 +4844,41 @@ void
 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.  */
@@ -4636,7 +4888,8 @@ assemble_alias (tree decl, tree target)
   /* 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)
@@ -4745,31 +4998,12 @@ init_varasm_once (void)
   const_alias_set = new_alias_set ();
 }
 
-static enum tls_model
-decl_tls_model (tree decl)
+enum tls_model
+decl_default_tls_model (tree decl)
 {
   enum tls_model kind;
-  tree attr = lookup_attribute ("tls_model", DECL_ATTRIBUTES (decl));
   bool is_local;
 
-  if (attr)
-    {
-      attr = TREE_VALUE (TREE_VALUE (attr));
-      gcc_assert (TREE_CODE (attr) == STRING_CST);
-      
-      if (!strcmp (TREE_STRING_POINTER (attr), "local-exec"))
-       kind = TLS_MODEL_LOCAL_EXEC;
-      else if (!strcmp (TREE_STRING_POINTER (attr), "initial-exec"))
-       kind = TLS_MODEL_INITIAL_EXEC;
-      else if (!strcmp (TREE_STRING_POINTER (attr), "local-dynamic"))
-       kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC;
-      else if (!strcmp (TREE_STRING_POINTER (attr), "global-dynamic"))
-       kind = TLS_MODEL_GLOBAL_DYNAMIC;
-      else
-       gcc_unreachable ();
-      return kind;
-    }
-
   is_local = targetm.binds_local_p (decl);
   if (!flag_shlib)
     {
@@ -4778,6 +5012,7 @@ decl_tls_model (tree decl)
       else
        kind = TLS_MODEL_INITIAL_EXEC;
     }
+
   /* Local dynamic is inefficient when we're not combining the
      parts of the address.  */
   else if (optimize && is_local)
@@ -4828,7 +5063,7 @@ default_section_type_flags_1 (tree decl, const char *name, int reloc,
   if (decl && DECL_ONE_ONLY (decl))
     flags |= SECTION_LINKONCE;
 
-  if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+  if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
     flags |= SECTION_TLS | SECTION_WRITE;
 
   if (strcmp (name, ".bss") == 0
@@ -5005,47 +5240,7 @@ default_select_section (tree decl, int reloc,
     data_section ();
 }
 
-/* A helper function for default_elf_select_section and
-   default_elf_unique_section.  Categorizes the DECL.  */
-
 enum section_category
-{
-  SECCAT_TEXT,
-
-  SECCAT_RODATA,
-  SECCAT_RODATA_MERGE_STR,
-  SECCAT_RODATA_MERGE_STR_INIT,
-  SECCAT_RODATA_MERGE_CONST,
-  SECCAT_SRODATA,
-
-  SECCAT_DATA,
-
-  /* To optimize loading of shared programs, define following subsections
-     of data section:
-       _REL    Contains data that has relocations, so they get grouped
-               together and dynamic linker will visit fewer pages in memory.
-       _RO     Contains data that is otherwise read-only.  This is useful
-               with prelinking as most relocations won't be dynamically
-               linked and thus stay read only.
-       _LOCAL  Marks data containing relocations only to local objects.
-               These relocations will get fully resolved by prelinking.  */
-  SECCAT_DATA_REL,
-  SECCAT_DATA_REL_LOCAL,
-  SECCAT_DATA_REL_RO,
-  SECCAT_DATA_REL_RO_LOCAL,
-
-  SECCAT_SDATA,
-  SECCAT_TDATA,
-
-  SECCAT_BSS,
-  SECCAT_SBSS,
-  SECCAT_TBSS
-};
-
-static enum section_category
-categorize_decl_for_section (tree, int, int);
-
-static enum section_category
 categorize_decl_for_section (tree decl, int reloc, int shlib)
 {
   enum section_category ret;
@@ -5106,7 +5301,7 @@ categorize_decl_for_section (tree decl, int reloc, int shlib)
     ret = SECCAT_RODATA;
 
   /* There are no read-only thread-local sections.  */
-  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
     {
       /* Note that this would be *just* SECCAT_BSS, except that there's
         no concept of a read-only thread-local-data section.  */
@@ -5370,8 +5565,8 @@ default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
     flags |= SYMBOL_FLAG_FUNCTION;
   if (targetm.binds_local_p (decl))
     flags |= SYMBOL_FLAG_LOCAL;
-  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
-    flags |= decl_tls_model (decl) << SYMBOL_FLAG_TLS_SHIFT;
+  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
+    flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT;
   else if (targetm.in_small_data_p (decl))
     flags |= SYMBOL_FLAG_SMALL;
   /* ??? Why is DECL_EXTERNAL ever set for non-PUBLIC names?  Without