OSDN Git Service

* config/i386/winnt.c (ix86_handle_dll_attribute): Set
[pf3gnuchains/gcc-fork.git] / gcc / varasm.c
index 4d6f4b6..edd0262 100644 (file)
@@ -1,6 +1,6 @@
 /* Output variables, constants and external declarations, for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -36,13 +36,14 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "expr.h"
 #include "hard-reg-set.h"
 #include "regs.h"
-#include "output.h"
 #include "real.h"
+#include "output.h"
 #include "toplev.h"
-#include "obstack.h"
 #include "hashtab.h"
 #include "c-pragma.h"
+#include "c-tree.h"
 #include "ggc.h"
+#include "langhooks.h"
 #include "tm_p.h"
 #include "debug.h"
 #include "target.h"
@@ -60,25 +61,18 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #define ASM_STABS_OP "\t.stabs\t"
 #endif
 
-/* Define the prefix to use when check_memory_usage_flag is enable.  */
-#define CHKR_PREFIX "_CHKR_"
-#define CHKR_PREFIX_SIZE (sizeof (CHKR_PREFIX) - 1)
-
 /* The (assembler) name of the first globally-visible object output.  */
 const char *first_global_object_name;
 const char *weak_global_object_name;
 
-extern struct obstack permanent_obstack;
-#define obstack_chunk_alloc xmalloc
-
 struct addr_const;
-struct constant_descriptor;
+struct constant_descriptor_rtx;
 struct rtx_const;
 struct pool_constant;
 
 #define MAX_RTX_HASH_TABLE 61
 
-struct varasm_status
+struct varasm_status GTY(())
 {
   /* Hash facility for making memory-constants
      from constant rtl-expressions.  It is used on RISC machines
@@ -88,22 +82,18 @@ struct varasm_status
      This pool of constants is reinitialized for each function
      so each function gets its own constants-pool that comes right before
      it.  */
-  struct constant_descriptor **x_const_rtx_hash_table;
-  struct pool_constant **x_const_rtx_sym_hash_table;
+  struct constant_descriptor_rtx ** GTY ((length ("MAX_RTX_HASH_TABLE")))
+    x_const_rtx_hash_table;
+  struct pool_constant ** GTY ((length ("MAX_RTX_HASH_TABLE")))
+    x_const_rtx_sym_hash_table;
 
   /* Pointers to first and last constant in pool.  */
-  struct pool_constant *x_first_pool, *x_last_pool;
+  struct pool_constant *x_first_pool;
+  struct pool_constant *x_last_pool;
 
   /* Current offset in constant pool (does not include any machine-specific
      header).  */
-  int x_pool_offset;
-
-  /* Chain of all CONST_DOUBLE rtx's constructed for the current function.
-     They are chained through the CONST_DOUBLE_CHAIN.
-     A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain.
-     In that case, CONST_DOUBLE_MEM is either a MEM,
-     or const0_rtx if no MEM has been made for this CONST_DOUBLE yet.  */
-  rtx x_const_double_chain;
+  HOST_WIDE_INT x_pool_offset;
 };
 
 #define const_rtx_hash_table (cfun->varasm->x_const_rtx_hash_table)
@@ -111,7 +101,6 @@ struct varasm_status
 #define first_pool (cfun->varasm->x_first_pool)
 #define last_pool (cfun->varasm->x_last_pool)
 #define pool_offset (cfun->varasm->x_pool_offset)
-#define const_double_chain (cfun->varasm->x_const_double_chain)
 
 /* Number for making the label on the next
    constant that is stored in memory.  */
@@ -135,25 +124,29 @@ int size_directive_output;
 
 tree last_assemble_variable_decl;
 
+/* RTX_UNCHANGING_P in a MEM can mean it is stored into, for initialization.
+   So giving constant the alias set for the type will allow such
+   initializations to appear to conflict with the load of the constant.  We
+   avoid this by giving all constants an alias set for just constants.
+   Since there will be no stores to that alias set, nothing will ever
+   conflict with them.  */
+
+static HOST_WIDE_INT const_alias_set;
+
 static const char *strip_reg_name      PARAMS ((const char *));
 static int contains_pointers_p         PARAMS ((tree));
-static void assemble_real_1            PARAMS ((PTR));
 static void decode_addr_const          PARAMS ((tree, struct addr_const *));
 static int const_hash                  PARAMS ((tree));
-static int compare_constant            PARAMS ((tree,
-                                              struct constant_descriptor *));
-static const unsigned char *compare_constant_1  PARAMS ((tree, const unsigned char *));
-static struct constant_descriptor *record_constant PARAMS ((tree));
-static void record_constant_1          PARAMS ((tree));
+static int compare_constant            PARAMS ((tree, tree));
 static tree copy_constant              PARAMS ((tree));
 static void output_constant_def_contents  PARAMS ((tree, int, int));
 static void decode_rtx_const           PARAMS ((enum machine_mode, rtx,
                                               struct rtx_const *));
 static int const_hash_rtx              PARAMS ((enum machine_mode, rtx));
-static int compare_constant_rtx                PARAMS ((enum machine_mode, rtx,
-                                              struct constant_descriptor *));
-static struct constant_descriptor *record_constant_rtx PARAMS ((enum machine_mode,
-                                                             rtx));
+static int compare_constant_rtx
+  PARAMS ((enum machine_mode, rtx, struct constant_descriptor_rtx *));
+static struct constant_descriptor_rtx * record_constant_rtx
+  PARAMS ((enum machine_mode, rtx));
 static struct pool_constant *find_pool_constant PARAMS ((struct function *, rtx));
 static void mark_constant_pool         PARAMS ((void));
 static void mark_constants             PARAMS ((rtx));
@@ -162,10 +155,10 @@ static int output_addressed_constants     PARAMS ((tree));
 static void output_after_function_constants PARAMS ((void));
 static unsigned HOST_WIDE_INT array_size_for_constructor PARAMS ((tree));
 static unsigned min_align              PARAMS ((unsigned, unsigned));
-static void output_constructor         PARAMS ((tree, int, unsigned));
-#ifdef ASM_WEAKEN_LABEL
-static void remove_from_pending_weak_list      PARAMS ((const char *));
-#endif
+static void output_constructor         PARAMS ((tree, HOST_WIDE_INT,
+                                                unsigned int));
+static void globalize_decl             PARAMS ((tree));
+static void maybe_assemble_visibility  PARAMS ((tree));
 static int in_named_entry_eq           PARAMS ((const PTR, const PTR));
 static hashval_t in_named_entry_hash   PARAMS ((const PTR));
 #ifdef ASM_OUTPUT_BSS
@@ -177,15 +170,11 @@ static void asm_output_aligned_bss        PARAMS ((FILE *, tree, const char *,
                                                 int, int));
 #endif
 #endif /* BSS_SECTION_ASM_OP */
-static void mark_pool_constant          PARAMS ((struct pool_constant *));
-static void mark_const_hash_entry      PARAMS ((void *));
-static int mark_const_str_htab_1       PARAMS ((void **, void *));
-static void mark_const_str_htab                PARAMS ((void *));
 static hashval_t const_str_htab_hash   PARAMS ((const void *x));
 static int const_str_htab_eq           PARAMS ((const void *x, const void *y));
-static void const_str_htab_del         PARAMS ((void *));
-static void asm_emit_uninitialised     PARAMS ((tree, const char*, int, int));
-static void resolve_unique_section     PARAMS ((tree, int));
+static bool asm_emit_uninitialised     PARAMS ((tree, const char*, int, int));
+static void resolve_unique_section     PARAMS ((tree, int, int));
+static void mark_weak                   PARAMS ((tree));
 \f
 static enum in_section { no_section, in_text, in_data, in_named
 #ifdef BSS_SECTION_ASM_OP
@@ -197,6 +186,9 @@ static enum in_section { no_section, in_text, in_data, in_named
 #ifdef DTORS_SECTION_ASM_OP
   , in_dtors
 #endif
+#ifdef READONLY_DATA_SECTION_ASM_OP
+  , in_readonly_data
+#endif
 #ifdef EXTRA_SECTIONS
   , EXTRA_SECTIONS
 #endif
@@ -235,12 +227,12 @@ text_section ()
 {
   if (in_section != in_text)
     {
+      in_section = in_text;
 #ifdef TEXT_SECTION
       TEXT_SECTION ();
 #else
       fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
 #endif
-      in_section = in_text;
     }
 }
 
@@ -251,6 +243,7 @@ data_section ()
 {
   if (in_section != in_data)
     {
+      in_section = in_data;
       if (flag_shared_data)
        {
 #ifdef SHARED_SECTION_ASM_OP
@@ -261,10 +254,9 @@ data_section ()
        }
       else
        fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
-
-      in_section = in_data;
     }
 }
+
 /* Tell assembler to ALWAYS switch to data section, in case
    it's not sure where it is.  */
 
@@ -284,8 +276,17 @@ readonly_data_section ()
 #ifdef READONLY_DATA_SECTION
   READONLY_DATA_SECTION ();  /* Note this can call data_section.  */
 #else
+#ifdef READONLY_DATA_SECTION_ASM_OP
+  if (in_section != in_readonly_data)
+    {
+      in_section = in_readonly_data;
+      fputs (READONLY_DATA_SECTION_ASM_OP, asm_out_file);
+      fputc ('\n', asm_out_file);
+    }
+#else
   text_section ();
 #endif
+#endif
 }
 
 /* Determine if we're in the text section.  */
@@ -336,7 +337,7 @@ get_named_section_flags (section)
 {
   struct in_named_entry **slot;
 
-  slot = (struct in_named_entry**)
+  slot = (struct in_named_entry **)
     htab_find_slot_with_hash (in_named_htab, section,
                              htab_hash_string (section), NO_INSERT);
 
@@ -344,7 +345,7 @@ get_named_section_flags (section)
 }
 
 /* Returns true if the section has been declared before.   Sets internal
-   flag on this section in in_named_hash so subsequent calls on this 
+   flag on this section in in_named_hash so subsequent calls on this
    section will return false.  */
 
 bool
@@ -353,15 +354,15 @@ named_section_first_declaration (name)
 {
   struct in_named_entry **slot;
 
-  slot = (struct in_named_entry**)
-    htab_find_slot_with_hash (in_named_htab, name, 
+  slot = (struct in_named_entry **)
+    htab_find_slot_with_hash (in_named_htab, name,
                              htab_hash_string (name), NO_INSERT);
   if (! (*slot)->declared)
     {
       (*slot)->declared = true;
       return true;
     }
-  else 
+  else
     {
       return false;
     }
@@ -378,7 +379,7 @@ set_named_section_flags (section, flags)
 {
   struct in_named_entry **slot, *entry;
 
-  slot = (struct in_named_entry**)
+  slot = (struct in_named_entry **)
     htab_find_slot_with_hash (in_named_htab, section,
                              htab_hash_string (section), INSERT);
   entry = *slot;
@@ -409,7 +410,7 @@ named_section_flags (name, flags)
       if (! set_named_section_flags (name, flags))
        abort ();
 
-      (* targetm.asm_out.named_section) (name, flags);
+      (*targetm.asm_out.named_section) (name, flags);
 
       if (flags & SECTION_FORGET)
        in_section = no_section;
@@ -442,11 +443,15 @@ named_section (decl, name, reloc)
   flags = (* targetm.section_type_flags) (decl, name, reloc);
 
   /* Sanity check user variables for flag changes.  Non-user
-     section flag changes will abort in named_section_flags.  */
+     section flag changes will abort in named_section_flags.
+     However, don't complain if SECTION_OVERRIDE is set.
+     We trust that the setter knows that it is safe to ignore
+     the default flags for this decl.  */
   if (decl && ! set_named_section_flags (name, flags))
     {
-      error_with_decl (decl, "%s causes a section type conflict");
       flags = get_named_section_flags (name);
+      if ((flags & SECTION_OVERRIDE) == 0)
+       error_with_decl (decl, "%s causes a section type conflict");
     }
 
   named_section_flags (name, flags);
@@ -455,15 +460,16 @@ named_section (decl, name, reloc)
 /* If required, set DECL_SECTION_NAME to a unique name.  */
 
 static void
-resolve_unique_section (decl, reloc)
+resolve_unique_section (decl, reloc, flag_function_or_data_sections)
      tree decl;
      int reloc ATTRIBUTE_UNUSED;
+     int flag_function_or_data_sections;
 {
   if (DECL_SECTION_NAME (decl) == NULL_TREE
-      && (flag_function_sections
-         || (targetm.have_named_sections
-             && DECL_ONE_ONLY (decl))))
-    UNIQUE_SECTION (decl, reloc);
+      && targetm.have_named_sections
+      && (flag_function_or_data_sections
+         || DECL_ONE_ONLY (decl)))
+    (*targetm.asm_out.unique_section) (decl, reloc);
 }
 
 #ifdef BSS_SECTION_ASM_OP
@@ -500,7 +506,7 @@ asm_output_bss (file, decl, name, size, rounded)
      const char *name;
      int size ATTRIBUTE_UNUSED, rounded;
 {
-  ASM_GLOBALIZE_LABEL (file, name);
+  (*targetm.asm_out.globalize_label) (file, name);
   bss_section ();
 #ifdef ASM_DECLARE_OBJECT_NAME
   last_assemble_variable_decl = decl;
@@ -509,7 +515,7 @@ asm_output_bss (file, decl, name, size, rounded)
   /* Standard thing is just output label for the object.  */
   ASM_OUTPUT_LABEL (file, name);
 #endif /* ASM_DECLARE_OBJECT_NAME */
-  ASM_OUTPUT_SKIP (file, rounded);
+  ASM_OUTPUT_SKIP (file, rounded ? rounded : 1);
 }
 
 #endif
@@ -528,7 +534,7 @@ asm_output_aligned_bss (file, decl, name, size, align)
      const char *name;
      int size, align;
 {
-  ASM_GLOBALIZE_LABEL (file, name);
+  (*targetm.asm_out.globalize_label) (file, name);
   bss_section ();
   ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
 #ifdef ASM_DECLARE_OBJECT_NAME
@@ -562,9 +568,8 @@ function_section (decl)
     text_section ();
 }
 
-/* Switch to section for variable DECL.
-
-   RELOC is the `reloc' argument to SELECT_SECTION.  */
+/* Switch to section for variable DECL.  RELOC is the same as the
+   argument to SELECT_SECTION.  */
 
 void
 variable_section (decl, reloc)
@@ -574,56 +579,30 @@ variable_section (decl, reloc)
   if (IN_NAMED_SECTION (decl))
     named_section (decl, NULL, reloc);
   else
-    {
-      /* C++ can have const variables that get initialized from constructors,
-        and thus can not be in a readonly section.  We prevent this by
-        verifying that the initial value is constant for objects put in a
-        readonly section.
-
-        error_mark_node is used by the C front end to indicate that the
-        initializer has not been seen yet.  In this case, we assume that
-        the initializer must be constant.
-
-        C++ uses error_mark_node for variables that have complicated
-        initializers, but these variables go in BSS so we won't be called
-        for them.  */
-
-#ifdef SELECT_SECTION
-      SELECT_SECTION (decl, reloc, DECL_ALIGN (decl));
-#else
-      if (DECL_READONLY_SECTION (decl, reloc))
-       readonly_data_section ();
-      else
-       data_section ();
-#endif
-    }
+    (*targetm.asm_out.select_section) (decl, reloc, DECL_ALIGN (decl));
 }
 
 /* Tell assembler to switch to the section for the exception handling
    table.  */
 
 void
-exception_section ()
+default_exception_section ()
 {
-#if defined (EXCEPTION_SECTION)
-  EXCEPTION_SECTION ();
-#else
   if (targetm.have_named_sections)
     named_section (NULL_TREE, ".gcc_except_table", 0);
   else if (flag_pic)
     data_section ();
   else
     readonly_data_section ();
-#endif
 }
 
 /* Tell assembler to switch to the section for string merging.  */
 
 void
 mergeable_string_section (decl, align, flags)
-  tree decl ATTRIBUTE_UNUSED;
-  unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
-  unsigned int flags ATTRIBUTE_UNUSED;
+     tree decl ATTRIBUTE_UNUSED;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+     unsigned int flags ATTRIBUTE_UNUSED;
 {
 #ifdef HAVE_GAS_SHF_MERGE
   if (flag_merge_constants
@@ -654,7 +633,7 @@ mergeable_string_section (decl, align, flags)
          for (i = 0; i < len; i += unit)
            {
              for (j = 0; j < unit; j++)
-               if (str [i + j] != '\0')
+               if (str[i + j] != '\0')
                  break;
              if (j == unit)
                break;
@@ -690,15 +669,15 @@ mergeable_string_section (decl, align, flags)
     }
 #endif
   readonly_data_section ();
-}  
+}
 
 /* Tell assembler to switch to the section for constant merging.  */
 
 void
 mergeable_constant_section (mode, align, flags)
-  enum machine_mode mode ATTRIBUTE_UNUSED;
-  unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
-  unsigned int flags ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+     unsigned int flags ATTRIBUTE_UNUSED;
 {
 #ifdef HAVE_GAS_SHF_MERGE
   unsigned int modesize = GET_MODE_BITSIZE (mode);
@@ -717,7 +696,7 @@ mergeable_constant_section (mode, align, flags)
       flags |= (align / 8) | SECTION_MERGE;
       named_section_flags (name, flags);
       return;
-    }            
+    }
 #endif
   readonly_data_section ();
 }
@@ -726,7 +705,7 @@ mergeable_constant_section (mode, align, flags)
 
 static const char *
 strip_reg_name (name)
-  const char *name;
+     const char *name;
 {
 #ifdef REGISTER_PREFIX
   if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX)))
@@ -747,7 +726,7 @@ strip_reg_name (name)
 
 int
 decode_reg_name (asmspec)
