[Dwarf Patch] Implement split debug info proposal--Updated (issue6305113)

Sterling Augustine saugustine@google.com
Tue Sep 18 22:27:00 GMT 2012


On Mon, Aug 27, 2012 at 11:00 AM, Sterling Augustine
<saugustine@google.com> wrote:
> The enclosed patch to implment -gsplit-dwarf addresses Jason's comments as
> responded to by myself and Cary.
>
> In particular, it:
>
> 1. Adds comments for force_direct and its use
> 2. Corrects the location of is_unit_die
> 3. Makes -gsplit-dwarf imply -gdwarf-4
> 4. Changes DW_LLE_* to DW_LLE_GNU_*
> 5. Fixes various comments
> 6. Adds a new enum to cleanup dtprel and the associated logic
> 7. Fixes the FIXME in output_macinfo_op
>
> However, it does not:
>
> 1. Cleanup redundant entries in the .debug_addr--Cary has a patch in progress
>    that handles this situation.
>
>    http://codereview.appspot.com/6305113/#msg6 has more discussion.
>
> 2. Defer assigning indices to DW_GNU_addr_index entries until output time. We
>    see this less than 0.1 percent of the time, and fixing it would require
>    a major restructuring of when the size of debug entries are calculated as
>    opposed to when they are emitted.  Further, Cary's forthcoming patch in is
>    likely to reduce this count even further.
>
>
> The line length issues were not real, as a unified diff adds two spaces to
> the line, and a script that doesn't account for that will reject 79 and 80
> character line lengths.
>
>
> Hopefully this addresses all remaining issues. OK for mainline?
>
> Sterling and Cary
>
>
> include/ChangeLog
>
> 2012-08-24  Sterling Augustine <saugustine@google.com>
>             Cary Coutant <ccoutant@google.com>
>
>         * dwarf2.def: Fix comment.
>         * dwarf2.h (dwarf_location_list_entry_type): New enum with members
>         DW_LLE_GNU_end_of_list_entry, DW_LLE_GNU_case_address_selection_entry,
>         DW_LLE_GNU_start_end_entry, DW_LLE_GNU_start_length_entry.
>
> gcc/ChangeLog
>
> 2012-08-24  Sterling Augustine <saugustine@google.com>
>             Cary Coutant <ccoutant@google.com>
>
>         * common.opt (gno-split-dwarf, gsplit-dwarf): New switches.
>         * doc/invoke.texi (Debugging Options): Document them.
>         * dwarf2out.h (dw_val_struct): New field val_index.
>         * dwarf2out.c (debug_skeleton_info_section,
>         debug_skeleton_abbrev_section, debug_addr_section,
>         debug_skeleton_line_section, debug_str_offsets_section): New sections.
>         (indirect_string_node): Add index field.
>         (dw_loc_list_node): Add begin_index field.
>         (dw_addr_op, new_addr_loc_descr, AT_index, set_AT_index,
>         add_addr_table_entry, remove_addr_table_entry, output_range_list_offset,
>         output_loc_list_offset, output_attr_index_or_value,
>         remove_loc_list_addr_table_entries, output_die_abbrevs,
>         add_top_level_skeleton_die_attrs, get_skeleton_type_unit,
>         output_skeleton_debug_sections, output_index_strings,
>         output_addr_table, index_location_lists, add_indirect_string): New
>         functions.
>         (DEBUG_DWO_INFO_SECTION, DEBUG_DWO_ABBREV_SECTION, DEBUG_ADDR_SECTION,
>         DEBUG_NORM_MACINFO_SECTION, DEBUG_DWO_MACINFO_SECTION,
>         DEBUG_DWO_LINE_SECTION, DEBUG_DWO_LOC_SECTION,
>         DEBUG_NORM_STR_OFFSETS_SECTION, DEBUG_DWO_STR_SECTION,
>         DEBUG_MACRO_SECTION_FLAGS, DEBUG_SKELETON_LINE_SECTION_LABEL,
>         DEBUG_SKELETON_INFO_SECTION_LABEL, DEBUG_SKELETON_ABBREV_SECTION_LABEL,
>         DEBUG_ADDR_SECTIN_LABEL, SKELETON_COMP_DIE_ABBREV,
>         SKELETON_TYPE_DIE_ABBREV): New defines.
>         (DEBUG_MACINFO_SECTION, DEBUG_MACRO_SECTION, DEBUG_STR_SECTION
>         DEBUG_STR_SECTION_FLAGS): Adjust definitions.
>         (TEXT_SECTION_LABEL, COLD_TEXT_SECTION_LABEL, DEBUG_INFO_SECTION_LABEL,
>         DEBUG_ABBREV_SECTION_LABEL, DEBUG_LOC_SECTION_LABEL,
>         DEBUG_RANGES_SECTION_LABEL, DEBUG_MACINFO_SECTION_LABEL,
>         DEBUG_MACRO_SECTION_LABEL): Adjust indentation.
>         (debug_skeleton_info_section_label, debug_skeleton_abbrev_section_label,
>         debug_addr_section_label, debug_skeleton_line_section_label,
>         dw_id_placeholder): New global variables.
>         (add_AT_lbl_id, add_AT_low_high_pc): Add force_direct parameter.
>         Adjust calls throughout file.  Handle dwarf_split_debug_info.
>         (add_AT_addr). Likewise.  Initialize val_index_field.
>         (add_AT_range_list): Add force parameter.  Adjust calls throughout file.
>         Initialize val_index field.
>         (add_ranges_by_labels): Add and handle force_direct parameter.  Adjust
>         calls throughout file.
>         (size_of_die): New variable form.  Handle dwarf_split_debug_info and
>         call AT_index.
>         (value_format): Use AT_class instead of calling val_class directly.
>         Handle DW_FORM_addr and dw_val_class_lbl_id appropriately for
>         dwarf_split_debug_info and AT_index.
>         (output_abbrev_section): Move most code to new function
>         output_die_abbrevs.
>         (output_loc_list): Handle dwarf_split_debug_info by using
>         DW_LLE_start_length_entry and DW_LLE_end_of_list_entry.
>         (output_die): Call output_attr_index_or_value, output_range_list_offset,
>         Fix format string.  Check val_str->form directly to avoid side effect.
>         (add_pubtype): Fix indention.
>         (output_comdat_type_unit): Handle dwarf_split_debug_info.
>         (output_pubnames): Likewise.
>         (output_aranges): Likewise.
>         (output_mac_info): Likewise.
>         (output_mac_info_op): Check for DW_FORM_GNU_str_index.  Call
>         add_indirect_string.
>         (output_line_info): New parameter prologue_only.  Adjust calls
>         throughout file.
>         (dtprel_bool, dtprel_false, dtprel_true): New enum and enumerators.
>         (mem_loc_descriptor): Call new_addr_loc_descr.
>         (loc_descriptor): Likewise.
>         (add_const_value_attribute): Likewise.
>         (loc_list_from_tree): Replace first_op and second_op with tls_op.
>         Update associated logic.  Call new_addr_loc_descr.
>         (dwarf2out_init): Handle dwarf_split_debug_info.  Initialize
>         debug_skeleton_info_section, debug_skeleton_abbrev_section,
>         debug_addr_section, debug_skeleton_line_section,
>         debug_str_offsets_section, debug_skeleton_info_section_label,
>         debug_skeleton_abbrev_section_label, debug_addr_section_label and
>         debug_skeleton_line_section_label.  Use DEBUG_MACRO_SECTION_FLAGS.
>         (new_loc_descr, build_cfa_loc, add_AT_flag, add_AT_int, add_AT_unsigned,
>         add_AT_double, add_AT_vec, add_AT_data8, add_AT_string, add_AT_die_ref,
>         add_AT_fde_ref, add_AT_loc, add_AT_loc_list, add_AT_file,
>         add_AT_vms_delta, add_AT_lineptr, add_AT_macptr, add_AT_offset):
>         Initialize val_index field.
>         (size_of_loc_descr, output_loc_operands, output_loc_operands_raw,
>         resolve_addr_in_expression, hash_loc_operands): Handle
>         DW_OP_GNU_addr_index and DW_OP_GNU_const_index.
>         (compare_loc_operands):  Likewise.  Adjust assertion.
>         (AT_string_form): Call set_AT_index and add_indirect_string.
>         (resolve_addr): New local variable l.  Check val_index.  Call
>         remove_addr_table_entry and remove_loc_list_addr_table_entries.
>         (dwarf2out_finish): Handle dwarf_split_debug_info.  New variable
>         main_comp_unit_die.  Call index_location_lists, add_AT_data8,
>         add_AT_lineptr, output_skeleton_debug_sections, output_addr_table.  Move
>         call to ASM_GENERATE_INTERNAL_LABEL to dwarf2out_init.  Call
>         ASM_OUTPUT_LABEL for debug_skeleton_line_section_label and
>         debug_addr_section_label.  Adjust comment.
>         * gcc.c (replace_extension_spec_func):  New function.
>         (ASM_FINAL_SPEC): Adjust.
>         (static_spec_functions): Add new field for replace-extension.
>         (check_live_switch): Adjust comment.  Add case for 'g'.
>         * opts.c (finish_options): Set x_debug_generate_pub_sections based on
>         x_dwarf_split_debug_info.  Call set_debug_level.
>         (common_handle_option): Add case for OPT_gsplit_dwarf.
>
> Index: include/dwarf2.def
> ===================================================================
> --- include/dwarf2.def  (revision 190603)
> +++ include/dwarf2.def  (working copy)
> @@ -586,7 +586,7 @@
>  DW_OP (DW_OP_GNU_reinterpret, 0xf9)
>  /* The GNU parameter ref extension.  */
>  DW_OP (DW_OP_GNU_parameter_ref, 0xfa)
> -/* Extension for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
> +/* Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
>  DW_OP (DW_OP_GNU_addr_index, 0xfb)
>  DW_OP (DW_OP_GNU_const_index, 0xfc)
>  /* HP extensions.  */
> Index: include/dwarf2.h
> ===================================================================
> --- include/dwarf2.h    (revision 190603)
> +++ include/dwarf2.h    (working copy)
> @@ -259,6 +259,17 @@
>      DW_LNE_HP_SFC_associate = 3
>    };
>
> +/* Type codes for location list entries.
> +   Extension for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
> +
> +enum dwarf_location_list_entry_type
> +  {
> +    DW_LLE_GNU_end_of_list_entry = 0,
> +    DW_LLE_GNU_base_address_selection_entry = 1,
> +    DW_LLE_GNU_start_end_entry = 2,
> +    DW_LLE_GNU_start_length_entry = 3
> +  };
> +
>  #define DW_CIE_ID        0xffffffff
>  #define DW64_CIE_ID      0xffffffffffffffffULL
>  #define DW_CIE_VERSION   1
> Index: gcc/doc/invoke.texi
> ===================================================================
> --- gcc/doc/invoke.texi (revision 190603)
> +++ gcc/doc/invoke.texi (working copy)
> @@ -4803,6 +4803,14 @@
>  The following options are useful when GCC is generated with the
>  capability for more than one debugging format.
>
> +@item -gsplit-dwarf
> +@opindex gsplit-dwarf
> +Separate as much dwarf debugging information as possible into a
> +separate output file with the extension .dwo.  This option allows
> +the build system to avoid linking files with debug information.  To
> +be useful, this option requires a debugger capable of reading .dwo
> +files.
> +
>  @item -ggdb
>  @opindex ggdb
>  Produce debugging information for use by GDB@.  This means to use the
> Index: gcc/gcc.c
> ===================================================================
> --- gcc/gcc.c   (revision 190603)
> +++ gcc/gcc.c   (working copy)
> @@ -267,6 +267,7 @@
>  static const char *compare_debug_self_opt_spec_function (int, const char **);
>  static const char *compare_debug_auxbase_opt_spec_function (int, const char **);
>  static const char *pass_through_libs_spec_func (int, const char **);
> +static const char *replace_extension_spec_func (int, const char **);
>
>  /* The Specs Language
>
> @@ -447,7 +448,7 @@
>  colon in these constructs, except between . or * and the corresponding
>  word.
>
> -The -O, -f, -m, and -W switches are handled specifically in these
> +The -O, -f, -g, -m, and -W switches are handled specifically in these
>  constructs.  If another value of -O or the negated form of a -f, -m, or
>  -W switch is found later in the command line, the earlier switch
>  value is ignored, except with {S*} where S is just one letter; this
> @@ -480,7 +481,14 @@
>  /* config.h can define ASM_FINAL_SPEC to run a post processor after
>     the assembler has run.  */
>  #ifndef ASM_FINAL_SPEC
> -#define ASM_FINAL_SPEC ""
> +#define ASM_FINAL_SPEC \
> +  "%{gsplit-dwarf: \n\
> +       objcopy --extract-dwo \
> +        %{c:%{o*:%*}%{!o*:%b%O}}%{!c:%U%O} \
> +        %{c:%{o*:%:replace-extension(%{o*:%*} .dwo)}%{!o*:%b.dwo}}%{!c:%b.dwo} \n\
> +       objcopy --strip-dwo \
> +        %{c:%{o*:%*}%{!o*:%b%O}}%{!c:%U%O} \
> +    }"
>  #endif
>
>  /* config.h can define CPP_SPEC to provide extra args to the C preprocessor
> @@ -1262,6 +1270,7 @@
>    { "compare-debug-self-opt",  compare_debug_self_opt_spec_function },
>    { "compare-debug-auxbase-opt", compare_debug_auxbase_opt_spec_function },
>    { "pass-through-libs",       pass_through_libs_spec_func },
> +  { "replace-extension",       replace_extension_spec_func },
>  #ifdef EXTRA_SPEC_FUNCTIONS
>    EXTRA_SPEC_FUNCTIONS
>  #endif
> @@ -5803,7 +5812,7 @@
>     on the command line.  PREFIX_LENGTH is the length of XXX in an {XXX*}
>     spec, or -1 if either exact match or %* is used.
>
> -   A -O switch is obsoleted by a later -O switch.  A -f, -m, or -W switch
> +   A -O switch is obsoleted by a later -O switch.  A -f, -g, -m, or -W switch
>     whose value does not begin with "no-" is obsoleted by the same value
>     with the "no-", similarly for a switch with the "no-" prefix.  */
>
> @@ -5840,7 +5849,7 @@
>           }
>        break;
>
> -    case 'W':  case 'f':  case 'm':
> +    case 'W':  case 'f':  case 'm': case 'g':
>        if (! strncmp (name + 1, "no-", 3))
>         {
>           /* We have Xno-YYY, search for XYYY.  */
> @@ -8363,3 +8372,33 @@
>      }
>    return prepended;
>  }
> +
> +/* %:replace-extension spec function.  Replaces the extension of the
> +   first argument with the second argument.  */
> +
> +const char *
> +replace_extension_spec_func (int argc, const char **argv)
> +{
> +  char *name;
> +  char *p;
> +  char *result;
> +  int i;
> +
> +  if (argc != 2)
> +    fatal_error ("too few arguments to %%:replace-extension");
> +
> +  name = xstrdup (argv[0]);
> +
> +  for (i = strlen(name) - 1; i >= 0; i--)
> +    if (IS_DIR_SEPARATOR (name[i]))
> +      break;
> +
> +  p = strrchr (name + i + 1, '.');
> +  if (p != NULL)
> +      *p = '\0';
> +
> +  result = concat (name, argv[1], NULL);
> +
> +  free (name);
> +  return result;
> +}
> Index: gcc/dwarf2out.c
> ===================================================================
> --- gcc/dwarf2out.c     (revision 190603)
> +++ gcc/dwarf2out.c     (working copy)
> @@ -145,14 +145,19 @@
>
>  /* Pointers to various DWARF2 sections.  */
>  static GTY(()) section *debug_info_section;
> +static GTY(()) section *debug_skeleton_info_section;
>  static GTY(()) section *debug_abbrev_section;
> +static GTY(()) section *debug_skeleton_abbrev_section;
>  static GTY(()) section *debug_aranges_section;
> +static GTY(()) section *debug_addr_section;
>  static GTY(()) section *debug_macinfo_section;
>  static GTY(()) section *debug_line_section;
> +static GTY(()) section *debug_skeleton_line_section;
>  static GTY(()) section *debug_loc_section;
>  static GTY(()) section *debug_pubnames_section;
>  static GTY(()) section *debug_pubtypes_section;
>  static GTY(()) section *debug_str_section;
> +static GTY(()) section *debug_str_offsets_section;
>  static GTY(()) section *debug_ranges_section;
>  static GTY(()) section *debug_frame_section;
>
> @@ -195,6 +200,7 @@
>    unsigned int refcount;
>    enum dwarf_form form;
>    char *label;
> +  unsigned int index;
>  };
>
>  static GTY ((param_is (struct indirect_string_node))) htab_t debug_str_hash;
> @@ -1201,7 +1207,8 @@
>     their entire life.  */
>  typedef struct GTY(()) dw_loc_list_struct {
>    dw_loc_list_ref dw_loc_next;
> -  const char *begin; /* Label for begin address of range */
> +  const char *begin; /* Label and index for begin address of range */
> +  unsigned int begin_index;
>    const char *end;  /* Label for end address of range */
>    char *ll_symbol; /* Label for beginning of location list.
>                       Only on head of list */
> @@ -1246,8 +1253,10 @@
>
>    descr->dw_loc_opc = op;
>    descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const;
> +  descr->dw_loc_oprnd1.val_index = -1U;
>    descr->dw_loc_oprnd1.v.val_unsigned = oprnd1;
>    descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const;
> +  descr->dw_loc_oprnd2.val_index = -1U;
>    descr->dw_loc_oprnd2.v.val_unsigned = oprnd2;
>
>    return descr;
> @@ -1452,6 +1461,10 @@
>      case DW_OP_addr:
>        size += DWARF2_ADDR_SIZE;
>        break;
> +    case DW_OP_GNU_addr_index:
> +    case DW_OP_GNU_const_index:
> +      size += size_of_uleb128 (loc->dw_loc_oprnd1.val_index);
> +      break;
>      case DW_OP_const1u:
>      case DW_OP_const1s:
>        size += 1;
> @@ -1888,6 +1901,12 @@
>         }
>        break;
>
> +    case DW_OP_GNU_addr_index:
> +    case DW_OP_GNU_const_index:
> +      dw2_asm_output_data_uleb128 (loc->dw_loc_oprnd1.val_index,
> +                                   "(index into .debug_addr)");
> +      break;
> +
>      case DW_OP_GNU_implicit_pointer:
>        {
>         char label[MAX_ARTIFICIAL_LABEL_BYTES
> @@ -2063,6 +2082,8 @@
>    switch (loc->dw_loc_opc)
>      {
>      case DW_OP_addr:
> +    case DW_OP_GNU_addr_index:
> +    case DW_OP_GNU_const_index:
>      case DW_OP_implicit_value:
>        /* We cannot output addresses in .cfi_escape, only bytes.  */
>        gcc_unreachable ();
> @@ -2246,6 +2267,7 @@
>      {
>        head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
>        head->dw_loc_oprnd1.val_class = dw_val_class_const;
> +      head->dw_loc_oprnd1.val_index = -1U;
>        tmp = new_loc_descr (DW_OP_deref, 0, 0);
>        add_loc_descr (&head, tmp);
>        if (offset != 0)
> @@ -2875,6 +2897,8 @@
>  static tree decl_class_context (tree);
>  static void add_dwarf_attr (dw_die_ref, dw_attr_ref);
>  static inline enum dw_val_class AT_class (dw_attr_ref);
> +static inline unsigned int AT_index (dw_attr_ref);
> +static inline void set_AT_index (dw_attr_ref, unsigned int);
>  static void add_AT_flag (dw_die_ref, enum dwarf_attribute, unsigned);
>  static inline unsigned AT_flag (dw_attr_ref);
>  static void add_AT_int (dw_die_ref, enum dwarf_attribute, HOST_WIDE_INT);
> @@ -2902,15 +2926,18 @@
>  static void add_AT_loc_list (dw_die_ref, enum dwarf_attribute,
>                              dw_loc_list_ref);
>  static inline dw_loc_list_ref AT_loc_list (dw_attr_ref);
> -static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx);
> +static unsigned int add_addr_table_entry (dw_attr_node *);
> +static void remove_addr_table_entry (unsigned int);
> +static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx, bool);
>  static inline rtx AT_addr (dw_attr_ref);
> -static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *);
> +static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *,
> +                          bool);
>  static void add_AT_lineptr (dw_die_ref, enum dwarf_attribute, const char *);
>  static void add_AT_macptr (dw_die_ref, enum dwarf_attribute, const char *);
>  static void add_AT_offset (dw_die_ref, enum dwarf_attribute,
>                            unsigned HOST_WIDE_INT);
>  static void add_AT_range_list (dw_die_ref, enum dwarf_attribute,
> -                              unsigned long);
> +                              unsigned long, bool);
>  static inline const char *AT_lbl (dw_attr_ref);
>  static dw_attr_ref get_AT (dw_die_ref, enum dwarf_attribute);
>  static const char *get_AT_low_pc (dw_die_ref);
> @@ -3005,6 +3032,7 @@
>  static enum dwarf_form value_format (dw_attr_ref);
>  static void output_value_format (dw_attr_ref);
>  static void output_abbrev_section (void);
> +static void output_die_abbrevs (unsigned long, dw_die_ref);
>  static void output_die_symbol (dw_die_ref);
>  static void output_die (dw_die_ref);
>  static void output_compilation_unit_header (void);
> @@ -3020,10 +3048,10 @@
>  static unsigned int add_ranges_num (int);
>  static unsigned int add_ranges (const_tree);
>  static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
> -                                 bool *);
> +                                 bool *, bool);
>  static void output_ranges (void);
>  static dw_line_info_table *new_line_info_table (void);
> -static void output_line_info (void);
> +static void output_line_info (bool);
>  static void output_file_names (void);
>  static dw_die_ref base_type_die (tree);
>  static int is_base_type (tree);
> @@ -3162,36 +3190,130 @@
>  static void schedule_generic_params_dies_gen (tree t);
>  static void gen_scheduled_generic_parms_dies (void);
>
> +/* enum for tracking thread-local variables whose address is really an offset
> +   relative to the TLS pointer, which will need link-time relocation, but will
> +   not need relocation by the DWARF consumer.  */
> +
> +enum dtprel_bool
> +  {
> +    dtprel_false = 0,
> +    dtprel_true = 1
> +  };
> +
> +/* Return the operator to use for an address of a variable.  For dtprel_true, we
> +   use DW_OP_const*.  For regular variables, which need both link-time
> +   relocation and consumer-level relocation (e.g., to account for shared objects
> +   loaded at a random address), we use DW_OP_addr*.  */
> +
> +static inline enum dwarf_location_atom
> +dw_addr_op (enum dtprel_bool dtprel)
> +{
> +  if (dtprel == dtprel_true)
> +    return (dwarf_split_debug_info ? DW_OP_GNU_const_index
> +            : (DWARF2_ADDR_SIZE == 4 ? DW_OP_const4u : DW_OP_const8u));
> +  else
> +    return (dwarf_split_debug_info ? DW_OP_GNU_addr_index : DW_OP_addr);
> +}
> +
> +/* Return a pointer to a newly allocated address location description.  If
> +   dwarf_split_debug_info is true, then record the address with the appropriate
> +   relocation.  */
> +static inline dw_loc_descr_ref
> +new_addr_loc_descr (rtx addr, enum dtprel_bool dtprel)
> +{
> +  dw_loc_descr_ref ref = new_loc_descr (dw_addr_op (dtprel), 0, 0);
> +
> +  ref->dw_loc_oprnd1.val_class = dw_val_class_addr;
> +  ref->dw_loc_oprnd1.val_index = -1U;
> +  ref->dw_loc_oprnd1.v.val_addr = addr;
> +  ref->dtprel = dtprel;
> +  if (dwarf_split_debug_info)
> +    {
> +      dw_attr_node attr;
> +
> +      attr.dw_attr = DW_AT_location;
> +      attr.dw_attr_val.val_class = (dtprel == dtprel_true
> +                                    ? dw_val_class_loc : dw_val_class_addr);
> +      attr.dw_attr_val.val_index = -1U;
> +      attr.dw_attr_val.v.val_addr = addr;
> +
> +      ref->dw_loc_oprnd1.val_index = add_addr_table_entry (&attr);
> +    }
> +  return ref;
> +}
> +
>  /* Section names used to hold DWARF debugging information.  */
> +
>  #ifndef DEBUG_INFO_SECTION
>  #define DEBUG_INFO_SECTION     ".debug_info"
>  #endif
> +#ifndef DEBUG_DWO_INFO_SECTION
> +#define DEBUG_DWO_INFO_SECTION ".debug_info.dwo"
> +#endif
>  #ifndef DEBUG_ABBREV_SECTION
>  #define DEBUG_ABBREV_SECTION   ".debug_abbrev"
>  #endif
> +#ifndef DEBUG_DWO_ABBREV_SECTION
> +#define DEBUG_DWO_ABBREV_SECTION ".debug_abbrev.dwo"
> +#endif
>  #ifndef DEBUG_ARANGES_SECTION
>  #define DEBUG_ARANGES_SECTION  ".debug_aranges"
>  #endif
> +#ifndef DEBUG_ADDR_SECTION
> +#define DEBUG_ADDR_SECTION     ".debug_addr"
> +#endif
> +#ifndef DEBUG_NORM_MACINFO_SECTION
> +#define DEBUG_NORM_MACINFO_SECTION     ".debug_macinfo"
> +#endif
> +#ifndef DEBUG_DWO_MACINFO_SECTION
> +#define DEBUG_DWO_MACINFO_SECTION      ".debug_macinfo.dwo"
> +#endif
>  #ifndef DEBUG_MACINFO_SECTION
> -#define DEBUG_MACINFO_SECTION  ".debug_macinfo"
> +#define DEBUG_MACINFO_SECTION                                           \
> +  (!dwarf_split_debug_info                                              \
> +   ? (DEBUG_NORM_MACINFO_SECTION) : (DEBUG_DWO_MACINFO_SECTION))
>  #endif
> +#ifndef DEBUG_NORM_MACRO_SECTION
> +#define DEBUG_NORM_MACRO_SECTION ".debug_macro"
> +#endif
> +#ifndef DEBUG_DWO_MACRO_SECTION
> +#define DEBUG_DWO_MACRO_SECTION        ".debug_macro.dwo"
> +#endif
>  #ifndef DEBUG_MACRO_SECTION
> -#define DEBUG_MACRO_SECTION    ".debug_macro"
> +#define DEBUG_MACRO_SECTION                                             \
> +  (!dwarf_split_debug_info                                              \
> +   ? (DEBUG_NORM_MACRO_SECTION) : (DEBUG_DWO_MACRO_SECTION))
>  #endif
>  #ifndef DEBUG_LINE_SECTION
>  #define DEBUG_LINE_SECTION     ".debug_line"
>  #endif
> +#ifndef DEBUG_DWO_LINE_SECTION
> +#define DEBUG_DWO_LINE_SECTION ".debug_line.dwo"
> +#endif
>  #ifndef DEBUG_LOC_SECTION
>  #define DEBUG_LOC_SECTION      ".debug_loc"
>  #endif
> +#ifndef DEBUG_DWO_LOC_SECTION
> +#define DEBUG_DWO_LOC_SECTION  ".debug_loc.dwo"
> +#endif
>  #ifndef DEBUG_PUBNAMES_SECTION
>  #define DEBUG_PUBNAMES_SECTION ".debug_pubnames"
>  #endif
>  #ifndef DEBUG_PUBTYPES_SECTION
>  #define DEBUG_PUBTYPES_SECTION ".debug_pubtypes"
>  #endif
> +#define DEBUG_NORM_STR_OFFSETS_SECTION ".debug_str_offsets"
> +#define DEBUG_DWO_STR_OFFSETS_SECTION ".debug_str_offsets.dwo"
> +#ifndef DEBUG_STR_OFFSETS_SECTION
> +#define DEBUG_STR_OFFSETS_SECTION                                       \
> +  (!dwarf_split_debug_info                                              \
> +   ? (DEBUG_NORM_STR_OFFSETS_SECTION) : (DEBUG_DWO_STR_OFFSETS_SECTION))
> +#endif
> +#define DEBUG_DWO_STR_SECTION   ".debug_str.dwo"
> +#define DEBUG_NORM_STR_SECTION  ".debug_str"
>  #ifndef DEBUG_STR_SECTION
> -#define DEBUG_STR_SECTION      ".debug_str"
> +#define DEBUG_STR_SECTION                               \
> +  (!dwarf_split_debug_info ? (DEBUG_NORM_STR_SECTION) : (DEBUG_DWO_STR_SECTION))
>  #endif
>  #ifndef DEBUG_RANGES_SECTION
>  #define DEBUG_RANGES_SECTION   ".debug_ranges"
> @@ -3202,44 +3324,63 @@
>  #define TEXT_SECTION_NAME      ".text"
>  #endif
>
> +/* Section flags for .debug_macinfo/.debug_macro section.  */
> +#define DEBUG_MACRO_SECTION_FLAGS \
> +  (dwarf_split_debug_info ? SECTION_DEBUG | SECTION_EXCLUDE : SECTION_DEBUG)
> +
>  /* Section flags for .debug_str section.  */
>  #define DEBUG_STR_SECTION_FLAGS \
> -  (HAVE_GAS_SHF_MERGE && flag_merge_debug_strings              \
> -   ? SECTION_DEBUG | SECTION_MERGE | SECTION_STRINGS | 1       \
> -   : SECTION_DEBUG)
> +  (dwarf_split_debug_info \
> +   ? SECTION_DEBUG | SECTION_EXCLUDE \
> +   : (HAVE_GAS_SHF_MERGE && flag_merge_debug_strings \
> +      ? SECTION_DEBUG | SECTION_MERGE | SECTION_STRINGS | 1        \
> +      : SECTION_DEBUG))
>
>  /* Labels we insert at beginning sections we can reference instead of
>     the section names themselves.  */
>
>  #ifndef TEXT_SECTION_LABEL
> -#define TEXT_SECTION_LABEL             "Ltext"
> +#define TEXT_SECTION_LABEL                 "Ltext"
>  #endif
>  #ifndef COLD_TEXT_SECTION_LABEL
> -#define COLD_TEXT_SECTION_LABEL         "Ltext_cold"
> +#define COLD_TEXT_SECTION_LABEL             "Ltext_cold"
>  #endif
>  #ifndef DEBUG_LINE_SECTION_LABEL
> -#define DEBUG_LINE_SECTION_LABEL       "Ldebug_line"
> +#define DEBUG_LINE_SECTION_LABEL           "Ldebug_line"
>  #endif
> +#ifndef DEBUG_SKELETON_LINE_SECTION_LABEL
> +#define DEBUG_SKELETON_LINE_SECTION_LABEL   "Lskeleton_debug_line"
> +#endif
>  #ifndef DEBUG_INFO_SECTION_LABEL
> -#define DEBUG_INFO_SECTION_LABEL       "Ldebug_info"
> +#define DEBUG_INFO_SECTION_LABEL           "Ldebug_info"
>  #endif
> +#ifndef DEBUG_SKELETON_INFO_SECTION_LABEL
> +#define DEBUG_SKELETON_INFO_SECTION_LABEL   "Lskeleton_debug_info"
> +#endif
>  #ifndef DEBUG_ABBREV_SECTION_LABEL
> -#define DEBUG_ABBREV_SECTION_LABEL     "Ldebug_abbrev"
> +#define DEBUG_ABBREV_SECTION_LABEL         "Ldebug_abbrev"
>  #endif
> +#ifndef DEBUG_SKELETON_ABBREV_SECTION_LABEL
> +#define DEBUG_SKELETON_ABBREV_SECTION_LABEL "Lskeleton_debug_abbrev"
> +#endif
> +#ifndef DEBUG_ADDR_SECTION_LABEL
> +#define DEBUG_ADDR_SECTION_LABEL           "Ldebug_addr"
> +#endif
>  #ifndef DEBUG_LOC_SECTION_LABEL
> -#define DEBUG_LOC_SECTION_LABEL                "Ldebug_loc"
> +#define DEBUG_LOC_SECTION_LABEL                    "Ldebug_loc"
>  #endif
>  #ifndef DEBUG_RANGES_SECTION_LABEL
> -#define DEBUG_RANGES_SECTION_LABEL     "Ldebug_ranges"
> +#define DEBUG_RANGES_SECTION_LABEL         "Ldebug_ranges"
>  #endif
>  #ifndef DEBUG_MACINFO_SECTION_LABEL
> -#define DEBUG_MACINFO_SECTION_LABEL     "Ldebug_macinfo"
> +#define DEBUG_MACINFO_SECTION_LABEL         "Ldebug_macinfo"
>  #endif
>  #ifndef DEBUG_MACRO_SECTION_LABEL
> -#define DEBUG_MACRO_SECTION_LABEL      "Ldebug_macro"
> +#define DEBUG_MACRO_SECTION_LABEL          "Ldebug_macro"
>  #endif
> +#define SKELETON_COMP_DIE_ABBREV 1
> +#define SKELETON_TYPE_DIE_ABBREV 2
>
> -
>  /* Definitions of defaults for formats and names of various special
>     (artificial) labels which may be generated within this file (when the -g
>     options is used and DWARF2_DEBUGGING_INFO is in effect.
> @@ -3252,7 +3393,11 @@
>  static char cold_end_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char abbrev_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char debug_info_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
> +static char debug_skeleton_info_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
> +static char debug_skeleton_abbrev_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char debug_line_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
> +static char debug_addr_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
> +static char debug_skeleton_line_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char macinfo_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char loc_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
>  static char ranges_section_label[2 * MAX_ARTIFICIAL_LABEL_BYTES];
> @@ -3493,6 +3638,31 @@
>    return a->dw_attr_val.val_class;
>  }
>
> +/* Return the index for any attribute that will be referenced with a
> +   DW_FORM_GNU_addr_index.  Strings have their indices handled differently to
> +   account for reference counting pruning.  */
> +
> +static inline unsigned int
> +AT_index (dw_attr_ref a)
> +{
> +  if (AT_class (a) == dw_val_class_str)
> +    return a->dw_attr_val.v.val_str->index;
> +  else
> +    return a->dw_attr_val.val_index;
> +}
> +
> +/* Set the index for any attribute that will be referenced with a
> +   DW_FORM_GNU_addr_index.  */
> +
> +static inline void
> +set_AT_index (dw_attr_ref a, unsigned int index)
> +{
> +  if (AT_class (a) == dw_val_class_str)
> +    a->dw_attr_val.v.val_str->index = index;
> +  else
> +    a->dw_attr_val.val_index = index;
> +}
> +
>  /* Add a flag value attribute to a DIE.  */
>
>  static inline void
> @@ -3502,6 +3672,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_flag;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_flag = flag;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3522,6 +3693,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_const;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_int = int_val;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3543,6 +3715,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_unsigned_const;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_unsigned = unsigned_val;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3564,6 +3737,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_const_double;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_double.high = high;
>    attr.dw_attr_val.v.val_double.low = low;
>    add_dwarf_attr (die, &attr);
> @@ -3579,6 +3753,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_vec;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_vec.length = length;
>    attr.dw_attr_val.v.val_vec.elt_size = elt_size;
>    attr.dw_attr_val.v.val_vec.array = array;
> @@ -3595,28 +3770,39 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_data8;
> +  attr.dw_attr_val.val_index = -1U;
>    memcpy (attr.dw_attr_val.v.val_data8, data8, 8);
>    add_dwarf_attr (die, &attr);
>  }
>
> -/* Add DW_AT_low_pc and DW_AT_high_pc to a DIE.  */
> +/* Add DW_AT_low_pc and DW_AT_high_pc to a DIE.  The parameter force_direct
> +   makes the attribute use DW_AT_addr, rather than DW_AT_GNU_addr_index.  */
> +
>  static inline void
> -add_AT_low_high_pc (dw_die_ref die, const char *lbl_low, const char *lbl_high)
> +add_AT_low_high_pc (dw_die_ref die, const char *lbl_low, const char *lbl_high,
> +                   bool force_direct)
>  {
>    dw_attr_node attr;
>
>    attr.dw_attr = DW_AT_low_pc;
>    attr.dw_attr_val.val_class = dw_val_class_lbl_id;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_low);
>    add_dwarf_attr (die, &attr);
> +  if (dwarf_split_debug_info && !force_direct)
> +    set_AT_index (get_AT (die, DW_AT_low_pc), add_addr_table_entry (&attr));
>
>    attr.dw_attr = DW_AT_high_pc;
>    if (dwarf_version < 4)
>      attr.dw_attr_val.val_class = dw_val_class_lbl_id;
>    else
>      attr.dw_attr_val.val_class = dw_val_class_high_pc;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_high);
>    add_dwarf_attr (die, &attr);
> +  if (attr.dw_attr_val.val_class == dw_val_class_lbl_id
> +      && dwarf_split_debug_info && !force_direct)
> +    set_AT_index (get_AT (die, DW_AT_high_pc), add_addr_table_entry (&attr));
>  }
>
>  /* Hash and equality functions for debug_str_hash.  */
> @@ -3673,6 +3859,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_str;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_str = node;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3684,6 +3871,38 @@
>    return a->dw_attr_val.v.val_str->str;
>  }
>
> +/* A vector for a table of strings in the form DW_FORM_GNU_index_str.  */
> +
> +typedef struct indirect_string_node indirect_string_node;
> +DEF_VEC_O(indirect_string_node);
> +DEF_VEC_ALLOC_O(indirect_string_node, gc);
> +static GTY (()) VEC (indirect_string_node, gc) *index_string_table;
> +
> +/* Add a new indirect string to the appropriate tables.  Returns the index of
> +   the new string.  Call this function directly to bypass AT_string_form's logic
> +   to put the string inline in the die. */
> +
> +static unsigned long
> +add_indirect_string (struct indirect_string_node *node, const char *str)
> +{
> +  static unsigned int index_string_count = 0;
> +  ++dw2_string_counter;
> +  node->label = xstrdup (str);
> +
> +  if (!dwarf_split_debug_info)
> +    {
> +      node->form = DW_FORM_strp;
> +      return -1U;
> +    }
> +  else
> +    {
> +      node->form = DW_FORM_GNU_str_index;
> +      index_string_count++;
> +      VEC_safe_push (indirect_string_node, gc, index_string_table, node);
> +      return index_string_count;
> +    }
> +}
> +
>  /* Find out whether a string should be output inline in DIE
>     or out-of-line in .debug_str section.  */
>
> @@ -3716,10 +3935,9 @@
>      return node->form = DW_FORM_string;
>
>    ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
> -  ++dw2_string_counter;
> -  node->label = xstrdup (label);
> +  set_AT_index (a, add_indirect_string (node, label));
>
> -  return node->form = DW_FORM_strp;
> +  return node->form;
>  }
>
>  /* Add a DIE reference attribute value to a DIE.  */
> @@ -3740,6 +3958,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_die_ref;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_die_ref.die = targ_die;
>    attr.dw_attr_val.v.val_die_ref.external = 0;
>    add_dwarf_attr (die, &attr);
> @@ -3798,6 +4017,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_fde_ref;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_fde_index = targ_fde;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3811,6 +4031,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_loc;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_loc = loc;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3829,6 +4050,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_loc_list;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_loc_list = loc_list;
>    add_dwarf_attr (die, &attr);
>    have_location_lists = true;
> @@ -3848,17 +4070,62 @@
>    return &a->dw_attr_val.v.val_loc_list;
>  }
>
> -/* Add an address constant attribute value to a DIE.  */
> +/* A table of entries into the .debug_addr section.  */
>
> +static GTY (()) VEC (dw_attr_node,gc) * addr_index_table;
> +
> +static unsigned int
> +add_addr_table_entry (dw_attr_node *attr)
> +{
> +  gcc_assert (dwarf_split_debug_info);
> +
> +  VEC_safe_push (dw_attr_node, gc, addr_index_table, attr);
> +  return VEC_length (dw_attr_node, addr_index_table) - 1;
> +}
> +
> +/* Remove an entry from the addr table.  Since we have already numbered
> +   all the entries, the best we can do here is null it out.  */
> +
> +static void
> +remove_addr_table_entry (unsigned int i)
> +{
> +  dw_attr_node *attr;
> +
> +  gcc_assert (dwarf_split_debug_info);
> +  gcc_assert (i < VEC_length (dw_attr_node, addr_index_table));
> +
> +  attr = &VEC_index (dw_attr_node, addr_index_table, i);
> +  attr->dw_attr = (enum dwarf_attribute) 0;
> +}
> +
> +/* Given a location list, remove all addresses it refers to from the
> +   address_table.  */
> +
> +static void
> +remove_loc_list_addr_table_entries (dw_loc_descr_ref descr)
> +{
> +  for (; descr; descr = descr->dw_loc_next)
> +    if (descr->dw_loc_oprnd1.val_index != -1U)
> +      remove_addr_table_entry (descr->dw_loc_oprnd1.val_index);
> +}
> +
> +/* Add an address constant attribute value to a DIE.  The parameter
> +   force_direct makes the attribute use DW_AT_addr, rather than
> +   DW_AT_GNU_addr_index.  */
> +
>  static inline void
> -add_AT_addr (dw_die_ref die, enum dwarf_attribute attr_kind, rtx addr)
> +add_AT_addr (dw_die_ref die, enum dwarf_attribute attr_kind, rtx addr,
> +            bool force_direct)
>  {
>    dw_attr_node attr;
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_addr;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_addr = addr;
>    add_dwarf_attr (die, &attr);
> +  if (dwarf_split_debug_info && !force_direct)
> +    set_AT_index (get_AT (die, attr_kind), add_addr_table_entry (&attr));
>  }
>
>  /* Get the RTX from to an address DIE attribute.  */
> @@ -3880,6 +4147,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_file;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_file = fd;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3903,22 +4171,29 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_vms_delta;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_vms_delta.lbl1 = xstrdup (lbl1);
>    attr.dw_attr_val.v.val_vms_delta.lbl2 = xstrdup (lbl2);
>    add_dwarf_attr (die, &attr);
>  }
>
> -/* Add a label identifier attribute value to a DIE.  */
> +/* Add a label identifier attribute value to a DIE.  The parameter
> +   force_direct makes the attribute use DW_AT_addr, rather than
> +   DW_AT_GNU_addr_index.  */
>
>  static inline void
> -add_AT_lbl_id (dw_die_ref die, enum dwarf_attribute attr_kind, const char *lbl_id)
> +add_AT_lbl_id (dw_die_ref die, enum dwarf_attribute attr_kind,
> +              const char *lbl_id, bool force_direct)
>  {
>    dw_attr_node attr;
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_lbl_id;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id);
>    add_dwarf_attr (die, &attr);
> +  if (dwarf_split_debug_info && !force_direct)
> +    set_AT_index (get_AT (die, attr_kind), add_addr_table_entry (&attr));
>  }
>
>  /* Add a section offset attribute value to a DIE, an offset into the
> @@ -3932,6 +4207,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_lineptr;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3947,6 +4223,7 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_macptr;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
>    add_dwarf_attr (die, &attr);
>  }
> @@ -3961,20 +4238,31 @@
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_offset;
> +  attr.dw_attr_val.val_index = -1U;
>    attr.dw_attr_val.v.val_offset = offset;
>    add_dwarf_attr (die, &attr);
>  }
>
> -/* Add an range_list attribute value to a DIE.  */
> +/* Add a range_list attribute value to a DIE.  The parameter
> +   force_direct makes the attribute use DW_AT_addr, rather than
> +   DW_AT_GNU_addr_index.  */
>
>  static void
>  add_AT_range_list (dw_die_ref die, enum dwarf_attribute attr_kind,
> -                  long unsigned int offset)
> +                  long unsigned int offset, bool force_direct)
>  {
>    dw_attr_node attr;
>
>    attr.dw_attr = attr_kind;
>    attr.dw_attr_val.val_class = dw_val_class_range_list;
> +  /* For the range_list attribute, val_index == -1U indicates that we want to
> +     output a relocated reference to the range list entry, while any other
> +     value indicates that we want to output the section-relative offset of the
> +     range list entry. In this case, we're not using the val_index field as a
> +     slot index like we do for references to .debug_addr.  This is used
> +     in output_range_list_offset.  */
> +  attr.dw_attr_val.val_index
> +      = (dwarf_split_debug_info && !force_direct) ? offset : -1U;
>    attr.dw_attr_val.v.val_offset = offset;
>    add_dwarf_attr (die, &attr);
>  }
> @@ -7158,6 +7446,7 @@
>    unsigned long size = 0;
>    dw_attr_ref a;
>    unsigned ix;
> +  enum dwarf_form form;
>
>    size += size_of_uleb128 (die->die_abbrev);
>    FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a)
> @@ -7165,7 +7454,10 @@
>        switch (AT_class (a))
>         {
>         case dw_val_class_addr:
> -         size += DWARF2_ADDR_SIZE;
> +          if (dwarf_split_debug_info && AT_index (a) != -1U)
> +            size += size_of_uleb128 (AT_index (a));
> +          else
> +            size += DWARF2_ADDR_SIZE;
>           break;
>         case dw_val_class_offset:
>           size += DWARF_OFFSET_SIZE;
> @@ -7183,10 +7475,13 @@
>           }
>           break;
>         case dw_val_class_loc_list:
> -         size += DWARF_OFFSET_SIZE;
> +          if (dwarf_split_debug_info && AT_index (a) != -1U)
> +            size += size_of_uleb128 (AT_index (a));
> +          else
> +            size += DWARF_OFFSET_SIZE;
>           break;
>         case dw_val_class_range_list:
> -         size += DWARF_OFFSET_SIZE;
> +          size += DWARF_OFFSET_SIZE;
>           break;
>         case dw_val_class_const:
>           size += size_of_sleb128 (AT_int (a));
> @@ -7246,15 +7541,21 @@
>           size += DWARF_OFFSET_SIZE;
>           break;
>         case dw_val_class_lbl_id:
> -         size += DWARF2_ADDR_SIZE;
> +          if (dwarf_split_debug_info && AT_index (a) != -1U)
> +            size += size_of_uleb128 (AT_index (a));
> +          else
> +            size += DWARF2_ADDR_SIZE;
>           break;
>         case dw_val_class_lineptr:
>         case dw_val_class_macptr:
> -         size += DWARF_OFFSET_SIZE;
> +          size += DWARF_OFFSET_SIZE;
>           break;
>         case dw_val_class_str:
> -         if (AT_string_form (a) == DW_FORM_strp)
> +          form = AT_string_form (a);
> +          if (form == DW_FORM_strp)
>             size += DWARF_OFFSET_SIZE;
> +         else if (form == DW_FORM_GNU_str_index)
> +            size += size_of_uleb128 (AT_index (a));
>           else
>             size += strlen (a->dw_attr_val.v.val_str->str) + 1;
>           break;
> @@ -7439,7 +7740,7 @@
>  static enum dwarf_form
>  value_format (dw_attr_ref a)
>  {
> -  switch (a->dw_attr_val.val_class)
> +  switch (AT_class (a))
>      {
>      case dw_val_class_addr:
>        /* Only very few attributes allow DW_FORM_addr.  */
> @@ -7449,7 +7750,8 @@
>         case DW_AT_high_pc:
>         case DW_AT_entry_pc:
>         case DW_AT_trampoline:
> -         return DW_FORM_addr;
> +          return (dwarf_split_debug_info && AT_index (a) != -1U
> +                 ? DW_FORM_GNU_addr_index : DW_FORM_addr);
>         default:
>           break;
>         }
> @@ -7565,7 +7867,8 @@
>      case dw_val_class_fde_ref:
>        return DW_FORM_data;
>      case dw_val_class_lbl_id:
> -      return DW_FORM_addr;
> +      return (dwarf_split_debug_info && AT_index (a) != -1U
> +             ? DW_FORM_GNU_addr_index : DW_FORM_addr);
>      case dw_val_class_lineptr:
>      case dw_val_class_macptr:
>        return dwarf_version >= 4 ? DW_FORM_sec_offset : DW_FORM_data;
> @@ -7613,6 +7916,36 @@
>    dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form));
>  }
>
> +/* Given a die and id, produce the appropriate abbreviations.  */
> +
> +static void
> +output_die_abbrevs (unsigned long abbrev_id, dw_die_ref abbrev)
> +{
> +  unsigned ix;
> +  dw_attr_ref a_attr;
> +
> +  dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)");
> +  dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
> +                               dwarf_tag_name (abbrev->die_tag));
> +
> +  if (abbrev->die_child != NULL)
> +    dw2_asm_output_data (1, DW_children_yes, "DW_children_yes");
> +  else
> +    dw2_asm_output_data (1, DW_children_no, "DW_children_no");
> +
> +  for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr);
> +       ix++)
> +    {
> +      dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)",
> +                                   dwarf_attr_name (a_attr->dw_attr));
> +      output_value_format (a_attr);
> +    }
> +
> +  dw2_asm_output_data (1, 0, NULL);
> +  dw2_asm_output_data (1, 0, NULL);
> +}
> +
> +
>  /* Output the .debug_abbrev section which defines the DIE abbreviation
>     table.  */
>
> @@ -7622,32 +7955,8 @@
>    unsigned long abbrev_id;
>
>    for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
> -    {
> -      dw_die_ref abbrev = abbrev_die_table[abbrev_id];
> -      unsigned ix;
> -      dw_attr_ref a_attr;
> +    output_die_abbrevs (abbrev_id, abbrev_die_table[abbrev_id]);
>
> -      dw2_asm_output_data_uleb128 (abbrev_id, "(abbrev code)");
> -      dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
> -                                  dwarf_tag_name (abbrev->die_tag));
> -
> -      if (abbrev->die_child != NULL)
> -       dw2_asm_output_data (1, DW_children_yes, "DW_children_yes");
> -      else
> -       dw2_asm_output_data (1, DW_children_no, "DW_children_no");
> -
> -      for (ix = 0; VEC_iterate (dw_attr_node, abbrev->die_attr, ix, a_attr);
> -          ix++)
> -       {
> -         dw2_asm_output_data_uleb128 (a_attr->dw_attr, "(%s)",
> -                                      dwarf_attr_name (a_attr->dw_attr));
> -         output_value_format (a_attr);
> -       }
> -
> -      dw2_asm_output_data (1, 0, NULL);
> -      dw2_asm_output_data (1, 0, NULL);
> -    }
> -
>    /* Terminate the table.  */
>    dw2_asm_output_data (1, 0, NULL);
>  }
> @@ -7683,6 +7992,7 @@
>    dw_loc_list_ref retlist = ggc_alloc_cleared_dw_loc_list_node ();
>
>    retlist->begin = begin;
> +  retlist->begin_index = -1U;
>    retlist->end = end;
>    retlist->expr = expr;
>    retlist->section = section;
> @@ -7727,7 +8037,22 @@
>          in a single range are unlikely very useful.  */
>        if (size > 0xffff)
>         continue;
> -      if (!have_multiple_function_sections)
> +      if (dwarf_split_debug_info)
> +        {
> +          dw2_asm_output_data (1, DW_LLE_GNU_start_length_entry,
> +                               "Location list start/length entry (%s)",
> +                               list_head->ll_symbol);
> +          dw2_asm_output_data_uleb128 (curr->begin_index,
> +                                       "Location list range start index (%s)",
> +                                       curr->begin);
> +          /* The length field is 4 bytes.  If we ever need to support
> +            an 8-byte length, we can add a new DW_LLE code or fall back
> +            to DW_LLE_GNU_start_end_entry.  */
> +          dw2_asm_output_delta (4, curr->end, curr->begin,
> +                               "Location list range length (%s)",
> +                               list_head->ll_symbol);
> +        }
> +      else if (!have_multiple_function_sections)
>         {
>           dw2_asm_output_delta (DWARF2_ADDR_SIZE, curr->begin, curr->section,
>                                 "Location list begin address (%s)",
> @@ -7753,14 +8078,85 @@
>        output_loc_sequence (curr->expr, -1);
>      }
>
> -  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);
> +  if (dwarf_split_debug_info)
> +    dw2_asm_output_data (1, DW_LLE_GNU_end_of_list_entry,
> +                        "Location list terminator (%s)",
> +                        list_head->ll_symbol);
> +  else
> +    {
> +      dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
> +                          "Location list terminator begin (%s)",
> +                          list_head->ll_symbol);
> +      dw2_asm_output_data (DWARF2_ADDR_SIZE, 0,
> +                          "Location list terminator end (%s)",
> +                          list_head->ll_symbol);
> +    }
>  }
>
> +/* Output the offset into the debug_range section.  */
> +
> +static void
> +output_range_list_offset (dw_attr_ref a)
> +{
> +  const char *name = dwarf_attr_name (a->dw_attr);
> +
> +  if (!dwarf_split_debug_info || AT_index (a) == -1U)
> +    {
> +      char *p = strchr (ranges_section_label, '\0');
> +      sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX, a->dw_attr_val.v.val_offset);
> +      dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label,
> +                             debug_ranges_section, "%s", name);
> +      *p = '\0';
> +    }
> +  else
> +    dw2_asm_output_data (DWARF_OFFSET_SIZE, a->dw_attr_val.v.val_offset,
> +                         "%s (offset from %s)", name, ranges_section_label);
> +}
> +
> +/* Output the offset into the debug_loc section.  */
> +
> +static void
> +output_loc_list_offset (dw_attr_ref a)
> +{
> +  char *sym = AT_loc_list (a)->ll_symbol;
> +
> +  gcc_assert (sym);
> +  if (dwarf_split_debug_info)
> +    dw2_asm_output_delta (DWARF_OFFSET_SIZE, sym, loc_section_label,
> +                          "%s", dwarf_attr_name (a->dw_attr));
> +  else
> +    dw2_asm_output_offset (DWARF_OFFSET_SIZE, sym, debug_loc_section,
> +                           "%s", dwarf_attr_name (a->dw_attr));
> +}
> +
> +/* Output an attribute's index or value appropriately.  */
> +
> +static void
> +output_attr_index_or_value (dw_attr_ref a)
> +{
> +  const char *name = dwarf_attr_name (a->dw_attr);
> +
> +  if (dwarf_split_debug_info && AT_index (a) != -1U)
> +    {
> +      dw2_asm_output_data_uleb128 (AT_index (a), "%s", name);
> +      return;
> +    }
> +  switch (AT_class (a))
> +    {
> +      case dw_val_class_addr:
> +        dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name);
> +        break;
> +      case dw_val_class_lbl_id:
> +        dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name);
> +        break;
> +      case dw_val_class_loc_list:
> +        output_loc_list_offset (a);
> +        break;
> +      default:
> +        gcc_unreachable ();
> +    }
> +}
> +
>  /* Output a type signature.  */
>
>  static inline void
> @@ -7799,7 +8195,7 @@
>        switch (AT_class (a))
>         {
>         case dw_val_class_addr:
> -         dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (a), "%s", name);
> +          output_attr_index_or_value (a);
>           break;
>
>         case dw_val_class_offset:
> @@ -7808,15 +8204,7 @@
>           break;
>
>         case dw_val_class_range_list:
> -         {
> -           char *p = strchr (ranges_section_label, '\0');
> -
> -           sprintf (p, "+" HOST_WIDE_INT_PRINT_HEX,
> -                    a->dw_attr_val.v.val_offset);
> -           dw2_asm_output_offset (DWARF_OFFSET_SIZE, ranges_section_label,
> -                                  debug_ranges_section, "%s", name);
> -           *p = '\0';
> -         }
> +          output_range_list_offset (a);
>           break;
>
>         case dw_val_class_loc:
> @@ -7872,7 +8260,7 @@
>               }
>
>             dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
> -                                first, name);
> +                                first, "%s", name);
>             dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
>                                  second, NULL);
>           }
> @@ -7919,13 +8307,7 @@
>           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);
> -         }
> +          output_attr_index_or_value (a);
>           break;
>
>         case dw_val_class_die_ref:
> @@ -7982,7 +8364,7 @@
>           break;
>
>         case dw_val_class_lbl_id:
> -         dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (a), "%s", name);
> +          output_attr_index_or_value (a);
>           break;
>
>         case dw_val_class_lineptr:
> @@ -7996,12 +8378,15 @@
>           break;
>
>         case dw_val_class_str:
> -         if (AT_string_form (a) == DW_FORM_strp)
> +         if (a->dw_attr_val.v.val_str->form == 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
> +         else if (a->dw_attr_val.v.val_str->form == DW_FORM_GNU_str_index)
> +            dw2_asm_output_data_uleb128 (AT_index (a),
> +                                         "%s: \"%s\"", name, AT_string (a));
> +          else
>             dw2_asm_output_nstring (AT_string (a), -1, "%s", name);
>           break;
>
> @@ -8144,6 +8529,96 @@
>      add_AT_flag (die, DW_AT_GNU_pubnames, 1);
>  }
>
> +/* Helper function to generate top-level dies for skeleton debug_info and
> +   debug_types.  */
> +
> +static void
> +add_top_level_skeleton_die_attrs (dw_die_ref die)
> +{
> +  const char *dwo_file_name = concat (aux_base_name, ".dwo", NULL);
> +  dw_attr_ref attr;
> +
> +  add_comp_dir_attribute (die);
> +  add_AT_string (die, DW_AT_GNU_dwo_name, dwo_file_name);
> +  /* The specification suggests that these attributes be inline to avoid
> +     having a .debug_str section.  We know that they exist in the die because
> +     we just added them.  */
> +  attr = get_AT (die, DW_AT_GNU_dwo_name);
> +  attr->dw_attr_val.v.val_str->form = DW_FORM_string;
> +  attr = get_AT (die, DW_AT_comp_dir);
> +  attr->dw_attr_val.v.val_str->form = DW_FORM_string;
> +
> +  add_AT_pubnames (die);
> +  add_AT_lineptr (die, DW_AT_GNU_addr_base, debug_addr_section_label);
> +}
> +
> +/* Return the single type-unit die for skeleton type units.  */
> +
> +static dw_die_ref
> +get_skeleton_type_unit (void)
> +{
> +  /* For dwarf_split_debug_sections with use_type info, all type units in the
> +     skeleton sections have identical dies (but different headers).  This
> +     single die will be output many times.  */
> +
> +  static dw_die_ref skeleton_type_unit = NULL;
> +
> +  if (skeleton_type_unit == NULL)
> +    {
> +      skeleton_type_unit = new_die (DW_TAG_type_unit, NULL, NULL);
> +      add_top_level_skeleton_die_attrs (skeleton_type_unit);
> +      skeleton_type_unit->die_abbrev = SKELETON_TYPE_DIE_ABBREV;
> +    }
> +  return skeleton_type_unit;
> +}
> +
> +/* Output skeleton debug sections that point to the dwo file.  */
> +
> +static void
> +output_skeleton_debug_sections (dw_die_ref comp_unit)
> +{
> +  /* These attributes will be found in the full debug_info section.  */
> +  remove_AT (comp_unit, DW_AT_producer);
> +  remove_AT (comp_unit, DW_AT_language);
> +
> +  /* Add attributes common to skeleton compile_units and type_units.  */
> +  add_top_level_skeleton_die_attrs (comp_unit);
> +
> +  switch_to_section (debug_skeleton_info_section);
> +  ASM_OUTPUT_LABEL (asm_out_file, debug_skeleton_info_section_label);
> +
> +  /* Produce the skeleton compilation-unit header.  This one differs enough from
> +     a normal CU header that it's better not to call output_compilation_unit
> +     header.  */
> +  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,
> +                      DWARF_COMPILE_UNIT_HEADER_SIZE
> +                       - DWARF_INITIAL_LENGTH_SIZE
> +                       + size_of_die (comp_unit),
> +                      "Length of Compilation Unit Info");
> +  dw2_asm_output_data (2, dwarf_version, "DWARF version number");
> +  dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_skeleton_abbrev_section_label,
> +                        debug_abbrev_section,
> +                        "Offset Into Abbrev. Section");
> +  dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
> +
> +  comp_unit->die_abbrev = SKELETON_COMP_DIE_ABBREV;
> +  output_die (comp_unit);
> +
> +  /* Build the skeleton debug_abbrev section.  */
> +  switch_to_section (debug_skeleton_abbrev_section);
> +  ASM_OUTPUT_LABEL (asm_out_file, debug_skeleton_abbrev_section_label);
> +
> +  output_die_abbrevs (SKELETON_COMP_DIE_ABBREV, comp_unit);
> +  if (use_debug_types)
> +    output_die_abbrevs (SKELETON_TYPE_DIE_ABBREV, get_skeleton_type_unit ());
> +
> +  dw2_asm_output_data (1, 0, "end of skeleton .debug_abbrev");
> +}
> +
>  /* Output a comdat type unit DIE and its children.  */
>
>  static void
> @@ -8171,7 +8646,11 @@
>    calc_die_sizes (node->root_die);
>
>  #if defined (OBJECT_FORMAT_ELF)
> -  secname = ".debug_types";
> +  if (!dwarf_split_debug_info)
> +    secname = ".debug_types";
> +  else
> +    secname = ".debug_types.dwo";
> +
>    tmp = XALLOCAVEC (char, 4 + DWARF_TYPE_SIGNATURE_SIZE * 2);
>    sprintf (tmp, "wt.");
>    for (i = 0; i < DWARF_TYPE_SIGNATURE_SIZE; i++)
> @@ -8180,6 +8659,7 @@
>    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.");
> @@ -8197,6 +8677,36 @@
>    output_die (node->root_die);
>
>    unmark_dies (node->root_die);
> +
> +  if (dwarf_split_debug_info)
> +    {
> +      /* Produce the skeleton type-unit header.  */
> +      const char *secname = ".debug_types";
> +
> +      targetm.asm_out.named_section (secname,
> +                                     SECTION_DEBUG | SECTION_LINKONCE,
> +                                     comdat_key);
> +      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,
> +                           DWARF_COMPILE_UNIT_HEADER_SIZE
> +                           - DWARF_INITIAL_LENGTH_SIZE
> +                           + size_of_die (get_skeleton_type_unit ())
> +                           + DWARF_TYPE_SIGNATURE_SIZE + DWARF_OFFSET_SIZE,
> +                           "Length of Type Unit Info");
> +      dw2_asm_output_data (2, dwarf_version, "DWARF version number");
> +      dw2_asm_output_offset (DWARF_OFFSET_SIZE,
> +                             debug_skeleton_abbrev_section_label,
> +                             debug_abbrev_section,
> +                             "Offset Into Abbrev. Section");
> +      dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
> +      output_signature (node->signature, "Type Signature");
> +      dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, "Offset to Type DIE");
> +
> +      output_die (get_skeleton_type_unit ());
> +    }
>  }
>
>  /* Return the DWARF2/3 pubname associated with a decl.  */
> @@ -8232,7 +8742,7 @@
>       class_member, it will either be inside the class already, or will have just
>       looked up the class to find the member.  Either way, searching the class is
>       faster than searching the index.  */
> -  if ((TREE_PUBLIC (decl) && !is_class_die (die->die_parent))
> +  if ((TREE_PUBLIC (decl) && !class_scope_p (die->die_parent))
>        || is_cu_die (die->die_parent) || is_namespace_die (die->die_parent))
>      {
>        const char *name = dwarf2_name (decl, 1);
> @@ -8340,9 +8850,14 @@
>                          "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");
> +  if (dwarf_split_debug_info)
> +    dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_skeleton_info_section_label,
> +                           debug_skeleton_info_section,
> +                           "Offset of Compilation Unit Info");
> +  else
> +    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");
>
> @@ -8403,9 +8918,14 @@
>                        "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");
> +  if (dwarf_split_debug_info)
> +    dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_skeleton_info_section_label,
> +                           debug_skeleton_info_section,
> +                           "Offset of Compilation Unit Info");
> +  else
> +    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");
>
> @@ -8502,12 +9022,13 @@
>    return add_ranges_num (block ? BLOCK_NUMBER (block) : 0);
>  }
>
> -/* Add a new entry to .debug_ranges corresponding to a pair of
> -   labels.  */
> +/* Add a new entry to .debug_ranges corresponding to a pair of labels.  The
> +   parameter force_direct makes the attribute use DW_AT_addr, rather than
> +   DW_AT_GNU_addr_index.  */
>
>  static void
>  add_ranges_by_labels (dw_die_ref die, const char *begin, const char *end,
> -                     bool *added)
> +                     bool *added, bool force_direct)
>  {
>    unsigned int in_use = ranges_by_label_in_use;
>    unsigned int offset;
> @@ -8530,7 +9051,7 @@
>    offset = add_ranges_num (-(int)in_use - 1);
>    if (!*added)
>      {
> -      add_AT_range_list (die, DW_AT_ranges, offset);
> +      add_AT_range_list (die, DW_AT_ranges, offset, force_direct);
>        *added = true;
>      }
>  }
> @@ -9067,7 +9588,7 @@
>     information goes into the .debug_line section.  */
>
>  static void
> -output_line_info (void)
> +output_line_info (bool prologue_only)
>  {
>    char l1[20], l2[20], p1[20], p2[20];
>    int ver = dwarf_version;
> @@ -9137,6 +9658,12 @@
>    /* Write out the information about the files we use.  */
>    output_file_names ();
>    ASM_OUTPUT_LABEL (asm_out_file, p2);
> +  if (prologue_only)
> +    {
> +      /* Output the marker for the end of the line number info.  */
> +      ASM_OUTPUT_LABEL (asm_out_file, l2);
> +      return;
> +    }
>
>    if (separate_line_info)
>      {
> @@ -11437,14 +11964,7 @@
>           if (!targetm.have_tls || !targetm.asm_out.output_dwarf_dtprel)
>             break;
>
> -         /* We used to emit DW_OP_addr here, but that's wrong, since
> -            DW_OP_addr should be relocated by the debug info consumer,
> -            while DW_OP_GNU_push_tls_address operand should not.  */
> -         temp = new_loc_descr (DWARF2_ADDR_SIZE == 4
> -                               ? DW_OP_const4u : DW_OP_const8u, 0, 0);
> -         temp->dw_loc_oprnd1.val_class = dw_val_class_addr;
> -         temp->dw_loc_oprnd1.v.val_addr = rtl;
> -         temp->dtprel = true;
> +          temp = new_addr_loc_descr (rtl, dtprel_true);
>
>           mem_loc_result = new_loc_descr (DW_OP_GNU_push_tls_address, 0, 0);
>           add_loc_descr (&mem_loc_result, temp);
> @@ -11456,9 +11976,7 @@
>         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;
> +      mem_loc_result = new_addr_loc_descr (rtl, dtprel_false);
>        VEC_safe_push (rtx, gc, used_rtx_array, rtl);
>        break;
>
> @@ -12360,9 +12878,7 @@
>        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;
> +         loc_result = new_addr_loc_descr (rtl, dtprel_false);
>           add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0));
>           VEC_safe_push (rtx, gc, used_rtx_array, rtl);
>         }
> @@ -13062,9 +13578,8 @@
>        if (DECL_THREAD_LOCAL_P (loc))
>         {
>           rtx rtl;
> -         enum dwarf_location_atom first_op;
> -         enum dwarf_location_atom second_op;
> -         bool dtprel = false;
> +         enum dwarf_location_atom tls_op;
> +         enum dtprel_bool dtprel = dtprel_false;
>
>           if (targetm.have_tls)
>             {
> @@ -13081,9 +13596,8 @@
>                   operand shouldn't be.  */
>               if (DECL_EXTERNAL (loc) && !targetm.binds_local_p (loc))
>                 return 0;
> -             first_op = DWARF2_ADDR_SIZE == 4 ? DW_OP_const4u : DW_OP_const8u;
> -             dtprel = true;
> -             second_op = DW_OP_GNU_push_tls_address;
> +             dtprel = dtprel_true;
> +             tls_op = DW_OP_GNU_push_tls_address;
>             }
>           else
>             {
> @@ -13095,8 +13609,7 @@
>                  no longer appear in gimple code.  We used the control
>                  variable in specific so that we could pick it up here.  */
>               loc = DECL_VALUE_EXPR (loc);
> -             first_op = DW_OP_addr;
> -             second_op = DW_OP_form_tls_address;
> +             tls_op = DW_OP_form_tls_address;
>             }
>
>           rtl = rtl_for_decl_location (loc);
> @@ -13109,12 +13622,8 @@
>           if (! CONSTANT_P (rtl))
>             return 0;
>
> -         ret = new_loc_descr (first_op, 0, 0);
> -         ret->dw_loc_oprnd1.val_class = dw_val_class_addr;
> -         ret->dw_loc_oprnd1.v.val_addr = rtl;
> -         ret->dtprel = dtprel;
> -
> -         ret1 = new_loc_descr (second_op, 0, 0);
> +          ret = new_addr_loc_descr (rtl, dtprel);
> +         ret1 = new_loc_descr (tls_op, 0, 0);
>           add_loc_descr (&ret, ret1);
>
>           have_address = 1;
> @@ -13159,11 +13668,7 @@
>             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;
> -           ret->dw_loc_oprnd1.v.val_addr = rtl;
> -         }
> +          ret = new_addr_loc_descr (rtl, dtprel_false);
>         else
>           {
>             enum machine_mode mode, mem_mode;
> @@ -14091,9 +14596,7 @@
>           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;
> +         loc_result = new_addr_loc_descr (rtl, dtprel_false);
>           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);
> @@ -15611,7 +16114,7 @@
>    if (TREE_CODE (decl) == FUNCTION_DECL && TREE_ASM_WRITTEN (decl))
>      {
>        add_AT_addr (die, DW_AT_VMS_rtnbeg_pd_address,
> -                  XEXP (DECL_RTL (decl), 0));
> +                  XEXP (DECL_RTL (decl), 0), false);
>        VEC_safe_push (rtx, gc, used_rtx_array, XEXP (DECL_RTL (decl), 0));
>      }
>  #endif /* VMS_DEBUGGING_INFO */
> @@ -15632,7 +16135,7 @@
>    add_name_attribute (die, VMS_DEBUG_MAIN_POINTER);
>    ASM_GENERATE_INTERNAL_LABEL (label, PROLOGUE_END_LABEL,
>                                current_function_funcdef_no);
> -  add_AT_lbl_id (die, DW_AT_entry_pc, label);
> +  add_AT_lbl_id (die, DW_AT_entry_pc, label, false);
>
>    /* Make it the first child of comp_unit_die ().  */
>    die->die_parent = comp_unit_die ();
> @@ -16223,7 +16726,7 @@
>    if (DECL_ABSTRACT (decl))
>      equate_decl_number_to_die (decl, decl_die);
>    else
> -    add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl));
> +    add_AT_lbl_id (decl_die, DW_AT_low_pc, decl_start_label (decl), false);
>  }
>  #endif
>
> @@ -16873,7 +17376,7 @@
>    if (stmt_die == NULL)
>      stmt_die = subr_die;
>    die = new_die (DW_TAG_GNU_call_site, stmt_die, NULL_TREE);
> -  add_AT_lbl_id (die, DW_AT_low_pc, ca_loc->label);
> +  add_AT_lbl_id (die, DW_AT_low_pc, ca_loc->label, false);
>    if (ca_loc->tail_call_p)
>      add_AT_flag (die, DW_AT_GNU_tail_call, 1);
>    if (ca_loc->symbol_ref)
> @@ -16882,7 +17385,7 @@
>        if (tdie)
>         add_AT_die_ref (die, DW_AT_abstract_origin, tdie);
>        else
> -       add_AT_addr (die, DW_AT_abstract_origin, ca_loc->symbol_ref);
> +       add_AT_addr (die, DW_AT_abstract_origin, ca_loc->symbol_ref, false);
>      }
>    return die;
>  }
> @@ -17073,7 +17576,8 @@
>           if (fde->dw_fde_begin)
>             {
>               /* We have already generated the labels.  */
> -             add_AT_low_high_pc (subr_die, fde->dw_fde_begin, fde->dw_fde_end);
> +             add_AT_low_high_pc (subr_die, fde->dw_fde_begin,
> +                                 fde->dw_fde_end, false);
>             }
>           else
>             {
> @@ -17084,7 +17588,8 @@
>                                            current_function_funcdef_no);
>               ASM_GENERATE_INTERNAL_LABEL (label_id_high, FUNC_END_LABEL,
>                                            current_function_funcdef_no);
> -             add_AT_low_high_pc (subr_die, label_id_low, label_id_high);
> +             add_AT_low_high_pc (subr_die, label_id_low, label_id_high,
> +                                 false);
>             }
>
>  #if VMS_DEBUGGING_INFO
> @@ -17127,10 +17632,11 @@
>                      alignment offset.  */
>                   bool range_list_added = false;
>                   add_ranges_by_labels (subr_die, fde->dw_fde_begin,
> -                                       fde->dw_fde_end, &range_list_added);
> +                                       fde->dw_fde_end, &range_list_added,
> +                                       false);
>                   add_ranges_by_labels (subr_die, fde->dw_fde_second_begin,
>                                         fde->dw_fde_second_end,
> -                                       &range_list_added);
> +                                       &range_list_added, false);
>                   if (range_list_added)
>                     add_ranges (NULL);
>                 }
> @@ -17149,7 +17655,7 @@
>
>                   /* Do the 'primary' section.   */
>                   add_AT_low_high_pc (subr_die, fde->dw_fde_begin,
> -                                     fde->dw_fde_end);
> +                                     fde->dw_fde_end, false);
>
>                   /* Build a minimal DIE for the secondary section.  */
>                   seg_die = new_die (DW_TAG_subprogram,
> @@ -17174,14 +17680,15 @@
>
>                   name = concat ("__second_sect_of_", name, NULL);
>                   add_AT_low_high_pc (seg_die, fde->dw_fde_second_begin,
> -                                     fde->dw_fde_second_end);
> +                                     fde->dw_fde_second_end, false);
>                   add_name_attribute (seg_die, name);
>                   if (want_pubnames ())
>                     add_pubname_string (name, seg_die);
>                 }
>             }
>           else
> -           add_AT_low_high_pc (subr_die, fde->dw_fde_begin, fde->dw_fde_end);
> +           add_AT_low_high_pc (subr_die, fde->dw_fde_begin, fde->dw_fde_end,
> +                               false);
>         }
>
>        cfa_fb_offset = CFA_FRAME_BASE_OFFSET (decl);
> @@ -17622,7 +18129,7 @@
>             {
>               /* Optimize the common case.  */
>               if (single_element_loc_list_p (loc)
> -                 && loc->expr->dw_loc_opc == DW_OP_addr
> +                  && 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)
>                 {
> @@ -17809,7 +18316,7 @@
>           gcc_assert (!INSN_DELETED_P (insn));
>
>           ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (insn));
> -         add_AT_lbl_id (lbl_die, DW_AT_low_pc, label);
> +         add_AT_lbl_id (lbl_die, DW_AT_low_pc, label, false);
>         }
>        else if (insn
>                && NOTE_P (insn)
> @@ -17817,7 +18324,7 @@
>                && CODE_LABEL_NUMBER (insn) != -1)
>         {
>           ASM_GENERATE_INTERNAL_LABEL (label, "LDL", CODE_LABEL_NUMBER (insn));
> -         add_AT_lbl_id (lbl_die, DW_AT_low_pc, label);
> +         add_AT_lbl_id (lbl_die, DW_AT_low_pc, label, false);
>         }
>      }
>  }
> @@ -17858,7 +18365,7 @@
>         {
>           ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL,
>                                        BLOCK_NUMBER (stmt));
> -         add_AT_lbl_id (die, DW_AT_entry_pc, label);
> +         add_AT_lbl_id (die, DW_AT_entry_pc, label, false);
>         }
>
>        /* Optimize duplicate .debug_ranges lists or even tails of
> @@ -17906,12 +18413,13 @@
>             ++thiscnt;
>           gcc_assert (supercnt >= thiscnt);
>           add_AT_range_list (die, DW_AT_ranges,
> -                            (off + supercnt - thiscnt)
> -                            * 2 * DWARF2_ADDR_SIZE);
> +                            ((off + supercnt - thiscnt)
> +                             * 2 * DWARF2_ADDR_SIZE),
> +                            false);
>           return;
>         }
>
> -      add_AT_range_list (die, DW_AT_ranges, add_ranges (stmt));
> +      add_AT_range_list (die, DW_AT_ranges, add_ranges (stmt), false);
>
>        chain = BLOCK_FRAGMENT_CHAIN (stmt);
>        do
> @@ -17929,7 +18437,7 @@
>                                    BLOCK_NUMBER (stmt));
>        ASM_GENERATE_INTERNAL_LABEL (label_high, BLOCK_END_LABEL,
>                                    BLOCK_NUMBER (stmt));
> -      add_AT_low_high_pc (die, label, label_high);
> +      add_AT_low_high_pc (die, label, label_high, false);
>      }
>  }
>
> @@ -20518,13 +21026,12 @@
>      case DW_MACRO_GNU_define_indirect:
>      case DW_MACRO_GNU_undef_indirect:
>        node = find_AT_string (ref->info);
> -      if (node->form != DW_FORM_strp)
> +      if ((node->form != DW_FORM_string)
> +          && (node->form != DW_FORM_GNU_str_index))
>         {
>           char label[32];
>           ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
> -         ++dw2_string_counter;
> -         node->label = xstrdup (label);
> -         node->form = DW_FORM_strp;
> +          add_indirect_string (node, label);
>         }
>        dw2_asm_output_data (1, ref->code,
>                            ref->code == DW_MACRO_GNU_define_indirect
> @@ -20705,8 +21212,10 @@
>         dw2_asm_output_data (1, 3, "Flags: 64-bit, lineptr present");
>        else
>         dw2_asm_output_data (1, 2, "Flags: 32-bit, lineptr present");
> -      dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_line_section_label,
> -                            debug_line_section, NULL);
> +      dw2_asm_output_offset (DWARF_OFFSET_SIZE,
> +                             (!dwarf_split_debug_info ? debug_line_section_label
> +                              : debug_skeleton_line_section_label),
> +                             debug_line_section, NULL);
>      }
>
>    /* In the first loop, it emits the primary .debug_macinfo section
> @@ -20845,26 +21354,60 @@
>
>    used_rtx_array = VEC_alloc (rtx, gc, 32);
>
> -  debug_info_section = get_section (DEBUG_INFO_SECTION,
> -                                   SECTION_DEBUG, NULL);
> -  debug_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
> -                                     SECTION_DEBUG, NULL);
> +  if (!dwarf_split_debug_info)
> +    {
> +      debug_info_section = get_section (DEBUG_INFO_SECTION,
> +                                        SECTION_DEBUG, NULL);
> +      debug_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
> +                                          SECTION_DEBUG, NULL);
> +      debug_loc_section = get_section (DEBUG_LOC_SECTION,
> +                                       SECTION_DEBUG, NULL);
> +    }
> +  else
> +    {
> +      debug_info_section = get_section (DEBUG_DWO_INFO_SECTION,
> +                                        SECTION_DEBUG | SECTION_EXCLUDE, NULL);
> +      debug_abbrev_section = get_section (DEBUG_DWO_ABBREV_SECTION,
> +                                          SECTION_DEBUG | SECTION_EXCLUDE,
> +                                          NULL);
> +      debug_addr_section = get_section (DEBUG_ADDR_SECTION,
> +                                        SECTION_DEBUG, NULL);
> +      debug_skeleton_info_section = get_section (DEBUG_INFO_SECTION,
> +                                                 SECTION_DEBUG, NULL);
> +      debug_skeleton_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
> +                                                   SECTION_DEBUG, NULL);
> +      ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_abbrev_section_label,
> +                                  DEBUG_SKELETON_ABBREV_SECTION_LABEL, 0);
> +
> +      /* Somewhat confusing detail: The skeleton_[abbrev|info] sections stay in
> +         the main .o, but the skeleton_line goes into the split off dwo.  */
> +      debug_skeleton_line_section
> +          = get_section (DEBUG_DWO_LINE_SECTION,
> +                         SECTION_DEBUG | SECTION_EXCLUDE, NULL);
> +      ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_line_section_label,
> +                                   DEBUG_SKELETON_LINE_SECTION_LABEL, 0);
> +      debug_str_offsets_section = get_section (DEBUG_STR_OFFSETS_SECTION,
> +                                               SECTION_DEBUG | SECTION_EXCLUDE,
> +                                               NULL);
> +      ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_info_section_label,
> +                                   DEBUG_SKELETON_INFO_SECTION_LABEL, 0);
> +      debug_loc_section = get_section (DEBUG_DWO_LOC_SECTION,
> +                                       SECTION_DEBUG | SECTION_EXCLUDE, NULL);
> +    }
>    debug_aranges_section = get_section (DEBUG_ARANGES_SECTION,
>                                        SECTION_DEBUG, NULL);
>    debug_macinfo_section = get_section (dwarf_strict
>                                        ? DEBUG_MACINFO_SECTION
>                                        : DEBUG_MACRO_SECTION,
> -                                      SECTION_DEBUG, NULL);
> +                                      DEBUG_MACRO_SECTION_FLAGS, NULL);
>    debug_line_section = get_section (DEBUG_LINE_SECTION,
>                                     SECTION_DEBUG, NULL);
> -  debug_loc_section = get_section (DEBUG_LOC_SECTION,
> -                                  SECTION_DEBUG, NULL);
>    debug_pubnames_section = get_section (DEBUG_PUBNAMES_SECTION,
>                                         SECTION_DEBUG, NULL);
>    debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION,
>                                         SECTION_DEBUG, NULL);
>    debug_str_section = get_section (DEBUG_STR_SECTION,
> -                                  DEBUG_STR_SECTION_FLAGS, NULL);
> +                                   DEBUG_STR_SECTION_FLAGS, NULL);
>    debug_ranges_section = get_section (DEBUG_RANGES_SECTION,
>                                       SECTION_DEBUG, NULL);
>    debug_frame_section = get_section (DEBUG_FRAME_SECTION,
> @@ -20884,10 +21427,13 @@
>                                DEBUG_LINE_SECTION_LABEL, 0);
>    ASM_GENERATE_INTERNAL_LABEL (ranges_section_label,
>                                DEBUG_RANGES_SECTION_LABEL, 0);
> +  ASM_GENERATE_INTERNAL_LABEL (debug_addr_section_label,
> +                              DEBUG_ADDR_SECTION_LABEL, 0);
>    ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label,
>                                dwarf_strict
>                                ? DEBUG_MACINFO_SECTION_LABEL
>                                : DEBUG_MACRO_SECTION_LABEL, 0);
> +  ASM_GENERATE_INTERNAL_LABEL (loc_section_label, DEBUG_LOC_SECTION_LABEL, 0);
>
>    if (debug_info_level >= DINFO_LEVEL_VERBOSE)
>      macinfo_table = VEC_alloc (macinfo_entry, gc, 64);
> @@ -20931,6 +21477,83 @@
>    return 1;
>  }
>
> +/* Output the indexed string table.  */
> +
> +static void
> +output_index_strings (void)
> +{
> +  unsigned int i;
> +  unsigned int len = 0;
> +  struct indirect_string_node *node;
> +
> +  gcc_assert (dwarf_split_debug_info);
> +
> +  if (VEC_empty (indirect_string_node, index_string_table))
> +    return;
> +
> +  switch_to_section (debug_str_offsets_section);
> +  for (i = 0; VEC_iterate (indirect_string_node, index_string_table, i, node);
> +       i++)
> +    {
> +      gcc_assert (node->form == DW_FORM_GNU_str_index);
> +      dw2_asm_output_data (DWARF_OFFSET_SIZE, len,
> +                           "indexed string 0x%x: %s", i, node->str);
> +      len += strlen (node->str) + 1;
> +    }
> +  switch_to_section (debug_str_section);
> +  for (i = 0; VEC_iterate (indirect_string_node, index_string_table, i, node);
> +       i++)
> +    {
> +      ASM_OUTPUT_LABEL (asm_out_file, node->label);
> +      assemble_string (node->str, strlen (node->str) + 1);
> +    }
> +}
> +
> +/* Write the index table.  */
> +
> +static void
> +output_addr_table (void)
> +{
> +  unsigned int i;
> +  dw_attr_node *node;
> +
> +  if (VEC_empty (dw_attr_node, addr_index_table))
> +    return;
> +
> +  switch_to_section (debug_addr_section);
> +  for (i = 0; VEC_iterate (dw_attr_node, addr_index_table, i, node); i++)
> +    {
> +      const char *name;
> +
> +      if (node->dw_attr == 0)
> +       {
> +         dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, "<Removed entry>");
> +         continue;
> +       }
> +
> +      name = dwarf_attr_name (node->dw_attr);
> +      switch (AT_class (node))
> +        {
> +          case dw_val_class_addr:
> +            dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, AT_addr (node),
> +                                     "%s", name);
> +            break;
> +          case dw_val_class_loc:
> +            gcc_assert (targetm.asm_out.output_dwarf_dtprel);
> +            targetm.asm_out.output_dwarf_dtprel (asm_out_file,
> +                                                 DWARF2_ADDR_SIZE,
> +                                                 node->dw_attr_val.v.val_addr);
> +            fputc ('\n', asm_out_file);
> +            break;
> +          case dw_val_class_lbl_id:
> +            dw2_asm_output_addr (DWARF2_ADDR_SIZE, AT_lbl (node), "%s", name);
> +            break;
> +          default:
> +            gcc_unreachable ();
> +        }
> +    }
> +}
> +
>  #if ENABLE_ASSERT_CHECKING
>  /* Verify that all marks are clear.  */
>
> @@ -21537,6 +22160,17 @@
>         if (resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL))
>           return false;
>         break;
> +      case DW_OP_GNU_addr_index:
> +      case DW_OP_GNU_const_index:
> +        {
> +          unsigned int idx = loc->dw_loc_oprnd1.val_index;
> +          dw_attr_node *node = &VEC_index (dw_attr_node, addr_index_table, idx);
> +          if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
> +               || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
> +              && resolve_one_addr (&node->dw_attr_val.v.val_addr, NULL))
> +            return false;
> +        }
> +       break;
>        case DW_OP_const4u:
>        case DW_OP_const8u:
>         if (loc->dtprel
> @@ -21671,11 +22305,15 @@
>                 if (!resolve_addr_in_expr ((*curr)->expr))
>                   {
>                     dw_loc_list_ref next = (*curr)->dw_loc_next;
> +                   dw_loc_descr_ref l = (*curr)->expr;
> +
>                     if (next && (*curr)->ll_symbol)
>                       {
>                         gcc_assert (!next->ll_symbol);
>                         next->ll_symbol = (*curr)->ll_symbol;
>                       }
> +                   if (dwarf_split_debug_info)
> +                     remove_loc_list_addr_table_entries (l);
>                     *curr = next;
>                   }
>                 else
> @@ -21689,6 +22327,8 @@
>             else
>               {
>                 loc->replaced = 1;
> +                if (dwarf_split_debug_info)
> +                  remove_loc_list_addr_table_entries (loc->expr);
>                 loc->dw_loc_next = *start;
>               }
>           }
> @@ -21713,6 +22353,8 @@
>                || l->dw_loc_next != NULL)
>               && !resolve_addr_in_expr (l))
>             {
> +             if (dwarf_split_debug_info)
> +               remove_loc_list_addr_table_entries (l);
>               remove_AT (die, a->dw_attr);
>               ix--;
>             }
> @@ -21724,6 +22366,8 @@
>         if (a->dw_attr == DW_AT_const_value
>             && resolve_one_addr (&a->dw_attr_val.v.val_addr, NULL))
>           {
> +           if (AT_index (a) != -1U)
> +             remove_addr_table_entry (AT_index (a));
>             remove_AT (die, a->dw_attr);
>             ix--;
>           }
> @@ -21747,6 +22391,8 @@
>               }
>             else
>               {
> +               if (AT_index (a) != -1U)
> +                 remove_addr_table_entry (AT_index (a));
>                 remove_AT (die, a->dw_attr);
>                 ix--;
>               }
> @@ -21880,6 +22526,19 @@
>         }
>        hash = iterative_hash_rtx (val1->v.val_addr, hash);
>        break;
> +    case DW_OP_GNU_addr_index:
> +    case DW_OP_GNU_const_index:
> +      {
> +        dw_attr_ref attr = &VEC_index (dw_attr_node, addr_index_table,
> +                                      val1->val_index);
> +        if (loc->dtprel)
> +         {
> +            unsigned char dtprel = 0xd1;
> +            hash = iterative_hash_object (dtprel, hash);
> +          }
> +        hash = iterative_hash_rtx (attr->dw_attr_val.v.val_addr, hash);
> +      }
> +      break;
>      case DW_OP_GNU_implicit_pointer:
>        hash = iterative_hash_object (val2->v.val_int, hash);
>        break;
> @@ -22061,9 +22720,12 @@
>        return valx1->v.val_int == valy1->v.val_int;
>      case DW_OP_skip:
>      case DW_OP_bra:
> +      /* If splitting debug info, the use of DW_OP_GNU_addr_index
> +        can cause irrelevant differences in dw_loc_addr.  */
>        gcc_assert (valx1->val_class == dw_val_class_loc
>                   && valy1->val_class == dw_val_class_loc
> -                 && x->dw_loc_addr == y->dw_loc_addr);
> +                 && (dwarf_split_debug_info
> +                     || x->dw_loc_addr == y->dw_loc_addr));
>        return valx1->v.val_loc->dw_loc_addr == valy1->v.val_loc->dw_loc_addr;
>      case DW_OP_implicit_value:
>        if (valx1->v.val_unsigned != valy1->v.val_unsigned
> @@ -22094,6 +22756,18 @@
>      case DW_OP_addr:
>      hash_addr:
>        return rtx_equal_p (valx1->v.val_addr, valy1->v.val_addr);
> +    case DW_OP_GNU_addr_index:
> +    case DW_OP_GNU_const_index:
> +      {
> +        dw_attr_node *attrx1 = &VEC_index (dw_attr_node,
> +                                           addr_index_table,
> +                                           valx1->val_index);
> +        dw_attr_node *attry1 = &VEC_index (dw_attr_node,
> +                                           addr_index_table,
> +                                           valy1->val_index);
> +        return rtx_equal_p (attrx1->dw_attr_val.v.val_addr,
> +                            attry1->dw_attr_val.v.val_addr);
> +      }
>      case DW_OP_GNU_implicit_pointer:
>        return valx1->val_class == dw_val_class_die_ref
>              && valx1->val_class == valy1->val_class
> @@ -22207,7 +22881,7 @@
>         if (*slot == NULL)
>           *slot = (void *) list;
>         else
> -         a->dw_attr_val.v.val_loc_list = (dw_loc_list_ref) *slot;
> +          a->dw_attr_val.v.val_loc_list = (dw_loc_list_ref) *slot;
>        }
>
>    FOR_EACH_CHILD (die, c, optimize_location_lists_1 (c, htab));
> @@ -22223,6 +22897,43 @@
>    optimize_location_lists_1 (die, htab);
>    htab_delete (htab);
>  }
> +
> +
> +/* Recursively assign each location list a unique index into the debug_addr
> +   section.  */
> +
> +static void
> +index_location_lists (dw_die_ref die)
> +{
> +  dw_die_ref c;
> +  dw_attr_ref a;
> +  unsigned ix;
> +
> +  FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a)
> +    if (AT_class (a) == dw_val_class_loc_list)
> +      {
> +        dw_loc_list_ref list = AT_loc_list (a);
> +        dw_loc_list_ref curr;
> +        for (curr = list; curr != NULL; curr = curr->dw_loc_next)
> +          {
> +            dw_attr_node attr;
> +
> +            /* Don't index an entry that has already been indexed
> +              or won't be output.  */
> +            if (curr->begin_index != -1U
> +               || (strcmp (curr->begin, curr->end) == 0 && !curr->force))
> +             continue;
> +
> +            attr.dw_attr = DW_AT_location;
> +            attr.dw_attr_val.val_class = dw_val_class_lbl_id;
> +            attr.dw_attr_val.val_index = -1U;
> +            attr.dw_attr_val.v.val_lbl_id = xstrdup (curr->begin);
> +            curr->begin_index = add_addr_table_entry (&attr);
> +          }
> +      }
> +
> +  FOR_EACH_CHILD (die, c, index_location_lists (c));
> +}
>
>  /* Output stuff that dwarf requires at the end of every file,
>     and generate the DWARF-2 debugging info.  */
> @@ -22234,6 +22945,7 @@
>    comdat_type_node *ctnode;
>    htab_t comdat_type_table;
>    unsigned int i;
> +  dw_die_ref main_comp_unit_die;
>
>    /* PCH might result in DW_AT_producer string being restored from the
>       header compilation, fix it up if needed.  */
> @@ -22386,6 +23098,14 @@
>    for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
>      add_sibling_attributes (ctnode->root_die);
>
> +  /* When splitting DWARF info, we put some attributes in the
> +     skeleton compile_unit DIE that remains in the .o, while
> +     most attributes go in the DWO compile_unit_die.  */
> +  if (dwarf_split_debug_info)
> +    main_comp_unit_die = gen_compile_unit_die (NULL);
> +  else
> +    main_comp_unit_die = comp_unit_die ();
> +
>    /* Output a terminator label for the .text section.  */
>    switch_to_section (text_section);
>    targetm.asm_out.internal_label (asm_out_file, TEXT_END_LABEL, 0);
> @@ -22402,8 +23122,8 @@
>      {
>        /* Don't add if the CU has no associated code.  */
>        if (text_section_used)
> -       add_AT_low_high_pc (comp_unit_die (), text_section_label,
> -                           text_end_label);
> +       add_AT_low_high_pc (main_comp_unit_die, text_section_label,
> +                           text_end_label, true);
>      }
>    else
>      {
> @@ -22412,22 +23132,24 @@
>        bool range_list_added = false;
>
>        if (text_section_used)
> -       add_ranges_by_labels (comp_unit_die (), text_section_label,
> -                             text_end_label, &range_list_added);
> +       add_ranges_by_labels (main_comp_unit_die, text_section_label,
> +                             text_end_label, &range_list_added, true);
>        if (cold_text_section_used)
> -       add_ranges_by_labels (comp_unit_die (), cold_text_section_label,
> -                             cold_end_label, &range_list_added);
> +       add_ranges_by_labels (main_comp_unit_die, cold_text_section_label,
> +                             cold_end_label, &range_list_added, true);
>
>        FOR_EACH_VEC_ELT (dw_fde_ref, fde_vec, fde_idx, fde)
>         {
>           if (DECL_IGNORED_P (fde->decl))
>             continue;
>           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_by_labels (main_comp_unit_die, fde->dw_fde_begin,
> +                                 fde->dw_fde_end, &range_list_added,
> +                                 true);
>           if (fde->dw_fde_second_begin && !fde->second_in_std_section)
> -           add_ranges_by_labels (comp_unit_die (), fde->dw_fde_second_begin,
> -                                 fde->dw_fde_second_end, &range_list_added);
> +           add_ranges_by_labels (main_comp_unit_die, fde->dw_fde_second_begin,
> +                                 fde->dw_fde_second_end, &range_list_added,
> +                                 true);
>         }
>
>        if (range_list_added)
> @@ -22437,16 +23159,16 @@
>              absolute.  Historically, we've emitted the unexpected
>              DW_AT_entry_pc instead of DW_AT_low_pc for this purpose.
>              Emit both to give time for other tools to adapt.  */
> -         add_AT_addr (comp_unit_die (), DW_AT_low_pc, const0_rtx);
> +         add_AT_addr (main_comp_unit_die, DW_AT_low_pc, const0_rtx, true);
>           if (! dwarf_strict && dwarf_version < 4)
> -           add_AT_addr (comp_unit_die (), DW_AT_entry_pc, const0_rtx);
> +           add_AT_addr (main_comp_unit_die, DW_AT_entry_pc, const0_rtx, true);
>
>           add_ranges (NULL);
>         }
>      }
>
>    if (debug_info_level >= DINFO_LEVEL_NORMAL)
> -    add_AT_lineptr (comp_unit_die (), DW_AT_stmt_list,
> +    add_AT_lineptr (main_comp_unit_die, DW_AT_stmt_list,
>                     debug_line_section_label);
>
>    if (have_macinfo)
> @@ -22455,7 +23177,11 @@
>                    macinfo_section_label);
>
>    if (have_location_lists)
> -    optimize_location_lists (comp_unit_die ());
> +    {
> +      optimize_location_lists (comp_unit_die ());
> +      if (dwarf_split_debug_info)
> +        index_location_lists (comp_unit_die ());
> +    }
>
>    /* Output all of the compilation units.  We put the main one last so that
>       the offsets are available to output_pubnames.  */
> @@ -22476,19 +23202,54 @@
>           attributes.  */
>        if (debug_info_level >= DINFO_LEVEL_NORMAL)
>          add_AT_lineptr (ctnode->root_die, DW_AT_stmt_list,
> -                       debug_line_section_label);
> +                        (!dwarf_split_debug_info
> +                         ? debug_line_section_label
> +                         : debug_skeleton_line_section_label));
>
>        output_comdat_type_unit (ctnode);
>        *slot = ctnode;
>      }
>    htab_delete (comdat_type_table);
>
> -  add_AT_pubnames (comp_unit_die ());
> +  /* The AT_pubnames attribute needs to go in all skeleton dies, including
> +     both the main_cu and all skeleton TUs.  Making this call unconditional
> +     would end up either adding a second copy of the AT_pubnames attribute, or
> +     requiring a special case in add_top_level_skeleton_die_attrs.  */
> +  if (!dwarf_split_debug_info)
> +    add_AT_pubnames (comp_unit_die ());
>
> +  if (dwarf_split_debug_info)
> +    {
> +      int mark;
> +      unsigned char checksum[16];
> +      struct md5_ctx ctx;
> +
> +      /* Compute a checksum of the comp_unit to use as the dwo_id.  */
> +      md5_init_ctx (&ctx);
> +      mark = 0;
> +      die_checksum (comp_unit_die (), &ctx, &mark);
> +      unmark_all_dies (comp_unit_die ());
> +      md5_finish_ctx (&ctx, checksum);
> +
> +      /* Use the first 8 bytes of the checksum as the dwo_id,
> +        and add it to both comp-unit DIEs.  */
> +      add_AT_data8 (main_comp_unit_die, DW_AT_GNU_dwo_id, checksum);
> +      add_AT_data8 (comp_unit_die (), DW_AT_GNU_dwo_id, checksum);
> +
> +      /* Add the base offset of the ranges table to the skeleton
> +        comp-unit DIE.  */
> +      if (ranges_table_in_use)
> +       add_AT_lineptr (main_comp_unit_die, DW_AT_GNU_ranges_base,
> +                       ranges_section_label);
> +    }
> +
>    /* Output the main compilation unit if non-empty or if .debug_macinfo
>       or .debug_macro will be emitted.  */
>    output_comp_unit (comp_unit_die (), have_macinfo);
>
> +  if (dwarf_split_debug_info && info_section_emitted)
> +    output_skeleton_debug_sections (main_comp_unit_die);
> +
>    /* Output the abbreviation table.  */
>    if (abbrev_die_table_in_use != 1)
>      {
> @@ -22502,8 +23263,6 @@
>      {
>        /* Output the location lists info.  */
>        switch_to_section (debug_loc_section);
> -      ASM_GENERATE_INTERNAL_LABEL (loc_section_label,
> -                                  DEBUG_LOC_SECTION_LABEL, 0);
>        ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
>        output_location_lists (comp_unit_die ());
>      }
> @@ -22554,10 +23313,22 @@
>    switch_to_section (debug_line_section);
>    ASM_OUTPUT_LABEL (asm_out_file, debug_line_section_label);
>    if (! DWARF2_ASM_LINE_DEBUG_INFO)
> -    output_line_info ();
> +    output_line_info (false);
>
> -  /* If we emitted any DW_FORM_strp form attribute, output the string
> -     table too.  */
> +  if (dwarf_split_debug_info && info_section_emitted)
> +    {
> +      switch_to_section (debug_skeleton_line_section);
> +      ASM_OUTPUT_LABEL (asm_out_file, debug_skeleton_line_section_label);
> +      output_line_info (true);
> +
> +      output_index_strings ();
> +
> +      switch_to_section (debug_addr_section);
> +      ASM_OUTPUT_LABEL (asm_out_file, debug_addr_section_label);
> +      output_addr_table ();
> +    }
> +
> +  /* If we emitted any indirect strings, output the string table too.  */
>    if (debug_str_hash)
>      htab_traverse (debug_str_hash, output_indirect_string, NULL);
>  }
> Index: gcc/dwarf2out.h
> ===================================================================
> --- gcc/dwarf2out.h     (revision 190603)
> +++ gcc/dwarf2out.h     (working copy)
> @@ -172,6 +172,7 @@
>
>  typedef struct GTY(()) dw_val_struct {
>    enum dw_val_class val_class;
> +  unsigned int val_index;
>    union dw_val_struct_union
>      {
>        rtx GTY ((tag ("dw_val_class_addr"))) val_addr;
> Index: gcc/opts.c
> ===================================================================
> --- gcc/opts.c  (revision 190603)
> +++ gcc/opts.c  (working copy)
> @@ -829,9 +829,14 @@
>    if (opts->x_warn_unused_but_set_parameter == -1)
>      opts->x_warn_unused_but_set_parameter = (opts->x_warn_unused
>                                              && opts->x_extra_warnings);
> +
>    /* Wunused-local-typedefs is enabled by -Wunused or -Wall.  */
>    if (opts->x_warn_unused_local_typedefs == -1)
>      opts->x_warn_unused_local_typedefs = opts->x_warn_unused;
> +
> +  /* The -gsplit-dwarf option requires -gpubnames.  */
> +  if (opts->x_dwarf_split_debug_info)
> +    opts->x_debug_generate_pub_sections = 1;
>  }
>
>  #define LEFT_COLUMN    27
> @@ -1692,6 +1697,13 @@
>        set_debug_level (DWARF2_DEBUG, false, "", opts, opts_set, loc);
>        break;
>
> +    case OPT_gsplit_dwarf:
> +      if (opts->x_dwarf_version < 4)
> +        opts->x_dwarf_version = 4;
> +      set_debug_level (DWARF2_DEBUG, DEFAULT_GDB_EXTENSIONS, "", opts, opts_set,
> +                      loc);
> +      break;
> +
>      case OPT_ggdb:
>        set_debug_level (NO_DEBUG, 2, arg, opts, opts_set, loc);
>        break;
> Index: gcc/common.opt
> ===================================================================
> --- gcc/common.opt      (revision 190603)
> +++ gcc/common.opt      (working copy)
> @@ -2282,6 +2282,20 @@
>  Common RejectNegative Var(dwarf_record_gcc_switches,1)
>  Record gcc command line switches in DWARF DW_AT_producer.
>
> +gno-split-dwarf
> +Common Driver RejectNegative Var(dwarf_split_debug_info,0) Init(0)
> +Don't generate debug information in separate .dwo files
> +
> +gsplit-dwarf
> +Common Driver RejectNegative Var(dwarf_split_debug_info,1)
> +Generate debug information in separate .dwo files
> +
>  gstabs
>  Common JoinedOrMissing Negative(gstabs+)
>  Generate debug information in STABS format
>
> --
> This patch is available for review at http://codereview.appspot.com/6305113

Hey Jason, Any thoughts on this patch?

Sterling



More information about the Gcc-patches mailing list