diff gcc/dwarf2out.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 58ad6c70ea60
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/dwarf2out.c	Fri Jul 17 14:47:48 2009 +0900
@@ -0,0 +1,16681 @@
+/* Output Dwarf2 format symbol table information from GCC.
+   Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Contributed by Gary Funck (gary@intrepid.com).
+   Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com).
+   Extensively modified by Jason Merrill (jason@cygnus.com).
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* TODO: Emit .debug_line header even when there are no functions, since
+	   the file numbers are used by .debug_info.  Alternately, leave
+	   out locations for types and decls.
+	 Avoid talking about ctors and op= for PODs.
+	 Factor out common prologue sequences into multiple CIEs.  */
+
+/* The first part of this file deals with the DWARF 2 frame unwind
+   information, which is also used by the GCC efficient exception handling
+   mechanism.  The second part, controlled only by an #ifdef
+   DWARF2_DEBUGGING_INFO, deals with the other DWARF 2 debugging
+   information.  */
+
+/* DWARF2 Abbreviation Glossary:
+
+   CFA = Canonical Frame Address
+	   a fixed address on the stack which identifies a call frame.
+	   We define it to be the value of SP just before the call insn.
+	   The CFA register and offset, which may change during the course
+	   of the function, are used to calculate its value at runtime.
+
+   CFI = Call Frame Instruction
+	   an instruction for the DWARF2 abstract machine
+
+   CIE = Common Information Entry
+	   information describing information common to one or more FDEs
+
+   DIE = Debugging Information Entry
+
+   FDE = Frame Description Entry
+	   information describing the stack call frame, in particular,
+	   how to restore registers
+
+   DW_CFA_... = DWARF2 CFA call frame instruction
+   DW_TAG_... = DWARF2 DIE tag */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "version.h"
+#include "flags.h"
+#include "real.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "reload.h"
+#include "function.h"
+#include "output.h"
+#include "expr.h"
+#include "libfuncs.h"
+#include "except.h"
+#include "dwarf2.h"
+#include "dwarf2out.h"
+#include "dwarf2asm.h"
+#include "toplev.h"
+#include "varray.h"
+#include "ggc.h"
+#include "md5.h"
+#include "tm_p.h"
+#include "diagnostic.h"
+#include "debug.h"
+#include "target.h"
+#include "langhooks.h"
+#include "hashtab.h"
+#include "cgraph.h"
+#include "input.h"
+
+#ifdef DWARF2_DEBUGGING_INFO
+static void dwarf2out_source_line (unsigned int, const char *);
+#endif
+
+#ifndef DWARF2_FRAME_INFO
+# ifdef DWARF2_DEBUGGING_INFO
+#  define DWARF2_FRAME_INFO \
+  (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG)
+# else
+#  define DWARF2_FRAME_INFO 0
+# endif
+#endif
+
+/* Map register numbers held in the call frame info that gcc has
+   collected using DWARF_FRAME_REGNUM to those that should be output in
+   .debug_frame and .eh_frame.  */
+#ifndef DWARF2_FRAME_REG_OUT
+#define DWARF2_FRAME_REG_OUT(REGNO, FOR_EH) (REGNO)
+#endif
+
+/* Save the result of dwarf2out_do_frame across PCH.  */
+static GTY(()) bool saved_do_cfi_asm = 0;
+
+/* Decide whether we want to emit frame unwind information for the current
+   translation unit.  */
+
+int
+dwarf2out_do_frame (void)
+{
+  /* We want to emit correct CFA location expressions or lists, so we
+     have to return true if we're going to output debug info, even if
+     we're not going to output frame or unwind info.  */
+  return (write_symbols == DWARF2_DEBUG
+	  || write_symbols == VMS_AND_DWARF2_DEBUG
+	  || DWARF2_FRAME_INFO || saved_do_cfi_asm
+#ifdef DWARF2_UNWIND_INFO
+	  || (DWARF2_UNWIND_INFO
+	      && (flag_unwind_tables
+		  || (flag_exceptions && ! USING_SJLJ_EXCEPTIONS)))
+#endif
+	  );
+}
+
+/* Decide whether to emit frame unwind via assembler directives.  */
+
+int
+dwarf2out_do_cfi_asm (void)
+{
+  int enc;
+
+#ifdef MIPS_DEBUGGING_INFO
+  return false;
+#endif
+  if (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ())
+    return false;
+  if (saved_do_cfi_asm || !eh_personality_libfunc)
+    return true;
+  if (!HAVE_GAS_CFI_PERSONALITY_DIRECTIVE)
+    return false;
+
+  /* Make sure the personality encoding is one the assembler can support.
+     In particular, aligned addresses can't be handled.  */
+  enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,/*global=*/1);
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+    return false;
+  enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,/*global=*/0);
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+    return false;
+
+  saved_do_cfi_asm = true;
+  return true;
+}
+
+/* The size of the target's pointer type.  */
+#ifndef PTR_SIZE
+#define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT)
+#endif
+
+/* Array of RTXes referenced by the debugging information, which therefore
+   must be kept around forever.  */
+static GTY(()) VEC(rtx,gc) *used_rtx_array;
+
+/* A pointer to the base of a list of incomplete types which might be
+   completed at some later time.  incomplete_types_list needs to be a
+   VEC(tree,gc) because we want to tell the garbage collector about
+   it.  */
+static GTY(()) VEC(tree,gc) *incomplete_types;
+
+/* A pointer to the base of a table of references to declaration
+   scopes.  This table is a display which tracks the nesting
+   of declaration scopes at the current scope and containing
+   scopes.  This table is used to find the proper place to
+   define type declaration DIE's.  */
+static GTY(()) VEC(tree,gc) *decl_scope_table;
+
+/* Pointers to various DWARF2 sections.  */
+static GTY(()) section *debug_info_section;
+static GTY(()) section *debug_abbrev_section;
+static GTY(()) section *debug_aranges_section;
+static GTY(()) section *debug_macinfo_section;
+static GTY(()) section *debug_line_section;
+static GTY(()) section *debug_loc_section;
+static GTY(()) section *debug_pubnames_section;
+static GTY(()) section *debug_pubtypes_section;
+static GTY(()) section *debug_str_section;
+static GTY(()) section *debug_ranges_section;
+static GTY(()) section *debug_frame_section;
+
+/* How to start an assembler comment.  */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START ";#"
+#endif
+
+typedef struct dw_cfi_struct *dw_cfi_ref;
+typedef struct dw_fde_struct *dw_fde_ref;
+typedef union  dw_cfi_oprnd_struct *dw_cfi_oprnd_ref;
+
+/* Call frames are described using a sequence of Call Frame
+   Information instructions.  The register number, offset
+   and address fields are provided as possible operands;
+   their use is selected by the opcode field.  */
+
+enum dw_cfi_oprnd_type {
+  dw_cfi_oprnd_unused,
+  dw_cfi_oprnd_reg_num,
+  dw_cfi_oprnd_offset,
+  dw_cfi_oprnd_addr,
+  dw_cfi_oprnd_loc
+};
+
+typedef union dw_cfi_oprnd_struct GTY(())
+{
+  unsigned int GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num;
+  HOST_WIDE_INT GTY ((tag ("dw_cfi_oprnd_offset"))) dw_cfi_offset;
+  const char * GTY ((tag ("dw_cfi_oprnd_addr"))) dw_cfi_addr;
+  struct dw_loc_descr_struct * GTY ((tag ("dw_cfi_oprnd_loc"))) dw_cfi_loc;
+}
+dw_cfi_oprnd;
+
+typedef struct dw_cfi_struct GTY(())
+{
+  dw_cfi_ref dw_cfi_next;
+  enum dwarf_call_frame_info dw_cfi_opc;
+  dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd1_desc (%1.dw_cfi_opc)")))
+    dw_cfi_oprnd1;
+  dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd2_desc (%1.dw_cfi_opc)")))
+    dw_cfi_oprnd2;
+}
+dw_cfi_node;
+
+/* This is how we define the location of the CFA. We use to handle it
+   as REG + OFFSET all the time,  but now it can be more complex.
+   It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
+   Instead of passing around REG and OFFSET, we pass a copy
+   of this structure.  */
+typedef struct cfa_loc GTY(())
+{
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT base_offset;
+  unsigned int reg;
+  int indirect;            /* 1 if CFA is accessed via a dereference.  */
+} dw_cfa_location;
+
+/* All call frame descriptions (FDE's) in the GCC generated DWARF
+   refer to a single Common Information Entry (CIE), defined at
+   the beginning of the .debug_frame section.  This use of a single
+   CIE obviates the need to keep track of multiple CIE's
+   in the DWARF generation routines below.  */
+
+typedef struct dw_fde_struct GTY(())
+{
+  tree decl;
+  const char *dw_fde_begin;
+  const char *dw_fde_current_label;
+  const char *dw_fde_end;
+  const char *dw_fde_hot_section_label;
+  const char *dw_fde_hot_section_end_label;
+  const char *dw_fde_unlikely_section_label;
+  const char *dw_fde_unlikely_section_end_label;
+  bool dw_fde_switched_sections;
+  dw_cfi_ref dw_fde_cfi;
+  unsigned funcdef_number;
+  HOST_WIDE_INT stack_realignment;
+  /* Dynamic realign argument pointer register.  */
+  unsigned int drap_reg;
+  /* Virtual dynamic realign argument pointer register.  */
+  unsigned int vdrap_reg;
+  unsigned all_throwers_are_sibcalls : 1;
+  unsigned nothrow : 1;
+  unsigned uses_eh_lsda : 1;
+  /* Whether we did stack realign in this call frame.  */
+  unsigned stack_realign : 1;
+  /* Whether dynamic realign argument pointer register has been saved.  */
+  unsigned drap_reg_saved: 1;
+}
+dw_fde_node;
+
+/* Maximum size (in bytes) of an artificially generated label.  */
+#define MAX_ARTIFICIAL_LABEL_BYTES	30
+
+/* The size of addresses as they appear in the Dwarf 2 data.
+   Some architectures use word addresses to refer to code locations,
+   but Dwarf 2 info always uses byte addresses.  On such machines,
+   Dwarf 2 addresses need to be larger than the architecture's
+   pointers.  */
+#ifndef DWARF2_ADDR_SIZE
+#define DWARF2_ADDR_SIZE (POINTER_SIZE / BITS_PER_UNIT)
+#endif
+
+/* The size in bytes of a DWARF field indicating an offset or length
+   relative to a debug info section, specified to be 4 bytes in the
+   DWARF-2 specification.  The SGI/MIPS ABI defines it to be the same
+   as PTR_SIZE.  */
+
+#ifndef DWARF_OFFSET_SIZE
+#define DWARF_OFFSET_SIZE 4
+#endif
+
+/* According to the (draft) DWARF 3 specification, the initial length
+   should either be 4 or 12 bytes.  When it's 12 bytes, the first 4
+   bytes are 0xffffffff, followed by the length stored in the next 8
+   bytes.
+
+   However, the SGI/MIPS ABI uses an initial length which is equal to
+   DWARF_OFFSET_SIZE.  It is defined (elsewhere) accordingly.  */
+
+#ifndef DWARF_INITIAL_LENGTH_SIZE
+#define DWARF_INITIAL_LENGTH_SIZE (DWARF_OFFSET_SIZE == 4 ? 4 : 12)
+#endif
+
+#define DWARF_VERSION 2
+
+/* Round SIZE up to the nearest BOUNDARY.  */
+#define DWARF_ROUND(SIZE,BOUNDARY) \
+  ((((SIZE) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY))
+
+/* Offsets recorded in opcodes are a multiple of this alignment factor.  */
+#ifndef DWARF_CIE_DATA_ALIGNMENT
+#ifdef STACK_GROWS_DOWNWARD
+#define DWARF_CIE_DATA_ALIGNMENT (-((int) UNITS_PER_WORD))
+#else
+#define DWARF_CIE_DATA_ALIGNMENT ((int) UNITS_PER_WORD)
+#endif
+#endif
+
+/* CIE identifier.  */
+#if HOST_BITS_PER_WIDE_INT >= 64
+#define DWARF_CIE_ID \
+  (unsigned HOST_WIDE_INT) (DWARF_OFFSET_SIZE == 4 ? DW_CIE_ID : DW64_CIE_ID)
+#else
+#define DWARF_CIE_ID DW_CIE_ID
+#endif
+
+/* A pointer to the base of a table that contains frame description
+   information for each routine.  */
+static GTY((length ("fde_table_allocated"))) dw_fde_ref fde_table;
+
+/* Number of elements currently allocated for fde_table.  */
+static GTY(()) unsigned fde_table_allocated;
+
+/* Number of elements in fde_table currently in use.  */
+static GTY(()) unsigned fde_table_in_use;
+
+/* Size (in elements) of increments by which we may expand the
+   fde_table.  */
+#define FDE_TABLE_INCREMENT 256
+
+/* Get the current fde_table entry we should use.  */
+
+static inline dw_fde_ref
+current_fde (void)
+{
+  return fde_table_in_use ? &fde_table[fde_table_in_use - 1] : NULL;
+}
+
+/* A list of call frame insns for the CIE.  */
+static GTY(()) dw_cfi_ref cie_cfi_head;
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+/* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram
+   attribute that accelerates the lookup of the FDE associated
+   with the subprogram.  This variable holds the table index of the FDE
+   associated with the current function (body) definition.  */
+static unsigned current_funcdef_fde;
+#endif
+
+struct indirect_string_node GTY(())
+{
+  const char *str;
+  unsigned int refcount;
+  unsigned int form;
+  char *label;
+};
+
+static GTY ((param_is (struct indirect_string_node))) htab_t debug_str_hash;
+
+static GTY(()) int dw2_string_counter;
+static GTY(()) unsigned long dwarf2out_cfi_label_num;
+
+/* True if the compilation unit places functions in more than one section.  */
+static GTY(()) bool have_multiple_function_sections = false;
+
+/* Whether the default text and cold text sections have been used at all.  */
+
+static GTY(()) bool text_section_used = false;
+static GTY(()) bool cold_text_section_used = false;
+
+/* The default cold text section.  */
+static GTY(()) section *cold_text_section;
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+
+/* Forward declarations for functions defined in this file.  */
+
+static char *stripattributes (const char *);
+static const char *dwarf_cfi_name (unsigned);
+static dw_cfi_ref new_cfi (void);
+static void add_cfi (dw_cfi_ref *, dw_cfi_ref);
+static void add_fde_cfi (const char *, dw_cfi_ref);
+static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *);
+static void lookup_cfa (dw_cfa_location *);
+static void reg_save (const char *, unsigned, unsigned, HOST_WIDE_INT);
+#ifdef DWARF2_UNWIND_INFO
+static void initial_return_save (rtx);
+#endif
+static HOST_WIDE_INT stack_adjust_offset (const_rtx, HOST_WIDE_INT,
+					  HOST_WIDE_INT);
+static void output_cfi (dw_cfi_ref, dw_fde_ref, int);
+static void output_cfi_directive (dw_cfi_ref);
+static void output_call_frame_info (int);
+static void dwarf2out_note_section_used (void);
+static void dwarf2out_stack_adjust (rtx, bool);
+static void dwarf2out_args_size_adjust (HOST_WIDE_INT, const char *);
+static void flush_queued_reg_saves (void);
+static bool clobbers_queued_reg_save (const_rtx);
+static void dwarf2out_frame_debug_expr (rtx, const char *);
+
+/* Support for complex CFA locations.  */
+static void output_cfa_loc (dw_cfi_ref);
+static void output_cfa_loc_raw (dw_cfi_ref);
+static void get_cfa_from_loc_descr (dw_cfa_location *,
+				    struct dw_loc_descr_struct *);
+static struct dw_loc_descr_struct *build_cfa_loc
+  (dw_cfa_location *, HOST_WIDE_INT);
+static struct dw_loc_descr_struct *build_cfa_aligned_loc
+  (HOST_WIDE_INT, HOST_WIDE_INT);
+static void def_cfa_1 (const char *, dw_cfa_location *);
+
+/* How to start an assembler comment.  */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START ";#"
+#endif
+
+/* Data and reference forms for relocatable data.  */
+#define DW_FORM_data (DWARF_OFFSET_SIZE == 8 ? DW_FORM_data8 : DW_FORM_data4)
+#define DW_FORM_ref (DWARF_OFFSET_SIZE == 8 ? DW_FORM_ref8 : DW_FORM_ref4)
+
+#ifndef DEBUG_FRAME_SECTION
+#define DEBUG_FRAME_SECTION	".debug_frame"
+#endif
+
+#ifndef FUNC_BEGIN_LABEL
+#define FUNC_BEGIN_LABEL	"LFB"
+#endif
+
+#ifndef FUNC_END_LABEL
+#define FUNC_END_LABEL		"LFE"
+#endif
+
+#ifndef FRAME_BEGIN_LABEL
+#define FRAME_BEGIN_LABEL	"Lframe"
+#endif
+#define CIE_AFTER_SIZE_LABEL	"LSCIE"
+#define CIE_END_LABEL		"LECIE"
+#define FDE_LABEL		"LSFDE"
+#define FDE_AFTER_SIZE_LABEL	"LASFDE"
+#define FDE_END_LABEL		"LEFDE"
+#define LINE_NUMBER_BEGIN_LABEL	"LSLT"
+#define LINE_NUMBER_END_LABEL	"LELT"
+#define LN_PROLOG_AS_LABEL	"LASLTP"
+#define LN_PROLOG_END_LABEL	"LELTP"
+#define DIE_LABEL_PREFIX	"DW"
+
+/* The DWARF 2 CFA column which tracks the return address.  Normally this
+   is the column for PC, or the first column after all of the hard
+   registers.  */
+#ifndef DWARF_FRAME_RETURN_COLUMN
+#ifdef PC_REGNUM
+#define DWARF_FRAME_RETURN_COLUMN	DWARF_FRAME_REGNUM (PC_REGNUM)
+#else
+#define DWARF_FRAME_RETURN_COLUMN	DWARF_FRAME_REGISTERS
+#endif
+#endif
+
+/* The mapping from gcc register number to DWARF 2 CFA column number.  By
+   default, we just provide columns for all registers.  */
+#ifndef DWARF_FRAME_REGNUM
+#define DWARF_FRAME_REGNUM(REG) DBX_REGISTER_NUMBER (REG)
+#endif
+
+/* Hook used by __throw.  */
+
+rtx
+expand_builtin_dwarf_sp_column (void)
+{
+  unsigned int dwarf_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
+  return GEN_INT (DWARF2_FRAME_REG_OUT (dwarf_regnum, 1));
+}
+
+/* Return a pointer to a copy of the section string name S with all
+   attributes stripped off, and an asterisk prepended (for assemble_name).  */
+
+static inline char *
+stripattributes (const char *s)
+{
+  char *stripped = XNEWVEC (char, strlen (s) + 2);
+  char *p = stripped;
+
+  *p++ = '*';
+
+  while (*s && *s != ',')
+    *p++ = *s++;
+
+  *p = '\0';
+  return stripped;
+}
+
+/* MEM is a memory reference for the register size table, each element of
+   which has mode MODE.  Initialize column C as a return address column.  */
+
+static void
+init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c)
+{
+  HOST_WIDE_INT offset = c * GET_MODE_SIZE (mode);
+  HOST_WIDE_INT size = GET_MODE_SIZE (Pmode);
+  emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size));
+}
+
+/* Generate code to initialize the register size table.  */
+
+void
+expand_builtin_init_dwarf_reg_sizes (tree address)
+{
+  unsigned int i;
+  enum machine_mode mode = TYPE_MODE (char_type_node);
+  rtx addr = expand_normal (address);
+  rtx mem = gen_rtx_MEM (BLKmode, addr);
+  bool wrote_return_column = false;
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      int rnum = DWARF2_FRAME_REG_OUT (DWARF_FRAME_REGNUM (i), 1);
+
+      if (rnum < DWARF_FRAME_REGISTERS)
+	{
+	  HOST_WIDE_INT offset = rnum * GET_MODE_SIZE (mode);
+	  enum machine_mode save_mode = reg_raw_mode[i];
+	  HOST_WIDE_INT size;
+
+	  if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode))
+	    save_mode = choose_hard_reg_mode (i, 1, true);
+	  if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN)
+	    {
+	      if (save_mode == VOIDmode)
+		continue;
+	      wrote_return_column = true;
+	    }
+	  size = GET_MODE_SIZE (save_mode);
+	  if (offset < 0)
+	    continue;
+
+	  emit_move_insn (adjust_address (mem, mode, offset),
+			  gen_int_mode (size, mode));
+	}
+    }
+
+  if (!wrote_return_column)
+    init_return_column_size (mode, mem, DWARF_FRAME_RETURN_COLUMN);
+
+#ifdef DWARF_ALT_FRAME_RETURN_COLUMN
+  init_return_column_size (mode, mem, DWARF_ALT_FRAME_RETURN_COLUMN);
+#endif
+
+  targetm.init_dwarf_reg_sizes_extra (address);
+}
+
+/* Convert a DWARF call frame info. operation to its string name */
+
+static const char *
+dwarf_cfi_name (unsigned int cfi_opc)
+{
+  switch (cfi_opc)
+    {
+    case DW_CFA_advance_loc:
+      return "DW_CFA_advance_loc";
+    case DW_CFA_offset:
+      return "DW_CFA_offset";
+    case DW_CFA_restore:
+      return "DW_CFA_restore";
+    case DW_CFA_nop:
+      return "DW_CFA_nop";
+    case DW_CFA_set_loc:
+      return "DW_CFA_set_loc";
+    case DW_CFA_advance_loc1:
+      return "DW_CFA_advance_loc1";
+    case DW_CFA_advance_loc2:
+      return "DW_CFA_advance_loc2";
+    case DW_CFA_advance_loc4:
+      return "DW_CFA_advance_loc4";
+    case DW_CFA_offset_extended:
+      return "DW_CFA_offset_extended";
+    case DW_CFA_restore_extended:
+      return "DW_CFA_restore_extended";
+    case DW_CFA_undefined:
+      return "DW_CFA_undefined";
+    case DW_CFA_same_value:
+      return "DW_CFA_same_value";
+    case DW_CFA_register:
+      return "DW_CFA_register";
+    case DW_CFA_remember_state:
+      return "DW_CFA_remember_state";
+    case DW_CFA_restore_state:
+      return "DW_CFA_restore_state";
+    case DW_CFA_def_cfa:
+      return "DW_CFA_def_cfa";
+    case DW_CFA_def_cfa_register:
+      return "DW_CFA_def_cfa_register";
+    case DW_CFA_def_cfa_offset:
+      return "DW_CFA_def_cfa_offset";
+
+    /* DWARF 3 */
+    case DW_CFA_def_cfa_expression:
+      return "DW_CFA_def_cfa_expression";
+    case DW_CFA_expression:
+      return "DW_CFA_expression";
+    case DW_CFA_offset_extended_sf:
+      return "DW_CFA_offset_extended_sf";
+    case DW_CFA_def_cfa_sf:
+      return "DW_CFA_def_cfa_sf";
+    case DW_CFA_def_cfa_offset_sf:
+      return "DW_CFA_def_cfa_offset_sf";
+
+    /* SGI/MIPS specific */
+    case DW_CFA_MIPS_advance_loc8:
+      return "DW_CFA_MIPS_advance_loc8";
+
+    /* GNU extensions */
+    case DW_CFA_GNU_window_save:
+      return "DW_CFA_GNU_window_save";
+    case DW_CFA_GNU_args_size:
+      return "DW_CFA_GNU_args_size";
+    case DW_CFA_GNU_negative_offset_extended:
+      return "DW_CFA_GNU_negative_offset_extended";
+
+    default:
+      return "DW_CFA_<unknown>";
+    }
+}
+
+/* Return a pointer to a newly allocated Call Frame Instruction.  */
+
+static inline dw_cfi_ref
+new_cfi (void)
+{
+  dw_cfi_ref cfi = GGC_NEW (dw_cfi_node);
+
+  cfi->dw_cfi_next = NULL;
+  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0;
+  cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0;
+
+  return cfi;
+}
+
+/* Add a Call Frame Instruction to list of instructions.  */
+
+static inline void
+add_cfi (dw_cfi_ref *list_head, dw_cfi_ref cfi)
+{
+  dw_cfi_ref *p;
+  dw_fde_ref fde = current_fde ();
+
+  /* When DRAP is used, CFA is defined with an expression.  Redefine
+     CFA may lead to a different CFA value.   */
+  if (fde && fde->drap_reg != INVALID_REGNUM)
+    switch (cfi->dw_cfi_opc)
+      {
+        case DW_CFA_def_cfa_register:
+        case DW_CFA_def_cfa_offset:
+        case DW_CFA_def_cfa_offset_sf:
+        case DW_CFA_def_cfa:
+        case DW_CFA_def_cfa_sf:
+	  gcc_unreachable ();
+
+        default:
+          break;
+      }
+
+  /* Find the end of the chain.  */
+  for (p = list_head; (*p) != NULL; p = &(*p)->dw_cfi_next)
+    ;
+
+  *p = cfi;
+}
+
+/* Generate a new label for the CFI info to refer to.  */
+
+char *
+dwarf2out_cfi_label (void)
+{
+  static char label[20];
+
+  if (dwarf2out_do_cfi_asm ())
+    {
+      /* In this case, we will be emitting the asm directive instead of
+	 the label, so just return a placeholder to keep the rest of the
+	 interfaces happy.  */
+      strcpy (label, "<do not output>");
+    }
+  else
+    {
+      ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", dwarf2out_cfi_label_num++);
+      ASM_OUTPUT_LABEL (asm_out_file, label);
+    }
+
+  return label;
+}
+
+/* Add CFI to the current fde at the PC value indicated by LABEL if specified,
+   or to the CIE if LABEL is NULL.  */
+
+static void
+add_fde_cfi (const char *label, dw_cfi_ref cfi)
+{
+  dw_cfi_ref *list_head = &cie_cfi_head;
+
+  if (dwarf2out_do_cfi_asm ())
+    {
+      if (label)
+	{
+	  output_cfi_directive (cfi);
+
+	  /* We still have to add the cfi to the list so that
+	     lookup_cfa works later on.  */
+	  list_head = &current_fde ()->dw_fde_cfi;
+	}
+      /* ??? If this is a CFI for the CIE, we don't emit.  This
+	 assumes that the standard CIE contents that the assembler
+	 uses matches the standard CIE contents that the compiler
+	 uses.  This is probably a bad assumption.  I'm not quite
+	 sure how to address this for now.  */
+    }
+  else if (label)
+    {
+      dw_fde_ref fde = current_fde ();
+
+      gcc_assert (fde != NULL);
+
+      if (*label == 0)
+	label = dwarf2out_cfi_label ();
+
+      if (fde->dw_fde_current_label == NULL
+	  || strcmp (label, fde->dw_fde_current_label) != 0)
+	{
+	  dw_cfi_ref xcfi;
+
+	  label = xstrdup (label);
+
+	  /* Set the location counter to the new label.  */
+	  xcfi = new_cfi ();
+	  /* If we have a current label, advance from there, otherwise
+	     set the location directly using set_loc.  */
+	  xcfi->dw_cfi_opc = fde->dw_fde_current_label
+			     ? DW_CFA_advance_loc4
+			     : DW_CFA_set_loc;
+	  xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
+	  add_cfi (&fde->dw_fde_cfi, xcfi);
+
+	  fde->dw_fde_current_label = label;
+	}
+
+      list_head = &fde->dw_fde_cfi;
+    }
+
+  add_cfi (list_head, cfi);
+}
+
+/* Subroutine of lookup_cfa.  */
+
+static void
+lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc)
+{
+  switch (cfi->dw_cfi_opc)
+    {
+    case DW_CFA_def_cfa_offset:
+    case DW_CFA_def_cfa_offset_sf:
+      loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
+      break;
+    case DW_CFA_def_cfa_register:
+      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      break;
+    case DW_CFA_def_cfa:
+    case DW_CFA_def_cfa_sf:
+      loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
+      loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
+      break;
+    case DW_CFA_def_cfa_expression:
+      get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
+      break;
+    default:
+      break;
+    }
+}
+
+/* Find the previous value for the CFA.  */
+
+static void
+lookup_cfa (dw_cfa_location *loc)
+{
+  dw_cfi_ref cfi;
+  dw_fde_ref fde;
+
+  loc->reg = INVALID_REGNUM;
+  loc->offset = 0;
+  loc->indirect = 0;
+  loc->base_offset = 0;
+
+  for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next)
+    lookup_cfa_1 (cfi, loc);
+
+  fde = current_fde ();
+  if (fde)
+    for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next)
+      lookup_cfa_1 (cfi, loc);
+}
+
+/* The current rule for calculating the DWARF2 canonical frame address.  */
+static dw_cfa_location cfa;
+
+/* The register used for saving registers to the stack, and its offset
+   from the CFA.  */
+static dw_cfa_location cfa_store;
+
+/* The running total of the size of arguments pushed onto the stack.  */
+static HOST_WIDE_INT args_size;
+
+/* The last args_size we actually output.  */
+static HOST_WIDE_INT old_args_size;
+
+/* Entry point to update the canonical frame address (CFA).
+   LABEL is passed to add_fde_cfi.  The value of CFA is now to be
+   calculated from REG+OFFSET.  */
+
+void
+dwarf2out_def_cfa (const char *label, unsigned int reg, HOST_WIDE_INT offset)
+{
+  dw_cfa_location loc;
+  loc.indirect = 0;
+  loc.base_offset = 0;
+  loc.reg = reg;
+  loc.offset = offset;
+  def_cfa_1 (label, &loc);
+}
+
+/* Determine if two dw_cfa_location structures define the same data.  */
+
+static bool
+cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2)
+{
+  return (loc1->reg == loc2->reg
+	  && loc1->offset == loc2->offset
+	  && loc1->indirect == loc2->indirect
+	  && (loc1->indirect == 0
+	      || loc1->base_offset == loc2->base_offset));
+}
+
+/* This routine does the actual work.  The CFA is now calculated from
+   the dw_cfa_location structure.  */
+
+static void
+def_cfa_1 (const char *label, dw_cfa_location *loc_p)
+{
+  dw_cfi_ref cfi;
+  dw_cfa_location old_cfa, loc;
+
+  cfa = *loc_p;
+  loc = *loc_p;
+
+  if (cfa_store.reg == loc.reg && loc.indirect == 0)
+    cfa_store.offset = loc.offset;
+
+  loc.reg = DWARF_FRAME_REGNUM (loc.reg);
+  lookup_cfa (&old_cfa);
+
+  /* If nothing changed, no need to issue any call frame instructions.  */
+  if (cfa_equal_p (&loc, &old_cfa))
+    return;
+
+  cfi = new_cfi ();
+
+  if (loc.reg == old_cfa.reg && !loc.indirect)
+    {
+      /* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
+	 the CFA register did not change but the offset did.  The data 
+	 factoring for DW_CFA_def_cfa_offset_sf happens in output_cfi, or
+	 in the assembler via the .cfi_def_cfa_offset directive.  */
+      if (loc.offset < 0)
+	cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
+      else
+	cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
+      cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset;
+    }
+
+#ifndef MIPS_DEBUGGING_INFO  /* SGI dbx thinks this means no offset.  */
+  else if (loc.offset == old_cfa.offset
+	   && old_cfa.reg != INVALID_REGNUM
+	   && !loc.indirect)
+    {
+      /* Construct a "DW_CFA_def_cfa_register <register>" instruction,
+	 indicating the CFA register has changed to <register> but the
+	 offset has not changed.  */
+      cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
+    }
+#endif
+
+  else if (loc.indirect == 0)
+    {
+      /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
+	 indicating the CFA register has changed to <register> with
+	 the specified offset.  The data factoring for DW_CFA_def_cfa_sf
+	 happens in output_cfi, or in the assembler via the .cfi_def_cfa
+	 directive.  */
+      if (loc.offset < 0)
+	cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
+      else
+	cfi->dw_cfi_opc = DW_CFA_def_cfa;
+      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg;
+      cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset;
+    }
+  else
+    {
+      /* Construct a DW_CFA_def_cfa_expression instruction to
+	 calculate the CFA using a full location expression since no
+	 register-offset pair is available.  */
+      struct dw_loc_descr_struct *loc_list;
+
+      cfi->dw_cfi_opc = DW_CFA_def_cfa_expression;
+      loc_list = build_cfa_loc (&loc, 0);
+      cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list;
+    }
+
+  add_fde_cfi (label, cfi);
+}
+
+/* Add the CFI for saving a register.  REG is the CFA column number.
+   LABEL is passed to add_fde_cfi.
+   If SREG is -1, the register is saved at OFFSET from the CFA;
+   otherwise it is saved in SREG.  */
+
+static void
+reg_save (const char *label, unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
+{
+  dw_cfi_ref cfi = new_cfi ();
+  dw_fde_ref fde = current_fde ();
+
+  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+
+  /* When stack is aligned, store REG using DW_CFA_expression with
+     FP.  */
+  if (fde
+      && fde->stack_realign
+      && sreg == INVALID_REGNUM)
+    {
+      cfi->dw_cfi_opc = DW_CFA_expression;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = reg;
+      cfi->dw_cfi_oprnd1.dw_cfi_loc
+	= build_cfa_aligned_loc (offset, fde->stack_realignment);
+    }
+  else if (sreg == INVALID_REGNUM)
+    {
+      if (offset < 0)
+	cfi->dw_cfi_opc = DW_CFA_offset_extended_sf;
+      else if (reg & ~0x3f)
+	cfi->dw_cfi_opc = DW_CFA_offset_extended;
+      else
+	cfi->dw_cfi_opc = DW_CFA_offset;
+      cfi->dw_cfi_oprnd2.dw_cfi_offset = offset;
+    }
+  else if (sreg == reg)
+    cfi->dw_cfi_opc = DW_CFA_same_value;
+  else
+    {
+      cfi->dw_cfi_opc = DW_CFA_register;
+      cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
+    }
+
+  add_fde_cfi (label, cfi);
+}
+
+/* Add the CFI for saving a register window.  LABEL is passed to reg_save.
+   This CFI tells the unwinder that it needs to restore the window registers
+   from the previous frame's window save area.
+
+   ??? Perhaps we should note in the CIE where windows are saved (instead of
+   assuming 0(cfa)) and what registers are in the window.  */
+
+void
+dwarf2out_window_save (const char *label)
+{
+  dw_cfi_ref cfi = new_cfi ();
+
+  cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
+  add_fde_cfi (label, cfi);
+}
+
+/* Add a CFI to update the running total of the size of arguments
+   pushed onto the stack.  */
+
+void
+dwarf2out_args_size (const char *label, HOST_WIDE_INT size)
+{
+  dw_cfi_ref cfi;
+
+  if (size == old_args_size)
+    return;
+
+  old_args_size = size;
+
+  cfi = new_cfi ();
+  cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
+  cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
+  add_fde_cfi (label, cfi);
+}
+
+/* Entry point for saving a register to the stack.  REG is the GCC register
+   number.  LABEL and OFFSET are passed to reg_save.  */
+
+void
+dwarf2out_reg_save (const char *label, unsigned int reg, HOST_WIDE_INT offset)
+{
+  reg_save (label, DWARF_FRAME_REGNUM (reg), INVALID_REGNUM, offset);
+}
+
+/* Entry point for saving the return address in the stack.
+   LABEL and OFFSET are passed to reg_save.  */
+
+void
+dwarf2out_return_save (const char *label, HOST_WIDE_INT offset)
+{
+  reg_save (label, DWARF_FRAME_RETURN_COLUMN, INVALID_REGNUM, offset);
+}
+
+/* Entry point for saving the return address in a register.
+   LABEL and SREG are passed to reg_save.  */
+
+void
+dwarf2out_return_reg (const char *label, unsigned int sreg)
+{
+  reg_save (label, DWARF_FRAME_RETURN_COLUMN, DWARF_FRAME_REGNUM (sreg), 0);
+}
+
+#ifdef DWARF2_UNWIND_INFO
+/* Record the initial position of the return address.  RTL is
+   INCOMING_RETURN_ADDR_RTX.  */
+
+static void
+initial_return_save (rtx rtl)
+{
+  unsigned int reg = INVALID_REGNUM;
+  HOST_WIDE_INT offset = 0;
+
+  switch (GET_CODE (rtl))
+    {
+    case REG:
+      /* RA is in a register.  */
+      reg = DWARF_FRAME_REGNUM (REGNO (rtl));
+      break;
+
+    case MEM:
+      /* RA is on the stack.  */
+      rtl = XEXP (rtl, 0);
+      switch (GET_CODE (rtl))
+	{
+	case REG:
+	  gcc_assert (REGNO (rtl) == STACK_POINTER_REGNUM);
+	  offset = 0;
+	  break;
+
+	case PLUS:
+	  gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
+	  offset = INTVAL (XEXP (rtl, 1));
+	  break;
+
+	case MINUS:
+	  gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
+	  offset = -INTVAL (XEXP (rtl, 1));
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+
+      break;
+
+    case PLUS:
+      /* The return address is at some offset from any value we can
+	 actually load.  For instance, on the SPARC it is in %i7+8. Just
+	 ignore the offset for now; it doesn't matter for unwinding frames.  */
+      gcc_assert (GET_CODE (XEXP (rtl, 1)) == CONST_INT);
+      initial_return_save (XEXP (rtl, 0));
+      return;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  if (reg != DWARF_FRAME_RETURN_COLUMN)
+    reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa.offset);
+}
+#endif
+
+/* Given a SET, calculate the amount of stack adjustment it
+   contains.  */
+
+static HOST_WIDE_INT
+stack_adjust_offset (const_rtx pattern, HOST_WIDE_INT cur_args_size,
+		     HOST_WIDE_INT cur_offset)
+{
+  const_rtx src = SET_SRC (pattern);
+  const_rtx dest = SET_DEST (pattern);
+  HOST_WIDE_INT offset = 0;
+  enum rtx_code code;
+
+  if (dest == stack_pointer_rtx)
+    {
+      code = GET_CODE (src);
+
+      /* Assume (set (reg sp) (reg whatever)) sets args_size
+	 level to 0.  */
+      if (code == REG && src != stack_pointer_rtx)
+	{
+	  offset = -cur_args_size;
+#ifndef STACK_GROWS_DOWNWARD
+	  offset = -offset;
+#endif
+	  return offset - cur_offset;
+	}
+
+      if (! (code == PLUS || code == MINUS)
+	  || XEXP (src, 0) != stack_pointer_rtx
+	  || GET_CODE (XEXP (src, 1)) != CONST_INT)
+	return 0;
+
+      /* (set (reg sp) (plus (reg sp) (const_int))) */
+      offset = INTVAL (XEXP (src, 1));
+      if (code == PLUS)
+	offset = -offset;
+      return offset;
+    }
+
+  if (MEM_P (src) && !MEM_P (dest))
+    dest = src;
+  if (MEM_P (dest))
+    {
+      /* (set (mem (pre_dec (reg sp))) (foo)) */
+      src = XEXP (dest, 0);
+      code = GET_CODE (src);
+
+      switch (code)
+	{
+	case PRE_MODIFY:
+	case POST_MODIFY:
+	  if (XEXP (src, 0) == stack_pointer_rtx)
+	    {
+	      rtx val = XEXP (XEXP (src, 1), 1);
+	      /* We handle only adjustments by constant amount.  */
+	      gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS
+			  && GET_CODE (val) == CONST_INT);
+	      offset = -INTVAL (val);
+	      break;
+	    }
+	  return 0;
+
+	case PRE_DEC:
+	case POST_DEC:
+	  if (XEXP (src, 0) == stack_pointer_rtx)
+	    {
+	      offset = GET_MODE_SIZE (GET_MODE (dest));
+	      break;
+	    }
+	  return 0;
+
+	case PRE_INC:
+	case POST_INC:
+	  if (XEXP (src, 0) == stack_pointer_rtx)
+	    {
+	      offset = -GET_MODE_SIZE (GET_MODE (dest));
+	      break;
+	    }
+	  return 0;
+
+	default:
+	  return 0;
+	}
+    }
+  else
+    return 0;
+
+  return offset;
+}
+
+/* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them,
+   indexed by INSN_UID.  */
+
+static HOST_WIDE_INT *barrier_args_size;
+
+/* Helper function for compute_barrier_args_size.  Handle one insn.  */
+
+static HOST_WIDE_INT
+compute_barrier_args_size_1 (rtx insn, HOST_WIDE_INT cur_args_size,
+			     VEC (rtx, heap) **next)
+{
+  HOST_WIDE_INT offset = 0;
+  int i;
+
+  if (! RTX_FRAME_RELATED_P (insn))
+    {
+      if (prologue_epilogue_contains (insn)
+	  || sibcall_epilogue_contains (insn))
+	/* Nothing */;
+      else if (GET_CODE (PATTERN (insn)) == SET)
+	offset = stack_adjust_offset (PATTERN (insn), cur_args_size, 0);
+      else if (GET_CODE (PATTERN (insn)) == PARALLEL
+	       || GET_CODE (PATTERN (insn)) == SEQUENCE)
+	{
+	  /* There may be stack adjustments inside compound insns.  Search
+	     for them.  */
+	  for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+	    if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
+	      offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
+					     cur_args_size, offset);
+	}
+    }
+  else
+    {
+      rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+
+      if (expr)
+	{
+	  expr = XEXP (expr, 0);
+	  if (GET_CODE (expr) == PARALLEL
+	      || GET_CODE (expr) == SEQUENCE)
+	    for (i = 1; i < XVECLEN (expr, 0); i++)
+	      {
+		rtx elem = XVECEXP (expr, 0, i);
+
+		if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem))
+		  offset += stack_adjust_offset (elem, cur_args_size, offset);
+	      }
+	}
+    }
+
+#ifndef STACK_GROWS_DOWNWARD
+  offset = -offset;
+#endif
+
+  cur_args_size += offset;
+  if (cur_args_size < 0)
+    cur_args_size = 0;
+
+  if (JUMP_P (insn))
+    {
+      rtx dest = JUMP_LABEL (insn);
+
+      if (dest)
+	{
+	  if (barrier_args_size [INSN_UID (dest)] < 0)
+	    {
+	      barrier_args_size [INSN_UID (dest)] = cur_args_size;
+	      VEC_safe_push (rtx, heap, *next, dest);
+	    }
+	}
+    }
+
+  return cur_args_size;
+}
+
+/* Walk the whole function and compute args_size on BARRIERs.  */
+
+static void
+compute_barrier_args_size (void)
+{
+  int max_uid = get_max_uid (), i;
+  rtx insn;
+  VEC (rtx, heap) *worklist, *next, *tmp;
+
+  barrier_args_size = XNEWVEC (HOST_WIDE_INT, max_uid);
+  for (i = 0; i < max_uid; i++)
+    barrier_args_size[i] = -1;
+
+  worklist = VEC_alloc (rtx, heap, 20);
+  next = VEC_alloc (rtx, heap, 20);
+  insn = get_insns ();
+  barrier_args_size[INSN_UID (insn)] = 0;
+  VEC_quick_push (rtx, worklist, insn);
+  for (;;)
+    {
+      while (!VEC_empty (rtx, worklist))
+	{
+	  rtx prev, body, first_insn;
+	  HOST_WIDE_INT cur_args_size;
+
+	  first_insn = insn = VEC_pop (rtx, worklist);
+	  cur_args_size = barrier_args_size[INSN_UID (insn)];
+	  prev = prev_nonnote_insn (insn);
+	  if (prev && BARRIER_P (prev))
+	    barrier_args_size[INSN_UID (prev)] = cur_args_size;
+
+	  for (; insn; insn = NEXT_INSN (insn))
+	    {
+	      if (INSN_DELETED_P (insn) || NOTE_P (insn))
+		continue;
+	      if (BARRIER_P (insn))
+		break;
+
+	      if (LABEL_P (insn))
+		{
+		  if (insn == first_insn)
+		    continue;
+		  else if (barrier_args_size[INSN_UID (insn)] < 0)
+		    {
+		      barrier_args_size[INSN_UID (insn)] = cur_args_size;
+		      continue;
+		    }
+		  else
+		    {
+		      /* The insns starting with this label have been
+			 already scanned or are in the worklist.  */
+		      break;
+		    }
+		}
+
+	      body = PATTERN (insn);
+	      if (GET_CODE (body) == SEQUENCE)
+		{
+		  HOST_WIDE_INT dest_args_size = cur_args_size;
+		  for (i = 1; i < XVECLEN (body, 0); i++)
+		    if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0))
+			&& INSN_FROM_TARGET_P (XVECEXP (body, 0, i)))
+		      dest_args_size
+			= compute_barrier_args_size_1 (XVECEXP (body, 0, i),
+						       dest_args_size, &next);
+		    else
+		      cur_args_size
+			= compute_barrier_args_size_1 (XVECEXP (body, 0, i),
+						       cur_args_size, &next);
+
+		  if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0)))
+		    compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
+						 dest_args_size, &next);
+		  else
+		    cur_args_size
+		      = compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
+						     cur_args_size, &next);
+		}
+	      else
+		cur_args_size
+		  = compute_barrier_args_size_1 (insn, cur_args_size, &next);
+	    }
+	}
+
+      if (VEC_empty (rtx, next))
+	break;
+
+      /* Swap WORKLIST with NEXT and truncate NEXT for next iteration.  */
+      tmp = next;
+      next = worklist;
+      worklist = tmp;
+      VEC_truncate (rtx, next, 0);
+    }
+
+  VEC_free (rtx, heap, worklist);
+  VEC_free (rtx, heap, next);
+}
+
+
+/* Check INSN to see if it looks like a push or a stack adjustment, and
+   make a note of it if it does.  EH uses this information to find out how
+   much extra space it needs to pop off the stack.  */
+
+static void
+dwarf2out_stack_adjust (rtx insn, bool after_p)
+{
+  HOST_WIDE_INT offset;
+  const char *label;
+  int i;
+
+  /* Don't handle epilogues at all.  Certainly it would be wrong to do so
+     with this function.  Proper support would require all frame-related
+     insns to be marked, and to be able to handle saving state around
+     epilogues textually in the middle of the function.  */
+  if (prologue_epilogue_contains (insn) || sibcall_epilogue_contains (insn))
+    return;
+
+  /* If INSN is an instruction from target of an annulled branch, the
+     effects are for the target only and so current argument size
+     shouldn't change at all.  */
+  if (final_sequence
+      && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
+      && INSN_FROM_TARGET_P (insn))
+    return;
+
+  /* If only calls can throw, and we have a frame pointer,
+     save up adjustments until we see the CALL_INSN.  */
+  if (!flag_asynchronous_unwind_tables && cfa.reg != STACK_POINTER_REGNUM)
+    {
+      if (CALL_P (insn) && !after_p)
+	{
+	  /* Extract the size of the args from the CALL rtx itself.  */
+	  insn = PATTERN (insn);
+	  if (GET_CODE (insn) == PARALLEL)
+	    insn = XVECEXP (insn, 0, 0);
+	  if (GET_CODE (insn) == SET)
+	    insn = SET_SRC (insn);
+	  gcc_assert (GET_CODE (insn) == CALL);
+	  dwarf2out_args_size ("", INTVAL (XEXP (insn, 1)));
+	}
+      return;
+    }
+
+  if (CALL_P (insn) && !after_p)
+    {
+      if (!flag_asynchronous_unwind_tables)
+	dwarf2out_args_size ("", args_size);
+      return;
+    }
+  else if (BARRIER_P (insn))
+    {
+      /* Don't call compute_barrier_args_size () if the only
+	 BARRIER is at the end of function.  */
+      if (barrier_args_size == NULL && next_nonnote_insn (insn))
+	compute_barrier_args_size ();
+      if (barrier_args_size == NULL)
+	offset = 0;
+      else
+	{
+	  offset = barrier_args_size[INSN_UID (insn)];
+	  if (offset < 0)
+	    offset = 0;
+	}
+
+      offset -= args_size;
+#ifndef STACK_GROWS_DOWNWARD
+      offset = -offset;
+#endif
+    }
+  else if (GET_CODE (PATTERN (insn)) == SET)
+    offset = stack_adjust_offset (PATTERN (insn), args_size, 0);
+  else if (GET_CODE (PATTERN (insn)) == PARALLEL
+	   || GET_CODE (PATTERN (insn)) == SEQUENCE)
+    {
+      /* There may be stack adjustments inside compound insns.  Search
+	 for them.  */
+      for (offset = 0, i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+	if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
+	  offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
+					 args_size, offset);
+    }
+  else
+    return;
+
+  if (offset == 0)
+    return;
+
+  label = dwarf2out_cfi_label ();
+  dwarf2out_args_size_adjust (offset, label);
+}
+
+/* Adjust args_size based on stack adjustment OFFSET.  */
+
+static void
+dwarf2out_args_size_adjust (HOST_WIDE_INT offset, const char *label)
+{
+  if (cfa.reg == STACK_POINTER_REGNUM)
+    cfa.offset += offset;
+
+  if (cfa_store.reg == STACK_POINTER_REGNUM)
+    cfa_store.offset += offset;
+
+#ifndef STACK_GROWS_DOWNWARD
+  offset = -offset;
+#endif
+
+  args_size += offset;
+  if (args_size < 0)
+    args_size = 0;
+
+  def_cfa_1 (label, &cfa);
+  if (flag_asynchronous_unwind_tables)
+    dwarf2out_args_size (label, args_size);
+}
+
+#endif
+
+/* We delay emitting a register save until either (a) we reach the end
+   of the prologue or (b) the register is clobbered.  This clusters
+   register saves so that there are fewer pc advances.  */
+
+struct queued_reg_save GTY(())
+{
+  struct queued_reg_save *next;
+  rtx reg;
+  HOST_WIDE_INT cfa_offset;
+  rtx saved_reg;
+};
+
+static GTY(()) struct queued_reg_save *queued_reg_saves;
+
+/* The caller's ORIG_REG is saved in SAVED_IN_REG.  */
+struct reg_saved_in_data GTY(()) {
+  rtx orig_reg;
+  rtx saved_in_reg;
+};
+
+/* A list of registers saved in other registers.
+   The list intentionally has a small maximum capacity of 4; if your
+   port needs more than that, you might consider implementing a
+   more efficient data structure.  */
+static GTY(()) struct reg_saved_in_data regs_saved_in_regs[4];
+static GTY(()) size_t num_regs_saved_in_regs;
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+static const char *last_reg_save_label;
+
+/* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at
+   SREG, or if SREG is NULL then it is saved at OFFSET to the CFA.  */
+
+static void
+queue_reg_save (const char *label, rtx reg, rtx sreg, HOST_WIDE_INT offset)
+{
+  struct queued_reg_save *q;
+
+  /* Duplicates waste space, but it's also necessary to remove them
+     for correctness, since the queue gets output in reverse
+     order.  */
+  for (q = queued_reg_saves; q != NULL; q = q->next)
+    if (REGNO (q->reg) == REGNO (reg))
+      break;
+
+  if (q == NULL)
+    {
+      q = GGC_NEW (struct queued_reg_save);
+      q->next = queued_reg_saves;
+      queued_reg_saves = q;
+    }
+
+  q->reg = reg;
+  q->cfa_offset = offset;
+  q->saved_reg = sreg;
+
+  last_reg_save_label = label;
+}
+
+/* Output all the entries in QUEUED_REG_SAVES.  */
+
+static void
+flush_queued_reg_saves (void)
+{
+  struct queued_reg_save *q;
+
+  for (q = queued_reg_saves; q; q = q->next)
+    {
+      size_t i;
+      unsigned int reg, sreg;
+
+      for (i = 0; i < num_regs_saved_in_regs; i++)
+	if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (q->reg))
+	  break;
+      if (q->saved_reg && i == num_regs_saved_in_regs)
+	{
+	  gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs));
+	  num_regs_saved_in_regs++;
+	}
+      if (i != num_regs_saved_in_regs)
+	{
+	  regs_saved_in_regs[i].orig_reg = q->reg;
+	  regs_saved_in_regs[i].saved_in_reg = q->saved_reg;
+	}
+
+      reg = DWARF_FRAME_REGNUM (REGNO (q->reg));
+      if (q->saved_reg)
+	sreg = DWARF_FRAME_REGNUM (REGNO (q->saved_reg));
+      else
+	sreg = INVALID_REGNUM;
+      reg_save (last_reg_save_label, reg, sreg, q->cfa_offset);
+    }
+
+  queued_reg_saves = NULL;
+  last_reg_save_label = NULL;
+}
+
+/* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved
+   location for?  Or, does it clobber a register which we've previously
+   said that some other register is saved in, and for which we now
+   have a new location for?  */
+
+static bool
+clobbers_queued_reg_save (const_rtx insn)
+{
+  struct queued_reg_save *q;
+
+  for (q = queued_reg_saves; q; q = q->next)
+    {
+      size_t i;
+      if (modified_in_p (q->reg, insn))
+	return true;
+      for (i = 0; i < num_regs_saved_in_regs; i++)
+	if (REGNO (q->reg) == REGNO (regs_saved_in_regs[i].orig_reg)
+	    && modified_in_p (regs_saved_in_regs[i].saved_in_reg, insn))
+	  return true;
+    }
+
+  return false;
+}
+
+/* Entry point for saving the first register into the second.  */
+
+void
+dwarf2out_reg_save_reg (const char *label, rtx reg, rtx sreg)
+{
+  size_t i;
+  unsigned int regno, sregno;
+
+  for (i = 0; i < num_regs_saved_in_regs; i++)
+    if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (reg))
+      break;
+  if (i == num_regs_saved_in_regs)
+    {
+      gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs));
+      num_regs_saved_in_regs++;
+    }
+  regs_saved_in_regs[i].orig_reg = reg;
+  regs_saved_in_regs[i].saved_in_reg = sreg;
+
+  regno = DWARF_FRAME_REGNUM (REGNO (reg));
+  sregno = DWARF_FRAME_REGNUM (REGNO (sreg));
+  reg_save (label, regno, sregno, 0);
+}
+
+/* What register, if any, is currently saved in REG?  */
+
+static rtx
+reg_saved_in (rtx reg)
+{
+  unsigned int regn = REGNO (reg);
+  size_t i;
+  struct queued_reg_save *q;
+
+  for (q = queued_reg_saves; q; q = q->next)
+    if (q->saved_reg && regn == REGNO (q->saved_reg))
+      return q->reg;
+
+  for (i = 0; i < num_regs_saved_in_regs; i++)
+    if (regs_saved_in_regs[i].saved_in_reg
+	&& regn == REGNO (regs_saved_in_regs[i].saved_in_reg))
+      return regs_saved_in_regs[i].orig_reg;
+
+  return NULL_RTX;
+}
+
+
+/* A temporary register holding an integral value used in adjusting SP
+   or setting up the store_reg.  The "offset" field holds the integer
+   value, not an offset.  */
+static dw_cfa_location cfa_temp;
+
+/* Record call frame debugging information for an expression EXPR,
+   which either sets SP or FP (adjusting how we calculate the frame
+   address) or saves a register to the stack or another register.
+   LABEL indicates the address of EXPR.
+
+   This function encodes a state machine mapping rtxes to actions on
+   cfa, cfa_store, and cfa_temp.reg.  We describe these rules so
+   users need not read the source code.
+
+  The High-Level Picture
+
+  Changes in the register we use to calculate the CFA: Currently we
+  assume that if you copy the CFA register into another register, we
+  should take the other one as the new CFA register; this seems to
+  work pretty well.  If it's wrong for some target, it's simple
+  enough not to set RTX_FRAME_RELATED_P on the insn in question.
+
+  Changes in the register we use for saving registers to the stack:
+  This is usually SP, but not always.  Again, we deduce that if you
+  copy SP into another register (and SP is not the CFA register),
+  then the new register is the one we will be using for register
+  saves.  This also seems to work.
+
+  Register saves: There's not much guesswork about this one; if
+  RTX_FRAME_RELATED_P is set on an insn which modifies memory, it's a
+  register save, and the register used to calculate the destination
+  had better be the one we think we're using for this purpose.
+  It's also assumed that a copy from a call-saved register to another
+  register is saving that register if RTX_FRAME_RELATED_P is set on
+  that instruction.  If the copy is from a call-saved register to
+  the *same* register, that means that the register is now the same
+  value as in the caller.
+
+  Except: If the register being saved is the CFA register, and the
+  offset is nonzero, we are saving the CFA, so we assume we have to
+  use DW_CFA_def_cfa_expression.  If the offset is 0, we assume that
+  the intent is to save the value of SP from the previous frame.
+
+  In addition, if a register has previously been saved to a different
+  register,
+
+  Invariants / Summaries of Rules
+
+  cfa	       current rule for calculating the CFA.  It usually
+	       consists of a register and an offset.
+  cfa_store    register used by prologue code to save things to the stack
+	       cfa_store.offset is the offset from the value of
+	       cfa_store.reg to the actual CFA
+  cfa_temp     register holding an integral value.  cfa_temp.offset
+	       stores the value, which will be used to adjust the
+	       stack pointer.  cfa_temp is also used like cfa_store,
+	       to track stores to the stack via fp or a temp reg.
+
+  Rules  1- 4: Setting a register's value to cfa.reg or an expression
+	       with cfa.reg as the first operand changes the cfa.reg and its
+	       cfa.offset.  Rule 1 and 4 also set cfa_temp.reg and
+	       cfa_temp.offset.
+
+  Rules  6- 9: Set a non-cfa.reg register value to a constant or an
+	       expression yielding a constant.  This sets cfa_temp.reg
+	       and cfa_temp.offset.
+
+  Rule 5:      Create a new register cfa_store used to save items to the
+	       stack.
+
+  Rules 10-14: Save a register to the stack.  Define offset as the
+	       difference of the original location and cfa_store's
+	       location (or cfa_temp's location if cfa_temp is used).
+
+  Rules 16-20: If AND operation happens on sp in prologue, we assume
+	       stack is realigned.  We will use a group of DW_OP_XXX
+	       expressions to represent the location of the stored
+	       register instead of CFA+offset.
+
+  The Rules
+
+  "{a,b}" indicates a choice of a xor b.
+  "<reg>:cfa.reg" indicates that <reg> must equal cfa.reg.
+
+  Rule 1:
+  (set <reg1> <reg2>:cfa.reg)
+  effects: cfa.reg = <reg1>
+	   cfa.offset unchanged
+	   cfa_temp.reg = <reg1>
+	   cfa_temp.offset = cfa.offset
+
+  Rule 2:
+  (set sp ({minus,plus,losum} {sp,fp}:cfa.reg
+			      {<const_int>,<reg>:cfa_temp.reg}))
+  effects: cfa.reg = sp if fp used
+	   cfa.offset += {+/- <const_int>, cfa_temp.offset} if cfa.reg==sp
+	   cfa_store.offset += {+/- <const_int>, cfa_temp.offset}
+	     if cfa_store.reg==sp
+
+  Rule 3:
+  (set fp ({minus,plus,losum} <reg>:cfa.reg <const_int>))
+  effects: cfa.reg = fp
+	   cfa_offset += +/- <const_int>
+
+  Rule 4:
+  (set <reg1> ({plus,losum} <reg2>:cfa.reg <const_int>))
+  constraints: <reg1> != fp
+	       <reg1> != sp
+  effects: cfa.reg = <reg1>
+	   cfa_temp.reg = <reg1>
+	   cfa_temp.offset = cfa.offset
+
+  Rule 5:
+  (set <reg1> (plus <reg2>:cfa_temp.reg sp:cfa.reg))
+  constraints: <reg1> != fp
+	       <reg1> != sp
+  effects: cfa_store.reg = <reg1>
+	   cfa_store.offset = cfa.offset - cfa_temp.offset
+
+  Rule 6:
+  (set <reg> <const_int>)
+  effects: cfa_temp.reg = <reg>
+	   cfa_temp.offset = <const_int>
+
+  Rule 7:
+  (set <reg1>:cfa_temp.reg (ior <reg2>:cfa_temp.reg <const_int>))
+  effects: cfa_temp.reg = <reg1>
+	   cfa_temp.offset |= <const_int>
+
+  Rule 8:
+  (set <reg> (high <exp>))
+  effects: none
+
+  Rule 9:
+  (set <reg> (lo_sum <exp> <const_int>))
+  effects: cfa_temp.reg = <reg>
+	   cfa_temp.offset = <const_int>
+
+  Rule 10:
+  (set (mem (pre_modify sp:cfa_store (???? <reg1> <const_int>))) <reg2>)
+  effects: cfa_store.offset -= <const_int>
+	   cfa.offset = cfa_store.offset if cfa.reg == sp
+	   cfa.reg = sp
+	   cfa.base_offset = -cfa_store.offset
+
+  Rule 11:
+  (set (mem ({pre_inc,pre_dec} sp:cfa_store.reg)) <reg>)
+  effects: cfa_store.offset += -/+ mode_size(mem)
+	   cfa.offset = cfa_store.offset if cfa.reg == sp
+	   cfa.reg = sp
+	   cfa.base_offset = -cfa_store.offset
+
+  Rule 12:
+  (set (mem ({minus,plus,losum} <reg1>:{cfa_store,cfa_temp} <const_int>))
+
+       <reg2>)
+  effects: cfa.reg = <reg1>
+	   cfa.base_offset = -/+ <const_int> - {cfa_store,cfa_temp}.offset
+
+  Rule 13:
+  (set (mem <reg1>:{cfa_store,cfa_temp}) <reg2>)
+  effects: cfa.reg = <reg1>
+	   cfa.base_offset = -{cfa_store,cfa_temp}.offset
+
+  Rule 14:
+  (set (mem (postinc <reg1>:cfa_temp <const_int>)) <reg2>)
+  effects: cfa.reg = <reg1>
+	   cfa.base_offset = -cfa_temp.offset
+	   cfa_temp.offset -= mode_size(mem)
+
+  Rule 15:
+  (set <reg> {unspec, unspec_volatile})
+  effects: target-dependent
+
+  Rule 16:
+  (set sp (and: sp <const_int>))
+  constraints: cfa_store.reg == sp
+  effects: current_fde.stack_realign = 1
+           cfa_store.offset = 0
+	   fde->drap_reg = cfa.reg if cfa.reg != sp and cfa.reg != fp
+
+  Rule 17:
+  (set (mem ({pre_inc, pre_dec} sp)) (mem (plus (cfa.reg) (const_int))))
+  effects: cfa_store.offset += -/+ mode_size(mem)
+
+  Rule 18:
+  (set (mem ({pre_inc, pre_dec} sp)) fp)
+  constraints: fde->stack_realign == 1
+  effects: cfa_store.offset = 0
+	   cfa.reg != HARD_FRAME_POINTER_REGNUM
+
+  Rule 19:
+  (set (mem ({pre_inc, pre_dec} sp)) cfa.reg)
+  constraints: fde->stack_realign == 1
+               && cfa.offset == 0
+               && cfa.indirect == 0
+               && cfa.reg != HARD_FRAME_POINTER_REGNUM
+  effects: Use DW_CFA_def_cfa_expression to define cfa
+  	   cfa.reg == fde->drap_reg
+
+  Rule 20:
+  (set reg fde->drap_reg)
+  constraints: fde->vdrap_reg == INVALID_REGNUM
+  effects: fde->vdrap_reg = reg.
+  (set mem fde->drap_reg)
+  constraints: fde->drap_reg_saved == 1
+  effects: none.  */
+
+static void
+dwarf2out_frame_debug_expr (rtx expr, const char *label)
+{
+  rtx src, dest, span;
+  HOST_WIDE_INT offset;
+  dw_fde_ref fde;
+
+  /* If RTX_FRAME_RELATED_P is set on a PARALLEL, process each member of
+     the PARALLEL independently. The first element is always processed if
+     it is a SET. This is for backward compatibility.   Other elements
+     are processed only if they are SETs and the RTX_FRAME_RELATED_P
+     flag is set in them.  */
+  if (GET_CODE (expr) == PARALLEL || GET_CODE (expr) == SEQUENCE)
+    {
+      int par_index;
+      int limit = XVECLEN (expr, 0);
+      rtx elem;
+
+      /* PARALLELs have strict read-modify-write semantics, so we
+	 ought to evaluate every rvalue before changing any lvalue.
+	 It's cumbersome to do that in general, but there's an
+	 easy approximation that is enough for all current users:
+	 handle register saves before register assignments.  */
+      if (GET_CODE (expr) == PARALLEL)
+	for (par_index = 0; par_index < limit; par_index++)
+	  {
+	    elem = XVECEXP (expr, 0, par_index);
+	    if (GET_CODE (elem) == SET
+		&& MEM_P (SET_DEST (elem))
+		&& (RTX_FRAME_RELATED_P (elem) || par_index == 0))
+	      dwarf2out_frame_debug_expr (elem, label);
+	  }
+
+      for (par_index = 0; par_index < limit; par_index++)
+	{
+	  elem = XVECEXP (expr, 0, par_index);
+	  if (GET_CODE (elem) == SET
+	      && (!MEM_P (SET_DEST (elem)) || GET_CODE (expr) == SEQUENCE)
+	      && (RTX_FRAME_RELATED_P (elem) || par_index == 0))
+	    dwarf2out_frame_debug_expr (elem, label);
+	  else if (GET_CODE (elem) == SET
+		   && par_index != 0
+		   && !RTX_FRAME_RELATED_P (elem))
+	    {
+	      /* Stack adjustment combining might combine some post-prologue
+		 stack adjustment into a prologue stack adjustment.  */
+	      HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0);
+
+	      if (offset != 0)
+		dwarf2out_args_size_adjust (offset, label);
+	    }
+	}
+      return;
+    }
+
+  gcc_assert (GET_CODE (expr) == SET);
+
+  src = SET_SRC (expr);
+  dest = SET_DEST (expr);
+
+  if (REG_P (src))
+    {
+      rtx rsi = reg_saved_in (src);
+      if (rsi)
+	src = rsi;
+    }
+
+  fde = current_fde ();
+
+  if (GET_CODE (src) == REG
+      && fde
+      && fde->drap_reg == REGNO (src)
+      && (fde->drap_reg_saved
+	  || GET_CODE (dest) == REG))
+    {
+      /* Rule 20 */
+      /* If we are saving dynamic realign argument pointer to a
+	 register, the destination is virtual dynamic realign
+	 argument pointer.  It may be used to access argument.  */
+      if (GET_CODE (dest) == REG)
+	{
+	  gcc_assert (fde->vdrap_reg == INVALID_REGNUM);
+	  fde->vdrap_reg = REGNO (dest);
+	}
+      return;
+    }
+
+  switch (GET_CODE (dest))
+    {
+    case REG:
+      switch (GET_CODE (src))
+	{
+	  /* Setting FP from SP.  */
+	case REG:
+	  if (cfa.reg == (unsigned) REGNO (src))
+	    {
+	      /* Rule 1 */
+	      /* Update the CFA rule wrt SP or FP.  Make sure src is
+		 relative to the current CFA register.
+
+		 We used to require that dest be either SP or FP, but the
+		 ARM copies SP to a temporary register, and from there to
+		 FP.  So we just rely on the backends to only set
+		 RTX_FRAME_RELATED_P on appropriate insns.  */
+	      cfa.reg = REGNO (dest);
+	      cfa_temp.reg = cfa.reg;
+	      cfa_temp.offset = cfa.offset;
+	    }
+	  else
+	    {
+	      /* Saving a register in a register.  */
+	      gcc_assert (!fixed_regs [REGNO (dest)]
+			  /* For the SPARC and its register window.  */
+			  || (DWARF_FRAME_REGNUM (REGNO (src))
+			      == DWARF_FRAME_RETURN_COLUMN));
+
+              /* After stack is aligned, we can only save SP in FP
+		 if drap register is used.  In this case, we have
+		 to restore stack pointer with the CFA value and we
+		 don't generate this DWARF information.  */
+	      if (fde
+		  && fde->stack_realign
+		  && REGNO (src) == STACK_POINTER_REGNUM)
+		gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
+			    && fde->drap_reg != INVALID_REGNUM
+			    && cfa.reg != REGNO (src));
+	      else
+		queue_reg_save (label, src, dest, 0);
+	    }
+	  break;
+
+	case PLUS:
+	case MINUS:
+	case LO_SUM:
+	  if (dest == stack_pointer_rtx)
+	    {
+	      /* Rule 2 */
+	      /* Adjusting SP.  */
+	      switch (GET_CODE (XEXP (src, 1)))
+		{
+		case CONST_INT:
+		  offset = INTVAL (XEXP (src, 1));
+		  break;
+		case REG:
+		  gcc_assert ((unsigned) REGNO (XEXP (src, 1))
+			      == cfa_temp.reg);
+		  offset = cfa_temp.offset;
+		  break;
+		default:
+		  gcc_unreachable ();
+		}
+
+	      if (XEXP (src, 0) == hard_frame_pointer_rtx)
+		{
+		  /* Restoring SP from FP in the epilogue.  */
+		  gcc_assert (cfa.reg == (unsigned) HARD_FRAME_POINTER_REGNUM);
+		  cfa.reg = STACK_POINTER_REGNUM;
+		}
+	      else if (GET_CODE (src) == LO_SUM)
+		/* Assume we've set the source reg of the LO_SUM from sp.  */
+		;
+	      else
+		gcc_assert (XEXP (src, 0) == stack_pointer_rtx);
+
+	      if (GET_CODE (src) != MINUS)
+		offset = -offset;
+	      if (cfa.reg == STACK_POINTER_REGNUM)
+		cfa.offset += offset;
+	      if (cfa_store.reg == STACK_POINTER_REGNUM)
+		cfa_store.offset += offset;
+	    }
+	  else if (dest == hard_frame_pointer_rtx)
+	    {
+	      /* Rule 3 */
+	      /* Either setting the FP from an offset of the SP,
+		 or adjusting the FP */
+	      gcc_assert (frame_pointer_needed);
+
+	      gcc_assert (REG_P (XEXP (src, 0))
+			  && (unsigned) REGNO (XEXP (src, 0)) == cfa.reg
+			  && GET_CODE (XEXP (src, 1)) == CONST_INT);
+	      offset = INTVAL (XEXP (src, 1));
+	      if (GET_CODE (src) != MINUS)
+		offset = -offset;
+	      cfa.offset += offset;
+	      cfa.reg = HARD_FRAME_POINTER_REGNUM;
+	    }
+	  else
+	    {
+	      gcc_assert (GET_CODE (src) != MINUS);
+
+	      /* Rule 4 */
+	      if (REG_P (XEXP (src, 0))
+		  && REGNO (XEXP (src, 0)) == cfa.reg
+		  && GET_CODE (XEXP (src, 1)) == CONST_INT)
+		{
+		  /* Setting a temporary CFA register that will be copied
+		     into the FP later on.  */
+		  offset = - INTVAL (XEXP (src, 1));
+		  cfa.offset += offset;
+		  cfa.reg = REGNO (dest);
+		  /* Or used to save regs to the stack.  */
+		  cfa_temp.reg = cfa.reg;
+		  cfa_temp.offset = cfa.offset;
+		}
+
+	      /* Rule 5 */
+	      else if (REG_P (XEXP (src, 0))
+		       && REGNO (XEXP (src, 0)) == cfa_temp.reg
+		       && XEXP (src, 1) == stack_pointer_rtx)
+		{
+		  /* Setting a scratch register that we will use instead
+		     of SP for saving registers to the stack.  */
+		  gcc_assert (cfa.reg == STACK_POINTER_REGNUM);
+		  cfa_store.reg = REGNO (dest);
+		  cfa_store.offset = cfa.offset - cfa_temp.offset;
+		}
+
+	      /* Rule 9 */
+	      else if (GET_CODE (src) == LO_SUM
+		       && GET_CODE (XEXP (src, 1)) == CONST_INT)
+		{
+		  cfa_temp.reg = REGNO (dest);
+		  cfa_temp.offset = INTVAL (XEXP (src, 1));
+		}
+	      else
+		gcc_unreachable ();
+	    }
+	  break;
+
+	  /* Rule 6 */
+	case CONST_INT:
+	  cfa_temp.reg = REGNO (dest);
+	  cfa_temp.offset = INTVAL (src);
+	  break;
+
+	  /* Rule 7 */
+	case IOR:
+	  gcc_assert (REG_P (XEXP (src, 0))
+		      && (unsigned) REGNO (XEXP (src, 0)) == cfa_temp.reg
+		      && GET_CODE (XEXP (src, 1)) == CONST_INT);
+
+	  if ((unsigned) REGNO (dest) != cfa_temp.reg)
+	    cfa_temp.reg = REGNO (dest);
+	  cfa_temp.offset |= INTVAL (XEXP (src, 1));
+	  break;
+
+	  /* Skip over HIGH, assuming it will be followed by a LO_SUM,
+	     which will fill in all of the bits.  */
+	  /* Rule 8 */
+	case HIGH:
+	  break;
+
+	  /* Rule 15 */
+	case UNSPEC:
+	case UNSPEC_VOLATILE:
+	  gcc_assert (targetm.dwarf_handle_frame_unspec);
+	  targetm.dwarf_handle_frame_unspec (label, expr, XINT (src, 1));
+	  return;
+
+	  /* Rule 16 */
+	case AND:
+          /* If this AND operation happens on stack pointer in prologue,
+	     we assume the stack is realigned and we extract the
+	     alignment.  */
+          if (fde && XEXP (src, 0) == stack_pointer_rtx)
+            {
+              gcc_assert (cfa_store.reg == REGNO (XEXP (src, 0)));
+              fde->stack_realign = 1;
+              fde->stack_realignment = INTVAL (XEXP (src, 1));
+              cfa_store.offset = 0;
+
+	      if (cfa.reg != STACK_POINTER_REGNUM
+		  && cfa.reg != HARD_FRAME_POINTER_REGNUM)
+		fde->drap_reg = cfa.reg;
+            }
+          return;
+
+	default:
+	  gcc_unreachable ();
+	}
+
+      def_cfa_1 (label, &cfa);
+      break;
+
+    case MEM:
+
+      /* Saving a register to the stack.  Make sure dest is relative to the
+	 CFA register.  */
+      switch (GET_CODE (XEXP (dest, 0)))
+	{
+	  /* Rule 10 */
+	  /* With a push.  */
+	case PRE_MODIFY:
+	  /* We can't handle variable size modifications.  */
+	  gcc_assert (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1))
+		      == CONST_INT);
+	  offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1));
+
+	  gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM
+		      && cfa_store.reg == STACK_POINTER_REGNUM);
+
+	  cfa_store.offset += offset;
+	  if (cfa.reg == STACK_POINTER_REGNUM)
+	    cfa.offset = cfa_store.offset;
+
+	  offset = -cfa_store.offset;
+	  break;
+
+	  /* Rule 11 */
+	case PRE_INC:
+	case PRE_DEC:
+	  offset = GET_MODE_SIZE (GET_MODE (dest));
+	  if (GET_CODE (XEXP (dest, 0)) == PRE_INC)
+	    offset = -offset;
+
+	  gcc_assert ((REGNO (XEXP (XEXP (dest, 0), 0))
+		       == STACK_POINTER_REGNUM)
+		      && cfa_store.reg == STACK_POINTER_REGNUM);
+
+	  cfa_store.offset += offset;
+
+          /* Rule 18: If stack is aligned, we will use FP as a
+	     reference to represent the address of the stored
+	     regiser.  */
+          if (fde
+              && fde->stack_realign
+              && src == hard_frame_pointer_rtx)
+	    {
+	      gcc_assert (cfa.reg != HARD_FRAME_POINTER_REGNUM);
+	      cfa_store.offset = 0;
+	    }
+
+	  if (cfa.reg == STACK_POINTER_REGNUM)
+	    cfa.offset = cfa_store.offset;
+
+	  offset = -cfa_store.offset;
+	  break;
+
+	  /* Rule 12 */
+	  /* With an offset.  */
+	case PLUS:
+	case MINUS:
+	case LO_SUM:
+	  {
+	    int regno;
+
+	    gcc_assert (GET_CODE (XEXP (XEXP (dest, 0), 1)) == CONST_INT
+			&& REG_P (XEXP (XEXP (dest, 0), 0)));
+	    offset = INTVAL (XEXP (XEXP (dest, 0), 1));
+	    if (GET_CODE (XEXP (dest, 0)) == MINUS)
+	      offset = -offset;
+
+	    regno = REGNO (XEXP (XEXP (dest, 0), 0));
+
+	    if (cfa_store.reg == (unsigned) regno)
+	      offset -= cfa_store.offset;
+	    else
+	      {
+		gcc_assert (cfa_temp.reg == (unsigned) regno);
+		offset -= cfa_temp.offset;
+	      }
+	  }
+	  break;
+
+	  /* Rule 13 */
+	  /* Without an offset.  */
+	case REG:
+	  {
+	    int regno = REGNO (XEXP (dest, 0));
+
+	    if (cfa_store.reg == (unsigned) regno)
+	      offset = -cfa_store.offset;
+	    else
+	      {
+		gcc_assert (cfa_temp.reg == (unsigned) regno);
+		offset = -cfa_temp.offset;
+	      }
+	  }
+	  break;
+
+	  /* Rule 14 */
+	case POST_INC:
+	  gcc_assert (cfa_temp.reg
+		      == (unsigned) REGNO (XEXP (XEXP (dest, 0), 0)));
+	  offset = -cfa_temp.offset;
+	  cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest));
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+
+        /* Rule 17 */
+        /* If the source operand of this MEM operation is not a
+	   register, basically the source is return address.  Here
+	   we only care how much stack grew and we don't save it.  */
+      if (!REG_P (src))
+        break;
+
+      if (REGNO (src) != STACK_POINTER_REGNUM
+	  && REGNO (src) != HARD_FRAME_POINTER_REGNUM
+	  && (unsigned) REGNO (src) == cfa.reg)
+	{
+	  /* We're storing the current CFA reg into the stack.  */
+
+	  if (cfa.offset == 0)
+	    {
+              /* Rule 19 */
+              /* If stack is aligned, putting CFA reg into stack means
+		 we can no longer use reg + offset to represent CFA.
+		 Here we use DW_CFA_def_cfa_expression instead.  The
+		 result of this expression equals to the original CFA
+		 value.  */
+              if (fde
+                  && fde->stack_realign
+                  && cfa.indirect == 0
+                  && cfa.reg != HARD_FRAME_POINTER_REGNUM)
+                {
+		  dw_cfa_location cfa_exp;
+
+		  gcc_assert (fde->drap_reg == cfa.reg);
+
+		  cfa_exp.indirect = 1;
+		  cfa_exp.reg = HARD_FRAME_POINTER_REGNUM;
+		  cfa_exp.base_offset = offset;
+		  cfa_exp.offset = 0;
+
+		  fde->drap_reg_saved = 1;
+
+		  def_cfa_1 (label, &cfa_exp);
+		  break;
+                }
+
+	      /* If the source register is exactly the CFA, assume
+		 we're saving SP like any other register; this happens
+		 on the ARM.  */
+	      def_cfa_1 (label, &cfa);
+	      queue_reg_save (label, stack_pointer_rtx, NULL_RTX, offset);
+	      break;
+	    }
+	  else
+	    {
+	      /* Otherwise, we'll need to look in the stack to
+		 calculate the CFA.  */
+	      rtx x = XEXP (dest, 0);
+
+	      if (!REG_P (x))
+		x = XEXP (x, 0);
+	      gcc_assert (REG_P (x));
+
+	      cfa.reg = REGNO (x);
+	      cfa.base_offset = offset;
+	      cfa.indirect = 1;
+	      def_cfa_1 (label, &cfa);
+	      break;
+	    }
+	}
+
+      def_cfa_1 (label, &cfa);
+      {
+	span = targetm.dwarf_register_span (src);
+
+	if (!span)
+	  queue_reg_save (label, src, NULL_RTX, offset);
+	else
+	  {
+	    /* We have a PARALLEL describing where the contents of SRC
+	       live.  Queue register saves for each piece of the
+	       PARALLEL.  */
+	    int par_index;
+	    int limit;
+	    HOST_WIDE_INT span_offset = offset;
+
+	    gcc_assert (GET_CODE (span) == PARALLEL);
+
+	    limit = XVECLEN (span, 0);
+	    for (par_index = 0; par_index < limit; par_index++)
+	      {
+		rtx elem = XVECEXP (span, 0, par_index);
+
+		queue_reg_save (label, elem, NULL_RTX, span_offset);
+		span_offset += GET_MODE_SIZE (GET_MODE (elem));
+	      }
+	  }
+      }
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Record call frame debugging information for INSN, which either
+   sets SP or FP (adjusting how we calculate the frame address) or saves a
+   register to the stack.  If INSN is NULL_RTX, initialize our state.
+
+   If AFTER_P is false, we're being called before the insn is emitted,
+   otherwise after.  Call instructions get invoked twice.  */
+
+void
+dwarf2out_frame_debug (rtx insn, bool after_p)
+{
+  const char *label;
+  rtx src;
+
+  if (insn == NULL_RTX)
+    {
+      size_t i;
+
+      /* Flush any queued register saves.  */
+      flush_queued_reg_saves ();
+
+      /* Set up state for generating call frame debug info.  */
+      lookup_cfa (&cfa);
+      gcc_assert (cfa.reg
+		  == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM));
+
+      cfa.reg = STACK_POINTER_REGNUM;
+      cfa_store = cfa;
+      cfa_temp.reg = -1;
+      cfa_temp.offset = 0;
+
+      for (i = 0; i < num_regs_saved_in_regs; i++)
+	{
+	  regs_saved_in_regs[i].orig_reg = NULL_RTX;
+	  regs_saved_in_regs[i].saved_in_reg = NULL_RTX;
+	}
+      num_regs_saved_in_regs = 0;
+
+      if (barrier_args_size)
+	{
+	  XDELETEVEC (barrier_args_size);
+	  barrier_args_size = NULL;
+	}
+      return;
+    }
+
+  if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
+    flush_queued_reg_saves ();
+
+  if (! RTX_FRAME_RELATED_P (insn))
+    {
+      if (!ACCUMULATE_OUTGOING_ARGS)
+	dwarf2out_stack_adjust (insn, after_p);
+      return;
+    }
+
+  label = dwarf2out_cfi_label ();
+  src = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+  if (src)
+    insn = XEXP (src, 0);
+  else
+    insn = PATTERN (insn);
+
+  dwarf2out_frame_debug_expr (insn, label);
+}
+
+#endif
+
+/* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used.  */
+static enum dw_cfi_oprnd_type dw_cfi_oprnd1_desc
+ (enum dwarf_call_frame_info cfi);
+
+static enum dw_cfi_oprnd_type
+dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi)
+{
+  switch (cfi)
+    {
+    case DW_CFA_nop:
+    case DW_CFA_GNU_window_save:
+      return dw_cfi_oprnd_unused;
+
+    case DW_CFA_set_loc:
+    case DW_CFA_advance_loc1:
+    case DW_CFA_advance_loc2:
+    case DW_CFA_advance_loc4:
+    case DW_CFA_MIPS_advance_loc8:
+      return dw_cfi_oprnd_addr;
+
+    case DW_CFA_offset:
+    case DW_CFA_offset_extended:
+    case DW_CFA_def_cfa:
+    case DW_CFA_offset_extended_sf:
+    case DW_CFA_def_cfa_sf:
+    case DW_CFA_restore_extended:
+    case DW_CFA_undefined:
+    case DW_CFA_same_value:
+    case DW_CFA_def_cfa_register:
+    case DW_CFA_register:
+      return dw_cfi_oprnd_reg_num;
+
+    case DW_CFA_def_cfa_offset:
+    case DW_CFA_GNU_args_size:
+    case DW_CFA_def_cfa_offset_sf:
+      return dw_cfi_oprnd_offset;
+
+    case DW_CFA_def_cfa_expression:
+    case DW_CFA_expression:
+      return dw_cfi_oprnd_loc;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used.  */
+static enum dw_cfi_oprnd_type dw_cfi_oprnd2_desc
+ (enum dwarf_call_frame_info cfi);
+
+static enum dw_cfi_oprnd_type
+dw_cfi_oprnd2_desc (enum dwarf_call_frame_info cfi)
+{
+  switch (cfi)
+    {
+    case DW_CFA_def_cfa:
+    case DW_CFA_def_cfa_sf:
+    case DW_CFA_offset:
+    case DW_CFA_offset_extended_sf:
+    case DW_CFA_offset_extended:
+      return dw_cfi_oprnd_offset;
+
+    case DW_CFA_register:
+      return dw_cfi_oprnd_reg_num;
+
+    default:
+      return dw_cfi_oprnd_unused;
+    }
+}
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+
+/* Switch to eh_frame_section.  If we don't have an eh_frame_section,
+   switch to the data section instead, and write out a synthetic label
+   for collect2.  */
+
+static void
+switch_to_eh_frame_section (void)
+{
+  tree label;
+
+#ifdef EH_FRAME_SECTION_NAME
+  if (eh_frame_section == 0)
+    {
+      int flags;
+
+      if (EH_TABLES_CAN_BE_READ_ONLY)
+	{
+	  int fde_encoding;
+	  int per_encoding;
+	  int lsda_encoding;
+
+	  fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1,
+						       /*global=*/0);
+	  per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,
+						       /*global=*/1);
+	  lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,
+							/*global=*/0);
+	  flags = ((! flag_pic
+		    || ((fde_encoding & 0x70) != DW_EH_PE_absptr
+			&& (fde_encoding & 0x70) != DW_EH_PE_aligned
+			&& (per_encoding & 0x70) != DW_EH_PE_absptr
+			&& (per_encoding & 0x70) != DW_EH_PE_aligned
+			&& (lsda_encoding & 0x70) != DW_EH_PE_absptr
+			&& (lsda_encoding & 0x70) != DW_EH_PE_aligned))
+		   ? 0 : SECTION_WRITE);
+	}
+      else
+	flags = SECTION_WRITE;
+      eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL);
+    }
+#endif
+
+  if (eh_frame_section)
+    switch_to_section (eh_frame_section);
+  else
+    {
+      /* We have no special eh_frame section.  Put the information in
+	 the data section and emit special labels to guide collect2.  */
+      switch_to_section (data_section);
+      label = get_file_function_name ("F");
+      ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE));
+      targetm.asm_out.globalize_label (asm_out_file,
+				       IDENTIFIER_POINTER (label));
+      ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label));
+    }
+}
+
+/* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder.  */
+
+static HOST_WIDE_INT
+div_data_align (HOST_WIDE_INT off)
+{
+  HOST_WIDE_INT r = off / DWARF_CIE_DATA_ALIGNMENT;
+  gcc_assert (r * DWARF_CIE_DATA_ALIGNMENT == off);
+  return r;
+}
+
+/* Output a Call Frame Information opcode and its operand(s).  */
+
+static void
+output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int for_eh)
+{
+  unsigned long r;
+  HOST_WIDE_INT off;
+
+  if (cfi->dw_cfi_opc == DW_CFA_advance_loc)
+    dw2_asm_output_data (1, (cfi->dw_cfi_opc
+			     | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)),
+			 "DW_CFA_advance_loc " HOST_WIDE_INT_PRINT_HEX,
+			 ((unsigned HOST_WIDE_INT)
+			  cfi->dw_cfi_oprnd1.dw_cfi_offset));
+  else if (cfi->dw_cfi_opc == DW_CFA_offset)
+    {
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+      dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)),
+			   "DW_CFA_offset, column 0x%lx", r);
+      off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset);
+      dw2_asm_output_data_uleb128 (off, NULL);
+    }
+  else if (cfi->dw_cfi_opc == DW_CFA_restore)
+    {
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+      dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)),
+			   "DW_CFA_restore, column 0x%lx", r);
+    }
+  else
+    {
+      dw2_asm_output_data (1, cfi->dw_cfi_opc,
+			   "%s", dwarf_cfi_name (cfi->dw_cfi_opc));
+
+      switch (cfi->dw_cfi_opc)
+	{
+	case DW_CFA_set_loc:
+	  if (for_eh)
+	    dw2_asm_output_encoded_addr_rtx (
+		ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0),
+		gen_rtx_SYMBOL_REF (Pmode, cfi->dw_cfi_oprnd1.dw_cfi_addr),
+		false, NULL);
+	  else
+	    dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+				 cfi->dw_cfi_oprnd1.dw_cfi_addr, NULL);
+	  fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	  break;
+
+	case DW_CFA_advance_loc1:
+	  dw2_asm_output_delta (1, cfi->dw_cfi_oprnd1.dw_cfi_addr,
+				fde->dw_fde_current_label, NULL);
+	  fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	  break;
+
+	case DW_CFA_advance_loc2:
+	  dw2_asm_output_delta (2, cfi->dw_cfi_oprnd1.dw_cfi_addr,
+				fde->dw_fde_current_label, NULL);
+	  fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	  break;
+
+	case DW_CFA_advance_loc4:
+	  dw2_asm_output_delta (4, cfi->dw_cfi_oprnd1.dw_cfi_addr,
+				fde->dw_fde_current_label, NULL);
+	  fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	  break;
+
+	case DW_CFA_MIPS_advance_loc8:
+	  dw2_asm_output_delta (8, cfi->dw_cfi_oprnd1.dw_cfi_addr,
+				fde->dw_fde_current_label, NULL);
+	  fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	  break;
+
+	case DW_CFA_offset_extended:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset);
+	  dw2_asm_output_data_uleb128 (off, NULL);
+	  break;
+
+	case DW_CFA_def_cfa:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL);
+	  break;
+
+	case DW_CFA_offset_extended_sf:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset);
+	  dw2_asm_output_data_sleb128 (off, NULL);
+	  break;
+
+	case DW_CFA_def_cfa_sf:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset);
+	  dw2_asm_output_data_sleb128 (off, NULL);
+	  break;
+
+	case DW_CFA_restore_extended:
+	case DW_CFA_undefined:
+	case DW_CFA_same_value:
+	case DW_CFA_def_cfa_register:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  break;
+
+	case DW_CFA_register:
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, for_eh);
+	  dw2_asm_output_data_uleb128 (r, NULL);
+	  break;
+
+	case DW_CFA_def_cfa_offset:
+	case DW_CFA_GNU_args_size:
+	  dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset, NULL);
+	  break;
+
+	case DW_CFA_def_cfa_offset_sf:
+	  off = div_data_align (cfi->dw_cfi_oprnd1.dw_cfi_offset);
+	  dw2_asm_output_data_sleb128 (off, NULL);
+	  break;
+
+	case DW_CFA_GNU_window_save:
+	  break;
+
+	case DW_CFA_def_cfa_expression:
+	case DW_CFA_expression:
+	  output_cfa_loc (cfi);
+	  break;
+
+	case DW_CFA_GNU_negative_offset_extended:
+	  /* Obsoleted by DW_CFA_offset_extended_sf.  */
+	  gcc_unreachable ();
+
+	default:
+	  break;
+	}
+    }
+}
+
+/* Similar, but do it via assembler directives instead.  */
+
+static void
+output_cfi_directive (dw_cfi_ref cfi)
+{
+  unsigned long r, r2;
+
+  switch (cfi->dw_cfi_opc)
+    {
+    case DW_CFA_advance_loc:
+    case DW_CFA_advance_loc1:
+    case DW_CFA_advance_loc2:
+    case DW_CFA_advance_loc4:
+    case DW_CFA_MIPS_advance_loc8:
+    case DW_CFA_set_loc:
+      /* Should only be created by add_fde_cfi in a code path not
+	 followed when emitting via directives.  The assembler is
+	 going to take care of this for us.  */
+      gcc_unreachable ();
+
+    case DW_CFA_offset:
+    case DW_CFA_offset_extended:
+    case DW_CFA_offset_extended_sf:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n",
+	       r, cfi->dw_cfi_oprnd2.dw_cfi_offset);
+      break;
+
+    case DW_CFA_restore:
+    case DW_CFA_restore_extended:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_restore %lu\n", r);
+      break;
+
+    case DW_CFA_undefined:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_undefined %lu\n", r);
+      break;
+
+    case DW_CFA_same_value:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_same_value %lu\n", r);
+      break;
+
+    case DW_CFA_def_cfa:
+    case DW_CFA_def_cfa_sf:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n",
+	       r, cfi->dw_cfi_oprnd2.dw_cfi_offset);
+      break;
+
+    case DW_CFA_def_cfa_register:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_def_cfa_register %lu\n", r);
+      break;
+
+    case DW_CFA_register:
+      r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 0);
+      r2 = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, 0);
+      fprintf (asm_out_file, "\t.cfi_register %lu, %lu\n", r, r2);
+      break;
+
+    case DW_CFA_def_cfa_offset:
+    case DW_CFA_def_cfa_offset_sf:
+      fprintf (asm_out_file, "\t.cfi_def_cfa_offset "
+	       HOST_WIDE_INT_PRINT_DEC"\n",
+	       cfi->dw_cfi_oprnd1.dw_cfi_offset);
+      break;
+
+    case DW_CFA_GNU_args_size:
+      fprintf (asm_out_file, "\t.cfi_escape 0x%x,", DW_CFA_GNU_args_size);
+      dw2_asm_output_data_uleb128_raw (cfi->dw_cfi_oprnd1.dw_cfi_offset);
+      if (flag_debug_asm)
+	fprintf (asm_out_file, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC,
+		 ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset);
+      fputc ('\n', asm_out_file);
+      break;
+
+    case DW_CFA_GNU_window_save:
+      fprintf (asm_out_file, "\t.cfi_window_save\n");
+      break;
+
+    case DW_CFA_def_cfa_expression:
+    case DW_CFA_expression:
+      fprintf (asm_out_file, "\t.cfi_escape 0x%x,", cfi->dw_cfi_opc);
+      output_cfa_loc_raw (cfi);
+      fputc ('\n', asm_out_file);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Output the call frame information used to record information
+   that relates to calculating the frame pointer, and records the
+   location of saved registers.  */
+
+static void
+output_call_frame_info (int for_eh)
+{
+  unsigned int i;
+  dw_fde_ref fde;
+  dw_cfi_ref cfi;
+  char l1[20], l2[20], section_start_label[20];
+  bool any_lsda_needed = false;
+  char augmentation[6];
+  int augmentation_size;
+  int fde_encoding = DW_EH_PE_absptr;
+  int per_encoding = DW_EH_PE_absptr;
+  int lsda_encoding = DW_EH_PE_absptr;
+  int return_reg;
+
+  /* Don't emit a CIE if there won't be any FDEs.  */
+  if (fde_table_in_use == 0)
+    return;
+
+  /* Nothing to do if the assembler's doing it all.  */
+  if (dwarf2out_do_cfi_asm ())
+    return;
+
+  /* If we make FDEs linkonce, we may have to emit an empty label for
+     an FDE that wouldn't otherwise be emitted.  We want to avoid
+     having an FDE kept around when the function it refers to is
+     discarded.  Example where this matters: a primary function
+     template in C++ requires EH information, but an explicit
+     specialization doesn't.  */
+  if (TARGET_USES_WEAK_UNWIND_INFO
+      && ! flag_asynchronous_unwind_tables
+      && flag_exceptions
+      && for_eh)
+    for (i = 0; i < fde_table_in_use; i++)
+      if ((fde_table[i].nothrow || fde_table[i].all_throwers_are_sibcalls)
+	  && !fde_table[i].uses_eh_lsda
+	  && ! DECL_WEAK (fde_table[i].decl))
+	targetm.asm_out.unwind_label (asm_out_file, fde_table[i].decl,
+				      for_eh, /* empty */ 1);
+
+  /* If we don't have any functions we'll want to unwind out of, don't
+     emit any EH unwind information.  Note that if exceptions aren't
+     enabled, we won't have collected nothrow information, and if we
+     asked for asynchronous tables, we always want this info.  */
+  if (for_eh)
+    {
+      bool any_eh_needed = !flag_exceptions || flag_asynchronous_unwind_tables;
+
+      for (i = 0; i < fde_table_in_use; i++)
+	if (fde_table[i].uses_eh_lsda)
+	  any_eh_needed = any_lsda_needed = true;
+	else if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl))
+	  any_eh_needed = true;
+	else if (! fde_table[i].nothrow
+		 && ! fde_table[i].all_throwers_are_sibcalls)
+	  any_eh_needed = true;
+
+      if (! any_eh_needed)
+	return;
+    }
+
+  /* We're going to be generating comments, so turn on app.  */
+  if (flag_debug_asm)
+    app_enable ();
+
+  if (for_eh)
+    switch_to_eh_frame_section ();
+  else
+    {
+      if (!debug_frame_section)
+	debug_frame_section = get_section (DEBUG_FRAME_SECTION,
+					   SECTION_DEBUG, NULL);
+      switch_to_section (debug_frame_section);
+    }
+
+  ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh);
+  ASM_OUTPUT_LABEL (asm_out_file, section_start_label);
+
+  /* Output the CIE.  */
+  ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh);
+  ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh);
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1,
+			"Length of Common Information Entry");
+  ASM_OUTPUT_LABEL (asm_out_file, l1);
+
+  /* Now that the CIE pointer is PC-relative for EH,
+     use 0 to identify the CIE.  */
+  dw2_asm_output_data ((for_eh ? 4 : DWARF_OFFSET_SIZE),
+		       (for_eh ? 0 : DWARF_CIE_ID),
+		       "CIE Identifier Tag");
+
+  dw2_asm_output_data (1, DW_CIE_VERSION, "CIE Version");
+
+  augmentation[0] = 0;
+  augmentation_size = 0;
+  if (for_eh)
+    {
+      char *p;
+
+      /* Augmentation:
+	 z	Indicates that a uleb128 is present to size the
+		augmentation section.
+	 L	Indicates the encoding (and thus presence) of
+		an LSDA pointer in the FDE augmentation.
+	 R	Indicates a non-default pointer encoding for
+		FDE code pointers.
+	 P	Indicates the presence of an encoding + language
+		personality routine in the CIE augmentation.  */
+
+      fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0);
+      per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1);
+      lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0);
+
+      p = augmentation + 1;
+      if (eh_personality_libfunc)
+	{
+	  *p++ = 'P';
+	  augmentation_size += 1 + size_of_encoded_value (per_encoding);
+	  assemble_external_libcall (eh_personality_libfunc);
+	}
+      if (any_lsda_needed)
+	{
+	  *p++ = 'L';
+	  augmentation_size += 1;
+	}
+      if (fde_encoding != DW_EH_PE_absptr)
+	{
+	  *p++ = 'R';
+	  augmentation_size += 1;
+	}
+      if (p > augmentation + 1)
+	{
+	  augmentation[0] = 'z';
+	  *p = '\0';
+	}
+
+      /* Ug.  Some platforms can't do unaligned dynamic relocations at all.  */
+      if (eh_personality_libfunc && per_encoding == DW_EH_PE_aligned)
+	{
+	  int offset = (  4		/* Length */
+			+ 4		/* CIE Id */
+			+ 1		/* CIE version */
+			+ strlen (augmentation) + 1	/* Augmentation */
+			+ size_of_uleb128 (1)		/* Code alignment */
+			+ size_of_sleb128 (DWARF_CIE_DATA_ALIGNMENT)
+			+ 1		/* RA column */
+			+ 1		/* Augmentation size */
+			+ 1		/* Personality encoding */ );
+	  int pad = -offset & (PTR_SIZE - 1);
+
+	  augmentation_size += pad;
+
+	  /* Augmentations should be small, so there's scarce need to
+	     iterate for a solution.  Die if we exceed one uleb128 byte.  */
+	  gcc_assert (size_of_uleb128 (augmentation_size) == 1);
+	}
+    }
+
+  dw2_asm_output_nstring (augmentation, -1, "CIE Augmentation");
+  dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor");
+  dw2_asm_output_data_sleb128 (DWARF_CIE_DATA_ALIGNMENT,
+			       "CIE Data Alignment Factor");
+
+  return_reg = DWARF2_FRAME_REG_OUT (DWARF_FRAME_RETURN_COLUMN, for_eh);
+  if (DW_CIE_VERSION == 1)
+    dw2_asm_output_data (1, return_reg, "CIE RA Column");
+  else
+    dw2_asm_output_data_uleb128 (return_reg, "CIE RA Column");
+
+  if (augmentation[0])
+    {
+      dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size");
+      if (eh_personality_libfunc)
+	{
+	  dw2_asm_output_data (1, per_encoding, "Personality (%s)",
+			       eh_data_format_name (per_encoding));
+	  dw2_asm_output_encoded_addr_rtx (per_encoding,
+					   eh_personality_libfunc,
+					   true, NULL);
+	}
+
+      if (any_lsda_needed)
+	dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)",
+			     eh_data_format_name (lsda_encoding));
+
+      if (fde_encoding != DW_EH_PE_absptr)
+	dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)",
+			     eh_data_format_name (fde_encoding));
+    }
+
+  for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next)
+    output_cfi (cfi, NULL, for_eh);
+
+  /* Pad the CIE out to an address sized boundary.  */
+  ASM_OUTPUT_ALIGN (asm_out_file,
+		    floor_log2 (for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE));
+  ASM_OUTPUT_LABEL (asm_out_file, l2);
+
+  /* Loop through all of the FDE's.  */
+  for (i = 0; i < fde_table_in_use; i++)
+    {
+      fde = &fde_table[i];
+
+      /* Don't emit EH unwind info for leaf functions that don't need it.  */
+      if (for_eh && !flag_asynchronous_unwind_tables && flag_exceptions
+	  && (fde->nothrow || fde->all_throwers_are_sibcalls)
+	  && ! (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl))
+	  && !fde->uses_eh_lsda)
+	continue;
+
+      targetm.asm_out.unwind_label (asm_out_file, fde->decl, for_eh, /* empty */ 0);
+      targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, for_eh + i * 2);
+      ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i * 2);
+      ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i * 2);
+      if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh)
+	dw2_asm_output_data (4, 0xffffffff,
+			     "Initial length escape value indicating 64-bit DWARF extension");
+      dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1,
+			    "FDE Length");
+      ASM_OUTPUT_LABEL (asm_out_file, l1);
+
+      if (for_eh)
+	dw2_asm_output_delta (4, l1, section_start_label, "FDE CIE offset");
+      else
+	dw2_asm_output_offset (DWARF_OFFSET_SIZE, section_start_label,
+			       debug_frame_section, "FDE CIE offset");
+
+      if (for_eh)
+	{
+	  if (fde->dw_fde_switched_sections)
+	    {
+	      rtx sym_ref2 = gen_rtx_SYMBOL_REF (Pmode,
+				      fde->dw_fde_unlikely_section_label);
+	      rtx sym_ref3= gen_rtx_SYMBOL_REF (Pmode,
+				      fde->dw_fde_hot_section_label);
+	      SYMBOL_REF_FLAGS (sym_ref2) |= SYMBOL_FLAG_LOCAL;
+	      SYMBOL_REF_FLAGS (sym_ref3) |= SYMBOL_FLAG_LOCAL;
+	      dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref3, false,
+					       "FDE initial location");
+	      dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+				    fde->dw_fde_hot_section_end_label,
+				    fde->dw_fde_hot_section_label,
+				    "FDE address range");
+	      dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref2, false,
+					       "FDE initial location");
+	      dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+				    fde->dw_fde_unlikely_section_end_label,
+				    fde->dw_fde_unlikely_section_label,
+				    "FDE address range");
+	    }
+	  else
+	    {
+	      rtx sym_ref = gen_rtx_SYMBOL_REF (Pmode, fde->dw_fde_begin);
+	      SYMBOL_REF_FLAGS (sym_ref) |= SYMBOL_FLAG_LOCAL;
+	      dw2_asm_output_encoded_addr_rtx (fde_encoding,
+					       sym_ref,
+					       false,
+					       "FDE initial location");
+	      dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
+				    fde->dw_fde_end, fde->dw_fde_begin,
+				    "FDE address range");
+	    }
+	}
+      else
+	{
+	  if (fde->dw_fde_switched_sections)
+	    {
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+				   fde->dw_fde_hot_section_label,
+				   "FDE initial location");
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+				    fde->dw_fde_hot_section_end_label,
+				    fde->dw_fde_hot_section_label,
+				    "FDE address range");
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+				   fde->dw_fde_unlikely_section_label,
+				   "FDE initial location");
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+				    fde->dw_fde_unlikely_section_end_label,
+				    fde->dw_fde_unlikely_section_label,
+				    "FDE address range");
+	    }
+	  else
+	    {
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin,
+				   "FDE initial location");
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+				    fde->dw_fde_end, fde->dw_fde_begin,
+				    "FDE address range");
+	    }
+	}
+
+      if (augmentation[0])
+	{
+	  if (any_lsda_needed)
+	    {
+	      int size = size_of_encoded_value (lsda_encoding);
+
+	      if (lsda_encoding == DW_EH_PE_aligned)
+		{
+		  int offset = (  4		/* Length */
+				+ 4		/* CIE offset */
+				+ 2 * size_of_encoded_value (fde_encoding)
+				+ 1		/* Augmentation size */ );
+		  int pad = -offset & (PTR_SIZE - 1);
+
+		  size += pad;
+		  gcc_assert (size_of_uleb128 (size) == 1);
+		}
+
+	      dw2_asm_output_data_uleb128 (size, "Augmentation size");
+
+	      if (fde->uses_eh_lsda)
+		{
+		  ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA",
+					       fde->funcdef_number);
+		  dw2_asm_output_encoded_addr_rtx (
+			lsda_encoding, gen_rtx_SYMBOL_REF (Pmode, l1),
+			false, "Language Specific Data Area");
+		}
+	      else
+		{
+		  if (lsda_encoding == DW_EH_PE_aligned)
+		    ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE));
+		  dw2_asm_output_data
+		    (size_of_encoded_value (lsda_encoding), 0,
+		     "Language Specific Data Area (none)");
+		}
+	    }
+	  else
+	    dw2_asm_output_data_uleb128 (0, "Augmentation size");
+	}
+
+      /* Loop through the Call Frame Instructions associated with
+	 this FDE.  */
+      fde->dw_fde_current_label = fde->dw_fde_begin;
+      for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
+	output_cfi (cfi, fde, for_eh);
+
+      /* Pad the FDE out to an address sized boundary.  */
+      ASM_OUTPUT_ALIGN (asm_out_file,
+			floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)));
+      ASM_OUTPUT_LABEL (asm_out_file, l2);
+    }
+
+  if (for_eh && targetm.terminate_dw2_eh_frame_info)
+    dw2_asm_output_data (4, 0, "End of Table");
+#ifdef MIPS_DEBUGGING_INFO
+  /* Work around Irix 6 assembler bug whereby labels at the end of a section
+     get a value of 0.  Putting .align 0 after the label fixes it.  */
+  ASM_OUTPUT_ALIGN (asm_out_file, 0);
+#endif
+
+  /* Turn off app to make assembly quicker.  */
+  if (flag_debug_asm)
+    app_disable ();
+}
+
+/* Output a marker (i.e. a label) for the beginning of a function, before
+   the prologue.  */
+
+void
+dwarf2out_begin_prologue (unsigned int line ATTRIBUTE_UNUSED,
+			  const char *file ATTRIBUTE_UNUSED)
+{
+  char label[MAX_ARTIFICIAL_LABEL_BYTES];
+  char * dup_label;
+  dw_fde_ref fde;
+
+  current_function_func_begin_label = NULL;
+
+#ifdef TARGET_UNWIND_INFO
+  /* ??? current_function_func_begin_label is also used by except.c
+     for call-site information.  We must emit this label if it might
+     be used.  */
+  if ((! flag_exceptions || USING_SJLJ_EXCEPTIONS)
+      && ! dwarf2out_do_frame ())
+    return;
+#else
+  if (! dwarf2out_do_frame ())
+    return;
+#endif
+
+  switch_to_section (function_section (current_function_decl));
+  ASM_GENERATE_INTERNAL_LABEL (label, FUNC_BEGIN_LABEL,
+			       current_function_funcdef_no);
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, FUNC_BEGIN_LABEL,
+			  current_function_funcdef_no);
+  dup_label = xstrdup (label);
+  current_function_func_begin_label = dup_label;
+
+#ifdef TARGET_UNWIND_INFO
+  /* We can elide the fde allocation if we're not emitting debug info.  */
+  if (! dwarf2out_do_frame ())
+    return;
+#endif
+
+  /* Expand the fde table if necessary.  */
+  if (fde_table_in_use == fde_table_allocated)
+    {
+      fde_table_allocated += FDE_TABLE_INCREMENT;
+      fde_table = GGC_RESIZEVEC (dw_fde_node, fde_table, fde_table_allocated);
+      memset (fde_table + fde_table_in_use, 0,
+	      FDE_TABLE_INCREMENT * sizeof (dw_fde_node));
+    }
+
+  /* Record the FDE associated with this function.  */
+  current_funcdef_fde = fde_table_in_use;
+
+  /* Add the new FDE at the end of the fde_table.  */
+  fde = &fde_table[fde_table_in_use++];
+  fde->decl = current_function_decl;
+  fde->dw_fde_begin = dup_label;
+  fde->dw_fde_current_label = dup_label;
+  fde->dw_fde_hot_section_label = NULL;
+  fde->dw_fde_hot_section_end_label = NULL;
+  fde->dw_fde_unlikely_section_label = NULL;
+  fde->dw_fde_unlikely_section_end_label = NULL;
+  fde->dw_fde_switched_sections = false;
+  fde->dw_fde_end = NULL;
+  fde->dw_fde_cfi = NULL;
+  fde->funcdef_number = current_function_funcdef_no;
+  fde->nothrow = TREE_NOTHROW (current_function_decl);
+  fde->uses_eh_lsda = crtl->uses_eh_lsda;
+  fde->all_throwers_are_sibcalls = crtl->all_throwers_are_sibcalls;
+  fde->drap_reg = INVALID_REGNUM;
+  fde->vdrap_reg = INVALID_REGNUM;
+
+  args_size = old_args_size = 0;
+
+  /* We only want to output line number information for the genuine dwarf2
+     prologue case, not the eh frame case.  */
+#ifdef DWARF2_DEBUGGING_INFO
+  if (file)
+    dwarf2out_source_line (line, file);
+#endif
+
+  if (dwarf2out_do_cfi_asm ())
+    {
+      int enc;
+      rtx ref;
+
+      fprintf (asm_out_file, "\t.cfi_startproc\n");
+
+      if (eh_personality_libfunc)
+	{
+	  enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); 
+	  ref = eh_personality_libfunc;
+
+	  /* ??? The GAS support isn't entirely consistent.  We have to
+	     handle indirect support ourselves, but PC-relative is done
+	     in the assembler.  Further, the assembler can't handle any
+	     of the weirder relocation types.  */
+	  if (enc & DW_EH_PE_indirect)
+	    ref = dw2_force_const_mem (ref, true);
+
+	  fprintf (asm_out_file, "\t.cfi_personality 0x%x,", enc);
+	  output_addr_const (asm_out_file, ref);
+	  fputc ('\n', asm_out_file);
+	}
+
+      if (crtl->uses_eh_lsda)
+	{
+	  char lab[20];
+
+	  enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0);
+	  ASM_GENERATE_INTERNAL_LABEL (lab, "LLSDA",
+				       current_function_funcdef_no);
+	  ref = gen_rtx_SYMBOL_REF (Pmode, lab);
+	  SYMBOL_REF_FLAGS (ref) = SYMBOL_FLAG_LOCAL;
+
+	  if (enc & DW_EH_PE_indirect)
+	    ref = dw2_force_const_mem (ref, true);
+
+	  fprintf (asm_out_file, "\t.cfi_lsda 0x%x,", enc);
+	  output_addr_const (asm_out_file, ref);
+	  fputc ('\n', asm_out_file);
+	}
+    }
+}
+
+/* Output a marker (i.e. a label) for the absolute end of the generated code
+   for a function definition.  This gets called *after* the epilogue code has
+   been generated.  */
+
+void
+dwarf2out_end_epilogue (unsigned int line ATTRIBUTE_UNUSED,
+			const char *file ATTRIBUTE_UNUSED)
+{
+  dw_fde_ref fde;
+  char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+  if (dwarf2out_do_cfi_asm ())
+    fprintf (asm_out_file, "\t.cfi_endproc\n");
+
+  /* Output a label to mark the endpoint of the code generated for this
+     function.  */
+  ASM_GENERATE_INTERNAL_LABEL (label, FUNC_END_LABEL,
+			       current_function_funcdef_no);
+  ASM_OUTPUT_LABEL (asm_out_file, label);
+  fde = current_fde ();
+  gcc_assert (fde != NULL);
+  fde->dw_fde_end = xstrdup (label);
+}
+
+void
+dwarf2out_frame_init (void)
+{
+  /* Allocate the initial hunk of the fde_table.  */
+  fde_table = GGC_CNEWVEC (dw_fde_node, FDE_TABLE_INCREMENT);
+  fde_table_allocated = FDE_TABLE_INCREMENT;
+  fde_table_in_use = 0;
+
+  /* Generate the CFA instructions common to all FDE's.  Do it now for the
+     sake of lookup_cfa.  */
+
+  /* On entry, the Canonical Frame Address is at SP.  */
+  dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET);
+
+#ifdef DWARF2_UNWIND_INFO
+  if (DWARF2_UNWIND_INFO || DWARF2_FRAME_INFO)
+    initial_return_save (INCOMING_RETURN_ADDR_RTX);
+#endif
+}
+
+void
+dwarf2out_frame_finish (void)
+{
+  /* Output call frame information.  */
+  if (DWARF2_FRAME_INFO)
+    output_call_frame_info (0);
+
+#ifndef TARGET_UNWIND_INFO
+  /* Output another copy for the unwinder.  */
+  if (! USING_SJLJ_EXCEPTIONS && (flag_unwind_tables || flag_exceptions))
+    output_call_frame_info (1);
+#endif
+}
+
+/* Note that the current function section is being used for code.  */
+
+static void
+dwarf2out_note_section_used (void)
+{
+  section *sec = current_function_section ();
+  if (sec == text_section)
+    text_section_used = true;
+  else if (sec == cold_text_section)
+    cold_text_section_used = true;
+}
+
+void
+dwarf2out_switch_text_section (void)
+{
+  dw_fde_ref fde = current_fde ();
+
+  gcc_assert (cfun && fde);
+
+  fde->dw_fde_switched_sections = true;
+  fde->dw_fde_hot_section_label = crtl->subsections.hot_section_label;
+  fde->dw_fde_hot_section_end_label = crtl->subsections.hot_section_end_label;
+  fde->dw_fde_unlikely_section_label = crtl->subsections.cold_section_label;
+  fde->dw_fde_unlikely_section_end_label = crtl->subsections.cold_section_end_label;
+  have_multiple_function_sections = true;
+
+  /* Reset the current label on switching text sections, so that we
+     don't attempt to advance_loc4 between labels in different sections.  */
+  fde->dw_fde_current_label = NULL;
+
+  /* There is no need to mark used sections when not debugging.  */
+  if (cold_text_section != NULL)
+    dwarf2out_note_section_used ();
+}
+#endif
+
+/* And now, the subset of the debugging information support code necessary
+   for emitting location expressions.  */
+
+/* Data about a single source file.  */
+struct dwarf_file_data GTY(())
+{
+  const char * filename;
+  int emitted_number;
+};
+
+/* We need some way to distinguish DW_OP_addr with a direct symbol
+   relocation from DW_OP_addr with a dtp-relative symbol relocation.  */
+#define INTERNAL_DW_OP_tls_addr		(0x100 + DW_OP_addr)
+
+
+typedef struct dw_val_struct *dw_val_ref;
+typedef struct die_struct *dw_die_ref;
+typedef const struct die_struct *const_dw_die_ref;
+typedef struct dw_loc_descr_struct *dw_loc_descr_ref;
+typedef struct dw_loc_list_struct *dw_loc_list_ref;
+
+typedef struct deferred_locations_struct GTY(()) 
+{
+  tree variable;
+  dw_die_ref die;
+} deferred_locations;
+
+DEF_VEC_O(deferred_locations);
+DEF_VEC_ALLOC_O(deferred_locations,gc);
+
+static GTY(()) VEC(deferred_locations, gc) *deferred_locations_list;
+
+/* Each DIE may have a series of attribute/value pairs.  Values
+   can take on several forms.  The forms that are used in this
+   implementation are listed below.  */
+
+enum dw_val_class
+{
+  dw_val_class_addr,
+  dw_val_class_offset,
+  dw_val_class_loc,
+  dw_val_class_loc_list,
+  dw_val_class_range_list,
+  dw_val_class_const,
+  dw_val_class_unsigned_const,
+  dw_val_class_long_long,
+  dw_val_class_vec,
+  dw_val_class_flag,
+  dw_val_class_die_ref,
+  dw_val_class_fde_ref,
+  dw_val_class_lbl_id,
+  dw_val_class_lineptr,
+  dw_val_class_str,
+  dw_val_class_macptr,
+  dw_val_class_file
+};
+
+/* Describe a double word constant value.  */
+/* ??? Every instance of long_long in the code really means CONST_DOUBLE.  */
+
+typedef struct dw_long_long_struct GTY(())
+{
+  unsigned long hi;
+  unsigned long low;
+}
+dw_long_long_const;
+
+/* Describe a floating point constant value, or a vector constant value.  */
+
+typedef struct dw_vec_struct GTY(())
+{
+  unsigned char * GTY((length ("%h.length"))) array;
+  unsigned length;
+  unsigned elt_size;
+}
+dw_vec_const;
+
+/* The dw_val_node describes an attribute's value, as it is
+   represented internally.  */
+
+typedef struct dw_val_struct GTY(())
+{
+  enum dw_val_class val_class;
+  union dw_val_struct_union
+    {
+      rtx GTY ((tag ("dw_val_class_addr"))) val_addr;
+      unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_offset"))) val_offset;
+      dw_loc_list_ref GTY ((tag ("dw_val_class_loc_list"))) val_loc_list;
+      dw_loc_descr_ref GTY ((tag ("dw_val_class_loc"))) val_loc;
+      HOST_WIDE_INT GTY ((default)) val_int;
+      unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_unsigned_const"))) val_unsigned;
+      dw_long_long_const GTY ((tag ("dw_val_class_long_long"))) val_long_long;
+      dw_vec_const GTY ((tag ("dw_val_class_vec"))) val_vec;
+      struct dw_val_die_union
+	{
+	  dw_die_ref die;
+	  int external;
+	} GTY ((tag ("dw_val_class_die_ref"))) val_die_ref;
+      unsigned GTY ((tag ("dw_val_class_fde_ref"))) val_fde_index;
+      struct indirect_string_node * GTY ((tag ("dw_val_class_str"))) val_str;
+      char * GTY ((tag ("dw_val_class_lbl_id"))) val_lbl_id;
+      unsigned char GTY ((tag ("dw_val_class_flag"))) val_flag;
+      struct dwarf_file_data * GTY ((tag ("dw_val_class_file"))) val_file;
+    }
+  GTY ((desc ("%1.val_class"))) v;
+}
+dw_val_node;
+
+/* Locations in memory are described using a sequence of stack machine
+   operations.  */
+
+typedef struct dw_loc_descr_struct GTY(())
+{
+  dw_loc_descr_ref dw_loc_next;
+  enum dwarf_location_atom dw_loc_opc;
+  int dw_loc_addr;
+  dw_val_node dw_loc_oprnd1;
+  dw_val_node dw_loc_oprnd2;
+}
+dw_loc_descr_node;
+
+/* Location lists are ranges + location descriptions for that range,
+   so you can track variables that are in different places over
+   their entire life.  */
+typedef struct dw_loc_list_struct GTY(())
+{
+  dw_loc_list_ref dw_loc_next;
+  const char *begin; /* Label for begin address of range */
+  const char *end;  /* Label for end address of range */
+  char *ll_symbol; /* Label for beginning of location list.
+		      Only on head of list */
+  const char *section; /* Section this loclist is relative to */
+  dw_loc_descr_ref expr;
+} dw_loc_list_node;
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+
+static dw_loc_descr_ref int_loc_descriptor (HOST_WIDE_INT);
+
+/* Convert a DWARF stack opcode into its string name.  */
+
+static const char *
+dwarf_stack_op_name (unsigned int op)
+{
+  switch (op)
+    {
+    case DW_OP_addr:
+    case INTERNAL_DW_OP_tls_addr:
+      return "DW_OP_addr";
+    case DW_OP_deref:
+      return "DW_OP_deref";
+    case DW_OP_const1u:
+      return "DW_OP_const1u";
+    case DW_OP_const1s:
+      return "DW_OP_const1s";
+    case DW_OP_const2u:
+      return "DW_OP_const2u";
+    case DW_OP_const2s:
+      return "DW_OP_const2s";
+    case DW_OP_const4u:
+      return "DW_OP_const4u";
+    case DW_OP_const4s:
+      return "DW_OP_const4s";
+    case DW_OP_const8u:
+      return "DW_OP_const8u";
+    case DW_OP_const8s:
+      return "DW_OP_const8s";
+    case DW_OP_constu:
+      return "DW_OP_constu";
+    case DW_OP_consts:
+      return "DW_OP_consts";
+    case DW_OP_dup:
+      return "DW_OP_dup";
+    case DW_OP_drop:
+      return "DW_OP_drop";
+    case DW_OP_over:
+      return "DW_OP_over";
+    case DW_OP_pick:
+      return "DW_OP_pick";
+    case DW_OP_swap:
+      return "DW_OP_swap";
+    case DW_OP_rot:
+      return "DW_OP_rot";
+    case DW_OP_xderef:
+      return "DW_OP_xderef";
+    case DW_OP_abs:
+      return "DW_OP_abs";
+    case DW_OP_and:
+      return "DW_OP_and";
+    case DW_OP_div:
+      return "DW_OP_div";
+    case DW_OP_minus:
+      return "DW_OP_minus";
+    case DW_OP_mod:
+      return "DW_OP_mod";
+    case DW_OP_mul:
+      return "DW_OP_mul";
+    case DW_OP_neg:
+      return "DW_OP_neg";
+    case DW_OP_not:
+      return "DW_OP_not";
+    case DW_OP_or:
+      return "DW_OP_or";
+    case DW_OP_plus:
+      return "DW_OP_plus";
+    case DW_OP_plus_uconst:
+      return "DW_OP_plus_uconst";
+    case DW_OP_shl:
+      return "DW_OP_shl";
+    case DW_OP_shr:
+      return "DW_OP_shr";
+    case DW_OP_shra:
+      return "DW_OP_shra";
+    case DW_OP_xor:
+      return "DW_OP_xor";
+    case DW_OP_bra:
+      return "DW_OP_bra";
+    case DW_OP_eq:
+      return "DW_OP_eq";
+    case DW_OP_ge:
+      return "DW_OP_ge";
+    case DW_OP_gt:
+      return "DW_OP_gt";
+    case DW_OP_le:
+      return "DW_OP_le";
+    case DW_OP_lt:
+      return "DW_OP_lt";
+    case DW_OP_ne:
+      return "DW_OP_ne";
+    case DW_OP_skip:
+      return "DW_OP_skip";
+    case DW_OP_lit0:
+      return "DW_OP_lit0";
+    case DW_OP_lit1:
+      return "DW_OP_lit1";
+    case DW_OP_lit2:
+      return "DW_OP_lit2";
+    case DW_OP_lit3:
+      return "DW_OP_lit3";
+    case DW_OP_lit4:
+      return "DW_OP_lit4";
+    case DW_OP_lit5:
+      return "DW_OP_lit5";
+    case DW_OP_lit6:
+      return "DW_OP_lit6";
+    case DW_OP_lit7:
+      return "DW_OP_lit7";
+    case DW_OP_lit8:
+      return "DW_OP_lit8";
+    case DW_OP_lit9:
+      return "DW_OP_lit9";
+    case DW_OP_lit10:
+      return "DW_OP_lit10";
+    case DW_OP_lit11:
+      return "DW_OP_lit11";
+    case DW_OP_lit12:
+      return "DW_OP_lit12";
+    case DW_OP_lit13:
+      return "DW_OP_lit13";
+    case DW_OP_lit14:
+      return "DW_OP_lit14";
+    case DW_OP_lit15:
+      return "DW_OP_lit15";
+    case DW_OP_lit16:
+      return "DW_OP_lit16";
+    case DW_OP_lit17:
+      return "DW_OP_lit17";
+    case DW_OP_lit18:
+      return "DW_OP_lit18";
+    case DW_OP_lit19:
+      return "DW_OP_lit19";
+    case DW_OP_lit20:
+      return "DW_OP_lit20";
+    case DW_OP_lit21:
+      return "DW_OP_lit21";
+    case DW_OP_lit22:
+      return "DW_OP_lit22";
+    case DW_OP_lit23:
+      return "DW_OP_lit23";
+    case DW_OP_lit24:
+      return "DW_OP_lit24";
+    case DW_OP_lit25:
+      return "DW_OP_lit25";
+    case DW_OP_lit26:
+      return "DW_OP_lit26";
+    case DW_OP_lit27:
+      return "DW_OP_lit27";
+    case DW_OP_lit28:
+      return "DW_OP_lit28";
+    case DW_OP_lit29:
+      return "DW_OP_lit29";
+    case DW_OP_lit30:
+      return "DW_OP_lit30";
+    case DW_OP_lit31:
+      return "DW_OP_lit31";
+    case DW_OP_reg0:
+      return "DW_OP_reg0";
+    case DW_OP_reg1:
+      return "DW_OP_reg1";
+    case DW_OP_reg2:
+      return "DW_OP_reg2";
+    case DW_OP_reg3:
+      return "DW_OP_reg3";
+    case DW_OP_reg4:
+      return "DW_OP_reg4";
+    case DW_OP_reg5:
+      return "DW_OP_reg5";
+    case DW_OP_reg6:
+      return "DW_OP_reg6";
+    case DW_OP_reg7:
+      return "DW_OP_reg7";
+    case DW_OP_reg8:
+      return "DW_OP_reg8";
+    case DW_OP_reg9:
+      return "DW_OP_reg9";
+    case DW_OP_reg10:
+      return "DW_OP_reg10";
+    case DW_OP_reg11:
+      return "DW_OP_reg11";
+    case DW_OP_reg12:
+      return "DW_OP_reg12";
+    case DW_OP_reg13:
+      return "DW_OP_reg13";
+    case DW_OP_reg14:
+      return "DW_OP_reg14";
+    case DW_OP_reg15:
+      return "DW_OP_reg15";
+    case DW_OP_reg16:
+      return "DW_OP_reg16";
+    case DW_OP_reg17:
+      return "DW_OP_reg17";
+    case DW_OP_reg18:
+      return "DW_OP_reg18";
+    case DW_OP_reg19:
+      return "DW_OP_reg19";
+    case DW_OP_reg20:
+      return "DW_OP_reg20";
+    case DW_OP_reg21:
+      return "DW_OP_reg21";
+    case DW_OP_reg22:
+      return "DW_OP_reg22";
+    case DW_OP_reg23:
+      return "DW_OP_reg23";
+    case DW_OP_reg24:
+      return "DW_OP_reg24";
+    case DW_OP_reg25:
+      return "DW_OP_reg25";
+    case DW_OP_reg26:
+      return "DW_OP_reg26";
+    case DW_OP_reg27:
+      return "DW_OP_reg27";
+    case DW_OP_reg28:
+      return "DW_OP_reg28";
+    case DW_OP_reg29:
+      return "DW_OP_reg29";
+    case DW_OP_reg30:
+      return "DW_OP_reg30";
+    case DW_OP_reg31:
+      return "DW_OP_reg31";
+    case DW_OP_breg0:
+      return "DW_OP_breg0";
+    case DW_OP_breg1:
+      return "DW_OP_breg1";
+    case DW_OP_breg2:
+      return "DW_OP_breg2";
+    case DW_OP_breg3:
+      return "DW_OP_breg3";
+    case DW_OP_breg4:
+      return "DW_OP_breg4";
+    case DW_OP_breg5:
+      return "DW_OP_breg5";
+    case DW_OP_breg6:
+      return "DW_OP_breg6";
+    case DW_OP_breg7:
+      return "DW_OP_breg7";
+    case DW_OP_breg8:
+      return "DW_OP_breg8";
+    case DW_OP_breg9:
+      return "DW_OP_breg9";
+    case DW_OP_breg10:
+      return "DW_OP_breg10";
+    case DW_OP_breg11:
+      return "DW_OP_breg11";
+    case DW_OP_breg12:
+      return "DW_OP_breg12";
+    case DW_OP_breg13:
+      return "DW_OP_breg13";
+    case DW_OP_breg14:
+      return "DW_OP_breg14";
+    case DW_OP_breg15:
+      return "DW_OP_breg15";
+    case DW_OP_breg16:
+      return "DW_OP_breg16";
+    case DW_OP_breg17:
+      return "DW_OP_breg17";
+    case DW_OP_breg18:
+      return "DW_OP_breg18";
+    case DW_OP_breg19:
+      return "DW_OP_breg19";
+    case DW_OP_breg20:
+      return "DW_OP_breg20";
+    case DW_OP_breg21:
+      return "DW_OP_breg21";
+    case DW_OP_breg22:
+      return "DW_OP_breg22";
+    case DW_OP_breg23:
+      return "DW_OP_breg23";
+    case DW_OP_breg24:
+      return "DW_OP_breg24";
+    case DW_OP_breg25:
+      return "DW_OP_breg25";
+    case DW_OP_breg26:
+      return "DW_OP_breg26";
+    case DW_OP_breg27:
+      return "DW_OP_breg27";
+    case DW_OP_breg28:
+      return "DW_OP_breg28";
+    case DW_OP_breg29:
+      return "DW_OP_breg29";
+    case DW_OP_breg30:
+      return "DW_OP_breg30";
+    case DW_OP_breg31:
+      return "DW_OP_breg31";
+    case DW_OP_regx:
+      return "DW_OP_regx";
+    case DW_OP_fbreg:
+      return "DW_OP_fbreg";
+    case DW_OP_bregx:
+      return "DW_OP_bregx";
+    case DW_OP_piece:
+      return "DW_OP_piece";
+    case DW_OP_deref_size:
+      return "DW_OP_deref_size";
+    case DW_OP_xderef_size:
+      return "DW_OP_xderef_size";
+    case DW_OP_nop:
+      return "DW_OP_nop";
+    case DW_OP_push_object_address:
+      return "DW_OP_push_object_address";
+    case DW_OP_call2:
+      return "DW_OP_call2";
+    case DW_OP_call4:
+      return "DW_OP_call4";
+    case DW_OP_call_ref:
+      return "DW_OP_call_ref";
+    case DW_OP_GNU_push_tls_address:
+      return "DW_OP_GNU_push_tls_address";
+    case DW_OP_GNU_uninit:
+      return "DW_OP_GNU_uninit";
+    default:
+      return "OP_<unknown>";
+    }
+}
+
+/* Return a pointer to a newly allocated location description.  Location
+   descriptions are simple expression terms that can be strung
+   together to form more complicated location (address) descriptions.  */
+
+static inline dw_loc_descr_ref
+new_loc_descr (enum dwarf_location_atom op, unsigned HOST_WIDE_INT oprnd1,
+	       unsigned HOST_WIDE_INT oprnd2)
+{
+  dw_loc_descr_ref descr = GGC_CNEW (dw_loc_descr_node);
+
+  descr->dw_loc_opc = op;
+  descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const;
+  descr->dw_loc_oprnd1.v.val_unsigned = oprnd1;
+  descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const;
+  descr->dw_loc_oprnd2.v.val_unsigned = oprnd2;
+
+  return descr;
+}
+
+/* Return a pointer to a newly allocated location description for
+   REG and OFFSET.  */
+
+static inline dw_loc_descr_ref
+new_reg_loc_descr (unsigned int reg,  unsigned HOST_WIDE_INT offset)
+{
+  if (offset)
+    {
+      if (reg <= 31)
+	return new_loc_descr (DW_OP_breg0 + reg, offset, 0);
+      else
+	return new_loc_descr (DW_OP_bregx, reg, offset);
+    }
+  else if (reg <= 31)
+    return new_loc_descr (DW_OP_reg0 + reg, 0, 0);
+  else
+   return new_loc_descr (DW_OP_regx, reg, 0);
+}
+
+/* Add a location description term to a location description expression.  */
+
+static inline void
+add_loc_descr (dw_loc_descr_ref *list_head, dw_loc_descr_ref descr)
+{
+  dw_loc_descr_ref *d;
+
+  /* Find the end of the chain.  */
+  for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next)
+    ;
+
+  *d = descr;
+}
+
+/* Return the size of a location descriptor.  */
+
+static unsigned long
+size_of_loc_descr (dw_loc_descr_ref loc)
+{
+  unsigned long size = 1;
+
+  switch (loc->dw_loc_opc)
+    {
+    case DW_OP_addr:
+    case INTERNAL_DW_OP_tls_addr:
+      size += DWARF2_ADDR_SIZE;
+      break;
+    case DW_OP_const1u:
+    case DW_OP_const1s:
+      size += 1;
+      break;
+    case DW_OP_const2u:
+    case DW_OP_const2s:
+      size += 2;
+      break;
+    case DW_OP_const4u:
+    case DW_OP_const4s:
+      size += 4;
+      break;
+    case DW_OP_const8u:
+    case DW_OP_const8s:
+      size += 8;
+      break;
+    case DW_OP_constu:
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned);
+      break;
+    case DW_OP_consts:
+      size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int);
+      break;
+    case DW_OP_pick:
+      size += 1;
+      break;
+    case DW_OP_plus_uconst:
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned);
+      break;
+    case DW_OP_skip:
+    case DW_OP_bra:
+      size += 2;
+      break;
+    case DW_OP_breg0:
+    case DW_OP_breg1:
+    case DW_OP_breg2:
+    case DW_OP_breg3:
+    case DW_OP_breg4:
+    case DW_OP_breg5:
+    case DW_OP_breg6:
+    case DW_OP_breg7:
+    case DW_OP_breg8:
+    case DW_OP_breg9:
+    case DW_OP_breg10:
+    case DW_OP_breg11:
+    case DW_OP_breg12:
+    case DW_OP_breg13:
+    case DW_OP_breg14:
+    case DW_OP_breg15:
+    case DW_OP_breg16:
+    case DW_OP_breg17:
+    case DW_OP_breg18:
+    case DW_OP_breg19:
+    case DW_OP_breg20:
+    case DW_OP_breg21:
+    case DW_OP_breg22:
+    case DW_OP_breg23:
+    case DW_OP_breg24:
+    case DW_OP_breg25:
+    case DW_OP_breg26:
+    case DW_OP_breg27:
+    case DW_OP_breg28:
+    case DW_OP_breg29:
+    case DW_OP_breg30:
+    case DW_OP_breg31:
+      size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int);
+      break;
+    case DW_OP_regx:
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned);
+      break;
+    case DW_OP_fbreg:
+      size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int);
+      break;
+    case DW_OP_bregx:
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned);
+      size += size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int);
+      break;
+    case DW_OP_piece:
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned);
+      break;
+    case DW_OP_deref_size:
+    case DW_OP_xderef_size:
+      size += 1;
+      break;
+    case DW_OP_call2:
+      size += 2;
+      break;
+    case DW_OP_call4:
+      size += 4;
+      break;
+    case DW_OP_call_ref:
+      size += DWARF2_ADDR_SIZE;
+      break;
+    default:
+      break;
+    }
+
+  return size;
+}
+
+/* Return the size of a series of location descriptors.  */
+
+static unsigned long
+size_of_locs (dw_loc_descr_ref loc)
+{
+  dw_loc_descr_ref l;
+  unsigned long size;
+
+  /* If there are no skip or bra opcodes, don't fill in the dw_loc_addr
+     field, to avoid writing to a PCH file.  */
+  for (size = 0, l = loc; l != NULL; l = l->dw_loc_next)
+    {
+      if (l->dw_loc_opc == DW_OP_skip || l->dw_loc_opc == DW_OP_bra)
+	break;
+      size += size_of_loc_descr (l);
+    }
+  if (! l)
+    return size;
+
+  for (size = 0, l = loc; l != NULL; l = l->dw_loc_next)
+    {
+      l->dw_loc_addr = size;
+      size += size_of_loc_descr (l);
+    }
+
+  return size;
+}
+
+/* Output location description stack opcode's operands (if any).  */
+
+static void
+output_loc_operands (dw_loc_descr_ref loc)
+{
+  dw_val_ref val1 = &loc->dw_loc_oprnd1;
+  dw_val_ref val2 = &loc->dw_loc_oprnd2;
+
+  switch (loc->dw_loc_opc)
+    {
+#ifdef DWARF2_DEBUGGING_INFO
+    case DW_OP_addr:
+      dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, val1->v.val_addr, NULL);
+      break;
+    case DW_OP_const2u:
+    case DW_OP_const2s:
+      dw2_asm_output_data (2, val1->v.val_int, NULL);
+      break;
+    case DW_OP_const4u:
+    case DW_OP_const4s:
+      dw2_asm_output_data (4, val1->v.val_int, NULL);
+      break;
+    case DW_OP_const8u:
+    case DW_OP_const8s:
+      gcc_assert (HOST_BITS_PER_LONG >= 64);
+      dw2_asm_output_data (8, val1->v.val_int, NULL);
+      break;
+    case DW_OP_skip:
+    case DW_OP_bra:
+      {
+	int offset;
+
+	gcc_assert (val1->val_class == dw_val_class_loc);
+	offset = val1->v.val_loc->dw_loc_addr - (loc->dw_loc_addr + 3);
+
+	dw2_asm_output_data (2, offset, NULL);
+      }
+      break;
+#else
+    case DW_OP_addr:
+    case DW_OP_const2u:
+    case DW_OP_const2s:
+    case DW_OP_const4u:
+    case DW_OP_const4s:
+    case DW_OP_const8u:
+    case DW_OP_const8s:
+    case DW_OP_skip:
+    case DW_OP_bra:
+      /* We currently don't make any attempt to make sure these are
+	 aligned properly like we do for the main unwind info, so
+	 don't support emitting things larger than a byte if we're
+	 only doing unwinding.  */
+      gcc_unreachable ();
+#endif
+    case DW_OP_const1u:
+    case DW_OP_const1s:
+      dw2_asm_output_data (1, val1->v.val_int, NULL);
+      break;
+    case DW_OP_constu:
+      dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+      break;
+    case DW_OP_consts:
+      dw2_asm_output_data_sleb128 (val1->v.val_int, NULL);
+      break;
+    case DW_OP_pick:
+      dw2_asm_output_data (1, val1->v.val_int, NULL);
+      break;
+    case DW_OP_plus_uconst:
+      dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+      break;
+    case DW_OP_breg0:
+    case DW_OP_breg1:
+    case DW_OP_breg2:
+    case DW_OP_breg3:
+    case DW_OP_breg4:
+    case DW_OP_breg5:
+    case DW_OP_breg6:
+    case DW_OP_breg7:
+    case DW_OP_breg8:
+    case DW_OP_breg9:
+    case DW_OP_breg10:
+    case DW_OP_breg11:
+    case DW_OP_breg12:
+    case DW_OP_breg13:
+    case DW_OP_breg14:
+    case DW_OP_breg15:
+    case DW_OP_breg16:
+    case DW_OP_breg17:
+    case DW_OP_breg18:
+    case DW_OP_breg19:
+    case DW_OP_breg20:
+    case DW_OP_breg21:
+    case DW_OP_breg22:
+    case DW_OP_breg23:
+    case DW_OP_breg24:
+    case DW_OP_breg25:
+    case DW_OP_breg26:
+    case DW_OP_breg27:
+    case DW_OP_breg28:
+    case DW_OP_breg29:
+    case DW_OP_breg30:
+    case DW_OP_breg31:
+      dw2_asm_output_data_sleb128 (val1->v.val_int, NULL);
+      break;
+    case DW_OP_regx:
+      dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+      break;
+    case DW_OP_fbreg:
+      dw2_asm_output_data_sleb128 (val1->v.val_int, NULL);
+      break;
+    case DW_OP_bregx:
+      dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+      dw2_asm_output_data_sleb128 (val2->v.val_int, NULL);
+      break;
+    case DW_OP_piece:
+      dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+      break;
+    case DW_OP_deref_size:
+    case DW_OP_xderef_size:
+      dw2_asm_output_data (1, val1->v.val_int, NULL);
+      break;
+
+    case INTERNAL_DW_OP_tls_addr:
+      if (targetm.asm_out.output_dwarf_dtprel)
+	{
+	  targetm.asm_out.output_dwarf_dtprel (asm_out_file,
+					       DWARF2_ADDR_SIZE,
+					       val1->v.val_addr);
+	  fputc ('\n', asm_out_file);
+	}
+      else
+	gcc_unreachable ();
+      break;
+
+    default:
+      /* Other codes have no operands.  */
+      break;
+    }
+}
+
+/* Output a sequence of location operations.  */
+
+static void
+output_loc_sequence (dw_loc_descr_ref loc)
+{
+  for (; loc != NULL; loc = loc->dw_loc_next)
+    {
+      /* Output the opcode.  */
+      dw2_asm_output_data (1, loc->dw_loc_opc,
+			   "%s", dwarf_stack_op_name (loc->dw_loc_opc));
+
+      /* Output the operand(s) (if any).  */
+      output_loc_operands (loc);
+    }
+}
+
+/* Output location description stack opcode's operands (if any).
+   The output is single bytes on a line, suitable for .cfi_escape.  */
+
+static void
+output_loc_operands_raw (dw_loc_descr_ref loc)
+{
+  dw_val_ref val1 = &loc->dw_loc_oprnd1;
+  dw_val_ref val2 = &loc->dw_loc_oprnd2;
+
+  switch (loc->dw_loc_opc)
+    {
+    case DW_OP_addr:
+      /* We cannot output addresses in .cfi_escape, only bytes.  */
+      gcc_unreachable ();
+
+    case DW_OP_const1u:
+    case DW_OP_const1s:
+    case DW_OP_pick:
+    case DW_OP_deref_size:
+    case DW_OP_xderef_size:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_raw (1, val1->v.val_int);
+      break;
+
+    case DW_OP_const2u:
+    case DW_OP_const2s:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_raw (2, val1->v.val_int);
+      break;
+
+    case DW_OP_const4u:
+    case DW_OP_const4s:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_raw (4, val1->v.val_int);
+      break;
+
+    case DW_OP_const8u:
+    case DW_OP_const8s:
+      gcc_assert (HOST_BITS_PER_LONG >= 64);
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_raw (8, val1->v.val_int);
+      break;
+
+    case DW_OP_skip:
+    case DW_OP_bra:
+      {
+	int offset;
+
+	gcc_assert (val1->val_class == dw_val_class_loc);
+	offset = val1->v.val_loc->dw_loc_addr - (loc->dw_loc_addr + 3);
+
+        fputc (',', asm_out_file);
+	dw2_asm_output_data_raw (2, offset);
+      }
+      break;
+
+    case DW_OP_constu:
+    case DW_OP_plus_uconst:
+    case DW_OP_regx:
+    case DW_OP_piece:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned);
+      break;
+
+    case DW_OP_consts:
+    case DW_OP_breg0:
+    case DW_OP_breg1:
+    case DW_OP_breg2:
+    case DW_OP_breg3:
+    case DW_OP_breg4:
+    case DW_OP_breg5:
+    case DW_OP_breg6:
+    case DW_OP_breg7:
+    case DW_OP_breg8:
+    case DW_OP_breg9:
+    case DW_OP_breg10:
+    case DW_OP_breg11:
+    case DW_OP_breg12:
+    case DW_OP_breg13:
+    case DW_OP_breg14:
+    case DW_OP_breg15:
+    case DW_OP_breg16:
+    case DW_OP_breg17:
+    case DW_OP_breg18:
+    case DW_OP_breg19:
+    case DW_OP_breg20:
+    case DW_OP_breg21:
+    case DW_OP_breg22:
+    case DW_OP_breg23:
+    case DW_OP_breg24:
+    case DW_OP_breg25:
+    case DW_OP_breg26:
+    case DW_OP_breg27:
+    case DW_OP_breg28:
+    case DW_OP_breg29:
+    case DW_OP_breg30:
+    case DW_OP_breg31:
+    case DW_OP_fbreg:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_sleb128_raw (val1->v.val_int);
+      break;
+
+    case DW_OP_bregx:
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned);
+      fputc (',', asm_out_file);
+      dw2_asm_output_data_sleb128_raw (val2->v.val_int);
+      break;
+
+    case INTERNAL_DW_OP_tls_addr:
+      gcc_unreachable ();
+
+    default:
+      /* Other codes have no operands.  */
+      break;
+    }
+}
+
+static void
+output_loc_sequence_raw (dw_loc_descr_ref loc)
+{
+  while (1)
+    {
+      /* Output the opcode.  */
+      fprintf (asm_out_file, "0x%x", loc->dw_loc_opc);
+      output_loc_operands_raw (loc);
+
+      if (!loc->dw_loc_next)
+	break;
+      loc = loc->dw_loc_next;
+
+      fputc (',', asm_out_file);
+    }
+}
+
+/* This routine will generate the correct assembly data for a location
+   description based on a cfi entry with a complex address.  */
+
+static void
+output_cfa_loc (dw_cfi_ref cfi)
+{
+  dw_loc_descr_ref loc;
+  unsigned long size;
+
+  if (cfi->dw_cfi_opc == DW_CFA_expression)
+    dw2_asm_output_data (1, cfi->dw_cfi_oprnd2.dw_cfi_reg_num, NULL);
+
+  /* Output the size of the block.  */
+  loc = cfi->dw_cfi_oprnd1.dw_cfi_loc;
+  size = size_of_locs (loc);
+  dw2_asm_output_data_uleb128 (size, NULL);
+
+  /* Now output the operations themselves.  */
+  output_loc_sequence (loc);
+}
+
+/* Similar, but used for .cfi_escape.  */
+
+static void
+output_cfa_loc_raw (dw_cfi_ref cfi)
+{
+  dw_loc_descr_ref loc;
+  unsigned long size;
+
+  if (cfi->dw_cfi_opc == DW_CFA_expression)
+    fprintf (asm_out_file, "0x%x,", cfi->dw_cfi_oprnd2.dw_cfi_reg_num);
+
+  /* Output the size of the block.  */
+  loc = cfi->dw_cfi_oprnd1.dw_cfi_loc;
+  size = size_of_locs (loc);
+  dw2_asm_output_data_uleb128_raw (size);
+  fputc (',', asm_out_file);
+
+  /* Now output the operations themselves.  */
+  output_loc_sequence_raw (loc);
+}
+
+/* This function builds a dwarf location descriptor sequence from a
+   dw_cfa_location, adding the given OFFSET to the result of the
+   expression.  */
+
+static struct dw_loc_descr_struct *
+build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset)
+{
+  struct dw_loc_descr_struct *head, *tmp;
+
+  offset += cfa->offset;
+
+  if (cfa->indirect)
+    {
+      head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
+      head->dw_loc_oprnd1.val_class = dw_val_class_const;
+      tmp = new_loc_descr (DW_OP_deref, 0, 0);
+      add_loc_descr (&head, tmp);
+      if (offset != 0)
+	{
+	  tmp = new_loc_descr (DW_OP_plus_uconst, offset, 0);
+	  add_loc_descr (&head, tmp);
+	}
+    }
+  else
+    head = new_reg_loc_descr (cfa->reg, offset);
+
+  return head;
+}
+
+/* This function builds a dwarf location descriptor sequence for
+   the address at OFFSET from the CFA when stack is aligned to
+   ALIGNMENT byte.  */
+
+static struct dw_loc_descr_struct *
+build_cfa_aligned_loc (HOST_WIDE_INT offset, HOST_WIDE_INT alignment)
+{
+  struct dw_loc_descr_struct *head;
+  unsigned int dwarf_fp
+    = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+
+ /* When CFA is defined as FP+OFFSET, emulate stack alignment.  */
+  if (cfa.reg == HARD_FRAME_POINTER_REGNUM && cfa.indirect == 0)
+    {
+      head = new_reg_loc_descr (dwarf_fp, 0);
+      add_loc_descr (&head, int_loc_descriptor (alignment));
+      add_loc_descr (&head, new_loc_descr (DW_OP_and, 0, 0));
+
+      add_loc_descr (&head, int_loc_descriptor (offset));
+      add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0));
+    }
+  else
+    head = new_reg_loc_descr (dwarf_fp, offset);
+  return head;
+}
+
+/* This function fills in aa dw_cfa_location structure from a dwarf location
+   descriptor sequence.  */
+
+static void
+get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_struct *loc)
+{
+  struct dw_loc_descr_struct *ptr;
+  cfa->offset = 0;
+  cfa->base_offset = 0;
+  cfa->indirect = 0;
+  cfa->reg = -1;
+
+  for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
+    {
+      enum dwarf_location_atom op = ptr->dw_loc_opc;
+
+      switch (op)
+	{
+	case DW_OP_reg0:
+	case DW_OP_reg1:
+	case DW_OP_reg2:
+	case DW_OP_reg3:
+	case DW_OP_reg4:
+	case DW_OP_reg5:
+	case DW_OP_reg6:
+	case DW_OP_reg7:
+	case DW_OP_reg8:
+	case DW_OP_reg9:
+	case DW_OP_reg10:
+	case DW_OP_reg11:
+	case DW_OP_reg12:
+	case DW_OP_reg13:
+	case DW_OP_reg14:
+	case DW_OP_reg15:
+	case DW_OP_reg16:
+	case DW_OP_reg17:
+	case DW_OP_reg18:
+	case DW_OP_reg19:
+	case DW_OP_reg20:
+	case DW_OP_reg21:
+	case DW_OP_reg22:
+	case DW_OP_reg23:
+	case DW_OP_reg24:
+	case DW_OP_reg25:
+	case DW_OP_reg26:
+	case DW_OP_reg27:
+	case DW_OP_reg28:
+	case DW_OP_reg29:
+	case DW_OP_reg30:
+	case DW_OP_reg31:
+	  cfa->reg = op - DW_OP_reg0;
+	  break;
+	case DW_OP_regx:
+	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_breg0:
+	case DW_OP_breg1:
+	case DW_OP_breg2:
+	case DW_OP_breg3:
+	case DW_OP_breg4:
+	case DW_OP_breg5:
+	case DW_OP_breg6:
+	case DW_OP_breg7:
+	case DW_OP_breg8:
+	case DW_OP_breg9:
+	case DW_OP_breg10:
+	case DW_OP_breg11:
+	case DW_OP_breg12:
+	case DW_OP_breg13:
+	case DW_OP_breg14:
+	case DW_OP_breg15:
+	case DW_OP_breg16:
+	case DW_OP_breg17:
+	case DW_OP_breg18:
+	case DW_OP_breg19:
+	case DW_OP_breg20:
+	case DW_OP_breg21:
+	case DW_OP_breg22:
+	case DW_OP_breg23:
+	case DW_OP_breg24:
+	case DW_OP_breg25:
+	case DW_OP_breg26:
+	case DW_OP_breg27:
+	case DW_OP_breg28:
+	case DW_OP_breg29:
+	case DW_OP_breg30:
+	case DW_OP_breg31:
+	  cfa->reg = op - DW_OP_breg0;
+	  cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
+	  break;
+	case DW_OP_bregx:
+	  cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
+	  cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
+	  break;
+	case DW_OP_deref:
+	  cfa->indirect = 1;
+	  break;
+	case DW_OP_plus_uconst:
+	  cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
+	  break;
+	default:
+	  internal_error ("DW_LOC_OP %s not implemented",
+			  dwarf_stack_op_name (ptr->dw_loc_opc));
+	}
+    }
+}
+#endif /* .debug_frame support */
+
+/* And now, the support for symbolic debugging information.  */
+#ifdef DWARF2_DEBUGGING_INFO
+
+/* .debug_str support.  */
+static int output_indirect_string (void **, void *);
+
+static void dwarf2out_init (const char *);
+static void dwarf2out_finish (const char *);
+static void dwarf2out_define (unsigned int, const char *);
+static void dwarf2out_undef (unsigned int, const char *);
+static void dwarf2out_start_source_file (unsigned, const char *);
+static void dwarf2out_end_source_file (unsigned);
+static void dwarf2out_begin_block (unsigned, unsigned);
+static void dwarf2out_end_block (unsigned, unsigned);
+static bool dwarf2out_ignore_block (const_tree);
+static void dwarf2out_global_decl (tree);
+static void dwarf2out_type_decl (tree, int);
+static void dwarf2out_imported_module_or_decl (tree, tree, tree, bool);
+static void dwarf2out_imported_module_or_decl_1 (tree, tree, tree,
+						 dw_die_ref);
+static void dwarf2out_abstract_function (tree);
+static void dwarf2out_var_location (rtx);
+static void dwarf2out_begin_function (tree);
+
+/* The debug hooks structure.  */
+
+const struct gcc_debug_hooks dwarf2_debug_hooks =
+{
+  dwarf2out_init,
+  dwarf2out_finish,
+  dwarf2out_define,
+  dwarf2out_undef,
+  dwarf2out_start_source_file,
+  dwarf2out_end_source_file,
+  dwarf2out_begin_block,
+  dwarf2out_end_block,
+  dwarf2out_ignore_block,
+  dwarf2out_source_line,
+  dwarf2out_begin_prologue,
+  debug_nothing_int_charstar,	/* end_prologue */
+  dwarf2out_end_epilogue,
+  dwarf2out_begin_function,
+  debug_nothing_int,		/* end_function */
+  dwarf2out_decl,		/* function_decl */
+  dwarf2out_global_decl,
+  dwarf2out_type_decl,		/* type_decl */
+  dwarf2out_imported_module_or_decl,
+  debug_nothing_tree,		/* deferred_inline_function */
+  /* The DWARF 2 backend tries to reduce debugging bloat by not
+     emitting the abstract description of inline functions until
+     something tries to reference them.  */
+  dwarf2out_abstract_function,	/* outlining_inline_function */
+  debug_nothing_rtx,		/* label */
+  debug_nothing_int,		/* handle_pch */
+  dwarf2out_var_location,
+  dwarf2out_switch_text_section,
+  1                             /* start_end_main_source_file */
+};
+#endif
+
+/* NOTE: In the comments in this file, many references are made to
+   "Debugging Information Entries".  This term is abbreviated as `DIE'
+   throughout the remainder of this file.  */
+
+/* An internal representation of the DWARF output is built, and then
+   walked to generate the DWARF debugging info.  The walk of the internal
+   representation is done after the entire program has been compiled.
+   The types below are used to describe the internal representation.  */
+
+/* Various DIE's use offsets relative to the beginning of the
+   .debug_info section to refer to each other.  */
+
+typedef long int dw_offset;
+
+/* Define typedefs here to avoid circular dependencies.  */
+
+typedef struct dw_attr_struct *dw_attr_ref;
+typedef struct dw_line_info_struct *dw_line_info_ref;
+typedef struct dw_separate_line_info_struct *dw_separate_line_info_ref;
+typedef struct pubname_struct *pubname_ref;
+typedef struct dw_ranges_struct *dw_ranges_ref;
+typedef struct dw_ranges_by_label_struct *dw_ranges_by_label_ref;
+
+/* Each entry in the line_info_table maintains the file and
+   line number associated with the label generated for that
+   entry.  The label gives the PC value associated with
+   the line number entry.  */
+
+typedef struct dw_line_info_struct GTY(())
+{
+  unsigned long dw_file_num;
+  unsigned long dw_line_num;
+}
+dw_line_info_entry;
+
+/* Line information for functions in separate sections; each one gets its
+   own sequence.  */
+typedef struct dw_separate_line_info_struct GTY(())
+{
+  unsigned long dw_file_num;
+  unsigned long dw_line_num;
+  unsigned long function;
+}
+dw_separate_line_info_entry;
+
+/* Each DIE attribute has a field specifying the attribute kind,
+   a link to the next attribute in the chain, and an attribute value.
+   Attributes are typically linked below the DIE they modify.  */
+
+typedef struct dw_attr_struct GTY(())
+{
+  enum dwarf_attribute dw_attr;
+  dw_val_node dw_attr_val;
+}
+dw_attr_node;
+
+DEF_VEC_O(dw_attr_node);
+DEF_VEC_ALLOC_O(dw_attr_node,gc);
+
+/* The Debugging Information Entry (DIE) structure.  DIEs form a tree.
+   The children of each node form a circular list linked by
+   die_sib.  die_child points to the node *before* the "first" child node.  */
+
+typedef struct die_struct GTY((chain_circular ("%h.die_sib")))
+{
+  enum dwarf_tag die_tag;
+  char *die_symbol;
+  VEC(dw_attr_node,gc) * die_attr;
+  dw_die_ref die_parent;
+  dw_die_ref die_child;
+  dw_die_ref die_sib;
+  dw_die_ref die_definition; /* ref from a specification to its definition */
+  dw_offset die_offset;
+  unsigned long die_abbrev;
+  int die_mark;
+  /* Die is used and must not be pruned as unused.  */
+  int die_perennial_p;
+  unsigned int decl_id;
+}
+die_node;
+
+/* Evaluate 'expr' while 'c' is set to each child of DIE in order.  */
+#define FOR_EACH_CHILD(die, c, expr) do {	\
+  c = die->die_child;				\
+  if (c) do {					\
+    c = c->die_sib;				\
+    expr;					\
+  } while (c != die->die_child);		\
+} while (0)
+
+/* The pubname structure */
+
+typedef struct pubname_struct GTY(())
+{
+  dw_die_ref die;
+  const char *name;
+}
+pubname_entry;
+
+DEF_VEC_O(pubname_entry);
+DEF_VEC_ALLOC_O(pubname_entry, gc);
+
+struct dw_ranges_struct GTY(())
+{
+  /* If this is positive, it's a block number, otherwise it's a
+     bitwise-negated index into dw_ranges_by_label.  */
+  int num;
+};
+
+struct dw_ranges_by_label_struct GTY(())
+{
+  const char *begin;
+  const char *end;
+};
+
+/* The limbo die list structure.  */
+typedef struct limbo_die_struct GTY(())
+{
+  dw_die_ref die;
+  tree created_for;
+  struct limbo_die_struct *next;
+}
+limbo_die_node;
+
+/* How to start an assembler comment.  */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START ";#"
+#endif
+
+/* Define a macro which returns nonzero for a TYPE_DECL which was
+   implicitly generated for a tagged type.
+
+   Note that unlike the gcc front end (which generates a NULL named
+   TYPE_DECL node for each complete tagged type, each array type, and
+   each function type node created) the g++ front end generates a
+   _named_ TYPE_DECL node for each tagged type node created.
+   These TYPE_DECLs have DECL_ARTIFICIAL set, so we know not to
+   generate a DW_TAG_typedef DIE for them.  */
+
+#define TYPE_DECL_IS_STUB(decl)				\
+  (DECL_NAME (decl) == NULL_TREE			\
+   || (DECL_ARTIFICIAL (decl)				\
+       && is_tagged_type (TREE_TYPE (decl))		\
+       && ((decl == TYPE_STUB_DECL (TREE_TYPE (decl)))	\
+	   /* This is necessary for stub decls that	\
+	      appear in nested inline functions.  */	\
+	   || (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE	\
+	       && (decl_ultimate_origin (decl)		\
+		   == TYPE_STUB_DECL (TREE_TYPE (decl)))))))
+
+/* Information concerning the compilation unit's programming
+   language, and compiler version.  */
+
+/* Fixed size portion of the DWARF compilation unit header.  */
+#define DWARF_COMPILE_UNIT_HEADER_SIZE \
+  (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 3)
+
+/* Fixed size portion of public names info.  */
+#define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2)
+
+/* Fixed size portion of the address range info.  */
+#define DWARF_ARANGES_HEADER_SIZE					\
+  (DWARF_ROUND (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4,	\
+		DWARF2_ADDR_SIZE * 2)					\
+   - DWARF_INITIAL_LENGTH_SIZE)
+
+/* Size of padding portion in the address range info.  It must be
+   aligned to twice the pointer size.  */
+#define DWARF_ARANGES_PAD_SIZE \
+  (DWARF_ROUND (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4, \
+		DWARF2_ADDR_SIZE * 2)				   \
+   - (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4))
+
+/* Use assembler line directives if available.  */
+#ifndef DWARF2_ASM_LINE_DEBUG_INFO
+#ifdef HAVE_AS_DWARF2_DEBUG_LINE
+#define DWARF2_ASM_LINE_DEBUG_INFO 1
+#else
+#define DWARF2_ASM_LINE_DEBUG_INFO 0
+#endif
+#endif
+
+/* Minimum line offset in a special line info. opcode.
+   This value was chosen to give a reasonable range of values.  */
+#define DWARF_LINE_BASE  -10
+
+/* First special line opcode - leave room for the standard opcodes.  */
+#define DWARF_LINE_OPCODE_BASE  10
+
+/* Range of line offsets in a special line info. opcode.  */
+#define DWARF_LINE_RANGE  (254-DWARF_LINE_OPCODE_BASE+1)
+
+/* Flag that indicates the initial value of the is_stmt_start flag.
+   In the present implementation, we do not mark any lines as
+   the beginning of a source statement, because that information
+   is not made available by the GCC front-end.  */
+#define	DWARF_LINE_DEFAULT_IS_STMT_START 1
+
+#ifdef DWARF2_DEBUGGING_INFO
+/* This location is used by calc_die_sizes() to keep track
+   the offset of each DIE within the .debug_info section.  */
+static unsigned long next_die_offset;
+#endif
+
+/* Record the root of the DIE's built for the current compilation unit.  */
+static GTY(()) dw_die_ref comp_unit_die;
+
+/* A list of DIEs with a NULL parent waiting to be relocated.  */
+static GTY(()) limbo_die_node *limbo_die_list;
+
+/* Filenames referenced by this compilation unit.  */
+static GTY((param_is (struct dwarf_file_data))) htab_t file_table;
+
+/* A hash table of references to DIE's that describe declarations.
+   The key is a DECL_UID() which is a unique number identifying each decl.  */
+static GTY ((param_is (struct die_struct))) htab_t decl_die_table;
+
+/* A hash table of references to DIE's that describe COMMON blocks.
+   The key is DECL_UID() ^ die_parent.  */
+static GTY ((param_is (struct die_struct))) htab_t common_block_die_table;
+
+/* Node of the variable location list.  */
+struct var_loc_node GTY ((chain_next ("%h.next")))
+{
+  rtx GTY (()) var_loc_note;
+  const char * GTY (()) label;
+  const char * GTY (()) section_label;
+  struct var_loc_node * GTY (()) next;
+};
+
+/* Variable location list.  */
+struct var_loc_list_def GTY (())
+{
+  struct var_loc_node * GTY (()) first;
+
+  /* Do not mark the last element of the chained list because
+     it is marked through the chain.  */
+  struct var_loc_node * GTY ((skip ("%h"))) last;
+
+  /* DECL_UID of the variable decl.  */
+  unsigned int decl_id;
+};
+typedef struct var_loc_list_def var_loc_list;
+
+
+/* Table of decl location linked lists.  */
+static GTY ((param_is (var_loc_list))) htab_t decl_loc_table;
+
+/* A pointer to the base of a list of references to DIE's that
+   are uniquely identified by their tag, presence/absence of
+   children DIE's, and list of attribute/value pairs.  */
+static GTY((length ("abbrev_die_table_allocated")))
+  dw_die_ref *abbrev_die_table;
+
+/* Number of elements currently allocated for abbrev_die_table.  */
+static GTY(()) unsigned abbrev_die_table_allocated;
+
+/* Number of elements in type_die_table currently in use.  */
+static GTY(()) unsigned abbrev_die_table_in_use;
+
+/* Size (in elements) of increments by which we may expand the
+   abbrev_die_table.  */
+#define ABBREV_DIE_TABLE_INCREMENT 256
+
+/* A pointer to the base of a table that contains line information
+   for each source code line in .text in the compilation unit.  */
+static GTY((length ("line_info_table_allocated")))
+     dw_line_info_ref line_info_table;
+
+/* Number of elements currently allocated for line_info_table.  */
+static GTY(()) unsigned line_info_table_allocated;
+
+/* Number of elements in line_info_table currently in use.  */
+static GTY(()) unsigned line_info_table_in_use;
+
+/* A pointer to the base of a table that contains line information
+   for each source code line outside of .text in the compilation unit.  */
+static GTY ((length ("separate_line_info_table_allocated")))
+     dw_separate_line_info_ref separate_line_info_table;
+
+/* Number of elements currently allocated for separate_line_info_table.  */
+static GTY(()) unsigned separate_line_info_table_allocated;
+
+/* Number of elements in separate_line_info_table currently in use.  */
+static GTY(()) unsigned separate_line_info_table_in_use;
+
+/* Size (in elements) of increments by which we may expand the
+   line_info_table.  */
+#define LINE_INFO_TABLE_INCREMENT 1024
+
+/* A pointer to the base of a table that contains a list of publicly
+   accessible names.  */
+static GTY (()) VEC (pubname_entry, gc) *  pubname_table;
+
+/* A pointer to the base of a table that contains a list of publicly
+   accessible types.  */
+static GTY (()) VEC (pubname_entry, gc) * pubtype_table;
+
+/* Array of dies for which we should generate .debug_arange info.  */
+static GTY((length ("arange_table_allocated"))) dw_die_ref *arange_table;
+
+/* Number of elements currently allocated for arange_table.  */
+static GTY(()) unsigned arange_table_allocated;
+
+/* Number of elements in arange_table currently in use.  */
+static GTY(()) unsigned arange_table_in_use;
+
+/* Size (in elements) of increments by which we may expand the
+   arange_table.  */
+#define ARANGE_TABLE_INCREMENT 64
+
+/* Array of dies for which we should generate .debug_ranges info.  */
+static GTY ((length ("ranges_table_allocated"))) dw_ranges_ref ranges_table;
+
+/* Number of elements currently allocated for ranges_table.  */
+static GTY(()) unsigned ranges_table_allocated;
+
+/* Number of elements in ranges_table currently in use.  */
+static GTY(()) unsigned ranges_table_in_use;
+
+/* Array of pairs of labels referenced in ranges_table.  */
+static GTY ((length ("ranges_by_label_allocated")))
+     dw_ranges_by_label_ref ranges_by_label;
+
+/* Number of elements currently allocated for ranges_by_label.  */
+static GTY(()) unsigned ranges_by_label_allocated;
+
+/* Number of elements in ranges_by_label currently in use.  */
+static GTY(()) unsigned ranges_by_label_in_use;
+
+/* Size (in elements) of increments by which we may expand the
+   ranges_table.  */
+#define RANGES_TABLE_INCREMENT 64
+
+/* Whether we have location lists that need outputting */
+static GTY(()) bool have_location_lists;
+
+/* Unique label counter.  */
+static GTY(()) unsigned int loclabel_num;
+
+#ifdef DWARF2_DEBUGGING_INFO
+/* Record whether the function being analyzed contains inlined functions.  */
+static int current_function_has_inlines;
+#endif
+#if 0 && defined (MIPS_DEBUGGING_INFO)
+static int comp_unit_has_inlines;
+#endif
+
+/* The last file entry emitted by maybe_emit_file().  */
+static GTY(()) struct dwarf_file_data * last_emitted_file;
+
+/* Number of internal labels generated by gen_internal_sym().  */
+static GTY(()) int label_num;
+
+/* Cached result of previous call to lookup_filename.  */
+static GTY(()) struct dwarf_file_data * file_table_last_lookup;
+
+#ifdef DWARF2_DEBUGGING_INFO
+
+/* Offset from the "steady-state frame pointer" to the frame base,
+   within the current function.  */
+static HOST_WIDE_INT frame_pointer_fb_offset;
+
+/* Forward declarations for functions defined in this file.  */
+
+static int is_pseudo_reg (const_rtx);
+static tree type_main_variant (tree);
+static int is_tagged_type (const_tree);
+static const char *dwarf_tag_name (unsigned);
+static const char *dwarf_attr_name (unsigned);
+static const char *dwarf_form_name (unsigned);
+static tree decl_ultimate_origin (const_tree);
+static tree decl_class_context (tree);
+static void add_dwarf_attr (dw_die_ref, dw_attr_ref);
+static inline enum dw_val_class AT_class (dw_attr_ref);
+static void add_AT_flag (dw_die_ref, enum dwarf_attribute, unsigned);
+static inline unsigned AT_flag (dw_attr_ref);
+static void add_AT_int (dw_die_ref, enum dwarf_attribute, HOST_WIDE_INT);
+static inline HOST_WIDE_INT AT_int (dw_attr_ref);
+static void add_AT_unsigned (dw_die_ref, enum dwarf_attribute, unsigned HOST_WIDE_INT);
+static inline unsigned HOST_WIDE_INT AT_unsigned (dw_attr_ref);
+static void add_AT_long_long (dw_die_ref, enum dwarf_attribute, unsigned long,
+			      unsigned long);
+static inline void add_AT_vec (dw_die_ref, enum dwarf_attribute, unsigned int,
+			       unsigned int, unsigned char *);
+static hashval_t debug_str_do_hash (const void *);
+static int debug_str_eq (const void *, const void *);
+static void add_AT_string (dw_die_ref, enum dwarf_attribute, const char *);
+static inline const char *AT_string (dw_attr_ref);
+static int AT_string_form (dw_attr_ref);
+static void add_AT_die_ref (dw_die_ref, enum dwarf_attribute, dw_die_ref);
+static void add_AT_specification (dw_die_ref, dw_die_ref);
+static inline dw_die_ref AT_ref (dw_attr_ref);
+static inline int AT_ref_external (dw_attr_ref);
+static inline void set_AT_ref_external (dw_attr_ref, int);
+static void add_AT_fde_ref (dw_die_ref, enum dwarf_attribute, unsigned);
+static void add_AT_loc (dw_die_ref, enum dwarf_attribute, dw_loc_descr_ref);
+static inline dw_loc_descr_ref AT_loc (dw_attr_ref);
+static void add_AT_loc_list (dw_die_ref, enum dwarf_attribute,
+			     dw_loc_list_ref);
+static inline dw_loc_list_ref AT_loc_list (dw_attr_ref);
+static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx);
+static inline rtx AT_addr (dw_attr_ref);
+static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *);
+static void add_AT_lineptr (dw_die_ref, enum dwarf_attribute, const char *);
+static void add_AT_macptr (dw_die_ref, enum dwarf_attribute, const char *);
+static void add_AT_offset (dw_die_ref, enum dwarf_attribute,
+			   unsigned HOST_WIDE_INT);
+static void add_AT_range_list (dw_die_ref, enum dwarf_attribute,
+			       unsigned long);
+static inline const char *AT_lbl (dw_attr_ref);
+static dw_attr_ref get_AT (dw_die_ref, enum dwarf_attribute);
+static const char *get_AT_low_pc (dw_die_ref);
+static const char *get_AT_hi_pc (dw_die_ref);
+static const char *get_AT_string (dw_die_ref, enum dwarf_attribute);
+static int get_AT_flag (dw_die_ref, enum dwarf_attribute);
+static unsigned get_AT_unsigned (dw_die_ref, enum dwarf_attribute);
+static inline dw_die_ref get_AT_ref (dw_die_ref, enum dwarf_attribute);
+static bool is_c_family (void);
+static bool is_cxx (void);
+static bool is_java (void);
+static bool is_fortran (void);
+static bool is_ada (void);
+static void remove_AT (dw_die_ref, enum dwarf_attribute);
+static void remove_child_TAG (dw_die_ref, enum dwarf_tag);
+static void add_child_die (dw_die_ref, dw_die_ref);
+static dw_die_ref new_die (enum dwarf_tag, dw_die_ref, tree);
+static dw_die_ref lookup_type_die (tree);
+static void equate_type_number_to_die (tree, dw_die_ref);
+static hashval_t decl_die_table_hash (const void *);
+static int decl_die_table_eq (const void *, const void *);
+static dw_die_ref lookup_decl_die (tree);
+static hashval_t common_block_die_table_hash (const void *);
+static int common_block_die_table_eq (const void *, const void *);
+static hashval_t decl_loc_table_hash (const void *);
+static int decl_loc_table_eq (const void *, const void *);
+static var_loc_list *lookup_decl_loc (const_tree);
+static void equate_decl_number_to_die (tree, dw_die_ref);
+static void add_var_loc_to_decl (tree, struct var_loc_node *);
+static void print_spaces (FILE *);
+static void print_die (dw_die_ref, FILE *);
+static void print_dwarf_line_table (FILE *);
+static dw_die_ref push_new_compile_unit (dw_die_ref, dw_die_ref);
+static dw_die_ref pop_compile_unit (dw_die_ref);
+static void loc_checksum (dw_loc_descr_ref, struct md5_ctx *);
+static void attr_checksum (dw_attr_ref, struct md5_ctx *, int *);
+static void die_checksum (dw_die_ref, struct md5_ctx *, int *);
+static int same_loc_p (dw_loc_descr_ref, dw_loc_descr_ref, int *);
+static int same_dw_val_p (const dw_val_node *, const dw_val_node *, int *);
+static int same_attr_p (dw_attr_ref, dw_attr_ref, int *);
+static int same_die_p (dw_die_ref, dw_die_ref, int *);
+static int same_die_p_wrap (dw_die_ref, dw_die_ref);
+static void compute_section_prefix (dw_die_ref);
+static int is_type_die (dw_die_ref);
+static int is_comdat_die (dw_die_ref);
+static int is_symbol_die (dw_die_ref);
+static void assign_symbol_names (dw_die_ref);
+static void break_out_includes (dw_die_ref);
+static hashval_t htab_cu_hash (const void *);
+static int htab_cu_eq (const void *, const void *);
+static void htab_cu_del (void *);
+static int check_duplicate_cu (dw_die_ref, htab_t, unsigned *);
+static void record_comdat_symbol_number (dw_die_ref, htab_t, unsigned);
+static void add_sibling_attributes (dw_die_ref);
+static void build_abbrev_table (dw_die_ref);
+static void output_location_lists (dw_die_ref);
+static int constant_size (unsigned HOST_WIDE_INT);
+static unsigned long size_of_die (dw_die_ref);
+static void calc_die_sizes (dw_die_ref);
+static void mark_dies (dw_die_ref);
+static void unmark_dies (dw_die_ref);
+static void unmark_all_dies (dw_die_ref);
+static unsigned long size_of_pubnames (VEC (pubname_entry,gc) *);
+static unsigned long size_of_aranges (void);
+static enum dwarf_form value_format (dw_attr_ref);
+static void output_value_format (dw_attr_ref);
+static void output_abbrev_section (void);
+static void output_die_symbol (dw_die_ref);
+static void output_die (dw_die_ref);
+static void output_compilation_unit_header (void);
+static void output_comp_unit (dw_die_ref, int);
+static const char *dwarf2_name (tree, int);
+static void add_pubname (tree, dw_die_ref);
+static void add_pubname_string (const char *, dw_die_ref);
+static void add_pubtype (tree, dw_die_ref);
+static void output_pubnames (VEC (pubname_entry,gc) *);
+static void add_arange (tree, dw_die_ref);
+static void output_aranges (void);
+static unsigned int add_ranges_num (int);
+static unsigned int add_ranges (const_tree);
+static unsigned int add_ranges_by_labels (const char *, const char *);
+static void output_ranges (void);
+static void output_line_info (void);
+static void output_file_names (void);
+static dw_die_ref base_type_die (tree);
+static int is_base_type (tree);
+static bool is_subrange_type (const_tree);
+static dw_die_ref subrange_type_die (tree, dw_die_ref);
+static dw_die_ref modified_type_die (tree, int, int, dw_die_ref);
+static int type_is_enum (const_tree);
+static unsigned int dbx_reg_number (const_rtx);
+static void add_loc_descr_op_piece (dw_loc_descr_ref *, int);
+static dw_loc_descr_ref reg_loc_descriptor (rtx, enum var_init_status);
+static dw_loc_descr_ref one_reg_loc_descriptor (unsigned int,
+						enum var_init_status);
+static dw_loc_descr_ref multiple_reg_loc_descriptor (rtx, rtx,
+						     enum var_init_status);
+static dw_loc_descr_ref based_loc_descr (rtx, HOST_WIDE_INT,
+					 enum var_init_status);
+static int is_based_loc (const_rtx);
+static dw_loc_descr_ref mem_loc_descriptor (rtx, enum machine_mode mode,
+					    enum var_init_status);
+static dw_loc_descr_ref concat_loc_descriptor (rtx, rtx,
+					       enum var_init_status);
+static dw_loc_descr_ref loc_descriptor (rtx, enum var_init_status);
+static dw_loc_descr_ref loc_descriptor_from_tree_1 (tree, int);
+static dw_loc_descr_ref loc_descriptor_from_tree (tree);
+static HOST_WIDE_INT ceiling (HOST_WIDE_INT, unsigned int);
+static tree field_type (const_tree);
+static unsigned int simple_type_align_in_bits (const_tree);
+static unsigned int simple_decl_align_in_bits (const_tree);
+static unsigned HOST_WIDE_INT simple_type_size_in_bits (const_tree);
+static HOST_WIDE_INT field_byte_offset (const_tree);
+static void add_AT_location_description	(dw_die_ref, enum dwarf_attribute,
+					 dw_loc_descr_ref);
+static void add_data_member_location_attribute (dw_die_ref, tree);
+static void add_const_value_attribute (dw_die_ref, rtx);
+static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *);
+static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
+static void insert_float (const_rtx, unsigned char *);
+static rtx rtl_for_decl_location (tree);
+static void add_location_or_const_value_attribute (dw_die_ref, tree,
+						   enum dwarf_attribute);
+static void tree_add_const_value_attribute (dw_die_ref, tree);
+static void add_name_attribute (dw_die_ref, const char *);
+static void add_comp_dir_attribute (dw_die_ref);
+static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree);
+static void add_subscript_info (dw_die_ref, tree, bool);
+static void add_byte_size_attribute (dw_die_ref, tree);
+static void add_bit_offset_attribute (dw_die_ref, tree);
+static void add_bit_size_attribute (dw_die_ref, tree);
+static void add_prototyped_attribute (dw_die_ref, tree);
+static dw_die_ref add_abstract_origin_attribute (dw_die_ref, tree);
+static void add_pure_or_virtual_attribute (dw_die_ref, tree);
+static void add_src_coords_attributes (dw_die_ref, tree);
+static void add_name_and_src_coords_attributes (dw_die_ref, tree);
+static void push_decl_scope (tree);
+static void pop_decl_scope (void);
+static dw_die_ref scope_die_for (tree, dw_die_ref);
+static inline int local_scope_p (dw_die_ref);
+static inline int class_or_namespace_scope_p (dw_die_ref);
+static void add_type_attribute (dw_die_ref, tree, int, int, dw_die_ref);
+static void add_calling_convention_attribute (dw_die_ref, tree);
+static const char *type_tag (const_tree);
+static tree member_declared_type (const_tree);
+#if 0
+static const char *decl_start_label (tree);
+#endif
+static void gen_array_type_die (tree, dw_die_ref);
+static void gen_descr_array_type_die (tree, struct array_descr_info *, dw_die_ref);
+#if 0
+static void gen_entry_point_die (tree, dw_die_ref);
+#endif
+static dw_die_ref gen_enumeration_type_die (tree, dw_die_ref);
+static dw_die_ref gen_formal_parameter_die (tree, tree, dw_die_ref);
+static void gen_unspecified_parameters_die (tree, dw_die_ref);
+static void gen_formal_types_die (tree, dw_die_ref);
+static void gen_subprogram_die (tree, dw_die_ref);
+static void gen_variable_die (tree, tree, dw_die_ref);
+static void gen_const_die (tree, dw_die_ref);
+static void gen_label_die (tree, dw_die_ref);
+static void gen_lexical_block_die (tree, dw_die_ref, int);
+static void gen_inlined_subroutine_die (tree, dw_die_ref, int);
+static void gen_field_die (tree, dw_die_ref);
+static void gen_ptr_to_mbr_type_die (tree, dw_die_ref);
+static dw_die_ref gen_compile_unit_die (const char *);
+static void gen_inheritance_die (tree, tree, dw_die_ref);
+static void gen_member_die (tree, dw_die_ref);
+static void gen_struct_or_union_type_die (tree, dw_die_ref,
+						enum debug_info_usage);
+static void gen_subroutine_type_die (tree, dw_die_ref);
+static void gen_typedef_die (tree, dw_die_ref);
+static void gen_type_die (tree, dw_die_ref);
+static void gen_block_die (tree, dw_die_ref, int);
+static void decls_for_scope (tree, dw_die_ref, int);
+static int is_redundant_typedef (const_tree);
+static void gen_namespace_die (tree, dw_die_ref);
+static void gen_decl_die (tree, tree, dw_die_ref);
+static dw_die_ref force_decl_die (tree);
+static dw_die_ref force_type_die (tree);
+static dw_die_ref setup_namespace_context (tree, dw_die_ref);
+static dw_die_ref declare_in_namespace (tree, dw_die_ref);
+static struct dwarf_file_data * lookup_filename (const char *);
+static void retry_incomplete_types (void);
+static void gen_type_die_for_member (tree, tree, dw_die_ref);
+static void splice_child_die (dw_die_ref, dw_die_ref);
+static int file_info_cmp (const void *, const void *);
+static dw_loc_list_ref new_loc_list (dw_loc_descr_ref, const char *,
+				     const char *, const char *, unsigned);
+static void add_loc_descr_to_loc_list (dw_loc_list_ref *, dw_loc_descr_ref,
+				       const char *, const char *,
+				       const char *);
+static void output_loc_list (dw_loc_list_ref);
+static char *gen_internal_sym (const char *);
+
+static void prune_unmark_dies (dw_die_ref);
+static void prune_unused_types_mark (dw_die_ref, int);
+static void prune_unused_types_walk (dw_die_ref);
+static void prune_unused_types_walk_attribs (dw_die_ref);
+static void prune_unused_types_prune (dw_die_ref);
+static void prune_unused_types (void);
+static int maybe_emit_file (struct dwarf_file_data *fd);
+
+/* Section names used to hold DWARF debugging information.  */
+#ifndef DEBUG_INFO_SECTION
+#define DEBUG_INFO_SECTION	".debug_info"
+#endif
+#ifndef DEBUG_ABBREV_SECTION
+#define DEBUG_ABBREV_SECTION	".debug_abbrev"
+#endif
+#ifndef DEBUG_ARANGES_SECTION
+#define DEBUG_ARANGES_SECTION	".debug_aranges"
+#endif
+#ifndef DEBUG_MACINFO_SECTION
+#define DEBUG_MACINFO_SECTION	".debug_macinfo"
+#endif
+#ifndef DEBUG_LINE_SECTION
+#define DEBUG_LINE_SECTION	".debug_line"
+#endif
+#ifndef DEBUG_LOC_SECTION
+#define DEBUG_LOC_SECTION	".debug_loc"
+#endif
+#ifndef DEBUG_PUBNAMES_SECTION
+#define DEBUG_PUBNAMES_SECTION	".debug_pubnames"
+#endif
+#ifndef DEBUG_STR_SECTION
+#define DEBUG_STR_SECTION	".debug_str"
+#endif
+#ifndef DEBUG_RANGES_SECTION
+#define DEBUG_RANGES_SECTION	".debug_ranges"
+#endif
+
+/* Standard ELF section names for compiled code and data.  */
+#ifndef TEXT_SECTION_NAME
+#define TEXT_SECTION_NAME	".text"
+#endif
+
+/* Section flags for .debug_str section.  */
+#define DEBUG_STR_SECTION_FLAGS \
+  (HAVE_GAS_SHF_MERGE && flag_merge_debug_strings		\
+   ? SECTION_DEBUG | SECTION_MERGE | SECTION_STRINGS | 1	\
+   : SECTION_DEBUG)
+
+/* Labels we insert at beginning sections we can reference instead of
+   the section names themselves.  */
+
+#ifndef TEXT_SECTION_LABEL
+#define TEXT_SECTION_LABEL		"Ltext"
+#endif
+#ifndef COLD_TEXT_SECTION_LABEL
+#define COLD_TEXT_SECTION_LABEL         "Ltext_cold"
+#endif
+#ifndef DEBUG_LINE_SECTION_LABEL
+#define DEBUG_LINE_SECTION_LABEL	"Ldebug_line"
+#endif
+#ifndef DEBUG_INFO_SECTION_LABEL
+#define DEBUG_INFO_SECTION_LABEL	"Ldebug_info"
+#endif
+#ifndef DEBUG_ABBREV_SECTION_LABEL
+#define DEBUG_ABBREV_SECTION_LABEL	"Ldebug_abbrev"
+#endif
+#ifndef DEBUG_LOC_SECTION_LABEL
+#define DEBUG_LOC_SECTION_LABEL		"Ldebug_loc"
+#endif
+#ifndef DEBUG_RANGES_SECTION_LABEL
+#define DEBUG_RANGES_SECTION_LABEL	"Ldebug_ranges"
+#endif
+#ifndef DEBUG_MACINFO_SECTION_LABEL
+#define DEBUG_MACINFO_SECTION_LABEL     "Ldebug_macinfo"
+#endif
+
+/* Definitions of defaults for formats and names of various special
+   (artificial) labels which may be generated within this file (when the -g
+   options is used and DWARF2_DEBUGGING_INFO is in effect.
+   If necessary, these may be overridden from within the tm.h file, but
+   typically, overriding these defaults is unnecessary.  */
+
+static char text_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char text_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char cold_text_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char cold_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char abbrev_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char debug_info_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char debug_line_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char macinfo_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char loc_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
+static char ranges_section_label[2 * MAX_ARTIFICIAL_LABEL_BYTES];
+
+#ifndef TEXT_END_LABEL
+#define TEXT_END_LABEL		"Letext"
+#endif
+#ifndef COLD_END_LABEL
+#define COLD_END_LABEL          "Letext_cold"
+#endif
+#ifndef BLOCK_BEGIN_LABEL
+#define BLOCK_BEGIN_LABEL	"LBB"
+#endif
+#ifndef BLOCK_END_LABEL
+#define BLOCK_END_LABEL		"LBE"
+#endif
+#ifndef LINE_CODE_LABEL
+#define LINE_CODE_LABEL		"LM"
+#endif
+#ifndef SEPARATE_LINE_CODE_LABEL
+#define SEPARATE_LINE_CODE_LABEL	"LSM"
+#endif
+
+
+/* We allow a language front-end to designate a function that is to be
+   called to "demangle" any name before it is put into a DIE.  */
+
+static const char *(*demangle_name_func) (const char *);
+
+void
+dwarf2out_set_demangle_name_func (const char *(*func) (const char *))
+{
+  demangle_name_func = func;
+}
+
+/* Test if rtl node points to a pseudo register.  */
+
+static inline int
+is_pseudo_reg (const_rtx rtl)
+{
+  return ((REG_P (rtl) && REGNO (rtl) >= FIRST_PSEUDO_REGISTER)
+	  || (GET_CODE (rtl) == SUBREG
+	      && REGNO (SUBREG_REG (rtl)) >= FIRST_PSEUDO_REGISTER));
+}
+
+/* Return a reference to a type, with its const and volatile qualifiers
+   removed.  */
+
+static inline tree
+type_main_variant (tree type)
+{
+  type = TYPE_MAIN_VARIANT (type);
+
+  /* ??? There really should be only one main variant among any group of
+     variants of a given type (and all of the MAIN_VARIANT values for all
+     members of the group should point to that one type) but sometimes the C
+     front-end messes this up for array types, so we work around that bug
+     here.  */
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    while (type != TYPE_MAIN_VARIANT (type))
+      type = TYPE_MAIN_VARIANT (type);
+
+  return type;
+}
+
+/* Return nonzero if the given type node represents a tagged type.  */
+
+static inline int
+is_tagged_type (const_tree type)
+{
+  enum tree_code code = TREE_CODE (type);
+
+  return (code == RECORD_TYPE || code == UNION_TYPE
+	  || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE);
+}
+
+/* Convert a DIE tag into its string name.  */
+
+static const char *
+dwarf_tag_name (unsigned int tag)
+{
+  switch (tag)
+    {
+    case DW_TAG_padding:
+      return "DW_TAG_padding";
+    case DW_TAG_array_type:
+      return "DW_TAG_array_type";
+    case DW_TAG_class_type:
+      return "DW_TAG_class_type";
+    case DW_TAG_entry_point:
+      return "DW_TAG_entry_point";
+    case DW_TAG_enumeration_type:
+      return "DW_TAG_enumeration_type";
+    case DW_TAG_formal_parameter:
+      return "DW_TAG_formal_parameter";
+    case DW_TAG_imported_declaration:
+      return "DW_TAG_imported_declaration";
+    case DW_TAG_label:
+      return "DW_TAG_label";
+    case DW_TAG_lexical_block:
+      return "DW_TAG_lexical_block";
+    case DW_TAG_member:
+      return "DW_TAG_member";
+    case DW_TAG_pointer_type:
+      return "DW_TAG_pointer_type";
+    case DW_TAG_reference_type:
+      return "DW_TAG_reference_type";
+    case DW_TAG_compile_unit:
+      return "DW_TAG_compile_unit";
+    case DW_TAG_string_type:
+      return "DW_TAG_string_type";
+    case DW_TAG_structure_type:
+      return "DW_TAG_structure_type";
+    case DW_TAG_subroutine_type:
+      return "DW_TAG_subroutine_type";
+    case DW_TAG_typedef:
+      return "DW_TAG_typedef";
+    case DW_TAG_union_type:
+      return "DW_TAG_union_type";
+    case DW_TAG_unspecified_parameters:
+      return "DW_TAG_unspecified_parameters";
+    case DW_TAG_variant:
+      return "DW_TAG_variant";
+    case DW_TAG_common_block:
+      return "DW_TAG_common_block";
+    case DW_TAG_common_inclusion:
+      return "DW_TAG_common_inclusion";
+    case DW_TAG_inheritance:
+      return "DW_TAG_inheritance";
+    case DW_TAG_inlined_subroutine:
+      return "DW_TAG_inlined_subroutine";
+    case DW_TAG_module:
+      return "DW_TAG_module";
+    case DW_TAG_ptr_to_member_type:
+      return "DW_TAG_ptr_to_member_type";
+    case DW_TAG_set_type:
+      return "DW_TAG_set_type";
+    case DW_TAG_subrange_type:
+      return "DW_TAG_subrange_type";
+    case DW_TAG_with_stmt:
+      return "DW_TAG_with_stmt";
+    case DW_TAG_access_declaration:
+      return "DW_TAG_access_declaration";
+    case DW_TAG_base_type:
+      return "DW_TAG_base_type";
+    case DW_TAG_catch_block:
+      return "DW_TAG_catch_block";
+    case DW_TAG_const_type:
+      return "DW_TAG_const_type";
+    case DW_TAG_constant:
+      return "DW_TAG_constant";
+    case DW_TAG_enumerator:
+      return "DW_TAG_enumerator";
+    case DW_TAG_file_type:
+      return "DW_TAG_file_type";
+    case DW_TAG_friend:
+      return "DW_TAG_friend";
+    case DW_TAG_namelist:
+      return "DW_TAG_namelist";
+    case DW_TAG_namelist_item:
+      return "DW_TAG_namelist_item";
+    case DW_TAG_packed_type:
+      return "DW_TAG_packed_type";
+    case DW_TAG_subprogram:
+      return "DW_TAG_subprogram";
+    case DW_TAG_template_type_param:
+      return "DW_TAG_template_type_param";
+    case DW_TAG_template_value_param:
+      return "DW_TAG_template_value_param";
+    case DW_TAG_thrown_type:
+      return "DW_TAG_thrown_type";
+    case DW_TAG_try_block:
+      return "DW_TAG_try_block";
+    case DW_TAG_variant_part:
+      return "DW_TAG_variant_part";
+    case DW_TAG_variable:
+      return "DW_TAG_variable";
+    case DW_TAG_volatile_type:
+      return "DW_TAG_volatile_type";
+    case DW_TAG_dwarf_procedure:
+      return "DW_TAG_dwarf_procedure";
+    case DW_TAG_restrict_type:
+      return "DW_TAG_restrict_type";
+    case DW_TAG_interface_type:
+      return "DW_TAG_interface_type";
+    case DW_TAG_namespace:
+      return "DW_TAG_namespace";
+    case DW_TAG_imported_module:
+      return "DW_TAG_imported_module";
+    case DW_TAG_unspecified_type:
+      return "DW_TAG_unspecified_type";
+    case DW_TAG_partial_unit:
+      return "DW_TAG_partial_unit";
+    case DW_TAG_imported_unit:
+      return "DW_TAG_imported_unit";
+    case DW_TAG_condition:
+      return "DW_TAG_condition";
+    case DW_TAG_shared_type:
+      return "DW_TAG_shared_type";
+    case DW_TAG_MIPS_loop:
+      return "DW_TAG_MIPS_loop";
+    case DW_TAG_format_label:
+      return "DW_TAG_format_label";
+    case DW_TAG_function_template:
+      return "DW_TAG_function_template";
+    case DW_TAG_class_template:
+      return "DW_TAG_class_template";
+    case DW_TAG_GNU_BINCL:
+      return "DW_TAG_GNU_BINCL";
+    case DW_TAG_GNU_EINCL:
+      return "DW_TAG_GNU_EINCL";
+    default:
+      return "DW_TAG_<unknown>";
+    }
+}
+
+/* Convert a DWARF attribute code into its string name.  */
+
+static const char *
+dwarf_attr_name (unsigned int attr)
+{
+  switch (attr)
+    {
+    case DW_AT_sibling:
+      return "DW_AT_sibling";
+    case DW_AT_location:
+      return "DW_AT_location";
+    case DW_AT_name:
+      return "DW_AT_name";
+    case DW_AT_ordering:
+      return "DW_AT_ordering";
+    case DW_AT_subscr_data:
+      return "DW_AT_subscr_data";
+    case DW_AT_byte_size:
+      return "DW_AT_byte_size";
+    case DW_AT_bit_offset:
+      return "DW_AT_bit_offset";
+    case DW_AT_bit_size:
+      return "DW_AT_bit_size";
+    case DW_AT_element_list:
+      return "DW_AT_element_list";
+    case DW_AT_stmt_list:
+      return "DW_AT_stmt_list";
+    case DW_AT_low_pc:
+      return "DW_AT_low_pc";
+    case DW_AT_high_pc:
+      return "DW_AT_high_pc";
+    case DW_AT_language:
+      return "DW_AT_language";
+    case DW_AT_member:
+      return "DW_AT_member";
+    case DW_AT_discr:
+      return "DW_AT_discr";
+    case DW_AT_discr_value:
+      return "DW_AT_discr_value";
+    case DW_AT_visibility:
+      return "DW_AT_visibility";
+    case DW_AT_import:
+      return "DW_AT_import";
+    case DW_AT_string_length:
+      return "DW_AT_string_length";
+    case DW_AT_common_reference:
+      return "DW_AT_common_reference";
+    case DW_AT_comp_dir:
+      return "DW_AT_comp_dir";
+    case DW_AT_const_value:
+      return "DW_AT_const_value";
+    case DW_AT_containing_type:
+      return "DW_AT_containing_type";
+    case DW_AT_default_value:
+      return "DW_AT_default_value";
+    case DW_AT_inline:
+      return "DW_AT_inline";
+    case DW_AT_is_optional:
+      return "DW_AT_is_optional";
+    case DW_AT_lower_bound:
+      return "DW_AT_lower_bound";
+    case DW_AT_producer:
+      return "DW_AT_producer";
+    case DW_AT_prototyped:
+      return "DW_AT_prototyped";
+    case DW_AT_return_addr:
+      return "DW_AT_return_addr";
+    case DW_AT_start_scope:
+      return "DW_AT_start_scope";
+    case DW_AT_bit_stride:
+      return "DW_AT_bit_stride";
+    case DW_AT_upper_bound:
+      return "DW_AT_upper_bound";
+    case DW_AT_abstract_origin:
+      return "DW_AT_abstract_origin";
+    case DW_AT_accessibility:
+      return "DW_AT_accessibility";
+    case DW_AT_address_class:
+      return "DW_AT_address_class";
+    case DW_AT_artificial:
+      return "DW_AT_artificial";
+    case DW_AT_base_types:
+      return "DW_AT_base_types";
+    case DW_AT_calling_convention:
+      return "DW_AT_calling_convention";
+    case DW_AT_count:
+      return "DW_AT_count";
+    case DW_AT_data_member_location:
+      return "DW_AT_data_member_location";
+    case DW_AT_decl_column:
+      return "DW_AT_decl_column";
+    case DW_AT_decl_file:
+      return "DW_AT_decl_file";
+    case DW_AT_decl_line:
+      return "DW_AT_decl_line";
+    case DW_AT_declaration:
+      return "DW_AT_declaration";
+    case DW_AT_discr_list:
+      return "DW_AT_discr_list";
+    case DW_AT_encoding:
+      return "DW_AT_encoding";
+    case DW_AT_external:
+      return "DW_AT_external";
+    case DW_AT_frame_base:
+      return "DW_AT_frame_base";
+    case DW_AT_friend:
+      return "DW_AT_friend";
+    case DW_AT_identifier_case:
+      return "DW_AT_identifier_case";
+    case DW_AT_macro_info:
+      return "DW_AT_macro_info";
+    case DW_AT_namelist_items:
+      return "DW_AT_namelist_items";
+    case DW_AT_priority:
+      return "DW_AT_priority";
+    case DW_AT_segment:
+      return "DW_AT_segment";
+    case DW_AT_specification:
+      return "DW_AT_specification";
+    case DW_AT_static_link:
+      return "DW_AT_static_link";
+    case DW_AT_type:
+      return "DW_AT_type";
+    case DW_AT_use_location:
+      return "DW_AT_use_location";
+    case DW_AT_variable_parameter:
+      return "DW_AT_variable_parameter";
+    case DW_AT_virtuality:
+      return "DW_AT_virtuality";
+    case DW_AT_vtable_elem_location:
+      return "DW_AT_vtable_elem_location";
+
+    case DW_AT_allocated:
+      return "DW_AT_allocated";
+    case DW_AT_associated:
+      return "DW_AT_associated";
+    case DW_AT_data_location:
+      return "DW_AT_data_location";
+    case DW_AT_byte_stride:
+      return "DW_AT_byte_stride";
+    case DW_AT_entry_pc:
+      return "DW_AT_entry_pc";
+    case DW_AT_use_UTF8:
+      return "DW_AT_use_UTF8";
+    case DW_AT_extension:
+      return "DW_AT_extension";
+    case DW_AT_ranges:
+      return "DW_AT_ranges";
+    case DW_AT_trampoline:
+      return "DW_AT_trampoline";
+    case DW_AT_call_column:
+      return "DW_AT_call_column";
+    case DW_AT_call_file:
+      return "DW_AT_call_file";
+    case DW_AT_call_line:
+      return "DW_AT_call_line";
+
+    case DW_AT_MIPS_fde:
+      return "DW_AT_MIPS_fde";
+    case DW_AT_MIPS_loop_begin:
+      return "DW_AT_MIPS_loop_begin";
+    case DW_AT_MIPS_tail_loop_begin:
+      return "DW_AT_MIPS_tail_loop_begin";
+    case DW_AT_MIPS_epilog_begin:
+      return "DW_AT_MIPS_epilog_begin";
+    case DW_AT_MIPS_loop_unroll_factor:
+      return "DW_AT_MIPS_loop_unroll_factor";
+    case DW_AT_MIPS_software_pipeline_depth:
+      return "DW_AT_MIPS_software_pipeline_depth";
+    case DW_AT_MIPS_linkage_name:
+      return "DW_AT_MIPS_linkage_name";
+    case DW_AT_MIPS_stride:
+      return "DW_AT_MIPS_stride";
+    case DW_AT_MIPS_abstract_name:
+      return "DW_AT_MIPS_abstract_name";
+    case DW_AT_MIPS_clone_origin:
+      return "DW_AT_MIPS_clone_origin";
+    case DW_AT_MIPS_has_inlines:
+      return "DW_AT_MIPS_has_inlines";
+
+    case DW_AT_sf_names:
+      return "DW_AT_sf_names";
+    case DW_AT_src_info:
+      return "DW_AT_src_info";
+    case DW_AT_mac_info:
+      return "DW_AT_mac_info";
+    case DW_AT_src_coords:
+      return "DW_AT_src_coords";
+    case DW_AT_body_begin:
+      return "DW_AT_body_begin";
+    case DW_AT_body_end:
+      return "DW_AT_body_end";
+    case DW_AT_GNU_vector:
+      return "DW_AT_GNU_vector";
+
+    case DW_AT_VMS_rtnbeg_pd_address:
+      return "DW_AT_VMS_rtnbeg_pd_address";
+
+    default:
+      return "DW_AT_<unknown>";
+    }
+}
+
+/* Convert a DWARF value form code into its string name.  */
+
+static const char *
+dwarf_form_name (unsigned int form)
+{
+  switch (form)
+    {
+    case DW_FORM_addr:
+      return "DW_FORM_addr";
+    case DW_FORM_block2:
+      return "DW_FORM_block2";
+    case DW_FORM_block4:
+      return "DW_FORM_block4";
+    case DW_FORM_data2:
+      return "DW_FORM_data2";
+    case DW_FORM_data4:
+      return "DW_FORM_data4";
+    case DW_FORM_data8:
+      return "DW_FORM_data8";
+    case DW_FORM_string:
+      return "DW_FORM_string";
+    case DW_FORM_block:
+      return "DW_FORM_block";
+    case DW_FORM_block1:
+      return "DW_FORM_block1";
+    case DW_FORM_data1:
+      return "DW_FORM_data1";
+    case DW_FORM_flag:
+      return "DW_FORM_flag";
+    case DW_FORM_sdata:
+      return "DW_FORM_sdata";
+    case DW_FORM_strp:
+      return "DW_FORM_strp";
+    case DW_FORM_udata:
+      return "DW_FORM_udata";
+    case DW_FORM_ref_addr:
+      return "DW_FORM_ref_addr";
+    case DW_FORM_ref1:
+      return "DW_FORM_ref1";
+    case DW_FORM_ref2:
+      return "DW_FORM_ref2";
+    case DW_FORM_ref4:
+      return "DW_FORM_ref4";
+    case DW_FORM_ref8:
+      return "DW_FORM_ref8";
+    case DW_FORM_ref_udata:
+      return "DW_FORM_ref_udata";
+    case DW_FORM_indirect:
+      return "DW_FORM_indirect";
+    default:
+      return "DW_FORM_<unknown>";
+    }
+}
+
+/* Determine the "ultimate origin" of a decl.  The decl may be an inlined
+   instance of an inlined instance of a decl which is local to an inline
+   function, so we have to trace all of the way back through the origin chain
+   to find out what sort of node actually served as the original seed for the
+   given block.  */
+
+static tree
+decl_ultimate_origin (const_tree decl)
+{
+  if (!CODE_CONTAINS_STRUCT (TREE_CODE (decl), TS_DECL_COMMON))
+    return NULL_TREE;
+
+  /* output_inline_function sets DECL_ABSTRACT_ORIGIN for all the
+     nodes in the function to point to themselves; ignore that if
+     we're trying to output the abstract instance of this function.  */
+  if (DECL_ABSTRACT (decl) && DECL_ABSTRACT_ORIGIN (decl) == decl)
+    return NULL_TREE;
+
+  /* Since the DECL_ABSTRACT_ORIGIN for a DECL is supposed to be the
+     most distant ancestor, this should never happen.  */
+  gcc_assert (!DECL_FROM_INLINE (DECL_ORIGIN (decl)));
+
+  return DECL_ABSTRACT_ORIGIN (decl);
+}
+
+/* Get the class to which DECL belongs, if any.  In g++, the DECL_CONTEXT
+   of a virtual function may refer to a base class, so we check the 'this'
+   parameter.  */
+
+static tree
+decl_class_context (tree decl)
+{
+  tree context = NULL_TREE;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL || ! DECL_VINDEX (decl))
+    context = DECL_CONTEXT (decl);
+  else
+    context = TYPE_MAIN_VARIANT
+      (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl)))));
+
+  if (context && !TYPE_P (context))
+    context = NULL_TREE;
+
+  return context;
+}
+
+/* Add an attribute/value pair to a DIE.  */
+
+static inline void
+add_dwarf_attr (dw_die_ref die, dw_attr_ref attr)
+{
+  /* Maybe this should be an assert?  */
+  if (die == NULL)
+    return;
+
+  if (die->die_attr == NULL)
+    die->die_attr = VEC_alloc (dw_attr_node, gc, 1);
+  VEC_safe_push (dw_attr_node, gc, die->die_attr, attr);
+}
+
+static inline enum dw_val_class
+AT_class (dw_attr_ref a)
+{
+  return a->dw_attr_val.val_class;
+}
+
+/* Add a flag value attribute to a DIE.  */
+
+static inline void
+add_AT_flag (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int flag)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_flag;
+  attr.dw_attr_val.v.val_flag = flag;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline unsigned
+AT_flag (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_flag);
+  return a->dw_attr_val.v.val_flag;
+}
+
+/* Add a signed integer attribute value to a DIE.  */
+
+static inline void
+add_AT_int (dw_die_ref die, enum dwarf_attribute attr_kind, HOST_WIDE_INT int_val)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_const;
+  attr.dw_attr_val.v.val_int = int_val;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline HOST_WIDE_INT
+AT_int (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_const);
+  return a->dw_attr_val.v.val_int;
+}
+
+/* Add an unsigned integer attribute value to a DIE.  */
+
+static inline void
+add_AT_unsigned (dw_die_ref die, enum dwarf_attribute attr_kind,
+		 unsigned HOST_WIDE_INT unsigned_val)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_unsigned_const;
+  attr.dw_attr_val.v.val_unsigned = unsigned_val;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline unsigned HOST_WIDE_INT
+AT_unsigned (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_unsigned_const);
+  return a->dw_attr_val.v.val_unsigned;
+}
+
+/* Add an unsigned double integer attribute value to a DIE.  */
+
+static inline void
+add_AT_long_long (dw_die_ref die, enum dwarf_attribute attr_kind,
+		  long unsigned int val_hi, long unsigned int val_low)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_long_long;
+  attr.dw_attr_val.v.val_long_long.hi = val_hi;
+  attr.dw_attr_val.v.val_long_long.low = val_low;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add a floating point attribute value to a DIE and return it.  */
+
+static inline void
+add_AT_vec (dw_die_ref die, enum dwarf_attribute attr_kind,
+	    unsigned int length, unsigned int elt_size, unsigned char *array)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_vec;
+  attr.dw_attr_val.v.val_vec.length = length;
+  attr.dw_attr_val.v.val_vec.elt_size = elt_size;
+  attr.dw_attr_val.v.val_vec.array = array;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Hash and equality functions for debug_str_hash.  */
+
+static hashval_t
+debug_str_do_hash (const void *x)
+{
+  return htab_hash_string (((const struct indirect_string_node *)x)->str);
+}
+
+static int
+debug_str_eq (const void *x1, const void *x2)
+{
+  return strcmp ((((const struct indirect_string_node *)x1)->str),
+		 (const char *)x2) == 0;
+}
+
+/* Add a string attribute value to a DIE.  */
+
+static inline void
+add_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind, const char *str)
+{
+  dw_attr_node attr;
+  struct indirect_string_node *node;
+  void **slot;
+
+  if (! debug_str_hash)
+    debug_str_hash = htab_create_ggc (10, debug_str_do_hash,
+				      debug_str_eq, NULL);
+
+  slot = htab_find_slot_with_hash (debug_str_hash, str,
+				   htab_hash_string (str), INSERT);
+  if (*slot == NULL)
+    {
+      node = (struct indirect_string_node *)
+	       ggc_alloc_cleared (sizeof (struct indirect_string_node));
+      node->str = ggc_strdup (str);
+      *slot = node;
+    }
+  else
+    node = (struct indirect_string_node *) *slot;
+
+  node->refcount++;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_str;
+  attr.dw_attr_val.v.val_str = node;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline const char *
+AT_string (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_str);
+  return a->dw_attr_val.v.val_str->str;
+}
+
+/* Find out whether a string should be output inline in DIE
+   or out-of-line in .debug_str section.  */
+
+static int
+AT_string_form (dw_attr_ref a)
+{
+  struct indirect_string_node *node;
+  unsigned int len;
+  char label[32];
+
+  gcc_assert (a && AT_class (a) == dw_val_class_str);
+
+  node = a->dw_attr_val.v.val_str;
+  if (node->form)
+    return node->form;
+
+  len = strlen (node->str) + 1;
+
+  /* If the string is shorter or equal to the size of the reference, it is
+     always better to put it inline.  */
+  if (len <= DWARF_OFFSET_SIZE || node->refcount == 0)
+    return node->form = DW_FORM_string;
+
+  /* If we cannot expect the linker to merge strings in .debug_str
+     section, only put it into .debug_str if it is worth even in this
+     single module.  */
+  if ((debug_str_section->common.flags & SECTION_MERGE) == 0
+      && (len - DWARF_OFFSET_SIZE) * node->refcount <= len)
+    return node->form = DW_FORM_string;
+
+  ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
+  ++dw2_string_counter;
+  node->label = xstrdup (label);
+
+  return node->form = DW_FORM_strp;
+}
+
+/* Add a DIE reference attribute value to a DIE.  */
+
+static inline void
+add_AT_die_ref (dw_die_ref die, enum dwarf_attribute attr_kind, dw_die_ref targ_die)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_die_ref;
+  attr.dw_attr_val.v.val_die_ref.die = targ_die;
+  attr.dw_attr_val.v.val_die_ref.external = 0;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add an AT_specification attribute to a DIE, and also make the back
+   pointer from the specification to the definition.  */
+
+static inline void
+add_AT_specification (dw_die_ref die, dw_die_ref targ_die)
+{
+  add_AT_die_ref (die, DW_AT_specification, targ_die);
+  gcc_assert (!targ_die->die_definition);
+  targ_die->die_definition = die;
+}
+
+static inline dw_die_ref
+AT_ref (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_die_ref);
+  return a->dw_attr_val.v.val_die_ref.die;
+}
+
+static inline int
+AT_ref_external (dw_attr_ref a)
+{
+  if (a && AT_class (a) == dw_val_class_die_ref)
+    return a->dw_attr_val.v.val_die_ref.external;
+
+  return 0;
+}
+
+static inline void
+set_AT_ref_external (dw_attr_ref a, int i)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_die_ref);
+  a->dw_attr_val.v.val_die_ref.external = i;
+}
+
+/* Add an FDE reference attribute value to a DIE.  */
+
+static inline void
+add_AT_fde_ref (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int targ_fde)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_fde_ref;
+  attr.dw_attr_val.v.val_fde_index = targ_fde;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add a location description attribute value to a DIE.  */
+
+static inline void
+add_AT_loc (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_descr_ref loc)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_loc;
+  attr.dw_attr_val.v.val_loc = loc;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline dw_loc_descr_ref
+AT_loc (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_loc);
+  return a->dw_attr_val.v.val_loc;
+}
+
+static inline void
+add_AT_loc_list (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_list_ref loc_list)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_loc_list;
+  attr.dw_attr_val.v.val_loc_list = loc_list;
+  add_dwarf_attr (die, &attr);
+  have_location_lists = true;
+}
+
+static inline dw_loc_list_ref
+AT_loc_list (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_loc_list);
+  return a->dw_attr_val.v.val_loc_list;
+}
+
+/* Add an address constant attribute value to a DIE.  */
+
+static inline void
+add_AT_addr (dw_die_ref die, enum dwarf_attribute attr_kind, rtx addr)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_addr;
+  attr.dw_attr_val.v.val_addr = addr;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Get the RTX from to an address DIE attribute.  */
+
+static inline rtx
+AT_addr (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_addr);
+  return a->dw_attr_val.v.val_addr;
+}
+
+/* Add a file attribute value to a DIE.  */
+
+static inline void
+add_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind,
+	     struct dwarf_file_data *fd)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_file;
+  attr.dw_attr_val.v.val_file = fd;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Get the dwarf_file_data from a file DIE attribute.  */
+
+static inline struct dwarf_file_data *
+AT_file (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_file);
+  return a->dw_attr_val.v.val_file;
+}
+
+/* Add a label identifier attribute value to a DIE.  */
+
+static inline void
+add_AT_lbl_id (dw_die_ref die, enum dwarf_attribute attr_kind, const char *lbl_id)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_lbl_id;
+  attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id);
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add a section offset attribute value to a DIE, an offset into the
+   debug_line section.  */
+
+static inline void
+add_AT_lineptr (dw_die_ref die, enum dwarf_attribute attr_kind,
+		const char *label)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_lineptr;
+  attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add a section offset attribute value to a DIE, an offset into the
+   debug_macinfo section.  */
+
+static inline void
+add_AT_macptr (dw_die_ref die, enum dwarf_attribute attr_kind,
+	       const char *label)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_macptr;
+  attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add an offset attribute value to a DIE.  */
+
+static inline void
+add_AT_offset (dw_die_ref die, enum dwarf_attribute attr_kind,
+	       unsigned HOST_WIDE_INT offset)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_offset;
+  attr.dw_attr_val.v.val_offset = offset;
+  add_dwarf_attr (die, &attr);
+}
+
+/* Add an range_list attribute value to a DIE.  */
+
+static void
+add_AT_range_list (dw_die_ref die, enum dwarf_attribute attr_kind,
+		   long unsigned int offset)
+{
+  dw_attr_node attr;
+
+  attr.dw_attr = attr_kind;
+  attr.dw_attr_val.val_class = dw_val_class_range_list;
+  attr.dw_attr_val.v.val_offset = offset;
+  add_dwarf_attr (die, &attr);
+}
+
+static inline const char *
+AT_lbl (dw_attr_ref a)
+{
+  gcc_assert (a && (AT_class (a) == dw_val_class_lbl_id
+		    || AT_class (a) == dw_val_class_lineptr
+		    || AT_class (a) == dw_val_class_macptr));
+  return a->dw_attr_val.v.val_lbl_id;
+}
+
+/* Get the attribute of type attr_kind.  */
+
+static dw_attr_ref
+get_AT (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a;
+  unsigned ix;
+  dw_die_ref spec = NULL;
+
+  if (! die)
+    return NULL;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (a->dw_attr == attr_kind)
+      return a;
+    else if (a->dw_attr == DW_AT_specification
+	     || a->dw_attr == DW_AT_abstract_origin)
+      spec = AT_ref (a);
+
+  if (spec)
+    return get_AT (spec, attr_kind);
+
+  return NULL;
+}
+
+/* Return the "low pc" attribute value, typically associated with a subprogram
+   DIE.  Return null if the "low pc" attribute is either not present, or if it
+   cannot be represented as an assembler label identifier.  */
+
+static inline const char *
+get_AT_low_pc (dw_die_ref die)
+{
+  dw_attr_ref a = get_AT (die, DW_AT_low_pc);
+
+  return a ? AT_lbl (a) : NULL;
+}
+
+/* Return the "high pc" attribute value, typically associated with a subprogram
+   DIE.  Return null if the "high pc" attribute is either not present, or if it
+   cannot be represented as an assembler label identifier.  */
+
+static inline const char *
+get_AT_hi_pc (dw_die_ref die)
+{
+  dw_attr_ref a = get_AT (die, DW_AT_high_pc);
+
+  return a ? AT_lbl (a) : NULL;
+}
+
+/* Return the value of the string attribute designated by ATTR_KIND, or
+   NULL if it is not present.  */
+
+static inline const char *
+get_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a = get_AT (die, attr_kind);
+
+  return a ? AT_string (a) : NULL;
+}
+
+/* Return the value of the flag attribute designated by ATTR_KIND, or -1
+   if it is not present.  */
+
+static inline int
+get_AT_flag (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a = get_AT (die, attr_kind);
+
+  return a ? AT_flag (a) : 0;
+}
+
+/* Return the value of the unsigned attribute designated by ATTR_KIND, or 0
+   if it is not present.  */
+
+static inline unsigned
+get_AT_unsigned (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a = get_AT (die, attr_kind);
+
+  return a ? AT_unsigned (a) : 0;
+}
+
+static inline dw_die_ref
+get_AT_ref (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a = get_AT (die, attr_kind);
+
+  return a ? AT_ref (a) : NULL;
+}
+
+static inline struct dwarf_file_data *
+get_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a = get_AT (die, attr_kind);
+
+  return a ? AT_file (a) : NULL;
+}
+
+/* Return TRUE if the language is C or C++.  */
+
+static inline bool
+is_c_family (void)
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language);
+
+  return (lang == DW_LANG_C || lang == DW_LANG_C89 || lang == DW_LANG_ObjC
+	  || lang == DW_LANG_C99
+	  || lang == DW_LANG_C_plus_plus || lang == DW_LANG_ObjC_plus_plus);
+}
+
+/* Return TRUE if the language is C++.  */
+
+static inline bool
+is_cxx (void)
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language);
+
+  return lang == DW_LANG_C_plus_plus || lang == DW_LANG_ObjC_plus_plus;
+}
+
+/* Return TRUE if the language is Fortran.  */
+
+static inline bool
+is_fortran (void)
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language);
+
+  return (lang == DW_LANG_Fortran77
+	  || lang == DW_LANG_Fortran90
+	  || lang == DW_LANG_Fortran95);
+}
+
+/* Return TRUE if the language is Java.  */
+
+static inline bool
+is_java (void)
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language);
+
+  return lang == DW_LANG_Java;
+}
+
+/* Return TRUE if the language is Ada.  */
+
+static inline bool
+is_ada (void)
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language);
+
+  return lang == DW_LANG_Ada95 || lang == DW_LANG_Ada83;
+}
+
+/* Remove the specified attribute if present.  */
+
+static void
+remove_AT (dw_die_ref die, enum dwarf_attribute attr_kind)
+{
+  dw_attr_ref a;
+  unsigned ix;
+
+  if (! die)
+    return;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (a->dw_attr == attr_kind)
+      {
+	if (AT_class (a) == dw_val_class_str)
+	  if (a->dw_attr_val.v.val_str->refcount)
+	    a->dw_attr_val.v.val_str->refcount--;
+
+	/* VEC_ordered_remove should help reduce the number of abbrevs
+	   that are needed.  */
+	VEC_ordered_remove (dw_attr_node, die->die_attr, ix);
+	return;
+      }
+}
+
+/* Remove CHILD from its parent.  PREV must have the property that
+   PREV->DIE_SIB == CHILD.  Does not alter CHILD.  */
+
+static void
+remove_child_with_prev (dw_die_ref child, dw_die_ref prev)
+{
+  gcc_assert (child->die_parent == prev->die_parent);
+  gcc_assert (prev->die_sib == child);
+  if (prev == child)
+    {
+      gcc_assert (child->die_parent->die_child == child);
+      prev = NULL;
+    }
+  else
+    prev->die_sib = child->die_sib;
+  if (child->die_parent->die_child == child)
+    child->die_parent->die_child = prev;
+}
+
+/* Remove child DIE whose die_tag is TAG.  Do nothing if no child
+   matches TAG.  */
+
+static void
+remove_child_TAG (dw_die_ref die, enum dwarf_tag tag)
+{
+  dw_die_ref c;
+
+  c = die->die_child;
+  if (c) do {
+    dw_die_ref prev = c;
+    c = c->die_sib;
+    while (c->die_tag == tag)
+      {
+	remove_child_with_prev (c, prev);
+	/* Might have removed every child.  */
+	if (c == c->die_sib)
+	  return;
+	c = c->die_sib;
+      }
+  } while (c != die->die_child);
+}
+
+/* Add a CHILD_DIE as the last child of DIE.  */
+
+static void
+add_child_die (dw_die_ref die, dw_die_ref child_die)
+{
+  /* FIXME this should probably be an assert.  */
+  if (! die || ! child_die)
+    return;
+  gcc_assert (die != child_die);
+
+  child_die->die_parent = die;
+  if (die->die_child)
+    {
+      child_die->die_sib = die->die_child->die_sib;
+      die->die_child->die_sib = child_die;
+    }
+  else
+    child_die->die_sib = child_die;
+  die->die_child = child_die;
+}
+
+/* Move CHILD, which must be a child of PARENT or the DIE for which PARENT
+   is the specification, to the end of PARENT's list of children.
+   This is done by removing and re-adding it.  */
+
+static void
+splice_child_die (dw_die_ref parent, dw_die_ref child)
+{
+  dw_die_ref p;
+
+  /* We want the declaration DIE from inside the class, not the
+     specification DIE at toplevel.  */
+  if (child->die_parent != parent)
+    {
+      dw_die_ref tmp = get_AT_ref (child, DW_AT_specification);
+
+      if (tmp)
+	child = tmp;
+    }
+
+  gcc_assert (child->die_parent == parent
+	      || (child->die_parent
+		  == get_AT_ref (parent, DW_AT_specification)));
+
+  for (p = child->die_parent->die_child; ; p = p->die_sib)
+    if (p->die_sib == child)
+      {
+	remove_child_with_prev (child, p);
+	break;
+      }
+
+  add_child_die (parent, child);
+}
+
+/* Return a pointer to a newly created DIE node.  */
+
+static inline dw_die_ref
+new_die (enum dwarf_tag tag_value, dw_die_ref parent_die, tree t)
+{
+  dw_die_ref die = GGC_CNEW (die_node);
+
+  die->die_tag = tag_value;
+
+  if (parent_die != NULL)
+    add_child_die (parent_die, die);
+  else
+    {
+      limbo_die_node *limbo_node;
+
+      limbo_node = GGC_CNEW (limbo_die_node);
+      limbo_node->die = die;
+      limbo_node->created_for = t;
+      limbo_node->next = limbo_die_list;
+      limbo_die_list = limbo_node;
+    }
+
+  return die;
+}
+
+/* Return the DIE associated with the given type specifier.  */
+
+static inline dw_die_ref
+lookup_type_die (tree type)
+{
+  return TYPE_SYMTAB_DIE (type);
+}
+
+/* Equate a DIE to a given type specifier.  */
+
+static inline void
+equate_type_number_to_die (tree type, dw_die_ref type_die)
+{
+  TYPE_SYMTAB_DIE (type) = type_die;
+}
+
+/* Returns a hash value for X (which really is a die_struct).  */
+
+static hashval_t
+decl_die_table_hash (const void *x)
+{
+  return (hashval_t) ((const_dw_die_ref) x)->decl_id;
+}
+
+/* Return nonzero if decl_id of die_struct X is the same as UID of decl *Y.  */
+
+static int
+decl_die_table_eq (const void *x, const void *y)
+{
+  return (((const_dw_die_ref) x)->decl_id == DECL_UID ((const_tree) y));
+}
+
+/* Return the DIE associated with a given declaration.  */
+
+static inline dw_die_ref
+lookup_decl_die (tree decl)
+{
+  return (dw_die_ref) htab_find_with_hash (decl_die_table, decl, DECL_UID (decl));
+}
+
+/* Returns a hash value for X (which really is a var_loc_list).  */
+
+static hashval_t
+decl_loc_table_hash (const void *x)
+{
+  return (hashval_t) ((const var_loc_list *) x)->decl_id;
+}
+
+/* Return nonzero if decl_id of var_loc_list X is the same as
+   UID of decl *Y.  */
+
+static int
+decl_loc_table_eq (const void *x, const void *y)
+{
+  return (((const var_loc_list *) x)->decl_id == DECL_UID ((const_tree) y));
+}
+
+/* Return the var_loc list associated with a given declaration.  */
+
+static inline var_loc_list *
+lookup_decl_loc (const_tree decl)
+{
+  return (var_loc_list *)
+    htab_find_with_hash (decl_loc_table, decl, DECL_UID (decl));
+}
+
+/* Equate a DIE to a particular declaration.  */
+
+static void
+equate_decl_number_to_die (tree decl, dw_die_ref decl_die)
+{
+  unsigned int decl_id = DECL_UID (decl);
+  void **slot;
+
+  slot = htab_find_slot_with_hash (decl_die_table, decl, decl_id, INSERT);
+  *slot = decl_die;
+  decl_die->decl_id = decl_id;
+}
+
+/* Add a variable location node to the linked list for DECL.  */
+
+static void
+add_var_loc_to_decl (tree decl, struct var_loc_node *loc)
+{
+  unsigned int decl_id = DECL_UID (decl);
+  var_loc_list *temp;
+  void **slot;
+
+  slot = htab_find_slot_with_hash (decl_loc_table, decl, decl_id, INSERT);
+  if (*slot == NULL)
+    {
+      temp = GGC_CNEW (var_loc_list);
+      temp->decl_id = decl_id;
+      *slot = temp;
+    }
+  else
+    temp = (var_loc_list *) *slot;
+
+  if (temp->last)
+    {
+      /* If the current location is the same as the end of the list,
+	 and either both or neither of the locations is uninitialized,
+	 we have nothing to do.  */
+      if ((!rtx_equal_p (NOTE_VAR_LOCATION_LOC (temp->last->var_loc_note),
+			 NOTE_VAR_LOCATION_LOC (loc->var_loc_note)))
+	  || ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
+	       != NOTE_VAR_LOCATION_STATUS (loc->var_loc_note))
+	      && ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
+		   == VAR_INIT_STATUS_UNINITIALIZED)
+		  || (NOTE_VAR_LOCATION_STATUS (loc->var_loc_note)
+		      == VAR_INIT_STATUS_UNINITIALIZED))))
+	{
+	  /* Add LOC to the end of list and update LAST.  */
+	  temp->last->next = loc;
+	  temp->last = loc;
+	}
+    }
+  /* Do not add empty location to the beginning of the list.  */
+  else if (NOTE_VAR_LOCATION_LOC (loc->var_loc_note) != NULL_RTX)
+    {
+      temp->first = loc;
+      temp->last = loc;
+    }
+}
+
+/* Keep track of the number of spaces used to indent the
+   output of the debugging routines that print the structure of
+   the DIE internal representation.  */
+static int print_indent;
+
+/* Indent the line the number of spaces given by print_indent.  */
+
+static inline void
+print_spaces (FILE *outfile)
+{
+  fprintf (outfile, "%*s", print_indent, "");
+}
+
+/* Print the information associated with a given DIE, and its children.
+   This routine is a debugging aid only.  */
+
+static void
+print_die (dw_die_ref die, FILE *outfile)
+{
+  dw_attr_ref a;
+  dw_die_ref c;
+  unsigned ix;
+
+  print_spaces (outfile);
+  fprintf (outfile, "DIE %4ld: %s\n",
+	   die->die_offset, dwarf_tag_name (die->die_tag));
+  print_spaces (outfile);
+  fprintf (outfile, "  abbrev id: %lu", die->die_abbrev);
+  fprintf (outfile, " offset: %ld\n", die->die_offset);
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    {
+      print_spaces (outfile);
+      fprintf (outfile, "  %s: ", dwarf_attr_name (a->dw_attr));
+
+      switch (AT_class (a))
+	{
+	case dw_val_class_addr:
+	  fprintf (outfile, "address");
+	  break;
+	case dw_val_class_offset:
+	  fprintf (outfile, "offset");
+	  break;
+	case dw_val_class_loc:
+	  fprintf (outfile, "location descriptor");
+	  break;
+	case dw_val_class_loc_list:
+	  fprintf (outfile, "location list -> label:%s",
+		   AT_loc_list (a)->ll_symbol);
+	  break;
+	case dw_val_class_range_list:
+	  fprintf (outfile, "range list");
+	  break;
+	case dw_val_class_const:
+	  fprintf (outfile, HOST_WIDE_INT_PRINT_DEC, AT_int (a));
+	  break;
+	case dw_val_class_unsigned_const:
+	  fprintf (outfile, HOST_WIDE_INT_PRINT_UNSIGNED, AT_unsigned (a));
+	  break;
+	case dw_val_class_long_long:
+	  fprintf (outfile, "constant (%lu,%lu)",
+		   a->dw_attr_val.v.val_long_long.hi,
+		   a->dw_attr_val.v.val_long_long.low);
+	  break;
+	case dw_val_class_vec:
+	  fprintf (outfile, "floating-point or vector constant");
+	  break;
+	case dw_val_class_flag:
+	  fprintf (outfile, "%u", AT_flag (a));
+	  break;
+	case dw_val_class_die_ref:
+	  if (AT_ref (a) != NULL)
+	    {
+	      if (AT_ref (a)->die_symbol)
+		fprintf (outfile, "die -> label: %s", AT_ref (a)->die_symbol);
+	      else
+		fprintf (outfile, "die -> %ld", AT_ref (a)->die_offset);
+	    }
+	  else
+	    fprintf (outfile, "die -> <null>");
+	  break;
+	case dw_val_class_lbl_id:
+	case dw_val_class_lineptr:
+	case dw_val_class_macptr:
+	  fprintf (outfile, "label: %s", AT_lbl (a));
+	  break;
+	case dw_val_class_str:
+	  if (AT_string (a) != NULL)
+	    fprintf (outfile, "\"%s\"", AT_string (a));
+	  else
+	    fprintf (outfile, "<null>");
+	  break;
+	case dw_val_class_file:
+	  fprintf (outfile, "\"%s\" (%d)", AT_file (a)->filename,
+		   AT_file (a)->emitted_number);
+	  break;
+	default:
+	  break;
+	}
+
+      fprintf (outfile, "\n");
+    }
+
+  if (die->die_child != NULL)
+    {
+      print_indent += 4;
+      FOR_EACH_CHILD (die, c, print_die (c, outfile));
+      print_indent -= 4;
+    }
+  if (print_indent == 0)
+    fprintf (outfile, "\n");
+}
+
+/* Print the contents of the source code line number correspondence table.
+   This routine is a debugging aid only.  */
+
+static void
+print_dwarf_line_table (FILE *outfile)
+{
+  unsigned i;
+  dw_line_info_ref line_info;
+
+  fprintf (outfile, "\n\nDWARF source line information\n");
+  for (i = 1; i < line_info_table_in_use; i++)
+    {
+      line_info = &line_info_table[i];
+      fprintf (outfile, "%5d: %4ld %6ld\n", i,
+	       line_info->dw_file_num,
+	       line_info->dw_line_num);
+    }
+
+  fprintf (outfile, "\n\n");
+}
+
+/* Print the information collected for a given DIE.  */
+
+void
+debug_dwarf_die (dw_die_ref die)
+{
+  print_die (die, stderr);
+}
+
+/* Print all DWARF information collected for the compilation unit.
+   This routine is a debugging aid only.  */
+
+void
+debug_dwarf (void)
+{
+  print_indent = 0;
+  print_die (comp_unit_die, stderr);
+  if (! DWARF2_ASM_LINE_DEBUG_INFO)
+    print_dwarf_line_table (stderr);
+}
+
+/* Start a new compilation unit DIE for an include file.  OLD_UNIT is the CU
+   for the enclosing include file, if any.  BINCL_DIE is the DW_TAG_GNU_BINCL
+   DIE that marks the start of the DIEs for this include file.  */
+
+static dw_die_ref
+push_new_compile_unit (dw_die_ref old_unit, dw_die_ref bincl_die)
+{
+  const char *filename = get_AT_string (bincl_die, DW_AT_name);
+  dw_die_ref new_unit = gen_compile_unit_die (filename);
+
+  new_unit->die_sib = old_unit;
+  return new_unit;
+}
+
+/* Close an include-file CU and reopen the enclosing one.  */
+
+static dw_die_ref
+pop_compile_unit (dw_die_ref old_unit)
+{
+  dw_die_ref new_unit = old_unit->die_sib;
+
+  old_unit->die_sib = NULL;
+  return new_unit;
+}
+
+#define CHECKSUM(FOO) md5_process_bytes (&(FOO), sizeof (FOO), ctx)
+#define CHECKSUM_STRING(FOO) md5_process_bytes ((FOO), strlen (FOO), ctx)
+
+/* Calculate the checksum of a location expression.  */
+
+static inline void
+loc_checksum (dw_loc_descr_ref loc, struct md5_ctx *ctx)
+{
+  CHECKSUM (loc->dw_loc_opc);
+  CHECKSUM (loc->dw_loc_oprnd1);
+  CHECKSUM (loc->dw_loc_oprnd2);
+}
+
+/* Calculate the checksum of an attribute.  */
+
+static void
+attr_checksum (dw_attr_ref at, struct md5_ctx *ctx, int *mark)
+{
+  dw_loc_descr_ref loc;
+  rtx r;
+
+  CHECKSUM (at->dw_attr);
+
+  /* We don't care that this was compiled with a different compiler
+     snapshot; if the output is the same, that's what matters.  */
+  if (at->dw_attr == DW_AT_producer)
+    return;
+
+  switch (AT_class (at))
+    {
+    case dw_val_class_const:
+      CHECKSUM (at->dw_attr_val.v.val_int);
+      break;
+    case dw_val_class_unsigned_const:
+      CHECKSUM (at->dw_attr_val.v.val_unsigned);
+      break;
+    case dw_val_class_long_long:
+      CHECKSUM (at->dw_attr_val.v.val_long_long);
+      break;
+    case dw_val_class_vec:
+      CHECKSUM (at->dw_attr_val.v.val_vec);
+      break;
+    case dw_val_class_flag:
+      CHECKSUM (at->dw_attr_val.v.val_flag);
+      break;
+    case dw_val_class_str:
+      CHECKSUM_STRING (AT_string (at));
+      break;
+
+    case dw_val_class_addr:
+      r = AT_addr (at);
+      gcc_assert (GET_CODE (r) == SYMBOL_REF);
+      CHECKSUM_STRING (XSTR (r, 0));
+      break;
+
+    case dw_val_class_offset:
+      CHECKSUM (at->dw_attr_val.v.val_offset);
+      break;
+
+    case dw_val_class_loc:
+      for (loc = AT_loc (at); loc; loc = loc->dw_loc_next)
+	loc_checksum (loc, ctx);
+      break;
+
+    case dw_val_class_die_ref:
+      die_checksum (AT_ref (at), ctx, mark);
+      break;
+
+    case dw_val_class_fde_ref:
+    case dw_val_class_lbl_id:
+    case dw_val_class_lineptr:
+    case dw_val_class_macptr:
+      break;
+
+    case dw_val_class_file:
+      CHECKSUM_STRING (AT_file (at)->filename);
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* Calculate the checksum of a DIE.  */
+
+static void
+die_checksum (dw_die_ref die, struct md5_ctx *ctx, int *mark)
+{
+  dw_die_ref c;
+  dw_attr_ref a;
+  unsigned ix;
+
+  /* To avoid infinite recursion.  */
+  if (die->die_mark)
+    {
+      CHECKSUM (die->die_mark);
+      return;
+    }
+  die->die_mark = ++(*mark);
+
+  CHECKSUM (die->die_tag);
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    attr_checksum (a, ctx, mark);
+
+  FOR_EACH_CHILD (die, c, die_checksum (c, ctx, mark));
+}
+
+#undef CHECKSUM
+#undef CHECKSUM_STRING
+
+/* Do the location expressions look same?  */
+static inline int
+same_loc_p (dw_loc_descr_ref loc1, dw_loc_descr_ref loc2, int *mark)
+{
+  return loc1->dw_loc_opc == loc2->dw_loc_opc
+	 && same_dw_val_p (&loc1->dw_loc_oprnd1, &loc2->dw_loc_oprnd1, mark)
+	 && same_dw_val_p (&loc1->dw_loc_oprnd2, &loc2->dw_loc_oprnd2, mark);
+}
+
+/* Do the values look the same?  */
+static int
+same_dw_val_p (const dw_val_node *v1, const dw_val_node *v2, int *mark)
+{
+  dw_loc_descr_ref loc1, loc2;
+  rtx r1, r2;
+
+  if (v1->val_class != v2->val_class)
+    return 0;
+
+  switch (v1->val_class)
+    {
+    case dw_val_class_const:
+      return v1->v.val_int == v2->v.val_int;
+    case dw_val_class_unsigned_const:
+      return v1->v.val_unsigned == v2->v.val_unsigned;
+    case dw_val_class_long_long:
+      return v1->v.val_long_long.hi == v2->v.val_long_long.hi
+	     && v1->v.val_long_long.low == v2->v.val_long_long.low;
+    case dw_val_class_vec:
+      if (v1->v.val_vec.length != v2->v.val_vec.length
+	  || v1->v.val_vec.elt_size != v2->v.val_vec.elt_size)
+	return 0;
+      if (memcmp (v1->v.val_vec.array, v2->v.val_vec.array,
+		  v1->v.val_vec.length * v1->v.val_vec.elt_size))
+	return 0;
+      return 1;
+    case dw_val_class_flag:
+      return v1->v.val_flag == v2->v.val_flag;
+    case dw_val_class_str:
+      return !strcmp(v1->v.val_str->str, v2->v.val_str->str);
+
+    case dw_val_class_addr:
+      r1 = v1->v.val_addr;
+      r2 = v2->v.val_addr;
+      if (GET_CODE (r1) != GET_CODE (r2))
+	return 0;
+      gcc_assert (GET_CODE (r1) == SYMBOL_REF);
+      return !strcmp (XSTR (r1, 0), XSTR (r2, 0));
+
+    case dw_val_class_offset:
+      return v1->v.val_offset == v2->v.val_offset;
+
+    case dw_val_class_loc:
+      for (loc1 = v1->v.val_loc, loc2 = v2->v.val_loc;
+	   loc1 && loc2;
+	   loc1 = loc1->dw_loc_next, loc2 = loc2->dw_loc_next)
+	if (!same_loc_p (loc1, loc2, mark))
+	  return 0;
+      return !loc1 && !loc2;
+
+    case dw_val_class_die_ref:
+      return same_die_p (v1->v.val_die_ref.die, v2->v.val_die_ref.die, mark);
+
+    case dw_val_class_fde_ref:
+    case dw_val_class_lbl_id:
+    case dw_val_class_lineptr:
+    case dw_val_class_macptr:
+      return 1;
+
+    case dw_val_class_file:
+      return v1->v.val_file == v2->v.val_file;
+
+    default:
+      return 1;
+    }
+}
+
+/* Do the attributes look the same?  */
+
+static int
+same_attr_p (dw_attr_ref at1, dw_attr_ref at2, int *mark)
+{
+  if (at1->dw_attr != at2->dw_attr)
+    return 0;
+
+  /* We don't care that this was compiled with a different compiler
+     snapshot; if the output is the same, that's what matters. */
+  if (at1->dw_attr == DW_AT_producer)
+    return 1;
+
+  return same_dw_val_p (&at1->dw_attr_val, &at2->dw_attr_val, mark);
+}
+
+/* Do the dies look the same?  */
+
+static int
+same_die_p (dw_die_ref die1, dw_die_ref die2, int *mark)
+{
+  dw_die_ref c1, c2;
+  dw_attr_ref a1;
+  unsigned ix;
+
+  /* To avoid infinite recursion.  */
+  if (die1->die_mark)
+    return die1->die_mark == die2->die_mark;
+  die1->die_mark = die2->die_mark = ++(*mark);
+
+  if (die1->die_tag != die2->die_tag)
+    return 0;
+
+  if (VEC_length (dw_attr_node, die1->die_attr)
+      != VEC_length (dw_attr_node, die2->die_attr))
+    return 0;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die1->die_attr, ix, a1); ix++)
+    if (!same_attr_p (a1, VEC_index (dw_attr_node, die2->die_attr, ix), mark))
+      return 0;
+
+  c1 = die1->die_child;
+  c2 = die2->die_child;
+  if (! c1)
+    {
+      if (c2)
+	return 0;
+    }
+  else
+    for (;;)
+      {
+	if (!same_die_p (c1, c2, mark))
+	  return 0;
+	c1 = c1->die_sib;
+	c2 = c2->die_sib;
+	if (c1 == die1->die_child)
+	  {
+	    if (c2 == die2->die_child)
+	      break;
+	    else
+	      return 0;
+	  }
+    }
+
+  return 1;
+}
+
+/* Do the dies look the same?  Wrapper around same_die_p.  */
+
+static int
+same_die_p_wrap (dw_die_ref die1, dw_die_ref die2)
+{
+  int mark = 0;
+  int ret = same_die_p (die1, die2, &mark);
+
+  unmark_all_dies (die1);
+  unmark_all_dies (die2);
+
+  return ret;
+}
+
+/* The prefix to attach to symbols on DIEs in the current comdat debug
+   info section.  */
+static char *comdat_symbol_id;
+
+/* The index of the current symbol within the current comdat CU.  */
+static unsigned int comdat_symbol_number;
+
+/* Calculate the MD5 checksum of the compilation unit DIE UNIT_DIE and its
+   children, and set comdat_symbol_id accordingly.  */
+
+static void
+compute_section_prefix (dw_die_ref unit_die)
+{
+  const char *die_name = get_AT_string (unit_die, DW_AT_name);
+  const char *base = die_name ? lbasename (die_name) : "anonymous";
+  char *name = XALLOCAVEC (char, strlen (base) + 64);
+  char *p;
+  int i, mark;
+  unsigned char checksum[16];
+  struct md5_ctx ctx;
+
+  /* Compute the checksum of the DIE, then append part of it as hex digits to
+     the name filename of the unit.  */
+
+  md5_init_ctx (&ctx);
+  mark = 0;
+  die_checksum (unit_die, &ctx, &mark);
+  unmark_all_dies (unit_die);
+  md5_finish_ctx (&ctx, checksum);
+
+  sprintf (name, "%s.", base);
+  clean_symbol_name (name);
+
+  p = name + strlen (name);
+  for (i = 0; i < 4; i++)
+    {
+      sprintf (p, "%.2x", checksum[i]);
+      p += 2;
+    }
+
+  comdat_symbol_id = unit_die->die_symbol = xstrdup (name);
+  comdat_symbol_number = 0;
+}
+
+/* Returns nonzero if DIE represents a type, in the sense of TYPE_P.  */
+
+static int
+is_type_die (dw_die_ref die)
+{
+  switch (die->die_tag)
+    {
+    case DW_TAG_array_type:
+    case DW_TAG_class_type:
+    case DW_TAG_interface_type:
+    case DW_TAG_enumeration_type:
+    case DW_TAG_pointer_type:
+    case DW_TAG_reference_type:
+    case DW_TAG_string_type:
+    case DW_TAG_structure_type:
+    case DW_TAG_subroutine_type:
+    case DW_TAG_union_type:
+    case DW_TAG_ptr_to_member_type:
+    case DW_TAG_set_type:
+    case DW_TAG_subrange_type:
+    case DW_TAG_base_type:
+    case DW_TAG_const_type:
+    case DW_TAG_file_type:
+    case DW_TAG_packed_type:
+    case DW_TAG_volatile_type:
+    case DW_TAG_typedef:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+/* Returns 1 iff C is the sort of DIE that should go into a COMDAT CU.
+   Basically, we want to choose the bits that are likely to be shared between
+   compilations (types) and leave out the bits that are specific to individual
+   compilations (functions).  */
+
+static int
+is_comdat_die (dw_die_ref c)
+{
+  /* I think we want to leave base types and __vtbl_ptr_type in the main CU, as
+     we do for stabs.  The advantage is a greater likelihood of sharing between
+     objects that don't include headers in the same order (and therefore would
+     put the base types in a different comdat).  jason 8/28/00 */
+
+  if (c->die_tag == DW_TAG_base_type)
+    return 0;
+
+  if (c->die_tag == DW_TAG_pointer_type
+      || c->die_tag == DW_TAG_reference_type
+      || c->die_tag == DW_TAG_const_type
+      || c->die_tag == DW_TAG_volatile_type)
+    {
+      dw_die_ref t = get_AT_ref (c, DW_AT_type);
+
+      return t ? is_comdat_die (t) : 0;
+    }
+
+  return is_type_die (c);
+}
+
+/* Returns 1 iff C is the sort of DIE that might be referred to from another
+   compilation unit.  */
+
+static int
+is_symbol_die (dw_die_ref c)
+{
+  return (is_type_die (c)
+	  || (get_AT (c, DW_AT_declaration)
+	      && !get_AT (c, DW_AT_specification))
+	  || c->die_tag == DW_TAG_namespace
+	  || c->die_tag == DW_TAG_module);
+}
+
+static char *
+gen_internal_sym (const char *prefix)
+{
+  char buf[256];
+
+  ASM_GENERATE_INTERNAL_LABEL (buf, prefix, label_num++);
+  return xstrdup (buf);
+}
+
+/* Assign symbols to all worthy DIEs under DIE.  */
+
+static void
+assign_symbol_names (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  if (is_symbol_die (die))
+    {
+      if (comdat_symbol_id)
+	{
+	  char *p = XALLOCAVEC (char, strlen (comdat_symbol_id) + 64);
+
+	  sprintf (p, "%s.%s.%x", DIE_LABEL_PREFIX,
+		   comdat_symbol_id, comdat_symbol_number++);
+	  die->die_symbol = xstrdup (p);
+	}
+      else
+	die->die_symbol = gen_internal_sym ("LDIE");
+    }
+
+  FOR_EACH_CHILD (die, c, assign_symbol_names (c));
+}
+
+struct cu_hash_table_entry
+{
+  dw_die_ref cu;
+  unsigned min_comdat_num, max_comdat_num;
+  struct cu_hash_table_entry *next;
+};
+
+/* Routines to manipulate hash table of CUs.  */
+static hashval_t
+htab_cu_hash (const void *of)
+{
+  const struct cu_hash_table_entry *const entry =
+    (const struct cu_hash_table_entry *) of;
+
+  return htab_hash_string (entry->cu->die_symbol);
+}
+
+static int
+htab_cu_eq (const void *of1, const void *of2)
+{
+  const struct cu_hash_table_entry *const entry1 =
+    (const struct cu_hash_table_entry *) of1;
+  const struct die_struct *const entry2 = (const struct die_struct *) of2;
+
+  return !strcmp (entry1->cu->die_symbol, entry2->die_symbol);
+}
+
+static void
+htab_cu_del (void *what)
+{
+  struct cu_hash_table_entry *next,
+    *entry = (struct cu_hash_table_entry *) what;
+
+  while (entry)
+    {
+      next = entry->next;
+      free (entry);
+      entry = next;
+    }
+}
+
+/* Check whether we have already seen this CU and set up SYM_NUM
+   accordingly.  */
+static int
+check_duplicate_cu (dw_die_ref cu, htab_t htable, unsigned int *sym_num)
+{
+  struct cu_hash_table_entry dummy;
+  struct cu_hash_table_entry **slot, *entry, *last = &dummy;
+
+  dummy.max_comdat_num = 0;
+
+  slot = (struct cu_hash_table_entry **)
+    htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol),
+	INSERT);
+  entry = *slot;
+
+  for (; entry; last = entry, entry = entry->next)
+    {
+      if (same_die_p_wrap (cu, entry->cu))
+	break;
+    }
+
+  if (entry)
+    {
+      *sym_num = entry->min_comdat_num;
+      return 1;
+    }
+
+  entry = XCNEW (struct cu_hash_table_entry);
+  entry->cu = cu;
+  entry->min_comdat_num = *sym_num = last->max_comdat_num;
+  entry->next = *slot;
+  *slot = entry;
+
+  return 0;
+}
+
+/* Record SYM_NUM to record of CU in HTABLE.  */
+static void
+record_comdat_symbol_number (dw_die_ref cu, htab_t htable, unsigned int sym_num)
+{
+  struct cu_hash_table_entry **slot, *entry;
+
+  slot = (struct cu_hash_table_entry **)
+    htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol),
+	NO_INSERT);
+  entry = *slot;
+
+  entry->max_comdat_num = sym_num;
+}
+
+/* Traverse the DIE (which is always comp_unit_die), and set up
+   additional compilation units for each of the include files we see
+   bracketed by BINCL/EINCL.  */
+
+static void
+break_out_includes (dw_die_ref die)
+{
+  dw_die_ref c;
+  dw_die_ref unit = NULL;
+  limbo_die_node *node, **pnode;
+  htab_t cu_hash_table;
+
+  c = die->die_child;
+  if (c) do {
+    dw_die_ref prev = c;
+    c = c->die_sib;
+    while (c->die_tag == DW_TAG_GNU_BINCL || c->die_tag == DW_TAG_GNU_EINCL
+	   || (unit && is_comdat_die (c)))
+      {
+	dw_die_ref next = c->die_sib;
+
+	/* This DIE is for a secondary CU; remove it from the main one.  */
+	remove_child_with_prev (c, prev);
+
+	if (c->die_tag == DW_TAG_GNU_BINCL)
+	  unit = push_new_compile_unit (unit, c);
+	else if (c->die_tag == DW_TAG_GNU_EINCL)
+	  unit = pop_compile_unit (unit);
+	else
+	  add_child_die (unit, c);
+	c = next;
+	if (c == die->die_child)
+	  break;
+      }
+  } while (c != die->die_child);
+
+#if 0
+  /* We can only use this in debugging, since the frontend doesn't check
+     to make sure that we leave every include file we enter.  */
+  gcc_assert (!unit);
+#endif
+
+  assign_symbol_names (die);
+  cu_hash_table = htab_create (10, htab_cu_hash, htab_cu_eq, htab_cu_del);
+  for (node = limbo_die_list, pnode = &limbo_die_list;
+       node;
+       node = node->next)
+    {
+      int is_dupl;
+
+      compute_section_prefix (node->die);
+      is_dupl = check_duplicate_cu (node->die, cu_hash_table,
+			&comdat_symbol_number);
+      assign_symbol_names (node->die);
+      if (is_dupl)
+	*pnode = node->next;
+      else
+	{
+	  pnode = &node->next;
+	  record_comdat_symbol_number (node->die, cu_hash_table,
+		comdat_symbol_number);
+	}
+    }
+  htab_delete (cu_hash_table);
+}
+
+/* Traverse the DIE and add a sibling attribute if it may have the
+   effect of speeding up access to siblings.  To save some space,
+   avoid generating sibling attributes for DIE's without children.  */
+
+static void
+add_sibling_attributes (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  if (! die->die_child)
+    return;
+
+  if (die->die_parent && die != die->die_parent->die_child)
+    add_AT_die_ref (die, DW_AT_sibling, die->die_sib);
+
+  FOR_EACH_CHILD (die, c, add_sibling_attributes (c));
+}
+
+/* Output all location lists for the DIE and its children.  */
+
+static void
+output_location_lists (dw_die_ref die)
+{
+  dw_die_ref c;
+  dw_attr_ref a;
+  unsigned ix;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (AT_class (a) == dw_val_class_loc_list)
+      output_loc_list (AT_loc_list (a));
+
+  FOR_EACH_CHILD (die, c, output_location_lists (c));
+}
+
+/* The format of each DIE (and its attribute value pairs) is encoded in an
+   abbreviation table.  This routine builds the abbreviation table and assigns
+   a unique abbreviation id for each abbreviation entry.  The children of each
+   die are visited recursively.  */
+
+static void
+build_abbrev_table (dw_die_ref die)
+{
+  unsigned long abbrev_id;
+  unsigned int n_alloc;
+  dw_die_ref c;
+  dw_attr_ref a;
+  unsigned ix;
+
+  /* Scan the DIE references, and mark as external any that refer to
+     DIEs from other CUs (i.e. those which are not marked).  */
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (AT_class (a) == dw_val_class_die_ref
+	&& AT_ref (a)->die_mark == 0)
+      {
+	gcc_assert (AT_ref (a)->die_symbol);
+
+	set_AT_ref_external (a, 1);
+      }
+
+  for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
+    {
+      dw_die_ref abbrev = abbrev_die_table[abbrev_id];
+      dw_attr_ref die_a, abbrev_a;
+      unsigned ix;
+      bool ok = true;
+
+      if (abbrev->die_tag != die->die_tag)
+	continue;
+      if ((abbrev->die_child != NULL) != (die->die_child != NULL))
+	continue;
+
+      if (VEC_length (dw_attr_node, abbrev->die_attr)
+	  != VEC_length (dw_attr_node, die->die_attr))
+	continue;
+
+      for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, die_a); ix++)
+	{
+	  abbrev_a = VEC_index (dw_attr_node, abbrev->die_attr, ix);
+	  if ((abbrev_a->dw_attr != die_a->dw_attr)
+	      || (value_format (abbrev_a) != value_format (die_a)))
+	    {
+	      ok = false;
+	      break;
+	    }
+	}
+      if (ok)
+	break;
+    }
+
+  if (abbrev_id >= abbrev_die_table_in_use)
+    {
+      if (abbrev_die_table_in_use >= abbrev_die_table_allocated)
+	{
+	  n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT;
+	  abbrev_die_table = GGC_RESIZEVEC (dw_die_ref, abbrev_die_table,
+					    n_alloc);
+
+	  memset (&abbrev_die_table[abbrev_die_table_allocated], 0,
+		 (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref));
+	  abbrev_die_table_allocated = n_alloc;
+	}
+
+      ++abbrev_die_table_in_use;
+      abbrev_die_table[abbrev_id] = die;
+    }
+
+  die->die_abbrev = abbrev_id;
+  FOR_EACH_CHILD (die, c, build_abbrev_table (c));
+}
+
+/* Return the power-of-two number of bytes necessary to represent VALUE.  */
+
+static int
+constant_size (unsigned HOST_WIDE_INT value)
+{
+  int log;
+
+  if (value == 0)
+    log = 0;
+  else
+    log = floor_log2 (value);
+
+  log = log / 8;
+  log = 1 << (floor_log2 (log) + 1);
+
+  return log;
+}
+
+/* Return the size of a DIE as it is represented in the
+   .debug_info section.  */
+
+static unsigned long
+size_of_die (dw_die_ref die)
+{
+  unsigned long size = 0;
+  dw_attr_ref a;
+  unsigned ix;
+
+  size += size_of_uleb128 (die->die_abbrev);
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    {
+      switch (AT_class (a))
+	{
+	case dw_val_class_addr:
+	  size += DWARF2_ADDR_SIZE;
+	  break;
+	case dw_val_class_offset:
+	  size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_loc:
+	  {
+	    unsigned long lsize = size_of_locs (AT_loc (a));
+
+	    /* Block length.  */
+	    size += constant_size (lsize);
+	    size += lsize;
+	  }
+	  break;
+	case dw_val_class_loc_list:
+	  size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_range_list:
+	  size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_const:
+	  size += size_of_sleb128 (AT_int (a));
+	  break;
+	case dw_val_class_unsigned_const:
+	  size += constant_size (AT_unsigned (a));
+	  break;
+	case dw_val_class_long_long:
+	  size += 1 + 2*HOST_BITS_PER_LONG/HOST_BITS_PER_CHAR; /* block */
+	  break;
+	case dw_val_class_vec:
+	  size += constant_size (a->dw_attr_val.v.val_vec.length
+				 * a->dw_attr_val.v.val_vec.elt_size)
+		  + a->dw_attr_val.v.val_vec.length
+		    * a->dw_attr_val.v.val_vec.elt_size; /* block */
+	  break;
+	case dw_val_class_flag:
+	  size += 1;
+	  break;
+	case dw_val_class_die_ref:
+	  if (AT_ref_external (a))
+	    size += DWARF2_ADDR_SIZE;
+	  else
+	    size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_fde_ref:
+	  size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_lbl_id:
+	  size += DWARF2_ADDR_SIZE;
+	  break;
+	case dw_val_class_lineptr:
+	case dw_val_class_macptr:
+	  size += DWARF_OFFSET_SIZE;
+	  break;
+	case dw_val_class_str:
+	  if (AT_string_form (a) == DW_FORM_strp)
+	    size += DWARF_OFFSET_SIZE;
+	  else
+	    size += strlen (a->dw_attr_val.v.val_str->str) + 1;
+	  break;
+	case dw_val_class_file:
+	  size += constant_size (maybe_emit_file (a->dw_attr_val.v.val_file));
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  return size;
+}
+
+/* Size the debugging information associated with a given DIE.  Visits the
+   DIE's children recursively.  Updates the global variable next_die_offset, on
+   each time through.  Uses the current value of next_die_offset to update the
+   die_offset field in each DIE.  */
+
+static void
+calc_die_sizes (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  die->die_offset = next_die_offset;
+  next_die_offset += size_of_die (die);
+
+  FOR_EACH_CHILD (die, c, calc_die_sizes (c));
+
+  if (die->die_child != NULL)
+    /* Count the null byte used to terminate sibling lists.  */
+    next_die_offset += 1;
+}
+
+/* Set the marks for a die and its children.  We do this so
+   that we know whether or not a reference needs to use FORM_ref_addr; only
+   DIEs in the same CU will be marked.  We used to clear out the offset
+   and use that as the flag, but ran into ordering problems.  */
+
+static void
+mark_dies (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  gcc_assert (!die->die_mark);
+
+  die->die_mark = 1;
+  FOR_EACH_CHILD (die, c, mark_dies (c));
+}
+
+/* Clear the marks for a die and its children.  */
+
+static void
+unmark_dies (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  gcc_assert (die->die_mark);
+
+  die->die_mark = 0;
+  FOR_EACH_CHILD (die, c, unmark_dies (c));
+}
+
+/* Clear the marks for a die, its children and referred dies.  */
+
+static void
+unmark_all_dies (dw_die_ref die)
+{
+  dw_die_ref c;
+  dw_attr_ref a;
+  unsigned ix;
+
+  if (!die->die_mark)
+    return;
+  die->die_mark = 0;
+
+  FOR_EACH_CHILD (die, c, unmark_all_dies (c));
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (AT_class (a) == dw_val_class_die_ref)
+      unmark_all_dies (AT_ref (a));
+}
+
+/* Return the size of the .debug_pubnames or .debug_pubtypes table
+   generated for the compilation unit.  */
+
+static unsigned long
+size_of_pubnames (VEC (pubname_entry, gc) * names)
+{
+  unsigned long size;
+  unsigned i;
+  pubname_ref p;
+
+  size = DWARF_PUBNAMES_HEADER_SIZE;
+  for (i = 0; VEC_iterate (pubname_entry, names, i, p); i++)
+    if (names != pubtype_table
+	|| p->die->die_offset != 0
+	|| !flag_eliminate_unused_debug_types)
+      size += strlen (p->name) + DWARF_OFFSET_SIZE + 1;
+
+  size += DWARF_OFFSET_SIZE;
+  return size;
+}
+
+/* Return the size of the information in the .debug_aranges section.  */
+
+static unsigned long
+size_of_aranges (void)
+{
+  unsigned long size;
+
+  size = DWARF_ARANGES_HEADER_SIZE;
+
+  /* Count the address/length pair for this compilation unit.  */
+  if (text_section_used)
+    size += 2 * DWARF2_ADDR_SIZE;
+  if (cold_text_section_used)
+    size += 2 * DWARF2_ADDR_SIZE;
+  size += 2 * DWARF2_ADDR_SIZE * arange_table_in_use;
+
+  /* Count the two zero words used to terminated the address range table.  */
+  size += 2 * DWARF2_ADDR_SIZE;
+  return size;
+}
+
+/* Select the encoding of an attribute value.  */
+
+static enum dwarf_form
+value_format (dw_attr_ref a)
+{
+  switch (a->dw_attr_val.val_class)
+    {
+    case dw_val_class_addr:
+      return DW_FORM_addr;
+    case dw_val_class_range_list:
+    case dw_val_class_offset:
+    case dw_val_class_loc_list:
+      switch (DWARF_OFFSET_SIZE)
+	{
+	case 4:
+	  return DW_FORM_data4;
+	case 8:
+	  return DW_FORM_data8;
+	default:
+	  gcc_unreachable ();
+	}
+    case dw_val_class_loc:
+      switch (constant_size (size_of_locs (AT_loc (a))))
+	{
+	case 1:
+	  return DW_FORM_block1;
+	case 2:
+	  return DW_FORM_block2;
+	default:
+	  gcc_unreachable ();
+	}
+    case dw_val_class_const:
+      return DW_FORM_sdata;
+    case dw_val_class_unsigned_const:
+      switch (constant_size (AT_unsigned (a)))
+	{
+	case 1:
+	  return DW_FORM_data1;
+	case 2:
+	  return DW_FORM_data2;
+	case 4:
+	  return DW_FORM_data4;
+	case 8:
+	  return DW_FORM_data8;
+	default:
+	  gcc_unreachable ();
+	}
+    case dw_val_class_long_long:
+      return DW_FORM_block1;
+    case dw_val_class_vec:
+      switch (constant_size (a->dw_attr_val.v.val_vec.length
+			     * a->dw_attr_val.v.val_vec.elt_size))
+	{
+	case 1:
+	  return DW_FORM_block1;
+	case 2:
+	  return DW_FORM_block2;
+	case 4:
+	  return DW_FORM_block4;
+	default:
+	  gcc_unreachable ();
+	}
+    case dw_val_class_flag:
+      return DW_FORM_flag;
+    case dw_val_class_die_ref:
+      if (AT_ref_external (a))
+	return DW_FORM_ref_addr;
+      else
+	return DW_FORM_ref;
+    case dw_val_class_fde_ref:
+      return DW_FORM_data;
+    case dw_val_class_lbl_id:
+      return DW_FORM_addr;
+    case dw_val_class_lineptr:
+    case dw_val_class_macptr:
+      return DW_FORM_data;
+    case dw_val_class_str:
+      return AT_string_form (a);
+    case dw_val_class_file:
+      switch (constant_size (maybe_emit_file (a->dw_attr_val.v.val_file)))
+	{
+	case 1:
+	  return DW_FORM_data1;
+	case 2:
+	  return DW_FORM_data2;
+	case 4:
+	  return DW_FORM_data4;
+	default:
+	  gcc_unreachable ();
+	}
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Output the encoding of an attribute value.  */
+
+static void
+output_value_format (dw_attr_ref a)
+{
+  enum dwarf_form form = value_format (a);
+
+  dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form));
+}
+
+/* Output the .debug_abbrev section which defines the DIE abbreviation
+   table.  */
+
+static void
+output_abbrev_section (void)
+{
+  unsigned long abbrev_id;
+
+  for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
+    {
+      dw_die_ref abbrev = abbrev_die_table[abbrev_id];
+      unsigned ix;
+      dw_attr_ref a_attr;
+
+      dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)");
+      dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
+				   dwarf_tag_name (abbrev->die_tag));
+
+      if (abbrev->die_child != NULL)
+	dw2_asm_output_data (1, DW_children_yes, "DW_children_yes");
+      else
+	dw2_asm_output_data (1, DW_children_no, "DW_children_no");
+
+      for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr);
+	   ix++)
+	{
+	  dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)",
+				       dwarf_attr_name (a_attr->dw_attr));
+	  output_value_format (a_attr);
+	}
+
+      dw2_asm_output_data (1, 0, NULL);
+      dw2_asm_output_data (1, 0, NULL);
+    }
+
+  /* Terminate the table.  */
+  dw2_asm_output_data (1, 0, NULL);
+}
+
+/* Output a symbol we can use to refer to this DIE from another CU.  */
+
+static inline void
+output_die_symbol (dw_die_ref die)
+{
+  char *sym = die->die_symbol;
+
+  if (sym == 0)
+    return;
+
+  if (strncmp (sym, DIE_LABEL_PREFIX, sizeof (DIE_LABEL_PREFIX) - 1) == 0)
+    /* We make these global, not weak; if the target doesn't support
+       .linkonce, it doesn't support combining the sections, so debugging
+       will break.  */
+    targetm.asm_out.globalize_label (asm_out_file, sym);
+
+  ASM_OUTPUT_LABEL (asm_out_file, sym);
+}
+
+/* Return a new location list, given the begin and end range, and the
+   expression. gensym tells us whether to generate a new internal symbol for
+   this location list node, which is done for the head of the list only.  */
+
+static inline dw_loc_list_ref
+new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
+	      const char *section, unsigned int gensym)
+{
+  dw_loc_list_ref retlist = GGC_CNEW (dw_loc_list_node);
+
+  retlist->begin = begin;
+  retlist->end = end;
+  retlist->expr = expr;
+  retlist->section = section;
+  if (gensym)
+    retlist->ll_symbol = gen_internal_sym ("LLST");
+
+  return retlist;
+}
+
+/* Add a location description expression to a location list.  */
+
+static inline void
+add_loc_descr_to_loc_list (dw_loc_list_ref *list_head, dw_loc_descr_ref descr,
+			   const char *begin, const char *end,
+			   const char *section)
+{
+  dw_loc_list_ref *d;
+
+  /* Find the end of the chain.  */
+  for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next)
+    ;
+
+  /* Add a new location list node to the list.  */
+  *d = new_loc_list (descr, begin, end, section, 0);
+}
+
+/* Output the location list given to us.  */
+
+static void
+output_loc_list (dw_loc_list_ref list_head)
+{
+  dw_loc_list_ref curr = list_head;
+
+  ASM_OUTPUT_LABEL (asm_out_file, list_head->ll_symbol);
+
+  /* Walk the location list, and output each range + expression.  */
+  for (curr = list_head; curr != NULL; curr = curr->dw_loc_next)
+    {
+      unsigned long size;
+      /* Don't output an entry that starts and ends at the same address.  */
+      if (strcmp (curr->begin, curr->end) == 0)
+	continue;
+      if (!have_multiple_function_sections)
+	{
+	  dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->begin, curr->section,
+				"Location list begin address (%s)",
+				list_head->ll_symbol);
+	  dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->end, curr->section,
+				"Location list end address (%s)",
+				list_head->ll_symbol);
+	}
+      else
+	{
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->begin,
+			       "Location list begin address (%s)",
+			       list_head->ll_symbol);
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->end,
+			       "Location list end address (%s)",
+			       list_head->ll_symbol);
+	}
+      size = size_of_locs (curr->expr);
+
+      /* Output the block length for this list of location operations.  */
+      gcc_assert (size <= 0xffff);
+      dw2_asm_output_data (2, size, "%s", "Location expression size");
+
+      output_loc_sequence (curr->expr);
+    }
+
+  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
+		       "Location list terminator begin (%s)",
+		       list_head->ll_symbol);
+  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
+		       "Location list terminator end (%s)",
+		       list_head->ll_symbol);
+}
+
+/* Output the DIE and its attributes.  Called recursively to generate
+   the definitions of each child DIE.  */
+
+static void
+output_die (dw_die_ref die)
+{
+  dw_attr_ref a;
+  dw_die_ref c;
+  unsigned long size;
+  unsigned ix;
+
+  /* If someone in another CU might refer to us, set up a symbol for
+     them to point to.  */
+  if (die->die_symbol)
+    output_die_symbol (die);
+
+  dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (0x%lx) %s)",
+			       (unsigned long)die->die_offset,
+			       dwarf_tag_name (die->die_tag));
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    {
+      const char *name = dwarf_attr_name (a->dw_attr);
+
+      switch (AT_class (a))
+	{
+	case dw_val_class_addr:
+	  dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name);
+	  break;
+
+	case dw_val_class_offset:
+	  dw2_asm_output_data (DWARF_OFFSET_SIZE, a->dw_attr_val.v.val_offset,
+			       "%s", name);
+	  break;
+
+	case dw_val_class_range_list:
+	  {
+	    char *p = strchr (ranges_section_label, '\0');
+
+	    sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX,
+		     a->dw_attr_val.v.val_offset);
+	    dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label,
+				   debug_ranges_section, "%s", name);
+	    *p = '\0';
+	  }
+	  break;
+
+	case dw_val_class_loc:
+	  size = size_of_locs (AT_loc (a));
+
+	  /* Output the block length for this list of location operations.  */
+	  dw2_asm_output_data (constant_size (size), size, "%s", name);
+
+	  output_loc_sequence (AT_loc (a));
+	  break;
+
+	case dw_val_class_const:
+	  /* ??? It would be slightly more efficient to use a scheme like is
+	     used for unsigned constants below, but gdb 4.x does not sign
+	     extend.  Gdb 5.x does sign extend.  */
+	  dw2_asm_output_data_sleb128 (AT_int (a), "%s", name);
+	  break;
+
+	case dw_val_class_unsigned_const:
+	  dw2_asm_output_data (constant_size (AT_unsigned (a)),
+			       AT_unsigned (a), "%s", name);
+	  break;
+
+	case dw_val_class_long_long:
+	  {
+	    unsigned HOST_WIDE_INT first, second;
+
+	    dw2_asm_output_data (1,
+				 2 * HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
+				 "%s", name);
+
+	    if (WORDS_BIG_ENDIAN)
+	      {
+		first = a->dw_attr_val.v.val_long_long.hi;
+		second = a->dw_attr_val.v.val_long_long.low;
+	      }
+	    else
+	      {
+		first = a->dw_attr_val.v.val_long_long.low;
+		second = a->dw_attr_val.v.val_long_long.hi;
+	      }
+
+	    dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
+				 first, "long long constant");
+	    dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
+				 second, NULL);
+	  }
+	  break;
+
+	case dw_val_class_vec:
+	  {
+	    unsigned int elt_size = a->dw_attr_val.v.val_vec.elt_size;
+	    unsigned int len = a->dw_attr_val.v.val_vec.length;
+	    unsigned int i;
+	    unsigned char *p;
+
+	    dw2_asm_output_data (constant_size (len * elt_size),
+				 len * elt_size, "%s", name);
+	    if (elt_size > sizeof (HOST_WIDE_INT))
+	      {
+		elt_size /= 2;
+		len *= 2;
+	      }
+	    for (i = 0, p = a->dw_attr_val.v.val_vec.array;
+		 i < len;
+		 i++, p += elt_size)
+	      dw2_asm_output_data (elt_size, extract_int (p, elt_size),
+				   "fp or vector constant word %u", i);
+	    break;
+	  }
+
+	case dw_val_class_flag:
+	  dw2_asm_output_data (1, AT_flag (a), "%s", name);
+	  break;
+
+	case dw_val_class_loc_list:
+	  {
+	    char *sym = AT_loc_list (a)->ll_symbol;
+
+	    gcc_assert (sym);
+	    dw2_asm_output_offset (DWARF_OFFSET_SIZE, sym, debug_loc_section,
+				   "%s", name);
+	  }
+	  break;
+
+	case dw_val_class_die_ref:
+	  if (AT_ref_external (a))
+	    {
+	      char *sym = AT_ref (a)->die_symbol;
+
+	      gcc_assert (sym);
+	      dw2_asm_output_offset (DWARF2_ADDR_SIZE, sym, debug_info_section,
+				     "%s", name);
+	    }
+	  else
+	    {
+	      gcc_assert (AT_ref (a)->die_offset);
+	      dw2_asm_output_data (DWARF_OFFSET_SIZE, AT_ref (a)->die_offset,
+				   "%s", name);
+	    }
+	  break;
+
+	case dw_val_class_fde_ref:
+	  {
+	    char l1[20];
+
+	    ASM_GENERATE_INTERNAL_LABEL (l1, FDE_LABEL,
+					 a->dw_attr_val.v.val_fde_index * 2);
+	    dw2_asm_output_offset (DWARF_OFFSET_SIZE, l1, debug_frame_section,
+				   "%s", name);
+	  }
+	  break;
+
+	case dw_val_class_lbl_id:
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name);
+	  break;
+
+	case dw_val_class_lineptr:
+	  dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
+				 debug_line_section, "%s", name);
+	  break;
+
+	case dw_val_class_macptr:
+	  dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
+				 debug_macinfo_section, "%s", name);
+	  break;
+
+	case dw_val_class_str:
+	  if (AT_string_form (a) == DW_FORM_strp)
+	    dw2_asm_output_offset (DWARF_OFFSET_SIZE,
+				   a->dw_attr_val.v.val_str->label,
+				   debug_str_section,
+				   "%s: \"%s\"", name, AT_string (a));
+	  else
+	    dw2_asm_output_nstring (AT_string (a), -1, "%s", name);
+	  break;
+
+	case dw_val_class_file:
+	  {
+	    int f = maybe_emit_file (a->dw_attr_val.v.val_file);
+
+	    dw2_asm_output_data (constant_size (f), f, "%s (%s)", name,
+				 a->dw_attr_val.v.val_file->filename);
+	    break;
+	  }
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  FOR_EACH_CHILD (die, c, output_die (c));
+
+  /* Add null byte to terminate sibling list.  */
+  if (die->die_child != NULL)
+    dw2_asm_output_data (1, 0, "end of children of DIE 0x%lx",
+			 (unsigned long) die->die_offset);
+}
+
+/* Output the compilation unit that appears at the beginning of the
+   .debug_info section, and precedes the DIE descriptions.  */
+
+static void
+output_compilation_unit_header (void)
+{
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE,
+		       next_die_offset - DWARF_INITIAL_LENGTH_SIZE,
+		       "Length of Compilation Unit Info");
+  dw2_asm_output_data (2, DWARF_VERSION, "DWARF version number");
+  dw2_asm_output_offset (DWARF_OFFSET_SIZE, abbrev_section_label,
+			 debug_abbrev_section,
+			 "Offset Into Abbrev. Section");
+  dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
+}
+
+/* Output the compilation unit DIE and its children.  */
+
+static void
+output_comp_unit (dw_die_ref die, int output_if_empty)
+{
+  const char *secname;
+  char *oldsym, *tmp;
+
+  /* Unless we are outputting main CU, we may throw away empty ones.  */
+  if (!output_if_empty && die->die_child == NULL)
+    return;
+
+  /* Even if there are no children of this DIE, we must output the information
+     about the compilation unit.  Otherwise, on an empty translation unit, we
+     will generate a present, but empty, .debug_info section.  IRIX 6.5 `nm'
+     will then complain when examining the file.  First mark all the DIEs in
+     this CU so we know which get local refs.  */
+  mark_dies (die);
+
+  build_abbrev_table (die);
+
+  /* Initialize the beginning DIE offset - and calculate sizes/offsets.  */
+  next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
+  calc_die_sizes (die);
+
+  oldsym = die->die_symbol;
+  if (oldsym)
+    {
+      tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
+
+      sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
+      secname = tmp;
+      die->die_symbol = NULL;
+      switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
+    }
+  else
+    switch_to_section (debug_info_section);
+
+  /* Output debugging information.  */
+  output_compilation_unit_header ();
+  output_die (die);
+
+  /* Leave the marks on the main CU, so we can check them in
+     output_pubnames.  */
+  if (oldsym)
+    {
+      unmark_dies (die);
+      die->die_symbol = oldsym;
+    }
+}
+
+/* Return the DWARF2/3 pubname associated with a decl.  */
+
+static const char *
+dwarf2_name (tree decl, int scope)
+{
+  return lang_hooks.dwarf_name (decl, scope ? 1 : 0);
+}
+
+/* Add a new entry to .debug_pubnames if appropriate.  */
+
+static void
+add_pubname_string (const char *str, dw_die_ref die)
+{
+  pubname_entry e;
+
+  e.die = die;
+  e.name = xstrdup (str);
+  VEC_safe_push (pubname_entry, gc, pubname_table, &e);
+}
+
+static void
+add_pubname (tree decl, dw_die_ref die)
+{
+
+  if (TREE_PUBLIC (decl))
+    add_pubname_string (dwarf2_name (decl, 1), die);
+}
+
+/* Add a new entry to .debug_pubtypes if appropriate.  */
+
+static void
+add_pubtype (tree decl, dw_die_ref die)
+{
+  pubname_entry e;
+
+  e.name = NULL;
+  if ((TREE_PUBLIC (decl)
+       || die->die_parent == comp_unit_die)
+      && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl)))
+    {
+      e.die = die;
+      if (TYPE_P (decl))
+	{
+	  if (TYPE_NAME (decl))
+	    {
+	      if (TREE_CODE (TYPE_NAME (decl)) == IDENTIFIER_NODE)
+		e.name = IDENTIFIER_POINTER (TYPE_NAME (decl));
+	      else if (TREE_CODE (TYPE_NAME (decl)) == TYPE_DECL
+		       && DECL_NAME (TYPE_NAME (decl)))
+		e.name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl)));
+	      else
+	       e.name = xstrdup ((const char *) get_AT_string (die, DW_AT_name));
+	    }
+	}
+      else
+	e.name = xstrdup (dwarf2_name (decl, 1));
+
+      /* If we don't have a name for the type, there's no point in adding
+	 it to the table.  */
+      if (e.name && e.name[0] != '\0')
+	VEC_safe_push (pubname_entry, gc, pubtype_table, &e);
+    }
+}
+
+/* Output the public names table used to speed up access to externally
+   visible names; or the public types table used to find type definitions.  */
+
+static void
+output_pubnames (VEC (pubname_entry, gc) * names)
+{
+  unsigned i;
+  unsigned long pubnames_length = size_of_pubnames (names);
+  pubname_ref pub;
+
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  if (names == pubname_table)
+    dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
+			 "Length of Public Names Info");
+  else
+    dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
+			 "Length of Public Type Names Info");
+  dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version");
+  dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+			 debug_info_section,
+			 "Offset of Compilation Unit Info");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset,
+		       "Compilation Unit Length");
+
+  for (i = 0; VEC_iterate (pubname_entry, names, i, pub); i++)
+    {
+      /* We shouldn't see pubnames for DIEs outside of the main CU.  */
+      if (names == pubname_table)
+	gcc_assert (pub->die->die_mark);
+
+      if (names != pubtype_table
+	  || pub->die->die_offset != 0
+	  || !flag_eliminate_unused_debug_types)
+	{
+	  dw2_asm_output_data (DWARF_OFFSET_SIZE, pub->die->die_offset,
+			       "DIE offset");
+
+	  dw2_asm_output_nstring (pub->name, -1, "external name");
+	}
+    }
+
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL);
+}
+
+/* Add a new entry to .debug_aranges if appropriate.  */
+
+static void
+add_arange (tree decl, dw_die_ref die)
+{
+  if (! DECL_SECTION_NAME (decl))
+    return;
+
+  if (arange_table_in_use == arange_table_allocated)
+    {
+      arange_table_allocated += ARANGE_TABLE_INCREMENT;
+      arange_table = GGC_RESIZEVEC (dw_die_ref, arange_table,
+				    arange_table_allocated);
+      memset (arange_table + arange_table_in_use, 0,
+	      ARANGE_TABLE_INCREMENT * sizeof (dw_die_ref));
+    }
+
+  arange_table[arange_table_in_use++] = die;
+}
+
+/* Output the information that goes into the .debug_aranges table.
+   Namely, define the beginning and ending address range of the
+   text section generated for this compilation unit.  */
+
+static void
+output_aranges (void)
+{
+  unsigned i;
+  unsigned long aranges_length = size_of_aranges ();
+
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length,
+		       "Length of Address Ranges Info");
+  dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version");
+  dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+			 debug_info_section,
+			 "Offset of Compilation Unit Info");
+  dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address");
+  dw2_asm_output_data (1, 0, "Size of Segment Descriptor");
+
+  /* We need to align to twice the pointer size here.  */
+  if (DWARF_ARANGES_PAD_SIZE)
+    {
+      /* Pad using a 2 byte words so that padding is correct for any
+	 pointer size.  */
+      dw2_asm_output_data (2, 0, "Pad to %d byte boundary",
+			   2 * DWARF2_ADDR_SIZE);
+      for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2)
+	dw2_asm_output_data (2, 0, NULL);
+    }
+
+  /* It is necessary not to output these entries if the sections were
+     not used; if the sections were not used, the length will be 0 and
+     the address may end up as 0 if the section is discarded by ld
+     --gc-sections, leaving an invalid (0, 0) entry that can be
+     confused with the terminator.  */
+  if (text_section_used)
+    {
+      dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_section_label, "Address");
+      dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
+			    text_section_label, "Length");
+    }
+  if (cold_text_section_used)
+    {
+      dw2_asm_output_addr (DWARF2_ADDR_SIZE, cold_text_section_label,
+			   "Address");
+      dw2_asm_output_delta (DWARF2_ADDR_SIZE, cold_end_label,
+			    cold_text_section_label, "Length");
+    }
+
+  for (i = 0; i < arange_table_in_use; i++)
+    {
+      dw_die_ref die = arange_table[i];
+
+      /* We shouldn't see aranges for DIEs outside of the main CU.  */
+      gcc_assert (die->die_mark);
+
+      if (die->die_tag == DW_TAG_subprogram)
+	{
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, get_AT_low_pc (die),
+			       "Address");
+	  dw2_asm_output_delta (DWARF2_ADDR_SIZE, get_AT_hi_pc (die),
+				get_AT_low_pc (die), "Length");
+	}
+      else
+	{
+	  /* A static variable; extract the symbol from DW_AT_location.
+	     Note that this code isn't currently hit, as we only emit
+	     aranges for functions (jason 9/23/99).  */
+	  dw_attr_ref a = get_AT (die, DW_AT_location);
+	  dw_loc_descr_ref loc;
+
+	  gcc_assert (a && AT_class (a) == dw_val_class_loc);
+
+	  loc = AT_loc (a);
+	  gcc_assert (loc->dw_loc_opc == DW_OP_addr);
+
+	  dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE,
+				   loc->dw_loc_oprnd1.v.val_addr, "Address");
+	  dw2_asm_output_data (DWARF2_ADDR_SIZE,
+			       get_AT_unsigned (die, DW_AT_byte_size),
+			       "Length");
+	}
+    }
+
+  /* Output the terminator words.  */
+  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+}
+
+/* Add a new entry to .debug_ranges.  Return the offset at which it
+   was placed.  */
+
+static unsigned int
+add_ranges_num (int num)
+{
+  unsigned int in_use = ranges_table_in_use;
+
+  if (in_use == ranges_table_allocated)
+    {
+      ranges_table_allocated += RANGES_TABLE_INCREMENT;
+      ranges_table = GGC_RESIZEVEC (struct dw_ranges_struct, ranges_table,
+				    ranges_table_allocated);
+      memset (ranges_table + ranges_table_in_use, 0,
+	      RANGES_TABLE_INCREMENT * sizeof (struct dw_ranges_struct));
+    }
+
+  ranges_table[in_use].num = num;
+  ranges_table_in_use = in_use + 1;
+
+  return in_use * 2 * DWARF2_ADDR_SIZE;
+}
+
+/* Add a new entry to .debug_ranges corresponding to a block, or a
+   range terminator if BLOCK is NULL.  */
+
+static unsigned int
+add_ranges (const_tree block)
+{
+  return add_ranges_num (block ? BLOCK_NUMBER (block) : 0);
+}
+
+/* Add a new entry to .debug_ranges corresponding to a pair of
+   labels.  */
+
+static unsigned int
+add_ranges_by_labels (const char *begin, const char *end)
+{
+  unsigned int in_use = ranges_by_label_in_use;
+
+  if (in_use == ranges_by_label_allocated)
+    {
+      ranges_by_label_allocated += RANGES_TABLE_INCREMENT;
+      ranges_by_label = GGC_RESIZEVEC (struct dw_ranges_by_label_struct,
+				       ranges_by_label,
+				       ranges_by_label_allocated);
+      memset (ranges_by_label + ranges_by_label_in_use, 0,
+	      RANGES_TABLE_INCREMENT
+	      * sizeof (struct dw_ranges_by_label_struct));
+    }
+
+  ranges_by_label[in_use].begin = begin;
+  ranges_by_label[in_use].end = end;
+  ranges_by_label_in_use = in_use + 1;
+
+  return add_ranges_num (-(int)in_use - 1);
+}
+
+static void
+output_ranges (void)
+{
+  unsigned i;
+  static const char *const start_fmt = "Offset 0x%x";
+  const char *fmt = start_fmt;
+
+  for (i = 0; i < ranges_table_in_use; i++)
+    {
+      int block_num = ranges_table[i].num;
+
+      if (block_num > 0)
+	{
+	  char blabel[MAX_ARTIFICIAL_LABEL_BYTES];
+	  char elabel[MAX_ARTIFICIAL_LABEL_BYTES];
+
+	  ASM_GENERATE_INTERNAL_LABEL (blabel, BLOCK_BEGIN_LABEL, block_num);
+	  ASM_GENERATE_INTERNAL_LABEL (elabel, BLOCK_END_LABEL, block_num);
+
+	  /* If all code is in the text section, then the compilation
+	     unit base address defaults to DW_AT_low_pc, which is the
+	     base of the text section.  */
+	  if (!have_multiple_function_sections)
+	    {
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
+				    text_section_label,
+				    fmt, i * 2 * DWARF2_ADDR_SIZE);
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
+				    text_section_label, NULL);
+	    }
+
+	  /* Otherwise, the compilation unit base address is zero,
+	     which allows us to use absolute addresses, and not worry
+	     about whether the target supports cross-section
+	     arithmetic.  */
+	  else
+	    {
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE, blabel,
+				   fmt, i * 2 * DWARF2_ADDR_SIZE);
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE, elabel, NULL);
+	    }
+
+	  fmt = NULL;
+	}
+
+      /* Negative block_num stands for an index into ranges_by_label.  */
+      else if (block_num < 0)
+	{
+	  int lab_idx = - block_num - 1;
+
+	  if (!have_multiple_function_sections)
+	    {
+	      gcc_unreachable ();
+#if 0
+	      /* If we ever use add_ranges_by_labels () for a single
+		 function section, all we have to do is to take out
+		 the #if 0 above.  */
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+				    ranges_by_label[lab_idx].begin,
+				    text_section_label,
+				    fmt, i * 2 * DWARF2_ADDR_SIZE);
+	      dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+				    ranges_by_label[lab_idx].end,
+				    text_section_label, NULL);
+#endif
+	    }
+	  else
+	    {
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+				   ranges_by_label[lab_idx].begin,
+				   fmt, i * 2 * DWARF2_ADDR_SIZE);
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+				   ranges_by_label[lab_idx].end,
+				   NULL);
+	    }
+	}
+      else
+	{
+	  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+	  dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+	  fmt = start_fmt;
+	}
+    }
+}
+
+/* Data structure containing information about input files.  */
+struct file_info
+{
+  const char *path;	/* Complete file name.  */
+  const char *fname;	/* File name part.  */
+  int length;		/* Length of entire string.  */
+  struct dwarf_file_data * file_idx;	/* Index in input file table.  */
+  int dir_idx;		/* Index in directory table.  */
+};
+
+/* Data structure containing information about directories with source
+   files.  */
+struct dir_info
+{
+  const char *path;	/* Path including directory name.  */
+  int length;		/* Path length.  */
+  int prefix;		/* Index of directory entry which is a prefix.  */
+  int count;		/* Number of files in this directory.  */
+  int dir_idx;		/* Index of directory used as base.  */
+};
+
+/* Callback function for file_info comparison.  We sort by looking at
+   the directories in the path.  */
+
+static int
+file_info_cmp (const void *p1, const void *p2)
+{
+  const struct file_info *const s1 = (const struct file_info *) p1;
+  const struct file_info *const s2 = (const struct file_info *) p2;
+  const unsigned char *cp1;
+  const unsigned char *cp2;
+
+  /* Take care of file names without directories.  We need to make sure that
+     we return consistent values to qsort since some will get confused if
+     we return the same value when identical operands are passed in opposite
+     orders.  So if neither has a directory, return 0 and otherwise return
+     1 or -1 depending on which one has the directory.  */
+  if ((s1->path == s1->fname || s2->path == s2->fname))
+    return (s2->path == s2->fname) - (s1->path == s1->fname);
+
+  cp1 = (const unsigned char *) s1->path;
+  cp2 = (const unsigned char *) s2->path;
+
+  while (1)
+    {
+      ++cp1;
+      ++cp2;
+      /* Reached the end of the first path?  If so, handle like above.  */
+      if ((cp1 == (const unsigned char *) s1->fname)
+	  || (cp2 == (const unsigned char *) s2->fname))
+	return ((cp2 == (const unsigned char *) s2->fname)
+		- (cp1 == (const unsigned char *) s1->fname));
+
+      /* Character of current path component the same?  */
+      else if (*cp1 != *cp2)
+	return *cp1 - *cp2;
+    }
+}
+
+struct file_name_acquire_data
+{
+  struct file_info *files;
+  int used_files;
+  int max_files;
+};
+
+/* Traversal function for the hash table.  */
+
+static int
+file_name_acquire (void ** slot, void *data)
+{
+  struct file_name_acquire_data *fnad = (struct file_name_acquire_data *) data;
+  struct dwarf_file_data *d = (struct dwarf_file_data *) *slot;
+  struct file_info *fi;
+  const char *f;
+
+  gcc_assert (fnad->max_files >= d->emitted_number);
+
+  if (! d->emitted_number)
+    return 1;
+
+  gcc_assert (fnad->max_files != fnad->used_files);
+
+  fi = fnad->files + fnad->used_files++;
+
+  /* Skip all leading "./".  */
+  f = d->filename;
+  while (f[0] == '.' && IS_DIR_SEPARATOR (f[1]))
+    f += 2;
+
+  /* Create a new array entry.  */
+  fi->path = f;
+  fi->length = strlen (f);
+  fi->file_idx = d;
+
+  /* Search for the file name part.  */
+  f = strrchr (f, DIR_SEPARATOR);
+#if defined (DIR_SEPARATOR_2)
+  {
+    char *g = strrchr (fi->path, DIR_SEPARATOR_2);
+
+    if (g != NULL)
+      {
+	if (f == NULL || f < g)
+	  f = g;
+      }
+  }
+#endif
+
+  fi->fname = f == NULL ? fi->path : f + 1;
+  return 1;
+}
+
+/* Output the directory table and the file name table.  We try to minimize
+   the total amount of memory needed.  A heuristic is used to avoid large
+   slowdowns with many input files.  */
+
+static void
+output_file_names (void)
+{
+  struct file_name_acquire_data fnad;
+  int numfiles;
+  struct file_info *files;
+  struct dir_info *dirs;
+  int *saved;
+  int *savehere;
+  int *backmap;
+  int ndirs;
+  int idx_offset;
+  int i;
+  int idx;
+
+  if (!last_emitted_file)
+    {
+      dw2_asm_output_data (1, 0, "End directory table");
+      dw2_asm_output_data (1, 0, "End file name table");
+      return;
+    }
+
+  numfiles = last_emitted_file->emitted_number;
+
+  /* Allocate the various arrays we need.  */
+  files = XALLOCAVEC (struct file_info, numfiles);
+  dirs = XALLOCAVEC (struct dir_info, numfiles);
+
+  fnad.files = files;
+  fnad.used_files = 0;
+  fnad.max_files = numfiles;
+  htab_traverse (file_table, file_name_acquire, &fnad);
+  gcc_assert (fnad.used_files == fnad.max_files);
+
+  qsort (files, numfiles, sizeof (files[0]), file_info_cmp);
+
+  /* Find all the different directories used.  */
+  dirs[0].path = files[0].path;
+  dirs[0].length = files[0].fname - files[0].path;
+  dirs[0].prefix = -1;
+  dirs[0].count = 1;
+  dirs[0].dir_idx = 0;
+  files[0].dir_idx = 0;
+  ndirs = 1;
+
+  for (i = 1; i < numfiles; i++)
+    if (files[i].fname - files[i].path == dirs[ndirs - 1].length
+	&& memcmp (dirs[ndirs - 1].path, files[i].path,
+		   dirs[ndirs - 1].length) == 0)
+      {
+	/* Same directory as last entry.  */
+	files[i].dir_idx = ndirs - 1;
+	++dirs[ndirs - 1].count;
+      }
+    else
+      {
+	int j;
+
+	/* This is a new directory.  */
+	dirs[ndirs].path = files[i].path;
+	dirs[ndirs].length = files[i].fname - files[i].path;
+	dirs[ndirs].count = 1;
+	dirs[ndirs].dir_idx = ndirs;
+	files[i].dir_idx = ndirs;
+
+	/* Search for a prefix.  */
+	dirs[ndirs].prefix = -1;
+	for (j = 0; j < ndirs; j++)
+	  if (dirs[j].length < dirs[ndirs].length
+	      && dirs[j].length > 1
+	      && (dirs[ndirs].prefix == -1
+		  || dirs[j].length > dirs[dirs[ndirs].prefix].length)
+	      && memcmp (dirs[j].path, dirs[ndirs].path, dirs[j].length) == 0)
+	    dirs[ndirs].prefix = j;
+
+	++ndirs;
+      }
+
+  /* Now to the actual work.  We have to find a subset of the directories which
+     allow expressing the file name using references to the directory table
+     with the least amount of characters.  We do not do an exhaustive search
+     where we would have to check out every combination of every single
+     possible prefix.  Instead we use a heuristic which provides nearly optimal
+     results in most cases and never is much off.  */
+  saved = XALLOCAVEC (int, ndirs);
+  savehere = XALLOCAVEC (int, ndirs);
+
+  memset (saved, '\0', ndirs * sizeof (saved[0]));
+  for (i = 0; i < ndirs; i++)
+    {
+      int j;
+      int total;
+
+      /* We can always save some space for the current directory.  But this
+	 does not mean it will be enough to justify adding the directory.  */
+      savehere[i] = dirs[i].length;
+      total = (savehere[i] - saved[i]) * dirs[i].count;
+
+      for (j = i + 1; j < ndirs; j++)
+	{
+	  savehere[j] = 0;
+	  if (saved[j] < dirs[i].length)
+	    {
+	      /* Determine whether the dirs[i] path is a prefix of the
+		 dirs[j] path.  */
+	      int k;
+
+	      k = dirs[j].prefix;
+	      while (k != -1 && k != (int) i)
+		k = dirs[k].prefix;
+
+	      if (k == (int) i)
+		{
+		  /* Yes it is.  We can possibly save some memory by
+		     writing the filenames in dirs[j] relative to
+		     dirs[i].  */
+		  savehere[j] = dirs[i].length;
+		  total += (savehere[j] - saved[j]) * dirs[j].count;
+		}
+	    }
+	}
+
+      /* Check whether we can save enough to justify adding the dirs[i]
+	 directory.  */
+      if (total > dirs[i].length + 1)
+	{
+	  /* It's worthwhile adding.  */
+	  for (j = i; j < ndirs; j++)
+	    if (savehere[j] > 0)
+	      {
+		/* Remember how much we saved for this directory so far.  */
+		saved[j] = savehere[j];
+
+		/* Remember the prefix directory.  */
+		dirs[j].dir_idx = i;
+	      }
+	}
+    }
+
+  /* Emit the directory name table.  */
+  idx = 1;
+  idx_offset = dirs[0].length > 0 ? 1 : 0;
+  for (i = 1 - idx_offset; i < ndirs; i++)
+    dw2_asm_output_nstring (dirs[i].path, dirs[i].length - 1,
+			    "Directory Entry: 0x%x", i + idx_offset);
+
+  dw2_asm_output_data (1, 0, "End directory table");
+
+  /* We have to emit them in the order of emitted_number since that's
+     used in the debug info generation.  To do this efficiently we
+     generate a back-mapping of the indices first.  */
+  backmap = XALLOCAVEC (int, numfiles);
+  for (i = 0; i < numfiles; i++)
+    backmap[files[i].file_idx->emitted_number - 1] = i;
+
+  /* Now write all the file names.  */
+  for (i = 0; i < numfiles; i++)
+    {
+      int file_idx = backmap[i];
+      int dir_idx = dirs[files[file_idx].dir_idx].dir_idx;
+
+      dw2_asm_output_nstring (files[file_idx].path + dirs[dir_idx].length, -1,
+			      "File Entry: 0x%x", (unsigned) i + 1);
+
+      /* Include directory index.  */
+      dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL);
+
+      /* Modification time.  */
+      dw2_asm_output_data_uleb128 (0, NULL);
+
+      /* File length in bytes.  */
+      dw2_asm_output_data_uleb128 (0, NULL);
+    }
+
+  dw2_asm_output_data (1, 0, "End file name table");
+}
+
+
+/* Output the source line number correspondence information.  This
+   information goes into the .debug_line section.  */
+
+static void
+output_line_info (void)
+{
+  char l1[20], l2[20], p1[20], p2[20];
+  char line_label[MAX_ARTIFICIAL_LABEL_BYTES];
+  char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES];
+  unsigned opc;
+  unsigned n_op_args;
+  unsigned long lt_index;
+  unsigned long current_line;
+  long line_offset;
+  long line_delta;
+  unsigned long current_file;
+  unsigned long function;
+
+  ASM_GENERATE_INTERNAL_LABEL (l1, LINE_NUMBER_BEGIN_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (l2, LINE_NUMBER_END_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (p1, LN_PROLOG_AS_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (p2, LN_PROLOG_END_LABEL, 0);
+
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_delta (DWARF_OFFSET_SIZE, l2, l1,
+			"Length of Source Line Info");
+  ASM_OUTPUT_LABEL (asm_out_file, l1);
+
+  dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version");
+  dw2_asm_output_delta (DWARF_OFFSET_SIZE, p2, p1, "Prolog Length");
+  ASM_OUTPUT_LABEL (asm_out_file, p1);
+
+  /* Define the architecture-dependent minimum instruction length (in
+   bytes).  In this implementation of DWARF, this field is used for
+   information purposes only.  Since GCC generates assembly language,
+   we have no a priori knowledge of how many instruction bytes are
+   generated for each source line, and therefore can use only the
+   DW_LNE_set_address and DW_LNS_fixed_advance_pc line information
+   commands.  Accordingly, we fix this as `1', which is "correct
+   enough" for all architectures, and don't let the target override.  */
+  dw2_asm_output_data (1, 1,
+		       "Minimum Instruction Length");
+
+  dw2_asm_output_data (1, DWARF_LINE_DEFAULT_IS_STMT_START,
+		       "Default is_stmt_start flag");
+  dw2_asm_output_data (1, DWARF_LINE_BASE,
+		       "Line Base Value (Special Opcodes)");
+  dw2_asm_output_data (1, DWARF_LINE_RANGE,
+		       "Line Range Value (Special Opcodes)");
+  dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE,
+		       "Special Opcode Base");
+
+  for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; opc++)
+    {
+      switch (opc)
+	{
+	case DW_LNS_advance_pc:
+	case DW_LNS_advance_line:
+	case DW_LNS_set_file:
+	case DW_LNS_set_column:
+	case DW_LNS_fixed_advance_pc:
+	  n_op_args = 1;
+	  break;
+	default:
+	  n_op_args = 0;
+	  break;
+	}
+
+      dw2_asm_output_data (1, n_op_args, "opcode: 0x%x has %d args",
+			   opc, n_op_args);
+    }
+
+  /* Write out the information about the files we use.  */
+  output_file_names ();
+  ASM_OUTPUT_LABEL (asm_out_file, p2);
+
+  /* We used to set the address register to the first location in the text
+     section here, but that didn't accomplish anything since we already
+     have a line note for the opening brace of the first function.  */
+
+  /* Generate the line number to PC correspondence table, encoded as
+     a series of state machine operations.  */
+  current_file = 1;
+  current_line = 1;
+
+  if (cfun && in_cold_section_p)
+    strcpy (prev_line_label, crtl->subsections.cold_section_label);
+  else
+    strcpy (prev_line_label, text_section_label);
+  for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index)
+    {
+      dw_line_info_ref line_info = &line_info_table[lt_index];
+
+#if 0
+      /* Disable this optimization for now; GDB wants to see two line notes
+	 at the beginning of a function so it can find the end of the
+	 prologue.  */
+
+      /* Don't emit anything for redundant notes.  Just updating the
+	 address doesn't accomplish anything, because we already assume
+	 that anything after the last address is this line.  */
+      if (line_info->dw_line_num == current_line
+	  && line_info->dw_file_num == current_file)
+	continue;
+#endif
+
+      /* Emit debug info for the address of the current line.
+
+	 Unfortunately, we have little choice here currently, and must always
+	 use the most general form.  GCC does not know the address delta
+	 itself, so we can't use DW_LNS_advance_pc.  Many ports do have length
+	 attributes which will give an upper bound on the address range.  We
+	 could perhaps use length attributes to determine when it is safe to
+	 use DW_LNS_fixed_advance_pc.  */
+
+      ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index);
+      if (0)
+	{
+	  /* This can handle deltas up to 0xffff.  This takes 3 bytes.  */
+	  dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+			       "DW_LNS_fixed_advance_pc");
+	  dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+	}
+      else
+	{
+	  /* This can handle any delta.  This takes
+	     4+DWARF2_ADDR_SIZE bytes.  */
+	  dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+	  dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+	  dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+	}
+
+      strcpy (prev_line_label, line_label);
+
+      /* Emit debug info for the source file of the current line, if
+	 different from the previous line.  */
+      if (line_info->dw_file_num != current_file)
+	{
+	  current_file = line_info->dw_file_num;
+	  dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file");
+	  dw2_asm_output_data_uleb128 (current_file, "%lu", current_file);
+	}
+
+      /* Emit debug info for the current line number, choosing the encoding
+	 that uses the least amount of space.  */
+      if (line_info->dw_line_num != current_line)
+	{
+	  line_offset = line_info->dw_line_num - current_line;
+	  line_delta = line_offset - DWARF_LINE_BASE;
+	  current_line = line_info->dw_line_num;
+	  if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1))
+	    /* This can handle deltas from -10 to 234, using the current
+	       definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE.  This
+	       takes 1 byte.  */
+	    dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta,
+				 "line %lu", current_line);
+	  else
+	    {
+	      /* This can handle any delta.  This takes at least 4 bytes,
+		 depending on the value being encoded.  */
+	      dw2_asm_output_data (1, DW_LNS_advance_line,
+				   "advance to line %lu", current_line);
+	      dw2_asm_output_data_sleb128 (line_offset, NULL);
+	      dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+	    }
+	}
+      else
+	/* We still need to start a new row, so output a copy insn.  */
+	dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+    }
+
+  /* Emit debug info for the address of the end of the function.  */
+  if (0)
+    {
+      dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+			   "DW_LNS_fixed_advance_pc");
+      dw2_asm_output_delta (2, text_end_label, prev_line_label, NULL);
+    }
+  else
+    {
+      dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+      dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+      dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+      dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_end_label, NULL);
+    }
+
+  dw2_asm_output_data (1, 0, "DW_LNE_end_sequence");
+  dw2_asm_output_data_uleb128 (1, NULL);
+  dw2_asm_output_data (1, DW_LNE_end_sequence, NULL);
+
+  function = 0;
+  current_file = 1;
+  current_line = 1;
+  for (lt_index = 0; lt_index < separate_line_info_table_in_use;)
+    {
+      dw_separate_line_info_ref line_info
+	= &separate_line_info_table[lt_index];
+
+#if 0
+      /* Don't emit anything for redundant notes.  */
+      if (line_info->dw_line_num == current_line
+	  && line_info->dw_file_num == current_file
+	  && line_info->function == function)
+	goto cont;
+#endif
+
+      /* Emit debug info for the address of the current line.  If this is
+	 a new function, or the first line of a function, then we need
+	 to handle it differently.  */
+      ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL,
+				   lt_index);
+      if (function != line_info->function)
+	{
+	  function = line_info->function;
+
+	  /* Set the address register to the first line in the function.  */
+	  dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+	  dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+	  dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+	  dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+	}
+      else
+	{
+	  /* ??? See the DW_LNS_advance_pc comment above.  */
+	  if (0)
+	    {
+	      dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+				   "DW_LNS_fixed_advance_pc");
+	      dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+	    }
+	  else
+	    {
+	      dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+	      dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+	      dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+	    }
+	}
+
+      strcpy (prev_line_label, line_label);
+
+      /* Emit debug info for the source file of the current line, if
+	 different from the previous line.  */
+      if (line_info->dw_file_num != current_file)
+	{
+	  current_file = line_info->dw_file_num;
+	  dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file");
+	  dw2_asm_output_data_uleb128 (current_file, "%lu", current_file);
+	}
+
+      /* Emit debug info for the current line number, choosing the encoding
+	 that uses the least amount of space.  */
+      if (line_info->dw_line_num != current_line)
+	{
+	  line_offset = line_info->dw_line_num - current_line;
+	  line_delta = line_offset - DWARF_LINE_BASE;
+	  current_line = line_info->dw_line_num;
+	  if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1))
+	    dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta,
+				 "line %lu", current_line);
+	  else
+	    {
+	      dw2_asm_output_data (1, DW_LNS_advance_line,
+				   "advance to line %lu", current_line);
+	      dw2_asm_output_data_sleb128 (line_offset, NULL);
+	      dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+	    }
+	}
+      else
+	dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+
+#if 0
+    cont:
+#endif
+
+      lt_index++;
+
+      /* If we're done with a function, end its sequence.  */
+      if (lt_index == separate_line_info_table_in_use
+	  || separate_line_info_table[lt_index].function != function)
+	{
+	  current_file = 1;
+	  current_line = 1;
+
+	  /* Emit debug info for the address of the end of the function.  */
+	  ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function);
+	  if (0)
+	    {
+	      dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+				   "DW_LNS_fixed_advance_pc");
+	      dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+	    }
+	  else
+	    {
+	      dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+	      dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+	      dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+	      dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+	    }
+
+	  /* Output the marker for the end of this sequence.  */
+	  dw2_asm_output_data (1, 0, "DW_LNE_end_sequence");
+	  dw2_asm_output_data_uleb128 (1, NULL);
+	  dw2_asm_output_data (1, DW_LNE_end_sequence, NULL);
+	}
+    }
+
+  /* Output the marker for the end of the line number info.  */
+  ASM_OUTPUT_LABEL (asm_out_file, l2);
+}
+
+/* Given a pointer to a tree node for some base type, return a pointer to
+   a DIE that describes the given type.
+
+   This routine must only be called for GCC type nodes that correspond to
+   Dwarf base (fundamental) types.  */
+
+static dw_die_ref
+base_type_die (tree type)
+{
+  dw_die_ref base_type_result;
+  enum dwarf_type encoding;
+
+  if (TREE_CODE (type) == ERROR_MARK || TREE_CODE (type) == VOID_TYPE)
+    return 0;
+
+  switch (TREE_CODE (type))
+    {
+    case INTEGER_TYPE:
+      if (TYPE_STRING_FLAG (type))
+	{
+	  if (TYPE_UNSIGNED (type))
+	    encoding = DW_ATE_unsigned_char;
+	  else
+	    encoding = DW_ATE_signed_char;
+	}
+      else if (TYPE_UNSIGNED (type))
+	encoding = DW_ATE_unsigned;
+      else
+	encoding = DW_ATE_signed;
+      break;
+
+    case REAL_TYPE:
+      if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
+	encoding = DW_ATE_decimal_float;
+      else
+	encoding = DW_ATE_float;
+      break;
+
+    case FIXED_POINT_TYPE:
+      if (TYPE_UNSIGNED (type))
+	encoding = DW_ATE_unsigned_fixed;
+      else
+	encoding = DW_ATE_signed_fixed;
+      break;
+
+      /* Dwarf2 doesn't know anything about complex ints, so use
+	 a user defined type for it.  */
+    case COMPLEX_TYPE:
+      if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE)
+	encoding = DW_ATE_complex_float;
+      else
+	encoding = DW_ATE_lo_user;
+      break;
+
+    case BOOLEAN_TYPE:
+      /* GNU FORTRAN/Ada/C++ BOOLEAN type.  */
+      encoding = DW_ATE_boolean;
+      break;
+
+    default:
+      /* No other TREE_CODEs are Dwarf fundamental types.  */
+      gcc_unreachable ();
+    }
+
+  base_type_result = new_die (DW_TAG_base_type, comp_unit_die, type);
+
+  /* This probably indicates a bug.  */
+  if (! TYPE_NAME (type))
+    add_name_attribute (base_type_result, "__unknown__");
+
+  add_AT_unsigned (base_type_result, DW_AT_byte_size,
+		   int_size_in_bytes (type));
+  add_AT_unsigned (base_type_result, DW_AT_encoding, encoding);
+
+  return base_type_result;
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return nonzero if the
+   given input type is a Dwarf "fundamental" type.  Otherwise return null.  */
+
+static inline int
+is_base_type (tree type)
+{
+  switch (TREE_CODE (type))
+    {
+    case ERROR_MARK:
+    case VOID_TYPE:
+    case INTEGER_TYPE:
+    case REAL_TYPE:
+    case FIXED_POINT_TYPE:
+    case COMPLEX_TYPE:
+    case BOOLEAN_TYPE:
+      return 1;
+
+    case ARRAY_TYPE:
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+    case ENUMERAL_TYPE:
+    case FUNCTION_TYPE:
+    case METHOD_TYPE:
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+    case OFFSET_TYPE:
+    case LANG_TYPE:
+    case VECTOR_TYPE:
+      return 0;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return 0;
+}
+
+/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE
+   node, return the size in bits for the type if it is a constant, or else
+   return the alignment for the type if the type's size is not constant, or
+   else return BITS_PER_WORD if the type actually turns out to be an
+   ERROR_MARK node.  */
+
+static inline unsigned HOST_WIDE_INT
+simple_type_size_in_bits (const_tree type)
+{
+  if (TREE_CODE (type) == ERROR_MARK)
+    return BITS_PER_WORD;
+  else if (TYPE_SIZE (type) == NULL_TREE)
+    return 0;
+  else if (host_integerp (TYPE_SIZE (type), 1))
+    return tree_low_cst (TYPE_SIZE (type), 1);
+  else
+    return TYPE_ALIGN (type);
+}
+
+/* Return true if the debug information for the given type should be
+   emitted as a subrange type.  */
+
+static inline bool
+is_subrange_type (const_tree type)
+{
+  tree subtype = TREE_TYPE (type);
+
+  /* Subrange types are identified by the fact that they are integer
+     types, and that they have a subtype which is either an integer type
+     or an enumeral type.  */
+
+  if (TREE_CODE (type) != INTEGER_TYPE
+      || subtype == NULL_TREE)
+    return false;
+
+  if (TREE_CODE (subtype) != INTEGER_TYPE
+      && TREE_CODE (subtype) != ENUMERAL_TYPE
+      && TREE_CODE (subtype) != BOOLEAN_TYPE)
+    return false;
+
+  if (TREE_CODE (type) == TREE_CODE (subtype)
+      && int_size_in_bytes (type) == int_size_in_bytes (subtype)
+      && TYPE_MIN_VALUE (type) != NULL
+      && TYPE_MIN_VALUE (subtype) != NULL
+      && tree_int_cst_equal (TYPE_MIN_VALUE (type), TYPE_MIN_VALUE (subtype))
+      && TYPE_MAX_VALUE (type) != NULL
+      && TYPE_MAX_VALUE (subtype) != NULL
+      && tree_int_cst_equal (TYPE_MAX_VALUE (type), TYPE_MAX_VALUE (subtype)))
+    {
+      /* The type and its subtype have the same representation.  If in
+	 addition the two types also have the same name, then the given
+	 type is not a subrange type, but rather a plain base type.  */
+      /* FIXME: brobecker/2004-03-22:
+	 Sizetype INTEGER_CSTs nodes are canonicalized.  It should
+	 therefore be sufficient to check the TYPE_SIZE node pointers
+	 rather than checking the actual size.  Unfortunately, we have
+	 found some cases, such as in the Ada "integer" type, where
+	 this is not the case.  Until this problem is solved, we need to
+	 keep checking the actual size.  */
+      tree type_name = TYPE_NAME (type);
+      tree subtype_name = TYPE_NAME (subtype);
+
+      if (type_name != NULL && TREE_CODE (type_name) == TYPE_DECL)
+	type_name = DECL_NAME (type_name);
+
+      if (subtype_name != NULL && TREE_CODE (subtype_name) == TYPE_DECL)
+	subtype_name = DECL_NAME (subtype_name);
+
+      if (type_name == subtype_name)
+	return false;
+    }
+
+  return true;
+}
+
+/*  Given a pointer to a tree node for a subrange type, return a pointer
+    to a DIE that describes the given type.  */
+
+static dw_die_ref
+subrange_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref subrange_die;
+  const HOST_WIDE_INT size_in_bytes = int_size_in_bytes (type);
+
+  if (context_die == NULL)
+    context_die = comp_unit_die;
+
+  subrange_die = new_die (DW_TAG_subrange_type, context_die, type);
+
+  if (int_size_in_bytes (TREE_TYPE (type)) != size_in_bytes)
+    {
+      /* The size of the subrange type and its base type do not match,
+	 so we need to generate a size attribute for the subrange type.  */
+      add_AT_unsigned (subrange_die, DW_AT_byte_size, size_in_bytes);
+    }
+
+  if (TYPE_MIN_VALUE (type) != NULL)
+    add_bound_info (subrange_die, DW_AT_lower_bound,
+		    TYPE_MIN_VALUE (type));
+  if (TYPE_MAX_VALUE (type) != NULL)
+    add_bound_info (subrange_die, DW_AT_upper_bound,
+		    TYPE_MAX_VALUE (type));
+
+  return subrange_die;
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging
+   entry that chains various modifiers in front of the given type.  */
+
+static dw_die_ref
+modified_type_die (tree type, int is_const_type, int is_volatile_type,
+		   dw_die_ref context_die)
+{
+  enum tree_code code = TREE_CODE (type);
+  dw_die_ref mod_type_die;
+  dw_die_ref sub_die = NULL;
+  tree item_type = NULL;
+  tree qualified_type;
+  tree name;
+
+  if (code == ERROR_MARK)
+    return NULL;
+
+  /* See if we already have the appropriately qualified variant of
+     this type.  */
+  qualified_type
+    = get_qualified_type (type,
+			  ((is_const_type ? TYPE_QUAL_CONST : 0)
+			   | (is_volatile_type ? TYPE_QUAL_VOLATILE : 0)));
+
+  /* If we do, then we can just use its DIE, if it exists.  */
+  if (qualified_type)
+    {
+      mod_type_die = lookup_type_die (qualified_type);
+      if (mod_type_die)
+	return mod_type_die;
+    }
+
+  name = qualified_type ? TYPE_NAME (qualified_type) : NULL;
+
+  /* Handle C typedef types.  */
+  if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name))
+    {
+      tree dtype = TREE_TYPE (name);
+
+      if (qualified_type == dtype)
+	{
+	  /* For a named type, use the typedef.  */
+	  gen_type_die (qualified_type, context_die);
+	  return lookup_type_die (qualified_type);
+	}
+      else if (is_const_type < TYPE_READONLY (dtype)
+	       || is_volatile_type < TYPE_VOLATILE (dtype)
+	       || (is_const_type <= TYPE_READONLY (dtype)
+		   && is_volatile_type <= TYPE_VOLATILE (dtype)
+		   && DECL_ORIGINAL_TYPE (name) != type))
+	/* cv-unqualified version of named type.  Just use the unnamed
+	   type to which it refers.  */
+	return modified_type_die (DECL_ORIGINAL_TYPE (name),
+				  is_const_type, is_volatile_type,
+				  context_die);
+      /* Else cv-qualified version of named type; fall through.  */
+    }
+
+  if (is_const_type)
+    {
+      mod_type_die = new_die (DW_TAG_const_type, comp_unit_die, type);
+      sub_die = modified_type_die (type, 0, is_volatile_type, context_die);
+    }
+  else if (is_volatile_type)
+    {
+      mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die, type);
+      sub_die = modified_type_die (type, 0, 0, context_die);
+    }
+  else if (code == POINTER_TYPE)
+    {
+      mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die, type);
+      add_AT_unsigned (mod_type_die, DW_AT_byte_size,
+		       simple_type_size_in_bits (type) / BITS_PER_UNIT);
+      item_type = TREE_TYPE (type);
+    }
+  else if (code == REFERENCE_TYPE)
+    {
+      mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die, type);
+      add_AT_unsigned (mod_type_die, DW_AT_byte_size,
+		       simple_type_size_in_bits (type) / BITS_PER_UNIT);
+      item_type = TREE_TYPE (type);
+    }
+  else if (is_subrange_type (type))
+    {
+      mod_type_die = subrange_type_die (type, context_die);
+      item_type = TREE_TYPE (type);
+    }
+  else if (is_base_type (type))
+    mod_type_die = base_type_die (type);
+  else
+    {
+      gen_type_die (type, context_die);
+
+      /* We have to get the type_main_variant here (and pass that to the
+	 `lookup_type_die' routine) because the ..._TYPE node we have
+	 might simply be a *copy* of some original type node (where the
+	 copy was created to help us keep track of typedef names) and
+	 that copy might have a different TYPE_UID from the original
+	 ..._TYPE node.  */
+      if (TREE_CODE (type) != VECTOR_TYPE)
+	return lookup_type_die (type_main_variant (type));
+      else
+	/* Vectors have the debugging information in the type,
+	   not the main variant.  */
+	return lookup_type_die (type);
+    }
+
+  /* Builtin types don't have a DECL_ORIGINAL_TYPE.  For those,
+     don't output a DW_TAG_typedef, since there isn't one in the
+     user's program; just attach a DW_AT_name to the type.  */
+  if (name
+      && (TREE_CODE (name) != TYPE_DECL
+	  || (TREE_TYPE (name) == qualified_type && DECL_NAME (name))))
+    {
+      if (TREE_CODE (name) == TYPE_DECL)
+	/* Could just call add_name_and_src_coords_attributes here,
+	   but since this is a builtin type it doesn't have any
+	   useful source coordinates anyway.  */
+	name = DECL_NAME (name);
+      add_name_attribute (mod_type_die, IDENTIFIER_POINTER (name));
+    }
+
+  if (qualified_type)
+    equate_type_number_to_die (qualified_type, mod_type_die);
+
+  if (item_type)
+    /* We must do this after the equate_type_number_to_die call, in case
+       this is a recursive type.  This ensures that the modified_type_die
+       recursion will terminate even if the type is recursive.  Recursive
+       types are possible in Ada.  */
+    sub_die = modified_type_die (item_type,
+				 TYPE_READONLY (item_type),
+				 TYPE_VOLATILE (item_type),
+				 context_die);
+
+  if (sub_die != NULL)
+    add_AT_die_ref (mod_type_die, DW_AT_type, sub_die);
+
+  return mod_type_die;
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is
+   an enumerated type.  */
+
+static inline int
+type_is_enum (const_tree type)
+{
+  return TREE_CODE (type) == ENUMERAL_TYPE;
+}
+
+/* Return the DBX register number described by a given RTL node.  */
+
+static unsigned int
+dbx_reg_number (const_rtx rtl)
+{
+  unsigned regno = REGNO (rtl);
+
+  gcc_assert (regno < FIRST_PSEUDO_REGISTER);
+
+#ifdef LEAF_REG_REMAP
+  if (current_function_uses_only_leaf_regs)
+    {
+      int leaf_reg = LEAF_REG_REMAP (regno);
+      if (leaf_reg != -1)
+	regno = (unsigned) leaf_reg;
+    }
+#endif
+
+  return DBX_REGISTER_NUMBER (regno);
+}
+
+/* Optionally add a DW_OP_piece term to a location description expression.
+   DW_OP_piece is only added if the location description expression already
+   doesn't end with DW_OP_piece.  */
+
+static void
+add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size)
+{
+  dw_loc_descr_ref loc;
+
+  if (*list_head != NULL)
+    {
+      /* Find the end of the chain.  */
+      for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next)
+	;
+
+      if (loc->dw_loc_opc != DW_OP_piece)
+	loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0);
+    }
+}
+
+/* Return a location descriptor that designates a machine register or
+   zero if there is none.  */
+
+static dw_loc_descr_ref
+reg_loc_descriptor (rtx rtl, enum var_init_status initialized)
+{
+  rtx regs;
+
+  if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)
+    return 0;
+
+  regs = targetm.dwarf_register_span (rtl);
+
+  if (hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)] > 1 || regs)
+    return multiple_reg_loc_descriptor (rtl, regs, initialized);
+  else
+    return one_reg_loc_descriptor (dbx_reg_number (rtl), initialized);
+}
+
+/* Return a location descriptor that designates a machine register for
+   a given hard register number.  */
+
+static dw_loc_descr_ref
+one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized)
+{
+  dw_loc_descr_ref reg_loc_descr = new_reg_loc_descr (regno, 0);
+
+  if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&reg_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return reg_loc_descr;
+}
+
+/* Given an RTL of a register, return a location descriptor that
+   designates a value that spans more than one register.  */
+
+static dw_loc_descr_ref
+multiple_reg_loc_descriptor (rtx rtl, rtx regs,
+			     enum var_init_status initialized)
+{
+  int nregs, size, i;
+  unsigned reg;
+  dw_loc_descr_ref loc_result = NULL;
+
+  reg = REGNO (rtl);
+#ifdef LEAF_REG_REMAP
+  if (current_function_uses_only_leaf_regs)
+    {
+      int leaf_reg = LEAF_REG_REMAP (reg);
+      if (leaf_reg != -1)
+	reg = (unsigned) leaf_reg;
+    }
+#endif
+  gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl));
+  nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)];
+
+  /* Simple, contiguous registers.  */
+  if (regs == NULL_RTX)
+    {
+      size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs;
+
+      loc_result = NULL;
+      while (nregs--)
+	{
+	  dw_loc_descr_ref t;
+
+	  t = one_reg_loc_descriptor (DBX_REGISTER_NUMBER (reg),
+				      VAR_INIT_STATUS_INITIALIZED);
+	  add_loc_descr (&loc_result, t);
+	  add_loc_descr_op_piece (&loc_result, size);
+	  ++reg;
+	}
+      return loc_result;
+    }
+
+  /* Now onto stupid register sets in non contiguous locations.  */
+
+  gcc_assert (GET_CODE (regs) == PARALLEL);
+
+  size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0)));
+  loc_result = NULL;
+
+  for (i = 0; i < XVECLEN (regs, 0); ++i)
+    {
+      dw_loc_descr_ref t;
+
+      t = one_reg_loc_descriptor (REGNO (XVECEXP (regs, 0, i)),
+				  VAR_INIT_STATUS_INITIALIZED);
+      add_loc_descr (&loc_result, t);
+      size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0)));
+      add_loc_descr_op_piece (&loc_result, size);
+    }
+
+  if (loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+  return loc_result;
+}
+
+#endif /* DWARF2_DEBUGGING_INFO */
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+
+/* Return a location descriptor that designates a constant.  */
+
+static dw_loc_descr_ref
+int_loc_descriptor (HOST_WIDE_INT i)
+{
+  enum dwarf_location_atom op;
+
+  /* Pick the smallest representation of a constant, rather than just
+     defaulting to the LEB encoding.  */
+  if (i >= 0)
+    {
+      if (i <= 31)
+	op = DW_OP_lit0 + i;
+      else if (i <= 0xff)
+	op = DW_OP_const1u;
+      else if (i <= 0xffff)
+	op = DW_OP_const2u;
+      else if (HOST_BITS_PER_WIDE_INT == 32
+	       || i <= 0xffffffff)
+	op = DW_OP_const4u;
+      else
+	op = DW_OP_constu;
+    }
+  else
+    {
+      if (i >= -0x80)
+	op = DW_OP_const1s;
+      else if (i >= -0x8000)
+	op = DW_OP_const2s;
+      else if (HOST_BITS_PER_WIDE_INT == 32
+	       || i >= -0x80000000)
+	op = DW_OP_const4s;
+      else
+	op = DW_OP_consts;
+    }
+
+  return new_loc_descr (op, i, 0);
+}
+#endif
+
+#ifdef DWARF2_DEBUGGING_INFO
+
+/* Return a location descriptor that designates a base+offset location.  */
+
+static dw_loc_descr_ref
+based_loc_descr (rtx reg, HOST_WIDE_INT offset,
+		 enum var_init_status initialized)
+{
+  unsigned int regno;
+  dw_loc_descr_ref result;
+  dw_fde_ref fde = current_fde ();
+
+  /* We only use "frame base" when we're sure we're talking about the
+     post-prologue local stack frame.  We do this by *not* running
+     register elimination until this point, and recognizing the special
+     argument pointer and soft frame pointer rtx's.  */
+  if (reg == arg_pointer_rtx || reg == frame_pointer_rtx)
+    {
+      rtx elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+
+      if (elim != reg)
+	{
+	  if (GET_CODE (elim) == PLUS)
+	    {
+	      offset += INTVAL (XEXP (elim, 1));
+	      elim = XEXP (elim, 0);
+	    }
+	  gcc_assert ((SUPPORTS_STACK_ALIGNMENT
+		       && (elim == hard_frame_pointer_rtx
+			   || elim == stack_pointer_rtx))
+	              || elim == (frame_pointer_needed
+				  ? hard_frame_pointer_rtx
+				  : stack_pointer_rtx));
+
+	  /* If drap register is used to align stack, use frame
+	     pointer + offset to access stack variables.  If stack
+	     is aligned without drap, use stack pointer + offset to
+	     access stack variables.  */
+	  if (crtl->stack_realign_tried
+	      && cfa.reg == HARD_FRAME_POINTER_REGNUM
+	      && reg == frame_pointer_rtx)
+	    {
+	      int base_reg
+		= DWARF_FRAME_REGNUM (cfa.indirect
+				      ? HARD_FRAME_POINTER_REGNUM
+				      : STACK_POINTER_REGNUM);
+	      return new_reg_loc_descr (base_reg, offset);
+	    }
+
+	  offset += frame_pointer_fb_offset;
+	  return new_loc_descr (DW_OP_fbreg, offset, 0);
+	}
+    }
+  else if (fde
+	   && fde->drap_reg != INVALID_REGNUM
+	   && (fde->drap_reg == REGNO (reg)
+	       || fde->vdrap_reg == REGNO (reg)))
+    {
+      /* Use cfa+offset to represent the location of arguments passed
+	 on stack when drap is used to align stack.  */
+      return new_loc_descr (DW_OP_fbreg, offset, 0);
+    }
+
+  regno = dbx_reg_number (reg);
+  if (regno <= 31)
+    result = new_loc_descr (DW_OP_breg0 + regno, offset, 0);
+  else
+    result = new_loc_descr (DW_OP_bregx, regno, offset);
+
+  if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return result;
+}
+
+/* Return true if this RTL expression describes a base+offset calculation.  */
+
+static inline int
+is_based_loc (const_rtx rtl)
+{
+  return (GET_CODE (rtl) == PLUS
+	  && ((REG_P (XEXP (rtl, 0))
+	       && REGNO (XEXP (rtl, 0)) < FIRST_PSEUDO_REGISTER
+	       && GET_CODE (XEXP (rtl, 1)) == CONST_INT)));
+}
+
+/* Return a descriptor that describes the concatenation of N locations
+   used to form the address of a memory location.  */
+
+static dw_loc_descr_ref
+concatn_mem_loc_descriptor (rtx concatn, enum machine_mode mode,
+			    enum var_init_status initialized)
+{
+  unsigned int i;
+  dw_loc_descr_ref cc_loc_result = NULL;
+  unsigned int n = XVECLEN (concatn, 0);
+
+  for (i = 0; i < n; ++i)
+    {
+      dw_loc_descr_ref ref;
+      rtx x = XVECEXP (concatn, 0, i);
+
+      ref = mem_loc_descriptor (x, mode, VAR_INIT_STATUS_INITIALIZED);
+      if (ref == NULL)
+	return NULL;
+
+      add_loc_descr (&cc_loc_result, ref);
+      add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x)));
+    }
+
+  if (cc_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return cc_loc_result;
+}
+
+/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0)
+   failed.  */
+
+static dw_loc_descr_ref
+tls_mem_loc_descriptor (rtx mem)
+{
+  tree base;
+  dw_loc_descr_ref loc_result, loc_result2;
+
+  if (MEM_EXPR (mem) == NULL_TREE || MEM_OFFSET (mem) == NULL_RTX)
+    return NULL;
+
+  base = get_base_address (MEM_EXPR (mem));
+  if (base == NULL
+      || TREE_CODE (base) != VAR_DECL
+      || !DECL_THREAD_LOCAL_P (base))
+    return NULL;
+
+  loc_result = loc_descriptor_from_tree_1 (MEM_EXPR (mem), 2);
+  if (loc_result == NULL)
+    return NULL;
+
+  if (INTVAL (MEM_OFFSET (mem)))
+    {
+      if (INTVAL (MEM_OFFSET (mem)) >= 0)
+	add_loc_descr (&loc_result,
+		       new_loc_descr (DW_OP_plus_uconst,
+				      INTVAL (MEM_OFFSET (mem)), 0));
+      else
+	{
+	  loc_result2 = mem_loc_descriptor (MEM_OFFSET (mem), GET_MODE (mem),
+					    VAR_INIT_STATUS_INITIALIZED);
+	  if (loc_result2 == 0)
+	    return NULL;
+	  add_loc_descr (&loc_result, loc_result2);
+	  add_loc_descr (&loc_result, new_loc_descr (DW_OP_plus, 0, 0));
+	}
+    }
+
+  return loc_result;
+}
+
+/* The following routine converts the RTL for a variable or parameter
+   (resident in memory) into an equivalent Dwarf representation of a
+   mechanism for getting the address of that same variable onto the top of a
+   hypothetical "address evaluation" stack.
+
+   When creating memory location descriptors, we are effectively transforming
+   the RTL for a memory-resident object into its Dwarf postfix expression
+   equivalent.  This routine recursively descends an RTL tree, turning
+   it into Dwarf postfix code as it goes.
+
+   MODE is the mode of the memory reference, needed to handle some
+   autoincrement addressing modes.
+
+   CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the
+   location list for RTL.
+
+   Return 0 if we can't represent the location.  */
+
+static dw_loc_descr_ref
+mem_loc_descriptor (rtx rtl, enum machine_mode mode,
+		    enum var_init_status initialized)
+{
+  dw_loc_descr_ref mem_loc_result = NULL;
+  enum dwarf_location_atom op;
+
+  /* Note that for a dynamically sized array, the location we will generate a
+     description of here will be the lowest numbered location which is
+     actually within the array.  That's *not* necessarily the same as the
+     zeroth element of the array.  */
+
+  rtl = targetm.delegitimize_address (rtl);
+
+  switch (GET_CODE (rtl))
+    {
+    case POST_INC:
+    case POST_DEC:
+    case POST_MODIFY:
+      /* POST_INC and POST_DEC can be handled just like a SUBREG.  So we
+	 just fall into the SUBREG code.  */
+
+      /* ... fall through ...  */
+
+    case SUBREG:
+      /* The case of a subreg may arise when we have a local (register)
+	 variable or a formal (register) parameter which doesn't quite fill
+	 up an entire register.  For now, just assume that it is
+	 legitimate to make the Dwarf info refer to the whole register which
+	 contains the given subreg.  */
+      rtl = XEXP (rtl, 0);
+
+      /* ... fall through ...  */
+
+    case REG:
+      /* Whenever a register number forms a part of the description of the
+	 method for calculating the (dynamic) address of a memory resident
+	 object, DWARF rules require the register number be referred to as
+	 a "base register".  This distinction is not based in any way upon
+	 what category of register the hardware believes the given register
+	 belongs to.  This is strictly DWARF terminology we're dealing with
+	 here. Note that in cases where the location of a memory-resident
+	 data object could be expressed as: OP_ADD (OP_BASEREG (basereg),
+	 OP_CONST (0)) the actual DWARF location descriptor that we generate
+	 may just be OP_BASEREG (basereg).  This may look deceptively like
+	 the object in question was allocated to a register (rather than in
+	 memory) so DWARF consumers need to be aware of the subtle
+	 distinction between OP_REG and OP_BASEREG.  */
+      if (REGNO (rtl) < FIRST_PSEUDO_REGISTER)
+	mem_loc_result = based_loc_descr (rtl, 0, VAR_INIT_STATUS_INITIALIZED);
+      else if (stack_realign_drap
+	       && crtl->drap_reg
+	       && crtl->args.internal_arg_pointer == rtl
+	       && REGNO (crtl->drap_reg) < FIRST_PSEUDO_REGISTER)
+	{
+	  /* If RTL is internal_arg_pointer, which has been optimized
+	     out, use DRAP instead.  */
+	  mem_loc_result = based_loc_descr (crtl->drap_reg, 0,
+					    VAR_INIT_STATUS_INITIALIZED);
+	}
+      break;
+
+    case MEM:
+      mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+					   VAR_INIT_STATUS_INITIALIZED);
+      if (mem_loc_result == NULL)
+	mem_loc_result = tls_mem_loc_descriptor (rtl);
+      if (mem_loc_result != 0)
+	add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0));
+      break;
+
+    case LO_SUM:
+	 rtl = XEXP (rtl, 1);
+
+      /* ... fall through ...  */
+
+    case LABEL_REF:
+      /* Some ports can transform a symbol ref into a label ref, because
+	 the symbol ref is too far away and has to be dumped into a constant
+	 pool.  */
+    case CONST:
+    case SYMBOL_REF:
+      /* Alternatively, the symbol in the constant pool might be referenced
+	 by a different symbol.  */
+      if (GET_CODE (rtl) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (rtl))
+	{
+	  bool marked;
+	  rtx tmp = get_pool_constant_mark (rtl, &marked);
+
+	  if (GET_CODE (tmp) == SYMBOL_REF)
+	    {
+	      rtl = tmp;
+	      if (CONSTANT_POOL_ADDRESS_P (tmp))
+		get_pool_constant_mark (tmp, &marked);
+	      else
+		marked = true;
+	    }
+
+	  /* If all references to this pool constant were optimized away,
+	     it was not output and thus we can't represent it.
+	     FIXME: might try to use DW_OP_const_value here, though
+	     DW_OP_piece complicates it.  */
+	  if (!marked)
+	    return 0;
+	}
+
+      mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+      mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+      mem_loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+      VEC_safe_push (rtx, gc, used_rtx_array, rtl);
+      break;
+
+    case PRE_MODIFY:
+      /* Extract the PLUS expression nested inside and fall into
+	 PLUS code below.  */
+      rtl = XEXP (rtl, 1);
+      goto plus;
+
+    case PRE_INC:
+    case PRE_DEC:
+      /* Turn these into a PLUS expression and fall into the PLUS code
+	 below.  */
+      rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0),
+			  GEN_INT (GET_CODE (rtl) == PRE_INC
+				   ? GET_MODE_UNIT_SIZE (mode)
+				   : -GET_MODE_UNIT_SIZE (mode)));
+
+      /* ... fall through ...  */
+
+    case PLUS:
+    plus:
+      if (is_based_loc (rtl))
+	mem_loc_result = based_loc_descr (XEXP (rtl, 0),
+					  INTVAL (XEXP (rtl, 1)),
+					  VAR_INIT_STATUS_INITIALIZED);
+      else
+	{
+	  mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode,
+					       VAR_INIT_STATUS_INITIALIZED);
+	  if (mem_loc_result == 0)
+	    break;
+
+	  if (GET_CODE (XEXP (rtl, 1)) == CONST_INT
+	      && INTVAL (XEXP (rtl, 1)) >= 0)
+	    add_loc_descr (&mem_loc_result,
+			   new_loc_descr (DW_OP_plus_uconst,
+					  INTVAL (XEXP (rtl, 1)), 0));
+	  else
+	    {
+	      dw_loc_descr_ref mem_loc_result2
+		= mem_loc_descriptor (XEXP (rtl, 1), mode,
+				      VAR_INIT_STATUS_INITIALIZED);
+	      if (mem_loc_result2 == 0)
+		break;
+	      add_loc_descr (&mem_loc_result, mem_loc_result2);
+	      add_loc_descr (&mem_loc_result,
+			     new_loc_descr (DW_OP_plus, 0, 0));
+	    }
+	}
+      break;
+
+    /* If a pseudo-reg is optimized away, it is possible for it to
+       be replaced with a MEM containing a multiply or shift.  */
+    case MULT:
+      op = DW_OP_mul;
+      goto do_binop;
+
+    case ASHIFT:
+      op = DW_OP_shl;
+      goto do_binop;
+
+    case ASHIFTRT:
+      op = DW_OP_shra;
+      goto do_binop;
+
+    case LSHIFTRT:
+      op = DW_OP_shr;
+      goto do_binop;
+
+    do_binop:
+      {
+	dw_loc_descr_ref op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+						   VAR_INIT_STATUS_INITIALIZED);
+	dw_loc_descr_ref op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+						   VAR_INIT_STATUS_INITIALIZED);
+
+	if (op0 == 0 || op1 == 0)
+	  break;
+
+	mem_loc_result = op0;
+	add_loc_descr (&mem_loc_result, op1);
+	add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+	break;
+      }
+
+    case CONST_INT:
+      mem_loc_result = int_loc_descriptor (INTVAL (rtl));
+      break;
+
+    case CONCATN:
+      mem_loc_result = concatn_mem_loc_descriptor (rtl, mode,
+						   VAR_INIT_STATUS_INITIALIZED);
+      break;
+
+    case UNSPEC:
+      /* If delegitimize_address couldn't do anything with the UNSPEC, we
+	 can't express it in the debug info.  This can happen e.g. with some
+	 TLS UNSPECs.  */
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  if (mem_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return mem_loc_result;
+}
+
+/* Return a descriptor that describes the concatenation of two locations.
+   This is typically a complex variable.  */
+
+static dw_loc_descr_ref
+concat_loc_descriptor (rtx x0, rtx x1, enum var_init_status initialized)
+{
+  dw_loc_descr_ref cc_loc_result = NULL;
+  dw_loc_descr_ref x0_ref = loc_descriptor (x0, VAR_INIT_STATUS_INITIALIZED);
+  dw_loc_descr_ref x1_ref = loc_descriptor (x1, VAR_INIT_STATUS_INITIALIZED);
+
+  if (x0_ref == 0 || x1_ref == 0)
+    return 0;
+
+  cc_loc_result = x0_ref;
+  add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x0)));
+
+  add_loc_descr (&cc_loc_result, x1_ref);
+  add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x1)));
+
+  if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return cc_loc_result;
+}
+
+/* Return a descriptor that describes the concatenation of N
+   locations.  */
+
+static dw_loc_descr_ref
+concatn_loc_descriptor (rtx concatn, enum var_init_status initialized)
+{
+  unsigned int i;
+  dw_loc_descr_ref cc_loc_result = NULL;
+  unsigned int n = XVECLEN (concatn, 0);
+
+  for (i = 0; i < n; ++i)
+    {
+      dw_loc_descr_ref ref;
+      rtx x = XVECEXP (concatn, 0, i);
+
+      ref = loc_descriptor (x, VAR_INIT_STATUS_INITIALIZED);
+      if (ref == NULL)
+	return NULL;
+
+      add_loc_descr (&cc_loc_result, ref);
+      add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x)));
+    }
+
+  if (cc_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+    add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+  return cc_loc_result;
+}
+
+/* Output a proper Dwarf location descriptor for a variable or parameter
+   which is either allocated in a register or in a memory location.  For a
+   register, we just generate an OP_REG and the register number.  For a
+   memory location we provide a Dwarf postfix expression describing how to
+   generate the (dynamic) address of the object onto the address stack.
+
+   If we don't know how to describe it, return 0.  */
+
+static dw_loc_descr_ref
+loc_descriptor (rtx rtl, enum var_init_status initialized)
+{
+  dw_loc_descr_ref loc_result = NULL;
+
+  switch (GET_CODE (rtl))
+    {
+    case SUBREG:
+      /* The case of a subreg may arise when we have a local (register)
+	 variable or a formal (register) parameter which doesn't quite fill
+	 up an entire register.  For now, just assume that it is
+	 legitimate to make the Dwarf info refer to the whole register which
+	 contains the given subreg.  */
+      rtl = SUBREG_REG (rtl);
+
+      /* ... fall through ...  */
+
+    case REG:
+      loc_result = reg_loc_descriptor (rtl, initialized);
+      break;
+
+    case MEM:
+      loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+				       initialized);
+      if (loc_result == NULL)
+	loc_result = tls_mem_loc_descriptor (rtl);
+      break;
+
+    case CONCAT:
+      loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1),
+					  initialized);
+      break;
+
+    case CONCATN:
+      loc_result = concatn_loc_descriptor (rtl, initialized);
+      break;
+
+    case VAR_LOCATION:
+      /* Single part.  */
+      if (GET_CODE (XEXP (rtl, 1)) != PARALLEL)
+	{
+	  loc_result = loc_descriptor (XEXP (XEXP (rtl, 1), 0), initialized);
+	  break;
+	}
+
+      rtl = XEXP (rtl, 1);
+      /* FALLTHRU */
+
+    case PARALLEL:
+      {
+	rtvec par_elems = XVEC (rtl, 0);
+	int num_elem = GET_NUM_ELEM (par_elems);
+	enum machine_mode mode;
+	int i;
+
+	/* Create the first one, so we have something to add to.  */
+	loc_result = loc_descriptor (XEXP (RTVEC_ELT (par_elems, 0), 0),
+				     initialized);
+	if (loc_result == NULL)
+	  return NULL;
+	mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, 0), 0));
+	add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
+	for (i = 1; i < num_elem; i++)
+	  {
+	    dw_loc_descr_ref temp;
+
+	    temp = loc_descriptor (XEXP (RTVEC_ELT (par_elems, i), 0),
+				   initialized);
+	    if (temp == NULL)
+	      return NULL;
+	    add_loc_descr (&loc_result, temp);
+	    mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, i), 0));
+	    add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
+	  }
+      }
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return loc_result;
+}
+
+/* Similar, but generate the descriptor from trees instead of rtl.  This comes
+   up particularly with variable length arrays.  WANT_ADDRESS is 2 if this is
+   a top-level invocation of loc_descriptor_from_tree; is 1 if this is not a
+   top-level invocation, and we require the address of LOC; is 0 if we require
+   the value of LOC.  */
+
+static dw_loc_descr_ref
+loc_descriptor_from_tree_1 (tree loc, int want_address)
+{
+  dw_loc_descr_ref ret, ret1;
+  int have_address = 0;
+  enum dwarf_location_atom op;
+
+  /* ??? Most of the time we do not take proper care for sign/zero
+     extending the values properly.  Hopefully this won't be a real
+     problem...  */
+
+  switch (TREE_CODE (loc))
+    {
+    case ERROR_MARK:
+      return 0;
+
+    case PLACEHOLDER_EXPR:
+      /* This case involves extracting fields from an object to determine the
+	 position of other fields.  We don't try to encode this here.  The
+	 only user of this is Ada, which encodes the needed information using
+	 the names of types.  */
+      return 0;
+
+    case CALL_EXPR:
+      return 0;
+
+    case PREINCREMENT_EXPR:
+    case PREDECREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+      /* There are no opcodes for these operations.  */
+      return 0;
+
+    case ADDR_EXPR:
+      /* If we already want an address, there's nothing we can do.  */
+      if (want_address)
+	return 0;
+
+      /* Otherwise, process the argument and look for the address.  */
+      return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 1);
+
+    case VAR_DECL:
+      if (DECL_THREAD_LOCAL_P (loc))
+	{
+	  rtx rtl;
+	  unsigned first_op;
+	  unsigned second_op;
+
+	  if (targetm.have_tls)
+	    {
+	      /* If this is not defined, we have no way to emit the
+	         data.  */
+	      if (!targetm.asm_out.output_dwarf_dtprel)
+		return 0;
+
+	       /* The way DW_OP_GNU_push_tls_address is specified, we
+	     	  can only look up addresses of objects in the current
+	     	  module.  */
+	      if (DECL_EXTERNAL (loc) && !targetm.binds_local_p (loc))
+		return 0;
+	      first_op = INTERNAL_DW_OP_tls_addr;
+	      second_op = DW_OP_GNU_push_tls_address;
+	    }
+	  else
+	    {
+	      if (!targetm.emutls.debug_form_tls_address)
+		return 0;
+	      loc = emutls_decl (loc);
+	      first_op = DW_OP_addr;
+	      second_op = DW_OP_form_tls_address;
+	    }
+
+	  rtl = rtl_for_decl_location (loc);
+	  if (rtl == NULL_RTX)
+	    return 0;
+
+	  if (!MEM_P (rtl))
+	    return 0;
+	  rtl = XEXP (rtl, 0);
+	  if (! CONSTANT_P (rtl))
+	    return 0;
+
+	  ret = new_loc_descr (first_op, 0, 0);
+	  ret->dw_loc_oprnd1.val_class = dw_val_class_addr;
+	  ret->dw_loc_oprnd1.v.val_addr = rtl;
+
+	  ret1 = new_loc_descr (second_op, 0, 0);
+	  add_loc_descr (&ret, ret1);
+
+	  have_address = 1;
+	  break;
+	}
+      /* FALLTHRU */
+
+    case PARM_DECL:
+      if (DECL_HAS_VALUE_EXPR_P (loc))
+	return loc_descriptor_from_tree_1 (DECL_VALUE_EXPR (loc),
+					   want_address);
+      /* FALLTHRU */
+
+    case RESULT_DECL:
+    case FUNCTION_DECL:
+      {
+	rtx rtl = rtl_for_decl_location (loc);
+
+	if (rtl == NULL_RTX)
+	  return 0;
+	else if (GET_CODE (rtl) == CONST_INT)
+	  {
+	    HOST_WIDE_INT val = INTVAL (rtl);
+	    if (TYPE_UNSIGNED (TREE_TYPE (loc)))
+	      val &= GET_MODE_MASK (DECL_MODE (loc));
+	    ret = int_loc_descriptor (val);
+	  }
+	else if (GET_CODE (rtl) == CONST_STRING)
+	  return 0;
+	else if (CONSTANT_P (rtl))
+	  {
+	    ret = new_loc_descr (DW_OP_addr, 0, 0);
+	    ret->dw_loc_oprnd1.val_class = dw_val_class_addr;
+	    ret->dw_loc_oprnd1.v.val_addr = rtl;
+	  }
+	else
+	  {
+	    enum machine_mode mode;
+
+	    /* Certain constructs can only be represented at top-level.  */
+	    if (want_address == 2)
+	      return loc_descriptor (rtl, VAR_INIT_STATUS_INITIALIZED);
+
+	    mode = GET_MODE (rtl);
+	    if (MEM_P (rtl))
+	      {
+		rtl = XEXP (rtl, 0);
+		have_address = 1;
+	      }
+	    ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+	  }
+      }
+      break;
+
+    case INDIRECT_REF:
+      ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+      have_address = 1;
+      break;
+
+    case COMPOUND_EXPR:
+      return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), want_address);
+
+    CASE_CONVERT:
+    case VIEW_CONVERT_EXPR:
+    case SAVE_EXPR:
+    case MODIFY_EXPR:
+      return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), want_address);
+
+    case COMPONENT_REF:
+    case BIT_FIELD_REF:
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	tree obj, offset;
+	HOST_WIDE_INT bitsize, bitpos, bytepos;
+	enum machine_mode mode;
+	int volatilep;
+	int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc));
+
+	obj = get_inner_reference (loc, &bitsize, &bitpos, &offset, &mode,
+				   &unsignedp, &volatilep, false);
+
+	if (obj == loc)
+	  return 0;
+
+	ret = loc_descriptor_from_tree_1 (obj, 1);
+	if (ret == 0
+	    || bitpos % BITS_PER_UNIT != 0 || bitsize % BITS_PER_UNIT != 0)
+	  return 0;
+
+	if (offset != NULL_TREE)
+	  {
+	    /* Variable offset.  */
+	    ret1 = loc_descriptor_from_tree_1 (offset, 0);
+	    if (ret1 == 0)
+	      return 0;
+	    add_loc_descr (&ret, ret1);
+	    add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0));
+	  }
+
+	bytepos = bitpos / BITS_PER_UNIT;
+	if (bytepos > 0)
+	  add_loc_descr (&ret, new_loc_descr (DW_OP_plus_uconst, bytepos, 0));
+	else if (bytepos < 0)
+	  {
+	    add_loc_descr (&ret, int_loc_descriptor (bytepos));
+	    add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0));
+	  }
+
+	have_address = 1;
+	break;
+      }
+
+    case INTEGER_CST:
+      if (host_integerp (loc, 0))
+	ret = int_loc_descriptor (tree_low_cst (loc, 0));
+      else
+	return 0;
+      break;
+
+    case CONSTRUCTOR:
+      {
+	/* Get an RTL for this, if something has been emitted.  */
+	rtx rtl = lookup_constant_def (loc);
+	enum machine_mode mode;
+
+	if (!rtl || !MEM_P (rtl))
+	  return 0;
+	mode = GET_MODE (rtl);
+	rtl = XEXP (rtl, 0);
+	ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+	have_address = 1;
+	break;
+      }
+
+    case TRUTH_AND_EXPR:
+    case TRUTH_ANDIF_EXPR:
+    case BIT_AND_EXPR:
+      op = DW_OP_and;
+      goto do_binop;
+
+    case TRUTH_XOR_EXPR:
+    case BIT_XOR_EXPR:
+      op = DW_OP_xor;
+      goto do_binop;
+
+    case TRUTH_OR_EXPR:
+    case TRUTH_ORIF_EXPR:
+    case BIT_IOR_EXPR:
+      op = DW_OP_or;
+      goto do_binop;
+
+    case FLOOR_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case TRUNC_DIV_EXPR:
+      op = DW_OP_div;
+      goto do_binop;
+
+    case MINUS_EXPR:
+      op = DW_OP_minus;
+      goto do_binop;
+
+    case FLOOR_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+    case TRUNC_MOD_EXPR:
+      op = DW_OP_mod;
+      goto do_binop;
+
+    case MULT_EXPR:
+      op = DW_OP_mul;
+      goto do_binop;
+
+    case LSHIFT_EXPR:
+      op = DW_OP_shl;
+      goto do_binop;
+
+    case RSHIFT_EXPR:
+      op = (TYPE_UNSIGNED (TREE_TYPE (loc)) ? DW_OP_shr : DW_OP_shra);
+      goto do_binop;
+
+    case POINTER_PLUS_EXPR:
+    case PLUS_EXPR:
+      if (TREE_CODE (TREE_OPERAND (loc, 1)) == INTEGER_CST
+	  && host_integerp (TREE_OPERAND (loc, 1), 0))
+	{
+	  ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+	  if (ret == 0)
+	    return 0;
+
+	  add_loc_descr (&ret,
+			 new_loc_descr (DW_OP_plus_uconst,
+					tree_low_cst (TREE_OPERAND (loc, 1),
+						      0),
+					0));
+	  break;
+	}
+
+      op = DW_OP_plus;
+      goto do_binop;
+
+    case LE_EXPR:
+      if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0))))
+	return 0;
+
+      op = DW_OP_le;
+      goto do_binop;
+
+    case GE_EXPR:
+      if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0))))
+	return 0;
+
+      op = DW_OP_ge;
+      goto do_binop;
+
+    case LT_EXPR:
+      if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0))))
+	return 0;
+
+      op = DW_OP_lt;
+      goto do_binop;
+
+    case GT_EXPR:
+      if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0))))
+	return 0;
+
+      op = DW_OP_gt;
+      goto do_binop;
+
+    case EQ_EXPR:
+      op = DW_OP_eq;
+      goto do_binop;
+
+    case NE_EXPR:
+      op = DW_OP_ne;
+      goto do_binop;
+
+    do_binop:
+      ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+      ret1 = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0);
+      if (ret == 0 || ret1 == 0)
+	return 0;
+
+      add_loc_descr (&ret, ret1);
+      add_loc_descr (&ret, new_loc_descr (op, 0, 0));
+      break;
+
+    case TRUTH_NOT_EXPR:
+    case BIT_NOT_EXPR:
+      op = DW_OP_not;
+      goto do_unop;
+
+    case ABS_EXPR:
+      op = DW_OP_abs;
+      goto do_unop;
+
+    case NEGATE_EXPR:
+      op = DW_OP_neg;
+      goto do_unop;
+
+    do_unop:
+      ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+      if (ret == 0)
+	return 0;
+
+      add_loc_descr (&ret, new_loc_descr (op, 0, 0));
+      break;
+
+    case MIN_EXPR:
+    case MAX_EXPR:
+      {
+	const enum tree_code code =
+	  TREE_CODE (loc) == MIN_EXPR ? GT_EXPR : LT_EXPR;
+
+	loc = build3 (COND_EXPR, TREE_TYPE (loc),
+		      build2 (code, integer_type_node,
+			      TREE_OPERAND (loc, 0), TREE_OPERAND (loc, 1)),
+		      TREE_OPERAND (loc, 1), TREE_OPERAND (loc, 0));
+      }
+
+      /* ... fall through ...  */
+
+    case COND_EXPR:
+      {
+	dw_loc_descr_ref lhs
+	  = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0);
+	dw_loc_descr_ref rhs
+	  = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 2), 0);
+	dw_loc_descr_ref bra_node, jump_node, tmp;
+
+	ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+	if (ret == 0 || lhs == 0 || rhs == 0)
+	  return 0;
+
+	bra_node = new_loc_descr (DW_OP_bra, 0, 0);
+	add_loc_descr (&ret, bra_node);
+
+	add_loc_descr (&ret, rhs);
+	jump_node = new_loc_descr (DW_OP_skip, 0, 0);
+	add_loc_descr (&ret, jump_node);
+
+	add_loc_descr (&ret, lhs);
+	bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	bra_node->dw_loc_oprnd1.v.val_loc = lhs;
+
+	/* ??? Need a node to point the skip at.  Use a nop.  */
+	tmp = new_loc_descr (DW_OP_nop, 0, 0);
+	add_loc_descr (&ret, tmp);
+	jump_node->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	jump_node->dw_loc_oprnd1.v.val_loc = tmp;
+      }
+      break;
+
+    case FIX_TRUNC_EXPR:
+      return 0;
+
+    default:
+      /* Leave front-end specific codes as simply unknown.  This comes
+	 up, for instance, with the C STMT_EXPR.  */
+      if ((unsigned int) TREE_CODE (loc)
+	  >= (unsigned int) LAST_AND_UNUSED_TREE_CODE)
+	return 0;
+
+#ifdef ENABLE_CHECKING
+      /* Otherwise this is a generic code; we should just lists all of
+	 these explicitly.  We forgot one.  */
+      gcc_unreachable ();
+#else
+      /* In a release build, we want to degrade gracefully: better to
+	 generate incomplete debugging information than to crash.  */
+      return NULL;
+#endif
+    }
+
+  /* Show if we can't fill the request for an address.  */
+  if (want_address && !have_address)
+    return 0;
+
+  /* If we've got an address and don't want one, dereference.  */
+  if (!want_address && have_address && ret)
+    {
+      HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (loc));
+
+      if (size > DWARF2_ADDR_SIZE || size == -1)
+	return 0;
+      else if (size == DWARF2_ADDR_SIZE)
+	op = DW_OP_deref;
+      else
+	op = DW_OP_deref_size;
+
+      add_loc_descr (&ret, new_loc_descr (op, size, 0));
+    }
+
+  return ret;
+}
+
+static inline dw_loc_descr_ref
+loc_descriptor_from_tree (tree loc)
+{
+  return loc_descriptor_from_tree_1 (loc, 2);
+}
+
+/* Given a value, round it up to the lowest multiple of `boundary'
+   which is not less than the value itself.  */
+
+static inline HOST_WIDE_INT
+ceiling (HOST_WIDE_INT value, unsigned int boundary)
+{
+  return (((value + boundary - 1) / boundary) * boundary);
+}
+
+/* Given a pointer to what is assumed to be a FIELD_DECL node, return a
+   pointer to the declared type for the relevant field variable, or return
+   `integer_type_node' if the given node turns out to be an
+   ERROR_MARK node.  */
+
+static inline tree
+field_type (const_tree decl)
+{
+  tree type;
+
+  if (TREE_CODE (decl) == ERROR_MARK)
+    return integer_type_node;
+
+  type = DECL_BIT_FIELD_TYPE (decl);
+  if (type == NULL_TREE)
+    type = TREE_TYPE (decl);
+
+  return type;
+}
+
+/* Given a pointer to a tree node, return the alignment in bits for
+   it, or else return BITS_PER_WORD if the node actually turns out to
+   be an ERROR_MARK node.  */
+
+static inline unsigned
+simple_type_align_in_bits (const_tree type)
+{
+  return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD;
+}
+
+static inline unsigned
+simple_decl_align_in_bits (const_tree decl)
+{
+  return (TREE_CODE (decl) != ERROR_MARK) ? DECL_ALIGN (decl) : BITS_PER_WORD;
+}
+
+/* Return the result of rounding T up to ALIGN.  */
+
+static inline HOST_WIDE_INT
+round_up_to_align (HOST_WIDE_INT t, unsigned int align)
+{
+  /* We must be careful if T is negative because HOST_WIDE_INT can be
+     either "above" or "below" unsigned int as per the C promotion
+     rules, depending on the host, thus making the signedness of the
+     direct multiplication and division unpredictable.  */
+  unsigned HOST_WIDE_INT u = (unsigned HOST_WIDE_INT) t;
+
+  u += align - 1;
+  u /= align;
+  u *= align;
+
+  return (HOST_WIDE_INT) u;
+}
+
+/* Given a pointer to a FIELD_DECL, compute and return the byte offset of the
+   lowest addressed byte of the "containing object" for the given FIELD_DECL,
+   or return 0 if we are unable to determine what that offset is, either
+   because the argument turns out to be a pointer to an ERROR_MARK node, or
+   because the offset is actually variable.  (We can't handle the latter case
+   just yet).  */
+
+static HOST_WIDE_INT
+field_byte_offset (const_tree decl)
+{
+  HOST_WIDE_INT object_offset_in_bits;
+  HOST_WIDE_INT bitpos_int;
+
+  if (TREE_CODE (decl) == ERROR_MARK)
+    return 0;
+
+  gcc_assert (TREE_CODE (decl) == FIELD_DECL);
+
+  /* We cannot yet cope with fields whose positions are variable, so
+     for now, when we see such things, we simply return 0.  Someday, we may
+     be able to handle such cases, but it will be damn difficult.  */
+  if (! host_integerp (bit_position (decl), 0))
+    return 0;
+
+  bitpos_int = int_bit_position (decl);
+
+#ifdef PCC_BITFIELD_TYPE_MATTERS
+  if (PCC_BITFIELD_TYPE_MATTERS)
+    {
+      tree type;
+      tree field_size_tree;
+      HOST_WIDE_INT deepest_bitpos;
+      unsigned HOST_WIDE_INT field_size_in_bits;
+      unsigned int type_align_in_bits;
+      unsigned int decl_align_in_bits;
+      unsigned HOST_WIDE_INT type_size_in_bits;
+
+      type = field_type (decl);
+      field_size_tree = DECL_SIZE (decl);
+
+      /* The size could be unspecified if there was an error, or for
+         a flexible array member.  */
+      if (! field_size_tree)
+        field_size_tree = bitsize_zero_node;
+
+      /* If we don't know the size of the field, pretend it's a full word.  */
+      if (host_integerp (field_size_tree, 1))
+        field_size_in_bits = tree_low_cst (field_size_tree, 1);
+      else
+        field_size_in_bits = BITS_PER_WORD;
+
+      type_size_in_bits = simple_type_size_in_bits (type);
+      type_align_in_bits = simple_type_align_in_bits (type);
+      decl_align_in_bits = simple_decl_align_in_bits (decl);
+
+      /* The GCC front-end doesn't make any attempt to keep track of the
+         starting bit offset (relative to the start of the containing
+         structure type) of the hypothetical "containing object" for a
+         bit-field.  Thus, when computing the byte offset value for the
+         start of the "containing object" of a bit-field, we must deduce
+         this information on our own. This can be rather tricky to do in
+         some cases.  For example, handling the following structure type
+         definition when compiling for an i386/i486 target (which only
+         aligns long long's to 32-bit boundaries) can be very tricky:
+
+	 struct S { int field1; long long field2:31; };
+
+         Fortunately, there is a simple rule-of-thumb which can be used
+         in such cases.  When compiling for an i386/i486, GCC will
+         allocate 8 bytes for the structure shown above.  It decides to
+         do this based upon one simple rule for bit-field allocation.
+         GCC allocates each "containing object" for each bit-field at
+         the first (i.e. lowest addressed) legitimate alignment boundary
+         (based upon the required minimum alignment for the declared
+         type of the field) which it can possibly use, subject to the
+         condition that there is still enough available space remaining
+         in the containing object (when allocated at the selected point)
+         to fully accommodate all of the bits of the bit-field itself.
+
+         This simple rule makes it obvious why GCC allocates 8 bytes for
+         each object of the structure type shown above.  When looking
+         for a place to allocate the "containing object" for `field2',
+         the compiler simply tries to allocate a 64-bit "containing
+         object" at each successive 32-bit boundary (starting at zero)
+         until it finds a place to allocate that 64- bit field such that
+         at least 31 contiguous (and previously unallocated) bits remain
+         within that selected 64 bit field.  (As it turns out, for the
+         example above, the compiler finds it is OK to allocate the
+         "containing object" 64-bit field at bit-offset zero within the
+         structure type.)
+
+         Here we attempt to work backwards from the limited set of facts
+         we're given, and we try to deduce from those facts, where GCC
+         must have believed that the containing object started (within
+         the structure type). The value we deduce is then used (by the
+         callers of this routine) to generate DW_AT_location and
+         DW_AT_bit_offset attributes for fields (both bit-fields and, in
+         the case of DW_AT_location, regular fields as well).  */
+
+      /* Figure out the bit-distance from the start of the structure to
+         the "deepest" bit of the bit-field.  */
+      deepest_bitpos = bitpos_int + field_size_in_bits;
+
+      /* This is the tricky part.  Use some fancy footwork to deduce
+         where the lowest addressed bit of the containing object must
+         be.  */
+      object_offset_in_bits = deepest_bitpos - type_size_in_bits;
+
+      /* Round up to type_align by default.  This works best for
+         bitfields.  */
+      object_offset_in_bits
+        = round_up_to_align (object_offset_in_bits, type_align_in_bits);
+
+      if (object_offset_in_bits > bitpos_int)
+        {
+          object_offset_in_bits = deepest_bitpos - type_size_in_bits;
+
+          /* Round up to decl_align instead.  */
+          object_offset_in_bits
+            = round_up_to_align (object_offset_in_bits, decl_align_in_bits);
+        }
+    }
+  else
+#endif
+    object_offset_in_bits = bitpos_int;
+
+  return object_offset_in_bits / BITS_PER_UNIT;
+}
+
+/* The following routines define various Dwarf attributes and any data
+   associated with them.  */
+
+/* Add a location description attribute value to a DIE.
+
+   This emits location attributes suitable for whole variables and
+   whole parameters.  Note that the location attributes for struct fields are
+   generated by the routine `data_member_location_attribute' below.  */
+
+static inline void
+add_AT_location_description (dw_die_ref die, enum dwarf_attribute attr_kind,
+			     dw_loc_descr_ref descr)
+{
+  if (descr != 0)
+    add_AT_loc (die, attr_kind, descr);
+}
+
+/* Attach the specialized form of location attribute used for data members of
+   struct and union types.  In the special case of a FIELD_DECL node which
+   represents a bit-field, the "offset" part of this special location
+   descriptor must indicate the distance in bytes from the lowest-addressed
+   byte of the containing struct or union type to the lowest-addressed byte of
+   the "containing object" for the bit-field.  (See the `field_byte_offset'
+   function above).
+
+   For any given bit-field, the "containing object" is a hypothetical object
+   (of some integral or enum type) within which the given bit-field lives.  The
+   type of this hypothetical "containing object" is always the same as the
+   declared type of the individual bit-field itself (for GCC anyway... the
+   DWARF spec doesn't actually mandate this).  Note that it is the size (in
+   bytes) of the hypothetical "containing object" which will be given in the
+   DW_AT_byte_size attribute for this bit-field.  (See the
+   `byte_size_attribute' function below.)  It is also used when calculating the
+   value of the DW_AT_bit_offset attribute.  (See the `bit_offset_attribute'
+   function below.)  */
+
+static void
+add_data_member_location_attribute (dw_die_ref die, tree decl)
+{
+  HOST_WIDE_INT offset;
+  dw_loc_descr_ref loc_descr = 0;
+
+  if (TREE_CODE (decl) == TREE_BINFO)
+    {
+      /* We're working on the TAG_inheritance for a base class.  */
+      if (BINFO_VIRTUAL_P (decl) && is_cxx ())
+	{
+	  /* For C++ virtual bases we can't just use BINFO_OFFSET, as they
+	     aren't at a fixed offset from all (sub)objects of the same
+	     type.  We need to extract the appropriate offset from our
+	     vtable.  The following dwarf expression means
+
+	       BaseAddr = ObAddr + *((*ObAddr) - Offset)
+
+	     This is specific to the V3 ABI, of course.  */
+
+	  dw_loc_descr_ref tmp;
+
+	  /* Make a copy of the object address.  */
+	  tmp = new_loc_descr (DW_OP_dup, 0, 0);
+	  add_loc_descr (&loc_descr, tmp);
+
+	  /* Extract the vtable address.  */
+	  tmp = new_loc_descr (DW_OP_deref, 0, 0);
+	  add_loc_descr (&loc_descr, tmp);
+
+	  /* Calculate the address of the offset.  */
+	  offset = tree_low_cst (BINFO_VPTR_FIELD (decl), 0);
+	  gcc_assert (offset < 0);
+
+	  tmp = int_loc_descriptor (-offset);
+	  add_loc_descr (&loc_descr, tmp);
+	  tmp = new_loc_descr (DW_OP_minus, 0, 0);
+	  add_loc_descr (&loc_descr, tmp);
+
+	  /* Extract the offset.  */
+	  tmp = new_loc_descr (DW_OP_deref, 0, 0);
+	  add_loc_descr (&loc_descr, tmp);
+
+	  /* Add it to the object address.  */
+	  tmp = new_loc_descr (DW_OP_plus, 0, 0);
+	  add_loc_descr (&loc_descr, tmp);
+	}
+      else
+	offset = tree_low_cst (BINFO_OFFSET (decl), 0);
+    }
+  else
+    offset = field_byte_offset (decl);
+
+  if (! loc_descr)
+    {
+      enum dwarf_location_atom op;
+
+      /* The DWARF2 standard says that we should assume that the structure
+	 address is already on the stack, so we can specify a structure field
+	 address by using DW_OP_plus_uconst.  */
+
+#ifdef MIPS_DEBUGGING_INFO
+      /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst
+	 operator correctly.  It works only if we leave the offset on the
+	 stack.  */
+      op = DW_OP_constu;
+#else
+      op = DW_OP_plus_uconst;
+#endif
+
+      loc_descr = new_loc_descr (op, offset, 0);
+    }
+
+  add_AT_loc (die, DW_AT_data_member_location, loc_descr);
+}
+
+/* Writes integer values to dw_vec_const array.  */
+
+static void
+insert_int (HOST_WIDE_INT val, unsigned int size, unsigned char *dest)
+{
+  while (size != 0)
+    {
+      *dest++ = val & 0xff;
+      val >>= 8;
+      --size;
+    }
+}
+
+/* Reads integers from dw_vec_const array.  Inverse of insert_int.  */
+
+static HOST_WIDE_INT
+extract_int (const unsigned char *src, unsigned int size)
+{
+  HOST_WIDE_INT val = 0;
+
+  src += size;
+  while (size != 0)
+    {
+      val <<= 8;
+      val |= *--src & 0xff;
+      --size;
+    }
+  return val;
+}
+
+/* Writes floating point values to dw_vec_const array.  */
+
+static void
+insert_float (const_rtx rtl, unsigned char *array)
+{
+  REAL_VALUE_TYPE rv;
+  long val[4];
+  int i;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (rv, rtl);
+  real_to_target (val, &rv, GET_MODE (rtl));
+
+  /* real_to_target puts 32-bit pieces in each long.  Pack them.  */
+  for (i = 0; i < GET_MODE_SIZE (GET_MODE (rtl)) / 4; i++)
+    {
+      insert_int (val[i], 4, array);
+      array += 4;
+    }
+}
+
+/* Attach a DW_AT_const_value attribute for a variable or a parameter which
+   does not have a "location" either in memory or in a register.  These
+   things can arise in GNU C when a constant is passed as an actual parameter
+   to an inlined function.  They can also arise in C++ where declared
+   constants do not necessarily get memory "homes".  */
+
+static void
+add_const_value_attribute (dw_die_ref die, rtx rtl)
+{
+  switch (GET_CODE (rtl))
+    {
+    case CONST_INT:
+      {
+	HOST_WIDE_INT val = INTVAL (rtl);
+
+	if (val < 0)
+	  add_AT_int (die, DW_AT_const_value, val);
+	else
+	  add_AT_unsigned (die, DW_AT_const_value, (unsigned HOST_WIDE_INT) val);
+      }
+      break;
+
+    case CONST_DOUBLE:
+      /* Note that a CONST_DOUBLE rtx could represent either an integer or a
+	 floating-point constant.  A CONST_DOUBLE is used whenever the
+	 constant requires more than one word in order to be adequately
+	 represented.  We output CONST_DOUBLEs as blocks.  */
+      {
+	enum machine_mode mode = GET_MODE (rtl);
+
+	if (SCALAR_FLOAT_MODE_P (mode))
+	  {
+	    unsigned int length = GET_MODE_SIZE (mode);
+	    unsigned char *array = GGC_NEWVEC (unsigned char, length);
+
+	    insert_float (rtl, array);
+	    add_AT_vec (die, DW_AT_const_value, length / 4, 4, array);
+	  }
+	else
+	  {
+	    /* ??? We really should be using HOST_WIDE_INT throughout.  */
+	    gcc_assert (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT);
+
+	    add_AT_long_long (die, DW_AT_const_value,
+			      CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl));
+	  }
+      }
+      break;
+
+    case CONST_VECTOR:
+      {
+	enum machine_mode mode = GET_MODE (rtl);
+	unsigned int elt_size = GET_MODE_UNIT_SIZE (mode);
+	unsigned int length = CONST_VECTOR_NUNITS (rtl);
+	unsigned char *array = GGC_NEWVEC (unsigned char, length * elt_size);
+	unsigned int i;
+	unsigned char *p;
+
+	switch (GET_MODE_CLASS (mode))
+	  {
+	  case MODE_VECTOR_INT:
+	    for (i = 0, p = array; i < length; i++, p += elt_size)
+	      {
+		rtx elt = CONST_VECTOR_ELT (rtl, i);
+		HOST_WIDE_INT lo, hi;
+
+		switch (GET_CODE (elt))
+		  {
+		  case CONST_INT:
+		    lo = INTVAL (elt);
+		    hi = -(lo < 0);
+		    break;
+
+		  case CONST_DOUBLE:
+		    lo = CONST_DOUBLE_LOW (elt);
+		    hi = CONST_DOUBLE_HIGH (elt);
+		    break;
+
+		  default:
+		    gcc_unreachable ();
+		  }
+
+		if (elt_size <= sizeof (HOST_WIDE_INT))
+		  insert_int (lo, elt_size, p);
+		else
+		  {
+		    unsigned char *p0 = p;
+		    unsigned char *p1 = p + sizeof (HOST_WIDE_INT);
+
+		    gcc_assert (elt_size == 2 * sizeof (HOST_WIDE_INT));
+		    if (WORDS_BIG_ENDIAN)
+		      {
+			p0 = p1;
+			p1 = p;
+		      }
+		    insert_int (lo, sizeof (HOST_WIDE_INT), p0);
+		    insert_int (hi, sizeof (HOST_WIDE_INT), p1);
+		  }
+	      }
+	    break;
+
+	  case MODE_VECTOR_FLOAT:
+	    for (i = 0, p = array; i < length; i++, p += elt_size)
+	      {
+		rtx elt = CONST_VECTOR_ELT (rtl, i);
+		insert_float (elt, p);
+	      }
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	  }
+
+	add_AT_vec (die, DW_AT_const_value, length, elt_size, array);
+      }
+      break;
+
+    case CONST_STRING:
+      add_AT_string (die, DW_AT_const_value, XSTR (rtl, 0));
+      break;
+
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST:
+      add_AT_addr (die, DW_AT_const_value, rtl);
+      VEC_safe_push (rtx, gc, used_rtx_array, rtl);
+      break;
+
+    case PLUS:
+      /* In cases where an inlined instance of an inline function is passed
+	 the address of an `auto' variable (which is local to the caller) we
+	 can get a situation where the DECL_RTL of the artificial local
+	 variable (for the inlining) which acts as a stand-in for the
+	 corresponding formal parameter (of the inline function) will look
+	 like (plus:SI (reg:SI FRAME_PTR) (const_int ...)).  This is not
+	 exactly a compile-time constant expression, but it isn't the address
+	 of the (artificial) local variable either.  Rather, it represents the
+	 *value* which the artificial local variable always has during its
+	 lifetime.  We currently have no way to represent such quasi-constant
+	 values in Dwarf, so for now we just punt and generate nothing.  */
+      break;
+
+    default:
+      /* No other kinds of rtx should be possible here.  */
+      gcc_unreachable ();
+    }
+
+}
+
+/* Determine whether the evaluation of EXPR references any variables
+   or functions which aren't otherwise used (and therefore may not be
+   output).  */
+static tree
+reference_to_unused (tree * tp, int * walk_subtrees,
+		     void * data ATTRIBUTE_UNUSED)
+{
+  if (! EXPR_P (*tp) && ! CONSTANT_CLASS_P (*tp))
+    *walk_subtrees = 0;
+
+  if (DECL_P (*tp) && ! TREE_PUBLIC (*tp) && ! TREE_USED (*tp)
+      && ! TREE_ASM_WRITTEN (*tp))
+    return *tp;
+  /* ???  The C++ FE emits debug information for using decls, so
+     putting gcc_unreachable here falls over.  See PR31899.  For now
+     be conservative.  */
+  else if (!cgraph_global_info_ready
+	   && (TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == FUNCTION_DECL))
+    return *tp;
+  else if (DECL_P (*tp) && TREE_CODE (*tp) == VAR_DECL)
+    {
+      struct varpool_node *node = varpool_node (*tp);
+      if (!node->needed)
+	return *tp;
+    }
+  else if (DECL_P (*tp) && TREE_CODE (*tp) == FUNCTION_DECL
+	   && (!DECL_EXTERNAL (*tp) || DECL_DECLARED_INLINE_P (*tp)))
+    {
+      struct cgraph_node *node = cgraph_node (*tp);
+      if (!node->output)
+	return *tp;
+    }
+  else if (TREE_CODE (*tp) == STRING_CST && !TREE_ASM_WRITTEN (*tp))
+    return *tp;
+
+  return NULL_TREE;
+}
+
+/* Generate an RTL constant from a decl initializer INIT with decl type TYPE,
+   for use in a later add_const_value_attribute call.  */
+
+static rtx
+rtl_for_decl_init (tree init, tree type)
+{
+  rtx rtl = NULL_RTX;
+
+  /* If a variable is initialized with a string constant without embedded
+     zeros, build CONST_STRING.  */
+  if (TREE_CODE (init) == STRING_CST && TREE_CODE (type) == ARRAY_TYPE)
+    {
+      tree enttype = TREE_TYPE (type);
+      tree domain = TYPE_DOMAIN (type);
+      enum machine_mode mode = TYPE_MODE (enttype);
+
+      if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_SIZE (mode) == 1
+	  && domain
+	  && integer_zerop (TYPE_MIN_VALUE (domain))
+	  && compare_tree_int (TYPE_MAX_VALUE (domain),
+			       TREE_STRING_LENGTH (init) - 1) == 0
+	  && ((size_t) TREE_STRING_LENGTH (init)
+	      == strlen (TREE_STRING_POINTER (init)) + 1))
+	rtl = gen_rtx_CONST_STRING (VOIDmode,
+				    ggc_strdup (TREE_STRING_POINTER (init)));
+    }
+  /* Other aggregates, and complex values, could be represented using
+     CONCAT: FIXME!  */
+  else if (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE)
+    ;
+  /* Vectors only work if their mode is supported by the target.
+     FIXME: generic vectors ought to work too.  */
+  else if (TREE_CODE (type) == VECTOR_TYPE && TYPE_MODE (type) == BLKmode)
+    ;
+  /* If the initializer is something that we know will expand into an
+     immediate RTL constant, expand it now.  We must be careful not to
+     reference variables which won't be output.  */
+  else if (initializer_constant_valid_p (init, type)
+	   && ! walk_tree (&init, reference_to_unused, NULL, NULL))
+    {
+      /* Convert vector CONSTRUCTOR initializers to VECTOR_CST if
+	 possible.  */
+      if (TREE_CODE (type) == VECTOR_TYPE)
+	switch (TREE_CODE (init))
+	  {
+	  case VECTOR_CST:
+	    break;
+	  case CONSTRUCTOR:
+	    if (TREE_CONSTANT (init))
+	      {
+		VEC(constructor_elt,gc) *elts = CONSTRUCTOR_ELTS (init);
+		bool constant_p = true;
+		tree value;
+		unsigned HOST_WIDE_INT ix;
+
+		/* Even when ctor is constant, it might contain non-*_CST
+		   elements (e.g. { 1.0/0.0 - 1.0/0.0, 0.0 }) and those don't
+		   belong into VECTOR_CST nodes.  */
+		FOR_EACH_CONSTRUCTOR_VALUE (elts, ix, value)
+		  if (!CONSTANT_CLASS_P (value))
+		    {
+		      constant_p = false;
+		      break;
+		    }
+
+		if (constant_p)
+		  {
+		    init = build_vector_from_ctor (type, elts);
+		    break;
+		  }
+	      }
+	    /* FALLTHRU */
+
+	  default:
+	    return NULL;
+	  }
+
+      rtl = expand_expr (init, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
+
+      /* If expand_expr returns a MEM, it wasn't immediate.  */
+      gcc_assert (!rtl || !MEM_P (rtl));
+    }
+
+  return rtl;
+}
+
+/* Generate RTL for the variable DECL to represent its location.  */
+
+static rtx
+rtl_for_decl_location (tree decl)
+{
+  rtx rtl;
+
+  /* Here we have to decide where we are going to say the parameter "lives"
+     (as far as the debugger is concerned).  We only have a couple of
+     choices.  GCC provides us with DECL_RTL and with DECL_INCOMING_RTL.
+
+     DECL_RTL normally indicates where the parameter lives during most of the
+     activation of the function.  If optimization is enabled however, this
+     could be either NULL or else a pseudo-reg.  Both of those cases indicate
+     that the parameter doesn't really live anywhere (as far as the code
+     generation parts of GCC are concerned) during most of the function's
+     activation.  That will happen (for example) if the parameter is never
+     referenced within the function.
+
+     We could just generate a location descriptor here for all non-NULL
+     non-pseudo values of DECL_RTL and ignore all of the rest, but we can be
+     a little nicer than that if we also consider DECL_INCOMING_RTL in cases
+     where DECL_RTL is NULL or is a pseudo-reg.
+
+     Note however that we can only get away with using DECL_INCOMING_RTL as
+     a backup substitute for DECL_RTL in certain limited cases.  In cases
+     where DECL_ARG_TYPE (decl) indicates the same type as TREE_TYPE (decl),
+     we can be sure that the parameter was passed using the same type as it is
+     declared to have within the function, and that its DECL_INCOMING_RTL
+     points us to a place where a value of that type is passed.
+
+     In cases where DECL_ARG_TYPE (decl) and TREE_TYPE (decl) are different,
+     we cannot (in general) use DECL_INCOMING_RTL as a substitute for DECL_RTL
+     because in these cases DECL_INCOMING_RTL points us to a value of some
+     type which is *different* from the type of the parameter itself.  Thus,
+     if we tried to use DECL_INCOMING_RTL to generate a location attribute in
+     such cases, the debugger would end up (for example) trying to fetch a
+     `float' from a place which actually contains the first part of a
+     `double'.  That would lead to really incorrect and confusing
+     output at debug-time.
+
+     So, in general, we *do not* use DECL_INCOMING_RTL as a backup for DECL_RTL
+     in cases where DECL_ARG_TYPE (decl) != TREE_TYPE (decl).  There
+     are a couple of exceptions however.  On little-endian machines we can
+     get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE (decl) is
+     not the same as TREE_TYPE (decl), but only when DECL_ARG_TYPE (decl) is
+     an integral type that is smaller than TREE_TYPE (decl). These cases arise
+     when (on a little-endian machine) a non-prototyped function has a
+     parameter declared to be of type `short' or `char'.  In such cases,
+     TREE_TYPE (decl) will be `short' or `char', DECL_ARG_TYPE (decl) will
+     be `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the
+     passed `int' value.  If the debugger then uses that address to fetch
+     a `short' or a `char' (on a little-endian machine) the result will be
+     the correct data, so we allow for such exceptional cases below.
+
+     Note that our goal here is to describe the place where the given formal
+     parameter lives during most of the function's activation (i.e. between the
+     end of the prologue and the start of the epilogue).  We'll do that as best
+     as we can. Note however that if the given formal parameter is modified
+     sometime during the execution of the function, then a stack backtrace (at
+     debug-time) will show the function as having been called with the *new*
+     value rather than the value which was originally passed in.  This happens
+     rarely enough that it is not a major problem, but it *is* a problem, and
+     I'd like to fix it.
+
+     A future version of dwarf2out.c may generate two additional attributes for
+     any given DW_TAG_formal_parameter DIE which will describe the "passed
+     type" and the "passed location" for the given formal parameter in addition
+     to the attributes we now generate to indicate the "declared type" and the
+     "active location" for each parameter.  This additional set of attributes
+     could be used by debuggers for stack backtraces. Separately, note that
+     sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL can be NULL also.
+     This happens (for example) for inlined-instances of inline function formal
+     parameters which are never referenced.  This really shouldn't be
+     happening.  All PARM_DECL nodes should get valid non-NULL
+     DECL_INCOMING_RTL values.  FIXME.  */
+
+  /* Use DECL_RTL as the "location" unless we find something better.  */
+  rtl = DECL_RTL_IF_SET (decl);
+
+  /* When generating abstract instances, ignore everything except
+     constants, symbols living in memory, and symbols living in
+     fixed registers.  */
+  if (! reload_completed)
+    {
+      if (rtl
+	  && (CONSTANT_P (rtl)
+	      || (MEM_P (rtl)
+	          && CONSTANT_P (XEXP (rtl, 0)))
+	      || (REG_P (rtl)
+	          && TREE_CODE (decl) == VAR_DECL
+		  && TREE_STATIC (decl))))
+	{
+	  rtl = targetm.delegitimize_address (rtl);
+	  return rtl;
+	}
+      rtl = NULL_RTX;
+    }
+  else if (TREE_CODE (decl) == PARM_DECL)
+    {
+      if (rtl == NULL_RTX || is_pseudo_reg (rtl))
+	{
+	  tree declared_type = TREE_TYPE (decl);
+	  tree passed_type = DECL_ARG_TYPE (decl);
+	  enum machine_mode dmode = TYPE_MODE (declared_type);
+	  enum machine_mode pmode = TYPE_MODE (passed_type);
+
+	  /* This decl represents a formal parameter which was optimized out.
+	     Note that DECL_INCOMING_RTL may be NULL in here, but we handle
+	     all cases where (rtl == NULL_RTX) just below.  */
+	  if (dmode == pmode)
+	    rtl = DECL_INCOMING_RTL (decl);
+	  else if (SCALAR_INT_MODE_P (dmode)
+		   && GET_MODE_SIZE (dmode) <= GET_MODE_SIZE (pmode)
+		   && DECL_INCOMING_RTL (decl))
+	    {
+	      rtx inc = DECL_INCOMING_RTL (decl);
+	      if (REG_P (inc))
+		rtl = inc;
+	      else if (MEM_P (inc))
+		{
+		  if (BYTES_BIG_ENDIAN)
+		    rtl = adjust_address_nv (inc, dmode,
+					     GET_MODE_SIZE (pmode)
+					     - GET_MODE_SIZE (dmode));
+		  else
+		    rtl = inc;
+		}
+	    }
+	}
+
+      /* If the parm was passed in registers, but lives on the stack, then
+	 make a big endian correction if the mode of the type of the
+	 parameter is not the same as the mode of the rtl.  */
+      /* ??? This is the same series of checks that are made in dbxout.c before
+	 we reach the big endian correction code there.  It isn't clear if all
+	 of these checks are necessary here, but keeping them all is the safe
+	 thing to do.  */
+      else if (MEM_P (rtl)
+	       && XEXP (rtl, 0) != const0_rtx
+	       && ! CONSTANT_P (XEXP (rtl, 0))
+	       /* Not passed in memory.  */
+	       && !MEM_P (DECL_INCOMING_RTL (decl))
+	       /* Not passed by invisible reference.  */
+	       && (!REG_P (XEXP (rtl, 0))
+		   || REGNO (XEXP (rtl, 0)) == HARD_FRAME_POINTER_REGNUM
+		   || REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM
+#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+		   || REGNO (XEXP (rtl, 0)) == ARG_POINTER_REGNUM
+#endif
+		     )
+	       /* Big endian correction check.  */
+	       && BYTES_BIG_ENDIAN
+	       && TYPE_MODE (TREE_TYPE (decl)) != GET_MODE (rtl)
+	       && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl)))
+		   < UNITS_PER_WORD))
+	{
+	  int offset = (UNITS_PER_WORD
+			- GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl))));
+
+	  rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (decl)),
+			     plus_constant (XEXP (rtl, 0), offset));
+	}
+    }
+  else if (TREE_CODE (decl) == VAR_DECL
+	   && rtl
+	   && MEM_P (rtl)
+	   && GET_MODE (rtl) != TYPE_MODE (TREE_TYPE (decl))
+	   && BYTES_BIG_ENDIAN)
+    {
+      int rsize = GET_MODE_SIZE (GET_MODE (rtl));
+      int dsize = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl)));
+
+      /* If a variable is declared "register" yet is smaller than
+	 a register, then if we store the variable to memory, it
+	 looks like we're storing a register-sized value, when in
+	 fact we are not.  We need to adjust the offset of the
+	 storage location to reflect the actual value's bytes,
+	 else gdb will not be able to display it.  */
+      if (rsize > dsize)
+	rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (decl)),
+			   plus_constant (XEXP (rtl, 0), rsize-dsize));
+    }
+
+  /* A variable with no DECL_RTL but a DECL_INITIAL is a compile-time constant,
+     and will have been substituted directly into all expressions that use it.
+     C does not have such a concept, but C++ and other languages do.  */
+  if (!rtl && TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl))
+    rtl = rtl_for_decl_init (DECL_INITIAL (decl), TREE_TYPE (decl));
+
+  if (rtl)
+    rtl = targetm.delegitimize_address (rtl);
+
+  /* If we don't look past the constant pool, we risk emitting a
+     reference to a constant pool entry that isn't referenced from
+     code, and thus is not emitted.  */
+  if (rtl)
+    rtl = avoid_constant_pool_reference (rtl);
+
+  return rtl;
+}
+
+/* We need to figure out what section we should use as the base for the
+   address ranges where a given location is valid.
+   1. If this particular DECL has a section associated with it, use that.
+   2. If this function has a section associated with it, use that.
+   3. Otherwise, use the text section.
+   XXX: If you split a variable across multiple sections, we won't notice.  */
+
+static const char *
+secname_for_decl (const_tree decl)
+{
+  const char *secname;
+
+  if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_SECTION_NAME (decl))
+    {
+      tree sectree = DECL_SECTION_NAME (decl);
+      secname = TREE_STRING_POINTER (sectree);
+    }
+  else if (current_function_decl && DECL_SECTION_NAME (current_function_decl))
+    {
+      tree sectree = DECL_SECTION_NAME (current_function_decl);
+      secname = TREE_STRING_POINTER (sectree);
+    }
+  else if (cfun && in_cold_section_p)
+    secname = crtl->subsections.cold_section_label;
+  else
+    secname = text_section_label;
+
+  return secname;
+}
+
+/* Check whether decl is a Fortran COMMON symbol.  If not, NULL_TREE is
+   returned.  If so, the decl for the COMMON block is returned, and the
+   value is the offset into the common block for the symbol.  */
+
+static tree
+fortran_common (tree decl, HOST_WIDE_INT *value)
+{
+  tree val_expr, cvar;
+  enum machine_mode mode;
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  int volatilep = 0, unsignedp = 0;
+
+  /* If the decl isn't a VAR_DECL, or if it isn't public or static, or if
+     it does not have a value (the offset into the common area), or if it
+     is thread local (as opposed to global) then it isn't common, and shouldn't
+     be handled as such.  */
+  if (TREE_CODE (decl) != VAR_DECL
+      || !TREE_PUBLIC (decl)
+      || !TREE_STATIC (decl)
+      || !DECL_HAS_VALUE_EXPR_P (decl)
+      || !is_fortran ())
+    return NULL_TREE;
+
+  val_expr = DECL_VALUE_EXPR (decl);
+  if (TREE_CODE (val_expr) != COMPONENT_REF)
+    return NULL_TREE;
+
+  cvar = get_inner_reference (val_expr, &bitsize, &bitpos, &offset,
+			      &mode, &unsignedp, &volatilep, true);
+
+  if (cvar == NULL_TREE
+      || TREE_CODE (cvar) != VAR_DECL
+      || DECL_ARTIFICIAL (cvar)
+      || !TREE_PUBLIC (cvar))
+    return NULL_TREE;
+
+  *value = 0;
+  if (offset != NULL)
+    {
+      if (!host_integerp (offset, 0))
+	return NULL_TREE;
+      *value = tree_low_cst (offset, 0);
+    }
+  if (bitpos != 0)
+    *value += bitpos / BITS_PER_UNIT;
+
+  return cvar;
+}
+
+/* Dereference a location expression LOC if DECL is passed by invisible
+   reference.  */
+
+static dw_loc_descr_ref
+loc_by_reference (dw_loc_descr_ref loc, tree decl)
+{
+  HOST_WIDE_INT size;
+  enum dwarf_location_atom op;
+
+  if (loc == NULL)
+    return NULL;
+
+  if ((TREE_CODE (decl) != PARM_DECL && TREE_CODE (decl) != RESULT_DECL)
+      || !DECL_BY_REFERENCE (decl))
+    return loc;
+
+  size = int_size_in_bytes (TREE_TYPE (decl));
+  if (size > DWARF2_ADDR_SIZE || size == -1)
+    return 0;
+  else if (size == DWARF2_ADDR_SIZE)
+    op = DW_OP_deref;
+  else
+    op = DW_OP_deref_size;
+  add_loc_descr (&loc, new_loc_descr (op, size, 0));
+  return loc;
+}
+
+/* Generate *either* a DW_AT_location attribute or else a DW_AT_const_value
+   data attribute for a variable or a parameter.  We generate the
+   DW_AT_const_value attribute only in those cases where the given variable
+   or parameter does not have a true "location" either in memory or in a
+   register.  This can happen (for example) when a constant is passed as an
+   actual argument in a call to an inline function.  (It's possible that
+   these things can crop up in other ways also.)  Note that one type of
+   constant value which can be passed into an inlined function is a constant
+   pointer.  This can happen for example if an actual argument in an inlined
+   function call evaluates to a compile-time constant address.  */
+
+static void
+add_location_or_const_value_attribute (dw_die_ref die, tree decl,
+				       enum dwarf_attribute attr)
+{
+  rtx rtl;
+  dw_loc_descr_ref descr;
+  var_loc_list *loc_list;
+  struct var_loc_node *node;
+  if (TREE_CODE (decl) == ERROR_MARK)
+    return;
+
+  gcc_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL
+	      || TREE_CODE (decl) == RESULT_DECL);
+
+  /* See if we possibly have multiple locations for this variable.  */
+  loc_list = lookup_decl_loc (decl);
+
+  /* If it truly has multiple locations, the first and last node will
+     differ.  */
+  if (loc_list && loc_list->first != loc_list->last)
+    {
+      const char *endname, *secname;
+      dw_loc_list_ref list;
+      rtx varloc;
+      enum var_init_status initialized;
+
+      /* Now that we know what section we are using for a base,
+	 actually construct the list of locations.
+	 The first location information is what is passed to the
+	 function that creates the location list, and the remaining
+	 locations just get added on to that list.
+	 Note that we only know the start address for a location
+	 (IE location changes), so to build the range, we use
+	 the range [current location start, next location start].
+	 This means we have to special case the last node, and generate
+	 a range of [last location start, end of function label].  */
+
+      node = loc_list->first;
+      varloc = NOTE_VAR_LOCATION (node->var_loc_note);
+      secname = secname_for_decl (decl);
+
+      if (NOTE_VAR_LOCATION_LOC (node->var_loc_note))
+	initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+      else
+	initialized = VAR_INIT_STATUS_INITIALIZED;
+
+      descr = loc_by_reference (loc_descriptor (varloc, initialized), decl);
+      list = new_loc_list (descr, node->label, node->next->label, secname, 1);
+      node = node->next;
+
+      for (; node->next; node = node->next)
+	if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
+	  {
+	    /* The variable has a location between NODE->LABEL and
+	       NODE->NEXT->LABEL.  */
+	    enum var_init_status initialized =
+	      NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+	    varloc = NOTE_VAR_LOCATION (node->var_loc_note);
+	    descr = loc_by_reference (loc_descriptor (varloc, initialized),
+				      decl);
+	    add_loc_descr_to_loc_list (&list, descr,
+				       node->label, node->next->label, secname);
+	  }
+
+      /* If the variable has a location at the last label
+	 it keeps its location until the end of function.  */
+      if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
+	{
+	  char label_id[MAX_ARTIFICIAL_LABEL_BYTES];
+	  enum var_init_status initialized =
+	    NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+
+	  varloc = NOTE_VAR_LOCATION (node->var_loc_note);
+	  if (!current_function_decl)
+	    endname = text_end_label;
+	  else
+	    {
+	      ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
+					   current_function_funcdef_no);
+	      endname = ggc_strdup (label_id);
+	    }
+	  descr = loc_by_reference (loc_descriptor (varloc, initialized),
+				    decl);
+	  add_loc_descr_to_loc_list (&list, descr,
+				     node->label, endname, secname);
+	}
+
+      /* Finally, add the location list to the DIE, and we are done.  */
+      add_AT_loc_list (die, attr, list);
+      return;
+    }
+
+  /* Try to get some constant RTL for this decl, and use that as the value of
+     the location.  */
+
+  rtl = rtl_for_decl_location (decl);
+  if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING))
+    {
+      add_const_value_attribute (die, rtl);
+      return;
+    }
+
+  /* If we have tried to generate the location otherwise, and it
+     didn't work out (we wouldn't be here if we did), and we have a one entry
+     location list, try generating a location from that.  */
+  if (loc_list && loc_list->first)
+    {
+      enum var_init_status status;
+      node = loc_list->first;
+      status = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+      descr = loc_descriptor (NOTE_VAR_LOCATION (node->var_loc_note), status);
+      if (descr)
+	{
+	  descr = loc_by_reference (descr, decl);
+	  add_AT_location_description (die, attr, descr);
+	  return;
+	}
+    }
+
+  /* We couldn't get any rtl, so try directly generating the location
+     description from the tree.  */
+  descr = loc_descriptor_from_tree (decl);
+  if (descr)
+    {
+      descr = loc_by_reference (descr, decl);
+      add_AT_location_description (die, attr, descr);
+      return;
+    }
+  /* None of that worked, so it must not really have a location;
+     try adding a constant value attribute from the DECL_INITIAL.  */
+  tree_add_const_value_attribute (die, decl);
+}
+
+/* Add VARIABLE and DIE into deferred locations list.  */
+
+static void
+defer_location (tree variable, dw_die_ref die)
+{
+  deferred_locations entry;
+  entry.variable = variable;
+  entry.die = die;
+  VEC_safe_push (deferred_locations, gc, deferred_locations_list, &entry);
+}
+
+/* Helper function for tree_add_const_value_attribute.  Natively encode
+   initializer INIT into an array.  Return true if successful.  */
+
+static bool
+native_encode_initializer (tree init, unsigned char *array, int size)
+{
+  tree type;
+
+  if (init == NULL_TREE)
+    return false;
+
+  STRIP_NOPS (init);
+  switch (TREE_CODE (init))
+    {
+    case STRING_CST:
+      type = TREE_TYPE (init);
+      if (TREE_CODE (type) == ARRAY_TYPE)
+	{
+	  tree enttype = TREE_TYPE (type);
+	  enum machine_mode mode = TYPE_MODE (enttype);
+
+	  if (GET_MODE_CLASS (mode) != MODE_INT || GET_MODE_SIZE (mode) != 1)
+	    return false;
+	  if (int_size_in_bytes (type) != size)
+	    return false;
+	  if (size > TREE_STRING_LENGTH (init))
+	    {
+	      memcpy (array, TREE_STRING_POINTER (init),
+		      TREE_STRING_LENGTH (init));
+	      memset (array + TREE_STRING_LENGTH (init),
+		      '\0', size - TREE_STRING_LENGTH (init));
+	    }
+	  else
+	    memcpy (array, TREE_STRING_POINTER (init), size);
+	  return true;
+	}
+      return false;
+    case CONSTRUCTOR:
+      type = TREE_TYPE (init);
+      if (int_size_in_bytes (type) != size)
+	return false;
+      if (TREE_CODE (type) == ARRAY_TYPE)
+	{
+	  HOST_WIDE_INT min_index;
+	  unsigned HOST_WIDE_INT cnt;
+	  int curpos = 0, fieldsize;
+	  constructor_elt *ce;
+
+	  if (TYPE_DOMAIN (type) == NULL_TREE
+	      || !host_integerp (TYPE_MIN_VALUE (TYPE_DOMAIN (type)), 0))
+	    return false;
+
+	  fieldsize = int_size_in_bytes (TREE_TYPE (type));
+	  if (fieldsize <= 0)
+	    return false;
+
+	  min_index = tree_low_cst (TYPE_MIN_VALUE (TYPE_DOMAIN (type)), 0);
+	  memset (array, '\0', size);
+	  for (cnt = 0;
+	       VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce);
+	       cnt++)
+	    {
+	      tree val = ce->value;
+	      tree index = ce->index;
+	      int pos = curpos;
+	      if (index && TREE_CODE (index) == RANGE_EXPR)
+		pos = (tree_low_cst (TREE_OPERAND (index, 0), 0) - min_index)
+		      * fieldsize;
+	      else if (index)
+		pos = (tree_low_cst (index, 0) - min_index) * fieldsize;
+
+	      if (val)
+		{
+		  STRIP_NOPS (val);
+		  if (!native_encode_initializer (val, array + pos, fieldsize))
+		    return false;
+		}
+	      curpos = pos + fieldsize;
+	      if (index && TREE_CODE (index) == RANGE_EXPR)
+		{
+		  int count = tree_low_cst (TREE_OPERAND (index, 1), 0)
+			      - tree_low_cst (TREE_OPERAND (index, 0), 0);
+		  while (count > 0)
+		    {
+		      if (val)
+			memcpy (array + curpos, array + pos, fieldsize);
+		      curpos += fieldsize;
+		    }
+		}
+	      gcc_assert (curpos <= size);
+	    }
+	  return true;
+	}
+      else if (TREE_CODE (type) == RECORD_TYPE
+	       || TREE_CODE (type) == UNION_TYPE)
+	{
+	  tree field = NULL_TREE;
+	  unsigned HOST_WIDE_INT cnt;
+	  constructor_elt *ce;
+
+	  if (int_size_in_bytes (type) != size)
+	    return false;
+
+	  if (TREE_CODE (type) == RECORD_TYPE)
+	    field = TYPE_FIELDS (type);
+
+	  for (cnt = 0;
+	       VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce);
+	       cnt++, field = field ? TREE_CHAIN (field) : 0)
+	    {
+	      tree val = ce->value;
+	      int pos, fieldsize;
+
+	      if (ce->index != 0)
+		field = ce->index;
+
+	      if (val)
+		STRIP_NOPS (val);
+
+	      if (field == NULL_TREE || DECL_BIT_FIELD (field))
+		return false;
+
+	      if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE
+		  && TYPE_DOMAIN (TREE_TYPE (field))
+		  && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field))))
+		return false;
+	      else if (DECL_SIZE_UNIT (field) == NULL_TREE
+		       || !host_integerp (DECL_SIZE_UNIT (field), 0))
+		return false;
+	      fieldsize = tree_low_cst (DECL_SIZE_UNIT (field), 0);
+	      pos = int_byte_position (field);
+	      gcc_assert (pos + fieldsize <= size);
+	      if (val
+		  && !native_encode_initializer (val, array + pos, fieldsize))
+		return false;
+	    }
+	  return true;
+	}
+      return false;
+    case VIEW_CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+      return native_encode_initializer (TREE_OPERAND (init, 0), array, size);
+    default:
+      return native_encode_expr (init, array, size) == size;
+    }
+}
+
+/* If we don't have a copy of this variable in memory for some reason (such
+   as a C++ member constant that doesn't have an out-of-line definition),
+   we should tell the debugger about the constant value.  */
+
+static void
+tree_add_const_value_attribute (dw_die_ref var_die, tree decl)
+{
+  tree init;
+  tree type = TREE_TYPE (decl);
+  rtx rtl;
+
+  if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL)
+    return;
+
+  init = DECL_INITIAL (decl);
+  if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl) && init)
+    /* OK */;
+  else
+    return;
+
+  rtl = rtl_for_decl_init (init, type);
+  if (rtl)
+    add_const_value_attribute (var_die, rtl);
+  /* If the host and target are sane, try harder.  */
+  else if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
+	   && initializer_constant_valid_p (init, type))
+    {
+      HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (init));
+      if (size > 0 && (int) size == size)
+	{
+	  unsigned char *array = GGC_CNEWVEC (unsigned char, size);
+
+	  if (native_encode_initializer (init, array, size))
+	    add_AT_vec (var_die, DW_AT_const_value, size, 1, array);
+	}
+    }
+}
+
+/* Convert the CFI instructions for the current function into a
+   location list.  This is used for DW_AT_frame_base when we targeting
+   a dwarf2 consumer that does not support the dwarf3
+   DW_OP_call_frame_cfa.  OFFSET is a constant to be added to all CFA
+   expressions.  */
+
+static dw_loc_list_ref
+convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
+{
+  dw_fde_ref fde;
+  dw_loc_list_ref list, *list_tail;
+  dw_cfi_ref cfi;
+  dw_cfa_location last_cfa, next_cfa;
+  const char *start_label, *last_label, *section;
+
+  fde = current_fde ();
+  gcc_assert (fde != NULL);
+
+  section = secname_for_decl (current_function_decl);
+  list_tail = &list;
+  list = NULL;
+
+  next_cfa.reg = INVALID_REGNUM;
+  next_cfa.offset = 0;
+  next_cfa.indirect = 0;
+  next_cfa.base_offset = 0;
+
+  start_label = fde->dw_fde_begin;
+
+  /* ??? Bald assumption that the CIE opcode list does not contain
+     advance opcodes.  */
+  for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next)
+    lookup_cfa_1 (cfi, &next_cfa);
+
+  last_cfa = next_cfa;
+  last_label = start_label;
+
+  for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next)
+    switch (cfi->dw_cfi_opc)
+      {
+      case DW_CFA_set_loc:
+      case DW_CFA_advance_loc1:
+      case DW_CFA_advance_loc2:
+      case DW_CFA_advance_loc4:
+	if (!cfa_equal_p (&last_cfa, &next_cfa))
+	  {
+	    *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
+				       start_label, last_label, section,
+				       list == NULL);
+
+	    list_tail = &(*list_tail)->dw_loc_next;
+	    last_cfa = next_cfa;
+	    start_label = last_label;
+	  }
+	last_label = cfi->dw_cfi_oprnd1.dw_cfi_addr;
+	break;
+
+      case DW_CFA_advance_loc:
+	/* The encoding is complex enough that we should never emit this.  */
+      case DW_CFA_remember_state:
+      case DW_CFA_restore_state:
+	/* We don't handle these two in this function.  It would be possible
+	   if it were to be required.  */
+	gcc_unreachable ();
+
+      default:
+	lookup_cfa_1 (cfi, &next_cfa);
+	break;
+      }
+
+  if (!cfa_equal_p (&last_cfa, &next_cfa))
+    {
+      *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
+				 start_label, last_label, section,
+				 list == NULL);
+      list_tail = &(*list_tail)->dw_loc_next;
+      start_label = last_label;
+    }
+  *list_tail = new_loc_list (build_cfa_loc (&next_cfa, offset),
+			     start_label, fde->dw_fde_end, section,
+			     list == NULL);
+
+  return list;
+}
+
+/* Compute a displacement from the "steady-state frame pointer" to the
+   frame base (often the same as the CFA), and store it in
+   frame_pointer_fb_offset.  OFFSET is added to the displacement
+   before the latter is negated.  */
+
+static void
+compute_frame_pointer_to_fb_displacement (HOST_WIDE_INT offset)
+{
+  rtx reg, elim;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+  reg = frame_pointer_rtx;
+  offset += FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
+  reg = arg_pointer_rtx;
+  offset += ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
+
+  elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+  if (GET_CODE (elim) == PLUS)
+    {
+      offset += INTVAL (XEXP (elim, 1));
+      elim = XEXP (elim, 0);
+    }
+
+  gcc_assert ((SUPPORTS_STACK_ALIGNMENT
+	       && (elim == hard_frame_pointer_rtx
+		   || elim == stack_pointer_rtx))
+	      || elim == (frame_pointer_needed
+			  ? hard_frame_pointer_rtx
+			  : stack_pointer_rtx));
+
+  frame_pointer_fb_offset = -offset;
+}
+
+/* Generate a DW_AT_name attribute given some string value to be included as
+   the value of the attribute.  */
+
+static void
+add_name_attribute (dw_die_ref die, const char *name_string)
+{
+  if (name_string != NULL && *name_string != 0)
+    {
+      if (demangle_name_func)
+	name_string = (*demangle_name_func) (name_string);
+
+      add_AT_string (die, DW_AT_name, name_string);
+    }
+}
+
+/* Generate a DW_AT_comp_dir attribute for DIE.  */
+
+static void
+add_comp_dir_attribute (dw_die_ref die)
+{
+  const char *wd = get_src_pwd ();
+  if (wd != NULL)
+    add_AT_string (die, DW_AT_comp_dir, remap_debug_filename (wd));
+}
+
+/* Given a tree node describing an array bound (either lower or upper) output
+   a representation for that bound.  */
+
+static void
+add_bound_info (dw_die_ref subrange_die, enum dwarf_attribute bound_attr, tree bound)
+{
+  switch (TREE_CODE (bound))
+    {
+    case ERROR_MARK:
+      return;
+
+    /* All fixed-bounds are represented by INTEGER_CST nodes.  */
+    case INTEGER_CST:
+      if (! host_integerp (bound, 0)
+	  || (bound_attr == DW_AT_lower_bound
+	      && (((is_c_family () || is_java ()) &&  integer_zerop (bound))
+		  || (is_fortran () && integer_onep (bound)))))
+	/* Use the default.  */
+	;
+      else
+	add_AT_unsigned (subrange_die, bound_attr, tree_low_cst (bound, 0));
+      break;
+
+    CASE_CONVERT:
+    case VIEW_CONVERT_EXPR:
+      add_bound_info (subrange_die, bound_attr, TREE_OPERAND (bound, 0));
+      break;
+
+    case SAVE_EXPR:
+      break;
+
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      {
+	dw_die_ref decl_die = lookup_decl_die (bound);
+	dw_loc_descr_ref loc;
+
+	/* ??? Can this happen, or should the variable have been bound
+	   first?  Probably it can, since I imagine that we try to create
+	   the types of parameters in the order in which they exist in
+	   the list, and won't have created a forward reference to a
+	   later parameter.  */
+	if (decl_die != NULL)
+	  add_AT_die_ref (subrange_die, bound_attr, decl_die);
+	else
+	  {
+	    loc = loc_descriptor_from_tree_1 (bound, 0);
+	    add_AT_location_description (subrange_die, bound_attr, loc);
+	  }
+	break;
+      }
+
+    default:
+      {
+	/* Otherwise try to create a stack operation procedure to
+	   evaluate the value of the array bound.  */
+
+	dw_die_ref ctx, decl_die;
+	dw_loc_descr_ref loc;
+
+	loc = loc_descriptor_from_tree (bound);
+	if (loc == NULL)
+	  break;
+
+	if (current_function_decl == 0)
+	  ctx = comp_unit_die;
+	else
+	  ctx = lookup_decl_die (current_function_decl);
+
+	decl_die = new_die (DW_TAG_variable, ctx, bound);
+	add_AT_flag (decl_die, DW_AT_artificial, 1);
+	add_type_attribute (decl_die, TREE_TYPE (bound), 1, 0, ctx);
+	add_AT_loc (decl_die, DW_AT_location, loc);
+
+	add_AT_die_ref (subrange_die, bound_attr, decl_die);
+	break;
+      }
+    }
+}
+
+/* Add subscript info to TYPE_DIE, describing an array TYPE, collapsing
+   possibly nested array subscripts in a flat sequence if COLLAPSE_P is true.
+   Note that the block of subscript information for an array type also
+   includes information about the element type of the given array type.  */
+
+static void
+add_subscript_info (dw_die_ref type_die, tree type, bool collapse_p)
+{
+  unsigned dimension_number;
+  tree lower, upper;
+  dw_die_ref subrange_die;
+
+  for (dimension_number = 0;
+       TREE_CODE (type) == ARRAY_TYPE && (dimension_number == 0 || collapse_p);
+       type = TREE_TYPE (type), dimension_number++)
+    {
+      tree domain = TYPE_DOMAIN (type);
+
+      if (TYPE_STRING_FLAG (type) && is_fortran () && dimension_number > 0)
+	break;
+
+      /* Arrays come in three flavors: Unspecified bounds, fixed bounds,
+	 and (in GNU C only) variable bounds.  Handle all three forms
+	 here.  */
+      subrange_die = new_die (DW_TAG_subrange_type, type_die, NULL);
+      if (domain)
+	{
+	  /* We have an array type with specified bounds.  */
+	  lower = TYPE_MIN_VALUE (domain);
+	  upper = TYPE_MAX_VALUE (domain);
+
+	  /* Define the index type.  */
+	  if (TREE_TYPE (domain))
+	    {
+	      /* ??? This is probably an Ada unnamed subrange type.  Ignore the
+		 TREE_TYPE field.  We can't emit debug info for this
+		 because it is an unnamed integral type.  */
+	      if (TREE_CODE (domain) == INTEGER_TYPE
+		  && TYPE_NAME (domain) == NULL_TREE
+		  && TREE_CODE (TREE_TYPE (domain)) == INTEGER_TYPE
+		  && TYPE_NAME (TREE_TYPE (domain)) == NULL_TREE)
+		;
+	      else
+		add_type_attribute (subrange_die, TREE_TYPE (domain), 0, 0,
+				    type_die);
+	    }
+
+	  /* ??? If upper is NULL, the array has unspecified length,
+	     but it does have a lower bound.  This happens with Fortran
+	       dimension arr(N:*)
+	     Since the debugger is definitely going to need to know N
+	     to produce useful results, go ahead and output the lower
+	     bound solo, and hope the debugger can cope.  */
+
+	  add_bound_info (subrange_die, DW_AT_lower_bound, lower);
+	  if (upper)
+	    add_bound_info (subrange_die, DW_AT_upper_bound, upper);
+	}
+
+      /* Otherwise we have an array type with an unspecified length.  The
+	 DWARF-2 spec does not say how to handle this; let's just leave out the
+	 bounds.  */
+    }
+}
+
+static void
+add_byte_size_attribute (dw_die_ref die, tree tree_node)
+{
+  unsigned size;
+
+  switch (TREE_CODE (tree_node))
+    {
+    case ERROR_MARK:
+      size = 0;
+      break;
+    case ENUMERAL_TYPE:
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      size = int_size_in_bytes (tree_node);
+      break;
+    case FIELD_DECL:
+      /* For a data member of a struct or union, the DW_AT_byte_size is
+	 generally given as the number of bytes normally allocated for an
+	 object of the *declared* type of the member itself.  This is true
+	 even for bit-fields.  */
+      size = simple_type_size_in_bits (field_type (tree_node)) / BITS_PER_UNIT;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Note that `size' might be -1 when we get to this point.  If it is, that
+     indicates that the byte size of the entity in question is variable.  We
+     have no good way of expressing this fact in Dwarf at the present time,
+     so just let the -1 pass on through.  */
+  add_AT_unsigned (die, DW_AT_byte_size, size);
+}
+
+/* For a FIELD_DECL node which represents a bit-field, output an attribute
+   which specifies the distance in bits from the highest order bit of the
+   "containing object" for the bit-field to the highest order bit of the
+   bit-field itself.
+
+   For any given bit-field, the "containing object" is a hypothetical object
+   (of some integral or enum type) within which the given bit-field lives.  The
+   type of this hypothetical "containing object" is always the same as the
+   declared type of the individual bit-field itself.  The determination of the
+   exact location of the "containing object" for a bit-field is rather
+   complicated.  It's handled by the `field_byte_offset' function (above).
+
+   Note that it is the size (in bytes) of the hypothetical "containing object"
+   which will be given in the DW_AT_byte_size attribute for this bit-field.
+   (See `byte_size_attribute' above).  */
+
+static inline void
+add_bit_offset_attribute (dw_die_ref die, tree decl)
+{
+  HOST_WIDE_INT object_offset_in_bytes = field_byte_offset (decl);
+  tree type = DECL_BIT_FIELD_TYPE (decl);
+  HOST_WIDE_INT bitpos_int;
+  HOST_WIDE_INT highest_order_object_bit_offset;
+  HOST_WIDE_INT highest_order_field_bit_offset;
+  HOST_WIDE_INT unsigned bit_offset;
+
+  /* Must be a field and a bit field.  */
+  gcc_assert (type && TREE_CODE (decl) == FIELD_DECL);
+
+  /* We can't yet handle bit-fields whose offsets are variable, so if we
+     encounter such things, just return without generating any attribute
+     whatsoever.  Likewise for variable or too large size.  */
+  if (! host_integerp (bit_position (decl), 0)
+      || ! host_integerp (DECL_SIZE (decl), 1))
+    return;
+
+  bitpos_int = int_bit_position (decl);
+
+  /* Note that the bit offset is always the distance (in bits) from the
+     highest-order bit of the "containing object" to the highest-order bit of
+     the bit-field itself.  Since the "high-order end" of any object or field
+     is different on big-endian and little-endian machines, the computation
+     below must take account of these differences.  */
+  highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT;
+  highest_order_field_bit_offset = bitpos_int;
+
+  if (! BYTES_BIG_ENDIAN)
+    {
+      highest_order_field_bit_offset += tree_low_cst (DECL_SIZE (decl), 0);
+      highest_order_object_bit_offset += simple_type_size_in_bits (type);
+    }
+
+  bit_offset
+    = (! BYTES_BIG_ENDIAN
+       ? highest_order_object_bit_offset - highest_order_field_bit_offset
+       : highest_order_field_bit_offset - highest_order_object_bit_offset);
+
+  add_AT_unsigned (die, DW_AT_bit_offset, bit_offset);
+}
+
+/* For a FIELD_DECL node which represents a bit field, output an attribute
+   which specifies the length in bits of the given field.  */
+
+static inline void
+add_bit_size_attribute (dw_die_ref die, tree decl)
+{
+  /* Must be a field and a bit field.  */
+  gcc_assert (TREE_CODE (decl) == FIELD_DECL
+	      && DECL_BIT_FIELD_TYPE (decl));
+
+  if (host_integerp (DECL_SIZE (decl), 1))
+    add_AT_unsigned (die, DW_AT_bit_size, tree_low_cst (DECL_SIZE (decl), 1));
+}
+
+/* If the compiled language is ANSI C, then add a 'prototyped'
+   attribute, if arg types are given for the parameters of a function.  */
+
+static inline void
+add_prototyped_attribute (dw_die_ref die, tree func_type)
+{
+  if (get_AT_unsigned (comp_unit_die, DW_AT_language) == DW_LANG_C89
+      && TYPE_ARG_TYPES (func_type) != NULL)
+    add_AT_flag (die, DW_AT_prototyped, 1);
+}
+
+/* Add an 'abstract_origin' attribute below a given DIE.  The DIE is found
+   by looking in either the type declaration or object declaration
+   equate table.  */
+
+static inline dw_die_ref
+add_abstract_origin_attribute (dw_die_ref die, tree origin)
+{
+  dw_die_ref origin_die = NULL;
+
+  if (TREE_CODE (origin) != FUNCTION_DECL)
+    {
+      /* We may have gotten separated from the block for the inlined
+	 function, if we're in an exception handler or some such; make
+	 sure that the abstract function has been written out.
+
+	 Doing this for nested functions is wrong, however; functions are
+	 distinct units, and our context might not even be inline.  */
+      tree fn = origin;
+
+      if (TYPE_P (fn))
+	fn = TYPE_STUB_DECL (fn);
+
+      fn = decl_function_context (fn);
+      if (fn)
+	dwarf2out_abstract_function (fn);
+    }
+
+  if (DECL_P (origin))
+    origin_die = lookup_decl_die (origin);
+  else if (TYPE_P (origin))
+    origin_die = lookup_type_die (origin);
+
+  /* XXX: Functions that are never lowered don't always have correct block
+     trees (in the case of java, they simply have no block tree, in some other
+     languages).  For these functions, there is nothing we can really do to
+     output correct debug info for inlined functions in all cases.  Rather
+     than die, we'll just produce deficient debug info now, in that we will
+     have variables without a proper abstract origin.  In the future, when all
+     functions are lowered, we should re-add a gcc_assert (origin_die)
+     here.  */
+
+  if (origin_die)
+    add_AT_die_ref (die, DW_AT_abstract_origin, origin_die);
+  return origin_die;
+}
+
+/* We do not currently support the pure_virtual attribute.  */
+
+static inline void
+add_pure_or_virtual_attribute (dw_die_ref die, tree func_decl)
+{
+  if (DECL_VINDEX (func_decl))
+    {
+      add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual);
+
+      if (host_integerp (DECL_VINDEX (func_decl), 0))
+	add_AT_loc (die, DW_AT_vtable_elem_location,
+		    new_loc_descr (DW_OP_constu,
+				   tree_low_cst (DECL_VINDEX (func_decl), 0),
+				   0));
+
+      /* GNU extension: Record what type this method came from originally.  */
+      if (debug_info_level > DINFO_LEVEL_TERSE)
+	add_AT_die_ref (die, DW_AT_containing_type,
+			lookup_type_die (DECL_CONTEXT (func_decl)));
+    }
+}
+
+/* Add source coordinate attributes for the given decl.  */
+
+static void
+add_src_coords_attributes (dw_die_ref die, tree decl)
+{
+  expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl));
+
+  add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file));
+  add_AT_unsigned (die, DW_AT_decl_line, s.line);
+}
+
+/* Add a DW_AT_name attribute and source coordinate attribute for the
+   given decl, but only if it actually has a name.  */
+
+static void
+add_name_and_src_coords_attributes (dw_die_ref die, tree decl)
+{
+  tree decl_name;
+
+  decl_name = DECL_NAME (decl);
+  if (decl_name != NULL && IDENTIFIER_POINTER (decl_name) != NULL)
+    {
+      add_name_attribute (die, dwarf2_name (decl, 0));
+      if (! DECL_ARTIFICIAL (decl))
+	add_src_coords_attributes (die, decl);
+
+      if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL)
+	  && TREE_PUBLIC (decl)
+	  && DECL_ASSEMBLER_NAME (decl) != DECL_NAME (decl)
+	  && !DECL_ABSTRACT (decl)
+	  && !(TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl))
+	  && !is_fortran ())
+	add_AT_string (die, DW_AT_MIPS_linkage_name,
+		       IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+    }
+
+#ifdef VMS_DEBUGGING_INFO
+  /* Get the function's name, as described by its RTL.  This may be different
+     from the DECL_NAME name used in the source file.  */
+  if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
+    {
+      add_AT_addr (die, DW_AT_VMS_rtnbeg_pd_address,
+		   XEXP (DECL_RTL (decl), 0));
+      VEC_safe_push (tree, gc, used_rtx_array, XEXP (DECL_RTL (decl), 0));
+    }
+#endif
+}
+
+/* Push a new declaration scope.  */
+
+static void
+push_decl_scope (tree scope)
+{
+  VEC_safe_push (tree, gc, decl_scope_table, scope);
+}
+
+/* Pop a declaration scope.  */
+
+static inline void
+pop_decl_scope (void)
+{
+  VEC_pop (tree, decl_scope_table);
+}
+
+/* Return the DIE for the scope that immediately contains this type.
+   Non-named types get global scope.  Named types nested in other
+   types get their containing scope if it's open, or global scope
+   otherwise.  All other types (i.e. function-local named types) get
+   the current active scope.  */
+
+static dw_die_ref
+scope_die_for (tree t, dw_die_ref context_die)
+{
+  dw_die_ref scope_die = NULL;
+  tree containing_scope;
+  int i;
+
+  /* Non-types always go in the current scope.  */
+  gcc_assert (TYPE_P (t));
+
+  containing_scope = TYPE_CONTEXT (t);
+
+  /* Use the containing namespace if it was passed in (for a declaration).  */
+  if (containing_scope && TREE_CODE (containing_scope) == NAMESPACE_DECL)
+    {
+      if (context_die == lookup_decl_die (containing_scope))
+	/* OK */;
+      else
+	containing_scope = NULL_TREE;
+    }
+
+  /* Ignore function type "scopes" from the C frontend.  They mean that
+     a tagged type is local to a parmlist of a function declarator, but
+     that isn't useful to DWARF.  */
+  if (containing_scope && TREE_CODE (containing_scope) == FUNCTION_TYPE)
+    containing_scope = NULL_TREE;
+
+  if (containing_scope == NULL_TREE)
+    scope_die = comp_unit_die;
+  else if (TYPE_P (containing_scope))
+    {
+      /* For types, we can just look up the appropriate DIE.  But
+	 first we check to see if we're in the middle of emitting it
+	 so we know where the new DIE should go.  */
+      for (i = VEC_length (tree, decl_scope_table) - 1; i >= 0; --i)
+	if (VEC_index (tree, decl_scope_table, i) == containing_scope)
+	  break;
+
+      if (i < 0)
+	{
+	  gcc_assert (debug_info_level <= DINFO_LEVEL_TERSE
+		      || TREE_ASM_WRITTEN (containing_scope));
+
+	  /* If none of the current dies are suitable, we get file scope.  */
+	  scope_die = comp_unit_die;
+	}
+      else
+	scope_die = lookup_type_die (containing_scope);
+    }
+  else
+    scope_die = context_die;
+
+  return scope_die;
+}
+
+/* Returns nonzero if CONTEXT_DIE is internal to a function.  */
+
+static inline int
+local_scope_p (dw_die_ref context_die)
+{
+  for (; context_die; context_die = context_die->die_parent)
+    if (context_die->die_tag == DW_TAG_inlined_subroutine
+	|| context_die->die_tag == DW_TAG_subprogram)
+      return 1;
+
+  return 0;
+}
+
+/* Returns nonzero if CONTEXT_DIE is a class or namespace, for deciding
+   whether or not to treat a DIE in this context as a declaration.  */
+
+static inline int
+class_or_namespace_scope_p (dw_die_ref context_die)
+{
+  return (context_die
+	  && (context_die->die_tag == DW_TAG_structure_type
+	      || context_die->die_tag == DW_TAG_class_type
+	      || context_die->die_tag == DW_TAG_interface_type
+	      || context_die->die_tag == DW_TAG_union_type
+	      || context_die->die_tag == DW_TAG_namespace));
+}
+
+/* Many forms of DIEs require a "type description" attribute.  This
+   routine locates the proper "type descriptor" die for the type given
+   by 'type', and adds a DW_AT_type attribute below the given die.  */
+
+static void
+add_type_attribute (dw_die_ref object_die, tree type, int decl_const,
+		    int decl_volatile, dw_die_ref context_die)
+{
+  enum tree_code code  = TREE_CODE (type);
+  dw_die_ref type_die  = NULL;
+
+  /* ??? If this type is an unnamed subrange type of an integral, floating-point
+     or fixed-point type, use the inner type.  This is because we have no
+     support for unnamed types in base_type_die.  This can happen if this is
+     an Ada subrange type.  Correct solution is emit a subrange type die.  */
+  if ((code == INTEGER_TYPE || code == REAL_TYPE || code == FIXED_POINT_TYPE)
+      && TREE_TYPE (type) != 0 && TYPE_NAME (type) == 0)
+    type = TREE_TYPE (type), code = TREE_CODE (type);
+
+  if (code == ERROR_MARK
+      /* Handle a special case.  For functions whose return type is void, we
+	 generate *no* type attribute.  (Note that no object may have type
+	 `void', so this only applies to function return types).  */
+      || code == VOID_TYPE)
+    return;
+
+  type_die = modified_type_die (type,
+				decl_const || TYPE_READONLY (type),
+				decl_volatile || TYPE_VOLATILE (type),
+				context_die);
+
+  if (type_die != NULL)
+    add_AT_die_ref (object_die, DW_AT_type, type_die);
+}
+
+/* Given an object die, add the calling convention attribute for the
+   function call type.  */
+static void
+add_calling_convention_attribute (dw_die_ref subr_die, tree decl)
+{
+  enum dwarf_calling_convention value = DW_CC_normal;
+
+  value = targetm.dwarf_calling_convention (TREE_TYPE (decl));
+
+  /* DWARF doesn't provide a way to identify a program's source-level
+     entry point.  DW_AT_calling_convention attributes are only meant
+     to describe functions' calling conventions.  However, lacking a
+     better way to signal the Fortran main program, we use this for the
+     time being, following existing custom.  */
+  if (is_fortran ()
+      && !strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), "MAIN__"))
+    value = DW_CC_program;
+
+  /* Only add the attribute if the backend requests it, and
+     is not DW_CC_normal.  */
+  if (value && (value != DW_CC_normal))
+    add_AT_unsigned (subr_die, DW_AT_calling_convention, value);
+}
+
+/* Given a tree pointer to a struct, class, union, or enum type node, return
+   a pointer to the (string) tag name for the given type, or zero if the type
+   was declared without a tag.  */
+
+static const char *
+type_tag (const_tree type)
+{
+  const char *name = 0;
+
+  if (TYPE_NAME (type) != 0)
+    {
+      tree t = 0;
+
+      /* Find the IDENTIFIER_NODE for the type name.  */
+      if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+	t = TYPE_NAME (type);
+
+      /* The g++ front end makes the TYPE_NAME of *each* tagged type point to
+	 a TYPE_DECL node, regardless of whether or not a `typedef' was
+	 involved.  */
+      else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+	       && ! DECL_IGNORED_P (TYPE_NAME (type)))
+	{
+	  /* We want to be extra verbose.  Don't call dwarf_name if
+	     DECL_NAME isn't set.  The default hook for decl_printable_name
+	     doesn't like that, and in this context it's correct to return
+	     0, instead of "<anonymous>" or the like.  */
+	  if (DECL_NAME (TYPE_NAME (type)))
+	    name = lang_hooks.dwarf_name (TYPE_NAME (type), 2);
+	}
+
+      /* Now get the name as a string, or invent one.  */
+      if (!name && t != 0)
+	name = IDENTIFIER_POINTER (t);
+    }
+
+  return (name == 0 || *name == '\0') ? 0 : name;
+}
+
+/* Return the type associated with a data member, make a special check
+   for bit field types.  */
+
+static inline tree
+member_declared_type (const_tree member)
+{
+  return (DECL_BIT_FIELD_TYPE (member)
+	  ? DECL_BIT_FIELD_TYPE (member) : TREE_TYPE (member));
+}
+
+/* Get the decl's label, as described by its RTL. This may be different
+   from the DECL_NAME name used in the source file.  */
+
+#if 0
+static const char *
+decl_start_label (tree decl)
+{
+  rtx x;
+  const char *fnname;
+
+  x = DECL_RTL (decl);
+  gcc_assert (MEM_P (x));
+
+  x = XEXP (x, 0);
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
+
+  fnname = XSTR (x, 0);
+  return fnname;
+}
+#endif
+
+/* These routines generate the internal representation of the DIE's for
+   the compilation unit.  Debugging information is collected by walking
+   the declaration trees passed in from dwarf2out_decl().  */
+
+static void
+gen_array_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref scope_die = scope_die_for (type, context_die);
+  dw_die_ref array_die;
+
+  /* GNU compilers represent multidimensional array types as sequences of one
+     dimensional array types whose element types are themselves array types.
+     We sometimes squish that down to a single array_type DIE with multiple
+     subscripts in the Dwarf debugging info.  The draft Dwarf specification
+     say that we are allowed to do this kind of compression in C, because
+     there is no difference between an array of arrays and a multidimensional
+     array.  We don't do this for Ada to remain as close as possible to the
+     actual representation, which is especially important against the language
+     flexibilty wrt arrays of variable size.  */
+
+  bool collapse_nested_arrays = !is_ada ();
+  tree element_type;
+
+  /* Emit DW_TAG_string_type for Fortran character types (with kind 1 only, as
+     DW_TAG_string_type doesn't have DW_AT_type attribute).  */
+  if (TYPE_STRING_FLAG (type)
+      && TREE_CODE (type) == ARRAY_TYPE
+      && is_fortran ()
+      && TYPE_MODE (TREE_TYPE (type)) == TYPE_MODE (char_type_node))
+    {
+      HOST_WIDE_INT size;
+
+      array_die = new_die (DW_TAG_string_type, scope_die, type);
+      add_name_attribute (array_die, type_tag (type));
+      equate_type_number_to_die (type, array_die);
+      size = int_size_in_bytes (type);
+      if (size >= 0)
+	add_AT_unsigned (array_die, DW_AT_byte_size, size);
+      else if (TYPE_DOMAIN (type) != NULL_TREE
+	       && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != NULL_TREE
+	       && DECL_P (TYPE_MAX_VALUE (TYPE_DOMAIN (type))))
+	{
+	  tree szdecl = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
+	  dw_loc_descr_ref loc = loc_descriptor_from_tree (szdecl);
+
+	  size = int_size_in_bytes (TREE_TYPE (szdecl));
+	  if (loc && size > 0)
+	    {
+	      add_AT_loc (array_die, DW_AT_string_length, loc);
+	      if (size != DWARF2_ADDR_SIZE)
+		add_AT_unsigned (array_die, DW_AT_byte_size, size);
+	    }
+	}
+      return;
+    }
+
+  /* ??? The SGI dwarf reader fails for array of array of enum types
+     (e.g. const enum machine_mode insn_operand_mode[2][10]) unless the inner
+     array type comes before the outer array type.  We thus call gen_type_die
+     before we new_die and must prevent nested array types collapsing for this
+     target.  */
+
+#ifdef MIPS_DEBUGGING_INFO
+  gen_type_die (TREE_TYPE (type), context_die);
+  collapse_nested_arrays = false;
+#endif
+
+  array_die = new_die (DW_TAG_array_type, scope_die, type);
+  add_name_attribute (array_die, type_tag (type));
+  equate_type_number_to_die (type, array_die);
+
+  if (TREE_CODE (type) == VECTOR_TYPE)
+    {
+      /* The frontend feeds us a representation for the vector as a struct
+	 containing an array.  Pull out the array type.  */
+      type = TREE_TYPE (TYPE_FIELDS (TYPE_DEBUG_REPRESENTATION_TYPE (type)));
+      add_AT_flag (array_die, DW_AT_GNU_vector, 1);
+    }
+
+  /* For Fortran multidimensional arrays use DW_ORD_col_major ordering.  */
+  if (is_fortran ()
+      && TREE_CODE (type) == ARRAY_TYPE
+      && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE
+      && !TYPE_STRING_FLAG (TREE_TYPE (type)))
+    add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_col_major);
+
+#if 0
+  /* We default the array ordering.  SDB will probably do
+     the right things even if DW_AT_ordering is not present.  It's not even
+     an issue until we start to get into multidimensional arrays anyway.  If
+     SDB is ever caught doing the Wrong Thing for multi-dimensional arrays,
+     then we'll have to put the DW_AT_ordering attribute back in.  (But if
+     and when we find out that we need to put these in, we will only do so
+     for multidimensional arrays.  */
+  add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_row_major);
+#endif
+
+#ifdef MIPS_DEBUGGING_INFO
+  /* The SGI compilers handle arrays of unknown bound by setting
+     AT_declaration and not emitting any subrange DIEs.  */
+  if (! TYPE_DOMAIN (type))
+    add_AT_flag (array_die, DW_AT_declaration, 1);
+  else
+#endif
+    add_subscript_info (array_die, type, collapse_nested_arrays);
+
+  /* Add representation of the type of the elements of this array type and
+     emit the corresponding DIE if we haven't done it already.  */  
+  element_type = TREE_TYPE (type);
+  if (collapse_nested_arrays)
+    while (TREE_CODE (element_type) == ARRAY_TYPE)
+      {
+	if (TYPE_STRING_FLAG (element_type) && is_fortran ())
+	  break;
+	element_type = TREE_TYPE (element_type);
+      }
+
+#ifndef MIPS_DEBUGGING_INFO
+  gen_type_die (element_type, context_die);
+#endif
+
+  add_type_attribute (array_die, element_type, 0, 0, context_die);
+
+  if (get_AT (array_die, DW_AT_name))
+    add_pubtype (type, array_die);
+}
+
+static dw_loc_descr_ref
+descr_info_loc (tree val, tree base_decl)
+{
+  HOST_WIDE_INT size;
+  dw_loc_descr_ref loc, loc2;
+  enum dwarf_location_atom op;
+
+  if (val == base_decl)
+    return new_loc_descr (DW_OP_push_object_address, 0, 0);
+
+  switch (TREE_CODE (val))
+    {
+    CASE_CONVERT:
+      return descr_info_loc (TREE_OPERAND (val, 0), base_decl);
+    case VAR_DECL:
+      return loc_descriptor_from_tree_1 (val, 0);
+    case INTEGER_CST:
+      if (host_integerp (val, 0))
+	return int_loc_descriptor (tree_low_cst (val, 0));
+      break;
+    case INDIRECT_REF:
+      size = int_size_in_bytes (TREE_TYPE (val));
+      if (size < 0)
+	break;
+      loc = descr_info_loc (TREE_OPERAND (val, 0), base_decl);
+      if (!loc)
+	break;
+      if (size == DWARF2_ADDR_SIZE)
+	add_loc_descr (&loc, new_loc_descr (DW_OP_deref, 0, 0));
+      else
+	add_loc_descr (&loc, new_loc_descr (DW_OP_deref_size, size, 0));
+      return loc;
+    case POINTER_PLUS_EXPR:
+    case PLUS_EXPR:
+      if (host_integerp (TREE_OPERAND (val, 1), 1)
+	  && (unsigned HOST_WIDE_INT) tree_low_cst (TREE_OPERAND (val, 1), 1)
+	     < 16384)
+	{
+	  loc = descr_info_loc (TREE_OPERAND (val, 0), base_decl);
+	  if (!loc)
+	    break;
+	  add_loc_descr (&loc,
+			 new_loc_descr (DW_OP_plus_uconst,
+					tree_low_cst (TREE_OPERAND (val, 1),
+						      1), 0));
+	}
+      else
+	{
+	  op = DW_OP_plus;
+	do_binop:
+	  loc = descr_info_loc (TREE_OPERAND (val, 0), base_decl);
+	  if (!loc)
+	    break;
+	  loc2 = descr_info_loc (TREE_OPERAND (val, 1), base_decl);
+	  if (!loc2)
+	    break;
+	  add_loc_descr (&loc, loc2);
+	  add_loc_descr (&loc2, new_loc_descr (op, 0, 0));
+	}
+      return loc;
+    case MINUS_EXPR:
+      op = DW_OP_minus;
+      goto do_binop;
+    case MULT_EXPR:
+      op = DW_OP_mul;
+      goto do_binop;
+    case EQ_EXPR:
+      op = DW_OP_eq;
+      goto do_binop;
+    case NE_EXPR:
+      op = DW_OP_ne;
+      goto do_binop;
+    default:
+      break;
+    }
+  return NULL;
+}
+
+static void
+add_descr_info_field (dw_die_ref die, enum dwarf_attribute attr,
+		      tree val, tree base_decl)
+{
+  dw_loc_descr_ref loc;
+
+  if (host_integerp (val, 0))
+    {
+      add_AT_unsigned (die, attr, tree_low_cst (val, 0));
+      return;
+    }
+
+  loc = descr_info_loc (val, base_decl);
+  if (!loc)
+    return;
+
+  add_AT_loc (die, attr, loc);
+}
+
+/* This routine generates DIE for array with hidden descriptor, details
+   are filled into *info by a langhook.  */
+
+static void
+gen_descr_array_type_die (tree type, struct array_descr_info *info,
+			  dw_die_ref context_die)
+{
+  dw_die_ref scope_die = scope_die_for (type, context_die);
+  dw_die_ref array_die;
+  int dim;
+
+  array_die = new_die (DW_TAG_array_type, scope_die, type);
+  add_name_attribute (array_die, type_tag (type));
+  equate_type_number_to_die (type, array_die);
+
+  /* For Fortran multidimensional arrays use DW_ORD_col_major ordering.  */
+  if (is_fortran ()
+      && info->ndimensions >= 2)
+    add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_col_major);
+
+  if (info->data_location)
+    add_descr_info_field (array_die, DW_AT_data_location, info->data_location,
+			  info->base_decl);
+  if (info->associated)
+    add_descr_info_field (array_die, DW_AT_associated, info->associated,
+			  info->base_decl);
+  if (info->allocated)
+    add_descr_info_field (array_die, DW_AT_allocated, info->allocated,
+			  info->base_decl);
+
+  for (dim = 0; dim < info->ndimensions; dim++)
+    {
+      dw_die_ref subrange_die
+	= new_die (DW_TAG_subrange_type, array_die, NULL);
+
+      if (info->dimen[dim].lower_bound)
+	{
+	  /* If it is the default value, omit it.  */
+	  if ((is_c_family () || is_java ())
+	      && integer_zerop (info->dimen[dim].lower_bound))
+	    ;
+	  else if (is_fortran ()
+		   && integer_onep (info->dimen[dim].lower_bound))
+	    ;
+	  else
+	    add_descr_info_field (subrange_die, DW_AT_lower_bound,
+				  info->dimen[dim].lower_bound,
+				  info->base_decl);
+	}
+      if (info->dimen[dim].upper_bound)
+	add_descr_info_field (subrange_die, DW_AT_upper_bound,
+			      info->dimen[dim].upper_bound,
+			      info->base_decl);
+      if (info->dimen[dim].stride)
+	add_descr_info_field (subrange_die, DW_AT_byte_stride,
+			      info->dimen[dim].stride,
+			      info->base_decl);
+    }
+
+  gen_type_die (info->element_type, context_die);
+  add_type_attribute (array_die, info->element_type, 0, 0, context_die);
+
+  if (get_AT (array_die, DW_AT_name))
+    add_pubtype (type, array_die);
+}
+
+#if 0
+static void
+gen_entry_point_die (tree decl, dw_die_ref context_die)
+{
+  tree origin = decl_ultimate_origin (decl);
+  dw_die_ref decl_die = new_die (DW_TAG_entry_point, context_die, decl);
+
+  if (origin != NULL)
+    add_abstract_origin_attribute (decl_die, origin);
+  else
+    {
+      add_name_and_src_coords_attributes (decl_die, decl);
+      add_type_attribute (decl_die, TREE_TYPE (TREE_TYPE (decl)),
+			  0, 0, context_die);
+    }
+
+  if (DECL_ABSTRACT (decl))
+    equate_decl_number_to_die (decl, decl_die);
+  else
+    add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl));
+}
+#endif
+
+/* Walk through the list of incomplete types again, trying once more to
+   emit full debugging info for them.  */
+
+static void
+retry_incomplete_types (void)
+{
+  int i;
+
+  for (i = VEC_length (tree, incomplete_types) - 1; i >= 0; i--)
+    gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die);
+}
+
+/* Determine what tag to use for a record type.  */
+
+static enum dwarf_tag
+record_type_tag (tree type)
+{
+  if (! lang_hooks.types.classify_record)
+    return DW_TAG_structure_type;
+
+  switch (lang_hooks.types.classify_record (type))
+    {
+    case RECORD_IS_STRUCT:
+      return DW_TAG_structure_type;
+
+    case RECORD_IS_CLASS:
+      return DW_TAG_class_type;
+
+    case RECORD_IS_INTERFACE:
+      return DW_TAG_interface_type;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Generate a DIE to represent an enumeration type.  Note that these DIEs
+   include all of the information about the enumeration values also. Each
+   enumerated type name/value is listed as a child of the enumerated type
+   DIE.  */
+
+static dw_die_ref
+gen_enumeration_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref type_die = lookup_type_die (type);
+
+  if (type_die == NULL)
+    {
+      type_die = new_die (DW_TAG_enumeration_type,
+			  scope_die_for (type, context_die), type);
+      equate_type_number_to_die (type, type_die);
+      add_name_attribute (type_die, type_tag (type));
+    }
+  else if (! TYPE_SIZE (type))
+    return type_die;
+  else
+    remove_AT (type_die, DW_AT_declaration);
+
+  /* Handle a GNU C/C++ extension, i.e. incomplete enum types.  If the
+     given enum type is incomplete, do not generate the DW_AT_byte_size
+     attribute or the DW_AT_element_list attribute.  */
+  if (TYPE_SIZE (type))
+    {
+      tree link;
+
+      TREE_ASM_WRITTEN (type) = 1;
+      add_byte_size_attribute (type_die, type);
+      if (TYPE_STUB_DECL (type) != NULL_TREE)
+	add_src_coords_attributes (type_die, TYPE_STUB_DECL (type));
+
+      /* If the first reference to this type was as the return type of an
+	 inline function, then it may not have a parent.  Fix this now.  */
+      if (type_die->die_parent == NULL)
+	add_child_die (scope_die_for (type, context_die), type_die);
+
+      for (link = TYPE_VALUES (type);
+	   link != NULL; link = TREE_CHAIN (link))
+	{
+	  dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die, link);
+	  tree value = TREE_VALUE (link);
+
+	  add_name_attribute (enum_die,
+			      IDENTIFIER_POINTER (TREE_PURPOSE (link)));
+
+	  if (TREE_CODE (value) == CONST_DECL)
+	    value = DECL_INITIAL (value);
+
+	  if (host_integerp (value, TYPE_UNSIGNED (TREE_TYPE (value))))
+	    /* DWARF2 does not provide a way of indicating whether or
+	       not enumeration constants are signed or unsigned.  GDB
+	       always assumes the values are signed, so we output all
+	       values as if they were signed.  That means that
+	       enumeration constants with very large unsigned values
+	       will appear to have negative values in the debugger.  */
+	    add_AT_int (enum_die, DW_AT_const_value,
+			tree_low_cst (value, tree_int_cst_sgn (value) > 0));
+	}
+    }
+  else
+    add_AT_flag (type_die, DW_AT_declaration, 1);
+
+  if (get_AT (type_die, DW_AT_name))
+    add_pubtype (type, type_die);
+
+  return type_die;
+}
+
+/* Generate a DIE to represent either a real live formal parameter decl or to
+   represent just the type of some formal parameter position in some function
+   type.
+
+   Note that this routine is a bit unusual because its argument may be a
+   ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which
+   represents an inlining of some PARM_DECL) or else some sort of a ..._TYPE
+   node.  If it's the former then this function is being called to output a
+   DIE to represent a formal parameter object (or some inlining thereof).  If
+   it's the latter, then this function is only being called to output a
+   DW_TAG_formal_parameter DIE to stand as a placeholder for some formal
+   argument type of some subprogram type.  */
+
+static dw_die_ref
+gen_formal_parameter_die (tree node, tree origin, dw_die_ref context_die)
+{
+  tree node_or_origin = node ? node : origin;
+  dw_die_ref parm_die
+    = new_die (DW_TAG_formal_parameter, context_die, node);
+
+  switch (TREE_CODE_CLASS (TREE_CODE (node_or_origin)))
+    {
+    case tcc_declaration:
+      if (!origin)
+        origin = decl_ultimate_origin (node);
+      if (origin != NULL)
+	add_abstract_origin_attribute (parm_die, origin);
+      else
+	{
+	  tree type = TREE_TYPE (node);
+	  add_name_and_src_coords_attributes (parm_die, node);
+	  if (DECL_BY_REFERENCE (node))
+	    add_type_attribute (parm_die, TREE_TYPE (type), 0, 0,
+				context_die);
+	  else
+	    add_type_attribute (parm_die, type,
+				TREE_READONLY (node),
+				TREE_THIS_VOLATILE (node),
+				context_die);
+	  if (DECL_ARTIFICIAL (node))
+	    add_AT_flag (parm_die, DW_AT_artificial, 1);
+	}
+
+      if (node)
+        equate_decl_number_to_die (node, parm_die);
+      if (! DECL_ABSTRACT (node_or_origin))
+	add_location_or_const_value_attribute (parm_die, node_or_origin,
+					       DW_AT_location);
+
+      break;
+
+    case tcc_type:
+      /* We were called with some kind of a ..._TYPE node.  */
+      add_type_attribute (parm_die, node_or_origin, 0, 0, context_die);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return parm_die;
+}
+
+/* Generate a special type of DIE used as a stand-in for a trailing ellipsis
+   at the end of an (ANSI prototyped) formal parameters list.  */
+
+static void
+gen_unspecified_parameters_die (tree decl_or_type, dw_die_ref context_die)
+{
+  new_die (DW_TAG_unspecified_parameters, context_die, decl_or_type);
+}
+
+/* Generate a list of nameless DW_TAG_formal_parameter DIEs (and perhaps a
+   DW_TAG_unspecified_parameters DIE) to represent the types of the formal
+   parameters as specified in some function type specification (except for
+   those which appear as part of a function *definition*).  */
+
+static void
+gen_formal_types_die (tree function_or_method_type, dw_die_ref context_die)
+{
+  tree link;
+  tree formal_type = NULL;
+  tree first_parm_type;
+  tree arg;
+
+  if (TREE_CODE (function_or_method_type) == FUNCTION_DECL)
+    {
+      arg = DECL_ARGUMENTS (function_or_method_type);
+      function_or_method_type = TREE_TYPE (function_or_method_type);
+    }
+  else
+    arg = NULL_TREE;
+
+  first_parm_type = TYPE_ARG_TYPES (function_or_method_type);
+
+  /* Make our first pass over the list of formal parameter types and output a
+     DW_TAG_formal_parameter DIE for each one.  */
+  for (link = first_parm_type; link; )
+    {
+      dw_die_ref parm_die;
+
+      formal_type = TREE_VALUE (link);
+      if (formal_type == void_type_node)
+	break;
+
+      /* Output a (nameless) DIE to represent the formal parameter itself.  */
+      parm_die = gen_formal_parameter_die (formal_type, NULL, context_die);
+      if ((TREE_CODE (function_or_method_type) == METHOD_TYPE
+	   && link == first_parm_type)
+	  || (arg && DECL_ARTIFICIAL (arg)))
+	add_AT_flag (parm_die, DW_AT_artificial, 1);
+
+      link = TREE_CHAIN (link);
+      if (arg)
+	arg = TREE_CHAIN (arg);
+    }
+
+  /* If this function type has an ellipsis, add a
+     DW_TAG_unspecified_parameters DIE to the end of the parameter list.  */
+  if (formal_type != void_type_node)
+    gen_unspecified_parameters_die (function_or_method_type, context_die);
+
+  /* Make our second (and final) pass over the list of formal parameter types
+     and output DIEs to represent those types (as necessary).  */
+  for (link = TYPE_ARG_TYPES (function_or_method_type);
+       link && TREE_VALUE (link);
+       link = TREE_CHAIN (link))
+    gen_type_die (TREE_VALUE (link), context_die);
+}
+
+/* We want to generate the DIE for TYPE so that we can generate the
+   die for MEMBER, which has been defined; we will need to refer back
+   to the member declaration nested within TYPE.  If we're trying to
+   generate minimal debug info for TYPE, processing TYPE won't do the
+   trick; we need to attach the member declaration by hand.  */
+
+static void
+gen_type_die_for_member (tree type, tree member, dw_die_ref context_die)
+{
+  gen_type_die (type, context_die);
+
+  /* If we're trying to avoid duplicate debug info, we may not have
+     emitted the member decl for this function.  Emit it now.  */
+  if (TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))
+      && ! lookup_decl_die (member))
+    {
+      dw_die_ref type_die;
+      gcc_assert (!decl_ultimate_origin (member));
+
+      push_decl_scope (type);
+      type_die = lookup_type_die (type);
+      if (TREE_CODE (member) == FUNCTION_DECL)
+	gen_subprogram_die (member, type_die);
+      else if (TREE_CODE (member) == FIELD_DECL)
+	{
+	  /* Ignore the nameless fields that are used to skip bits but handle
+	     C++ anonymous unions and structs.  */
+	  if (DECL_NAME (member) != NULL_TREE
+	      || TREE_CODE (TREE_TYPE (member)) == UNION_TYPE
+	      || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE)
+	    {
+	      gen_type_die (member_declared_type (member), type_die);
+	      gen_field_die (member, type_die);
+	    }
+	}
+      else
+	gen_variable_die (member, NULL_TREE, type_die);
+
+      pop_decl_scope ();
+    }
+}
+
+/* Generate the DWARF2 info for the "abstract" instance of a function which we
+   may later generate inlined and/or out-of-line instances of.  */
+
+static void
+dwarf2out_abstract_function (tree decl)
+{
+  dw_die_ref old_die;
+  tree save_fn;
+  tree context;
+  int was_abstract = DECL_ABSTRACT (decl);
+
+  /* Make sure we have the actual abstract inline, not a clone.  */
+  decl = DECL_ORIGIN (decl);
+
+  old_die = lookup_decl_die (decl);
+  if (old_die && get_AT (old_die, DW_AT_inline))
+    /* We've already generated the abstract instance.  */
+    return;
+
+  /* Be sure we've emitted the in-class declaration DIE (if any) first, so
+     we don't get confused by DECL_ABSTRACT.  */
+  if (debug_info_level > DINFO_LEVEL_TERSE)
+    {
+      context = decl_class_context (decl);
+      if (context)
+	gen_type_die_for_member
+	  (context, decl, decl_function_context (decl) ? NULL : comp_unit_die);
+    }
+
+  /* Pretend we've just finished compiling this function.  */
+  save_fn = current_function_decl;
+  current_function_decl = decl;
+  push_cfun (DECL_STRUCT_FUNCTION (decl));
+
+  set_decl_abstract_flags (decl, 1);
+  dwarf2out_decl (decl);
+  if (! was_abstract)
+    set_decl_abstract_flags (decl, 0);
+
+  current_function_decl = save_fn;
+  pop_cfun ();
+}
+
+/* Helper function of premark_used_types() which gets called through
+   htab_traverse_resize().
+
+   Marks the DIE of a given type in *SLOT as perennial, so it never gets
+   marked as unused by prune_unused_types.  */
+static int
+premark_used_types_helper (void **slot, void *data ATTRIBUTE_UNUSED)
+{
+  tree type;
+  dw_die_ref die;
+
+  type = (tree) *slot;
+  die = lookup_type_die (type);
+  if (die != NULL)
+    die->die_perennial_p = 1;
+  return 1;
+}
+
+/* Mark all members of used_types_hash as perennial.  */
+static void
+premark_used_types (void)
+{
+  if (cfun && cfun->used_types_hash)
+    htab_traverse (cfun->used_types_hash, premark_used_types_helper, NULL);
+}
+
+/* Generate a DIE to represent a declared function (either file-scope or
+   block-local).  */
+
+static void
+gen_subprogram_die (tree decl, dw_die_ref context_die)
+{
+  char label_id[MAX_ARTIFICIAL_LABEL_BYTES];
+  tree origin = decl_ultimate_origin (decl);
+  dw_die_ref subr_die;
+  tree fn_arg_types;
+  tree outer_scope;
+  dw_die_ref old_die = lookup_decl_die (decl);
+  int declaration = (current_function_decl != decl
+		     || class_or_namespace_scope_p (context_die));
+
+  premark_used_types ();
+
+  /* It is possible to have both DECL_ABSTRACT and DECLARATION be true if we
+     started to generate the abstract instance of an inline, decided to output
+     its containing class, and proceeded to emit the declaration of the inline
+     from the member list for the class.  If so, DECLARATION takes priority;
+     we'll get back to the abstract instance when done with the class.  */
+
+  /* The class-scope declaration DIE must be the primary DIE.  */
+  if (origin && declaration && class_or_namespace_scope_p (context_die))
+    {
+      origin = NULL;
+      gcc_assert (!old_die);
+    }
+
+  /* Now that the C++ front end lazily declares artificial member fns, we
+     might need to retrofit the declaration into its class.  */
+  if (!declaration && !origin && !old_die
+      && DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl))
+      && !class_or_namespace_scope_p (context_die)
+      && debug_info_level > DINFO_LEVEL_TERSE)
+    old_die = force_decl_die (decl);
+
+  if (origin != NULL)
+    {
+      gcc_assert (!declaration || local_scope_p (context_die));
+
+      /* Fixup die_parent for the abstract instance of a nested
+	 inline function.  */
+      if (old_die && old_die->die_parent == NULL)
+	add_child_die (context_die, old_die);
+
+      subr_die = new_die (DW_TAG_subprogram, context_die, decl);
+      add_abstract_origin_attribute (subr_die, origin);
+    }
+  else if (old_die)
+    {
+      expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl));
+      struct dwarf_file_data * file_index = lookup_filename (s.file);
+
+      if (!get_AT_flag (old_die, DW_AT_declaration)
+	  /* We can have a normal definition following an inline one in the
+	     case of redefinition of GNU C extern inlines.
+	     It seems reasonable to use AT_specification in this case.  */
+	  && !get_AT (old_die, DW_AT_inline))
+	{
+	  /* Detect and ignore this case, where we are trying to output
+	     something we have already output.  */
+	  return;
+	}
+
+      /* If the definition comes from the same place as the declaration,
+	 maybe use the old DIE.  We always want the DIE for this function
+	 that has the *_pc attributes to be under comp_unit_die so the
+	 debugger can find it.  We also need to do this for abstract
+	 instances of inlines, since the spec requires the out-of-line copy
+	 to have the same parent.  For local class methods, this doesn't
+	 apply; we just use the old DIE.  */
+      if ((old_die->die_parent == comp_unit_die || context_die == NULL)
+	  && (DECL_ARTIFICIAL (decl)
+	      || (get_AT_file (old_die, DW_AT_decl_file) == file_index
+		  && (get_AT_unsigned (old_die, DW_AT_decl_line)
+		      == (unsigned) s.line))))
+	{
+	  subr_die = old_die;
+
+	  /* Clear out the declaration attribute and the formal parameters.
+	     Do not remove all children, because it is possible that this
+	     declaration die was forced using force_decl_die(). In such
+	     cases die that forced declaration die (e.g. TAG_imported_module)
+	     is one of the children that we do not want to remove.  */
+	  remove_AT (subr_die, DW_AT_declaration);
+	  remove_child_TAG (subr_die, DW_TAG_formal_parameter);
+	}
+      else
+	{
+	  subr_die = new_die (DW_TAG_subprogram, context_die, decl);
+	  add_AT_specification (subr_die, old_die);
+	  if (get_AT_file (old_die, DW_AT_decl_file) != file_index)
+	    add_AT_file (subr_die, DW_AT_decl_file, file_index);
+	  if (get_AT_unsigned (old_die, DW_AT_decl_line) != (unsigned) s.line)
+	    add_AT_unsigned (subr_die, DW_AT_decl_line, s.line);
+	}
+    }
+  else
+    {
+      subr_die = new_die (DW_TAG_subprogram, context_die, decl);
+
+      if (TREE_PUBLIC (decl))
+	add_AT_flag (subr_die, DW_AT_external, 1);
+
+      add_name_and_src_coords_attributes (subr_die, decl);
+      if (debug_info_level > DINFO_LEVEL_TERSE)
+	{
+	  add_prototyped_attribute (subr_die, TREE_TYPE (decl));
+	  add_type_attribute (subr_die, TREE_TYPE (TREE_TYPE (decl)),
+			      0, 0, context_die);
+	}
+
+      add_pure_or_virtual_attribute (subr_die, decl);
+      if (DECL_ARTIFICIAL (decl))
+	add_AT_flag (subr_die, DW_AT_artificial, 1);
+
+      if (TREE_PROTECTED (decl))
+	add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_protected);
+      else if (TREE_PRIVATE (decl))
+	add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_private);
+    }
+
+  if (declaration)
+    {
+      if (!old_die || !get_AT (old_die, DW_AT_inline))
+	{
+	  add_AT_flag (subr_die, DW_AT_declaration, 1);
+
+	  /* The first time we see a member function, it is in the context of
+	     the class to which it belongs.  We make sure of this by emitting
+	     the class first.  The next time is the definition, which is
+	     handled above.  The two may come from the same source text.
+
+	     Note that force_decl_die() forces function declaration die. It is
+	     later reused to represent definition.  */
+	  equate_decl_number_to_die (decl, subr_die);
+	}
+    }
+  else if (DECL_ABSTRACT (decl))
+    {
+      if (DECL_DECLARED_INLINE_P (decl))
+	{
+	  if (cgraph_function_possibly_inlined_p (decl))
+	    add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_inlined);
+	  else
+	    add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_not_inlined);
+	}
+      else
+	{
+	  if (cgraph_function_possibly_inlined_p (decl))
+	    add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_inlined);
+	  else
+	    add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_not_inlined);
+	}
+
+      if (DECL_DECLARED_INLINE_P (decl)
+	  && lookup_attribute ("artificial", DECL_ATTRIBUTES (decl)))
+	add_AT_flag (subr_die, DW_AT_artificial, 1);
+
+      equate_decl_number_to_die (decl, subr_die);
+    }
+  else if (!DECL_EXTERNAL (decl))
+    {
+      HOST_WIDE_INT cfa_fb_offset;
+
+      if (!old_die || !get_AT (old_die, DW_AT_inline))
+	equate_decl_number_to_die (decl, subr_die);
+
+      if (!flag_reorder_blocks_and_partition)
+	{
+	  ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL,
+				       current_function_funcdef_no);
+	  add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id);
+	  ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
+				       current_function_funcdef_no);
+	  add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id);
+
+	  add_pubname (decl, subr_die);
+	  add_arange (decl, subr_die);
+	}
+      else
+	{  /* Do nothing for now; maybe need to duplicate die, one for
+	      hot section and one for cold section, then use the hot/cold
+	      section begin/end labels to generate the aranges...  */
+	  /*
+	    add_AT_lbl_id (subr_die, DW_AT_low_pc, hot_section_label);
+	    add_AT_lbl_id (subr_die, DW_AT_high_pc, hot_section_end_label);
+	    add_AT_lbl_id (subr_die, DW_AT_lo_user, unlikely_section_label);
+	    add_AT_lbl_id (subr_die, DW_AT_hi_user, cold_section_end_label);
+
+	    add_pubname (decl, subr_die);
+	    add_arange (decl, subr_die);
+	    add_arange (decl, subr_die);
+	   */
+	}
+
+#ifdef MIPS_DEBUGGING_INFO
+      /* Add a reference to the FDE for this routine.  */
+      add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, current_funcdef_fde);
+#endif
+
+      cfa_fb_offset = CFA_FRAME_BASE_OFFSET (decl);
+
+      /* We define the "frame base" as the function's CFA.  This is more
+	 convenient for several reasons: (1) It's stable across the prologue
+	 and epilogue, which makes it better than just a frame pointer,
+	 (2) With dwarf3, there exists a one-byte encoding that allows us
+	 to reference the .debug_frame data by proxy, but failing that,
+	 (3) We can at least reuse the code inspection and interpretation
+	 code that determines the CFA position at various points in the
+	 function.  */
+      /* ??? Use some command-line or configury switch to enable the use
+	 of dwarf3 DW_OP_call_frame_cfa.  At present there are no dwarf
+	 consumers that understand it; fall back to "pure" dwarf2 and
+	 convert the CFA data into a location list.  */
+      {
+	dw_loc_list_ref list = convert_cfa_to_fb_loc_list (cfa_fb_offset);
+	if (list->dw_loc_next)
+	  add_AT_loc_list (subr_die, DW_AT_frame_base, list);
+	else
+	  add_AT_loc (subr_die, DW_AT_frame_base, list->expr);
+      }
+
+      /* Compute a displacement from the "steady-state frame pointer" to
+	 the CFA.  The former is what all stack slots and argument slots
+	 will reference in the rtl; the later is what we've told the
+	 debugger about.  We'll need to adjust all frame_base references
+	 by this displacement.  */
+      compute_frame_pointer_to_fb_displacement (cfa_fb_offset);
+
+      if (cfun->static_chain_decl)
+	add_AT_location_description (subr_die, DW_AT_static_link,
+		 loc_descriptor_from_tree (cfun->static_chain_decl));
+    }
+
+  /* Now output descriptions of the arguments for this function. This gets
+     (unnecessarily?) complex because of the fact that the DECL_ARGUMENT list
+     for a FUNCTION_DECL doesn't indicate cases where there was a trailing
+     `...' at the end of the formal parameter list.  In order to find out if
+     there was a trailing ellipsis or not, we must instead look at the type
+     associated with the FUNCTION_DECL.  This will be a node of type
+     FUNCTION_TYPE. If the chain of type nodes hanging off of this
+     FUNCTION_TYPE node ends with a void_type_node then there should *not* be
+     an ellipsis at the end.  */
+
+  /* In the case where we are describing a mere function declaration, all we
+     need to do here (and all we *can* do here) is to describe the *types* of
+     its formal parameters.  */
+  if (debug_info_level <= DINFO_LEVEL_TERSE)
+    ;
+  else if (declaration)
+    gen_formal_types_die (decl, subr_die);
+  else
+    {
+      /* Generate DIEs to represent all known formal parameters.  */
+      tree arg_decls = DECL_ARGUMENTS (decl);
+      tree parm;
+
+      /* When generating DIEs, generate the unspecified_parameters DIE
+	 instead if we come across the arg "__builtin_va_alist" */
+      for (parm = arg_decls; parm; parm = TREE_CHAIN (parm))
+	if (TREE_CODE (parm) == PARM_DECL)
+	  {
+	    if (DECL_NAME (parm)
+		&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)),
+			    "__builtin_va_alist"))
+	      gen_unspecified_parameters_die (parm, subr_die);
+	    else
+	      gen_decl_die (parm, NULL, subr_die);
+	  }
+
+      /* Decide whether we need an unspecified_parameters DIE at the end.
+	 There are 2 more cases to do this for: 1) the ansi ... declaration -
+	 this is detectable when the end of the arg list is not a
+	 void_type_node 2) an unprototyped function declaration (not a
+	 definition).  This just means that we have no info about the
+	 parameters at all.  */
+      fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      if (fn_arg_types != NULL)
+	{
+	  /* This is the prototyped case, check for....  */
+	  if (TREE_VALUE (tree_last (fn_arg_types)) != void_type_node)
+	    gen_unspecified_parameters_die (decl, subr_die);
+	}
+      else if (DECL_INITIAL (decl) == NULL_TREE)
+	gen_unspecified_parameters_die (decl, subr_die);
+    }
+
+  /* Output Dwarf info for all of the stuff within the body of the function
+     (if it has one - it may be just a declaration).  */
+  outer_scope = DECL_INITIAL (decl);
+
+  /* OUTER_SCOPE is a pointer to the outermost BLOCK node created to represent
+     a function.  This BLOCK actually represents the outermost binding contour
+     for the function, i.e. the contour in which the function's formal
+     parameters and labels get declared. Curiously, it appears that the front
+     end doesn't actually put the PARM_DECL nodes for the current function onto
+     the BLOCK_VARS list for this outer scope, but are strung off of the
+     DECL_ARGUMENTS list for the function instead.
+
+     The BLOCK_VARS list for the `outer_scope' does provide us with a list of
+     the LABEL_DECL nodes for the function however, and we output DWARF info
+     for those in decls_for_scope.  Just within the `outer_scope' there will be
+     a BLOCK node representing the function's outermost pair of curly braces,
+     and any blocks used for the base and member initializers of a C++
+     constructor function.  */
+  if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK)
+    {
+      /* Emit a DW_TAG_variable DIE for a named return value.  */
+      if (DECL_NAME (DECL_RESULT (decl)))
+	gen_decl_die (DECL_RESULT (decl), NULL, subr_die);
+
+      current_function_has_inlines = 0;
+      decls_for_scope (outer_scope, subr_die, 0);
+
+#if 0 && defined (MIPS_DEBUGGING_INFO)
+      if (current_function_has_inlines)
+	{
+	  add_AT_flag (subr_die, DW_AT_MIPS_has_inlines, 1);
+	  if (! comp_unit_has_inlines)
+	    {
+	      add_AT_flag (comp_unit_die, DW_AT_MIPS_has_inlines, 1);
+	      comp_unit_has_inlines = 1;
+	    }
+	}
+#endif
+    }
+  /* Add the calling convention attribute if requested.  */
+  add_calling_convention_attribute (subr_die, decl);
+
+}
+
+/* Returns a hash value for X (which really is a die_struct).  */
+
+static hashval_t
+common_block_die_table_hash (const void *x)
+{
+  const_dw_die_ref d = (const_dw_die_ref) x;
+  return (hashval_t) d->decl_id ^ htab_hash_pointer (d->die_parent);
+}
+
+/* Return nonzero if decl_id and die_parent of die_struct X is the same
+   as decl_id and die_parent of die_struct Y.  */
+
+static int
+common_block_die_table_eq (const void *x, const void *y)
+{
+  const_dw_die_ref d = (const_dw_die_ref) x;
+  const_dw_die_ref e = (const_dw_die_ref) y;
+  return d->decl_id == e->decl_id && d->die_parent == e->die_parent;
+}
+
+/* Generate a DIE to represent a declared data object.
+   Either DECL or ORIGIN must be non-null.  */
+
+static void
+gen_variable_die (tree decl, tree origin, dw_die_ref context_die)
+{
+  HOST_WIDE_INT off;
+  tree com_decl;
+  tree decl_or_origin = decl ? decl : origin;
+  dw_die_ref var_die;
+  dw_die_ref old_die = decl ? lookup_decl_die (decl) : NULL;
+  dw_die_ref origin_die;
+  int declaration = (DECL_EXTERNAL (decl_or_origin)
+		     /* If DECL is COMDAT and has not actually been
+			emitted, we cannot take its address; there
+			might end up being no definition anywhere in
+			the program.  For example, consider the C++
+			test case:
+
+			  template <class T>
+			  struct S { static const int i = 7; };
+
+			  template <class T>
+			  const int S<T>::i;
+
+			  int f() { return S<int>::i; }
+
+			Here, S<int>::i is not DECL_EXTERNAL, but no
+			definition is required, so the compiler will
+			not emit a definition.  */
+		     || (TREE_CODE (decl_or_origin) == VAR_DECL
+			 && DECL_COMDAT (decl_or_origin)
+			 && !TREE_ASM_WRITTEN (decl_or_origin))
+		     || class_or_namespace_scope_p (context_die));
+
+  if (!origin)
+    origin = decl_ultimate_origin (decl);
+
+  com_decl = fortran_common (decl_or_origin, &off);
+
+  /* Symbol in common gets emitted as a child of the common block, in the form
+     of a data member.  */
+  if (com_decl)
+    {
+      tree field;
+      dw_die_ref com_die;
+      dw_loc_descr_ref loc;
+      die_node com_die_arg;
+
+      var_die = lookup_decl_die (decl_or_origin);
+      if (var_die)
+	{
+	  if (get_AT (var_die, DW_AT_location) == NULL)
+	    {
+	      loc = loc_descriptor_from_tree (com_decl);
+	      if (loc)
+		{
+		  if (off)
+		    {
+		      /* Optimize the common case.  */
+		      if (loc->dw_loc_opc == DW_OP_addr
+			  && loc->dw_loc_next == NULL
+			  && GET_CODE (loc->dw_loc_oprnd1.v.val_addr)
+			     == SYMBOL_REF)
+			loc->dw_loc_oprnd1.v.val_addr
+			  = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+			else
+			  add_loc_descr (&loc,
+					 new_loc_descr (DW_OP_plus_uconst,
+							off, 0));
+		    }
+		  add_AT_loc (var_die, DW_AT_location, loc);
+		  remove_AT (var_die, DW_AT_declaration);
+		}
+	    }
+	  return;
+	}
+
+      if (common_block_die_table == NULL)
+	common_block_die_table
+	  = htab_create_ggc (10, common_block_die_table_hash,
+			     common_block_die_table_eq, NULL);
+
+      field = TREE_OPERAND (DECL_VALUE_EXPR (decl), 0);
+      com_die_arg.decl_id = DECL_UID (com_decl);
+      com_die_arg.die_parent = context_die;
+      com_die = (dw_die_ref) htab_find (common_block_die_table, &com_die_arg);
+      loc = loc_descriptor_from_tree (com_decl);
+      if (com_die == NULL)
+	{
+	  const char *cnam
+	    = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (com_decl));
+	  void **slot;
+
+	  com_die = new_die (DW_TAG_common_block, context_die, decl);
+	  add_name_and_src_coords_attributes (com_die, com_decl);
+	  if (loc)
+	    {
+	      add_AT_loc (com_die, DW_AT_location, loc);
+	      /* Avoid sharing the same loc descriptor between
+		 DW_TAG_common_block and DW_TAG_variable.  */
+	      loc = loc_descriptor_from_tree (com_decl);
+	    }
+          else if (DECL_EXTERNAL (decl))
+	    add_AT_flag (com_die, DW_AT_declaration, 1);
+	  add_pubname_string (cnam, com_die); /* ??? needed? */
+	  com_die->decl_id = DECL_UID (com_decl);
+	  slot = htab_find_slot (common_block_die_table, com_die, INSERT);
+	  *slot = (void *) com_die;
+	}
+      else if (get_AT (com_die, DW_AT_location) == NULL && loc)
+	{
+	  add_AT_loc (com_die, DW_AT_location, loc);
+	  loc = loc_descriptor_from_tree (com_decl);
+	  remove_AT (com_die, DW_AT_declaration);
+	}
+      var_die = new_die (DW_TAG_variable, com_die, decl);
+      add_name_and_src_coords_attributes (var_die, decl);
+      add_type_attribute (var_die, TREE_TYPE (decl), TREE_READONLY (decl),
+			  TREE_THIS_VOLATILE (decl), context_die);
+      add_AT_flag (var_die, DW_AT_external, 1);
+      if (loc)
+	{
+	  if (off)
+	    {
+	      /* Optimize the common case.  */
+	      if (loc->dw_loc_opc == DW_OP_addr
+		  && loc->dw_loc_next == NULL
+		  && GET_CODE (loc->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF)
+		loc->dw_loc_oprnd1.v.val_addr
+		  = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+	      else
+		add_loc_descr (&loc, new_loc_descr (DW_OP_plus_uconst,
+						    off, 0));
+	    }
+	  add_AT_loc (var_die, DW_AT_location, loc);
+	}
+      else if (DECL_EXTERNAL (decl))
+	add_AT_flag (var_die, DW_AT_declaration, 1);
+      equate_decl_number_to_die (decl, var_die);
+      return;
+    }
+
+  /* If the compiler emitted a definition for the DECL declaration
+     and if we already emitted a DIE for it, don't emit a second
+     DIE for it again.  */
+  if (old_die
+      && declaration
+      && old_die->die_parent == context_die)
+    return;
+
+  var_die = new_die (DW_TAG_variable, context_die, decl);
+
+  origin_die = NULL;
+  if (origin != NULL)
+    origin_die = add_abstract_origin_attribute (var_die, origin);
+
+  /* Loop unrolling can create multiple blocks that refer to the same
+     static variable, so we must test for the DW_AT_declaration flag.
+
+     ??? Loop unrolling/reorder_blocks should perhaps be rewritten to
+     copy decls and set the DECL_ABSTRACT flag on them instead of
+     sharing them.
+
+     ??? Duplicated blocks have been rewritten to use .debug_ranges.
+
+     ??? The declare_in_namespace support causes us to get two DIEs for one
+     variable, both of which are declarations.  We want to avoid considering
+     one to be a specification, so we must test that this DIE is not a
+     declaration.  */
+  else if (old_die && TREE_STATIC (decl) && ! declaration
+	   && get_AT_flag (old_die, DW_AT_declaration) == 1)
+    {
+      /* This is a definition of a C++ class level static.  */
+      add_AT_specification (var_die, old_die);
+      if (DECL_NAME (decl))
+	{
+	  expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl));
+	  struct dwarf_file_data * file_index = lookup_filename (s.file);
+
+	  if (get_AT_file (old_die, DW_AT_decl_file) != file_index)
+	    add_AT_file (var_die, DW_AT_decl_file, file_index);
+
+	  if (get_AT_unsigned (old_die, DW_AT_decl_line) != (unsigned) s.line)
+	    add_AT_unsigned (var_die, DW_AT_decl_line, s.line);
+	}
+    }
+  else
+    {
+      tree type = TREE_TYPE (decl);
+
+      add_name_and_src_coords_attributes (var_die, decl);
+      if ((TREE_CODE (decl) == PARM_DECL
+	   || TREE_CODE (decl) == RESULT_DECL)
+	  && DECL_BY_REFERENCE (decl))
+	add_type_attribute (var_die, TREE_TYPE (type), 0, 0, context_die);
+      else
+	add_type_attribute (var_die, type, TREE_READONLY (decl),
+			    TREE_THIS_VOLATILE (decl), context_die);
+
+      if (TREE_PUBLIC (decl))
+	add_AT_flag (var_die, DW_AT_external, 1);
+
+      if (DECL_ARTIFICIAL (decl))
+	add_AT_flag (var_die, DW_AT_artificial, 1);
+
+      if (TREE_PROTECTED (decl))
+	add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_protected);
+      else if (TREE_PRIVATE (decl))
+	add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_private);
+    }
+
+  if (declaration)
+    add_AT_flag (var_die, DW_AT_declaration, 1);
+
+  if (decl && (DECL_ABSTRACT (decl) || declaration))
+    equate_decl_number_to_die (decl, var_die);
+
+  if (! declaration
+      && (! DECL_ABSTRACT (decl_or_origin)
+	  /* Local static vars are shared between all clones/inlines,
+	     so emit DW_AT_location on the abstract DIE if DECL_RTL is
+	     already set.  */
+	  || (TREE_CODE (decl_or_origin) == VAR_DECL
+	      && TREE_STATIC (decl_or_origin)
+	      && DECL_RTL_SET_P (decl_or_origin)))
+      /* When abstract origin already has DW_AT_location attribute, no need
+	 to add it again.  */
+      && (origin_die == NULL || get_AT (origin_die, DW_AT_location) == NULL))
+    {
+      if (TREE_CODE (decl_or_origin) == VAR_DECL && TREE_STATIC (decl_or_origin)
+          && !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl_or_origin)))
+	defer_location (decl_or_origin, var_die);
+      else
+        add_location_or_const_value_attribute (var_die,
+					       decl_or_origin,
+					       DW_AT_location);
+      add_pubname (decl_or_origin, var_die);
+    }
+  else
+    tree_add_const_value_attribute (var_die, decl_or_origin);
+}
+
+/* Generate a DIE to represent a named constant.  */
+
+static void
+gen_const_die (tree decl, dw_die_ref context_die)
+{
+  dw_die_ref const_die;
+  tree type = TREE_TYPE (decl);
+
+  const_die = new_die (DW_TAG_constant, context_die, decl);
+  add_name_and_src_coords_attributes (const_die, decl);
+  add_type_attribute (const_die, type, 1, 0, context_die);
+  if (TREE_PUBLIC (decl))
+    add_AT_flag (const_die, DW_AT_external, 1);
+  if (DECL_ARTIFICIAL (decl))
+    add_AT_flag (const_die, DW_AT_artificial, 1);
+  tree_add_const_value_attribute (const_die, decl);
+}
+
+/* Generate a DIE to represent a label identifier.  */
+
+static void
+gen_label_die (tree decl, dw_die_ref context_die)
+{
+  tree origin = decl_ultimate_origin (decl);
+  dw_die_ref lbl_die = new_die (DW_TAG_label, context_die, decl);
+  rtx insn;
+  char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+  if (origin != NULL)
+    add_abstract_origin_attribute (lbl_die, origin);
+  else
+    add_name_and_src_coords_attributes (lbl_die, decl);
+
+  if (DECL_ABSTRACT (decl))
+    equate_decl_number_to_die (decl, lbl_die);
+  else
+    {
+      insn = DECL_RTL_IF_SET (decl);
+
+      /* Deleted labels are programmer specified labels which have been
+	 eliminated because of various optimizations.  We still emit them
+	 here so that it is possible to put breakpoints on them.  */
+      if (insn
+	  && (LABEL_P (insn)
+	      || ((NOTE_P (insn)
+	           && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))))
+	{
+	  /* When optimization is enabled (via -O) some parts of the compiler
+	     (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which
+	     represent source-level labels which were explicitly declared by
+	     the user.  This really shouldn't be happening though, so catch
+	     it if it ever does happen.  */
+	  gcc_assert (!INSN_DELETED_P (insn));
+
+	  ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (insn));
+	  add_AT_lbl_id (lbl_die, DW_AT_low_pc, label);
+	}
+    }
+}
+
+/* A helper function for gen_inlined_subroutine_die.  Add source coordinate
+   attributes to the DIE for a block STMT, to describe where the inlined
+   function was called from.  This is similar to add_src_coords_attributes.  */
+
+static inline void
+add_call_src_coords_attributes (tree stmt, dw_die_ref die)
+{
+  expanded_location s = expand_location (BLOCK_SOURCE_LOCATION (stmt));
+
+  add_AT_file (die, DW_AT_call_file, lookup_filename (s.file));
+  add_AT_unsigned (die, DW_AT_call_line, s.line);
+}
+
+
+/* A helper function for gen_lexical_block_die and gen_inlined_subroutine_die.
+   Add low_pc and high_pc attributes to the DIE for a block STMT.  */
+
+static inline void
+add_high_low_attributes (tree stmt, dw_die_ref die)
+{
+  char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+  if (BLOCK_FRAGMENT_CHAIN (stmt))
+    {
+      tree chain;
+
+      if (inlined_function_outer_scope_p (stmt))
+	{
+	  ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL,
+				       BLOCK_NUMBER (stmt));
+	  add_AT_lbl_id (die, DW_AT_entry_pc, label);
+	}
+
+      add_AT_range_list (die, DW_AT_ranges, add_ranges (stmt));
+
+      chain = BLOCK_FRAGMENT_CHAIN (stmt);
+      do
+	{
+	  add_ranges (chain);
+	  chain = BLOCK_FRAGMENT_CHAIN (chain);
+	}
+      while (chain);
+      add_ranges (NULL);
+    }
+  else
+    {
+      ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL,
+				   BLOCK_NUMBER (stmt));
+      add_AT_lbl_id (die, DW_AT_low_pc, label);
+      ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL,
+				   BLOCK_NUMBER (stmt));
+      add_AT_lbl_id (die, DW_AT_high_pc, label);
+    }
+}
+
+/* Generate a DIE for a lexical block.  */
+
+static void
+gen_lexical_block_die (tree stmt, dw_die_ref context_die, int depth)
+{
+  dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt);
+
+  if (! BLOCK_ABSTRACT (stmt) && TREE_ASM_WRITTEN (stmt))
+    add_high_low_attributes (stmt, stmt_die);
+
+  decls_for_scope (stmt, stmt_die, depth);
+}
+
+/* Generate a DIE for an inlined subprogram.  */
+
+static void
+gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die, int depth)
+{
+  tree decl = block_ultimate_origin (stmt);
+
+  /* Emit info for the abstract instance first, if we haven't yet.  We
+     must emit this even if the block is abstract, otherwise when we
+     emit the block below (or elsewhere), we may end up trying to emit
+     a die whose origin die hasn't been emitted, and crashing.  */
+  dwarf2out_abstract_function (decl);
+
+  if (! BLOCK_ABSTRACT (stmt))
+    {
+      dw_die_ref subr_die
+	= new_die (DW_TAG_inlined_subroutine, context_die, stmt);
+
+      add_abstract_origin_attribute (subr_die, decl);
+      if (TREE_ASM_WRITTEN (stmt))
+        add_high_low_attributes (stmt, subr_die);
+      add_call_src_coords_attributes (stmt, subr_die);
+
+      decls_for_scope (stmt, subr_die, depth);
+      current_function_has_inlines = 1;
+    }
+  else
+    /* We may get here if we're the outer block of function A that was
+       inlined into function B that was inlined into function C.  When
+       generating debugging info for C, dwarf2out_abstract_function(B)
+       would mark all inlined blocks as abstract, including this one.
+       So, we wouldn't (and shouldn't) expect labels to be generated
+       for this one.  Instead, just emit debugging info for
+       declarations within the block.  This is particularly important
+       in the case of initializers of arguments passed from B to us:
+       if they're statement expressions containing declarations, we
+       wouldn't generate dies for their abstract variables, and then,
+       when generating dies for the real variables, we'd die (pun
+       intended :-)  */
+    gen_lexical_block_die (stmt, context_die, depth);
+}
+
+/* Generate a DIE for a field in a record, or structure.  */
+
+static void
+gen_field_die (tree decl, dw_die_ref context_die)
+{
+  dw_die_ref decl_die;
+
+  if (TREE_TYPE (decl) == error_mark_node)
+    return;
+
+  decl_die = new_die (DW_TAG_member, context_die, decl);
+  add_name_and_src_coords_attributes (decl_die, decl);
+  add_type_attribute (decl_die, member_declared_type (decl),
+		      TREE_READONLY (decl), TREE_THIS_VOLATILE (decl),
+		      context_die);
+
+  if (DECL_BIT_FIELD_TYPE (decl))
+    {
+      add_byte_size_attribute (decl_die, decl);
+      add_bit_size_attribute (decl_die, decl);
+      add_bit_offset_attribute (decl_die, decl);
+    }
+
+  if (TREE_CODE (DECL_FIELD_CONTEXT (decl)) != UNION_TYPE)
+    add_data_member_location_attribute (decl_die, decl);
+
+  if (DECL_ARTIFICIAL (decl))
+    add_AT_flag (decl_die, DW_AT_artificial, 1);
+
+  if (TREE_PROTECTED (decl))
+    add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_protected);
+  else if (TREE_PRIVATE (decl))
+    add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_private);
+
+  /* Equate decl number to die, so that we can look up this decl later on.  */
+  equate_decl_number_to_die (decl, decl_die);
+}
+
+#if 0
+/* Don't generate either pointer_type DIEs or reference_type DIEs here.
+   Use modified_type_die instead.
+   We keep this code here just in case these types of DIEs may be needed to
+   represent certain things in other languages (e.g. Pascal) someday.  */
+
+static void
+gen_pointer_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref ptr_die
+    = new_die (DW_TAG_pointer_type, scope_die_for (type, context_die), type);
+
+  equate_type_number_to_die (type, ptr_die);
+  add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die);
+  add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE);
+}
+
+/* Don't generate either pointer_type DIEs or reference_type DIEs here.
+   Use modified_type_die instead.
+   We keep this code here just in case these types of DIEs may be needed to
+   represent certain things in other languages (e.g. Pascal) someday.  */
+
+static void
+gen_reference_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref ref_die
+    = new_die (DW_TAG_reference_type, scope_die_for (type, context_die), type);
+
+  equate_type_number_to_die (type, ref_die);
+  add_type_attribute (ref_die, TREE_TYPE (type), 0, 0, context_die);
+  add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE);
+}
+#endif
+
+/* Generate a DIE for a pointer to a member type.  */
+
+static void
+gen_ptr_to_mbr_type_die (tree type, dw_die_ref context_die)
+{
+  dw_die_ref ptr_die
+    = new_die (DW_TAG_ptr_to_member_type,
+	       scope_die_for (type, context_die), type);
+
+  equate_type_number_to_die (type, ptr_die);
+  add_AT_die_ref (ptr_die, DW_AT_containing_type,
+		  lookup_type_die (TYPE_OFFSET_BASETYPE (type)));
+  add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die);
+}
+
+/* Generate the DIE for the compilation unit.  */
+
+static dw_die_ref
+gen_compile_unit_die (const char *filename)
+{
+  dw_die_ref die;
+  char producer[250];
+  const char *language_string = lang_hooks.name;
+  int language;
+
+  die = new_die (DW_TAG_compile_unit, NULL, NULL);
+
+  if (filename)
+    {
+      add_name_attribute (die, filename);
+      /* Don't add cwd for <built-in>.  */
+      if (!IS_ABSOLUTE_PATH (filename) && filename[0] != '<')
+	add_comp_dir_attribute (die);
+    }
+
+  sprintf (producer, "%s %s", language_string, version_string);
+
+#ifdef MIPS_DEBUGGING_INFO
+  /* The MIPS/SGI compilers place the 'cc' command line options in the producer
+     string.  The SGI debugger looks for -g, -g1, -g2, or -g3; if they do
+     not appear in the producer string, the debugger reaches the conclusion
+     that the object file is stripped and has no debugging information.
+     To get the MIPS/SGI debugger to believe that there is debugging
+     information in the object file, we add a -g to the producer string.  */
+  if (debug_info_level > DINFO_LEVEL_TERSE)
+    strcat (producer, " -g");
+#endif
+
+  add_AT_string (die, DW_AT_producer, producer);
+
+  if (strcmp (language_string, "GNU C++") == 0)
+    language = DW_LANG_C_plus_plus;
+  else if (strcmp (language_string, "GNU Ada") == 0)
+    language = DW_LANG_Ada95;
+  else if (strcmp (language_string, "GNU F77") == 0)
+    language = DW_LANG_Fortran77;
+  else if (strcmp (language_string, "GNU Fortran") == 0)
+    language = DW_LANG_Fortran95;
+  else if (strcmp (language_string, "GNU Pascal") == 0)
+    language = DW_LANG_Pascal83;
+  else if (strcmp (language_string, "GNU Java") == 0)
+    language = DW_LANG_Java;
+  else if (strcmp (language_string, "GNU Objective-C") == 0)
+    language = DW_LANG_ObjC;
+  else if (strcmp (language_string, "GNU Objective-C++") == 0)
+    language = DW_LANG_ObjC_plus_plus;
+  else
+    language = DW_LANG_C89;
+
+  add_AT_unsigned (die, DW_AT_language, language);
+  return die;
+}
+
+/* Generate the DIE for a base class.  */
+
+static void
+gen_inheritance_die (tree binfo, tree access, dw_die_ref context_die)
+{
+  dw_die_ref die = new_die (DW_TAG_inheritance, context_die, binfo);
+
+  add_type_attribute (die, BINFO_TYPE (binfo), 0, 0, context_die);
+  add_data_member_location_attribute (die, binfo);
+
+  if (BINFO_VIRTUAL_P (binfo))
+    add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual);
+
+  if (access == access_public_node)
+    add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public);
+  else if (access == access_protected_node)
+    add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected);
+}
+
+/* Generate a DIE for a class member.  */
+
+static void
+gen_member_die (tree type, dw_die_ref context_die)
+{
+  tree member;
+  tree binfo = TYPE_BINFO (type);
+  dw_die_ref child;
+
+  /* If this is not an incomplete type, output descriptions of each of its
+     members. Note that as we output the DIEs necessary to represent the
+     members of this record or union type, we will also be trying to output
+     DIEs to represent the *types* of those members. However the `type'
+     function (above) will specifically avoid generating type DIEs for member
+     types *within* the list of member DIEs for this (containing) type except
+     for those types (of members) which are explicitly marked as also being
+     members of this (containing) type themselves.  The g++ front- end can
+     force any given type to be treated as a member of some other (containing)
+     type by setting the TYPE_CONTEXT of the given (member) type to point to
+     the TREE node representing the appropriate (containing) type.  */
+
+  /* First output info about the base classes.  */
+  if (binfo)
+    {
+      VEC(tree,gc) *accesses = BINFO_BASE_ACCESSES (binfo);
+      int i;
+      tree base;
+
+      for (i = 0; BINFO_BASE_ITERATE (binfo, i, base); i++)
+	gen_inheritance_die (base,
+			     (accesses ? VEC_index (tree, accesses, i)
+			      : access_public_node), context_die);
+    }
+
+  /* Now output info about the data members and type members.  */
+  for (member = TYPE_FIELDS (type); member; member = TREE_CHAIN (member))
+    {
+      /* If we thought we were generating minimal debug info for TYPE
+	 and then changed our minds, some of the member declarations
+	 may have already been defined.  Don't define them again, but
+	 do put them in the right order.  */
+
+      child = lookup_decl_die (member);
+      if (child)
+	splice_child_die (context_die, child);
+      else
+	gen_decl_die (member, NULL, context_die);
+    }
+
+  /* Now output info about the function members (if any).  */
+  for (member = TYPE_METHODS (type); member; member = TREE_CHAIN (member))
+    {
+      /* Don't include clones in the member list.  */
+      if (DECL_ABSTRACT_ORIGIN (member))
+	continue;
+
+      child = lookup_decl_die (member);
+      if (child)
+	splice_child_die (context_die, child);
+      else
+	gen_decl_die (member, NULL, context_die);
+    }
+}
+
+/* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
+   is set, we pretend that the type was never defined, so we only get the
+   member DIEs needed by later specification DIEs.  */
+
+static void
+gen_struct_or_union_type_die (tree type, dw_die_ref context_die,
+				enum debug_info_usage usage)
+{
+  dw_die_ref type_die = lookup_type_die (type);
+  dw_die_ref scope_die = 0;
+  int nested = 0;
+  int complete = (TYPE_SIZE (type)
+		  && (! TYPE_STUB_DECL (type)
+		      || ! TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))));
+  int ns_decl = (context_die && context_die->die_tag == DW_TAG_namespace);
+  complete = complete && should_emit_struct_debug (type, usage);
+
+  if (type_die && ! complete)
+    return;
+
+  if (TYPE_CONTEXT (type) != NULL_TREE
+      && (AGGREGATE_TYPE_P (TYPE_CONTEXT (type))
+	  || TREE_CODE (TYPE_CONTEXT (type)) == NAMESPACE_DECL))
+    nested = 1;
+
+  scope_die = scope_die_for (type, context_die);
+
+  if (! type_die || (nested && scope_die == comp_unit_die))
+    /* First occurrence of type or toplevel definition of nested class.  */
+    {
+      dw_die_ref old_die = type_die;
+
+      type_die = new_die (TREE_CODE (type) == RECORD_TYPE
+			  ? record_type_tag (type) : DW_TAG_union_type,
+			  scope_die, type);
+      equate_type_number_to_die (type, type_die);
+      if (old_die)
+	add_AT_specification (type_die, old_die);
+      else
+	add_name_attribute (type_die, type_tag (type));
+    }
+  else
+    remove_AT (type_die, DW_AT_declaration);
+
+  /* If this type has been completed, then give it a byte_size attribute and
+     then give a list of members.  */
+  if (complete && !ns_decl)
+    {
+      /* Prevent infinite recursion in cases where the type of some member of
+	 this type is expressed in terms of this type itself.  */
+      TREE_ASM_WRITTEN (type) = 1;
+      add_byte_size_attribute (type_die, type);
+      if (TYPE_STUB_DECL (type) != NULL_TREE)
+	add_src_coords_attributes (type_die, TYPE_STUB_DECL (type));
+
+      /* If the first reference to this type was as the return type of an
+	 inline function, then it may not have a parent.  Fix this now.  */
+      if (type_die->die_parent == NULL)
+	add_child_die (scope_die, type_die);
+
+      push_decl_scope (type);
+      gen_member_die (type, type_die);
+      pop_decl_scope ();
+
+      /* GNU extension: Record what type our vtable lives in.  */
+      if (TYPE_VFIELD (type))
+	{
+	  tree vtype = DECL_FCONTEXT (TYPE_VFIELD (type));
+
+	  gen_type_die (vtype, context_die);
+	  add_AT_die_ref (type_die, DW_AT_containing_type,
+			  lookup_type_die (vtype));
+	}
+    }
+  else
+    {
+      add_AT_flag (type_die, DW_AT_declaration, 1);
+
+      /* We don't need to do this for function-local types.  */
+      if (TYPE_STUB_DECL (type)
+	  && ! decl_function_context (TYPE_STUB_DECL (type)))
+	VEC_safe_push (tree, gc, incomplete_types, type);
+    }
+
+  if (get_AT (type_die, DW_AT_name))
+    add_pubtype (type, type_die);
+}
+
+/* Generate a DIE for a subroutine _type_.  */
+
+static void
+gen_subroutine_type_die (tree type, dw_die_ref context_die)
+{
+  tree return_type = TREE_TYPE (type);
+  dw_die_ref subr_die
+    = new_die (DW_TAG_subroutine_type,
+	       scope_die_for (type, context_die), type);
+
+  equate_type_number_to_die (type, subr_die);
+  add_prototyped_attribute (subr_die, type);
+  add_type_attribute (subr_die, return_type, 0, 0, context_die);
+  gen_formal_types_die (type, subr_die);
+
+  if (get_AT (subr_die, DW_AT_name))
+    add_pubtype (type, subr_die);
+}
+
+/* Generate a DIE for a type definition.  */
+
+static void
+gen_typedef_die (tree decl, dw_die_ref context_die)
+{
+  dw_die_ref type_die;
+  tree origin;
+
+  if (TREE_ASM_WRITTEN (decl))
+    return;
+
+  TREE_ASM_WRITTEN (decl) = 1;
+  type_die = new_die (DW_TAG_typedef, context_die, decl);
+  origin = decl_ultimate_origin (decl);
+  if (origin != NULL)
+    add_abstract_origin_attribute (type_die, origin);
+  else
+    {
+      tree type;
+
+      add_name_and_src_coords_attributes (type_die, decl);
+      if (DECL_ORIGINAL_TYPE (decl))
+	{
+	  type = DECL_ORIGINAL_TYPE (decl);
+
+	  gcc_assert (type != TREE_TYPE (decl));
+	  equate_type_number_to_die (TREE_TYPE (decl), type_die);
+	}
+      else
+	type = TREE_TYPE (decl);
+
+      add_type_attribute (type_die, type, TREE_READONLY (decl),
+			  TREE_THIS_VOLATILE (decl), context_die);
+    }
+
+  if (DECL_ABSTRACT (decl))
+    equate_decl_number_to_die (decl, type_die);
+
+  if (get_AT (type_die, DW_AT_name))
+    add_pubtype (decl, type_die);
+}
+
+/* Generate a type description DIE.  */
+
+static void
+gen_type_die_with_usage (tree type, dw_die_ref context_die,
+				enum debug_info_usage usage)
+{
+  int need_pop;
+  struct array_descr_info info;
+
+  if (type == NULL_TREE || type == error_mark_node)
+    return;
+
+  if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+      && DECL_ORIGINAL_TYPE (TYPE_NAME (type)))
+    {
+      if (TREE_ASM_WRITTEN (type))
+	return;
+
+      /* Prevent broken recursion; we can't hand off to the same type.  */
+      gcc_assert (DECL_ORIGINAL_TYPE (TYPE_NAME (type)) != type);
+
+      TREE_ASM_WRITTEN (type) = 1;
+      gen_decl_die (TYPE_NAME (type), NULL, context_die);
+      return;
+    }
+
+  /* If this is an array type with hidden descriptor, handle it first.  */
+  if (!TREE_ASM_WRITTEN (type)
+      && lang_hooks.types.get_array_descr_info
+      && lang_hooks.types.get_array_descr_info (type, &info))
+    {
+      gen_descr_array_type_die (type, &info, context_die);
+      TREE_ASM_WRITTEN (type) = 1;
+      return;
+    }
+
+  /* We are going to output a DIE to represent the unqualified version
+     of this type (i.e. without any const or volatile qualifiers) so
+     get the main variant (i.e. the unqualified version) of this type
+     now.  (Vectors are special because the debugging info is in the
+     cloned type itself).  */
+  if (TREE_CODE (type) != VECTOR_TYPE)
+    type = type_main_variant (type);
+
+  if (TREE_ASM_WRITTEN (type))
+    return;
+
+  switch (TREE_CODE (type))
+    {
+    case ERROR_MARK:
+      break;
+
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+      /* We must set TREE_ASM_WRITTEN in case this is a recursive type.  This
+	 ensures that the gen_type_die recursion will terminate even if the
+	 type is recursive.  Recursive types are possible in Ada.  */
+      /* ??? We could perhaps do this for all types before the switch
+	 statement.  */
+      TREE_ASM_WRITTEN (type) = 1;
+
+      /* For these types, all that is required is that we output a DIE (or a
+	 set of DIEs) to represent the "basis" type.  */
+      gen_type_die_with_usage (TREE_TYPE (type), context_die,
+				DINFO_USAGE_IND_USE);
+      break;
+
+    case OFFSET_TYPE:
+      /* This code is used for C++ pointer-to-data-member types.
+	 Output a description of the relevant class type.  */
+      gen_type_die_with_usage (TYPE_OFFSET_BASETYPE (type), context_die,
+					DINFO_USAGE_IND_USE);
+
+      /* Output a description of the type of the object pointed to.  */
+      gen_type_die_with_usage (TREE_TYPE (type), context_die,
+					DINFO_USAGE_IND_USE);
+
+      /* Now output a DIE to represent this pointer-to-data-member type
+	 itself.  */
+      gen_ptr_to_mbr_type_die (type, context_die);
+      break;
+
+    case FUNCTION_TYPE:
+      /* Force out return type (in case it wasn't forced out already).  */
+      gen_type_die_with_usage (TREE_TYPE (type), context_die,
+					DINFO_USAGE_DIR_USE);
+      gen_subroutine_type_die (type, context_die);
+      break;
+
+    case METHOD_TYPE:
+      /* Force out return type (in case it wasn't forced out already).  */
+      gen_type_die_with_usage (TREE_TYPE (type), context_die,
+					DINFO_USAGE_DIR_USE);
+      gen_subroutine_type_die (type, context_die);
+      break;
+
+    case ARRAY_TYPE:
+      gen_array_type_die (type, context_die);
+      break;
+
+    case VECTOR_TYPE:
+      gen_array_type_die (type, context_die);
+      break;
+
+    case ENUMERAL_TYPE:
+    case RECORD_TYPE:
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      /* If this is a nested type whose containing class hasn't been written
+	 out yet, writing it out will cover this one, too.  This does not apply
+	 to instantiations of member class templates; they need to be added to
+	 the containing class as they are generated.  FIXME: This hurts the
+	 idea of combining type decls from multiple TUs, since we can't predict
+	 what set of template instantiations we'll get.  */
+      if (TYPE_CONTEXT (type)
+	  && AGGREGATE_TYPE_P (TYPE_CONTEXT (type))
+	  && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type)))
+	{
+	  gen_type_die_with_usage (TYPE_CONTEXT (type), context_die, usage);
+
+	  if (TREE_ASM_WRITTEN (type))
+	    return;
+
+	  /* If that failed, attach ourselves to the stub.  */
+	  push_decl_scope (TYPE_CONTEXT (type));
+	  context_die = lookup_type_die (TYPE_CONTEXT (type));
+	  need_pop = 1;
+	}
+      else
+	{
+	  context_die = declare_in_namespace (type, context_die);
+	  need_pop = 0;
+	}
+
+      if (TREE_CODE (type) == ENUMERAL_TYPE)
+	{
+	  /* This might have been written out by the call to
+	     declare_in_namespace.  */
+	  if (!TREE_ASM_WRITTEN (type))
+	    gen_enumeration_type_die (type, context_die);
+	}
+      else
+	gen_struct_or_union_type_die (type, context_die, usage);
+
+      if (need_pop)
+	pop_decl_scope ();
+
+      /* Don't set TREE_ASM_WRITTEN on an incomplete struct; we want to fix
+	 it up if it is ever completed.  gen_*_type_die will set it for us
+	 when appropriate.  */
+      return;
+
+    case VOID_TYPE:
+    case INTEGER_TYPE:
+    case REAL_TYPE:
+    case FIXED_POINT_TYPE:
+    case COMPLEX_TYPE:
+    case BOOLEAN_TYPE:
+      /* No DIEs needed for fundamental types.  */
+      break;
+
+    case LANG_TYPE:
+      /* No Dwarf representation currently defined.  */
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  TREE_ASM_WRITTEN (type) = 1;
+}
+
+static void
+gen_type_die (tree type, dw_die_ref context_die)
+{
+  gen_type_die_with_usage (type, context_die, DINFO_USAGE_DIR_USE);
+}
+
+/* Generate a DW_TAG_lexical_block DIE followed by DIEs to represent all of the
+   things which are local to the given block.  */
+
+static void
+gen_block_die (tree stmt, dw_die_ref context_die, int depth)
+{
+  int must_output_die = 0;
+  bool inlined_func;
+
+  /* Ignore blocks that are NULL.  */
+  if (stmt == NULL_TREE)
+    return;
+
+  inlined_func = inlined_function_outer_scope_p (stmt);
+
+  /* If the block is one fragment of a non-contiguous block, do not
+     process the variables, since they will have been done by the
+     origin block.  Do process subblocks.  */
+  if (BLOCK_FRAGMENT_ORIGIN (stmt))
+    {
+      tree sub;
+
+      for (sub = BLOCK_SUBBLOCKS (stmt); sub; sub = BLOCK_CHAIN (sub))
+	gen_block_die (sub, context_die, depth + 1);
+
+      return;
+    }
+
+  /* Determine if we need to output any Dwarf DIEs at all to represent this
+     block.  */
+  if (inlined_func)
+    /* The outer scopes for inlinings *must* always be represented.  We
+       generate DW_TAG_inlined_subroutine DIEs for them.  (See below.) */
+    must_output_die = 1;
+  else
+    {
+      /* Determine if this block directly contains any "significant"
+	 local declarations which we will need to output DIEs for.  */
+      if (debug_info_level > DINFO_LEVEL_TERSE)
+	/* We are not in terse mode so *any* local declaration counts
+	   as being a "significant" one.  */
+	must_output_die = ((BLOCK_VARS (stmt) != NULL
+			    || BLOCK_NUM_NONLOCALIZED_VARS (stmt))
+			   && (TREE_USED (stmt)
+			       || TREE_ASM_WRITTEN (stmt)
+			       || BLOCK_ABSTRACT (stmt)));
+      else if ((TREE_USED (stmt)
+		|| TREE_ASM_WRITTEN (stmt)
+		|| BLOCK_ABSTRACT (stmt))
+      	       && !dwarf2out_ignore_block (stmt))
+	must_output_die = 1;
+    }
+
+  /* It would be a waste of space to generate a Dwarf DW_TAG_lexical_block
+     DIE for any block which contains no significant local declarations at
+     all.  Rather, in such cases we just call `decls_for_scope' so that any
+     needed Dwarf info for any sub-blocks will get properly generated. Note
+     that in terse mode, our definition of what constitutes a "significant"
+     local declaration gets restricted to include only inlined function
+     instances and local (nested) function definitions.  */
+  if (must_output_die)
+    {
+      if (inlined_func)
+	gen_inlined_subroutine_die (stmt, context_die, depth);
+      else
+	gen_lexical_block_die (stmt, context_die, depth);
+    }
+  else
+    decls_for_scope (stmt, context_die, depth);
+}
+
+/* Process variable DECL (or variable with origin ORIGIN) within
+   block STMT and add it to CONTEXT_DIE.  */
+static void
+process_scope_var (tree stmt, tree decl, tree origin, dw_die_ref context_die)
+{
+  dw_die_ref die;
+  tree decl_or_origin = decl ? decl : origin;
+  tree ultimate_origin = origin ? decl_ultimate_origin (origin) : NULL;
+
+  if (ultimate_origin)
+    origin = ultimate_origin;
+
+  if (TREE_CODE (decl_or_origin) == FUNCTION_DECL)
+    die = lookup_decl_die (decl_or_origin);
+  else if (TREE_CODE (decl_or_origin) == TYPE_DECL
+           && TYPE_DECL_IS_STUB (decl_or_origin))
+    die = lookup_type_die (TREE_TYPE (decl_or_origin));
+  else
+    die = NULL;
+
+  if (die != NULL && die->die_parent == NULL)
+    add_child_die (context_die, die);
+  else if (TREE_CODE (decl_or_origin) == IMPORTED_DECL)
+    dwarf2out_imported_module_or_decl_1 (decl_or_origin, DECL_NAME (decl_or_origin),
+					 stmt, context_die);
+  else
+    gen_decl_die (decl, origin, context_die);
+}
+
+/* Generate all of the decls declared within a given scope and (recursively)
+   all of its sub-blocks.  */
+
+static void
+decls_for_scope (tree stmt, dw_die_ref context_die, int depth)
+{
+  tree decl;
+  unsigned int i;
+  tree subblocks;
+
+  /* Ignore NULL blocks.  */
+  if (stmt == NULL_TREE)
+    return;
+
+  /* Output the DIEs to represent all of the data objects and typedefs
+     declared directly within this block but not within any nested
+     sub-blocks.  Also, nested function and tag DIEs have been
+     generated with a parent of NULL; fix that up now.  */
+  for (decl = BLOCK_VARS (stmt); decl != NULL; decl = TREE_CHAIN (decl))
+    process_scope_var (stmt, decl, NULL_TREE, context_die);
+  for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
+    process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i),
+    		       context_die);
+
+  /* If we're at -g1, we're not interested in subblocks.  */
+  if (debug_info_level <= DINFO_LEVEL_TERSE)
+    return;
+
+  /* Output the DIEs to represent all sub-blocks (and the items declared
+     therein) of this block.  */
+  for (subblocks = BLOCK_SUBBLOCKS (stmt);
+       subblocks != NULL;
+       subblocks = BLOCK_CHAIN (subblocks))
+    gen_block_die (subblocks, context_die, depth + 1);
+}
+
+/* Is this a typedef we can avoid emitting?  */
+
+static inline int
+is_redundant_typedef (const_tree decl)
+{
+  if (TYPE_DECL_IS_STUB (decl))
+    return 1;
+
+  if (DECL_ARTIFICIAL (decl)
+      && DECL_CONTEXT (decl)
+      && is_tagged_type (DECL_CONTEXT (decl))
+      && TREE_CODE (TYPE_NAME (DECL_CONTEXT (decl))) == TYPE_DECL
+      && DECL_NAME (decl) == DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl))))
+    /* Also ignore the artificial member typedef for the class name.  */
+    return 1;
+
+  return 0;
+}
+
+/* Returns the DIE for a context.  */
+
+static inline dw_die_ref
+get_context_die (tree context)
+{
+  if (context)
+    {
+      /* Find die that represents this context.  */
+      if (TYPE_P (context))
+	return force_type_die (context);
+      else
+	return force_decl_die (context);
+    }
+  return comp_unit_die;
+}
+
+/* Returns the DIE for decl.  A DIE will always be returned.  */
+
+static dw_die_ref
+force_decl_die (tree decl)
+{
+  dw_die_ref decl_die;
+  unsigned saved_external_flag;
+  tree save_fn = NULL_TREE;
+  decl_die = lookup_decl_die (decl);
+  if (!decl_die)
+    {
+      dw_die_ref context_die = get_context_die (DECL_CONTEXT (decl));
+
+      decl_die = lookup_decl_die (decl);
+      if (decl_die)
+	return decl_die;
+
+      switch (TREE_CODE (decl))
+	{
+	case FUNCTION_DECL:
+	  /* Clear current_function_decl, so that gen_subprogram_die thinks
+	     that this is a declaration. At this point, we just want to force
+	     declaration die.  */
+	  save_fn = current_function_decl;
+	  current_function_decl = NULL_TREE;
+	  gen_subprogram_die (decl, context_die);
+	  current_function_decl = save_fn;
+	  break;
+
+	case VAR_DECL:
+	  /* Set external flag to force declaration die. Restore it after
+	   gen_decl_die() call.  */
+	  saved_external_flag = DECL_EXTERNAL (decl);
+	  DECL_EXTERNAL (decl) = 1;
+	  gen_decl_die (decl, NULL, context_die);
+	  DECL_EXTERNAL (decl) = saved_external_flag;
+	  break;
+
+	case NAMESPACE_DECL:
+	  dwarf2out_decl (decl);
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+
+      /* We should be able to find the DIE now.  */
+      if (!decl_die)
+	decl_die = lookup_decl_die (decl);
+      gcc_assert (decl_die);
+    }
+
+  return decl_die;
+}
+
+/* Returns the DIE for TYPE, that must not be a base type.  A DIE is
+   always returned.  */
+
+static dw_die_ref
+force_type_die (tree type)
+{
+  dw_die_ref type_die;
+
+  type_die = lookup_type_die (type);
+  if (!type_die)
+    {
+      dw_die_ref context_die = get_context_die (TYPE_CONTEXT (type));
+
+      type_die = modified_type_die (type, TYPE_READONLY (type),
+				    TYPE_VOLATILE (type), context_die);
+      gcc_assert (type_die);
+    }
+  return type_die;
+}
+
+/* Force out any required namespaces to be able to output DECL,
+   and return the new context_die for it, if it's changed.  */
+
+static dw_die_ref
+setup_namespace_context (tree thing, dw_die_ref context_die)
+{
+  tree context = (DECL_P (thing)
+		  ? DECL_CONTEXT (thing) : TYPE_CONTEXT (thing));
+  if (context && TREE_CODE (context) == NAMESPACE_DECL)
+    /* Force out the namespace.  */
+    context_die = force_decl_die (context);
+
+  return context_die;
+}
+
+/* Emit a declaration DIE for THING (which is either a DECL or a tagged
+   type) within its namespace, if appropriate.
+
+   For compatibility with older debuggers, namespace DIEs only contain
+   declarations; all definitions are emitted at CU scope.  */
+
+static dw_die_ref
+declare_in_namespace (tree thing, dw_die_ref context_die)
+{
+  dw_die_ref ns_context;
+
+  if (debug_info_level <= DINFO_LEVEL_TERSE)
+    return context_die;
+
+  /* If this decl is from an inlined function, then don't try to emit it in its
+     namespace, as we will get confused.  It would have already been emitted
+     when the abstract instance of the inline function was emitted anyways.  */
+  if (DECL_P (thing) && DECL_ABSTRACT_ORIGIN (thing))
+    return context_die;
+
+  ns_context = setup_namespace_context (thing, context_die);
+
+  if (ns_context != context_die)
+    {
+      if (is_fortran ())
+	return ns_context;
+      if (DECL_P (thing))
+	gen_decl_die (thing, NULL, ns_context);
+      else
+	gen_type_die (thing, ns_context);
+    }
+  return context_die;
+}
+
+/* Generate a DIE for a namespace or namespace alias.  */
+
+static void
+gen_namespace_die (tree decl, dw_die_ref context_die)
+{
+  dw_die_ref namespace_die;
+
+  /* Namespace aliases have a DECL_ABSTRACT_ORIGIN of the namespace
+     they are an alias of.  */
+  if (DECL_ABSTRACT_ORIGIN (decl) == NULL)
+    {
+      /* Output a real namespace or module.  */
+      context_die = setup_namespace_context (decl, comp_unit_die);
+      namespace_die = new_die (is_fortran ()
+			       ? DW_TAG_module : DW_TAG_namespace,
+			       context_die, decl);
+      /* For Fortran modules defined in different CU don't add src coords.  */
+      if (namespace_die->die_tag == DW_TAG_module && DECL_EXTERNAL (decl))
+	add_name_attribute (namespace_die, dwarf2_name (decl, 0));
+      else
+	add_name_and_src_coords_attributes (namespace_die, decl);
+      if (DECL_EXTERNAL (decl))
+	add_AT_flag (namespace_die, DW_AT_declaration, 1);
+      equate_decl_number_to_die (decl, namespace_die);
+    }
+  else
+    {
+      /* Output a namespace alias.  */
+
+      /* Force out the namespace we are an alias of, if necessary.  */
+      dw_die_ref origin_die
+	= force_decl_die (DECL_ABSTRACT_ORIGIN (decl));
+
+      if (DECL_CONTEXT (decl) == NULL_TREE
+	  || TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL)
+	context_die = setup_namespace_context (decl, comp_unit_die);
+      /* Now create the namespace alias DIE.  */
+      namespace_die = new_die (DW_TAG_imported_declaration, context_die, decl);
+      add_name_and_src_coords_attributes (namespace_die, decl);
+      add_AT_die_ref (namespace_die, DW_AT_import, origin_die);
+      equate_decl_number_to_die (decl, namespace_die);
+    }
+}
+
+/* Generate Dwarf debug information for a decl described by DECL.  */
+
+static void
+gen_decl_die (tree decl, tree origin, dw_die_ref context_die)
+{
+  tree decl_or_origin = decl ? decl : origin;
+  tree class_origin = NULL;
+
+  if (DECL_P (decl_or_origin) && DECL_IGNORED_P (decl_or_origin))
+    return;
+
+  switch (TREE_CODE (decl_or_origin))
+    {
+    case ERROR_MARK:
+      break;
+
+    case CONST_DECL:
+      if (!is_fortran ())
+	{
+	  /* The individual enumerators of an enum type get output when we output
+	     the Dwarf representation of the relevant enum type itself.  */
+	  break;
+	}
+
+      /* Emit its type.  */
+      gen_type_die (TREE_TYPE (decl), context_die);
+
+      /* And its containing namespace.  */
+      context_die = declare_in_namespace (decl, context_die);
+
+      gen_const_die (decl, context_die);
+      break;
+
+    case FUNCTION_DECL:
+      /* Don't output any DIEs to represent mere function declarations,
+	 unless they are class members or explicit block externs.  */
+      if (DECL_INITIAL (decl_or_origin) == NULL_TREE
+          && DECL_CONTEXT (decl_or_origin) == NULL_TREE
+	  && (current_function_decl == NULL_TREE
+	      || DECL_ARTIFICIAL (decl_or_origin)))
+	break;
+
+#if 0
+      /* FIXME */
+      /* This doesn't work because the C frontend sets DECL_ABSTRACT_ORIGIN
+	 on local redeclarations of global functions.  That seems broken.  */
+      if (current_function_decl != decl)
+	/* This is only a declaration.  */;
+#endif
+
+      /* If we're emitting a clone, emit info for the abstract instance.  */
+      if (origin || DECL_ORIGIN (decl) != decl)
+	dwarf2out_abstract_function (origin ? origin : DECL_ABSTRACT_ORIGIN (decl));
+
+      /* If we're emitting an out-of-line copy of an inline function,
+	 emit info for the abstract instance and set up to refer to it.  */
+      else if (cgraph_function_possibly_inlined_p (decl)
+	       && ! DECL_ABSTRACT (decl)
+	       && ! class_or_namespace_scope_p (context_die)
+	       /* dwarf2out_abstract_function won't emit a die if this is just
+		  a declaration.  We must avoid setting DECL_ABSTRACT_ORIGIN in
+		  that case, because that works only if we have a die.  */
+	       && DECL_INITIAL (decl) != NULL_TREE)
+	{
+	  dwarf2out_abstract_function (decl);
+	  set_decl_origin_self (decl);
+	}
+
+      /* Otherwise we're emitting the primary DIE for this decl.  */
+      else if (debug_info_level > DINFO_LEVEL_TERSE)
+	{
+	  /* Before we describe the FUNCTION_DECL itself, make sure that we
+	     have described its return type.  */
+	  gen_type_die (TREE_TYPE (TREE_TYPE (decl)), context_die);
+
+	  /* And its virtual context.  */
+	  if (DECL_VINDEX (decl) != NULL_TREE)
+	    gen_type_die (DECL_CONTEXT (decl), context_die);
+
+	  /* And its containing type.  */
+	  if (!origin)
+	    origin = decl_class_context (decl);
+	  if (origin != NULL_TREE)
+	    gen_type_die_for_member (origin, decl, context_die);
+
+	  /* And its containing namespace.  */
+	  context_die = declare_in_namespace (decl, context_die);
+	}
+
+      /* Now output a DIE to represent the function itself.  */
+      if (decl)
+        gen_subprogram_die (decl, context_die);
+      break;
+
+    case TYPE_DECL:
+      /* If we are in terse mode, don't generate any DIEs to represent any
+	 actual typedefs.  */
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	break;
+
+      /* In the special case of a TYPE_DECL node representing the declaration
+	 of some type tag, if the given TYPE_DECL is marked as having been
+	 instantiated from some other (original) TYPE_DECL node (e.g. one which
+	 was generated within the original definition of an inline function) we
+	 used to generate a special (abbreviated) DW_TAG_structure_type,
+	 DW_TAG_union_type, or DW_TAG_enumeration_type DIE here.  But nothing
+	 should be actually referencing those DIEs, as variable DIEs with that
+	 type would be emitted already in the abstract origin, so it was always
+	 removed during unused type prunning.  Don't add anything in this
+	 case.  */
+      if (TYPE_DECL_IS_STUB (decl) && decl_ultimate_origin (decl) != NULL_TREE)
+	break;
+
+      if (is_redundant_typedef (decl))
+	gen_type_die (TREE_TYPE (decl), context_die);
+      else
+	/* Output a DIE to represent the typedef itself.  */
+	gen_typedef_die (decl, context_die);
+      break;
+
+    case LABEL_DECL:
+      if (debug_info_level >= DINFO_LEVEL_NORMAL)
+	gen_label_die (decl, context_die);
+      break;
+
+    case VAR_DECL:
+    case RESULT_DECL:
+      /* If we are in terse mode, don't generate any DIEs to represent any
+	 variable declarations or definitions.  */
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	break;
+
+      /* Output any DIEs that are needed to specify the type of this data
+	 object.  */
+      if (TREE_CODE (decl_or_origin) == RESULT_DECL
+          && DECL_BY_REFERENCE (decl_or_origin))
+	gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
+      else
+	gen_type_die (TREE_TYPE (decl_or_origin), context_die);
+
+      /* And its containing type.  */
+      class_origin = decl_class_context (decl_or_origin);
+      if (class_origin != NULL_TREE)
+	gen_type_die_for_member (class_origin, decl_or_origin, context_die);
+
+      /* And its containing namespace.  */
+      context_die = declare_in_namespace (decl_or_origin, context_die);
+
+      /* Now output the DIE to represent the data object itself.  This gets
+	 complicated because of the possibility that the VAR_DECL really
+	 represents an inlined instance of a formal parameter for an inline
+	 function.  */
+      if (!origin)
+        origin = decl_ultimate_origin (decl);
+      if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL)
+	gen_formal_parameter_die (decl, origin, context_die);
+      else
+	gen_variable_die (decl, origin, context_die);
+      break;
+
+    case FIELD_DECL:
+      /* Ignore the nameless fields that are used to skip bits but handle C++
+	 anonymous unions and structs.  */
+      if (DECL_NAME (decl) != NULL_TREE
+	  || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE
+	  || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE)
+	{
+	  gen_type_die (member_declared_type (decl), context_die);
+	  gen_field_die (decl, context_die);
+	}
+      break;
+
+    case PARM_DECL:
+      if (DECL_BY_REFERENCE (decl_or_origin))
+	gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
+      else
+	gen_type_die (TREE_TYPE (decl_or_origin), context_die);
+      gen_formal_parameter_die (decl, origin, context_die);
+      break;
+
+    case NAMESPACE_DECL:
+    case IMPORTED_DECL:
+      gen_namespace_die (decl, context_die);
+      break;
+
+    default:
+      /* Probably some frontend-internal decl.  Assume we don't care.  */
+      gcc_assert ((int)TREE_CODE (decl) > NUM_TREE_CODES);
+      break;
+    }
+}
+
+/* Output debug information for global decl DECL.  Called from toplev.c after
+   compilation proper has finished.  */
+
+static void
+dwarf2out_global_decl (tree decl)
+{
+  /* Output DWARF2 information for file-scope tentative data object
+     declarations, file-scope (extern) function declarations (which
+     had no corresponding body) and file-scope tagged type declarations
+     and definitions which have not yet been forced out.  */
+  if (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl))
+    dwarf2out_decl (decl);
+}
+
+/* Output debug information for type decl DECL.  Called from toplev.c
+   and from language front ends (to record built-in types).  */
+static void
+dwarf2out_type_decl (tree decl, int local)
+{
+  if (!local)
+    dwarf2out_decl (decl);
+}
+
+/* Output debug information for imported module or decl DECL.
+   NAME is non-NULL name in the lexical block if the decl has been renamed.
+   LEXICAL_BLOCK is the lexical block (which TREE_CODE is a BLOCK)
+   that DECL belongs to.
+   LEXICAL_BLOCK_DIE is the DIE of LEXICAL_BLOCK.  */
+static void
+dwarf2out_imported_module_or_decl_1 (tree decl,
+				     tree name,
+				     tree lexical_block,
+				     dw_die_ref lexical_block_die)
+{
+  expanded_location xloc;
+  dw_die_ref imported_die = NULL;
+  dw_die_ref at_import_die;
+
+  if (TREE_CODE (decl) == IMPORTED_DECL)
+    {
+      xloc = expand_location (DECL_SOURCE_LOCATION (decl));
+      decl = IMPORTED_DECL_ASSOCIATED_DECL (decl);
+      gcc_assert (decl);
+    }
+  else
+    xloc = expand_location (input_location);
+
+  if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == CONST_DECL)
+    {
+      if (is_base_type (TREE_TYPE (decl)))
+	at_import_die = base_type_die (TREE_TYPE (decl));
+      else
+	at_import_die = force_type_die (TREE_TYPE (decl));
+      /* For namespace N { typedef void T; } using N::T; base_type_die
+	 returns NULL, but DW_TAG_imported_declaration requires
+	 the DW_AT_import tag.  Force creation of DW_TAG_typedef.  */
+      if (!at_import_die)
+	{
+	  gcc_assert (TREE_CODE (decl) == TYPE_DECL);
+	  gen_typedef_die (decl, get_context_die (DECL_CONTEXT (decl)));
+	  at_import_die = lookup_type_die (TREE_TYPE (decl));
+	  gcc_assert (at_import_die);
+	}
+    }
+  else
+    {
+      at_import_die = lookup_decl_die (decl);
+      if (!at_import_die)
+	{
+	  /* If we're trying to avoid duplicate debug info, we may not have
+	     emitted the member decl for this field.  Emit it now.  */
+	  if (TREE_CODE (decl) == FIELD_DECL)
+	    {
+	      tree type = DECL_CONTEXT (decl);
+
+	      if (TYPE_CONTEXT (type)
+		  && TYPE_P (TYPE_CONTEXT (type))
+		  && !should_emit_struct_debug (TYPE_CONTEXT (type),
+						DINFO_USAGE_DIR_USE))
+		return;
+	      gen_type_die_for_member (type, decl,
+				       get_context_die (TYPE_CONTEXT (type)));
+	    }
+	  at_import_die = force_decl_die (decl);
+	}
+    }
+
+  if (TREE_CODE (decl) == NAMESPACE_DECL)
+    imported_die = new_die (DW_TAG_imported_module,
+			    lexical_block_die,
+			    lexical_block);
+  else
+    imported_die = new_die (DW_TAG_imported_declaration,
+			    lexical_block_die,
+			    lexical_block);
+
+  add_AT_file (imported_die, DW_AT_decl_file, lookup_filename (xloc.file));
+  add_AT_unsigned (imported_die, DW_AT_decl_line, xloc.line);
+  if (name)
+    add_AT_string (imported_die, DW_AT_name,
+		   IDENTIFIER_POINTER (name));
+  add_AT_die_ref (imported_die, DW_AT_import, at_import_die);
+}
+
+/* Output debug information for imported module or decl DECL.
+   NAME is non-NULL name in context if the decl has been renamed.
+   CHILD is true if decl is one of the renamed decls as part of
+   importing whole module.  */
+
+static void
+dwarf2out_imported_module_or_decl (tree decl, tree name, tree context,
+				   bool child)
+{
+  /* dw_die_ref at_import_die;  */
+  dw_die_ref scope_die;
+
+  if (debug_info_level <= DINFO_LEVEL_TERSE)
+    return;
+
+  gcc_assert (decl);
+
+  /* To emit DW_TAG_imported_module or DW_TAG_imported_decl, we need two DIEs.
+     We need decl DIE for reference and scope die. First, get DIE for the decl
+     itself.  */
+
+  /* Get the scope die for decl context. Use comp_unit_die for global module
+     or decl. If die is not found for non globals, force new die.  */
+  if (context
+      && TYPE_P (context)
+      && !should_emit_struct_debug (context, DINFO_USAGE_DIR_USE))
+    return;
+  scope_die = get_context_die (context);
+
+  if (child)
+    {
+      gcc_assert (scope_die->die_child);
+      gcc_assert (scope_die->die_child->die_tag == DW_TAG_imported_module);
+      gcc_assert (TREE_CODE (decl) != NAMESPACE_DECL);
+      scope_die = scope_die->die_child;
+    }
+
+  /* OK, now we have DIEs for decl as well as scope. Emit imported die.  */
+  dwarf2out_imported_module_or_decl_1 (decl, name, context, scope_die);
+
+}
+
+/* Write the debugging output for DECL.  */
+
+void
+dwarf2out_decl (tree decl)
+{
+  dw_die_ref context_die = comp_unit_die;
+
+  switch (TREE_CODE (decl))
+    {
+    case ERROR_MARK:
+      return;
+
+    case FUNCTION_DECL:
+      /* What we would really like to do here is to filter out all mere
+	 file-scope declarations of file-scope functions which are never
+	 referenced later within this translation unit (and keep all of ones
+	 that *are* referenced later on) but we aren't clairvoyant, so we have
+	 no idea which functions will be referenced in the future (i.e. later
+	 on within the current translation unit). So here we just ignore all
+	 file-scope function declarations which are not also definitions.  If
+	 and when the debugger needs to know something about these functions,
+	 it will have to hunt around and find the DWARF information associated
+	 with the definition of the function.
+
+	 We can't just check DECL_EXTERNAL to find out which FUNCTION_DECL
+	 nodes represent definitions and which ones represent mere
+	 declarations.  We have to check DECL_INITIAL instead. That's because
+	 the C front-end supports some weird semantics for "extern inline"
+	 function definitions.  These can get inlined within the current
+	 translation unit (and thus, we need to generate Dwarf info for their
+	 abstract instances so that the Dwarf info for the concrete inlined
+	 instances can have something to refer to) but the compiler never
+	 generates any out-of-lines instances of such things (despite the fact
+	 that they *are* definitions).
+
+	 The important point is that the C front-end marks these "extern
+	 inline" functions as DECL_EXTERNAL, but we need to generate DWARF for
+	 them anyway. Note that the C++ front-end also plays some similar games
+	 for inline function definitions appearing within include files which
+	 also contain `#pragma interface' pragmas.  */
+      if (DECL_INITIAL (decl) == NULL_TREE)
+	return;
+
+      /* If we're a nested function, initially use a parent of NULL; if we're
+	 a plain function, this will be fixed up in decls_for_scope.  If
+	 we're a method, it will be ignored, since we already have a DIE.  */
+      if (decl_function_context (decl)
+	  /* But if we're in terse mode, we don't care about scope.  */
+	  && debug_info_level > DINFO_LEVEL_TERSE)
+	context_die = NULL;
+      break;
+
+    case VAR_DECL:
+      /* Ignore this VAR_DECL if it refers to a file-scope extern data object
+	 declaration and if the declaration was never even referenced from
+	 within this entire compilation unit.  We suppress these DIEs in
+	 order to save space in the .debug section (by eliminating entries
+	 which are probably useless).  Note that we must not suppress
+	 block-local extern declarations (whether used or not) because that
+	 would screw-up the debugger's name lookup mechanism and cause it to
+	 miss things which really ought to be in scope at a given point.  */
+      if (DECL_EXTERNAL (decl) && !TREE_USED (decl))
+	return;
+
+      /* For local statics lookup proper context die.  */
+      if (TREE_STATIC (decl) && decl_function_context (decl))
+	context_die = lookup_decl_die (DECL_CONTEXT (decl));
+
+      /* If we are in terse mode, don't generate any DIEs to represent any
+	 variable declarations or definitions.  */
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	return;
+      break;
+
+    case CONST_DECL:
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	return;
+      if (!is_fortran ())
+	return;
+      if (TREE_STATIC (decl) && decl_function_context (decl))
+	context_die = lookup_decl_die (DECL_CONTEXT (decl));
+      break;
+
+    case NAMESPACE_DECL:
+    case IMPORTED_DECL:
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	return;
+      if (lookup_decl_die (decl) != NULL)
+	return;
+      break;
+
+    case TYPE_DECL:
+      /* Don't emit stubs for types unless they are needed by other DIEs.  */
+      if (TYPE_DECL_SUPPRESS_DEBUG (decl))
+	return;
+
+      /* Don't bother trying to generate any DIEs to represent any of the
+	 normal built-in types for the language we are compiling.  */
+      if (DECL_IS_BUILTIN (decl))
+	{
+	  /* OK, we need to generate one for `bool' so GDB knows what type
+	     comparisons have.  */
+	  if (is_cxx ()
+	      && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE
+	      && ! DECL_IGNORED_P (decl))
+	    modified_type_die (TREE_TYPE (decl), 0, 0, NULL);
+
+	  return;
+	}
+
+      /* If we are in terse mode, don't generate any DIEs for types.  */
+      if (debug_info_level <= DINFO_LEVEL_TERSE)
+	return;
+
+      /* If we're a function-scope tag, initially use a parent of NULL;
+	 this will be fixed up in decls_for_scope.  */
+      if (decl_function_context (decl))
+	context_die = NULL;
+
+      break;
+
+    default:
+      return;
+    }
+
+  gen_decl_die (decl, NULL, context_die);
+}
+
+/* Output a marker (i.e. a label) for the beginning of the generated code for
+   a lexical block.  */
+
+static void
+dwarf2out_begin_block (unsigned int line ATTRIBUTE_UNUSED,
+		       unsigned int blocknum)
+{
+  switch_to_section (current_function_section ());
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum);
+}
+
+/* Output a marker (i.e. a label) for the end of the generated code for a
+   lexical block.  */
+
+static void
+dwarf2out_end_block (unsigned int line ATTRIBUTE_UNUSED, unsigned int blocknum)
+{
+  switch_to_section (current_function_section ());
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_END_LABEL, blocknum);
+}
+
+/* Returns nonzero if it is appropriate not to emit any debugging
+   information for BLOCK, because it doesn't contain any instructions.
+
+   Don't allow this for blocks with nested functions or local classes
+   as we would end up with orphans, and in the presence of scheduling
+   we may end up calling them anyway.  */
+
+static bool
+dwarf2out_ignore_block (const_tree block)
+{
+  tree decl;
+  unsigned int i;
+
+  for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
+    if (TREE_CODE (decl) == FUNCTION_DECL
+	|| (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl)))
+      return 0;
+  for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (block); i++)
+    {
+      decl = BLOCK_NONLOCALIZED_VAR (block, i);
+      if (TREE_CODE (decl) == FUNCTION_DECL
+	  || (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl)))
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Hash table routines for file_hash.  */
+
+static int
+file_table_eq (const void *p1_p, const void *p2_p)
+{
+  const struct dwarf_file_data *const p1 =
+    (const struct dwarf_file_data *) p1_p;
+  const char *const p2 = (const char *) p2_p;
+  return strcmp (p1->filename, p2) == 0;
+}
+
+static hashval_t
+file_table_hash (const void *p_p)
+{
+  const struct dwarf_file_data *const p = (const struct dwarf_file_data *) p_p;
+  return htab_hash_string (p->filename);
+}
+
+/* Lookup FILE_NAME (in the list of filenames that we know about here in
+   dwarf2out.c) and return its "index".  The index of each (known) filename is
+   just a unique number which is associated with only that one filename.  We
+   need such numbers for the sake of generating labels (in the .debug_sfnames
+   section) and references to those files numbers (in the .debug_srcinfo
+   and.debug_macinfo sections).  If the filename given as an argument is not
+   found in our current list, add it to the list and assign it the next
+   available unique index number.  In order to speed up searches, we remember
+   the index of the filename was looked up last.  This handles the majority of
+   all searches.  */
+
+static struct dwarf_file_data *
+lookup_filename (const char *file_name)
+{
+  void ** slot;
+  struct dwarf_file_data * created;
+
+  /* Check to see if the file name that was searched on the previous
+     call matches this file name.  If so, return the index.  */
+  if (file_table_last_lookup
+      && (file_name == file_table_last_lookup->filename
+	  || strcmp (file_table_last_lookup->filename, file_name) == 0))
+    return file_table_last_lookup;
+
+  /* Didn't match the previous lookup, search the table.  */
+  slot = htab_find_slot_with_hash (file_table, file_name,
+				   htab_hash_string (file_name), INSERT);
+  if (*slot)
+    return (struct dwarf_file_data *) *slot;
+
+  created = GGC_NEW (struct dwarf_file_data);
+  created->filename = file_name;
+  created->emitted_number = 0;
+  *slot = created;
+  return created;
+}
+
+/* If the assembler will construct the file table, then translate the compiler
+   internal file table number into the assembler file table number, and emit
+   a .file directive if we haven't already emitted one yet.  The file table
+   numbers are different because we prune debug info for unused variables and
+   types, which may include filenames.  */
+
+static int
+maybe_emit_file (struct dwarf_file_data * fd)
+{
+  if (! fd->emitted_number)
+    {
+      if (last_emitted_file)
+	fd->emitted_number = last_emitted_file->emitted_number + 1;
+      else
+	fd->emitted_number = 1;
+      last_emitted_file = fd;
+
+      if (DWARF2_ASM_LINE_DEBUG_INFO)
+	{
+	  fprintf (asm_out_file, "\t.file %u ", fd->emitted_number);
+	  output_quoted_string (asm_out_file,
+				remap_debug_filename (fd->filename));
+	  fputc ('\n', asm_out_file);
+	}
+    }
+
+  return fd->emitted_number;
+}
+
+/* Called by the final INSN scan whenever we see a var location.  We
+   use it to drop labels in the right places, and throw the location in
+   our lookup table.  */
+
+static void
+dwarf2out_var_location (rtx loc_note)
+{
+  char loclabel[MAX_ARTIFICIAL_LABEL_BYTES];
+  struct var_loc_node *newloc;
+  rtx prev_insn;
+  static rtx last_insn;
+  static const char *last_label;
+  tree decl;
+
+  if (!DECL_P (NOTE_VAR_LOCATION_DECL (loc_note)))
+    return;
+  prev_insn = PREV_INSN (loc_note);
+
+  newloc = GGC_CNEW (struct var_loc_node);
+  /* If the insn we processed last time is the previous insn
+     and it is also a var location note, use the label we emitted
+     last time.  */
+  if (last_insn != NULL_RTX
+      && last_insn == prev_insn
+      && NOTE_P (prev_insn)
+      && NOTE_KIND (prev_insn) == NOTE_INSN_VAR_LOCATION)
+    {
+      newloc->label = last_label;
+    }
+  else
+    {
+      ASM_GENERATE_INTERNAL_LABEL (loclabel, "LVL", loclabel_num);
+      ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LVL", loclabel_num);
+      loclabel_num++;
+      newloc->label = ggc_strdup (loclabel);
+    }
+  newloc->var_loc_note = loc_note;
+  newloc->next = NULL;
+
+  if (cfun && in_cold_section_p)
+    newloc->section_label = crtl->subsections.cold_section_label;
+  else
+    newloc->section_label = text_section_label;
+
+  last_insn = loc_note;
+  last_label = newloc->label;
+  decl = NOTE_VAR_LOCATION_DECL (loc_note);
+  add_var_loc_to_decl (decl, newloc);
+}
+
+/* We need to reset the locations at the beginning of each
+   function. We can't do this in the end_function hook, because the
+   declarations that use the locations won't have been output when
+   that hook is called.  Also compute have_multiple_function_sections here.  */
+
+static void
+dwarf2out_begin_function (tree fun)
+{
+  htab_empty (decl_loc_table);
+
+  if (function_section (fun) != text_section)
+    have_multiple_function_sections = true;
+
+  dwarf2out_note_section_used ();
+}
+
+/* Output a label to mark the beginning of a source code line entry
+   and record information relating to this source line, in
+   'line_info_table' for later output of the .debug_line section.  */
+
+static void
+dwarf2out_source_line (unsigned int line, const char *filename)
+{
+  if (debug_info_level >= DINFO_LEVEL_NORMAL
+      && line != 0)
+    {
+      int file_num = maybe_emit_file (lookup_filename (filename));
+
+      switch_to_section (current_function_section ());
+
+      /* If requested, emit something human-readable.  */
+      if (flag_debug_asm)
+	fprintf (asm_out_file, "\t%s %s:%d\n", ASM_COMMENT_START,
+		 filename, line);
+
+      if (DWARF2_ASM_LINE_DEBUG_INFO)
+	{
+	  /* Emit the .loc directive understood by GNU as.  */
+	  fprintf (asm_out_file, "\t.loc %d %d 0\n", file_num, line);
+
+	  /* Indicate that line number info exists.  */
+	  line_info_table_in_use++;
+	}
+      else if (function_section (current_function_decl) != text_section)
+	{
+	  dw_separate_line_info_ref line_info;
+	  targetm.asm_out.internal_label (asm_out_file,
+					  SEPARATE_LINE_CODE_LABEL,
+					  separate_line_info_table_in_use);
+
+	  /* Expand the line info table if necessary.  */
+	  if (separate_line_info_table_in_use
+	      == separate_line_info_table_allocated)
+	    {
+	      separate_line_info_table_allocated += LINE_INFO_TABLE_INCREMENT;
+	      separate_line_info_table
+		= GGC_RESIZEVEC (dw_separate_line_info_entry,
+				 separate_line_info_table,
+				 separate_line_info_table_allocated);
+	      memset (separate_line_info_table
+		       + separate_line_info_table_in_use,
+		      0,
+		      (LINE_INFO_TABLE_INCREMENT
+		       * sizeof (dw_separate_line_info_entry)));
+	    }
+
+	  /* Add the new entry at the end of the line_info_table.  */
+	  line_info
+	    = &separate_line_info_table[separate_line_info_table_in_use++];
+	  line_info->dw_file_num = file_num;
+	  line_info->dw_line_num = line;
+	  line_info->function = current_function_funcdef_no;
+	}
+      else
+	{
+	  dw_line_info_ref line_info;
+
+	  targetm.asm_out.internal_label (asm_out_file, LINE_CODE_LABEL,
+				     line_info_table_in_use);
+
+	  /* Expand the line info table if necessary.  */
+	  if (line_info_table_in_use == line_info_table_allocated)
+	    {
+	      line_info_table_allocated += LINE_INFO_TABLE_INCREMENT;
+	      line_info_table
+		= GGC_RESIZEVEC (dw_line_info_entry, line_info_table,
+				 line_info_table_allocated);
+	      memset (line_info_table + line_info_table_in_use, 0,
+		      LINE_INFO_TABLE_INCREMENT * sizeof (dw_line_info_entry));
+	    }
+
+	  /* Add the new entry at the end of the line_info_table.  */
+	  line_info = &line_info_table[line_info_table_in_use++];
+	  line_info->dw_file_num = file_num;
+	  line_info->dw_line_num = line;
+	}
+    }
+}
+
+/* Record the beginning of a new source file.  */
+
+static void
+dwarf2out_start_source_file (unsigned int lineno, const char *filename)
+{
+  if (flag_eliminate_dwarf2_dups)
+    {
+      /* Record the beginning of the file for break_out_includes.  */
+      dw_die_ref bincl_die;
+
+      bincl_die = new_die (DW_TAG_GNU_BINCL, comp_unit_die, NULL);
+      add_AT_string (bincl_die, DW_AT_name, remap_debug_filename (filename));
+    }
+
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      int file_num = maybe_emit_file (lookup_filename (filename));
+
+      switch_to_section (debug_macinfo_section);
+      dw2_asm_output_data (1, DW_MACINFO_start_file, "Start new file");
+      dw2_asm_output_data_uleb128 (lineno, "Included from line number %d",
+				   lineno);
+
+      dw2_asm_output_data_uleb128 (file_num, "file %s", filename);
+    }
+}
+
+/* Record the end of a source file.  */
+
+static void
+dwarf2out_end_source_file (unsigned int lineno ATTRIBUTE_UNUSED)
+{
+  if (flag_eliminate_dwarf2_dups)
+    /* Record the end of the file for break_out_includes.  */
+    new_die (DW_TAG_GNU_EINCL, comp_unit_die, NULL);
+
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      switch_to_section (debug_macinfo_section);
+      dw2_asm_output_data (1, DW_MACINFO_end_file, "End file");
+    }
+}
+
+/* Called from debug_define in toplev.c.  The `buffer' parameter contains
+   the tail part of the directive line, i.e. the part which is past the
+   initial whitespace, #, whitespace, directive-name, whitespace part.  */
+
+static void
+dwarf2out_define (unsigned int lineno ATTRIBUTE_UNUSED,
+		  const char *buffer ATTRIBUTE_UNUSED)
+{
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      switch_to_section (debug_macinfo_section);
+      dw2_asm_output_data (1, DW_MACINFO_define, "Define macro");
+      dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno);
+      dw2_asm_output_nstring (buffer, -1, "The macro");
+    }
+}
+
+/* Called from debug_undef in toplev.c.  The `buffer' parameter contains
+   the tail part of the directive line, i.e. the part which is past the
+   initial whitespace, #, whitespace, directive-name, whitespace part.  */
+
+static void
+dwarf2out_undef (unsigned int lineno ATTRIBUTE_UNUSED,
+		 const char *buffer ATTRIBUTE_UNUSED)
+{
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      switch_to_section (debug_macinfo_section);
+      dw2_asm_output_data (1, DW_MACINFO_undef, "Undefine macro");
+      dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno);
+      dw2_asm_output_nstring (buffer, -1, "The macro");
+    }
+}
+
+/* Set up for Dwarf output at the start of compilation.  */
+
+static void
+dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
+{
+  /* Allocate the file_table.  */
+  file_table = htab_create_ggc (50, file_table_hash,
+				file_table_eq, NULL);
+
+  /* Allocate the decl_die_table.  */
+  decl_die_table = htab_create_ggc (10, decl_die_table_hash,
+				    decl_die_table_eq, NULL);
+
+  /* Allocate the decl_loc_table.  */
+  decl_loc_table = htab_create_ggc (10, decl_loc_table_hash,
+				    decl_loc_table_eq, NULL);
+
+  /* Allocate the initial hunk of the decl_scope_table.  */
+  decl_scope_table = VEC_alloc (tree, gc, 256);
+
+  /* Allocate the initial hunk of the abbrev_die_table.  */
+  abbrev_die_table = GGC_CNEWVEC (dw_die_ref, ABBREV_DIE_TABLE_INCREMENT);
+  abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT;
+  /* Zero-th entry is allocated, but unused.  */
+  abbrev_die_table_in_use = 1;
+
+  /* Allocate the initial hunk of the line_info_table.  */
+  line_info_table = GGC_CNEWVEC (dw_line_info_entry, LINE_INFO_TABLE_INCREMENT);
+  line_info_table_allocated = LINE_INFO_TABLE_INCREMENT;
+
+  /* Zero-th entry is allocated, but unused.  */
+  line_info_table_in_use = 1;
+
+  /* Allocate the pubtypes and pubnames vectors.  */
+  pubname_table = VEC_alloc (pubname_entry, gc, 32);
+  pubtype_table = VEC_alloc (pubname_entry, gc, 32);
+
+  /* Generate the initial DIE for the .debug section.  Note that the (string)
+     value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE
+     will (typically) be a relative pathname and that this pathname should be
+     taken as being relative to the directory from which the compiler was
+     invoked when the given (base) source file was compiled.  We will fill
+     in this value in dwarf2out_finish.  */
+  comp_unit_die = gen_compile_unit_die (NULL);
+
+  incomplete_types = VEC_alloc (tree, gc, 64);
+
+  used_rtx_array = VEC_alloc (rtx, gc, 32);
+
+  debug_info_section = get_section (DEBUG_INFO_SECTION,
+				    SECTION_DEBUG, NULL);
+  debug_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
+				      SECTION_DEBUG, NULL);
+  debug_aranges_section = get_section (DEBUG_ARANGES_SECTION,
+				       SECTION_DEBUG, NULL);
+  debug_macinfo_section = get_section (DEBUG_MACINFO_SECTION,
+				       SECTION_DEBUG, NULL);
+  debug_line_section = get_section (DEBUG_LINE_SECTION,
+				    SECTION_DEBUG, NULL);
+  debug_loc_section = get_section (DEBUG_LOC_SECTION,
+				   SECTION_DEBUG, NULL);
+  debug_pubnames_section = get_section (DEBUG_PUBNAMES_SECTION,
+					SECTION_DEBUG, NULL);
+#ifdef DEBUG_PUBTYPES_SECTION
+  debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION,
+					SECTION_DEBUG, NULL);
+#endif
+  debug_str_section = get_section (DEBUG_STR_SECTION,
+				   DEBUG_STR_SECTION_FLAGS, NULL);
+  debug_ranges_section = get_section (DEBUG_RANGES_SECTION,
+				      SECTION_DEBUG, NULL);
+  debug_frame_section = get_section (DEBUG_FRAME_SECTION,
+				     SECTION_DEBUG, NULL);
+
+  ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (abbrev_section_label,
+			       DEBUG_ABBREV_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label,
+			       COLD_TEXT_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0);
+
+  ASM_GENERATE_INTERNAL_LABEL (debug_info_section_label,
+			       DEBUG_INFO_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (debug_line_section_label,
+			       DEBUG_LINE_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (ranges_section_label,
+			       DEBUG_RANGES_SECTION_LABEL, 0);
+  switch_to_section (debug_abbrev_section);
+  ASM_OUTPUT_LABEL (asm_out_file, abbrev_section_label);
+  switch_to_section (debug_info_section);
+  ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label);
+  switch_to_section (debug_line_section);
+  ASM_OUTPUT_LABEL (asm_out_file, debug_line_section_label);
+
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      switch_to_section (debug_macinfo_section);
+      ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label,
+				   DEBUG_MACINFO_SECTION_LABEL, 0);
+      ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label);
+    }
+
+  switch_to_section (text_section);
+  ASM_OUTPUT_LABEL (asm_out_file, text_section_label);
+  if (flag_reorder_blocks_and_partition)
+    {
+      cold_text_section = unlikely_text_section ();
+      switch_to_section (cold_text_section);
+      ASM_OUTPUT_LABEL (asm_out_file, cold_text_section_label);
+    }
+}
+
+/* A helper function for dwarf2out_finish called through
+   ht_forall.  Emit one queued .debug_str string.  */
+
+static int
+output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED)
+{
+  struct indirect_string_node *node = (struct indirect_string_node *) *h;
+
+  if (node->form == DW_FORM_strp)
+    {
+      switch_to_section (debug_str_section);
+      ASM_OUTPUT_LABEL (asm_out_file, node->label);
+      assemble_string (node->str, strlen (node->str) + 1);
+    }
+
+  return 1;
+}
+
+#if ENABLE_ASSERT_CHECKING
+/* Verify that all marks are clear.  */
+
+static void
+verify_marks_clear (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  gcc_assert (! die->die_mark);
+  FOR_EACH_CHILD (die, c, verify_marks_clear (c));
+}
+#endif /* ENABLE_ASSERT_CHECKING */
+
+/* Clear the marks for a die and its children.
+   Be cool if the mark isn't set.  */
+
+static void
+prune_unmark_dies (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  if (die->die_mark)
+    die->die_mark = 0;
+  FOR_EACH_CHILD (die, c, prune_unmark_dies (c));
+}
+
+/* Given DIE that we're marking as used, find any other dies
+   it references as attributes and mark them as used.  */
+
+static void
+prune_unused_types_walk_attribs (dw_die_ref die)
+{
+  dw_attr_ref a;
+  unsigned ix;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    {
+      if (a->dw_attr_val.val_class == dw_val_class_die_ref)
+	{
+	  /* A reference to another DIE.
+	     Make sure that it will get emitted.  */
+	  prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1);
+	}
+      /* Set the string's refcount to 0 so that prune_unused_types_mark
+	 accounts properly for it.  */
+      if (AT_class (a) == dw_val_class_str)
+	a->dw_attr_val.v.val_str->refcount = 0;
+    }
+}
+
+
+/* Mark DIE as being used.  If DOKIDS is true, then walk down
+   to DIE's children.  */
+
+static void
+prune_unused_types_mark (dw_die_ref die, int dokids)
+{
+  dw_die_ref c;
+
+  if (die->die_mark == 0)
+    {
+      /* We haven't done this node yet.  Mark it as used.  */
+      die->die_mark = 1;
+
+      /* We also have to mark its parents as used.
+	 (But we don't want to mark our parents' kids due to this.)  */
+      if (die->die_parent)
+	prune_unused_types_mark (die->die_parent, 0);
+
+      /* Mark any referenced nodes.  */
+      prune_unused_types_walk_attribs (die);
+
+      /* If this node is a specification,
+	 also mark the definition, if it exists.  */
+      if (get_AT_flag (die, DW_AT_declaration) && die->die_definition)
+	prune_unused_types_mark (die->die_definition, 1);
+    }
+
+  if (dokids && die->die_mark != 2)
+    {
+      /* We need to walk the children, but haven't done so yet.
+	 Remember that we've walked the kids.  */
+      die->die_mark = 2;
+
+      /* If this is an array type, we need to make sure our
+	 kids get marked, even if they're types.  */
+      if (die->die_tag == DW_TAG_array_type)
+	FOR_EACH_CHILD (die, c, prune_unused_types_mark (c, 1));
+      else
+	FOR_EACH_CHILD (die, c, prune_unused_types_walk (c));
+    }
+}
+
+/* For local classes, look if any static member functions were emitted
+   and if so, mark them.  */
+
+static void
+prune_unused_types_walk_local_classes (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  if (die->die_mark == 2)
+    return;
+
+  switch (die->die_tag)
+    {
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+    case DW_TAG_class_type:
+      break;
+
+    case DW_TAG_subprogram:
+      if (!get_AT_flag (die, DW_AT_declaration)
+	  || die->die_definition != NULL)
+	prune_unused_types_mark (die, 1);
+      return;
+
+    default:
+      return;
+    }
+
+  /* Mark children.  */
+  FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c));
+}
+
+/* Walk the tree DIE and mark types that we actually use.  */
+
+static void
+prune_unused_types_walk (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  /* Don't do anything if this node is already marked and
+     children have been marked as well.  */
+  if (die->die_mark == 2)
+    return;
+
+  switch (die->die_tag)
+    {
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+    case DW_TAG_class_type:
+      if (die->die_perennial_p)
+	break;
+
+      for (c = die->die_parent; c; c = c->die_parent)
+	if (c->die_tag == DW_TAG_subprogram)
+	  break;
+
+      /* Finding used static member functions inside of classes
+	 is needed just for local classes, because for other classes
+	 static member function DIEs with DW_AT_specification
+	 are emitted outside of the DW_TAG_*_type.  If we ever change
+	 it, we'd need to call this even for non-local classes.  */
+      if (c)
+	prune_unused_types_walk_local_classes (die);
+
+      /* It's a type node --- don't mark it.  */
+      return;
+
+    case DW_TAG_const_type:
+    case DW_TAG_packed_type:
+    case DW_TAG_pointer_type:
+    case DW_TAG_reference_type:
+    case DW_TAG_volatile_type:
+    case DW_TAG_typedef:
+    case DW_TAG_array_type:
+    case DW_TAG_interface_type:
+    case DW_TAG_friend:
+    case DW_TAG_variant_part:
+    case DW_TAG_enumeration_type:
+    case DW_TAG_subroutine_type:
+    case DW_TAG_string_type:
+    case DW_TAG_set_type:
+    case DW_TAG_subrange_type:
+    case DW_TAG_ptr_to_member_type:
+    case DW_TAG_file_type:
+      if (die->die_perennial_p)
+	break;
+
+      /* It's a type node --- don't mark it.  */
+      return;
+
+    default:
+      /* Mark everything else.  */
+      break;
+  }
+
+  if (die->die_mark == 0)
+    {
+      die->die_mark = 1;
+
+      /* Now, mark any dies referenced from here.  */
+      prune_unused_types_walk_attribs (die);
+    }
+
+  die->die_mark = 2;
+
+  /* Mark children.  */
+  FOR_EACH_CHILD (die, c, prune_unused_types_walk (c));
+}
+
+/* Increment the string counts on strings referred to from DIE's
+   attributes.  */
+
+static void
+prune_unused_types_update_strings (dw_die_ref die)
+{
+  dw_attr_ref a;
+  unsigned ix;
+
+  for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+    if (AT_class (a) == dw_val_class_str)
+      {
+	struct indirect_string_node *s = a->dw_attr_val.v.val_str;
+	s->refcount++;
+	/* Avoid unnecessarily putting strings that are used less than
+	   twice in the hash table.  */
+	if (s->refcount
+	    == ((DEBUG_STR_SECTION_FLAGS & SECTION_MERGE) ? 1 : 2))
+	  {
+	    void ** slot;
+	    slot = htab_find_slot_with_hash (debug_str_hash, s->str,
+					     htab_hash_string (s->str),
+					     INSERT);
+	    gcc_assert (*slot == NULL);
+	    *slot = s;
+	  }
+      }
+}
+
+/* Remove from the tree DIE any dies that aren't marked.  */
+
+static void
+prune_unused_types_prune (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  gcc_assert (die->die_mark);
+  prune_unused_types_update_strings (die);
+
+  if (! die->die_child)
+    return;
+
+  c = die->die_child;
+  do {
+    dw_die_ref prev = c;
+    for (c = c->die_sib; ! c->die_mark; c = c->die_sib)
+      if (c == die->die_child)
+	{
+	  /* No marked children between 'prev' and the end of the list.  */
+	  if (prev == c)
+	    /* No marked children at all.  */
+	    die->die_child = NULL;
+	  else
+	    {
+	      prev->die_sib = c->die_sib;
+	      die->die_child = prev;
+	    }
+	  return;
+	}
+
+    if (c != prev->die_sib)
+      prev->die_sib = c;
+    prune_unused_types_prune (c);
+  } while (c != die->die_child);
+}
+
+
+/* Remove dies representing declarations that we never use.  */
+
+static void
+prune_unused_types (void)
+{
+  unsigned int i;
+  limbo_die_node *node;
+  pubname_ref pub;
+
+#if ENABLE_ASSERT_CHECKING
+  /* All the marks should already be clear.  */
+  verify_marks_clear (comp_unit_die);
+  for (node = limbo_die_list; node; node = node->next)
+    verify_marks_clear (node->die);
+#endif /* ENABLE_ASSERT_CHECKING */
+
+  /* Set the mark on nodes that are actually used.  */
+  prune_unused_types_walk (comp_unit_die);
+  for (node = limbo_die_list; node; node = node->next)
+    prune_unused_types_walk (node->die);
+
+  /* Also set the mark on nodes referenced from the
+     pubname_table or arange_table.  */
+  for (i = 0; VEC_iterate (pubname_entry, pubname_table, i, pub); i++)
+    prune_unused_types_mark (pub->die, 1);
+  for (i = 0; i < arange_table_in_use; i++)
+    prune_unused_types_mark (arange_table[i], 1);
+
+  /* Get rid of nodes that aren't marked; and update the string counts.  */
+  if (debug_str_hash)
+    htab_empty (debug_str_hash);
+  prune_unused_types_prune (comp_unit_die);
+  for (node = limbo_die_list; node; node = node->next)
+    prune_unused_types_prune (node->die);
+
+  /* Leave the marks clear.  */
+  prune_unmark_dies (comp_unit_die);
+  for (node = limbo_die_list; node; node = node->next)
+    prune_unmark_dies (node->die);
+}
+
+/* Set the parameter to true if there are any relative pathnames in
+   the file table.  */
+static int
+file_table_relative_p (void ** slot, void *param)
+{
+  bool *p = (bool *) param;
+  struct dwarf_file_data *d = (struct dwarf_file_data *) *slot;
+  if (!IS_ABSOLUTE_PATH (d->filename))
+    {
+      *p = true;
+      return 0;
+    }
+  return 1;
+}
+
+/* Output stuff that dwarf requires at the end of every file,
+   and generate the DWARF-2 debugging info.  */
+
+static void
+dwarf2out_finish (const char *filename)
+{
+  limbo_die_node *node, *next_node;
+  dw_die_ref die = 0;
+  unsigned int i;
+
+  /* Add the name for the main input file now.  We delayed this from
+     dwarf2out_init to avoid complications with PCH.  */
+  add_name_attribute (comp_unit_die, remap_debug_filename (filename));
+  if (!IS_ABSOLUTE_PATH (filename))
+    add_comp_dir_attribute (comp_unit_die);
+  else if (get_AT (comp_unit_die, DW_AT_comp_dir) == NULL)
+    {
+      bool p = false;
+      htab_traverse (file_table, file_table_relative_p, &p);
+      if (p)
+	add_comp_dir_attribute (comp_unit_die);
+    }
+
+  for (i = 0; i < VEC_length (deferred_locations, deferred_locations_list); i++)
+    {
+      add_location_or_const_value_attribute (
+        VEC_index (deferred_locations, deferred_locations_list, i)->die,
+        VEC_index (deferred_locations, deferred_locations_list, i)->variable,
+	DW_AT_location);
+    }
+
+  /* Traverse the limbo die list, and add parent/child links.  The only
+     dies without parents that should be here are concrete instances of
+     inline functions, and the comp_unit_die.  We can ignore the comp_unit_die.
+     For concrete instances, we can get the parent die from the abstract
+     instance.  */
+  for (node = limbo_die_list; node; node = next_node)
+    {
+      next_node = node->next;
+      die = node->die;
+
+      if (die->die_parent == NULL)
+	{
+	  dw_die_ref origin = get_AT_ref (die, DW_AT_abstract_origin);
+
+	  if (origin)
+	    add_child_die (origin->die_parent, die);
+	  else if (die == comp_unit_die)
+	    ;
+	  else if (errorcount > 0 || sorrycount > 0)
+	    /* It's OK to be confused by errors in the input.  */
+	    add_child_die (comp_unit_die, die);
+	  else
+	    {
+	      /* In certain situations, the lexical block containing a
+		 nested function can be optimized away, which results
+		 in the nested function die being orphaned.  Likewise
+		 with the return type of that nested function.  Force
+		 this to be a child of the containing function.
+
+		 It may happen that even the containing function got fully
+		 inlined and optimized out.  In that case we are lost and
+		 assign the empty child.  This should not be big issue as
+		 the function is likely unreachable too.  */
+	      tree context = NULL_TREE;
+
+	      gcc_assert (node->created_for);
+
+	      if (DECL_P (node->created_for))
+		context = DECL_CONTEXT (node->created_for);
+	      else if (TYPE_P (node->created_for))
+		context = TYPE_CONTEXT (node->created_for);
+
+	      gcc_assert (context
+			  && (TREE_CODE (context) == FUNCTION_DECL
+			      || TREE_CODE (context) == NAMESPACE_DECL));
+
+	      origin = lookup_decl_die (context);
+	      if (origin)
+	        add_child_die (origin, die);
+	      else
+	        add_child_die (comp_unit_die, die);
+	    }
+	}
+    }
+
+  limbo_die_list = NULL;
+
+  /* Walk through the list of incomplete types again, trying once more to
+     emit full debugging info for them.  */
+  retry_incomplete_types ();
+
+  if (flag_eliminate_unused_debug_types)
+    prune_unused_types ();
+
+  /* Generate separate CUs for each of the include files we've seen.
+     They will go into limbo_die_list.  */
+  if (flag_eliminate_dwarf2_dups)
+    break_out_includes (comp_unit_die);
+
+  /* Traverse the DIE's and add add sibling attributes to those DIE's
+     that have children.  */
+  add_sibling_attributes (comp_unit_die);
+  for (node = limbo_die_list; node; node = node->next)
+    add_sibling_attributes (node->die);
+
+  /* Output a terminator label for the .text section.  */
+  switch_to_section (text_section);
+  targetm.asm_out.internal_label (asm_out_file, TEXT_END_LABEL, 0);
+  if (flag_reorder_blocks_and_partition)
+    {
+      switch_to_section (unlikely_text_section ());
+      targetm.asm_out.internal_label (asm_out_file, COLD_END_LABEL, 0);
+    }
+
+  /* We can only use the low/high_pc attributes if all of the code was
+     in .text.  */
+  if (!have_multiple_function_sections)
+    {
+      add_AT_lbl_id (comp_unit_die, DW_AT_low_pc, text_section_label);
+      add_AT_lbl_id (comp_unit_die, DW_AT_high_pc, text_end_label);
+    }
+
+  else
+    {
+      unsigned fde_idx = 0;
+
+      /* We need to give .debug_loc and .debug_ranges an appropriate
+	 "base address".  Use zero so that these addresses become
+	 absolute.  Historically, we've emitted the unexpected
+	 DW_AT_entry_pc instead of DW_AT_low_pc for this purpose.
+	 Emit both to give time for other tools to adapt.  */
+      add_AT_addr (comp_unit_die, DW_AT_low_pc, const0_rtx);
+      add_AT_addr (comp_unit_die, DW_AT_entry_pc, const0_rtx);
+
+      add_AT_range_list (comp_unit_die, DW_AT_ranges,
+			 add_ranges_by_labels (text_section_label,
+					       text_end_label));
+      if (flag_reorder_blocks_and_partition)
+	add_ranges_by_labels (cold_text_section_label,
+			      cold_end_label);
+
+      for (fde_idx = 0; fde_idx < fde_table_in_use; fde_idx++)
+	{
+	  dw_fde_ref fde = &fde_table[fde_idx];
+
+	  if (fde->dw_fde_switched_sections)
+	    {
+	      add_ranges_by_labels (fde->dw_fde_hot_section_label,
+				    fde->dw_fde_hot_section_end_label);
+	      add_ranges_by_labels (fde->dw_fde_unlikely_section_label,
+				    fde->dw_fde_unlikely_section_end_label);
+	    }
+	  else
+	    add_ranges_by_labels (fde->dw_fde_begin,
+				  fde->dw_fde_end);
+	}
+
+      add_ranges (NULL);
+    }
+
+  /* Output location list section if necessary.  */
+  if (have_location_lists)
+    {
+      /* Output the location lists info.  */
+      switch_to_section (debug_loc_section);
+      ASM_GENERATE_INTERNAL_LABEL (loc_section_label,
+				   DEBUG_LOC_SECTION_LABEL, 0);
+      ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
+      output_location_lists (die);
+    }
+
+  if (debug_info_level >= DINFO_LEVEL_NORMAL)
+    add_AT_lineptr (comp_unit_die, DW_AT_stmt_list,
+		    debug_line_section_label);
+
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    add_AT_macptr (comp_unit_die, DW_AT_macro_info, macinfo_section_label);
+
+  /* Output all of the compilation units.  We put the main one last so that
+     the offsets are available to output_pubnames.  */
+  for (node = limbo_die_list; node; node = node->next)
+    output_comp_unit (node->die, 0);
+
+  /* Output the main compilation unit if non-empty or if .debug_macinfo
+     has been emitted.  */
+  output_comp_unit (comp_unit_die, debug_info_level >= DINFO_LEVEL_VERBOSE);
+
+  /* Output the abbreviation table.  */
+  switch_to_section (debug_abbrev_section);
+  output_abbrev_section ();
+
+  /* Output public names table if necessary.  */
+  if (!VEC_empty (pubname_entry, pubname_table))
+    {
+      switch_to_section (debug_pubnames_section);
+      output_pubnames (pubname_table);
+    }
+
+#ifdef DEBUG_PUBTYPES_SECTION
+  /* Output public types table if necessary.  */
+  if (!VEC_empty (pubname_entry, pubtype_table))
+    {
+      switch_to_section (debug_pubtypes_section);
+      output_pubnames (pubtype_table);
+    }
+#endif
+
+  /* Output the address range information.  We only put functions in the arange
+     table, so don't write it out if we don't have any.  */
+  if (fde_table_in_use)
+    {
+      switch_to_section (debug_aranges_section);
+      output_aranges ();
+    }
+
+  /* Output ranges section if necessary.  */
+  if (ranges_table_in_use)
+    {
+      switch_to_section (debug_ranges_section);
+      ASM_OUTPUT_LABEL (asm_out_file, ranges_section_label);
+      output_ranges ();
+    }
+
+  /* Output the source line correspondence table.  We must do this
+     even if there is no line information.  Otherwise, on an empty
+     translation unit, we will generate a present, but empty,
+     .debug_info section.  IRIX 6.5 `nm' will then complain when
+     examining the file.  This is done late so that any filenames
+     used by the debug_info section are marked as 'used'.  */
+  if (! DWARF2_ASM_LINE_DEBUG_INFO)
+    {
+      switch_to_section (debug_line_section);
+      output_line_info ();
+    }
+
+  /* Have to end the macro section.  */
+  if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+    {
+      switch_to_section (debug_macinfo_section);
+      dw2_asm_output_data (1, 0, "End compilation unit");
+    }
+
+  /* If we emitted any DW_FORM_strp form attribute, output the string
+     table too.  */
+  if (debug_str_hash)
+    htab_traverse (debug_str_hash, output_indirect_string, NULL);
+}
+#else
+
+/* This should never be used, but its address is needed for comparisons.  */
+const struct gcc_debug_hooks dwarf2_debug_hooks;
+
+#endif /* DWARF2_DEBUGGING_INFO */
+
+#include "gt-dwarf2out.h"