386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 17 Oct 1990 04:16:31 +0000 (20:16 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 17 Oct 1990 04:16:31 +0000 (20:16 -0800)
Work on file usr/src/usr.bin/gcc/cc1/dbxout.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/gcc/cc1/dbxout.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gcc/cc1/dbxout.c b/usr/src/usr.bin/gcc/cc1/dbxout.c
new file mode 100644 (file)
index 0000000..0cba1e3
--- /dev/null
@@ -0,0 +1,1201 @@
+/* Output dbx-format symbol table information from GNU compiler.
+   Copyright (C) 1987, 1988 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 1, 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 "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+#include <stdio.h>
+
+/* 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 "stab.h"  /* If doing DBX on sysV, use our own stab.h.  */
+#else
+#include <stab.h>  /* On BSD, use the system's stab.h.  */
+#endif /* not USG */
+
+/* Stream for writing to assembler file.  */
+
+static FILE *asmfile;
+
+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 int 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_tags ();
+void dbxout_args ();
+void dbxout_symbol ();
+static void dbxout_type_name ();
+static void dbxout_type ();
+static void dbxout_finish_symbol ();
+static void dbxout_continue ();
+\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)
+     FILE *asm_file;
+     char *input_file_name;
+{
+  asmfile = asm_file;
+
+  typevec_len = 100;
+  typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]);
+  bzero (typevec, typevec_len * sizeof typevec[0]);
+
+  /* Used to put `Ltext:' before the reference, but that loses on sun 4.  */
+  fprintf (asmfile,
+          "\t.stabs \"%s\",%d,0,0,Ltext\nLtext:\n",
+          input_file_name, N_SO);
+
+  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.  */
+
+  dbxout_symbol (TYPE_NAME (integer_type_node), 0);
+  dbxout_symbol (TYPE_NAME (char_type_node), 0);
+
+  /* Get all permanent types not yet gotten, and output them.  */
+
+  dbxout_types (get_permanent_types ());
+}
+
+/* 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 ();
+  fprintf (asmfile, ".stabs \"");
+  current_sym_nchars = 0;
+}
+\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_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:
+      /* 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.  */
+       fprintf (asmfile, "r2;0;127;");
+      else
+       /* Output other integer types as subranges of `int'.  */
+       fprintf (asmfile, "r1;%d;%d;",
+                TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)),
+                TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)));
+      CHARS (25);
+      break;
+
+    case REAL_TYPE:
+      /* This must be magic.  */
+      fprintf (asmfile, "r1;%d;0;",
+              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.  */
+      fprintf (asmfile, "ar1;0;%d;",
+              (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:
+      /* 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);
+      fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d",
+              TREE_INT_CST_LOW (tem));
+
+      if (TYPE_BASETYPES (type) && use_gdb_dbx_extensions)
+       {
+         putc ('!', asmfile);
+         putc ((TREE_PUBLIC (TYPE_BASETYPES (type)) ? '2' : '0'),
+               asmfile);
+         dbxout_type (TREE_VALUE (TYPE_BASETYPES (type)), 0);
+         putc (',', asmfile);
+         CHARS (3);
+       }
+      CHARS (11);
+
+      for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
+       /* Output the name, type, position (in bits), size (in bits)
+          of each field.  */
+       /* Omit here the nameless fields that are used to skip bits.  */
+       if (DECL_NAME (tem) != 0)
+         {
+           /* Continue the line if necessary,
+              but not before the first field.  */
+           if (tem != TYPE_FIELDS (type))
+             CONTIN;
+           fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem)));
+           CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem)));
+#ifdef TREE_PRIVATE
+           if (use_gdb_dbx_extensions
+               && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem)
+                   || TREE_CODE (tem) != FIELD_DECL))
+             {
+               putc ('/', asmfile);
+               putc ((TREE_PRIVATE (tem) ? '0'
+                      : TREE_PROTECTED (tem) ? '1' : '2'),
+                     asmfile);
+               CHARS (2);
+               if (TREE_CODE (tem) == FUNCTION_DECL)
+                 {
+                   putc (':', asmfile);
+                   CHARS (1);
+                   dbxout_type (TREE_TYPE (tem), 0); /* FUNCTION_TYPE */
+                   dbxout_args (TYPE_ARG_TYPES (TREE_TYPE (tem)));
+#ifdef TREE_VIRTUAL
+                   fprintf (asmfile, ":%s;%c", 
+                            XSTR (XEXP (DECL_RTL (tem), 0), 0),
+                            TREE_VIRTUAL (tem) ? '*' : '.');
+#endif
+                   CHARS (3 + strlen (XSTR (XEXP (DECL_RTL (tem), 0), 0)));
+                 }
+               else
+                 dbxout_type (TREE_TYPE (tem), 0);
+             }
+           else
+#endif
+             dbxout_type (TREE_TYPE (tem), 0);
+
+           if (TREE_CODE (tem) == VAR_DECL)
+             {
+               if (use_gdb_dbx_extensions)
+                 {
+                   fprintf (asmfile, ":%s", 
+                            XSTR (XEXP (DECL_RTL (tem), 0), 0));
+                   CHARS (2 + strlen (XSTR (XEXP (DECL_RTL (tem), 0), 0)));
+                 }
+               else
+                 {
+                   fprintf (asmfile, ",0,0;");
+                   CHARS (5);
+                 }
+             }
+           else
+             {
+               fprintf (asmfile, ",%d,%d;", DECL_OFFSET (tem),
+                        (TREE_INT_CST_LOW (DECL_SIZE (tem))
+                         * DECL_SIZE_UNIT (tem)));
+               CHARS (23);
+             }
+         }
+
+      putc (';', asmfile);
+      CHARS (1);
+      break;
+
+    case ENUMERAL_TYPE:
+      if ((TYPE_NAME (type) != 0 && !full)
+         || 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)
+       {
+         putc ('@', asmfile);
+         CHARS (1);
+         dbxout_type (TYPE_METHOD_BASETYPE (type), 0);
+         putc (',', asmfile);
+         CHARS (1);
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      else
+       {
+         /* Treat it as a function type.  */
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      break;
+
+    case OFFSET_TYPE:
+      if (use_gdb_dbx_extensions)
+       {
+         putc ('@', asmfile);
+         CHARS (1);
+         dbxout_type (TYPE_OFFSET_BASETYPE (type), 0);
+         putc (',', asmfile);
+         CHARS (1);
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      else
+       {
+         /* Treat it as a function type.  */
+         dbxout_type (TREE_TYPE (type), 0);
+       }
+      break;
+
+    case REFERENCE_TYPE:
+      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 ();
+    }
+}
+
+/* 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);
+
+  /* If global, first output all types and all
+     struct, enum and union tags that have been created
+     and not yet output.  */
+
+  if (local == 0)
+    {
+      dbxout_tags (gettags ());
+      dbxout_types (get_permanent_types ());
+    }
+
+  current_sym_code = 0;
+  current_sym_value = 0;
+  current_sym_addr = 0;
+
+  /* The output will always start with the symbol name,
+     so count that always in the length-output-so-far.  */
+
+  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;
+      if (GET_CODE (DECL_RTL (decl)) != MEM
+         || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
+       break;
+      FORCE_TEXT;
+      fprintf (asmfile, ".stabs \"%s:%c",
+              IDENTIFIER_POINTER (DECL_NAME (decl)),
+              TREE_PUBLIC (decl) ? 'F' : 'f');
+
+      current_sym_code = N_FUN;
+      current_sym_addr = XEXP (DECL_RTL (decl), 0);
+
+      if (TREE_TYPE (TREE_TYPE (decl)))
+       dbxout_type (TREE_TYPE (TREE_TYPE (decl)), 0);
+      else
+       dbxout_type (void_type_node, 0);
+      dbxout_finish_symbol ();
+      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.  */
+      if (TREE_ASM_WRITTEN (decl))
+       return;
+
+      /* Output typedef name.  */
+      FORCE_TEXT;
+      fprintf (asmfile, ".stabs \"%s:t",
+              IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+      current_sym_code = N_LSYM;
+
+      dbxout_type (TREE_TYPE (decl), 1);
+      dbxout_finish_symbol ();
+
+      /* 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 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;
+
+      /* Don't mention a variable at all
+        if it was completely optimized into nothingness.  */
+      if (GET_CODE (DECL_RTL (decl)) == REG
+         && (REGNO (DECL_RTL (decl)) < 0
+             || REGNO (DECL_RTL (decl)) >= FIRST_PSEUDO_REGISTER))
+       break;
+
+      /* 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_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 (GET_CODE (DECL_RTL (decl)) == REG)
+       {
+         letter = 'r';
+         current_sym_code = N_RSYM;
+         current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (decl)));
+       }
+      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 = INTVAL (XEXP (XEXP (XEXP (DECL_RTL (decl), 0), 0), 1));
+           }
+
+         /* 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 (POINTER_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 = 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 = INTVAL (XEXP (XEXP (DECL_RTL (decl), 0), 1));
+       }
+      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;
+      fprintf (asmfile, ".stabs \"%s:",
+              IDENTIFIER_POINTER (DECL_NAME (decl)));
+      if (letter) putc (letter, asmfile);
+      dbxout_type (type, 0);
+      dbxout_finish_symbol ();
+      break;
+    }
+}
+
+static void
+dbxout_finish_symbol ()
+{
+  fprintf (asmfile, "\",%d,0,0,", current_sym_code);
+  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_OFFSET (parms) >= 0)
+       {
+         current_sym_code = N_PSYM;
+         current_sym_value = DECL_OFFSET (parms) / BITS_PER_UNIT;
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+         FORCE_TEXT;
+         fprintf (asmfile, ".stabs \"%s:p",
+                  IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+         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
+           {
+             /* 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.  */
+#ifdef 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 = DECL_OFFSET (parms) / BITS_PER_UNIT;
+                 dbxout_type (DECL_ARG_TYPE (parms), 0);
+               }
+           }
+         dbxout_finish_symbol ();
+       }
+      /* Parm was passed in registers.
+        If it lives in a hard register, output a "regparm" symbol
+        for the register it lives in.  */
+      else if (GET_CODE (DECL_RTL (parms)) == REG
+              && REGNO (DECL_RTL (parms)) >= 0
+              && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+       {
+         current_sym_code = N_RSYM;
+         current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)));
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+         FORCE_TEXT;
+         fprintf (asmfile, ".stabs \"%s:P",
+                  IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+         dbxout_type (DECL_ARG_TYPE (parms), 0);
+         dbxout_finish_symbol ();
+       }
+      else if (GET_CODE (DECL_RTL (parms)) == MEM
+              && XEXP (DECL_RTL (parms), 0) != const0_rtx)
+       {
+         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 = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+         FORCE_TEXT;
+         fprintf (asmfile, ".stabs \"%s:p",
+                  IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+#if 0 /* This is actually the case in which a parameter
+        is passed in registers but lives on the stack in a local slot.
+        The address we are using is already correct, so don't change it.  */
+
+         /* 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.  */
+#ifdef 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
+#endif /* 0 */
+
+         dbxout_type (TREE_TYPE (parms), 0);
+         dbxout_finish_symbol ();
+       }
+    }
+}
+
+/* Output definitions, referring to registers,
+   of all the parms in PARMS which are stored in registers during the function.
+   PARMS is a chain of PARM_DECL nodes.  */
+
+static void
+dbxout_reg_parms (parms)
+     tree parms;
+{
+  while (parms)
+    {
+      /* Report parms that live in registers during the function.  */
+      if (GET_CODE (DECL_RTL (parms)) == REG
+         && REGNO (DECL_RTL (parms)) >= 0
+         && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER
+         && DECL_OFFSET (parms) >= 0)
+       {
+         current_sym_code = N_RSYM;
+         current_sym_value = DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)));
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+         FORCE_TEXT;
+         fprintf (asmfile, ".stabs \"%s:r",
+                  IDENTIFIER_POINTER (DECL_NAME (parms)));
+         dbxout_type (TREE_TYPE (parms), 0);
+         dbxout_finish_symbol ();
+       }
+      /* Report parms that live in memory but outside the parmlist.  */
+      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)
+       {
+         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.  */
+#ifdef 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)
+           {
+             current_sym_code = N_LSYM;
+             current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
+             current_sym_addr = 0;
+             current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+             FORCE_TEXT;
+             fprintf (asmfile, ".stabs \"%s:",
+                      IDENTIFIER_POINTER (DECL_NAME (parms)));
+             dbxout_type (TREE_TYPE (parms), 0);
+             dbxout_finish_symbol ();
+           }
+       }
+      parms = TREE_CHAIN (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);
+    }
+}
+
+/* Output the tags (struct, union and enum definitions with names) for a block,
+   given a list of them (a chain of TREE_LIST nodes) in TAGS.
+   We must check to include those that have been mentioned already with
+   only a cross-reference.  */
+
+void
+dbxout_tags (tags)
+     tree tags;
+{
+  register tree link;
+  for (link = tags; link; link = TREE_CHAIN (link))
+    {
+      register tree type = TYPE_MAIN_VARIANT (TREE_VALUE (link));
+      if (TREE_PURPOSE (link) != 0
+         && ! TREE_ASM_WRITTEN (link)
+         && TYPE_SIZE (type) != 0)
+       {
+         TREE_ASM_WRITTEN (link) = 1;
+         current_sym_code = N_LSYM;
+         current_sym_value = 0;
+         current_sym_addr = 0;
+         current_sym_nchars = 2 + IDENTIFIER_LENGTH (TREE_PURPOSE (link));
+
+         FORCE_TEXT;
+         fprintf (asmfile, ".stabs \"%s:T",
+                  IDENTIFIER_POINTER (TREE_PURPOSE (link)));
+         dbxout_type (type, 1);
+         dbxout_finish_symbol ();
+       }
+    }
+}
+\f
+/* Output everything about a symbol block (that is to say, a LET_STMT node
+   that represents a scope level),
+   including recursive output of contained blocks.
+
+   STMT is the LET_STMT 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.
+
+   Actually, STMT may be several statements chained together.
+   We handle them all in sequence.  */
+
+static void
+dbxout_block (stmt, depth, args)
+     register tree stmt;
+     int depth;
+     tree args;
+{
+  int blocknum;
+
+  while (stmt)
+    {
+      switch (TREE_CODE (stmt))
+       {
+       case COMPOUND_STMT:
+       case LOOP_STMT:
+         dbxout_block (STMT_BODY (stmt), depth, 0);
+         break;
+
+       case IF_STMT:
+         dbxout_block (STMT_THEN (stmt), depth, 0);
+         dbxout_block (STMT_ELSE (stmt), depth, 0);
+         break;
+
+       case LET_STMT:
+         /* Ignore LET_STMTs for blocks never really used to make RTL.  */
+         if (! TREE_USED (stmt))
+           break;
+         /* In dbx format, the syms of a block come before the N_LBRAC.  */
+         dbxout_tags (STMT_TYPE_TAGS (stmt));
+         dbxout_syms (STMT_VARS (stmt));
+         if (args)
+           dbxout_reg_parms (args);
+
+         /* 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)
+           {
+             char buf[20];
+             blocknum = next_block_number++;
+             ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum);
+             fprintf (asmfile, ".stabn %d,0,0,", N_LBRAC);
+             assemble_name (asmfile, buf);
+             fprintf (asmfile, "\n");
+           }
+
+         /* Output the subblocks.  */
+         dbxout_block (STMT_SUBBLOCKS (stmt), depth + 1, 0);
+
+         /* Refer to the marker for the end of the block.  */
+         if (depth > 0)
+           {
+             char buf[20];
+             ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum);
+             fprintf (asmfile, ".stabn %d,0,0,", N_RBRAC);
+             assemble_name (asmfile, buf);
+             fprintf (asmfile, "\n");
+           }
+       }
+      stmt = TREE_CHAIN (stmt);
+    }
+}
+
+/* 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;
+{
+  dbxout_symbol (decl, 0);
+  dbxout_parms (DECL_ARGUMENTS (decl));
+  dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl));
+  
+  /* If we made any temporary types in this fn that weren't
+     output, output them now.  */
+  dbxout_types (get_temporary_types ());
+}
+\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_tags (tags)
+     tree tags;
+{}
+
+void
+dbxout_function (decl)
+     tree decl;
+{}
+
+#endif /* DBX_DEBUGGING_INFO */