-  const char *asmspec;
+     const char *asmspec;
 {
   if (asmspec != 0)
     {
@@ -841,22 +820,18 @@ make_decl_rtl (decl, asmspec)
     {
       /* If the old RTL had the wrong mode, fix the mode.  */
       if (GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl))
-       {
-         rtx rtl = DECL_RTL (decl);
-         PUT_MODE (rtl, DECL_MODE (decl));
-       }
+       SET_DECL_RTL (decl, adjust_address_nv (DECL_RTL (decl),
+                                              DECL_MODE (decl), 0));
+
+      /* ??? Another way to do this would be to maintain a hashed
+        table of such critters.  Instead of adding stuff to a DECL
+        to give certain attributes to it, we could use an external
+        hash map from DECL to set of attributes.  */
 
-      /* ??? Another way to do this would be to do what halfpic.c does
-        and maintain a hashed table of such critters.  */
-      /* ??? Another way to do this would be to pass a flag bit to
-        ENCODE_SECTION_INFO saying whether this is a new decl or not.  */
       /* Let the target reassign the RTL if it wants.
         This is necessary, for example, when one machine specific
         decl attribute overrides another.  */
-#ifdef REDO_SECTION_INFO_P
-      if (REDO_SECTION_INFO_P (decl))
-       ENCODE_SECTION_INFO (decl);
-#endif
+      (* targetm.encode_section_info) (decl, false);
       return;
     }
 
@@ -901,14 +876,11 @@ make_decl_rtl (decl, asmspec)
 
          /* If the user specified one of the eliminables registers here,
             e.g., FRAME_POINTER_REGNUM, we don't want to get this variable
-            confused with that register and be eliminated.  Although this
-            usage is somewhat suspect, we nevertheless use the following
-            kludge to avoid setting DECL_RTL to frame_pointer_rtx.  */
-
-         SET_DECL_RTL (decl,
-                       gen_rtx_REG (DECL_MODE (decl),
-                                    FIRST_PSEUDO_REGISTER));
-         REGNO (DECL_RTL (decl)) = reg_number;
+            confused with that register and be eliminated.  This usage is
+            somewhat suspect...  */
+
+         SET_DECL_RTL (decl, gen_rtx_raw_REG (DECL_MODE (decl), reg_number));
+         ORIGINAL_REGNO (DECL_RTL (decl)) = reg_number;
          REG_USERVAR_P (DECL_RTL (decl)) = 1;
 
          if (TREE_STATIC (decl))
@@ -943,6 +915,10 @@ make_decl_rtl (decl, asmspec)
       && DECL_COMMON (decl))
     DECL_COMMON (decl) = 0;
 
+  /* Variables can't be both common and weak.  */
+  if (TREE_CODE (decl) == VAR_DECL && DECL_WEAK (decl))
+    DECL_COMMON (decl) = 0;
+
   /* Can't use just the variable's own name for a variable
      whose scope is less than the whole file, unless it's a member
      of a local class (which will already be unambiguous).
@@ -953,25 +929,12 @@ make_decl_rtl (decl, asmspec)
       && name == IDENTIFIER_POINTER (DECL_NAME (decl)))
     {
       char *label;
+
       ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
       var_labelno++;
       new_name = label;
     }
 
-  /* When -fprefix-function-name is used, the functions
-     names are prefixed.  Only nested function names are not
-     prefixed.  */
-  else if (flag_prefix_function_name && TREE_CODE (decl) == FUNCTION_DECL)
-    {
-      size_t name_len = IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl));
-      char *pname;
-
-      pname = alloca (name_len + CHKR_PREFIX_SIZE + 1);
-      memcpy (pname, CHKR_PREFIX, CHKR_PREFIX_SIZE);
-      memcpy (pname + CHKR_PREFIX_SIZE, name, name_len + 1);
-      new_name = pname;
-    }
-
   if (name != new_name)
     {
       SET_DECL_ASSEMBLER_NAME (decl, get_identifier (new_name));
@@ -996,9 +959,7 @@ make_decl_rtl (decl, asmspec)
      such as that it is a function name.
      If the name is changed, the macro ASM_OUTPUT_LABELREF
      will have to know how to strip this information.  */
-#ifdef ENCODE_SECTION_INFO
-  ENCODE_SECTION_INFO (decl);
-#endif
+  (* targetm.encode_section_info) (decl, true);
 }
 
 /* Make the rtl for variable VAR be volatile.
@@ -1204,7 +1165,7 @@ assemble_start_function (decl, fnname)
   if (CONSTANT_POOL_BEFORE_FUNCTION)
     output_constant_pool (fnname, decl);
 
-  resolve_unique_section (decl, 0);
+  resolve_unique_section (decl, 0, flag_function_sections);
   function_section (decl);
 
   /* Tell assembler to move to target machine's alignment for functions.  */
@@ -1217,11 +1178,12 @@ assemble_start_function (decl, fnname)
   /* Handle a user-specified function alignment.
      Note that we still need to align to FUNCTION_BOUNDARY, as above,
      because ASM_OUTPUT_MAX_SKIP_ALIGN might not do any alignment at all.  */
-  if (align_functions_log > align)
+  if (align_functions_log > align
+      && cfun->function_frequency != FUNCTION_FREQUENCY_UNLIKELY_EXECUTED)
     {
 #ifdef ASM_OUTPUT_MAX_SKIP_ALIGN
       ASM_OUTPUT_MAX_SKIP_ALIGN (asm_out_file,
-                                align_functions_log, align_functions-1);
+                                align_functions_log, align_functions - 1);
 #else
       ASM_OUTPUT_ALIGN (asm_out_file, align_functions_log);
 #endif
@@ -1242,9 +1204,8 @@ assemble_start_function (decl, fnname)
          const char *p;
          char *name;
 
-         STRIP_NAME_ENCODING (p, fnname);
-         name = permalloc (strlen (p) + 1);
-         strcpy (name, p);
+         p = (* targetm.strip_name_encoding) (fnname);
+         name = xstrdup (p);
 
          if (! DECL_WEAK (decl) && ! DECL_ONE_ONLY (decl))
            first_global_object_name = name;
@@ -1252,18 +1213,9 @@ assemble_start_function (decl, fnname)
            weak_global_object_name = name;
        }
 
-#ifdef ASM_WEAKEN_LABEL
-      if (DECL_WEAK (decl))
-       {
-         ASM_WEAKEN_LABEL (asm_out_file, fnname);
-         /* Remove this function from the pending weak list so that
-            we do not emit multiple .weak directives for it.  */
-         remove_from_pending_weak_list
-           (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
-       }
-      else
-#endif
-      ASM_GLOBALIZE_LABEL (asm_out_file, fnname);
+      globalize_decl (decl);
+
+      maybe_assemble_visibility (decl);
     }
 
   /* Do any machine/system dependent processing of the function name */
@@ -1397,10 +1349,10 @@ assemble_string (p, size)
 #endif
 #endif
 
-static void
+static bool
 asm_emit_uninitialised (decl, name, size, rounded)
      tree decl;
-     const char * name;
+     const char *name;
      int size ATTRIBUTE_UNUSED;
      int rounded ATTRIBUTE_UNUSED;
 {
@@ -1412,16 +1364,24 @@ asm_emit_uninitialised (decl, name, size, rounded)
   }
   destination = asm_dest_local;
 
+  /* ??? We should handle .bss via select_section mechanisms rather than
+     via special target hooks.  That would eliminate this special case.  */
   if (TREE_PUBLIC (decl))
     {
-#if defined ASM_EMIT_BSS
-      if (! DECL_COMMON (decl))
+      if (!DECL_COMMON (decl))
+#ifdef ASM_EMIT_BSS
        destination = asm_dest_bss;
-      else
+#else
+       return false;
 #endif
+      else
        destination = asm_dest_common;
     }
 
+  if (destination == asm_dest_bss)
+    globalize_decl (decl);
+  resolve_unique_section (decl, 0, flag_data_sections);
+
   if (flag_shared_data)
     {
       switch (destination)
@@ -1446,8 +1406,6 @@ asm_emit_uninitialised (decl, name, size, rounded)
        }
     }
 
-  resolve_unique_section (decl, 0);
-
   switch (destination)
     {
 #ifdef ASM_EMIT_BSS
@@ -1465,7 +1423,7 @@ asm_emit_uninitialised (decl, name, size, rounded)
       abort ();
     }
 
-  return;
+  return true;
 }
 
 /* Assemble everything that is needed for a variable or function declaration.
@@ -1539,7 +1497,8 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
   if (TREE_ASM_WRITTEN (decl))
     return;
 
-  /* Make sure ENCODE_SECTION_INFO is invoked before we set ASM_WRITTEN.  */
+  /* Make sure targetm.encode_section_info is invoked before we set
+     ASM_WRITTEN.  */
   decl_rtl = DECL_RTL (decl);
 
   TREE_ASM_WRITTEN (decl) = 1;
@@ -1568,9 +1527,8 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
       const char *p;
       char *xname;
 
-      STRIP_NAME_ENCODING (p, name);
-      xname = permalloc (strlen (p) + 1);
-      strcpy (xname, p);
+      p = (* targetm.strip_name_encoding) (name);
+      xname = xstrdup (p);
       first_global_object_name = xname;
     }
 
@@ -1593,8 +1551,8 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
   if (align > MAX_OFILE_ALIGNMENT)
     {
       warning_with_decl (decl,
-       "alignment of `%s' is greater than maximum object file alignment. Using %d.",
-                    MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
+       "alignment of `%s' is greater than maximum object file alignment. Using %d",
+                        MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);
       align = MAX_OFILE_ALIGNMENT;
     }
 
@@ -1606,24 +1564,41 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
 #endif
 #ifdef CONSTANT_ALIGNMENT
       if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node)
-        align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align);
+       align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align);
 #endif
     }
 
   /* Reset the alignment in case we have made it tighter, so we can benefit
      from it in get_pointer_alignment.  */
   DECL_ALIGN (decl) = align;
+  set_mem_align (decl_rtl, align);
+
+  if (TREE_PUBLIC (decl))
+    maybe_assemble_visibility (decl);
+
+  /* Output any data that we will need to use the address of.  */
+  if (DECL_INITIAL (decl) == error_mark_node)
+    reloc = contains_pointers_p (TREE_TYPE (decl)) ? 3 : 0;
+  else if (DECL_INITIAL (decl))
+    reloc = output_addressed_constants (DECL_INITIAL (decl));
+  resolve_unique_section (decl, reloc, flag_data_sections);
 
   /* Handle uninitialized definitions.  */
 
-  if ((DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)
-      /* If the target can't output uninitialized but not common global data
-        in .bss, then we have to use .data.  */
-#if ! defined ASM_EMIT_BSS
-      && DECL_COMMON (decl)
-#endif
-      && DECL_SECTION_NAME (decl) == NULL_TREE
-      && ! dont_output_data)
+  /* If the decl has been given an explicit section name, then it
+     isn't common, and shouldn't be handled as such.  */
+  if (DECL_SECTION_NAME (decl) || dont_output_data)
+    ;
+  /* We don't implement common thread-local data at present.  */
+  else if (DECL_THREAD_LOCAL (decl))
+    {
+      if (DECL_COMMON (decl))
+       sorry ("thread-local COMMON data not implemented");
+    }
+  else if (DECL_INITIAL (decl) == 0
+          || DECL_INITIAL (decl) == error_mark_node
+          || (flag_zero_initialized_in_bss
+              && initializer_zerop (DECL_INITIAL (decl))))
     {
       unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
       unsigned HOST_WIDE_INT rounded = size;
@@ -1639,16 +1614,16 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
       rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
                 * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
 
-/* Don't continue this line--convex cc version 4.1 would lose.  */
 #if !defined(ASM_OUTPUT_ALIGNED_COMMON) && !defined(ASM_OUTPUT_ALIGNED_DECL_COMMON) && !defined(ASM_OUTPUT_ALIGNED_BSS)
       if ((unsigned HOST_WIDE_INT) DECL_ALIGN (decl) / BITS_PER_UNIT > rounded)
-         warning_with_decl
-           (decl, "requested alignment for %s is greater than implemented alignment of %d.",rounded);
+       warning_with_decl
+         (decl, "requested alignment for %s is greater than implemented alignment of %d",rounded);
 #endif
 
-      asm_emit_uninitialised (decl, name, size, rounded);
-
-      return;
+      /* If the target cannot output uninitialized but not common global data
+        in .bss, then we have to use .data, so fall through.  */
+      if (asm_emit_uninitialised (decl, name, size, rounded))
+       return;
     }
 
   /* Handle initialized definitions.
@@ -1657,29 +1632,9 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
 
   /* First make the assembler name(s) global if appropriate.  */
   if (TREE_PUBLIC (decl) && DECL_NAME (decl))
-    {
-#ifdef ASM_WEAKEN_LABEL
-      if (DECL_WEAK (decl))
-       {
-         ASM_WEAKEN_LABEL (asm_out_file, name);
-          /* Remove this variable from the pending weak list so that
-             we do not emit multiple .weak directives for it.  */
-         remove_from_pending_weak_list
-           (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
-       }
-      else
-#endif
-      ASM_GLOBALIZE_LABEL (asm_out_file, name);
-    }
-
-  /* Output any data that we will need to use the address of.  */
-  if (DECL_INITIAL (decl) == error_mark_node)
-    reloc = contains_pointers_p (TREE_TYPE (decl));
-  else if (DECL_INITIAL (decl))
-    reloc = output_addressed_constants (DECL_INITIAL (decl));
+    globalize_decl (decl);
 
   /* Switch to the appropriate section.  */
-  resolve_unique_section (decl, reloc);
   variable_section (decl, reloc);
 
   /* dbxout.c needs to know this.  */
@@ -1704,7 +1659,7 @@ assemble_variable (decl, top_level, at_end, dont_output_data)
 
   if (!dont_output_data)
     {
-      if (DECL_INITIAL (decl))
+      if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node)
        /* Output the actual data.  */
        output_constant (DECL_INITIAL (decl),
                         tree_low_cst (DECL_SIZE_UNIT (decl), 1),
@@ -1799,15 +1754,6 @@ assemble_external_libcall (fun)
 #endif
 }
 
-/* Declare the label NAME global.  */
-
-void
-assemble_global (name)
-     const char *name;
-{
-  ASM_GLOBALIZE_LABEL (asm_out_file, name);
-}
-
 /* Assemble a label named NAME.  */
 
 void
@@ -1831,10 +1777,7 @@ assemble_name (file, name)
   const char *real_name;
   tree id;
 
-  STRIP_NAME_ENCODING (real_name, name);
-  if (flag_prefix_function_name
-      && ! memcmp (real_name, CHKR_PREFIX, CHKR_PREFIX_SIZE))
-    real_name = real_name + CHKR_PREFIX_SIZE;
+  real_name = (* targetm.strip_name_encoding) (name);
 
   id = maybe_get_identifier (real_name);
   if (id)
@@ -1937,146 +1880,112 @@ min_align (a, b)
   return (a | b) & -(a | b);
 }
 
-/* Assemble the integer constant X into an object of SIZE bytes.  ALIGN is
-   the alignment of the integer in bits.  Return 1 if we were able to output
-   the constant, otherwise 0.  If FORCE is non-zero, abort if we can't output
-   the constant.  */
+/* Return the assembler directive for creating a given kind of integer
+   object.  SIZE is the number of bytes in the object and ALIGNED_P
+   indicates whether it is known to be aligned.  Return NULL if the
+   assembly dialect has no such directive.
 
-int
-assemble_integer (x, size, align, force)
-     rtx x;
-     unsigned int size;
-     unsigned int align;
-     int force;
+   The returned string should be printed at the start of a new line and
+   be followed immediately by the object's initial value.  */
+
+const char *
+integer_asm_op (size, aligned_p)
+     int size;
+     int aligned_p;
 {
-  /* First try to use the standard 1, 2, 4, 8, and 16 byte
-     ASM_OUTPUT... macros.  */
+  struct asm_int_op *ops;
 
-  if (align >= MIN (size * BITS_PER_UNIT, BIGGEST_ALIGNMENT))
-    switch (size)
-      {
-#ifdef ASM_OUTPUT_CHAR
-      case 1:
-       ASM_OUTPUT_CHAR (asm_out_file, x);
-       return 1;
-#endif
-#ifdef ASM_OUTPUT_SHORT
-      case 2:
-       ASM_OUTPUT_SHORT (asm_out_file, x);
-       return 1;
-#endif
-#ifdef ASM_OUTPUT_INT
-      case 4:
-       ASM_OUTPUT_INT (asm_out_file, x);
-       return 1;
-#endif
-#ifdef ASM_OUTPUT_DOUBLE_INT
-      case 8:
-       ASM_OUTPUT_DOUBLE_INT (asm_out_file, x);
-       return 1;
-#endif
-#ifdef ASM_OUTPUT_QUADRUPLE_INT
-      case 16:
-       ASM_OUTPUT_QUADRUPLE_INT (asm_out_file, x);
-       return 1;
-#endif
-      }
+  if (aligned_p)
+    ops = &targetm.asm_out.aligned_op;
   else
+    ops = &targetm.asm_out.unaligned_op;
+
+  switch (size)
     {
-      const char *asm_op = NULL;
+    case 1:
+      return targetm.asm_out.byte_op;
+    case 2:
+      return ops->hi;
+    case 4:
+      return ops->si;
+    case 8:
+      return ops->di;
+    case 16:
+      return ops->ti;
+    default:
+      return NULL;
+    }
+}
 
-      /* ??? This isn't quite as flexible as the ASM_OUTPUT_INT type hooks.
-        At present powerpc-eabi can't jump -mrelocatable hoops, so you can
-        get assembler errors from symbolic references in packed structs.  */
-      switch (size)
-       {
-#ifdef UNALIGNED_SHORT_ASM_OP
-       case 2:
-         asm_op = UNALIGNED_SHORT_ASM_OP;
-         break;
-#endif
-#ifdef UNALIGNED_INT_ASM_OP
-       case 4:
-         asm_op = UNALIGNED_INT_ASM_OP;
-         break;
-#endif
-#ifdef UNALIGNED_DOUBLE_INT_ASM_OP
-       case 8:
-         asm_op = UNALIGNED_DOUBLE_INT_ASM_OP;
-         break;
-#endif
-       }
+/* Use directive OP to assemble an integer object X.  Print OP at the
+   start of the line, followed immediately by the value of X.  */
 
-      if (asm_op)
-       {
-         fputs (asm_op, asm_out_file);
-         output_addr_const (asm_out_file, x);
-         fputc ('\n', asm_out_file);
-         return 1;
-       }
-    }
+void
+assemble_integer_with_op (op, x)
+     const char *op;
+     rtx x;
+{
+  fputs (op, asm_out_file);
+  output_addr_const (asm_out_file, x);
+  fputc ('\n', asm_out_file);
+}
 
-  /* If we couldn't do it that way, there are two other possibilities: First,
-     if the machine can output an explicit byte and this is a 1 byte constant,
-     we can use ASM_OUTPUT_BYTE.  */
+/* The default implementation of the asm_out.integer target hook.  */
 
-#ifdef ASM_OUTPUT_BYTE
-  if (size == 1 && GET_CODE (x) == CONST_INT)
-    {
-      ASM_OUTPUT_BYTE (asm_out_file, INTVAL (x));
-      return 1;
-    }
-#endif
+bool
+default_assemble_integer (x, size, aligned_p)
+     rtx x ATTRIBUTE_UNUSED;
+     unsigned int size ATTRIBUTE_UNUSED;
+     int aligned_p ATTRIBUTE_UNUSED;
+{
+  const char *op = integer_asm_op (size, aligned_p);
+  return op && (assemble_integer_with_op (op, x), true);
+}
 
-  /* If SIZE is larger than a single word, try to output the constant
-     one word at a time.  */
+/* Assemble the integer constant X into an object of SIZE bytes.  ALIGN is
+   the alignment of the integer in bits.  Return 1 if we were able to output
+   the constant, otherwise 0.  If FORCE is non-zero, abort if we can't output
+   the constant.  */
 
