home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Education Sampler 1992 [NeXTSTEP]
/
Education_1992_Sampler.iso
/
NeXT
/
GnuSource
/
cc-61.0.1
/
cc
/
dwarfout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-03
|
111KB
|
3,651 lines
/* This file contains code written by Ron Guilmette (rfg@ncd.com) for
Network Computing Devices, August, September, October, November 1990.
Output DWARF format symbol table information from the GNU C compiler.
Copyright (C) 1990, 1991 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. */
#include "config.h"
#ifdef DWARF_DEBUGGING_INFO
#include <stdio.h>
#include <dwarf.h>
#include "tree.h"
#include "flags.h"
#include "rtl.h"
#include "output.h"
#define NDEBUG 1
#include <assert.h>
#ifdef USG
#include <string.h>
#else
#include <strings.h>
#endif
#if 0 /* Probably not needed, now that we don't want MAXPATHLEN. */
#include <sys/param.h>
#endif
/* IMPORTANT NOTE: Please see the file README.DWARF for important details
regarding the GNU implementation of DWARF. */
/* NOTE: In the comments in this file, many references are made to
so called "Debugging Information Entries". For the sake of brevity,
this term is abbreviated to `DIE' throughout the remainder of this
file. */
/* Define GPLUSPLUS if this file is being used with the g++ compiler. Note
that the implementation of C++ support herein is (as yet) unfinished.
If you want to use it, you're on your own. */
/* Define DWARF_DESCRIBE_USED_EXTERNS if you want the GNU DWARF produced
by the code in this file to include descriptions of things (i.e. data
objects and functions) which are explicitly declared as `extern' and
which are also referenced within the current compilation. This optional
feature will only be useful if you have a `smart linker' which is capable
of doing type checking across object files (based upon DWARF type
specifications).
When this option is enabled, the descriptions of the external items
are placed into the (special) section called `.externs'.
Please note that DWARF_DESCRIBE_USED_EXTERNS cannot be enabled unless
you have definitions for the attribute symbols AT_file, AT_line, and
AT_src_info in your dwarf.h file.
*/
/* Other extended features of GNU DWARF are enabled (as described below)
based upon the presence of #defines for various GNU DWARF extended
attributes in your dwarf.h file. To disable these features, you will
have to either remove the applicable #defines from your dwarf.h file
or else you will have to #undef the relevant extended attribute names
somewhere (early) within this file.
*/
/* If the symbols AT_file and AT_line are defined, then the DWARF debugging
information produced by the code herein will include additional attribute
information which specifies the source file and source line for all kinds
of named entities.
There are two cases in which the presence of these extended attributes
(in the DWARF debugging information) may be useful.
First, as noted above in the description of the DWARF_DESCRIBE_USED_EXTERNS
option, if you have a `smart linker' which can do cross-module type
checking (based upon DWARF typing information) then the presence of AT_file
and AT_line attributes can give the `smart linker' the ability to pinpoint
(for the user) the file and line where a conflicting declaration of some
named entity occurs.
Second, the presence of AT_file and AT_line attributes may be useful if
your debugger supports a command which will tell the user the exact
source location associated with a declaration (or definition) of some
named entity.
Please note that in order for these additional attributes to be produced,
the symbol AT_src_info must also be defined in your dwarf.h file.
*/
/* If the symbol AT_src_info is defined in your dwarf.h file, then the DWARF
debugging information produced by the code herein will include additional
information (in the .srcfile and .srcinfo sections) giving the names of
all source files which contributed one or more lines of executable code
to the current compilation unit.
It is STRONGLY recommended that this extended GNU DWARF feature always
be used because (a) it typically takes up very little additional space
in the object file and because (b) without it there is insufficient
information available to the debugger to allow it to understand and know
about code which was brought into the current compilation unit from an
include file.
See the README.DWARF file for more information about AT_src_info attributes.
*/
/* If the symbol AT_comp_dir is defined in your dwarf.h file, then the DWARF
debugging information produced by the code herein will include one
additional attribute in each TAG_source_file DIE, namely the AT_comp_dir
attribute. This is a string valued attribute which specifies the name
of the directory from whence the compiler was invoked when the compilation
unit in question was compiled.
*/
/* If the symbol AT_addr_ranges is defined within your dwarf.h file, then the
DWARF debugging information produced by the code herein will include one
additional attribute in each TAG_source_file DIE, namely the AT_addr_ranges
attribute.
The AT_addr_ranges attribute is a FORM_BLOCK2 attribute whose value is a
set of low/high pairs of addresses. Each pair of addresses consists of
two 32-bit numbers. In each pair, the first of the two numbers is the
low address of the range and the second one is the high address of the
range plus one. When present (as part of an AT_addr_ranges attribute)
these address pairs must give the address ranges for ALL of the sections
of the compilation unit which can contain entities (e.g. functions and
data objects) which were declared by the user. This usually includes the
.text, .data, .bss, and .rodata sections.
When present, at AT_addr_ranges attribute for a compilation unit can aid
the debugger in quickly finding the debug information for some named
entity. Basically, when forced to lookup a name, the debugger can first
lookup the name in the linker symbol table (of the executable file) and
then, armed with the named entity's corresponding address (from the
linker symbol table), the debugger can quickly find out which compilation
unit contains the debugging information for the named entity.
*/
/* If the symbol AT_prototyped is defined within your dwarf.h file, then the
DWARF debugging information produced by the code herein will include one
additional attribute (namely the AT_prototyped attribute) for each
TAG_global_subroutine, TAG_inline_subroutine, TAG_member_function,
TAG_subroutine, or TAG_subroutine_type which is associated with an
entity for which a formal parameter list in prototype form was given.
The AT_prototyped attribute is a flag attribute, and it has the form
FORM_NONE.
This information may be essential to the debugger if the debugger allows
the user to make calls to functions which are part of the process being
debugged (as GDB does). The reason that this information is important
is that when such calls are made to prototyped functions, the debugger
must properly coerce the types of any passed parameters to the exact
types listed in the function's prototype.
*/
#if defined(AT_file) && defined(AT_line)
#ifndef AT_src_info
You need the AT_src_info attribute if you have AT_file and AT_line attributes!
#endif
#define HAVE_SRC_LOCATION_ATTRIBUTES
#endif
#if defined(DWARF_DESCRIBE_USED_EXTERNS) && !defined (HAVE_SRC_LOCATION_ATTRIBUTES)
You need the AT_file and AT_line attributes to describe used externs!
#endif
#ifndef USG
#define getcwd(s,len) getwd(s)
#endif
#ifndef FT_long_long
#define FT_long_long 0x0015
#endif
#ifndef FT_signed_long_long
#define FT_signed_long_long 0x0016
#endif
#ifndef FT_unsigned_long_long
#define FT_unsigned_long_long 0x0017
#endif
#define TREE_UID(TREE) ((unsigned) (TREE))
#ifndef CLASSTYPE_BASECLASS
#define CLASSTYPE_BASECLASS(classtype, index) ((tree) 0)
#endif
#ifndef CLASSTYPE_N_BASECLASSES
#define CLASSTYPE_N_BASECLASSES(classtype) 0
#endif
#define BITFIELD_OFFSET_BITS(DECL) (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (DECL)))
#define BITFIELD_OFFSET_UNITS(DECL) (BITFIELD_OFFSET_BITS(DECL) / BITS_PER_UNIT)
#define BITFIELD_OFFSET_WORDS_IN_UNITS(DECL) \
((BITFIELD_OFFSET_BITS(DECL) / BITS_PER_WORD) * UNITS_PER_WORD)
#if 0 /* No good: this variable is C-specific and may not exist in other
languages. */
/* External data objects defined in other files of GCC. */
extern tree named_labels; /* From c-decl.c */
#endif
/* Maximum size (in bytes) of an artificially generated label. */
#define MAX_ARTIFICIAL_LABEL_BYTES 30
#ifndef DWARF_REGISTER_NUMBER
#define DWARF_REGISTER_NUMBER(REGNO) DBX_REGISTER_NUMBER(REGNO)
#endif
/* Make sure we know the sizes of the various types dwarf can describe. */
#ifndef CHAR_TYPE_SIZE
#define CHAR_TYPE_SIZE BITS_PER_UNIT
#endif
#ifndef SHORT_TYPE_SIZE
#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2))
#endif
#ifndef INT_TYPE_SIZE
#define INT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_TYPE_SIZE
#define LONG_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_LONG_TYPE_SIZE
#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
#endif
#ifndef WCHAR_UNSIGNED
#define WCHAR_UNSIGNED 0
#endif
#ifndef FLOAT_TYPE_SIZE
#define FLOAT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef DOUBLE_TYPE_SIZE
#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef LONG_DOUBLE_TYPE_SIZE
#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifdef AT_src_info
/* Structure to keep track of source filenames. */
struct filename_entry
{
unsigned number;
char * name;
};
typedef struct filename_entry filename_entry_t;
/* Pointer to an array of elements, each one having the structure above. */
static filename_entry_t *filename_table = 0;
/* Total number of entries in the table (i.e. array) pointed to by
`filename_table'. This is the *total* and includes both used and
unused slots. */
static unsigned total_ft_entries = 0;
/* Number of entries in the table pointed to by `filename_table' which are
actually in use at the moment. The used entries always start with
the zero'th element of the table and range up to (valid_ft_entries-1). */
static unsigned valid_ft_entries = 0;
#endif
/* Pointer to a list of TREE_LIST nodes, each of whose TREE_VALUEs is either
NULL (indicating a scope boundary marker) or else points to a pending
STRUCT_TYPE, UNION_TYPE, or ENUMERATION_TYPE node which has yet to be
described (with DWARF info) for some pending scope. */
static tree pending_tagged_types = 0;
#ifdef DWARF_DESCRIBE_USED_EXTERNS
/* Pointer to a list of TREE_LIST nodes, each of whose TREE_VALUEs is a
pointer to either a VAR_DECL node or a FUNCTION_DECL node. This list
is maintained so that, at the end of compilation, we can write DWARF
descriptions of these (used) extern declarations to the .externs
section. */
static tree pending_extern_decls = 0;
/* Flag which says whether or not we are generating entries in the .externs
section for variables and function declared external. */
static int finalizing = FALSE;
#endif
/* Pointer to the most recent filename for which we produced some line info. */
static char *last_filename;
/* For DWARF output, we must assign lexical-blocks id numbers
in the order in which their beginnings are encountered.
We output DWARF debugging info that refers to the beginnings
and ends of the ranges of code for each lexical block with
assembler labels ..Bn and ..Bn.e, where n is the block number.
The labels themselves are generated in final.c, which assigns
numbers to the blocks in the same way. */
static int next_block_number = 1;
/* Counter to generate unique names for DIEs. */
static unsigned next_unused_dienum = 1;
/* Number of the DIE which is currently being generated. */
static unsigned current_dienum;
#define NEXT_DIE_NUM pending_sibling_stack[pending_sibling_stack_depth-1]
/* Counter to keep track of the number of pre-reserved and still pending
sibling DIE numbers. */
static unsigned pending_sibling_stack_depth = 0;
/* Pointer to a dynamically allocated list of pre-reserved and still
pending sibling DIE numbers. Note that this list will grow as needed. */
static unsigned *pending_sibling_stack;
/* The currently allocated size of the above list (expressed in number of
list elements). */
static unsigned pending_sibling_stack_size = 0;
/* Size (in elements) of increments by which we may expand the pending
sibling stack. Actually, a single hunk of space of this size should
be enough for most typical programs. */
#define PENDING_SIBLING_STACK_INCREMENT 64
/* Forward declarations for functions defined in this file. */
static void output_dies_for_type ();
static void type_attribute ();
static void output_symbol ();
static unsigned lookup_filename ();
/* Definitions of defaults for assembler-dependent names of various
pseudo-ops and section names.
Theses may be overridden in your tm.h file (if necessary) for your
particular assembler. The default values provided here correspond to
what is expected by "standard" AT&T System V.4 assemblers. */
#ifndef FILE_ASM_OP
#define FILE_ASM_OP "\t.file"
#endif
#ifndef VERSION_ASM_OP
#define VERSION_ASM_OP "\t.version"
#endif
#ifndef SECTION_ASM_OP
#define SECTION_ASM_OP "\t.section"
#endif
#ifndef PREVIOUS_ASM_OP
#define PREVIOUS_ASM_OP "\t.previous"
#endif
#ifndef CHAR_ASM_OP
#define CHAR_ASM_OP "\t.byte"
#endif
#ifndef UNALIGNED_SHORT_ASM_OP
#define UNALIGNED_SHORT_ASM_OP "\t.2byte"
#endif
#ifndef UNALIGNED_INT_ASM_OP
#define UNALIGNED_INT_ASM_OP "\t.4byte"
#endif
#ifndef DEF_ASM_OP
#define DEF_ASM_OP "\t.set"
#endif
#ifndef DEBUG_SECTION
#define DEBUG_SECTION ".debug"
#endif
#ifndef LINE_SECTION
#define LINE_SECTION ".line"
#endif
#ifdef AT_src_info
#ifndef SOURCES_SECTION
#define SOURCES_SECTION ".sources"
#endif
#ifndef SRCINFO_SECTION
#define SRCINFO_SECTION ".srcinfo"
#endif
#endif
#ifdef AT_addr_ranges
#ifndef DATA_SECTION
#define DATA_SECTION ".data"
#endif
#ifndef BSS_SECTION
#define BSS_SECTION ".bss"
#endif
#ifndef RODATA_SECTION
#define RODATA_SECTION ".rodata"
#endif
#endif
#ifdef DWARF_DESCRIBE_USED_EXTERNS
#ifndef EXTERNS_SECTION
#define EXTERNS_SECTION ".externs"
#endif
#endif
/* Definitions of defaults for formats and names of various special
(artificial) labels which may be generated within this file (when
the -g options is used and DWARF_DEBUGGING_INFO is in effect.
If necessary, these may be overridden from within your tm-*.h file,
but typically, you should never need to override these. */
#ifndef TEXT_BEGIN_LABEL
#define TEXT_BEGIN_LABEL "..text.b"
#endif
#ifndef TEXT_END_LABEL
#define TEXT_END_LABEL "..text.e"
#endif
#ifndef LINE_BEGIN_LABEL
#define LINE_BEGIN_LABEL "..line.b"
#endif
#ifndef LINE_END_LABEL
#define LINE_END_LABEL "..line.e"
#endif
#ifdef AT_src_info
#ifndef SRCINFO_BEGIN_LABEL
#define SRCINFO_BEGIN_LABEL "..srcinfo.b"
#endif
#endif
#ifdef AT_addr_ranges
#ifndef DATA_BEGIN_LABEL
#define DATA_BEGIN_LABEL "..data.b"
#endif
#ifndef DATA_END_LABEL
#define DATA_END_LABEL "..data.e"
#endif
#ifndef BSS_BEGIN_LABEL
#define BSS_BEGIN_LABEL "..bss.b"
#endif
#ifndef BSS_END_LABEL
#define BSS_END_LABEL "..bss.e"
#endif
#ifndef RODATA_BEGIN_LABEL
#define RODATA_BEGIN_LABEL "..rodata.b"
#endif
#ifndef RODATA_END_LABEL
#define RODATA_END_LABEL "..rodata.e"
#endif
#endif
#ifndef DIE_BEGIN_LABEL_FMT
#define DIE_BEGIN_LABEL_FMT "..D%u"
#endif
#ifndef DIE_END_LABEL_FMT
#define DIE_END_LABEL_FMT "..D%u.e"
#endif
#ifndef INSN_LABEL_FMT
#define INSN_LABEL_FMT "..I%u.%u"
#endif
#ifndef BLOCK_BEGIN_LABEL_FMT
#define BLOCK_BEGIN_LABEL_FMT "..B%u"
#endif
#ifndef BLOCK_END_LABEL_FMT
#define BLOCK_END_LABEL_FMT "..B%u.e"
#endif
#ifndef SS_BEGIN_LABEL_FMT
#define SS_BEGIN_LABEL_FMT "..s%u"
#endif
#ifndef SS_END_LABEL_FMT
#define SS_END_LABEL_FMT "..s%u.e"
#endif
#ifndef EE_BEGIN_LABEL_FMT
#define EE_BEGIN_LABEL_FMT "..e%u"
#endif
#ifndef EE_END_LABEL_FMT
#define EE_END_LABEL_FMT "..e%u.e"
#endif
#ifndef MT_BEGIN_LABEL_FMT
#define MT_BEGIN_LABEL_FMT "..t%u"
#endif
#ifndef MT_END_LABEL_FMT
#define MT_END_LABEL_FMT "..t%u.e"
#endif
#ifndef LOC_BEGIN_LABEL_FMT
#define LOC_BEGIN_LABEL_FMT "..l%u"
#endif
#ifndef LOC_END_LABEL_FMT
#define LOC_END_LABEL_FMT "..l%u.e"
#endif
#ifndef BOUND_BEGIN_LABEL_FMT
#define BOUND_BEGIN_LABEL_FMT "..b%u.%u.%c"
#endif
#ifndef BOUND_END_LABEL_FMT
#define BOUND_END_LABEL_FMT "..b%u.%u.%c.e"
#endif
#ifndef DERIV_BEGIN_LABEL_FMT
#define DERIV_BEGIN_LABEL_FMT "..d%u"
#endif
#ifndef DERIV_END_LABEL_FMT
#define DERIV_END_LABEL_FMT "..d%u.e"
#endif
#ifndef FUNC_END_LABEL_FMT
#define FUNC_END_LABEL_FMT "..f%u.pchi"
#endif
#ifndef TYPE_NAME_FMT
#define TYPE_NAME_FMT "..T%u"
#endif
#ifndef LINE_CODE_LABEL_FMT
#define LINE_CODE_LABEL_FMT "..LC%u"
#endif
#ifdef AT_src_info
#ifndef FILE_ENTRY_LABEL_FMT
#define FILE_ENTRY_LABEL_FMT "..F%u"
#endif
#ifndef LINE_ENTRY_LABEL_FMT
#define LINE_ENTRY_LABEL_FMT "..LE%u"
#endif
#endif
/* Definitions of defaults for various types of primitive assembly language
output operations.
If necessary, these may be overridden from within your tm-*.h file,
but typically, you should never need to override these. */
#ifndef ASM_OUTPUT_SOURCE_FILENAME
#define ASM_OUTPUT_SOURCE_FILENAME(FILE,NAME) \
fprintf ((FILE), "%s\t\"%s\"\n", FILE_ASM_OP, NAME)
#endif
#ifndef ASM_OUTPUT_DEF
#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \
fprintf ((FILE), "%s\t%s,%s\n", DEF_ASM_OP, LABEL1, LABEL2)
#endif
#ifndef ASM_CHANGE_SECTION
#define ASM_CHANGE_SECTION(FILE,SECTION_NAME) \
fprintf ((FILE), "%s\t%s\n", SECTION_ASM_OP, SECTION_NAME)
#endif
#ifndef ASM_PREVIOUS_SECTION
#define ASM_PREVIOUS_SECTION(FILE) \
fprintf ((FILE), "%s\n", PREVIOUS_ASM_OP)
#endif
#ifndef ASM_OUTPUT_DWARF_BLOCK2
#define ASM_OUTPUT_DWARF_BLOCK2(FILE,LABEL1,LABEL2) \
fprintf ((FILE), "%s\t%s-%s\n", UNALIGNED_SHORT_ASM_OP, LABEL1, LABEL2)
#endif
#ifndef ASM_OUTPUT_DWARF_BLOCK4
#define ASM_OUTPUT_DWARF_BLOCK4(FILE,LABEL1,LABEL2) \
fprintf ((FILE), "%s\t%s-%s\n", UNALIGNED_INT_ASM_OP, LABEL1, LABEL2)
#endif
#ifndef ASM_OUTPUT_DWARF_TAG
#define ASM_OUTPUT_DWARF_TAG(FILE,TAG) \
fprintf ((FILE), "%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, TAG)
#endif
#ifndef ASM_OUTPUT_DWARF_ATTRIBUTE
#define ASM_OUTPUT_DWARF_ATTRIBUTE(FILE,ATTRIBUTE) \
fprintf ((FILE), "%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, ATTRIBUTE)
#endif
#ifndef ASM_OUTPUT_DWARF_ADDR
#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \
fprintf ((FILE), "%s\t%s\n", UNALIGNED_INT_ASM_OP, LABEL)
#endif
#ifndef ASM_OUTPUT_DWARF_ADDR_CONST
#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \
fprintf ((FILE), "%s\t", UNALIGNED_INT_ASM_OP); \
output_addr_const ((FILE), (RTX)); \
fputc ('\n', (FILE))
#endif
#ifndef ASM_OUTPUT_DWARF_REF
#define ASM_OUTPUT_DWARF_REF(FILE,LABEL) \
fprintf ((FILE), "%s\t%s\n", UNALIGNED_INT_ASM_OP, LABEL)
#endif
#ifndef ASM_OUTPUT_DWARF_DATA1
#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \
fprintf ((FILE), "%s\t0x%x\n", CHAR_ASM_OP, VALUE)
#endif
#ifndef ASM_OUTPUT_DWARF_DATA2
#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \
fprintf ((FILE), "%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, VALUE)
#endif
#ifndef ASM_OUTPUT_DWARF_DATA4
#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \
fprintf ((FILE), "%s\t0x%x\n", UNALIGNED_INT_ASM_OP, VALUE)
#endif
#ifndef ASM_OUTPUT_DWARF_DATA8
#define ASM_OUTPUT_DWARF_DATA8(FILE,VALUE) \
fprintf ((FILE), "%s\t0x%x\n", UNALIGNED_8BYTE_ASM_OP, VALUE)
#endif
#ifndef ASM_OUTPUT_DWARF_STRING
#define ASM_OUTPUT_DWARF_STRING(FILE,P) \
ASM_OUTPUT_ASCII ((FILE), P, strlen (P)+1)
#endif
/************************ general utility functions **************************/
static char *
xstrdup (s)
char *s;
{
char *p = (char *) xmalloc (strlen (s) + 1);
strcpy (p, s);
return p;
}
/**************** utility functions for attribute functions ******************/
/* Given a pointer to a tree node for some type, return a DWARF fundamental
type code for the given type.
This routine must only be called for GCC type nodes that correspond to
DWARF fundamental types.
Note that in some non-GNU implementations of DWARF, there may be an
attempt made to promulgate the (incorrect) notion that an integral
type which is explicitly declared as `signed' and an (otherwise
identical) `plain' integral are somehow different. In fact, ANSI C
and GCC believe that they are *not* different types, but rather are
exactly the same type. (Note that there is an exception in case of
`char' types, and that a `char' and a `signed char' are in fact
different.)
Here, we rely on the ANSI C and GCC interpretation. Thus, this routine
*never* returns any of the three (bogus) fundamental type codes
FT_signed_integer, FT_signed_long, or FT_signed_short. */
static int
fundamental_type_code (type)
register tree type;
{
type = TYPE_MAIN_VARIANT (type);
switch (TREE_CODE (type))
{
case ERROR_MARK:
return FT_void;
case VOID_TYPE:
return FT_void;
case INTEGER_TYPE:
/* Carefully distinguish all the standard types of C,
without messing up if the language is not C.
Note that we check only for the names that contain spaces;
other names might occur by coincidence in other languages. */
if (TYPE_NAME (type) != 0
&& TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& DECL_NAME (TYPE_NAME (type)) != 0
&& TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE)
{
char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
if (!strcmp (name, "unsigned char"))
return FT_unsigned_char;
if (!strcmp (name, "signed char"))
return FT_signed_char;
if (!strcmp (name, "unsigned int"))
return FT_unsigned_integer;
if (!strcmp (name, "short int"))
return FT_short;
if (!strcmp (name, "short unsigned int"))
return FT_unsigned_short;
if (!strcmp (name, "long int"))
return FT_long;
if (!strcmp (name, "long unsigned int"))
return FT_unsigned_long;
if (!strcmp (name, "long long int"))
return FT_long_long;
if (!strcmp (name, "long long unsigned int"))
return FT_unsigned_long_long;
}
/* Most integer types will be sorted out above, however, for the
sake of special `array index' integer types, the following code
is also provided. */
if (TYPE_PRECISION (type) == INT_TYPE_SIZE)
return (TREE_UNSIGNED (type) ? FT_unsigned_integer : FT_integer);
if (TYPE_PRECISION (type) == LONG_TYPE_SIZE)
return (TREE_UNSIGNED (type) ? FT_unsigned_long : FT_long);
if (TYPE_PRECISION (type) == LONG_LONG_TYPE_SIZE)
return (TREE_UNSIGNED (type) ? FT_unsigned_long_long : FT_long_long);
if (TYPE_PRECISION (type) == SHORT_TYPE_SIZE)
return (TREE_UNSIGNED (type) ? FT_unsigned_short : FT_short);
if (TYPE_PRECISION (type) == CHAR_TYPE_SIZE)
return (TREE_UNSIGNED (type) ? FT_unsigned_char : FT_char);
abort ();
case REAL_TYPE:
/* Carefully distinguish all the standard types of C,
without messing up if the language is not C. */
if (TYPE_NAME (type) != 0
&& TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& DECL_NAME (TYPE_NAME (type)) != 0
&& TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE)
{
char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
if (!strcmp (name, "long double"))
return FT_ext_prec_float;
}
if (TYPE_PRECISION (type) == DOUBLE_TYPE_SIZE)
return FT_dbl_prec_float;
if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
return FT_float;
if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE)
return FT_ext_prec_float;
abort ();
case COMPLEX_TYPE:
return FT_complex; /* GNU FORTRAN COMPLEX type. */
case CHAR_TYPE:
return FT_char; /* GNU Pascal CHAR type. Not used in C. */
case SET_TYPE:
return FT_set; /* GNU Pascal SET type. */
case BOOLEAN_TYPE:
return FT_integer; /* GNU FORTRAN BOOLEAN type. */
case FILE_TYPE: /* GNU Pascal FILE type. */
case STRING_TYPE: /* GNU Pascal/FORTRAN/Ada STRING type. */
case OFFSET_TYPE: /* GNU C++ member pointer type. */
case LANG_TYPE: /* GNU generic language-specific type. */
default:
abort (); /* No DWARF representation currently defined. */
}
}
/* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to
the DWARF "root" type for the given input type. The DWARF "root" type
of a given type is generally the same as the given type, except that if
the given type is a pointer or reference type, then the root type of
that type is the root type of the referenced type for the pointer or
reference type. (This definition of the "root" type is recursive.) */
static tree
root_type (type)
register tree type;
{
type = TYPE_MAIN_VARIANT (type);
switch (TREE_CODE (type))
{
case POINTER_TYPE:
case REFERENCE_TYPE:
return root_type (TREE_TYPE (type));
default:
return type;
}
}
/* Given a pointer to an arbitrary ..._TYPE tree node, write out a sequence
of zero or more DWARF "type-modifier" bytes applicable to the type. */
static void
write_modifier_bytes (type)
register tree type;
{
type = TYPE_MAIN_VARIANT (type);
switch (TREE_CODE (type))
{
case POINTER_TYPE:
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, MOD_pointer_to);
write_modifier_bytes (TREE_TYPE (type));
return;
case REFERENCE_TYPE:
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, MOD_reference_to);
write_modifier_bytes (TREE_TYPE (type));
return;
default:
return;
}
}
/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the
given input type is a DWARF "fundamental" type. Otherwise return zero. */
static int
type_is_fundamental (type)
register tree type;
{
switch (TREE_CODE (type))
{
case ERROR_MARK:
case VOID_TYPE:
case INTEGER_TYPE:
case REAL_TYPE:
case COMPLEX_TYPE:
case BOOLEAN_TYPE:
case CHAR_TYPE:
case SET_TYPE:
return 1;
case ARRAY_TYPE:
case RECORD_TYPE:
case UNION_TYPE:
case ENUMERAL_TYPE:
case FUNCTION_TYPE:
case METHOD_TYPE:
case POINTER_TYPE:
case REFERENCE_TYPE:
return 0;
case FILE_TYPE: /* No DWARF fundamental type code defined. */
case STRING_TYPE: /* No DWARF fundamental type code defined. */
case OFFSET_TYPE: /* No DWARF fundamental type code defined. */
case LANG_TYPE: /* No DWARF fundamental type code defined. */
default:
abort ();
}
}
/* Given a pointer to some ..._TYPE tree node, generate an assembly language
equate directive which will associate an easily remembered symbolic name
with the current DIE.
The name used is an artificial label generated from the TREE_UID number
associated with the given type node. The name is gest equated to is the
symbolic lable that we (previously) output at the start of the DIE that
we are currently generating.
Calling this function while generating some "type related" form of DIE
makes it easy to later refer to the DIE which represents the given type
simply by re-generating the alternative name from the ..._TYPE node's
UID number. */
static void
equate_type_number_to_die_number (type)
register tree type;
{
char type_label[MAX_ARTIFICIAL_LABEL_BYTES];
char die_label[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (type_label, TYPE_NAME_FMT, TREE_UID (type));
sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum);
ASM_OUTPUT_DEF (asm_out_file, type_label, die_label);
}
/* The following routine is a nice and simple transducer. It converts an RTL
representation of a mechanism for getting the value of a given variable
into an equivalent DWARF representation of a mechanism for getting the
value of that same variable.
Note that the output from this routine will deviate in some respects
from the DWARF location descriptions which may be produced by some
other C compilers. THIS IS BY INTENT!
In particular, some C compilers may produce DWARF location descriptions
for memory-resident variables which only describe how to get the *address*
of the given variable. That's inconsistant with the way location
descriptions are generated for register-resident variables, in which the
access mechanism described by the location description actually describes
how to obtain the *value* of the variable (and not the variable's address).
In GNU DWARF, location descriptions for variables always describe a mechanism
which (if executed) would leave the *value* of the variable on top of the
stack. Thus, for memory-resident variables, the last byte in GNU DWARF
location descriptions will always be OP_DEREF4.
Using this more consistant approach makes the DWARF location descriptions
produced here more rational, and it also helps to simplify the following
routine dramatically.
*/
static void
output_location_description (rtl, suppress_trailing_deref)
register rtx rtl;
register int suppress_trailing_deref;
{
/* Note that for a dynamically sized array, the location we will
generate a description of here will be the lowest numbered location
which is actually within the array. */
switch (GET_CODE (rtl))
{
case REG:
/* This is kinda silly, but some debuggers may want to see OP_BASEREG
rather than OP_REG when the register in question is the frame
pointer. I personally would prefer that the OP_BASEREG code were
never used, but some debuggers stupidly insist that some registers
be called `base registers' rather than simply `registers'. */
ASM_OUTPUT_DWARF_DATA1 (asm_out_file,
(rtl == frame_pointer_rtx) ? OP_BASEREG : OP_REG);
/* The case of a pseudo register happens when an argument is not
used at all and the function is optimized. In such cases, we
generate a reference to register zero here. This may be truly
incorrect on some non-RISC machines, but let's not even worry
about that. If the user has optimized his code, and if he is
debugging, and if he happens to be looking at a variable that
has been optimized away, then he will not see anything meaningful,
but who cares. */
ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
(REGNO (rtl) < FIRST_PSEUDO_REGISTER)
? DWARF_REGISTER_NUMBER (REGNO (rtl))
: 0);
break;
case SUBREG:
/* The case of a subreg may arise when we have a local (register)
variable or a formal (register) parameter which doesn't quite
fill up an entire register. For now, just assume that it is
legitimate to make the DWARF info refer to the whole register
which contains the given subreg. */
output_location_description (XEXP (rtl, 0), FALSE);
break;
case MEM:
output_location_description (XEXP (rtl, 0), FALSE);
/* This is really a kludge. When creating location descriptions,
we are normally trying to produce a description of a procedure
by which debuggers can *read* the *value* of the item in question.
Unfortunately, the way DWARF is produced by existing compilers,
this simple and consistant definition doesn't always match what
these other compilers actually do.
When the item in question is a value in memory, existing compilers
omit the final OP_DEREF4 that really should be there. Here we will
be consistant with those (broken?) implementations.
*/
if (! suppress_trailing_deref)
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_DEREF4);
break;
case SYMBOL_REF:
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_ADDR);
ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl);
break;
case PLUS:
output_location_description (XEXP (rtl, 0), FALSE);
output_location_description (XEXP (rtl, 1), FALSE);
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_ADD);
break;
case CONST_INT:
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_CONST);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, INTVAL (rtl));
break;
default:
abort ();
}
}
/* Given a tree node describing an array bound (either lower or upper)
output a representation for that bound. */
static void
output_bound_representation (bound, dim_num, u_or_l)
register tree bound;
register unsigned dim_num; /* For multi-dimensional arrays. */
register char u_or_l; /* Designates upper or lower bound. */
{
switch (TREE_CODE (bound))
{
case ERROR_MARK:
break;
/* All fixed-bounds are represented by INTEGER_CST nodes. */
case INTEGER_CST:
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, TREE_INT_CST_LOW (bound));
break;
/* All dynamic bounds are represented by NOP_EXPR nodes. */
case NOP_EXPR:
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (begin_label, BOUND_BEGIN_LABEL_FMT,
current_dienum, dim_num, u_or_l);
sprintf (end_label, BOUND_END_LABEL_FMT,
current_dienum, dim_num, u_or_l);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
/* If we are working on a bound for a dynamic dimension in C,
the dynamic dimension in question had better have a static
(zero) lower bound and a dynamic *upper* bound. */
if (u_or_l != 'u')
abort ();
/* If optimization is turned on, the SAVE_EXPRs that describe
how to access the upper bound values are essentially bogus.
They only describe (at best) how to get at these values at
the points in the generated code right after they have just
been computed. Worse yet, in the typical case, the upper
bound values will not even *be* computed in the optimized
code, so these SAVE_EXPRs are entirely bogus.
In order to compensate for this fact, we check here to see
if optimization is enabled, and if so, we effectively create
an empty location description for the (unknown and unknowable)
upper bound.
This should not cause too much trouble for existing (stupid?)
debuggers because they have to deal with empty upper bounds
location descriptions anyway in order to be able to deal with
incomplete array types.
Of course an intelligent debugger (GDB?) should be able to
comprehend that a missing upper bound specification in a
array type used for a stroage class `auto' local array variable
indicates that the upper bound is both unknown (at compile-
time) and unknowable (at run-time) due to optimization.
*/
if (! optimize)
output_location_description (
(rtx) TREE_OPERAND (TREE_OPERAND (bound, 0), 1),
TRUE);
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
break;
default:
abort ();
}
}
/* Recursive function to output a sequence of value/name pairs for
enumeration constants in reversed order. This is called from
enumeration_type_die(). */
static void
output_enumeral_list (link)
register tree link;
{
if (link)
{
output_enumeral_list (TREE_CHAIN (link));
ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
TREE_INT_CST_LOW (TREE_VALUE (link)));
ASM_OUTPUT_DWARF_STRING (asm_out_file,
IDENTIFIER_POINTER (TREE_PURPOSE (link)));
}
}
/****************************** attributes *********************************/
/* The following routines are responsible for writing out the various types
of DWARF attributes (and any following data bytes associated with them).
These routines are listed in order based on the numerical codes of their
associated attributes. */
/* Generate an AT_sibling attribute. */
static void
sibling_attribute ()
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sibling);
sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM);
ASM_OUTPUT_DWARF_REF (asm_out_file, label);
}
static void
location_attribute (decl)
register tree decl;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location);
sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, LOC_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
switch (TREE_CODE (decl))
{
case ERROR_MARK:
break;
case VAR_DECL:
case PARM_DECL:
output_location_description (DECL_RTL (decl), TRUE);
break;
case FIELD_DECL:
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_CONST);
/* This is pretty strange, but existing compilers producing DWARF
apparently calculate the byte offset of a field differently
depending upon whether or not it is a bit-field. If the given
field is *not* a bit-field, then the offset is simply the
the byte offset of the given field from the beginning of the
struct. For bit-fields however, the offset is the offset (in
bytes) of the beginning of the *containing word* from the
beginning of the whole struct. */
ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
(DECL_BIT_FIELD (decl))
? BITFIELD_OFFSET_WORDS_IN_UNITS (decl)
: BITFIELD_OFFSET_UNITS (decl));
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, OP_ADD);
break;
default:
/* FUNCTION_DECLs don't get location attributes because their
low_pc attributes are sufficient to determine their locations. */
/* LABEL_DECLs don't get location attributes because their low_pc
attributes serve this purpose. */
/* TYPE_DECLs and TAG_DECLs don't get location attributes because
these things just don't exist at run-time. */
/* RESULT_DECLs don't get location attributes because it is not
even clear what the debugger would be able to do with such
information. */
abort ();
}
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
/* Generate an AT_name attribute given some string value to be included as
the value of the attribute. If the name is null, don't do anything. */
static void
name_attribute (name_string)
register char *name_string;
{
if (name_string && *name_string)
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_name);
ASM_OUTPUT_DWARF_STRING (asm_out_file, name_string);
}
}
#if 0
/* I'm still trying to figure out when (or if) this type of attribute is
ever actually used for anything. If you find out, please let me know. */
static void
dimension_attribute (type)
register tree type;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_dimension);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, ?);
}
#endif
static void
fund_type_attribute (type)
register tree type;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_fund_type);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, fundamental_type_code (type));
}
static void
mod_fund_type_attribute (type)
register tree type;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_fund_type);
sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, MT_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
write_modifier_bytes (type);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, fundamental_type_code (root_type (type)));
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
static void
user_def_type_attribute (type)
register tree type;
{
char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_user_def_type);
sprintf (ud_type_name, TYPE_NAME_FMT, TREE_UID (type));
ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name);
}
static void
mod_u_d_type_attribute (type)
register tree type;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_u_d_type);
sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, MT_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
write_modifier_bytes (type);
sprintf (ud_type_name, TYPE_NAME_FMT, TREE_UID (root_type (type)));
ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name);
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
static void
ordering_attribute (ordering)
register int ordering;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_ordering);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, ordering);
}
/* Note that the block of subscript information for an array type also
includes information about the element type of type given array type. */
static void
subscript_data_attribute (type)
register tree type;
{
register unsigned dimension_number;
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_subscr_data);
sprintf (begin_label, SS_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, SS_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
/* The GNU compilers represent multidimensional array types as sequences
of one dimensional array types whose element types are themselves array
types. Here we squish that down, so that each multidimensional array
type gets only one array_type DIE in the DWARF debugging info. */
for (type = TYPE_MAIN_VARIANT (type), dimension_number = 0;
TREE_CODE (type) == ARRAY_TYPE;
type = TYPE_MAIN_VARIANT (TREE_TYPE (type)), dimension_number++)
{
register tree domain = TYPE_DOMAIN (type);
/* Arrays come in three flavors. Unspecified bounds, fixed
bounds, and (in GNU C only) variable bounds. Handle all
three forms here. */
if (domain)
{
/* We have an array type with specified bounds. */
register tree lower = TYPE_MIN_VALUE (domain);
register tree upper = TYPE_MAX_VALUE (domain);
register unsigned upper_bound_representation;
register unsigned lower_bound_representation;
register unsigned index_type_representation;
/* Start by categorizing each bound as either fixed or variable. */
lower_bound_representation =
(TREE_CODE (lower) == INTEGER_CST) ? FMT_CONST : FMT_EXPR;
upper_bound_representation =
(TREE_CODE (lower) == INTEGER_CST) ? FMT_CONST : FMT_EXPR;
/* Now categorize the index range type as either a DWARF
`fundamental' type or as a user defined type. */
index_type_representation =
type_is_fundamental (domain) ? FMT_FT : FMT_UDT;
/* Handle only fundamental types as index types for now. */
if (index_type_representation != FMT_FT)
abort ();
/* Output the representation format byte for this dimension. */
ASM_OUTPUT_DWARF_DATA1 (asm_out_file,
(index_type_representation<<2)
|| (lower_bound_representation<<1)
|| (upper_bound_representation));
/* Output the index type for this dimension. */
ASM_OUTPUT_DWARF_DATA2 (asm_out_file,
fundamental_type_code (domain));
/* Output the representation for the lower bound. */
output_bound_representation (lower, dimension_number, 'l');
/* Output the representation for the upper bound. */
output_bound_representation (upper, dimension_number, 'u');
}
else
{
/* We have an array type with an unspecified length. For C and
C++ we can assume that this really means that (a) the index
type is an integral type, and (b) the lower bound is zero.
Note that DWARF defines the representation of an unspecified
(upper) bound as being a zero-length location description. */
/* Output the array-bounds format byte. */
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, FMT_FT_C_X);
/* Output the (assumed) index type. */
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, FT_integer);
/* Output the (assumed) lower bound (constant) value. */
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
/* Output the (empty) location description for the upper bound. */
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0);
}
}
/* Output the prefix byte that says that the element type is comming up. */
ASM_OUTPUT_DWARF_DATA1 (asm_out_file, FMT_ET);
/* Output a representation of the type of the elements of this array type. */
type_attribute (type);
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
static void
byte_size_attribute (tree_node)
register tree tree_node;
{
register unsigned size;
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_byte_size);
switch (TREE_CODE (tree_node))
{
case ERROR_MARK:
size = 0;
break;
case ENUMERAL_TYPE:
case RECORD_TYPE:
case UNION_TYPE:
size = int_size_in_bytes (tree_node);
break;
case FIELD_DECL:
{
register unsigned words;
register unsigned bits;
bits = TREE_INT_CST_LOW(DECL_SIZE(tree_node));
words = (bits + (BITS_PER_WORD-1)) / BITS_PER_WORD;
size = words * (BITS_PER_WORD / BITS_PER_UNIT);
}
break;
default:
abort ();
}
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, size);
}
/* For a FIELD_DECL node which represents a bit field, output an attribute
which specifies the distance in bits from the start of the *word*
containing the given field to the first bit of the field. */
static void
bit_offset_attribute (decl)
register tree decl;
{
assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */
assert (DECL_BIT_FIELD (decl)); /* Must be a bit field. */
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_offset);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file,
BITFIELD_OFFSET_BITS (decl) % BITS_PER_WORD);
}
/* For a FIELD_DECL node which represents a bit field, output an attribute
which specifies the length in bits of the given field. */
static void
bit_size_attribute (decl)
register tree decl;
{
assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */
assert (DECL_BIT_FIELD (decl)); /* Must be a bit field. */
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_size);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
TREE_INT_CST_LOW (DECL_SIZE (decl)));
}
static void
deriv_list_attribute (type)
register tree type;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
register unsigned basenum;
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_deriv_list);
sprintf (begin_label, DERIV_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, DERIV_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
for (basenum = CLASSTYPE_N_BASECLASSES (type); basenum > 0; basenum--)
{
char baseclass_type_name[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (baseclass_type_name, TYPE_NAME_FMT,
TREE_UID (CLASSTYPE_BASECLASS (type, basenum)));
ASM_OUTPUT_DWARF_REF (asm_out_file, baseclass_type_name);
}
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
/* The following routine outputs the `element_list' attribute for enumeration
type DIEs. The element_lits attribute includes the names and values of
all of the enumeration constants associated with the given enumeration
type. */
static void
element_list_attribute (element)
register tree element;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_element_list);
sprintf (begin_label, EE_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, EE_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
/* This is a kludge whose only purpose is to be compatible with what
existing `traditional DWARF' compilers do.
Here we output a list of value/name pairs for each enumeration constant
defined for this enumeration type (as required), but we do it in REVERSE
order. The order isn't really defined for DWARF, but existing compilers
use this order, so we do likewise, even if it seems stupid and
even if debuggers don't give a darn about the order. */
output_enumeral_list (element); /* Recursively output the whole list. */
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
/* Generate an AT_stmt_list attribute. These are normally present only in
DIEs with a TAG_source_file tag. */
static void
stmt_list_attribute (label)
register char *label;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_stmt_list);
/* Don't use ASM_OUTPUT_DWARF_DATA4() here. */
ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
}
/* Generate an AT_low_pc attribute for a label DIE, a lexical_block DIE or
for a subroutine DIE. */
static void
low_pc_attribute (asm_low_label)
register char *asm_low_label;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_low_pc);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_low_label);
}
/* Generate an AT_high_pc attribute for a lexical_block DIE or for a
subroutine DIE. */
static void
high_pc_attribute (asm_high_label)
register char *asm_high_label;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_high_pc);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_high_label);
}
/* Generate an AT_language attribute given a LANG value. These attributes
are used only within TAG_source_file DIEs. */
static void
language_attribute (language_code)
register LANG language_code;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_language);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, (int) language_code);
}
static void
member_attribute (decl)
register tree decl;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
/* Use this attribute only for member functions/objects in C++. */
assert (TREE_CODE (decl) == METHOD_DECL || TREE_CODE (decl) == FIELD_DECL);
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_member);
sprintf (label, TYPE_NAME_FMT, TREE_UID (DECL_CONTEXT (decl)));
ASM_OUTPUT_DWARF_REF (asm_out_file, label);
}
#if 0
/* The AT_discr attribute is used only for variant records in languages like
Ada or Pascal or Modula. It identifies the member whose value determines
which variant is currently selected. */
static void
discr_attribute (decl)
register tree decl;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_discr);
sprintf (label, TYPE_NAME_FMT, TREE_UID (decl));
ASM_OUTPUT_DWARF_REF (asm_out_file, label);
}
static void
discr_value (value)
register tree value;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_discr_value);
sprintf (begin_label, DISC_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, DISC_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
?
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
#endif
/* Visibility attributes are generated for all entities (both functions and
data objects) which have `internal' linkage. (See the ANSI C standard for
a precise definition of internal linkage.) Things which have internal
linkage are (only) those things which are declared at file-scope with
the `static' type modifier. */
static void
visibility_attribute (visibility)
register int visibility;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_visibility);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, visibility);
}
#if 0
/* Add this when there is a GNU Modula-{2,3} front end. */
static void
import_attribute (decl)
register tree decl;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_import);
sprintf (label, TYPE_NAME_FMT, TREE_UID (decl));
ASM_OUTPUT_DWARF_REF (asm_out_file, label);
}
#endif
#if 0
/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */
static void
string_length_attribute (decl)
register tree decl;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_string_length);
sprintf (begin_label, DISC_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, DISC_END_LABEL_FMT, current_dienum);
ASM_OUTPUT_DWARF_BLOCK2 (asm_out_file, end_label, begin_label);
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
?
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
#endif
#ifdef AT_file
static void
file_attribute (filename)
char *filename;
{
unsigned file_number = lookup_filename (filename);
char label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_file);
sprintf (label, FILE_ENTRY_LABEL_FMT, file_number);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
}
#endif
#ifdef AT_line
static void
line_attribute (line)
unsigned line;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_line);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, line);
}
#endif
#ifdef AT_comp_dir
static void
comp_dir_attribute (dirname)
char *dirname;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_comp_dir);
ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname);
}
#endif
#ifdef AT_src_info
static void
src_info_attribute (src_info_start_label)
char *src_info_start_label;
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_info);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, src_info_start_label);
}
#endif
#ifdef AT_addr_ranges
static void
addr_ranges_attribute ()
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_addr_ranges);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, (2 * 4) * 4);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_END_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA_BEGIN_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA_END_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, BSS_BEGIN_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, BSS_END_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA_BEGIN_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA_END_LABEL);
}
#endif
#ifdef AT_prototyped
static void
prototyped_attribute ()
{
ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_prototyped);
}
#endif
/************************* end of attributes *****************************/
/********************* utility routines for DIEs *************************/
static void
source_location_attributes (decl)
register tree decl;
{
#ifdef AT_file
file_attribute (DECL_SOURCE_FILE (decl));
#endif
#ifdef AT_line
line_attribute (DECL_SOURCE_LINE (decl));
#endif
}
/* Many forms of DIEs contain a "type description" part. The following
routine writes out these "type descriptor" parts. */
static void
type_attribute (type)
register tree type;
{
register enum tree_code code;
register int root_type_modified;
type = TYPE_MAIN_VARIANT (type);
code = TREE_CODE (type);
root_type_modified = (code == POINTER_TYPE || code == REFERENCE_TYPE);
if (type_is_fundamental (root_type (type)))
if (root_type_modified)
mod_fund_type_attribute (type);
else
fund_type_attribute (type);
else
if (root_type_modified)
mod_u_d_type_attribute (type);
else
user_def_type_attribute (type);
}
/* Given a pointer to a FUNCTION_TYPE node or a METHOD_TYPE node, return
non-zero if the given FUNCTION_TYPE or METHOD_TYPE represents a
prototyped type. */
int
is_prototyped (func_type)
register tree func_type;
{
return (TYPE_ARG_TYPES (func_type) != (tree) 0);
}
/* Given a tree pointer to a struct, class, union, or enum type node, return
a pointer to the (string) tag name for the given type, or zero if the
type was declared without a tag. */
static char *
type_tag (type)
register tree type;
{
register char *name = 0;
if (TYPE_NAME (type) != 0)
{
register tree t = 0;
/* Find the IDENTIFIER_NODE for the type name. */
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
t = TYPE_NAME (type);
/* Now get the name as a string, or invent one. */
if (t != 0)
name = IDENTIFIER_POINTER (t);
}
return (name == 0 || *name == '\0') ? 0 : name;
}
static void
dienum_push ()
{
/* Start by checking if the pending_sibling_stack needs to be expanded.
If necessary, expand it. */
if (pending_sibling_stack_depth+1 > pending_sibling_stack_size)
{
pending_sibling_stack_size += PENDING_SIBLING_STACK_INCREMENT;
pending_sibling_stack
= (unsigned *) xrealloc (pending_sibling_stack,
pending_sibling_stack_size * sizeof (unsigned));
}
pending_sibling_stack_depth++;
NEXT_DIE_NUM = next_unused_dienum++;
}
/* Pop the sibling stack so that the most recently pushed DIEnum becomes the
NEXT_DIE_NUM. */
static void
dienum_pop ()
{
pending_sibling_stack_depth--;
}
static tree
member_declared_type (member)
register tree member;
{
return (DECL_BIT_FIELD (member))
? DECL_BIT_FIELD_TYPE (member)
: TREE_TYPE (member);
}
/******************************* DIEs ************************************/
/* Output routines for individual types of DIEs. These are arranged here in
order of the numeric codes for the associated TAG_... constants. */
/* Note that (contrary to popular belief) every type of DIE gets a sibling. */
static void
output_array_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_array_type);
sibling_attribute ();
equate_type_number_to_die_number (type);
/* This is a kludge put here only for the sake of being compatible with
what other `traditional DWARF' compilers do. The definition of
traditional DWARF only says that we have to include an ordering
attribute in cases of multi-dimensional arrays, but existing
compilers output an ordering attribute for all arrays, regardless
of their dimensionality. */
ordering_attribute (ORD_row_major);
subscript_data_attribute (type);
}
#if 0
/* This routine is not used because GCC & G++ treat classes pretty much the
same as structs. */
static void
output_class_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_class_type);
sibling_attribute ();
dienum_push ();
equate_type_number_to_die_number (type);
name_attribute (type_tag (type));
byte_size_attribute (type);
}
#endif
#if 0
/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */
static void
output_entry_point_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
register tree return_type = TREE_TYPE (type);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_entry_point);
sibling_attribute ();
dienum_push ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
#ifdef AT_prototyped
if (is_prototyped (type))
prototyped_attribute ();
#endif
if (TREE_CODE (return_type) != VOID_TYPE)
type_attribute (return_type);
if (TREE_PUBLIC (decl) || TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
#endif
/* Output a DIE to represent an enumeration type. Note that these DIEs
include all of the information about the enumeration values also.
This information is encoded into the element_list attribute. */
static void
output_enumeration_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type);
sibling_attribute ();
equate_type_number_to_die_number (type);
name_attribute (type_tag (type));
byte_size_attribute (type);
element_list_attribute (TYPE_FIELDS (type));
}
/* There are two routines here to output DIEs for formal parameters. The
first one is used to create DIEs for the formal parameters associated
with function *types*. For function types, GCC maintains only lists
of the types of the formal parameters. For actual functions, GCC
maintains lists of PARM_DECL tree nodes. The following two functions
handle these two separate cases. */
static void
output_formal_parameter_die_for_type (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_formal_parameter);
sibling_attribute ();
/* Note that here we only have information about the type of some
particular formal parameter for some particular function type.
Thus, we do not produce a name_attribute here because there is
no particular name which is uniquely associated with the formal
parameter type that we are describing. */
type_attribute (type);
}
/* Output a description of an authentic formal parameter for an actual
function (not merely a function type). */
static void
output_formal_parameter_die_for_decl (arg)
register void *arg;
{
register tree decl = arg;
tree type = TREE_TYPE (decl);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_formal_parameter);
sibling_attribute ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
type_attribute (type);
location_attribute (decl);
}
static void
output_global_subroutine_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
register tree return_type = TREE_TYPE (type);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_subroutine);
sibling_attribute ();
dienum_push ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
#ifdef AT_prototyped
if (is_prototyped (type))
prototyped_attribute ();
#endif
if (TREE_CODE (return_type) != VOID_TYPE)
type_attribute (return_type);
if (!TREE_PUBLIC (decl))
visibility_attribute (VIS_local);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (!TREE_EXTERNAL (decl))
#endif
{
char func_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
low_pc_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
sprintf (func_end_label, FUNC_END_LABEL_FMT, TREE_UID (decl));
high_pc_attribute (func_end_label);
}
if (TREE_PUBLIC (decl) || TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
static void
output_global_variable_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_variable);
sibling_attribute ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
type_attribute (type);
if (!TREE_PUBLIC (decl))
visibility_attribute (VIS_local);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (!TREE_EXTERNAL (decl))
#endif
location_attribute (decl);
if (TREE_PUBLIC (decl) || TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
#if 0
/* Implement this routine when there is a GNU Modula front-end. */
static void
output_imported_declaration_die (arg)
register void *arg;
{
register tree decl = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_imported_declaration);
sibling_attribute ();
}
#endif
static void
output_inline_subroutine_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
register tree return_type = TREE_TYPE (type);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_inline_subroutine);
sibling_attribute ();
dienum_push ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
#ifdef AT_prototyped
if (is_prototyped (type))
prototyped_attribute ();
#endif
if (TREE_CODE (return_type) != VOID_TYPE)
type_attribute (return_type);
if (!TREE_PUBLIC (decl))
visibility_attribute (VIS_local);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (!TREE_EXTERNAL (decl))
#endif
{
char func_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
low_pc_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
sprintf (func_end_label, FUNC_END_LABEL_FMT, TREE_UID (decl));
high_pc_attribute (func_end_label);
}
if (TREE_PUBLIC (decl) || TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
static void
output_label_die (arg)
register void *arg;
{
register tree decl = arg;
register rtx insn = DECL_RTL (decl);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_label);
sibling_attribute ();
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
/* When optimization is enabled (with -O) the code in jump.c and in flow.c
may cause insns representing one of more of the user's own labels to
be deleted. This happens whenever it is determined that a given label
is unreachable.
In such cases, we here generate an abbreviated form of a label DIE.
This abbreviated version does *not* have a low_pc attribute. This
should signify to the debugger that the label has been optimized away.
Note that a CODE_LABEL can get deleted either by begin converted into
a NOTE_INSN_DELETED note, or by simply having its INSN_DELETED_P flag
set to true. We handle both cases here.
*/
if (GET_CODE (insn) == CODE_LABEL && ! INSN_DELETED_P (insn))
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (label, INSN_LABEL_FMT, TREE_UID (current_function_decl),
INSN_UID (insn));
low_pc_attribute (label);
}
}
static void
output_lexical_block_die (arg)
register void *arg;
{
register tree stmt = arg;
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_lexical_block);
sibling_attribute ();
dienum_push ();
sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number);
low_pc_attribute (begin_label);
sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number);
high_pc_attribute (end_label);
}
static void
output_local_variable_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_local_variable);
sibling_attribute ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
type_attribute (type);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (!TREE_EXTERNAL (decl))
#endif
location_attribute (decl);
if (TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
static void
output_member_die (arg)
register void *arg;
{
register tree decl = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_member);
sibling_attribute ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
type_attribute (member_declared_type (decl));
if (DECL_BIT_FIELD (decl)) /* If this is a bit field... */
{
byte_size_attribute (decl);
bit_size_attribute (decl);
bit_offset_attribute (decl);
}
location_attribute (decl);
}
static void
output_member_function_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
register tree return_type = TREE_TYPE (type);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_member_function);
sibling_attribute ();
dienum_push ();
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
#ifdef AT_prototyped
if (is_prototyped (type))
prototyped_attribute ();
#endif
if (TREE_CODE (return_type) != VOID_TYPE)
type_attribute (return_type);
if (!TREE_PUBLIC (decl))
visibility_attribute (VIS_local);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (!TREE_EXTERNAL (decl))
#endif
{
char func_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
low_pc_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
sprintf (func_end_label, FUNC_END_LABEL_FMT, TREE_UID (decl));
high_pc_attribute (func_end_label);
}
if (TREE_PUBLIC (decl) || TREE_EXTERNAL (decl))
source_location_attributes (decl);
}
#if 0
/* Don't generate either pointer_type DIEs or reference_type DIEs. According
to the 4-4-90 DWARF draft spec (just after requirement #47):
These two type entries are not currently generated by any compiler.
Since the only way to name a pointer (or reference) type is C or C++
is via a "typedef", an entry with the "typedef" tag is generated
instead.
*/
static void
output_pointer_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_pointer_type);
sibling_attribute ();
type_attribute (TREE_TYPE (type));
}
static void
output_reference_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_reference_type);
sibling_attribute ();
type_attribute (TREE_TYPE (type));
}
#endif
static void
output_source_file_die (arg)
register void *arg;
{
register char *main_input_filename = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_source_file);
sibling_attribute ();
dienum_push ();
name_attribute (main_input_filename);
/* If we are producing the source_file_die for the .externs section, then
produce only the abbreviated version. */
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (! finalizing)
#endif
{
language_attribute (LANG_ANSI_C_V1);
low_pc_attribute (TEXT_BEGIN_LABEL);
high_pc_attribute (TEXT_END_LABEL);
stmt_list_attribute (LINE_BEGIN_LABEL);
last_filename = xstrdup (main_input_filename);
}
/* Produce some specialized GNU DWARF attributes if needed. */
#ifdef AT_comp_dir
{
int len = 500;
char *dirname = (char *) xmalloc (len + 1);
/* We don't know how much space the dirname needs,
so try bigger and bigger buffers until it fits. */
while (1)
{
getcwd (dirname, len); /* Being conservative here. */
if (strlen (dirname) < len - 1) /* Being conservative here. */
break;
len *= 2;
dirname = (char *) xrealloc (dirname, len + 1);
}
comp_dir_attribute (dirname);
free (dirname);
}
#endif
#ifdef AT_src_info
src_info_attribute (SRCINFO_BEGIN_LABEL);
#endif
#ifdef AT_addr_ranges
addr_ranges_attribute ();
#endif
}
#if 0
/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */
static void
output_string_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_string_type);
sibling_attribute ();
string_length_attribute (?);
}
#endif
static void
output_structure_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type);
sibling_attribute ();
equate_type_number_to_die_number (type);
name_attribute (type_tag (type));
/* If this type has been completed, then give it a byte_size attribute
and prepare to give a list of members. Otherwise, don't do either of
these things. In the latter case, we will not be generating a list
of members (since we don't have any idea what they might be for an
incomplete type). */
if (TYPE_SIZE (type))
{
dienum_push ();
byte_size_attribute (type);
}
#ifdef GPLUSPLUS
if (CLASSTYPE_N_BASECLASSES (type) > 0)
deriv_list_attribute (type);
#endif
}
#if 0
/* Don't use this. It was a dumb idea on the part of the DWARF designers to
try to support non-global (i.e. local functions & procedures in languages
other than C and C++. It would have been better to use an attribute for
the "local" aspect of such functions/procedures or (better still) just
have the debugger figure out that a function is either local or global
depending upon what sibling chain it is on. */
static void
output_subroutine_die (arg)
register void *arg;
{
register tree decl = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine);
sibling_attribute ();
dienum_push ();
}
#endif
static void
output_subroutine_type_die (arg)
register void *arg;
{
register tree type = arg;
register tree return_type = TREE_TYPE (type);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine_type);
sibling_attribute ();
dienum_push ();
equate_type_number_to_die_number (type);
#ifdef AT_prototyped
if (is_prototyped (type))
prototyped_attribute ();
#endif
if (TREE_CODE (return_type) != VOID_TYPE)
type_attribute (return_type);
}
static void
output_typedef_die (arg)
register void *arg;
{
register tree decl = arg;
register tree type = TREE_TYPE (decl);
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_typedef);
sibling_attribute ();
equate_type_number_to_die_number (decl);
if (DECL_NAME (decl))
name_attribute (IDENTIFIER_POINTER (DECL_NAME (decl)));
type_attribute (type);
}
static void
output_union_type_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type);
sibling_attribute ();
equate_type_number_to_die_number (type);
name_attribute (type_tag (type));
/* If this type has been completed, then give it a byte_size attribute
and prepare to give a list of members. Otherwise, don't do either of
these things. In the latter case, we will not be generating a list
of members (since we don't have any idea what they might be for an
incomplete type). */
if (TYPE_SIZE (type))
{
dienum_push ();
byte_size_attribute (type);
}
}
/* Generate a special type of DIE used as a stand-in for a trailing ellipsis
at the end of an (ANSI prototyped) formal parameters list. */
static void
output_unspecified_parameters_die (arg)
register void *arg;
{
register int generate_name = (int) arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_unspecified_parameters);
sibling_attribute ();
/* This kludge is here only for the sake of being compatible with what
other `traditional DWARF' compilers do. The definition of traditional
DWARF doesn't even allow unspecified_parameters DIEs to contain any
sort of name attribute, but existing compilers output one containing
`...' for function declarations (but not for function types) so we do
likewise here. */
if (generate_name)
name_attribute ("...");
}
#if 0
/* Implement this when there is a GNU Pascal or Modula or Ada front-end. */
static void
output_variant_die (arg)
register void *arg;
{
register tree type = arg;
ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_variant);
sibling_attribute ();
dienum_push ();
}
#endif
static void
output_padded_null_die (arg)
register void *arg;
{
ASM_OUTPUT_ALIGN (asm_out_file, 2); /* 2**2 == 4 */
}
/*************************** end of DIEs *********************************/
/* Generate some type of DIE. This routine generates the generic outer
wrapper stuff which goes around all types of DIE's (regardless of their
TAGs. All forms of DIEs start with a DIE-specific label, followed by a
DIE-length word, followed by the guts of the DIE itself. After the guts
of the DIE, there must always be a terminator label for the DIE. */
static void
output_die (die_specific_output_function, param)
register void (*die_specific_output_function)();
register void *param;
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
current_dienum = NEXT_DIE_NUM;
NEXT_DIE_NUM = next_unused_dienum;
sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum);
sprintf (end_label, DIE_END_LABEL_FMT, current_dienum);
/* Write a label which will act as the name for the start of this DIE. */
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
/* Write the DIE-length word. */
ASM_OUTPUT_DWARF_BLOCK4 (asm_out_file, end_label, begin_label);
/* Fill in the guts of the DIE. */
next_unused_dienum++;
die_specific_output_function (param);
/* Write a label which will act as the name for the end of this DIE. */
ASM_OUTPUT_LABEL (asm_out_file, end_label);
}
static void
end_sibling_chain ()
{
char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
current_dienum = NEXT_DIE_NUM;
NEXT_DIE_NUM = next_unused_dienum;
sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum);
/* Write a label which will act as the name for the start of this DIE. */
ASM_OUTPUT_LABEL (asm_out_file, begin_label);
/* Write the DIE-length word. */
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4);
dienum_pop ();
}
static void
output_member_list_types (mbr)
register tree mbr;
{
for ( ; mbr; mbr = TREE_CHAIN (mbr))
/* Omit here the nameless fields that are used to skip bits. */
if (DECL_NAME (mbr) != 0)
output_dies_for_type (member_declared_type (mbr));
}
static void
output_member_list (mbr)
register tree mbr;
{
for ( ; mbr; mbr = TREE_CHAIN (mbr))
/* Omit here the nameless fields that are used to skip bits. */
if (DECL_NAME (mbr) != 0)
output_die (output_member_die, mbr);
end_sibling_chain (); /* End of member chain. */
}
static void
output_formals_for_type (func_type)
register tree func_type;
{
register tree link;
for (link = TYPE_ARG_TYPES (func_type); link; link = TREE_CHAIN (link))
{
register tree formal_type = TREE_VALUE (link);
if (formal_type == void_type_node)
return;
/* Output a (nameless) DIE to represent the formal parameter itself. */
output_die (output_formal_parameter_die_for_type, formal_type);
}
/* This function type has an ellipsis. So add a TAG_unspecified_parameters
DIE to the end of the parameter list. */
output_die (output_unspecified_parameters_die, (void*) FALSE);
}
static void
output_dies_for_types_of_formals (func_type)
register tree func_type;
{
register tree link;
for (link = TYPE_ARG_TYPES (func_type); link; link = TREE_CHAIN (link))
{
register tree formal_type = TREE_VALUE (link);
if (formal_type == void_type_node)
break;
output_dies_for_type (formal_type);
}
}
static void
output_dies_for_type (type)
register tree type;
{
if (type == 0 || type == error_mark_node)
return;
type = TYPE_MAIN_VARIANT (type);
if (TREE_ASM_WRITTEN (type))
return;
switch (TREE_CODE (type))
{
case ERROR_MARK:
break;
case POINTER_TYPE:
case REFERENCE_TYPE:
output_dies_for_type (TREE_TYPE (type));
break;
case ARRAY_TYPE:
{
register tree element_type;
element_type = TYPE_MAIN_VARIANT(TREE_TYPE(TYPE_MAIN_VARIANT(type)));
while (TREE_CODE (element_type) == ARRAY_TYPE)
element_type = TYPE_MAIN_VARIANT (TREE_TYPE (element_type));
output_dies_for_type (element_type);
output_die (output_array_type_die, type);
}
break;
case RECORD_TYPE:
case UNION_TYPE:
case ENUMERAL_TYPE:
/* Basically, don't do anything for tagged types now. They will be
output later on when we see their associated TAG_DECLs in the
set of ..._DECL nodes for the current scope. Note however that
it is possible that we might never see such a TAG_DECL later in
the current scope. This can happen in those (rare?) cases where
there was a declaration in the current scope for something (e.g.
a pointer variable or a pointer typedef) which made reference to
a tagged type (i.e. a struct, union, or enum type) which never
actually gets completed within the current scope. For the sake
of such cases, we must keep track here of each (still unprocessed)
tag type for the current scope. At the end of the scope, we must
revisit the list that we made of these unprocessed taggged types
and output descriptions of any of them which have not yet been
described. (These will all be incomplete tagged types.) */
pending_tagged_types = perm_tree_cons (0, type, pending_tagged_types);
/* EARLY EXIT! Avoid setting TREE_ASM_WRITTEN because we haven't
necessarily written out the full info for this tagged type yet. */
return;
case FUNCTION_TYPE:
/* Force out return type (in case it wasn't forced out already). */
output_dies_for_type (TREE_TYPE (type));
output_dies_for_types_of_formals (type);
output_die (output_subroutine_type_die, type);
output_formals_for_type (type);
end_sibling_chain ();
break;
case METHOD_TYPE:
/* Force out return type (in case it wasn't forced out already). */
output_dies_for_type (TREE_TYPE (type));
output_dies_for_types_of_formals (type);
output_die (output_subroutine_type_die, type);
output_formals_for_type (type);
end_sibling_chain ();
break;
case VOID_TYPE:
case INTEGER_TYPE:
case REAL_TYPE:
case COMPLEX_TYPE:
case BOOLEAN_TYPE:
case CHAR_TYPE:
case SET_TYPE:
break; /* No DIEs needed for fundamental types. */
case FILE_TYPE: /* No DWARF representation currently defined. */
case STRING_TYPE: /* No DWARF representation currently defined. */
case OFFSET_TYPE: /* No DWARF representation currently defined. */
case LANG_TYPE: /* No DWARF representation currently defined. */
default:
abort ();
}
TREE_ASM_WRITTEN (type) = 1;
}
static void
output_dies_for_tagged_type (type)
register tree type;
{
if (type == 0 || type == error_mark_node)
return;
assert (type == TYPE_MAIN_VARIANT (type));
if (TREE_ASM_WRITTEN (type))
return;
switch (TREE_CODE (type))
{
case RECORD_TYPE:
/* If this is not an incomplete type, output descriptions of the
types of each of its members. */
if (TYPE_SIZE (type))
output_member_list_types (TYPE_FIELDS (type));
output_die (output_structure_type_die, type);
/* If this is not an incomplete type, output descriptions of each of
its members. */
if (TYPE_SIZE (type))
output_member_list (TYPE_FIELDS (type));
break;
case UNION_TYPE:
/* If this is not an incomplete type, output descriptions of the
types of each of its members. */
if (TYPE_SIZE (type))
output_member_list_types (TYPE_FIELDS (type));
output_die (output_union_type_die, type);
/* If this is not an incomplete type, output descriptions of each of
its members. */
if (TYPE_SIZE (type))
output_member_list (TYPE_FIELDS (type));
break;
case ENUMERAL_TYPE:
output_die (output_enumeration_type_die, type);
break;
default:
abort ();
}
TREE_ASM_WRITTEN (type) = 1;
}
static void
output_pending_tagged_types ()
{
/* Output DIEs to represent any tagged types which are declared within the
current scope but which were never completed within the current scope. */
while (TREE_VALUE (pending_tagged_types) != NULL)
{
output_dies_for_tagged_type (TREE_VALUE (pending_tagged_types));
pending_tagged_types = TREE_CHAIN (pending_tagged_types);
}
/* Throw away the scope marker. */
pending_tagged_types = TREE_CHAIN (pending_tagged_types);
}
/* Output all of the symbols declared within a given lexical block (also
called a `binding contour') and (recursively) all of it's sub-blocks. */
static void
output_symbols_for_block (stmt, labels)
register tree stmt;
register tree labels;
{
tree subblocks;
/* Ignore BLOCKs for blocks never really used to make RTL. */
if ((! stmt || ! TREE_USED (stmt)) && ! labels)
return;
/* Output a DIE to represent this block itself. */
next_block_number++;
output_die (output_lexical_block_die, stmt);
/* Output DWARF info for all of the labels within this block. */
{
register tree link;
for (link = labels; link; link = TREE_CHAIN (link))
output_die (output_label_die, TREE_VALUE (link));
}
/* Place a scope marker on the list of pending tagged types. */
pending_tagged_types = perm_tree_cons (NULL, NULL, pending_tagged_types);
/* Output the DIEs to represent all of the items declared
directly within this block (but not within any nested
sub-blocks). */
{
register tree symbol;
for (symbol = BLOCK_VARS (stmt); symbol; symbol = TREE_CHAIN (symbol))
output_symbol (symbol, 1);
}
output_pending_tagged_types ();
/* Output the DIEs to represent all sub-blocks of this block. */
for (subblocks = BLOCK_SUBBLOCKS (stmt) ; stmt; stmt = BLOCK_CHAIN (stmt))
output_symbols_for_block (subblocks, 0);
/* Terminate the list of `owned' items for this lexical block. */
end_sibling_chain ();
}
/* Output DWARF information for a symbol described by DECL.
LOCAL is nonzero if the symbol is not file-scope. */
static void
output_symbol (decl, local)
register tree decl;
register int local;
{
switch (TREE_CODE (decl))
{
case ERROR_MARK:
break;
case CONST_DECL:
/* Enum values are defined by defining the enum type. */
break;
case FUNCTION_DECL:
#ifdef DWARF_DESCRIBE_USED_EXTERNS
/* Even if we are describing externals, don't describe ones which are
never even used. They may be totally bogus and nonexistant. */
if (TREE_EXTERNAL (decl) && ! TREE_USED (decl))
break;
#else
/* Don't mention functions that are external. Rather, assume that
these functions will be described in the debugging information
for the compilation units in which these function get defined. */
if (TREE_EXTERNAL (decl))
break;
#endif
if (GET_CODE (DECL_RTL (decl)) != MEM
|| GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
break;
/* Before we describe the FUNCTION_DECL itself, make sure that we
have already described its return type and the types of each of
each of its parameters. */
output_dies_for_type (TREE_TYPE (TREE_TYPE (decl)));
output_dies_for_types_of_formals (TREE_TYPE (decl));
/* Not that the call above to output_dies_for_types_of_formals()
is only sufficient to force out the types of all of the formal
parameters when the current function is prototyped. For the sake
of non-prototyped functions we also need the following code to
be sure of getting the types of all formal parameters adequately
represented in the DWARF output. */
{
register tree parm;
for (parm = DECL_ARGUMENTS (decl); parm; parm = TREE_CHAIN (parm))
output_dies_for_type (TREE_TYPE (parm));
}
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (TREE_EXTERNAL (decl) && ! finalizing)
{
/* Remember this function if it is *not* a builtin. */
if (! DECL_FUNCTION_CODE (decl))
{
/* Remember this FUNCTION_DECL on the pending_extern_decls list.
We will output a description of it later, at the end of
compilation, to the .externs section. */
pending_extern_decls = perm_tree_cons (0, decl, pending_extern_decls);
/* Make sure that nested extern declarations are not thrown
away until we have had a fair chance to finalize them. */
if (DECL_CONTEXT (decl))
preserve_data ();
}
break;
}
#endif
/* Now output a DIE to represent the function itself. */
if (TREE_INLINE (decl))
output_die (output_inline_subroutine_die, decl);
else
output_die (output_global_subroutine_die, decl);
/* Now output descriptions of the arguments for this function.
This gets (unnecessarily?) complex because of the fact that
the DECL_ARGUMENT list for a FUNCTION_DECL doesn't indicate
cases where there was a trailing `...' at the end of the formal
parameter list. In order to find out if there was a trailing
ellipsis or not, we must instead look at the type associated
with the FUNCTION_DECL. This will be a node of type FUNCTION_TYPE.
If the chain of type nodes hanging of of this FUNCTION_TYPE node
ends with a void_type_node then there should not be an ellipsis
at the end. */
#ifdef DWARF_DESCRIBE_USED_EXTERNS
/* In the case where we are describing an external function, all we can
do here is to describe the *types* of its formal parameters. */
if (TREE_EXTERNAL (decl))
output_formals_for_type (TREE_TYPE (decl));
else
#endif
{
register tree arg_decls = DECL_ARGUMENTS (decl);
/* Generate DIEs to represent all known formal parameters, but
don't do it if this looks like a varargs function. */
if (! arg_decls
|| ! DECL_NAME (arg_decls)
|| strcmp (IDENTIFIER_POINTER (DECL_NAME (arg_decls)),
"__builtin_va_alist"))
{
register tree parm;
for (parm = arg_decls; parm; parm = TREE_CHAIN (parm))
if (TREE_CODE(parm) == PARM_DECL)
output_die (output_formal_parameter_die_for_decl, parm);
}
/* Now try to decide if we should put an ellipsis at the end. */
{
register int has_ellipsis = TRUE; /* default assumption */
register tree fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
if (fn_arg_types)
{
/* This function declaration/definition was prototyped. */
/* If the list of formal argument types ends with a
void_type_node, then the formals list did *not* end
with an ellipsis. */
if (TREE_VALUE (tree_last (fn_arg_types)) == void_type_node)
has_ellipsis = FALSE;
}
else
{
/* This function declaration/definition was not prototyped. */
/* Note that all non-prototyped function *declarations* are
assumed to represent varargs functions (until proven
otherwise). */
if (DECL_INITIAL (decl)) /* if this is a func definition */
{
if (!arg_decls)
has_ellipsis = FALSE; /* no args == (void) */
else
{
/* For a non-prototyped function definition which
declares one or more formal parameters, if the name
of the first formal parameter is *not*
__builtin_va_alist then we must assume that this
is *not* a varargs function. */
if (DECL_NAME (arg_decls)
&& strcmp (IDENTIFIER_POINTER (DECL_NAME (arg_decls)),
"__builtin_va_alist"))
has_ellipsis = FALSE;
}
}
}
if (has_ellipsis)
output_die (output_unspecified_parameters_die, (void*) TRUE);
}
}
/* Output DWARF info for all of the stuff within the body of the
function (if it has one - it may be just a declaration).
Note that some implementations of DWARF may not really recognize
the fact that the outermost block of a function definition is
itself a lexical block (with all of the rights & privledges which
apply thereto). At this point, these other (broken?) DWARF
implementations would just go ahead and output a sequence of DIEs
to represent the things declared directly within the outermost
lexical block of the function definition without first generating
a lexical block DIE.
We don't do that here though. Rather we create a lexical_block
DIE which will `own' all of the things declared within it.
*/
{
register tree outer_let = DECL_INITIAL (decl);
if (outer_let) /* if this is a function definition */
{
/* Note that here, `outer_let' is a pointer to the outermost
BLOCK node created to represent the body of a function.
This outermost BLOCK actually represents the outermost
binding contour for the function, i.e. the contour in which
the function's formal parameters get declared. Just within
this contour, there will be another (nested) BLOCK which
represents the function's outermost block. Whenever the
symbol DWARF_DEBUGGING_INFO is defined, this nested BLOCK
should be created by the compiler whether or not it would
otherwise seem to be needed. It is useful to have this
particular BLOCK exist because it provides us with pointers
to the first and last instructions which are actually a part
of the body of the function (i.e. *not* a part of the prologue
or epilogue code). */
#if 0
output_symbols_for_block (BLOCK_SUBBLOCKS (outer_let), named_labels);
#else
output_symbols_for_block (BLOCK_SUBBLOCKS (outer_let), 0);
#endif
/* Clear out the named label list in case the next following
FUNCTION_DECL is a function declaration but not a function
definition. */
#if 0
named_labels = NULL;
#endif
}
}
/* Generate a terminator for the list of stuff `owned' by this
function. */
end_sibling_chain ();
break;
case TYPE_DECL:
output_dies_for_type (TREE_TYPE (decl));
/* Output a DIE for the typedef itself. */
if (DECL_NAME (decl))
output_die (output_typedef_die, decl);
break;
case LABEL_DECL:
abort (); /* These are on a separate chain - see named_labels above. */
case FIELD_DECL:
/* Field declarations come in chains which are attached to
struct & union declarations. The field declarations are output
when their associated struct & union declarations are output. */
fatal ("In output_symbol a parameter was found on the wrong chain");
break;
case PARM_DECL:
/* Parmeter declarations come in chains which are attached to
function declarations. The parameter declarations are output
when their associated function declarations are output. */
fatal ("In output_symbol a parameter was found on the wrong chain");
break;
case VAR_DECL:
#ifdef DWARF_DESCRIBE_USED_EXTERNS
/* Even if we are describing externals, don't describe ones which are
never even used. They may be totally bogus and nonexistant. */
if (TREE_EXTERNAL (decl) && ! TREE_USED (decl))
break;
#else
/* Don't mention data objects that are external. Rather, assume that
these data objects will be described in the debugging information
for the compilation units in which these function get defined. */
if (TREE_EXTERNAL (decl))
break;
#endif
/* If there was an error in the declaration, then there will be
no RTL associated with the variable. In that case, avoid
possible segfaults by just ignoring this variable. */
if (DECL_RTL (decl) == 0)
break;
#if 0
/* 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;
#endif
/* Defer DWARF information for top-level initialized variables!
These are handled separately by dwarfout_toplevel_data later on. */
if (!local && GET_CODE (DECL_RTL (decl)) == MEM && DECL_INITIAL (decl))
break;
/* Output any DIEs that are needed to specify the type of this data
object. */
output_dies_for_type (TREE_TYPE (decl));
#ifdef DWARF_DESCRIBE_USED_EXTERNS
if (TREE_EXTERNAL (decl) && ! finalizing)
{
/* Remember this VAR_DECL on the pending_extern_decls list. We
will output a description of it later, at the end of compilation,
to the .externs section. */
pending_extern_decls = perm_tree_cons (0, decl, pending_extern_decls);
/* Make sure that nested extern declarations are not thrown away
until we have had a fair chance to finalize them. */
if (DECL_CONTEXT (decl))
preserve_data ();
break;
}
#endif
/* Output the DIE for the data object itself. */
output_die ((local)
? output_local_variable_die : output_global_variable_die,
decl);
break;
default:
abort ();
}
}
/* Output any necessary DWARF information for things declared at file-scope
which have not yet been described with DWARF info. */
void
dwarfout_file_scope_delayed_symbol (decl)
register tree decl;
{
switch (TREE_CODE (decl))
{
case ERROR_MARK:
break;
case VAR_DECL:
if ((DECL_INITIAL (decl) && GET_CODE (DECL_RTL (decl)) == MEM)
#ifdef DWARF_DESCRIBE_USED_EXTERNS
|| (TREE_EXTERNAL (decl) && TREE_USED (decl))
#endif
)
{
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DEBUG_SECTION);
output_symbol (decl, FALSE);
ASM_PREVIOUS_SECTION (asm_out_file);
}
break;
#ifdef DWARF_DESCRIBE_USED_EXTERNS
case FUNCTION_DECL:
if (TREE_EXTERNAL (decl) && TREE_USED (decl))
{
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DEBUG_SECTION);
output_symbol (decl, FALSE);
ASM_PREVIOUS_SECTION (asm_out_file);
}
break;
#endif
default:
break; /* Ignore others. */
}
}
void
dwarfout_file_scope_symbol (decl)
register tree decl;
{
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DEBUG_SECTION);
output_symbol (decl, FALSE);
ASM_PREVIOUS_SECTION (asm_out_file);
}
/* Output a marker (i.e. a label) for the beginning of the generated code
for a lexical block. */
void
dwarfout_begin_block (blocknum)
register int blocknum;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
text_section ();
sprintf (label, BLOCK_BEGIN_LABEL_FMT, blocknum);
ASM_OUTPUT_LABEL (asm_out_file, label);
}
/* Output a marker (i.e. a label) for the end of the generated code
for a lexical block. */
void
dwarfout_end_block (blocknum)
register int blocknum;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
text_section ();
sprintf (label, BLOCK_END_LABEL_FMT, blocknum);
ASM_OUTPUT_LABEL (asm_out_file, label);
}
/* Output a marker (i.e. a label) at a point in the assembly code which
corresponds to a given source level label. */
void
dwarfout_label (insn)
register rtx insn;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
text_section ();
sprintf (label, INSN_LABEL_FMT, TREE_UID (current_function_decl),
INSN_UID (insn));
ASM_OUTPUT_LABEL (asm_out_file, label);
}
/* Output a marker (i.e. a label) for the absoulute end of the generated code
for a function definition. This gets called *after* the epilogue code
has been generated. */
void
dwarfout_end_epilogue ()
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
/* Output a label to mark the endpoint of the code generated for this
function. */
sprintf (label, FUNC_END_LABEL_FMT, TREE_UID (current_function_decl));
ASM_OUTPUT_LABEL (asm_out_file, label);
}
#ifdef AT_src_info
static unsigned
lookup_filename (func_name)
char *func_name;
{
if (filename_table)
{
register filename_entry_t *search_p;
register filename_entry_t *limit_p = &filename_table[valid_ft_entries];
for (search_p = filename_table; search_p < limit_p; search_p++)
if (!strcmp (func_name, search_p->name))
{
/* When we get here, we have found the filename that we were
looking for in the filename_table. Now we want to make sure
that it gets moved to the zero'th entry in the table (if it
is not already there) so that subsequent attempts to find the
same filename will find it as quickly as possible. */
if (search_p)
{
filename_entry_t temp_entry;
register filename_entry_t *move_p;
temp_entry = *search_p;
/* Shift entries up to make room at [0] for the found entry. */
limit_p = &filename_table[0];
for (move_p = search_p; move_p > limit_p; move_p--)
*move_p = *(move_p-1);
/* Install the found entry at [0]. */
filename_table[0] = temp_entry;
}
return filename_table[0].number;
}
}
else
{
/* When we get here, we have never looked up a filename before. There
is no filename table yet, so we have to create one from scratch. */
total_ft_entries = 64;
filename_table
= (filename_entry_t *) xmalloc (total_ft_entries
* sizeof (filename_entry_t));
valid_ft_entries = 0;
}
/* We come here whenever we have a new filename which is not registered
in the current table. Here we add it to the table. */
/* Prepare to add a new table entry by making sure there is enough space
in the table to do so. If not, expand the current table by doubling
its current size (whatever that is). */
if ((valid_ft_entries + 1) > total_ft_entries)
{
total_ft_entries <<= 1;
filename_table
= (filename_entry_t *) xrealloc (filename_table,
(total_ft_entries
* sizeof (filename_entry_t)));
}
/* Shift all existing table entries up so that we can install the new
entry at filename_table[0]. */
{
register filename_entry_t *move_p;
register filename_entry_t *limit_p = &filename_table[0];
for (move_p = &filename_table[valid_ft_entries]; move_p > limit_p; move_p--)
*move_p = *(move_p-1);
}
/* Now create the new entry at filename_table[0]. */
filename_table[0].name = xstrdup (func_name);
return filename_table[0].number = ++valid_ft_entries;
}
static void
generate_srcinfo_entry (line_entry_num, files_entry_num)
unsigned line_entry_num;
unsigned files_entry_num;
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, SRCINFO_SECTION);
sprintf (label, LINE_ENTRY_LABEL_FMT, line_entry_num);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
sprintf (label, FILE_ENTRY_LABEL_FMT, files_entry_num);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
ASM_PREVIOUS_SECTION (asm_out_file);
}
#endif
void
dwarfout_line (filename, line)
register char *filename;
register unsigned line;
{
static unsigned last_line_entry_num = 0;
char label[MAX_ARTIFICIAL_LABEL_BYTES];
#ifdef AT_src_info
static unsigned prev_file_entry_num = 0;
unsigned this_file_entry_num = lookup_filename (filename);
#endif
text_section ();
sprintf (label, LINE_CODE_LABEL_FMT, ++last_line_entry_num);
ASM_OUTPUT_LABEL (asm_out_file, label);
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, LINE_SECTION);
#ifdef AT_src_info
if (this_file_entry_num != prev_file_entry_num)
{
char line_entry_label[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (line_entry_label, LINE_ENTRY_LABEL_FMT, last_line_entry_num);
ASM_OUTPUT_LABEL (asm_out_file, line_entry_label);
}
#endif
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, line);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff);
ASM_OUTPUT_DWARF_BLOCK4 (asm_out_file, label, TEXT_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
#ifdef AT_src_info
if (this_file_entry_num != prev_file_entry_num)
generate_srcinfo_entry (last_line_entry_num, this_file_entry_num);
prev_file_entry_num = this_file_entry_num;
#endif
}
/* Set up for DWARF output at the start of compilation. */
void
dwarfout_init (asm_out_file, main_input_filename)
register FILE *asm_out_file;
register char *main_input_filename;
{
#ifdef AT_src_info
unsigned first_file_entry_num;
#endif
/* allocate the initial hunk of the pending_sibling_stack. */
pending_sibling_stack = (unsigned *) xmalloc (PENDING_SIBLING_STACK_INCREMENT
* sizeof (unsigned));
pending_sibling_stack_size = PENDING_SIBLING_STACK_INCREMENT;
pending_sibling_stack_depth = 1;
/* Output a starting label for the .text section. */
fputc ('\n', asm_out_file);
text_section ();
ASM_OUTPUT_LABEL (asm_out_file, TEXT_BEGIN_LABEL);
#ifdef AT_src_info
/* Output a starting label for the .srcinfo section. This label will be
referenced by the AT_src_info attribute in the TAG_source_file DIE. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, SRCINFO_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, SRCINFO_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
#endif
#ifdef AT_addr_ranges
/* Output a starting label for the .data section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DATA_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, DATA_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Output a starting label for the .bss section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, BSS_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, BSS_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Output a starting label for the .rodata section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, RODATA_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, RODATA_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
#endif
/* Setup first DIE number == 1. */
NEXT_DIE_NUM = next_unused_dienum++;
/* Generate the initial DIE for the .debug section. Note that the
(string) value given in the AT_name attribute of the TAG_source_file
DIE will (typically) be a relative pathname and that this pathname
should be taken as being relative to the directory from which the
compiler was invoked when the given (base) source file was compiled. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DEBUG_SECTION);
output_die (output_source_file_die, main_input_filename);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Generate the initial (special) entry for the .line section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, LINE_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, LINE_BEGIN_LABEL);
ASM_OUTPUT_DWARF_BLOCK4 (asm_out_file, LINE_END_LABEL, LINE_BEGIN_LABEL);
ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Place a scope marker on the list of pending tagged types. This will
represent the global scope. */
pending_tagged_types = perm_tree_cons (NULL, NULL, pending_tagged_types);
fputc ('\n', asm_out_file);
}
/* Output stuff that dwarf requires at the end of every file. */
void
dwarfout_finalize ()
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
/* Output DIEs to represent any tagged types which are declared within the
current scope but which were never completed within the current scope. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DEBUG_SECTION);
output_pending_tagged_types ();
/* Mark the end of the chain of siblings which represent all file-scope
declarations in this compilation unit. */
/* The (null) DIE which represents the terminator for the (sibling linked)
list of file-scope items is *special*. Normally, we would just call
end_sibling_chain() at this point in order to output a word with the
value `4' and that word would act as the terminator for the list of
DIEs describing file-scope items. Unfortunately, if we were to simply
do that, the label that would follow this DIE in the .debug section
(i.e. `..D2') would *not* be properly aligned (as it must be on some
machines) to a 4 byte boundary.
In order to force the label `..D2' to get aligned to a 4 byte boundary,
the trick used is to insert extra (otherwise useless) padding bytes
into the (null) DIE that we know must preceed the ..D2 label in the
.debug section. The amount of padding required can be anywhere between
0 and 3 bytes. The length word at the start of this DIE (i.e. the one
with the padding) would normally contain the value 4, but now it will
also have to include the padding bytes, so it will instead have some
value in the range 4..7.
Fortunately, the rules of DWARF say that any DIE whose length word
contains *any* value less than 8 should be treated as a null DIE, so
this trick works out nicely. Clever, eh? Don't give me any credit
(or blame). I didn't think of this scheme. I just conformed to it.
*/
output_die (output_padded_null_die, (void *)0);
dienum_pop ();
sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM);
ASM_OUTPUT_LABEL (asm_out_file, label); /* should be ..D2 */
ASM_PREVIOUS_SECTION (asm_out_file);
#ifdef DWARF_DESCRIBE_USED_EXTERNS
/* Setup first DIE number for the .externs section. */
NEXT_DIE_NUM = next_unused_dienum++;
/* Generate the entire .externs section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, EXTERNS_SECTION);
finalizing = TRUE;
output_die (output_source_file_die, main_input_filename);
{
register tree link;
for (link = pending_extern_decls; link; link = TREE_CHAIN (link))
output_symbol (TREE_VALUE (link),
DECL_CONTEXT (TREE_VALUE (link)) != 0);
}
end_sibling_chain ();
sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM);
ASM_OUTPUT_LABEL (asm_out_file, label);
ASM_PREVIOUS_SECTION (asm_out_file);
#endif
/* Output a terminator label for the .text section. */
fputc ('\n', asm_out_file);
text_section ();
ASM_OUTPUT_LABEL (asm_out_file, TEXT_END_LABEL);
#ifdef AT_addr_ranges
/* Output a terminating entry for the .data section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, DATA_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, DATA_END_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Output a terminating entry for the .bss section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, BSS_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, BSS_END_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
/* Output a terminating entry for the .rodata section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, RODATA_SECTION);
ASM_OUTPUT_LABEL (asm_out_file, RODATA_END_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
#endif
/* Output a terminating entry for the .line section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, LINE_SECTION);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff);
ASM_OUTPUT_DWARF_BLOCK4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL);
ASM_OUTPUT_LABEL (asm_out_file, LINE_END_LABEL);
ASM_PREVIOUS_SECTION (asm_out_file);
#ifdef AT_src_info
/* Create all of the (string) entries for the .sources section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, SOURCES_SECTION);
{
register filename_entry_t *print_p;
register filename_entry_t *limit_p = &filename_table[valid_ft_entries];
for (print_p = &filename_table[0]; print_p < limit_p; print_p++)
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
sprintf (label, FILE_ENTRY_LABEL_FMT, print_p->number);
ASM_OUTPUT_LABEL (asm_out_file, label);
ASM_OUTPUT_DWARF_STRING (asm_out_file, print_p->name);
}
}
ASM_PREVIOUS_SECTION (asm_out_file);
/* Output a terminating entry for the .srcinfo section. */
fputc ('\n', asm_out_file);
ASM_CHANGE_SECTION (asm_out_file, SRCINFO_SECTION);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1);
ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1);
ASM_PREVIOUS_SECTION (asm_out_file);
#endif
}
#endif /* DWARF_DEBUGGING_INFO */