| /* 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 the |
| DWARF 1 implementation written by 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" |
| |
| /* 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 unecessary. */ |
| |
| #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_OFFSET4 |
| #define ASM_OUTPUT_DWARF_OFFSET4(FILE,LABEL) \ |
| do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \ |
| assemble_name (FILE, LABEL); \ |
| } while (0) |
| #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 |
| |
| #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_OFFSET4(FILE,LABEL) \ |
| assemble_integer (gen_rtx (SYMBOL_REF, SImode, LABEL), 4, 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) |
| |
| #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); \ |
| fputc (',', FILE); \ |
| assemble_name (FILE, HI); \ |
| fputc ('-', FILE); \ |
| assemble_name (FILE, LO); \ |
| } 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; |
| if (n_ranges >= 5) |
| abort (); |
| } |
| 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) |
| { |
| if ((DWARF_FRAME_REGNUM (ranges[1].end) |
| - DWARF_FRAME_REGNUM (ranges[1].beg)) |
| != ranges[1].end - ranges[1].beg) |
| abort (); |
| 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--; ) |
| { |
| if ((DWARF_FRAME_REGNUM (ranges[n_ranges].end) |
| - DWARF_FRAME_REGNUM (ranges[n_ranges].beg)) |
| != ranges[n_ranges].end - ranges[n_ranges].beg) |
| abort (); |
| if (DWARF_FRAME_REGNUM (ranges[n_ranges].beg) >= size) |
| abort (); |
| 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; |
| } |
| } |
| |
| /* 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; |
| if (offset < 0) |
| abort (); |
| 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: |
| if (REGNO (rtl) != STACK_POINTER_REGNUM) |
| abort (); |
| offset = 0; |
| break; |
| case PLUS: |
| if (REGNO (XEXP (rtl, 0)) != STACK_POINTER_REGNUM) |
| abort (); |
| offset = INTVAL (XEXP (rtl, 1)); |
| break; |
| case MINUS: |
| if (REGNO (XEXP (rtl, 0)) != STACK_POINTER_REGNUM) |
| abort (); |
| 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. */ |
| if (GET_CODE (XEXP (rtl, 1)) != CONST_INT) |
| abort (); |
| 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); |
| if (cfa_reg != DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)) |
| abort (); |
| 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); |
| if (GET_CODE (insn) != SET) |
| abort (); |
| |
| 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: |
| if (cfa_reg != REGNO (src)) |
| abort (); |
| if (REGNO (dest) != STACK_POINTER_REGNUM |
| && !(frame_pointer_needed |
| && REGNO (dest) == HARD_FRAME_POINTER_REGNUM)) |
| abort (); |
| 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: |
| if (REGNO (XEXP (src, 1)) != cfa_temp_reg) |
| abort (); |
| offset = cfa_temp_value; |
| break; |
| default: |
| abort (); |
| } |
| |
| if (XEXP (src, 0) == hard_frame_pointer_rtx) |
| { |
| /* Restoring SP from FP in the epilogue. */ |
| if (cfa_reg != HARD_FRAME_POINTER_REGNUM) |
| abort (); |
| cfa_reg = STACK_POINTER_REGNUM; |
| } |
| else if (XEXP (src, 0) != stack_pointer_rtx) |
| abort (); |
| |
| 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. */ |
| if (GET_CODE (src) != PLUS |
| || XEXP (src, 1) != stack_pointer_rtx |
| || cfa_store_reg != STACK_POINTER_REGNUM) |
| abort (); |
| if (GET_CODE (XEXP (src, 0)) != REG |
| || REGNO (XEXP (src, 0)) != cfa_temp_reg) |
| abort (); |
| if (cfa_reg != STACK_POINTER_REGNUM) |
| abort (); |
| 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: |
| if (GET_CODE (XEXP (src, 0)) != REG |
| || REGNO (XEXP (src, 0)) != cfa_temp_reg |
| || REGNO (dest) != cfa_temp_reg |
| || GET_CODE (XEXP (src, 1)) != CONST_INT) |
| abort (); |
| 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. */ |
| if (GET_CODE (src) != REG) |
| abort (); |
| 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; |
| |
| if (REGNO (XEXP (XEXP (dest, 0), 0)) != STACK_POINTER_REGNUM |
| || cfa_store_reg != STACK_POINTER_REGNUM) |
| abort (); |
| 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; |
| |
| if (cfa_store_reg != REGNO (XEXP (XEXP (dest, 0), 0))) |
| abort (); |
| 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 (); |
| |
| /* Only output the info if it will be interesting. */ |
| for (i = 0; i < fde_table_in_use; ++i) |
| if (fde_table[i].dw_fde_cfi != NULL) |
| break; |
| if (i == fde_table_in_use) |
| return; |
| |
| 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_OFFSET4 (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); |
| if (flag_debug_asm) |
| fprintf (asm_out_file, "\t%s CIE Length Symbol", ASM_COMMENT_START); |
| fputc ('\n', asm_out_file); |
| #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_OFFSET4 (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); |
| if (flag_debug_asm) |
| fprintf (asm_out_file, "\t%s FDE Length Symbol", ASM_COMMENT_START); |
| fputc ('\n', asm_out_file); |
| #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 nuber 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 attibute'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, indentifying 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 declataion 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_setion_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_atttributes 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 |
| |
| /* Standerd 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 psuedo 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"; |
|