-  if (size > UNITS_PER_WORD)
-    {
-      enum machine_mode mode
-       = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
-      unsigned align2 = min_align (align, BITS_PER_WORD);
-      unsigned int i;
+bool
+assemble_integer (x, size, align, force)
+     rtx x;
+     unsigned int size;
+     unsigned int align;
+     int force;
+{
+  int aligned_p;
 
-      for (i = 0; i < size / UNITS_PER_WORD; i++)
-       {
-         rtx word = operand_subword (x, i, 0, mode);
-         if (word == 0)
-           break;
-         if (! assemble_integer (word, UNITS_PER_WORD, align2, 0))
-           break;
-       }
+  aligned_p = (align >= MIN (size * BITS_PER_UNIT, BIGGEST_ALIGNMENT));
 
-      if (i == size / UNITS_PER_WORD)
-       return 1;
-      /* If we output at least one word and then could not finish,
-        there is no valid way to continue.  */
-      if (i > 0)
-       abort ();
-    }
+  /* See if the target hook can handle this kind of object.  */
+  if ((*targetm.asm_out.integer) (x, size, aligned_p))
+    return true;
 
-  /* If unaligned, and this is a constant, emit it one byte at a time.  */
-  if (align < size * BITS_PER_UNIT)
+  /* If the object is a multi-byte one, try splitting it up.  Split
+     it into words it if is multi-word, otherwise split it into bytes.  */
+  if (size > 1)
     {
       enum machine_mode omode, imode;
-      unsigned int i;
+      unsigned int subalign;
+      unsigned int subsize, i;
 
-      omode = mode_for_size (BITS_PER_UNIT, MODE_INT, 0);
+      subsize = size > UNITS_PER_WORD? UNITS_PER_WORD : 1;
+      subalign = MIN (align, subsize * BITS_PER_UNIT);
+      omode = mode_for_size (subsize * BITS_PER_UNIT, MODE_INT, 0);
       imode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
 
-      for (i = 0; i < size; i++)
+      for (i = 0; i < size; i += subsize)
        {
-         rtx byte = simplify_subreg (omode, x, imode, i);
-         if (byte == 0)
-           break;
-         if (! assemble_integer (byte, 1, BITS_PER_UNIT, 0))
+         rtx partial = simplify_subreg (omode, x, imode, i);
+         if (!partial || !assemble_integer (partial, subsize, subalign, 0))
            break;
        }
-
       if (i == size)
-       return 1;
-      /* If we output at least one byte and then could not finish,
-        there is no valid way to continue.  */
+       return true;
+
+      /* If we've printed some of it, but not all of it, there's no going
+        back now.  */
       if (i > 0)
        abort ();
     }
@@ -2084,323 +1993,114 @@ assemble_integer (x, size, align, force)
   if (force)
     abort ();
 
-  return 0;
+  return false;
 }
 \f
-/* Assemble the floating-point constant D into an object of size MODE.  */
-struct assemble_real_args
-{
-  REAL_VALUE_TYPE *d;
-  enum machine_mode mode;
-};
-
-static void
-assemble_real_1 (p)
-     PTR p;
+void
+assemble_real (d, mode, align)
+     REAL_VALUE_TYPE d;
+     enum machine_mode mode;
+     unsigned int align;
 {
-  struct assemble_real_args *args = (struct assemble_real_args *) p;
-  REAL_VALUE_TYPE *d = args->d;
-  enum machine_mode mode = args->mode;
+  long data[4];
+  long l;
+  unsigned int nalign = min_align (align, 32);
 
-  switch (mode)
+  switch (BITS_PER_UNIT)
     {
-#ifdef ASM_OUTPUT_BYTE_FLOAT
-    case QFmode:
-      ASM_OUTPUT_BYTE_FLOAT (asm_out_file, *d);
-      break;
-#endif
-#ifdef ASM_OUTPUT_SHORT_FLOAT
-    case HFmode:
-      ASM_OUTPUT_SHORT_FLOAT (asm_out_file, *d);
-      break;
-#endif
-#ifdef ASM_OUTPUT_THREE_QUARTER_FLOAT
-    case TQFmode:
-      ASM_OUTPUT_THREE_QUARTER_FLOAT (asm_out_file, *d);
-      break;
-#endif
-#ifdef ASM_OUTPUT_FLOAT
-    case SFmode:
-      ASM_OUTPUT_FLOAT (asm_out_file, *d);
+    case 8:
+      switch (mode)
+       {
+       case SFmode:
+         REAL_VALUE_TO_TARGET_SINGLE (d, l);
+         assemble_integer (GEN_INT (l), 4, align, 1);
+         break;
+       case DFmode:
+         REAL_VALUE_TO_TARGET_DOUBLE (d, data);
+         assemble_integer (GEN_INT (data[0]), 4, align, 1);
+         assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
+         break;
+       case XFmode:
+         REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, data);
+         assemble_integer (GEN_INT (data[0]), 4, align, 1);
+         assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
+         assemble_integer (GEN_INT (data[2]), 4, nalign, 1);
+         break;
+       case TFmode:
+         REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, data);
+         assemble_integer (GEN_INT (data[0]), 4, align, 1);
+         assemble_integer (GEN_INT (data[1]), 4, nalign, 1);
+         assemble_integer (GEN_INT (data[2]), 4, nalign, 1);
+         assemble_integer (GEN_INT (data[3]), 4, nalign, 1);
+         break;
+       default:
+         abort ();
+       }
       break;
-#endif
 
-#ifdef ASM_OUTPUT_DOUBLE
-    case DFmode:
-      ASM_OUTPUT_DOUBLE (asm_out_file, *d);
+    case 16:
+      switch (mode)
+       {
+       case HFmode:
+         REAL_VALUE_TO_TARGET_SINGLE (d, l);
+         assemble_integer (GEN_INT (l), 2, align, 1);
+         break;
+       case TQFmode:
+         REAL_VALUE_TO_TARGET_DOUBLE (d, data);
+         assemble_integer (GEN_INT (data[0]), 2, align, 1);
+         assemble_integer (GEN_INT (data[1]), 1, nalign, 1);
+         break;
+       default:
+         abort ();
+       }
       break;
-#endif
 
-#ifdef ASM_OUTPUT_LONG_DOUBLE
-    case XFmode:
-    case TFmode:
-      ASM_OUTPUT_LONG_DOUBLE (asm_out_file, *d);
+    case 32:
+      switch (mode)
+       {
+       case QFmode:
+         REAL_VALUE_TO_TARGET_SINGLE (d, l);
+         assemble_integer (GEN_INT (l), 1, align, 1);
+         break;
+       case HFmode:
+         REAL_VALUE_TO_TARGET_DOUBLE (d, data);
+         assemble_integer (GEN_INT (data[0]), 1, align, 1);
+         assemble_integer (GEN_INT (data[1]), 1, nalign, 1);
+         break;
+       default:
+         abort ();
+       }
       break;
-#endif
 
     default:
       abort ();
     }
 }
-
-void
-assemble_real (d, mode, align)
-     REAL_VALUE_TYPE d;
-     enum machine_mode mode;
-     unsigned int align;
-{
-  struct assemble_real_args args;
-  args.d = &d;
-  args.mode = mode;
-
-  /* We cannot emit unaligned floating point constants.  This is slightly
-     complicated in that we don't know what "unaligned" means exactly.  */
-#ifdef BIGGEST_FIELD_ALIGNMENT
-  if (align >= BIGGEST_FIELD_ALIGNMENT)
-    ;
-  else
-#endif
-  if (align < GET_MODE_ALIGNMENT (mode))
-    abort ();
-
-  if (do_float_handler (assemble_real_1, (PTR) &args))
-    return;
-
-  internal_error ("floating point trap outputting a constant");
-}
 \f
-/* Here we combine duplicate floating constants to make
-   CONST_DOUBLE rtx's, and force those out to memory when necessary.  */
+/* Given an expression EXP with a constant value,
+   reduce it to the sum of an assembler symbol and an integer.
+   Store them both in the structure *VALUE.
+   Abort if EXP does not reduce.  */
 
-/* Return a CONST_DOUBLE or CONST_INT for a value specified as a pair of ints.
-   For an integer, I0 is the low-order word and I1 is the high-order word.
-   For a real number, I0 is the word with the low address
-   and I1 is the word with the high address.  */
+struct addr_const GTY(())
+{
+  rtx base;
+  HOST_WIDE_INT offset;
+};
 
