| /* Output Dwarf2 format symbol table information from GCC. |
| Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, |
| 2003, 2004, 2005, 2006 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, 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301, 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 "version.h" |
| /* APPLE LOCAL radar 2338865 optimization notification */ |
| #include "options.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" |
| #include "input.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 */ |
| |
| #ifndef DWARF2_FRAME_INFO |
| # ifdef DWARF2_DEBUGGING_INFO |
| # define DWARF2_FRAME_INFO \ |
| (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) |
| # else |
| # define DWARF2_FRAME_INFO 0 |
| # endif |
| #endif |
| |
| /* 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 |
| |
| /* Decide whether we want to emit frame unwind information for the current |
| translation unit. */ |
| |
| int |
| dwarf2out_do_frame (void) |
| { |
| /* We want to emit correct CFA location expressions or lists, so we |
| have to return true if we're going to output debug info, even if |
| we're not going to output frame or unwind info. */ |
| return (write_symbols == DWARF2_DEBUG |
| || write_symbols == VMS_AND_DWARF2_DEBUG |
| || DWARF2_FRAME_INFO |
| #ifdef DWARF2_UNWIND_INFO |
| || (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 |
| |
| /* Array of RTXes referenced by the debugging information, which therefore |
| must be kept around forever. */ |
| static GTY(()) VEC(rtx,gc) *used_rtx_array; |
| |
| /* 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 |
| VEC(tree,gc) because we want to tell the garbage collector about |
| it. */ |
| static GTY(()) VEC(tree,gc) *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(()) VEC(tree,gc) *decl_scope_table; |
| |
| /* Pointers to various DWARF2 sections. */ |
| static GTY(()) section *debug_info_section; |
| static GTY(()) section *debug_abbrev_section; |
| static GTY(()) section *debug_aranges_section; |
| static GTY(()) section *debug_macinfo_section; |
| static GTY(()) section *debug_line_section; |
| static GTY(()) section *debug_loc_section; |
| static GTY(()) section *debug_pubnames_section; |
| /* APPLE LOCAL pubtypes, approved for 4.3 4535968 */ |
| static GTY(()) section *debug_pubtypes_section; |
| static GTY(()) section *debug_str_section; |
| static GTY(()) section *debug_ranges_section; |
| static GTY(()) section *debug_frame_section; |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| static GTY(()) section *debug_inlined_section; |
| |
| /* 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 int 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(()) |
| { |
| HOST_WIDE_INT offset; |
| HOST_WIDE_INT base_offset; |
| unsigned int reg; |
| 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(()) |
| { |
| tree decl; |
| const char *dw_fde_begin; |
| const char *dw_fde_current_label; |
| const char *dw_fde_end; |
| const char *dw_fde_hot_section_label; |
| const char *dw_fde_hot_section_end_label; |
| const char *dw_fde_unlikely_section_label; |
| const char *dw_fde_unlikely_section_end_label; |
| bool dw_fde_switched_sections; |
| 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 |
| |
| /* CIE identifier. */ |
| #if HOST_BITS_PER_WIDE_INT >= 64 |
| #define DWARF_CIE_ID \ |
| (unsigned HOST_WIDE_INT) (DWARF_OFFSET_SIZE == 4 ? DW_CIE_ID : DW64_CIE_ID) |
| #else |
| #define DWARF_CIE_ID DW_CIE_ID |
| #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; |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| bool is_fn_name; |
| 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, bool); |
| 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 *, HOST_WIDE_INT); |
| 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 |
| |
| #ifndef FRAME_BEGIN_LABEL |
| #define FRAME_BEGIN_LABEL "Lframe" |
| #endif |
| #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 |
| |
| /* APPLE LOCAL begin differentiate between arm & thumb. */ |
| #define DW_ISA_UNKNOWN 0 |
| #define DW_ISA_ARM_thumb 1 |
| #define DW_ISA_ARM_arm 2 |
| #define DW_ISA_USE_STMT_LIST -1 |
| /* APPLE LOCAL end differentiate between arm & thumb. */ |
| |
| /* Hook used by __throw. */ |
| |
| rtx |
| expand_builtin_dwarf_sp_column (void) |
| { |
| unsigned int dwarf_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM); |
| return GEN_INT (DWARF2_FRAME_REG_OUT (dwarf_regnum, 1)); |
| } |
| |
| /* 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 = XNEWVEC (char, 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) |
| { |
| unsigned int i; |
| enum machine_mode mode = TYPE_MODE (char_type_node); |
| rtx addr = expand_normal (address); |
| rtx mem = gen_rtx_MEM (BLKmode, addr); |
| bool wrote_return_column = false; |
| |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| { |
| int rnum = DWARF2_FRAME_REG_OUT (DWARF_FRAME_REGNUM (i), 1); |
| |
| if (rnum < DWARF_FRAME_REGISTERS) |
| { |
| HOST_WIDE_INT offset = rnum * 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_mode (size, mode)); |
| } |
| } |
| |
| #ifdef DWARF_ALT_FRAME_RETURN_COLUMN |
| gcc_assert (wrote_return_column); |
| 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; |
| |
| label = xstrdup (label); |
| |
| /* Set the location counter to the new label. */ |
| xcfi = new_cfi (); |
| /* If we have a current label, advance from there, otherwise |
| set the location directly using set_loc. */ |
| xcfi->dw_cfi_opc = fde->dw_fde_current_label |
| ? DW_CFA_advance_loc4 |
| : DW_CFA_set_loc; |
| xcfi->dw_cfi_oprnd1.dw_cfi_addr = label; |
| add_cfi (&fde->dw_fde_cfi, xcfi); |
| |
| fde->dw_fde_current_label = label; |
| } |
| |
| add_cfi (&fde->dw_fde_cfi, cfi); |
| } |
| |
| else |
| add_cfi (&cie_cfi_head, cfi); |
| } |
| |
| /* Subroutine of lookup_cfa. */ |
| |
| static 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_offset_sf: |
| loc->offset |
| = cfi->dw_cfi_oprnd1.dw_cfi_offset * DWARF_CIE_DATA_ALIGNMENT; |
| 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_sf: |
| loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; |
| loc->offset |
| = cfi->dw_cfi_oprnd2.dw_cfi_offset * DWARF_CIE_DATA_ALIGNMENT; |
| 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 = INVALID_REGNUM; |
| 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); |
| } |
| |
| /* Determine if two dw_cfa_location structures define the same data. */ |
| |
| static bool |
| cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2) |
| { |
| return (loc1->reg == loc2->reg |
| && loc1->offset == loc2->offset |
| && loc1->indirect == loc2->indirect |
| && (loc1->indirect == 0 |
| || loc1->base_offset == loc2->base_offset)); |
| } |
| |
| /* 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 (cfa_equal_p (&loc, &old_cfa)) |
| 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. */ |
| if (loc.offset < 0) |
| { |
| HOST_WIDE_INT f_offset = loc.offset / DWARF_CIE_DATA_ALIGNMENT; |
| gcc_assert (f_offset * DWARF_CIE_DATA_ALIGNMENT == loc.offset); |
| |
| cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf; |
| cfi->dw_cfi_oprnd1.dw_cfi_offset = f_offset; |
| } |
| else |
| { |
| 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 != INVALID_REGNUM |
| && !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. */ |
| if (loc.offset < 0) |
| { |
| HOST_WIDE_INT f_offset = loc.offset / DWARF_CIE_DATA_ALIGNMENT; |
| gcc_assert (f_offset * DWARF_CIE_DATA_ALIGNMENT == loc.offset); |
| |
| cfi->dw_cfi_opc = DW_CFA_def_cfa_sf; |
| cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg; |
| cfi->dw_cfi_oprnd2.dw_cfi_offset = f_offset; |
| } |
| else |
| { |
| 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, 0); |
| 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; |
| |
| if (sreg == INVALID_REGNUM) |
| { |
| 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; |
| |
| gcc_assert (check_offset * DWARF_CIE_DATA_ALIGNMENT == offset); |
| } |
| #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) |
| cfi->dw_cfi_opc = DW_CFA_same_value; |
| 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), INVALID_REGNUM, 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, INVALID_REGNUM, 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, DWARF_FRAME_REGNUM (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 = INVALID_REGNUM; |
| 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: |
| gcc_assert (REGNO (rtl) == STACK_POINTER_REGNUM); |
| offset = 0; |
| break; |
| |
| case PLUS: |
| gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); |
| offset = INTVAL (XEXP (rtl, 1)); |
| break; |
| |
| case MINUS: |
| gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); |
| offset = -INTVAL (XEXP (rtl, 1)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| 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. */ |
| gcc_assert (GET_CODE (XEXP (rtl, 1)) == CONST_INT); |
| initial_return_save (XEXP (rtl, 0)); |
| return; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (reg != DWARF_FRAME_RETURN_COLUMN) |
| 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 (MEM_P (dest)) |
| { |
| /* (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. */ |
| gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS |
| && GET_CODE (val) == CONST_INT); |
| 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, bool after_p) |
| { |
| 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 only calls can throw, and we have a frame pointer, |
| save up adjustments until we see the CALL_INSN. */ |
| if (!flag_asynchronous_unwind_tables && cfa.reg != STACK_POINTER_REGNUM) |
| { |
| if (CALL_P (insn) && !after_p) |
| { |
| /* 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); |
| gcc_assert (GET_CODE (insn) == CALL); |
| dwarf2out_args_size ("", INTVAL (XEXP (insn, 1))); |
| } |
| return; |
| } |
| |
| if (CALL_P (insn) && !after_p) |
| { |
| if (!flag_asynchronous_unwind_tables) |
| dwarf2out_args_size ("", args_size); |
| return; |
| } |
| else if (BARRIER_P (insn)) |
| { |
| /* 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); |
| if (flag_asynchronous_unwind_tables) |
| 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; |
| rtx saved_reg; |
| }; |
| |
| static GTY(()) struct queued_reg_save *queued_reg_saves; |
| |
| /* The caller's ORIG_REG is saved in SAVED_IN_REG. */ |
| struct reg_saved_in_data GTY(()) { |
| rtx orig_reg; |
| rtx saved_in_reg; |
| }; |
| |
| /* A list of registers saved in other registers. |
| The list intentionally has a small maximum capacity of 4; if your |
| port needs more than that, you might consider implementing a |
| more efficient data structure. */ |
| static GTY(()) struct reg_saved_in_data regs_saved_in_regs[4]; |
| static GTY(()) size_t num_regs_saved_in_regs; |
| |
| #if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO) |
| static const char *last_reg_save_label; |
| |
| /* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at |
| SREG, or if SREG is NULL then it is saved at OFFSET to the CFA. */ |
| |
| static void |
| queue_reg_save (const char *label, rtx reg, rtx sreg, HOST_WIDE_INT offset) |
| { |
| struct queued_reg_save *q; |
| |
| /* Duplicates waste space, but it's also necessary to remove them |
| for correctness, since the queue gets output in reverse |
| order. */ |
| for (q = queued_reg_saves; q != NULL; q = q->next) |
| if (REGNO (q->reg) == REGNO (reg)) |
| break; |
| |
| if (q == NULL) |
| { |
| q = ggc_alloc (sizeof (*q)); |
| q->next = queued_reg_saves; |
| queued_reg_saves = q; |
| } |
| |
| q->reg = reg; |
| q->cfa_offset = offset; |
| q->saved_reg = sreg; |
| |
| last_reg_save_label = label; |
| } |
| |
| /* Output all the entries in QUEUED_REG_SAVES. */ |
| |
| static void |
| flush_queued_reg_saves (void) |
| { |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| { |
| size_t i; |
| unsigned int reg, sreg; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (q->reg)) |
| break; |
| if (q->saved_reg && i == num_regs_saved_in_regs) |
| { |
| gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); |
| num_regs_saved_in_regs++; |
| } |
| if (i != num_regs_saved_in_regs) |
| { |
| regs_saved_in_regs[i].orig_reg = q->reg; |
| regs_saved_in_regs[i].saved_in_reg = q->saved_reg; |
| } |
| |
| reg = DWARF_FRAME_REGNUM (REGNO (q->reg)); |
| if (q->saved_reg) |
| sreg = DWARF_FRAME_REGNUM (REGNO (q->saved_reg)); |
| else |
| sreg = INVALID_REGNUM; |
| reg_save (last_reg_save_label, reg, sreg, q->cfa_offset); |
| } |
| |
| queued_reg_saves = NULL; |
| last_reg_save_label = NULL; |
| } |
| |
| /* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved |
| location for? Or, does it clobber a register which we've previously |
| said that some other register is saved in, and for which we now |
| have a new location for? */ |
| |
| static bool |
| clobbers_queued_reg_save (rtx insn) |
| { |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| { |
| size_t i; |
| if (modified_in_p (q->reg, insn)) |
| return true; |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (q->reg) == REGNO (regs_saved_in_regs[i].orig_reg) |
| && modified_in_p (regs_saved_in_regs[i].saved_in_reg, insn)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Entry point for saving the first register into the second. */ |
| |
| void |
| dwarf2out_reg_save_reg (const char *label, rtx reg, rtx sreg) |
| { |
| size_t i; |
| unsigned int regno, sregno; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (reg)) |
| break; |
| if (i == num_regs_saved_in_regs) |
| { |
| gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); |
| num_regs_saved_in_regs++; |
| } |
| regs_saved_in_regs[i].orig_reg = reg; |
| regs_saved_in_regs[i].saved_in_reg = sreg; |
| |
| regno = DWARF_FRAME_REGNUM (REGNO (reg)); |
| sregno = DWARF_FRAME_REGNUM (REGNO (sreg)); |
| reg_save (label, regno, sregno, 0); |
| } |
| |
| /* What register, if any, is currently saved in REG? */ |
| |
| static rtx |
| reg_saved_in (rtx reg) |
| { |
| unsigned int regn = REGNO (reg); |
| size_t i; |
| struct queued_reg_save *q; |
| |
| for (q = queued_reg_saves; q; q = q->next) |
| if (q->saved_reg && regn == REGNO (q->saved_reg)) |
| return q->reg; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| if (regs_saved_in_regs[i].saved_in_reg |
| && regn == REGNO (regs_saved_in_regs[i].saved_in_reg)) |
| return regs_saved_in_regs[i].orig_reg; |
| |
| return NULL_RTX; |
| } |
| |
| |
| /* 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 or another register. |
| 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. |
| It's also assumed that a copy from a call-saved register to another |
| register is saving that register if RTX_FRAME_RELATED_P is set on |
| that instruction. If the copy is from a call-saved register to |
| the *same* register, that means that the register is now the same |
| value as in the caller. |
| |
| 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. |
| |
| In addition, if a register has previously been saved to a different |
| register, |
| |
| 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) |
| |
| Rule 15: |
| (set <reg> {unspec, unspec_volatile}) |
| effects: target-dependent */ |
| |
| 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; |
| } |
| |
| gcc_assert (GET_CODE (expr) == SET); |
| |
| src = SET_SRC (expr); |
| dest = SET_DEST (expr); |
| |
| if (REG_P (src)) |
| { |
| rtx rsi = reg_saved_in (src); |
| if (rsi) |
| src = rsi; |
| } |
| |
| switch (GET_CODE (dest)) |
| { |
| case REG: |
| switch (GET_CODE (src)) |
| { |
| /* Setting FP from SP. */ |
| case REG: |
| if (cfa.reg == (unsigned) REGNO (src)) |
| { |
| /* Rule 1 */ |
| /* Update the CFA rule wrt SP or FP. Make sure src is |
| relative to the current CFA register. |
| |
| 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; |
| } |
| else |
| { |
| /* Saving a register in a register. */ |
| gcc_assert (!fixed_regs [REGNO (dest)] |
| /* For the SPARC and its register window. */ |
| || (DWARF_FRAME_REGNUM (REGNO (src)) |
| == DWARF_FRAME_RETURN_COLUMN)); |
| queue_reg_save (label, src, dest, 0); |
| } |
| 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: |
| gcc_assert ((unsigned) REGNO (XEXP (src, 1)) |
| == cfa_temp.reg); |
| offset = cfa_temp.offset; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (XEXP (src, 0) == hard_frame_pointer_rtx) |
| { |
| /* Restoring SP from FP in the epilogue. */ |
| gcc_assert (cfa.reg == (unsigned) HARD_FRAME_POINTER_REGNUM); |
| 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 |
| gcc_assert (XEXP (src, 0) == stack_pointer_rtx); |
| |
| 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 */ |
| gcc_assert (frame_pointer_needed); |
| |
| gcc_assert (REG_P (XEXP (src, 0)) |
| && (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 |
| { |
| gcc_assert (GET_CODE (src) != MINUS); |
| |
| /* Rule 4 */ |
| if (REG_P (XEXP (src, 0)) |
| && 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 (REG_P (XEXP (src, 0)) |
| && 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. */ |
| gcc_assert (cfa.reg == STACK_POINTER_REGNUM); |
| 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 |
| gcc_unreachable (); |
| } |
| break; |
| |
| /* Rule 6 */ |
| case CONST_INT: |
| cfa_temp.reg = REGNO (dest); |
| cfa_temp.offset = INTVAL (src); |
| break; |
| |
| /* Rule 7 */ |
| case IOR: |
| gcc_assert (REG_P (XEXP (src, 0)) |
| && (unsigned) REGNO (XEXP (src, 0)) == cfa_temp.reg |
| && GET_CODE (XEXP (src, 1)) == CONST_INT); |
| |
| 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; |
| |
| /* Rule 15 */ |
| case UNSPEC: |
| case UNSPEC_VOLATILE: |
| gcc_assert (targetm.dwarf_handle_frame_unspec); |
| targetm.dwarf_handle_frame_unspec (label, expr, XINT (src, 1)); |
| return; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| def_cfa_1 (label, &cfa); |
| break; |
| |
| case MEM: |
| gcc_assert (REG_P (src)); |
| |
| /* 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. */ |
| gcc_assert (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) |
| == CONST_INT); |
| offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)); |
| |
| gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM |
| && cfa_store.reg == STACK_POINTER_REGNUM); |
| |
| cfa_store.offset += offset; |
| if (cfa.reg == STACK_POINTER_REGNUM) |
| cfa.offset = cfa_store.offset; |
| |
| offset = -cfa_store.offset; |
| break; |
| |
| /* 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; |
| |
| gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM |
| && cfa_store.reg == STACK_POINTER_REGNUM); |
| |
| cfa_store.offset += offset; |
| if (cfa.reg == STACK_POINTER_REGNUM) |
| cfa.offset = cfa_store.offset; |
| |
| offset = -cfa_store.offset; |
| break; |
| |
| /* Rule 12 */ |
| /* With an offset. */ |
| case PLUS: |
| case MINUS: |
| case LO_SUM: |
| { |
| int regno; |
| |
| gcc_assert (GET_CODE (XEXP (XEXP (dest, 0), 1)) == CONST_INT |
| && REG_P (XEXP (XEXP (dest, 0), 0))); |
| offset = INTVAL (XEXP (XEXP (dest, 0), 1)); |
| if (GET_CODE (XEXP (dest, 0)) == MINUS) |
| offset = -offset; |
| |
| regno = REGNO (XEXP (XEXP (dest, 0), 0)); |
| |
| if (cfa_store.reg == (unsigned) regno) |
| offset -= cfa_store.offset; |
| else |
| { |
| gcc_assert (cfa_temp.reg == (unsigned) regno); |
| offset -= cfa_temp.offset; |
| } |
| } |
| break; |
| |
| /* Rule 13 */ |
| /* Without an offset. */ |
| case REG: |
| { |
| int regno = REGNO (XEXP (dest, 0)); |
| |
| if (cfa_store.reg == (unsigned) regno) |
| offset = -cfa_store.offset; |
| else |
| { |
| gcc_assert (cfa_temp.reg == (unsigned) regno); |
| offset = -cfa_temp.offset; |
| } |
| } |
| break; |
| |
| /* Rule 14 */ |
| case POST_INC: |
| gcc_assert (cfa_temp.reg |
| == (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))); |
| offset = -cfa_temp.offset; |
| cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| 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, NULL_RTX, offset); |
| break; |
| } |
| else |
| { |
| /* Otherwise, we'll need to look in the stack to |
| calculate the CFA. */ |
| rtx x = XEXP (dest, 0); |
| |
| if (!REG_P (x)) |
| x = XEXP (x, 0); |
| gcc_assert (REG_P (x)); |
| |
| 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, NULL_RTX, offset); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* 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. |
| |
| If AFTER_P is false, we're being called before the insn is emitted, |
| otherwise after. Call instructions get invoked twice. */ |
| |
| void |
| dwarf2out_frame_debug (rtx insn, bool after_p) |
| { |
| const char *label; |
| rtx src; |
| |
| if (insn == NULL_RTX) |
| { |
| size_t i; |
| |
| /* Flush any queued register saves. */ |
| flush_queued_reg_saves (); |
| |
| /* Set up state for generating call frame debug info. */ |
| lookup_cfa (&cfa); |
| gcc_assert (cfa.reg |
| == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)); |
| |
| cfa.reg = STACK_POINTER_REGNUM; |
| cfa_store = cfa; |
| cfa_temp.reg = -1; |
| cfa_temp.offset = 0; |
| |
| for (i = 0; i < num_regs_saved_in_regs; i++) |
| { |
| regs_saved_in_regs[i].orig_reg = NULL_RTX; |
| regs_saved_in_regs[i].saved_in_reg = NULL_RTX; |
| } |
| num_regs_saved_in_regs = 0; |
| return; |
| } |
| |
| if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn)) |
| flush_queued_reg_saves (); |
| |
| if (! RTX_FRAME_RELATED_P (insn)) |
| { |
| if (!ACCUMULATE_OUTGOING_ARGS) |
| dwarf2out_stack_adjust (insn, after_p); |
| 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: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* 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) |
| |
| /* Switch to eh_frame_section. If we don't have an eh_frame_section, |
| switch to the data section instead, and write out a synthetic label |
| for collect2. */ |
| |
| static void |
| switch_to_eh_frame_section (void) |
| { |
| tree label; |
| |
| #ifdef EH_FRAME_SECTION_NAME |
| if (eh_frame_section == 0) |
| { |
| int flags; |
| |
| if (EH_TABLES_CAN_BE_READ_ONLY) |
| { |
| int fde_encoding; |
| int per_encoding; |
| int lsda_encoding; |
| |
| 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); |
| 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); |
| } |
| else |
| flags = SECTION_WRITE; |
| eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL); |
| } |
| #endif |
| |
| if (eh_frame_section) |
| switch_to_section (eh_frame_section); |
| else |
| { |
| /* We have no special eh_frame section. Put the information in |
| the data section and emit special labels to guide collect2. */ |
| switch_to_section (data_section); |
| /* APPLE LOCAL begin mainline 2006-11-01 5125268 */ \ |
| label = get_file_function_name ("F"); |
| /* APPLE LOCAL end mainline 2006-11-01 5125268 */ \ |
| 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)); |
| } |
| } |
| |
| /* 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), |
| false, NULL); |
| else |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| cfi->dw_cfi_oprnd1.dw_cfi_addr, NULL); |
| fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| 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. */ |
| gcc_unreachable (); |
| |
| 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; |
| int return_reg; |
| |
| /* Don't emit a CIE if there won't be any FDEs. */ |
| if (fde_table_in_use == 0) |
| return; |
| |
| /* If we make FDEs linkonce, we may have to emit an empty label for |
| an FDE that wouldn't otherwise be emitted. We want to avoid |
| having an FDE kept around when the function it refers to is |
| discarded. Example where this matters: a primary function |
| template in C++ requires EH information, but an explicit |
| specialization doesn't. */ |
| if (TARGET_USES_WEAK_UNWIND_INFO |
| && ! flag_asynchronous_unwind_tables |
| /* APPLE LOCAL begin for-fsf-4_4 5480287 */ \ |
| && flag_exceptions |
| /* APPLE LOCAL end for-fsf-4_4 5480287 */ \ |
| && for_eh) |
| for (i = 0; i < fde_table_in_use; i++) |
| if ((fde_table[i].nothrow || fde_table[i].all_throwers_are_sibcalls) |
| && !fde_table[i].uses_eh_lsda |
| && ! DECL_WEAK (fde_table[i].decl)) |
| targetm.asm_out.unwind_label (asm_out_file, fde_table[i].decl, |
| for_eh, /* empty */ 1); |
| |
| /* 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 (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl)) |
| any_eh_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) |
| switch_to_eh_frame_section (); |
| else |
| { |
| if (!debug_frame_section) |
| debug_frame_section = get_section (DEBUG_FRAME_SECTION, |
| SECTION_DEBUG, NULL); |
| switch_to_section (debug_frame_section); |
| } |
| |
| 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); |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| 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 : DWARF_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. */ |
| gcc_assert (size_of_uleb128 (augmentation_size) == 1); |
| } |
| } |
| |
| 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"); |
| |
| return_reg = DWARF2_FRAME_REG_OUT (DWARF_FRAME_RETURN_COLUMN, for_eh); |
| if (DW_CIE_VERSION == 1) |
| dw2_asm_output_data (1, return_reg, "CIE RA Column"); |
| else |
| dw2_asm_output_data_uleb128 (return_reg, "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, |
| true, 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) |
| && ! (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl)) |
| && !fde->uses_eh_lsda) |
| continue; |
| |
| targetm.asm_out.unwind_label (asm_out_file, fde->decl, for_eh, /* empty */ 0); |
| 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); |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| 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, |
| debug_frame_section, "FDE CIE offset"); |
| |
| if (for_eh) |
| { |
| rtx sym_ref = gen_rtx_SYMBOL_REF (Pmode, fde->dw_fde_begin); |
| SYMBOL_REF_FLAGS (sym_ref) |= SYMBOL_FLAG_LOCAL; |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, |
| sym_ref, |
| false, |
| "FDE initial location"); |
| if (fde->dw_fde_switched_sections) |
| { |
| rtx sym_ref2 = gen_rtx_SYMBOL_REF (Pmode, |
| fde->dw_fde_unlikely_section_label); |
| rtx sym_ref3= gen_rtx_SYMBOL_REF (Pmode, |
| fde->dw_fde_hot_section_label); |
| SYMBOL_REF_FLAGS (sym_ref2) |= SYMBOL_FLAG_LOCAL; |
| SYMBOL_REF_FLAGS (sym_ref3) |= SYMBOL_FLAG_LOCAL; |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref3, false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_hot_section_end_label, |
| fde->dw_fde_hot_section_label, |
| "FDE address range"); |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref2, false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| fde->dw_fde_unlikely_section_end_label, |
| fde->dw_fde_unlikely_section_label, |
| "FDE address range"); |
| } |
| else |
| 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"); |
| if (fde->dw_fde_switched_sections) |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| fde->dw_fde_hot_section_label, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_hot_section_end_label, |
| fde->dw_fde_hot_section_label, |
| "FDE address range"); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, |
| fde->dw_fde_unlikely_section_label, |
| "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, |
| fde->dw_fde_unlikely_section_end_label, |
| fde->dw_fde_unlikely_section_label, |
| "FDE address range"); |
| } |
| else |
| 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; |
| gcc_assert (size_of_uleb128 (size) == 1); |
| } |
| |
| 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), |
| false, "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]; |
| char * dup_label; |
| dw_fde_ref fde; |
| |
| current_function_func_begin_label = NULL; |
| |
| #ifdef TARGET_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 |
| |
| switch_to_section (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); |
| dup_label = xstrdup (label); |
| current_function_func_begin_label = dup_label; |
| |
| #ifdef TARGET_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->decl = current_function_decl; |
| fde->dw_fde_begin = dup_label; |
| fde->dw_fde_current_label = dup_label; |
| fde->dw_fde_hot_section_label = NULL; |
| fde->dw_fde_hot_section_end_label = NULL; |
| fde->dw_fde_unlikely_section_label = NULL; |
| fde->dw_fde_unlikely_section_end_label = NULL; |
| fde->dw_fde_switched_sections = false; |
| fde->dw_fde_end = NULL; |
| fde->dw_fde_cfi = NULL; |
| fde->funcdef_number = current_function_funcdef_no; |
| fde->nothrow = TREE_NOTHROW (current_function_decl); |
| 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. */ |
| |
| /* On entry, the Canonical Frame Address is at SP. */ |
| dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET); |
| |
| #ifdef DWARF2_UNWIND_INFO |
| if (DWARF2_UNWIND_INFO) |
| initial_return_save (INCOMING_RETURN_ADDR_RTX); |
| #endif |
| } |
| |
| void |
| dwarf2out_frame_finish (void) |
| { |
| /* Output call frame information. */ |
| if (DWARF2_FRAME_INFO) |
| output_call_frame_info (0); |
| |
| #ifndef TARGET_UNWIND_INFO |
| /* Output another copy for the unwinder. */ |
| if (! USING_SJLJ_EXCEPTIONS && (flag_unwind_tables || flag_exceptions)) |
| output_call_frame_info (1); |
| #endif |
| } |
| #endif |
| |
| /* And now, the subset of the debugging information support code necessary |
| for emitting location expressions. */ |
| |
| /* Data about a single source file. */ |
| struct dwarf_file_data GTY(()) |
| { |
| const char * filename; |
| int emitted_number; |
| }; |
| |
| /* 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_lineptr, |
| dw_val_class_str, |
| dw_val_class_macptr, |
| dw_val_class_file |
| }; |
| |
| /* 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; |
| struct dwarf_file_data * GTY ((tag ("dw_val_class_file"))) val_file; |
| } |
| 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"; |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| case DW_OP_APPLE_uninit: |
| return "DW_OP_APPLE_uninit"; |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| 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) |
| { |
| dw_loc_descr_ref l; |
| unsigned long size; |
| |
| /* If there are no skip or bra opcodes, don't fill in the dw_loc_addr |
| field, to avoid writing to a PCH file. */ |
| for (size = 0, l = loc; l != NULL; l = l->dw_loc_next) |
| { |
| if (l->dw_loc_opc == DW_OP_skip || l->dw_loc_opc == DW_OP_bra) |
| break; |
| size += size_of_loc_descr (l); |
| } |
| if (! l) |
| return size; |
| |
| for (size = 0, l = loc; l != NULL; l = l->dw_loc_next) |
| { |
| l->dw_loc_addr = size; |
| size += size_of_loc_descr (l); |
| } |
| |
| 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: |
| gcc_assert (HOST_BITS_PER_LONG >= 64); |
| dw2_asm_output_data (8, val1->v.val_int, NULL); |
| break; |
| case DW_OP_skip: |
| case DW_OP_bra: |
| { |
| int offset; |
| |
| gcc_assert (val1->val_class == dw_val_class_loc); |
| offset = val1->v.val_loc->dw_loc_addr - (loc->dw_loc_addr + 3); |
| |
| 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. */ |
| gcc_unreachable (); |
| #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: |
| if (targetm.asm_out.output_dwarf_dtprel) |
| { |
| targetm.asm_out.output_dwarf_dtprel (asm_out_file, |
| DWARF2_ADDR_SIZE, |
| val1->v.val_addr); |
| fputc ('\n', asm_out_file); |
| } |
| else |
| gcc_unreachable (); |
| 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, adding the given OFFSET to the result of the |
| expression. */ |
| |
| static struct dw_loc_descr_struct * |
| build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset) |
| { |
| struct dw_loc_descr_struct *head, *tmp; |
| |
| offset += cfa->offset; |
| |
| if (cfa->indirect) |
| { |
| 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 (offset != 0) |
| { |
| tmp = new_loc_descr (DW_OP_plus_uconst, offset, 0); |
| add_loc_descr (&head, tmp); |
| } |
| } |
| else |
| { |
| if (offset == 0) |
| 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); |
| else if (cfa->reg <= 31) |
| head = new_loc_descr (DW_OP_breg0 + cfa->reg, offset, 0); |
| else |
| head = new_loc_descr (DW_OP_bregx, cfa->reg, offset); |
| } |
| |
| 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", |
| 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_type_decl (tree, int); |
| static void dwarf2out_imported_module_or_decl (tree, tree); |
| static void dwarf2out_abstract_function (tree); |
| static void dwarf2out_var_location (rtx); |
| static void dwarf2out_begin_function (tree); |
| /* APPLE LOCAL opt diary */ |
| static void dwarf2out_od_entry (enum debug_od_msg, expanded_location); |
| static void dwarf2out_switch_text_section (void); |
| |
| /* 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, |
| dwarf2out_begin_function, |
| debug_nothing_int, /* end_function */ |
| dwarf2out_decl, /* function_decl */ |
| dwarf2out_global_decl, |
| dwarf2out_type_decl, /* type_decl */ |
| dwarf2out_imported_module_or_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 */ |
| dwarf2out_var_location, |
| /* APPLE LOCAL opt diary */ |
| dwarf2out_od_entry, /* Optimization Diary Entry */ |
| dwarf2out_switch_text_section, |
| 1 /* start_end_main_source_file */ |
| }; |
| #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 *dw_separate_line_info_ref; |
| typedef struct pubname_struct *pubname_ref; |
| typedef struct dw_ranges_struct *dw_ranges_ref; |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| typedef struct inlined_entry_struct *inlined_ref; |
| |
| /* Each entry in the line_info_table maintains the file and |
| line number associated with the label generated for that |
| entry. The label gives the PC value associated with |
| the line number entry. */ |
| |
| typedef struct dw_line_info_struct GTY(()) |
| { |
| unsigned long dw_file_num; |
| unsigned long dw_line_num; |
| } |
| dw_line_info_entry; |
| |
| /* Line information for functions in separate sections; each one gets its |
| own sequence. */ |
| typedef struct dw_separate_line_info_struct GTY(()) |
| { |
| unsigned long dw_file_num; |
| unsigned long dw_line_num; |
| unsigned long function; |
| } |
| dw_separate_line_info_entry; |
| |
| /* Each DIE attribute has a field specifying the attribute kind, |
| a link to the next attribute in the chain, and an attribute value. |
| Attributes are typically linked below the DIE they modify. */ |
| |
| typedef struct dw_attr_struct GTY(()) |
| { |
| enum dwarf_attribute dw_attr; |
| dw_val_node dw_attr_val; |
| } |
| dw_attr_node; |
| |
| DEF_VEC_O(dw_attr_node); |
| DEF_VEC_ALLOC_O(dw_attr_node,gc); |
| |
| /* The Debugging Information Entry (DIE) structure. DIEs form a tree. |
| The children of each node form a circular list linked by |
| die_sib. die_child points to the node *before* the "first" child node. */ |
| |
| typedef struct die_struct GTY(()) |
| { |
| enum dwarf_tag die_tag; |
| char *die_symbol; |
| VEC(dw_attr_node,gc) * die_attr; |
| dw_die_ref die_parent; |
| dw_die_ref die_child; |
| dw_die_ref die_sib; |
| dw_die_ref die_definition; /* ref from a specification to its definition */ |
| dw_offset die_offset; |
| unsigned long die_abbrev; |
| int die_mark; |
| /* Die is used and must not be pruned as unused. */ |
| int die_perennial_p; |
| unsigned int decl_id; |
| } |
| die_node; |
| |
| /* Evaluate 'expr' while 'c' is set to each child of DIE in order. */ |
| #define FOR_EACH_CHILD(die, c, expr) do { \ |
| c = die->die_child; \ |
| if (c) do { \ |
| c = c->die_sib; \ |
| expr; \ |
| } while (c != die->die_child); \ |
| } while (0) |
| |
| /* The pubname structure */ |
| |
| typedef struct pubname_struct GTY(()) |
| { |
| dw_die_ref die; |
| char *name; |
| } |
| pubname_entry; |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| DEF_VEC_O(pubname_entry); |
| DEF_VEC_ALLOC_O(pubname_entry, gc); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| DEF_VEC_O(dw_die_ref); |
| DEF_VEC_ALLOC_O(dw_die_ref, gc); |
| |
| typedef struct inlined_entry_struct GTY (()) |
| { |
| dw_die_ref origin_die; |
| VEC(dw_die_ref,gc) *inlined_instances; |
| } |
| inlined_entry; |
| |
| DEF_VEC_O(inlined_entry); |
| DEF_VEC_ALLOC_O(inlined_entry, gc); |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| struct dw_ranges_struct GTY(()) |
| { |
| int block_num; |
| }; |
| |
| /* The limbo die list structure. */ |
| typedef struct limbo_die_struct GTY(()) |
| { |
| dw_die_ref die; |
| tree created_for; |
| struct limbo_die_struct *next; |
| } |
| limbo_die_node; |
| |
| /* How to start an assembler comment. */ |
| #ifndef ASM_COMMENT_START |
| #define ASM_COMMENT_START ";#" |
| #endif |
| |
| /* Define a macro which returns nonzero for a TYPE_DECL which was |
| implicitly generated for a tagged type. |
| |
| Note that unlike the gcc front end (which generates a NULL named |
| TYPE_DECL node for each complete tagged type, each array type, and |
| each function type node created) the g++ front end generates a |
| _named_ TYPE_DECL node for each tagged type node created. |
| These TYPE_DECLs have DECL_ARTIFICIAL set, so we know not to |
| generate a DW_TAG_typedef DIE for them. */ |
| |
| #define TYPE_DECL_IS_STUB(decl) \ |
| (DECL_NAME (decl) == NULL_TREE \ |
| || (DECL_ARTIFICIAL (decl) \ |
| && is_tagged_type (TREE_TYPE (decl)) \ |
| && ((decl == TYPE_STUB_DECL (TREE_TYPE (decl))) \ |
| /* This is necessary for stub decls that \ |
| appear in nested inline functions. */ \ |
| || (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE \ |
| && (decl_ultimate_origin (decl) \ |
| == TYPE_STUB_DECL (TREE_TYPE (decl))))))) |
| |
| /* Information concerning the compilation unit's programming |
| language, and compiler version. */ |
| |
| /* Fixed size portion of the DWARF compilation unit header. */ |
| #define DWARF_COMPILE_UNIT_HEADER_SIZE \ |
| (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 3) |
| |
| /* Fixed size portion of public names info. */ |
| #define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2) |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| /* Fixed size portion of inlined section info. */ |
| #define DWARF_INLINED_HEADER_SIZE (3) |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| /* Fixed size portion of the address range info. */ |
| #define DWARF_ARANGES_HEADER_SIZE \ |
| (DWARF_ROUND (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4, \ |
| DWARF2_ADDR_SIZE * 2) \ |
| - DWARF_INITIAL_LENGTH_SIZE) |
| |
| /* Size of padding portion in the address range info. It must be |
| aligned to twice the pointer size. */ |
| #define DWARF_ARANGES_PAD_SIZE \ |
| (DWARF_ROUND (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4, \ |
| DWARF2_ADDR_SIZE * 2) \ |
| - (DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 4)) |
| |
| /* Use assembler line directives if available. */ |
| #ifndef DWARF2_ASM_LINE_DEBUG_INFO |
| #ifdef HAVE_AS_DWARF2_DEBUG_LINE |
| #define DWARF2_ASM_LINE_DEBUG_INFO 1 |
| #else |
| #define DWARF2_ASM_LINE_DEBUG_INFO 0 |
| #endif |
| #endif |
| |
| /* Minimum line offset in a special line info. opcode. |
| This value was chosen to give a reasonable range of values. */ |
| #define DWARF_LINE_BASE -10 |
| |
| /* First special line opcode - leave room for the standard opcodes. */ |
| #define DWARF_LINE_OPCODE_BASE 10 |
| |
| /* Range of line offsets in a special line info. opcode. */ |
| #define DWARF_LINE_RANGE (254-DWARF_LINE_OPCODE_BASE+1) |
| |
| /* Flag that indicates the initial value of the is_stmt_start flag. |
| In the present implementation, we do not mark any lines as |
| the beginning of a source statement, because that information |
| is not made available by the GCC front-end. */ |
| #define DWARF_LINE_DEFAULT_IS_STMT_START 1 |
| |
| #ifdef DWARF2_DEBUGGING_INFO |
| /* This location is used by calc_die_sizes() to keep track |
| the offset of each DIE within the .debug_info section. */ |
| static unsigned long next_die_offset; |
| #endif |
| |
| /* Record the root of the DIE's built for the current compilation unit. */ |
| static GTY(()) dw_die_ref comp_unit_die; |
| |
| /* A list of DIEs with a NULL parent waiting to be relocated. */ |
| static GTY(()) limbo_die_node *limbo_die_list; |
| |
| /* Filenames referenced by this compilation unit. */ |
| static GTY((param_is (struct dwarf_file_data))) htab_t file_table; |
| |
| /* A hash table of references to DIE's that describe declarations. |
| The key is a DECL_UID() which is a unique number identifying each decl. */ |
| static GTY ((param_is (struct die_struct))) htab_t decl_die_table; |
| |
| /* Node of the variable location list. */ |
| struct var_loc_node GTY ((chain_next ("%h.next"))) |
| { |
| rtx GTY (()) var_loc_note; |
| const char * GTY (()) label; |
| const char * GTY (()) section_label; |
| struct var_loc_node * GTY (()) next; |
| }; |
| |
| /* Variable location list. */ |
| struct var_loc_list_def GTY (()) |
| { |
| struct var_loc_node * GTY (()) first; |
| |
| /* Do not mark the last element of the chained list because |
| it is marked through the chain. */ |
| struct var_loc_node * GTY ((skip ("%h"))) last; |
| |
| /* DECL_UID of the variable decl. */ |
| unsigned int decl_id; |
| }; |
| typedef struct var_loc_list_def var_loc_list; |
| |
| |
| /* Table of decl location linked lists. */ |
| static GTY ((param_is (var_loc_list))) htab_t decl_loc_table; |
| |
| /* A pointer to the base of a list of references to DIE's that |
| are uniquely identified by their tag, presence/absence of |
| children DIE's, and list of attribute/value pairs. */ |
| static GTY((length ("abbrev_die_table_allocated"))) |
| dw_die_ref *abbrev_die_table; |
| |
| /* Number of elements currently allocated for abbrev_die_table. */ |
| static GTY(()) unsigned abbrev_die_table_allocated; |
| |
| /* Number of elements in type_die_table currently in use. */ |
| static GTY(()) unsigned abbrev_die_table_in_use; |
| |
| /* Size (in elements) of increments by which we may expand the |
| abbrev_die_table. */ |
| #define ABBREV_DIE_TABLE_INCREMENT 256 |
| |
| /* A pointer to the base of a table that contains line information |
| for each source code line in .text in the compilation unit. */ |
| static GTY((length ("line_info_table_allocated"))) |
| dw_line_info_ref line_info_table; |
| |
| /* Number of elements currently allocated for line_info_table. */ |
| static GTY(()) unsigned line_info_table_allocated; |
| |
| /* Number of elements in line_info_table currently in use. */ |
| static GTY(()) unsigned line_info_table_in_use; |
| |
| /* True if the compilation unit places functions in more than one section. */ |
| static GTY(()) bool have_multiple_function_sections = false; |
| |
| /* A pointer to the base of a table that contains line information |
| for each source code line outside of .text in the compilation unit. */ |
| static GTY ((length ("separate_line_info_table_allocated"))) |
| dw_separate_line_info_ref separate_line_info_table; |
| |
| /* Number of elements currently allocated for separate_line_info_table. */ |
| static GTY(()) unsigned separate_line_info_table_allocated; |
| |
| /* Number of elements in separate_line_info_table currently in use. */ |
| static GTY(()) unsigned separate_line_info_table_in_use; |
| |
| /* Size (in elements) of increments by which we may expand the |
| line_info_table. */ |
| #define LINE_INFO_TABLE_INCREMENT 1024 |
| |
| /* A pointer to the base of a table that contains a list of publicly |
| accessible names. */ |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| static GTY (()) VEC (pubname_entry, gc) * pubname_table; |
| |
| /* A pointer to the base of a table that contains a list of publicy |
| accessible types. */ |
| |
| static GTY (()) VEC (pubname_entry, gc) * pubtype_table; |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| static GTY (()) VEC (inlined_entry, gc) * debug_inlined_table; |
| |
| /* Array of dies for which we should generate .debug_arange info. */ |
| static GTY((length ("arange_table_allocated"))) dw_die_ref *arange_table; |
| |
| /* Number of elements currently allocated for arange_table. */ |
| static GTY(()) unsigned arange_table_allocated; |
| |
| /* Number of elements in arange_table currently in use. */ |
| static GTY(()) unsigned arange_table_in_use; |
| |
| /* Size (in elements) of increments by which we may expand the |
| arange_table. */ |
| #define ARANGE_TABLE_INCREMENT 64 |
| |
| /* Array of dies for which we should generate .debug_ranges info. */ |
| static GTY ((length ("ranges_table_allocated"))) dw_ranges_ref ranges_table; |
| |
| /* Number of elements currently allocated for ranges_table. */ |
| static GTY(()) unsigned ranges_table_allocated; |
| |
| /* Number of elements in ranges_table currently in use. */ |
| static GTY(()) unsigned ranges_table_in_use; |
| |
| /* Size (in elements) of increments by which we may expand the |
| ranges_table. */ |
| #define RANGES_TABLE_INCREMENT 64 |
| |
| /* Whether we have location lists that need outputting */ |
| static GTY(()) bool have_location_lists; |
| |
| /* Unique label counter. */ |
| static GTY(()) unsigned int loclabel_num; |
| |
| #ifdef DWARF2_DEBUGGING_INFO |
| /* Record whether the function being analyzed contains inlined functions. */ |
| static int current_function_has_inlines; |
| #endif |
| #if 0 && defined (MIPS_DEBUGGING_INFO) |
| static int comp_unit_has_inlines; |
| #endif |
| |
| /* The last file entry emitted by maybe_emit_file(). */ |
| static GTY(()) struct dwarf_file_data * last_emitted_file; |
| |
| /* Number of internal labels generated by gen_internal_sym(). */ |
| static GTY(()) int label_num; |
| |
| /* Cached result of previous call to lookup_filename. */ |
| static GTY(()) struct dwarf_file_data * file_table_last_lookup; |
| |
| #ifdef DWARF2_DEBUGGING_INFO |
| |
| /* Offset from the "steady-state frame pointer" to the frame base, |
| within the current function. */ |
| static HOST_WIDE_INT frame_pointer_fb_offset; |
| |
| /* APPLE LOCAL begin ARM prefer SP to FP */ |
| /* Which register was used to calculate the frame_pointer_fb_offset. */ |
| static rtx frame_pointer_fb_offset_from; |
| /* APPLE LOCAL end ARM prefer SP to FP */ |
| |
| /* Forward declarations for functions defined in this file. */ |
| |
| static int is_pseudo_reg (rtx); |
| static tree type_main_variant (tree); |
| static int is_tagged_type (tree); |
| static const char *dwarf_tag_name (unsigned); |
| static const char *dwarf_attr_name (unsigned); |
| static const char *dwarf_form_name (unsigned); |
| static tree decl_ultimate_origin (tree); |
| static tree block_ultimate_origin (tree); |
| static tree decl_class_context (tree); |
| static void add_dwarf_attr (dw_die_ref, dw_attr_ref); |
| static inline enum dw_val_class AT_class (dw_attr_ref); |
| static void add_AT_flag (dw_die_ref, enum dwarf_attribute, unsigned); |
| static inline unsigned AT_flag (dw_attr_ref); |
| static void add_AT_int (dw_die_ref, enum dwarf_attribute, HOST_WIDE_INT); |
| static inline HOST_WIDE_INT AT_int (dw_attr_ref); |
| static void add_AT_unsigned (dw_die_ref, enum dwarf_attribute, unsigned HOST_WIDE_INT); |
| static inline unsigned HOST_WIDE_INT AT_unsigned (dw_attr_ref); |
| static void add_AT_long_long (dw_die_ref, enum dwarf_attribute, unsigned long, |
| unsigned long); |
| static inline void add_AT_vec (dw_die_ref, enum dwarf_attribute, unsigned int, |
| unsigned int, unsigned char *); |
| static hashval_t debug_str_do_hash (const void *); |
| static int debug_str_eq (const void *, const void *); |
| static void add_AT_string (dw_die_ref, enum dwarf_attribute, const char *); |
| static inline const char *AT_string (dw_attr_ref); |
| static int AT_string_form (dw_attr_ref); |
| static void add_AT_die_ref (dw_die_ref, enum dwarf_attribute, dw_die_ref); |
| static void add_AT_specification (dw_die_ref, dw_die_ref); |
| static inline dw_die_ref AT_ref (dw_attr_ref); |
| static inline int AT_ref_external (dw_attr_ref); |
| static inline void set_AT_ref_external (dw_attr_ref, int); |
| static void add_AT_fde_ref (dw_die_ref, enum dwarf_attribute, unsigned); |
| static void add_AT_loc (dw_die_ref, enum dwarf_attribute, dw_loc_descr_ref); |
| static inline dw_loc_descr_ref AT_loc (dw_attr_ref); |
| static void add_AT_loc_list (dw_die_ref, enum dwarf_attribute, |
| dw_loc_list_ref); |
| static inline dw_loc_list_ref AT_loc_list (dw_attr_ref); |
| static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx); |
| static inline rtx AT_addr (dw_attr_ref); |
| static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *); |
| static void add_AT_lineptr (dw_die_ref, enum dwarf_attribute, const char *); |
| static void add_AT_macptr (dw_die_ref, enum dwarf_attribute, const char *); |
| static void add_AT_offset (dw_die_ref, enum dwarf_attribute, |
| unsigned HOST_WIDE_INT); |
| static void add_AT_range_list (dw_die_ref, enum dwarf_attribute, |
| unsigned long); |
| static inline const char *AT_lbl (dw_attr_ref); |
| static dw_attr_ref get_AT (dw_die_ref, enum dwarf_attribute); |
| static const char *get_AT_low_pc (dw_die_ref); |
| static const char *get_AT_hi_pc (dw_die_ref); |
| static const char *get_AT_string (dw_die_ref, enum dwarf_attribute); |
| static int get_AT_flag (dw_die_ref, enum dwarf_attribute); |
| static unsigned get_AT_unsigned (dw_die_ref, enum dwarf_attribute); |
| static inline dw_die_ref get_AT_ref (dw_die_ref, enum dwarf_attribute); |
| static bool is_c_family (void); |
| static bool is_cxx (void); |
| /* APPLE LOCAL begin radar 6113240 */ |
| static bool is_objc (void); |
| static bool is_objcxx (void); |
| /* APPLE LOCAL end radar 6113240 */ |
| static bool is_java (void); |
| static bool is_fortran (void); |
| static bool is_ada (void); |
| static void remove_AT (dw_die_ref, enum dwarf_attribute); |
| static void remove_child_TAG (dw_die_ref, enum dwarf_tag); |
| static void add_child_die (dw_die_ref, dw_die_ref); |
| static dw_die_ref new_die (enum dwarf_tag, dw_die_ref, tree); |
| static dw_die_ref lookup_type_die (tree); |
| static void equate_type_number_to_die (tree, dw_die_ref); |
| static hashval_t decl_die_table_hash (const void *); |
| static int decl_die_table_eq (const void *, const void *); |
| static dw_die_ref lookup_decl_die (tree); |
| static hashval_t decl_loc_table_hash (const void *); |
| static int decl_loc_table_eq (const void *, const void *); |
| static var_loc_list *lookup_decl_loc (tree); |
| static void equate_decl_number_to_die (tree, dw_die_ref); |
| static void add_var_loc_to_decl (tree, struct var_loc_node *); |
| static void print_spaces (FILE *); |
| static void print_die (dw_die_ref, FILE *); |
| static void print_dwarf_line_table (FILE *); |
| static dw_die_ref push_new_compile_unit (dw_die_ref, dw_die_ref); |
| static dw_die_ref pop_compile_unit (dw_die_ref); |
| static void loc_checksum (dw_loc_descr_ref, struct md5_ctx *); |
| static void attr_checksum (dw_attr_ref, struct md5_ctx *, int *); |
| static void die_checksum (dw_die_ref, struct md5_ctx *, int *); |
| static int same_loc_p (dw_loc_descr_ref, dw_loc_descr_ref, int *); |
| static int same_dw_val_p (dw_val_node *, dw_val_node *, int *); |
| static int same_attr_p (dw_attr_ref, dw_attr_ref, int *); |
| static int same_die_p (dw_die_ref, dw_die_ref, int *); |
| static int same_die_p_wrap (dw_die_ref, dw_die_ref); |
| static void compute_section_prefix (dw_die_ref); |
| static int is_type_die (dw_die_ref); |
| static int is_comdat_die (dw_die_ref); |
| static int is_symbol_die (dw_die_ref); |
| static void assign_symbol_names (dw_die_ref); |
| static void break_out_includes (dw_die_ref); |
| static hashval_t htab_cu_hash (const void *); |
| static int htab_cu_eq (const void *, const void *); |
| static void htab_cu_del (void *); |
| static int check_duplicate_cu (dw_die_ref, htab_t, unsigned *); |
| static void record_comdat_symbol_number (dw_die_ref, htab_t, unsigned); |
| static void add_sibling_attributes (dw_die_ref); |
| static void build_abbrev_table (dw_die_ref); |
| static void output_location_lists (dw_die_ref); |
| static int constant_size (long unsigned); |
| static unsigned long size_of_die (dw_die_ref); |
| static void calc_die_sizes (dw_die_ref); |
| static void mark_dies (dw_die_ref); |
| static void unmark_dies (dw_die_ref); |
| static void unmark_all_dies (dw_die_ref); |
| /* APPLE LOCAL pubtypes, approved for 4.3 4535968 */ |
| static unsigned long size_of_pubnames (VEC (pubname_entry,gc) *); |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| static unsigned long size_of_inlined (VEC (inlined_entry,gc) *); |
| static unsigned long size_of_aranges (void); |
| static enum dwarf_form value_format (dw_attr_ref); |
| static void output_value_format (dw_attr_ref); |
| static void output_abbrev_section (void); |
| static void output_die_symbol (dw_die_ref); |
| static void output_die (dw_die_ref); |
| static void output_compilation_unit_header (void); |
| static void output_comp_unit (dw_die_ref, int); |
| static const char *dwarf2_name (tree, int); |
| static void add_pubname (tree, dw_die_ref); |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| static void add_pubtype (tree, dw_die_ref); |
| static void output_pubnames (VEC (pubname_entry,gc) *); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| static void add_inlined_section_entry (dw_die_ref); |
| static void output_debug_inlined_section (VEC (inlined_entry,gc) *); |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| static void add_arange (tree, dw_die_ref); |
| static void output_aranges (void); |
| static unsigned int add_ranges (tree); |
| static void output_ranges (void); |
| static void output_line_info (void); |
| static void output_file_names (void); |
| static dw_die_ref base_type_die (tree); |
| static tree root_type (tree); |
| static int is_base_type (tree); |
| static bool is_subrange_type (tree); |
| static dw_die_ref subrange_type_die (tree, dw_die_ref); |
| static dw_die_ref modified_type_die (tree, int, int, dw_die_ref); |
| static int type_is_enum (tree); |
| static unsigned int dbx_reg_number (rtx); |
| static void add_loc_descr_op_piece (dw_loc_descr_ref *, int); |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref reg_loc_descriptor (rtx, enum var_init_status); |
| static dw_loc_descr_ref one_reg_loc_descriptor (unsigned int, |
| enum var_init_status); |
| static dw_loc_descr_ref multiple_reg_loc_descriptor (rtx, rtx, |
| enum var_init_status); |
| static dw_loc_descr_ref int_loc_descriptor (HOST_WIDE_INT); |
| static dw_loc_descr_ref based_loc_descr (rtx, HOST_WIDE_INT, |
| enum var_init_status); |
| static int is_based_loc (rtx); |
| static dw_loc_descr_ref mem_loc_descriptor (rtx, enum machine_mode mode, |
| enum var_init_status); |
| static dw_loc_descr_ref concat_loc_descriptor (rtx, rtx, |
| enum var_init_status); |
| static dw_loc_descr_ref loc_descriptor (rtx, enum var_init_status); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| static dw_loc_descr_ref loc_descriptor_from_tree_1 (tree, int); |
| static dw_loc_descr_ref loc_descriptor_from_tree (tree); |
| static HOST_WIDE_INT ceiling (HOST_WIDE_INT, unsigned int); |
| static tree field_type (tree); |
| static unsigned int simple_type_align_in_bits (tree); |
| static unsigned int simple_decl_align_in_bits (tree); |
| static unsigned HOST_WIDE_INT simple_type_size_in_bits (tree); |
| static HOST_WIDE_INT field_byte_offset (tree); |
| static void add_AT_location_description (dw_die_ref, enum dwarf_attribute, |
| dw_loc_descr_ref); |
| static void add_data_member_location_attribute (dw_die_ref, tree); |
| static void add_const_value_attribute (dw_die_ref, rtx); |
| static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *); |
| static HOST_WIDE_INT extract_int (const unsigned char *, unsigned); |
| static void insert_float (rtx, unsigned char *); |
| static rtx rtl_for_decl_location (tree); |
| static void add_location_or_const_value_attribute (dw_die_ref, tree, |
| enum dwarf_attribute); |
| static void tree_add_const_value_attribute (dw_die_ref, tree); |
| static void add_name_attribute (dw_die_ref, const char *); |
| static void add_comp_dir_attribute (dw_die_ref); |
| static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree); |
| static void add_subscript_info (dw_die_ref, tree); |
| static void add_byte_size_attribute (dw_die_ref, tree); |
| static void add_bit_offset_attribute (dw_die_ref, tree); |
| static void add_bit_size_attribute (dw_die_ref, tree); |
| static void add_prototyped_attribute (dw_die_ref, tree); |
| static void add_abstract_origin_attribute (dw_die_ref, tree); |
| static void add_pure_or_virtual_attribute (dw_die_ref, tree); |
| static void add_src_coords_attributes (dw_die_ref, tree); |
| static void add_name_and_src_coords_attributes (dw_die_ref, tree); |
| static void push_decl_scope (tree); |
| static void pop_decl_scope (void); |
| static dw_die_ref scope_die_for (tree, dw_die_ref); |
| static inline int local_scope_p (dw_die_ref); |
| static inline int class_or_namespace_scope_p (dw_die_ref); |
| static void add_type_attribute (dw_die_ref, tree, int, int, dw_die_ref); |
| static void add_calling_convention_attribute (dw_die_ref, tree); |
| static const char *type_tag (tree); |
| static tree member_declared_type (tree); |
| #if 0 |
| static const char *decl_start_label (tree); |
| #endif |
| static void gen_array_type_die (tree, dw_die_ref); |
| #if 0 |
| static void gen_entry_point_die (tree, dw_die_ref); |
| #endif |
| static void gen_inlined_enumeration_type_die (tree, dw_die_ref); |
| static void gen_inlined_structure_type_die (tree, dw_die_ref); |
| static void gen_inlined_union_type_die (tree, dw_die_ref); |
| static dw_die_ref gen_enumeration_type_die (tree, dw_die_ref); |
| static dw_die_ref gen_formal_parameter_die (tree, dw_die_ref); |
| static void gen_unspecified_parameters_die (tree, dw_die_ref); |
| static void gen_formal_types_die (tree, dw_die_ref); |
| static void gen_subprogram_die (tree, dw_die_ref); |
| static void gen_variable_die (tree, dw_die_ref); |
| static void gen_label_die (tree, dw_die_ref); |
| static void gen_lexical_block_die (tree, dw_die_ref, int); |
| static void gen_inlined_subroutine_die (tree, dw_die_ref, int); |
| static void gen_field_die (tree, dw_die_ref); |
| static void gen_ptr_to_mbr_type_die (tree, dw_die_ref); |
| static dw_die_ref gen_compile_unit_die (const char *); |
| static void gen_inheritance_die (tree, tree, dw_die_ref); |
| static void gen_member_die (tree, dw_die_ref); |
| static void gen_struct_or_union_type_die (tree, dw_die_ref); |
| static void gen_subroutine_type_die (tree, dw_die_ref); |
| static void gen_typedef_die (tree, dw_die_ref); |
| static void gen_type_die (tree, dw_die_ref); |
| static void gen_tagged_type_instantiation_die (tree, dw_die_ref); |
| static void gen_block_die (tree, dw_die_ref, int); |
| static void decls_for_scope (tree, dw_die_ref, int); |
| static int is_redundant_typedef (tree); |
| static void gen_namespace_die (tree); |
| static void gen_decl_die (tree, dw_die_ref); |
| static dw_die_ref force_decl_die (tree); |
| static dw_die_ref force_type_die (tree); |
| static dw_die_ref setup_namespace_context (tree, dw_die_ref); |
| static void declare_in_namespace (tree, dw_die_ref); |
| static struct dwarf_file_data * lookup_filename (const char *); |
| static void retry_incomplete_types (void); |
| static void gen_type_die_for_member (tree, tree, dw_die_ref); |
| static void splice_child_die (dw_die_ref, dw_die_ref); |
| static int file_info_cmp (const void *, const void *); |
| static dw_loc_list_ref new_loc_list (dw_loc_descr_ref, const char *, |
| const char *, const char *, unsigned); |
| static void add_loc_descr_to_loc_list (dw_loc_list_ref *, dw_loc_descr_ref, |
| const char *, const char *, |
| const char *); |
| static void output_loc_list (dw_loc_list_ref); |
| static char *gen_internal_sym (const char *); |
| |
| static void prune_unmark_dies (dw_die_ref); |
| static void prune_unused_types_mark (dw_die_ref, int); |
| static void prune_unused_types_walk (dw_die_ref); |
| static void prune_unused_types_walk_attribs (dw_die_ref); |
| static void prune_unused_types_prune (dw_die_ref); |
| static void prune_unused_types (void); |
| static int maybe_emit_file (struct dwarf_file_data *fd); |
| |
| /* Section names used to hold DWARF debugging information. */ |
| #ifndef DEBUG_INFO_SECTION |
| #define DEBUG_INFO_SECTION ".debug_info" |
| #endif |
| #ifndef DEBUG_ABBREV_SECTION |
| #define DEBUG_ABBREV_SECTION ".debug_abbrev" |
| #endif |
| #ifndef DEBUG_ARANGES_SECTION |
| #define DEBUG_ARANGES_SECTION ".debug_aranges" |
| #endif |
| #ifndef DEBUG_MACINFO_SECTION |
| #define DEBUG_MACINFO_SECTION ".debug_macinfo" |
| #endif |
| #ifndef DEBUG_LINE_SECTION |
| #define DEBUG_LINE_SECTION ".debug_line" |
| #endif |
| #ifndef DEBUG_LOC_SECTION |
| #define DEBUG_LOC_SECTION ".debug_loc" |
| #endif |
| #ifndef DEBUG_PUBNAMES_SECTION |
| #define DEBUG_PUBNAMES_SECTION ".debug_pubnames" |
| #endif |
| #ifndef DEBUG_STR_SECTION |
| #define DEBUG_STR_SECTION ".debug_str" |
| #endif |
| #ifndef DEBUG_RANGES_SECTION |
| #define DEBUG_RANGES_SECTION ".debug_ranges" |
| #endif |
| |
| /* Standard ELF section names for compiled code and data. */ |
| #ifndef TEXT_SECTION_NAME |
| #define TEXT_SECTION_NAME ".text" |
| #endif |
| |
| /* Section flags for .debug_str section. */ |
| #define DEBUG_STR_SECTION_FLAGS \ |
| (HAVE_GAS_SHF_MERGE && flag_merge_constants \ |
| ? SECTION_DEBUG | SECTION_MERGE | SECTION_STRINGS | 1 \ |
| : SECTION_DEBUG) |
| |
| /* Labels we insert at beginning sections we can reference instead of |
| the section names themselves. */ |
| |
| #ifndef TEXT_SECTION_LABEL |
| #define TEXT_SECTION_LABEL "Ltext" |
| #endif |
| #ifndef COLD_TEXT_SECTION_LABEL |
| #define COLD_TEXT_SECTION_LABEL "Ltext_cold" |
| #endif |
| #ifndef DEBUG_LINE_SECTION_LABEL |
| #define DEBUG_LINE_SECTION_LABEL "Ldebug_line" |
| #endif |
| #ifndef DEBUG_INFO_SECTION_LABEL |
| #define DEBUG_INFO_SECTION_LABEL "Ldebug_info" |
| #endif |
| #ifndef DEBUG_ABBREV_SECTION_LABEL |
| #define DEBUG_ABBREV_SECTION_LABEL "Ldebug_abbrev" |
| #endif |
| #ifndef DEBUG_LOC_SECTION_LABEL |
| #define DEBUG_LOC_SECTION_LABEL "Ldebug_loc" |
| #endif |
| #ifndef DEBUG_RANGES_SECTION_LABEL |
| #define DEBUG_RANGES_SECTION_LABEL "Ldebug_ranges" |
| #endif |
| #ifndef DEBUG_MACINFO_SECTION_LABEL |
| #define DEBUG_MACINFO_SECTION_LABEL "Ldebug_macinfo" |
| #endif |
| |
| /* Definitions of defaults for formats and names of various special |
| (artificial) labels which may be generated within this file (when the -g |
| options is used and DWARF2_DEBUGGING_INFO is in effect. |
| If necessary, these may be overridden from within the tm.h file, but |
| typically, overriding these defaults is unnecessary. */ |
| |
| static char text_end_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char text_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char cold_text_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char cold_end_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char abbrev_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char debug_info_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char debug_line_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char macinfo_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char loc_section_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| static char ranges_section_label[2 * MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| #ifndef TEXT_END_LABEL |
| #define TEXT_END_LABEL "Letext" |
| #endif |
| #ifndef COLD_END_LABEL |
| #define COLD_END_LABEL "Letext_cold" |
| #endif |
| #ifndef BLOCK_BEGIN_LABEL |
| #define BLOCK_BEGIN_LABEL "LBB" |
| #endif |
| #ifndef BLOCK_END_LABEL |
| #define BLOCK_END_LABEL "LBE" |
| #endif |
| #ifndef LINE_CODE_LABEL |
| #define LINE_CODE_LABEL "LM" |
| #endif |
| #ifndef SEPARATE_LINE_CODE_LABEL |
| #define SEPARATE_LINE_CODE_LABEL "LSM" |
| #endif |
| |
| /* We allow a language front-end to designate a function that is to be |
| called to "demangle" any name before it is put into a DIE. */ |
| |
| static const char *(*demangle_name_func) (const char *); |
| |
| void |
| dwarf2out_set_demangle_name_func (const char *(*func) (const char *)) |
| { |
| demangle_name_func = func; |
| } |
| |
| /* Test if rtl node points to a pseudo register. */ |
| |
| static inline int |
| is_pseudo_reg (rtx rtl) |
| { |
| return ((REG_P (rtl) && REGNO (rtl) >= FIRST_PSEUDO_REGISTER) |
| || (GET_CODE (rtl) == SUBREG |
| && REGNO (SUBREG_REG (rtl)) >= FIRST_PSEUDO_REGISTER)); |
| } |
| |
| /* Return a reference to a type, with its const and volatile qualifiers |
| removed. */ |
| |
| static inline tree |
| type_main_variant (tree type) |
| { |
| type = TYPE_MAIN_VARIANT (type); |
| |
| /* ??? There really should be only one main variant among any group of |
| variants of a given type (and all of the MAIN_VARIANT values for all |
| members of the group should point to that one type) but sometimes the C |
| front-end messes this up for array types, so we work around that bug |
| here. */ |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| while (type != TYPE_MAIN_VARIANT (type)) |
| type = TYPE_MAIN_VARIANT (type); |
| |
| return type; |
| } |
| |
| /* Return nonzero if the given type node represents a tagged type. */ |
| |
| static inline int |
| is_tagged_type (tree type) |
| { |
| enum tree_code code = TREE_CODE (type); |
| |
| return (code == RECORD_TYPE || code == UNION_TYPE |
| || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); |
| } |
| |
| /* Convert a DIE tag into its string name. */ |
| |
| static const char * |
| dwarf_tag_name (unsigned int tag) |
| { |
| switch (tag) |
| { |
| case DW_TAG_padding: |
| return "DW_TAG_padding"; |
| case DW_TAG_array_type: |
| return "DW_TAG_array_type"; |
| case DW_TAG_class_type: |
| return "DW_TAG_class_type"; |
| case DW_TAG_entry_point: |
| return "DW_TAG_entry_point"; |
| case DW_TAG_enumeration_type: |
| return "DW_TAG_enumeration_type"; |
| case DW_TAG_formal_parameter: |
| return "DW_TAG_formal_parameter"; |
| case DW_TAG_imported_declaration: |
| return "DW_TAG_imported_declaration"; |
| case DW_TAG_label: |
| return "DW_TAG_label"; |
| case DW_TAG_lexical_block: |
| return "DW_TAG_lexical_block"; |
| case DW_TAG_member: |
| return "DW_TAG_member"; |
| case DW_TAG_pointer_type: |
| return "DW_TAG_pointer_type"; |
| case DW_TAG_reference_type: |
| return "DW_TAG_reference_type"; |
| case DW_TAG_compile_unit: |
| return "DW_TAG_compile_unit"; |
| case DW_TAG_string_type: |
| return "DW_TAG_string_type"; |
| case DW_TAG_structure_type: |
| return "DW_TAG_structure_type"; |
| case DW_TAG_subroutine_type: |
| return "DW_TAG_subroutine_type"; |
| case DW_TAG_typedef: |
| return "DW_TAG_typedef"; |
| case DW_TAG_union_type: |
| return "DW_TAG_union_type"; |
| case DW_TAG_unspecified_parameters: |
| return "DW_TAG_unspecified_parameters"; |
| case DW_TAG_variant: |
| return "DW_TAG_variant"; |
| case DW_TAG_common_block: |
| return "DW_TAG_common_block"; |
| case DW_TAG_common_inclusion: |
| return "DW_TAG_common_inclusion"; |
| case DW_TAG_inheritance: |
| return "DW_TAG_inheritance"; |
| case DW_TAG_inlined_subroutine: |
| return "DW_TAG_inlined_subroutine"; |
| case DW_TAG_module: |
| return "DW_TAG_module"; |
| case DW_TAG_ptr_to_member_type: |
| return "DW_TAG_ptr_to_member_type"; |
| case DW_TAG_set_type: |
| return "DW_TAG_set_type"; |
| case DW_TAG_subrange_type: |
| return "DW_TAG_subrange_type"; |
| case DW_TAG_with_stmt: |
| return "DW_TAG_with_stmt"; |
| case DW_TAG_access_declaration: |
| return "DW_TAG_access_declaration"; |
| case DW_TAG_base_type: |
| return "DW_TAG_base_type"; |
| case DW_TAG_catch_block: |
| return "DW_TAG_catch_block"; |
| case DW_TAG_const_type: |
| return "DW_TAG_const_type"; |
| case DW_TAG_constant: |
| return "DW_TAG_constant"; |
| case DW_TAG_enumerator: |
| return "DW_TAG_enumerator"; |
| case DW_TAG_file_type: |
| return "DW_TAG_file_type"; |
| case DW_TAG_friend: |
| return "DW_TAG_friend"; |
| case DW_TAG_namelist: |
| return "DW_TAG_namelist"; |
| case DW_TAG_namelist_item: |
| return "DW_TAG_namelist_item"; |
| case DW_TAG_namespace: |
| return "DW_TAG_namespace"; |
| case DW_TAG_packed_type: |
| return "DW_TAG_packed_type"; |
| case DW_TAG_subprogram: |
| return "DW_TAG_subprogram"; |
| case DW_TAG_template_type_param: |
| return "DW_TAG_template_type_param"; |
| case DW_TAG_template_value_param: |
| return "DW_TAG_template_value_param"; |
| case DW_TAG_thrown_type: |
| return "DW_TAG_thrown_type"; |
| case DW_TAG_try_block: |
| return "DW_TAG_try_block"; |
| case DW_TAG_variant_part: |
| return "DW_TAG_variant_part"; |
| case DW_TAG_variable: |
| return "DW_TAG_variable"; |
| case DW_TAG_volatile_type: |
| return "DW_TAG_volatile_type"; |
| case DW_TAG_imported_module: |
| return "DW_TAG_imported_module"; |
| case DW_TAG_MIPS_loop: |
| return "DW_TAG_MIPS_loop"; |
| case DW_TAG_format_label: |
| return "DW_TAG_format_label"; |
| case DW_TAG_function_template: |
| return "DW_TAG_function_template"; |
| case DW_TAG_class_template: |
| return "DW_TAG_class_template"; |
| case DW_TAG_GNU_BINCL: |
| return "DW_TAG_GNU_BINCL"; |
| case DW_TAG_GNU_EINCL: |
| return "DW_TAG_GNU_EINCL"; |
| /* APPLE LOCAL begin opt diary */ |
| case DW_TAG_GNU_OD_entry: |
| return "DW_TAG_GNU_OD_entry"; |
| /* APPLE LOCAL end opt diary */ |
| default: |
| return "DW_TAG_<unknown>"; |
| } |
| } |
| |
| /* Convert a DWARF attribute code into its string name. */ |
| |
| static const char * |
| dwarf_attr_name (unsigned int attr) |
| { |
| switch (attr) |
| { |
| case DW_AT_sibling: |
| return "DW_AT_sibling"; |
| case DW_AT_location: |
| return "DW_AT_location"; |
| case DW_AT_name: |
| return "DW_AT_name"; |
| case DW_AT_ordering: |
| return "DW_AT_ordering"; |
| case DW_AT_subscr_data: |
| return "DW_AT_subscr_data"; |
| case DW_AT_byte_size: |
| return "DW_AT_byte_size"; |
| case DW_AT_bit_offset: |
| return "DW_AT_bit_offset"; |
| case DW_AT_bit_size: |
| return "DW_AT_bit_size"; |
| case DW_AT_element_list: |
| return "DW_AT_element_list"; |
| case DW_AT_stmt_list: |
| return "DW_AT_stmt_list"; |
| case DW_AT_low_pc: |
| return "DW_AT_low_pc"; |
| case DW_AT_high_pc: |
| return "DW_AT_high_pc"; |
| case DW_AT_language: |
| return "DW_AT_language"; |
| case DW_AT_member: |
| return "DW_AT_member"; |
| case DW_AT_discr: |
| return "DW_AT_discr"; |
| case DW_AT_discr_value: |
| return "DW_AT_discr_value"; |
| case DW_AT_visibility: |
| return "DW_AT_visibility"; |
| case DW_AT_import: |
| return "DW_AT_import"; |
| case DW_AT_string_length: |
| return "DW_AT_string_length"; |
| case DW_AT_common_reference: |
| return "DW_AT_common_reference"; |
| case DW_AT_comp_dir: |
| return "DW_AT_comp_dir"; |
| case DW_AT_const_value: |
| return "DW_AT_const_value"; |
| case DW_AT_containing_type: |
| return "DW_AT_containing_type"; |
| case DW_AT_default_value: |
| return "DW_AT_default_value"; |
| case DW_AT_inline: |
| return "DW_AT_inline"; |
| case DW_AT_is_optional: |
| return "DW_AT_is_optional"; |
| case DW_AT_lower_bound: |
| return "DW_AT_lower_bound"; |
| case DW_AT_producer: |
| return "DW_AT_producer"; |
| case DW_AT_prototyped: |
| return "DW_AT_prototyped"; |
| case DW_AT_return_addr: |
| return "DW_AT_return_addr"; |
| case DW_AT_start_scope: |
| return "DW_AT_start_scope"; |
| case DW_AT_stride_size: |
| return "DW_AT_stride_size"; |
| case DW_AT_upper_bound: |
| return "DW_AT_upper_bound"; |
| case DW_AT_abstract_origin: |
| return "DW_AT_abstract_origin"; |
| case DW_AT_accessibility: |
| return "DW_AT_accessibility"; |
| case DW_AT_address_class: |
| return "DW_AT_address_class"; |
| case DW_AT_artificial: |
| return "DW_AT_artificial"; |
| case DW_AT_base_types: |
| return "DW_AT_base_types"; |
| case DW_AT_calling_convention: |
| return "DW_AT_calling_convention"; |
| case DW_AT_count: |
| return "DW_AT_count"; |
| case DW_AT_data_member_location: |
| return "DW_AT_data_member_location"; |
| case DW_AT_decl_column: |
| return "DW_AT_decl_column"; |
| case DW_AT_decl_file: |
| return "DW_AT_decl_file"; |
| case DW_AT_decl_line: |
| return "DW_AT_decl_line"; |
| case DW_AT_declaration: |
| return "DW_AT_declaration"; |
| case DW_AT_discr_list: |
| return "DW_AT_discr_list"; |
| case DW_AT_encoding: |
| return "DW_AT_encoding"; |
| case DW_AT_external: |
| return "DW_AT_external"; |
| case DW_AT_frame_base: |
| return "DW_AT_frame_base"; |
| case DW_AT_friend: |
| return "DW_AT_friend"; |
| case DW_AT_identifier_case: |
| return "DW_AT_identifier_case"; |
| case DW_AT_macro_info: |
| return "DW_AT_macro_info"; |
| case DW_AT_namelist_items: |
| return "DW_AT_namelist_items"; |
| case DW_AT_priority: |
| return "DW_AT_priority"; |
| case DW_AT_segment: |
| return "DW_AT_segment"; |
| case DW_AT_specification: |
| return "DW_AT_specification"; |
| case DW_AT_static_link: |
| return "DW_AT_static_link"; |
| case DW_AT_type: |
| return "DW_AT_type"; |
| case DW_AT_use_location: |
| return "DW_AT_use_location"; |
| case DW_AT_variable_parameter: |
| return "DW_AT_variable_parameter"; |
| case DW_AT_virtuality: |
| return "DW_AT_virtuality"; |
| case DW_AT_vtable_elem_location: |
| return "DW_AT_vtable_elem_location"; |
| |
| case DW_AT_allocated: |
| return "DW_AT_allocated"; |
| case DW_AT_associated: |
| return "DW_AT_associated"; |
| case DW_AT_data_location: |
| return "DW_AT_data_location"; |
| case DW_AT_stride: |
| return "DW_AT_stride"; |
| case DW_AT_entry_pc: |
| return "DW_AT_entry_pc"; |
| case DW_AT_use_UTF8: |
| return "DW_AT_use_UTF8"; |
| case DW_AT_extension: |
| return "DW_AT_extension"; |
| case DW_AT_ranges: |
| return "DW_AT_ranges"; |
| case DW_AT_trampoline: |
| return "DW_AT_trampoline"; |
| case DW_AT_call_column: |
| return "DW_AT_call_column"; |
| case DW_AT_call_file: |
| return "DW_AT_call_file"; |
| case DW_AT_call_line: |
| return "DW_AT_call_line"; |
| |
| case DW_AT_MIPS_fde: |
| return "DW_AT_MIPS_fde"; |
| case DW_AT_MIPS_loop_begin: |
| return "DW_AT_MIPS_loop_begin"; |
| case DW_AT_MIPS_tail_loop_begin: |
| return "DW_AT_MIPS_tail_loop_begin"; |
| case DW_AT_MIPS_epilog_begin: |
| return "DW_AT_MIPS_epilog_begin"; |
| case DW_AT_MIPS_loop_unroll_factor: |
| return "DW_AT_MIPS_loop_unroll_factor"; |
| case DW_AT_MIPS_software_pipeline_depth: |
| return "DW_AT_MIPS_software_pipeline_depth"; |
| case DW_AT_MIPS_linkage_name: |
| return "DW_AT_MIPS_linkage_name"; |
| case DW_AT_MIPS_stride: |
| return "DW_AT_MIPS_stride"; |
| case DW_AT_MIPS_abstract_name: |
| return "DW_AT_MIPS_abstract_name"; |
| case DW_AT_MIPS_clone_origin: |
| return "DW_AT_MIPS_clone_origin"; |
| case DW_AT_MIPS_has_inlines: |
| return "DW_AT_MIPS_has_inlines"; |
| |
| case DW_AT_sf_names: |
| return "DW_AT_sf_names"; |
| case DW_AT_src_info: |
| return "DW_AT_src_info"; |
| case DW_AT_mac_info: |
| return "DW_AT_mac_info"; |
| case DW_AT_src_coords: |
| return "DW_AT_src_coords"; |
| case DW_AT_body_begin: |
| return "DW_AT_body_begin"; |
| case DW_AT_body_end: |
| return "DW_AT_body_end"; |
| case DW_AT_GNU_vector: |
| return "DW_AT_GNU_vector"; |
| /* APPLE LOCAL begin opt diary */ |
| case DW_AT_GNU_OD_msg: |
| return "DW_AT_GNU_OD_msg"; |
| case DW_AT_GNU_OD_category: |
| return "DW_AT_GNU_OD_category"; |
| case DW_AT_GNU_OD_version: |
| return "DW_AT_GNU_OD_version"; |
| /* APPLE LOCAL end opt diary */ |
| |
| case DW_AT_VMS_rtnbeg_pd_address: |
| return "DW_AT_VMS_rtnbeg_pd_address"; |
| |
| /* APPLE LOCAL begin radar 2338865 optimization notification */ |
| case DW_AT_APPLE_flags: |
| return "DW_AT_APPLE_flags"; |
| case DW_AT_APPLE_optimized: |
| return "DW_AT_APPLE_optimized"; |
| /* APPLE LOCAL end radar 2338865 optimization notification */ |
| /* APPLE LOCAL begin differentiate between arm & thumb. */ |
| case DW_AT_APPLE_isa: |
| return "DW_AT_APPLE_isa"; |
| /* APPLE LOCAL end differentiate between arm & thumb. */ |
| |
| /* APPLE LOCAL begin radar 5811943 - Fix type of pointers to Blocks */ |
| case DW_AT_APPLE_block: |
| return "DW_AT_APPLE_block"; |
| /* APPLE LOCAL end radar 5811943 - Fix type of pointers to Blocks */ |
| default: |
| return "DW_AT_<unknown>"; |
| } |
| } |
| |
| /* Convert a DWARF value form code into its string name. */ |
| |
| static const char * |
| dwarf_form_name (unsigned int form) |
| { |
| switch (form) |
| { |
| case DW_FORM_addr: |
| return "DW_FORM_addr"; |
| case DW_FORM_block2: |
| return "DW_FORM_block2"; |
| case DW_FORM_block4: |
| return "DW_FORM_block4"; |
| case DW_FORM_data2: |
| return "DW_FORM_data2"; |
| case DW_FORM_data4: |
| return "DW_FORM_data4"; |
| case DW_FORM_data8: |
| return "DW_FORM_data8"; |
| case DW_FORM_string: |
| return "DW_FORM_string"; |
| case DW_FORM_block: |
| return "DW_FORM_block"; |
| case DW_FORM_block1: |
| return "DW_FORM_block1"; |
| case DW_FORM_data1: |
| return "DW_FORM_data1"; |
| case DW_FORM_flag: |
| return "DW_FORM_flag"; |
| case DW_FORM_sdata: |
| return "DW_FORM_sdata"; |
| case DW_FORM_strp: |
| return "DW_FORM_strp"; |
| case DW_FORM_udata: |
| return "DW_FORM_udata"; |
| case DW_FORM_ref_addr: |
| return "DW_FORM_ref_addr"; |
| case DW_FORM_ref1: |
| return "DW_FORM_ref1"; |
| case DW_FORM_ref2: |
| return "DW_FORM_ref2"; |
| case DW_FORM_ref4: |
| return "DW_FORM_ref4"; |
| case DW_FORM_ref8: |
| return "DW_FORM_ref8"; |
| case DW_FORM_ref_udata: |
| return "DW_FORM_ref_udata"; |
| case DW_FORM_indirect: |
| return "DW_FORM_indirect"; |
| default: |
| return "DW_FORM_<unknown>"; |
| } |
| } |
| |
| /* Determine the "ultimate origin" of a decl. The decl may be an inlined |
| instance of an inlined instance of a decl which is local to an inline |
| function, so we have to trace all of the way back through the origin chain |
| to find out what sort of node actually served as the original seed for the |
| given block. */ |
| |
| static tree |
| decl_ultimate_origin (tree decl) |
| { |
| if (!CODE_CONTAINS_STRUCT (TREE_CODE (decl), TS_DECL_COMMON)) |
| return NULL_TREE; |
| |
| /* output_inline_function sets DECL_ABSTRACT_ORIGIN for all the |
| nodes in the function to point to themselves; ignore that if |
| we're trying to output the abstract instance of this function. */ |
| if (DECL_ABSTRACT (decl) && DECL_ABSTRACT_ORIGIN (decl) == decl) |
| return NULL_TREE; |
| |
| /* Since the DECL_ABSTRACT_ORIGIN for a DECL is supposed to be the |
| most distant ancestor, this should never happen. */ |
| gcc_assert (!DECL_FROM_INLINE (DECL_ORIGIN (decl))); |
| |
| return DECL_ABSTRACT_ORIGIN (decl); |
| } |
| |
| /* Determine the "ultimate origin" of a block. The block may be an inlined |
| instance of an inlined instance of a block which is local to an inline |
| function, so we have to trace all of the way back through the origin chain |
| to find out what sort of node actually served as the original seed for the |
| given block. */ |
| |
| static tree |
| block_ultimate_origin (tree block) |
| { |
| tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block); |
| |
| /* output_inline_function sets BLOCK_ABSTRACT_ORIGIN for all the |
| nodes in the function to point to themselves; ignore that if |
| we're trying to output the abstract instance of this function. */ |
| if (BLOCK_ABSTRACT (block) && immediate_origin == block) |
| return NULL_TREE; |
| |
| if (immediate_origin == NULL_TREE) |
| return NULL_TREE; |
| else |
| { |
| tree ret_val; |
| tree lookahead = immediate_origin; |
| |
| do |
| { |
| ret_val = lookahead; |
| lookahead = (TREE_CODE (ret_val) == BLOCK |
| ? BLOCK_ABSTRACT_ORIGIN (ret_val) : NULL); |
| } |
| while (lookahead != NULL && lookahead != ret_val); |
| |
| /* The block's abstract origin chain may not be the *ultimate* origin of |
| the block. It could lead to a DECL that has an abstract origin set. |
| If so, we want that DECL's abstract origin (which is what DECL_ORIGIN |
| will give us if it has one). Note that DECL's abstract origins are |
| supposed to be the most distant ancestor (or so decl_ultimate_origin |
| claims), so we don't need to loop following the DECL origins. */ |
| if (DECL_P (ret_val)) |
| return DECL_ORIGIN (ret_val); |
| |
| return ret_val; |
| } |
| } |
| |
| /* Get the class to which DECL belongs, if any. In g++, the DECL_CONTEXT |
| of a virtual function may refer to a base class, so we check the 'this' |
| parameter. */ |
| |
| static tree |
| decl_class_context (tree decl) |
| { |
| tree context = NULL_TREE; |
| |
| if (TREE_CODE (decl) != FUNCTION_DECL || ! DECL_VINDEX (decl)) |
| context = DECL_CONTEXT (decl); |
| else |
| context = TYPE_MAIN_VARIANT |
| (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))))); |
| |
| if (context && !TYPE_P (context)) |
| context = NULL_TREE; |
| |
| return context; |
| } |
| |
| /* Add an attribute/value pair to a DIE. */ |
| |
| static inline void |
| add_dwarf_attr (dw_die_ref die, dw_attr_ref attr) |
| { |
| /* Maybe this should be an assert? */ |
| if (die == NULL) |
| return; |
| |
| if (die->die_attr == NULL) |
| die->die_attr = VEC_alloc (dw_attr_node, gc, 1); |
| VEC_safe_push (dw_attr_node, gc, die->die_attr, attr); |
| } |
| |
| static inline enum dw_val_class |
| AT_class (dw_attr_ref a) |
| { |
| return a->dw_attr_val.val_class; |
| } |
| |
| /* Add a flag value attribute to a DIE. */ |
| |
| static inline void |
| add_AT_flag (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int flag) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_flag; |
| attr.dw_attr_val.v.val_flag = flag; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline unsigned |
| AT_flag (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_flag); |
| return a->dw_attr_val.v.val_flag; |
| } |
| |
| /* Add a signed integer attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_int (dw_die_ref die, enum dwarf_attribute attr_kind, HOST_WIDE_INT int_val) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_const; |
| attr.dw_attr_val.v.val_int = int_val; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline HOST_WIDE_INT |
| AT_int (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_const); |
| return a->dw_attr_val.v.val_int; |
| } |
| |
| /* Add an unsigned integer attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_unsigned (dw_die_ref die, enum dwarf_attribute attr_kind, |
| unsigned HOST_WIDE_INT unsigned_val) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_unsigned_const; |
| attr.dw_attr_val.v.val_unsigned = unsigned_val; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline unsigned HOST_WIDE_INT |
| AT_unsigned (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_unsigned_const); |
| return a->dw_attr_val.v.val_unsigned; |
| } |
| |
| /* Add an unsigned double integer attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_long_long (dw_die_ref die, enum dwarf_attribute attr_kind, |
| long unsigned int val_hi, long unsigned int val_low) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_long_long; |
| attr.dw_attr_val.v.val_long_long.hi = val_hi; |
| attr.dw_attr_val.v.val_long_long.low = val_low; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add a floating point attribute value to a DIE and return it. */ |
| |
| static inline void |
| add_AT_vec (dw_die_ref die, enum dwarf_attribute attr_kind, |
| unsigned int length, unsigned int elt_size, unsigned char *array) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_vec; |
| attr.dw_attr_val.v.val_vec.length = length; |
| attr.dw_attr_val.v.val_vec.elt_size = elt_size; |
| attr.dw_attr_val.v.val_vec.array = array; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Hash and equality functions for debug_str_hash. */ |
| |
| static hashval_t |
| debug_str_do_hash (const void *x) |
| { |
| return htab_hash_string (((const struct indirect_string_node *)x)->str); |
| } |
| |
| static int |
| debug_str_eq (const void *x1, const void *x2) |
| { |
| return strcmp ((((const struct indirect_string_node *)x1)->str), |
| (const char *)x2) == 0; |
| } |
| |
| /* Add a string attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind, const char *str) |
| { |
| dw_attr_node attr; |
| struct indirect_string_node *node; |
| void **slot; |
| |
| if (! debug_str_hash) |
| debug_str_hash = htab_create_ggc (10, debug_str_do_hash, |
| debug_str_eq, NULL); |
| |
| slot = htab_find_slot_with_hash (debug_str_hash, str, |
| htab_hash_string (str), INSERT); |
| if (*slot == NULL) |
| *slot = ggc_alloc_cleared (sizeof (struct indirect_string_node)); |
| node = (struct indirect_string_node *) *slot; |
| node->str = ggc_strdup (str); |
| node->refcount++; |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| #ifdef DEBUG_INLINED_SECTION |
| /* Force the names of functions (which may be inlined) to be in the |
| debug string section (i.e. string pointers). */ |
| if (die |
| && die->die_tag == DW_TAG_subprogram |
| && ((attr_kind == DW_AT_name) |
| || (attr_kind == DW_AT_MIPS_linkage_name))) |
| node->is_fn_name = true; |
| #endif |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_str; |
| attr.dw_attr_val.v.val_str = node; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline const char * |
| AT_string (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_str); |
| return a->dw_attr_val.v.val_str->str; |
| } |
| |
| /* Find out whether a string should be output inline in DIE |
| or out-of-line in .debug_str section. */ |
| |
| static int |
| AT_string_form (dw_attr_ref a) |
| { |
| struct indirect_string_node *node; |
| unsigned int len; |
| char label[32]; |
| |
| gcc_assert (a && AT_class (a) == dw_val_class_str); |
| |
| node = a->dw_attr_val.v.val_str; |
| if (node->form) |
| return node->form; |
| |
| len = strlen (node->str) + 1; |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| /* Ths sentence below is false. If the string is a function name we |
| always want it to go into the debug_str section. */ |
| /* If the string is shorter or equal to the size of the reference, it is |
| always better to put it inline. */ |
| if ((len <= DWARF_OFFSET_SIZE || node->refcount == 0) |
| && !node->is_fn_name) |
| return node->form = DW_FORM_string; |
| |
| /* If we cannot expect the linker to merge strings in .debug_str |
| section, only put it into .debug_str if it is worth even in this |
| single module. */ |
| if ((debug_str_section->common.flags & SECTION_MERGE) == 0 |
| && (len - DWARF_OFFSET_SIZE) * node->refcount <= len |
| && !node->is_fn_name) |
| return node->form = DW_FORM_string; |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter); |
| ++dw2_string_counter; |
| node->label = xstrdup (label); |
| |
| return node->form = DW_FORM_strp; |
| } |
| |
| /* Add a DIE reference attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_die_ref (dw_die_ref die, enum dwarf_attribute attr_kind, dw_die_ref targ_die) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_die_ref; |
| attr.dw_attr_val.v.val_die_ref.die = targ_die; |
| attr.dw_attr_val.v.val_die_ref.external = 0; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add an AT_specification attribute to a DIE, and also make the back |
| pointer from the specification to the definition. */ |
| |
| static inline void |
| add_AT_specification (dw_die_ref die, dw_die_ref targ_die) |
| { |
| add_AT_die_ref (die, DW_AT_specification, targ_die); |
| gcc_assert (!targ_die->die_definition); |
| targ_die->die_definition = die; |
| } |
| |
| static inline dw_die_ref |
| AT_ref (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_die_ref); |
| return a->dw_attr_val.v.val_die_ref.die; |
| } |
| |
| static inline int |
| AT_ref_external (dw_attr_ref a) |
| { |
| if (a && AT_class (a) == dw_val_class_die_ref) |
| return a->dw_attr_val.v.val_die_ref.external; |
| |
| return 0; |
| } |
| |
| static inline void |
| set_AT_ref_external (dw_attr_ref a, int i) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_die_ref); |
| a->dw_attr_val.v.val_die_ref.external = i; |
| } |
| |
| /* Add an FDE reference attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_fde_ref (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int targ_fde) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_fde_ref; |
| attr.dw_attr_val.v.val_fde_index = targ_fde; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add a location description attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_loc (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_descr_ref loc) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_loc; |
| attr.dw_attr_val.v.val_loc = loc; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline dw_loc_descr_ref |
| AT_loc (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_loc); |
| return a->dw_attr_val.v.val_loc; |
| } |
| |
| static inline void |
| add_AT_loc_list (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_list_ref loc_list) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_loc_list; |
| attr.dw_attr_val.v.val_loc_list = loc_list; |
| add_dwarf_attr (die, &attr); |
| have_location_lists = true; |
| } |
| |
| static inline dw_loc_list_ref |
| AT_loc_list (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_loc_list); |
| return a->dw_attr_val.v.val_loc_list; |
| } |
| |
| /* Add an address constant attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_addr (dw_die_ref die, enum dwarf_attribute attr_kind, rtx addr) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_addr; |
| attr.dw_attr_val.v.val_addr = addr; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Get the RTX from to an address DIE attribute. */ |
| |
| static inline rtx |
| AT_addr (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_addr); |
| return a->dw_attr_val.v.val_addr; |
| } |
| |
| /* Add a file attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind, |
| struct dwarf_file_data *fd) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_file; |
| attr.dw_attr_val.v.val_file = fd; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Get the dwarf_file_data from a file DIE attribute. */ |
| |
| static inline struct dwarf_file_data * |
| AT_file (dw_attr_ref a) |
| { |
| gcc_assert (a && AT_class (a) == dw_val_class_file); |
| return a->dw_attr_val.v.val_file; |
| } |
| |
| /* Add a label identifier attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_lbl_id (dw_die_ref die, enum dwarf_attribute attr_kind, const char *lbl_id) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_lbl_id; |
| attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id); |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add a section offset attribute value to a DIE, an offset into the |
| debug_line section. */ |
| |
| static inline void |
| add_AT_lineptr (dw_die_ref die, enum dwarf_attribute attr_kind, |
| const char *label) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_lineptr; |
| attr.dw_attr_val.v.val_lbl_id = xstrdup (label); |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add a section offset attribute value to a DIE, an offset into the |
| debug_macinfo section. */ |
| |
| static inline void |
| add_AT_macptr (dw_die_ref die, enum dwarf_attribute attr_kind, |
| const char *label) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_macptr; |
| attr.dw_attr_val.v.val_lbl_id = xstrdup (label); |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add an offset attribute value to a DIE. */ |
| |
| static inline void |
| add_AT_offset (dw_die_ref die, enum dwarf_attribute attr_kind, |
| unsigned HOST_WIDE_INT offset) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_offset; |
| attr.dw_attr_val.v.val_offset = offset; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| /* Add an range_list attribute value to a DIE. */ |
| |
| static void |
| add_AT_range_list (dw_die_ref die, enum dwarf_attribute attr_kind, |
| long unsigned int offset) |
| { |
| dw_attr_node attr; |
| |
| attr.dw_attr = attr_kind; |
| attr.dw_attr_val.val_class = dw_val_class_range_list; |
| attr.dw_attr_val.v.val_offset = offset; |
| add_dwarf_attr (die, &attr); |
| } |
| |
| static inline const char * |
| AT_lbl (dw_attr_ref a) |
| { |
| gcc_assert (a && (AT_class (a) == dw_val_class_lbl_id |
| || AT_class (a) == dw_val_class_lineptr |
| || AT_class (a) == dw_val_class_macptr)); |
| return a->dw_attr_val.v.val_lbl_id; |
| } |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| |
| /* Get the attribute of type attr_kind, if the die has it. |
| This differs from get_AT in that it does NOT search any |
| abstract origin or specification dies; it ONLY looks directly |
| at the die that was passed in. */ |
| |
| static dw_attr_ref |
| has_AT (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a; |
| unsigned ix; |
| |
| if (! die) |
| return NULL; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (a->dw_attr == attr_kind) |
| return a; |
| |
| return NULL; |
| } |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| /* Get the attribute of type attr_kind. */ |
| |
| static dw_attr_ref |
| get_AT (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a; |
| unsigned ix; |
| dw_die_ref spec = NULL; |
| |
| if (! die) |
| return NULL; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (a->dw_attr == attr_kind) |
| return a; |
| else if (a->dw_attr == DW_AT_specification |
| || a->dw_attr == DW_AT_abstract_origin) |
| spec = AT_ref (a); |
| |
| if (spec) |
| return get_AT (spec, attr_kind); |
| |
| return NULL; |
| } |
| |
| /* Return the "low pc" attribute value, typically associated with a subprogram |
| DIE. Return null if the "low pc" attribute is either not present, or if it |
| cannot be represented as an assembler label identifier. */ |
| |
| static inline const char * |
| get_AT_low_pc (dw_die_ref die) |
| { |
| dw_attr_ref a = get_AT (die, DW_AT_low_pc); |
| |
| return a ? AT_lbl (a) : NULL; |
| } |
| |
| /* Return the "high pc" attribute value, typically associated with a subprogram |
| DIE. Return null if the "high pc" attribute is either not present, or if it |
| cannot be represented as an assembler label identifier. */ |
| |
| static inline const char * |
| get_AT_hi_pc (dw_die_ref die) |
| { |
| dw_attr_ref a = get_AT (die, DW_AT_high_pc); |
| |
| return a ? AT_lbl (a) : NULL; |
| } |
| |
| /* Return the value of the string attribute designated by ATTR_KIND, or |
| NULL if it is not present. */ |
| |
| static inline const char * |
| get_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a = get_AT (die, attr_kind); |
| |
| return a ? AT_string (a) : NULL; |
| } |
| |
| /* Return the value of the flag attribute designated by ATTR_KIND, or -1 |
| if it is not present. */ |
| |
| static inline int |
| get_AT_flag (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a = get_AT (die, attr_kind); |
| |
| return a ? AT_flag (a) : 0; |
| } |
| |
| /* Return the value of the unsigned attribute designated by ATTR_KIND, or 0 |
| if it is not present. */ |
| |
| static inline unsigned |
| get_AT_unsigned (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a = get_AT (die, attr_kind); |
| |
| return a ? AT_unsigned (a) : 0; |
| } |
| |
| static inline dw_die_ref |
| get_AT_ref (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a = get_AT (die, attr_kind); |
| |
| return a ? AT_ref (a) : NULL; |
| } |
| |
| static inline struct dwarf_file_data * |
| get_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a = get_AT (die, attr_kind); |
| |
| return a ? AT_file (a) : NULL; |
| } |
| |
| /* Return TRUE if the language is C or C++. */ |
| |
| static inline bool |
| is_c_family (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return (lang == DW_LANG_C || lang == DW_LANG_C89 || lang == DW_LANG_ObjC |
| || lang == DW_LANG_C99 |
| || lang == DW_LANG_C_plus_plus || lang == DW_LANG_ObjC_plus_plus); |
| } |
| |
| /* Return TRUE if the language is C++. */ |
| |
| static inline bool |
| is_cxx (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return lang == DW_LANG_C_plus_plus || lang == DW_LANG_ObjC_plus_plus; |
| } |
| |
| /* APPLE LOCAL begin radar 6113240 */ |
| /* Return TRUE if the language is ObjC. */ |
| |
| static inline bool |
| is_objc (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return lang == DW_LANG_ObjC; |
| } |
| |
| /* Return TRUE if the language is ObjC++. */ |
| |
| static inline bool |
| is_objcxx (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return lang == DW_LANG_ObjC_plus_plus; |
| } |
| |
| /* APPLE LOCAL end radar 6113240 */ |
| /* Return TRUE if the language is Fortran. */ |
| |
| static inline bool |
| is_fortran (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return (lang == DW_LANG_Fortran77 |
| || lang == DW_LANG_Fortran90 |
| || lang == DW_LANG_Fortran95); |
| } |
| |
| /* Return TRUE if the language is Java. */ |
| |
| static inline bool |
| is_java (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return lang == DW_LANG_Java; |
| } |
| |
| /* Return TRUE if the language is Ada. */ |
| |
| static inline bool |
| is_ada (void) |
| { |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| return lang == DW_LANG_Ada95 || lang == DW_LANG_Ada83; |
| } |
| |
| /* Remove the specified attribute if present. */ |
| |
| static void |
| remove_AT (dw_die_ref die, enum dwarf_attribute attr_kind) |
| { |
| dw_attr_ref a; |
| unsigned ix; |
| |
| if (! die) |
| return; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (a->dw_attr == attr_kind) |
| { |
| if (AT_class (a) == dw_val_class_str) |
| if (a->dw_attr_val.v.val_str->refcount) |
| a->dw_attr_val.v.val_str->refcount--; |
| |
| /* VEC_ordered_remove should help reduce the number of abbrevs |
| that are needed. */ |
| VEC_ordered_remove (dw_attr_node, die->die_attr, ix); |
| return; |
| } |
| } |
| |
| /* Remove CHILD from its parent. PREV must have the property that |
| PREV->DIE_SIB == CHILD. Does not alter CHILD. */ |
| |
| static void |
| remove_child_with_prev (dw_die_ref child, dw_die_ref prev) |
| { |
| gcc_assert (child->die_parent == prev->die_parent); |
| gcc_assert (prev->die_sib == child); |
| if (prev == child) |
| { |
| gcc_assert (child->die_parent->die_child == child); |
| prev = NULL; |
| } |
| else |
| prev->die_sib = child->die_sib; |
| if (child->die_parent->die_child == child) |
| child->die_parent->die_child = prev; |
| } |
| |
| /* Remove child DIE whose die_tag is TAG. Do nothing if no child |
| matches TAG. */ |
| |
| static void |
| remove_child_TAG (dw_die_ref die, enum dwarf_tag tag) |
| { |
| dw_die_ref c; |
| |
| c = die->die_child; |
| if (c) do { |
| dw_die_ref prev = c; |
| c = c->die_sib; |
| while (c->die_tag == tag) |
| { |
| remove_child_with_prev (c, prev); |
| /* Might have removed every child. */ |
| if (c == c->die_sib) |
| return; |
| c = c->die_sib; |
| } |
| } while (c != die->die_child); |
| } |
| |
| /* Add a CHILD_DIE as the last child of DIE. */ |
| |
| static void |
| add_child_die (dw_die_ref die, dw_die_ref child_die) |
| { |
| /* FIXME this should probably be an assert. */ |
| if (! die || ! child_die) |
| return; |
| gcc_assert (die != child_die); |
| |
| child_die->die_parent = die; |
| if (die->die_child) |
| { |
| child_die->die_sib = die->die_child->die_sib; |
| die->die_child->die_sib = child_die; |
| } |
| else |
| child_die->die_sib = child_die; |
| die->die_child = child_die; |
| } |
| |
| /* Move CHILD, which must be a child of PARENT or the DIE for which PARENT |
| is the specification, to the end of PARENT's list of children. |
| This is done by removing and re-adding it. */ |
| |
| static void |
| splice_child_die (dw_die_ref parent, dw_die_ref child) |
| { |
| dw_die_ref p; |
| |
| /* We want the declaration DIE from inside the class, not the |
| specification DIE at toplevel. */ |
| if (child->die_parent != parent) |
| { |
| dw_die_ref tmp = get_AT_ref (child, DW_AT_specification); |
| |
| if (tmp) |
| child = tmp; |
| } |
| |
| gcc_assert (child->die_parent == parent |
| || (child->die_parent |
| == get_AT_ref (parent, DW_AT_specification))); |
| |
| for (p = child->die_parent->die_child; ; p = p->die_sib) |
| if (p->die_sib == child) |
| { |
| remove_child_with_prev (child, p); |
| break; |
| } |
| |
| add_child_die (parent, child); |
| } |
| |
| /* Return a pointer to a newly created DIE node. */ |
| |
| static inline dw_die_ref |
| new_die (enum dwarf_tag tag_value, dw_die_ref parent_die, tree t) |
| { |
| dw_die_ref die = ggc_alloc_cleared (sizeof (die_node)); |
| |
| die->die_tag = tag_value; |
| |
| if (parent_die != NULL) |
| add_child_die (parent_die, die); |
| else |
| { |
| limbo_die_node *limbo_node; |
| |
| limbo_node = ggc_alloc_cleared (sizeof (limbo_die_node)); |
| limbo_node->die = die; |
| limbo_node->created_for = t; |
| limbo_node->next = limbo_die_list; |
| limbo_die_list = limbo_node; |
| } |
| |
| return die; |
| } |
| |
| /* Return the DIE associated with the given type specifier. */ |
| |
| static inline dw_die_ref |
| lookup_type_die (tree type) |
| { |
| return TYPE_SYMTAB_DIE (type); |
| } |
| |
| /* Equate a DIE to a given type specifier. */ |
| |
| static inline void |
| equate_type_number_to_die (tree type, dw_die_ref type_die) |
| { |
| TYPE_SYMTAB_DIE (type) = type_die; |
| } |
| |
| /* Returns a hash value for X (which really is a die_struct). */ |
| |
| static hashval_t |
| decl_die_table_hash (const void *x) |
| { |
| return (hashval_t) ((const dw_die_ref) x)->decl_id; |
| } |
| |
| /* Return nonzero if decl_id of die_struct X is the same as UID of decl *Y. */ |
| |
| static int |
| decl_die_table_eq (const void *x, const void *y) |
| { |
| return (((const dw_die_ref) x)->decl_id == DECL_UID ((const tree) y)); |
| } |
| |
| /* Return the DIE associated with a given declaration. */ |
| |
| static inline dw_die_ref |
| lookup_decl_die (tree decl) |
| { |
| return htab_find_with_hash (decl_die_table, decl, DECL_UID (decl)); |
| } |
| |
| /* Returns a hash value for X (which really is a var_loc_list). */ |
| |
| static hashval_t |
| decl_loc_table_hash (const void *x) |
| { |
| return (hashval_t) ((const var_loc_list *) x)->decl_id; |
| } |
| |
| /* Return nonzero if decl_id of var_loc_list X is the same as |
| UID of decl *Y. */ |
| |
| static int |
| decl_loc_table_eq (const void *x, const void *y) |
| { |
| return (((const var_loc_list *) x)->decl_id == DECL_UID ((const tree) y)); |
| } |
| |
| /* Return the var_loc list associated with a given declaration. */ |
| |
| static inline var_loc_list * |
| lookup_decl_loc (tree decl) |
| { |
| return htab_find_with_hash (decl_loc_table, decl, DECL_UID (decl)); |
| } |
| |
| /* Equate a DIE to a particular declaration. */ |
| |
| static void |
| equate_decl_number_to_die (tree decl, dw_die_ref decl_die) |
| { |
| unsigned int decl_id = DECL_UID (decl); |
| void **slot; |
| |
| slot = htab_find_slot_with_hash (decl_die_table, decl, decl_id, INSERT); |
| *slot = decl_die; |
| decl_die->decl_id = decl_id; |
| } |
| |
| /* Add a variable location node to the linked list for DECL. */ |
| |
| static void |
| add_var_loc_to_decl (tree decl, struct var_loc_node *loc) |
| { |
| unsigned int decl_id = DECL_UID (decl); |
| var_loc_list *temp; |
| void **slot; |
| |
| slot = htab_find_slot_with_hash (decl_loc_table, decl, decl_id, INSERT); |
| if (*slot == NULL) |
| { |
| temp = ggc_alloc_cleared (sizeof (var_loc_list)); |
| temp->decl_id = decl_id; |
| *slot = temp; |
| } |
| else |
| temp = *slot; |
| |
| if (temp->last) |
| { |
| /* If the current location is the same as the end of the list, |
| we have nothing to do. */ |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if ((!rtx_equal_p (NOTE_VAR_LOCATION_LOC (temp->last->var_loc_note), |
| NOTE_VAR_LOCATION_LOC (loc->var_loc_note))) |
| || ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note) |
| != NOTE_VAR_LOCATION_STATUS (loc->var_loc_note)) |
| && ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note) |
| == STATUS_UNINITIALIZED) |
| || (NOTE_VAR_LOCATION_STATUS (loc->var_loc_note) |
| == STATUS_UNINITIALIZED)))) |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| { |
| /* Add LOC to the end of list and update LAST. */ |
| temp->last->next = loc; |
| temp->last = loc; |
| } |
| } |
| /* Do not add empty location to the beginning of the list. */ |
| else if (NOTE_VAR_LOCATION_LOC (loc->var_loc_note) != NULL_RTX) |
| { |
| temp->first = loc; |
| temp->last = loc; |
| } |
| } |
| |
| /* Keep track of the number of spaces used to indent the |
| output of the debugging routines that print the structure of |
| the DIE internal representation. */ |
| static int print_indent; |
| |
| /* Indent the line the number of spaces given by print_indent. */ |
| |
| static inline void |
| print_spaces (FILE *outfile) |
| { |
| fprintf (outfile, "%*s", print_indent, ""); |
| } |
| |
| /* Print the information associated with a given DIE, and its children. |
| This routine is a debugging aid only. */ |
| |
| static void |
| print_die (dw_die_ref die, FILE *outfile) |
| { |
| dw_attr_ref a; |
| dw_die_ref c; |
| unsigned ix; |
| |
| print_spaces (outfile); |
| fprintf (outfile, "DIE %4lu: %s\n", |
| die->die_offset, dwarf_tag_name (die->die_tag)); |
| print_spaces (outfile); |
| fprintf (outfile, " abbrev id: %lu", die->die_abbrev); |
| fprintf (outfile, " offset: %lu\n", die->die_offset); |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| { |
| print_spaces (outfile); |
| fprintf (outfile, " %s: ", dwarf_attr_name (a->dw_attr)); |
| |
| switch (AT_class (a)) |
| { |
| case dw_val_class_addr: |
| fprintf (outfile, "address"); |
| break; |
| case dw_val_class_offset: |
| fprintf (outfile, "offset"); |
| break; |
| case dw_val_class_loc: |
| fprintf (outfile, "location descriptor"); |
| break; |
| case dw_val_class_loc_list: |
| fprintf (outfile, "location list -> label:%s", |
| AT_loc_list (a)->ll_symbol); |
| break; |
| case dw_val_class_range_list: |
| fprintf (outfile, "range list"); |
| break; |
| case dw_val_class_const: |
| fprintf (outfile, HOST_WIDE_INT_PRINT_DEC, AT_int (a)); |
| break; |
| case dw_val_class_unsigned_const: |
| fprintf (outfile, HOST_WIDE_INT_PRINT_UNSIGNED, AT_unsigned (a)); |
| break; |
| case dw_val_class_long_long: |
| fprintf (outfile, "constant (%lu,%lu)", |
| a->dw_attr_val.v.val_long_long.hi, |
| a->dw_attr_val.v.val_long_long.low); |
| break; |
| case dw_val_class_vec: |
| fprintf (outfile, "floating-point or vector constant"); |
| break; |
| case dw_val_class_flag: |
| fprintf (outfile, "%u", AT_flag (a)); |
| break; |
| case dw_val_class_die_ref: |
| if (AT_ref (a) != NULL) |
| { |
| if (AT_ref (a)->die_symbol) |
| fprintf (outfile, "die -> label: %s", AT_ref (a)->die_symbol); |
| else |
| fprintf (outfile, "die -> %lu", AT_ref (a)->die_offset); |
| } |
| else |
| fprintf (outfile, "die -> <null>"); |
| break; |
| case dw_val_class_lbl_id: |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| fprintf (outfile, "label: %s", AT_lbl (a)); |
| break; |
| case dw_val_class_str: |
| if (AT_string (a) != NULL) |
| fprintf (outfile, "\"%s\"", AT_string (a)); |
| else |
| fprintf (outfile, "<null>"); |
| break; |
| case dw_val_class_file: |
| fprintf (outfile, "\"%s\" (%d)", AT_file (a)->filename, |
| AT_file (a)->emitted_number); |
| break; |
| default: |
| break; |
| } |
| |
| fprintf (outfile, "\n"); |
| } |
| |
| if (die->die_child != NULL) |
| { |
| print_indent += 4; |
| FOR_EACH_CHILD (die, c, print_die (c, outfile)); |
| print_indent -= 4; |
| } |
| if (print_indent == 0) |
| fprintf (outfile, "\n"); |
| } |
| |
| /* Print the contents of the source code line number correspondence table. |
| This routine is a debugging aid only. */ |
| |
| static void |
| print_dwarf_line_table (FILE *outfile) |
| { |
| unsigned i; |
| dw_line_info_ref line_info; |
| |
| fprintf (outfile, "\n\nDWARF source line information\n"); |
| for (i = 1; i < line_info_table_in_use; i++) |
| { |
| line_info = &line_info_table[i]; |
| fprintf (outfile, "%5d: %4ld %6ld\n", i, |
| line_info->dw_file_num, |
| line_info->dw_line_num); |
| } |
| |
| fprintf (outfile, "\n\n"); |
| } |
| |
| /* Print the information collected for a given DIE. */ |
| |
| void |
| debug_dwarf_die (dw_die_ref die) |
| { |
| print_die (die, stderr); |
| } |
| |
| /* Print all DWARF information collected for the compilation unit. |
| This routine is a debugging aid only. */ |
| |
| void |
| debug_dwarf (void) |
| { |
| print_indent = 0; |
| print_die (comp_unit_die, stderr); |
| if (! DWARF2_ASM_LINE_DEBUG_INFO) |
| print_dwarf_line_table (stderr); |
| } |
| |
| /* Start a new compilation unit DIE for an include file. OLD_UNIT is the CU |
| for the enclosing include file, if any. BINCL_DIE is the DW_TAG_GNU_BINCL |
| DIE that marks the start of the DIEs for this include file. */ |
| |
| static dw_die_ref |
| push_new_compile_unit (dw_die_ref old_unit, dw_die_ref bincl_die) |
| { |
| const char *filename = get_AT_string (bincl_die, DW_AT_name); |
| dw_die_ref new_unit = gen_compile_unit_die (filename); |
| |
| new_unit->die_sib = old_unit; |
| return new_unit; |
| } |
| |
| /* Close an include-file CU and reopen the enclosing one. */ |
| |
| static dw_die_ref |
| pop_compile_unit (dw_die_ref old_unit) |
| { |
| dw_die_ref new_unit = old_unit->die_sib; |
| |
| old_unit->die_sib = NULL; |
| return new_unit; |
| } |
| |
| #define CHECKSUM(FOO) md5_process_bytes (&(FOO), sizeof (FOO), ctx) |
| #define CHECKSUM_STRING(FOO) md5_process_bytes ((FOO), strlen (FOO), ctx) |
| |
| /* Calculate the checksum of a location expression. */ |
| |
| static inline void |
| loc_checksum (dw_loc_descr_ref loc, struct md5_ctx *ctx) |
| { |
| CHECKSUM (loc->dw_loc_opc); |
| CHECKSUM (loc->dw_loc_oprnd1); |
| CHECKSUM (loc->dw_loc_oprnd2); |
| } |
| |
| /* Calculate the checksum of an attribute. */ |
| |
| static void |
| attr_checksum (dw_attr_ref at, struct md5_ctx *ctx, int *mark) |
| { |
| dw_loc_descr_ref loc; |
| rtx r; |
| |
| CHECKSUM (at->dw_attr); |
| |
| /* We don't care that this was compiled with a different compiler |
| snapshot; if the output is the same, that's what matters. */ |
| if (at->dw_attr == DW_AT_producer) |
| return; |
| |
| switch (AT_class (at)) |
| { |
| case dw_val_class_const: |
| CHECKSUM (at->dw_attr_val.v.val_int); |
| break; |
| case dw_val_class_unsigned_const: |
| CHECKSUM (at->dw_attr_val.v.val_unsigned); |
| break; |
| case dw_val_class_long_long: |
| CHECKSUM (at->dw_attr_val.v.val_long_long); |
| break; |
| case dw_val_class_vec: |
| CHECKSUM (at->dw_attr_val.v.val_vec); |
| break; |
| case dw_val_class_flag: |
| CHECKSUM (at->dw_attr_val.v.val_flag); |
| break; |
| case dw_val_class_str: |
| CHECKSUM_STRING (AT_string (at)); |
| break; |
| |
| case dw_val_class_addr: |
| r = AT_addr (at); |
| gcc_assert (GET_CODE (r) == SYMBOL_REF); |
| CHECKSUM_STRING (XSTR (r, 0)); |
| break; |
| |
| case dw_val_class_offset: |
| CHECKSUM (at->dw_attr_val.v.val_offset); |
| break; |
| |
| case dw_val_class_loc: |
| for (loc = AT_loc (at); loc; loc = loc->dw_loc_next) |
| loc_checksum (loc, ctx); |
| break; |
| |
| case dw_val_class_die_ref: |
| die_checksum (AT_ref (at), ctx, mark); |
| break; |
| |
| case dw_val_class_fde_ref: |
| case dw_val_class_lbl_id: |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| break; |
| |
| case dw_val_class_file: |
| CHECKSUM_STRING (AT_file (at)->filename); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Calculate the checksum of a DIE. */ |
| |
| static void |
| die_checksum (dw_die_ref die, struct md5_ctx *ctx, int *mark) |
| { |
| dw_die_ref c; |
| dw_attr_ref a; |
| unsigned ix; |
| |
| /* To avoid infinite recursion. */ |
| if (die->die_mark) |
| { |
| CHECKSUM (die->die_mark); |
| return; |
| } |
| die->die_mark = ++(*mark); |
| |
| CHECKSUM (die->die_tag); |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| attr_checksum (a, ctx, mark); |
| |
| FOR_EACH_CHILD (die, c, die_checksum (c, ctx, mark)); |
| } |
| |
| #undef CHECKSUM |
| #undef CHECKSUM_STRING |
| |
| /* Do the location expressions look same? */ |
| static inline int |
| same_loc_p (dw_loc_descr_ref loc1, dw_loc_descr_ref loc2, int *mark) |
| { |
| return loc1->dw_loc_opc == loc2->dw_loc_opc |
| && same_dw_val_p (&loc1->dw_loc_oprnd1, &loc2->dw_loc_oprnd1, mark) |
| && same_dw_val_p (&loc1->dw_loc_oprnd2, &loc2->dw_loc_oprnd2, mark); |
| } |
| |
| /* Do the values look the same? */ |
| static int |
| same_dw_val_p (dw_val_node *v1, dw_val_node *v2, int *mark) |
| { |
| dw_loc_descr_ref loc1, loc2; |
| rtx r1, r2; |
| |
| if (v1->val_class != v2->val_class) |
| return 0; |
| |
| switch (v1->val_class) |
| { |
| case dw_val_class_const: |
| return v1->v.val_int == v2->v.val_int; |
| case dw_val_class_unsigned_const: |
| return v1->v.val_unsigned == v2->v.val_unsigned; |
| case dw_val_class_long_long: |
| return v1->v.val_long_long.hi == v2->v.val_long_long.hi |
| && v1->v.val_long_long.low == v2->v.val_long_long.low; |
| case dw_val_class_vec: |
| if (v1->v.val_vec.length != v2->v.val_vec.length |
| || v1->v.val_vec.elt_size != v2->v.val_vec.elt_size) |
| return 0; |
| if (memcmp (v1->v.val_vec.array, v2->v.val_vec.array, |
| v1->v.val_vec.length * v1->v.val_vec.elt_size)) |
| return 0; |
| return 1; |
| case dw_val_class_flag: |
| return v1->v.val_flag == v2->v.val_flag; |
| case dw_val_class_str: |
| return !strcmp(v1->v.val_str->str, v2->v.val_str->str); |
| |
| case dw_val_class_addr: |
| r1 = v1->v.val_addr; |
| r2 = v2->v.val_addr; |
| if (GET_CODE (r1) != GET_CODE (r2)) |
| return 0; |
| gcc_assert (GET_CODE (r1) == SYMBOL_REF); |
| return !strcmp (XSTR (r1, 0), XSTR (r2, 0)); |
| |
| case dw_val_class_offset: |
| return v1->v.val_offset == v2->v.val_offset; |
| |
| case dw_val_class_loc: |
| for (loc1 = v1->v.val_loc, loc2 = v2->v.val_loc; |
| loc1 && loc2; |
| loc1 = loc1->dw_loc_next, loc2 = loc2->dw_loc_next) |
| if (!same_loc_p (loc1, loc2, mark)) |
| return 0; |
| return !loc1 && !loc2; |
| |
| case dw_val_class_die_ref: |
| return same_die_p (v1->v.val_die_ref.die, v2->v.val_die_ref.die, mark); |
| |
| case dw_val_class_fde_ref: |
| case dw_val_class_lbl_id: |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| return 1; |
| |
| case dw_val_class_file: |
| return v1->v.val_file == v2->v.val_file; |
| |
| default: |
| return 1; |
| } |
| } |
| |
| /* Do the attributes look the same? */ |
| |
| static int |
| same_attr_p (dw_attr_ref at1, dw_attr_ref at2, int *mark) |
| { |
| if (at1->dw_attr != at2->dw_attr) |
| return 0; |
| |
| /* We don't care that this was compiled with a different compiler |
| snapshot; if the output is the same, that's what matters. */ |
| if (at1->dw_attr == DW_AT_producer) |
| return 1; |
| |
| return same_dw_val_p (&at1->dw_attr_val, &at2->dw_attr_val, mark); |
| } |
| |
| /* Do the dies look the same? */ |
| |
| static int |
| same_die_p (dw_die_ref die1, dw_die_ref die2, int *mark) |
| { |
| dw_die_ref c1, c2; |
| dw_attr_ref a1; |
| unsigned ix; |
| |
| /* To avoid infinite recursion. */ |
| if (die1->die_mark) |
| return die1->die_mark == die2->die_mark; |
| die1->die_mark = die2->die_mark = ++(*mark); |
| |
| if (die1->die_tag != die2->die_tag) |
| return 0; |
| |
| if (VEC_length (dw_attr_node, die1->die_attr) |
| != VEC_length (dw_attr_node, die2->die_attr)) |
| return 0; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die1->die_attr, ix, a1); ix++) |
| if (!same_attr_p (a1, VEC_index (dw_attr_node, die2->die_attr, ix), mark)) |
| return 0; |
| |
| c1 = die1->die_child; |
| c2 = die2->die_child; |
| if (! c1) |
| { |
| if (c2) |
| return 0; |
| } |
| else |
| for (;;) |
| { |
| if (!same_die_p (c1, c2, mark)) |
| return 0; |
| c1 = c1->die_sib; |
| c2 = c2->die_sib; |
| if (c1 == die1->die_child) |
| { |
| if (c2 == die2->die_child) |
| break; |
| else |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /* Do the dies look the same? Wrapper around same_die_p. */ |
| |
| static int |
| same_die_p_wrap (dw_die_ref die1, dw_die_ref die2) |
| { |
| int mark = 0; |
| int ret = same_die_p (die1, die2, &mark); |
| |
| unmark_all_dies (die1); |
| unmark_all_dies (die2); |
| |
| return ret; |
| } |
| |
| /* The prefix to attach to symbols on DIEs in the current comdat debug |
| info section. */ |
| static char *comdat_symbol_id; |
| |
| /* The index of the current symbol within the current comdat CU. */ |
| static unsigned int comdat_symbol_number; |
| |
| /* Calculate the MD5 checksum of the compilation unit DIE UNIT_DIE and its |
| children, and set comdat_symbol_id accordingly. */ |
| |
| static void |
| compute_section_prefix (dw_die_ref unit_die) |
| { |
| const char *die_name = get_AT_string (unit_die, DW_AT_name); |
| const char *base = die_name ? lbasename (die_name) : "anonymous"; |
| char *name = alloca (strlen (base) + 64); |
| char *p; |
| int i, mark; |
| unsigned char checksum[16]; |
| struct md5_ctx ctx; |
| |
| /* Compute the checksum of the DIE, then append part of it as hex digits to |
| the name filename of the unit. */ |
| |
| md5_init_ctx (&ctx); |
| mark = 0; |
| die_checksum (unit_die, &ctx, &mark); |
| unmark_all_dies (unit_die); |
| md5_finish_ctx (&ctx, checksum); |
| |
| sprintf (name, "%s.", base); |
| clean_symbol_name (name); |
| |
| p = name + strlen (name); |
| for (i = 0; i < 4; i++) |
| { |
| sprintf (p, "%.2x", checksum[i]); |
| p += 2; |
| } |
| |
| comdat_symbol_id = unit_die->die_symbol = xstrdup (name); |
| comdat_symbol_number = 0; |
| } |
| |
| /* Returns nonzero if DIE represents a type, in the sense of TYPE_P. */ |
| |
| static int |
| is_type_die (dw_die_ref die) |
| { |
| switch (die->die_tag) |
| { |
| case DW_TAG_array_type: |
| case DW_TAG_class_type: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_string_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_union_type: |
| case DW_TAG_ptr_to_member_type: |
| case DW_TAG_set_type: |
| case DW_TAG_subrange_type: |
| case DW_TAG_base_type: |
| case DW_TAG_const_type: |
| case DW_TAG_file_type: |
| case DW_TAG_packed_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_typedef: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| /* Returns 1 iff C is the sort of DIE that should go into a COMDAT CU. |
| Basically, we want to choose the bits that are likely to be shared between |
| compilations (types) and leave out the bits that are specific to individual |
| compilations (functions). */ |
| |
| static int |
| is_comdat_die (dw_die_ref c) |
| { |
| /* I think we want to leave base types and __vtbl_ptr_type in the main CU, as |
| we do for stabs. The advantage is a greater likelihood of sharing between |
| objects that don't include headers in the same order (and therefore would |
| put the base types in a different comdat). jason 8/28/00 */ |
| |
| if (c->die_tag == DW_TAG_base_type) |
| return 0; |
| |
| if (c->die_tag == DW_TAG_pointer_type |
| || c->die_tag == DW_TAG_reference_type |
| || c->die_tag == DW_TAG_const_type |
| || c->die_tag == DW_TAG_volatile_type) |
| { |
| dw_die_ref t = get_AT_ref (c, DW_AT_type); |
| |
| return t ? is_comdat_die (t) : 0; |
| } |
| |
| return is_type_die (c); |
| } |
| |
| /* Returns 1 iff C is the sort of DIE that might be referred to from another |
| compilation unit. */ |
| |
| static int |
| is_symbol_die (dw_die_ref c) |
| { |
| return (is_type_die (c) |
| || (get_AT (c, DW_AT_declaration) |
| && !get_AT (c, DW_AT_specification)) |
| || c->die_tag == DW_TAG_namespace); |
| } |
| |
| static char * |
| gen_internal_sym (const char *prefix) |
| { |
| char buf[256]; |
| |
| ASM_GENERATE_INTERNAL_LABEL (buf, prefix, label_num++); |
| return xstrdup (buf); |
| } |
| |
| /* Assign symbols to all worthy DIEs under DIE. */ |
| |
| static void |
| assign_symbol_names (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| if (is_symbol_die (die)) |
| { |
| if (comdat_symbol_id) |
| { |
| char *p = alloca (strlen (comdat_symbol_id) + 64); |
| |
| sprintf (p, "%s.%s.%x", DIE_LABEL_PREFIX, |
| comdat_symbol_id, comdat_symbol_number++); |
| die->die_symbol = xstrdup (p); |
| } |
| else |
| die->die_symbol = gen_internal_sym ("LDIE"); |
| } |
| |
| FOR_EACH_CHILD (die, c, assign_symbol_names (c)); |
| } |
| |
| struct cu_hash_table_entry |
| { |
| dw_die_ref cu; |
| unsigned min_comdat_num, max_comdat_num; |
| struct cu_hash_table_entry *next; |
| }; |
| |
| /* Routines to manipulate hash table of CUs. */ |
| static hashval_t |
| htab_cu_hash (const void *of) |
| { |
| const struct cu_hash_table_entry *entry = of; |
| |
| return htab_hash_string (entry->cu->die_symbol); |
| } |
| |
| static int |
| htab_cu_eq (const void *of1, const void *of2) |
| { |
| const struct cu_hash_table_entry *entry1 = of1; |
| const struct die_struct *entry2 = of2; |
| |
| return !strcmp (entry1->cu->die_symbol, entry2->die_symbol); |
| } |
| |
| static void |
| htab_cu_del (void *what) |
| { |
| struct cu_hash_table_entry *next, *entry = what; |
| |
| while (entry) |
| { |
| next = entry->next; |
| free (entry); |
| entry = next; |
| } |
| } |
| |
| /* Check whether we have already seen this CU and set up SYM_NUM |
| accordingly. */ |
| static int |
| check_duplicate_cu (dw_die_ref cu, htab_t htable, unsigned int *sym_num) |
| { |
| struct cu_hash_table_entry dummy; |
| struct cu_hash_table_entry **slot, *entry, *last = &dummy; |
| |
| dummy.max_comdat_num = 0; |
| |
| slot = (struct cu_hash_table_entry **) |
| htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol), |
| INSERT); |
| entry = *slot; |
| |
| for (; entry; last = entry, entry = entry->next) |
| { |
| if (same_die_p_wrap (cu, entry->cu)) |
| break; |
| } |
| |
| if (entry) |
| { |
| *sym_num = entry->min_comdat_num; |
| return 1; |
| } |
| |
| entry = XCNEW (struct cu_hash_table_entry); |
| entry->cu = cu; |
| entry->min_comdat_num = *sym_num = last->max_comdat_num; |
| entry->next = *slot; |
| *slot = entry; |
| |
| return 0; |
| } |
| |
| /* Record SYM_NUM to record of CU in HTABLE. */ |
| static void |
| record_comdat_symbol_number (dw_die_ref cu, htab_t htable, unsigned int sym_num) |
| { |
| struct cu_hash_table_entry **slot, *entry; |
| |
| slot = (struct cu_hash_table_entry **) |
| htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol), |
| NO_INSERT); |
| entry = *slot; |
| |
| entry->max_comdat_num = sym_num; |
| } |
| |
| /* Traverse the DIE (which is always comp_unit_die), and set up |
| additional compilation units for each of the include files we see |
| bracketed by BINCL/EINCL. */ |
| |
| static void |
| break_out_includes (dw_die_ref die) |
| { |
| dw_die_ref c; |
| dw_die_ref unit = NULL; |
| limbo_die_node *node, **pnode; |
| htab_t cu_hash_table; |
| |
| c = die->die_child; |
| if (c) do { |
| dw_die_ref prev = c; |
| c = c->die_sib; |
| while (c->die_tag == DW_TAG_GNU_BINCL || c->die_tag == DW_TAG_GNU_EINCL |
| || (unit && is_comdat_die (c))) |
| { |
| dw_die_ref next = c->die_sib; |
| |
| /* This DIE is for a secondary CU; remove it from the main one. */ |
| remove_child_with_prev (c, prev); |
| |
| if (c->die_tag == DW_TAG_GNU_BINCL) |
| unit = push_new_compile_unit (unit, c); |
| else if (c->die_tag == DW_TAG_GNU_EINCL) |
| unit = pop_compile_unit (unit); |
| else |
| add_child_die (unit, c); |
| c = next; |
| if (c == die->die_child) |
| break; |
| } |
| } while (c != die->die_child); |
| |
| #if 0 |
| /* We can only use this in debugging, since the frontend doesn't check |
| to make sure that we leave every include file we enter. */ |
| gcc_assert (!unit); |
| #endif |
| |
| assign_symbol_names (die); |
| cu_hash_table = htab_create (10, htab_cu_hash, htab_cu_eq, htab_cu_del); |
| for (node = limbo_die_list, pnode = &limbo_die_list; |
| node; |
| node = node->next) |
| { |
| int is_dupl; |
| |
| compute_section_prefix (node->die); |
| is_dupl = check_duplicate_cu (node->die, cu_hash_table, |
| &comdat_symbol_number); |
| assign_symbol_names (node->die); |
| if (is_dupl) |
| *pnode = node->next; |
| else |
| { |
| pnode = &node->next; |
| record_comdat_symbol_number (node->die, cu_hash_table, |
| comdat_symbol_number); |
| } |
| } |
| htab_delete (cu_hash_table); |
| } |
| |
| /* APPLE LOCAL begin radar 5636185 */ |
| /* Search backwards through the die structures to see if the current |
| die, DIE is contained within a subprogram die or not. Return the |
| result. |
| |
| This is used when making final determination whether or not to |
| write out DW_AT_MIPS_linkage_name (in add_sibling_attributes). |
| DW_AT_MIPS_linkage_name needs to be written out for global level |
| structs, classes and namespaces, and any nested structs, classes |
| or namespaces, but not for such things that are local to |
| functions. */ |
| |
| static bool |
| contained_in_subroutine (dw_die_ref die) |
| { |
| dw_die_ref ancestor_die; |
| bool ret_val = false; |
| |
| ancestor_die = die->die_parent; |
| |
| while (ancestor_die |
| && ancestor_die->die_tag != DW_TAG_compile_unit |
| && ancestor_die->die_tag != DW_TAG_subprogram) |
| ancestor_die = ancestor_die->die_parent; |
| |
| if (ancestor_die && ancestor_die->die_tag == DW_TAG_subprogram) |
| ret_val = true; |
| |
| return ret_val; |
| } |
| /* APPLE LOCAL end radar 5636185 */ |
| |
| /* Traverse the DIE and add a sibling attribute if it may have the |
| effect of speeding up access to siblings. To save some space, |
| avoid generating sibling attributes for DIE's without children. */ |
| |
| static void |
| add_sibling_attributes (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| /* APPLE LOCAL radar 6066486 - move code further down. */ |
| /* APPLE LOCAL begin radar 5636185 */ |
| /* As we are traversing the entire structure of dies, to add the |
| sibling attributes, also check each die to see if it has the |
| attribute DW_AT_MIPS_linkage_name. If it has the attribute, |
| verify that the attribute is supposed to be output for the die, |
| i.e. that the die is not local to an subroutine. If the current |
| die *is* local to a subroutine, then remove the |
| DW_AT_MIPS_linkage_name attribute. |
| |
| This is done here, rather than at the time the |
| DW_AT_MIPS_linkage_name attribute is first added to the die, |
| because *here* we are guaranteed that all the dies have been |
| generated and the die structure is complete, whereas that is not |
| true at the time the attribute is first generated. */ |
| |
| if (get_AT (die, DW_AT_MIPS_linkage_name) |
| && contained_in_subroutine (die)) |
| remove_AT (die, DW_AT_MIPS_linkage_name); |
| /* APPLE LOCAL end radar 5636185 */ |
| /* APPLE LOCAL begin radar 6066486 - Code moved from above. */ |
| /* Don't return until after removing the DW_AT_MIPS_linkage_name, |
| if appropriate. */ |
| if (! die->die_child) |
| return; |
| /* APPLE LOCAL end radar 6066486 - Code moved from above. */ |
| |
| if (die->die_parent && die != die->die_parent->die_child) |
| add_AT_die_ref (die, DW_AT_sibling, die->die_sib); |
| |
| FOR_EACH_CHILD (die, c, add_sibling_attributes (c)); |
| } |
| |
| /* Output all location lists for the DIE and its children. */ |
| |
| static void |
| output_location_lists (dw_die_ref die) |
| { |
| dw_die_ref c; |
| dw_attr_ref a; |
| unsigned ix; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (AT_class (a) == dw_val_class_loc_list) |
| output_loc_list (AT_loc_list (a)); |
| |
| FOR_EACH_CHILD (die, c, output_location_lists (c)); |
| } |
| |
| /* The format of each DIE (and its attribute value pairs) is encoded in an |
| abbreviation table. This routine builds the abbreviation table and assigns |
| a unique abbreviation id for each abbreviation entry. The children of each |
| die are visited recursively. */ |
| |
| static void |
| build_abbrev_table (dw_die_ref die) |
| { |
| unsigned long abbrev_id; |
| unsigned int n_alloc; |
| dw_die_ref c; |
| dw_attr_ref a; |
| unsigned ix; |
| |
| /* Scan the DIE references, and mark as external any that refer to |
| DIEs from other CUs (i.e. those which are not marked). */ |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (AT_class (a) == dw_val_class_die_ref |
| && AT_ref (a)->die_mark == 0) |
| { |
| gcc_assert (AT_ref (a)->die_symbol); |
| |
| set_AT_ref_external (a, 1); |
| } |
| |
| for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) |
| { |
| dw_die_ref abbrev = abbrev_die_table[abbrev_id]; |
| dw_attr_ref die_a, abbrev_a; |
| unsigned ix; |
| bool ok = true; |
| |
| if (abbrev->die_tag != die->die_tag) |
| continue; |
| if ((abbrev->die_child != NULL) != (die->die_child != NULL)) |
| continue; |
| |
| if (VEC_length (dw_attr_node, abbrev->die_attr) |
| != VEC_length (dw_attr_node, die->die_attr)) |
| continue; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, die_a); ix++) |
| { |
| abbrev_a = VEC_index (dw_attr_node, abbrev->die_attr, ix); |
| if ((abbrev_a->dw_attr != die_a->dw_attr) |
| || (value_format (abbrev_a) != value_format (die_a))) |
| { |
| ok = false; |
| break; |
| } |
| } |
| if (ok) |
| break; |
| } |
| |
| if (abbrev_id >= abbrev_die_table_in_use) |
| { |
| if (abbrev_die_table_in_use >= abbrev_die_table_allocated) |
| { |
| n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT; |
| abbrev_die_table = ggc_realloc (abbrev_die_table, |
| sizeof (dw_die_ref) * n_alloc); |
| |
| memset (&abbrev_die_table[abbrev_die_table_allocated], 0, |
| (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref)); |
| abbrev_die_table_allocated = n_alloc; |
| } |
| |
| ++abbrev_die_table_in_use; |
| abbrev_die_table[abbrev_id] = die; |
| } |
| |
| die->die_abbrev = abbrev_id; |
| FOR_EACH_CHILD (die, c, build_abbrev_table (c)); |
| } |
| |
| /* Return the power-of-two number of bytes necessary to represent VALUE. */ |
| |
| static int |
| constant_size (long unsigned int value) |
| { |
| int log; |
| |
| if (value == 0) |
| log = 0; |
| else |
| log = floor_log2 (value); |
| |
| log = log / 8; |
| log = 1 << (floor_log2 (log) + 1); |
| |
| return log; |
| } |
| |
| /* Return the size of a DIE as it is represented in the |
| .debug_info section. */ |
| |
| static unsigned long |
| size_of_die (dw_die_ref die) |
| { |
| unsigned long size = 0; |
| dw_attr_ref a; |
| unsigned ix; |
| |
| size += size_of_uleb128 (die->die_abbrev); |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| { |
| switch (AT_class (a)) |
| { |
| case dw_val_class_addr: |
| size += DWARF2_ADDR_SIZE; |
| break; |
| case dw_val_class_offset: |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_loc: |
| { |
| unsigned long lsize = size_of_locs (AT_loc (a)); |
| |
| /* Block length. */ |
| size += constant_size (lsize); |
| size += lsize; |
| } |
| break; |
| case dw_val_class_loc_list: |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_range_list: |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_const: |
| size += size_of_sleb128 (AT_int (a)); |
| break; |
| case dw_val_class_unsigned_const: |
| size += constant_size (AT_unsigned (a)); |
| break; |
| case dw_val_class_long_long: |
| size += 1 + 2*HOST_BITS_PER_LONG/HOST_BITS_PER_CHAR; /* block */ |
| break; |
| case dw_val_class_vec: |
| size += 1 + (a->dw_attr_val.v.val_vec.length |
| * a->dw_attr_val.v.val_vec.elt_size); /* block */ |
| break; |
| case dw_val_class_flag: |
| size += 1; |
| break; |
| case dw_val_class_die_ref: |
| if (AT_ref_external (a)) |
| size += DWARF2_ADDR_SIZE; |
| else |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_fde_ref: |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_lbl_id: |
| size += DWARF2_ADDR_SIZE; |
| break; |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| size += DWARF_OFFSET_SIZE; |
| break; |
| case dw_val_class_str: |
| if (AT_string_form (a) == DW_FORM_strp) |
| size += DWARF_OFFSET_SIZE; |
| else |
| size += strlen (a->dw_attr_val.v.val_str->str) + 1; |
| break; |
| case dw_val_class_file: |
| size += constant_size (maybe_emit_file (a->dw_attr_val.v.val_file)); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| return size; |
| } |
| |
| /* Size the debugging information associated with a given DIE. Visits the |
| DIE's children recursively. Updates the global variable next_die_offset, on |
| each time through. Uses the current value of next_die_offset to update the |
| die_offset field in each DIE. */ |
| |
| static void |
| calc_die_sizes (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| die->die_offset = next_die_offset; |
| next_die_offset += size_of_die (die); |
| |
| FOR_EACH_CHILD (die, c, calc_die_sizes (c)); |
| |
| if (die->die_child != NULL) |
| /* Count the null byte used to terminate sibling lists. */ |
| next_die_offset += 1; |
| } |
| |
| /* Set the marks for a die and its children. We do this so |
| that we know whether or not a reference needs to use FORM_ref_addr; only |
| DIEs in the same CU will be marked. We used to clear out the offset |
| and use that as the flag, but ran into ordering problems. */ |
| |
| static void |
| mark_dies (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| gcc_assert (!die->die_mark); |
| |
| die->die_mark = 1; |
| FOR_EACH_CHILD (die, c, mark_dies (c)); |
| } |
| |
| /* Clear the marks for a die and its children. */ |
| |
| static void |
| unmark_dies (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| gcc_assert (die->die_mark); |
| |
| die->die_mark = 0; |
| FOR_EACH_CHILD (die, c, unmark_dies (c)); |
| } |
| |
| /* Clear the marks for a die, its children and referred dies. */ |
| |
| static void |
| unmark_all_dies (dw_die_ref die) |
| { |
| dw_die_ref c; |
| dw_attr_ref a; |
| unsigned ix; |
| |
| if (!die->die_mark) |
| return; |
| die->die_mark = 0; |
| |
| FOR_EACH_CHILD (die, c, unmark_all_dies (c)); |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (AT_class (a) == dw_val_class_die_ref) |
| unmark_all_dies (AT_ref (a)); |
| } |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| static unsigned long |
| size_of_inlined (VEC (inlined_entry, gc) * inlined_dies) |
| { |
| unsigned long size; |
| unsigned i; |
| unsigned num_entries; |
| inlined_ref iptr; |
| |
| size = DWARF_INLINED_HEADER_SIZE; |
| |
| for (i = 0; VEC_iterate (inlined_entry, inlined_dies, i, iptr); i++) |
| { |
| num_entries = VEC_length (dw_die_ref, iptr->inlined_instances); |
| size += (2 * DWARF_OFFSET_SIZE) + (num_entries * DWARF2_ADDR_SIZE) |
| + (num_entries * DWARF_OFFSET_SIZE) + size_of_uleb128 (num_entries); |
| } |
| |
| return size; |
| } |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| /* Return the size of the .debug_pubnames or .debug_pubtypes table |
| generated for the compilation unit. */ |
| |
| static unsigned long |
| size_of_pubnames (VEC (pubname_entry, gc) * names) |
| { |
| unsigned long size; |
| unsigned i; |
| pubname_ref p; |
| |
| size = DWARF_PUBNAMES_HEADER_SIZE; |
| for (i = 0; VEC_iterate (pubname_entry, names, i, p); i++) |
| if (names != pubtype_table |
| || p->die->die_offset != 0 |
| || !flag_eliminate_unused_debug_types) |
| size += strlen (p->name) + DWARF_OFFSET_SIZE + 1; |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| size += DWARF_OFFSET_SIZE; |
| return size; |
| } |
| |
| /* Return the size of the information in the .debug_aranges section. */ |
| |
| static unsigned long |
| size_of_aranges (void) |
| { |
| unsigned long size; |
| |
| size = DWARF_ARANGES_HEADER_SIZE; |
| |
| /* Count the address/length pair for this compilation unit. */ |
| size += 2 * DWARF2_ADDR_SIZE; |
| size += 2 * DWARF2_ADDR_SIZE * arange_table_in_use; |
| |
| /* Count the two zero words used to terminated the address range table. */ |
| size += 2 * DWARF2_ADDR_SIZE; |
| return size; |
| } |
| |
| /* Select the encoding of an attribute value. */ |
| |
| static enum dwarf_form |
| value_format (dw_attr_ref a) |
| { |
| switch (a->dw_attr_val.val_class) |
| { |
| case dw_val_class_addr: |
| return DW_FORM_addr; |
| case dw_val_class_range_list: |
| case dw_val_class_offset: |
| case dw_val_class_loc_list: |
| switch (DWARF_OFFSET_SIZE) |
| { |
| case 4: |
| return DW_FORM_data4; |
| case 8: |
| return DW_FORM_data8; |
| default: |
| gcc_unreachable (); |
| } |
| case dw_val_class_loc: |
| switch (constant_size (size_of_locs (AT_loc (a)))) |
| { |
| case 1: |
| return DW_FORM_block1; |
| case 2: |
| return DW_FORM_block2; |
| default: |
| gcc_unreachable (); |
| } |
| case dw_val_class_const: |
| return DW_FORM_sdata; |
| case dw_val_class_unsigned_const: |
| switch (constant_size (AT_unsigned (a))) |
| { |
| case 1: |
| return DW_FORM_data1; |
| case 2: |
| return DW_FORM_data2; |
| case 4: |
| return DW_FORM_data4; |
| case 8: |
| return DW_FORM_data8; |
| default: |
| gcc_unreachable (); |
| } |
| case dw_val_class_long_long: |
| return DW_FORM_block1; |
| case dw_val_class_vec: |
| return DW_FORM_block1; |
| case dw_val_class_flag: |
| return DW_FORM_flag; |
| case dw_val_class_die_ref: |
| if (AT_ref_external (a)) |
| return DW_FORM_ref_addr; |
| else |
| return DW_FORM_ref; |
| case dw_val_class_fde_ref: |
| return DW_FORM_data; |
| case dw_val_class_lbl_id: |
| return DW_FORM_addr; |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| return DW_FORM_data; |
| case dw_val_class_str: |
| return AT_string_form (a); |
| case dw_val_class_file: |
| switch (constant_size (maybe_emit_file (a->dw_attr_val.v.val_file))) |
| { |
| case 1: |
| return DW_FORM_data1; |
| case 2: |
| return DW_FORM_data2; |
| case 4: |
| return DW_FORM_data4; |
| default: |
| gcc_unreachable (); |
| } |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Output the encoding of an attribute value. */ |
| |
| static void |
| output_value_format (dw_attr_ref a) |
| { |
| enum dwarf_form form = value_format (a); |
| |
| dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form)); |
| } |
| |
| /* Output the .debug_abbrev section which defines the DIE abbreviation |
| table. */ |
| |
| static void |
| output_abbrev_section (void) |
| { |
| unsigned long abbrev_id; |
| |
| for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id) |
| { |
| dw_die_ref abbrev = abbrev_die_table[abbrev_id]; |
| unsigned ix; |
| dw_attr_ref a_attr; |
| |
| dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)"); |
| dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)", |
| dwarf_tag_name (abbrev->die_tag)); |
| |
| if (abbrev->die_child != NULL) |
| dw2_asm_output_data (1, DW_children_yes, "DW_children_yes"); |
| else |
| dw2_asm_output_data (1, DW_children_no, "DW_children_no"); |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr); |
| ix++) |
| { |
| dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)", |
| dwarf_attr_name (a_attr->dw_attr)); |
| output_value_format (a_attr); |
| } |
| |
| dw2_asm_output_data (1, 0, NULL); |
| dw2_asm_output_data (1, 0, NULL); |
| } |
| |
| /* Terminate the table. */ |
| dw2_asm_output_data (1, 0, NULL); |
| } |
| |
| /* Output a symbol we can use to refer to this DIE from another CU. */ |
| |
| static inline void |
| output_die_symbol (dw_die_ref die) |
| { |
| char *sym = die->die_symbol; |
| |
| if (sym == 0) |
| return; |
| |
| if (strncmp (sym, DIE_LABEL_PREFIX, sizeof (DIE_LABEL_PREFIX) - 1) == 0) |
| /* We make these global, not weak; if the target doesn't support |
| .linkonce, it doesn't support combining the sections, so debugging |
| will break. */ |
| targetm.asm_out.globalize_label (asm_out_file, sym); |
| |
| ASM_OUTPUT_LABEL (asm_out_file, sym); |
| } |
| |
| /* Return a new location list, given the begin and end range, and the |
| expression. gensym tells us whether to generate a new internal symbol for |
| this location list node, which is done for the head of the list only. */ |
| |
| static inline dw_loc_list_ref |
| new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end, |
| const char *section, unsigned int gensym) |
| { |
| dw_loc_list_ref retlist = ggc_alloc_cleared (sizeof (dw_loc_list_node)); |
| |
| retlist->begin = begin; |
| retlist->end = end; |
| retlist->expr = expr; |
| retlist->section = section; |
| if (gensym) |
| retlist->ll_symbol = gen_internal_sym ("LLST"); |
| |
| return retlist; |
| } |
| |
| /* Add a location description expression to a location list. */ |
| |
| static inline void |
| add_loc_descr_to_loc_list (dw_loc_list_ref *list_head, dw_loc_descr_ref descr, |
| const char *begin, const char *end, |
| const char *section) |
| { |
| dw_loc_list_ref *d; |
| |
| /* Find the end of the chain. */ |
| for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next) |
| ; |
| |
| /* Add a new location list node to the list. */ |
| *d = new_loc_list (descr, begin, end, section, 0); |
| } |
| |
| static void |
| dwarf2out_switch_text_section (void) |
| { |
| dw_fde_ref fde; |
| |
| gcc_assert (cfun); |
| |
| fde = &fde_table[fde_table_in_use - 1]; |
| fde->dw_fde_switched_sections = true; |
| fde->dw_fde_hot_section_label = cfun->hot_section_label; |
| fde->dw_fde_hot_section_end_label = cfun->hot_section_end_label; |
| fde->dw_fde_unlikely_section_label = cfun->cold_section_label; |
| fde->dw_fde_unlikely_section_end_label = cfun->cold_section_end_label; |
| have_multiple_function_sections = true; |
| |
| /* Reset the current label on switching text sections, so that we |
| don't attempt to advance_loc4 between labels in different sections. */ |
| fde->dw_fde_current_label = NULL; |
| } |
| |
| /* Output the location list given to us. */ |
| |
| static void |
| output_loc_list (dw_loc_list_ref list_head) |
| { |
| dw_loc_list_ref curr = list_head; |
| |
| ASM_OUTPUT_LABEL (asm_out_file, list_head->ll_symbol); |
| |
| /* Walk the location list, and output each range + expression. */ |
| for (curr = list_head; curr != NULL; curr = curr->dw_loc_next) |
| { |
| unsigned long size; |
| /* APPLE LOCAL begin Don't output useless records. */ |
| if (strcmp (curr->begin, curr->end) == 0) |
| continue; |
| /* APPLE LOCAL end Don't output useless records. */ |
| if (!have_multiple_function_sections) |
| { |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->begin, curr->section, |
| "Location list begin address (%s)", |
| list_head->ll_symbol); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->end, curr->section, |
| "Location list end address (%s)", |
| list_head->ll_symbol); |
| } |
| else |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->begin, |
| "Location list begin address (%s)", |
| list_head->ll_symbol); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->end, |
| "Location list end address (%s)", |
| list_head->ll_symbol); |
| } |
| size = size_of_locs (curr->expr); |
| |
| /* Output the block length for this list of location operations. */ |
| gcc_assert (size <= 0xffff); |
| dw2_asm_output_data (2, size, "%s", "Location expression size"); |
| |
| output_loc_sequence (curr->expr); |
| } |
| |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, |
| "Location list terminator begin (%s)", |
| list_head->ll_symbol); |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, |
| "Location list terminator end (%s)", |
| list_head->ll_symbol); |
| } |
| |
| /* Output the DIE and its attributes. Called recursively to generate |
| the definitions of each child DIE. */ |
| |
| static void |
| output_die (dw_die_ref die) |
| { |
| dw_attr_ref a; |
| dw_die_ref c; |
| unsigned long size; |
| unsigned ix; |
| |
| /* If someone in another CU might refer to us, set up a symbol for |
| them to point to. */ |
| if (die->die_symbol) |
| output_die_symbol (die); |
| |
| dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (0x%lx) %s)", |
| die->die_offset, dwarf_tag_name (die->die_tag)); |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| { |
| const char *name = dwarf_attr_name (a->dw_attr); |
| |
| switch (AT_class (a)) |
| { |
| case dw_val_class_addr: |
| dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name); |
| break; |
| |
| case dw_val_class_offset: |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, a->dw_attr_val.v.val_offset, |
| "%s", name); |
| break; |
| |
| case dw_val_class_range_list: |
| { |
| char *p = strchr (ranges_section_label, '\0'); |
| |
| sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX, |
| a->dw_attr_val.v.val_offset); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label, |
| debug_ranges_section, "%s", name); |
| *p = '\0'; |
| } |
| break; |
| |
| case dw_val_class_loc: |
| size = size_of_locs (AT_loc (a)); |
| |
| /* Output the block length for this list of location operations. */ |
| dw2_asm_output_data (constant_size (size), size, "%s", name); |
| |
| output_loc_sequence (AT_loc (a)); |
| break; |
| |
| case dw_val_class_const: |
| /* ??? It would be slightly more efficient to use a scheme like is |
| used for unsigned constants below, but gdb 4.x does not sign |
| extend. Gdb 5.x does sign extend. */ |
| dw2_asm_output_data_sleb128 (AT_int (a), "%s", name); |
| break; |
| |
| case dw_val_class_unsigned_const: |
| dw2_asm_output_data (constant_size (AT_unsigned (a)), |
| AT_unsigned (a), "%s", name); |
| break; |
| |
| case dw_val_class_long_long: |
| { |
| unsigned HOST_WIDE_INT first, second; |
| |
| dw2_asm_output_data (1, |
| 2 * HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR, |
| "%s", name); |
| |
| if (WORDS_BIG_ENDIAN) |
| { |
| first = a->dw_attr_val.v.val_long_long.hi; |
| second = a->dw_attr_val.v.val_long_long.low; |
| } |
| else |
| { |
| first = a->dw_attr_val.v.val_long_long.low; |
| second = a->dw_attr_val.v.val_long_long.hi; |
| } |
| |
| dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR, |
| first, "long long constant"); |
| dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR, |
| second, NULL); |
| } |
| break; |
| |
| case dw_val_class_vec: |
| { |
| unsigned int elt_size = a->dw_attr_val.v.val_vec.elt_size; |
| unsigned int len = a->dw_attr_val.v.val_vec.length; |
| unsigned int i; |
| unsigned char *p; |
| |
| dw2_asm_output_data (1, len * elt_size, "%s", name); |
| if (elt_size > sizeof (HOST_WIDE_INT)) |
| { |
| elt_size /= 2; |
| len *= 2; |
| } |
| for (i = 0, p = a->dw_attr_val.v.val_vec.array; |
| i < len; |
| i++, p += elt_size) |
| dw2_asm_output_data (elt_size, extract_int (p, elt_size), |
| "fp or vector constant word %u", i); |
| break; |
| } |
| |
| case dw_val_class_flag: |
| dw2_asm_output_data (1, AT_flag (a), "%s", name); |
| break; |
| |
| case dw_val_class_loc_list: |
| { |
| char *sym = AT_loc_list (a)->ll_symbol; |
| |
| gcc_assert (sym); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, sym, debug_loc_section, |
| "%s", name); |
| } |
| break; |
| |
| case dw_val_class_die_ref: |
| if (AT_ref_external (a)) |
| { |
| char *sym = AT_ref (a)->die_symbol; |
| |
| gcc_assert (sym); |
| dw2_asm_output_offset (DWARF2_ADDR_SIZE, sym, debug_info_section, |
| "%s", name); |
| } |
| else |
| { |
| gcc_assert (AT_ref (a)->die_offset); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, AT_ref (a)->die_offset, |
| "%s", name); |
| } |
| break; |
| |
| case dw_val_class_fde_ref: |
| { |
| char l1[20]; |
| |
| ASM_GENERATE_INTERNAL_LABEL (l1, FDE_LABEL, |
| a->dw_attr_val.v.val_fde_index * 2); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, l1, debug_frame_section, |
| "%s", name); |
| } |
| break; |
| |
| case dw_val_class_lbl_id: |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name); |
| break; |
| |
| case dw_val_class_lineptr: |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a), |
| debug_line_section, "%s", name); |
| break; |
| |
| case dw_val_class_macptr: |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a), |
| debug_macinfo_section, "%s", name); |
| break; |
| |
| case dw_val_class_str: |
| if (AT_string_form (a) == DW_FORM_strp) |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, |
| a->dw_attr_val.v.val_str->label, |
| debug_str_section, |
| "%s: \"%s\"", name, AT_string (a)); |
| else |
| dw2_asm_output_nstring (AT_string (a), -1, "%s", name); |
| break; |
| |
| case dw_val_class_file: |
| { |
| int f = maybe_emit_file (a->dw_attr_val.v.val_file); |
| |
| dw2_asm_output_data (constant_size (f), f, "%s (%s)", name, |
| a->dw_attr_val.v.val_file->filename); |
| break; |
| } |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| FOR_EACH_CHILD (die, c, output_die (c)); |
| |
| /* Add null byte to terminate sibling list. */ |
| if (die->die_child != NULL) |
| dw2_asm_output_data (1, 0, "end of children of DIE 0x%lx", |
| die->die_offset); |
| } |
| |
| /* Output the compilation unit that appears at the beginning of the |
| .debug_info section, and precedes the DIE descriptions. */ |
| |
| static void |
| output_compilation_unit_header (void) |
| { |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, |
| next_die_offset - DWARF_INITIAL_LENGTH_SIZE, |
| "Length of Compilation Unit Info"); |
| dw2_asm_output_data (2, DWARF_VERSION, "DWARF version number"); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, abbrev_section_label, |
| debug_abbrev_section, |
| "Offset Into Abbrev. Section"); |
| dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)"); |
| } |
| |
| /* Output the compilation unit DIE and its children. */ |
| |
| static void |
| output_comp_unit (dw_die_ref die, int output_if_empty) |
| { |
| const char *secname; |
| char *oldsym, *tmp; |
| |
| /* Unless we are outputting main CU, we may throw away empty ones. */ |
| if (!output_if_empty && die->die_child == NULL) |
| return; |
| |
| /* Even if there are no children of this DIE, we must output the information |
| about the compilation unit. Otherwise, on an empty translation unit, we |
| will generate a present, but empty, .debug_info section. IRIX 6.5 `nm' |
| will then complain when examining the file. First mark all the DIEs in |
| this CU so we know which get local refs. */ |
| mark_dies (die); |
| |
| build_abbrev_table (die); |
| |
| /* Initialize the beginning DIE offset - and calculate sizes/offsets. */ |
| next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE; |
| calc_die_sizes (die); |
| |
| oldsym = die->die_symbol; |
| if (oldsym) |
| { |
| tmp = alloca (strlen (oldsym) + 24); |
| |
| sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym); |
| secname = tmp; |
| die->die_symbol = NULL; |
| switch_to_section (get_section (secname, SECTION_DEBUG, NULL)); |
| } |
| else |
| switch_to_section (debug_info_section); |
| |
| /* Output debugging information. */ |
| output_compilation_unit_header (); |
| output_die (die); |
| |
| /* Leave the marks on the main CU, so we can check them in |
| output_pubnames. */ |
| if (oldsym) |
| { |
| unmark_dies (die); |
| die->die_symbol = oldsym; |
| } |
| } |
| |
| /* Return the DWARF2/3 pubname associated with a decl. */ |
| |
| static const char * |
| dwarf2_name (tree decl, int scope) |
| { |
| return lang_hooks.dwarf_name (decl, scope ? 1 : 0); |
| } |
| |
| /* Add a new entry to .debug_pubnames if appropriate. */ |
| |
| static void |
| add_pubname (tree decl, dw_die_ref die) |
| { |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| pubname_entry e; |
| |
| if (! TREE_PUBLIC (decl)) |
| return; |
| |
| e.die = die; |
| e.name = xstrdup (dwarf2_name (decl, 1)); |
| VEC_safe_push (pubname_entry, gc, pubname_table, &e); |
| } |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| /* Add a new entry to .debug_pubtypes if appropriate. */ |
| |
| static void |
| add_pubtype (tree decl, dw_die_ref die) |
| { |
| pubname_entry e; |
| |
| e.name = NULL; |
| if ((TREE_PUBLIC (decl) |
| || die->die_parent == comp_unit_die) |
| && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl))) |
| { |
| e.die = die; |
| if (TYPE_P (decl)) |
| { |
| if (TYPE_NAME (decl)) |
| { |
| if (TREE_CODE (TYPE_NAME (decl)) == IDENTIFIER_NODE) |
| e.name = |
| xstrdup ((const char *) IDENTIFIER_POINTER (TYPE_NAME (decl))); |
| else if (TREE_CODE (TYPE_NAME (decl)) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (decl))) |
| e.name = |
| xstrdup ((const char *) IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl)))); |
| else |
| e.name = xstrdup ((const char *) get_AT_string (die, DW_AT_name)); |
| } |
| } |
| else |
| e.name = xstrdup (dwarf2_name (decl, 1)); |
| |
| /* If we don't have a name for the type, there's no point in adding |
| it to the table. */ |
| if (e.name && e.name[0] != '\0') |
| VEC_safe_push (pubname_entry, gc, pubtype_table, &e); |
| } |
| } |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| static void |
| add_inlined_section_entry (dw_die_ref die) |
| { |
| unsigned i; |
| bool found = false; |
| inlined_ref iptr; |
| dw_die_ref current_origin_die = get_AT_ref (die, DW_AT_abstract_origin); |
| |
| /* APPLE LOCAL begin radar 6292557 */ |
| if (!current_origin_die) |
| return; |
| /* APPLE LOCAL end radar 6292557 */ |
| |
| if (!has_AT (die, DW_AT_low_pc)) |
| return; |
| |
| for (i = 0; VEC_iterate (inlined_entry, debug_inlined_table, i, iptr); i++) |
| { |
| if (iptr->origin_die == current_origin_die) |
| { |
| VEC_safe_push (dw_die_ref, gc, iptr->inlined_instances, &die); |
| found = true; |
| } |
| } |
| |
| if (!found) |
| { |
| inlined_entry *new_entry = (inlined_entry *) xmalloc (sizeof (inlined_entry)); |
| new_entry->origin_die = current_origin_die; |
| new_entry->inlined_instances = VEC_alloc (dw_die_ref, gc, 32); |
| VEC_safe_push (dw_die_ref, gc, new_entry->inlined_instances, &die); |
| VEC_safe_push (inlined_entry, gc, debug_inlined_table, new_entry); |
| } |
| } |
| |
| static void |
| output_debug_inlined_section (VEC (inlined_entry, gc) * inlined_dies) |
| { |
| unsigned i; |
| unsigned long inlined_length = size_of_inlined (inlined_dies); |
| inlined_entry *iptr; |
| |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, inlined_length, |
| "Length of Inlined Subroutines Info"); |
| dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version"); |
| /* |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label, |
| debug_info_section, |
| "Offset of Compilation Unit Info"); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset, |
| "Compilation Unit Length"); |
| */ |
| dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)"); |
| |
| for (i = 0; VEC_iterate (inlined_entry, inlined_dies, i, iptr); i++) |
| { |
| dw_attr_ref name_attr; |
| dw_attr_ref mips_name_attr; |
| dw_die_ref *diep; |
| unsigned j; |
| unsigned num_entries = VEC_length (dw_die_ref, iptr->inlined_instances); |
| dw_die_ref origin_die = iptr->origin_die; |
| |
| mips_name_attr = get_AT (origin_die, DW_AT_MIPS_linkage_name); |
| name_attr = get_AT (origin_die, DW_AT_name); |
| |
| if (!mips_name_attr) |
| mips_name_attr = name_attr; |
| |
| gcc_assert (origin_die != NULL); |
| gcc_assert (name_attr != NULL); |
| gcc_assert (mips_name_attr != NULL); |
| gcc_assert (num_entries > 0); |
| gcc_assert (AT_string_form (name_attr) == DW_FORM_strp); |
| gcc_assert (AT_string_form (mips_name_attr) == DW_FORM_strp); |
| |
| /* |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, origin_die->die_offset, |
| "Origin die offset"); |
| */ |
| |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, |
| mips_name_attr->dw_attr_val.v.val_str->label, |
| debug_str_section, |
| "MIPS linkage name: \"%s\"", |
| AT_string (mips_name_attr)); |
| |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, |
| name_attr->dw_attr_val.v.val_str->label, |
| debug_str_section, |
| "Function name: \"%s\"", AT_string (name_attr)); |
| |
| dw2_asm_output_data_uleb128 (num_entries, NULL); |
| |
| for (j = 0; VEC_iterate (dw_die_ref, iptr->inlined_instances, j, diep); |
| j++) |
| { |
| const char *low_pc = get_AT_low_pc (*diep); |
| |
| gcc_assert (low_pc != NULL); |
| |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, (*diep)->die_offset, |
| "inlined subroutine die offset"); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, low_pc, "%s", |
| "Low PC address"); |
| |
| } |
| } |
| } |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| /* Output the public names table used to speed up access to externally |
| visible names; or the public types table used to find type |
| definitions. For now, only generate entries for externally |
| visible procedures (in pubnames table). */ |
| |
| static void |
| output_pubnames (VEC (pubname_entry, gc) * names) |
| { |
| unsigned i; |
| unsigned long pubnames_length = size_of_pubnames (names); |
| pubname_ref pub; |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (names == pubname_table) |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length, |
| "Length of Public Names Info"); |
| else |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length, |
| "Length of Public Type Names Info"); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version"); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label, |
| debug_info_section, |
| "Offset of Compilation Unit Info"); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset, |
| "Compilation Unit Length"); |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| for (i = 0; VEC_iterate (pubname_entry, names, i, pub); i++) |
| { |
| /* We shouldn't see pubnames for DIEs outside of the main CU. */ |
| if (names == pubname_table) |
| gcc_assert (pub->die->die_mark); |
| |
| if (names != pubtype_table |
| || pub->die->die_offset != 0 |
| || !flag_eliminate_unused_debug_types) |
| { |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, pub->die->die_offset, |
| "DIE offset"); |
| |
| dw2_asm_output_nstring (pub->name, -1, "external name"); |
| } |
| } |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL); |
| } |
| |
| /* Add a new entry to .debug_aranges if appropriate. */ |
| |
| static void |
| add_arange (tree decl, dw_die_ref die) |
| { |
| if (! DECL_SECTION_NAME (decl)) |
| return; |
| |
| if (arange_table_in_use == arange_table_allocated) |
| { |
| arange_table_allocated += ARANGE_TABLE_INCREMENT; |
| arange_table = ggc_realloc (arange_table, |
| (arange_table_allocated |
| * sizeof (dw_die_ref))); |
| memset (arange_table + arange_table_in_use, 0, |
| ARANGE_TABLE_INCREMENT * sizeof (dw_die_ref)); |
| } |
| |
| arange_table[arange_table_in_use++] = die; |
| } |
| |
| /* Output the information that goes into the .debug_aranges table. |
| Namely, define the beginning and ending address range of the |
| text section generated for this compilation unit. */ |
| |
| static void |
| output_aranges (void) |
| { |
| unsigned i; |
| unsigned long aranges_length = size_of_aranges (); |
| |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length, |
| "Length of Address Ranges Info"); |
| dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version"); |
| dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label, |
| debug_info_section, |
| "Offset of Compilation Unit Info"); |
| dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address"); |
| dw2_asm_output_data (1, 0, "Size of Segment Descriptor"); |
| |
| /* We need to align to twice the pointer size here. */ |
| if (DWARF_ARANGES_PAD_SIZE) |
| { |
| /* Pad using a 2 byte words so that padding is correct for any |
| pointer size. */ |
| dw2_asm_output_data (2, 0, "Pad to %d byte boundary", |
| 2 * DWARF2_ADDR_SIZE); |
| for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2) |
| dw2_asm_output_data (2, 0, NULL); |
| } |
| |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_section_label, "Address"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label, |
| text_section_label, "Length"); |
| if (flag_reorder_blocks_and_partition) |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, cold_text_section_label, |
| "Address"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, cold_end_label, |
| cold_text_section_label, "Length"); |
| } |
| |
| for (i = 0; i < arange_table_in_use; i++) |
| { |
| dw_die_ref die = arange_table[i]; |
| |
| /* We shouldn't see aranges for DIEs outside of the main CU. */ |
| gcc_assert (die->die_mark); |
| |
| if (die->die_tag == DW_TAG_subprogram) |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, get_AT_low_pc (die), |
| "Address"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, get_AT_hi_pc (die), |
| get_AT_low_pc (die), "Length"); |
| } |
| else |
| { |
| /* A static variable; extract the symbol from DW_AT_location. |
| Note that this code isn't currently hit, as we only emit |
| aranges for functions (jason 9/23/99). */ |
| dw_attr_ref a = get_AT (die, DW_AT_location); |
| dw_loc_descr_ref loc; |
| |
| gcc_assert (a && AT_class (a) == dw_val_class_loc); |
| |
| loc = AT_loc (a); |
| gcc_assert (loc->dw_loc_opc == DW_OP_addr); |
| |
| dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, |
| loc->dw_loc_oprnd1.v.val_addr, "Address"); |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, |
| get_AT_unsigned (die, DW_AT_byte_size), |
| "Length"); |
| } |
| } |
| |
| /* Output the terminator words. */ |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL); |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL); |
| } |
| |
| /* Add a new entry to .debug_ranges. Return the offset at which it |
| was placed. */ |
| |
| static unsigned int |
| add_ranges (tree block) |
| { |
| unsigned int in_use = ranges_table_in_use; |
| |
| if (in_use == ranges_table_allocated) |
| { |
| ranges_table_allocated += RANGES_TABLE_INCREMENT; |
| ranges_table |
| = ggc_realloc (ranges_table, (ranges_table_allocated |
| * sizeof (struct dw_ranges_struct))); |
| memset (ranges_table + ranges_table_in_use, 0, |
| RANGES_TABLE_INCREMENT * sizeof (struct dw_ranges_struct)); |
| } |
| |
| ranges_table[in_use].block_num = (block ? BLOCK_NUMBER (block) : 0); |
| ranges_table_in_use = in_use + 1; |
| |
| return in_use * 2 * DWARF2_ADDR_SIZE; |
| } |
| |
| static void |
| output_ranges (void) |
| { |
| unsigned i; |
| static const char *const start_fmt = "Offset 0x%x"; |
| const char *fmt = start_fmt; |
| |
| for (i = 0; i < ranges_table_in_use; i++) |
| { |
| int block_num = ranges_table[i].block_num; |
| |
| if (block_num) |
| { |
| char blabel[MAX_ARTIFICIAL_LABEL_BYTES]; |
| char elabel[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| ASM_GENERATE_INTERNAL_LABEL (blabel, BLOCK_BEGIN_LABEL, block_num); |
| ASM_GENERATE_INTERNAL_LABEL (elabel, BLOCK_END_LABEL, block_num); |
| |
| /* If all code is in the text section, then the compilation |
| unit base address defaults to DW_AT_low_pc, which is the |
| base of the text section. */ |
| if (!have_multiple_function_sections) |
| { |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel, |
| text_section_label, |
| fmt, i * 2 * DWARF2_ADDR_SIZE); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel, |
| text_section_label, NULL); |
| } |
| |
| /* Otherwise, we add a DW_AT_entry_pc attribute to force the |
| compilation unit base address to zero, which allows us to |
| use absolute addresses, and not worry about whether the |
| target supports cross-section arithmetic. */ |
| else |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, blabel, |
| fmt, i * 2 * DWARF2_ADDR_SIZE); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, elabel, NULL); |
| } |
| |
| fmt = NULL; |
| } |
| else |
| { |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL); |
| dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL); |
| fmt = start_fmt; |
| } |
| } |
| } |
| |
| /* Data structure containing information about input files. */ |
| struct file_info |
| { |
| const char *path; /* Complete file name. */ |
| const char *fname; /* File name part. */ |
| int length; /* Length of entire string. */ |
| struct dwarf_file_data * file_idx; /* Index in input file table. */ |
| int dir_idx; /* Index in directory table. */ |
| }; |
| |
| /* Data structure containing information about directories with source |
| files. */ |
| struct dir_info |
| { |
| const char *path; /* Path including directory name. */ |
| int length; /* Path length. */ |
| int prefix; /* Index of directory entry which is a prefix. */ |
| int count; /* Number of files in this directory. */ |
| int dir_idx; /* Index of directory used as base. */ |
| }; |
| |
| /* Callback function for file_info comparison. We sort by looking at |
| the directories in the path. */ |
| |
| static int |
| file_info_cmp (const void *p1, const void *p2) |
| { |
| const struct file_info *s1 = p1; |
| const struct file_info *s2 = p2; |
| unsigned char *cp1; |
| unsigned char *cp2; |
| |
| /* Take care of file names without directories. We need to make sure that |
| we return consistent values to qsort since some will get confused if |
| we return the same value when identical operands are passed in opposite |
| orders. So if neither has a directory, return 0 and otherwise return |
| 1 or -1 depending on which one has the directory. */ |
| if ((s1->path == s1->fname || s2->path == s2->fname)) |
| return (s2->path == s2->fname) - (s1->path == s1->fname); |
| |
| cp1 = (unsigned char *) s1->path; |
| cp2 = (unsigned char *) s2->path; |
| |
| while (1) |
| { |
| ++cp1; |
| ++cp2; |
| /* Reached the end of the first path? If so, handle like above. */ |
| if ((cp1 == (unsigned char *) s1->fname) |
| || (cp2 == (unsigned char *) s2->fname)) |
| return ((cp2 == (unsigned char *) s2->fname) |
| - (cp1 == (unsigned char *) s1->fname)); |
| |
| /* Character of current path component the same? */ |
| else if (*cp1 != *cp2) |
| return *cp1 - *cp2; |
| } |
| } |
| |
| struct file_name_acquire_data |
| { |
| struct file_info *files; |
| int used_files; |
| int max_files; |
| }; |
| |
| /* Traversal function for the hash table. */ |
| |
| static int |
| file_name_acquire (void ** slot, void *data) |
| { |
| struct file_name_acquire_data *fnad = data; |
| struct dwarf_file_data *d = *slot; |
| struct file_info *fi; |
| const char *f; |
| |
| gcc_assert (fnad->max_files >= d->emitted_number); |
| |
| if (! d->emitted_number) |
| return 1; |
| |
| gcc_assert (fnad->max_files != fnad->used_files); |
| |
| fi = fnad->files + fnad->used_files++; |
| |
| /* Skip all leading "./". */ |
| f = d->filename; |
| while (f[0] == '.' && f[1] == '/') |
| f += 2; |
| |
| /* Create a new array entry. */ |
| fi->path = f; |
| fi->length = strlen (f); |
| fi->file_idx = d; |
| |
| /* Search for the file name part. */ |
| f = strrchr (f, '/'); |
| fi->fname = f == NULL ? fi->path : f + 1; |
| return 1; |
| } |
| |
| /* Output the directory table and the file name table. We try to minimize |
| the total amount of memory needed. A heuristic is used to avoid large |
| slowdowns with many input files. */ |
| |
| static void |
| output_file_names (void) |
| { |
| struct file_name_acquire_data fnad; |
| int numfiles; |
| struct file_info *files; |
| struct dir_info *dirs; |
| int *saved; |
| int *savehere; |
| int *backmap; |
| int ndirs; |
| int idx_offset; |
| int i; |
| int idx; |
| |
| if (!last_emitted_file) |
| { |
| dw2_asm_output_data (1, 0, "End directory table"); |
| dw2_asm_output_data (1, 0, "End file name table"); |
| return; |
| } |
| |
| numfiles = last_emitted_file->emitted_number; |
| |
| /* Allocate the various arrays we need. */ |
| files = alloca (numfiles * sizeof (struct file_info)); |
| dirs = alloca (numfiles * sizeof (struct dir_info)); |
| |
| fnad.files = files; |
| fnad.used_files = 0; |
| fnad.max_files = numfiles; |
| htab_traverse (file_table, file_name_acquire, &fnad); |
| gcc_assert (fnad.used_files == fnad.max_files); |
| |
| qsort (files, numfiles, sizeof (files[0]), file_info_cmp); |
| |
| /* Find all the different directories used. */ |
| dirs[0].path = files[0].path; |
| dirs[0].length = files[0].fname - files[0].path; |
| dirs[0].prefix = -1; |
| dirs[0].count = 1; |
| dirs[0].dir_idx = 0; |
| files[0].dir_idx = 0; |
| ndirs = 1; |
| |
| for (i = 1; i < numfiles; i++) |
| if (files[i].fname - files[i].path == dirs[ndirs - 1].length |
| && memcmp (dirs[ndirs - 1].path, files[i].path, |
| dirs[ndirs - 1].length) == 0) |
| { |
| /* Same directory as last entry. */ |
| files[i].dir_idx = ndirs - 1; |
| ++dirs[ndirs - 1].count; |
| } |
| else |
| { |
| int j; |
| |
| /* This is a new directory. */ |
| dirs[ndirs].path = files[i].path; |
| dirs[ndirs].length = files[i].fname - files[i].path; |
| dirs[ndirs].count = 1; |
| dirs[ndirs].dir_idx = ndirs; |
| files[i].dir_idx = ndirs; |
| |
| /* Search for a prefix. */ |
| dirs[ndirs].prefix = -1; |
| for (j = 0; j < ndirs; j++) |
| if (dirs[j].length < dirs[ndirs].length |
| && dirs[j].length > 1 |
| && (dirs[ndirs].prefix == -1 |
| || dirs[j].length > dirs[dirs[ndirs].prefix].length) |
| && memcmp (dirs[j].path, dirs[ndirs].path, dirs[j].length) == 0) |
| dirs[ndirs].prefix = j; |
| |
| ++ndirs; |
| } |
| |
| /* Now to the actual work. We have to find a subset of the directories which |
| allow expressing the file name using references to the directory table |
| with the least amount of characters. We do not do an exhaustive search |
| where we would have to check out every combination of every single |
| possible prefix. Instead we use a heuristic which provides nearly optimal |
| results in most cases and never is much off. */ |
| saved = alloca (ndirs * sizeof (int)); |
| savehere = alloca (ndirs * sizeof (int)); |
| |
| memset (saved, '\0', ndirs * sizeof (saved[0])); |
| for (i = 0; i < ndirs; i++) |
| { |
| int j; |
| int total; |
| |
| /* We can always save some space for the current directory. But this |
| does not mean it will be enough to justify adding the directory. */ |
| savehere[i] = dirs[i].length; |
| total = (savehere[i] - saved[i]) * dirs[i].count; |
| |
| for (j = i + 1; j < ndirs; j++) |
| { |
| savehere[j] = 0; |
| if (saved[j] < dirs[i].length) |
| { |
| /* Determine whether the dirs[i] path is a prefix of the |
| dirs[j] path. */ |
| int k; |
| |
| k = dirs[j].prefix; |
| while (k != -1 && k != (int) i) |
| k = dirs[k].prefix; |
| |
| if (k == (int) i) |
| { |
| /* Yes it is. We can possibly save some memory by |
| writing the filenames in dirs[j] relative to |
| dirs[i]. */ |
| savehere[j] = dirs[i].length; |
| total += (savehere[j] - saved[j]) * dirs[j].count; |
| } |
| } |
| } |
| |
| /* Check whether we can save enough to justify adding the dirs[i] |
| directory. */ |
| if (total > dirs[i].length + 1) |
| { |
| /* It's worthwhile adding. */ |
| for (j = i; j < ndirs; j++) |
| if (savehere[j] > 0) |
| { |
| /* Remember how much we saved for this directory so far. */ |
| saved[j] = savehere[j]; |
| |
| /* Remember the prefix directory. */ |
| dirs[j].dir_idx = i; |
| } |
| } |
| } |
| |
| /* Emit the directory name table. */ |
| idx = 1; |
| idx_offset = dirs[0].length > 0 ? 1 : 0; |
| for (i = 1 - idx_offset; i < ndirs; i++) |
| dw2_asm_output_nstring (dirs[i].path, dirs[i].length - 1, |
| "Directory Entry: 0x%x", i + idx_offset); |
| |
| dw2_asm_output_data (1, 0, "End directory table"); |
| |
| /* We have to emit them in the order of emitted_number since that's |
| used in the debug info generation. To do this efficiently we |
| generate a back-mapping of the indices first. */ |
| backmap = alloca (numfiles * sizeof (int)); |
| for (i = 0; i < numfiles; i++) |
| backmap[files[i].file_idx->emitted_number - 1] = i; |
| |
| /* Now write all the file names. */ |
| for (i = 0; i < numfiles; i++) |
| { |
| int file_idx = backmap[i]; |
| int dir_idx = dirs[files[file_idx].dir_idx].dir_idx; |
| |
| dw2_asm_output_nstring (files[file_idx].path + dirs[dir_idx].length, -1, |
| "File Entry: 0x%x", (unsigned) i + 1); |
| |
| /* Include directory index. */ |
| dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL); |
| |
| /* Modification time. */ |
| dw2_asm_output_data_uleb128 (0, NULL); |
| |
| /* File length in bytes. */ |
| dw2_asm_output_data_uleb128 (0, NULL); |
| } |
| |
| dw2_asm_output_data (1, 0, "End file name table"); |
| } |
| |
| |
| /* Output the source line number correspondence information. This |
| information goes into the .debug_line section. */ |
| |
| static void |
| output_line_info (void) |
| { |
| char l1[20], l2[20], p1[20], p2[20]; |
| char line_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| unsigned opc; |
| unsigned n_op_args; |
| unsigned long lt_index; |
| unsigned long current_line; |
| long line_offset; |
| long line_delta; |
| unsigned long current_file; |
| unsigned long function; |
| |
| ASM_GENERATE_INTERNAL_LABEL (l1, LINE_NUMBER_BEGIN_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (l2, LINE_NUMBER_END_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (p1, LN_PROLOG_AS_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (p2, LN_PROLOG_END_LABEL, 0); |
| |
| if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) |
| dw2_asm_output_data (4, 0xffffffff, |
| "Initial length escape value indicating 64-bit DWARF extension"); |
| dw2_asm_output_delta (DWARF_OFFSET_SIZE, l2, l1, |
| "Length of Source Line Info"); |
| ASM_OUTPUT_LABEL (asm_out_file, l1); |
| |
| dw2_asm_output_data (2, DWARF_VERSION, "DWARF Version"); |
| dw2_asm_output_delta (DWARF_OFFSET_SIZE, p2, p1, "Prolog Length"); |
| ASM_OUTPUT_LABEL (asm_out_file, p1); |
| |
| /* Define the architecture-dependent minimum instruction length (in |
| bytes). In this implementation of DWARF, this field is used for |
| information purposes only. Since GCC generates assembly language, |
| we have no a priori knowledge of how many instruction bytes are |
| generated for each source line, and therefore can use only the |
| DW_LNE_set_address and DW_LNS_fixed_advance_pc line information |
| commands. Accordingly, we fix this as `1', which is "correct |
| enough" for all architectures, and don't let the target override. */ |
| dw2_asm_output_data (1, 1, |
| "Minimum Instruction Length"); |
| |
| dw2_asm_output_data (1, DWARF_LINE_DEFAULT_IS_STMT_START, |
| "Default is_stmt_start flag"); |
| dw2_asm_output_data (1, DWARF_LINE_BASE, |
| "Line Base Value (Special Opcodes)"); |
| dw2_asm_output_data (1, DWARF_LINE_RANGE, |
| "Line Range Value (Special Opcodes)"); |
| dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE, |
| "Special Opcode Base"); |
| |
| for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; opc++) |
| { |
| switch (opc) |
| { |
| case DW_LNS_advance_pc: |
| case DW_LNS_advance_line: |
| case DW_LNS_set_file: |
| case DW_LNS_set_column: |
| case DW_LNS_fixed_advance_pc: |
| n_op_args = 1; |
| break; |
| default: |
| n_op_args = 0; |
| break; |
| } |
| |
| dw2_asm_output_data (1, n_op_args, "opcode: 0x%x has %d args", |
| opc, n_op_args); |
| } |
| |
| /* Write out the information about the files we use. */ |
| output_file_names (); |
| ASM_OUTPUT_LABEL (asm_out_file, p2); |
| |
| /* We used to set the address register to the first location in the text |
| section here, but that didn't accomplish anything since we already |
| have a line note for the opening brace of the first function. */ |
| |
| /* Generate the line number to PC correspondence table, encoded as |
| a series of state machine operations. */ |
| current_file = 1; |
| current_line = 1; |
| |
| if (cfun && in_cold_section_p) |
| strcpy (prev_line_label, cfun->cold_section_label); |
| else |
| strcpy (prev_line_label, text_section_label); |
| for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) |
| { |
| dw_line_info_ref line_info = &line_info_table[lt_index]; |
| |
| #if 0 |
| /* Disable this optimization for now; GDB wants to see two line notes |
| at the beginning of a function so it can find the end of the |
| prologue. */ |
| |
| /* Don't emit anything for redundant notes. Just updating the |
| address doesn't accomplish anything, because we already assume |
| that anything after the last address is this line. */ |
| if (line_info->dw_line_num == current_line |
| && line_info->dw_file_num == current_file) |
| continue; |
| #endif |
| |
| /* Emit debug info for the address of the current line. |
| |
| Unfortunately, we have little choice here currently, and must always |
| use the most general form. GCC does not know the address delta |
| itself, so we can't use DW_LNS_advance_pc. Many ports do have length |
| attributes which will give an upper bound on the address range. We |
| could perhaps use length attributes to determine when it is safe to |
| use DW_LNS_fixed_advance_pc. */ |
| |
| ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index); |
| if (0) |
| { |
| /* This can handle deltas up to 0xffff. This takes 3 bytes. */ |
| dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, |
| "DW_LNS_fixed_advance_pc"); |
| dw2_asm_output_delta (2, line_label, prev_line_label, NULL); |
| } |
| else |
| { |
| /* This can handle any delta. This takes |
| 4+DWARF2_ADDR_SIZE bytes. */ |
| dw2_asm_output_data (1, 0, "DW_LNE_set_address"); |
| dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); |
| dw2_asm_output_data (1, DW_LNE_set_address, NULL); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); |
| } |
| |
| strcpy (prev_line_label, line_label); |
| |
| /* Emit debug info for the source file of the current line, if |
| different from the previous line. */ |
| if (line_info->dw_file_num != current_file) |
| { |
| current_file = line_info->dw_file_num; |
| dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file"); |
| dw2_asm_output_data_uleb128 (current_file, "%lu", current_file); |
| } |
| |
| /* Emit debug info for the current line number, choosing the encoding |
| that uses the least amount of space. */ |
| if (line_info->dw_line_num != current_line) |
| { |
| line_offset = line_info->dw_line_num - current_line; |
| line_delta = line_offset - DWARF_LINE_BASE; |
| current_line = line_info->dw_line_num; |
| if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) |
| /* This can handle deltas from -10 to 234, using the current |
| definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This |
| takes 1 byte. */ |
| dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta, |
| "line %lu", current_line); |
| else |
| { |
| /* This can handle any delta. This takes at least 4 bytes, |
| depending on the value being encoded. */ |
| dw2_asm_output_data (1, DW_LNS_advance_line, |
| "advance to line %lu", current_line); |
| dw2_asm_output_data_sleb128 (line_offset, NULL); |
| dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); |
| } |
| } |
| else |
| /* We still need to start a new row, so output a copy insn. */ |
| dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); |
| } |
| |
| /* Emit debug info for the address of the end of the function. */ |
| if (0) |
| { |
| dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, |
| "DW_LNS_fixed_advance_pc"); |
| dw2_asm_output_delta (2, text_end_label, prev_line_label, NULL); |
| } |
| else |
| { |
| dw2_asm_output_data (1, 0, "DW_LNE_set_address"); |
| dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); |
| dw2_asm_output_data (1, DW_LNE_set_address, NULL); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_end_label, NULL); |
| } |
| |
| dw2_asm_output_data (1, 0, "DW_LNE_end_sequence"); |
| dw2_asm_output_data_uleb128 (1, NULL); |
| dw2_asm_output_data (1, DW_LNE_end_sequence, NULL); |
| |
| function = 0; |
| current_file = 1; |
| current_line = 1; |
| for (lt_index = 0; lt_index < separate_line_info_table_in_use;) |
| { |
| dw_separate_line_info_ref line_info |
| = &separate_line_info_table[lt_index]; |
| |
| #if 0 |
| /* Don't emit anything for redundant notes. */ |
| if (line_info->dw_line_num == current_line |
| && line_info->dw_file_num == current_file |
| && line_info->function == function) |
| goto cont; |
| #endif |
| |
| /* Emit debug info for the address of the current line. If this is |
| a new function, or the first line of a function, then we need |
| to handle it differently. */ |
| ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL, |
| lt_index); |
| if (function != line_info->function) |
| { |
| function = line_info->function; |
| |
| /* Set the address register to the first line in the function. */ |
| dw2_asm_output_data (1, 0, "DW_LNE_set_address"); |
| dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); |
| dw2_asm_output_data (1, DW_LNE_set_address, NULL); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); |
| } |
| else |
| { |
| /* ??? See the DW_LNS_advance_pc comment above. */ |
| if (0) |
| { |
| dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, |
| "DW_LNS_fixed_advance_pc"); |
| dw2_asm_output_delta (2, line_label, prev_line_label, NULL); |
| } |
| else |
| { |
| dw2_asm_output_data (1, 0, "DW_LNE_set_address"); |
| dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); |
| dw2_asm_output_data (1, DW_LNE_set_address, NULL); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); |
| } |
| } |
| |
| strcpy (prev_line_label, line_label); |
| |
| /* Emit debug info for the source file of the current line, if |
| different from the previous line. */ |
| if (line_info->dw_file_num != current_file) |
| { |
| current_file = line_info->dw_file_num; |
| dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file"); |
| dw2_asm_output_data_uleb128 (current_file, "%lu", current_file); |
| } |
| |
| /* Emit debug info for the current line number, choosing the encoding |
| that uses the least amount of space. */ |
| if (line_info->dw_line_num != current_line) |
| { |
| line_offset = line_info->dw_line_num - current_line; |
| line_delta = line_offset - DWARF_LINE_BASE; |
| current_line = line_info->dw_line_num; |
| if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) |
| dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta, |
| "line %lu", current_line); |
| else |
| { |
| dw2_asm_output_data (1, DW_LNS_advance_line, |
| "advance to line %lu", current_line); |
| dw2_asm_output_data_sleb128 (line_offset, NULL); |
| dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); |
| } |
| } |
| else |
| dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); |
| |
| #if 0 |
| cont: |
| #endif |
| |
| lt_index++; |
| |
| /* If we're done with a function, end its sequence. */ |
| if (lt_index == separate_line_info_table_in_use |
| || separate_line_info_table[lt_index].function != function) |
| { |
| current_file = 1; |
| current_line = 1; |
| |
| /* Emit debug info for the address of the end of the function. */ |
| ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function); |
| if (0) |
| { |
| dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, |
| "DW_LNS_fixed_advance_pc"); |
| dw2_asm_output_delta (2, line_label, prev_line_label, NULL); |
| } |
| else |
| { |
| dw2_asm_output_data (1, 0, "DW_LNE_set_address"); |
| dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); |
| dw2_asm_output_data (1, DW_LNE_set_address, NULL); |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); |
| } |
| |
| /* Output the marker for the end of this sequence. */ |
| dw2_asm_output_data (1, 0, "DW_LNE_end_sequence"); |
| dw2_asm_output_data_uleb128 (1, NULL); |
| dw2_asm_output_data (1, DW_LNE_end_sequence, NULL); |
| } |
| } |
| |
| /* Output the marker for the end of the line number info. */ |
| ASM_OUTPUT_LABEL (asm_out_file, l2); |
| } |
| |
| /* Given a pointer to a tree node for some base type, return a pointer to |
| a DIE that describes the given type. |
| |
| This routine must only be called for GCC type nodes that correspond to |
| Dwarf base (fundamental) types. */ |
| |
| static dw_die_ref |
| base_type_die (tree type) |
| { |
| dw_die_ref base_type_result; |
| enum dwarf_type encoding; |
| |
| if (TREE_CODE (type) == ERROR_MARK || TREE_CODE (type) == VOID_TYPE) |
| return 0; |
| |
| switch (TREE_CODE (type)) |
| { |
| case INTEGER_TYPE: |
| if (TYPE_STRING_FLAG (type)) |
| { |
| if (TYPE_UNSIGNED (type)) |
| encoding = DW_ATE_unsigned_char; |
| else |
| encoding = DW_ATE_signed_char; |
| } |
| else if (TYPE_UNSIGNED (type)) |
| encoding = DW_ATE_unsigned; |
| else |
| encoding = DW_ATE_signed; |
| break; |
| |
| case REAL_TYPE: |
| if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type))) |
| encoding = DW_ATE_decimal_float; |
| else |
| encoding = DW_ATE_float; |
| break; |
| |
| /* Dwarf2 doesn't know anything about complex ints, so use |
| a user defined type for it. */ |
| case COMPLEX_TYPE: |
| if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE) |
| encoding = DW_ATE_complex_float; |
| else |
| encoding = DW_ATE_lo_user; |
| break; |
| |
| case BOOLEAN_TYPE: |
| /* GNU FORTRAN/Ada/C++ BOOLEAN type. */ |
| encoding = DW_ATE_boolean; |
| break; |
| |
| default: |
| /* No other TREE_CODEs are Dwarf fundamental types. */ |
| gcc_unreachable (); |
| } |
| |
| base_type_result = new_die (DW_TAG_base_type, comp_unit_die, type); |
| |
| /* This probably indicates a bug. */ |
| if (! TYPE_NAME (type)) |
| add_name_attribute (base_type_result, "__unknown__"); |
| |
| add_AT_unsigned (base_type_result, DW_AT_byte_size, |
| int_size_in_bytes (type)); |
| add_AT_unsigned (base_type_result, DW_AT_encoding, encoding); |
| |
| return base_type_result; |
| } |
| |
| /* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to |
| the Dwarf "root" type for the given input type. The Dwarf "root" type of |
| a given type is generally the same as the given type, except that if the |
| given type is a pointer or reference type, then the root type of the given |
| type is the root type of the "basis" type for the pointer or reference |
| type. (This definition of the "root" type is recursive.) Also, the root |
| type of a `const' qualified type or a `volatile' qualified type is the |
| root type of the given type without the qualifiers. */ |
| |
| static tree |
| root_type (tree type) |
| { |
| if (TREE_CODE (type) == ERROR_MARK) |
| return error_mark_node; |
| |
| switch (TREE_CODE (type)) |
| { |
| case ERROR_MARK: |
| return error_mark_node; |
| |
| /* APPLE LOCAL radar 5732232 - blocks */ |
| case BLOCK_POINTER_TYPE: |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| return type_main_variant (root_type (TREE_TYPE (type))); |
| |
| default: |
| return type_main_variant (type); |
| } |
| } |
| |
| /* Given a pointer to an arbitrary ..._TYPE tree node, return nonzero if the |
| given input type is a Dwarf "fundamental" type. Otherwise return null. */ |
| |
| static inline int |
| is_base_type (tree type) |
| { |
| switch (TREE_CODE (type)) |
| { |
| case ERROR_MARK: |
| case VOID_TYPE: |
| case INTEGER_TYPE: |
| case REAL_TYPE: |
| case COMPLEX_TYPE: |
| case BOOLEAN_TYPE: |
| return 1; |
| |
| case ARRAY_TYPE: |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| case ENUMERAL_TYPE: |
| case FUNCTION_TYPE: |
| case METHOD_TYPE: |
| /* APPLE LOCAL radar 5732232 - blocks */ |
| case BLOCK_POINTER_TYPE: |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| case OFFSET_TYPE: |
| case LANG_TYPE: |
| case VECTOR_TYPE: |
| return 0; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return 0; |
| } |
| |
| /* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE |
| node, return the size in bits for the type if it is a constant, or else |
| return the alignment for the type if the type's size is not constant, or |
| else return BITS_PER_WORD if the type actually turns out to be an |
| ERROR_MARK node. */ |
| |
| static inline unsigned HOST_WIDE_INT |
| simple_type_size_in_bits (tree type) |
| { |
| if (TREE_CODE (type) == ERROR_MARK) |
| return BITS_PER_WORD; |
| else if (TYPE_SIZE (type) == NULL_TREE) |
| return 0; |
| else if (host_integerp (TYPE_SIZE (type), 1)) |
| return tree_low_cst (TYPE_SIZE (type), 1); |
| else |
| return TYPE_ALIGN (type); |
| } |
| |
| /* Return true if the debug information for the given type should be |
| emitted as a subrange type. */ |
| |
| static inline bool |
| is_subrange_type (tree type) |
| { |
| tree subtype = TREE_TYPE (type); |
| |
| /* Subrange types are identified by the fact that they are integer |
| types, and that they have a subtype which is either an integer type |
| or an enumeral type. */ |
| |
| if (TREE_CODE (type) != INTEGER_TYPE |
| || subtype == NULL_TREE) |
| return false; |
| |
| if (TREE_CODE (subtype) != INTEGER_TYPE |
| && TREE_CODE (subtype) != ENUMERAL_TYPE) |
| return false; |
| |
| if (TREE_CODE (type) == TREE_CODE (subtype) |
| && int_size_in_bytes (type) == int_size_in_bytes (subtype) |
| && TYPE_MIN_VALUE (type) != NULL |
| && TYPE_MIN_VALUE (subtype) != NULL |
| && tree_int_cst_equal (TYPE_MIN_VALUE (type), TYPE_MIN_VALUE (subtype)) |
| && TYPE_MAX_VALUE (type) != NULL |
| && TYPE_MAX_VALUE (subtype) != NULL |
| && tree_int_cst_equal (TYPE_MAX_VALUE (type), TYPE_MAX_VALUE (subtype))) |
| { |
| /* The type and its subtype have the same representation. If in |
| addition the two types also have the same name, then the given |
| type is not a subrange type, but rather a plain base type. */ |
| /* FIXME: brobecker/2004-03-22: |
| Sizetype INTEGER_CSTs nodes are canonicalized. It should |
| therefore be sufficient to check the TYPE_SIZE node pointers |
| rather than checking the actual size. Unfortunately, we have |
| found some cases, such as in the Ada "integer" type, where |
| this is not the case. Until this problem is solved, we need to |
| keep checking the actual size. */ |
| tree type_name = TYPE_NAME (type); |
| tree subtype_name = TYPE_NAME (subtype); |
| |
| if (type_name != NULL && TREE_CODE (type_name) == TYPE_DECL) |
| type_name = DECL_NAME (type_name); |
| |
| if (subtype_name != NULL && TREE_CODE (subtype_name) == TYPE_DECL) |
| subtype_name = DECL_NAME (subtype_name); |
| |
| if (type_name == subtype_name) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Given a pointer to a tree node for a subrange type, return a pointer |
| to a DIE that describes the given type. */ |
| |
| static dw_die_ref |
| subrange_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref subrange_die; |
| const HOST_WIDE_INT size_in_bytes = int_size_in_bytes (type); |
| |
| if (context_die == NULL) |
| context_die = comp_unit_die; |
| |
| subrange_die = new_die (DW_TAG_subrange_type, context_die, type); |
| |
| if (int_size_in_bytes (TREE_TYPE (type)) != size_in_bytes) |
| { |
| /* The size of the subrange type and its base type do not match, |
| so we need to generate a size attribute for the subrange type. */ |
| add_AT_unsigned (subrange_die, DW_AT_byte_size, size_in_bytes); |
| } |
| |
| if (TYPE_MIN_VALUE (type) != NULL) |
| add_bound_info (subrange_die, DW_AT_lower_bound, |
| TYPE_MIN_VALUE (type)); |
| if (TYPE_MAX_VALUE (type) != NULL) |
| add_bound_info (subrange_die, DW_AT_upper_bound, |
| TYPE_MAX_VALUE (type)); |
| |
| return subrange_die; |
| } |
| |
| /* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging |
| entry that chains various modifiers in front of the given type. */ |
| |
| static dw_die_ref |
| modified_type_die (tree type, int is_const_type, int is_volatile_type, |
| dw_die_ref context_die) |
| { |
| enum tree_code code = TREE_CODE (type); |
| dw_die_ref mod_type_die; |
| dw_die_ref sub_die = NULL; |
| tree item_type = NULL; |
| tree qualified_type; |
| tree name; |
| |
| if (code == ERROR_MARK) |
| return NULL; |
| |
| /* APPLE LOCAL begin Radar 5741731, typedefs used in '@try' blocks */ |
| if (is_volatile_type |
| /* APPLE LOCAL - radar 6113240 */ |
| && (is_objc () || is_objcxx ()) |
| && lookup_attribute ("objc_volatilized", TYPE_ATTRIBUTES (type))) |
| { |
| is_volatile_type = 0; |
| if (TYPE_NAME (type) && TREE_TYPE (TYPE_NAME (type))) |
| type = TREE_TYPE (TYPE_NAME (type)); |
| } |
| /* APPLE LOCAL end Radar 5741731, typedefs used in '@try' blocks */ |
| |
| /* See if we already have the appropriately qualified variant of |
| this type. */ |
| qualified_type |
| = get_qualified_type (type, |
| ((is_const_type ? TYPE_QUAL_CONST : 0) |
| | (is_volatile_type ? TYPE_QUAL_VOLATILE : 0))); |
| |
| /* If we do, then we can just use its DIE, if it exists. */ |
| if (qualified_type) |
| { |
| mod_type_die = lookup_type_die (qualified_type); |
| if (mod_type_die) |
| return mod_type_die; |
| } |
| |
| name = qualified_type ? TYPE_NAME (qualified_type) : NULL; |
| |
| /* Handle C typedef types. */ |
| if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name)) |
| { |
| tree dtype = TREE_TYPE (name); |
| |
| if (qualified_type == dtype) |
| { |
| /* For a named type, use the typedef. */ |
| gen_type_die (qualified_type, context_die); |
| return lookup_type_die (qualified_type); |
| } |
| else if (is_const_type < TYPE_READONLY (dtype) |
| || is_volatile_type < TYPE_VOLATILE (dtype) |
| || (is_const_type <= TYPE_READONLY (dtype) |
| && is_volatile_type <= TYPE_VOLATILE (dtype) |
| && DECL_ORIGINAL_TYPE (name) != type)) |
| /* cv-unqualified version of named type. Just use the unnamed |
| type to which it refers. */ |
| return modified_type_die (DECL_ORIGINAL_TYPE (name), |
| is_const_type, is_volatile_type, |
| context_die); |
| /* Else cv-qualified version of named type; fall through. */ |
| } |
| |
| if (is_const_type) |
| { |
| mod_type_die = new_die (DW_TAG_const_type, comp_unit_die, type); |
| sub_die = modified_type_die (type, 0, is_volatile_type, context_die); |
| } |
| else if (is_volatile_type) |
| { |
| mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die, type); |
| sub_die = modified_type_die (type, 0, 0, context_die); |
| } |
| /* APPLE LOCAL radar 5732232 - blocks */ |
| else if (code == POINTER_TYPE || code == BLOCK_POINTER_TYPE) |
| { |
| mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die, type); |
| add_AT_unsigned (mod_type_die, DW_AT_byte_size, |
| simple_type_size_in_bits (type) / BITS_PER_UNIT); |
| item_type = TREE_TYPE (type); |
| } |
| else if (code == REFERENCE_TYPE) |
| { |
| mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die, type); |
| add_AT_unsigned (mod_type_die, DW_AT_byte_size, |
| simple_type_size_in_bits (type) / BITS_PER_UNIT); |
| item_type = TREE_TYPE (type); |
| } |
| else if (is_subrange_type (type)) |
| { |
| mod_type_die = subrange_type_die (type, context_die); |
| item_type = TREE_TYPE (type); |
| } |
| else if (is_base_type (type)) |
| mod_type_die = base_type_die (type); |
| else |
| { |
| gen_type_die (type, context_die); |
| |
| /* We have to get the type_main_variant here (and pass that to the |
| `lookup_type_die' routine) because the ..._TYPE node we have |
| might simply be a *copy* of some original type node (where the |
| copy was created to help us keep track of typedef names) and |
| that copy might have a different TYPE_UID from the original |
| ..._TYPE node. */ |
| if (TREE_CODE (type) != VECTOR_TYPE) |
| return lookup_type_die (type_main_variant (type)); |
| else |
| /* Vectors have the debugging information in the type, |
| not the main variant. */ |
| return lookup_type_die (type); |
| } |
| |
| /* Builtin types don't have a DECL_ORIGINAL_TYPE. For those, |
| don't output a DW_TAG_typedef, since there isn't one in the |
| user's program; just attach a DW_AT_name to the type. */ |
| if (name |
| && (TREE_CODE (name) != TYPE_DECL || TREE_TYPE (name) == qualified_type)) |
| { |
| if (TREE_CODE (name) == TYPE_DECL) |
| /* Could just call add_name_and_src_coords_attributes here, |
| but since this is a builtin type it doesn't have any |
| useful source coordinates anyway. */ |
| name = DECL_NAME (name); |
| add_name_attribute (mod_type_die, IDENTIFIER_POINTER (name)); |
| } |
| |
| if (qualified_type) |
| equate_type_number_to_die (qualified_type, mod_type_die); |
| |
| if (item_type) |
| /* We must do this after the equate_type_number_to_die call, in case |
| this is a recursive type. This ensures that the modified_type_die |
| recursion will terminate even if the type is recursive. Recursive |
| types are possible in Ada. */ |
| sub_die = modified_type_die (item_type, |
| TYPE_READONLY (item_type), |
| TYPE_VOLATILE (item_type), |
| context_die); |
| |
| if (sub_die != NULL) |
| add_AT_die_ref (mod_type_die, DW_AT_type, sub_die); |
| |
| /* APPLE LOCAL begin radar 5359827 add named pointer types to pubtype table */ |
| if (mod_type_die |
| && mod_type_die->die_tag == DW_TAG_pointer_type |
| && get_AT (mod_type_die, DW_AT_name)) |
| add_pubtype (type, mod_type_die); |
| /* APPLE LOCAL end radar 5359827 add named pointer types to pubtype table */ |
| |
| return mod_type_die; |
| } |
| |
| /* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is |
| an enumerated type. */ |
| |
| static inline int |
| type_is_enum (tree type) |
| { |
| return TREE_CODE (type) == ENUMERAL_TYPE; |
| } |
| |
| /* Return the DBX register number described by a given RTL node. */ |
| |
| static unsigned int |
| dbx_reg_number (rtx rtl) |
| { |
| unsigned regno = REGNO (rtl); |
| |
| gcc_assert (regno < FIRST_PSEUDO_REGISTER); |
| |
| #ifdef LEAF_REG_REMAP |
| if (current_function_uses_only_leaf_regs) |
| { |
| int leaf_reg = LEAF_REG_REMAP (regno); |
| if (leaf_reg != -1) |
| regno = (unsigned) leaf_reg; |
| } |
| #endif |
| |
| return DBX_REGISTER_NUMBER (regno); |
| } |
| |
| /* Optionally add a DW_OP_piece term to a location description expression. |
| DW_OP_piece is only added if the location description expression already |
| doesn't end with DW_OP_piece. */ |
| |
| static void |
| add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size) |
| { |
| dw_loc_descr_ref loc; |
| |
| if (*list_head != NULL) |
| { |
| /* Find the end of the chain. */ |
| for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next) |
| ; |
| |
| if (loc->dw_loc_opc != DW_OP_piece) |
| loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0); |
| } |
| } |
| |
| /* Return a location descriptor that designates a machine register or |
| zero if there is none. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| reg_loc_descriptor (rtx rtl, enum var_init_status initialized) |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| { |
| rtx regs; |
| |
| if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER) |
| return 0; |
| |
| regs = targetm.dwarf_register_span (rtl); |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)] > 1 || regs) |
| return multiple_reg_loc_descriptor (rtl, regs, initialized); |
| else |
| return one_reg_loc_descriptor (dbx_reg_number (rtl), initialized); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| } |
| |
| /* Return a location descriptor that designates a machine register for |
| a given hard register number. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized) |
| { |
| dw_loc_descr_ref reg_loc_descr; |
| |
| if (regno <= 31) |
| reg_loc_descr = new_loc_descr (DW_OP_reg0 + regno, 0, 0); |
| else |
| reg_loc_descr = new_loc_descr (DW_OP_regx, regno, 0); |
| |
| if (initialized == STATUS_UNINITIALIZED) |
| add_loc_descr (®_loc_descr, new_loc_descr (DW_OP_APPLE_uninit, 0, 0)); |
| |
| return reg_loc_descr; |
| } |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| /* Given an RTL of a register, return a location descriptor that |
| designates a value that spans more than one register. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| multiple_reg_loc_descriptor (rtx rtl, rtx regs, |
| enum var_init_status initialized) |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| { |
| int nregs, size, i; |
| unsigned reg; |
| dw_loc_descr_ref loc_result = NULL; |
| |
| reg = REGNO (rtl); |
| #ifdef LEAF_REG_REMAP |
| if (current_function_uses_only_leaf_regs) |
| { |
| int leaf_reg = LEAF_REG_REMAP (reg); |
| if (leaf_reg != -1) |
| reg = (unsigned) leaf_reg; |
| } |
| #endif |
| gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl)); |
| nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)]; |
| |
| /* Simple, contiguous registers. */ |
| if (regs == NULL_RTX) |
| { |
| size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs; |
| |
| loc_result = NULL; |
| while (nregs--) |
| { |
| dw_loc_descr_ref t; |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| t = one_reg_loc_descriptor (DBX_REGISTER_NUMBER (reg), |
| STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| add_loc_descr (&loc_result, t); |
| add_loc_descr_op_piece (&loc_result, size); |
| ++reg; |
| } |
| return loc_result; |
| } |
| |
| /* Now onto stupid register sets in non contiguous locations. */ |
| |
| gcc_assert (GET_CODE (regs) == PARALLEL); |
| |
| size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); |
| loc_result = NULL; |
| |
| for (i = 0; i < XVECLEN (regs, 0); ++i) |
| { |
| dw_loc_descr_ref t; |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| t = one_reg_loc_descriptor (REGNO (XVECEXP (regs, 0, i)), |
| STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| add_loc_descr (&loc_result, t); |
| size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); |
| add_loc_descr_op_piece (&loc_result, size); |
| } |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (loc_result && initialized == STATUS_UNINITIALIZED) |
| add_loc_descr (&loc_result, new_loc_descr (DW_OP_APPLE_uninit, 0, 0)); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| return loc_result; |
| } |
| |
| /* Return a location descriptor that designates a constant. */ |
| |
| static dw_loc_descr_ref |
| int_loc_descriptor (HOST_WIDE_INT i) |
| { |
| enum dwarf_location_atom op; |
| |
| /* Pick the smallest representation of a constant, rather than just |
| defaulting to the LEB encoding. */ |
| if (i >= 0) |
| { |
| if (i <= 31) |
| op = DW_OP_lit0 + i; |
| else if (i <= 0xff) |
| op = DW_OP_const1u; |
| else if (i <= 0xffff) |
| op = DW_OP_const2u; |
| else if (HOST_BITS_PER_WIDE_INT == 32 |
| || i <= 0xffffffff) |
| op = DW_OP_const4u; |
| else |
| op = DW_OP_constu; |
| } |
| else |
| { |
| if (i >= -0x80) |
| op = DW_OP_const1s; |
| else if (i >= -0x8000) |
| op = DW_OP_const2s; |
| else if (HOST_BITS_PER_WIDE_INT == 32 |
| || i >= -0x80000000) |
| op = DW_OP_const4s; |
| else |
| op = DW_OP_consts; |
| } |
| |
| return new_loc_descr (op, i, 0); |
| } |
| |
| /* Return a location descriptor that designates a base+offset location. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| based_loc_descr (rtx reg, HOST_WIDE_INT offset, |
| enum var_init_status initialized) |
| { |
| unsigned int regno; |
| dw_loc_descr_ref result; |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| /* We only use "frame base" when we're sure we're talking about the |
| post-prologue local stack frame. We do this by *not* running |
| register elimination until this point, and recognizing the special |
| argument pointer and soft frame pointer rtx's. */ |
| if (reg == arg_pointer_rtx || reg == frame_pointer_rtx) |
| { |
| rtx elim = eliminate_regs (reg, VOIDmode, NULL_RTX); |
| |
| if (elim != reg) |
| { |
| if (GET_CODE (elim) == PLUS) |
| { |
| offset += INTVAL (XEXP (elim, 1)); |
| elim = XEXP (elim, 0); |
| } |
| /* APPLE LOCAL begin ARM prefer SP to FP */ |
| /* Make sure we are using the same base register. */ |
| gcc_assert (elim == frame_pointer_fb_offset_from); |
| /* APPLE LOCAL end ARM prefer SP to FP */ |
| offset += frame_pointer_fb_offset; |
| |
| return new_loc_descr (DW_OP_fbreg, offset, 0); |
| } |
| } |
| |
| regno = dbx_reg_number (reg); |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (regno <= 31) |
| result = new_loc_descr (DW_OP_breg0 + regno, offset, 0); |
| else |
| result = new_loc_descr (DW_OP_bregx, regno, offset); |
| |
| if (initialized == STATUS_UNINITIALIZED) |
| add_loc_descr (&result, new_loc_descr (DW_OP_APPLE_uninit, 0, 0)); |
| |
| return result; |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| } |
| |
| /* Return true if this RTL expression describes a base+offset calculation. */ |
| |
| static inline int |
| is_based_loc (rtx rtl) |
| { |
| return (GET_CODE (rtl) == PLUS |
| && ((REG_P (XEXP (rtl, 0)) |
| && REGNO (XEXP (rtl, 0)) < FIRST_PSEUDO_REGISTER |
| && GET_CODE (XEXP (rtl, 1)) == CONST_INT))); |
| } |
| |
| /* The following routine converts the RTL for a variable or parameter |
| (resident in memory) into an equivalent Dwarf representation of a |
| mechanism for getting the address of that same variable onto the top of a |
| hypothetical "address evaluation" stack. |
| |
| When creating memory location descriptors, we are effectively transforming |
| the RTL for a memory-resident object into its Dwarf postfix expression |
| equivalent. This routine recursively descends an RTL tree, turning |
| it into Dwarf postfix code as it goes. |
| |
| MODE is the mode of the memory reference, needed to handle some |
| autoincrement addressing modes. |
| |
| CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the |
| location list for RTL. |
| |
| Return 0 if we can't represent the location. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| mem_loc_descriptor (rtx rtl, enum machine_mode mode, |
| enum var_init_status initialized) |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| { |
| dw_loc_descr_ref mem_loc_result = NULL; |
| enum dwarf_location_atom op; |
| |
| /* Note that for a dynamically sized array, the location we will generate a |
| description of here will be the lowest numbered location which is |
| actually within the array. That's *not* necessarily the same as the |
| zeroth element of the array. */ |
| |
| rtl = targetm.delegitimize_address (rtl); |
| |
| switch (GET_CODE (rtl)) |
| { |
| case POST_INC: |
| case POST_DEC: |
| case POST_MODIFY: |
| /* POST_INC and POST_DEC can be handled just like a SUBREG. So we |
| just fall into the SUBREG code. */ |
| |
| /* ... fall through ... */ |
| |
| case SUBREG: |
| /* The case of a subreg may arise when we have a local (register) |
| variable or a formal (register) parameter which doesn't quite fill |
| up an entire register. For now, just assume that it is |
| legitimate to make the Dwarf info refer to the whole register which |
| contains the given subreg. */ |
| rtl = XEXP (rtl, 0); |
| |
| /* ... fall through ... */ |
| |
| case REG: |
| /* Whenever a register number forms a part of the description of the |
| method for calculating the (dynamic) address of a memory resident |
| object, DWARF rules require the register number be referred to as |
| a "base register". This distinction is not based in any way upon |
| what category of register the hardware believes the given register |
| belongs to. This is strictly DWARF terminology we're dealing with |
| here. Note that in cases where the location of a memory-resident |
| data object could be expressed as: OP_ADD (OP_BASEREG (basereg), |
| OP_CONST (0)) the actual DWARF location descriptor that we generate |
| may just be OP_BASEREG (basereg). This may look deceptively like |
| the object in question was allocated to a register (rather than in |
| memory) so DWARF consumers need to be aware of the subtle |
| distinction between OP_REG and OP_BASEREG. */ |
| if (REGNO (rtl) < FIRST_PSEUDO_REGISTER) |
| /* APPLE LOCAL track initialization status 4964532 */ |
| mem_loc_result = based_loc_descr (rtl, 0, STATUS_INITIALIZED); |
| break; |
| |
| case MEM: |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl), |
| STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| if (mem_loc_result != 0) |
| add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0)); |
| break; |
| |
| case LO_SUM: |
| rtl = XEXP (rtl, 1); |
| |
| /* ... fall through ... */ |
| |
| case LABEL_REF: |
| /* Some ports can transform a symbol ref into a label ref, because |
| the symbol ref is too far away and has to be dumped into a constant |
| pool. */ |
| case CONST: |
| case SYMBOL_REF: |
| /* Alternatively, the symbol in the constant pool might be referenced |
| by a different symbol. */ |
| if (GET_CODE (rtl) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (rtl)) |
| { |
| bool marked; |
| rtx tmp = get_pool_constant_mark (rtl, &marked); |
| |
| if (GET_CODE (tmp) == SYMBOL_REF) |
| { |
| rtl = tmp; |
| if (CONSTANT_POOL_ADDRESS_P (tmp)) |
| get_pool_constant_mark (tmp, &marked); |
| else |
| marked = true; |
| } |
| |
| /* If all references to this pool constant were optimized away, |
| it was not output and thus we can't represent it. |
| FIXME: might try to use DW_OP_const_value here, though |
| DW_OP_piece complicates it. */ |
| if (!marked) |
| return 0; |
| } |
| |
| mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0); |
| mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr; |
| mem_loc_result->dw_loc_oprnd1.v.val_addr = rtl; |
| VEC_safe_push (rtx, gc, used_rtx_array, rtl); |
| break; |
| |
| case PRE_MODIFY: |
| /* Extract the PLUS expression nested inside and fall into |
| PLUS code below. */ |
| rtl = XEXP (rtl, 1); |
| goto plus; |
| |
| case PRE_INC: |
| case PRE_DEC: |
| /* Turn these into a PLUS expression and fall into the PLUS code |
| below. */ |
| rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0), |
| GEN_INT (GET_CODE (rtl) == PRE_INC |
| ? GET_MODE_UNIT_SIZE (mode) |
| : -GET_MODE_UNIT_SIZE (mode))); |
| |
| /* ... fall through ... */ |
| |
| case PLUS: |
| plus: |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (is_based_loc (rtl)) |
| mem_loc_result = based_loc_descr (XEXP (rtl, 0), |
| INTVAL (XEXP (rtl, 1)), |
| STATUS_INITIALIZED); |
| else |
| { |
| mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode, |
| STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| if (mem_loc_result == 0) |
| break; |
| |
| if (GET_CODE (XEXP (rtl, 1)) == CONST_INT |
| && INTVAL (XEXP (rtl, 1)) >= 0) |
| add_loc_descr (&mem_loc_result, |
| new_loc_descr (DW_OP_plus_uconst, |
| INTVAL (XEXP (rtl, 1)), 0)); |
| else |
| { |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| add_loc_descr (&mem_loc_result, |
| mem_loc_descriptor (XEXP (rtl, 1), mode, |
| STATUS_INITIALIZED)); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| add_loc_descr (&mem_loc_result, |
| new_loc_descr (DW_OP_plus, 0, 0)); |
| } |
| } |
| break; |
| |
| /* If a pseudo-reg is optimized away, it is possible for it to |
| be replaced with a MEM containing a multiply or shift. */ |
| case MULT: |
| op = DW_OP_mul; |
| goto do_binop; |
| |
| case ASHIFT: |
| op = DW_OP_shl; |
| goto do_binop; |
| |
| case ASHIFTRT: |
| op = DW_OP_shra; |
| goto do_binop; |
| |
| case LSHIFTRT: |
| op = DW_OP_shr; |
| goto do_binop; |
| |
| do_binop: |
| { |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| dw_loc_descr_ref op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, |
| STATUS_INITIALIZED); |
| dw_loc_descr_ref op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, |
| STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| if (op0 == 0 || op1 == 0) |
| break; |
| |
| mem_loc_result = op0; |
| add_loc_descr (&mem_loc_result, op1); |
| add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0)); |
| break; |
| } |
| |
| case CONST_INT: |
| mem_loc_result = int_loc_descriptor (INTVAL (rtl)); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (mem_loc_result && initialized == STATUS_UNINITIALIZED) |
| add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_APPLE_uninit, 0, |
| 0)); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| return mem_loc_result; |
| } |
| |
| /* Return a descriptor that describes the concatenation of two locations. |
| This is typically a complex variable. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| concat_loc_descriptor (rtx x0, rtx x1, enum var_init_status initialized) |
| { |
| dw_loc_descr_ref cc_loc_result = NULL; |
| dw_loc_descr_ref x0_ref = loc_descriptor (x0, STATUS_INITIALIZED); |
| dw_loc_descr_ref x1_ref = loc_descriptor (x1, STATUS_INITIALIZED); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| if (x0_ref == 0 || x1_ref == 0) |
| return 0; |
| |
| cc_loc_result = x0_ref; |
| add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x0))); |
| |
| add_loc_descr (&cc_loc_result, x1_ref); |
| add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x1))); |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (initialized == STATUS_UNINITIALIZED) |
| add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_APPLE_uninit, 0, 0)); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| return cc_loc_result; |
| } |
| |
| /* Output a proper Dwarf location descriptor for a variable or parameter |
| which is either allocated in a register or in a memory location. For a |
| register, we just generate an OP_REG and the register number. For a |
| memory location we provide a Dwarf postfix expression describing how to |
| generate the (dynamic) address of the object onto the address stack. |
| |
| If we don't know how to describe it, return 0. */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| static dw_loc_descr_ref |
| loc_descriptor (rtx rtl, enum var_init_status initialized) |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| { |
| dw_loc_descr_ref loc_result = NULL; |
| |
| switch (GET_CODE (rtl)) |
| { |
| case SUBREG: |
| /* The case of a subreg may arise when we have a local (register) |
| variable or a formal (register) parameter which doesn't quite fill |
| up an entire register. For now, just assume that it is |
| legitimate to make the Dwarf info refer to the whole register which |
| contains the given subreg. */ |
| rtl = SUBREG_REG (rtl); |
| |
| /* ... fall through ... */ |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| case REG: |
| loc_result = reg_loc_descriptor (rtl, initialized); |
| break; |
| |
| case MEM: |
| loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl), |
| initialized); |
| break; |
| |
| case CONCAT: |
| loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1), |
| initialized); |
| break; |
| |
| case VAR_LOCATION: |
| /* Single part. */ |
| if (GET_CODE (XEXP (rtl, 1)) != PARALLEL) |
| { |
| loc_result = loc_descriptor (XEXP (XEXP (rtl, 1), 0), initialized); |
| break; |
| } |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| rtl = XEXP (rtl, 1); |
| /* FALLTHRU */ |
| |
| case PARALLEL: |
| { |
| rtvec par_elems = XVEC (rtl, 0); |
| int num_elem = GET_NUM_ELEM (par_elems); |
| enum machine_mode mode; |
| int i; |
| |
| /* Create the first one, so we have something to add to. */ |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| loc_result = loc_descriptor (XEXP (RTVEC_ELT (par_elems, 0), 0), |
| initialized); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, 0), 0)); |
| add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode)); |
| for (i = 1; i < num_elem; i++) |
| { |
| dw_loc_descr_ref temp; |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| temp = loc_descriptor (XEXP (RTVEC_ELT (par_elems, i), 0), |
| initialized); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| add_loc_descr (&loc_result, temp); |
| mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, i), 0)); |
| add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode)); |
| } |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return loc_result; |
| } |
| |
| /* Similar, but generate the descriptor from trees instead of rtl. This comes |
| up particularly with variable length arrays. WANT_ADDRESS is 2 if this is |
| a top-level invocation of loc_descriptor_from_tree; is 1 if this is not a |
| top-level invocation, and we require the address of LOC; is 0 if we require |
| the value of LOC. */ |
| |
| static dw_loc_descr_ref |
| loc_descriptor_from_tree_1 (tree loc, int want_address) |
| { |
| dw_loc_descr_ref ret, ret1; |
| int have_address = 0; |
| enum dwarf_location_atom op; |
| |
| /* ??? Most of the time we do not take proper care for sign/zero |
| extending the values properly. Hopefully this won't be a real |
| problem... */ |
| |
| switch (TREE_CODE (loc)) |
| { |
| case ERROR_MARK: |
| return 0; |
| |
| case PLACEHOLDER_EXPR: |
| /* This case involves extracting fields from an object to determine the |
| position of other fields. We don't try to encode this here. The |
| only user of this is Ada, which encodes the needed information using |
| the names of types. */ |
| return 0; |
| |
| case CALL_EXPR: |
| return 0; |
| |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| /* There are no opcodes for these operations. */ |
| return 0; |
| |
| case ADDR_EXPR: |
| /* If we already want an address, there's nothing we can do. */ |
| if (want_address) |
| return 0; |
| |
| /* Otherwise, process the argument and look for the address. */ |
| return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 1); |
| |
| case VAR_DECL: |
| if (DECL_THREAD_LOCAL_P (loc)) |
| { |
| rtx rtl; |
| |
| /* If this is not defined, we have no way to emit the data. */ |
| if (!targetm.asm_out.output_dwarf_dtprel) |
| return 0; |
| |
| /* The way DW_OP_GNU_push_tls_address is specified, we can only |
| look up addresses of objects in the current module. */ |
| if (DECL_EXTERNAL (loc)) |
| return 0; |
| |
| rtl = rtl_for_decl_location (loc); |
| if (rtl == NULL_RTX) |
| return 0; |
| |
| if (!MEM_P (rtl)) |
| return 0; |
| rtl = XEXP (rtl, 0); |
| if (! CONSTANT_P (rtl)) |
| return 0; |
| |
| ret = new_loc_descr (INTERNAL_DW_OP_tls_addr, 0, 0); |
| ret->dw_loc_oprnd1.val_class = dw_val_class_addr; |
| ret->dw_loc_oprnd1.v.val_addr = rtl; |
| |
| ret1 = new_loc_descr (DW_OP_GNU_push_tls_address, 0, 0); |
| add_loc_descr (&ret, ret1); |
| |
| have_address = 1; |
| break; |
| } |
| /* FALLTHRU */ |
| |
| case PARM_DECL: |
| if (DECL_HAS_VALUE_EXPR_P (loc)) |
| return loc_descriptor_from_tree_1 (DECL_VALUE_EXPR (loc), |
| want_address); |
| /* FALLTHRU */ |
| |
| case RESULT_DECL: |
| case FUNCTION_DECL: |
| { |
| rtx rtl = rtl_for_decl_location (loc); |
| |
| if (rtl == NULL_RTX) |
| return 0; |
| else if (GET_CODE (rtl) == CONST_INT) |
| { |
| HOST_WIDE_INT val = INTVAL (rtl); |
| if (TYPE_UNSIGNED (TREE_TYPE (loc))) |
| val &= GET_MODE_MASK (DECL_MODE (loc)); |
| ret = int_loc_descriptor (val); |
| } |
| else if (GET_CODE (rtl) == CONST_STRING) |
| return 0; |
| else if (CONSTANT_P (rtl)) |
| { |
| ret = new_loc_descr (DW_OP_addr, 0, 0); |
| ret->dw_loc_oprnd1.val_class = dw_val_class_addr; |
| ret->dw_loc_oprnd1.v.val_addr = rtl; |
| } |
| else |
| { |
| enum machine_mode mode; |
| |
| /* Certain constructs can only be represented at top-level. */ |
| if (want_address == 2) |
| /* APPLE LOCAL track initialization status 4964532 */ |
| return loc_descriptor (rtl, STATUS_INITIALIZED); |
| |
| mode = GET_MODE (rtl); |
| if (MEM_P (rtl)) |
| { |
| rtl = XEXP (rtl, 0); |
| have_address = 1; |
| } |
| /* APPLE LOCAL track initialization status 4964532 */ |
| ret = mem_loc_descriptor (rtl, mode, STATUS_INITIALIZED); |
| } |
| } |
| break; |
| |
| case INDIRECT_REF: |
| ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0); |
| have_address = 1; |
| break; |
| |
| case COMPOUND_EXPR: |
| return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), want_address); |
| |
| case NOP_EXPR: |
| case CONVERT_EXPR: |
| case NON_LVALUE_EXPR: |
| case VIEW_CONVERT_EXPR: |
| case SAVE_EXPR: |
| case MODIFY_EXPR: |
| return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), want_address); |
| |
| case COMPONENT_REF: |
| case BIT_FIELD_REF: |
| case ARRAY_REF: |
| case ARRAY_RANGE_REF: |
| { |
| tree obj, offset; |
| HOST_WIDE_INT bitsize, bitpos, bytepos; |
| enum machine_mode mode; |
| int volatilep; |
| int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc)); |
| |
| obj = get_inner_reference (loc, &bitsize, &bitpos, &offset, &mode, |
| &unsignedp, &volatilep, false); |
| |
| if (obj == loc) |
| return 0; |
| |
| ret = loc_descriptor_from_tree_1 (obj, 1); |
| if (ret == 0 |
| || bitpos % BITS_PER_UNIT != 0 || bitsize % BITS_PER_UNIT != 0) |
| return 0; |
| |
| if (offset != NULL_TREE) |
| { |
| /* Variable offset. */ |
| add_loc_descr (&ret, loc_descriptor_from_tree_1 (offset, 0)); |
| add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0)); |
| } |
| |
| bytepos = bitpos / BITS_PER_UNIT; |
| if (bytepos > 0) |
| add_loc_descr (&ret, new_loc_descr (DW_OP_plus_uconst, bytepos, 0)); |
| else if (bytepos < 0) |
| { |
| add_loc_descr (&ret, int_loc_descriptor (bytepos)); |
| add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0)); |
| } |
| |
| have_address = 1; |
| break; |
| } |
| |
| case INTEGER_CST: |
| if (host_integerp (loc, 0)) |
| ret = int_loc_descriptor (tree_low_cst (loc, 0)); |
| else |
| return 0; |
| break; |
| |
| case CONSTRUCTOR: |
| { |
| /* Get an RTL for this, if something has been emitted. */ |
| rtx rtl = lookup_constant_def (loc); |
| enum machine_mode mode; |
| |
| if (!rtl || !MEM_P (rtl)) |
| return 0; |
| mode = GET_MODE (rtl); |
| rtl = XEXP (rtl, 0); |
| /* APPLE LOCAL track initialization status 4964532 */ |
| ret = mem_loc_descriptor (rtl, mode, STATUS_INITIALIZED); |
| have_address = 1; |
| break; |
| } |
| |
| case TRUTH_AND_EXPR: |
| case TRUTH_ANDIF_EXPR: |
| case BIT_AND_EXPR: |
| op = DW_OP_and; |
| goto do_binop; |
| |
| case TRUTH_XOR_EXPR: |
| case BIT_XOR_EXPR: |
| op = DW_OP_xor; |
| goto do_binop; |
| |
| case TRUTH_OR_EXPR: |
| case TRUTH_ORIF_EXPR: |
| case BIT_IOR_EXPR: |
| op = DW_OP_or; |
| goto do_binop; |
| |
| case FLOOR_DIV_EXPR: |
| case CEIL_DIV_EXPR: |
| case ROUND_DIV_EXPR: |
| case TRUNC_DIV_EXPR: |
| op = DW_OP_div; |
| goto do_binop; |
| |
| case MINUS_EXPR: |
| op = DW_OP_minus; |
| goto do_binop; |
| |
| case FLOOR_MOD_EXPR: |
| case CEIL_MOD_EXPR: |
| case ROUND_MOD_EXPR: |
| case TRUNC_MOD_EXPR: |
| op = DW_OP_mod; |
| goto do_binop; |
| |
| case MULT_EXPR: |
| op = DW_OP_mul; |
| goto do_binop; |
| |
| case LSHIFT_EXPR: |
| op = DW_OP_shl; |
| goto do_binop; |
| |
| case RSHIFT_EXPR: |
| op = (TYPE_UNSIGNED (TREE_TYPE (loc)) ? DW_OP_shr : DW_OP_shra); |
| goto do_binop; |
| |
| case PLUS_EXPR: |
| if (TREE_CODE (TREE_OPERAND (loc, 1)) == INTEGER_CST |
| && host_integerp (TREE_OPERAND (loc, 1), 0)) |
| { |
| ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0); |
| if (ret == 0) |
| return 0; |
| |
| add_loc_descr (&ret, |
| new_loc_descr (DW_OP_plus_uconst, |
| tree_low_cst (TREE_OPERAND (loc, 1), |
| 0), |
| 0)); |
| break; |
| } |
| |
| op = DW_OP_plus; |
| goto do_binop; |
| |
| case LE_EXPR: |
| if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0)))) |
| return 0; |
| |
| op = DW_OP_le; |
| goto do_binop; |
| |
| case GE_EXPR: |
| if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0)))) |
| return 0; |
| |
| op = DW_OP_ge; |
| goto do_binop; |
| |
| case LT_EXPR: |
| if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0)))) |
| return 0; |
| |
| op = DW_OP_lt; |
| goto do_binop; |
| |
| case GT_EXPR: |
| if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (loc, 0)))) |
| return 0; |
| |
| op = DW_OP_gt; |
| goto do_binop; |
| |
| case EQ_EXPR: |
| op = DW_OP_eq; |
| goto do_binop; |
| |
| case NE_EXPR: |
| op = DW_OP_ne; |
| goto do_binop; |
| |
| do_binop: |
| ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0); |
| ret1 = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0); |
| if (ret == 0 || ret1 == 0) |
| return 0; |
| |
| add_loc_descr (&ret, ret1); |
| add_loc_descr (&ret, new_loc_descr (op, 0, 0)); |
| break; |
| |
| case TRUTH_NOT_EXPR: |
| case BIT_NOT_EXPR: |
| op = DW_OP_not; |
| goto do_unop; |
| |
| case ABS_EXPR: |
| op = DW_OP_abs; |
| goto do_unop; |
| |
| case NEGATE_EXPR: |
| op = DW_OP_neg; |
| goto do_unop; |
| |
| do_unop: |
| ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0); |
| if (ret == 0) |
| return 0; |
| |
| add_loc_descr (&ret, new_loc_descr (op, 0, 0)); |
| break; |
| |
| case MIN_EXPR: |
| case MAX_EXPR: |
| { |
| const enum tree_code code = |
| TREE_CODE (loc) == MIN_EXPR ? GT_EXPR : LT_EXPR; |
| |
| loc = build3 (COND_EXPR, TREE_TYPE (loc), |
| build2 (code, integer_type_node, |
| TREE_OPERAND (loc, 0), TREE_OPERAND (loc, 1)), |
| TREE_OPERAND (loc, 1), TREE_OPERAND (loc, 0)); |
| } |
| |
| /* ... fall through ... */ |
| |
| case COND_EXPR: |
| { |
| dw_loc_descr_ref lhs |
| = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0); |
| dw_loc_descr_ref rhs |
| = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 2), 0); |
| dw_loc_descr_ref bra_node, jump_node, tmp; |
| |
| ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0); |
| if (ret == 0 || lhs == 0 || rhs == 0) |
| return 0; |
| |
| bra_node = new_loc_descr (DW_OP_bra, 0, 0); |
| add_loc_descr (&ret, bra_node); |
| |
| add_loc_descr (&ret, rhs); |
| jump_node = new_loc_descr (DW_OP_skip, 0, 0); |
| add_loc_descr (&ret, jump_node); |
| |
| add_loc_descr (&ret, lhs); |
| bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc; |
| bra_node->dw_loc_oprnd1.v.val_loc = lhs; |
| |
| /* ??? Need a node to point the skip at. Use a nop. */ |
| tmp = new_loc_descr (DW_OP_nop, 0, 0); |
| add_loc_descr (&ret, tmp); |
| jump_node->dw_loc_oprnd1.val_class = dw_val_class_loc; |
| jump_node->dw_loc_oprnd1.v.val_loc = tmp; |
| } |
| break; |
| |
| case FIX_TRUNC_EXPR: |
| case FIX_CEIL_EXPR: |
| case FIX_FLOOR_EXPR: |
| case FIX_ROUND_EXPR: |
| return 0; |
| |
| default: |
| /* Leave front-end specific codes as simply unknown. This comes |
| up, for instance, with the C STMT_EXPR. */ |
| if ((unsigned int) TREE_CODE (loc) |
| >= (unsigned int) LAST_AND_UNUSED_TREE_CODE) |
| return 0; |
| |
| #ifdef ENABLE_CHECKING |
| /* Otherwise this is a generic code; we should just lists all of |
| these explicitly. We forgot one. */ |
| gcc_unreachable (); |
| #else |
| /* In a release build, we want to degrade gracefully: better to |
| generate incomplete debugging information than to crash. */ |
| return NULL; |
| #endif |
| } |
| |
| /* Show if we can't fill the request for an address. */ |
| if (want_address && !have_address) |
| return 0; |
| |
| /* If we've got an address and don't want one, dereference. */ |
| if (!want_address && have_address && ret) |
| { |
| HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (loc)); |
| |
| if (size > DWARF2_ADDR_SIZE || size == -1) |
| return 0; |
| else if (size == DWARF2_ADDR_SIZE) |
| op = DW_OP_deref; |
| else |
| op = DW_OP_deref_size; |
| |
| add_loc_descr (&ret, new_loc_descr (op, size, 0)); |
| } |
| |
| return ret; |
| } |
| |
| static inline dw_loc_descr_ref |
| loc_descriptor_from_tree (tree loc) |
| { |
| return loc_descriptor_from_tree_1 (loc, 2); |
| } |
| |
| /* Given a value, round it up to the lowest multiple of `boundary' |
| which is not less than the value itself. */ |
| |
| static inline HOST_WIDE_INT |
| ceiling (HOST_WIDE_INT value, unsigned int boundary) |
| { |
| return (((value + boundary - 1) / boundary) * boundary); |
| } |
| |
| /* Given a pointer to what is assumed to be a FIELD_DECL node, return a |
| pointer to the declared type for the relevant field variable, or return |
| `integer_type_node' if the given node turns out to be an |
| ERROR_MARK node. */ |
| |
| static inline tree |
| field_type (tree decl) |
| { |
| tree type; |
| |
| if (TREE_CODE (decl) == ERROR_MARK) |
| return integer_type_node; |
| |
| type = DECL_BIT_FIELD_TYPE (decl); |
| if (type == NULL_TREE) |
| type = TREE_TYPE (decl); |
| |
| return type; |
| } |
| |
| /* Given a pointer to a tree node, return the alignment in bits for |
| it, or else return BITS_PER_WORD if the node actually turns out to |
| be an ERROR_MARK node. */ |
| |
| static inline unsigned |
| simple_type_align_in_bits (tree type) |
| { |
| return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD; |
| } |
| |
| static inline unsigned |
| simple_decl_align_in_bits (tree decl) |
| { |
| return (TREE_CODE (decl) != ERROR_MARK) ? DECL_ALIGN (decl) : BITS_PER_WORD; |
| } |
| |
| /* Given a pointer to a FIELD_DECL, compute and return the byte offset of the |
| lowest addressed byte of the "containing object" for the given FIELD_DECL, |
| or return 0 if we are unable to determine what that offset is, either |
| because the argument turns out to be a pointer to an ERROR_MARK node, or |
| because the offset is actually variable. (We can't handle the latter case |
| just yet). */ |
| |
| static HOST_WIDE_INT |
| field_byte_offset (tree decl) |
| { |
| unsigned int type_align_in_bits; |
| unsigned int decl_align_in_bits; |
| unsigned HOST_WIDE_INT type_size_in_bits; |
| HOST_WIDE_INT object_offset_in_bits; |
| tree type; |
| tree field_size_tree; |
| HOST_WIDE_INT bitpos_int; |
| HOST_WIDE_INT deepest_bitpos; |
| unsigned HOST_WIDE_INT field_size_in_bits; |
| |
| if (TREE_CODE (decl) == ERROR_MARK) |
| return 0; |
| |
| gcc_assert (TREE_CODE (decl) == FIELD_DECL); |
| |
| type = field_type (decl); |
| field_size_tree = DECL_SIZE (decl); |
| |
| /* The size could be unspecified if there was an error, or for |
| a flexible array member. */ |
| if (! field_size_tree) |
| field_size_tree = bitsize_zero_node; |
| |
| /* We cannot yet cope with fields whose positions are variable, so |
| for now, when we see such things, we simply return 0. Someday, we may |
| be able to handle such cases, but it will be damn difficult. */ |
| if (! host_integerp (bit_position (decl), 0)) |
| return 0; |
| |
| bitpos_int = int_bit_position (decl); |
| |
| /* If we don't know the size of the field, pretend it's a full word. */ |
| if (host_integerp (field_size_tree, 1)) |
| field_size_in_bits = tree_low_cst (field_size_tree, 1); |
| else |
| field_size_in_bits = BITS_PER_WORD; |
| |
| type_size_in_bits = simple_type_size_in_bits (type); |
| type_align_in_bits = simple_type_align_in_bits (type); |
| decl_align_in_bits = simple_decl_align_in_bits (decl); |
| |
| /* The GCC front-end doesn't make any attempt to keep track of the starting |
| bit offset (relative to the start of the containing structure type) of the |
| hypothetical "containing object" for a bit-field. Thus, when computing |
| the byte offset value for the start of the "containing object" of a |
| bit-field, we must deduce this information on our own. This can be rather |
| tricky to do in some cases. For example, handling the following structure |
| type definition when compiling for an i386/i486 target (which only aligns |
| long long's to 32-bit boundaries) can be very tricky: |
| |
| struct S { int field1; long long field2:31; }; |
| |
| Fortunately, there is a simple rule-of-thumb which can be used in such |
| cases. When compiling for an i386/i486, GCC will allocate 8 bytes for the |
| structure shown above. It decides to do this based upon one simple rule |
| for bit-field allocation. GCC allocates each "containing object" for each |
| bit-field at the first (i.e. lowest addressed) legitimate alignment |
| boundary (based upon the required minimum alignment for the declared type |
| of the field) which it can possibly use, subject to the condition that |
| there is still enough available space remaining in the containing object |
| (when allocated at the selected point) to fully accommodate all of the |
| bits of the bit-field itself. |
| |
| This simple rule makes it obvious why GCC allocates 8 bytes for each |
| object of the structure type shown above. When looking for a place to |
| allocate the "containing object" for `field2', the compiler simply tries |
| to allocate a 64-bit "containing object" at each successive 32-bit |
| boundary (starting at zero) until it finds a place to allocate that 64- |
| bit field such that at least 31 contiguous (and previously unallocated) |
| bits remain within that selected 64 bit field. (As it turns out, for the |
| example above, the compiler finds it is OK to allocate the "containing |
| object" 64-bit field at bit-offset zero within the structure type.) |
| |
| Here we attempt to work backwards from the limited set of facts we're |
| given, and we try to deduce from those facts, where GCC must have believed |
| that the containing object started (within the structure type). The value |
| we deduce is then used (by the callers of this routine) to generate |
| DW_AT_location and DW_AT_bit_offset attributes for fields (both bit-fields |
| and, in the case of DW_AT_location, regular fields as well). */ |
| |
| /* Figure out the bit-distance from the start of the structure to the |
| "deepest" bit of the bit-field. */ |
| deepest_bitpos = bitpos_int + field_size_in_bits; |
| |
| /* This is the tricky part. Use some fancy footwork to deduce where the |
| lowest addressed bit of the containing object must be. */ |
| object_offset_in_bits = deepest_bitpos - type_size_in_bits; |
| |
| /* Round up to type_align by default. This works best for bitfields. */ |
| object_offset_in_bits += type_align_in_bits - 1; |
| object_offset_in_bits /= type_align_in_bits; |
| object_offset_in_bits *= type_align_in_bits; |
| |
| if (object_offset_in_bits > bitpos_int) |
| { |
| /* Sigh, the decl must be packed. */ |
| object_offset_in_bits = deepest_bitpos - type_size_in_bits; |
| |
| /* Round up to decl_align instead. */ |
| object_offset_in_bits += decl_align_in_bits - 1; |
| object_offset_in_bits /= decl_align_in_bits; |
| object_offset_in_bits *= decl_align_in_bits; |
| } |
| |
| return object_offset_in_bits / BITS_PER_UNIT; |
| } |
| |
| /* The following routines define various Dwarf attributes and any data |
| associated with them. */ |
| |
| /* Add a location description attribute value to a DIE. |
| |
| This emits location attributes suitable for whole variables and |
| whole parameters. Note that the location attributes for struct fields are |
| generated by the routine `data_member_location_attribute' below. */ |
| |
| static inline void |
| add_AT_location_description (dw_die_ref die, enum dwarf_attribute attr_kind, |
| dw_loc_descr_ref descr) |
| { |
| if (descr != 0) |
| add_AT_loc (die, attr_kind, descr); |
| } |
| |
| /* Attach the specialized form of location attribute used for data members of |
| struct and union types. In the special case of a FIELD_DECL node which |
| represents a bit-field, the "offset" part of this special location |
| descriptor must indicate the distance in bytes from the lowest-addressed |
| byte of the containing struct or union type to the lowest-addressed byte of |
| the "containing object" for the bit-field. (See the `field_byte_offset' |
| function above). |
| |
| For any given bit-field, the "containing object" is a hypothetical object |
| (of some integral or enum type) within which the given bit-field lives. The |
| type of this hypothetical "containing object" is always the same as the |
| declared type of the individual bit-field itself (for GCC anyway... the |
| DWARF spec doesn't actually mandate this). Note that it is the size (in |
| bytes) of the hypothetical "containing object" which will be given in the |
| DW_AT_byte_size attribute for this bit-field. (See the |
| `byte_size_attribute' function below.) It is also used when calculating the |
| value of the DW_AT_bit_offset attribute. (See the `bit_offset_attribute' |
| function below.) */ |
| |
| static void |
| add_data_member_location_attribute (dw_die_ref die, tree decl) |
| { |
| HOST_WIDE_INT offset; |
| dw_loc_descr_ref loc_descr = 0; |
| |
| if (TREE_CODE (decl) == TREE_BINFO) |
| { |
| /* We're working on the TAG_inheritance for a base class. */ |
| if (BINFO_VIRTUAL_P (decl) && is_cxx ()) |
| { |
| /* For C++ virtual bases we can't just use BINFO_OFFSET, as they |
| aren't at a fixed offset from all (sub)objects of the same |
| type. We need to extract the appropriate offset from our |
| vtable. The following dwarf expression means |
| |
| BaseAddr = ObAddr + *((*ObAddr) - Offset) |
| |
| This is specific to the V3 ABI, of course. */ |
| |
| dw_loc_descr_ref tmp; |
| |
| /* Make a copy of the object address. */ |
| tmp = new_loc_descr (DW_OP_dup, 0, 0); |
| add_loc_descr (&loc_descr, tmp); |
| |
| /* Extract the vtable address. */ |
| tmp = new_loc_descr (DW_OP_deref, 0, 0); |
| add_loc_descr (&loc_descr, tmp); |
| |
| /* Calculate the address of the offset. */ |
| offset = tree_low_cst (BINFO_VPTR_FIELD (decl), 0); |
| gcc_assert (offset < 0); |
| |
| tmp = int_loc_descriptor (-offset); |
| add_loc_descr (&loc_descr, tmp); |
| tmp = new_loc_descr (DW_OP_minus, 0, 0); |
| add_loc_descr (&loc_descr, tmp); |
| |
| /* Extract the offset. */ |
| tmp = new_loc_descr (DW_OP_deref, 0, 0); |
| add_loc_descr (&loc_descr, tmp); |
| |
| /* Add it to the object address. */ |
| tmp = new_loc_descr (DW_OP_plus, 0, 0); |
| add_loc_descr (&loc_descr, tmp); |
| } |
| else |
| offset = tree_low_cst (BINFO_OFFSET (decl), 0); |
| } |
| else |
| offset = field_byte_offset (decl); |
| |
| if (! loc_descr) |
| { |
| enum dwarf_location_atom op; |
| |
| /* The DWARF2 standard says that we should assume that the structure |
| address is already on the stack, so we can specify a structure field |
| address by using DW_OP_plus_uconst. */ |
| |
| #ifdef MIPS_DEBUGGING_INFO |
| /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst |
| operator correctly. It works only if we leave the offset on the |
| stack. */ |
| op = DW_OP_constu; |
| #else |
| op = DW_OP_plus_uconst; |
| #endif |
| |
| loc_descr = new_loc_descr (op, offset, 0); |
| } |
| |
| add_AT_loc (die, DW_AT_data_member_location, loc_descr); |
| } |
| |
| /* Writes integer values to dw_vec_const array. */ |
| |
| static void |
| insert_int (HOST_WIDE_INT val, unsigned int size, unsigned char *dest) |
| { |
| while (size != 0) |
| { |
| *dest++ = val & 0xff; |
| val >>= 8; |
| --size; |
| } |
| } |
| |
| /* Reads integers from dw_vec_const array. Inverse of insert_int. */ |
| |
| static HOST_WIDE_INT |
| extract_int (const unsigned char *src, unsigned int size) |
| { |
| HOST_WIDE_INT val = 0; |
| |
| src += size; |
| while (size != 0) |
| { |
| val <<= 8; |
| val |= *--src & 0xff; |
| --size; |
| } |
| return val; |
| } |
| |
| /* Writes floating point values to dw_vec_const array. */ |
| |
| static void |
| insert_float (rtx rtl, unsigned char *array) |
| { |
| REAL_VALUE_TYPE rv; |
| long val[4]; |
| int i; |
| |
| REAL_VALUE_FROM_CONST_DOUBLE (rv, rtl); |
| real_to_target (val, &rv, GET_MODE (rtl)); |
| |
| /* real_to_target puts 32-bit pieces in each long. Pack them. */ |
| for (i = 0; i < GET_MODE_SIZE (GET_MODE (rtl)) / 4; i++) |
| { |
| insert_int (val[i], 4, array); |
| array += 4; |
| } |
| } |
| |
| /* Attach a DW_AT_const_value attribute for a variable or a parameter which |
| does not have a "location" either in memory or in a register. These |
| things can arise in GNU C when a constant is passed as an actual parameter |
| to an inlined function. They can also arise in C++ where declared |
| constants do not necessarily get memory "homes". */ |
| |
| static void |
| add_const_value_attribute (dw_die_ref die, rtx rtl) |
| { |
| switch (GET_CODE (rtl)) |
| { |
| case CONST_INT: |
| { |
| HOST_WIDE_INT val = INTVAL (rtl); |
| |
| if (val < 0) |
| add_AT_int (die, DW_AT_const_value, val); |
| else |
| add_AT_unsigned (die, DW_AT_const_value, (unsigned HOST_WIDE_INT) val); |
| } |
| break; |
| |
| case CONST_DOUBLE: |
| /* Note that a CONST_DOUBLE rtx could represent either an integer or a |
| floating-point constant. A CONST_DOUBLE is used whenever the |
| constant requires more than one word in order to be adequately |
| represented. We output CONST_DOUBLEs as blocks. */ |
| { |
| enum machine_mode mode = GET_MODE (rtl); |
| |
| if (SCALAR_FLOAT_MODE_P (mode)) |
| { |
| unsigned int length = GET_MODE_SIZE (mode); |
| unsigned char *array = ggc_alloc (length); |
| |
| insert_float (rtl, array); |
| add_AT_vec (die, DW_AT_const_value, length / 4, 4, array); |
| } |
| else |
| { |
| /* ??? We really should be using HOST_WIDE_INT throughout. */ |
| gcc_assert (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT); |
| |
| add_AT_long_long (die, DW_AT_const_value, |
| CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl)); |
| } |
| } |
| break; |
| |
| case CONST_VECTOR: |
| { |
| enum machine_mode mode = GET_MODE (rtl); |
| unsigned int elt_size = GET_MODE_UNIT_SIZE (mode); |
| unsigned int length = CONST_VECTOR_NUNITS (rtl); |
| unsigned char *array = ggc_alloc (length * elt_size); |
| unsigned int i; |
| unsigned char *p; |
| |
| switch (GET_MODE_CLASS (mode)) |
| { |
| case MODE_VECTOR_INT: |
| for (i = 0, p = array; i < length; i++, p += elt_size) |
| { |
| rtx elt = CONST_VECTOR_ELT (rtl, i); |
| HOST_WIDE_INT lo, hi; |
| |
| switch (GET_CODE (elt)) |
| { |
| case CONST_INT: |
| lo = INTVAL (elt); |
| hi = -(lo < 0); |
| break; |
| |
| case CONST_DOUBLE: |
| lo = CONST_DOUBLE_LOW (elt); |
| hi = CONST_DOUBLE_HIGH (elt); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (elt_size <= sizeof (HOST_WIDE_INT)) |
| insert_int (lo, elt_size, p); |
| else |
| { |
| unsigned char *p0 = p; |
| unsigned char *p1 = p + sizeof (HOST_WIDE_INT); |
| |
| gcc_assert (elt_size == 2 * sizeof (HOST_WIDE_INT)); |
| if (WORDS_BIG_ENDIAN) |
| { |
| p0 = p1; |
| p1 = p; |
| } |
| insert_int (lo, sizeof (HOST_WIDE_INT), p0); |
| insert_int (hi, sizeof (HOST_WIDE_INT), p1); |
| } |
| } |
| break; |
| |
| case MODE_VECTOR_FLOAT: |
| for (i = 0, p = array; i < length; i++, p += elt_size) |
| { |
| rtx elt = CONST_VECTOR_ELT (rtl, i); |
| insert_float (elt, p); |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| add_AT_vec (die, DW_AT_const_value, length, elt_size, array); |
| } |
| break; |
| |
| case CONST_STRING: |
| add_AT_string (die, DW_AT_const_value, XSTR (rtl, 0)); |
| break; |
| |
| case SYMBOL_REF: |
| case LABEL_REF: |
| case CONST: |
| add_AT_addr (die, DW_AT_const_value, rtl); |
| VEC_safe_push (rtx, gc, used_rtx_array, rtl); |
| break; |
| |
| case PLUS: |
| /* In cases where an inlined instance of an inline function is passed |
| the address of an `auto' variable (which is local to the caller) we |
| can get a situation where the DECL_RTL of the artificial local |
| variable (for the inlining) which acts as a stand-in for the |
| corresponding formal parameter (of the inline function) will look |
| like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). This is not |
| exactly a compile-time constant expression, but it isn't the address |
| of the (artificial) local variable either. Rather, it represents the |
| *value* which the artificial local variable always has during its |
| lifetime. We currently have no way to represent such quasi-constant |
| values in Dwarf, so for now we just punt and generate nothing. */ |
| break; |
| |
| default: |
| /* No other kinds of rtx should be possible here. */ |
| gcc_unreachable (); |
| } |
| |
| } |
| |
| /* Determine whether the evaluation of EXPR references any variables |
| or functions which aren't otherwise used (and therefore may not be |
| output). */ |
| static tree |
| reference_to_unused (tree * tp, int * walk_subtrees, |
| void * data ATTRIBUTE_UNUSED) |
| { |
| if (! EXPR_P (*tp) && ! CONSTANT_CLASS_P (*tp)) |
| *walk_subtrees = 0; |
| |
| if (DECL_P (*tp) && ! TREE_PUBLIC (*tp) && ! TREE_USED (*tp) |
| && ! TREE_ASM_WRITTEN (*tp)) |
| return *tp; |
| else if (!flag_unit_at_a_time) |
| return NULL_TREE; |
| else if (!cgraph_global_info_ready |
| && (TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == FUNCTION_DECL)) |
| gcc_unreachable (); |
| else if (DECL_P (*tp) && TREE_CODE (*tp) == VAR_DECL) |
| { |
| struct cgraph_varpool_node *node = cgraph_varpool_node (*tp); |
| if (!node->needed) |
| return *tp; |
| } |
| else if (DECL_P (*tp) && TREE_CODE (*tp) == FUNCTION_DECL |
| && (!DECL_EXTERNAL (*tp) || DECL_DECLARED_INLINE_P (*tp))) |
| { |
| struct cgraph_node *node = cgraph_node (*tp); |
| if (!node->output) |
| return *tp; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Generate an RTL constant from a decl initializer INIT with decl type TYPE, |
| for use in a later add_const_value_attribute call. */ |
| |
| static rtx |
| rtl_for_decl_init (tree init, tree type) |
| { |
| rtx rtl = NULL_RTX; |
| |
| /* If a variable is initialized with a string constant without embedded |
| zeros, build CONST_STRING. */ |
| if (TREE_CODE (init) == STRING_CST && TREE_CODE (type) == ARRAY_TYPE) |
| { |
| tree enttype = TREE_TYPE (type); |
| tree domain = TYPE_DOMAIN (type); |
| enum machine_mode mode = TYPE_MODE (enttype); |
| |
| if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_SIZE (mode) == 1 |
| && domain |
| && integer_zerop (TYPE_MIN_VALUE (domain)) |
| && compare_tree_int (TYPE_MAX_VALUE (domain), |
| TREE_STRING_LENGTH (init) - 1) == 0 |
| && ((size_t) TREE_STRING_LENGTH (init) |
| == strlen (TREE_STRING_POINTER (init)) + 1)) |
| rtl = gen_rtx_CONST_STRING (VOIDmode, |
| ggc_strdup (TREE_STRING_POINTER (init))); |
| } |
| /* Other aggregates, and complex values, could be represented using |
| CONCAT: FIXME! */ |
| else if (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE) |
| ; |
| /* Vectors only work if their mode is supported by the target. |
| FIXME: generic vectors ought to work too. */ |
| else if (TREE_CODE (type) == VECTOR_TYPE && TYPE_MODE (type) == BLKmode) |
| ; |
| /* If the initializer is something that we know will expand into an |
| immediate RTL constant, expand it now. We must be careful not to |
| reference variables which won't be output. */ |
| else if (initializer_constant_valid_p (init, type) |
| && ! walk_tree (&init, reference_to_unused, NULL, NULL)) |
| { |
| rtl = expand_expr (init, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); |
| |
| /* If expand_expr returns a MEM, it wasn't immediate. */ |
| gcc_assert (!rtl || !MEM_P (rtl)); |
| } |
| |
| return rtl; |
| } |
| |
| /* Generate RTL for the variable DECL to represent its location. */ |
| |
| static rtx |
| rtl_for_decl_location (tree decl) |
| { |
| rtx rtl; |
| |
| /* Here we have to decide where we are going to say the parameter "lives" |
| (as far as the debugger is concerned). We only have a couple of |
| choices. GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. |
| |
| DECL_RTL normally indicates where the parameter lives during most of the |
| activation of the function. If optimization is enabled however, this |
| could be either NULL or else a pseudo-reg. Both of those cases indicate |
| that the parameter doesn't really live anywhere (as far as the code |
| generation parts of GCC are concerned) during most of the function's |
| activation. That will happen (for example) if the parameter is never |
| referenced within the function. |
| |
| We could just generate a location descriptor here for all non-NULL |
| non-pseudo values of DECL_RTL and ignore all of the rest, but we can be |
| a little nicer than that if we also consider DECL_INCOMING_RTL in cases |
| where DECL_RTL is NULL or is a pseudo-reg. |
| |
| Note however that we can only get away with using DECL_INCOMING_RTL as |
| a backup substitute for DECL_RTL in certain limited cases. In cases |
| where DECL_ARG_TYPE (decl) indicates the same type as TREE_TYPE (decl), |
| we can be sure that the parameter was passed using the same type as it is |
| declared to have within the function, and that its DECL_INCOMING_RTL |
| points us to a place where a value of that type is passed. |
| |
| In cases where DECL_ARG_TYPE (decl) and TREE_TYPE (decl) are different, |
| we cannot (in general) use DECL_INCOMING_RTL as a substitute for DECL_RTL |
| because in these cases DECL_INCOMING_RTL points us to a value of some |
| type which is *different* from the type of the parameter itself. Thus, |
| if we tried to use DECL_INCOMING_RTL to generate a location attribute in |
| such cases, the debugger would end up (for example) trying to fetch a |
| `float' from a place which actually contains the first part of a |
| `double'. That would lead to really incorrect and confusing |
| output at debug-time. |
| |
| So, in general, we *do not* use DECL_INCOMING_RTL as a backup for DECL_RTL |
| in cases where DECL_ARG_TYPE (decl) != TREE_TYPE (decl). There |
| are a couple of exceptions however. On little-endian machines we can |
| get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE (decl) is |
| not the same as TREE_TYPE (decl), but only when DECL_ARG_TYPE (decl) is |
| an integral type that is smaller than TREE_TYPE (decl). These cases arise |
| when (on a little-endian machine) a non-prototyped function has a |
| parameter declared to be of type `short' or `char'. In such cases, |
| TREE_TYPE (decl) will be `short' or `char', DECL_ARG_TYPE (decl) will |
| be `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the |
| passed `int' value. If the debugger then uses that address to fetch |
| a `short' or a `char' (on a little-endian machine) the result will be |
| the correct data, so we allow for such exceptional cases below. |
| |
| Note that our goal here is to describe the place where the given formal |
| parameter lives during most of the function's activation (i.e. between the |
| end of the prologue and the start of the epilogue). We'll do that as best |
| as we can. Note however that if the given formal parameter is modified |
| sometime during the execution of the function, then a stack backtrace (at |
| debug-time) will show the function as having been called with the *new* |
| value rather than the value which was originally passed in. This happens |
| rarely enough that it is not a major problem, but it *is* a problem, and |
| I'd like to fix it. |
| |
| A future version of dwarf2out.c may generate two additional attributes for |
| any given DW_TAG_formal_parameter DIE which will describe the "passed |
| type" and the "passed location" for the given formal parameter in addition |
| to the attributes we now generate to indicate the "declared type" and the |
| "active location" for each parameter. This additional set of attributes |
| could be used by debuggers for stack backtraces. Separately, note that |
| sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL can be NULL also. |
| This happens (for example) for inlined-instances of inline function formal |
| parameters which are never referenced. This really shouldn't be |
| happening. All PARM_DECL nodes should get valid non-NULL |
| DECL_INCOMING_RTL values. FIXME. */ |
| |
| /* Use DECL_RTL as the "location" unless we find something better. */ |
| rtl = DECL_RTL_IF_SET (decl); |
| |
| /* When generating abstract instances, ignore everything except |
| constants, symbols living in memory, and symbols living in |
| fixed registers. */ |
| if (! reload_completed) |
| { |
| if (rtl |
| && (CONSTANT_P (rtl) |
| || (MEM_P (rtl) |
| && CONSTANT_P (XEXP (rtl, 0))) |
| || (REG_P (rtl) |
| && TREE_CODE (decl) == VAR_DECL |
| && TREE_STATIC (decl)))) |
| { |
| rtl = targetm.delegitimize_address (rtl); |
| return rtl; |
| } |
| rtl = NULL_RTX; |
| } |
| else if (TREE_CODE (decl) == PARM_DECL) |
| { |
| if (rtl == NULL_RTX || is_pseudo_reg (rtl)) |
| { |
| tree declared_type = TREE_TYPE (decl); |
| tree passed_type = DECL_ARG_TYPE (decl); |
| enum machine_mode dmode = TYPE_MODE (declared_type); |
| enum machine_mode pmode = TYPE_MODE (passed_type); |
| |
| /* This decl represents a formal parameter which was optimized out. |
| Note that DECL_INCOMING_RTL may be NULL in here, but we handle |
| all cases where (rtl == NULL_RTX) just below. */ |
| if (dmode == pmode) |
| rtl = DECL_INCOMING_RTL (decl); |
| else if (SCALAR_INT_MODE_P (dmode) |
| && GET_MODE_SIZE (dmode) <= GET_MODE_SIZE (pmode) |
| && DECL_INCOMING_RTL (decl)) |
| { |
| rtx inc = DECL_INCOMING_RTL (decl); |
| if (REG_P (inc)) |
| rtl = inc; |
| else if (MEM_P (inc)) |
| { |
| if (BYTES_BIG_ENDIAN) |
| rtl = adjust_address_nv (inc, dmode, |
| GET_MODE_SIZE (pmode) |
| - GET_MODE_SIZE (dmode)); |
| else |
| rtl = inc; |
| } |
| } |
| } |
| |
| /* If the parm was passed in registers, but lives on the stack, then |
| make a big endian correction if the mode of the type of the |
| parameter is not the same as the mode of the rtl. */ |
| /* ??? This is the same series of checks that are made in dbxout.c before |
| we reach the big endian correction code there. It isn't clear if all |
| of these checks are necessary here, but keeping them all is the safe |
| thing to do. */ |
| else if (MEM_P (rtl) |
| && XEXP (rtl, 0) != const0_rtx |
| && ! CONSTANT_P (XEXP (rtl, 0)) |
| /* Not passed in memory. */ |
| && !MEM_P (DECL_INCOMING_RTL (decl)) |
| /* Not passed by invisible reference. */ |
| && (!REG_P (XEXP (rtl, 0)) |
| || REGNO (XEXP (rtl, 0)) == HARD_FRAME_POINTER_REGNUM |
| || REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM |
| #if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM |
| || REGNO (XEXP (rtl, 0)) == ARG_POINTER_REGNUM |
| #endif |
| ) |
| /* Big endian correction check. */ |
| && BYTES_BIG_ENDIAN |
| && TYPE_MODE (TREE_TYPE (decl)) != GET_MODE (rtl) |
| && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl))) |
| < UNITS_PER_WORD)) |
| { |
| int offset = (UNITS_PER_WORD |
| - GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl)))); |
| |
| rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (decl)), |
| plus_constant (XEXP (rtl, 0), offset)); |
| } |
| } |
| else if (TREE_CODE (decl) == VAR_DECL |
| && rtl |
| && MEM_P (rtl) |
| && GET_MODE (rtl) != TYPE_MODE (TREE_TYPE (decl)) |
| && BYTES_BIG_ENDIAN) |
| { |
| int rsize = GET_MODE_SIZE (GET_MODE (rtl)); |
| int dsize = GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (decl))); |
| |
| /* If a variable is declared "register" yet is smaller than |
| a register, then if we store the variable to memory, it |
| looks like we're storing a register-sized value, when in |
| fact we are not. We need to adjust the offset of the |
| storage location to reflect the actual value's bytes, |
| else gdb will not be able to display it. */ |
| if (rsize > dsize) |
| rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (decl)), |
| plus_constant (XEXP (rtl, 0), rsize-dsize)); |
| } |
| |
| /* A variable with no DECL_RTL but a DECL_INITIAL is a compile-time constant, |
| and will have been substituted directly into all expressions that use it. |
| C does not have such a concept, but C++ and other languages do. */ |
| if (!rtl && TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl)) |
| rtl = rtl_for_decl_init (DECL_INITIAL (decl), TREE_TYPE (decl)); |
| |
| if (rtl) |
| rtl = targetm.delegitimize_address (rtl); |
| |
| /* If we don't look past the constant pool, we risk emitting a |
| reference to a constant pool entry that isn't referenced from |
| code, and thus is not emitted. */ |
| if (rtl) |
| rtl = avoid_constant_pool_reference (rtl); |
| |
| return rtl; |
| } |
| |
| /* We need to figure out what section we should use as the base for the |
| address ranges where a given location is valid. |
| 1. If this particular DECL has a section associated with it, use that. |
| 2. If this function has a section associated with it, use that. |
| 3. Otherwise, use the text section. |
| XXX: If you split a variable across multiple sections, we won't notice. */ |
| |
| static const char * |
| secname_for_decl (tree decl) |
| { |
| const char *secname; |
| |
| if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_SECTION_NAME (decl)) |
| { |
| tree sectree = DECL_SECTION_NAME (decl); |
| secname = TREE_STRING_POINTER (sectree); |
| } |
| else if (current_function_decl && DECL_SECTION_NAME (current_function_decl)) |
| { |
| tree sectree = DECL_SECTION_NAME (current_function_decl); |
| secname = TREE_STRING_POINTER (sectree); |
| } |
| else if (cfun && in_cold_section_p) |
| secname = cfun->cold_section_label; |
| else |
| secname = text_section_label; |
| |
| return secname; |
| } |
| |
| /* Generate *either* a DW_AT_location attribute or else a DW_AT_const_value |
| data attribute for a variable or a parameter. We generate the |
| DW_AT_const_value attribute only in those cases where the given variable |
| or parameter does not have a true "location" either in memory or in a |
| register. This can happen (for example) when a constant is passed as an |
| actual argument in a call to an inline function. (It's possible that |
| these things can crop up in other ways also.) Note that one type of |
| constant value which can be passed into an inlined function is a constant |
| pointer. This can happen for example if an actual argument in an inlined |
| function call evaluates to a compile-time constant address. */ |
| |
| static void |
| add_location_or_const_value_attribute (dw_die_ref die, tree decl, |
| enum dwarf_attribute attr) |
| { |
| rtx rtl; |
| dw_loc_descr_ref descr; |
| var_loc_list *loc_list; |
| struct var_loc_node *node; |
| if (TREE_CODE (decl) == ERROR_MARK) |
| return; |
| |
| gcc_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL |
| || TREE_CODE (decl) == RESULT_DECL); |
| |
| /* See if we possibly have multiple locations for this variable. */ |
| loc_list = lookup_decl_loc (decl); |
| |
| /* If it truly has multiple locations, the first and last node will |
| differ. */ |
| if (loc_list && loc_list->first != loc_list->last) |
| { |
| const char *endname, *secname; |
| dw_loc_list_ref list; |
| rtx varloc; |
| /* APPLE LOCAL track initialization status 4964532 */ |
| enum var_init_status initialized; |
| |
| /* Now that we know what section we are using for a base, |
| actually construct the list of locations. |
| The first location information is what is passed to the |
| function that creates the location list, and the remaining |
| locations just get added on to that list. |
| Note that we only know the start address for a location |
| (IE location changes), so to build the range, we use |
| the range [current location start, next location start]. |
| This means we have to special case the last node, and generate |
| a range of [last location start, end of function label]. */ |
| |
| node = loc_list->first; |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| secname = secname_for_decl (decl); |
| |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| if (NOTE_VAR_LOCATION_LOC (node->var_loc_note)) |
| initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note); |
| else |
| initialized = STATUS_INITIALIZED; |
| |
| list = new_loc_list (loc_descriptor (varloc, initialized), |
| node->label, node->next->label, secname, 1); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| node = node->next; |
| |
| for (; node->next; node = node->next) |
| if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX) |
| { |
| /* The variable has a location between NODE->LABEL and |
| NODE->NEXT->LABEL. */ |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| enum var_init_status initialized = |
| NOTE_VAR_LOCATION_STATUS (node->var_loc_note); |
| |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| add_loc_descr_to_loc_list (&list, |
| loc_descriptor (varloc, initialized), |
| node->label, node->next->label, secname); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| } |
| |
| /* If the variable has a location at the last label |
| it keeps its location until the end of function. */ |
| if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX) |
| { |
| char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| enum var_init_status initialized = |
| NOTE_VAR_LOCATION_STATUS (node->var_loc_note); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| if (!current_function_decl) |
| endname = text_end_label; |
| else |
| { |
| ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, |
| current_function_funcdef_no); |
| endname = ggc_strdup (label_id); |
| } |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| add_loc_descr_to_loc_list (&list, |
| loc_descriptor (varloc, initialized), |
| node->label, endname, secname); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| } |
| |
| /* Finally, add the location list to the DIE, and we are done. */ |
| add_AT_loc_list (die, attr, list); |
| return; |
| } |
| |
| /* Try to get some constant RTL for this decl, and use that as the value of |
| the location. */ |
| |
| rtl = rtl_for_decl_location (decl); |
| if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING)) |
| { |
| add_const_value_attribute (die, rtl); |
| return; |
| } |
| |
| /* If we have tried to generate the location otherwise, and it |
| didn't work out (we wouldn't be here if we did), and we have a one entry |
| location list, try generating a location from that. */ |
| if (loc_list && loc_list->first) |
| { |
| /* APPLE LOCAL begin track initialization status 4964532 */ |
| enum var_init_status status; |
| node = loc_list->first; |
| status = NOTE_VAR_LOCATION_STATUS (node->var_loc_note); |
| descr = loc_descriptor (NOTE_VAR_LOCATION (node->var_loc_note), status); |
| /* APPLE LOCAL end track initialization status 4964532 */ |
| if (descr) |
| { |
| add_AT_location_description (die, attr, descr); |
| return; |
| } |
| } |
| |
| /* We couldn't get any rtl, so try directly generating the location |
| description from the tree. */ |
| descr = loc_descriptor_from_tree (decl); |
| if (descr) |
| { |
| add_AT_location_description (die, attr, descr); |
| return; |
| } |
| /* None of that worked, so it must not really have a location; |
| try adding a constant value attribute from the DECL_INITIAL. */ |
| tree_add_const_value_attribute (die, decl); |
| } |
| |
| /* If we don't have a copy of this variable in memory for some reason (such |
| as a C++ member constant that doesn't have an out-of-line definition), |
| we should tell the debugger about the constant value. */ |
| |
| static void |
| tree_add_const_value_attribute (dw_die_ref var_die, tree decl) |
| { |
| tree init = DECL_INITIAL (decl); |
| tree type = TREE_TYPE (decl); |
| rtx rtl; |
| |
| if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl) && init) |
| /* OK */; |
| else |
| return; |
| |
| rtl = rtl_for_decl_init (init, type); |
| if (rtl) |
| add_const_value_attribute (var_die, rtl); |
| } |
| |
| /* Convert the CFI instructions for the current function into a |
| location list. This is used for DW_AT_frame_base when we targeting |
| a dwarf2 consumer that does not support the dwarf3 |
| DW_OP_call_frame_cfa. OFFSET is a constant to be added to all CFA |
| expressions. */ |
| |
| static dw_loc_list_ref |
| convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) |
| { |
| dw_fde_ref fde; |
| dw_loc_list_ref list, *list_tail; |
| dw_cfi_ref cfi; |
| dw_cfa_location last_cfa, next_cfa; |
| const char *start_label, *last_label, *section; |
| |
| fde = &fde_table[fde_table_in_use - 1]; |
| |
| section = secname_for_decl (current_function_decl); |
| list_tail = &list; |
| list = NULL; |
| |
| next_cfa.reg = INVALID_REGNUM; |
| next_cfa.offset = 0; |
| next_cfa.indirect = 0; |
| next_cfa.base_offset = 0; |
| |
| start_label = fde->dw_fde_begin; |
| |
| /* ??? Bald assumption that the CIE opcode list does not contain |
| advance opcodes. */ |
| for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) |
| lookup_cfa_1 (cfi, &next_cfa); |
| |
| last_cfa = next_cfa; |
| last_label = start_label; |
| |
| for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) |
| switch (cfi->dw_cfi_opc) |
| { |
| case DW_CFA_set_loc: |
| case DW_CFA_advance_loc1: |
| case DW_CFA_advance_loc2: |
| case DW_CFA_advance_loc4: |
| if (!cfa_equal_p (&last_cfa, &next_cfa)) |
| { |
| *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), |
| start_label, last_label, section, |
| list == NULL); |
| |
| list_tail = &(*list_tail)->dw_loc_next; |
| last_cfa = next_cfa; |
| start_label = last_label; |
| } |
| last_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; |
| break; |
| |
| case DW_CFA_advance_loc: |
| /* The encoding is complex enough that we should never emit this. */ |
| case DW_CFA_remember_state: |
| case DW_CFA_restore_state: |
| /* We don't handle these two in this function. It would be possible |
| if it were to be required. */ |
| gcc_unreachable (); |
| |
| default: |
| lookup_cfa_1 (cfi, &next_cfa); |
| break; |
| } |
| |
| if (!cfa_equal_p (&last_cfa, &next_cfa)) |
| { |
| *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), |
| start_label, last_label, section, |
| list == NULL); |
| list_tail = &(*list_tail)->dw_loc_next; |
| start_label = last_label; |
| } |
| *list_tail = new_loc_list (build_cfa_loc (&next_cfa, offset), |
| start_label, fde->dw_fde_end, section, |
| list == NULL); |
| |
| return list; |
| } |
| |
| /* Compute a displacement from the "steady-state frame pointer" to the |
| frame base (often the same as the CFA), and store it in |
| frame_pointer_fb_offset. OFFSET is added to the displacement |
| before the latter is negated. */ |
| |
| static void |
| compute_frame_pointer_to_fb_displacement (HOST_WIDE_INT offset) |
| { |
| rtx reg, elim; |
| |
| #ifdef FRAME_POINTER_CFA_OFFSET |
| reg = frame_pointer_rtx; |
| offset += FRAME_POINTER_CFA_OFFSET (current_function_decl); |
| #else |
| reg = arg_pointer_rtx; |
| offset += ARG_POINTER_CFA_OFFSET (current_function_decl); |
| #endif |
| |
| elim = eliminate_regs (reg, VOIDmode, NULL_RTX); |
| if (GET_CODE (elim) == PLUS) |
| { |
| offset += INTVAL (XEXP (elim, 1)); |
| elim = XEXP (elim, 0); |
| } |
| /* APPLE LOCAL begin ARM prefer SP to FP */ |
| |
| frame_pointer_fb_offset_from = elim; |
| /* APPLE LOCAL end ARM prefer SP to FP */ |
| frame_pointer_fb_offset = -offset; |
| } |
| |
| /* Generate a DW_AT_name attribute given some string value to be included as |
| the value of the attribute. */ |
| |
| static void |
| add_name_attribute (dw_die_ref die, const char *name_string) |
| { |
| if (name_string != NULL && *name_string != 0) |
| { |
| if (demangle_name_func) |
| name_string = (*demangle_name_func) (name_string); |
| |
| add_AT_string (die, DW_AT_name, name_string); |
| } |
| } |
| |
| /* Generate a DW_AT_comp_dir attribute for DIE. */ |
| |
| static void |
| add_comp_dir_attribute (dw_die_ref die) |
| { |
| const char *wd = get_src_pwd (); |
| if (wd != NULL) |
| add_AT_string (die, DW_AT_comp_dir, wd); |
| } |
| |
| /* Given a tree node describing an array bound (either lower or upper) output |
| a representation for that bound. */ |
| |
| static void |
| add_bound_info (dw_die_ref subrange_die, enum dwarf_attribute bound_attr, tree bound) |
| { |
| switch (TREE_CODE (bound)) |
| { |
| case ERROR_MARK: |
| return; |
| |
| /* All fixed-bounds are represented by INTEGER_CST nodes. */ |
| case INTEGER_CST: |
| if (! host_integerp (bound, 0) |
| || (bound_attr == DW_AT_lower_bound |
| && (((is_c_family () || is_java ()) && integer_zerop (bound)) |
| || (is_fortran () && integer_onep (bound))))) |
| /* Use the default. */ |
| ; |
| else |
| add_AT_unsigned (subrange_die, bound_attr, tree_low_cst (bound, 0)); |
| break; |
| |
| case CONVERT_EXPR: |
| case NOP_EXPR: |
| case NON_LVALUE_EXPR: |
| case VIEW_CONVERT_EXPR: |
| add_bound_info (subrange_die, bound_attr, TREE_OPERAND (bound, 0)); |
| break; |
| |
| case SAVE_EXPR: |
| break; |
| |
| case VAR_DECL: |
| case PARM_DECL: |
| case RESULT_DECL: |
| { |
| dw_die_ref decl_die = lookup_decl_die (bound); |
| |
| /* ??? Can this happen, or should the variable have been bound |
| first? Probably it can, since I imagine that we try to create |
| the types of parameters in the order in which they exist in |
| the list, and won't have created a forward reference to a |
| later parameter. */ |
| if (decl_die != NULL) |
| add_AT_die_ref (subrange_die, bound_attr, decl_die); |
| break; |
| } |
| |
| default: |
| { |
| /* Otherwise try to create a stack operation procedure to |
| evaluate the value of the array bound. */ |
| |
| dw_die_ref ctx, decl_die; |
| dw_loc_descr_ref loc; |
| |
| loc = loc_descriptor_from_tree (bound); |
| if (loc == NULL) |
| break; |
| |
| if (current_function_decl == 0) |
| ctx = comp_unit_die; |
| else |
| ctx = lookup_decl_die (current_function_decl); |
| |
| decl_die = new_die (DW_TAG_variable, ctx, bound); |
| add_AT_flag (decl_die, DW_AT_artificial, 1); |
| add_type_attribute (decl_die, TREE_TYPE (bound), 1, 0, ctx); |
| add_AT_loc (decl_die, DW_AT_location, loc); |
| |
| add_AT_die_ref (subrange_die, bound_attr, decl_die); |
| break; |
| } |
| } |
| } |
| |
| /* Note that the block of subscript information for an array type also |
| includes information about the element type of type given array type. */ |
| |
| static void |
| add_subscript_info (dw_die_ref type_die, tree type) |
| { |
| #ifndef MIPS_DEBUGGING_INFO |
| unsigned dimension_number; |
| #endif |
| tree lower, upper; |
| dw_die_ref subrange_die; |
| |
| /* The GNU compilers represent multidimensional array types as sequences of |
| one dimensional array types whose element types are themselves array |
| types. Here we squish that down, so that each multidimensional array |
| type gets only one array_type DIE in the Dwarf debugging info. The draft |
| Dwarf specification say that we are allowed to do this kind of |
| compression in C (because there is no difference between an array or |
| arrays and a multidimensional array in C) but for other source languages |
| (e.g. Ada) we probably shouldn't do this. */ |
| |
| /* ??? The SGI dwarf reader fails for multidimensional arrays with a |
| const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. |
| We work around this by disabling this feature. See also |
| gen_array_type_die. */ |
| #ifndef MIPS_DEBUGGING_INFO |
| for (dimension_number = 0; |
| TREE_CODE (type) == ARRAY_TYPE; |
| type = TREE_TYPE (type), dimension_number++) |
| #endif |
| { |
| tree domain = TYPE_DOMAIN (type); |
| |
| /* Arrays come in three flavors: Unspecified bounds, fixed bounds, |
| and (in GNU C only) variable bounds. Handle all three forms |
| here. */ |
| subrange_die = new_die (DW_TAG_subrange_type, type_die, NULL); |
| if (domain) |
| { |
| /* We have an array type with specified bounds. */ |
| lower = TYPE_MIN_VALUE (domain); |
| upper = TYPE_MAX_VALUE (domain); |
| |
| /* Define the index type. */ |
| if (TREE_TYPE (domain)) |
| { |
| /* ??? This is probably an Ada unnamed subrange type. Ignore the |
| TREE_TYPE field. We can't emit debug info for this |
| because it is an unnamed integral type. */ |
| if (TREE_CODE (domain) == INTEGER_TYPE |
| && TYPE_NAME (domain) == NULL_TREE |
| && TREE_CODE (TREE_TYPE (domain)) == INTEGER_TYPE |
| && TYPE_NAME (TREE_TYPE (domain)) == NULL_TREE) |
| ; |
| else |
| add_type_attribute (subrange_die, TREE_TYPE (domain), 0, 0, |
| type_die); |
| } |
| |
| /* ??? If upper is NULL, the array has unspecified length, |
| but it does have a lower bound. This happens with Fortran |
| dimension arr(N:*) |
| Since the debugger is definitely going to need to know N |
| to produce useful results, go ahead and output the lower |
| bound solo, and hope the debugger can cope. */ |
| |
| add_bound_info (subrange_die, DW_AT_lower_bound, lower); |
| if (upper) |
| add_bound_info (subrange_die, DW_AT_upper_bound, upper); |
| } |
| |
| /* Otherwise we have an array type with an unspecified length. The |
| DWARF-2 spec does not say how to handle this; let's just leave out the |
| bounds. */ |
| } |
| } |
| |
| static void |
| add_byte_size_attribute (dw_die_ref die, tree tree_node) |
| { |
| unsigned size; |
| |
| switch (TREE_CODE (tree_node)) |
| { |
| case ERROR_MARK: |
| size = 0; |
| break; |
| case ENUMERAL_TYPE: |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| size = int_size_in_bytes (tree_node); |
| break; |
| case FIELD_DECL: |
| /* For a data member of a struct or union, the DW_AT_byte_size is |
| generally given as the number of bytes normally allocated for an |
| object of the *declared* type of the member itself. This is true |
| even for bit-fields. */ |
| size = simple_type_size_in_bits (field_type (tree_node)) / BITS_PER_UNIT; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Note that `size' might be -1 when we get to this point. If it is, that |
| indicates that the byte size of the entity in question is variable. We |
| have no good way of expressing this fact in Dwarf at the present time, |
| so just let the -1 pass on through. */ |
| add_AT_unsigned (die, DW_AT_byte_size, size); |
| } |
| |
| /* For a FIELD_DECL node which represents a bit-field, output an attribute |
| which specifies the distance in bits from the highest order bit of the |
| "containing object" for the bit-field to the highest order bit of the |
| bit-field itself. |
| |
| For any given bit-field, the "containing object" is a hypothetical object |
| (of some integral or enum type) within which the given bit-field lives. The |
| type of this hypothetical "containing object" is always the same as the |
| declared type of the individual bit-field itself. The determination of the |
| exact location of the "containing object" for a bit-field is rather |
| complicated. It's handled by the `field_byte_offset' function (above). |
| |
| Note that it is the size (in bytes) of the hypothetical "containing object" |
| which will be given in the DW_AT_byte_size attribute for this bit-field. |
| (See `byte_size_attribute' above). */ |
| |
| static inline void |
| add_bit_offset_attribute (dw_die_ref die, tree decl) |
| { |
| HOST_WIDE_INT object_offset_in_bytes = field_byte_offset (decl); |
| tree type = DECL_BIT_FIELD_TYPE (decl); |
| HOST_WIDE_INT bitpos_int; |
| HOST_WIDE_INT highest_order_object_bit_offset; |
| HOST_WIDE_INT highest_order_field_bit_offset; |
| HOST_WIDE_INT unsigned bit_offset; |
| |
| /* Must be a field and a bit field. */ |
| gcc_assert (type && TREE_CODE (decl) == FIELD_DECL); |
| |
| /* We can't yet handle bit-fields whose offsets are variable, so if we |
| encounter such things, just return without generating any attribute |
| whatsoever. Likewise for variable or too large size. */ |
| if (! host_integerp (bit_position (decl), 0) |
| || ! host_integerp (DECL_SIZE (decl), 1)) |
| return; |
| |
| bitpos_int = int_bit_position (decl); |
| |
| /* Note that the bit offset is always the distance (in bits) from the |
| highest-order bit of the "containing object" to the highest-order bit of |
| the bit-field itself. Since the "high-order end" of any object or field |
| is different on big-endian and little-endian machines, the computation |
| below must take account of these differences. */ |
| highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT; |
| highest_order_field_bit_offset = bitpos_int; |
| |
| if (! BYTES_BIG_ENDIAN) |
| { |
| highest_order_field_bit_offset += tree_low_cst (DECL_SIZE (decl), 0); |
| highest_order_object_bit_offset += simple_type_size_in_bits (type); |
| } |
| |
| bit_offset |
| = (! BYTES_BIG_ENDIAN |
| ? highest_order_object_bit_offset - highest_order_field_bit_offset |
| : highest_order_field_bit_offset - highest_order_object_bit_offset); |
| |
| add_AT_unsigned (die, DW_AT_bit_offset, bit_offset); |
| } |
| |
| /* For a FIELD_DECL node which represents a bit field, output an attribute |
| which specifies the length in bits of the given field. */ |
| |
| static inline void |
| add_bit_size_attribute (dw_die_ref die, tree decl) |
| { |
| /* Must be a field and a bit field. */ |
| gcc_assert (TREE_CODE (decl) == FIELD_DECL |
| && DECL_BIT_FIELD_TYPE (decl)); |
| |
| if (host_integerp (DECL_SIZE (decl), 1)) |
| add_AT_unsigned (die, DW_AT_bit_size, tree_low_cst (DECL_SIZE (decl), 1)); |
| } |
| |
| /* If the compiled language is ANSI C, then add a 'prototyped' |
| attribute, if arg types are given for the parameters of a function. */ |
| |
| static inline void |
| add_prototyped_attribute (dw_die_ref die, tree func_type) |
| { |
| /* APPLE LOCAL begin radar 5344182 */ |
| unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); |
| |
| if ((lang == DW_LANG_C89 || lang == DW_LANG_ObjC) |
| && TYPE_ARG_TYPES (func_type) != NULL) |
| /* APPLE LOCAL end radar 5344182 */ |
| add_AT_flag (die, DW_AT_prototyped, 1); |
| } |
| |
| /* Add an 'abstract_origin' attribute below a given DIE. The DIE is found |
| by looking in either the type declaration or object declaration |
| equate table. */ |
| |
| static inline void |
| add_abstract_origin_attribute (dw_die_ref die, tree origin) |
| { |
| dw_die_ref origin_die = NULL; |
| |
| if (TREE_CODE (origin) != FUNCTION_DECL) |
| { |
| /* We may have gotten separated from the block for the inlined |
| function, if we're in an exception handler or some such; make |
| sure that the abstract function has been written out. |
| |
| Doing this for nested functions is wrong, however; functions are |
| distinct units, and our context might not even be inline. */ |
| tree fn = origin; |
| |
| if (TYPE_P (fn)) |
| fn = TYPE_STUB_DECL (fn); |
| |
| fn = decl_function_context (fn); |
| if (fn) |
| dwarf2out_abstract_function (fn); |
| } |
| |
| if (DECL_P (origin)) |
| origin_die = lookup_decl_die (origin); |
| else if (TYPE_P (origin)) |
| origin_die = lookup_type_die (origin); |
| |
| /* XXX: Functions that are never lowered don't always have correct block |
| trees (in the case of java, they simply have no block tree, in some other |
| languages). For these functions, there is nothing we can really do to |
| output correct debug info for inlined functions in all cases. Rather |
| than die, we'll just produce deficient debug info now, in that we will |
| have variables without a proper abstract origin. In the future, when all |
| functions are lowered, we should re-add a gcc_assert (origin_die) |
| here. */ |
| |
| if (origin_die) |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| { |
| add_AT_die_ref (die, DW_AT_abstract_origin, origin_die); |
| if (die->die_tag == DW_TAG_inlined_subroutine) |
| { |
| dw_attr_ref inline_attr = get_AT (origin_die, DW_AT_inline); |
| if (!inline_attr) |
| { |
| if (DECL_P (origin) && DECL_DECLARED_INLINE_P (origin)) |
| add_AT_unsigned (origin_die, DW_AT_inline, DW_INL_declared_inlined); |
| else |
| add_AT_unsigned (origin_die, DW_AT_inline, DW_INL_inlined); |
| } |
| } |
| } |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| } |
| |
| /* We do not currently support the pure_virtual attribute. */ |
| |
| static inline void |
| add_pure_or_virtual_attribute (dw_die_ref die, tree func_decl) |
| { |
| if (DECL_VINDEX (func_decl)) |
| { |
| add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); |
| |
| if (host_integerp (DECL_VINDEX (func_decl), 0)) |
| add_AT_loc (die, DW_AT_vtable_elem_location, |
| new_loc_descr (DW_OP_constu, |
| tree_low_cst (DECL_VINDEX (func_decl), 0), |
| 0)); |
| |
| /* GNU extension: Record what type this method came from originally. */ |
| if (debug_info_level > DINFO_LEVEL_TERSE) |
| add_AT_die_ref (die, DW_AT_containing_type, |
| lookup_type_die (DECL_CONTEXT (func_decl))); |
| } |
| } |
| |
| /* APPLE LOCAL begin opt diary */ |
| /* Add source coordinate attributes for the given location. */ |
| |
| static void |
| add_src_coords_attributes_locus (dw_die_ref die, expanded_location s) |
| { |
| add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file)); |
| add_AT_unsigned (die, DW_AT_decl_line, s.line); |
| } |
| /* APPLE LOCAL end opt diary */ |
| |
| /* Add source coordinate attributes for the given decl. */ |
| |
| static void |
| add_src_coords_attributes (dw_die_ref die, tree decl) |
| { |
| expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl)); |
| |
| add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file)); |
| add_AT_unsigned (die, DW_AT_decl_line, s.line); |
| } |
| |
| /* APPLE LOCAL begin radar 5636185 */ |
| /* Given a function or variable decl, DECL, check to see if |
| it has an assembler name, and if so, check to see if the |
| decl name is just a prefix for the assembler name, or if the |
| assembler name is really different. For example if the name |
| is "foo" and the assembler name is "foo.37", then the names |
| do not really differ, and the assembler name should not be written |
| out. However for names such as "cXf" vs "_ZN1cblahblahF3cXfE", |
| the assembler name does differ and should be written out. |
| |
| This is used to determine if the DW_AT_MIPS_linkage_name attribute |
| needs to be added to the die for DECL or not. Later, after all |
| the dies have been generated, there is a further check to see |
| if the die is local to a subroutine; if it is, then the attribute |
| is removed before the debug information is written out. */ |
| |
| static bool |
| assembler_name_exists_and_is_different (tree decl) |
| { |
| bool ret_val = true; |
| const char *assem_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| const char *name = IDENTIFIER_POINTER (DECL_NAME (decl)); |
| unsigned int len = strlen (name); |
| |
| if (!assem_name) |
| ret_val = false; |
| else |
| { |
| if (strlen (assem_name) < len) |
| len = strlen (assem_name); |
| |
| if (strncmp (name, assem_name, len) == 0) |
| ret_val = false; |
| } |
| |
| return ret_val; |
| } |
| |
| /* APPLE LOCAL end radar 5636185 */ |
| |
| /* Add a DW_AT_name attribute and source coordinate attribute for the |
| given decl, but only if it actually has a name. */ |
| |
| static void |
| add_name_and_src_coords_attributes (dw_die_ref die, tree decl) |
| { |
| tree decl_name; |
| |
| decl_name = DECL_NAME (decl); |
| if (decl_name != NULL && IDENTIFIER_POINTER (decl_name) != NULL) |
| { |
| add_name_attribute (die, dwarf2_name (decl, 0)); |
| if (! DECL_ARTIFICIAL (decl)) |
| add_src_coords_attributes (die, decl); |
| |
| if ((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) |
| /* APPLE LOCAL begin radar 5636185 */ |
| && (TREE_PUBLIC (decl) || TREE_STATIC (decl) || DECL_EXTERNAL (decl)) |
| && assembler_name_exists_and_is_different (decl) |
| /* APPLE LOCAL end radar 5636185 */ |
| && !DECL_ABSTRACT (decl) |
| && !(TREE_CODE (decl) == VAR_DECL && DECL_REGISTER (decl))) |
| add_AT_string (die, DW_AT_MIPS_linkage_name, |
| IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); |
| } |
| |
| #ifdef VMS_DEBUGGING_INFO |
| /* Get the function's name, as described by its RTL. This may be different |
| from the DECL_NAME name used in the source file. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl)) |
| { |
| add_AT_addr (die, DW_AT_VMS_rtnbeg_pd_address, |
| XEXP (DECL_RTL (decl), 0)); |
| VEC_safe_push (tree, gc, used_rtx_array, XEXP (DECL_RTL (decl), 0)); |
| } |
| #endif |
| } |
| |
| /* Push a new declaration scope. */ |
| |
| static void |
| push_decl_scope (tree scope) |
| { |
| VEC_safe_push (tree, gc, decl_scope_table, scope); |
| } |
| |
| /* Pop a declaration scope. */ |
| |
| static inline void |
| pop_decl_scope (void) |
| { |
| VEC_pop (tree, decl_scope_table); |
| } |
| |
| /* Return the DIE for the scope that immediately contains this type. |
| Non-named types get global scope. Named types nested in other |
| types get their containing scope if it's open, or global scope |
| otherwise. All other types (i.e. function-local named types) get |
| the current active scope. */ |
| |
| static dw_die_ref |
| scope_die_for (tree t, dw_die_ref context_die) |
| { |
| dw_die_ref scope_die = NULL; |
| tree containing_scope; |
| int i; |
| |
| /* Non-types always go in the current scope. */ |
| gcc_assert (TYPE_P (t)); |
| |
| containing_scope = TYPE_CONTEXT (t); |
| |
| /* Use the containing namespace if it was passed in (for a declaration). */ |
| if (containing_scope && TREE_CODE (containing_scope) == NAMESPACE_DECL) |
| { |
| if (context_die == lookup_decl_die (containing_scope)) |
| /* OK */; |
| else |
| containing_scope = NULL_TREE; |
| } |
| |
| /* Ignore function type "scopes" from the C frontend. They mean that |
| a tagged type is local to a parmlist of a function declarator, but |
| that isn't useful to DWARF. */ |
| if (containing_scope && TREE_CODE (containing_scope) == FUNCTION_TYPE) |
| containing_scope = NULL_TREE; |
| |
| if (containing_scope == NULL_TREE) |
| scope_die = comp_unit_die; |
| else if (TYPE_P (containing_scope)) |
| { |
| /* For types, we can just look up the appropriate DIE. But |
| first we check to see if we're in the middle of emitting it |
| so we know where the new DIE should go. */ |
| for (i = VEC_length (tree, decl_scope_table) - 1; i >= 0; --i) |
| if (VEC_index (tree, decl_scope_table, i) == containing_scope) |
| break; |
| |
| if (i < 0) |
| { |
| gcc_assert (debug_info_level <= DINFO_LEVEL_TERSE |
| || TREE_ASM_WRITTEN (containing_scope)); |
| |
| /* If none of the current dies are suitable, we get file scope. */ |
| scope_die = comp_unit_die; |
| } |
| else |
| scope_die = lookup_type_die (containing_scope); |
| } |
| else |
| scope_die = context_die; |
| |
| return scope_die; |
| } |
| |
| /* Returns nonzero if CONTEXT_DIE is internal to a function. */ |
| |
| static inline int |
| local_scope_p (dw_die_ref context_die) |
| { |
| for (; context_die; context_die = context_die->die_parent) |
| if (context_die->die_tag == DW_TAG_inlined_subroutine |
| || context_die->die_tag == DW_TAG_subprogram) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Returns nonzero if CONTEXT_DIE is a class or namespace, for deciding |
| whether or not to treat a DIE in this context as a declaration. */ |
| |
| static inline int |
| class_or_namespace_scope_p (dw_die_ref context_die) |
| { |
| return (context_die |
| && (context_die->die_tag == DW_TAG_structure_type |
| || context_die->die_tag == DW_TAG_union_type |
| || context_die->die_tag == DW_TAG_namespace)); |
| } |
| |
| /* Many forms of DIEs require a "type description" attribute. This |
| routine locates the proper "type descriptor" die for the type given |
| by 'type', and adds a DW_AT_type attribute below the given die. */ |
| |
| static void |
| add_type_attribute (dw_die_ref object_die, tree type, int decl_const, |
| int decl_volatile, dw_die_ref context_die) |
| { |
| enum tree_code code = TREE_CODE (type); |
| dw_die_ref type_die = NULL; |
| |
| /* APPLE LOCAL begin radar 5847213 */ |
| /* APPLE LOCAL begin radar 5811943 - Fix type of pointers to blocks */ |
| /* APPLE LOCAL - radar 6113240 */ |
| /* APPLE LOCAL begin radar 6300081 */ |
| if (code == BLOCK_POINTER_TYPE && generic_block_literal_struct_type) |
| { |
| type = build_pointer_type (generic_block_literal_struct_type); |
| code = TREE_CODE (type); |
| } |
| /* APPLE LOCAL end radar 6300081 */ |
| /* APPLE LOCAL end radar 5811943 - Fix type of pointers to Blocks */ |
| /* APPLE LOCAL end radar 5847213 */ |
| |
| /* ??? If this type is an unnamed subrange type of an integral or |
| floating-point type, use the inner type. This is because we have no |
| support for unnamed types in base_type_die. This can happen if this is |
| an Ada subrange type. Correct solution is emit a subrange type die. */ |
| if ((code == INTEGER_TYPE || code == REAL_TYPE) |
| && TREE_TYPE (type) != 0 && TYPE_NAME (type) == 0) |
| type = TREE_TYPE (type), code = TREE_CODE (type); |
| |
| if (code == ERROR_MARK |
| /* Handle a special case. For functions whose return type is void, we |
| generate *no* type attribute. (Note that no object may have type |
| `void', so this only applies to function return types). */ |
| || code == VOID_TYPE) |
| return; |
| |
| type_die = modified_type_die (type, |
| decl_const || TYPE_READONLY (type), |
| decl_volatile || TYPE_VOLATILE (type), |
| context_die); |
| |
| if (type_die != NULL) |
| add_AT_die_ref (object_die, DW_AT_type, type_die); |
| } |
| |
| /* Given an object die, add the calling convention attribute for the |
| function call type. */ |
| static void |
| add_calling_convention_attribute (dw_die_ref subr_die, tree type) |
| { |
| enum dwarf_calling_convention value = DW_CC_normal; |
| |
| value = targetm.dwarf_calling_convention (type); |
| |
| /* Only add the attribute if the backend requests it, and |
| is not DW_CC_normal. */ |
| if (value && (value != DW_CC_normal)) |
| add_AT_unsigned (subr_die, DW_AT_calling_convention, value); |
| } |
| |
| /* Given a tree pointer to a struct, class, union, or enum type node, return |
| a pointer to the (string) tag name for the given type, or zero if the type |
| was declared without a tag. */ |
| |
| static const char * |
| type_tag (tree type) |
| { |
| const char *name = 0; |
| |
| if (TYPE_NAME (type) != 0) |
| { |
| tree t = 0; |
| |
| /* Find the IDENTIFIER_NODE for the type name. */ |
| if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) |
| t = TYPE_NAME (type); |
| |
| /* The g++ front end makes the TYPE_NAME of *each* tagged type point to |
| a TYPE_DECL node, regardless of whether or not a `typedef' was |
| involved. */ |
| else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL |
| && ! DECL_IGNORED_P (TYPE_NAME (type))) |
| t = DECL_NAME (TYPE_NAME (type)); |
| |
| /* Now get the name as a string, or invent one. */ |
| if (t != 0) |
| name = IDENTIFIER_POINTER (t); |
| } |
| |
| return (name == 0 || *name == '\0') ? 0 : name; |
| } |
| |
| /* Return the type associated with a data member, make a special check |
| for bit field types. */ |
| |
| static inline tree |
| member_declared_type (tree member) |
| { |
| return (DECL_BIT_FIELD_TYPE (member) |
| ? DECL_BIT_FIELD_TYPE (member) : TREE_TYPE (member)); |
| } |
| |
| /* Get the decl's label, as described by its RTL. This may be different |
| from the DECL_NAME name used in the source file. */ |
| |
| #if 0 |
| static const char * |
| decl_start_label (tree decl) |
| { |
| rtx x; |
| const char *fnname; |
| |
| x = DECL_RTL (decl); |
| gcc_assert (MEM_P (x)); |
| |
| x = XEXP (x, 0); |
| gcc_assert (GET_CODE (x) == SYMBOL_REF); |
| |
| fnname = XSTR (x, 0); |
| return fnname; |
| } |
| #endif |
| |
| /* These routines generate the internal representation of the DIE's for |
| the compilation unit. Debugging information is collected by walking |
| the declaration trees passed in from dwarf2out_decl(). */ |
| |
| static void |
| gen_array_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref scope_die = scope_die_for (type, context_die); |
| dw_die_ref array_die; |
| tree element_type; |
| |
| /* ??? The SGI dwarf reader fails for array of array of enum types unless |
| the inner array type comes before the outer array type. Thus we must |
| call gen_type_die before we call new_die. See below also. */ |
| #ifdef MIPS_DEBUGGING_INFO |
| gen_type_die (TREE_TYPE (type), context_die); |
| #endif |
| |
| array_die = new_die (DW_TAG_array_type, scope_die, type); |
| add_name_attribute (array_die, type_tag (type)); |
| equate_type_number_to_die (type, array_die); |
| |
| if (TREE_CODE (type) == VECTOR_TYPE) |
| { |
| /* The frontend feeds us a representation for the vector as a struct |
| containing an array. Pull out the array type. */ |
| type = TREE_TYPE (TYPE_FIELDS (TYPE_DEBUG_REPRESENTATION_TYPE (type))); |
| add_AT_flag (array_die, DW_AT_GNU_vector, 1); |
| } |
| |
| #if 0 |
| /* We default the array ordering. SDB will probably do |
| the right things even if DW_AT_ordering is not present. It's not even |
| an issue until we start to get into multidimensional arrays anyway. If |
| SDB is ever caught doing the Wrong Thing for multi-dimensional arrays, |
| then we'll have to put the DW_AT_ordering attribute back in. (But if |
| and when we find out that we need to put these in, we will only do so |
| for multidimensional arrays. */ |
| add_AT_unsigned (array_die, DW_AT_ordering, DW_ORD_row_major); |
| #endif |
| |
| #ifdef MIPS_DEBUGGING_INFO |
| /* The SGI compilers handle arrays of unknown bound by setting |
| AT_declaration and not emitting any subrange DIEs. */ |
| if (! TYPE_DOMAIN (type)) |
| add_AT_flag (array_die, DW_AT_declaration, 1); |
| else |
| #endif |
| add_subscript_info (array_die, type); |
| |
| /* Add representation of the type of the elements of this array type. */ |
| element_type = TREE_TYPE (type); |
| |
| /* ??? The SGI dwarf reader fails for multidimensional arrays with a |
| const enum type. E.g. const enum machine_mode insn_operand_mode[2][10]. |
| We work around this by disabling this feature. See also |
| add_subscript_info. */ |
| #ifndef MIPS_DEBUGGING_INFO |
| while (TREE_CODE (element_type) == ARRAY_TYPE) |
| element_type = TREE_TYPE (element_type); |
| |
| gen_type_die (element_type, context_die); |
| #endif |
| |
| add_type_attribute (array_die, element_type, 0, 0, context_die); |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (get_AT (array_die, DW_AT_name)) |
| add_pubtype (type, array_die); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| } |
| |
| #if 0 |
| static void |
| gen_entry_point_die (tree decl, dw_die_ref context_die) |
| { |
| tree origin = decl_ultimate_origin (decl); |
| dw_die_ref decl_die = new_die (DW_TAG_entry_point, context_die, decl); |
| |
| if (origin != NULL) |
| add_abstract_origin_attribute (decl_die, origin); |
| else |
| { |
| add_name_and_src_coords_attributes (decl_die, decl); |
| add_type_attribute (decl_die, TREE_TYPE (TREE_TYPE (decl)), |
| 0, 0, context_die); |
| } |
| |
| if (DECL_ABSTRACT (decl)) |
| equate_decl_number_to_die (decl, decl_die); |
| else |
| add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl)); |
| } |
| #endif |
| |
| /* Walk through the list of incomplete types again, trying once more to |
| emit full debugging info for them. */ |
| |
| static void |
| retry_incomplete_types (void) |
| { |
| int i; |
| |
| for (i = VEC_length (tree, incomplete_types) - 1; i >= 0; i--) |
| gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die); |
| } |
| |
| /* Generate a DIE to represent an inlined instance of an enumeration type. */ |
| |
| static void |
| gen_inlined_enumeration_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref type_die = new_die (DW_TAG_enumeration_type, context_die, type); |
| |
| /* We do not check for TREE_ASM_WRITTEN (type) being set, as the type may |
| be incomplete and such types are not marked. */ |
| add_abstract_origin_attribute (type_die, type); |
| } |
| |
| /* Generate a DIE to represent an inlined instance of a structure type. */ |
| |
| static void |
| gen_inlined_structure_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref type_die = new_die (DW_TAG_structure_type, context_die, type); |
| |
| /* We do not check for TREE_ASM_WRITTEN (type) being set, as the type may |
| be incomplete and such types are not marked. */ |
| add_abstract_origin_attribute (type_die, type); |
| } |
| |
| /* Generate a DIE to represent an inlined instance of a union type. */ |
| |
| static void |
| gen_inlined_union_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref type_die = new_die (DW_TAG_union_type, context_die, type); |
| |
| /* We do not check for TREE_ASM_WRITTEN (type) being set, as the type may |
| be incomplete and such types are not marked. */ |
| add_abstract_origin_attribute (type_die, type); |
| } |
| |
| /* Generate a DIE to represent an enumeration type. Note that these DIEs |
| include all of the information about the enumeration values also. Each |
| enumerated type name/value is listed as a child of the enumerated type |
| DIE. */ |
| |
| static dw_die_ref |
| gen_enumeration_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref type_die = lookup_type_die (type); |
| |
| if (type_die == NULL) |
| { |
| type_die = new_die (DW_TAG_enumeration_type, |
| scope_die_for (type, context_die), type); |
| equate_type_number_to_die (type, type_die); |
| add_name_attribute (type_die, type_tag (type)); |
| } |
| else if (! TYPE_SIZE (type)) |
| return type_die; |
| else |
| remove_AT (type_die, DW_AT_declaration); |
| |
| /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the |
| given enum type is incomplete, do not generate the DW_AT_byte_size |
| attribute or the DW_AT_element_list attribute. */ |
| if (TYPE_SIZE (type)) |
| { |
| tree link; |
| |
| TREE_ASM_WRITTEN (type) = 1; |
| add_byte_size_attribute (type_die, type); |
| if (TYPE_STUB_DECL (type) != NULL_TREE) |
| add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); |
| |
| /* If the first reference to this type was as the return type of an |
| inline function, then it may not have a parent. Fix this now. */ |
| if (type_die->die_parent == NULL) |
| add_child_die (scope_die_for (type, context_die), type_die); |
| |
| for (link = TYPE_VALUES (type); |
| link != NULL; link = TREE_CHAIN (link)) |
| { |
| dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die, link); |
| tree value = TREE_VALUE (link); |
| |
| add_name_attribute (enum_die, |
| IDENTIFIER_POINTER (TREE_PURPOSE (link))); |
| |
| if (host_integerp (value, TYPE_UNSIGNED (TREE_TYPE (value)))) |
| /* DWARF2 does not provide a way of indicating whether or |
| not enumeration constants are signed or unsigned. GDB |
| always assumes the values are signed, so we output all |
| values as if they were signed. That means that |
| enumeration constants with very large unsigned values |
| will appear to have negative values in the debugger. */ |
| add_AT_int (enum_die, DW_AT_const_value, |
| tree_low_cst (value, tree_int_cst_sgn (value) > 0)); |
| } |
| } |
| else |
| add_AT_flag (type_die, DW_AT_declaration, 1); |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (get_AT (type_die, DW_AT_name)) |
| add_pubtype (type, type_die); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| return type_die; |
| } |
| |
| /* Generate a DIE to represent either a real live formal parameter decl or to |
| represent just the type of some formal parameter position in some function |
| type. |
| |
| Note that this routine is a bit unusual because its argument may be a |
| ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which |
| represents an inlining of some PARM_DECL) or else some sort of a ..._TYPE |
| node. If it's the former then this function is being called to output a |
| DIE to represent a formal parameter object (or some inlining thereof). If |
| it's the latter, then this function is only being called to output a |
| DW_TAG_formal_parameter DIE to stand as a placeholder for some formal |
| argument type of some subprogram type. */ |
| |
| static dw_die_ref |
| gen_formal_parameter_die (tree node, dw_die_ref context_die) |
| { |
| dw_die_ref parm_die |
| = new_die (DW_TAG_formal_parameter, context_die, node); |
| tree origin; |
| |
| switch (TREE_CODE_CLASS (TREE_CODE (node))) |
| { |
| case tcc_declaration: |
| origin = decl_ultimate_origin (node); |
| if (origin != NULL) |
| add_abstract_origin_attribute (parm_die, origin); |
| else |
| { |
| add_name_and_src_coords_attributes (parm_die, node); |
| add_type_attribute (parm_die, TREE_TYPE (node), |
| TREE_READONLY (node), |
| TREE_THIS_VOLATILE (node), |
| context_die); |
| if (DECL_ARTIFICIAL (node)) |
| add_AT_flag (parm_die, DW_AT_artificial, 1); |
| } |
| |
| equate_decl_number_to_die (node, parm_die); |
| if (! DECL_ABSTRACT (node)) |
| add_location_or_const_value_attribute (parm_die, node, DW_AT_location); |
| |
| break; |
| |
| case tcc_type: |
| /* We were called with some kind of a ..._TYPE node. */ |
| add_type_attribute (parm_die, node, 0, 0, context_die); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return parm_die; |
| } |
| |
| /* Generate a special type of DIE used as a stand-in for a trailing ellipsis |
| at the end of an (ANSI prototyped) formal parameters list. */ |
| |
| static void |
| gen_unspecified_parameters_die (tree decl_or_type, dw_die_ref context_die) |
| { |
| new_die (DW_TAG_unspecified_parameters, context_die, decl_or_type); |
| } |
| |
| /* Generate a list of nameless DW_TAG_formal_parameter DIEs (and perhaps a |
| DW_TAG_unspecified_parameters DIE) to represent the types of the formal |
| parameters as specified in some function type specification (except for |
| those which appear as part of a function *definition*). */ |
| |
| static void |
| gen_formal_types_die (tree function_or_method_type, dw_die_ref context_die) |
| { |
| tree link; |
| tree formal_type = NULL; |
| tree first_parm_type; |
| tree arg; |
| |
| if (TREE_CODE (function_or_method_type) == FUNCTION_DECL) |
| { |
| arg = DECL_ARGUMENTS (function_or_method_type); |
| function_or_method_type = TREE_TYPE (function_or_method_type); |
| } |
| else |
| arg = NULL_TREE; |
| |
| first_parm_type = TYPE_ARG_TYPES (function_or_method_type); |
| |
| /* Make our first pass over the list of formal parameter types and output a |
| DW_TAG_formal_parameter DIE for each one. */ |
| for (link = first_parm_type; link; ) |
| { |
| dw_die_ref parm_die; |
| |
| formal_type = TREE_VALUE (link); |
| if (formal_type == void_type_node) |
| break; |
| |
| /* Output a (nameless) DIE to represent the formal parameter itself. */ |
| parm_die = gen_formal_parameter_die (formal_type, context_die); |
| if ((TREE_CODE (function_or_method_type) == METHOD_TYPE |
| && link == first_parm_type) |
| || (arg && DECL_ARTIFICIAL (arg))) |
| add_AT_flag (parm_die, DW_AT_artificial, 1); |
| |
| link = TREE_CHAIN (link); |
| if (arg) |
| arg = TREE_CHAIN (arg); |
| } |
| |
| /* If this function type has an ellipsis, add a |
| DW_TAG_unspecified_parameters DIE to the end of the parameter list. */ |
| if (formal_type != void_type_node) |
| gen_unspecified_parameters_die (function_or_method_type, context_die); |
| |
| /* Make our second (and final) pass over the list of formal parameter types |
| and output DIEs to represent those types (as necessary). */ |
| for (link = TYPE_ARG_TYPES (function_or_method_type); |
| link && TREE_VALUE (link); |
| link = TREE_CHAIN (link)) |
| gen_type_die (TREE_VALUE (link), context_die); |
| } |
| |
| /* We want to generate the DIE for TYPE so that we can generate the |
| die for MEMBER, which has been defined; we will need to refer back |
| to the member declaration nested within TYPE. If we're trying to |
| generate minimal debug info for TYPE, processing TYPE won't do the |
| trick; we need to attach the member declaration by hand. */ |
| |
| static void |
| gen_type_die_for_member (tree type, tree member, dw_die_ref context_die) |
| { |
| gen_type_die (type, context_die); |
| |
| /* If we're trying to avoid duplicate debug info, we may not have |
| emitted the member decl for this function. Emit it now. */ |
| if (TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type)) |
| && ! lookup_decl_die (member)) |
| { |
| dw_die_ref type_die; |
| gcc_assert (!decl_ultimate_origin (member)); |
| |
| push_decl_scope (type); |
| type_die = lookup_type_die (type); |
| if (TREE_CODE (member) == FUNCTION_DECL) |
| gen_subprogram_die (member, type_die); |
| else if (TREE_CODE (member) == FIELD_DECL) |
| { |
| /* Ignore the nameless fields that are used to skip bits but handle |
| C++ anonymous unions and structs. */ |
| if (DECL_NAME (member) != NULL_TREE |
| || TREE_CODE (TREE_TYPE (member)) == UNION_TYPE |
| || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE) |
| { |
| gen_type_die (member_declared_type (member), type_die); |
| gen_field_die (member, type_die); |
| } |
| } |
| else |
| gen_variable_die (member, type_die); |
| |
| pop_decl_scope (); |
| } |
| } |
| |
| /* Generate the DWARF2 info for the "abstract" instance of a function which we |
| may later generate inlined and/or out-of-line instances of. */ |
| |
| static void |
| dwarf2out_abstract_function (tree decl) |
| { |
| dw_die_ref old_die; |
| tree save_fn; |
| struct function *save_cfun; |
| tree context; |
| int was_abstract = DECL_ABSTRACT (decl); |
| |
| /* Make sure we have the actual abstract inline, not a clone. */ |
| decl = DECL_ORIGIN (decl); |
| |
| old_die = lookup_decl_die (decl); |
| if (old_die && get_AT (old_die, DW_AT_inline)) |
| /* We've already generated the abstract instance. */ |
| return; |
| |
| /* Be sure we've emitted the in-class declaration DIE (if any) first, so |
| we don't get confused by DECL_ABSTRACT. */ |
| if (debug_info_level > DINFO_LEVEL_TERSE) |
| { |
| context = decl_class_context (decl); |
| if (context) |
| gen_type_die_for_member |
| (context, decl, decl_function_context (decl) ? NULL : comp_unit_die); |
| } |
| |
| /* Pretend we've just finished compiling this function. */ |
| save_fn = current_function_decl; |
| save_cfun = cfun; |
| current_function_decl = decl; |
| cfun = DECL_STRUCT_FUNCTION (decl); |
| |
| set_decl_abstract_flags (decl, 1); |
| dwarf2out_decl (decl); |
| if (! was_abstract) |
| set_decl_abstract_flags (decl, 0); |
| |
| current_function_decl = save_fn; |
| cfun = save_cfun; |
| } |
| |
| /* Helper function of premark_used_types() which gets called through |
| htab_traverse_resize(). |
| |
| Marks the DIE of a given type in *SLOT as perennial, so it never gets |
| marked as unused by prune_unused_types. */ |
| static int |
| premark_used_types_helper (void **slot, void *data ATTRIBUTE_UNUSED) |
| { |
| tree type; |
| dw_die_ref die; |
| |
| type = *slot; |
| die = lookup_type_die (type); |
| if (die != NULL) |
| die->die_perennial_p = 1; |
| return 1; |
| } |
| |
| /* Mark all members of used_types_hash as perennial. */ |
| static void |
| premark_used_types (void) |
| { |
| if (cfun && cfun->used_types_hash) |
| htab_traverse (cfun->used_types_hash, premark_used_types_helper, NULL); |
| } |
| |
| /* Generate a DIE to represent a declared function (either file-scope or |
| block-local). */ |
| |
| static void |
| gen_subprogram_die (tree decl, dw_die_ref context_die) |
| { |
| char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; |
| tree origin = decl_ultimate_origin (decl); |
| dw_die_ref subr_die; |
| tree fn_arg_types; |
| tree outer_scope; |
| dw_die_ref old_die = lookup_decl_die (decl); |
| int declaration = (current_function_decl != decl |
| || class_or_namespace_scope_p (context_die)); |
| |
| premark_used_types (); |
| |
| /* It is possible to have both DECL_ABSTRACT and DECLARATION be true if we |
| started to generate the abstract instance of an inline, decided to output |
| its containing class, and proceeded to emit the declaration of the inline |
| from the member list for the class. If so, DECLARATION takes priority; |
| we'll get back to the abstract instance when done with the class. */ |
| |
| /* The class-scope declaration DIE must be the primary DIE. */ |
| if (origin && declaration && class_or_namespace_scope_p (context_die)) |
| { |
| origin = NULL; |
| gcc_assert (!old_die); |
| } |
| |
| /* Now that the C++ front end lazily declares artificial member fns, we |
| might need to retrofit the declaration into its class. */ |
| if (!declaration && !origin && !old_die |
| && DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)) |
| && !class_or_namespace_scope_p (context_die) |
| && debug_info_level > DINFO_LEVEL_TERSE) |
| old_die = force_decl_die (decl); |
| |
| if (origin != NULL) |
| { |
| gcc_assert (!declaration || local_scope_p (context_die)); |
| |
| /* Fixup die_parent for the abstract instance of a nested |
| inline function. */ |
| if (old_die && old_die->die_parent == NULL) |
| add_child_die (context_die, old_die); |
| |
| subr_die = new_die (DW_TAG_subprogram, context_die, decl); |
| add_abstract_origin_attribute (subr_die, origin); |
| } |
| else if (old_die) |
| { |
| expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl)); |
| struct dwarf_file_data * file_index = lookup_filename (s.file); |
| |
| if (!get_AT_flag (old_die, DW_AT_declaration) |
| /* We can have a normal definition following an inline one in the |
| case of redefinition of GNU C extern inlines. |
| It seems reasonable to use AT_specification in this case. */ |
| && !get_AT (old_die, DW_AT_inline)) |
| { |
| /* Detect and ignore this case, where we are trying to output |
| something we have already output. */ |
| return; |
| } |
| |
| /* If the definition comes from the same place as the declaration, |
| maybe use the old DIE. We always want the DIE for this function |
| that has the *_pc attributes to be under comp_unit_die so the |
| debugger can find it. We also need to do this for abstract |
| instances of inlines, since the spec requires the out-of-line copy |
| to have the same parent. For local class methods, this doesn't |
| apply; we just use the old DIE. */ |
| if ((old_die->die_parent == comp_unit_die || context_die == NULL) |
| && (DECL_ARTIFICIAL (decl) |
| || (get_AT_file (old_die, DW_AT_decl_file) == file_index |
| && (get_AT_unsigned (old_die, DW_AT_decl_line) |
| == (unsigned) s.line)))) |
| { |
| subr_die = old_die; |
| |
| /* Clear out the declaration attribute and the formal parameters. |
| Do not remove all children, because it is possible that this |
| declaration die was forced using force_decl_die(). In such |
| cases die that forced declaration die (e.g. TAG_imported_module) |
| is one of the children that we do not want to remove. */ |
| remove_AT (subr_die, DW_AT_declaration); |
| remove_child_TAG (subr_die, DW_TAG_formal_parameter); |
| } |
| else |
| { |
| subr_die = new_die (DW_TAG_subprogram, context_die, decl); |
| add_AT_specification (subr_die, old_die); |
| if (get_AT_file (old_die, DW_AT_decl_file) != file_index) |
| add_AT_file (subr_die, DW_AT_decl_file, file_index); |
| if (get_AT_unsigned (old_die, DW_AT_decl_line) != (unsigned) s.line) |
| add_AT_unsigned (subr_die, DW_AT_decl_line, s.line); |
| } |
| } |
| else |
| { |
| /* APPLE LOCAL begin radar 6193416 */ |
| if (BLOCK_SYNTHESIZED_FUNC (decl)) |
| subr_die = new_die (DW_TAG_subprogram, comp_unit_die, decl); |
| else |
| subr_die = new_die (DW_TAG_subprogram, context_die, decl); |
| /* APPLE LOCAL end radar 6193416 */ |
| |
| if (TREE_PUBLIC (decl)) |
| add_AT_flag (subr_die, DW_AT_external, 1); |
| |
| add_name_and_src_coords_attributes (subr_die, decl); |
| if (debug_info_level > DINFO_LEVEL_TERSE) |
| { |
| add_prototyped_attribute (subr_die, TREE_TYPE (decl)); |
| add_type_attribute (subr_die, TREE_TYPE (TREE_TYPE (decl)), |
| 0, 0, context_die); |
| } |
| |
| add_pure_or_virtual_attribute (subr_die, decl); |
| if (DECL_ARTIFICIAL (decl)) |
| add_AT_flag (subr_die, DW_AT_artificial, 1); |
| |
| if (TREE_PROTECTED (decl)) |
| add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_protected); |
| else if (TREE_PRIVATE (decl)) |
| add_AT_unsigned (subr_die, DW_AT_accessibility, DW_ACCESS_private); |
| } |
| |
| if (declaration) |
| { |
| if (!old_die || !get_AT (old_die, DW_AT_inline)) |
| { |
| add_AT_flag (subr_die, DW_AT_declaration, 1); |
| |
| /* The first time we see a member function, it is in the context of |
| the class to which it belongs. We make sure of this by emitting |
| the class first. The next time is the definition, which is |
| handled above. The two may come from the same source text. |
| |
| Note that force_decl_die() forces function declaration die. It is |
| later reused to represent definition. */ |
| equate_decl_number_to_die (decl, subr_die); |
| } |
| } |
| else if (DECL_ABSTRACT (decl)) |
| { |
| if (DECL_DECLARED_INLINE_P (decl)) |
| { |
| if (cgraph_function_possibly_inlined_p (decl)) |
| add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_inlined); |
| else |
| add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_declared_not_inlined); |
| } |
| else |
| { |
| if (cgraph_function_possibly_inlined_p (decl)) |
| add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_inlined); |
| else |
| add_AT_unsigned (subr_die, DW_AT_inline, DW_INL_not_inlined); |
| } |
| |
| equate_decl_number_to_die (decl, subr_die); |
| } |
| else if (!DECL_EXTERNAL (decl)) |
| { |
| HOST_WIDE_INT cfa_fb_offset; |
| |
| /* APPLE LOCAL begin dwarf 4444941 */ |
| if (TREE_PUBLIC (decl) && DECL_ASSEMBLER_NAME (decl) != DECL_NAME (decl) |
| && DECL_ABSTRACT_ORIGIN (decl)) |
| add_AT_string (subr_die, DW_AT_MIPS_linkage_name, |
| IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); |
| /* APPLE LOCAL end dwarf 4444941 */ |
| |
| if (!old_die || !get_AT (old_die, DW_AT_inline)) |
| equate_decl_number_to_die (decl, subr_die); |
| |
| if (!flag_reorder_blocks_and_partition) |
| { |
| ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL, |
| current_function_funcdef_no); |
| add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id); |
| ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, |
| current_function_funcdef_no); |
| add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id); |
| |
| add_pubname (decl, subr_die); |
| add_arange (decl, subr_die); |
| } |
| else |
| { /* Do nothing for now; maybe need to duplicate die, one for |
| hot section and ond for cold section, then use the hot/cold |
| section begin/end labels to generate the aranges... */ |
| /* |
| add_AT_lbl_id (subr_die, DW_AT_low_pc, hot_section_label); |
| add_AT_lbl_id (subr_die, DW_AT_high_pc, hot_section_end_label); |
| add_AT_lbl_id (subr_die, DW_AT_lo_user, unlikely_section_label); |
| add_AT_lbl_id (subr_die, DW_AT_hi_user, cold_section_end_label); |
| |
| add_pubname (decl, subr_die); |
| add_arange (decl, subr_die); |
| add_arange (decl, subr_die); |
| */ |
| } |
| |
| #ifdef MIPS_DEBUGGING_INFO |
| /* Add a reference to the FDE for this routine. */ |
| add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, current_funcdef_fde); |
| #endif |
| |
| cfa_fb_offset = CFA_FRAME_BASE_OFFSET (decl); |
| |
| /* We define the "frame base" as the function's CFA. This is more |
| convenient for several reasons: (1) It's stable across the prologue |
| and epilogue, which makes it better than just a frame pointer, |
| (2) With dwarf3, there exists a one-byte encoding that allows us |
| to reference the .debug_frame data by proxy, but failing that, |
| (3) We can at least reuse the code inspection and interpretation |
| code that determines the CFA position at various points in the |
| function. */ |
| /* ??? Use some command-line or configury switch to enable the use |
| of dwarf3 DW_OP_call_frame_cfa. At present there are no dwarf |
| consumers that understand it; fall back to "pure" dwarf2 and |
| convert the CFA data into a location list. */ |
| { |
| dw_loc_list_ref list = convert_cfa_to_fb_loc_list (cfa_fb_offset); |
| if (list->dw_loc_next) |
| add_AT_loc_list (subr_die, DW_AT_frame_base, list); |
| else |
| add_AT_loc (subr_die, DW_AT_frame_base, list->expr); |
| } |
| |
| /* Compute a displacement from the "steady-state frame pointer" to |
| the CFA. The former is what all stack slots and argument slots |
| will reference in the rtl; the later is what we've told the |
| debugger about. We'll need to adjust all frame_base references |
| by this displacement. */ |
| compute_frame_pointer_to_fb_displacement (cfa_fb_offset); |
| |
| if (cfun->static_chain_decl) |
| add_AT_location_description (subr_die, DW_AT_static_link, |
| loc_descriptor_from_tree (cfun->static_chain_decl)); |
| } |
| |
| /* Now output descriptions of the arguments for this function. This gets |
| (unnecessarily?) complex because of the fact that the DECL_ARGUMENT list |
| for a FUNCTION_DECL doesn't indicate cases where there was a trailing |
| `...' at the end of the formal parameter list. In order to find out if |
| there was a trailing ellipsis or not, we must instead look at the type |
| associated with the FUNCTION_DECL. This will be a node of type |
| FUNCTION_TYPE. If the chain of type nodes hanging off of this |
| FUNCTION_TYPE node ends with a void_type_node then there should *not* be |
| an ellipsis at the end. */ |
| |
| /* In the case where we are describing a mere function declaration, all we |
| need to do here (and all we *can* do here) is to describe the *types* of |
| its formal parameters. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| ; |
| else if (declaration) |
| gen_formal_types_die (decl, subr_die); |
| else |
| { |
| /* Generate DIEs to represent all known formal parameters. */ |
| tree arg_decls = DECL_ARGUMENTS (decl); |
| tree parm; |
| |
| /* When generating DIEs, generate the unspecified_parameters DIE |
| instead if we come across the arg "__builtin_va_alist" */ |
| for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) |
| if (TREE_CODE (parm) == PARM_DECL) |
| { |
| if (DECL_NAME (parm) |
| && !strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)), |
| "__builtin_va_alist")) |
| gen_unspecified_parameters_die (parm, subr_die); |
| else |
| gen_decl_die (parm, subr_die); |
| } |
| |
| /* Decide whether we need an unspecified_parameters DIE at the end. |
| There are 2 more cases to do this for: 1) the ansi ... declaration - |
| this is detectable when the end of the arg list is not a |
| void_type_node 2) an unprototyped function declaration (not a |
| definition). This just means that we have no info about the |
| parameters at all. */ |
| fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); |
| if (fn_arg_types != NULL) |
| { |
| /* This is the prototyped case, check for.... */ |
| if (TREE_VALUE (tree_last (fn_arg_types)) != void_type_node) |
| gen_unspecified_parameters_die (decl, subr_die); |
| } |
| else if (DECL_INITIAL (decl) == NULL_TREE) |
| gen_unspecified_parameters_die (decl, subr_die); |
| } |
| |
| /* Output Dwarf info for all of the stuff within the body of the function |
| (if it has one - it may be just a declaration). */ |
| outer_scope = DECL_INITIAL (decl); |
| |
| /* OUTER_SCOPE is a pointer to the outermost BLOCK node created to represent |
| a function. This BLOCK actually represents the outermost binding contour |
| for the function, i.e. the contour in which the function's formal |
| parameters and labels get declared. Curiously, it appears that the front |
| end doesn't actually put the PARM_DECL nodes for the current function onto |
| the BLOCK_VARS list for this outer scope, but are strung off of the |
| DECL_ARGUMENTS list for the function instead. |
| |
| The BLOCK_VARS list for the `outer_scope' does provide us with a list of |
| the LABEL_DECL nodes for the function however, and we output DWARF info |
| for those in decls_for_scope. Just within the `outer_scope' there will be |
| a BLOCK node representing the function's outermost pair of curly braces, |
| and any blocks used for the base and member initializers of a C++ |
| constructor function. */ |
| if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) |
| { |
| /* Emit a DW_TAG_variable DIE for a named return value. */ |
| if (DECL_NAME (DECL_RESULT (decl))) |
| gen_decl_die (DECL_RESULT (decl), subr_die); |
| |
| current_function_has_inlines = 0; |
| decls_for_scope (outer_scope, subr_die, 0); |
| |
| #if 0 && defined (MIPS_DEBUGGING_INFO) |
| if (current_function_has_inlines) |
| { |
| add_AT_flag (subr_die, DW_AT_MIPS_has_inlines, 1); |
| if (! comp_unit_has_inlines) |
| { |
| add_AT_flag (comp_unit_die, DW_AT_MIPS_has_inlines, 1); |
| comp_unit_has_inlines = 1; |
| } |
| } |
| #endif |
| } |
| /* Add the calling convention attribute if requested. */ |
| add_calling_convention_attribute (subr_die, TREE_TYPE (decl)); |
| |
| /* APPLE LOCAL begin radar 2338865 optimization notification */ |
| if (optimize > 0) |
| add_AT_flag (subr_die, DW_AT_APPLE_optimized, 1); |
| /* APPLE LOCAL end radar 2338865 optimization notification */ |
| /* APPLE LOCAL begin differentiate between arm & thumb. */ |
| #ifdef TARGET_ARM |
| if (TARGET_THUMB) |
| add_AT_int (subr_die, DW_AT_APPLE_isa, DW_ISA_ARM_thumb); |
| else if (TARGET_ARM) |
| add_AT_int (subr_die, DW_AT_APPLE_isa, DW_ISA_ARM_arm); |
| #endif |
| /* APPLE LOCAL end differentiate between arm & thumb. */ |
| /* APPLE LOCAL confused diff */ |
| } |
| /* APPLE LOCAL begin radar 6048397 handle block byref variables */ |
| |
| /* Byref variables, in blocks, are declared by the programmer as |
| "SomeType VarName;", but the compiler creates a |
| __Block_byref_x_VarName struct, and gives the variable VarName |
| either the struct, or a pointer to the struct, as its type. This |
| is necessary for various behind-the-scenes things the compiler |
| needs to do with by-reference variables in blocks. |
| |
| However, as far as the original *programmer* is concerned, the |
| variable should still have type 'SomeType', as originally declared. |
| |
| The following function dives into the __Block_byref_x_VarName |
| struct to find the original type of the variable. This will be |
| passed back to the code generating the type for the Debug |
| Information Entry for the variable 'VarName'. 'VarName' will then |
| have the original type 'SomeType' in its debug information. |
| |
| The original type 'SomeType' will be the type of the field named |
| 'VarName' inside the __Block_byref_x_VarName struct. |
| |
| NOTE: In order for this to not completely fail on the debugger |
| side, the Debug Information Entry for the variable VarName needs to |
| have a DW_AT_location that tells the debugger how to unwind through |
| the pointers and __Block_byref_x_VarName struct to find the actual |
| value of the variable. The function |
| add_block_byref_var_location_attribute does this. */ |
| |
| static tree |
| find_block_byref_var_real_type (tree decl) |
| { |
| tree block_struct = TREE_TYPE (decl); |
| const char *var_name ; |
| tree var_field; |
| bool found = false; |
| tree ret_type = NULL_TREE; |
| |
| |
| if ((! (DECL_NAME (decl))) |
| || (! IDENTIFIER_POINTER (DECL_NAME (decl)))) |
| return ret_type; |
| |
| if (!block_struct) |
| return ret_type; |
| |
| /* Get the name of the variable whose real type we are trying to find. */ |
| |
| var_name = IDENTIFIER_POINTER (DECL_NAME (decl)); |
| |
| /* If the type for the variable was pointer to __Block_byref_etc, then |
| dereference the pointer type to get the structure __Block_byref_etc. */ |
| |
| if (TREE_CODE (block_struct) == POINTER_TYPE) |
| block_struct = TREE_TYPE (block_struct); |
| |
| /* If block_struct is NOT a __Block_byref_etc record, then something is |
| wrong, so we should bail out here. */ |
| |
| if (TREE_CODE (block_struct) != RECORD_TYPE) |
| return ret_type; |
| |
| /* APPLE LOCAL begin radar 6237086 */ |
| /* Look for a type name, and make sure it contains the __Block_byref_ |
| substring. */ |
| |
| if (! TYPE_NAME (block_struct)) |
| return ret_type; |
| |
| if (TREE_CODE (TYPE_NAME (block_struct)) != IDENTIFIER_NODE |
| && TREE_CODE (TYPE_NAME (block_struct)) != TYPE_DECL) |
| return ret_type; |
| |
| if (TREE_CODE (TYPE_NAME (block_struct)) == IDENTIFIER_NODE |
| && (strncmp (IDENTIFIER_POINTER (TYPE_NAME (block_struct)), |
| "__Block_byref_", 14) != 0)) |
| return ret_type; |
| else if (TREE_CODE (TYPE_NAME (block_struct)) == TYPE_DECL |
| && ((! DECL_NAME (TYPE_NAME (block_struct))) |
| || (strncmp |
| (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (block_struct))), |
| "__Block_byref_", 14) != 0))) |
| return ret_type; |
| /* APPLE LOCAL end radar 6237086 */ |
| |
| /* We've got the record for a __Byref_block_etc; go through the fields |
| looking for one with the same name as the var_decl. */ |
| |
| var_field = TYPE_FIELDS (block_struct); |
| |
| while (var_field && !found) |
| { |
| if (TREE_CODE (var_field) != FIELD_DECL) |
| return ret_type; |
| if (DECL_NAME (var_field) |
| && IDENTIFIER_POINTER (DECL_NAME (var_field)) |
| && strcmp (IDENTIFIER_POINTER (DECL_NAME (var_field)), var_name) == 0) |
| { |
| /* We've found the right field. Return its type. */ |
| ret_type = TREE_TYPE (var_field); |
| found = 1; |
| } |
| else |
| var_field = TREE_CHAIN (var_field); |
| } |
| |
| return ret_type; |
| } |
| |
| /* APPLE LOCAL begin radar 6237616 */ |
| /* Called from build_byref_var_location_expression, this function |
| checks to see if the starting location description is of type |
| DW_OP_regX (which, legally, cannot have anything added to it), |
| and if so, changes it to the equivalent DW_OP_bregX + 0, which |
| CAN legally have other stuff added to it. */ |
| |
| static void |
| blocks_fixup_location_description (dw_loc_descr_ref *main_descr) |
| { |
| dw_loc_descr_ref old_descr; |
| dw_loc_descr_ref new_descr; |
| enum dwarf_location_atom new_op; |
| int regno; |
| unsigned int offset = 0; |
| |
| old_descr = *main_descr; |
| |
| if (old_descr->dw_loc_opc >= DW_OP_reg0 |
| && old_descr->dw_loc_opc <= DW_OP_reg31) |
| { |
| regno = old_descr->dw_loc_opc - DW_OP_reg0; |
| new_op = DW_OP_breg0 + regno; |
| new_descr = new_loc_descr (new_op, offset, 0); |
| *main_descr = new_descr; |
| } |
| } |
| /* APPLE LOCAL end radar 6237616 */ |
| |
| /* This function is a helper function for the function below, |
| add_block_byref_var_location_attribute. See comments there |
| for full description. */ |
| |
| static void |
| build_byref_var_location_expression (dw_loc_descr_ref *main_descr, |
| bool is_pointer, |
| int forwarding_field_offset, |
| int var_field_offset) |
| { |
| dw_loc_descr_ref temp_descr; |
| |
| /* APPLE LOCAL begin radar 6237616 */ |
| /* Make sure main_descr is legal, to start with. */ |
| |
| blocks_fixup_location_description (main_descr); |
| /* APPLE LOCAL end radar 6237616 */ |
| |
| /* If we started with a pointer to the __Block_byref... struct, then |
| the first thing we need to do is dereference the pointer |
| (DW_OP_deref). */ |
| |
| if (is_pointer) |
| { |
| temp_descr = new_loc_descr (DW_OP_deref, 0, 0); |
| add_loc_descr (main_descr, temp_descr); |
| } |
| |
| /* Next add the offset for the 'forwarding' field: |
| DW_OP_plus_uconst forwarding_field_offset |
| Note, there's no point in adding it if the offset is 0. */ |
| |
| if (forwarding_field_offset != 0) |
| { |
| temp_descr = new_loc_descr (DW_OP_plus_uconst, forwarding_field_offset, |
| 0); |
| add_loc_descr (main_descr, temp_descr); |
| } |
| |
| /* Follow that pointer to find the *real* __Block_byref struct: |
| DW_OP_deref */ |
| |
| temp_descr = new_loc_descr (DW_OP_deref, 0, 0); |
| add_loc_descr (main_descr, temp_descr); |
| |
| /* Now we've got the real __Block_byref struct, add the offset for |
| the variable's field to get the location of the actual variable: |
| DW_OP_plus_uconst var_field_offset |
| Again, there's no point in adding the offset if it is 0. */ |
| |
| if (var_field_offset != 0) |
| { |
| temp_descr = new_loc_descr (DW_OP_plus_uconst, var_field_offset, 0); |
| add_loc_descr (main_descr, temp_descr); |
| } |
| } |
| |
| /* Byref variables, in blocks, are declared by the programmer as |
| "SomeType VarName;", but the compiler creates a |
| __Block_byref_x_VarName struct, and gives the variable VarName |
| either the struct, or a pointer to the struct, as its type. This |
| is necessary for various behind-the-scenes things the compiler |
| needs to do with by-reference variables in blocks. |
| |
| However, as far as the original *programmer* is concerned, the |
| variable should still have type 'SomeType', as originally declared. |
| |
| The function find_block_byref_var_real_type dives into the |
| __Block_byref_x_VarName struct to find the original type of the |
| variable, which is then assigned to the variable's Debug |
| Information Entry as its real type. So far, so good. However now |
| the debugger will expect the variable VarName to have the type |
| SomeType. So we need the location attribute for the variable to be |
| an expression that explains to the debugger how to navigate through |
| the pointers and struct to find the actual variable of type |
| SomeType. |
| |
| The following function does just that. We start by getting |
| the "normal" location for the variable. This will be the location |
| of either the struct __Block_byref_x_VarName or the pointer to the |
| struct __Block_byref_x_VarName. |
| |
| The struct will look something like: |
| |
| struct __Block_byref_x_VarName { |
| struct __Block_byref_x_VarName *forwarding; |
| ... <various irrelevant fields> |
| SomeType VarName; |
| }; |
| |
| If we are given the struct directly (as our starting point) we |
| need to tell the debugger to: |
| |
| 1). Add the offset of the forwarding field (do NOT assume the field |
| will always be as position 0). |
| |
| 2). Follow that pointer to get the the real __Block_byref_x_VarName |
| struct to use (the real one may have been copied onto the heap). |
| |
| 3). Add the offset for the field VarName, to find the actual variable. |
| |
| If we started with a pointer to the struct, then we need to |
| derefernce (follow) that pointer first, before the other steps. |
| Translating this into DWARF ops, we will need to append the following |
| to the current location description for the variable: |
| |
| DW_OP_deref -- optional, if we start with a pointer |
| DW_OP_plus_uconst <forward_fld_offset> |
| DW_OP_deref |
| DW_OP_plus_uconst <varName_fld_offset> |
| |
| This function returns a boolean indicating whether or not it was |
| able to successfully create and add the location description. */ |
| |
| static bool |
| add_block_byref_var_location_attribute (dw_die_ref var_die, tree decl) |
| { |
| tree block_struct = TREE_TYPE (decl); |
| const char *var_name = IDENTIFIER_POINTER (DECL_NAME (decl)); |
| tree var_field = NULL; |
| tree forwarding_field = NULL; |
| tree temp_field; |
| unsigned int forwarding_field_offset = 0; |
| unsigned int var_field_offset = 0; |
| bool is_pointer = false; |
| dw_loc_descr_ref descr; |
| var_loc_list *loc_list; |
| |
| if (!block_struct) |
| return false; |
| |
| if ((! DECL_NAME (decl)) |
| || (! IDENTIFIER_POINTER (DECL_NAME (decl)))) |
| return false; |
| |
| /* Get the name of the variable whose location we are decoding. */ |
| |
| var_name = IDENTIFIER_POINTER (DECL_NAME (decl)); |
| |
| if (TREE_CODE (block_struct) == POINTER_TYPE) |
| block_struct = TREE_TYPE (block_struct); |
| |
| /* Verify that we have a record that is a __Block_byref_... */ |
| |
| if (TREE_CODE (block_struct) != RECORD_TYPE) |
| return false; |
| |
| /* APPLE LOCAL begin radar 6237086 */ |
| /* Look for a type name, and make sure it contains the __Block_byref_ |
| substring. */ |
| |
| if (! TYPE_NAME (block_struct)) |
| return false; |
| |
| if (TREE_CODE (TYPE_NAME (block_struct)) != IDENTIFIER_NODE |
| && TREE_CODE (TYPE_NAME (block_struct)) != TYPE_DECL) |
| return false; |
| |
| if (TREE_CODE (TYPE_NAME (block_struct)) == IDENTIFIER_NODE |
| && (strncmp (IDENTIFIER_POINTER (TYPE_NAME (block_struct)), |
| "__Block_byref_", 14) != 0)) |
| return false; |
| else if (TREE_CODE (TYPE_NAME (block_struct)) == TYPE_DECL |
| && ((! DECL_NAME (TYPE_NAME (block_struct))) |
| || (strncmp |
| (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (block_struct))), |
| "__Block_byref_", 14) != 0))) |
| return false; |
| /* APPLE LOCAL end radar 6237086 */ |
| |
| /* Find the forwarding field and the variable field within |
| the struct. */ |
| |
| temp_field = TYPE_FIELDS (block_struct); |
| |
| while (temp_field |
| && (!var_field || !forwarding_field)) |
| { |
| if (TREE_CODE (temp_field) != FIELD_DECL) |
| return false; |
| if (DECL_NAME (temp_field) |
| && IDENTIFIER_POINTER (DECL_NAME (temp_field))) |
| { |
| if (strcmp (IDENTIFIER_POINTER (DECL_NAME (temp_field)), |
| var_name) == 0) |
| var_field = temp_field; |
| else if (strcmp (IDENTIFIER_POINTER (DECL_NAME (temp_field)), |
| "__forwarding") == 0) |
| forwarding_field = temp_field; |
| } |
| |
| temp_field = TREE_CHAIN (temp_field); |
| } |
| |
| /* If we didn't find both fields, we can't continue. */ |
| |
| if (!var_field || !forwarding_field) |
| return false; |
| |
| /* Get the offsets of the fields within the struct. */ |
| |
| if (var_field) |
| var_field_offset = field_byte_offset (var_field); |
| if (forwarding_field) |
| forwarding_field_offset = field_byte_offset (forwarding_field); |
| |
| /* Check to see if we start with a pointer we need to dereference, |
| or not. */ |
| |
| if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE) |
| is_pointer = true; |
| |
| /* See if we possible have multiple locations for this variable. */ |
| loc_list = lookup_decl_loc (decl); |
| |
| /* If it truly has multiple locations, the first and last node will |
| differ. */ |
| if (loc_list && loc_list->first != loc_list->last) |
| { |
| struct var_loc_node *node; |
| rtx varloc; |
| dw_loc_list_ref list; |
| const char *endname, *secname; |
| |
| /* Build the first entry for the location list. */ |
| |
| node = loc_list->first; |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| secname = secname_for_decl (decl); |
| |
| descr = loc_descriptor (varloc, STATUS_INITIALIZED); |
| build_byref_var_location_expression (&descr, is_pointer, |
| forwarding_field_offset, |
| var_field_offset); |
| list = new_loc_list (descr, node->label, node->next->label, secname, 1); |
| node = node->next; |
| |
| for (; node->next; node = node->next) |
| |
| /* Build the other entries for the location list, except the last. */ |
| |
| if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX) |
| { |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| descr = loc_descriptor (varloc, STATUS_INITIALIZED); |
| build_byref_var_location_expression (&descr, is_pointer, |
| forwarding_field_offset, |
| var_field_offset); |
| add_loc_descr_to_loc_list (&list, descr, node->label, |
| node->next->label, secname); |
| } |
| if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX) |
| { |
| char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| /* Build the last entry for the location list. */ |
| |
| varloc = NOTE_VAR_LOCATION (node->var_loc_note); |
| if (!current_function_decl) |
| endname = text_end_label; |
| else |
| { |
| ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, |
| current_function_funcdef_no); |
| endname = ggc_strdup (label_id); |
| } |
| descr = loc_descriptor (varloc, STATUS_INITIALIZED); |
| build_byref_var_location_expression (&descr, is_pointer, |
| forwarding_field_offset, |
| var_field_offset); |
| add_loc_descr_to_loc_list (&list, descr, node->label, endname, |
| secname); |
| } |
| add_AT_loc_list (var_die, DW_AT_location, list); |
| } |
| else |
| { |
| /* We are not dealing with a location list so... */ |
| |
| /* 'descr' starts with the base location of the __Block_byref_... struct, |
| or the pointer to the __Block_byref_... struct. */ |
| |
| descr = loc_descriptor_from_tree (decl); |
| if (!descr) |
| return false; |
| |
| build_byref_var_location_expression (&descr, is_pointer, |
| forwarding_field_offset, |
| var_field_offset); |
| |
| /* Finally, now that we've built up the location description to find the |
| actual value of the variable, add the location description to the |
| variable's die. */ |
| |
| add_AT_location_description (var_die, DW_AT_location, descr); |
| } |
| |
| return true; |
| } |
| /* APPLE LOCAL end radar 6048397 handle block byref variables */ |
| |
| /* Generate a DIE to represent a declared data object. */ |
| |
| static void |
| gen_variable_die (tree decl, dw_die_ref context_die) |
| { |
| /* APPLE LOCAL begin radar 6048397 handle block byref variables */ |
| bool is_block_byref_var = false; |
| tree decl_type = TREE_TYPE (decl); |
| /* APPLE LOCAL end radar 6048397 handle block byref variables */ |
| tree origin = decl_ultimate_origin (decl); |
| dw_die_ref var_die = new_die (DW_TAG_variable, context_die, decl); |
| |
| dw_die_ref old_die = lookup_decl_die (decl); |
| int declaration = (DECL_EXTERNAL (decl) |
| /* If DECL is COMDAT and has not actually been |
| emitted, we cannot take its address; there |
| might end up being no definition anywhere in |
| the program. For example, consider the C++ |
| test case: |
| |
| template <class T> |
| struct S { static const int i = 7; }; |
| |
| template <class T> |
| const int S<T>::i; |
| |
| int f() { return S<int>::i; } |
| |
| Here, S<int>::i is not DECL_EXTERNAL, but no |
| definition is required, so the compiler will |
| not emit a definition. */ |
| || (TREE_CODE (decl) == VAR_DECL |
| && DECL_COMDAT (decl) && !TREE_ASM_WRITTEN (decl)) |
| || class_or_namespace_scope_p (context_die)); |
| |
| /* APPLE LOCAL begin radar 6048397 handle block byref variables */ |
| /* Check to see if this variable is for a block passed-by-refernce |
| variable, in which case we need to do some special stuff for its |
| type and location. */ |
| if (decl_type) |
| { |
| if (TREE_CODE (decl_type) == POINTER_TYPE) |
| decl_type = TREE_TYPE (decl_type); |
| /* APPLE LOCAL begin radar 6237086 */ |
| if (decl_type |
| && TREE_CODE (decl_type) == RECORD_TYPE |
| && TYPE_NAME (decl_type)) |
| { |
| if (TREE_CODE (TYPE_NAME (decl_type)) == IDENTIFIER_NODE |
| && strncmp (IDENTIFIER_POINTER (TYPE_NAME (decl_type)), |
| "__Block_byref_", 14) == 0) |
| is_block_byref_var = true; |
| else if (TREE_CODE (TYPE_NAME (decl_type)) == TYPE_DECL |
| && DECL_NAME (TYPE_NAME (decl_type)) |
| && IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl_type))) |
| && (strncmp |
| (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl_type))), |
| "__Block_byref_", 14) == 0)) |
| is_block_byref_var = true; |
| else |
| is_block_byref_var = false; |
| } |
| /* APPLE LOCAL end radar 6237086 */ |
| } |
| /* APPLE LOCAL end radar 6048397 handle block byref variables */ |
| if (origin != NULL) |
| add_abstract_origin_attribute (var_die, origin); |
| |
| /* Loop unrolling can create multiple blocks that refer to the same |
| static variable, so we must test for the DW_AT_declaration flag. |
| |
| ??? Loop unrolling/reorder_blocks should perhaps be rewritten to |
| copy decls and set the DECL_ABSTRACT flag on them instead of |
| sharing them. |
| |
| ??? Duplicated blocks have been rewritten to use .debug_ranges. |
| |
| ??? The declare_in_namespace support causes us to get two DIEs for one |
| variable, both of which are declarations. We want to avoid considering |
| one to be a specification, so we must test that this DIE is not a |
| declaration. */ |
| else if (old_die && TREE_STATIC (decl) && ! declaration |
| && get_AT_flag (old_die, DW_AT_declaration) == 1) |
| { |
| /* This is a definition of a C++ class level static. */ |
| add_AT_specification (var_die, old_die); |
| if (DECL_NAME (decl)) |
| { |
| expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl)); |
| struct dwarf_file_data * file_index = lookup_filename (s.file); |
| |
| if (get_AT_file (old_die, DW_AT_decl_file) != file_index) |
| add_AT_file (var_die, DW_AT_decl_file, file_index); |
| |
| if (get_AT_unsigned (old_die, DW_AT_decl_line) != (unsigned) s.line) |
| |
| add_AT_unsigned (var_die, DW_AT_decl_line, s.line); |
| } |
| } |
| else |
| { |
| add_name_and_src_coords_attributes (var_die, decl); |
| /* APPLE LOCAL begin radar 6048397 handle block byref variables */ |
| if (is_block_byref_var) |
| { |
| /* Try to find the real type for the variable; if we can't find |
| it, fall back on the old behavior. */ |
| tree real_type = NULL_TREE; |
| real_type = find_block_byref_var_real_type (decl); |
| if (real_type) |
| add_type_attribute (var_die, real_type, TREE_READONLY (decl), |
| TREE_THIS_VOLATILE (decl), context_die); |
| else |
| add_type_attribute (var_die, TREE_TYPE (decl), TREE_READONLY (decl), |
| TREE_THIS_VOLATILE (decl), context_die); |
| } |
| else |
| add_type_attribute (var_die, TREE_TYPE (decl), TREE_READONLY (decl), |
| TREE_THIS_VOLATILE (decl), context_die); |
| /* APPLE LOCAL end radar 6048397 handle block byref variables */ |
| |
| if (TREE_PUBLIC (decl)) |
| add_AT_flag (var_die, DW_AT_external, 1); |
| |
| if (DECL_ARTIFICIAL (decl)) |
| add_AT_flag (var_die, DW_AT_artificial, 1); |
| |
| if (TREE_PROTECTED (decl)) |
| add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_protected); |
| else if (TREE_PRIVATE (decl)) |
| add_AT_unsigned (var_die, DW_AT_accessibility, DW_ACCESS_private); |
| } |
| |
| if (declaration) |
| add_AT_flag (var_die, DW_AT_declaration, 1); |
| |
| if (DECL_ABSTRACT (decl) || declaration) |
| equate_decl_number_to_die (decl, var_die); |
| |
| if (! declaration && ! DECL_ABSTRACT (decl)) |
| { |
| /* APPLE LOCAL begin radar 6048397 handle block byref variables */ |
| /* Try to build and add the location info for navigating through |
| a __Block_byref_... struct to find the real variable. If that |
| fails, fall back on the old behavior. */ |
| bool loc_added = false; |
| if (is_block_byref_var) |
| loc_added = add_block_byref_var_location_attribute (var_die, decl); |
| if (!loc_added) |
| add_location_or_const_value_attribute (var_die, decl, DW_AT_location); |
| /* APPLE LOCAL end radar 6048397 handle block byref variables */ |
| add_pubname (decl, var_die); |
| } |
| else |
| tree_add_const_value_attribute (var_die, decl); |
| } |
| |
| /* Generate a DIE to represent a label identifier. */ |
| |
| static void |
| gen_label_die (tree decl, dw_die_ref context_die) |
| { |
| tree origin = decl_ultimate_origin (decl); |
| dw_die_ref lbl_die = new_die (DW_TAG_label, context_die, decl); |
| rtx insn; |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| if (origin != NULL) |
| add_abstract_origin_attribute (lbl_die, origin); |
| else |
| add_name_and_src_coords_attributes (lbl_die, decl); |
| |
| if (DECL_ABSTRACT (decl)) |
| equate_decl_number_to_die (decl, lbl_die); |
| else |
| { |
| insn = DECL_RTL_IF_SET (decl); |
| |
| /* Deleted labels are programmer specified labels which have been |
| eliminated because of various optimizations. We still emit them |
| here so that it is possible to put breakpoints on them. */ |
| if (insn |
| && (LABEL_P (insn) |
| || ((NOTE_P (insn) |
| && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)))) |
| { |
| /* When optimization is enabled (via -O) some parts of the compiler |
| (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which |
| represent source-level labels which were explicitly declared by |
| the user. This really shouldn't be happening though, so catch |
| it if it ever does happen. */ |
| gcc_assert (!INSN_DELETED_P (insn)); |
| |
| ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (insn)); |
| add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); |
| } |
| } |
| } |
| |
| /* A helper function for gen_inlined_subroutine_die. Add source coordinate |
| attributes to the DIE for a block STMT, to describe where the inlined |
| function was called from. This is similar to add_src_coords_attributes. */ |
| |
| static inline void |
| add_call_src_coords_attributes (tree stmt, dw_die_ref die) |
| { |
| expanded_location s = expand_location (BLOCK_SOURCE_LOCATION (stmt)); |
| |
| add_AT_file (die, DW_AT_call_file, lookup_filename (s.file)); |
| add_AT_unsigned (die, DW_AT_call_line, s.line); |
| } |
| |
| /* A helper function for gen_lexical_block_die and gen_inlined_subroutine_die. |
| Add low_pc and high_pc attributes to the DIE for a block STMT. */ |
| |
| static inline void |
| add_high_low_attributes (tree stmt, dw_die_ref die) |
| { |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| if (BLOCK_FRAGMENT_CHAIN (stmt)) |
| { |
| tree chain; |
| |
| add_AT_range_list (die, DW_AT_ranges, add_ranges (stmt)); |
| |
| chain = BLOCK_FRAGMENT_CHAIN (stmt); |
| do |
| { |
| add_ranges (chain); |
| chain = BLOCK_FRAGMENT_CHAIN (chain); |
| } |
| while (chain); |
| add_ranges (NULL); |
| } |
| else |
| { |
| ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL, |
| BLOCK_NUMBER (stmt)); |
| add_AT_lbl_id (die, DW_AT_low_pc, label); |
| ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_END_LABEL, |
| BLOCK_NUMBER (stmt)); |
| add_AT_lbl_id (die, DW_AT_high_pc, label); |
| } |
| } |
| |
| /* Generate a DIE for a lexical block. */ |
| |
| static void |
| gen_lexical_block_die (tree stmt, dw_die_ref context_die, int depth) |
| { |
| dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt); |
| |
| if (! BLOCK_ABSTRACT (stmt)) |
| add_high_low_attributes (stmt, stmt_die); |
| |
| decls_for_scope (stmt, stmt_die, depth); |
| } |
| |
| /* Generate a DIE for an inlined subprogram. */ |
| |
| static void |
| gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die, int depth) |
| { |
| tree decl = block_ultimate_origin (stmt); |
| |
| /* Emit info for the abstract instance first, if we haven't yet. We |
| must emit this even if the block is abstract, otherwise when we |
| emit the block below (or elsewhere), we may end up trying to emit |
| a die whose origin die hasn't been emitted, and crashing. */ |
| dwarf2out_abstract_function (decl); |
| |
| if (! BLOCK_ABSTRACT (stmt)) |
| { |
| dw_die_ref subr_die |
| = new_die (DW_TAG_inlined_subroutine, context_die, stmt); |
| |
| add_abstract_origin_attribute (subr_die, decl); |
| add_high_low_attributes (stmt, subr_die); |
| add_call_src_coords_attributes (stmt, subr_die); |
| |
| decls_for_scope (stmt, subr_die, depth); |
| current_function_has_inlines = 1; |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| add_inlined_section_entry (subr_die); |
| } |
| else |
| /* We may get here if we're the outer block of function A that was |
| inlined into function B that was inlined into function C. When |
| generating debugging info for C, dwarf2out_abstract_function(B) |
| would mark all inlined blocks as abstract, including this one. |
| So, we wouldn't (and shouldn't) expect labels to be generated |
| for this one. Instead, just emit debugging info for |
| declarations within the block. This is particularly important |
| in the case of initializers of arguments passed from B to us: |
| if they're statement expressions containing declarations, we |
| wouldn't generate dies for their abstract variables, and then, |
| when generating dies for the real variables, we'd die (pun |
| intended :-) */ |
| gen_lexical_block_die (stmt, context_die, depth); |
| } |
| |
| /* Generate a DIE for a field in a record, or structure. */ |
| |
| static void |
| gen_field_die (tree decl, dw_die_ref context_die) |
| { |
| dw_die_ref decl_die; |
| |
| if (TREE_TYPE (decl) == error_mark_node) |
| return; |
| |
| decl_die = new_die (DW_TAG_member, context_die, decl); |
| add_name_and_src_coords_attributes (decl_die, decl); |
| add_type_attribute (decl_die, member_declared_type (decl), |
| TREE_READONLY (decl), TREE_THIS_VOLATILE (decl), |
| context_die); |
| |
| if (DECL_BIT_FIELD_TYPE (decl)) |
| { |
| add_byte_size_attribute (decl_die, decl); |
| add_bit_size_attribute (decl_die, decl); |
| add_bit_offset_attribute (decl_die, decl); |
| } |
| |
| if (TREE_CODE (DECL_FIELD_CONTEXT (decl)) != UNION_TYPE) |
| add_data_member_location_attribute (decl_die, decl); |
| |
| if (DECL_ARTIFICIAL (decl)) |
| add_AT_flag (decl_die, DW_AT_artificial, 1); |
| |
| if (TREE_PROTECTED (decl)) |
| add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_protected); |
| else if (TREE_PRIVATE (decl)) |
| add_AT_unsigned (decl_die, DW_AT_accessibility, DW_ACCESS_private); |
| |
| /* Equate decl number to die, so that we can look up this decl later on. */ |
| equate_decl_number_to_die (decl, decl_die); |
| } |
| |
| #if 0 |
| /* Don't generate either pointer_type DIEs or reference_type DIEs here. |
| Use modified_type_die instead. |
| We keep this code here just in case these types of DIEs may be needed to |
| represent certain things in other languages (e.g. Pascal) someday. */ |
| |
| static void |
| gen_pointer_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref ptr_die |
| = new_die (DW_TAG_pointer_type, scope_die_for (type, context_die), type); |
| |
| equate_type_number_to_die (type, ptr_die); |
| add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); |
| add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); |
| } |
| |
| /* Don't generate either pointer_type DIEs or reference_type DIEs here. |
| Use modified_type_die instead. |
| We keep this code here just in case these types of DIEs may be needed to |
| represent certain things in other languages (e.g. Pascal) someday. */ |
| |
| static void |
| gen_reference_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref ref_die |
| = new_die (DW_TAG_reference_type, scope_die_for (type, context_die), type); |
| |
| equate_type_number_to_die (type, ref_die); |
| add_type_attribute (ref_die, TREE_TYPE (type), 0, 0, context_die); |
| add_AT_unsigned (mod_type_die, DW_AT_byte_size, PTR_SIZE); |
| } |
| #endif |
| |
| /* Generate a DIE for a pointer to a member type. */ |
| |
| static void |
| gen_ptr_to_mbr_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref ptr_die |
| = new_die (DW_TAG_ptr_to_member_type, |
| scope_die_for (type, context_die), type); |
| |
| equate_type_number_to_die (type, ptr_die); |
| add_AT_die_ref (ptr_die, DW_AT_containing_type, |
| lookup_type_die (TYPE_OFFSET_BASETYPE (type))); |
| add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); |
| } |
| |
| /* Generate the DIE for the compilation unit. */ |
| |
| static dw_die_ref |
| gen_compile_unit_die (const char *filename) |
| { |
| dw_die_ref die; |
| char producer[250]; |
| const char *language_string = lang_hooks.name; |
| int language; |
| |
| die = new_die (DW_TAG_compile_unit, NULL, NULL); |
| |
| if (filename) |
| { |
| add_name_attribute (die, filename); |
| /* Don't add cwd for <built-in>. */ |
| if (filename[0] != DIR_SEPARATOR && filename[0] != '<') |
| add_comp_dir_attribute (die); |
| } |
| |
| sprintf (producer, "%s %s", language_string, version_string); |
| |
| #ifdef MIPS_DEBUGGING_INFO |
| /* The MIPS/SGI compilers place the 'cc' command line options in the producer |
| string. The SGI debugger looks for -g, -g1, -g2, or -g3; if they do |
| not appear in the producer string, the debugger reaches the conclusion |
| that the object file is stripped and has no debugging information. |
| To get the MIPS/SGI debugger to believe that there is debugging |
| information in the object file, we add a -g to the producer string. */ |
| if (debug_info_level > DINFO_LEVEL_TERSE) |
| strcat (producer, " -g"); |
| #endif |
| |
| add_AT_string (die, DW_AT_producer, producer); |
| |
| if (strcmp (language_string, "GNU C++") == 0) |
| language = DW_LANG_C_plus_plus; |
| else if (strcmp (language_string, "GNU Ada") == 0) |
| language = DW_LANG_Ada95; |
| else if (strcmp (language_string, "GNU F77") == 0) |
| language = DW_LANG_Fortran77; |
| else if (strcmp (language_string, "GNU F95") == 0) |
| language = DW_LANG_Fortran95; |
| else if (strcmp (language_string, "GNU Pascal") == 0) |
| language = DW_LANG_Pascal83; |
| else if (strcmp (language_string, "GNU Java") == 0) |
| language = DW_LANG_Java; |
| else if (strcmp (language_string, "GNU Objective-C") == 0) |
| language = DW_LANG_ObjC; |
| else if (strcmp (language_string, "GNU Objective-C++") == 0) |
| language = DW_LANG_ObjC_plus_plus; |
| else |
| language = DW_LANG_C89; |
| |
| add_AT_unsigned (die, DW_AT_language, language); |
| return die; |
| } |
| |
| /* Generate the DIE for a base class. */ |
| |
| static void |
| gen_inheritance_die (tree binfo, tree access, dw_die_ref context_die) |
| { |
| dw_die_ref die = new_die (DW_TAG_inheritance, context_die, binfo); |
| |
| add_type_attribute (die, BINFO_TYPE (binfo), 0, 0, context_die); |
| add_data_member_location_attribute (die, binfo); |
| |
| if (BINFO_VIRTUAL_P (binfo)) |
| add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); |
| |
| if (access == access_public_node) |
| add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); |
| else if (access == access_protected_node) |
| add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected); |
| } |
| |
| /* Generate a DIE for a class member. */ |
| |
| static void |
| gen_member_die (tree type, dw_die_ref context_die) |
| { |
| tree member; |
| tree binfo = TYPE_BINFO (type); |
| dw_die_ref child; |
| |
| /* If this is not an incomplete type, output descriptions of each of its |
| members. Note that as we output the DIEs necessary to represent the |
| members of this record or union type, we will also be trying to output |
| DIEs to represent the *types* of those members. However the `type' |
| function (above) will specifically avoid generating type DIEs for member |
| types *within* the list of member DIEs for this (containing) type except |
| for those types (of members) which are explicitly marked as also being |
| members of this (containing) type themselves. The g++ front- end can |
| force any given type to be treated as a member of some other (containing) |
| type by setting the TYPE_CONTEXT of the given (member) type to point to |
| the TREE node representing the appropriate (containing) type. */ |
| |
| /* First output info about the base classes. */ |
| if (binfo) |
| { |
| VEC(tree,gc) *accesses = BINFO_BASE_ACCESSES (binfo); |
| int i; |
| tree base; |
| |
| for (i = 0; BINFO_BASE_ITERATE (binfo, i, base); i++) |
| gen_inheritance_die (base, |
| (accesses ? VEC_index (tree, accesses, i) |
| : access_public_node), context_die); |
| } |
| |
| /* Now output info about the data members and type members. */ |
| for (member = TYPE_FIELDS (type); member; member = TREE_CHAIN (member)) |
| { |
| /* If we thought we were generating minimal debug info for TYPE |
| and then changed our minds, some of the member declarations |
| may have already been defined. Don't define them again, but |
| do put them in the right order. */ |
| |
| child = lookup_decl_die (member); |
| if (child) |
| splice_child_die (context_die, child); |
| else |
| gen_decl_die (member, context_die); |
| } |
| |
| /* Now output info about the function members (if any). */ |
| for (member = TYPE_METHODS (type); member; member = TREE_CHAIN (member)) |
| { |
| /* Don't include clones in the member list. */ |
| if (DECL_ABSTRACT_ORIGIN (member)) |
| continue; |
| |
| child = lookup_decl_die (member); |
| if (child) |
| splice_child_die (context_die, child); |
| else |
| gen_decl_die (member, context_die); |
| } |
| } |
| |
| /* Generate a DIE for a structure or union type. If TYPE_DECL_SUPPRESS_DEBUG |
| is set, we pretend that the type was never defined, so we only get the |
| member DIEs needed by later specification DIEs. */ |
| |
| static void |
| gen_struct_or_union_type_die (tree type, dw_die_ref context_die) |
| { |
| dw_die_ref type_die = lookup_type_die (type); |
| dw_die_ref scope_die = 0; |
| int nested = 0; |
| int complete = (TYPE_SIZE (type) |
| && (! TYPE_STUB_DECL (type) |
| || ! TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type)))); |
| int ns_decl = (context_die && context_die->die_tag == DW_TAG_namespace); |
| |
| if (type_die && ! complete) |
| return; |
| |
| if (TYPE_CONTEXT (type) != NULL_TREE |
| && (AGGREGATE_TYPE_P (TYPE_CONTEXT (type)) |
| || TREE_CODE (TYPE_CONTEXT (type)) == NAMESPACE_DECL)) |
| nested = 1; |
| |
| scope_die = scope_die_for (type, context_die); |
| |
| if (! type_die || (nested && scope_die == comp_unit_die)) |
| /* First occurrence of type or toplevel definition of nested class. */ |
| { |
| dw_die_ref old_die = type_die; |
| |
| type_die = new_die (TREE_CODE (type) == RECORD_TYPE |
| ? DW_TAG_structure_type : DW_TAG_union_type, |
| scope_die, type); |
| equate_type_number_to_die (type, type_die); |
| if (old_die) |
| add_AT_specification (type_die, old_die); |
| else |
| add_name_attribute (type_die, type_tag (type)); |
| |
| /* APPLE LOCAL begin radar 5811943 - Fix type of pointers to Blocks */ |
| if (TYPE_BLOCK_IMPL_STRUCT (type)) |
| add_AT_flag (type_die, DW_AT_APPLE_block, 1); |
| /* APPLE LOCAL end radar 5811943 - Fix type of pointers to Blocks */ |
| } |
| else |
| remove_AT (type_die, DW_AT_declaration); |
| |
| /* If this type has been completed, then give it a byte_size attribute and |
| then give a list of members. */ |
| if (complete && !ns_decl) |
| { |
| /* Prevent infinite recursion in cases where the type of some member of |
| this type is expressed in terms of this type itself. */ |
| TREE_ASM_WRITTEN (type) = 1; |
| add_byte_size_attribute (type_die, type); |
| if (TYPE_STUB_DECL (type) != NULL_TREE) |
| add_src_coords_attributes (type_die, TYPE_STUB_DECL (type)); |
| |
| /* If the first reference to this type was as the return type of an |
| inline function, then it may not have a parent. Fix this now. */ |
| if (type_die->die_parent == NULL) |
| add_child_die (scope_die, type_die); |
| |
| push_decl_scope (type); |
| gen_member_die (type, type_die); |
| pop_decl_scope (); |
| |
| /* GNU extension: Record what type our vtable lives in. */ |
| if (TYPE_VFIELD (type)) |
| { |
| tree vtype = DECL_FCONTEXT (TYPE_VFIELD (type)); |
| |
| gen_type_die (vtype, context_die); |
| add_AT_die_ref (type_die, DW_AT_containing_type, |
| lookup_type_die (vtype)); |
| } |
| } |
| else |
| { |
| add_AT_flag (type_die, DW_AT_declaration, 1); |
| |
| /* We don't need to do this for function-local types. */ |
| if (TYPE_STUB_DECL (type) |
| && ! decl_function_context (TYPE_STUB_DECL (type))) |
| VEC_safe_push (tree, gc, incomplete_types, type); |
| } |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (get_AT (type_die, DW_AT_name)) |
| add_pubtype (type, type_die); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| } |
| |
| /* Generate a DIE for a subroutine _type_. */ |
| |
| static void |
| gen_subroutine_type_die (tree type, dw_die_ref context_die) |
| { |
| tree return_type = TREE_TYPE (type); |
| dw_die_ref subr_die |
| = new_die (DW_TAG_subroutine_type, |
| scope_die_for (type, context_die), type); |
| |
| equate_type_number_to_die (type, subr_die); |
| add_prototyped_attribute (subr_die, type); |
| add_type_attribute (subr_die, return_type, 0, 0, context_die); |
| gen_formal_types_die (type, subr_die); |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (get_AT (subr_die, DW_AT_name)) |
| add_pubtype (type, subr_die); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| } |
| |
| /* Generate a DIE for a type definition. */ |
| |
| static void |
| gen_typedef_die (tree decl, dw_die_ref context_die) |
| { |
| dw_die_ref type_die; |
| tree origin; |
| /* APPLE LOCAL pubtypes, radar 5619139 */ |
| bool type_is_complete_p = true; |
| |
| if (TREE_ASM_WRITTEN (decl)) |
| return; |
| |
| TREE_ASM_WRITTEN (decl) = 1; |
| type_die = new_die (DW_TAG_typedef, context_die, decl); |
| origin = decl_ultimate_origin (decl); |
| if (origin != NULL) |
| add_abstract_origin_attribute (type_die, origin); |
| else |
| { |
| tree type; |
| |
| add_name_and_src_coords_attributes (type_die, decl); |
| if (DECL_ORIGINAL_TYPE (decl)) |
| { |
| type = DECL_ORIGINAL_TYPE (decl); |
| |
| gcc_assert (type != TREE_TYPE (decl)); |
| equate_type_number_to_die (TREE_TYPE (decl), type_die); |
| } |
| else |
| type = TREE_TYPE (decl); |
| |
| /* APPLE LOCAL begin pubtypes, radar 5619139 */ |
| type_is_complete_p = COMPLETE_TYPE_P (type); |
| /* APPLE LOCAL end pubtypes, radar 5619139 */ |
| |
| add_type_attribute (type_die, type, TREE_READONLY (decl), |
| TREE_THIS_VOLATILE (decl), context_die); |
| } |
| |
| if (DECL_ABSTRACT (decl)) |
| equate_decl_number_to_die (decl, type_die); |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| /* Only add typedef type to pubtypes table if the type it is renaming |
| is fully defined in the current file. Radar 5619139 */ |
| if (get_AT (type_die, DW_AT_name) |
| && type_is_complete_p) |
| add_pubtype (decl, type_die); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| } |
| |
| /* Generate a type description DIE. */ |
| |
| static void |
| gen_type_die (tree type, dw_die_ref context_die) |
| { |
| int need_pop; |
| |
| if (type == NULL_TREE || type == error_mark_node) |
| return; |
| |
| if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL |
| && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) |
| { |
| if (TREE_ASM_WRITTEN (type)) |
| return; |
| |
| /* Prevent broken recursion; we can't hand off to the same type. */ |
| gcc_assert (DECL_ORIGINAL_TYPE (TYPE_NAME (type)) != type); |
| |
| TREE_ASM_WRITTEN (type) = 1; |
| gen_decl_die (TYPE_NAME (type), context_die); |
| return; |
| } |
| |
| /* We are going to output a DIE to represent the unqualified version |
| of this type (i.e. without any const or volatile qualifiers) so |
| get the main variant (i.e. the unqualified version) of this type |
| now. (Vectors are special because the debugging info is in the |
| cloned type itself). */ |
| if (TREE_CODE (type) != VECTOR_TYPE) |
| type = type_main_variant (type); |
| |
| if (TREE_ASM_WRITTEN (type)) |
| return; |
| |
| switch (TREE_CODE (type)) |
| { |
| case ERROR_MARK: |
| break; |
| |
| /* APPLE LOCAL radar 5732232 - blocks */ |
| case BLOCK_POINTER_TYPE: |
| case POINTER_TYPE: |
| case REFERENCE_TYPE: |
| /* We must set TREE_ASM_WRITTEN in case this is a recursive type. This |
| ensures that the gen_type_die recursion will terminate even if the |
| type is recursive. Recursive types are possible in Ada. */ |
| /* ??? We could perhaps do this for all types before the switch |
| statement. */ |
| TREE_ASM_WRITTEN (type) = 1; |
| |
| /* For these types, all that is required is that we output a DIE (or a |
| set of DIEs) to represent the "basis" type. */ |
| gen_type_die (TREE_TYPE (type), context_die); |
| break; |
| |
| case OFFSET_TYPE: |
| /* This code is used for C++ pointer-to-data-member types. |
| Output a description of the relevant class type. */ |
| gen_type_die (TYPE_OFFSET_BASETYPE (type), context_die); |
| |
| /* Output a description of the type of the object pointed to. */ |
| gen_type_die (TREE_TYPE (type), context_die); |
| |
| /* Now output a DIE to represent this pointer-to-data-member type |
| itself. */ |
| gen_ptr_to_mbr_type_die (type, context_die); |
| break; |
| |
| case FUNCTION_TYPE: |
| /* Force out return type (in case it wasn't forced out already). */ |
| gen_type_die (TREE_TYPE (type), context_die); |
| gen_subroutine_type_die (type, context_die); |
| break; |
| |
| case METHOD_TYPE: |
| /* Force out return type (in case it wasn't forced out already). */ |
| gen_type_die (TREE_TYPE (type), context_die); |
| gen_subroutine_type_die (type, context_die); |
| break; |
| |
| case ARRAY_TYPE: |
| gen_array_type_die (type, context_die); |
| break; |
| |
| case VECTOR_TYPE: |
| gen_array_type_die (type, context_die); |
| break; |
| |
| case ENUMERAL_TYPE: |
| case RECORD_TYPE: |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| /* If this is a nested type whose containing class hasn't been written |
| out yet, writing it out will cover this one, too. This does not apply |
| to instantiations of member class templates; they need to be added to |
| the containing class as they are generated. FIXME: This hurts the |
| idea of combining type decls from multiple TUs, since we can't predict |
| what set of template instantiations we'll get. */ |
| if (TYPE_CONTEXT (type) |
| && AGGREGATE_TYPE_P (TYPE_CONTEXT (type)) |
| && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) |
| { |
| gen_type_die (TYPE_CONTEXT (type), context_die); |
| |
| if (TREE_ASM_WRITTEN (type)) |
| return; |
| |
| /* If that failed, attach ourselves to the stub. */ |
| push_decl_scope (TYPE_CONTEXT (type)); |
| context_die = lookup_type_die (TYPE_CONTEXT (type)); |
| need_pop = 1; |
| } |
| else |
| { |
| declare_in_namespace (type, context_die); |
| need_pop = 0; |
| } |
| |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| { |
| /* This might have been written out by the call to |
| declare_in_namespace. */ |
| if (!TREE_ASM_WRITTEN (type)) |
| gen_enumeration_type_die (type, context_die); |
| } |
| else |
| gen_struct_or_union_type_die (type, context_die); |
| |
| if (need_pop) |
| pop_decl_scope (); |
| |
| /* Don't set TREE_ASM_WRITTEN on an incomplete struct; we want to fix |
| it up if it is ever completed. gen_*_type_die will set it for us |
| when appropriate. */ |
| return; |
| |
| case VOID_TYPE: |
| case INTEGER_TYPE: |
| case REAL_TYPE: |
| case COMPLEX_TYPE: |
| case BOOLEAN_TYPE: |
| /* No DIEs needed for fundamental types. */ |
| break; |
| |
| case LANG_TYPE: |
| /* No Dwarf representation currently defined. */ |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| TREE_ASM_WRITTEN (type) = 1; |
| } |
| |
| /* Generate a DIE for a tagged type instantiation. */ |
| |
| static void |
| gen_tagged_type_instantiation_die (tree type, dw_die_ref context_die) |
| { |
| if (type == NULL_TREE || type == error_mark_node) |
| return; |
| |
| /* We are going to output a DIE to represent the unqualified version of |
| this type (i.e. without any const or volatile qualifiers) so make sure |
| that we have the main variant (i.e. the unqualified version) of this |
| type now. */ |
| gcc_assert (type == type_main_variant (type)); |
| |
| /* Do not check TREE_ASM_WRITTEN (type) as it may not be set if this is |
| an instance of an unresolved type. */ |
| |
| switch (TREE_CODE (type)) |
| { |
| case ERROR_MARK: |
| break; |
| |
| case ENUMERAL_TYPE: |
| gen_inlined_enumeration_type_die (type, context_die); |
| break; |
| |
| case RECORD_TYPE: |
| gen_inlined_structure_type_die (type, context_die); |
| break; |
| |
| case UNION_TYPE: |
| case QUAL_UNION_TYPE: |
| gen_inlined_union_type_die (type, context_die); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Generate a DW_TAG_lexical_block DIE followed by DIEs to represent all of the |
| things which are local to the given block. */ |
| |
| static void |
| gen_block_die (tree stmt, dw_die_ref context_die, int depth) |
| { |
| int must_output_die = 0; |
| tree origin; |
| tree decl; |
| enum tree_code origin_code; |
| |
| /* Ignore blocks that are NULL. */ |
| if (stmt == NULL_TREE) |
| return; |
| |
| /* If the block is one fragment of a non-contiguous block, do not |
| process the variables, since they will have been done by the |
| origin block. Do process subblocks. */ |
| if (BLOCK_FRAGMENT_ORIGIN (stmt)) |
| { |
| tree sub; |
| |
| for (sub = BLOCK_SUBBLOCKS (stmt); sub; sub = BLOCK_CHAIN (sub)) |
| gen_block_die (sub, context_die, depth + 1); |
| |
| return; |
| } |
| |
| /* Determine the "ultimate origin" of this block. This block may be an |
| inlined instance of an inlined instance of inline function, so we have |
| to trace all of the way back through the origin chain to find out what |
| sort of node actually served as the original seed for the creation of |
| the current block. */ |
| origin = block_ultimate_origin (stmt); |
| origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK; |
| |
| /* Determine if we need to output any Dwarf DIEs at all to represent this |
| block. */ |
| if (origin_code == FUNCTION_DECL) |
| /* The outer scopes for inlinings *must* always be represented. We |
| generate DW_TAG_inlined_subroutine DIEs for them. (See below.) */ |
| must_output_die = 1; |
| else |
| { |
| /* In the case where the current block represents an inlining of the |
| "body block" of an inline function, we must *NOT* output any DIE for |
| this block because we have already output a DIE to represent the whole |
| inlined function scope and the "body block" of any function doesn't |
| really represent a different scope according to ANSI C rules. So we |
| check here to make sure that this block does not represent a "body |
| block inlining" before trying to set the MUST_OUTPUT_DIE flag. */ |
| if (! is_body_block (origin ? origin : stmt)) |
| { |
| /* Determine if this block directly contains any "significant" |
| local declarations which we will need to output DIEs for. */ |
| if (debug_info_level > DINFO_LEVEL_TERSE) |
| /* We are not in terse mode so *any* local declaration counts |
| as being a "significant" one. */ |
| must_output_die = (BLOCK_VARS (stmt) != NULL |
| && (TREE_USED (stmt) |
| || TREE_ASM_WRITTEN (stmt) |
| || BLOCK_ABSTRACT (stmt))); |
| else |
| /* We are in terse mode, so only local (nested) function |
| definitions count as "significant" local declarations. */ |
| for (decl = BLOCK_VARS (stmt); |
| decl != NULL; decl = TREE_CHAIN (decl)) |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| && DECL_INITIAL (decl)) |
| { |
| must_output_die = 1; |
| break; |
| } |
| } |
| } |
| |
| /* It would be a waste of space to generate a Dwarf DW_TAG_lexical_block |
| DIE for any block which contains no significant local declarations at |
| all. Rather, in such cases we just call `decls_for_scope' so that any |
| needed Dwarf info for any sub-blocks will get properly generated. Note |
| that in terse mode, our definition of what constitutes a "significant" |
| local declaration gets restricted to include only inlined function |
| instances and local (nested) function definitions. */ |
| if (must_output_die) |
| { |
| if (origin_code == FUNCTION_DECL) |
| gen_inlined_subroutine_die (stmt, context_die, depth); |
| else |
| gen_lexical_block_die (stmt, context_die, depth); |
| } |
| else |
| decls_for_scope (stmt, context_die, depth); |
| } |
| |
| /* Generate all of the decls declared within a given scope and (recursively) |
| all of its sub-blocks. */ |
| |
| static void |
| decls_for_scope (tree stmt, dw_die_ref context_die, int depth) |
| { |
| tree decl; |
| tree subblocks; |
| |
| /* Ignore NULL blocks. */ |
| if (stmt == NULL_TREE) |
| return; |
| |
| if (TREE_USED (stmt)) |
| { |
| /* Output the DIEs to represent all of the data objects and typedefs |
| declared directly within this block but not within any nested |
| sub-blocks. Also, nested function and tag DIEs have been |
| generated with a parent of NULL; fix that up now. */ |
| for (decl = BLOCK_VARS (stmt); decl != NULL; decl = TREE_CHAIN (decl)) |
| { |
| dw_die_ref die; |
| |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| die = lookup_decl_die (decl); |
| else if (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl)) |
| die = lookup_type_die (TREE_TYPE (decl)); |
| else |
| die = NULL; |
| |
| if (die != NULL && die->die_parent == NULL) |
| add_child_die (context_die, die); |
| /* Do not produce debug information for static variables since |
| these might be optimized out. We are called for these later |
| in cgraph_varpool_analyze_pending_decls. */ |
| if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)) |
| ; |
| else |
| gen_decl_die (decl, context_die); |
| } |
| } |
| |
| /* If we're at -g1, we're not interested in subblocks. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| |
| /* Output the DIEs to represent all sub-blocks (and the items declared |
| therein) of this block. */ |
| for (subblocks = BLOCK_SUBBLOCKS (stmt); |
| subblocks != NULL; |
| subblocks = BLOCK_CHAIN (subblocks)) |
| gen_block_die (subblocks, context_die, depth + 1); |
| } |
| |
| /* Is this a typedef we can avoid emitting? */ |
| |
| static inline int |
| is_redundant_typedef (tree decl) |
| { |
| if (TYPE_DECL_IS_STUB (decl)) |
| return 1; |
| |
| if (DECL_ARTIFICIAL (decl) |
| && DECL_CONTEXT (decl) |
| && is_tagged_type (DECL_CONTEXT (decl)) |
| && TREE_CODE (TYPE_NAME (DECL_CONTEXT (decl))) == TYPE_DECL |
| && DECL_NAME (decl) == DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl)))) |
| /* Also ignore the artificial member typedef for the class name. */ |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Returns the DIE for decl. A DIE will always be returned. */ |
| |
| static dw_die_ref |
| force_decl_die (tree decl) |
| { |
| dw_die_ref decl_die; |
| unsigned saved_external_flag; |
| tree save_fn = NULL_TREE; |
| decl_die = lookup_decl_die (decl); |
| if (!decl_die) |
| { |
| dw_die_ref context_die; |
| tree decl_context = DECL_CONTEXT (decl); |
| if (decl_context) |
| { |
| /* Find die that represents this context. */ |
| if (TYPE_P (decl_context)) |
| context_die = force_type_die (decl_context); |
| else |
| context_die = force_decl_die (decl_context); |
| } |
| else |
| context_die = comp_unit_die; |
| |
| decl_die = lookup_decl_die (decl); |
| if (decl_die) |
| return decl_die; |
| |
| switch (TREE_CODE (decl)) |
| { |
| case FUNCTION_DECL: |
| /* Clear current_function_decl, so that gen_subprogram_die thinks |
| that this is a declaration. At this point, we just want to force |
| declaration die. */ |
| save_fn = current_function_decl; |
| current_function_decl = NULL_TREE; |
| gen_subprogram_die (decl, context_die); |
| current_function_decl = save_fn; |
| break; |
| |
| case VAR_DECL: |
| /* Set external flag to force declaration die. Restore it after |
| gen_decl_die() call. */ |
| saved_external_flag = DECL_EXTERNAL (decl); |
| DECL_EXTERNAL (decl) = 1; |
| gen_decl_die (decl, context_die); |
| DECL_EXTERNAL (decl) = saved_external_flag; |
| break; |
| |
| case NAMESPACE_DECL: |
| dwarf2out_decl (decl); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* We should be able to find the DIE now. */ |
| if (!decl_die) |
| decl_die = lookup_decl_die (decl); |
| gcc_assert (decl_die); |
| } |
| |
| return decl_die; |
| } |
| |
| /* Returns the DIE for TYPE, that must not be a base type. A DIE is |
| always returned. */ |
| |
| static dw_die_ref |
| force_type_die (tree type) |
| { |
| dw_die_ref type_die; |
| |
| type_die = lookup_type_die (type); |
| if (!type_die) |
| { |
| dw_die_ref context_die; |
| if (TYPE_CONTEXT (type)) |
| { |
| if (TYPE_P (TYPE_CONTEXT (type))) |
| context_die = force_type_die (TYPE_CONTEXT (type)); |
| else |
| context_die = force_decl_die (TYPE_CONTEXT (type)); |
| } |
| else |
| context_die = comp_unit_die; |
| |
| type_die = lookup_type_die (type); |
| if (type_die) |
| return type_die; |
| gen_type_die (type, context_die); |
| type_die = lookup_type_die (type); |
| gcc_assert (type_die); |
| } |
| return type_die; |
| } |
| |
| /* Force out any required namespaces to be able to output DECL, |
| and return the new context_die for it, if it's changed. */ |
| |
| static dw_die_ref |
| setup_namespace_context (tree thing, dw_die_ref context_die) |
| { |
| tree context = (DECL_P (thing) |
| ? DECL_CONTEXT (thing) : TYPE_CONTEXT (thing)); |
| if (context && TREE_CODE (context) == NAMESPACE_DECL) |
| /* Force out the namespace. */ |
| context_die = force_decl_die (context); |
| |
| return context_die; |
| } |
| |
| /* Emit a declaration DIE for THING (which is either a DECL or a tagged |
| type) within its namespace, if appropriate. |
| |
| For compatibility with older debuggers, namespace DIEs only contain |
| declarations; all definitions are emitted at CU scope. */ |
| |
| static void |
| declare_in_namespace (tree thing, dw_die_ref context_die) |
| { |
| dw_die_ref ns_context; |
| |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| |
| /* If this decl is from an inlined function, then don't try to emit it in its |
| namespace, as we will get confused. It would have already been emitted |
| when the abstract instance of the inline function was emitted anyways. */ |
| if (DECL_P (thing) && DECL_ABSTRACT_ORIGIN (thing)) |
| return; |
| |
| ns_context = setup_namespace_context (thing, context_die); |
| |
| if (ns_context != context_die) |
| { |
| if (DECL_P (thing)) |
| gen_decl_die (thing, ns_context); |
| else |
| gen_type_die (thing, ns_context); |
| } |
| } |
| |
| /* Generate a DIE for a namespace or namespace alias. */ |
| |
| static void |
| gen_namespace_die (tree decl) |
| { |
| dw_die_ref context_die = setup_namespace_context (decl, comp_unit_die); |
| |
| /* Namespace aliases have a DECL_ABSTRACT_ORIGIN of the namespace |
| they are an alias of. */ |
| if (DECL_ABSTRACT_ORIGIN (decl) == NULL) |
| { |
| /* Output a real namespace. */ |
| dw_die_ref namespace_die |
| = new_die (DW_TAG_namespace, context_die, decl); |
| add_name_and_src_coords_attributes (namespace_die, decl); |
| equate_decl_number_to_die (decl, namespace_die); |
| } |
| else |
| { |
| /* Output a namespace alias. */ |
| |
| /* Force out the namespace we are an alias of, if necessary. */ |
| dw_die_ref origin_die |
| = force_decl_die (DECL_ABSTRACT_ORIGIN (decl)); |
| |
| /* Now create the namespace alias DIE. */ |
| dw_die_ref namespace_die |
| = new_die (DW_TAG_imported_declaration, context_die, decl); |
| add_name_and_src_coords_attributes (namespace_die, decl); |
| add_AT_die_ref (namespace_die, DW_AT_import, origin_die); |
| equate_decl_number_to_die (decl, namespace_die); |
| } |
| } |
| |
| /* Generate Dwarf debug information for a decl described by DECL. */ |
| |
| static void |
| gen_decl_die (tree decl, dw_die_ref context_die) |
| { |
| tree origin; |
| |
| if (DECL_P (decl) && DECL_IGNORED_P (decl)) |
| return; |
| |
| switch (TREE_CODE (decl)) |
| { |
| case ERROR_MARK: |
| break; |
| |
| case CONST_DECL: |
| /* The individual enumerators of an enum type get output when we output |
| the Dwarf representation of the relevant enum type itself. */ |
| break; |
| |
| case FUNCTION_DECL: |
| /* Don't output any DIEs to represent mere function declarations, |
| unless they are class members or explicit block externs. */ |
| if (DECL_INITIAL (decl) == NULL_TREE && DECL_CONTEXT (decl) == NULL_TREE |
| && (current_function_decl == NULL_TREE || DECL_ARTIFICIAL (decl))) |
| break; |
| |
| #if 0 |
| /* FIXME */ |
| /* This doesn't work because the C frontend sets DECL_ABSTRACT_ORIGIN |
| on local redeclarations of global functions. That seems broken. */ |
| if (current_function_decl != decl) |
| /* This is only a declaration. */; |
| #endif |
| |
| /* If we're emitting a clone, emit info for the abstract instance. */ |
| if (DECL_ORIGIN (decl) != decl) |
| dwarf2out_abstract_function (DECL_ABSTRACT_ORIGIN (decl)); |
| |
| /* If we're emitting an out-of-line copy of an inline function, |
| emit info for the abstract instance and set up to refer to it. */ |
| else if (cgraph_function_possibly_inlined_p (decl) |
| && ! DECL_ABSTRACT (decl) |
| && ! class_or_namespace_scope_p (context_die) |
| /* dwarf2out_abstract_function won't emit a die if this is just |
| a declaration. We must avoid setting DECL_ABSTRACT_ORIGIN in |
| that case, because that works only if we have a die. */ |
| && DECL_INITIAL (decl) != NULL_TREE) |
| { |
| dwarf2out_abstract_function (decl); |
| set_decl_origin_self (decl); |
| } |
| |
| /* Otherwise we're emitting the primary DIE for this decl. */ |
| else if (debug_info_level > DINFO_LEVEL_TERSE) |
| { |
| /* Before we describe the FUNCTION_DECL itself, make sure that we |
| have described its return type. */ |
| gen_type_die (TREE_TYPE (TREE_TYPE (decl)), context_die); |
| |
| /* And its virtual context. */ |
| if (DECL_VINDEX (decl) != NULL_TREE) |
| gen_type_die (DECL_CONTEXT (decl), context_die); |
| |
| /* And its containing type. */ |
| origin = decl_class_context (decl); |
| if (origin != NULL_TREE) |
| gen_type_die_for_member (origin, decl, context_die); |
| |
| /* And its containing namespace. */ |
| declare_in_namespace (decl, context_die); |
| } |
| |
| /* Now output a DIE to represent the function itself. */ |
| gen_subprogram_die (decl, context_die); |
| break; |
| |
| case TYPE_DECL: |
| /* If we are in terse mode, don't generate any DIEs to represent any |
| actual typedefs. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| break; |
| |
| /* In the special case of a TYPE_DECL node representing the declaration |
| of some type tag, if the given TYPE_DECL is marked as having been |
| instantiated from some other (original) TYPE_DECL node (e.g. one which |
| was generated within the original definition of an inline function) we |
| have to generate a special (abbreviated) DW_TAG_structure_type, |
| DW_TAG_union_type, or DW_TAG_enumeration_type DIE here. */ |
| if (TYPE_DECL_IS_STUB (decl) && decl_ultimate_origin (decl) != NULL_TREE) |
| { |
| gen_tagged_type_instantiation_die (TREE_TYPE (decl), context_die); |
| break; |
| } |
| |
| if (is_redundant_typedef (decl)) |
| gen_type_die (TREE_TYPE (decl), context_die); |
| else |
| /* Output a DIE to represent the typedef itself. */ |
| gen_typedef_die (decl, context_die); |
| break; |
| |
| case LABEL_DECL: |
| if (debug_info_level >= DINFO_LEVEL_NORMAL) |
| gen_label_die (decl, context_die); |
| break; |
| |
| case VAR_DECL: |
| case RESULT_DECL: |
| /* If we are in terse mode, don't generate any DIEs to represent any |
| variable declarations or definitions. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| break; |
| |
| /* Output any DIEs that are needed to specify the type of this data |
| object. */ |
| gen_type_die (TREE_TYPE (decl), context_die); |
| |
| /* And its containing type. */ |
| origin = decl_class_context (decl); |
| if (origin != NULL_TREE) |
| gen_type_die_for_member (origin, decl, context_die); |
| |
| /* And its containing namespace. */ |
| declare_in_namespace (decl, context_die); |
| |
| /* Now output the DIE to represent the data object itself. This gets |
| complicated because of the possibility that the VAR_DECL really |
| represents an inlined instance of a formal parameter for an inline |
| function. */ |
| origin = decl_ultimate_origin (decl); |
| if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL) |
| gen_formal_parameter_die (decl, context_die); |
| else |
| gen_variable_die (decl, context_die); |
| break; |
| |
| case FIELD_DECL: |
| /* Ignore the nameless fields that are used to skip bits but handle C++ |
| anonymous unions and structs. */ |
| if (DECL_NAME (decl) != NULL_TREE |
| || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE |
| || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE) |
| { |
| gen_type_die (member_declared_type (decl), context_die); |
| gen_field_die (decl, context_die); |
| } |
| break; |
| |
| case PARM_DECL: |
| gen_type_die (TREE_TYPE (decl), context_die); |
| gen_formal_parameter_die (decl, context_die); |
| break; |
| |
| case NAMESPACE_DECL: |
| gen_namespace_die (decl); |
| break; |
| |
| default: |
| /* Probably some frontend-internal decl. Assume we don't care. */ |
| gcc_assert ((int)TREE_CODE (decl) > NUM_TREE_CODES); |
| break; |
| } |
| } |
| |
| /* Output debug information for global decl DECL. Called from toplev.c after |
| compilation proper has finished. */ |
| |
| static void |
| dwarf2out_global_decl (tree decl) |
| { |
| /* Output DWARF2 information for file-scope tentative data object |
| declarations, file-scope (extern) function declarations (which had no |
| corresponding body) and file-scope tagged type declarations and |
| definitions which have not yet been forced out. */ |
| if (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl)) |
| dwarf2out_decl (decl); |
| } |
| |
| /* Output debug information for type decl DECL. Called from toplev.c |
| and from language front ends (to record built-in types). */ |
| static void |
| dwarf2out_type_decl (tree decl, int local) |
| { |
| if (!local) |
| dwarf2out_decl (decl); |
| } |
| |
| /* Output debug information for imported module or decl. */ |
| |
| static void |
| dwarf2out_imported_module_or_decl (tree decl, tree context) |
| { |
| dw_die_ref imported_die, at_import_die; |
| dw_die_ref scope_die; |
| expanded_location xloc; |
| |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| |
| gcc_assert (decl); |
| |
| /* To emit DW_TAG_imported_module or DW_TAG_imported_decl, we need two DIEs. |
| We need decl DIE for reference and scope die. First, get DIE for the decl |
| itself. */ |
| |
| /* Get the scope die for decl context. Use comp_unit_die for global module |
| or decl. If die is not found for non globals, force new die. */ |
| if (!context) |
| scope_die = comp_unit_die; |
| else if (TYPE_P (context)) |
| scope_die = force_type_die (context); |
| else |
| scope_die = force_decl_die (context); |
| |
| /* For TYPE_DECL or CONST_DECL, lookup TREE_TYPE. */ |
| if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == CONST_DECL) |
| { |
| if (is_base_type (TREE_TYPE (decl))) |
| at_import_die = base_type_die (TREE_TYPE (decl)); |
| else |
| at_import_die = force_type_die (TREE_TYPE (decl)); |
| } |
| else |
| { |
| at_import_die = lookup_decl_die (decl); |
| if (!at_import_die) |
| { |
| /* If we're trying to avoid duplicate debug info, we may not have |
| emitted the member decl for this field. Emit it now. */ |
| if (TREE_CODE (decl) == FIELD_DECL) |
| { |
| tree type = DECL_CONTEXT (decl); |
| dw_die_ref type_context_die; |
| |
| if (TYPE_CONTEXT (type)) |
| if (TYPE_P (TYPE_CONTEXT (type))) |
| type_context_die = force_type_die (TYPE_CONTEXT (type)); |
| else |
| type_context_die = force_decl_die (TYPE_CONTEXT (type)); |
| else |
| type_context_die = comp_unit_die; |
| gen_type_die_for_member (type, decl, type_context_die); |
| } |
| at_import_die = force_decl_die (decl); |
| } |
| } |
| |
| /* OK, now we have DIEs for decl as well as scope. Emit imported die. */ |
| if (TREE_CODE (decl) == NAMESPACE_DECL) |
| imported_die = new_die (DW_TAG_imported_module, scope_die, context); |
| else |
| imported_die = new_die (DW_TAG_imported_declaration, scope_die, context); |
| |
| xloc = expand_location (input_location); |
| add_AT_file (imported_die, DW_AT_decl_file, lookup_filename (xloc.file)); |
| add_AT_unsigned (imported_die, DW_AT_decl_line, xloc.line); |
| add_AT_die_ref (imported_die, DW_AT_import, at_import_die); |
| } |
| |
| /* Write the debugging output for DECL. */ |
| |
| void |
| dwarf2out_decl (tree decl) |
| { |
| dw_die_ref context_die = comp_unit_die; |
| |
| switch (TREE_CODE (decl)) |
| { |
| case ERROR_MARK: |
| return; |
| |
| case FUNCTION_DECL: |
| /* What we would really like to do here is to filter out all mere |
| file-scope declarations of file-scope functions which are never |
| referenced later within this translation unit (and keep all of ones |
| that *are* referenced later on) but we aren't clairvoyant, so we have |
| no idea which functions will be referenced in the future (i.e. later |
| on within the current translation unit). So here we just ignore all |
| file-scope function declarations which are not also definitions. If |
| and when the debugger needs to know something about these functions, |
| it will have to hunt around and find the DWARF information associated |
| with the definition of the function. |
| |
| We can't just check DECL_EXTERNAL to find out which FUNCTION_DECL |
| nodes represent definitions and which ones represent mere |
| declarations. We have to check DECL_INITIAL instead. That's because |
| the C front-end supports some weird semantics for "extern inline" |
| function definitions. These can get inlined within the current |
| translation unit (and thus, we need to generate Dwarf info for their |
| abstract instances so that the Dwarf info for the concrete inlined |
| instances can have something to refer to) but the compiler never |
| generates any out-of-lines instances of such things (despite the fact |
| that they *are* definitions). |
| |
| The important point is that the C front-end marks these "extern |
| inline" functions as DECL_EXTERNAL, but we need to generate DWARF for |
| them anyway. Note that the C++ front-end also plays some similar games |
| for inline function definitions appearing within include files which |
| also contain `#pragma interface' pragmas. */ |
| if (DECL_INITIAL (decl) == NULL_TREE) |
| return; |
| |
| /* If we're a nested function, initially use a parent of NULL; if we're |
| a plain function, this will be fixed up in decls_for_scope. If |
| we're a method, it will be ignored, since we already have a DIE. */ |
| if (decl_function_context (decl) |
| /* APPLE LOCAL blocks 5811952 - radar 6172148 */ |
| && (! BLOCK_SYNTHESIZED_FUNC (decl)) |
| /* But if we're in terse mode, we don't care about scope. */ |
| && debug_info_level > DINFO_LEVEL_TERSE) |
| context_die = NULL; |
| break; |
| |
| case VAR_DECL: |
| /* Ignore this VAR_DECL if it refers to a file-scope extern data object |
| declaration and if the declaration was never even referenced from |
| within this entire compilation unit. We suppress these DIEs in |
| order to save space in the .debug section (by eliminating entries |
| which are probably useless). Note that we must not suppress |
| block-local extern declarations (whether used or not) because that |
| would screw-up the debugger's name lookup mechanism and cause it to |
| miss things which really ought to be in scope at a given point. */ |
| if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) |
| return; |
| |
| /* For local statics lookup proper context die. */ |
| if (TREE_STATIC (decl) && decl_function_context (decl)) |
| context_die = lookup_decl_die (DECL_CONTEXT (decl)); |
| |
| /* If we are in terse mode, don't generate any DIEs to represent any |
| variable declarations or definitions. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| break; |
| |
| case NAMESPACE_DECL: |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| if (lookup_decl_die (decl) != NULL) |
| return; |
| break; |
| |
| case TYPE_DECL: |
| /* Don't emit stubs for types unless they are needed by other DIEs. */ |
| if (TYPE_DECL_SUPPRESS_DEBUG (decl)) |
| return; |
| |
| /* Don't bother trying to generate any DIEs to represent any of the |
| normal built-in types for the language we are compiling. */ |
| if (DECL_IS_BUILTIN (decl)) |
| { |
| /* OK, we need to generate one for `bool' so GDB knows what type |
| comparisons have. */ |
| if (is_cxx () |
| && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE |
| && ! DECL_IGNORED_P (decl)) |
| modified_type_die (TREE_TYPE (decl), 0, 0, NULL); |
| |
| return; |
| } |
| |
| /* If we are in terse mode, don't generate any DIEs for types. */ |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return; |
| |
| /* If we're a function-scope tag, initially use a parent of NULL; |
| this will be fixed up in decls_for_scope. */ |
| if (decl_function_context (decl)) |
| context_die = NULL; |
| |
| break; |
| |
| default: |
| return; |
| } |
| |
| gen_decl_die (decl, context_die); |
| } |
| |
| /* Output a marker (i.e. a label) for the beginning of the generated code for |
| a lexical block. */ |
| |
| static void |
| dwarf2out_begin_block (unsigned int line ATTRIBUTE_UNUSED, |
| unsigned int blocknum) |
| { |
| switch_to_section (current_function_section ()); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_BEGIN_LABEL, blocknum); |
| } |
| |
| /* Output a marker (i.e. a label) for the end of the generated code for a |
| lexical block. */ |
| |
| static void |
| dwarf2out_end_block (unsigned int line ATTRIBUTE_UNUSED, unsigned int blocknum) |
| { |
| switch_to_section (current_function_section ()); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, BLOCK_END_LABEL, blocknum); |
| } |
| |
| /* Returns nonzero if it is appropriate not to emit any debugging |
| information for BLOCK, because it doesn't contain any instructions. |
| |
| Don't allow this for blocks with nested functions or local classes |
| as we would end up with orphans, and in the presence of scheduling |
| we may end up calling them anyway. */ |
| |
| static bool |
| dwarf2out_ignore_block (tree block) |
| { |
| tree decl; |
| |
| for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl)) |
| if (TREE_CODE (decl) == FUNCTION_DECL |
| || (TREE_CODE (decl) == TYPE_DECL && TYPE_DECL_IS_STUB (decl))) |
| return 0; |
| |
| return 1; |
| } |
| |
| /* Hash table routines for file_hash. */ |
| |
| static int |
| file_table_eq (const void *p1_p, const void *p2_p) |
| { |
| const struct dwarf_file_data * p1 = p1_p; |
| const char * p2 = p2_p; |
| return strcmp (p1->filename, p2) == 0; |
| } |
| |
| static hashval_t |
| file_table_hash (const void *p_p) |
| { |
| const struct dwarf_file_data * p = p_p; |
| return htab_hash_string (p->filename); |
| } |
| |
| /* Lookup FILE_NAME (in the list of filenames that we know about here in |
| dwarf2out.c) and return its "index". The index of each (known) filename is |
| just a unique number which is associated with only that one filename. We |
| need such numbers for the sake of generating labels (in the .debug_sfnames |
| section) and references to those files numbers (in the .debug_srcinfo |
| and.debug_macinfo sections). If the filename given as an argument is not |
| found in our current list, add it to the list and assign it the next |
| available unique index number. In order to speed up searches, we remember |
| the index of the filename was looked up last. This handles the majority of |
| all searches. */ |
| |
| static struct dwarf_file_data * |
| lookup_filename (const char *file_name) |
| { |
| void ** slot; |
| struct dwarf_file_data * created; |
| |
| /* Check to see if the file name that was searched on the previous |
| call matches this file name. If so, return the index. */ |
| if (file_table_last_lookup |
| && (file_name == file_table_last_lookup->filename |
| || strcmp (file_table_last_lookup->filename, file_name) == 0)) |
| return file_table_last_lookup; |
| |
| /* Didn't match the previous lookup, search the table. */ |
| slot = htab_find_slot_with_hash (file_table, file_name, |
| htab_hash_string (file_name), INSERT); |
| if (*slot) |
| return *slot; |
| |
| created = ggc_alloc (sizeof (struct dwarf_file_data)); |
| created->filename = file_name; |
| created->emitted_number = 0; |
| *slot = created; |
| return created; |
| } |
| |
| /* If the assembler will construct the file table, then translate the compiler |
| internal file table number into the assembler file table number, and emit |
| a .file directive if we haven't already emitted one yet. The file table |
| numbers are different because we prune debug info for unused variables and |
| types, which may include filenames. */ |
| |
| static int |
| maybe_emit_file (struct dwarf_file_data * fd) |
| { |
| if (! fd->emitted_number) |
| { |
| if (last_emitted_file) |
| fd->emitted_number = last_emitted_file->emitted_number + 1; |
| else |
| fd->emitted_number = 1; |
| last_emitted_file = fd; |
| |
| if (DWARF2_ASM_LINE_DEBUG_INFO) |
| { |
| fprintf (asm_out_file, "\t.file %u ", fd->emitted_number); |
| output_quoted_string (asm_out_file, fd->filename); |
| fputc ('\n', asm_out_file); |
| } |
| } |
| |
| return fd->emitted_number; |
| } |
| |
| /* Called by the final INSN scan whenever we see a var location. We |
| use it to drop labels in the right places, and throw the location in |
| our lookup table. */ |
| |
| static void |
| dwarf2out_var_location (rtx loc_note) |
| { |
| char loclabel[MAX_ARTIFICIAL_LABEL_BYTES]; |
| struct var_loc_node *newloc; |
| rtx prev_insn; |
| static rtx last_insn; |
| static const char *last_label; |
| tree decl; |
| |
| if (!DECL_P (NOTE_VAR_LOCATION_DECL (loc_note))) |
| return; |
| prev_insn = PREV_INSN (loc_note); |
| |
| newloc = ggc_alloc_cleared (sizeof (struct var_loc_node)); |
| /* If the insn we processed last time is the previous insn |
| and it is also a var location note, use the label we emitted |
| last time. */ |
| if (last_insn != NULL_RTX |
| && last_insn == prev_insn |
| && NOTE_P (prev_insn) |
| && NOTE_LINE_NUMBER (prev_insn) == NOTE_INSN_VAR_LOCATION) |
| { |
| newloc->label = last_label; |
| } |
| else |
| { |
| ASM_GENERATE_INTERNAL_LABEL (loclabel, "LVL", loclabel_num); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LVL", loclabel_num); |
| loclabel_num++; |
| newloc->label = ggc_strdup (loclabel); |
| } |
| newloc->var_loc_note = loc_note; |
| newloc->next = NULL; |
| |
| if (cfun && in_cold_section_p) |
| newloc->section_label = cfun->cold_section_label; |
| else |
| newloc->section_label = text_section_label; |
| |
| last_insn = loc_note; |
| last_label = newloc->label; |
| decl = NOTE_VAR_LOCATION_DECL (loc_note); |
| add_var_loc_to_decl (decl, newloc); |
| } |
| |
| /* We need to reset the locations at the beginning of each |
| function. We can't do this in the end_function hook, because the |
| declarations that use the locations won't have been output when |
| that hook is called. Also compute have_multiple_function_sections here. */ |
| |
| static void |
| dwarf2out_begin_function (tree fun) |
| { |
| htab_empty (decl_loc_table); |
| |
| if (function_section (fun) != text_section) |
| have_multiple_function_sections = true; |
| } |
| |
| /* Output a label to mark the beginning of a source code line entry |
| and record information relating to this source line, in |
| 'line_info_table' for later output of the .debug_line section. */ |
| |
| static void |
| dwarf2out_source_line (unsigned int line, const char *filename) |
| { |
| if (debug_info_level >= DINFO_LEVEL_NORMAL |
| && line != 0) |
| { |
| int file_num = maybe_emit_file (lookup_filename (filename)); |
| |
| switch_to_section (current_function_section ()); |
| |
| /* If requested, emit something human-readable. */ |
| if (flag_debug_asm) |
| fprintf (asm_out_file, "\t%s %s:%d\n", ASM_COMMENT_START, |
| filename, line); |
| |
| if (DWARF2_ASM_LINE_DEBUG_INFO) |
| { |
| /* Emit the .loc directive understood by GNU as. */ |
| fprintf (asm_out_file, "\t.loc %d %d 0\n", file_num, line); |
| |
| /* Indicate that line number info exists. */ |
| line_info_table_in_use++; |
| } |
| else if (function_section (current_function_decl) != text_section) |
| { |
| dw_separate_line_info_ref line_info; |
| targetm.asm_out.internal_label (asm_out_file, |
| SEPARATE_LINE_CODE_LABEL, |
| separate_line_info_table_in_use); |
| |
| /* Expand the line info table if necessary. */ |
| if (separate_line_info_table_in_use |
| == separate_line_info_table_allocated) |
| { |
| separate_line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; |
| separate_line_info_table |
| = ggc_realloc (separate_line_info_table, |
| separate_line_info_table_allocated |
| * sizeof (dw_separate_line_info_entry)); |
| memset (separate_line_info_table |
| + separate_line_info_table_in_use, |
| 0, |
| (LINE_INFO_TABLE_INCREMENT |
| * sizeof (dw_separate_line_info_entry))); |
| } |
| |
| /* Add the new entry at the end of the line_info_table. */ |
| line_info |
| = &separate_line_info_table[separate_line_info_table_in_use++]; |
| line_info->dw_file_num = file_num; |
| line_info->dw_line_num = line; |
| line_info->function = current_function_funcdef_no; |
| } |
| else |
| { |
| dw_line_info_ref line_info; |
| |
| targetm.asm_out.internal_label (asm_out_file, LINE_CODE_LABEL, |
| line_info_table_in_use); |
| |
| /* Expand the line info table if necessary. */ |
| if (line_info_table_in_use == line_info_table_allocated) |
| { |
| line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; |
| line_info_table |
| = ggc_realloc (line_info_table, |
| (line_info_table_allocated |
| * sizeof (dw_line_info_entry))); |
| memset (line_info_table + line_info_table_in_use, 0, |
| LINE_INFO_TABLE_INCREMENT * sizeof (dw_line_info_entry)); |
| } |
| |
| /* Add the new entry at the end of the line_info_table. */ |
| line_info = &line_info_table[line_info_table_in_use++]; |
| line_info->dw_file_num = file_num; |
| line_info->dw_line_num = line; |
| } |
| } |
| } |
| |
| /* APPLE LOCAL begin opt diary */ |
| /* Emit od_msg. */ |
| static void |
| dwarf2out_od_msg (dw_die_ref die, HOST_WIDE_INT value) |
| { |
| add_AT_int (die, DW_AT_GNU_OD_msg, value); |
| } |
| |
| |
| /* Emit od_category. */ |
| static void |
| dwarf2out_od_category (dw_die_ref die, HOST_WIDE_INT value) |
| { |
| add_AT_int (die, DW_AT_GNU_OD_category, value); |
| } |
| |
| /* Record optimization diary version number. */ |
| |
| static void |
| dwarf2out_od_version (void) |
| { |
| if (flag_opt_diary) |
| add_AT_unsigned (comp_unit_die, DW_AT_GNU_OD_version, 1); |
| } |
| |
| /* Generate od_entry */ |
| static dw_die_ref |
| gen_new_od_entry_die (dw_die_ref parent) |
| { |
| return new_die (DW_TAG_GNU_OD_entry, parent ? parent : comp_unit_die, NULL); |
| } |
| |
| static void |
| dwarf2out_od_entry (enum debug_od_msg msg, expanded_location l) |
| { |
| dw_die_ref entry_die = gen_new_od_entry_die (NULL); |
| |
| dwarf2out_od_msg (entry_die, msg); |
| add_src_coords_attributes_locus (entry_die, l); |
| |
| switch (msg) |
| { |
| case OD_msg_loop_vectorized: |
| dwarf2out_od_category (entry_die, OD_report | OD_action); |
| break; |
| case OD_msg_loop_not_vectorized: |
| dwarf2out_od_category (entry_die, OD_report); |
| break; |
| case OD_msg_loop_vectorized_using_versioning: |
| dwarf2out_od_category (entry_die, OD_report | OD_action); |
| break; |
| case OD_msg_loop_vectorized_using_peeling: |
| dwarf2out_od_category (entry_die, OD_report | OD_action); |
| break; |
| case OD_msg_loop_not_vectorized_multiple_exits: |
| dwarf2out_od_category (entry_die, OD_report); |
| break; |
| case OD_msg_loop_not_vectorized_bad_data_ref: |
| dwarf2out_od_category (entry_die, OD_report | OD_hint); |
| break; |
| case OD_msg_loop_not_vectorized_unsupported_ops: |
| dwarf2out_od_category (entry_die, OD_report | OD_limit); |
| break; |
| case OD_msg_loop_not_vectorized_data_dep: |
| dwarf2out_od_category (entry_die, OD_report | OD_hint); |
| break; |
| default: |
| break; |
| } |
| } |
| /* APPLE LOCAL end opt diary */ |
| /* Record the beginning of a new source file. */ |
| |
| static void |
| dwarf2out_start_source_file (unsigned int lineno, const char *filename) |
| { |
| if (flag_eliminate_dwarf2_dups) |
| { |
| /* Record the beginning of the file for break_out_includes. */ |
| dw_die_ref bincl_die; |
| |
| bincl_die = new_die (DW_TAG_GNU_BINCL, comp_unit_die, NULL); |
| add_AT_string (bincl_die, DW_AT_name, filename); |
| } |
| |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| int file_num = maybe_emit_file (lookup_filename (filename)); |
| |
| switch_to_section (debug_macinfo_section); |
| dw2_asm_output_data (1, DW_MACINFO_start_file, "Start new file"); |
| dw2_asm_output_data_uleb128 (lineno, "Included from line number %d", |
| lineno); |
| |
| dw2_asm_output_data_uleb128 (file_num, "file %s", filename); |
| } |
| } |
| |
| /* Record the end of a source file. */ |
| |
| static void |
| dwarf2out_end_source_file (unsigned int lineno ATTRIBUTE_UNUSED) |
| { |
| if (flag_eliminate_dwarf2_dups) |
| /* Record the end of the file for break_out_includes. */ |
| new_die (DW_TAG_GNU_EINCL, comp_unit_die, NULL); |
| |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| switch_to_section (debug_macinfo_section); |
| dw2_asm_output_data (1, DW_MACINFO_end_file, "End file"); |
| } |
| } |
| |
| /* Called from debug_define in toplev.c. The `buffer' parameter contains |
| the tail part of the directive line, i.e. the part which is past the |
| initial whitespace, #, whitespace, directive-name, whitespace part. */ |
| |
| static void |
| dwarf2out_define (unsigned int lineno ATTRIBUTE_UNUSED, |
| const char *buffer ATTRIBUTE_UNUSED) |
| { |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| switch_to_section (debug_macinfo_section); |
| dw2_asm_output_data (1, DW_MACINFO_define, "Define macro"); |
| dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno); |
| dw2_asm_output_nstring (buffer, -1, "The macro"); |
| } |
| } |
| |
| /* Called from debug_undef in toplev.c. The `buffer' parameter contains |
| the tail part of the directive line, i.e. the part which is past the |
| initial whitespace, #, whitespace, directive-name, whitespace part. */ |
| |
| static void |
| dwarf2out_undef (unsigned int lineno ATTRIBUTE_UNUSED, |
| const char *buffer ATTRIBUTE_UNUSED) |
| { |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| switch_to_section (debug_macinfo_section); |
| dw2_asm_output_data (1, DW_MACINFO_undef, "Undefine macro"); |
| dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno); |
| dw2_asm_output_nstring (buffer, -1, "The macro"); |
| } |
| } |
| |
| /* Set up for Dwarf output at the start of compilation. */ |
| |
| static void |
| dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) |
| { |
| /* Allocate the file_table. */ |
| file_table = htab_create_ggc (50, file_table_hash, |
| file_table_eq, NULL); |
| |
| /* Allocate the decl_die_table. */ |
| decl_die_table = htab_create_ggc (10, decl_die_table_hash, |
| decl_die_table_eq, NULL); |
| |
| /* Allocate the decl_loc_table. */ |
| decl_loc_table = htab_create_ggc (10, decl_loc_table_hash, |
| decl_loc_table_eq, NULL); |
| |
| /* Allocate the initial hunk of the decl_scope_table. */ |
| decl_scope_table = VEC_alloc (tree, gc, 256); |
| |
| /* Allocate the initial hunk of the abbrev_die_table. */ |
| abbrev_die_table = ggc_alloc_cleared (ABBREV_DIE_TABLE_INCREMENT |
| * sizeof (dw_die_ref)); |
| abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT; |
| /* Zero-th entry is allocated, but unused. */ |
| abbrev_die_table_in_use = 1; |
| |
| /* Allocate the initial hunk of the line_info_table. */ |
| line_info_table = ggc_alloc_cleared (LINE_INFO_TABLE_INCREMENT |
| * sizeof (dw_line_info_entry)); |
| line_info_table_allocated = LINE_INFO_TABLE_INCREMENT; |
| |
| /* Zero-th entry is allocated, but unused. */ |
| line_info_table_in_use = 1; |
| |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| /* Allocate the pubtypes and pubnames vectors. */ |
| |
| pubname_table = VEC_alloc (pubname_entry, gc, 32); |
| pubtype_table = VEC_alloc (pubname_entry, gc, 32); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| /* APPLE LOCAL radar 6275985 debug inlined section */ |
| debug_inlined_table = VEC_alloc (inlined_entry, gc, 32); |
| |
| /* Generate the initial DIE for the .debug section. Note that the (string) |
| value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE |
| will (typically) be a relative pathname and that this pathname should be |
| taken as being relative to the directory from which the compiler was |
| invoked when the given (base) source file was compiled. We will fill |
| in this value in dwarf2out_finish. */ |
| comp_unit_die = gen_compile_unit_die (NULL); |
| |
| incomplete_types = VEC_alloc (tree, gc, 64); |
| |
| used_rtx_array = VEC_alloc (rtx, gc, 32); |
| |
| debug_info_section = get_section (DEBUG_INFO_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_abbrev_section = get_section (DEBUG_ABBREV_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_aranges_section = get_section (DEBUG_ARANGES_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_macinfo_section = get_section (DEBUG_MACINFO_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_line_section = get_section (DEBUG_LINE_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_loc_section = get_section (DEBUG_LOC_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_pubnames_section = get_section (DEBUG_PUBNAMES_SECTION, |
| SECTION_DEBUG, NULL); |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| #ifdef DEBUG_PUBTYPES_SECTION |
| debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION, |
| SECTION_DEBUG, NULL); |
| #endif |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| #ifdef DEBUG_INLINED_SECTION |
| debug_inlined_section = get_section (DEBUG_INLINED_SECTION, |
| SECTION_DEBUG, NULL); |
| #endif |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| debug_str_section = get_section (DEBUG_STR_SECTION, |
| DEBUG_STR_SECTION_FLAGS, NULL); |
| debug_ranges_section = get_section (DEBUG_RANGES_SECTION, |
| SECTION_DEBUG, NULL); |
| debug_frame_section = get_section (DEBUG_FRAME_SECTION, |
| SECTION_DEBUG, NULL); |
| |
| ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (abbrev_section_label, |
| DEBUG_ABBREV_SECTION_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label, |
| COLD_TEXT_SECTION_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0); |
| |
| ASM_GENERATE_INTERNAL_LABEL (debug_info_section_label, |
| DEBUG_INFO_SECTION_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (debug_line_section_label, |
| DEBUG_LINE_SECTION_LABEL, 0); |
| ASM_GENERATE_INTERNAL_LABEL (ranges_section_label, |
| DEBUG_RANGES_SECTION_LABEL, 0); |
| switch_to_section (debug_abbrev_section); |
| ASM_OUTPUT_LABEL (asm_out_file, abbrev_section_label); |
| switch_to_section (debug_info_section); |
| ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label); |
| switch_to_section (debug_line_section); |
| ASM_OUTPUT_LABEL (asm_out_file, debug_line_section_label); |
| |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| switch_to_section (debug_macinfo_section); |
| ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label, |
| DEBUG_MACINFO_SECTION_LABEL, 0); |
| ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label); |
| } |
| |
| switch_to_section (text_section); |
| ASM_OUTPUT_LABEL (asm_out_file, text_section_label); |
| /* APPLE LOCAL opt diary */ |
| dwarf2out_od_version (); |
| if (flag_reorder_blocks_and_partition) |
| { |
| switch_to_section (unlikely_text_section ()); |
| ASM_OUTPUT_LABEL (asm_out_file, cold_text_section_label); |
| } |
| } |
| |
| /* A helper function for dwarf2out_finish called through |
| ht_forall. Emit one queued .debug_str string. */ |
| |
| static int |
| output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED) |
| { |
| struct indirect_string_node *node = (struct indirect_string_node *) *h; |
| |
| if (node->form == DW_FORM_strp) |
| { |
| switch_to_section (debug_str_section); |
| ASM_OUTPUT_LABEL (asm_out_file, node->label); |
| assemble_string (node->str, strlen (node->str) + 1); |
| } |
| |
| return 1; |
| } |
| |
| #if ENABLE_ASSERT_CHECKING |
| /* Verify that all marks are clear. */ |
| |
| static void |
| verify_marks_clear (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| gcc_assert (! die->die_mark); |
| FOR_EACH_CHILD (die, c, verify_marks_clear (c)); |
| } |
| #endif /* ENABLE_ASSERT_CHECKING */ |
| |
| /* Clear the marks for a die and its children. |
| Be cool if the mark isn't set. */ |
| |
| static void |
| prune_unmark_dies (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| if (die->die_mark) |
| die->die_mark = 0; |
| FOR_EACH_CHILD (die, c, prune_unmark_dies (c)); |
| } |
| |
| /* Given DIE that we're marking as used, find any other dies |
| it references as attributes and mark them as used. */ |
| |
| static void |
| prune_unused_types_walk_attribs (dw_die_ref die) |
| { |
| dw_attr_ref a; |
| unsigned ix; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| { |
| if (a->dw_attr_val.val_class == dw_val_class_die_ref) |
| { |
| /* A reference to another DIE. |
| Make sure that it will get emitted. */ |
| prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1); |
| } |
| /* Set the string's refcount to 0 so that prune_unused_types_mark |
| accounts properly for it. */ |
| if (AT_class (a) == dw_val_class_str) |
| a->dw_attr_val.v.val_str->refcount = 0; |
| } |
| } |
| |
| |
| /* Mark DIE as being used. If DOKIDS is true, then walk down |
| to DIE's children. */ |
| |
| static void |
| prune_unused_types_mark (dw_die_ref die, int dokids) |
| { |
| dw_die_ref c; |
| |
| if (die->die_mark == 0) |
| { |
| /* We haven't done this node yet. Mark it as used. */ |
| die->die_mark = 1; |
| |
| /* We also have to mark its parents as used. |
| (But we don't want to mark our parents' kids due to this.) */ |
| if (die->die_parent) |
| prune_unused_types_mark (die->die_parent, 0); |
| |
| /* Mark any referenced nodes. */ |
| prune_unused_types_walk_attribs (die); |
| |
| /* If this node is a specification, |
| also mark the definition, if it exists. */ |
| if (get_AT_flag (die, DW_AT_declaration) && die->die_definition) |
| prune_unused_types_mark (die->die_definition, 1); |
| } |
| |
| if (dokids && die->die_mark != 2) |
| { |
| /* We need to walk the children, but haven't done so yet. |
| Remember that we've walked the kids. */ |
| die->die_mark = 2; |
| |
| /* If this is an array type, we need to make sure our |
| kids get marked, even if they're types. */ |
| if (die->die_tag == DW_TAG_array_type) |
| FOR_EACH_CHILD (die, c, prune_unused_types_mark (c, 1)); |
| else |
| FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); |
| } |
| } |
| |
| |
| /* Walk the tree DIE and mark types that we actually use. */ |
| |
| static void |
| prune_unused_types_walk (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| /* Don't do anything if this node is already marked. */ |
| if (die->die_mark) |
| return; |
| |
| switch (die->die_tag) { |
| case DW_TAG_const_type: |
| case DW_TAG_packed_type: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_typedef: |
| case DW_TAG_array_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_class_type: |
| case DW_TAG_friend: |
| case DW_TAG_variant_part: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_string_type: |
| case DW_TAG_set_type: |
| case DW_TAG_subrange_type: |
| case DW_TAG_ptr_to_member_type: |
| case DW_TAG_file_type: |
| if (die->die_perennial_p) |
| break; |
| |
| /* It's a type node --- don't mark it. */ |
| return; |
| |
| default: |
| /* Mark everything else. */ |
| break; |
| } |
| |
| die->die_mark = 1; |
| |
| /* Now, mark any dies referenced from here. */ |
| prune_unused_types_walk_attribs (die); |
| |
| /* Mark children. */ |
| FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); |
| } |
| |
| /* Increment the string counts on strings referred to from DIE's |
| attributes. */ |
| |
| static void |
| prune_unused_types_update_strings (dw_die_ref die) |
| { |
| dw_attr_ref a; |
| unsigned ix; |
| |
| for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) |
| if (AT_class (a) == dw_val_class_str) |
| { |
| struct indirect_string_node *s = a->dw_attr_val.v.val_str; |
| s->refcount++; |
| /* Avoid unnecessarily putting strings that are used less than |
| twice in the hash table. */ |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| /* We always want function names to go into the hash table. */ |
| if ((s->refcount |
| == ((DEBUG_STR_SECTION_FLAGS & SECTION_MERGE) ? 1 : 2)) |
| || s->is_fn_name) |
| { |
| void ** slot; |
| slot = htab_find_slot_with_hash (debug_str_hash, s->str, |
| htab_hash_string (s->str), |
| INSERT); |
| if (*slot == NULL) |
| *slot = s; |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| } |
| } |
| } |
| |
| /* Remove from the tree DIE any dies that aren't marked. */ |
| |
| static void |
| prune_unused_types_prune (dw_die_ref die) |
| { |
| dw_die_ref c; |
| |
| gcc_assert (die->die_mark); |
| prune_unused_types_update_strings (die); |
| |
| if (! die->die_child) |
| return; |
| |
| c = die->die_child; |
| do { |
| dw_die_ref prev = c; |
| for (c = c->die_sib; ! c->die_mark; c = c->die_sib) |
| if (c == die->die_child) |
| { |
| /* No marked children between 'prev' and the end of the list. */ |
| if (prev == c) |
| /* No marked children at all. */ |
| die->die_child = NULL; |
| else |
| { |
| prev->die_sib = c->die_sib; |
| die->die_child = prev; |
| } |
| return; |
| } |
| |
| if (c != prev->die_sib) |
| prev->die_sib = c; |
| prune_unused_types_prune (c); |
| } while (c != die->die_child); |
| } |
| |
| |
| /* Remove dies representing declarations that we never use. */ |
| |
| static void |
| prune_unused_types (void) |
| { |
| unsigned int i; |
| limbo_die_node *node; |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| pubname_ref pub; |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| #if ENABLE_ASSERT_CHECKING |
| /* All the marks should already be clear. */ |
| verify_marks_clear (comp_unit_die); |
| for (node = limbo_die_list; node; node = node->next) |
| verify_marks_clear (node->die); |
| #endif /* ENABLE_ASSERT_CHECKING */ |
| |
| /* Set the mark on nodes that are actually used. */ |
| prune_unused_types_walk (comp_unit_die); |
| for (node = limbo_die_list; node; node = node->next) |
| prune_unused_types_walk (node->die); |
| |
| /* Also set the mark on nodes referenced from the |
| pubname_table or arange_table. */ |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| for (i = 0; VEC_iterate (pubname_entry, pubname_table, i, pub); i++) |
| prune_unused_types_mark (pub->die, 1); |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| for (i = 0; i < arange_table_in_use; i++) |
| prune_unused_types_mark (arange_table[i], 1); |
| |
| /* Get rid of nodes that aren't marked; and update the string counts. */ |
| if (debug_str_hash) |
| htab_empty (debug_str_hash); |
| prune_unused_types_prune (comp_unit_die); |
| for (node = limbo_die_list; node; node = node->next) |
| prune_unused_types_prune (node->die); |
| |
| /* Leave the marks clear. */ |
| prune_unmark_dies (comp_unit_die); |
| for (node = limbo_die_list; node; node = node->next) |
| prune_unmark_dies (node->die); |
| } |
| |
| /* Set the parameter to true if there are any relative pathnames in |
| the file table. */ |
| static int |
| file_table_relative_p (void ** slot, void *param) |
| { |
| bool *p = param; |
| struct dwarf_file_data *d = *slot; |
| if (d->emitted_number && d->filename[0] != DIR_SEPARATOR) |
| { |
| *p = true; |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* Output stuff that dwarf requires at the end of every file, |
| and generate the DWARF-2 debugging info. */ |
| |
| static void |
| dwarf2out_finish (const char *filename) |
| { |
| limbo_die_node *node, *next_node; |
| dw_die_ref die = 0; |
| |
| /* Add the name for the main input file now. We delayed this from |
| dwarf2out_init to avoid complications with PCH. */ |
| add_name_attribute (comp_unit_die, filename); |
| /* APPLE LOCAL Radar 5645155 */ |
| maybe_emit_file (lookup_filename (filename)); |
| if (filename[0] != DIR_SEPARATOR) |
| add_comp_dir_attribute (comp_unit_die); |
| else if (get_AT (comp_unit_die, DW_AT_comp_dir) == NULL) |
| { |
| bool p = false; |
| htab_traverse (file_table, file_table_relative_p, &p); |
| if (p) |
| add_comp_dir_attribute (comp_unit_die); |
| } |
| |
| /* APPLE LOCAL begin option verifier 4957887 */ |
| /* Add the options for this compilation now, so that the options |
| are from the final compilation not the PCH. |
| Do this only when the undocumented RC_DEBUG_OPTIONS |
| environment variable is set to a nonempty string. |
| This is intended only for internal Apple use. */ |
| { |
| char * debugopt = getenv("RC_DEBUG_OPTIONS"); |
| if (debugopt && debugopt[0]) |
| add_AT_string (comp_unit_die, DW_AT_APPLE_flags, get_arguments()); |
| } |
| /* APPLE LOCAL end option verifier 4957887 */ |
| |
| /* APPLE LOCAL begin radar 2338865 optimization notification */ |
| if (optimize > 0) |
| add_AT_flag (comp_unit_die, DW_AT_APPLE_optimized, 1); |
| /* APPLE LOCAL end radar 2338865 optimization notification */ |
| |
| /* Traverse the limbo die list, and add parent/child links. The only |
| dies without parents that should be here are concrete instances of |
| inline functions, and the comp_unit_die. We can ignore the comp_unit_die. |
| For concrete instances, we can get the parent die from the abstract |
| instance. */ |
| for (node = limbo_die_list; node; node = next_node) |
| { |
| next_node = node->next; |
| die = node->die; |
| |
| if (die->die_parent == NULL) |
| { |
| dw_die_ref origin = get_AT_ref (die, DW_AT_abstract_origin); |
| |
| if (origin) |
| add_child_die (origin->die_parent, die); |
| else if (die == comp_unit_die) |
| ; |
| else if (errorcount > 0 || sorrycount > 0) |
| /* It's OK to be confused by errors in the input. */ |
| add_child_die (comp_unit_die, die); |
| else |
| { |
| /* In certain situations, the lexical block containing a |
| nested function can be optimized away, which results |
| in the nested function die being orphaned. Likewise |
| with the return type of that nested function. Force |
| this to be a child of the containing function. |
| |
| It may happen that even the containing function got fully |
| inlined and optimized out. In that case we are lost and |
| assign the empty child. This should not be big issue as |
| the function is likely unreachable too. */ |
| tree context = NULL_TREE; |
| |
| gcc_assert (node->created_for); |
| |
| if (DECL_P (node->created_for)) |
| context = DECL_CONTEXT (node->created_for); |
| else if (TYPE_P (node->created_for)) |
| context = TYPE_CONTEXT (node->created_for); |
| |
| gcc_assert (context |
| && (TREE_CODE (context) == FUNCTION_DECL |
| || TREE_CODE (context) == NAMESPACE_DECL)); |
| |
| origin = lookup_decl_die (context); |
| if (origin) |
| add_child_die (origin, die); |
| else |
| add_child_die (comp_unit_die, die); |
| } |
| } |
| } |
| |
| limbo_die_list = NULL; |
| |
| /* Walk through the list of incomplete types again, trying once more to |
| emit full debugging info for them. */ |
| retry_incomplete_types (); |
| |
| if (flag_eliminate_unused_debug_types) |
| prune_unused_types (); |
| |
| /* Generate separate CUs for each of the include files we've seen. |
| They will go into limbo_die_list. */ |
| if (flag_eliminate_dwarf2_dups) |
| break_out_includes (comp_unit_die); |
| |
| /* Traverse the DIE's and add add sibling attributes to those DIE's |
| that have children. */ |
| add_sibling_attributes (comp_unit_die); |
| for (node = limbo_die_list; node; node = node->next) |
| add_sibling_attributes (node->die); |
| |
| /* Output a terminator label for the .text section. */ |
| switch_to_section (text_section); |
| targetm.asm_out.internal_label (asm_out_file, TEXT_END_LABEL, 0); |
| if (flag_reorder_blocks_and_partition) |
| { |
| switch_to_section (unlikely_text_section ()); |
| targetm.asm_out.internal_label (asm_out_file, COLD_END_LABEL, 0); |
| } |
| |
| /* We can only use the low/high_pc attributes if all of the code was |
| in .text. */ |
| if (!have_multiple_function_sections) |
| { |
| add_AT_lbl_id (comp_unit_die, DW_AT_low_pc, text_section_label); |
| add_AT_lbl_id (comp_unit_die, DW_AT_high_pc, text_end_label); |
| } |
| |
| /* If it wasn't, we need to give .debug_loc and .debug_ranges an appropriate |
| "base address". Use zero so that these addresses become absolute. */ |
| else if (have_location_lists || ranges_table_in_use) |
| add_AT_addr (comp_unit_die, DW_AT_entry_pc, const0_rtx); |
| |
| /* Output location list section if necessary. */ |
| if (have_location_lists) |
| { |
| /* Output the location lists info. */ |
| switch_to_section (debug_loc_section); |
| ASM_GENERATE_INTERNAL_LABEL (loc_section_label, |
| DEBUG_LOC_SECTION_LABEL, 0); |
| ASM_OUTPUT_LABEL (asm_out_file, loc_section_label); |
| output_location_lists (die); |
| } |
| |
| if (debug_info_level >= DINFO_LEVEL_NORMAL) |
| add_AT_lineptr (comp_unit_die, DW_AT_stmt_list, |
| debug_line_section_label); |
| |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| add_AT_macptr (comp_unit_die, DW_AT_macro_info, macinfo_section_label); |
| |
| /* Output all of the compilation units. We put the main one last so that |
| the offsets are available to output_pubnames. */ |
| for (node = limbo_die_list; node; node = node->next) |
| output_comp_unit (node->die, 0); |
| |
| output_comp_unit (comp_unit_die, 0); |
| |
| /* Output the abbreviation table. */ |
| switch_to_section (debug_abbrev_section); |
| output_abbrev_section (); |
| |
| /* Output public names table if necessary. */ |
| /* APPLE LOCAL begin pubtypes, approved for 4.3 4535968 */ |
| if (! VEC_empty (pubname_entry, pubname_table)) |
| { |
| switch_to_section (debug_pubnames_section); |
| output_pubnames (pubname_table); |
| } |
| |
| #ifdef DEBUG_PUBTYPES_SECTION |
| /* Output public types table if necessary. */ |
| if (! VEC_empty (pubname_entry, pubtype_table)) |
| { |
| switch_to_section (debug_pubtypes_section); |
| output_pubnames (pubtype_table); |
| } |
| #endif |
| /* APPLE LOCAL end pubtypes, approved for 4.3 4535968 */ |
| |
| /* APPLE LOCAL begin radar 6275985 debug inlined section */ |
| #ifdef DEBUG_INLINED_SECTION |
| /* Output debug section containing info about inlined subroutines */ |
| if (! VEC_empty (inlined_entry, debug_inlined_table)) |
| { |
| switch_to_section (debug_inlined_section); |
| output_debug_inlined_section (debug_inlined_table); |
| } |
| #endif |
| /* APPLE LOCAL end radar 6275985 debug inlined section */ |
| |
| /* Output the address range information. We only put functions in the arange |
| table, so don't write it out if we don't have any. */ |
| if (fde_table_in_use) |
| { |
| switch_to_section (debug_aranges_section); |
| output_aranges (); |
| } |
| |
| /* Output ranges section if necessary. */ |
| if (ranges_table_in_use) |
| { |
| switch_to_section (debug_ranges_section); |
| ASM_OUTPUT_LABEL (asm_out_file, ranges_section_label); |
| output_ranges (); |
| } |
| |
| /* Output the source line correspondence table. We must do this |
| even if there is no line information. Otherwise, on an empty |
| translation unit, we will generate a present, but empty, |
| .debug_info section. IRIX 6.5 `nm' will then complain when |
| examining the file. This is done late so that any filenames |
| used by the debug_info section are marked as 'used'. */ |
| if (! DWARF2_ASM_LINE_DEBUG_INFO) |
| { |
| switch_to_section (debug_line_section); |
| output_line_info (); |
| } |
| |
| /* Have to end the macro section. */ |
| if (debug_info_level >= DINFO_LEVEL_VERBOSE) |
| { |
| switch_to_section (debug_macinfo_section); |
| dw2_asm_output_data (1, 0, "End compilation unit"); |
| } |
| |
| /* If we emitted any DW_FORM_strp form attribute, output the string |
| table too. */ |
| if (debug_str_hash) |
| htab_traverse (debug_str_hash, output_indirect_string, NULL); |
| } |
| #else |
| |
| /* This should never be used, but its address is needed for comparisons. */ |
| const struct gcc_debug_hooks dwarf2_debug_hooks; |
| |
| #endif /* DWARF2_DEBUGGING_INFO */ |
| |
| #include "gt-dwarf2out.h" |