diff options
Diffstat (limited to 'gnu/usr.bin/gcc/dwarf2out.c')
-rw-r--r-- | gnu/usr.bin/gcc/dwarf2out.c | 9652 |
1 files changed, 9652 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/dwarf2out.c b/gnu/usr.bin/gcc/dwarf2out.c new file mode 100644 index 00000000000..b1f748cdd97 --- /dev/null +++ b/gnu/usr.bin/gcc/dwarf2out.c @@ -0,0 +1,9652 @@ +/* Output Dwarf2 format symbol table information from the GNU C compiler. + Copyright (C) 1992, 1993, 1995, 1996, 1997 Free Software Foundation, Inc. + Contributed by Gary Funck (gary@intrepid.com). + Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com). + Extensively modified by Jason Merrill (jason@cygnus.com). + +This file is part of 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. */ + +/* The first part of this file deals with the DWARF 2 frame unwind + information, which is also used by the GCC efficient exception handling + mechanism. The second part, controlled only by an #ifdef + DWARF2_DEBUGGING_INFO, deals with the other DWARF 2 debugging + information. */ + +#include "config.h" +#include "defaults.h" +#include <stdio.h> +#include "tree.h" +#include "flags.h" +#include "rtl.h" +#include "hard-reg-set.h" +#include "regs.h" +#include "insn-config.h" +#include "reload.h" +#include "output.h" +#include "expr.h" +#include "except.h" +#include "dwarf2.h" + +/* We cannot use <assert.h> in GCC source, since that would include + GCC's assert.h, which may not be compatible with the host compiler. */ +#undef assert +#ifdef NDEBUG +# define assert(e) +#else +# define assert(e) do { if (! (e)) abort (); } while (0) +#endif + +/* Decide whether we want to emit frame unwind information for the current + translation unit. */ + +int +dwarf2out_do_frame () +{ + return (write_symbols == DWARF2_DEBUG +#ifdef DWARF2_UNWIND_INFO + || (flag_exceptions && ! exceptions_via_longjmp) +#endif + ); +} + +#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) + +#ifndef __GNUC__ +#define inline +#endif + +/* How to start an assembler comment. */ +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START ";#" +#endif + +typedef struct dw_cfi_struct *dw_cfi_ref; +typedef struct dw_fde_struct *dw_fde_ref; +typedef union dw_cfi_oprnd_struct *dw_cfi_oprnd_ref; + +/* Call frames are described using a sequence of Call Frame + Information instructions. The register number, offset + and address fields are provided as possible operands; + their use is selected by the opcode field. */ + +typedef union dw_cfi_oprnd_struct +{ + unsigned long dw_cfi_reg_num; + long int dw_cfi_offset; + char *dw_cfi_addr; +} +dw_cfi_oprnd; + +typedef struct dw_cfi_struct +{ + dw_cfi_ref dw_cfi_next; + enum dwarf_call_frame_info dw_cfi_opc; + dw_cfi_oprnd dw_cfi_oprnd1; + dw_cfi_oprnd dw_cfi_oprnd2; +} +dw_cfi_node; + +/* All call frame descriptions (FDE's) in the GCC generated DWARF + refer to a single Common Information Entry (CIE), defined at + the beginning of the .debug_frame section. This used of a single + CIE obviates the need to keep track of multiple CIE's + in the DWARF generation routines below. */ + +typedef struct dw_fde_struct +{ + char *dw_fde_begin; + char *dw_fde_current_label; + char *dw_fde_end; + dw_cfi_ref dw_fde_cfi; +} +dw_fde_node; + +/* Maximum size (in bytes) of an artificially generated label. */ +#define MAX_ARTIFICIAL_LABEL_BYTES 30 + +/* Make sure we know the sizes of the various types dwarf can describe. These + are only defaults. If the sizes are different for your target, you should + override these values by defining the appropriate symbols in your tm.h + file. */ + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif +#ifndef PTR_SIZE +#define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT) +#endif + +/* The size in bytes of a DWARF field indicating an offset or length + relative to a debug info section, specified to be 4 bytes in the DWARF-2 + specification. The SGI/MIPS ABI defines it to be the same as PTR_SIZE. */ + +#ifndef DWARF_OFFSET_SIZE +#define DWARF_OFFSET_SIZE 4 +#endif + +#define DWARF_VERSION 2 + +/* Round SIZE up to the nearest BOUNDARY. */ +#define DWARF_ROUND(SIZE,BOUNDARY) \ + (((SIZE) + (BOUNDARY) - 1) & ~((BOUNDARY) - 1)) + +/* Offsets recorded in opcodes are a multiple of this alignment factor. */ +#ifdef STACK_GROWS_DOWNWARD +#define DWARF_CIE_DATA_ALIGNMENT (-UNITS_PER_WORD) +#else +#define DWARF_CIE_DATA_ALIGNMENT UNITS_PER_WORD +#endif + +/* A pointer to the base of a table that contains frame description + information for each routine. */ +static dw_fde_ref fde_table; + +/* Number of elements currently allocated for fde_table. */ +static unsigned fde_table_allocated; + +/* Number of elements in fde_table currently in use. */ +static unsigned fde_table_in_use; + +/* Size (in elements) of increments by which we may expand the + fde_table. */ +#define FDE_TABLE_INCREMENT 256 + +/* A list of call frame insns for the CIE. */ +static dw_cfi_ref cie_cfi_head; + +/* The number of the current function definition for which debugging + information is being generated. These numbers range from 1 up to the + maximum number of function definitions contained within the current + compilation unit. These numbers are used to create unique label id's + unique to each function definition. */ +static unsigned current_funcdef_number = 0; + +/* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram + attribute that accelerates the lookup of the FDE associated + with the subprogram. This variable holds the table index of the FDE + associated with the current function (body) definition. */ +static unsigned current_funcdef_fde; + +/* Forward declarations for functions defined in this file. */ + +static char *stripattributes PROTO((char *)); +static char *dwarf_cfi_name PROTO((unsigned)); +static dw_cfi_ref new_cfi PROTO((void)); +static void add_cfi PROTO((dw_cfi_ref *, dw_cfi_ref)); +static unsigned long size_of_uleb128 PROTO((unsigned long)); +static unsigned long size_of_sleb128 PROTO((long)); +static void output_uleb128 PROTO((unsigned long)); +static void output_sleb128 PROTO((long)); +static void add_fde_cfi PROTO((char *, dw_cfi_ref)); +static void lookup_cfa_1 PROTO((dw_cfi_ref, unsigned long *, + long *)); +static void lookup_cfa PROTO((unsigned long *, long *)); +static void reg_save PROTO((char *, unsigned, unsigned, + long)); +static void initial_return_save PROTO((rtx)); +static void output_cfi PROTO((dw_cfi_ref, dw_fde_ref)); +static void output_call_frame_info PROTO((int)); +static unsigned reg_number PROTO((rtx)); + +/* Definitions of defaults for assembler-dependent names of various + pseudo-ops and section names. + Theses may be overridden in the tm.h file (if necessary) for a particular + assembler. */ + +#ifdef OBJECT_FORMAT_ELF +#ifndef UNALIGNED_SHORT_ASM_OP +#define UNALIGNED_SHORT_ASM_OP ".2byte" +#endif +#ifndef UNALIGNED_INT_ASM_OP +#define UNALIGNED_INT_ASM_OP ".4byte" +#endif +#ifndef UNALIGNED_DOUBLE_INT_ASM_OP +#define UNALIGNED_DOUBLE_INT_ASM_OP ".8byte" +#endif +#endif /* OBJECT_FORMAT_ELF */ + +#ifndef ASM_BYTE_OP +#define ASM_BYTE_OP ".byte" +#endif + +/* Data and reference forms for relocatable data. */ +#define DW_FORM_data (DWARF_OFFSET_SIZE == 8 ? DW_FORM_data8 : DW_FORM_data4) +#define DW_FORM_ref (DWARF_OFFSET_SIZE == 8 ? DW_FORM_ref8 : DW_FORM_ref4) + +/* Pseudo-op for defining a new section. */ +#ifndef SECTION_ASM_OP +#define SECTION_ASM_OP ".section" +#endif + +/* The default format used by the ASM_OUTPUT_SECTION macro (see below) to + print the SECTION_ASM_OP and the section name. The default here works for + almost all svr4 assemblers, except for the sparc, where the section name + must be enclosed in double quotes. (See sparcv4.h). */ +#ifndef SECTION_FORMAT +#ifdef PUSHSECTION_FORMAT +#define SECTION_FORMAT PUSHSECTION_FORMAT +#else +#define SECTION_FORMAT "\t%s\t%s\n" +#endif +#endif + +#ifndef FRAME_SECTION +#define FRAME_SECTION ".debug_frame" +#endif + +#ifndef FUNC_BEGIN_LABEL +#define FUNC_BEGIN_LABEL "LFB" +#endif +#ifndef FUNC_END_LABEL +#define FUNC_END_LABEL "LFE" +#endif +#define CIE_AFTER_SIZE_LABEL "LSCIE" +#define CIE_END_LABEL "LECIE" +#define CIE_LENGTH_LABEL "LLCIE" +#define FDE_AFTER_SIZE_LABEL "LSFDE" +#define FDE_END_LABEL "LEFDE" +#define FDE_LENGTH_LABEL "LLFDE" + +/* Definitions of defaults for various types of primitive assembly language + output operations. These may be overridden from within the tm.h file, + but typically, that is unnecessary. */ + +#ifndef ASM_OUTPUT_SECTION +#define ASM_OUTPUT_SECTION(FILE, SECTION) \ + fprintf ((FILE), SECTION_FORMAT, SECTION_ASM_OP, SECTION) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA1 +#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, VALUE) +#endif + +#ifdef UNALIGNED_INT_ASM_OP + +#ifndef UNALIGNED_OFFSET_ASM_OP +#define UNALIGNED_OFFSET_ASM_OP \ + (DWARF_OFFSET_SIZE == 8 ? UNALIGNED_DOUBLE_INT_ASM_OP : UNALIGNED_INT_ASM_OP) +#endif + +#ifndef UNALIGNED_WORD_ASM_OP +#define UNALIGNED_WORD_ASM_OP \ + (PTR_SIZE == 8 ? UNALIGNED_DOUBLE_INT_ASM_OP : UNALIGNED_INT_ASM_OP) +#endif + +#ifndef ASM_OUTPUT_DWARF_DELTA2 +#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_SHORT_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DELTA4 +#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DELTA +#define ASM_OUTPUT_DWARF_DELTA(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_OFFSET_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR_DELTA +#define ASM_OUTPUT_DWARF_ADDR_DELTA(FILE,LABEL1,LABEL2) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \ + assemble_name (FILE, LABEL1); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, LABEL2); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR +#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_WORD_ASM_OP); \ + assemble_name (FILE, LABEL); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR_CONST +#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,ADDR) \ + fprintf ((FILE), "\t%s\t%s", UNALIGNED_WORD_ASM_OP, (ADDR)) +#endif + +#ifndef ASM_OUTPUT_DWARF_OFFSET +#define ASM_OUTPUT_DWARF_OFFSET(FILE,LABEL) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_OFFSET_ASM_OP); \ + assemble_name (FILE, LABEL); \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA2 +#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_SHORT_ASM_OP, (unsigned) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA4 +#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_INT_ASM_OP, (unsigned) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA +#define ASM_OUTPUT_DWARF_DATA(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_OFFSET_ASM_OP, \ + (unsigned long) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_ADDR_DATA +#define ASM_OUTPUT_DWARF_ADDR_DATA(FILE,VALUE) \ + fprintf ((FILE), "\t%s\t0x%lx", UNALIGNED_WORD_ASM_OP, \ + (unsigned long) VALUE) +#endif + +#ifndef ASM_OUTPUT_DWARF_DATA8 +#define ASM_OUTPUT_DWARF_DATA8(FILE,HIGH_VALUE,LOW_VALUE) \ + do { \ + if (WORDS_BIG_ENDIAN) \ + { \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ + fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ + } \ + else \ + { \ + fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\ + fprintf ((FILE), "\t%s\t0x%x", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \ + } \ + } while (0) +#endif + +#ifndef ASM_OUTPUT_DWARF_VALUE4 +#define ASM_OUTPUT_DWARF_VALUE4(FILE,LABEL) \ + do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ + assemble_name (FILE, LABEL); \ + } while (0) +#endif + +#else /* UNALIGNED_INT_ASM_OP */ + +/* We don't have unaligned support, let's hope the normal output works for + .debug_frame. */ + +#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \ + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, LABEL), PTR_SIZE, 1) + +#define ASM_OUTPUT_DWARF_OFFSET(FILE,LABEL) \ + assemble_integer (gen_rtx (SYMBOL_REF, SImode, LABEL), 4, 1) + +#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \ + assemble_integer (gen_rtx (MINUS, HImode, \ + gen_rtx (SYMBOL_REF, Pmode, LABEL1), \ + gen_rtx (SYMBOL_REF, Pmode, LABEL2)), \ + 2, 1) + +#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \ + assemble_integer (gen_rtx (MINUS, SImode, \ + gen_rtx (SYMBOL_REF, Pmode, LABEL1), \ + gen_rtx (SYMBOL_REF, Pmode, LABEL2)), \ + 4, 1) + +#define ASM_OUTPUT_DWARF_ADDR_DELTA(FILE,LABEL1,LABEL2) \ + assemble_integer (gen_rtx (MINUS, Pmode, \ + gen_rtx (SYMBOL_REF, Pmode, LABEL1), \ + gen_rtx (SYMBOL_REF, Pmode, LABEL2)), \ + PTR_SIZE, 1) + +#define ASM_OUTPUT_DWARF_DELTA(FILE,LABEL1,LABEL2) \ + ASM_OUTPUT_DWARF_DELTA4 (FILE,LABEL1,LABEL2) + +#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \ + assemble_integer (GEN_INT (VALUE), 4, 1) + +#define ASM_OUTPUT_DWARF_VALUE4(FILE,LABEL) \ + assemble_integer (gen_rtx (SYMBOL_REF, Pmode, LABEL), 4, 1) + +#endif /* UNALIGNED_INT_ASM_OP */ + +#ifdef SET_ASM_OP +#ifndef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL +#define ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL(FILE, SY, HI, LO) \ + do { fprintf ((FILE), "\t%s\t", SET_ASM_OP); \ + assemble_name (FILE, SY); \ + fprintf ((FILE), ","); \ + assemble_name (FILE, HI); \ + fprintf ((FILE), "-"); \ + assemble_name (FILE, LO); \ + fprintf ((FILE), "\n"); \ + } while (0) +#endif +#endif /* SET_ASM_OP */ + +/* This is similar to the default ASM_OUTPUT_ASCII, except that no trailing + newline is produced. When flag_debug_asm is asserted, we add commentary + at the end of the line, so we must avoid output of a newline here. */ +#ifndef ASM_OUTPUT_DWARF_STRING +#define ASM_OUTPUT_DWARF_STRING(FILE,P) \ + do { \ + register int slen = strlen(P); \ + register char *p = (P); \ + register int i; \ + fprintf (FILE, "\t.ascii \""); \ + for (i = 0; i < slen; i++) \ + { \ + register int c = p[i]; \ + if (c == '\"' || c == '\\') \ + putc ('\\', FILE); \ + if (c >= ' ' && c < 0177) \ + putc (c, FILE); \ + else \ + { \ + fprintf (FILE, "\\%o", c); \ + } \ + } \ + fprintf (FILE, "\\0\""); \ + } \ + while (0) +#endif + +/* The DWARF 2 CFA column which tracks the return address. Normally this + is the column for PC, or the first column after all of the hard + registers. */ +#ifndef DWARF_FRAME_RETURN_COLUMN +#ifdef PC_REGNUM +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (PC_REGNUM) +#else +#define DWARF_FRAME_RETURN_COLUMN FIRST_PSEUDO_REGISTER +#endif +#endif + +/* The mapping from gcc register number to DWARF 2 CFA column number. By + default, we just provide columns for all registers. */ +#ifndef DWARF_FRAME_REGNUM +#define DWARF_FRAME_REGNUM(REG) DBX_REGISTER_NUMBER (REG) +#endif + +/* Hook used by __throw. */ + +rtx +expand_builtin_dwarf_fp_regnum () +{ + return GEN_INT (DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM)); +} + +/* The offset from the incoming value of %sp to the top of the stack frame + for the current function. */ +#ifndef INCOMING_FRAME_SP_OFFSET +#define INCOMING_FRAME_SP_OFFSET 0 +#endif + +/* Return a pointer to a copy of the section string name S with all + attributes stripped off. */ + +static inline char * +stripattributes (s) + char *s; +{ + char *stripped = xstrdup (s); + char *p = stripped; + + while (*p && *p != ',') + p++; + + *p = '\0'; + return stripped; +} + +/* Return the register number described by a given RTL node. */ + +static unsigned +reg_number (rtl) + register rtx rtl; +{ + register unsigned regno = REGNO (rtl); + + if (regno >= FIRST_PSEUDO_REGISTER) + { + warning ("internal regno botch: regno = %d\n", regno); + regno = 0; + } + + regno = DBX_REGISTER_NUMBER (regno); + return regno; +} + +struct reg_size_range +{ + int beg; + int end; + int size; +}; + +/* Given a register number in REG_TREE, return an rtx for its size in bytes. + We do this in kind of a roundabout way, by building up a list of + register size ranges and seeing where our register falls in one of those + ranges. We need to do it this way because REG_TREE is not a constant, + and the target macros were not designed to make this task easy. */ + +rtx +expand_builtin_dwarf_reg_size (reg_tree, target) + tree reg_tree; + rtx target; +{ + int size; + struct reg_size_range ranges[5]; + tree t, t2; + + int i = 0; + int n_ranges = 0; + int last_size = -1; + + for (; i < FIRST_PSEUDO_REGISTER; ++i) + { + /* The return address is out of order on the MIPS, and we don't use + copy_reg for it anyway, so we don't care here how large it is. */ + if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN) + continue; + + size = GET_MODE_SIZE (reg_raw_mode[i]); + if (size != last_size) + { + ranges[n_ranges].beg = i; + ranges[n_ranges].size = last_size = GET_MODE_SIZE (reg_raw_mode[i]); + ++n_ranges; + assert (n_ranges < 5); + } + ranges[n_ranges-1].end = i; + } + + /* The usual case: fp regs surrounded by general regs. */ + if (n_ranges == 3 && ranges[0].size == ranges[2].size) + { + assert ((DWARF_FRAME_REGNUM (ranges[1].end) + - DWARF_FRAME_REGNUM (ranges[1].beg)) + == ranges[1].end - ranges[1].beg); + t = fold (build (GE_EXPR, integer_type_node, reg_tree, + build_int_2 (DWARF_FRAME_REGNUM (ranges[1].beg), 0))); + t2 = fold (build (LE_EXPR, integer_type_node, reg_tree, + build_int_2 (DWARF_FRAME_REGNUM (ranges[1].end), 0))); + t = fold (build (TRUTH_ANDIF_EXPR, integer_type_node, t, t2)); + t = fold (build (COND_EXPR, integer_type_node, t, + build_int_2 (ranges[1].size, 0), + build_int_2 (ranges[0].size, 0))); + } + else + { + --n_ranges; + t = build_int_2 (ranges[n_ranges].size, 0); + size = DWARF_FRAME_REGNUM (ranges[n_ranges].beg); + for (; n_ranges--; ) + { + assert ((DWARF_FRAME_REGNUM (ranges[n_ranges].end) + - DWARF_FRAME_REGNUM (ranges[n_ranges].beg)) + == ranges[n_ranges].end - ranges[n_ranges].beg); + assert (DWARF_FRAME_REGNUM (ranges[n_ranges].beg) < size); + size = DWARF_FRAME_REGNUM (ranges[n_ranges].beg); + t2 = fold (build (LE_EXPR, integer_type_node, reg_tree, + build_int_2 (DWARF_FRAME_REGNUM + (ranges[n_ranges].end), 0))); + t = fold (build (COND_EXPR, integer_type_node, t2, + build_int_2 (ranges[n_ranges].size, 0), t)); + } + } + return expand_expr (t, target, Pmode, 0); +} + +/* Convert a DWARF call frame info. operation to its string name */ + +static char * +dwarf_cfi_name (cfi_opc) + register unsigned cfi_opc; +{ + switch (cfi_opc) + { + case DW_CFA_advance_loc: + return "DW_CFA_advance_loc"; + case DW_CFA_offset: + return "DW_CFA_offset"; + case DW_CFA_restore: + return "DW_CFA_restore"; + case DW_CFA_nop: + return "DW_CFA_nop"; + case DW_CFA_set_loc: + return "DW_CFA_set_loc"; + case DW_CFA_advance_loc1: + return "DW_CFA_advance_loc1"; + case DW_CFA_advance_loc2: + return "DW_CFA_advance_loc2"; + case DW_CFA_advance_loc4: + return "DW_CFA_advance_loc4"; + case DW_CFA_offset_extended: + return "DW_CFA_offset_extended"; + case DW_CFA_restore_extended: + return "DW_CFA_restore_extended"; + case DW_CFA_undefined: + return "DW_CFA_undefined"; + case DW_CFA_same_value: + return "DW_CFA_same_value"; + case DW_CFA_register: + return "DW_CFA_register"; + case DW_CFA_remember_state: + return "DW_CFA_remember_state"; + case DW_CFA_restore_state: + return "DW_CFA_restore_state"; + case DW_CFA_def_cfa: + return "DW_CFA_def_cfa"; + case DW_CFA_def_cfa_register: + return "DW_CFA_def_cfa_register"; + case DW_CFA_def_cfa_offset: + return "DW_CFA_def_cfa_offset"; + + /* SGI/MIPS specific */ + case DW_CFA_MIPS_advance_loc8: + return "DW_CFA_MIPS_advance_loc8"; + + /* GNU extensions */ + case DW_CFA_GNU_window_save: + return "DW_CFA_GNU_window_save"; + case DW_CFA_GNU_args_size: + return "DW_CFA_GNU_args_size"; + + default: + return "DW_CFA_<unknown>"; + } +} + +/* Return a pointer to a newly allocated Call Frame Instruction. */ + +static inline dw_cfi_ref +new_cfi () +{ + register dw_cfi_ref cfi = (dw_cfi_ref) xmalloc (sizeof (dw_cfi_node)); + + cfi->dw_cfi_next = NULL; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0; + cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0; + + return cfi; +} + +/* Add a Call Frame Instruction to list of instructions. */ + +static inline void +add_cfi (list_head, cfi) + register dw_cfi_ref *list_head; + register dw_cfi_ref cfi; +{ + register dw_cfi_ref *p; + + /* Find the end of the chain. */ + for (p = list_head; (*p) != NULL; p = &(*p)->dw_cfi_next) + ; + + *p = cfi; +} + +/* Generate a new label for the CFI info to refer to. */ + +char * +dwarf2out_cfi_label () +{ + static char label[20]; + static unsigned long label_num = 0; + + ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", label_num++); + ASM_OUTPUT_LABEL (asm_out_file, label); + + return label; +} + +/* Add CFI to the current fde at the PC value indicated by LABEL if specified, + or to the CIE if LABEL is NULL. */ + +static void +add_fde_cfi (label, cfi) + register char *label; + register dw_cfi_ref cfi; +{ + if (label) + { + register dw_fde_ref fde = &fde_table[fde_table_in_use - 1]; + + if (*label == 0) + label = dwarf2out_cfi_label (); + + if (fde->dw_fde_current_label == NULL + || strcmp (label, fde->dw_fde_current_label) != 0) + { + register dw_cfi_ref xcfi; + + fde->dw_fde_current_label = label = xstrdup (label); + + /* Set the location counter to the new label. */ + xcfi = new_cfi (); + xcfi->dw_cfi_opc = DW_CFA_advance_loc4; + xcfi->dw_cfi_oprnd1.dw_cfi_addr = label; + add_cfi (&fde->dw_fde_cfi, xcfi); + } + + add_cfi (&fde->dw_fde_cfi, cfi); + } + + else + add_cfi (&cie_cfi_head, cfi); +} + +/* Subroutine of lookup_cfa. */ + +static inline void +lookup_cfa_1 (cfi, regp, offsetp) + register dw_cfi_ref cfi; + register unsigned long *regp; + register long *offsetp; +{ + switch (cfi->dw_cfi_opc) + { + case DW_CFA_def_cfa_offset: + *offsetp = cfi->dw_cfi_oprnd1.dw_cfi_offset; + break; + case DW_CFA_def_cfa_register: + *regp = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; + break; + case DW_CFA_def_cfa: + *regp = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; + *offsetp = cfi->dw_cfi_oprnd2.dw_cfi_offset; + break; + default: + break; + } +} + +/* Find the previous value for the CFA. */ + +static void +lookup_cfa (regp, offsetp) + register unsigned long *regp; + register long *offsetp; +{ + register dw_cfi_ref cfi; + + *regp = (unsigned long) -1; + *offsetp = 0; + + for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) + lookup_cfa_1 (cfi, regp, offsetp); + + if (fde_table_in_use) + { + register dw_fde_ref fde = &fde_table[fde_table_in_use - 1]; + for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) + lookup_cfa_1 (cfi, regp, offsetp); + } +} + +/* The current rule for calculating the DWARF2 canonical frame address. */ +static unsigned long cfa_reg; +static long cfa_offset; + +/* The register used for saving registers to the stack, and its offset + from the CFA. */ +static unsigned cfa_store_reg; +static long cfa_store_offset; + +/* The running total of the size of arguments pushed onto the stack. */ +static long args_size; + +/* Entry point to update the canonical frame address (CFA). + LABEL is passed to add_fde_cfi. The value of CFA is now to be + calculated from REG+OFFSET. */ + +void +dwarf2out_def_cfa (label, reg, offset) + register char *label; + register unsigned reg; + register long offset; +{ + register dw_cfi_ref cfi; + unsigned long old_reg; + long old_offset; + + cfa_reg = reg; + cfa_offset = offset; + if (cfa_store_reg == reg) + cfa_store_offset = offset; + + reg = DWARF_FRAME_REGNUM (reg); + lookup_cfa (&old_reg, &old_offset); + + if (reg == old_reg && offset == old_offset) + return; + + cfi = new_cfi (); + + if (reg == old_reg) + { + cfi->dw_cfi_opc = DW_CFA_def_cfa_offset; + cfi->dw_cfi_oprnd1.dw_cfi_offset = offset; + } + +#ifndef MIPS_DEBUGGING_INFO /* SGI dbx thinks this means no offset. */ + else if (offset == old_offset && old_reg != (unsigned long) -1) + { + cfi->dw_cfi_opc = DW_CFA_def_cfa_register; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; + } +#endif + + else + { + cfi->dw_cfi_opc = DW_CFA_def_cfa; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; + cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; + } + + add_fde_cfi (label, cfi); +} + +/* Add the CFI for saving a register. REG is the CFA column number. + LABEL is passed to add_fde_cfi. + If SREG is -1, the register is saved at OFFSET from the CFA; + otherwise it is saved in SREG. */ + +static void +reg_save (label, reg, sreg, offset) + register char * label; + register unsigned reg; + register unsigned sreg; + register long offset; +{ + register dw_cfi_ref cfi = new_cfi (); + + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; + + if (sreg == -1) + { + if (reg & ~0x3f) + /* The register number won't fit in 6 bits, so we have to use + the long form. */ + cfi->dw_cfi_opc = DW_CFA_offset_extended; + else + cfi->dw_cfi_opc = DW_CFA_offset; + + offset /= DWARF_CIE_DATA_ALIGNMENT; + assert (offset >= 0); + cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; + } + else + { + cfi->dw_cfi_opc = DW_CFA_register; + cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg; + } + + add_fde_cfi (label, cfi); +} + +/* Add the CFI for saving a register window. LABEL is passed to reg_save. + This CFI tells the unwinder that it needs to restore the window registers + from the previous frame's window save area. + + ??? Perhaps we should note in the CIE where windows are saved (instead of + assuming 0(cfa)) and what registers are in the window. */ + +void +dwarf2out_window_save (label) + register char * label; +{ + register dw_cfi_ref cfi = new_cfi (); + cfi->dw_cfi_opc = DW_CFA_GNU_window_save; + add_fde_cfi (label, cfi); +} + +/* Add a CFI to update the running total of the size of arguments + pushed onto the stack. */ + +void +dwarf2out_args_size (label, size) + char *label; + long size; +{ + register dw_cfi_ref cfi = new_cfi (); + cfi->dw_cfi_opc = DW_CFA_GNU_args_size; + cfi->dw_cfi_oprnd1.dw_cfi_offset = size; + add_fde_cfi (label, cfi); +} + +/* Entry point for saving a register to the stack. REG is the GCC register + number. LABEL and OFFSET are passed to reg_save. */ + +void +dwarf2out_reg_save (label, reg, offset) + register char * label; + register unsigned reg; + register long offset; +{ + reg_save (label, DWARF_FRAME_REGNUM (reg), -1, offset); +} + +/* Entry point for saving the return address in the stack. + LABEL and OFFSET are passed to reg_save. */ + +void +dwarf2out_return_save (label, offset) + register char * label; + register long offset; +{ + reg_save (label, DWARF_FRAME_RETURN_COLUMN, -1, offset); +} + +/* Entry point for saving the return address in a register. + LABEL and SREG are passed to reg_save. */ + +void +dwarf2out_return_reg (label, sreg) + register char * label; + register unsigned sreg; +{ + reg_save (label, DWARF_FRAME_RETURN_COLUMN, sreg, 0); +} + +/* Record the initial position of the return address. RTL is + INCOMING_RETURN_ADDR_RTX. */ + +static void +initial_return_save (rtl) + register rtx rtl; +{ + unsigned reg = -1; + long offset = 0; + + switch (GET_CODE (rtl)) + { + case REG: + /* RA is in a register. */ + reg = reg_number (rtl); + break; + case MEM: + /* RA is on the stack. */ + rtl = XEXP (rtl, 0); + switch (GET_CODE (rtl)) + { + case REG: + assert (REGNO (rtl) == STACK_POINTER_REGNUM); + offset = 0; + break; + case PLUS: + assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); + offset = INTVAL (XEXP (rtl, 1)); + break; + case MINUS: + assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); + offset = -INTVAL (XEXP (rtl, 1)); + break; + default: + abort (); + } + break; + case PLUS: + /* The return address is at some offset from any value we can + actually load. For instance, on the SPARC it is in %i7+8. Just + ignore the offset for now; it doesn't matter for unwinding frames. */ + assert (GET_CODE (XEXP (rtl, 1)) == CONST_INT); + initial_return_save (XEXP (rtl, 0)); + return; + default: + abort (); + } + + reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa_offset); +} + +/* Check INSN to see if it looks like a push or a stack adjustment, and + make a note of it if it does. EH uses this information to find out how + much extra space it needs to pop off the stack. */ + +static void +dwarf2out_stack_adjust (insn) + rtx insn; +{ + long offset; + char *label; + + if (GET_CODE (insn) == BARRIER) + { + /* When we see a BARRIER, we know to reset args_size to 0. Usually + the compiler will have already emitted a stack adjustment, but + doesn't bother for calls to noreturn functions. */ +#ifdef STACK_GROWS_DOWNWARD + offset = -args_size; +#else + offset = args_size; +#endif + } + else if (GET_CODE (PATTERN (insn)) == SET) + { + rtx src, dest; + enum rtx_code code; + + insn = PATTERN (insn); + src = SET_SRC (insn); + dest = SET_DEST (insn); + + if (dest == stack_pointer_rtx) + { + /* (set (reg sp) (plus (reg sp) (const_int))) */ + code = GET_CODE (src); + if (! (code == PLUS || code == MINUS) + || XEXP (src, 0) != stack_pointer_rtx + || GET_CODE (XEXP (src, 1)) != CONST_INT) + return; + + offset = INTVAL (XEXP (src, 1)); + } + else if (GET_CODE (dest) == MEM) + { + /* (set (mem (pre_dec (reg sp))) (foo)) */ + src = XEXP (dest, 0); + code = GET_CODE (src); + + if (! (code == PRE_DEC || code == PRE_INC) + || XEXP (src, 0) != stack_pointer_rtx) + return; + + offset = GET_MODE_SIZE (GET_MODE (dest)); + } + else + return; + + if (code == PLUS || code == PRE_INC) + offset = -offset; + } + else + return; + + if (offset == 0) + return; + + if (cfa_reg == STACK_POINTER_REGNUM) + cfa_offset += offset; + +#ifndef STACK_GROWS_DOWNWARD + offset = -offset; +#endif + args_size += offset; + if (args_size < 0) + args_size = 0; + + label = dwarf2out_cfi_label (); + dwarf2out_def_cfa (label, cfa_reg, cfa_offset); + dwarf2out_args_size (label, args_size); +} + +/* Record call frame debugging information for INSN, which either + sets SP or FP (adjusting how we calculate the frame address) or saves a + register to the stack. If INSN is NULL_RTX, initialize our state. */ + +void +dwarf2out_frame_debug (insn) + rtx insn; +{ + char *label; + rtx src, dest; + long offset; + + /* A temporary register used in adjusting SP or setting up the store_reg. */ + static unsigned cfa_temp_reg; + static long cfa_temp_value; + + if (insn == NULL_RTX) + { + /* Set up state for generating call frame debug info. */ + lookup_cfa (&cfa_reg, &cfa_offset); + assert (cfa_reg == DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)); + cfa_reg = STACK_POINTER_REGNUM; + cfa_store_reg = cfa_reg; + cfa_store_offset = cfa_offset; + cfa_temp_reg = -1; + cfa_temp_value = 0; + return; + } + + if (! RTX_FRAME_RELATED_P (insn)) + { + dwarf2out_stack_adjust (insn); + return; + } + + label = dwarf2out_cfi_label (); + + insn = PATTERN (insn); + /* Assume that in a PARALLEL prologue insn, only the first elt is + significant. Currently this is true. */ + if (GET_CODE (insn) == PARALLEL) + insn = XVECEXP (insn, 0, 0); + assert (GET_CODE (insn) == SET); + + src = SET_SRC (insn); + dest = SET_DEST (insn); + + switch (GET_CODE (dest)) + { + case REG: + /* Update the CFA rule wrt SP or FP. Make sure src is + relative to the current CFA register. */ + switch (GET_CODE (src)) + { + /* Setting FP from SP. */ + case REG: + assert (cfa_reg == REGNO (src)); + assert (REGNO (dest) == STACK_POINTER_REGNUM + || (frame_pointer_needed + && REGNO (dest) == HARD_FRAME_POINTER_REGNUM)); + cfa_reg = REGNO (dest); + break; + + case PLUS: + case MINUS: + if (dest == stack_pointer_rtx) + { + /* Adjusting SP. */ + switch (GET_CODE (XEXP (src, 1))) + { + case CONST_INT: + offset = INTVAL (XEXP (src, 1)); + break; + case REG: + assert (REGNO (XEXP (src, 1)) == cfa_temp_reg); + offset = cfa_temp_value; + break; + default: + abort (); + } + + if (XEXP (src, 0) == hard_frame_pointer_rtx) + { + /* Restoring SP from FP in the epilogue. */ + assert (cfa_reg == HARD_FRAME_POINTER_REGNUM); + cfa_reg = STACK_POINTER_REGNUM; + } + else + assert (XEXP (src, 0) == stack_pointer_rtx); + + if (GET_CODE (src) == PLUS) + offset = -offset; + if (cfa_reg == STACK_POINTER_REGNUM) + cfa_offset += offset; + if (cfa_store_reg == STACK_POINTER_REGNUM) + cfa_store_offset += offset; + } + else + { + /* Initializing the store base register. */ + assert (GET_CODE (src) == PLUS); + assert (XEXP (src, 1) == stack_pointer_rtx); + assert (GET_CODE (XEXP (src, 0)) == REG + && REGNO (XEXP (src, 0)) == cfa_temp_reg); + assert (cfa_reg == STACK_POINTER_REGNUM); + cfa_store_reg = REGNO (dest); + cfa_store_offset = cfa_offset - cfa_temp_value; + } + break; + + case CONST_INT: + cfa_temp_reg = REGNO (dest); + cfa_temp_value = INTVAL (src); + break; + + case IOR: + assert (GET_CODE (XEXP (src, 0)) == REG + && REGNO (XEXP (src, 0)) == cfa_temp_reg); + assert (REGNO (dest) == cfa_temp_reg); + assert (GET_CODE (XEXP (src, 1)) == CONST_INT); + cfa_temp_value |= INTVAL (XEXP (src, 1)); + break; + + default: + abort (); + } + dwarf2out_def_cfa (label, cfa_reg, cfa_offset); + break; + + case MEM: + /* Saving a register to the stack. Make sure dest is relative to the + CFA register. */ + assert (GET_CODE (src) == REG); + switch (GET_CODE (XEXP (dest, 0))) + { + /* With a push. */ + case PRE_INC: + case PRE_DEC: + offset = GET_MODE_SIZE (GET_MODE (dest)); + if (GET_CODE (XEXP (dest, 0)) == PRE_INC) + offset = -offset; + + assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM); + assert (cfa_store_reg == STACK_POINTER_REGNUM); + cfa_store_offset += offset; + if (cfa_reg == STACK_POINTER_REGNUM) + cfa_offset = cfa_store_offset; + + offset = -cfa_store_offset; + break; + + /* With an offset. */ + case PLUS: + case MINUS: + offset = INTVAL (XEXP (XEXP (dest, 0), 1)); + if (GET_CODE (src) == MINUS) + offset = -offset; + + assert (cfa_store_reg == REGNO (XEXP (XEXP (dest, 0), 0))); + offset -= cfa_store_offset; + break; + + default: + abort (); + } + dwarf2out_def_cfa (label, cfa_reg, cfa_offset); + dwarf2out_reg_save (label, REGNO (src), offset); + break; + + default: + abort (); + } +} + +/* Return the size of an unsigned LEB128 quantity. */ + +static inline unsigned long +size_of_uleb128 (value) + register unsigned long value; +{ + register unsigned long size = 0; + register unsigned byte; + + do + { + byte = (value & 0x7f); + value >>= 7; + size += 1; + } + while (value != 0); + + return size; +} + +/* Return the size of a signed LEB128 quantity. */ + +static inline unsigned long +size_of_sleb128 (value) + register long value; +{ + register unsigned long size = 0; + register unsigned byte; + + do + { + byte = (value & 0x7f); + value >>= 7; + size += 1; + } + while (!(((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + + return size; +} + +/* Output an unsigned LEB128 quantity. */ + +static void +output_uleb128 (value) + register unsigned long value; +{ + unsigned long save_value = value; + + fprintf (asm_out_file, "\t%s\t", ASM_BYTE_OP); + do + { + register unsigned byte = (value & 0x7f); + value >>= 7; + if (value != 0) + /* More bytes to follow. */ + byte |= 0x80; + + fprintf (asm_out_file, "0x%x", byte); + if (value != 0) + fprintf (asm_out_file, ","); + } + while (value != 0); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s ULEB128 0x%x", ASM_COMMENT_START, save_value); +} + +/* Output an signed LEB128 quantity. */ + +static void +output_sleb128 (value) + register long value; +{ + register int more; + register unsigned byte; + long save_value = value; + + fprintf (asm_out_file, "\t%s\t", ASM_BYTE_OP); + do + { + byte = (value & 0x7f); + /* arithmetic shift */ + value >>= 7; + more = !((((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + if (more) + byte |= 0x80; + + fprintf (asm_out_file, "0x%x", byte); + if (more) + fprintf (asm_out_file, ","); + } + + while (more); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s SLEB128 %d", ASM_COMMENT_START, save_value); +} + +/* Output a Call Frame Information opcode and its operand(s). */ + +static void +output_cfi (cfi, fde) + register dw_cfi_ref cfi; + register dw_fde_ref fde; +{ + if (cfi->dw_cfi_opc == DW_CFA_advance_loc) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + cfi->dw_cfi_opc + | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_CFA_advance_loc 0x%x", + ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset); + fputc ('\n', asm_out_file); + } + + else if (cfi->dw_cfi_opc == DW_CFA_offset) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + cfi->dw_cfi_opc + | (cfi->dw_cfi_oprnd1.dw_cfi_reg_num & 0x3f)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_CFA_offset, column 0x%x", + ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + + fputc ('\n', asm_out_file); + output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset); + fputc ('\n', asm_out_file); + } + else if (cfi->dw_cfi_opc == DW_CFA_restore) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + cfi->dw_cfi_opc + | (cfi->dw_cfi_oprnd1.dw_cfi_reg_num & 0x3f)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_CFA_restore, column 0x%x", + ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + + fputc ('\n', asm_out_file); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, cfi->dw_cfi_opc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", ASM_COMMENT_START, + dwarf_cfi_name (cfi->dw_cfi_opc)); + + fputc ('\n', asm_out_file); + switch (cfi->dw_cfi_opc) + { + case DW_CFA_set_loc: + ASM_OUTPUT_DWARF_ADDR (asm_out_file, cfi->dw_cfi_oprnd1.dw_cfi_addr); + fputc ('\n', asm_out_file); + break; + case DW_CFA_advance_loc1: + /* TODO: not currently implemented. */ + abort (); + break; + case DW_CFA_advance_loc2: + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, + cfi->dw_cfi_oprnd1.dw_cfi_addr, + fde->dw_fde_current_label); + fputc ('\n', asm_out_file); + fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; + break; + case DW_CFA_advance_loc4: + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, + cfi->dw_cfi_oprnd1.dw_cfi_addr, + fde->dw_fde_current_label); + fputc ('\n', asm_out_file); + fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; + break; +#ifdef MIPS_DEBUGGING_INFO + case DW_CFA_MIPS_advance_loc8: + /* TODO: not currently implemented. */ + abort (); + break; +#endif + case DW_CFA_offset_extended: + case DW_CFA_def_cfa: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + fputc ('\n', asm_out_file); + output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset); + fputc ('\n', asm_out_file); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + fputc ('\n', asm_out_file); + break; + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + fputc ('\n', asm_out_file); + break; + case DW_CFA_register: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); + fputc ('\n', asm_out_file); + output_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_reg_num); + fputc ('\n', asm_out_file); + break; + case DW_CFA_def_cfa_offset: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset); + fputc ('\n', asm_out_file); + break; + case DW_CFA_GNU_window_save: + break; + case DW_CFA_GNU_args_size: + output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset); + fputc ('\n', asm_out_file); + break; + default: + break; + } + } +} + +#if !defined (EH_FRAME_SECTION) +#if defined (EH_FRAME_SECTION_ASM_OP) +#define EH_FRAME_SECTION() eh_frame_section(); +#else +#if defined (ASM_OUTPUT_SECTION_NAME) +#define EH_FRAME_SECTION() \ + do { \ + named_section (NULL_TREE, ".eh_frame", 0); \ + } while (0) +#endif +#endif +#endif + +/* Output the call frame information used to used to record information + that relates to calculating the frame pointer, and records the + location of saved registers. */ + +static void +output_call_frame_info (for_eh) + int for_eh; +{ + register unsigned long i, j; + register dw_fde_ref fde; + register unsigned long fde_size; + register dw_cfi_ref cfi; + unsigned long fde_pad; + char l1[20], l2[20]; +#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL + char ld[20]; +#endif + + /* Do we want to include a pointer to the exception table? */ + int eh_ptr = for_eh && exception_table_p (); + + fputc ('\n', asm_out_file); + + /* We're going to be generating comments, so turn on app. */ + if (flag_debug_asm) + app_enable (); + + if (for_eh) + { +#ifdef EH_FRAME_SECTION + EH_FRAME_SECTION (); +#else + tree label = get_file_function_name ('F'); + + data_section (); + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); + ASM_GLOBALIZE_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); + ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); +#endif + assemble_label ("__FRAME_BEGIN__"); + } + else + ASM_OUTPUT_SECTION (asm_out_file, FRAME_SECTION); + + /* Output the CIE. */ + ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); + ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); +#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL + ASM_GENERATE_INTERNAL_LABEL (ld, CIE_LENGTH_LABEL, for_eh); + if (for_eh) + ASM_OUTPUT_DWARF_VALUE4 (asm_out_file, ld); + else + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, ld); +#else + if (for_eh) + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, l2, l1); + else + ASM_OUTPUT_DWARF_DELTA (asm_out_file, l2, l1); +#endif + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Length of Common Information Entry", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_LABEL (asm_out_file, l1); + + if (for_eh) + /* Now that the CIE pointer is PC-relative for EH, + use 0 to identify the CIE. */ + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + else + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, DW_CIE_ID); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s CIE Identifier Tag", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + if (! for_eh && DWARF_OFFSET_SIZE == 8) + { + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, DW_CIE_ID); + fputc ('\n', asm_out_file); + } + + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_CIE_VERSION); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s CIE Version", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + if (eh_ptr) + { + /* The CIE contains a pointer to the exception region info for the + frame. Make the augmentation string three bytes (including the + trailing null) so the pointer is 4-byte aligned. The Solaris ld + can't handle unaligned relocs. */ + if (flag_debug_asm) + { + ASM_OUTPUT_DWARF_STRING (asm_out_file, "eh"); + fprintf (asm_out_file, "\t%s CIE Augmentation", ASM_COMMENT_START); + } + else + { + ASM_OUTPUT_ASCII (asm_out_file, "eh", 3); + } + fputc ('\n', asm_out_file); + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, "__EXCEPTION_TABLE__"); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s pointer to exception region info", + ASM_COMMENT_START); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s CIE Augmentation (none)", + ASM_COMMENT_START); + } + + fputc ('\n', asm_out_file); + output_uleb128 (1); + if (flag_debug_asm) + fprintf (asm_out_file, " (CIE Code Alignment Factor)"); + + fputc ('\n', asm_out_file); + output_sleb128 (DWARF_CIE_DATA_ALIGNMENT); + if (flag_debug_asm) + fprintf (asm_out_file, " (CIE Data Alignment Factor)"); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_FRAME_RETURN_COLUMN); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s CIE RA Column", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + + for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) + output_cfi (cfi, NULL); + + /* Pad the CIE out to an address sized boundary. */ + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); + ASM_OUTPUT_LABEL (asm_out_file, l2); +#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL + ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); +#endif + + /* Loop through all of the FDE's. */ + for (i = 0; i < fde_table_in_use; ++i) + { + fde = &fde_table[i]; + + ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i*2); + ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i*2); +#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL + ASM_GENERATE_INTERNAL_LABEL (ld, FDE_LENGTH_LABEL, for_eh + i*2); + if (for_eh) + ASM_OUTPUT_DWARF_VALUE4 (asm_out_file, ld); + else + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, ld); +#else + if (for_eh) + ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, l2, l1); + else + ASM_OUTPUT_DWARF_DELTA (asm_out_file, l2, l1); +#endif + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s FDE Length", ASM_COMMENT_START); + fputc ('\n', asm_out_file); + ASM_OUTPUT_LABEL (asm_out_file, l1); + + if (for_eh) + ASM_OUTPUT_DWARF_DELTA (asm_out_file, l1, "__FRAME_BEGIN__"); + else + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (FRAME_SECTION)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s FDE CIE offset", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, fde->dw_fde_begin); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s FDE initial location", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, + fde->dw_fde_end, fde->dw_fde_begin); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s FDE address range", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + + /* Loop through the Call Frame Instructions associated with + this FDE. */ + fde->dw_fde_current_label = fde->dw_fde_begin; + for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next) + output_cfi (cfi, fde); + + /* Pad the FDE out to an address sized boundary. */ + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); + ASM_OUTPUT_LABEL (asm_out_file, l2); +#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL + ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); +#endif + } +#ifndef EH_FRAME_SECTION + if (for_eh) + { + /* Emit terminating zero for table. */ + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0); + fputc ('\n', asm_out_file); + } +#endif +#ifdef MIPS_DEBUGGING_INFO + /* Work around Irix 6 assembler bug whereby labels at the end of a section + get a value of 0. Putting .align 0 after the label fixes it. */ + ASM_OUTPUT_ALIGN (asm_out_file, 0); +#endif + + /* Turn off app to make assembly quicker. */ + if (flag_debug_asm) + app_disable (); +} + +/* Output a marker (i.e. a label) for the beginning of a function, before + the prologue. */ + +void +dwarf2out_begin_prologue () +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + register dw_fde_ref fde; + + ++current_funcdef_number; + + function_section (current_function_decl); + ASM_GENERATE_INTERNAL_LABEL (label, FUNC_BEGIN_LABEL, + current_funcdef_number); + ASM_OUTPUT_LABEL (asm_out_file, label); + + /* Expand the fde table if necessary. */ + if (fde_table_in_use == fde_table_allocated) + { + fde_table_allocated += FDE_TABLE_INCREMENT; + fde_table + = (dw_fde_ref) xrealloc (fde_table, + fde_table_allocated * sizeof (dw_fde_node)); + } + + /* Record the FDE associated with this function. */ + current_funcdef_fde = fde_table_in_use; + + /* Add the new FDE at the end of the fde_table. */ + fde = &fde_table[fde_table_in_use++]; + fde->dw_fde_begin = xstrdup (label); + fde->dw_fde_current_label = NULL; + fde->dw_fde_end = NULL; + fde->dw_fde_cfi = NULL; + + args_size = 0; +} + +/* Output a marker (i.e. a label) for the absolute end of the generated code + for a function definition. This gets called *after* the epilogue code has + been generated. */ + +void +dwarf2out_end_epilogue () +{ + dw_fde_ref fde; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + /* Output a label to mark the endpoint of the code generated for this + function. */ + ASM_GENERATE_INTERNAL_LABEL (label, FUNC_END_LABEL, current_funcdef_number); + ASM_OUTPUT_LABEL (asm_out_file, label); + fde = &fde_table[fde_table_in_use - 1]; + fde->dw_fde_end = xstrdup (label); +} + +void +dwarf2out_frame_init () +{ + /* Allocate the initial hunk of the fde_table. */ + fde_table + = (dw_fde_ref) xmalloc (FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); + bzero ((char *) fde_table, FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); + fde_table_allocated = FDE_TABLE_INCREMENT; + fde_table_in_use = 0; + + /* Generate the CFA instructions common to all FDE's. Do it now for the + sake of lookup_cfa. */ + +#ifdef DWARF2_UNWIND_INFO + /* On entry, the Canonical Frame Address is at SP. */ + dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET); + initial_return_save (INCOMING_RETURN_ADDR_RTX); +#endif +} + +void +dwarf2out_frame_finish () +{ + /* Output call frame information. */ +#ifdef MIPS_DEBUGGING_INFO + if (write_symbols == DWARF2_DEBUG) + output_call_frame_info (0); + if (flag_exceptions && ! exceptions_via_longjmp) + output_call_frame_info (1); +#else + if (write_symbols == DWARF2_DEBUG + || (flag_exceptions && ! exceptions_via_longjmp)) + output_call_frame_info (1); +#endif +} + +#endif /* .debug_frame support */ + +/* And now, the support for symbolic debugging information. */ +#ifdef DWARF2_DEBUGGING_INFO + +extern char *getpwd (); + +/* NOTE: In the comments in this file, many references are made to + "Debugging Information Entries". This term is abbreviated as `DIE' + throughout the remainder of this file. */ + +/* An internal representation of the DWARF output is built, and then + walked to generate the DWARF debugging info. The walk of the internal + representation is done after the entire program has been compiled. + The types below are used to describe the internal representation. */ + +/* Each DIE may have a series of attribute/value pairs. Values + can take on several forms. The forms that are used in this + implementation are listed below. */ + +typedef enum +{ + dw_val_class_addr, + dw_val_class_loc, + dw_val_class_const, + dw_val_class_unsigned_const, + dw_val_class_long_long, + dw_val_class_float, + dw_val_class_flag, + dw_val_class_die_ref, + dw_val_class_fde_ref, + dw_val_class_lbl_id, + dw_val_class_section_offset, + dw_val_class_str +} +dw_val_class; + +/* Various DIE's use offsets relative to the beginning of the + .debug_info section to refer to each other. */ + +typedef long int dw_offset; + +/* Define typedefs here to avoid circular dependencies. */ + +typedef struct die_struct *dw_die_ref; +typedef struct dw_attr_struct *dw_attr_ref; +typedef struct dw_val_struct *dw_val_ref; +typedef struct dw_line_info_struct *dw_line_info_ref; +typedef struct dw_separate_line_info_struct *dw_separate_line_info_ref; +typedef struct dw_loc_descr_struct *dw_loc_descr_ref; +typedef struct pubname_struct *pubname_ref; +typedef dw_die_ref *arange_ref; + +/* Describe a double word constant value. */ + +typedef struct dw_long_long_struct +{ + unsigned long hi; + unsigned long low; +} +dw_long_long_const; + +/* Describe a floating point constant value. */ + +typedef struct dw_fp_struct +{ + long *array; + unsigned length; +} +dw_float_const; + +/* Each entry in the line_info_table maintains the file and + line number associated with the label generated for that + entry. The label gives the PC value associated with + the line number entry. */ + +typedef struct dw_line_info_struct +{ + unsigned long dw_file_num; + unsigned long dw_line_num; +} +dw_line_info_entry; + +/* Line information for functions in separate sections; each one gets its + own sequence. */ +typedef struct dw_separate_line_info_struct +{ + unsigned long dw_file_num; + unsigned long dw_line_num; + unsigned long function; +} +dw_separate_line_info_entry; + +/* The dw_val_node describes an attribute's value, as it is + represented internally. */ + +typedef struct dw_val_struct +{ + dw_val_class val_class; + union + { + char *val_addr; + dw_loc_descr_ref val_loc; + long int val_int; + long unsigned val_unsigned; + dw_long_long_const val_long_long; + dw_float_const val_float; + dw_die_ref val_die_ref; + unsigned val_fde_index; + char *val_str; + char *val_lbl_id; + char *val_section; + unsigned char val_flag; + } + v; +} +dw_val_node; + +/* Locations in memory are described using a sequence of stack machine + operations. */ + +typedef struct dw_loc_descr_struct +{ + dw_loc_descr_ref dw_loc_next; + enum dwarf_location_atom dw_loc_opc; + dw_val_node dw_loc_oprnd1; + dw_val_node dw_loc_oprnd2; +} +dw_loc_descr_node; + +/* Each DIE attribute has a field specifying the attribute kind, + a link to the next attribute in the chain, and an attribute value. + Attributes are typically linked below the DIE they modify. */ + +typedef struct dw_attr_struct +{ + enum dwarf_attribute dw_attr; + dw_attr_ref dw_attr_next; + dw_val_node dw_attr_val; +} +dw_attr_node; + +/* The Debugging Information Entry (DIE) structure */ + +typedef struct die_struct +{ + enum dwarf_tag die_tag; + dw_attr_ref die_attr; + dw_attr_ref die_attr_last; + dw_die_ref die_parent; + dw_die_ref die_child; + dw_die_ref die_child_last; + dw_die_ref die_sib; + dw_offset die_offset; + unsigned long die_abbrev; +} +die_node; + +/* The pubname structure */ + +typedef struct pubname_struct +{ + dw_die_ref die; + char * name; +} +pubname_entry; + +/* The limbo die list structure. */ +typedef struct limbo_die_struct +{ + dw_die_ref die; + struct limbo_die_struct *next; +} +limbo_die_node; + +/* How to start an assembler comment. */ +#ifndef ASM_COMMENT_START +#define ASM_COMMENT_START ";#" +#endif + +/* Define a macro which returns non-zero for a TYPE_DECL which was + implicitly generated for a tagged type. + + Note that unlike the gcc front end (which generates a NULL named + TYPE_DECL node for each complete tagged type, each array type, and + each function type node created) the g++ front end generates a + _named_ TYPE_DECL node for each tagged type node created. + These TYPE_DECLs have DECL_ARTIFICIAL set, so we know not to + generate a DW_TAG_typedef DIE for them. */ + +#define TYPE_DECL_IS_STUB(decl) \ + (DECL_NAME (decl) == NULL_TREE \ + || (DECL_ARTIFICIAL (decl) \ + && is_tagged_type (TREE_TYPE (decl)) \ + && ((decl == TYPE_STUB_DECL (TREE_TYPE (decl))) \ + /* This is necessary for stub decls that \ + appear in nested inline functions. */ \ + || (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE \ + && (decl_ultimate_origin (decl) \ + == TYPE_STUB_DECL (TREE_TYPE (decl))))))) + +/* Information concerning the compilation unit's programming + language, and compiler version. */ + +extern int flag_traditional; +extern char *version_string; +extern char *language_string; + +/* Fixed size portion of the DWARF compilation unit header. */ +#define DWARF_COMPILE_UNIT_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 3) + +/* Fixed size portion of debugging line information prolog. */ +#define DWARF_LINE_PROLOG_HEADER_SIZE 5 + +/* Fixed size portion of public names info. */ +#define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2) + +/* Fixed size portion of the address range info. */ +#define DWARF_ARANGES_HEADER_SIZE \ + (DWARF_ROUND (2 * DWARF_OFFSET_SIZE + 4, PTR_SIZE * 2) - DWARF_OFFSET_SIZE) + +/* Define the architecture-dependent minimum instruction length (in bytes). + In this implementation of DWARF, this field is used for information + purposes only. Since GCC generates assembly language, we have + no a priori knowledge of how many instruction bytes are generated + for each source line, and therefore can use only the DW_LNE_set_address + and DW_LNS_fixed_advance_pc line information commands. */ + +#ifndef DWARF_LINE_MIN_INSTR_LENGTH +#define DWARF_LINE_MIN_INSTR_LENGTH 4 +#endif + +/* Minimum line offset in a special line info. opcode. + This value was chosen to give a reasonable range of values. */ +#define DWARF_LINE_BASE -10 + +/* First special line opcde - leave room for the standard opcodes. */ +#define DWARF_LINE_OPCODE_BASE 10 + +/* Range of line offsets in a special line info. opcode. */ +#define DWARF_LINE_RANGE (254-DWARF_LINE_OPCODE_BASE+1) + +/* Flag that indicates the initial value of the is_stmt_start flag. + In the present implementation, we do not mark any lines as + the beginning of a source statement, because that information + is not made available by the GCC front-end. */ +#define DWARF_LINE_DEFAULT_IS_STMT_START 1 + +/* This location is used by calc_die_sizes() to keep track + the offset of each DIE within the .debug_info section. */ +static unsigned long next_die_offset; + +/* Record the root of the DIE's built for the current compilation unit. */ +static dw_die_ref comp_unit_die; + +/* A list of DIEs with a NULL parent waiting to be relocated. */ +static limbo_die_node *limbo_die_list = 0; + +/* Pointer to an array of filenames referenced by this compilation unit. */ +static char **file_table; + +/* Total number of entries in the table (i.e. array) pointed to by + `file_table'. This is the *total* and includes both used and unused + slots. */ +static unsigned file_table_allocated; + +/* Number of entries in the file_table which are actually in use. */ +static unsigned file_table_in_use; + +/* Size (in elements) of increments by which we may expand the filename + table. */ +#define FILE_TABLE_INCREMENT 64 + +/* Local pointer to the name of the main input file. Initialized in + dwarf2out_init. */ +static char *primary_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. The labels themselves are generated in final.c, which + assigns numbers to the blocks in the same way. */ +static unsigned next_block_number = 2; + +/* A pointer to the base of a table of references to DIE's that describe + declarations. The table is indexed by DECL_UID() which is a unique + number identifying each decl. */ +static dw_die_ref *decl_die_table; + +/* Number of elements currently allocated for the decl_die_table. */ +static unsigned decl_die_table_allocated; + +/* Number of elements in decl_die_table currently in use. */ +static unsigned decl_die_table_in_use; + +/* Size (in elements) of increments by which we may expand the + decl_die_table. */ +#define DECL_DIE_TABLE_INCREMENT 256 + +/* A pointer to the base of a table of references to declaration + scopes. This table is a display which tracks the nesting + of declaration scopes at the current scope and containing + scopes. This table is used to find the proper place to + define type declaration DIE's. */ +static tree *decl_scope_table; + +/* Number of elements currently allocated for the decl_scope_table. */ +static unsigned decl_scope_table_allocated; + +/* Current level of nesting of declaration scopes. */ +static unsigned decl_scope_depth; + +/* Size (in elements) of increments by which we may expand the + decl_scope_table. */ +#define DECL_SCOPE_TABLE_INCREMENT 64 + +/* A pointer to the base of a list of references to DIE's that + are uniquely identified by their tag, presence/absence of + children DIE's, and list of attribute/value pairs. */ +static dw_die_ref *abbrev_die_table; + +/* Number of elements currently allocated for abbrev_die_table. */ +static unsigned abbrev_die_table_allocated; + +/* Number of elements in type_die_table currently in use. */ +static unsigned abbrev_die_table_in_use; + +/* Size (in elements) of increments by which we may expand the + abbrev_die_table. */ +#define ABBREV_DIE_TABLE_INCREMENT 256 + +/* A pointer to the base of a table that contains line information + for each source code line in .text in the compilation unit. */ +static dw_line_info_ref line_info_table; + +/* Number of elements currently allocated for line_info_table. */ +static unsigned line_info_table_allocated; + +/* Number of elements in separate_line_info_table currently in use. */ +static unsigned separate_line_info_table_in_use; + +/* A pointer to the base of a table that contains line information + for each source code line outside of .text in the compilation unit. */ +static dw_separate_line_info_ref separate_line_info_table; + +/* Number of elements currently allocated for separate_line_info_table. */ +static unsigned separate_line_info_table_allocated; + +/* Number of elements in line_info_table currently in use. */ +static unsigned line_info_table_in_use; + +/* Size (in elements) of increments by which we may expand the + line_info_table. */ +#define LINE_INFO_TABLE_INCREMENT 1024 + +/* A pointer to the base of a table that contains a list of publicly + accessible names. */ +static pubname_ref pubname_table; + +/* Number of elements currently allocated for pubname_table. */ +static unsigned pubname_table_allocated; + +/* Number of elements in pubname_table currently in use. */ +static unsigned pubname_table_in_use; + +/* Size (in elements) of increments by which we may expand the + pubname_table. */ +#define PUBNAME_TABLE_INCREMENT 64 + +/* A pointer to the base of a table that contains a list of publicly + accessible names. */ +static arange_ref arange_table; + +/* Number of elements currently allocated for arange_table. */ +static unsigned arange_table_allocated; + +/* Number of elements in arange_table currently in use. */ +static unsigned arange_table_in_use; + +/* Size (in elements) of increments by which we may expand the + arange_table. */ +#define ARANGE_TABLE_INCREMENT 64 + +/* A pointer to the base of a list of pending types which we haven't + generated DIEs for yet, but which we will have to come back to + later on. */ + +static tree *pending_types_list; + +/* Number of elements currently allocated for the pending_types_list. */ +static unsigned pending_types_allocated; + +/* Number of elements of pending_types_list currently in use. */ +static unsigned pending_types; + +/* Size (in elements) of increments by which we may expand the pending + types list. Actually, a single hunk of space of this size should + be enough for most typical programs. */ +#define PENDING_TYPES_INCREMENT 64 + +/* Record whether the function being analyzed contains inlined functions. */ +static int current_function_has_inlines; +static int comp_unit_has_inlines; + +/* A pointer to the ..._DECL node which we have most recently been working + on. We keep this around just in case something about it looks screwy and + we want to tell the user what the source coordinates for the actual + declaration are. */ +static tree dwarf_last_decl; + +/* Forward declarations for functions defined in this file. */ + +static void addr_const_to_string PROTO((char *, rtx)); +static char *addr_to_string PROTO((rtx)); +static int is_pseudo_reg PROTO((rtx)); +static tree type_main_variant PROTO((tree)); +static int is_tagged_type PROTO((tree)); +static char *dwarf_tag_name PROTO((unsigned)); +static char *dwarf_attr_name PROTO((unsigned)); +static char *dwarf_form_name PROTO((unsigned)); +static char *dwarf_stack_op_name PROTO((unsigned)); +static char *dwarf_type_encoding_name PROTO((unsigned)); +static tree decl_ultimate_origin PROTO((tree)); +static tree block_ultimate_origin PROTO((tree)); +static tree decl_class_context PROTO((tree)); +static void add_dwarf_attr PROTO((dw_die_ref, dw_attr_ref)); +static void add_AT_flag PROTO((dw_die_ref, + enum dwarf_attribute, + unsigned)); +static void add_AT_int PROTO((dw_die_ref, + enum dwarf_attribute, long)); +static void add_AT_unsigned PROTO((dw_die_ref, + enum dwarf_attribute, + unsigned long)); +static void add_AT_long_long PROTO((dw_die_ref, + enum dwarf_attribute, + unsigned long, unsigned long)); +static void add_AT_float PROTO((dw_die_ref, + enum dwarf_attribute, + unsigned, long *)); +static void add_AT_string PROTO((dw_die_ref, + enum dwarf_attribute, char *)); +static void add_AT_die_ref PROTO((dw_die_ref, + enum dwarf_attribute, + dw_die_ref)); +static void add_AT_fde_ref PROTO((dw_die_ref, + enum dwarf_attribute, + unsigned)); +static void add_AT_loc PROTO((dw_die_ref, + enum dwarf_attribute, + dw_loc_descr_ref)); +static void add_AT_addr PROTO((dw_die_ref, + enum dwarf_attribute, char *)); +static void add_AT_lbl_id PROTO((dw_die_ref, + enum dwarf_attribute, char *)); +static void add_AT_section_offset PROTO((dw_die_ref, + enum dwarf_attribute, char *)); +static int is_extern_subr_die PROTO((dw_die_ref)); +static dw_attr_ref get_AT PROTO((dw_die_ref, + enum dwarf_attribute)); +static char *get_AT_low_pc PROTO((dw_die_ref)); +static char *get_AT_hi_pc PROTO((dw_die_ref)); +static char *get_AT_string PROTO((dw_die_ref, + enum dwarf_attribute)); +static int get_AT_flag PROTO((dw_die_ref, + enum dwarf_attribute)); +static unsigned get_AT_unsigned PROTO((dw_die_ref, + enum dwarf_attribute)); +static int is_c_family PROTO((void)); +static int is_fortran PROTO((void)); +static void remove_AT PROTO((dw_die_ref, + enum dwarf_attribute)); +static void remove_children PROTO((dw_die_ref)); +static void add_child_die PROTO((dw_die_ref, dw_die_ref)); +static dw_die_ref new_die PROTO((enum dwarf_tag, dw_die_ref)); +static dw_die_ref lookup_type_die PROTO((tree)); +static void equate_type_number_to_die PROTO((tree, dw_die_ref)); +static dw_die_ref lookup_decl_die PROTO((tree)); +static void equate_decl_number_to_die PROTO((tree, dw_die_ref)); +static dw_loc_descr_ref new_loc_descr PROTO((enum dwarf_location_atom, + unsigned long, unsigned long)); +static void add_loc_descr PROTO((dw_loc_descr_ref *, + dw_loc_descr_ref)); +static void print_spaces PROTO((FILE *)); +static void print_die PROTO((dw_die_ref, FILE *)); +static void print_dwarf_line_table PROTO((FILE *)); +static void add_sibling_attributes PROTO((dw_die_ref)); +static void build_abbrev_table PROTO((dw_die_ref)); +static unsigned long size_of_string PROTO((char *)); +static unsigned long size_of_loc_descr PROTO((dw_loc_descr_ref)); +static unsigned long size_of_locs PROTO((dw_loc_descr_ref)); +static int constant_size PROTO((long unsigned)); +static unsigned long size_of_die PROTO((dw_die_ref)); +static void calc_die_sizes PROTO((dw_die_ref)); +static unsigned long size_of_prolog PROTO((void)); +static unsigned long size_of_line_info PROTO((void)); +static unsigned long size_of_pubnames PROTO((void)); +static unsigned long size_of_aranges PROTO((void)); +static enum dwarf_form value_format PROTO((dw_val_ref)); +static void output_value_format PROTO((dw_val_ref)); +static void output_abbrev_section PROTO((void)); +static void output_loc_operands PROTO((dw_loc_descr_ref)); +static unsigned long sibling_offset PROTO((dw_die_ref)); +static void output_die PROTO((dw_die_ref)); +static void output_compilation_unit_header PROTO((void)); +static char *dwarf2_name PROTO((tree, int)); +static void add_pubname PROTO((tree, dw_die_ref)); +static void output_pubnames PROTO((void)); +static void add_arrange PROTO((tree, dw_die_ref)); +static void output_arranges PROTO((void)); +static void output_line_info PROTO((void)); +static int is_body_block PROTO((tree)); +static dw_die_ref base_type_die PROTO((tree)); +static tree root_type PROTO((tree)); +static int is_base_type PROTO((tree)); +static dw_die_ref modified_type_die PROTO((tree, int, int, dw_die_ref)); +static int type_is_enum PROTO((tree)); +static dw_loc_descr_ref reg_loc_descriptor PROTO((rtx)); +static dw_loc_descr_ref based_loc_descr PROTO((unsigned, long)); +static int is_based_loc PROTO((rtx)); +static dw_loc_descr_ref mem_loc_descriptor PROTO((rtx)); +static dw_loc_descr_ref concat_loc_descriptor PROTO((rtx, rtx)); +static dw_loc_descr_ref loc_descriptor PROTO((rtx)); +static unsigned ceiling PROTO((unsigned, unsigned)); +static tree field_type PROTO((tree)); +static unsigned simple_type_align_in_bits PROTO((tree)); +static unsigned simple_type_size_in_bits PROTO((tree)); +static unsigned field_byte_offset PROTO((tree)); +static void add_AT_location_description PROTO((dw_die_ref, + enum dwarf_attribute, rtx)); +static void add_data_member_location_attribute PROTO((dw_die_ref, tree)); +static void add_const_value_attribute PROTO((dw_die_ref, rtx)); +static void add_location_or_const_value_attribute PROTO((dw_die_ref, tree)); +static void add_name_attribute PROTO((dw_die_ref, char *)); +static void add_bound_info PROTO((dw_die_ref, + enum dwarf_attribute, tree)); +static void add_subscript_info PROTO((dw_die_ref, tree)); +static void add_byte_size_attribute PROTO((dw_die_ref, tree)); +static void add_bit_offset_attribute PROTO((dw_die_ref, tree)); +static void add_bit_size_attribute PROTO((dw_die_ref, tree)); +static void add_prototyped_attribute PROTO((dw_die_ref, tree)); +static void add_abstract_origin_attribute PROTO((dw_die_ref, tree)); +static void add_pure_or_virtual_attribute PROTO((dw_die_ref, tree)); +static void add_src_coords_attributes PROTO((dw_die_ref, tree)); +static void ad_name_and_src_coords_attributes PROTO((dw_die_ref, tree)); +static void push_decl_scope PROTO((tree)); +static dw_die_ref scope_die_for PROTO((tree, dw_die_ref)); +static void pop_decl_scope PROTO((void)); +static void add_type_attribute PROTO((dw_die_ref, tree, int, int, + dw_die_ref)); +static char *type_tag PROTO((tree)); +static tree member_declared_type PROTO((tree)); +static char *decl_start_label PROTO((tree)); +static void gen_arrqay_type_die PROTO((tree, dw_die_ref)); +static void gen_set_type_die PROTO((tree, dw_die_ref)); +static void gen_entry_point_die PROTO((tree, dw_die_ref)); +static void pend_type PROTO((tree)); +static void output_pending_types_for_scope PROTO((dw_die_ref)); +static void gen_inlined_enumeration_type_die PROTO((tree, dw_die_ref)); +static void gen_inlined_structure_type_die PROTO((tree, dw_die_ref)); +static void gen_inlined_union_type_die PROTO((tree, dw_die_ref)); +static void gen_enumeration_type_die PROTO((tree, dw_die_ref)); +static dw_die_ref gen_formal_parameter_die PROTO((tree, dw_die_ref)); +static void gen_unspecified_parameters_die PROTO((tree, dw_die_ref)); +static void gen_formal_types_die PROTO((tree, dw_die_ref)); +static void gen_subprogram_die PROTO((tree, dw_die_ref)); +static void gen_variable_die PROTO((tree, dw_die_ref)); +static void gen_label_die PROTO((tree, dw_die_ref)); +static void gen_lexical_block_die PROTO((tree, dw_die_ref, int)); +static void gen_inlined_subprogram_die PROTO((tree, dw_die_ref, int)); +static void gen_field_die PROTO((tree, dw_die_ref)); +static void gen_ptr_to_mbr_type_die PROTO((tree, dw_die_ref)); +static void gen_compile_unit_die PROTO((char *)); +static void gen_string_type_die PROTO((tree, dw_die_ref)); +static void gen_inheritance_die PROTO((tree, dw_die_ref)); +static void gen_member_die PROTO((tree, dw_die_ref)); +static void gen_struct_or_union_type_die PROTO((tree, dw_die_ref)); +static void gen_subroutine_type_die PROTO((tree, dw_die_ref)); +static void gen_typedef_die PROTO((tree, dw_die_ref)); +static void gen_type_die PROTO((tree, dw_die_ref)); +static void gen_tagged_type_instantiation_die PROTO((tree, dw_die_ref)); +static void gen_block_die PROTO((tree, dw_die_ref, int)); +static void decls_for_scope PROTO((tree, dw_die_ref, int)); +static int is_redundant_typedef PROTO((tree)); +static void gen_decl_die PROTO((tree, dw_die_ref)); +static unsigned lookup_filename PROTO((char *)); + +/* Section names used to hold DWARF debugging information. */ +#ifndef DEBUG_INFO_SECTION +#define DEBUG_INFO_SECTION ".debug_info" +#endif +#ifndef ABBREV_SECTION +#define ABBREV_SECTION ".debug_abbrev" +#endif +#ifndef ARANGES_SECTION +#define ARANGES_SECTION ".debug_aranges" +#endif +#ifndef DW_MACINFO_SECTION +#define DW_MACINFO_SECTION ".debug_macinfo" +#endif +#ifndef DEBUG_LINE_SECTION +#define DEBUG_LINE_SECTION ".debug_line" +#endif +#ifndef LOC_SECTION +#define LOC_SECTION ".debug_loc" +#endif +#ifndef PUBNAMES_SECTION +#define PUBNAMES_SECTION ".debug_pubnames" +#endif +#ifndef STR_SECTION +#define STR_SECTION ".debug_str" +#endif + +/* Standard ELF section names for compiled code and data. */ +#ifndef TEXT_SECTION +#define TEXT_SECTION ".text" +#endif +#ifndef DATA_SECTION +#define DATA_SECTION ".data" +#endif +#ifndef BSS_SECTION +#define BSS_SECTION ".bss" +#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 the tm.h file, but + typically, overriding these defaults is unnecessary. */ + +static char text_end_label[MAX_ARTIFICIAL_LABEL_BYTES]; + +#ifndef TEXT_END_LABEL +#define TEXT_END_LABEL "Letext" +#endif +#ifndef DATA_END_LABEL +#define DATA_END_LABEL "Ledata" +#endif +#ifndef BSS_END_LABEL +#define BSS_END_LABEL "Lebss" +#endif +#ifndef INSN_LABEL_FMT +#define INSN_LABEL_FMT "LI%u_" +#endif +#ifndef BLOCK_BEGIN_LABEL +#define BLOCK_BEGIN_LABEL "LBB" +#endif +#ifndef BLOCK_END_LABEL +#define BLOCK_END_LABEL "LBE" +#endif +#ifndef BODY_BEGIN_LABEL +#define BODY_BEGIN_LABEL "Lbb" +#endif +#ifndef BODY_END_LABEL +#define BODY_END_LABEL "Lbe" +#endif +#ifndef LINE_CODE_LABEL +#define LINE_CODE_LABEL "LM" +#endif +#ifndef SEPARATE_LINE_CODE_LABEL +#define SEPARATE_LINE_CODE_LABEL "LSM" +#endif + +/* Convert a reference to the assembler name of a C-level name. This + macro has the same effect as ASM_OUTPUT_LABELREF, but copies to + a string rather than writing to a file. */ +#ifndef ASM_NAME_TO_STRING +#define ASM_NAME_TO_STRING(STR, NAME) \ + do { \ + if ((NAME)[0] == '*') \ + strcpy (STR, NAME+1); \ + else \ + strcpy (STR, NAME); \ + } \ + while (0) +#endif + +/* Convert an integer constant expression into assembler syntax. Addition + and subtraction are the only arithmetic that may appear in these + expressions. This is an adaptation of output_addr_const in final.c. + Here, the target of the conversion is a string buffer. We can't use + output_addr_const directly, because it writes to a file. */ + +static void +addr_const_to_string (str, x) + char *str; + rtx x; +{ + char buf1[256]; + char buf2[256]; + +restart: + str[0] = '\0'; + switch (GET_CODE (x)) + { + case PC: + if (flag_pic) + strcat (str, ","); + else + abort (); + break; + + case SYMBOL_REF: + ASM_NAME_TO_STRING (buf1, XSTR (x, 0)); + strcat (str, buf1); + break; + + case LABEL_REF: + ASM_GENERATE_INTERNAL_LABEL (buf1, "L", CODE_LABEL_NUMBER (XEXP (x, 0))); + ASM_NAME_TO_STRING (buf2, buf1); + strcat (str, buf2); + break; + + case CODE_LABEL: + ASM_GENERATE_INTERNAL_LABEL (buf1, "L", CODE_LABEL_NUMBER (x)); + ASM_NAME_TO_STRING (buf2, buf1); + strcat (str, buf2); + break; + + case CONST_INT: + sprintf (buf1, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); + strcat (str, buf1); + break; + + case CONST: + /* This used to output parentheses around the expression, but that does + not work on the 386 (either ATT or BSD assembler). */ + addr_const_to_string (buf1, XEXP (x, 0)); + strcat (str, buf1); + break; + + case CONST_DOUBLE: + if (GET_MODE (x) == VOIDmode) + { + /* We can use %d if the number is one word and positive. */ + if (CONST_DOUBLE_HIGH (x)) + sprintf (buf1, HOST_WIDE_INT_PRINT_DOUBLE_HEX, + CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x)); + else if (CONST_DOUBLE_LOW (x) < 0) + sprintf (buf1, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x)); + else + sprintf (buf1, HOST_WIDE_INT_PRINT_DEC, + CONST_DOUBLE_LOW (x)); + strcat (str, buf1); + } + else + /* We can't handle floating point constants; PRINT_OPERAND must + handle them. */ + output_operand_lossage ("floating constant misused"); + break; + + case PLUS: + /* Some assemblers need integer constants to appear last (eg masm). */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + { + addr_const_to_string (buf1, XEXP (x, 1)); + strcat (str, buf1); + if (INTVAL (XEXP (x, 0)) >= 0) + strcat (str, "+"); + + addr_const_to_string (buf1, XEXP (x, 0)); + strcat (str, buf1); + } + else + { + addr_const_to_string (buf1, XEXP (x, 0)); + strcat (str, buf1); + if (INTVAL (XEXP (x, 1)) >= 0) + strcat (str, "+"); + + addr_const_to_string (buf1, XEXP (x, 1)); + strcat (str, buf1); + } + break; + + case MINUS: + /* Avoid outputting things like x-x or x+5-x, since some assemblers + can't handle that. */ + x = simplify_subtraction (x); + if (GET_CODE (x) != MINUS) + goto restart; + + addr_const_to_string (buf1, XEXP (x, 0)); + strcat (str, buf1); + strcat (str, "-"); + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < 0) + { + strcat (str, ASM_OPEN_PAREN); + addr_const_to_string (buf1, XEXP (x, 1)); + strcat (str, buf1); + strcat (str, ASM_CLOSE_PAREN); + } + else + { + addr_const_to_string (buf1, XEXP (x, 1)); + strcat (str, buf1); + } + break; + + case ZERO_EXTEND: + case SIGN_EXTEND: + addr_const_to_string (buf1, XEXP (x, 0)); + strcat (str, buf1); + break; + + default: + output_operand_lossage ("invalid expression as operand"); + } +} + +/* Convert an address constant to a string, and return a pointer to + a copy of the result, located on the heap. */ + +static char * +addr_to_string (x) + rtx x; +{ + char buf[1024]; + addr_const_to_string (buf, x); + return xstrdup (buf); +} + +/* Test if rtl node points to a pseudo register. */ + +static inline int +is_pseudo_reg (rtl) + register rtx rtl; +{ + return (((GET_CODE (rtl) == REG) && (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)) + || ((GET_CODE (rtl) == SUBREG) + && (REGNO (XEXP (rtl, 0)) >= FIRST_PSEUDO_REGISTER))); +} + +/* Return a reference to a type, with its const and volatile qualifiers + removed. */ + +static inline tree +type_main_variant (type) + register tree type; +{ + type = TYPE_MAIN_VARIANT (type); + + /* There really should be only one main variant among any group of variants + of a given type (and all of the MAIN_VARIANT values for all members of + the group should point to that one type) but sometimes the C front-end + messes this up for array types, so we work around that bug here. */ + + if (TREE_CODE (type) == ARRAY_TYPE) + while (type != TYPE_MAIN_VARIANT (type)) + type = TYPE_MAIN_VARIANT (type); + + return type; +} + +/* Return non-zero if the given type node represents a tagged type. */ + +static inline int +is_tagged_type (type) + register tree type; +{ + register enum tree_code code = TREE_CODE (type); + + return (code == RECORD_TYPE || code == UNION_TYPE + || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); +} + +/* Convert a DIE tag into its string name. */ + +static char * +dwarf_tag_name (tag) + register unsigned tag; +{ + switch (tag) + { + case DW_TAG_padding: + return "DW_TAG_padding"; + case DW_TAG_array_type: + return "DW_TAG_array_type"; + case DW_TAG_class_type: + return "DW_TAG_class_type"; + case DW_TAG_entry_point: + return "DW_TAG_entry_point"; + case DW_TAG_enumeration_type: + return "DW_TAG_enumeration_type"; + case DW_TAG_formal_parameter: + return "DW_TAG_formal_parameter"; + case DW_TAG_imported_declaration: + return "DW_TAG_imported_declaration"; + case DW_TAG_label: + return "DW_TAG_label"; + case DW_TAG_lexical_block: + return "DW_TAG_lexical_block"; + case DW_TAG_member: + return "DW_TAG_member"; + case DW_TAG_pointer_type: + return "DW_TAG_pointer_type"; + case DW_TAG_reference_type: + return "DW_TAG_reference_type"; + case DW_TAG_compile_unit: + return "DW_TAG_compile_unit"; + case DW_TAG_string_type: + return "DW_TAG_string_type"; + case DW_TAG_structure_type: + return "DW_TAG_structure_type"; + case DW_TAG_subroutine_type: + return "DW_TAG_subroutine_type"; + case DW_TAG_typedef: + return "DW_TAG_typedef"; + case DW_TAG_union_type: + return "DW_TAG_union_type"; + case DW_TAG_unspecified_parameters: + return "DW_TAG_unspecified_parameters"; + case DW_TAG_variant: + return "DW_TAG_variant"; + case DW_TAG_common_block: + return "DW_TAG_common_block"; + case DW_TAG_common_inclusion: + return "DW_TAG_common_inclusion"; + case DW_TAG_inheritance: + return "DW_TAG_inheritance"; + case DW_TAG_inlined_subroutine: + return "DW_TAG_inlined_subroutine"; + case DW_TAG_module: + return "DW_TAG_module"; + case DW_TAG_ptr_to_member_type: + return "DW_TAG_ptr_to_member_type"; + case DW_TAG_set_type: + return "DW_TAG_set_type"; + case DW_TAG_subrange_type: + return "DW_TAG_subrange_type"; + case DW_TAG_with_stmt: + return "DW_TAG_with_stmt"; + case DW_TAG_access_declaration: + return "DW_TAG_access_declaration"; + case DW_TAG_base_type: + return "DW_TAG_base_type"; + case DW_TAG_catch_block: + return "DW_TAG_catch_block"; + case DW_TAG_const_type: + return "DW_TAG_const_type"; + case DW_TAG_constant: + return "DW_TAG_constant"; + case DW_TAG_enumerator: + return "DW_TAG_enumerator"; + case DW_TAG_file_type: + return "DW_TAG_file_type"; + case DW_TAG_friend: + return "DW_TAG_friend"; + case DW_TAG_namelist: + return "DW_TAG_namelist"; + case DW_TAG_namelist_item: + return "DW_TAG_namelist_item"; + case DW_TAG_packed_type: + return "DW_TAG_packed_type"; + case DW_TAG_subprogram: + return "DW_TAG_subprogram"; + case DW_TAG_template_type_param: + return "DW_TAG_template_type_param"; + case DW_TAG_template_value_param: + return "DW_TAG_template_value_param"; + case DW_TAG_thrown_type: + return "DW_TAG_thrown_type"; + case DW_TAG_try_block: + return "DW_TAG_try_block"; + case DW_TAG_variant_part: + return "DW_TAG_variant_part"; + case DW_TAG_variable: + return "DW_TAG_variable"; + case DW_TAG_volatile_type: + return "DW_TAG_volatile_type"; + case DW_TAG_MIPS_loop: + return "DW_TAG_MIPS_loop"; + case DW_TAG_format_label: + return "DW_TAG_format_label"; + case DW_TAG_function_template: + return "DW_TAG_function_template"; + case DW_TAG_class_template: + return "DW_TAG_class_template"; + default: + return "DW_TAG_<unknown>"; + } +} + +/* Convert a DWARF attribute code into its string name. */ + +static char * +dwarf_attr_name (attr) + register unsigned attr; +{ + switch (attr) + { + case DW_AT_sibling: + return "DW_AT_sibling"; + case DW_AT_location: + return "DW_AT_location"; + case DW_AT_name: + return "DW_AT_name"; + case DW_AT_ordering: + return "DW_AT_ordering"; + case DW_AT_subscr_data: + return "DW_AT_subscr_data"; + case DW_AT_byte_size: + return "DW_AT_byte_size"; + case DW_AT_bit_offset: + return "DW_AT_bit_offset"; + case DW_AT_bit_size: + return "DW_AT_bit_size"; + case DW_AT_element_list: + return "DW_AT_element_list"; + case DW_AT_stmt_list: + return "DW_AT_stmt_list"; + case DW_AT_low_pc: + return "DW_AT_low_pc"; + case DW_AT_high_pc: + return "DW_AT_high_pc"; + case DW_AT_language: + return "DW_AT_language"; + case DW_AT_member: + return "DW_AT_member"; + case DW_AT_discr: + return "DW_AT_discr"; + case DW_AT_discr_value: + return "DW_AT_discr_value"; + case DW_AT_visibility: + return "DW_AT_visibility"; + case DW_AT_import: + return "DW_AT_import"; + case DW_AT_string_length: + return "DW_AT_string_length"; + case DW_AT_common_reference: + return "DW_AT_common_reference"; + case DW_AT_comp_dir: + return "DW_AT_comp_dir"; + case DW_AT_const_value: + return "DW_AT_const_value"; + case DW_AT_containing_type: + return "DW_AT_containing_type"; + case DW_AT_default_value: + return "DW_AT_default_value"; + case DW_AT_inline: + return "DW_AT_inline"; + case DW_AT_is_optional: + return "DW_AT_is_optional"; + case DW_AT_lower_bound: + return "DW_AT_lower_bound"; + case DW_AT_producer: + return "DW_AT_producer"; + case DW_AT_prototyped: + return "DW_AT_prototyped"; + case DW_AT_return_addr: + return "DW_AT_return_addr"; + case DW_AT_start_scope: + return "DW_AT_start_scope"; + case DW_AT_stride_size: + return "DW_AT_stride_size"; + case DW_AT_upper_bound: + return "DW_AT_upper_bound"; + case DW_AT_abstract_origin: + return "DW_AT_abstract_origin"; + case DW_AT_accessibility: + return "DW_AT_accessibility"; + case DW_AT_address_class: + return "DW_AT_address_class"; + case DW_AT_artificial: + return "DW_AT_artificial"; + case DW_AT_base_types: + return "DW_AT_base_types"; + case DW_AT_calling_convention: + return "DW_AT_calling_convention"; + case DW_AT_count: + return "DW_AT_count"; + case DW_AT_data_member_location: + return "DW_AT_data_member_location"; + case DW_AT_decl_column: + return "DW_AT_decl_column"; + case DW_AT_decl_file: + return "DW_AT_decl_file"; + case DW_AT_decl_line: + return "DW_AT_decl_line"; + case DW_AT_declaration: + return "DW_AT_declaration"; + case DW_AT_discr_list: + return "DW_AT_discr_list"; + case DW_AT_encoding: + return "DW_AT_encoding"; + case DW_AT_external: + return "DW_AT_external"; + case DW_AT_frame_base: + return "DW_AT_frame_base"; + case DW_AT_friend: + return "DW_AT_friend"; + case DW_AT_identifier_case: + return "DW_AT_identifier_case"; + case DW_AT_macro_info: + return "DW_AT_macro_info"; + case DW_AT_namelist_items: + return "DW_AT_namelist_items"; + case DW_AT_priority: + return "DW_AT_priority"; + case DW_AT_segment: + return "DW_AT_segment"; + case DW_AT_specification: + return "DW_AT_specification"; + case DW_AT_static_link: + return "DW_AT_static_link"; + case DW_AT_type: + return "DW_AT_type"; + case DW_AT_use_location: + return "DW_AT_use_location"; + case DW_AT_variable_parameter: + return "DW_AT_variable_parameter"; + case DW_AT_virtuality: + return "DW_AT_virtuality"; + case DW_AT_vtable_elem_location: + return "DW_AT_vtable_elem_location"; + + case DW_AT_MIPS_fde: + return "DW_AT_MIPS_fde"; + case DW_AT_MIPS_loop_begin: + return "DW_AT_MIPS_loop_begin"; + case DW_AT_MIPS_tail_loop_begin: + return "DW_AT_MIPS_tail_loop_begin"; + case DW_AT_MIPS_epilog_begin: + return "DW_AT_MIPS_epilog_begin"; + case DW_AT_MIPS_loop_unroll_factor: + return "DW_AT_MIPS_loop_unroll_factor"; + case DW_AT_MIPS_software_pipeline_depth: + return "DW_AT_MIPS_software_pipeline_depth"; + case DW_AT_MIPS_linkage_name: + return "DW_AT_MIPS_linkage_name"; + case DW_AT_MIPS_stride: + return "DW_AT_MIPS_stride"; + case DW_AT_MIPS_abstract_name: + return "DW_AT_MIPS_abstract_name"; + case DW_AT_MIPS_clone_origin: + return "DW_AT_MIPS_clone_origin"; + case DW_AT_MIPS_has_inlines: + return "DW_AT_MIPS_has_inlines"; + + case DW_AT_sf_names: + return "DW_AT_sf_names"; + case DW_AT_src_info: + return "DW_AT_src_info"; + case DW_AT_mac_info: + return "DW_AT_mac_info"; + case DW_AT_src_coords: + return "DW_AT_src_coords"; + case DW_AT_body_begin: + return "DW_AT_body_begin"; + case DW_AT_body_end: + return "DW_AT_body_end"; + default: + return "DW_AT_<unknown>"; + } +} + +/* Convert a DWARF value form code into its string name. */ + +static char * +dwarf_form_name (form) + register unsigned form; +{ + switch (form) + { + case DW_FORM_addr: + return "DW_FORM_addr"; + case DW_FORM_block2: + return "DW_FORM_block2"; + case DW_FORM_block4: + return "DW_FORM_block4"; + case DW_FORM_data2: + return "DW_FORM_data2"; + case DW_FORM_data4: + return "DW_FORM_data4"; + case DW_FORM_data8: + return "DW_FORM_data8"; + case DW_FORM_string: + return "DW_FORM_string"; + case DW_FORM_block: + return "DW_FORM_block"; + case DW_FORM_block1: + return "DW_FORM_block1"; + case DW_FORM_data1: + return "DW_FORM_data1"; + case DW_FORM_flag: + return "DW_FORM_flag"; + case DW_FORM_sdata: + return "DW_FORM_sdata"; + case DW_FORM_strp: + return "DW_FORM_strp"; + case DW_FORM_udata: + return "DW_FORM_udata"; + case DW_FORM_ref_addr: + return "DW_FORM_ref_addr"; + case DW_FORM_ref1: + return "DW_FORM_ref1"; + case DW_FORM_ref2: + return "DW_FORM_ref2"; + case DW_FORM_ref4: + return "DW_FORM_ref4"; + case DW_FORM_ref8: + return "DW_FORM_ref8"; + case DW_FORM_ref_udata: + return "DW_FORM_ref_udata"; + case DW_FORM_indirect: + return "DW_FORM_indirect"; + default: + return "DW_FORM_<unknown>"; + } +} + +/* Convert a DWARF stack opcode into its string name. */ + +static char * +dwarf_stack_op_name (op) + register unsigned op; +{ + switch (op) + { + case DW_OP_addr: + return "DW_OP_addr"; + case DW_OP_deref: + return "DW_OP_deref"; + case DW_OP_const1u: + return "DW_OP_const1u"; + case DW_OP_const1s: + return "DW_OP_const1s"; + case DW_OP_const2u: + return "DW_OP_const2u"; + case DW_OP_const2s: + return "DW_OP_const2s"; + case DW_OP_const4u: + return "DW_OP_const4u"; + case DW_OP_const4s: + return "DW_OP_const4s"; + case DW_OP_const8u: + return "DW_OP_const8u"; + case DW_OP_const8s: + return "DW_OP_const8s"; + case DW_OP_constu: + return "DW_OP_constu"; + case DW_OP_consts: + return "DW_OP_consts"; + case DW_OP_dup: + return "DW_OP_dup"; + case DW_OP_drop: + return "DW_OP_drop"; + case DW_OP_over: + return "DW_OP_over"; + case DW_OP_pick: + return "DW_OP_pick"; + case DW_OP_swap: + return "DW_OP_swap"; + case DW_OP_rot: + return "DW_OP_rot"; + case DW_OP_xderef: + return "DW_OP_xderef"; + case DW_OP_abs: + return "DW_OP_abs"; + case DW_OP_and: + return "DW_OP_and"; + case DW_OP_div: + return "DW_OP_div"; + case DW_OP_minus: + return "DW_OP_minus"; + case DW_OP_mod: + return "DW_OP_mod"; + case DW_OP_mul: + return "DW_OP_mul"; + case DW_OP_neg: + return "DW_OP_neg"; + case DW_OP_not: + return "DW_OP_not"; + case DW_OP_or: + return "DW_OP_or"; + case DW_OP_plus: + return "DW_OP_plus"; + case DW_OP_plus_uconst: + return "DW_OP_plus_uconst"; + case DW_OP_shl: + return "DW_OP_shl"; + case DW_OP_shr: + return "DW_OP_shr"; + case DW_OP_shra: + return "DW_OP_shra"; + case DW_OP_xor: + return "DW_OP_xor"; + case DW_OP_bra: + return "DW_OP_bra"; + case DW_OP_eq: + return "DW_OP_eq"; + case DW_OP_ge: + return "DW_OP_ge"; + case DW_OP_gt: + return "DW_OP_gt"; + case DW_OP_le: + return "DW_OP_le"; + case DW_OP_lt: + return "DW_OP_lt"; + case DW_OP_ne: + return "DW_OP_ne"; + case DW_OP_skip: + return "DW_OP_skip"; + case DW_OP_lit0: + return "DW_OP_lit0"; + case DW_OP_lit1: + return "DW_OP_lit1"; + case DW_OP_lit2: + return "DW_OP_lit2"; + case DW_OP_lit3: + return "DW_OP_lit3"; + case DW_OP_lit4: + return "DW_OP_lit4"; + case DW_OP_lit5: + return "DW_OP_lit5"; + case DW_OP_lit6: + return "DW_OP_lit6"; + case DW_OP_lit7: + return "DW_OP_lit7"; + case DW_OP_lit8: + return "DW_OP_lit8"; + case DW_OP_lit9: + return "DW_OP_lit9"; + case DW_OP_lit10: + return "DW_OP_lit10"; + case DW_OP_lit11: + return "DW_OP_lit11"; + case DW_OP_lit12: + return "DW_OP_lit12"; + case DW_OP_lit13: + return "DW_OP_lit13"; + case DW_OP_lit14: + return "DW_OP_lit14"; + case DW_OP_lit15: + return "DW_OP_lit15"; + case DW_OP_lit16: + return "DW_OP_lit16"; + case DW_OP_lit17: + return "DW_OP_lit17"; + case DW_OP_lit18: + return "DW_OP_lit18"; + case DW_OP_lit19: + return "DW_OP_lit19"; + case DW_OP_lit20: + return "DW_OP_lit20"; + case DW_OP_lit21: + return "DW_OP_lit21"; + case DW_OP_lit22: + return "DW_OP_lit22"; + case DW_OP_lit23: + return "DW_OP_lit23"; + case DW_OP_lit24: + return "DW_OP_lit24"; + case DW_OP_lit25: + return "DW_OP_lit25"; + case DW_OP_lit26: + return "DW_OP_lit26"; + case DW_OP_lit27: + return "DW_OP_lit27"; + case DW_OP_lit28: + return "DW_OP_lit28"; + case DW_OP_lit29: + return "DW_OP_lit29"; + case DW_OP_lit30: + return "DW_OP_lit30"; + case DW_OP_lit31: + return "DW_OP_lit31"; + case DW_OP_reg0: + return "DW_OP_reg0"; + case DW_OP_reg1: + return "DW_OP_reg1"; + case DW_OP_reg2: + return "DW_OP_reg2"; + case DW_OP_reg3: + return "DW_OP_reg3"; + case DW_OP_reg4: + return "DW_OP_reg4"; + case DW_OP_reg5: + return "DW_OP_reg5"; + case DW_OP_reg6: + return "DW_OP_reg6"; + case DW_OP_reg7: + return "DW_OP_reg7"; + case DW_OP_reg8: + return "DW_OP_reg8"; + case DW_OP_reg9: + return "DW_OP_reg9"; + case DW_OP_reg10: + return "DW_OP_reg10"; + case DW_OP_reg11: + return "DW_OP_reg11"; + case DW_OP_reg12: + return "DW_OP_reg12"; + case DW_OP_reg13: + return "DW_OP_reg13"; + case DW_OP_reg14: + return "DW_OP_reg14"; + case DW_OP_reg15: + return "DW_OP_reg15"; + case DW_OP_reg16: + return "DW_OP_reg16"; + case DW_OP_reg17: + return "DW_OP_reg17"; + case DW_OP_reg18: + return "DW_OP_reg18"; + case DW_OP_reg19: + return "DW_OP_reg19"; + case DW_OP_reg20: + return "DW_OP_reg20"; + case DW_OP_reg21: + return "DW_OP_reg21"; + case DW_OP_reg22: + return "DW_OP_reg22"; + case DW_OP_reg23: + return "DW_OP_reg23"; + case DW_OP_reg24: + return "DW_OP_reg24"; + case DW_OP_reg25: + return "DW_OP_reg25"; + case DW_OP_reg26: + return "DW_OP_reg26"; + case DW_OP_reg27: + return "DW_OP_reg27"; + case DW_OP_reg28: + return "DW_OP_reg28"; + case DW_OP_reg29: + return "DW_OP_reg29"; + case DW_OP_reg30: + return "DW_OP_reg30"; + case DW_OP_reg31: + return "DW_OP_reg31"; + case DW_OP_breg0: + return "DW_OP_breg0"; + case DW_OP_breg1: + return "DW_OP_breg1"; + case DW_OP_breg2: + return "DW_OP_breg2"; + case DW_OP_breg3: + return "DW_OP_breg3"; + case DW_OP_breg4: + return "DW_OP_breg4"; + case DW_OP_breg5: + return "DW_OP_breg5"; + case DW_OP_breg6: + return "DW_OP_breg6"; + case DW_OP_breg7: + return "DW_OP_breg7"; + case DW_OP_breg8: + return "DW_OP_breg8"; + case DW_OP_breg9: + return "DW_OP_breg9"; + case DW_OP_breg10: + return "DW_OP_breg10"; + case DW_OP_breg11: + return "DW_OP_breg11"; + case DW_OP_breg12: + return "DW_OP_breg12"; + case DW_OP_breg13: + return "DW_OP_breg13"; + case DW_OP_breg14: + return "DW_OP_breg14"; + case DW_OP_breg15: + return "DW_OP_breg15"; + case DW_OP_breg16: + return "DW_OP_breg16"; + case DW_OP_breg17: + return "DW_OP_breg17"; + case DW_OP_breg18: + return "DW_OP_breg18"; + case DW_OP_breg19: + return "DW_OP_breg19"; + case DW_OP_breg20: + return "DW_OP_breg20"; + case DW_OP_breg21: + return "DW_OP_breg21"; + case DW_OP_breg22: + return "DW_OP_breg22"; + case DW_OP_breg23: + return "DW_OP_breg23"; + case DW_OP_breg24: + return "DW_OP_breg24"; + case DW_OP_breg25: + return "DW_OP_breg25"; + case DW_OP_breg26: + return "DW_OP_breg26"; + case DW_OP_breg27: + return "DW_OP_breg27"; + case DW_OP_breg28: + return "DW_OP_breg28"; + case DW_OP_breg29: + return "DW_OP_breg29"; + case DW_OP_breg30: + return "DW_OP_breg30"; + case DW_OP_breg31: + return "DW_OP_breg31"; + case DW_OP_regx: + return "DW_OP_regx"; + case DW_OP_fbreg: + return "DW_OP_fbreg"; + case DW_OP_bregx: + return "DW_OP_bregx"; + case DW_OP_piece: + return "DW_OP_piece"; + case DW_OP_deref_size: + return "DW_OP_deref_size"; + case DW_OP_xderef_size: + return "DW_OP_xderef_size"; + case DW_OP_nop: + return "DW_OP_nop"; + default: + return "OP_<unknown>"; + } +} + +/* Convert a DWARF type code into its string name. */ + +static char * +dwarf_type_encoding_name (enc) + register unsigned enc; +{ + switch (enc) + { + case DW_ATE_address: + return "DW_ATE_address"; + case DW_ATE_boolean: + return "DW_ATE_boolean"; + case DW_ATE_complex_float: + return "DW_ATE_complex_float"; + case DW_ATE_float: + return "DW_ATE_float"; + case DW_ATE_signed: + return "DW_ATE_signed"; + case DW_ATE_signed_char: + return "DW_ATE_signed_char"; + case DW_ATE_unsigned: + return "DW_ATE_unsigned"; + case DW_ATE_unsigned_char: + return "DW_ATE_unsigned_char"; + default: + return "DW_ATE_<unknown>"; + } +} + +/* Determine the "ultimate origin" of a decl. The decl may be an inlined + instance of an inlined instance of a decl which is local to an inline + function, so we have to trace all of the way back through the origin chain + to find out what sort of node actually served as the original seed for the + given block. */ + +static tree +decl_ultimate_origin (decl) + register tree decl; +{ + register tree immediate_origin = DECL_ABSTRACT_ORIGIN (decl); + + if (immediate_origin == NULL_TREE) + return NULL_TREE; + else + { + register tree ret_val; + register tree lookahead = immediate_origin; + + do + { + ret_val = lookahead; + lookahead = DECL_ABSTRACT_ORIGIN (ret_val); + } + while (lookahead != NULL && lookahead != ret_val); + + return ret_val; + } +} + +/* Determine the "ultimate origin" of a block. The block may be an inlined + instance of an inlined instance of a block which is local to an inline + function, so we have to trace all of the way back through the origin chain + to find out what sort of node actually served as the original seed for the + given block. */ + +static tree +block_ultimate_origin (block) + register tree block; +{ + register tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block); + + if (immediate_origin == NULL_TREE) + return NULL_TREE; + else + { + register tree ret_val; + register tree lookahead = immediate_origin; + + do + { + ret_val = lookahead; + lookahead = (TREE_CODE (ret_val) == BLOCK) + ? BLOCK_ABSTRACT_ORIGIN (ret_val) + : NULL; + } + while (lookahead != NULL && lookahead != ret_val); + + return ret_val; + } +} + +/* Get the class to which DECL belongs, if any. In g++, the DECL_CONTEXT + of a virtual function may refer to a base class, so we check the 'this' + parameter. */ + +static tree +decl_class_context (decl) + tree decl; +{ + tree context = NULL_TREE; + + if (TREE_CODE (decl) != FUNCTION_DECL || ! DECL_VINDEX (decl)) + context = DECL_CONTEXT (decl); + else + context = TYPE_MAIN_VARIANT + (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))))); + + if (context && TREE_CODE_CLASS (TREE_CODE (context)) != 't') + context = NULL_TREE; + + return context; +} + +/* Add an attribute/value pair to a DIE */ + +static inline void +add_dwarf_attr (die, attr) + register dw_die_ref die; + register dw_attr_ref attr; +{ + if (die != NULL && attr != NULL) + { + if (die->die_attr == NULL) + { + die->die_attr = attr; + die->die_attr_last = attr; + } + else + { + die->die_attr_last->dw_attr_next = attr; + die->die_attr_last = attr; + } + } +} + +/* Add a flag value attribute to a DIE. */ + +static inline void +add_AT_flag (die, attr_kind, flag) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register unsigned flag; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_flag; + attr->dw_attr_val.v.val_flag = flag; + add_dwarf_attr (die, attr); +} + +/* Add a signed integer attribute value to a DIE. */ + +static inline void +add_AT_int (die, attr_kind, int_val) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register long int int_val; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_const; + attr->dw_attr_val.v.val_int = int_val; + add_dwarf_attr (die, attr); +} + +/* Add an unsigned integer attribute value to a DIE. */ + +static inline void +add_AT_unsigned (die, attr_kind, unsigned_val) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register unsigned long unsigned_val; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_unsigned_const; + attr->dw_attr_val.v.val_unsigned = unsigned_val; + add_dwarf_attr (die, attr); +} + +/* Add an unsigned double integer attribute value to a DIE. */ + +static inline void +add_AT_long_long (die, attr_kind, val_hi, val_low) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register unsigned long val_hi; + register unsigned long val_low; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_long_long; + attr->dw_attr_val.v.val_long_long.hi = val_hi; + attr->dw_attr_val.v.val_long_long.low = val_low; + add_dwarf_attr (die, attr); +} + +/* Add a floating point attribute value to a DIE and return it. */ + +static inline void +add_AT_float (die, attr_kind, length, array) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register unsigned length; + register long *array; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_float; + attr->dw_attr_val.v.val_float.length = length; + attr->dw_attr_val.v.val_float.array = array; + add_dwarf_attr (die, attr); +} + +/* Add a string attribute value to a DIE. */ + +static inline void +add_AT_string (die, attr_kind, str) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register char *str; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_str; + attr->dw_attr_val.v.val_str = xstrdup (str); + add_dwarf_attr (die, attr); +} + +/* Add a DIE reference attribute value to a DIE. */ + +static inline void +add_AT_die_ref (die, attr_kind, targ_die) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register dw_die_ref targ_die; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_die_ref; + attr->dw_attr_val.v.val_die_ref = targ_die; + add_dwarf_attr (die, attr); +} + +/* Add an FDE reference attribute value to a DIE. */ + +static inline void +add_AT_fde_ref (die, attr_kind, targ_fde) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register unsigned targ_fde; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_fde_ref; + attr->dw_attr_val.v.val_fde_index = targ_fde; + add_dwarf_attr (die, attr); +} + +/* Add a location description attribute value to a DIE. */ + +static inline void +add_AT_loc (die, attr_kind, loc) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register dw_loc_descr_ref loc; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_loc; + attr->dw_attr_val.v.val_loc = loc; + add_dwarf_attr (die, attr); +} + +/* Add an address constant attribute value to a DIE. */ + +static inline void +add_AT_addr (die, attr_kind, addr) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + char *addr; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_addr; + attr->dw_attr_val.v.val_addr = addr; + add_dwarf_attr (die, attr); +} + +/* Add a label identifier attribute value to a DIE. */ + +static inline void +add_AT_lbl_id (die, attr_kind, lbl_id) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register char *lbl_id; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_lbl_id; + attr->dw_attr_val.v.val_lbl_id = xstrdup (lbl_id); + add_dwarf_attr (die, attr); +} + +/* Add a section offset attribute value to a DIE. */ + +static inline void +add_AT_section_offset (die, attr_kind, section) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; + register char *section; +{ + register dw_attr_ref attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + + attr->dw_attr_next = NULL; + attr->dw_attr = attr_kind; + attr->dw_attr_val.val_class = dw_val_class_section_offset; + attr->dw_attr_val.v.val_section = section; + add_dwarf_attr (die, attr); + +} + +/* Test if die refers to an external subroutine. */ + +static inline int +is_extern_subr_die (die) + register dw_die_ref die; +{ + register dw_attr_ref a; + register int is_subr = FALSE; + register int is_extern = FALSE; + + if (die != NULL && die->die_tag == DW_TAG_subprogram) + { + is_subr = TRUE; + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + if (a->dw_attr == DW_AT_external + && a->dw_attr_val.val_class == dw_val_class_flag + && a->dw_attr_val.v.val_flag != 0) + { + is_extern = TRUE; + break; + } + } + } + + return is_subr && is_extern; +} + +/* Get the attribute of type attr_kind. */ + +static inline dw_attr_ref +get_AT (die, attr_kind) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; +{ + register dw_attr_ref a; + register dw_die_ref spec = NULL; + + if (die != NULL) + { + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + if (a->dw_attr == attr_kind) + return a; + + if (a->dw_attr == DW_AT_specification + || a->dw_attr == DW_AT_abstract_origin) + spec = a->dw_attr_val.v.val_die_ref; + } + + if (spec) + return get_AT (spec, attr_kind); + } + + return NULL; +} + +/* Return the "low pc" attribute value, typically associated with + a subprogram DIE. Return null if the "low pc" attribute is + either not prsent, or if it cannot be represented as an + assembler label identifier. */ + +static inline char * +get_AT_low_pc (die) + register dw_die_ref die; +{ + register dw_attr_ref a = get_AT (die, DW_AT_low_pc); + + if (a && a->dw_attr_val.val_class == dw_val_class_lbl_id) + return a->dw_attr_val.v.val_lbl_id; + + return NULL; +} + +/* Return the "high pc" attribute value, typically associated with + a subprogram DIE. Return null if the "high pc" attribute is + either not prsent, or if it cannot be represented as an + assembler label identifier. */ + +static inline char * +get_AT_hi_pc (die) + register dw_die_ref die; +{ + register dw_attr_ref a = get_AT (die, DW_AT_high_pc); + + if (a && a->dw_attr_val.val_class == dw_val_class_lbl_id) + return a->dw_attr_val.v.val_lbl_id; + + return NULL; +} + +/* Return the value of the string attribute designated by ATTR_KIND, or + NULL if it is not present. */ + +static inline char * +get_AT_string (die, attr_kind) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; +{ + register dw_attr_ref a = get_AT (die, attr_kind); + + if (a && a->dw_attr_val.val_class == dw_val_class_str) + return a->dw_attr_val.v.val_str; + + return NULL; +} + +/* Return the value of the flag attribute designated by ATTR_KIND, or -1 + if it is not present. */ + +static inline int +get_AT_flag (die, attr_kind) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; +{ + register dw_attr_ref a = get_AT (die, attr_kind); + + if (a && a->dw_attr_val.val_class == dw_val_class_flag) + return a->dw_attr_val.v.val_flag; + + return -1; +} + +/* Return the value of the unsigned attribute designated by ATTR_KIND, or 0 + if it is not present. */ + +static inline unsigned +get_AT_unsigned (die, attr_kind) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; +{ + register dw_attr_ref a = get_AT (die, attr_kind); + + if (a && a->dw_attr_val.val_class == dw_val_class_unsigned_const) + return a->dw_attr_val.v.val_unsigned; + + return 0; +} + +static inline int +is_c_family () +{ + register unsigned lang = get_AT_unsigned (comp_unit_die, DW_AT_language); + + return (lang == DW_LANG_C || lang == DW_LANG_C89 + || lang == DW_LANG_C_plus_plus); +} + +static inline int +is_fortran () +{ + register unsigned lang = get_AT_unsigned (comp_unit_die, DW_AT_language); + + return (lang == DW_LANG_Fortran77 || lang == DW_LANG_Fortran90); +} + +/* Remove the specified attribute if present. */ + +static inline void +remove_AT (die, attr_kind) + register dw_die_ref die; + register enum dwarf_attribute attr_kind; +{ + register dw_attr_ref a; + register dw_attr_ref removed = NULL;; + + if (die != NULL) + { + if (die->die_attr->dw_attr == attr_kind) + { + removed = die->die_attr; + if (die->die_attr_last == die->die_attr) + die->die_attr_last = NULL; + + die->die_attr = die->die_attr->dw_attr_next; + } + + else + for (a = die->die_attr; a->dw_attr_next != NULL; + a = a->dw_attr_next) + if (a->dw_attr_next->dw_attr == attr_kind) + { + removed = a->dw_attr_next; + if (die->die_attr_last == a->dw_attr_next) + die->die_attr_last = a; + + a->dw_attr_next = a->dw_attr_next->dw_attr_next; + break; + } + + if (removed != 0) + free (removed); + } +} + +/* Discard the children of this DIE. */ + +static inline void +remove_children (die) + register dw_die_ref die; +{ + register dw_die_ref child_die = die->die_child; + + die->die_child = NULL; + die->die_child_last = NULL; + + while (child_die != NULL) + { + register dw_die_ref tmp_die = child_die; + register dw_attr_ref a; + + child_die = child_die->die_sib; + + for (a = tmp_die->die_attr; a != NULL; ) + { + register dw_attr_ref tmp_a = a; + + a = a->dw_attr_next; + free (tmp_a); + } + + free (tmp_die); + } +} + +/* Add a child DIE below its parent. */ + +static inline void +add_child_die (die, child_die) + register dw_die_ref die; + register dw_die_ref child_die; +{ + if (die != NULL && child_die != NULL) + { + assert (die != child_die); + child_die->die_parent = die; + child_die->die_sib = NULL; + + if (die->die_child == NULL) + { + die->die_child = child_die; + die->die_child_last = child_die; + } + else + { + die->die_child_last->die_sib = child_die; + die->die_child_last = child_die; + } + } +} + +/* Return a pointer to a newly created DIE node. */ + +static inline dw_die_ref +new_die (tag_value, parent_die) + register enum dwarf_tag tag_value; + register dw_die_ref parent_die; +{ + register dw_die_ref die = (dw_die_ref) xmalloc (sizeof (die_node)); + + die->die_tag = tag_value; + die->die_abbrev = 0; + die->die_offset = 0; + die->die_child = NULL; + die->die_parent = NULL; + die->die_sib = NULL; + die->die_child_last = NULL; + die->die_attr = NULL; + die->die_attr_last = NULL; + + if (parent_die != NULL) + add_child_die (parent_die, die); + else + { + limbo_die_node *limbo_node; + + limbo_node = (limbo_die_node *) xmalloc (sizeof (limbo_die_node)); + limbo_node->die = die; + limbo_node->next = limbo_die_list; + limbo_die_list = limbo_node; + } + + return die; +} + +/* Return the DIE associated with the given type specifier. */ + +static inline dw_die_ref +lookup_type_die (type) + register tree type; +{ + return (dw_die_ref) TYPE_SYMTAB_POINTER (type); +} + +/* Equate a DIE to a given type specifier. */ + +static void +equate_type_number_to_die (type, type_die) + register tree type; + register dw_die_ref type_die; +{ + TYPE_SYMTAB_POINTER (type) = (char *) type_die; +} + +/* Return the DIE associated with a given declaration. */ + +static inline dw_die_ref +lookup_decl_die (decl) + register tree decl; +{ + register unsigned decl_id = DECL_UID (decl); + + return (decl_id < decl_die_table_in_use + ? decl_die_table[decl_id] : NULL); +} + +/* Equate a DIE to a particular declaration. */ + +static void +equate_decl_number_to_die (decl, decl_die) + register tree decl; + register dw_die_ref decl_die; +{ + register unsigned decl_id = DECL_UID (decl); + register unsigned i; + register unsigned num_allocated; + + if (decl_id >= decl_die_table_allocated) + { + num_allocated + = ((decl_id + 1 + DECL_DIE_TABLE_INCREMENT - 1) + / DECL_DIE_TABLE_INCREMENT) + * DECL_DIE_TABLE_INCREMENT; + + decl_die_table + = (dw_die_ref *) xrealloc (decl_die_table, + sizeof (dw_die_ref) * num_allocated); + + bzero ((char *) &decl_die_table[decl_die_table_allocated], + (num_allocated - decl_die_table_allocated) * sizeof (dw_die_ref)); + decl_die_table_allocated = num_allocated; + } + + if (decl_id >= decl_die_table_in_use) + decl_die_table_in_use = (decl_id + 1); + + decl_die_table[decl_id] = decl_die; +} + +/* Return a pointer to a newly allocated location description. Location + descriptions are simple expression terms that can be strung + together to form more complicated location (address) descriptions. */ + +static inline dw_loc_descr_ref +new_loc_descr (op, oprnd1, oprnd2) + register enum dwarf_location_atom op; + register unsigned long oprnd1; + register unsigned long oprnd2; +{ + register dw_loc_descr_ref descr + = (dw_loc_descr_ref) xmalloc (sizeof (dw_loc_descr_node)); + + descr->dw_loc_next = NULL; + descr->dw_loc_opc = op; + descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const; + descr->dw_loc_oprnd1.v.val_unsigned = oprnd1; + descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const; + descr->dw_loc_oprnd2.v.val_unsigned = oprnd2; + + return descr; +} + +/* Add a location description term to a location description expression. */ + +static inline void +add_loc_descr (list_head, descr) + register dw_loc_descr_ref *list_head; + register dw_loc_descr_ref descr; +{ + register dw_loc_descr_ref *d; + + /* Find the end of the chain. */ + for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next) + ; + + *d = descr; +} + +/* Keep track of the number of spaces used to indent the + output of the debugging routines that print the structure of + the DIE internal representation. */ +static int print_indent; + +/* Indent the line the number of spaces given by print_indent. */ + +static inline void +print_spaces (outfile) + FILE *outfile; +{ + fprintf (outfile, "%*s", print_indent, ""); +} + +/* Print the information associated with a given DIE, and its children. + This routine is a debugging aid only. */ + +static void +print_die (die, outfile) + dw_die_ref die; + FILE *outfile; +{ + register dw_attr_ref a; + register dw_die_ref c; + + print_spaces (outfile); + fprintf (outfile, "DIE %4u: %s\n", + die->die_offset, dwarf_tag_name (die->die_tag)); + print_spaces (outfile); + fprintf (outfile, " abbrev id: %u", die->die_abbrev); + fprintf (outfile, " offset: %u\n", die->die_offset); + + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + print_spaces (outfile); + fprintf (outfile, " %s: ", dwarf_attr_name (a->dw_attr)); + + switch (a->dw_attr_val.val_class) + { + case dw_val_class_addr: + fprintf (outfile, "address"); + break; + case dw_val_class_loc: + fprintf (outfile, "location descriptor"); + break; + case dw_val_class_const: + fprintf (outfile, "%d", a->dw_attr_val.v.val_int); + break; + case dw_val_class_unsigned_const: + fprintf (outfile, "%u", a->dw_attr_val.v.val_unsigned); + break; + case dw_val_class_long_long: + fprintf (outfile, "constant (%u,%u)", + a->dw_attr_val.v.val_long_long.hi, + a->dw_attr_val.v.val_long_long.low); + break; + case dw_val_class_float: + fprintf (outfile, "floating-point constant"); + break; + case dw_val_class_flag: + fprintf (outfile, "%u", a->dw_attr_val.v.val_flag); + break; + case dw_val_class_die_ref: + if (a->dw_attr_val.v.val_die_ref != NULL) + fprintf (outfile, "die -> %u", + a->dw_attr_val.v.val_die_ref->die_offset); + else + fprintf (outfile, "die -> <null>"); + break; + case dw_val_class_lbl_id: + fprintf (outfile, "label: %s", a->dw_attr_val.v.val_lbl_id); + break; + case dw_val_class_section_offset: + fprintf (outfile, "section: %s", a->dw_attr_val.v.val_section); + break; + case dw_val_class_str: + if (a->dw_attr_val.v.val_str != NULL) + fprintf (outfile, "\"%s\"", a->dw_attr_val.v.val_str); + else + fprintf (outfile, "<null>"); + break; + default: + break; + } + + fprintf (outfile, "\n"); + } + + if (die->die_child != NULL) + { + print_indent += 4; + for (c = die->die_child; c != NULL; c = c->die_sib) + print_die (c, outfile); + + print_indent -= 4; + } +} + +/* Print the contents of the source code line number correspondence table. + This routine is a debugging aid only. */ + +static void +print_dwarf_line_table (outfile) + FILE *outfile; +{ + register unsigned i; + register dw_line_info_ref line_info; + + fprintf (outfile, "\n\nDWARF source line information\n"); + for (i = 1; i < line_info_table_in_use; ++i) + { + line_info = &line_info_table[i]; + fprintf (outfile, "%5d: ", i); + fprintf (outfile, "%-20s", file_table[line_info->dw_file_num]); + fprintf (outfile, "%6d", line_info->dw_line_num); + fprintf (outfile, "\n"); + } + + fprintf (outfile, "\n\n"); +} + +/* Print the information collected for a given DIE. */ + +void +debug_dwarf_die (die) + dw_die_ref die; +{ + print_die (die, stderr); +} + +/* Print all DWARF information collected for the compilation unit. + This routine is a debugging aid only. */ + +void +debug_dwarf () +{ + print_indent = 0; + print_die (comp_unit_die, stderr); + print_dwarf_line_table (stderr); +} + +/* Traverse the DIE, and add a sibling attribute if it may have the + effect of speeding up access to siblings. To save some space, + avoid generating sibling attributes for DIE's without children. */ + +static void +add_sibling_attributes(die) + register dw_die_ref die; +{ + register dw_die_ref c; + register dw_attr_ref attr; + if (die != comp_unit_die && die->die_child != NULL) + { + attr = (dw_attr_ref) xmalloc (sizeof (dw_attr_node)); + attr->dw_attr_next = NULL; + attr->dw_attr = DW_AT_sibling; + attr->dw_attr_val.val_class = dw_val_class_die_ref; + attr->dw_attr_val.v.val_die_ref = die->die_sib; + + /* Add the sibling link to the front of the attribute list. */ + attr->dw_attr_next = die->die_attr; + if (die->die_attr == NULL) + die->die_attr_last = attr; + + die->die_attr = attr; + } + + for (c = die->die_child; c != NULL; c = c->die_sib) + add_sibling_attributes (c); +} + +/* The format of each DIE (and its attribute value pairs) + is encoded in an abbreviation table. This routine builds the + abbreviation table and assigns a unique abbreviation id for + each abbreviation entry. The children of each die are visited + recursively. */ + +static void +build_abbrev_table (die) + register dw_die_ref die; +{ + register unsigned long abbrev_id; + register unsigned long n_alloc; + register dw_die_ref c; + register dw_attr_ref d_attr, a_attr; + for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) + { + register dw_die_ref abbrev = abbrev_die_table[abbrev_id]; + + if (abbrev->die_tag == die->die_tag) + { + if ((abbrev->die_child != NULL) == (die->die_child != NULL)) + { + a_attr = abbrev->die_attr; + d_attr = die->die_attr; + + while (a_attr != NULL && d_attr != NULL) + { + if ((a_attr->dw_attr != d_attr->dw_attr) + || (value_format (&a_attr->dw_attr_val) + != value_format (&d_attr->dw_attr_val))) + break; + + a_attr = a_attr->dw_attr_next; + d_attr = d_attr->dw_attr_next; + } + + if (a_attr == NULL && d_attr == NULL) + break; + } + } + } + + if (abbrev_id >= abbrev_die_table_in_use) + { + if (abbrev_die_table_in_use >= abbrev_die_table_allocated) + { + n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT; + abbrev_die_table + = (dw_die_ref *) xrealloc (abbrev_die_table, + sizeof (dw_die_ref) * n_alloc); + + bzero ((char *) &abbrev_die_table[abbrev_die_table_allocated], + (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref)); + abbrev_die_table_allocated = n_alloc; + } + + ++abbrev_die_table_in_use; + abbrev_die_table[abbrev_id] = die; + } + + die->die_abbrev = abbrev_id; + for (c = die->die_child; c != NULL; c = c->die_sib) + build_abbrev_table (c); +} + +/* Return the size of a string, including the null byte. */ + +static unsigned long +size_of_string (str) + register char *str; +{ + register unsigned long size = 0; + register unsigned long slen = strlen (str); + register unsigned long i; + register unsigned c; + + for (i = 0; i < slen; ++i) + { + c = str[i]; + if (c == '\\') + ++i; + + size += 1; + } + + /* Null terminator. */ + size += 1; + return size; +} + +/* Return the size of a location descriptor. */ + +static unsigned long +size_of_loc_descr (loc) + register dw_loc_descr_ref loc; +{ + register unsigned long size = 1; + + switch (loc->dw_loc_opc) + { + case DW_OP_addr: + size += PTR_SIZE; + break; + case DW_OP_const1u: + case DW_OP_const1s: + size += 1; + break; + case DW_OP_const2u: + case DW_OP_const2s: + size += 2; + break; + case DW_OP_const4u: + case DW_OP_const4s: + size += 4; + break; + case DW_OP_const8u: + case DW_OP_const8s: + size += 8; + break; + case DW_OP_constu: + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + break; + case DW_OP_consts: + size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); + break; + case DW_OP_pick: + size += 1; + break; + case DW_OP_plus_uconst: + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + break; + case DW_OP_skip: + case DW_OP_bra: + size += 2; + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); + break; + case DW_OP_regx: + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + break; + case DW_OP_fbreg: + size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); + break; + case DW_OP_bregx: + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + size += size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); + break; + case DW_OP_piece: + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + break; + case DW_OP_deref_size: + case DW_OP_xderef_size: + size += 1; + break; + default: + break; + } + + return size; +} + +/* Return the size of a series of location descriptors. */ + +static unsigned long +size_of_locs (loc) + register dw_loc_descr_ref loc; +{ + register unsigned long size = 0; + + for (; loc != NULL; loc = loc->dw_loc_next) + size += size_of_loc_descr (loc); + + return size; +} + +/* Return the power-of-two number of bytes necessary to represent VALUE. */ + +static int +constant_size (value) + long unsigned value; +{ + int log; + + if (value == 0) + log = 0; + else + log = floor_log2 (value); + + log = log / 8; + log = 1 << (floor_log2 (log) + 1); + + return log; +} + +/* Return the size of a DIE, as it is represented in the + .debug_info section. */ + +static unsigned long +size_of_die (die) + register dw_die_ref die; +{ + register unsigned long size = 0; + register dw_attr_ref a; + + size += size_of_uleb128 (die->die_abbrev); + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + switch (a->dw_attr_val.val_class) + { + case dw_val_class_addr: + size += PTR_SIZE; + break; + case dw_val_class_loc: + { + register unsigned long lsize + = size_of_locs (a->dw_attr_val.v.val_loc); + + /* Block length. */ + size += constant_size (lsize); + size += lsize; + } + break; + case dw_val_class_const: + size += 4; + break; + case dw_val_class_unsigned_const: + size += constant_size (a->dw_attr_val.v.val_unsigned); + break; + case dw_val_class_long_long: + size += 1 + 8; /* block */ + break; + case dw_val_class_float: + size += 1 + a->dw_attr_val.v.val_float.length * 4; /* block */ + break; + case dw_val_class_flag: + size += 1; + break; + case dw_val_class_die_ref: + size += DWARF_OFFSET_SIZE; + break; + case dw_val_class_fde_ref: + size += DWARF_OFFSET_SIZE; + break; + case dw_val_class_lbl_id: + size += PTR_SIZE; + break; + case dw_val_class_section_offset: + size += DWARF_OFFSET_SIZE; + break; + case dw_val_class_str: + size += size_of_string (a->dw_attr_val.v.val_str); + break; + default: + abort (); + } + } + + return size; +} + +/* Size the debugging information associated with a given DIE. + Visits the DIE's children recursively. Updates the global + variable next_die_offset, on each time through. Uses the + current value of next_die_offset to update the die_offset + field in each DIE. */ + +static void +calc_die_sizes (die) + dw_die_ref die; +{ + register dw_die_ref c; + die->die_offset = next_die_offset; + next_die_offset += size_of_die (die); + + for (c = die->die_child; c != NULL; c = c->die_sib) + calc_die_sizes (c); + + if (die->die_child != NULL) + /* Count the null byte used to terminate sibling lists. */ + next_die_offset += 1; +} + +/* Return the size of the line information prolog generated for the + compilation unit. */ + +static unsigned long +size_of_line_prolog () +{ + register unsigned long size; + register unsigned long ft_index; + + size = DWARF_LINE_PROLOG_HEADER_SIZE; + + /* Count the size of the table giving number of args for each + standard opcode. */ + size += DWARF_LINE_OPCODE_BASE - 1; + + /* Include directory table is empty (at present). Count only the + the null byte used to terminate the table. */ + size += 1; + + for (ft_index = 1; ft_index < file_table_in_use; ++ft_index) + { + /* File name entry. */ + size += size_of_string (file_table[ft_index]); + + /* Include directory index. */ + size += size_of_uleb128 (0); + + /* Modification time. */ + size += size_of_uleb128 (0); + + /* File length in bytes. */ + size += size_of_uleb128 (0); + } + + /* Count the file table terminator. */ + size += 1; + return size; +} + +/* Return the size of the line information generated for this + compilation unit. */ + +static unsigned long +size_of_line_info () +{ + register unsigned long size; + register unsigned long lt_index; + register unsigned long current_line; + register long line_offset; + register long line_delta; + register unsigned long current_file; + register unsigned long function; + unsigned long size_of_set_address; + + /* Size of a DW_LNE_set_address instruction. */ + size_of_set_address = 1 + size_of_uleb128 (1 + PTR_SIZE) + 1 + PTR_SIZE; + + /* Version number. */ + size = 2; + + /* Prolog length specifier. */ + size += DWARF_OFFSET_SIZE; + + /* Prolog. */ + size += size_of_line_prolog (); + + /* Set address register instruction. */ + size += size_of_set_address; + + current_file = 1; + current_line = 1; + for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) + { + register dw_line_info_ref line_info; + + /* Advance pc instruction. */ + /* ??? See the DW_LNS_advance_pc comment in output_line_info. */ + if (0) + size += 1 + 2; + else + size += size_of_set_address; + + line_info = &line_info_table[lt_index]; + if (line_info->dw_file_num != current_file) + { + /* Set file number instruction. */ + size += 1; + current_file = line_info->dw_file_num; + size += size_of_uleb128 (current_file); + } + + if (line_info->dw_line_num != current_line) + { + line_offset = line_info->dw_line_num - current_line; + line_delta = line_offset - DWARF_LINE_BASE; + current_line = line_info->dw_line_num; + if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) + /* 1-byte special line number instruction. */ + size += 1; + else + { + /* Advance line instruction. */ + size += 1; + size += size_of_sleb128 (line_offset); + /* Generate line entry instruction. */ + size += 1; + } + } + } + + /* Advance pc instruction. */ + if (0) + size += 1 + 2; + else + size += size_of_set_address; + + /* End of line number info. marker. */ + size += 1 + size_of_uleb128 (1) + 1; + + function = 0; + current_file = 1; + current_line = 1; + for (lt_index = 0; lt_index < separate_line_info_table_in_use; ) + { + register dw_separate_line_info_ref line_info + = &separate_line_info_table[lt_index]; + if (function != line_info->function) + { + function = line_info->function; + /* Set address register instruction. */ + size += size_of_set_address; + } + else + { + /* Advance pc instruction. */ + if (0) + size += 1 + 2; + else + size += size_of_set_address; + } + + if (line_info->dw_file_num != current_file) + { + /* Set file number instruction. */ + size += 1; + current_file = line_info->dw_file_num; + size += size_of_uleb128 (current_file); + } + + if (line_info->dw_line_num != current_line) + { + line_offset = line_info->dw_line_num - current_line; + line_delta = line_offset - DWARF_LINE_BASE; + current_line = line_info->dw_line_num; + if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) + /* 1-byte special line number instruction. */ + size += 1; + else + { + /* Advance line instruction. */ + size += 1; + size += size_of_sleb128 (line_offset); + + /* Generate line entry instruction. */ + size += 1; + } + } + + ++lt_index; + + /* If we're done with a function, end its sequence. */ + if (lt_index == separate_line_info_table_in_use + || separate_line_info_table[lt_index].function != function) + { + current_file = 1; + current_line = 1; + + /* Advance pc instruction. */ + if (0) + size += 1 + 2; + else + size += size_of_set_address; + + /* End of line number info. marker. */ + size += 1 + size_of_uleb128 (1) + 1; + } + } + + return size; +} + +/* Return the size of the .debug_pubnames table generated for the + compilation unit. */ + +static unsigned long +size_of_pubnames () +{ + register unsigned long size; + register unsigned i; + + size = DWARF_PUBNAMES_HEADER_SIZE; + for (i = 0; i < pubname_table_in_use; ++i) + { + register pubname_ref p = &pubname_table[i]; + size += DWARF_OFFSET_SIZE + size_of_string (p->name); + } + + size += DWARF_OFFSET_SIZE; + return size; +} + +/* Return the size of the information in the .debug_aranges section. */ + +static unsigned long +size_of_aranges () +{ + register unsigned long size; + + size = DWARF_ARANGES_HEADER_SIZE; + + /* Count the address/length pair for this compilation unit. */ + size += 2 * PTR_SIZE; + size += 2 * PTR_SIZE * arange_table_in_use; + + /* Count the two zero words used to terminated the address range table. */ + size += 2 * PTR_SIZE; + return size; +} + +/* Select the encoding of an attribute value. */ + +static enum dwarf_form +value_format (v) + dw_val_ref v; +{ + switch (v->val_class) + { + case dw_val_class_addr: + return DW_FORM_addr; + case dw_val_class_loc: + switch (constant_size (size_of_locs (v->v.val_loc))) + { + case 1: + return DW_FORM_block1; + case 2: + return DW_FORM_block2; + default: + abort (); + } + case dw_val_class_const: + return DW_FORM_data4; + case dw_val_class_unsigned_const: + switch (constant_size (v->v.val_unsigned)) + { + case 1: + return DW_FORM_data1; + case 2: + return DW_FORM_data2; + case 4: + return DW_FORM_data4; + case 8: + return DW_FORM_data8; + default: + abort (); + } + case dw_val_class_long_long: + return DW_FORM_block1; + case dw_val_class_float: + return DW_FORM_block1; + case dw_val_class_flag: + return DW_FORM_flag; + case dw_val_class_die_ref: + return DW_FORM_ref; + case dw_val_class_fde_ref: + return DW_FORM_data; + case dw_val_class_lbl_id: + return DW_FORM_addr; + case dw_val_class_section_offset: + return DW_FORM_data; + case dw_val_class_str: + return DW_FORM_string; + default: + abort (); + } +} + +/* Output the encoding of an attribute value. */ + +static void +output_value_format (v) + dw_val_ref v; +{ + enum dwarf_form form = value_format (v); + + output_uleb128 (form); + if (flag_debug_asm) + fprintf (asm_out_file, " (%s)", dwarf_form_name (form)); + + fputc ('\n', asm_out_file); +} + +/* Output the .debug_abbrev section which defines the DIE abbreviation + table. */ + +static void +output_abbrev_section () +{ + unsigned long abbrev_id; + + dw_attr_ref a_attr; + for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) + { + register dw_die_ref abbrev = abbrev_die_table[abbrev_id]; + + output_uleb128 (abbrev_id); + if (flag_debug_asm) + fprintf (asm_out_file, " (abbrev code)"); + + fputc ('\n', asm_out_file); + output_uleb128 (abbrev->die_tag); + if (flag_debug_asm) + fprintf (asm_out_file, " (TAG: %s)", + dwarf_tag_name (abbrev->die_tag)); + + fputc ('\n', asm_out_file); + fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, + abbrev->die_child != NULL ? DW_children_yes : DW_children_no); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", + ASM_COMMENT_START, + (abbrev->die_child != NULL + ? "DW_children_yes" : "DW_children_no")); + + fputc ('\n', asm_out_file); + + for (a_attr = abbrev->die_attr; a_attr != NULL; + a_attr = a_attr->dw_attr_next) + { + output_uleb128 (a_attr->dw_attr); + if (flag_debug_asm) + fprintf (asm_out_file, " (%s)", + dwarf_attr_name (a_attr->dw_attr)); + + fputc ('\n', asm_out_file); + output_value_format (&a_attr->dw_attr_val); + } + + fprintf (asm_out_file, "\t%s\t0,0\n", ASM_BYTE_OP); + } +} + +/* Output location description stack opcode's operands (if any). */ + +static void +output_loc_operands (loc) + register dw_loc_descr_ref loc; +{ + register dw_val_ref val1 = &loc->dw_loc_oprnd1; + register dw_val_ref val2 = &loc->dw_loc_oprnd2; + + switch (loc->dw_loc_opc) + { + case DW_OP_addr: + ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, val1->v.val_addr); + fputc ('\n', asm_out_file); + break; + case DW_OP_const1u: + case DW_OP_const1s: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_flag); + fputc ('\n', asm_out_file); + break; + case DW_OP_const2u: + case DW_OP_const2s: + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_const4u: + case DW_OP_const4s: + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_const8u: + case DW_OP_const8s: + abort (); + fputc ('\n', asm_out_file); + break; + case DW_OP_constu: + output_uleb128 (val1->v.val_unsigned); + fputc ('\n', asm_out_file); + break; + case DW_OP_consts: + output_sleb128 (val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_pick: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_plus_uconst: + output_uleb128 (val1->v.val_unsigned); + fputc ('\n', asm_out_file); + break; + case DW_OP_skip: + case DW_OP_bra: + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + output_sleb128 (val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_regx: + output_uleb128 (val1->v.val_unsigned); + fputc ('\n', asm_out_file); + break; + case DW_OP_fbreg: + output_sleb128 (val1->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_bregx: + output_uleb128 (val1->v.val_unsigned); + fputc ('\n', asm_out_file); + output_sleb128 (val2->v.val_int); + fputc ('\n', asm_out_file); + break; + case DW_OP_piece: + output_uleb128 (val1->v.val_unsigned); + fputc ('\n', asm_out_file); + break; + case DW_OP_deref_size: + case DW_OP_xderef_size: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, val1->v.val_flag); + fputc ('\n', asm_out_file); + break; + default: + break; + } +} + +/* Compute the offset of a sibling. */ + +static unsigned long +sibling_offset (die) + dw_die_ref die; +{ + unsigned long offset; + + if (die->die_child_last == NULL) + offset = die->die_offset + size_of_die (die); + else + offset = sibling_offset (die->die_child_last) + 1; + + return offset; +} + +/* Output the DIE and its attributes. Called recursively to generate + the definitions of each child DIE. */ + +static void +output_die (die) + register dw_die_ref die; +{ + register dw_attr_ref a; + register dw_die_ref c; + register unsigned long ref_offset; + register unsigned long size; + register dw_loc_descr_ref loc; + register int i; + + output_uleb128 (die->die_abbrev); + if (flag_debug_asm) + fprintf (asm_out_file, " (DIE (0x%x) %s)", + die->die_offset, dwarf_tag_name (die->die_tag)); + + fputc ('\n', asm_out_file); + + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + switch (a->dw_attr_val.val_class) + { + case dw_val_class_addr: + ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, + a->dw_attr_val.v.val_addr); + break; + + case dw_val_class_loc: + size = size_of_locs (a->dw_attr_val.v.val_loc); + + /* Output the block length for this list of location operations. */ + switch (constant_size (size)) + { + case 1: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, size); + break; + case 2: + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, size); + break; + default: + abort (); + } + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", + ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); + + fputc ('\n', asm_out_file); + for (loc = a->dw_attr_val.v.val_loc; loc != NULL; + loc = loc->dw_loc_next) + { + /* Output the opcode. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, loc->dw_loc_opc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", ASM_COMMENT_START, + dwarf_stack_op_name (loc->dw_loc_opc)); + + fputc ('\n', asm_out_file); + + /* Output the operand(s) (if any). */ + output_loc_operands (loc); + } + break; + + case dw_val_class_const: + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, a->dw_attr_val.v.val_int); + break; + + case dw_val_class_unsigned_const: + switch (constant_size (a->dw_attr_val.v.val_unsigned)) + { + case 1: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + a->dw_attr_val.v.val_unsigned); + break; + case 2: + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, + a->dw_attr_val.v.val_unsigned); + break; + case 4: + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + a->dw_attr_val.v.val_unsigned); + break; + case 8: + ASM_OUTPUT_DWARF_DATA8 (asm_out_file, + a->dw_attr_val.v.val_long_long.hi, + a->dw_attr_val.v.val_long_long.low); + break; + default: + abort (); + } + break; + + case dw_val_class_long_long: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 8); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", + ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA8 (asm_out_file, + a->dw_attr_val.v.val_long_long.hi, + a->dw_attr_val.v.val_long_long.low); + + if (flag_debug_asm) + fprintf (asm_out_file, + "\t%s long long constant", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + break; + + case dw_val_class_float: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + a->dw_attr_val.v.val_float.length * 4); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", + ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); + + fputc ('\n', asm_out_file); + for (i = 0; i < a->dw_attr_val.v.val_float.length; ++i) + { + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, + a->dw_attr_val.v.val_float.array[i]); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s fp constant word %d", + ASM_COMMENT_START, i); + + fputc ('\n', asm_out_file); + } + break; + + case dw_val_class_flag: + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, a->dw_attr_val.v.val_flag); + break; + + case dw_val_class_die_ref: + if (a->dw_attr_val.v.val_die_ref != NULL) + ref_offset = a->dw_attr_val.v.val_die_ref->die_offset; + else if (a->dw_attr == DW_AT_sibling) + ref_offset = sibling_offset(die); + else + abort (); + + ASM_OUTPUT_DWARF_DATA (asm_out_file, ref_offset); + break; + + case dw_val_class_fde_ref: + { + char l1[20]; + ASM_GENERATE_INTERNAL_LABEL + (l1, FDE_AFTER_SIZE_LABEL, a->dw_attr_val.v.val_fde_index * 2); + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, l1); + fprintf (asm_out_file, " - %d", DWARF_OFFSET_SIZE); + } + break; + + case dw_val_class_lbl_id: + ASM_OUTPUT_DWARF_ADDR (asm_out_file, a->dw_attr_val.v.val_lbl_id); + break; + + case dw_val_class_section_offset: + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, + stripattributes + (a->dw_attr_val.v.val_section)); + break; + + case dw_val_class_str: + if (flag_debug_asm) + ASM_OUTPUT_DWARF_STRING (asm_out_file, a->dw_attr_val.v.val_str); + else + ASM_OUTPUT_ASCII (asm_out_file, + a->dw_attr_val.v.val_str, + strlen (a->dw_attr_val.v.val_str) + 1); + break; + + default: + abort (); + } + + if (a->dw_attr_val.val_class != dw_val_class_loc + && a->dw_attr_val.val_class != dw_val_class_long_long + && a->dw_attr_val.val_class != dw_val_class_float) + { + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s", + ASM_COMMENT_START, dwarf_attr_name (a->dw_attr)); + + fputc ('\n', asm_out_file); + } + } + + for (c = die->die_child; c != NULL; c = c->die_sib) + output_die (c); + + if (die->die_child != NULL) + { + /* Add null byte to terminate sibling list. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s end of children of DIE 0x%x", + ASM_COMMENT_START, die->die_offset); + + fputc ('\n', asm_out_file); + } +} + +/* Output the compilation unit that appears at the beginning of the + .debug_info section, and precedes the DIE descriptions. */ + +static void +output_compilation_unit_header () +{ + ASM_OUTPUT_DWARF_DATA (asm_out_file, next_die_offset - DWARF_OFFSET_SIZE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Length of Compilation Unit Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DWARF version number", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (ABBREV_SECTION)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Offset Into Abbrev. Section", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, PTR_SIZE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Pointer Size (in bytes)", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); +} + +/* The DWARF2 pubname for a nested thingy looks like "A::f". The output + of decl_printable_name for C++ looks like "A::f(int)". Let's drop the + argument list, and maybe the scope. */ + +static char * +dwarf2_name (decl, scope) + tree decl; + int scope; +{ + return (*decl_printable_name) (decl, scope ? 1 : 0); +} + +/* Add a new entry to .debug_pubnames if appropriate. */ + +static void +add_pubname (decl, die) + tree decl; + dw_die_ref die; +{ + pubname_ref p; + + if (! TREE_PUBLIC (decl)) + return; + + if (pubname_table_in_use == pubname_table_allocated) + { + pubname_table_allocated += PUBNAME_TABLE_INCREMENT; + pubname_table = (pubname_ref) xrealloc + (pubname_table, pubname_table_allocated * sizeof (pubname_entry)); + } + + p = &pubname_table[pubname_table_in_use++]; + p->die = die; + + p->name = xstrdup (dwarf2_name (decl, 1)); +} + +/* Output the public names table used to speed up access to externally + visible names. For now, only generate entries for externally + visible procedures. */ + +static void +output_pubnames () +{ + register unsigned i; + register unsigned long pubnames_length = size_of_pubnames (); + + ASM_OUTPUT_DWARF_DATA (asm_out_file, pubnames_length); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Length of Public Names Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (DEBUG_INFO_SECTION)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Offset of Compilation Unit Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA (asm_out_file, next_die_offset); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Compilation Unit Length", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + for (i = 0; i < pubname_table_in_use; ++i) + { + register pubname_ref pub = &pubname_table[i]; + + ASM_OUTPUT_DWARF_DATA (asm_out_file, pub->die->die_offset); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DIE offset", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + + if (flag_debug_asm) + { + ASM_OUTPUT_DWARF_STRING (asm_out_file, pub->name); + fprintf (asm_out_file, "%s external name", ASM_COMMENT_START); + } + else + { + ASM_OUTPUT_ASCII (asm_out_file, pub->name, strlen (pub->name) + 1); + } + + fputc ('\n', asm_out_file); + } + + ASM_OUTPUT_DWARF_DATA (asm_out_file, 0); + fputc ('\n', asm_out_file); +} + +/* Add a new entry to .debug_aranges if appropriate. */ + +static void +add_arange (decl, die) + tree decl; + dw_die_ref die; +{ + if (! DECL_SECTION_NAME (decl)) + return; + + if (arange_table_in_use == arange_table_allocated) + { + arange_table_allocated += ARANGE_TABLE_INCREMENT; + arange_table + = (arange_ref) xrealloc (arange_table, + arange_table_allocated * sizeof (dw_die_ref)); + } + + arange_table[arange_table_in_use++] = die; +} + +/* Output the information that goes into the .debug_aranges table. + Namely, define the beginning and ending address range of the + text section generated for this compilation unit. */ + +static void +output_aranges () +{ + register unsigned i; + register unsigned long aranges_length = size_of_aranges (); + + ASM_OUTPUT_DWARF_DATA (asm_out_file, aranges_length); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Length of Address Ranges Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_OFFSET (asm_out_file, stripattributes (DEBUG_INFO_SECTION)); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Offset of Compilation Unit Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, PTR_SIZE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Size of Address", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Size of Segment Descriptor", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4); + if (PTR_SIZE == 8) + fprintf (asm_out_file, ",0,0"); + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Pad to %d byte boundary", + ASM_COMMENT_START, 2 * PTR_SIZE); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_SECTION); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Address", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, text_end_label, TEXT_SECTION); + if (flag_debug_asm) + fprintf (asm_out_file, "%s Length", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + for (i = 0; i < arange_table_in_use; ++i) + { + dw_die_ref a = arange_table[i]; + + if (a->die_tag == DW_TAG_subprogram) + ASM_OUTPUT_DWARF_ADDR (asm_out_file, get_AT_low_pc (a)); + else + { + char *name = get_AT_string (a, DW_AT_MIPS_linkage_name); + if (! name) + name = get_AT_string (a, DW_AT_name); + + ASM_OUTPUT_DWARF_ADDR (asm_out_file, name); + } + + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Address", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + if (a->die_tag == DW_TAG_subprogram) + ASM_OUTPUT_DWARF_ADDR_DELTA (asm_out_file, get_AT_hi_pc (a), + get_AT_low_pc (a)); + else + ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, + get_AT_unsigned (a, DW_AT_byte_size)); + + if (flag_debug_asm) + fprintf (asm_out_file, "%s Length", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + } + + /* Output the terminator words. */ + ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, 0); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR_DATA (asm_out_file, 0); + fputc ('\n', asm_out_file); +} + +/* Output the source line number correspondence information. This + information goes into the .debug_line section. + + If the format of this data changes, then the function size_of_line_info + must also be adjusted the same way. */ + +static void +output_line_info () +{ + char line_label[MAX_ARTIFICIAL_LABEL_BYTES]; + char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES]; + register unsigned opc; + register unsigned n_op_args; + register unsigned long ft_index; + register unsigned long lt_index; + register unsigned long current_line; + register long line_offset; + register long line_delta; + register unsigned long current_file; + register unsigned long function; + + ASM_OUTPUT_DWARF_DATA (asm_out_file, size_of_line_info ()); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Length of Source Line Info.", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA2 (asm_out_file, DWARF_VERSION); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DWARF Version", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA (asm_out_file, size_of_line_prolog ()); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Prolog Length", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_LINE_MIN_INSTR_LENGTH); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Minimum Instruction Length", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DWARF_LINE_DEFAULT_IS_STMT_START); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Default is_stmt_start flag", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + fprintf (asm_out_file, "\t%s\t%d", ASM_BYTE_OP, DWARF_LINE_BASE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Line Base Value (Special Opcodes)", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + fprintf (asm_out_file, "\t%s\t%u", ASM_BYTE_OP, DWARF_LINE_RANGE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Line Range Value (Special Opcodes)", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + fprintf (asm_out_file, "\t%s\t%u", ASM_BYTE_OP, DWARF_LINE_OPCODE_BASE); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s Special Opcode Base", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; ++opc) + { + switch (opc) + { + case DW_LNS_advance_pc: + case DW_LNS_advance_line: + case DW_LNS_set_file: + case DW_LNS_set_column: + case DW_LNS_fixed_advance_pc: + n_op_args = 1; + break; + default: + n_op_args = 0; + break; + } + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, n_op_args); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s opcode: 0x%x has %d args", + ASM_COMMENT_START, opc, n_op_args); + fputc ('\n', asm_out_file); + } + + if (flag_debug_asm) + fprintf (asm_out_file, "%s Include Directory Table\n", ASM_COMMENT_START); + + /* Include directory table is empty, at present */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + fputc ('\n', asm_out_file); + if (flag_debug_asm) + fprintf (asm_out_file, "%s File Name Table\n", ASM_COMMENT_START); + + for (ft_index = 1; ft_index < file_table_in_use; ++ft_index) + { + if (flag_debug_asm) + { + ASM_OUTPUT_DWARF_STRING (asm_out_file, file_table[ft_index]); + fprintf (asm_out_file, "%s File Entry: 0x%x", + ASM_COMMENT_START, ft_index); + } + else + { + ASM_OUTPUT_ASCII (asm_out_file, + file_table[ft_index], + strlen (file_table[ft_index]) + 1); + } + + fputc ('\n', asm_out_file); + + /* Include directory index */ + output_uleb128 (0); + fputc ('\n', asm_out_file); + + /* Modification time */ + output_uleb128 (0); + fputc ('\n', asm_out_file); + + /* File length in bytes */ + output_uleb128 (0); + fputc ('\n', asm_out_file); + } + + /* Terminate the file name table */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + fputc ('\n', asm_out_file); + + /* Set the address register to the first location in the text section */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_SECTION); + fputc ('\n', asm_out_file); + + /* Generate the line number to PC correspondence table, encoded as + a series of state machine operations. */ + current_file = 1; + current_line = 1; + strcpy (prev_line_label, TEXT_SECTION); + for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) + { + register dw_line_info_ref line_info; + + /* Emit debug info for the address of the current line, choosing + the encoding that uses the least amount of space. */ + /* ??? Unfortunately, we have little choice here currently, and must + always use the most general form. Gcc does not know the address + delta itself, so we can't use DW_LNS_advance_pc. There are no known + dwarf2 aware assemblers at this time, so we can't use any special + pseudo ops that would allow the assembler to optimally encode this for + us. Many ports do have length attributes which will give an upper + bound on the address range. We could perhaps use length attributes + to determine when it is safe to use DW_LNS_fixed_advance_pc. */ + ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index); + if (0) + { + /* This can handle deltas up to 0xffff. This takes 3 bytes. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, prev_line_label); + fputc ('\n', asm_out_file); + } + else + { + /* This can handle any delta. This takes 4+PTR_SIZE bytes. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", + ASM_COMMENT_START); + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); + fputc ('\n', asm_out_file); + } + strcpy (prev_line_label, line_label); + + /* Emit debug info for the source file of the current line, if + different from the previous line. */ + line_info = &line_info_table[lt_index]; + if (line_info->dw_file_num != current_file) + { + current_file = line_info->dw_file_num; + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_set_file); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_set_file", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (current_file); + if (flag_debug_asm) + fprintf (asm_out_file, " (\"%s\")", file_table[current_file]); + + fputc ('\n', asm_out_file); + } + + /* Emit debug info for the current line number, choosing the encoding + that uses the least amount of space. */ + line_offset = line_info->dw_line_num - current_line; + line_delta = line_offset - DWARF_LINE_BASE; + current_line = line_info->dw_line_num; + if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) + { + /* This can handle deltas from -10 to 234, using the current + definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This + takes 1 byte. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + DWARF_LINE_OPCODE_BASE + line_delta); + if (flag_debug_asm) + fprintf (asm_out_file, + "\t%s line %d", ASM_COMMENT_START, current_line); + + fputc ('\n', asm_out_file); + } + else + { + /* This can handle any delta. This takes at least 4 bytes, depending + on the value being encoded. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_advance_line); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s advance to line %d", + ASM_COMMENT_START, current_line); + + fputc ('\n', asm_out_file); + output_sleb128 (line_offset); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_copy); + fputc ('\n', asm_out_file); + } + } + + /* Emit debug info for the address of the end of the function. */ + if (0) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, text_end_label, prev_line_label); + fputc ('\n', asm_out_file); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", ASM_COMMENT_START); + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, text_end_label); + fputc ('\n', asm_out_file); + } + + /* Output the marker for the end of the line number info. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_end_sequence", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (1); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_end_sequence); + fputc ('\n', asm_out_file); + + function = 0; + current_file = 1; + current_line = 1; + for (lt_index = 0; lt_index < separate_line_info_table_in_use; ) + { + register dw_separate_line_info_ref line_info + = &separate_line_info_table[lt_index]; + + /* Emit debug info for the address of the current line. If this is + a new function, or the first line of a function, then we need + to handle it differently. */ + ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL, + lt_index); + if (function != line_info->function) + { + function = line_info->function; + + /* Set the address register to the first line in the function */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); + fputc ('\n', asm_out_file); + } + else + { + /* ??? See the DW_LNS_advance_pc comment above. */ + if (0) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, + prev_line_label); + fputc ('\n', asm_out_file); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", + ASM_COMMENT_START); + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); + fputc ('\n', asm_out_file); + } + } + strcpy (prev_line_label, line_label); + + /* Emit debug info for the source file of the current line, if + different from the previous line. */ + if (line_info->dw_file_num != current_file) + { + current_file = line_info->dw_file_num; + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_set_file); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_set_file", ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (current_file); + if (flag_debug_asm) + fprintf (asm_out_file, " (\"%s\")", file_table[current_file]); + + fputc ('\n', asm_out_file); + } + + /* Emit debug info for the current line number, choosing the encoding + that uses the least amount of space. */ + if (line_info->dw_line_num != current_line) + { + line_offset = line_info->dw_line_num - current_line; + line_delta = line_offset - DWARF_LINE_BASE; + current_line = line_info->dw_line_num; + if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, + DWARF_LINE_OPCODE_BASE + line_delta); + if (flag_debug_asm) + fprintf (asm_out_file, + "\t%s line %d", ASM_COMMENT_START, current_line); + + fputc ('\n', asm_out_file); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_advance_line); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s advance to line %d", + ASM_COMMENT_START, current_line); + + fputc ('\n', asm_out_file); + output_sleb128 (line_offset); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_copy); + fputc ('\n', asm_out_file); + } + } + + ++lt_index; + + /* If we're done with a function, end its sequence. */ + if (lt_index == separate_line_info_table_in_use + || separate_line_info_table[lt_index].function != function) + { + current_file = 1; + current_line = 1; + + /* Emit debug info for the address of the end of the function. */ + ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function); + if (0) + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNS_fixed_advance_pc); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNS_fixed_advance_pc", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, line_label, + prev_line_label); + fputc ('\n', asm_out_file); + } + else + { + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_set_address", + ASM_COMMENT_START); + fputc ('\n', asm_out_file); + output_uleb128 (1 + PTR_SIZE); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_set_address); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_ADDR (asm_out_file, line_label); + fputc ('\n', asm_out_file); + } + + /* Output the marker for the end of this sequence. */ + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, 0); + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s DW_LNE_end_sequence", + ASM_COMMENT_START); + + fputc ('\n', asm_out_file); + output_uleb128 (1); + fputc ('\n', asm_out_file); + ASM_OUTPUT_DWARF_DATA1 (asm_out_file, DW_LNE_end_sequence); + fputc ('\n', asm_out_file); + } + } +} + +/* Given a pointer to a BLOCK node return non-zero if (and only if) the node + in question represents the outermost pair of curly braces (i.e. the "body + block") of a function or method. + + For any BLOCK node representing a "body block" of a function or method, the + BLOCK_SUPERCONTEXT of the node will point to another BLOCK node which + represents the outermost (function) scope for the function or method (i.e. + the one which includes the formal parameters). The BLOCK_SUPERCONTEXT of + *that* node in turn will point to the relevant FUNCTION_DECL node. */ + +static inline int +is_body_block (stmt) + register tree stmt; +{ + if (TREE_CODE (stmt) == BLOCK) + { + register tree parent = BLOCK_SUPERCONTEXT (stmt); + + if (TREE_CODE (parent) == BLOCK) + { + register tree grandparent = BLOCK_SUPERCONTEXT (parent); + + if (TREE_CODE (grandparent) == FUNCTION_DECL) + return 1; + } + } + + return 0; +} + +/* Given a pointer to a tree node for some base type, return a pointer to + a DIE that describes the given type. + + This routine must only be called for GCC type nodes that correspond to + Dwarf base (fundamental) types. */ + +static dw_die_ref +base_type_die (type) + register tree type; +{ + register dw_die_ref base_type_result; + register char *type_name; + register enum dwarf_type encoding; + register tree name = TYPE_NAME (type); + + if (TREE_CODE (type) == ERROR_MARK + || TREE_CODE (type) == VOID_TYPE) + return 0; + + if (TREE_CODE (name) == TYPE_DECL) + name = DECL_NAME (name); + type_name = IDENTIFIER_POINTER (name); + + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + /* Carefully distinguish the C character types, 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_PRECISION (type) == CHAR_TYPE_SIZE + && (type == char_type_node + || ! strcmp (type_name, "signed char") + || ! strcmp (type_name, "unsigned char")))) + { + if (TREE_UNSIGNED (type)) + encoding = DW_ATE_unsigned; + else + encoding = DW_ATE_signed; + break; + } + /* else fall through */ + + case CHAR_TYPE: + /* GNU Pascal/Ada CHAR type. Not used in C. */ + if (TREE_UNSIGNED (type)) + encoding = DW_ATE_unsigned_char; + else + encoding = DW_ATE_signed_char; + break; + + case REAL_TYPE: + encoding = DW_ATE_float; + break; + + case COMPLEX_TYPE: + encoding = DW_ATE_complex_float; + break; + + case BOOLEAN_TYPE: + /* GNU FORTRAN/Ada/C++ BOOLEAN type. */ + encoding = DW_ATE_boolean; + break; + + default: + abort (); /* No other TREE_CODEs are Dwarf fundamental types. */ + } + + base_type_result = new_die (DW_TAG_base_type, comp_unit_die); + add_AT_string (base_type_result, DW_AT_name, type_name); + add_AT_unsigned (base_type_result, DW_AT_byte_size, + TYPE_PRECISION (type) / BITS_PER_UNIT); + add_AT_unsigned (base_type_result, DW_AT_encoding, encoding); + + return base_type_result; +} + +/* 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 the given + type is the root type of the "basis" type for the pointer or reference + type. (This definition of the "root" type is recursive.) Also, the root + type of a `const' qualified type or a `volatile' qualified type is the + root type of the given type without the qualifiers. */ + +static tree +root_type (type) + register tree type; +{ + if (TREE_CODE (type) == ERROR_MARK) + return error_mark_node; + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + return error_mark_node; + + case POINTER_TYPE: + case REFERENCE_TYPE: + return type_main_variant (root_type (TREE_TYPE (type))); + + default: + return type_main_variant (type); + } +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the + given input type is a Dwarf "fundamental" type. Otherwise return null. */ + +static inline int +is_base_type (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: + return 1; + + case SET_TYPE: + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case ENUMERAL_TYPE: + case FUNCTION_TYPE: + case METHOD_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + case FILE_TYPE: + case OFFSET_TYPE: + case LANG_TYPE: + return 0; + + default: + abort (); + } + + return 0; +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging + entry that chains various modifiers in front of the given type. */ + +static dw_die_ref +modified_type_die (type, is_const_type, is_volatile_type, context_die) + register tree type; + register int is_const_type; + register int is_volatile_type; + register dw_die_ref context_die; +{ + register enum tree_code code = TREE_CODE (type); + register dw_die_ref mod_type_die = NULL; + register dw_die_ref sub_die = NULL; + register tree item_type = NULL; + + if (code != ERROR_MARK) + { + type = build_type_variant (type, is_const_type, is_volatile_type); + + mod_type_die = lookup_type_die (type); + if (mod_type_die) + return mod_type_die; + + /* Handle C typedef types. */ + if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) + { + tree dtype = TREE_TYPE (TYPE_NAME (type)); + if (type == dtype) + { + /* For a named type, use the typedef. */ + gen_type_die (type, context_die); + mod_type_die = lookup_type_die (type); + } + + else if (is_const_type < TYPE_READONLY (dtype) + || is_volatile_type < TYPE_VOLATILE (dtype)) + /* cv-unqualified version of named type. Just use the unnamed + type to which it refers. */ + mod_type_die + = modified_type_die (DECL_ORIGINAL_TYPE (TYPE_NAME (type)), + is_const_type, is_volatile_type, + context_die); + /* Else cv-qualified version of named type; fall through. */ + } + + if (mod_type_die) + /* OK */; + else if (is_const_type) + { + mod_type_die = new_die (DW_TAG_const_type, comp_unit_die); + sub_die = modified_type_die (type, 0, is_volatile_type, context_die); + } + else if (is_volatile_type) + { + mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die); + sub_die = modified_type_die (type, 0, 0, context_die); + } + else if (code == POINTER_TYPE) + { + mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); +#if 0 + add_AT_unsigned (mod_type_die, DW_AT_address_class, 0); +#endif + item_type = TREE_TYPE (type); + } + else if (code == REFERENCE_TYPE) + { + mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); +#if 0 + add_AT_unsigned (mod_type_die, DW_AT_address_class, 0); +#endif + item_type = TREE_TYPE (type); + } + else if (is_base_type (type)) + mod_type_die = base_type_die (type); + else + { + gen_type_die (type, context_die); + + /* We have to get the type_main_variant here (and pass that to the + `lookup_type_die' routine) because the ..._TYPE node we have + might simply be a *copy* of some original type node (where the + copy was created to help us keep track of typedef names) and + that copy might have a different TYPE_UID from the original + ..._TYPE node. */ + mod_type_die = lookup_type_die (type_main_variant (type)); + assert (mod_type_die != NULL); + } + } + + equate_type_number_to_die (type, mod_type_die); + if (item_type) + /* We must do this after the equate_type_number_to_die call, in case + this is a recursive type. This ensures that the modified_type_die + recursion will terminate even if the type is recursive. Recursive + types are possible in Ada. */ + sub_die = modified_type_die (item_type, + TYPE_READONLY (item_type), + TYPE_VOLATILE (item_type), + context_die); + + if (sub_die != NULL) + add_AT_die_ref (mod_type_die, DW_AT_type, sub_die); + + return mod_type_die; +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is + an enumerated type. */ + +static inline int +type_is_enum (type) + register tree type; +{ + return TREE_CODE (type) == ENUMERAL_TYPE; +} + +/* Return a location descriptor that designates a machine register. */ + +static dw_loc_descr_ref +reg_loc_descriptor (rtl) + register rtx rtl; +{ + register dw_loc_descr_ref loc_result = NULL; + register unsigned reg = reg_number (rtl); + + if (reg >= 0 && reg <= 31) + loc_result = new_loc_descr (DW_OP_reg0 + reg, 0, 0); + else + loc_result = new_loc_descr (DW_OP_regx, reg, 0); + + return loc_result; +} + +/* Return a location descriptor that designates a base+offset location. */ + +static dw_loc_descr_ref +based_loc_descr (reg, offset) + unsigned reg; + long int offset; +{ + register dw_loc_descr_ref loc_result; + /* For the "frame base", we use the frame pointer or stack pointer + registers, since the RTL for local variables is relative to one of + them. */ + register unsigned fp_reg = DBX_REGISTER_NUMBER (frame_pointer_needed + ? HARD_FRAME_POINTER_REGNUM + : STACK_POINTER_REGNUM); + + if (reg == fp_reg) + loc_result = new_loc_descr (DW_OP_fbreg, offset, 0); + else if (reg >= 0 && reg <= 31) + loc_result = new_loc_descr (DW_OP_breg0 + reg, offset, 0); + else + loc_result = new_loc_descr (DW_OP_bregx, reg, offset); + + return loc_result; +} + +/* Return true if this RTL expression describes a base+offset calculation. */ + +static inline int +is_based_loc (rtl) + register rtx rtl; +{ + return (GET_CODE (rtl) == PLUS + && ((GET_CODE (XEXP (rtl, 0)) == REG + && GET_CODE (XEXP (rtl, 1)) == CONST_INT))); +} + +/* The following routine converts the RTL for a variable or parameter + (resident in memory) into an equivalent Dwarf representation of a + mechanism for getting the address of that same variable onto the top of a + hypothetical "address evaluation" stack. + + When creating memory location descriptors, we are effectively transforming + the RTL for a memory-resident object into its Dwarf postfix expression + equivalent. This routine recursively descends an RTL tree, turning + it into Dwarf postfix code as it goes. */ + +static dw_loc_descr_ref +mem_loc_descriptor (rtl) + register rtx rtl; +{ + dw_loc_descr_ref mem_loc_result = NULL; + /* Note that for a dynamically sized array, the location we will generate a + description of here will be the lowest numbered location which is + actually within the array. That's *not* necessarily the same as the + zeroth element of the array. */ + + switch (GET_CODE (rtl)) + { + case SUBREG: + /* The case of a subreg may arise when we have a local (register) + variable or a formal (register) parameter which doesn't quite fill + up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register which + contains the given subreg. */ + rtl = XEXP (rtl, 0); + + /* ... fall through ... */ + + case REG: + /* Whenever a register number forms a part of the description of the + method for calculating the (dynamic) address of a memory resident + object, DWARF rules require the register number be referred to as + a "base register". This distinction is not based in any way upon + what category of register the hardware believes the given register + belongs to. This is strictly DWARF terminology we're dealing with + here. Note that in cases where the location of a memory-resident + data object could be expressed as: OP_ADD (OP_BASEREG (basereg), + OP_CONST (0)) the actual DWARF location descriptor that we generate + may just be OP_BASEREG (basereg). This may look deceptively like + the object in question was allocated to a register (rather than in + memory) so DWARF consumers need to be aware of the subtle + distinction between OP_REG and OP_BASEREG. */ + mem_loc_result = based_loc_descr (reg_number (rtl), 0); + break; + + case MEM: + mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0)); + add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0)); + break; + + case CONST: + case SYMBOL_REF: + mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0); + mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr; + mem_loc_result->dw_loc_oprnd1.v.val_addr = addr_to_string (rtl); + break; + + case PLUS: + if (is_based_loc (rtl)) + mem_loc_result = based_loc_descr (reg_number (XEXP (rtl, 0)), + INTVAL (XEXP (rtl, 1))); + else + { + add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 0))); + add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 1))); + add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_plus, 0, 0)); + } + break; + + case MULT: + /* If a pseudo-reg is optimized away, it is possible for it to + be replaced with a MEM containing a multiply. */ + add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 0))); + add_loc_descr (&mem_loc_result, mem_loc_descriptor (XEXP (rtl, 1))); + add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_mul, 0, 0)); + break; + + case CONST_INT: + mem_loc_result = new_loc_descr (DW_OP_constu, INTVAL (rtl), 0); + break; + + default: + abort (); + } + + return mem_loc_result; +} + +/* Return a descriptor that describes the concatenation of two locations. + This is typically a complex variable. */ + +static dw_loc_descr_ref +concat_loc_descriptor (x0, x1) + register rtx x0, x1; +{ + dw_loc_descr_ref cc_loc_result = NULL; + + if (!is_pseudo_reg (x0) + && (GET_CODE (x0) != MEM || !is_pseudo_reg (XEXP (x0, 0)))) + add_loc_descr (&cc_loc_result, loc_descriptor (x0)); + add_loc_descr (&cc_loc_result, + new_loc_descr (DW_OP_piece, GET_MODE_SIZE (GET_MODE (x0)), 0)); + + if (!is_pseudo_reg (x1) + && (GET_CODE (x1) != MEM || !is_pseudo_reg (XEXP (x1, 0)))) + add_loc_descr (&cc_loc_result, loc_descriptor (x1)); + add_loc_descr (&cc_loc_result, + new_loc_descr (DW_OP_piece, GET_MODE_SIZE (GET_MODE (x1)), 0)); + + return cc_loc_result; +} + +/* Output a proper Dwarf location descriptor for a variable or parameter + which is either allocated in a register or in a memory location. For a + register, we just generate an OP_REG and the register number. For a + memory location we provide a Dwarf postfix expression describing how to + generate the (dynamic) address of the object onto the address stack. */ + +static dw_loc_descr_ref +loc_descriptor (rtl) + register rtx rtl; +{ + dw_loc_descr_ref loc_result = NULL; + switch (GET_CODE (rtl)) + { + case SUBREG: + /* The case of a subreg may arise when we have a local (register) + variable or a formal (register) parameter which doesn't quite fill + up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register which + contains the given subreg. */ + rtl = XEXP (rtl, 0); + + /* ... fall through ... */ + + case REG: + loc_result = reg_loc_descriptor (rtl); + break; + + case MEM: + loc_result = mem_loc_descriptor (XEXP (rtl, 0)); + break; + + case CONCAT: + loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1)); + break; + + default: + abort (); + } + + return loc_result; +} + +/* Given an unsigned value, round it up to the lowest multiple of `boundary' + which is not less than the value itself. */ + +static inline unsigned +ceiling (value, boundary) + register unsigned value; + register unsigned boundary; +{ + return (((value + boundary - 1) / boundary) * boundary); +} + +/* Given a pointer to what is assumed to be a FIELD_DECL node, return a + pointer to the declared type for the relevant field variable, or return + `integer_type_node' if the given node turns out to be an + ERROR_MARK node. */ + +static inline tree +field_type (decl) + register tree decl; +{ + register tree type; + + if (TREE_CODE (decl) == ERROR_MARK) + return integer_type_node; + + type = DECL_BIT_FIELD_TYPE (decl); + if (type == NULL_TREE) + type = TREE_TYPE (decl); + + return type; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the alignment in bits for the type, or else return + BITS_PER_WORD if the node actually turns out to be an + ERROR_MARK node. */ + +static inline unsigned +simple_type_align_in_bits (type) + register tree type; +{ + return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the size in bits for the type if it is a constant, or else + return the alignment for the type if the type's size is not constant, or + else return BITS_PER_WORD if the type actually turns out to be an + ERROR_MARK node. */ + +static inline unsigned +simple_type_size_in_bits (type) + register tree type; +{ + if (TREE_CODE (type) == ERROR_MARK) + return BITS_PER_WORD; + else + { + register tree type_size_tree = TYPE_SIZE (type); + + if (TREE_CODE (type_size_tree) != INTEGER_CST) + return TYPE_ALIGN (type); + + return (unsigned) TREE_INT_CST_LOW (type_size_tree); + } +} + +/* Given a pointer to what is assumed to be a FIELD_DECL node, compute and + return the byte offset of the lowest addressed byte of the "containing + object" for the given FIELD_DECL, or return 0 if we are unable to + determine what that offset is, either because the argument turns out to + be a pointer to an ERROR_MARK node, or because the offset is actually + variable. (We can't handle the latter case just yet). */ + +static unsigned +field_byte_offset (decl) + register tree decl; +{ + register unsigned type_align_in_bytes; + register unsigned type_align_in_bits; + register unsigned type_size_in_bits; + register unsigned object_offset_in_align_units; + register unsigned object_offset_in_bits; + register unsigned object_offset_in_bytes; + register tree type; + register tree bitpos_tree; + register tree field_size_tree; + register unsigned bitpos_int; + register unsigned deepest_bitpos; + register unsigned field_size_in_bits; + + if (TREE_CODE (decl) == ERROR_MARK) + return 0; + + if (TREE_CODE (decl) != FIELD_DECL) + abort (); + + type = field_type (decl); + + bitpos_tree = DECL_FIELD_BITPOS (decl); + field_size_tree = DECL_SIZE (decl); + + /* We cannot yet cope with fields whose positions or sizes are variable, so + for now, when we see such things, we simply return 0. Someday, we may + be able to handle such cases, but it will be damn difficult. */ + if (TREE_CODE (bitpos_tree) != INTEGER_CST) + return 0; + bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); + + if (TREE_CODE (field_size_tree) != INTEGER_CST) + return 0; + + field_size_in_bits = (unsigned) TREE_INT_CST_LOW (field_size_tree); + type_size_in_bits = simple_type_size_in_bits (type); + type_align_in_bits = simple_type_align_in_bits (type); + type_align_in_bytes = type_align_in_bits / BITS_PER_UNIT; + + /* Note that the GCC front-end doesn't make any attempt to keep track of + the starting bit offset (relative to the start of the containing + structure type) of the hypothetical "containing object" for a bit- + field. Thus, when computing the byte offset value for the start of the + "containing object" of a bit-field, we must deduce this information on + our own. This can be rather tricky to do in some cases. For example, + handling the following structure type definition when compiling for an + i386/i486 target (which only aligns long long's to 32-bit boundaries) + can be very tricky: + + struct S { int field1; long long field2:31; }; + + Fortunately, there is a simple rule-of-thumb which can be + used in such cases. When compiling for an i386/i486, GCC will allocate + 8 bytes for the structure shown above. It decides to do this based upon + one simple rule for bit-field allocation. Quite simply, GCC allocates + each "containing object" for each bit-field at the first (i.e. lowest + addressed) legitimate alignment boundary (based upon the required + minimum alignment for the declared type of the field) which it can + possibly use, subject to the condition that there is still enough + available space remaining in the containing object (when allocated at + the selected point) to fully accommodate all of the bits of the + bit-field itself. This simple rule makes it obvious why GCC allocates + 8 bytes for each object of the structure type shown above. When looking + for a place to allocate the "containing object" for `field2', the + compiler simply tries to allocate a 64-bit "containing object" at each + successive 32-bit boundary (starting at zero) until it finds a place to + allocate that 64- bit field such that at least 31 contiguous (and + previously unallocated) bits remain within that selected 64 bit field. + (As it turns out, for the example above, the compiler finds that it is + OK to allocate the "containing object" 64-bit field at bit-offset zero + within the structure type.) Here we attempt to work backwards from the + limited set of facts we're given, and we try to deduce from those facts, + where GCC must have believed that the containing object started (within + the structure type). The value we deduce is then used (by the callers of + this routine) to generate DW_AT_location and DW_AT_bit_offset attributes + for fields (both bit-fields and, in the case of DW_AT_location, regular + fields as well). */ + + /* Figure out the bit-distance from the start of the structure to the + "deepest" bit of the bit-field. */ + deepest_bitpos = bitpos_int + field_size_in_bits; + + /* This is the tricky part. Use some fancy footwork to deduce where the + lowest addressed bit of the containing object must be. */ + object_offset_in_bits + = ceiling (deepest_bitpos, type_align_in_bits) - type_size_in_bits; + + /* Compute the offset of the containing object in "alignment units". */ + object_offset_in_align_units = object_offset_in_bits / type_align_in_bits; + + /* Compute the offset of the containing object in bytes. */ + object_offset_in_bytes = object_offset_in_align_units * type_align_in_bytes; + + return object_offset_in_bytes; +} + +/* The following routines define various Dwarf attributes and any data + associated with them. */ + +/* Add a location description attribute value to a DIE. + + This emits location attributes suitable for whole variables and + whole parameters. Note that the location attributes for struct fields are + generated by the routine `data_member_location_attribute' below. */ + +static void +add_AT_location_description (die, attr_kind, rtl) + dw_die_ref die; + enum dwarf_attribute attr_kind; + register rtx rtl; +{ + /* Handle a special case. If we are about to output a location descriptor + for a variable or parameter which has been optimized out of existence, + don't do that. A variable which has been optimized out + of existence will have a DECL_RTL value which denotes a pseudo-reg. + Currently, in some rare cases, variables can have DECL_RTL values which + look like (MEM (REG pseudo-reg#)). These cases are due to bugs + elsewhere in the compiler. We treat such cases as if the variable(s) in + question had been optimized out of existence. */ + + if (is_pseudo_reg (rtl) + || (GET_CODE (rtl) == MEM + && is_pseudo_reg (XEXP (rtl, 0))) + || (GET_CODE (rtl) == CONCAT + && is_pseudo_reg (XEXP (rtl, 0)) + && is_pseudo_reg (XEXP (rtl, 1)))) + return; + + add_AT_loc (die, attr_kind, loc_descriptor (rtl)); +} + +/* Attach the specialized form of location attribute used for data + members of struct and union types. In the special case of a + FIELD_DECL node which represents a bit-field, the "offset" part + of this special location descriptor must indicate the distance + in bytes from the lowest-addressed byte of the containing struct + or union type to the lowest-addressed byte of the "containing + object" for the bit-field. (See the `field_byte_offset' function + above).. For any given bit-field, the "containing object" is a + hypothetical object (of some integral or enum type) within which + the given bit-field lives. The type of this hypothetical + "containing object" is always the same as the declared type of + the individual bit-field itself (for GCC anyway... the DWARF + spec doesn't actually mandate this). Note that it is the size + (in bytes) of the hypothetical "containing object" which will + be given in the DW_AT_byte_size attribute for this bit-field. + (See the `byte_size_attribute' function below.) It is also used + when calculating the value of the DW_AT_bit_offset attribute. + (See the `bit_offset_attribute' function below). */ + +static void +add_data_member_location_attribute (die, decl) + register dw_die_ref die; + register tree decl; +{ + register unsigned long offset; + register dw_loc_descr_ref loc_descr; + register enum dwarf_location_atom op; + + if (TREE_CODE (decl) == TREE_VEC) + offset = TREE_INT_CST_LOW (BINFO_OFFSET (decl)); + else + offset = field_byte_offset (decl); + + /* The DWARF2 standard says that we should assume that the structure address + is already on the stack, so we can specify a structure field address + by using DW_OP_plus_uconst. */ + +#ifdef MIPS_DEBUGGING_INFO + /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst operator + correctly. It works only if we leave the offset on the stack. */ + op = DW_OP_constu; +#else + op = DW_OP_plus_uconst; +#endif + + loc_descr = new_loc_descr (op, offset, 0); + add_AT_loc (die, DW_AT_data_member_location, loc_descr); +} + +/* Attach an DW_AT_const_value attribute for a variable or a parameter which + does not have a "location" either in memory or in a register. These + things can arise in GNU C when a constant is passed as an actual parameter + to an inlined function. They can also arise in C++ where declared + constants do not necessarily get memory "homes". */ + +static void +add_const_value_attribute (die, rtl) + register dw_die_ref die; + register rtx rtl; +{ + switch (GET_CODE (rtl)) + { + case CONST_INT: + /* Note that a CONST_INT rtx could represent either an integer or a + floating-point constant. A CONST_INT is used whenever the constant + will fit into a single word. In all such cases, the original mode + of the constant value is wiped out, and the CONST_INT rtx is + assigned VOIDmode. */ + add_AT_unsigned (die, DW_AT_const_value, (unsigned) INTVAL (rtl)); + break; + + case CONST_DOUBLE: + /* Note that a CONST_DOUBLE rtx could represent either an integer or a + floating-point constant. A CONST_DOUBLE is used whenever the + constant requires more than one word in order to be adequately + represented. We output CONST_DOUBLEs as blocks. */ + { + register enum machine_mode mode = GET_MODE (rtl); + + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + { + register unsigned length = GET_MODE_SIZE (mode) / sizeof (long); + long array[4]; + REAL_VALUE_TYPE rv; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, rtl); + switch (mode) + { + case SFmode: + REAL_VALUE_TO_TARGET_SINGLE (rv, array[0]); + break; + + case DFmode: + REAL_VALUE_TO_TARGET_DOUBLE (rv, array); + break; + + case XFmode: + case TFmode: + REAL_VALUE_TO_TARGET_LONG_DOUBLE (rv, array); + break; + + default: + abort (); + } + + add_AT_float (die, DW_AT_const_value, length, array); + } + else + add_AT_long_long (die, DW_AT_const_value, + CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl)); + } + break; + + case CONST_STRING: + add_AT_string (die, DW_AT_const_value, XSTR (rtl, 0)); + break; + + case SYMBOL_REF: + case LABEL_REF: + case CONST: + add_AT_addr (die, DW_AT_const_value, addr_to_string (rtl)); + break; + + case PLUS: + /* In cases where an inlined instance of an inline function is passed + the address of an `auto' variable (which is local to the caller) we + can get a situation where the DECL_RTL of the artificial local + variable (for the inlining) which acts as a stand-in for the + corresponding formal parameter (of the inline function) will look + like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). This is not + exactly a compile-time constant expression, but it isn't the address + of the (artificial) local variable either. Rather, it represents the + *value* which the artificial local variable always has during its + lifetime. We currently have no way to represent such quasi-constant + values in Dwarf, so for now we just punt and generate nothing. */ + break; + + default: + /* No other kinds of rtx should be possible here. */ + abort (); + } + +} + +/* Generate *either* an DW_AT_location attribute or else an DW_AT_const_value + data attribute for a variable or a parameter. We generate the + DW_AT_const_value attribute only in those cases where the given variable + or parameter does not have a true "location" either in memory or in a + register. This can happen (for example) when a constant is passed as an + actual argument in a call to an inline function. (It's possible that + these things can crop up in other ways also.) Note that one type of + constant value which can be passed into an inlined function is a constant + pointer. This can happen for example if an actual argument in an inlined + function call evaluates to a compile-time constant address. */ + +static void +add_location_or_const_value_attribute (die, decl) + register dw_die_ref die; + register tree decl; +{ + register rtx rtl; + register tree declared_type; + register tree passed_type; + + if (TREE_CODE (decl) == ERROR_MARK) + return; + + if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != PARM_DECL) + abort (); + + /* Here we have to decide where we are going to say the parameter "lives" + (as far as the debugger is concerned). We only have a couple of + choices. GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. + + DECL_RTL normally indicates where the parameter lives during most of the + activation of the function. If optimization is enabled however, this + could be either NULL or else a pseudo-reg. Both of those cases indicate + that the parameter doesn't really live anywhere (as far as the code + generation parts of GCC are concerned) during most of the function's + activation. That will happen (for example) if the parameter is never + referenced within the function. + + We could just generate a location descriptor here for all non-NULL + non-pseudo values of DECL_RTL and ignore all of the rest, but we can be + a little nicer than that if we also consider DECL_INCOMING_RTL in cases + where DECL_RTL is NULL or is a pseudo-reg. + + Note however that we can only get away with using DECL_INCOMING_RTL as + a backup substitute for DECL_RTL in certain limited cases. In cases + where DECL_ARG_TYPE (decl) indicates the same type as TREE_TYPE (decl), + we can be sure that the parameter was passed using the same type as it is + declared to have within the function, and that its DECL_INCOMING_RTL + points us to a place where a value of that type is passed. + + In cases where DECL_ARG_TYPE (decl) and TREE_TYPE (decl) are different, + we cannot (in general) use DECL_INCOMING_RTL as a substitute for DECL_RTL + because in these cases DECL_INCOMING_RTL points us to a value of some + type which is *different* from the type of the parameter itself. Thus, + if we tried to use DECL_INCOMING_RTL to generate a location attribute in + such cases, the debugger would end up (for example) trying to fetch a + `float' from a place which actually contains the first part of a + `double'. That would lead to really incorrect and confusing + output at debug-time. + + So, in general, we *do not* use DECL_INCOMING_RTL as a backup for DECL_RTL + in cases where DECL_ARG_TYPE (decl) != TREE_TYPE (decl). There + are a couple of exceptions however. On little-endian machines we can + get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE (decl) is + not the same as TREE_TYPE (decl), but only when DECL_ARG_TYPE (decl) is + an integral type that is smaller than TREE_TYPE (decl). These cases arise + when (on a little-endian machine) a non-prototyped function has a + parameter declared to be of type `short' or `char'. In such cases, + TREE_TYPE (decl) will be `short' or `char', DECL_ARG_TYPE (decl) will + be `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the + passed `int' value. If the debugger then uses that address to fetch + a `short' or a `char' (on a little-endian machine) the result will be + the correct data, so we allow for such exceptional cases below. + + Note that our goal here is to describe the place where the given formal + parameter lives during most of the function's activation (i.e. between + the end of the prologue and the start of the epilogue). We'll do that + as best as we can. Note however that if the given formal parameter is + modified sometime during the execution of the function, then a stack + backtrace (at debug-time) will show the function as having been + called with the *new* value rather than the value which was + originally passed in. This happens rarely enough that it is not + a major problem, but it *is* a problem, and I'd like to fix it. + + A future version of dwarf2out.c may generate two additional + attributes for any given DW_TAG_formal_parameter DIE which will + describe the "passed type" and the "passed location" for the + given formal parameter in addition to the attributes we now + generate to indicate the "declared type" and the "active + location" for each parameter. This additional set of attributes + could be used by debuggers for stack backtraces. Separately, note + that sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL can be + NULL also. This happens (for example) for inlined-instances of + inline function formal parameters which are never referenced. + This really shouldn't be happening. All PARM_DECL nodes should + get valid non-NULL DECL_INCOMING_RTL values, but integrate.c + doesn't currently generate these values for inlined instances of + inline function parameters, so when we see such cases, we are + just out-of-luck for the time being (until integrate.c + gets fixed). */ + + /* Use DECL_RTL as the "location" unless we find something better. */ + rtl = DECL_RTL (decl); + + if (TREE_CODE (decl) == PARM_DECL) + { + if (rtl == NULL_RTX || is_pseudo_reg (rtl)) + { + declared_type = type_main_variant (TREE_TYPE (decl)); + passed_type = type_main_variant (DECL_ARG_TYPE (decl)); + + /* This decl represents a formal parameter which was optimized out. + Note that DECL_INCOMING_RTL may be NULL in here, but we handle + all* cases where (rtl == NULL_RTX) just below. */ + if (declared_type == passed_type) + rtl = DECL_INCOMING_RTL (decl); + else if (! BYTES_BIG_ENDIAN + && TREE_CODE (declared_type) == INTEGER_TYPE + && TYPE_SIZE (declared_type) <= TYPE_SIZE (passed_type)) + rtl = DECL_INCOMING_RTL (decl); + } + } + + if (rtl == NULL_RTX) + return; + + rtl = eliminate_regs (rtl, 0, NULL_RTX, 0); +#ifdef LEAF_REG_REMAP + if (leaf_function) + leaf_renumber_regs_insn (rtl); +#endif + + switch (GET_CODE (rtl)) + { + case ADDRESSOF: + /* The address of a variable that was optimized away; don't emit + anything. */ + break; + + case CONST_INT: + case CONST_DOUBLE: + case CONST_STRING: + case SYMBOL_REF: + case LABEL_REF: + case CONST: + case PLUS: + /* DECL_RTL could be (plus (reg ...) (const_int ...)) */ + add_const_value_attribute (die, rtl); + break; + + case MEM: + case REG: + case SUBREG: + case CONCAT: + add_AT_location_description (die, DW_AT_location, rtl); + break; + + default: + abort (); + } +} + +/* Generate an DW_AT_name attribute given some string value to be included as + the value of the attribute. */ + +static inline void +add_name_attribute (die, name_string) + register dw_die_ref die; + register char *name_string; +{ + if (name_string != NULL && *name_string != 0) + add_AT_string (die, DW_AT_name, name_string); +} + +/* Given a tree node describing an array bound (either lower or upper) output + a representation for that bound. */ + +static void +add_bound_info (subrange_die, bound_attr, bound) + register dw_die_ref subrange_die; + register enum dwarf_attribute bound_attr; + register tree bound; +{ + register unsigned bound_value = 0; + + /* If this is an Ada unconstrained array type, then don't emit any debug + info because the array bounds are unknown. They are parameterized when + the type is instantiated. */ + if (contains_placeholder_p (bound)) + return; + + switch (TREE_CODE (bound)) + { + case ERROR_MARK: + return; + + /* All fixed-bounds are represented by INTEGER_CST nodes. */ + case INTEGER_CST: + bound_value = TREE_INT_CST_LOW (bound); + if (bound_attr == DW_AT_lower_bound + && ((is_c_family () && bound_value == 0) + || (is_fortran () && bound_value == 1))) + /* use the default */; + else + add_AT_unsigned (subrange_die, bound_attr, bound_value); + break; + + case CONVERT_EXPR: + case NOP_EXPR: + case NON_LVALUE_EXPR: + add_bound_info (subrange_die, bound_attr, TREE_OPERAND (bound, 0)); + break; + + case SAVE_EXPR: + /* If optimization is turned on, the SAVE_EXPRs that describe how to + access the upper bound values may be bogus. If they refer to a + register, they may only describe 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 (though the + number of elements will), 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 don't add an attribute + 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 storage class `auto' local array variable + indicates that the upper bound is both unknown (at compile- time) + and unknowable (at run-time) due to optimization. + + We assume that a MEM rtx is safe because gcc wouldn't put the + value there unless it was going to be used repeatedly in the + function, i.e. for cleanups. */ + if (! optimize || GET_CODE (SAVE_EXPR_RTL (bound)) == MEM) + { + register dw_die_ref ctx = lookup_decl_die (current_function_decl); + register dw_die_ref decl_die = new_die (DW_TAG_variable, ctx); + add_AT_flag (decl_die, DW_AT_artificial, 1); + add_type_attribute (decl_die, TREE_TYPE (bound), 1, 0, ctx); + add_AT_location_description (decl_die, DW_AT_location, + SAVE_EXPR_RTL (bound)); + add_AT_die_ref (subrange_die, bound_attr, decl_die); + } + + /* Else leave out the attribute. */ + break; + + case MAX_EXPR: + case VAR_DECL: + case COMPONENT_REF: + /* ??? These types of bounds can be created by the Ada front end, + and it isn't clear how to emit debug info for them. */ + break; + + default: + abort (); + } +} + +/* 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 +add_subscript_info (type_die, type) + register dw_die_ref type_die; + register tree type; +{ + register unsigned dimension_number; + register tree lower, upper; + register dw_die_ref subrange_die; + + /* 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. The draft + Dwarf specification say that we are allowed to do this kind of + compression in C (because there is no difference between an array or + arrays and a multidimensional array in C) but for other source languages + (e.g. Ada) we probably shouldn't do this. */ + + /* ??? The SGI dwarf reader fails for multidimensional arrays with a + const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. + We work around this by disabling this feature. See also + gen_array_type_die. */ +#ifndef MIPS_DEBUGGING_INFO + for (dimension_number = 0; + TREE_CODE (type) == ARRAY_TYPE; + type = TREE_TYPE (type), dimension_number++) + { +#endif + 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. */ + subrange_die = new_die (DW_TAG_subrange_type, type_die); + if (domain) + { + /* We have an array type with specified bounds. */ + lower = TYPE_MIN_VALUE (domain); + upper = TYPE_MAX_VALUE (domain); + + /* define the index type. */ + if (TREE_TYPE (domain)) + { + /* ??? This is probably an Ada unnamed subrange type. Ignore the + TREE_TYPE field. We can't emit debug info for this + because it is an unnamed integral type. */ + if (TREE_CODE (domain) == INTEGER_TYPE + && TYPE_NAME (domain) == NULL_TREE + && TREE_CODE (TREE_TYPE (domain)) == INTEGER_TYPE + && TYPE_NAME (TREE_TYPE (domain)) == NULL_TREE) + ; + else + add_type_attribute (subrange_die, TREE_TYPE (domain), 0, 0, + type_die); + } + + add_bound_info (subrange_die, DW_AT_lower_bound, lower); + add_bound_info (subrange_die, DW_AT_upper_bound, upper); + } + else + /* We have an array type with an unspecified length. The DWARF-2 + spec does not say how to handle this; let's just leave out the + bounds. */ + ; + +#ifndef MIPS_DEBUGGING_INFO + } +#endif +} + +static void +add_byte_size_attribute (die, tree_node) + dw_die_ref die; + register tree tree_node; +{ + register unsigned size; + + switch (TREE_CODE (tree_node)) + { + case ERROR_MARK: + size = 0; + break; + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + size = int_size_in_bytes (tree_node); + break; + case FIELD_DECL: + /* For a data member of a struct or union, the DW_AT_byte_size is + generally given as the number of bytes normally allocated for an + object of the *declared* type of the member itself. This is true + even for bit-fields. */ + size = simple_type_size_in_bits (field_type (tree_node)) / BITS_PER_UNIT; + break; + default: + abort (); + } + + /* Note that `size' might be -1 when we get to this point. If it is, that + indicates that the byte size of the entity in question is variable. We + have no good way of expressing this fact in Dwarf at the present time, + so just let the -1 pass on through. */ + + add_AT_unsigned (die, DW_AT_byte_size, size); +} + +/* For a FIELD_DECL node which represents a bit-field, output an attribute + which specifies the distance in bits from the highest order bit of the + "containing object" for the bit-field to the highest order bit of the + bit-field itself. + + For any given bit-field, the "containing object" is a hypothetical + object (of some integral or enum type) within which the given bit-field + lives. The type of this hypothetical "containing object" is always the + same as the declared type of the individual bit-field itself. The + determination of the exact location of the "containing object" for a + bit-field is rather complicated. It's handled by the + `field_byte_offset' function (above). + + Note that it is the size (in bytes) of the hypothetical "containing object" + which will be given in the DW_AT_byte_size attribute for this bit-field. + (See `byte_size_attribute' above). */ + +static inline void +add_bit_offset_attribute (die, decl) + register dw_die_ref die; + register tree decl; +{ + register unsigned object_offset_in_bytes = field_byte_offset (decl); + register tree type = DECL_BIT_FIELD_TYPE (decl); + register tree bitpos_tree = DECL_FIELD_BITPOS (decl); + register unsigned bitpos_int; + register unsigned highest_order_object_bit_offset; + register unsigned highest_order_field_bit_offset; + register unsigned bit_offset; + + assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ + assert (type); /* Must be a bit field. */ + + /* We can't yet handle bit-fields whose offsets are variable, so if we + encounter such things, just return without generating any attribute + whatsoever. */ + if (TREE_CODE (bitpos_tree) != INTEGER_CST) + return; + + bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree); + + /* Note that the bit offset is always the distance (in bits) from the + highest-order bit of the "containing object" to the highest-order bit of + the bit-field itself. Since the "high-order end" of any object or field + is different on big-endian and little-endian machines, the computation + below must take account of these differences. */ + highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT; + highest_order_field_bit_offset = bitpos_int; + + if (! BYTES_BIG_ENDIAN) + { + highest_order_field_bit_offset + += (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl)); + + highest_order_object_bit_offset += simple_type_size_in_bits (type); + } + + bit_offset + = (! BYTES_BIG_ENDIAN + ? highest_order_object_bit_offset - highest_order_field_bit_offset + : highest_order_field_bit_offset - highest_order_object_bit_offset); + + add_AT_unsigned (die, DW_AT_bit_offset, bit_offset); +} + +/* For a FIELD_DECL node which represents a bit field, output an attribute + which specifies the length in bits of the given field. */ + +static inline void +add_bit_size_attribute (die, decl) + register dw_die_ref die; + register tree decl; +{ + assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */ + assert (DECL_BIT_FIELD_TYPE (decl)); /* Must be a bit field. */ + add_AT_unsigned (die, DW_AT_bit_size, + (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl))); +} + +/* If the compiled language is ANSI C, then add a 'prototyped' + attribute, if arg types are given for the parameters of a function. */ + +static inline void +add_prototyped_attribute (die, func_type) + register dw_die_ref die; + register tree func_type; +{ + if (get_AT_unsigned (comp_unit_die, DW_AT_language) == DW_LANG_C89 + && TYPE_ARG_TYPES (func_type) != NULL) + add_AT_flag (die, DW_AT_prototyped, 1); +} + + +/* Add an 'abstract_origin' attribute below a given DIE. The DIE is found + by looking in either the type declaration or object declaration + equate table. */ + +static inline void +add_abstract_origin_attribute (die, origin) + register dw_die_ref die; + register tree origin; +{ + dw_die_ref origin_die = NULL; + if (TREE_CODE_CLASS (TREE_CODE (origin)) == 'd') + origin_die = lookup_decl_die (origin); + else if (TREE_CODE_CLASS (TREE_CODE (origin)) == 't') + origin_die = lookup_type_die (origin); + + add_AT_die_ref (die, DW_AT_abstract_origin, origin_die); +} + +/* We do not currently support the pure_virtual attribute. */ + +static inline void +add_pure_or_virtual_attribute (die, func_decl) + register dw_die_ref die; + register tree func_decl; +{ + if (DECL_VINDEX (func_decl)) + { + add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); + add_AT_loc (die, DW_AT_vtable_elem_location, + new_loc_descr (DW_OP_constu, + TREE_INT_CST_LOW (DECL_VINDEX (func_decl)), + 0)); + + /* GNU extension: Record what type this method came from originally. */ + if (debug_info_level > DINFO_LEVEL_TERSE) + add_AT_die_ref (die, DW_AT_containing_type, + lookup_type_die (DECL_CONTEXT (func_decl))); + } +} + +/* Add source coordinate attributes for the given decl. */ + +static void +add_src_coords_attributes (die, decl) + register dw_die_ref die; + register tree decl; +{ + register unsigned file_index = lookup_filename (DECL_SOURCE_FILE (decl)); + + add_AT_unsigned (die, DW_AT_decl_file, file_index); + add_AT_unsigned (die, DW_AT_decl_line, DECL_SOURCE_LINE (decl)); +} + +/* Add an DW_AT_name attribute and source coordinate attribute for the + given decl, but only if it actually has a name. */ + +static void +add_name_and_src_coords_attributes (die, decl) + register dw_die_ref die; + register tree decl; +{ + register tree decl_name; + + decl_name = DECL_NAME (decl); + if (decl_name != NULL && IDENTIFIER_POINTER (decl_name) != NULL) + { + add_name_attribute (die, dwarf2_name (decl, 0)); + add_src_coords_attributes (die, decl); + if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) + && DECL_ASSEMBLER_NAME (decl) != DECL_NAME (decl)) + add_AT_string (die, DW_AT_MIPS_linkage_name, + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); + } +} + +/* Push a new declaration scope. */ + +static void +push_decl_scope (scope) + tree scope; +{ + /* Make room in the decl_scope_table, if necessary. */ + if (decl_scope_table_allocated == decl_scope_depth) + { + decl_scope_table_allocated += DECL_SCOPE_TABLE_INCREMENT; + decl_scope_table + = (tree *) xrealloc (decl_scope_table, + decl_scope_table_allocated * sizeof (tree)); + } + + decl_scope_table[decl_scope_depth++] = scope; +} + +/* Return the DIE for the scope the immediately contains this declaration. */ + +static dw_die_ref +scope_die_for (t, context_die) + register tree t; + register dw_die_ref context_die; +{ + register dw_die_ref scope_die = NULL; + register tree containing_scope; + register unsigned long i; + + /* Walk back up the declaration tree looking for a place to define + this type. */ + if (TREE_CODE_CLASS (TREE_CODE (t)) == 't') + containing_scope = TYPE_CONTEXT (t); + else if (TREE_CODE (t) == FUNCTION_DECL && DECL_VINDEX (t)) + containing_scope = decl_class_context (t); + else + containing_scope = DECL_CONTEXT (t); + + /* Function-local tags and functions get stuck in limbo until they are + fixed up by decls_for_scope. */ + if (context_die == NULL && containing_scope != NULL_TREE + && (TREE_CODE (t) == FUNCTION_DECL || is_tagged_type (t))) + return NULL; + + if (containing_scope == NULL_TREE) + scope_die = comp_unit_die; + else + { + for (i = decl_scope_depth, scope_die = context_die; + i > 0 && decl_scope_table[i - 1] != containing_scope; + scope_die = scope_die->die_parent, --i) + ; + + if (i == 0) + { + assert (scope_die == comp_unit_die); + assert (TREE_CODE_CLASS (TREE_CODE (containing_scope)) == 't'); + if (debug_info_level > DINFO_LEVEL_TERSE) + assert (TREE_ASM_WRITTEN (containing_scope)); + } + } + + return scope_die; +} + +/* Pop a declaration scope. */ +static inline void +pop_decl_scope () +{ + assert (decl_scope_depth > 0); + --decl_scope_depth; +} + +/* Many forms of DIEs require a "type description" attribute. This + routine locates the proper "type descriptor" die for the type given + by 'type', and adds an DW_AT_type attribute below the given die. */ + +static void +add_type_attribute (object_die, type, decl_const, decl_volatile, context_die) + register dw_die_ref object_die; + register tree type; + register int decl_const; + register int decl_volatile; + register dw_die_ref context_die; +{ + register enum tree_code code = TREE_CODE (type); + register dw_die_ref type_die = NULL; + + /* ??? If this type is an unnamed subrange type of an integral or + floating-point type, use the inner type. This is because we have no + support for unnamed types in base_type_die. This can happen if this is + an Ada subrange type. Correct solution is emit a subrange type die. */ + if ((code == INTEGER_TYPE || code == REAL_TYPE) + && TREE_TYPE (type) != 0 && TYPE_NAME (type) == 0) + type = TREE_TYPE (type), code = TREE_CODE (type); + + if (code == ERROR_MARK) + return; + + /* Handle a special case. For functions whose return type is void, we + generate *no* type attribute. (Note that no object may have type + `void', so this only applies to function return types). */ + if (code == VOID_TYPE) + return; + + type_die = modified_type_die (type, + decl_const || TYPE_READONLY (type), + decl_volatile || TYPE_VOLATILE (type), + context_die); + if (type_die != NULL) + add_AT_die_ref (object_die, DW_AT_type, type_die); +} + +/* Given 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); + + /* The g++ front end makes the TYPE_NAME of *each* tagged type point to + a TYPE_DECL node, regardless of whether or not a `typedef' was + involved. */ + else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && ! DECL_IGNORED_P (TYPE_NAME (type))) + t = DECL_NAME (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; +} + +/* Return the type associated with a data member, make a special check + for bit field types. */ + +static inline tree +member_declared_type (member) + register tree member; +{ + return (DECL_BIT_FIELD_TYPE (member) + ? DECL_BIT_FIELD_TYPE (member) + : TREE_TYPE (member)); +} + +/* Get the decl's label, as described by its RTL. This may be different + from the DECL_NAME name used in the source file. */ + +static char * +decl_start_label (decl) + register tree decl; +{ + rtx x; + char *fnname; + x = DECL_RTL (decl); + if (GET_CODE (x) != MEM) + abort (); + + x = XEXP (x, 0); + if (GET_CODE (x) != SYMBOL_REF) + abort (); + + fnname = XSTR (x, 0); + return fnname; +} + +/* These routines generate the internal representation of the DIE's for + the compilation unit. Debugging information is collected by walking + the declaration trees passed in from dwarf2out_decl(). */ + +static void +gen_array_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref scope_die = scope_die_for (type, context_die); + register dw_die_ref array_die; + register tree element_type; + + /* ??? The SGI dwarf reader fails for array of array of enum types unless + the inner array type comes before the outer array type. Thus we must + call gen_type_die before we call new_die. See below also. */ +#ifdef MIPS_DEBUGGING_INFO + gen_type_die (TREE_TYPE (type), context_die); +#endif + + array_die = new_die (DW_TAG_array_type, scope_die); + +#if 0 + /* We default the array ordering. SDB will probably do + the right things even if DW_AT_ordering is not present. It's not even + an issue until we start to get into multidimensional arrays anyway. If + SDB is ever caught doing the Wrong Thing for multi-dimensional arrays, + then we'll have to put the DW_AT_ordering attribute back in. (But if + and when we find out that we need to put these in, we will only do so + for multidimensional arrays. */ + add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_row_major); +#endif + +#ifdef MIPS_DEBUGGING_INFO + /* The SGI compilers handle arrays of unknown bound by setting + AT_declaration and not emitting any subrange DIEs. */ + if (! TYPE_DOMAIN (type)) + add_AT_unsigned (array_die, DW_AT_declaration, 1); + else +#endif + add_subscript_info (array_die, type); + + equate_type_number_to_die (type, array_die); + + /* Add representation of the type of the elements of this array type. */ + element_type = TREE_TYPE (type); + + /* ??? The SGI dwarf reader fails for multidimensional arrays with a + const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. + We work around this by disabling this feature. See also + add_subscript_info. */ +#ifndef MIPS_DEBUGGING_INFO + while (TREE_CODE (element_type) == ARRAY_TYPE) + element_type = TREE_TYPE (element_type); + + gen_type_die (element_type, context_die); +#endif + + add_type_attribute (array_die, element_type, 0, 0, context_die); +} + +static void +gen_set_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die + = new_die (DW_TAG_set_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, type_die); + add_type_attribute (type_die, TREE_TYPE (type), 0, 0, context_die); +} + +static void +gen_entry_point_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register tree origin = decl_ultimate_origin (decl); + register dw_die_ref decl_die = new_die (DW_TAG_entry_point, context_die); + if (origin != NULL) + add_abstract_origin_attribute (decl_die, origin); + else + { + add_name_and_src_coords_attributes (decl_die, decl); + add_type_attribute (decl_die, TREE_TYPE (TREE_TYPE (decl)), + 0, 0, context_die); + } + + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die (decl, decl_die); + else + add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl)); +} + +/* Remember a type in the pending_types_list. */ + +static void +pend_type (type) + register tree type; +{ + if (pending_types == pending_types_allocated) + { + pending_types_allocated += PENDING_TYPES_INCREMENT; + pending_types_list + = (tree *) xrealloc (pending_types_list, + sizeof (tree) * pending_types_allocated); + } + + pending_types_list[pending_types++] = type; +} + +/* Output any pending types (from the pending_types list) which we can output + now (taking into account the scope that we are working on now). + + For each type output, remove the given type from the pending_types_list + *before* we try to output it. */ + +static void +output_pending_types_for_scope (context_die) + register dw_die_ref context_die; +{ + register tree type; + + while (pending_types) + { + --pending_types; + type = pending_types_list[pending_types]; + gen_type_die (type, context_die); + assert (TREE_ASM_WRITTEN (type)); + } +} + +/* Generate a DIE to represent an inlined instance of an enumeration type. */ + +static void +gen_inlined_enumeration_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die = new_die (DW_TAG_enumeration_type, + scope_die_for (type, context_die)); + + assert (TREE_ASM_WRITTEN (type)); + add_abstract_origin_attribute (type_die, type); +} + +/* Generate a DIE to represent an inlined instance of a structure type. */ + +static void +gen_inlined_structure_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die = new_die (DW_TAG_structure_type, + scope_die_for (type, context_die)); + + assert (TREE_ASM_WRITTEN (type)); + add_abstract_origin_attribute (type_die, type); +} + +/* Generate a DIE to represent an inlined instance of a union type. */ + +static void +gen_inlined_union_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die = new_die (DW_TAG_union_type, + scope_die_for (type, context_die)); + + assert (TREE_ASM_WRITTEN (type)); + add_abstract_origin_attribute (type_die, type); +} + +/* Generate a DIE to represent an enumeration type. Note that these DIEs + include all of the information about the enumeration values also. Each + enumerated type name/value is listed as a child of the enumerated type + DIE. */ + +static void +gen_enumeration_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die = lookup_type_die (type); + + if (type_die == NULL) + { + type_die = new_die (DW_TAG_enumeration_type, + scope_die_for (type, context_die)); + equate_type_number_to_die (type, type_die); + add_name_attribute (type_die, type_tag (type)); + } + else if (! TYPE_SIZE (type)) + return; + else + remove_AT (type_die, DW_AT_declaration); + + /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the + given enum type is incomplete, do not generate the DW_AT_byte_size + attribute or the DW_AT_element_list attribute. */ + if (TYPE_SIZE (type)) + { + register tree link; + + TREE_ASM_WRITTEN (type) = 1; + add_byte_size_attribute (type_die, type); + if (TYPE_STUB_DECL (type) != NULL_TREE) + add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); + + /* If the first reference to this type was as the return type of an + inline function, then it may not have a parent. Fix this now. */ + if (type_die->die_parent == NULL) + add_child_die (scope_die_for (type, context_die), type_die); + + for (link = TYPE_FIELDS (type); + link != NULL; link = TREE_CHAIN (link)) + { + register dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die); + + add_name_attribute (enum_die, + IDENTIFIER_POINTER (TREE_PURPOSE (link))); + add_AT_unsigned (enum_die, DW_AT_const_value, + (unsigned) TREE_INT_CST_LOW (TREE_VALUE (link))); + } + } + else + add_AT_flag (type_die, DW_AT_declaration, 1); +} + + +/* Generate a DIE to represent either a real live formal parameter decl or to + represent just the type of some formal parameter position in some function + type. + + Note that this routine is a bit unusual because its argument may be a + ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which + represents an inlining of some PARM_DECL) or else some sort of a ..._TYPE + node. If it's the former then this function is being called to output a + DIE to represent a formal parameter object (or some inlining thereof). If + it's the latter, then this function is only being called to output a + DW_TAG_formal_parameter DIE to stand as a placeholder for some formal + argument type of some subprogram type. */ + +static dw_die_ref +gen_formal_parameter_die (node, context_die) + register tree node; + register dw_die_ref context_die; +{ + register dw_die_ref parm_die + = new_die (DW_TAG_formal_parameter, context_die); + register tree origin; + + switch (TREE_CODE_CLASS (TREE_CODE (node))) + { + case 'd': + origin = decl_ultimate_origin (node); + if (origin != NULL) + add_abstract_origin_attribute (parm_die, origin); + else + { + add_name_and_src_coords_attributes (parm_die, node); + add_type_attribute (parm_die, TREE_TYPE (node), + TREE_READONLY (node), + TREE_THIS_VOLATILE (node), + context_die); + if (DECL_ARTIFICIAL (node)) + add_AT_flag (parm_die, DW_AT_artificial, 1); + } + + equate_decl_number_to_die (node, parm_die); + if (! DECL_ABSTRACT (node)) + add_location_or_const_value_attribute (parm_die, node); + + break; + + case 't': + /* We were called with some kind of a ..._TYPE node. */ + add_type_attribute (parm_die, node, 0, 0, context_die); + break; + + default: + abort (); + } + + return parm_die; +} + +/* Generate a special type of DIE used as a stand-in for a trailing ellipsis + at the end of an (ANSI prototyped) formal parameters list. */ + +static void +gen_unspecified_parameters_die (decl_or_type, context_die) + register tree decl_or_type; + register dw_die_ref context_die; +{ + register dw_die_ref parm_die = new_die (DW_TAG_unspecified_parameters, + context_die); +} + +/* Generate a list of nameless DW_TAG_formal_parameter DIEs (and perhaps a + DW_TAG_unspecified_parameters DIE) to represent the types of the formal + parameters as specified in some function type specification (except for + those which appear as part of a function *definition*). + + Note we must be careful here to output all of the parameter DIEs before* + we output any DIEs needed to represent the types of the formal parameters. + This keeps svr4 SDB happy because it (incorrectly) thinks that the first + non-parameter DIE it sees ends the formal parameter list. */ + +static void +gen_formal_types_die (function_or_method_type, context_die) + register tree function_or_method_type; + register dw_die_ref context_die; +{ + register tree link; + register tree formal_type = NULL; + register tree first_parm_type = TYPE_ARG_TYPES (function_or_method_type); + +#if 0 + /* In the case where we are generating a formal types list for a C++ + non-static member function type, skip over the first thing on the + TYPE_ARG_TYPES list because it only represents the type of the hidden + `this pointer'. The debugger should be able to figure out (without + being explicitly told) that this non-static member function type takes a + `this pointer' and should be able to figure what the type of that hidden + parameter is from the DW_AT_member attribute of the parent + DW_TAG_subroutine_type DIE. */ + if (TREE_CODE (function_or_method_type) == METHOD_TYPE) + first_parm_type = TREE_CHAIN (first_parm_type); +#endif + + /* Make our first pass over the list of formal parameter types and output a + DW_TAG_formal_parameter DIE for each one. */ + for (link = first_parm_type; link; link = TREE_CHAIN (link)) + { + register dw_die_ref parm_die; + + formal_type = TREE_VALUE (link); + if (formal_type == void_type_node) + break; + + /* Output a (nameless) DIE to represent the formal parameter itself. */ + parm_die = gen_formal_parameter_die (formal_type, context_die); + if (TREE_CODE (function_or_method_type) == METHOD_TYPE + && link == first_parm_type) + add_AT_flag (parm_die, DW_AT_artificial, 1); + } + + /* If this function type has an ellipsis, add a + DW_TAG_unspecified_parameters DIE to the end of the parameter list. */ + if (formal_type != void_type_node) + gen_unspecified_parameters_die (function_or_method_type, context_die); + + /* Make our second (and final) pass over the list of formal parameter types + and output DIEs to represent those types (as necessary). */ + for (link = TYPE_ARG_TYPES (function_or_method_type); + link; + link = TREE_CHAIN (link)) + { + formal_type = TREE_VALUE (link); + if (formal_type == void_type_node) + break; + + gen_type_die (formal_type, context_die); + } +} + +/* Generate a DIE to represent a declared function (either file-scope or + block-local). */ + +static void +gen_subprogram_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; + register tree origin = decl_ultimate_origin (decl); + register dw_die_ref subr_die; + register dw_loc_descr_ref fp_loc = NULL; + register rtx fp_reg; + register tree fn_arg_types; + register tree outer_scope; + register dw_die_ref old_die = lookup_decl_die (decl); + register int declaration + = (current_function_decl != decl + || (context_die + && (context_die->die_tag == DW_TAG_structure_type + || context_die->die_tag == DW_TAG_union_type))); + + if (origin != NULL) + { + subr_die = new_die (DW_TAG_subprogram, context_die); + add_abstract_origin_attribute (subr_die, origin); + } + else if (old_die && DECL_ABSTRACT (decl) + && get_AT_unsigned (old_die, DW_AT_inline)) + { + /* This must be a redefinition of an extern inline function. + We can just reuse the old die here. */ + subr_die = old_die; + + /* Clear out the inlined attribute and parm types. */ + remove_AT (subr_die, DW_AT_inline); + remove_children (subr_die); + } + else if (old_die) + { + register unsigned file_index + = lookup_filename (DECL_SOURCE_FILE (decl)); + + assert (get_AT_flag (old_die, DW_AT_declaration) == 1); + + /* If the definition comes from the same place as the declaration, + maybe use the old DIE. We always want the DIE for this function + that has the *_pc attributes to be under comp_unit_die so the + debugger can find it. For inlines, that is the concrete instance, + so we can use the old DIE here. For non-inline methods, we want a + specification DIE at toplevel, so we need a new DIE. For local + class methods, this does not apply. */ + if ((DECL_ABSTRACT (decl) || old_die->die_parent == comp_unit_die + || context_die == NULL) + && get_AT_unsigned (old_die, DW_AT_decl_file) == file_index + && (get_AT_unsigned (old_die, DW_AT_decl_line) + == DECL_SOURCE_LINE (decl))) + { + subr_die = old_die; + + /* Clear out the declaration attribute and the parm types. */ + remove_AT (subr_die, DW_AT_declaration); + remove_children (subr_die); + } + else + { + subr_die = new_die (DW_TAG_subprogram, context_die); + add_AT_die_ref (subr_die, DW_AT_specification, old_die); + if (get_AT_unsigned (old_die, DW_AT_decl_file) != file_index) + add_AT_unsigned (subr_die, DW_AT_decl_file, file_index); + if (get_AT_unsigned (old_die, DW_AT_decl_line) + != DECL_SOURCE_LINE (decl)) + add_AT_unsigned + (subr_die, DW_AT_decl_line, DECL_SOURCE_LINE (decl)); + } + } + else + { + register dw_die_ref scope_die; + + if (DECL_CONTEXT (decl)) + scope_die = scope_die_for (decl, context_die); + else + /* Don't put block extern declarations under comp_unit_die. */ + scope_die = context_die; + + subr_die = new_die (DW_TAG_subprogram, scope_die); + + if (TREE_PUBLIC (decl)) + add_AT_flag (subr_die, DW_AT_external, 1); + + add_name_and_src_coords_attributes (subr_die, decl); + if (debug_info_level > DINFO_LEVEL_TERSE) + { + register tree type = TREE_TYPE (decl); + + add_prototyped_attribute (subr_die, type); + add_type_attribute (subr_die, TREE_TYPE (type), 0, 0, context_die); + } + + add_pure_or_virtual_attribute (subr_die, decl); + if (DECL_ARTIFICIAL (decl)) + add_AT_flag (subr_die, DW_AT_artificial, 1); + if (TREE_PROTECTED (decl)) + add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_protected); + else if (TREE_PRIVATE (decl)) + add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_private); + } + + if (declaration) + { + add_AT_flag (subr_die, DW_AT_declaration, 1); + + /* The first time we see a member function, it is in the context of + the class to which it belongs. We make sure of this by emitting + the class first. The next time is the definition, which is + handled above. The two may come from the same source text. */ + if (DECL_CONTEXT (decl)) + equate_decl_number_to_die (decl, subr_die); + } + else if (DECL_ABSTRACT (decl)) + { + /* ??? Checking DECL_DEFER_OUTPUT is correct for static inline functions, + but not for extern inline functions. We can't get this completely + correct because information about whether the function was declared + inline is not saved anywhere. */ + if (DECL_DEFER_OUTPUT (decl)) + { + if (DECL_INLINE (decl)) + add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_inlined); + else + add_AT_unsigned (subr_die, DW_AT_inline, + DW_INL_declared_not_inlined); + } + else if (DECL_INLINE (decl)) + add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_inlined); + else + abort (); + + equate_decl_number_to_die (decl, subr_die); + } + else if (!DECL_EXTERNAL (decl)) + { + if (origin == NULL_TREE) + equate_decl_number_to_die (decl, subr_die); + + ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL, + current_funcdef_number); + add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id); + ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, + current_funcdef_number); + add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id); + + add_pubname (decl, subr_die); + add_arange (decl, subr_die); + +#ifdef MIPS_DEBUGGING_INFO + /* Add a reference to the FDE for this routine. */ + add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, current_funcdef_fde); +#endif + + /* Define the "frame base" location for this routine. We use the + frame pointer or stack pointer registers, since the RTL for local + variables is relative to one of them. */ + fp_reg + = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx; + add_AT_loc (subr_die, DW_AT_frame_base, reg_loc_descriptor (fp_reg)); + +#if 0 + /* ??? This fails for nested inline functions, because context_display + is not part of the state saved/restored for inline functions. */ + if (current_function_needs_context) + add_AT_location_description (subr_die, DW_AT_static_link, + lookup_static_chain (decl)); +#endif + } + + /* Now output descriptions of the arguments for this function. This gets + (unnecessarily?) complex because of the fact that the DECL_ARGUMENT list + for a FUNCTION_DECL doesn't indicate cases where there was a trailing + `...' at the end of the formal parameter list. In order to find out if + there was a trailing ellipsis or not, we must instead look at the type + associated with the FUNCTION_DECL. This will be a node of type + FUNCTION_TYPE. If the chain of type nodes hanging off of this + FUNCTION_TYPE node ends with a void_type_node then there should *not* be + an ellipsis at the end. */ + push_decl_scope (decl); + + /* In the case where we are describing a mere function declaration, all we + need to do here (and all we *can* do here) is to describe the *types* of + its formal parameters. */ + if (debug_info_level <= DINFO_LEVEL_TERSE) + ; + else if (declaration) + gen_formal_types_die (TREE_TYPE (decl), subr_die); + else + { + /* Generate DIEs to represent all known formal parameters */ + register tree arg_decls = DECL_ARGUMENTS (decl); + register tree parm; + + /* When generating DIEs, generate the unspecified_parameters DIE + instead if we come across the arg "__builtin_va_alist" */ + for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) + if (TREE_CODE (parm) == PARM_DECL) + { + if (DECL_NAME (parm) + && !strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), + "__builtin_va_alist")) + gen_unspecified_parameters_die (parm, subr_die); + else + gen_decl_die (parm, subr_die); + } + + /* Decide whether we need a unspecified_parameters DIE at the end. + There are 2 more cases to do this for: 1) the ansi ... declaration - + this is detectable when the end of the arg list is not a + void_type_node 2) an unprototyped function declaration (not a + definition). This just means that we have no info about the + parameters at all. */ + fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); + if (fn_arg_types != NULL) + { + /* this is the prototyped case, check for ... */ + if (TREE_VALUE (tree_last (fn_arg_types)) != void_type_node) + gen_unspecified_parameters_die (decl, subr_die); + } + else if (DECL_INITIAL (decl) == NULL_TREE) + gen_unspecified_parameters_die (decl, subr_die); + } + + /* Output Dwarf info for all of the stuff within the body of the function + (if it has one - it may be just a declaration). */ + outer_scope = DECL_INITIAL (decl); + + /* Note that here, `outer_scope' is a pointer to the outermost BLOCK + node created to represent 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 and labels get + declared. Curiously, it appears that the front end doesn't actually + put the PARM_DECL nodes for the current function onto the BLOCK_VARS + list for this outer scope. (They are strung off of the DECL_ARGUMENTS + list for the function instead.) The BLOCK_VARS list for the + `outer_scope' does provide us with a list of the LABEL_DECL nodes for + the function however, and we output DWARF info for those in + decls_for_scope. Just within the `outer_scope' there will be a BLOCK + node representing the function's outermost pair of curly braces, and + any blocks used for the base and member initializers of a C++ + constructor function. */ + if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) + { + current_function_has_inlines = 0; + decls_for_scope (outer_scope, subr_die, 0); + +#if 0 && defined (MIPS_DEBUGGING_INFO) + if (current_function_has_inlines) + { + add_AT_flag (subr_die, DW_AT_MIPS_has_inlines, 1); + if (! comp_unit_has_inlines) + { + add_AT_flag (comp_unit_die, DW_AT_MIPS_has_inlines, 1); + comp_unit_has_inlines = 1; + } + } +#endif + } + + pop_decl_scope (); +} + +/* Generate a DIE to represent a declared data object. */ + +static void +gen_variable_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register tree origin = decl_ultimate_origin (decl); + register dw_die_ref var_die = new_die (DW_TAG_variable, context_die); + + dw_die_ref old_die = lookup_decl_die (decl); + int declaration + = (DECL_EXTERNAL (decl) + || current_function_decl != decl_function_context (decl) + || context_die->die_tag == DW_TAG_structure_type + || context_die->die_tag == DW_TAG_union_type); + + if (origin != NULL) + add_abstract_origin_attribute (var_die, origin); + /* Loop unrolling can create multiple blocks that refer to the same + static variable, so we must test for the DW_AT_declaration flag. */ + /* ??? Loop unrolling/reorder_blocks should perhaps be rewritten to + copy decls and set the DECL_ABSTRACT flag on them instead of + sharing them. */ + else if (old_die && TREE_STATIC (decl) + && get_AT_flag (old_die, DW_AT_declaration) == 1) + { + /* ??? This is an instantiation of a C++ class level static. */ + add_AT_die_ref (var_die, DW_AT_specification, old_die); + if (DECL_NAME (decl)) + { + register unsigned file_index + = lookup_filename (DECL_SOURCE_FILE (decl)); + + if (get_AT_unsigned (old_die, DW_AT_decl_file) != file_index) + add_AT_unsigned (var_die, DW_AT_decl_file, file_index); + + if (get_AT_unsigned (old_die, DW_AT_decl_line) + != DECL_SOURCE_LINE (decl)) + + add_AT_unsigned (var_die, DW_AT_decl_line, + DECL_SOURCE_LINE (decl)); + } + } + else + { + add_name_and_src_coords_attributes (var_die, decl); + add_type_attribute (var_die, TREE_TYPE (decl), + TREE_READONLY (decl), + TREE_THIS_VOLATILE (decl), context_die); + + if (TREE_PUBLIC (decl)) + add_AT_flag (var_die, DW_AT_external, 1); + + if (DECL_ARTIFICIAL (decl)) + add_AT_flag (var_die, DW_AT_artificial, 1); + + if (TREE_PROTECTED (decl)) + add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_protected); + + else if (TREE_PRIVATE (decl)) + add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_private); + } + + if (declaration) + add_AT_flag (var_die, DW_AT_declaration, 1); + + if ((declaration && decl_class_context (decl)) || DECL_ABSTRACT (decl)) + equate_decl_number_to_die (decl, var_die); + + if (! declaration && ! DECL_ABSTRACT (decl)) + { + equate_decl_number_to_die (decl, var_die); + add_location_or_const_value_attribute (var_die, decl); + add_pubname (decl, var_die); + } +} + +/* Generate a DIE to represent a label identifier. */ + +static void +gen_label_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register tree origin = decl_ultimate_origin (decl); + register dw_die_ref lbl_die = new_die (DW_TAG_label, context_die); + register rtx insn; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + char label2[MAX_ARTIFICIAL_LABEL_BYTES]; + + if (origin != NULL) + add_abstract_origin_attribute (lbl_die, origin); + else + add_name_and_src_coords_attributes (lbl_die, decl); + + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die (decl, lbl_die); + else + { + insn = DECL_RTL (decl); + if (GET_CODE (insn) == CODE_LABEL) + { + /* When optimization is enabled (via -O) some parts of the compiler + (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which + represent source-level labels which were explicitly declared by + the user. This really shouldn't be happening though, so catch + it if it ever does happen. */ + if (INSN_DELETED_P (insn)) + abort (); + + sprintf (label2, INSN_LABEL_FMT, current_funcdef_number); + ASM_GENERATE_INTERNAL_LABEL (label, label2, + (unsigned) INSN_UID (insn)); + add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); + } + } +} + +/* Generate a DIE for a lexical block. */ + +static void +gen_lexical_block_die (stmt, context_die, depth) + register tree stmt; + register dw_die_ref context_die; + int depth; +{ + register dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die); + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + if (! BLOCK_ABSTRACT (stmt)) + { + ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, + next_block_number); + add_AT_lbl_id (stmt_die, DW_AT_low_pc, label); + ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL, next_block_number); + add_AT_lbl_id (stmt_die, DW_AT_high_pc, label); + } + + push_decl_scope (stmt); + decls_for_scope (stmt, stmt_die, depth); + pop_decl_scope (); +} + +/* Generate a DIE for an inlined subprogram. */ + +static void +gen_inlined_subroutine_die (stmt, context_die, depth) + register tree stmt; + register dw_die_ref context_die; + int depth; +{ + if (! BLOCK_ABSTRACT (stmt)) + { + register dw_die_ref subr_die + = new_die (DW_TAG_inlined_subroutine, context_die); + register tree decl = block_ultimate_origin (stmt); + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + add_abstract_origin_attribute (subr_die, decl); + ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, + next_block_number); + add_AT_lbl_id (subr_die, DW_AT_low_pc, label); + ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL, next_block_number); + add_AT_lbl_id (subr_die, DW_AT_high_pc, label); + push_decl_scope (decl); + decls_for_scope (stmt, subr_die, depth); + pop_decl_scope (); + current_function_has_inlines = 1; + } +} + +/* Generate a DIE for a field in a record, or structure. */ + +static void +gen_field_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register dw_die_ref decl_die = new_die (DW_TAG_member, context_die); + + add_name_and_src_coords_attributes (decl_die, decl); + add_type_attribute (decl_die, member_declared_type (decl), + TREE_READONLY (decl), TREE_THIS_VOLATILE (decl), + context_die); + + /* If this is a bit field... */ + if (DECL_BIT_FIELD_TYPE (decl)) + { + add_byte_size_attribute (decl_die, decl); + add_bit_size_attribute (decl_die, decl); + add_bit_offset_attribute (decl_die, decl); + } + + if (TREE_CODE (DECL_FIELD_CONTEXT (decl)) != UNION_TYPE) + add_data_member_location_attribute (decl_die, decl); + + if (DECL_ARTIFICIAL (decl)) + add_AT_flag (decl_die, DW_AT_artificial, 1); + + if (TREE_PROTECTED (decl)) + add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_protected); + + else if (TREE_PRIVATE (decl)) + add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_private); +} + +#if 0 +/* Don't generate either pointer_type DIEs or reference_type DIEs here. + Use modified_type_die instead. + We keep this code here just in case these types of DIEs may be needed to + represent certain things in other languages (e.g. Pascal) someday. */ +static void +gen_pointer_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref ptr_die + = new_die (DW_TAG_pointer_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, ptr_die); + add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); +} + +/* Don't generate either pointer_type DIEs or reference_type DIEs here. + Use modified_type_die instead. + We keep this code here just in case these types of DIEs may be needed to + represent certain things in other languages (e.g. Pascal) someday. */ +static void +gen_reference_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref ref_die + = new_die (DW_TAG_reference_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, ref_die); + add_type_attribute (ref_die, TREE_TYPE (type), 0, 0, context_die); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); +} +#endif + +/* Generate a DIE for a pointer to a member type. */ +static void +gen_ptr_to_mbr_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref ptr_die + = new_die (DW_TAG_ptr_to_member_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, ptr_die); + add_AT_die_ref (ptr_die, DW_AT_containing_type, + lookup_type_die (TYPE_OFFSET_BASETYPE (type))); + add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); +} + +/* Generate the DIE for the compilation unit. */ + +static void +gen_compile_unit_die (main_input_filename) + register char *main_input_filename; +{ + char producer[250]; + char *wd = getpwd (); + + comp_unit_die = new_die (DW_TAG_compile_unit, NULL); + add_name_attribute (comp_unit_die, main_input_filename); + + if (wd != NULL) + add_AT_string (comp_unit_die, DW_AT_comp_dir, wd); + + sprintf (producer, "%s %s", language_string, version_string); + +#ifdef MIPS_DEBUGGING_INFO + /* The MIPS/SGI compilers place the 'cc' command line options in the producer + string. The SGI debugger looks for -g, -g1, -g2, or -g3; if they do + not appear in the producer string, the debugger reaches the conclusion + that the object file is stripped and has no debugging information. + To get the MIPS/SGI debugger to believe that there is debugging + information in the object file, we add a -g to the producer string. */ + if (debug_info_level > DINFO_LEVEL_TERSE) + strcat (producer, " -g"); +#endif + + add_AT_string (comp_unit_die, DW_AT_producer, producer); + + if (strcmp (language_string, "GNU C++") == 0) + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C_plus_plus); + + else if (strcmp (language_string, "GNU Ada") == 0) + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Ada83); + + else if (strcmp (language_string, "GNU F77") == 0) + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Fortran77); + + else if (strcmp (language_string, "GNU Pascal") == 0) + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_Pascal83); + + else if (flag_traditional) + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C); + + else + add_AT_unsigned (comp_unit_die, DW_AT_language, DW_LANG_C89); + +#if 0 /* unimplemented */ + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + add_AT_unsigned (comp_unit_die, DW_AT_macro_info, 0); +#endif +} + +/* Generate a DIE for a string type. */ + +static void +gen_string_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die + = new_die (DW_TAG_string_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, type_die); + + /* Fudge the string length attribute for now. */ + + /* TODO: add string length info. + string_length_attribute (TYPE_MAX_VALUE (TYPE_DOMAIN (type))); + bound_representation (upper_bound, 0, 'u'); */ +} + +/* Generate the DIE for a base class. */ + +static void +gen_inheritance_die (binfo, context_die) + register tree binfo; + register dw_die_ref context_die; +{ + dw_die_ref die = new_die (DW_TAG_inheritance, context_die); + + add_type_attribute (die, BINFO_TYPE (binfo), 0, 0, context_die); + add_data_member_location_attribute (die, binfo); + + if (TREE_VIA_VIRTUAL (binfo)) + add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); + if (TREE_VIA_PUBLIC (binfo)) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); + else if (TREE_VIA_PROTECTED (binfo)) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected); +} + +/* Generate a DIE for a class member. */ + +static void +gen_member_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register tree member; + + /* If this is not an incomplete type, output descriptions of each of its + members. Note that as we output the DIEs necessary to represent the + members of this record or union type, we will also be trying to output + DIEs to represent the *types* of those members. However the `type' + function (above) will specifically avoid generating type DIEs for member + types *within* the list of member DIEs for this (containing) type execpt + for those types (of members) which are explicitly marked as also being + members of this (containing) type themselves. The g++ front- end can + force any given type to be treated as a member of some other + (containing) type by setting the TYPE_CONTEXT of the given (member) type + to point to the TREE node representing the appropriate (containing) + type. */ + + /* First output info about the base classes. */ + if (TYPE_BINFO (type) && TYPE_BINFO_BASETYPES (type)) + { + register tree bases = TYPE_BINFO_BASETYPES (type); + register int n_bases = TREE_VEC_LENGTH (bases); + register int i; + + for (i = 0; i < n_bases; i++) + gen_inheritance_die (TREE_VEC_ELT (bases, i), context_die); + } + + /* Now output info about the data members and type members. */ + for (member = TYPE_FIELDS (type); member; member = TREE_CHAIN (member)) + gen_decl_die (member, context_die); + + /* Now output info about the function members (if any). */ + for (member = TYPE_METHODS (type); member; member = TREE_CHAIN (member)) + gen_decl_die (member, context_die); +} + +/* Generate a DIE for a structure or union type. */ + +static void +gen_struct_or_union_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register dw_die_ref type_die = lookup_type_die (type); + register dw_die_ref scope_die = 0; + register int nested = 0; + + if (type_die && ! TYPE_SIZE (type)) + return; + + if (TYPE_CONTEXT (type) != NULL_TREE + && TREE_CODE_CLASS (TREE_CODE (TYPE_CONTEXT (type))) == 't') + nested = 1; + + scope_die = scope_die_for (type, context_die); + + if (! type_die || (nested && scope_die == comp_unit_die)) + /* First occurrence of type or toplevel definition of nested class. */ + { + register dw_die_ref old_die = type_die; + + type_die = new_die (TREE_CODE (type) == RECORD_TYPE + ? DW_TAG_structure_type : DW_TAG_union_type, + scope_die); + equate_type_number_to_die (type, type_die); + add_name_attribute (type_die, type_tag (type)); + if (old_die) + add_AT_die_ref (type_die, DW_AT_specification, old_die); + } + else + remove_AT (type_die, DW_AT_declaration); + + /* If we're not in the right context to be defining this type, defer to + avoid tricky recursion. */ + if (TYPE_SIZE (type) && decl_scope_depth > 0 && scope_die == comp_unit_die) + { + add_AT_flag (type_die, DW_AT_declaration, 1); + pend_type (type); + } + /* If this type has been completed, then give it a byte_size attribute and + then give a list of members. */ + else if (TYPE_SIZE (type)) + { + /* Prevent infinite recursion in cases where the type of some member of + this type is expressed in terms of this type itself. */ + TREE_ASM_WRITTEN (type) = 1; + add_byte_size_attribute (type_die, type); + if (TYPE_STUB_DECL (type) != NULL_TREE) + add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); + + /* If the first reference to this type was as the return type of an + inline function, then it may not have a parent. Fix this now. */ + if (type_die->die_parent == NULL) + add_child_die (scope_die, type_die); + + push_decl_scope (type); + gen_member_die (type, type_die); + pop_decl_scope (); + + /* GNU extension: Record what type our vtable lives in. */ + if (TYPE_VFIELD (type)) + { + tree vtype = DECL_FCONTEXT (TYPE_VFIELD (type)); + + gen_type_die (vtype, context_die); + add_AT_die_ref (type_die, DW_AT_containing_type, + lookup_type_die (vtype)); + } + } + else + add_AT_flag (type_die, DW_AT_declaration, 1); +} + +/* Generate a DIE for a subroutine _type_. */ + +static void +gen_subroutine_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + register tree return_type = TREE_TYPE (type); + register dw_die_ref subr_die + = new_die (DW_TAG_subroutine_type, scope_die_for (type, context_die)); + + equate_type_number_to_die (type, subr_die); + add_prototyped_attribute (subr_die, type); + add_type_attribute (subr_die, return_type, 0, 0, context_die); + gen_formal_types_die (type, subr_die); +} + +/* Generate a DIE for a type definition */ + +static void +gen_typedef_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register dw_die_ref type_die; + register tree origin; + + if (TREE_ASM_WRITTEN (decl)) + return; + TREE_ASM_WRITTEN (decl) = 1; + + type_die = new_die (DW_TAG_typedef, scope_die_for (decl, context_die)); + origin = decl_ultimate_origin (decl); + if (origin != NULL) + add_abstract_origin_attribute (type_die, origin); + else + { + register tree type; + add_name_and_src_coords_attributes (type_die, decl); + if (DECL_ORIGINAL_TYPE (decl)) + { + type = DECL_ORIGINAL_TYPE (decl); + equate_type_number_to_die (TREE_TYPE (decl), type_die); + } + else + type = TREE_TYPE (decl); + add_type_attribute (type_die, type, TREE_READONLY (decl), + TREE_THIS_VOLATILE (decl), context_die); + } + + if (DECL_ABSTRACT (decl)) + equate_decl_number_to_die (decl, type_die); +} + +/* Generate a type description DIE. */ + +static void +gen_type_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + if (type == NULL_TREE || type == error_mark_node) + return; + + /* We are going to output a DIE to represent the unqualified version of of + this type (i.e. without any const or volatile qualifiers) so get the + main variant (i.e. the unqualified version) of this type now. */ + type = type_main_variant (type); + + if (TREE_ASM_WRITTEN (type)) + return; + + if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) + { + TREE_ASM_WRITTEN (type) = 1; + gen_decl_die (TYPE_NAME (type), context_die); + return; + } + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + /* We must set TREE_ASM_WRITTEN in case this is a recursive type. This + ensures that the gen_type_die recursion will terminate even if the + type is recursive. Recursive types are possible in Ada. */ + /* ??? We could perhaps do this for all types before the switch + statement. */ + TREE_ASM_WRITTEN (type) = 1; + + /* For these types, all that is required is that we output a DIE (or a + set of DIEs) to represent the "basis" type. */ + gen_type_die (TREE_TYPE (type), context_die); + break; + + case OFFSET_TYPE: + /* This code is used for C++ pointer-to-data-member types. + Output a description of the relevant class type. */ + gen_type_die (TYPE_OFFSET_BASETYPE (type), context_die); + + /* Output a description of the type of the object pointed to. */ + gen_type_die (TREE_TYPE (type), context_die); + + /* Now output a DIE to represent this pointer-to-data-member type + itself. */ + gen_ptr_to_mbr_type_die (type, context_die); + break; + + case SET_TYPE: + gen_type_die (TYPE_DOMAIN (type), context_die); + gen_set_type_die (type, context_die); + break; + + case FILE_TYPE: + gen_type_die (TREE_TYPE (type), context_die); + abort (); /* No way to represent these in Dwarf yet! */ + break; + + case FUNCTION_TYPE: + /* Force out return type (in case it wasn't forced out already). */ + gen_type_die (TREE_TYPE (type), context_die); + gen_subroutine_type_die (type, context_die); + break; + + case METHOD_TYPE: + /* Force out return type (in case it wasn't forced out already). */ + gen_type_die (TREE_TYPE (type), context_die); + gen_subroutine_type_die (type, context_die); + break; + + case ARRAY_TYPE: + if (TYPE_STRING_FLAG (type) && TREE_CODE (TREE_TYPE (type)) == CHAR_TYPE) + { + gen_type_die (TREE_TYPE (type), context_die); + gen_string_type_die (type, context_die); + } + else + gen_array_type_die (type, context_die); + break; + + case ENUMERAL_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + /* If this is a nested type whose containing class hasn't been + written out yet, writing it out will cover this one, too. */ + if (TYPE_CONTEXT (type) + && TREE_CODE_CLASS (TREE_CODE (TYPE_CONTEXT (type))) == 't' + && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) + { + gen_type_die (TYPE_CONTEXT (type), context_die); + + if (TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) + return; + + /* If that failed, attach ourselves to the stub. */ + push_decl_scope (TYPE_CONTEXT (type)); + context_die = lookup_type_die (TYPE_CONTEXT (type)); + } + + if (TREE_CODE (type) == ENUMERAL_TYPE) + gen_enumeration_type_die (type, context_die); + else + gen_struct_or_union_type_die (type, context_die); + + if (TYPE_CONTEXT (type) + && TREE_CODE_CLASS (TREE_CODE (TYPE_CONTEXT (type))) == 't' + && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) + pop_decl_scope (); + + /* Don't set TREE_ASM_WRITTEN on an incomplete struct; we want to fix + it up if it is ever completed. gen_*_type_die will set it for us + when appropriate. */ + return; + + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case COMPLEX_TYPE: + case BOOLEAN_TYPE: + case CHAR_TYPE: + /* No DIEs needed for fundamental types. */ + break; + + case LANG_TYPE: + /* No Dwarf representation currently defined. */ + break; + + default: + abort (); + } + + TREE_ASM_WRITTEN (type) = 1; +} + +/* Generate a DIE for a tagged type instantiation. */ + +static void +gen_tagged_type_instantiation_die (type, context_die) + register tree type; + register dw_die_ref context_die; +{ + if (type == NULL_TREE || type == error_mark_node) + return; + + /* We are going to output a DIE to represent the unqualified version of of + this type (i.e. without any const or volatile qualifiers) so make sure + that we have the main variant (i.e. the unqualified version) of this + type now. */ + assert (type == type_main_variant (type)); + assert (TREE_ASM_WRITTEN (type)); + + switch (TREE_CODE (type)) + { + case ERROR_MARK: + break; + + case ENUMERAL_TYPE: + gen_inlined_enumeration_type_die (type, context_die); + break; + + case RECORD_TYPE: + gen_inlined_structure_type_die (type, context_die); + break; + + case UNION_TYPE: + case QUAL_UNION_TYPE: + gen_inlined_union_type_die (type, context_die); + break; + + default: + abort (); + } +} + +/* Generate a DW_TAG_lexical_block DIE followed by DIEs to represent all of the + things which are local to the given block. */ + +static void +gen_block_die (stmt, context_die, depth) + register tree stmt; + register dw_die_ref context_die; + int depth; +{ + register int must_output_die = 0; + register tree origin; + register tree decl; + register enum tree_code origin_code; + + /* Ignore blocks never really used to make RTL. */ + + if (stmt == NULL_TREE || !TREE_USED (stmt)) + return; + + /* Determine the "ultimate origin" of this block. This block may be an + inlined instance of an inlined instance of inline function, so we have + to trace all of the way back through the origin chain to find out what + sort of node actually served as the original seed for the creation of + the current block. */ + origin = block_ultimate_origin (stmt); + origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK; + + /* Determine if we need to output any Dwarf DIEs at all to represent this + block. */ + if (origin_code == FUNCTION_DECL) + /* The outer scopes for inlinings *must* always be represented. We + generate DW_TAG_inlined_subroutine DIEs for them. (See below.) */ + must_output_die = 1; + else + { + /* In the case where the current block represents an inlining of the + "body block" of an inline function, we must *NOT* output any DIE for + this block because we have already output a DIE to represent the + whole inlined function scope and the "body block" of any function + doesn't really represent a different scope according to ANSI C + rules. So we check here to make sure that this block does not + represent a "body block inlining" before trying to set the + `must_output_die' flag. */ + if (! is_body_block (origin ? origin : stmt)) + { + /* Determine if this block directly contains any "significant" + local declarations which we will need to output DIEs for. */ + if (debug_info_level > DINFO_LEVEL_TERSE) + /* We are not in terse mode so *any* local declaration counts + as being a "significant" one. */ + must_output_die = (BLOCK_VARS (stmt) != NULL); + else + /* We are in terse mode, so only local (nested) function + definitions count as "significant" local declarations. */ + for (decl = BLOCK_VARS (stmt); + decl != NULL; decl = TREE_CHAIN (decl)) + if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_INITIAL (decl)) + { + must_output_die = 1; + break; + } + } + } + + /* It would be a waste of space to generate a Dwarf DW_TAG_lexical_block + DIE for any block which contains no significant local declarations at + all. Rather, in such cases we just call `decls_for_scope' so that any + needed Dwarf info for any sub-blocks will get properly generated. Note + that in terse mode, our definition of what constitutes a "significant" + local declaration gets restricted to include only inlined function + instances and local (nested) function definitions. */ + if (must_output_die) + { + if (origin_code == FUNCTION_DECL) + gen_inlined_subroutine_die (stmt, context_die, depth); + else + gen_lexical_block_die (stmt, context_die, depth); + } + else + decls_for_scope (stmt, context_die, depth); +} + +/* Generate all of the decls declared within a given scope and (recursively) + all of it's sub-blocks. */ + +static void +decls_for_scope (stmt, context_die, depth) + register tree stmt; + register dw_die_ref context_die; + int depth; +{ + register tree decl; + register tree subblocks; + + /* Ignore blocks never really used to make RTL. */ + if (stmt == NULL_TREE || ! TREE_USED (stmt)) + return; + + if (!BLOCK_ABSTRACT (stmt) && depth > 0) + next_block_number++; + + /* Output the DIEs to represent all of the data objects and typedefs + declared directly within this block but not within any nested + sub-blocks. Also, nested function and tag DIEs have been + generated with a parent of NULL; fix that up now. */ + for (decl = BLOCK_VARS (stmt); + decl != NULL; decl = TREE_CHAIN (decl)) + { + register dw_die_ref die; + + if (TREE_CODE (decl) == FUNCTION_DECL) + die = lookup_decl_die (decl); + else if (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl)) + die = lookup_type_die (TREE_TYPE (decl)); + else + die = NULL; + + if (die != NULL && die->die_parent == NULL) + add_child_die (context_die, die); + else + gen_decl_die (decl, context_die); + } + + /* Output the DIEs to represent all sub-blocks (and the items declared + therein) of this block. */ + for (subblocks = BLOCK_SUBBLOCKS (stmt); + subblocks != NULL; + subblocks = BLOCK_CHAIN (subblocks)) + gen_block_die (subblocks, context_die, depth + 1); +} + +/* Is this a typedef we can avoid emitting? */ + +static inline int +is_redundant_typedef (decl) + register tree decl; +{ + if (TYPE_DECL_IS_STUB (decl)) + return 1; + + if (DECL_ARTIFICIAL (decl) + && DECL_CONTEXT (decl) + && is_tagged_type (DECL_CONTEXT (decl)) + && TREE_CODE (TYPE_NAME (DECL_CONTEXT (decl))) == TYPE_DECL + && DECL_NAME (decl) == DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl)))) + /* Also ignore the artificial member typedef for the class name. */ + return 1; + + return 0; +} + +/* Generate Dwarf debug information for a decl described by DECL. */ + +static void +gen_decl_die (decl, context_die) + register tree decl; + register dw_die_ref context_die; +{ + register tree origin; + + /* Make a note of the decl node we are going to be working on. We may need + to give the user the source coordinates of where it appeared in case we + notice (later on) that something about it looks screwy. */ + dwarf_last_decl = decl; + + if (TREE_CODE (decl) == ERROR_MARK) + return; + + /* If this ..._DECL node is marked to be ignored, then ignore it. But don't + ignore a function definition, since that would screw up our count of + blocks, and that it turn will completely screw up the the labels we will + reference in subsequent DW_AT_low_pc and DW_AT_high_pc attributes (for + subsequent blocks). */ + if (DECL_IGNORED_P (decl) && TREE_CODE (decl) != FUNCTION_DECL) + return; + + switch (TREE_CODE (decl)) + { + case CONST_DECL: + /* The individual enumerators of an enum type get output when we output + the Dwarf representation of the relevant enum type itself. */ + break; + + case FUNCTION_DECL: + /* Don't output any DIEs to represent mere function declarations, + unless they are class members or explicit block externs. */ + if (DECL_INITIAL (decl) == NULL_TREE && DECL_CONTEXT (decl) == NULL_TREE + && (current_function_decl == NULL_TREE || ! DECL_ARTIFICIAL (decl))) + break; + + if (debug_info_level > DINFO_LEVEL_TERSE) + { + /* Before we describe the FUNCTION_DECL itself, make sure that we + have described its return type. */ + gen_type_die (TREE_TYPE (TREE_TYPE (decl)), context_die); + + /* And its containing type. */ + origin = decl_class_context (decl); + if (origin != NULL_TREE) + gen_type_die (origin, context_die); + + /* And its virtual context. */ + if (DECL_VINDEX (decl) != NULL_TREE) + gen_type_die (DECL_CONTEXT (decl), context_die); + } + + /* Now output a DIE to represent the function itself. */ + gen_subprogram_die (decl, context_die); + break; + + case TYPE_DECL: + /* If we are in terse mode, don't generate any DIEs to represent any + actual typedefs. */ + if (debug_info_level <= DINFO_LEVEL_TERSE) + break; + + /* In the special case of a TYPE_DECL node representing the + declaration of some type tag, if the given TYPE_DECL is marked as + having been instantiated from some other (original) TYPE_DECL node + (e.g. one which was generated within the original definition of an + inline function) we have to generate a special (abbreviated) + DW_TAG_structure_type, DW_TAG_union_type, or DW_TAG_enumeration_type + DIE here. */ + if (TYPE_DECL_IS_STUB (decl) && DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE) + { + gen_tagged_type_instantiation_die (TREE_TYPE (decl), context_die); + break; + } + + if (is_redundant_typedef (decl)) + gen_type_die (TREE_TYPE (decl), context_die); + else + /* Output a DIE to represent the typedef itself. */ + gen_typedef_die (decl, context_die); + break; + + case LABEL_DECL: + if (debug_info_level >= DINFO_LEVEL_NORMAL) + gen_label_die (decl, context_die); + break; + + case VAR_DECL: + /* If we are in terse mode, don't generate any DIEs to represent any + variable declarations or definitions. */ + if (debug_info_level <= DINFO_LEVEL_TERSE) + break; + + /* Output any DIEs that are needed to specify the type of this data + object. */ + gen_type_die (TREE_TYPE (decl), context_die); + + /* And its containing type. */ + origin = decl_class_context (decl); + if (origin != NULL_TREE) + gen_type_die (origin, context_die); + + /* Now output the DIE to represent the data object itself. This gets + complicated because of the possibility that the VAR_DECL really + represents an inlined instance of a formal parameter for an inline + function. */ + origin = decl_ultimate_origin (decl); + if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL) + gen_formal_parameter_die (decl, context_die); + else + gen_variable_die (decl, context_die); + break; + + case FIELD_DECL: + /* Ignore the nameless fields that are used to skip bits, but + handle C++ anonymous unions. */ + if (DECL_NAME (decl) != NULL_TREE + || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE) + { + gen_type_die (member_declared_type (decl), context_die); + gen_field_die (decl, context_die); + } + break; + + case PARM_DECL: + gen_type_die (TREE_TYPE (decl), context_die); + gen_formal_parameter_die (decl, context_die); + break; + + default: + abort (); + } +} + +/* Write the debugging output for DECL. */ + +void +dwarf2out_decl (decl) + register tree decl; +{ + register dw_die_ref context_die = comp_unit_die; + + if (TREE_CODE (decl) == ERROR_MARK) + return; + + /* If this ..._DECL node is marked to be ignored, then ignore it. We gotta + hope that the node in question doesn't represent a function definition. + If it does, then totally ignoring it is bound to screw up our count of + blocks, and that it turn will completely screw up the the labels we will + reference in subsequent DW_AT_low_pc and DW_AT_high_pc attributes (for + subsequent blocks). (It's too bad that BLOCK nodes don't carry their + own sequence numbers with them!) */ + if (DECL_IGNORED_P (decl)) + { + if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_INITIAL (decl) != NULL) + abort (); + + return; + } + + switch (TREE_CODE (decl)) + { + case FUNCTION_DECL: + /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of a + builtin function. Explicit programmer-supplied declarations of + these same functions should NOT be ignored however. */ + if (DECL_EXTERNAL (decl) && DECL_FUNCTION_CODE (decl)) + return; + + /* What we would really like to do here is to filter out all mere + file-scope declarations of file-scope functions which are never + referenced later within this translation unit (and keep all of ones + that *are* referenced later on) but we aren't clairvoyant, so we have + no idea which functions will be referenced in the future (i.e. later + on within the current translation unit). So here we just ignore all + file-scope function declarations which are not also definitions. If + and when the debugger needs to know something about these functions, + it wil have to hunt around and find the DWARF information associated + with the definition of the function. Note that we can't just check + `DECL_EXTERNAL' to find out which FUNCTION_DECL nodes represent + definitions and which ones represent mere declarations. We have to + check `DECL_INITIAL' instead. That's because the C front-end + supports some weird semantics for "extern inline" function + definitions. These can get inlined within the current translation + unit (an thus, we need to generate DWARF info for their abstract + instances so that the DWARF info for the concrete inlined instances + can have something to refer to) but the compiler never generates any + out-of-lines instances of such things (despite the fact that they + *are* definitions). The important point is that the C front-end + marks these "extern inline" functions as DECL_EXTERNAL, but we need + to generate DWARF for them anyway. Note that the C++ front-end also + plays some similar games for inline function definitions appearing + within include files which also contain + `#pragma interface' pragmas. */ + if (DECL_INITIAL (decl) == NULL_TREE) + return; + + /* If we're a nested function, initially use a parent of NULL; if we're + a plain function, this will be fixed up in decls_for_scope. If + we're a method, it will be ignored, since we already have a DIE. */ + if (decl_function_context (decl)) + context_die = NULL; + + break; + + case VAR_DECL: + /* Ignore this VAR_DECL if it refers to a file-scope extern data object + declaration and if the declaration was never even referenced from + within this entire compilation unit. We suppress these DIEs in + order to save space in the .debug section (by eliminating entries + which are probably useless). Note that we must not suppress + block-local extern declarations (whether used or not) because that + would screw-up the debugger's name lookup mechanism and cause it to + miss things which really ought to be in scope at a given point. */ + if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) + return; + + /* If we are in terse mode, don't generate any DIEs to represent any + variable declarations or definitions. */ + if (debug_info_level <= DINFO_LEVEL_TERSE) + return; + break; + + case TYPE_DECL: + /* Don't bother trying to generate any DIEs to represent any of the + normal built-in types for the language we are compiling. */ + if (DECL_SOURCE_LINE (decl) == 0) + { + /* OK, we need to generate one for `bool' so GDB knows what type + comparisons have. */ + if ((get_AT_unsigned (comp_unit_die, DW_AT_language) + == DW_LANG_C_plus_plus) + && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE) + modified_type_die (TREE_TYPE (decl), 0, 0, NULL); + + return; + } + + /* If we are in terse mode, don't generate any DIEs for types. */ + if (debug_info_level <= DINFO_LEVEL_TERSE) + return; + + /* If we're a function-scope tag, initially use a parent of NULL; + this will be fixed up in decls_for_scope. */ + if (decl_function_context (decl)) + context_die = NULL; + + break; + + default: + return; + } + + gen_decl_die (decl, context_die); + output_pending_types_for_scope (comp_unit_die); +} + +/* Output a marker (i.e. a label) for the beginning of the generated code for + a lexical block. */ + +void +dwarf2out_begin_block (blocknum) + register unsigned blocknum; +{ + function_section (current_function_decl); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum); +} + +/* Output a marker (i.e. a label) for the end of the generated code for a + lexical block. */ + +void +dwarf2out_end_block (blocknum) + register unsigned blocknum; +{ + function_section (current_function_decl); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BLOCK_END_LABEL, blocknum); +} + +/* Output a marker (i.e. a label) at a point in the assembly code which + corresponds to a given source level label. */ + +void +dwarf2out_label (insn) + register rtx insn; +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + function_section (current_function_decl); + sprintf (label, INSN_LABEL_FMT, current_funcdef_number); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, label, + (unsigned) INSN_UID (insn)); + } +} + +/* Lookup a filename (in the list of filenames that we know about here in + dwarf2out.c) and return its "index". The index of each (known) filename is + just a unique number which is associated with only that one filename. + We need such numbers for the sake of generating labels + (in the .debug_sfnames section) and references to those + files numbers (in the .debug_srcinfo and.debug_macinfo sections). + If the filename given as an argument is not found in our current list, + add it to the list and assign it the next available unique index number. + In order to speed up searches, we remember the index of the filename + was looked up last. This handles the majority of all searches. */ + +static unsigned +lookup_filename (file_name) + char *file_name; +{ + static unsigned last_file_lookup_index = 0; + register unsigned i; + + /* Check to see if the file name that was searched on the previous call + matches this file name. If so, return the index. */ + if (last_file_lookup_index != 0) + if (strcmp (file_name, file_table[last_file_lookup_index]) == 0) + return last_file_lookup_index; + + /* Didn't match the previous lookup, search the table */ + for (i = 1; i < file_table_in_use; ++i) + if (strcmp (file_name, file_table[i]) == 0) + { + last_file_lookup_index = i; + return i; + } + + /* 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. */ + if (file_table_in_use == file_table_allocated) + { + file_table_allocated += FILE_TABLE_INCREMENT; + file_table + = (char **) xrealloc (file_table, + file_table_allocated * sizeof (char *)); + } + + /* Add the new entry to the end of the filename table. */ + file_table[file_table_in_use] = xstrdup (file_name); + last_file_lookup_index = file_table_in_use++; + + return last_file_lookup_index; +} + +/* Output a label to mark the beginning of a source code line entry + and record information relating to this source line, in + 'line_info_table' for later output of the .debug_line section. */ + +void +dwarf2out_line (filename, line) + register char *filename; + register unsigned line; +{ + if (debug_info_level >= DINFO_LEVEL_NORMAL) + { + function_section (current_function_decl); + + if (DECL_SECTION_NAME (current_function_decl)) + { + register dw_separate_line_info_ref line_info; + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, SEPARATE_LINE_CODE_LABEL, + separate_line_info_table_in_use); + fputc ('\n', asm_out_file); + + /* expand the line info table if necessary */ + if (separate_line_info_table_in_use + == separate_line_info_table_allocated) + { + separate_line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; + separate_line_info_table + = (dw_separate_line_info_ref) + xrealloc (separate_line_info_table, + separate_line_info_table_allocated + * sizeof (dw_separate_line_info_entry)); + } + + /* Add the new entry at the end of the line_info_table. */ + line_info + = &separate_line_info_table[separate_line_info_table_in_use++]; + line_info->dw_file_num = lookup_filename (filename); + line_info->dw_line_num = line; + line_info->function = current_funcdef_number; + } + else + { + register dw_line_info_ref line_info; + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, LINE_CODE_LABEL, + line_info_table_in_use); + fputc ('\n', asm_out_file); + + /* Expand the line info table if necessary. */ + if (line_info_table_in_use == line_info_table_allocated) + { + line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; + line_info_table + = (dw_line_info_ref) + xrealloc (line_info_table, + (line_info_table_allocated + * sizeof (dw_line_info_entry))); + } + + /* Add the new entry at the end of the line_info_table. */ + line_info = &line_info_table[line_info_table_in_use++]; + line_info->dw_file_num = lookup_filename (filename); + line_info->dw_line_num = line; + } + } +} + +/* Record the beginning of a new source file, for later output + of the .debug_macinfo section. At present, unimplemented. */ + +void +dwarf2out_start_source_file (filename) + register char *filename; +{ +} + +/* Record the end of a source file, for later output + of the .debug_macinfo section. At present, unimplemented. */ + +void +dwarf2out_end_source_file () +{ +} + +/* Called from check_newline in c-parse.y. The `buffer' parameter contains + the tail part of the directive line, i.e. the part which is past the + initial whitespace, #, whitespace, directive-name, whitespace part. */ + +void +dwarf2out_define (lineno, buffer) + register unsigned lineno; + register char *buffer; +{ + static int initialized = 0; + if (!initialized) + { + dwarf2out_start_source_file (primary_filename); + initialized = 1; + } +} + +/* Called from check_newline in c-parse.y. The `buffer' parameter contains + the tail part of the directive line, i.e. the part which is past the + initial whitespace, #, whitespace, directive-name, whitespace part. */ + +void +dwarf2out_undef (lineno, buffer) + register unsigned lineno; + register char *buffer; +{ +} + +/* Set up for Dwarf output at the start of compilation. */ + +void +dwarf2out_init (asm_out_file, main_input_filename) + register FILE *asm_out_file; + register char *main_input_filename; +{ + /* Remember the name of the primary input file. */ + primary_filename = main_input_filename; + + /* Allocate the initial hunk of the file_table. */ + file_table = (char **) xmalloc (FILE_TABLE_INCREMENT * sizeof (char *)); + bzero ((char *) file_table, FILE_TABLE_INCREMENT * sizeof (char *)); + file_table_allocated = FILE_TABLE_INCREMENT; + + /* Skip the first entry - file numbers begin at 1. */ + file_table_in_use = 1; + + /* Allocate the initial hunk of the decl_die_table. */ + decl_die_table + = (dw_die_ref *) xmalloc (DECL_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); + bzero ((char *) decl_die_table, + DECL_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); + decl_die_table_allocated = DECL_DIE_TABLE_INCREMENT; + decl_die_table_in_use = 0; + + /* Allocate the initial hunk of the decl_scope_table. */ + decl_scope_table + = (tree *) xmalloc (DECL_SCOPE_TABLE_INCREMENT * sizeof (tree)); + bzero ((char *) decl_scope_table, + DECL_SCOPE_TABLE_INCREMENT * sizeof (tree)); + decl_scope_table_allocated = DECL_SCOPE_TABLE_INCREMENT; + decl_scope_depth = 0; + + /* Allocate the initial hunk of the abbrev_die_table. */ + abbrev_die_table + = (dw_die_ref *) xmalloc (ABBREV_DIE_TABLE_INCREMENT + * sizeof (dw_die_ref)); + bzero ((char *) abbrev_die_table, + ABBREV_DIE_TABLE_INCREMENT * sizeof (dw_die_ref)); + abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT; + /* Zero-th entry is allocated, but unused */ + abbrev_die_table_in_use = 1; + + /* Allocate the initial hunk of the line_info_table. */ + line_info_table + = (dw_line_info_ref) xmalloc (LINE_INFO_TABLE_INCREMENT + * sizeof (dw_line_info_entry)); + bzero ((char *) line_info_table, + LINE_INFO_TABLE_INCREMENT * sizeof (dw_line_info_entry)); + line_info_table_allocated = LINE_INFO_TABLE_INCREMENT; + /* Zero-th entry is allocated, but unused */ + line_info_table_in_use = 1; + + /* Generate the initial DIE for the .debug section. Note that the (string) + value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE + will (typically) be a relative pathname and that this pathname should be + taken as being relative to the directory from which the compiler was + invoked when the given (base) source file was compiled. */ + gen_compile_unit_die (main_input_filename); + + ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0); +} + +/* Output stuff that dwarf requires at the end of every file, + and generate the DWARF-2 debugging info. */ + +void +dwarf2out_finish () +{ + limbo_die_node *node, *next_node; + dw_die_ref die; + dw_attr_ref a; + + /* Traverse the limbo die list, and add parent/child links. The only + dies without parents that should be here are concrete instances of + inline functions, and the comp_unit_die. We can ignore the comp_unit_die. + For concrete instances, we can get the parent die from the abstract + instance. */ + for (node = limbo_die_list; node; node = next_node) + { + next_node = node->next; + die = node->die; + + if (die->die_parent == NULL) + { + a = get_AT (die, DW_AT_abstract_origin); + if (a) + add_child_die (a->dw_attr_val.v.val_die_ref->die_parent, die); + else if (die == comp_unit_die) + ; + else + abort (); + } + free (node); + } + + /* Traverse the DIE tree and add sibling attributes to those DIE's + that have children. */ + add_sibling_attributes (comp_unit_die); + + /* Output a terminator label for the .text section. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, TEXT_SECTION); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, TEXT_END_LABEL, 0); + +#if 0 + /* Output a terminator label for the .data section. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, DATA_SECTION); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, DATA_END_LABEL, 0); + + /* Output a terminator label for the .bss section. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, BSS_SECTION); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, BSS_END_LABEL, 0); +#endif + + /* Output the source line correspondence table. */ + if (line_info_table_in_use > 1 || separate_line_info_table_in_use) + { + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, DEBUG_LINE_SECTION); + output_line_info (); + + /* We can only use the low/high_pc attributes if all of the code + was in .text. */ + if (separate_line_info_table_in_use == 0) + { + add_AT_lbl_id (comp_unit_die, DW_AT_low_pc, TEXT_SECTION); + add_AT_lbl_id (comp_unit_die, DW_AT_high_pc, text_end_label); + } + + add_AT_section_offset (comp_unit_die, DW_AT_stmt_list, DEBUG_LINE_SECTION); + } + + /* Output the abbreviation table. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, ABBREV_SECTION); + build_abbrev_table (comp_unit_die); + output_abbrev_section (); + + /* Initialize the beginning DIE offset - and calculate sizes/offsets. */ + next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE; + calc_die_sizes (comp_unit_die); + + /* Output debugging information. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, DEBUG_INFO_SECTION); + output_compilation_unit_header (); + output_die (comp_unit_die); + + if (pubname_table_in_use) + { + /* Output public names table. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, PUBNAMES_SECTION); + output_pubnames (); + } + + if (fde_table_in_use) + { + /* Output the address range information. */ + fputc ('\n', asm_out_file); + ASM_OUTPUT_SECTION (asm_out_file, ARANGES_SECTION); + output_aranges (); + } +} +#endif /* DWARF2_DEBUGGING_INFO */ |