-rtx
-immed_double_const (i0, i1, mode)
-     HOST_WIDE_INT i0, i1;
-     enum machine_mode mode;
+static void
+decode_addr_const (exp, value)
+     tree exp;
+     struct addr_const *value;
 {
-  rtx r;
+  tree target = TREE_OPERAND (exp, 0);
+  int offset = 0;
+  rtx x;
 
-  if (GET_MODE_CLASS (mode) == MODE_INT
-      || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+  while (1)
     {
-      /* We clear out all bits that don't belong in MODE, unless they and our
-        sign bit are all one.  So we get either a reasonable negative value
-        or a reasonable unsigned value for this mode.  */
-      int width = GET_MODE_BITSIZE (mode);
-      if (width < HOST_BITS_PER_WIDE_INT
-         && ((i0 & ((HOST_WIDE_INT) (-1) << (width - 1)))
-             != ((HOST_WIDE_INT) (-1) << (width - 1))))
-       i0 &= ((HOST_WIDE_INT) 1 << width) - 1, i1 = 0;
-      else if (width == HOST_BITS_PER_WIDE_INT
-              && ! (i1 == ~0 && i0 < 0))
-       i1 = 0;
-      else if (width > 2 * HOST_BITS_PER_WIDE_INT)
-       /* We cannot represent this value as a constant.  */
-       abort ();
-
-      /* If this would be an entire word for the target, but is not for
-        the host, then sign-extend on the host so that the number will look
-        the same way on the host that it would on the target.
-
-        For example, when building a 64 bit alpha hosted 32 bit sparc
-        targeted compiler, then we want the 32 bit unsigned value -1 to be
-        represented as a 64 bit value -1, and not as 0x00000000ffffffff.
-        The later confuses the sparc backend.  */
-
-      if (width < HOST_BITS_PER_WIDE_INT
-         && (i0 & ((HOST_WIDE_INT) 1 << (width - 1))))
-       i0 |= ((HOST_WIDE_INT) (-1) << width);
-
-      /* If MODE fits within HOST_BITS_PER_WIDE_INT, always use a CONST_INT.
-
-        ??? Strictly speaking, this is wrong if we create a CONST_INT
-        for a large unsigned constant with the size of MODE being
-        HOST_BITS_PER_WIDE_INT and later try to interpret that constant in a
-        wider mode.  In that case we will mis-interpret it as a negative
-        number.
-
-        Unfortunately, the only alternative is to make a CONST_DOUBLE
-        for any constant in any mode if it is an unsigned constant larger
-        than the maximum signed integer in an int on the host.  However,
-        doing this will break everyone that always expects to see a CONST_INT
-        for SImode and smaller.
-
-        We have always been making CONST_INTs in this case, so nothing new
-        is being broken.  */
-
-      if (width <= HOST_BITS_PER_WIDE_INT)
-       i1 = (i0 < 0) ? ~(HOST_WIDE_INT) 0 : 0;
-
-      /* If this integer fits in one word, return a CONST_INT.  */
-      if ((i1 == 0 && i0 >= 0)
-         || (i1 == ~0 && i0 < 0))
-       return GEN_INT (i0);
-
-      /* We use VOIDmode for integers.  */
-      mode = VOIDmode;
-    }
-
-  /* Search the chain for an existing CONST_DOUBLE with the right value.
-     If one is found, return it.  */
-  if (cfun != 0)
-    for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r))
-      if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1
-         && GET_MODE (r) == mode)
-       return r;
-
-  /* No; make a new one and add it to the chain.  */
-  r = gen_rtx_CONST_DOUBLE (mode, const0_rtx, i0, i1);
-
-  /* Don't touch const_double_chain if not inside any function.  */
-  if (current_function_decl != 0)
-    {
-      CONST_DOUBLE_CHAIN (r) = const_double_chain;
-      const_double_chain = r;
-    }
-
-  return r;
-}
-
-/* Return a CONST_DOUBLE for a specified `double' value
-   and machine mode.  */
-
-rtx
-immed_real_const_1 (d, mode)
-     REAL_VALUE_TYPE d;
-     enum machine_mode mode;
-{
-  union real_extract u;
-  rtx r;
-
-  /* Get the desired `double' value as a sequence of ints
-     since that is how they are stored in a CONST_DOUBLE.  */
-
-  u.d = d;
-
-  /* Detect special cases.  */
-  if (REAL_VALUES_IDENTICAL (dconst0, d))
-    return CONST0_RTX (mode);
-
-  /* Check for NaN first, because some ports (specifically the i386) do not
-     emit correct ieee-fp code by default, and thus will generate a core
-     dump here if we pass a NaN to REAL_VALUES_EQUAL and if REAL_VALUES_EQUAL
-     does a floating point comparison.  */
-  else if (! REAL_VALUE_ISNAN (d) && REAL_VALUES_EQUAL (dconst1, d))
-    return CONST1_RTX (mode);
-
-  if (sizeof u == sizeof (HOST_WIDE_INT))
-    return immed_double_const (u.i[0], 0, mode);
-  if (sizeof u == 2 * sizeof (HOST_WIDE_INT))
-    return immed_double_const (u.i[0], u.i[1], mode);
-
-  /* The rest of this function handles the case where
-     a float value requires more than 2 ints of space.
-     It will be deleted as dead code on machines that don't need it.  */
-
-  /* Search the chain for an existing CONST_DOUBLE with the right value.
-     If one is found, return it.  */
-  if (cfun != 0)
-    for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r))
-      if (! memcmp ((char *) &CONST_DOUBLE_LOW (r), (char *) &u, sizeof u)
-         && GET_MODE (r) == mode)
-       return r;
-
-  /* No; make a new one and add it to the chain.
-
-     We may be called by an optimizer which may be discarding any memory
-     allocated during its processing (such as combine and loop).  However,
-     we will be leaving this constant on the chain, so we cannot tolerate
-     freed memory.  */
-  r = rtx_alloc (CONST_DOUBLE);
-  PUT_MODE (r, mode);
-  memcpy ((char *) &CONST_DOUBLE_LOW (r), (char *) &u, sizeof u);
-
-  /* If we aren't inside a function, don't put r on the
-     const_double_chain.  */
-  if (current_function_decl != 0)
-    {
-      CONST_DOUBLE_CHAIN (r) = const_double_chain;
-      const_double_chain = r;
-    }
-  else
-    CONST_DOUBLE_CHAIN (r) = NULL_RTX;
-
-  /* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the
-     chain, but has not been allocated memory.  Actual use of CONST_DOUBLE_MEM
-     is only through force_const_mem.  */
-
-  CONST_DOUBLE_MEM (r) = const0_rtx;
-
-  return r;
-}
-
-/* Return a CONST_DOUBLE rtx for a value specified by EXP,
-   which must be a REAL_CST tree node.  */
-
-rtx
-immed_real_const (exp)
-     tree exp;
-{
-  return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp)));
-}
-
-/* At the end of a function, forget the memory-constants
-   previously made for CONST_DOUBLEs.  Mark them as not on real_constant_chain.
-   Also clear out real_constant_chain and clear out all the chain-pointers.  */
-
-void
-clear_const_double_mem ()
-{
-  rtx r, next;
-  enum machine_mode mode;
-  int i;
-
-  for (r = const_double_chain; r; r = next)
-    {
-      next = CONST_DOUBLE_CHAIN (r);
-      CONST_DOUBLE_CHAIN (r) = 0;
-      CONST_DOUBLE_MEM (r) = cc0_rtx;
-    }
-  const_double_chain = 0;
-
-  for (i = 0; i <= 2; i++)
-    for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
-         mode = GET_MODE_WIDER_MODE (mode))
-      {
-       r = const_tiny_rtx[i][(int) mode];
-       CONST_DOUBLE_CHAIN (r) = 0;
-       CONST_DOUBLE_MEM (r) = cc0_rtx;
-      }
-}
-\f
-/* Given an expression EXP with a constant value,
-   reduce it to the sum of an assembler symbol and an integer.
-   Store them both in the structure *VALUE.
-   Abort if EXP does not reduce.  */
-
-struct addr_const
-{
-  rtx base;
-  HOST_WIDE_INT offset;
-};
-
-static void
-decode_addr_const (exp, value)
-     tree exp;
-     struct addr_const *value;
-{
-  tree target = TREE_OPERAND (exp, 0);
-  int offset = 0;
-  rtx x;
-
-  while (1)
-    {
-      if (TREE_CODE (target) == COMPONENT_REF
-         && host_integerp (byte_position (TREE_OPERAND (target, 1)), 0))
+      if (TREE_CODE (target) == COMPONENT_REF
+         && host_integerp (byte_position (TREE_OPERAND (target, 1)), 0))
 
        {
          offset += int_byte_position (TREE_OPERAND (target, 1));
@@ -2435,7 +2135,9 @@ decode_addr_const (exp, value)
     case COMPLEX_CST:
     case CONSTRUCTOR:
     case INTEGER_CST:
-      x = TREE_CST_RTL (target);
+      /* This constant should have been output already, but we can't simply
+        use TREE_CST_RTL since INTEGER_CST doesn't have one.  */
+      x = output_constant_def (target, 1);
       break;
 
     default:
@@ -2450,94 +2152,67 @@ decode_addr_const (exp, value)
   value->offset = offset;
 }
 \f
-enum kind { RTX_DOUBLE, RTX_INT };
-struct rtx_const
+/* We do RTX_UNSPEC + XINT (blah), so nothing can go after RTX_UNSPEC.  */
+enum kind { RTX_UNKNOWN, RTX_DOUBLE, RTX_VECTOR, RTX_INT, RTX_UNSPEC };
+struct rtx_const GTY(())
 {
   ENUM_BITFIELD(kind) kind : 16;
   ENUM_BITFIELD(machine_mode) mode : 16;
-  union {
-    union real_extract du;
-    struct addr_const addr;
-    struct {HOST_WIDE_INT high, low;} di;
-  } un;
+  union rtx_const_un {
+    REAL_VALUE_TYPE du;
+    struct addr_const GTY ((tag ("1"))) addr;
+    struct rtx_const_u_di {
+      HOST_WIDE_INT high;
+      HOST_WIDE_INT low;
+    } GTY ((tag ("0"))) di;
+
+    /* The max vector size we have is 8 wide.  This should be enough.  */
+    struct rtx_const_vec {
+      HOST_WIDE_INT veclo;
+      HOST_WIDE_INT vechi; 
+    } GTY ((tag ("2"))) vec[16];
+  } GTY ((desc ("%1.kind >= RTX_INT"), descbits ("1"))) un;
 };
 
 /* Uniquize all constants that appear in memory.
    Each constant in memory thus far output is recorded
-   in `const_hash_table' with a `struct constant_descriptor'
-   that contains a polish representation of the value of
-   the constant.
-
-   We cannot store the trees in the hash table
-   because the trees may be temporary.  */
+   in `const_hash_table'.  */
 
-struct constant_descriptor
+struct constant_descriptor_tree GTY(())
 {
-  struct constant_descriptor *next;
+  /* More constant_descriptors with the same hash code.  */
+  struct constant_descriptor_tree *next;
+
+  /* The label of the constant.  */
   const char *label;
+
+  /* A MEM for the constant.  */
   rtx rtl;
-  /* Make sure the data is reasonably aligned.  */
-  union
-  {
-    unsigned char contents[1];
-#ifdef HAVE_LONG_DOUBLE
-    long double d;
-#else
-    double d;
-#endif
-  } u;
+
+  /* The value of the constant.  */
+  tree value;
 };
 
 #define HASHBITS 30
 #define MAX_HASH_TABLE 1009
-static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE];
+static GTY(()) struct constant_descriptor_tree *
+  const_hash_table[MAX_HASH_TABLE];
 
-#define STRHASH(x) ((hashval_t)((long)(x) >> 3))
+/* We maintain a hash table of STRING_CST values.  Unless we are asked to force
+   out a string constant, we defer output of the constants until we know
+   they are actually used.  This will be if something takes its address or if
+   there is a usage of the string in the RTL of a function.  */
 
-struct deferred_string
+#define STRHASH(x) ((hashval_t) ((long) (x) >> 3))
+
+struct deferred_string GTY(())
 {
   const char *label;
   tree exp;
   int labelno;
 };
 
-static htab_t const_str_htab;
-
-/* Mark a const_hash_table descriptor for GC.  */
-
-static void
-mark_const_hash_entry (ptr)
-     void *ptr;
-{
-  struct constant_descriptor *desc = * (struct constant_descriptor **) ptr;
-
-  while (desc)
-    {
-      ggc_mark_rtx (desc->rtl);
-      desc = desc->next;
-    }
-}
-
-/* Mark the hash-table element X (which is really a pointer to an
-   struct deferred_string *).  */
-
-static int
-mark_const_str_htab_1 (x, data)
-     void **x;
-     void *data ATTRIBUTE_UNUSED;
-{
-  ggc_mark_tree (((struct deferred_string *) *x)->exp);
-  return 1;
-}
-
-/* Mark a const_str_htab for GC.  */
-
-static void
-mark_const_str_htab (htab)
-     void *htab;
-{
-  htab_traverse (*((htab_t *) htab), mark_const_str_htab_1, NULL);
-}
+static GTY ((param_is (struct deferred_string))) htab_t const_str_htab;
 
 /* Returns a hash code for X (which is a really a
    struct deferred_string *).  */
@@ -2561,15 +2236,6 @@ const_str_htab_eq (x, y)
   return (((const struct deferred_string *) x)->label == (const char *) y);
 }
 
-/* Delete the hash table entry dfsp.  */
-
-static void
-const_str_htab_del (dfsp)
-    void *dfsp;
-{
-  free (dfsp);
-}
-
 /* Compute a hash code for a constant expression.  */
 
 static int
@@ -2641,6 +2307,7 @@ const_hash (exp)
        }
 
     case ADDR_EXPR:
+    case FDESC_EXPR:
       {
        struct addr_const value;
 
@@ -2657,7 +2324,7 @@ const_hash (exp)
        else if (GET_CODE (value.base) == LABEL_REF)
          hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13;
        else
-         abort();
+         abort ();
 
        hi &= (1 << HASHBITS) - 1;
        hi %= MAX_HASH_TABLE;
@@ -2688,528 +2355,232 @@ const_hash (exp)
   hi %= MAX_HASH_TABLE;
   return hi;
 }
-\f
-/* Compare a constant expression EXP with a constant-descriptor DESC.
-   Return 1 if DESC describes a constant with the same value as EXP.  */
-
-static int
-compare_constant (exp, desc)
-     tree exp;
-     struct constant_descriptor *desc;
-{
-  return 0 != compare_constant_1 (exp, desc->u.contents);
-}
 
-/* Compare constant expression EXP with a substring P of a constant descriptor.
-   If they match, return a pointer to the end of the substring matched.
-   If they do not match, return 0.
+/* Compare t1 and t2, and return 1 only if they are known to result in
+   the same bit pattern on output.  */
 
-   Since descriptors are written in polish prefix notation,
-   this function can be used recursively to test one operand of EXP
-   against a subdescriptor, and if it succeeds it returns the
-   address of the subdescriptor for the next operand.  */
-
-static const unsigned char *
-compare_constant_1 (exp, p)
-     tree exp;
-     const unsigned char *p;
+static int
+compare_constant (t1, t2)
+     tree t1;
+     tree t2;
 {
-  const unsigned char *strp;
-  int len;
-  enum tree_code code = TREE_CODE (exp);
+  enum tree_code typecode;
 
-  if (code != (enum tree_code) *p++)
+  if (t1 == NULL_TREE)
+    return t2 == NULL_TREE;
+  if (t2 == NULL_TREE)
     return 0;
 
-  /* Either set STRP, P and LEN to pointers and length to compare and exit the
-     switch, or return the result of the comparison.  */
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return 0;
 
-  switch (code)
+  switch (TREE_CODE (t1))
     {
     case INTEGER_CST:
       /* Integer constants are the same only if the same width of type.  */
-      if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
+      if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
        return 0;
-
-      strp = (unsigned char *) &TREE_INT_CST (exp);
-      len = sizeof TREE_INT_CST (exp);
-      break;
+      return tree_int_cst_equal (t1, t2);
 
     case REAL_CST:
       /* Real constants are the same only if the same width of type.  */
-      if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
+      if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
        return 0;
 
-      strp = (unsigned char *) &TREE_REAL_CST (exp);
-      len = sizeof TREE_REAL_CST (exp);
-      break;
+      return REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2));
 
     case STRING_CST:
       if (flag_writable_strings)
        return 0;
 
-      if ((enum machine_mode) *p++ != TYPE_MODE (TREE_TYPE (exp)))
-       return 0;
-
-      strp = (const unsigned char *)TREE_STRING_POINTER (exp);
-      len = TREE_STRING_LENGTH (exp);
-      if (memcmp ((char *) &TREE_STRING_LENGTH (exp), p,
-               sizeof TREE_STRING_LENGTH (exp)))
+      if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
        return 0;
 
-      p += sizeof TREE_STRING_LENGTH (exp);
-      break;
+      return (TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2)
+             && ! memcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2),
+                        TREE_STRING_LENGTH (t1)));
 
     case COMPLEX_CST:
-      p = compare_constant_1 (TREE_REALPART (exp), p);
-      if (p == 0)
-       return 0;
-
-      return compare_constant_1 (TREE_IMAGPART (exp), p);
+      return (compare_constant (TREE_REALPART (t1), TREE_REALPART (t2))
+             && compare_constant (TREE_IMAGPART (t1), TREE_IMAGPART (t2)));
 
     case CONSTRUCTOR:
-      if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
-       {
-         int xlen = len = int_size_in_bytes (TREE_TYPE (exp));
-         unsigned char *tmp = (unsigned char *) alloca (len);
-
-         get_set_constructor_bytes (exp, tmp, len);
-         strp = (unsigned char *) tmp;
-         if (memcmp ((char *) &xlen, p, sizeof xlen))
-           return 0;
+      typecode = TREE_CODE (TREE_TYPE (t1));
+      if (typecode != TREE_CODE (TREE_TYPE (t2)))
+       return 0;
 
-         p += sizeof xlen;
-         break;
-       }
-      else
+      if (typecode == SET_TYPE)
        {
-         tree link;
-         int length = list_length (CONSTRUCTOR_ELTS (exp));
-         tree type;
-         enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-         int have_purpose = 0;
+         int len = int_size_in_bytes (TREE_TYPE (t2));
+         unsigned char *tmp1, *tmp2;
 
-         for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-           if (TREE_PURPOSE (link))
-             have_purpose = 1;
-
-         if (memcmp ((char *) &length, p, sizeof length))
+         if (int_size_in_bytes (TREE_TYPE (t1)) != len)
            return 0;
 
-         p += sizeof length;
-
-         /* For record constructors, insist that the types match.
-            For arrays, just verify both constructors are for arrays.
-            Then insist that either both or none have any TREE_PURPOSE
-            values.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
-           type = TREE_TYPE (exp);
-         else
-           type = 0;
+         tmp1 = (unsigned char *) alloca (len);
+         tmp2 = (unsigned char *) alloca (len);
 
-         if (memcmp ((char *) &type, p, sizeof type))
+         if (get_set_constructor_bytes (t1, tmp1, len) != NULL_TREE)
+           return 0;
+         if (get_set_constructor_bytes (t2, tmp2, len) != NULL_TREE)
            return 0;
 
-         if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
+         return memcmp (tmp1, tmp2, len) != 0;
+       }
+      else
+       {
+         tree l1, l2;
+
+         if (typecode == ARRAY_TYPE)
            {
-             if (memcmp ((char *) &mode, p, sizeof mode))
+             HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1));
+             /* For arrays, check that the sizes all match.  */
+             if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))
+                 || size_1 == -1
+                 || size_1 != int_size_in_bytes (TREE_TYPE (t2)))
                return 0;
-
-             p += sizeof mode;
            }
-
-         p += sizeof type;
-
-         if (memcmp ((char *) &have_purpose, p, sizeof have_purpose))
-           return 0;
-
-         p += sizeof have_purpose;
-
-         /* For arrays, insist that the size in bytes match.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
+         else
            {
-             HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
-
-             if (memcmp ((char *) &size, p, sizeof size))
+             /* For record and union constructors, require exact type
+                 equality.  */
+             if (TREE_TYPE (t1) != TREE_TYPE (t2))
                return 0;
-
-             p += sizeof size;
            }
 
-         for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
+         for (l1 = CONSTRUCTOR_ELTS (t1), l2 = CONSTRUCTOR_ELTS (t2);
+              l1 && l2;
+              l1 = TREE_CHAIN (l1), l2 = TREE_CHAIN (l2))
            {
-             if (TREE_VALUE (link))
+             /* Check that each value is the same...  */
+             if (! compare_constant (TREE_VALUE (l1), TREE_VALUE (l2)))
+               return 0;
+             /* ... and that they apply to the same fields!  */
+             if (typecode == ARRAY_TYPE)
                {
-                 if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0)
+                 if (! compare_constant (TREE_PURPOSE (l1),
+                                         TREE_PURPOSE (l2)))
                    return 0;
                }
              else
                {
-                 tree zero = 0;
-
-                 if (memcmp ((char *) &zero, p, sizeof zero))
-                   return 0;
-
-                 p += sizeof zero;
-               }
-
-             if (TREE_PURPOSE (link)
-                 && TREE_CODE (TREE_PURPOSE (link)) == FIELD_DECL)
-               {
-                 if (memcmp ((char *) &TREE_PURPOSE (link), p,
-                           sizeof TREE_PURPOSE (link)))
-                   return 0;
-
-                 p += sizeof TREE_PURPOSE (link);
-               }
-             else if (TREE_PURPOSE (link))
-               {
-                 if ((p = compare_constant_1 (TREE_PURPOSE (link), p)) == 0)
-                   return 0;
-               }
-             else if (have_purpose)
-               {
-                 int zero = 0;
-
-                 if (memcmp ((char *) &zero, p, sizeof zero))
+                 if (TREE_PURPOSE (l1) != TREE_PURPOSE (l2))
                    return 0;
-
-                 p += sizeof zero;
                }
            }
 
-         return p;
+         return l1 == NULL_TREE && l2 == NULL_TREE;
        }
 
     case ADDR_EXPR:
+    case FDESC_EXPR:
       {
-       struct addr_const value;
-
-       decode_addr_const (exp, &value);
-       strp = (unsigned char *) &value.offset;
-       len = sizeof value.offset;
-       /* Compare the offset.  */
-       while (--len >= 0)
-         if (*p++ != *strp++)
-           return 0;
+       struct addr_const value1, value2;
 
-       /* Compare symbol name.  */
-       strp = (const unsigned char *) XSTR (value.base, 0);
-       len = strlen ((const char *) strp) + 1;
+       decode_addr_const (t1, &value1);
+       decode_addr_const (t2, &value2);
+       return (value1.offset == value2.offset
+               && strcmp (XSTR (value1.base, 0), XSTR (value2.base, 0)) == 0);
       }
-      break;
 
     case PLUS_EXPR:
     case MINUS_EXPR:
     case RANGE_EXPR:
-      p = compare_constant_1 (TREE_OPERAND (exp, 0), p);
-      if (p == 0)
-       return 0;
-
-      return compare_constant_1 (TREE_OPERAND (exp, 1), p);
+      return (compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))
+             && compare_constant(TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)));
 
     case NOP_EXPR:
     case CONVERT_EXPR:
     case NON_LVALUE_EXPR:
-      return compare_constant_1 (TREE_OPERAND (exp, 0), p);
+      return compare_constant (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
 
     default:
-      if (lang_expand_constant)
-        {
-          exp = (*lang_expand_constant) (exp);
-          return compare_constant_1 (exp, p);
-        }
-      return 0;
+      {
+       tree nt1, nt2;
+       nt1 = (*lang_hooks.expand_constant) (t1);
+       nt2 = (*lang_hooks.expand_constant) (t2);
+       if (nt1 != t1 || nt2 != t2)
+         return compare_constant (nt1, nt2);
+       else
+         return 0;
+      }
     }
 
-  /* Compare constant contents.  */
-  while (--len >= 0)
-    if (*p++ != *strp++)
-      return 0;
-
-  return p;
+  /* Should not get here.  */
+  abort ();
 }
 \f
-/* Construct a constant descriptor for the expression EXP.
-   It is up to the caller to enter the descriptor in the hash table.  */
+/* Record a list of constant expressions that were passed to
+   output_constant_def but that could not be output right away.  */
 
-static struct constant_descriptor *
-record_constant (exp)
-     tree exp;
+struct deferred_constant
 {
-  struct constant_descriptor *next = 0;
-  char *label = 0;
-  rtx rtl = 0;
-  int pad;
+  struct deferred_constant *next;
+  tree exp;
+  int reloc;
+  int labelno;
+};
+
+static struct deferred_constant *deferred_constants;
 
-  /* Make a struct constant_descriptor.  The first three pointers will
-     be filled in later.  Here we just leave space for them.  */
+/* Another list of constants which should be output after the
+   function.  */
+static struct deferred_constant *after_function_constants;
 
-  obstack_grow (&permanent_obstack, (char *) &next, sizeof next);
-  obstack_grow (&permanent_obstack, (char *) &label, sizeof label);
-  obstack_grow (&permanent_obstack, (char *) &rtl, sizeof rtl);
+/* Nonzero means defer output of addressed subconstants
+   (i.e., those for which output_constant_def is called.)  */
+static int defer_addressed_constants_flag;
 
-  /* Align the descriptor for the data payload.  */
-  pad = (offsetof (struct constant_descriptor, u)
-        - offsetof(struct constant_descriptor, rtl)
-        - sizeof(next->rtl));
-  if (pad > 0)
-    obstack_blank (&permanent_obstack, pad);
+/* Start deferring output of subconstants.  */
 
-  record_constant_1 (exp);
-  return (struct constant_descriptor *) obstack_finish (&permanent_obstack);
+void
+defer_addressed_constants ()
+{
+  defer_addressed_constants_flag++;
 }
 
-/* Add a description of constant expression EXP
-   to the object growing in `permanent_obstack'.
-   No need to return its address; the caller will get that
-   from the obstack when the object is complete.  */
+/* Stop deferring output of subconstants,
+   and output now all those that have been deferred.  */
 
-static void
-record_constant_1 (exp)
-     tree exp;
+void
+output_deferred_addressed_constants ()
 {
-  const unsigned char *strp;
-  int len;
-  enum tree_code code = TREE_CODE (exp);
+  struct deferred_constant *p, *next;
+
+  defer_addressed_constants_flag--;
 
-  obstack_1grow (&permanent_obstack, (unsigned int) code);
+  if (defer_addressed_constants_flag > 0)
+    return;
 
-  switch (code)
+  for (p = deferred_constants; p; p = next)
     {
-    case INTEGER_CST:
-      obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
-      strp = (unsigned char *) &TREE_INT_CST (exp);
-      len = sizeof TREE_INT_CST (exp);
-      break;
+      output_constant_def_contents (p->exp, p->reloc, p->labelno);
+      next = p->next;
+      free (p);
+    }
 
-    case REAL_CST:
-      obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
-      strp = (unsigned char *) &TREE_REAL_CST (exp);
-      len = sizeof TREE_REAL_CST (exp);
-      break;
+  deferred_constants = 0;
+}
 
-    case STRING_CST:
-      if (flag_writable_strings)
-       return;
+/* Output any constants which should appear after a function.  */
 
-      obstack_1grow (&permanent_obstack, TYPE_MODE (TREE_TYPE (exp)));
-      strp = (const unsigned char *) TREE_STRING_POINTER (exp);
-      len = TREE_STRING_LENGTH (exp);
-      obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp),
-                   sizeof TREE_STRING_LENGTH (exp));
-      break;
+static void
+output_after_function_constants ()
+{
+  struct deferred_constant *p, *next;
 
-    case COMPLEX_CST:
-      record_constant_1 (TREE_REALPART (exp));
-      record_constant_1 (TREE_IMAGPART (exp));
-      return;
-
-    case CONSTRUCTOR:
-      if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
-       {
-         int nbytes = int_size_in_bytes (TREE_TYPE (exp));
-         obstack_grow (&permanent_obstack, &nbytes, sizeof (nbytes));
-         obstack_blank (&permanent_obstack, nbytes);
-         get_set_constructor_bytes
-           (exp, (unsigned char *) permanent_obstack.next_free-nbytes,
-            nbytes);
-         return;
-       }
-      else
-       {
-         tree link;
-         int length = list_length (CONSTRUCTOR_ELTS (exp));
-         enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-         tree type;
-         int have_purpose = 0;
-
-         for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-           if (TREE_PURPOSE (link))
-             have_purpose = 1;
-
-         obstack_grow (&permanent_obstack, (char *) &length, sizeof length);
-
-         /* For record constructors, insist that the types match.
-            For arrays, just verify both constructors are for arrays
-            of the same mode.  Then insist that either both or none
-            have any TREE_PURPOSE values.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
-           type = TREE_TYPE (exp);
-         else
-           type = 0;
-
-         obstack_grow (&permanent_obstack, (char *) &type, sizeof type);
-         if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
-           obstack_grow (&permanent_obstack, &mode, sizeof mode);
-
-         obstack_grow (&permanent_obstack, (char *) &have_purpose,
-                       sizeof have_purpose);
-
-         /* For arrays, insist that the size in bytes match.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
-           {
-             HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
-             obstack_grow (&permanent_obstack, (char *) &size, sizeof size);
-           }
-
-         for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-           {
-             if (TREE_VALUE (link))
-               record_constant_1 (TREE_VALUE (link));
-             else
-               {
-                 tree zero = 0;
-
-                 obstack_grow (&permanent_obstack,
-                               (char *) &zero, sizeof zero);
-               }
-
-             if (TREE_PURPOSE (link)
-                 && TREE_CODE (TREE_PURPOSE (link)) == FIELD_DECL)
-               obstack_grow (&permanent_obstack,
-                             (char *) &TREE_PURPOSE (link),
-                             sizeof TREE_PURPOSE (link));
-             else if (TREE_PURPOSE (link))
-               record_constant_1 (TREE_PURPOSE (link));
-             else if (have_purpose)
-               {
-                 int zero = 0;
-
-                 obstack_grow (&permanent_obstack,
-                               (char *) &zero, sizeof zero);
-               }
-           }
-       }
-      return;
-
-    case ADDR_EXPR:
-      {
-       struct addr_const value;
-
-       decode_addr_const (exp, &value);
-       /* Record the offset.  */
-       obstack_grow (&permanent_obstack,
-                     (char *) &value.offset, sizeof value.offset);
-
-       switch (GET_CODE (value.base))
-         {
-         case SYMBOL_REF:
-           /* Record the symbol name.  */
-           obstack_grow (&permanent_obstack, XSTR (value.base, 0),
-                         strlen (XSTR (value.base, 0)) + 1);
-           break;
-         case LABEL_REF:
-           /* Record the address of the CODE_LABEL.  It may not have
-              been emitted yet, so it's UID may be zero.  But pointer
-              identity is good enough.  */
-           obstack_grow (&permanent_obstack, &XEXP (value.base, 0),
-                         sizeof (rtx));
-           break;
-         default:
-           abort ();
-         }
-      }
-      return;
-
-    case PLUS_EXPR:
-    case MINUS_EXPR:
-    case RANGE_EXPR:
-      record_constant_1 (TREE_OPERAND (exp, 0));
-      record_constant_1 (TREE_OPERAND (exp, 1));
-      return;
-
-    case NOP_EXPR:
-    case CONVERT_EXPR:
-    case NON_LVALUE_EXPR:
-      record_constant_1 (TREE_OPERAND (exp, 0));
-      return;
-
-    default:
-      if (lang_expand_constant)
-        {
-          exp = (*lang_expand_constant) (exp);
-          record_constant_1 (exp);
-        }
-      return;
-    }
-
-  /* Record constant contents.  */
-  obstack_grow (&permanent_obstack, strp, len);
-}
-\f
-/* Record a list of constant expressions that were passed to
-   output_constant_def but that could not be output right away.  */
-
-struct deferred_constant
-{
-  struct deferred_constant *next;
-  tree exp;
-  int reloc;
-  int labelno;
-};
-
-static struct deferred_constant *deferred_constants;
-
-/* Another list of constants which should be output after the
-   function.  */
-static struct deferred_constant *after_function_constants;
-
-/* Nonzero means defer output of addressed subconstants
-   (i.e., those for which output_constant_def is called.)  */
-static int defer_addressed_constants_flag;
-
-/* Start deferring output of subconstants.  */
-
-void
-defer_addressed_constants ()
-{
-  defer_addressed_constants_flag++;
-}
-
-/* Stop deferring output of subconstants,
-   and output now all those that have been deferred.  */
-
-void
-output_deferred_addressed_constants ()
-{
-  struct deferred_constant *p, *next;
-
-  defer_addressed_constants_flag--;
-
-  if (defer_addressed_constants_flag > 0)
-    return;
-
-  for (p = deferred_constants; p; p = next)
-    {
-      output_constant_def_contents (p->exp, p->reloc, p->labelno);
-      next = p->next;
-      free (p);
-    }
-
-  deferred_constants = 0;
-}
-
-/* Output any constants which should appear after a function.  */
-
-static void
-output_after_function_constants ()
-{
-  struct deferred_constant *p, *next;
-
-  for (p = after_function_constants; p; p = next)
-    {
-      output_constant_def_contents (p->exp, p->reloc, p->labelno);
-      next = p->next;
-      free (p);
-    }
+  for (p = after_function_constants; p; p = next)
+    {
+      output_constant_def_contents (p->exp, p->reloc, p->labelno);
+      next = p->next;
+      free (p);
+    }
 
   after_function_constants = 0;
 }
 
