| /* Output Dwarf2 format symbol table information from GCC. |
| Copyright (C) 1992-2022 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 "target.h" |
| #include "function.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "memmodel.h" |
| #include "tm_p.h" |
| #include "stringpool.h" |
| #include "insn-config.h" |
| #include "ira.h" |
| #include "cgraph.h" |
| #include "diagnostic.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "version.h" |
| #include "flags.h" |
| #include "rtlhash.h" |
| #include "reload.h" |
| #include "output.h" |
| #include "expr.h" |
| #include "dwarf2out.h" |
| #include "dwarf2ctf.h" |
| #include "dwarf2asm.h" |
| #include "toplev.h" |
| #include "md5.h" |
| #include "tree-pretty-print.h" |
| #include "print-rtl.h" |
| #include "debug.h" |
| #include "common/common-target.h" |
| #include "langhooks.h" |
| #include "lra.h" |
| #include "dumpfile.h" |
| #include "opts.h" |
| #include "tree-dfa.h" |
| #include "gdb/gdb-index.h" |
| #include "rtl-iter.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "file-prefix-map.h" /* remap_debug_filename() */ |
| |
| static void dwarf2out_source_line (unsigned int, 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); |
| static bool is_redundant_typedef (const_tree); |
| |
| #ifndef XCOFF_DEBUGGING_INFO |
| #define XCOFF_DEBUGGING_INFO 0 |
| #endif |
| |
| #ifndef HAVE_XCOFF_DWARF_EXTRAS |
| #define HAVE_XCOFF_DWARF_EXTRAS 0 |
| #endif |
| |
| #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; |
| |
| /* 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 const char *debug_macinfo_section_name; |
| static unsigned macinfo_label_base = 1; |
| 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_line_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_ranges_dwo_section; |
| static GTY(()) section *debug_frame_section; |
| |
| /* Maximum size (in bytes) of an artificially generated label. */ |
| #define MAX_ARTIFICIAL_LABEL_BYTES 40 |
| |
| /* 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 |
| |
| #ifndef DWARF_INITIAL_LENGTH_SIZE_STR |
| #define DWARF_INITIAL_LENGTH_SIZE_STR (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_ptr_hash<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; |
| |
| static GTY (()) hash_table<indirect_string_hasher> *debug_line_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 four forms: DW_FORM_string, DW_FORM_strp, |
| DW_FORM_line_strp or DW_FORM_strx/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; |
| |
| /* The default cold text section. */ |
| static GTY(()) section *cold_text_section; |
| |
| /* True if currently in text section. */ |
| static GTY(()) bool in_text_section_p = false; |
| |
| /* Last debug-on location in corresponding section. */ |
| static GTY(()) const char *last_text_label; |
| static GTY(()) const char *last_cold_label; |
| |
| /* Mark debug-on/off locations per section. |
| NULL means the section is not used at all. */ |
| static GTY(()) vec<const char *, va_gc> *switch_text_ranges; |
| static GTY(()) vec<const char *, va_gc> *switch_cold_ranges; |
| |
| /* 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 void output_call_frame_info (int); |
| |
| /* Personality decl of current unit. Used only when assembler does not support |
| personality CFI. */ |
| static GTY(()) rtx current_unit_personality; |
| |
| /* Whether an eh_frame section is required. */ |
| static GTY(()) bool do_eh_frame = false; |
| |
| /* .debug_rnglists next index. */ |
| static unsigned int rnglist_idx; |
| |
| /* 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_SECOND_SECT_LABEL |
| #define FUNC_SECOND_SECT_LABEL "LFSB" |
| #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. */ |
| |
| static unsigned int |
| get_full_len (const wide_int &op) |
| { |
| int prec = wi::get_precision (op); |
| 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) |
| { |
| if (debug_info_level <= DINFO_LEVEL_TERSE) |
| return false; |
| |
| 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); |
| } |
| |
| /* 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 ATTRIBUTE_UNUSED) |
| { |
| 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; |
| |
| #ifdef EH_FRAME_SECTION_NAME |
| eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL); |
| #else |
| eh_frame_section = ((flags == SECTION_WRITE) |
| ? data_section : readonly_data_section); |
| #endif /* EH_FRAME_SECTION_NAME */ |
| } |
| |
| switch_to_section (eh_frame_section); |
| |
| #ifdef EH_FRAME_THROUGH_COLLECT2 |
| /* We have no special eh_frame section. Emit special labels to guide |
| collect2. */ |
| if (!back) |
| { |
| tree 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)); |
| } |
| #endif |
| } |
| |
| /* 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: |
| case DW_CFA_val_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: |
| case DW_CFA_val_expression: |
| return dw_cfi_oprnd_loc; |
| |
| case DW_CFA_def_cfa_expression: |
| return dw_cfi_oprnd_cfa_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[MAX_ARTIFICIAL_LABEL_BYTES], l2[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| 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 (!XCOFF_DEBUGGING_INFO || 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, |
| "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[MAX_ARTIFICIAL_LABEL_BYTES], l2[MAX_ARTIFICIAL_LABEL_BYTES]; |
| char section_start_label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| 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 (!XCOFF_DEBUGGING_INFO || 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; |
| |
| fprintf (asm_out_file, "\t.cfi_startproc\n"); |
| |
| targetm.asm_out.post_cfi_startproc (asm_out_file, current_function_decl); |
| |
| /* .cfi_personality and .cfi_lsda are only relevant to DWARF2 |
| eh unwinders. */ |
| if (targetm_common.except_unwind_info (&global_options) != UI_DWARF2) |
| return; |
| |
| rtx personality = get_personality_function (current_function_decl); |
| |
| 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) |
| { |
| if (targetm.asm_out.make_eh_symbol_indirect != NULL) |
| ref = targetm.asm_out.make_eh_symbol_indirect (ref, true); |
| else |
| 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[MAX_ARTIFICIAL_LABEL_BYTES]; |
| |
| 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) |
| { |
| if (targetm.asm_out.make_eh_symbol_indirect != NULL) |
| ref = targetm.asm_out.make_eh_symbol_indirect (ref, true); |
| else |
| 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, |
| unsigned int column 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.cc 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 FDE allocation if we're not emitting frame unwind info. */ |
| if (!do_frame) |
| return; |
| |
| /* Unlike the debug version, the EH version of frame unwind info is a per- |
| function setting so we need to record whether we need it for the unit. */ |
| do_eh_frame |= dwarf2out_do_eh_frame (); |
| |
| /* 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)); |
| fde->ignored_debug = DECL_IGNORED_P (current_function_decl); |
| in_text_section_p = fnsec == 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, column, 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); |
| } |
| |
| /* Mark the ranges of non-debug subsections in the std text sections. */ |
| |
| static void |
| mark_ignored_debug_section (dw_fde_ref fde, bool second) |
| { |
| bool std_section; |
| const char *begin_label, *end_label; |
| const char **last_end_label; |
| vec<const char *, va_gc> **switch_ranges; |
| |
| if (second) |
| { |
| std_section = fde->second_in_std_section; |
| begin_label = fde->dw_fde_second_begin; |
| end_label = fde->dw_fde_second_end; |
| } |
| else |
| { |
| std_section = fde->in_std_section; |
| begin_label = fde->dw_fde_begin; |
| end_label = fde->dw_fde_end; |
| } |
| |
| if (!std_section) |
| return; |
| |
| if (in_text_section_p) |
| { |
| last_end_label = &last_text_label; |
| switch_ranges = &switch_text_ranges; |
| } |
| else |
| { |
| last_end_label = &last_cold_label; |
| switch_ranges = &switch_cold_ranges; |
| } |
| |
| if (fde->ignored_debug) |
| { |
| if (*switch_ranges && !(vec_safe_length (*switch_ranges) & 1)) |
| vec_safe_push (*switch_ranges, *last_end_label); |
| } |
| else |
| { |
| *last_end_label = end_label; |
| |
| if (!*switch_ranges) |
| vec_alloc (*switch_ranges, 16); |
| else if (vec_safe_length (*switch_ranges) & 1) |
| vec_safe_push (*switch_ranges, begin_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); |
| |
| mark_ignored_debug_section (fde, fde->dw_fde_second_begin != NULL); |
| } |
| |
| 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 (do_eh_frame) |
| output_call_frame_info (1); |
| } |
| |
| static void var_location_switch_text_section (void); |
| static void set_cur_line_info_table (section *); |
| |
| void |
| dwarf2out_switch_text_section (void) |
| { |
| char label[MAX_ARTIFICIAL_LABEL_BYTES]; |
| section *sect; |
| dw_fde_ref fde = cfun->fde; |
| |
| gcc_assert (cfun && fde && fde->dw_fde_second_begin == NULL); |
| |
| ASM_GENERATE_INTERNAL_LABEL (label, FUNC_SECOND_SECT_LABEL, |
| current_function_funcdef_no); |
| |
| fde->dw_fde_second_begin = ggc_strdup (label); |
| if (!in_cold_section_p) |
| { |
| fde->dw_fde_end = crtl->subsections.cold_section_end_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_end = crtl->subsections.cold_section_end_label; |
| } |
| have_multiple_function_sections = true; |
| |
| if (dwarf2out_do_cfi_asm ()) |
| fprintf (asm_out_file, "\t.cfi_endproc\n"); |
| |
| mark_ignored_debug_section (fde, false); |
| |
| /* 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)); |
| in_text_section_p = sect == 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. */ |
| |
| /* Describe an entry into the .debug_addr section. */ |
| |
| enum ate_kind { |
| ate_kind_rtx, |
| ate_kind_rtx_dtprel, |
| ate_kind_label |
| }; |
| |
| struct GTY((for_user)) addr_table_entry { |
| 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; |
| }; |
| |
| typedef unsigned int var_loc_view; |
| |
| /* 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 */ |
| addr_table_entry *end_entry; |
| char *ll_symbol; /* Label for beginning of location list. |
| Only on head of list. */ |
| char *vl_symbol; /* Label for beginning of view list. Ditto. */ |
| const char *section; /* Section this loclist is relative to */ |
| dw_loc_descr_ref expr; |
| var_loc_view vbegin, vend; |
| 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; |
| /* True if it has been emitted into .debug_loc* / .debug_loclists* |
| section. */ |
| unsigned char emitted : 1; |
| /* True if hash field is index rather than hash value. */ |
| unsigned char num_assigned : 1; |
| /* True if .debug_loclists.dwo offset has been emitted for it already. */ |
| unsigned char offset_emitted : 1; |
| /* True if note_variable_value_in_expr has been called on it. */ |
| unsigned char noted_variable_value : 1; |
| /* 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 (poly_int64); |
| static dw_loc_descr_ref uint_loc_descriptor (unsigned 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 TRUE iff we're to output location view lists as a separate |
| attribute next to the location lists, as an extension compatible |
| with DWARF 2 and above. */ |
| |
| static inline bool |
| dwarf2out_locviews_in_attribute () |
| { |
| return debug_variable_location_views == 1; |
| } |
| |
| /* Return TRUE iff we're to output location view lists as part of the |
| location lists, as proposed for standardization after DWARF 5. */ |
| |
| static inline bool |
| dwarf2out_locviews_in_loclist () |
| { |
| #ifndef DW_LLE_view_pair |
| return false; |
| #else |
| return debug_variable_location_views == -1; |
| #endif |
| } |
| |
| /* 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; |
| } |
| |
| /* 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_unsigned_const_implicit: |
| case dw_val_class_const_implicit: |
| case dw_val_class_range_list: |
| /* 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_view_list: |
| return a->v.val_view_list == b->v.val_view_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_symview: |
| return strcmp (a->v.val_symbolic_view, b->v.val_symbolic_view) == 0; |
| case dw_val_class_lbl_id: |
| case dw_val_class_lineptr: |
| case dw_val_class_macptr: |
| case dw_val_class_loclistsptr: |
| 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: |
| case dw_val_class_file_implicit: |
| 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.lbl2, b->v.val_vms_delta.lbl2)); |
| |
| case dw_val_class_discr_value: |
| return (a->v.val_discr_value.pos == b->v.val_discr_value.pos |
| && a->v.val_discr_value.v.uval == b->v.val_discr_value.v.uval); |
| case dw_val_class_discr_list: |
| /* It makes no sense comparing two discriminant value lists. */ |
| return false; |
| } |
| 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 POLY_OFFSET to a location expression. */ |
| |
| static void |
| loc_descr_plus_const (dw_loc_descr_ref *list_head, poly_int64 poly_offset) |
| { |
| dw_loc_descr_ref loc; |
| HOST_WIDE_INT *p; |
| |
| gcc_assert (*list_head != NULL); |
| |
| if (known_eq (poly_offset, 0)) |
| return; |
| |
| /* Find the end of the chain. */ |
| for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next) |
| ; |
| |
| HOST_WIDE_INT offset; |
| if (!poly_offset.is_constant (&offset)) |
| { |
| loc->dw_loc_next = int_loc_descriptor (poly_offset); |
| add_loc_descr (&loc->dw_loc_next, new_loc_descr (DW_OP_plus, 0, 0)); |
| return; |
| } |
| |
| 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 |
| = uint_loc_descriptor (-(unsigned HOST_WIDE_INT) offset); |
| add_loc_descr (&loc->dw_loc_next, new_loc_descr (DW_OP_minus, 0, 0)); |
| } |
| } |
| |
| /* 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, poly_int64 offset) |
| { |
| HOST_WIDE_INT const_offset; |
| if (offset.is_constant (&const_offset)) |
| { |
| if (reg <= 31) |
| return new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + reg), |
| const_offset, 0); |
| else |
| return new_loc_descr (DW_OP_bregx, reg, const_offset); |
| } |
| else |
| { |
| dw_loc_descr_ref ret = new_reg_loc_descr (reg, 0); |
| loc_descr_plus_const (&ret, offset); |
| return ret; |
| } |
| } |
| |
| /* Add a constant OFFSET to a location list. */ |
| |
| static void |
| loc_list_plus_const (dw_loc_list_ref list_head, poly_int64 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) |
| |
| /* The number of bits that can be encoded by largest DW_FORM_dataN. |
| In DWARF4 and earlier it is DW_FORM_data8 with 64 bits, in DWARF5 |
| DW_FORM_data16 with 128 bits. */ |
| #define DWARF_LARGEST_DATA_FORM_BITS \ |
| (dwarf_version >= 5 ? 128 : 64) |
| |
| /* Utility inline function for construction of ops that were GNU extension |
| before DWARF 5. */ |
| static inline enum dwarf_location_atom |
| dwarf_OP (enum dwarf_location_atom op) |
| { |
| switch (op) |
| { |
| case DW_OP_implicit_pointer: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_implicit_pointer; |
| break; |
| |
| case DW_OP_entry_value: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_entry_value; |
| break; |
| |
| case DW_OP_const_type: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_const_type; |
| break; |
| |
| case DW_OP_regval_type: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_regval_type; |
| break; |
| |
| case DW_OP_deref_type: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_deref_type; |
| break; |
| |
| case DW_OP_convert: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_convert; |
| break; |
| |
| case DW_OP_reinterpret: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_reinterpret; |
| break; |
| |
| case DW_OP_addrx: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_addr_index; |
| break; |
| |
| case DW_OP_constx: |
| if (dwarf_version < 5) |
| return DW_OP_GNU_const_index; |
| break; |
| |
| default: |
| break; |
| } |
| return op; |
| } |
| |
| /* Similarly for attributes. */ |
| static inline enum dwarf_attribute |
| dwarf_AT (enum dwarf_attribute at) |
| { |
| switch (at) |
| { |
| case DW_AT_call_return_pc: |
| if (dwarf_version < 5) |
| return DW_AT_low_pc; |
| break; |
| |
| case DW_AT_call_tail_call: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_tail_call; |
| break; |
| |
| case DW_AT_call_origin: |
| if (dwarf_version < 5) |
| return DW_AT_abstract_origin; |
| break; |
| |
| case DW_AT_call_target: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_call_site_target; |
| break; |
| |
| case DW_AT_call_target_clobbered: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_call_site_target_clobbered; |
| break; |
| |
| case DW_AT_call_parameter: |
| if (dwarf_version < 5) |
| return DW_AT_abstract_origin; |
| break; |
| |
| case DW_AT_call_value: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_call_site_value; |
| break; |
| |
| case DW_AT_call_data_value: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_call_site_data_value; |
| break; |
| |
| case DW_AT_call_all_calls: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_all_call_sites; |
| break; |
| |
| case DW_AT_call_all_tail_calls: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_all_tail_call_sites; |
| break; |
| |
| case DW_AT_dwo_name: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_dwo_name; |
| break; |
| |
| case DW_AT_addr_base: |
| if (dwarf_version < 5) |
| return DW_AT_GNU_addr_base; |
| break; |
| |
| default: |
| break; |
| } |
| return at; |
| } |
| |
| /* And similarly for tags. */ |
| static inline enum dwarf_tag |
| dwarf_TAG (enum dwarf_tag tag) |
| { |
| switch (tag) |
| { |
| case DW_TAG_call_site: |
| if (dwarf_version < 5) |
| return DW_TAG_GNU_call_site; |
| break; |
| |
| case DW_TAG_call_site_parameter: |
| if (dwarf_version < 5) |
| return DW_TAG_GNU_call_site_parameter; |
| break; |
| |
| default: |
| break; |
| } |
| return tag; |
| } |
| |
| /* And similarly for forms. */ |
| static inline enum dwarf_form |
| dwarf_FORM (enum dwarf_form form) |
| { |
| switch (form) |
| { |
| case DW_FORM_addrx: |
| if (dwarf_version < 5) |
| return DW_FORM_GNU_addr_index; |
| break; |
| |
| case DW_FORM_strx: |
| if (dwarf_version < 5) |
| return DW_FORM_GNU_str_index; |
| break; |
| |
| default: |
| break; |
| } |
| return form; |
| } |
| |
| 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_addrx: |
| case DW_OP_GNU_const_index: |
| case DW_OP_constx: |
| 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: |
| case DW_OP_GNU_variable_value: |
| 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_implicit_pointer: |
| case DW_OP_GNU_implicit_pointer: |
| size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); |
| break; |
| case DW_OP_entry_value: |
| 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_const_type: |
| 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_regval_type: |
| 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_deref_type: |
| 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_convert: |
| case DW_OP_reinterpret: |
| 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; |
| } |
| |
| /* Return the size of the value in a DW_AT_discr_value attribute. */ |
| |
| static int |
| size_of_discr_value (dw_discr_value *discr_value) |
| { |
| if (discr_value->pos) |
| return size_of_uleb128 (discr_value->v.uval); |
| else |
| return size_of_sleb128 (discr_value->v.sval); |
| } |
| |
| /* Return the size of the value in a DW_AT_discr_list attribute. */ |
| |
| static int |
| size_of_discr_list (dw_discr_list_ref discr_list) |
| { |
| int size = 0; |
| |
| for (dw_discr_list_ref list = discr_list; |
| list != NULL; |
| list = list->dw_discr_next) |
| { |
| /* One byte for the discriminant value descriptor, and then one or two |
| LEB128 numbers, depending on whether it's a single case label or a |
| range label. */ |
| size += 1; |
| size += size_of_discr_value (&list->dw_discr_lower_bound); |
| if (list->dw_discr_range != 0) |
| size += size_of_discr_value (&list->dw_discr_upper_bound); |
| } |
| 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 = (unsigned char *) 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_addrx: |
| case DW_OP_GNU_const_index: |
| case DW_OP_constx: |
| 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_call2: |
| case DW_OP_call4: |
| { |
| unsigned long die_offset |
| = get_ref_die_offset (val1->v.val_die_ref.die); |
| /* Make sure the offset has been computed and that we can encode it as |
| an operand. */ |
| gcc_assert (die_offset > 0 |
| && die_offset <= (loc->dw_loc_opc == DW_OP_call2 |
| ? 0xffff |
| : 0xffffffff)); |
| dw2_asm_output_data ((loc->dw_loc_opc == DW_OP_call2) ? 2 : 4, |
| die_offset, NULL); |
| } |
| break; |
| |
| case DW_OP_call_ref: |
| case DW_OP_GNU_variable_value: |
| { |
| 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); |
| } |
| break; |
| |
| case DW_OP_implicit_pointer: |
| 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_entry_value: |
| 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_const_type: |
| 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 = (unsigned char *) 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_regval_type: |
| 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_deref_type: |
| 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_convert: |
| case DW_OP_reinterpret: |
| 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_addrx: |
| case DW_OP_GNU_const_index: |
| case DW_OP_constx: |
| 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_implicit_pointer: |
| case DW_OP_entry_value: |
| case DW_OP_const_type: |
| case DW_OP_regval_type: |
| case DW_OP_deref_type: |
| case DW_OP_convert: |
| case DW_OP_reinterpret: |
| 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); |
| } |
| } |
| |
| static void |
| build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno) |
| { |
| if (regno <= 31) |
| add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom) |
| (DW_OP_breg0 + regno), 0, 0)); |
| else |
| add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0)); |
| } |
| |
| /* Build a dwarf location for a cfa_reg spanning multiple |
| consecutive registers. */ |
| |
| struct dw_loc_descr_node * |
| build_span_loc (struct cfa_reg reg) |
| { |
| struct dw_loc_descr_node *head = NULL; |
| |
| gcc_assert (reg.span_width > 0); |
| gcc_assert (reg.span > 1); |
| |
| /* Start from the highest number register as it goes in the upper bits. */ |
| unsigned int regno = reg.reg + reg.span - 1; |
| build_breg_loc (&head, regno); |
| |
| /* Deal with the remaining registers in the span. */ |
| for (int i = reg.span - 2; i >= 0; i--) |
| { |
| add_loc_descr (&head, int_loc_descriptor (reg.span_width * 8)); |
| add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0)); |
| regno--; |
| build_breg_loc (&head, regno); |
| add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0)); |
| } |
| return head; |
| } |
| |
| /* 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, poly_int64 offset) |
| { |
| struct dw_loc_descr_node *head, *tmp; |
| |
| offset += cfa->offset; |
| |
| if (cfa->reg.span > 1) |
| { |
| head = build_span_loc (cfa->reg); |
| |
| if (maybe_ne (offset, 0)) |
| loc_descr_plus_const (&head, offset); |
| } |
| else if (cfa->indirect) |
| { |
| head = new_reg_loc_descr (cfa->reg.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); |
| loc_descr_plus_const (&head, offset); |
| } |
| else |
| head = new_reg_loc_descr (cfa->reg.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, |
| poly_int64 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.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_early_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_set_ignored_loc (unsigned, unsigned, const char *); |
| static void dwarf2out_early_global_decl (tree); |
| static void dwarf2out_late_global_decl (tree); |
| static void dwarf2out_type_decl (tree, int); |
| static void dwarf2out_imported_module_or_decl (tree, tree, tree, bool, 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_inline_entry (tree); |
| static void dwarf2out_size_function (tree); |
| 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); |
| static void dwarf2out_register_external_die (tree decl, const char *sym, |
| unsigned HOST_WIDE_INT off); |
| static bool dwarf2out_die_ref_for_decl (tree decl, const char **sym, |
| unsigned HOST_WIDE_INT *off); |
| |
| /* The debug hooks structure. */ |
| |
| const struct gcc_debug_hooks dwarf2_debug_hooks = |
| { |
| dwarf2out_init, |
| dwarf2out_finish, |
| dwarf2out_early_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_set_ignored_loc, |
| 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_early_global_decl, |
| dwarf2out_late_global_decl, |
| dwarf2out_type_decl, /* type_decl */ |
| dwarf2out_imported_module_or_decl, |
| dwarf2out_die_ref_for_decl, |
| dwarf2out_register_external_die, |
| 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_inline_entry, /* inline_entry */ |
| dwarf2out_size_function, /* size_function */ |
| dwarf2out_switch_text_section, |
| dwarf2out_set_name, |
| 1, /* start_end_main_source_file */ |
| TYPE_SYMTAB_IS_DIE /* tree_type_symtab_field */ |
| }; |
| |
| const struct gcc_debug_hooks dwarf2_lineno_debug_hooks = |
| { |
| dwarf2out_init, |
| debug_nothing_charstar, |
| debug_nothing_charstar, |
| dwarf2out_assembly_start, |
| debug_nothing_int_charstar, |
| debug_nothing_int_charstar, |
| debug_nothing_int_charstar, |
| debug_nothing_int, |
| debug_nothing_int_int, /* begin_block */ |
| debug_nothing_int_int, /* end_block */ |
| debug_true_const_tree, /* ignore_block */ |
| dwarf2out_source_line, /* source_line */ |
| debug_nothing_int_int_charstar, /* set_ignored_loc */ |
| debug_nothing_int_int_charstar, /* begin_prologue */ |
| debug_nothing_int_charstar, /* end_prologue */ |
| debug_nothing_int_charstar, /* begin_epilogue */ |
| debug_nothing_int_charstar, /* end_epilogue */ |
| debug_nothing_tree, /* begin_function */ |
| debug_nothing_int, /* end_function */ |
| debug_nothing_tree, /* register_main_translation_unit */ |
| debug_nothing_tree, /* function_decl */ |
| debug_nothing_tree, /* early_global_decl */ |
| debug_nothing_tree, /* late_global_decl */ |
| debug_nothing_tree_int, /* type_decl */ |
| debug_nothing_tree_tree_tree_bool_bool,/* imported_module_or_decl */ |
| debug_false_tree_charstarstar_uhwistar,/* die_ref_for_decl */ |
| debug_nothing_tree_charstar_uhwi, /* register_external_die */ |
| debug_nothing_tree, /* deferred_inline_function */ |
| debug_nothing_tree, /* outlining_inline_function */ |
| debug_nothing_rtx_code_label, /* label */ |
| debug_nothing_int, /* handle_pch */ |
| debug_nothing_rtx_insn, /* var_location */ |
| debug_nothing_tree, /* inline_entry */ |
| debug_nothing_tree, /* size_function */ |
| debug_nothing_void, /* switch_text_section */ |
| debug_nothing_tree_tree, /* set_name */ |
| 0, /* start_end_main_source_file */ |
| TYPE_SYMTAB_IS_ADDRESS /* 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. For Dwarf V5 or higher .debug_types doesn't exist any more, |
| it is DW_UT_type unit type in .debug_info section. For late LTO |
| debug there should be almost no types emitted so avoid enabling |
| -fdebug-types-section there. */ |
| |
| #define use_debug_types (dwarf_version >= 4 \ |
| && flag_debug_types_section \ |
| && !in_lto_p) |
| |
| /* Various DIE's use offsets relative to the beginning of the |
| .debug_info section to refer to each other. */ |
| |
| typedef long int dw_offset; |
| |
| struct comdat_type_node; |
| |
| /* 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, |
| |
| /* Output a Fixed Advance PC; the target PC is the label index; the |
| base PC is the previous LI_adv_address or LI_set_address entry. |
| We only use this when emitting debug views without assembler |
| support, at explicit user request. Ideally, we should only use |
| it when the offset might be zero but we can't tell: it's the only |
| way to maybe change the PC without resetting the view number. */ |
| LI_adv_address |
| }; |
| |
| typedef struct GTY(()) dw_line_info_struct { |
| enum dw_line_info_opcode opcode; |
| unsigned int val; |
| } dw_line_info_entry; |
| |
| |
| struct GTY(()) dw_line_info_table { |
| /* 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; |
| |
| /* This denotes the NEXT view number. |
| |
| If it is 0, it is known that the NEXT view will be the first view |
| at the given PC. |
| |
| If it is -1, we're forcing the view number to be reset, e.g. at a |
| function entry. |
| |
| The meaning of other nonzero values depends on whether we're |
| computing views internally or leaving it for the assembler to do |
| so. If we're emitting them internally, view denotes the view |
| number since the last known advance of PC. If we're leaving it |
| for the assembler, it denotes the LVU label number that we're |
| going to ask the assembler to assign. */ |
| var_loc_view view; |
| |
| /* This counts the number of symbolic views emitted in this table |
| since the latest view reset. Its max value, over all tables, |
| sets symview_upper_bound. */ |
| var_loc_view symviews_since_reset; |
| |
| #define FORCE_RESET_NEXT_VIEW(x) ((x) = (var_loc_view)-1) |
| #define RESET_NEXT_VIEW(x) ((x) = (var_loc_view)0) |
| #define FORCE_RESETTING_VIEW_P(x) ((x) == (var_loc_view)-1) |
| #define RESETTING_VIEW_P(x) ((x) == (var_loc_view)0 || FORCE_RESETTING_VIEW_P (x)) |
| |
| vec<dw_line_info_entry, va_gc> *entries; |
| }; |
| |
| /* This is an upper bound for view numbers that the assembler may |
| assign to symbolic views output in this translation. It is used to |
| decide how big a field to use to represent view numbers in |
| symview-classed attributes. */ |
| |
| static var_loc_view symview_upper_bound; |
| |
| /* If we're keep track of location views and their reset points, and |
| INSN is a reset point (i.e., it necessarily advances the PC), mark |
| the next view in TABLE as reset. */ |
| |
| static void |
| maybe_reset_location_view (rtx_insn *insn, dw_line_info_table *table) |
| { |
| if (!debug_internal_reset_location_views) |
| return; |
| |
| /* Maybe turn (part of?) this test into a default target hook. */ |
| int reset = 0; |
| |
| if (targetm.reset_location_view) |
| reset = targetm.reset_location_view (insn); |
| |
| if (reset) |
| ; |
| else if (JUMP_TABLE_DATA_P (insn)) |
| reset = 1; |
| else if (GET_CODE (insn) == USE |
| || GET_CODE (insn) == CLOBBER |
| || GET_CODE (insn) == ASM_INPUT |
| || asm_noperands (insn) >= 0) |
| ; |
| else if (get_attr_min_length (insn) > 0) |
| reset = 1; |
| |
| if (reset > 0 && !RESETTING_VIEW_P (table->view)) |
| RESET_NEXT_VIEW (table->view); |
| } |
| |
| /* 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 *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 */ |
| /* For an external ref to die_symbol if die_offset contains an extra |
| offset to that symbol. */ |
| BOOL_BITFIELD with_offset : 1; |
| /* Whether this DIE was removed from the DIE tree, for example via |
| prune_unused_types. We don't consider those present from the |
| DIE lookup routines. */ |
| BOOL_BITFIELD removed : 1; |
| /* Lots of spare bits. */ |
| } |
| die_node; |
| |
| /* Set to TRUE while dwarf2out_early_global_decl is running. */ |
| static bool early_dwarf; |
| static bool early_dwarf_finished; |
| class set_early_dwarf { |
| public: |
| bool saved; |
| set_early_dwarf () : saved(early_dwarf) |
| { |
| gcc_assert (! early_dwarf_finished); |
| early_dwarf = true; |
| } |
| ~set_early_dwarf () { ea
|