From: Richard Stallman Date: Sat, 15 Feb 1992 21:24:33 +0000 (+0000) Subject: Initial revision X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=00fe048c3cda113284df716a5fbb502253233359;p=gcc.git Initial revision From-SVN: r327 --- diff --git a/gcc/dbxout.c b/gcc/dbxout.c new file mode 100644 index 00000000000..feff991c059 --- /dev/null +++ b/gcc/dbxout.c @@ -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 +#include +#include + +#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 /* 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 (); + +#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 */ + +/* 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, <ext_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, <ext_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, <ext_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; +} + +/* 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); + } + } +} + +/* 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); + } +} + +/* 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); + } +} + +/* 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)); +} + +/* 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); + } +} + +/* 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); + } + } +} + +/* 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); + } +} + +/* 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 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 +} + +#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 */