-/* Make a copy of the whole tree structure for a constant.
-   This handles the same types of nodes that compare_constant
-   and record_constant handle.  */
+/* Make a copy of the whole tree structure for a constant.  This
+   handles the same types of nodes that compare_constant handles.  */
 
 static tree
 copy_constant (exp)
@@ -3265,7 +2636,14 @@ copy_constant (exp)
       }
 
     default:
-      abort ();
+      {
+       tree t;
+       t = (*lang_hooks.expand_constant) (exp);
+       if (t != exp)
+         return copy_constant (t);
+       else
+         abort ();
+      }
     }
 }
 \f
@@ -3289,15 +2667,19 @@ output_constant_def (exp, defer)
      int defer;
 {
   int hash;
-  struct constant_descriptor *desc;
+  struct constant_descriptor_tree *desc;
   struct deferred_string **defstr;
   char label[256];
   int reloc;
   int found = 1;
   int after_function = 0;
   int labelno = -1;
+  rtx rtl;
 
-  if (TREE_CST_RTL (exp))
+  /* We can't just use the saved RTL if this is a deferred string constant
+     and we are not to defer anymore.  */
+  if (TREE_CODE (exp) != INTEGER_CST && TREE_CST_RTL (exp)
+      && (defer || !STRING_POOL_ADDRESS_P (XEXP (TREE_CST_RTL (exp), 0))))
     return TREE_CST_RTL (exp);
 
   /* Make sure any other constants whose addresses appear in EXP
@@ -3312,7 +2694,7 @@ output_constant_def (exp, defer)
   hash = const_hash (exp) % MAX_HASH_TABLE;
 
   for (desc = const_hash_table[hash]; desc; desc = desc->next)
-    if (compare_constant (exp, desc))
+    if (compare_constant (exp, desc->value))
       break;
 
   if (desc == 0)
@@ -3326,36 +2708,44 @@ output_constant_def (exp, defer)
       labelno = const_labelno++;
       ASM_GENERATE_INTERNAL_LABEL (label, "LC", labelno);
 
-      desc = record_constant (exp);
+      desc = ggc_alloc (sizeof (*desc));
       desc->next = const_hash_table[hash];
       desc->label = ggc_strdup (label);
+      desc->value = copy_constant (exp);
       const_hash_table[hash] = desc;
 
       /* We have a symbol name; construct the SYMBOL_REF and the MEM.  */
-      desc->rtl
+      rtl = desc->rtl
        = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
                       gen_rtx_SYMBOL_REF (Pmode, desc->label));
 
-      set_mem_attributes (desc->rtl, exp, 1);
+      set_mem_attributes (rtl, exp, 1);
+      set_mem_alias_set (rtl, 0);
+      set_mem_alias_set (rtl, const_alias_set);
 
       found = 0;
     }
+  else
+    rtl = desc->rtl;
 
-  TREE_CST_RTL (exp) = desc->rtl;
+  if (TREE_CODE (exp) != INTEGER_CST)
+    TREE_CST_RTL (exp) = rtl;
 
   /* Optionally set flags or add text to the name to record information
      such as that it is a function name.  If the name is changed, the macro
      ASM_OUTPUT_LABELREF will have to know how to strip this information.  */
-#ifdef ENCODE_SECTION_INFO
   /* A previously-processed constant would already have section info
      encoded in it.  */
   if (! found)
     {
-      ENCODE_SECTION_INFO (exp);
-      desc->rtl = TREE_CST_RTL (exp);
+      /* Take care not to invoke targetm.encode_section_info for
+        constants which don't have a TREE_CST_RTL.  */
+      if (TREE_CODE (exp) != INTEGER_CST)
+       (*targetm.encode_section_info) (exp, true);
+
+      desc->rtl = rtl;
       desc->label = XSTR (XEXP (desc->rtl, 0), 0);
     }
-#endif
 
 #ifdef CONSTANT_AFTER_FUNCTION_P
   if (current_function_decl != 0
@@ -3364,7 +2754,7 @@ output_constant_def (exp, defer)
 #endif
 
   if (found
-      && STRING_POOL_ADDRESS_P (XEXP (desc->rtl, 0))
+      && STRING_POOL_ADDRESS_P (XEXP (rtl, 0))
       && (!defer || defer_addressed_constants_flag || after_function))
     {
       defstr = (struct deferred_string **)
@@ -3376,7 +2766,7 @@ output_constant_def (exp, defer)
             remove it from deferred string hash table.  */
          found = 0;
          labelno = (*defstr)->labelno;
-         STRING_POOL_ADDRESS_P (XEXP (desc->rtl, 0)) = 0;
+         STRING_POOL_ADDRESS_P (XEXP (rtl, 0)) = 0;
          htab_clear_slot (const_str_htab, (void **) defstr);
        }
     }
@@ -3387,10 +2777,11 @@ output_constant_def (exp, defer)
     {
       if (defer_addressed_constants_flag || after_function)
        {
-         struct deferred_constant *p;
-         p = (struct deferred_constant *) xmalloc (sizeof (struct deferred_constant));
+         struct deferred_constant *p
+           = (struct deferred_constant *)
+             xmalloc (sizeof (struct deferred_constant));
 
-         p->exp = copy_constant (exp);
+         p->exp = desc->value;
          p->reloc = reloc;
          p->labelno = labelno;
          if (after_function)
@@ -3423,19 +2814,19 @@ output_constant_def (exp, defer)
                  struct deferred_string *p;
 
                  p = (struct deferred_string *)
-                     xmalloc (sizeof (struct deferred_string));
+                     ggc_alloc (sizeof (struct deferred_string));
 
-                 p->exp = copy_constant (exp);
+                 p->exp = desc->value;
                  p->label = desc->label;
                  p->labelno = labelno;
                  *defstr = p;
-                 STRING_POOL_ADDRESS_P (XEXP (desc->rtl, 0)) = 1;
+                 STRING_POOL_ADDRESS_P (XEXP (rtl, 0)) = 1;
                }
            }
        }
     }
 
-  return TREE_CST_RTL (exp);
+  return rtl;
 }
 
 /* Now output assembler code to define the label for EXP,
@@ -3458,18 +2849,7 @@ output_constant_def_contents (exp, reloc, labelno)
   if (IN_NAMED_SECTION (exp))
     named_section (exp, NULL, reloc);
   else
-    {
-      /* First switch to text section, except for writable strings.  */
-#ifdef SELECT_SECTION
-      SELECT_SECTION (exp, reloc, align);
-#else
-      if (((TREE_CODE (exp) == STRING_CST) && flag_writable_strings)
-         || (flag_pic && reloc))
-       data_section ();
-      else
-       readonly_data_section ();
-#endif
-    }
+    (*targetm.asm_out.select_section) (exp, reloc, align);
 
   if (align > BITS_PER_UNIT)
     {
@@ -3489,21 +2869,41 @@ output_constant_def_contents (exp, reloc, labelno)
 
 }
 \f
+/* Used in the hash tables to avoid outputting the same constant
+   twice.  Unlike 'struct constant_descriptor_tree', RTX constants
+   are output once per function, not once per file; there seems
+   to be no reason for the difference.  */
+
+struct constant_descriptor_rtx GTY(())
+{
+  /* More constant_descriptors with the same hash code.  */
+  struct constant_descriptor_rtx *next;
+
+  /* The label of the constant.  */
+  const char *label;
+
+  /* A MEM for the constant.  */
+  rtx rtl;
+
+  /* The value of the constant.  */
+  struct rtx_const value;
+};
+
 /* Structure to represent sufficient information about a constant so that
    it can be output when the constant pool is output, so that function
    integration can be done, and to simplify handling on machines that reference
    constant pool as base+displacement.  */
 
-struct pool_constant
+struct pool_constant GTY(())
 {
-  struct constant_descriptor *desc;
-  struct pool_constant *next, *next_sym;
-  const char *label;
+  struct constant_descriptor_rtx *desc;
+  struct pool_constant *next;
+  struct pool_constant *next_sym;
   rtx constant;
   enum machine_mode mode;
   int labelno;
-  int align;
-  int offset;
+  unsigned int align;
+  HOST_WIDE_INT offset;
   int mark;
 };
 
@@ -3520,94 +2920,19 @@ init_varasm_status (f)
      struct function *f;
 {
   struct varasm_status *p;
-  p = (struct varasm_status *) xmalloc (sizeof (struct varasm_status));
+  p = (struct varasm_status *) ggc_alloc (sizeof (struct varasm_status));
   f->varasm = p;
   p->x_const_rtx_hash_table
-    = ((struct constant_descriptor **)
-       xcalloc (MAX_RTX_HASH_TABLE, sizeof (struct constant_descriptor *)));
+    = ((struct constant_descriptor_rtx **)
+       ggc_alloc_cleared (MAX_RTX_HASH_TABLE
+                         * sizeof (struct constant_descriptor_rtx *)));
   p->x_const_rtx_sym_hash_table
     = ((struct pool_constant **)
-       xcalloc (MAX_RTX_HASH_TABLE, sizeof (struct pool_constant *)));
+       ggc_alloc_cleared (MAX_RTX_HASH_TABLE
+                         * sizeof (struct pool_constant *)));
 
   p->x_first_pool = p->x_last_pool = 0;
   p->x_pool_offset = 0;
-  p->x_const_double_chain = 0;
-}
-
-/* Nested functions diddle with our const_double_chain via
-   clear_const_double_mem and const_tiny_rtx.  Remove these
-   entries from our const_double_chain.  */
-
-void
-restore_varasm_status (f)
-     struct function *f;
-{
-  rtx *p = &f->varasm->x_const_double_chain;
-
-  while (*p)
-    if (CONST_DOUBLE_MEM (*p) == cc0_rtx)
-      *p = CONST_DOUBLE_CHAIN (*p);
-    else
-      p = &CONST_DOUBLE_CHAIN (*p);
-}
-
-/* Mark PC for GC.  */
-
-static void
-mark_pool_constant (pc)
-     struct pool_constant *pc;
-{
-  while (pc)
-    {
-      ggc_mark (pc);
-      ggc_mark_rtx (pc->constant);
-      pc = pc->next;
-    }
-}
-
-/* Mark P for GC.  */
-
-void
-mark_varasm_status (p)
-     struct varasm_status *p;
-{
-  if (p == NULL)
-    return;
-
-  mark_pool_constant (p->x_first_pool);
-  ggc_mark_rtx (p->x_const_double_chain);
-}
-
-/* Clear out all parts of the state in F that can safely be discarded
-   after the function has been compiled, to let garbage collection
-   reclaim the memory.  */
-
-void
-free_varasm_status (f)
-     struct function *f;
-{
-  struct varasm_status *p;
-  int i;
-
-  p = f->varasm;
-
-  /* Clear out the hash tables.  */
-  for (i = 0; i < MAX_RTX_HASH_TABLE; ++i)
-    {
-      struct constant_descriptor* cd;
-
-      cd = p->x_const_rtx_hash_table[i];
-      while (cd) {
-       struct constant_descriptor* next = cd->next;
-       free (cd);
-       cd = next;
-      }
-    }
-
-  free (p->x_const_rtx_hash_table);
-  free (p->x_const_rtx_sym_hash_table);
-  free (p);
-  f->varasm = NULL;
 }
 \f
 
@@ -3634,8 +2959,7 @@ decode_rtx_const (mode, x, value)
       if (GET_MODE (x) != VOIDmode)
        {
          value->mode = GET_MODE (x);
-         memcpy ((char *) &value->un.du,
-                 (char *) &CONST_DOUBLE_LOW (x), sizeof value->un.du);
+         REAL_VALUE_FROM_CONST_DOUBLE (value->un.du, x);
        }
       else
        {
@@ -3644,6 +2968,34 @@ decode_rtx_const (mode, x, value)
        }
       break;
 
+    case CONST_VECTOR:
+      {
+       int units, i;
+       rtx elt;
+
+       units = CONST_VECTOR_NUNITS (x);
+       value->kind = RTX_VECTOR;
+       value->mode = mode;
+
+       for (i = 0; i < units; ++i)
+         {
+           elt = CONST_VECTOR_ELT (x, i);
+           if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
+             {
+               value->un.vec[i].veclo = (HOST_WIDE_INT) INTVAL (elt);
+               value->un.vec[i].vechi = 0;
+             }
+           else if (GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+             {
+               value->un.vec[i].veclo = (HOST_WIDE_INT) CONST_DOUBLE_LOW (elt);
+               value->un.vec[i].vechi = (HOST_WIDE_INT) CONST_DOUBLE_HIGH (elt);
+             }
+           else
+             abort ();
+         }
+      }
+      break;
+
     case CONST_INT:
       value->un.addr.offset = INTVAL (x);
       break;
@@ -3674,17 +3026,37 @@ decode_rtx_const (mode, x, value)
       break;
 
     default:
-      abort ();
+      value->kind = RTX_UNKNOWN;
+      break;
+    }
+
+  if (value->kind == RTX_INT && value->un.addr.base != 0
+      && GET_CODE (value->un.addr.base) == UNSPEC)
+    {
+      /* For a simple UNSPEC, the base is set to the
+        operand, the kind field is set to the index of
+        the unspec expression.
+        Together with the code below, in case that
+        the operand is a SYMBOL_REF or LABEL_REF,
+        the address of the string or the code_label
+        is taken as base.  */
+      if (XVECLEN (value->un.addr.base, 0) == 1)
+       {
+         value->kind = RTX_UNSPEC + XINT (value->un.addr.base, 1);
+         value->un.addr.base = XVECEXP (value->un.addr.base, 0, 0);
+       }
     }
 
-  if (value->kind == RTX_INT && value->un.addr.base != 0)
+  if (value->kind > RTX_VECTOR && value->un.addr.base != 0)
     switch (GET_CODE (value->un.addr.base))
       {
+#if 0
       case SYMBOL_REF:
        /* Use the string's address, not the SYMBOL_REF's address,
           for the sake of addresses of library routines.  */
        value->un.addr.base = (rtx) XSTR (value->un.addr.base, 0);
        break;
+#endif
 
       case LABEL_REF:
        /* For a LABEL_REF, compare labels.  */
@@ -3707,8 +3079,11 @@ simplify_subtraction (x)
   decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0);
   decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1);
 
-  if (val0.un.addr.base == val1.un.addr.base)
+  if (val0.kind > RTX_DOUBLE
+      && val0.kind == val1.kind
+      && val0.un.addr.base == val1.un.addr.base)
     return GEN_INT (val0.un.addr.offset - val1.un.addr.offset);
