/* 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 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
Contributed by Gary Funck (gary@intrepid.com).
Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com).
Extensively modified by Jason Merrill (jason@cygnus.com).
#include "expr.h"
#include "libfuncs.h"
#include "except.h"
-#include "elf/dwarf2.h"
+#include "dwarf2.h"
#include "dwarf2out.h"
#include "dwarf2asm.h"
#include "toplev.h"
#include "hashtab.h"
#include "cgraph.h"
#include "input.h"
+#include "gimple.h"
+#include "tree-pass.h"
#ifdef DWARF2_DEBUGGING_INFO
static void dwarf2out_source_line (unsigned int, const char *, int, bool);
static rtx last_var_location_insn;
#endif
+#ifdef VMS_DEBUGGING_INFO
+int vms_file_stats_name (const char *, long long *, long *, char *, int *);
+
+/* Define this macro to be a nonzero value if the directory specifications
+ which are output in the debug info should end with a separator. */
+#define DWARF2_DIR_SHOULD_END_WITH_SEPARATOR 1
+/* Define this macro to evaluate to a nonzero value if GCC should refrain
+ from generating indirect strings in DWARF2 debug information, for instance
+ if your target is stuck with an old version of GDB that is unable to
+ process them properly or uses VMS Debug. */
+#define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 1
+#else
+#define DWARF2_DIR_SHOULD_END_WITH_SEPARATOR 0
+#define DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET 0
+#endif
+
#ifndef DWARF2_FRAME_INFO
# ifdef DWARF2_DEBUGGING_INFO
# define DWARF2_FRAME_INFO \
#endif
if (!flag_dwarf2_cfi_asm || !dwarf2out_do_frame ())
return false;
- if (saved_do_cfi_asm || !eh_personality_libfunc)
+ if (saved_do_cfi_asm)
return true;
if (!HAVE_GAS_CFI_PERSONALITY_DIRECTIVE)
return false;
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;
}
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 ";#"
const char *dw_fde_hot_section_end_label;
const char *dw_fde_unlikely_section_label;
const char *dw_fde_unlikely_section_end_label;
- bool dw_fde_switched_sections;
dw_cfi_ref dw_fde_cfi;
+ dw_cfi_ref dw_fde_switch_cfi; /* Last CFI before switching sections. */
unsigned funcdef_number;
HOST_WIDE_INT stack_realignment;
/* Dynamic realign argument pointer register. */
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;
#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
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;
static void output_cfi_directive (dw_cfi_ref);
static void output_call_frame_info (int);
static void dwarf2out_note_section_used (void);
-static void dwarf2out_stack_adjust (rtx, bool);
-static void dwarf2out_args_size_adjust (HOST_WIDE_INT, const char *);
static void flush_queued_reg_saves (void);
static bool clobbers_queued_reg_save (const_rtx);
static void dwarf2out_frame_debug_expr (rtx, const char *);
/* Emit the state save. */
emit_cfa_remember = false;
- cfi_remember = new_cfi ();
+ cfi_remember = new_cfi ();
cfi_remember->dw_cfi_opc = DW_CFA_remember_state;
add_fde_cfi (label, cfi_remember);
}
if (loc.reg == old_cfa.reg && !loc.indirect)
{
/* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
- the CFA register did not change but the offset did. The data
+ 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 (need_data_align_sf_opcode (loc.offset))
+ if (loc.offset < 0)
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
else
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
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 (need_data_align_sf_opcode (loc.offset))
+ if (loc.offset < 0)
cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
else
cfi->dw_cfi_opc = DW_CFA_def_cfa;
&& sreg == INVALID_REGNUM)
{
cfi->dw_cfi_opc = DW_CFA_expression;
- cfi->dw_cfi_oprnd2.dw_cfi_reg_num = reg;
- cfi->dw_cfi_oprnd1.dw_cfi_loc
+ 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)
add_fde_cfi (label, cfi);
}
-/* Add a CFI to update the running total of the size of arguments
- pushed onto the stack. */
-
-void
-dwarf2out_args_size (const char *label, HOST_WIDE_INT size)
-{
- dw_cfi_ref cfi;
-
- if (size == old_args_size)
- return;
-
- old_args_size = size;
-
- cfi = new_cfi ();
- cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
- cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
- add_fde_cfi (label, cfi);
-}
-
/* Entry point for saving a register to the stack. REG is the GCC register
number. LABEL and OFFSET are passed to reg_save. */
VEC_free (rtx, heap, next);
}
+/* Add a CFI to update the running total of the size of arguments
+ pushed onto the stack. */
+
+static void
+dwarf2out_args_size (const char *label, HOST_WIDE_INT size)
+{
+ dw_cfi_ref cfi;
+
+ if (size == old_args_size)
+ return;
+
+ old_args_size = size;
+
+ cfi = new_cfi ();
+ cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
+ cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
+ add_fde_cfi (label, cfi);
+}
+
+/* Record a stack adjustment of OFFSET bytes. */
+
+static void
+dwarf2out_stack_adjust (HOST_WIDE_INT offset, const char *label)
+{
+ if (cfa.reg == STACK_POINTER_REGNUM)
+ cfa.offset += offset;
+
+ if (cfa_store.reg == STACK_POINTER_REGNUM)
+ cfa_store.offset += offset;
+
+ if (ACCUMULATE_OUTGOING_ARGS)
+ 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);
+}
/* 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. */
+ make a note of it if it does. EH uses this information to find out
+ how much extra space it needs to pop off the stack. */
static void
-dwarf2out_stack_adjust (rtx insn, bool after_p)
+dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
{
HOST_WIDE_INT offset;
const char *label;
return;
label = dwarf2out_cfi_label (false);
- dwarf2out_args_size_adjust (offset, label);
-}
-
-/* Adjust args_size based on stack adjustment OFFSET. */
-
-static void
-dwarf2out_args_size_adjust (HOST_WIDE_INT offset, const char *label)
-{
- if (cfa.reg == STACK_POINTER_REGNUM)
- cfa.offset += offset;
-
- if (cfa_store.reg == STACK_POINTER_REGNUM)
- cfa_store.offset += offset;
-
-#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);
+ dwarf2out_stack_adjust (offset, label);
}
#endif
addr = XEXP (set, 0);
gcc_assert (MEM_P (addr));
addr = XEXP (addr, 0);
-
+
/* As documented, only consider extremely simple addresses. */
switch (GET_CODE (addr))
{
&& cfa.indirect == 0
&& cfa.reg != HARD_FRAME_POINTER_REGNUM
effects: Use DW_CFA_def_cfa_expression to define cfa
- cfa.reg == fde->drap_reg
-
- Rule 20:
- (set reg fde->drap_reg)
- constraints: fde->vdrap_reg == INVALID_REGNUM
- effects: fde->vdrap_reg = reg.
- (set mem fde->drap_reg)
- constraints: fde->drap_reg_saved == 1
- effects: none. */
+ cfa.reg == fde->drap_reg */
static void
dwarf2out_frame_debug_expr (rtx expr, const char *label)
HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0);
if (offset != 0)
- dwarf2out_args_size_adjust (offset, label);
+ dwarf2out_stack_adjust (offset, label);
}
}
return;
fde = current_fde ();
- if (REG_P (src)
- && fde
- && fde->drap_reg == REGNO (src)
- && (fde->drap_reg_saved
- || REG_P (dest)))
- {
- /* Rule 20 */
- /* If we are saving dynamic realign argument pointer to a
- register, the destination is virtual dynamic realign
- argument pointer. It may be used to access argument. */
- if (REG_P (dest))
- {
- gcc_assert (fde->vdrap_reg == INVALID_REGNUM);
- fde->vdrap_reg = REGNO (dest);
- }
- return;
- }
-
switch (GET_CODE (dest))
{
case REG:
if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
flush_queued_reg_saves ();
- if (! RTX_FRAME_RELATED_P (insn))
+ 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_stack_adjust (insn, after_p);
+ dwarf2out_notice_stack_adjust (insn, after_p);
return;
}
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 (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;
}
void
dwarf2out_frame_debug_restore_state (void)
{
- dw_cfi_ref cfi = new_cfi ();
+ dw_cfi_ref cfi = new_cfi ();
const char *label = dwarf2out_cfi_label (false);
cfi->dw_cfi_opc = DW_CFA_restore_state;
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:
return dw_cfi_oprnd_offset;
case DW_CFA_def_cfa_expression:
- case DW_CFA_expression:
return dw_cfi_oprnd_loc;
default:
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;
}
#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
-/* Switch to eh_frame_section. If we don't have an eh_frame_section,
- switch to the data section instead, and write out a synthetic label
- for collect2. */
+/* 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 (void)
+switch_to_eh_frame_section (bool back)
{
tree label;
/* 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);
- 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 (!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 the call frame information used to record information
- that relates to calculating the frame pointer, and records the
- location of saved registers. */
+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_call_frame_info (int for_eh)
+output_cfis (dw_cfi_ref cfi, bool do_cfi_asm, dw_fde_ref fde, bool 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;
- int dw_cie_version;
+ 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;
- /* Don't emit a CIE if there won't be any FDEs. */
- if (fde_table_in_use == 0)
- return;
+ 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;
- /* Nothing to do if the assembler's doing it all. */
- if (dwarf2out_do_cfi_asm ())
- return;
+ /* 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 we make FDEs linkonce, we may have to emit an empty label for
- an FDE that wouldn't otherwise be emitted. We want to avoid
- having an FDE kept around when the function it refers to is
- discarded. Example where this matters: a primary function
- template in C++ requires EH information, but an explicit
- specialization doesn't. */
- if (TARGET_USES_WEAK_UNWIND_INFO
- && ! flag_asynchronous_unwind_tables
- && flag_exceptions
- && for_eh)
- for (i = 0; i < fde_table_in_use; i++)
- if ((fde_table[i].nothrow || fde_table[i].all_throwers_are_sibcalls)
- && !fde_table[i].uses_eh_lsda
- && ! DECL_WEAK (fde_table[i].decl))
- targetm.asm_out.unwind_label (asm_out_file, fde_table[i].decl,
- for_eh, /* empty */ 1);
+ if (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.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 we don't have any functions we'll want to unwind out of, don't
- emit any EH unwind information. Note that if exceptions aren't
- enabled, we won't have collected nothrow information, and if we
- asked for asynchronous tables, we always want this info. */
if (for_eh)
+ 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)
{
- bool any_eh_needed = !flag_exceptions || flag_asynchronous_unwind_tables;
+ 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;
+ }
- for (i = 0; i < fde_table_in_use; i++)
+ 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;
+}
+
+/* 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 make FDEs linkonce, we may have to emit an empty label for
+ an FDE that wouldn't otherwise be emitted. We want to avoid
+ having an FDE kept around when the function it refers to is
+ discarded. Example where this matters: a primary function
+ template in C++ requires EH information, but an explicit
+ specialization doesn't. */
+ if (TARGET_USES_WEAK_UNWIND_INFO
+ && ! flag_asynchronous_unwind_tables
+ && flag_exceptions
+ && for_eh)
+ for (i = 0; i < fde_table_in_use; i++)
+ if ((fde_table[i].nothrow || fde_table[i].all_throwers_are_sibcalls)
+ && !fde_table[i].uses_eh_lsda
+ && ! DECL_WEAK (fde_table[i].decl))
+ targetm.asm_out.unwind_label (asm_out_file, fde_table[i].decl,
+ for_eh, /* empty */ 1);
+
+ /* If we don't have any functions we'll want to unwind out of, don't
+ emit any EH unwind information. Note that if exceptions aren't
+ enabled, we won't have collected nothrow information, and if we
+ asked for asynchronous tables, we always want this info. */
+ if (for_eh)
+ {
+ bool any_eh_needed = !flag_exceptions || flag_asynchronous_unwind_tables;
+
+ for (i = 0; i < fde_table_in_use; i++)
if (fde_table[i].uses_eh_lsda)
any_eh_needed = any_lsda_needed = true;
else if (TARGET_USES_WEAK_UNWIND_INFO && DECL_WEAK (fde_table[i].decl))
if (flag_debug_asm)
app_enable ();
- if (for_eh)
- switch_to_eh_frame_section ();
- else
- {
- if (!debug_frame_section)
- debug_frame_section = get_section (DEBUG_FRAME_SECTION,
- SECTION_DEBUG, NULL);
- switch_to_section (debug_frame_section);
- }
+ /* 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);
augmentation[0] = 0;
augmentation_size = 0;
+
+ personality = current_unit_personality;
if (for_eh)
{
char *p;
lsda_encoding = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/0);
p = augmentation + 1;
- if (eh_personality_libfunc)
+ if (personality)
{
*p++ = 'P';
augmentation_size += 1 + size_of_encoded_value (per_encoding);
- assemble_external_libcall (eh_personality_libfunc);
+ assemble_external_libcall (personality);
}
if (any_lsda_needed)
{
}
/* Ug. Some platforms can't do unaligned dynamic relocations at all. */
- if (eh_personality_libfunc && per_encoding == DW_EH_PE_aligned)
+ if (personality && per_encoding == DW_EH_PE_aligned)
{
int offset = ( 4 /* Length */
+ 4 /* CIE Id */
if (augmentation[0])
{
dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size");
- if (eh_personality_libfunc)
+ 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,
- eh_personality_libfunc,
+ personality,
true, NULL);
}
/* 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. */
&& !fde->uses_eh_lsda)
continue;
- targetm.asm_out.unwind_label (asm_out_file, fde->decl, for_eh, /* empty */ 0);
- targetm.asm_out.internal_label (asm_out_file, FDE_LABEL, for_eh + i * 2);
- ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i * 2);
- ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i * 2);
- if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4 && !for_eh)
- dw2_asm_output_data (4, 0xffffffff,
- "Initial length escape value indicating 64-bit DWARF extension");
- dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1,
- "FDE Length");
- ASM_OUTPUT_LABEL (asm_out_file, l1);
-
- if (for_eh)
- dw2_asm_output_delta (4, l1, section_start_label, "FDE CIE offset");
- else
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, section_start_label,
- debug_frame_section, "FDE CIE offset");
-
- if (for_eh)
- {
- if (fde->dw_fde_switched_sections)
- {
- rtx sym_ref2 = gen_rtx_SYMBOL_REF (Pmode,
- fde->dw_fde_unlikely_section_label);
- rtx sym_ref3= gen_rtx_SYMBOL_REF (Pmode,
- fde->dw_fde_hot_section_label);
- SYMBOL_REF_FLAGS (sym_ref2) |= SYMBOL_FLAG_LOCAL;
- SYMBOL_REF_FLAGS (sym_ref3) |= SYMBOL_FLAG_LOCAL;
- dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref3, false,
- "FDE initial location");
- dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
- fde->dw_fde_hot_section_end_label,
- fde->dw_fde_hot_section_label,
- "FDE address range");
- dw2_asm_output_encoded_addr_rtx (fde_encoding, sym_ref2, false,
- "FDE initial location");
- dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
- fde->dw_fde_unlikely_section_end_label,
- fde->dw_fde_unlikely_section_label,
- "FDE address range");
- }
- else
- {
- rtx sym_ref = gen_rtx_SYMBOL_REF (Pmode, fde->dw_fde_begin);
- SYMBOL_REF_FLAGS (sym_ref) |= SYMBOL_FLAG_LOCAL;
- dw2_asm_output_encoded_addr_rtx (fde_encoding,
- sym_ref,
- false,
- "FDE initial location");
- dw2_asm_output_delta (size_of_encoded_value (fde_encoding),
- fde->dw_fde_end, fde->dw_fde_begin,
- "FDE address range");
- }
- }
- else
- {
- if (fde->dw_fde_switched_sections)
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE,
- fde->dw_fde_hot_section_label,
- "FDE initial location");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- fde->dw_fde_hot_section_end_label,
- fde->dw_fde_hot_section_label,
- "FDE address range");
- dw2_asm_output_addr (DWARF2_ADDR_SIZE,
- fde->dw_fde_unlikely_section_label,
- "FDE initial location");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- fde->dw_fde_unlikely_section_end_label,
- fde->dw_fde_unlikely_section_label,
- "FDE address range");
- }
- else
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, fde->dw_fde_begin,
- "FDE initial location");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- fde->dw_fde_end, fde->dw_fde_begin,
- "FDE address range");
- }
- }
-
- if (augmentation[0])
- {
- if (any_lsda_needed)
- {
- int size = size_of_encoded_value (lsda_encoding);
-
- if (lsda_encoding == DW_EH_PE_aligned)
- {
- int offset = ( 4 /* Length */
- + 4 /* CIE offset */
- + 2 * size_of_encoded_value (fde_encoding)
- + 1 /* Augmentation size */ );
- int pad = -offset & (PTR_SIZE - 1);
-
- size += pad;
- gcc_assert (size_of_uleb128 (size) == 1);
- }
-
- dw2_asm_output_data_uleb128 (size, "Augmentation size");
-
- if (fde->uses_eh_lsda)
- {
- ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA",
- fde->funcdef_number);
- dw2_asm_output_encoded_addr_rtx (
- lsda_encoding, gen_rtx_SYMBOL_REF (Pmode, l1),
- false, "Language Specific Data Area");
- }
- else
- {
- if (lsda_encoding == DW_EH_PE_aligned)
- ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE));
- dw2_asm_output_data
- (size_of_encoded_value (lsda_encoding), 0,
- "Language Specific Data Area (none)");
- }
- }
- else
- dw2_asm_output_data_uleb128 (0, "Augmentation size");
- }
-
- /* Loop through the Call Frame Instructions associated with
- this FDE. */
- fde->dw_fde_current_label = fde->dw_fde_begin;
- for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
- output_cfi (cfi, fde, for_eh);
-
- /* Pad the FDE out to an address sized boundary. */
- ASM_OUTPUT_ALIGN (asm_out_file,
- floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)));
- ASM_OUTPUT_LABEL (asm_out_file, l2);
+ 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)
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 0x%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 0x%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. */
char label[MAX_ARTIFICIAL_LABEL_BYTES];
char * dup_label;
dw_fde_ref fde;
+ section *fnsec;
current_function_func_begin_label = NULL;
return;
#endif
- switch_to_section (function_section (current_function_decl));
+ 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,
fde->dw_fde_hot_section_end_label = NULL;
fde->dw_fde_unlikely_section_label = NULL;
fde->dw_fde_unlikely_section_end_label = NULL;
- fde->dw_fde_switched_sections = false;
+ fde->dw_fde_switched_sections = 0;
+ fde->dw_fde_switched_cold_to_hot = 0;
fde->dw_fde_end = NULL;
fde->dw_fde_cfi = NULL;
+ fde->dw_fde_switch_cfi = NULL;
fde->funcdef_number = current_function_funcdef_no;
fde->nothrow = crtl->nothrow;
fde->uses_eh_lsda = crtl->uses_eh_lsda;
fde->all_throwers_are_sibcalls = crtl->all_throwers_are_sibcalls;
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;
#endif
if (dwarf2out_do_cfi_asm ())
+ dwarf2out_do_cfi_startproc (false);
+ else
{
- int enc;
- rtx ref;
-
- fprintf (asm_out_file, "\t.cfi_startproc\n");
-
- if (eh_personality_libfunc)
- {
- enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1);
- ref = eh_personality_libfunc;
-
- /* ??? 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 0x%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, "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);
+ rtx personality = get_personality_function (current_function_decl);
+ if (!current_unit_personality)
+ current_unit_personality = personality;
- fprintf (asm_out_file, "\t.cfi_lsda 0x%x,", enc);
- output_addr_const (asm_out_file, ref);
- fputc ('\n', asm_out_file);
- }
+ /* 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.");
}
}
{
dw_fde_ref fde = current_fde ();
- gcc_assert (cfun && 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_switched_sections = true;
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;
/* 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;
+ }
}
#endif
\f
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. */
dw_val_class_range_list,
dw_val_class_const,
dw_val_class_unsigned_const,
- dw_val_class_long_long,
+ dw_val_class_const_double,
dw_val_class_vec,
dw_val_class_flag,
dw_val_class_die_ref,
dw_val_class_lineptr,
dw_val_class_str,
dw_val_class_macptr,
- dw_val_class_file
+ dw_val_class_file,
+ dw_val_class_data8
};
-/* Describe a double word constant value. */
-/* ??? Every instance of long_long in the code really means CONST_DOUBLE. */
-
-typedef struct GTY(()) dw_long_long_struct {
- unsigned long hi;
- unsigned long low;
-}
-dw_long_long_const;
-
/* Describe a floating point constant value, or a vector constant value. */
typedef struct GTY(()) dw_vec_struct {
dw_loc_descr_ref GTY ((tag ("dw_val_class_loc"))) val_loc;
HOST_WIDE_INT GTY ((default)) val_int;
unsigned HOST_WIDE_INT GTY ((tag ("dw_val_class_unsigned_const"))) val_unsigned;
- dw_long_long_const GTY ((tag ("dw_val_class_long_long"))) val_long_long;
+ 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
{
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];
}
GTY ((desc ("%1.val_class"))) v;
}
return "DW_OP_call4";
case DW_OP_call_ref:
return "DW_OP_call_ref";
+ case DW_OP_implicit_value:
+ return "DW_OP_implicit_value";
+ case DW_OP_stack_value:
+ return "DW_OP_stack_value";
case DW_OP_form_tls_address:
return "DW_OP_form_tls_address";
case DW_OP_call_frame_cfa:
}
}
+#ifdef DWARF2_DEBUGGING_INFO
+/* Add a constant OFFSET to a location list. */
+
+static void
+loc_list_plus_const (dw_loc_list_ref list_head, HOST_WIDE_INT offset)
+{
+ dw_loc_list_ref d;
+ for (d = list_head; d != NULL; d = d->dw_loc_next)
+ loc_descr_plus_const (&d->expr, offset);
+}
+#endif
+
/* Return the size of a location descriptor. */
static unsigned long
case DW_OP_call_ref:
size += DWARF2_ADDR_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;
default:
break;
}
return size;
}
+#ifdef DWARF2_DEBUGGING_INFO
+static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
+#endif
+
/* Output location description stack opcode's operands (if any). */
static void
break;
case DW_OP_const8u:
case DW_OP_const8s:
- gcc_assert (HOST_BITS_PER_LONG >= 64);
+ gcc_assert (HOST_BITS_PER_WIDE_INT >= 64);
dw2_asm_output_data (8, val1->v.val_int, NULL);
break;
case DW_OP_skip:
dw2_asm_output_data (2, offset, NULL);
}
break;
-#else
- case DW_OP_const2u:
- case DW_OP_const2s:
- case DW_OP_const4u:
- case DW_OP_const4s:
- case DW_OP_const8u:
- case DW_OP_const8s:
- case DW_OP_skip:
- case DW_OP_bra:
- /* We currently don't make any attempt to make sure these are
- aligned properly like we do for the main unwind info, so
- don't support emitting things larger than a byte if we're
- only doing unwinding. */
- gcc_unreachable ();
-#endif
- case DW_OP_const1u:
- case DW_OP_const1s:
- dw2_asm_output_data (1, val1->v.val_int, NULL);
- break;
- case DW_OP_constu:
- dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
- break;
- case DW_OP_consts:
- dw2_asm_output_data_sleb128 (val1->v.val_int, NULL);
- break;
- case DW_OP_pick:
- dw2_asm_output_data (1, val1->v.val_int, NULL);
- break;
- case DW_OP_plus_uconst:
+ case DW_OP_implicit_value:
dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
- break;
+ switch (val2->val_class)
+ {
+ case dw_val_class_const:
+ dw2_asm_output_data (val1->v.val_unsigned, val2->v.val_int, NULL);
+ break;
+ case dw_val_class_vec:
+ {
+ unsigned int elt_size = val2->v.val_vec.elt_size;
+ unsigned int len = val2->v.val_vec.length;
+ unsigned int i;
+ unsigned char *p;
+
+ if (elt_size > sizeof (HOST_WIDE_INT))
+ {
+ elt_size /= 2;
+ len *= 2;
+ }
+ for (i = 0, p = val2->v.val_vec.array;
+ i < len;
+ i++, p += elt_size)
+ dw2_asm_output_data (elt_size, extract_int (p, elt_size),
+ "fp or vector constant word %u", i);
+ }
+ break;
+ case dw_val_class_const_double:
+ {
+ unsigned HOST_WIDE_INT first, second;
+
+ if (WORDS_BIG_ENDIAN)
+ {
+ first = val2->v.val_double.high;
+ second = val2->v.val_double.low;
+ }
+ else
+ {
+ first = val2->v.val_double.low;
+ second = val2->v.val_double.high;
+ }
+ dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+ first, NULL);
+ dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+ second, NULL);
+ }
+ break;
+ case dw_val_class_addr:
+ gcc_assert (val1->v.val_unsigned == DWARF2_ADDR_SIZE);
+ dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, val2->v.val_addr, NULL);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+#else
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_skip:
+ case DW_OP_bra:
+ case DW_OP_implicit_value:
+ /* We currently don't make any attempt to make sure these are
+ aligned properly like we do for the main unwind info, so
+ don't support emitting things larger than a byte if we're
+ only doing unwinding. */
+ gcc_unreachable ();
+#endif
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ dw2_asm_output_data (1, val1->v.val_int, NULL);
+ break;
+ case DW_OP_constu:
+ dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+ break;
+ case DW_OP_consts:
+ dw2_asm_output_data_sleb128 (val1->v.val_int, NULL);
+ break;
+ case DW_OP_pick:
+ dw2_asm_output_data (1, val1->v.val_int, NULL);
+ break;
+ case DW_OP_plus_uconst:
+ dw2_asm_output_data_uleb128 (val1->v.val_unsigned, NULL);
+ break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
switch (loc->dw_loc_opc)
{
case DW_OP_addr:
+ case DW_OP_implicit_value:
/* We cannot output addresses in .cfi_escape, only bytes. */
gcc_unreachable ();
case DW_OP_const8u:
case DW_OP_const8s:
- gcc_assert (HOST_BITS_PER_LONG >= 64);
+ gcc_assert (HOST_BITS_PER_WIDE_INT >= 64);
fputc (',', asm_out_file);
dw2_asm_output_data_raw (8, val1->v.val_int);
break;
unsigned long size;
if (cfi->dw_cfi_opc == DW_CFA_expression)
- dw2_asm_output_data (1, cfi->dw_cfi_oprnd2.dw_cfi_reg_num, NULL);
+ {
+ 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. */
- loc = cfi->dw_cfi_oprnd1.dw_cfi_loc;
size = size_of_locs (loc);
dw2_asm_output_data_uleb128 (size, NULL);
unsigned long size;
if (cfi->dw_cfi_opc == DW_CFA_expression)
- fprintf (asm_out_file, "0x%x,", cfi->dw_cfi_oprnd2.dw_cfi_reg_num);
+ {
+ fprintf (asm_out_file, "0x%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. */
- loc = cfi->dw_cfi_oprnd1.dw_cfi_loc;
size = size_of_locs (loc);
dw2_asm_output_data_uleb128_raw (size);
fputc (',', asm_out_file);
static void dwarf2out_init (const char *);
static void dwarf2out_finish (const char *);
+static void dwarf2out_assembly_start (void);
static void dwarf2out_define (unsigned int, const char *);
static void dwarf2out_undef (unsigned int, const char *);
static void dwarf2out_start_source_file (unsigned, const char *);
static void dwarf2out_end_source_file (unsigned);
+static void dwarf2out_function_decl (tree);
static void dwarf2out_begin_block (unsigned, unsigned);
static void dwarf2out_end_block (unsigned, unsigned);
static bool dwarf2out_ignore_block (const_tree);
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);
{
dwarf2out_init,
dwarf2out_finish,
+ dwarf2out_assembly_start,
dwarf2out_define,
dwarf2out_undef,
dwarf2out_start_source_file,
dwarf2out_end_epilogue,
dwarf2out_begin_function,
debug_nothing_int, /* end_function */
- dwarf2out_decl, /* function_decl */
+ dwarf2out_function_decl, /* function_decl */
dwarf2out_global_decl,
dwarf2out_type_decl, /* type_decl */
dwarf2out_imported_module_or_decl,
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 */
};
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
typedef struct GTY((chain_circular ("%h.die_sib"))) die_struct {
enum dwarf_tag die_tag;
- char *die_symbol;
+ 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;
VEC(dw_attr_node,gc) * die_attr;
dw_die_ref die_parent;
dw_die_ref die_child;
const char *end;
};
+/* The comdat type node structure. */
+typedef struct GTY(()) comdat_type_struct
+{
+ dw_die_ref root_die;
+ dw_die_ref type_die;
+ char signature[DWARF_TYPE_SIGNATURE_SIZE];
+ struct comdat_type_struct *next;
+}
+comdat_type_node;
+
/* The limbo die list structure. */
typedef struct GTY(()) limbo_die_struct {
dw_die_ref die;
}
limbo_die_node;
+typedef struct GTY(()) skeleton_chain_struct
+{
+ dw_die_ref old_die;
+ dw_die_ref new_die;
+ struct skeleton_chain_struct *parent;
+}
+skeleton_chain_node;
+
/* How to start an assembler comment. */
#ifndef ASM_COMMENT_START
#define ASM_COMMENT_START ";#"
#define DWARF_COMPILE_UNIT_HEADER_SIZE \
(DWARF_INITIAL_LENGTH_SIZE + DWARF_OFFSET_SIZE + 3)
+/* Fixed size portion of the DWARF comdat type unit header. */
+#define DWARF_COMDAT_TYPE_UNIT_HEADER_SIZE \
+ (DWARF_COMPILE_UNIT_HEADER_SIZE + DWARF_TYPE_SIGNATURE_SIZE \
+ + DWARF_OFFSET_SIZE)
+
/* Fixed size portion of public names info. */
#define DWARF_PUBNAMES_HEADER_SIZE (2 * DWARF_OFFSET_SIZE + 2)
/* Record the root of the DIE's built for the current compilation unit. */
static GTY(()) dw_die_ref comp_unit_die;
+/* A list of type DIEs that have been separated into comdat sections. */
+static GTY(()) comdat_type_node *comdat_type_list;
+
/* A list of DIEs with a NULL parent waiting to be relocated. */
static GTY(()) limbo_die_node *limbo_die_list;
The key is DECL_UID() ^ die_parent. */
static GTY ((param_is (struct die_struct))) htab_t common_block_die_table;
+typedef struct GTY(()) die_arg_entry_struct {
+ dw_die_ref die;
+ tree arg;
+} die_arg_entry;
+
+DEF_VEC_O(die_arg_entry);
+DEF_VEC_ALLOC_O(die_arg_entry,gc);
+
/* Node of the variable location list. */
struct GTY ((chain_next ("%h.next"))) var_loc_node {
rtx GTY (()) var_loc_note;
const char * GTY (()) label;
- const char * GTY (()) section_label;
struct var_loc_node * GTY (()) next;
};
/* Unique label counter. */
static GTY(()) unsigned int loclabel_num;
+/* Unique label counter for point-of-call tables. */
+static GTY(()) unsigned int poc_label_num;
+
+/* The 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;
+
#ifdef DWARF2_DEBUGGING_INFO
/* Record whether the function being analyzed contains inlined functions. */
static int current_function_has_inlines;
/* Cached result of previous call to lookup_filename. */
static GTY(()) struct dwarf_file_data * file_table_last_lookup;
+static GTY(()) VEC(die_arg_entry,gc) *tmpl_value_parm_die_table;
+
#ifdef DWARF2_DEBUGGING_INFO
/* Offset from the "steady-state frame pointer" to the frame base,
static inline HOST_WIDE_INT AT_int (dw_attr_ref);
static void add_AT_unsigned (dw_die_ref, enum dwarf_attribute, unsigned HOST_WIDE_INT);
static inline unsigned HOST_WIDE_INT AT_unsigned (dw_attr_ref);
-static void add_AT_long_long (dw_die_ref, enum dwarf_attribute, unsigned long,
- unsigned long);
+static void add_AT_double (dw_die_ref, enum dwarf_attribute,
+ HOST_WIDE_INT, unsigned HOST_WIDE_INT);
static inline void add_AT_vec (dw_die_ref, enum dwarf_attribute, unsigned int,
unsigned int, unsigned char *);
+static void add_AT_data8 (dw_die_ref, enum dwarf_attribute, unsigned char *);
static hashval_t debug_str_do_hash (const void *);
static int debug_str_eq (const void *, const void *);
static void add_AT_string (dw_die_ref, enum dwarf_attribute, const char *);
static int decl_loc_table_eq (const void *, const void *);
static var_loc_list *lookup_decl_loc (const_tree);
static void equate_decl_number_to_die (tree, dw_die_ref);
-static void add_var_loc_to_decl (tree, struct var_loc_node *);
+static struct var_loc_node *add_var_loc_to_decl (tree, rtx);
static void print_spaces (FILE *);
static void print_die (dw_die_ref, FILE *);
static void print_dwarf_line_table (FILE *);
static void loc_checksum (dw_loc_descr_ref, struct md5_ctx *);
static void attr_checksum (dw_attr_ref, struct md5_ctx *, int *);
static void die_checksum (dw_die_ref, struct md5_ctx *, int *);
+static void checksum_sleb128 (HOST_WIDE_INT, struct md5_ctx *);
+static void checksum_uleb128 (unsigned HOST_WIDE_INT, struct md5_ctx *);
+static void loc_checksum_ordered (dw_loc_descr_ref, struct md5_ctx *);
+static void attr_checksum_ordered (enum dwarf_tag, dw_attr_ref,
+ struct md5_ctx *, int *);
+struct checksum_attributes;
+static void collect_checksum_attributes (struct checksum_attributes *, dw_die_ref);
+static void die_checksum_ordered (dw_die_ref, struct md5_ctx *, int *);
+static void checksum_die_context (dw_die_ref, struct md5_ctx *);
+static void generate_type_signature (dw_die_ref, comdat_type_node *);
static int same_loc_p (dw_loc_descr_ref, dw_loc_descr_ref, int *);
static int same_dw_val_p (const dw_val_node *, const dw_val_node *, int *);
static int same_attr_p (dw_attr_ref, dw_attr_ref, int *);
static int is_symbol_die (dw_die_ref);
static void assign_symbol_names (dw_die_ref);
static void break_out_includes (dw_die_ref);
+static int is_declaration_die (dw_die_ref);
+static int should_move_die_to_comdat (dw_die_ref);
+static dw_die_ref clone_as_declaration (dw_die_ref);
+static dw_die_ref clone_die (dw_die_ref);
+static dw_die_ref clone_tree (dw_die_ref);
+static void copy_declaration_context (dw_die_ref, dw_die_ref);
+static void generate_skeleton_ancestor_tree (skeleton_chain_node *);
+static void generate_skeleton_bottom_up (skeleton_chain_node *);
+static dw_die_ref generate_skeleton (dw_die_ref);
+static dw_die_ref remove_child_or_replace_with_skeleton (dw_die_ref,
+ dw_die_ref);
+static void break_out_comdat_types (dw_die_ref);
+static dw_die_ref copy_ancestor_tree (dw_die_ref, dw_die_ref, htab_t);
+static void copy_decls_walk (dw_die_ref, dw_die_ref, htab_t);
+static void copy_decls_for_unworthy_types (dw_die_ref);
+
static hashval_t htab_cu_hash (const void *);
static int htab_cu_eq (const void *, const void *);
static void htab_cu_del (void *);
static void output_die (dw_die_ref);
static void output_compilation_unit_header (void);
static void output_comp_unit (dw_die_ref, int);
+static void output_comdat_type_unit (comdat_type_node *);
static const char *dwarf2_name (tree, int);
static void add_pubname (tree, dw_die_ref);
static void add_pubname_string (const char *, dw_die_ref);
static void output_aranges (void);
static unsigned int add_ranges_num (int);
static unsigned int add_ranges (const_tree);
-static unsigned int add_ranges_by_labels (const char *, const char *);
+static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
+ bool *);
static void output_ranges (void);
static void output_line_info (void);
static void output_file_names (void);
static int is_base_type (tree);
static dw_die_ref subrange_type_die (tree, tree, tree, dw_die_ref);
static dw_die_ref modified_type_die (tree, int, int, dw_die_ref);
+static dw_die_ref generic_parameter_die (tree, tree, bool, dw_die_ref);
+static dw_die_ref template_parameter_pack_die (tree, tree, dw_die_ref);
static int type_is_enum (const_tree);
static unsigned int dbx_reg_number (const_rtx);
static void add_loc_descr_op_piece (dw_loc_descr_ref *, int);
static dw_loc_descr_ref based_loc_descr (rtx, HOST_WIDE_INT,
enum var_init_status);
static int is_based_loc (const_rtx);
+static int resolve_one_addr (rtx *, void *);
static dw_loc_descr_ref mem_loc_descriptor (rtx, enum machine_mode mode,
enum var_init_status);
static dw_loc_descr_ref concat_loc_descriptor (rtx, rtx,
enum var_init_status);
-static dw_loc_descr_ref loc_descriptor (rtx, enum var_init_status);
-static dw_loc_descr_ref loc_descriptor_from_tree_1 (tree, int);
-static dw_loc_descr_ref loc_descriptor_from_tree (tree);
+static dw_loc_descr_ref loc_descriptor (rtx, enum machine_mode mode,
+ enum var_init_status);
+static dw_loc_list_ref loc_list_from_tree (tree, int);
+static dw_loc_descr_ref loc_descriptor_from_tree (tree, int);
static HOST_WIDE_INT ceiling (HOST_WIDE_INT, unsigned int);
static tree field_type (const_tree);
static unsigned int simple_type_align_in_bits (const_tree);
static unsigned HOST_WIDE_INT simple_type_size_in_bits (const_tree);
static HOST_WIDE_INT field_byte_offset (const_tree);
static void add_AT_location_description (dw_die_ref, enum dwarf_attribute,
- dw_loc_descr_ref);
+ dw_loc_list_ref);
static void add_data_member_location_attribute (dw_die_ref, tree);
-static void add_const_value_attribute (dw_die_ref, rtx);
+static bool add_const_value_attribute (dw_die_ref, rtx);
static void insert_int (HOST_WIDE_INT, unsigned, unsigned char *);
-static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
static void insert_float (const_rtx, unsigned char *);
static rtx rtl_for_decl_location (tree);
-static void add_location_or_const_value_attribute (dw_die_ref, tree,
+static bool add_location_or_const_value_attribute (dw_die_ref, tree,
enum dwarf_attribute);
-static void tree_add_const_value_attribute (dw_die_ref, tree);
+static 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_comp_dir_attribute (dw_die_ref);
static void add_bound_info (dw_die_ref, enum dwarf_attribute, tree);
static void gen_entry_point_die (tree, dw_die_ref);
#endif
static dw_die_ref gen_enumeration_type_die (tree, dw_die_ref);
-static dw_die_ref gen_formal_parameter_die (tree, tree, dw_die_ref);
+static dw_die_ref gen_formal_parameter_die (tree, tree, bool, dw_die_ref);
+static dw_die_ref gen_formal_parameter_pack_die (tree, tree, dw_die_ref, tree*);
static void gen_unspecified_parameters_die (tree, dw_die_ref);
static void gen_formal_types_die (tree, dw_die_ref);
static void gen_subprogram_die (tree, dw_die_ref);
static void gen_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 dw_die_ref get_context_die (tree);
static void gen_namespace_die (tree, dw_die_ref);
static void gen_decl_die (tree, tree, dw_die_ref);
static dw_die_ref force_decl_die (tree);
static struct dwarf_file_data * lookup_filename (const char *);
static void retry_incomplete_types (void);
static void gen_type_die_for_member (tree, tree, dw_die_ref);
+static void gen_generic_params_dies (tree);
static void splice_child_die (dw_die_ref, dw_die_ref);
static int file_info_cmp (const void *, const void *);
static dw_loc_list_ref new_loc_list (dw_loc_descr_ref, const char *,
- const char *, const char *, unsigned);
-static void add_loc_descr_to_loc_list (dw_loc_list_ref *, dw_loc_descr_ref,
- const char *, const char *,
- const char *);
+ const char *, const char *);
static void output_loc_list (dw_loc_list_ref);
static char *gen_internal_sym (const char *);
static void prune_unused_types_prune (dw_die_ref);
static void prune_unused_types (void);
static int maybe_emit_file (struct dwarf_file_data *fd);
+static void append_entry_to_tmpl_value_parm_die_table (dw_die_ref, tree);
+static void gen_remaining_tmpl_value_param_die_attribute (void);
/* Section names used to hold DWARF debugging information. */
#ifndef DEBUG_INFO_SECTION
#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
return "DW_TAG_condition";
case DW_TAG_shared_type:
return "DW_TAG_shared_type";
+ case DW_TAG_type_unit:
+ return "DW_TAG_type_unit";
+ case DW_TAG_rvalue_reference_type:
+ return "DW_TAG_rvalue_reference_type";
+ case DW_TAG_template_alias:
+ return "DW_TAG_template_alias";
+ case DW_TAG_GNU_template_parameter_pack:
+ return "DW_TAG_GNU_template_parameter_pack";
+ case DW_TAG_GNU_formal_parameter_pack:
+ return "DW_TAG_GNU_formal_parameter_pack";
case DW_TAG_MIPS_loop:
return "DW_TAG_MIPS_loop";
case DW_TAG_format_label:
return "DW_TAG_GNU_BINCL";
case DW_TAG_GNU_EINCL:
return "DW_TAG_GNU_EINCL";
+ case DW_TAG_GNU_template_template_param:
+ return "DW_TAG_GNU_template_template_param";
default:
return "DW_TAG_<unknown>";
}
case DW_AT_call_line:
return "DW_AT_call_line";
+ case DW_AT_signature:
+ return "DW_AT_signature";
+ case DW_AT_main_subprogram:
+ return "DW_AT_main_subprogram";
+ case DW_AT_data_bit_offset:
+ return "DW_AT_data_bit_offset";
+ case DW_AT_const_expr:
+ return "DW_AT_const_expr";
+ case DW_AT_enum_class:
+ return "DW_AT_enum_class";
+ case DW_AT_linkage_name:
+ return "DW_AT_linkage_name";
+
case DW_AT_MIPS_fde:
return "DW_AT_MIPS_fde";
case DW_AT_MIPS_loop_begin:
return "DW_AT_body_end";
case DW_AT_GNU_vector:
return "DW_AT_GNU_vector";
+ case DW_AT_GNU_guarded_by:
+ return "DW_AT_GNU_guarded_by";
+ case DW_AT_GNU_pt_guarded_by:
+ return "DW_AT_GNU_pt_guarded_by";
+ case DW_AT_GNU_guarded:
+ return "DW_AT_GNU_guarded";
+ case DW_AT_GNU_pt_guarded:
+ return "DW_AT_GNU_pt_guarded";
+ case DW_AT_GNU_locks_excluded:
+ return "DW_AT_GNU_locks_excluded";
+ case DW_AT_GNU_exclusive_locks_required:
+ return "DW_AT_GNU_exclusive_locks_required";
+ case DW_AT_GNU_shared_locks_required:
+ return "DW_AT_GNU_shared_locks_required";
+ case DW_AT_GNU_odr_signature:
+ return "DW_AT_GNU_odr_signature";
+ case DW_AT_GNU_template_name:
+ return "DW_AT_GNU_template_name";
case DW_AT_VMS_rtnbeg_pd_address:
return "DW_AT_VMS_rtnbeg_pd_address";
return "DW_FORM_ref_udata";
case DW_FORM_indirect:
return "DW_FORM_indirect";
+ case DW_FORM_sec_offset:
+ return "DW_FORM_sec_offset";
+ case DW_FORM_exprloc:
+ return "DW_FORM_exprloc";
+ case DW_FORM_flag_present:
+ return "DW_FORM_flag_present";
+ case DW_FORM_ref_sig8:
+ return "DW_FORM_ref_sig8";
default:
return "DW_FORM_<unknown>";
}
/* Add an unsigned double integer attribute value to a DIE. */
static inline void
-add_AT_long_long (dw_die_ref die, enum dwarf_attribute attr_kind,
- long unsigned int val_hi, long unsigned int val_low)
+add_AT_double (dw_die_ref die, enum dwarf_attribute attr_kind,
+ HOST_WIDE_INT high, unsigned HOST_WIDE_INT low)
{
dw_attr_node attr;
attr.dw_attr = attr_kind;
- attr.dw_attr_val.val_class = dw_val_class_long_long;
- attr.dw_attr_val.v.val_long_long.hi = val_hi;
- attr.dw_attr_val.v.val_long_long.low = val_low;
+ attr.dw_attr_val.val_class = dw_val_class_const_double;
+ attr.dw_attr_val.v.val_double.high = high;
+ attr.dw_attr_val.v.val_double.low = low;
add_dwarf_attr (die, &attr);
}
add_dwarf_attr (die, &attr);
}
+/* Add an 8-byte data attribute value to a DIE. */
+
+static inline void
+add_AT_data8 (dw_die_ref die, enum dwarf_attribute attr_kind,
+ unsigned char data8[8])
+{
+ dw_attr_node attr;
+
+ attr.dw_attr = attr_kind;
+ attr.dw_attr_val.val_class = dw_val_class_data8;
+ memcpy (attr.dw_attr_val.v.val_data8, data8, 8);
+ add_dwarf_attr (die, &attr);
+}
+
/* Hash and equality functions for debug_str_hash. */
static hashval_t
(const char *)x2) == 0;
}
+/* Add STR to the indirect string hash table. */
+
static struct indirect_string_node *
find_AT_string (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)
{
{
struct indirect_string_node *node;
unsigned int len;
- char label[32];
gcc_assert (a && AT_class (a) == dw_val_class_str);
/* If we cannot expect the linker to merge strings in .debug_str
section, only put it into .debug_str if it is worth even in this
single module. */
- if ((debug_str_section->common.flags & SECTION_MERGE) == 0
- && (len - DWARF_OFFSET_SIZE) * node->refcount <= len)
+ if (DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET
+ || ((debug_str_section->common.flags & SECTION_MERGE) == 0
+ && (len - DWARF_OFFSET_SIZE) * node->refcount <= len))
return node->form = DW_FORM_string;
- ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
- ++dw2_string_counter;
- node->label = xstrdup (label);
+ gen_label_for_indirect_string (node);
return node->form = DW_FORM_strp;
}
return a->dw_attr_val.v.val_loc_list;
}
+static inline dw_loc_list_ref *
+AT_loc_list_ptr (dw_attr_ref a)
+{
+ gcc_assert (a && AT_class (a) == dw_val_class_loc_list);
+ return &a->dw_attr_val.v.val_loc_list;
+}
+
/* Add an address constant attribute value to a DIE. */
static inline void
child->die_parent->die_child = prev;
}
+/* Replace OLD_CHILD with NEW_CHILD. PREV must have the property that
+ PREV->DIE_SIB == OLD_CHILD. Does not alter OLD_CHILD. */
+
+static void
+replace_child (dw_die_ref old_child, dw_die_ref new_child, dw_die_ref prev)
+{
+ dw_die_ref parent = old_child->die_parent;
+
+ gcc_assert (parent == prev->die_parent);
+ gcc_assert (prev->die_sib == old_child);
+
+ new_child->die_parent = parent;
+ if (prev == old_child)
+ {
+ gcc_assert (parent->die_child == old_child);
+ new_child->die_sib = new_child;
+ }
+ else
+ {
+ prev->die_sib = new_child;
+ new_child->die_sib = old_child->die_sib;
+ }
+ if (old_child->die_parent->die_child == old_child)
+ old_child->die_parent->die_child = new_child;
+}
+
+/* Move all children from OLD_PARENT to NEW_PARENT. */
+
+static void
+move_all_children (dw_die_ref old_parent, dw_die_ref new_parent)
+{
+ dw_die_ref c;
+ new_parent->die_child = old_parent->die_child;
+ old_parent->die_child = NULL;
+ FOR_EACH_CHILD (new_parent, c, c->die_parent = new_parent);
+}
+
/* Remove child DIE whose die_tag is TAG. Do nothing if no child
matches TAG. */
static inline var_loc_list *
lookup_decl_loc (const_tree decl)
{
+ if (!decl_loc_table)
+ return NULL;
return (var_loc_list *)
htab_find_with_hash (decl_loc_table, decl, DECL_UID (decl));
}
/* Add a variable location node to the linked list for DECL. */
-static void
-add_var_loc_to_decl (tree decl, struct var_loc_node *loc)
+static struct var_loc_node *
+add_var_loc_to_decl (tree decl, rtx loc_note)
{
unsigned int decl_id = DECL_UID (decl);
var_loc_list *temp;
void **slot;
+ struct var_loc_node *loc = NULL;
slot = htab_find_slot_with_hash (decl_loc_table, decl, decl_id, INSERT);
if (*slot == NULL)
and either both or neither of the locations is uninitialized,
we have nothing to do. */
if ((!rtx_equal_p (NOTE_VAR_LOCATION_LOC (temp->last->var_loc_note),
- NOTE_VAR_LOCATION_LOC (loc->var_loc_note)))
+ NOTE_VAR_LOCATION_LOC (loc_note)))
|| ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
- != NOTE_VAR_LOCATION_STATUS (loc->var_loc_note))
+ != NOTE_VAR_LOCATION_STATUS (loc_note))
&& ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
== VAR_INIT_STATUS_UNINITIALIZED)
- || (NOTE_VAR_LOCATION_STATUS (loc->var_loc_note)
+ || (NOTE_VAR_LOCATION_STATUS (loc_note)
== VAR_INIT_STATUS_UNINITIALIZED))))
{
/* Add LOC to the end of list and update LAST. */
+ loc = GGC_CNEW (struct var_loc_node);
temp->last->next = loc;
temp->last = loc;
}
}
- /* Do not add empty location to the beginning of the list. */
- else if (NOTE_VAR_LOCATION_LOC (loc->var_loc_note) != NULL_RTX)
+ else
{
+ loc = GGC_CNEW (struct var_loc_node);
temp->first = loc;
temp->last = loc;
}
+ return loc;
}
\f
/* Keep track of the number of spaces used to indent the
fprintf (outfile, "%*s", print_indent, "");
}
+/* Print a type signature in hex. */
+
+static inline void
+print_signature (FILE *outfile, char *sig)
+{
+ int i;
+
+ for (i = 0; i < DWARF_TYPE_SIGNATURE_SIZE; i++)
+ fprintf (outfile, "%02x", sig[i] & 0xff);
+}
+
/* Print the information associated with a given DIE, and its children.
This routine is a debugging aid only. */
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)
+ {
+ print_spaces (outfile);
+ fprintf (outfile, " signature: ");
+ print_signature (outfile, die->die_id.die_type_node->signature);
+ fprintf (outfile, "\n");
+ }
for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
{
case dw_val_class_unsigned_const:
fprintf (outfile, HOST_WIDE_INT_PRINT_UNSIGNED, AT_unsigned (a));
break;
- case dw_val_class_long_long:
- fprintf (outfile, "constant (%lu,%lu)",
- a->dw_attr_val.v.val_long_long.hi,
- a->dw_attr_val.v.val_long_long.low);
+ case dw_val_class_const_double:
+ fprintf (outfile, "constant ("HOST_WIDE_INT_PRINT_DEC","\
+ HOST_WIDE_INT_PRINT_UNSIGNED")",
+ a->dw_attr_val.v.val_double.high,
+ a->dw_attr_val.v.val_double.low);
break;
case dw_val_class_vec:
fprintf (outfile, "floating-point or vector constant");
case dw_val_class_die_ref:
if (AT_ref (a) != NULL)
{
- if (AT_ref (a)->die_symbol)
- fprintf (outfile, "die -> label: %s", AT_ref (a)->die_symbol);
+ if (dwarf_version >= 4 && 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)
+ fprintf (outfile, "die -> label: %s",
+ AT_ref (a)->die_id.die_symbol);
else
fprintf (outfile, "die -> %ld", AT_ref (a)->die_offset);
}
fprintf (outfile, "\"%s\" (%d)", AT_file (a)->filename,
AT_file (a)->emitted_number);
break;
+ case dw_val_class_data8:
+ {
+ int i;
+
+ for (i = 0; i < 8; i++)
+ fprintf (outfile, "%02x", a->dw_attr_val.v.val_data8[i]);
+ break;
+ }
default:
break;
}
case dw_val_class_unsigned_const:
CHECKSUM (at->dw_attr_val.v.val_unsigned);
break;
- case dw_val_class_long_long:
- CHECKSUM (at->dw_attr_val.v.val_long_long);
+ case dw_val_class_const_double:
+ CHECKSUM (at->dw_attr_val.v.val_double);
break;
case dw_val_class_vec:
CHECKSUM (at->dw_attr_val.v.val_vec);
CHECKSUM_STRING (AT_file (at)->filename);
break;
+ case dw_val_class_data8:
+ CHECKSUM (at->dw_attr_val.v.val_data8);
+ break;
+
default:
break;
}
#undef CHECKSUM
#undef CHECKSUM_STRING
-/* Do the location expressions look same? */
-static inline int
-same_loc_p (dw_loc_descr_ref loc1, dw_loc_descr_ref loc2, int *mark)
+/* For DWARF-4 types, include the trailing NULL when checksumming strings. */
+#define CHECKSUM(FOO) md5_process_bytes (&(FOO), sizeof (FOO), ctx)
+#define CHECKSUM_STRING(FOO) md5_process_bytes ((FOO), strlen (FOO) + 1, ctx)
+#define CHECKSUM_SLEB128(FOO) checksum_sleb128 ((FOO), ctx)
+#define CHECKSUM_ULEB128(FOO) checksum_uleb128 ((FOO), ctx)
+#define CHECKSUM_ATTR(FOO) \
+ if (FOO) attr_checksum_ordered (die->die_tag, (FOO), ctx, mark)
+
+/* Calculate the checksum of a number in signed LEB128 format. */
+
+static void
+checksum_sleb128 (HOST_WIDE_INT value, struct md5_ctx *ctx)
{
- return loc1->dw_loc_opc == loc2->dw_loc_opc
- && same_dw_val_p (&loc1->dw_loc_oprnd1, &loc2->dw_loc_oprnd1, mark)
- && same_dw_val_p (&loc1->dw_loc_oprnd2, &loc2->dw_loc_oprnd2, mark);
+ unsigned char byte;
+ bool more;
+
+ while (1)
+ {
+ byte = (value & 0x7f);
+ value >>= 7;
+ more = !((value == 0 && (byte & 0x40) == 0)
+ || (value == -1 && (byte & 0x40) != 0));
+ if (more)
+ byte |= 0x80;
+ CHECKSUM (byte);
+ if (!more)
+ break;
+ }
}
-/* Do the values look the same? */
-static int
-same_dw_val_p (const dw_val_node *v1, const dw_val_node *v2, int *mark)
+/* Calculate the checksum of a number in unsigned LEB128 format. */
+
+static void
+checksum_uleb128 (unsigned HOST_WIDE_INT value, struct md5_ctx *ctx)
{
- dw_loc_descr_ref loc1, loc2;
- rtx r1, r2;
+ while (1)
+ {
+ unsigned char byte = (value & 0x7f);
+ value >>= 7;
+ if (value != 0)
+ /* More bytes to follow. */
+ byte |= 0x80;
+ CHECKSUM (byte);
+ if (value == 0)
+ break;
+ }
+}
- if (v1->val_class != v2->val_class)
- return 0;
+/* Checksum the context of the DIE. This adds the names of any
+ surrounding namespaces or structures to the checksum. */
- switch (v1->val_class)
- {
- case dw_val_class_const:
- return v1->v.val_int == v2->v.val_int;
- case dw_val_class_unsigned_const:
- return v1->v.val_unsigned == v2->v.val_unsigned;
- case dw_val_class_long_long:
- return v1->v.val_long_long.hi == v2->v.val_long_long.hi
- && v1->v.val_long_long.low == v2->v.val_long_long.low;
- case dw_val_class_vec:
- if (v1->v.val_vec.length != v2->v.val_vec.length
- || v1->v.val_vec.elt_size != v2->v.val_vec.elt_size)
- return 0;
- if (memcmp (v1->v.val_vec.array, v2->v.val_vec.array,
- v1->v.val_vec.length * v1->v.val_vec.elt_size))
- return 0;
- return 1;
- case dw_val_class_flag:
- return v1->v.val_flag == v2->v.val_flag;
- case dw_val_class_str:
- return !strcmp(v1->v.val_str->str, v2->v.val_str->str);
+static void
+checksum_die_context (dw_die_ref die, struct md5_ctx *ctx)
+{
+ const char *name;
+ dw_die_ref spec;
+ int tag = die->die_tag;
- case dw_val_class_addr:
- r1 = v1->v.val_addr;
- r2 = v2->v.val_addr;
- if (GET_CODE (r1) != GET_CODE (r2))
- return 0;
- gcc_assert (GET_CODE (r1) == SYMBOL_REF);
- return !strcmp (XSTR (r1, 0), XSTR (r2, 0));
+ if (tag != DW_TAG_namespace
+ && tag != DW_TAG_structure_type
+ && tag != DW_TAG_class_type)
+ return;
- case dw_val_class_offset:
- return v1->v.val_offset == v2->v.val_offset;
+ name = get_AT_string (die, DW_AT_name);
- case dw_val_class_loc:
- for (loc1 = v1->v.val_loc, loc2 = v2->v.val_loc;
- loc1 && loc2;
- loc1 = loc1->dw_loc_next, loc2 = loc2->dw_loc_next)
- if (!same_loc_p (loc1, loc2, mark))
- return 0;
- return !loc1 && !loc2;
+ spec = get_AT_ref (die, DW_AT_specification);
+ if (spec != NULL)
+ die = spec;
- case dw_val_class_die_ref:
- return same_die_p (v1->v.val_die_ref.die, v2->v.val_die_ref.die, mark);
+ if (die->die_parent != NULL)
+ checksum_die_context (die->die_parent, ctx);
- case dw_val_class_fde_ref:
- case dw_val_class_lbl_id:
- case dw_val_class_lineptr:
- case dw_val_class_macptr:
- return 1;
+ CHECKSUM_ULEB128 ('C');
+ CHECKSUM_ULEB128 (tag);
+ if (name != NULL)
+ CHECKSUM_STRING (name);
+}
- case dw_val_class_file:
- return v1->v.val_file == v2->v.val_file;
+/* Calculate the checksum of a location expression. */
- default:
- return 1;
+static inline void
+loc_checksum_ordered (dw_loc_descr_ref loc, struct md5_ctx *ctx)
+{
+ /* Special case for lone DW_OP_plus_uconst: checksum as if the location
+ were emitted as a DW_FORM_sdata instead of a location expression. */
+ if (loc->dw_loc_opc == DW_OP_plus_uconst && loc->dw_loc_next == NULL)
+ {
+ CHECKSUM_ULEB128 (DW_FORM_sdata);
+ CHECKSUM_SLEB128 ((HOST_WIDE_INT) loc->dw_loc_oprnd1.v.val_unsigned);
+ return;
+ }
+
+ /* Otherwise, just checksum the raw location expression. */
+ while (loc != NULL)
+ {
+ CHECKSUM_ULEB128 (loc->dw_loc_opc);
+ CHECKSUM (loc->dw_loc_oprnd1);
+ CHECKSUM (loc->dw_loc_oprnd2);
+ loc = loc->dw_loc_next;
}
}
-/* Do the attributes look the same? */
+/* Calculate the checksum of an attribute. */
-static int
-same_attr_p (dw_attr_ref at1, dw_attr_ref at2, int *mark)
+static void
+attr_checksum_ordered (enum dwarf_tag tag, dw_attr_ref at,
+ struct md5_ctx *ctx, int *mark)
{
- if (at1->dw_attr != at2->dw_attr)
- return 0;
+ dw_loc_descr_ref loc;
+ rtx r;
- /* We don't care that this was compiled with a different compiler
- snapshot; if the output is the same, that's what matters. */
- if (at1->dw_attr == DW_AT_producer)
- return 1;
+ if (AT_class (at) == dw_val_class_die_ref)
+ {
+ dw_die_ref target_die = AT_ref (at);
+
+ /* For pointer and reference types, we checksum only the (qualified)
+ name of the target type (if there is a name). For friend entries,
+ we checksum only the (qualified) name of the target type or function.
+ This allows the checksum to remain the same whether the target type
+ is complete or not. */
+ if ((at->dw_attr == DW_AT_type
+ && (tag == DW_TAG_pointer_type
+ || tag == DW_TAG_reference_type
+ || tag == DW_TAG_ptr_to_member_type))
+ || (at->dw_attr == DW_AT_friend
+ && tag == DW_TAG_friend))
+ {
+ dw_attr_ref name_attr = get_AT (target_die, DW_AT_name);
- return same_dw_val_p (&at1->dw_attr_val, &at2->dw_attr_val, mark);
-}
+ if (name_attr != NULL)
+ {
+ dw_die_ref decl = get_AT_ref (target_die, DW_AT_specification);
+
+ if (decl == NULL)
+ decl = target_die;
+ CHECKSUM_ULEB128 ('N');
+ CHECKSUM_ULEB128 (at->dw_attr);
+ if (decl->die_parent != NULL)
+ checksum_die_context (decl->die_parent, ctx);
+ CHECKSUM_ULEB128 ('E');
+ CHECKSUM_STRING (AT_string (name_attr));
+ return;
+ }
+ }
-/* Do the dies look the same? */
+ /* For all other references to another DIE, we check to see if the
+ target DIE has already been visited. If it has, we emit a
+ backward reference; if not, we descend recursively. */
+ if (target_die->die_mark > 0)
+ {
+ CHECKSUM_ULEB128 ('R');
+ CHECKSUM_ULEB128 (at->dw_attr);
+ CHECKSUM_ULEB128 (target_die->die_mark);
+ }
+ else
+ {
+ dw_die_ref decl = get_AT_ref (target_die, DW_AT_specification);
+
+ if (decl == NULL)
+ decl = target_die;
+ target_die->die_mark = ++(*mark);
+ CHECKSUM_ULEB128 ('T');
+ CHECKSUM_ULEB128 (at->dw_attr);
+ if (decl->die_parent != NULL)
+ checksum_die_context (decl->die_parent, ctx);
+ die_checksum_ordered (target_die, ctx, mark);
+ }
+ return;
+ }
-static int
-same_die_p (dw_die_ref die1, dw_die_ref die2, int *mark)
-{
- dw_die_ref c1, c2;
- dw_attr_ref a1;
- unsigned ix;
+ CHECKSUM_ULEB128 ('A');
+ CHECKSUM_ULEB128 (at->dw_attr);
- /* To avoid infinite recursion. */
- if (die1->die_mark)
- return die1->die_mark == die2->die_mark;
- die1->die_mark = die2->die_mark = ++(*mark);
+ switch (AT_class (at))
+ {
+ case dw_val_class_const:
+ CHECKSUM_ULEB128 (DW_FORM_sdata);
+ CHECKSUM_SLEB128 (at->dw_attr_val.v.val_int);
+ break;
- if (die1->die_tag != die2->die_tag)
- return 0;
+ case dw_val_class_unsigned_const:
+ CHECKSUM_ULEB128 (DW_FORM_sdata);
+ CHECKSUM_SLEB128 ((int) at->dw_attr_val.v.val_unsigned);
+ break;
- if (VEC_length (dw_attr_node, die1->die_attr)
- != VEC_length (dw_attr_node, die2->die_attr))
- return 0;
+ case dw_val_class_const_double:
+ CHECKSUM_ULEB128 (DW_FORM_block);
+ CHECKSUM_ULEB128 (sizeof (at->dw_attr_val.v.val_double));
+ CHECKSUM (at->dw_attr_val.v.val_double);
+ break;
- for (ix = 0; VEC_iterate (dw_attr_node, die1->die_attr, ix, a1); ix++)
- if (!same_attr_p (a1, VEC_index (dw_attr_node, die2->die_attr, ix), mark))
- return 0;
+ case dw_val_class_vec:
+ CHECKSUM_ULEB128 (DW_FORM_block);
+ CHECKSUM_ULEB128 (sizeof (at->dw_attr_val.v.val_vec));
+ CHECKSUM (at->dw_attr_val.v.val_vec);
+ break;
- c1 = die1->die_child;
- c2 = die2->die_child;
- if (! c1)
- {
- if (c2)
- return 0;
- }
- else
- for (;;)
- {
- if (!same_die_p (c1, c2, mark))
- return 0;
- c1 = c1->die_sib;
- c2 = c2->die_sib;
- if (c1 == die1->die_child)
- {
- if (c2 == die2->die_child)
- break;
- else
- return 0;
- }
- }
+ case dw_val_class_flag:
+ CHECKSUM_ULEB128 (DW_FORM_flag);
+ CHECKSUM_ULEB128 (at->dw_attr_val.v.val_flag ? 1 : 0);
+ break;
- return 1;
+ case dw_val_class_str:
+ CHECKSUM_ULEB128 (DW_FORM_string);
+ CHECKSUM_STRING (AT_string (at));
+ break;
+
+ case dw_val_class_addr:
+ r = AT_addr (at);
+ gcc_assert (GET_CODE (r) == SYMBOL_REF);
+ CHECKSUM_ULEB128 (DW_FORM_string);
+ CHECKSUM_STRING (XSTR (r, 0));
+ break;
+
+ case dw_val_class_offset:
+ CHECKSUM_ULEB128 (DW_FORM_sdata);
+ CHECKSUM_ULEB128 (at->dw_attr_val.v.val_offset);
+ break;
+
+ case dw_val_class_loc:
+ for (loc = AT_loc (at); loc; loc = loc->dw_loc_next)
+ loc_checksum_ordered (loc, ctx);
+ break;
+
+ case dw_val_class_fde_ref:
+ case dw_val_class_lbl_id:
+ case dw_val_class_lineptr:
+ case dw_val_class_macptr:
+ break;
+
+ case dw_val_class_file:
+ CHECKSUM_ULEB128 (DW_FORM_string);
+ CHECKSUM_STRING (AT_file (at)->filename);
+ break;
+
+ case dw_val_class_data8:
+ CHECKSUM (at->dw_attr_val.v.val_data8);
+ break;
+
+ default:
+ break;
+ }
}
-/* Do the dies look the same? Wrapper around same_die_p. */
+struct checksum_attributes
+{
+ dw_attr_ref at_name;
+ dw_attr_ref at_type;
+ dw_attr_ref at_friend;
+ dw_attr_ref at_accessibility;
+ dw_attr_ref at_address_class;
+ dw_attr_ref at_allocated;
+ dw_attr_ref at_artificial;
+ dw_attr_ref at_associated;
+ dw_attr_ref at_binary_scale;
+ dw_attr_ref at_bit_offset;
+ dw_attr_ref at_bit_size;
+ dw_attr_ref at_bit_stride;
+ dw_attr_ref at_byte_size;
+ dw_attr_ref at_byte_stride;
+ dw_attr_ref at_const_value;
+ dw_attr_ref at_containing_type;
+ dw_attr_ref at_count;
+ dw_attr_ref at_data_location;
+ dw_attr_ref at_data_member_location;
+ dw_attr_ref at_decimal_scale;
+ dw_attr_ref at_decimal_sign;
+ dw_attr_ref at_default_value;
+ dw_attr_ref at_digit_count;
+ dw_attr_ref at_discr;
+ dw_attr_ref at_discr_list;
+ dw_attr_ref at_discr_value;
+ dw_attr_ref at_encoding;
+ dw_attr_ref at_endianity;
+ dw_attr_ref at_explicit;
+ dw_attr_ref at_is_optional;
+ dw_attr_ref at_location;
+ dw_attr_ref at_lower_bound;
+ dw_attr_ref at_mutable;
+ dw_attr_ref at_ordering;
+ dw_attr_ref at_picture_string;
+ dw_attr_ref at_prototyped;
+ dw_attr_ref at_small;
+ dw_attr_ref at_segment;
+ dw_attr_ref at_string_length;
+ dw_attr_ref at_threads_scaled;
+ dw_attr_ref at_upper_bound;
+ dw_attr_ref at_use_location;
+ dw_attr_ref at_use_UTF8;
+ dw_attr_ref at_variable_parameter;
+ dw_attr_ref at_virtuality;
+ dw_attr_ref at_visibility;
+ dw_attr_ref at_vtable_elem_location;
+};
-static int
-same_die_p_wrap (dw_die_ref die1, dw_die_ref die2)
-{
- int mark = 0;
- int ret = same_die_p (die1, die2, &mark);
+/* Collect the attributes that we will want to use for the checksum. */
- unmark_all_dies (die1);
- unmark_all_dies (die2);
+static void
+collect_checksum_attributes (struct checksum_attributes *attrs, dw_die_ref die)
+{
+ dw_attr_ref a;
+ unsigned ix;
- return ret;
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ {
+ switch (a->dw_attr)
+ {
+ case DW_AT_name:
+ attrs->at_name = a;
+ break;
+ case DW_AT_type:
+ attrs->at_type = a;
+ break;
+ case DW_AT_friend:
+ attrs->at_friend = a;
+ break;
+ case DW_AT_accessibility:
+ attrs->at_accessibility = a;
+ break;
+ case DW_AT_address_class:
+ attrs->at_address_class = a;
+ break;
+ case DW_AT_allocated:
+ attrs->at_allocated = a;
+ break;
+ case DW_AT_artificial:
+ attrs->at_artificial = a;
+ break;
+ case DW_AT_associated:
+ attrs->at_associated = a;
+ break;
+ case DW_AT_binary_scale:
+ attrs->at_binary_scale = a;
+ break;
+ case DW_AT_bit_offset:
+ attrs->at_bit_offset = a;
+ break;
+ case DW_AT_bit_size:
+ attrs->at_bit_size = a;
+ break;
+ case DW_AT_bit_stride:
+ attrs->at_bit_stride = a;
+ break;
+ case DW_AT_byte_size:
+ attrs->at_byte_size = a;
+ break;
+ case DW_AT_byte_stride:
+ attrs->at_byte_stride = a;
+ break;
+ case DW_AT_const_value:
+ attrs->at_const_value = a;
+ break;
+ case DW_AT_containing_type:
+ attrs->at_containing_type = a;
+ break;
+ case DW_AT_count:
+ attrs->at_count = a;
+ break;
+ case DW_AT_data_location:
+ attrs->at_data_location = a;
+ break;
+ case DW_AT_data_member_location:
+ attrs->at_data_member_location = a;
+ break;
+ case DW_AT_decimal_scale:
+ attrs->at_decimal_scale = a;
+ break;
+ case DW_AT_decimal_sign:
+ attrs->at_decimal_sign = a;
+ break;
+ case DW_AT_default_value:
+ attrs->at_default_value = a;
+ break;
+ case DW_AT_digit_count:
+ attrs->at_digit_count = a;
+ break;
+ case DW_AT_discr:
+ attrs->at_discr = a;
+ break;
+ case DW_AT_discr_list:
+ attrs->at_discr_list = a;
+ break;
+ case DW_AT_discr_value:
+ attrs->at_discr_value = a;
+ break;
+ case DW_AT_encoding:
+ attrs->at_encoding = a;
+ break;
+ case DW_AT_endianity:
+ attrs->at_endianity = a;
+ break;
+ case DW_AT_explicit:
+ attrs->at_explicit = a;
+ break;
+ case DW_AT_is_optional:
+ attrs->at_is_optional = a;
+ break;
+ case DW_AT_location:
+ attrs->at_location = a;
+ break;
+ case DW_AT_lower_bound:
+ attrs->at_lower_bound = a;
+ break;
+ case DW_AT_mutable:
+ attrs->at_mutable = a;
+ break;
+ case DW_AT_ordering:
+ attrs->at_ordering = a;
+ break;
+ case DW_AT_picture_string:
+ attrs->at_picture_string = a;
+ break;
+ case DW_AT_prototyped:
+ attrs->at_prototyped = a;
+ break;
+ case DW_AT_small:
+ attrs->at_small = a;
+ break;
+ case DW_AT_segment:
+ attrs->at_segment = a;
+ break;
+ case DW_AT_string_length:
+ attrs->at_string_length = a;
+ break;
+ case DW_AT_threads_scaled:
+ attrs->at_threads_scaled = a;
+ break;
+ case DW_AT_upper_bound:
+ attrs->at_upper_bound = a;
+ break;
+ case DW_AT_use_location:
+ attrs->at_use_location = a;
+ break;
+ case DW_AT_use_UTF8:
+ attrs->at_use_UTF8 = a;
+ break;
+ case DW_AT_variable_parameter:
+ attrs->at_variable_parameter = a;
+ break;
+ case DW_AT_virtuality:
+ attrs->at_virtuality = a;
+ break;
+ case DW_AT_visibility:
+ attrs->at_visibility = a;
+ break;
+ case DW_AT_vtable_elem_location:
+ attrs->at_vtable_elem_location = a;
+ break;
+ default:
+ break;
+ }
+ }
}
-/* The prefix to attach to symbols on DIEs in the current comdat debug
- info section. */
-static char *comdat_symbol_id;
+/* Calculate the checksum of a DIE, using an ordered subset of attributes. */
-/* The index of the current symbol within the current comdat CU. */
-static unsigned int comdat_symbol_number;
+static void
+die_checksum_ordered (dw_die_ref die, struct md5_ctx *ctx, int *mark)
+{
+ dw_die_ref c;
+ dw_die_ref decl;
+ struct checksum_attributes attrs;
+
+ CHECKSUM_ULEB128 ('D');
+ CHECKSUM_ULEB128 (die->die_tag);
+
+ memset (&attrs, 0, sizeof (attrs));
+
+ decl = get_AT_ref (die, DW_AT_specification);
+ if (decl != NULL)
+ collect_checksum_attributes (&attrs, decl);
+ collect_checksum_attributes (&attrs, die);
+
+ CHECKSUM_ATTR (attrs.at_name);
+ CHECKSUM_ATTR (attrs.at_accessibility);
+ CHECKSUM_ATTR (attrs.at_address_class);
+ CHECKSUM_ATTR (attrs.at_allocated);
+ CHECKSUM_ATTR (attrs.at_artificial);
+ CHECKSUM_ATTR (attrs.at_associated);
+ CHECKSUM_ATTR (attrs.at_binary_scale);
+ CHECKSUM_ATTR (attrs.at_bit_offset);
+ CHECKSUM_ATTR (attrs.at_bit_size);
+ CHECKSUM_ATTR (attrs.at_bit_stride);
+ CHECKSUM_ATTR (attrs.at_byte_size);
+ CHECKSUM_ATTR (attrs.at_byte_stride);
+ CHECKSUM_ATTR (attrs.at_const_value);
+ CHECKSUM_ATTR (attrs.at_containing_type);
+ CHECKSUM_ATTR (attrs.at_count);
+ CHECKSUM_ATTR (attrs.at_data_location);
+ CHECKSUM_ATTR (attrs.at_data_member_location);
+ CHECKSUM_ATTR (attrs.at_decimal_scale);
+ CHECKSUM_ATTR (attrs.at_decimal_sign);
+ CHECKSUM_ATTR (attrs.at_default_value);
+ CHECKSUM_ATTR (attrs.at_digit_count);
+ CHECKSUM_ATTR (attrs.at_discr);
+ CHECKSUM_ATTR (attrs.at_discr_list);
+ CHECKSUM_ATTR (attrs.at_discr_value);
+ CHECKSUM_ATTR (attrs.at_encoding);
+ CHECKSUM_ATTR (attrs.at_endianity);
+ CHECKSUM_ATTR (attrs.at_explicit);
+ CHECKSUM_ATTR (attrs.at_is_optional);
+ CHECKSUM_ATTR (attrs.at_location);
+ CHECKSUM_ATTR (attrs.at_lower_bound);
+ CHECKSUM_ATTR (attrs.at_mutable);
+ CHECKSUM_ATTR (attrs.at_ordering);
+ CHECKSUM_ATTR (attrs.at_picture_string);
+ CHECKSUM_ATTR (attrs.at_prototyped);
+ CHECKSUM_ATTR (attrs.at_small);
+ CHECKSUM_ATTR (attrs.at_segment);
+ CHECKSUM_ATTR (attrs.at_string_length);
+ CHECKSUM_ATTR (attrs.at_threads_scaled);
+ CHECKSUM_ATTR (attrs.at_upper_bound);
+ CHECKSUM_ATTR (attrs.at_use_location);
+ CHECKSUM_ATTR (attrs.at_use_UTF8);
+ CHECKSUM_ATTR (attrs.at_variable_parameter);
+ CHECKSUM_ATTR (attrs.at_virtuality);
+ CHECKSUM_ATTR (attrs.at_visibility);
+ CHECKSUM_ATTR (attrs.at_vtable_elem_location);
+ CHECKSUM_ATTR (attrs.at_type);
+ CHECKSUM_ATTR (attrs.at_friend);
+
+ /* Checksum the child DIEs, except for nested types and member functions. */
+ c = die->die_child;
+ if (c) do {
+ dw_attr_ref name_attr;
-/* Calculate the MD5 checksum of the compilation unit DIE UNIT_DIE and its
- children, and set comdat_symbol_id accordingly. */
+ c = c->die_sib;
+ name_attr = get_AT (c, DW_AT_name);
+ if ((is_type_die (c) || c->die_tag == DW_TAG_subprogram)
+ && name_attr != NULL)
+ {
+ CHECKSUM_ULEB128 ('S');
+ CHECKSUM_ULEB128 (c->die_tag);
+ CHECKSUM_STRING (AT_string (name_attr));
+ }
+ else
+ {
+ /* Mark this DIE so it gets processed when unmarking. */
+ if (c->die_mark == 0)
+ c->die_mark = -1;
+ die_checksum_ordered (c, ctx, mark);
+ }
+ } while (c != die->die_child);
+
+ CHECKSUM_ULEB128 (0);
+}
+
+#undef CHECKSUM
+#undef CHECKSUM_STRING
+#undef CHECKSUM_ATTR
+#undef CHECKSUM_LEB128
+#undef CHECKSUM_ULEB128
+
+/* Generate the type signature for DIE. This is computed by generating an
+ MD5 checksum over the DIE's tag, its relevant attributes, and its
+ children. Attributes that are references to other DIEs are processed
+ by recursion, using the MARK field to prevent infinite recursion.
+ If the DIE is nested inside a namespace or another type, we also
+ need to include that context in the signature. The lower 64 bits
+ of the resulting MD5 checksum comprise the signature. */
static void
-compute_section_prefix (dw_die_ref unit_die)
+generate_type_signature (dw_die_ref die, comdat_type_node *type_node)
{
- const char *die_name = get_AT_string (unit_die, DW_AT_name);
- const char *base = die_name ? lbasename (die_name) : "anonymous";
- char *name = XALLOCAVEC (char, strlen (base) + 64);
- char *p;
- int i, mark;
+ int mark;
+ const char *name;
unsigned char checksum[16];
struct md5_ctx ctx;
+ dw_die_ref decl;
- /* Compute the checksum of the DIE, then append part of it as hex digits to
- the name filename of the unit. */
-
- md5_init_ctx (&ctx);
- mark = 0;
- die_checksum (unit_die, &ctx, &mark);
- unmark_all_dies (unit_die);
- md5_finish_ctx (&ctx, checksum);
+ name = get_AT_string (die, DW_AT_name);
+ decl = get_AT_ref (die, DW_AT_specification);
- sprintf (name, "%s.", base);
- clean_symbol_name (name);
+ /* First, compute a signature for just the type name (and its surrounding
+ context, if any. This is stored in the type unit DIE for link-time
+ ODR (one-definition rule) checking. */
- p = name + strlen (name);
- for (i = 0; i < 4; i++)
+ if (is_cxx() && name != NULL)
{
- sprintf (p, "%.2x", checksum[i]);
- p += 2;
+ md5_init_ctx (&ctx);
+
+ /* Checksum the names of surrounding namespaces and structures. */
+ if (decl != NULL && decl->die_parent != NULL)
+ checksum_die_context (decl->die_parent, &ctx);
+
+ md5_process_bytes (&die->die_tag, sizeof (die->die_tag), &ctx);
+ md5_process_bytes (name, strlen (name) + 1, &ctx);
+ md5_finish_ctx (&ctx, checksum);
+
+ add_AT_data8 (type_node->root_die, DW_AT_GNU_odr_signature, &checksum[8]);
}
- comdat_symbol_id = unit_die->die_symbol = xstrdup (name);
- comdat_symbol_number = 0;
-}
+ /* Next, compute the complete type signature. */
-/* Returns nonzero if DIE represents a type, in the sense of TYPE_P. */
+ md5_init_ctx (&ctx);
+ mark = 1;
+ die->die_mark = mark;
-static int
-is_type_die (dw_die_ref die)
-{
- switch (die->die_tag)
- {
- case DW_TAG_array_type:
- case DW_TAG_class_type:
- case DW_TAG_interface_type:
- case DW_TAG_enumeration_type:
+ /* Checksum the names of surrounding namespaces and structures. */
+ if (decl != NULL && decl->die_parent != NULL)
+ checksum_die_context (decl->die_parent, &ctx);
+
+ /* Checksum the DIE and its children. */
+ die_checksum_ordered (die, &ctx, &mark);
+ unmark_all_dies (die);
+ md5_finish_ctx (&ctx, checksum);
+
+ /* Store the signature in the type node and link the type DIE and the
+ type node together. */
+ memcpy (type_node->signature, &checksum[16 - DWARF_TYPE_SIGNATURE_SIZE],
+ DWARF_TYPE_SIGNATURE_SIZE);
+ die->die_id.die_type_node = type_node;
+ type_node->type_die = die;
+
+ /* If the DIE is a specification, link its declaration to the type node
+ as well. */
+ if (decl != NULL)
+ decl->die_id.die_type_node = type_node;
+}
+
+/* Do the location expressions look same? */
+static inline int
+same_loc_p (dw_loc_descr_ref loc1, dw_loc_descr_ref loc2, int *mark)
+{
+ return loc1->dw_loc_opc == loc2->dw_loc_opc
+ && same_dw_val_p (&loc1->dw_loc_oprnd1, &loc2->dw_loc_oprnd1, mark)
+ && same_dw_val_p (&loc1->dw_loc_oprnd2, &loc2->dw_loc_oprnd2, mark);
+}
+
+/* Do the values look the same? */
+static int
+same_dw_val_p (const dw_val_node *v1, const dw_val_node *v2, int *mark)
+{
+ dw_loc_descr_ref loc1, loc2;
+ rtx r1, r2;
+
+ if (v1->val_class != v2->val_class)
+ return 0;
+
+ switch (v1->val_class)
+ {
+ case dw_val_class_const:
+ return v1->v.val_int == v2->v.val_int;
+ case dw_val_class_unsigned_const:
+ return v1->v.val_unsigned == v2->v.val_unsigned;
+ case dw_val_class_const_double:
+ return v1->v.val_double.high == v2->v.val_double.high
+ && v1->v.val_double.low == v2->v.val_double.low;
+ case dw_val_class_vec:
+ if (v1->v.val_vec.length != v2->v.val_vec.length
+ || v1->v.val_vec.elt_size != v2->v.val_vec.elt_size)
+ return 0;
+ if (memcmp (v1->v.val_vec.array, v2->v.val_vec.array,
+ v1->v.val_vec.length * v1->v.val_vec.elt_size))
+ return 0;
+ return 1;
+ case dw_val_class_flag:
+ return v1->v.val_flag == v2->v.val_flag;
+ case dw_val_class_str:
+ return !strcmp(v1->v.val_str->str, v2->v.val_str->str);
+
+ case dw_val_class_addr:
+ r1 = v1->v.val_addr;
+ r2 = v2->v.val_addr;
+ if (GET_CODE (r1) != GET_CODE (r2))
+ return 0;
+ return !rtx_equal_p (r1, r2);
+
+ case dw_val_class_offset:
+ return v1->v.val_offset == v2->v.val_offset;
+
+ case dw_val_class_loc:
+ for (loc1 = v1->v.val_loc, loc2 = v2->v.val_loc;
+ loc1 && loc2;
+ loc1 = loc1->dw_loc_next, loc2 = loc2->dw_loc_next)
+ if (!same_loc_p (loc1, loc2, mark))
+ return 0;
+ return !loc1 && !loc2;
+
+ case dw_val_class_die_ref:
+ return same_die_p (v1->v.val_die_ref.die, v2->v.val_die_ref.die, mark);
+
+ case dw_val_class_fde_ref:
+ case dw_val_class_lbl_id:
+ case dw_val_class_lineptr:
+ case dw_val_class_macptr:
+ return 1;
+
+ case dw_val_class_file:
+ return v1->v.val_file == v2->v.val_file;
+
+ case dw_val_class_data8:
+ return !memcmp (v1->v.val_data8, v2->v.val_data8, 8);
+
+ default:
+ return 1;
+ }
+}
+
+/* Do the attributes look the same? */
+
+static int
+same_attr_p (dw_attr_ref at1, dw_attr_ref at2, int *mark)
+{
+ if (at1->dw_attr != at2->dw_attr)
+ return 0;
+
+ /* We don't care that this was compiled with a different compiler
+ snapshot; if the output is the same, that's what matters. */
+ if (at1->dw_attr == DW_AT_producer)
+ return 1;
+
+ return same_dw_val_p (&at1->dw_attr_val, &at2->dw_attr_val, mark);
+}
+
+/* Do the dies look the same? */
+
+static int
+same_die_p (dw_die_ref die1, dw_die_ref die2, int *mark)
+{
+ dw_die_ref c1, c2;
+ dw_attr_ref a1;
+ unsigned ix;
+
+ /* To avoid infinite recursion. */
+ if (die1->die_mark)
+ return die1->die_mark == die2->die_mark;
+ die1->die_mark = die2->die_mark = ++(*mark);
+
+ if (die1->die_tag != die2->die_tag)
+ return 0;
+
+ if (VEC_length (dw_attr_node, die1->die_attr)
+ != VEC_length (dw_attr_node, die2->die_attr))
+ return 0;
+
+ for (ix = 0; VEC_iterate (dw_attr_node, die1->die_attr, ix, a1); ix++)
+ if (!same_attr_p (a1, VEC_index (dw_attr_node, die2->die_attr, ix), mark))
+ return 0;
+
+ c1 = die1->die_child;
+ c2 = die2->die_child;
+ if (! c1)
+ {
+ if (c2)
+ return 0;
+ }
+ else
+ for (;;)
+ {
+ if (!same_die_p (c1, c2, mark))
+ return 0;
+ c1 = c1->die_sib;
+ c2 = c2->die_sib;
+ if (c1 == die1->die_child)
+ {
+ if (c2 == die2->die_child)
+ break;
+ else
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Do the dies look the same? Wrapper around same_die_p. */
+
+static int
+same_die_p_wrap (dw_die_ref die1, dw_die_ref die2)
+{
+ int mark = 0;
+ int ret = same_die_p (die1, die2, &mark);
+
+ unmark_all_dies (die1);
+ unmark_all_dies (die2);
+
+ return ret;
+}
+
+/* The prefix to attach to symbols on DIEs in the current comdat debug
+ info section. */
+static char *comdat_symbol_id;
+
+/* The index of the current symbol within the current comdat CU. */
+static unsigned int comdat_symbol_number;
+
+/* Calculate the MD5 checksum of the compilation unit DIE UNIT_DIE and its
+ children, and set comdat_symbol_id accordingly. */
+
+static void
+compute_section_prefix (dw_die_ref unit_die)
+{
+ const char *die_name = get_AT_string (unit_die, DW_AT_name);
+ const char *base = die_name ? lbasename (die_name) : "anonymous";
+ char *name = XALLOCAVEC (char, strlen (base) + 64);
+ char *p;
+ int i, mark;
+ unsigned char checksum[16];
+ struct md5_ctx ctx;
+
+ /* Compute the checksum of the DIE, then append part of it as hex digits to
+ the name filename of the unit. */
+
+ md5_init_ctx (&ctx);
+ mark = 0;
+ die_checksum (unit_die, &ctx, &mark);
+ unmark_all_dies (unit_die);
+ md5_finish_ctx (&ctx, checksum);
+
+ sprintf (name, "%s.", base);
+ clean_symbol_name (name);
+
+ p = name + strlen (name);
+ for (i = 0; i < 4; i++)
+ {
+ sprintf (p, "%.2x", checksum[i]);
+ p += 2;
+ }
+
+ comdat_symbol_id = unit_die->die_id.die_symbol = xstrdup (name);
+ comdat_symbol_number = 0;
+}
+
+/* Returns nonzero if DIE represents a type, in the sense of TYPE_P. */
+
+static int
+is_type_die (dw_die_ref die)
+{
+ switch (die->die_tag)
+ {
+ case DW_TAG_array_type:
+ case DW_TAG_class_type:
+ case DW_TAG_interface_type:
+ case DW_TAG_enumeration_type:
case DW_TAG_pointer_type:
case DW_TAG_reference_type:
case DW_TAG_string_type:
is_symbol_die (dw_die_ref c)
{
return (is_type_die (c)
- || (get_AT (c, DW_AT_declaration)
- && !get_AT (c, DW_AT_specification))
+ || is_declaration_die (c)
|| c->die_tag == DW_TAG_namespace
|| c->die_tag == DW_TAG_module);
}
sprintf (p, "%s.%s.%x", DIE_LABEL_PREFIX,
comdat_symbol_id, comdat_symbol_number++);
- die->die_symbol = xstrdup (p);
+ die->die_id.die_symbol = xstrdup (p);
}
else
- die->die_symbol = gen_internal_sym ("LDIE");
+ die->die_id.die_symbol = gen_internal_sym ("LDIE");
}
FOR_EACH_CHILD (die, c, assign_symbol_names (c));
const struct cu_hash_table_entry *const entry =
(const struct cu_hash_table_entry *) of;
- return htab_hash_string (entry->cu->die_symbol);
+ return htab_hash_string (entry->cu->die_id.die_symbol);
}
static int
(const struct cu_hash_table_entry *) of1;
const struct die_struct *const entry2 = (const struct die_struct *) of2;
- return !strcmp (entry1->cu->die_symbol, entry2->die_symbol);
+ return !strcmp (entry1->cu->die_id.die_symbol, entry2->die_id.die_symbol);
}
static void
dummy.max_comdat_num = 0;
slot = (struct cu_hash_table_entry **)
- htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol),
+ htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_id.die_symbol),
INSERT);
entry = *slot;
struct cu_hash_table_entry **slot, *entry;
slot = (struct cu_hash_table_entry **)
- htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_symbol),
+ htab_find_slot_with_hash (htable, cu, htab_hash_string (cu->die_id.die_symbol),
NO_INSERT);
entry = *slot;
htab_delete (cu_hash_table);
}
-/* Traverse the DIE and add a sibling attribute if it may have the
- effect of speeding up access to siblings. To save some space,
- avoid generating sibling attributes for DIE's without children. */
-
-static void
-add_sibling_attributes (dw_die_ref die)
-{
- dw_die_ref c;
-
- if (! die->die_child)
- return;
-
- if (die->die_parent && die != die->die_parent->die_child)
- add_AT_die_ref (die, DW_AT_sibling, die->die_sib);
-
- FOR_EACH_CHILD (die, c, add_sibling_attributes (c));
-}
-
-/* Output all location lists for the DIE and its children. */
+/* Return non-zero if this DIE is a declaration. */
-static void
-output_location_lists (dw_die_ref die)
+static int
+is_declaration_die (dw_die_ref die)
{
- dw_die_ref c;
dw_attr_ref a;
unsigned ix;
for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
- if (AT_class (a) == dw_val_class_loc_list)
- output_loc_list (AT_loc_list (a));
+ if (a->dw_attr == DW_AT_declaration)
+ return 1;
- FOR_EACH_CHILD (die, c, output_location_lists (c));
+ return 0;
}
-/* The format of each DIE (and its attribute value pairs) is encoded in an
- abbreviation table. This routine builds the abbreviation table and assigns
- a unique abbreviation id for each abbreviation entry. The children of each
- die are visited recursively. */
+/* Return non-zero if this is a type DIE that should be moved to a
+ COMDAT .debug_types section. */
-static void
-build_abbrev_table (dw_die_ref die)
+static int
+should_move_die_to_comdat (dw_die_ref die)
{
- unsigned long abbrev_id;
- unsigned int n_alloc;
- dw_die_ref c;
- dw_attr_ref a;
- unsigned ix;
-
- /* Scan the DIE references, and mark as external any that refer to
- DIEs from other CUs (i.e. those which are not marked). */
- for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
- if (AT_class (a) == dw_val_class_die_ref
- && AT_ref (a)->die_mark == 0)
- {
- gcc_assert (AT_ref (a)->die_symbol);
- set_AT_ref_external (a, 1);
- }
-
- for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
+ switch (die->die_tag)
{
- dw_die_ref abbrev = abbrev_die_table[abbrev_id];
- dw_attr_ref die_a, abbrev_a;
- unsigned ix;
- bool ok = true;
-
- if (abbrev->die_tag != die->die_tag)
- continue;
- if ((abbrev->die_child != NULL) != (die->die_child != NULL))
- continue;
-
- if (VEC_length (dw_attr_node, abbrev->die_attr)
- != VEC_length (dw_attr_node, die->die_attr))
- continue;
-
- for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, die_a); ix++)
- {
- abbrev_a = VEC_index (dw_attr_node, abbrev->die_attr, ix);
- if ((abbrev_a->dw_attr != die_a->dw_attr)
- || (value_format (abbrev_a) != value_format (die_a)))
- {
- ok = false;
- break;
- }
- }
- if (ok)
- break;
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_union_type:
+ /* Don't move declarations or inlined instances. */
+ if (is_declaration_die (die) || get_AT (die, DW_AT_abstract_origin))
+ return 0;
+ return 1;
+ case DW_TAG_array_type:
+ case DW_TAG_interface_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_string_type:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_ptr_to_member_type:
+ case DW_TAG_set_type:
+ case DW_TAG_subrange_type:
+ case DW_TAG_base_type:
+ case DW_TAG_const_type:
+ case DW_TAG_file_type:
+ case DW_TAG_packed_type:
+ case DW_TAG_volatile_type:
+ case DW_TAG_typedef:
+ default:
+ return 0;
}
+}
- if (abbrev_id >= abbrev_die_table_in_use)
- {
- if (abbrev_die_table_in_use >= abbrev_die_table_allocated)
- {
- n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT;
- abbrev_die_table = GGC_RESIZEVEC (dw_die_ref, abbrev_die_table,
- n_alloc);
+/* Make a clone of DIE. */
- memset (&abbrev_die_table[abbrev_die_table_allocated], 0,
- (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref));
- abbrev_die_table_allocated = n_alloc;
- }
+static dw_die_ref
+clone_die (dw_die_ref die)
+{
+ dw_die_ref clone;
+ dw_attr_ref a;
+ unsigned ix;
- ++abbrev_die_table_in_use;
- abbrev_die_table[abbrev_id] = die;
- }
+ clone = GGC_CNEW (die_node);
+ clone->die_tag = die->die_tag;
- die->die_abbrev = abbrev_id;
- FOR_EACH_CHILD (die, c, build_abbrev_table (c));
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ add_dwarf_attr (clone, a);
+
+ return clone;
}
-\f
-/* Return the power-of-two number of bytes necessary to represent VALUE. */
-static int
-constant_size (unsigned HOST_WIDE_INT value)
-{
- int log;
+/* Make a clone of the tree rooted at DIE. */
- if (value == 0)
- log = 0;
- else
- log = floor_log2 (value);
+static dw_die_ref
+clone_tree (dw_die_ref die)
+{
+ dw_die_ref c;
+ dw_die_ref clone = clone_die (die);
- log = log / 8;
- log = 1 << (floor_log2 (log) + 1);
+ FOR_EACH_CHILD (die, c, add_child_die (clone, clone_tree(c)));
- return log;
+ return clone;
}
-/* Return the size of a DIE as it is represented in the
- .debug_info section. */
+/* Make a clone of DIE as a declaration. */
-static unsigned long
-size_of_die (dw_die_ref die)
+static dw_die_ref
+clone_as_declaration (dw_die_ref die)
{
- unsigned long size = 0;
+ dw_die_ref clone;
+ dw_die_ref decl;
dw_attr_ref a;
unsigned ix;
- size += size_of_uleb128 (die->die_abbrev);
+ /* If the DIE is already a declaration, just clone it. */
+ if (is_declaration_die (die))
+ return clone_die (die);
+
+ /* If the DIE is a specification, just clone its declaration DIE. */
+ decl = get_AT_ref (die, DW_AT_specification);
+ if (decl != NULL)
+ return clone_die (decl);
+
+ clone = GGC_CNEW (die_node);
+ clone->die_tag = die->die_tag;
+
for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
{
- switch (AT_class (a))
- {
- case dw_val_class_addr:
- size += DWARF2_ADDR_SIZE;
- break;
- case dw_val_class_offset:
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_loc:
- {
- unsigned long lsize = size_of_locs (AT_loc (a));
+ /* We don't want to copy over all attributes.
+ For example we don't want DW_AT_byte_size because otherwise we will no
+ longer have a declaration and GDB will treat it as a definition. */
- /* Block length. */
- size += constant_size (lsize);
- size += lsize;
- }
- break;
- case dw_val_class_loc_list:
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_range_list:
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_const:
- size += size_of_sleb128 (AT_int (a));
- break;
- case dw_val_class_unsigned_const:
- size += constant_size (AT_unsigned (a));
- break;
- case dw_val_class_long_long:
- size += 1 + 2*HOST_BITS_PER_LONG/HOST_BITS_PER_CHAR; /* block */
- break;
- case dw_val_class_vec:
- size += constant_size (a->dw_attr_val.v.val_vec.length
- * a->dw_attr_val.v.val_vec.elt_size)
- + a->dw_attr_val.v.val_vec.length
- * a->dw_attr_val.v.val_vec.elt_size; /* block */
- break;
- case dw_val_class_flag:
- size += 1;
- break;
- case dw_val_class_die_ref:
- /* In DWARF2, DW_FORM_ref_addr is sized by target address length,
- whereas in DWARF3 it's always sized as an offset. */
- if (AT_ref_external (a) && dwarf_version == 2)
- size += DWARF2_ADDR_SIZE;
- else
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_fde_ref:
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_lbl_id:
- size += DWARF2_ADDR_SIZE;
- break;
- case dw_val_class_lineptr:
- case dw_val_class_macptr:
- size += DWARF_OFFSET_SIZE;
- break;
- case dw_val_class_str:
- if (AT_string_form (a) == DW_FORM_strp)
- size += DWARF_OFFSET_SIZE;
- else
- size += strlen (a->dw_attr_val.v.val_str->str) + 1;
- break;
- case dw_val_class_file:
- size += constant_size (maybe_emit_file (a->dw_attr_val.v.val_file));
- break;
- default:
- gcc_unreachable ();
- }
+ switch (a->dw_attr)
+ {
+ case DW_AT_artificial:
+ case DW_AT_containing_type:
+ case DW_AT_external:
+ case DW_AT_name:
+ case DW_AT_type:
+ case DW_AT_virtuality:
+ case DW_AT_MIPS_linkage_name:
+ add_dwarf_attr (clone, a);
+ break;
+ case DW_AT_byte_size:
+ default:
+ break;
+ }
}
- return size;
+ if (die->die_id.die_type_node)
+ add_AT_die_ref (clone, DW_AT_signature, die);
+
+ add_AT_flag (clone, DW_AT_declaration, 1);
+ return clone;
}
-/* Size the debugging information associated with a given DIE. Visits the
- DIE's children recursively. Updates the global variable next_die_offset, on
- each time through. Uses the current value of next_die_offset to update the
- die_offset field in each DIE. */
+/* Copy the declaration context to the new compile unit DIE. This includes
+ any surrounding namespace or type declarations. If the DIE has an
+ AT_specification attribute, it also includes attributes and children
+ attached to the specification. */
static void
-calc_die_sizes (dw_die_ref die)
+copy_declaration_context (dw_die_ref unit, dw_die_ref die)
{
- dw_die_ref c;
+ dw_die_ref decl;
+ dw_die_ref new_decl;
- die->die_offset = next_die_offset;
- next_die_offset += size_of_die (die);
+ decl = get_AT_ref (die, DW_AT_specification);
+ if (decl == NULL)
+ decl = die;
+ else
+ {
+ unsigned ix;
+ dw_die_ref c;
+ dw_attr_ref a;
- FOR_EACH_CHILD (die, c, calc_die_sizes (c));
+ /* Copy the type node pointer from the new DIE to the original
+ declaration DIE so we can forward references later. */
+ decl->die_id.die_type_node = die->die_id.die_type_node;
- if (die->die_child != NULL)
- /* Count the null byte used to terminate sibling lists. */
- next_die_offset += 1;
+ remove_AT (die, DW_AT_specification);
+
+ for (ix = 0; VEC_iterate (dw_attr_node, decl->die_attr, ix, a); ix++)
+ {
+ if (a->dw_attr != DW_AT_name
+ && a->dw_attr != DW_AT_declaration
+ && a->dw_attr != DW_AT_external)
+ add_dwarf_attr (die, a);
+ }
+
+ FOR_EACH_CHILD (decl, c, add_child_die (die, clone_tree(c)));
+ }
+
+ if (decl->die_parent != NULL
+ && decl->die_parent->die_tag != DW_TAG_compile_unit
+ && decl->die_parent->die_tag != DW_TAG_type_unit)
+ {
+ new_decl = copy_ancestor_tree (unit, decl, NULL);
+ if (new_decl != NULL)
+ {
+ remove_AT (new_decl, DW_AT_signature);
+ add_AT_specification (die, new_decl);
+ }
+ }
}
-/* Set the marks for a die and its children. We do this so
- that we know whether or not a reference needs to use FORM_ref_addr; only
- DIEs in the same CU will be marked. We used to clear out the offset
- and use that as the flag, but ran into ordering problems. */
+/* Generate the skeleton ancestor tree for the given NODE, then clone
+ the DIE and add the clone into the tree. */
static void
-mark_dies (dw_die_ref die)
+generate_skeleton_ancestor_tree (skeleton_chain_node *node)
{
- dw_die_ref c;
+ if (node->new_die != NULL)
+ return;
- gcc_assert (!die->die_mark);
+ node->new_die = clone_as_declaration (node->old_die);
- die->die_mark = 1;
- FOR_EACH_CHILD (die, c, mark_dies (c));
+ if (node->parent != NULL)
+ {
+ generate_skeleton_ancestor_tree (node->parent);
+ add_child_die (node->parent->new_die, node->new_die);
+ }
}
-/* Clear the marks for a die and its children. */
+/* Generate a skeleton tree of DIEs containing any declarations that are
+ found in the original tree. We traverse the tree looking for declaration
+ DIEs, and construct the skeleton from the bottom up whenever we find one. */
static void
-unmark_dies (dw_die_ref die)
+generate_skeleton_bottom_up (skeleton_chain_node *parent)
{
+ skeleton_chain_node node;
dw_die_ref c;
+ dw_die_ref first;
+ dw_die_ref prev = NULL;
+ dw_die_ref next = NULL;
- gcc_assert (die->die_mark);
+ node.parent = parent;
- die->die_mark = 0;
- FOR_EACH_CHILD (die, c, unmark_dies (c));
+ first = c = parent->old_die->die_child;
+ if (c)
+ next = c->die_sib;
+ if (c) do {
+ if (prev == NULL || prev->die_sib == c)
+ prev = c;
+ c = next;
+ next = (c == first ? NULL : c->die_sib);
+ node.old_die = c;
+ node.new_die = NULL;
+ if (is_declaration_die (c))
+ {
+ /* Clone the existing DIE, move the original to the skeleton
+ tree (which is in the main CU), and put the clone, with
+ all the original's children, where the original came from. */
+ dw_die_ref clone = clone_die (c);
+ move_all_children (c, clone);
+
+ replace_child (c, clone, prev);
+ generate_skeleton_ancestor_tree (parent);
+ add_child_die (parent->new_die, c);
+ node.new_die = c;
+ c = clone;
+ }
+ generate_skeleton_bottom_up (&node);
+ } while (next != NULL);
}
-/* Clear the marks for a die, its children and referred dies. */
+/* Wrapper function for generate_skeleton_bottom_up. */
-static void
-unmark_all_dies (dw_die_ref die)
+static dw_die_ref
+generate_skeleton (dw_die_ref die)
{
- dw_die_ref c;
- dw_attr_ref a;
- unsigned ix;
+ skeleton_chain_node node;
- if (!die->die_mark)
- return;
- die->die_mark = 0;
+ node.old_die = die;
+ node.new_die = NULL;
+ node.parent = NULL;
- FOR_EACH_CHILD (die, c, unmark_all_dies (c));
+ /* If this type definition is nested inside another type,
+ always leave at least a declaration in its place. */
+ if (die->die_parent != NULL && is_type_die (die->die_parent))
+ node.new_die = clone_as_declaration (die);
- for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
- if (AT_class (a) == dw_val_class_die_ref)
- unmark_all_dies (AT_ref (a));
+ generate_skeleton_bottom_up (&node);
+ return node.new_die;
}
-/* Return the size of the .debug_pubnames or .debug_pubtypes table
- generated for the compilation unit. */
+/* Remove the DIE from its parent, possibly replacing it with a cloned
+ declaration. The original DIE will be moved to a new compile unit
+ so that existing references to it follow it to the new location. If
+ any of the original DIE's descendants is a declaration, we need to
+ replace the original DIE with a skeleton tree and move the
+ declarations back into the skeleton tree. */
-static unsigned long
-size_of_pubnames (VEC (pubname_entry, gc) * names)
+static dw_die_ref
+remove_child_or_replace_with_skeleton (dw_die_ref child, dw_die_ref prev)
{
- unsigned long size;
- unsigned i;
- pubname_ref p;
+ dw_die_ref skeleton;
- size = DWARF_PUBNAMES_HEADER_SIZE;
- for (i = 0; VEC_iterate (pubname_entry, names, i, p); i++)
- if (names != pubtype_table
- || p->die->die_offset != 0
- || !flag_eliminate_unused_debug_types)
- size += strlen (p->name) + DWARF_OFFSET_SIZE + 1;
+ skeleton = generate_skeleton (child);
+ if (skeleton == NULL)
+ remove_child_with_prev (child, prev);
+ else
+ {
+ skeleton->die_id.die_type_node = child->die_id.die_type_node;
+ replace_child (child, skeleton, prev);
+ }
- size += DWARF_OFFSET_SIZE;
- return size;
+ return skeleton;
}
-/* Return the size of the information in the .debug_aranges section. */
+/* Traverse the DIE and set up additional .debug_types sections for each
+ type worthy of being placed in a COMDAT section. */
-static unsigned long
-size_of_aranges (void)
+static void
+break_out_comdat_types (dw_die_ref die)
{
- unsigned long size;
+ dw_die_ref c;
+ dw_die_ref first;
+ dw_die_ref prev = NULL;
+ dw_die_ref next = NULL;
+ dw_die_ref unit = NULL;
- size = DWARF_ARANGES_HEADER_SIZE;
+ first = c = die->die_child;
+ if (c)
+ next = c->die_sib;
+ if (c) do {
+ if (prev == NULL || prev->die_sib == c)
+ prev = c;
+ c = next;
+ next = (c == first ? NULL : c->die_sib);
+ if (should_move_die_to_comdat (c))
+ {
+ dw_die_ref replacement;
+ comdat_type_node_ref type_node;
- /* Count the address/length pair for this compilation unit. */
- if (text_section_used)
- size += 2 * DWARF2_ADDR_SIZE;
- if (cold_text_section_used)
- size += 2 * DWARF2_ADDR_SIZE;
- size += 2 * DWARF2_ADDR_SIZE * arange_table_in_use;
+ /* Create a new type unit DIE as the root for the new tree, and
+ 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));
+ type_node = GGC_CNEW (comdat_type_node);
+ type_node->root_die = unit;
+ type_node->next = comdat_type_list;
+ comdat_type_list = type_node;
- /* Count the two zero words used to terminated the address range table. */
- size += 2 * DWARF2_ADDR_SIZE;
- return size;
+ /* Generate the type signature. */
+ generate_type_signature (c, type_node);
+
+ /* Copy the declaration context, attributes, and children of the
+ declaration into the new compile unit DIE. */
+ copy_declaration_context (unit, c);
+
+ /* Remove this DIE from the main CU. */
+ replacement = remove_child_or_replace_with_skeleton (c, prev);
+
+ /* Break out nested types into their own type units. */
+ break_out_comdat_types (c);
+
+ /* Add the DIE to the new compunit. */
+ add_child_die (unit, c);
+
+ if (replacement != NULL)
+ c = replacement;
+ }
+ else if (c->die_tag == DW_TAG_namespace
+ || c->die_tag == DW_TAG_class_type
+ || c->die_tag == DW_TAG_structure_type
+ || c->die_tag == DW_TAG_union_type)
+ {
+ /* Look for nested types that can be broken out. */
+ break_out_comdat_types (c);
+ }
+ } while (next != NULL);
}
-\f
-/* Select the encoding of an attribute value. */
-static enum dwarf_form
-value_format (dw_attr_ref a)
+/* Structure to map a DIE in one CU to its copy in a comdat type unit. */
+
+struct decl_table_entry
{
- switch (a->dw_attr_val.val_class)
- {
- case dw_val_class_addr:
- return DW_FORM_addr;
- case dw_val_class_range_list:
- case dw_val_class_offset:
- case dw_val_class_loc_list:
- switch (DWARF_OFFSET_SIZE)
- {
- case 4:
- return DW_FORM_data4;
- case 8:
- return DW_FORM_data8;
- default:
- gcc_unreachable ();
- }
- case dw_val_class_loc:
- switch (constant_size (size_of_locs (AT_loc (a))))
- {
- case 1:
- return DW_FORM_block1;
- case 2:
- return DW_FORM_block2;
- default:
- gcc_unreachable ();
- }
- case dw_val_class_const:
- return DW_FORM_sdata;
- case dw_val_class_unsigned_const:
- switch (constant_size (AT_unsigned (a)))
- {
- case 1:
- return DW_FORM_data1;
- case 2:
- return DW_FORM_data2;
- case 4:
- return DW_FORM_data4;
- case 8:
- return DW_FORM_data8;
- default:
- gcc_unreachable ();
- }
- case dw_val_class_long_long:
- return DW_FORM_block1;
- case dw_val_class_vec:
- switch (constant_size (a->dw_attr_val.v.val_vec.length
- * a->dw_attr_val.v.val_vec.elt_size))
- {
- case 1:
- return DW_FORM_block1;
- case 2:
- return DW_FORM_block2;
- case 4:
- return DW_FORM_block4;
- default:
- gcc_unreachable ();
- }
- case dw_val_class_flag:
- return DW_FORM_flag;
- case dw_val_class_die_ref:
- if (AT_ref_external (a))
- return DW_FORM_ref_addr;
- else
- return DW_FORM_ref;
- case dw_val_class_fde_ref:
- return DW_FORM_data;
- case dw_val_class_lbl_id:
- return DW_FORM_addr;
- case dw_val_class_lineptr:
- case dw_val_class_macptr:
- return DW_FORM_data;
- case dw_val_class_str:
- return AT_string_form (a);
- case dw_val_class_file:
- switch (constant_size (maybe_emit_file (a->dw_attr_val.v.val_file)))
- {
- case 1:
- return DW_FORM_data1;
- case 2:
- return DW_FORM_data2;
- case 4:
- return DW_FORM_data4;
- default:
- gcc_unreachable ();
- }
+ dw_die_ref orig;
+ dw_die_ref copy;
+};
- default:
- gcc_unreachable ();
- }
+/* Routines to manipulate hash table of copied declarations. */
+
+static hashval_t
+htab_decl_hash (const void *of)
+{
+ const struct decl_table_entry *const entry =
+ (const struct decl_table_entry *) of;
+
+ return htab_hash_pointer (entry->orig);
}
-/* Output the encoding of an attribute value. */
+static int
+htab_decl_eq (const void *of1, const void *of2)
+{
+ const struct decl_table_entry *const entry1 =
+ (const struct decl_table_entry *) of1;
+ const struct die_struct *const entry2 = (const struct die_struct *) of2;
+
+ return entry1->orig == entry2;
+}
static void
-output_value_format (dw_attr_ref a)
+htab_decl_del (void *what)
{
- enum dwarf_form form = value_format (a);
+ struct decl_table_entry *entry = (struct decl_table_entry *) what;
- dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form));
+ free (entry);
}
-/* Output the .debug_abbrev section which defines the DIE abbreviation
- table. */
+/* Copy DIE and its ancestors, up to, but not including, the compile unit
+ or type unit entry, to a new tree. Adds the new tree to UNIT and returns
+ a pointer to the copy of DIE. If DECL_TABLE is provided, it is used
+ to check if the ancestor has already been copied into UNIT. */
-static void
-output_abbrev_section (void)
+static dw_die_ref
+copy_ancestor_tree (dw_die_ref unit, dw_die_ref die, htab_t decl_table)
{
- unsigned long abbrev_id;
+ dw_die_ref parent = die->die_parent;
+ dw_die_ref new_parent = unit;
+ dw_die_ref copy;
+ void **slot = NULL;
+ struct decl_table_entry *entry = NULL;
- for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
+ if (decl_table)
{
- dw_die_ref abbrev = abbrev_die_table[abbrev_id];
- unsigned ix;
- dw_attr_ref a_attr;
+ /* Check if the entry has already been copied to UNIT. */
+ slot = htab_find_slot_with_hash (decl_table, die,
+ htab_hash_pointer (die), INSERT);
+ if (*slot != HTAB_EMPTY_ENTRY)
+ {
+ entry = (struct decl_table_entry *) *slot;
+ return entry->copy;
+ }
- dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)");
- dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
- dwarf_tag_name (abbrev->die_tag));
+ /* Record in DECL_TABLE that DIE has been copied to UNIT. */
+ entry = XCNEW (struct decl_table_entry);
+ entry->orig = die;
+ entry->copy = NULL;
+ *slot = entry;
+ }
- if (abbrev->die_child != NULL)
- dw2_asm_output_data (1, DW_children_yes, "DW_children_yes");
- else
- dw2_asm_output_data (1, DW_children_no, "DW_children_no");
+ if (parent != NULL)
+ {
+ dw_die_ref spec = get_AT_ref (parent, DW_AT_specification);
+ if (spec != NULL)
+ parent = spec;
+ if (parent->die_tag != DW_TAG_compile_unit
+ && parent->die_tag != DW_TAG_type_unit)
+ new_parent = copy_ancestor_tree (unit, parent, decl_table);
+ }
- for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr);
- ix++)
- {
- dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)",
- dwarf_attr_name (a_attr->dw_attr));
- output_value_format (a_attr);
- }
+ copy = clone_as_declaration (die);
+ add_child_die (new_parent, copy);
- dw2_asm_output_data (1, 0, NULL);
- dw2_asm_output_data (1, 0, NULL);
+ if (decl_table != NULL)
+ {
+ /* Make sure the copy is marked as part of the type unit. */
+ copy->die_mark = 1;
+ /* Record the pointer to the copy. */
+ entry->copy = copy;
}
- /* Terminate the table. */
- dw2_asm_output_data (1, 0, NULL);
+ return copy;
}
-/* Output a symbol we can use to refer to this DIE from another CU. */
+/* Walk the DIE and its children, looking for references to incomplete
+ or trivial types that are unmarked (i.e., that are not in the current
+ type_unit). */
-static inline void
-output_die_symbol (dw_die_ref die)
+static void
+copy_decls_walk (dw_die_ref unit, dw_die_ref die, htab_t decl_table)
{
- char *sym = die->die_symbol;
+ dw_die_ref c;
+ dw_attr_ref a;
+ unsigned ix;
- if (sym == 0)
- return;
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ {
+ if (AT_class (a) == dw_val_class_die_ref)
+ {
+ dw_die_ref targ = AT_ref (a);
+ comdat_type_node_ref type_node = targ->die_id.die_type_node;
+ void **slot;
+ struct decl_table_entry *entry;
- if (strncmp (sym, DIE_LABEL_PREFIX, sizeof (DIE_LABEL_PREFIX) - 1) == 0)
- /* We make these global, not weak; if the target doesn't support
- .linkonce, it doesn't support combining the sections, so debugging
- will break. */
- targetm.asm_out.globalize_label (asm_out_file, sym);
+ if (targ->die_mark != 0 || type_node != NULL)
+ continue;
- ASM_OUTPUT_LABEL (asm_out_file, sym);
+ slot = htab_find_slot_with_hash (decl_table, targ,
+ htab_hash_pointer (targ), INSERT);
+
+ if (*slot != HTAB_EMPTY_ENTRY)
+ {
+ /* TARG has already been copied, so we just need to
+ modify the reference to point to the copy. */
+ entry = (struct decl_table_entry *) *slot;
+ a->dw_attr_val.v.val_die_ref.die = entry->copy;
+ }
+ else
+ {
+ dw_die_ref parent = unit;
+ dw_die_ref copy = clone_tree (targ);
+
+ /* Make sure the cloned tree is marked as part of the
+ type unit. */
+ mark_dies (copy);
+
+ /* Record in DECL_TABLE that TARG has been copied.
+ Need to do this now, before the recursive call,
+ because DECL_TABLE may be expanded and SLOT
+ would no longer be a valid pointer. */
+ entry = XCNEW (struct decl_table_entry);
+ entry->orig = targ;
+ entry->copy = copy;
+ *slot = entry;
+
+ /* If TARG has surrounding context, copy its ancestor tree
+ into the new type unit. */
+ if (targ->die_parent != NULL
+ && targ->die_parent->die_tag != DW_TAG_compile_unit
+ && targ->die_parent->die_tag != DW_TAG_type_unit)
+ parent = copy_ancestor_tree (unit, targ->die_parent,
+ decl_table);
+
+ add_child_die (parent, copy);
+ a->dw_attr_val.v.val_die_ref.die = copy;
+
+ /* Make sure the newly-copied DIE is walked. If it was
+ installed in a previously-added context, it won't
+ get visited otherwise. */
+ if (parent != unit)
+ copy_decls_walk (unit, parent, decl_table);
+ }
+ }
+ }
+
+ FOR_EACH_CHILD (die, c, copy_decls_walk (unit, c, decl_table));
}
-/* Return a new location list, given the begin and end range, and the
- expression. gensym tells us whether to generate a new internal symbol for
- this location list node, which is done for the head of the list only. */
+/* Copy declarations for "unworthy" types into the new comdat section.
+ Incomplete types, modified types, and certain other types aren't broken
+ out into comdat sections of their own, so they don't have a signature,
+ and we need to copy the declaration into the same section so that we
+ don't have an external reference. */
-static inline dw_loc_list_ref
-new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
- const char *section, unsigned int gensym)
+static void
+copy_decls_for_unworthy_types (dw_die_ref unit)
{
- dw_loc_list_ref retlist = GGC_CNEW (dw_loc_list_node);
+ htab_t decl_table;
- retlist->begin = begin;
- retlist->end = end;
- retlist->expr = expr;
- retlist->section = section;
- if (gensym)
- retlist->ll_symbol = gen_internal_sym ("LLST");
+ mark_dies (unit);
+ decl_table = htab_create (10, htab_decl_hash, htab_decl_eq, htab_decl_del);
+ copy_decls_walk (unit, unit, decl_table);
+ htab_delete (decl_table);
+ unmark_dies (unit);
+}
- return retlist;
+/* Traverse the DIE and add a sibling attribute if it may have the
+ effect of speeding up access to siblings. To save some space,
+ avoid generating sibling attributes for DIE's without children. */
+
+static void
+add_sibling_attributes (dw_die_ref die)
+{
+ dw_die_ref c;
+
+ if (! die->die_child)
+ return;
+
+ if (die->die_parent && die != die->die_parent->die_child)
+ add_AT_die_ref (die, DW_AT_sibling, die->die_sib);
+
+ FOR_EACH_CHILD (die, c, add_sibling_attributes (c));
}
-/* Add a location description expression to a location list. */
+/* Output all location lists for the DIE and its children. */
-static inline void
-add_loc_descr_to_loc_list (dw_loc_list_ref *list_head, dw_loc_descr_ref descr,
- const char *begin, const char *end,
- const char *section)
+static void
+output_location_lists (dw_die_ref die)
{
- dw_loc_list_ref *d;
+ dw_die_ref c;
+ dw_attr_ref a;
+ unsigned ix;
- /* Find the end of the chain. */
- for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next)
- ;
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ if (AT_class (a) == dw_val_class_loc_list)
+ output_loc_list (AT_loc_list (a));
- /* Add a new location list node to the list. */
- *d = new_loc_list (descr, begin, end, section, 0);
+ FOR_EACH_CHILD (die, c, output_location_lists (c));
}
-/* Output the location list given to us. */
+/* The format of each DIE (and its attribute value pairs) is encoded in an
+ abbreviation table. This routine builds the abbreviation table and assigns
+ a unique abbreviation id for each abbreviation entry. The children of each
+ die are visited recursively. */
static void
-output_loc_list (dw_loc_list_ref list_head)
+build_abbrev_table (dw_die_ref die)
{
- dw_loc_list_ref curr = list_head;
+ unsigned long abbrev_id;
+ unsigned int n_alloc;
+ dw_die_ref c;
+ dw_attr_ref a;
+ unsigned ix;
- ASM_OUTPUT_LABEL (asm_out_file, list_head->ll_symbol);
+ /* Scan the DIE references, and mark as external any that refer to
+ DIEs from other CUs (i.e. those which are not marked). */
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ if (AT_class (a) == dw_val_class_die_ref
+ && AT_ref (a)->die_mark == 0)
+ {
+ gcc_assert (dwarf_version >= 4 || AT_ref (a)->die_id.die_symbol);
+ set_AT_ref_external (a, 1);
+ }
- /* Walk the location list, and output each range + expression. */
- for (curr = list_head; curr != NULL; curr = curr->dw_loc_next)
+ for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
{
- unsigned long size;
- /* Don't output an entry that starts and ends at the same address. */
- if (strcmp (curr->begin, curr->end) == 0)
+ dw_die_ref abbrev = abbrev_die_table[abbrev_id];
+ dw_attr_ref die_a, abbrev_a;
+ unsigned ix;
+ bool ok = true;
+
+ if (abbrev->die_tag != die->die_tag)
continue;
- if (!have_multiple_function_sections)
+ if ((abbrev->die_child != NULL) != (die->die_child != NULL))
+ continue;
+
+ if (VEC_length (dw_attr_node, abbrev->die_attr)
+ != VEC_length (dw_attr_node, die->die_attr))
+ continue;
+
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, die_a); ix++)
{
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->begin, curr->section,
- "Location list begin address (%s)",
- list_head->ll_symbol);
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->end, curr->section,
- "Location list end address (%s)",
- list_head->ll_symbol);
+ abbrev_a = VEC_index (dw_attr_node, abbrev->die_attr, ix);
+ if ((abbrev_a->dw_attr != die_a->dw_attr)
+ || (value_format (abbrev_a) != value_format (die_a)))
+ {
+ ok = false;
+ break;
+ }
}
- else
+ if (ok)
+ break;
+ }
+
+ if (abbrev_id >= abbrev_die_table_in_use)
+ {
+ if (abbrev_die_table_in_use >= abbrev_die_table_allocated)
{
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->begin,
- "Location list begin address (%s)",
- list_head->ll_symbol);
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->end,
- "Location list end address (%s)",
- list_head->ll_symbol);
- }
- size = size_of_locs (curr->expr);
+ n_alloc = abbrev_die_table_allocated + ABBREV_DIE_TABLE_INCREMENT;
+ abbrev_die_table = GGC_RESIZEVEC (dw_die_ref, abbrev_die_table,
+ n_alloc);
- /* Output the block length for this list of location operations. */
- gcc_assert (size <= 0xffff);
- dw2_asm_output_data (2, size, "%s", "Location expression size");
+ memset (&abbrev_die_table[abbrev_die_table_allocated], 0,
+ (n_alloc - abbrev_die_table_allocated) * sizeof (dw_die_ref));
+ abbrev_die_table_allocated = n_alloc;
+ }
- output_loc_sequence (curr->expr);
+ ++abbrev_die_table_in_use;
+ abbrev_die_table[abbrev_id] = die;
}
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
- "Location list terminator begin (%s)",
- list_head->ll_symbol);
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
- "Location list terminator end (%s)",
- list_head->ll_symbol);
+ die->die_abbrev = abbrev_id;
+ FOR_EACH_CHILD (die, c, build_abbrev_table (c));
}
+\f
+/* Return the power-of-two number of bytes necessary to represent VALUE. */
-/* Output the DIE and its attributes. Called recursively to generate
- the definitions of each child DIE. */
+static int
+constant_size (unsigned HOST_WIDE_INT value)
+{
+ int log;
-static void
-output_die (dw_die_ref die)
+ if (value == 0)
+ log = 0;
+ else
+ log = floor_log2 (value);
+
+ log = log / 8;
+ log = 1 << (floor_log2 (log) + 1);
+
+ return log;
+}
+
+/* Return the size of a DIE as it is represented in the
+ .debug_info section. */
+
+static unsigned long
+size_of_die (dw_die_ref die)
{
+ unsigned long size = 0;
dw_attr_ref a;
- dw_die_ref c;
- unsigned long size;
unsigned ix;
- /* If someone in another CU might refer to us, set up a symbol for
- them to point to. */
- if (die->die_symbol)
- output_die_symbol (die);
-
- dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (0x%lx) %s)",
- (unsigned long)die->die_offset,
- dwarf_tag_name (die->die_tag));
-
+ size += size_of_uleb128 (die->die_abbrev);
for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
{
- const char *name = dwarf_attr_name (a->dw_attr);
-
switch (AT_class (a))
{
case dw_val_class_addr:
- dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name);
+ size += DWARF2_ADDR_SIZE;
break;
-
case dw_val_class_offset:
- dw2_asm_output_data (DWARF_OFFSET_SIZE, a->dw_attr_val.v.val_offset,
- "%s", name);
+ size += DWARF_OFFSET_SIZE;
break;
-
- case dw_val_class_range_list:
+ case dw_val_class_loc:
{
- char *p = strchr (ranges_section_label, '\0');
+ unsigned long lsize = size_of_locs (AT_loc (a));
- sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX,
- a->dw_attr_val.v.val_offset);
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label,
- debug_ranges_section, "%s", name);
- *p = '\0';
+ /* Block length. */
+ size += constant_size (lsize);
+ size += lsize;
}
break;
-
- case dw_val_class_loc:
- size = size_of_locs (AT_loc (a));
-
- /* Output the block length for this list of location operations. */
- dw2_asm_output_data (constant_size (size), size, "%s", name);
-
- output_loc_sequence (AT_loc (a));
+ case dw_val_class_loc_list:
+ size += DWARF_OFFSET_SIZE;
+ break;
+ case dw_val_class_range_list:
+ size += DWARF_OFFSET_SIZE;
break;
-
case dw_val_class_const:
- /* ??? It would be slightly more efficient to use a scheme like is
- used for unsigned constants below, but gdb 4.x does not sign
- extend. Gdb 5.x does sign extend. */
- dw2_asm_output_data_sleb128 (AT_int (a), "%s", name);
+ size += size_of_sleb128 (AT_int (a));
break;
-
case dw_val_class_unsigned_const:
- dw2_asm_output_data (constant_size (AT_unsigned (a)),
- AT_unsigned (a), "%s", name);
+ size += constant_size (AT_unsigned (a));
break;
-
- case dw_val_class_long_long:
- {
- unsigned HOST_WIDE_INT first, second;
-
- dw2_asm_output_data (1,
- 2 * HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
- "%s", name);
-
- if (WORDS_BIG_ENDIAN)
- {
- first = a->dw_attr_val.v.val_long_long.hi;
- second = a->dw_attr_val.v.val_long_long.low;
- }
- else
- {
- first = a->dw_attr_val.v.val_long_long.low;
- second = a->dw_attr_val.v.val_long_long.hi;
- }
-
- dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
- first, "long long constant");
- dw2_asm_output_data (HOST_BITS_PER_LONG / HOST_BITS_PER_CHAR,
- second, NULL);
- }
+ case dw_val_class_const_double:
+ size += 2 * HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR;
+ if (HOST_BITS_PER_WIDE_INT >= 64)
+ size++; /* block */
break;
-
case dw_val_class_vec:
- {
- unsigned int elt_size = a->dw_attr_val.v.val_vec.elt_size;
- unsigned int len = a->dw_attr_val.v.val_vec.length;
- unsigned int i;
- unsigned char *p;
-
- dw2_asm_output_data (constant_size (len * elt_size),
- len * elt_size, "%s", name);
- if (elt_size > sizeof (HOST_WIDE_INT))
- {
- elt_size /= 2;
- len *= 2;
- }
- for (i = 0, p = a->dw_attr_val.v.val_vec.array;
- i < len;
- i++, p += elt_size)
- dw2_asm_output_data (elt_size, extract_int (p, elt_size),
- "fp or vector constant word %u", i);
- break;
- }
-
- case dw_val_class_flag:
- dw2_asm_output_data (1, AT_flag (a), "%s", name);
+ size += constant_size (a->dw_attr_val.v.val_vec.length
+ * a->dw_attr_val.v.val_vec.elt_size)
+ + a->dw_attr_val.v.val_vec.length
+ * a->dw_attr_val.v.val_vec.elt_size; /* block */
break;
-
- case dw_val_class_loc_list:
- {
- char *sym = AT_loc_list (a)->ll_symbol;
-
- gcc_assert (sym);
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, sym, debug_loc_section,
- "%s", name);
- }
+ case dw_val_class_flag:
+ size += 1;
break;
-
case dw_val_class_die_ref:
if (AT_ref_external (a))
{
- char *sym = AT_ref (a)->die_symbol;
- int size;
-
- gcc_assert (sym);
-
- /* 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 == 2)
- size = DWARF2_ADDR_SIZE;
+ /* In DWARF4, we use DW_FORM_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)
+ size += DWARF_TYPE_SIGNATURE_SIZE;
+ else if (dwarf_version == 2)
+ size += DWARF2_ADDR_SIZE;
else
- size = DWARF_OFFSET_SIZE;
- dw2_asm_output_offset (size, sym, debug_info_section, "%s", name);
+ size += DWARF_OFFSET_SIZE;
}
else
- {
- gcc_assert (AT_ref (a)->die_offset);
- dw2_asm_output_data (DWARF_OFFSET_SIZE, AT_ref (a)->die_offset,
- "%s", name);
- }
+ size += DWARF_OFFSET_SIZE;
break;
-
case dw_val_class_fde_ref:
- {
- char l1[20];
-
- ASM_GENERATE_INTERNAL_LABEL (l1, FDE_LABEL,
- a->dw_attr_val.v.val_fde_index * 2);
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, l1, debug_frame_section,
- "%s", name);
- }
+ size += DWARF_OFFSET_SIZE;
break;
-
case dw_val_class_lbl_id:
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name);
+ size += DWARF2_ADDR_SIZE;
break;
-
case dw_val_class_lineptr:
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
- debug_line_section, "%s", name);
- break;
-
case dw_val_class_macptr:
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
- debug_macinfo_section, "%s", name);
+ size += DWARF_OFFSET_SIZE;
break;
-
case dw_val_class_str:
if (AT_string_form (a) == DW_FORM_strp)
- dw2_asm_output_offset (DWARF_OFFSET_SIZE,
- a->dw_attr_val.v.val_str->label,
- debug_str_section,
- "%s: \"%s\"", name, AT_string (a));
+ size += DWARF_OFFSET_SIZE;
else
- dw2_asm_output_nstring (AT_string (a), -1, "%s", name);
+ size += strlen (a->dw_attr_val.v.val_str->str) + 1;
break;
-
case dw_val_class_file:
- {
- int f = maybe_emit_file (a->dw_attr_val.v.val_file);
-
- dw2_asm_output_data (constant_size (f), f, "%s (%s)", name,
- a->dw_attr_val.v.val_file->filename);
- break;
- }
-
+ size += constant_size (maybe_emit_file (a->dw_attr_val.v.val_file));
+ break;
+ case dw_val_class_data8:
+ size += 8;
+ break;
default:
gcc_unreachable ();
}
}
- FOR_EACH_CHILD (die, c, output_die (c));
-
- /* Add null byte to terminate sibling list. */
- if (die->die_child != NULL)
- dw2_asm_output_data (1, 0, "end of children of DIE 0x%lx",
- (unsigned long) die->die_offset);
-}
-
-/* Output the compilation unit that appears at the beginning of the
- .debug_info section, and precedes the DIE descriptions. */
-
-static void
-output_compilation_unit_header (void)
-{
- if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
- dw2_asm_output_data (4, 0xffffffff,
- "Initial length escape value indicating 64-bit DWARF extension");
- dw2_asm_output_data (DWARF_OFFSET_SIZE,
- next_die_offset - DWARF_INITIAL_LENGTH_SIZE,
- "Length of Compilation Unit Info");
- dw2_asm_output_data (2, dwarf_version, "DWARF version number");
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, abbrev_section_label,
- debug_abbrev_section,
- "Offset Into Abbrev. Section");
- dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
+ return size;
}
-/* Output the compilation unit DIE and its children. */
+/* Size the debugging information associated with a given DIE. Visits the
+ DIE's children recursively. Updates the global variable next_die_offset, on
+ each time through. Uses the current value of next_die_offset to update the
+ die_offset field in each DIE. */
static void
-output_comp_unit (dw_die_ref die, int output_if_empty)
+calc_die_sizes (dw_die_ref die)
{
- const char *secname;
- char *oldsym, *tmp;
-
- /* Unless we are outputting main CU, we may throw away empty ones. */
- if (!output_if_empty && die->die_child == NULL)
- return;
-
- /* Even if there are no children of this DIE, we must output the information
- about the compilation unit. Otherwise, on an empty translation unit, we
- will generate a present, but empty, .debug_info section. IRIX 6.5 `nm'
- will then complain when examining the file. First mark all the DIEs in
- this CU so we know which get local refs. */
- mark_dies (die);
-
- build_abbrev_table (die);
-
- /* Initialize the beginning DIE offset - and calculate sizes/offsets. */
- next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
- calc_die_sizes (die);
-
- oldsym = die->die_symbol;
- if (oldsym)
- {
- tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
-
- sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
- secname = tmp;
- die->die_symbol = NULL;
- switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
- }
- else
- switch_to_section (debug_info_section);
-
- /* Output debugging information. */
- output_compilation_unit_header ();
- output_die (die);
+ dw_die_ref c;
- /* Leave the marks on the main CU, so we can check them in
- output_pubnames. */
- if (oldsym)
- {
- unmark_dies (die);
- die->die_symbol = oldsym;
- }
-}
+ die->die_offset = next_die_offset;
+ next_die_offset += size_of_die (die);
-/* Return the DWARF2/3 pubname associated with a decl. */
+ FOR_EACH_CHILD (die, c, calc_die_sizes (c));
-static const char *
-dwarf2_name (tree decl, int scope)
-{
- return lang_hooks.dwarf_name (decl, scope ? 1 : 0);
+ if (die->die_child != NULL)
+ /* Count the null byte used to terminate sibling lists. */
+ next_die_offset += 1;
}
-/* Add a new entry to .debug_pubnames if appropriate. */
+/* Set the marks for a die and its children. We do this so
+ that we know whether or not a reference needs to use FORM_ref_addr; only
+ DIEs in the same CU will be marked. We used to clear out the offset
+ and use that as the flag, but ran into ordering problems. */
static void
-add_pubname_string (const char *str, dw_die_ref die)
+mark_dies (dw_die_ref die)
{
- pubname_entry e;
+ dw_die_ref c;
- e.die = die;
- e.name = xstrdup (str);
- VEC_safe_push (pubname_entry, gc, pubname_table, &e);
-}
+ gcc_assert (!die->die_mark);
-static void
-add_pubname (tree decl, dw_die_ref die)
-{
- if (TREE_PUBLIC (decl))
- add_pubname_string (dwarf2_name (decl, 1), die);
+ die->die_mark = 1;
+ FOR_EACH_CHILD (die, c, mark_dies (c));
}
-/* Add a new entry to .debug_pubtypes if appropriate. */
+/* Clear the marks for a die and its children. */
static void
-add_pubtype (tree decl, dw_die_ref die)
+unmark_dies (dw_die_ref die)
{
- pubname_entry e;
+ dw_die_ref c;
- e.name = NULL;
- if ((TREE_PUBLIC (decl)
- || die->die_parent == comp_unit_die)
- && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl)))
- {
- e.die = die;
- if (TYPE_P (decl))
- {
- if (TYPE_NAME (decl))
- {
- if (TREE_CODE (TYPE_NAME (decl)) == IDENTIFIER_NODE)
- e.name = IDENTIFIER_POINTER (TYPE_NAME (decl));
- else if (TREE_CODE (TYPE_NAME (decl)) == TYPE_DECL
- && DECL_NAME (TYPE_NAME (decl)))
- e.name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl)));
- else
- e.name = xstrdup ((const char *) get_AT_string (die, DW_AT_name));
- }
- }
- else
- e.name = xstrdup (dwarf2_name (decl, 1));
+ if (dwarf_version < 4)
+ gcc_assert (die->die_mark);
- /* If we don't have a name for the type, there's no point in adding
- it to the table. */
- if (e.name && e.name[0] != '\0')
- VEC_safe_push (pubname_entry, gc, pubtype_table, &e);
- }
+ die->die_mark = 0;
+ FOR_EACH_CHILD (die, c, unmark_dies (c));
}
-/* Output the public names table used to speed up access to externally
- visible names; or the public types table used to find type definitions. */
+/* Clear the marks for a die, its children and referred dies. */
static void
-output_pubnames (VEC (pubname_entry, gc) * names)
+unmark_all_dies (dw_die_ref die)
{
- unsigned i;
- unsigned long pubnames_length = size_of_pubnames (names);
- pubname_ref pub;
-
- if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
- dw2_asm_output_data (4, 0xffffffff,
- "Initial length escape value indicating 64-bit DWARF extension");
- if (names == pubname_table)
- dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
- "Length of Public Names Info");
- else
- dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
- "Length of Public Type Names Info");
- /* Version number for pubnames/pubtypes is still 2, even in DWARF3. */
- dw2_asm_output_data (2, 2, "DWARF Version");
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
- debug_info_section,
- "Offset of Compilation Unit Info");
- dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset,
- "Compilation Unit Length");
-
- for (i = 0; VEC_iterate (pubname_entry, names, i, pub); i++)
- {
- /* We shouldn't see pubnames for DIEs outside of the main CU. */
- if (names == pubname_table)
- gcc_assert (pub->die->die_mark);
+ dw_die_ref c;
+ dw_attr_ref a;
+ unsigned ix;
- if (names != pubtype_table
- || pub->die->die_offset != 0
- || !flag_eliminate_unused_debug_types)
- {
- dw2_asm_output_data (DWARF_OFFSET_SIZE, pub->die->die_offset,
- "DIE offset");
+ if (!die->die_mark)
+ return;
+ die->die_mark = 0;
- dw2_asm_output_nstring (pub->name, -1, "external name");
- }
- }
+ FOR_EACH_CHILD (die, c, unmark_all_dies (c));
- dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL);
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ if (AT_class (a) == dw_val_class_die_ref)
+ unmark_all_dies (AT_ref (a));
}
-/* Add a new entry to .debug_aranges if appropriate. */
+/* Return the size of the .debug_pubnames or .debug_pubtypes table
+ generated for the compilation unit. */
-static void
-add_arange (tree decl, dw_die_ref die)
+static unsigned long
+size_of_pubnames (VEC (pubname_entry, gc) * names)
{
- if (! DECL_SECTION_NAME (decl))
- return;
+ unsigned long size;
+ unsigned i;
+ pubname_ref p;
- 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));
- }
+ size = DWARF_PUBNAMES_HEADER_SIZE;
+ for (i = 0; VEC_iterate (pubname_entry, names, i, p); i++)
+ if (names != pubtype_table
+ || p->die->die_offset != 0
+ || !flag_eliminate_unused_debug_types)
+ size += strlen (p->name) + DWARF_OFFSET_SIZE + 1;
- arange_table[arange_table_in_use++] = die;
+ size += DWARF_OFFSET_SIZE;
+ return size;
}
-/* 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. */
+/* Return the size of the information in the .debug_aranges section. */
-static void
-output_aranges (void)
+static unsigned long
+size_of_aranges (void)
{
- unsigned i;
- unsigned long aranges_length = size_of_aranges ();
+ unsigned long size;
- if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
- dw2_asm_output_data (4, 0xffffffff,
- "Initial length escape value indicating 64-bit DWARF extension");
- dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length,
- "Length of Address Ranges Info");
- /* Version number for aranges is still 2, even in DWARF3. */
- dw2_asm_output_data (2, 2, "DWARF Version");
- dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
- debug_info_section,
- "Offset of Compilation Unit Info");
- dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address");
- dw2_asm_output_data (1, 0, "Size of Segment Descriptor");
+ size = DWARF_ARANGES_HEADER_SIZE;
- /* We need to align to twice the pointer size here. */
- if (DWARF_ARANGES_PAD_SIZE)
- {
- /* Pad using a 2 byte words so that padding is correct for any
- pointer size. */
- dw2_asm_output_data (2, 0, "Pad to %d byte boundary",
- 2 * DWARF2_ADDR_SIZE);
- for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2)
- dw2_asm_output_data (2, 0, NULL);
- }
+ /* Count the address/length pair for this compilation unit. */
+ if (text_section_used)
+ size += 2 * DWARF2_ADDR_SIZE;
+ if (cold_text_section_used)
+ size += 2 * DWARF2_ADDR_SIZE;
+ size += 2 * DWARF2_ADDR_SIZE * arange_table_in_use;
- /* It is necessary not to output these entries if the sections were
- not used; if the sections were not used, the length will be 0 and
- the address may end up as 0 if the section is discarded by ld
- --gc-sections, leaving an invalid (0, 0) entry that can be
- confused with the terminator. */
- if (text_section_used)
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_section_label, "Address");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
- text_section_label, "Length");
- }
- if (cold_text_section_used)
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, cold_text_section_label,
- "Address");
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, cold_end_label,
- cold_text_section_label, "Length");
- }
+ /* Count the two zero words used to terminated the address range table. */
+ size += 2 * DWARF2_ADDR_SIZE;
+ return size;
+}
+\f
+/* Select the encoding of an attribute value. */
- for (i = 0; i < arange_table_in_use; i++)
+static enum dwarf_form
+value_format (dw_attr_ref a)
+{
+ switch (a->dw_attr_val.val_class)
{
- dw_die_ref die = arange_table[i];
-
- /* We shouldn't see aranges for DIEs outside of the main CU. */
- gcc_assert (die->die_mark);
-
- if (die->die_tag == DW_TAG_subprogram)
+ case dw_val_class_addr:
+ /* Only very few attributes allow DW_FORM_addr. */
+ switch (a->dw_attr)
{
- 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");
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_entry_pc:
+ case DW_AT_trampoline:
+ return DW_FORM_addr;
+ default:
+ break;
+ }
+ switch (DWARF2_ADDR_SIZE)
+ {
+ case 1:
+ return DW_FORM_data1;
+ case 2:
+ return DW_FORM_data2;
+ case 4:
+ return DW_FORM_data4;
+ case 8:
+ return DW_FORM_data8;
+ default:
+ gcc_unreachable ();
+ }
+ case dw_val_class_range_list:
+ case dw_val_class_offset:
+ case dw_val_class_loc_list:
+ switch (DWARF_OFFSET_SIZE)
+ {
+ case 4:
+ return DW_FORM_data4;
+ case 8:
+ return DW_FORM_data8;
+ default:
+ gcc_unreachable ();
+ }
+ case dw_val_class_loc:
+ switch (constant_size (size_of_locs (AT_loc (a))))
+ {
+ case 1:
+ return DW_FORM_block1;
+ case 2:
+ return DW_FORM_block2;
+ default:
+ gcc_unreachable ();
+ }
+ case dw_val_class_const:
+ return DW_FORM_sdata;
+ case dw_val_class_unsigned_const:
+ switch (constant_size (AT_unsigned (a)))
+ {
+ case 1:
+ return DW_FORM_data1;
+ case 2:
+ return DW_FORM_data2;
+ case 4:
+ return DW_FORM_data4;
+ case 8:
+ return DW_FORM_data8;
+ default:
+ gcc_unreachable ();
+ }
+ case dw_val_class_const_double:
+ switch (HOST_BITS_PER_WIDE_INT)
+ {
+ case 8:
+ return DW_FORM_data2;
+ case 16:
+ return DW_FORM_data4;
+ case 32:
+ return DW_FORM_data8;
+ case 64:
+ default:
+ return DW_FORM_block1;
+ }
+ case dw_val_class_vec:
+ switch (constant_size (a->dw_attr_val.v.val_vec.length
+ * a->dw_attr_val.v.val_vec.elt_size))
+ {
+ case 1:
+ return DW_FORM_block1;
+ case 2:
+ return DW_FORM_block2;
+ case 4:
+ return DW_FORM_block4;
+ default:
+ gcc_unreachable ();
}
+ case dw_val_class_flag:
+ 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;
else
+ return DW_FORM_ref;
+ case dw_val_class_fde_ref:
+ return DW_FORM_data;
+ case dw_val_class_lbl_id:
+ return DW_FORM_addr;
+ case dw_val_class_lineptr:
+ case dw_val_class_macptr:
+ return DW_FORM_data;
+ case dw_val_class_str:
+ return AT_string_form (a);
+ case dw_val_class_file:
+ switch (constant_size (maybe_emit_file (a->dw_attr_val.v.val_file)))
{
- /* 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);
+ case 1:
+ return DW_FORM_data1;
+ case 2:
+ return DW_FORM_data2;
+ case 4:
+ return DW_FORM_data4;
+ default:
+ gcc_unreachable ();
+ }
- loc = AT_loc (a);
- gcc_assert (loc->dw_loc_opc == DW_OP_addr);
+ case dw_val_class_data8:
+ return DW_FORM_data8;
- 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");
- }
+ default:
+ gcc_unreachable ();
}
+}
- /* Output the terminator words. */
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+/* Output the encoding of an attribute value. */
+
+static void
+output_value_format (dw_attr_ref a)
+{
+ enum dwarf_form form = value_format (a);
+
+ dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form));
}
-/* Add a new entry to .debug_ranges. Return the offset at which it
- was placed. */
+/* Output the .debug_abbrev section which defines the DIE abbreviation
+ table. */
-static unsigned int
-add_ranges_num (int num)
+static void
+output_abbrev_section (void)
{
- unsigned int in_use = ranges_table_in_use;
+ unsigned long abbrev_id;
- if (in_use == ranges_table_allocated)
+ for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
{
- ranges_table_allocated += RANGES_TABLE_INCREMENT;
- ranges_table = GGC_RESIZEVEC (struct dw_ranges_struct, ranges_table,
- ranges_table_allocated);
- memset (ranges_table + ranges_table_in_use, 0,
- RANGES_TABLE_INCREMENT * sizeof (struct dw_ranges_struct));
- }
+ dw_die_ref abbrev = abbrev_die_table[abbrev_id];
+ unsigned ix;
+ dw_attr_ref a_attr;
- ranges_table[in_use].num = num;
- ranges_table_in_use = in_use + 1;
+ dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)");
+ dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
+ dwarf_tag_name (abbrev->die_tag));
- return in_use * 2 * DWARF2_ADDR_SIZE;
-}
+ if (abbrev->die_child != NULL)
+ dw2_asm_output_data (1, DW_children_yes, "DW_children_yes");
+ else
+ dw2_asm_output_data (1, DW_children_no, "DW_children_no");
-/* Add a new entry to .debug_ranges corresponding to a block, or a
- range terminator if BLOCK is NULL. */
+ for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr);
+ ix++)
+ {
+ dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)",
+ dwarf_attr_name (a_attr->dw_attr));
+ output_value_format (a_attr);
+ }
-static unsigned int
-add_ranges (const_tree block)
-{
- return add_ranges_num (block ? BLOCK_NUMBER (block) : 0);
+ dw2_asm_output_data (1, 0, NULL);
+ dw2_asm_output_data (1, 0, NULL);
+ }
+
+ /* Terminate the table. */
+ dw2_asm_output_data (1, 0, NULL);
}
-/* Add a new entry to .debug_ranges corresponding to a pair of
- labels. */
+/* Output a symbol we can use to refer to this DIE from another CU. */
-static unsigned int
-add_ranges_by_labels (const char *begin, const char *end)
+static inline void
+output_die_symbol (dw_die_ref die)
{
- unsigned int in_use = ranges_by_label_in_use;
+ char *sym = die->die_id.die_symbol;
- if (in_use == ranges_by_label_allocated)
- {
- ranges_by_label_allocated += RANGES_TABLE_INCREMENT;
- ranges_by_label = GGC_RESIZEVEC (struct dw_ranges_by_label_struct,
- ranges_by_label,
- ranges_by_label_allocated);
- memset (ranges_by_label + ranges_by_label_in_use, 0,
- RANGES_TABLE_INCREMENT
- * sizeof (struct dw_ranges_by_label_struct));
- }
+ if (sym == 0)
+ return;
- ranges_by_label[in_use].begin = begin;
- ranges_by_label[in_use].end = end;
- ranges_by_label_in_use = in_use + 1;
+ if (strncmp (sym, DIE_LABEL_PREFIX, sizeof (DIE_LABEL_PREFIX) - 1) == 0)
+ /* We make these global, not weak; if the target doesn't support
+ .linkonce, it doesn't support combining the sections, so debugging
+ will break. */
+ targetm.asm_out.globalize_label (asm_out_file, sym);
- return add_ranges_num (-(int)in_use - 1);
+ ASM_OUTPUT_LABEL (asm_out_file, sym);
}
-static void
-output_ranges (void)
-{
- unsigned i;
- static const char *const start_fmt = "Offset 0x%x";
- const char *fmt = start_fmt;
+/* Return a new location list, given the begin and end range, and the
+ expression. */
- for (i = 0; i < ranges_table_in_use; i++)
- {
- int block_num = ranges_table[i].num;
+static inline dw_loc_list_ref
+new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
+ const char *section)
+{
+ dw_loc_list_ref retlist = GGC_CNEW (dw_loc_list_node);
- if (block_num > 0)
- {
- char blabel[MAX_ARTIFICIAL_LABEL_BYTES];
- char elabel[MAX_ARTIFICIAL_LABEL_BYTES];
+ retlist->begin = begin;
+ retlist->end = end;
+ retlist->expr = expr;
+ retlist->section = section;
- ASM_GENERATE_INTERNAL_LABEL (blabel, BLOCK_BEGIN_LABEL, block_num);
- ASM_GENERATE_INTERNAL_LABEL (elabel, BLOCK_END_LABEL, block_num);
+ return retlist;
+}
- /* If all code is in the text section, then the compilation
- unit base address defaults to DW_AT_low_pc, which is the
- base of the text section. */
- if (!have_multiple_function_sections)
- {
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
- text_section_label,
- fmt, i * 2 * DWARF2_ADDR_SIZE);
- dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
- text_section_label, NULL);
- }
+/* Generate a new internal symbol for this location list node, if it
+ hasn't got one yet. */
- /* Otherwise, the compilation unit base address is zero,
- which allows us to use absolute addresses, and not worry
- about whether the target supports cross-section
- arithmetic. */
- else
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, blabel,
- fmt, i * 2 * DWARF2_ADDR_SIZE);
- dw2_asm_output_addr (DWARF2_ADDR_SIZE, elabel, NULL);
- }
+static inline void
+gen_llsym (dw_loc_list_ref list)
+{
+ gcc_assert (!list->ll_symbol);
+ list->ll_symbol = gen_internal_sym ("LLST");
+}
- fmt = NULL;
- }
+/* Output the location list given to us. */
- /* Negative block_num stands for an index into ranges_by_label. */
- else if (block_num < 0)
- {
- int lab_idx = - block_num - 1;
+static void
+output_loc_list (dw_loc_list_ref list_head)
+{
+ dw_loc_list_ref curr = list_head;
- if (!have_multiple_function_sections)
- {
- gcc_unreachable ();
-#if 0
- /* If we ever use add_ranges_by_labels () for a single
- function section, all we have to do is to take out
- the #if 0 above. */
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- ranges_by_label[lab_idx].begin,
- text_section_label,
- fmt, i * 2 * DWARF2_ADDR_SIZE);
- dw2_asm_output_delta (DWARF2_ADDR_SIZE,
- ranges_by_label[lab_idx].end,
- text_section_label, NULL);
-#endif
- }
- else
- {
- dw2_asm_output_addr (DWARF2_ADDR_SIZE,
- ranges_by_label[lab_idx].begin,
- fmt, i * 2 * DWARF2_ADDR_SIZE);
- dw2_asm_output_addr (DWARF2_ADDR_SIZE,
- ranges_by_label[lab_idx].end,
- NULL);
- }
+ ASM_OUTPUT_LABEL (asm_out_file, list_head->ll_symbol);
+
+ /* Walk the location list, and output each range + expression. */
+ for (curr = list_head; curr != NULL; curr = curr->dw_loc_next)
+ {
+ unsigned long size;
+ /* Don't output an entry that starts and ends at the same address. */
+ if (strcmp (curr->begin, curr->end) == 0)
+ continue;
+ if (!have_multiple_function_sections)
+ {
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->begin, curr->section,
+ "Location list begin address (%s)",
+ list_head->ll_symbol);
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->end, curr->section,
+ "Location list end address (%s)",
+ list_head->ll_symbol);
}
else
{
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
- dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
- fmt = start_fmt;
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->begin,
+ "Location list begin address (%s)",
+ list_head->ll_symbol);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, curr->end,
+ "Location list end address (%s)",
+ list_head->ll_symbol);
}
+ size = size_of_locs (curr->expr);
+
+ /* Output the block length for this list of location operations. */
+ gcc_assert (size <= 0xffff);
+ dw2_asm_output_data (2, size, "%s", "Location expression size");
+
+ output_loc_sequence (curr->expr);
}
+
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
+ "Location list terminator begin (%s)",
+ list_head->ll_symbol);
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
+ "Location list terminator end (%s)",
+ list_head->ll_symbol);
}
-/* Data structure containing information about input files. */
-struct file_info
-{
- const char *path; /* Complete file name. */
- const char *fname; /* File name part. */
- int length; /* Length of entire string. */
- struct dwarf_file_data * file_idx; /* Index in input file table. */
- int dir_idx; /* Index in directory table. */
-};
+/* Output a type signature. */
-/* Data structure containing information about directories with source
- files. */
-struct dir_info
+static inline void
+output_signature (const char *sig, const char *name)
{
- const char *path; /* Path including directory name. */
- int length; /* Path length. */
- int prefix; /* Index of directory entry which is a prefix. */
- int count; /* Number of files in this directory. */
- int dir_idx; /* Index of directory used as base. */
-};
+ int i;
-/* Callback function for file_info comparison. We sort by looking at
- the directories in the path. */
+ for (i = 0; i < DWARF_TYPE_SIGNATURE_SIZE; i++)
+ dw2_asm_output_data (1, sig[i], i == 0 ? "%s" : NULL, name);
+}
-static int
-file_info_cmp (const void *p1, const void *p2)
+/* Output the DIE and its attributes. Called recursively to generate
+ the definitions of each child DIE. */
+
+static void
+output_die (dw_die_ref die)
{
- const struct file_info *const s1 = (const struct file_info *) p1;
- const struct file_info *const s2 = (const struct file_info *) p2;
- const unsigned char *cp1;
- const unsigned char *cp2;
+ dw_attr_ref a;
+ dw_die_ref c;
+ unsigned long size;
+ unsigned ix;
- /* Take care of file names without directories. We need to make sure that
- we return consistent values to qsort since some will get confused if
- we return the same value when identical operands are passed in opposite
- orders. So if neither has a directory, return 0 and otherwise return
- 1 or -1 depending on which one has the directory. */
- if ((s1->path == s1->fname || s2->path == s2->fname))
- return (s2->path == s2->fname) - (s1->path == s1->fname);
+ /* 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)
+ output_die_symbol (die);
- cp1 = (const unsigned char *) s1->path;
- cp2 = (const unsigned char *) s2->path;
+ dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (0x%lx) %s)",
+ (unsigned long)die->die_offset,
+ dwarf_tag_name (die->die_tag));
- while (1)
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
{
- ++cp1;
- ++cp2;
- /* Reached the end of the first path? If so, handle like above. */
- if ((cp1 == (const unsigned char *) s1->fname)
- || (cp2 == (const unsigned char *) s2->fname))
- return ((cp2 == (const unsigned char *) s2->fname)
- - (cp1 == (const unsigned char *) s1->fname));
+ const char *name = dwarf_attr_name (a->dw_attr);
- /* Character of current path component the same? */
- else if (*cp1 != *cp2)
- return *cp1 - *cp2;
- }
-}
+ switch (AT_class (a))
+ {
+ case dw_val_class_addr:
+ dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name);
+ break;
-struct file_name_acquire_data
-{
- struct file_info *files;
- int used_files;
- int max_files;
-};
+ case dw_val_class_offset:
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, a->dw_attr_val.v.val_offset,
+ "%s", name);
+ break;
-/* Traversal function for the hash table. */
+ case dw_val_class_range_list:
+ {
+ char *p = strchr (ranges_section_label, '\0');
-static int
-file_name_acquire (void ** slot, void *data)
-{
- struct file_name_acquire_data *fnad = (struct file_name_acquire_data *) data;
- struct dwarf_file_data *d = (struct dwarf_file_data *) *slot;
- struct file_info *fi;
- const char *f;
+ sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX,
+ a->dw_attr_val.v.val_offset);
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label,
+ debug_ranges_section, "%s", name);
+ *p = '\0';
+ }
+ break;
- gcc_assert (fnad->max_files >= d->emitted_number);
+ case dw_val_class_loc:
+ size = size_of_locs (AT_loc (a));
- if (! d->emitted_number)
- return 1;
+ /* Output the block length for this list of location operations. */
+ dw2_asm_output_data (constant_size (size), size, "%s", name);
- gcc_assert (fnad->max_files != fnad->used_files);
+ output_loc_sequence (AT_loc (a));
+ break;
- fi = fnad->files + fnad->used_files++;
+ case dw_val_class_const:
+ /* ??? It would be slightly more efficient to use a scheme like is
+ used for unsigned constants below, but gdb 4.x does not sign
+ extend. Gdb 5.x does sign extend. */
+ dw2_asm_output_data_sleb128 (AT_int (a), "%s", name);
+ break;
- /* Skip all leading "./". */
- f = d->filename;
- while (f[0] == '.' && IS_DIR_SEPARATOR (f[1]))
- f += 2;
+ case dw_val_class_unsigned_const:
+ dw2_asm_output_data (constant_size (AT_unsigned (a)),
+ AT_unsigned (a), "%s", name);
+ break;
- /* Create a new array entry. */
- fi->path = f;
- fi->length = strlen (f);
- fi->file_idx = d;
+ case dw_val_class_const_double:
+ {
+ unsigned HOST_WIDE_INT first, second;
- /* Search for the file name part. */
- f = strrchr (f, DIR_SEPARATOR);
-#if defined (DIR_SEPARATOR_2)
- {
- char *g = strrchr (fi->path, DIR_SEPARATOR_2);
+ if (HOST_BITS_PER_WIDE_INT >= 64)
+ dw2_asm_output_data (1,
+ 2 * HOST_BITS_PER_WIDE_INT
+ / HOST_BITS_PER_CHAR,
+ NULL);
- if (g != NULL)
- {
- if (f == NULL || f < g)
- f = g;
- }
- }
-#endif
-
- fi->fname = f == NULL ? fi->path : f + 1;
- return 1;
-}
-
-/* Output the directory table and the file name table. We try to minimize
- the total amount of memory needed. A heuristic is used to avoid large
- slowdowns with many input files. */
+ if (WORDS_BIG_ENDIAN)
+ {
+ first = a->dw_attr_val.v.val_double.high;
+ second = a->dw_attr_val.v.val_double.low;
+ }
+ else
+ {
+ first = a->dw_attr_val.v.val_double.low;
+ second = a->dw_attr_val.v.val_double.high;
+ }
-static void
-output_file_names (void)
-{
- struct file_name_acquire_data fnad;
- int numfiles;
- struct file_info *files;
- struct dir_info *dirs;
- int *saved;
- int *savehere;
- int *backmap;
- int ndirs;
- int idx_offset;
- int i;
- int idx;
+ dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+ first, name);
+ dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+ second, NULL);
+ }
+ break;
- if (!last_emitted_file)
- {
- dw2_asm_output_data (1, 0, "End directory table");
- dw2_asm_output_data (1, 0, "End file name table");
- return;
- }
+ case dw_val_class_vec:
+ {
+ unsigned int elt_size = a->dw_attr_val.v.val_vec.elt_size;
+ unsigned int len = a->dw_attr_val.v.val_vec.length;
+ unsigned int i;
+ unsigned char *p;
- numfiles = last_emitted_file->emitted_number;
+ dw2_asm_output_data (constant_size (len * elt_size),
+ len * elt_size, "%s", name);
+ if (elt_size > sizeof (HOST_WIDE_INT))
+ {
+ elt_size /= 2;
+ len *= 2;
+ }
+ for (i = 0, p = a->dw_attr_val.v.val_vec.array;
+ i < len;
+ i++, p += elt_size)
+ dw2_asm_output_data (elt_size, extract_int (p, elt_size),
+ "fp or vector constant word %u", i);
+ break;
+ }
- /* Allocate the various arrays we need. */
- files = XALLOCAVEC (struct file_info, numfiles);
- dirs = XALLOCAVEC (struct dir_info, numfiles);
+ case dw_val_class_flag:
+ dw2_asm_output_data (1, AT_flag (a), "%s", name);
+ break;
- fnad.files = files;
- fnad.used_files = 0;
- fnad.max_files = numfiles;
- htab_traverse (file_table, file_name_acquire, &fnad);
- gcc_assert (fnad.used_files == fnad.max_files);
+ case dw_val_class_loc_list:
+ {
+ char *sym = AT_loc_list (a)->ll_symbol;
- qsort (files, numfiles, sizeof (files[0]), file_info_cmp);
+ gcc_assert (sym);
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, sym, debug_loc_section,
+ "%s", name);
+ }
+ break;
- /* Find all the different directories used. */
- dirs[0].path = files[0].path;
- dirs[0].length = files[0].fname - files[0].path;
- dirs[0].prefix = -1;
- dirs[0].count = 1;
- dirs[0].dir_idx = 0;
- files[0].dir_idx = 0;
- ndirs = 1;
+ case dw_val_class_die_ref:
+ if (AT_ref_external (a))
+ {
+ if (dwarf_version >= 4)
+ {
+ comdat_type_node_ref type_node =
+ AT_ref (a)->die_id.die_type_node;
- for (i = 1; i < numfiles; i++)
- if (files[i].fname - files[i].path == dirs[ndirs - 1].length
- && memcmp (dirs[ndirs - 1].path, files[i].path,
- dirs[ndirs - 1].length) == 0)
- {
- /* Same directory as last entry. */
- files[i].dir_idx = ndirs - 1;
- ++dirs[ndirs - 1].count;
- }
- else
- {
- int j;
+ gcc_assert (type_node);
+ output_signature (type_node->signature, name);
+ }
+ else
+ {
+ char *sym = AT_ref (a)->die_id.die_symbol;
+ int size;
+
+ gcc_assert (sym);
+ /* 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 == 2)
+ size = DWARF2_ADDR_SIZE;
+ else
+ size = DWARF_OFFSET_SIZE;
+ dw2_asm_output_offset (size, sym, debug_info_section, "%s",
+ name);
+ }
+ }
+ else
+ {
+ gcc_assert (AT_ref (a)->die_offset);
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, AT_ref (a)->die_offset,
+ "%s", name);
+ }
+ break;
- /* This is a new directory. */
- dirs[ndirs].path = files[i].path;
- dirs[ndirs].length = files[i].fname - files[i].path;
- dirs[ndirs].count = 1;
- dirs[ndirs].dir_idx = ndirs;
- files[i].dir_idx = ndirs;
+ case dw_val_class_fde_ref:
+ {
+ char l1[20];
- /* Search for a prefix. */
- dirs[ndirs].prefix = -1;
- for (j = 0; j < ndirs; j++)
- if (dirs[j].length < dirs[ndirs].length
- && dirs[j].length > 1
- && (dirs[ndirs].prefix == -1
- || dirs[j].length > dirs[dirs[ndirs].prefix].length)
- && memcmp (dirs[j].path, dirs[ndirs].path, dirs[j].length) == 0)
- dirs[ndirs].prefix = j;
+ ASM_GENERATE_INTERNAL_LABEL (l1, FDE_LABEL,
+ a->dw_attr_val.v.val_fde_index * 2);
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, l1, debug_frame_section,
+ "%s", name);
+ }
+ break;
- ++ndirs;
- }
+ case dw_val_class_lbl_id:
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name);
+ break;
- /* Now to the actual work. We have to find a subset of the directories which
- allow expressing the file name using references to the directory table
- with the least amount of characters. We do not do an exhaustive search
- where we would have to check out every combination of every single
- possible prefix. Instead we use a heuristic which provides nearly optimal
- results in most cases and never is much off. */
- saved = XALLOCAVEC (int, ndirs);
- savehere = XALLOCAVEC (int, ndirs);
+ case dw_val_class_lineptr:
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
+ debug_line_section, "%s", name);
+ break;
- memset (saved, '\0', ndirs * sizeof (saved[0]));
- for (i = 0; i < ndirs; i++)
- {
- int j;
- int total;
+ case dw_val_class_macptr:
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, AT_lbl (a),
+ debug_macinfo_section, "%s", name);
+ break;
- /* We can always save some space for the current directory. But this
- does not mean it will be enough to justify adding the directory. */
- savehere[i] = dirs[i].length;
- total = (savehere[i] - saved[i]) * dirs[i].count;
+ case dw_val_class_str:
+ if (AT_string_form (a) == DW_FORM_strp)
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE,
+ a->dw_attr_val.v.val_str->label,
+ debug_str_section,
+ "%s: \"%s\"", name, AT_string (a));
+ else
+ dw2_asm_output_nstring (AT_string (a), -1, "%s", name);
+ break;
- for (j = i + 1; j < ndirs; j++)
- {
- savehere[j] = 0;
- if (saved[j] < dirs[i].length)
- {
- /* Determine whether the dirs[i] path is a prefix of the
- dirs[j] path. */
- int k;
+ case dw_val_class_file:
+ {
+ int f = maybe_emit_file (a->dw_attr_val.v.val_file);
- k = dirs[j].prefix;
- while (k != -1 && k != (int) i)
- k = dirs[k].prefix;
+ dw2_asm_output_data (constant_size (f), f, "%s (%s)", name,
+ a->dw_attr_val.v.val_file->filename);
+ break;
+ }
- if (k == (int) i)
- {
- /* Yes it is. We can possibly save some memory by
- writing the filenames in dirs[j] relative to
- dirs[i]. */
- savehere[j] = dirs[i].length;
- total += (savehere[j] - saved[j]) * dirs[j].count;
- }
- }
- }
+ case dw_val_class_data8:
+ {
+ int i;
- /* Check whether we can save enough to justify adding the dirs[i]
- directory. */
- if (total > dirs[i].length + 1)
- {
- /* It's worthwhile adding. */
- for (j = i; j < ndirs; j++)
- if (savehere[j] > 0)
- {
- /* Remember how much we saved for this directory so far. */
- saved[j] = savehere[j];
+ for (i = 0; i < 8; i++)
+ dw2_asm_output_data (1, a->dw_attr_val.v.val_data8[i],
+ i == 0 ? "%s" : NULL, name);
+ break;
+ }
- /* Remember the prefix directory. */
- dirs[j].dir_idx = i;
- }
+ default:
+ gcc_unreachable ();
}
}
- /* Emit the directory name table. */
- idx = 1;
- idx_offset = dirs[0].length > 0 ? 1 : 0;
- for (i = 1 - idx_offset; i < ndirs; i++)
- dw2_asm_output_nstring (dirs[i].path, dirs[i].length - 1,
- "Directory Entry: 0x%x", i + idx_offset);
+ FOR_EACH_CHILD (die, c, output_die (c));
- dw2_asm_output_data (1, 0, "End directory table");
+ /* Add null byte to terminate sibling list. */
+ if (die->die_child != NULL)
+ dw2_asm_output_data (1, 0, "end of children of DIE 0x%lx",
+ (unsigned long) die->die_offset);
+}
- /* We have to emit them in the order of emitted_number since that's
- used in the debug info generation. To do this efficiently we
- generate a back-mapping of the indices first. */
- backmap = XALLOCAVEC (int, numfiles);
- for (i = 0; i < numfiles; i++)
- backmap[files[i].file_idx->emitted_number - 1] = i;
+/* Output the compilation unit that appears at the beginning of the
+ .debug_info section, and precedes the DIE descriptions. */
- /* Now write all the file names. */
- for (i = 0; i < numfiles; i++)
- {
- int file_idx = backmap[i];
- int dir_idx = dirs[files[file_idx].dir_idx].dir_idx;
+static void
+output_compilation_unit_header (void)
+{
+ int ver = dwarf_version;
- dw2_asm_output_nstring (files[file_idx].path + dirs[dir_idx].length, -1,
- "File Entry: 0x%x", (unsigned) i + 1);
+ /* Don't mark the output as DWARF-4 until we make full use of the
+ version 4 extensions, and gdb supports them. For now, -gdwarf-4
+ selects only a few extensions from the DWARF-4 spec. */
+ if (ver > 3)
+ ver = 3;
+ if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+ dw2_asm_output_data (4, 0xffffffff,
+ "Initial length escape value indicating 64-bit DWARF extension");
+ dw2_asm_output_data (DWARF_OFFSET_SIZE,
+ next_die_offset - DWARF_INITIAL_LENGTH_SIZE,
+ "Length of Compilation Unit Info");
+ dw2_asm_output_data (2, ver, "DWARF version number");
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, abbrev_section_label,
+ debug_abbrev_section,
+ "Offset Into Abbrev. Section");
+ dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
+}
- /* Include directory index. */
- dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL);
+/* Output the compilation unit DIE and its children. */
- /* Modification time. */
- dw2_asm_output_data_uleb128 (0, NULL);
+static void
+output_comp_unit (dw_die_ref die, int output_if_empty)
+{
+ const char *secname;
+ char *oldsym, *tmp;
- /* File length in bytes. */
- dw2_asm_output_data_uleb128 (0, NULL);
+ /* Unless we are outputting main CU, we may throw away empty ones. */
+ if (!output_if_empty && die->die_child == NULL)
+ return;
+
+ /* Even if there are no children of this DIE, we must output the information
+ about the compilation unit. Otherwise, on an empty translation unit, we
+ will generate a present, but empty, .debug_info section. IRIX 6.5 `nm'
+ will then complain when examining the file. First mark all the DIEs in
+ this CU so we know which get local refs. */
+ mark_dies (die);
+
+ build_abbrev_table (die);
+
+ /* Initialize the beginning DIE offset - and calculate sizes/offsets. */
+ next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
+ calc_die_sizes (die);
+
+ oldsym = die->die_id.die_symbol;
+ if (oldsym)
+ {
+ tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
+
+ sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
+ secname = tmp;
+ die->die_id.die_symbol = NULL;
+ switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
}
+ else
+ switch_to_section (debug_info_section);
- dw2_asm_output_data (1, 0, "End file name table");
-}
+ /* Output debugging information. */
+ output_compilation_unit_header ();
+ output_die (die);
+ /* Leave the marks on the main CU, so we can check them in
+ output_pubnames. */
+ if (oldsym)
+ {
+ unmark_dies (die);
+ die->die_id.die_symbol = oldsym;
+ }
+}
-/* Output the source line number correspondence information. This
- information goes into the .debug_line section. */
+/* Output a comdat type unit DIE and its children. */
static void
-output_line_info (void)
+output_comdat_type_unit (comdat_type_node *node)
{
- 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;
+ const char *secname;
+ char *tmp;
+ int i;
+#if defined (OBJECT_FORMAT_ELF)
+ tree comdat_key;
+#endif
- ASM_GENERATE_INTERNAL_LABEL (l1, LINE_NUMBER_BEGIN_LABEL, 0);
- ASM_GENERATE_INTERNAL_LABEL (l2, LINE_NUMBER_END_LABEL, 0);
- ASM_GENERATE_INTERNAL_LABEL (p1, LN_PROLOG_AS_LABEL, 0);
- ASM_GENERATE_INTERNAL_LABEL (p2, LN_PROLOG_END_LABEL, 0);
+ /* First mark all the DIEs in this CU so we know which get local refs. */
+ mark_dies (node->root_die);
- if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
- dw2_asm_output_data (4, 0xffffffff,
- "Initial length escape value indicating 64-bit DWARF extension");
- dw2_asm_output_delta (DWARF_OFFSET_SIZE, l2, l1,
- "Length of Source Line Info");
- ASM_OUTPUT_LABEL (asm_out_file, l1);
+ build_abbrev_table (node->root_die);
- dw2_asm_output_data (2, dwarf_version, "DWARF Version");
- dw2_asm_output_delta (DWARF_OFFSET_SIZE, p2, p1, "Prolog Length");
- ASM_OUTPUT_LABEL (asm_out_file, p1);
+ /* Initialize the beginning DIE offset - and calculate sizes/offsets. */
+ next_die_offset = DWARF_COMDAT_TYPE_UNIT_HEADER_SIZE;
+ calc_die_sizes (node->root_die);
+
+#if defined (OBJECT_FORMAT_ELF)
+ secname = ".debug_types";
+ tmp = XALLOCAVEC (char, 4 + DWARF_TYPE_SIGNATURE_SIZE * 2);
+ sprintf (tmp, "wt.");
+ for (i = 0; i < DWARF_TYPE_SIGNATURE_SIZE; i++)
+ sprintf (tmp + 3 + i * 2, "%02x", node->signature[i] & 0xff);
+ comdat_key = get_identifier (tmp);
+ targetm.asm_out.named_section (secname,
+ SECTION_DEBUG | SECTION_LINKONCE,
+ comdat_key);
+#else
+ tmp = XALLOCAVEC (char, 18 + DWARF_TYPE_SIGNATURE_SIZE * 2);
+ sprintf (tmp, ".gnu.linkonce.wt.");
+ for (i = 0; i < DWARF_TYPE_SIGNATURE_SIZE; i++)
+ sprintf (tmp + 17 + i * 2, "%02x", node->signature[i] & 0xff);
+ secname = tmp;
+ switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
+#endif
- /* 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");
+ /* Output debugging information. */
+ output_compilation_unit_header ();
+ output_signature (node->signature, "Type Signature");
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, node->type_die->die_offset,
+ "Offset to Type DIE");
+ output_die (node->root_die);
- dw2_asm_output_data (1, DWARF_LINE_DEFAULT_IS_STMT_START,
- "Default is_stmt_start flag");
- dw2_asm_output_data (1, DWARF_LINE_BASE,
- "Line Base Value (Special Opcodes)");
- dw2_asm_output_data (1, DWARF_LINE_RANGE,
- "Line Range Value (Special Opcodes)");
- dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE,
- "Special Opcode Base");
+ unmark_dies (node->root_die);
+}
- for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; opc++)
- {
- switch (opc)
- {
- case DW_LNS_advance_pc:
- case DW_LNS_advance_line:
- case DW_LNS_set_file:
- case DW_LNS_set_column:
- case DW_LNS_fixed_advance_pc:
- n_op_args = 1;
- break;
- default:
- n_op_args = 0;
- break;
- }
+/* Return the DWARF2/3 pubname associated with a decl. */
- dw2_asm_output_data (1, n_op_args, "opcode: 0x%x has %d args",
- opc, n_op_args);
- }
+static const char *
+dwarf2_name (tree decl, int scope)
+{
+ return lang_hooks.dwarf_name (decl, scope ? 1 : 0);
+}
- /* Write out the information about the files we use. */
- output_file_names ();
- ASM_OUTPUT_LABEL (asm_out_file, p2);
+/* Add a new entry to .debug_pubnames if appropriate. */
- /* 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. */
+static void
+add_pubname_string (const char *str, dw_die_ref die)
+{
+ pubname_entry e;
- /* Generate the line number to PC correspondence table, encoded as
- a series of state machine operations. */
- current_file = 1;
- current_line = 1;
+ e.die = die;
+ e.name = xstrdup (str);
+ VEC_safe_push (pubname_entry, gc, pubname_table, &e);
+}
- 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)
+static void
+add_pubname (tree decl, dw_die_ref die)
+{
+ if (TREE_PUBLIC (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
+ const char *name = dwarf2_name (decl, 1);
+ if (name)
+ add_pubname_string (name, die);
+ }
+}
- /* Emit debug info for the address of the current line.
+/* Add a new entry to .debug_pubtypes if appropriate. */
- 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. */
+static void
+add_pubtype (tree decl, dw_die_ref die)
+{
+ pubname_entry e;
- ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index);
- if (0)
+ e.name = NULL;
+ if ((TREE_PUBLIC (decl)
+ || die->die_parent == comp_unit_die)
+ && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl)))
+ {
+ e.die = die;
+ if (TYPE_P (decl))
{
- /* 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);
+ if (TYPE_NAME (decl))
+ {
+ if (TREE_CODE (TYPE_NAME (decl)) == IDENTIFIER_NODE)
+ e.name = IDENTIFIER_POINTER (TYPE_NAME (decl));
+ else if (TREE_CODE (TYPE_NAME (decl)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (decl)))
+ e.name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (decl)));
+ else
+ e.name = xstrdup ((const char *) get_AT_string (die, DW_AT_name));
+ }
}
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);
+ e.name = dwarf2_name (decl, 1);
+ if (e.name)
+ e.name = xstrdup (e.name);
}
- strcpy (prev_line_label, line_label);
+ /* If we don't have a name for the type, there's no point in adding
+ it to the table. */
+ if (e.name && e.name[0] != '\0')
+ VEC_safe_push (pubname_entry, gc, pubtype_table, &e);
+ }
+}
- /* 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);
- }
+/* Output the public names table used to speed up access to externally
+ visible names; or the public types table used to find type definitions. */
- /* Emit debug info for the current line number, choosing the encoding
- that uses the least amount of space. */
- if (line_info->dw_line_num != current_line)
- {
- line_offset = line_info->dw_line_num - current_line;
- line_delta = line_offset - DWARF_LINE_BASE;
- current_line = line_info->dw_line_num;
- if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1))
- /* This can handle deltas from -10 to 234, using the current
- definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This
- takes 1 byte. */
- dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta,
- "line %lu", current_line);
- else
- {
- /* This can handle any delta. This takes at least 4 bytes,
- depending on the value being encoded. */
- dw2_asm_output_data (1, DW_LNS_advance_line,
- "advance to line %lu", current_line);
- dw2_asm_output_data_sleb128 (line_offset, NULL);
- dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
- }
+static void
+output_pubnames (VEC (pubname_entry, gc) * names)
+{
+ unsigned i;
+ unsigned long pubnames_length = size_of_pubnames (names);
+ pubname_ref pub;
+
+ if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+ dw2_asm_output_data (4, 0xffffffff,
+ "Initial length escape value indicating 64-bit DWARF extension");
+ if (names == pubname_table)
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
+ "Length of Public Names Info");
+ else
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length,
+ "Length of Public Type Names Info");
+ /* Version number for pubnames/pubtypes is still 2, even in DWARF3. */
+ dw2_asm_output_data (2, 2, "DWARF Version");
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+ debug_info_section,
+ "Offset of Compilation Unit Info");
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset,
+ "Compilation Unit Length");
+
+ for (i = 0; VEC_iterate (pubname_entry, names, i, pub); i++)
+ {
+ /* We shouldn't see pubnames for DIEs outside of the main CU. */
+ if (names == pubname_table)
+ gcc_assert (pub->die->die_mark);
+
+ if (names != pubtype_table
+ || pub->die->die_offset != 0
+ || !flag_eliminate_unused_debug_types)
+ {
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, pub->die->die_offset,
+ "DIE offset");
+
+ dw2_asm_output_nstring (pub->name, -1, "external name");
}
- else
- /* We still need to start a new row, so output a copy insn. */
- dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
}
- /* Emit debug info for the address of the end of the function. */
- if (0)
+ dw2_asm_output_data (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)
{
- 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);
+ 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));
}
- else
+
+ arange_table[arange_table_in_use++] = die;
+}
+
+/* Output the information that goes into the .debug_aranges table.
+ Namely, define the beginning and ending address range of the
+ text section generated for this compilation unit. */
+
+static void
+output_aranges (void)
+{
+ unsigned i;
+ unsigned long aranges_length = size_of_aranges ();
+
+ if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+ dw2_asm_output_data (4, 0xffffffff,
+ "Initial length escape value indicating 64-bit DWARF extension");
+ dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length,
+ "Length of Address Ranges Info");
+ /* Version number for aranges is still 2, even in DWARF3. */
+ dw2_asm_output_data (2, 2, "DWARF Version");
+ dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+ debug_info_section,
+ "Offset of Compilation Unit Info");
+ dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address");
+ dw2_asm_output_data (1, 0, "Size of Segment Descriptor");
+
+ /* We need to align to twice the pointer size here. */
+ if (DWARF_ARANGES_PAD_SIZE)
{
- 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);
+ /* Pad using a 2 byte words so that padding is correct for any
+ pointer size. */
+ dw2_asm_output_data (2, 0, "Pad to %d byte boundary",
+ 2 * DWARF2_ADDR_SIZE);
+ for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2)
+ dw2_asm_output_data (2, 0, NULL);
}
- dw2_asm_output_data (1, 0, "DW_LNE_end_sequence");
- dw2_asm_output_data_uleb128 (1, NULL);
- dw2_asm_output_data (1, DW_LNE_end_sequence, NULL);
+ /* It is necessary not to output these entries if the sections were
+ not used; if the sections were not used, the length will be 0 and
+ the address may end up as 0 if the section is discarded by ld
+ --gc-sections, leaving an invalid (0, 0) entry that can be
+ confused with the terminator. */
+ if (text_section_used)
+ {
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_section_label, "Address");
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, text_end_label,
+ text_section_label, "Length");
+ }
+ if (cold_text_section_used)
+ {
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, cold_text_section_label,
+ "Address");
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, cold_end_label,
+ cold_text_section_label, "Length");
+ }
- function = 0;
- current_file = 1;
- current_line = 1;
- for (lt_index = 0; lt_index < separate_line_info_table_in_use;)
+ for (i = 0; i < arange_table_in_use; i++)
{
- dw_separate_line_info_ref line_info
- = &separate_line_info_table[lt_index];
+ dw_die_ref die = arange_table[i];
-#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
+ /* We shouldn't see aranges for DIEs outside of the main CU. */
+ gcc_assert (die->die_mark);
- /* 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)
+ if (die->die_tag == DW_TAG_subprogram)
{
- 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);
+ 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
{
- /* ??? 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);
- }
- }
+ /* 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;
- strcpy (prev_line_label, line_label);
+ gcc_assert (a && AT_class (a) == dw_val_class_loc);
- /* 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);
- }
+ loc = AT_loc (a);
+ gcc_assert (loc->dw_loc_opc == DW_OP_addr);
- /* 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");
- }
+ 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");
}
- else
- dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+ }
-#if 0
- cont:
-#endif
+ /* Output the terminator words. */
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+}
- lt_index++;
+/* Add a new entry to .debug_ranges. Return the offset at which it
+ was placed. */
- /* 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;
+static unsigned int
+add_ranges_num (int num)
+{
+ unsigned int in_use = ranges_table_in_use;
- /* 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);
- }
+ if (in_use == ranges_table_allocated)
+ {
+ ranges_table_allocated += RANGES_TABLE_INCREMENT;
+ ranges_table = GGC_RESIZEVEC (struct dw_ranges_struct, ranges_table,
+ ranges_table_allocated);
+ memset (ranges_table + ranges_table_in_use, 0,
+ RANGES_TABLE_INCREMENT * sizeof (struct dw_ranges_struct));
}
- /* Output the marker for the end of the line number info. */
- ASM_OUTPUT_LABEL (asm_out_file, l2);
+ ranges_table[in_use].num = num;
+ ranges_table_in_use = in_use + 1;
+
+ return in_use * 2 * DWARF2_ADDR_SIZE;
}
-\f
-/* 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. */
+/* Add a new entry to .debug_ranges corresponding to a block, or a
+ range terminator if BLOCK is NULL. */
-static dw_die_ref
-base_type_die (tree type)
+static unsigned int
+add_ranges (const_tree block)
{
- dw_die_ref base_type_result;
- enum dwarf_type encoding;
+ return add_ranges_num (block ? BLOCK_NUMBER (block) : 0);
+}
- if (TREE_CODE (type) == ERROR_MARK || TREE_CODE (type) == VOID_TYPE)
- return 0;
+/* Add a new entry to .debug_ranges corresponding to a pair of
+ labels. */
- /* 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);
+static void
+add_ranges_by_labels (dw_die_ref die, const char *begin, const char *end,
+ bool *added)
+{
+ unsigned int in_use = ranges_by_label_in_use;
+ unsigned int offset;
- switch (TREE_CODE (type))
+ if (in_use == ranges_by_label_allocated)
{
- case INTEGER_TYPE:
- if (TYPE_STRING_FLAG (type))
- {
- if (TYPE_UNSIGNED (type))
- encoding = DW_ATE_unsigned_char;
- else
- encoding = DW_ATE_signed_char;
- }
- else if (TYPE_UNSIGNED (type))
- encoding = DW_ATE_unsigned;
- else
- encoding = DW_ATE_signed;
- break;
-
- case REAL_TYPE:
- if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
- encoding = DW_ATE_decimal_float;
- else
- encoding = DW_ATE_float;
- break;
-
- case FIXED_POINT_TYPE:
- 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;
+ ranges_by_label_allocated += RANGES_TABLE_INCREMENT;
+ ranges_by_label = GGC_RESIZEVEC (struct dw_ranges_by_label_struct,
+ ranges_by_label,
+ ranges_by_label_allocated);
+ memset (ranges_by_label + ranges_by_label_in_use, 0,
+ RANGES_TABLE_INCREMENT
+ * sizeof (struct dw_ranges_by_label_struct));
+ }
- case BOOLEAN_TYPE:
- /* GNU FORTRAN/Ada/C++ BOOLEAN type. */
- encoding = DW_ATE_boolean;
- break;
+ ranges_by_label[in_use].begin = begin;
+ ranges_by_label[in_use].end = end;
+ ranges_by_label_in_use = in_use + 1;
- default:
- /* No other TREE_CODEs are Dwarf fundamental types. */
- gcc_unreachable ();
+ offset = add_ranges_num (-(int)in_use - 1);
+ if (!*added)
+ {
+ add_AT_range_list (die, DW_AT_ranges, offset);
+ *added = true;
}
+}
- base_type_result = new_die (DW_TAG_base_type, comp_unit_die, type);
+static void
+output_ranges (void)
+{
+ unsigned i;
+ static const char *const start_fmt = "Offset 0x%x";
+ const char *fmt = start_fmt;
- /* This probably indicates a bug. */
- if (! TYPE_NAME (type))
- add_name_attribute (base_type_result, "__unknown__");
+ for (i = 0; i < ranges_table_in_use; i++)
+ {
+ int block_num = ranges_table[i].num;
- 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);
+ if (block_num > 0)
+ {
+ char blabel[MAX_ARTIFICIAL_LABEL_BYTES];
+ char elabel[MAX_ARTIFICIAL_LABEL_BYTES];
- return base_type_result;
-}
+ ASM_GENERATE_INTERNAL_LABEL (blabel, BLOCK_BEGIN_LABEL, block_num);
+ ASM_GENERATE_INTERNAL_LABEL (elabel, BLOCK_END_LABEL, block_num);
-/* Given a pointer to an arbitrary ..._TYPE tree node, return nonzero if the
- given input type is a Dwarf "fundamental" type. Otherwise return null. */
+ /* If all code is in the text section, then the compilation
+ unit base address defaults to DW_AT_low_pc, which is the
+ base of the text section. */
+ if (!have_multiple_function_sections)
+ {
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, blabel,
+ text_section_label,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE, elabel,
+ text_section_label, NULL);
+ }
-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;
+ /* Otherwise, the compilation unit base address is zero,
+ which allows us to use absolute addresses, and not worry
+ about whether the target supports cross-section
+ arithmetic. */
+ else
+ {
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, blabel,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, elabel, NULL);
+ }
- 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;
+ fmt = NULL;
+ }
- default:
- gcc_unreachable ();
- }
+ /* Negative block_num stands for an index into ranges_by_label. */
+ else if (block_num < 0)
+ {
+ int lab_idx = - block_num - 1;
- return 0;
+ if (!have_multiple_function_sections)
+ {
+ gcc_unreachable ();
+#if 0
+ /* If we ever use add_ranges_by_labels () for a single
+ function section, all we have to do is to take out
+ the #if 0 above. */
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+ ranges_by_label[lab_idx].begin,
+ text_section_label,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_delta (DWARF2_ADDR_SIZE,
+ ranges_by_label[lab_idx].end,
+ text_section_label, NULL);
+#endif
+ }
+ else
+ {
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+ ranges_by_label[lab_idx].begin,
+ fmt, i * 2 * DWARF2_ADDR_SIZE);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE,
+ ranges_by_label[lab_idx].end,
+ NULL);
+ }
+ }
+ else
+ {
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+ dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
+ fmt = start_fmt;
+ }
+ }
}
-/* 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. */
+/* Data structure containing information about input files. */
+struct file_info
+{
+ const char *path; /* Complete file name. */
+ const char *fname; /* File name part. */
+ int length; /* Length of entire string. */
+ struct dwarf_file_data * file_idx; /* Index in input file table. */
+ int dir_idx; /* Index in directory table. */
+};
-static inline unsigned HOST_WIDE_INT
-simple_type_size_in_bits (const_tree type)
+/* Data structure containing information about directories with source
+ files. */
+struct dir_info
{
- 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);
-}
+ const char *path; /* Path including directory name. */
+ int length; /* Path length. */
+ int prefix; /* Index of directory entry which is a prefix. */
+ int count; /* Number of files in this directory. */
+ int dir_idx; /* Index of directory used as base. */
+};
-/* Given a pointer to a tree node for a subrange type, return a pointer
- to a DIE that describes the given type. */
+/* Callback function for file_info comparison. We sort by looking at
+ the directories in the path. */
-static dw_die_ref
-subrange_type_die (tree type, tree low, tree high, dw_die_ref context_die)
+static int
+file_info_cmp (const void *p1, const void *p2)
{
- dw_die_ref subrange_die;
- const HOST_WIDE_INT size_in_bytes = int_size_in_bytes (type);
+ const struct file_info *const s1 = (const struct file_info *) p1;
+ const struct file_info *const s2 = (const struct file_info *) p2;
+ const unsigned char *cp1;
+ const unsigned char *cp2;
- if (context_die == NULL)
- context_die = comp_unit_die;
+ /* Take care of file names without directories. We need to make sure that
+ we return consistent values to qsort since some will get confused if
+ we return the same value when identical operands are passed in opposite
+ orders. So if neither has a directory, return 0 and otherwise return
+ 1 or -1 depending on which one has the directory. */
+ if ((s1->path == s1->fname || s2->path == s2->fname))
+ return (s2->path == s2->fname) - (s1->path == s1->fname);
- subrange_die = new_die (DW_TAG_subrange_type, context_die, type);
+ cp1 = (const unsigned char *) s1->path;
+ cp2 = (const unsigned char *) s2->path;
- if (int_size_in_bytes (TREE_TYPE (type)) != size_in_bytes)
+ while (1)
{
- /* 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);
+ ++cp1;
+ ++cp2;
+ /* Reached the end of the first path? If so, handle like above. */
+ if ((cp1 == (const unsigned char *) s1->fname)
+ || (cp2 == (const unsigned char *) s2->fname))
+ return ((cp2 == (const unsigned char *) s2->fname)
+ - (cp1 == (const unsigned char *) s1->fname));
- return subrange_die;
+ /* Character of current path component the same? */
+ else if (*cp1 != *cp2)
+ return *cp1 - *cp2;
+ }
}
-/* Given a pointer to an arbitrary ..._TYPE tree node, return a debugging
- entry that chains various modifiers in front of the given type. */
+struct file_name_acquire_data
+{
+ struct file_info *files;
+ int used_files;
+ int max_files;
+};
-static dw_die_ref
-modified_type_die (tree type, int is_const_type, int is_volatile_type,
- dw_die_ref context_die)
+/* Traversal function for the hash table. */
+
+static int
+file_name_acquire (void ** slot, void *data)
{
- 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;
+ struct file_name_acquire_data *fnad = (struct file_name_acquire_data *) data;
+ struct dwarf_file_data *d = (struct dwarf_file_data *) *slot;
+ struct file_info *fi;
+ const char *f;
- if (code == ERROR_MARK)
- return NULL;
+ gcc_assert (fnad->max_files >= d->emitted_number);
- /* 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 (! d->emitted_number)
+ return 1;
- /* 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;
- }
+ gcc_assert (fnad->max_files != fnad->used_files);
- name = qualified_type ? TYPE_NAME (qualified_type) : NULL;
+ fi = fnad->files + fnad->used_files++;
- /* Handle C typedef types. */
- if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name))
- {
- tree dtype = TREE_TYPE (name);
+ /* Skip all leading "./". */
+ f = d->filename;
+ while (f[0] == '.' && IS_DIR_SEPARATOR (f[1]))
+ f += 2;
- 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. */
- }
+ /* Create a new array entry. */
+ fi->path = f;
+ fi->length = strlen (f);
+ fi->file_idx = d;
- 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);
- }
- else if (code == REFERENCE_TYPE)
- {
- mod_type_die = new_die (DW_TAG_reference_type, comp_unit_die, type);
- add_AT_unsigned (mod_type_die, DW_AT_byte_size,
- simple_type_size_in_bits (type) / BITS_PER_UNIT);
- item_type = TREE_TYPE (type);
- }
- else if (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);
+ /* Search for the file name part. */
+ f = strrchr (f, DIR_SEPARATOR);
+#if defined (DIR_SEPARATOR_2)
+ {
+ char *g = strrchr (fi->path, DIR_SEPARATOR_2);
- /* 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);
- }
+ if (g != NULL)
+ {
+ if (f == NULL || f < g)
+ f = g;
+ }
+ }
+#endif
- /* Builtin types don't have a DECL_ORIGINAL_TYPE. For those,
- don't output a DW_TAG_typedef, since there isn't one in the
- user's program; just attach a DW_AT_name to the type. */
- if (name
- && (TREE_CODE (name) != TYPE_DECL
- || (TREE_TYPE (name) == qualified_type && DECL_NAME (name))))
+ fi->fname = f == NULL ? fi->path : f + 1;
+ return 1;
+}
+
+/* Output the directory table and the file name table. We try to minimize
+ the total amount of memory needed. A heuristic is used to avoid large
+ slowdowns with many input files. */
+
+static void
+output_file_names (void)
+{
+ struct file_name_acquire_data fnad;
+ int numfiles;
+ struct file_info *files;
+ struct dir_info *dirs;
+ int *saved;
+ int *savehere;
+ int *backmap;
+ int ndirs;
+ int idx_offset;
+ int i;
+
+ if (!last_emitted_file)
{
- 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));
+ dw2_asm_output_data (1, 0, "End directory table");
+ dw2_asm_output_data (1, 0, "End file name table");
+ return;
}
- if (qualified_type)
- equate_type_number_to_die (qualified_type, mod_type_die);
+ numfiles = last_emitted_file->emitted_number;
- 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);
+ /* Allocate the various arrays we need. */
+ files = XALLOCAVEC (struct file_info, numfiles);
+ dirs = XALLOCAVEC (struct dir_info, numfiles);
+
+ fnad.files = files;
+ fnad.used_files = 0;
+ fnad.max_files = numfiles;
+ htab_traverse (file_table, file_name_acquire, &fnad);
+ gcc_assert (fnad.used_files == fnad.max_files);
+
+ qsort (files, numfiles, sizeof (files[0]), file_info_cmp);
+
+ /* Find all the different directories used. */
+ dirs[0].path = files[0].path;
+ dirs[0].length = files[0].fname - files[0].path;
+ dirs[0].prefix = -1;
+ dirs[0].count = 1;
+ dirs[0].dir_idx = 0;
+ files[0].dir_idx = 0;
+ ndirs = 1;
+
+ for (i = 1; i < numfiles; i++)
+ if (files[i].fname - files[i].path == dirs[ndirs - 1].length
+ && memcmp (dirs[ndirs - 1].path, files[i].path,
+ dirs[ndirs - 1].length) == 0)
+ {
+ /* Same directory as last entry. */
+ files[i].dir_idx = ndirs - 1;
+ ++dirs[ndirs - 1].count;
+ }
+ else
+ {
+ int j;
+
+ /* This is a new directory. */
+ dirs[ndirs].path = files[i].path;
+ dirs[ndirs].length = files[i].fname - files[i].path;
+ dirs[ndirs].count = 1;
+ dirs[ndirs].dir_idx = ndirs;
+ files[i].dir_idx = ndirs;
+
+ /* Search for a prefix. */
+ dirs[ndirs].prefix = -1;
+ for (j = 0; j < ndirs; j++)
+ if (dirs[j].length < dirs[ndirs].length
+ && dirs[j].length > 1
+ && (dirs[ndirs].prefix == -1
+ || dirs[j].length > dirs[dirs[ndirs].prefix].length)
+ && memcmp (dirs[j].path, dirs[ndirs].path, dirs[j].length) == 0)
+ dirs[ndirs].prefix = j;
+
+ ++ndirs;
+ }
+
+ /* Now to the actual work. We have to find a subset of the directories which
+ allow expressing the file name using references to the directory table
+ with the least amount of characters. We do not do an exhaustive search
+ where we would have to check out every combination of every single
+ possible prefix. Instead we use a heuristic which provides nearly optimal
+ results in most cases and never is much off. */
+ saved = XALLOCAVEC (int, ndirs);
+ savehere = XALLOCAVEC (int, ndirs);
+
+ memset (saved, '\0', ndirs * sizeof (saved[0]));
+ for (i = 0; i < ndirs; i++)
+ {
+ int j;
+ int total;
+
+ /* We can always save some space for the current directory. But this
+ does not mean it will be enough to justify adding the directory. */
+ savehere[i] = dirs[i].length;
+ total = (savehere[i] - saved[i]) * dirs[i].count;
+
+ for (j = i + 1; j < ndirs; j++)
+ {
+ savehere[j] = 0;
+ if (saved[j] < dirs[i].length)
+ {
+ /* Determine whether the dirs[i] path is a prefix of the
+ dirs[j] path. */
+ int k;
+
+ k = dirs[j].prefix;
+ while (k != -1 && k != (int) i)
+ k = dirs[k].prefix;
+
+ if (k == (int) i)
+ {
+ /* Yes it is. We can possibly save some memory by
+ writing the filenames in dirs[j] relative to
+ dirs[i]. */
+ savehere[j] = dirs[i].length;
+ total += (savehere[j] - saved[j]) * dirs[j].count;
+ }
+ }
+ }
+
+ /* Check whether we can save enough to justify adding the dirs[i]
+ directory. */
+ if (total > dirs[i].length + 1)
+ {
+ /* It's worthwhile adding. */
+ for (j = i; j < ndirs; j++)
+ if (savehere[j] > 0)
+ {
+ /* Remember how much we saved for this directory so far. */
+ saved[j] = savehere[j];
+
+ /* Remember the prefix directory. */
+ dirs[j].dir_idx = i;
+ }
+ }
+ }
+
+ /* Emit the directory name table. */
+ idx_offset = dirs[0].length > 0 ? 1 : 0;
+ for (i = 1 - idx_offset; i < ndirs; i++)
+ dw2_asm_output_nstring (dirs[i].path,
+ dirs[i].length
+ - !DWARF2_DIR_SHOULD_END_WITH_SEPARATOR,
+ "Directory Entry: 0x%x", i + idx_offset);
+
+ dw2_asm_output_data (1, 0, "End directory table");
+
+ /* We have to emit them in the order of emitted_number since that's
+ used in the debug info generation. To do this efficiently we
+ generate a back-mapping of the indices first. */
+ backmap = XALLOCAVEC (int, numfiles);
+ for (i = 0; i < numfiles; i++)
+ backmap[files[i].file_idx->emitted_number - 1] = i;
+
+ /* Now write all the file names. */
+ for (i = 0; i < numfiles; i++)
+ {
+ int file_idx = backmap[i];
+ int dir_idx = dirs[files[file_idx].dir_idx].dir_idx;
+
+#ifdef VMS_DEBUGGING_INFO
+#define MAX_VMS_VERSION_LEN 6 /* ";32768" */
+
+ /* Setting these fields can lead to debugger miscomparisons,
+ but VMS Debug requires them to be set correctly. */
+
+ int ver;
+ long long cdt;
+ long siz;
+ int maxfilelen = strlen (files[file_idx].path)
+ + dirs[dir_idx].length
+ + MAX_VMS_VERSION_LEN + 1;
+ char *filebuf = XALLOCAVEC (char, maxfilelen);
+
+ vms_file_stats_name (files[file_idx].path, 0, 0, 0, &ver);
+ snprintf (filebuf, maxfilelen, "%s;%d",
+ files[file_idx].path + dirs[dir_idx].length, ver);
+
+ dw2_asm_output_nstring
+ (filebuf, -1, "File Entry: 0x%x", (unsigned) i + 1);
+
+ /* Include directory index. */
+ dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL);
+
+ /* Modification time. */
+ dw2_asm_output_data_uleb128
+ ((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: 0x%x", (unsigned) i + 1);
+
+ /* Include directory index. */
+ dw2_asm_output_data_uleb128 (dir_idx + idx_offset, NULL);
+
+ /* Modification time. */
+ dw2_asm_output_data_uleb128 (0, NULL);
+
+ /* File length in bytes. */
+ dw2_asm_output_data_uleb128 (0, NULL);
+#endif
+ }
+
+ dw2_asm_output_data (1, 0, "End file name table");
+}
+
+
+/* Output the source line number correspondence information. This
+ information goes into the .debug_line section. */
+
+static void
+output_line_info (void)
+{
+ char l1[20], l2[20], p1[20], p2[20];
+ char line_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char prev_line_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ unsigned opc;
+ unsigned n_op_args;
+ unsigned long lt_index;
+ unsigned long current_line;
+ long line_offset;
+ long line_delta;
+ unsigned long current_file;
+ unsigned long function;
+ int ver = dwarf_version;
+
+ /* Don't mark the output as DWARF-4 until we make full use of the
+ version 4 extensions, and gdb supports them. For now, -gdwarf-4
+ selects only a few extensions from the DWARF-4 spec. */
+ if (ver > 3)
+ ver = 3;
+
+ ASM_GENERATE_INTERNAL_LABEL (l1, LINE_NUMBER_BEGIN_LABEL, 0);
+ ASM_GENERATE_INTERNAL_LABEL (l2, LINE_NUMBER_END_LABEL, 0);
+ ASM_GENERATE_INTERNAL_LABEL (p1, LN_PROLOG_AS_LABEL, 0);
+ ASM_GENERATE_INTERNAL_LABEL (p2, LN_PROLOG_END_LABEL, 0);
+
+ if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+ dw2_asm_output_data (4, 0xffffffff,
+ "Initial length escape value indicating 64-bit DWARF extension");
+ dw2_asm_output_delta (DWARF_OFFSET_SIZE, l2, l1,
+ "Length of Source Line Info");
+ ASM_OUTPUT_LABEL (asm_out_file, l1);
+
+ dw2_asm_output_data (2, ver, "DWARF Version");
+ dw2_asm_output_delta (DWARF_OFFSET_SIZE, p2, p1, "Prolog Length");
+ ASM_OUTPUT_LABEL (asm_out_file, p1);
+
+ /* Define the architecture-dependent minimum instruction length (in
+ bytes). In this implementation of DWARF, this field is used for
+ information purposes only. Since GCC generates assembly language,
+ we have no a priori knowledge of how many instruction bytes are
+ generated for each source line, and therefore can use only the
+ DW_LNE_set_address and DW_LNS_fixed_advance_pc line information
+ commands. Accordingly, we fix this as `1', which is "correct
+ enough" for all architectures, and don't let the target override. */
+ dw2_asm_output_data (1, 1,
+ "Minimum Instruction Length");
+
+ dw2_asm_output_data (1, DWARF_LINE_DEFAULT_IS_STMT_START,
+ "Default is_stmt_start flag");
+ dw2_asm_output_data (1, DWARF_LINE_BASE,
+ "Line Base Value (Special Opcodes)");
+ dw2_asm_output_data (1, DWARF_LINE_RANGE,
+ "Line Range Value (Special Opcodes)");
+ dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE,
+ "Special Opcode Base");
+
+ for (opc = 1; opc < DWARF_LINE_OPCODE_BASE; opc++)
+ {
+ switch (opc)
+ {
+ case DW_LNS_advance_pc:
+ case DW_LNS_advance_line:
+ case DW_LNS_set_file:
+ case DW_LNS_set_column:
+ case DW_LNS_fixed_advance_pc:
+ n_op_args = 1;
+ break;
+ default:
+ n_op_args = 0;
+ break;
+ }
+
+ dw2_asm_output_data (1, n_op_args, "opcode: 0x%x has %d args",
+ opc, n_op_args);
+ }
+
+ /* Write out the information about the files we use. */
+ output_file_names ();
+ ASM_OUTPUT_LABEL (asm_out_file, p2);
+
+ /* We used to set the address register to the first location in the text
+ section here, but that didn't accomplish anything since we already
+ have a line note for the opening brace of the first function. */
+
+ /* Generate the line number to PC correspondence table, encoded as
+ a series of state machine operations. */
+ current_file = 1;
+ current_line = 1;
+
+ if (cfun && in_cold_section_p)
+ strcpy (prev_line_label, 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)
+ {
+ dw_line_info_ref line_info = &line_info_table[lt_index];
+
+#if 0
+ /* Disable this optimization for now; GDB wants to see two line notes
+ at the beginning of a function so it can find the end of the
+ prologue. */
+
+ /* Don't emit anything for redundant notes. Just updating the
+ address doesn't accomplish anything, because we already assume
+ that anything after the last address is this line. */
+ if (line_info->dw_line_num == current_line
+ && line_info->dw_file_num == current_file)
+ continue;
+#endif
+
+ /* Emit debug info for the address of the current line.
+
+ Unfortunately, we have little choice here currently, and must always
+ use the most general form. GCC does not know the address delta
+ itself, so we can't use DW_LNS_advance_pc. Many ports do have length
+ attributes which will give an upper bound on the address range. We
+ could perhaps use length attributes to determine when it is safe to
+ use DW_LNS_fixed_advance_pc. */
+
+ ASM_GENERATE_INTERNAL_LABEL (line_label, LINE_CODE_LABEL, lt_index);
+ if (0)
+ {
+ /* This can handle deltas up to 0xffff. This takes 3 bytes. */
+ dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+ "DW_LNS_fixed_advance_pc");
+ dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+ }
+ else
+ {
+ /* This can handle any delta. This takes
+ 4+DWARF2_ADDR_SIZE bytes. */
+ dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+ dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+ dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+ }
+
+ strcpy (prev_line_label, line_label);
+
+ /* Emit debug info for the source file of the current line, if
+ different from the previous line. */
+ if (line_info->dw_file_num != current_file)
+ {
+ current_file = line_info->dw_file_num;
+ dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file");
+ dw2_asm_output_data_uleb128 (current_file, "%lu", current_file);
+ }
+
+ /* Emit debug info for the current line number, choosing the encoding
+ that uses the least amount of space. */
+ if (line_info->dw_line_num != current_line)
+ {
+ line_offset = line_info->dw_line_num - current_line;
+ line_delta = line_offset - DWARF_LINE_BASE;
+ current_line = line_info->dw_line_num;
+ if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1))
+ /* This can handle deltas from -10 to 234, using the current
+ definitions of DWARF_LINE_BASE and DWARF_LINE_RANGE. This
+ takes 1 byte. */
+ dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta,
+ "line %lu", current_line);
+ else
+ {
+ /* This can handle any delta. This takes at least 4 bytes,
+ depending on the value being encoded. */
+ dw2_asm_output_data (1, DW_LNS_advance_line,
+ "advance to line %lu", current_line);
+ dw2_asm_output_data_sleb128 (line_offset, NULL);
+ dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+ }
+ }
+ else
+ /* We still need to start a new row, so output a copy insn. */
+ dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+ }
+
+ /* Emit debug info for the address of the end of the function. */
+ if (0)
+ {
+ dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+ "DW_LNS_fixed_advance_pc");
+ dw2_asm_output_delta (2, text_end_label, prev_line_label, NULL);
+ }
+ else
+ {
+ dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+ dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+ dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, text_end_label, NULL);
+ }
+
+ dw2_asm_output_data (1, 0, "DW_LNE_end_sequence");
+ dw2_asm_output_data_uleb128 (1, NULL);
+ dw2_asm_output_data (1, DW_LNE_end_sequence, NULL);
+
+ function = 0;
+ current_file = 1;
+ current_line = 1;
+ for (lt_index = 0; lt_index < separate_line_info_table_in_use;)
+ {
+ dw_separate_line_info_ref line_info
+ = &separate_line_info_table[lt_index];
+
+#if 0
+ /* Don't emit anything for redundant notes. */
+ if (line_info->dw_line_num == current_line
+ && line_info->dw_file_num == current_file
+ && line_info->function == function)
+ goto cont;
+#endif
+
+ /* Emit debug info for the address of the current line. If this is
+ a new function, or the first line of a function, then we need
+ to handle it differently. */
+ ASM_GENERATE_INTERNAL_LABEL (line_label, SEPARATE_LINE_CODE_LABEL,
+ lt_index);
+ if (function != line_info->function)
+ {
+ function = line_info->function;
+
+ /* Set the address register to the first line in the function. */
+ dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+ dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+ dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+ }
+ else
+ {
+ /* ??? See the DW_LNS_advance_pc comment above. */
+ if (0)
+ {
+ dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+ "DW_LNS_fixed_advance_pc");
+ dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+ }
+ else
+ {
+ dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+ dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+ dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+ }
+ }
+
+ strcpy (prev_line_label, line_label);
+
+ /* Emit debug info for the source file of the current line, if
+ different from the previous line. */
+ if (line_info->dw_file_num != current_file)
+ {
+ current_file = line_info->dw_file_num;
+ dw2_asm_output_data (1, DW_LNS_set_file, "DW_LNS_set_file");
+ dw2_asm_output_data_uleb128 (current_file, "%lu", current_file);
+ }
+
+ /* Emit debug info for the current line number, choosing the encoding
+ that uses the least amount of space. */
+ if (line_info->dw_line_num != current_line)
+ {
+ line_offset = line_info->dw_line_num - current_line;
+ line_delta = line_offset - DWARF_LINE_BASE;
+ current_line = line_info->dw_line_num;
+ if (line_delta >= 0 && line_delta < (DWARF_LINE_RANGE - 1))
+ dw2_asm_output_data (1, DWARF_LINE_OPCODE_BASE + line_delta,
+ "line %lu", current_line);
+ else
+ {
+ dw2_asm_output_data (1, DW_LNS_advance_line,
+ "advance to line %lu", current_line);
+ dw2_asm_output_data_sleb128 (line_offset, NULL);
+ dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+ }
+ }
+ else
+ dw2_asm_output_data (1, DW_LNS_copy, "DW_LNS_copy");
+
+#if 0
+ cont:
+#endif
+
+ lt_index++;
+
+ /* If we're done with a function, end its sequence. */
+ if (lt_index == separate_line_info_table_in_use
+ || separate_line_info_table[lt_index].function != function)
+ {
+ current_file = 1;
+ current_line = 1;
+
+ /* Emit debug info for the address of the end of the function. */
+ ASM_GENERATE_INTERNAL_LABEL (line_label, FUNC_END_LABEL, function);
+ if (0)
+ {
+ dw2_asm_output_data (1, DW_LNS_fixed_advance_pc,
+ "DW_LNS_fixed_advance_pc");
+ dw2_asm_output_delta (2, line_label, prev_line_label, NULL);
+ }
+ else
+ {
+ dw2_asm_output_data (1, 0, "DW_LNE_set_address");
+ dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL);
+ dw2_asm_output_data (1, DW_LNE_set_address, NULL);
+ dw2_asm_output_addr (DWARF2_ADDR_SIZE, line_label, NULL);
+ }
+
+ /* Output the marker for the end of this sequence. */
+ dw2_asm_output_data (1, 0, "DW_LNE_end_sequence");
+ dw2_asm_output_data_uleb128 (1, NULL);
+ dw2_asm_output_data (1, DW_LNE_end_sequence, NULL);
+ }
+ }
+
+ /* Output the marker for the end of the line number info. */
+ ASM_OUTPUT_LABEL (asm_out_file, l2);
+}
+
+/* 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++)
+ {
+ 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);
+ }
+
+ 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++)
+ {
+ /* 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");
+ }
+}
+\f
+/* 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;
+
+ /* Header: version + pointer size. */
+ size = 2 + 1;
+
+ /* 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);
+
+ return size;
+}
+
+/* Output the virtual call table used to disambiguate PC values when
+ identical function have been merged. */
+
+static void
+output_vcall_table (void)
+{
+ unsigned i;
+ unsigned long vcall_length = size_of_vcall_table ();
+ vcall_entry *p;
+ char poc_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ 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)");
+
+ for (i = 0; VEC_iterate (vcall_entry, vcall_table, i, p); 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");
+ }
+}
+\f
+/* 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 (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);
+
+ /* This probably indicates a bug. */
+ if (! TYPE_NAME (type))
+ add_name_attribute (base_type_result, "__unknown__");
+
+ add_AT_unsigned (base_type_result, DW_AT_byte_size,
+ int_size_in_bytes (type));
+ add_AT_unsigned (base_type_result, DW_AT_encoding, encoding);
+
+ return base_type_result;
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return 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 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);
+}
+
+/* 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;
+
+ /* See if we already have the appropriately qualified variant of
+ this type. */
+ qualified_type
+ = get_qualified_type (type,
+ ((is_const_type ? TYPE_QUAL_CONST : 0)
+ | (is_volatile_type ? TYPE_QUAL_VOLATILE : 0)));
+
+ /* If we do, then we can just use its DIE, if it exists. */
+ if (qualified_type)
+ {
+ mod_type_die = lookup_type_die (qualified_type);
+ if (mod_type_die)
+ return mod_type_die;
+ }
+
+ name = qualified_type ? TYPE_NAME (qualified_type) : NULL;
+
+ /* Handle C typedef types. */
+ if (name && TREE_CODE (name) == TYPE_DECL && DECL_ORIGINAL_TYPE (name))
+ {
+ tree dtype = TREE_TYPE (name);
+
+ if (qualified_type == dtype)
+ {
+ /* For a named type, use the typedef. */
+ gen_type_die (qualified_type, context_die);
+ return lookup_type_die (qualified_type);
+ }
+ else if (is_const_type < TYPE_READONLY (dtype)
+ || is_volatile_type < TYPE_VOLATILE (dtype)
+ || (is_const_type <= TYPE_READONLY (dtype)
+ && is_volatile_type <= TYPE_VOLATILE (dtype)
+ && DECL_ORIGINAL_TYPE (name) != type))
+ /* cv-unqualified version of named type. Just use the unnamed
+ type to which it refers. */
+ return modified_type_die (DECL_ORIGINAL_TYPE (name),
+ is_const_type, is_volatile_type,
+ context_die);
+ /* Else cv-qualified version of named type; fall through. */
+ }
+
+ if (is_const_type)
+ {
+ mod_type_die = new_die (DW_TAG_const_type, comp_unit_die, type);
+ sub_die = modified_type_die (type, 0, is_volatile_type, context_die);
+ }
+ else if (is_volatile_type)
+ {
+ mod_type_die = new_die (DW_TAG_volatile_type, comp_unit_die, type);
+ sub_die = modified_type_die (type, 0, 0, context_die);
+ }
+ 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)
+ {
+ 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);
+
+ /* We have to get the type_main_variant here (and pass that to the
+ `lookup_type_die' routine) because the ..._TYPE node we have
+ might simply be a *copy* of some original type node (where the
+ copy was created to help us keep track of typedef names) and
+ that copy might have a different TYPE_UID from the original
+ ..._TYPE node. */
+ if (TREE_CODE (type) != VECTOR_TYPE)
+ return lookup_type_die (type_main_variant (type));
+ else
+ /* Vectors have the debugging information in the type,
+ not the main variant. */
+ return lookup_type_die (type);
+ }
+
+ /* Builtin types don't have a DECL_ORIGINAL_TYPE. For those,
+ don't output a DW_TAG_typedef, since there isn't one in the
+ user's program; just attach a DW_AT_name to the type.
+ 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))))
+ {
+ if (TREE_CODE (name) == TYPE_DECL)
+ /* Could just call add_name_and_src_coords_attributes here,
+ but since this is a builtin type it doesn't have any
+ useful source coordinates anyway. */
+ name = DECL_NAME (name);
+ add_name_attribute (mod_type_die, IDENTIFIER_POINTER (name));
+ }
+
+ if (qualified_type)
+ equate_type_number_to_die (qualified_type, mod_type_die);
+
+ if (item_type)
+ /* We must do this after the equate_type_number_to_die call, in case
+ this is a recursive type. This ensures that the modified_type_die
+ recursion will terminate even if the type is recursive. Recursive
+ types are possible in Ada. */
+ sub_die = modified_type_die (item_type,
+ TYPE_READONLY (item_type),
+ TYPE_VOLATILE (item_type),
+ context_die);
+
+ if (sub_die != NULL)
+ add_AT_die_ref (mod_type_die, DW_AT_type, sub_die);
+
+ return mod_type_die;
+}
+
+/* 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
+gen_generic_params_dies (tree t)
+{
+ tree parms, args;
+ int parms_num, i;
+ dw_die_ref die = NULL;
+
+ 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;
+
+ 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;
+
+ 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);
+ }
+ }
+}
+
+/* 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
+generic_parameter_die (tree parm, tree arg,
+ bool emit_name_p,
+ dw_die_ref parent_die)
+{
+ dw_die_ref tmpl_die = NULL;
+ const char *name = NULL;
+
+ if (!parm || !DECL_NAME (parm) || !arg)
+ 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)
+ {
+ 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)
+ {
+ name = IDENTIFIER_POINTER (DECL_NAME (parm));
+ gcc_assert (name);
+ add_AT_string (tmpl_die, DW_AT_name, name);
+ }
+
+ 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
+ {
+ /* 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. */
+
+ /* 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 (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);
+ }
+
+ return tmpl_die;
+}
+
+/* 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. */
+
+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;
+
+ gcc_assert (parent_die && parm_pack);
+
+ 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 true if it is
+ an enumerated type. */
+
+static inline int
+type_is_enum (const_tree type)
+{
+ return TREE_CODE (type) == ENUMERAL_TYPE;
+}
+
+/* Return the DBX register number described by a given RTL node. */
+
+static unsigned int
+dbx_reg_number (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 DBX_REGISTER_NUMBER (regno);
+}
+
+/* Optionally add a DW_OP_piece term to a location description expression.
+ DW_OP_piece is only added if the location description expression already
+ doesn't end with DW_OP_piece. */
+
+static void
+add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size)
+{
+ dw_loc_descr_ref loc;
+
+ if (*list_head != NULL)
+ {
+ /* Find the end of the chain. */
+ for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next)
+ ;
+
+ if (loc->dw_loc_opc != DW_OP_piece)
+ loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0);
+ }
+}
+
+/* Return a location descriptor that designates a machine register or
+ zero if there is none. */
+
+static dw_loc_descr_ref
+reg_loc_descriptor (rtx rtl, enum var_init_status initialized)
+{
+ rtx regs;
+
+ if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)
+ return 0;
+
+ regs = targetm.dwarf_register_span (rtl);
+
+ 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);
+}
+
+/* Return a location descriptor that designates a machine register for
+ a given hard register number. */
+
+static dw_loc_descr_ref
+one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized)
+{
+ dw_loc_descr_ref reg_loc_descr;
+
+ if (regno <= 31)
+ reg_loc_descr
+ = new_loc_descr ((enum dwarf_location_atom) (DW_OP_reg0 + regno), 0, 0);
+ else
+ reg_loc_descr = new_loc_descr (DW_OP_regx, regno, 0);
+
+ if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+ add_loc_descr (®_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+ return reg_loc_descr;
+}
+
+/* Given an RTL of a register, return a location descriptor that
+ designates a value that spans more than one register. */
+
+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;
+
+ reg = REGNO (rtl);
+#ifdef LEAF_REG_REMAP
+ if (current_function_uses_only_leaf_regs)
+ {
+ int leaf_reg = LEAF_REG_REMAP (reg);
+ if (leaf_reg != -1)
+ reg = (unsigned) leaf_reg;
+ }
+#endif
+ gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl));
+ nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)];
+
+ /* Simple, contiguous registers. */
+ if (regs == NULL_RTX)
+ {
+ size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs;
+
+ loc_result = NULL;
+ while (nregs--)
+ {
+ dw_loc_descr_ref t;
+
+ 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;
+ }
+
+ /* Now onto stupid register sets in non contiguous locations. */
+
+ gcc_assert (GET_CODE (regs) == PARALLEL);
+
+ size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0)));
+ loc_result = NULL;
+
+ for (i = 0; i < XVECLEN (regs, 0); ++i)
+ {
+ dw_loc_descr_ref t;
+
+ 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 (loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+ add_loc_descr (&loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+ return loc_result;
+}
+
+#endif /* DWARF2_DEBUGGING_INFO */
+
+#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+
+/* Return a location descriptor that designates a constant. */
+
+static dw_loc_descr_ref
+int_loc_descriptor (HOST_WIDE_INT i)
+{
+ enum dwarf_location_atom op;
+
+ /* Pick the smallest representation of a constant, rather than just
+ defaulting to the LEB encoding. */
+ if (i >= 0)
+ {
+ if (i <= 31)
+ op = (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;
+ }
+ else
+ {
+ if (i >= -0x80)
+ op = DW_OP_const1s;
+ else if (i >= -0x8000)
+ op = DW_OP_const2s;
+ else if (HOST_BITS_PER_WIDE_INT == 32
+ || i >= -0x80000000)
+ op = DW_OP_const4s;
+ else
+ op = DW_OP_consts;
+ }
+
+ return new_loc_descr (op, i, 0);
+}
+#endif
+
+#ifdef DWARF2_DEBUGGING_INFO
+/* Return loc description representing "address" of integer value.
+ This can appear only as toplevel expression. */
+
+static dw_loc_descr_ref
+address_of_int_loc_descriptor (int size, HOST_WIDE_INT i)
+{
+ int litsize;
+ dw_loc_descr_ref loc_result = NULL;
+
+ if (!(dwarf_version >= 4 || !dwarf_strict))
+ return NULL;
+
+ if (i >= 0)
+ {
+ 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);
+ }
+ else
+ {
+ if (i >= -0x80)
+ litsize = 2;
+ else if (i >= -0x8000)
+ litsize = 3;
+ else if (HOST_BITS_PER_WIDE_INT == 32
+ || i >= -0x80000000)
+ litsize = 5;
+ 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;
+ }
+
+ 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 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 = current_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)
+ {
+ 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));
+
+ /* 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);
+ }
+
+ offset += frame_pointer_fb_offset;
+ return new_loc_descr (DW_OP_fbreg, offset, 0);
+ }
+ }
+ else if (!optimize
+ && fde
+ && (fde->drap_reg == REGNO (reg)
+ || fde->vdrap_reg == REGNO (reg)))
+ {
+ /* 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);
+ }
+
+ regno = dbx_reg_number (reg);
+ 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);
+
+ if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+ add_loc_descr (&result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+ return result;
+}
+
+/* Return true if this RTL expression describes a base+offset calculation. */
+
+static inline int
+is_based_loc (const_rtx rtl)
+{
+ return (GET_CODE (rtl) == PLUS
+ && ((REG_P (XEXP (rtl, 0))
+ && REGNO (XEXP (rtl, 0)) < FIRST_PSEUDO_REGISTER
+ && CONST_INT_P (XEXP (rtl, 1)))));
+}
+
+/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0)
+ failed. */
+
+static dw_loc_descr_ref
+tls_mem_loc_descriptor (rtx mem)
+{
+ tree base;
+ dw_loc_descr_ref loc_result;
+
+ if (MEM_EXPR (mem) == NULL_TREE || MEM_OFFSET (mem) == NULL_RTX)
+ return NULL;
+
+ base = get_base_address (MEM_EXPR (mem));
+ if (base == NULL
+ || TREE_CODE (base) != VAR_DECL
+ || !DECL_THREAD_LOCAL_P (base))
+ return NULL;
+
+ loc_result = loc_descriptor_from_tree (MEM_EXPR (mem), 1);
+ if (loc_result == NULL)
+ return NULL;
+
+ if (INTVAL (MEM_OFFSET (mem)))
+ loc_descr_plus_const (&loc_result, INTVAL (MEM_OFFSET (mem)));
+
+ return loc_result;
+}
+
+/* Output debug info about reason why we failed to expand expression as dwarf
+ expression. */
+
+static void
+expansion_failed (tree expr, rtx rtl, char const *reason)
+{
+ 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);
+ }
+}
+
+/* Helper function for const_ok_for_output, called either directly
+ or via for_each_rtx. */
+
+static int
+const_ok_for_output_1 (rtx *rtlp, void *data ATTRIBUTE_UNUSED)
+{
+ rtx rtl = *rtlp;
+
+ 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;
+ }
+
+ if (GET_CODE (rtl) != SYMBOL_REF)
+ return 0;
+
+ if (CONSTANT_POOL_ADDRESS_P (rtl))
+ {
+ 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;
+ }
+ }
+
+ 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))
+ {
+ 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 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 bool
+const_ok_for_output (rtx rtl)
+{
+ 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;
+}
+
+/* The following routine converts the RTL for a variable or parameter
+ (resident in memory) into an equivalent Dwarf representation of a
+ mechanism for getting the address of that same variable onto the top of a
+ hypothetical "address evaluation" stack.
+
+ When creating memory location descriptors, we are effectively transforming
+ the RTL for a memory-resident object into its Dwarf postfix expression
+ equivalent. This routine recursively descends an RTL tree, turning
+ it into Dwarf postfix code as it goes.
+
+ MODE is the mode of the memory reference, needed to handle some
+ autoincrement addressing modes.
+
+ CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the
+ location list for RTL.
+
+ Return 0 if we can't represent the location. */
+
+static dw_loc_descr_ref
+mem_loc_descriptor (rtx rtl, enum machine_mode 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;
+
+ /* Note that for a dynamically sized array, the location we will generate a
+ description of here will be the lowest numbered location which is
+ actually within the array. That's *not* necessarily the same as the
+ zeroth element of the array. */
+
+ rtl = targetm.delegitimize_address (rtl);
+
+ switch (GET_CODE (rtl))
+ {
+ case POST_INC:
+ case POST_DEC:
+ case POST_MODIFY:
+ return mem_loc_descriptor (XEXP (rtl, 0), mode, initialized);
+
+ case SUBREG:
+ /* The case of a subreg may arise when we have a local (register)
+ variable or a formal (register) parameter which doesn't quite fill
+ up an entire register. For now, just assume that it is
+ legitimate to make the Dwarf info refer to the whole register which
+ contains the given subreg. */
+ if (!subreg_lowpart_p (rtl))
+ break;
+ rtl = SUBREG_REG (rtl);
+ if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE)
+ break;
+ if (GET_MODE_CLASS (GET_MODE (rtl)) != MODE_INT)
+ break;
+ mem_loc_result = mem_loc_descriptor (rtl, mode, initialized);
+ break;
+
+ case REG:
+ /* Whenever a register number forms a part of the description of the
+ method for calculating the (dynamic) address of a memory resident
+ object, DWARF rules require the register number be referred to as
+ a "base register". This distinction is not based in any way upon
+ what category of register the hardware believes the given register
+ belongs to. This is strictly DWARF terminology we're dealing with
+ here. Note that in cases where the location of a memory-resident
+ data object could be expressed as: OP_ADD (OP_BASEREG (basereg),
+ OP_CONST (0)) the actual DWARF location descriptor that we generate
+ may just be OP_BASEREG (basereg). This may look deceptively like
+ the object in question was allocated to a register (rather than in
+ memory) so DWARF consumers need to be aware of the subtle
+ distinction between OP_REG and OP_BASEREG. */
+ if (REGNO (rtl) < FIRST_PSEUDO_REGISTER)
+ mem_loc_result = based_loc_descr (rtl, 0, VAR_INIT_STATUS_INITIALIZED);
+ else if (stack_realign_drap
+ && crtl->drap_reg
+ && crtl->args.internal_arg_pointer == rtl
+ && REGNO (crtl->drap_reg) < FIRST_PSEUDO_REGISTER)
+ {
+ /* If RTL is internal_arg_pointer, which has been optimized
+ out, use DRAP instead. */
+ mem_loc_result = based_loc_descr (crtl->drap_reg, 0,
+ VAR_INIT_STATUS_INITIALIZED);
+ }
+ break;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (op0 == 0)
+ break;
+ else
+ {
+ int shift = DWARF2_ADDR_SIZE
+ - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0)));
+ shift *= BITS_PER_UNIT;
+ if (GET_CODE (rtl) == SIGN_EXTEND)
+ op = DW_OP_shra;
+ else
+ op = DW_OP_shr;
+ mem_loc_result = op0;
+ add_loc_descr (&mem_loc_result, int_loc_descriptor (shift));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_shl, 0, 0));
+ add_loc_descr (&mem_loc_result, int_loc_descriptor (shift));
+ add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+ }
+ break;
+
+ case MEM:
+ mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+ VAR_INIT_STATUS_INITIALIZED);
+ if (mem_loc_result == NULL)
+ mem_loc_result = tls_mem_loc_descriptor (rtl);
+ if (mem_loc_result != 0)
+ {
+ if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE)
+ {
+ expansion_failed (NULL_TREE, rtl, "DWARF address size mismatch");
+ return 0;
+ }
+ else if (GET_MODE_SIZE (GET_MODE (rtl)) == 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);
+ }
+ break;
+
+ case LO_SUM:
+ rtl = XEXP (rtl, 1);
+
+ /* ... fall through ... */
+
+ case LABEL_REF:
+ /* Some ports can transform a symbol ref into a label ref, because
+ the symbol ref is too far away and has to be dumped into a constant
+ pool. */
+ case CONST:
+ case SYMBOL_REF:
+ if (GET_CODE (rtl) == SYMBOL_REF
+ && SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE)
+ {
+ dw_loc_descr_ref temp;
+
+ /* If this is not defined, we have no way to emit the data. */
+ if (!targetm.have_tls || !targetm.asm_out.output_dwarf_dtprel)
+ break;
+
+ temp = new_loc_descr (DW_OP_addr, 0, 0);
+ temp->dw_loc_oprnd1.val_class = dw_val_class_addr;
+ temp->dw_loc_oprnd1.v.val_addr = rtl;
+ temp->dtprel = true;
+
+ mem_loc_result = new_loc_descr (DW_OP_GNU_push_tls_address, 0, 0);
+ add_loc_descr (&mem_loc_result, temp);
+
+ break;
+ }
+
+ if (!const_ok_for_output (rtl))
+ break;
+
+ symref:
+ mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+ mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+ mem_loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+ VEC_safe_push (rtx, gc, used_rtx_array, rtl);
+ break;
+
+ case CONCAT:
+ case CONCATN:
+ case VAR_LOCATION:
+ expansion_failed (NULL_TREE, rtl,
+ "CONCAT/CONCATN/VAR_LOCATION is handled only by loc_descriptor");
+ return 0;
+
+ case PRE_MODIFY:
+ /* Extract the PLUS expression nested inside and fall into
+ PLUS code below. */
+ rtl = XEXP (rtl, 1);
+ goto plus;
+
+ case PRE_INC:
+ case PRE_DEC:
+ /* Turn these into a PLUS expression and fall into the PLUS code
+ below. */
+ rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0),
+ GEN_INT (GET_CODE (rtl) == PRE_INC
+ ? GET_MODE_UNIT_SIZE (mode)
+ : -GET_MODE_UNIT_SIZE (mode)));
- if (sub_die != NULL)
- add_AT_die_ref (mod_type_die, DW_AT_type, sub_die);
+ /* ... fall through ... */
- return mod_type_die;
-}
+ case PLUS:
+ plus:
+ if (is_based_loc (rtl))
+ 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,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (mem_loc_result == 0)
+ break;
-/* Given a pointer to an arbitrary ..._TYPE tree node, return true if it is
- an enumerated type. */
+ if (CONST_INT_P (XEXP (rtl, 1)))
+ 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)
+ break;
+ add_loc_descr (&mem_loc_result, mem_loc_result2);
+ add_loc_descr (&mem_loc_result,
+ new_loc_descr (DW_OP_plus, 0, 0));
+ }
+ }
+ break;
-static inline int
-type_is_enum (const_tree type)
-{
- return TREE_CODE (type) == ENUMERAL_TYPE;
-}
+ /* If a pseudo-reg is optimized away, it is possible for it to
+ be replaced with a MEM containing a multiply or shift. */
+ case MINUS:
+ op = DW_OP_minus;
+ goto do_binop;
-/* Return the DBX register number described by a given RTL node. */
+ case MULT:
+ op = DW_OP_mul;
+ goto do_binop;
-static unsigned int
-dbx_reg_number (const_rtx rtl)
-{
- unsigned regno = REGNO (rtl);
+ case DIV:
+ op = DW_OP_div;
+ goto do_binop;
- gcc_assert (regno < FIRST_PSEUDO_REGISTER);
+ case UMOD:
+ op = DW_OP_mod;
+ goto do_binop;
-#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
+ case ASHIFT:
+ op = DW_OP_shl;
+ goto do_binop;
- return DBX_REGISTER_NUMBER (regno);
-}
+ case ASHIFTRT:
+ op = DW_OP_shra;
+ goto do_binop;
-/* 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. */
+ case LSHIFTRT:
+ op = DW_OP_shr;
+ goto do_binop;
-static void
-add_loc_descr_op_piece (dw_loc_descr_ref *list_head, int size)
-{
- dw_loc_descr_ref loc;
+ case AND:
+ op = DW_OP_and;
+ goto do_binop;
- if (*list_head != NULL)
- {
- /* Find the end of the chain. */
- for (loc = *list_head; loc->dw_loc_next != NULL; loc = loc->dw_loc_next)
- ;
+ case IOR:
+ op = DW_OP_or;
+ goto do_binop;
- if (loc->dw_loc_opc != DW_OP_piece)
- loc->dw_loc_next = new_loc_descr (DW_OP_piece, size, 0);
- }
-}
+ case XOR:
+ op = DW_OP_xor;
+ goto do_binop;
-/* Return a location descriptor that designates a machine register or
- zero if there is none. */
+ do_binop:
+ op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+ VAR_INIT_STATUS_INITIALIZED);
-static dw_loc_descr_ref
-reg_loc_descriptor (rtx rtl, enum var_init_status initialized)
-{
- rtx regs;
+ if (op0 == 0 || op1 == 0)
+ break;
- if (REGNO (rtl) >= FIRST_PSEUDO_REGISTER)
- return 0;
+ mem_loc_result = op0;
+ add_loc_descr (&mem_loc_result, op1);
+ add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+ break;
- regs = targetm.dwarf_register_span (rtl);
+ case MOD:
+ 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 (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);
-}
+ if (op0 == 0 || op1 == 0)
+ break;
-/* Return a location descriptor that designates a machine register for
- a given hard register number. */
+ mem_loc_result = op0;
+ add_loc_descr (&mem_loc_result, op1);
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_over, 0, 0));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_over, 0, 0));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_div, 0, 0));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_mul, 0, 0));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_minus, 0, 0));
+ break;
-static dw_loc_descr_ref
-one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized)
-{
- dw_loc_descr_ref reg_loc_descr;
+ case NOT:
+ op = DW_OP_not;
+ goto do_unop;
- 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);
+ case ABS:
+ op = DW_OP_abs;
+ goto do_unop;
- if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
- add_loc_descr (®_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+ case NEG:
+ op = DW_OP_neg;
+ goto do_unop;
- return reg_loc_descr;
-}
+ do_unop:
+ op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
-/* Given an RTL of a register, return a location descriptor that
- designates a value that spans more than one register. */
+ if (op0 == 0)
+ break;
-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;
+ mem_loc_result = op0;
+ add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+ break;
- reg = REGNO (rtl);
-#ifdef LEAF_REG_REMAP
- if (current_function_uses_only_leaf_regs)
- {
- int leaf_reg = LEAF_REG_REMAP (reg);
- if (leaf_reg != -1)
- reg = (unsigned) leaf_reg;
- }
-#endif
- gcc_assert ((unsigned) DBX_REGISTER_NUMBER (reg) == dbx_reg_number (rtl));
- nregs = hard_regno_nregs[REGNO (rtl)][GET_MODE (rtl)];
+ case CONST_INT:
+ mem_loc_result = int_loc_descriptor (INTVAL (rtl));
+ break;
- /* Simple, contiguous registers. */
- if (regs == NULL_RTX)
- {
- size = GET_MODE_SIZE (GET_MODE (rtl)) / nregs;
+ case EQ:
+ op = DW_OP_eq;
+ goto do_scompare;
- loc_result = NULL;
- while (nregs--)
- {
- dw_loc_descr_ref t;
+ case GE:
+ op = DW_OP_ge;
+ goto do_scompare;
- 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;
- }
+ case GT:
+ op = DW_OP_gt;
+ goto do_scompare;
- /* Now onto stupid register sets in non contiguous locations. */
+ case LE:
+ op = DW_OP_le;
+ goto do_scompare;
- gcc_assert (GET_CODE (regs) == PARALLEL);
+ case LT:
+ op = DW_OP_lt;
+ goto do_scompare;
- size = GET_MODE_SIZE (GET_MODE (XVECEXP (regs, 0, 0)));
- loc_result = NULL;
+ case NE:
+ op = DW_OP_ne;
+ goto do_scompare;
- for (i = 0; i < XVECLEN (regs, 0); ++i)
- {
- dw_loc_descr_ref t;
+ 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));
- 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 (op_mode == VOIDmode)
+ op_mode = GET_MODE (XEXP (rtl, 1));
+ if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
+ break;
- 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), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+ VAR_INIT_STATUS_INITIALIZED);
-#endif /* DWARF2_DEBUGGING_INFO */
+ if (op0 == 0 || op1 == 0)
+ break;
-#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
+ 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));
+ }
+ }
+ }
-/* Return a location descriptor that designates a constant. */
+ 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));
+ }
+ break;
-static dw_loc_descr_ref
-int_loc_descriptor (HOST_WIDE_INT i)
-{
- enum dwarf_location_atom op;
+ case GEU:
+ op = DW_OP_ge;
+ goto do_ucompare;
- /* Pick the smallest representation of a constant, rather than just
- defaulting to the LEB encoding. */
- if (i >= 0)
- {
- 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;
- }
- 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;
+ case GTU:
+ op = DW_OP_gt;
+ goto do_ucompare;
+
+ case LEU:
+ op = DW_OP_le;
+ goto do_ucompare;
+
+ 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
- op = DW_OP_consts;
- }
+ {
+ enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0));
- return new_loc_descr (op, i, 0);
-}
-#endif
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (XEXP (rtl, 1));
+ if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
+ break;
-#ifdef DWARF2_DEBUGGING_INFO
+ op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+ VAR_INIT_STATUS_INITIALIZED);
-/* Return a location descriptor that designates a base+offset location. */
+ if (op0 == 0 || op1 == 0)
+ break;
-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 = current_fde ();
+ 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;
+
+ 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;
- /* 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);
+ 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 (elim != reg)
+ if (op0 == 0 || op1 == 0)
+ 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_CODE (elim) == PLUS)
+ if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE)
{
- offset += INTVAL (XEXP (elim, 1));
- elim = XEXP (elim, 0);
+ 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));
}
- 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));
-
- /* 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
- && cfa.reg == HARD_FRAME_POINTER_REGNUM
- && reg == frame_pointer_rtx)
+ else
{
- int base_reg
- = DWARF_FRAME_REGNUM (cfa.indirect
- ? HARD_FRAME_POINTER_REGNUM
- : STACK_POINTER_REGNUM);
- return new_reg_loc_descr (base_reg, offset);
+ 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));
+ }
- offset += frame_pointer_fb_offset;
- return new_loc_descr (DW_OP_fbreg, offset, 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;
+ }
+ break;
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ if (CONST_INT_P (XEXP (rtl, 1))
+ && 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)
+ {
+ int shift, size;
+ op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (op0 == 0)
+ break;
+ if (GET_CODE (rtl) == SIGN_EXTRACT)
+ op = DW_OP_shra;
+ else
+ op = DW_OP_shr;
+ mem_loc_result = op0;
+ size = INTVAL (XEXP (rtl, 1));
+ shift = INTVAL (XEXP (rtl, 2));
+ if (BITS_BIG_ENDIAN)
+ shift = GET_MODE_BITSIZE (GET_MODE (XEXP (rtl, 0)))
+ - shift - size;
+ if (shift + size != (int) DWARF2_ADDR_SIZE)
+ {
+ add_loc_descr (&mem_loc_result,
+ int_loc_descriptor (DWARF2_ADDR_SIZE
+ - shift - size));
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_shl, 0, 0));
+ }
+ if (size != (int) DWARF2_ADDR_SIZE)
+ {
+ add_loc_descr (&mem_loc_result,
+ int_loc_descriptor (DWARF2_ADDR_SIZE - size));
+ add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+ }
}
- }
- else if (fde
- && fde->drap_reg != INVALID_REGNUM
- && (fde->drap_reg == REGNO (reg)
- || fde->vdrap_reg == REGNO (reg)))
- {
- /* Use cfa+offset to represent the location of arguments passed
- on stack when drap is used to align stack. */
- return new_loc_descr (DW_OP_fbreg, offset, 0);
- }
+ break;
- regno = dbx_reg_number (reg);
- 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);
+ case COMPARE:
+ case IF_THEN_ELSE:
+ case ROTATE:
+ case ROTATERT:
+ case TRUNCATE:
+ /* In theory, we could implement the above. */
+ /* DWARF cannot represent the unsigned compare operations
+ natively. */
+ case SS_MULT:
+ case US_MULT:
+ case SS_DIV:
+ case US_DIV:
+ case SS_PLUS:
+ case US_PLUS:
+ case SS_MINUS:
+ case US_MINUS:
+ case SS_NEG:
+ case US_NEG:
+ case SS_ABS:
+ case SS_ASHIFT:
+ case US_ASHIFT:
+ case SS_TRUNCATE:
+ case US_TRUNCATE:
+ case UDIV:
+ case UNORDERED:
+ case ORDERED:
+ case UNEQ:
+ case UNGE:
+ case UNGT:
+ 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:
+ case VEC_CONCAT:
+ case VEC_DUPLICATE:
+ case UNSPEC:
+ case HIGH:
+ /* 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. */
+ break;
- if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
- add_loc_descr (&result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+ case CONST_STRING:
+ resolve_one_addr (&rtl, NULL);
+ goto symref;
- return result;
+ default:
+#ifdef ENABLE_CHECKING
+ print_rtl (stderr, rtl);
+ gcc_unreachable ();
+#else
+ break;
+#endif
+ }
+
+ if (mem_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
+ add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+ return mem_loc_result;
}
-/* Return true if this RTL expression describes a base+offset calculation. */
+/* Return a descriptor that describes the concatenation of two locations.
+ This is typically a complex variable. */
-static inline int
-is_based_loc (const_rtx rtl)
+static dw_loc_descr_ref
+concat_loc_descriptor (rtx x0, rtx x1, enum var_init_status initialized)
{
- 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 cc_loc_result = NULL;
+ dw_loc_descr_ref x0_ref
+ = loc_descriptor (x0, VOIDmode, VAR_INIT_STATUS_INITIALIZED);
+ dw_loc_descr_ref x1_ref
+ = loc_descriptor (x1, VOIDmode, VAR_INIT_STATUS_INITIALIZED);
+
+ if (x0_ref == 0 || x1_ref == 0)
+ return 0;
+
+ cc_loc_result = x0_ref;
+ add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x0)));
+
+ add_loc_descr (&cc_loc_result, x1_ref);
+ add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x1)));
+
+ if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
+ add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+
+ return cc_loc_result;
}
-/* Return a descriptor that describes the concatenation of N locations
- used to form the address of a memory location. */
+/* Return a descriptor that describes the concatenation of N
+ locations. */
static dw_loc_descr_ref
-concatn_mem_loc_descriptor (rtx concatn, enum machine_mode mode,
- enum var_init_status initialized)
+concatn_loc_descriptor (rtx concatn, enum var_init_status initialized)
{
unsigned int i;
dw_loc_descr_ref cc_loc_result = NULL;
dw_loc_descr_ref ref;
rtx x = XVECEXP (concatn, 0, i);
- ref = mem_loc_descriptor (x, mode, VAR_INIT_STATUS_INITIALIZED);
+ ref = loc_descriptor (x, VOIDmode, VAR_INIT_STATUS_INITIALIZED);
if (ref == NULL)
return NULL;
return cc_loc_result;
}
-/* Try to handle TLS MEMs, for which mem_loc_descriptor on XEXP (mem, 0)
- failed. */
+/* Output a proper Dwarf location descriptor for a variable or parameter
+ which is either allocated in a register or in a memory location. For a
+ register, we just generate an OP_REG and the register number. For a
+ memory location we provide a Dwarf postfix expression describing how to
+ generate the (dynamic) address of the object onto the address stack.
+
+ MODE is mode of the decl if this loc_descriptor is going to be used in
+ .debug_loc section where DW_OP_stack_value and DW_OP_implicit_value are
+ allowed, VOIDmode otherwise.
+
+ If we don't know how to describe it, return 0. */
static dw_loc_descr_ref
-tls_mem_loc_descriptor (rtx mem)
+loc_descriptor (rtx rtl, enum machine_mode mode,
+ enum var_init_status initialized)
{
- tree base;
- dw_loc_descr_ref loc_result;
+ dw_loc_descr_ref loc_result = NULL;
- if (MEM_EXPR (mem) == NULL_TREE || MEM_OFFSET (mem) == NULL_RTX)
- return NULL;
+ switch (GET_CODE (rtl))
+ {
+ case SUBREG:
+ /* The case of a subreg may arise when we have a local (register)
+ variable or a formal (register) parameter which doesn't quite fill
+ up an entire register. For now, just assume that it is
+ legitimate to make the Dwarf info refer to the whole register which
+ contains the given subreg. */
+ loc_result = loc_descriptor (SUBREG_REG (rtl), mode, initialized);
+ break;
- base = get_base_address (MEM_EXPR (mem));
- if (base == NULL
- || TREE_CODE (base) != VAR_DECL
- || !DECL_THREAD_LOCAL_P (base))
- return NULL;
+ case REG:
+ loc_result = reg_loc_descriptor (rtl, initialized);
+ break;
- loc_result = loc_descriptor_from_tree_1 (MEM_EXPR (mem), 2);
- if (loc_result == NULL)
- return NULL;
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ loc_result = loc_descriptor (XEXP (rtl, 0), mode, initialized);
+ break;
- if (INTVAL (MEM_OFFSET (mem)))
- loc_descr_plus_const (&loc_result, INTVAL (MEM_OFFSET (mem)));
+ case MEM:
+ loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+ initialized);
+ if (loc_result == NULL)
+ loc_result = tls_mem_loc_descriptor (rtl);
+ if (loc_result == NULL)
+ {
+ rtx new_rtl = avoid_constant_pool_reference (rtl);
+ if (new_rtl != rtl)
+ loc_result = loc_descriptor (new_rtl, mode, initialized);
+ }
+ break;
- return loc_result;
-}
+ case CONCAT:
+ loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1),
+ initialized);
+ break;
-/* The following routine converts the RTL for a variable or parameter
- (resident in memory) into an equivalent Dwarf representation of a
- mechanism for getting the address of that same variable onto the top of a
- hypothetical "address evaluation" stack.
+ case CONCATN:
+ loc_result = concatn_loc_descriptor (rtl, initialized);
+ break;
- When creating memory location descriptors, we are effectively transforming
- the RTL for a memory-resident object into its Dwarf postfix expression
- equivalent. This routine recursively descends an RTL tree, turning
- it into Dwarf postfix code as it goes.
+ case VAR_LOCATION:
+ /* Single part. */
+ if (GET_CODE (PAT_VAR_LOCATION_LOC (rtl)) != PARALLEL)
+ {
+ rtx loc = PAT_VAR_LOCATION_LOC (rtl);
+ if (GET_CODE (loc) == EXPR_LIST)
+ loc = XEXP (loc, 0);
+ loc_result = loc_descriptor (loc, mode, initialized);
+ break;
+ }
+
+ rtl = XEXP (rtl, 1);
+ /* FALLTHRU */
+
+ case PARALLEL:
+ {
+ rtvec par_elems = XVEC (rtl, 0);
+ int num_elem = GET_NUM_ELEM (par_elems);
+ enum machine_mode mode;
+ int i;
+
+ /* Create the first one, so we have something to add to. */
+ loc_result = loc_descriptor (XEXP (RTVEC_ELT (par_elems, 0), 0),
+ VOIDmode, initialized);
+ if (loc_result == NULL)
+ return NULL;
+ mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, 0), 0));
+ add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
+ for (i = 1; i < num_elem; i++)
+ {
+ dw_loc_descr_ref temp;
+
+ temp = loc_descriptor (XEXP (RTVEC_ELT (par_elems, i), 0),
+ VOIDmode, initialized);
+ if (temp == NULL)
+ return NULL;
+ add_loc_descr (&loc_result, temp);
+ mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, i), 0));
+ add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
+ }
+ }
+ break;
+
+ case CONST_INT:
+ if (mode != VOIDmode && mode != BLKmode)
+ loc_result = address_of_int_loc_descriptor (GET_MODE_SIZE (mode),
+ INTVAL (rtl));
+ break;
+
+ case CONST_DOUBLE:
+ if (mode == VOIDmode)
+ mode = GET_MODE (rtl);
- MODE is the mode of the memory reference, needed to handle some
- autoincrement addressing modes.
+ if (mode != VOIDmode && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ gcc_assert (mode == GET_MODE (rtl) || VOIDmode == GET_MODE (rtl));
+
+ /* 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. */
+ loc_result = new_loc_descr (DW_OP_implicit_value,
+ GET_MODE_SIZE (mode), 0);
+ if (SCALAR_FLOAT_MODE_P (mode))
+ {
+ unsigned int length = GET_MODE_SIZE (mode);
+ unsigned char *array = GGC_NEWVEC (unsigned char, length);
+
+ insert_float (rtl, array);
+ loc_result->dw_loc_oprnd2.val_class = dw_val_class_vec;
+ loc_result->dw_loc_oprnd2.v.val_vec.length = length / 4;
+ loc_result->dw_loc_oprnd2.v.val_vec.elt_size = 4;
+ loc_result->dw_loc_oprnd2.v.val_vec.array = array;
+ }
+ else
+ {
+ loc_result->dw_loc_oprnd2.val_class = dw_val_class_const_double;
+ loc_result->dw_loc_oprnd2.v.val_double.high
+ = CONST_DOUBLE_HIGH (rtl);
+ loc_result->dw_loc_oprnd2.v.val_double.low
+ = CONST_DOUBLE_LOW (rtl);
+ }
+ }
+ break;
- CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the
- location list for RTL.
+ case CONST_VECTOR:
+ if (mode == VOIDmode)
+ mode = GET_MODE (rtl);
- Return 0 if we can't represent the location. */
+ if (mode != VOIDmode && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ unsigned int elt_size = GET_MODE_UNIT_SIZE (GET_MODE (rtl));
+ unsigned int length = CONST_VECTOR_NUNITS (rtl);
+ unsigned char *array = GGC_NEWVEC (unsigned char, length * elt_size);
+ unsigned int i;
+ unsigned char *p;
+
+ gcc_assert (mode == GET_MODE (rtl) || VOIDmode == GET_MODE (rtl));
+ switch (GET_MODE_CLASS (mode))
+ {
+ case MODE_VECTOR_INT:
+ for (i = 0, p = array; i < length; i++, p += elt_size)
+ {
+ rtx elt = CONST_VECTOR_ELT (rtl, i);
+ HOST_WIDE_INT lo, hi;
-static dw_loc_descr_ref
-mem_loc_descriptor (rtx rtl, enum machine_mode mode,
- enum var_init_status initialized)
-{
- dw_loc_descr_ref mem_loc_result = NULL;
- enum dwarf_location_atom op;
+ switch (GET_CODE (elt))
+ {
+ case CONST_INT:
+ lo = INTVAL (elt);
+ hi = -(lo < 0);
+ break;
- /* Note that for a dynamically sized array, the location we will generate a
- description of here will be the lowest numbered location which is
- actually within the array. That's *not* necessarily the same as the
- zeroth element of the array. */
+ case CONST_DOUBLE:
+ lo = CONST_DOUBLE_LOW (elt);
+ hi = CONST_DOUBLE_HIGH (elt);
+ break;
- rtl = targetm.delegitimize_address (rtl);
+ default:
+ gcc_unreachable ();
+ }
- switch (GET_CODE (rtl))
- {
- case POST_INC:
- case POST_DEC:
- case POST_MODIFY:
- /* POST_INC and POST_DEC can be handled just like a SUBREG. So we
- just fall into the SUBREG code. */
+ if (elt_size <= sizeof (HOST_WIDE_INT))
+ insert_int (lo, elt_size, p);
+ else
+ {
+ unsigned char *p0 = p;
+ unsigned char *p1 = p + sizeof (HOST_WIDE_INT);
+
+ gcc_assert (elt_size == 2 * sizeof (HOST_WIDE_INT));
+ if (WORDS_BIG_ENDIAN)
+ {
+ p0 = p1;
+ p1 = p;
+ }
+ insert_int (lo, sizeof (HOST_WIDE_INT), p0);
+ insert_int (hi, sizeof (HOST_WIDE_INT), p1);
+ }
+ }
+ break;
- /* ... fall through ... */
+ case MODE_VECTOR_FLOAT:
+ for (i = 0, p = array; i < length; i++, p += elt_size)
+ {
+ rtx elt = CONST_VECTOR_ELT (rtl, i);
+ insert_float (elt, p);
+ }
+ break;
- case SUBREG:
- /* The case of a subreg may arise when we have a local (register)
- variable or a formal (register) parameter which doesn't quite fill
- up an entire register. For now, just assume that it is
- legitimate to make the Dwarf info refer to the whole register which
- contains the given subreg. */
- rtl = XEXP (rtl, 0);
+ default:
+ gcc_unreachable ();
+ }
- /* ... fall through ... */
+ loc_result = new_loc_descr (DW_OP_implicit_value,
+ length * elt_size, 0);
+ loc_result->dw_loc_oprnd2.val_class = dw_val_class_vec;
+ loc_result->dw_loc_oprnd2.v.val_vec.length = length;
+ loc_result->dw_loc_oprnd2.v.val_vec.elt_size = elt_size;
+ loc_result->dw_loc_oprnd2.v.val_vec.array = array;
+ }
+ break;
- case REG:
- /* Whenever a register number forms a part of the description of the
- method for calculating the (dynamic) address of a memory resident
- object, DWARF rules require the register number be referred to as
- a "base register". This distinction is not based in any way upon
- what category of register the hardware believes the given register
- belongs to. This is strictly DWARF terminology we're dealing with
- here. Note that in cases where the location of a memory-resident
- data object could be expressed as: OP_ADD (OP_BASEREG (basereg),
- OP_CONST (0)) the actual DWARF location descriptor that we generate
- may just be OP_BASEREG (basereg). This may look deceptively like
- the object in question was allocated to a register (rather than in
- memory) so DWARF consumers need to be aware of the subtle
- distinction between OP_REG and OP_BASEREG. */
- if (REGNO (rtl) < FIRST_PSEUDO_REGISTER)
- mem_loc_result = based_loc_descr (rtl, 0, VAR_INIT_STATUS_INITIALIZED);
- else if (stack_realign_drap
- && crtl->drap_reg
- && crtl->args.internal_arg_pointer == rtl
- && REGNO (crtl->drap_reg) < FIRST_PSEUDO_REGISTER)
+ case CONST:
+ if (mode == VOIDmode
+ || GET_CODE (XEXP (rtl, 0)) == CONST_INT
+ || GET_CODE (XEXP (rtl, 0)) == CONST_DOUBLE
+ || GET_CODE (XEXP (rtl, 0)) == CONST_VECTOR)
{
- /* If RTL is internal_arg_pointer, which has been optimized
- out, use DRAP instead. */
- mem_loc_result = based_loc_descr (crtl->drap_reg, 0,
- VAR_INIT_STATUS_INITIALIZED);
+ loc_result = loc_descriptor (XEXP (rtl, 0), mode, initialized);
+ break;
+ }
+ /* FALLTHROUGH */
+ case SYMBOL_REF:
+ if (!const_ok_for_output (rtl))
+ break;
+ case LABEL_REF:
+ if (mode != VOIDmode && GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE
+ && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+ loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+ loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+ add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0));
+ VEC_safe_push (rtx, gc, used_rtx_array, rtl);
}
break;
- case MEM:
- mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
- VAR_INIT_STATUS_INITIALIZED);
- if (mem_loc_result == NULL)
- mem_loc_result = tls_mem_loc_descriptor (rtl);
- if (mem_loc_result != 0)
- add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0));
+ 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))
+ {
+ /* Value expression. */
+ loc_result = mem_loc_descriptor (rtl, VOIDmode, initialized);
+ if (loc_result)
+ add_loc_descr (&loc_result,
+ new_loc_descr (DW_OP_stack_value, 0, 0));
+ }
break;
+ }
- case LO_SUM:
- rtl = XEXP (rtl, 1);
+ return loc_result;
+}
- /* ... fall through ... */
+/* We need to figure out what section we should use as the base for the
+ address ranges where a given location is valid.
+ 1. If this particular DECL has a section associated with it, use that.
+ 2. If this function has a section associated with it, use that.
+ 3. Otherwise, use the text section.
+ XXX: If you split a variable across multiple sections, we won't notice. */
- case LABEL_REF:
- /* Some ports can transform a symbol ref into a label ref, because
- the symbol ref is too far away and has to be dumped into a constant
- pool. */
- case CONST:
- case SYMBOL_REF:
- /* Alternatively, the symbol in the constant pool might be referenced
- by a different symbol. */
- if (GET_CODE (rtl) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (rtl))
- {
- bool marked;
- rtx tmp = get_pool_constant_mark (rtl, &marked);
+static const char *
+secname_for_decl (const_tree decl)
+{
+ const char *secname;
- if (GET_CODE (tmp) == SYMBOL_REF)
- {
- rtl = tmp;
- if (CONSTANT_POOL_ADDRESS_P (tmp))
- get_pool_constant_mark (tmp, &marked);
- else
- marked = true;
- }
+ if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_SECTION_NAME (decl))
+ {
+ tree sectree = DECL_SECTION_NAME (decl);
+ secname = TREE_STRING_POINTER (sectree);
+ }
+ else if (current_function_decl && DECL_SECTION_NAME (current_function_decl))
+ {
+ tree sectree = DECL_SECTION_NAME (current_function_decl);
+ secname = TREE_STRING_POINTER (sectree);
+ }
+ else if (cfun && in_cold_section_p)
+ secname = crtl->subsections.cold_section_label;
+ else
+ secname = text_section_label;
- /* If all references to this pool constant were optimized away,
- it was not output and thus we can't represent it.
- FIXME: might try to use DW_OP_const_value here, though
- DW_OP_piece complicates it. */
- if (!marked)
- return 0;
- }
+ return secname;
+}
- mem_loc_result = new_loc_descr (DW_OP_addr, 0, 0);
- mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
- mem_loc_result->dw_loc_oprnd1.v.val_addr = rtl;
- VEC_safe_push (rtx, gc, used_rtx_array, rtl);
- break;
+/* Return true when DECL_BY_REFERENCE is defined and set for DECL. */
- case PRE_MODIFY:
- /* Extract the PLUS expression nested inside and fall into
- PLUS code below. */
- rtl = XEXP (rtl, 1);
- goto plus;
+static bool
+decl_by_reference_p (tree decl)
+{
+ return ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL
+ || TREE_CODE (decl) == VAR_DECL)
+ && DECL_BY_REFERENCE (decl));
+}
- case PRE_INC:
- case PRE_DEC:
- /* Turn these into a PLUS expression and fall into the PLUS code
- below. */
- rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0),
- GEN_INT (GET_CODE (rtl) == PRE_INC
- ? GET_MODE_UNIT_SIZE (mode)
- : -GET_MODE_UNIT_SIZE (mode)));
+/* Helper function for dw_loc_list. Compute proper Dwarf location descriptor
+ for VARLOC. */
- /* ... fall through ... */
+static dw_loc_descr_ref
+dw_loc_list_1 (tree loc, rtx varloc, int want_address,
+ enum var_init_status initialized)
+{
+ int have_address = 0;
+ dw_loc_descr_ref descr;
+ enum machine_mode mode;
- case PLUS:
- plus:
- if (is_based_loc (rtl))
- mem_loc_result = based_loc_descr (XEXP (rtl, 0),
- INTVAL (XEXP (rtl, 1)),
- VAR_INIT_STATUS_INITIALIZED);
- else
+ if (want_address != 2)
+ {
+ gcc_assert (GET_CODE (varloc) == VAR_LOCATION);
+ /* Single part. */
+ if (GET_CODE (PAT_VAR_LOCATION_LOC (varloc)) != PARALLEL)
{
- mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode,
- VAR_INIT_STATUS_INITIALIZED);
- if (mem_loc_result == 0)
- break;
-
- if (CONST_INT_P (XEXP (rtl, 1)))
- loc_descr_plus_const (&mem_loc_result, INTVAL (XEXP (rtl, 1)));
- else
+ varloc = PAT_VAR_LOCATION_LOC (varloc);
+ if (GET_CODE (varloc) == EXPR_LIST)
+ varloc = XEXP (varloc, 0);
+ mode = GET_MODE (varloc);
+ if (MEM_P (varloc))
{
- dw_loc_descr_ref mem_loc_result2
- = mem_loc_descriptor (XEXP (rtl, 1), mode,
- VAR_INIT_STATUS_INITIALIZED);
- if (mem_loc_result2 == 0)
- break;
- add_loc_descr (&mem_loc_result, mem_loc_result2);
- add_loc_descr (&mem_loc_result,
- new_loc_descr (DW_OP_plus, 0, 0));
+ rtx addr = XEXP (varloc, 0);
+ descr = mem_loc_descriptor (addr, 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);
+ }
}
+ else
+ descr = mem_loc_descriptor (varloc, mode, initialized);
}
- break;
-
- /* If a pseudo-reg is optimized away, it is possible for it to
- be replaced with a MEM containing a multiply or shift. */
- case MULT:
- op = DW_OP_mul;
- goto do_binop;
-
- case ASHIFT:
- op = DW_OP_shl;
- goto do_binop;
-
- case ASHIFTRT:
- op = DW_OP_shra;
- goto do_binop;
-
- case LSHIFTRT:
- op = DW_OP_shr;
- goto do_binop;
-
- do_binop:
- {
- dw_loc_descr_ref op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
- VAR_INIT_STATUS_INITIALIZED);
- dw_loc_descr_ref op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
- VAR_INIT_STATUS_INITIALIZED);
-
- if (op0 == 0 || op1 == 0)
- break;
+ else
+ return 0;
+ }
+ else
+ {
+ descr = loc_descriptor (varloc, DECL_MODE (loc), initialized);
+ have_address = 1;
+ }
- mem_loc_result = op0;
- add_loc_descr (&mem_loc_result, op1);
- add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
- break;
- }
+ if (!descr)
+ return 0;
- case CONST_INT:
- mem_loc_result = int_loc_descriptor (INTVAL (rtl));
- break;
+ if (want_address == 2 && !have_address
+ && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ if (int_size_in_bytes (TREE_TYPE (loc)) > DWARF2_ADDR_SIZE)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "DWARF address size mismatch");
+ return 0;
+ }
+ add_loc_descr (&descr, new_loc_descr (DW_OP_stack_value, 0, 0));
+ have_address = 1;
+ }
+ /* Show if we can't fill the request for an address. */
+ if (want_address && !have_address)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "Want address and only have value");
+ return 0;
+ }
- case CONCATN:
- mem_loc_result = concatn_mem_loc_descriptor (rtl, mode,
- VAR_INIT_STATUS_INITIALIZED);
- break;
+ /* If we've got an address and don't want one, dereference. */
+ if (!want_address && have_address)
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (loc));
+ enum dwarf_location_atom op;
- case UNSPEC:
- /* 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. */
- break;
+ if (size > DWARF2_ADDR_SIZE || size == -1)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "DWARF address size mismatch");
+ return 0;
+ }
+ else if (size == DWARF2_ADDR_SIZE)
+ op = DW_OP_deref;
+ else
+ op = DW_OP_deref_size;
- default:
- gcc_unreachable ();
+ add_loc_descr (&descr, new_loc_descr (op, size, 0));
}
- if (mem_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
- add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
-
- return mem_loc_result;
+ return descr;
}
-/* Return a descriptor that describes the concatenation of two locations.
- This is typically a complex variable. */
+/* Return the dwarf representation of the location list LOC_LIST of
+ DECL. WANT_ADDRESS has the same meaning as in loc_list_from_tree
+ function. */
-static dw_loc_descr_ref
-concat_loc_descriptor (rtx x0, rtx x1, enum var_init_status initialized)
+static dw_loc_list_ref
+dw_loc_list (var_loc_list *loc_list, tree decl, int want_address)
{
- dw_loc_descr_ref cc_loc_result = NULL;
- dw_loc_descr_ref x0_ref = loc_descriptor (x0, VAR_INIT_STATUS_INITIALIZED);
- dw_loc_descr_ref x1_ref = loc_descriptor (x1, VAR_INIT_STATUS_INITIALIZED);
-
- if (x0_ref == 0 || x1_ref == 0)
- return 0;
+ const char *endname, *secname;
+ rtx varloc;
+ enum var_init_status initialized;
+ struct var_loc_node *node;
+ dw_loc_descr_ref descr;
+ char label_id[MAX_ARTIFICIAL_LABEL_BYTES];
+ dw_loc_list_ref list = NULL;
+ dw_loc_list_ref *listp = &list;
+
+ /* Now that we know what section we are using for a base,
+ actually construct the list of locations.
+ The first location information is what is passed to the
+ function that creates the location list, and the remaining
+ locations just get added on to that list.
+ Note that we only know the start address for a location
+ (IE location changes), so to build the range, we use
+ the range [current location start, next location start].
+ This means we have to special case the last node, and generate
+ a range of [last location start, end of function label]. */
+
+ secname = secname_for_decl (decl);
+
+ for (node = loc_list->first; node->next; node = node->next)
+ if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
+ {
+ /* The variable has a location between NODE->LABEL and
+ NODE->NEXT->LABEL. */
+ initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+ varloc = NOTE_VAR_LOCATION (node->var_loc_note);
+ descr = dw_loc_list_1 (decl, varloc, want_address, initialized);
+ if (descr)
+ {
+ *listp = new_loc_list (descr, node->label, node->next->label,
+ secname);
+ listp = &(*listp)->dw_loc_next;
+ }
+ }
- cc_loc_result = x0_ref;
- add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x0)));
+ /* If the variable has a location at the last label
+ it keeps its location until the end of function. */
+ if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
+ {
+ initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
+ varloc = NOTE_VAR_LOCATION (node->var_loc_note);
+ descr = dw_loc_list_1 (decl, varloc, want_address, initialized);
+ if (descr)
+ {
+ if (!current_function_decl)
+ endname = text_end_label;
+ else
+ {
+ ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
+ current_function_funcdef_no);
+ endname = ggc_strdup (label_id);
+ }
- add_loc_descr (&cc_loc_result, x1_ref);
- add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x1)));
+ *listp = new_loc_list (descr, node->label, endname, secname);
+ listp = &(*listp)->dw_loc_next;
+ }
+ }
- if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
- add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+ /* Try to avoid the overhead of a location list emitting a location
+ expression instead, but only if we didn't have more than one
+ location entry in the first place. If some entries were not
+ representable, we don't want to pretend a single entry that was
+ applies to the entire scope in which the variable is
+ available. */
+ if (list && loc_list->first->next)
+ gen_llsym (list);
- return cc_loc_result;
+ return list;
}
-/* Return a descriptor that describes the concatenation of N
- locations. */
+/* Return if the loc_list has only single element and thus can be represented
+ as location description. */
-static dw_loc_descr_ref
-concatn_loc_descriptor (rtx concatn, enum var_init_status initialized)
+static bool
+single_element_loc_list_p (dw_loc_list_ref list)
{
- unsigned int i;
- dw_loc_descr_ref cc_loc_result = NULL;
- unsigned int n = XVECLEN (concatn, 0);
-
- for (i = 0; i < n; ++i)
- {
- dw_loc_descr_ref ref;
- rtx x = XVECEXP (concatn, 0, i);
+ gcc_assert (!list->dw_loc_next || list->ll_symbol);
+ return !list->ll_symbol;
+}
- ref = loc_descriptor (x, VAR_INIT_STATUS_INITIALIZED);
- if (ref == NULL)
- return NULL;
+/* To each location in list LIST add loc descr REF. */
- add_loc_descr (&cc_loc_result, ref);
- add_loc_descr_op_piece (&cc_loc_result, GET_MODE_SIZE (GET_MODE (x)));
+static void
+add_loc_descr_to_each (dw_loc_list_ref list, dw_loc_descr_ref ref)
+{
+ dw_loc_descr_ref copy;
+ add_loc_descr (&list->expr, ref);
+ list = list->dw_loc_next;
+ while (list)
+ {
+ copy = GGC_CNEW (dw_loc_descr_node);
+ memcpy (copy, ref, sizeof (dw_loc_descr_node));
+ add_loc_descr (&list->expr, copy);
+ while (copy->dw_loc_next)
+ {
+ dw_loc_descr_ref new_copy = GGC_CNEW (dw_loc_descr_node);
+ memcpy (new_copy, copy->dw_loc_next, sizeof (dw_loc_descr_node));
+ copy->dw_loc_next = new_copy;
+ copy = new_copy;
+ }
+ list = list->dw_loc_next;
}
+}
- if (cc_loc_result && initialized == VAR_INIT_STATUS_UNINITIALIZED)
- add_loc_descr (&cc_loc_result, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
+/* Given two lists RET and LIST
+ produce location list that is result of adding expression in LIST
+ to expression in RET on each possition in program.
+ Might be destructive on both RET and LIST.
- return cc_loc_result;
-}
+ TODO: We handle only simple cases of RET or LIST having at most one
+ element. General case would inolve sorting the lists in program order
+ and merging them that will need some additional work.
+ Adding that will improve quality of debug info especially for SRA-ed
+ structures. */
-/* Output a proper Dwarf location descriptor for a variable or parameter
- which is either allocated in a register or in a memory location. For a
- register, we just generate an OP_REG and the register number. For a
- memory location we provide a Dwarf postfix expression describing how to
- generate the (dynamic) address of the object onto the address stack.
+static void
+add_loc_list (dw_loc_list_ref *ret, dw_loc_list_ref list)
+{
+ if (!list)
+ return;
+ if (!*ret)
+ {
+ *ret = list;
+ return;
+ }
+ if (!list->dw_loc_next)
+ {
+ add_loc_descr_to_each (*ret, list->expr);
+ return;
+ }
+ if (!(*ret)->dw_loc_next)
+ {
+ add_loc_descr_to_each (list, (*ret)->expr);
+ *ret = list;
+ return;
+ }
+ expansion_failed (NULL_TREE, NULL_RTX,
+ "Don't know how to merge two non-trivial"
+ " location lists.\n");
+ *ret = NULL;
+ return;
+}
- If we don't know how to describe it, return 0. */
+/* LOC is constant expression. Try a luck, look it up in constant
+ pool and return its loc_descr of its address. */
static dw_loc_descr_ref
-loc_descriptor (rtx rtl, enum var_init_status initialized)
+cst_pool_loc_descr (tree loc)
{
- dw_loc_descr_ref loc_result = NULL;
+ /* Get an RTL for this, if something has been emitted. */
+ rtx rtl = lookup_constant_def (loc);
+ enum machine_mode mode;
- switch (GET_CODE (rtl))
+ if (!rtl || !MEM_P (rtl))
{
- case SUBREG:
- /* The case of a subreg may arise when we have a local (register)
- variable or a formal (register) parameter which doesn't quite fill
- up an entire register. For now, just assume that it is
- legitimate to make the Dwarf info refer to the whole register which
- contains the given subreg. */
- rtl = SUBREG_REG (rtl);
-
- /* ... fall through ... */
-
- case REG:
- loc_result = reg_loc_descriptor (rtl, initialized);
- break;
+ gcc_assert (!rtl);
+ return 0;
+ }
+ gcc_assert (GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF);
- case MEM:
- loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
- initialized);
- if (loc_result == NULL)
- loc_result = tls_mem_loc_descriptor (rtl);
- break;
+ /* TODO: We might get more coverage if we was actually delaying expansion
+ of all expressions till end of compilation when constant pools are fully
+ populated. */
+ if (!TREE_ASM_WRITTEN (SYMBOL_REF_DECL (XEXP (rtl, 0))))
+ {
+ expansion_failed (loc, NULL_RTX,
+ "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);
+}
- case CONCAT:
- loc_result = concat_loc_descriptor (XEXP (rtl, 0), XEXP (rtl, 1),
- initialized);
- break;
+/* Return dw_loc_list representing address of addr_expr LOC
+ by looking for innder INDIRECT_REF expression and turing it
+ into simple arithmetics. */
- case CONCATN:
- loc_result = concatn_loc_descriptor (rtl, initialized);
- break;
+static dw_loc_list_ref
+loc_list_for_address_of_addr_expr_of_indirect_ref (tree loc, bool toplev)
+{
+ tree obj, offset;
+ HOST_WIDE_INT bitsize, bitpos, bytepos;
+ enum machine_mode mode;
+ int volatilep;
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (loc));
+ dw_loc_list_ref list_ret = NULL, list_ret1 = NULL;
- case VAR_LOCATION:
- /* Single part. */
- if (GET_CODE (XEXP (rtl, 1)) != PARALLEL)
+ obj = get_inner_reference (TREE_OPERAND (loc, 0),
+ &bitsize, &bitpos, &offset, &mode,
+ &unsignedp, &volatilep, false);
+ STRIP_NOPS (obj);
+ if (bitpos % BITS_PER_UNIT)
+ {
+ expansion_failed (loc, NULL_RTX, "bitfield access");
+ return 0;
+ }
+ if (!INDIRECT_REF_P (obj))
+ {
+ expansion_failed (obj,
+ NULL_RTX, "no indirect ref in inner refrence");
+ return 0;
+ }
+ if (!offset && !bitpos)
+ list_ret = loc_list_from_tree (TREE_OPERAND (obj, 0), toplev ? 2 : 1);
+ else if (toplev
+ && int_size_in_bytes (TREE_TYPE (loc)) <= DWARF2_ADDR_SIZE
+ && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ list_ret = loc_list_from_tree (TREE_OPERAND (obj, 0), 0);
+ if (!list_ret)
+ return 0;
+ if (offset)
{
- loc_result = loc_descriptor (XEXP (XEXP (rtl, 1), 0), initialized);
- break;
+ /* Variable offset. */
+ list_ret1 = loc_list_from_tree (offset, 0);
+ if (list_ret1 == 0)
+ return 0;
+ add_loc_list (&list_ret, list_ret1);
+ if (!list_ret)
+ return 0;
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_plus, 0, 0));
}
-
- rtl = XEXP (rtl, 1);
- /* FALLTHRU */
-
- case PARALLEL:
- {
- rtvec par_elems = XVEC (rtl, 0);
- int num_elem = GET_NUM_ELEM (par_elems);
- enum machine_mode mode;
- int i;
-
- /* Create the first one, so we have something to add to. */
- loc_result = loc_descriptor (XEXP (RTVEC_ELT (par_elems, 0), 0),
- initialized);
- if (loc_result == NULL)
- return NULL;
- mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, 0), 0));
- add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
- for (i = 1; i < num_elem; i++)
- {
- dw_loc_descr_ref temp;
-
- temp = loc_descriptor (XEXP (RTVEC_ELT (par_elems, i), 0),
- initialized);
- if (temp == NULL)
- return NULL;
- add_loc_descr (&loc_result, temp);
- mode = GET_MODE (XEXP (RTVEC_ELT (par_elems, i), 0));
- add_loc_descr_op_piece (&loc_result, GET_MODE_SIZE (mode));
- }
- }
- break;
-
- default:
- gcc_unreachable ();
+ bytepos = bitpos / BITS_PER_UNIT;
+ if (bytepos > 0)
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_plus_uconst,
+ bytepos, 0));
+ else if (bytepos < 0)
+ loc_list_plus_const (list_ret, bytepos);
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_stack_value, 0, 0));
}
-
- return loc_result;
+ return list_ret;
}
-/* Similar, but generate the descriptor from trees instead of rtl. This comes
- up particularly with variable length arrays. WANT_ADDRESS is 2 if this is
- a top-level invocation of loc_descriptor_from_tree; is 1 if this is not a
- top-level invocation, and we require the address of LOC; is 0 if we require
- the value of LOC. */
-static dw_loc_descr_ref
-loc_descriptor_from_tree_1 (tree loc, int want_address)
+/* Generate Dwarf location list representing LOC.
+ If WANT_ADDRESS is false, expression computing LOC will be computed
+ If WANT_ADDRESS is 1, expression computing address of LOC will be returned
+ if WANT_ADDRESS is 2, expression computing address useable in location
+ will be returned (i.e. DW_OP_reg can be used
+ to refer to register values). */
+
+static dw_loc_list_ref
+loc_list_from_tree (tree loc, int want_address)
{
- dw_loc_descr_ref ret, ret1;
+ dw_loc_descr_ref ret = NULL, ret1 = NULL;
+ dw_loc_list_ref list_ret = NULL, list_ret1 = NULL;
int have_address = 0;
enum dwarf_location_atom op;
switch (TREE_CODE (loc))
{
case ERROR_MARK:
+ expansion_failed (loc, NULL_RTX, "ERROR_MARK");
return 0;
case PLACEHOLDER_EXPR:
position of other fields. We don't try to encode this here. The
only user of this is Ada, which encodes the needed information using
the names of types. */
+ expansion_failed (loc, NULL_RTX, "PLACEHOLDER_EXPR");
return 0;
case CALL_EXPR:
+ expansion_failed (loc, NULL_RTX, "CALL_EXPR");
+ /* There are no opcodes for these operations. */
return 0;
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
+ expansion_failed (loc, NULL_RTX, "PRE/POST INDCREMENT/DECREMENT");
/* There are no opcodes for these operations. */
return 0;
case ADDR_EXPR:
- /* If we already want an address, there's nothing we can do. */
+ /* If we already want an address, see if there is INDIRECT_REF inside
+ e.g. for &this->field. */
if (want_address)
- return 0;
-
- /* Otherwise, process the argument and look for the address. */
- return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 1);
+ {
+ list_ret = loc_list_for_address_of_addr_expr_of_indirect_ref
+ (loc, want_address == 2);
+ if (list_ret)
+ have_address = 1;
+ else if (decl_address_ip_invariant_p (TREE_OPERAND (loc, 0))
+ && (ret = cst_pool_loc_descr (loc)))
+ have_address = 1;
+ }
+ /* Otherwise, process the argument and look for the address. */
+ if (!list_ret && !ret)
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 1);
+ else
+ {
+ if (want_address)
+ expansion_failed (loc, NULL_RTX, "need address of ADDR_EXPR");
+ return NULL;
+ }
+ break;
case VAR_DECL:
if (DECL_THREAD_LOCAL_P (loc))
if (targetm.have_tls)
{
/* If this is not defined, we have no way to emit the
- data. */
+ data. */
if (!targetm.asm_out.output_dwarf_dtprel)
return 0;
}
else
{
- if (!targetm.emutls.debug_form_tls_address)
+ if (!targetm.emutls.debug_form_tls_address
+ || !(dwarf_version >= 3 || !dwarf_strict))
return 0;
loc = emutls_decl (loc);
first_op = DW_OP_addr;
case PARM_DECL:
if (DECL_HAS_VALUE_EXPR_P (loc))
- return loc_descriptor_from_tree_1 (DECL_VALUE_EXPR (loc),
- want_address);
+ return loc_list_from_tree (DECL_VALUE_EXPR (loc),
+ want_address);
/* FALLTHRU */
case RESULT_DECL:
case FUNCTION_DECL:
{
- rtx rtl = rtl_for_decl_location (loc);
+ rtx rtl;
+ var_loc_list *loc_list = lookup_decl_loc (loc);
+ if (loc_list && loc_list->first)
+ {
+ list_ret = dw_loc_list (loc_list, loc, want_address);
+ have_address = want_address != 0;
+ break;
+ }
+ rtl = rtl_for_decl_location (loc);
if (rtl == NULL_RTX)
- return 0;
+ {
+ expansion_failed (loc, NULL_RTX, "DECL has no RTL");
+ return 0;
+ }
else if (CONST_INT_P (rtl))
{
HOST_WIDE_INT val = INTVAL (rtl);
ret = int_loc_descriptor (val);
}
else if (GET_CODE (rtl) == CONST_STRING)
- return 0;
- else if (CONSTANT_P (rtl))
+ {
+ expansion_failed (loc, NULL_RTX, "CONST_STRING");
+ return 0;
+ }
+ else if (CONSTANT_P (rtl) && const_ok_for_output (rtl))
{
ret = new_loc_descr (DW_OP_addr, 0, 0);
ret->dw_loc_oprnd1.val_class = dw_val_class_addr;
/* Certain constructs can only be represented at top-level. */
if (want_address == 2)
- return loc_descriptor (rtl, VAR_INIT_STATUS_INITIALIZED);
-
- mode = GET_MODE (rtl);
- if (MEM_P (rtl))
{
- rtl = XEXP (rtl, 0);
+ ret = loc_descriptor (rtl, VOIDmode,
+ VAR_INIT_STATUS_INITIALIZED);
have_address = 1;
}
- ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+ else
+ {
+ mode = GET_MODE (rtl);
+ if (MEM_P (rtl))
+ {
+ rtl = XEXP (rtl, 0);
+ have_address = 1;
+ }
+ ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+ }
+ if (!ret)
+ expansion_failed (loc, rtl,
+ "failed to produce loc descriptor for rtl");
}
}
break;
case INDIRECT_REF:
- ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
have_address = 1;
break;
case COMPOUND_EXPR:
- return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), want_address);
+ return loc_list_from_tree (TREE_OPERAND (loc, 1), want_address);
CASE_CONVERT:
case VIEW_CONVERT_EXPR:
case SAVE_EXPR:
case MODIFY_EXPR:
- return loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), want_address);
+ return loc_list_from_tree (TREE_OPERAND (loc, 0), want_address);
case COMPONENT_REF:
case BIT_FIELD_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
{
tree obj, offset;
HOST_WIDE_INT bitsize, bitpos, bytepos;
obj = get_inner_reference (loc, &bitsize, &bitpos, &offset, &mode,
&unsignedp, &volatilep, false);
- if (obj == loc)
- return 0;
+ gcc_assert (obj != loc);
- ret = loc_descriptor_from_tree_1 (obj, 1);
- if (ret == 0
- || bitpos % BITS_PER_UNIT != 0 || bitsize % BITS_PER_UNIT != 0)
+ list_ret = loc_list_from_tree (obj,
+ want_address == 2
+ && !bitpos && !offset ? 2 : 1);
+ /* TODO: We can extract value of the small expression via shifting even
+ for nonzero bitpos. */
+ if (list_ret == 0)
return 0;
+ if (bitpos % BITS_PER_UNIT != 0 || bitsize % BITS_PER_UNIT != 0)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "bitfield access");
+ return 0;
+ }
if (offset != NULL_TREE)
{
/* Variable offset. */
- ret1 = loc_descriptor_from_tree_1 (offset, 0);
- if (ret1 == 0)
+ list_ret1 = loc_list_from_tree (offset, 0);
+ if (list_ret1 == 0)
+ return 0;
+ add_loc_list (&list_ret, list_ret1);
+ if (!list_ret)
return 0;
- add_loc_descr (&ret, ret1);
- add_loc_descr (&ret, new_loc_descr (DW_OP_plus, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_plus, 0, 0));
}
bytepos = bitpos / BITS_PER_UNIT;
- loc_descr_plus_const (&ret, bytepos);
+ if (bytepos > 0)
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_plus_uconst, bytepos, 0));
+ else if (bytepos < 0)
+ loc_list_plus_const (list_ret, bytepos);
have_address = 1;
break;
}
case INTEGER_CST:
- if (host_integerp (loc, 0))
+ if ((want_address || !host_integerp (loc, 0))
+ && (ret = cst_pool_loc_descr (loc)))
+ have_address = 1;
+ else if (want_address == 2
+ && host_integerp (loc, 0)
+ && (ret = address_of_int_loc_descriptor
+ (int_size_in_bytes (TREE_TYPE (loc)),
+ tree_low_cst (loc, 0))))
+ have_address = 1;
+ else if (host_integerp (loc, 0))
ret = int_loc_descriptor (tree_low_cst (loc, 0));
else
- return 0;
+ {
+ expansion_failed (loc, NULL_RTX,
+ "Integer operand is not host integer");
+ return 0;
+ }
break;
case CONSTRUCTOR:
- {
- /* Get an RTL for this, if something has been emitted. */
- rtx rtl = lookup_constant_def (loc);
- enum machine_mode mode;
-
- if (!rtl || !MEM_P (rtl))
- return 0;
- mode = GET_MODE (rtl);
- rtl = XEXP (rtl, 0);
- ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+ case REAL_CST:
+ case STRING_CST:
+ case COMPLEX_CST:
+ if ((ret = cst_pool_loc_descr (loc)))
have_address = 1;
- break;
- }
+ else
+ /* We can construct small constants here using int_loc_descriptor. */
+ expansion_failed (loc, NULL_RTX,
+ "constructor or constant not in constant pool");
+ break;
case TRUTH_AND_EXPR:
case TRUTH_ANDIF_EXPR:
case CEIL_DIV_EXPR:
case ROUND_DIV_EXPR:
case TRUNC_DIV_EXPR:
+ if (TYPE_UNSIGNED (TREE_TYPE (loc)))
+ return 0;
op = DW_OP_div;
goto do_binop;
case CEIL_MOD_EXPR:
case ROUND_MOD_EXPR:
case TRUNC_MOD_EXPR:
- op = DW_OP_mod;
- goto do_binop;
+ if (TYPE_UNSIGNED (TREE_TYPE (loc)))
+ {
+ op = DW_OP_mod;
+ goto do_binop;
+ }
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+ list_ret1 = loc_list_from_tree (TREE_OPERAND (loc, 1), 0);
+ if (list_ret == 0 || list_ret1 == 0)
+ return 0;
+
+ add_loc_list (&list_ret, list_ret1);
+ if (list_ret == 0)
+ return 0;
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_over, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_over, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_div, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_mul, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_minus, 0, 0));
+ break;
case MULT_EXPR:
op = DW_OP_mul;
if (TREE_CODE (TREE_OPERAND (loc, 1)) == INTEGER_CST
&& host_integerp (TREE_OPERAND (loc, 1), 0))
{
- ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
- if (ret == 0)
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+ if (list_ret == 0)
return 0;
- loc_descr_plus_const (&ret, tree_low_cst (TREE_OPERAND (loc, 1), 0));
+ loc_list_plus_const (list_ret, tree_low_cst (TREE_OPERAND (loc, 1), 0));
break;
}
goto do_binop;
do_binop:
- ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
- ret1 = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0);
- if (ret == 0 || ret1 == 0)
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+ list_ret1 = loc_list_from_tree (TREE_OPERAND (loc, 1), 0);
+ if (list_ret == 0 || list_ret1 == 0)
return 0;
- add_loc_descr (&ret, ret1);
- add_loc_descr (&ret, new_loc_descr (op, 0, 0));
+ add_loc_list (&list_ret, list_ret1);
+ if (list_ret == 0)
+ return 0;
+ add_loc_descr_to_each (list_ret, new_loc_descr (op, 0, 0));
break;
case TRUTH_NOT_EXPR:
goto do_unop;
do_unop:
- ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
- if (ret == 0)
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+ if (list_ret == 0)
return 0;
- add_loc_descr (&ret, new_loc_descr (op, 0, 0));
+ add_loc_descr_to_each (list_ret, new_loc_descr (op, 0, 0));
break;
case MIN_EXPR:
case COND_EXPR:
{
dw_loc_descr_ref lhs
- = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 1), 0);
- dw_loc_descr_ref rhs
- = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 2), 0);
+ = loc_descriptor_from_tree (TREE_OPERAND (loc, 1), 0);
+ dw_loc_list_ref rhs
+ = loc_list_from_tree (TREE_OPERAND (loc, 2), 0);
dw_loc_descr_ref bra_node, jump_node, tmp;
- ret = loc_descriptor_from_tree_1 (TREE_OPERAND (loc, 0), 0);
- if (ret == 0 || lhs == 0 || rhs == 0)
+ list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+ if (list_ret == 0 || lhs == 0 || rhs == 0)
return 0;
bra_node = new_loc_descr (DW_OP_bra, 0, 0);
- add_loc_descr (&ret, bra_node);
+ add_loc_descr_to_each (list_ret, bra_node);
- add_loc_descr (&ret, rhs);
+ add_loc_list (&list_ret, rhs);
jump_node = new_loc_descr (DW_OP_skip, 0, 0);
- add_loc_descr (&ret, jump_node);
+ add_loc_descr_to_each (list_ret, jump_node);
- add_loc_descr (&ret, lhs);
+ add_loc_descr_to_each (list_ret, lhs);
bra_node->dw_loc_oprnd1.val_class = dw_val_class_loc;
bra_node->dw_loc_oprnd1.v.val_loc = lhs;
/* ??? Need a node to point the skip at. Use a nop. */
tmp = new_loc_descr (DW_OP_nop, 0, 0);
- add_loc_descr (&ret, tmp);
+ add_loc_descr_to_each (list_ret, tmp);
jump_node->dw_loc_oprnd1.val_class = dw_val_class_loc;
jump_node->dw_loc_oprnd1.v.val_loc = tmp;
}
up, for instance, with the C STMT_EXPR. */
if ((unsigned int) TREE_CODE (loc)
>= (unsigned int) LAST_AND_UNUSED_TREE_CODE)
- return 0;
+ {
+ expansion_failed (loc, NULL_RTX,
+ "language specific tree node");
+ return 0;
+ }
#ifdef ENABLE_CHECKING
/* Otherwise this is a generic code; we should just lists all of
#endif
}
+ if (!ret && !list_ret)
+ return 0;
+
+ if (want_address == 2 && !have_address
+ && (dwarf_version >= 4 || !dwarf_strict))
+ {
+ if (int_size_in_bytes (TREE_TYPE (loc)) > DWARF2_ADDR_SIZE)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "DWARF address size mismatch");
+ return 0;
+ }
+ if (ret)
+ add_loc_descr (&ret, new_loc_descr (DW_OP_stack_value, 0, 0));
+ else
+ add_loc_descr_to_each (list_ret,
+ new_loc_descr (DW_OP_stack_value, 0, 0));
+ have_address = 1;
+ }
/* Show if we can't fill the request for an address. */
if (want_address && !have_address)
- return 0;
+ {
+ expansion_failed (loc, NULL_RTX,
+ "Want address and only have value");
+ return 0;
+ }
+
+ gcc_assert (!ret || !list_ret);
/* If we've got an address and don't want one, dereference. */
- if (!want_address && have_address && ret)
+ if (!want_address && have_address)
{
HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (loc));
if (size > DWARF2_ADDR_SIZE || size == -1)
- return 0;
+ {
+ expansion_failed (loc, NULL_RTX,
+ "DWARF address size mismatch");
+ return 0;
+ }
else if (size == DWARF2_ADDR_SIZE)
op = DW_OP_deref;
else
op = DW_OP_deref_size;
- add_loc_descr (&ret, new_loc_descr (op, size, 0));
+ if (ret)
+ add_loc_descr (&ret, new_loc_descr (op, size, 0));
+ else
+ add_loc_descr_to_each (list_ret, new_loc_descr (op, size, 0));
}
+ if (ret)
+ list_ret = new_loc_list (ret, NULL, NULL, NULL);
- return ret;
+ return list_ret;
}
-static inline dw_loc_descr_ref
-loc_descriptor_from_tree (tree loc)
+/* Same as above but return only single location expression. */
+static dw_loc_descr_ref
+loc_descriptor_from_tree (tree loc, int want_address)
{
- return loc_descriptor_from_tree_1 (loc, 2);
+ dw_loc_list_ref ret = loc_list_from_tree (loc, want_address);
+ if (!ret)
+ return NULL;
+ if (ret->dw_loc_next)
+ {
+ expansion_failed (loc, NULL_RTX,
+ "Location list where only loc descriptor needed");
+ return NULL;
+ }
+ return ret->expr;
}
/* Given a value, round it up to the lowest multiple of `boundary'
field_size_tree = DECL_SIZE (decl);
/* The size could be unspecified if there was an error, or for
- a flexible array member. */
+ a flexible array member. */
if (!field_size_tree)
- field_size_tree = bitsize_zero_node;
+ field_size_tree = bitsize_zero_node;
/* If the size of the field is not constant, use the type size. */
if (host_integerp (field_size_tree, 1))
static inline void
add_AT_location_description (dw_die_ref die, enum dwarf_attribute attr_kind,
- dw_loc_descr_ref descr)
+ dw_loc_list_ref descr)
{
- if (descr != 0)
- add_AT_loc (die, attr_kind, descr);
+ if (descr == 0)
+ return;
+ if (single_element_loc_list_p (descr))
+ add_AT_loc (die, attr_kind, descr->expr);
+ else
+ add_AT_loc_list (die, attr_kind, descr);
}
/* Attach the specialized form of location attribute used for data members of
if (! loc_descr)
{
- enum dwarf_location_atom op;
+ if (dwarf_version > 2)
+ {
+ /* Don't need to output a location expression, just the constant. */
+ add_AT_int (die, DW_AT_data_member_location, offset);
+ return;
+ }
+ else
+ {
+ enum dwarf_location_atom op;
- /* The DWARF2 standard says that we should assume that the structure
- address is already on the stack, so we can specify a structure field
- address by using DW_OP_plus_uconst. */
+ /* The DWARF2 standard says that we should assume that the structure
+ address is already on the stack, so we can specify a structure
+ field address by using DW_OP_plus_uconst. */
#ifdef MIPS_DEBUGGING_INFO
- /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst
- operator correctly. It works only if we leave the offset on the
- stack. */
- op = DW_OP_constu;
+ /* ??? The SGI dwarf reader does not handle the DW_OP_plus_uconst
+ operator correctly. It works only if we leave the offset on the
+ stack. */
+ op = DW_OP_constu;
#else
- op = DW_OP_plus_uconst;
+ op = DW_OP_plus_uconst;
#endif
- loc_descr = new_loc_descr (op, offset, 0);
+ loc_descr = new_loc_descr (op, offset, 0);
+ }
}
add_AT_loc (die, DW_AT_data_member_location, loc_descr);
to an inlined function. They can also arise in C++ where declared
constants do not necessarily get memory "homes". */
-static void
+static bool
add_const_value_attribute (dw_die_ref die, rtx rtl)
{
switch (GET_CODE (rtl))
else
add_AT_unsigned (die, DW_AT_const_value, (unsigned HOST_WIDE_INT) val);
}
- break;
+ return true;
case CONST_DOUBLE:
/* Note that a CONST_DOUBLE rtx could represent either an integer or a
floating-point constant. A CONST_DOUBLE is used whenever the
constant requires more than one word in order to be adequately
- represented. We output CONST_DOUBLEs as blocks. */
+ represented. */
{
enum machine_mode mode = GET_MODE (rtl);
add_AT_vec (die, DW_AT_const_value, length / 4, 4, array);
}
else
- {
- /* ??? We really should be using HOST_WIDE_INT throughout. */
- gcc_assert (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT);
-
- add_AT_long_long (die, DW_AT_const_value,
- CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl));
- }
+ add_AT_double (die, DW_AT_const_value,
+ CONST_DOUBLE_HIGH (rtl), CONST_DOUBLE_LOW (rtl));
}
- break;
+ return true;
case CONST_VECTOR:
{
add_AT_vec (die, DW_AT_const_value, length, elt_size, array);
}
- break;
+ return true;
case CONST_STRING:
- add_AT_string (die, DW_AT_const_value, XSTR (rtl, 0));
- break;
+ if (dwarf_version >= 4 || !dwarf_strict)
+ {
+ dw_loc_descr_ref loc_result;
+ resolve_one_addr (&rtl, NULL);
+ rtl_addr:
+ loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+ loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+ loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+ add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0));
+ add_AT_loc (die, DW_AT_location, loc_result);
+ VEC_safe_push (rtx, gc, used_rtx_array, rtl);
+ return true;
+ }
+ return false;
+ case CONST:
+ if (CONSTANT_P (XEXP (rtl, 0)))
+ return add_const_value_attribute (die, XEXP (rtl, 0));
+ /* FALLTHROUGH */
case SYMBOL_REF:
+ if (!const_ok_for_output (rtl))
+ return false;
case LABEL_REF:
- case CONST:
- add_AT_addr (die, DW_AT_const_value, rtl);
- VEC_safe_push (rtx, gc, used_rtx_array, rtl);
- break;
+ if (dwarf_version >= 4 || !dwarf_strict)
+ goto rtl_addr;
+ return false;
case PLUS:
/* In cases where an inlined instance of an inline function is passed
*value* which the artificial local variable always has during its
lifetime. We currently have no way to represent such quasi-constant
values in Dwarf, so for now we just punt and generate nothing. */
- break;
+ return false;
+
+ case HIGH:
+ case CONST_FIXED:
+ return false;
+
+ case MEM:
+ if (GET_CODE (XEXP (rtl, 0)) == CONST_STRING
+ && MEM_READONLY_P (rtl)
+ && GET_MODE (rtl) == BLKmode)
+ {
+ add_AT_string (die, DW_AT_const_value, XSTR (XEXP (rtl, 0), 0));
+ return true;
+ }
+ return false;
default:
/* No other kinds of rtx should be possible here. */
gcc_unreachable ();
}
-
+ return false;
}
/* Determine whether the evaluation of EXPR references any variables
else if (!cgraph_global_info_ready
&& (TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == FUNCTION_DECL))
return *tp;
- else if (DECL_P (*tp) && TREE_CODE (*tp) == VAR_DECL)
+ else if (TREE_CODE (*tp) == VAR_DECL)
{
struct varpool_node *node = varpool_node (*tp);
if (!node->needed)
return *tp;
}
- else if (DECL_P (*tp) && TREE_CODE (*tp) == FUNCTION_DECL
+ else if (TREE_CODE (*tp) == FUNCTION_DECL
&& (!DECL_EXTERNAL (*tp) || DECL_DECLARED_INLINE_P (*tp)))
{
- struct cgraph_node *node = cgraph_node (*tp);
- if (node->process || TREE_ASM_WRITTEN (*tp))
+ /* The call graph machinery must have finished analyzing,
+ optimizing and gimplifying the CU by now.
+ So if *TP has no call graph node associated
+ to it, it means *TP will not be emitted. */
+ if (!cgraph_get_node (*tp))
return *tp;
}
else if (TREE_CODE (*tp) == STRING_CST && !TREE_ASM_WRITTEN (*tp))
TREE_STRING_LENGTH (init) - 1) == 0
&& ((size_t) TREE_STRING_LENGTH (init)
== strlen (TREE_STRING_POINTER (init)) + 1))
- rtl = gen_rtx_CONST_STRING (VOIDmode,
- ggc_strdup (TREE_STRING_POINTER (init)));
+ {
+ rtl = gen_rtx_CONST_STRING (VOIDmode,
+ ggc_strdup (TREE_STRING_POINTER (init)));
+ rtl = gen_rtx_MEM (BLKmode, rtl);
+ MEM_READONLY_P (rtl) = 1;
+ }
}
/* Other aggregates, and complex values, could be represented using
CONCAT: FIXME! */
if (rtl)
rtl = avoid_constant_pool_reference (rtl);
- return rtl;
-}
-
-/* We need to figure out what section we should use as the base for the
- address ranges where a given location is valid.
- 1. If this particular DECL has a section associated with it, use that.
- 2. If this function has a section associated with it, use that.
- 3. Otherwise, use the text section.
- XXX: If you split a variable across multiple sections, we won't notice. */
-
-static const char *
-secname_for_decl (const_tree decl)
-{
- const char *secname;
-
- if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_SECTION_NAME (decl))
- {
- tree sectree = DECL_SECTION_NAME (decl);
- secname = TREE_STRING_POINTER (sectree);
- }
- else if (current_function_decl && DECL_SECTION_NAME (current_function_decl))
+ /* Try harder to get a rtl. If this symbol ends up not being emitted
+ in the current CU, resolve_addr will remove the expression referencing
+ it. */
+ if (rtl == NULL_RTX
+ && TREE_CODE (decl) == VAR_DECL
+ && !DECL_EXTERNAL (decl)
+ && TREE_STATIC (decl)
+ && DECL_NAME (decl)
+ && !DECL_HARD_REGISTER (decl)
+ && DECL_MODE (decl) != VOIDmode)
{
- tree sectree = DECL_SECTION_NAME (current_function_decl);
- secname = TREE_STRING_POINTER (sectree);
+ rtl = make_decl_rtl_for_debug (decl);
+ if (!MEM_P (rtl)
+ || GET_CODE (XEXP (rtl, 0)) != SYMBOL_REF
+ || SYMBOL_REF_DECL (XEXP (rtl, 0)) != decl)
+ rtl = NULL_RTX;
}
- else if (cfun && in_cold_section_p)
- secname = crtl->subsections.cold_section_label;
- else
- secname = text_section_label;
- return secname;
+ return rtl;
}
/* Check whether decl is a Fortran COMMON symbol. If not, NULL_TREE is
tree offset;
int volatilep = 0, unsignedp = 0;
- /* If the decl isn't a VAR_DECL, or if it isn't public or static, or if
+ /* If the decl isn't a VAR_DECL, or if it isn't static, or if
it does not have a value (the offset into the common area), or if it
is thread local (as opposed to global) then it isn't common, and shouldn't
be handled as such. */
if (TREE_CODE (decl) != VAR_DECL
- || !TREE_PUBLIC (decl)
|| !TREE_STATIC (decl)
|| !DECL_HAS_VALUE_EXPR_P (decl)
|| !is_fortran ())
return cvar;
}
-/* Dereference a location expression LOC if DECL is passed by invisible
- reference. */
-
-static dw_loc_descr_ref
-loc_by_reference (dw_loc_descr_ref loc, tree decl)
-{
- HOST_WIDE_INT size;
- enum dwarf_location_atom op;
-
- if (loc == NULL)
- return NULL;
-
- if ((TREE_CODE (decl) != PARM_DECL
- && TREE_CODE (decl) != RESULT_DECL
- && TREE_CODE (decl) != VAR_DECL)
- || !DECL_BY_REFERENCE (decl))
- return loc;
-
- /* If loc is DW_OP_reg{0...31,x}, don't add DW_OP_deref, instead
- change it into corresponding DW_OP_breg{0...31,x} 0. Then the
- location expression is considered to be address of a memory location,
- rather than the register itself. */
- if (((loc->dw_loc_opc >= DW_OP_reg0 && loc->dw_loc_opc <= DW_OP_reg31)
- || loc->dw_loc_opc == DW_OP_regx)
- && (loc->dw_loc_next == NULL
- || (loc->dw_loc_next->dw_loc_opc == DW_OP_GNU_uninit
- && loc->dw_loc_next->dw_loc_next == NULL)))
- {
- if (loc->dw_loc_opc == DW_OP_regx)
- {
- loc->dw_loc_opc = DW_OP_bregx;
- loc->dw_loc_oprnd2.v.val_int = 0;
- }
- else
- {
- loc->dw_loc_opc
- = (enum dwarf_location_atom)
- (loc->dw_loc_opc + (DW_OP_breg0 - DW_OP_reg0));
- loc->dw_loc_oprnd1.v.val_int = 0;
- }
- return loc;
- }
-
- size = int_size_in_bytes (TREE_TYPE (decl));
- if (size > DWARF2_ADDR_SIZE || size == -1)
- return 0;
- else if (size == DWARF2_ADDR_SIZE)
- op = DW_OP_deref;
- else
- op = DW_OP_deref_size;
- add_loc_descr (&loc, new_loc_descr (op, size, 0));
- return loc;
-}
-
/* Generate *either* a DW_AT_location attribute or else a DW_AT_const_value
data attribute for a variable or a parameter. We generate the
DW_AT_const_value attribute only in those cases where the given variable
pointer. This can happen for example if an actual argument in an inlined
function call evaluates to a compile-time constant address. */
-static void
+static bool
add_location_or_const_value_attribute (dw_die_ref die, tree decl,
enum dwarf_attribute attr)
{
rtx rtl;
- dw_loc_descr_ref descr;
+ dw_loc_list_ref list;
var_loc_list *loc_list;
- struct var_loc_node *node;
+
if (TREE_CODE (decl) == ERROR_MARK)
- return;
+ return false;
gcc_assert (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL);
- /* See if we possibly have multiple locations for this variable. */
- loc_list = lookup_decl_loc (decl);
-
- /* If it truly has multiple locations, the first and last node will
- differ. */
- if (loc_list && loc_list->first != loc_list->last)
- {
- const char *endname, *secname;
- dw_loc_list_ref list;
- rtx varloc;
- enum var_init_status initialized;
-
- /* Now that we know what section we are using for a base,
- actually construct the list of locations.
- The first location information is what is passed to the
- function that creates the location list, and the remaining
- locations just get added on to that list.
- Note that we only know the start address for a location
- (IE location changes), so to build the range, we use
- the range [current location start, next location start].
- This means we have to special case the last node, and generate
- a range of [last location start, end of function label]. */
-
- node = loc_list->first;
- varloc = NOTE_VAR_LOCATION (node->var_loc_note);
- secname = secname_for_decl (decl);
-
- if (NOTE_VAR_LOCATION_LOC (node->var_loc_note))
- initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
- else
- initialized = VAR_INIT_STATUS_INITIALIZED;
-
- descr = loc_by_reference (loc_descriptor (varloc, initialized), decl);
- list = new_loc_list (descr, node->label, node->next->label, secname, 1);
- node = node->next;
-
- for (; node->next; node = node->next)
- if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
- {
- /* The variable has a location between NODE->LABEL and
- NODE->NEXT->LABEL. */
- enum var_init_status initialized =
- NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
- varloc = NOTE_VAR_LOCATION (node->var_loc_note);
- descr = loc_by_reference (loc_descriptor (varloc, initialized),
- decl);
- add_loc_descr_to_loc_list (&list, descr,
- node->label, node->next->label, secname);
- }
-
- /* If the variable has a location at the last label
- it keeps its location until the end of function. */
- if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
- {
- char label_id[MAX_ARTIFICIAL_LABEL_BYTES];
- enum var_init_status initialized =
- NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
-
- varloc = NOTE_VAR_LOCATION (node->var_loc_note);
- if (!current_function_decl)
- endname = text_end_label;
- else
- {
- ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
- current_function_funcdef_no);
- endname = ggc_strdup (label_id);
- }
- descr = loc_by_reference (loc_descriptor (varloc, initialized),
- decl);
- add_loc_descr_to_loc_list (&list, descr,
- node->label, endname, secname);
- }
-
- /* Finally, add the location list to the DIE, and we are done. */
- add_AT_loc_list (die, attr, list);
- return;
- }
-
/* Try to get some constant RTL for this decl, and use that as the value of
the location. */
rtl = rtl_for_decl_location (decl);
- if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING))
- {
- add_const_value_attribute (die, rtl);
- return;
- }
+ if (rtl && (CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING)
+ && add_const_value_attribute (die, rtl))
+ return true;
- /* If we have tried to generate the location otherwise, and it
- didn't work out (we wouldn't be here if we did), and we have a one entry
- location list, try generating a location from that. */
- if (loc_list && loc_list->first)
+ /* See if we have single element location list that is equivalent to
+ a constant value. That way we are better to use add_const_value_attribute
+ rather than expanding constant value equivalent. */
+ loc_list = lookup_decl_loc (decl);
+ if (loc_list
+ && loc_list->first
+ && loc_list->first == loc_list->last
+ && NOTE_VAR_LOCATION (loc_list->first->var_loc_note)
+ && NOTE_VAR_LOCATION_LOC (loc_list->first->var_loc_note))
{
- enum var_init_status status;
+ struct var_loc_node *node;
+
node = loc_list->first;
- status = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
- descr = loc_descriptor (NOTE_VAR_LOCATION (node->var_loc_note), status);
- if (descr)
- {
- descr = loc_by_reference (descr, decl);
- add_AT_location_description (die, attr, descr);
- return;
- }
+ rtl = NOTE_VAR_LOCATION_LOC (node->var_loc_note);
+ if (GET_CODE (rtl) == EXPR_LIST)
+ rtl = XEXP (rtl, 0);
+ if ((CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING)
+ && add_const_value_attribute (die, rtl))
+ return true;
}
-
- /* We couldn't get any rtl, so try directly generating the location
- description from the tree. */
- descr = loc_descriptor_from_tree (decl);
- if (descr)
+ list = loc_list_from_tree (decl, decl_by_reference_p (decl) ? 0 : 2);
+ if (list)
{
- descr = loc_by_reference (descr, decl);
- add_AT_location_description (die, attr, descr);
- return;
+ add_AT_location_description (die, attr, list);
+ return true;
}
/* None of that worked, so it must not really have a location;
try adding a constant value attribute from the DECL_INITIAL. */
- tree_add_const_value_attribute (die, decl);
+ return tree_add_const_value_attribute_for_decl (die, decl);
}
/* Add VARIABLE and DIE into deferred locations list. */
}
}
-/* If we don't have a copy of this variable in memory for some reason (such
- as a C++ member constant that doesn't have an out-of-line definition),
- we should tell the debugger about the constant value. */
+/* Attach a DW_AT_const_value attribute to DIE. The value of the
+ attribute is the const value T. */
-static void
-tree_add_const_value_attribute (dw_die_ref var_die, tree decl)
+static bool
+tree_add_const_value_attribute (dw_die_ref die, tree t)
{
tree init;
- tree type = TREE_TYPE (decl);
+ tree type = TREE_TYPE (t);
rtx rtl;
- if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != CONST_DECL)
- return;
+ if (!t || !TREE_TYPE (t) || TREE_TYPE (t) == error_mark_node)
+ return false;
- init = DECL_INITIAL (decl);
- if (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl) && init)
- /* OK */;
- else
- return;
+ init = t;
+ gcc_assert (!DECL_P (init));
rtl = rtl_for_decl_init (init, type);
if (rtl)
- add_const_value_attribute (var_die, rtl);
+ return add_const_value_attribute (die, rtl);
/* If the host and target are sane, try harder. */
else if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
&& initializer_constant_valid_p (init, type))
unsigned char *array = GGC_CNEWVEC (unsigned char, size);
if (native_encode_initializer (init, array, size))
- add_AT_vec (var_die, DW_AT_const_value, size, 1, array);
+ {
+ add_AT_vec (die, DW_AT_const_value, size, 1, array);
+ return true;
+ }
}
}
+ return false;
+}
+
+/* Attach a DW_AT_const_value attribute to VAR_DIE. The value of the
+ attribute is the const value of T, where T is an integral constant
+ variable with static storage duration
+ (so it can't be a PARM_DECL or a RESULT_DECL). */
+
+static bool
+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))
+ return false;
+
+ if (TREE_READONLY (decl)
+ && ! TREE_THIS_VOLATILE (decl)
+ && DECL_INITIAL (decl))
+ /* OK */;
+ else
+ return false;
+
+ /* Don't add DW_AT_const_value if abstract origin already has one. */
+ if (get_AT (var_die, DW_AT_const_value))
+ return false;
+
+ return tree_add_const_value_attribute (var_die, DECL_INITIAL (decl));
}
/* Convert the CFI instructions for the current function into a
if (!cfa_equal_p (&last_cfa, &next_cfa))
{
*list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
- start_label, last_label, section,
- list == NULL);
+ start_label, last_label, section);
list_tail = &(*list_tail)->dw_loc_next;
last_cfa = next_cfa;
if (!cfa_equal_p (&last_cfa, &next_cfa))
{
*list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
- start_label, last_label, section,
- list == NULL);
+ start_label, last_label, section);
list_tail = &(*list_tail)->dw_loc_next;
start_label = last_label;
}
+
*list_tail = new_loc_list (build_cfa_loc (&next_cfa, offset),
- start_label, fde->dw_fde_end, section,
- list == NULL);
+ start_label, fde->dw_fde_end, section);
+
+ if (list && list->dw_loc_next)
+ gen_llsym (list);
return list;
}
add_comp_dir_attribute (dw_die_ref die)
{
const char *wd = get_src_pwd ();
- if (wd != NULL)
+ char *wd1;
+
+ if (wd == NULL)
+ return;
+
+ if (DWARF2_DIR_SHOULD_END_WITH_SEPARATOR)
+ {
+ int wdlen;
+
+ wdlen = strlen (wd);
+ wd1 = GGC_NEWVEC (char, wdlen + 2);
+ strcpy (wd1, wd);
+ wd1 [wdlen] = DIR_SEPARATOR;
+ wd1 [wdlen + 1] = 0;
+ wd = wd1;
+ }
+
add_AT_string (die, DW_AT_comp_dir, remap_debug_filename (wd));
}
static void
add_bound_info (dw_die_ref subrange_die, enum dwarf_attribute bound_attr, tree bound)
{
+ int want_address = 2;
+
switch (TREE_CODE (bound))
{
case ERROR_MARK:
/* All fixed-bounds are represented by INTEGER_CST nodes. */
case INTEGER_CST:
- if (! host_integerp (bound, 0)
- || (bound_attr == DW_AT_lower_bound
- && (((is_c_family () || is_java ()) && integer_zerop (bound))
- || (is_fortran () && integer_onep (bound)))))
- /* Use the default. */
- ;
- else
- add_AT_unsigned (subrange_die, bound_attr, tree_low_cst (bound, 0));
+ {
+ unsigned int prec = simple_type_size_in_bits (TREE_TYPE (bound));
+
+ /* Use the default if possible. */
+ if (bound_attr == DW_AT_lower_bound
+ && (((is_c_family () || is_java ()) && integer_zerop (bound))
+ || (is_fortran () && integer_onep (bound))))
+ ;
+
+ /* Otherwise represent the bound as an unsigned value with the
+ precision of its type. The precision and signedness of the
+ type will be necessary to re-interpret it unambiguously. */
+ else if (prec < HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT mask
+ = ((unsigned HOST_WIDE_INT) 1 << prec) - 1;
+ add_AT_unsigned (subrange_die, bound_attr,
+ TREE_INT_CST_LOW (bound) & mask);
+ }
+ else if (prec == HOST_BITS_PER_WIDE_INT
+ || TREE_INT_CST_HIGH (bound) == 0)
+ add_AT_unsigned (subrange_die, bound_attr,
+ TREE_INT_CST_LOW (bound));
+ else
+ add_AT_double (subrange_die, bound_attr, TREE_INT_CST_HIGH (bound),
+ TREE_INT_CST_LOW (bound));
+ }
break;
CASE_CONVERT:
case RESULT_DECL:
{
dw_die_ref decl_die = lookup_decl_die (bound);
- dw_loc_descr_ref loc;
/* ??? Can this happen, or should the variable have been bound
first? Probably it can, since I imagine that we try to create
the list, and won't have created a forward reference to a
later parameter. */
if (decl_die != NULL)
- add_AT_die_ref (subrange_die, bound_attr, decl_die);
- else
{
- loc = loc_descriptor_from_tree_1 (bound, 0);
- add_AT_location_description (subrange_die, bound_attr, loc);
+ add_AT_die_ref (subrange_die, bound_attr, decl_die);
+ break;
}
- break;
+ want_address = 0;
}
+ /* FALLTHRU */
default:
{
evaluate the value of the array bound. */
dw_die_ref ctx, decl_die;
- dw_loc_descr_ref loc;
+ dw_loc_list_ref list;
- loc = loc_descriptor_from_tree (bound);
- if (loc == NULL)
+ list = loc_list_from_tree (bound, want_address);
+ if (list == NULL)
break;
+ if (single_element_loc_list_p (list))
+ {
+ add_AT_loc (subrange_die, bound_attr, list->expr);
+ break;
+ }
+
if (current_function_decl == 0)
ctx = comp_unit_die;
else
decl_die = new_die (DW_TAG_variable, ctx, bound);
add_AT_flag (decl_die, DW_AT_artificial, 1);
add_type_attribute (decl_die, TREE_TYPE (bound), 1, 0, ctx);
- add_AT_loc (decl_die, DW_AT_location, loc);
-
+ add_AT_location_description (decl_die, DW_AT_location, list);
add_AT_die_ref (subrange_die, bound_attr, decl_die);
break;
}
0));
/* GNU extension: Record what type this method came from originally. */
- if (debug_info_level > DINFO_LEVEL_TERSE)
+ if (debug_info_level > DINFO_LEVEL_TERSE
+ && DECL_CONTEXT (func_decl))
add_AT_die_ref (die, DW_AT_containing_type,
lookup_type_die (DECL_CONTEXT (func_decl)));
}
decl_name = DECL_NAME (decl);
if (decl_name != NULL && IDENTIFIER_POINTER (decl_name) != NULL)
{
- add_name_attribute (die, dwarf2_name (decl, 0));
+ const char *name = dwarf2_name (decl, 0);
+ if (name)
+ add_name_attribute (die, name);
if (! DECL_ARTIFICIAL (decl))
add_src_coords_attributes (die, decl);
{
add_AT_addr (die, DW_AT_VMS_rtnbeg_pd_address,
XEXP (DECL_RTL (decl), 0));
- VEC_safe_push (tree, gc, used_rtx_array, XEXP (DECL_RTL (decl), 0));
+ VEC_safe_push (rtx, gc, used_rtx_array, XEXP (DECL_RTL (decl), 0));
}
#endif
}
&& DECL_P (TYPE_MAX_VALUE (TYPE_DOMAIN (type))))
{
tree szdecl = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- dw_loc_descr_ref loc = loc_descriptor_from_tree (szdecl);
+ dw_loc_list_ref loc = loc_list_from_tree (szdecl, 2);
size = int_size_in_bytes (TREE_TYPE (szdecl));
if (loc && size > 0)
{
- add_AT_loc (array_die, DW_AT_string_length, loc);
+ add_AT_location_description (array_die, DW_AT_string_length, loc);
if (size != DWARF2_ADDR_SIZE)
add_AT_unsigned (array_die, DW_AT_byte_size, size);
}
add_subscript_info (array_die, type, collapse_nested_arrays);
/* Add representation of the type of the elements of this array type and
- emit the corresponding DIE if we haven't done it already. */
+ emit the corresponding DIE if we haven't done it already. */
element_type = TREE_TYPE (type);
if (collapse_nested_arrays)
while (TREE_CODE (element_type) == ARRAY_TYPE)
CASE_CONVERT:
return descr_info_loc (TREE_OPERAND (val, 0), base_decl);
case VAR_DECL:
- return loc_descriptor_from_tree_1 (val, 0);
+ return loc_descriptor_from_tree (val, 0);
case INTEGER_CST:
if (host_integerp (val, 0))
return int_loc_descriptor (tree_low_cst (val, 0));
int i;
for (i = VEC_length (tree, incomplete_types) - 1; i >= 0; i--)
- gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die);
+ 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);
}
/* Determine what tag to use for a record type. */
return DW_TAG_class_type;
case RECORD_IS_INTERFACE:
- return DW_TAG_interface_type;
+ if (dwarf_version >= 3 || !dwarf_strict)
+ return DW_TAG_interface_type;
+ return DW_TAG_structure_type;
default:
gcc_unreachable ();
DIE to represent a formal parameter object (or some inlining thereof). If
it's the latter, then this function is only being called to output a
DW_TAG_formal_parameter DIE to stand as a placeholder for some formal
- argument type of some subprogram type. */
+ argument type of some subprogram type.
+ If EMIT_NAME_P is true, name and source coordinate attributes
+ are emitted. */
static dw_die_ref
-gen_formal_parameter_die (tree node, tree origin, dw_die_ref context_die)
+gen_formal_parameter_die (tree node, tree origin, bool emit_name_p,
+ dw_die_ref context_die)
{
tree node_or_origin = node ? node : origin;
+ tree ultimate_origin;
dw_die_ref parm_die
= new_die (DW_TAG_formal_parameter, context_die, node);
switch (TREE_CODE_CLASS (TREE_CODE (node_or_origin)))
{
case tcc_declaration:
- if (!origin)
- origin = decl_ultimate_origin (node);
+ ultimate_origin = decl_ultimate_origin (node_or_origin);
+ if (node || ultimate_origin)
+ origin = ultimate_origin;
if (origin != NULL)
add_abstract_origin_attribute (parm_die, origin);
else
{
tree type = TREE_TYPE (node);
- add_name_and_src_coords_attributes (parm_die, node);
- if (DECL_BY_REFERENCE (node))
+ if (emit_name_p)
+ add_name_and_src_coords_attributes (parm_die, node);
+ if (decl_by_reference_p (node))
add_type_attribute (parm_die, TREE_TYPE (type), 0, 0,
context_die);
else
add_AT_flag (parm_die, DW_AT_artificial, 1);
}
- if (node)
+ if (node && node != origin)
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,
return parm_die;
}
+/* Generate and return a DW_TAG_GNU_formal_parameter_pack. Also generate
+ children DW_TAG_formal_parameter DIEs representing the arguments of the
+ parameter pack.
+
+ PARM_PACK must be a function parameter pack.
+ PACK_ARG is the first argument of the parameter pack. Its TREE_CHAIN
+ must point to the subsequent arguments of the function PACK_ARG belongs to.
+ SUBR_DIE is the DIE of the function PACK_ARG belongs to.
+ If NEXT_ARG is non NULL, *NEXT_ARG is set to the function argument
+ following the last one for which a DIE was generated. */
+
+static dw_die_ref
+gen_formal_parameter_pack_die (tree parm_pack,
+ tree pack_arg,
+ dw_die_ref subr_die,
+ tree *next_arg)
+{
+ tree arg;
+ dw_die_ref parm_pack_die;
+
+ gcc_assert (parm_pack
+ && lang_hooks.function_parameter_pack_p (parm_pack)
+ && subr_die);
+
+ parm_pack_die = new_die (DW_TAG_GNU_formal_parameter_pack, subr_die, parm_pack);
+ add_src_coords_attributes (parm_pack_die, parm_pack);
+
+ for (arg = pack_arg; arg; arg = TREE_CHAIN (arg))
+ {
+ if (! lang_hooks.decls.function_parm_expanded_from_pack_p (arg,
+ parm_pack))
+ break;
+ gen_formal_parameter_die (arg, NULL,
+ false /* Don't emit name attribute. */,
+ parm_pack_die);
+ }
+ if (next_arg)
+ *next_arg = arg;
+ return parm_pack_die;
+}
+
/* Generate a special type of DIE used as a stand-in for a trailing ellipsis
at the end of an (ANSI prototyped) formal parameters list. */
break;
/* Output a (nameless) DIE to represent the formal parameter itself. */
- parm_die = gen_formal_parameter_die (formal_type, NULL, context_die);
+ parm_die = gen_formal_parameter_die (formal_type, NULL,
+ true /* Emit name attribute. */,
+ context_die);
if ((TREE_CODE (function_or_method_type) == METHOD_TYPE
&& link == first_parm_type)
|| (arg && DECL_ARTIFICIAL (arg)))
/* If we're trying to avoid duplicate debug info, we may not have
emitted the member decl for this function. Emit it now. */
- if (TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))
+ if (TYPE_STUB_DECL (type)
+ && TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))
&& ! lookup_decl_die (member))
{
dw_die_ref type_die;
dw_die_ref old_die;
tree save_fn;
tree context;
- int was_abstract = DECL_ABSTRACT (decl);
+ int was_abstract;
+ htab_t old_decl_loc_table;
/* Make sure we have the actual abstract inline, not a clone. */
decl = DECL_ORIGIN (decl);
- htab_empty (decl_loc_table);
old_die = lookup_decl_die (decl);
if (old_die && get_AT (old_die, DW_AT_inline))
/* We've already generated the abstract instance. */
return;
+ /* We can be called while recursively when seeing block defining inlined subroutine
+ DIE. Be sure to not clobber the outer location table nor use it or we would
+ get locations in abstract instantces. */
+ old_decl_loc_table = decl_loc_table;
+ decl_loc_table = NULL;
+
/* Be sure we've emitted the in-class declaration DIE (if any) first, so
we don't get confused by DECL_ABSTRACT. */
if (debug_info_level > DINFO_LEVEL_TERSE)
current_function_decl = decl;
push_cfun (DECL_STRUCT_FUNCTION (decl));
+ was_abstract = DECL_ABSTRACT (decl);
set_decl_abstract_flags (decl, 1);
dwarf2out_decl (decl);
if (! was_abstract)
set_decl_abstract_flags (decl, 0);
current_function_decl = save_fn;
+ decl_loc_table = old_decl_loc_table;
pop_cfun ();
}
/* Helper function of premark_used_types() which gets called through
- htab_traverse_resize().
+ htab_traverse.
Marks the DIE of a given type in *SLOT as perennial, so it never gets
marked as unused by prune_unused_types. */
+
static int
premark_used_types_helper (void **slot, void *data ATTRIBUTE_UNUSED)
{
return 1;
}
+/* Helper function of premark_types_used_by_global_vars which gets called
+ through htab_traverse.
+
+ Marks the DIE of a given type in *SLOT as perennial, so it never gets
+ marked as unused by prune_unused_types. The DIE of the type is marked
+ only if the global variable using the type will actually be emitted. */
+
+static int
+premark_types_used_by_global_vars_helper (void **slot,
+ void *data ATTRIBUTE_UNUSED)
+{
+ struct types_used_by_vars_entry *entry;
+ dw_die_ref die;
+
+ entry = (struct types_used_by_vars_entry *) *slot;
+ gcc_assert (entry->type != NULL
+ && entry->var_decl != NULL);
+ die = lookup_type_die (entry->type);
+ if (die)
+ {
+ /* Ask cgraph if the global variable really is to be emitted.
+ If yes, then we'll keep the DIE of ENTRY->TYPE. */
+ struct varpool_node *node = varpool_node (entry->var_decl);
+ if (node->needed)
+ {
+ die->die_perennial_p = 1;
+ /* Keep the parent DIEs as well. */
+ while ((die = die->die_parent) && die->die_perennial_p == 0)
+ die->die_perennial_p = 1;
+ }
+ }
+ return 1;
+}
+
/* Mark all members of used_types_hash as perennial. */
+
static void
premark_used_types (void)
{
htab_traverse (cfun->used_types_hash, premark_used_types_helper, NULL);
}
+/* Mark all members of types_used_by_vars_entry as perennial. */
+
+static void
+premark_types_used_by_global_vars (void)
+{
+ if (types_used_by_vars_hash)
+ htab_traverse (types_used_by_vars_hash,
+ premark_types_used_by_global_vars_helper, NULL);
+}
+
/* Generate a DIE to represent a declared function (either file-scope or
block-local). */
/* If this is an explicit function declaration then generate
a DW_AT_explicit attribute. */
- if (lang_hooks.decls.function_decl_explicit_p (decl))
+ if (lang_hooks.decls.function_decl_explicit_p (decl)
+ && (dwarf_version >= 3 || !dwarf_strict))
add_AT_flag (subr_die, DW_AT_explicit, 1);
/* The first time we see a member function, it is in the context of
if (cfun->static_chain_decl)
add_AT_location_description (subr_die, DW_AT_static_link,
- loc_descriptor_from_tree (cfun->static_chain_decl));
+ loc_list_from_tree (cfun->static_chain_decl, 2));
}
+ /* Generate child dies for template paramaters. */
+ if (debug_info_level > DINFO_LEVEL_TERSE)
+ gen_generic_params_dies (decl);
+
/* Now output descriptions of the arguments for this function. This gets
(unnecessarily?) complex because of the fact that the DECL_ARGUMENT list
for a FUNCTION_DECL doesn't indicate cases where there was a trailing
else
{
/* Generate DIEs to represent all known formal parameters. */
- tree arg_decls = DECL_ARGUMENTS (decl);
- tree parm;
-
- /* When generating DIEs, generate the unspecified_parameters DIE
- instead if we come across the arg "__builtin_va_alist" */
- for (parm = arg_decls; parm; parm = TREE_CHAIN (parm))
- if (TREE_CODE (parm) == PARM_DECL)
- {
- if (DECL_NAME (parm)
- && !strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)),
- "__builtin_va_alist"))
- gen_unspecified_parameters_die (parm, subr_die);
- else
+ tree parm = DECL_ARGUMENTS (decl);
+ tree generic_decl = lang_hooks.decls.get_generic_function_decl (decl);
+ tree generic_decl_parm = generic_decl
+ ? DECL_ARGUMENTS (generic_decl)
+ : NULL;
+
+ /* Now we want to walk the list of parameters of the function and
+ emit their relevant DIEs.
+
+ We consider the case of DECL being an instance of a generic function
+ as well as it being a normal function.
+
+ If DECL is an instance of a generic function we walk the
+ parameters of the generic function declaration _and_ the parameters of
+ DECL itself. This is useful because we want to emit specific DIEs for
+ function parameter packs and those are declared as part of the
+ generic function declaration. In that particular case,
+ the parameter pack yields a DW_TAG_GNU_formal_parameter_pack DIE.
+ That DIE has children DIEs representing the set of arguments
+ of the pack. Note that the set of pack arguments can be empty.
+ In that case, the DW_TAG_GNU_formal_parameter_pack DIE will not have any
+ children DIE.
+
+ Otherwise, we just consider the parameters of DECL. */
+ while (generic_decl_parm || parm)
+ {
+ if (generic_decl_parm
+ && lang_hooks.function_parameter_pack_p (generic_decl_parm))
+ gen_formal_parameter_pack_die (generic_decl_parm,
+ parm, subr_die,
+ &parm);
+ else if (parm)
+ {
gen_decl_die (parm, NULL, subr_die);
- }
+ parm = TREE_CHAIN (parm);
+ }
+
+ if (generic_decl_parm)
+ generic_decl_parm = TREE_CHAIN (generic_decl_parm);
+ }
/* Decide whether we need an unspecified_parameters DIE at the end.
There are 2 more cases to do this for: 1) the ansi ... declaration -
HOST_WIDE_INT off;
tree com_decl;
tree decl_or_origin = decl ? decl : origin;
+ tree ultimate_origin;
dw_die_ref var_die;
dw_die_ref old_die = decl ? lookup_decl_die (decl) : NULL;
dw_die_ref origin_die;
int declaration = (DECL_EXTERNAL (decl_or_origin)
- /* If DECL is COMDAT and has not actually been
- emitted, we cannot take its address; there
- might end up being no definition anywhere in
- the program. For example, consider the C++
- test case:
-
- template <class T>
- struct S { static const int i = 7; };
-
- template <class T>
- const int S<T>::i;
-
- int f() { return S<int>::i; }
-
- Here, S<int>::i is not DECL_EXTERNAL, but no
- definition is required, so the compiler will
- not emit a definition. */
- || (TREE_CODE (decl_or_origin) == VAR_DECL
- && DECL_COMDAT (decl_or_origin)
- && !TREE_ASM_WRITTEN (decl_or_origin))
|| class_or_namespace_scope_p (context_die));
- if (!origin)
- origin = decl_ultimate_origin (decl);
-
+ ultimate_origin = decl_ultimate_origin (decl_or_origin);
+ if (decl || ultimate_origin)
+ origin = ultimate_origin;
com_decl = fortran_common (decl_or_origin, &off);
/* Symbol in common gets emitted as a child of the common block, in the form
of a data member. */
if (com_decl)
{
- tree field;
dw_die_ref com_die;
- dw_loc_descr_ref loc;
+ dw_loc_list_ref loc;
die_node com_die_arg;
var_die = lookup_decl_die (decl_or_origin);
{
if (get_AT (var_die, DW_AT_location) == NULL)
{
- loc = loc_descriptor_from_tree (com_decl);
+ loc = loc_list_from_tree (com_decl, off ? 1 : 2);
if (loc)
{
if (off)
{
/* Optimize the common case. */
- if (loc->dw_loc_opc == DW_OP_addr
- && loc->dw_loc_next == NULL
- && GET_CODE (loc->dw_loc_oprnd1.v.val_addr)
+ if (single_element_loc_list_p (loc)
+ && loc->expr->dw_loc_opc == DW_OP_addr
+ && loc->expr->dw_loc_next == NULL
+ && GET_CODE (loc->expr->dw_loc_oprnd1.v.val_addr)
== SYMBOL_REF)
- loc->dw_loc_oprnd1.v.val_addr
- = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+ loc->expr->dw_loc_oprnd1.v.val_addr
+ = plus_constant (loc->expr->dw_loc_oprnd1.v.val_addr, off);
else
- loc_descr_plus_const (&loc, off);
+ loc_list_plus_const (loc, off);
}
- add_AT_loc (var_die, DW_AT_location, loc);
+ add_AT_location_description (var_die, DW_AT_location, loc);
remove_AT (var_die, DW_AT_declaration);
}
}
= htab_create_ggc (10, common_block_die_table_hash,
common_block_die_table_eq, NULL);
- field = TREE_OPERAND (DECL_VALUE_EXPR (decl), 0);
com_die_arg.decl_id = DECL_UID (com_decl);
com_die_arg.die_parent = context_die;
com_die = (dw_die_ref) htab_find (common_block_die_table, &com_die_arg);
- loc = loc_descriptor_from_tree (com_decl);
+ loc = loc_list_from_tree (com_decl, 2);
if (com_die == NULL)
{
const char *cnam
add_name_and_src_coords_attributes (com_die, com_decl);
if (loc)
{
- add_AT_loc (com_die, DW_AT_location, loc);
+ add_AT_location_description (com_die, DW_AT_location, loc);
/* Avoid sharing the same loc descriptor between
DW_TAG_common_block and DW_TAG_variable. */
- loc = loc_descriptor_from_tree (com_decl);
+ loc = loc_list_from_tree (com_decl, 2);
}
else if (DECL_EXTERNAL (decl))
add_AT_flag (com_die, DW_AT_declaration, 1);
}
else if (get_AT (com_die, DW_AT_location) == NULL && loc)
{
- add_AT_loc (com_die, DW_AT_location, loc);
- loc = loc_descriptor_from_tree (com_decl);
+ add_AT_location_description (com_die, DW_AT_location, loc);
+ loc = loc_list_from_tree (com_decl, 2);
remove_AT (com_die, DW_AT_declaration);
}
var_die = new_die (DW_TAG_variable, com_die, decl);
if (off)
{
/* Optimize the common case. */
- if (loc->dw_loc_opc == DW_OP_addr
- && loc->dw_loc_next == NULL
- && GET_CODE (loc->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF)
- loc->dw_loc_oprnd1.v.val_addr
- = plus_constant (loc->dw_loc_oprnd1.v.val_addr, off);
+ if (single_element_loc_list_p (loc)
+ && loc->expr->dw_loc_opc == DW_OP_addr
+ && loc->expr->dw_loc_next == NULL
+ && GET_CODE (loc->expr->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF)
+ loc->expr->dw_loc_oprnd1.v.val_addr
+ = plus_constant (loc->expr->dw_loc_oprnd1.v.val_addr, off);
else
- loc_descr_plus_const (&loc, off);
+ loc_list_plus_const (loc, off);
}
- add_AT_loc (var_die, DW_AT_location, loc);
+ add_AT_location_description (var_die, DW_AT_location, loc);
}
else if (DECL_EXTERNAL (decl))
add_AT_flag (var_die, DW_AT_declaration, 1);
and if we already emitted a DIE for it, don't emit a second
DIE for it again. */
if (old_die
- && declaration
- && old_die->die_parent == context_die)
+ && declaration)
return;
/* For static data members, the declaration in the class is supposed
tree type = TREE_TYPE (decl);
add_name_and_src_coords_attributes (var_die, decl);
- if ((TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL
- || TREE_CODE (decl) == VAR_DECL)
- && DECL_BY_REFERENCE (decl))
+ if (decl_by_reference_p (decl))
add_type_attribute (var_die, TREE_TYPE (type), 0, 0, context_die);
else
add_type_attribute (var_die, type, TREE_READONLY (decl),
add_pubname (decl_or_origin, var_die);
}
else
- tree_add_const_value_attribute (var_die, decl_or_origin);
+ tree_add_const_value_attribute_for_decl (var_die, decl_or_origin);
}
/* Generate a DIE to represent a named constant. */
add_AT_flag (const_die, DW_AT_external, 1);
if (DECL_ARTIFICIAL (decl))
add_AT_flag (const_die, DW_AT_artificial, 1);
- tree_add_const_value_attribute (const_die, decl);
+ tree_add_const_value_attribute_for_decl (const_die, decl);
}
/* Generate a DIE to represent a label identifier. */
{
expanded_location s = expand_location (BLOCK_SOURCE_LOCATION (stmt));
- add_AT_file (die, DW_AT_call_file, lookup_filename (s.file));
- add_AT_unsigned (die, DW_AT_call_line, s.line);
+ if (dwarf_version >= 3 || !dwarf_strict)
+ {
+ add_AT_file (die, DW_AT_call_file, lookup_filename (s.file));
+ add_AT_unsigned (die, DW_AT_call_line, s.line);
+ }
}
{
char label[MAX_ARTIFICIAL_LABEL_BYTES];
- if (BLOCK_FRAGMENT_CHAIN (stmt))
+ if (BLOCK_FRAGMENT_CHAIN (stmt)
+ && (dwarf_version >= 3 || !dwarf_strict))
{
tree chain;
static void
gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die, int depth)
{
- tree decl = block_ultimate_origin (stmt);
+ tree decl;
+
+ /* The instance of function that is effectively being inlined shall not
+ be abstract. */
+ gcc_assert (! BLOCK_ABSTRACT (stmt));
+
+ decl = block_ultimate_origin (stmt);
/* Emit info for the abstract instance first, if we haven't yet. We
must emit this even if the block is abstract, otherwise when we
decls_for_scope (stmt, subr_die, depth);
current_function_has_inlines = 1;
}
- else
- /* We may get here if we're the outer block of function A that was
- inlined into function B that was inlined into function C. When
- generating debugging info for C, dwarf2out_abstract_function(B)
- would mark all inlined blocks as abstract, including this one.
- So, we wouldn't (and shouldn't) expect labels to be generated
- for this one. Instead, just emit debugging info for
- declarations within the block. This is particularly important
- in the case of initializers of arguments passed from B to us:
- if they're statement expressions containing declarations, we
- wouldn't generate dies for their abstract variables, and then,
- when generating dies for the real variables, we'd die (pun
- intended :-) */
- gen_lexical_block_die (stmt, context_die, depth);
}
/* Generate a DIE for a field in a record, or structure. */
add_AT_string (die, DW_AT_producer, producer);
+ language = DW_LANG_C89;
if (strcmp (language_string, "GNU C++") == 0)
language = DW_LANG_C_plus_plus;
- else if (strcmp (language_string, "GNU Ada") == 0)
- language = DW_LANG_Ada95;
else if (strcmp (language_string, "GNU F77") == 0)
language = DW_LANG_Fortran77;
- else if (strcmp (language_string, "GNU Fortran") == 0)
- language = DW_LANG_Fortran95;
else if (strcmp (language_string, "GNU Pascal") == 0)
language = DW_LANG_Pascal83;
- else if (strcmp (language_string, "GNU Java") == 0)
- language = DW_LANG_Java;
- else if (strcmp (language_string, "GNU Objective-C") == 0)
- language = DW_LANG_ObjC;
- else if (strcmp (language_string, "GNU Objective-C++") == 0)
- language = DW_LANG_ObjC_plus_plus;
- else
- language = DW_LANG_C89;
+ else if (dwarf_version >= 3 || !dwarf_strict)
+ {
+ if (strcmp (language_string, "GNU Ada") == 0)
+ language = DW_LANG_Ada95;
+ else if (strcmp (language_string, "GNU Fortran") == 0)
+ language = DW_LANG_Fortran95;
+ else if (strcmp (language_string, "GNU Java") == 0)
+ language = DW_LANG_Java;
+ else if (strcmp (language_string, "GNU Objective-C") == 0)
+ language = DW_LANG_ObjC;
+ else if (strcmp (language_string, "GNU Objective-C++") == 0)
+ language = DW_LANG_ObjC_plus_plus;
+ }
add_AT_unsigned (die, DW_AT_language, language);
return die;
else
remove_AT (type_die, DW_AT_declaration);
+ /* Generate child dies for template paramaters. */
+ if (debug_info_level > DINFO_LEVEL_TERSE
+ && COMPLETE_TYPE_P (type))
+ gen_generic_params_dies (type);
+
/* If this type has been completed, then give it a byte_size attribute and
then give a list of members. */
if (complete && !ns_decl)
if (type == NULL_TREE || type == error_mark_node)
return;
+ /* If TYPE is a typedef type variant, let's generate debug info
+ for the parent typedef which TYPE is a type of. */
if (TYPE_NAME (type) && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& DECL_ORIGINAL_TYPE (TYPE_NAME (type)))
{
the type description DIE we want to generate. */
if (DECL_CONTEXT (TYPE_NAME (type))
&& TREE_CODE (DECL_CONTEXT (TYPE_NAME (type))) == NAMESPACE_DECL)
- context_die = lookup_decl_die (DECL_CONTEXT (TYPE_NAME (type)));
+ context_die = get_context_die (DECL_CONTEXT (TYPE_NAME (type)));
TREE_ASM_WRITTEN (type) = 1;
gen_decl_die (TYPE_NAME (type), NULL, context_die);
/* If this is an array type with hidden descriptor, handle it first. */
if (!TREE_ASM_WRITTEN (type)
&& lang_hooks.types.get_array_descr_info
- && lang_hooks.types.get_array_descr_info (type, &info))
+ && lang_hooks.types.get_array_descr_info (type, &info)
+ && (dwarf_version >= 3 || !dwarf_strict))
{
gen_descr_array_type_die (type, &info, context_die);
TREE_ASM_WRITTEN (type) = 1;
context_die = lookup_type_die (TYPE_CONTEXT (type));
need_pop = 1;
}
+ else if (TYPE_CONTEXT (type) != NULL_TREE
+ && (TREE_CODE (TYPE_CONTEXT (type)) == FUNCTION_DECL))
+ {
+ /* If this type is local to a function that hasn't been written
+ 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));
+ need_pop = 0;
+ }
else
{
context_die = declare_in_namespace (type, context_die);
if (must_output_die)
{
if (inlined_func)
- gen_inlined_subroutine_die (stmt, context_die, depth);
+ {
+ /* If STMT block is abstract, that means we have been called
+ indirectly from dwarf2out_abstract_function.
+ That function rightfully marks the descendent blocks (of
+ the abstract function it is dealing with) as being abstract,
+ precisely to prevent us from emitting any
+ DW_TAG_inlined_subroutine DIE as a descendent
+ of an abstract function instance. So in that case, we should
+ not call gen_inlined_subroutine_die.
+
+ Later though, when cgraph asks dwarf2out to emit info
+ for the concrete instance of the function decl into which
+ the concrete instance of STMT got inlined, the later will lead
+ to the generation of a DW_TAG_inlined_subroutine DIE. */
+ if (! BLOCK_ABSTRACT (stmt))
+ gen_inlined_subroutine_die (stmt, context_die, depth);
+ }
else
gen_lexical_block_die (stmt, context_die, depth);
}
{
dw_die_ref die;
tree decl_or_origin = decl ? decl : origin;
- tree ultimate_origin = origin ? decl_ultimate_origin (origin) : NULL;
-
- if (ultimate_origin)
- origin = ultimate_origin;
if (TREE_CODE (decl_or_origin) == FUNCTION_DECL)
die = lookup_decl_die (decl_or_origin);
{
/* Find die that represents this context. */
if (TYPE_P (context))
- return force_type_die (context);
+ return force_type_die (TYPE_MAIN_VARIANT (context));
else
return force_decl_die (context);
}
break;
case NAMESPACE_DECL:
- dwarf2out_decl (decl);
+ if (dwarf_version >= 3 || !dwarf_strict)
+ dwarf2out_decl (decl);
+ else
+ /* DWARF2 has neither DW_TAG_module, nor DW_TAG_namespace. */
+ decl_die = comp_unit_die;
break;
default:
context_die, decl);
/* For Fortran modules defined in different CU don't add src coords. */
if (namespace_die->die_tag == DW_TAG_module && DECL_EXTERNAL (decl))
- add_name_attribute (namespace_die, dwarf2_name (decl, 0));
+ {
+ const char *name = dwarf2_name (decl, 0);
+ if (name)
+ add_name_attribute (namespace_die, name);
+ }
else
add_name_and_src_coords_attributes (namespace_die, decl);
if (DECL_EXTERNAL (decl))
gen_decl_die (tree decl, tree origin, dw_die_ref context_die)
{
tree decl_or_origin = decl ? decl : origin;
- tree class_origin = NULL;
+ tree class_origin = NULL, ultimate_origin;
if (DECL_P (decl_or_origin) && DECL_IGNORED_P (decl_or_origin))
return;
/* If we're emitting a clone, emit info for the abstract instance. */
if (origin || DECL_ORIGIN (decl) != decl)
- dwarf2out_abstract_function (origin ? origin : DECL_ABSTRACT_ORIGIN (decl));
+ dwarf2out_abstract_function (origin
+ ? DECL_ORIGIN (origin)
+ : DECL_ABSTRACT_ORIGIN (decl));
/* If we're emitting an out-of-line copy of an inline function,
emit info for the abstract instance and set up to refer to it. */
/* Output any DIEs that are needed to specify the type of this data
object. */
- if ((TREE_CODE (decl_or_origin) == RESULT_DECL
- || TREE_CODE (decl_or_origin) == VAR_DECL)
- && DECL_BY_REFERENCE (decl_or_origin))
+ if (decl_by_reference_p (decl_or_origin))
gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
else
gen_type_die (TREE_TYPE (decl_or_origin), context_die);
complicated because of the possibility that the VAR_DECL really
represents an inlined instance of a formal parameter for an inline
function. */
- if (!origin)
- origin = decl_ultimate_origin (decl);
- if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL)
- gen_formal_parameter_die (decl, origin, context_die);
+ ultimate_origin = decl_ultimate_origin (decl_or_origin);
+ if (ultimate_origin != NULL_TREE
+ && TREE_CODE (ultimate_origin) == PARM_DECL)
+ gen_formal_parameter_die (decl, origin,
+ true /* Emit name attribute. */,
+ context_die);
else
gen_variable_die (decl, origin, context_die);
break;
gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
else
gen_type_die (TREE_TYPE (decl_or_origin), context_die);
- gen_formal_parameter_die (decl, origin, context_die);
+ gen_formal_parameter_die (decl, origin,
+ true /* Emit name attribute. */,
+ context_die);
break;
case NAMESPACE_DECL:
case IMPORTED_DECL:
- gen_namespace_die (decl, context_die);
+ if (dwarf_version >= 3 || !dwarf_strict)
+ gen_namespace_die (decl, context_die);
break;
default:
}
if (TREE_CODE (decl) == NAMESPACE_DECL)
- imported_die = new_die (DW_TAG_imported_module,
- lexical_block_die,
- lexical_block);
+ {
+ if (dwarf_version >= 3 || !dwarf_strict)
+ imported_die = new_die (DW_TAG_imported_module,
+ lexical_block_die,
+ lexical_block);
+ else
+ return;
+ }
else
imported_die = new_die (DW_TAG_imported_declaration,
lexical_block_die,
&& TYPE_P (context)
&& !should_emit_struct_debug (context, DINFO_USAGE_DIR_USE))
return;
+
+ if (!(dwarf_version >= 3 || !dwarf_strict))
+ return;
+
scope_die = get_context_die (context);
if (child)
gen_decl_die (decl, NULL, context_die);
}
+/* Write the debugging output for DECL. */
+
+static void
+dwarf2out_function_decl (tree decl)
+{
+ dwarf2out_decl (decl);
+
+ htab_empty (decl_loc_table);
+}
+
/* Output a marker (i.e. a label) for the beginning of the generated code for
a lexical block. */
return fd->emitted_number;
}
+/* Schedule generation of a DW_AT_const_value attribute to DIE.
+ That generation should happen after function debug info has been
+ generated. The value of the attribute is the constant value of ARG. */
+
+static void
+append_entry_to_tmpl_value_parm_die_table (dw_die_ref die, tree arg)
+{
+ die_arg_entry entry;
+
+ if (!die || !arg)
+ return;
+
+ if (!tmpl_value_parm_die_table)
+ tmpl_value_parm_die_table
+ = VEC_alloc (die_arg_entry, gc, 32);
+
+ entry.die = die;
+ entry.arg = arg;
+ VEC_safe_push (die_arg_entry, gc,
+ tmpl_value_parm_die_table,
+ &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. */
+
+static void
+gen_remaining_tmpl_value_param_die_attribute (void)
+{
+ 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);
+ }
+}
+
+
/* Replace DW_AT_name for the decl with name. */
-
+
static void
dwarf2out_set_name (tree decl, tree name)
{
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)
+ return;
+
attr = get_AT (die, DW_AT_name);
if (attr)
{
struct indirect_string_node *node;
- node = find_AT_string (dwarf2_name (name, 0));
+ node = find_AT_string (dname);
/* replace the string. */
attr->dw_attr_val.v.val_str = node;
}
else
- add_name_attribute (die, dwarf2_name (name, 0));
+ 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_NEW (struct 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;
+
+ 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;
+}
+
+
+/* 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. */
+
+static void
+dwarf2out_virtual_call_token (tree addr, int insn_uid)
+{
+ if (is_cxx() && TREE_CODE (addr) == OBJ_TYPE_REF)
+ {
+ tree token = OBJ_TYPE_REF_TOKEN (addr);
+ if (TREE_CODE (token) == INTEGER_CST)
+ store_vcall_insn (TREE_INT_CST_LOW (token), insn_uid);
+ }
+}
+
+/* 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. */
+
+static void
+dwarf2out_copy_call_info (rtx old_insn, rtx new_insn)
+{
+ unsigned int vtable_slot = lookup_vcall_insn (INSN_UID (old_insn));
+
+ if (vtable_slot != (unsigned int) -1)
+ store_vcall_insn (vtable_slot, INSN_UID (new_insn));
+}
+
+/* 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. */
+
+static void
+dwarf2out_virtual_call (int insn_uid)
+{
+ unsigned int vtable_slot = lookup_vcall_insn (insn_uid);
+ vcall_entry e;
+
+ if (vtable_slot == (unsigned int) -1)
+ return;
+
+ e.poc_label_num = poc_label_num++;
+ e.vtable_slot = vtable_slot;
+ VEC_safe_push (vcall_entry, gc, vcall_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);
}
/* Called by the final INSN scan whenever we see a var location. We
static void
dwarf2out_var_location (rtx loc_note)
{
- char loclabel[MAX_ARTIFICIAL_LABEL_BYTES];
+ char loclabel[MAX_ARTIFICIAL_LABEL_BYTES + 2];
struct var_loc_node *newloc;
rtx next_real;
static const char *last_label;
+ static const char *last_postcall_label;
static bool last_in_cold_section_p;
tree decl;
if (next_real == NULL_RTX)
return;
- newloc = GGC_CNEW (struct var_loc_node);
+ decl = NOTE_VAR_LOCATION_DECL (loc_note);
+ newloc = add_var_loc_to_decl (decl, loc_note);
+ if (newloc == NULL)
+ return;
+
/* If there were no real insns between note we processed last time
and this note, use the label we emitted last time. */
- if (last_var_location_insn != NULL_RTX
- && last_var_location_insn == next_real
- && last_in_cold_section_p == in_cold_section_p)
- newloc->label = last_label;
- else
+ if (last_var_location_insn == NULL_RTX
+ || last_var_location_insn != next_real
+ || last_in_cold_section_p != in_cold_section_p)
{
ASM_GENERATE_INTERNAL_LABEL (loclabel, "LVL", loclabel_num);
ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LVL", loclabel_num);
loclabel_num++;
- newloc->label = ggc_strdup (loclabel);
+ last_label = ggc_strdup (loclabel);
+ last_postcall_label = NULL;
}
newloc->var_loc_note = loc_note;
newloc->next = NULL;
- if (cfun && in_cold_section_p)
- newloc->section_label = crtl->subsections.cold_section_label;
+ if (!NOTE_DURING_CALL_P (loc_note))
+ newloc->label = last_label;
else
- newloc->section_label = text_section_label;
+ {
+ if (!last_postcall_label)
+ {
+ sprintf (loclabel, "%s-1", last_label);
+ last_postcall_label = ggc_strdup (loclabel);
+ }
+ newloc->label = last_postcall_label;
+ }
last_var_location_insn = next_real;
- last_label = newloc->label;
last_in_cold_section_p = in_cold_section_p;
- decl = NOTE_VAR_LOCATION_DECL (loc_note);
- add_var_loc_to_decl (decl, newloc);
}
/* We need to reset the locations at the beginning of each
static void
dwarf2out_begin_function (tree fun)
{
- htab_empty (decl_loc_table);
-
if (function_section (fun) != text_section)
have_multiple_function_sections = true;
static void
dwarf2out_start_source_file (unsigned int lineno, const char *filename)
{
- if (flag_eliminate_dwarf2_dups)
+ if (flag_eliminate_dwarf2_dups && dwarf_version < 4)
{
/* Record the beginning of the file for break_out_includes. */
dw_die_ref bincl_die;
static void
dwarf2out_end_source_file (unsigned int lineno ATTRIBUTE_UNUSED)
{
- if (flag_eliminate_dwarf2_dups)
+ if (flag_eliminate_dwarf2_dups && dwarf_version < 4)
/* Record the end of the file for break_out_includes. */
new_die (DW_TAG_GNU_EINCL, comp_unit_die, NULL);
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
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,
switch_to_section (cold_text_section);
ASM_OUTPUT_LABEL (asm_out_file, cold_text_section_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 ())
+ {
+#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");
+ }
}
/* A helper function for dwarf2out_finish called through
- ht_forall. Emit one queued .debug_str string. */
+ 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)
+ if (node->label && node->refcount)
{
switch_to_section (debug_str_section);
ASM_OUTPUT_LABEL (asm_out_file, node->label);
if (a->dw_attr_val.val_class == dw_val_class_die_ref)
{
/* A reference to another DIE.
- Make sure that it will get emitted. */
- prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1);
+ 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. */
die->die_mark = 2;
/* If this is an array type, we need to make sure our
- kids get marked, even if they're types. */
- if (die->die_tag == DW_TAG_array_type)
+ 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));
} 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;
+}
/* Remove dies representing declarations that we never use. */
{
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 ();
+
/* 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);
+ }
/* Also set the mark on nodes referenced from the
pubname_table or arange_table. */
for (i = 0; i < arange_table_in_use; i++)
prune_unused_types_mark (arange_table[i], 1);
+ /* 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);
+
/* Get rid of nodes that aren't marked; and update the string counts. */
- if (debug_str_hash)
+ 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);
/* 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);
}
/* Set the parameter to true if there are any relative pathnames in
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));
+}
+
/* 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
}
}
+/* 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. */
+
+static int
+resolve_one_addr (rtx *addr, void *data ATTRIBUTE_UNUSED)
+{
+ rtx rtl = *addr;
+
+ if (GET_CODE (rtl) == CONST_STRING)
+ {
+ 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;
+ }
+
+ if (GET_CODE (rtl) == SYMBOL_REF
+ && SYMBOL_REF_DECL (rtl)
+ && TREE_CODE (SYMBOL_REF_DECL (rtl)) == VAR_DECL
+ && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl)))
+ return 1;
+
+ if (GET_CODE (rtl) == CONST
+ && for_each_rtx (&XEXP (rtl, 0), resolve_one_addr, NULL))
+ return 1;
+
+ return 0;
+}
+
+/* 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 bool
+resolve_addr_in_expr (dw_loc_descr_ref loc)
+{
+ for (; loc; loc = loc->dw_loc_next)
+ if ((loc->dw_loc_opc == DW_OP_addr
+ && 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;
+}
+
+/* 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
+resolve_addr (dw_die_ref die)
+{
+ dw_die_ref c;
+ dw_attr_ref a;
+ dw_loc_list_ref *curr;
+ unsigned ix;
+
+ for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
+ switch (AT_class (a))
+ {
+ 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;
+ }
+
+ FOR_EACH_CHILD (die, c, resolve_addr (c));
+}
+
/* Output stuff that dwarf requires at the end of every file,
and generate the DWARF-2 debugging info. */
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;
+ 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));
limbo_die_list = NULL;
+ resolve_addr (comp_unit_die);
+
for (node = deferred_asm_name; node; node = node->next)
{
tree decl = node->created_for;
/* Generate separate CUs for each of the include files we've seen.
They will go into limbo_die_list. */
- if (flag_eliminate_dwarf2_dups)
+ if (flag_eliminate_dwarf2_dups && dwarf_version < 4)
break_out_includes (comp_unit_die);
+ /* Generate separate COMDAT sections for type DIEs. */
+ if (dwarf_version >= 4)
+ {
+ 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
+ limbo die list. */
+ limbo_die_list = NULL;
+
+ /* For each new comdat type unit, copy declarations for incomplete
+ types to make the new unit self-contained (i.e., no direct
+ 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);
+
+ /* In the process of copying declarations from one unit to another,
+ we may have left some declarations behind that are no longer
+ referenced. Prune them. */
+ prune_unused_types ();
+ }
+
/* Traverse the DIE's and add add sibling attributes to those DIE's
that have children. */
add_sibling_attributes (comp_unit_die);
for (node = limbo_die_list; node; node = node->next)
add_sibling_attributes (node->die);
+ for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+ add_sibling_attributes (ctnode->root_die);
/* Output a terminator label for the .text section. */
switch_to_section (text_section);
/* We can only use the low/high_pc attributes if all of the code was
in .text. */
- if (!have_multiple_function_sections)
+ 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);
else
{
unsigned fde_idx = 0;
+ 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
add_AT_addr (comp_unit_die, DW_AT_low_pc, const0_rtx);
add_AT_addr (comp_unit_die, DW_AT_entry_pc, const0_rtx);
- add_AT_range_list (comp_unit_die, DW_AT_ranges,
- add_ranges_by_labels (text_section_label,
- text_end_label));
- if (flag_reorder_blocks_and_partition)
- add_ranges_by_labels (cold_text_section_label,
- cold_end_label);
+ if (text_section_used)
+ 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,
+ cold_end_label, &range_list_added);
for (fde_idx = 0; fde_idx < fde_table_in_use; fde_idx++)
{
if (fde->dw_fde_switched_sections)
{
- add_ranges_by_labels (fde->dw_fde_hot_section_label,
- fde->dw_fde_hot_section_end_label);
- add_ranges_by_labels (fde->dw_fde_unlikely_section_label,
- fde->dw_fde_unlikely_section_end_label);
+ 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
- add_ranges_by_labels (fde->dw_fde_begin,
- fde->dw_fde_end);
+ else if (!fde->in_std_section)
+ add_ranges_by_labels (comp_unit_die, fde->dw_fde_begin,
+ fde->dw_fde_end, &range_list_added);
}
- add_ranges (NULL);
+ if (range_list_added)
+ add_ranges (NULL);
}
/* Output location list section if necessary. */
for (node = limbo_die_list; node; node = node->next)
output_comp_unit (node->die, 0);
+ comdat_type_table = htab_create (100, htab_ct_hash, htab_ct_eq, NULL);
+ for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+ {
+ void **slot = htab_find_slot (comdat_type_table, ctnode, INSERT);
+
+ /* Don't output duplicate types. */
+ if (*slot != HTAB_EMPTY_ENTRY)
+ continue;
+
+ /* Add a pointer to the line table for the main compilation unit
+ so that the debugger can make sense of DW_AT_decl_file
+ attributes. */
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ add_AT_lineptr (ctnode->root_die, DW_AT_stmt_list,
+ debug_line_section_label);
+
+ output_comdat_type_unit (ctnode);
+ *slot = ctnode;
+ }
+ 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);
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))
+ {
+ switch_to_section (debug_vcall_section);
+ output_vcall_table ();
+ }
+
/* 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)
{
0, /* init */
0, /* finish */
+ 0, /* assembly_start */
0, /* define */
0, /* undef */
0, /* start_source_file */
0, /* handle_pch */
0, /* var_location */
0, /* switch_text_section */
+ 0, /* direct_call */
+ 0, /* virtual_call_token */
+ 0, /* copy_call_info */
+ 0, /* virtual_call */
0, /* set_name */
0 /* start_end_main_source_file */
};