X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fdwarf2out.c;h=1450e806556c0e65ad5926bf2ca0ae1e886c2b20;hb=8d3dddbfd699bd26bbec40041f2f4dd37975be6d;hp=2c79c6941377d44345350faf6a0754105a3cf18a;hpb=3c347ca592ecd6fad23869592e75ce63e71a7811;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 2c79c694137..850eb556217 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -1,6 +1,6 @@ /* Output Dwarf2 format symbol table information from GCC. Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Gary Funck (gary@intrepid.com). Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com). @@ -85,6 +85,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "debug.h" #include "target.h" +#include "common/common-target.h" #include "langhooks.h" #include "hashtab.h" #include "cgraph.h" @@ -92,9 +93,12 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "tree-pass.h" #include "tree-flow.h" +#include "cfglayout.h" +#include "opts.h" static void dwarf2out_source_line (unsigned int, const char *, int, bool); static rtx last_var_location_insn; +static rtx cached_next_real_insn; #ifdef VMS_DEBUGGING_INFO int vms_file_stats_name (const char *, long long *, long *, char *, int *); @@ -112,85 +116,14 @@ int vms_file_stats_name (const char *, long long *, long *, char *, int *); #define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 0 #endif -#ifndef DWARF2_FRAME_INFO -# ifdef DWARF2_DEBUGGING_INFO -# define DWARF2_FRAME_INFO \ - (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG) -# else -# define DWARF2_FRAME_INFO 0 -# endif +/* ??? 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 -/* Map register numbers held in the call frame info that gcc has - collected using DWARF_FRAME_REGNUM to those that should be output in - .debug_frame and .eh_frame. */ -#ifndef DWARF2_FRAME_REG_OUT -#define DWARF2_FRAME_REG_OUT(REGNO, FOR_EH) (REGNO) -#endif - -/* Save the result of dwarf2out_do_frame across PCH. */ -static GTY(()) bool saved_do_cfi_asm = 0; - -/* Decide whether we want to emit frame unwind information for the current - translation unit. */ - -int -dwarf2out_do_frame (void) -{ - /* We want to emit correct CFA location expressions or lists, so we - have to return true if we're going to output debug info, even if - we're not going to output frame or unwind info. */ - return (write_symbols == DWARF2_DEBUG - || write_symbols == VMS_AND_DWARF2_DEBUG - || DWARF2_FRAME_INFO || saved_do_cfi_asm -#ifdef DWARF2_UNWIND_INFO - || (DWARF2_UNWIND_INFO - && (flag_unwind_tables - || (flag_exceptions && ! USING_SJLJ_EXCEPTIONS))) -#endif - ); -} - -/* Decide whether to emit frame unwind via assembler directives. */ - -int -dwarf2out_do_cfi_asm (void) -{ - int enc; - -#ifdef MIPS_DEBUGGING_INFO - return false; -#endif - if (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ()) - return false; - if (saved_do_cfi_asm) - return true; - if (!HAVE_GAS_CFI_PERSONALITY_DIRECTIVE) - return false; - - /* Make sure the personality encoding is one the assembler can support. - In particular, aligned addresses can't be handled. */ - enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,/*global=*/1); - if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel) - return false; - enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,/*global=*/0); - if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel) - return false; - - if (!HAVE_GAS_CFI_SECTIONS_DIRECTIVE) - { -#ifdef TARGET_UNWIND_INFO - return false; -#else - if (USING_SJLJ_EXCEPTIONS || (!flag_unwind_tables && !flag_exceptions)) - return false; -#endif - } - - saved_do_cfi_asm = true; - return true; -} - /* The size of the target's pointer type. */ #ifndef PTR_SIZE #define PTR_SIZE (POINTER_SIZE / BITS_PER_UNIT) @@ -222,141 +155,13 @@ static GTY(()) section *debug_line_section; static GTY(()) section *debug_loc_section; static GTY(()) section *debug_pubnames_section; static GTY(()) section *debug_pubtypes_section; -static GTY(()) section *debug_dcall_section; -static GTY(()) section *debug_vcall_section; static GTY(()) section *debug_str_section; static GTY(()) section *debug_ranges_section; static GTY(()) section *debug_frame_section; -/* Personality decl of current unit. Used only when assembler does not support - personality CFI. */ -static GTY(()) rtx current_unit_personality; - -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif - -typedef struct dw_cfi_struct *dw_cfi_ref; -typedef struct dw_fde_struct *dw_fde_ref; -typedef union dw_cfi_oprnd_struct *dw_cfi_oprnd_ref; - -/* Call frames are described using a sequence of Call Frame - Information instructions. The register number, offset - and address fields are provided as possible operands; - their use is selected by the opcode field. */ - -enum dw_cfi_oprnd_type { - dw_cfi_oprnd_unused, - dw_cfi_oprnd_reg_num, - dw_cfi_oprnd_offset, - dw_cfi_oprnd_addr, - dw_cfi_oprnd_loc -}; - -typedef union GTY(()) dw_cfi_oprnd_struct { - unsigned int GTY ((tag ("dw_cfi_oprnd_reg_num"))) dw_cfi_reg_num; - HOST_WIDE_INT GTY ((tag ("dw_cfi_oprnd_offset"))) dw_cfi_offset; - const char * GTY ((tag ("dw_cfi_oprnd_addr"))) dw_cfi_addr; - struct dw_loc_descr_struct * GTY ((tag ("dw_cfi_oprnd_loc"))) dw_cfi_loc; -} -dw_cfi_oprnd; - -typedef struct GTY(()) dw_cfi_struct { - dw_cfi_ref dw_cfi_next; - enum dwarf_call_frame_info dw_cfi_opc; - dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd1_desc (%1.dw_cfi_opc)"))) - dw_cfi_oprnd1; - dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd2_desc (%1.dw_cfi_opc)"))) - dw_cfi_oprnd2; -} -dw_cfi_node; - -/* This is how we define the location of the CFA. We use to handle it - as REG + OFFSET all the time, but now it can be more complex. - It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET. - Instead of passing around REG and OFFSET, we pass a copy - of this structure. */ -typedef struct GTY(()) cfa_loc { - HOST_WIDE_INT offset; - HOST_WIDE_INT base_offset; - unsigned int reg; - BOOL_BITFIELD indirect : 1; /* 1 if CFA is accessed via a dereference. */ - BOOL_BITFIELD in_use : 1; /* 1 if a saved cfa is stored here. */ -} dw_cfa_location; - -/* All call frame descriptions (FDE's) in the GCC generated DWARF - refer to a single Common Information Entry (CIE), defined at - the beginning of the .debug_frame section. This use of a single - CIE obviates the need to keep track of multiple CIE's - in the DWARF generation routines below. */ - -typedef struct GTY(()) dw_fde_struct { - tree decl; - const char *dw_fde_begin; - const char *dw_fde_current_label; - const char *dw_fde_end; - const char *dw_fde_vms_end_prologue; - const char *dw_fde_vms_begin_epilogue; - const char *dw_fde_hot_section_label; - const char *dw_fde_hot_section_end_label; - const char *dw_fde_unlikely_section_label; - const char *dw_fde_unlikely_section_end_label; - dw_cfi_ref dw_fde_cfi; - dw_cfi_ref dw_fde_switch_cfi; /* Last CFI before switching sections. */ - HOST_WIDE_INT stack_realignment; - unsigned funcdef_number; - /* Dynamic realign argument pointer register. */ - unsigned int drap_reg; - /* Virtual dynamic realign argument pointer register. */ - unsigned int vdrap_reg; - /* These 3 flags are copied from rtl_data in function.h. */ - unsigned all_throwers_are_sibcalls : 1; - unsigned uses_eh_lsda : 1; - unsigned nothrow : 1; - /* Whether we did stack realign in this call frame. */ - unsigned stack_realign : 1; - /* Whether dynamic realign argument pointer register has been saved. */ - unsigned drap_reg_saved: 1; - /* True iff dw_fde_begin label is in text_section or cold_text_section. */ - unsigned in_std_section : 1; - /* True iff dw_fde_unlikely_section_label is in text_section or - cold_text_section. */ - unsigned cold_in_std_section : 1; - /* True iff switched sections. */ - unsigned dw_fde_switched_sections : 1; - /* True iff switching from cold to hot section. */ - unsigned dw_fde_switched_cold_to_hot : 1; -} -dw_fde_node; - /* Maximum size (in bytes) of an artificially generated label. */ #define MAX_ARTIFICIAL_LABEL_BYTES 30 -/* The size of addresses as they appear in the Dwarf 2 data. - Some architectures use word addresses to refer to code locations, - but Dwarf 2 info always uses byte addresses. On such machines, - Dwarf 2 addresses need to be larger than the architecture's - pointers. */ -#ifndef DWARF2_ADDR_SIZE -#define DWARF2_ADDR_SIZE (POINTER_SIZE / BITS_PER_UNIT) -#endif - -/* The size in bytes of a DWARF field indicating an offset or length - relative to a debug info section, specified to be 4 bytes in the - DWARF-2 specification. The SGI/MIPS ABI defines it to be the same - as PTR_SIZE. */ - -#ifndef DWARF_OFFSET_SIZE -#define DWARF_OFFSET_SIZE 4 -#endif - -/* The size in bytes of a DWARF 4 type signature. */ - -#ifndef DWARF_TYPE_SIGNATURE_SIZE -#define DWARF_TYPE_SIGNATURE_SIZE 8 -#endif - /* According to the (draft) DWARF 3 specification, the initial length should either be 4 or 12 bytes. When it's 12 bytes, the first 4 bytes are 0xffffffff, followed by the length stored in the next 8 @@ -373,15 +178,6 @@ dw_fde_node; #define DWARF_ROUND(SIZE,BOUNDARY) \ ((((SIZE) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY)) -/* Offsets recorded in opcodes are a multiple of this alignment factor. */ -#ifndef DWARF_CIE_DATA_ALIGNMENT -#ifdef STACK_GROWS_DOWNWARD -#define DWARF_CIE_DATA_ALIGNMENT (-((int) UNITS_PER_WORD)) -#else -#define DWARF_CIE_DATA_ALIGNMENT ((int) UNITS_PER_WORD) -#endif -#endif - /* CIE identifier. */ #if HOST_BITS_PER_WIDE_INT >= 64 #define DWARF_CIE_ID \ @@ -390,36 +186,12 @@ dw_fde_node; #define DWARF_CIE_ID DW_CIE_ID #endif -/* A pointer to the base of a table that contains frame description - information for each routine. */ -static GTY((length ("fde_table_allocated"))) dw_fde_ref fde_table; - -/* Number of elements currently allocated for fde_table. */ -static GTY(()) unsigned fde_table_allocated; - -/* Number of elements in fde_table currently in use. */ -static GTY(()) unsigned fde_table_in_use; - -/* Size (in elements) of increments by which we may expand the - fde_table. */ -#define FDE_TABLE_INCREMENT 256 - -/* Get the current fde_table entry we should use. */ - -static inline dw_fde_ref -current_fde (void) -{ - return fde_table_in_use ? &fde_table[fde_table_in_use - 1] : NULL; -} - -/* A list of call frame insns for the CIE. */ -static GTY(()) dw_cfi_ref cie_cfi_head; +DEF_VEC_P (dw_fde_ref); +DEF_VEC_ALLOC_P (dw_fde_ref, gc); -/* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram - attribute that accelerates the lookup of the FDE associated - with the subprogram. This variable holds the table index of the FDE - associated with the current function (body) definition. */ -static unsigned current_funcdef_fde; +/* A vector for a table that contains frame description + information for each routine. */ +static GTY(()) VEC(dw_fde_ref, gc) *fde_vec; struct GTY(()) indirect_string_node { const char *str; @@ -430,12 +202,7 @@ struct GTY(()) indirect_string_node { static GTY ((param_is (struct indirect_string_node))) htab_t debug_str_hash; -/* True if the compilation unit has location entries that reference - debug strings. */ -static GTY(()) bool debug_str_hash_forced = false; - static GTY(()) int dw2_string_counter; -static GTY(()) unsigned long dwarf2out_cfi_label_num; /* True if the compilation unit places functions in more than one section. */ static GTY(()) bool have_multiple_function_sections = false; @@ -451,41 +218,12 @@ static GTY(()) section *cold_text_section; /* Forward declarations for functions defined in this file. */ static char *stripattributes (const char *); -static const char *dwarf_cfi_name (unsigned); -static dw_cfi_ref new_cfi (void); -static void add_cfi (dw_cfi_ref *, dw_cfi_ref); -static void add_fde_cfi (const char *, dw_cfi_ref); -static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *, dw_cfa_location *); -static void lookup_cfa (dw_cfa_location *); -static void reg_save (const char *, unsigned, unsigned, HOST_WIDE_INT); -static void initial_return_save (rtx); -static HOST_WIDE_INT stack_adjust_offset (const_rtx, HOST_WIDE_INT, - HOST_WIDE_INT); -static void output_cfi (dw_cfi_ref, dw_fde_ref, int); -static void output_cfi_directive (dw_cfi_ref); static void output_call_frame_info (int); static void dwarf2out_note_section_used (void); -static void flush_queued_reg_saves (void); -static bool clobbers_queued_reg_save (const_rtx); -static void dwarf2out_frame_debug_expr (rtx, const char *); - -/* Support for complex CFA locations. */ -static void output_cfa_loc (dw_cfi_ref); -static void output_cfa_loc_raw (dw_cfi_ref); -static void get_cfa_from_loc_descr (dw_cfa_location *, - struct dw_loc_descr_struct *); -static struct dw_loc_descr_struct *build_cfa_loc - (dw_cfa_location *, HOST_WIDE_INT); -static struct dw_loc_descr_struct *build_cfa_aligned_loc - (HOST_WIDE_INT, HOST_WIDE_INT); -static void def_cfa_1 (const char *, dw_cfa_location *); -static struct dw_loc_descr_struct *mem_loc_descriptor - (rtx, enum machine_mode mode, enum var_init_status); - -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif + +/* Personality decl of current unit. Used only when assembler does not support + personality CFI. */ +static GTY(()) rtx current_unit_personality; /* Data and reference forms for relocatable data. */ #define DW_FORM_data (DWARF_OFFSET_SIZE == 8 ? DW_FORM_data8 : DW_FORM_data4) @@ -524,33 +262,90 @@ static struct dw_loc_descr_struct *mem_loc_descriptor #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) -/* The DWARF 2 CFA column which tracks the return address. Normally this - is the column for PC, or the first column after all of the hard - registers. */ -#ifndef DWARF_FRAME_RETURN_COLUMN -#ifdef PC_REGNUM -#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (PC_REGNUM) #else -#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGISTERS -#endif -#endif -/* The mapping from gcc register number to DWARF 2 CFA column number. By - default, we just provide columns for all registers. */ -#ifndef DWARF_FRAME_REGNUM -#define DWARF_FRAME_REGNUM(REG) DBX_REGISTER_NUMBER (REG) +#define DUMP_GSTRUCT(type, usage, criterion, generic, matches, result) \ + (result) + #endif - -/* Hook used by __throw. */ -rtx -expand_builtin_dwarf_sp_column (void) +static bool +should_emit_struct_debug (tree type, enum debug_info_usage usage) { - unsigned int dwarf_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM); - return GEN_INT (DWARF2_FRAME_REG_OUT (dwarf_regnum, 1)); -} + 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 (criterion == DINFO_STRUCT_FILE_SYS && DECL_IN_SYSTEM_HEADER (type_decl)) + return DUMP_GSTRUCT (type, usage, criterion, generic, false, true); + + if (matches_main_base (DECL_SOURCE_FILE (type_decl))) + return DUMP_GSTRUCT (type, usage, criterion, generic, true, true); + return DUMP_GSTRUCT (type, usage, criterion, generic, false, false); +} + /* Return a pointer to a copy of the section string name S with all attributes stripped off, and an asterisk prepended (for assemble_name). */ @@ -569,3806 +364,874 @@ stripattributes (const char *s) return stripped; } -/* MEM is a memory reference for the register size table, each element of - which has mode MODE. Initialize column C as a return address column. */ +/* 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 -init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c) -{ - HOST_WIDE_INT offset = c * GET_MODE_SIZE (mode); - HOST_WIDE_INT size = GET_MODE_SIZE (Pmode); - emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size)); -} - -/* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder. */ - -static inline HOST_WIDE_INT -div_data_align (HOST_WIDE_INT off) +switch_to_eh_frame_section (bool back) { - HOST_WIDE_INT r = off / DWARF_CIE_DATA_ALIGNMENT; - gcc_assert (r * DWARF_CIE_DATA_ALIGNMENT == off); - return r; -} - -/* Return true if we need a signed version of a given opcode - (e.g. DW_CFA_offset_extended_sf vs DW_CFA_offset_extended). */ + tree label; -static inline bool -need_data_align_sf_opcode (HOST_WIDE_INT off) -{ - return DWARF_CIE_DATA_ALIGNMENT < 0 ? off > 0 : off < 0; -} +#ifdef EH_FRAME_SECTION_NAME + if (eh_frame_section == 0) + { + int flags; -/* Generate code to initialize the register size table. */ + if (EH_TABLES_CAN_BE_READ_ONLY) + { + int fde_encoding; + int per_encoding; + int lsda_encoding; -void -expand_builtin_init_dwarf_reg_sizes (tree address) -{ - unsigned int i; - enum machine_mode mode = TYPE_MODE (char_type_node); - rtx addr = expand_normal (address); - rtx mem = gen_rtx_MEM (BLKmode, addr); - bool wrote_return_column = false; + fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, + /*global=*/0); + per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, + /*global=*/1); + lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, + /*global=*/0); + flags = ((! flag_pic + || ((fde_encoding & 0x70) != DW_EH_PE_absptr + && (fde_encoding & 0x70) != DW_EH_PE_aligned + && (per_encoding & 0x70) != DW_EH_PE_absptr + && (per_encoding & 0x70) != DW_EH_PE_aligned + && (lsda_encoding & 0x70) != DW_EH_PE_absptr + && (lsda_encoding & 0x70) != DW_EH_PE_aligned)) + ? 0 : SECTION_WRITE); + } + else + flags = SECTION_WRITE; + eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL); + } +#endif /* EH_FRAME_SECTION_NAME */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (eh_frame_section) + switch_to_section (eh_frame_section); + else { - int rnum = DWARF2_FRAME_REG_OUT (DWARF_FRAME_REGNUM (i), 1); + /* We have no special eh_frame section. Put the information in + the data section and emit special labels to guide collect2. */ + switch_to_section (data_section); - if (rnum < DWARF_FRAME_REGISTERS) + if (!back) { - HOST_WIDE_INT offset = rnum * GET_MODE_SIZE (mode); - enum machine_mode save_mode = reg_raw_mode[i]; - HOST_WIDE_INT size; - - if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode)) - save_mode = choose_hard_reg_mode (i, 1, true); - if (DWARF_FRAME_REGNUM (i) == DWARF_FRAME_RETURN_COLUMN) - { - if (save_mode == VOIDmode) - continue; - wrote_return_column = true; - } - size = GET_MODE_SIZE (save_mode); - if (offset < 0) - continue; - - emit_move_insn (adjust_address (mem, mode, offset), - gen_int_mode (size, mode)); + 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)); } } +} - if (!wrote_return_column) - init_return_column_size (mode, mem, DWARF_FRAME_RETURN_COLUMN); - -#ifdef DWARF_ALT_FRAME_RETURN_COLUMN - init_return_column_size (mode, mem, DWARF_ALT_FRAME_RETURN_COLUMN); -#endif +/* Switch [BACK] to the eh or debug frame table section, depending on + FOR_EH. */ - targetm.init_dwarf_reg_sizes_extra (address); +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); + } } -/* Convert a DWARF call frame info. operation to its string name */ +/* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ -static const char * -dwarf_cfi_name (unsigned int cfi_opc) +enum dw_cfi_oprnd_type +dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi) { - switch (cfi_opc) + switch (cfi) { - case DW_CFA_advance_loc: - return "DW_CFA_advance_loc"; - case DW_CFA_offset: - return "DW_CFA_offset"; - case DW_CFA_restore: - return "DW_CFA_restore"; case DW_CFA_nop: - return "DW_CFA_nop"; + case DW_CFA_GNU_window_save: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + return dw_cfi_oprnd_unused; + case DW_CFA_set_loc: - return "DW_CFA_set_loc"; case DW_CFA_advance_loc1: - return "DW_CFA_advance_loc1"; case DW_CFA_advance_loc2: - return "DW_CFA_advance_loc2"; case DW_CFA_advance_loc4: - return "DW_CFA_advance_loc4"; + case DW_CFA_MIPS_advance_loc8: + return dw_cfi_oprnd_addr; + + case DW_CFA_offset: case DW_CFA_offset_extended: - return "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: - return "DW_CFA_restore_extended"; case DW_CFA_undefined: - return "DW_CFA_undefined"; case DW_CFA_same_value: - return "DW_CFA_same_value"; - case DW_CFA_register: - return "DW_CFA_register"; - case DW_CFA_remember_state: - return "DW_CFA_remember_state"; - case DW_CFA_restore_state: - return "DW_CFA_restore_state"; - case DW_CFA_def_cfa: - return "DW_CFA_def_cfa"; case DW_CFA_def_cfa_register: - return "DW_CFA_def_cfa_register"; - case DW_CFA_def_cfa_offset: - return "DW_CFA_def_cfa_offset"; - - /* DWARF 3 */ - case DW_CFA_def_cfa_expression: - return "DW_CFA_def_cfa_expression"; + case DW_CFA_register: case DW_CFA_expression: - return "DW_CFA_expression"; - case DW_CFA_offset_extended_sf: - return "DW_CFA_offset_extended_sf"; - case DW_CFA_def_cfa_sf: - return "DW_CFA_def_cfa_sf"; - case DW_CFA_def_cfa_offset_sf: - return "DW_CFA_def_cfa_offset_sf"; - - /* SGI/MIPS specific */ - case DW_CFA_MIPS_advance_loc8: - return "DW_CFA_MIPS_advance_loc8"; + return dw_cfi_oprnd_reg_num; - /* GNU extensions */ - case DW_CFA_GNU_window_save: - return "DW_CFA_GNU_window_save"; + case DW_CFA_def_cfa_offset: case DW_CFA_GNU_args_size: - return "DW_CFA_GNU_args_size"; - case DW_CFA_GNU_negative_offset_extended: - return "DW_CFA_GNU_negative_offset_extended"; + case DW_CFA_def_cfa_offset_sf: + return dw_cfi_oprnd_offset; + + case DW_CFA_def_cfa_expression: + return dw_cfi_oprnd_loc; default: - return "DW_CFA_"; + gcc_unreachable (); } } -/* Return a pointer to a newly allocated Call Frame Instruction. */ +/* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used. */ -static inline dw_cfi_ref -new_cfi (void) +enum dw_cfi_oprnd_type +dw_cfi_oprnd2_desc (enum dwarf_call_frame_info cfi) { - dw_cfi_ref cfi = ggc_alloc_dw_cfi_node (); - - cfi->dw_cfi_next = NULL; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0; - cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0; - - return cfi; -} - -/* Add a Call Frame Instruction to list of instructions. */ - -static inline void -add_cfi (dw_cfi_ref *list_head, dw_cfi_ref cfi) -{ - dw_cfi_ref *p; - dw_fde_ref fde = current_fde (); - - /* When DRAP is used, CFA is defined with an expression. Redefine - CFA may lead to a different CFA value. */ - /* ??? Of course, this heuristic fails when we're annotating epilogues, - because of course we'll always want to redefine the CFA back to the - stack pointer on the way out. Where should we move this check? */ - if (0 && fde && fde->drap_reg != INVALID_REGNUM) - switch (cfi->dw_cfi_opc) - { - case DW_CFA_def_cfa_register: - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - gcc_unreachable (); + 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; - default: - break; - } + case DW_CFA_register: + return dw_cfi_oprnd_reg_num; - /* Find the end of the chain. */ - for (p = list_head; (*p) != NULL; p = &(*p)->dw_cfi_next) - ; + case DW_CFA_expression: + return dw_cfi_oprnd_loc; - *p = cfi; + default: + return dw_cfi_oprnd_unused; + } } -/* Generate a new label for the CFI info to refer to. FORCE is true - if a label needs to be output even when using .cfi_* directives. */ +/* Output one FDE. */ -char * -dwarf2out_cfi_label (bool force) +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) { - static char label[20]; - - if (!force && dwarf2out_do_cfi_asm ()) - { - /* In this case, we will be emitting the asm directive instead of - the label, so just return a placeholder to keep the rest of the - interfaces happy. */ - strcpy (label, ""); - } - else - { - ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", dwarf2out_cfi_label_num++); - ASM_OUTPUT_LABEL (asm_out_file, label); - } - - return label; -} + const char *begin, *end; + static unsigned int j; + char l1[20], l2[20]; -/* True if remember_state should be emitted before following CFI directive. */ -static bool emit_cfa_remember; + targetm.asm_out.emit_unwind_label (asm_out_file, fde->decl, for_eh, + /* empty */ 0); + targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, + for_eh + j); + ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + j); + ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + j); + if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) + dw2_asm_output_data (4, 0xffffffff, "Initial length escape value" + " indicating 64-bit DWARF extension"); + dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, + "FDE Length"); + ASM_OUTPUT_LABEL (asm_out_file, l1); -/* Add CFI to the current fde at the PC value indicated by LABEL if specified, - or to the CIE if LABEL is NULL. */ + 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"); -static void -add_fde_cfi (const char *label, dw_cfi_ref cfi) -{ - dw_cfi_ref *list_head; + begin = second ? fde->dw_fde_second_begin : fde->dw_fde_begin; + end = second ? fde->dw_fde_second_end : fde->dw_fde_end; - if (emit_cfa_remember) + if (for_eh) { - dw_cfi_ref cfi_remember; - - /* Emit the state save. */ - emit_cfa_remember = false; - cfi_remember = new_cfi (); - cfi_remember->dw_cfi_opc = DW_CFA_remember_state; - add_fde_cfi (label, cfi_remember); + 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"); } - list_head = &cie_cfi_head; - - if (dwarf2out_do_cfi_asm ()) + if (augmentation[0]) { - if (label) + if (any_lsda_needed) { - dw_fde_ref fde = current_fde (); - - gcc_assert (fde != NULL); - - /* We still have to add the cfi to the list so that lookup_cfa - works later on. When -g2 and above we even need to force - emitting of CFI labels and add to list a DW_CFA_set_loc for - convert_cfa_to_fb_loc_list purposes. If we're generating - DWARF3 output we use DW_OP_call_frame_cfa and so don't use - convert_cfa_to_fb_loc_list. */ - if (dwarf_version == 2 - && debug_info_level > DINFO_LEVEL_TERSE - && (write_symbols == DWARF2_DEBUG - || write_symbols == VMS_AND_DWARF2_DEBUG)) + int size = size_of_encoded_value (lsda_encoding); + + if (lsda_encoding == DW_EH_PE_aligned) { - switch (cfi->dw_cfi_opc) - { - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - case DW_CFA_def_cfa_register: - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - case DW_CFA_def_cfa_expression: - case DW_CFA_restore_state: - if (*label == 0 || strcmp (label, "") == 0) - label = dwarf2out_cfi_label (true); - - if (fde->dw_fde_current_label == NULL - || strcmp (label, fde->dw_fde_current_label) != 0) - { - dw_cfi_ref xcfi; - - label = xstrdup (label); - - /* Set the location counter to the new label. */ - xcfi = new_cfi (); - /* It doesn't metter whether DW_CFA_set_loc - or DW_CFA_advance_loc4 is added here, those aren't - emitted into assembly, only looked up by - convert_cfa_to_fb_loc_list. */ - xcfi->dw_cfi_opc = DW_CFA_set_loc; - xcfi->dw_cfi_oprnd1.dw_cfi_addr = label; - add_cfi (&fde->dw_fde_cfi, xcfi); - fde->dw_fde_current_label = label; - } - break; - default: - break; - } + 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); } - output_cfi_directive (cfi); + dw2_asm_output_data_uleb128 (size, "Augmentation size"); - list_head = &fde->dw_fde_cfi; + 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)"); + } } - /* ??? If this is a CFI for the CIE, we don't emit. This - assumes that the standard CIE contents that the assembler - uses matches the standard CIE contents that the compiler - uses. This is probably a bad assumption. I'm not quite - sure how to address this for now. */ + else + dw2_asm_output_data_uleb128 (0, "Augmentation size"); } - else if (label) - { - dw_fde_ref fde = current_fde (); - - gcc_assert (fde != NULL); - - if (*label == 0) - label = dwarf2out_cfi_label (false); - - if (fde->dw_fde_current_label == NULL - || strcmp (label, fde->dw_fde_current_label) != 0) - { - dw_cfi_ref xcfi; - - label = xstrdup (label); - /* Set the location counter to the new label. */ - xcfi = new_cfi (); - /* If we have a current label, advance from there, otherwise - set the location directly using set_loc. */ - xcfi->dw_cfi_opc = fde->dw_fde_current_label - ? DW_CFA_advance_loc4 - : DW_CFA_set_loc; - xcfi->dw_cfi_oprnd1.dw_cfi_addr = label; - add_cfi (&fde->dw_fde_cfi, xcfi); + /* Loop through the Call Frame Instructions associated with this FDE. */ + fde->dw_fde_current_label = begin; + { + size_t from, until, i; - fde->dw_fde_current_label = label; - } + from = 0; + until = VEC_length (dw_cfi_ref, fde->dw_fde_cfi); - list_head = &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; - add_cfi (list_head, cfi); -} + for (i = from; i < until; i++) + output_cfi (VEC_index (dw_cfi_ref, fde->dw_fde_cfi, i), fde, for_eh); + } -/* Subroutine of lookup_cfa. */ + /* 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. */ -static void -lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember) -{ - switch (cfi->dw_cfi_opc) - { - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset; - break; - case DW_CFA_def_cfa_register: - loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; - break; - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; - loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset; - break; - case DW_CFA_def_cfa_expression: - get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc); - break; +#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 - case DW_CFA_remember_state: - gcc_assert (!remember->in_use); - *remember = *loc; - remember->in_use = 1; - break; - case DW_CFA_restore_state: - gcc_assert (remember->in_use); - *loc = *remember; - remember->in_use = 0; - break; + /* 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); - default: - break; - } + j += 2; } -/* Find the previous value for the CFA. */ +/* Return true if frame description entry FDE is needed for EH. */ -static void -lookup_cfa (dw_cfa_location *loc) +static bool +fde_needed_for_eh_p (dw_fde_ref fde) { - dw_cfi_ref cfi; - dw_fde_ref fde; - dw_cfa_location remember; - - memset (loc, 0, sizeof (*loc)); - loc->reg = INVALID_REGNUM; - remember = *loc; - - for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) - lookup_cfa_1 (cfi, loc, &remember); - - fde = current_fde (); - if (fde) - for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) - lookup_cfa_1 (cfi, loc, &remember); -} - -/* The current rule for calculating the DWARF2 canonical frame address. */ -static dw_cfa_location cfa; - -/* The register used for saving registers to the stack, and its offset - from the CFA. */ -static dw_cfa_location cfa_store; - -/* The current save location around an epilogue. */ -static dw_cfa_location cfa_remember; - -/* The running total of the size of arguments pushed onto the stack. */ -static HOST_WIDE_INT args_size; - -/* The last args_size we actually output. */ -static HOST_WIDE_INT old_args_size; + if (flag_asynchronous_unwind_tables) + return true; -/* Entry point to update the canonical frame address (CFA). - LABEL is passed to add_fde_cfi. The value of CFA is now to be - calculated from REG+OFFSET. */ + if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde->decl)) + return true; -void -dwarf2out_def_cfa (const char *label, unsigned int reg, HOST_WIDE_INT offset) -{ - dw_cfa_location loc; - loc.indirect = 0; - loc.base_offset = 0; - loc.reg = reg; - loc.offset = offset; - def_cfa_1 (label, &loc); -} + if (fde->uses_eh_lsda) + return true; -/* Determine if two dw_cfa_location structures define the same data. */ + /* If exceptions are enabled, we have collected nothrow info. */ + if (flag_exceptions && (fde->all_throwers_are_sibcalls || fde->nothrow)) + return false; -static bool -cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2) -{ - return (loc1->reg == loc2->reg - && loc1->offset == loc2->offset - && loc1->indirect == loc2->indirect - && (loc1->indirect == 0 - || loc1->base_offset == loc2->base_offset)); + return true; } -/* This routine does the actual work. The CFA is now calculated from - the dw_cfa_location structure. */ +/* 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 -def_cfa_1 (const char *label, dw_cfa_location *loc_p) +output_call_frame_info (int for_eh) { + unsigned int i; + dw_fde_ref fde; dw_cfi_ref cfi; - dw_cfa_location old_cfa, loc; - - cfa = *loc_p; - loc = *loc_p; - - if (cfa_store.reg == loc.reg && loc.indirect == 0) - cfa_store.offset = loc.offset; - - loc.reg = DWARF_FRAME_REGNUM (loc.reg); - lookup_cfa (&old_cfa); + char l1[20], l2[20], section_start_label[20]; + bool any_lsda_needed = false; + char augmentation[6]; + int augmentation_size; + int fde_encoding = DW_EH_PE_absptr; + int per_encoding = DW_EH_PE_absptr; + int lsda_encoding = DW_EH_PE_absptr; + int return_reg; + rtx personality = NULL; + int dw_cie_version; - /* If nothing changed, no need to issue any call frame instructions. */ - if (cfa_equal_p (&loc, &old_cfa)) + /* Don't emit a CIE if there won't be any FDEs. */ + if (fde_vec == NULL) return; - cfi = new_cfi (); - - if (loc.reg == old_cfa.reg && !loc.indirect && !old_cfa.indirect) - { - /* Construct a "DW_CFA_def_cfa_offset " instruction, indicating - the CFA register did not change but the offset did. The data - factoring for DW_CFA_def_cfa_offset_sf happens in output_cfi, or - in the assembler via the .cfi_def_cfa_offset directive. */ - if (loc.offset < 0) - cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf; - else - cfi->dw_cfi_opc = DW_CFA_def_cfa_offset; - cfi->dw_cfi_oprnd1.dw_cfi_offset = loc.offset; - } + /* Nothing to do if the assembler's doing it all. */ + if (dwarf2out_do_cfi_asm ()) + return; -#ifndef MIPS_DEBUGGING_INFO /* SGI dbx thinks this means no offset. */ - else if (loc.offset == old_cfa.offset - && old_cfa.reg != INVALID_REGNUM - && !loc.indirect - && !old_cfa.indirect) + /* 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) { - /* Construct a "DW_CFA_def_cfa_register " instruction, - indicating the CFA register has changed to but the - offset has not changed. */ - cfi->dw_cfi_opc = DW_CFA_def_cfa_register; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg; - } -#endif + bool any_eh_needed = false; - else if (loc.indirect == 0) - { - /* Construct a "DW_CFA_def_cfa " instruction, - indicating the CFA register has changed to with - the specified offset. The data factoring for DW_CFA_def_cfa_sf - happens in output_cfi, or in the assembler via the .cfi_def_cfa - directive. */ - if (loc.offset < 0) - cfi->dw_cfi_opc = DW_CFA_def_cfa_sf; - else - cfi->dw_cfi_opc = DW_CFA_def_cfa; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = loc.reg; - cfi->dw_cfi_oprnd2.dw_cfi_offset = loc.offset; - } - else - { - /* Construct a DW_CFA_def_cfa_expression instruction to - calculate the CFA using a full location expression since no - register-offset pair is available. */ - struct dw_loc_descr_struct *loc_list; + FOR_EACH_VEC_ELT (dw_fde_ref, 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); + } - cfi->dw_cfi_opc = DW_CFA_def_cfa_expression; - loc_list = build_cfa_loc (&loc, 0); - cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list; + if (!any_eh_needed) + return; } - add_fde_cfi (label, cfi); -} + /* We're going to be generating comments, so turn on app. */ + if (flag_debug_asm) + app_enable (); -/* Add the CFI for saving a register. REG is the CFA column number. - LABEL is passed to add_fde_cfi. - If SREG is -1, the register is saved at OFFSET from the CFA; - otherwise it is saved in SREG. */ + /* Switch to the proper frame section, first time. */ + switch_to_frame_table_section (for_eh, false); -static void -reg_save (const char *label, unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset) -{ - dw_cfi_ref cfi = new_cfi (); - dw_fde_ref fde = current_fde (); + ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh); + ASM_OUTPUT_LABEL (asm_out_file, section_start_label); - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; + /* Output the CIE. */ + ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); + ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); + if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) + dw2_asm_output_data (4, 0xffffffff, + "Initial length escape value indicating 64-bit DWARF extension"); + dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, + "Length of Common Information Entry"); + ASM_OUTPUT_LABEL (asm_out_file, l1); - /* When stack is aligned, store REG using DW_CFA_expression with - FP. */ - if (fde - && fde->stack_realign - && sreg == INVALID_REGNUM) - { - cfi->dw_cfi_opc = DW_CFA_expression; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; - cfi->dw_cfi_oprnd2.dw_cfi_loc - = build_cfa_aligned_loc (offset, fde->stack_realignment); - } - else if (sreg == INVALID_REGNUM) - { - if (need_data_align_sf_opcode (offset)) - cfi->dw_cfi_opc = DW_CFA_offset_extended_sf; - else if (reg & ~0x3f) - cfi->dw_cfi_opc = DW_CFA_offset_extended; - else - cfi->dw_cfi_opc = DW_CFA_offset; - cfi->dw_cfi_oprnd2.dw_cfi_offset = offset; - } - else if (sreg == reg) - cfi->dw_cfi_opc = DW_CFA_same_value; - else - { - cfi->dw_cfi_opc = DW_CFA_register; - cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg; - } + /* 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"); - add_fde_cfi (label, cfi); -} + /* 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"); -/* Add the CFI for saving a register window. LABEL is passed to reg_save. - This CFI tells the unwinder that it needs to restore the window registers - from the previous frame's window save area. + augmentation[0] = 0; + augmentation_size = 0; - ??? Perhaps we should note in the CIE where windows are saved (instead of - assuming 0(cfa)) and what registers are in the window. */ + personality = current_unit_personality; + if (for_eh) + { + char *p; -void -dwarf2out_window_save (const char *label) -{ - dw_cfi_ref cfi = new_cfi (); - - cfi->dw_cfi_opc = DW_CFA_GNU_window_save; - add_fde_cfi (label, cfi); -} - -/* Entry point for saving a register to the stack. REG is the GCC register - number. LABEL and OFFSET are passed to reg_save. */ + /* 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. */ -void -dwarf2out_reg_save (const char *label, unsigned int reg, HOST_WIDE_INT offset) -{ - reg_save (label, DWARF_FRAME_REGNUM (reg), INVALID_REGNUM, offset); -} + 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); -/* Entry point for saving the return address in the stack. - LABEL and OFFSET are passed to reg_save. */ + 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'; + } -void -dwarf2out_return_save (const char *label, HOST_WIDE_INT offset) -{ - reg_save (label, DWARF_FRAME_RETURN_COLUMN, INVALID_REGNUM, offset); -} + /* 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); -/* Entry point for saving the return address in a register. - LABEL and SREG are passed to reg_save. */ + augmentation_size += pad; -void -dwarf2out_return_reg (const char *label, unsigned int sreg) -{ - reg_save (label, DWARF_FRAME_RETURN_COLUMN, DWARF_FRAME_REGNUM (sreg), 0); -} + /* 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); + } + } -/* Record the initial position of the return address. RTL is - INCOMING_RETURN_ADDR_RTX. */ + 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"); -static void -initial_return_save (rtx rtl) -{ - unsigned int reg = INVALID_REGNUM; - HOST_WIDE_INT offset = 0; + 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"); - switch (GET_CODE (rtl)) + if (augmentation[0]) { - case REG: - /* RA is in a register. */ - reg = DWARF_FRAME_REGNUM (REGNO (rtl)); - break; - - case MEM: - /* RA is on the stack. */ - rtl = XEXP (rtl, 0); - switch (GET_CODE (rtl)) + dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size"); + if (personality) { - case REG: - gcc_assert (REGNO (rtl) == STACK_POINTER_REGNUM); - offset = 0; - break; + 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); + } - case PLUS: - gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); - offset = INTVAL (XEXP (rtl, 1)); - break; + if (any_lsda_needed) + dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)", + eh_data_format_name (lsda_encoding)); - case MINUS: - gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM); - offset = -INTVAL (XEXP (rtl, 1)); - break; + if (fde_encoding != DW_EH_PE_absptr) + dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)", + eh_data_format_name (fde_encoding)); + } - default: - gcc_unreachable (); - } + FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, i, cfi) + output_cfi (cfi, NULL, for_eh); - break; + /* 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); - case PLUS: - /* The return address is at some offset from any value we can - actually load. For instance, on the SPARC it is in %i7+8. Just - ignore the offset for now; it doesn't matter for unwinding frames. */ - gcc_assert (CONST_INT_P (XEXP (rtl, 1))); - initial_return_save (XEXP (rtl, 0)); - return; + /* Loop through all of the FDE's. */ + FOR_EACH_VEC_ELT (dw_fde_ref, fde_vec, i, fde) + { + unsigned int k; - default: - gcc_unreachable (); + /* 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 (reg != DWARF_FRAME_RETURN_COLUMN) - reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa.offset); + if (for_eh && targetm.terminate_dw2_eh_frame_info) + dw2_asm_output_data (4, 0, "End of Table"); +#ifdef MIPS_DEBUGGING_INFO + /* Work around Irix 6 assembler bug whereby labels at the end of a section + get a value of 0. Putting .align 0 after the label fixes it. */ + ASM_OUTPUT_ALIGN (asm_out_file, 0); +#endif + + /* Turn off app to make assembly quicker. */ + if (flag_debug_asm) + app_disable (); } -/* Given a SET, calculate the amount of stack adjustment it - contains. */ +/* Emit .cfi_startproc and .cfi_personality/.cfi_lsda if needed. */ -static HOST_WIDE_INT -stack_adjust_offset (const_rtx pattern, HOST_WIDE_INT cur_args_size, - HOST_WIDE_INT cur_offset) +static void +dwarf2out_do_cfi_startproc (bool second) { - const_rtx src = SET_SRC (pattern); - const_rtx dest = SET_DEST (pattern); - HOST_WIDE_INT offset = 0; - enum rtx_code code; + int enc; + rtx ref; + rtx personality = get_personality_function (current_function_decl); - if (dest == stack_pointer_rtx) - { - code = GET_CODE (src); + fprintf (asm_out_file, "\t.cfi_startproc\n"); - /* Assume (set (reg sp) (reg whatever)) sets args_size - level to 0. */ - if (code == REG && src != stack_pointer_rtx) - { - offset = -cur_args_size; -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - return offset - cur_offset; - } + if (personality) + { + enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); + ref = personality; - if (! (code == PLUS || code == MINUS) - || XEXP (src, 0) != stack_pointer_rtx - || !CONST_INT_P (XEXP (src, 1))) - return 0; + /* ??? The GAS support isn't entirely consistent. We have to + handle indirect support ourselves, but PC-relative is done + in the assembler. Further, the assembler can't handle any + of the weirder relocation types. */ + if (enc & DW_EH_PE_indirect) + ref = dw2_force_const_mem (ref, true); - /* (set (reg sp) (plus (reg sp) (const_int))) */ - offset = INTVAL (XEXP (src, 1)); - if (code == PLUS) - offset = -offset; - return offset; + fprintf (asm_out_file, "\t.cfi_personality %#x,", enc); + output_addr_const (asm_out_file, ref); + fputc ('\n', asm_out_file); } - if (MEM_P (src) && !MEM_P (dest)) - dest = src; - if (MEM_P (dest)) + if (crtl->uses_eh_lsda) { - /* (set (mem (pre_dec (reg sp))) (foo)) */ - src = XEXP (dest, 0); - code = GET_CODE (src); - - switch (code) - { - case PRE_MODIFY: - case POST_MODIFY: - if (XEXP (src, 0) == stack_pointer_rtx) - { - rtx val = XEXP (XEXP (src, 1), 1); - /* We handle only adjustments by constant amount. */ - gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS - && CONST_INT_P (val)); - offset = -INTVAL (val); - break; - } - return 0; + char lab[20]; - case PRE_DEC: - case POST_DEC: - if (XEXP (src, 0) == stack_pointer_rtx) - { - offset = GET_MODE_SIZE (GET_MODE (dest)); - break; - } - return 0; + 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; - case PRE_INC: - case POST_INC: - if (XEXP (src, 0) == stack_pointer_rtx) - { - offset = -GET_MODE_SIZE (GET_MODE (dest)); - break; - } - return 0; + if (enc & DW_EH_PE_indirect) + ref = dw2_force_const_mem (ref, true); - default: - return 0; - } + fprintf (asm_out_file, "\t.cfi_lsda %#x,", enc); + output_addr_const (asm_out_file, ref); + fputc ('\n', asm_out_file); } - else - return 0; - - return offset; } -/* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them, - indexed by INSN_UID. */ - -static HOST_WIDE_INT *barrier_args_size; +/* Allocate CURRENT_FDE. Immediately initialize all we can, noting that + this allocation may be done before pass_final. */ -/* Helper function for compute_barrier_args_size. Handle one insn. */ - -static HOST_WIDE_INT -compute_barrier_args_size_1 (rtx insn, HOST_WIDE_INT cur_args_size, - VEC (rtx, heap) **next) +dw_fde_ref +dwarf2out_alloc_current_fde (void) { - HOST_WIDE_INT offset = 0; - int i; + dw_fde_ref fde; - if (! RTX_FRAME_RELATED_P (insn)) - { - if (prologue_epilogue_contains (insn)) - /* Nothing */; - else if (GET_CODE (PATTERN (insn)) == SET) - offset = stack_adjust_offset (PATTERN (insn), cur_args_size, 0); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - || GET_CODE (PATTERN (insn)) == SEQUENCE) - { - /* There may be stack adjustments inside compound insns. Search - for them. */ - for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) - offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i), - cur_args_size, offset); - } - } - else - { - rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); + fde = ggc_alloc_cleared_dw_fde_node (); + fde->decl = current_function_decl; + fde->funcdef_number = current_function_funcdef_no; + fde->fde_index = VEC_length (dw_fde_ref, 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; - if (expr) - { - expr = XEXP (expr, 0); - if (GET_CODE (expr) == PARALLEL - || GET_CODE (expr) == SEQUENCE) - for (i = 1; i < XVECLEN (expr, 0); i++) - { - rtx elem = XVECEXP (expr, 0, i); + /* Record the FDE associated with this function. */ + cfun->fde = fde; + VEC_safe_push (dw_fde_ref, gc, fde_vec, fde); - if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem)) - offset += stack_adjust_offset (elem, cur_args_size, offset); - } - } - } + return fde; +} -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif +/* Output a marker (i.e. a label) for the beginning of a function, before + the prologue. */ - cur_args_size += offset; - if (cur_args_size < 0) - cur_args_size = 0; +void +dwarf2out_begin_prologue (unsigned int line ATTRIBUTE_UNUSED, + const char *file ATTRIBUTE_UNUSED) +{ + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + char * dup_label; + dw_fde_ref fde; + section *fnsec; + bool do_frame; - if (JUMP_P (insn)) - { - rtx dest = JUMP_LABEL (insn); + current_function_func_begin_label = NULL; - if (dest) - { - if (barrier_args_size [INSN_UID (dest)] < 0) - { - barrier_args_size [INSN_UID (dest)] = cur_args_size; - VEC_safe_push (rtx, heap, *next, dest); - } - } - } + do_frame = dwarf2out_do_frame (); - return cur_args_size; -} + /* ??? current_function_func_begin_label is also used by except.c for + call-site information. We must emit this label if it might be used. */ + if (!do_frame + && (!flag_exceptions + || targetm_common.except_unwind_info (&global_options) != UI_TARGET)) + return; -/* Walk the whole function and compute args_size on BARRIERs. */ + 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; -static void -compute_barrier_args_size (void) -{ - int max_uid = get_max_uid (), i; - rtx insn; - VEC (rtx, heap) *worklist, *next, *tmp; + /* We can elide the fde allocation if we're not emitting debug info. */ + if (!do_frame) + return; - barrier_args_size = XNEWVEC (HOST_WIDE_INT, max_uid); - for (i = 0; i < max_uid; i++) - barrier_args_size[i] = -1; + /* 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 (); - worklist = VEC_alloc (rtx, heap, 20); - next = VEC_alloc (rtx, heap, 20); - insn = get_insns (); - barrier_args_size[INSN_UID (insn)] = 0; - VEC_quick_push (rtx, worklist, insn); - for (;;) - { - while (!VEC_empty (rtx, worklist)) - { - rtx prev, body, first_insn; - HOST_WIDE_INT cur_args_size; - - first_insn = insn = VEC_pop (rtx, worklist); - cur_args_size = barrier_args_size[INSN_UID (insn)]; - prev = prev_nonnote_insn (insn); - if (prev && BARRIER_P (prev)) - barrier_args_size[INSN_UID (prev)] = cur_args_size; - - for (; insn; insn = NEXT_INSN (insn)) - { - if (INSN_DELETED_P (insn) || NOTE_P (insn)) - continue; - if (BARRIER_P (insn)) - break; - - if (LABEL_P (insn)) - { - if (insn == first_insn) - continue; - else if (barrier_args_size[INSN_UID (insn)] < 0) - { - barrier_args_size[INSN_UID (insn)] = cur_args_size; - continue; - } - else - { - /* The insns starting with this label have been - already scanned or are in the worklist. */ - break; - } - } + /* 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)); - body = PATTERN (insn); - if (GET_CODE (body) == SEQUENCE) - { - HOST_WIDE_INT dest_args_size = cur_args_size; - for (i = 1; i < XVECLEN (body, 0); i++) - if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0)) - && INSN_FROM_TARGET_P (XVECEXP (body, 0, i))) - dest_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, i), - dest_args_size, &next); - else - cur_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, i), - cur_args_size, &next); - - if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0))) - compute_barrier_args_size_1 (XVECEXP (body, 0, 0), - dest_args_size, &next); - else - cur_args_size - = compute_barrier_args_size_1 (XVECEXP (body, 0, 0), - cur_args_size, &next); - } - else - cur_args_size - = compute_barrier_args_size_1 (insn, cur_args_size, &next); - } - } + /* We only want to output line number information for the genuine dwarf2 + prologue case, not the eh frame case. */ +#ifdef DWARF2_DEBUGGING_INFO + if (file) + dwarf2out_source_line (line, file, 0, true); +#endif - if (VEC_empty (rtx, next)) - break; + 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; - /* Swap WORKLIST with NEXT and truncate NEXT for next iteration. */ - tmp = next; - next = worklist; - worklist = tmp; - VEC_truncate (rtx, next, 0); + /* 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"); } - - VEC_free (rtx, heap, worklist); - VEC_free (rtx, heap, next); } -/* Add a CFI to update the running total of the size of arguments - pushed onto the stack. */ +/* 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. */ -static void -dwarf2out_args_size (const char *label, HOST_WIDE_INT size) +void +dwarf2out_vms_end_prologue (unsigned int line ATTRIBUTE_UNUSED, + const char *file ATTRIBUTE_UNUSED) { - dw_cfi_ref cfi; - - if (size == old_args_size) - return; - - old_args_size = size; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; - cfi = new_cfi (); - cfi->dw_cfi_opc = DW_CFA_GNU_args_size; - cfi->dw_cfi_oprnd1.dw_cfi_offset = size; - add_fde_cfi (label, cfi); + /* 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); } -/* Record a stack adjustment of OFFSET bytes. */ +/* 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. */ -static void -dwarf2out_stack_adjust (HOST_WIDE_INT offset, const char *label) +void +dwarf2out_vms_begin_epilogue (unsigned int line ATTRIBUTE_UNUSED, + const char *file ATTRIBUTE_UNUSED) { - if (cfa.reg == STACK_POINTER_REGNUM) - cfa.offset += offset; - - if (cfa_store.reg == STACK_POINTER_REGNUM) - cfa_store.offset += offset; + dw_fde_ref fde = cfun->fde; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; - if (ACCUMULATE_OUTGOING_ARGS) + if (fde->dw_fde_vms_begin_epilogue) return; -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - - args_size += offset; - if (args_size < 0) - args_size = 0; - - def_cfa_1 (label, &cfa); - if (flag_asynchronous_unwind_tables) - dwarf2out_args_size (label, args_size); + /* 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); } -/* Check INSN to see if it looks like a push or a stack adjustment, and - make a note of it if it does. EH uses this information to find out - how much extra space it needs to pop off the stack. */ +/* 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. */ -static void -dwarf2out_notice_stack_adjust (rtx insn, bool after_p) +void +dwarf2out_end_epilogue (unsigned int line ATTRIBUTE_UNUSED, + const char *file ATTRIBUTE_UNUSED) { - HOST_WIDE_INT offset; - const char *label; - int i; - - /* Don't handle epilogues at all. Certainly it would be wrong to do so - with this function. Proper support would require all frame-related - insns to be marked, and to be able to handle saving state around - epilogues textually in the middle of the function. */ - if (prologue_epilogue_contains (insn)) - return; - - /* If INSN is an instruction from target of an annulled branch, the - effects are for the target only and so current argument size - shouldn't change at all. */ - if (final_sequence - && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)) - && INSN_FROM_TARGET_P (insn)) - return; - - /* If only calls can throw, and we have a frame pointer, - save up adjustments until we see the CALL_INSN. */ - if (!flag_asynchronous_unwind_tables && cfa.reg != STACK_POINTER_REGNUM) - { - if (CALL_P (insn) && !after_p) - { - /* Extract the size of the args from the CALL rtx itself. */ - insn = PATTERN (insn); - if (GET_CODE (insn) == PARALLEL) - insn = XVECEXP (insn, 0, 0); - if (GET_CODE (insn) == SET) - insn = SET_SRC (insn); - gcc_assert (GET_CODE (insn) == CALL); - dwarf2out_args_size ("", INTVAL (XEXP (insn, 1))); - } - return; - } - - if (CALL_P (insn) && !after_p) - { - if (!flag_asynchronous_unwind_tables) - dwarf2out_args_size ("", args_size); - return; - } - else if (BARRIER_P (insn)) - { - /* Don't call compute_barrier_args_size () if the only - BARRIER is at the end of function. */ - if (barrier_args_size == NULL && next_nonnote_insn (insn)) - compute_barrier_args_size (); - if (barrier_args_size == NULL) - offset = 0; - else - { - offset = barrier_args_size[INSN_UID (insn)]; - if (offset < 0) - offset = 0; - } + dw_fde_ref fde; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; - offset -= args_size; -#ifndef STACK_GROWS_DOWNWARD - offset = -offset; -#endif - } - else if (GET_CODE (PATTERN (insn)) == SET) - offset = stack_adjust_offset (PATTERN (insn), args_size, 0); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - || GET_CODE (PATTERN (insn)) == SEQUENCE) - { - /* There may be stack adjustments inside compound insns. Search - for them. */ - for (offset = 0, i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) - offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i), - args_size, offset); - } - else - return; + last_var_location_insn = NULL_RTX; + cached_next_real_insn = NULL_RTX; - if (offset == 0) - return; + if (dwarf2out_do_cfi_asm ()) + fprintf (asm_out_file, "\t.cfi_endproc\n"); - label = dwarf2out_cfi_label (false); - dwarf2out_stack_adjust (offset, label); + /* 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); } -/* We delay emitting a register save until either (a) we reach the end - of the prologue or (b) the register is clobbered. This clusters - register saves so that there are fewer pc advances. */ - -struct GTY(()) queued_reg_save { - struct queued_reg_save *next; - rtx reg; - HOST_WIDE_INT cfa_offset; - rtx saved_reg; -}; - -static GTY(()) struct queued_reg_save *queued_reg_saves; - -/* The caller's ORIG_REG is saved in SAVED_IN_REG. */ -struct GTY(()) reg_saved_in_data { - rtx orig_reg; - rtx saved_in_reg; -}; - -/* A list of registers saved in other registers. - The list intentionally has a small maximum capacity of 4; if your - port needs more than that, you might consider implementing a - more efficient data structure. */ -static GTY(()) struct reg_saved_in_data regs_saved_in_regs[4]; -static GTY(()) size_t num_regs_saved_in_regs; - -static const char *last_reg_save_label; - -/* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at - SREG, or if SREG is NULL then it is saved at OFFSET to the CFA. */ - -static void -queue_reg_save (const char *label, rtx reg, rtx sreg, HOST_WIDE_INT offset) +void +dwarf2out_frame_finish (void) { - struct queued_reg_save *q; - - /* Duplicates waste space, but it's also necessary to remove them - for correctness, since the queue gets output in reverse - order. */ - for (q = queued_reg_saves; q != NULL; q = q->next) - if (REGNO (q->reg) == REGNO (reg)) - break; - - if (q == NULL) - { - q = ggc_alloc_queued_reg_save (); - q->next = queued_reg_saves; - queued_reg_saves = q; - } - - q->reg = reg; - q->cfa_offset = offset; - q->saved_reg = sreg; + /* Output call frame information. */ + if (targetm.debug_unwind_info () == UI_DWARF2) + output_call_frame_info (0); - last_reg_save_label = label; + /* Output another copy for the unwinder. */ + if ((flag_unwind_tables || flag_exceptions) + && targetm_common.except_unwind_info (&global_options) == UI_DWARF2) + output_call_frame_info (1); } -/* Output all the entries in QUEUED_REG_SAVES. */ +/* Note that the current function section is being used for code. */ static void -flush_queued_reg_saves (void) +dwarf2out_note_section_used (void) { - struct queued_reg_save *q; - - for (q = queued_reg_saves; q; q = q->next) - { - size_t i; - unsigned int reg, sreg; - - for (i = 0; i < num_regs_saved_in_regs; i++) - if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (q->reg)) - break; - if (q->saved_reg && i == num_regs_saved_in_regs) - { - gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); - num_regs_saved_in_regs++; - } - if (i != num_regs_saved_in_regs) - { - regs_saved_in_regs[i].orig_reg = q->reg; - regs_saved_in_regs[i].saved_in_reg = q->saved_reg; - } - - reg = DWARF_FRAME_REGNUM (REGNO (q->reg)); - if (q->saved_reg) - sreg = DWARF_FRAME_REGNUM (REGNO (q->saved_reg)); - else - sreg = INVALID_REGNUM; - reg_save (last_reg_save_label, reg, sreg, q->cfa_offset); - } - - queued_reg_saves = NULL; - last_reg_save_label = NULL; + section *sec = current_function_section (); + if (sec == text_section) + text_section_used = true; + else if (sec == cold_text_section) + cold_text_section_used = true; } -/* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved - location for? Or, does it clobber a register which we've previously - said that some other register is saved in, and for which we now - have a new location for? */ +static void var_location_switch_text_section (void); +static void set_cur_line_info_table (section *); -static bool -clobbers_queued_reg_save (const_rtx insn) +void +dwarf2out_switch_text_section (void) { - struct queued_reg_save *q; + section *sect; + dw_fde_ref fde = cfun->fde; + + gcc_assert (cfun && fde && fde->dw_fde_second_begin == NULL); - for (q = queued_reg_saves; q; q = q->next) + if (!in_cold_section_p) { - size_t i; - if (modified_in_p (q->reg, insn)) - return true; - for (i = 0; i < num_regs_saved_in_regs; i++) - if (REGNO (q->reg) == REGNO (regs_saved_in_regs[i].orig_reg) - && modified_in_p (regs_saved_in_regs[i].saved_in_reg, insn)) - return true; + fde->dw_fde_end = crtl->subsections.cold_section_end_label; + fde->dw_fde_second_begin = crtl->subsections.hot_section_label; + fde->dw_fde_second_end = crtl->subsections.hot_section_end_label; } - - return false; -} - -/* Entry point for saving the first register into the second. */ - -void -dwarf2out_reg_save_reg (const char *label, rtx reg, rtx sreg) -{ - size_t i; - unsigned int regno, sregno; - - for (i = 0; i < num_regs_saved_in_regs; i++) - if (REGNO (regs_saved_in_regs[i].orig_reg) == REGNO (reg)) - break; - if (i == num_regs_saved_in_regs) + else { - gcc_assert (i != ARRAY_SIZE (regs_saved_in_regs)); - num_regs_saved_in_regs++; + fde->dw_fde_end = crtl->subsections.hot_section_end_label; + fde->dw_fde_second_begin = crtl->subsections.cold_section_label; + fde->dw_fde_second_end = crtl->subsections.cold_section_end_label; } - regs_saved_in_regs[i].orig_reg = reg; - regs_saved_in_regs[i].saved_in_reg = sreg; - - regno = DWARF_FRAME_REGNUM (REGNO (reg)); - sregno = DWARF_FRAME_REGNUM (REGNO (sreg)); - reg_save (label, regno, sregno, 0); -} + have_multiple_function_sections = true; -/* What register, if any, is currently saved in REG? */ + /* There is no need to mark used sections when not debugging. */ + if (cold_text_section != NULL) + dwarf2out_note_section_used (); -static rtx -reg_saved_in (rtx reg) -{ - unsigned int regn = REGNO (reg); - size_t i; - struct queued_reg_save *q; + if (dwarf2out_do_cfi_asm ()) + fprintf (asm_out_file, "\t.cfi_endproc\n"); - for (q = queued_reg_saves; q; q = q->next) - if (q->saved_reg && regn == REGNO (q->saved_reg)) - return q->reg; + /* Now do the real section switch. */ + sect = current_function_section (); + switch_to_section (sect); - for (i = 0; i < num_regs_saved_in_regs; i++) - if (regs_saved_in_regs[i].saved_in_reg - && regn == REGNO (regs_saved_in_regs[i].saved_in_reg)) - return regs_saved_in_regs[i].orig_reg; + fde->second_in_std_section + = (sect == text_section + || (cold_text_section && sect == cold_text_section)); - return NULL_RTX; -} + if (dwarf2out_do_cfi_asm ()) + dwarf2out_do_cfi_startproc (true); + var_location_switch_text_section (); -/* A temporary register holding an integral value used in adjusting SP - or setting up the store_reg. The "offset" field holds the integer - value, not an offset. */ -static dw_cfa_location cfa_temp; + set_cur_line_info_table (sect); +} + +/* And now, the subset of the debugging information support code necessary + for emitting location expressions. */ -/* A subroutine of dwarf2out_frame_debug, process a REG_DEF_CFA note. */ +/* Data about a single source file. */ +struct GTY(()) dwarf_file_data { + const char * filename; + int emitted_number; +}; -static void -dwarf2out_frame_debug_def_cfa (rtx pat, const char *label) +typedef struct GTY(()) deferred_locations_struct { - memset (&cfa, 0, sizeof (cfa)); + tree variable; + dw_die_ref die; +} deferred_locations; - switch (GET_CODE (pat)) - { - case PLUS: - cfa.reg = REGNO (XEXP (pat, 0)); - cfa.offset = INTVAL (XEXP (pat, 1)); - break; +DEF_VEC_O(deferred_locations); +DEF_VEC_ALLOC_O(deferred_locations,gc); - case REG: - cfa.reg = REGNO (pat); - break; +static GTY(()) VEC(deferred_locations, gc) *deferred_locations_list; - case MEM: - cfa.indirect = 1; - pat = XEXP (pat, 0); - if (GET_CODE (pat) == PLUS) - { - cfa.base_offset = INTVAL (XEXP (pat, 1)); - pat = XEXP (pat, 0); - } - cfa.reg = REGNO (pat); - break; +DEF_VEC_P(dw_die_ref); +DEF_VEC_ALLOC_P(dw_die_ref,heap); - default: - /* Recurse and define an expression. */ - gcc_unreachable (); - } +/* 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 for begin address of range */ + const char *end; /* Label for end address of range */ + char *ll_symbol; /* Label for beginning of location list. + Only on head of list */ + const char *section; /* Section this loclist is relative to */ + dw_loc_descr_ref expr; + hashval_t hash; + /* True if all addresses in this and subsequent lists are known to be + resolved. */ + bool resolved_addr; + /* True if this list has been replaced by dw_loc_next. */ + bool replaced; + bool emitted; + /* True if the range should be emitted even if begin and end + are the same. */ + bool force; +} dw_loc_list_node; - def_cfa_1 (label, &cfa); -} +static dw_loc_descr_ref int_loc_descriptor (HOST_WIDE_INT); -/* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note. */ +/* Convert a DWARF stack opcode into its string name. */ -static void -dwarf2out_frame_debug_adjust_cfa (rtx pat, const char *label) -{ - rtx src, dest; - - gcc_assert (GET_CODE (pat) == SET); - dest = XEXP (pat, 0); - src = XEXP (pat, 1); - - switch (GET_CODE (src)) - { - case PLUS: - gcc_assert (REGNO (XEXP (src, 0)) == cfa.reg); - cfa.offset -= INTVAL (XEXP (src, 1)); - break; - - case REG: - break; - - default: - gcc_unreachable (); - } - - cfa.reg = REGNO (dest); - gcc_assert (cfa.indirect == 0); - - def_cfa_1 (label, &cfa); -} - -/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_OFFSET note. */ - -static void -dwarf2out_frame_debug_cfa_offset (rtx set, const char *label) -{ - HOST_WIDE_INT offset; - rtx src, addr, span; - - src = XEXP (set, 1); - addr = XEXP (set, 0); - gcc_assert (MEM_P (addr)); - addr = XEXP (addr, 0); - - /* As documented, only consider extremely simple addresses. */ - switch (GET_CODE (addr)) - { - case REG: - gcc_assert (REGNO (addr) == cfa.reg); - offset = -cfa.offset; - break; - case PLUS: - gcc_assert (REGNO (XEXP (addr, 0)) == cfa.reg); - offset = INTVAL (XEXP (addr, 1)) - cfa.offset; - break; - default: - gcc_unreachable (); - } - - span = targetm.dwarf_register_span (src); - - /* ??? We'd like to use queue_reg_save, but we need to come up with - a different flushing heuristic for epilogues. */ - if (!span) - reg_save (label, DWARF_FRAME_REGNUM (REGNO (src)), INVALID_REGNUM, offset); - else - { - /* We have a PARALLEL describing where the contents of SRC live. - Queue register saves for each piece of the PARALLEL. */ - int par_index; - int limit; - HOST_WIDE_INT span_offset = offset; - - gcc_assert (GET_CODE (span) == PARALLEL); - - limit = XVECLEN (span, 0); - for (par_index = 0; par_index < limit; par_index++) - { - rtx elem = XVECEXP (span, 0, par_index); - - reg_save (label, DWARF_FRAME_REGNUM (REGNO (elem)), - INVALID_REGNUM, span_offset); - span_offset += GET_MODE_SIZE (GET_MODE (elem)); - } - } -} - -/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_REGISTER note. */ - -static void -dwarf2out_frame_debug_cfa_register (rtx set, const char *label) -{ - rtx src, dest; - unsigned sregno, dregno; - - src = XEXP (set, 1); - dest = XEXP (set, 0); - - if (src == pc_rtx) - sregno = DWARF_FRAME_RETURN_COLUMN; - else - sregno = DWARF_FRAME_REGNUM (REGNO (src)); - - dregno = DWARF_FRAME_REGNUM (REGNO (dest)); - - /* ??? We'd like to use queue_reg_save, but we need to come up with - a different flushing heuristic for epilogues. */ - reg_save (label, sregno, dregno, 0); -} - -/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */ - -static void -dwarf2out_frame_debug_cfa_expression (rtx set, const char *label) -{ - rtx src, dest, span; - dw_cfi_ref cfi = new_cfi (); - - dest = SET_DEST (set); - src = SET_SRC (set); - - gcc_assert (REG_P (src)); - gcc_assert (MEM_P (dest)); - - span = targetm.dwarf_register_span (src); - gcc_assert (!span); - - cfi->dw_cfi_opc = DW_CFA_expression; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (REGNO (src)); - cfi->dw_cfi_oprnd2.dw_cfi_loc - = mem_loc_descriptor (XEXP (dest, 0), GET_MODE (dest), - VAR_INIT_STATUS_INITIALIZED); - - /* ??? We'd like to use queue_reg_save, were the interface different, - and, as above, we could manage flushing for epilogues. */ - add_fde_cfi (label, cfi); -} - -/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note. */ - -static void -dwarf2out_frame_debug_cfa_restore (rtx reg, const char *label) -{ - dw_cfi_ref cfi = new_cfi (); - unsigned int regno = DWARF_FRAME_REGNUM (REGNO (reg)); - - cfi->dw_cfi_opc = (regno & ~0x3f ? DW_CFA_restore_extended : DW_CFA_restore); - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = regno; - - add_fde_cfi (label, cfi); -} - -/* Record call frame debugging information for an expression EXPR, - which either sets SP or FP (adjusting how we calculate the frame - address) or saves a register to the stack or another register. - LABEL indicates the address of EXPR. - - This function encodes a state machine mapping rtxes to actions on - cfa, cfa_store, and cfa_temp.reg. We describe these rules so - users need not read the source code. - - The High-Level Picture - - Changes in the register we use to calculate the CFA: Currently we - assume that if you copy the CFA register into another register, we - should take the other one as the new CFA register; this seems to - work pretty well. If it's wrong for some target, it's simple - enough not to set RTX_FRAME_RELATED_P on the insn in question. - - Changes in the register we use for saving registers to the stack: - This is usually SP, but not always. Again, we deduce that if you - copy SP into another register (and SP is not the CFA register), - then the new register is the one we will be using for register - saves. This also seems to work. - - Register saves: There's not much guesswork about this one; if - RTX_FRAME_RELATED_P is set on an insn which modifies memory, it's a - register save, and the register used to calculate the destination - had better be the one we think we're using for this purpose. - It's also assumed that a copy from a call-saved register to another - register is saving that register if RTX_FRAME_RELATED_P is set on - that instruction. If the copy is from a call-saved register to - the *same* register, that means that the register is now the same - value as in the caller. - - Except: If the register being saved is the CFA register, and the - offset is nonzero, we are saving the CFA, so we assume we have to - use DW_CFA_def_cfa_expression. If the offset is 0, we assume that - the intent is to save the value of SP from the previous frame. - - In addition, if a register has previously been saved to a different - register, - - Invariants / Summaries of Rules - - cfa current rule for calculating the CFA. It usually - consists of a register and an offset. - cfa_store register used by prologue code to save things to the stack - cfa_store.offset is the offset from the value of - cfa_store.reg to the actual CFA - cfa_temp register holding an integral value. cfa_temp.offset - stores the value, which will be used to adjust the - stack pointer. cfa_temp is also used like cfa_store, - to track stores to the stack via fp or a temp reg. - - Rules 1- 4: Setting a register's value to cfa.reg or an expression - with cfa.reg as the first operand changes the cfa.reg and its - cfa.offset. Rule 1 and 4 also set cfa_temp.reg and - cfa_temp.offset. - - Rules 6- 9: Set a non-cfa.reg register value to a constant or an - expression yielding a constant. This sets cfa_temp.reg - and cfa_temp.offset. - - Rule 5: Create a new register cfa_store used to save items to the - stack. - - Rules 10-14: Save a register to the stack. Define offset as the - difference of the original location and cfa_store's - location (or cfa_temp's location if cfa_temp is used). - - Rules 16-20: If AND operation happens on sp in prologue, we assume - stack is realigned. We will use a group of DW_OP_XXX - expressions to represent the location of the stored - register instead of CFA+offset. - - The Rules - - "{a,b}" indicates a choice of a xor b. - ":cfa.reg" indicates that must equal cfa.reg. - - Rule 1: - (set :cfa.reg) - effects: cfa.reg = - cfa.offset unchanged - cfa_temp.reg = - cfa_temp.offset = cfa.offset - - Rule 2: - (set sp ({minus,plus,losum} {sp,fp}:cfa.reg - {,:cfa_temp.reg})) - effects: cfa.reg = sp if fp used - cfa.offset += {+/- , cfa_temp.offset} if cfa.reg==sp - cfa_store.offset += {+/- , cfa_temp.offset} - if cfa_store.reg==sp - - Rule 3: - (set fp ({minus,plus,losum} :cfa.reg )) - effects: cfa.reg = fp - cfa_offset += +/- - - Rule 4: - (set ({plus,losum} :cfa.reg )) - constraints: != fp - != sp - effects: cfa.reg = - cfa_temp.reg = - cfa_temp.offset = cfa.offset - - Rule 5: - (set (plus :cfa_temp.reg sp:cfa.reg)) - constraints: != fp - != sp - effects: cfa_store.reg = - cfa_store.offset = cfa.offset - cfa_temp.offset - - Rule 6: - (set ) - effects: cfa_temp.reg = - cfa_temp.offset = - - Rule 7: - (set :cfa_temp.reg (ior :cfa_temp.reg )) - effects: cfa_temp.reg = - cfa_temp.offset |= - - Rule 8: - (set (high )) - effects: none - - Rule 9: - (set (lo_sum )) - effects: cfa_temp.reg = - cfa_temp.offset = - - Rule 10: - (set (mem (pre_modify sp:cfa_store (???? ))) ) - effects: cfa_store.offset -= - cfa.offset = cfa_store.offset if cfa.reg == sp - cfa.reg = sp - cfa.base_offset = -cfa_store.offset - - Rule 11: - (set (mem ({pre_inc,pre_dec} sp:cfa_store.reg)) ) - effects: cfa_store.offset += -/+ mode_size(mem) - cfa.offset = cfa_store.offset if cfa.reg == sp - cfa.reg = sp - cfa.base_offset = -cfa_store.offset - - Rule 12: - (set (mem ({minus,plus,losum} :{cfa_store,cfa_temp} )) - - ) - effects: cfa.reg = - cfa.base_offset = -/+ - {cfa_store,cfa_temp}.offset - - Rule 13: - (set (mem :{cfa_store,cfa_temp}) ) - effects: cfa.reg = - cfa.base_offset = -{cfa_store,cfa_temp}.offset - - Rule 14: - (set (mem (postinc :cfa_temp )) ) - effects: cfa.reg = - cfa.base_offset = -cfa_temp.offset - cfa_temp.offset -= mode_size(mem) - - Rule 15: - (set {unspec, unspec_volatile}) - effects: target-dependent - - Rule 16: - (set sp (and: sp )) - constraints: cfa_store.reg == sp - effects: current_fde.stack_realign = 1 - cfa_store.offset = 0 - fde->drap_reg = cfa.reg if cfa.reg != sp and cfa.reg != fp - - Rule 17: - (set (mem ({pre_inc, pre_dec} sp)) (mem (plus (cfa.reg) (const_int)))) - effects: cfa_store.offset += -/+ mode_size(mem) - - Rule 18: - (set (mem ({pre_inc, pre_dec} sp)) fp) - constraints: fde->stack_realign == 1 - effects: cfa_store.offset = 0 - cfa.reg != HARD_FRAME_POINTER_REGNUM - - Rule 19: - (set (mem ({pre_inc, pre_dec} sp)) cfa.reg) - constraints: fde->stack_realign == 1 - && cfa.offset == 0 - && cfa.indirect == 0 - && cfa.reg != HARD_FRAME_POINTER_REGNUM - effects: Use DW_CFA_def_cfa_expression to define cfa - cfa.reg == fde->drap_reg */ - -static void -dwarf2out_frame_debug_expr (rtx expr, const char *label) -{ - rtx src, dest, span; - HOST_WIDE_INT offset; - dw_fde_ref fde; - - /* If RTX_FRAME_RELATED_P is set on a PARALLEL, process each member of - the PARALLEL independently. The first element is always processed if - it is a SET. This is for backward compatibility. Other elements - are processed only if they are SETs and the RTX_FRAME_RELATED_P - flag is set in them. */ - if (GET_CODE (expr) == PARALLEL || GET_CODE (expr) == SEQUENCE) - { - int par_index; - int limit = XVECLEN (expr, 0); - rtx elem; - - /* PARALLELs have strict read-modify-write semantics, so we - ought to evaluate every rvalue before changing any lvalue. - It's cumbersome to do that in general, but there's an - easy approximation that is enough for all current users: - handle register saves before register assignments. */ - if (GET_CODE (expr) == PARALLEL) - for (par_index = 0; par_index < limit; par_index++) - { - elem = XVECEXP (expr, 0, par_index); - if (GET_CODE (elem) == SET - && MEM_P (SET_DEST (elem)) - && (RTX_FRAME_RELATED_P (elem) || par_index == 0)) - dwarf2out_frame_debug_expr (elem, label); - } - - for (par_index = 0; par_index < limit; par_index++) - { - elem = XVECEXP (expr, 0, par_index); - if (GET_CODE (elem) == SET - && (!MEM_P (SET_DEST (elem)) || GET_CODE (expr) == SEQUENCE) - && (RTX_FRAME_RELATED_P (elem) || par_index == 0)) - dwarf2out_frame_debug_expr (elem, label); - else if (GET_CODE (elem) == SET - && par_index != 0 - && !RTX_FRAME_RELATED_P (elem)) - { - /* Stack adjustment combining might combine some post-prologue - stack adjustment into a prologue stack adjustment. */ - HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0); - - if (offset != 0) - dwarf2out_stack_adjust (offset, label); - } - } - return; - } - - gcc_assert (GET_CODE (expr) == SET); - - src = SET_SRC (expr); - dest = SET_DEST (expr); - - if (REG_P (src)) - { - rtx rsi = reg_saved_in (src); - if (rsi) - src = rsi; - } - - fde = current_fde (); - - switch (GET_CODE (dest)) - { - case REG: - switch (GET_CODE (src)) - { - /* Setting FP from SP. */ - case REG: - if (cfa.reg == (unsigned) REGNO (src)) - { - /* Rule 1 */ - /* Update the CFA rule wrt SP or FP. Make sure src is - relative to the current CFA register. - - We used to require that dest be either SP or FP, but the - ARM copies SP to a temporary register, and from there to - FP. So we just rely on the backends to only set - RTX_FRAME_RELATED_P on appropriate insns. */ - cfa.reg = REGNO (dest); - cfa_temp.reg = cfa.reg; - cfa_temp.offset = cfa.offset; - } - else - { - /* Saving a register in a register. */ - gcc_assert (!fixed_regs [REGNO (dest)] - /* For the SPARC and its register window. */ - || (DWARF_FRAME_REGNUM (REGNO (src)) - == DWARF_FRAME_RETURN_COLUMN)); - - /* After stack is aligned, we can only save SP in FP - if drap register is used. In this case, we have - to restore stack pointer with the CFA value and we - don't generate this DWARF information. */ - if (fde - && fde->stack_realign - && REGNO (src) == STACK_POINTER_REGNUM) - gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM - && fde->drap_reg != INVALID_REGNUM - && cfa.reg != REGNO (src)); - else - queue_reg_save (label, src, dest, 0); - } - break; - - case PLUS: - case MINUS: - case LO_SUM: - if (dest == stack_pointer_rtx) - { - /* Rule 2 */ - /* Adjusting SP. */ - switch (GET_CODE (XEXP (src, 1))) - { - case CONST_INT: - offset = INTVAL (XEXP (src, 1)); - break; - case REG: - gcc_assert ((unsigned) REGNO (XEXP (src, 1)) - == cfa_temp.reg); - offset = cfa_temp.offset; - break; - default: - gcc_unreachable (); - } - - if (XEXP (src, 0) == hard_frame_pointer_rtx) - { - /* Restoring SP from FP in the epilogue. */ - gcc_assert (cfa.reg == (unsigned) HARD_FRAME_POINTER_REGNUM); - cfa.reg = STACK_POINTER_REGNUM; - } - else if (GET_CODE (src) == LO_SUM) - /* Assume we've set the source reg of the LO_SUM from sp. */ - ; - else - gcc_assert (XEXP (src, 0) == stack_pointer_rtx); - - if (GET_CODE (src) != MINUS) - offset = -offset; - if (cfa.reg == STACK_POINTER_REGNUM) - cfa.offset += offset; - if (cfa_store.reg == STACK_POINTER_REGNUM) - cfa_store.offset += offset; - } - else if (dest == hard_frame_pointer_rtx) - { - /* Rule 3 */ - /* Either setting the FP from an offset of the SP, - or adjusting the FP */ - gcc_assert (frame_pointer_needed); - - gcc_assert (REG_P (XEXP (src, 0)) - && (unsigned) REGNO (XEXP (src, 0)) == cfa.reg - && CONST_INT_P (XEXP (src, 1))); - offset = INTVAL (XEXP (src, 1)); - if (GET_CODE (src) != MINUS) - offset = -offset; - cfa.offset += offset; - cfa.reg = HARD_FRAME_POINTER_REGNUM; - } - else - { - gcc_assert (GET_CODE (src) != MINUS); - - /* Rule 4 */ - if (REG_P (XEXP (src, 0)) - && REGNO (XEXP (src, 0)) == cfa.reg - && CONST_INT_P (XEXP (src, 1))) - { - /* Setting a temporary CFA register that will be copied - into the FP later on. */ - offset = - INTVAL (XEXP (src, 1)); - cfa.offset += offset; - cfa.reg = REGNO (dest); - /* Or used to save regs to the stack. */ - cfa_temp.reg = cfa.reg; - cfa_temp.offset = cfa.offset; - } - - /* Rule 5 */ - else if (REG_P (XEXP (src, 0)) - && REGNO (XEXP (src, 0)) == cfa_temp.reg - && XEXP (src, 1) == stack_pointer_rtx) - { - /* Setting a scratch register that we will use instead - of SP for saving registers to the stack. */ - gcc_assert (cfa.reg == STACK_POINTER_REGNUM); - cfa_store.reg = REGNO (dest); - cfa_store.offset = cfa.offset - cfa_temp.offset; - } - - /* Rule 9 */ - else if (GET_CODE (src) == LO_SUM - && CONST_INT_P (XEXP (src, 1))) - { - cfa_temp.reg = REGNO (dest); - cfa_temp.offset = INTVAL (XEXP (src, 1)); - } - else - gcc_unreachable (); - } - break; - - /* Rule 6 */ - case CONST_INT: - cfa_temp.reg = REGNO (dest); - cfa_temp.offset = INTVAL (src); - break; - - /* Rule 7 */ - case IOR: - gcc_assert (REG_P (XEXP (src, 0)) - && (unsigned) REGNO (XEXP (src, 0)) == cfa_temp.reg - && CONST_INT_P (XEXP (src, 1))); - - if ((unsigned) REGNO (dest) != cfa_temp.reg) - cfa_temp.reg = REGNO (dest); - cfa_temp.offset |= INTVAL (XEXP (src, 1)); - break; - - /* Skip over HIGH, assuming it will be followed by a LO_SUM, - which will fill in all of the bits. */ - /* Rule 8 */ - case HIGH: - break; - - /* Rule 15 */ - case UNSPEC: - case UNSPEC_VOLATILE: - gcc_assert (targetm.dwarf_handle_frame_unspec); - targetm.dwarf_handle_frame_unspec (label, expr, XINT (src, 1)); - return; - - /* Rule 16 */ - case AND: - /* If this AND operation happens on stack pointer in prologue, - we assume the stack is realigned and we extract the - alignment. */ - if (fde && XEXP (src, 0) == stack_pointer_rtx) - { - gcc_assert (cfa_store.reg == REGNO (XEXP (src, 0))); - fde->stack_realign = 1; - fde->stack_realignment = INTVAL (XEXP (src, 1)); - cfa_store.offset = 0; - - if (cfa.reg != STACK_POINTER_REGNUM - && cfa.reg != HARD_FRAME_POINTER_REGNUM) - fde->drap_reg = cfa.reg; - } - return; - - default: - gcc_unreachable (); - } - - def_cfa_1 (label, &cfa); - break; - - case MEM: - - /* Saving a register to the stack. Make sure dest is relative to the - CFA register. */ - switch (GET_CODE (XEXP (dest, 0))) - { - /* Rule 10 */ - /* With a push. */ - case PRE_MODIFY: - /* We can't handle variable size modifications. */ - gcc_assert (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) - == CONST_INT); - offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)); - - gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM - && cfa_store.reg == STACK_POINTER_REGNUM); - - cfa_store.offset += offset; - if (cfa.reg == STACK_POINTER_REGNUM) - cfa.offset = cfa_store.offset; - - offset = -cfa_store.offset; - break; - - /* Rule 11 */ - case PRE_INC: - case PRE_DEC: - offset = GET_MODE_SIZE (GET_MODE (dest)); - if (GET_CODE (XEXP (dest, 0)) == PRE_INC) - offset = -offset; - - gcc_assert ((REGNO (XEXP (XEXP (dest, 0), 0)) - == STACK_POINTER_REGNUM) - && cfa_store.reg == STACK_POINTER_REGNUM); - - cfa_store.offset += offset; - - /* Rule 18: If stack is aligned, we will use FP as a - reference to represent the address of the stored - regiser. */ - if (fde - && fde->stack_realign - && src == hard_frame_pointer_rtx) - { - gcc_assert (cfa.reg != HARD_FRAME_POINTER_REGNUM); - cfa_store.offset = 0; - } - - if (cfa.reg == STACK_POINTER_REGNUM) - cfa.offset = cfa_store.offset; - - offset = -cfa_store.offset; - break; - - /* Rule 12 */ - /* With an offset. */ - case PLUS: - case MINUS: - case LO_SUM: - { - int regno; - - gcc_assert (CONST_INT_P (XEXP (XEXP (dest, 0), 1)) - && REG_P (XEXP (XEXP (dest, 0), 0))); - offset = INTVAL (XEXP (XEXP (dest, 0), 1)); - if (GET_CODE (XEXP (dest, 0)) == MINUS) - offset = -offset; - - regno = REGNO (XEXP (XEXP (dest, 0), 0)); - - if (cfa_store.reg == (unsigned) regno) - offset -= cfa_store.offset; - else - { - gcc_assert (cfa_temp.reg == (unsigned) regno); - offset -= cfa_temp.offset; - } - } - break; - - /* Rule 13 */ - /* Without an offset. */ - case REG: - { - int regno = REGNO (XEXP (dest, 0)); - - if (cfa_store.reg == (unsigned) regno) - offset = -cfa_store.offset; - else - { - gcc_assert (cfa_temp.reg == (unsigned) regno); - offset = -cfa_temp.offset; - } - } - break; - - /* Rule 14 */ - case POST_INC: - gcc_assert (cfa_temp.reg - == (unsigned) REGNO (XEXP (XEXP (dest, 0), 0))); - offset = -cfa_temp.offset; - cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest)); - break; - - default: - gcc_unreachable (); - } - - /* Rule 17 */ - /* If the source operand of this MEM operation is not a - register, basically the source is return address. Here - we only care how much stack grew and we don't save it. */ - if (!REG_P (src)) - break; - - if (REGNO (src) != STACK_POINTER_REGNUM - && REGNO (src) != HARD_FRAME_POINTER_REGNUM - && (unsigned) REGNO (src) == cfa.reg) - { - /* We're storing the current CFA reg into the stack. */ - - if (cfa.offset == 0) - { - /* Rule 19 */ - /* If stack is aligned, putting CFA reg into stack means - we can no longer use reg + offset to represent CFA. - Here we use DW_CFA_def_cfa_expression instead. The - result of this expression equals to the original CFA - value. */ - if (fde - && fde->stack_realign - && cfa.indirect == 0 - && cfa.reg != HARD_FRAME_POINTER_REGNUM) - { - dw_cfa_location cfa_exp; - - gcc_assert (fde->drap_reg == cfa.reg); - - cfa_exp.indirect = 1; - cfa_exp.reg = HARD_FRAME_POINTER_REGNUM; - cfa_exp.base_offset = offset; - cfa_exp.offset = 0; - - fde->drap_reg_saved = 1; - - def_cfa_1 (label, &cfa_exp); - break; - } - - /* If the source register is exactly the CFA, assume - we're saving SP like any other register; this happens - on the ARM. */ - def_cfa_1 (label, &cfa); - queue_reg_save (label, stack_pointer_rtx, NULL_RTX, offset); - break; - } - else - { - /* Otherwise, we'll need to look in the stack to - calculate the CFA. */ - rtx x = XEXP (dest, 0); - - if (!REG_P (x)) - x = XEXP (x, 0); - gcc_assert (REG_P (x)); - - cfa.reg = REGNO (x); - cfa.base_offset = offset; - cfa.indirect = 1; - def_cfa_1 (label, &cfa); - break; - } - } - - def_cfa_1 (label, &cfa); - { - span = targetm.dwarf_register_span (src); - - if (!span) - queue_reg_save (label, src, NULL_RTX, offset); - else - { - /* We have a PARALLEL describing where the contents of SRC - live. Queue register saves for each piece of the - PARALLEL. */ - int par_index; - int limit; - HOST_WIDE_INT span_offset = offset; - - gcc_assert (GET_CODE (span) == PARALLEL); - - limit = XVECLEN (span, 0); - for (par_index = 0; par_index < limit; par_index++) - { - rtx elem = XVECEXP (span, 0, par_index); - - queue_reg_save (label, elem, NULL_RTX, span_offset); - span_offset += GET_MODE_SIZE (GET_MODE (elem)); - } - } - } - break; - - default: - gcc_unreachable (); - } -} - -/* Record call frame debugging information for INSN, which either - sets SP or FP (adjusting how we calculate the frame address) or saves a - register to the stack. If INSN is NULL_RTX, initialize our state. - - If AFTER_P is false, we're being called before the insn is emitted, - otherwise after. Call instructions get invoked twice. */ - -void -dwarf2out_frame_debug (rtx insn, bool after_p) -{ - const char *label; - rtx note, n; - bool handled_one = false; - - if (insn == NULL_RTX) - { - size_t i; - - /* Flush any queued register saves. */ - flush_queued_reg_saves (); - - /* Set up state for generating call frame debug info. */ - lookup_cfa (&cfa); - gcc_assert (cfa.reg - == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM)); - - cfa.reg = STACK_POINTER_REGNUM; - cfa_store = cfa; - cfa_temp.reg = -1; - cfa_temp.offset = 0; - - for (i = 0; i < num_regs_saved_in_regs; i++) - { - regs_saved_in_regs[i].orig_reg = NULL_RTX; - regs_saved_in_regs[i].saved_in_reg = NULL_RTX; - } - num_regs_saved_in_regs = 0; - - if (barrier_args_size) - { - XDELETEVEC (barrier_args_size); - barrier_args_size = NULL; - } - return; - } - - if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn)) - flush_queued_reg_saves (); - - if (!RTX_FRAME_RELATED_P (insn)) - { - /* ??? This should be done unconditionally since stack adjustments - matter if the stack pointer is not the CFA register anymore but - is still used to save registers. */ - if (!ACCUMULATE_OUTGOING_ARGS) - dwarf2out_notice_stack_adjust (insn, after_p); - return; - } - - label = dwarf2out_cfi_label (false); - - for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) - switch (REG_NOTE_KIND (note)) - { - case REG_FRAME_RELATED_EXPR: - insn = XEXP (note, 0); - goto found; - - case REG_CFA_DEF_CFA: - dwarf2out_frame_debug_def_cfa (XEXP (note, 0), label); - handled_one = true; - break; - - case REG_CFA_ADJUST_CFA: - n = XEXP (note, 0); - if (n == NULL) - { - n = PATTERN (insn); - if (GET_CODE (n) == PARALLEL) - n = XVECEXP (n, 0, 0); - } - dwarf2out_frame_debug_adjust_cfa (n, label); - handled_one = true; - break; - - case REG_CFA_OFFSET: - n = XEXP (note, 0); - if (n == NULL) - n = single_set (insn); - dwarf2out_frame_debug_cfa_offset (n, label); - handled_one = true; - break; - - case REG_CFA_REGISTER: - n = XEXP (note, 0); - if (n == NULL) - { - n = PATTERN (insn); - if (GET_CODE (n) == PARALLEL) - n = XVECEXP (n, 0, 0); - } - dwarf2out_frame_debug_cfa_register (n, label); - handled_one = true; - break; - - case REG_CFA_EXPRESSION: - n = XEXP (note, 0); - if (n == NULL) - n = single_set (insn); - dwarf2out_frame_debug_cfa_expression (n, label); - handled_one = true; - break; - - case REG_CFA_RESTORE: - n = XEXP (note, 0); - if (n == NULL) - { - n = PATTERN (insn); - if (GET_CODE (n) == PARALLEL) - n = XVECEXP (n, 0, 0); - n = XEXP (n, 0); - } - dwarf2out_frame_debug_cfa_restore (n, label); - handled_one = true; - break; - - case REG_CFA_SET_VDRAP: - n = XEXP (note, 0); - if (REG_P (n)) - { - dw_fde_ref fde = current_fde (); - if (fde) - { - gcc_assert (fde->vdrap_reg == INVALID_REGNUM); - if (REG_P (n)) - fde->vdrap_reg = REGNO (n); - } - } - handled_one = true; - break; - - default: - break; - } - if (handled_one) - return; - - insn = PATTERN (insn); - found: - dwarf2out_frame_debug_expr (insn, label); - - /* Check again. A parallel can save and update the same register. - We could probably check just once, here, but this is safer than - removing the check above. */ - if (clobbers_queued_reg_save (insn)) - flush_queued_reg_saves (); -} - -/* Determine if we need to save and restore CFI information around this - epilogue. If SIBCALL is true, then this is a sibcall epilogue. If - we do need to save/restore, then emit the save now, and insert a - NOTE_INSN_CFA_RESTORE_STATE at the appropriate place in the stream. */ - -void -dwarf2out_cfi_begin_epilogue (rtx insn) -{ - bool saw_frp = false; - rtx i; - - /* Scan forward to the return insn, noticing if there are possible - frame related insns. */ - for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i)) - { - if (!INSN_P (i)) - continue; - - /* Look for both regular and sibcalls to end the block. */ - if (returnjump_p (i)) - break; - if (CALL_P (i) && SIBLING_CALL_P (i)) - break; - - if (GET_CODE (PATTERN (i)) == SEQUENCE) - { - int idx; - rtx seq = PATTERN (i); - - if (returnjump_p (XVECEXP (seq, 0, 0))) - break; - if (CALL_P (XVECEXP (seq, 0, 0)) - && SIBLING_CALL_P (XVECEXP (seq, 0, 0))) - break; - - for (idx = 0; idx < XVECLEN (seq, 0); idx++) - if (RTX_FRAME_RELATED_P (XVECEXP (seq, 0, idx))) - saw_frp = true; - } - - if (RTX_FRAME_RELATED_P (i)) - saw_frp = true; - } - - /* If the port doesn't emit epilogue unwind info, we don't need a - save/restore pair. */ - if (!saw_frp) - return; - - /* Otherwise, search forward to see if the return insn was the last - basic block of the function. If so, we don't need save/restore. */ - gcc_assert (i != NULL); - i = next_real_insn (i); - if (i == NULL) - return; - - /* Insert the restore before that next real insn in the stream, and before - a potential NOTE_INSN_EPILOGUE_BEG -- we do need these notes to be - properly nested. This should be after any label or alignment. This - will be pushed into the CFI stream by the function below. */ - while (1) - { - rtx p = PREV_INSN (i); - if (!NOTE_P (p)) - break; - if (NOTE_KIND (p) == NOTE_INSN_BASIC_BLOCK) - break; - i = p; - } - emit_note_before (NOTE_INSN_CFA_RESTORE_STATE, i); - - emit_cfa_remember = true; - - /* And emulate the state save. */ - gcc_assert (!cfa_remember.in_use); - cfa_remember = cfa; - cfa_remember.in_use = 1; -} - -/* A "subroutine" of dwarf2out_cfi_begin_epilogue. Emit the restore - required. */ - -void -dwarf2out_frame_debug_restore_state (void) -{ - dw_cfi_ref cfi = new_cfi (); - const char *label = dwarf2out_cfi_label (false); - - cfi->dw_cfi_opc = DW_CFA_restore_state; - add_fde_cfi (label, cfi); - - gcc_assert (cfa_remember.in_use); - cfa = cfa_remember; - cfa_remember.in_use = 0; -} - -/* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used. */ -static enum dw_cfi_oprnd_type dw_cfi_oprnd1_desc - (enum dwarf_call_frame_info cfi); - -static enum dw_cfi_oprnd_type -dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi) -{ - switch (cfi) - { - case DW_CFA_nop: - case DW_CFA_GNU_window_save: - case DW_CFA_remember_state: - case DW_CFA_restore_state: - return dw_cfi_oprnd_unused; - - case DW_CFA_set_loc: - case DW_CFA_advance_loc1: - case DW_CFA_advance_loc2: - case DW_CFA_advance_loc4: - case DW_CFA_MIPS_advance_loc8: - return dw_cfi_oprnd_addr; - - case DW_CFA_offset: - case DW_CFA_offset_extended: - case DW_CFA_def_cfa: - case DW_CFA_offset_extended_sf: - case DW_CFA_def_cfa_sf: - case DW_CFA_restore: - case DW_CFA_restore_extended: - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_def_cfa_register: - case DW_CFA_register: - case DW_CFA_expression: - return dw_cfi_oprnd_reg_num; - - case DW_CFA_def_cfa_offset: - case DW_CFA_GNU_args_size: - case DW_CFA_def_cfa_offset_sf: - return dw_cfi_oprnd_offset; - - case DW_CFA_def_cfa_expression: - return dw_cfi_oprnd_loc; - - default: - gcc_unreachable (); - } -} - -/* Describe for the GTY machinery what parts of dw_cfi_oprnd2 are used. */ -static enum dw_cfi_oprnd_type dw_cfi_oprnd2_desc - (enum dwarf_call_frame_info cfi); - -static enum dw_cfi_oprnd_type -dw_cfi_oprnd2_desc (enum dwarf_call_frame_info cfi) -{ - switch (cfi) - { - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - case DW_CFA_offset: - case DW_CFA_offset_extended_sf: - case DW_CFA_offset_extended: - return dw_cfi_oprnd_offset; - - case DW_CFA_register: - return dw_cfi_oprnd_reg_num; - - case DW_CFA_expression: - return dw_cfi_oprnd_loc; - - default: - return dw_cfi_oprnd_unused; - } -} - -/* Switch [BACK] to eh_frame_section. If we don't have an eh_frame_section, - switch to the data section instead, and write out a synthetic start label - for collect2 the first time around. */ - -static void -switch_to_eh_frame_section (bool back) -{ - tree label; - -#ifdef EH_FRAME_SECTION_NAME - if (eh_frame_section == 0) - { - int flags; - - if (EH_TABLES_CAN_BE_READ_ONLY) - { - int fde_encoding; - int per_encoding; - int lsda_encoding; - - fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, - /*global=*/0); - per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, - /*global=*/1); - lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, - /*global=*/0); - flags = ((! flag_pic - || ((fde_encoding & 0x70) != DW_EH_PE_absptr - && (fde_encoding & 0x70) != DW_EH_PE_aligned - && (per_encoding & 0x70) != DW_EH_PE_absptr - && (per_encoding & 0x70) != DW_EH_PE_aligned - && (lsda_encoding & 0x70) != DW_EH_PE_absptr - && (lsda_encoding & 0x70) != DW_EH_PE_aligned)) - ? 0 : SECTION_WRITE); - } - else - flags = SECTION_WRITE; - eh_frame_section = get_section (EH_FRAME_SECTION_NAME, flags, NULL); - } -#endif /* EH_FRAME_SECTION_NAME */ - - if (eh_frame_section) - switch_to_section (eh_frame_section); - else - { - /* We have no special eh_frame section. Put the information in - the data section and emit special labels to guide collect2. */ - switch_to_section (data_section); - - if (!back) - { - label = get_file_function_name ("F"); - ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE)); - targetm.asm_out.globalize_label (asm_out_file, - IDENTIFIER_POINTER (label)); - ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label)); - } - } -} - -/* Switch [BACK] to the eh or debug frame table section, depending on - FOR_EH. */ - -static void -switch_to_frame_table_section (int for_eh, bool back) -{ - if (for_eh) - switch_to_eh_frame_section (back); - else - { - if (!debug_frame_section) - debug_frame_section = get_section (DEBUG_FRAME_SECTION, - SECTION_DEBUG, NULL); - switch_to_section (debug_frame_section); - } -} - -/* Output a Call Frame Information opcode and its operand(s). */ - -static void -output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int for_eh) -{ - unsigned long r; - HOST_WIDE_INT off; - - if (cfi->dw_cfi_opc == DW_CFA_advance_loc) - dw2_asm_output_data (1, (cfi->dw_cfi_opc - | (cfi->dw_cfi_oprnd1.dw_cfi_offset & 0x3f)), - "DW_CFA_advance_loc " HOST_WIDE_INT_PRINT_HEX, - ((unsigned HOST_WIDE_INT) - cfi->dw_cfi_oprnd1.dw_cfi_offset)); - else if (cfi->dw_cfi_opc == DW_CFA_offset) - { - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), - "DW_CFA_offset, column %#lx", r); - off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); - dw2_asm_output_data_uleb128 (off, NULL); - } - else if (cfi->dw_cfi_opc == DW_CFA_restore) - { - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)), - "DW_CFA_restore, column %#lx", r); - } - else - { - dw2_asm_output_data (1, cfi->dw_cfi_opc, - "%s", dwarf_cfi_name (cfi->dw_cfi_opc)); - - switch (cfi->dw_cfi_opc) - { - case DW_CFA_set_loc: - if (for_eh) - dw2_asm_output_encoded_addr_rtx ( - ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0), - gen_rtx_SYMBOL_REF (Pmode, cfi->dw_cfi_oprnd1.dw_cfi_addr), - false, NULL); - else - dw2_asm_output_addr (DWARF2_ADDR_SIZE, - cfi->dw_cfi_oprnd1.dw_cfi_addr, NULL); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - - case DW_CFA_advance_loc1: - dw2_asm_output_delta (1, cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label, NULL); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - - case DW_CFA_advance_loc2: - dw2_asm_output_delta (2, cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label, NULL); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - - case DW_CFA_advance_loc4: - dw2_asm_output_delta (4, cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label, NULL); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - - case DW_CFA_MIPS_advance_loc8: - dw2_asm_output_delta (8, cfi->dw_cfi_oprnd1.dw_cfi_addr, - fde->dw_fde_current_label, NULL); - fde->dw_fde_current_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; - - case DW_CFA_offset_extended: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); - dw2_asm_output_data_uleb128 (off, NULL); - break; - - case DW_CFA_def_cfa: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd2.dw_cfi_offset, NULL); - break; - - case DW_CFA_offset_extended_sf: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); - dw2_asm_output_data_sleb128 (off, NULL); - break; - - case DW_CFA_def_cfa_sf: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - off = div_data_align (cfi->dw_cfi_oprnd2.dw_cfi_offset); - dw2_asm_output_data_sleb128 (off, NULL); - break; - - case DW_CFA_restore_extended: - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_def_cfa_register: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - break; - - case DW_CFA_register: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, for_eh); - dw2_asm_output_data_uleb128 (r, NULL); - break; - - case DW_CFA_def_cfa_offset: - case DW_CFA_GNU_args_size: - dw2_asm_output_data_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset, NULL); - break; - - case DW_CFA_def_cfa_offset_sf: - off = div_data_align (cfi->dw_cfi_oprnd1.dw_cfi_offset); - dw2_asm_output_data_sleb128 (off, NULL); - break; - - case DW_CFA_GNU_window_save: - break; - - case DW_CFA_def_cfa_expression: - case DW_CFA_expression: - output_cfa_loc (cfi); - break; - - case DW_CFA_GNU_negative_offset_extended: - /* Obsoleted by DW_CFA_offset_extended_sf. */ - gcc_unreachable (); - - default: - break; - } - } -} - -/* Similar, but do it via assembler directives instead. */ - -static void -output_cfi_directive (dw_cfi_ref cfi) -{ - unsigned long r, r2; - - switch (cfi->dw_cfi_opc) - { - case DW_CFA_advance_loc: - case DW_CFA_advance_loc1: - case DW_CFA_advance_loc2: - case DW_CFA_advance_loc4: - case DW_CFA_MIPS_advance_loc8: - case DW_CFA_set_loc: - /* Should only be created by add_fde_cfi in a code path not - followed when emitting via directives. The assembler is - going to take care of this for us. */ - gcc_unreachable (); - - case DW_CFA_offset: - case DW_CFA_offset_extended: - case DW_CFA_offset_extended_sf: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n", - r, cfi->dw_cfi_oprnd2.dw_cfi_offset); - break; - - case DW_CFA_restore: - case DW_CFA_restore_extended: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_restore %lu\n", r); - break; - - case DW_CFA_undefined: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_undefined %lu\n", r); - break; - - case DW_CFA_same_value: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_same_value %lu\n", r); - break; - - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n", - r, cfi->dw_cfi_oprnd2.dw_cfi_offset); - break; - - case DW_CFA_def_cfa_register: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_def_cfa_register %lu\n", r); - break; - - case DW_CFA_register: - r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1); - r2 = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, 1); - fprintf (asm_out_file, "\t.cfi_register %lu, %lu\n", r, r2); - break; - - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - fprintf (asm_out_file, "\t.cfi_def_cfa_offset " - HOST_WIDE_INT_PRINT_DEC"\n", - cfi->dw_cfi_oprnd1.dw_cfi_offset); - break; - - case DW_CFA_remember_state: - fprintf (asm_out_file, "\t.cfi_remember_state\n"); - break; - case DW_CFA_restore_state: - fprintf (asm_out_file, "\t.cfi_restore_state\n"); - break; - - case DW_CFA_GNU_args_size: - fprintf (asm_out_file, "\t.cfi_escape %#x,", DW_CFA_GNU_args_size); - dw2_asm_output_data_uleb128_raw (cfi->dw_cfi_oprnd1.dw_cfi_offset); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC, - ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset); - fputc ('\n', asm_out_file); - break; - - case DW_CFA_GNU_window_save: - fprintf (asm_out_file, "\t.cfi_window_save\n"); - break; - - case DW_CFA_def_cfa_expression: - case DW_CFA_expression: - fprintf (asm_out_file, "\t.cfi_escape %#x,", cfi->dw_cfi_opc); - output_cfa_loc_raw (cfi); - fputc ('\n', asm_out_file); - break; - - default: - gcc_unreachable (); - } -} - -DEF_VEC_P (dw_cfi_ref); -DEF_VEC_ALLOC_P (dw_cfi_ref, heap); - -/* Output CFIs to bring current FDE to the same state as after executing - CFIs in CFI chain. DO_CFI_ASM is true if .cfi_* directives shall - be emitted, false otherwise. If it is false, FDE and FOR_EH are the - other arguments to pass to output_cfi. */ - -static void -output_cfis (dw_cfi_ref cfi, bool do_cfi_asm, dw_fde_ref fde, bool for_eh) -{ - struct dw_cfi_struct cfi_buf; - dw_cfi_ref cfi2; - dw_cfi_ref cfi_args_size = NULL, cfi_cfa = NULL, cfi_cfa_offset = NULL; - VEC (dw_cfi_ref, heap) *regs = VEC_alloc (dw_cfi_ref, heap, 32); - unsigned int len, idx; - - for (;; cfi = cfi->dw_cfi_next) - switch (cfi ? cfi->dw_cfi_opc : DW_CFA_nop) - { - case DW_CFA_advance_loc: - case DW_CFA_advance_loc1: - case DW_CFA_advance_loc2: - case DW_CFA_advance_loc4: - case DW_CFA_MIPS_advance_loc8: - case DW_CFA_set_loc: - /* All advances should be ignored. */ - break; - case DW_CFA_remember_state: - { - dw_cfi_ref args_size = cfi_args_size; - - /* Skip everything between .cfi_remember_state and - .cfi_restore_state. */ - for (cfi2 = cfi->dw_cfi_next; cfi2; cfi2 = cfi2->dw_cfi_next) - if (cfi2->dw_cfi_opc == DW_CFA_restore_state) - break; - else if (cfi2->dw_cfi_opc == DW_CFA_GNU_args_size) - args_size = cfi2; - else - gcc_assert (cfi2->dw_cfi_opc != DW_CFA_remember_state); - - if (cfi2 == NULL) - goto flush_all; - else - { - cfi = cfi2; - cfi_args_size = args_size; - } - break; - } - case DW_CFA_GNU_args_size: - cfi_args_size = cfi; - break; - case DW_CFA_GNU_window_save: - goto flush_all; - case DW_CFA_offset: - case DW_CFA_offset_extended: - case DW_CFA_offset_extended_sf: - case DW_CFA_restore: - case DW_CFA_restore_extended: - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_register: - case DW_CFA_val_offset: - case DW_CFA_val_offset_sf: - case DW_CFA_expression: - case DW_CFA_val_expression: - case DW_CFA_GNU_negative_offset_extended: - if (VEC_length (dw_cfi_ref, regs) <= cfi->dw_cfi_oprnd1.dw_cfi_reg_num) - VEC_safe_grow_cleared (dw_cfi_ref, heap, regs, - cfi->dw_cfi_oprnd1.dw_cfi_reg_num + 1); - VEC_replace (dw_cfi_ref, regs, cfi->dw_cfi_oprnd1.dw_cfi_reg_num, cfi); - break; - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - case DW_CFA_def_cfa_expression: - cfi_cfa = cfi; - cfi_cfa_offset = cfi; - break; - case DW_CFA_def_cfa_register: - cfi_cfa = cfi; - break; - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - cfi_cfa_offset = cfi; - break; - case DW_CFA_nop: - gcc_assert (cfi == NULL); - flush_all: - len = VEC_length (dw_cfi_ref, regs); - for (idx = 0; idx < len; idx++) - { - cfi2 = VEC_replace (dw_cfi_ref, regs, idx, NULL); - if (cfi2 != NULL - && cfi2->dw_cfi_opc != DW_CFA_restore - && cfi2->dw_cfi_opc != DW_CFA_restore_extended) - { - if (do_cfi_asm) - output_cfi_directive (cfi2); - else - output_cfi (cfi2, fde, for_eh); - } - } - if (cfi_cfa && cfi_cfa_offset && cfi_cfa_offset != cfi_cfa) - { - gcc_assert (cfi_cfa->dw_cfi_opc != DW_CFA_def_cfa_expression); - cfi_buf = *cfi_cfa; - switch (cfi_cfa_offset->dw_cfi_opc) - { - case DW_CFA_def_cfa_offset: - cfi_buf.dw_cfi_opc = DW_CFA_def_cfa; - cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1; - break; - case DW_CFA_def_cfa_offset_sf: - cfi_buf.dw_cfi_opc = DW_CFA_def_cfa_sf; - cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1; - break; - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - cfi_buf.dw_cfi_opc = cfi_cfa_offset->dw_cfi_opc; - cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd2; - break; - default: - gcc_unreachable (); - } - cfi_cfa = &cfi_buf; - } - else if (cfi_cfa_offset) - cfi_cfa = cfi_cfa_offset; - if (cfi_cfa) - { - if (do_cfi_asm) - output_cfi_directive (cfi_cfa); - else - output_cfi (cfi_cfa, fde, for_eh); - } - cfi_cfa = NULL; - cfi_cfa_offset = NULL; - if (cfi_args_size - && cfi_args_size->dw_cfi_oprnd1.dw_cfi_offset) - { - if (do_cfi_asm) - output_cfi_directive (cfi_args_size); - else - output_cfi (cfi_args_size, fde, for_eh); - } - cfi_args_size = NULL; - if (cfi == NULL) - { - VEC_free (dw_cfi_ref, heap, regs); - return; - } - else if (do_cfi_asm) - output_cfi_directive (cfi); - else - output_cfi (cfi, fde, for_eh); - break; - default: - gcc_unreachable (); - } -} - -/* Output one FDE. */ - -static void -output_fde (dw_fde_ref fde, bool for_eh, bool second, - char *section_start_label, int fde_encoding, char *augmentation, - bool any_lsda_needed, int lsda_encoding) -{ - const char *begin, *end; - static unsigned int j; - char l1[20], l2[20]; - dw_cfi_ref cfi; - - targetm.asm_out.emit_unwind_label (asm_out_file, fde->decl, for_eh, - /* empty */ 0); - targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, - for_eh + j); - ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + j); - ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + j); - if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) - dw2_asm_output_data (4, 0xffffffff, "Initial length escape value" - " indicating 64-bit DWARF extension"); - dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, - "FDE Length"); - ASM_OUTPUT_LABEL (asm_out_file, l1); - - if (for_eh) - dw2_asm_output_delta (4, l1, section_start_label, "FDE CIE offset"); - else - dw2_asm_output_offset (DWARF_OFFSET_SIZE, section_start_label, - debug_frame_section, "FDE CIE offset"); - - if (!fde->dw_fde_switched_sections) - { - begin = fde->dw_fde_begin; - end = fde->dw_fde_end; - } - else - { - /* For the first section, prefer dw_fde_begin over - dw_fde_{hot,cold}_section_label, as the latter - might be separated from the real start of the - function by alignment padding. */ - if (!second) - begin = fde->dw_fde_begin; - else if (fde->dw_fde_switched_cold_to_hot) - begin = fde->dw_fde_hot_section_label; - else - begin = fde->dw_fde_unlikely_section_label; - if (second ^ fde->dw_fde_switched_cold_to_hot) - end = fde->dw_fde_unlikely_section_end_label; - else - end = fde->dw_fde_hot_section_end_label; - } - - 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; - if (!fde->dw_fde_switched_sections) - for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next) - output_cfi (cfi, fde, for_eh); - else if (!second) - { - if (fde->dw_fde_switch_cfi) - for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next) - { - output_cfi (cfi, fde, for_eh); - if (cfi == fde->dw_fde_switch_cfi) - break; - } - } - else - { - dw_cfi_ref cfi_next = fde->dw_fde_cfi; - - if (fde->dw_fde_switch_cfi) - { - cfi_next = fde->dw_fde_switch_cfi->dw_cfi_next; - fde->dw_fde_switch_cfi->dw_cfi_next = NULL; - output_cfis (fde->dw_fde_cfi, false, fde, for_eh); - fde->dw_fde_switch_cfi->dw_cfi_next = cfi_next; - } - for (cfi = cfi_next; cfi != NULL; cfi = cfi->dw_cfi_next) - output_cfi (cfi, fde, for_eh); - } - - /* If we are to emit a ref/link from function bodies to their frame tables, - do it now. This is typically performed to make sure that tables - associated with functions are dragged with them and not discarded in - garbage collecting links. We need to do this on a per function basis to - cope with -ffunction-sections. */ - -#ifdef ASM_OUTPUT_DWARF_TABLE_REF - /* Switch to the function section, emit the ref to the tables, and - switch *back* into the table section. */ - switch_to_section (function_section (fde->decl)); - ASM_OUTPUT_DWARF_TABLE_REF (section_start_label); - switch_to_frame_table_section (for_eh, true); -#endif - - /* Pad the FDE out to an address sized boundary. */ - ASM_OUTPUT_ALIGN (asm_out_file, - floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE))); - ASM_OUTPUT_LABEL (asm_out_file, l2); - - j += 2; -} - -/* Return true if frame description entry FDE is needed for EH. */ - -static bool -fde_needed_for_eh_p (dw_fde_ref fde) -{ - if (flag_asynchronous_unwind_tables) - return true; - - if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde->decl)) - return true; - - if (fde->uses_eh_lsda) - return true; - - /* If exceptions are enabled, we have collected nothrow info. */ - if (flag_exceptions && (fde->all_throwers_are_sibcalls || fde->nothrow)) - return false; - - return true; -} - -/* Output the call frame information used to record information - that relates to calculating the frame pointer, and records the - location of saved registers. */ - -static void -output_call_frame_info (int for_eh) -{ - unsigned int i; - dw_fde_ref fde; - dw_cfi_ref cfi; - char l1[20], l2[20], section_start_label[20]; - bool any_lsda_needed = false; - char augmentation[6]; - int augmentation_size; - int fde_encoding = DW_EH_PE_absptr; - int per_encoding = DW_EH_PE_absptr; - int lsda_encoding = DW_EH_PE_absptr; - int return_reg; - rtx personality = NULL; - int dw_cie_version; - - /* Don't emit a CIE if there won't be any FDEs. */ - if (fde_table_in_use == 0) - 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 (i = 0; i < fde_table_in_use; i++) - if (fde_table[i].uses_eh_lsda) - any_eh_needed = any_lsda_needed = true; - else if (fde_needed_for_eh_p (&fde_table[i])) - any_eh_needed = true; - else if (TARGET_USES_WEAK_UNWIND_INFO) - targetm.asm_out.emit_unwind_label (asm_out_file, fde_table[i].decl, - 1, 1); - - if (!any_eh_needed) - return; - } - - /* We're going to be generating comments, so turn on app. */ - if (flag_debug_asm) - app_enable (); - - /* Switch to the proper frame section, first time. */ - switch_to_frame_table_section (for_eh, false); - - ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh); - ASM_OUTPUT_LABEL (asm_out_file, section_start_label); - - /* Output the CIE. */ - ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); - ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); - if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh) - dw2_asm_output_data (4, 0xffffffff, - "Initial length escape value indicating 64-bit DWARF extension"); - dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, - "Length of Common Information Entry"); - ASM_OUTPUT_LABEL (asm_out_file, l1); - - /* Now that the CIE pointer is PC-relative for EH, - use 0 to identify the CIE. */ - dw2_asm_output_data ((for_eh ? 4 : DWARF_OFFSET_SIZE), - (for_eh ? 0 : DWARF_CIE_ID), - "CIE Identifier Tag"); - - /* Use the CIE version 3 for DWARF3; allow DWARF2 to continue to - use CIE version 1, unless that would produce incorrect results - due to overflowing the return register column. */ - return_reg = DWARF2_FRAME_REG_OUT (DWARF_FRAME_RETURN_COLUMN, for_eh); - dw_cie_version = 1; - if (return_reg >= 256 || dwarf_version > 2) - dw_cie_version = 3; - dw2_asm_output_data (1, dw_cie_version, "CIE Version"); - - augmentation[0] = 0; - augmentation_size = 0; - - personality = current_unit_personality; - if (for_eh) - { - char *p; - - /* Augmentation: - z Indicates that a uleb128 is present to size the - augmentation section. - L Indicates the encoding (and thus presence) of - an LSDA pointer in the FDE augmentation. - R Indicates a non-default pointer encoding for - FDE code pointers. - P Indicates the presence of an encoding + language - personality routine in the CIE augmentation. */ - - fde_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/1, /*global=*/0); - per_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); - lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); - - p = augmentation + 1; - if (personality) - { - *p++ = 'P'; - augmentation_size += 1 + size_of_encoded_value (per_encoding); - assemble_external_libcall (personality); - } - if (any_lsda_needed) - { - *p++ = 'L'; - augmentation_size += 1; - } - if (fde_encoding != DW_EH_PE_absptr) - { - *p++ = 'R'; - augmentation_size += 1; - } - if (p > augmentation + 1) - { - augmentation[0] = 'z'; - *p = '\0'; - } - - /* Ug. Some platforms can't do unaligned dynamic relocations at all. */ - if (personality && per_encoding == DW_EH_PE_aligned) - { - int offset = ( 4 /* Length */ - + 4 /* CIE Id */ - + 1 /* CIE version */ - + strlen (augmentation) + 1 /* Augmentation */ - + size_of_uleb128 (1) /* Code alignment */ - + size_of_sleb128 (DWARF_CIE_DATA_ALIGNMENT) - + 1 /* RA column */ - + 1 /* Augmentation size */ - + 1 /* Personality encoding */ ); - int pad = -offset & (PTR_SIZE - 1); - - augmentation_size += pad; - - /* Augmentations should be small, so there's scarce need to - iterate for a solution. Die if we exceed one uleb128 byte. */ - gcc_assert (size_of_uleb128 (augmentation_size) == 1); - } - } - - dw2_asm_output_nstring (augmentation, -1, "CIE Augmentation"); - if (dw_cie_version >= 4) - { - dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "CIE Address Size"); - dw2_asm_output_data (1, 0, "CIE Segment Size"); - } - dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor"); - dw2_asm_output_data_sleb128 (DWARF_CIE_DATA_ALIGNMENT, - "CIE Data Alignment Factor"); - - if (dw_cie_version == 1) - dw2_asm_output_data (1, return_reg, "CIE RA Column"); - else - dw2_asm_output_data_uleb128 (return_reg, "CIE RA Column"); - - if (augmentation[0]) - { - dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size"); - if (personality) - { - dw2_asm_output_data (1, per_encoding, "Personality (%s)", - eh_data_format_name (per_encoding)); - dw2_asm_output_encoded_addr_rtx (per_encoding, - personality, - true, NULL); - } - - if (any_lsda_needed) - dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)", - eh_data_format_name (lsda_encoding)); - - if (fde_encoding != DW_EH_PE_absptr) - dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)", - eh_data_format_name (fde_encoding)); - } - - for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) - output_cfi (cfi, NULL, for_eh); - - /* Pad the CIE out to an address sized boundary. */ - ASM_OUTPUT_ALIGN (asm_out_file, - floor_log2 (for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)); - ASM_OUTPUT_LABEL (asm_out_file, l2); - - /* Loop through all of the FDE's. */ - for (i = 0; i < fde_table_in_use; i++) - { - unsigned int k; - fde = &fde_table[i]; - - /* 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_switched_sections ? 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"); -#ifdef MIPS_DEBUGGING_INFO - /* Work around Irix 6 assembler bug whereby labels at the end of a section - get a value of 0. Putting .align 0 after the label fixes it. */ - ASM_OUTPUT_ALIGN (asm_out_file, 0); -#endif - - /* Turn off app to make assembly quicker. */ - if (flag_debug_asm) - app_disable (); -} - -/* Emit .cfi_startproc and .cfi_personality/.cfi_lsda if needed. */ - -static void -dwarf2out_do_cfi_startproc (bool second) -{ - int enc; - rtx ref; - rtx personality = get_personality_function (current_function_decl); - - fprintf (asm_out_file, "\t.cfi_startproc\n"); - - if (personality) - { - enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1); - ref = personality; - - /* ??? The GAS support isn't entirely consistent. We have to - handle indirect support ourselves, but PC-relative is done - in the assembler. Further, the assembler can't handle any - of the weirder relocation types. */ - if (enc & DW_EH_PE_indirect) - ref = dw2_force_const_mem (ref, true); - - fprintf (asm_out_file, "\t.cfi_personality %#x,", enc); - output_addr_const (asm_out_file, ref); - fputc ('\n', asm_out_file); - } - - if (crtl->uses_eh_lsda) - { - char lab[20]; - - enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0); - ASM_GENERATE_INTERNAL_LABEL (lab, second ? "LLSDAC" : "LLSDA", - current_function_funcdef_no); - ref = gen_rtx_SYMBOL_REF (Pmode, lab); - SYMBOL_REF_FLAGS (ref) = SYMBOL_FLAG_LOCAL; - - if (enc & DW_EH_PE_indirect) - ref = dw2_force_const_mem (ref, true); - - fprintf (asm_out_file, "\t.cfi_lsda %#x,", enc); - output_addr_const (asm_out_file, ref); - fputc ('\n', asm_out_file); - } -} - -/* Output a marker (i.e. a label) for the beginning of a function, before - the prologue. */ - -void -dwarf2out_begin_prologue (unsigned int line ATTRIBUTE_UNUSED, - const char *file ATTRIBUTE_UNUSED) -{ - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - char * dup_label; - dw_fde_ref fde; - section *fnsec; - - current_function_func_begin_label = NULL; - -#ifdef TARGET_UNWIND_INFO - /* ??? current_function_func_begin_label is also used by except.c - for call-site information. We must emit this label if it might - be used. */ - if ((! flag_exceptions || USING_SJLJ_EXCEPTIONS) - && ! dwarf2out_do_frame ()) - return; -#else - if (! dwarf2out_do_frame ()) - return; -#endif - - 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; - -#ifdef TARGET_UNWIND_INFO - /* We can elide the fde allocation if we're not emitting debug info. */ - if (! dwarf2out_do_frame ()) - return; -#endif - - /* Expand the fde table if necessary. */ - if (fde_table_in_use == fde_table_allocated) - { - fde_table_allocated += FDE_TABLE_INCREMENT; - fde_table = GGC_RESIZEVEC (dw_fde_node, fde_table, fde_table_allocated); - memset (fde_table + fde_table_in_use, 0, - FDE_TABLE_INCREMENT * sizeof (dw_fde_node)); - } - - /* Record the FDE associated with this function. */ - current_funcdef_fde = fde_table_in_use; - - /* Add the new FDE at the end of the fde_table. */ - fde = &fde_table[fde_table_in_use++]; - fde->decl = current_function_decl; - fde->dw_fde_begin = dup_label; - fde->dw_fde_current_label = dup_label; - fde->dw_fde_hot_section_label = NULL; - fde->dw_fde_hot_section_end_label = NULL; - fde->dw_fde_unlikely_section_label = NULL; - fde->dw_fde_unlikely_section_end_label = NULL; - fde->dw_fde_switched_sections = 0; - fde->dw_fde_switched_cold_to_hot = 0; - fde->dw_fde_end = NULL; - fde->dw_fde_vms_end_prologue = NULL; - fde->dw_fde_vms_begin_epilogue = NULL; - fde->dw_fde_cfi = NULL; - fde->dw_fde_switch_cfi = NULL; - fde->funcdef_number = current_function_funcdef_no; - 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; - if (flag_reorder_blocks_and_partition) - { - section *unlikelysec; - if (first_function_block_is_cold) - fde->in_std_section = 1; - else - fde->in_std_section - = (fnsec == text_section - || (cold_text_section && fnsec == cold_text_section)); - unlikelysec = unlikely_text_section (); - fde->cold_in_std_section - = (unlikelysec == text_section - || (cold_text_section && unlikelysec == cold_text_section)); - } - else - { - fde->in_std_section - = (fnsec == text_section - || (cold_text_section && fnsec == cold_text_section)); - fde->cold_in_std_section = 0; - } - - args_size = old_args_size = 0; - - /* We only want to output line number information for the genuine dwarf2 - prologue case, not the eh frame case. */ -#ifdef DWARF2_DEBUGGING_INFO - if (file) - dwarf2out_source_line (line, file, 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) -{ - dw_fde_ref fde; - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - /* Output a label to mark the endpoint of the code generated for this - function. */ - ASM_GENERATE_INTERNAL_LABEL (label, PROLOGUE_END_LABEL, - current_function_funcdef_no); - ASM_OUTPUT_DEBUG_LABEL (asm_out_file, PROLOGUE_END_LABEL, - current_function_funcdef_no); - fde = &fde_table[fde_table_in_use - 1]; - 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; - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - fde = &fde_table[fde_table_in_use - 1]; - if (fde->dw_fde_vms_begin_epilogue) - return; - - /* Output a label to mark the endpoint of the code generated for this - function. */ - ASM_GENERATE_INTERNAL_LABEL (label, EPILOGUE_BEGIN_LABEL, - current_function_funcdef_no); - ASM_OUTPUT_DEBUG_LABEL (asm_out_file, EPILOGUE_BEGIN_LABEL, - current_function_funcdef_no); - fde->dw_fde_vms_begin_epilogue = xstrdup (label); -} - -/* Output a marker (i.e. a label) for the absolute end of the generated code - for a function definition. This gets called *after* the epilogue code has - been generated. */ - -void -dwarf2out_end_epilogue (unsigned int line ATTRIBUTE_UNUSED, - const char *file ATTRIBUTE_UNUSED) -{ - dw_fde_ref fde; - char label[MAX_ARTIFICIAL_LABEL_BYTES]; - - last_var_location_insn = NULL_RTX; - - 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 = current_fde (); - gcc_assert (fde != NULL); - fde->dw_fde_end = xstrdup (label); -} - -void -dwarf2out_frame_init (void) -{ - /* Allocate the initial hunk of the fde_table. */ - fde_table = ggc_alloc_cleared_vec_dw_fde_node (FDE_TABLE_INCREMENT); - fde_table_allocated = FDE_TABLE_INCREMENT; - fde_table_in_use = 0; - - /* Generate the CFA instructions common to all FDE's. Do it now for the - sake of lookup_cfa. */ - - /* On entry, the Canonical Frame Address is at SP. */ - dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET); - -#ifdef DWARF2_UNWIND_INFO - if (DWARF2_UNWIND_INFO || DWARF2_FRAME_INFO) - initial_return_save (INCOMING_RETURN_ADDR_RTX); -#endif -} - -void -dwarf2out_frame_finish (void) -{ - /* Output call frame information. */ - if (DWARF2_FRAME_INFO) - output_call_frame_info (0); - -#ifndef TARGET_UNWIND_INFO - /* Output another copy for the unwinder. */ - if (! USING_SJLJ_EXCEPTIONS && (flag_unwind_tables || flag_exceptions)) - output_call_frame_info (1); -#endif -} - -/* Note that the current function section is being used for code. */ - -static void -dwarf2out_note_section_used (void) -{ - section *sec = current_function_section (); - if (sec == text_section) - text_section_used = true; - else if (sec == cold_text_section) - cold_text_section_used = true; -} - -void -dwarf2out_switch_text_section (void) -{ - dw_fde_ref fde = current_fde (); - - gcc_assert (cfun && fde && !fde->dw_fde_switched_sections); - - fde->dw_fde_switched_sections = 1; - fde->dw_fde_switched_cold_to_hot = !in_cold_section_p; - - fde->dw_fde_hot_section_label = crtl->subsections.hot_section_label; - fde->dw_fde_hot_section_end_label = crtl->subsections.hot_section_end_label; - fde->dw_fde_unlikely_section_label = crtl->subsections.cold_section_label; - fde->dw_fde_unlikely_section_end_label = crtl->subsections.cold_section_end_label; - have_multiple_function_sections = true; - - /* Reset the current label on switching text sections, so that we - don't attempt to advance_loc4 between labels in different sections. */ - fde->dw_fde_current_label = NULL; - - /* There is no need to mark used sections when not debugging. */ - if (cold_text_section != NULL) - dwarf2out_note_section_used (); - - if (dwarf2out_do_cfi_asm ()) - fprintf (asm_out_file, "\t.cfi_endproc\n"); - - /* Now do the real section switch. */ - switch_to_section (current_function_section ()); - - if (dwarf2out_do_cfi_asm ()) - { - dwarf2out_do_cfi_startproc (true); - /* As this is a different FDE, insert all current CFI instructions - again. */ - output_cfis (fde->dw_fde_cfi, true, fde, true); - } - else - { - dw_cfi_ref cfi = fde->dw_fde_cfi; - - cfi = fde->dw_fde_cfi; - if (cfi) - while (cfi->dw_cfi_next != NULL) - cfi = cfi->dw_cfi_next; - fde->dw_fde_switch_cfi = cfi; - } -} - -/* And now, the subset of the debugging information support code necessary - for emitting location expressions. */ - -/* Data about a single source file. */ -struct GTY(()) dwarf_file_data { - const char * filename; - int emitted_number; -}; - -typedef struct dw_val_struct *dw_val_ref; -typedef struct die_struct *dw_die_ref; -typedef const struct die_struct *const_dw_die_ref; -typedef struct dw_loc_descr_struct *dw_loc_descr_ref; -typedef struct dw_loc_list_struct *dw_loc_list_ref; - -typedef struct GTY(()) deferred_locations_struct -{ - tree variable; - dw_die_ref die; -} deferred_locations; - -DEF_VEC_O(deferred_locations); -DEF_VEC_ALLOC_O(deferred_locations,gc); - -static GTY(()) VEC(deferred_locations, gc) *deferred_locations_list; - -DEF_VEC_P(dw_die_ref); -DEF_VEC_ALLOC_P(dw_die_ref,heap); - -/* Each DIE may have a series of attribute/value pairs. Values - can take on several forms. The forms that are used in this - implementation are listed below. */ - -enum dw_val_class -{ - dw_val_class_addr, - dw_val_class_offset, - dw_val_class_loc, - dw_val_class_loc_list, - dw_val_class_range_list, - dw_val_class_const, - dw_val_class_unsigned_const, - dw_val_class_const_double, - dw_val_class_vec, - dw_val_class_flag, - dw_val_class_die_ref, - dw_val_class_fde_ref, - dw_val_class_lbl_id, - dw_val_class_lineptr, - dw_val_class_str, - dw_val_class_macptr, - dw_val_class_file, - dw_val_class_data8, - dw_val_class_vms_delta -}; - -/* Describe a floating point constant value, or a vector constant value. */ - -typedef struct GTY(()) dw_vec_struct { - unsigned char * GTY((length ("%h.length"))) array; - unsigned length; - unsigned elt_size; -} -dw_vec_const; - -/* The dw_val_node describes an attribute's value, as it is - represented internally. */ - -typedef struct GTY(()) dw_val_struct { - enum dw_val_class val_class; - union dw_val_struct_union - { - rtx GTY ((tag ("dw_val_class_addr"))) val_addr; - unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_offset"))) val_offset; - dw_loc_list_ref GTY ((tag ("dw_val_class_loc_list"))) val_loc_list; - dw_loc_descr_ref GTY ((tag ("dw_val_class_loc"))) val_loc; - HOST_WIDE_INT GTY ((default)) val_int; - unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_unsigned_const"))) val_unsigned; - double_int GTY ((tag ("dw_val_class_const_double"))) val_double; - dw_vec_const GTY ((tag ("dw_val_class_vec"))) val_vec; - struct dw_val_die_union - { - dw_die_ref die; - int external; - } GTY ((tag ("dw_val_class_die_ref"))) val_die_ref; - unsigned GTY ((tag ("dw_val_class_fde_ref"))) val_fde_index; - struct indirect_string_node * GTY ((tag ("dw_val_class_str"))) val_str; - char * GTY ((tag ("dw_val_class_lbl_id"))) val_lbl_id; - unsigned char GTY ((tag ("dw_val_class_flag"))) val_flag; - struct dwarf_file_data * GTY ((tag ("dw_val_class_file"))) val_file; - unsigned char GTY ((tag ("dw_val_class_data8"))) val_data8[8]; - struct dw_val_vms_delta_union - { - char * lbl1; - char * lbl2; - } GTY ((tag ("dw_val_class_vms_delta"))) val_vms_delta; - } - GTY ((desc ("%1.val_class"))) v; -} -dw_val_node; - -/* Locations in memory are described using a sequence of stack machine - operations. */ - -typedef struct GTY(()) dw_loc_descr_struct { - dw_loc_descr_ref dw_loc_next; - ENUM_BITFIELD (dwarf_location_atom) dw_loc_opc : 8; - /* Used to distinguish DW_OP_addr with a direct symbol relocation - from DW_OP_addr with a dtp-relative symbol relocation. */ - unsigned int dtprel : 1; - int dw_loc_addr; - dw_val_node dw_loc_oprnd1; - dw_val_node dw_loc_oprnd2; -} -dw_loc_descr_node; - -/* Location lists are ranges + location descriptions for that range, - so you can track variables that are in different places over - their entire life. */ -typedef struct GTY(()) dw_loc_list_struct { - dw_loc_list_ref dw_loc_next; - const char *begin; /* Label for begin address of range */ - const char *end; /* Label for end address of range */ - char *ll_symbol; /* Label for beginning of location list. - Only on head of list */ - const char *section; /* Section this loclist is relative to */ - dw_loc_descr_ref expr; -} dw_loc_list_node; - -static dw_loc_descr_ref int_loc_descriptor (HOST_WIDE_INT); - -/* Convert a DWARF stack opcode into its string name. */ - -static const char * -dwarf_stack_op_name (unsigned int op) +static const char * +dwarf_stack_op_name (unsigned int op) { switch (op) { @@ -4688,6 +1551,22 @@ dwarf_stack_op_name (unsigned int op) return "DW_OP_GNU_uninit"; case DW_OP_GNU_encoded_addr: return "DW_OP_GNU_encoded_addr"; + case DW_OP_GNU_implicit_pointer: + return "DW_OP_GNU_implicit_pointer"; + case DW_OP_GNU_entry_value: + return "DW_OP_GNU_entry_value"; + case DW_OP_GNU_const_type: + return "DW_OP_GNU_const_type"; + case DW_OP_GNU_regval_type: + return "DW_OP_GNU_regval_type"; + case DW_OP_GNU_deref_type: + return "DW_OP_GNU_deref_type"; + case DW_OP_GNU_convert: + return "DW_OP_GNU_convert"; + case DW_OP_GNU_reinterpret: + return "DW_OP_GNU_reinterpret"; + case DW_OP_GNU_parameter_ref: + return "DW_OP_GNU_parameter_ref"; default: return "OP_"; @@ -4740,6 +1619,109 @@ add_loc_descr (dw_loc_descr_ref *list_head, dw_loc_descr_ref descr) *d = descr; } +/* Compare two location operands for exact equality. */ + +static bool +dw_val_equal_p (dw_val_node *a, dw_val_node *b) +{ + if (a->val_class != b->val_class) + return false; + switch (a->val_class) + { + case dw_val_class_none: + return true; + case dw_val_class_addr: + return rtx_equal_p (a->v.val_addr, b->v.val_addr); + + case dw_val_class_offset: + case dw_val_class_unsigned_const: + case dw_val_class_const: + case dw_val_class_range_list: + case dw_val_class_lineptr: + case dw_val_class_macptr: + /* These are all HOST_WIDE_INT, signed or unsigned. */ + return a->v.val_unsigned == b->v.val_unsigned; + + case dw_val_class_loc: + return a->v.val_loc == b->v.val_loc; + case dw_val_class_loc_list: + return a->v.val_loc_list == b->v.val_loc_list; + case dw_val_class_die_ref: + return a->v.val_die_ref.die == b->v.val_die_ref.die; + case dw_val_class_fde_ref: + return a->v.val_fde_index == b->v.val_fde_index; + case dw_val_class_lbl_id: + return strcmp (a->v.val_lbl_id, b->v.val_lbl_id) == 0; + case dw_val_class_str: + return a->v.val_str == b->v.val_str; + case dw_val_class_flag: + return a->v.val_flag == b->v.val_flag; + case dw_val_class_file: + return a->v.val_file == b->v.val_file; + case dw_val_class_decl_ref: + return a->v.val_decl_ref == b->v.val_decl_ref; + + case dw_val_class_const_double: + return (a->v.val_double.high == b->v.val_double.high + && a->v.val_double.low == b->v.val_double.low); + + case dw_val_class_vec: + { + size_t a_len = a->v.val_vec.elt_size * a->v.val_vec.length; + size_t b_len = b->v.val_vec.elt_size * b->v.val_vec.length; + + return (a_len == b_len + && !memcmp (a->v.val_vec.array, b->v.val_vec.array, a_len)); + } + + case dw_val_class_data8: + return memcmp (a->v.val_data8, b->v.val_data8, 8) == 0; + + case dw_val_class_vms_delta: + return (!strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1) + && !strcmp (a->v.val_vms_delta.lbl1, b->v.val_vms_delta.lbl1)); + } + gcc_unreachable (); +} + +/* Compare two location atoms for exact equality. */ + +static bool +loc_descr_equal_p_1 (dw_loc_descr_ref a, dw_loc_descr_ref b) +{ + if (a->dw_loc_opc != b->dw_loc_opc) + return false; + + /* ??? This is only ever set for DW_OP_constNu, for N equal to the + address size, but since we always allocate cleared storage it + should be zero for other types of locations. */ + if (a->dtprel != b->dtprel) + return false; + + return (dw_val_equal_p (&a->dw_loc_oprnd1, &b->dw_loc_oprnd1) + && dw_val_equal_p (&a->dw_loc_oprnd2, &b->dw_loc_oprnd2)); +} + +/* Compare two complete location expressions for exact equality. */ + +bool +loc_descr_equal_p (dw_loc_descr_ref a, dw_loc_descr_ref b) +{ + while (1) + { + if (a == b) + return true; + if (a == NULL || b == NULL) + return false; + if (!loc_descr_equal_p_1 (a, b)) + return false; + + a = a->dw_loc_next; + b = b->dw_loc_next; + } +} + + /* Add a constant OFFSET to a location expression. */ static void @@ -4791,6 +1773,11 @@ loc_list_plus_const (dw_loc_list_ref list_head, HOST_WIDE_INT offset) loc_descr_plus_const (&d->expr, offset); } +#define DWARF_REF_SIZE \ + (dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE) + +static unsigned long int get_base_type_offset (dw_die_ref); + /* Return the size of a location descriptor. */ static unsigned long @@ -4897,12 +1884,72 @@ size_of_loc_descr (dw_loc_descr_ref loc) size += 4; break; case DW_OP_call_ref: - size += DWARF2_ADDR_SIZE; + size += DWARF_REF_SIZE; break; case DW_OP_implicit_value: size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned) + loc->dw_loc_oprnd1.v.val_unsigned; break; + case DW_OP_GNU_implicit_pointer: + size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int); + break; + case DW_OP_GNU_entry_value: + { + unsigned long op_size = size_of_locs (loc->dw_loc_oprnd1.v.val_loc); + size += size_of_uleb128 (op_size) + op_size; + break; + } + case DW_OP_GNU_const_type: + { + unsigned long o + = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die); + size += size_of_uleb128 (o) + 1; + switch (loc->dw_loc_oprnd2.val_class) + { + case dw_val_class_vec: + size += loc->dw_loc_oprnd2.v.val_vec.length + * loc->dw_loc_oprnd2.v.val_vec.elt_size; + break; + case dw_val_class_const: + size += HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT; + break; + case dw_val_class_const_double: + size += 2 * HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT; + break; + default: + gcc_unreachable (); + } + break; + } + case DW_OP_GNU_regval_type: + { + unsigned long o + = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die); + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned) + + size_of_uleb128 (o); + } + break; + case DW_OP_GNU_deref_type: + { + unsigned long o + = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die); + size += 1 + size_of_uleb128 (o); + } + break; + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const) + size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned); + else + { + unsigned long o + = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die); + size += size_of_uleb128 (o); + } + break; + case DW_OP_GNU_parameter_ref: + size += 4; + break; default: break; } @@ -4912,7 +1959,7 @@ size_of_loc_descr (dw_loc_descr_ref loc) /* Return the size of a series of location descriptors. */ -static unsigned long +unsigned long size_of_locs (dw_loc_descr_ref loc) { dw_loc_descr_ref l; @@ -4939,11 +1986,18 @@ size_of_locs (dw_loc_descr_ref loc) } 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). */ +/* 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) +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; @@ -5114,14 +2168,28 @@ output_loc_operands (dw_loc_descr_ref loc) dw2_asm_output_data_sleb128 (val1->v.val_int, NULL); break; case DW_OP_regx: - dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); + { + 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: - dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); - dw2_asm_output_data_sleb128 (val2->v.val_int, NULL); + { + 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); @@ -5158,25 +2226,166 @@ output_loc_operands (dw_loc_descr_ref loc) } break; + case DW_OP_GNU_implicit_pointer: + { + char label[MAX_ARTIFICIAL_LABEL_BYTES + + HOST_BITS_PER_WIDE_INT / 2 + 2]; + gcc_assert (val1->val_class == dw_val_class_die_ref); + get_ref_die_offset_label (label, val1->v.val_die_ref.die); + dw2_asm_output_offset (DWARF_REF_SIZE, label, debug_info_section, NULL); + dw2_asm_output_data_sleb128 (val2->v.val_int, NULL); + } + break; + + case DW_OP_GNU_entry_value: + dw2_asm_output_data_uleb128 (size_of_locs (val1->v.val_loc), NULL); + output_loc_sequence (val1->v.val_loc, for_eh_or_skip); + break; + + case DW_OP_GNU_const_type: + { + unsigned long o = get_base_type_offset (val1->v.val_die_ref.die), l; + gcc_assert (o); + dw2_asm_output_data_uleb128 (o, NULL); + switch (val2->val_class) + { + case dw_val_class_const: + l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; + dw2_asm_output_data (1, l, NULL); + dw2_asm_output_data (l, val2->v.val_int, NULL); + break; + case dw_val_class_vec: + { + unsigned int elt_size = val2->v.val_vec.elt_size; + unsigned int len = val2->v.val_vec.length; + unsigned int i; + unsigned char *p; + + l = len * elt_size; + dw2_asm_output_data (1, l, NULL); + if (elt_size > sizeof (HOST_WIDE_INT)) + { + elt_size /= 2; + len *= 2; + } + for (i = 0, p = val2->v.val_vec.array; + i < len; + i++, p += elt_size) + dw2_asm_output_data (elt_size, extract_int (p, elt_size), + "fp or vector constant word %u", i); + } + break; + case dw_val_class_const_double: + { + unsigned HOST_WIDE_INT first, second; + l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; + + dw2_asm_output_data (1, 2 * l, NULL); + if (WORDS_BIG_ENDIAN) + { + first = val2->v.val_double.high; + second = val2->v.val_double.low; + } + else + { + first = val2->v.val_double.low; + second = val2->v.val_double.high; + } + dw2_asm_output_data (l, first, NULL); + dw2_asm_output_data (l, second, NULL); + } + break; + default: + gcc_unreachable (); + } + } + break; + case DW_OP_GNU_regval_type: + { + unsigned r = val1->v.val_unsigned; + unsigned long o = get_base_type_offset (val2->v.val_die_ref.die); + gcc_assert (o); + if (for_eh_or_skip >= 0) + { + r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip); + gcc_assert (size_of_uleb128 (r) + == size_of_uleb128 (val1->v.val_unsigned)); + } + dw2_asm_output_data_uleb128 (r, NULL); + dw2_asm_output_data_uleb128 (o, NULL); + } + break; + case DW_OP_GNU_deref_type: + { + unsigned long o = get_base_type_offset (val2->v.val_die_ref.die); + gcc_assert (o); + dw2_asm_output_data (1, val1->v.val_int, NULL); + dw2_asm_output_data_uleb128 (o, NULL); + } + break; + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const) + dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL); + else + { + unsigned long o = get_base_type_offset (val1->v.val_die_ref.die); + gcc_assert (o); + dw2_asm_output_data_uleb128 (o, NULL); + } + break; + + case DW_OP_GNU_parameter_ref: + { + unsigned long o; + gcc_assert (val1->val_class == dw_val_class_die_ref); + o = get_ref_die_offset (val1->v.val_die_ref.die); + dw2_asm_output_data (4, o, NULL); + } + break; + default: /* Other codes have no operands. */ break; } } -/* Output a sequence of location operations. */ +/* 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. */ -static void -output_loc_sequence (dw_loc_descr_ref loc) +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. */ - dw2_asm_output_data (1, loc->dw_loc_opc, - "%s", dwarf_stack_op_name (loc->dw_loc_opc)); + 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); + output_loc_operands (loc, for_eh_or_skip); } } @@ -5237,9 +2446,18 @@ output_loc_operands_raw (dw_loc_descr_ref loc) } 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_regx: case DW_OP_piece: fputc (',', asm_out_file); dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned); @@ -5290,10 +2508,26 @@ output_loc_operands_raw (dw_loc_descr_ref loc) break; case DW_OP_bregx: - fputc (',', asm_out_file); - dw2_asm_output_data_uleb128_raw (val1->v.val_unsigned); - fputc (',', asm_out_file); - dw2_asm_output_data_sleb128_raw (val2->v.val_int); + { + unsigned r = DWARF2_FRAME_REG_OUT (val1->v.val_unsigned, 1); + gcc_assert (size_of_uleb128 (r) + == size_of_uleb128 (val1->v.val_unsigned)); + fputc (',', asm_out_file); + dw2_asm_output_data_uleb128_raw (r); + fputc (',', asm_out_file); + dw2_asm_output_data_sleb128_raw (val2->v.val_int); + } + break; + + case DW_OP_GNU_implicit_pointer: + case DW_OP_GNU_entry_value: + case DW_OP_GNU_const_type: + case DW_OP_GNU_regval_type: + case DW_OP_GNU_deref_type: + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + case DW_OP_GNU_parameter_ref: + gcc_unreachable (); break; default: @@ -5302,13 +2536,29 @@ output_loc_operands_raw (dw_loc_descr_ref loc) } } -static void +void output_loc_sequence_raw (dw_loc_descr_ref loc) { while (1) { + enum dwarf_location_atom opc = loc->dw_loc_opc; /* Output the opcode. */ - fprintf (asm_out_file, "%#x", loc->dw_loc_opc); + 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) @@ -5319,61 +2569,11 @@ output_loc_sequence_raw (dw_loc_descr_ref loc) } } -/* This routine will generate the correct assembly data for a location - description based on a cfi entry with a complex address. */ - -static void -output_cfa_loc (dw_cfi_ref cfi) -{ - dw_loc_descr_ref loc; - unsigned long size; - - if (cfi->dw_cfi_opc == DW_CFA_expression) - { - dw2_asm_output_data (1, cfi->dw_cfi_oprnd1.dw_cfi_reg_num, NULL); - loc = cfi->dw_cfi_oprnd2.dw_cfi_loc; - } - else - loc = cfi->dw_cfi_oprnd1.dw_cfi_loc; - - /* Output the size of the block. */ - size = size_of_locs (loc); - dw2_asm_output_data_uleb128 (size, NULL); - - /* Now output the operations themselves. */ - output_loc_sequence (loc); -} - -/* Similar, but used for .cfi_escape. */ - -static void -output_cfa_loc_raw (dw_cfi_ref cfi) -{ - dw_loc_descr_ref loc; - unsigned long size; - - if (cfi->dw_cfi_opc == DW_CFA_expression) - { - fprintf (asm_out_file, "%#x,", cfi->dw_cfi_oprnd1.dw_cfi_reg_num); - loc = cfi->dw_cfi_oprnd2.dw_cfi_loc; - } - else - loc = cfi->dw_cfi_oprnd1.dw_cfi_loc; - - /* Output the size of the block. */ - size = size_of_locs (loc); - dw2_asm_output_data_uleb128_raw (size); - fputc (',', asm_out_file); - - /* Now output the operations themselves. */ - output_loc_sequence_raw (loc); -} - /* This function builds a dwarf location descriptor sequence from a dw_cfa_location, adding the given OFFSET to the result of the expression. */ -static struct dw_loc_descr_struct * +struct dw_loc_descr_struct * build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset) { struct dw_loc_descr_struct *head, *tmp; @@ -5402,15 +2602,16 @@ build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset) the address at OFFSET from the CFA when stack is aligned to ALIGNMENT byte. */ -static struct dw_loc_descr_struct * -build_cfa_aligned_loc (HOST_WIDE_INT offset, HOST_WIDE_INT alignment) +struct dw_loc_descr_struct * +build_cfa_aligned_loc (dw_cfa_location *cfa, + HOST_WIDE_INT offset, HOST_WIDE_INT alignment) { struct dw_loc_descr_struct *head; unsigned int dwarf_fp = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM); - /* When CFA is defined as FP+OFFSET, emulate stack alignment. */ - if (cfa.reg == HARD_FRAME_POINTER_REGNUM && cfa.indirect == 0) + /* When CFA is defined as FP+OFFSET, emulate stack alignment. */ + if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0) { head = new_reg_loc_descr (dwarf_fp, 0); add_loc_descr (&head, int_loc_descriptor (alignment)); @@ -5421,113 +2622,6 @@ build_cfa_aligned_loc (HOST_WIDE_INT offset, HOST_WIDE_INT alignment) head = new_reg_loc_descr (dwarf_fp, offset); return head; } - -/* This function fills in aa dw_cfa_location structure from a dwarf location - descriptor sequence. */ - -static void -get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_struct *loc) -{ - struct dw_loc_descr_struct *ptr; - cfa->offset = 0; - cfa->base_offset = 0; - cfa->indirect = 0; - cfa->reg = -1; - - for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next) - { - enum dwarf_location_atom op = ptr->dw_loc_opc; - - switch (op) - { - case DW_OP_reg0: - case DW_OP_reg1: - case DW_OP_reg2: - case DW_OP_reg3: - case DW_OP_reg4: - case DW_OP_reg5: - case DW_OP_reg6: - case DW_OP_reg7: - case DW_OP_reg8: - case DW_OP_reg9: - case DW_OP_reg10: - case DW_OP_reg11: - case DW_OP_reg12: - case DW_OP_reg13: - case DW_OP_reg14: - case DW_OP_reg15: - case DW_OP_reg16: - case DW_OP_reg17: - case DW_OP_reg18: - case DW_OP_reg19: - case DW_OP_reg20: - case DW_OP_reg21: - case DW_OP_reg22: - case DW_OP_reg23: - case DW_OP_reg24: - case DW_OP_reg25: - case DW_OP_reg26: - case DW_OP_reg27: - case DW_OP_reg28: - case DW_OP_reg29: - case DW_OP_reg30: - case DW_OP_reg31: - cfa->reg = op - DW_OP_reg0; - break; - case DW_OP_regx: - cfa->reg = ptr->dw_loc_oprnd1.v.val_int; - break; - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: - cfa->reg = op - DW_OP_breg0; - cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int; - break; - case DW_OP_bregx: - cfa->reg = ptr->dw_loc_oprnd1.v.val_int; - cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int; - break; - case DW_OP_deref: - cfa->indirect = 1; - break; - case DW_OP_plus_uconst: - cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned; - break; - default: - internal_error ("DW_LOC_OP %s not implemented", - dwarf_stack_op_name (ptr->dw_loc_opc)); - } - } -} /* And now, the support for symbolic debugging information. */ @@ -5552,10 +2646,6 @@ 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); -static void dwarf2out_direct_call (tree); -static void dwarf2out_virtual_call_token (tree, int); -static void dwarf2out_copy_call_info (rtx, rtx); -static void dwarf2out_virtual_call (int); static void dwarf2out_begin_function (tree); static void dwarf2out_set_name (tree, tree); @@ -5598,12 +2688,9 @@ const struct gcc_debug_hooks dwarf2_debug_hooks = debug_nothing_int, /* handle_pch */ dwarf2out_var_location, dwarf2out_switch_text_section, - dwarf2out_direct_call, - dwarf2out_virtual_call_token, - dwarf2out_copy_call_info, - dwarf2out_virtual_call, dwarf2out_set_name, - 1 /* start_end_main_source_file */ + 1, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_DIE /* tree_type_symtab_field */ }; /* NOTE: In the comments in this file, many references are made to @@ -5615,6 +2702,16 @@ const struct gcc_debug_hooks dwarf2_debug_hooks = representation is done after the entire program has been compiled. The types below are used to describe the internal representation. */ +/* Whether to put type DIEs into their own section .debug_types instead + of making them part of the .debug_info section. Only supported for + Dwarf V4 or higher and the user didn't disable them through + -fno-debug-types-section. It is more efficient to put them in a + separate comdat sections since the linker will then be able to + remove duplicates. But not all tools support .debug_types sections + yet. */ + +#define use_debug_types (dwarf_version >= 4 && flag_debug_types_section) + /* Various DIE's use offsets relative to the beginning of the .debug_info section to refer to each other. */ @@ -5624,31 +2721,70 @@ typedef long int dw_offset; typedef struct dw_attr_struct *dw_attr_ref; typedef struct dw_line_info_struct *dw_line_info_ref; -typedef struct dw_separate_line_info_struct *dw_separate_line_info_ref; typedef struct pubname_struct *pubname_ref; typedef struct dw_ranges_struct *dw_ranges_ref; typedef struct dw_ranges_by_label_struct *dw_ranges_by_label_ref; typedef struct comdat_type_struct *comdat_type_node_ref; -/* Each entry in the line_info_table maintains the file and - line number associated with the label generated for that - entry. The label gives the PC value associated with - the line number entry. */ +/* The entries in the line_info table more-or-less mirror the opcodes + that are used in the real dwarf line table. Arrays of these entries + are collected per section when DWARF2_ASM_LINE_DEBUG_INFO is not + supported. */ + +enum dw_line_info_opcode { + /* Emit DW_LNE_set_address; the operand is the label index. */ + LI_set_address, + + /* Emit a row to the matrix with the given line. This may be done + via any combination of DW_LNS_copy, DW_LNS_advance_line, and + special opcodes. */ + LI_set_line, + + /* Emit a DW_LNS_set_file. */ + LI_set_file, + + /* Emit a DW_LNS_set_column. */ + LI_set_column, + + /* Emit a DW_LNS_negate_stmt; the operand is ignored. */ + LI_negate_stmt, + + /* Emit a DW_LNS_set_prologue_end/epilogue_begin; the operand is ignored. */ + LI_set_prologue_end, + LI_set_epilogue_begin, + + /* Emit a DW_LNE_set_discriminator. */ + LI_set_discriminator +}; typedef struct GTY(()) dw_line_info_struct { - unsigned long dw_file_num; - unsigned long dw_line_num; -} -dw_line_info_entry; + enum dw_line_info_opcode opcode; + unsigned int val; +} dw_line_info_entry; + +DEF_VEC_O(dw_line_info_entry); +DEF_VEC_ALLOC_O(dw_line_info_entry, gc); + +typedef struct GTY(()) dw_line_info_table_struct { + /* The label that marks the end of this section. */ + const char *end_label; + + /* The values for the last row of the matrix, as collected in the table. + These are used to minimize the changes to the next row. */ + unsigned int file_num; + unsigned int line_num; + unsigned int column_num; + int discrim_num; + bool is_stmt; + bool in_use; + + VEC(dw_line_info_entry, gc) *entries; +} dw_line_info_table; -/* Line information for functions in separate sections; each one gets its - own sequence. */ -typedef struct GTY(()) dw_separate_line_info_struct { - unsigned long dw_file_num; - unsigned long dw_line_num; - unsigned long function; -} -dw_separate_line_info_entry; +typedef dw_line_info_table *dw_line_info_table_p; + +DEF_VEC_P(dw_line_info_table_p); +DEF_VEC_ALLOC_P(dw_line_info_table_p, gc); /* Each DIE attribute has a field specifying the attribute kind, a link to the next attribute in the chain, and an attribute value. @@ -5668,13 +2804,12 @@ DEF_VEC_ALLOC_O(dw_attr_node,gc); die_sib. die_child points to the node *before* the "first" child node. */ typedef struct GTY((chain_circular ("%h.die_sib"))) die_struct { - enum dwarf_tag die_tag; union die_symbol_or_type_node { char * GTY ((tag ("0"))) die_symbol; comdat_type_node_ref GTY ((tag ("1"))) die_type_node; } - GTY ((desc ("dwarf_version >= 4"))) die_id; + GTY ((desc ("use_debug_types"))) die_id; VEC(dw_attr_node,gc) * die_attr; dw_die_ref die_parent; dw_die_ref die_child; @@ -5686,6 +2821,7 @@ typedef struct GTY((chain_circular ("%h.die_sib"))) die_struct { /* Die is used and must not be pruned as unused. */ int die_perennial_p; unsigned int decl_id; + enum dwarf_tag die_tag; } die_node; @@ -5715,6 +2851,18 @@ struct GTY(()) dw_ranges_struct { int num; }; +/* A structure to hold a macinfo entry. */ + +typedef struct GTY(()) macinfo_struct { + unsigned char code; + unsigned HOST_WIDE_INT lineno; + const char *info; +} +macinfo_entry; + +DEF_VEC_O(macinfo_entry); +DEF_VEC_ALLOC_O(macinfo_entry, gc); + struct GTY(()) dw_ranges_by_label_struct { const char *begin; const char *end; @@ -5738,7 +2886,7 @@ typedef struct GTY(()) limbo_die_struct { } limbo_die_node; -typedef struct GTY(()) skeleton_chain_struct +typedef struct skeleton_chain_struct { dw_die_ref old_die; dw_die_ref new_die; @@ -5746,25 +2894,20 @@ typedef struct GTY(()) skeleton_chain_struct } skeleton_chain_node; -/* How to start an assembler comment. */ -#ifndef ASM_COMMENT_START -#define ASM_COMMENT_START ";#" -#endif - /* Define a macro which returns nonzero for a TYPE_DECL which was - implicitly generated for a tagged type. + implicitly generated for a type. - Note that unlike the gcc front end (which generates a NULL named - TYPE_DECL node for each complete tagged type, each array type, and - each function type node created) the g++ front end generates a - _named_ TYPE_DECL node for each tagged type node created. + Note that, unlike the C front-end (which generates a NULL named + TYPE_DECL node for each complete tagged type, each array type, + and each function type node created) the C++ front-end generates + a _named_ TYPE_DECL node for each tagged type node created. These TYPE_DECLs have DECL_ARTIFICIAL set, so we know not to - generate a DW_TAG_typedef DIE for them. */ + generate a DW_TAG_typedef DIE for them. Likewise with the Ada + front-end, but for each type, tagged or not. */ #define TYPE_DECL_IS_STUB(decl) \ (DECL_NAME (decl) == NULL_TREE \ || (DECL_ARTIFICIAL (decl) \ - && is_tagged_type (TREE_TYPE (decl)) \ && ((decl == TYPE_STUB_DECL (TREE_TYPE (decl))) \ /* This is necessary for stub decls that \ appear in nested inline functions. */ \ @@ -5814,7 +2957,7 @@ skeleton_chain_node; #define DWARF_LINE_BASE -10 /* First special line opcode - leave room for the standard opcodes. */ -#define DWARF_LINE_OPCODE_BASE 10 +#define DWARF_LINE_OPCODE_BASE ((int)DW_LNS_set_isa + 1) /* Range of line offsets in a special line info. opcode. */ #define DWARF_LINE_RANGE (254-DWARF_LINE_OPCODE_BASE+1) @@ -5835,7 +2978,7 @@ skeleton_chain_node; static unsigned long next_die_offset; /* Record the root of the DIE's built for the current compilation unit. */ -static GTY(()) dw_die_ref comp_unit_die; +static GTY(()) dw_die_ref single_comp_unit_die; /* A list of type DIEs that have been separated into comdat sections. */ static GTY(()) comdat_type_node *comdat_type_list; @@ -5893,15 +3036,56 @@ struct GTY (()) var_loc_list_def { Do not mark it for GC because it is marked through the chain. */ struct var_loc_node * GTY ((skip ("%h"))) last; + /* Pointer to the last element before section switch, + if NULL, either sections weren't switched or first + is after section switch. */ + struct var_loc_node * GTY ((skip ("%h"))) last_before_switch; + /* DECL_UID of the variable decl. */ unsigned int decl_id; }; typedef struct var_loc_list_def var_loc_list; +/* Call argument location list. */ +struct GTY ((chain_next ("%h.next"))) call_arg_loc_node { + rtx GTY (()) call_arg_loc_note; + const char * GTY (()) label; + tree GTY (()) block; + bool tail_call_p; + rtx GTY (()) symbol_ref; + struct call_arg_loc_node * GTY (()) next; +}; + /* Table of decl location linked lists. */ static GTY ((param_is (var_loc_list))) htab_t decl_loc_table; +/* Head and tail of call_arg_loc chain. */ +static GTY (()) struct call_arg_loc_node *call_arg_locations; +static struct call_arg_loc_node *call_arg_loc_last; + +/* Number of call sites in the current function. */ +static int call_site_count = -1; +/* Number of tail call sites in the current function. */ +static int tail_call_site_count = -1; + +/* Vector mapping block numbers to DW_TAG_{lexical_block,inlined_subroutine} + DIEs. */ +static VEC (dw_die_ref, heap) *block_map; + +/* A cached location list. */ +struct GTY (()) cached_dw_loc_list_def { + /* The DECL_UID of the decl that this entry describes. */ + unsigned int decl_id; + + /* The cached location list. */ + dw_loc_list_ref loc_list; +}; +typedef struct cached_dw_loc_list_def cached_dw_loc_list; + +/* Table of cached location lists. */ +static GTY ((param_is (cached_dw_loc_list))) htab_t cached_dw_loc_list_table; + /* A pointer to the base of a list of references to DIE's that are uniquely identified by their tag, presence/absence of children DIE's, and list of attribute/value pairs. */ @@ -5918,31 +3102,24 @@ static GTY(()) unsigned abbrev_die_table_in_use; abbrev_die_table. */ #define ABBREV_DIE_TABLE_INCREMENT 256 -/* A pointer to the base of a table that contains line information - for each source code line in .text in the compilation unit. */ -static GTY((length ("line_info_table_allocated"))) - dw_line_info_ref line_info_table; - -/* Number of elements currently allocated for line_info_table. */ -static GTY(()) unsigned line_info_table_allocated; - -/* Number of elements in line_info_table currently in use. */ -static GTY(()) unsigned line_info_table_in_use; +/* A global counter for generating labels for line number data. */ +static unsigned int line_info_label_num; -/* A pointer to the base of a table that contains line information - for each source code line outside of .text in the compilation unit. */ -static GTY ((length ("separate_line_info_table_allocated"))) - dw_separate_line_info_ref separate_line_info_table; +/* The current table to which we should emit line number information + for the current function. This will be set up at the beginning of + assembly for the function. */ +static dw_line_info_table *cur_line_info_table; -/* Number of elements currently allocated for separate_line_info_table. */ -static GTY(()) unsigned separate_line_info_table_allocated; +/* The two default tables of line number info. */ +static GTY(()) dw_line_info_table *text_section_line_info; +static GTY(()) dw_line_info_table *cold_text_section_line_info; -/* Number of elements in separate_line_info_table currently in use. */ -static GTY(()) unsigned separate_line_info_table_in_use; +/* The set of all non-default tables of line number info. */ +static GTY(()) VEC (dw_line_info_table_p, gc) *separate_line_info; -/* Size (in elements) of increments by which we may expand the - line_info_table. */ -#define LINE_INFO_TABLE_INCREMENT 1024 +/* A flag to tell pubnames/types export if there is an info section to + refer to. */ +static bool info_section_emitted; /* A pointer to the base of a table that contains a list of publicly accessible names. */ @@ -5952,18 +3129,9 @@ static GTY (()) VEC (pubname_entry, gc) * pubname_table; accessible types. */ static GTY (()) VEC (pubname_entry, gc) * pubtype_table; -/* Array of dies for which we should generate .debug_arange info. */ -static GTY((length ("arange_table_allocated"))) dw_die_ref *arange_table; - -/* Number of elements currently allocated for arange_table. */ -static GTY(()) unsigned arange_table_allocated; - -/* Number of elements in arange_table currently in use. */ -static GTY(()) unsigned arange_table_in_use; - -/* Size (in elements) of increments by which we may expand the - arange_table. */ -#define ARANGE_TABLE_INCREMENT 64 +/* A pointer to the base of a table that contains a list of macro + defines/undefines (and file start/end markers). */ +static GTY (()) VEC (macinfo_entry, gc) * macinfo_table; /* Array of dies for which we should generate .debug_ranges info. */ static GTY ((length ("ranges_table_allocated"))) dw_ranges_ref ranges_table; @@ -5997,47 +3165,8 @@ static GTY(()) unsigned int loclabel_num; /* Unique label counter for point-of-call tables. */ static GTY(()) unsigned int poc_label_num; -/* The direct call table structure. */ - -typedef struct GTY(()) dcall_struct { - unsigned int poc_label_num; - tree poc_decl; - dw_die_ref targ_die; -} -dcall_entry; - -DEF_VEC_O(dcall_entry); -DEF_VEC_ALLOC_O(dcall_entry, gc); - -/* The virtual call table structure. */ - -typedef struct GTY(()) vcall_struct { - unsigned int poc_label_num; - unsigned int vtable_slot; -} -vcall_entry; - -DEF_VEC_O(vcall_entry); -DEF_VEC_ALLOC_O(vcall_entry, gc); - -/* Pointers to the direct and virtual call tables. */ -static GTY (()) VEC (dcall_entry, gc) * dcall_table = NULL; -static GTY (()) VEC (vcall_entry, gc) * vcall_table = NULL; - -/* A hash table to map INSN_UIDs to vtable slot indexes. */ - -struct GTY (()) vcall_insn { - int insn_uid; - unsigned int vtable_slot; -}; - -static GTY ((param_is (struct vcall_insn))) htab_t vcall_insn_table; - /* Record whether the function being analyzed contains inlined functions. */ static int current_function_has_inlines; -#if 0 && defined (MIPS_DEBUGGING_INFO) -static int comp_unit_has_inlines; -#endif /* The last file entry emitted by maybe_emit_file(). */ static GTY(()) struct dwarf_file_data * last_emitted_file; @@ -6050,9 +3179,18 @@ static GTY(()) struct dwarf_file_data * file_table_last_lookup; static GTY(()) VEC(die_arg_entry,gc) *tmpl_value_parm_die_table; +/* Instances of generic types for which we need to generate debug + info that describe their generic parameters and arguments. That + generation needs to happen once all types are properly laid out so + we do it at the end of compilation. */ +static GTY(()) VEC(tree,gc) *generic_type_instances; + /* Offset from the "steady-state frame pointer" to the frame base, within the current function. */ static HOST_WIDE_INT frame_pointer_fb_offset; +static bool frame_pointer_fb_offset_valid; + +static VEC (dw_die_ref, heap) *base_types; /* Forward declarations for functions defined in this file. */ @@ -6118,6 +3256,8 @@ static void remove_child_TAG (dw_die_ref, enum dwarf_tag); static void add_child_die (dw_die_ref, dw_die_ref); static dw_die_ref new_die (enum dwarf_tag, dw_die_ref, tree); static dw_die_ref lookup_type_die (tree); +static dw_die_ref strip_naming_typedef (tree, dw_die_ref); +static dw_die_ref lookup_type_die_strip_naming_typedef (tree); static void equate_type_number_to_die (tree, dw_die_ref); static hashval_t decl_die_table_hash (const void *); static int decl_die_table_eq (const void *, const void *); @@ -6131,7 +3271,6 @@ static void equate_decl_number_to_die (tree, dw_die_ref); static struct var_loc_node *add_var_loc_to_decl (tree, rtx, const char *); static void print_spaces (FILE *); static void print_die (dw_die_ref, FILE *); -static void print_dwarf_line_table (FILE *); static dw_die_ref push_new_compile_unit (dw_die_ref, dw_die_ref); static dw_die_ref pop_compile_unit (dw_die_ref); static void loc_checksum (dw_loc_descr_ref, struct md5_ctx *); @@ -6185,6 +3324,7 @@ static void output_location_lists (dw_die_ref); static int constant_size (unsigned HOST_WIDE_INT); static unsigned long size_of_die (dw_die_ref); static void calc_die_sizes (dw_die_ref); +static void calc_base_type_die_sizes (void); static void mark_dies (dw_die_ref); static void unmark_dies (dw_die_ref); static void unmark_all_dies (dw_die_ref); @@ -6203,13 +3343,13 @@ static void add_pubname (tree, dw_die_ref); static void add_pubname_string (const char *, dw_die_ref); static void add_pubtype (tree, dw_die_ref); static void output_pubnames (VEC (pubname_entry,gc) *); -static void add_arange (tree, dw_die_ref); -static void output_aranges (void); +static void output_aranges (unsigned long); static unsigned int add_ranges_num (int); static unsigned int add_ranges (const_tree); static void add_ranges_by_labels (dw_die_ref, const char *, const char *, bool *); static void output_ranges (void); +static dw_line_info_table *new_line_info_table (void); static void output_line_info (void); static void output_file_names (void); static dw_die_ref base_type_die (tree); @@ -6250,11 +3390,12 @@ static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *); static void insert_double (double_int, unsigned char *); static void insert_float (const_rtx, unsigned char *); static rtx rtl_for_decl_location (tree); -static bool add_location_or_const_value_attribute (dw_die_ref, tree, +static bool add_location_or_const_value_attribute (dw_die_ref, tree, bool, enum dwarf_attribute); static bool tree_add_const_value_attribute (dw_die_ref, tree); static bool tree_add_const_value_attribute_for_decl (dw_die_ref, tree); static void add_name_attribute (dw_die_ref, const char *); +static void add_gnat_descriptive_type_attribute (dw_die_ref, tree, dw_die_ref); static void add_comp_dir_attribute (dw_die_ref); static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree); static void add_subscript_info (dw_die_ref, tree, bool); @@ -6307,7 +3448,7 @@ static void gen_typedef_die (tree, dw_die_ref); static void gen_type_die (tree, dw_die_ref); static void gen_block_die (tree, dw_die_ref, int); static void decls_for_scope (tree, dw_die_ref, int); -static int is_redundant_typedef (const_tree); +static inline int is_redundant_typedef (const_tree); static bool is_naming_typedef_decl (const_tree); static inline dw_die_ref get_context_die (tree); static void gen_namespace_die (tree, dw_die_ref); @@ -6330,6 +3471,7 @@ static void output_loc_list (dw_loc_list_ref); static char *gen_internal_sym (const char *); static void prune_unmark_dies (dw_die_ref); +static void prune_unused_types_mark_generic_parms_dies (dw_die_ref); static void prune_unused_types_mark (dw_die_ref, int); static void prune_unused_types_walk (dw_die_ref); static void prune_unused_types_walk_attribs (dw_die_ref); @@ -6342,6 +3484,9 @@ static inline void add_AT_vms_delta (dw_die_ref, enum dwarf_attribute, const char *, const char *); static void append_entry_to_tmpl_value_parm_die_table (dw_die_ref, tree); static void gen_remaining_tmpl_value_param_die_attribute (void); +static bool generic_type_p (tree); +static void schedule_generic_params_dies_gen (tree t); +static void gen_scheduled_generic_parms_dies (void); /* Section names used to hold DWARF debugging information. */ #ifndef DEBUG_INFO_SECTION @@ -6356,6 +3501,9 @@ static void gen_remaining_tmpl_value_param_die_attribute (void); #ifndef DEBUG_MACINFO_SECTION #define DEBUG_MACINFO_SECTION ".debug_macinfo" #endif +#ifndef DEBUG_MACRO_SECTION +#define DEBUG_MACRO_SECTION ".debug_macro" +#endif #ifndef DEBUG_LINE_SECTION #define DEBUG_LINE_SECTION ".debug_line" #endif @@ -6368,12 +3516,6 @@ static void gen_remaining_tmpl_value_param_die_attribute (void); #ifndef DEBUG_PUBTYPES_SECTION #define DEBUG_PUBTYPES_SECTION ".debug_pubtypes" #endif -#ifndef DEBUG_DCALL_SECTION -#define DEBUG_DCALL_SECTION ".debug_dcall" -#endif -#ifndef DEBUG_VCALL_SECTION -#define DEBUG_VCALL_SECTION ".debug_vcall" -#endif #ifndef DEBUG_STR_SECTION #define DEBUG_STR_SECTION ".debug_str" #endif @@ -6419,6 +3561,9 @@ static void gen_remaining_tmpl_value_param_die_attribute (void); #ifndef DEBUG_MACINFO_SECTION_LABEL #define DEBUG_MACINFO_SECTION_LABEL "Ldebug_macinfo" #endif +#ifndef DEBUG_MACRO_SECTION_LABEL +#define DEBUG_MACRO_SECTION_LABEL "Ldebug_macro" +#endif /* Definitions of defaults for formats and names of various special @@ -6453,11 +3598,17 @@ static char ranges_section_label[2 * MAX_ARTIFICIAL_LABEL_BYTES]; #ifndef LINE_CODE_LABEL #define LINE_CODE_LABEL "LM" #endif -#ifndef SEPARATE_LINE_CODE_LABEL -#define SEPARATE_LINE_CODE_LABEL "LSM" -#endif +/* Return the root of the DIE's built for the current compilation unit. */ +static dw_die_ref +comp_unit_die (void) +{ + if (!single_comp_unit_die) + single_comp_unit_die = gen_compile_unit_die (NULL); + return single_comp_unit_die; +} + /* We allow a language front-end to designate a function that is to be called to "demangle" any name before it is put into a DIE. */ @@ -6510,6 +3661,38 @@ is_tagged_type (const_tree type) || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE); } +/* Set label to debug_info_section_label + die_offset of a DIE reference. */ + +static void +get_ref_die_offset_label (char *label, dw_die_ref ref) +{ + sprintf (label, "%s+%ld", debug_info_section_label, ref->die_offset); +} + +/* Return die_offset of a DIE reference to a base type. */ + +static unsigned long int +get_base_type_offset (dw_die_ref ref) +{ + if (ref->die_offset) + return ref->die_offset; + if (comp_unit_die ()->die_abbrev) + { + calc_base_type_die_sizes (); + gcc_assert (ref->die_offset); + } + return ref->die_offset; +} + +/* Return die_offset of a DIE reference other than base type. */ + +static unsigned long int +get_ref_die_offset (dw_die_ref ref) +{ + gcc_assert (ref->die_offset); + return ref->die_offset; +} + /* Convert a DIE tag into its string name. */ static const char * @@ -6657,6 +3840,10 @@ dwarf_tag_name (unsigned int tag) return "DW_TAG_GNU_EINCL"; case DW_TAG_GNU_template_template_param: return "DW_TAG_GNU_template_template_param"; + case DW_TAG_GNU_call_site: + return "DW_TAG_GNU_call_site"; + case DW_TAG_GNU_call_site_parameter: + return "DW_TAG_GNU_call_site_parameter"; default: return "DW_TAG_"; } @@ -6881,6 +4068,7 @@ dwarf_attr_name (unsigned int attr) return "DW_AT_body_begin"; case DW_AT_body_end: return "DW_AT_body_end"; + case DW_AT_GNU_vector: return "DW_AT_GNU_vector"; case DW_AT_GNU_guarded_by: @@ -6901,6 +4089,27 @@ dwarf_attr_name (unsigned int attr) return "DW_AT_GNU_odr_signature"; case DW_AT_GNU_template_name: return "DW_AT_GNU_template_name"; + case DW_AT_GNU_call_site_value: + return "DW_AT_GNU_call_site_value"; + case DW_AT_GNU_call_site_data_value: + return "DW_AT_GNU_call_site_data_value"; + case DW_AT_GNU_call_site_target: + return "DW_AT_GNU_call_site_target"; + case DW_AT_GNU_call_site_target_clobbered: + return "DW_AT_GNU_call_site_target_clobbered"; + case DW_AT_GNU_tail_call: + return "DW_AT_GNU_tail_call"; + case DW_AT_GNU_all_tail_call_sites: + return "DW_AT_GNU_all_tail_call_sites"; + case DW_AT_GNU_all_call_sites: + return "DW_AT_GNU_all_call_sites"; + case DW_AT_GNU_all_source_call_sites: + return "DW_AT_GNU_all_source_call_sites"; + case DW_AT_GNU_macros: + return "DW_AT_GNU_macros"; + + case DW_AT_GNAT_descriptive_type: + return "DW_AT_GNAT_descriptive_type"; case DW_AT_VMS_rtnbeg_pd_address: return "DW_AT_VMS_rtnbeg_pd_address"; @@ -7202,37 +4411,6 @@ add_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind, const char *str) add_dwarf_attr (die, &attr); } -/* Create a label for an indirect string node, ensuring it is going to - be output, unless its reference count goes down to zero. */ - -static inline void -gen_label_for_indirect_string (struct indirect_string_node *node) -{ - char label[32]; - - if (node->label) - return; - - ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter); - ++dw2_string_counter; - node->label = xstrdup (label); -} - -/* Create a SYMBOL_REF rtx whose value is the initial address of a - debug string STR. */ - -static inline rtx -get_debug_string_label (const char *str) -{ - struct indirect_string_node *node = find_AT_string (str); - - debug_str_hash_forced = true; - - gen_label_for_indirect_string (node); - - return gen_rtx_SYMBOL_REF (Pmode, node->label); -} - static inline const char * AT_string (dw_attr_ref a) { @@ -7248,6 +4426,7 @@ AT_string_form (dw_attr_ref a) { struct indirect_string_node *node; unsigned int len; + char label[32]; gcc_assert (a && AT_class (a) == dw_val_class_str); @@ -7270,7 +4449,9 @@ AT_string_form (dw_attr_ref a) && (len - DWARF_OFFSET_SIZE) * node->refcount <= len)) return node->form = DW_FORM_string; - gen_label_for_indirect_string (node); + ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter); + ++dw2_string_counter; + node->label = xstrdup (label); return node->form = DW_FORM_strp; } @@ -7282,6 +4463,15 @@ add_AT_die_ref (dw_die_ref die, enum dwarf_attribute attr_kind, dw_die_ref targ_ { dw_attr_node attr; +#ifdef ENABLE_CHECKING + gcc_assert (targ_die != NULL); +#else + /* With LTO we can end up trying to reference something we didn't create + a DIE for. Avoid crashing later on a NULL referenced DIE. */ + if (targ_die == NULL) + return; +#endif + attr.dw_attr = attr_kind; attr.dw_attr_val.val_class = dw_val_class_die_ref; attr.dw_attr_val.v.val_die_ref.die = targ_die; @@ -7552,7 +4742,7 @@ get_AT (dw_die_ref die, enum dwarf_attribute attr_kind) if (! die) return NULL; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (a->dw_attr == attr_kind) return a; else if (a->dw_attr == DW_AT_specification @@ -7643,7 +4833,7 @@ get_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind) static inline bool is_cxx (void) { - unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); + unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language); return lang == DW_LANG_C_plus_plus || lang == DW_LANG_ObjC_plus_plus; } @@ -7653,7 +4843,7 @@ is_cxx (void) static inline bool is_fortran (void) { - unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); + unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language); return (lang == DW_LANG_Fortran77 || lang == DW_LANG_Fortran90 @@ -7665,7 +4855,7 @@ is_fortran (void) static inline bool is_ada (void) { - unsigned int lang = get_AT_unsigned (comp_unit_die, DW_AT_language); + unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language); return lang == DW_LANG_Ada95 || lang == DW_LANG_Ada83; } @@ -7681,7 +4871,7 @@ remove_AT (dw_die_ref die, enum dwarf_attribute attr_kind) if (! die) return; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (a->dw_attr == attr_kind) { if (AT_class (a) == dw_val_class_str) @@ -7861,6 +5051,39 @@ lookup_type_die (tree type) return TYPE_SYMTAB_DIE (type); } +/* Given a TYPE_DIE representing the type TYPE, if TYPE is an + anonymous type named by the typedef TYPE_DIE, return the DIE of the + anonymous type instead the one of the naming typedef. */ + +static inline dw_die_ref +strip_naming_typedef (tree type, dw_die_ref type_die) +{ + if (type + && TREE_CODE (type) == RECORD_TYPE + && type_die + && type_die->die_tag == DW_TAG_typedef + && is_naming_typedef_decl (TYPE_NAME (type))) + type_die = get_AT_ref (type_die, DW_AT_type); + return type_die; +} + +/* Like lookup_type_die, but if type is an anonymous type named by a + typedef[1], return the DIE of the anonymous type instead the one of + the naming typedef. This is because in gen_typedef_die, we did + equate the anonymous struct named by the typedef with the DIE of + the naming typedef. So by default, lookup_type_die on an anonymous + struct yields the DIE of the naming typedef. + + [1]: Read the comment of is_naming_typedef_decl to learn about what + a naming typedef is. */ + +static inline dw_die_ref +lookup_type_die_strip_naming_typedef (tree type) +{ + dw_die_ref die = lookup_type_die (type); + return strip_naming_typedef (type, die); +} + /* Equate a DIE to a given type specifier. */ static inline void @@ -7921,6 +5144,24 @@ lookup_decl_loc (const_tree decl) htab_find_with_hash (decl_loc_table, decl, DECL_UID (decl)); } +/* Returns a hash value for X (which really is a cached_dw_loc_list_list). */ + +static hashval_t +cached_dw_loc_list_table_hash (const void *x) +{ + return (hashval_t) ((const cached_dw_loc_list *) x)->decl_id; +} + +/* Return nonzero if decl_id of cached_dw_loc_list X is the same as + UID of decl *Y. */ + +static int +cached_dw_loc_list_table_eq (const void *x, const void *y) +{ + return (((const cached_dw_loc_list *) x)->decl_id + == DECL_UID ((const_tree) y)); +} + /* Equate a DIE to a particular declaration. */ static void @@ -8113,7 +5354,30 @@ add_var_loc_to_decl (tree decl, rtx loc_note, const char *label) else temp = (var_loc_list *) *slot; - if (temp->last) + /* For PARM_DECLs try to keep around the original incoming value, + even if that means we'll emit a zero-range .debug_loc entry. */ + if (temp->last + && temp->first == temp->last + && TREE_CODE (decl) == PARM_DECL + && GET_CODE (temp->first->loc) == NOTE + && NOTE_VAR_LOCATION_DECL (temp->first->loc) == decl + && DECL_INCOMING_RTL (decl) + && NOTE_VAR_LOCATION_LOC (temp->first->loc) + && GET_CODE (NOTE_VAR_LOCATION_LOC (temp->first->loc)) + == GET_CODE (DECL_INCOMING_RTL (decl)) + && prev_real_insn (temp->first->loc) == NULL_RTX + && (bitsize != -1 + || !rtx_equal_p (NOTE_VAR_LOCATION_LOC (temp->first->loc), + NOTE_VAR_LOCATION_LOC (loc_note)) + || (NOTE_VAR_LOCATION_STATUS (temp->first->loc) + != NOTE_VAR_LOCATION_STATUS (loc_note)))) + { + loc = ggc_alloc_cleared_var_loc_node (); + temp->first->next = loc; + temp->last = loc; + loc->loc = construct_piece_list (loc_note, bitpos, bitsize); + } + else if (temp->last) { struct var_loc_node *last = temp->last, *unused = NULL; rtx *piece_loc = NULL, last_loc_note; @@ -8159,7 +5423,9 @@ add_var_loc_to_decl (tree decl, rtx loc_note, const char *label) } else { - gcc_assert (temp->first == temp->last); + gcc_assert (temp->first == temp->last + || (temp->first->next == temp->last + && TREE_CODE (decl) == PARM_DECL)); memset (temp->last, '\0', sizeof (*temp->last)); temp->last->loc = construct_piece_list (loc_note, bitpos, bitsize); return temp->last; @@ -8256,12 +5522,15 @@ print_die (dw_die_ref die, FILE *outfile) unsigned ix; print_spaces (outfile); - fprintf (outfile, "DIE %4ld: %s\n", - die->die_offset, dwarf_tag_name (die->die_tag)); + fprintf (outfile, "DIE %4ld: %s (%p)\n", + die->die_offset, dwarf_tag_name (die->die_tag), + (void*) die); print_spaces (outfile); fprintf (outfile, " abbrev id: %lu", die->die_abbrev); - fprintf (outfile, " offset: %ld\n", die->die_offset); - if (dwarf_version >= 4 && die->die_id.die_type_node) + fprintf (outfile, " offset: %ld", die->die_offset); + fprintf (outfile, " mark: %d\n", die->die_mark); + + if (use_debug_types && die->die_id.die_type_node) { print_spaces (outfile); fprintf (outfile, " signature: "); @@ -8269,7 +5538,7 @@ print_die (dw_die_ref die, FILE *outfile) fprintf (outfile, "\n"); } - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { print_spaces (outfile); fprintf (outfile, " %s: ", dwarf_attr_name (a->dw_attr)); @@ -8313,17 +5582,18 @@ print_die (dw_die_ref die, FILE *outfile) case dw_val_class_die_ref: if (AT_ref (a) != NULL) { - if (dwarf_version >= 4 && AT_ref (a)->die_id.die_type_node) + if (use_debug_types && AT_ref (a)->die_id.die_type_node) { fprintf (outfile, "die -> signature: "); print_signature (outfile, AT_ref (a)->die_id.die_type_node->signature); } - else if (dwarf_version < 4 && AT_ref (a)->die_id.die_symbol) + else if (! use_debug_types && AT_ref (a)->die_id.die_symbol) fprintf (outfile, "die -> label: %s", AT_ref (a)->die_id.die_symbol); else fprintf (outfile, "die -> %ld", AT_ref (a)->die_offset); + fprintf (outfile, " (%p)", (void *) AT_ref (a)); } else fprintf (outfile, "die -> "); @@ -8372,27 +5642,6 @@ print_die (dw_die_ref die, FILE *outfile) fprintf (outfile, "\n"); } -/* Print the contents of the source code line number correspondence table. - This routine is a debugging aid only. */ - -static void -print_dwarf_line_table (FILE *outfile) -{ - unsigned i; - dw_line_info_ref line_info; - - fprintf (outfile, "\n\nDWARF source line information\n"); - for (i = 1; i < line_info_table_in_use; i++) - { - line_info = &line_info_table[i]; - fprintf (outfile, "%5d: %4ld %6ld\n", i, - line_info->dw_file_num, - line_info->dw_line_num); - } - - fprintf (outfile, "\n\n"); -} - /* Print the information collected for a given DIE. */ DEBUG_FUNCTION void @@ -8408,9 +5657,7 @@ DEBUG_FUNCTION void debug_dwarf (void) { print_indent = 0; - print_die (comp_unit_die, stderr); - if (! DWARF2_ASM_LINE_DEBUG_INFO) - print_dwarf_line_table (stderr); + print_die (comp_unit_die (), stderr); } /* Start a new compilation unit DIE for an include file. OLD_UNIT is the CU @@ -8548,7 +5795,7 @@ die_checksum (dw_die_ref die, struct md5_ctx *ctx, int *mark) CHECKSUM (die->die_tag); - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) attr_checksum (a, ctx, mark); FOR_EACH_CHILD (die, c, die_checksum (c, ctx, mark)); @@ -8861,7 +6108,7 @@ collect_checksum_attributes (struct checksum_attributes *attrs, dw_die_ref die) dw_attr_ref a; unsigned ix; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { switch (a->dw_attr) { @@ -9295,7 +6542,7 @@ same_die_p (dw_die_ref die1, dw_die_ref die2, int *mark) != VEC_length (dw_attr_node, die2->die_attr)) return 0; - for (ix = 0; VEC_iterate (dw_attr_node, die1->die_attr, ix, a1); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die1->die_attr, ix, a1) if (!same_attr_p (a1, VEC_index (dw_attr_node, die2->die_attr, ix), mark)) return 0; @@ -9458,6 +6705,14 @@ is_symbol_die (dw_die_ref c) || c->die_tag == DW_TAG_module); } +/* Returns true iff C is a compile-unit DIE. */ + +static inline bool +is_cu_die (dw_die_ref c) +{ + return c && c->die_tag == DW_TAG_compile_unit; +} + static char * gen_internal_sym (const char *prefix) { @@ -9656,7 +6911,7 @@ is_declaration_die (dw_die_ref die) dw_attr_ref a; unsigned ix; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (a->dw_attr == DW_AT_declaration) return 1; @@ -9675,6 +6930,20 @@ is_nested_in_subprogram (dw_die_ref die) return local_scope_p (decl); } +/* Return non-zero if this DIE contains a defining declaration of a + subprogram. */ + +static int +contains_subprogram_definition (dw_die_ref die) +{ + dw_die_ref c; + + if (die->die_tag == DW_TAG_subprogram && ! is_declaration_die (die)) + return 1; + FOR_EACH_CHILD (die, c, if (contains_subprogram_definition(c)) return 1); + return 0; +} + /* Return non-zero if this is a type DIE that should be moved to a COMDAT .debug_types section. */ @@ -9693,6 +6962,8 @@ should_move_die_to_comdat (dw_die_ref die) || get_AT (die, DW_AT_abstract_origin) || is_nested_in_subprogram (die)) return 0; + /* A type definition should never contain a subprogram definition. */ + gcc_assert (!contains_subprogram_definition (die)); return 1; case DW_TAG_array_type: case DW_TAG_interface_type: @@ -9727,7 +6998,7 @@ clone_die (dw_die_ref die) clone = ggc_alloc_cleared_die_node (); clone->die_tag = die->die_tag; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) add_dwarf_attr (clone, a); return clone; @@ -9768,7 +7039,7 @@ clone_as_declaration (dw_die_ref die) clone = ggc_alloc_cleared_die_node (); clone->die_tag = die->die_tag; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { /* We don't want to copy over all attributes. For example we don't want DW_AT_byte_size because otherwise we will no @@ -9825,7 +7096,7 @@ copy_declaration_context (dw_die_ref unit, dw_die_ref die) remove_AT (die, DW_AT_specification); - for (ix = 0; VEC_iterate (dw_attr_node, decl->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, decl->die_attr, ix, a) { if (a->dw_attr != DW_AT_name && a->dw_attr != DW_AT_declaration @@ -9983,7 +7254,7 @@ break_out_comdat_types (dw_die_ref die) add it to the list of comdat types. */ unit = new_die (DW_TAG_type_unit, NULL, NULL); add_AT_unsigned (unit, DW_AT_language, - get_AT_unsigned (comp_unit_die, DW_AT_language)); + get_AT_unsigned (comp_unit_die (), DW_AT_language)); type_node = ggc_alloc_cleared_comdat_type_node (); type_node->root_die = unit; type_node->next = comdat_type_list; @@ -10121,7 +7392,7 @@ copy_decls_walk (dw_die_ref unit, dw_die_ref die, htab_t decl_table) dw_attr_ref a; unsigned ix; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { if (AT_class (a) == dw_val_class_die_ref) { @@ -10240,7 +7511,7 @@ output_location_lists (dw_die_ref die) dw_attr_ref a; unsigned ix; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (AT_class (a) == dw_val_class_loc_list) output_loc_list (AT_loc_list (a)); @@ -10263,11 +7534,11 @@ build_abbrev_table (dw_die_ref die) /* Scan the DIE references, and mark as external any that refer to DIEs from other CUs (i.e. those which are not marked). */ - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (AT_class (a) == dw_val_class_die_ref && AT_ref (a)->die_mark == 0) { - gcc_assert (dwarf_version >= 4 || AT_ref (a)->die_id.die_symbol); + gcc_assert (use_debug_types || AT_ref (a)->die_id.die_symbol); set_AT_ref_external (a, 1); } @@ -10287,7 +7558,7 @@ build_abbrev_table (dw_die_ref die) != VEC_length (dw_attr_node, die->die_attr)) continue; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, die_a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, die_a) { abbrev_a = VEC_index (dw_attr_node, abbrev->die_attr, ix); if ((abbrev_a->dw_attr != die_a->dw_attr) @@ -10351,7 +7622,7 @@ size_of_die (dw_die_ref die) unsigned ix; size += size_of_uleb128 (die->die_abbrev); - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { switch (AT_class (a)) { @@ -10383,7 +7654,15 @@ size_of_die (dw_die_ref die) size += size_of_sleb128 (AT_int (a)); break; case dw_val_class_unsigned_const: - size += constant_size (AT_unsigned (a)); + { + int csize = constant_size (AT_unsigned (a)); + if (dwarf_version == 3 + && a->dw_attr == DW_AT_data_member_location + && csize >= 4) + size += size_of_uleb128 (AT_unsigned (a)); + else + size += csize; + } break; case dw_val_class_const_double: size += 2 * HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; @@ -10411,11 +7690,11 @@ size_of_die (dw_die_ref die) case dw_val_class_die_ref: if (AT_ref_external (a)) { - /* In DWARF4, we use DW_FORM_sig8; for earlier versions + /* In DWARF4, we use DW_FORM_ref_sig8; for earlier versions we use DW_FORM_ref_addr. In DWARF2, DW_FORM_ref_addr is sized by target address length, whereas in DWARF3 it's always sized as an offset. */ - if (dwarf_version >= 4) + if (use_debug_types) size += DWARF_TYPE_SIGNATURE_SIZE; else if (dwarf_version == 2) size += DWARF2_ADDR_SIZE; @@ -10468,6 +7747,8 @@ calc_die_sizes (dw_die_ref die) { dw_die_ref c; + gcc_assert (die->die_offset == 0 + || (unsigned long int) die->die_offset == next_die_offset); die->die_offset = next_die_offset; next_die_offset += size_of_die (die); @@ -10478,6 +7759,36 @@ calc_die_sizes (dw_die_ref die) next_die_offset += 1; } +/* Size just the base type children at the start of the CU. + This is needed because build_abbrev needs to size locs + and sizing of type based stack ops needs to know die_offset + values for the base types. */ + +static void +calc_base_type_die_sizes (void) +{ + unsigned long die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE; + unsigned int i; + dw_die_ref base_type; +#if ENABLE_ASSERT_CHECKING + dw_die_ref prev = comp_unit_die ()->die_child; +#endif + + die_offset += size_of_die (comp_unit_die ()); + for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++) + { +#if ENABLE_ASSERT_CHECKING + gcc_assert (base_type->die_offset == 0 + && prev->die_sib == base_type + && base_type->die_child == NULL + && base_type->die_abbrev); + prev = base_type; +#endif + base_type->die_offset = die_offset; + die_offset += size_of_die (base_type); + } +} + /* Set the marks for a die and its children. We do this so that we know whether or not a reference needs to use FORM_ref_addr; only DIEs in the same CU will be marked. We used to clear out the offset @@ -10501,7 +7812,7 @@ unmark_dies (dw_die_ref die) { dw_die_ref c; - if (dwarf_version < 4) + if (! use_debug_types) gcc_assert (die->die_mark); die->die_mark = 0; @@ -10523,7 +7834,7 @@ unmark_all_dies (dw_die_ref die) FOR_EACH_CHILD (die, c, unmark_all_dies (c)); - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) if (AT_class (a) == dw_val_class_die_ref) unmark_all_dies (AT_ref (a)); } @@ -10539,7 +7850,7 @@ size_of_pubnames (VEC (pubname_entry, gc) * names) pubname_ref p; size = DWARF_PUBNAMES_HEADER_SIZE; - for (i = 0; VEC_iterate (pubname_entry, names, i, p); i++) + FOR_EACH_VEC_ELT (pubname_entry, names, i, p) if (names != pubtype_table || p->die->die_offset != 0 || !flag_eliminate_unused_debug_types) @@ -10563,7 +7874,19 @@ size_of_aranges (void) size += 2 * DWARF2_ADDR_SIZE; if (cold_text_section_used) size += 2 * DWARF2_ADDR_SIZE; - size += 2 * DWARF2_ADDR_SIZE * arange_table_in_use; + if (have_multiple_function_sections) + { + unsigned fde_idx; + dw_fde_ref fde; + + FOR_EACH_VEC_ELT (dw_fde_ref, fde_vec, fde_idx, fde) + { + if (!fde->in_std_section) + size += 2 * DWARF2_ADDR_SIZE; + if (fde->dw_fde_second_begin && !fde->second_in_std_section) + size += 2 * DWARF2_ADDR_SIZE; + } + } /* Count the two zero words used to terminated the address range table. */ size += 2 * DWARF2_ADDR_SIZE; @@ -10640,8 +7963,16 @@ value_format (dw_attr_ref a) case 2: return DW_FORM_data2; case 4: + /* In DWARF3 DW_AT_data_member_location with + DW_FORM_data4 or DW_FORM_data8 is a loclistptr, not + constant, so we need to use DW_FORM_udata if we need + a large constant. */ + if (dwarf_version == 3 && a->dw_attr == DW_AT_data_member_location) + return DW_FORM_udata; return DW_FORM_data4; case 8: + if (dwarf_version == 3 && a->dw_attr == DW_AT_data_member_location) + return DW_FORM_udata; return DW_FORM_data8; default: gcc_unreachable (); @@ -10687,7 +8018,7 @@ value_format (dw_attr_ref a) return DW_FORM_flag; case dw_val_class_die_ref: if (AT_ref_external (a)) - return dwarf_version >= 4 ? DW_FORM_sig8 : DW_FORM_ref_addr; + return use_debug_types ? DW_FORM_ref_sig8 : DW_FORM_ref_addr; else return DW_FORM_ref; case dw_val_class_fde_ref: @@ -10822,6 +8153,10 @@ output_loc_list (dw_loc_list_ref list_head) { dw_loc_list_ref curr = list_head; + if (list_head->emitted) + return; + list_head->emitted = true; + ASM_OUTPUT_LABEL (asm_out_file, list_head->ll_symbol); /* Walk the location list, and output each range + expression. */ @@ -10829,7 +8164,7 @@ output_loc_list (dw_loc_list_ref list_head) { unsigned long size; /* Don't output an entry that starts and ends at the same address. */ - if (strcmp (curr->begin, curr->end) == 0) + if (strcmp (curr->begin, curr->end) == 0 && !curr->force) continue; if (!have_multiple_function_sections) { @@ -10855,7 +8190,7 @@ output_loc_list (dw_loc_list_ref list_head) gcc_assert (size <= 0xffff); dw2_asm_output_data (2, size, "%s", "Location expression size"); - output_loc_sequence (curr->expr); + output_loc_sequence (curr->expr, -1); } dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, @@ -10890,14 +8225,14 @@ output_die (dw_die_ref die) /* If someone in another CU might refer to us, set up a symbol for them to point to. */ - if (dwarf_version < 4 && die->die_id.die_symbol) + if (! use_debug_types && die->die_id.die_symbol) output_die_symbol (die); dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (%#lx) %s)", (unsigned long)die->die_offset, dwarf_tag_name (die->die_tag)); - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) { const char *name = dwarf_attr_name (a->dw_attr); @@ -10933,7 +8268,7 @@ output_die (dw_die_ref die) else dw2_asm_output_data (constant_size (size), size, "%s", name); - output_loc_sequence (AT_loc (a)); + output_loc_sequence (AT_loc (a), -1); break; case dw_val_class_const: @@ -10944,8 +8279,15 @@ output_die (dw_die_ref die) break; case dw_val_class_unsigned_const: - dw2_asm_output_data (constant_size (AT_unsigned (a)), - AT_unsigned (a), "%s", name); + { + int csize = constant_size (AT_unsigned (a)); + if (dwarf_version == 3 + && a->dw_attr == DW_AT_data_member_location + && csize >= 4) + dw2_asm_output_data_uleb128 (AT_unsigned (a), "%s", name); + else + dw2_asm_output_data (csize, AT_unsigned (a), "%s", name); + } break; case dw_val_class_const_double: @@ -11029,7 +8371,7 @@ output_die (dw_die_ref die) case dw_val_class_die_ref: if (AT_ref_external (a)) { - if (dwarf_version >= 4) + if (use_debug_types) { comdat_type_node_ref type_node = AT_ref (a)->die_id.die_type_node; @@ -11192,7 +8534,11 @@ output_comp_unit (dw_die_ref die, int output_if_empty) switch_to_section (get_section (secname, SECTION_DEBUG, NULL)); } else - switch_to_section (debug_info_section); + { + switch_to_section (debug_info_section); + ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label); + info_section_emitted = true; + } /* Output debugging information. */ output_compilation_unit_header (); @@ -11305,7 +8651,7 @@ add_pubtype (tree decl, dw_die_ref die) e.name = NULL; if ((TREE_PUBLIC (decl) - || die->die_parent == comp_unit_die) + || is_cu_die (die->die_parent)) && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl))) { e.die = die; @@ -11363,7 +8709,7 @@ output_pubnames (VEC (pubname_entry, gc) * names) dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset, "Compilation Unit Length"); - for (i = 0; VEC_iterate (pubname_entry, names, i, pub); i++) + FOR_EACH_VEC_ELT (pubname_entry, names, i, pub) { /* We shouldn't see pubnames for DIEs outside of the main CU. */ if (names == pubname_table) @@ -11383,35 +8729,14 @@ output_pubnames (VEC (pubname_entry, gc) * names) dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL); } -/* Add a new entry to .debug_aranges if appropriate. */ - -static void -add_arange (tree decl, dw_die_ref die) -{ - if (! DECL_SECTION_NAME (decl)) - return; - - if (arange_table_in_use == arange_table_allocated) - { - arange_table_allocated += ARANGE_TABLE_INCREMENT; - arange_table = GGC_RESIZEVEC (dw_die_ref, arange_table, - arange_table_allocated); - memset (arange_table + arange_table_in_use, 0, - ARANGE_TABLE_INCREMENT * sizeof (dw_die_ref)); - } - - arange_table[arange_table_in_use++] = die; -} - /* Output the information that goes into the .debug_aranges table. Namely, define the beginning and ending address range of the text section generated for this compilation unit. */ static void -output_aranges (void) +output_aranges (unsigned long aranges_length) { unsigned i; - unsigned long aranges_length = size_of_aranges (); if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) dw2_asm_output_data (4, 0xffffffff, @@ -11456,38 +8781,27 @@ output_aranges (void) cold_text_section_label, "Length"); } - for (i = 0; i < arange_table_in_use; i++) + if (have_multiple_function_sections) { - dw_die_ref die = arange_table[i]; - - /* We shouldn't see aranges for DIEs outside of the main CU. */ - gcc_assert (die->die_mark); + unsigned fde_idx; + dw_fde_ref fde; - if (die->die_tag == DW_TAG_subprogram) + FOR_EACH_VEC_ELT (dw_fde_ref, fde_vec, fde_idx, fde) { - dw2_asm_output_addr (DWARF2_ADDR_SIZE, get_AT_low_pc (die), - "Address"); - dw2_asm_output_delta (DWARF2_ADDR_SIZE, get_AT_hi_pc (die), - get_AT_low_pc (die), "Length"); - } - else - { - /* A static variable; extract the symbol from DW_AT_location. - Note that this code isn't currently hit, as we only emit - aranges for functions (jason 9/23/99). */ - dw_attr_ref a = get_AT (die, DW_AT_location); - dw_loc_descr_ref loc; - - gcc_assert (a && AT_class (a) == dw_val_class_loc); - - loc = AT_loc (a); - gcc_assert (loc->dw_loc_opc == DW_OP_addr); - - dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, - loc->dw_loc_oprnd1.v.val_addr, "Address"); - dw2_asm_output_data (DWARF2_ADDR_SIZE, - get_AT_unsigned (die, DW_AT_byte_size), - "Length"); + if (!fde->in_std_section) + { + dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin, + "Address"); + dw2_asm_output_delta (DWARF2_ADDR_SIZE, fde->dw_fde_end, + fde->dw_fde_begin, "Length"); + } + if (fde->dw_fde_second_begin && !fde->second_in_std_section) + { + dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_second_begin, + "Address"); + dw2_asm_output_delta (DWARF2_ADDR_SIZE, fde->dw_fde_second_end, + fde->dw_fde_second_begin, "Length"); + } } } @@ -11948,35 +9262,146 @@ output_file_names (void) /* Include directory index. */ dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL); - /* Modification time. */ - dw2_asm_output_data_uleb128 - ((vms_file_stats_name (files[file_idx].path, &cdt, 0, 0, 0) == 0) - ? cdt : 0, - NULL); + /* Modification time. */ + dw2_asm_output_data_uleb128 + ((vms_file_stats_name (files[file_idx].path, &cdt, 0, 0, 0) == 0) + ? cdt : 0, + NULL); + + /* File length in bytes. */ + dw2_asm_output_data_uleb128 + ((vms_file_stats_name (files[file_idx].path, 0, &siz, 0, 0) == 0) + ? siz : 0, + NULL); +#else + dw2_asm_output_nstring (files[file_idx].path + dirs[dir_idx].length, -1, + "File Entry: %#x", (unsigned) i + 1); + + /* Include directory index. */ + dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL); + + /* Modification time. */ + dw2_asm_output_data_uleb128 (0, NULL); + + /* File length in bytes. */ + dw2_asm_output_data_uleb128 (0, NULL); +#endif /* VMS_DEBUGGING_INFO */ + } + + dw2_asm_output_data (1, 0, "End file name table"); +} + + +/* Output one line number table into the .debug_line section. */ + +static void +output_one_line_info_table (dw_line_info_table *table) +{ + char line_label[MAX_ARTIFICIAL_LABEL_BYTES]; + unsigned int current_line = 1; + bool current_is_stmt = DWARF_LINE_DEFAULT_IS_STMT_START; + dw_line_info_entry *ent; + size_t i; + + FOR_EACH_VEC_ELT (dw_line_info_entry, table->entries, i, ent) + { + switch (ent->opcode) + { + case LI_set_address: + /* ??? Unfortunately, we have little choice here currently, and + must always use the most general form. GCC does not know the + address delta itself, so we can't use DW_LNS_advance_pc. Many + ports do have length attributes which will give an upper bound + on the address range. We could perhaps use length attributes + to determine when it is safe to use DW_LNS_fixed_advance_pc. */ + ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, ent->val); + + /* This can handle any delta. This takes + 4+DWARF2_ADDR_SIZE bytes. */ + dw2_asm_output_data (1, 0, "set address %s", line_label); + dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); + dw2_asm_output_data (1, DW_LNE_set_address, NULL); + dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); + break; + + case LI_set_line: + if (ent->val == current_line) + { + /* We still need to start a new row, so output a copy insn. */ + dw2_asm_output_data (1, DW_LNS_copy, + "copy line %u", current_line); + } + else + { + int line_offset = ent->val - current_line; + int line_delta = line_offset - DWARF_LINE_BASE; + + current_line = ent->val; + if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) + { + /* This can handle deltas from -10 to 234, using the current + definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. + This takes 1 byte. */ + dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta, + "line %u", current_line); + } + else + { + /* This can handle any delta. This takes at least 4 bytes, + depending on the value being encoded. */ + dw2_asm_output_data (1, DW_LNS_advance_line, + "advance to line %u", current_line); + dw2_asm_output_data_sleb128 (line_offset, NULL); + dw2_asm_output_data (1, DW_LNS_copy, NULL); + } + } + break; + + case LI_set_file: + dw2_asm_output_data (1, DW_LNS_set_file, "set file %u", ent->val); + dw2_asm_output_data_uleb128 (ent->val, "%u", ent->val); + break; - /* File length in bytes. */ - dw2_asm_output_data_uleb128 - ((vms_file_stats_name (files[file_idx].path, 0, &siz, 0, 0) == 0) - ? siz : 0, - NULL); -#else - dw2_asm_output_nstring (files[file_idx].path + dirs[dir_idx].length, -1, - "File Entry: %#x", (unsigned) i + 1); + case LI_set_column: + dw2_asm_output_data (1, DW_LNS_set_column, "column %u", ent->val); + dw2_asm_output_data_uleb128 (ent->val, "%u", ent->val); + break; - /* Include directory index. */ - dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL); + case LI_negate_stmt: + current_is_stmt = !current_is_stmt; + dw2_asm_output_data (1, DW_LNS_negate_stmt, + "is_stmt %d", current_is_stmt); + break; - /* Modification time. */ - dw2_asm_output_data_uleb128 (0, NULL); + case LI_set_prologue_end: + dw2_asm_output_data (1, DW_LNS_set_prologue_end, + "set prologue end"); + break; + + case LI_set_epilogue_begin: + dw2_asm_output_data (1, DW_LNS_set_epilogue_begin, + "set epilogue begin"); + break; - /* File length in bytes. */ - dw2_asm_output_data_uleb128 (0, NULL); -#endif /* VMS_DEBUGGING_INFO */ + case LI_set_discriminator: + dw2_asm_output_data (1, 0, "discriminator %u", ent->val); + dw2_asm_output_data_uleb128 (1 + size_of_uleb128 (ent->val), NULL); + dw2_asm_output_data (1, DW_LNE_set_discriminator, NULL); + dw2_asm_output_data_uleb128 (ent->val, NULL); + break; + } } - dw2_asm_output_data (1, 0, "End file name table"); -} + /* Emit debug info for the address of the end of the table. */ + dw2_asm_output_data (1, 0, "set address %s", table->end_label); + dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); + dw2_asm_output_data (1, DW_LNE_set_address, NULL); + dw2_asm_output_addr (DWARF2_ADDR_SIZE, table->end_label, NULL); + dw2_asm_output_data (1, 0, "end sequence"); + dw2_asm_output_data_uleb128 (1, NULL); + dw2_asm_output_data (1, DW_LNE_end_sequence, NULL); +} /* Output the source line number correspondence information. This information goes into the .debug_line section. */ @@ -11985,17 +9410,9 @@ static void output_line_info (void) { char l1[20], l2[20], p1[20], p2[20]; - char line_label[MAX_ARTIFICIAL_LABEL_BYTES]; - char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES]; - unsigned opc; - unsigned n_op_args; - unsigned long lt_index; - unsigned long current_line; - long line_offset; - long line_delta; - unsigned long current_file; - unsigned long function; int ver = dwarf_version; + bool saw_one = false; + int opc; ASM_GENERATE_INTERNAL_LABEL (l1, LINE_NUMBER_BEGIN_LABEL, 0); ASM_GENERATE_INTERNAL_LABEL (l2, LINE_NUMBER_END_LABEL, 0); @@ -12013,16 +9430,15 @@ output_line_info (void) dw2_asm_output_delta (DWARF_OFFSET_SIZE, p2, p1, "Prolog Length"); ASM_OUTPUT_LABEL (asm_out_file, p1); - /* Define the architecture-dependent minimum instruction length (in - bytes). In this implementation of DWARF, this field is used for - information purposes only. Since GCC generates assembly language, - we have no a priori knowledge of how many instruction bytes are - generated for each source line, and therefore can use only the - DW_LNE_set_address and DW_LNS_fixed_advance_pc line information - commands. Accordingly, we fix this as `1', which is "correct - enough" for all architectures, and don't let the target override. */ - dw2_asm_output_data (1, 1, - "Minimum Instruction Length"); + /* Define the architecture-dependent minimum instruction length (in bytes). + In this implementation of DWARF, this field is used for information + purposes only. Since GCC generates assembly language, we have no + a priori knowledge of how many instruction bytes are generated for each + source line, and therefore can use only the DW_LNE_set_address and + DW_LNS_fixed_advance_pc line information commands. Accordingly, we fix + this as '1', which is "correct enough" for all architectures, + and don't let the target override. */ + dw2_asm_output_data (1, 1, "Minimum Instruction Length"); if (ver >= 4) dw2_asm_output_data (1, DWARF_LINE_DEFAULT_MAX_OPS_PER_INSN, @@ -12038,6 +9454,7 @@ output_line_info (void) for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; opc++) { + int n_op_args; switch (opc) { case DW_LNS_advance_pc: @@ -12045,6 +9462,7 @@ output_line_info (void) case DW_LNS_set_file: case DW_LNS_set_column: case DW_LNS_fixed_advance_pc: + case DW_LNS_set_isa: n_op_args = 1; break; default: @@ -12060,1409 +9478,2008 @@ output_line_info (void) output_file_names (); ASM_OUTPUT_LABEL (asm_out_file, p2); - /* We used to set the address register to the first location in the text - section here, but that didn't accomplish anything since we already - have a line note for the opening brace of the first function. */ + if (separate_line_info) + { + dw_line_info_table *table; + size_t i; + + FOR_EACH_VEC_ELT (dw_line_info_table_p, separate_line_info, i, table) + if (table->in_use) + { + output_one_line_info_table (table); + saw_one = true; + } + } + if (cold_text_section_line_info && cold_text_section_line_info->in_use) + { + output_one_line_info_table (cold_text_section_line_info); + saw_one = true; + } + + /* ??? Some Darwin linkers crash on a .debug_line section with no + sequences. Further, merely a DW_LNE_end_sequence entry is not + sufficient -- the address column must also be initialized. + Make sure to output at least one set_address/end_sequence pair, + choosing .text since that section is always present. */ + if (text_section_line_info->in_use || !saw_one) + output_one_line_info_table (text_section_line_info); + + /* Output the marker for the end of the line number info. */ + ASM_OUTPUT_LABEL (asm_out_file, l2); +} + +/* Given a pointer to a tree node for some base type, return a pointer to + a DIE that describes the given type. + + This routine must only be called for GCC type nodes that correspond to + Dwarf base (fundamental) types. */ + +static dw_die_ref +base_type_die (tree type) +{ + dw_die_ref base_type_result; + enum dwarf_type encoding; + + if (TREE_CODE (type) == ERROR_MARK || TREE_CODE (type) == VOID_TYPE) + return 0; + + /* If this is a subtype that should not be emitted as a subrange type, + use the base type. See subrange_type_for_debug_p. */ + if (TREE_CODE (type) == INTEGER_TYPE && TREE_TYPE (type) != NULL_TREE) + type = TREE_TYPE (type); + + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + if ((dwarf_version >= 4 || !dwarf_strict) + && TYPE_NAME (type) + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && DECL_IS_BUILTIN (TYPE_NAME (type)) + && DECL_NAME (TYPE_NAME (type))) + { + const char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + if (strcmp (name, "char16_t") == 0 + || strcmp (name, "char32_t") == 0) + { + encoding = DW_ATE_UTF; + break; + } + } + if (TYPE_STRING_FLAG (type)) + { + if (TYPE_UNSIGNED (type)) + encoding = DW_ATE_unsigned_char; + else + encoding = DW_ATE_signed_char; + } + else if (TYPE_UNSIGNED (type)) + encoding = DW_ATE_unsigned; + else + encoding = DW_ATE_signed; + break; + + case REAL_TYPE: + if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type))) + { + if (dwarf_version >= 3 || !dwarf_strict) + encoding = DW_ATE_decimal_float; + else + encoding = DW_ATE_lo_user; + } + else + encoding = DW_ATE_float; + break; + + case FIXED_POINT_TYPE: + if (!(dwarf_version >= 3 || !dwarf_strict)) + encoding = DW_ATE_lo_user; + else if (TYPE_UNSIGNED (type)) + encoding = DW_ATE_unsigned_fixed; + else + encoding = DW_ATE_signed_fixed; + break; + + /* Dwarf2 doesn't know anything about complex ints, so use + a user defined type for it. */ + case COMPLEX_TYPE: + if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE) + encoding = DW_ATE_complex_float; + else + encoding = DW_ATE_lo_user; + break; + + case BOOLEAN_TYPE: + /* GNU FORTRAN/Ada/C++ BOOLEAN type. */ + encoding = DW_ATE_boolean; + break; + + default: + /* No other TREE_CODEs are Dwarf fundamental types. */ + gcc_unreachable (); + } + + base_type_result = new_die (DW_TAG_base_type, comp_unit_die (), type); + + add_AT_unsigned (base_type_result, DW_AT_byte_size, + int_size_in_bytes (type)); + add_AT_unsigned (base_type_result, DW_AT_encoding, encoding); + + return base_type_result; +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return nonzero if the + given input type is a Dwarf "fundamental" type. Otherwise return null. */ + +static inline int +is_base_type (tree type) +{ + switch (TREE_CODE (type)) + { + case ERROR_MARK: + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case COMPLEX_TYPE: + case BOOLEAN_TYPE: + return 1; + + case ARRAY_TYPE: + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case ENUMERAL_TYPE: + case FUNCTION_TYPE: + case METHOD_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + case NULLPTR_TYPE: + case OFFSET_TYPE: + case LANG_TYPE: + case VECTOR_TYPE: + return 0; + + default: + gcc_unreachable (); + } + + return 0; +} + +/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE + node, return the size in bits for the type if it is a constant, or else + return the alignment for the type if the type's size is not constant, or + else return BITS_PER_WORD if the type actually turns out to be an + ERROR_MARK node. */ + +static inline unsigned HOST_WIDE_INT +simple_type_size_in_bits (const_tree type) +{ + if (TREE_CODE (type) == ERROR_MARK) + return BITS_PER_WORD; + else if (TYPE_SIZE (type) == NULL_TREE) + return 0; + else if (host_integerp (TYPE_SIZE (type), 1)) + return tree_low_cst (TYPE_SIZE (type), 1); + else + return TYPE_ALIGN (type); +} + +/* Similarly, but return a double_int instead of UHWI. */ + +static inline double_int +double_int_type_size_in_bits (const_tree type) +{ + if (TREE_CODE (type) == ERROR_MARK) + return uhwi_to_double_int (BITS_PER_WORD); + else if (TYPE_SIZE (type) == NULL_TREE) + return double_int_zero; + else if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) + return tree_to_double_int (TYPE_SIZE (type)); + else + return uhwi_to_double_int (TYPE_ALIGN (type)); +} + +/* Given a pointer to a tree node for a subrange type, return a pointer + to a DIE that describes the given type. */ + +static dw_die_ref +subrange_type_die (tree type, tree low, tree high, dw_die_ref context_die) +{ + dw_die_ref subrange_die; + const HOST_WIDE_INT size_in_bytes = int_size_in_bytes (type); + + if (context_die == NULL) + context_die = comp_unit_die (); + + subrange_die = new_die (DW_TAG_subrange_type, context_die, type); + + if (int_size_in_bytes (TREE_TYPE (type)) != size_in_bytes) + { + /* The size of the subrange type and its base type do not match, + so we need to generate a size attribute for the subrange type. */ + add_AT_unsigned (subrange_die, DW_AT_byte_size, size_in_bytes); + } + + if (low) + add_bound_info (subrange_die, DW_AT_lower_bound, low); + if (high) + add_bound_info (subrange_die, DW_AT_upper_bound, high); + + return subrange_die; +} + +/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging + entry that chains various modifiers in front of the given type. */ + +static dw_die_ref +modified_type_die (tree type, int is_const_type, int is_volatile_type, + dw_die_ref context_die) +{ + enum tree_code code = TREE_CODE (type); + dw_die_ref mod_type_die; + dw_die_ref sub_die = NULL; + tree item_type = NULL; + tree qualified_type; + tree name, low, high; + + if (code == ERROR_MARK) + return NULL; - /* Generate the line number to PC correspondence table, encoded as - a series of state machine operations. */ - current_file = 1; - current_line = 1; + /* See if we already have the appropriately qualified variant of + this type. */ + qualified_type + = get_qualified_type (type, + ((is_const_type ? TYPE_QUAL_CONST : 0) + | (is_volatile_type ? TYPE_QUAL_VOLATILE : 0))); - if (cfun && in_cold_section_p) - strcpy (prev_line_label, crtl->subsections.cold_section_label); - else - strcpy (prev_line_label, text_section_label); - for (lt_index = 1; lt_index < line_info_table_in_use; ++lt_index) + if (qualified_type == sizetype + && TYPE_NAME (qualified_type) + && TREE_CODE (TYPE_NAME (qualified_type)) == TYPE_DECL) { - dw_line_info_ref line_info = &line_info_table[lt_index]; - -#if 0 - /* Disable this optimization for now; GDB wants to see two line notes - at the beginning of a function so it can find the end of the - prologue. */ - - /* Don't emit anything for redundant notes. Just updating the - address doesn't accomplish anything, because we already assume - that anything after the last address is this line. */ - if (line_info->dw_line_num == current_line - && line_info->dw_file_num == current_file) - continue; -#endif - - /* Emit debug info for the address of the current line. + tree t = TREE_TYPE (TYPE_NAME (qualified_type)); - Unfortunately, we have little choice here currently, and must always - use the most general form. GCC does not know the address delta - itself, so we can't use DW_LNS_advance_pc. Many ports do have length - attributes which will give an upper bound on the address range. We - could perhaps use length attributes to determine when it is safe to - use DW_LNS_fixed_advance_pc. */ + gcc_checking_assert (TREE_CODE (t) == INTEGER_TYPE + && TYPE_PRECISION (t) + == TYPE_PRECISION (qualified_type) + && TYPE_UNSIGNED (t) + == TYPE_UNSIGNED (qualified_type)); + qualified_type = t; + } - ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index); - if (0) - { - /* This can handle deltas up to 0xffff. This takes 3 bytes. */ - dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, - "DW_LNS_fixed_advance_pc"); - dw2_asm_output_delta (2, line_label, prev_line_label, NULL); - } - else - { - /* This can handle any delta. This takes - 4+DWARF2_ADDR_SIZE bytes. */ - dw2_asm_output_data (1, 0, "DW_LNE_set_address"); - dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); - dw2_asm_output_data (1, DW_LNE_set_address, NULL); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); - } + /* If we do, then we can just use its DIE, if it exists. */ + if (qualified_type) + { + mod_type_die = lookup_type_die (qualified_type); + if (mod_type_die) + return mod_type_die; + } - strcpy (prev_line_label, line_label); + name = qualified_type ? TYPE_NAME (qualified_type) : NULL; - /* Emit debug info for the source file of the current line, if - different from the previous line. */ - if (line_info->dw_file_num != current_file) - { - current_file = line_info->dw_file_num; - dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file"); - dw2_asm_output_data_uleb128 (current_file, "%lu", current_file); - } + /* Handle C typedef types. */ + if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name) + && !DECL_ARTIFICIAL (name)) + { + tree dtype = TREE_TYPE (name); - /* Emit debug info for the current line number, choosing the encoding - that uses the least amount of space. */ - if (line_info->dw_line_num != current_line) + if (qualified_type == dtype) { - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - /* This can handle deltas from -10 to 234, using the current - definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This - takes 1 byte. */ - dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta, - "line %lu", current_line); - else - { - /* This can handle any delta. This takes at least 4 bytes, - depending on the value being encoded. */ - dw2_asm_output_data (1, DW_LNS_advance_line, - "advance to line %lu", current_line); - dw2_asm_output_data_sleb128 (line_offset, NULL); - dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); - } + /* For a named type, use the typedef. */ + gen_type_die (qualified_type, context_die); + return lookup_type_die (qualified_type); } - else - /* We still need to start a new row, so output a copy insn. */ - dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); + else if (is_const_type < TYPE_READONLY (dtype) + || is_volatile_type < TYPE_VOLATILE (dtype) + || (is_const_type <= TYPE_READONLY (dtype) + && is_volatile_type <= TYPE_VOLATILE (dtype) + && DECL_ORIGINAL_TYPE (name) != type)) + /* cv-unqualified version of named type. Just use the unnamed + type to which it refers. */ + return modified_type_die (DECL_ORIGINAL_TYPE (name), + is_const_type, is_volatile_type, + context_die); + /* Else cv-qualified version of named type; fall through. */ } - /* Emit debug info for the address of the end of the function. */ - if (0) + if (is_const_type + /* If both is_const_type and is_volatile_type, prefer the path + which leads to a qualified type. */ + && (!is_volatile_type + || get_qualified_type (type, TYPE_QUAL_CONST) == NULL_TREE + || get_qualified_type (type, TYPE_QUAL_VOLATILE) != NULL_TREE)) { - dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, - "DW_LNS_fixed_advance_pc"); - dw2_asm_output_delta (2, text_end_label, prev_line_label, NULL); + mod_type_die = new_die (DW_TAG_const_type, comp_unit_die (), type); + sub_die = modified_type_die (type, 0, is_volatile_type, context_die); } - else + else if (is_volatile_type) { - dw2_asm_output_data (1, 0, "DW_LNE_set_address"); - dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); - dw2_asm_output_data (1, DW_LNE_set_address, NULL); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_end_label, NULL); + mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die (), type); + sub_die = modified_type_die (type, is_const_type, 0, context_die); } - - dw2_asm_output_data (1, 0, "DW_LNE_end_sequence"); - dw2_asm_output_data_uleb128 (1, NULL); - dw2_asm_output_data (1, DW_LNE_end_sequence, NULL); - - function = 0; - current_file = 1; - current_line = 1; - for (lt_index = 0; lt_index < separate_line_info_table_in_use;) + else if (code == POINTER_TYPE) { - dw_separate_line_info_ref line_info - = &separate_line_info_table[lt_index]; - -#if 0 - /* Don't emit anything for redundant notes. */ - if (line_info->dw_line_num == current_line - && line_info->dw_file_num == current_file - && line_info->function == function) - goto cont; -#endif - - /* Emit debug info for the address of the current line. If this is - a new function, or the first line of a function, then we need - to handle it differently. */ - ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL, - lt_index); - if (function != line_info->function) - { - function = line_info->function; - - /* Set the address register to the first line in the function. */ - dw2_asm_output_data (1, 0, "DW_LNE_set_address"); - dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); - dw2_asm_output_data (1, DW_LNE_set_address, NULL); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); - } + mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die (), type); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, + simple_type_size_in_bits (type) / BITS_PER_UNIT); + item_type = TREE_TYPE (type); + if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (item_type))) + add_AT_unsigned (mod_type_die, DW_AT_address_class, + TYPE_ADDR_SPACE (item_type)); + } + else if (code == REFERENCE_TYPE) + { + if (TYPE_REF_IS_RVALUE (type) && dwarf_version >= 4) + mod_type_die = new_die (DW_TAG_rvalue_reference_type, comp_unit_die (), + type); else - { - /* ??? See the DW_LNS_advance_pc comment above. */ - if (0) - { - dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, - "DW_LNS_fixed_advance_pc"); - dw2_asm_output_delta (2, line_label, prev_line_label, NULL); - } - else - { - dw2_asm_output_data (1, 0, "DW_LNE_set_address"); - dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); - dw2_asm_output_data (1, DW_LNE_set_address, NULL); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); - } - } - - strcpy (prev_line_label, line_label); - - /* Emit debug info for the source file of the current line, if - different from the previous line. */ - if (line_info->dw_file_num != current_file) - { - current_file = line_info->dw_file_num; - dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file"); - dw2_asm_output_data_uleb128 (current_file, "%lu", current_file); - } + mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die (), type); + add_AT_unsigned (mod_type_die, DW_AT_byte_size, + simple_type_size_in_bits (type) / BITS_PER_UNIT); + item_type = TREE_TYPE (type); + if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (item_type))) + add_AT_unsigned (mod_type_die, DW_AT_address_class, + TYPE_ADDR_SPACE (item_type)); + } + else if (code == INTEGER_TYPE + && TREE_TYPE (type) != NULL_TREE + && subrange_type_for_debug_p (type, &low, &high)) + { + mod_type_die = subrange_type_die (type, low, high, context_die); + item_type = TREE_TYPE (type); + } + else if (is_base_type (type)) + mod_type_die = base_type_die (type); + else + { + gen_type_die (type, context_die); - /* Emit debug info for the current line number, choosing the encoding - that uses the least amount of space. */ - if (line_info->dw_line_num != current_line) - { - line_offset = line_info->dw_line_num - current_line; - line_delta = line_offset - DWARF_LINE_BASE; - current_line = line_info->dw_line_num; - if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1)) - dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta, - "line %lu", current_line); - else - { - dw2_asm_output_data (1, DW_LNS_advance_line, - "advance to line %lu", current_line); - dw2_asm_output_data_sleb128 (line_offset, NULL); - dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); - } - } + /* We have to get the type_main_variant here (and pass that to the + `lookup_type_die' routine) because the ..._TYPE node we have + might simply be a *copy* of some original type node (where the + copy was created to help us keep track of typedef names) and + that copy might have a different TYPE_UID from the original + ..._TYPE node. */ + if (TREE_CODE (type) != VECTOR_TYPE) + return lookup_type_die (type_main_variant (type)); else - dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy"); - -#if 0 - cont: -#endif - - lt_index++; - - /* If we're done with a function, end its sequence. */ - if (lt_index == separate_line_info_table_in_use - || separate_line_info_table[lt_index].function != function) - { - current_file = 1; - current_line = 1; - - /* Emit debug info for the address of the end of the function. */ - ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function); - if (0) - { - dw2_asm_output_data (1, DW_LNS_fixed_advance_pc, - "DW_LNS_fixed_advance_pc"); - dw2_asm_output_delta (2, line_label, prev_line_label, NULL); - } - else - { - dw2_asm_output_data (1, 0, "DW_LNE_set_address"); - dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); - dw2_asm_output_data (1, DW_LNE_set_address, NULL); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL); - } - - /* Output the marker for the end of this sequence. */ - dw2_asm_output_data (1, 0, "DW_LNE_end_sequence"); - dw2_asm_output_data_uleb128 (1, NULL); - dw2_asm_output_data (1, DW_LNE_end_sequence, NULL); - } + /* Vectors have the debugging information in the type, + not the main variant. */ + return lookup_type_die (type); } - /* Output the marker for the end of the line number info. */ - ASM_OUTPUT_LABEL (asm_out_file, l2); -} - -/* Return the size of the .debug_dcall table for the compilation unit. */ - -static unsigned long -size_of_dcall_table (void) -{ - unsigned long size; - unsigned int i; - dcall_entry *p; - tree last_poc_decl = NULL; - - /* Header: version + debug info section pointer + pointer size. */ - size = 2 + DWARF_OFFSET_SIZE + 1; - - /* Each entry: code label + DIE offset. */ - for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, p); i++) + /* Builtin types don't have a DECL_ORIGINAL_TYPE. For those, + don't output a DW_TAG_typedef, since there isn't one in the + user's program; just attach a DW_AT_name to the type. + Don't attach a DW_AT_name to DW_TAG_const_type or DW_TAG_volatile_type + if the base type already has the same name. */ + if (name + && ((TREE_CODE (name) != TYPE_DECL + && (qualified_type == TYPE_MAIN_VARIANT (type) + || (!is_const_type && !is_volatile_type))) + || (TREE_CODE (name) == TYPE_DECL + && TREE_TYPE (name) == qualified_type + && DECL_NAME (name)))) { - gcc_assert (p->targ_die != NULL); - /* Insert a "from" entry when the point-of-call DIE offset changes. */ - if (p->poc_decl != last_poc_decl) - { - dw_die_ref poc_die = lookup_decl_die (p->poc_decl); - gcc_assert (poc_die); - last_poc_decl = p->poc_decl; - if (poc_die) - size += (DWARF_OFFSET_SIZE - + size_of_uleb128 (poc_die->die_offset)); - } - size += DWARF_OFFSET_SIZE + size_of_uleb128 (p->targ_die->die_offset); + if (TREE_CODE (name) == TYPE_DECL) + /* Could just call add_name_and_src_coords_attributes here, + but since this is a builtin type it doesn't have any + useful source coordinates anyway. */ + name = DECL_NAME (name); + add_name_attribute (mod_type_die, IDENTIFIER_POINTER (name)); + add_gnat_descriptive_type_attribute (mod_type_die, type, context_die); + if (TYPE_ARTIFICIAL (type)) + add_AT_flag (mod_type_die, DW_AT_artificial, 1); } - - return size; -} - -/* Output the direct call table used to disambiguate PC values when - identical function have been merged. */ - -static void -output_dcall_table (void) -{ - unsigned i; - unsigned long dcall_length = size_of_dcall_table (); - dcall_entry *p; - char poc_label[MAX_ARTIFICIAL_LABEL_BYTES]; - tree last_poc_decl = NULL; - - if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) - dw2_asm_output_data (4, 0xffffffff, - "Initial length escape value indicating 64-bit DWARF extension"); - dw2_asm_output_data (DWARF_OFFSET_SIZE, dcall_length, - "Length of Direct Call Table"); - dw2_asm_output_data (2, 4, "Version number"); - dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label, - debug_info_section, - "Offset of Compilation Unit Info"); - dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)"); - - for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, p); i++) + /* This probably indicates a bug. */ + else if (mod_type_die && mod_type_die->die_tag == DW_TAG_base_type) { - /* Insert a "from" entry when the point-of-call DIE offset changes. */ - if (p->poc_decl != last_poc_decl) - { - dw_die_ref poc_die = lookup_decl_die (p->poc_decl); - last_poc_decl = p->poc_decl; - if (poc_die) - { - dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, "New caller"); - dw2_asm_output_data_uleb128 (poc_die->die_offset, - "Caller DIE offset"); - } - } - ASM_GENERATE_INTERNAL_LABEL (poc_label, "LPOC", p->poc_label_num); - dw2_asm_output_addr (DWARF_OFFSET_SIZE, poc_label, "Point of call"); - dw2_asm_output_data_uleb128 (p->targ_die->die_offset, - "Callee DIE offset"); + name = TYPE_NAME (type); + if (name + && TREE_CODE (name) == TYPE_DECL) + name = DECL_NAME (name); + add_name_attribute (mod_type_die, + name ? IDENTIFIER_POINTER (name) : "__unknown__"); } -} - -/* Return the size of the .debug_vcall table for the compilation unit. */ -static unsigned long -size_of_vcall_table (void) -{ - unsigned long size; - unsigned int i; - vcall_entry *p; + if (qualified_type) + equate_type_number_to_die (qualified_type, mod_type_die); - /* Header: version + pointer size. */ - size = 2 + 1; + if (item_type) + /* We must do this after the equate_type_number_to_die call, in case + this is a recursive type. This ensures that the modified_type_die + recursion will terminate even if the type is recursive. Recursive + types are possible in Ada. */ + sub_die = modified_type_die (item_type, + TYPE_READONLY (item_type), + TYPE_VOLATILE (item_type), + context_die); - /* Each entry: code label + vtable slot index. */ - for (i = 0; VEC_iterate (vcall_entry, vcall_table, i, p); i++) - size += DWARF_OFFSET_SIZE + size_of_uleb128 (p->vtable_slot); + if (sub_die != NULL) + add_AT_die_ref (mod_type_die, DW_AT_type, sub_die); - return size; + return mod_type_die; } -/* Output the virtual call table used to disambiguate PC values when - identical function have been merged. */ +/* Generate DIEs for the generic parameters of T. + T must be either a generic type or a generic function. + See http://gcc.gnu.org/wiki/TemplateParmsDwarf for more. */ static void -output_vcall_table (void) +gen_generic_params_dies (tree t) { - unsigned i; - unsigned long vcall_length = size_of_vcall_table (); - vcall_entry *p; - char poc_label[MAX_ARTIFICIAL_LABEL_BYTES]; + tree parms, args; + int parms_num, i; + dw_die_ref die = NULL; - if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4) - dw2_asm_output_data (4, 0xffffffff, - "Initial length escape value indicating 64-bit DWARF extension"); - dw2_asm_output_data (DWARF_OFFSET_SIZE, vcall_length, - "Length of Virtual Call Table"); - dw2_asm_output_data (2, 4, "Version number"); - dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)"); + if (!t || (TYPE_P (t) && !COMPLETE_TYPE_P (t))) + return; + + if (TYPE_P (t)) + die = lookup_type_die (t); + else if (DECL_P (t)) + die = lookup_decl_die (t); + + gcc_assert (die); + + parms = lang_hooks.get_innermost_generic_parms (t); + if (!parms) + /* T has no generic parameter. It means T is neither a generic type + or function. End of story. */ + return; - for (i = 0; VEC_iterate (vcall_entry, vcall_table, i, p); i++) + parms_num = TREE_VEC_LENGTH (parms); + args = lang_hooks.get_innermost_generic_args (t); + for (i = 0; i < parms_num; i++) { - ASM_GENERATE_INTERNAL_LABEL (poc_label, "LPOC", p->poc_label_num); - dw2_asm_output_addr (DWARF_OFFSET_SIZE, poc_label, "Point of call"); - dw2_asm_output_data_uleb128 (p->vtable_slot, "Vtable slot"); + tree parm, arg, arg_pack_elems; + + parm = TREE_VEC_ELT (parms, i); + arg = TREE_VEC_ELT (args, i); + arg_pack_elems = lang_hooks.types.get_argument_pack_elems (arg); + gcc_assert (parm && TREE_VALUE (parm) && arg); + + if (parm && TREE_VALUE (parm) && arg) + { + /* If PARM represents a template parameter pack, + emit a DW_TAG_GNU_template_parameter_pack DIE, followed + by DW_TAG_template_*_parameter DIEs for the argument + pack elements of ARG. Note that ARG would then be + an argument pack. */ + if (arg_pack_elems) + template_parameter_pack_die (TREE_VALUE (parm), + arg_pack_elems, + die); + else + generic_parameter_die (TREE_VALUE (parm), arg, + true /* Emit DW_AT_name */, die); + } } } - -/* Given a pointer to a tree node for some base type, return a pointer to - a DIE that describes the given type. - This routine must only be called for GCC type nodes that correspond to - Dwarf base (fundamental) types. */ +/* Create and return a DIE for PARM which should be + the representation of a generic type parameter. + For instance, in the C++ front end, PARM would be a template parameter. + ARG is the argument to PARM. + EMIT_NAME_P if tree, the DIE will have DW_AT_name attribute set to the + name of the PARM. + PARENT_DIE is the parent DIE which the new created DIE should be added to, + as a child node. */ static dw_die_ref -base_type_die (tree type) +generic_parameter_die (tree parm, tree arg, + bool emit_name_p, + dw_die_ref parent_die) { - dw_die_ref base_type_result; - enum dwarf_type encoding; + dw_die_ref tmpl_die = NULL; + const char *name = NULL; - if (TREE_CODE (type) == ERROR_MARK || TREE_CODE (type) == VOID_TYPE) - return 0; + if (!parm || !DECL_NAME (parm) || !arg) + return NULL; - /* If this is a subtype that should not be emitted as a subrange type, - use the base type. See subrange_type_for_debug_p. */ - if (TREE_CODE (type) == INTEGER_TYPE && TREE_TYPE (type) != NULL_TREE) - type = TREE_TYPE (type); + /* We support non-type generic parameters and arguments, + type generic parameters and arguments, as well as + generic generic parameters (a.k.a. template template parameters in C++) + and arguments. */ + if (TREE_CODE (parm) == PARM_DECL) + /* PARM is a nontype generic parameter */ + tmpl_die = new_die (DW_TAG_template_value_param, parent_die, parm); + else if (TREE_CODE (parm) == TYPE_DECL) + /* PARM is a type generic parameter. */ + tmpl_die = new_die (DW_TAG_template_type_param, parent_die, parm); + else if (lang_hooks.decls.generic_generic_parameter_decl_p (parm)) + /* PARM is a generic generic parameter. + Its DIE is a GNU extension. It shall have a + DW_AT_name attribute to represent the name of the template template + parameter, and a DW_AT_GNU_template_name attribute to represent the + name of the template template argument. */ + tmpl_die = new_die (DW_TAG_GNU_template_template_param, + parent_die, parm); + else + gcc_unreachable (); - switch (TREE_CODE (type)) + if (tmpl_die) { - case INTEGER_TYPE: - if ((dwarf_version >= 4 || !dwarf_strict) - && TYPE_NAME (type) - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_IS_BUILTIN (TYPE_NAME (type)) - && DECL_NAME (TYPE_NAME (type))) - { - const char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - if (strcmp (name, "char16_t") == 0 - || strcmp (name, "char32_t") == 0) - { - encoding = DW_ATE_UTF; - break; - } - } - if (TYPE_STRING_FLAG (type)) + tree tmpl_type; + + /* If PARM is a generic parameter pack, it means we are + emitting debug info for a template argument pack element. + In other terms, ARG is a template argument pack element. + In that case, we don't emit any DW_AT_name attribute for + the die. */ + if (emit_name_p) { - if (TYPE_UNSIGNED (type)) - encoding = DW_ATE_unsigned_char; - else - encoding = DW_ATE_signed_char; + name = IDENTIFIER_POINTER (DECL_NAME (parm)); + gcc_assert (name); + add_AT_string (tmpl_die, DW_AT_name, name); } - else if (TYPE_UNSIGNED (type)) - encoding = DW_ATE_unsigned; - else - encoding = DW_ATE_signed; - break; - case REAL_TYPE: - if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type))) + if (!lang_hooks.decls.generic_generic_parameter_decl_p (parm)) { - if (dwarf_version >= 3 || !dwarf_strict) - encoding = DW_ATE_decimal_float; - else - encoding = DW_ATE_lo_user; + /* DWARF3, 5.6.8 says if PARM is a non-type generic parameter + TMPL_DIE should have a child DW_AT_type attribute that is set + to the type of the argument to PARM, which is ARG. + If PARM is a type generic parameter, TMPL_DIE should have a + child DW_AT_type that is set to ARG. */ + tmpl_type = TYPE_P (arg) ? arg : TREE_TYPE (arg); + add_type_attribute (tmpl_die, tmpl_type, 0, + TREE_THIS_VOLATILE (tmpl_type), + parent_die); } else - encoding = DW_ATE_float; - break; + { + /* So TMPL_DIE is a DIE representing a + a generic generic template parameter, a.k.a template template + parameter in C++ and arg is a template. */ - case FIXED_POINT_TYPE: - if (!(dwarf_version >= 3 || !dwarf_strict)) - encoding = DW_ATE_lo_user; - else if (TYPE_UNSIGNED (type)) - encoding = DW_ATE_unsigned_fixed; - else - encoding = DW_ATE_signed_fixed; - break; + /* The DW_AT_GNU_template_name attribute of the DIE must be set + to the name of the argument. */ + name = dwarf2_name (TYPE_P (arg) ? TYPE_NAME (arg) : arg, 1); + if (name) + add_AT_string (tmpl_die, DW_AT_GNU_template_name, name); + } - /* Dwarf2 doesn't know anything about complex ints, so use - a user defined type for it. */ - case COMPLEX_TYPE: - if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE) - encoding = DW_ATE_complex_float; - else - encoding = DW_ATE_lo_user; - break; + if (TREE_CODE (parm) == PARM_DECL) + /* So PARM is a non-type generic parameter. + DWARF3 5.6.8 says we must set a DW_AT_const_value child + attribute of TMPL_DIE which value represents the value + of ARG. + We must be careful here: + The value of ARG might reference some function decls. + We might currently be emitting debug info for a generic + type and types are emitted before function decls, we don't + know if the function decls referenced by ARG will actually be + emitted after cgraph computations. + So must defer the generation of the DW_AT_const_value to + after cgraph is ready. */ + append_entry_to_tmpl_value_parm_die_table (tmpl_die, arg); + } - case BOOLEAN_TYPE: - /* GNU FORTRAN/Ada/C++ BOOLEAN type. */ - encoding = DW_ATE_boolean; - break; + return tmpl_die; +} - default: - /* No other TREE_CODEs are Dwarf fundamental types. */ - gcc_unreachable (); - } +/* Generate and return a DW_TAG_GNU_template_parameter_pack DIE representing. + PARM_PACK must be a template parameter pack. The returned DIE + will be child DIE of PARENT_DIE. */ - base_type_result = new_die (DW_TAG_base_type, comp_unit_die, type); +static dw_die_ref +template_parameter_pack_die (tree parm_pack, + tree parm_pack_args, + dw_die_ref parent_die) +{ + dw_die_ref die; + int j; - add_AT_unsigned (base_type_result, DW_AT_byte_size, - int_size_in_bytes (type)); - add_AT_unsigned (base_type_result, DW_AT_encoding, encoding); + gcc_assert (parent_die && parm_pack); - return base_type_result; + die = new_die (DW_TAG_GNU_template_parameter_pack, parent_die, parm_pack); + add_name_and_src_coords_attributes (die, parm_pack); + for (j = 0; j < TREE_VEC_LENGTH (parm_pack_args); j++) + generic_parameter_die (parm_pack, + TREE_VEC_ELT (parm_pack_args, j), + false /* Don't emit DW_AT_name */, + die); + return die; } -/* Given a pointer to an arbitrary ..._TYPE tree node, return nonzero if the - given input type is a Dwarf "fundamental" type. Otherwise return null. */ +/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is + an enumerated type. */ static inline int -is_base_type (tree type) +type_is_enum (const_tree type) { - switch (TREE_CODE (type)) - { - case ERROR_MARK: - case VOID_TYPE: - case INTEGER_TYPE: - case REAL_TYPE: - case FIXED_POINT_TYPE: - case COMPLEX_TYPE: - case BOOLEAN_TYPE: - return 1; + return TREE_CODE (type) == ENUMERAL_TYPE; +} - case ARRAY_TYPE: - case RECORD_TYPE: - case UNION_TYPE: - case QUAL_UNION_TYPE: - case ENUMERAL_TYPE: - case FUNCTION_TYPE: - case METHOD_TYPE: - case POINTER_TYPE: - case REFERENCE_TYPE: - case OFFSET_TYPE: - case LANG_TYPE: - case VECTOR_TYPE: - return 0; +/* Return the DBX register number described by a given RTL node. */ - default: - gcc_unreachable (); +static unsigned int +dbx_reg_number (const_rtx rtl) +{ + unsigned regno = REGNO (rtl); + + gcc_assert (regno < FIRST_PSEUDO_REGISTER); + +#ifdef LEAF_REG_REMAP + if (current_function_uses_only_leaf_regs) + { + int leaf_reg = LEAF_REG_REMAP (regno); + if (leaf_reg != -1) + regno = (unsigned) leaf_reg; } +#endif - return 0; + return DBX_REGISTER_NUMBER (regno); } -/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE - node, return the size in bits for the type if it is a constant, or else - return the alignment for the type if the type's size is not constant, or - else return BITS_PER_WORD if the type actually turns out to be an - ERROR_MARK node. */ +/* Optionally add a DW_OP_piece term to a location description expression. + DW_OP_piece is only added if the location description expression already + doesn't end with DW_OP_piece. */ -static inline unsigned HOST_WIDE_INT -simple_type_size_in_bits (const_tree type) +static void +add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size) { - if (TREE_CODE (type) == ERROR_MARK) - return BITS_PER_WORD; - else if (TYPE_SIZE (type) == NULL_TREE) - return 0; - else if (host_integerp (TYPE_SIZE (type), 1)) - return tree_low_cst (TYPE_SIZE (type), 1); - else - return TYPE_ALIGN (type); -} + dw_loc_descr_ref loc; -/* Similarly, but return a double_int instead of UHWI. */ + if (*list_head != NULL) + { + /* Find the end of the chain. */ + for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next) + ; -static inline double_int -double_int_type_size_in_bits (const_tree type) -{ - if (TREE_CODE (type) == ERROR_MARK) - return uhwi_to_double_int (BITS_PER_WORD); - else if (TYPE_SIZE (type) == NULL_TREE) - return double_int_zero; - else if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) - return tree_to_double_int (TYPE_SIZE (type)); - else - return uhwi_to_double_int (TYPE_ALIGN (type)); + if (loc->dw_loc_opc != DW_OP_piece) + loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0); + } } -/* Given a pointer to a tree node for a subrange type, return a pointer - to a DIE that describes the given type. */ +/* Return a location descriptor that designates a machine register or + zero if there is none. */ -static dw_die_ref -subrange_type_die (tree type, tree low, tree high, dw_die_ref context_die) +static dw_loc_descr_ref +reg_loc_descriptor (rtx rtl, enum var_init_status initialized) { - dw_die_ref subrange_die; - const HOST_WIDE_INT size_in_bytes = int_size_in_bytes (type); - - if (context_die == NULL) - context_die = comp_unit_die; + rtx regs; - subrange_die = new_die (DW_TAG_subrange_type, context_die, type); + if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER) + return 0; - if (int_size_in_bytes (TREE_TYPE (type)) != size_in_bytes) + /* We only use "frame base" when we're sure we're talking about the + post-prologue local stack frame. We do this by *not* running + register elimination until this point, and recognizing the special + argument pointer and soft frame pointer rtx's. + Use DW_OP_fbreg offset DW_OP_stack_value in this case. */ + if ((rtl == arg_pointer_rtx || rtl == frame_pointer_rtx) + && eliminate_regs (rtl, VOIDmode, NULL_RTX) != rtl) { - /* The size of the subrange type and its base type do not match, - so we need to generate a size attribute for the subrange type. */ - add_AT_unsigned (subrange_die, DW_AT_byte_size, size_in_bytes); + dw_loc_descr_ref result = NULL; + + if (dwarf_version >= 4 || !dwarf_strict) + { + result = mem_loc_descriptor (rtl, GET_MODE (rtl), VOIDmode, + initialized); + if (result) + add_loc_descr (&result, + new_loc_descr (DW_OP_stack_value, 0, 0)); + } + return result; } - if (low) - add_bound_info (subrange_die, DW_AT_lower_bound, low); - if (high) - add_bound_info (subrange_die, DW_AT_upper_bound, high); + regs = targetm.dwarf_register_span (rtl); - return subrange_die; + if (hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)] > 1 || regs) + return multiple_reg_loc_descriptor (rtl, regs, initialized); + else + return one_reg_loc_descriptor (dbx_reg_number (rtl), initialized); } -/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging - entry that chains various modifiers in front of the given type. */ +/* Return a location descriptor that designates a machine register for + a given hard register number. */ -static dw_die_ref -modified_type_die (tree type, int is_const_type, int is_volatile_type, - dw_die_ref context_die) +static dw_loc_descr_ref +one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized) { - enum tree_code code = TREE_CODE (type); - dw_die_ref mod_type_die; - dw_die_ref sub_die = NULL; - tree item_type = NULL; - tree qualified_type; - tree name, low, high; + dw_loc_descr_ref reg_loc_descr; - if (code == ERROR_MARK) - return NULL; + if (regno <= 31) + reg_loc_descr + = new_loc_descr ((enum dwarf_location_atom) (DW_OP_reg0 + regno), 0, 0); + else + reg_loc_descr = new_loc_descr (DW_OP_regx, regno, 0); - /* See if we already have the appropriately qualified variant of - this type. */ - qualified_type - = get_qualified_type (type, - ((is_const_type ? TYPE_QUAL_CONST : 0) - | (is_volatile_type ? TYPE_QUAL_VOLATILE : 0))); + if (initialized == VAR_INIT_STATUS_UNINITIALIZED) + add_loc_descr (®_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); - if (qualified_type == sizetype - && TYPE_NAME (qualified_type) - && TREE_CODE (TYPE_NAME (qualified_type)) == TYPE_DECL) - { -#ifdef ENABLE_CHECKING - gcc_assert (TREE_CODE (TREE_TYPE (TYPE_NAME (qualified_type))) - == INTEGER_TYPE - && TYPE_PRECISION (TREE_TYPE (TYPE_NAME (qualified_type))) - == TYPE_PRECISION (qualified_type) - && TYPE_UNSIGNED (TREE_TYPE (TYPE_NAME (qualified_type))) - == TYPE_UNSIGNED (qualified_type)); -#endif - qualified_type = TREE_TYPE (TYPE_NAME (qualified_type)); - } + return reg_loc_descr; +} - /* If we do, then we can just use its DIE, if it exists. */ - if (qualified_type) - { - mod_type_die = lookup_type_die (qualified_type); - if (mod_type_die) - return mod_type_die; - } +/* Given an RTL of a register, return a location descriptor that + designates a value that spans more than one register. */ - name = qualified_type ? TYPE_NAME (qualified_type) : NULL; +static dw_loc_descr_ref +multiple_reg_loc_descriptor (rtx rtl, rtx regs, + enum var_init_status initialized) +{ + int nregs, size, i; + unsigned reg; + dw_loc_descr_ref loc_result = NULL; - /* Handle C typedef types. */ - if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name) - && !DECL_ARTIFICIAL (name)) + reg = REGNO (rtl); +#ifdef LEAF_REG_REMAP + if (current_function_uses_only_leaf_regs) { - tree dtype = TREE_TYPE (name); - - if (qualified_type == dtype) - { - /* For a named type, use the typedef. */ - gen_type_die (qualified_type, context_die); - return lookup_type_die (qualified_type); - } - else if (is_const_type < TYPE_READONLY (dtype) - || is_volatile_type < TYPE_VOLATILE (dtype) - || (is_const_type <= TYPE_READONLY (dtype) - && is_volatile_type <= TYPE_VOLATILE (dtype) - && DECL_ORIGINAL_TYPE (name) != type)) - /* cv-unqualified version of named type. Just use the unnamed - type to which it refers. */ - return modified_type_die (DECL_ORIGINAL_TYPE (name), - is_const_type, is_volatile_type, - context_die); - /* Else cv-qualified version of named type; fall through. */ + int leaf_reg = LEAF_REG_REMAP (reg); + if (leaf_reg != -1) + reg = (unsigned) leaf_reg; } +#endif + gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl)); + nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)]; - if (is_const_type) - { - mod_type_die = new_die (DW_TAG_const_type, comp_unit_die, type); - sub_die = modified_type_die (type, 0, is_volatile_type, context_die); - } - else if (is_volatile_type) - { - mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die, type); - sub_die = modified_type_die (type, 0, 0, context_die); - } - else if (code == POINTER_TYPE) - { - mod_type_die = new_die (DW_TAG_pointer_type, comp_unit_die, type); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, - simple_type_size_in_bits (type) / BITS_PER_UNIT); - item_type = TREE_TYPE (type); - if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (item_type))) - add_AT_unsigned (mod_type_die, DW_AT_address_class, - TYPE_ADDR_SPACE (item_type)); - } - else if (code == REFERENCE_TYPE) - { - if (TYPE_REF_IS_RVALUE (type) && dwarf_version >= 4) - mod_type_die = new_die (DW_TAG_rvalue_reference_type, comp_unit_die, - type); - else - mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die, type); - add_AT_unsigned (mod_type_die, DW_AT_byte_size, - simple_type_size_in_bits (type) / BITS_PER_UNIT); - item_type = TREE_TYPE (type); - if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (item_type))) - add_AT_unsigned (mod_type_die, DW_AT_address_class, - TYPE_ADDR_SPACE (item_type)); - } - else if (code == INTEGER_TYPE - && TREE_TYPE (type) != NULL_TREE - && subrange_type_for_debug_p (type, &low, &high)) - { - mod_type_die = subrange_type_die (type, low, high, context_die); - item_type = TREE_TYPE (type); - } - else if (is_base_type (type)) - mod_type_die = base_type_die (type); - else + /* Simple, contiguous registers. */ + if (regs == NULL_RTX) { - gen_type_die (type, context_die); + size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs; - /* We have to get the type_main_variant here (and pass that to the - `lookup_type_die' routine) because the ..._TYPE node we have - might simply be a *copy* of some original type node (where the - copy was created to help us keep track of typedef names) and - that copy might have a different TYPE_UID from the original - ..._TYPE node. */ - if (TREE_CODE (type) != VECTOR_TYPE) - return lookup_type_die (type_main_variant (type)); - else - /* Vectors have the debugging information in the type, - not the main variant. */ - return lookup_type_die (type); + loc_result = NULL; + while (nregs--) + { + dw_loc_descr_ref t; + + t = one_reg_loc_descriptor (DBX_REGISTER_NUMBER (reg), + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&loc_result, t); + add_loc_descr_op_piece (&loc_result, size); + ++reg; + } + return loc_result; } - /* Builtin types don't have a DECL_ORIGINAL_TYPE. For those, - don't output a DW_TAG_typedef, since there isn't one in the - user's program; just attach a DW_AT_name to the type. - Don't attach a DW_AT_name to DW_TAG_const_type or DW_TAG_volatile_type - if the base type already has the same name. */ - if (name - && ((TREE_CODE (name) != TYPE_DECL - && (qualified_type == TYPE_MAIN_VARIANT (type) - || (!is_const_type && !is_volatile_type))) - || (TREE_CODE (name) == TYPE_DECL - && TREE_TYPE (name) == qualified_type - && DECL_NAME (name)))) + /* Now onto stupid register sets in non contiguous locations. */ + + gcc_assert (GET_CODE (regs) == PARALLEL); + + size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); + loc_result = NULL; + + for (i = 0; i < XVECLEN (regs, 0); ++i) { - if (TREE_CODE (name) == TYPE_DECL) - /* Could just call add_name_and_src_coords_attributes here, - but since this is a builtin type it doesn't have any - useful source coordinates anyway. */ - name = DECL_NAME (name); - add_name_attribute (mod_type_die, IDENTIFIER_POINTER (name)); + dw_loc_descr_ref t; + + t = one_reg_loc_descriptor (REGNO (XVECEXP (regs, 0, i)), + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&loc_result, t); + size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); + add_loc_descr_op_piece (&loc_result, size); } - /* This probably indicates a bug. */ - else if (mod_type_die && mod_type_die->die_tag == DW_TAG_base_type) - add_name_attribute (mod_type_die, "__unknown__"); - if (qualified_type) - equate_type_number_to_die (qualified_type, mod_type_die); + if (loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED) + add_loc_descr (&loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); + return loc_result; +} - if (item_type) - /* We must do this after the equate_type_number_to_die call, in case - this is a recursive type. This ensures that the modified_type_die - recursion will terminate even if the type is recursive. Recursive - types are possible in Ada. */ - sub_die = modified_type_die (item_type, - TYPE_READONLY (item_type), - TYPE_VOLATILE (item_type), - context_die); +static unsigned long size_of_int_loc_descriptor (HOST_WIDE_INT); - if (sub_die != NULL) - add_AT_die_ref (mod_type_die, DW_AT_type, sub_die); +/* Return a location descriptor that designates a constant i, + as a compound operation from constant (i >> shift), constant shift + and DW_OP_shl. */ - return mod_type_die; +static dw_loc_descr_ref +int_shift_loc_descriptor (HOST_WIDE_INT i, int shift) +{ + dw_loc_descr_ref ret = int_loc_descriptor (i >> shift); + add_loc_descr (&ret, int_loc_descriptor (shift)); + add_loc_descr (&ret, new_loc_descr (DW_OP_shl, 0, 0)); + return ret; } -/* Generate DIEs for the generic parameters of T. - T must be either a generic type or a generic function. - See http://gcc.gnu.org/wiki/TemplateParmsDwarf for more. */ +/* Return a location descriptor that designates a constant. */ -static void -gen_generic_params_dies (tree t) +static dw_loc_descr_ref +int_loc_descriptor (HOST_WIDE_INT i) { - tree parms, args; - int parms_num, i; - dw_die_ref die = NULL; + enum dwarf_location_atom op; - if (!t || (TYPE_P (t) && !COMPLETE_TYPE_P (t))) - return; + /* Pick the smallest representation of a constant, rather than just + defaulting to the LEB encoding. */ + if (i >= 0) + { + int clz = clz_hwi (i); + int ctz = ctz_hwi (i); + if (i <= 31) + op = (enum dwarf_location_atom) (DW_OP_lit0 + i); + else if (i <= 0xff) + op = DW_OP_const1u; + else if (i <= 0xffff) + op = DW_OP_const2u; + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 5 + && clz + 5 + 255 >= HOST_BITS_PER_WIDE_INT) + /* DW_OP_litX DW_OP_litY DW_OP_shl takes just 3 bytes and + DW_OP_litX DW_OP_const1u Y DW_OP_shl takes just 4 bytes, + while DW_OP_const4u is 5 bytes. */ + return int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT - clz - 5); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 8 + && clz + 8 + 31 >= HOST_BITS_PER_WIDE_INT) + /* DW_OP_const1u X DW_OP_litY DW_OP_shl takes just 4 bytes, + while DW_OP_const4u is 5 bytes. */ + return int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT - clz - 8); + else if (HOST_BITS_PER_WIDE_INT == 32 || i <= 0xffffffff) + op = DW_OP_const4u; + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 8 + && clz + 8 + 255 >= HOST_BITS_PER_WIDE_INT) + /* DW_OP_const1u X DW_OP_const1u Y DW_OP_shl takes just 5 bytes, + while DW_OP_constu of constant >= 0x100000000 takes at least + 6 bytes. */ + return int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT - clz - 8); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 16 + && clz + 16 + (size_of_uleb128 (i) > 5 ? 255 : 31) + >= HOST_BITS_PER_WIDE_INT) + /* DW_OP_const2u X DW_OP_litY DW_OP_shl takes just 5 bytes, + DW_OP_const2u X DW_OP_const1u Y DW_OP_shl takes 6 bytes, + while DW_OP_constu takes in this case at least 6 bytes. */ + return int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT - clz - 16); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 32 + && clz + 32 + 31 >= HOST_BITS_PER_WIDE_INT + && size_of_uleb128 (i) > 6) + /* DW_OP_const4u X DW_OP_litY DW_OP_shl takes just 7 bytes. */ + return int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT - clz - 32); + else + op = DW_OP_constu; + } + else + { + if (i >= -0x80) + op = DW_OP_const1s; + else if (i >= -0x8000) + op = DW_OP_const2s; + else if (HOST_BITS_PER_WIDE_INT == 32 || i >= -0x80000000) + { + if (size_of_int_loc_descriptor (i) < 5) + { + dw_loc_descr_ref ret = int_loc_descriptor (-i); + add_loc_descr (&ret, new_loc_descr (DW_OP_neg, 0, 0)); + return ret; + } + op = DW_OP_const4s; + } + else + { + if (size_of_int_loc_descriptor (i) + < (unsigned long) 1 + size_of_sleb128 (i)) + { + dw_loc_descr_ref ret = int_loc_descriptor (-i); + add_loc_descr (&ret, new_loc_descr (DW_OP_neg, 0, 0)); + return ret; + } + op = DW_OP_consts; + } + } - if (TYPE_P (t)) - die = lookup_type_die (t); - else if (DECL_P (t)) - die = lookup_decl_die (t); + return new_loc_descr (op, i, 0); +} - gcc_assert (die); +/* Return size_of_locs (int_shift_loc_descriptor (i, shift)) + without actually allocating it. */ - parms = lang_hooks.get_innermost_generic_parms (t); - if (!parms) - /* T has no generic parameter. It means T is neither a generic type - or function. End of story. */ - return; +static unsigned long +size_of_int_shift_loc_descriptor (HOST_WIDE_INT i, int shift) +{ + return size_of_int_loc_descriptor (i >> shift) + + size_of_int_loc_descriptor (shift) + + 1; +} - parms_num = TREE_VEC_LENGTH (parms); - args = lang_hooks.get_innermost_generic_args (t); - for (i = 0; i < parms_num; i++) - { - tree parm, arg, arg_pack_elems; +/* Return size_of_locs (int_loc_descriptor (i)) without + actually allocating it. */ - parm = TREE_VEC_ELT (parms, i); - arg = TREE_VEC_ELT (args, i); - arg_pack_elems = lang_hooks.types.get_argument_pack_elems (arg); - gcc_assert (parm && TREE_VALUE (parm) && arg); +static unsigned long +size_of_int_loc_descriptor (HOST_WIDE_INT i) +{ + unsigned long s; - if (parm && TREE_VALUE (parm) && arg) + if (i >= 0) + { + int clz, ctz; + if (i <= 31) + return 1; + else if (i <= 0xff) + return 2; + else if (i <= 0xffff) + return 3; + clz = clz_hwi (i); + ctz = ctz_hwi (i); + if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 5 + && clz + 5 + 255 >= HOST_BITS_PER_WIDE_INT) + return size_of_int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT + - clz - 5); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 8 + && clz + 8 + 31 >= HOST_BITS_PER_WIDE_INT) + return size_of_int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT + - clz - 8); + else if (HOST_BITS_PER_WIDE_INT == 32 || i <= 0xffffffff) + return 5; + s = size_of_uleb128 ((unsigned HOST_WIDE_INT) i); + if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 8 + && clz + 8 + 255 >= HOST_BITS_PER_WIDE_INT) + return size_of_int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT + - clz - 8); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 16 + && clz + 16 + (s > 5 ? 255 : 31) >= HOST_BITS_PER_WIDE_INT) + return size_of_int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT + - clz - 16); + else if (clz + ctz >= HOST_BITS_PER_WIDE_INT - 32 + && clz + 32 + 31 >= HOST_BITS_PER_WIDE_INT + && s > 6) + return size_of_int_shift_loc_descriptor (i, HOST_BITS_PER_WIDE_INT + - clz - 32); + else + return 1 + s; + } + else + { + if (i >= -0x80) + return 2; + else if (i >= -0x8000) + return 3; + else if (HOST_BITS_PER_WIDE_INT == 32 || i >= -0x80000000) { - /* If PARM represents a template parameter pack, - emit a DW_TAG_GNU_template_parameter_pack DIE, followed - by DW_TAG_template_*_parameter DIEs for the argument - pack elements of ARG. Note that ARG would then be - an argument pack. */ - if (arg_pack_elems) - template_parameter_pack_die (TREE_VALUE (parm), - arg_pack_elems, - die); - else - generic_parameter_die (TREE_VALUE (parm), arg, - true /* Emit DW_AT_name */, die); + if (-(unsigned HOST_WIDE_INT) i != (unsigned HOST_WIDE_INT) i) + { + s = size_of_int_loc_descriptor (-i) + 1; + if (s < 5) + return s; + } + return 5; + } + else + { + unsigned long r = 1 + size_of_sleb128 (i); + if (-(unsigned HOST_WIDE_INT) i != (unsigned HOST_WIDE_INT) i) + { + s = size_of_int_loc_descriptor (-i) + 1; + if (s < r) + return s; + } + return r; } } } -/* Create and return a DIE for PARM which should be - the representation of a generic type parameter. - For instance, in the C++ front end, PARM would be a template parameter. - ARG is the argument to PARM. - EMIT_NAME_P if tree, the DIE will have DW_AT_name attribute set to the - name of the PARM. - PARENT_DIE is the parent DIE which the new created DIE should be added to, - as a child node. */ +/* Return loc description representing "address" of integer value. + This can appear only as toplevel expression. */ -static dw_die_ref -generic_parameter_die (tree parm, tree arg, - bool emit_name_p, - dw_die_ref parent_die) +static dw_loc_descr_ref +address_of_int_loc_descriptor (int size, HOST_WIDE_INT i) { - dw_die_ref tmpl_die = NULL; - const char *name = NULL; + int litsize; + dw_loc_descr_ref loc_result = NULL; - if (!parm || !DECL_NAME (parm) || !arg) + if (!(dwarf_version >= 4 || !dwarf_strict)) return NULL; - /* We support non-type generic parameters and arguments, - type generic parameters and arguments, as well as - generic generic parameters (a.k.a. template template parameters in C++) - and arguments. */ - if (TREE_CODE (parm) == PARM_DECL) - /* PARM is a nontype generic parameter */ - tmpl_die = new_die (DW_TAG_template_value_param, parent_die, parm); - else if (TREE_CODE (parm) == TYPE_DECL) - /* PARM is a type generic parameter. */ - tmpl_die = new_die (DW_TAG_template_type_param, parent_die, parm); - else if (lang_hooks.decls.generic_generic_parameter_decl_p (parm)) - /* PARM is a generic generic parameter. - Its DIE is a GNU extension. It shall have a - DW_AT_name attribute to represent the name of the template template - parameter, and a DW_AT_GNU_template_name attribute to represent the - name of the template template argument. */ - tmpl_die = new_die (DW_TAG_GNU_template_template_param, - parent_die, parm); - else - gcc_unreachable (); - - if (tmpl_die) + litsize = size_of_int_loc_descriptor (i); + /* Determine if DW_OP_stack_value or DW_OP_implicit_value + is more compact. For DW_OP_stack_value we need: + litsize + 1 (DW_OP_stack_value) + and for DW_OP_implicit_value: + 1 (DW_OP_implicit_value) + 1 (length) + size. */ + if ((int) DWARF2_ADDR_SIZE >= size && litsize + 1 <= 1 + 1 + size) { - tree tmpl_type; + loc_result = int_loc_descriptor (i); + add_loc_descr (&loc_result, + new_loc_descr (DW_OP_stack_value, 0, 0)); + return loc_result; + } - /* If PARM is a generic parameter pack, it means we are - emitting debug info for a template argument pack element. - In other terms, ARG is a template argument pack element. - In that case, we don't emit any DW_AT_name attribute for - the die. */ - if (emit_name_p) - { - name = IDENTIFIER_POINTER (DECL_NAME (parm)); - gcc_assert (name); - add_AT_string (tmpl_die, DW_AT_name, name); - } + loc_result = new_loc_descr (DW_OP_implicit_value, + size, 0); + loc_result->dw_loc_oprnd2.val_class = dw_val_class_const; + loc_result->dw_loc_oprnd2.v.val_int = i; + return loc_result; +} - if (!lang_hooks.decls.generic_generic_parameter_decl_p (parm)) - { - /* DWARF3, 5.6.8 says if PARM is a non-type generic parameter - TMPL_DIE should have a child DW_AT_type attribute that is set - to the type of the argument to PARM, which is ARG. - If PARM is a type generic parameter, TMPL_DIE should have a - child DW_AT_type that is set to ARG. */ - tmpl_type = TYPE_P (arg) ? arg : TREE_TYPE (arg); - add_type_attribute (tmpl_die, tmpl_type, 0, - TREE_THIS_VOLATILE (tmpl_type), - parent_die); - } - else +/* Return a location descriptor that designates a base+offset location. */ + +static dw_loc_descr_ref +based_loc_descr (rtx reg, HOST_WIDE_INT offset, + enum var_init_status initialized) +{ + unsigned int regno; + dw_loc_descr_ref result; + dw_fde_ref fde = cfun->fde; + + /* We only use "frame base" when we're sure we're talking about the + post-prologue local stack frame. We do this by *not* running + register elimination until this point, and recognizing the special + argument pointer and soft frame pointer rtx's. */ + if (reg == arg_pointer_rtx || reg == frame_pointer_rtx) + { + rtx elim = eliminate_regs (reg, VOIDmode, NULL_RTX); + + if (elim != reg) { - /* So TMPL_DIE is a DIE representing a - a generic generic template parameter, a.k.a template template - parameter in C++ and arg is a template. */ + if (GET_CODE (elim) == PLUS) + { + offset += INTVAL (XEXP (elim, 1)); + elim = XEXP (elim, 0); + } + gcc_assert ((SUPPORTS_STACK_ALIGNMENT + && (elim == hard_frame_pointer_rtx + || elim == stack_pointer_rtx)) + || elim == (frame_pointer_needed + ? hard_frame_pointer_rtx + : stack_pointer_rtx)); - /* The DW_AT_GNU_template_name attribute of the DIE must be set - to the name of the argument. */ - name = dwarf2_name (TYPE_P (arg) ? TYPE_NAME (arg) : arg, 1); - if (name) - add_AT_string (tmpl_die, DW_AT_GNU_template_name, name); - } + /* If drap register is used to align stack, use frame + pointer + offset to access stack variables. If stack + is aligned without drap, use stack pointer + offset to + access stack variables. */ + if (crtl->stack_realign_tried + && reg == frame_pointer_rtx) + { + int base_reg + = DWARF_FRAME_REGNUM ((fde && fde->drap_reg != INVALID_REGNUM) + ? HARD_FRAME_POINTER_REGNUM + : REGNO (elim)); + return new_reg_loc_descr (base_reg, offset); + } - if (TREE_CODE (parm) == PARM_DECL) - /* So PARM is a non-type generic parameter. - DWARF3 5.6.8 says we must set a DW_AT_const_value child - attribute of TMPL_DIE which value represents the value - of ARG. - We must be careful here: - The value of ARG might reference some function decls. - We might currently be emitting debug info for a generic - type and types are emitted before function decls, we don't - know if the function decls referenced by ARG will actually be - emitted after cgraph computations. - So must defer the generation of the DW_AT_const_value to - after cgraph is ready. */ - append_entry_to_tmpl_value_parm_die_table (tmpl_die, arg); + gcc_assert (frame_pointer_fb_offset_valid); + offset += frame_pointer_fb_offset; + return new_loc_descr (DW_OP_fbreg, offset, 0); + } } - return tmpl_die; -} + regno = DWARF_FRAME_REGNUM (REGNO (reg)); -/* Generate and return a DW_TAG_GNU_template_parameter_pack DIE representing. - PARM_PACK must be a template parameter pack. The returned DIE - will be child DIE of PARENT_DIE. */ + if (!optimize && fde + && (fde->drap_reg == regno || fde->vdrap_reg == regno)) + { + /* Use cfa+offset to represent the location of arguments passed + on the stack when drap is used to align stack. + Only do this when not optimizing, for optimized code var-tracking + is supposed to track where the arguments live and the register + used as vdrap or drap in some spot might be used for something + else in other part of the routine. */ + return new_loc_descr (DW_OP_fbreg, offset, 0); + } -static dw_die_ref -template_parameter_pack_die (tree parm_pack, - tree parm_pack_args, - dw_die_ref parent_die) -{ - dw_die_ref die; - int j; + if (regno <= 31) + result = new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + regno), + offset, 0); + else + result = new_loc_descr (DW_OP_bregx, regno, offset); - gcc_assert (parent_die && parm_pack); + if (initialized == VAR_INIT_STATUS_UNINITIALIZED) + add_loc_descr (&result, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); - die = new_die (DW_TAG_GNU_template_parameter_pack, parent_die, parm_pack); - add_name_and_src_coords_attributes (die, parm_pack); - for (j = 0; j < TREE_VEC_LENGTH (parm_pack_args); j++) - generic_parameter_die (parm_pack, - TREE_VEC_ELT (parm_pack_args, j), - false /* Don't emit DW_AT_name */, - die); - return die; + return result; } -/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is - an enumerated type. */ +/* Return true if this RTL expression describes a base+offset calculation. */ static inline int -type_is_enum (const_tree type) +is_based_loc (const_rtx rtl) { - return TREE_CODE (type) == ENUMERAL_TYPE; + return (GET_CODE (rtl) == PLUS + && ((REG_P (XEXP (rtl, 0)) + && REGNO (XEXP (rtl, 0)) < FIRST_PSEUDO_REGISTER + && CONST_INT_P (XEXP (rtl, 1))))); } -/* Return the DBX register number described by a given RTL node. */ +/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0) + failed. */ -static unsigned int -dbx_reg_number (const_rtx rtl) +static dw_loc_descr_ref +tls_mem_loc_descriptor (rtx mem) { - unsigned regno = REGNO (rtl); + tree base; + dw_loc_descr_ref loc_result; - gcc_assert (regno < FIRST_PSEUDO_REGISTER); + if (MEM_EXPR (mem) == NULL_TREE || !MEM_OFFSET_KNOWN_P (mem)) + return NULL; -#ifdef LEAF_REG_REMAP - if (current_function_uses_only_leaf_regs) - { - int leaf_reg = LEAF_REG_REMAP (regno); - if (leaf_reg != -1) - regno = (unsigned) leaf_reg; - } -#endif + base = get_base_address (MEM_EXPR (mem)); + if (base == NULL + || TREE_CODE (base) != VAR_DECL + || !DECL_THREAD_LOCAL_P (base)) + return NULL; - return DBX_REGISTER_NUMBER (regno); + loc_result = loc_descriptor_from_tree (MEM_EXPR (mem), 1); + if (loc_result == NULL) + return NULL; + + if (MEM_OFFSET (mem)) + loc_descr_plus_const (&loc_result, MEM_OFFSET (mem)); + + return loc_result; } -/* Optionally add a DW_OP_piece term to a location description expression. - DW_OP_piece is only added if the location description expression already - doesn't end with DW_OP_piece. */ +/* Output debug info about reason why we failed to expand expression as dwarf + expression. */ static void -add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size) +expansion_failed (tree expr, rtx rtl, char const *reason) { - dw_loc_descr_ref loc; - - if (*list_head != NULL) + if (dump_file && (dump_flags & TDF_DETAILS)) { - /* Find the end of the chain. */ - for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next) - ; - - if (loc->dw_loc_opc != DW_OP_piece) - loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0); + fprintf (dump_file, "Failed to expand as dwarf: "); + if (expr) + print_generic_expr (dump_file, expr, dump_flags); + if (rtl) + { + fprintf (dump_file, "\n"); + print_rtl (dump_file, rtl); + } + fprintf (dump_file, "\nReason: %s\n", reason); } } -/* Return a location descriptor that designates a machine register or - zero if there is none. */ +/* Helper function for const_ok_for_output, called either directly + or via for_each_rtx. */ -static dw_loc_descr_ref -reg_loc_descriptor (rtx rtl, enum var_init_status initialized) +static int +const_ok_for_output_1 (rtx *rtlp, void *data ATTRIBUTE_UNUSED) { - rtx regs; + rtx rtl = *rtlp; - if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER) + if (GET_CODE (rtl) == UNSPEC) + { + /* If delegitimize_address couldn't do anything with the UNSPEC, assume + we can't express it in the debug info. */ +#ifdef ENABLE_CHECKING + /* Don't complain about TLS UNSPECs, those are just too hard to + delegitimize. */ + if (XVECLEN (rtl, 0) != 1 + || GET_CODE (XVECEXP (rtl, 0, 0)) != SYMBOL_REF + || SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0)) == NULL + || TREE_CODE (SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0))) != VAR_DECL + || !DECL_THREAD_LOCAL_P (SYMBOL_REF_DECL (XVECEXP (rtl, 0, 0)))) + inform (current_function_decl + ? DECL_SOURCE_LOCATION (current_function_decl) + : UNKNOWN_LOCATION, +#if NUM_UNSPEC_VALUES > 0 + "non-delegitimized UNSPEC %s (%d) found in variable location", + ((XINT (rtl, 1) >= 0 && XINT (rtl, 1) < NUM_UNSPEC_VALUES) + ? unspec_strings[XINT (rtl, 1)] : "unknown"), + XINT (rtl, 1)); +#else + "non-delegitimized UNSPEC %d found in variable location", + XINT (rtl, 1)); +#endif +#endif + expansion_failed (NULL_TREE, rtl, + "UNSPEC hasn't been delegitimized.\n"); + return 1; + } + + if (GET_CODE (rtl) != SYMBOL_REF) return 0; - /* We only use "frame base" when we're sure we're talking about the - post-prologue local stack frame. We do this by *not* running - register elimination until this point, and recognizing the special - argument pointer and soft frame pointer rtx's. - Use DW_OP_fbreg offset DW_OP_stack_value in this case. */ - if ((rtl == arg_pointer_rtx || rtl == frame_pointer_rtx) - && eliminate_regs (rtl, VOIDmode, NULL_RTX) != rtl) + if (CONSTANT_POOL_ADDRESS_P (rtl)) { - dw_loc_descr_ref result = NULL; - - if (dwarf_version >= 4 || !dwarf_strict) + bool marked; + get_pool_constant_mark (rtl, &marked); + /* If all references to this pool constant were optimized away, + it was not output and thus we can't represent it. */ + if (!marked) { - result = mem_loc_descriptor (rtl, VOIDmode, initialized); - if (result) - add_loc_descr (&result, - new_loc_descr (DW_OP_stack_value, 0, 0)); + expansion_failed (NULL_TREE, rtl, + "Constant was removed from constant pool.\n"); + return 1; } - return result; } - regs = targetm.dwarf_register_span (rtl); + if (SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE) + return 1; - if (hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)] > 1 || regs) - return multiple_reg_loc_descriptor (rtl, regs, initialized); - else - return one_reg_loc_descriptor (dbx_reg_number (rtl), initialized); + /* Avoid references to external symbols in debug info, on several targets + the linker might even refuse to link when linking a shared library, + and in many other cases the relocations for .debug_info/.debug_loc are + dropped, so the address becomes zero anyway. Hidden symbols, guaranteed + to be defined within the same shared library or executable are fine. */ + if (SYMBOL_REF_EXTERNAL_P (rtl)) + { + tree decl = SYMBOL_REF_DECL (rtl); + + if (decl == NULL || !targetm.binds_local_p (decl)) + { + expansion_failed (NULL_TREE, rtl, + "Symbol not defined in current TU.\n"); + return 1; + } + } + + return 0; } -/* Return a location descriptor that designates a machine register for - a given hard register number. */ +/* Return true if constant RTL can be emitted in DW_OP_addr or + DW_AT_const_value. TLS SYMBOL_REFs, external SYMBOL_REFs or + non-marked constant pool SYMBOL_REFs can't be referenced in it. */ -static dw_loc_descr_ref -one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized) +static bool +const_ok_for_output (rtx rtl) { - dw_loc_descr_ref reg_loc_descr; + if (GET_CODE (rtl) == SYMBOL_REF) + return const_ok_for_output_1 (&rtl, NULL) == 0; - if (regno <= 31) - reg_loc_descr - = new_loc_descr ((enum dwarf_location_atom) (DW_OP_reg0 + regno), 0, 0); - else - reg_loc_descr = new_loc_descr (DW_OP_regx, regno, 0); + if (GET_CODE (rtl) == CONST) + return for_each_rtx (&XEXP (rtl, 0), const_ok_for_output_1, NULL) == 0; - if (initialized == VAR_INIT_STATUS_UNINITIALIZED) - add_loc_descr (®_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); + return true; +} - return reg_loc_descr; +/* Return a reference to DW_TAG_base_type corresponding to MODE and UNSIGNEDP + if possible, NULL otherwise. */ + +static dw_die_ref +base_type_for_mode (enum machine_mode mode, bool unsignedp) +{ + dw_die_ref type_die; + tree type = lang_hooks.types.type_for_mode (mode, unsignedp); + + if (type == NULL) + return NULL; + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + case REAL_TYPE: + break; + default: + return NULL; + } + type_die = lookup_type_die (type); + if (!type_die) + type_die = modified_type_die (type, false, false, comp_unit_die ()); + if (type_die == NULL || type_die->die_tag != DW_TAG_base_type) + return NULL; + return type_die; } -/* Given an RTL of a register, return a location descriptor that - designates a value that spans more than one register. */ +/* For OP descriptor assumed to be in unsigned MODE, convert it to a unsigned + type matching MODE, or, if MODE is narrower than or as wide as + DWARF2_ADDR_SIZE, untyped. Return NULL if the conversion is not + possible. */ static dw_loc_descr_ref -multiple_reg_loc_descriptor (rtx rtl, rtx regs, - enum var_init_status initialized) +convert_descriptor_to_mode (enum machine_mode mode, dw_loc_descr_ref op) { - int nregs, size, i; - unsigned reg; - dw_loc_descr_ref loc_result = NULL; + enum machine_mode outer_mode = mode; + dw_die_ref type_die; + dw_loc_descr_ref cvt; - reg = REGNO (rtl); -#ifdef LEAF_REG_REMAP - if (current_function_uses_only_leaf_regs) + if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE) { - int leaf_reg = LEAF_REG_REMAP (reg); - if (leaf_reg != -1) - reg = (unsigned) leaf_reg; + add_loc_descr (&op, new_loc_descr (DW_OP_GNU_convert, 0, 0)); + return op; } -#endif - gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl)); - nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)]; - - /* Simple, contiguous registers. */ - if (regs == NULL_RTX) - { - size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs; + type_die = base_type_for_mode (outer_mode, 1); + if (type_die == NULL) + return NULL; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op, cvt); + return op; +} - loc_result = NULL; - while (nregs--) - { - dw_loc_descr_ref t; +/* Return location descriptor for comparison OP with operands OP0 and OP1. */ - t = one_reg_loc_descriptor (DBX_REGISTER_NUMBER (reg), - VAR_INIT_STATUS_INITIALIZED); - add_loc_descr (&loc_result, t); - add_loc_descr_op_piece (&loc_result, size); - ++reg; - } - return loc_result; +static dw_loc_descr_ref +compare_loc_descriptor (enum dwarf_location_atom op, dw_loc_descr_ref op0, + dw_loc_descr_ref op1) +{ + dw_loc_descr_ref ret = op0; + add_loc_descr (&ret, op1); + add_loc_descr (&ret, new_loc_descr (op, 0, 0)); + if (STORE_FLAG_VALUE != 1) + { + add_loc_descr (&ret, int_loc_descriptor (STORE_FLAG_VALUE)); + add_loc_descr (&ret, new_loc_descr (DW_OP_mul, 0, 0)); } + return ret; +} - /* Now onto stupid register sets in non contiguous locations. */ - - gcc_assert (GET_CODE (regs) == PARALLEL); +/* Return location descriptor for signed comparison OP RTL. */ - size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); - loc_result = NULL; +static dw_loc_descr_ref +scompare_loc_descriptor (enum dwarf_location_atom op, rtx rtl, + enum machine_mode mem_mode) +{ + enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0)); + dw_loc_descr_ref op0, op1; + int shift; - for (i = 0; i < XVECLEN (regs, 0); ++i) - { - dw_loc_descr_ref t; + if (op_mode == VOIDmode) + op_mode = GET_MODE (XEXP (rtl, 1)); + if (op_mode == VOIDmode) + return NULL; - t = one_reg_loc_descriptor (REGNO (XVECEXP (regs, 0, i)), - VAR_INIT_STATUS_INITIALIZED); - add_loc_descr (&loc_result, t); - size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0))); - add_loc_descr_op_piece (&loc_result, size); - } + if (dwarf_strict + && (GET_MODE_CLASS (op_mode) != MODE_INT + || GET_MODE_SIZE (op_mode) > DWARF2_ADDR_SIZE)) + return NULL; - if (loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED) - add_loc_descr (&loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); - return loc_result; -} + op0 = mem_loc_descriptor (XEXP (rtl, 0), op_mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (XEXP (rtl, 1), op_mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); -/* Return a location descriptor that designates a constant. */ + if (op0 == NULL || op1 == NULL) + return NULL; -static dw_loc_descr_ref -int_loc_descriptor (HOST_WIDE_INT i) -{ - enum dwarf_location_atom op; + if (GET_MODE_CLASS (op_mode) != MODE_INT + || GET_MODE_SIZE (op_mode) == DWARF2_ADDR_SIZE) + return compare_loc_descriptor (op, op0, op1); - /* Pick the smallest representation of a constant, rather than just - defaulting to the LEB encoding. */ - if (i >= 0) + if (GET_MODE_SIZE (op_mode) > DWARF2_ADDR_SIZE) { - if (i <= 31) - op = (enum dwarf_location_atom) (DW_OP_lit0 + i); - else if (i <= 0xff) - op = DW_OP_const1u; - else if (i <= 0xffff) - op = DW_OP_const2u; - else if (HOST_BITS_PER_WIDE_INT == 32 - || i <= 0xffffffff) - op = DW_OP_const4u; - else - op = DW_OP_constu; + dw_die_ref type_die = base_type_for_mode (op_mode, 0); + dw_loc_descr_ref cvt; + + if (type_die == NULL) + return NULL; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op0, cvt); + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op1, cvt); + return compare_loc_descriptor (op, op0, op1); + } + + shift = (DWARF2_ADDR_SIZE - GET_MODE_SIZE (op_mode)) * BITS_PER_UNIT; + /* For eq/ne, if the operands are known to be zero-extended, + there is no need to do the fancy shifting up. */ + if (op == DW_OP_eq || op == DW_OP_ne) + { + dw_loc_descr_ref last0, last1; + for (last0 = op0; last0->dw_loc_next != NULL; last0 = last0->dw_loc_next) + ; + for (last1 = op1; last1->dw_loc_next != NULL; last1 = last1->dw_loc_next) + ; + /* deref_size zero extends, and for constants we can check + whether they are zero extended or not. */ + if (((last0->dw_loc_opc == DW_OP_deref_size + && last0->dw_loc_oprnd1.v.val_int <= GET_MODE_SIZE (op_mode)) + || (CONST_INT_P (XEXP (rtl, 0)) + && (unsigned HOST_WIDE_INT) INTVAL (XEXP (rtl, 0)) + == (INTVAL (XEXP (rtl, 0)) & GET_MODE_MASK (op_mode)))) + && ((last1->dw_loc_opc == DW_OP_deref_size + && last1->dw_loc_oprnd1.v.val_int <= GET_MODE_SIZE (op_mode)) + || (CONST_INT_P (XEXP (rtl, 1)) + && (unsigned HOST_WIDE_INT) INTVAL (XEXP (rtl, 1)) + == (INTVAL (XEXP (rtl, 1)) & GET_MODE_MASK (op_mode))))) + return compare_loc_descriptor (op, op0, op1); + + /* EQ/NE comparison against constant in narrower type than + DWARF2_ADDR_SIZE can be performed either as + DW_OP_const1u DW_OP_shl DW_OP_const* + DW_OP_{eq,ne} + or + DW_OP_const*u DW_OP_and DW_OP_const* + DW_OP_{eq,ne}. Pick whatever is shorter. */ + if (CONST_INT_P (XEXP (rtl, 1)) + && GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT + && (size_of_int_loc_descriptor (shift) + 1 + + size_of_int_loc_descriptor (INTVAL (XEXP (rtl, 1)) << shift) + >= size_of_int_loc_descriptor (GET_MODE_MASK (op_mode)) + 1 + + size_of_int_loc_descriptor (INTVAL (XEXP (rtl, 1)) + & GET_MODE_MASK (op_mode)))) + { + add_loc_descr (&op0, int_loc_descriptor (GET_MODE_MASK (op_mode))); + add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0)); + op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) + & GET_MODE_MASK (op_mode)); + return compare_loc_descriptor (op, op0, op1); + } } + add_loc_descr (&op0, int_loc_descriptor (shift)); + add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0)); + if (CONST_INT_P (XEXP (rtl, 1))) + op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) << shift); else { - if (i >= -0x80) - op = DW_OP_const1s; - else if (i >= -0x8000) - op = DW_OP_const2s; - else if (HOST_BITS_PER_WIDE_INT == 32 - || i >= -0x80000000) - op = DW_OP_const4s; - else - op = DW_OP_consts; + add_loc_descr (&op1, int_loc_descriptor (shift)); + add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0)); } - - return new_loc_descr (op, i, 0); + return compare_loc_descriptor (op, op0, op1); } -/* Return loc description representing "address" of integer value. - This can appear only as toplevel expression. */ +/* Return location descriptor for unsigned comparison OP RTL. */ static dw_loc_descr_ref -address_of_int_loc_descriptor (int size, HOST_WIDE_INT i) +ucompare_loc_descriptor (enum dwarf_location_atom op, rtx rtl, + enum machine_mode mem_mode) { - int litsize; - dw_loc_descr_ref loc_result = NULL; + enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0)); + dw_loc_descr_ref op0, op1; - if (!(dwarf_version >= 4 || !dwarf_strict)) + if (op_mode == VOIDmode) + op_mode = GET_MODE (XEXP (rtl, 1)); + if (op_mode == VOIDmode) + return NULL; + if (GET_MODE_CLASS (op_mode) != MODE_INT) return NULL; - if (i >= 0) + if (dwarf_strict && GET_MODE_SIZE (op_mode) > DWARF2_ADDR_SIZE) + return NULL; + + op0 = mem_loc_descriptor (XEXP (rtl, 0), op_mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (XEXP (rtl, 1), op_mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + + if (op0 == NULL || op1 == NULL) + return NULL; + + if (GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE) { - if (i <= 31) - litsize = 1; - else if (i <= 0xff) - litsize = 2; - else if (i <= 0xffff) - litsize = 3; - else if (HOST_BITS_PER_WIDE_INT == 32 - || i <= 0xffffffff) - litsize = 5; - else - litsize = 1 + size_of_uleb128 ((unsigned HOST_WIDE_INT) i); + HOST_WIDE_INT mask = GET_MODE_MASK (op_mode); + dw_loc_descr_ref last0, last1; + for (last0 = op0; last0->dw_loc_next != NULL; last0 = last0->dw_loc_next) + ; + for (last1 = op1; last1->dw_loc_next != NULL; last1 = last1->dw_loc_next) + ; + if (CONST_INT_P (XEXP (rtl, 0))) + op0 = int_loc_descriptor (INTVAL (XEXP (rtl, 0)) & mask); + /* deref_size zero extends, so no need to mask it again. */ + else if (last0->dw_loc_opc != DW_OP_deref_size + || last0->dw_loc_oprnd1.v.val_int > GET_MODE_SIZE (op_mode)) + { + add_loc_descr (&op0, int_loc_descriptor (mask)); + add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0)); + } + if (CONST_INT_P (XEXP (rtl, 1))) + op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) & mask); + /* deref_size zero extends, so no need to mask it again. */ + else if (last1->dw_loc_opc != DW_OP_deref_size + || last1->dw_loc_oprnd1.v.val_int > GET_MODE_SIZE (op_mode)) + { + add_loc_descr (&op1, int_loc_descriptor (mask)); + add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0)); + } } - else + else if (GET_MODE_SIZE (op_mode) == DWARF2_ADDR_SIZE) { - if (i >= -0x80) - litsize = 2; - else if (i >= -0x8000) - litsize = 3; - else if (HOST_BITS_PER_WIDE_INT == 32 - || i >= -0x80000000) - litsize = 5; + HOST_WIDE_INT bias = 1; + bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1); + add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0)); + if (CONST_INT_P (XEXP (rtl, 1))) + op1 = int_loc_descriptor ((unsigned HOST_WIDE_INT) bias + + INTVAL (XEXP (rtl, 1))); else - litsize = 1 + size_of_sleb128 (i); - } - /* Determine if DW_OP_stack_value or DW_OP_implicit_value - is more compact. For DW_OP_stack_value we need: - litsize + 1 (DW_OP_stack_value) - and for DW_OP_implicit_value: - 1 (DW_OP_implicit_value) + 1 (length) + size. */ - if ((int) DWARF2_ADDR_SIZE >= size && litsize + 1 <= 1 + 1 + size) - { - loc_result = int_loc_descriptor (i); - add_loc_descr (&loc_result, - new_loc_descr (DW_OP_stack_value, 0, 0)); - return loc_result; + add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst, + bias, 0)); } - - loc_result = new_loc_descr (DW_OP_implicit_value, - size, 0); - loc_result->dw_loc_oprnd2.val_class = dw_val_class_const; - loc_result->dw_loc_oprnd2.v.val_int = i; - return loc_result; + return compare_loc_descriptor (op, op0, op1); } -/* Return a location descriptor that designates a base+offset location. */ +/* Return location descriptor for {U,S}{MIN,MAX}. */ static dw_loc_descr_ref -based_loc_descr (rtx reg, HOST_WIDE_INT offset, - enum var_init_status initialized) +minmax_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode) { - unsigned int regno; - dw_loc_descr_ref result; - dw_fde_ref fde = current_fde (); + enum dwarf_location_atom op; + dw_loc_descr_ref op0, op1, ret; + dw_loc_descr_ref bra_node, drop_node; - /* We only use "frame base" when we're sure we're talking about the - post-prologue local stack frame. We do this by *not* running - register elimination until this point, and recognizing the special - argument pointer and soft frame pointer rtx's. */ - if (reg == arg_pointer_rtx || reg == frame_pointer_rtx) - { - rtx elim = eliminate_regs (reg, VOIDmode, NULL_RTX); + if (dwarf_strict + && (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE)) + return NULL; - if (elim != reg) - { - if (GET_CODE (elim) == PLUS) - { - offset += INTVAL (XEXP (elim, 1)); - elim = XEXP (elim, 0); - } - gcc_assert ((SUPPORTS_STACK_ALIGNMENT - && (elim == hard_frame_pointer_rtx - || elim == stack_pointer_rtx)) - || elim == (frame_pointer_needed - ? hard_frame_pointer_rtx - : stack_pointer_rtx)); + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); - /* If drap register is used to align stack, use frame - pointer + offset to access stack variables. If stack - is aligned without drap, use stack pointer + offset to - access stack variables. */ - if (crtl->stack_realign_tried - && reg == frame_pointer_rtx) - { - int base_reg - = DWARF_FRAME_REGNUM ((fde && fde->drap_reg != INVALID_REGNUM) - ? HARD_FRAME_POINTER_REGNUM - : STACK_POINTER_REGNUM); - return new_reg_loc_descr (base_reg, offset); - } + if (op0 == NULL || op1 == NULL) + return NULL; - offset += frame_pointer_fb_offset; - return new_loc_descr (DW_OP_fbreg, offset, 0); + add_loc_descr (&op0, new_loc_descr (DW_OP_dup, 0, 0)); + add_loc_descr (&op1, new_loc_descr (DW_OP_swap, 0, 0)); + add_loc_descr (&op1, new_loc_descr (DW_OP_over, 0, 0)); + if (GET_CODE (rtl) == UMIN || GET_CODE (rtl) == UMAX) + { + if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE) + { + HOST_WIDE_INT mask = GET_MODE_MASK (mode); + add_loc_descr (&op0, int_loc_descriptor (mask)); + add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0)); + add_loc_descr (&op1, int_loc_descriptor (mask)); + add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0)); + } + else if (GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE) + { + HOST_WIDE_INT bias = 1; + bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1); + add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0)); + add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst, bias, 0)); } } - else if (!optimize - && fde - && (fde->drap_reg == REGNO (reg) - || fde->vdrap_reg == REGNO (reg))) + else if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE) { - /* Use cfa+offset to represent the location of arguments passed - on the stack when drap is used to align stack. - Only do this when not optimizing, for optimized code var-tracking - is supposed to track where the arguments live and the register - used as vdrap or drap in some spot might be used for something - else in other part of the routine. */ - return new_loc_descr (DW_OP_fbreg, offset, 0); + int shift = (DWARF2_ADDR_SIZE - GET_MODE_SIZE (mode)) * BITS_PER_UNIT; + add_loc_descr (&op0, int_loc_descriptor (shift)); + add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0)); + add_loc_descr (&op1, int_loc_descriptor (shift)); + add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0)); } - - regno = dbx_reg_number (reg); - if (regno <= 31) - result = new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + regno), - offset, 0); + else if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE) + { + dw_die_ref type_die = base_type_for_mode (mode, 0); + dw_loc_descr_ref cvt; + if (type_die == NULL) + return NULL; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op0, cvt); + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op1, cvt); + } + + if (GET_CODE (rtl) == SMIN || GET_CODE (rtl) == UMIN) + op = DW_OP_lt; else - result = new_loc_descr (DW_OP_bregx, regno, offset); - - if (initialized == VAR_INIT_STATUS_UNINITIALIZED) - add_loc_descr (&result, new_loc_descr (DW_OP_GNU_uninit, 0, 0)); - - return result; + op = DW_OP_gt; + ret = op0; + add_loc_descr (&ret, op1); + add_loc_descr (&ret, new_loc_descr (op, 0, 0)); + bra_node = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&ret, bra_node); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + drop_node = new_loc_descr (DW_OP_drop, 0, 0); + add_loc_descr (&ret, drop_node); + bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc; + bra_node->dw_loc_oprnd1.v.val_loc = drop_node; + if ((GET_CODE (rtl) == SMIN || GET_CODE (rtl) == SMAX) + && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE) + ret = convert_descriptor_to_mode (mode, ret); + return ret; } -/* Return true if this RTL expression describes a base+offset calculation. */ +/* Helper function for mem_loc_descriptor. Perform OP binary op, + but after converting arguments to type_die, afterwards + convert back to unsigned. */ -static inline int -is_based_loc (const_rtx rtl) +static dw_loc_descr_ref +typed_binop (enum dwarf_location_atom op, rtx rtl, dw_die_ref type_die, + enum machine_mode mode, enum machine_mode mem_mode) { - return (GET_CODE (rtl) == PLUS - && ((REG_P (XEXP (rtl, 0)) - && REGNO (XEXP (rtl, 0)) < FIRST_PSEUDO_REGISTER - && CONST_INT_P (XEXP (rtl, 1))))); + dw_loc_descr_ref cvt, op0, op1; + + if (type_die == NULL) + return NULL; + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL || op1 == NULL) + return NULL; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op0, cvt); + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op1, cvt); + add_loc_descr (&op0, op1); + add_loc_descr (&op0, new_loc_descr (op, 0, 0)); + return convert_descriptor_to_mode (mode, op0); +} + +/* CLZ (where constV is CLZ_DEFINED_VALUE_AT_ZERO computed value, + const0 is DW_OP_lit0 or corresponding typed constant, + const1 is DW_OP_lit1 or corresponding typed constant + and constMSB is constant with just the MSB bit set + for the mode): + DW_OP_dup DW_OP_bra DW_OP_drop constV DW_OP_skip + L1: const0 DW_OP_swap + L2: DW_OP_dup constMSB DW_OP_and DW_OP_bra const1 DW_OP_shl + DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip + L3: DW_OP_drop + L4: DW_OP_nop + + CTZ is similar: + DW_OP_dup DW_OP_bra DW_OP_drop constV DW_OP_skip + L1: const0 DW_OP_swap + L2: DW_OP_dup const1 DW_OP_and DW_OP_bra const1 DW_OP_shr + DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip + L3: DW_OP_drop + L4: DW_OP_nop + + FFS is similar: + DW_OP_dup DW_OP_bra DW_OP_drop const0 DW_OP_skip + L1: const1 DW_OP_swap + L2: DW_OP_dup const1 DW_OP_and DW_OP_bra const1 DW_OP_shr + DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip + L3: DW_OP_drop + L4: DW_OP_nop */ + +static dw_loc_descr_ref +clz_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode) +{ + dw_loc_descr_ref op0, ret, tmp; + HOST_WIDE_INT valv; + dw_loc_descr_ref l1jump, l1label; + dw_loc_descr_ref l2jump, l2label; + dw_loc_descr_ref l3jump, l3label; + dw_loc_descr_ref l4jump, l4label; + rtx msb; + + if (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE (XEXP (rtl, 0)) != mode + || (GET_CODE (rtl) == CLZ + && GET_MODE_BITSIZE (mode) > 2 * HOST_BITS_PER_WIDE_INT)) + return NULL; + + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL) + return NULL; + ret = op0; + if (GET_CODE (rtl) == CLZ) + { + if (!CLZ_DEFINED_VALUE_AT_ZERO (mode, valv)) + valv = GET_MODE_BITSIZE (mode); + } + else if (GET_CODE (rtl) == FFS) + valv = 0; + else if (!CTZ_DEFINED_VALUE_AT_ZERO (mode, valv)) + valv = GET_MODE_BITSIZE (mode); + add_loc_descr (&ret, new_loc_descr (DW_OP_dup, 0, 0)); + l1jump = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&ret, l1jump); + add_loc_descr (&ret, new_loc_descr (DW_OP_drop, 0, 0)); + tmp = mem_loc_descriptor (GEN_INT (valv), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + l4jump = new_loc_descr (DW_OP_skip, 0, 0); + add_loc_descr (&ret, l4jump); + l1label = mem_loc_descriptor (GET_CODE (rtl) == FFS + ? const1_rtx : const0_rtx, + mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (l1label == NULL) + return NULL; + add_loc_descr (&ret, l1label); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + l2label = new_loc_descr (DW_OP_dup, 0, 0); + add_loc_descr (&ret, l2label); + if (GET_CODE (rtl) != CLZ) + msb = const1_rtx; + else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT) + msb = GEN_INT ((unsigned HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (mode) - 1)); + else + msb = immed_double_const (0, (unsigned HOST_WIDE_INT) 1 + << (GET_MODE_BITSIZE (mode) + - HOST_BITS_PER_WIDE_INT - 1), mode); + if (GET_CODE (msb) == CONST_INT && INTVAL (msb) < 0) + tmp = new_loc_descr (HOST_BITS_PER_WIDE_INT == 32 + ? DW_OP_const4u : HOST_BITS_PER_WIDE_INT == 64 + ? DW_OP_const8u : DW_OP_constu, INTVAL (msb), 0); + else + tmp = mem_loc_descriptor (msb, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_and, 0, 0)); + l3jump = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&ret, l3jump); + tmp = mem_loc_descriptor (const1_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (GET_CODE (rtl) == CLZ + ? DW_OP_shl : DW_OP_shr, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_plus_uconst, 1, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + l2jump = new_loc_descr (DW_OP_skip, 0, 0); + add_loc_descr (&ret, l2jump); + l3label = new_loc_descr (DW_OP_drop, 0, 0); + add_loc_descr (&ret, l3label); + l4label = new_loc_descr (DW_OP_nop, 0, 0); + add_loc_descr (&ret, l4label); + l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l1jump->dw_loc_oprnd1.v.val_loc = l1label; + l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l2jump->dw_loc_oprnd1.v.val_loc = l2label; + l3jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l3jump->dw_loc_oprnd1.v.val_loc = l3label; + l4jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l4jump->dw_loc_oprnd1.v.val_loc = l4label; + return ret; } -/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0) - failed. */ +/* POPCOUNT (const0 is DW_OP_lit0 or corresponding typed constant, + const1 is DW_OP_lit1 or corresponding typed constant): + const0 DW_OP_swap + L1: DW_OP_dup DW_OP_bra DW_OP_dup DW_OP_rot const1 DW_OP_and + DW_OP_plus DW_OP_swap const1 DW_OP_shr DW_OP_skip + L2: DW_OP_drop + + PARITY is similar: + L1: DW_OP_dup DW_OP_bra DW_OP_dup DW_OP_rot const1 DW_OP_and + DW_OP_xor DW_OP_swap const1 DW_OP_shr DW_OP_skip + L2: DW_OP_drop */ static dw_loc_descr_ref -tls_mem_loc_descriptor (rtx mem) +popcount_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode) { - tree base; - dw_loc_descr_ref loc_result; + dw_loc_descr_ref op0, ret, tmp; + dw_loc_descr_ref l1jump, l1label; + dw_loc_descr_ref l2jump, l2label; - if (MEM_EXPR (mem) == NULL_TREE || MEM_OFFSET (mem) == NULL_RTX) + if (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE (XEXP (rtl, 0)) != mode) return NULL; - base = get_base_address (MEM_EXPR (mem)); - if (base == NULL - || TREE_CODE (base) != VAR_DECL - || !DECL_THREAD_LOCAL_P (base)) + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL) + return NULL; + ret = op0; + tmp = mem_loc_descriptor (const0_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + l1label = new_loc_descr (DW_OP_dup, 0, 0); + add_loc_descr (&ret, l1label); + l2jump = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&ret, l2jump); + add_loc_descr (&ret, new_loc_descr (DW_OP_dup, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_rot, 0, 0)); + tmp = mem_loc_descriptor (const1_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) return NULL; + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_and, 0, 0)); + add_loc_descr (&ret, new_loc_descr (GET_CODE (rtl) == POPCOUNT + ? DW_OP_plus : DW_OP_xor, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + tmp = mem_loc_descriptor (const1_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_shr, 0, 0)); + l1jump = new_loc_descr (DW_OP_skip, 0, 0); + add_loc_descr (&ret, l1jump); + l2label = new_loc_descr (DW_OP_drop, 0, 0); + add_loc_descr (&ret, l2label); + l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l1jump->dw_loc_oprnd1.v.val_loc = l1label; + l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l2jump->dw_loc_oprnd1.v.val_loc = l2label; + return ret; +} + +/* BSWAP (constS is initial shift count, either 56 or 24): + constS const0 + L1: DW_OP_pick <2> constS DW_OP_pick <3> DW_OP_minus DW_OP_shr + const255 DW_OP_and DW_OP_pick <2> DW_OP_shl DW_OP_or + DW_OP_swap DW_OP_dup const0 DW_OP_eq DW_OP_bra const8 + DW_OP_minus DW_OP_swap DW_OP_skip + L2: DW_OP_drop DW_OP_swap DW_OP_drop */ + +static dw_loc_descr_ref +bswap_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode) +{ + dw_loc_descr_ref op0, ret, tmp; + dw_loc_descr_ref l1jump, l1label; + dw_loc_descr_ref l2jump, l2label; - loc_result = loc_descriptor_from_tree (MEM_EXPR (mem), 1); - if (loc_result == NULL) + if (GET_MODE_CLASS (mode) != MODE_INT + || BITS_PER_UNIT != 8 + || (GET_MODE_BITSIZE (mode) != 32 + && GET_MODE_BITSIZE (mode) != 64)) return NULL; - if (INTVAL (MEM_OFFSET (mem))) - loc_descr_plus_const (&loc_result, INTVAL (MEM_OFFSET (mem))); + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL) + return NULL; - return loc_result; + ret = op0; + tmp = mem_loc_descriptor (GEN_INT (GET_MODE_BITSIZE (mode) - 8), + mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + tmp = mem_loc_descriptor (const0_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + l1label = new_loc_descr (DW_OP_pick, 2, 0); + add_loc_descr (&ret, l1label); + tmp = mem_loc_descriptor (GEN_INT (GET_MODE_BITSIZE (mode) - 8), + mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_pick, 3, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_minus, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_shr, 0, 0)); + tmp = mem_loc_descriptor (GEN_INT (255), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (tmp == NULL) + return NULL; + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_and, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_pick, 2, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_shl, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_or, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_dup, 0, 0)); + tmp = mem_loc_descriptor (const0_rtx, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_eq, 0, 0)); + l2jump = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&ret, l2jump); + tmp = mem_loc_descriptor (GEN_INT (8), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + add_loc_descr (&ret, tmp); + add_loc_descr (&ret, new_loc_descr (DW_OP_minus, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + l1jump = new_loc_descr (DW_OP_skip, 0, 0); + add_loc_descr (&ret, l1jump); + l2label = new_loc_descr (DW_OP_drop, 0, 0); + add_loc_descr (&ret, l2label); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_drop, 0, 0)); + l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l1jump->dw_loc_oprnd1.v.val_loc = l1label; + l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc; + l2jump->dw_loc_oprnd1.v.val_loc = l2label; + return ret; } -/* Output debug info about reason why we failed to expand expression as dwarf - expression. */ +/* ROTATE (constMASK is mode mask, BITSIZE is bitsize of mode): + DW_OP_over DW_OP_over DW_OP_shl [ constMASK DW_OP_and ] DW_OP_rot + [ DW_OP_swap constMASK DW_OP_and DW_OP_swap ] DW_OP_neg + DW_OP_plus_uconst DW_OP_shr DW_OP_or -static void -expansion_failed (tree expr, rtx rtl, char const *reason) + ROTATERT is similar: + DW_OP_over DW_OP_over DW_OP_neg DW_OP_plus_uconst + DW_OP_shl [ constMASK DW_OP_and ] DW_OP_rot + [ DW_OP_swap constMASK DW_OP_and DW_OP_swap ] DW_OP_shr DW_OP_or */ + +static dw_loc_descr_ref +rotate_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode) { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Failed to expand as dwarf: "); - if (expr) - print_generic_expr (dump_file, expr, dump_flags); - if (rtl) - { - fprintf (dump_file, "\n"); - print_rtl (dump_file, rtl); - } - fprintf (dump_file, "\nReason: %s\n", reason); - } -} + rtx rtlop1 = XEXP (rtl, 1); + dw_loc_descr_ref op0, op1, ret, mask[2] = { NULL, NULL }; + int i; -/* Helper function for const_ok_for_output, called either directly - or via for_each_rtx. */ + if (GET_MODE_CLASS (mode) != MODE_INT) + return NULL; -static int -const_ok_for_output_1 (rtx *rtlp, void *data ATTRIBUTE_UNUSED) -{ - rtx rtl = *rtlp; + if (GET_MODE (rtlop1) != VOIDmode + && GET_MODE_BITSIZE (GET_MODE (rtlop1)) < GET_MODE_BITSIZE (mode)) + rtlop1 = gen_rtx_ZERO_EXTEND (mode, rtlop1); + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (rtlop1, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL || op1 == NULL) + return NULL; + if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE) + for (i = 0; i < 2; i++) + { + if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) + mask[i] = mem_loc_descriptor (GEN_INT (GET_MODE_MASK (mode)), + mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT) + mask[i] = new_loc_descr (HOST_BITS_PER_WIDE_INT == 32 + ? DW_OP_const4u + : HOST_BITS_PER_WIDE_INT == 64 + ? DW_OP_const8u : DW_OP_constu, + GET_MODE_MASK (mode), 0); + else + mask[i] = NULL; + if (mask[i] == NULL) + return NULL; + add_loc_descr (&mask[i], new_loc_descr (DW_OP_and, 0, 0)); + } + ret = op0; + add_loc_descr (&ret, op1); + add_loc_descr (&ret, new_loc_descr (DW_OP_over, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_over, 0, 0)); + if (GET_CODE (rtl) == ROTATERT) + { + add_loc_descr (&ret, new_loc_descr (DW_OP_neg, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_plus_uconst, + GET_MODE_BITSIZE (mode), 0)); + } + add_loc_descr (&ret, new_loc_descr (DW_OP_shl, 0, 0)); + if (mask[0] != NULL) + add_loc_descr (&ret, mask[0]); + add_loc_descr (&ret, new_loc_descr (DW_OP_rot, 0, 0)); + if (mask[1] != NULL) + { + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + add_loc_descr (&ret, mask[1]); + add_loc_descr (&ret, new_loc_descr (DW_OP_swap, 0, 0)); + } + if (GET_CODE (rtl) == ROTATE) + { + add_loc_descr (&ret, new_loc_descr (DW_OP_neg, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_plus_uconst, + GET_MODE_BITSIZE (mode), 0)); + } + add_loc_descr (&ret, new_loc_descr (DW_OP_shr, 0, 0)); + add_loc_descr (&ret, new_loc_descr (DW_OP_or, 0, 0)); + return ret; +} - if (GET_CODE (rtl) == UNSPEC) - { - /* If delegitimize_address couldn't do anything with the UNSPEC, assume - we can't express it in the debug info. */ -#ifdef ENABLE_CHECKING - inform (current_function_decl - ? DECL_SOURCE_LOCATION (current_function_decl) - : UNKNOWN_LOCATION, - "non-delegitimized UNSPEC %d found in variable location", - XINT (rtl, 1)); -#endif - expansion_failed (NULL_TREE, rtl, - "UNSPEC hasn't been delegitimized.\n"); - return 1; - } +/* Helper function for mem_loc_descriptor. Return DW_OP_GNU_parameter_ref + for DEBUG_PARAMETER_REF RTL. */ - if (GET_CODE (rtl) != SYMBOL_REF) - return 0; +static dw_loc_descr_ref +parameter_ref_descriptor (rtx rtl) +{ + dw_loc_descr_ref ret; + dw_die_ref ref; - if (CONSTANT_POOL_ADDRESS_P (rtl)) + if (dwarf_strict) + return NULL; + gcc_assert (TREE_CODE (DEBUG_PARAMETER_REF_DECL (rtl)) == PARM_DECL); + ref = lookup_decl_die (DEBUG_PARAMETER_REF_DECL (rtl)); + ret = new_loc_descr (DW_OP_GNU_parameter_ref, 0, 0); + if (ref) { - bool marked; - get_pool_constant_mark (rtl, &marked); - /* If all references to this pool constant were optimized away, - it was not output and thus we can't represent it. */ - if (!marked) - { - expansion_failed (NULL_TREE, rtl, - "Constant was removed from constant pool.\n"); - return 1; - } + ret->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + ret->dw_loc_oprnd1.v.val_die_ref.die = ref; + ret->dw_loc_oprnd1.v.val_die_ref.external = 0; } - - if (SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE) - return 1; - - /* Avoid references to external symbols in debug info, on several targets - the linker might even refuse to link when linking a shared library, - and in many other cases the relocations for .debug_info/.debug_loc are - dropped, so the address becomes zero anyway. Hidden symbols, guaranteed - to be defined within the same shared library or executable are fine. */ - if (SYMBOL_REF_EXTERNAL_P (rtl)) + else { - tree decl = SYMBOL_REF_DECL (rtl); - - if (decl == NULL || !targetm.binds_local_p (decl)) - { - expansion_failed (NULL_TREE, rtl, - "Symbol not defined in current TU.\n"); - return 1; - } + ret->dw_loc_oprnd1.val_class = dw_val_class_decl_ref; + ret->dw_loc_oprnd1.v.val_decl_ref = DEBUG_PARAMETER_REF_DECL (rtl); } - - return 0; + return ret; } -/* Return true if constant RTL can be emitted in DW_OP_addr or - DW_AT_const_value. TLS SYMBOL_REFs, external SYMBOL_REFs or - non-marked constant pool SYMBOL_REFs can't be referenced in it. */ +/* Helper function to get mode of MEM's address. */ -static bool -const_ok_for_output (rtx rtl) +enum machine_mode +get_address_mode (rtx mem) { - if (GET_CODE (rtl) == SYMBOL_REF) - return const_ok_for_output_1 (&rtl, NULL) == 0; - - if (GET_CODE (rtl) == CONST) - return for_each_rtx (&XEXP (rtl, 0), const_ok_for_output_1, NULL) == 0; - - return true; + enum machine_mode mode = GET_MODE (XEXP (mem, 0)); + if (mode != VOIDmode) + return mode; + return targetm.addr_space.address_mode (MEM_ADDR_SPACE (mem)); } /* The following routine converts the RTL for a variable or parameter @@ -13475,22 +11492,25 @@ const_ok_for_output (rtx rtl) equivalent. This routine recursively descends an RTL tree, turning it into Dwarf postfix code as it goes. - MODE is the mode of the memory reference, needed to handle some - autoincrement addressing modes. + MODE is the mode that should be assumed for the rtl if it is VOIDmode. - CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the - location list for RTL. + MEM_MODE is the mode of the memory reference, needed to handle some + autoincrement addressing modes. Return 0 if we can't represent the location. */ -static dw_loc_descr_ref +dw_loc_descr_ref mem_loc_descriptor (rtx rtl, enum machine_mode mode, + enum machine_mode mem_mode, enum var_init_status initialized) { dw_loc_descr_ref mem_loc_result = NULL; enum dwarf_location_atom op; dw_loc_descr_ref op0, op1; + if (mode == VOIDmode) + mode = GET_MODE (rtl); + /* Note that for a dynamically sized array, the location we will generate a description of here will be the lowest numbered location which is actually within the array. That's *not* necessarily the same as the @@ -13498,12 +11518,15 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, rtl = targetm.delegitimize_address (rtl); + if (mode != GET_MODE (rtl) && GET_MODE (rtl) != VOIDmode) + return NULL; + switch (GET_CODE (rtl)) { case POST_INC: case POST_DEC: case POST_MODIFY: - return mem_loc_descriptor (XEXP (rtl, 0), mode, initialized); + return mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, initialized); case SUBREG: /* The case of a subreg may arise when we have a local (register) @@ -13513,15 +11536,82 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, contains the given subreg. */ if (!subreg_lowpart_p (rtl)) break; - rtl = SUBREG_REG (rtl); - if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE) + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_CLASS (GET_MODE (SUBREG_REG (rtl))) == MODE_INT + && (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE +#ifdef POINTERS_EXTEND_UNSIGNED + || (mode == Pmode && mem_mode != VOIDmode) +#endif + ) + && GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl))) <= DWARF2_ADDR_SIZE) + { + mem_loc_result = mem_loc_descriptor (SUBREG_REG (rtl), + GET_MODE (SUBREG_REG (rtl)), + mem_mode, initialized); + break; + } + if (dwarf_strict) + break; + if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl)))) break; - if (GET_MODE_CLASS (GET_MODE (rtl)) != MODE_INT) + if (GET_MODE_SIZE (mode) != GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl))) + && (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE_CLASS (GET_MODE (SUBREG_REG (rtl))) != MODE_INT)) break; - mem_loc_result = mem_loc_descriptor (rtl, mode, initialized); + else + { + dw_die_ref type_die; + dw_loc_descr_ref cvt; + + mem_loc_result = mem_loc_descriptor (SUBREG_REG (rtl), + GET_MODE (SUBREG_REG (rtl)), + mem_mode, initialized); + if (mem_loc_result == NULL) + break; + type_die = base_type_for_mode (mode, + GET_MODE_CLASS (mode) == MODE_INT); + if (type_die == NULL) + { + mem_loc_result = NULL; + break; + } + if (GET_MODE_SIZE (mode) + != GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl)))) + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + else + cvt = new_loc_descr (DW_OP_GNU_reinterpret, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&mem_loc_result, cvt); + } break; case REG: + if (GET_MODE_CLASS (mode) != MODE_INT + || (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE +#ifdef POINTERS_EXTEND_UNSIGNED + && (mode != Pmode || mem_mode == VOIDmode) +#endif + )) + { + dw_die_ref type_die; + + if (dwarf_strict) + break; + if (REGNO (rtl) > FIRST_PSEUDO_REGISTER) + break; + type_die = base_type_for_mode (mode, + GET_MODE_CLASS (mode) == MODE_INT); + if (type_die == NULL) + break; + mem_loc_result = new_loc_descr (DW_OP_GNU_regval_type, + dbx_reg_number (rtl), 0); + mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_die_ref; + mem_loc_result->dw_loc_oprnd2.v.val_die_ref.die = type_die; + mem_loc_result->dw_loc_oprnd2.v.val_die_ref.external = 0; + break; + } /* Whenever a register number forms a part of the description of the method for calculating the (dynamic) address of a memory resident object, DWARF rules require the register number be referred to as @@ -13551,11 +11641,28 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case SIGN_EXTEND: case ZERO_EXTEND: - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, - VAR_INIT_STATUS_INITIALIZED); + if (GET_MODE_CLASS (mode) != MODE_INT) + break; + op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)), + mem_mode, VAR_INIT_STATUS_INITIALIZED); if (op0 == 0) break; - else + else if (GET_CODE (rtl) == ZERO_EXTEND + && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE + && GET_MODE_BITSIZE (GET_MODE (XEXP (rtl, 0))) + < HOST_BITS_PER_WIDE_INT + /* If DW_OP_const{1,2,4}u won't be used, it is shorter + to expand zero extend as two shifts instead of + masking. */ + && GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) <= 4) + { + enum machine_mode imode = GET_MODE (XEXP (rtl, 0)); + mem_loc_result = op0; + add_loc_descr (&mem_loc_result, + int_loc_descriptor (GET_MODE_MASK (imode))); + add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_and, 0, 0)); + } + else if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE) { int shift = DWARF2_ADDR_SIZE - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))); @@ -13570,39 +11677,80 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, add_loc_descr (&mem_loc_result, int_loc_descriptor (shift)); add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0)); } + else if (!dwarf_strict) + { + dw_die_ref type_die1, type_die2; + dw_loc_descr_ref cvt; + + type_die1 = base_type_for_mode (GET_MODE (XEXP (rtl, 0)), + GET_CODE (rtl) == ZERO_EXTEND); + if (type_die1 == NULL) + break; + type_die2 = base_type_for_mode (mode, 1); + if (type_die2 == NULL) + break; + mem_loc_result = op0; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die1; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&mem_loc_result, cvt); + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die2; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&mem_loc_result, cvt); + } break; case MEM: - mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl), + { + rtx new_rtl = avoid_constant_pool_reference (rtl); + if (new_rtl != rtl) + { + mem_loc_result = mem_loc_descriptor (new_rtl, mode, mem_mode, + initialized); + if (mem_loc_result != NULL) + return mem_loc_result; + } + } + mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), + get_address_mode (rtl), mode, VAR_INIT_STATUS_INITIALIZED); if (mem_loc_result == NULL) mem_loc_result = tls_mem_loc_descriptor (rtl); - if (mem_loc_result != 0) + if (mem_loc_result != NULL) { - if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE) + if (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE + || GET_MODE_CLASS (mode) != MODE_INT) { - expansion_failed (NULL_TREE, rtl, "DWARF address size mismatch"); - return 0; + dw_die_ref type_die; + dw_loc_descr_ref deref; + + if (dwarf_strict) + return NULL; + type_die + = base_type_for_mode (mode, GET_MODE_CLASS (mode) == MODE_INT); + if (type_die == NULL) + return NULL; + deref = new_loc_descr (DW_OP_GNU_deref_type, + GET_MODE_SIZE (mode), 0); + deref->dw_loc_oprnd2.val_class = dw_val_class_die_ref; + deref->dw_loc_oprnd2.v.val_die_ref.die = type_die; + deref->dw_loc_oprnd2.v.val_die_ref.external = 0; + add_loc_descr (&mem_loc_result, deref); } - else if (GET_MODE_SIZE (GET_MODE (rtl)) == DWARF2_ADDR_SIZE) + else if (GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE) add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0)); else add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref_size, - GET_MODE_SIZE (GET_MODE (rtl)), 0)); - } - else - { - rtx new_rtl = avoid_constant_pool_reference (rtl); - if (new_rtl != rtl) - return mem_loc_descriptor (new_rtl, mode, initialized); + GET_MODE_SIZE (mode), 0)); } break; case LO_SUM: - rtl = XEXP (rtl, 1); - - /* ... fall through ... */ + return mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, initialized); case LABEL_REF: /* Some ports can transform a symbol ref into a label ref, because @@ -13610,6 +11758,13 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, pool. */ case CONST: case SYMBOL_REF: + if (GET_MODE_CLASS (mode) != MODE_INT + || (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE +#ifdef POINTERS_EXTEND_UNSIGNED + && (mode != Pmode || mem_mode == VOIDmode) +#endif + )) + break; if (GET_CODE (rtl) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE) { @@ -13647,10 +11802,46 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case CONCAT: case CONCATN: case VAR_LOCATION: + case DEBUG_IMPLICIT_PTR: expansion_failed (NULL_TREE, rtl, "CONCAT/CONCATN/VAR_LOCATION is handled only by loc_descriptor"); return 0; + case ENTRY_VALUE: + if (dwarf_strict) + return NULL; + if (REG_P (ENTRY_VALUE_EXP (rtl))) + { + if (GET_MODE_CLASS (mode) != MODE_INT + || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE) + op0 = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), mode, + VOIDmode, VAR_INIT_STATUS_INITIALIZED); + else + op0 + = one_reg_loc_descriptor (dbx_reg_number (ENTRY_VALUE_EXP (rtl)), + VAR_INIT_STATUS_INITIALIZED); + } + else if (MEM_P (ENTRY_VALUE_EXP (rtl)) + && REG_P (XEXP (ENTRY_VALUE_EXP (rtl), 0))) + { + op0 = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), mode, + VOIDmode, VAR_INIT_STATUS_INITIALIZED); + if (op0 && op0->dw_loc_opc == DW_OP_fbreg) + return NULL; + } + else + gcc_unreachable (); + if (op0 == NULL) + return NULL; + mem_loc_result = new_loc_descr (DW_OP_GNU_entry_value, 0, 0); + mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_loc; + mem_loc_result->dw_loc_oprnd1.v.val_loc = op0; + break; + + case DEBUG_PARAMETER_REF: + mem_loc_result = parameter_ref_descriptor (rtl); + break; + case PRE_MODIFY: /* Extract the PLUS expression nested inside and fall into PLUS code below. */ @@ -13661,36 +11852,38 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case PRE_DEC: /* Turn these into a PLUS expression and fall into the PLUS code below. */ - rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0), + rtl = gen_rtx_PLUS (mode, XEXP (rtl, 0), GEN_INT (GET_CODE (rtl) == PRE_INC - ? GET_MODE_UNIT_SIZE (mode) - : -GET_MODE_UNIT_SIZE (mode))); + ? GET_MODE_UNIT_SIZE (mem_mode) + : -GET_MODE_UNIT_SIZE (mem_mode))); /* ... fall through ... */ case PLUS: plus: - if (is_based_loc (rtl)) + if (is_based_loc (rtl) + && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE + && GET_MODE_CLASS (mode) == MODE_INT) mem_loc_result = based_loc_descr (XEXP (rtl, 0), INTVAL (XEXP (rtl, 1)), VAR_INIT_STATUS_INITIALIZED); else { - mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode, + mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); if (mem_loc_result == 0) break; - if (CONST_INT_P (XEXP (rtl, 1))) + if (CONST_INT_P (XEXP (rtl, 1)) + && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE) loc_descr_plus_const (&mem_loc_result, INTVAL (XEXP (rtl, 1))); else { - dw_loc_descr_ref mem_loc_result2 - = mem_loc_descriptor (XEXP (rtl, 1), mode, - VAR_INIT_STATUS_INITIALIZED); - if (mem_loc_result2 == 0) + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op1 == 0) break; - add_loc_descr (&mem_loc_result, mem_loc_result2); + add_loc_descr (&mem_loc_result, op1); add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_plus, 0, 0)); } @@ -13708,6 +11901,15 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, goto do_binop; case DIV: + if (!dwarf_strict + && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE) + { + mem_loc_result = typed_binop (DW_OP_div, rtl, + base_type_for_mode (mode, 0), + mode, mem_mode); + break; + } op = DW_OP_div; goto do_binop; @@ -13717,15 +11919,38 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case ASHIFT: op = DW_OP_shl; - goto do_binop; + goto do_shift; case ASHIFTRT: op = DW_OP_shra; - goto do_binop; + goto do_shift; case LSHIFTRT: op = DW_OP_shr; - goto do_binop; + goto do_shift; + + do_shift: + if (GET_MODE_CLASS (mode) != MODE_INT) + break; + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + { + rtx rtlop1 = XEXP (rtl, 1); + if (GET_MODE (rtlop1) != VOIDmode + && GET_MODE_BITSIZE (GET_MODE (rtlop1)) + < GET_MODE_BITSIZE (mode)) + rtlop1 = gen_rtx_ZERO_EXTEND (mode, rtlop1); + op1 = mem_loc_descriptor (rtlop1, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + } + + if (op0 == 0 || op1 == 0) + break; + + mem_loc_result = op0; + add_loc_descr (&mem_loc_result, op1); + add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0)); + break; case AND: op = DW_OP_and; @@ -13740,9 +11965,9 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, goto do_binop; do_binop: - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); - op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); if (op0 == 0 || op1 == 0) @@ -13754,9 +11979,17 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, break; case MOD: - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, + if (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE && !dwarf_strict) + { + mem_loc_result = typed_binop (DW_OP_mod, rtl, + base_type_for_mode (mode, 0), + mode, mem_mode); + break; + } + + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); - op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); if (op0 == 0 || op1 == 0) @@ -13771,6 +12004,20 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_minus, 0, 0)); break; + case UDIV: + if (!dwarf_strict && GET_MODE_CLASS (mode) == MODE_INT) + { + if (GET_MODE_CLASS (mode) > DWARF2_ADDR_SIZE) + { + op = DW_OP_div; + goto do_binop; + } + mem_loc_result = typed_binop (DW_OP_div, rtl, + base_type_for_mode (mode, 1), + mode, mem_mode); + } + break; + case NOT: op = DW_OP_not; goto do_unop; @@ -13784,7 +12031,7 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, goto do_unop; do_unop: - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, + op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, VAR_INIT_STATUS_INITIALIZED); if (op0 == 0) @@ -13795,268 +12042,151 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, break; case CONST_INT: - mem_loc_result = int_loc_descriptor (INTVAL (rtl)); + if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE +#ifdef POINTERS_EXTEND_UNSIGNED + || (mode == Pmode + && mem_mode != VOIDmode + && trunc_int_for_mode (INTVAL (rtl), ptr_mode) == INTVAL (rtl)) +#endif + ) + { + mem_loc_result = int_loc_descriptor (INTVAL (rtl)); + break; + } + if (!dwarf_strict + && (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT + || GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT)) + { + dw_die_ref type_die = base_type_for_mode (mode, 1); + enum machine_mode amode; + if (type_die == NULL) + return NULL; + amode = mode_for_size (DWARF2_ADDR_SIZE * BITS_PER_UNIT, + MODE_INT, 0); + if (INTVAL (rtl) >= 0 + && amode != BLKmode + && trunc_int_for_mode (INTVAL (rtl), amode) == INTVAL (rtl) + /* const DW_OP_GNU_convert vs. + DW_OP_GNU_const_type . */ + && size_of_int_loc_descriptor (INTVAL (rtl)) + 1 + 1 + < (unsigned long) 1 + 1 + 1 + GET_MODE_SIZE (mode)) + { + mem_loc_result = int_loc_descriptor (INTVAL (rtl)); + op0 = new_loc_descr (DW_OP_GNU_convert, 0, 0); + op0->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + op0->dw_loc_oprnd1.v.val_die_ref.die = type_die; + op0->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&mem_loc_result, op0); + return mem_loc_result; + } + mem_loc_result = new_loc_descr (DW_OP_GNU_const_type, 0, + INTVAL (rtl)); + mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + mem_loc_result->dw_loc_oprnd1.v.val_die_ref.die = type_die; + mem_loc_result->dw_loc_oprnd1.v.val_die_ref.external = 0; + if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT) + mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_const; + else + { + mem_loc_result->dw_loc_oprnd2.val_class + = dw_val_class_const_double; + mem_loc_result->dw_loc_oprnd2.v.val_double + = shwi_to_double_int (INTVAL (rtl)); + } + } break; - case EQ: - op = DW_OP_eq; - goto do_scompare; - - case GE: - op = DW_OP_ge; - goto do_scompare; - - case GT: - op = DW_OP_gt; - goto do_scompare; - - case LE: - op = DW_OP_le; - goto do_scompare; + case CONST_DOUBLE: + if (!dwarf_strict) + { + dw_die_ref type_die; - case LT: - op = DW_OP_lt; - goto do_scompare; + /* Note that a CONST_DOUBLE rtx could represent either an integer + or a floating-point constant. A CONST_DOUBLE is used whenever + the constant requires more than one word in order to be + adequately represented. We output CONST_DOUBLEs as blocks. */ + if (mode == VOIDmode + || (GET_MODE (rtl) == VOIDmode + && GET_MODE_BITSIZE (mode) != 2 * HOST_BITS_PER_WIDE_INT)) + break; + type_die = base_type_for_mode (mode, + GET_MODE_CLASS (mode) == MODE_INT); + if (type_die == NULL) + return NULL; + mem_loc_result = new_loc_descr (DW_OP_GNU_const_type, 0, 0); + mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + mem_loc_result->dw_loc_oprnd1.v.val_die_ref.die = type_die; + mem_loc_result->dw_loc_oprnd1.v.val_die_ref.external = 0; + if (SCALAR_FLOAT_MODE_P (mode)) + { + unsigned int length = GET_MODE_SIZE (mode); + unsigned char *array + = (unsigned char*) ggc_alloc_atomic (length); - case NE: - op = DW_OP_ne; - goto do_scompare; + insert_float (rtl, array); + mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_vec; + mem_loc_result->dw_loc_oprnd2.v.val_vec.length = length / 4; + mem_loc_result->dw_loc_oprnd2.v.val_vec.elt_size = 4; + mem_loc_result->dw_loc_oprnd2.v.val_vec.array = array; + } + else + { + mem_loc_result->dw_loc_oprnd2.val_class + = dw_val_class_const_double; + mem_loc_result->dw_loc_oprnd2.v.val_double + = rtx_to_double_int (rtl); + } + } + break; - do_scompare: - if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE - || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 1))) > DWARF2_ADDR_SIZE) - break; - else - { - enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0)); + case EQ: + mem_loc_result = scompare_loc_descriptor (DW_OP_eq, rtl, mem_mode); + break; - if (op_mode == VOIDmode) - op_mode = GET_MODE (XEXP (rtl, 1)); - if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT) - break; + case GE: + mem_loc_result = scompare_loc_descriptor (DW_OP_ge, rtl, mem_mode); + break; - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, - VAR_INIT_STATUS_INITIALIZED); - op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, - VAR_INIT_STATUS_INITIALIZED); + case GT: + mem_loc_result = scompare_loc_descriptor (DW_OP_gt, rtl, mem_mode); + break; - if (op0 == 0 || op1 == 0) - break; + case LE: + mem_loc_result = scompare_loc_descriptor (DW_OP_le, rtl, mem_mode); + break; - if (op_mode != VOIDmode - && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE) - { - int shift = DWARF2_ADDR_SIZE - GET_MODE_SIZE (op_mode); - shift *= BITS_PER_UNIT; - /* For eq/ne, if the operands are known to be zero-extended, - there is no need to do the fancy shifting up. */ - if (op == DW_OP_eq || op == DW_OP_ne) - { - dw_loc_descr_ref last0, last1; - for (last0 = op0; - last0->dw_loc_next != NULL; - last0 = last0->dw_loc_next) - ; - for (last1 = op1; - last1->dw_loc_next != NULL; - last1 = last1->dw_loc_next) - ; - /* deref_size zero extends, and for constants we can check - whether they are zero extended or not. */ - if (((last0->dw_loc_opc == DW_OP_deref_size - && last0->dw_loc_oprnd1.v.val_int - <= GET_MODE_SIZE (op_mode)) - || (CONST_INT_P (XEXP (rtl, 0)) - && (unsigned HOST_WIDE_INT) INTVAL (XEXP (rtl, 0)) - == (INTVAL (XEXP (rtl, 0)) - & GET_MODE_MASK (op_mode)))) - && ((last1->dw_loc_opc == DW_OP_deref_size - && last1->dw_loc_oprnd1.v.val_int - <= GET_MODE_SIZE (op_mode)) - || (CONST_INT_P (XEXP (rtl, 1)) - && (unsigned HOST_WIDE_INT) - INTVAL (XEXP (rtl, 1)) - == (INTVAL (XEXP (rtl, 1)) - & GET_MODE_MASK (op_mode))))) - goto do_compare; - } - add_loc_descr (&op0, int_loc_descriptor (shift)); - add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0)); - if (CONST_INT_P (XEXP (rtl, 1))) - op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) << shift); - else - { - add_loc_descr (&op1, int_loc_descriptor (shift)); - add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0)); - } - } - } + case LT: + mem_loc_result = scompare_loc_descriptor (DW_OP_lt, rtl, mem_mode); + break; - do_compare: - mem_loc_result = op0; - add_loc_descr (&mem_loc_result, op1); - add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0)); - if (STORE_FLAG_VALUE != 1) - { - add_loc_descr (&mem_loc_result, - int_loc_descriptor (STORE_FLAG_VALUE)); - add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_mul, 0, 0)); - } + case NE: + mem_loc_result = scompare_loc_descriptor (DW_OP_ne, rtl, mem_mode); break; case GEU: - op = DW_OP_ge; - goto do_ucompare; + mem_loc_result = ucompare_loc_descriptor (DW_OP_ge, rtl, mem_mode); + break; case GTU: - op = DW_OP_gt; - goto do_ucompare; + mem_loc_result = ucompare_loc_descriptor (DW_OP_gt, rtl, mem_mode); + break; case LEU: - op = DW_OP_le; - goto do_ucompare; + mem_loc_result = ucompare_loc_descriptor (DW_OP_le, rtl, mem_mode); + break; case LTU: - op = DW_OP_lt; - goto do_ucompare; - - do_ucompare: - if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE - || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 1))) > DWARF2_ADDR_SIZE) - break; - else - { - enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0)); - - if (op_mode == VOIDmode) - op_mode = GET_MODE (XEXP (rtl, 1)); - if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT) - break; - - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, - VAR_INIT_STATUS_INITIALIZED); - op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, - VAR_INIT_STATUS_INITIALIZED); - - if (op0 == 0 || op1 == 0) - break; - - if (op_mode != VOIDmode - && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE) - { - HOST_WIDE_INT mask = GET_MODE_MASK (op_mode); - dw_loc_descr_ref last0, last1; - for (last0 = op0; - last0->dw_loc_next != NULL; - last0 = last0->dw_loc_next) - ; - for (last1 = op1; - last1->dw_loc_next != NULL; - last1 = last1->dw_loc_next) - ; - if (CONST_INT_P (XEXP (rtl, 0))) - op0 = int_loc_descriptor (INTVAL (XEXP (rtl, 0)) & mask); - /* deref_size zero extends, so no need to mask it again. */ - else if (last0->dw_loc_opc != DW_OP_deref_size - || last0->dw_loc_oprnd1.v.val_int - > GET_MODE_SIZE (op_mode)) - { - add_loc_descr (&op0, int_loc_descriptor (mask)); - add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0)); - } - if (CONST_INT_P (XEXP (rtl, 1))) - op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) & mask); - /* deref_size zero extends, so no need to mask it again. */ - else if (last1->dw_loc_opc != DW_OP_deref_size - || last1->dw_loc_oprnd1.v.val_int - > GET_MODE_SIZE (op_mode)) - { - add_loc_descr (&op1, int_loc_descriptor (mask)); - add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0)); - } - } - else - { - HOST_WIDE_INT bias = 1; - bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1); - add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0)); - if (CONST_INT_P (XEXP (rtl, 1))) - op1 = int_loc_descriptor ((unsigned HOST_WIDE_INT) bias - + INTVAL (XEXP (rtl, 1))); - else - add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst, - bias, 0)); - } - } - goto do_compare; + mem_loc_result = ucompare_loc_descriptor (DW_OP_lt, rtl, mem_mode); + break; - case SMIN: - case SMAX: case UMIN: case UMAX: - if (GET_MODE_CLASS (GET_MODE (XEXP (rtl, 0))) != MODE_INT - || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE - || GET_MODE (XEXP (rtl, 0)) != GET_MODE (XEXP (rtl, 1))) - break; - - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, - VAR_INIT_STATUS_INITIALIZED); - op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, - VAR_INIT_STATUS_INITIALIZED); - - if (op0 == 0 || op1 == 0) + if (GET_MODE_CLASS (mode) != MODE_INT) break; - - add_loc_descr (&op0, new_loc_descr (DW_OP_dup, 0, 0)); - add_loc_descr (&op1, new_loc_descr (DW_OP_swap, 0, 0)); - add_loc_descr (&op1, new_loc_descr (DW_OP_over, 0, 0)); - if (GET_CODE (rtl) == UMIN || GET_CODE (rtl) == UMAX) - { - if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE) - { - HOST_WIDE_INT mask = GET_MODE_MASK (GET_MODE (XEXP (rtl, 0))); - add_loc_descr (&op0, int_loc_descriptor (mask)); - add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0)); - add_loc_descr (&op1, int_loc_descriptor (mask)); - add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0)); - } - else - { - HOST_WIDE_INT bias = 1; - bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1); - add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0)); - add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst, bias, 0)); - } - } - else if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE) - { - int shift = DWARF2_ADDR_SIZE - - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))); - shift *= BITS_PER_UNIT; - add_loc_descr (&op0, int_loc_descriptor (shift)); - add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0)); - add_loc_descr (&op1, int_loc_descriptor (shift)); - add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0)); - } - - if (GET_CODE (rtl) == SMIN || GET_CODE (rtl) == UMIN) - op = DW_OP_lt; - else - op = DW_OP_gt; - mem_loc_result = op0; - add_loc_descr (&mem_loc_result, op1); - add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0)); - { - dw_loc_descr_ref bra_node, drop_node; - - bra_node = new_loc_descr (DW_OP_bra, 0, 0); - add_loc_descr (&mem_loc_result, bra_node); - add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_swap, 0, 0)); - drop_node = new_loc_descr (DW_OP_drop, 0, 0); - add_loc_descr (&mem_loc_result, drop_node); - bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc; - bra_node->dw_loc_oprnd1.v.val_loc = drop_node; - } + /* FALLTHRU */ + case SMIN: + case SMAX: + mem_loc_result = minmax_loc_descriptor (rtl, mode, mem_mode); break; case ZERO_EXTRACT: @@ -14065,13 +12195,14 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, && CONST_INT_P (XEXP (rtl, 2)) && ((unsigned) INTVAL (XEXP (rtl, 1)) + (unsigned) INTVAL (XEXP (rtl, 2)) - <= GET_MODE_BITSIZE (GET_MODE (rtl))) - && GET_MODE_BITSIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE - && GET_MODE_BITSIZE (GET_MODE (XEXP (rtl, 0))) <= DWARF2_ADDR_SIZE) + <= GET_MODE_BITSIZE (mode)) + && GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE + && GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) <= DWARF2_ADDR_SIZE) { int shift, size; - op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, - VAR_INIT_STATUS_INITIALIZED); + op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)), + mem_mode, VAR_INIT_STATUS_INITIALIZED); if (op0 == 0) break; if (GET_CODE (rtl) == SIGN_EXTRACT) @@ -14100,10 +12231,104 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, } break; - case COMPARE: case IF_THEN_ELSE: + { + dw_loc_descr_ref op2, bra_node, drop_node; + op0 = mem_loc_descriptor (XEXP (rtl, 0), + GET_MODE (XEXP (rtl, 0)) == VOIDmode + ? word_mode : GET_MODE (XEXP (rtl, 0)), + mem_mode, VAR_INIT_STATUS_INITIALIZED); + op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + op2 = mem_loc_descriptor (XEXP (rtl, 2), mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL || op1 == NULL || op2 == NULL) + break; + + mem_loc_result = op1; + add_loc_descr (&mem_loc_result, op2); + add_loc_descr (&mem_loc_result, op0); + bra_node = new_loc_descr (DW_OP_bra, 0, 0); + add_loc_descr (&mem_loc_result, bra_node); + add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_swap, 0, 0)); + drop_node = new_loc_descr (DW_OP_drop, 0, 0); + add_loc_descr (&mem_loc_result, drop_node); + bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc; + bra_node->dw_loc_oprnd1.v.val_loc = drop_node; + } + break; + + case FLOAT_EXTEND: + case FLOAT_TRUNCATE: + case FLOAT: + case UNSIGNED_FLOAT: + case FIX: + case UNSIGNED_FIX: + if (!dwarf_strict) + { + dw_die_ref type_die; + dw_loc_descr_ref cvt; + + op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)), + mem_mode, VAR_INIT_STATUS_INITIALIZED); + if (op0 == NULL) + break; + if (GET_MODE_CLASS (GET_MODE (XEXP (rtl, 0))) == MODE_INT + && (GET_CODE (rtl) == FLOAT + || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) + <= DWARF2_ADDR_SIZE)) + { + type_die = base_type_for_mode (GET_MODE (XEXP (rtl, 0)), + GET_CODE (rtl) == UNSIGNED_FLOAT); + if (type_die == NULL) + break; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op0, cvt); + } + type_die = base_type_for_mode (mode, GET_CODE (rtl) == UNSIGNED_FIX); + if (type_die == NULL) + break; + cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0); + cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die; + cvt->dw_loc_oprnd1.v.val_die_ref.external = 0; + add_loc_descr (&op0, cvt); + if (GET_MODE_CLASS (mode) == MODE_INT + && (GET_CODE (rtl) == FIX + || GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)) + { + op0 = convert_descriptor_to_mode (mode, op0); + if (op0 == NULL) + break; + } + mem_loc_result = op0; + } + break; + + case CLZ: + case CTZ: + case FFS: + mem_loc_result = clz_loc_descriptor (rtl, mode, mem_mode); + break; + + case POPCOUNT: + case PARITY: + mem_loc_result = popcount_loc_descriptor (rtl, mode, mem_mode); + break; + + case BSWAP: + mem_loc_result = bswap_loc_descriptor (rtl, mode, mem_mode); + break; + case ROTATE: case ROTATERT: + mem_loc_result = rotate_loc_descriptor (rtl, mode, mem_mode); + break; + + case COMPARE: case TRUNCATE: /* In theory, we could implement the above. */ /* DWARF cannot represent the unsigned compare operations @@ -14123,7 +12348,6 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case US_ASHIFT: case SS_TRUNCATE: case US_TRUNCATE: - case UDIV: case UNORDERED: case ORDERED: case UNEQ: @@ -14132,23 +12356,11 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case UNLE: case UNLT: case LTGT: - case FLOAT_EXTEND: - case FLOAT_TRUNCATE: - case FLOAT: - case UNSIGNED_FLOAT: - case FIX: - case UNSIGNED_FIX: case FRACT_CONVERT: case UNSIGNED_FRACT_CONVERT: case SAT_FRACT: case UNSIGNED_SAT_FRACT: case SQRT: - case BSWAP: - case FFS: - case CLZ: - case CTZ: - case POPCOUNT: - case PARITY: case ASM_OPERANDS: case VEC_MERGE: case VEC_SELECT: @@ -14156,6 +12368,11 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode, case VEC_DUPLICATE: case UNSPEC: case HIGH: + case FMA: + case STRICT_LOW_PART: + case CONST_VECTOR: + case CONST_FIXED: + case CLRSB: /* If delegitimize_address couldn't do anything with the UNSPEC, we can't express it in the debug info. This can happen e.g. with some TLS UNSPECs. */ @@ -14236,6 +12453,37 @@ concatn_loc_descriptor (rtx concatn, enum var_init_status initialized) return cc_loc_result; } +/* Helper function for loc_descriptor. Return DW_OP_GNU_implicit_pointer + for DEBUG_IMPLICIT_PTR RTL. */ + +static dw_loc_descr_ref +implicit_ptr_descriptor (rtx rtl, HOST_WIDE_INT offset) +{ + dw_loc_descr_ref ret; + dw_die_ref ref; + + if (dwarf_strict) + return NULL; + gcc_assert (TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == VAR_DECL + || TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == PARM_DECL + || TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == RESULT_DECL); + ref = lookup_decl_die (DEBUG_IMPLICIT_PTR_DECL (rtl)); + ret = new_loc_descr (DW_OP_GNU_implicit_pointer, 0, offset); + ret->dw_loc_oprnd2.val_class = dw_val_class_const; + if (ref) + { + ret->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + ret->dw_loc_oprnd1.v.val_die_ref.die = ref; + ret->dw_loc_oprnd1.v.val_die_ref.external = 0; + } + else + { + ret->dw_loc_oprnd1.val_class = dw_val_class_decl_ref; + ret->dw_loc_oprnd1.v.val_decl_ref = DEBUG_IMPLICIT_PTR_DECL (rtl); + } + return ret; +} + /* Output a proper Dwarf location descriptor for a variable or parameter which is either allocated in a register or in a memory location. For a register, we just generate an OP_REG and the register number. For a @@ -14262,7 +12510,11 @@ loc_descriptor (rtx rtl, enum machine_mode mode, up an entire register. For now, just assume that it is legitimate to make the Dwarf info refer to the whole register which contains the given subreg. */ - loc_result = loc_descriptor (SUBREG_REG (rtl), mode, initialized); + if (REG_P (SUBREG_REG (rtl)) && subreg_lowpart_p (rtl)) + loc_result = loc_descriptor (SUBREG_REG (rtl), + GET_MODE (SUBREG_REG (rtl)), initialized); + else + goto do_default; break; case REG: @@ -14270,8 +12522,8 @@ loc_descriptor (rtx rtl, enum machine_mode mode, break; case MEM: - loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl), - initialized); + loc_result = mem_loc_descriptor (XEXP (rtl, 0), get_address_mode (rtl), + GET_MODE (rtl), initialized); if (loc_result == NULL) loc_result = tls_mem_loc_descriptor (rtl); if (loc_result == NULL) @@ -14453,13 +12705,28 @@ loc_descriptor (rtx rtl, enum machine_mode mode, } break; + case DEBUG_IMPLICIT_PTR: + loc_result = implicit_ptr_descriptor (rtl, 0); + break; + + case PLUS: + if (GET_CODE (XEXP (rtl, 0)) == DEBUG_IMPLICIT_PTR + && CONST_INT_P (XEXP (rtl, 1))) + { + loc_result + = implicit_ptr_descriptor (XEXP (rtl, 0), INTVAL (XEXP (rtl, 1))); + break; + } + /* FALLTHRU */ + do_default: default: - if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE (rtl) == mode - && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE - && (dwarf_version >= 4 || !dwarf_strict)) + if ((GET_MODE_CLASS (mode) == MODE_INT && GET_MODE (rtl) == mode + && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE + && dwarf_version >= 4) + || (!dwarf_strict && mode != VOIDmode && mode != BLKmode)) { /* Value expression. */ - loc_result = mem_loc_descriptor (rtl, VOIDmode, initialized); + loc_result = mem_loc_descriptor (rtl, mode, VOIDmode, initialized); if (loc_result) add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0)); @@ -14534,18 +12801,20 @@ dw_loc_list_1 (tree loc, rtx varloc, int want_address, if (MEM_P (varloc)) { rtx addr = XEXP (varloc, 0); - descr = mem_loc_descriptor (addr, mode, initialized); + descr = mem_loc_descriptor (addr, get_address_mode (varloc), + mode, initialized); if (descr) have_address = 1; else { rtx x = avoid_constant_pool_reference (varloc); if (x != varloc) - descr = mem_loc_descriptor (x, mode, initialized); + descr = mem_loc_descriptor (x, mode, VOIDmode, + initialized); } } else - descr = mem_loc_descriptor (varloc, mode, initialized); + descr = mem_loc_descriptor (varloc, mode, VOIDmode, initialized); } else return 0; @@ -14734,7 +13003,7 @@ dw_sra_loc_expr (tree decl, rtx loc) if (MEM_P (varloc)) { unsigned HOST_WIDE_INT memsize - = INTVAL (MEM_SIZE (varloc)) * BITS_PER_UNIT; + = MEM_SIZE (varloc) * BITS_PER_UNIT; if (memsize != bitsize) { if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN @@ -14816,9 +13085,23 @@ dw_loc_list (var_loc_list *loc_list, tree decl, int want_address) } if (descr) { + bool range_across_switch = false; + /* If section switch happens in between node->label + and node->next->label (or end of function) and + we can't emit it as a single entry list, + emit two ranges, first one ending at the end + of first partition and second one starting at the + beginning of second partition. */ + if (node == loc_list->last_before_switch + && (node != loc_list->first || loc_list->first->next) + && current_function_decl) + { + endname = cfun->fde->dw_fde_end; + range_across_switch = true; + } /* The variable has a location between NODE->LABEL and NODE->NEXT->LABEL. */ - if (node->next) + else if (node->next) endname = node->next->label; /* If the variable has a location at the last label it keeps its location until the end of function. */ @@ -14832,7 +13115,36 @@ dw_loc_list (var_loc_list *loc_list, tree decl, int want_address) } *listp = new_loc_list (descr, node->label, endname, secname); + if (TREE_CODE (decl) == PARM_DECL + && node == loc_list->first + && GET_CODE (node->loc) == NOTE + && strcmp (node->label, endname) == 0) + (*listp)->force = true; listp = &(*listp)->dw_loc_next; + + if (range_across_switch) + { + if (GET_CODE (node->loc) == EXPR_LIST) + descr = dw_sra_loc_expr (decl, node->loc); + else + { + initialized = NOTE_VAR_LOCATION_STATUS (node->loc); + varloc = NOTE_VAR_LOCATION (node->loc); + descr = dw_loc_list_1 (decl, varloc, want_address, + initialized); + } + gcc_assert (descr); + /* The variable has a location between NODE->LABEL and + NODE->NEXT->LABEL. */ + if (node->next) + endname = node->next->label; + else + endname = cfun->fde->dw_fde_second_end; + *listp = new_loc_list (descr, + cfun->fde->dw_fde_second_begin, + endname, secname); + listp = &(*listp)->dw_loc_next; + } } } @@ -14929,7 +13241,6 @@ cst_pool_loc_descr (tree loc) { /* Get an RTL for this, if something has been emitted. */ rtx rtl = lookup_constant_def (loc); - enum machine_mode mode; if (!rtl || !MEM_P (rtl)) { @@ -14947,9 +13258,8 @@ cst_pool_loc_descr (tree loc) "CST value in contant pool but not marked."); return 0; } - mode = GET_MODE (rtl); - rtl = XEXP (rtl, 0); - return mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED); + return mem_loc_descriptor (XEXP (rtl, 0), get_address_mode (rtl), + GET_MODE (rtl), VAR_INIT_STATUS_INITIALIZED); } /* Return dw_loc_list representing address of addr_expr LOC @@ -15151,12 +13461,12 @@ loc_list_from_tree (tree loc, int want_address) /* FALLTHRU */ case PARM_DECL: + case RESULT_DECL: if (DECL_HAS_VALUE_EXPR_P (loc)) return loc_list_from_tree (DECL_VALUE_EXPR (loc), want_address); /* FALLTHRU */ - case RESULT_DECL: case FUNCTION_DECL: { rtx rtl; @@ -15194,7 +13504,7 @@ loc_list_from_tree (tree loc, int want_address) } else { - enum machine_mode mode; + enum machine_mode mode, mem_mode; /* Certain constructs can only be represented at top-level. */ if (want_address == 2) @@ -15206,12 +13516,16 @@ loc_list_from_tree (tree loc, int want_address) else { mode = GET_MODE (rtl); + mem_mode = VOIDmode; if (MEM_P (rtl)) { + mem_mode = mode; + mode = get_address_mode (rtl); rtl = XEXP (rtl, 0); have_address = 1; } - ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED); + ret = mem_loc_descriptor (rtl, mode, mem_mode, + VAR_INIT_STATUS_INITIALIZED); } if (!ret) expansion_failed (loc, rtl, @@ -15226,7 +13540,6 @@ loc_list_from_tree (tree loc, int want_address) return 0; /* Fallthru. */ case INDIRECT_REF: - case MISALIGNED_INDIRECT_REF: list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0); have_address = 1; break; @@ -15839,10 +14152,22 @@ add_AT_location_description (dw_die_ref die, enum dwarf_attribute attr_kind, static void add_accessibility_attribute (dw_die_ref die, tree decl) { + /* In DWARF3+ the default is DW_ACCESS_private only in DW_TAG_class_type + children, otherwise the default is DW_ACCESS_public. In DWARF2 + the default has always been DW_ACCESS_public. */ if (TREE_PROTECTED (decl)) add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected); else if (TREE_PRIVATE (decl)) - add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_private); + { + if (dwarf_version == 2 + || die->die_parent == NULL + || die->die_parent->die_tag != DW_TAG_class_type) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_private); + } + else if (dwarf_version > 2 + && die->die_parent + && die->die_parent->die_tag == DW_TAG_class_type) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); } /* Attach the specialized form of location attribute used for data members of @@ -16220,6 +14545,8 @@ rtl_for_decl_init (tree init, tree type) { rtx rtl = NULL_RTX; + STRIP_NOPS (init); + /* If a variable is initialized with a string constant without embedded zeros, build CONST_STRING. */ if (TREE_CODE (init) == STRING_CST && TREE_CODE (type) == ARRAY_TYPE) @@ -16244,11 +14571,15 @@ rtl_for_decl_init (tree init, tree type) } /* Other aggregates, and complex values, could be represented using CONCAT: FIXME! */ - else if (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE) + else if (AGGREGATE_TYPE_P (type) + || (TREE_CODE (init) == VIEW_CONVERT_EXPR + && AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (init, 0)))) + || TREE_CODE (type) == COMPLEX_TYPE) ; /* Vectors only work if their mode is supported by the target. FIXME: generic vectors ought to work too. */ - else if (TREE_CODE (type) == VECTOR_TYPE && TYPE_MODE (type) == BLKmode) + else if (TREE_CODE (type) == VECTOR_TYPE + && !VECTOR_MODE_P (TYPE_MODE (type))) ; /* If the initializer is something that we know will expand into an immediate RTL constant, expand it now. We must be careful not to @@ -16402,7 +14733,13 @@ rtl_for_decl_location (tree decl) } else if (TREE_CODE (decl) == PARM_DECL) { - if (rtl == NULL_RTX || is_pseudo_reg (rtl)) + if (rtl == NULL_RTX + || is_pseudo_reg (rtl) + || (MEM_P (rtl) + && is_pseudo_reg (XEXP (rtl, 0)) + && DECL_INCOMING_RTL (decl) + && MEM_P (DECL_INCOMING_RTL (decl)) + && GET_MODE (rtl) == GET_MODE (DECL_INCOMING_RTL (decl)))) { tree declared_type = TREE_TYPE (decl); tree passed_type = DECL_ARG_TYPE (decl); @@ -16414,7 +14751,8 @@ rtl_for_decl_location (tree decl) all cases where (rtl == NULL_RTX) just below. */ if (dmode == pmode) rtl = DECL_INCOMING_RTL (decl); - else if (SCALAR_INT_MODE_P (dmode) + else if ((rtl == NULL_RTX || is_pseudo_reg (rtl)) + && SCALAR_INT_MODE_P (dmode) && GET_MODE_SIZE (dmode) <= GET_MODE_SIZE (pmode) && DECL_INCOMING_RTL (decl)) { @@ -16449,7 +14787,7 @@ rtl_for_decl_location (tree decl) && (!REG_P (XEXP (rtl, 0)) || REGNO (XEXP (rtl, 0)) == HARD_FRAME_POINTER_REGNUM || REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM -#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM +#if !HARD_FRAME_POINTER_IS_ARG_POINTER || REGNO (XEXP (rtl, 0)) == ARG_POINTER_REGNUM #endif ) @@ -16580,15 +14918,22 @@ fortran_common (tree decl, HOST_WIDE_INT *value) these things can crop up in other ways also.) Note that one type of constant value which can be passed into an inlined function is a constant pointer. This can happen for example if an actual argument in an inlined - function call evaluates to a compile-time constant address. */ + function call evaluates to a compile-time constant address. + + CACHE_P is true if it is worth caching the location list for DECL, + so that future calls can reuse it rather than regenerate it from scratch. + This is true for BLOCK_NONLOCALIZED_VARS in inlined subroutines, + since we will need to refer to them each time the function is inlined. */ static bool -add_location_or_const_value_attribute (dw_die_ref die, tree decl, +add_location_or_const_value_attribute (dw_die_ref die, tree decl, bool cache_p, enum dwarf_attribute attr) { rtx rtl; dw_loc_list_ref list; var_loc_list *loc_list; + cached_dw_loc_list *cache; + void **slot; if (TREE_CODE (decl) == ERROR_MARK) return false; @@ -16625,7 +14970,33 @@ add_location_or_const_value_attribute (dw_die_ref die, tree decl, && add_const_value_attribute (die, rtl)) return true; } - list = loc_list_from_tree (decl, decl_by_reference_p (decl) ? 0 : 2); + /* If this decl is from BLOCK_NONLOCALIZED_VARS, we might need its + list several times. See if we've already cached the contents. */ + list = NULL; + if (loc_list == NULL || cached_dw_loc_list_table == NULL) + cache_p = false; + if (cache_p) + { + cache = (cached_dw_loc_list *) + htab_find_with_hash (cached_dw_loc_list_table, decl, DECL_UID (decl)); + if (cache) + list = cache->loc_list; + } + if (list == NULL) + { + list = loc_list_from_tree (decl, decl_by_reference_p (decl) ? 0 : 2); + /* It is usually worth caching this result if the decl is from + BLOCK_NONLOCALIZED_VARS and if the list has at least two elements. */ + if (cache_p && list && list->dw_loc_next) + { + slot = htab_find_slot_with_hash (cached_dw_loc_list_table, decl, + DECL_UID (decl), INSERT); + cache = ggc_alloc_cleared_cached_dw_loc_list (); + cache->decl_id = DECL_UID (decl); + cache->loc_list = list; + *slot = cache; + } + } if (list) { add_AT_location_description (die, attr, list); @@ -16705,9 +15076,7 @@ native_encode_initializer (tree init, unsigned char *array, int size) min_index = tree_low_cst (TYPE_MIN_VALUE (TYPE_DOMAIN (type)), 0); memset (array, '\0', size); - for (cnt = 0; - VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce); - cnt++) + FOR_EACH_VEC_ELT (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce) { tree val = ce->value; tree index = ce->index; @@ -16729,7 +15098,7 @@ native_encode_initializer (tree init, unsigned char *array, int size) { int count = tree_low_cst (TREE_OPERAND (index, 1), 0) - tree_low_cst (TREE_OPERAND (index, 0), 0); - while (count > 0) + while (count-- > 0) { if (val) memcpy (array + curpos, array + pos, fieldsize); @@ -16753,9 +15122,7 @@ native_encode_initializer (tree init, unsigned char *array, int size) if (TREE_CODE (type) == RECORD_TYPE) field = TYPE_FIELDS (type); - for (cnt = 0; - VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce); - cnt++, field = field ? DECL_CHAIN (field) : 0) + FOR_EACH_VEC_ELT (constructor_elt, CONSTRUCTOR_ELTS (init), cnt, ce) { tree val = ce->value; int pos, fieldsize; @@ -16844,7 +15211,9 @@ tree_add_const_value_attribute_for_decl (dw_die_ref var_die, tree decl) if (!decl || (TREE_CODE (decl) != VAR_DECL - && TREE_CODE (decl) != CONST_DECL)) + && TREE_CODE (decl) != CONST_DECL) + || (TREE_CODE (decl) == VAR_DECL + && !TREE_STATIC (decl))) return false; if (TREE_READONLY (decl) @@ -16870,6 +15239,7 @@ tree_add_const_value_attribute_for_decl (dw_die_ref var_die, tree decl) static dw_loc_list_ref convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) { + int ix; dw_fde_ref fde; dw_loc_list_ref list, *list_tail; dw_cfi_ref cfi; @@ -16877,7 +15247,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) const char *start_label, *last_label, *section; dw_cfa_location remember; - fde = current_fde (); + fde = cfun->fde; gcc_assert (fde != NULL); section = secname_for_decl (current_function_decl); @@ -16892,39 +15262,67 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) /* ??? Bald assumption that the CIE opcode list does not contain advance opcodes. */ - for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next) + FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, ix, cfi) lookup_cfa_1 (cfi, &next_cfa, &remember); last_cfa = next_cfa; last_label = start_label; - for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next) - switch (cfi->dw_cfi_opc) - { - case DW_CFA_set_loc: - case DW_CFA_advance_loc1: - case DW_CFA_advance_loc2: - case DW_CFA_advance_loc4: - if (!cfa_equal_p (&last_cfa, &next_cfa)) - { - *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), - start_label, last_label, section); + if (fde->dw_fde_second_begin && fde->dw_fde_switch_cfi_index == 0) + { + /* If the first partition contained no CFI adjustments, the + CIE opcodes apply to the whole first partition. */ + *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), + fde->dw_fde_begin, fde->dw_fde_end, section); + list_tail =&(*list_tail)->dw_loc_next; + start_label = last_label = fde->dw_fde_second_begin; + } - list_tail = &(*list_tail)->dw_loc_next; - last_cfa = next_cfa; - start_label = last_label; - } - last_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; - break; + FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi) + { + switch (cfi->dw_cfi_opc) + { + case DW_CFA_set_loc: + case DW_CFA_advance_loc1: + case DW_CFA_advance_loc2: + case DW_CFA_advance_loc4: + if (!cfa_equal_p (&last_cfa, &next_cfa)) + { + *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), + start_label, last_label, section); - case DW_CFA_advance_loc: - /* The encoding is complex enough that we should never emit this. */ - gcc_unreachable (); + list_tail = &(*list_tail)->dw_loc_next; + last_cfa = next_cfa; + start_label = last_label; + } + last_label = cfi->dw_cfi_oprnd1.dw_cfi_addr; + break; - default: - lookup_cfa_1 (cfi, &next_cfa, &remember); - break; - } + case DW_CFA_advance_loc: + /* The encoding is complex enough that we should never emit this. */ + gcc_unreachable (); + + default: + lookup_cfa_1 (cfi, &next_cfa, &remember); + break; + } + if (ix + 1 == fde->dw_fde_switch_cfi_index) + { + if (!cfa_equal_p (&last_cfa, &next_cfa)) + { + *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), + start_label, last_label, section); + + list_tail = &(*list_tail)->dw_loc_next; + last_cfa = next_cfa; + start_label = last_label; + } + *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset), + start_label, fde->dw_fde_end, section); + list_tail = &(*list_tail)->dw_loc_next; + start_label = last_label = fde->dw_fde_second_begin; + } + } if (!cfa_equal_p (&last_cfa, &next_cfa)) { @@ -16935,7 +15333,10 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) } *list_tail = new_loc_list (build_cfa_loc (&next_cfa, offset), - start_label, fde->dw_fde_end, section); + start_label, + fde->dw_fde_second_begin + ? fde->dw_fde_second_end : fde->dw_fde_end, + section); if (list && list->dw_loc_next) gen_llsym (list); @@ -16968,14 +15369,20 @@ compute_frame_pointer_to_fb_displacement (HOST_WIDE_INT offset) elim = XEXP (elim, 0); } - gcc_assert ((SUPPORTS_STACK_ALIGNMENT - && (elim == hard_frame_pointer_rtx - || elim == stack_pointer_rtx)) - || elim == (frame_pointer_needed - ? hard_frame_pointer_rtx - : stack_pointer_rtx)); - frame_pointer_fb_offset = -offset; + + /* ??? AVR doesn't set up valid eliminations when there is no stack frame + in which to eliminate. This is because it's stack pointer isn't + directly accessible as a register within the ISA. To work around + this, assume that while we cannot provide a proper value for + frame_pointer_fb_offset, we won't need one either. */ + frame_pointer_fb_offset_valid + = ((SUPPORTS_STACK_ALIGNMENT + && (elim == hard_frame_pointer_rtx + || elim == stack_pointer_rtx)) + || elim == (frame_pointer_needed + ? hard_frame_pointer_rtx + : stack_pointer_rtx)); } /* Generate a DW_AT_name attribute given some string value to be included as @@ -16993,6 +15400,42 @@ add_name_attribute (dw_die_ref die, const char *name_string) } } +/* Retrieve the descriptive type of TYPE, if any, make sure it has a + DIE and attach a DW_AT_GNAT_descriptive_type attribute to the DIE + of TYPE accordingly. + + ??? This is a temporary measure until after we're able to generate + regular DWARF for the complex Ada type system. */ + +static void +add_gnat_descriptive_type_attribute (dw_die_ref die, tree type, + dw_die_ref context_die) +{ + tree dtype; + dw_die_ref dtype_die; + + if (!lang_hooks.types.descriptive_type) + return; + + dtype = lang_hooks.types.descriptive_type (type); + if (!dtype) + return; + + dtype_die = lookup_type_die (dtype); + if (!dtype_die) + { + /* The descriptive type indirectly references TYPE if this is also the + case for TYPE itself. Do not deal with the circularity here. */ + TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type)) = 1; + gen_type_die (dtype, context_die); + TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type)) = 0; + dtype_die = lookup_type_die (dtype); + gcc_assert (dtype_die); + } + + add_AT_die_ref (die, DW_AT_GNAT_descriptive_type, dtype_die); +} + /* Generate a DW_AT_comp_dir attribute for DIE. */ static void @@ -17025,7 +15468,7 @@ add_comp_dir_attribute (dw_die_ref die) static int lower_bound_default (void) { - switch (get_AT_unsigned (comp_unit_die, DW_AT_language)) + switch (get_AT_unsigned (comp_unit_die (), DW_AT_language)) { case DW_LANG_C: case DW_LANG_C89: @@ -17154,7 +15597,7 @@ add_bound_info (dw_die_ref subrange_die, enum dwarf_attribute bound_attr, tree b break; if (current_function_decl == 0) - ctx = comp_unit_die; + ctx = comp_unit_die (); else ctx = lookup_decl_die (current_function_decl); @@ -17291,7 +15734,7 @@ add_bit_offset_attribute (dw_die_ref die, tree decl) HOST_WIDE_INT bitpos_int; HOST_WIDE_INT highest_order_object_bit_offset; HOST_WIDE_INT highest_order_field_bit_offset; - HOST_WIDE_INT unsigned bit_offset; + HOST_WIDE_INT bit_offset; /* Must be a field and a bit field. */ gcc_assert (type && TREE_CODE (decl) == FIELD_DECL); @@ -17324,7 +15767,10 @@ add_bit_offset_attribute (dw_die_ref die, tree decl) ? highest_order_object_bit_offset - highest_order_field_bit_offset : highest_order_field_bit_offset - highest_order_object_bit_offset); - add_AT_unsigned (die, DW_AT_bit_offset, bit_offset); + if (bit_offset < 0) + add_AT_int (die, DW_AT_bit_offset, bit_offset); + else + add_AT_unsigned (die, DW_AT_bit_offset, (unsigned HOST_WIDE_INT) bit_offset); } /* For a FIELD_DECL node which represents a bit field, output an attribute @@ -17347,8 +15793,8 @@ add_bit_size_attribute (dw_die_ref die, tree decl) static inline void add_prototyped_attribute (dw_die_ref die, tree func_type) { - if (get_AT_unsigned (comp_unit_die, DW_AT_language) == DW_LANG_C89 - && TYPE_ARG_TYPES (func_type) != NULL) + if (get_AT_unsigned (comp_unit_die (), DW_AT_language) == DW_LANG_C89 + && prototype_p (func_type)) add_AT_flag (die, DW_AT_prototyped, 1); } @@ -17445,8 +15891,11 @@ add_linkage_attr (dw_die_ref die, tree decl) static void add_src_coords_attributes (dw_die_ref die, tree decl) { - expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl)); + expanded_location s; + if (DECL_SOURCE_LOCATION (decl) == UNKNOWN_LOCATION) + return; + s = expand_location (DECL_SOURCE_LOCATION (decl)); add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file)); add_AT_unsigned (die, DW_AT_decl_line, s.line); } @@ -17527,17 +15976,17 @@ dwarf2out_vms_debug_main_pointer (void) current_function_funcdef_no); add_AT_lbl_id (die, DW_AT_entry_pc, label); - /* Make it the first child of comp_unit_die. */ - die->die_parent = comp_unit_die; - if (comp_unit_die->die_child) + /* Make it the first child of comp_unit_die (). */ + die->die_parent = comp_unit_die (); + if (comp_unit_die ()->die_child) { - die->die_sib = comp_unit_die->die_child->die_sib; - comp_unit_die->die_child->die_sib = die; + die->die_sib = comp_unit_die ()->die_child->die_sib; + comp_unit_die ()->die_child->die_sib = die; } else { die->die_sib = die; - comp_unit_die->die_child = die; + comp_unit_die ()->die_child = die; } } #endif /* VMS_DEBUGGING_INFO */ @@ -17591,8 +16040,8 @@ scope_die_for (tree t, dw_die_ref context_die) if (containing_scope && TREE_CODE (containing_scope) == FUNCTION_TYPE) containing_scope = NULL_TREE; - if (containing_scope == NULL_TREE) - scope_die = comp_unit_die; + if (SCOPE_FILE_SCOPE_P (containing_scope)) + scope_die = comp_unit_die (); else if (TYPE_P (containing_scope)) { /* For types, we can just look up the appropriate DIE. But @@ -17612,10 +16061,10 @@ scope_die_for (tree t, dw_die_ref context_die) /* If none of the current dies are suitable, we get file scope. */ if (scope_die == NULL) - scope_die = comp_unit_die; + scope_die = comp_unit_die (); } else - scope_die = lookup_type_die (containing_scope); + scope_die = lookup_type_die_strip_naming_typedef (containing_scope); } else scope_die = context_die; @@ -17703,14 +16152,21 @@ add_calling_convention_attribute (dw_die_ref subr_die, tree decl) value = ((enum dwarf_calling_convention) targetm.dwarf_calling_convention (TREE_TYPE (decl))); - /* DWARF doesn't provide a way to identify a program's source-level - entry point. DW_AT_calling_convention attributes are only meant - to describe functions' calling conventions. However, lacking a - better way to signal the Fortran main program, we use this for the - time being, following existing custom. */ if (is_fortran () && !strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), "MAIN__")) - value = DW_CC_program; + { + /* DWARF 2 doesn't provide a way to identify a program's source-level + entry point. DW_AT_calling_convention attributes are only meant + to describe functions' calling conventions. However, lacking a + better way to signal the Fortran main program, we used this for + a long time, following existing custom. Now, DWARF 4 has + DW_AT_main_subprogram, which we add below, but some tools still + rely on the old way, which we thus keep. */ + value = DW_CC_program; + + if (dwarf_version >= 4 || !dwarf_strict) + add_AT_flag (subr_die, DW_AT_main_subprogram, 1); + } /* Only add the attribute if the backend requests it, and is not DW_CC_normal. */ @@ -17859,15 +16315,13 @@ gen_array_type_die (tree type, dw_die_ref context_die) array_die = new_die (DW_TAG_array_type, scope_die, type); add_name_attribute (array_die, type_tag (type)); + add_gnat_descriptive_type_attribute (array_die, type, context_die); + if (TYPE_ARTIFICIAL (type)) + add_AT_flag (array_die, DW_AT_artificial, 1); equate_type_number_to_die (type, array_die); if (TREE_CODE (type) == VECTOR_TYPE) - { - /* The frontend feeds us a representation for the vector as a struct - containing an array. Pull out the array type. */ - type = TREE_TYPE (TYPE_FIELDS (TYPE_DEBUG_REPRESENTATION_TYPE (type))); - add_AT_flag (array_die, DW_AT_GNU_vector, 1); - } + add_AT_flag (array_die, DW_AT_GNU_vector, 1); /* For Fortran multidimensional arrays use DW_ORD_col_major ordering. */ if (is_fortran () @@ -17890,10 +16344,20 @@ gen_array_type_die (tree type, dw_die_ref context_die) #ifdef MIPS_DEBUGGING_INFO /* The SGI compilers handle arrays of unknown bound by setting AT_declaration and not emitting any subrange DIEs. */ - if (! TYPE_DOMAIN (type)) + if (TREE_CODE (type) == ARRAY_TYPE + && ! TYPE_DOMAIN (type)) add_AT_flag (array_die, DW_AT_declaration, 1); else #endif + if (TREE_CODE (type) == VECTOR_TYPE) + { + /* For VECTOR_TYPEs we use an array die with appropriate bounds. */ + dw_die_ref subrange_die = new_die (DW_TAG_subrange_type, array_die, NULL); + add_bound_info (subrange_die, DW_AT_lower_bound, size_zero_node); + add_bound_info (subrange_die, DW_AT_upper_bound, + size_int (TYPE_VECTOR_SUBPARTS (type) - 1)); + } + else add_subscript_info (array_die, type, collapse_nested_arrays); /* Add representation of the type of the elements of this array type and @@ -18111,7 +16575,7 @@ retry_incomplete_types (void) for (i = VEC_length (tree, incomplete_types) - 1; i >= 0; i--) if (should_emit_struct_debug (VEC_index (tree, incomplete_types, i), DINFO_USAGE_DIR_USE)) - gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die); + gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die ()); } /* Determine what tag to use for a record type. */ @@ -18156,9 +16620,16 @@ gen_enumeration_type_die (tree type, dw_die_ref context_die) scope_die_for (type, context_die), type); equate_type_number_to_die (type, type_die); add_name_attribute (type_die, type_tag (type)); - if ((dwarf_version >= 4 || !dwarf_strict) - && ENUM_IS_SCOPED (type)) - add_AT_flag (type_die, DW_AT_enum_class, 1); + add_gnat_descriptive_type_attribute (type_die, type, context_die); + if (TYPE_ARTIFICIAL (type)) + add_AT_flag (type_die, DW_AT_artificial, 1); + if (dwarf_version >= 4 || !dwarf_strict) + { + if (ENUM_IS_SCOPED (type)) + add_AT_flag (type_die, DW_AT_enum_class, 1); + if (ENUM_IS_OPAQUE (type)) + add_AT_flag (type_die, DW_AT_declaration, 1); + } } else if (! TYPE_SIZE (type)) return type_die; @@ -18274,7 +16745,7 @@ gen_formal_parameter_die (tree node, tree origin, bool emit_name_p, equate_decl_number_to_die (node, parm_die); if (! DECL_ABSTRACT (node_or_origin)) add_location_or_const_value_attribute (parm_die, node_or_origin, - DW_AT_location); + node == NULL, DW_AT_location); break; @@ -18426,7 +16897,7 @@ gen_type_die_for_member (tree type, tree member, dw_die_ref context_die) gcc_assert (!decl_ultimate_origin (member)); push_decl_scope (type); - type_die = lookup_type_die (type); + type_die = lookup_type_die_strip_naming_typedef (type); if (TREE_CODE (member) == FUNCTION_DECL) gen_subprogram_die (member, type_die); else if (TREE_CODE (member) == FIELD_DECL) @@ -18459,6 +16930,9 @@ dwarf2out_abstract_function (tree decl) tree context; int was_abstract; htab_t old_decl_loc_table; + htab_t old_cached_dw_loc_list_table; + int old_call_site_count, old_tail_call_site_count; + struct call_arg_loc_node *old_call_arg_locations; /* Make sure we have the actual abstract inline, not a clone. */ decl = DECL_ORIGIN (decl); @@ -18473,6 +16947,14 @@ dwarf2out_abstract_function (tree decl) get locations in abstract instantces. */ old_decl_loc_table = decl_loc_table; decl_loc_table = NULL; + old_cached_dw_loc_list_table = cached_dw_loc_list_table; + cached_dw_loc_list_table = NULL; + old_call_arg_locations = call_arg_locations; + call_arg_locations = NULL; + old_call_site_count = call_site_count; + call_site_count = -1; + old_tail_call_site_count = tail_call_site_count; + tail_call_site_count = -1; /* Be sure we've emitted the in-class declaration DIE (if any) first, so we don't get confused by DECL_ABSTRACT. */ @@ -18481,7 +16963,7 @@ dwarf2out_abstract_function (tree decl) context = decl_class_context (decl); if (context) gen_type_die_for_member - (context, decl, decl_function_context (decl) ? NULL : comp_unit_die); + (context, decl, decl_function_context (decl) ? NULL : comp_unit_die ()); } /* Pretend we've just finished compiling this function. */ @@ -18497,6 +16979,10 @@ dwarf2out_abstract_function (tree decl) current_function_decl = save_fn; decl_loc_table = old_decl_loc_table; + cached_dw_loc_list_table = old_cached_dw_loc_list_table; + call_arg_locations = old_call_arg_locations; + call_site_count = old_call_site_count; + tail_call_site_count = old_tail_call_site_count; pop_cfun (); } @@ -18572,16 +17058,51 @@ premark_types_used_by_global_vars (void) premark_types_used_by_global_vars_helper, NULL); } +/* Generate a DW_TAG_GNU_call_site DIE in function DECL under SUBR_DIE + for CA_LOC call arg loc node. */ + +static dw_die_ref +gen_call_site_die (tree decl, dw_die_ref subr_die, + struct call_arg_loc_node *ca_loc) +{ + dw_die_ref stmt_die = NULL, die; + tree block = ca_loc->block; + + while (block + && block != DECL_INITIAL (decl) + && TREE_CODE (block) == BLOCK) + { + if (VEC_length (dw_die_ref, block_map) > BLOCK_NUMBER (block)) + stmt_die = VEC_index (dw_die_ref, block_map, BLOCK_NUMBER (block)); + if (stmt_die) + break; + block = BLOCK_SUPERCONTEXT (block); + } + if (stmt_die == NULL) + stmt_die = subr_die; + die = new_die (DW_TAG_GNU_call_site, stmt_die, NULL_TREE); + add_AT_lbl_id (die, DW_AT_low_pc, ca_loc->label); + if (ca_loc->tail_call_p) + add_AT_flag (die, DW_AT_GNU_tail_call, 1); + if (ca_loc->symbol_ref) + { + dw_die_ref tdie = lookup_decl_die (SYMBOL_REF_DECL (ca_loc->symbol_ref)); + if (tdie) + add_AT_die_ref (die, DW_AT_abstract_origin, tdie); + else + add_AT_addr (die, DW_AT_abstract_origin, ca_loc->symbol_ref); + } + return die; +} + /* Generate a DIE to represent a declared function (either file-scope or block-local). */ static void gen_subprogram_die (tree decl, dw_die_ref context_die) { - char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; tree origin = decl_ultimate_origin (decl); dw_die_ref subr_die; - tree fn_arg_types; tree outer_scope; dw_die_ref old_die = lookup_decl_die (decl); int declaration = (current_function_decl != decl @@ -18621,6 +17142,12 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) subr_die = new_die (DW_TAG_subprogram, context_die, decl); add_abstract_origin_attribute (subr_die, origin); + /* This is where the actual code for a cloned function is. + Let's emit linkage name attribute for it. This helps + debuggers to e.g, set breakpoints into + constructors/destructors when the user asks "break + K::K". */ + add_linkage_name (subr_die, decl); } else if (old_die) { @@ -18645,7 +17172,7 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) instances of inlines, since the spec requires the out-of-line copy to have the same parent. For local class methods, this doesn't apply; we just use the old DIE. */ - if ((old_die->die_parent == comp_unit_die || context_die == NULL) + if ((is_cu_die (old_die->die_parent) || context_die == NULL) && (DECL_ARTIFICIAL (decl) || (get_AT_file (old_die, DW_AT_decl_file) == file_index && (get_AT_unsigned (old_die, DW_AT_decl_line) @@ -18748,12 +17275,24 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) if (!flag_reorder_blocks_and_partition) { - ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL, - current_function_funcdef_no); - add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id); - ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, - current_function_funcdef_no); - add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id); + dw_fde_ref fde = cfun->fde; + if (fde->dw_fde_begin) + { + /* We have already generated the labels. */ + add_AT_lbl_id (subr_die, DW_AT_low_pc, fde->dw_fde_begin); + add_AT_lbl_id (subr_die, DW_AT_high_pc, fde->dw_fde_end); + } + else + { + /* Create start/end labels and add the range. */ + char label_id[MAX_ARTIFICIAL_LABEL_BYTES]; + ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_BEGIN_LABEL, + current_function_funcdef_no); + add_AT_lbl_id (subr_die, DW_AT_low_pc, label_id); + ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL, + current_function_funcdef_no); + add_AT_lbl_id (subr_die, DW_AT_high_pc, label_id); + } #if VMS_DEBUGGING_INFO /* HP OpenVMS Industry Standard 64: DWARF Extensions @@ -18769,8 +17308,6 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) attributes allow a compiler to communicate the location(s) to use. */ { - dw_fde_ref fde = &fde_table[current_funcdef_fde]; - if (fde->dw_fde_vms_end_prologue) add_AT_vms_delta (subr_die, DW_AT_HP_prologue, fde->dw_fde_begin, fde->dw_fde_vms_end_prologue); @@ -18782,27 +17319,92 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) #endif add_pubname (decl, subr_die); - add_arange (decl, subr_die); } else - { /* Do nothing for now; maybe need to duplicate die, one for - hot section and one for cold section, then use the hot/cold - section begin/end labels to generate the aranges... */ - /* - add_AT_lbl_id (subr_die, DW_AT_low_pc, hot_section_label); - add_AT_lbl_id (subr_die, DW_AT_high_pc, hot_section_end_label); - add_AT_lbl_id (subr_die, DW_AT_lo_user, unlikely_section_label); - add_AT_lbl_id (subr_die, DW_AT_hi_user, cold_section_end_label); - - add_pubname (decl, subr_die); - add_arange (decl, subr_die); - add_arange (decl, subr_die); - */ + { + /* Generate pubnames entries for the split function code ranges. */ + dw_fde_ref fde = cfun->fde; + + if (fde->dw_fde_second_begin) + { + if (dwarf_version >= 3 || !dwarf_strict) + { + /* We should use ranges for non-contiguous code section + addresses. Use the actual code range for the initial + section, since the HOT/COLD labels might precede an + alignment offset. */ + bool range_list_added = false; + add_ranges_by_labels (subr_die, fde->dw_fde_begin, + fde->dw_fde_end, &range_list_added); + add_ranges_by_labels (subr_die, fde->dw_fde_second_begin, + fde->dw_fde_second_end, + &range_list_added); + add_pubname (decl, subr_die); + if (range_list_added) + add_ranges (NULL); + } + else + { + /* There is no real support in DW2 for this .. so we make + a work-around. First, emit the pub name for the segment + containing the function label. Then make and emit a + simplified subprogram DIE for the second segment with the + name pre-fixed by __hot/cold_sect_of_. We use the same + linkage name for the second die so that gdb will find both + sections when given "b foo". */ + const char *name = NULL; + tree decl_name = DECL_NAME (decl); + dw_die_ref seg_die; + + /* Do the 'primary' section. */ + add_AT_lbl_id (subr_die, DW_AT_low_pc, + fde->dw_fde_begin); + add_AT_lbl_id (subr_die, DW_AT_high_pc, + fde->dw_fde_end); + /* Add it. */ + add_pubname (decl, subr_die); + + /* Build a minimal DIE for the secondary section. */ + seg_die = new_die (DW_TAG_subprogram, + subr_die->die_parent, decl); + + if (TREE_PUBLIC (decl)) + add_AT_flag (seg_die, DW_AT_external, 1); + + if (decl_name != NULL + && IDENTIFIER_POINTER (decl_name) != NULL) + { + name = dwarf2_name (decl, 1); + if (! DECL_ARTIFICIAL (decl)) + add_src_coords_attributes (seg_die, decl); + + add_linkage_name (seg_die, decl); + } + gcc_assert (name != NULL); + add_pure_or_virtual_attribute (seg_die, decl); + if (DECL_ARTIFICIAL (decl)) + add_AT_flag (seg_die, DW_AT_artificial, 1); + + name = concat ("__second_sect_of_", name, NULL); + add_AT_lbl_id (seg_die, DW_AT_low_pc, + fde->dw_fde_second_begin); + add_AT_lbl_id (seg_die, DW_AT_high_pc, + fde->dw_fde_second_end); + add_name_attribute (seg_die, name); + add_pubname_string (name, seg_die); + } + } + else + { + add_AT_lbl_id (subr_die, DW_AT_low_pc, fde->dw_fde_begin); + add_AT_lbl_id (subr_die, DW_AT_high_pc, fde->dw_fde_end); + add_pubname (decl, subr_die); + } } #ifdef MIPS_DEBUGGING_INFO /* Add a reference to the FDE for this routine. */ - add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, current_funcdef_fde); + add_AT_fde_ref (subr_die, DW_AT_MIPS_fde, cfun->fde->fde_index); #endif cfa_fb_offset = CFA_FRAME_BASE_OFFSET (decl); @@ -18919,11 +17521,10 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) void_type_node 2) an unprototyped function declaration (not a definition). This just means that we have no info about the parameters at all. */ - fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); - if (fn_arg_types != NULL) + if (prototype_p (TREE_TYPE (decl))) { /* This is the prototyped case, check for.... */ - if (TREE_VALUE (tree_last (fn_arg_types)) != void_type_node) + if (stdarg_p (TREE_TYPE (decl))) gen_unspecified_parameters_die (decl, subr_die); } else if (DECL_INITIAL (decl) == NULL_TREE) @@ -18950,6 +17551,9 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) constructor function. */ if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK) { + int call_site_note_count = 0; + int tail_call_site_note_count = 0; + /* Emit a DW_TAG_variable DIE for a named return value. */ if (DECL_NAME (DECL_RESULT (decl))) gen_decl_die (DECL_RESULT (decl), NULL, subr_die); @@ -18957,17 +17561,154 @@ gen_subprogram_die (tree decl, dw_die_ref context_die) current_function_has_inlines = 0; decls_for_scope (outer_scope, subr_die, 0); -#if 0 && defined (MIPS_DEBUGGING_INFO) - if (current_function_has_inlines) + if (call_arg_locations && !dwarf_strict) { - add_AT_flag (subr_die, DW_AT_MIPS_has_inlines, 1); - if (! comp_unit_has_inlines) + struct call_arg_loc_node *ca_loc; + for (ca_loc = call_arg_locations; ca_loc; ca_loc = ca_loc->next) { - add_AT_flag (comp_unit_die, DW_AT_MIPS_has_inlines, 1); - comp_unit_has_inlines = 1; + dw_die_ref die = NULL; + rtx tloc = NULL_RTX, tlocc = NULL_RTX; + rtx arg, next_arg; + + for (arg = NOTE_VAR_LOCATION (ca_loc->call_arg_loc_note); + arg; arg = next_arg) + { + dw_loc_descr_ref reg, val; + enum machine_mode mode = GET_MODE (XEXP (XEXP (arg, 0), 1)); + dw_die_ref cdie, tdie = NULL; + + next_arg = XEXP (arg, 1); + if (REG_P (XEXP (XEXP (arg, 0), 0)) + && next_arg + && MEM_P (XEXP (XEXP (next_arg, 0), 0)) + && REG_P (XEXP (XEXP (XEXP (next_arg, 0), 0), 0)) + && REGNO (XEXP (XEXP (arg, 0), 0)) + == REGNO (XEXP (XEXP (XEXP (next_arg, 0), 0), 0))) + next_arg = XEXP (next_arg, 1); + if (mode == VOIDmode) + { + mode = GET_MODE (XEXP (XEXP (arg, 0), 0)); + if (mode == VOIDmode) + mode = GET_MODE (XEXP (arg, 0)); + } + if (mode == VOIDmode || mode == BLKmode) + continue; + if (XEXP (XEXP (arg, 0), 0) == pc_rtx) + { + gcc_assert (ca_loc->symbol_ref == NULL_RTX); + tloc = XEXP (XEXP (arg, 0), 1); + continue; + } + else if (GET_CODE (XEXP (XEXP (arg, 0), 0)) == CLOBBER + && XEXP (XEXP (XEXP (arg, 0), 0), 0) == pc_rtx) + { + gcc_assert (ca_loc->symbol_ref == NULL_RTX); + tlocc = XEXP (XEXP (arg, 0), 1); + continue; + } + reg = NULL; + if (REG_P (XEXP (XEXP (arg, 0), 0))) + reg = reg_loc_descriptor (XEXP (XEXP (arg, 0), 0), + VAR_INIT_STATUS_INITIALIZED); + else if (MEM_P (XEXP (XEXP (arg, 0), 0))) + { + rtx mem = XEXP (XEXP (arg, 0), 0); + reg = mem_loc_descriptor (XEXP (mem, 0), + get_address_mode (mem), + GET_MODE (mem), + VAR_INIT_STATUS_INITIALIZED); + } + else if (GET_CODE (XEXP (XEXP (arg, 0), 0)) + == DEBUG_PARAMETER_REF) + { + tree tdecl + = DEBUG_PARAMETER_REF_DECL (XEXP (XEXP (arg, 0), 0)); + tdie = lookup_decl_die (tdecl); + if (tdie == NULL) + continue; + } + else + continue; + if (reg == NULL + && GET_CODE (XEXP (XEXP (arg, 0), 0)) + != DEBUG_PARAMETER_REF) + continue; + val = mem_loc_descriptor (XEXP (XEXP (arg, 0), 1), mode, + VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (val == NULL) + continue; + if (die == NULL) + die = gen_call_site_die (decl, subr_die, ca_loc); + cdie = new_die (DW_TAG_GNU_call_site_parameter, die, + NULL_TREE); + if (reg != NULL) + add_AT_loc (cdie, DW_AT_location, reg); + else if (tdie != NULL) + add_AT_die_ref (cdie, DW_AT_abstract_origin, tdie); + add_AT_loc (cdie, DW_AT_GNU_call_site_value, val); + if (next_arg != XEXP (arg, 1)) + { + mode = GET_MODE (XEXP (XEXP (XEXP (arg, 1), 0), 1)); + if (mode == VOIDmode) + mode = GET_MODE (XEXP (XEXP (XEXP (arg, 1), 0), 0)); + val = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 1), + 0), 1), + mode, VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (val != NULL) + add_AT_loc (cdie, DW_AT_GNU_call_site_data_value, val); + } + } + if (die == NULL + && (ca_loc->symbol_ref || tloc)) + die = gen_call_site_die (decl, subr_die, ca_loc); + if (die != NULL && (tloc != NULL_RTX || tlocc != NULL_RTX)) + { + dw_loc_descr_ref tval = NULL; + + if (tloc != NULL_RTX) + tval = mem_loc_descriptor (tloc, + GET_MODE (tloc) == VOIDmode + ? Pmode : GET_MODE (tloc), + VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (tval) + add_AT_loc (die, DW_AT_GNU_call_site_target, tval); + else if (tlocc != NULL_RTX) + { + tval = mem_loc_descriptor (tlocc, + GET_MODE (tlocc) == VOIDmode + ? Pmode : GET_MODE (tlocc), + VOIDmode, + VAR_INIT_STATUS_INITIALIZED); + if (tval) + add_AT_loc (die, DW_AT_GNU_call_site_target_clobbered, + tval); + } + } + if (die != NULL) + { + call_site_note_count++; + if (ca_loc->tail_call_p) + tail_call_site_note_count++; + } } } -#endif + call_arg_locations = NULL; + call_arg_loc_last = NULL; + if (tail_call_site_count >= 0 + && tail_call_site_count == tail_call_site_note_count + && !dwarf_strict) + { + if (call_site_count >= 0 + && call_site_count == call_site_note_count) + add_AT_flag (subr_die, DW_AT_GNU_all_call_sites, 1); + else + add_AT_flag (subr_die, DW_AT_GNU_all_tail_call_sites, 1); + } + call_site_count = -1; + tail_call_site_count = -1; } /* Add the calling convention attribute if requested. */ add_calling_convention_attribute (subr_die, decl); @@ -19202,7 +17943,7 @@ gen_variable_die (tree decl, tree origin, dw_die_ref context_die) if (declaration) add_AT_flag (var_die, DW_AT_declaration, 1); - if (decl && (DECL_ABSTRACT (decl) || declaration)) + if (decl && (DECL_ABSTRACT (decl) || declaration || old_die == NULL)) equate_decl_number_to_die (decl, var_die); if (! declaration @@ -19221,9 +17962,8 @@ gen_variable_die (tree decl, tree origin, dw_die_ref context_die) && !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl_or_origin))) defer_location (decl_or_origin, var_die); else - add_location_or_const_value_attribute (var_die, - decl_or_origin, - DW_AT_location); + add_location_or_const_value_attribute (var_die, decl_or_origin, + decl == NULL, DW_AT_location); add_pubname (decl_or_origin, var_die); } else @@ -19287,6 +18027,14 @@ gen_label_die (tree decl, dw_die_ref context_die) ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (insn)); add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); } + else if (insn + && NOTE_P (insn) + && NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL + && CODE_LABEL_NUMBER (insn) != -1) + { + ASM_GENERATE_INTERNAL_LABEL (label, "LDL", CODE_LABEL_NUMBER (insn)); + add_AT_lbl_id (lbl_die, DW_AT_low_pc, label); + } } } @@ -19356,6 +18104,14 @@ gen_lexical_block_die (tree stmt, dw_die_ref context_die, int depth) { dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt); + if (call_arg_locations) + { + if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt)) + VEC_safe_grow_cleared (dw_die_ref, heap, block_map, + BLOCK_NUMBER (stmt) + 1); + VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), stmt_die); + } + if (! BLOCK_ABSTRACT (stmt) && TREE_ASM_WRITTEN (stmt)) add_high_low_attributes (stmt, stmt_die); @@ -19386,6 +18142,13 @@ gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die, int depth) dw_die_ref subr_die = new_die (DW_TAG_inlined_subroutine, context_die, stmt); + if (call_arg_locations) + { + if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt)) + VEC_safe_grow_cleared (dw_die_ref, heap, block_map, + BLOCK_NUMBER (stmt) + 1); + VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), subr_die); + } add_abstract_origin_attribute (subr_die, decl); if (TREE_ASM_WRITTEN (stmt)) add_high_low_attributes (stmt, subr_die); @@ -19484,13 +18247,124 @@ gen_ptr_to_mbr_type_die (tree type, dw_die_ref context_die) add_type_attribute (ptr_die, TREE_TYPE (type), 0, 0, context_die); } +typedef const char *dchar_p; /* For DEF_VEC_P. */ +DEF_VEC_P(dchar_p); +DEF_VEC_ALLOC_P(dchar_p,heap); + +static char *producer_string; + +/* Return a heap allocated producer string including command line options + if -grecord-gcc-switches. */ + +static char * +gen_producer_string (void) +{ + size_t j; + VEC(dchar_p, heap) *switches = NULL; + const char *language_string = lang_hooks.name; + char *producer, *tail; + const char *p; + size_t len = dwarf_record_gcc_switches ? 0 : 3; + size_t plen = strlen (language_string) + 1 + strlen (version_string); + + for (j = 1; dwarf_record_gcc_switches && j < save_decoded_options_count; j++) + switch (save_decoded_options[j].opt_index) + { + case OPT_o: + case OPT_d: + case OPT_dumpbase: + case OPT_dumpdir: + case OPT_auxbase: + case OPT_auxbase_strip: + case OPT_quiet: + case OPT_version: + case OPT_v: + case OPT_w: + case OPT_L: + case OPT_D: + case OPT_I: + case OPT_U: + case OPT_SPECIAL_unknown: + case OPT_SPECIAL_ignore: + case OPT_SPECIAL_program_name: + case OPT_SPECIAL_input_file: + case OPT_grecord_gcc_switches: + case OPT_gno_record_gcc_switches: + case OPT__output_pch_: + case OPT_fdiagnostics_show_location_: + case OPT_fdiagnostics_show_option: + case OPT_fverbose_asm: + case OPT____: + case OPT__sysroot_: + case OPT_nostdinc: + case OPT_nostdinc__: + /* Ignore these. */ + continue; + default: + gcc_checking_assert (save_decoded_options[j].canonical_option[0][0] + == '-'); + switch (save_decoded_options[j].canonical_option[0][1]) + { + case 'M': + case 'i': + case 'W': + continue; + case 'f': + if (strncmp (save_decoded_options[j].canonical_option[0] + 2, + "dump", 4) == 0) + continue; + break; + default: + break; + } + VEC_safe_push (dchar_p, heap, switches, + save_decoded_options[j].orig_option_with_args_text); + len += strlen (save_decoded_options[j].orig_option_with_args_text) + 1; + break; + } + + producer = XNEWVEC (char, plen + 1 + len + 1); + tail = producer; + sprintf (tail, "%s %s", language_string, version_string); + tail += plen; + + if (!dwarf_record_gcc_switches) + { +#ifdef MIPS_DEBUGGING_INFO + /* The MIPS/SGI compilers place the 'cc' command line options in the + producer string. The SGI debugger looks for -g, -g1, -g2, or -g3; + if they do not appear in the producer string, the debugger reaches + the conclusion that the object file is stripped and has no debugging + information. To get the MIPS/SGI debugger to believe that there is + debugging information in the object file, we add a -g to the producer + string. */ + if (debug_info_level > DINFO_LEVEL_TERSE) + { + memcpy (tail, " -g", 3); + tail += 3; + } +#endif + } + + FOR_EACH_VEC_ELT (dchar_p, switches, j, p) + { + len = strlen (p); + *tail = ' '; + memcpy (tail + 1, p, len); + tail += len + 1; + } + + *tail = '\0'; + VEC_free (dchar_p, heap, switches); + return producer; +} + /* Generate the DIE for the compilation unit. */ static dw_die_ref gen_compile_unit_die (const char *filename) { dw_die_ref die; - char producer[250]; const char *language_string = lang_hooks.name; int language; @@ -19504,20 +18378,41 @@ gen_compile_unit_die (const char *filename) add_comp_dir_attribute (die); } - sprintf (producer, "%s %s", language_string, version_string); + if (producer_string == NULL) + producer_string = gen_producer_string (); + add_AT_string (die, DW_AT_producer, producer_string); -#ifdef MIPS_DEBUGGING_INFO - /* The MIPS/SGI compilers place the 'cc' command line options in the producer - string. The SGI debugger looks for -g, -g1, -g2, or -g3; if they do - not appear in the producer string, the debugger reaches the conclusion - that the object file is stripped and has no debugging information. - To get the MIPS/SGI debugger to believe that there is debugging - information in the object file, we add a -g to the producer string. */ - if (debug_info_level > DINFO_LEVEL_TERSE) - strcat (producer, " -g"); -#endif + /* If our producer is LTO try to figure out a common language to use + from the global list of translation units. */ + if (strcmp (language_string, "GNU GIMPLE") == 0) + { + unsigned i; + tree t; + const char *common_lang = NULL; + + FOR_EACH_VEC_ELT (tree, all_translation_units, i, t) + { + if (!TRANSLATION_UNIT_LANGUAGE (t)) + continue; + if (!common_lang) + common_lang = TRANSLATION_UNIT_LANGUAGE (t); + else if (strcmp (common_lang, TRANSLATION_UNIT_LANGUAGE (t)) == 0) + ; + else if (strncmp (common_lang, "GNU C", 5) == 0 + && strncmp (TRANSLATION_UNIT_LANGUAGE (t), "GNU C", 5) == 0) + /* Mixing C and C++ is ok, use C++ in that case. */ + common_lang = "GNU C++"; + else + { + /* Fall back to C. */ + common_lang = NULL; + break; + } + } - add_AT_string (die, DW_AT_producer, producer); + if (common_lang) + language_string = common_lang; + } language = DW_LANG_C89; if (strcmp (language_string, "GNU C++") == 0) @@ -19538,6 +18433,11 @@ gen_compile_unit_die (const char *filename) language = DW_LANG_ObjC; else if (strcmp (language_string, "GNU Objective-C++") == 0) language = DW_LANG_ObjC_plus_plus; + else if (dwarf_version >= 5 || !dwarf_strict) + { + if (strcmp (language_string, "GNU Go") == 0) + language = DW_LANG_Go; + } } add_AT_unsigned (die, DW_AT_language, language); @@ -19571,10 +18471,20 @@ gen_inheritance_die (tree binfo, tree access, dw_die_ref context_die) if (BINFO_VIRTUAL_P (binfo)) add_AT_unsigned (die, DW_AT_virtuality, DW_VIRTUALITY_virtual); + /* In DWARF3+ the default is DW_ACCESS_private only in DW_TAG_class_type + children, otherwise the default is DW_ACCESS_public. In DWARF2 + the default has always been DW_ACCESS_private. */ if (access == access_public_node) - add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); + { + if (dwarf_version == 2 + || context_die->die_tag == DW_TAG_class_type) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_public); + } else if (access == access_protected_node) add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_protected); + else if (dwarf_version > 2 + && context_die->die_tag != DW_TAG_class_type) + add_AT_unsigned (die, DW_AT_accessibility, DW_ACCESS_private); } /* Generate a DIE for a class member. */ @@ -19668,7 +18578,7 @@ gen_struct_or_union_type_die (tree type, dw_die_ref context_die, scope_die = scope_die_for (type, context_die); - if (! type_die || (nested && scope_die == comp_unit_die)) + if (! type_die || (nested && is_cu_die (scope_die))) /* First occurrence of type or toplevel definition of nested class. */ { dw_die_ref old_die = type_die; @@ -19680,7 +18590,12 @@ gen_struct_or_union_type_die (tree type, dw_die_ref context_die, if (old_die) add_AT_specification (type_die, old_die); else - add_name_attribute (type_die, type_tag (type)); + { + add_name_attribute (type_die, type_tag (type)); + add_gnat_descriptive_type_attribute (type_die, type, context_die); + if (TYPE_ARTIFICIAL (type)) + add_AT_flag (type_die, DW_AT_artificial, 1); + } } else remove_AT (type_die, DW_AT_declaration); @@ -19688,7 +18603,7 @@ gen_struct_or_union_type_die (tree type, dw_die_ref context_die, /* Generate child dies for template paramaters. */ if (debug_info_level > DINFO_LEVEL_TERSE && COMPLETE_TYPE_P (type)) - gen_generic_params_dies (type); + schedule_generic_params_dies_gen (type); /* If this type has been completed, then give it a byte_size attribute and then give a list of members. */ @@ -19806,6 +18721,14 @@ gen_typedef_die (tree decl, dw_die_ref context_die) anonymous struct DIE. */ if (!TREE_ASM_WRITTEN (type)) gen_tagged_type_die (type, context_die, DINFO_USAGE_DIR_USE); + + /* This is a GNU Extension. We are adding a + DW_AT_linkage_name attribute to the DIE of the + anonymous struct TYPE. The value of that attribute + is the name of the typedef decl naming the anonymous + struct. This greatly eases the work of consumers of + this debug info. */ + add_linkage_attr (lookup_type_die (type), decl); } } @@ -19868,6 +18791,10 @@ gen_tagged_type_die (tree type, out yet, use a NULL context for now; it will be fixed up in decls_for_scope. */ context_die = lookup_decl_die (TYPE_CONTEXT (type)); + /* A declaration DIE doesn't count; nested types need to go in the + specification. */ + if (context_die && is_declaration_die (context_die)) + context_die = NULL; need_pop = 0; } else @@ -19898,13 +18825,23 @@ gen_tagged_type_die (tree type, static void gen_type_die_with_usage (tree type, dw_die_ref context_die, - enum debug_info_usage usage) + enum debug_info_usage usage) { struct array_descr_info info; if (type == NULL_TREE || type == error_mark_node) return; + if (TYPE_NAME (type) != NULL_TREE + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL + && is_redundant_typedef (TYPE_NAME (type)) + && DECL_ORIGINAL_TYPE (TYPE_NAME (type))) + /* The DECL of this type is a typedef we don't want to emit debug + info for but we want debug info for its underlying typedef. + This can happen for e.g, the injected-class-name of a C++ + type. */ + type = DECL_ORIGINAL_TYPE (TYPE_NAME (type)); + /* If TYPE is a typedef type variant, let's generate debug info for the parent typedef which TYPE is a type of. */ if (typedef_variant_p (type)) @@ -19917,8 +18854,9 @@ gen_type_die_with_usage (tree type, dw_die_ref context_die, /* Use the DIE of the containing namespace as the parent DIE of the type description DIE we want to generate. */ - if (DECL_CONTEXT (TYPE_NAME (type)) - && TREE_CODE (DECL_CONTEXT (TYPE_NAME (type))) == NAMESPACE_DECL) + if (DECL_FILE_SCOPE_P (TYPE_NAME (type)) + || (DECL_CONTEXT (TYPE_NAME (type)) + && TREE_CODE (DECL_CONTEXT (TYPE_NAME (type))) == NAMESPACE_DECL)) context_die = get_context_die (DECL_CONTEXT (TYPE_NAME (type))); TREE_ASM_WRITTEN (type) = 1; @@ -20036,6 +18974,7 @@ gen_type_die_with_usage (tree type, dw_die_ref context_die, /* No DIEs needed for fundamental types. */ break; + case NULLPTR_TYPE: case LANG_TYPE: /* Just use DW_TAG_unspecified_type. */ { @@ -20045,7 +18984,7 @@ gen_type_die_with_usage (tree type, dw_die_ref context_die, tree name = TYPE_NAME (type); if (TREE_CODE (name) == TYPE_DECL) name = DECL_NAME (name); - type_die = new_die (DW_TAG_unspecified_type, comp_unit_die, type); + type_die = new_die (DW_TAG_unspecified_type, comp_unit_die (), type); add_name_attribute (type_die, IDENTIFIER_POINTER (name)); equate_type_number_to_die (type, type_die); } @@ -20271,11 +19210,14 @@ get_context_die (tree context) { /* Find die that represents this context. */ if (TYPE_P (context)) - return force_type_die (TYPE_MAIN_VARIANT (context)); + { + context = TYPE_MAIN_VARIANT (context); + return strip_naming_typedef (context, force_type_die (context)); + } else return force_decl_die (context); } - return comp_unit_die; + return comp_unit_die (); } /* Returns the DIE for decl. A DIE will always be returned. */ @@ -20321,7 +19263,11 @@ force_decl_die (tree decl) dwarf2out_decl (decl); else /* DWARF2 has neither DW_TAG_module, nor DW_TAG_namespace. */ - decl_die = comp_unit_die; + decl_die = comp_unit_die (); + break; + + case TRANSLATION_UNIT_DECL: + decl_die = comp_unit_die (); break; default: @@ -20418,7 +19364,7 @@ gen_namespace_die (tree decl, dw_die_ref context_die) if (DECL_ABSTRACT_ORIGIN (decl) == NULL) { /* Output a real namespace or module. */ - context_die = setup_namespace_context (decl, comp_unit_die); + context_die = setup_namespace_context (decl, comp_unit_die ()); namespace_die = new_die (is_fortran () ? DW_TAG_module : DW_TAG_namespace, context_die, decl); @@ -20443,9 +19389,9 @@ gen_namespace_die (tree decl, dw_die_ref context_die) dw_die_ref origin_die = force_decl_die (DECL_ABSTRACT_ORIGIN (decl)); - if (DECL_CONTEXT (decl) == NULL_TREE + if (DECL_FILE_SCOPE_P (decl) || TREE_CODE (DECL_CONTEXT (decl)) == NAMESPACE_DECL) - context_die = setup_namespace_context (decl, comp_unit_die); + context_die = setup_namespace_context (decl, comp_unit_die ()); /* Now create the namespace alias DIE. */ namespace_die = new_die (DW_TAG_imported_declaration, context_die, decl); add_name_and_src_coords_attributes (namespace_die, decl); @@ -20473,7 +19419,7 @@ gen_decl_die (tree decl, tree origin, dw_die_ref context_die) break; case CONST_DECL: - if (!is_fortran ()) + if (!is_fortran () && !is_ada ()) { /* The individual enumerators of an enum type get output when we output the Dwarf representation of the relevant enum type itself. */ @@ -20493,7 +19439,7 @@ gen_decl_die (tree decl, tree origin, dw_die_ref context_die) /* Don't output any DIEs to represent mere function declarations, unless they are class members or explicit block externs. */ if (DECL_INITIAL (decl_or_origin) == NULL_TREE - && DECL_CONTEXT (decl_or_origin) == NULL_TREE + && DECL_FILE_SCOPE_P (decl_or_origin) && (current_function_decl == NULL_TREE || DECL_ARTIFICIAL (decl_or_origin))) break; @@ -20530,16 +19476,20 @@ gen_decl_die (tree decl, tree origin, dw_die_ref context_die) else if (debug_info_level > DINFO_LEVEL_TERSE) { /* Before we describe the FUNCTION_DECL itself, make sure that we - have described its return type. */ + have its containing type. */ + if (!origin) + origin = decl_class_context (decl); + if (origin != NULL_TREE) + gen_type_die (origin, context_die); + + /* And its return type. */ gen_type_die (TREE_TYPE (TREE_TYPE (decl)), context_die); /* And its virtual context. */ if (DECL_VINDEX (decl) != NULL_TREE) gen_type_die (DECL_CONTEXT (decl), context_die); - /* And its containing type. */ - if (!origin) - origin = decl_class_context (decl); + /* Make sure we have a member DIE for decl. */ if (origin != NULL_TREE) gen_type_die_for_member (origin, decl, context_die); @@ -20812,7 +19762,7 @@ dwarf2out_imported_module_or_decl (tree decl, tree name, tree context, void dwarf2out_decl (tree decl) { - dw_die_ref context_die = comp_unit_die; + dw_die_ref context_die = comp_unit_die (); switch (TREE_CODE (decl)) { @@ -20884,7 +19834,7 @@ dwarf2out_decl (tree decl) case CONST_DECL: if (debug_info_level <= DINFO_LEVEL_TERSE) return; - if (!is_fortran ()) + if (!is_fortran () && !is_ada ()) return; if (TREE_STATIC (decl) && decl_function_context (decl)) context_die = lookup_decl_die (DECL_CONTEXT (decl)); @@ -20906,16 +19856,7 @@ dwarf2out_decl (tree decl) /* Don't bother trying to generate any DIEs to represent any of the normal built-in types for the language we are compiling. */ if (DECL_IS_BUILTIN (decl)) - { - /* OK, we need to generate one for `bool' so GDB knows what type - comparisons have. */ - if (is_cxx () - && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE - && ! DECL_IGNORED_P (decl)) - modified_type_die (TREE_TYPE (decl), 0, 0, NULL); - - return; - } + return; /* If we are in terse mode, don't generate any DIEs for types. */ if (debug_info_level <= DINFO_LEVEL_TERSE) @@ -20941,8 +19882,13 @@ static void dwarf2out_function_decl (tree decl) { dwarf2out_decl (decl); - + call_arg_locations = NULL; + call_arg_loc_last = NULL; + call_site_count = -1; + tail_call_site_count = -1; + VEC_free (dw_die_ref, heap, block_map); htab_empty (decl_loc_table); + htab_empty (cached_dw_loc_list_table); } /* Output a marker (i.e. a label) for the beginning of the generated code for @@ -21002,7 +19948,7 @@ file_table_eq (const void *p1_p, const void *p2_p) const struct dwarf_file_data *const p1 = (const struct dwarf_file_data *) p1_p; const char *const p2 = (const char *) p2_p; - return strcmp (p1->filename, p2) == 0; + return filename_cmp (p1->filename, p2) == 0; } static hashval_t @@ -21033,7 +19979,7 @@ lookup_filename (const char *file_name) call matches this file name. If so, return the index. */ if (file_table_last_lookup && (file_name == file_table_last_lookup->filename - || strcmp (file_table_last_lookup->filename, file_name) == 0)) + || filename_cmp (file_table_last_lookup->filename, file_name) == 0)) return file_table_last_lookup; /* Didn't match the previous lookup, search the table. */ @@ -21101,184 +20047,98 @@ append_entry_to_tmpl_value_parm_die_table (dw_die_ref die, tree arg) &entry); } -/* Add a DW_AT_const_value attribute to DIEs that were scheduled - by append_entry_to_tmpl_value_parm_die_table. This function must - be called after function DIEs have been generated. */ +/* Return TRUE if T is an instance of generic type, FALSE + otherwise. */ -static void -gen_remaining_tmpl_value_param_die_attribute (void) +static bool +generic_type_p (tree t) { - if (tmpl_value_parm_die_table) - { - unsigned i; - die_arg_entry *e; - - for (i = 0; - VEC_iterate (die_arg_entry, tmpl_value_parm_die_table, i, e); - i++) - tree_add_const_value_attribute (e->die, e->arg); - } + if (t == NULL_TREE || !TYPE_P (t)) + return false; + return lang_hooks.get_innermost_generic_parms (t) != NULL_TREE; } - -/* Replace DW_AT_name for the decl with name. */ +/* Schedule the generation of the generic parameter dies for the + instance of generic type T. The proper generation itself is later + done by gen_scheduled_generic_parms_dies. */ static void -dwarf2out_set_name (tree decl, tree name) +schedule_generic_params_dies_gen (tree t) { - dw_die_ref die; - dw_attr_ref attr; - const char *dname; - - die = TYPE_SYMTAB_DIE (decl); - if (!die) - return; - - dname = dwarf2_name (name, 0); - if (!dname) + if (!generic_type_p (t)) return; - attr = get_AT (die, DW_AT_name); - if (attr) - { - struct indirect_string_node *node; - - node = find_AT_string (dname); - /* replace the string. */ - attr->dw_attr_val.v.val_str = node; - } - - else - add_name_attribute (die, dname); -} - -/* Called by the final INSN scan whenever we see a direct function call. - Make an entry into the direct call table, recording the point of call - and a reference to the target function's debug entry. */ - -static void -dwarf2out_direct_call (tree targ) -{ - dcall_entry e; - tree origin = decl_ultimate_origin (targ); - - /* If this is a clone, use the abstract origin as the target. */ - if (origin) - targ = origin; - - e.poc_label_num = poc_label_num++; - e.poc_decl = current_function_decl; - e.targ_die = force_decl_die (targ); - VEC_safe_push (dcall_entry, gc, dcall_table, &e); - - /* Drop a label at the return point to mark the point of call. */ - ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LPOC", e.poc_label_num); -} - -/* Returns a hash value for X (which really is a struct vcall_insn). */ - -static hashval_t -vcall_insn_table_hash (const void *x) -{ - return (hashval_t) ((const struct vcall_insn *) x)->insn_uid; -} - -/* Return nonzero if insn_uid of struct vcall_insn *X is the same as - insnd_uid of *Y. */ - -static int -vcall_insn_table_eq (const void *x, const void *y) -{ - return (((const struct vcall_insn *) x)->insn_uid - == ((const struct vcall_insn *) y)->insn_uid); -} - -/* Associate VTABLE_SLOT with INSN_UID in the VCALL_INSN_TABLE. */ - -static void -store_vcall_insn (unsigned int vtable_slot, int insn_uid) -{ - struct vcall_insn *item = ggc_alloc_vcall_insn (); - struct vcall_insn **slot; - - gcc_assert (item); - item->insn_uid = insn_uid; - item->vtable_slot = vtable_slot; - slot = (struct vcall_insn **) - htab_find_slot_with_hash (vcall_insn_table, &item, - (hashval_t) insn_uid, INSERT); - *slot = item; -} - -/* Return the VTABLE_SLOT associated with INSN_UID. */ - -static unsigned int -lookup_vcall_insn (unsigned int insn_uid) -{ - struct vcall_insn item; - struct vcall_insn *p; + if (generic_type_instances == NULL) + generic_type_instances = VEC_alloc (tree, gc, 256); - item.insn_uid = insn_uid; - item.vtable_slot = 0; - p = (struct vcall_insn *) htab_find_with_hash (vcall_insn_table, - (void *) &item, - (hashval_t) insn_uid); - if (p == NULL) - return (unsigned int) -1; - return p->vtable_slot; + VEC_safe_push (tree, gc, generic_type_instances, t); } - -/* Called when lowering indirect calls to RTL. We make a note of INSN_UID - and the OBJ_TYPE_REF_TOKEN from ADDR. For C++ virtual calls, the token - is the vtable slot index that we will need to put in the virtual call - table later. */ +/* Add a DW_AT_const_value attribute to DIEs that were scheduled + by append_entry_to_tmpl_value_parm_die_table. This function must + be called after function DIEs have been generated. */ static void -dwarf2out_virtual_call_token (tree addr, int insn_uid) +gen_remaining_tmpl_value_param_die_attribute (void) { - if (is_cxx() && TREE_CODE (addr) == OBJ_TYPE_REF) + if (tmpl_value_parm_die_table) { - tree token = OBJ_TYPE_REF_TOKEN (addr); - if (TREE_CODE (token) == INTEGER_CST) - store_vcall_insn (TREE_INT_CST_LOW (token), insn_uid); + unsigned i; + die_arg_entry *e; + + FOR_EACH_VEC_ELT (die_arg_entry, tmpl_value_parm_die_table, i, e) + tree_add_const_value_attribute (e->die, e->arg); } } -/* Called when scheduling RTL, when a CALL_INSN is split. Copies the - OBJ_TYPE_REF_TOKEN previously associated with OLD_INSN and associates it - with NEW_INSN. */ +/* Generate generic parameters DIEs for instances of generic types + that have been previously scheduled by + schedule_generic_params_dies_gen. This function must be called + after all the types of the CU have been laid out. */ static void -dwarf2out_copy_call_info (rtx old_insn, rtx new_insn) +gen_scheduled_generic_parms_dies (void) { - unsigned int vtable_slot = lookup_vcall_insn (INSN_UID (old_insn)); + unsigned i; + tree t; - if (vtable_slot != (unsigned int) -1) - store_vcall_insn (vtable_slot, INSN_UID (new_insn)); + if (generic_type_instances == NULL) + return; + + FOR_EACH_VEC_ELT (tree, generic_type_instances, i, t) + gen_generic_params_dies (t); } -/* Called by the final INSN scan whenever we see a virtual function call. - Make an entry into the virtual call table, recording the point of call - and the slot index of the vtable entry used to call the virtual member - function. The slot index was associated with the INSN_UID during the - lowering to RTL. */ + +/* Replace DW_AT_name for the decl with name. */ static void -dwarf2out_virtual_call (int insn_uid) +dwarf2out_set_name (tree decl, tree name) { - unsigned int vtable_slot = lookup_vcall_insn (insn_uid); - vcall_entry e; + dw_die_ref die; + dw_attr_ref attr; + const char *dname; + + die = TYPE_SYMTAB_DIE (decl); + if (!die) + return; - if (vtable_slot == (unsigned int) -1) + dname = dwarf2_name (name, 0); + if (!dname) return; - e.poc_label_num = poc_label_num++; - e.vtable_slot = vtable_slot; - VEC_safe_push (vcall_entry, gc, vcall_table, &e); + attr = get_AT (die, DW_AT_name); + if (attr) + { + struct indirect_string_node *node; + + node = find_AT_string (dname); + /* replace the string. */ + attr->dw_attr_val.v.val_str = node; + } - /* Drop a label at the return point to mark the point of call. */ - ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LPOC", e.poc_label_num); + else + add_name_attribute (die, dname); } /* Called by the final INSN scan whenever we see a var location. We @@ -21290,21 +20150,69 @@ dwarf2out_var_location (rtx loc_note) { char loclabel[MAX_ARTIFICIAL_LABEL_BYTES + 2]; struct var_loc_node *newloc; - rtx next_real; + rtx next_real, next_note; static const char *last_label; static const char *last_postcall_label; static bool last_in_cold_section_p; + static rtx expected_next_loc_note; tree decl; + bool var_loc_p; + + if (!NOTE_P (loc_note)) + { + if (CALL_P (loc_note)) + { + call_site_count++; + if (SIBLING_CALL_P (loc_note)) + tail_call_site_count++; + } + return; + } - if (!DECL_P (NOTE_VAR_LOCATION_DECL (loc_note))) + var_loc_p = NOTE_KIND (loc_note) == NOTE_INSN_VAR_LOCATION; + if (var_loc_p && !DECL_P (NOTE_VAR_LOCATION_DECL (loc_note))) return; - next_real = next_real_insn (loc_note); + /* Optimize processing a large consecutive sequence of location + notes so we don't spend too much time in next_real_insn. If the + next insn is another location note, remember the next_real_insn + calculation for next time. */ + next_real = cached_next_real_insn; + if (next_real) + { + if (expected_next_loc_note != loc_note) + next_real = NULL_RTX; + } + + next_note = NEXT_INSN (loc_note); + if (! next_note + || INSN_DELETED_P (next_note) + || GET_CODE (next_note) != NOTE + || (NOTE_KIND (next_note) != NOTE_INSN_VAR_LOCATION + && NOTE_KIND (next_note) != NOTE_INSN_CALL_ARG_LOCATION)) + next_note = NULL_RTX; + + if (! next_real) + next_real = next_real_insn (loc_note); + + if (next_note) + { + expected_next_loc_note = next_note; + cached_next_real_insn = next_real; + } + else + cached_next_real_insn = NULL_RTX; + /* If there are no instructions which would be affected by this note, don't do anything. */ - if (next_real == NULL_RTX && !NOTE_DURING_CALL_P (loc_note)) + if (var_loc_p + && next_real == NULL_RTX + && !NOTE_DURING_CALL_P (loc_note)) return; + if (next_real == NULL_RTX) + next_real = get_last_insn (); + /* If there were any real insns between note we processed last time and this note (or if it is the first note), clear last_{,postcall_}label so that they are not reused this time. */ @@ -21316,12 +20224,20 @@ dwarf2out_var_location (rtx loc_note) last_postcall_label = NULL; } - decl = NOTE_VAR_LOCATION_DECL (loc_note); - newloc = add_var_loc_to_decl (decl, loc_note, - NOTE_DURING_CALL_P (loc_note) - ? last_postcall_label : last_label); - if (newloc == NULL) - return; + if (var_loc_p) + { + decl = NOTE_VAR_LOCATION_DECL (loc_note); + newloc = add_var_loc_to_decl (decl, loc_note, + NOTE_DURING_CALL_P (loc_note) + ? last_postcall_label : last_label); + if (newloc == NULL) + return; + } + else + { + decl = NULL_TREE; + newloc = NULL; + } /* If there were no real insns between note we processed last time and this note, use the label we emitted last time. Otherwise @@ -21334,7 +20250,43 @@ dwarf2out_var_location (rtx loc_note) last_label = ggc_strdup (loclabel); } - if (!NOTE_DURING_CALL_P (loc_note)) + if (!var_loc_p) + { + struct call_arg_loc_node *ca_loc + = ggc_alloc_cleared_call_arg_loc_node (); + rtx prev = prev_real_insn (loc_note), x; + ca_loc->call_arg_loc_note = loc_note; + ca_loc->next = NULL; + ca_loc->label = last_label; + gcc_assert (prev + && (CALL_P (prev) + || (NONJUMP_INSN_P (prev) + && GET_CODE (PATTERN (prev)) == SEQUENCE + && CALL_P (XVECEXP (PATTERN (prev), 0, 0))))); + if (!CALL_P (prev)) + prev = XVECEXP (PATTERN (prev), 0, 0); + ca_loc->tail_call_p = SIBLING_CALL_P (prev); + x = PATTERN (prev); + if (GET_CODE (x) == PARALLEL) + x = XVECEXP (x, 0, 0); + if (GET_CODE (x) == SET) + x = SET_SRC (x); + if (GET_CODE (x) == CALL && MEM_P (XEXP (x, 0))) + { + x = XEXP (XEXP (x, 0), 0); + if (GET_CODE (x) == SYMBOL_REF + && SYMBOL_REF_DECL (x) + && TREE_CODE (SYMBOL_REF_DECL (x)) == FUNCTION_DECL) + ca_loc->symbol_ref = x; + } + ca_loc->block = insn_scope (prev); + if (call_arg_locations) + call_arg_loc_last->next = ca_loc; + else + call_arg_locations = ca_loc; + call_arg_loc_last = ca_loc; + } + else if (!NOTE_DURING_CALL_P (loc_note)) newloc->label = last_label; else { @@ -21350,6 +20302,96 @@ dwarf2out_var_location (rtx loc_note) last_in_cold_section_p = in_cold_section_p; } +/* Note in one location list that text section has changed. */ + +static int +var_location_switch_text_section_1 (void **slot, void *data ATTRIBUTE_UNUSED) +{ + var_loc_list *list = (var_loc_list *) *slot; + if (list->first) + list->last_before_switch + = list->last->next ? list->last->next : list->last; + return 1; +} + +/* Note in all location lists that text section has changed. */ + +static void +var_location_switch_text_section (void) +{ + if (decl_loc_table == NULL) + return; + + htab_traverse (decl_loc_table, var_location_switch_text_section_1, NULL); +} + +/* Create a new line number table. */ + +static dw_line_info_table * +new_line_info_table (void) +{ + dw_line_info_table *table; + + table = ggc_alloc_cleared_dw_line_info_table_struct (); + table->file_num = 1; + table->line_num = 1; + table->is_stmt = DWARF_LINE_DEFAULT_IS_STMT_START; + + return table; +} + +/* Lookup the "current" table into which we emit line info, so + that we don't have to do it for every source line. */ + +static void +set_cur_line_info_table (section *sec) +{ + dw_line_info_table *table; + + if (sec == text_section) + table = text_section_line_info; + else if (sec == cold_text_section) + { + table = cold_text_section_line_info; + if (!table) + { + cold_text_section_line_info = table = new_line_info_table (); + table->end_label = cold_end_label; + } + } + else + { + const char *end_label; + + if (flag_reorder_blocks_and_partition) + { + if (in_cold_section_p) + end_label = crtl->subsections.cold_section_end_label; + else + end_label = crtl->subsections.hot_section_end_label; + } + else + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + ASM_GENERATE_INTERNAL_LABEL (label, FUNC_END_LABEL, + current_function_funcdef_no); + end_label = ggc_strdup (label); + } + + table = new_line_info_table (); + table->end_label = end_label; + + VEC_safe_push (dw_line_info_table_p, gc, separate_line_info, table); + } + + if (DWARF2_ASM_LINE_DEBUG_INFO) + table->is_stmt = (cur_line_info_table + ? cur_line_info_table->is_stmt + : DWARF_LINE_DEFAULT_IS_STMT_START); + cur_line_info_table = table; +} + + /* We need to reset the locations at the beginning of each function. We can't do this in the end_function hook, because the declarations that use the locations won't have been output when @@ -21358,104 +20400,134 @@ dwarf2out_var_location (rtx loc_note) static void dwarf2out_begin_function (tree fun) { - if (function_section (fun) != text_section) + section *sec = function_section (fun); + + if (sec != text_section) have_multiple_function_sections = true; + if (flag_reorder_blocks_and_partition && !cold_text_section) + { + gcc_assert (current_function_decl == fun); + cold_text_section = unlikely_text_section (); + switch_to_section (cold_text_section); + ASM_OUTPUT_LABEL (asm_out_file, cold_text_section_label); + switch_to_section (sec); + } + dwarf2out_note_section_used (); + call_site_count = 0; + tail_call_site_count = 0; + + set_cur_line_info_table (sec); +} + +/* Add OPCODE+VAL as an entry at the end of the opcode array in TABLE. */ + +static void +push_dw_line_info_entry (dw_line_info_table *table, + enum dw_line_info_opcode opcode, unsigned int val) +{ + dw_line_info_entry e; + e.opcode = opcode; + e.val = val; + VEC_safe_push (dw_line_info_entry, gc, table->entries, &e); } /* Output a label to mark the beginning of a source code line entry and record information relating to this source line, in 'line_info_table' for later output of the .debug_line section. */ +/* ??? The discriminator parameter ought to be unsigned. */ static void dwarf2out_source_line (unsigned int line, const char *filename, int discriminator, bool is_stmt) { - static bool last_is_stmt = true; + unsigned int file_num; + dw_line_info_table *table; - if (debug_info_level >= DINFO_LEVEL_NORMAL - && line != 0) - { - int file_num = maybe_emit_file (lookup_filename (filename)); + if (debug_info_level < DINFO_LEVEL_NORMAL || line == 0) + return; + + /* The discriminator column was added in dwarf4. Simplify the below + by simply removing it if we're not supposed to output it. */ + if (dwarf_version < 4 && dwarf_strict) + discriminator = 0; + + table = cur_line_info_table; + file_num = maybe_emit_file (lookup_filename (filename)); + + /* ??? TODO: Elide duplicate line number entries. Traditionally, + the debugger has used the second (possibly duplicate) line number + at the beginning of the function to mark the end of the prologue. + We could eliminate any other duplicates within the function. For + Dwarf3, we ought to include the DW_LNS_set_prologue_end mark in + that second line number entry. */ + /* Recall that this end-of-prologue indication is *not* the same thing + as the end_prologue debug hook. The NOTE_INSN_PROLOGUE_END note, + to which the hook corresponds, follows the last insn that was + emitted by gen_prologue. What we need is to preceed the first insn + that had been emitted after NOTE_INSN_FUNCTION_BEG, i.e. the first + insn that corresponds to something the user wrote. These may be + very different locations once scheduling is enabled. */ + + if (0 && file_num == table->file_num + && line == table->line_num + && discriminator == table->discrim_num + && is_stmt == table->is_stmt) + return; - switch_to_section (current_function_section ()); + switch_to_section (current_function_section ()); - /* If requested, emit something human-readable. */ - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s %s:%d\n", ASM_COMMENT_START, - filename, line); + /* If requested, emit something human-readable. */ + if (flag_debug_asm) + fprintf (asm_out_file, "\t%s %s:%d\n", ASM_COMMENT_START, filename, line); - if (DWARF2_ASM_LINE_DEBUG_INFO) - { - /* Emit the .loc directive understood by GNU as. */ - fprintf (asm_out_file, "\t.loc %d %d 0", file_num, line); - if (is_stmt != last_is_stmt) - { - fprintf (asm_out_file, " is_stmt %d", is_stmt ? 1 : 0); - last_is_stmt = is_stmt; - } - if (SUPPORTS_DISCRIMINATOR && discriminator != 0) - fprintf (asm_out_file, " discriminator %d", discriminator); - fputc ('\n', asm_out_file); + if (DWARF2_ASM_LINE_DEBUG_INFO) + { + /* Emit the .loc directive understood by GNU as. */ + /* "\t.loc %u %u 0 is_stmt %u discriminator %u", + file_num, line, is_stmt, discriminator */ + fputs ("\t.loc ", asm_out_file); + fprint_ul (asm_out_file, file_num); + putc (' ', asm_out_file); + fprint_ul (asm_out_file, line); + putc (' ', asm_out_file); + putc ('0', asm_out_file); - /* Indicate that line number info exists. */ - line_info_table_in_use++; - } - else if (function_section (current_function_decl) != text_section) + if (is_stmt != table->is_stmt) { - dw_separate_line_info_ref line_info; - targetm.asm_out.internal_label (asm_out_file, - SEPARATE_LINE_CODE_LABEL, - separate_line_info_table_in_use); - - /* Expand the line info table if necessary. */ - if (separate_line_info_table_in_use - == separate_line_info_table_allocated) - { - separate_line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; - separate_line_info_table - = GGC_RESIZEVEC (dw_separate_line_info_entry, - separate_line_info_table, - separate_line_info_table_allocated); - memset (separate_line_info_table - + separate_line_info_table_in_use, - 0, - (LINE_INFO_TABLE_INCREMENT - * sizeof (dw_separate_line_info_entry))); - } - - /* Add the new entry at the end of the line_info_table. */ - line_info - = &separate_line_info_table[separate_line_info_table_in_use++]; - line_info->dw_file_num = file_num; - line_info->dw_line_num = line; - line_info->function = current_function_funcdef_no; + fputs (" is_stmt ", asm_out_file); + putc (is_stmt ? '1' : '0', asm_out_file); } - else + if (SUPPORTS_DISCRIMINATOR && discriminator != 0) { - dw_line_info_ref line_info; - - targetm.asm_out.internal_label (asm_out_file, LINE_CODE_LABEL, - line_info_table_in_use); + gcc_assert (discriminator > 0); + fputs (" discriminator ", asm_out_file); + fprint_ul (asm_out_file, (unsigned long) discriminator); + } + putc ('\n', asm_out_file); + } + else + { + unsigned int label_num = ++line_info_label_num; - /* Expand the line info table if necessary. */ - if (line_info_table_in_use == line_info_table_allocated) - { - line_info_table_allocated += LINE_INFO_TABLE_INCREMENT; - line_info_table - = GGC_RESIZEVEC (dw_line_info_entry, line_info_table, - line_info_table_allocated); - memset (line_info_table + line_info_table_in_use, 0, - LINE_INFO_TABLE_INCREMENT * sizeof (dw_line_info_entry)); - } + targetm.asm_out.internal_label (asm_out_file, LINE_CODE_LABEL, label_num); - /* Add the new entry at the end of the line_info_table. */ - line_info = &line_info_table[line_info_table_in_use++]; - line_info->dw_file_num = file_num; - line_info->dw_line_num = line; - } + push_dw_line_info_entry (table, LI_set_address, label_num); + if (file_num != table->file_num) + push_dw_line_info_entry (table, LI_set_file, file_num); + if (discriminator != table->discrim_num) + push_dw_line_info_entry (table, LI_set_discriminator, discriminator); + if (is_stmt != table->is_stmt) + push_dw_line_info_entry (table, LI_negate_stmt, 0); + push_dw_line_info_entry (table, LI_set_line, line); } + + table->file_num = file_num; + table->line_num = line; + table->discrim_num = discriminator; + table->is_stmt = is_stmt; + table->in_use = true; } /* Record the beginning of a new source file. */ @@ -21463,25 +20535,22 @@ dwarf2out_source_line (unsigned int line, const char *filename, static void dwarf2out_start_source_file (unsigned int lineno, const char *filename) { - if (flag_eliminate_dwarf2_dups && dwarf_version < 4) + if (flag_eliminate_dwarf2_dups && ! use_debug_types) { /* Record the beginning of the file for break_out_includes. */ dw_die_ref bincl_die; - bincl_die = new_die (DW_TAG_GNU_BINCL, comp_unit_die, NULL); + bincl_die = new_die (DW_TAG_GNU_BINCL, comp_unit_die (), NULL); add_AT_string (bincl_die, DW_AT_name, remap_debug_filename (filename)); } if (debug_info_level >= DINFO_LEVEL_VERBOSE) { - int file_num = maybe_emit_file (lookup_filename (filename)); - - switch_to_section (debug_macinfo_section); - dw2_asm_output_data (1, DW_MACINFO_start_file, "Start new file"); - dw2_asm_output_data_uleb128 (lineno, "Included from line number %d", - lineno); - - dw2_asm_output_data_uleb128 (file_num, "file %s", filename); + macinfo_entry e; + e.code = DW_MACINFO_start_file; + e.lineno = lineno; + e.info = xstrdup (filename); + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); } } @@ -21490,14 +20559,17 @@ dwarf2out_start_source_file (unsigned int lineno, const char *filename) static void dwarf2out_end_source_file (unsigned int lineno ATTRIBUTE_UNUSED) { - if (flag_eliminate_dwarf2_dups && dwarf_version < 4) + if (flag_eliminate_dwarf2_dups && ! use_debug_types) /* Record the end of the file for break_out_includes. */ - new_die (DW_TAG_GNU_EINCL, comp_unit_die, NULL); + new_die (DW_TAG_GNU_EINCL, comp_unit_die (), NULL); if (debug_info_level >= DINFO_LEVEL_VERBOSE) { - switch_to_section (debug_macinfo_section); - dw2_asm_output_data (1, DW_MACINFO_end_file, "End file"); + macinfo_entry e; + e.code = DW_MACINFO_end_file; + e.lineno = lineno; + e.info = NULL; + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); } } @@ -21511,28 +20583,418 @@ dwarf2out_define (unsigned int lineno ATTRIBUTE_UNUSED, { if (debug_info_level >= DINFO_LEVEL_VERBOSE) { - switch_to_section (debug_macinfo_section); - dw2_asm_output_data (1, DW_MACINFO_define, "Define macro"); - dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno); - dw2_asm_output_nstring (buffer, -1, "The macro"); + macinfo_entry e; + /* Insert a dummy first entry to be able to optimize the whole + predefined macro block using DW_MACRO_GNU_transparent_include. */ + if (VEC_empty (macinfo_entry, macinfo_table) && lineno == 0) + { + e.code = 0; + e.lineno = 0; + e.info = NULL; + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); + } + e.code = DW_MACINFO_define; + e.lineno = lineno; + e.info = xstrdup (buffer);; + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); + } +} + +/* Called from debug_undef in toplev.c. The `buffer' parameter contains + the tail part of the directive line, i.e. the part which is past the + initial whitespace, #, whitespace, directive-name, whitespace part. */ + +static void +dwarf2out_undef (unsigned int lineno ATTRIBUTE_UNUSED, + const char *buffer ATTRIBUTE_UNUSED) +{ + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + { + macinfo_entry e; + /* Insert a dummy first entry to be able to optimize the whole + predefined macro block using DW_MACRO_GNU_transparent_include. */ + if (VEC_empty (macinfo_entry, macinfo_table) && lineno == 0) + { + e.code = 0; + e.lineno = 0; + e.info = NULL; + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); + } + e.code = DW_MACINFO_undef; + e.lineno = lineno; + e.info = xstrdup (buffer); + VEC_safe_push (macinfo_entry, gc, macinfo_table, &e); + } +} + +/* Routines to manipulate hash table of CUs. */ + +static hashval_t +htab_macinfo_hash (const void *of) +{ + const macinfo_entry *const entry = + (const macinfo_entry *) of; + + return htab_hash_string (entry->info); +} + +static int +htab_macinfo_eq (const void *of1, const void *of2) +{ + const macinfo_entry *const entry1 = (const macinfo_entry *) of1; + const macinfo_entry *const entry2 = (const macinfo_entry *) of2; + + return !strcmp (entry1->info, entry2->info); +} + +/* Output a single .debug_macinfo entry. */ + +static void +output_macinfo_op (macinfo_entry *ref) +{ + int file_num; + size_t len; + struct indirect_string_node *node; + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + struct dwarf_file_data *fd; + + switch (ref->code) + { + case DW_MACINFO_start_file: + fd = lookup_filename (ref->info); + if (fd->filename == ref->info) + fd->filename = ggc_strdup (fd->filename); + file_num = maybe_emit_file (fd); + dw2_asm_output_data (1, DW_MACINFO_start_file, "Start new file"); + dw2_asm_output_data_uleb128 (ref->lineno, + "Included from line number %lu", + (unsigned long) ref->lineno); + dw2_asm_output_data_uleb128 (file_num, "file %s", ref->info); + break; + case DW_MACINFO_end_file: + dw2_asm_output_data (1, DW_MACINFO_end_file, "End file"); + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + len = strlen (ref->info) + 1; + if (!dwarf_strict + && len > DWARF_OFFSET_SIZE + && !DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET + && (debug_str_section->common.flags & SECTION_MERGE) != 0) + { + ref->code = ref->code == DW_MACINFO_define + ? DW_MACRO_GNU_define_indirect + : DW_MACRO_GNU_undef_indirect; + output_macinfo_op (ref); + return; + } + dw2_asm_output_data (1, ref->code, + ref->code == DW_MACINFO_define + ? "Define macro" : "Undefine macro"); + dw2_asm_output_data_uleb128 (ref->lineno, "At line number %lu", + (unsigned long) ref->lineno); + dw2_asm_output_nstring (ref->info, -1, "The macro"); + break; + case DW_MACRO_GNU_define_indirect: + case DW_MACRO_GNU_undef_indirect: + node = find_AT_string (ref->info); + if (node->form != DW_FORM_strp) + { + char label[32]; + ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter); + ++dw2_string_counter; + node->label = xstrdup (label); + node->form = DW_FORM_strp; + } + dw2_asm_output_data (1, ref->code, + ref->code == DW_MACRO_GNU_define_indirect + ? "Define macro indirect" + : "Undefine macro indirect"); + dw2_asm_output_data_uleb128 (ref->lineno, "At line number %lu", + (unsigned long) ref->lineno); + dw2_asm_output_offset (DWARF_OFFSET_SIZE, node->label, + debug_str_section, "The macro: \"%s\"", + ref->info); + break; + case DW_MACRO_GNU_transparent_include: + dw2_asm_output_data (1, ref->code, "Transparent include"); + ASM_GENERATE_INTERNAL_LABEL (label, + DEBUG_MACRO_SECTION_LABEL, ref->lineno); + dw2_asm_output_offset (DWARF_OFFSET_SIZE, label, NULL, NULL); + break; + default: + fprintf (asm_out_file, "%s unrecognized macinfo code %lu\n", + ASM_COMMENT_START, (unsigned long) ref->code); + break; + } +} + +/* Attempt to make a sequence of define/undef macinfo ops shareable with + other compilation unit .debug_macinfo sections. IDX is the first + index of a define/undef, return the number of ops that should be + emitted in a comdat .debug_macinfo section and emit + a DW_MACRO_GNU_transparent_include entry referencing it. + If the define/undef entry should be emitted normally, return 0. */ + +static unsigned +optimize_macinfo_range (unsigned int idx, VEC (macinfo_entry, gc) *files, + htab_t *macinfo_htab) +{ + macinfo_entry *first, *second, *cur, *inc; + char linebuf[sizeof (HOST_WIDE_INT) * 3 + 1]; + unsigned char checksum[16]; + struct md5_ctx ctx; + char *grp_name, *tail; + const char *base; + unsigned int i, count, encoded_filename_len, linebuf_len; + void **slot; + + first = VEC_index (macinfo_entry, macinfo_table, idx); + second = VEC_index (macinfo_entry, macinfo_table, idx + 1); + + /* Optimize only if there are at least two consecutive define/undef ops, + and either all of them are before first DW_MACINFO_start_file + with lineno 0 (i.e. predefined macro block), or all of them are + in some included header file. */ + if (second->code != DW_MACINFO_define && second->code != DW_MACINFO_undef) + return 0; + if (VEC_empty (macinfo_entry, files)) + { + if (first->lineno != 0 || second->lineno != 0) + return 0; + } + else if (first->lineno == 0) + return 0; + + /* Find the last define/undef entry that can be grouped together + with first and at the same time compute md5 checksum of their + codes, linenumbers and strings. */ + md5_init_ctx (&ctx); + for (i = idx; VEC_iterate (macinfo_entry, macinfo_table, i, cur); i++) + if (cur->code != DW_MACINFO_define && cur->code != DW_MACINFO_undef) + break; + else if (first->lineno == 0 && cur->lineno != 0) + break; + else + { + unsigned char code = cur->code; + md5_process_bytes (&code, 1, &ctx); + checksum_uleb128 (cur->lineno, &ctx); + md5_process_bytes (cur->info, strlen (cur->info) + 1, &ctx); + } + md5_finish_ctx (&ctx, checksum); + count = i - idx; + + /* From the containing include filename (if any) pick up just + usable characters from its basename. */ + if (first->lineno == 0) + base = ""; + else + base = lbasename (VEC_last (macinfo_entry, files)->info); + for (encoded_filename_len = 0, i = 0; base[i]; i++) + if (ISIDNUM (base[i]) || base[i] == '.') + encoded_filename_len++; + /* Count . at the end. */ + if (encoded_filename_len) + encoded_filename_len++; + + sprintf (linebuf, HOST_WIDE_INT_PRINT_UNSIGNED, first->lineno); + linebuf_len = strlen (linebuf); + + /* The group name format is: wmN.[.]. */ + grp_name = XNEWVEC (char, 4 + encoded_filename_len + linebuf_len + 1 + + 16 * 2 + 1); + memcpy (grp_name, DWARF_OFFSET_SIZE == 4 ? "wm4." : "wm8.", 4); + tail = grp_name + 4; + if (encoded_filename_len) + { + for (i = 0; base[i]; i++) + if (ISIDNUM (base[i]) || base[i] == '.') + *tail++ = base[i]; + *tail++ = '.'; + } + memcpy (tail, linebuf, linebuf_len); + tail += linebuf_len; + *tail++ = '.'; + for (i = 0; i < 16; i++) + sprintf (tail + i * 2, "%02x", checksum[i] & 0xff); + + /* Construct a macinfo_entry for DW_MACRO_GNU_transparent_include + in the empty vector entry before the first define/undef. */ + inc = VEC_index (macinfo_entry, macinfo_table, idx - 1); + inc->code = DW_MACRO_GNU_transparent_include; + inc->lineno = 0; + inc->info = grp_name; + if (*macinfo_htab == NULL) + *macinfo_htab = htab_create (10, htab_macinfo_hash, htab_macinfo_eq, NULL); + /* Avoid emitting duplicates. */ + slot = htab_find_slot (*macinfo_htab, inc, INSERT); + if (*slot != NULL) + { + free (CONST_CAST (char *, inc->info)); + inc->code = 0; + inc->info = NULL; + /* If such an entry has been used before, just emit + a DW_MACRO_GNU_transparent_include op. */ + inc = (macinfo_entry *) *slot; + output_macinfo_op (inc); + /* And clear all macinfo_entry in the range to avoid emitting them + in the second pass. */ + for (i = idx; + VEC_iterate (macinfo_entry, macinfo_table, i, cur) + && i < idx + count; + i++) + { + cur->code = 0; + free (CONST_CAST (char *, cur->info)); + cur->info = NULL; + } + } + else + { + *slot = inc; + inc->lineno = htab_elements (*macinfo_htab); + output_macinfo_op (inc); } + return count; } -/* Called from debug_undef in toplev.c. The `buffer' parameter contains - the tail part of the directive line, i.e. the part which is past the - initial whitespace, #, whitespace, directive-name, whitespace part. */ +/* Output macinfo section(s). */ + +static void +output_macinfo (void) +{ + unsigned i; + unsigned long length = VEC_length (macinfo_entry, macinfo_table); + macinfo_entry *ref; + VEC (macinfo_entry, gc) *files = NULL; + htab_t macinfo_htab = NULL; + + if (! length) + return; + + /* output_macinfo* uses these interchangeably. */ + gcc_assert ((int) DW_MACINFO_define == (int) DW_MACRO_GNU_define + && (int) DW_MACINFO_undef == (int) DW_MACRO_GNU_undef + && (int) DW_MACINFO_start_file == (int) DW_MACRO_GNU_start_file + && (int) DW_MACINFO_end_file == (int) DW_MACRO_GNU_end_file); + + /* For .debug_macro emit the section header. */ + if (!dwarf_strict) + { + dw2_asm_output_data (2, 4, "DWARF macro version number"); + if (DWARF_OFFSET_SIZE == 8) + dw2_asm_output_data (1, 3, "Flags: 64-bit, lineptr present"); + else + dw2_asm_output_data (1, 2, "Flags: 32-bit, lineptr present"); + dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_line_section_label, + debug_line_section, NULL); + } + + /* In the first loop, it emits the primary .debug_macinfo section + and after each emitted op the macinfo_entry is cleared. + If a longer range of define/undef ops can be optimized using + DW_MACRO_GNU_transparent_include, the + DW_MACRO_GNU_transparent_include op is emitted and kept in + the vector before the first define/undef in the range and the + whole range of define/undef ops is not emitted and kept. */ + for (i = 0; VEC_iterate (macinfo_entry, macinfo_table, i, ref); i++) + { + switch (ref->code) + { + case DW_MACINFO_start_file: + VEC_safe_push (macinfo_entry, gc, files, ref); + break; + case DW_MACINFO_end_file: + if (!VEC_empty (macinfo_entry, files)) + { + macinfo_entry *file = VEC_last (macinfo_entry, files); + free (CONST_CAST (char *, file->info)); + VEC_pop (macinfo_entry, files); + } + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + if (!dwarf_strict + && HAVE_COMDAT_GROUP + && VEC_length (macinfo_entry, files) != 1 + && i > 0 + && i + 1 < length + && VEC_index (macinfo_entry, macinfo_table, i - 1)->code == 0) + { + unsigned count = optimize_macinfo_range (i, files, &macinfo_htab); + if (count) + { + i += count - 1; + continue; + } + } + break; + case 0: + /* A dummy entry may be inserted at the beginning to be able + to optimize the whole block of predefined macros. */ + if (i == 0) + continue; + default: + break; + } + output_macinfo_op (ref); + /* For DW_MACINFO_start_file ref->info has been copied into files + vector. */ + if (ref->code != DW_MACINFO_start_file) + free (CONST_CAST (char *, ref->info)); + ref->info = NULL; + ref->code = 0; + } + + if (macinfo_htab == NULL) + return; + + htab_delete (macinfo_htab); -static void -dwarf2out_undef (unsigned int lineno ATTRIBUTE_UNUSED, - const char *buffer ATTRIBUTE_UNUSED) -{ - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - { - switch_to_section (debug_macinfo_section); - dw2_asm_output_data (1, DW_MACINFO_undef, "Undefine macro"); - dw2_asm_output_data_uleb128 (lineno, "At line number %d", lineno); - dw2_asm_output_nstring (buffer, -1, "The macro"); - } + /* If any DW_MACRO_GNU_transparent_include were used, on those + DW_MACRO_GNU_transparent_include entries terminate the + current chain and switch to a new comdat .debug_macinfo + section and emit the define/undef entries within it. */ + for (i = 0; VEC_iterate (macinfo_entry, macinfo_table, i, ref); i++) + switch (ref->code) + { + case 0: + continue; + case DW_MACRO_GNU_transparent_include: + { + char label[MAX_ARTIFICIAL_LABEL_BYTES]; + tree comdat_key = get_identifier (ref->info); + /* Terminate the previous .debug_macinfo section. */ + dw2_asm_output_data (1, 0, "End compilation unit"); + targetm.asm_out.named_section (DEBUG_MACRO_SECTION, + SECTION_DEBUG + | SECTION_LINKONCE, + comdat_key); + ASM_GENERATE_INTERNAL_LABEL (label, + DEBUG_MACRO_SECTION_LABEL, + ref->lineno); + ASM_OUTPUT_LABEL (asm_out_file, label); + ref->code = 0; + free (CONST_CAST (char *, ref->info)); + ref->info = NULL; + dw2_asm_output_data (2, 4, "DWARF macro version number"); + if (DWARF_OFFSET_SIZE == 8) + dw2_asm_output_data (1, 1, "Flags: 64-bit"); + else + dw2_asm_output_data (1, 0, "Flags: 32-bit"); + } + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + output_macinfo_op (ref); + ref->code = 0; + free (CONST_CAST (char *, ref->info)); + ref->info = NULL; + break; + default: + gcc_unreachable (); + } } /* Set up for Dwarf output at the start of compilation. */ @@ -21552,6 +21014,11 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) decl_loc_table = htab_create_ggc (10, decl_loc_table_hash, decl_loc_table_eq, NULL); + /* Allocate the cached_dw_loc_list_table. */ + cached_dw_loc_list_table + = htab_create_ggc (10, cached_dw_loc_list_table_hash, + cached_dw_loc_list_table_eq, NULL); + /* Allocate the initial hunk of the decl_scope_table. */ decl_scope_table = VEC_alloc (tree, gc, 256); @@ -21562,30 +21029,10 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) /* Zero-th entry is allocated, but unused. */ abbrev_die_table_in_use = 1; - /* Allocate the initial hunk of the line_info_table. */ - line_info_table = ggc_alloc_cleared_vec_dw_line_info_entry - (LINE_INFO_TABLE_INCREMENT); - line_info_table_allocated = LINE_INFO_TABLE_INCREMENT; - - /* Zero-th entry is allocated, but unused. */ - line_info_table_in_use = 1; - /* Allocate the pubtypes and pubnames vectors. */ pubname_table = VEC_alloc (pubname_entry, gc, 32); pubtype_table = VEC_alloc (pubname_entry, gc, 32); - /* Allocate the table that maps insn UIDs to vtable slot indexes. */ - vcall_insn_table = htab_create_ggc (10, vcall_insn_table_hash, - vcall_insn_table_eq, NULL); - - /* Generate the initial DIE for the .debug section. Note that the (string) - value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE - will (typically) be a relative pathname and that this pathname should be - taken as being relative to the directory from which the compiler was - invoked when the given (base) source file was compiled. We will fill - in this value in dwarf2out_finish. */ - comp_unit_die = gen_compile_unit_die (NULL); - incomplete_types = VEC_alloc (tree, gc, 64); used_rtx_array = VEC_alloc (rtx, gc, 32); @@ -21596,7 +21043,9 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) SECTION_DEBUG, NULL); debug_aranges_section = get_section (DEBUG_ARANGES_SECTION, SECTION_DEBUG, NULL); - debug_macinfo_section = get_section (DEBUG_MACINFO_SECTION, + debug_macinfo_section = get_section (dwarf_strict + ? DEBUG_MACINFO_SECTION + : DEBUG_MACRO_SECTION, SECTION_DEBUG, NULL); debug_line_section = get_section (DEBUG_LINE_SECTION, SECTION_DEBUG, NULL); @@ -21606,10 +21055,6 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) SECTION_DEBUG, NULL); debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION, SECTION_DEBUG, NULL); - debug_dcall_section = get_section (DEBUG_DCALL_SECTION, - SECTION_DEBUG, NULL); - debug_vcall_section = get_section (DEBUG_VCALL_SECTION, - SECTION_DEBUG, NULL); debug_str_section = get_section (DEBUG_STR_SECTION, DEBUG_STR_SECTION_FLAGS, NULL); debug_ranges_section = get_section (DEBUG_RANGES_SECTION, @@ -21617,619 +21062,1358 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED) debug_frame_section = get_section (DEBUG_FRAME_SECTION, SECTION_DEBUG, NULL); - ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (abbrev_section_label, - DEBUG_ABBREV_SECTION_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label, - COLD_TEXT_SECTION_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (abbrev_section_label, + DEBUG_ABBREV_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label, + COLD_TEXT_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0); + + ASM_GENERATE_INTERNAL_LABEL (debug_info_section_label, + DEBUG_INFO_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (debug_line_section_label, + DEBUG_LINE_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (ranges_section_label, + DEBUG_RANGES_SECTION_LABEL, 0); + ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label, + dwarf_strict + ? DEBUG_MACINFO_SECTION_LABEL + : DEBUG_MACRO_SECTION_LABEL, 0); + + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + macinfo_table = VEC_alloc (macinfo_entry, gc, 64); + + switch_to_section (text_section); + ASM_OUTPUT_LABEL (asm_out_file, text_section_label); + + /* Make sure the line number table for .text always exists. */ + text_section_line_info = new_line_info_table (); + text_section_line_info->end_label = text_end_label; +} + +/* Called before cgraph_optimize starts outputtting functions, variables + and toplevel asms into assembly. */ + +static void +dwarf2out_assembly_start (void) +{ + if (HAVE_GAS_CFI_SECTIONS_DIRECTIVE + && dwarf2out_do_cfi_asm () + && (!(flag_unwind_tables || flag_exceptions) + || targetm_common.except_unwind_info (&global_options) != UI_DWARF2)) + fprintf (asm_out_file, "\t.cfi_sections\t.debug_frame\n"); +} + +/* A helper function for dwarf2out_finish called through + htab_traverse. Emit one queued .debug_str string. */ + +static int +output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED) +{ + struct indirect_string_node *node = (struct indirect_string_node *) *h; + + if (node->form == DW_FORM_strp) + { + switch_to_section (debug_str_section); + ASM_OUTPUT_LABEL (asm_out_file, node->label); + assemble_string (node->str, strlen (node->str) + 1); + } + + return 1; +} + +#if ENABLE_ASSERT_CHECKING +/* Verify that all marks are clear. */ + +static void +verify_marks_clear (dw_die_ref die) +{ + dw_die_ref c; + + gcc_assert (! die->die_mark); + FOR_EACH_CHILD (die, c, verify_marks_clear (c)); +} +#endif /* ENABLE_ASSERT_CHECKING */ + +/* Clear the marks for a die and its children. + Be cool if the mark isn't set. */ + +static void +prune_unmark_dies (dw_die_ref die) +{ + dw_die_ref c; + + if (die->die_mark) + die->die_mark = 0; + FOR_EACH_CHILD (die, c, prune_unmark_dies (c)); +} + +/* Given DIE that we're marking as used, find any other dies + it references as attributes and mark them as used. */ + +static void +prune_unused_types_walk_attribs (dw_die_ref die) +{ + dw_attr_ref a; + unsigned ix; + + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) + { + if (a->dw_attr_val.val_class == dw_val_class_die_ref) + { + /* A reference to another DIE. + Make sure that it will get emitted. + If it was broken out into a comdat group, don't follow it. */ + if (! use_debug_types + || a->dw_attr == DW_AT_specification + || a->dw_attr_val.v.val_die_ref.die->die_id.die_type_node == NULL) + prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1); + } + /* Set the string's refcount to 0 so that prune_unused_types_mark + accounts properly for it. */ + if (AT_class (a) == dw_val_class_str) + a->dw_attr_val.v.val_str->refcount = 0; + } +} + +/* Mark the generic parameters and arguments children DIEs of DIE. */ + +static void +prune_unused_types_mark_generic_parms_dies (dw_die_ref die) +{ + dw_die_ref c; + + if (die == NULL || die->die_child == NULL) + return; + c = die->die_child; + do + { + switch (c->die_tag) + { + case DW_TAG_template_type_param: + case DW_TAG_template_value_param: + case DW_TAG_GNU_template_template_param: + case DW_TAG_GNU_template_parameter_pack: + prune_unused_types_mark (c, 1); + break; + default: + break; + } + c = c->die_sib; + } while (c && c != die->die_child); +} + +/* Mark DIE as being used. If DOKIDS is true, then walk down + to DIE's children. */ + +static void +prune_unused_types_mark (dw_die_ref die, int dokids) +{ + dw_die_ref c; + + if (die->die_mark == 0) + { + /* We haven't done this node yet. Mark it as used. */ + die->die_mark = 1; + /* If this is the DIE of a generic type instantiation, + mark the children DIEs that describe its generic parms and + args. */ + prune_unused_types_mark_generic_parms_dies (die); + + /* We also have to mark its parents as used. + (But we don't want to mark our parents' kids due to this.) */ + if (die->die_parent) + prune_unused_types_mark (die->die_parent, 0); + + /* Mark any referenced nodes. */ + prune_unused_types_walk_attribs (die); + + /* If this node is a specification, + also mark the definition, if it exists. */ + if (get_AT_flag (die, DW_AT_declaration) && die->die_definition) + prune_unused_types_mark (die->die_definition, 1); + } + + if (dokids && die->die_mark != 2) + { + /* We need to walk the children, but haven't done so yet. + Remember that we've walked the kids. */ + die->die_mark = 2; + + /* If this is an array type, we need to make sure our + kids get marked, even if they're types. If we're + breaking out types into comdat sections, do this + for all type definitions. */ + if (die->die_tag == DW_TAG_array_type + || (use_debug_types + && is_type_die (die) && ! is_declaration_die (die))) + FOR_EACH_CHILD (die, c, prune_unused_types_mark (c, 1)); + else + FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); + } +} + +/* For local classes, look if any static member functions were emitted + and if so, mark them. */ + +static void +prune_unused_types_walk_local_classes (dw_die_ref die) +{ + dw_die_ref c; + + if (die->die_mark == 2) + return; + + switch (die->die_tag) + { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + break; + + case DW_TAG_subprogram: + if (!get_AT_flag (die, DW_AT_declaration) + || die->die_definition != NULL) + prune_unused_types_mark (die, 1); + return; + + default: + return; + } + + /* Mark children. */ + FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c)); +} + +/* Walk the tree DIE and mark types that we actually use. */ + +static void +prune_unused_types_walk (dw_die_ref die) +{ + dw_die_ref c; + + /* Don't do anything if this node is already marked and + children have been marked as well. */ + if (die->die_mark == 2) + return; + + switch (die->die_tag) + { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + if (die->die_perennial_p) + break; + + for (c = die->die_parent; c; c = c->die_parent) + if (c->die_tag == DW_TAG_subprogram) + break; + + /* Finding used static member functions inside of classes + is needed just for local classes, because for other classes + static member function DIEs with DW_AT_specification + are emitted outside of the DW_TAG_*_type. If we ever change + it, we'd need to call this even for non-local classes. */ + if (c) + prune_unused_types_walk_local_classes (die); + + /* It's a type node --- don't mark it. */ + return; + + case DW_TAG_const_type: + case DW_TAG_packed_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_volatile_type: + case DW_TAG_typedef: + case DW_TAG_array_type: + case DW_TAG_interface_type: + case DW_TAG_friend: + case DW_TAG_variant_part: + case DW_TAG_enumeration_type: + case DW_TAG_subroutine_type: + case DW_TAG_string_type: + case DW_TAG_set_type: + case DW_TAG_subrange_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_file_type: + if (die->die_perennial_p) + break; + + /* It's a type node --- don't mark it. */ + return; - ASM_GENERATE_INTERNAL_LABEL (debug_info_section_label, - DEBUG_INFO_SECTION_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (debug_line_section_label, - DEBUG_LINE_SECTION_LABEL, 0); - ASM_GENERATE_INTERNAL_LABEL (ranges_section_label, - DEBUG_RANGES_SECTION_LABEL, 0); - switch_to_section (debug_abbrev_section); - ASM_OUTPUT_LABEL (asm_out_file, abbrev_section_label); - switch_to_section (debug_info_section); - ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label); - switch_to_section (debug_line_section); - ASM_OUTPUT_LABEL (asm_out_file, debug_line_section_label); + default: + /* Mark everything else. */ + break; + } - if (debug_info_level >= DINFO_LEVEL_VERBOSE) + if (die->die_mark == 0) { - switch_to_section (debug_macinfo_section); - ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label, - DEBUG_MACINFO_SECTION_LABEL, 0); - ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label); - } + die->die_mark = 1; - switch_to_section (text_section); - ASM_OUTPUT_LABEL (asm_out_file, text_section_label); - if (flag_reorder_blocks_and_partition) - { - cold_text_section = unlikely_text_section (); - switch_to_section (cold_text_section); - ASM_OUTPUT_LABEL (asm_out_file, cold_text_section_label); + /* Now, mark any dies referenced from here. */ + prune_unused_types_walk_attribs (die); } + die->die_mark = 2; + + /* Mark children. */ + FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); } -/* Called before cgraph_optimize starts outputtting functions, variables - and toplevel asms into assembly. */ +/* Increment the string counts on strings referred to from DIE's + attributes. */ static void -dwarf2out_assembly_start (void) +prune_unused_types_update_strings (dw_die_ref die) { - if (HAVE_GAS_CFI_SECTIONS_DIRECTIVE && dwarf2out_do_cfi_asm ()) - { -#ifndef TARGET_UNWIND_INFO - if (USING_SJLJ_EXCEPTIONS || (!flag_unwind_tables && !flag_exceptions)) -#endif - fprintf (asm_out_file, "\t.cfi_sections\t.debug_frame\n"); - } + dw_attr_ref a; + unsigned ix; + + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) + if (AT_class (a) == dw_val_class_str) + { + struct indirect_string_node *s = a->dw_attr_val.v.val_str; + s->refcount++; + /* Avoid unnecessarily putting strings that are used less than + twice in the hash table. */ + if (s->refcount + == ((DEBUG_STR_SECTION_FLAGS & SECTION_MERGE) ? 1 : 2)) + { + void ** slot; + slot = htab_find_slot_with_hash (debug_str_hash, s->str, + htab_hash_string (s->str), + INSERT); + gcc_assert (*slot == NULL); + *slot = s; + } + } } -/* A helper function for dwarf2out_finish called through - htab_traverse. Emit one queued .debug_str string. */ +/* Remove from the tree DIE any dies that aren't marked. */ -static int -output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED) +static void +prune_unused_types_prune (dw_die_ref die) { - struct indirect_string_node *node = (struct indirect_string_node *) *h; + dw_die_ref c; - if (node->label && node->refcount) - { - switch_to_section (debug_str_section); - ASM_OUTPUT_LABEL (asm_out_file, node->label); - assemble_string (node->str, strlen (node->str) + 1); - } + gcc_assert (die->die_mark); + prune_unused_types_update_strings (die); - return 1; + if (! die->die_child) + return; + + c = die->die_child; + do { + dw_die_ref prev = c; + for (c = c->die_sib; ! c->die_mark; c = c->die_sib) + if (c == die->die_child) + { + /* No marked children between 'prev' and the end of the list. */ + if (prev == c) + /* No marked children at all. */ + die->die_child = NULL; + else + { + prev->die_sib = c->die_sib; + die->die_child = prev; + } + return; + } + + if (c != prev->die_sib) + prev->die_sib = c; + prune_unused_types_prune (c); + } while (c != die->die_child); } -#if ENABLE_ASSERT_CHECKING -/* Verify that all marks are clear. */ +/* Remove dies representing declarations that we never use. */ static void -verify_marks_clear (dw_die_ref die) +prune_unused_types (void) { - dw_die_ref c; + unsigned int i; + limbo_die_node *node; + comdat_type_node *ctnode; + pubname_ref pub; + dw_die_ref base_type; - gcc_assert (! die->die_mark); - FOR_EACH_CHILD (die, c, verify_marks_clear (c)); -} +#if ENABLE_ASSERT_CHECKING + /* All the marks should already be clear. */ + verify_marks_clear (comp_unit_die ()); + for (node = limbo_die_list; node; node = node->next) + verify_marks_clear (node->die); + for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) + verify_marks_clear (ctnode->root_die); #endif /* ENABLE_ASSERT_CHECKING */ -/* Clear the marks for a die and its children. - Be cool if the mark isn't set. */ + /* Mark types that are used in global variables. */ + premark_types_used_by_global_vars (); -static void -prune_unmark_dies (dw_die_ref die) -{ - dw_die_ref c; + /* Set the mark on nodes that are actually used. */ + prune_unused_types_walk (comp_unit_die ()); + for (node = limbo_die_list; node; node = node->next) + prune_unused_types_walk (node->die); + for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) + { + prune_unused_types_walk (ctnode->root_die); + prune_unused_types_mark (ctnode->type_die, 1); + } - if (die->die_mark) - die->die_mark = 0; - FOR_EACH_CHILD (die, c, prune_unmark_dies (c)); -} + /* Also set the mark on nodes referenced from the + pubname_table. */ + FOR_EACH_VEC_ELT (pubname_entry, pubname_table, i, pub) + prune_unused_types_mark (pub->die, 1); + for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++) + prune_unused_types_mark (base_type, 1); -/* Given DIE that we're marking as used, find any other dies - it references as attributes and mark them as used. */ + if (debug_str_hash) + htab_empty (debug_str_hash); + prune_unused_types_prune (comp_unit_die ()); + for (node = limbo_die_list; node; node = node->next) + prune_unused_types_prune (node->die); + for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) + prune_unused_types_prune (ctnode->root_die); -static void -prune_unused_types_walk_attribs (dw_die_ref die) -{ - dw_attr_ref a; - unsigned ix; + /* Leave the marks clear. */ + prune_unmark_dies (comp_unit_die ()); + for (node = limbo_die_list; node; node = node->next) + prune_unmark_dies (node->die); + for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) + prune_unmark_dies (ctnode->root_die); +} - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) +/* Set the parameter to true if there are any relative pathnames in + the file table. */ +static int +file_table_relative_p (void ** slot, void *param) +{ + bool *p = (bool *) param; + struct dwarf_file_data *d = (struct dwarf_file_data *) *slot; + if (!IS_ABSOLUTE_PATH (d->filename)) { - if (a->dw_attr_val.val_class == dw_val_class_die_ref) - { - /* A reference to another DIE. - Make sure that it will get emitted. - If it was broken out into a comdat group, don't follow it. */ - if (dwarf_version < 4 - || a->dw_attr == DW_AT_specification - || a->dw_attr_val.v.val_die_ref.die->die_id.die_type_node == NULL) - prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1); - } - /* Set the string's refcount to 0 so that prune_unused_types_mark - accounts properly for it. */ - if (AT_class (a) == dw_val_class_str) - a->dw_attr_val.v.val_str->refcount = 0; + *p = true; + return 0; } + return 1; } +/* Routines to manipulate hash table of comdat type units. */ -/* Mark DIE as being used. If DOKIDS is true, then walk down - to DIE's children. */ +static hashval_t +htab_ct_hash (const void *of) +{ + hashval_t h; + const comdat_type_node *const type_node = (const comdat_type_node *) of; -static void -prune_unused_types_mark (dw_die_ref die, int dokids) + memcpy (&h, type_node->signature, sizeof (h)); + return h; +} + +static int +htab_ct_eq (const void *of1, const void *of2) { - dw_die_ref c; + const comdat_type_node *const type_node_1 = (const comdat_type_node *) of1; + const comdat_type_node *const type_node_2 = (const comdat_type_node *) of2; - if (die->die_mark == 0) - { - /* We haven't done this node yet. Mark it as used. */ - die->die_mark = 1; + return (! memcmp (type_node_1->signature, type_node_2->signature, + DWARF_TYPE_SIGNATURE_SIZE)); +} - /* We also have to mark its parents as used. - (But we don't want to mark our parents' kids due to this.) */ - if (die->die_parent) - prune_unused_types_mark (die->die_parent, 0); +/* Move a DW_AT_{,MIPS_}linkage_name attribute just added to dw_die_ref + to the location it would have been added, should we know its + DECL_ASSEMBLER_NAME when we added other attributes. This will + probably improve compactness of debug info, removing equivalent + abbrevs, and hide any differences caused by deferring the + computation of the assembler name, triggered by e.g. PCH. */ + +static inline void +move_linkage_attr (dw_die_ref die) +{ + unsigned ix = VEC_length (dw_attr_node, die->die_attr); + dw_attr_node linkage = *VEC_index (dw_attr_node, die->die_attr, ix - 1); + + gcc_assert (linkage.dw_attr == DW_AT_linkage_name + || linkage.dw_attr == DW_AT_MIPS_linkage_name); - /* Mark any referenced nodes. */ - prune_unused_types_walk_attribs (die); + while (--ix > 0) + { + dw_attr_node *prev = VEC_index (dw_attr_node, die->die_attr, ix - 1); - /* If this node is a specification, - also mark the definition, if it exists. */ - if (get_AT_flag (die, DW_AT_declaration) && die->die_definition) - prune_unused_types_mark (die->die_definition, 1); + if (prev->dw_attr == DW_AT_decl_line || prev->dw_attr == DW_AT_name) + break; } - if (dokids && die->die_mark != 2) + if (ix != VEC_length (dw_attr_node, die->die_attr) - 1) { - /* We need to walk the children, but haven't done so yet. - Remember that we've walked the kids. */ - die->die_mark = 2; - - /* If this is an array type, we need to make sure our - kids get marked, even if they're types. If we're - breaking out types into comdat sections, do this - for all type definitions. */ - if (die->die_tag == DW_TAG_array_type - || (dwarf_version >= 4 - && is_type_die (die) && ! is_declaration_die (die))) - FOR_EACH_CHILD (die, c, prune_unused_types_mark (c, 1)); - else - FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); + VEC_pop (dw_attr_node, die->die_attr); + VEC_quick_insert (dw_attr_node, die->die_attr, ix, &linkage); } } -/* For local classes, look if any static member functions were emitted - and if so, mark them. */ +/* Helper function for resolve_addr, mark DW_TAG_base_type nodes + referenced from typed stack ops and count how often they are used. */ static void -prune_unused_types_walk_local_classes (dw_die_ref die) +mark_base_types (dw_loc_descr_ref loc) { - dw_die_ref c; - - if (die->die_mark == 2) - return; + dw_die_ref base_type = NULL; - switch (die->die_tag) + for (; loc; loc = loc->dw_loc_next) { - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_class_type: - break; - - case DW_TAG_subprogram: - if (!get_AT_flag (die, DW_AT_declaration) - || die->die_definition != NULL) - prune_unused_types_mark (die, 1); - return; - - default: - return; + switch (loc->dw_loc_opc) + { + case DW_OP_GNU_regval_type: + case DW_OP_GNU_deref_type: + base_type = loc->dw_loc_oprnd2.v.val_die_ref.die; + break; + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const) + continue; + /* FALLTHRU */ + case DW_OP_GNU_const_type: + base_type = loc->dw_loc_oprnd1.v.val_die_ref.die; + break; + case DW_OP_GNU_entry_value: + mark_base_types (loc->dw_loc_oprnd1.v.val_loc); + continue; + default: + continue; + } + gcc_assert (base_type->die_parent == comp_unit_die ()); + if (base_type->die_mark) + base_type->die_mark++; + else + { + VEC_safe_push (dw_die_ref, heap, base_types, base_type); + base_type->die_mark = 1; + } } +} - /* Mark children. */ - FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c)); +/* Comparison function for sorting marked base types. */ + +static int +base_type_cmp (const void *x, const void *y) +{ + dw_die_ref dx = *(const dw_die_ref *) x; + dw_die_ref dy = *(const dw_die_ref *) y; + unsigned int byte_size1, byte_size2; + unsigned int encoding1, encoding2; + if (dx->die_mark > dy->die_mark) + return -1; + if (dx->die_mark < dy->die_mark) + return 1; + byte_size1 = get_AT_unsigned (dx, DW_AT_byte_size); + byte_size2 = get_AT_unsigned (dy, DW_AT_byte_size); + if (byte_size1 < byte_size2) + return 1; + if (byte_size1 > byte_size2) + return -1; + encoding1 = get_AT_unsigned (dx, DW_AT_encoding); + encoding2 = get_AT_unsigned (dy, DW_AT_encoding); + if (encoding1 < encoding2) + return 1; + if (encoding1 > encoding2) + return -1; + return 0; } -/* Walk the tree DIE and mark types that we actually use. */ +/* Move base types marked by mark_base_types as early as possible + in the CU, sorted by decreasing usage count both to make the + uleb128 references as small as possible and to make sure they + will have die_offset already computed by calc_die_sizes when + sizes of typed stack loc ops is computed. */ static void -prune_unused_types_walk (dw_die_ref die) +move_marked_base_types (void) { - dw_die_ref c; + unsigned int i; + dw_die_ref base_type, die, c; - /* Don't do anything if this node is already marked and - children have been marked as well. */ - if (die->die_mark == 2) + if (VEC_empty (dw_die_ref, base_types)) return; - switch (die->die_tag) + /* Sort by decreasing usage count, they will be added again in that + order later on. */ + VEC_qsort (dw_die_ref, base_types, base_type_cmp); + die = comp_unit_die (); + c = die->die_child; + do { - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_class_type: - if (die->die_perennial_p) - break; - - for (c = die->die_parent; c; c = c->die_parent) - if (c->die_tag == DW_TAG_subprogram) - break; - - /* Finding used static member functions inside of classes - is needed just for local classes, because for other classes - static member function DIEs with DW_AT_specification - are emitted outside of the DW_TAG_*_type. If we ever change - it, we'd need to call this even for non-local classes. */ - if (c) - prune_unused_types_walk_local_classes (die); - - /* It's a type node --- don't mark it. */ - return; - - case DW_TAG_const_type: - case DW_TAG_packed_type: - case DW_TAG_pointer_type: - case DW_TAG_reference_type: - case DW_TAG_rvalue_reference_type: - case DW_TAG_volatile_type: - case DW_TAG_typedef: - case DW_TAG_array_type: - case DW_TAG_interface_type: - case DW_TAG_friend: - case DW_TAG_variant_part: - case DW_TAG_enumeration_type: - case DW_TAG_subroutine_type: - case DW_TAG_string_type: - case DW_TAG_set_type: - case DW_TAG_subrange_type: - case DW_TAG_ptr_to_member_type: - case DW_TAG_file_type: - if (die->die_perennial_p) - break; + dw_die_ref prev = c; + c = c->die_sib; + while (c->die_mark) + { + remove_child_with_prev (c, prev); + /* As base types got marked, there must be at least + one node other than DW_TAG_base_type. */ + gcc_assert (c != c->die_sib); + c = c->die_sib; + } + } + while (c != die->die_child); + gcc_assert (die->die_child); + c = die->die_child; + for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++) + { + base_type->die_mark = 0; + base_type->die_sib = c->die_sib; + c->die_sib = base_type; + c = base_type; + } +} - /* It's a type node --- don't mark it. */ - return; +/* Helper function for resolve_addr, attempt to resolve + one CONST_STRING, return non-zero if not successful. Similarly verify that + SYMBOL_REFs refer to variables emitted in the current CU. */ - default: - /* Mark everything else. */ - break; - } +static int +resolve_one_addr (rtx *addr, void *data ATTRIBUTE_UNUSED) +{ + rtx rtl = *addr; - if (die->die_mark == 0) + if (GET_CODE (rtl) == CONST_STRING) { - die->die_mark = 1; + size_t len = strlen (XSTR (rtl, 0)) + 1; + tree t = build_string (len, XSTR (rtl, 0)); + tree tlen = size_int (len - 1); + TREE_TYPE (t) + = build_array_type (char_type_node, build_index_type (tlen)); + rtl = lookup_constant_def (t); + if (!rtl || !MEM_P (rtl)) + return 1; + rtl = XEXP (rtl, 0); + VEC_safe_push (rtx, gc, used_rtx_array, rtl); + *addr = rtl; + return 0; + } - /* Now, mark any dies referenced from here. */ - prune_unused_types_walk_attribs (die); + if (GET_CODE (rtl) == SYMBOL_REF + && SYMBOL_REF_DECL (rtl)) + { + if (TREE_CONSTANT_POOL_ADDRESS_P (rtl)) + { + if (!TREE_ASM_WRITTEN (DECL_INITIAL (SYMBOL_REF_DECL (rtl)))) + return 1; + } + else if (!TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl))) + return 1; } - die->die_mark = 2; + if (GET_CODE (rtl) == CONST + && for_each_rtx (&XEXP (rtl, 0), resolve_one_addr, NULL)) + return 1; - /* Mark children. */ - FOR_EACH_CHILD (die, c, prune_unused_types_walk (c)); + return 0; } -/* Increment the string counts on strings referred to from DIE's - attributes. */ +/* Helper function for resolve_addr, handle one location + expression, return false if at least one CONST_STRING or SYMBOL_REF in + the location list couldn't be resolved. */ -static void -prune_unused_types_update_strings (dw_die_ref die) +static bool +resolve_addr_in_expr (dw_loc_descr_ref loc) { - dw_attr_ref a; - unsigned ix; - - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) - if (AT_class (a) == dw_val_class_str) + dw_loc_descr_ref keep = NULL; + for (; loc; loc = loc->dw_loc_next) + switch (loc->dw_loc_opc) { - struct indirect_string_node *s = a->dw_attr_val.v.val_str; - s->refcount++; - /* Avoid unnecessarily putting strings that are used less than - twice in the hash table. */ - if (s->refcount - == ((DEBUG_STR_SECTION_FLAGS & SECTION_MERGE) ? 1 : 2)) + case DW_OP_addr: + if (resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL)) + return false; + break; + case DW_OP_const4u: + case DW_OP_const8u: + if (loc->dtprel + && resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL)) + return false; + break; + case DW_OP_plus_uconst: + if (size_of_loc_descr (loc) + > size_of_int_loc_descriptor (loc->dw_loc_oprnd1.v.val_unsigned) + + 1 + && loc->dw_loc_oprnd1.v.val_unsigned > 0) { - void ** slot; - slot = htab_find_slot_with_hash (debug_str_hash, s->str, - htab_hash_string (s->str), - INSERT); - gcc_assert (*slot == NULL); - *slot = s; + dw_loc_descr_ref repl + = int_loc_descriptor (loc->dw_loc_oprnd1.v.val_unsigned); + add_loc_descr (&repl, new_loc_descr (DW_OP_plus, 0, 0)); + add_loc_descr (&repl, loc->dw_loc_next); + *loc = *repl; + } + break; + case DW_OP_implicit_value: + if (loc->dw_loc_oprnd2.val_class == dw_val_class_addr + && resolve_one_addr (&loc->dw_loc_oprnd2.v.val_addr, NULL)) + return false; + break; + case DW_OP_GNU_implicit_pointer: + case DW_OP_GNU_parameter_ref: + if (loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref) + { + dw_die_ref ref + = lookup_decl_die (loc->dw_loc_oprnd1.v.val_decl_ref); + if (ref == NULL) + return false; + loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref; + loc->dw_loc_oprnd1.v.val_die_ref.die = ref; + loc->dw_loc_oprnd1.v.val_die_ref.external = 0; + } + break; + 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: + while (loc->dw_loc_next + && loc->dw_loc_next->dw_loc_opc == DW_OP_GNU_convert) + { + dw_die_ref base1, base2; + unsigned enc1, enc2, size1, size2; + if (loc->dw_loc_opc == DW_OP_GNU_regval_type + || loc->dw_loc_opc == DW_OP_GNU_deref_type) + base1 = loc->dw_loc_oprnd2.v.val_die_ref.die; + else if (loc->dw_loc_oprnd1.val_class + == dw_val_class_unsigned_const) + break; + else + base1 = loc->dw_loc_oprnd1.v.val_die_ref.die; + if (loc->dw_loc_next->dw_loc_oprnd1.val_class + == dw_val_class_unsigned_const) + break; + base2 = loc->dw_loc_next->dw_loc_oprnd1.v.val_die_ref.die; + gcc_assert (base1->die_tag == DW_TAG_base_type + && base2->die_tag == DW_TAG_base_type); + enc1 = get_AT_unsigned (base1, DW_AT_encoding); + enc2 = get_AT_unsigned (base2, DW_AT_encoding); + size1 = get_AT_unsigned (base1, DW_AT_byte_size); + size2 = get_AT_unsigned (base2, DW_AT_byte_size); + if (size1 == size2 + && (((enc1 == DW_ATE_unsigned || enc1 == DW_ATE_signed) + && (enc2 == DW_ATE_unsigned || enc2 == DW_ATE_signed) + && loc != keep) + || enc1 == enc2)) + { + /* Optimize away next DW_OP_GNU_convert after + adjusting LOC's base type die reference. */ + if (loc->dw_loc_opc == DW_OP_GNU_regval_type + || loc->dw_loc_opc == DW_OP_GNU_deref_type) + loc->dw_loc_oprnd2.v.val_die_ref.die = base2; + else + loc->dw_loc_oprnd1.v.val_die_ref.die = base2; + loc->dw_loc_next = loc->dw_loc_next->dw_loc_next; + continue; + } + /* Don't change integer DW_OP_GNU_convert after e.g. floating + point typed stack entry. */ + else if (enc1 != DW_ATE_unsigned && enc1 != DW_ATE_signed) + keep = loc->dw_loc_next; + break; } + break; + default: + break; } + return true; } -/* Remove from the tree DIE any dies that aren't marked. */ +/* Resolve DW_OP_addr and DW_AT_const_value CONST_STRING arguments to + an address in .rodata section if the string literal is emitted there, + or remove the containing location list or replace DW_AT_const_value + with DW_AT_location and empty location expression, if it isn't found + in .rodata. Similarly for SYMBOL_REFs, keep only those that refer + to something that has been emitted in the current CU. */ static void -prune_unused_types_prune (dw_die_ref die) +resolve_addr (dw_die_ref die) { dw_die_ref c; + dw_attr_ref a; + dw_loc_list_ref *curr, *start, loc; + unsigned ix; - gcc_assert (die->die_mark); - prune_unused_types_update_strings (die); - - if (! die->die_child) - return; - - c = die->die_child; - do { - dw_die_ref prev = c; - for (c = c->die_sib; ! c->die_mark; c = c->die_sib) - if (c == die->die_child) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) + switch (AT_class (a)) + { + case dw_val_class_loc_list: + start = curr = AT_loc_list_ptr (a); + loc = *curr; + gcc_assert (loc); + /* The same list can be referenced more than once. See if we have + already recorded the result from a previous pass. */ + if (loc->replaced) + *curr = loc->dw_loc_next; + else if (!loc->resolved_addr) + { + /* As things stand, we do not expect or allow one die to + reference a suffix of another die's location list chain. + References must be identical or completely separate. + There is therefore no need to cache the result of this + pass on any list other than the first; doing so + would lead to unnecessary writes. */ + while (*curr) + { + gcc_assert (!(*curr)->replaced && !(*curr)->resolved_addr); + if (!resolve_addr_in_expr ((*curr)->expr)) + { + dw_loc_list_ref next = (*curr)->dw_loc_next; + if (next && (*curr)->ll_symbol) + { + gcc_assert (!next->ll_symbol); + next->ll_symbol = (*curr)->ll_symbol; + } + *curr = next; + } + else + { + mark_base_types ((*curr)->expr); + curr = &(*curr)->dw_loc_next; + } + } + if (loc == *start) + loc->resolved_addr = 1; + else + { + loc->replaced = 1; + loc->dw_loc_next = *start; + } + } + if (!*start) + { + remove_AT (die, a->dw_attr); + ix--; + } + break; + case dw_val_class_loc: { - /* No marked children between 'prev' and the end of the list. */ - if (prev == c) - /* No marked children at all. */ - die->die_child = NULL; - else + dw_loc_descr_ref l = AT_loc (a); + /* For -gdwarf-2 don't attempt to optimize + DW_AT_data_member_location containing + DW_OP_plus_uconst - older consumers might + rely on it being that op instead of a more complex, + but shorter, location description. */ + if ((dwarf_version > 2 + || a->dw_attr != DW_AT_data_member_location + || l == NULL + || l->dw_loc_opc != DW_OP_plus_uconst + || l->dw_loc_next != NULL) + && !resolve_addr_in_expr (l)) { - prev->die_sib = c->die_sib; - die->die_child = prev; + remove_AT (die, a->dw_attr); + ix--; } - return; + else + mark_base_types (l); } + break; + case dw_val_class_addr: + if (a->dw_attr == DW_AT_const_value + && resolve_one_addr (&a->dw_attr_val.v.val_addr, NULL)) + { + remove_AT (die, a->dw_attr); + ix--; + } + if (die->die_tag == DW_TAG_GNU_call_site + && a->dw_attr == DW_AT_abstract_origin) + { + tree tdecl = SYMBOL_REF_DECL (a->dw_attr_val.v.val_addr); + dw_die_ref tdie = lookup_decl_die (tdecl); + if (tdie == NULL + && DECL_EXTERNAL (tdecl) + && DECL_ABSTRACT_ORIGIN (tdecl) == NULL_TREE) + { + force_decl_die (tdecl); + tdie = lookup_decl_die (tdecl); + } + if (tdie) + { + a->dw_attr_val.val_class = dw_val_class_die_ref; + a->dw_attr_val.v.val_die_ref.die = tdie; + a->dw_attr_val.v.val_die_ref.external = 0; + } + else + { + remove_AT (die, a->dw_attr); + ix--; + } + } + break; + default: + break; + } - if (c != prev->die_sib) - prev->die_sib = c; - prune_unused_types_prune (c); - } while (c != die->die_child); -} - -/* A helper function for dwarf2out_finish called through - htab_traverse. Clear .debug_str strings that we haven't already - decided to emit. */ - -static int -prune_indirect_string (void **h, void *v ATTRIBUTE_UNUSED) -{ - struct indirect_string_node *node = (struct indirect_string_node *) *h; - - if (!node->label || !node->refcount) - htab_clear_slot (debug_str_hash, h); - - return 1; + FOR_EACH_CHILD (die, c, resolve_addr (c)); } + +/* Helper routines for optimize_location_lists. + This pass tries to share identical local lists in .debug_loc + section. */ -/* Remove dies representing declarations that we never use. */ +/* Iteratively hash operands of LOC opcode. */ -static void -prune_unused_types (void) +static inline hashval_t +hash_loc_operands (dw_loc_descr_ref loc, hashval_t hash) { - unsigned int i; - limbo_die_node *node; - comdat_type_node *ctnode; - pubname_ref pub; - dcall_entry *dcall; - -#if ENABLE_ASSERT_CHECKING - /* All the marks should already be clear. */ - verify_marks_clear (comp_unit_die); - for (node = limbo_die_list; node; node = node->next) - verify_marks_clear (node->die); - for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) - verify_marks_clear (ctnode->root_die); -#endif /* ENABLE_ASSERT_CHECKING */ - - /* Mark types that are used in global variables. */ - premark_types_used_by_global_vars (); + dw_val_ref val1 = &loc->dw_loc_oprnd1; + dw_val_ref val2 = &loc->dw_loc_oprnd2; - /* Set the mark on nodes that are actually used. */ - prune_unused_types_walk (comp_unit_die); - for (node = limbo_die_list; node; node = node->next) - prune_unused_types_walk (node->die); - for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) + switch (loc->dw_loc_opc) { - prune_unused_types_walk (ctnode->root_die); - prune_unused_types_mark (ctnode->type_die, 1); - } - - /* Also set the mark on nodes referenced from the - pubname_table or arange_table. */ - for (i = 0; VEC_iterate (pubname_entry, pubname_table, i, pub); i++) - prune_unused_types_mark (pub->die, 1); - for (i = 0; i < arange_table_in_use; i++) - prune_unused_types_mark (arange_table[i], 1); + case DW_OP_const4u: + case DW_OP_const8u: + if (loc->dtprel) + goto hash_addr; + /* FALLTHRU */ + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4s: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + case DW_OP_pick: + case DW_OP_plus_uconst: + 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_regx: + case DW_OP_fbreg: + case DW_OP_piece: + case DW_OP_deref_size: + case DW_OP_xderef_size: + hash = iterative_hash_object (val1->v.val_int, hash); + break; + case DW_OP_skip: + case DW_OP_bra: + { + int offset; - /* Mark nodes referenced from the direct call table. */ - for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, dcall); i++) - prune_unused_types_mark (dcall->targ_die, 1); + gcc_assert (val1->val_class == dw_val_class_loc); + offset = val1->v.val_loc->dw_loc_addr - (loc->dw_loc_addr + 3); + hash = iterative_hash_object (offset, hash); + } + break; + case DW_OP_implicit_value: + hash = iterative_hash_object (val1->v.val_unsigned, hash); + switch (val2->val_class) + { + case dw_val_class_const: + hash = iterative_hash_object (val2->v.val_int, hash); + break; + case dw_val_class_vec: + { + unsigned int elt_size = val2->v.val_vec.elt_size; + unsigned int len = val2->v.val_vec.length; - /* Get rid of nodes that aren't marked; and update the string counts. */ - if (debug_str_hash && debug_str_hash_forced) - htab_traverse (debug_str_hash, prune_indirect_string, NULL); - else if (debug_str_hash) - htab_empty (debug_str_hash); - prune_unused_types_prune (comp_unit_die); - for (node = limbo_die_list; node; node = node->next) - prune_unused_types_prune (node->die); - for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) - prune_unused_types_prune (ctnode->root_die); + hash = iterative_hash_object (elt_size, hash); + hash = iterative_hash_object (len, hash); + hash = iterative_hash (val2->v.val_vec.array, + len * elt_size, hash); + } + break; + case dw_val_class_const_double: + hash = iterative_hash_object (val2->v.val_double.low, hash); + hash = iterative_hash_object (val2->v.val_double.high, hash); + break; + case dw_val_class_addr: + hash = iterative_hash_rtx (val2->v.val_addr, hash); + break; + default: + gcc_unreachable (); + } + break; + case DW_OP_bregx: + case DW_OP_bit_piece: + hash = iterative_hash_object (val1->v.val_int, hash); + hash = iterative_hash_object (val2->v.val_int, hash); + break; + case DW_OP_addr: + hash_addr: + if (loc->dtprel) + { + unsigned char dtprel = 0xd1; + hash = iterative_hash_object (dtprel, hash); + } + hash = iterative_hash_rtx (val1->v.val_addr, hash); + break; + case DW_OP_GNU_implicit_pointer: + hash = iterative_hash_object (val2->v.val_int, hash); + break; + case DW_OP_GNU_entry_value: + hash = hash_loc_operands (val1->v.val_loc, hash); + break; + case DW_OP_GNU_regval_type: + case DW_OP_GNU_deref_type: + { + unsigned int byte_size + = get_AT_unsigned (val2->v.val_die_ref.die, DW_AT_byte_size); + unsigned int encoding + = get_AT_unsigned (val2->v.val_die_ref.die, DW_AT_encoding); + hash = iterative_hash_object (val1->v.val_int, hash); + hash = iterative_hash_object (byte_size, hash); + hash = iterative_hash_object (encoding, hash); + } + break; + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + if (val1->val_class == dw_val_class_unsigned_const) + { + hash = iterative_hash_object (val1->v.val_unsigned, hash); + break; + } + /* FALLTHRU */ + case DW_OP_GNU_const_type: + { + unsigned int byte_size + = get_AT_unsigned (val1->v.val_die_ref.die, DW_AT_byte_size); + unsigned int encoding + = get_AT_unsigned (val1->v.val_die_ref.die, DW_AT_encoding); + hash = iterative_hash_object (byte_size, hash); + hash = iterative_hash_object (encoding, hash); + if (loc->dw_loc_opc != DW_OP_GNU_const_type) + break; + hash = iterative_hash_object (val2->val_class, hash); + switch (val2->val_class) + { + case dw_val_class_const: + hash = iterative_hash_object (val2->v.val_int, hash); + break; + case dw_val_class_vec: + { + unsigned int elt_size = val2->v.val_vec.elt_size; + unsigned int len = val2->v.val_vec.length; - /* Leave the marks clear. */ - prune_unmark_dies (comp_unit_die); - for (node = limbo_die_list; node; node = node->next) - prune_unmark_dies (node->die); - for (ctnode = comdat_type_list; ctnode; ctnode = ctnode->next) - prune_unmark_dies (ctnode->root_die); -} + hash = iterative_hash_object (elt_size, hash); + hash = iterative_hash_object (len, hash); + hash = iterative_hash (val2->v.val_vec.array, + len * elt_size, hash); + } + break; + case dw_val_class_const_double: + hash = iterative_hash_object (val2->v.val_double.low, hash); + hash = iterative_hash_object (val2->v.val_double.high, hash); + break; + default: + gcc_unreachable (); + } + } + break; -/* Set the parameter to true if there are any relative pathnames in - the file table. */ -static int -file_table_relative_p (void ** slot, void *param) -{ - bool *p = (bool *) param; - struct dwarf_file_data *d = (struct dwarf_file_data *) *slot; - if (!IS_ABSOLUTE_PATH (d->filename)) - { - *p = true; - return 0; + default: + /* Other codes have no operands. */ + break; } - return 1; -} - -/* Routines to manipulate hash table of comdat type units. */ - -static hashval_t -htab_ct_hash (const void *of) -{ - hashval_t h; - const comdat_type_node *const type_node = (const comdat_type_node *) of; - - memcpy (&h, type_node->signature, sizeof (h)); - return h; -} - -static int -htab_ct_eq (const void *of1, const void *of2) -{ - const comdat_type_node *const type_node_1 = (const comdat_type_node *) of1; - const comdat_type_node *const type_node_2 = (const comdat_type_node *) of2; - - return (! memcmp (type_node_1->signature, type_node_2->signature, - DWARF_TYPE_SIGNATURE_SIZE)); + return hash; } -/* Move a DW_AT_{,MIPS_}linkage_name attribute just added to dw_die_ref - to the location it would have been added, should we know its - DECL_ASSEMBLER_NAME when we added other attributes. This will - probably improve compactness of debug info, removing equivalent - abbrevs, and hide any differences caused by deferring the - computation of the assembler name, triggered by e.g. PCH. */ +/* Iteratively hash the whole DWARF location expression LOC. */ -static inline void -move_linkage_attr (dw_die_ref die) +static inline hashval_t +hash_locs (dw_loc_descr_ref loc, hashval_t hash) { - unsigned ix = VEC_length (dw_attr_node, die->die_attr); - dw_attr_node linkage = *VEC_index (dw_attr_node, die->die_attr, ix - 1); + dw_loc_descr_ref l; + bool sizes_computed = false; + /* Compute sizes, so that DW_OP_skip/DW_OP_bra can be checksummed. */ + size_of_locs (loc); - gcc_assert (linkage.dw_attr == DW_AT_linkage_name - || linkage.dw_attr == DW_AT_MIPS_linkage_name); + for (l = loc; l != NULL; l = l->dw_loc_next) + { + enum dwarf_location_atom opc = l->dw_loc_opc; + hash = iterative_hash_object (opc, hash); + if ((opc == DW_OP_skip || opc == DW_OP_bra) && !sizes_computed) + { + size_of_locs (loc); + sizes_computed = true; + } + hash = hash_loc_operands (l, hash); + } + return hash; +} - while (--ix > 0) - { - dw_attr_node *prev = VEC_index (dw_attr_node, die->die_attr, ix - 1); +/* Compute hash of the whole location list LIST_HEAD. */ - if (prev->dw_attr == DW_AT_decl_line || prev->dw_attr == DW_AT_name) - break; - } +static inline void +hash_loc_list (dw_loc_list_ref list_head) +{ + dw_loc_list_ref curr = list_head; + hashval_t hash = 0; - if (ix != VEC_length (dw_attr_node, die->die_attr) - 1) + for (curr = list_head; curr != NULL; curr = curr->dw_loc_next) { - VEC_pop (dw_attr_node, die->die_attr); - VEC_quick_insert (dw_attr_node, die->die_attr, ix, &linkage); + hash = iterative_hash (curr->begin, strlen (curr->begin) + 1, hash); + hash = iterative_hash (curr->end, strlen (curr->end) + 1, hash); + if (curr->section) + hash = iterative_hash (curr->section, strlen (curr->section) + 1, + hash); + hash = hash_locs (curr->expr, hash); } + list_head->hash = hash; } -/* Helper function for resolve_addr, attempt to resolve - one CONST_STRING, return non-zero if not successful. Similarly verify that - SYMBOL_REFs refer to variables emitted in the current CU. */ +/* Return true if X and Y opcodes have the same operands. */ -static int -resolve_one_addr (rtx *addr, void *data ATTRIBUTE_UNUSED) +static inline bool +compare_loc_operands (dw_loc_descr_ref x, dw_loc_descr_ref y) { - rtx rtl = *addr; + dw_val_ref valx1 = &x->dw_loc_oprnd1; + dw_val_ref valx2 = &x->dw_loc_oprnd2; + dw_val_ref valy1 = &y->dw_loc_oprnd1; + dw_val_ref valy2 = &y->dw_loc_oprnd2; - if (GET_CODE (rtl) == CONST_STRING) + switch (x->dw_loc_opc) { - size_t len = strlen (XSTR (rtl, 0)) + 1; - tree t = build_string (len, XSTR (rtl, 0)); - tree tlen = build_int_cst (NULL_TREE, len - 1); - TREE_TYPE (t) - = build_array_type (char_type_node, build_index_type (tlen)); - rtl = lookup_constant_def (t); - if (!rtl || !MEM_P (rtl)) - return 1; - rtl = XEXP (rtl, 0); - VEC_safe_push (rtx, gc, used_rtx_array, rtl); - *addr = rtl; - return 0; + case DW_OP_const4u: + case DW_OP_const8u: + if (x->dtprel) + goto hash_addr; + /* FALLTHRU */ + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4s: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + case DW_OP_pick: + case DW_OP_plus_uconst: + 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_regx: + case DW_OP_fbreg: + case DW_OP_piece: + case DW_OP_deref_size: + case DW_OP_xderef_size: + return valx1->v.val_int == valy1->v.val_int; + case DW_OP_skip: + case DW_OP_bra: + gcc_assert (valx1->val_class == dw_val_class_loc + && valy1->val_class == dw_val_class_loc + && x->dw_loc_addr == y->dw_loc_addr); + return valx1->v.val_loc->dw_loc_addr == valy1->v.val_loc->dw_loc_addr; + case DW_OP_implicit_value: + if (valx1->v.val_unsigned != valy1->v.val_unsigned + || valx2->val_class != valy2->val_class) + return false; + switch (valx2->val_class) + { + case dw_val_class_const: + return valx2->v.val_int == valy2->v.val_int; + case dw_val_class_vec: + return valx2->v.val_vec.elt_size == valy2->v.val_vec.elt_size + && valx2->v.val_vec.length == valy2->v.val_vec.length + && memcmp (valx2->v.val_vec.array, valy2->v.val_vec.array, + valx2->v.val_vec.elt_size + * valx2->v.val_vec.length) == 0; + case dw_val_class_const_double: + return valx2->v.val_double.low == valy2->v.val_double.low + && valx2->v.val_double.high == valy2->v.val_double.high; + case dw_val_class_addr: + return rtx_equal_p (valx2->v.val_addr, valy2->v.val_addr); + default: + gcc_unreachable (); + } + case DW_OP_bregx: + case DW_OP_bit_piece: + return valx1->v.val_int == valy1->v.val_int + && valx2->v.val_int == valy2->v.val_int; + case DW_OP_addr: + hash_addr: + return rtx_equal_p (valx1->v.val_addr, valy1->v.val_addr); + case DW_OP_GNU_implicit_pointer: + return valx1->val_class == dw_val_class_die_ref + && valx1->val_class == valy1->val_class + && valx1->v.val_die_ref.die == valy1->v.val_die_ref.die + && valx2->v.val_int == valy2->v.val_int; + case DW_OP_GNU_entry_value: + return compare_loc_operands (valx1->v.val_loc, valy1->v.val_loc); + case DW_OP_GNU_const_type: + if (valx1->v.val_die_ref.die != valy1->v.val_die_ref.die + || valx2->val_class != valy2->val_class) + return false; + switch (valx2->val_class) + { + case dw_val_class_const: + return valx2->v.val_int == valy2->v.val_int; + case dw_val_class_vec: + return valx2->v.val_vec.elt_size == valy2->v.val_vec.elt_size + && valx2->v.val_vec.length == valy2->v.val_vec.length + && memcmp (valx2->v.val_vec.array, valy2->v.val_vec.array, + valx2->v.val_vec.elt_size + * valx2->v.val_vec.length) == 0; + case dw_val_class_const_double: + return valx2->v.val_double.low == valy2->v.val_double.low + && valx2->v.val_double.high == valy2->v.val_double.high; + default: + gcc_unreachable (); + } + case DW_OP_GNU_regval_type: + case DW_OP_GNU_deref_type: + return valx1->v.val_int == valy1->v.val_int + && valx2->v.val_die_ref.die == valy2->v.val_die_ref.die; + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + if (valx1->val_class != valy1->val_class) + return false; + if (valx1->val_class == dw_val_class_unsigned_const) + return valx1->v.val_unsigned == valy1->v.val_unsigned; + return valx1->v.val_die_ref.die == valy1->v.val_die_ref.die; + case DW_OP_GNU_parameter_ref: + return valx1->val_class == dw_val_class_die_ref + && valx1->val_class == valy1->val_class + && valx1->v.val_die_ref.die == valy1->v.val_die_ref.die; + default: + /* Other codes have no operands. */ + return true; } +} - if (GET_CODE (rtl) == SYMBOL_REF - && SYMBOL_REF_DECL (rtl) - && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl))) - return 1; +/* Return true if DWARF location expressions X and Y are the same. */ - if (GET_CODE (rtl) == CONST - && for_each_rtx (&XEXP (rtl, 0), resolve_one_addr, NULL)) - return 1; +static inline bool +compare_locs (dw_loc_descr_ref x, dw_loc_descr_ref y) +{ + for (; x != NULL && y != NULL; x = x->dw_loc_next, y = y->dw_loc_next) + if (x->dw_loc_opc != y->dw_loc_opc + || x->dtprel != y->dtprel + || !compare_loc_operands (x, y)) + break; + return x == NULL && y == NULL; +} - return 0; +/* Return precomputed hash of location list X. */ + +static hashval_t +loc_list_hash (const void *x) +{ + return ((const struct dw_loc_list_struct *) x)->hash; } -/* Helper function for resolve_addr, handle one location - expression, return false if at least one CONST_STRING or SYMBOL_REF in - the location list couldn't be resolved. */ +/* Return 1 if location lists X and Y are the same. */ -static bool -resolve_addr_in_expr (dw_loc_descr_ref loc) +static int +loc_list_eq (const void *x, const void *y) { - for (; loc; loc = loc->dw_loc_next) - if (((loc->dw_loc_opc == DW_OP_addr || loc->dtprel) - && resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL)) - || (loc->dw_loc_opc == DW_OP_implicit_value - && loc->dw_loc_oprnd2.val_class == dw_val_class_addr - && resolve_one_addr (&loc->dw_loc_oprnd2.v.val_addr, NULL))) - return false; - return true; + const struct dw_loc_list_struct *a = (const struct dw_loc_list_struct *) x; + const struct dw_loc_list_struct *b = (const struct dw_loc_list_struct *) y; + if (a == b) + return 1; + if (a->hash != b->hash) + return 0; + for (; a != NULL && b != NULL; a = a->dw_loc_next, b = b->dw_loc_next) + if (strcmp (a->begin, b->begin) != 0 + || strcmp (a->end, b->end) != 0 + || (a->section == NULL) != (b->section == NULL) + || (a->section && strcmp (a->section, b->section) != 0) + || !compare_locs (a->expr, b->expr)) + break; + return a == NULL && b == NULL; } -/* Resolve DW_OP_addr and DW_AT_const_value CONST_STRING arguments to - an address in .rodata section if the string literal is emitted there, - or remove the containing location list or replace DW_AT_const_value - with DW_AT_location and empty location expression, if it isn't found - in .rodata. Similarly for SYMBOL_REFs, keep only those that refer - to something that has been emitted in the current CU. */ +/* Recursively optimize location lists referenced from DIE + children and share them whenever possible. */ static void -resolve_addr (dw_die_ref die) +optimize_location_lists_1 (dw_die_ref die, htab_t htab) { dw_die_ref c; dw_attr_ref a; - dw_loc_list_ref *curr; unsigned ix; + void **slot; - for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++) - switch (AT_class (a)) + FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a) + if (AT_class (a) == dw_val_class_loc_list) { - case dw_val_class_loc_list: - curr = AT_loc_list_ptr (a); - while (*curr) - { - if (!resolve_addr_in_expr ((*curr)->expr)) - { - dw_loc_list_ref next = (*curr)->dw_loc_next; - if (next && (*curr)->ll_symbol) - { - gcc_assert (!next->ll_symbol); - next->ll_symbol = (*curr)->ll_symbol; - } - *curr = next; - } - else - curr = &(*curr)->dw_loc_next; - } - if (!AT_loc_list (a)) - { - remove_AT (die, a->dw_attr); - ix--; - } - break; - case dw_val_class_loc: - if (!resolve_addr_in_expr (AT_loc (a))) - { - remove_AT (die, a->dw_attr); - ix--; - } - break; - case dw_val_class_addr: - if (a->dw_attr == DW_AT_const_value - && resolve_one_addr (&a->dw_attr_val.v.val_addr, NULL)) - { - remove_AT (die, a->dw_attr); - ix--; - } - break; - default: - break; + dw_loc_list_ref list = AT_loc_list (a); + /* TODO: perform some optimizations here, before hashing + it and storing into the hash table. */ + hash_loc_list (list); + slot = htab_find_slot_with_hash (htab, list, list->hash, + INSERT); + if (*slot == NULL) + *slot = (void *) list; + else + a->dw_attr_val.v.val_loc_list = (dw_loc_list_ref) *slot; } - FOR_EACH_CHILD (die, c, resolve_addr (c)); + FOR_EACH_CHILD (die, c, optimize_location_lists_1 (c, htab)); } +/* Optimize location lists referenced from DIE + children and share them whenever possible. */ + +static void +optimize_location_lists (dw_die_ref die) +{ + htab_t htab = htab_create (500, loc_list_hash, loc_list_eq, NULL); + optimize_location_lists_1 (die, htab); + htab_delete (htab); +} + /* Output stuff that dwarf requires at the end of every file, and generate the DWARF-2 debugging info. */ @@ -22239,22 +22423,31 @@ dwarf2out_finish (const char *filename) limbo_die_node *node, *next_node; comdat_type_node *ctnode; htab_t comdat_type_table; - dw_die_ref die = 0; unsigned int i; + /* PCH might result in DW_AT_producer string being restored from the + header compilation, fix it up if needed. */ + dw_attr_ref producer = get_AT (comp_unit_die (), DW_AT_producer); + if (strcmp (AT_string (producer), producer_string) != 0) + { + struct indirect_string_node *node = find_AT_string (producer_string); + producer->dw_attr_val.v.val_str = node; + } + + gen_scheduled_generic_parms_dies (); gen_remaining_tmpl_value_param_die_attribute (); /* Add the name for the main input file now. We delayed this from dwarf2out_init to avoid complications with PCH. */ - add_name_attribute (comp_unit_die, remap_debug_filename (filename)); + add_name_attribute (comp_unit_die (), remap_debug_filename (filename)); if (!IS_ABSOLUTE_PATH (filename)) - add_comp_dir_attribute (comp_unit_die); - else if (get_AT (comp_unit_die, DW_AT_comp_dir) == NULL) + add_comp_dir_attribute (comp_unit_die ()); + else if (get_AT (comp_unit_die (), DW_AT_comp_dir) == NULL) { bool p = false; htab_traverse (file_table, file_table_relative_p, &p); if (p) - add_comp_dir_attribute (comp_unit_die); + add_comp_dir_attribute (comp_unit_die ()); } for (i = 0; i < VEC_length (deferred_locations, deferred_locations_list); i++) @@ -22262,6 +22455,7 @@ dwarf2out_finish (const char *filename) add_location_or_const_value_attribute ( VEC_index (deferred_locations, deferred_locations_list, i)->die, VEC_index (deferred_locations, deferred_locations_list, i)->variable, + false, DW_AT_location); } @@ -22272,20 +22466,20 @@ dwarf2out_finish (const char *filename) instance. */ for (node = limbo_die_list; node; node = next_node) { + dw_die_ref die = node->die; next_node = node->next; - die = node->die; if (die->die_parent == NULL) { dw_die_ref origin = get_AT_ref (die, DW_AT_abstract_origin); - if (origin) + if (origin && origin->die_parent) add_child_die (origin->die_parent, die); - else if (die == comp_unit_die) + else if (is_cu_die (die)) ; else if (seen_error ()) /* It's OK to be confused by errors in the input. */ - add_child_die (comp_unit_die, die); + add_child_die (comp_unit_die (), die); else { /* In certain situations, the lexical block containing a @@ -22315,14 +22509,21 @@ dwarf2out_finish (const char *filename) if (origin) add_child_die (origin, die); else - add_child_die (comp_unit_die, die); + add_child_die (comp_unit_die (), die); } } } limbo_die_list = NULL; - resolve_addr (comp_unit_die); +#if ENABLE_ASSERT_CHECKING + { + dw_die_ref die = comp_unit_die (), c; + FOR_EACH_CHILD (die, c, gcc_assert (! c->die_mark)); + } +#endif + resolve_addr (comp_unit_die ()); + move_marked_base_types (); for (node = deferred_asm_name; node; node = node->next) { @@ -22345,13 +22546,13 @@ dwarf2out_finish (const char *filename) /* Generate separate CUs for each of the include files we've seen. They will go into limbo_die_list. */ - if (flag_eliminate_dwarf2_dups && dwarf_version < 4) - break_out_includes (comp_unit_die); + if (flag_eliminate_dwarf2_dups && ! use_debug_types) + break_out_includes (comp_unit_die ()); /* Generate separate COMDAT sections for type DIEs. */ - if (dwarf_version >= 4) + if (use_debug_types) { - break_out_comdat_types (comp_unit_die); + break_out_comdat_types (comp_unit_die ()); /* Each new type_unit DIE was added to the limbo die list when created. Since these have all been added to comdat_type_list, clear the @@ -22363,7 +22564,7 @@ dwarf2out_finish (const char *filename) references to the main compile unit). */ for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next) copy_decls_for_unworthy_types (ctnode->root_die); - copy_decls_for_unworthy_types (comp_unit_die); + copy_decls_for_unworthy_types (comp_unit_die ()); /* In the process of copying declarations from one unit to another, we may have left some declarations behind that are no longer @@ -22373,7 +22574,7 @@ dwarf2out_finish (const char *filename) /* Traverse the DIE's and add add sibling attributes to those DIE's that have children. */ - add_sibling_attributes (comp_unit_die); + add_sibling_attributes (comp_unit_die ()); for (node = limbo_die_list; node; node = node->next) add_sibling_attributes (node->die); for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next) @@ -22382,84 +22583,73 @@ dwarf2out_finish (const char *filename) /* Output a terminator label for the .text section. */ switch_to_section (text_section); targetm.asm_out.internal_label (asm_out_file, TEXT_END_LABEL, 0); - if (flag_reorder_blocks_and_partition) + if (cold_text_section) { - switch_to_section (unlikely_text_section ()); + switch_to_section (cold_text_section); targetm.asm_out.internal_label (asm_out_file, COLD_END_LABEL, 0); } /* We can only use the low/high_pc attributes if all of the code was in .text. */ - if (!have_multiple_function_sections - || !(dwarf_version >= 3 || !dwarf_strict)) + if (!have_multiple_function_sections + || (dwarf_version < 3 && dwarf_strict)) { - add_AT_lbl_id (comp_unit_die, DW_AT_low_pc, text_section_label); - add_AT_lbl_id (comp_unit_die, DW_AT_high_pc, text_end_label); + /* Don't add if the CU has no associated code. */ + if (text_section_used) + { + add_AT_lbl_id (comp_unit_die (), DW_AT_low_pc, text_section_label); + add_AT_lbl_id (comp_unit_die (), DW_AT_high_pc, text_end_label); + } } - else { - unsigned fde_idx = 0; + unsigned fde_idx; + dw_fde_ref fde; bool range_list_added = false; - /* We need to give .debug_loc and .debug_ranges an appropriate - "base address". Use zero so that these addresses become - absolute. Historically, we've emitted the unexpected - DW_AT_entry_pc instead of DW_AT_low_pc for this purpose. - Emit both to give time for other tools to adapt. */ - add_AT_addr (comp_unit_die, DW_AT_low_pc, const0_rtx); - add_AT_addr (comp_unit_die, DW_AT_entry_pc, const0_rtx); - if (text_section_used) - add_ranges_by_labels (comp_unit_die, text_section_label, + add_ranges_by_labels (comp_unit_die (), text_section_label, text_end_label, &range_list_added); - if (flag_reorder_blocks_and_partition && cold_text_section_used) - add_ranges_by_labels (comp_unit_die, cold_text_section_label, + if (cold_text_section_used) + add_ranges_by_labels (comp_unit_die (), cold_text_section_label, cold_end_label, &range_list_added); - for (fde_idx = 0; fde_idx < fde_table_in_use; fde_idx++) + FOR_EACH_VEC_ELT (dw_fde_ref, fde_vec, fde_idx, fde) { - dw_fde_ref fde = &fde_table[fde_idx]; - - if (fde->dw_fde_switched_sections) - { - if (!fde->in_std_section) - add_ranges_by_labels (comp_unit_die, - fde->dw_fde_hot_section_label, - fde->dw_fde_hot_section_end_label, - &range_list_added); - if (!fde->cold_in_std_section) - add_ranges_by_labels (comp_unit_die, - fde->dw_fde_unlikely_section_label, - fde->dw_fde_unlikely_section_end_label, - &range_list_added); - } - else if (!fde->in_std_section) - add_ranges_by_labels (comp_unit_die, fde->dw_fde_begin, + if (!fde->in_std_section) + add_ranges_by_labels (comp_unit_die (), fde->dw_fde_begin, fde->dw_fde_end, &range_list_added); + if (fde->dw_fde_second_begin && !fde->second_in_std_section) + add_ranges_by_labels (comp_unit_die (), fde->dw_fde_second_begin, + fde->dw_fde_second_end, &range_list_added); } if (range_list_added) - add_ranges (NULL); - } - - /* Output location list section if necessary. */ - if (have_location_lists) - { - /* Output the location lists info. */ - switch_to_section (debug_loc_section); - ASM_GENERATE_INTERNAL_LABEL (loc_section_label, - DEBUG_LOC_SECTION_LABEL, 0); - ASM_OUTPUT_LABEL (asm_out_file, loc_section_label); - output_location_lists (die); + { + /* We need to give .debug_loc and .debug_ranges an appropriate + "base address". Use zero so that these addresses become + absolute. Historically, we've emitted the unexpected + DW_AT_entry_pc instead of DW_AT_low_pc for this purpose. + Emit both to give time for other tools to adapt. */ + add_AT_addr (comp_unit_die (), DW_AT_low_pc, const0_rtx); + if (! dwarf_strict && dwarf_version < 4) + add_AT_addr (comp_unit_die (), DW_AT_entry_pc, const0_rtx); + + add_ranges (NULL); + } } if (debug_info_level >= DINFO_LEVEL_NORMAL) - add_AT_lineptr (comp_unit_die, DW_AT_stmt_list, + add_AT_lineptr (comp_unit_die (), DW_AT_stmt_list, debug_line_section_label); if (debug_info_level >= DINFO_LEVEL_VERBOSE) - add_AT_macptr (comp_unit_die, DW_AT_macro_info, macinfo_section_label); + add_AT_macptr (comp_unit_die (), + dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros, + macinfo_section_label); + + if (have_location_lists) + optimize_location_lists (comp_unit_die ()); /* Output all of the compilation units. We put the main one last so that the offsets are available to output_pubnames. */ @@ -22488,16 +22678,32 @@ dwarf2out_finish (const char *filename) htab_delete (comdat_type_table); /* Output the main compilation unit if non-empty or if .debug_macinfo - has been emitted. */ - output_comp_unit (comp_unit_die, debug_info_level >= DINFO_LEVEL_VERBOSE); + will be emitted. */ + output_comp_unit (comp_unit_die (), debug_info_level >= DINFO_LEVEL_VERBOSE); /* Output the abbreviation table. */ - switch_to_section (debug_abbrev_section); - output_abbrev_section (); + if (abbrev_die_table_in_use != 1) + { + switch_to_section (debug_abbrev_section); + ASM_OUTPUT_LABEL (asm_out_file, abbrev_section_label); + output_abbrev_section (); + } + + /* Output location list section if necessary. */ + if (have_location_lists) + { + /* Output the location lists info. */ + switch_to_section (debug_loc_section); + ASM_GENERATE_INTERNAL_LABEL (loc_section_label, + DEBUG_LOC_SECTION_LABEL, 0); + ASM_OUTPUT_LABEL (asm_out_file, loc_section_label); + output_location_lists (comp_unit_die ()); + } /* Output public names table if necessary. */ if (!VEC_empty (pubname_entry, pubname_table)) { + gcc_assert (info_section_emitted); switch_to_section (debug_pubnames_section); output_pubnames (pubname_table); } @@ -22508,28 +22714,40 @@ dwarf2out_finish (const char *filename) simply won't look for the section. */ if (!VEC_empty (pubname_entry, pubtype_table)) { - switch_to_section (debug_pubtypes_section); - output_pubnames (pubtype_table); + bool empty = false; + + if (flag_eliminate_unused_debug_types) + { + /* The pubtypes table might be emptied by pruning unused items. */ + unsigned i; + pubname_ref p; + empty = true; + FOR_EACH_VEC_ELT (pubname_entry, pubtype_table, i, p) + if (p->die->die_offset != 0) + { + empty = false; + break; + } + } + if (!empty) + { + gcc_assert (info_section_emitted); + switch_to_section (debug_pubtypes_section); + output_pubnames (pubtype_table); + } } - /* Output direct and virtual call tables if necessary. */ - if (!VEC_empty (dcall_entry, dcall_table)) - { - switch_to_section (debug_dcall_section); - output_dcall_table (); - } - if (!VEC_empty (vcall_entry, vcall_table)) + /* Output the address range information if a CU (.debug_info section) + was emitted. We output an empty table even if we had no functions + to put in it. This because the consumer has no way to tell the + difference between an empty table that we omitted and failure to + generate a table that would have contained data. */ + if (info_section_emitted) { - switch_to_section (debug_vcall_section); - output_vcall_table (); - } + unsigned long aranges_length = size_of_aranges (); - /* Output the address range information. We only put functions in the arange - table, so don't write it out if we don't have any. */ - if (fde_table_in_use) - { switch_to_section (debug_aranges_section); - output_aranges (); + output_aranges (aranges_length); } /* Output ranges section if necessary. */ @@ -22540,24 +22758,26 @@ dwarf2out_finish (const char *filename) output_ranges (); } + /* Have to end the macro section. */ + if (debug_info_level >= DINFO_LEVEL_VERBOSE) + { + switch_to_section (debug_macinfo_section); + ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label); + if (!VEC_empty (macinfo_entry, macinfo_table)) + output_macinfo (); + dw2_asm_output_data (1, 0, "End compilation unit"); + } + /* Output the source line correspondence table. We must do this even if there is no line information. Otherwise, on an empty translation unit, we will generate a present, but empty, .debug_info section. IRIX 6.5 `nm' will then complain when examining the file. This is done late so that any filenames used by the debug_info section are marked as 'used'. */ + switch_to_section (debug_line_section); + ASM_OUTPUT_LABEL (asm_out_file, debug_line_section_label); if (! DWARF2_ASM_LINE_DEBUG_INFO) - { - switch_to_section (debug_line_section); - output_line_info (); - } - - /* Have to end the macro section. */ - if (debug_info_level >= DINFO_LEVEL_VERBOSE) - { - switch_to_section (debug_macinfo_section); - dw2_asm_output_data (1, 0, "End compilation unit"); - } + output_line_info (); /* If we emitted any DW_FORM_strp form attribute, output the string table too. */