+
   return x;
 }
 
@@ -3742,43 +3117,50 @@ static int
 compare_constant_rtx (mode, x, desc)
      enum machine_mode mode;
      rtx x;
-     struct constant_descriptor *desc;
+     struct constant_descriptor_rtx *desc;
 {
-  int *p = (int *) desc->u.contents;
-  int *strp;
-  int len;
   struct rtx_const value;
 
   decode_rtx_const (mode, x, &value);
-  strp = (int *) &value;
-  len = sizeof value / sizeof (int);
 
   /* Compare constant contents.  */
-  while (--len >= 0)
-    if (*p++ != *strp++)
-      return 0;
-
-  return 1;
+  return memcmp (&value, &desc->value, sizeof (struct rtx_const)) == 0;
 }
 
 /* Construct a constant descriptor for the rtl-expression X.
    It is up to the caller to enter the descriptor in the hash table.  */
 
-static struct constant_descriptor *
+static struct constant_descriptor_rtx *
 record_constant_rtx (mode, x)
      enum machine_mode mode;
      rtx x;
 {
-  struct constant_descriptor *ptr;
+  struct constant_descriptor_rtx *ptr;
 
-  ptr = ((struct constant_descriptor *)
-        xcalloc (1, (offsetof (struct constant_descriptor, u)
-                     + sizeof (struct rtx_const))));
-  decode_rtx_const (mode, x, (struct rtx_const *) ptr->u.contents);
+  ptr = (struct constant_descriptor_rtx *) ggc_alloc (sizeof (*ptr));
+  decode_rtx_const (mode, x, &ptr->value);
 
   return ptr;
 }
 \f
+/* Given a constant rtx X, return a MEM for the location in memory at which
+   this constant has been placed.  Return 0 if it not has been placed yet.  */
+
+rtx
+mem_for_const_double (x)
+     rtx x;
+{
+  enum machine_mode mode = GET_MODE (x);
+  struct constant_descriptor_rtx *desc;
+
+  for (desc = const_rtx_hash_table[const_hash_rtx (mode, x)]; desc;
+       desc = desc->next)
+    if (compare_constant_rtx (mode, x, desc))
+      return desc->rtl;
+
+  return 0;
+}
+
 /* Given a constant rtx X, make (or find) a memory constant for its value
    and return a MEM rtx to refer to it in memory.  */
 
@@ -3788,118 +3170,80 @@ force_const_mem (mode, x)
      rtx x;
 {
   int hash;
-  struct constant_descriptor *desc;
+  struct constant_descriptor_rtx *desc;
   char label[256];
-  const char *found = 0;
   rtx def;
-
-  /* If we want this CONST_DOUBLE in the same mode as it is in memory
-     (this will always be true for floating CONST_DOUBLEs that have been
-     placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs),
-     use the previous copy.  Otherwise, make a new one.  Note that in
-     the unlikely event that this same CONST_DOUBLE is used in two different
-     modes in an alternating fashion, we will allocate a lot of different
-     memory locations, but this should be extremely rare.  */
-
-  if (GET_CODE (x) == CONST_DOUBLE
-      && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM
-      && GET_MODE (CONST_DOUBLE_MEM (x)) == mode)
-    return CONST_DOUBLE_MEM (x);
+  struct pool_constant *pool;
+  unsigned int align;
 
   /* Compute hash code of X.  Search the descriptors for that hash code
-     to see if any of them describes X.  If yes, the descriptor records
-     the label number already assigned.  */
-
+     to see if any of them describes X.  If yes, we have an rtx to use.  */
   hash = const_hash_rtx (mode, x);
-
   for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next)
     if (compare_constant_rtx (mode, x, desc))
-      {
-       found = desc->label;
-       break;
-      }
-
-  if (found == 0)
-    {
-      struct pool_constant *pool;
-      int align;
-
-      /* No constant equal to X is known to have been output.
-        Make a constant descriptor to enter X in the hash table.
-        Assign the label number and record it in the descriptor for
-        future calls to this function to find.  */
+      return desc->rtl;
 
-      desc = record_constant_rtx (mode, x);
-      desc->next = const_rtx_hash_table[hash];
-      const_rtx_hash_table[hash] = desc;
+  /* No constant equal to X is known to have been output.
+     Make a constant descriptor to enter X in the hash table
+     and make a MEM for it.  */
+  desc = record_constant_rtx (mode, x);
+  desc->next = const_rtx_hash_table[hash];
+  const_rtx_hash_table[hash] = desc;
 
-      /* Align the location counter as required by EXP's data type.  */
-      align = GET_MODE_ALIGNMENT (mode == VOIDmode ? word_mode : mode);
+  /* Align the location counter as required by EXP's data type.  */
+  align = GET_MODE_ALIGNMENT (mode == VOIDmode ? word_mode : mode);
 #ifdef CONSTANT_ALIGNMENT
-      align = CONSTANT_ALIGNMENT (make_tree (type_for_mode (mode, 0), x),
-                                 align);
+  align = CONSTANT_ALIGNMENT (make_tree ((*lang_hooks.types.type_for_mode)
+                                        (mode, 0), x), align);
 #endif
 
-      pool_offset += (align / BITS_PER_UNIT) - 1;
-      pool_offset &= ~ ((align / BITS_PER_UNIT) - 1);
-
-      if (GET_CODE (x) == LABEL_REF)
-       LABEL_PRESERVE_P (XEXP (x, 0)) = 1;
-
-      /* Allocate a pool constant descriptor, fill it in, and chain it in.  */
-
-      pool = (struct pool_constant *) ggc_alloc (sizeof (struct pool_constant));
-      pool->desc = desc;
-      pool->constant = x;
-      pool->mode = mode;
-      pool->labelno = const_labelno;
-      pool->align = align;
-      pool->offset = pool_offset;
-      pool->mark = 1;
-      pool->next = 0;
-
-      if (last_pool == 0)
-       first_pool = pool;
-      else
-       last_pool->next = pool;
-
-      last_pool = pool;
-      pool_offset += GET_MODE_SIZE (mode);
-
-      /* Create a string containing the label name, in LABEL.  */
-      ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
+  pool_offset += (align / BITS_PER_UNIT) - 1;
+  pool_offset &= ~ ((align / BITS_PER_UNIT) - 1);
+
+  if (GET_CODE (x) == LABEL_REF)
+    LABEL_PRESERVE_P (XEXP (x, 0)) = 1;
+
+  /* Allocate a pool constant descriptor, fill it in, and chain it in.  */
+  pool = (struct pool_constant *) ggc_alloc (sizeof (struct pool_constant));
+  pool->desc = desc;
+  pool->constant = x;
+  pool->mode = mode;
+  pool->labelno = const_labelno;
+  pool->align = align;
+  pool->offset = pool_offset;
+  pool->mark = 1;
+  pool->next = 0;
+
+  if (last_pool == 0)
+    first_pool = pool;
+  else
+    last_pool->next = pool;
 
-      ++const_labelno;
+  last_pool = pool;
+  pool_offset += GET_MODE_SIZE (mode);
 
-      desc->label = found = ggc_strdup (label);
+  /* Create a string containing the label name, in LABEL.  */
+  ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
 
-      /* Add label to symbol hash table.  */
-      hash = SYMHASH (found);
-      pool->label = found;
-      pool->next_sym = const_rtx_sym_hash_table[hash];
-      const_rtx_sym_hash_table[hash] = pool;
-    }
+  ++const_labelno;
 
-  /* We have a symbol name; construct the SYMBOL_REF and the MEM.  */
+  /* Construct the SYMBOL_REF and the MEM.  */
 
-  def = gen_rtx_MEM (mode, gen_rtx_SYMBOL_REF (Pmode, found));
-  set_mem_attributes (def, type_for_mode (mode, 0), 1);
+  pool->desc->rtl = def
+    = gen_rtx_MEM (mode, gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (label)));
+  set_mem_alias_set (def, const_alias_set);
+  set_mem_attributes (def, (*lang_hooks.types.type_for_mode) (mode, 0), 1);
   RTX_UNCHANGING_P (def) = 1;
 
+  /* Add label to symbol hash table.  */
+  hash = SYMHASH (XSTR (XEXP (def, 0), 0));
+  pool->next_sym = const_rtx_sym_hash_table[hash];
+  const_rtx_sym_hash_table[hash] = pool;
+
   /* Mark the symbol_ref as belonging to this constants pool.  */
   CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1;
   current_function_uses_const_pool = 1;
 
-  if (GET_CODE (x) == CONST_DOUBLE)
-    {
-      if (CONST_DOUBLE_MEM (x) == cc0_rtx)
-       {
-         CONST_DOUBLE_CHAIN (x) = const_double_chain;
-         const_double_chain = x;
-       }
-      CONST_DOUBLE_MEM (x) = def;
-    }
-
   return def;
 }
 \f
@@ -3916,7 +3260,7 @@ find_pool_constant (f, addr)
 
   for (pool = f->varasm->x_const_rtx_sym_hash_table[SYMHASH (label)]; pool;
        pool = pool->next_sym)
-    if (pool->label == label)
+    if (XSTR (XEXP (pool->desc->rtl, 0), 0) == label)
       return pool;
 
   abort ();
@@ -3931,10 +3275,23 @@ get_pool_constant (addr)
   return (find_pool_constant (cfun, addr))->constant;
 }
 
-/* Likewise, but for the constant pool of a specific function.  */
+/* Given a constant pool SYMBOL_REF, return the corresponding constant
+   and whether it has been output or not.  */
 
 rtx
-get_pool_constant_for_function (f, addr)
+get_pool_constant_mark (addr, pmarked)
+     rtx addr;
+     bool *pmarked;
+{
+  struct pool_constant *pool = find_pool_constant (cfun, addr);
+  *pmarked = (pool->mark != 0);
+  return pool->constant;
+}
+
+/* Likewise, but for the constant pool of a specific function.  */
+
+rtx
+get_pool_constant_for_function (f, addr)
      struct function *f;
      rtx addr;
 {
@@ -3979,12 +3336,12 @@ get_pool_size ()
 
 void
 output_constant_pool (fnname, fndecl)
-  const char *fnname ATTRIBUTE_UNUSED;
-  tree fndecl ATTRIBUTE_UNUSED;
+     const char *fnname ATTRIBUTE_UNUSED;
+     tree fndecl ATTRIBUTE_UNUSED;
 {
   struct pool_constant *pool;
   rtx x;
-  union real_extract u;
+  REAL_VALUE_TYPE r;
 
   /* It is possible for gcc to call force_const_mem and then to later
      discard the instructions which refer to the constant.  In such a
@@ -4039,11 +3396,7 @@ output_constant_pool (fnname, fndecl)
        }
 
       /* First switch to correct section.  */
-#ifdef SELECT_RTX_SECTION
-      SELECT_RTX_SECTION (pool->mode, x, pool->align);
-#else
-      readonly_data_section ();
-#endif
+      (*targetm.asm_out.select_rtx_section) (pool->mode, x, pool->align);
 
 #ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
       ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode,
@@ -4062,8 +3415,8 @@ output_constant_pool (fnname, fndecl)
          if (GET_CODE (x) != CONST_DOUBLE)
            abort ();
 
-         memcpy ((char *) &u, (char *) &CONST_DOUBLE_LOW (x), sizeof u);
-         assemble_real (u.d, pool->mode, pool->align);
+         REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+         assemble_real (r, pool->mode, pool->align);
          break;
 
        case MODE_INT:
@@ -4071,6 +3424,44 @@ output_constant_pool (fnname, fndecl)
          assemble_integer (x, GET_MODE_SIZE (pool->mode), pool->align, 1);
          break;
 
+       case MODE_VECTOR_FLOAT:
+         {
+           int i, units;
+           rtx elt;
+
+           if (GET_CODE (x) != CONST_VECTOR)
+             abort ();
+
+           units = CONST_VECTOR_NUNITS (x);
+
+           for (i = 0; i < units; i++)
+             {
+               elt = CONST_VECTOR_ELT (x, i);
+               REAL_VALUE_FROM_CONST_DOUBLE (r, elt);
+               assemble_real (r, GET_MODE_INNER (pool->mode), pool->align);
+             }
+         }
+         break;
+
+       case MODE_VECTOR_INT:
+         {
+           int i, units;
+           rtx elt;
+
+           if (GET_CODE (x) != CONST_VECTOR)
+             abort ();
+
+           units = CONST_VECTOR_NUNITS (x);
+
+           for (i = 0; i < units; i++)
+             {
+               elt = CONST_VECTOR_ELT (x, i);
+               assemble_integer (elt, GET_MODE_UNIT_SIZE (pool->mode),
+                                 pool->align, 1);
+             }
+         }
+         break;
+
        default:
          abort ();
        }
@@ -4096,6 +3487,7 @@ static void
 mark_constant_pool ()
 {
   rtx insn;
+  rtx link;
   struct pool_constant *pool;
 
   if (first_pool == 0 && htab_elements (const_str_htab) == 0)
@@ -4108,11 +3500,15 @@ mark_constant_pool ()
     if (INSN_P (insn))
       mark_constants (PATTERN (insn));
 
-  for (insn = current_function_epilogue_delay_list;
-       insn;
-       insn = XEXP (insn, 1))
-    if (INSN_P (insn))
-      mark_constants (PATTERN (insn));
+  for (link = current_function_epilogue_delay_list;
+       link;
+       link = XEXP (link, 1))
+    {
+      insn = XEXP (link, 0);
+
+      if (INSN_P (insn))
+       mark_constants (PATTERN (insn));
+    }
 }
 
 /* Look through appropriate parts of X, marking all entries in the
@@ -4135,10 +3531,6 @@ mark_constants (x)
       mark_constant (&x, NULL);
       return;
     }
-  /* Never search inside a CONST_DOUBLE, because CONST_DOUBLE_MEM may be
-     a MEM, but does not constitute a use of that MEM.  */
-  else if (GET_CODE (x) == CONST_DOUBLE)
-    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
@@ -4176,6 +3568,7 @@ mark_constants (x)
        case 'w':
        case 'n':
        case 'u':
+       case 'B':
          break;
 
        default:
@@ -4186,7 +3579,7 @@ mark_constants (x)
 
 /* 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.  */
+   be used with for_each_rtx to mark all SYMBOL_REFs in an rtx.  */
 
 static int
 mark_constant (current_rtx, data)
@@ -4197,19 +3590,17 @@ mark_constant (current_rtx, data)
 
   if (x == NULL_RTX)
     return 0;
-  else if (GET_CODE(x) == CONST_DOUBLE)
-    /* Never search inside a CONST_DOUBLE because CONST_DOUBLE_MEM may
-       be a MEM but does not constitute a use of that MEM.  */
-    return -1;
+
   else if (GET_CODE (x) == SYMBOL_REF)
     {
       if (CONSTANT_POOL_ADDRESS_P (x))
        {
          struct pool_constant *pool = find_pool_constant (cfun, x);
-         if (pool->mark == 0) {
-           pool->mark = 1;
-           for_each_rtx (&(pool->constant), &mark_constant, NULL);
-         }
+         if (pool->mark == 0)
+           {
+             pool->mark = 1;
+             for_each_rtx (&(pool->constant), &mark_constant, NULL);
+           }
          else
            return -1;
        }
@@ -4242,30 +3633,31 @@ output_addressed_constants (exp)
      tree exp;
 {
   int reloc = 0;
+  tree tem;
 
   /* Give the front-end a chance to convert VALUE to something that
      looks more like a constant to the back-end.  */
-  if (lang_expand_constant)
-    exp = (*lang_expand_constant) (exp);
+  exp = (*lang_hooks.expand_constant) (exp);
 
   switch (TREE_CODE (exp))
     {
     case ADDR_EXPR:
-      {
-       tree constant = TREE_OPERAND (exp, 0);
-
-       while (TREE_CODE (constant) == COMPONENT_REF)
-         {
-           constant = TREE_OPERAND (constant, 0);
-         }
-
-       if (TREE_CODE_CLASS (TREE_CODE (constant)) == 'c'
-           || TREE_CODE (constant) == CONSTRUCTOR)
-         /* No need to do anything here
-            for addresses of variables or functions.  */
-         output_constant_def (constant, 0);
-      }
-      reloc = 1;
+    case FDESC_EXPR:
+      /* Go inside any operations that get_inner_reference can handle and see
+        if what's inside is a constant: no need to do anything here for
+        addresses of variables or functions.  */
+      for (tem = TREE_OPERAND (exp, 0); handled_component_p (tem);
+          tem = TREE_OPERAND (tem, 0))
+       ;
+
+      if (TREE_CODE_CLASS (TREE_CODE (tem)) == 'c'
+         || TREE_CODE (tem) == CONSTRUCTOR)
+       output_constant_def (tem, 0);
+
+      if (TREE_PUBLIC (tem))
+       reloc |= 2;
+      else
+       reloc |= 1;
       break;
 
     case PLUS_EXPR:
@@ -4281,12 +3673,10 @@ output_addressed_constants (exp)
       break;
 
     case CONSTRUCTOR:
-      {
-       tree link;
-       for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-         if (TREE_VALUE (link) != 0)
-           reloc |= output_addressed_constants (TREE_VALUE (link));
-      }
+      for (tem = CONSTRUCTOR_ELTS (exp); tem; tem = TREE_CHAIN (tem))
+       if (TREE_VALUE (tem) != 0)
+         reloc |= output_addressed_constants (TREE_VALUE (tem));
+
       break;
 
     default:
@@ -4312,8 +3702,7 @@ initializer_constant_valid_p (value, endtype)
 {
   /* Give the front-end a chance to convert VALUE to something that
      looks more like a constant to the back-end.  */
-  if (lang_expand_constant)
-    value = (*lang_expand_constant) (value);
+  value = (*lang_hooks.expand_constant) (value);
 
   switch (TREE_CODE (value))
     {
@@ -4329,6 +3718,7 @@ initializer_constant_valid_p (value, endtype)
       return TREE_STATIC (value) ? null_pointer_node : 0;
 
     case INTEGER_CST:
+    case VECTOR_CST:
     case REAL_CST:
     case STRING_CST:
     case COMPLEX_CST:
@@ -4338,6 +3728,7 @@ initializer_constant_valid_p (value, endtype)
     case FDESC_EXPR:
       return staticp (TREE_OPERAND (value, 0)) ? TREE_OPERAND (value, 0) : 0;
 
+    case VIEW_CONVERT_EXPR:
     case NON_LVALUE_EXPR:
       return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
 
@@ -4402,7 +3793,7 @@ initializer_constant_valid_p (value, endtype)
     case PLUS_EXPR:
       if (! INTEGRAL_TYPE_P (endtype)
          || TYPE_PRECISION (endtype) >= POINTER_SIZE)
-        {
+       {
          tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
                                                      endtype);
          tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
@@ -4412,7 +3803,7 @@ initializer_constant_valid_p (value, endtype)
            return valid1;
          if (valid1 == null_pointer_node)
            return valid0;
-        }
+       }
       break;
 
     case MINUS_EXPR:
@@ -4446,8 +3837,37 @@ initializer_constant_valid_p (value, endtype)
          tree op0, op1;
          op0 = TREE_OPERAND (value, 0);
          op1 = TREE_OPERAND (value, 1);
-         STRIP_NOPS (op0);
-         STRIP_NOPS (op1);
+
+         /* Like STRIP_NOPS except allow the operand mode to widen.
+            This works around a feature of fold that simplfies
+            (int)(p1 - p2) to ((int)p1 - (int)p2) under the theory
+            that the narrower operation is cheaper.  */
+
+         while (TREE_CODE (op0) == NOP_EXPR
+                || TREE_CODE (op0) == CONVERT_EXPR
+                || TREE_CODE (op0) == NON_LVALUE_EXPR)
+           {
+             tree inner = TREE_OPERAND (op0, 0);
+             if (inner == error_mark_node
+                 || ! INTEGRAL_MODE_P (TYPE_MODE (TREE_TYPE (inner)))
+                 || (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (op0)))
+                     > GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (inner)))))
+               break;
+             op0 = inner;
+           }
+
+         while (TREE_CODE (op1) == NOP_EXPR
+                || TREE_CODE (op1) == CONVERT_EXPR
+                || TREE_CODE (op1) == NON_LVALUE_EXPR)
+           {
+             tree inner = TREE_OPERAND (op1, 0);
+             if (inner == error_mark_node
+                 || ! INTEGRAL_MODE_P (TYPE_MODE (TREE_TYPE (inner)))
+                 || (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (op1)))
+                     > GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (inner)))))
+               break;
+             op1 = inner;
+           }
 
          if (TREE_CODE (op0) == ADDR_EXPR
              && TREE_CODE (TREE_OPERAND (op0, 0)) == LABEL_DECL
@@ -4487,38 +3907,29 @@ initializer_constant_valid_p (value, endtype)
 void
 output_constant (exp, size, align)
      tree exp;
-     int size;
+     HOST_WIDE_INT size;
      unsigned int align;
 {
-  enum tree_code code = TREE_CODE (TREE_TYPE (exp));
+  enum tree_code code;
+  HOST_WIDE_INT thissize;
 
-  /* Some front-ends use constants other than the standard
-     language-indepdent varieties, but which may still be output
-     directly.  Give the front-end a chance to convert EXP to a
-     language-independent representation.  */
-  if (lang_expand_constant)
-    {
-      exp = (*lang_expand_constant) (exp);
-      code = TREE_CODE (TREE_TYPE (exp));
-    }
+  /* Some front-ends use constants other than the standard language-indepdent
+     varieties, but which may still be output directly.  Give the front-end a
+     chance to convert EXP to a language-independent representation.  */
+  exp = (*lang_hooks.expand_constant) (exp);
 
   if (size == 0 || flag_syntax_only)
     return;
 
-  /* Eliminate the NON_LVALUE_EXPR_EXPR that makes a cast not be an lvalue.
-     That way we get the constant (we hope) inside it.  Also, strip off any
-     NOP_EXPR that converts between two record, union, array, or set types
-     or a CONVERT_EXPR that converts to a union TYPE.  */
-  while ((TREE_CODE (exp) == NOP_EXPR
-         && (TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))
-             || AGGREGATE_TYPE_P (TREE_TYPE (exp))))
-        || (TREE_CODE (exp) == CONVERT_EXPR
-            && code == UNION_TYPE)
-        || TREE_CODE (exp) == NON_LVALUE_EXPR)
-    {
-      exp = TREE_OPERAND (exp, 0);
-      code = TREE_CODE (TREE_TYPE (exp));
-    }
+  /* 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);
+
+  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.  */
@@ -4540,6 +3951,8 @@ output_constant (exp, size, align)
       return;
     }
 
