| /* Output Dwarf2 format symbol table information from GCC. |
| Copyright (C) 1992-2015 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* 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. */ |
| |
| /* 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 */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "hash-set.h" |
| #include "machmode.h" |
| #include "vec.h" |
| #include "double-int.h" |
| #include "input.h" |
| #include "alias.h" |
| #include "symtab.h" |
| #include "wide-int.h" |
| #include "inchash.h" |
| #include "real.h" |
| #include "tree.h" |
| #include "fold-const.h" |
| #include "stringpool.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "hashtab.h" |
| #include "hard-reg-set.h" |
| #include "function.h" |
| #include "emit-rtl.h" |
| #include "hash-table.h" |
| #include "version.h" |
| #include "flags.h" |
| #include "regs.h" |
| #include "rtlhash.h" |
| #include "insn-config.h" |
| #include "reload.h" |
| #include "output.h" |
| #include "statistics.h" |
| #include "fixed-value.h" |
| #include "expmed.h" |
| #include "dojump.h" |
| #include "explow.h" |
| #include "calls.h" |
| #include "stmt.h" |
| #include "expr.h" |
| #include "except.h" |
| #include "dwarf2.h" |
| #include "dwarf2out.h" |
| #include "dwarf2asm.h" |
| #include "toplev.h" |
| #include "md5.h" |
| #include "tm_p.h" |
| #include "diagnostic.h" |
| #include "tree-pretty-print.h" |
| #include "debug.h" |
| #include "target.h" |
| #include "common/common-target.h" |
| #include "langhooks.h" |
| #include "hash-map.h" |
| #include "is-a.h" |
| #include "plugin-api.h" |
| #include "ipa-ref.h" |
| #include "cgraph.h" |
| #include "ira.h" |
| #include "lra.h" |
| #include "dumpfile.h" |
| #include "opts.h" |
| #include "tree-dfa.h" |
| #include "gdb/gdb-index.h" |
| #include "rtl-iter.h" |
| |
| static void dwarf2out_source_line (unsigned int, const char *, int, bool); |
| static rtx_insn *last_var_location_insn; |
| static rtx_insn *cached_next_real_insn; |
| static void dwarf2out_decl (tree); |
| |
| #ifdef VMS_DEBUGGING_INFO |
| int vms_file_stats_name (const char *, long long *, long *, char *, int *); |
| |
| /* Define this macro to be a nonzero value if the directory specifications |
| which are output in the debug info should end with a separator. */ |
| #define DWARF2_DIR_SHOULD_END_WITH_SEPARATOR 1 |
| /* Define this macro to evaluate to a nonzero value if GCC should refrain |
| from generating indirect strings in DWARF2 debug information, for instance |
| if your target is stuck with an old version of GDB that is unable to |
| process them properly or uses VMS Debug. */ |
| #define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 1 |
| #else |
| #define DWARF2_DIR_SHOULD_END_WITH_SEPARATOR 0 |
| #define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 0 |
| #endif |
| |
| /* ??? Poison these here until it can be done generically. They've been |
| totally replaced in this file; make sure it stays that way. */ |
| #undef DWARF2_UNWIND_INFO |
| #undef DWARF2_FRAME_INFO |
| #if (GCC_VERSION >= 3000) |
| #pragma GCC poison DWARF2_UNWIND_INFO DWARF2_FRAME_INFO |
| #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, va_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, va_gc> *because we want to tell the garbage collector about |
| it. */ |
| static GTY(()) vec<tree, va_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, va_gc> *decl_scope_table; |
| |
| /* Pointers to various DWARF2 sections. */ |
| static GTY(()) section *debug_info_section; |
| static GTY(()) section *debug_skeleton_info_section; |
| static GTY(()) section *debug_abbrev_section; |
| static GTY(()) section *debug_skeleton_abbrev_section; |
| static GTY(()) section *debug_aranges_section; |
| static GTY(()) section *debug_addr_section; |
| static GTY(()) section *debug_macinfo_section; |
| static GTY(()) section *debug_line_section; |
| static GTY(()) section *debug_skeleton_line_section; |
| static GTY(()) section *debug_loc_section; |
| static GTY(()) section *debug_pubnames_section; |
| static GTY(()) section *debug_pubtypes_section; |
| static GTY(()) section *debug_str_section; |
| static GTY(()) section *debug_str_dwo_section; |
| static GTY(()) section *debug_str_offsets_section; |
| static GTY(()) section *debug_ranges_section; |
| static GTY(()) section *debug_frame_section; |
| |
| /* Maximum size (in bytes) of an artificially generated label. */ |
| #define MAX_ARTIFICIAL_LABEL_BYTES 30 |
| |
| /* 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 |
| |
| /* Round SIZE up to the nearest BOUNDARY. */ |
| #define DWARF_ROUND(SIZE,BOUNDARY) \ |
| ((((SIZE) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY)) |
| |
| /* 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 vector for a table that contains frame description |
| information for each routine. */ |
| #define NOT_INDEXED (-1U) |
| #define NO_INDEX_ASSIGNED (-2U) |
| |
| static GTY(()) vec<dw_fde_ref, va_gc> *fde_vec; |
| |
| struct GTY((for_user)) indirect_string_node { |
| const char *str; |
| unsigned int refcount; |
| enum dwarf_form form; |
| char *label; |
| unsigned int index; |
| }; |
| |
| struct indirect_string_hasher : ggc_hasher<indirect_string_node *> |
| { |
| typedef const char *compare_type; |
| |
| static hashval_t hash (indirect_string_node *); |
| static bool equal (indirect_string_node *, const char *); |
| }; |
| |
| static GTY (()) hash_table<indirect_string_hasher> *debug_str_hash; |
| |
| /* With split_debug_info, both the comp_dir and dwo_name go in the |
| main object file, rather than the dwo, similar to the force_direct |
| parameter elsewhere but with additional complications: |
| |
| 1) The string is needed in both the main object file and the dwo. |
| That is, the comp_dir and dwo_name will appear in both places. |
| |
| 2) Strings can use three forms: DW_FORM_string, DW_FORM_strp or |
| DW_FORM_GNU_str_index. |
| |
| 3) GCC chooses the form to use late, depending on the size and |
| reference count. |
| |
| Rather than forcing the all debug string handling functions and |
| callers to deal with these complications, simply use a separate, |
| special-cased string table for any attribute that should go in the |
| main object file. This limits the complexity to just the places |
| that need it. */ |
| |
| static GTY (()) hash_table<indirect_string_hasher> *skeleton_debug_str_hash; |
| |
| static GTY(()) int dw2_string_counter; |
| |
| /* True if the compilation unit places functions in more than one section. */ |
| static GTY(()) bool have_multiple_function_sections = false; |
| |
| /* Whether the default text and cold text sections have been used at all. */ |
| |
| static GTY(()) bool text_section_used = false; |
| static GTY(()) bool cold_text_section_used = false; |
| |
| /* The default cold text section. */ |
| static GTY(()) section *cold_text_section; |
| |
| /* The DIE for C++14 'auto' in a function return type. */ |
| static GTY(()) dw_die_ref auto_die; |
| |
| /* The DIE for C++14 'decltype(auto)' in a function return type. */ |
| static GTY(()) dw_die_ref decltype_auto_die; |
| |
| /* Forward declarations for functions defined in this file. */ |
| |
| static char *stripattributes (const char *); |
| static void output_call_frame_info (int); |
| static void dwarf2out_note_section_used (void); |
| |
| /* Personality decl of current unit. Used only when assembler does not support |
| personality CFI. */ |
| static GTY(()) rtx current_unit_personality; |
| |
| /* 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 PROLOGUE_END_LABEL |
| #define PROLOGUE_END_LABEL "LPE" |
| #endif |
| |
| #ifndef EPILOGUE_BEGIN_LABEL |
| #define EPILOGUE_BEGIN_LABEL "LEB" |
| #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" |
| |
| /* Match the base name of a file to the base name of a compilation unit. */ |
| |
| static int |
| matches_main_base (const char *path) |
| { |
| /* Cache the last query. */ |
| static const char *last_path = NULL; |
| static int last_match = 0; |
| if (path != last_path) |
| { |
| const char *base; |
| int length = base_of_path (path, &base); |
| last_path = path; |
| last_match = (length == main_input_baselength |
| && memcmp (base, main_input_basename, length) == 0); |
| } |
| return last_match; |
| } |
| |
| #ifdef DEBUG_DEBUG_STRUCT |
| |
| static int |
| dump_struct_debug (tree type, enum debug_info_usage usage, |
| enum debug_struct_file criterion, int generic, |
| int matches, int result) |
| { |
| /* Find the type name. */ |
| tree type_decl = TYPE_STUB_DECL (type); |
| tree t = type_decl; |
| const char *name = 0; |
| if (TREE_CODE (t) == TYPE_DECL) |
| t = DECL_NAME (t); |
| if (t) |
| name = IDENTIFIER_POINTER (t); |
| |
| fprintf (stderr, " struct %d %s %s %s %s %d %p %s\n", |
| criterion, |
| DECL_IN_SYSTEM_HEADER (type_decl) ? "sys" : "usr", |
| matches ? "bas" : "hdr", |
| generic ? "gen" : "ord", |
| usage == DINFO_USAGE_DFN ? ";" : |
| usage == DINFO_USAGE_DIR_USE ? "." : "*", |
| result, |
| (void*) type_decl, name); |
| return result; |
| } |
| #define DUMP_GSTRUCT(type, usage, criterion, generic, matches, result) \ |
| dump_struct_debug (type, usage, criterion, generic, matches, result) |
| |
| #else |
| |
| #define DUMP_GSTRUCT(type, usage, criterion, generic, matches, result) \ |
| (result) |
| |
| #endif |
| |
| /* Get the number of HOST_WIDE_INTs needed to represent the precision |
| of the number. Some constants have a large uniform precision, so |
| we get the precision needed for the actual value of the number. */ |
| |
| static unsigned int |
| get_full_len (const wide_int &op) |
| { |
| int prec = wi::min_precision (op, UNSIGNED); |
| return ((prec + HOST_BITS_PER_WIDE_INT - 1) |
| / HOST_BITS_PER_WIDE_INT); |
| } |
| |
| static bool |
| should_emit_struct_debug (tree type, enum debug_info_usage usage) |
| { |
| enum debug_struct_file criterion; |
| tree type_decl; |
| bool generic = lang_hooks.types.generic_p (type); |
| |
| if (generic) |
| criterion = debug_struct_generic[usage]; |
| else |
| criterion = debug_struct_ordinary[usage]; |
| |
| if (criterion == DINFO_STRUCT_FILE_NONE) |
| return DUMP_GSTRUCT (type, usage, criterion, generic, false, false); |
| if (criterion == DINFO_STRUCT_FILE_ANY) |
| return DUMP_GSTRUCT (type, usage, criterion, generic, false, true); |
| |
| type_decl = TYPE_STUB_DECL (TYPE_MAIN_VARIANT (type)); |
| |
| if (type_decl != NULL) |
| { |
| if (criterion == DINFO_STRUCT_FILE_SYS && DECL_IN_SYSTEM_HEADER (type_decl)) |
| return DUMP_GSTRUCT (type, usage, criterion, generic, false, true); |
| |
| if (matches_main_base (DECL_SOURCE_FILE (type_decl))) |
| return DUMP_GSTRUCT (type, usage, criterion, generic, true, true); |
| } |
| |
| return DUMP_GSTRUCT (type, usage, criterion, generic, false, false); |
| } |
| |
| /* 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; |
| } |
| |
| /* Switch [BACK] to eh_frame_section. If we don't have an eh_frame_section, |
| switch to the data section instead, and write out a synthetic start label |
| for collect2 the first time around. */ |
| |
| static void |
| switch_to_eh_frame_section (bool back) |
| { |
| 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 /* EH_FRAME_SECTION_NAME */ |
| |
| 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); |
| |
| if (!back) |
| { |
| label = get_file_function_name ("F"); |
| 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)); |
| } |
| } |
| } |
| |
| /* Switch [BACK] to the eh or debug frame table section, depending on |
| FOR_EH. */ |
| |
| static void |
| switch_to_frame_table_section (int for_eh, bool back) |
| { |
| if (for_eh) |
| switch_to_eh_frame_section (back); |
| else |
| { |
| if (!debug_frame_section) |
| debug_frame_section = get_section (DEBUG_FRAME_SECTION, |
| SECTION_DEBUG, NULL); |
| switch_to_section (debug_frame_section); |
| } |
| } |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ |
| |
| 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: |
| case DW_CFA_remember_state: |
| case DW_CFA_restore_state: |
| 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: |
| case DW_CFA_restore_extended: |
| case DW_CFA_undefined: |
| case DW_CFA_same_value: |
| case DW_CFA_def_cfa_register: |
| case DW_CFA_register: |
| case DW_CFA_expression: |
| 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: |
| return dw_cfi_oprnd_loc; |
| |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used. */ |
| |
| 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; |
| |
| case DW_CFA_expression: |
| return dw_cfi_oprnd_loc; |
| |
| default: |
| return dw_cfi_oprnd_unused; |
| } |
| } |
| |
| /* Output one FDE. */ |
| |
| static void |
| output_fde (dw_fde_ref fde, bool for_eh, bool second, |
| char *section_start_label, int fde_encoding, char *augmentation, |
| bool any_lsda_needed, int lsda_encoding) |
| { |
| const char *begin, *end; |
| static unsigned int j; |
| char l1[20], l2[20]; |
| |
| targetm.asm_out.emit_unwind_label (asm_out_file, fde->decl, for_eh, |
| /* empty */ 0); |
| targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, |
| for_eh + j); |
| ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + j); |
| ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + j); |
| 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"); |
| |
| begin = second ? fde->dw_fde_second_begin : fde->dw_fde_begin; |
| end = second ? fde->dw_fde_second_end : fde->dw_fde_end; |
| |
| if (for_eh) |
| { |
| rtx sym_ref = gen_rtx_SYMBOL_REF (Pmode, begin); |
| SYMBOL_REF_FLAGS (sym_ref) |= SYMBOL_FLAG_LOCAL; |
| dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref, false, |
| "FDE initial location"); |
| dw2_asm_output_delta (size_of_encoded_value (fde_encoding), |
| end, begin, "FDE address range"); |
| } |
| else |
| { |
| dw2_asm_output_addr (DWARF2_ADDR_SIZE, begin, "FDE initial location"); |
| dw2_asm_output_delta (DWARF2_ADDR_SIZE, end, 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, second ? "LLSDAC" : "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 = begin; |
| { |
| size_t from, until, i; |
| |
| from = 0; |
| until = vec_safe_length (fde->dw_fde_cfi); |
| |
| if (fde->dw_fde_second_begin == NULL) |
| ; |
| else if (!second) |
| until = fde->dw_fde_switch_cfi_index; |
| else |
| from = fde->dw_fde_switch_cfi_index; |
| |
| for (i = from; i < until; i++) |
| output_cfi ((*fde->dw_fde_cfi)[i], fde, for_eh); |
| } |
| |
| /* If we are to emit a ref/link from function bodies to their frame tables, |
| do it now. This is typically performed to make sure that tables |
| associated with functions are dragged with them and not discarded in |
| garbage collecting links. We need to do this on a per function basis to |
| cope with -ffunction-sections. */ |
| |
| #ifdef ASM_OUTPUT_DWARF_TABLE_REF |
| /* Switch to the function section, emit the ref to the tables, and |
| switch *back* into the table section. */ |
| switch_to_section (function_section (fde->decl)); |
| ASM_OUTPUT_DWARF_TABLE_REF (section_start_label); |
| switch_to_frame_table_section (for_eh, true); |
| #endif |
| |
| /* 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); |
| |
| j += 2; |
| } |
| |
| /* Return true if frame description entry FDE is needed for EH. */ |
| |
| static bool |
| fde_needed_for_eh_p (dw_fde_ref fde) |
| { |
| if (flag_asynchronous_unwind_tables) |
| return true; |
| |
| if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde->decl)) |
| return true; |
| |
| if (fde->uses_eh_lsda) |
| return true; |
| |
| /* If exceptions are enabled, we have collected nothrow info. */ |
| if (flag_exceptions && (fde->all_throwers_are_sibcalls || fde->nothrow)) |
| return false; |
| |
| return true; |
| } |
| |
| /* 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; |
| rtx personality = NULL; |
| int dw_cie_version; |
| |
| /* Don't emit a CIE if there won't be any FDEs. */ |
| if (!fde_vec) |
| return; |
| |
| /* Nothing to do if the assembler's doing it all. */ |
| if (dwarf2out_do_cfi_asm ()) |
| return; |
| |
| /* If we don't have any functions we'll want to unwind out of, don't emit |
| any EH unwind information. 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, an explicit specialization doesn't. */ |
| if (for_eh) |
| { |
| bool any_eh_needed = false; |
| |
| FOR_EACH_VEC_ELT (*fde_vec, i, fde) |
| { |
| if (fde->uses_eh_lsda) |
| any_eh_needed = any_lsda_needed = true; |
| else if (fde_needed_for_eh_p (fde)) |
| any_eh_needed = true; |
| else if (TARGET_USES_WEAK_UNWIND_INFO) |
| targetm.asm_out.emit_unwind_label (asm_out_file, fde->decl, 1, 1); |
| } |
| |
| if (!any_eh_needed) |
| return; |
| } |
| |
| /* We're going to be generating comments, so turn on app. */ |
| if (flag_debug_asm) |
| app_enable (); |
| |
| /* Switch to the proper frame section, first time. */ |
| switch_to_frame_table_section (for_eh, false); |
| |
| 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"); |
| |
| /* Use the CIE version 3 for DWARF3; allow DWARF2 to continue to |
| use CIE version 1, unless that would produce incorrect results |
| due to overflowing the return register column. */ |
| return_reg = DWARF2_FRAME_REG_OUT (DWARF_FRAME_RETURN_COLUMN, for_eh); |
| dw_cie_version = 1; |
| if (return_reg >= 256 || dwarf_version > 2) |
| dw_cie_version = 3; |
| dw2_asm_output_data (1, dw_cie_version, "CIE Version"); |
| |
| augmentation[0] = 0; |
| augmentation_size = 0; |
| |
| personality = current_unit_personality; |
| 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 (personality) |
| { |
| *p++ = 'P'; |
| augmentation_size += 1 + size_of_encoded_value (per_encoding); |
| assemble_external_libcall (personality); |
| } |
| 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 (personality && 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"); |
| if (dw_cie_version >= 4) |
| { |
| dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "CIE Address Size"); |
| dw2_asm_output_data (1, 0, "CIE Segment Size"); |
| } |
| dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor"); |
| dw2_asm_output_data_sleb128 (DWARF_CIE_DATA_ALIGNMENT, |
| "CIE Data Alignment Factor"); |
| |
| 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 (personality) |
| { |
| dw2_asm_output_data (1, per_encoding, "Personality (%s)", |
| eh_data_format_name (per_encoding)); |
| dw2_asm_output_encoded_addr_rtx (per_encoding, |
| personality, |
| 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_EACH_VEC_ELT (*cie_cfi_vec, i, cfi) |
| 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_EACH_VEC_ELT (*fde_vec, i, fde) |
| { |
| unsigned int k; |
| |
| /* Don't emit EH unwind info for leaf functions that don't need it. */ |
| if (for_eh && !fde_needed_for_eh_p (fde)) |
| continue; |
| |
| for (k = 0; k < (fde->dw_fde_second_begin ? 2 : 1); k++) |
| output_fde (fde, for_eh, k, section_start_label, fde_encoding, |
| augmentation, any_lsda_needed, lsda_encoding); |
| } |
| |
| if (for_eh && targetm.terminate_dw2_eh_frame_info) |
| dw2_asm_output_data (4, 0, "End of Table"); |
| |
| /* Turn off app to make assembly quicker. */ |
| if (flag_debug_asm) |
| app_disable (); |
| } |
| |
| /* Emit .cfi_startproc and .cfi_personality/.cfi_lsda if needed. */ |
| |
| static void |
| dwarf2out_do_cfi_startproc (bool second) |
| { |
| int enc; |
| rtx ref; |
| rtx personality = get_personality_function (current_function_decl); |
| |
| fprintf (asm_out_file, "\t.cfi_startproc\n"); |
| |
| if (personality) |
| { |
| enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); |
| ref = personality; |
| |
| /* ??? The GAS support isn't entirely consistent. We have to |
| handle indirect support ourselves, but PC-relative is done |
| in the assembler. Further, the assembler can't handle any |
| of the weirder relocation types. */ |
| if (enc & DW_EH_PE_indirect) |
| ref = dw2_force_const_mem (ref, true); |
| |
| fprintf (asm_out_file, "\t.cfi_personality %#x,", enc); |
| output_addr_const (asm_out_file, ref); |
| fputc ('\n', asm_out_file); |
| } |
| |
| if (crtl->uses_eh_lsda) |
| { |
| char lab[20]; |
| |
| enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); |
| ASM_GENERATE_INTERNAL_LABEL (lab, second ? "LLSDAC" : "LLSDA", |
| current_function_funcdef_no); |
| ref = gen_rtx_SYMBOL_REF (Pmode, lab); |
| SYMBOL_REF_FLAGS (ref) = SYMBOL_FLAG_LOCAL; |
| |
| if (enc & DW_EH_PE_indirect) |
| ref = dw2_force_const_mem (ref, true); |
| |
| fprintf (asm_out_file, "\t.cfi_lsda %#x,", enc); |
| output_addr_const (asm_out_file, ref); |
| fputc ('\n', asm_out_file); |
| } |
| } |
| |
| /* Allocate CURRENT_FDE. Immediately initialize all we can, noting that |
| this allocation may be done before pass_final. */ |
| |
| dw_fde_ref |
| dwarf2out_alloc_current_fde (void) |
| { |
| dw_fde_ref fde; |
| |
| fde = ggc_cleared_alloc<dw_fde_node> (); |
| fde->decl = current_function_decl; |
| fde->funcdef_number = current_function_funcdef_no; |
| fde->fde_index = vec_safe_length (fde_vec); |
| fde->all_throwers_are_sibcalls = crtl->all_throwers_are_sibcalls; |
| fde->uses_eh_lsda = crtl->uses_eh_lsda; |
| fde->nothrow = crtl->nothrow; |
| fde->drap_reg = INVALID_REGNUM; |
| fde->vdrap_reg = INVALID_REGNUM; |
| |
| /* Record the FDE associated with this function. */ |
| cfun->fde = fde; |
| vec_safe_push (fde_vec, fde); |
| |
| return fde; |
| } |
| |
| /* 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; |
| section *fnsec; |
| bool do_frame; |
| |
| current_function_func_begin_label = NULL; |
| |
| do_frame = dwarf2out_do_frame (); |
| |
| /* ??? 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 (!do_frame |
| && (!flag_exceptions |
| || targetm_common.except_unwind_info (&global_options) == UI_SJLJ)) |
| return; |
| |
| fnsec = function_section (current_function_decl); |
| switch_to_section (fnsec); |
| 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; |
| |
| /* We can elide the fde allocation if we're not emitting debug info. */ |
| if (!do_frame) |
| return; |
| |
| /* Cater to the various TARGET_ASM_OUTPUT_MI_THUNK implementations that |
| emit insns as rtx but bypass the bulk of rest_of_compilation, which |
| would include pass_dwarf2_frame. If we've not created the FDE yet, |
| do so now. */ |
| fde = cfun->fde; |
| if (fde == NULL) |
| fde = dwarf2out_alloc_current_fde (); |
| |
| /* Initialize the bits of CURRENT_FDE that were not available earlier. */ |
| fde->dw_fde_begin = dup_label; |
| fde->dw_fde_current_label = dup_label; |
| fde->in_std_section = (fnsec == text_section |
| || (cold_text_section && fnsec == cold_text_section)); |
| |
| /* 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, 0, true); |
| #endif |
| |
| if (dwarf2out_do_cfi_asm ()) |
| dwarf2out_do_cfi_startproc (false); |
| else |
| { |
| rtx personality = get_personality_function (current_function_decl); |
| if (!current_unit_personality) |
| current_unit_personality = personality; |
| |
| /* We cannot keep a current personality per function as without CFI |
| asm, at the point where we emit the CFI data, there is no current |
| function anymore. */ |
| if (personality && current_unit_personality != personality) |
| sorry ("multiple EH personalities are supported only with assemblers " |
| "supporting .cfi_personality directive"); |
| } |
| } |
| |
| /* Output a marker (i.e. a label) for the end of the generated code |
| for a function prologue. This gets called *after* the prologue code has |
| been generated. */ |
| |
| void |
| dwarf2out_vms_end_prologue (unsigned int line ATTRIBUTE_UNUSED, |
| const char *file ATTRIBUTE_UNUSED) |
| { |
| 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, PROLOGUE_END_LABEL, |
| current_function_funcdef_no); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, PROLOGUE_END_LABEL, |
| current_function_funcdef_no); |
| cfun->fde->dw_fde_vms_end_prologue = xstrdup (label); |
| } |
| |
| /* Output a marker (i.e. a label) for the beginning of the generated code |
| for a function epilogue. This gets called *before* the prologue code has |
| been generated. */ |
| |
| void |
| dwarf2out_vms_begin_epilogue (unsigned int line ATTRIBUTE_UNUSED, |
| const char *file ATTRIBUTE_UNUSED) |
| { |
| dw_fde_ref fde = cfun->fde; |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| if (fde->dw_fde_vms_begin_epilogue) |
| return; |
| |
| /* Output a label to mark the endpoint of the code generated for this |
| function. */ |
| ASM_GENERATE_INTERNAL_LABEL (label, EPILOGUE_BEGIN_LABEL, |
| current_function_funcdef_no); |
| ASM_OUTPUT_DEBUG_LABEL (asm_out_file, EPILOGUE_BEGIN_LABEL, |
| current_function_funcdef_no); |
| fde->dw_fde_vms_begin_epilogue = xstrdup (label); |
| } |
| |
| /* 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]; |
| |
| last_var_location_insn = NULL; |
| cached_next_real_insn = NULL; |
| |
| if (dwarf2out_do_cfi_asm ()) |
| fprintf (asm_out_file, "\t.cfi_endproc\n"); |
| |
| /* 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 = cfun->fde; |
| gcc_assert (fde != NULL); |
| if (fde->dw_fde_second_begin == NULL) |
| fde->dw_fde_end = xstrdup (label); |
| } |
| |
| void |
| dwarf2out_frame_finish (void) |
| { |
| /* Output call frame information. */ |
| if (targetm.debug_unwind_info () == UI_DWARF2) |
| output_call_frame_info (0); |
| |
| /* Output another copy for the unwinder. */ |
| if ((flag_unwind_tables || flag_exceptions) |
| && targetm_common.except_unwind_info (&global_options) == UI_DWARF2) |
| output_call_frame_info (1); |
| } |
| |
| /* Note that the current function section is being used for code. */ |
| |
| static void |
| dwarf2out_note_section_used (void) |
| { |
| section *sec = current_function_section (); |
| if (sec == text_section) |
| text_section_used = true; |
| else if (sec == cold_text_section) |
| cold_text_section_used = true; |
| } |
| |
| static void var_location_switch_text_section (void); |
| static void set_cur_line_info_table (section *); |
| |
| void |
| dwarf2out_switch_text_section (void) |
| { |
| section *sect; |
| dw_fde_ref fde = cfun->fde; |
| |
| gcc_assert (cfun && fde && fde->dw_fde_second_begin == NULL); |
| |
| if (!in_cold_section_p) |
| { |
| fde->dw_fde_end = crtl->subsections.cold_section_end_label; |
| fde->dw_fde_second_begin = crtl->subsections.hot_section_label; |
| fde->dw_fde_second_end = crtl->subsections.hot_section_end_label; |
| } |
| else |
| { |
| fde->dw_fde_end = crtl->subsections.hot_section_end_label; |
| fde->dw_fde_second_begin = crtl->subsections.cold_section_label; |
| fde->dw_fde_second_end = crtl->subsections.cold_section_end_label; |
| } |
| have_multiple_function_sections = true; |
| |
| /* There is no need to mark used sections when not debugging. */ |
| if (cold_text_section != NULL) |
| dwarf2out_note_section_used (); |
| |
| if (dwarf2out_do_cfi_asm ()) |
| fprintf (asm_out_file, "\t.cfi_endproc\n"); |
| |
| /* Now do the real section switch. */ |
| sect = current_function_section (); |
| switch_to_section (sect); |
| |
| fde->second_in_std_section |
| = (sect == text_section |
| || (cold_text_section && sect == cold_text_section)); |
| |
| if (dwarf2out_do_cfi_asm ()) |
| dwarf2out_do_cfi_startproc (true); |
| |
| var_location_switch_text_section (); |
| |
| if (cold_text_section != NULL) |
| set_cur_line_info_table (sect); |
| } |
| |
| /* And now, the subset of the debugging information support code necessary |
| for emitting location expressions. */ |
| |
| /* Data about a single source file. */ |
| struct GTY((for_user)) dwarf_file_data { |
| const char * filename; |
| int emitted_number; |
| }; |
| |
| typedef struct GTY(()) deferred_locations_struct |
| { |
| tree variable; |
| dw_die_ref die; |
| } deferred_locations; |
| |
| |
| static GTY(()) vec<deferred_locations, va_gc> *deferred_locations_list; |
| |
| |
| /* Describe an entry into the .debug_addr section. */ |
| |
| enum ate_kind { |
| ate_kind_rtx, |
| ate_kind_rtx_dtprel, |
| ate_kind_label |
| }; |
| |
| typedef struct GTY((for_user)) addr_table_entry_struct { |
| enum ate_kind kind; |
| unsigned int refcount; |
| unsigned int index; |
| union addr_table_entry_struct_union |
| { |
| rtx GTY ((tag ("0"))) rtl; |
| char * GTY ((tag ("1"))) label; |
| } |
| GTY ((desc ("%1.kind"))) addr; |
| } |
| addr_table_entry; |
| |
| /* 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 GTY(()) dw_loc_list_struct { |
| dw_loc_list_ref dw_loc_next; |
| const char *begin; /* Label and addr_entry for start of range */ |
| addr_table_entry *begin_entry; |
| const char *end; /* Label for end 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; |
| hashval_t hash; |
| /* True if all addresses in this and subsequent lists are known to be |
| resolved. */ |
| bool resolved_addr; |
| /* True if this list has been replaced by dw_loc_next. */ |
| bool replaced; |
| bool emitted; |
| /* True if the range should be emitted even if begin and end |
| are the same. */ |
| bool force; |
| } dw_loc_list_node; |
| |
| static dw_loc_descr_ref int_loc_descriptor (HOST_WIDE_INT); |
| |
| /* Convert a DWARF stack opcode into its string name. */ |
| |
| static const char * |
| dwarf_stack_op_name (unsigned int op) |
| { |
| const char *name = get_DW_OP_name (op); |
| |
| if (name != NULL) |
| return name; |
| |
| 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_cleared_alloc<dw_loc_descr_node> (); |
| |
| descr->dw_loc_opc = op; |
| descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const; |
| descr->dw_loc_oprnd1.val_entry = NULL; |
| descr->dw_loc_oprnd1.v.val_unsigned = oprnd1; |
| descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const; |
| descr->dw_loc_oprnd2.val_entry = NULL; |
| descr->dw_loc_oprnd2.v.val_unsigned = oprnd2; |
| |
| return descr; |
| } |
| |
| /* Return a pointer to a newly allocated location description for |
| REG and OFFSET. */ |
| |
| static inline dw_loc_descr_ref |
| new_reg_loc_descr (unsigned int reg, unsigned HOST_WIDE_INT offset) |
| { |
| if (reg <= 31) |
| return new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + reg), |
| offset, 0); |
| else |
| return new_loc_descr (DW_OP_bregx, reg, offset); |
| } |
| |
| /* 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; |
| } |
| |
| /* Compare two location operands for exact equality. */ |
| |
| static bool |
| dw_val_equal_p (dw_val_node *a, dw_val_node *b) |
| { |
| if (a->val_class != b->val_class) |
| return false; |
| switch (a->val_class) |
| { |
| case dw_val_class_none: |
| return true; |
| case dw_val_class_addr: |
| return rtx_equal_p (a->v.val_addr, b->v.val_addr); |
| |
| case dw_val_class_offset: |
| case dw_val_class_unsigned_const: |
| case dw_val_class_const: |
| case dw_val_class_range_list: |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| /* These are all HOST_WIDE_INT, signed or unsigned. */ |
| return a->v.val_unsigned == b->v.val_unsigned; |
| |
| case dw_val_class_loc: |
| return a->v.val_loc == b->v.val_loc; |
| case dw_val_class_loc_list: |
| return a->v.val_loc_list == b->v.val_loc_list; |
| case dw_val_class_die_ref: |
| return a->v.val_die_ref.die == b->v.val_die_ref.die; |
| case dw_val_class_fde_ref: |
| return a->v.val_fde_index == b->v.val_fde_index; |
| case dw_val_class_lbl_id: |
| case dw_val_class_high_pc: |
| return strcmp (a->v.val_lbl_id, b->v.val_lbl_id) == 0; |
| case dw_val_class_str: |
| return a->v.val_str == b->v.val_str; |
| case dw_val_class_flag: |
| return a->v.val_flag == b->v.val_flag; |
| case dw_val_class_file: |
| return a->v.val_file == b->v.val_file; |
| case dw_val_class_decl_ref: |
| return a->v.val_decl_ref == b->v.val_decl_ref; |
| |
| case dw_val_class_const_double: |
| return (a->v.val_double.high == b->v.val_double.high |
| && a->v.val_double.low == b->v.val_double.low); |
| |
| case dw_val_class_wide_int: |
| return *a->v.val_wide == *b->v.val_wide; |
| |
| case dw_val_class_vec: |
| { |
| size_t a_len = a->v.val_vec.elt_size * a->v.val_vec.length; |
| size_t b_len = b->v.val_vec.elt_size * b->v.val_vec.length; |
| |
| return (a_len == b_len |
| && !memcmp (a->v.val_vec.array, b->v.val_vec.array, a_len)); |
| } |
| |
| case dw_val_class_data8: |
| return memcmp (a->v.val_data8, b->v.val_data8, 8) == 0; |
| |
| case dw_val_class_vms_delta: |
| return (!strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1) |
| && !strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1)); |
| } |
| gcc_unreachable (); |
| } |
| |
| /* Compare two location atoms for exact equality. */ |
| |
| static bool |
| loc_descr_equal_p_1 (dw_loc_descr_ref a, dw_loc_descr_ref b) |
| { |
| if (a->dw_loc_opc != b->dw_loc_opc) |
| return false; |
| |
| /* ??? This is only ever set for DW_OP_constNu, for N equal to the |
| address size, but since we always allocate cleared storage it |
| should be zero for other types of locations. */ |
| if (a->dtprel != b->dtprel) |
| return false; |
| |
| return (dw_val_equal_p (&a->dw_loc_oprnd1, &b->dw_loc_oprnd1) |
| && dw_val_equal_p (&a->dw_loc_oprnd2, &b->dw_loc_oprnd2)); |
| } |
| |
| /* Compare two complete location expressions for exact equality. */ |
| |
| bool |
| loc_descr_equal_p (dw_loc_descr_ref a, dw_loc_descr_ref b) |
| { |
| while (1) |
| { |
| if (a == b) |
| return true; |
| if (a == NULL || b == NULL) |
| return false; |
| if (!loc_descr_equal_p_1 (a, b)) |
| return false; |
| |
| a = a->dw_loc_next; |
| b = b->dw_loc_next; |
| } |
| } |
| |
| |
| /* Add a constant OFFSET to a location expression. */ |
| |
| static void |
| loc_descr_plus_const (dw_loc_descr_ref *list_head, HOST_WIDE_INT offset) |
| { |
| dw_loc_descr_ref loc; |
| HOST_WIDE_INT *p; |
| |
| gcc_assert (*list_head != NULL); |
| |
| if (!offset) |
| return; |
| |
| /* Find the end of the chain. */ |
| for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next) |
| ; |
| |
| p = NULL; |
| if (loc->dw_loc_opc == DW_OP_fbreg |
| || (loc->dw_loc_opc >= DW_OP_breg0 && loc->dw_loc_opc <= DW_OP_breg31)) |
| p = &loc->dw_loc_oprnd1.v.val_int; |
| else if (loc->dw_loc_opc == DW_OP_bregx) |
| p = &loc->dw_loc_oprnd2.v.val_int; |
| |
| /* If the last operation is fbreg, breg{0..31,x}, optimize by adjusting its |
| offset. Don't optimize if an signed integer overflow would happen. */ |
| if (p != NULL |
| && ((offset > 0 && *p <= INTTYPE_MAXIMUM (HOST_WIDE_INT) - offset) |
| || (offset < 0 && *p >= INTTYPE_MINIMUM (HOST_WIDE_INT) - offset))) |
| *p += offset; |
| |
| else if (offset > 0) |
| loc->dw_loc_next = new_loc_descr (DW_OP_plus_uconst, offset, 0); |
| |
| else |
| { |
| loc->dw_loc_next = int_loc_descriptor (-offset); |
| add_loc_descr (&loc->dw_loc_next, new_loc_descr (DW_OP_minus, 0, 0)); |
| } |
| } |
| |
| /* Add a constant OFFSET to a location list. */ |
| |
| static void |
| loc_list_plus_const (dw_loc_list_ref list_head, HOST_WIDE_INT offset) |
| { |
| dw_loc_list_ref d; |
| for (d = list_head; d != NULL; d = d->dw_loc_next) |
| loc_descr_plus_const (&d->expr, offset); |
| } |
| |
| #define DWARF_REF_SIZE \ |
| (dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE) |
| |
| static unsigned long int get_base_type_offset (dw_die_ref); |
| |
| /* 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: |
| size += DWARF2_ADDR_SIZE; |
| break; |
| case DW_OP_GNU_addr_index: |
| case DW_OP_GNU_const_index: |
| gcc_assert (loc->dw_loc_oprnd1.val_entry->index != NO_INDEX_ASSIGNED); |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.val_entry->index); |
| 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_bit_piece: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| size += size_of_uleb128 (loc->dw_loc_oprnd2.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 += DWARF_REF_SIZE; |
| break; |
| case DW_OP_implicit_value: |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned) |
| + loc->dw_loc_oprnd1.v.val_unsigned; |
| break; |
| case DW_OP_GNU_implicit_pointer: |
| size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); |
| break; |
| case DW_OP_GNU_entry_value: |
| { |
| unsigned long op_size = size_of_locs (loc->dw_loc_oprnd1.v.val_loc); |
| size += size_of_uleb128 (op_size) + op_size; |
| break; |
| } |
| case DW_OP_GNU_const_type: |
| { |
| unsigned long o |
| = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die); |
| size += size_of_uleb128 (o) + 1; |
| switch (loc->dw_loc_oprnd2.val_class) |
| { |
| case dw_val_class_vec: |
| size += loc->dw_loc_oprnd2.v.val_vec.length |
| * loc->dw_loc_oprnd2.v.val_vec.elt_size; |
| break; |
| case dw_val_class_const: |
| size += HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT; |
| break; |
| case dw_val_class_const_double: |
| size += HOST_BITS_PER_DOUBLE_INT / BITS_PER_UNIT; |
| break; |
| case dw_val_class_wide_int: |
| size += (get_full_len (*loc->dw_loc_oprnd2.v.val_wide) |
| * HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| } |
| case DW_OP_GNU_regval_type: |
| { |
| unsigned long o |
| = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die); |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned) |
| + size_of_uleb128 (o); |
| } |
| break; |
| case DW_OP_GNU_deref_type: |
| { |
| unsigned long o |
| = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die); |
| size += 1 + size_of_uleb128 (o); |
| } |
| break; |
| case DW_OP_GNU_convert: |
| case DW_OP_GNU_reinterpret: |
| if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const) |
| size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); |
| else |
| { |
| unsigned long o |
| = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die); |
| size += size_of_uleb128 (o); |
| } |
| break; |
| case DW_OP_GNU_parameter_ref: |
| size += 4; |
| break; |
| default: |
| break; |
| } |
| |
| return size; |
| } |
| |
| /* Return the size of a series of location descriptors. */ |
| |
| 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; |
| } |
| |
| static HOST_WIDE_INT extract_int (const unsigned char *, unsigned); |
| static void get_ref_die_offset_label (char *, dw_die_ref); |
| static unsigned long int get_ref_die_offset (dw_die_ref); |
| |
| /* Output location description stack opcode's operands (if any). |
| The for_eh_or_skip parameter controls whether register numbers are |
| converted using DWARF2_FRAME_REG_OUT, which is needed in the case that |
| hard reg numbers have been processed via DWARF_FRAME_REGNUM (i.e. for unwind |
| info). This should be suppressed for the cases that have not been converted |
| (i.e. symbolic debug info), by setting the parameter < 0. See PR47324. */ |
| |
| static void |
| output_loc_operands (dw_loc_descr_ref loc, int for_eh_or_skip) |
| { |
| 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_const2u: |
| case DW_OP_const2s: |
| dw2_asm_output_data (2, val1->v.val_int, NULL); |
| break; |
| case DW_OP_const4u: |
| if (loc->dtprel) |
| { |
| gcc_assert (targetm.asm_out.output_dwarf_dtprel); |
| targetm.asm_out.output_dwarf_dtprel (asm_out_file, 4, |
| val1->v.val_addr); |
| fputc ('\n', asm_out_file); |
| break; |
| } |
| /* FALLTHRU */ |
| case DW_OP_const4s: |
| dw2_asm_output_data (4, val1->v.val_int, NULL); |
| break; |
| case DW_OP_const8u: |
| if (loc->dtprel) |
| { |
| gcc_assert (targetm.asm_out.output_dwarf_dtprel); |
| targetm.asm_out.output_dwarf_dtprel (asm_out_file, 8, |
| val1->v.val_addr); |
| fputc ('\n', asm_out_file); |
| break; |
| } |
| /* FALLTHRU */ |
| case DW_OP_const8s: |
| gcc_assert (HOST_BITS_PER_WIDE_INT >= 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; |
| case DW_OP_implicit_value: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| switch (val2->val_class) |
| { |
| case dw_val_class_const: |
| dw2_asm_output_data (val1->v.val_unsigned, val2->v.val_int, NULL); |
| break; |
| case dw_val_class_vec: |
| { |
| unsigned int elt_size = val2->v.val_vec.elt_size; |
| unsigned int len = val2->v.val_vec.length; |
| unsigned int i; |
| unsigned char *p; |
| |
| if (elt_size > sizeof (HOST_WIDE_INT)) |
| { |
| elt_size /= 2; |
| len *= 2; |
| } |
| for (i = 0, p = val2->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_const_double: |
| { |
| unsigned HOST_WIDE_INT first, second; |
| |
| if (WORDS_BIG_ENDIAN) |
| { |
| first = val2->v.val_double.high; |
| second = val2->v.val_double.low; |
| } |
| else |
| { |
| first = val2->v.val_double.low; |
| second = val2->v.val_double.high; |
| } |
| dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR, |
| first, NULL); |
| dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR, |
| second, NULL); |
| } |
| break; |
| case dw_val_class_wide_int: |
| { |
| int i; |
| int len = get_full_len (*val2->v.val_wide); |
| if (WORDS_BIG_ENDIAN) |
| for (i = len - 1; i >= 0; --i) |
| dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR, |
| val2->v.val_wide->elt (i), NULL); |
| else |
| for (i = 0; i < len; ++i) |
| dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR, |
| val2->v.val_wide->elt (i), NULL); |
| } |
| break; |
| case dw_val_class_addr: |
| gcc_assert (val1->v.val_unsigned == DWARF2_ADDR_SIZE); |
| dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, val2->v.val_addr, NULL); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| #else |
| 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: |
| case DW_OP_implicit_value: |
| /* 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: |
| { |
| unsigned r = val1->v.val_unsigned; |
| if (for_eh_or_skip >= 0) |
| r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); |
| gcc_assert (size_of_uleb128 (r) |
| == size_of_uleb128 (val1->v.val_unsigned)); |
| dw2_asm_output_data_uleb128 (r, NULL); |
| } |
| break; |
| case DW_OP_fbreg: |
| dw2_asm_output_data_sleb128 (val1->v.val_int, NULL); |
| break; |
| case DW_OP_bregx: |
| { |
| unsigned r = val1->v.val_unsigned; |
| if (for_eh_or_skip >= 0) |
| r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); |
| gcc_assert (size_of_uleb128 (r) |
| == size_of_uleb128 (val1->v.val_unsigned)); |
| dw2_asm_output_data_uleb128 (r, 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_bit_piece: |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| dw2_asm_output_data_uleb128 (val2->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 DW_OP_addr: |
| if (loc->dtprel) |
| { |
| 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 (); |
| } |
| else |
| { |
| #ifdef DWARF2_DEBUGGING_INFO |
| dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, val1->v.val_addr, NULL); |
| #else |
| gcc_unreachable (); |
| #endif |
| } |
| break; |
| |
| case DW_OP_GNU_addr_index: |
| case DW_OP_GNU_const_index: |
| gcc_assert (loc->dw_loc_oprnd1.val_entry->index != NO_INDEX_ASSIGNED); |
| dw2_asm_output_data_uleb128 (loc->dw_loc_oprnd1.val_entry->index, |
| "(index into .debug_addr)"); |
| break; |
| |
| case DW_OP_GNU_implicit_pointer: |
| { |
| char label[MAX_ARTIFICIAL_LABEL_BYTES |
| + HOST_BITS_PER_WIDE_INT / 2 + 2]; |
| gcc_assert (val1->val_class == dw_val_class_die_ref); |
| get_ref_die_offset_label (label, val1->v.val_die_ref.die); |
| dw2_asm_output_offset (DWARF_REF_SIZE, label, debug_info_section, NULL); |
| dw2_asm_output_data_sleb128 (val2->v.val_int, NULL); |
| } |
| break; |
| |
| case DW_OP_GNU_entry_value: |
| dw2_asm_output_data_uleb128 (size_of_locs (val1->v.val_loc), NULL); |
| output_loc_sequence (val1->v.val_loc, for_eh_or_skip); |
| break; |
| |
| case DW_OP_GNU_const_type: |
| { |
| unsigned long o = get_base_type_offset (val1->v.val_die_ref.die), l; |
| gcc_assert (o); |
| dw2_asm_output_data_uleb128 (o, NULL); |
| switch (val2->val_class) |
| { |
| case dw_val_class_const: |
| l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; |
| dw2_asm_output_data (1, l, NULL); |
| dw2_asm_output_data (l, val2->v.val_int, NULL); |
| break; |
| case dw_val_class_vec: |
| { |
| unsigned int elt_size = val2->v.val_vec.elt_size; |
| unsigned int len = val2->v.val_vec.length; |
| unsigned int i; |
| unsigned char *p; |
| |
| l = len * elt_size; |
| dw2_asm_output_data (1, l, NULL); |
| if (elt_size > sizeof (HOST_WIDE_INT)) |
| { |
| elt_size /= 2; |
| len *= 2; |
| } |
| for (i = 0, p = val2->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_const_double: |
| { |
| unsigned HOST_WIDE_INT first, second; |
| l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; |
| |
| dw2_asm_output_data (1, 2 * l, NULL); |
| if (WORDS_BIG_ENDIAN) |
| { |
| first = val2->v.val_double.high; |
| second = val2->v.val_double.low; |
| } |
| else |
| { |
| first = val2->v.val_double.low; |
| second = val2->v.val_double.high; |
| } |
| dw2_asm_output_data (l, first, NULL); |
| dw2_asm_output_data (l, second, NULL); |
| } |
| break; |
| case dw_val_class_wide_int: |
| { |
| int i; |
| int len = get_full_len (*val2->v.val_wide); |
| l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; |
| |
| dw2_asm_output_data (1, len * l, NULL); |
| if (WORDS_BIG_ENDIAN) |
| for (i = len - 1; i >= 0; --i) |
| dw2_asm_output_data (l, val2->v.val_wide->elt (i), NULL); |
| else |
| for (i = 0; i < len; ++i) |
| dw2_asm_output_data (l, val2->v.val_wide->elt (i), NULL); |
| } |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| break; |
| case DW_OP_GNU_regval_type: |
| { |
| unsigned r = val1->v.val_unsigned; |
| unsigned long o = get_base_type_offset (val2->v.val_die_ref.die); |
| gcc_assert (o); |
| if (for_eh_or_skip >= 0) |
| { |
| r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); |
| gcc_assert (size_of_uleb128 (r) |
| == size_of_uleb128 (val1->v.val_unsigned)); |
| } |
| dw2_asm_output_data_uleb128 (r, NULL); |
| dw2_asm_output_data_uleb128 (o, NULL); |
| } |
| break; |
| case DW_OP_GNU_deref_type: |
| { |
| unsigned long o = get_base_type_offset (val2->v.val_die_ref.die); |
| gcc_assert (o); |
| dw2_asm_output_data (1, val1->v.val_int, NULL); |
| dw2_asm_output_data_uleb128 (o, NULL); |
| } |
| break; |
| case DW_OP_GNU_convert: |
| case DW_OP_GNU_reinterpret: |
| if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const) |
| dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); |
| else |
| { |
| unsigned long o = get_base_type_offset (val1->v.val_die_ref.die); |
| gcc_assert (o); |
| dw2_asm_output_data_uleb128 (o, NULL); |
| } |
| break; |
| |
| case DW_OP_GNU_parameter_ref: |
| { |
| unsigned long o; |
| gcc_assert (val1->val_class == dw_val_class_die_ref); |
| o = get_ref_die_offset (val1->v.val_die_ref.die); |
| dw2_asm_output_data (4, o, NULL); |
| } |
| break; |
| |
| default: |
| /* Other codes have no operands. */ |
| break; |
| } |
| } |
| |
| /* Output a sequence of location operations. |
| The for_eh_or_skip parameter controls whether register numbers are |
| converted using DWARF2_FRAME_REG_OUT, which is needed in the case that |
| hard reg numbers have been processed via DWARF_FRAME_REGNUM (i.e. for unwind |
| info). This should be suppressed for the cases that have not been converted |
| (i.e. symbolic debug info), by setting the parameter < 0. See PR47324. */ |
| |
| void |
| output_loc_sequence (dw_loc_descr_ref loc, int for_eh_or_skip) |
| { |
| for (; loc != NULL; loc = loc->dw_loc_next) |
| { |
| enum dwarf_location_atom opc = loc->dw_loc_opc; |
| /* Output the opcode. */ |
| if (for_eh_or_skip >= 0 |
| && opc >= DW_OP_breg0 && opc <= DW_OP_breg31) |
| { |
| unsigned r = (opc - DW_OP_breg0); |
| r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); |
| gcc_assert (r <= 31); |
| opc = (enum dwarf_location_atom) (DW_OP_breg0 + r); |
| } |
| else if (for_eh_or_skip >= 0 |
| && opc >= DW_OP_reg0 && opc <= DW_OP_reg31) |
| { |
| unsigned r = (opc - DW_OP_reg0); |
| r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); |
| gcc_assert (r <= 31); |
| opc = (enum dwarf_location_atom) (DW_OP_reg0 + r); |
| } |
| |
| dw2_asm_output_data (1, opc, |
| "%s", dwarf_stack_op_name (opc)); |
| |
| /* Output the operand(s) (if any). */ |
| output_loc_operands (loc, for_eh_or_skip); |
| } |
| } |
| |
| /* Output location description stack opcode's operands (if any). |
| The output is single bytes on a line, suitable for .cfi_escape. */ |
| |
| static void |
| output_loc_operands_raw (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) |
| { |
| case DW_OP_addr: |
| case DW_OP_GNU_addr_index: |
| case DW_OP_GNU_const_index: |
| case DW_OP_implicit_value: |
| /* We cannot output addresses in .cfi_escape, only bytes. */ |
| gcc_unreachable (); |
| |
| case DW_OP_const1u: |
| case DW_OP_const1s: |
| case DW_OP_pick: |
| case DW_OP_deref_size: |
| case DW_OP_xderef_size: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_raw (1, val1->v.val_int); |
| break; |
| |
| case DW_OP_const2u: |
| case DW_OP_const2s: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_raw (2, val1->v.val_int); |
| break; |
| |
| case DW_OP_const4u: |
| case DW_OP_const4s: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_raw (4, val1->v.val_int); |
| break; |
| |
| case DW_OP_const8u: |
| case DW_OP_const8s: |
| gcc_assert (HOST_BITS_PER_WIDE_INT >= 64); |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_raw (8, val1->v.val_int); |
| 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); |
| |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_raw (2, offset); |
| } |
| break; |
| |
| case DW_OP_regx: |
| { |
| unsigned r = DWARF2_FRAME_REG_OUT (val1->v.val_unsigned, 1); |
| gcc_assert (size_of_uleb128 (r) |
| == size_of_uleb128 (val1->v.val_unsigned)); |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_uleb128_raw (r); |
| } |
| break; |
| |
| case DW_OP_constu: |
| case DW_OP_plus_uconst: |
| case DW_OP_piece: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned); |
| break; |
| |
| case DW_OP_bit_piece: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned); |
| dw2_asm_output_data_uleb128_raw (val2->v.val_unsigned); |
| break; |
| |
| case DW_OP_consts: |
| 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: |
| case DW_OP_fbreg: |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_sleb128_raw (val1->v.val_int); |
| break; |
| |
| case DW_OP_bregx: |
| { |
| unsigned r = DWARF2_FRAME_REG_OUT (val1->v.val_unsigned, 1); |
| gcc_assert (size_of_uleb128 (r) |
| == size_of_uleb128 (val1->v.val_unsigned)); |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_uleb128_raw (r); |
| fputc (',', asm_out_file); |
| dw2_asm_output_data_sleb128_raw (val2->v.val_int); |
| } |
| break; |
| |
| case DW_OP_GNU_implicit_pointer: |
| case DW_OP_GNU_entry_value: |
| case DW_OP_GNU_const_type: |
| case DW_OP_GNU_regval_type: |
| case DW_OP_GNU_deref_type: |
| case DW_OP_GNU_convert: |
| case DW_OP_GNU_reinterpret: |
| case DW_OP_GNU_parameter_ref: |
| gcc_unreachable (); |
| break; |
| |
| default: |
| /* Other codes have no operands. */ |
| break; |
| } |
| } |
| |
| void |
| output_loc_sequence_raw (dw_loc_descr_ref loc) |
| { |
| while (1) |
| { |
| enum dwarf_location_atom opc = loc->dw_loc_opc; |
| /* Output the opcode. */ |
| if (opc >= DW_OP_breg0 && opc <= DW_OP_breg31) |
| { |
| unsigned r = (opc - DW_OP_breg0); |
| r = DWARF2_FRAME_REG_OUT (r, 1); |
| gcc_assert (r <= 31); |
| opc = (enum dwarf_location_atom) (DW_OP_breg0 + r); |
| } |
| else if (opc >= DW_OP_reg0 && opc <= DW_OP_reg31) |
| { |
| unsigned r = (opc - DW_OP_reg0); |
| r = DWARF2_FRAME_REG_OUT (r, 1); |
| gcc_assert (r <= 31); |
| opc = (enum dwarf_location_atom) (DW_OP_reg0 + r); |
| } |
| /* Output the opcode. */ |
| fprintf (asm_out_file, "%#x", opc); |
| output_loc_operands_raw (loc); |
| |
| if (!loc->dw_loc_next) |
| break; |
| loc = loc->dw_loc_next; |
| |
| fputc (',', asm_out_file); |
| } |
| } |
| |
| /* This function builds a dwarf location descriptor sequence from a |
| dw_cfa_location, adding the given OFFSET to the result of the |
| expression. */ |
| |
| struct dw_loc_descr_node * |
| build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset) |
| { |
| struct dw_loc_descr_node *head, *tmp; |
| |
| offset += cfa->offset; |
| |
| if (cfa->indirect) |
| { |
| head = new_reg_loc_descr (cfa->reg, cfa->base_offset); |
| head->dw_loc_oprnd1.val_class = dw_val_class_const; |
| head->dw_loc_oprnd1.val_entry = NULL; |
| 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 |
| head = new_reg_loc_descr (cfa->reg, offset); |
| |
| return head; |
| } |
| |
| /* This function builds a dwarf location descriptor sequence for |
| the address at OFFSET from the CFA when stack is aligned to |
| ALIGNMENT byte. */ |
| |
| struct dw_loc_descr_node * |
| build_cfa_aligned_loc (dw_cfa_location *cfa, |
| HOST_WIDE_INT offset, HOST_WIDE_INT alignment) |
| { |
| struct dw_loc_descr_node *head; |
| unsigned int dwarf_fp |
| = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM); |
| |
| /* When CFA is defined as FP+OFFSET, emulate stack alignment. */ |
| if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0) |
| { |
| head = new_reg_loc_descr (dwarf_fp, 0); |
| add_loc_descr (&head, int_loc_descriptor (alignment)); |
| add_loc_descr (&head, new_loc_descr (DW_OP_and, 0, 0)); |
| loc_descr_plus_const (&head, offset); |
| } |
| else |
| head = new_reg_loc_descr (dwarf_fp, offset); |
| return head; |
| } |
| |
| /* And now, the support for symbolic debugging information. */ |
| |
| /* .debug_str support. */ |
| |
| static void dwarf2out_init (const char *); |
| static void dwarf2out_finish (const char *); |
| static void dwarf2out_assembly_start (void); |
| 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_function_decl (tree); |
| static void dwarf2out_begin_block (unsigned, unsigned); |
| static void dwarf2out_end_block (unsigned, unsigned); |
| static bool dwarf2out_ignore_block (const_tree); |
| static void dwarf2out_global_decl (tree); |
| static void dwarf2out_type_decl (tree, int); |
| static void dwarf2out_imported_module_or_decl (tree, tree, tree, bool); |
| static void dwarf2out_imported_module_or_decl_1 (tree, tree, tree, |
| dw_die_ref); |
| static void dwarf2out_abstract_function (tree); |
| static void dwarf2out_var_location (rtx_insn *); |
| static void dwarf2out_begin_function (tree); |
| static void dwarf2out_end_function (unsigned int); |
| static void dwarf2out_register_main_translation_unit (tree unit); |
| static void dwarf2out_set_name (tree, tree); |
| |
| /* The debug hooks structure. */ |
| |
| const struct gcc_debug_hooks dwarf2_debug_hooks = |
| { |
| dwarf2out_init, |
| dwarf2out_finish, |
| dwarf2out_assembly_start, |
| 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, |
| #if VMS_DEBUGGING_INFO |
| dwarf2out_vms_end_prologue, |
| dwarf2out_vms_begin_epilogue, |
| #else |
| debug_nothing_int_charstar, |
| debug_nothing_int_charstar, |
| #endif |
| dwarf2out_end_epilogue, |
| dwarf2out_begin_function, |
| dwarf2out_end_function, /* end_function */ |
| dwarf2out_register_main_translation_unit, |
| dwarf2out_function_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_code_label, /* label */ |
| debug_nothing_int, /* handle_pch */ |
| dwarf2out_var_location, |
| dwarf2out_switch_text_section, |
| dwarf2out_set_name, |
| 1, /* start_end_main_source_file */ |
| TYPE_SYMTAB_IS_DIE /* tree_type_symtab_field */ |
| }; |
| |
| /* 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. */ |
| |
| /* Whether to put type DIEs into their own section .debug_types instead |
| of making them part of the .debug_info section. Only supported for |
| Dwarf V4 or higher and the user didn't disable them through |
| -fno-debug-types-section. It is more efficient to put them in a |
| separate comdat sections since the linker will then be able to |
| remove duplicates. But not all tools support .debug_types sections |
| yet. */ |
| |
| #define use_debug_types (dwarf_version >= 4 && flag_debug_types_section) |
| |
| /* 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 pubname_struct *pubname_ref; |
| typedef struct dw_ranges_struct *dw_ranges_ref; |
| typedef struct dw_ranges_by_label_struct *dw_ranges_by_label_ref; |
| typedef struct comdat_type_struct *comdat_type_node_ref; |
| |
| /* The entries in the line_info table more-or-less mirror the opcodes |
| that are used in the real dwarf line table. Arrays of these entries |
| are collected per section when DWARF2_ASM_LINE_DEBUG_INFO is not |
| supported. */ |
| |
| enum dw_line_info_opcode { |
| /* Emit DW_LNE_set_address; the operand is the label index. */ |
| LI_set_address, |
| |
| /* Emit a row to the matrix with the given line. This may be done |
| via any combination of DW_LNS_copy, DW_LNS_advance_line, and |
| special opcodes. */ |
| LI_set_line, |
| |
| /* Emit a DW_LNS_set_file. */ |
| LI_set_file, |
| |
| /* Emit a DW_LNS_set_column. */ |
| LI_set_column, |
| |
| /* Emit a DW_LNS_negate_stmt; the operand is ignored. */ |
| LI_negate_stmt, |
| |
| /* Emit a DW_LNS_set_prologue_end/epilogue_begin; the operand is ignored. */ |
| LI_set_prologue_end, |
| LI_set_epilogue_begin, |
| |
| /* Emit a DW_LNE_set_discriminator. */ |
| LI_set_discriminator |
| }; |
| |
| typedef struct GTY(()) dw_line_info_struct { |
| enum dw_line_info_opcode opcode; |
| unsigned int val; |
| } dw_line_info_entry; |
| |
| |
| typedef struct GTY(()) dw_line_info_table_struct { |
| /* The label that marks the end of this section. */ |
| const char *end_label; |
| |
| /* The values for the last row of the matrix, as collected in the table. |
| These are used to minimize the changes to the next row. */ |
| unsigned int file_num; |
| unsigned int line_num; |
| unsigned int column_num; |
| int discrim_num; |
| bool is_stmt; |
| bool in_use; |
| |
| vec<dw_line_info_entry, va_gc> *entries; |
| } dw_line_info_table; |
| |
| typedef dw_line_info_table *dw_line_info_table_p; |
| |
| |
| /* 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 GTY(()) dw_attr_struct { |
| enum dwarf_attribute dw_attr; |
| dw_val_node dw_attr_val; |
| } |
| dw_attr_node; |
| |
| |
| /* 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 GTY((chain_circular ("%h.die_sib"), for_user)) die_struct { |
| union die_symbol_or_type_node |
| { |
| const char * GTY ((tag ("0"))) die_symbol; |
| comdat_type_node_ref GTY ((tag ("1"))) die_type_node; |
| } |
| GTY ((desc ("%0.comdat_type_p"))) die_id; |
| vec<dw_attr_node, va_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; |
| unsigned int decl_id; |
| enum dwarf_tag die_tag; |
| /* Die is used and must not be pruned as unused. */ |
| BOOL_BITFIELD die_perennial_p : 1; |
| BOOL_BITFIELD comdat_type_p : 1; /* DIE has a type signature */ |
| /* Lots of spare bits. */ |
| } |
| 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 GTY(()) pubname_struct { |
| dw_die_ref die; |
| const char *name; |
| } |
| pubname_entry; |
| |
| |
| struct GTY(()) dw_ranges_struct { |
| /* If this is positive, it's a block number, otherwise it's a |
| bitwise-negated index into dw_ranges_by_label. */ |
| int num; |
| }; |
| |
| /* A structure to hold a macinfo entry. */ |
| |
| typedef struct GTY(()) macinfo_struct { |
| unsigned char code; |
| unsigned HOST_WIDE_INT lineno; |
| const char *info; |
| } |
| macinfo_entry; |
| |
| |
| struct GTY(()) dw_ranges_by_label_struct { |
| const char *begin; |
| const char *end; |
| }; |
| |
| /* The comdat type node structure. */ |
| typedef struct GTY(()) comdat_type_struct |
| { |
| dw_die_ref root_die; |
| dw_die_ref type_die; |
| dw_die_ref skeleton_die; |
| char signature[DWARF_TYPE_SIGNATURE_SIZE]; |
| struct comdat_type_struct *next; |
| } |
| comdat_type_node; |
| |
| /* The limbo die list structure. */ |
| typedef struct GTY(()) limbo_die_struct { |
| dw_die_ref die; |
| tree created_for; |
| struct limbo_die_struct *next; |
| } |
| limbo_die_node; |
| |
| typedef struct skeleton_chain_struct |
| { |
| dw_die_ref old_die; |
| dw_die_ref new_die; |
| struct skeleton_chain_struct *parent; |
| } |
| skeleton_chain_node; |
| |
| /* Define a macro which returns nonzero for a TYPE_DECL which was |
| implicitly generated for a type. |
| |
| Note that, unlike the C 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 C++ 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. Likewise with the Ada |
| front-end, but for each type, tagged or not. */ |
| |
| #define TYPE_DECL_IS_STUB(decl) \ |
| (DECL_NAME (decl) == NULL_TREE \ |
| || (DECL_ARTIFICIAL (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 the DWARF comdat type unit header. */ |
| #define DWARF_COMDAT_TYPE_UNIT_HEADER_SIZE \ |
| (DWARF_COMPILE_UNIT_HEADER_SIZE + DWARF_TYPE_SIGNATURE_SIZE \ |
| + DWARF_OFFSET_SIZE) |
| |
| /* Fixed size portion of public names info. */ |
| #define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2) |
| |
| /* Fixed size portion of the address range info. */ |
| #define DWARF_ARANGES_HEADER_SIZE \ |
| (DWARF_ROUND (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 ((int)DW_LNS_set_isa + 1) |
| |
| /* 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 |
| |
| /* Maximum number of operations per instruction bundle. */ |
| #ifndef DWARF_LINE_DEFAULT_MAX_OPS_PER_INSN |
| #define DWARF_LINE_DEFAULT_MAX_OPS_PER_INSN 1 |
| #endif |
| |
| /* This location is used by calc_die_sizes() to keep track |
| the offset of each DIE within the .debug_info section. */ |
| static unsigned long next_die_offset; |
| |
| /* Record the root of the DIE's built for the current compilation unit. */ |
| static GTY(()) dw_die_ref single_comp_unit_die; |
| |
| /* A list of type DIEs that have been separated into comdat sections. */ |
| static GTY(()) comdat_type_node *comdat_type_list; |
| |
| /* A list of DIEs with a NULL parent waiting to be relocated. */ |
| static GTY(()) limbo_die_node *limbo_die_list; |
| |
| /* A list of DIEs for which we may have to generate |
| DW_AT_{,MIPS_}linkage_name once their DECL_ASSEMBLER_NAMEs are set. */ |
| static GTY(()) limbo_die_node *deferred_asm_name; |
| |
| struct dwarf_file_hasher : ggc_hasher<dwarf_file_data *> |
| { |
| typedef const char *compare_type; |
| |
| static hashval_t hash (dwarf_file_data *); |
| static bool equal (dwarf_file_data *, const char *); |
| }; |
| |
| /* Filenames referenced by this compilation unit. */ |
| static GTY(()) hash_table<dwarf_file_hasher> *file_table; |
| |
| struct decl_die_hasher : ggc_hasher<die_node *> |
| { |
| typedef tree compare_type; |
| |
| static hashval_t hash (die_node *); |
| static bool equal (die_node *, tree); |
| }; |
| /* 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 (()) hash_table<decl_die_hasher> *decl_die_table; |
| |
| struct block_die_hasher : ggc_hasher<die_struct *> |
| { |
| static hashval_t hash (die_struct *); |
| static bool equal (die_struct *, die_struct *); |
| }; |
| |
| /* A hash table of references to DIE's that describe COMMON blocks. |
| The key is DECL_UID() ^ die_parent. */ |
| static GTY (()) hash_table<block_die_hasher> *common_block_die_table; |
| |
| typedef struct GTY(()) die_arg_entry_struct { |
| dw_die_ref die; |
| tree arg; |
| } die_arg_entry; |
| |
| |
| /* Node of the variable location list. */ |
| struct GTY ((chain_next ("%h.next"))) var_loc_node { |
| /* Either NOTE_INSN_VAR_LOCATION, or, for SRA optimized variables, |
| EXPR_LIST chain. For small bitsizes, bitsize is encoded |
| in mode of the EXPR_LIST node and first EXPR_LIST operand |
| is either NOTE_INSN_VAR_LOCATION for a piece with a known |
| location or NULL for padding. For larger bitsizes, |
| mode is 0 and first operand is a CONCAT with bitsize |
| as first CONCAT operand and NOTE_INSN_VAR_LOCATION resp. |
| NULL as second operand. */ |
| rtx GTY (()) loc; |
| const char * GTY (()) label; |
| struct var_loc_node * GTY (()) next; |
| }; |
| |
| /* Variable location list. */ |
| struct GTY ((for_user)) var_loc_list_def { |
| struct var_loc_node * GTY (()) first; |
| |
| /* Pointer to the last but one or last element of the |
| chained list. If the list is empty, both first and |
| last are NULL, if the list contains just one node |
| or the last node certainly is not redundant, it points |
| to the last node, otherwise points to the last but one. |
| Do not mark it for GC because it is marked through the chain. */ |
| struct var_loc_node * GTY ((skip ("%h"))) last; |
| |
| /* Pointer to the last element before section switch, |
| if NULL, either sections weren't switched or first |
| is after section switch. */ |
| struct var_loc_node * GTY ((skip ("%h"))) last_before_switch; |
| |
| /* DECL_UID of the variable decl. */ |
| unsigned int decl_id; |
| }; |
| typedef struct var_loc_list_def var_loc_list; |
| |
| /* Call argument location list. */ |
| struct GTY ((chain_next ("%h.next"))) call_arg_loc_node { |
| rtx GTY (()) call_arg_loc_note; |
| const char * GTY (()) label; |
| tree GTY (()) block; |
| bool tail_call_p; |
| rtx GTY (()) symbol_ref; |
| struct call_arg_loc_node * GTY (()) next; |
| }; |
| |
| |
| struct decl_loc_hasher : ggc_hasher<var_loc_list *> |
| { |
| typedef const_tree compare_type; |
| |
| static hashval_t hash (var_loc_list *); |
| static bool equal (var_loc_list *, const_tree); |
| }; |
| |
| /* Table of decl location linked lists. */ |
| static GTY (()) hash_table<decl_loc_hasher> *decl_loc_table; |
| |
| /* Head and tail of call_arg_loc chain. */ |
| static GTY (()) struct call_arg_loc_node *call_arg_locations; |
| static struct call_arg_loc_node *call_arg_loc_last; |
| |
| /* Number of call sites in the current function. */ |
| static int call_site_count = -1; |
| /* Number of tail call sites in the current function. */ |
| static int tail_call_site_count = -1; |
| |
| /* Vector mapping block numbers to DW_TAG_{lexical_block,inlined_subroutine} |
| DIEs. */ |
| static vec<dw_die_ref> block_map; |
| |
| /* A cached location list. */ |
| struct GTY ((for_user)) cached_dw_loc_list_def { |
| /* The DECL_UID of the decl that this entry describes. */ |
| unsigned int decl_id; |
| |
| /* The cached location list. */ |
| dw_loc_list_ref loc_list; |
| }; |
| typedef struct cached_dw_loc_list_def cached_dw_loc_list; |
| |
| struct dw_loc_list_hasher : ggc_hasher<cached_dw_loc_list *> |
| { |
| |
| typedef const_tree compare_type; |
| |
| static hashval_t hash (cached_dw_loc_list *); |
| static bool equal (cached_dw_loc_list *, const_tree); |
| }; |
| |
| /* Table of cached location lists. */ |
| static GTY (()) hash_table<dw_loc_list_hasher> *cached_dw_loc_list_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 global counter for generating labels for line number data. */ |
| static unsigned int line_info_label_num; |
| |
| /* The current table to which we should emit line number information |
| for the current function. This will be set up at the beginning of |
| assembly for the function. */ |
| static dw_line_info_table *cur_line_info_table; |
| |
| /* The two default tables of line number info. */ |
| static GTY(()) dw_line_info_table *text_section_line_info; |
| static GTY(()) dw_line_info_table *cold_text_section_line_info; |
| |
| /* The set of all non-default tables of line number info. */ |
| static GTY(()) vec<dw_line_info_table_p, va_gc> *separate_line_info; |
| |
| /* A flag to tell pubnames/types export if there is an info section to |
| refer to. */ |
| static bool info_section_emitted; |
| |
| /* A pointer to the base of a table that contains a list of publicly |
| accessible names. */ |
| static GTY (()) vec<pubname_entry, va_gc> *pubname_table; |
| |
| /* A pointer to the base of a table that contains a list of publicly |
| accessible types. */ |
| static GTY (()) vec<pubname_entry, va_gc> *pubtype_table; |
| |
| /* A pointer to the base of a table that contains a list of macro |
| defines/undefines (and file start/end markers). */ |
| static GTY (()) vec<macinfo_entry, va_gc> *macinfo_table; |
| |
| /* True if .debug_macinfo or .debug_macros section is going to be |
| emitted. */ |
| #define have_macinfo \ |
| (debug_info_level >= DINFO_LEVEL_VERBOSE \ |
| && !macinfo_table->is_empty ()) |
| |
| /* 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; |
| |
| /* Array of pairs of labels referenced in ranges_table. */ |
| static GTY ((length ("ranges_by_label_allocated"))) |
| dw_ranges_by_label_ref ranges_by_label; |
| |
| /* Number of elements currently allocated for ranges_by_label. */ |
| static GTY(()) unsigned ranges_by_label_allocated; |
| |
| /* Number of elements in ranges_by_label currently in use. */ |
| static GTY(()) unsigned ranges_by_label_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; |
| |
| /* Unique label counter for point-of-call tables. */ |
| static GTY(()) unsigned int poc_label_num; |
| |
| /* 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; |
| |
| static GTY(()) vec<die_arg_entry, va_gc> *tmpl_value_parm_die_table; |
| |
| /* Instances of generic types for which we need to generate debug |
| info that describe their generic parameters and arguments. That |
| generation needs to happen once all types are properly laid out so |
| we do it at the end of compilation. */ |
| static GTY(()) vec<tree, va_gc> *generic_type_instances; |
| |
| /* Offset from the "steady-state frame pointer" to the frame base, |
| within the current function. */ |
| static HOST_WIDE_INT frame_pointer_fb_offset; |
| static bool frame_pointer_fb_offset_valid; |
| |
| static vec<dw_die_ref> base_types; |
| |
| /* Flags to represent a set of attribute classes for attributes that represent |
| a scalar value (bounds, pointers, ...). */ |
| enum dw_scalar_form |
| { |
| dw_scalar_form_constant = 0x01, |
| dw_scalar_form_exprloc = 0x02, |
| dw_scalar_form_reference = 0x04 |
| }; |
| |
| /* Forward declarations for functions defined in this file. */ |
| |
| static int is_pseudo_reg (const_rtx); |
| static tree type_main_variant (tree); |
| static int is_tagged_type (const_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 (const_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 inline unsigned int AT_index (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_double (dw_die_ref, enum dwarf_attribute, |
| HOST_WIDE_INT, unsigned HOST_WIDE_INT); |
| static inline void add_AT_vec (dw_die_ref, enum dwarf_attribute, unsigned int, |
| unsigned int, unsigned char *); |
| static void add_AT_data8 (dw_die_ref, enum dwarf_attribute, unsigned char *); |
| static void add_AT_string (dw_die_ref, enum dwarf_attribute, const char *); |
| static inline const char *AT_string (dw_attr_ref); |
| static enum dwarf_form 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 addr_table_entry *add_addr_table_entry (void *, enum ate_kind); |
| static void remove_addr_table_entry (addr_table_entry *); |
| static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx, bool); |
| 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, bool); |
| 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_cxx (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 dw_die_ref strip_naming_typedef (tree, dw_die_ref); |
| static dw_die_ref lookup_type_die_strip_naming_typedef (tree); |
| static void equate_type_number_to_die (tree, dw_die_ref); |
| static dw_die_ref lookup_decl_die (tree); |
| static var_loc_list *lookup_decl_loc (const_tree); |
| static void equate_decl_number_to_die (tree, dw_die_ref); |
| static struct var_loc_node *add_var_loc_to_decl (tree, rtx, const char *); |
| static void print_spaces (FILE *); |
| static void print_die (dw_die_ref, 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 void checksum_sleb128 (HOST_WIDE_INT, struct md5_ctx *); |
| static void checksum_uleb128 (unsigned HOST_WIDE_INT, struct md5_ctx *); |
| static void loc_checksum_ordered (dw_loc_descr_ref, struct md5_ctx *); |
| static void attr_checksum_ordered (enum dwarf_tag, dw_attr_ref, |
| struct md5_ctx *, int *); |
| struct checksum_attributes; |
| static void collect_checksum_attributes (struct checksum_attributes *, dw_die_ref); |
| static void die_checksum_ordered (dw_die_ref, struct md5_ctx *, int *); |
| static void checksum_die_context (dw_die_ref, struct md5_ctx *); |
| static void generate_type_signature (dw_die_ref, comdat_type_node *); |
| static int same_loc_p (dw_loc_descr_ref, dw_loc_descr_ref, int *); |
| static int same_dw_val_p (const dw_val_node *, const 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 inline bool is_template_instantiation (dw_die_ref); |
| static void assign_symbol_names (dw_die_ref); |
| static void break_out_includes (dw_die_ref); |
| static int is_declaration_die (dw_die_ref); |
| static int should_move_die_to_comdat (dw_die_ref); |
| static dw_die_ref clone_as_declaration (dw_die_ref); |
| static dw_die_ref clone_die (dw_die_ref); |
| static dw_die_ref clone_tree (dw_die_ref); |
| static dw_die_ref copy_declaration_context (dw_die_ref, dw_die_ref); |
| static void generate_skeleton_ancestor_tree (skeleton_chain_node *); |
| static void generate_skeleton_bottom_up (skeleton_chain_node *); |
| static dw_die_ref generate_skeleton (dw_die_ref); |
| static dw_die_ref remove_child_or_replace_with_skeleton (dw_die_ref, |
| dw_die_ref, |
| dw_die_ref); |
| static void break_out_comdat_types (dw_die_ref); |
| static void copy_decls_for_unworthy_types (dw_die_ref); |
| |
| static void add_sibling_attributes (dw_die_ref); |
| static void output_location_lists (dw_die_ref); |
| static int constant_size (unsigned HOST_WIDE_INT); |
| static unsigned long size_of_die (dw_die_ref); |
| static void calc_die_sizes (dw_die_ref); |
| static void calc_base_type_die_sizes (void); |
| static void mark_dies (dw_die_ref); |
| static void unmark_dies (dw_die_ref); |
| static void unmark_all_dies (dw_die_ref); |
| static unsigned long size_of_pubnames (vec<pubname_entry, va_gc> *); |
| static unsigned long size_of_aranges (void); |
| static enum dwarf_form value_format |