| /* Output Dwarf2 format symbol table information from GCC. |
| Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, |
| 2003, 2004 Free Software Foundation, Inc. |
| Contributed by Gary Funck (gary@intrepid.com). |
| Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com). |
| Extensively modified by Jason Merrill (jason@cygnus.com). |
| |
| This file is part of GCC. |
| |
| GCC 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. |
| |
| GCC 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 GCC; see the file COPYING. If not, write to the Free |
| Software Foundation, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| /* TODO: Emit .debug_line header even when there are no functions, since |
| the file numbers are used by .debug_info. Alternately, leave |
| out locations for types and decls. |
| Avoid talking about ctors and op= for PODs. |
| Factor out common prologue sequences into multiple CIEs. */ |
| |
| /* 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 "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tree.h" |
| #include "flags.h" |
| #include "real.h" |
| #include "rtl.h" |
| #include "hard-reg-set.h" |
| #include "regs.h" |
| #include "insn-config.h" |
| #include "reload.h" |
| #include "function.h" |
| #include "output.h" |
| #include "expr.h" |
| #include "libfuncs.h" |
| #include "except.h" |
| #include "dwarf2.h" |
| #include "dwarf2out.h" |
| #include "dwarf2asm.h" |
| #include "toplev.h" |
| #include "varray.h" |
| #include "ggc.h" |
| #include "md5.h" |
| #include "tm_p.h" |
| #include "diagnostic.h" |
| #include "debug.h" |
| #include "target.h" |
| #include "langhooks.h" |
| #include "hashtab.h" |
| #include "cgraph.h" |
| |
| #ifdef DWARF2_DEBUGGING_INFO |
| static void dwarf2out_source_line (unsigned int, const char *); |
| #endif |
| |
| /* DWARF2 Abbreviation Glossary: |
| CFA = Canonical Frame Address |
| a fixed address on the stack which identifies a call frame. |
| We define it to be the value of SP just before the call insn. |
| The CFA register and offset, which may change during the course |
| of the function, are used to calculate its value at runtime. |
| CFI = Call Frame Instruction |
| an instruction for the DWARF2 abstract machine |
| CIE = Common Information Entry |
| information describing information common to one or more FDEs |
| DIE = Debugging Information Entry |
| FDE = Frame Description Entry |
| information describing the stack call frame, in particular, |
| how to restore registers |
| |
| DW_CFA_... = DWARF2 CFA call frame instruction |
| DW_TAG_... = DWARF2 DIE tag */ |
| |
| /* Decide whether we want to emit frame unwind information for the current |
| translation unit. */ |
| |
| int |
| dwarf2out_do_frame (void) |
| { |
| return (write_symbols == DWARF2_DEBUG |
| || write_symbols == VMS_AND_DWARF2_DEBUG |
| #ifdef DWARF2_FRAME_INFO |
| || DWARF2_FRAME_INFO |
| #endif |
| #ifdef DWARF2_UNWIND_INFO |
| || flag_unwind_tables |
| || (flag_exceptions && ! USING_SJLJ_EXCEPTIONS) |
| #endif |
| ); |
| } |
| |
| /* The size of the target's pointer type. */ |
| #ifndef PTR_SIZE |
| #define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT) |
| #endif |
| |
| /* Various versions of targetm.eh_frame_section. Note these must appear |
| outside the DWARF2_DEBUGGING_INFO || DWARF2_UNWIND_INFO macro guards. */ |
| |
| /* Version of targetm.eh_frame_section for systems with named sections. */ |
| void |
| named_section_eh_frame_section (void) |
| { |
| #ifdef EH_FRAME_SECTION_NAME |
| #ifdef HAVE_LD_RO_RW_SECTION_MIXING |
| int fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0); |
| int per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); |
| int lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); |
| int flags; |
| |
| flags = (! flag_pic |
| || ((fde_encoding & 0x70) != DW_EH_PE_absptr |
| && (fde_encoding & 0x70) != DW_EH_PE_aligned |
| && (per_encoding & 0x70) != DW_EH_PE_absptr |
| && (per_encoding & 0x70) != DW_EH_PE_aligned |
| && (lsda_encoding & 0x70) != DW_EH_PE_absptr |
| && (lsda_encoding & 0x70) != DW_EH_PE_aligned)) |
| ? 0 : SECTION_WRITE; |
| named_section_flags (EH_FRAME_SECTION_NAME, flags); |
| #else |
| named_section_flags (EH_FRAME_SECTION_NAME, SECTION_WRITE); |
| #endif |
| #endif |
| } |
| |
| /* Version of targetm.eh_frame_section for systems using collect2. */ |
| void |
| collect2_eh_frame_section (void) |
| { |
| tree label = get_file_function_name ('F'); |
| |
| data_section (); |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); |
| (*targetm.asm_out.globalize_label) (asm_out_file, IDENTIFIER_POINTER (label)); |
| ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); |
| } |
| |
| /* Default version of targetm.eh_frame_section. */ |
| void |
| default_eh_frame_section (void) |
| { |
| #ifdef EH_FRAME_SECTION_NAME |
| named_section_eh_frame_section (); |
| #else |
| collect2_eh_frame_section (); |
| #endif |
| } |
| |
| /* Array of RTXes referenced by the debugging information, which therefore |
| must be kept around forever. */ |
| static GTY(()) varray_type used_rtx_varray; |
| |
| /* A pointer to the base of a list of incomplete types which might be |
| completed at some later time. incomplete_types_list needs to be a VARRAY |
| because we want to tell the garbage collector about it. */ |
| static GTY(()) varray_type incomplete_types; |
| |
| /* 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 GTY(()) varray_type decl_scope_table; |
| |
| /* 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. */ |
| |
| enum dw_cfi_oprnd_type { |
| dw_cfi_oprnd_unused, |
| dw_cfi_oprnd_reg_num, |
| dw_cfi_oprnd_offset, |
| dw_cfi_oprnd_addr, |
| dw_cfi_oprnd_loc |
| }; |
| |
| typedef union dw_cfi_oprnd_struct GTY(()) |
| { |
| unsigned long GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num; |
| HOST_WIDE_INT GTY ((tag ("dw_cfi_oprnd_offset"))) dw_cfi_offset; |
| const char * GTY ((tag ("dw_cfi_oprnd_addr"))) dw_cfi_addr; |
| struct dw_loc_descr_struct * GTY ((tag ("dw_cfi_oprnd_loc"))) dw_cfi_loc; |
| } |
| dw_cfi_oprnd; |
| |
| typedef struct dw_cfi_struct GTY(()) |
| { |
| dw_cfi_ref dw_cfi_next; |
| enum dwarf_call_frame_info dw_cfi_opc; |
| dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd1_desc (%1.dw_cfi_opc)"))) |
| dw_cfi_oprnd1; |
| dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd2_desc (%1.dw_cfi_opc)"))) |
| dw_cfi_oprnd2; |
| } |
| dw_cfi_node; |
| |
| /* This is how we define the location of the CFA. We use to handle it |
| as REG + OFFSET all the time, but now it can be more complex. |
| It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET. |
| Instead of passing around REG and OFFSET, we pass a copy |
| of this structure. */ |
| typedef struct cfa_loc GTY(()) |
| { |
| unsigned long reg; |
| HOST_WIDE_INT offset; |
| HOST_WIDE_INT base_offset; |
| int indirect; /* 1 if CFA is accessed via a dereference. */ |
| } dw_cfa_location; |
| |
| /* 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 use 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 GTY(()) |
| { |
| const char *dw_fde_begin; |
| const char *dw_fde_current_label; |
| const char *dw_fde_end; |
| dw_cfi_ref dw_fde_cfi; |
| unsigned funcdef_number; |
| unsigned all_throwers_are_sibcalls : 1; |
| unsigned nothrow : 1; |
| unsigned uses_eh_lsda : 1; |
| } |
| dw_fde_node; |
| |
| /* Maximum size (in bytes) of an artificially generated label. */ |
| #define MAX_ARTIFICIAL_LABEL_BYTES 30 |
| |
| /* The size of addresses as they appear in the Dwarf 2 data. |
| Some architectures use word addresses to refer to code locations, |
| but Dwarf 2 info always uses byte addresses. On such machines, |
| Dwarf 2 addresses need to be larger than the architecture's |
| pointers. */ |
| #ifndef DWARF2_ADDR_SIZE |
| #define DWARF2_ADDR_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 |
| |
| /* According to the (draft) DWARF 3 specification, the initial length |
| should either be 4 or 12 bytes. When it's 12 bytes, the first 4 |
| bytes are 0xffffffff, followed by the length stored in the next 8 |
| bytes. |
| |
| However, the SGI/MIPS ABI uses an initial length which is equal to |
| DWARF_OFFSET_SIZE. It is defined (elsewhere) accordingly. */ |
| |
| #ifndef DWARF_INITIAL_LENGTH_SIZE |
| #define DWARF_INITIAL_LENGTH_SIZE (DWARF_OFFSET_SIZE == 4 ? 4 : 12) |
| #endif |
| |
| #define DWARF_VERSION 2 |
| |
| /* Round SIZE up to the nearest BOUNDARY. */ |
| #define DWARF_ROUND(SIZE,BOUNDARY) \ |
| ((((SIZE) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY)) |
| |
| /* Offsets recorded in opcodes are a multiple of this alignment factor. */ |
| #ifndef DWARF_CIE_DATA_ALIGNMENT |
| #ifdef STACK_GROWS_DOWNWARD |
| #define DWARF_CIE_DATA_ALIGNMENT (-((int) UNITS_PER_WORD)) |
| #else |
| #define DWARF_CIE_DATA_ALIGNMENT ((int) UNITS_PER_WORD) |
| #endif |
| #endif |
| |
| /* A pointer to the base of a table that contains frame description |
| information for each routine. */ |
| static GTY((length ("fde_table_allocated"))) dw_fde_ref fde_table; |
| |
| /* Number of elements currently allocated for fde_table. */ |
| static GTY(()) unsigned fde_table_allocated; |
| |
| /* Number of elements in fde_table currently in use. */ |
| static GTY(()) 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 GTY(()) dw_cfi_ref cie_cfi_head; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| /* 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; |
| #endif |
| |
| struct indirect_string_node GTY(()) |
| { |
| const char *str; |
| unsigned int refcount; |
| unsigned int form; |
| char *label; |
| }; |
| |
| static GTY ((param_is (struct indirect_string_node))) htab_t debug_str_hash; |
| |
| static GTY(()) int dw2_string_counter; |
| static GTY(()) unsigned long dwarf2out_cfi_label_num; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| |
| /* Forward declarations for functions defined in this file. */ |
| |
| static char *stripattributes (const char *); |
| static const char *dwarf_cfi_name (unsigned); |
| static dw_cfi_ref new_cfi (void); |
| static void add_cfi (dw_cfi_ref *, dw_cfi_ref); |
| static void add_fde_cfi (const char *, dw_cfi_ref); |
| static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *); |
| static void lookup_cfa (dw_cfa_location *); |
| static void reg_save (const char *, unsigned, unsigned, HOST_WIDE_INT); |
| static void initial_return_save (rtx); |
| static HOST_WIDE_INT stack_adjust_offset (rtx); |
| static void output_cfi (dw_cfi_ref, dw_fde_ref, int); |
| static void output_call_frame_info (int); |
| static void dwarf2out_stack_adjust (rtx); |
| static void queue_reg_save (const char *, rtx, HOST_WIDE_INT); |
| static void flush_queued_reg_saves (void); |
| static bool clobbers_queued_reg_save (rtx); |
| static void dwarf2out_frame_debug_expr (rtx, const char *); |
| |
| /* Support for complex CFA locations. */ |
| static void output_cfa_loc (dw_cfi_ref); |
| static void get_cfa_from_loc_descr (dw_cfa_location *, |
| struct dw_loc_descr_struct *); |
| static struct dw_loc_descr_struct *build_cfa_loc |
| (dw_cfa_location *); |
| static void def_cfa_1 (const char *, dw_cfa_location *); |
| |
| /* How to start an assembler comment. */ |
| #ifndef ASM_COMMENT_START |
| #define ASM_COMMENT_START ";#" |
| #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) |
| |
| #ifndef DEBUG_FRAME_SECTION |
| #define DEBUG_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 FRAME_BEGIN_LABEL "Lframe" |
| #define CIE_AFTER_SIZE_LABEL "LSCIE" |
| #define CIE_END_LABEL "LECIE" |
| #define FDE_LABEL "LSFDE" |
| #define FDE_AFTER_SIZE_LABEL "LASFDE" |
| #define FDE_END_LABEL "LEFDE" |
| #define LINE_NUMBER_BEGIN_LABEL "LSLT" |
| #define LINE_NUMBER_END_LABEL "LELT" |
| #define LN_PROLOG_AS_LABEL "LASLTP" |
| #define LN_PROLOG_END_LABEL "LELTP" |
| #define DIE_LABEL_PREFIX "DW" |
| |
| /* 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 DWARF_FRAME_REGISTERS |
| #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 |
| |
| /* 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 |
| |
| /* Hook used by __throw. */ |
| |
| rtx |
| expand_builtin_dwarf_sp_column (void) |
| { |
| return GEN_INT (DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)); |
| } |
| |
| /* Return a pointer to a copy of the section string name S with all |
| attributes stripped off, and an asterisk prepended (for assemble_name). */ |
| |
| static inline char * |
| stripattributes (const char *s) |
| { |
| char *stripped = xmalloc (strlen (s) + 2); |
| char *p = stripped; |
| |
| *p++ = '*'; |
| |
| while (*s && *s != ',') |
| *p++ = *s++; |
| |
| *p = '\0'; |
| return stripped; |
| } |
| |
| /* Generate code to initialize the register size table. */ |
| |
| void |
| expand_builtin_init_dwarf_reg_sizes (tree address) |
| { |
| int i; |
| enum machine_mode mode = TYPE_MODE (char_type_node); |
| rtx addr = expand_expr (address, NULL_RTX, VOIDmode, 0); |
| rtx mem = gen_rtx_MEM (BLKmode, addr); |
| bool wrote_return_column = false; |
| |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| if (DWARF_FRAME_REGNUM (i) < DWARF_FRAME_REGISTERS) |
| { |
| HOST_WIDE_INT offset = DWARF_FRAME_REGNUM (i) * GET_MODE_SIZE (mode); |
| enum machine_mode save_mode = reg_raw_mode[i]; |
| HOST_WIDE_INT size; |
| |
| if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode)) |
| save_mode = choose_hard_reg_mode (i, 1, true); |
| if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN) |
| { |
| if (save_mode == VOIDmode) |
| continue; |
| wrote_return_column = true; |
| } |
| size = GET_MODE_SIZE (save_mode); |
| if (offset < 0) |
| continue; |
| |
| emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size)); |
| } |
| |
| #ifdef DWARF_ALT_FRAME_RETURN_COLUMN |
| if (! wrote_return_column) |
| abort (); |
| i = DWARF_ALT_FRAME_RETURN_COLUMN; |
| wrote_return_column = false; |
| #else |
| i = DWARF_FRAME_RETURN_COLUMN; |
| #endif |
| |
| if (! wrote_return_column) |
| { |
| enum machine_mode save_mode = Pmode; |
| HOST_WIDE_INT offset = i * GET_MODE_SIZE (mode); |
| HOST_WIDE_INT size = GET_MODE_SIZE (save_mode); |
| emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size)); |
| } |
| } |
| |
| /* Convert a DWARF call frame info. operation to its string name */ |
| |
| static const char * |
| dwarf_cfi_name (unsigned int 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"; |
| |
| /* DWARF 3 */ |
| case DW_CFA_def_cfa_expression: |
| return "DW_CFA_def_cfa_expression"; |
| case DW_CFA_expression: |
| return "DW_CFA_expression"; |
| case DW_CFA_offset_extended_sf: |
| return "DW_CFA_offset_extended_sf"; |
| case DW_CFA_def_cfa_sf: |
| return "DW_CFA_def_cfa_sf"; |
| case DW_CFA_def_cfa_offset_sf: |
| return "DW_CFA_def_cfa_offset_sf"; |
| |
| /* 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"; |
| case DW_CFA_GNU_negative_offset_extended: |
| return "DW_CFA_GNU_negative_offset_extended"; |
| |
| default: |
| return "DW_CFA_<unknown>"; |
| } |
| } |
| |
| /* Return a pointer to a newly allocated Call Frame Instruction. */ |
| |
| static inline dw_cfi_ref |
| new_cfi (void) |
| { |
| dw_cfi_ref cfi = ggc_alloc (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 (dw_cfi_ref *list_head, dw_cfi_ref cfi) |
| { |
| 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 (void) |
| { |
| static char label[20]; |
| |
| ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", dwarf2out_cfi_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 (const char *label, dw_cfi_ref cfi) |
| { |
| if (label) |
| { |
| 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) |
| { |
| 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 (dw_cfi_ref cfi, dw_cfa_location *loc) |
| { |
| switch (cfi->dw_cfi_opc) |
| { |
| case DW_CFA_def_cfa_offset: |
| loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset; |
| break; |
| case DW_CFA_def_cfa_register: |
| loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; |
| break; |
| case DW_CFA_def_cfa: |
| loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; |
| loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset; |
| break; |
| case DW_CFA_def_cfa_expression: |
| get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* Find the previous value for the CFA. */ |
| |
| static void |
| lookup_cfa (dw_cfa_location *loc) |
| { |
| dw_cfi_ref cfi; |
| |
| loc->reg = (unsigned long) -1; |
| loc->offset = 0; |
| loc->indirect = 0; |
| loc->base_offset = 0; |
| |
| for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) |
| lookup_cfa_1 (cfi, loc); |
| |
| if (fde_table_in_use) |
| { |
| 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, loc); |
| } |
| } |
| |
| /* The current rule for calculating the DWARF2 canonical frame address. */ |
| static dw_cfa_location cfa; |
| |
| /* The register used for saving registers to the stack, and its offset |
| from the CFA. */ |
| static dw_cfa_location cfa_store; |
| |
| /* The running total of the size of arguments pushed onto the stack. */ |
| static HOST_WIDE_INT args_size; |
| |
| /* The last args_size we actually output. */ |
| static HOST_WIDE_INT old_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 (const char *label, unsigned int reg, HOST_WIDE_INT offset) |
| { |
| dw_cfa_location loc; |
| loc.indirect = 0; |
| loc.base_offset = 0; |
| loc.reg = reg; |
| loc.offset = offset; |
| def_cfa_1 (label, &loc); |
| } |
| |
| /* This routine does the actual work. The CFA is now calculated from |
| the dw_cfa_location structure. */ |
| |
| static void |
| def_cfa_1 (const char *label, dw_cfa_location *loc_p) |
| { |
| dw_cfi_ref cfi; |
| dw_cfa_location old_cfa, loc; |
| |
| cfa = *loc_p; |
| loc = *loc_p; |
| |
| if (cfa_store.reg == loc.reg && loc.indirect == 0) |
| cfa_store.offset = loc.offset; |
| |
| loc.reg = DWARF_FRAME_REGNUM (loc.reg); |
| lookup_cfa (&old_cfa); |
| |
| /* If nothing changed, no need to issue any call frame instructions. */ |
| if (loc.reg == old_cfa.reg && loc.offset == old_cfa.offset |
| && loc.indirect == old_cfa.indirect |
| && (loc.indirect == 0 || loc.base_offset == old_cfa.base_offset)) |
| return; |
| |
| cfi = new_cfi (); |
| |
| if (loc.reg == old_cfa.reg && !loc.indirect) |
| { |
| /* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, |
| indicating the CFA register did not change but the offset |
| did. */ |
| cfi->dw_cfi_opc = DW_CFA_def_cfa_offset; |
| cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset; |
| } |
| |
| #ifndef MIPS_DEBUGGING_INFO /* SGI dbx thinks this means no offset. */ |
| else if (loc.offset == old_cfa.offset && old_cfa.reg != (unsigned long) -1 |
| && !loc.indirect) |
| { |
| /* Construct a "DW_CFA_def_cfa_register <register>" instruction, |
| indicating the CFA register has changed to <register> but the |
| offset has not changed. */ |
| cfi->dw_cfi_opc = DW_CFA_def_cfa_register; |
| cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg; |
| } |
| #endif |
| |
| else if (loc.indirect == 0) |
| { |
| /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction, |
| indicating the CFA register has changed to <register> with |
| the specified offset. */ |
| cfi->dw_cfi_opc = DW_CFA_def_cfa; |
| cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg; |
| cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset; |
| } |
| else |
| { |
| /* Construct a DW_CFA_def_cfa_expression instruction to |
| calculate the CFA using a full location expression since no |
| register-offset pair is available. */ |
| struct dw_loc_descr_struct *loc_list; |
| |
| cfi->dw_cfi_opc = DW_CFA_def_cfa_expression; |
| loc_list = build_cfa_loc (&loc); |
| cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list; |
| } |
| |
| 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 (const char *label, unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset) |
| { |
| dw_cfi_ref cfi = new_cfi (); |
| |
| cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; |
| |
| /* The following comparison is correct. -1 is used to indicate that |
| the value isn't a register number. */ |
| if (sreg == (unsigned int) -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; |
| |
| #ifdef ENABLE_CHECKING |
| { |
| /* If we get an offset that is not a multiple of |
| DWARF_CIE_DATA_ALIGNMENT, there is either a bug in the |
| definition of DWARF_CIE_DATA_ALIGNMENT, or a bug in the machine |
| description. */ |
| HOST_WIDE_INT check_offset = offset / DWARF_CIE_DATA_ALIGNMENT; |
| |
| if (check_offset * DWARF_CIE_DATA_ALIGNMENT != offset) |
| abort (); |
| } |
| #endif |
| offset /= DWARF_CIE_DATA_ALIGNMENT; |
| if (offset < 0) |
| cfi->dw_cfi_opc = DW_CFA_offset_extended_sf; |
| |
| cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; |
| } |
| else if (sreg == reg) |
| /* We could emit a DW_CFA_same_value in this case, but don't bother. */ |
| return; |
| 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 (const char *label) |
| { |
| 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 (const char *label, HOST_WIDE_INT size) |
| { |
| dw_cfi_ref cfi; |
| |
| if (size == old_args_size) |
| return; |
| |
| old_args_size = size; |
| |
| 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 (const char *label, unsigned int reg, HOST_WIDE_INT 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 (const char *label, HOST_WIDE_INT 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 (const char *label, unsigned int 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 (rtx rtl) |
| { |
| unsigned int reg = (unsigned int) -1; |
| HOST_WIDE_INT offset = 0; |
| |
| switch (GET_CODE (rtl)) |
| { |
| case REG: |
| /* RA is in a register. */ |
| reg = DWARF_FRAME_REGNUM (REGNO (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); |
| } |
| |
| /* Given a SET, calculate the amount of stack adjustment it |
| contains. */ |
| |
| static HOST_WIDE_INT |
| stack_adjust_offset (rtx pattern) |
| { |
| rtx src = SET_SRC (pattern); |
| rtx dest = SET_DEST (pattern); |
| HOST_WIDE_INT offset = 0; |
| enum rtx_code code; |
| |
| 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 0; |
| |
| offset = INTVAL (XEXP (src, 1)); |
| if (code == PLUS) |
| offset = -offset; |
| } |
| else if (GET_CODE (dest) == MEM) |
| { |
| /* (set (mem (pre_dec (reg sp))) (foo)) */ |
| src = XEXP (dest, 0); |
| code = GET_CODE (src); |
| |
| switch (code) |
| { |
| case PRE_MODIFY: |
| case POST_MODIFY: |
| if (XEXP (src, 0) == stack_pointer_rtx) |
| { |
| rtx val = XEXP (XEXP (src, 1), 1); |
| /* We handle only adjustments by constant amount. */ |
| if (GET_CODE (XEXP (src, 1)) != PLUS || |
| GET_CODE (val) != CONST_INT) |
| abort (); |
| offset = -INTVAL (val); |
| break; |
| } |
| return 0; |
| |
| case PRE_DEC: |
| case POST_DEC: |
| if (XEXP (src, 0) == stack_pointer_rtx) |
| { |
| offset = GET_MODE_SIZE (GET_MODE (dest)); |
| break; |
| } |
| return 0; |
| |
| case PRE_INC: |
| case POST_INC: |
| if (XEXP (src, 0) == stack_pointer_rtx) |
| { |
| offset = -GET_MODE_SIZE (GET_MODE (dest)); |
| break; |
| } |
| return 0; |
| |
| default: |
| return 0; |
| } |
| } |
| else |
| return 0; |
| |
| return 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 (rtx insn) |
| { |
| HOST_WIDE_INT offset; |
| const char *label; |
| int i; |
| |
| /* Don't handle epilogues at all. Certainly it would be wrong to do so |
| with this function. Proper support would require all frame-related |
| insns to be marked, and to be able to handle saving state around |
| epilogues textually in the middle of the function. */ |
| if (prologue_epilogue_contains (insn) || sibcall_epilogue_contains (insn)) |
| return; |
| |
| if (!flag_asynchronous_unwind_tables && GET_CODE (insn) == CALL_INSN) |
| { |
| /* Extract the size of the args from the CALL rtx itself. */ |
| insn = PATTERN (insn); |
| if (GET_CODE (insn) == PARALLEL) |
| insn = XVECEXP (insn, 0, 0); |
| if (GET_CODE (insn) == SET) |
| insn = SET_SRC (insn); |
| if (GET_CODE (insn) != CALL) |
| abort (); |
| |
| dwarf2out_args_size ("", INTVAL (XEXP (insn, 1))); |
| return; |
| } |
| |
| /* If only calls can throw, and we have a frame pointer, |
| save up adjustments until we see the CALL_INSN. */ |
| else if (!flag_asynchronous_unwind_tables && cfa.reg != STACK_POINTER_REGNUM) |
| return; |
| |
| 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) |
| offset = stack_adjust_offset (PATTERN (insn)); |
| else if (GET_CODE (PATTERN (insn)) == PARALLEL |
| || GET_CODE (PATTERN (insn)) == SEQUENCE) |
| { |
| /* There may be stack adjustments inside compound insns. Search |
| for them. */ |
| for (offset = 0, i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) |
| if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) |
| offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i)); |
| } |
| 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 (); |
| def_cfa_1 (label, &cfa); |
| dwarf2out_args_size (label, args_size); |
| } |
| |
| #endif |
| |
| /* We delay emitting a register save until either (a) we reach the end |
| of the prologue or (b) the register is clobbered. This clusters |
| register saves so that there are fewer pc advances. */ |
| |
| struct queued_reg_save GTY(()) |
| { |
| struct queued_reg_save *next; |
| rtx reg; |
| HOST_WIDE_INT cfa_offset; |
| }; |
| |
| static GTY(()) struct queued_reg_save *queued_reg_saves; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| static const char *last_reg_save_label; |
| |
| static void |
| queue_reg_save (const char *label, rtx reg, HOST_WIDE_INT offset) |
| { |
| struct queued_reg_save *q = ggc_alloc (sizeof (*q)); |
| |
| q->next = queued_reg_saves; |
| q->reg = reg; |
| q->cfa_offset = offset; |
| queued_reg_saves = q; |
| |
| last_reg_save_label = label; |
| } |
| |
| static void |
| flush_queued_reg_saves (void) |
| { |
| struct queued_reg_save *q, *next; |
| |
| for (q = queued_reg_saves; q; q = next) |
| { |
| dwarf2out_reg_save (last_reg_save_label, REGNO (q->reg), q->cfa_offset); |
| next = q->next; |
| } |
| |
| queued_reg_saves = NULL; |
| last_reg_save_label = NULL; |
| } |
| |
| static bool |
| clobbers_queued_reg_save (rtx insn) |
| { |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| if (modified_in_p (q->reg, insn)) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /* A temporary register holding an integral value used in adjusting SP |
| or setting up the store_reg. The "offset" field holds the integer |
| value, not an offset. */ |
| static dw_cfa_location cfa_temp; |
| |
| /* Record call frame debugging information for an expression EXPR, |
| which either sets SP or FP (adjusting how we calculate the frame |
| address) or saves a register to the stack. LABEL indicates the |
| address of EXPR. |
| |
| This function encodes a state machine mapping rtxes to actions on |
| cfa, cfa_store, and cfa_temp.reg. We describe these rules so |
| users need not read the source code. |
| |
| The High-Level Picture |
| |
| Changes in the register we use to calculate the CFA: Currently we |
| assume that if you copy the CFA register into another register, we |
| should take the other one as the new CFA register; this seems to |
| work pretty well. If it's wrong for some target, it's simple |
| enough not to set RTX_FRAME_RELATED_P on the insn in question. |
| |
| Changes in the register we use for saving registers to the stack: |
| This is usually SP, but not always. Again, we deduce that if you |
| copy SP into another register (and SP is not the CFA register), |
| then the new register is the one we will be using for register |
| saves. This also seems to work. |
| |
| Register saves: There's not much guesswork about this one; if |
| RTX_FRAME_RELATED_P is set on an insn which modifies memory, it's a |
| register save, and the register used to calculate the destination |
| had better be the one we think we're using for this purpose. |
| |
| Except: If the register being saved is the CFA register, and the |
| offset is nonzero, we are saving the CFA, so we assume we have to |
| use DW_CFA_def_cfa_expression. If the offset is 0, we assume that |
| the intent is to save the value of SP from the previous frame. |
| |
| Invariants / Summaries of Rules |
| |
| cfa current rule for calculating the CFA. It usually |
| consists of a register and an offset. |
| cfa_store register used by prologue code to save things to the stack |
| cfa_store.offset is the offset from the value of |
| cfa_store.reg to the actual CFA |
| cfa_temp register holding an integral value. cfa_temp.offset |
| stores the value, which will be used to adjust the |
| stack pointer. cfa_temp is also used like cfa_store, |
| to track stores to the stack via fp or a temp reg. |
| |
| Rules 1- 4: Setting a register's value to cfa.reg or an expression |
| with cfa.reg as the first operand changes the cfa.reg and its |
| cfa.offset. Rule 1 and 4 also set cfa_temp.reg and |
| cfa_temp.offset. |
| |
| Rules 6- 9: Set a non-cfa.reg register value to a constant or an |
| expression yielding a constant. This sets cfa_temp.reg |
| and cfa_temp.offset. |
| |
| Rule 5: Create a new register cfa_store used to save items to the |
| stack. |
| |
| Rules 10-14: Save a register to the stack. Define offset as the |
| difference of the original location and cfa_store's |
| location (or cfa_temp's location if cfa_temp is used). |
| |
| The Rules |
| |
| "{a,b}" indicates a choice of a xor b. |
| "<reg>:cfa.reg" indicates that <reg> must equal cfa.reg. |
| |
| Rule 1: |
| (set <reg1> <reg2>:cfa.reg) |
| effects: cfa.reg = <reg1> |
| cfa.offset unchanged |
| cfa_temp.reg = <reg1> |
| cfa_temp.offset = cfa.offset |
| |
| Rule 2: |
| (set sp ({minus,plus,losum} {sp,fp}:cfa.reg |
| {<const_int>,<reg>:cfa_temp.reg})) |
| effects: cfa.reg = sp if fp used |
| cfa.offset += {+/- <const_int>, cfa_temp.offset} if cfa.reg==sp |
| cfa_store.offset += {+/- <const_int>, cfa_temp.offset} |
| if cfa_store.reg==sp |
| |
| Rule 3: |
| (set fp ({minus,plus,losum} <reg>:cfa.reg <const_int>)) |
| effects: cfa.reg = fp |
| cfa_offset += +/- <const_int> |
| |
| Rule 4: |
| (set <reg1> ({plus,losum} <reg2>:cfa.reg <const_int>)) |
| constraints: <reg1> != fp |
| <reg1> != sp |
| effects: cfa.reg = <reg1> |
| cfa_temp.reg = <reg1> |
| cfa_temp.offset = cfa.offset |
| |
| Rule 5: |
| (set <reg1> (plus <reg2>:cfa_temp.reg sp:cfa.reg)) |
| constraints: <reg1> != fp |
| <reg1> != sp |
| effects: cfa_store.reg = <reg1> |
| cfa_store.offset = cfa.offset - cfa_temp.offset |
| |
| Rule 6: |
| (set <reg> <const_int>) |
| effects: cfa_temp.reg = <reg> |
| cfa_temp.offset = <const_int> |
| |
| Rule 7: |
| (set <reg1>:cfa_temp.reg (ior <reg2>:cfa_temp.reg <const_int>)) |
| effects: cfa_temp.reg = <reg1> |
| cfa_temp.offset |= <const_int> |
| |
| Rule 8: |
| (set <reg> (high <exp>)) |
| effects: none |
| |
| Rule 9: |
| (set <reg> (lo_sum <exp> <const_int>)) |
| effects: cfa_temp.reg = <reg> |
| cfa_temp.offset = <const_int> |
| |
| Rule 10: |
| (set (mem (pre_modify sp:cfa_store (???? <reg1> <const_int>))) <reg2>) |
| effects: cfa_store.offset -= <const_int> |
| cfa.offset = cfa_store.offset if cfa.reg == sp |
| cfa.reg = sp |
| cfa.base_offset = -cfa_store.offset |
| |
| Rule 11: |
| (set (mem ({pre_inc,pre_dec} sp:cfa_store.reg)) <reg>) |
| effects: cfa_store.offset += -/+ mode_size(mem) |
| cfa.offset = cfa_store.offset if cfa.reg == sp |
| cfa.reg = sp |
| cfa.base_offset = -cfa_store.offset |
| |
| Rule 12: |
| (set (mem ({minus,plus,losum} <reg1>:{cfa_store,cfa_temp} <const_int>)) |
| |
| <reg2>) |
| effects: cfa.reg = <reg1> |
| cfa.base_offset = -/+ <const_int> - {cfa_store,cfa_temp}.offset |
| |
| Rule 13: |
| (set (mem <reg1>:{cfa_store,cfa_temp}) <reg2>) |
| effects: cfa.reg = <reg1> |
| cfa.base_offset = -{cfa_store,cfa_temp}.offset |
| |
| Rule 14: |
| (set (mem (postinc <reg1>:cfa_temp <const_int>)) <reg2>) |
| effects: cfa.reg = <reg1> |
| cfa.base_offset = -cfa_temp.offset |
| cfa_temp.offset -= mode_size(mem) */ |
| |
| static void |
| dwarf2out_frame_debug_expr (rtx expr, const char *label) |
| { |
| rtx src, dest; |
| HOST_WIDE_INT offset; |
| |
| /* If RTX_FRAME_RELATED_P is set on a PARALLEL, process each member of |
| the PARALLEL independently. The first element is always processed if |
| it is a SET. This is for backward compatibility. Other elements |
| are processed only if they are SETs and the RTX_FRAME_RELATED_P |
| flag is set in them. */ |
| if (GET_CODE (expr) == PARALLEL || GET_CODE (expr) == SEQUENCE) |
| { |
| int par_index; |
| int limit = XVECLEN (expr, 0); |
| |
| for (par_index = 0; par_index < limit; par_index++) |
| if (GET_CODE (XVECEXP (expr, 0, par_index)) == SET |
| && (RTX_FRAME_RELATED_P (XVECEXP (expr, 0, par_index)) |
| || par_index == 0)) |
| dwarf2out_frame_debug_expr (XVECEXP (expr, 0, par_index), label); |
| |
| return; |
| } |
| |
| if (GET_CODE (expr) != SET) |
| abort (); |
| |
| src = SET_SRC (expr); |
| dest = SET_DEST (expr); |
| |
| switch (GET_CODE (dest)) |
| { |
| case REG: |
| /* Rule 1 */ |
| /* 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 == (unsigned) REGNO (src)) |
| /* OK. */ |
| ; |
| else |
| abort (); |
| |
| /* We used to require that dest be either SP or FP, but the |
| ARM copies SP to a temporary register, and from there to |
| FP. So we just rely on the backends to only set |
| RTX_FRAME_RELATED_P on appropriate insns. */ |
| cfa.reg = REGNO (dest); |
| cfa_temp.reg = cfa.reg; |
| cfa_temp.offset = cfa.offset; |
| break; |
| |
| case PLUS: |
| case MINUS: |
| case LO_SUM: |
| if (dest == stack_pointer_rtx) |
| { |
| /* Rule 2 */ |
| /* Adjusting SP. */ |
| switch (GET_CODE (XEXP (src, 1))) |
| { |
| case CONST_INT: |
| offset = INTVAL (XEXP (src, 1)); |
| break; |
| case REG: |
| if ((unsigned) REGNO (XEXP (src, 1)) != cfa_temp.reg) |
| abort (); |
| offset = cfa_temp.offset; |
| break; |
| default: |
| abort (); |
| } |
| |
| if (XEXP (src, 0) == hard_frame_pointer_rtx) |
| { |
| /* Restoring SP from FP in the epilogue. */ |
| if (cfa.reg != (unsigned) HARD_FRAME_POINTER_REGNUM) |
| abort (); |
| cfa.reg = STACK_POINTER_REGNUM; |
| } |
| else if (GET_CODE (src) == LO_SUM) |
| /* Assume we've set the source reg of the LO_SUM from sp. */ |
| ; |
| else if (XEXP (src, 0) != stack_pointer_rtx) |
| abort (); |
| |
| if (GET_CODE (src) != MINUS) |
| offset = -offset; |
| if (cfa.reg == STACK_POINTER_REGNUM) |
| cfa.offset += offset; |
| if (cfa_store.reg == STACK_POINTER_REGNUM) |
| cfa_store.offset += offset; |
| } |
| else if (dest == hard_frame_pointer_rtx) |
| { |
| /* Rule 3 */ |
| /* Either setting the FP from an offset of the SP, |
| or adjusting the FP */ |
| if (! frame_pointer_needed) |
| abort (); |
| |
| if (GET_CODE (XEXP (src, 0)) == REG |
| && (unsigned) REGNO (XEXP (src, 0)) == cfa.reg |
| && GET_CODE (XEXP (src, 1)) == CONST_INT) |
| { |
| offset = INTVAL (XEXP (src, 1)); |
| if (GET_CODE (src) != MINUS) |
| offset = -offset; |
| cfa.offset += offset; |
| cfa.reg = HARD_FRAME_POINTER_REGNUM; |
| } |
| else |
| abort (); |
| } |
| else |
| { |
| if (GET_CODE (src) == MINUS) |
| abort (); |
| |
| /* Rule 4 */ |
| if (GET_CODE (XEXP (src, 0)) == REG |
| && REGNO (XEXP (src, 0)) == cfa.reg |
| && GET_CODE (XEXP (src, 1)) == CONST_INT) |
| { |
| /* Setting a temporary CFA register that will be copied |
| into the FP later on. */ |
| offset = - INTVAL (XEXP (src, 1)); |
| cfa.offset += offset; |
| cfa.reg = REGNO (dest); |
| /* Or used to save regs to the stack. */ |
| cfa_temp.reg = cfa.reg; |
| cfa_temp.offset = cfa.offset; |
| } |
| |
| /* Rule 5 */ |
| else if (GET_CODE (XEXP (src, 0)) == REG |
| && REGNO (XEXP (src, 0)) == cfa_temp.reg |
| && XEXP (src, 1) == stack_pointer_rtx) |
| { |
| /* Setting a scratch register that we will use instead |
| of SP for saving registers to the stack. */ |
| if (cfa.reg != STACK_POINTER_REGNUM) |
| abort (); |
| cfa_store.reg = REGNO (dest); |
| cfa_store.offset = cfa.offset - cfa_temp.offset; |
| } |
| |
| /* Rule 9 */ |
| else if (GET_CODE (src) == LO_SUM |
| && GET_CODE (XEXP (src, 1)) == CONST_INT) |
| { |
| cfa_temp.reg = REGNO (dest); |
| cfa_temp.offset = INTVAL (XEXP (src, 1)); |
| } |
| else |
| abort (); |
| } |
| break; |
| |
| /* Rule 6 */ |
| case CONST_INT: |
| cfa_temp.reg = REGNO (dest); |
| cfa_temp.offset = INTVAL (src); |
| break; |
| |
| /* Rule 7 */ |
| case IOR: |
| if (GET_CODE (XEXP (src, 0)) != REG |
| || (unsigned) REGNO (XEXP (src, 0)) != cfa_temp.reg |
| || GET_CODE (XEXP (src, 1)) != CONST_INT) |
| abort (); |
| |
| if ((unsigned) REGNO (dest) != cfa_temp.reg) |
| cfa_temp.reg = REGNO (dest); |
| cfa_temp.offset |= INTVAL (XEXP (src, 1)); |
| break; |
| |
| /* Skip over HIGH, assuming it will be followed by a LO_SUM, |
| which will fill in all of the bits. */ |
| /* Rule 8 */ |
| case HIGH: |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| def_cfa_1 (label, &cfa); |
| break; |
| |
| case MEM: |
| if (GET_CODE (src) != REG) |
| abort (); |
| |
| /* Saving a register to the stack. Make sure dest is relative to the |
| CFA register. */ |
| switch (GET_CODE (XEXP (dest, 0))) |
| { |
| /* Rule 10 */ |
| /* With a push. */ |
| case PRE_MODIFY: |
| /* We can't handle variable size modifications. */ |
| if (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) != CONST_INT) |
| abort (); |
| offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)); |
| |
| 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; |
| |
| /* Rule 11 */ |
| 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; |
| |
| /* Rule 12 */ |
| /* With an offset. */ |
| case PLUS: |
| case MINUS: |
| case LO_SUM: |
| if (GET_CODE (XEXP (XEXP (dest, 0), 1)) != CONST_INT) |
| abort (); |
| offset = INTVAL (XEXP (XEXP (dest, 0), 1)); |
| if (GET_CODE (XEXP (dest, 0)) == MINUS) |
| offset = -offset; |
| |
| if (cfa_store.reg == (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))) |
| offset -= cfa_store.offset; |
| else if (cfa_temp.reg == (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))) |
| offset -= cfa_temp.offset; |
| else |
| abort (); |
| break; |
| |
| /* Rule 13 */ |
| /* Without an offset. */ |
| case REG: |
| if (cfa_store.reg == (unsigned) REGNO (XEXP (dest, 0))) |
| offset = -cfa_store.offset; |
| else if (cfa_temp.reg == (unsigned) REGNO (XEXP (dest, 0))) |
| offset = -cfa_temp.offset; |
| else |
| abort (); |
| break; |
| |
| /* Rule 14 */ |
| case POST_INC: |
| if (cfa_temp.reg != (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))) |
| abort (); |
| offset = -cfa_temp.offset; |
| cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest)); |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (REGNO (src) != STACK_POINTER_REGNUM |
| && REGNO (src) != HARD_FRAME_POINTER_REGNUM |
| && (unsigned) REGNO (src) == cfa.reg) |
| { |
| /* We're storing the current CFA reg into the stack. */ |
| |
| if (cfa.offset == 0) |
| { |
| /* If the source register is exactly the CFA, assume |
| we're saving SP like any other register; this happens |
| on the ARM. */ |
| def_cfa_1 (label, &cfa); |
| queue_reg_save (label, stack_pointer_rtx, offset); |
| break; |
| } |
| else |
| { |
| /* Otherwise, we'll need to look in the stack to |
| calculate the CFA. */ |
| rtx x = XEXP (dest, 0); |
| |
| if (GET_CODE (x) != REG) |
| x = XEXP (x, 0); |
| if (GET_CODE (x) != REG) |
| abort (); |
| |
| cfa.reg = REGNO (x); |
| cfa.base_offset = offset; |
| cfa.indirect = 1; |
| def_cfa_1 (label, &cfa); |
| break; |
| } |
| } |
| |
| def_cfa_1 (label, &cfa); |
| queue_reg_save (label, src, offset); |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| /* 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 (rtx insn) |
| { |
| const char *label; |
| rtx src; |
| |
| if (insn == NULL_RTX) |
| { |
| /* Flush any queued register saves. */ |
| flush_queued_reg_saves (); |
| |
| /* Set up state for generating call frame debug info. */ |
| lookup_cfa (&cfa); |
| if (cfa.reg != (unsigned long) DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)) |
| abort (); |
| |
| cfa.reg = STACK_POINTER_REGNUM; |
| cfa_store = cfa; |
| cfa_temp.reg = -1; |
| cfa_temp.offset = 0; |
| return; |
| } |
| |
| if (GET_CODE (insn) != INSN || clobbers_queued_reg_save (insn)) |
| flush_queued_reg_saves (); |
| |
| if (! RTX_FRAME_RELATED_P (insn)) |
| { |
| if (!ACCUMULATE_OUTGOING_ARGS) |
| dwarf2out_stack_adjust (insn); |
| |
| return; |
| } |
| |
| label = dwarf2out_cfi_label (); |
| src = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); |
| if (src) |
| insn = XEXP (src, 0); |
| else |
| insn = PATTERN (insn); |
| |
| dwarf2out_frame_debug_expr (insn, label); |
| } |
| |
| #endif |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ |
| static enum dw_cfi_oprnd_type dw_cfi_oprnd1_desc |
| (enum dwarf_call_frame_info cfi); |
| |
| static enum dw_cfi_oprnd_type |
| dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi) |
| { |
| switch (cfi) |
| { |
| case DW_CFA_nop: |
| case DW_CFA_GNU_window_save: |
| return dw_cfi_oprnd_unused; |
| |
| case DW_CFA_set_loc: |
| case DW_CFA_advance_loc1: |
| case DW_CFA_advance_loc2: |
| case DW_CFA_advance_loc4: |
| case DW_CFA_MIPS_advance_loc8: |
| return dw_cfi_oprnd_addr; |
| |
| case DW_CFA_offset: |
| case DW_CFA_offset_extended: |
| case DW_CFA_def_cfa: |
| case DW_CFA_offset_extended_sf: |
| case DW_CFA_def_cfa_sf: |
| case DW_CFA_restore_extended: |
| case DW_CFA_undefined: |
| case DW_CFA_same_value: |
| case DW_CFA_def_cfa_register: |
| case DW_CFA_register: |
| return dw_cfi_oprnd_reg_num; |
| |
| case DW_CFA_def_cfa_offset: |
| case DW_CFA_GNU_args_size: |
| case DW_CFA_def_cfa_offset_sf: |
| return dw_cfi_oprnd_offset; |
| |
| case DW_CFA_def_cfa_expression: |
| case DW_CFA_expression: |
| return dw_cfi_oprnd_loc; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used. */ |
| static enum dw_cfi_oprnd_type dw_cfi_oprnd2_desc |
| (enum dwarf_call_frame_info cfi); |
| |
| static enum dw_cfi_oprnd_type |
| dw_cfi_oprnd2_desc (enum dwarf_call_frame_info cfi) |
| { |
| switch (cfi) |
| { |
| case DW_CFA_def_cfa: |
| case DW_CFA_def_cfa_sf: |
| case DW_CFA_offset: |
| case DW_CFA_offset_extended_sf: |
| case DW_CFA_offset_extended: |
| return dw_cfi_oprnd_offset; |
| |
| case DW_CFA_register: |
| return dw_cfi_oprnd_reg_num; |
| |
| default: |
| return dw_cfi_oprnd_unused; |
| } |
| } |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| |
| /* Map register numbers held in the call frame info that gcc has |
| collected using DWARF_FRAME_REGNUM to those that should be output in |
| .debug_frame and .eh_frame. */ |
| #ifndef DWARF2_FRAME_REG_OUT |
| #define DWARF2_FRAME_REG_OUT(REGNO, FOR_EH) (REGNO) |
| #endif |
| |
| /* Output a Call Frame Information opcode and its operand(s). */ |
| |
| static void |
| output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int for_eh) |
| { |
| unsigned long r; |
| if (cfi->dw_cfi_opc == DW_CFA_advance_loc) |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc |
| | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)), |
| "DW_CFA_advance_loc " HOST_WIDE_INT_PRINT_HEX, |
| cfi->dw_cfi_oprnd1.dw_cfi_offset); |
| else if (cfi->dw_cfi_opc == DW_CFA_offset) |
| { |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), |
| "DW_CFA_offset, column 0x%lx", r); |
| dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL); |
| } |
| else if (cfi->dw_cfi_opc == DW_CFA_restore) |
| { |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), |
| "DW_CFA_restore, column 0x%lx", r); |
| } |
| else |
| { |
| dw2_asm_output_data (1, cfi->dw_cfi_opc, |
| "%s", dwarf_cfi_name (cfi->dw_cfi_opc)); |
| |
| switch (cfi->dw_cfi_opc) |
| { |
| case DW_CFA_set_loc: |
| if (for_eh) |
| dw2_asm_output_encoded_addr_rtx ( |
| ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0), |
| gen_rtx_SYMBOL_REF (Pmode, cfi->dw_cfi_oprnd1.dw_cfi_addr), |
| NULL); |
| else |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| cfi->dw_cfi_oprnd1.dw_cfi_addr, NULL); |
| break; |
| |
| case DW_CFA_advance_loc1: |
| dw2_asm_output_delta (1, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc2: |
| dw2_asm_output_delta (2, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc4: |
| dw2_asm_output_delta (4, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_MIPS_advance_loc8: |
| dw2_asm_output_delta (8, cfi->dw_cfi_oprnd1.dw_cfi_addr, |
| fde->dw_fde_current_label, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_offset_extended: |
| case DW_CFA_def_cfa: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_offset_extended_sf: |
| case DW_CFA_def_cfa_sf: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| dw2_asm_output_data_sleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_restore_extended: |
| case DW_CFA_undefined: |
| case DW_CFA_same_value: |
| case DW_CFA_def_cfa_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| break; |
| |
| case DW_CFA_register: |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, for_eh); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| break; |
| |
| case DW_CFA_def_cfa_offset: |
| case DW_CFA_GNU_args_size: |
| dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_def_cfa_offset_sf: |
| dw2_asm_output_data_sleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset, NULL); |
| break; |
| |
| case DW_CFA_GNU_window_save: |
| break; |
| |
| case DW_CFA_def_cfa_expression: |
| case DW_CFA_expression: |
| output_cfa_loc (cfi); |
| break; |
| |
| case DW_CFA_GNU_negative_offset_extended: |
| /* Obsoleted by DW_CFA_offset_extended_sf. */ |
| abort (); |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /* Output the call frame information used to record information |
| that relates to calculating the frame pointer, and records the |
| location of saved registers. */ |
| |
| static void |
| output_call_frame_info (int for_eh) |
| { |
| unsigned int i; |
| dw_fde_ref fde; |
| dw_cfi_ref cfi; |
| char l1[20], l2[20], section_start_label[20]; |
| bool any_lsda_needed = false; |
| char augmentation[6]; |
| int augmentation_size; |
| int fde_encoding = DW_EH_PE_absptr; |
| int per_encoding = DW_EH_PE_absptr; |
| int lsda_encoding = DW_EH_PE_absptr; |
| |
| /* Don't emit a CIE if there won't be any FDEs. */ |
| if (fde_table_in_use == 0) |
| return; |
| |
| /* If we don't have any functions we'll want to unwind out of, don't |
| emit any EH unwind information. Note that if exceptions aren't |
| enabled, we won't have collected nothrow information, and if we |
| asked for asynchronous tables, we always want this info. */ |
| if (for_eh) |
| { |
| bool any_eh_needed = !flag_exceptions || flag_asynchronous_unwind_tables; |
| |
| for (i = 0; i < fde_table_in_use; i++) |
| if (fde_table[i].uses_eh_lsda) |
| any_eh_needed = any_lsda_needed = true; |
| else if (! fde_table[i].nothrow |
| && ! fde_table[i].all_throwers_are_sibcalls) |
| any_eh_needed = true; |
| |
| if (! any_eh_needed) |
| return; |
| } |
| |
| /* We're going to be generating comments, so turn on app. */ |
| if (flag_debug_asm) |
| app_enable (); |
| |
| if (for_eh) |
| (*targetm.asm_out.eh_frame_section) (); |
| else |
| named_section_flags (DEBUG_FRAME_SECTION, SECTION_DEBUG); |
| |
| ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh); |
| ASM_OUTPUT_LABEL (asm_out_file, section_start_label); |
| |
| /* Output the CIE. */ |
| ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); |
| ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); |
| dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, |
| "Length of Common Information Entry"); |
| ASM_OUTPUT_LABEL (asm_out_file, l1); |
| |
| /* Now that the CIE pointer is PC-relative for EH, |
| use 0 to identify the CIE. */ |
| dw2_asm_output_data ((for_eh ? 4 : DWARF_OFFSET_SIZE), |
| (for_eh ? 0 : DW_CIE_ID), |
| "CIE Identifier Tag"); |
| |
| dw2_asm_output_data (1, DW_CIE_VERSION, "CIE Version"); |
| |
| augmentation[0] = 0; |
| augmentation_size = 0; |
| if (for_eh) |
| { |
| char *p; |
| |
| /* Augmentation: |
| z Indicates that a uleb128 is present to size the |
| augmentation section. |
| L Indicates the encoding (and thus presence) of |
| an LSDA pointer in the FDE augmentation. |
| R Indicates a non-default pointer encoding for |
| FDE code pointers. |
| P Indicates the presence of an encoding + language |
| personality routine in the CIE augmentation. */ |
| |
| fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0); |
| per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); |
| lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); |
| |
| p = augmentation + 1; |
| if (eh_personality_libfunc) |
| { |
| *p++ = 'P'; |
| augmentation_size += 1 + size_of_encoded_value (per_encoding); |
| } |
| if (any_lsda_needed) |
| { |
| *p++ = 'L'; |
| augmentation_size += 1; |
| } |
| if (fde_encoding != DW_EH_PE_absptr) |
| { |
| *p++ = 'R'; |
| augmentation_size += 1; |
| } |
| if (p > augmentation + 1) |
| { |
| augmentation[0] = 'z'; |
| *p = '\0'; |
| } |
| |
| /* Ug. Some platforms can't do unaligned dynamic relocations at all. */ |
| if (eh_personality_libfunc && per_encoding == DW_EH_PE_aligned) |
| { |
| int offset = ( 4 /* Length */ |
| + 4 /* CIE Id */ |
| + 1 /* CIE version */ |
| + strlen (augmentation) + 1 /* Augmentation */ |
| + size_of_uleb128 (1) /* Code alignment */ |
| + size_of_sleb128 (DWARF_CIE_DATA_ALIGNMENT) |
| + 1 /* RA column */ |
| + 1 /* Augmentation size */ |
| + 1 /* Personality encoding */ ); |
| int pad = -offset & (PTR_SIZE - 1); |
| |
| augmentation_size += pad; |
| |
| /* Augmentations should be small, so there's scarce need to |
| iterate for a solution. Die if we exceed one uleb128 byte. */ |
| if (size_of_uleb128 (augmentation_size) != 1) |
| abort (); |
| } |
| } |
| |
| dw2_asm_output_nstring (augmentation, -1, "CIE Augmentation"); |
| dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor"); |
| dw2_asm_output_data_sleb128 (DWARF_CIE_DATA_ALIGNMENT, |
| "CIE Data Alignment Factor"); |
| dw2_asm_output_data (1, DWARF_FRAME_RETURN_COLUMN, "CIE RA Column"); |
| |
| if (augmentation[0]) |
| { |
| dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size"); |
| if (eh_personality_libfunc) |
| { |
| dw2_asm_output_data (1, per_encoding, "Personality (%s)", |
| eh_data_format_name (per_encoding)); |
| dw2_asm_output_encoded_addr_rtx (per_encoding, |
| eh_personality_libfunc, NULL); |
| } |
| |
| if (any_lsda_needed) |
| dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)", |
| eh_data_format_name (lsda_encoding)); |
| |
| if (fde_encoding != DW_EH_PE_absptr) |
| dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)", |
| eh_data_format_name (fde_encoding)); |
| } |
| |
| for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) |
| output_cfi (cfi, NULL, for_eh); |
| |
| /* Pad the CIE out to an address sized boundary. */ |
| ASM_OUTPUT_ALIGN (asm_out_file, |
| floor_log2 (for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)); |
| ASM_OUTPUT_LABEL (asm_out_file, l2); |
| |
| /* Loop through all of the FDE's. */ |
| for (i = 0; i < fde_table_in_use; i++) |
| { |
| fde = &fde_table[i]; |
| |
| /* Don't emit EH unwind info for leaf functions that don't need it. */ |
| if (for_eh && !flag_asynchronous_unwind_tables && flag_exceptions |
| && (fde->nothrow || fde->all_throwers_are_sibcalls) |
| && !fde->uses_eh_lsda) |
| continue; |
| |
| (*targetm.asm_out.internal_label) (asm_out_file, FDE_LABEL, for_eh + i * 2); |
| 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); |
| dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, |
| "FDE Length"); |
| ASM_OUTPUT_LABEL (asm_out_file, l1); |
| |
| if (for_eh) |
| dw2_asm_output_delta (4, l1, section_start_label, "FDE CIE offset"); |
| else |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, section_start_label, |
| "FDE CIE offset"); |
| |
| if (for_eh) |
| { |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, |
| gen_rtx_SYMBOL_REF (Pmode, fde->dw_fde_begin), |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_end, fde->dw_fde_begin, |
| "FDE address range"); |
| } |
| else |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_end, fde->dw_fde_begin, |
| "FDE address range"); |
| } |
| |
| if (augmentation[0]) |
| { |
| if (any_lsda_needed) |
| { |
| int size = size_of_encoded_value (lsda_encoding); |
| |
| if (lsda_encoding == DW_EH_PE_aligned) |
| { |
| int offset = ( 4 /* Length */ |
| + 4 /* CIE offset */ |
| + 2 * size_of_encoded_value (fde_encoding) |
| + 1 /* Augmentation size */ ); |
| int pad = -offset & (PTR_SIZE - 1); |
| |
| size += pad; |
| if (size_of_uleb128 (size) != 1) |
| abort (); |
| } |
| |
| dw2_asm_output_data_uleb128 (size, "Augmentation size"); |
| |
| if (fde->uses_eh_lsda) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA", |
| fde->funcdef_number); |
| dw2_asm_output_encoded_addr_rtx ( |
| lsda_encoding, gen_rtx_SYMBOL_REF (Pmode, l1), |
| "Language Specific Data Area"); |
| } |
| else |
| { |
| if (lsda_encoding == DW_EH_PE_aligned) |
| ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); |
| dw2_asm_output_data |
| (size_of_encoded_value (lsda_encoding), 0, |
| "Language Specific Data Area (none)"); |
| } |
| } |
| else |
| dw2_asm_output_data_uleb128 (0, "Augmentation size"); |
| } |
| |
| /* 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, for_eh); |
| |
| /* Pad the FDE out to an address sized boundary. */ |
| ASM_OUTPUT_ALIGN (asm_out_file, |
| floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE))); |
| ASM_OUTPUT_LABEL (asm_out_file, l2); |
| } |
| |
| if (for_eh && targetm.terminate_dw2_eh_frame_info) |
| dw2_asm_output_data (4, 0, "End of Table"); |
| #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 (unsigned int line ATTRIBUTE_UNUSED, |
| const char *file ATTRIBUTE_UNUSED) |
| { |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| dw_fde_ref fde; |
| |
| current_function_func_begin_label = 0; |
| |
| #ifdef IA64_UNWIND_INFO |
| /* ??? current_function_func_begin_label is also used by except.c |
| for call-site information. We must emit this label if it might |
| be used. */ |
| if ((! flag_exceptions || USING_SJLJ_EXCEPTIONS) |
| && ! dwarf2out_do_frame ()) |
| return; |
| #else |
| if (! dwarf2out_do_frame ()) |
| return; |
| #endif |
| |
| function_section (current_function_decl); |
| ASM_GENERATE_INTERNAL_LABEL (label, FUNC_BEGIN_LABEL, |
| current_function_funcdef_no); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, FUNC_BEGIN_LABEL, |
| current_function_funcdef_no); |
| current_function_func_begin_label = get_identifier (label); |
| |
| #ifdef IA64_UNWIND_INFO |
| /* We can elide the fde allocation if we're not emitting debug info. */ |
| if (! dwarf2out_do_frame ()) |
| return; |
| #endif |
| |
| /* Expand the fde table if necessary. */ |
| if (fde_table_in_use == fde_table_allocated) |
| { |
| fde_table_allocated += FDE_TABLE_INCREMENT; |
| fde_table = ggc_realloc (fde_table, |
| fde_table_allocated * sizeof (dw_fde_node)); |
| memset (fde_table + fde_table_in_use, 0, |
| FDE_TABLE_INCREMENT * 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; |
| fde->funcdef_number = current_function_funcdef_no; |
| fde->nothrow = current_function_nothrow; |
| fde->uses_eh_lsda = cfun->uses_eh_lsda; |
| fde->all_throwers_are_sibcalls = cfun->all_throwers_are_sibcalls; |
| |
| args_size = old_args_size = 0; |
| |
| /* We only want to output line number information for the genuine dwarf2 |
| prologue case, not the eh frame case. */ |
| #ifdef DWARF2_DEBUGGING_INFO |
| if (file) |
| dwarf2out_source_line (line, file); |
| #endif |
| } |
| |
| /* 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 (unsigned int line ATTRIBUTE_UNUSED, |
| const char *file ATTRIBUTE_UNUSED) |
| { |
| 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_function_funcdef_no); |
| 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 (void) |
| { |
| /* Allocate the initial hunk of the fde_table. */ |
| fde_table = ggc_alloc_cleared (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 (void) |
| { |
| /* Output call frame information. */ |
| if (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) |
| output_call_frame_info (0); |
| |
| if (! USING_SJLJ_EXCEPTIONS && (flag_unwind_tables || flag_exceptions)) |
| output_call_frame_info (1); |
| } |
| #endif |
| |
| /* And now, the subset of the debugging information support code necessary |
| for emitting location expressions. */ |
| |
| /* We need some way to distinguish DW_OP_addr with a direct symbol |
| relocation from DW_OP_addr with a dtp-relative symbol relocation. */ |
| #define INTERNAL_DW_OP_tls_addr (0x100 + DW_OP_addr) |
| |
| |
| typedef struct dw_val_struct *dw_val_ref; |
| typedef struct die_struct *dw_die_ref; |
| typedef struct dw_loc_descr_struct *dw_loc_descr_ref; |
| typedef struct dw_loc_list_struct *dw_loc_list_ref; |
| |
| /* 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. */ |
| |
| enum dw_val_class |
| { |
| dw_val_class_addr, |
| dw_val_class_offset, |
| dw_val_class_loc, |
| dw_val_class_loc_list, |
| dw_val_class_range_list, |
| dw_val_class_const, |
| dw_val_class_unsigned_const, |
| dw_val_class_long_long, |
| dw_val_class_vec, |
| dw_val_class_flag, |
| dw_val_class_die_ref, |
| dw_val_class_fde_ref, |
| dw_val_class_lbl_id, |
| dw_val_class_lbl_offset, |
| dw_val_class_str |
| }; |
| |
| /* Describe a double word constant value. */ |
| /* ??? Every instance of long_long in the code really means CONST_DOUBLE. */ |
| |
| typedef struct dw_long_long_struct GTY(()) |
| { |
| unsigned long hi; |
| unsigned long low; |
| } |
| dw_long_long_const; |
| |
| /* Describe a floating point constant value, or a vector constant value. */ |
| |
| typedef struct dw_vec_struct GTY(()) |
| { |
| unsigned char * GTY((length ("%h.length"))) array; |
| unsigned length; |
| unsigned elt_size; |
| } |
| dw_vec_const; |
| |
| /* The dw_val_node describes an attribute's value, as it is |
| represented internally. */ |
| |
| typedef struct dw_val_struct GTY(()) |
| { |
| enum dw_val_class val_class; |
| union dw_val_struct_union |
| { |
| rtx GTY ((tag ("dw_val_class_addr"))) val_addr; |
| unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_offset"))) val_offset; |
| dw_loc_list_ref GTY ((tag ("dw_val_class_loc_list"))) val_loc_list; |
| dw_loc_descr_ref GTY ((tag ("dw_val_class_loc"))) val_loc; |
| HOST_WIDE_INT GTY ((default (""))) val_int; |
| unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_unsigned_const"))) val_unsigned; |
| dw_long_long_const GTY ((tag ("dw_val_class_long_long"))) val_long_long; |
| dw_vec_const GTY ((tag ("dw_val_class_vec"))) val_vec; |
| struct dw_val_die_union |
| { |
| dw_die_ref die; |
| int external; |
| } GTY ((tag ("dw_val_class_die_ref"))) val_die_ref; |
| unsigned GTY ((tag ("dw_val_class_fde_ref"))) val_fde_index; |
| struct indirect_string_node * GTY ((tag ("dw_val_class_str"))) val_str; |
| char * GTY ((tag ("dw_val_class_lbl_id"))) val_lbl_id; |
| unsigned char GTY ((tag ("dw_val_class_flag"))) val_flag; |
| } |
| GTY ((desc ("%1.val_class"))) v; |
| } |
| dw_val_node; |
| |
| /* Locations in memory are described using a sequence of stack machine |
| operations. */ |
| |
| typedef struct dw_loc_descr_struct GTY(()) |
| { |
| 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; |
| int dw_loc_addr; |
| } |
| dw_loc_descr_node; |
| |
| /* Location lists are ranges + location descriptions for that range, |
| so you can track variables that are in different places over |
| their entire life. */ |
| typedef struct dw_loc_list_struct GTY(()) |
| { |
| dw_loc_list_ref dw_loc_next; |
| const char *begin; /* Label for begin address of range */ |
| const char *end; /* Label for end address of range */ |
| char *ll_symbol; /* Label for beginning of location list. |
| Only on head of list */ |
| const char *section; /* Section this loclist is relative to */ |
| dw_loc_descr_ref expr; |
| } dw_loc_list_node; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| |
| static const char *dwarf_stack_op_name (unsigned); |
| static dw_loc_descr_ref new_loc_descr (enum dwarf_location_atom, |
| unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT); |
| static void add_loc_descr (dw_loc_descr_ref *, dw_loc_descr_ref); |
| static unsigned long size_of_loc_descr (dw_loc_descr_ref); |
| static unsigned long size_of_locs (dw_loc_descr_ref); |
| static void output_loc_operands (dw_loc_descr_ref); |
| static void output_loc_sequence (dw_loc_descr_ref); |
| |
| /* Convert a DWARF stack opcode into its string name. */ |
| |
| static const char * |
| dwarf_stack_op_name (unsigned int op) |
| { |
| switch (op) |
| { |
| case DW_OP_addr: |
| case INTERNAL_DW_OP_tls_addr: |
| return "DW_OP_addr"; |
| case DW_OP_deref: |
| return "DW_OP_deref"; |
| case DW_OP_const1u: |
| return "DW_OP_const1u"; |
| case DW_OP_const1s: |
| return "DW_OP_const1s"; |
| case DW_OP_const2u: |
| return "DW_OP_const2u"; |
| case DW_OP_const2s: |
| return "DW_OP_const2s"; |
| case DW_OP_const4u: |
| return "DW_OP_const4u"; |
| case DW_OP_const4s: |
| return "DW_OP_const4s"; |
| case DW_OP_const8u: |
| return "DW_OP_const8u"; |
| case DW_OP_const8s: |
| return "DW_OP_const8s"; |
| case DW_OP_constu: |
| return "DW_OP_constu"; |
| case DW_OP_consts: |
| return "DW_OP_consts"; |
| case DW_OP_dup: |
| return "DW_OP_dup"; |
| case DW_OP_drop: |
| return "DW_OP_drop"; |
| case DW_OP_over: |
| return "DW_OP_over"; |
| case DW_OP_pick: |
| return "DW_OP_pick"; |
| case DW_OP_swap: |
| return "DW_OP_swap"; |
| case DW_OP_rot: |
| return "DW_OP_rot"; |
| case DW_OP_xderef: |
| return "DW_OP_xderef"; |
| case DW_OP_abs: |
| return "DW_OP_abs"; |
| case DW_OP_and: |
| return "DW_OP_and"; |
| case DW_OP_div: |
| return "DW_OP_div"; |
| case DW_OP_minus: |
| return "DW_OP_minus"; |
| case DW_OP_mod: |
| return "DW_OP_mod"; |
| case DW_OP_mul: |
| return "DW_OP_mul"; |
| case DW_OP_neg: |
| return "DW_OP_neg"; |
| case DW_OP_not: |
| return "DW_OP_not"; |
| case DW_OP_or: |
| return "DW_OP_or"; |
| case DW_OP_plus: |
| return "DW_OP_plus"; |
| case DW_OP_plus_uconst: |
| return "DW_OP_plus_uconst"; |
| case DW_OP_shl: |
| return "DW_OP_shl"; |
| case DW_OP_shr: |
| return "DW_OP_shr"; |
| case DW_OP_shra: |
| return "DW_OP_shra"; |
| case DW_OP_xor: |
| return "DW_OP_xor"; |
| case DW_OP_bra: |
| return "DW_OP_bra"; |
| case DW_OP_eq: |
| return "DW_OP_eq"; |
| case DW_OP_ge: |
| return "DW_OP_ge"; |
| case DW_OP_gt: |
| return "DW_OP_gt"; |
| case DW_OP_le: |
| return "DW_OP_le"; |
| case DW_OP_lt: |
| return "DW_OP_lt"; |
| case DW_OP_ne: |
| return "DW_OP_ne"; |
| case DW_OP_skip: |
| return "DW_OP_skip"; |
| case DW_OP_lit0: |
| return "DW_OP_lit0"; |
| case DW_OP_lit1: |
| return "DW_OP_lit1"; |
| case DW_OP_lit2: |
| return "DW_OP_lit2"; |
| case DW_OP_lit3: |
| return "DW_OP_lit3"; |
| case DW_OP_lit4: |
| return "DW_OP_lit4"; |
| case DW_OP_lit5: |
| return "DW_OP_lit5"; |
| case DW_OP_lit6: |
| return "DW_OP_lit6"; |
| case DW_OP_lit7: |
| return "DW_OP_lit7"; |
| case DW_OP_lit8: |
| return "DW_OP_lit8"; |
| case DW_OP_lit9: |
| return "DW_OP_lit9"; |
| case DW_OP_lit10: |
| return "DW_OP_lit10"; |
| case DW_OP_lit11: |
| return "DW_OP_lit11"; |
| case DW_OP_lit12: |
| return "DW_OP_lit12"; |
| case DW_OP_lit13: |
| return "DW_OP_lit13"; |
| case DW_OP_lit14: |
| return "DW_OP_lit14"; |
| case DW_OP_lit15: |
| return "DW_OP_lit15"; |
| case DW_OP_lit16: |
| return "DW_OP_lit16"; |
| case DW_OP_lit17: |
| return "DW_OP_lit17"; |
| case DW_OP_lit18: |
| return "DW_OP_lit18"; |
| case DW_OP_lit19: |
| return "DW_OP_lit19"; |
| case DW_OP_lit20: |
| return "DW_OP_lit20"; |
| case DW_OP_lit21: |
| return "DW_OP_lit21"; |
| case DW_OP_lit22: |
| return "DW_OP_lit22"; |
| case DW_OP_lit23: |
| return "DW_OP_lit23"; |
| case DW_OP_lit24: |
| return "DW_OP_lit24"; |
| case DW_OP_lit25: |
| return "DW_OP_lit25"; |
| case DW_OP_lit26: |
| return "DW_OP_lit26"; |
| case DW_OP_lit27: |
| return "DW_OP_lit27"; |
| case DW_OP_lit28: |
| return "DW_OP_lit28"; |
| case DW_OP_lit29: |
| return "DW_OP_lit29"; |
| case DW_OP_lit30: |
| return "DW_OP_lit30"; |
| case DW_OP_lit31: |
| return "DW_OP_lit31"; |
| case DW_OP_reg0: |
| return "DW_OP_reg0"; |
| case DW_OP_reg1: |
| return "DW_OP_reg1"; |
| case DW_OP_reg2: |
| return "DW_OP_reg2"; |
| case DW_OP_reg3: |
| return "DW_OP_reg3"; |
| case DW_OP_reg4: |
| return "DW_OP_reg4"; |
| case DW_OP_reg5: |
| return "DW_OP_reg5"; |
| case DW_OP_reg6: |
| return "DW_OP_reg6"; |
| case DW_OP_reg7: |
| return "DW_OP_reg7"; |
| case DW_OP_reg8: |
| return "DW_OP_reg8"; |
| case DW_OP_reg9: |
| return "DW_OP_reg9"; |
| case DW_OP_reg10: |
| return "DW_OP_reg10"; |
| case DW_OP_reg11: |
| return "DW_OP_reg11"; |
| case DW_OP_reg12: |
| return "DW_OP_reg12"; |
| case DW_OP_reg13: |
| return "DW_OP_reg13"; |
| case DW_OP_reg14: |
| return "DW_OP_reg14"; |
| case DW_OP_reg15: |
| return "DW_OP_reg15"; |
| case DW_OP_reg16: |
| return "DW_OP_reg16"; |
| case DW_OP_reg17: |
| return "DW_OP_reg17"; |
| case DW_OP_reg18: |
| return "DW_OP_reg18"; |
| case DW_OP_reg19: |
| return "DW_OP_reg19"; |
| case DW_OP_reg20: |
| return "DW_OP_reg20"; |
| case DW_OP_reg21: |
| return "DW_OP_reg21"; |
| case DW_OP_reg22: |
| return "DW_OP_reg22"; |
| case DW_OP_reg23: |
| return "DW_OP_reg23"; |
| case DW_OP_reg24: |
| return "DW_OP_reg24"; |
| case DW_OP_reg25: |
| return "DW_OP_reg25"; |
| case DW_OP_reg26: |
| return "DW_OP_reg26"; |
| case DW_OP_reg27: |
| return "DW_OP_reg27"; |
| case DW_OP_reg28: |
| return "DW_OP_reg28"; |
| case DW_OP_reg29: |
| return "DW_OP_reg29"; |
| case DW_OP_reg30: |
| return "DW_OP_reg30"; |
| case DW_OP_reg31: |
| return "DW_OP_reg31"; |
| case DW_OP_breg0: |
| return "DW_OP_breg0"; |
| case DW_OP_breg1: |
| return "DW_OP_breg1"; |
| case DW_OP_breg2: |
| return "DW_OP_breg2"; |
| case DW_OP_breg3: |
| return "DW_OP_breg3"; |
| case DW_OP_breg4: |
| return "DW_OP_breg4"; |
| case DW_OP_breg5: |
| return "DW_OP_breg5"; |
| case DW_OP_breg6: |
| return "DW_OP_breg6"; |
| case DW_OP_breg7: |
| return "DW_OP_breg7"; |
| case DW_OP_breg8: |
| return "DW_OP_breg8"; |
| case DW_OP_breg9: |
| return "DW_OP_breg9"; |
| case DW_OP_breg10: |
| return "DW_OP_breg10"; |
| case DW_OP_breg11: |
| return "DW_OP_breg11"; |
| case DW_OP_breg12: |
| return "DW_OP_breg12"; |
| case DW_OP_breg13: |
| return "DW_OP_breg13"; |
| case DW_OP_breg14: |
| return "DW_OP_breg14"; |
| case DW_OP_breg15: |
| return "DW_OP_breg15"; |
| case DW_OP_breg16: |
| return "DW_OP_breg16"; |
| case DW_OP_breg17: |
| return "DW_OP_breg17"; |
| case DW_OP_breg18: |
| return "DW_OP_breg18"; |
| case DW_OP_breg19: |
| return "DW_OP_breg19"; |
| case DW_OP_breg20: |
| return "DW_OP_breg20"; |
| case DW_OP_breg21: |
| return "DW_OP_breg21"; |
| case DW_OP_breg22: |
| return "DW_OP_breg22"; |
| case DW_OP_breg23: |
| return "DW_OP_breg23"; |
| case DW_OP_breg24: |
| return "DW_OP_breg24"; |
| case DW_OP_breg25: |
| return "DW_OP_breg25"; |
| case DW_OP_breg26: |
| return "DW_OP_breg26"; |
| case DW_OP_breg27: |
| return "DW_OP_breg27"; |
| case DW_OP_breg28: |
| return "DW_OP_breg28"; |
| case DW_OP_breg29: |
| return "DW_OP_breg29"; |
| case DW_OP_breg30: |
| return "DW_OP_breg30"; |
| case DW_OP_breg31: |
| return "DW_OP_breg31"; |
| case DW_OP_regx: |
| return "DW_OP_regx"; |
| case DW_OP_fbreg: |
| return "DW_OP_fbreg"; |
| case DW_OP_bregx: |
| return "DW_OP_bregx"; |
| case DW_OP_piece: |
| return "DW_OP_piece"; |
| case DW_OP_deref_size: |
| return "DW_OP_deref_size"; |
| case DW_OP_xderef_size: |
| return "DW_OP_xderef_size"; |
| case DW_OP_nop: |
| return "DW_OP_nop"; |
| case DW_OP_push_object_address: |
| return "DW_OP_push_object_address"; |
| case DW_OP_call2: |
| return "DW_OP_call2"; |
| case DW_OP_call4: |
| return "DW_OP_call4"; |
| case DW_OP_call_ref: |
| return "DW_OP_call_ref"; |
| case DW_OP_GNU_push_tls_address: |
| return "DW_OP_GNU_push_tls_address"; |
| default: |
| return "OP_<unknown>"; |
| } |
| } |
| |
| /* Return a pointer to a newly allocated location description. Location |
| descriptions are simple expression terms that can be strung |
| together to form more complicated location (address) descriptions. */ |
| |
| static inline dw_loc_descr_ref |
| new_loc_descr (enum dwarf_location_atom op, unsigned HOST_WIDE_INT oprnd1, |
| unsigned HOST_WIDE_INT oprnd2) |
| { |
| dw_loc_descr_ref descr = ggc_alloc_cleared (sizeof (dw_loc_descr_node)); |
| |
| descr->dw_loc_opc = op; |
| descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const; |
| descr->dw_loc_oprnd1.v.val_unsigned = oprnd1; |
| descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const; |
| descr->dw_loc_oprnd2.v.val_unsigned = oprnd2; |
| |
| return descr; |
| } |
| |
| |
| /* Add a location description term to a location description expression. */ |
| |
| static inline void |
| add_loc_descr (dw_loc_descr_ref *list_head, dw_loc_descr_ref descr) |
| { |
| dw_loc_descr_ref *d; |
| |
| /* Find the end of the chain. */ |
| for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next) |
| ; |
| |
| *d = descr; |
| } |
| |
| /* Return the size of a location descriptor. */ |
| |
| static unsigned long |
| size_of_loc_descr (dw_loc_descr_ref loc) |
| { |
| unsigned long size = 1; |
| |
| switch (loc->dw_loc_opc) |
| { |
| case DW_OP_addr: |
| case INTERNAL_DW_OP_tls_addr: |
| size += DWARF2_ADDR_SIZE; |
| break; |
| case DW_OP_const1u: |
| case DW_OP_const1s: |
| size += 1; |
| break; |
| case DW_OP_const2u: |
| case DW_OP_const2s: |
| size += 2; |
| break; |
| case DW_OP_const4u: |
| case DW_OP_const4s: |
| size += 4; |
| break; |
| case DW_OP_const8u: |
| case DW_OP_const8s: |
| size += 8; |
| break; |
| case DW_OP_constu: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| break; |
| case DW_OP_consts: |
| size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); |
| break; |
| case DW_OP_pick: |
| size += 1; |
| break; |
| case DW_OP_plus_uconst: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| break; |
| case DW_OP_skip: |
| case DW_OP_bra: |
| size += 2; |
| break; |
| case DW_OP_breg0: |
| case DW_OP_breg1: |
| case DW_OP_breg2: |
| case DW_OP_breg3: |
| case DW_OP_breg4: |
| case DW_OP_breg5: |
| case DW_OP_breg6: |
| case DW_OP_breg7: |
| case DW_OP_breg8: |
| case DW_OP_breg9: |
| case DW_OP_breg10: |
| case DW_OP_breg11: |
| case DW_OP_breg12: |
| case DW_OP_breg13: |
| case DW_OP_breg14: |
| case DW_OP_breg15: |
| case DW_OP_breg16: |
| case DW_OP_breg17: |
| case DW_OP_breg18: |
| case DW_OP_breg19: |
| case DW_OP_breg20: |
| case DW_OP_breg21: |
| case DW_OP_breg22: |
| case DW_OP_breg23: |
| case DW_OP_breg24: |
| case DW_OP_breg25: |
| case DW_OP_breg26: |
| case DW_OP_breg27: |
| case DW_OP_breg28: |
| case DW_OP_breg29: |
| case DW_OP_breg30: |
| case DW_OP_breg31: |
| size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); |
| break; |
| case DW_OP_regx: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| break; |
| case DW_OP_fbreg: |
| size += size_of_sleb128 (loc->dw_loc_oprnd1.v.val_int); |
| break; |
| case DW_OP_bregx: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| size += size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); |
| break; |
| case DW_OP_piece: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| break; |
| case DW_OP_deref_size: |
| case DW_OP_xderef_size: |
| size += 1; |
| break; |
| case DW_OP_call2: |
| size += 2; |
| break; |
| case DW_OP_call4: |
| size += 4; |
| break; |
| case DW_OP_call_ref: |
| size += DWARF2_ADDR_SIZE; |
| break; |
| default: |
| break; |
| } |
| |
| return size; |
| } |
| |
| /* Return the size of a series of location descriptors. */ |
| |
| static unsigned long |
| size_of_locs (dw_loc_descr_ref loc) |
| { |
| unsigned long size; |
| |
| for (size = 0; loc != NULL; loc = loc->dw_loc_next) |
| { |
| loc->dw_loc_addr = size; |
| size += size_of_loc_descr (loc); |
| } |
| |
| return size; |
| } |
| |
| /* Output location description stack opcode's operands (if any). */ |
| |
| static void |
| output_loc_operands (dw_loc_descr_ref loc) |
| { |
| dw_val_ref val1 = &loc->dw_loc_oprnd1; |
| dw_val_ref val2 = &loc->dw_loc_oprnd2; |
| |
| switch (loc->dw_loc_opc) |
| { |
| #ifdef DWARF2_DEBUGGING_INFO |
| case DW_OP_addr: |
| dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, val1->v.val_addr, NULL); |
| break; |
| case DW_OP_const2u: |
| case DW_OP_const2s: |
| dw2_asm_output_data (2, val1->v.val_int, NULL); |
| break; |
| case DW_OP_const4u: |
| case DW_OP_const4s: |
| dw2_asm_output_data (4, val1->v.val_int, NULL); |
| break; |
| case DW_OP_const8u: |
| case DW_OP_const8s: |
| if (HOST_BITS_PER_LONG < 64) |
| abort (); |
| dw2_asm_output_data (8, val1->v.val_int, NULL); |
| break; |
| case DW_OP_skip: |
| case DW_OP_bra: |
| { |
| int offset; |
| |
| if (val1->val_class == dw_val_class_loc) |
| offset = val1->v.val_loc->dw_loc_addr - (loc->dw_loc_addr + 3); |
| else |
| abort (); |
| |
| dw2_asm_output_data (2, offset, NULL); |
| } |
| break; |
| #else |
| case DW_OP_addr: |
| case DW_OP_const2u: |
| case DW_OP_const2s: |
| case DW_OP_const4u: |
| case DW_OP_const4s: |
| case DW_OP_const8u: |
| case DW_OP_const8s: |
| case DW_OP_skip: |
| case DW_OP_bra: |
| /* We currently don't make any attempt to make sure these are |
| aligned properly like we do for the main unwind info, so |
| don't support emitting things larger than a byte if we're |
| only doing unwinding. */ |
| abort (); |
| #endif |
| case DW_OP_const1u: |
| case DW_OP_const1s: |
| dw2_asm_output_data (1, val1->v.val_int, NULL); |
| break; |
| case DW_OP_constu: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| break; |
| case DW_OP_consts: |
| dw2_asm_output_data_sleb128 (val1->v.val_int, NULL); |
| break; |
| case DW_OP_pick: |
| dw2_asm_output_data (1, val1->v.val_int, NULL); |
| break; |
| case DW_OP_plus_uconst: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| break; |
| case DW_OP_breg0: |
| case DW_OP_breg1: |
| case DW_OP_breg2: |
| case DW_OP_breg3: |
| case DW_OP_breg4: |
| case DW_OP_breg5: |
| case DW_OP_breg6: |
| case DW_OP_breg7: |
| case DW_OP_breg8: |
| case DW_OP_breg9: |
| case DW_OP_breg10: |
| case DW_OP_breg11: |
| case DW_OP_breg12: |
| case DW_OP_breg13: |
| case DW_OP_breg14: |
| case DW_OP_breg15: |
| case DW_OP_breg16: |
| case DW_OP_breg17: |
| case DW_OP_breg18: |
| case DW_OP_breg19: |
| case DW_OP_breg20: |
| case DW_OP_breg21: |
| case DW_OP_breg22: |
| case DW_OP_breg23: |
| case DW_OP_breg24: |
| case DW_OP_breg25: |
| case DW_OP_breg26: |
| case DW_OP_breg27: |
| case DW_OP_breg28: |
| case DW_OP_breg29: |
| case DW_OP_breg30: |
| case DW_OP_breg31: |
| dw2_asm_output_data_sleb128 (val1->v.val_int, NULL); |
| break; |
| case DW_OP_regx: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| break; |
| case DW_OP_fbreg: |
| dw2_asm_output_data_sleb128 (val1->v.val_int, NULL); |
| break; |
| case DW_OP_bregx: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| dw2_asm_output_data_sleb128 (val2->v.val_int, NULL); |
| break; |
| case DW_OP_piece: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| break; |
| case DW_OP_deref_size: |
| case DW_OP_xderef_size: |
| dw2_asm_output_data (1, val1->v.val_int, NULL); |
| break; |
| |
| case INTERNAL_DW_OP_tls_addr: |
| #ifdef ASM_OUTPUT_DWARF_DTPREL |
| ASM_OUTPUT_DWARF_DTPREL (asm_out_file, DWARF2_ADDR_SIZE, |
| val1->v.val_addr); |
| fputc ('\n', asm_out_file); |
| #else |
| abort (); |
| #endif |
| break; |
| |
| default: |
| /* Other codes have no operands. */ |
| break; |
| } |
| } |
| |
| /* Output a sequence of location operations. */ |
| |
| static void |
| output_loc_sequence (dw_loc_descr_ref loc) |
| { |
| for (; loc != NULL; loc = loc->dw_loc_next) |
| { |
| /* Output the opcode. */ |
| dw2_asm_output_data (1, loc->dw_loc_opc, |
| "%s", dwarf_stack_op_name (loc->dw_loc_opc)); |
| |
| /* Output the operand(s) (if any). */ |
| output_loc_operands (loc); |
| } |
| } |
| |
| /* This routine will generate the correct assembly data for a location |
| description based on a cfi entry with a complex address. */ |
| |
| static void |
| output_cfa_loc (dw_cfi_ref cfi) |
| { |
| dw_loc_descr_ref loc; |
| unsigned long size; |
| |
| /* Output the size of the block. */ |
| loc = cfi->dw_cfi_oprnd1.dw_cfi_loc; |
| size = size_of_locs (loc); |
| dw2_asm_output_data_uleb128 (size, NULL); |
| |
| /* Now output the operations themselves. */ |
| output_loc_sequence (loc); |
| } |
| |
| /* This function builds a dwarf location descriptor sequence from |
| a dw_cfa_location. */ |
| |
| static struct dw_loc_descr_struct * |
| build_cfa_loc (dw_cfa_location *cfa) |
| { |
| struct dw_loc_descr_struct *head, *tmp; |
| |
| if (cfa->indirect == 0) |
| abort (); |
| |
| if (cfa->base_offset) |
| { |
| if (cfa->reg <= 31) |
| head = new_loc_descr (DW_OP_breg0 + cfa->reg, cfa->base_offset, 0); |
| else |
| head = new_loc_descr (DW_OP_bregx, cfa->reg, cfa->base_offset); |
| } |
| else if (cfa->reg <= 31) |
| head = new_loc_descr (DW_OP_reg0 + cfa->reg, 0, 0); |
| else |
| head = new_loc_descr (DW_OP_regx, cfa->reg, 0); |
| |
| head->dw_loc_oprnd1.val_class = dw_val_class_const; |
| tmp = new_loc_descr (DW_OP_deref, 0, 0); |
| add_loc_descr (&head, tmp); |
| if (cfa->offset != 0) |
| { |
| tmp = new_loc_descr (DW_OP_plus_uconst, cfa->offset, 0); |
| add_loc_descr (&head, tmp); |
| } |
| |
| return head; |
| } |
| |
| /* This function fills in aa dw_cfa_location structure from a dwarf location |
| descriptor sequence. */ |
| |
| static void |
| get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_struct *loc) |
| { |
| struct dw_loc_descr_struct *ptr; |
| cfa->offset = 0; |
| cfa->base_offset = 0; |
| cfa->indirect = 0; |
| cfa->reg = -1; |
| |
| for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next) |
| { |
| enum dwarf_location_atom op = ptr->dw_loc_opc; |
| |
| switch (op) |
| { |
| case DW_OP_reg0: |
| case DW_OP_reg1: |
| case DW_OP_reg2: |
| case DW_OP_reg3: |
| case DW_OP_reg4: |
| case DW_OP_reg5: |
| case DW_OP_reg6: |
| case DW_OP_reg7: |
| case DW_OP_reg8: |
| case DW_OP_reg9: |
| case DW_OP_reg10: |
| case DW_OP_reg11: |
| case DW_OP_reg12: |
| case DW_OP_reg13: |
| case DW_OP_reg14: |
| case DW_OP_reg15: |
| case DW_OP_reg16: |
| case DW_OP_reg17: |
| case DW_OP_reg18: |
| case DW_OP_reg19: |
| case DW_OP_reg20: |
| case DW_OP_reg21: |
| case DW_OP_reg22: |
| case DW_OP_reg23: |
| case DW_OP_reg24: |
| case DW_OP_reg25: |
| case DW_OP_reg26: |
| case DW_OP_reg27: |
| case DW_OP_reg28: |
| case DW_OP_reg29: |
| case DW_OP_reg30: |
| case DW_OP_reg31: |
| cfa->reg = op - DW_OP_reg0; |
| break; |
| case DW_OP_regx: |
| cfa->reg = ptr->dw_loc_oprnd1.v.val_int; |
| break; |
| case DW_OP_breg0: |
| case DW_OP_breg1: |
| case DW_OP_breg2: |
| case DW_OP_breg3: |
| case DW_OP_breg4: |
| case DW_OP_breg5: |
| case DW_OP_breg6: |
| case DW_OP_breg7: |
| case DW_OP_breg8: |
| case DW_OP_breg9: |
| case DW_OP_breg10: |
| case DW_OP_breg11: |
| case DW_OP_breg12: |
| case DW_OP_breg13: |
| case DW_OP_breg14: |
| case DW_OP_breg15: |
| case DW_OP_breg16: |
| case DW_OP_breg17: |
| case DW_OP_breg18: |
| case DW_OP_breg19: |
| case DW_OP_breg20: |
| case DW_OP_breg21: |
| case DW_OP_breg22: |
| case DW_OP_breg23: |
| case DW_OP_breg24: |
| case DW_OP_breg25: |
| case DW_OP_breg26: |
| case DW_OP_breg27: |
| case DW_OP_breg28: |
| case DW_OP_breg29: |
| case DW_OP_breg30: |
| case DW_OP_breg31: |
| cfa->reg = op - DW_OP_breg0; |
| cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int; |
| break; |
| case DW_OP_bregx: |
| cfa->reg = ptr->dw_loc_oprnd1.v.val_int; |
| cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int; |
| break; |
| case DW_OP_deref: |
| cfa->indirect = 1; |
| break; |
| case DW_OP_plus_uconst: |
| cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned; |
| break; |
| default: |
| internal_error ("DW_LOC_OP %s not implemented\n", |
| dwarf_stack_op_name (ptr->dw_loc_opc)); |
| } |
| } |
| } |
| #endif /* .debug_frame support */ |
| |
| /* And now, the support for symbolic debugging information. */ |
| #ifdef DWARF2_DEBUGGING_INFO |
| |
| /* .debug_str support. */ |
| static int output_indirect_string (void **, void *); |
| |
| static void dwarf2out_init (const char *); |
| static void dwarf2out_finish (const char *); |
| static void dwarf2out_define (unsigned int, const char *); |
| static void dwarf2out_undef (unsigned int, const char *); |
| static void dwarf2out_start_source_file (unsigned, const char *); |
| static void dwarf2out_end_source_file (unsigned); |
| static void dwarf2out_begin_block (unsigned, unsigned); |
| static void dwarf2out_end_block (unsigned, unsigned); |
| static bool dwarf2out_ignore_block (tree); |
| static void dwarf2out_global_decl (tree); |
| static void dwarf2out_abstract_function (tree); |
| |
| /* The debug hooks structure. */ |
| |
| const struct gcc_debug_hooks dwarf2_debug_hooks = |
| { |
| dwarf2out_init, |
| dwarf2out_finish, |
| dwarf2out_define, |
| dwarf2out_undef, |
| dwarf2out_start_source_file, |
| dwarf2out_end_source_file, |
| dwarf2out_begin_block, |
| dwarf2out_end_block, |
| dwarf2out_ignore_block, |
| dwarf2out_source_line, |
| dwarf2out_begin_prologue, |
| debug_nothing_int_charstar, /* end_prologue */ |
| dwarf2out_end_epilogue, |
| debug_nothing_tree, /* begin_function */ |
| debug_nothing_int, /* end_function */ |
| dwarf2out_decl, /* function_decl */ |
| dwarf2out_global_decl, |
| debug_nothing_tree, /* deferred_inline_function */ |
| /* The DWARF 2 backend tries to reduce debugging bloat by not |
| emitting the abstract description of inline functions until |
| something tries to reference them. */ |
| dwarf2out_abstract_function, /* outlining_inline_function */ |
| debug_nothing_rtx, /* label */ |
| debug_nothing_int /* handle_pch */ |
| }; |
| #endif |
| |
| /* 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. */ |
| |
| /* 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 dw_attr_struct *dw_attr_ref; |
| typedef struct dw_line_info_struct *dw_line_info_ref; |
| typedef struct dw_separate_line_info_struct <
|