+  /* Now output the underlying data.  If we've handling the padding, return.
+     Otherwise, break and ensure THISSIZE is the size written.  */
   switch (code)
     {
     case CHAR_TYPE:
@@ -4548,16 +3961,10 @@ output_constant (exp, size, align)
     case ENUMERAL_TYPE:
     case POINTER_TYPE:
     case REFERENCE_TYPE:
-      /* ??? What about       (int)((float)(int)&foo + 4)    */
-      while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
-            || TREE_CODE (exp) == NON_LVALUE_EXPR)
-       exp = TREE_OPERAND (exp, 0);
-
       if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode,
                                           EXPAND_INITIALIZER),
                              size, align, 0))
        error ("initializer for integer value is too complicated");
-      size = 0;
       break;
 
     case REAL_TYPE:
@@ -4567,17 +3974,16 @@ output_constant (exp, size, align)
       assemble_real (TREE_REAL_CST (exp),
                     mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0),
                     align);
-      size = 0;
       break;
 
     case COMPLEX_TYPE:
-      output_constant (TREE_REALPART (exp), size / 2, align);
-      output_constant (TREE_IMAGPART (exp), size / 2,
-                      min_align (align, BITS_PER_UNIT * (size / 2)));
-      size -= (size / 2) * 2;
+      output_constant (TREE_REALPART (exp), thissize / 2, align);
+      output_constant (TREE_IMAGPART (exp), thissize / 2,
+                      min_align (align, BITS_PER_UNIT * (thissize / 2)));
       break;
 
     case ARRAY_TYPE:
+    case VECTOR_TYPE:
       if (TREE_CODE (exp) == CONSTRUCTOR)
        {
          output_constructor (exp, size, align);
@@ -4585,16 +3991,8 @@ output_constant (exp, size, align)
        }
       else if (TREE_CODE (exp) == STRING_CST)
        {
-         int excess = 0;
-
-         if (size > TREE_STRING_LENGTH (exp))
-           {
-             excess = size - TREE_STRING_LENGTH (exp);
-             size = TREE_STRING_LENGTH (exp);
-           }
-
-         assemble_string (TREE_STRING_POINTER (exp), size);
-         size = excess;
+         thissize = MIN (TREE_STRING_LENGTH (exp), size);
+         assemble_string (TREE_STRING_POINTER (exp), thissize);
        }
       else
        abort ();
@@ -4612,22 +4010,26 @@ output_constant (exp, size, align)
       if (TREE_CODE (exp) == INTEGER_CST)
        assemble_integer (expand_expr (exp, NULL_RTX,
                                       VOIDmode, EXPAND_INITIALIZER),
-                         size, align, 1);
+                         thissize, align, 1);
       else if (TREE_CODE (exp) == CONSTRUCTOR)
        {
-         unsigned char *buffer = (unsigned char *) alloca (size);
-         if (get_set_constructor_bytes (exp, buffer, size))
+         unsigned char *buffer = (unsigned char *) alloca (thissize);
+         if (get_set_constructor_bytes (exp, buffer, thissize))
            abort ();
-         assemble_string ((char *) buffer, size);
+         assemble_string ((char *) buffer, thissize);
        }
       else
        error ("unknown set constructor type");
       return;
 
+    case ERROR_MARK:
+      return;
+
     default:
-      break; /* ??? */
+      abort ();
     }
 
+  size -= thissize;
   if (size > 0)
     assemble_zeros (size);
 }
@@ -4643,15 +4045,14 @@ array_size_for_constructor (val)
 {
   tree max_index, 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
+     doing it here.  */
   if (TREE_CODE (val) == STRING_CST)
-    {
-      HOST_WIDE_INT len = TREE_STRING_LENGTH(val);
-      HOST_WIDE_INT esz = int_size_in_bytes (TREE_TYPE (TREE_TYPE (val)));
-      HOST_WIDE_INT tsz = len * esz;
-      return tsz;
-    }
+    return TREE_STRING_LENGTH (val);
+
   max_index = NULL_TREE;
-  for (i = CONSTRUCTOR_ELTS (val); i ; i = TREE_CHAIN (i))
+  for (i = CONSTRUCTOR_ELTS (val); i; i = TREE_CHAIN (i))
     {
       tree index = TREE_PURPOSE (i);
 
@@ -4682,7 +4083,7 @@ array_size_for_constructor (val)
 static void
 output_constructor (exp, size, align)
      tree exp;
-     int size;
+     HOST_WIDE_INT size;
      unsigned int align;
 {
   tree type = TREE_TYPE (exp);
@@ -4774,7 +4175,7 @@ output_constructor (exp, size, align)
          /* Output any buffered-up bit-fields preceding this element.  */
          if (byte_buffer_in_use)
            {
-             ASM_OUTPUT_BYTE (asm_out_file, byte);
+             assemble_integer (GEN_INT (byte), 1, BITS_PER_UNIT, 1);
              total_bytes++;
              byte_buffer_in_use = 0;
            }
@@ -4852,7 +4253,7 @@ output_constructor (exp, size, align)
              /* Output remnant of any bit field in previous bytes.  */
              if (byte_buffer_in_use)
                {
-                 ASM_OUTPUT_BYTE (asm_out_file, byte);
+                 assemble_integer (GEN_INT (byte), 1, BITS_PER_UNIT, 1);
                  total_bytes++;
                  byte_buffer_in_use = 0;
                }
@@ -4888,7 +4289,7 @@ output_constructor (exp, size, align)
                 within this element when necessary.  */
              while (next_byte != total_bytes)
                {
-                 ASM_OUTPUT_BYTE (asm_out_file, byte);
+                 assemble_integer (GEN_INT (byte), 1, BITS_PER_UNIT, 1);
                  total_bytes++;
                  byte = 0;
                }
@@ -4974,7 +4375,7 @@ output_constructor (exp, size, align)
 
   if (byte_buffer_in_use)
     {
-      ASM_OUTPUT_BYTE (asm_out_file, byte);
+      assemble_integer (GEN_INT (byte), 1, BITS_PER_UNIT, 1);
       total_bytes++;
     }
 
@@ -4982,39 +4383,79 @@ output_constructor (exp, size, align)
     assemble_zeros (size - total_bytes);
 }
 
-
-/* This structure contains any weak symbol declarations waiting
+/* This TREE_LIST contains any weak symbol declarations waiting
    to be emitted.  */
-struct weak_syms
-{
-  struct weak_syms * next;
-  const char * name;
-  const char * value;
-};
-
-static struct weak_syms * weak_decls;
+static GTY(()) tree weak_decls;
 
-/* Add function NAME to the weak symbols list.  VALUE is a weak alias
-   associated with NAME.  */
+/* Mark DECL as weak.  */
 
-int
-add_weak (name, value)
-     const char *name;
-     const char *value;
+static void
+mark_weak (decl)
+     tree decl;
 {
-  struct weak_syms *weak;
+  DECL_WEAK (decl) = 1;
 
-  weak = (struct weak_syms *) xmalloc (sizeof (struct weak_syms));
+  if (DECL_RTL_SET_P (decl)
+      && GET_CODE (DECL_RTL (decl)) == MEM
+      && XEXP (DECL_RTL (decl), 0)
+      && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF)
+    SYMBOL_REF_WEAK (XEXP (DECL_RTL (decl), 0)) = 1;
+}
 
-  if (weak == NULL)
-    return 0;
+/* Merge weak status between NEWDECL and OLDDECL.  */
+
+void
+merge_weak (newdecl, olddecl)
+     tree newdecl;
+     tree olddecl;
+{
+  if (DECL_WEAK (newdecl) == DECL_WEAK (olddecl))
+    return;
 
-  weak->next = weak_decls;
-  weak->name = name;
-  weak->value = value;
-  weak_decls = weak;
+  if (DECL_WEAK (newdecl))
+    {
+      tree wd;
+
+      /* NEWDECL is weak, but OLDDECL is not.  */
+
+      /* If we already output the OLDDECL, we're in trouble; we can't
+        go back and make it weak.  This error cannot caught in
+        declare_weak because the NEWDECL and OLDDECL was not yet
+        been merged; therefore, TREE_ASM_WRITTEN was not set.  */
+      if (TREE_ASM_WRITTEN (olddecl))
+       error_with_decl (newdecl,
+                        "weak declaration of `%s' must precede definition");
+
+      /* If we've already generated rtl referencing OLDDECL, we may
+        have done so in a way that will not function properly with
+        a weak symbol.  */
+      else if (TREE_USED (olddecl)
+              && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (olddecl)))
+       warning_with_decl (newdecl, "weak declaration of `%s' after first use results in unspecified behavior");
+
+      if (SUPPORTS_WEAK)
+       {
+         /* We put the NEWDECL on the weak_decls list at some point.
+            Replace it with the OLDDECL.  */
+         for (wd = weak_decls; wd; wd = TREE_CHAIN (wd))
+           if (TREE_VALUE (wd) == newdecl)
+             {
+               TREE_VALUE (wd) = olddecl;
+               break;
+             }
+         /* We may not find the entry on the list.  If NEWDECL is a
+            weak alias, then we will have already called
+            globalize_decl to remove the entry; in that case, we do
+            not need to do anything.  */
+       }
 
-  return 1;
+      /* Make the OLDDECL weak; it's OLDDECL that we'll be keeping.  */
+      mark_weak (olddecl);
+    }
+  else
+    /* OLDDECL was weak, but NEWDECL was not explicitly marked as
+       weak.  Just update NEWDECL to indicate that it's weak too.  */
+    mark_weak (newdecl);
 }
 
 /* Declare DECL to be a weak symbol.  */
@@ -5025,14 +4466,17 @@ declare_weak (decl)
 {
   if (! TREE_PUBLIC (decl))
     error_with_decl (decl, "weak declaration of `%s' must be public");
-  else if (TREE_ASM_WRITTEN (decl))
+  else if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
     error_with_decl (decl, "weak declaration of `%s' must precede definition");
   else if (SUPPORTS_WEAK)
-    add_weak (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), NULL);
+    {
+      if (! DECL_WEAK (decl))
+       weak_decls = tree_cons (NULL, decl, weak_decls);
+    }
   else
     warning_with_decl (decl, "weak declaration of `%s' not supported");
 
-  DECL_WEAK (decl) = 1;
+  mark_weak (decl);
 }
 
 /* Emit any pending weak declarations.  */
@@ -5040,48 +4484,65 @@ declare_weak (decl)
 void
 weak_finish ()
 {
-  if (SUPPORTS_WEAK)
+  tree t;
+
+  for (t = weak_decls; t; t = TREE_CHAIN (t))
     {
-      struct weak_syms *t;
-      for (t = weak_decls; t; t = t->next)
-       {
-#ifdef ASM_OUTPUT_WEAK_ALIAS
-         ASM_OUTPUT_WEAK_ALIAS (asm_out_file, t->name, t->value);
+      tree decl = TREE_VALUE (t);
+      const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+      if (! TREE_USED (decl))
+       continue;
+
+#ifdef ASM_WEAKEN_DECL
+      ASM_WEAKEN_DECL (asm_out_file, decl, name, NULL);
 #else
 #ifdef ASM_WEAKEN_LABEL
-         if (t->value)
-           abort ();
-         ASM_WEAKEN_LABEL (asm_out_file, t->name);
+      ASM_WEAKEN_LABEL (asm_out_file, name);
+#else
+#ifdef ASM_OUTPUT_WEAK_ALIAS
+      warning ("only weak aliases are supported in this configuration");
+      return;
+#endif
 #endif
 #endif
-       }
     }
 }
 
-/* Remove NAME from the pending list of weak symbols.  This prevents
-   the compiler from emitting multiple .weak directives which confuses
-   some assemblers.  */
-#ifdef ASM_WEAKEN_LABEL
+/* Emit the assembly bits to indicate that DECL is globally visible.  */
+
 static void
-remove_from_pending_weak_list (name)
-     const char *name;
+globalize_decl (decl)
+     tree decl;
 {
-  struct weak_syms *t;
-  struct weak_syms **p;
+  const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
 
-  for (p = &weak_decls; *p; )
+#if defined (ASM_WEAKEN_LABEL) || defined (ASM_WEAKEN_DECL)
+  if (DECL_WEAK (decl))
     {
-      t = *p;
-      if (strcmp (name, t->name) == 0)
-        {
-          *p = t->next;
-          free (t);
-        }
-      else
-        p = &(t->next);
+      tree *p, t;
+
+#ifdef ASM_WEAKEN_DECL
+      ASM_WEAKEN_DECL (asm_out_file, decl, name, 0);
+#else
+      ASM_WEAKEN_LABEL (asm_out_file, name);
+#endif
+
+      /* Remove this function from the pending weak list so that
+        we do not emit multiple .weak directives for it.  */
+      for (p = &weak_decls; (t = *p) ; )
+       {
+         if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t)))
+           *p = TREE_CHAIN (t);
+         else
+           p = &TREE_CHAIN (t);
+       }
+      return;
     }
+#endif
+
+  (*targetm.asm_out.globalize_label) (asm_out_file, name);
 }
-#endif /* ASM_WEAKEN_LABEL */
 
 /* Emit an assembler directive to make the symbol for DECL an alias to
    the symbol for TARGET.  */
