Initial revision
authorRichard Stallman <rms@gnu.org>
Sat, 15 Feb 1992 21:24:33 +0000 (21:24 +0000)
committerRichard Stallman <rms@gnu.org>
Sat, 15 Feb 1992 21:24:33 +0000 (21:24 +0000)
From-SVN: r327

gcc/dbxout.c [new file with mode: 0644]

diff --git a/gcc/dbxout.c b/gcc/dbxout.c
new file mode 100644 (file)
index 0000000..feff991
--- /dev/null
@@ -0,0 +1,2160 @@
+/* Output dbx-format symbol table information from GNU compiler.
+   Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+/* Output dbx-format symbol table data.
+   This consists of many symbol table entries, each of them
+   a .stabs assembler pseudo-op with four operands:
+   a "name" which is really a description of one symbol and its type,
+   a "code", which is a symbol defined in stab.h whose name starts with N_,
+   an unused operand always 0,
+   and a "value" which is an address or an offset.
+   The name is enclosed in doublequote characters.
+
+   Each function, variable, typedef, and structure tag
+   has a symbol table entry to define it.
+   The beginning and end of each level of name scoping within
+   a function are also marked by special symbol table entries.
+
+   The "name" consists of the symbol name, a colon, a kind-of-symbol letter,
+   and a data type number.  The data type number may be followed by
+   "=" and a type definition; normally this will happen the first time
+   the type number is mentioned.  The type definition may refer to
+   other types by number, and those type numbers may be followed
+   by "=" and nested definitions.
+
+   This can make the "name" quite long.
+   When a name is more than 80 characters, we split the .stabs pseudo-op
+   into two .stabs pseudo-ops, both sharing the same "code" and "value".
+   The first one is marked as continued with a double-backslash at the
+   end of its "name".
+
+   The kind-of-symbol letter distinguished function names from global
+   variables from file-scope variables from parameters from auto
+   variables in memory from typedef names from register variables.
+   See `dbxout_symbol'.
+
+   The "code" is mostly redundant with the kind-of-symbol letter
+   that goes in the "name", but not entirely: for symbols located
+   in static storage, the "code" says which segment the address is in,
+   which controls how it is relocated.
+
+   The "value" for a symbol in static storage
+   is the core address of the symbol (actually, the assembler
+   label for the symbol).  For a symbol located in a stack slot
+   it is the stack offset; for one in a register, the register number.
+   For a typedef symbol, it is zero.
+
+   If DEBUG_SYMS_TEXT is defined, all debugging symbols must be
+   output while in the text section.
+
+   For more on data type definitions, see `dbxout_type'.  */
+
+/* Include these first, because they may define MIN and MAX.  */
+#include <stdio.h>
+#include <sys/param.h>
+#include <errno.h>
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "reload.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifndef ASM_STABS_OP
+#define ASM_STABS_OP ".stabs"
+#endif
+
+#ifndef ASM_STABN_OP
+#define ASM_STABN_OP ".stabn"
+#endif
+
+/* Nonzero means if the type has methods, only output debugging
+   information if methods are actually written to the asm file.  */
+
+static int flag_minimal_debug = 1;
+
+/* Nonzero if we have actually used any of the GDB extensions
+   to the debugging format.  The idea is that we use them for the
+   first time only if there's a strong reason, but once we have done that,
+   we use them whenever convenient.  */
+
+static int have_used_extensions = 0;
+
+/* Virtually every UN*X system now in common use (except for pre-4.3-tahoe
+   BSD systems) now provides getcwd as called for by POSIX.  Allow for
+   the few exceptions to the general rule here.  */
+
+#if !(defined (USG) || defined (VMS))
+extern char *getwd ();
+#define getcwd(buf,len) getwd(buf)
+#define GUESSPATHLEN (MAXPATHLEN + 1)
+#else /* (defined (USG) || defined (VMS)) */
+extern char *getcwd ();
+/* We actually use this as a starting point, not a limit.  */
+#define GUESSPATHLEN 100
+#endif /* (defined (USG) || defined (VMS)) */
+
+/* Typical USG systems don't have stab.h, and they also have
+   no use for DBX-format debugging info.  */
+
+#ifdef DBX_DEBUGGING_INFO
+
+#ifdef DEBUG_SYMS_TEXT
+#define FORCE_TEXT text_section ();
+#else
+#define FORCE_TEXT
+#endif
+
+#ifdef USG
+#include "gstab.h"  /* If doing DBX on sysV, use our own stab.h.  */
+#else
+#include <stab.h>  /* On BSD, use the system's stab.h.  */
+
+/* This is a GNU extension we need to reference in this file.  */
+#ifndef N_CATCH
+#define N_CATCH 0x54
+#endif
+#endif /* not USG */
+
+#ifdef __GNU_STAB__
+#define STAB_CODE_TYPE enum __stab_debug_code
+#else
+#define STAB_CODE_TYPE int
+#endif
+
+/* 1 if PARM is passed to this function in memory.  */
+
+#define PARM_PASSED_IN_MEMORY(PARM) \
+ (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM)
+
+/* A C expression for the integer offset value of an automatic variable
+   (N_LSYM) having address X (an RTX).  */
+#ifndef DEBUGGER_AUTO_OFFSET
+#define DEBUGGER_AUTO_OFFSET(X) \
+  (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0)
+#endif
+
+/* A C expression for the integer offset value of an argument (N_PSYM)
+   having address X (an RTX).  The nominal offset is OFFSET.  */
+#ifndef DEBUGGER_ARG_OFFSET
+#define DEBUGGER_ARG_OFFSET(OFFSET, X) (OFFSET)
+#endif
+
+/* Stream for writing to assembler file.  */
+
+static FILE *asmfile;
+
+/* Last source file name mentioned in a NOTE insn.  */
+
+static char *lastfile;
+
+/* Current working directory.  */
+
+static char *cwd;
+static enum {not_gotten, gotten, error_getting} cwd_status = not_gotten;
+
+enum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED};
+
+/* Vector recording the status of describing C data types.
+   When we first notice a data type (a tree node),
+   we assign it a number using next_type_number.
+   That is its index in this vector.
+   The vector element says whether we have yet output
+   the definition of the type.  TYPE_XREF says we have
+   output it as a cross-reference only.  */
+
+enum typestatus *typevec;
+
+/* Number of elements of space allocated in `typevec'.  */
+
+static int typevec_len;
+
+/* In dbx output, each type gets a unique number.
+   This is the number for the next type output.
+   The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field.  */
+
+static int next_type_number;
+
+/* In dbx output, we must assign symbol-blocks id numbers
+   in the order in which their beginnings are encountered.
+   We output debugging info that refers to the beginning and
+   end of the ranges of code in each block
+   with assembler labels LBBn and LBEn, where n is the block number.
+   The labels are generated in final, which assigns numbers to the
+   blocks in the same way.  */
+
+static int next_block_number;
+
+/* These variables are for dbxout_symbol to communicate to
+   dbxout_finish_symbol.
+   current_sym_code is the symbol-type-code, a symbol N_... define in stab.h.
+   current_sym_value and current_sym_addr are two ways to address the
+   value to store in the symtab entry.
+   current_sym_addr if nonzero represents the value as an rtx.
+   If that is zero, current_sym_value is used.  This is used
+   when the value is an offset (such as for auto variables,
+   register variables and parms).  */
+
+static STAB_CODE_TYPE current_sym_code;
+static int current_sym_value;
+static rtx current_sym_addr;
+
+/* Number of chars of symbol-description generated so far for the
+   current symbol.  Used by CHARS and CONTIN.  */
+
+static int current_sym_nchars;
+
+/* Report having output N chars of the current symbol-description.  */
+
+#define CHARS(N) (current_sym_nchars += (N))
+
+/* Break the current symbol-description, generating a continuation,
+   if it has become long.  */
+
+#ifndef DBX_CONTIN_LENGTH
+#define DBX_CONTIN_LENGTH 80
+#endif
+
+#if DBX_CONTIN_LENGTH > 0
+#define CONTIN  \
+  do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0)
+#else
+#define CONTIN
+#endif
+
+void dbxout_types ();
+void dbxout_args ();
+void dbxout_symbol ();
+static void dbxout_type_name ();
+static void dbxout_type ();
+static void dbxout_typedefs ();
+static void dbxout_prepare_symbol ();
+static void dbxout_finish_symbol ();
+static void dbxout_continue ();
+static void print_int_cst_octal ();
+static void print_octal ();
+\f
+#if 0 /* Not clear we will actually need this.  */
+
+/* Return the absolutized filename for the given relative
+   filename.  Note that if that filename is already absolute, it may
+   still be returned in a modified form because this routine also
+   eliminates redundant slashes and single dots and eliminates double
+   dots to get a shortest possible filename from the given input
+   filename.  The absolutization of relative filenames is made by
+   assuming that the given filename is to be taken as relative to
+   the first argument (cwd) or to the current directory if cwd is
+   NULL.  */
+
+static char *
+abspath (rel_filename)
+     char *rel_filename;
+{
+  /* Setup the current working directory as needed.  */
+  char *abs_buffer
+    = (char *) alloca (strlen (cwd) + strlen (rel_filename) + 1);
+  char *endp = abs_buffer;
+  char *outp, *inp;
+  char *value;
+
+  /* Copy the  filename (possibly preceeded by the current working
+     directory name) into the absolutization buffer.  */
+
+  {
+    char *src_p;
+
+    if (rel_filename[0] != '/')
+      {
+        src_p = cwd;
+        while (*endp++ = *src_p++)
+          continue;
+        *(endp-1) = '/';                       /* overwrite null */
+      }
+    src_p = rel_filename;
+    while (*endp++ = *src_p++)
+      continue;
+    if (endp[-1] == '/')
+      *endp = '\0';
+  }
+
+  /* Now make a copy of abs_buffer into abs_buffer, shortening the
+     filename (by taking out slashes and dots) as we go.  */
+
+  outp = inp = abs_buffer;
+  *outp++ = *inp++;            /* copy first slash */
+  for (;;)
+    {
+      if (!inp[0])
+        break;
+      else if (inp[0] == '/' && outp[-1] == '/')
+        {
+          inp++;
+          continue;
+        }
+      else if (inp[0] == '.' && outp[-1] == '/')
+        {
+          if (!inp[1])
+                  break;
+          else if (inp[1] == '/')
+            {
+                    inp += 2;
+                    continue;
+            }
+          else if ((inp[1] == '.') && (inp[2] == 0 || inp[2] == '/'))
+            {
+                    inp += (inp[2] == '/') ? 3 : 2;
+                    outp -= 2;
+                    while (outp >= abs_buffer && *outp != '/')
+               outp--;
+                    if (outp < abs_buffer)
+                {
+                  /* Catch cases like /.. where we try to backup to a
+                     point above the absolute root of the logical file
+                     system.  */
+
+                 fprintf (stderr, "%s: invalid file name: %s\n",
+                          pname, rel_filename);
+                 exit (1);
+               }
+                    *++outp = '\0';
+                    continue;
+            }
+        }
+      *outp++ = *inp++;
+    }
+
+  /* On exit, make sure that there is a trailing null, and make sure that
+     the last character of the returned string is *not* a slash.  */
+
+  *outp = '\0';
+  if (outp[-1] == '/')
+    *--outp  = '\0';
+
+  /* Make a copy (in the heap) of the stuff left in the absolutization
+     buffer and return a pointer to the copy.  */
+
+  value = (char *) oballoc (strlen (abs_buffer) + 1);
+  strcpy (value, abs_buffer);
+  return value;
+}
+#endif /* 0 */
+\f
+/* At the beginning of compilation, start writing the symbol table.
+   Initialize `typevec' and output the standard data types of C.  */
+
+void
+dbxout_init (asm_file, input_file_name, syms)
+     FILE *asm_file;
+     char *input_file_name;
+     tree syms;
+{
+  char ltext_label_name[100];
+
+  asmfile = asm_file;
+
+  typevec_len = 100;
+  typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]);
+  bzero (typevec, typevec_len * sizeof typevec[0]);
+
+  /* Convert Ltext into the appropriate format for local labels in case
+     the system doesn't insert underscores in front of user generated
+     labels.  */
+  ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
+
+  /* Put the current working directory in an N_SO symbol.  */
+  {
+    int size;
+
+    if (cwd_status == not_gotten)
+      {
+       char *value;
+
+       /* Read the working directory, avoiding arbitrary limit.  */
+       size = GUESSPATHLEN;
+       while (1)
+         {
+           cwd = (char *) xmalloc (size);
+           value = getcwd (cwd, size);
+           if (value != 0 || errno != ERANGE)
+             break;
+           free (cwd);
+           size *= 2;
+         }
+
+       if (value != 0)
+         cwd_status = gotten;
+       else
+         cwd_status = error_getting;
+      }
+
+    if (cwd_status == gotten)
+      {
+#ifdef DBX_OUTPUT_MAIN_SOURCE_DIRECTORY
+       DBX_OUTPUT_MAIN_SOURCE_DIRECTORY (asmfile, cwd);
+#else /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */
+       fprintf (asmfile, "%s \"%s/\",%d,0,0,%s\n", ASM_STABS_OP,
+                cwd, N_SO, &ltext_label_name[1]);
+#endif /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */
+      }
+  }
+
+#ifdef DBX_OUTPUT_MAIN_SOURCE_FILENAME
+  /* This should NOT be DBX_OUTPUT_SOURCE_FILENAME. That
+     would give us an N_SOL, and we want an N_SO.  */
+  DBX_OUTPUT_MAIN_SOURCE_FILENAME (asmfile, input_file_name);
+#else /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */
+  /* We include outputting `Ltext:' here,
+     because that gives you a way to override it.  */
+  /* Used to put `Ltext:' before the reference, but that loses on sun 4.  */
+  fprintf (asmfile, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, input_file_name,
+          N_SO, &ltext_label_name[1]);
+  text_section ();
+  ASM_OUTPUT_INTERNAL_LABEL (asmfile, "Ltext", 0);
+#endif /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */
+
+  lastfile = input_file_name;
+
+  next_type_number = 1;
+  next_block_number = 2;
+
+  /* Make sure that types `int' and `char' have numbers 1 and 2.
+     Definitions of other integer types will refer to those numbers.
+     (Actually it should no longer matter what their numbers are.
+     Also, if any types with tags have been defined, dbxout_symbol
+     will output them first, so the numbers won't be 1 and 2.  That
+     happens in C++.  So it's a good thing it should no longer matter).  */
+
+#ifdef DBX_OUTPUT_STANDARD_TYPES
+  DBX_OUTPUT_STANDARD_TYPES (syms);
+#else
+  dbxout_symbol (TYPE_NAME (integer_type_node), 0);
+  dbxout_symbol (TYPE_NAME (char_type_node), 0);
+#endif
+
+  /* Get all permanent types that have typedef names,
+     and output them all, except for those already output.  */
+
+  dbxout_typedefs (syms);
+}
+
+/* Output any typedef names for types described by TYPE_DECLs in SYMS,
+   in the reverse order from that which is found in SYMS.  */
+
+static void
+dbxout_typedefs (syms)
+     tree syms;
+{
+  if (syms)
+    {
+      dbxout_typedefs (TREE_CHAIN (syms));
+      if (TREE_CODE (syms) == TYPE_DECL)
+       {
+         tree type = TREE_TYPE (syms);
+         if (TYPE_NAME (type)
+             && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+             && ! TREE_ASM_WRITTEN (TYPE_NAME (type)))
+           dbxout_symbol (TYPE_NAME (type), 0);
+       }
+    }
+}
+
+/* Output debugging info to FILE to switch to sourcefile FILENAME.  */
+
+void
+dbxout_source_file (file, filename)
+     FILE *file;
+     char *filename;
+{
+  char ltext_label_name[100];
+
+  if (filename && (lastfile == 0 || strcmp (filename, lastfile)))
+    {
+#ifdef DBX_OUTPUT_SOURCE_FILENAME
+      DBX_OUTPUT_SOURCE_FILENAME (file, filename);
+#else
+      ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
+      fprintf (file, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP,
+              filename, N_SOL, &ltext_label_name[1]);
+#endif
+      lastfile = filename;
+    }
+}
+
+/* At the end of compilation, finish writing the symbol table.
+   Unless you define DBX_OUTPUT_MAIN_SOURCE_FILE_END, the default is
+   to do nothing. */
+
+void
+dbxout_finish (file, filename)
+     FILE *file;
+     char *filename;
+{
+#ifdef DBX_OUTPUT_MAIN_SOURCE_FILE_END
+  DBX_OUTPUT_MAIN_SOURCE_FILE_END (file, filename);
+#endif /* DBX_OUTPUT_MAIN_SOURCE_FILE_END */
+}
+
+/* Continue a symbol-description that gets too big.
+   End one symbol table entry with a double-backslash
+   and start a new one, eventually producing something like
+   .stabs "start......\\",code,0,value
+   .stabs "...rest",code,0,value   */
+
+static void
+dbxout_continue ()
+{
+#ifdef DBX_CONTIN_CHAR
+  fprintf (asmfile, "%c", DBX_CONTIN_CHAR);
+#else
+  fprintf (asmfile, "\\\\");
+#endif
+  dbxout_finish_symbol (0);
+  fprintf (asmfile, "%s \"", ASM_STABS_OP);
+  current_sym_nchars = 0;
+}
+\f
+/* Subtroutine of `dbxout_type'.  Output the type fields of TYPE.
+   This must be a separate function because anonymous unions require
+   recursive calls.  */
+
+static void
+dbxout_type_fields (type)
+     tree type;
+{
+  tree tem;
+  for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
+    {
+      /* Output the name, type, position (in bits), size (in bits)
+        of each field.  */
+      if (DECL_NAME (tem) == NULL_TREE
+         && TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE)
+       dbxout_type_fields (TREE_TYPE (tem));
+      /* Omit here local type decls until we know how to support them.  */
+      else if (TREE_CODE (tem) == TYPE_DECL)
+       continue;
+      /* Omit here the nameless fields that are used to skip bits.  */
+      else if (DECL_NAME (tem) != 0 && TREE_CODE (tem) != CONST_DECL)
+       {
+         /* Continue the line if necessary,
+            but not before the first field.  */
+         if (tem != TYPE_FIELDS (type))
+           CONTIN;
+
+         if (use_gdb_dbx_extensions
+             && flag_minimal_debug
+             && TREE_CODE (tem) == FIELD_DECL
+             && DECL_VIRTUAL_P (tem)
+             && DECL_ASSEMBLER_NAME (tem))
+           {
+             have_used_extensions = 1;
+             CHARS (3 + IDENTIFIER_LENGTH (DECL_NAME (TYPE_NAME (DECL_FCONTEXT (tem)))));
+             fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)), asmfile);
+             dbxout_type (DECL_FCONTEXT (tem), 0);
+             fprintf (asmfile, ":");
+             dbxout_type (TREE_TYPE (tem), 0);
+             fprintf (asmfile, ",%d;",
+                      TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)));
+             continue;
+           }
+
+         fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem)));
+         CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem)));
+
+         if (use_gdb_dbx_extensions
+             && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem)
+                 || TREE_CODE (tem) != FIELD_DECL))
+           {
+             have_used_extensions = 1;
+             putc ('/', asmfile);
+             putc ((TREE_PRIVATE (tem) ? '0'
+                    : TREE_PROTECTED (tem) ? '1' : '2'),
+                   asmfile);
+             CHARS (2);
+           }
+
+         dbxout_type ((TREE_CODE (tem) == FIELD_DECL
+                       && DECL_BIT_FIELD_TYPE (tem))
+                      ? DECL_BIT_FIELD_TYPE (tem)
+                      : TREE_TYPE (tem), 0);
+
+         if (TREE_CODE (tem) == VAR_DECL)
+           {
+             if (TREE_STATIC (tem) && use_gdb_dbx_extensions)
+               {
+                 char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem));
+                 have_used_extensions = 1;
+
+#if 0 /* ??? Comment below makes it clear this is unacceptable.  */
+                 /* Adding 1 here only works on systems
+                    which flush an initial underscore from
+                    the .stabs entry.  This loses for static names
+                    which have an initial leading '_' on systems which
+                    don't use leading underscores.  */
+                 if (name[0] == '_')
+                   name += 1;
+#endif
+
+                 fprintf (asmfile, ":%s;", name);
+                 CHARS (strlen (name));
+               }
+             else
+               {
+                 /* If TEM is non-static, GDB won't understand it.  */
+                 fprintf (asmfile, ",0,0;");
+               }
+           }
+         else if (TREE_CODE (DECL_FIELD_BITPOS (tem)) == INTEGER_CST)
+           {
+             fprintf (asmfile, ",%d,%d;",
+                      TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)),
+                      TREE_INT_CST_LOW (DECL_SIZE (tem)));
+           }
+         else
+           /* This has yet to be implemented.  */
+           abort ();
+         CHARS (23);
+       }
+    }
+}
+\f
+/* Subtroutine of `dbxout_type_methods'.  Output debug info about the
+   method described DECL.  DEBUG_NAME is an encoding of the method's
+   type signature.  ??? We may be able to do without DEBUG_NAME altogether
+   now.  */
+
+static void
+dbxout_type_method_1 (decl, debug_name)
+     tree decl;
+     char *debug_name;
+{
+  tree firstarg = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl)));
+  char c1 = 'A', c2;
+
+  if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)
+    c2 = '?';
+  else /* it's a METHOD_TYPE.  */
+    {
+      /* A for normal functions.
+        B for `const' member functions.
+        C for `volatile' member functions.
+        D for `const volatile' member functions.  */
+      if (TYPE_READONLY (TREE_TYPE (firstarg)))
+       c1 += 1;
+      if (TYPE_VOLATILE (TREE_TYPE (firstarg)))
+       c1 += 2;
+
+      if (DECL_VINDEX (decl))
+       c2 = '*';
+      else
+       c2 = '.';
+    }
+
+  fprintf (asmfile, ":%s;%c%c%c", debug_name,
+          TREE_PRIVATE (decl) ? '0' : TREE_PROTECTED (decl) ? '1' : '2', c1, c2);
+  CHARS (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl)) + 6
+        - (debug_name - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))));
+  if (DECL_VINDEX (decl))
+    {
+      fprintf (asmfile, "%d;",
+              TREE_INT_CST_LOW (DECL_VINDEX (decl)));
+      dbxout_type (DECL_CONTEXT (decl), 0);
+      fprintf (asmfile, ";");
+      CHARS (8);
+    }
+}
+\f
+/* Subroutine of `dbxout_type'.  Output debug info about the methods defined
+   in TYPE.  */
+
+static void
+dbxout_type_methods (type)
+     register tree type;
+{
+  /* C++: put out the method names and their parameter lists */
+  tree ctor_name;
+  tree methods = TYPE_METHODS (type);
+  register tree fndecl;
+  register tree last;
+  register int type_identifier_length;
+
+  if (methods == NULL_TREE)
+    return;
+
+  ctor_name = DECL_NAME (TYPE_NAME (type));
+  type_identifier_length = IDENTIFIER_LENGTH (ctor_name);
+  if (TREE_CODE (methods) == FUNCTION_DECL)
+    fndecl = methods;
+  else if (TREE_VEC_ELT (methods, 0) != NULL_TREE)
+    fndecl = TREE_VEC_ELT (methods, 0);
+  else fndecl = TREE_VEC_ELT (methods, 1);
+
+  if (TREE_CODE (type) == RECORD_TYPE && DECL_NAME (fndecl) == ctor_name)
+    {
+      tree ctor = fndecl;
+      tree dtor;
+
+      /* Destructors lie in a special place.
+         n.b.  TYPE_HAS_DESTRUCTOR == TYPE_LANG_FLAG_2 */
+      if (TYPE_LANG_FLAG_2 (type))
+       {
+         dtor = fndecl;
+         fndecl = ctor = TREE_CHAIN (dtor);
+       }
+      else
+       dtor = NULL_TREE;
+
+      CHARS (2);
+
+      if (ctor)
+       {
+         int need_prefix = 1;
+
+         while (ctor)
+           {
+             /* Output the name of the field (after overloading), as
+                well as the name of the field before overloading, along
+                with its parameter list.  */
+             char *debug_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (ctor));
+             int old_minimal_debug = flag_minimal_debug;
+
+             CONTIN;
+
+             if (DECL_NAME (ctor) == ctor_name && ! DECL_IGNORED_P (ctor))
+               {
+                 if (need_prefix)
+                   {
+                     fprintf (asmfile, "%s::", IDENTIFIER_POINTER (DECL_NAME (ctor)));
+                     CHARS (IDENTIFIER_LENGTH (DECL_NAME (ctor)) + 3);
+                     need_prefix = 0;
+                   }
+
+                 if (ctor == dtor)
+                   /* Always output destructors with full information.  */
+                   flag_minimal_debug = 0;
+
+                 dbxout_type (TREE_TYPE (ctor), 0);
+                 flag_minimal_debug = old_minimal_debug;
+
+                 if (flag_minimal_debug && ctor != dtor)
+                   {
+                     /* Cut down on debugging information by not outputting
+                        the parts of the name we can just as easily
+                        have the debugger figure out.  */
+
+                     /* Get past '__'.  */
+                     debug_name += 2;
+                     /* Get past const and volatile qualifiers.  */
+                     while (*debug_name == 'C' || *debug_name == 'V')
+                       debug_name++;
+                     /* Get past numeric type length prefix.  */
+                     while (*debug_name >= '0' && *debug_name <= '9')
+                       debug_name++;
+                     /* Get past type of `this'.  */
+                     debug_name += type_identifier_length;
+                   }
+                 dbxout_type_method_1 (ctor, debug_name);
+               }
+
+             if (ctor == dtor)
+               break;
+
+             ctor = TREE_CHAIN (ctor);
+             if (ctor == NULL_TREE || DECL_NAME (ctor) != ctor_name)
+               {
+                 fndecl = ctor;
+                 ctor = dtor;
+               }
+           }
+         if (! need_prefix)
+           putc (';', asmfile);
+       }
+    }
+
+  while (fndecl)
+    {
+      tree name = DECL_NAME (fndecl);
+      fprintf (asmfile, "%s::", IDENTIFIER_POINTER (name));
+      CHARS (IDENTIFIER_LENGTH (name) + 3);
+
+      for (last = NULL_TREE;
+          fndecl && (last == NULL_TREE || DECL_NAME (fndecl) == DECL_NAME (last));
+          fndecl = TREE_CHAIN (fndecl))
+       /* Output the name of the field (after overloading), as
+          well as the name of the field before overloading, along
+          with its parameter list */
+       {
+         char *debug_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl));
+
+         CONTIN;
+
+         last = fndecl;
+         dbxout_type (TREE_TYPE (fndecl), 0);
+         if (flag_minimal_debug)
+           {
+             debug_name += IDENTIFIER_LENGTH (DECL_NAME (fndecl)) + 2;
+             /* Get past const and volatile qualifiers.  */
+             while (*debug_name == 'C' || *debug_name == 'V')
+               debug_name++;
+             while (*debug_name >= '0' && *debug_name <= '9')
+               debug_name++;
+             debug_name += type_identifier_length;
+           }
+         dbxout_type_method_1 (fndecl, debug_name);
+       }
+      putc (';', asmfile);
+      CHARS (1);
+    }
+}
+\f
+/* Output a reference to a type.  If the type has not yet been
+   described in the dbx output, output its definition now.
+   For a type already defined, just refer to its definition
+   using the type number.
+
+   If FULL is nonzero, and the type has been described only with
+   a forward-reference, output the definition now.
+   If FULL is zero in this case, just refer to the forward-reference
+   using the number previously allocated.  */
+
+static void
+dbxout_type (type, full)
+     tree type;
+     int full;
+{
+  register tree tem;
+
+  /* If there was an input error and we don't really have a type,
+     avoid crashing and write something that is at least valid
+     by assuming `int'.  */
+  if (type == error_mark_node)
+    type = integer_type_node;
+  else
+    {
+      type = TYPE_MAIN_VARIANT (type);
+      if (TYPE_NAME (type)
+         && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+         && DECL_IGNORED_P (TYPE_NAME (type)))
+       full = 0;
+    }
+
+  if (TYPE_SYMTAB_ADDRESS (type) == 0)
+    {
+      /* Type has no dbx number assigned.  Assign next available number.  */
+      TYPE_SYMTAB_ADDRESS (type) = next_type_number++;
+
+      /* Make sure type vector is long enough to record about this type.  */
+
+      if (next_type_number == typevec_len)
+       {
+         typevec = (enum typestatus *) xrealloc (typevec, typevec_len * 2 * sizeof typevec[0]);
+         bzero (typevec + typevec_len, typevec_len * sizeof typevec[0]);
+         typevec_len *= 2;
+       }
+    }
+
+  /* Output the number of this type, to refer to it.  */
+  fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
+  CHARS (3);
+
+  /* If this type's definition has been output or is now being output,
+     that is all.  */
+
+  switch (typevec[TYPE_SYMTAB_ADDRESS (type)])
+    {
+    case TYPE_UNSEEN:
+      break;
+    case TYPE_XREF:
+      if (! full)
+       return;
+      break;
+    case TYPE_DEFINED:
+      return;
+    }
+
+#ifdef DBX_NO_XREFS
+  /* For systems where dbx output does not allow the `=xsNAME:' syntax,
+     leave the type-number completely undefined rather than output
+     a cross-reference.  */
+  if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
+      || TREE_CODE (type) == ENUMERAL_TYPE)
+
+    if ((TYPE_NAME (type) != 0 && !full)
+       || TYPE_SIZE (type) == 0)
+      {
+       typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+       return;
+      }
+#endif
+
+  /* Output a definition now.  */
+
+  fprintf (asmfile, "=");
+  CHARS (1);
+
+  /* Mark it as defined, so that if it is self-referent
+     we will not get into an infinite recursion of definitions.  */
+
+  typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED;
+
+  switch (TREE_CODE (type))
+    {
+    case VOID_TYPE:
+    case LANG_TYPE:
+      /* For a void type, just define it as itself; ie, "5=5".
+        This makes us consider it defined
+        without saying what it is.  The debugger will make it
+        a void type when the reference is seen, and nothing will
+        ever override that default.  */
+      fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
+      CHARS (3);
+      break;
+
+    case INTEGER_TYPE:
+      if (type == char_type_node && ! TREE_UNSIGNED (type))
+       /* Output the type `char' as a subrange of itself!
+          I don't understand this definition, just copied it
+          from the output of pcc.
+          This used to use `r2' explicitly and we used to
+          take care to make sure that `char' was type number 2.  */
+       fprintf (asmfile, "r%d;0;127;", TYPE_SYMTAB_ADDRESS (type));
+#ifdef WINNING_GDB
+      else if (TYPE_PRECISION (type) > BITS_PER_WORD)
+       {
+         /* This used to say `r1' and we used to take care
+            to make sure that `int' was type number 1.  */
+         fprintf (asmfile, "r%d;", TYPE_SYMTAB_ADDRESS (integer_type_node));
+         print_int_cst_octal (TYPE_MIN_VALUE (type));
+         fprintf (asmfile, ";");
+         print_int_cst_octal (TYPE_MAX_VALUE (type));
+         fprintf (asmfile, ";");
+       }
+#endif
+      else
+       /* Output other integer types as subranges of `int'.  */
+       /* This used to say `r1' and we used to take care
+          to make sure that `int' was type number 1.  */
+       fprintf (asmfile, "r%d;%d;%d;",
+                TYPE_SYMTAB_ADDRESS (integer_type_node),
+                TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)),
+                TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)));
+      CHARS (25);
+      break;
+
+    case REAL_TYPE:
+      /* This used to say `r1' and we used to take care
+        to make sure that `int' was type number 1.  */
+      fprintf (asmfile, "r%d;%d;0;", TYPE_SYMTAB_ADDRESS (integer_type_node),
+              TREE_INT_CST_LOW (size_in_bytes (type)));
+      CHARS (16);
+      break;
+
+    case ARRAY_TYPE:
+      /* Output "a" followed by a range type definition
+        for the index type of the array
+        followed by a reference to the target-type.
+        ar1;0;N;M for an array of type M and size N.  */
+      /* This used to say `r1' and we used to take care
+        to make sure that `int' was type number 1.  */
+      fprintf (asmfile, "ar%d;0;%d;", TYPE_SYMTAB_ADDRESS (integer_type_node),
+
+              (TYPE_DOMAIN (type)
+               ? TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))
+               : -1));
+      CHARS (17);
+      dbxout_type (TREE_TYPE (type), 0);
+      break;
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+      {
+       int i, n_baseclasses = 0;
+
+       if (TYPE_BINFO (type) != 0 && TYPE_BINFO_BASETYPES (type) != 0)
+         n_baseclasses = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type));
+
+       /* Output a structure type.  */
+       if ((TYPE_NAME (type) != 0 && !full)
+           || TYPE_SIZE (type) == 0)
+         {
+           /* If the type is just a cross reference, output one
+              and mark the type as partially described.
+              If it later becomes defined, we will output
+              its real definition.
+              If the type has a name, don't nest its definition within
+              another type's definition; instead, output an xref
+              and let the definition come when the name is defined.  */
+           fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu");
+           CHARS (3);
+#if 0                          /* This assertion is legitimately false in C++.  */
+           /* We shouldn't be outputting a reference to a type before its
+              definition unless the type has a tag name.
+              A typedef name without a tag name should be impossible.  */
+           if (TREE_CODE (TYPE_NAME (type)) != IDENTIFIER_NODE)
+             abort ();
+#endif
+           dbxout_type_name (type);
+           fprintf (asmfile, ":");
+           typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+           break;
+         }
+       tem = size_in_bytes (type);
+
+       /* The code below assumes the size is an integer constant.  */
+       if (TREE_CODE (tem) != INTEGER_CST)
+         abort ();
+
+       /* Identify record or union, and print its size.  */
+       fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d",
+                TREE_INT_CST_LOW (tem));
+
+       if (use_gdb_dbx_extensions)
+         {
+           if (n_baseclasses)
+             {
+               have_used_extensions = 1;
+               fprintf (asmfile, "!%d,", n_baseclasses);
+               CHARS (8);
+             }
+         }
+       for (i = 0; i < n_baseclasses; i++)
+         {
+           tree child = TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (type)), i);
+           if (use_gdb_dbx_extensions)
+             {
+               have_used_extensions = 1;
+               putc (TREE_VIA_VIRTUAL (child) ? '1'
+                     : '0',
+                     asmfile);
+               putc (TREE_VIA_PUBLIC (child) ? '2'
+                     : '0',
+                     asmfile);
+               fprintf (asmfile, "%d,",
+                        TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT);
+               CHARS (15);
+               dbxout_type (BINFO_TYPE (child), 0);
+               putc (';', asmfile);
+             }
+           else
+             {
+               /* Print out the base class information with fields
+                  which have the same names at the types they hold.  */
+               dbxout_type_name (BINFO_TYPE (child));
+               putc (':', asmfile);
+               dbxout_type (BINFO_TYPE (child), full);
+               fprintf (asmfile, ",%d,%d;",
+                        TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT,
+                        TREE_INT_CST_LOW (DECL_SIZE (TYPE_NAME (BINFO_TYPE (child)))) * BITS_PER_UNIT);
+               CHARS (20);
+             }
+         }
+      }
+
+      CHARS (11);
+
+      /* Write out the field declarations.  */
+      dbxout_type_fields (type);
+      if (use_gdb_dbx_extensions)
+       {
+         have_used_extensions = 1;
+         dbxout_type_methods (type);
+       }
+      putc (';', asmfile);
+
+      if (use_gdb_dbx_extensions && TREE_CODE (type) == RECORD_TYPE
+         /* Avoid the ~ if we don't really need it--it confuses dbx.  */
+         && TYPE_VFIELD (type))
+       {
+         have_used_extensions = 1;
+
+         /* Tell GDB+ that it may keep reading.  */
+         putc ('~', asmfile);
+
+         /* We need to write out info about what field this class
+            uses as its "main" vtable pointer field, because if this
+            field is inherited from a base class, GDB cannot necessarily
+            figure out which field it's using in time.  */
+         if (TYPE_VFIELD (type))
+           {
+             putc ('%', asmfile);
+             dbxout_type (DECL_FCONTEXT (TYPE_VFIELD (type)), 0);
+           }
+         putc (';', asmfile);
+         CHARS (3);
+       }
+      break;
+
+    case ENUMERAL_TYPE:
+      if ((TYPE_NAME (type) != 0 && !full
+          && (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+              && ! DECL_IGNORED_P (TYPE_NAME (type))))
+         || TYPE_SIZE (type) == 0)
+       {
+         fprintf (asmfile, "xe");
+         CHARS (3);
+         dbxout_type_name (type);
+         typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+         fprintf (asmfile, ":");
+         return;
+       }
+      putc ('e', asmfile);
+      CHARS (1);
+      for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
+       {
+         fprintf (asmfile, "%s:%d,", IDENTIFIER_POINTER (TREE_PURPOSE (tem)),
+                  TREE_INT_CST_LOW (TREE_VALUE (tem)));
+         CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem)));
+         if (TREE_CHAIN (tem) != 0)
+           CONTIN;
+       }
+      putc (';', asmfile);
+      CHARS (1);
+      break;
+
+    case POINTER_TYPE:
+      putc ('*', asmfile);
+      CHARS (1);
+      dbxout_type (TREE_TYPE (type), 0);
+      break;
+
+    case METHOD_TYPE:
+      if (use_gdb_dbx_extensions)
+       {
+         have_used_extensions = 1;
+         putc ('#', asmfile);
+         CHARS (1);
+         if (flag_minimal_debug)
+           {
+             putc ('#', asmfile);
+             dbxout_type (TREE_TYPE (type), 0);
+             putc (';', asmfile);
+             CHARS (1);
+           }
+         else
+           {
+             dbxout_type (TYPE_METHOD_BASETYPE (type), 0);
+             putc (',', asmfile);
+             CHARS (1);
+             dbxout_type (TREE_TYPE (type), 0);
+             dbxout_args (TYPE_ARG_TYPES (type));
+             putc (';', asmfile);
+             CHARS (1);
+           }
+       }
+      else
+       {
+         /* Treat it as a function type.  */
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      break;
+
+    case OFFSET_TYPE:
+      if (use_gdb_dbx_extensions)
+       {
+         have_used_extensions = 1;
+         putc ('@', asmfile);
+         CHARS (1);
+         dbxout_type (TYPE_OFFSET_BASETYPE (type), 0);
+         putc (',', asmfile);
+         CHARS (1);
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      else
+       {
+         /* Should print as an int, because it is really
+            just an offset.  */
+         dbxout_type (integer_type_node, 0);
+       }
+      break;
+
+    case REFERENCE_TYPE:
+      if (use_gdb_dbx_extensions)
+       have_used_extensions = 1;
+      putc (use_gdb_dbx_extensions ? '&' : '*', asmfile);
+      CHARS (1);
+      dbxout_type (TREE_TYPE (type), 0);
+      break;
+
+    case FUNCTION_TYPE:
+      putc ('f', asmfile);
+      CHARS (1);
+      dbxout_type (TREE_TYPE (type), 0);
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+/* Print the value of integer constant C, in octal,
+   handling double precision.  */
+
+static void
+print_int_cst_octal (c)
+     tree c;
+{
+  unsigned int high = TREE_INT_CST_HIGH (c);
+  unsigned int low = TREE_INT_CST_LOW (c);
+  int excess = (3 - (HOST_BITS_PER_INT % 3));
+
+  fprintf (asmfile, "0");
+
+  if (excess == 3)
+    {
+      print_octal (high, HOST_BITS_PER_INT / 3);
+      print_octal (low, HOST_BITS_PER_INT / 3);
+    }
+  else
+    {
+      unsigned int beg = high >> excess;
+      unsigned int middle
+       = ((high & ((1 << excess) - 1)) << (3 - excess)
+          | (low >> (HOST_BITS_PER_INT / 3 * 3)));
+      unsigned int end = low & ((1 << (HOST_BITS_PER_INT / 3 * 3)) - 1);
+      fprintf (asmfile, "%o%01o", beg, middle);
+      print_octal (end, HOST_BITS_PER_INT / 3);
+    }
+}
+
+static void
+print_octal (value, digits)
+     unsigned int value;
+     int digits;
+{
+  int i;
+
+  for (i = digits - 1; i >= 0; i--)
+    fprintf (asmfile, "%01o", ((value >> (3 * i)) & 7));
+}
+
+/* Output the name of type TYPE, with no punctuation.
+   Such names can be set up either by typedef declarations
+   or by struct, enum and union tags.  */
+
+static void
+dbxout_type_name (type)
+     register tree type;
+{
+  tree t;
+  if (TYPE_NAME (type) == 0)
+    abort ();
+  if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+    {
+      t = TYPE_NAME (type);
+    }
+  else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
+    {
+      t = DECL_NAME (TYPE_NAME (type));
+    }
+  else
+    abort ();
+
+  fprintf (asmfile, "%s", IDENTIFIER_POINTER (t));
+  CHARS (IDENTIFIER_LENGTH (t));
+}
+\f
+/* Output a .stabs for the symbol defined by DECL,
+   which must be a ..._DECL node in the normal namespace.
+   It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL.
+   LOCAL is nonzero if the scope is less than the entire file.  */
+
+void
+dbxout_symbol (decl, local)
+     tree decl;
+     int local;
+{
+  int letter = 0;
+  tree type = TREE_TYPE (decl);
+  tree context = NULL_TREE;
+  int regno = -1;
+
+  /* Cast avoids warning in old compilers.  */
+  current_sym_code = (STAB_CODE_TYPE) 0;
+  current_sym_value = 0;
+  current_sym_addr = 0;
+
+  /* Ignore nameless syms, but don't ignore type tags.  */
+
+  if ((DECL_NAME (decl) == 0 && TREE_CODE (decl) != TYPE_DECL)
+      || DECL_IGNORED_P (decl))
+    return;
+
+  dbxout_prepare_symbol (decl);
+
+  /* The output will always start with the symbol name,
+     so always count that in the length-output-so-far.  */
+
+  if (DECL_NAME (decl) != 0)
+    current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl));
+
+  switch (TREE_CODE (decl))
+    {
+    case CONST_DECL:
+      /* Enum values are defined by defining the enum type.  */
+      break;
+
+    case FUNCTION_DECL:
+      if (DECL_RTL (decl) == 0)
+       return;
+      if (TREE_EXTERNAL (decl))
+       break;
+      /* Don't mention a nested function under its parent.  */
+      context = decl_function_context (decl);
+      if (context == current_function_decl)
+       break;
+      if (GET_CODE (DECL_RTL (decl)) != MEM
+         || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
+       break;
+      FORCE_TEXT;
+
+      fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+              IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+              TREE_PUBLIC (decl) ? 'F' : 'f');
+
+      current_sym_code = N_FUN;
+      current_sym_addr = XEXP (DECL_RTL (decl), 0);
+
+      if (TREE_TYPE (type))
+       dbxout_type (TREE_TYPE (type), 0);
+      else
+       dbxout_type (void_type_node, 0);
+
+      /* For a nested function, when that function is compiled,
+        mention the containing function name
+        as well as (since dbx wants it) our own assembler-name.  */
+      if (context != 0)
+       fprintf (asmfile, ",%s,%s",
+                IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+                IDENTIFIER_POINTER (DECL_NAME (context)));
+
+      dbxout_finish_symbol (decl);
+      break;
+
+    case TYPE_DECL:
+#if 0
+      /* This seems all wrong.  Outputting most kinds of types gives no name
+        at all.  A true definition gives no name; a cross-ref for a
+        structure can give the tag name, but not a type name.
+        It seems that no typedef name is defined by outputting a type.  */
+
+      /* If this typedef name was defined by outputting the type,
+        don't duplicate it.  */
+      if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED
+         && TYPE_NAME (TREE_TYPE (decl)) == decl)
+       return;
+#endif
+      /* Don't output the same typedef twice.
+         And don't output what language-specific stuff doesn't want output.  */
+      if (TREE_ASM_WRITTEN (decl) || DECL_IGNORED_P (decl))
+       return;
+
+      FORCE_TEXT;
+
+      if (DECL_NAME (decl))
+       {
+         /* Output typedef name.  */
+         fprintf (asmfile, "%s \"%s:", ASM_STABS_OP,
+                  IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+         /* If there is a typedecl for this type with the same name
+            as the tag, output an abbreviated form for that typedecl.  */
+         if (use_gdb_dbx_extensions && have_used_extensions
+             && (TREE_CODE (type) == RECORD_TYPE
+                 || TREE_CODE (type) == UNION_TYPE)
+             && (TYPE_NAME (type) == decl))
+               {
+                 putc ('T', asmfile);
+                 TREE_ASM_WRITTEN (TYPE_NAME (type)) = 1;
+               }
+         putc ('t', asmfile);
+         current_sym_code = N_LSYM;
+
+         dbxout_type (type, 1);
+         dbxout_finish_symbol (decl);
+       }
+      else if (TYPE_NAME (type) != 0 && !TREE_ASM_WRITTEN (TYPE_NAME (type)))
+       {
+         /* Output a tag (a TYPE_DECL with no name, but the type has a name).
+            This is what represents `struct foo' with no typedef.  */
+         /* In C++, the name of a type is the corresponding typedef.
+            In C, it is an IDENTIFIER_NODE.  */
+         tree name = TYPE_NAME (type);
+         if (TREE_CODE (name) == TYPE_DECL)
+           name = DECL_NAME (name);
+
+         current_sym_code = N_LSYM;
+         current_sym_value = 0;
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + IDENTIFIER_LENGTH (name);
+
+         fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP,
+                  IDENTIFIER_POINTER (name));
+         dbxout_type (type, 1);
+         dbxout_finish_symbol (0);
+       }
+
+      /* Prevent duplicate output of a typedef.  */
+      TREE_ASM_WRITTEN (decl) = 1;
+      break;
+
+    case PARM_DECL:
+      /* Parm decls go in their own separate chains
+        and are output by dbxout_reg_parms and dbxout_parms.  */
+      abort ();
+
+    case RESULT_DECL:
+      /* Named return value, treat like a VAR_DECL.  */
+    case VAR_DECL:
+      if (DECL_RTL (decl) == 0)
+       return;
+      /* Don't mention a variable that is external.
+        Let the file that defines it describe it.  */
+      if (TREE_EXTERNAL (decl))
+       break;
+
+      /* If the variable is really a constant
+        and not written in memory, inform the debugger.  */
+      if (TREE_STATIC (decl) && TREE_READONLY (decl)
+         && DECL_INITIAL (decl) != 0
+         && ! TREE_ASM_WRITTEN (decl)
+         && (DECL_FIELD_CONTEXT (decl) == NULL_TREE
+             || TREE_CODE (DECL_FIELD_CONTEXT (decl)) == BLOCK))
+       {
+         if (TREE_PUBLIC (decl) == 0)
+           {
+             /* The sun4 assembler does not grok this.  */
+             char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
+             if (TREE_CODE (TREE_TYPE (decl)) == INTEGER_TYPE
+                 || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
+               {
+                 int ival = TREE_INT_CST_LOW (DECL_INITIAL (decl));
+#ifdef DBX_OUTPUT_CONSTANT_SYMBOL
+                 DBX_OUTPUT_CONSTANT_SYMBOL (asmfile, name, ival);
+#else
+                 fprintf (asmfile, "%s \"%s:c=i%d\",0x%x,0,0,0\n",
+                          ASM_STABS_OP, name, ival, N_LSYM);
+#endif
+                 return;
+               }
+             else if (TREE_CODE (TREE_TYPE (decl)) == REAL_TYPE)
+               {
+                 /* don't know how to do this yet.  */
+               }
+             break;
+           }
+         /* else it is something we handle like a normal variable.  */
+       }
+
+      DECL_RTL (decl) = eliminate_regs (DECL_RTL (decl));
+#ifdef LEAF_REG_REMAP
+      if (leaf_function)
+       leaf_renumber_regs_insn (DECL_RTL (decl));
+#endif
+
+      /* Don't mention a variable at all
+        if it was completely optimized into nothingness.
+
+        If DECL was from an inline function, then it's rtl
+        is not identically the rtl that was used in this
+        particular compilation.  */
+      if (GET_CODE (DECL_RTL (decl)) == REG)
+       {
+         regno = REGNO (DECL_RTL (decl));
+         if (regno >= FIRST_PSEUDO_REGISTER)
+           regno = reg_renumber[REGNO (DECL_RTL (decl))];
+         if (regno < 0)
+           break;
+       }
+      else if (GET_CODE (DECL_RTL (decl)) == SUBREG)
+       {
+         rtx value = DECL_RTL (decl);
+         int offset = 0;
+         while (GET_CODE (value) == SUBREG)
+           {
+             offset += SUBREG_WORD (value);
+             value = SUBREG_REG (value);
+           }
+         if (GET_CODE (value) == REG)
+           {
+             regno = REGNO (value);
+             if (regno >= FIRST_PSEUDO_REGISTER)
+               regno = reg_renumber[REGNO (value)];
+             if (regno >= 0)
+               regno += offset;
+           }
+       }
+
+      /* The kind-of-variable letter depends on where
+        the variable is and on the scope of its name:
+        G and N_GSYM for static storage and global scope,
+        S for static storage and file scope,
+        V for static storage and local scope,
+           for those two, use N_LCSYM if data is in bss segment,
+           N_STSYM if in data segment, N_FUN otherwise.
+           (We used N_FUN originally, then changed to N_STSYM
+           to please GDB.  However, it seems that confused ld.
+           Now GDB has been fixed to like N_FUN, says Kingdon.)
+        no letter at all, and N_LSYM, for auto variable,
+        r and N_RSYM for register variable.  */
+
+      if (GET_CODE (DECL_RTL (decl)) == MEM
+         && GET_CODE (XEXP (DECL_RTL (decl), 0)) == SYMBOL_REF)
+       {
+         if (TREE_PUBLIC (decl))
+           {
+             letter = 'G';
+             current_sym_code = N_GSYM;
+           }
+         else
+           {
+             current_sym_addr = XEXP (DECL_RTL (decl), 0);
+
+             letter = TREE_PERMANENT (decl) ? 'S' : 'V';
+
+             if (!DECL_INITIAL (decl))
+               current_sym_code = N_LCSYM;
+             else if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl))
+               /* This is not quite right, but it's the closest
+                  of all the codes that Unix defines.  */
+               current_sym_code = N_FUN;
+             else
+               {
+/* Ultrix `as' seems to need this.  */
+#ifdef DBX_STATIC_STAB_DATA_SECTION
+                 data_section ();
+#endif
+                 current_sym_code = N_STSYM;
+               }
+           }
+       }
+      else if (regno >= 0)
+       {
+         letter = 'r';
+         current_sym_code = N_RSYM;
+         current_sym_value = DBX_REGISTER_NUMBER (regno);
+       }
+      else if (GET_CODE (DECL_RTL (decl)) == SUBREG)
+       {
+         rtx value = DECL_RTL (decl);
+         int offset = 0;
+         while (GET_CODE (value) == SUBREG)
+           {
+             offset += SUBREG_WORD (value);
+             value = SUBREG_REG (value);
+           }
+         letter = 'r';
+         current_sym_code = N_RSYM;
+         current_sym_value = DBX_REGISTER_NUMBER (REGNO (value) + offset);
+       }
+      else if (GET_CODE (DECL_RTL (decl)) == MEM
+              && (GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
+                  || (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG
+                      && REGNO (XEXP (DECL_RTL (decl), 0)) != FRAME_POINTER_REGNUM)))
+       /* If the value is indirect by memory or by a register
+          that isn't the frame pointer
+          then it means the object is variable-sized and address through
+          that register or stack slot.  DBX has no way to represent this
+          so all we can do is output the variable as a pointer.
+          If it's not a parameter, ignore it.
+          (VAR_DECLs like this can be made by integrate.c.)  */
+       {
+         if (GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG)
+           {
+             letter = 'r';
+             current_sym_code = N_RSYM;
+             current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (DECL_RTL (decl), 0)));
+           }
+         else
+           {
+             current_sym_code = N_LSYM;
+             /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))).
+                We want the value of that CONST_INT.  */
+             current_sym_value
+               = DEBUGGER_AUTO_OFFSET (XEXP (XEXP (DECL_RTL (decl), 0), 0));
+           }
+
+         /* Effectively do build_pointer_type, but don't cache this type,
+            since it might be temporary whereas the type it points to
+            might have been saved for inlining.  */
+         type = make_node (REFERENCE_TYPE);
+         TREE_TYPE (type) = TREE_TYPE (decl);
+       }
+      else if (GET_CODE (DECL_RTL (decl)) == MEM
+              && GET_CODE (XEXP (DECL_RTL (decl), 0)) == REG)
+       {
+         current_sym_code = N_LSYM;
+         current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0));
+       }
+      else if (GET_CODE (DECL_RTL (decl)) == MEM
+              && GET_CODE (XEXP (DECL_RTL (decl), 0)) == PLUS
+              && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 1)) == CONST_INT)
+       {
+         current_sym_code = N_LSYM;
+         /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...)))
+            We want the value of that CONST_INT.  */
+         current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (decl), 0));
+       }
+      else
+       /* Address might be a MEM, when DECL is a variable-sized object.
+          Or it might be const0_rtx, meaning previous passes
+          want us to ignore this variable.  */
+       break;
+
+      /* Ok, start a symtab entry and output the variable name.  */
+      FORCE_TEXT;
+      /* One slight hitch: if this is a VAR_DECL which is a static
+        class member, we must put out the mangled name instead of the
+        DECL_NAME.  */
+      {
+       char *name;
+       /* Note also that static member (variable) names DO NOT begin
+          with underscores in .stabs directives.  */
+       if (DECL_LANG_SPECIFIC (decl))
+         {
+           name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+           /* Adding 1 here only works on systems
+              which flush an initial underscore.  */
+           if (name[0] == '_')
+             name += 1;
+         }
+       else
+         name = IDENTIFIER_POINTER (DECL_NAME (decl));
+       fprintf (asmfile, "%s \"%s:", ASM_STABS_OP, name);
+      }
+      if (letter) putc (letter, asmfile);
+      dbxout_type (type, 0);
+      dbxout_finish_symbol (decl);
+      break;
+    }
+}
+
+static void
+dbxout_prepare_symbol (decl)
+     tree decl;
+{
+#ifdef WINNING_GDB
+  char *filename = DECL_SOURCE_FILE (decl);
+
+  dbxout_source_file (asmfile, filename);
+#endif
+}
+
+static void
+dbxout_finish_symbol (sym)
+     tree sym;
+{
+  int line = 0;
+#ifdef WINNING_GDB
+  if (sym != 0)
+    line = DECL_SOURCE_LINE (sym);
+#endif
+  fprintf (asmfile, "\",%d,0,%d,", current_sym_code, line);
+  if (current_sym_addr)
+    output_addr_const (asmfile, current_sym_addr);
+  else
+    fprintf (asmfile, "%d", current_sym_value);
+  putc ('\n', asmfile);
+}
+
+/* Output definitions of all the decls in a chain.  */
+
+static void
+dbxout_syms (syms)
+     tree syms;
+{
+  while (syms)
+    {
+      dbxout_symbol (syms, 1);
+      syms = TREE_CHAIN (syms);
+    }
+}
+\f
+/* The following two functions output definitions of function parameters.
+   Each parameter gets a definition locating it in the parameter list.
+   Each parameter that is a register variable gets a second definition
+   locating it in the register.
+
+   Printing or argument lists in gdb uses the definitions that
+   locate in the parameter list.  But reference to the variable in
+   expressions uses preferentially the definition as a register.  */
+
+/* Output definitions, referring to storage in the parmlist,
+   of all the parms in PARMS, which is a chain of PARM_DECL nodes.  */
+
+static void
+dbxout_parms (parms)
+     tree parms;
+{
+  for (; parms; parms = TREE_CHAIN (parms))
+    if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
+      {
+       dbxout_prepare_symbol (parms);
+
+       /* Perform any necessary register eliminations on the parameter's rtl,
+          so that the debugging output will be accurate.  */
+       DECL_INCOMING_RTL (parms)
+         = eliminate_regs (DECL_INCOMING_RTL (parms), 0, 0);
+       DECL_RTL (parms) = eliminate_regs (DECL_RTL (parms), 0, 0);
+#ifdef LEAF_REG_REMAP
+       if (leaf_function)
+         {
+           leaf_renumber_regs_insn (DECL_INCOMING_RTL (parms));
+           leaf_renumber_regs_insn (DECL_RTL (parms));
+         }
+#endif
+
+       if (PARM_PASSED_IN_MEMORY (parms))
+         {
+           rtx addr = XEXP (DECL_INCOMING_RTL (parms), 0);
+
+           /* ??? Here we assume that the parm address is indexed
+              off the frame pointer or arg pointer.
+              If that is not true, we produce meaningless results,
+              but do not crash.  */
+           if (GET_CODE (addr) == PLUS
+               && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+             current_sym_value = INTVAL (XEXP (addr, 1));
+           else
+             current_sym_value = 0;
+
+           current_sym_code = N_PSYM;
+           current_sym_addr = 0;
+
+           FORCE_TEXT;
+           if (DECL_NAME (parms))
+             {
+               current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+
+               fprintf (asmfile, "%s \"%s:p", ASM_STABS_OP,
+                        IDENTIFIER_POINTER (DECL_NAME (parms)));
+             }
+           else
+             {
+               current_sym_nchars = 8;
+               fprintf (asmfile, "%s \"(anon):p", ASM_STABS_OP);
+             }
+
+           if (GET_CODE (DECL_RTL (parms)) == REG
+               && REGNO (DECL_RTL (parms)) >= 0
+               && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+             dbxout_type (DECL_ARG_TYPE (parms), 0);
+           else
+             {
+               int original_value = current_sym_value;
+
+               /* This is the case where the parm is passed as an int or double
+                  and it is converted to a char, short or float and stored back
+                  in the parmlist.  In this case, describe the parm
+                  with the variable's declared type, and adjust the address
+                  if the least significant bytes (which we are using) are not
+                  the first ones.  */
+#if BYTES_BIG_ENDIAN
+               if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+                 current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+                                       - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+
+               if (GET_CODE (DECL_RTL (parms)) == MEM
+                   && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+                   && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
+                   && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value)
+                 dbxout_type (TREE_TYPE (parms), 0);
+               else
+                 {
+                   current_sym_value = original_value;
+                   dbxout_type (DECL_ARG_TYPE (parms), 0);
+                 }
+             }
+           current_sym_value = DEBUGGER_ARG_OFFSET (current_sym_value, addr);
+           dbxout_finish_symbol (parms);
+         }
+       else if (GET_CODE (DECL_RTL (parms)) == REG)
+         {
+           rtx best_rtl;
+           /* Parm passed in registers and lives in registers or nowhere.  */
+
+           current_sym_code = N_RSYM;
+           current_sym_addr = 0;
+
+           /* If parm lives in a register, use that register;
+              pretend the parm was passed there.  It would be more consistent
+              to describe the register where the parm was passed,
+              but in practice that register usually holds something else.  */
+           if (REGNO (DECL_RTL (parms)) >= 0
+               && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+             best_rtl = DECL_RTL (parms);
+           /* If the parm lives nowhere,
+              use the register where it was passed.  */
+           else
+             best_rtl = DECL_INCOMING_RTL (parms);
+           current_sym_value = DBX_REGISTER_NUMBER (REGNO (best_rtl));
+
+           FORCE_TEXT;
+           if (DECL_NAME (parms))
+             {
+               current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+               fprintf (asmfile, "%s \"%s:P", ASM_STABS_OP,
+                        IDENTIFIER_POINTER (DECL_NAME (parms)));
+             }
+           else
+             {
+               current_sym_nchars = 8;
+               fprintf (asmfile, "%s \"(anon):P", ASM_STABS_OP);
+             }
+
+           dbxout_type (DECL_ARG_TYPE (parms), 0);
+           dbxout_finish_symbol (parms);
+         }
+       else if (GET_CODE (DECL_RTL (parms)) == MEM
+                && XEXP (DECL_RTL (parms), 0) != const0_rtx)
+         {
+           /* Parm was passed in registers but lives on the stack.  */
+
+           current_sym_code = N_PSYM;
+           /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))),
+              in which case we want the value of that CONST_INT,
+              or (MEM (REG ...)) or (MEM (MEM ...)),
+              in which case we use a value of zero.  */
+           if (GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG
+               || GET_CODE (XEXP (DECL_RTL (parms), 0)) == MEM)
+             current_sym_value = 0;
+           else
+             current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
+           current_sym_addr = 0;
+
+           FORCE_TEXT;
+           if (DECL_NAME (parms))
+             {
+               current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+               fprintf (asmfile, "%s \"%s:p", ASM_STABS_OP,
+                        IDENTIFIER_POINTER (DECL_NAME (parms)));
+             }
+           else
+             {
+               current_sym_nchars = 8;
+               fprintf (asmfile, "%s \"(anon):p", ASM_STABS_OP);
+             }
+
+           current_sym_value
+             = DEBUGGER_ARG_OFFSET (current_sym_value,
+                                    XEXP (DECL_RTL (parms), 0));
+           dbxout_type (TREE_TYPE (parms), 0);
+           dbxout_finish_symbol (parms);
+         }
+      }
+}
+
+/* Output definitions for the places where parms live during the function,
+   when different from where they were passed, when the parms were passed
+   in memory.
+
+   It is not useful to do this for parms passed in registers
+   that live during the function in different registers, because it is
+   impossible to look in the passed register for the passed value,
+   so we use the within-the-function register to begin with.
+
+   PARMS is a chain of PARM_DECL nodes.  */
+
+static void
+dbxout_reg_parms (parms)
+     tree parms;
+{
+  for (; parms; parms = TREE_CHAIN (parms))
+    if (DECL_NAME (parms))
+      {
+       dbxout_prepare_symbol (parms);
+
+       /* Report parms that live in registers during the function
+          but were passed in memory.  */
+       if (GET_CODE (DECL_RTL (parms)) == REG
+           && REGNO (DECL_RTL (parms)) >= 0
+           && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER
+           && PARM_PASSED_IN_MEMORY (parms))
+         {
+           current_sym_code = N_RSYM;
+           current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)));
+           current_sym_addr = 0;
+
+           FORCE_TEXT;
+           if (DECL_NAME (parms))
+             {
+               current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+               fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP,
+                        IDENTIFIER_POINTER (DECL_NAME (parms)));
+             }
+           else
+             {
+               current_sym_nchars = 8;
+               fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP);
+             }
+           dbxout_type (TREE_TYPE (parms), 0);
+           dbxout_finish_symbol (parms);
+         }
+       /* Report parms that live in memory but not where they were passed.  */
+       else if (GET_CODE (DECL_RTL (parms)) == MEM
+                && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+                && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
+                && PARM_PASSED_IN_MEMORY (parms)
+                && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms)))
+         {
+#if 0 /* ??? It is not clear yet what should replace this.  */
+           int offset = DECL_OFFSET (parms) / BITS_PER_UNIT;
+           /* A parm declared char is really passed as an int,
+              so it occupies the least significant bytes.
+              On a big-endian machine those are not the low-numbered ones.  */
+#if BYTES_BIG_ENDIAN
+           if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+             offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+                        - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+           if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...}
+#endif
+           current_sym_code = N_LSYM;
+           current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (parms), 0));
+           current_sym_addr = 0;
+           FORCE_TEXT;
+           if (DECL_NAME (parms))
+             {
+               current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+               fprintf (asmfile, "%s \"%s:", ASM_STABS_OP,
+                        IDENTIFIER_POINTER (DECL_NAME (parms)));
+             }
+           else
+             {
+               current_sym_nchars = 8;
+               fprintf (asmfile, "%s \"(anon):", ASM_STABS_OP);
+             }
+           dbxout_type (TREE_TYPE (parms), 0);
+           dbxout_finish_symbol (parms);
+         }
+      }
+}
+\f
+/* Given a chain of ..._TYPE nodes (as come in a parameter list),
+   output definitions of those names, in raw form */
+
+void
+dbxout_args (args)
+     tree args;
+{
+  while (args)
+    {
+      putc (',', asmfile);
+      dbxout_type (TREE_VALUE (args), 0);
+      CHARS (1);
+      args = TREE_CHAIN (args);
+    }
+}
+\f
+/* Given a chain of ..._TYPE nodes,
+   find those which have typedef names and output those names.
+   This is to ensure those types get output.  */
+
+void
+dbxout_types (types)
+     register tree types;
+{
+  while (types)
+    {
+      if (TYPE_NAME (types)
+         && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL
+         && ! TREE_ASM_WRITTEN (TYPE_NAME (types)))
+       dbxout_symbol (TYPE_NAME (types), 1);
+      types = TREE_CHAIN (types);
+    }
+}
+\f
+/* Output everything about a symbol block (a BLOCK node
+   that represents a scope level),
+   including recursive output of contained blocks.
+
+   BLOCK is the BLOCK node.
+   DEPTH is its depth within containing symbol blocks.
+   ARGS is usually zero; but for the outermost block of the
+   body of a function, it is a chain of PARM_DECLs for the function parameters.
+   We output definitions of all the register parms
+   as if they were local variables of that block.
+
+   If -g1 was used, we count blocks just the same, but output nothing
+   except for the outermost block.
+
+   Actually, BLOCK may be several blocks chained together.
+   We handle them all in sequence.  */
+
+static void
+dbxout_block (block, depth, args)
+     register tree block;
+     int depth;
+     tree args;
+{
+  int blocknum;
+
+  while (block)
+    {
+      /* Ignore blocks never expanded or otherwise marked as real.  */
+      if (TREE_USED (block))
+       {
+#ifndef DBX_LBRAC_FIRST
+         /* In dbx format, the syms of a block come before the N_LBRAC.  */
+         if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0)
+           dbxout_syms (BLOCK_VARS (block));
+         if (args)
+           dbxout_reg_parms (args);
+#endif
+
+         /* Now output an N_LBRAC symbol to represent the beginning of
+            the block.  Use the block's tree-walk order to generate
+            the assembler symbols LBBn and LBEn
+            that final will define around the code in this block.  */
+         if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE)
+           {
+             char buf[20];
+             blocknum = next_block_number++;
+             ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum);
+
+             if (BLOCK_HANDLER_BLOCK (block))
+               {
+                 /* A catch block.  Must precede N_LBRAC.  */
+                 tree decl = BLOCK_VARS (block);
+                 while (decl)
+                   {
+#ifdef DBX_OUTPUT_CATCH
+                     DBX_OUTPUT_CATCH (asmfile, decl, buf);
+#else
+                     fprintf (asmfile, "%s \"%s:C1\",%d,0,0,",
+                              ASM_STABS_OP,
+                              IDENTIFIER_POINTER (DECL_NAME (decl)), N_CATCH);
+                     assemble_name (asmfile, buf);
+                     fprintf (asmfile, "\n");
+#endif
+                     decl = TREE_CHAIN (decl);
+                   }
+               }
+
+             fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_LBRAC);
+             assemble_name (asmfile, buf);
+             fprintf (asmfile, "\n");
+           }
+         else if (depth > 0)
+           /* Count blocks the same way regardless of debug_info_level.  */
+           next_block_number++;
+
+#ifdef DBX_LBRAC_FIRST
+         /* On some weird machines, the syms of a block
+            come after the N_LBRAC.  */
+         if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0)
+           dbxout_syms (BLOCK_VARS (block));
+         if (args)
+           dbxout_reg_parms (args);
+#endif
+
+         /* Output the subblocks.  */
+         dbxout_block (BLOCK_SUBBLOCKS (block), depth + 1, 0);
+
+         /* Refer to the marker for the end of the block.  */
+         if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE)
+           {
+             char buf[20];
+             ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum);
+             fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_RBRAC);
+             assemble_name (asmfile, buf);
+             fprintf (asmfile, "\n");
+           }
+       }
+      block = BLOCK_CHAIN (block);
+    }
+}
+
+/* Output the information about a function and its arguments and result.
+   Usually this follows the function's code,
+   but on some systems, it comes before.  */
+
+static void
+dbxout_really_begin_function (decl)
+     tree decl;
+{
+  dbxout_symbol (decl, 0);
+  dbxout_parms (DECL_ARGUMENTS (decl));
+  if (DECL_NAME (DECL_RESULT (decl)) != 0)
+    dbxout_symbol (DECL_RESULT (decl), 1);
+}
+
+/* Called at beginning of output of function definition.  */
+
+void
+dbxout_begin_function (decl)
+     tree decl;
+{
+#ifdef DBX_FUNCTION_FIRST
+  dbxout_really_begin_function (decl);
+#endif
+}
+
+/* Output dbx data for a function definition.
+   This includes a definition of the function name itself (a symbol),
+   definitions of the parameters (locating them in the parameter list)
+   and then output the block that makes up the function's body
+   (including all the auto variables of the function).  */
+
+void
+dbxout_function (decl)
+     tree decl;
+{
+#ifndef DBX_FUNCTION_FIRST
+  dbxout_really_begin_function (decl);
+#endif
+  dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl));
+#ifdef DBX_OUTPUT_FUNCTION_END
+  DBX_OUTPUT_FUNCTION_END (asmfile, decl);
+#endif
+}
+\f
+#else /* not DBX_DEBUGGING_INFO */
+
+void
+dbxout_init (asm_file, input_file_name)
+     FILE *asm_file;
+     char *input_file_name;
+{}
+
+void
+dbxout_symbol (decl, local)
+     tree decl;
+     int local;
+{}
+
+void
+dbxout_types (types)
+     register tree types;
+{}
+
+void
+dbxout_function (decl)
+     tree decl;
+{}
+
+#endif /* DBX_DEBUGGING_INFO */