@@ -5103,18 +4564,8 @@ assemble_alias (decl, target)
 
   if (TREE_PUBLIC (decl))
     {
-#ifdef ASM_WEAKEN_LABEL
-      if (DECL_WEAK (decl))
-       {
-         ASM_WEAKEN_LABEL (asm_out_file, name);
-         /* Remove this function from the pending weak list so that
-            we do not emit multiple .weak directives for it.  */
-         remove_from_pending_weak_list
-           (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
-       }
-      else
-#endif
-       ASM_GLOBALIZE_LABEL (asm_out_file, name);
+      globalize_decl (decl);
+      maybe_assemble_visibility (decl);
     }
 
 #ifdef ASM_OUTPUT_DEF_FROM_DECLS
@@ -5122,18 +4573,59 @@ assemble_alias (decl, target)
 #else
   ASM_OUTPUT_DEF (asm_out_file, name, IDENTIFIER_POINTER (target));
 #endif
-  TREE_ASM_WRITTEN (decl) = 1;
-#else
-#ifdef ASM_OUTPUT_WEAK_ALIAS
+#else /* !ASM_OUTPUT_DEF */
+#if defined (ASM_OUTPUT_WEAK_ALIAS) || defined (ASM_WEAKEN_DECL)
   if (! DECL_WEAK (decl))
     warning ("only weak aliases are supported in this configuration");
 
+#ifdef ASM_WEAKEN_DECL
+  ASM_WEAKEN_DECL (asm_out_file, decl, name, IDENTIFIER_POINTER (target));
+#else
   ASM_OUTPUT_WEAK_ALIAS (asm_out_file, name, IDENTIFIER_POINTER (target));
-  TREE_ASM_WRITTEN (decl) = 1;
+#endif
 #else
   warning ("alias definitions not supported in this configuration; ignored");
 #endif
 #endif
+
+  TREE_USED (decl) = 1;
+  TREE_ASM_WRITTEN (decl) = 1;
+  TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
+}
+
+/* Emit an assembler directive to set symbol for DECL visibility to
+   VISIBILITY_TYPE.  */
+
+void
+default_assemble_visibility (decl, visibility_type)
+     tree decl;
+     const char *visibility_type ATTRIBUTE_UNUSED;
+{
+  const char *name;
+
+  name = (* targetm.strip_name_encoding)
+        (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+
+#ifdef HAVE_GAS_HIDDEN
+  fprintf (asm_out_file, "\t.%s\t%s\n", visibility_type, name);
+#else
+  warning ("visibility attribute not supported in this configuration; ignored");
+#endif
+}
+
+/* A helper function to call assemble_visibility when needed for a decl.  */
+
+static void
+maybe_assemble_visibility (decl)
+     tree decl;
+{
+  tree visibility = lookup_attribute ("visibility", DECL_ATTRIBUTES (decl));
+  if (visibility)
+    {
+      const char *type
+       = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (visibility)));
+      (* targetm.asm_out.visibility) (decl, type);
+    }
 }
 
 /* Returns 1 if the target configuration supports defining public symbols
@@ -5180,15 +4672,12 @@ make_decl_one_only (decl)
 void
 init_varasm_once ()
 {
-  const_str_htab = htab_create (128, const_str_htab_hash, const_str_htab_eq,
-                               const_str_htab_del);
+  const_str_htab = htab_create_ggc (128, const_str_htab_hash,
+                                   const_str_htab_eq, NULL);
   in_named_htab = htab_create (31, in_named_entry_hash,
                               in_named_entry_eq, NULL);
 
-  ggc_add_root (const_hash_table, MAX_HASH_TABLE, sizeof const_hash_table[0],
-               mark_const_hash_entry);
-  ggc_add_root (&const_str_htab, 1, sizeof const_str_htab,
-               mark_const_str_htab);
+  const_alias_set = new_alias_set ();
 }
 
 /* Select a set of attributes for section NAME based on the properties
@@ -5204,11 +4693,21 @@ default_section_type_flags (decl, name, reloc)
      const char *name;
      int reloc;
 {
+  return default_section_type_flags_1 (decl, name, reloc, flag_pic);
+}
+
+unsigned int
+default_section_type_flags_1 (decl, name, reloc, shlib)
+     tree decl;
+     const char *name;
+     int reloc;
+     int shlib;
+{
   unsigned int flags;
 
   if (decl && TREE_CODE (decl) == FUNCTION_DECL)
     flags = SECTION_CODE;
-  else if (decl && DECL_READONLY_SECTION (decl, reloc))
+  else if (decl && decl_readonly_section_1 (decl, reloc, shlib))
     flags = 0;
   else
     flags = SECTION_WRITE;
@@ -5216,14 +4715,25 @@ default_section_type_flags (decl, name, reloc)
   if (decl && DECL_ONE_ONLY (decl))
     flags |= SECTION_LINKONCE;
 
+  if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+    flags |= SECTION_TLS | SECTION_WRITE;
+
   if (strcmp (name, ".bss") == 0
       || strncmp (name, ".bss.", 5) == 0
       || strncmp (name, ".gnu.linkonce.b.", 16) == 0
       || strcmp (name, ".sbss") == 0
       || strncmp (name, ".sbss.", 6) == 0
-      || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
+      || strncmp (name, ".gnu.linkonce.sb.", 17) == 0
+      || strcmp (name, ".tbss") == 0
+      || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
     flags |= SECTION_BSS;
 
+  if (strcmp (name, ".tdata") == 0
+      || strcmp (name, ".tbss") == 0
+      || strncmp (name, ".gnu.linkonce.td.", 17) == 0
+      || strncmp (name, ".gnu.linkonce.tb.", 17) == 0)
+    flags |= SECTION_TLS;
+
   return flags;
 }
 
@@ -5266,6 +4776,8 @@ default_elf_asm_named_section (name, flags)
     *f++ = 'M';
   if (flags & SECTION_STRINGS)
     *f++ = 'S';
+  if (flags & SECTION_TLS)
+    *f++ = 'T';
   *f = '\0';
 
   if (flags & SECTION_BSS)
@@ -5329,7 +4841,7 @@ assemble_vtable_entry (symbol, offset)
   fputc ('\n', asm_out_file);
 }
 
-/* Used for vtable gc in GNU binutils.  Record the class heirarchy by noting
+/* Used for vtable gc in GNU binutils.  Record the class hierarchy by noting
    that the vtable symbol CHILD is derived from the vtable symbol PARENT.  */
 
 void
@@ -5342,3 +4854,465 @@ assemble_vtable_inherit (child, parent)
   output_addr_const (asm_out_file, parent);
   fputc ('\n', asm_out_file);
 }
+\f
+/* The lame default section selector.  */
+
+void
+default_select_section (decl, reloc, align)
+     tree decl;
+     int reloc;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+{
+  bool readonly = false;
+
+  if (DECL_P (decl))
+    {
+      if (decl_readonly_section (decl, reloc))
+       readonly = true;
+    }
+  else if (TREE_CODE (decl) == CONSTRUCTOR)
+    {
+      if (! ((flag_pic && reloc)
+            || !TREE_READONLY (decl)
+            || TREE_SIDE_EFFECTS (decl)
+            || !TREE_CONSTANT (decl)))
+       readonly = true;
+    }
+  else if (TREE_CODE (decl) == STRING_CST)
+    readonly = !flag_writable_strings;
+  else if (! (flag_pic && reloc))
+    readonly = true;
+
+  if (readonly)
+    readonly_data_section ();
+  else
+    data_section ();
+}
+
+/* A helper function for default_elf_select_section and
+   default_elf_unique_section.  Categorizes the DECL.  */
+
+enum section_category
+{
+  SECCAT_TEXT,
+
+  SECCAT_RODATA,
+  SECCAT_RODATA_MERGE_STR,
+  SECCAT_RODATA_MERGE_STR_INIT,
+  SECCAT_RODATA_MERGE_CONST,
+  SECCAT_SRODATA,
+
+  SECCAT_DATA,
+
+  /* To optimize loading of shared programs, define following subsections
+     of data section:
+       _REL    Contains data that has relocations, so they get grouped
+               together and dynamic linker will visit fewer pages in memory.
+       _RO     Contains data that is otherwise read-only.  This is useful
+               with prelinking as most relocations won't be dynamically
+               linked and thus stay read only.
+       _LOCAL  Marks data containing relocations only to local objects.
+               These relocations will get fully resolved by prelinking.  */
+  SECCAT_DATA_REL,
+  SECCAT_DATA_REL_LOCAL,
+  SECCAT_DATA_REL_RO,
+  SECCAT_DATA_REL_RO_LOCAL,
+
+  SECCAT_SDATA,
+  SECCAT_TDATA,
+
+  SECCAT_BSS,
+  SECCAT_SBSS,
+  SECCAT_TBSS
+};
+
+static enum section_category
+categorize_decl_for_section PARAMS ((tree, int, int));
+
+static enum section_category
+categorize_decl_for_section (decl, reloc, shlib)
+     tree decl;
+     int reloc;
+     int shlib;
+{
+  enum section_category ret;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    return SECCAT_TEXT;
+  else if (TREE_CODE (decl) == STRING_CST)
+    {
+      if (flag_writable_strings)
+       return SECCAT_DATA;
+      else
+       return SECCAT_RODATA_MERGE_STR;
+    }
+  else if (TREE_CODE (decl) == VAR_DECL)
+    {
+      if (DECL_INITIAL (decl) == NULL
+         || DECL_INITIAL (decl) == error_mark_node)
+       ret = SECCAT_BSS;
+      else if (! TREE_READONLY (decl)
+              || TREE_SIDE_EFFECTS (decl)
+              || ! TREE_CONSTANT (DECL_INITIAL (decl)))
+       {
+         if (shlib && (reloc & 2))
+           ret = SECCAT_DATA_REL;
+         else if (shlib && reloc)
+           ret = SECCAT_DATA_REL_LOCAL;
+         else
+           ret = SECCAT_DATA;
+       }
+      else if (shlib && (reloc & 2))
+       ret = SECCAT_DATA_REL_RO;
+      else if (shlib && reloc)
+       ret = SECCAT_DATA_REL_RO_LOCAL;
+      else if (flag_merge_constants < 2)
+       /* C and C++ don't allow different variables to share the same
+          location.  -fmerge-all-constants allows even that (at the
+          expense of not conforming).  */
+       ret = SECCAT_RODATA;
+      else if (TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
+       ret = SECCAT_RODATA_MERGE_STR_INIT;
+      else
+       ret = SECCAT_RODATA_MERGE_CONST;
+    }
+  else if (TREE_CODE (decl) == CONSTRUCTOR)
+    {
+      if ((shlib && reloc)
+         || TREE_SIDE_EFFECTS (decl)
+         || ! TREE_CONSTANT (decl))
+       ret = SECCAT_DATA;
+      else
+       ret = SECCAT_RODATA;
+    }
+  else
+    ret = SECCAT_RODATA;
+
+  /* There are no read-only thread-local sections.  */
+  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+    {
+      if (ret == SECCAT_BSS)
+       ret = SECCAT_TBSS;
+      else
+       ret = SECCAT_TDATA;
+    }
+
+  /* If the target uses small data sections, select it.  */
+  else if ((*targetm.in_small_data_p) (decl))
+    {
+      if (ret == SECCAT_BSS)
+       ret = SECCAT_SBSS;
+      else if (targetm.have_srodata_section && ret == SECCAT_RODATA)
+       ret = SECCAT_SRODATA;
+      else
+       ret = SECCAT_SDATA;
+    }
+
+  return ret;
+}
+
+bool
+decl_readonly_section (decl, reloc)
+     tree decl;
+     int reloc;
+{
+  return decl_readonly_section_1 (decl, reloc, flag_pic);
+}
+
+bool
+decl_readonly_section_1 (decl, reloc, shlib)
+     tree decl;
+     int reloc;
+     int shlib;
+{
+  switch (categorize_decl_for_section (decl, reloc, shlib))
+    {
+    case SECCAT_RODATA:
+    case SECCAT_RODATA_MERGE_STR:
+    case SECCAT_RODATA_MERGE_STR_INIT:
+    case SECCAT_RODATA_MERGE_CONST:
+    case SECCAT_SRODATA:
+      return true;
+      break;
+    default:
+      return false;
+      break;
+    }
+}
+
+/* Select a section based on the above categorization.  */
+
+void
+default_elf_select_section (decl, reloc, align)
+     tree decl;
+     int reloc;
+     unsigned HOST_WIDE_INT align;
+{
+  default_elf_select_section_1 (decl, reloc, align, flag_pic);
+}
+
+void
+default_elf_select_section_1 (decl, reloc, align, shlib)
+     tree decl;
+     int reloc;
+     unsigned HOST_WIDE_INT align;
+     int shlib;
+{
+  switch (categorize_decl_for_section (decl, reloc, shlib))
+    {
+    case SECCAT_TEXT:
+      /* We're not supposed to be called on FUNCTION_DECLs.  */
+      abort ();
+    case SECCAT_RODATA:
+      readonly_data_section ();
+      break;
+    case SECCAT_RODATA_MERGE_STR:
+      mergeable_string_section (decl, align, 0);
+      break;
+    case SECCAT_RODATA_MERGE_STR_INIT:
+      mergeable_string_section (DECL_INITIAL (decl), align, 0);
+      break;
+    case SECCAT_RODATA_MERGE_CONST:
+      mergeable_constant_section (DECL_MODE (decl), align, 0);
+      break;
+    case SECCAT_SRODATA:
+      named_section (NULL_TREE, ".sdata2", reloc);
+      break;
+    case SECCAT_DATA:
+      data_section ();
+      break;
+    case SECCAT_DATA_REL:
+      named_section (NULL_TREE, ".data.rel", reloc);
+      break;
+    case SECCAT_DATA_REL_LOCAL:
+      named_section (NULL_TREE, ".data.rel.local", reloc);
+      break;
+    case SECCAT_DATA_REL_RO:
+      named_section (NULL_TREE, ".data.rel.ro", reloc);
+      break;
+    case SECCAT_DATA_REL_RO_LOCAL:
+      named_section (NULL_TREE, ".data.rel.ro.local", reloc);
+      break;
+    case SECCAT_SDATA:
+      named_section (NULL_TREE, ".sdata", reloc);
+      break;
+    case SECCAT_TDATA:
+      named_section (NULL_TREE, ".tdata", reloc);
+      break;
+    case SECCAT_BSS:
+#ifdef BSS_SECTION_ASM_OP
+      bss_section ();
+#else
+      named_section (NULL_TREE, ".bss", reloc);
+#endif
+      break;
+    case SECCAT_SBSS:
+      named_section (NULL_TREE, ".sbss", reloc);
+      break;
+    case SECCAT_TBSS:
+      named_section (NULL_TREE, ".tbss", reloc);
+      break;
+    default:
+      abort ();
+    }
+}
+
+/* Construct a unique section name based on the decl name and the
+   categorization performed above.  */
+
+void
+default_unique_section (decl, reloc)
+     tree decl;
+     int reloc;
+{
+  default_unique_section_1 (decl, reloc, flag_pic);
+}
+
+void
+default_unique_section_1 (decl, reloc, shlib)
+     tree decl;
+     int reloc;
+     int shlib;
+{
+  bool one_only = DECL_ONE_ONLY (decl);
+  const char *prefix, *name;
+  size_t nlen, plen;
+  char *string;
+
+  switch (categorize_decl_for_section (decl, reloc, shlib))
+    {
+    case SECCAT_TEXT:
+      prefix = one_only ? ".gnu.linkonce.t." : ".text.";
+      break;
+    case SECCAT_RODATA:
+    case SECCAT_RODATA_MERGE_STR:
+    case SECCAT_RODATA_MERGE_STR_INIT:
+    case SECCAT_RODATA_MERGE_CONST:
+      prefix = one_only ? ".gnu.linkonce.r." : ".rodata.";
+      break;
+    case SECCAT_SRODATA:
+      prefix = one_only ? ".gnu.linkonce.s2." : ".sdata2.";
+      break;
+    case SECCAT_DATA:
+    case SECCAT_DATA_REL:
+    case SECCAT_DATA_REL_LOCAL:
+    case SECCAT_DATA_REL_RO:
+    case SECCAT_DATA_REL_RO_LOCAL:
+      prefix = one_only ? ".gnu.linkonce.d." : ".data.";
+      break;
+    case SECCAT_SDATA:
+      prefix = one_only ? ".gnu.linkonce.s." : ".sdata.";
+      break;
+    case SECCAT_BSS:
+      prefix = one_only ? ".gnu.linkonce.b." : ".bss.";
+      break;
+    case SECCAT_SBSS:
+      prefix = one_only ? ".gnu.linkonce.sb." : ".sbss.";
+      break;
+    case SECCAT_TDATA:
+      prefix = one_only ? ".gnu.linkonce.td." : ".tdata.";
+      break;
+    case SECCAT_TBSS:
+      prefix = one_only ? ".gnu.linkonce.tb." : ".tbss.";
+      break;
+    default:
+      abort ();
+    }
+  plen = strlen (prefix);
+
+  name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+  name = (* targetm.strip_name_encoding) (name);
+  nlen = strlen (name);
+
+  string = alloca (nlen + plen + 1);
+  memcpy (string, prefix, plen);
+  memcpy (string + plen, name, nlen + 1);
+
+  DECL_SECTION_NAME (decl) = build_string (nlen + plen, string);
+}
+
+void
+default_select_rtx_section (mode, x, align)
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     rtx x;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+{
+  if (flag_pic)
+    switch (GET_CODE (x))
+      {
+      case CONST:
+      case SYMBOL_REF:
+      case LABEL_REF:
+       data_section ();
+       return;
+
+      default:
+       break;
+      }
+
+  readonly_data_section ();
+}
+
+void
+default_elf_select_rtx_section (mode, x, align)
+     enum machine_mode mode;
+     rtx x;
+     unsigned HOST_WIDE_INT align;
+{
+  /* ??? Handle small data here somehow.  */
+
+  if (flag_pic)
+    switch (GET_CODE (x))
+      {
+      case CONST:
+      case SYMBOL_REF:
+       named_section (NULL_TREE, ".data.rel.ro", 3);
+       return;
+
+      case LABEL_REF:
+       named_section (NULL_TREE, ".data.rel.ro.local", 1);
+       return;
+
+      default:
+       break;
+      }
+
+  mergeable_constant_section (mode, align, 0);
+}
+
+/* By default, we do nothing for encode_section_info, so we need not
+   do anything but discard the '*' marker.  */
+
+const char *
+default_strip_name_encoding (str)
+     const char *str;
+{
+  return str + (*str == '*');
+}
+
+/* Assume ELF-ish defaults, since that's pretty much the most liberal
+   wrt cross-module name binding.  */
+
+bool
+default_binds_local_p (exp)
+     tree exp;
+{
+  return default_binds_local_p_1 (exp, flag_pic);
+}
+
+bool
+default_binds_local_p_1 (exp, shlib)
+     tree exp;
+     int shlib;
+{
+  bool local_p;
+
+  /* A non-decl is an entry in the constant pool.  */
+  if (!DECL_P (exp))
+    local_p = true;
+  /* Static variables are always local.  */
+  else if (! TREE_PUBLIC (exp))
+    local_p = true;
+  /* A variable is local if the user tells us so.  */
+  else if (MODULE_LOCAL_P (exp))
+    local_p = true;
+  /* Otherwise, variables defined outside this object may not be local.  */
+  else if (DECL_EXTERNAL (exp))
+    local_p = false;
+  /* Linkonce and weak data are never local.  */
+  else if (DECL_ONE_ONLY (exp) || DECL_WEAK (exp))
+    local_p = false;
+  /* If PIC, then assume that any global name can be overridden by
+     symbols resolved from other modules.  */
+  else if (shlib)
+    local_p = false;
+  /* Uninitialized COMMON variable may be unified with symbols
+     resolved from other modules.  */
+  else if (DECL_COMMON (exp)
+          && (DECL_INITIAL (exp) == NULL
+              || DECL_INITIAL (exp) == error_mark_node))
+    local_p = false;
+  /* Otherwise we're left with initialized (or non-common) global data
+     which is of necessity defined locally.  */
+  else
+    local_p = true;
+
+  return local_p;
+}
+
+/* Default function to output code that will globalize a label.  A
+   target must define GLOBAL_ASM_OP or provide it's own function to
+   globalize a label.  */
+#ifdef GLOBAL_ASM_OP
+void
+default_globalize_label (stream, name)
+     FILE * stream;
+     const char *name;
+{
+  fputs (GLOBAL_ASM_OP, stream);
+  assemble_name (stream, name);
+  putc ('\n', stream);
+}
+#endif /* GLOBAL_ASM_OP */
+  
+#include "gt-varasm.h"