]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
common.opt (gno-split-dwarf, [...]): New switches.
authorSterling Augustine <saugustine@google.com>
Tue, 6 Nov 2012 23:15:25 +0000 (23:15 +0000)
committerSterling Augustine <sterling@gcc.gnu.org>
Tue, 6 Nov 2012 23:15:25 +0000 (23:15 +0000)
gcc/ChangeLog

2012-11-06  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.
* 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.
(common_handle_option): Add case for OPT_gsplit_dwarf.
* dwarf2out.h (addr_table_entry_struct): Add forward declaration.
(dw_val_struct): Add val_entry pointer.
* dwarf2out.c: (debug_skeleton_info_section,
debug_skeleton_abbrev_section, debug_addr_section,
debug_skeleton_line_section, debug_str_offsets_section): New globals.
(NOT_INDEXED, NO_INDEX_ASSIGNED): New defines.
(indirect_string_node): New field index.
(ate_kind): New enum with fields ate_kind_rtc, ate_kind_rtx_dtprel,
ate_kind_label.
(addr_table_entry): New structure and type.
(dw_loc_list_struct): Add field begin_entry.
(new_loc_desc): Initialize val_entry.
(size_of_loc_descr, output_loc_operands, output_loc_operands_raw):
Add cases for DW_OP_GNU_addr_index and DW_OP_const_index.
(build_cfa_loc): Initialize val_entry.
(AT_index, add_addr_table_entry, remove_addr_table_entry,
add_AT_lbl_id): New functions.
(add_AT_addr, add_AT_range_list): New parameter force_direct.
(output_die_abbrevs): New function.
(add_ranges_by_labels): New parameter force_direct.
(output_line_info): New parameter prologue_only.
(dtprel_bool): New enum with dtprel_false and dtprel_true.
(dw_addr_op, new_addr_loc_descr): New functions.
(DEBUG_DWO_INFO_SECTION, DEBUG_DWO_ABBREV_SECTION,
DEBUG_ADDR_SECTION, DEBUG_NORM_MACINFO_SECTION,
DEBUG_DWO_MACINFO_SECTION, DEBUG_MACINFO_SECTION,
DEBUG_NORM_MACRO_SECTION, DEBUG_DWO_MACRO_SECTION,
DEBUG_MACRO_SECTION, DEBUG_DWO_LINE_SECTION,
DEBUG_DWO_LOC_SECTION, DEBUG_NORM_STR_OFFSETS_SECTION,
DEBUG_DWO_STR_OFFSETS_SECTION, DEBUG_STR_OFFSETS_SECTION,
DEBUG_DWO_STR_SECTION, DEBUG_NORM_STR_SECTION, DEBUG_STR_SECTION,
DEBUG_MACRO_SECTION_FLAGS, DEBUG_SKELETON_LINE_SECTION_LABEL,
DEBUG_SKELETON_INFO_SECTION_LABEL, DEBUG_ADDR_SECTION_LABEL
DEBUG_SKELETON_ABBREV_SECTION_LABEL): New macros.
(DEBUG_STR_SECTION_FLAGS): Adjust.
(TEXT_SECTION_LABEL, COLD_TEXT_SECTION_LABEL,
DEBUG_LINE_SECTION_LABEL, DEBUG_INFO_SECTION_LABEL,
DEBUG_ABBREV_SECTION_LABEL, DEBUG_ADDR_SECTION_LABEL,
DEBUG_LOC_SECTION_LABEL, DEBUG_RANGES_SECTION_LABEL,
DEBUG_MACINFO_SECTION_LABEL, DEBUG_MACRO_SECTION_LABEL): Adjust
indentation.
(debug_skeleton_abbrev_section_label, debug_addr_section_label,
debug_skeleton_line_section_label, debug_skeleton_info_section_label):
New global variables.
(add_AT_flag, add_AT_int, add_AT_unsigned, add_AT_double, add_AT_vec,
add_AT_data8): Initialize val_entry.
(add_AT_low_high_pc): New parameter force_direct. Handle
dwarf_split_debug_info.
(set_indirect_string, find_AT_string_form): New functions.
(AT_string_form): Adjust to call find_AT_string_from.
(add_AT_die_ref, add_AT_fde_ref, add_AT_loc, add_AT_list):
Initialize val_entry.
(addr_index_table): New global variable.
(addr_table_entry_do_hash, addr_table_entry_eq, add_addr_table_entry,
init_addr_table_entry, remove_addr_table_entry, index_addr_table_entry,
remove_loc_list_addr_table_entries): New functions.
(add_AT_addr, add_AT_lbl_id, add_AT_range_list): New parameter
force_direct.  Handle dwarf_split_debug_info.
(add_AT_file, add_AT_vms_delta, add_AT_lineptr, add_AT_macptr,
add_AT_offset): Initialize val_entry.
(UNRELOCATED_OFFSET, RELOCATED_OFFSET): New defines.
(size_of_die): Handle dwarf_split_debug_info.
(size_of_aranges, value_format): Call AT_class.  Check AT_index.
(output_die_abbrevs): New function.
(output_abbrev_section): Call output_die_abbrevs.
(new_loc_list): Initialize begin_entry.
(output_loc_list): Handle dwarf_split_debug_info.
(output_range_list_offset, output_loc_list_offset,
output_attr_index_or_value, ): New functions.
(output_die): Fix call to dw2_asm_output_data.  Call
output_attr_index_or_value and output_range_list_offset.
Adjust logic around dw_val_class_str.
(add_top_lebel_skeleton_die_attrs, get_skeleton_type_unit,
output_skeleton_debug_sections): New functions.
(output_comdat_type_unit, output_pubname, output_aranges): Handle
dwarf_split_debug_info.
(add_ranges_by_labels): New parameter force_direct.
(mem_loc_descriptor, loc_descr): Call new_addr_loc_descr.
(loc_list_from_tree, add_const_value_attribtue): Use dtprel_bools in
place of generic integer.
(dwarf2out_vms_debug_main_pointer, gen_entry_point_die, gen_label_die,
gen_call_site_die, gen_subprogram_die, gen_variable_die,
add_high_low_attributes): Adjust calls to add_AT_lbl_id.
(output_macinfo_op): Adjust indirect_string_logic.
(save_macinfo_strings): New function.
(output_macinfo): Adjust.
(dwarf2out_init): Handle dwarf_split_debug_info.
(index_string, output_index_string_offset, output_index_string): New
functions.
(output_indirect_string): Adjust.
(output_indirect_strings, output_addr_table_entry, output_addr_table):
New functions.
(resolve_addr_in_expr, hash_loc_operands): Handle DW_OP_GNU_addr_index
and DW_OP_GNU_const_index.  Handle dwarf_split_debug_info.  Call
remove_loc_list_addr_table_entries and remove_addr_table_entry.
(index_location_lists): New function.
(dwarf2out_finish): Handle dwarf_split_debug_info.  New variable
main_comp_unit_die.  Adjust calls to add_AT_low_high_pc,
add_ranges_by_labels, add_AT_addr, and add_AT_lineptr. Call
save_macinfo_strings and output_indirect_strings.

include/ChangeLog

2012-10-29  Sterling Augustine <saugustine@google.com>
            Cary Coutant <ccoutant@google.com>

* dwarf2.h (dwarf_location_list_entry_type): New enum with fields
DW_LLE_GNU_end_of_list_entry, DW_LLE_GNU_base_address_selection_entry,
DW_LLE_GNU_start_end_entry and DW_LLE_GNU_start_length_entry.

Co-Authored-By: Cary Coutant <ccoutant@google.com>
From-SVN: r193267

gcc/ChangeLog
gcc/common.opt
gcc/doc/invoke.texi
gcc/dwarf2out.c
gcc/dwarf2out.h
gcc/gcc.c
gcc/opts.c
include/ChangeLog
include/dwarf2.h

index 3c38d3f6ae887bfaf4751dcc0c4ef2b6f39fc271..b4dd302fe6028b7bafd000b1db3de894d81838b2 100644 (file)
@@ -1,3 +1,116 @@
+2012-11-06  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.
+       * 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.
+       (common_handle_option): Add case for OPT_gsplit_dwarf.
+       * dwarf2out.h (addr_table_entry_struct): Add forward declaration.
+       (dw_val_struct): Add val_entry pointer.
+       * dwarf2out.c: (debug_skeleton_info_section,
+       debug_skeleton_abbrev_section, debug_addr_section,
+       debug_skeleton_line_section, debug_str_offsets_section): New globals.
+       (NOT_INDEXED, NO_INDEX_ASSIGNED): New defines.
+       (indirect_string_node): New field index.
+       (ate_kind): New enum with fields ate_kind_rtc, ate_kind_rtx_dtprel,
+       ate_kind_label.
+       (addr_table_entry): New structure and type.
+       (dw_loc_list_struct): Add field begin_entry.
+       (new_loc_desc): Initialize val_entry.
+       (size_of_loc_descr, output_loc_operands, output_loc_operands_raw):
+       Add cases for DW_OP_GNU_addr_index and DW_OP_const_index.
+       (build_cfa_loc): Initialize val_entry.
+       (AT_index, add_addr_table_entry, remove_addr_table_entry,
+       add_AT_lbl_id): New functions.
+       (add_AT_addr, add_AT_range_list): New parameter force_direct.
+       (output_die_abbrevs): New function.
+       (add_ranges_by_labels): New parameter force_direct.
+       (output_line_info): New parameter prologue_only.
+       (dtprel_bool): New enum with dtprel_false and dtprel_true.
+       (dw_addr_op, new_addr_loc_descr): New functions.
+       (DEBUG_DWO_INFO_SECTION, DEBUG_DWO_ABBREV_SECTION,
+       DEBUG_ADDR_SECTION, DEBUG_NORM_MACINFO_SECTION,
+       DEBUG_DWO_MACINFO_SECTION, DEBUG_MACINFO_SECTION,
+       DEBUG_NORM_MACRO_SECTION, DEBUG_DWO_MACRO_SECTION,
+       DEBUG_MACRO_SECTION, DEBUG_DWO_LINE_SECTION,
+       DEBUG_DWO_LOC_SECTION, DEBUG_NORM_STR_OFFSETS_SECTION,
+       DEBUG_DWO_STR_OFFSETS_SECTION, DEBUG_STR_OFFSETS_SECTION,
+       DEBUG_DWO_STR_SECTION, DEBUG_NORM_STR_SECTION, DEBUG_STR_SECTION,
+       DEBUG_MACRO_SECTION_FLAGS, DEBUG_SKELETON_LINE_SECTION_LABEL,
+       DEBUG_SKELETON_INFO_SECTION_LABEL, DEBUG_ADDR_SECTION_LABEL
+       DEBUG_SKELETON_ABBREV_SECTION_LABEL): New macros.
+       (DEBUG_STR_SECTION_FLAGS): Adjust.
+       (TEXT_SECTION_LABEL, COLD_TEXT_SECTION_LABEL,
+       DEBUG_LINE_SECTION_LABEL, DEBUG_INFO_SECTION_LABEL,
+       DEBUG_ABBREV_SECTION_LABEL, DEBUG_ADDR_SECTION_LABEL,
+       DEBUG_LOC_SECTION_LABEL, DEBUG_RANGES_SECTION_LABEL,
+       DEBUG_MACINFO_SECTION_LABEL, DEBUG_MACRO_SECTION_LABEL): Adjust
+       indentation.
+       (debug_skeleton_abbrev_section_label, debug_addr_section_label,
+       debug_skeleton_line_section_label, debug_skeleton_info_section_label):
+       New global variables.
+       (add_AT_flag, add_AT_int, add_AT_unsigned, add_AT_double, add_AT_vec,
+       add_AT_data8): Initialize val_entry.
+       (add_AT_low_high_pc): New parameter force_direct. Handle
+       dwarf_split_debug_info.
+       (set_indirect_string, find_AT_string_form): New functions.
+       (AT_string_form): Adjust to call find_AT_string_from.
+       (add_AT_die_ref, add_AT_fde_ref, add_AT_loc, add_AT_list):
+       Initialize val_entry.
+       (addr_index_table): New global variable.
+       (addr_table_entry_do_hash, addr_table_entry_eq, add_addr_table_entry,
+       init_addr_table_entry, remove_addr_table_entry, index_addr_table_entry,
+       remove_loc_list_addr_table_entries): New functions.
+       (add_AT_addr, add_AT_lbl_id, add_AT_range_list): New parameter
+       force_direct.  Handle dwarf_split_debug_info.
+       (add_AT_file, add_AT_vms_delta, add_AT_lineptr, add_AT_macptr,
+       add_AT_offset): Initialize val_entry.
+       (UNRELOCATED_OFFSET, RELOCATED_OFFSET): New defines.
+       (size_of_die): Handle dwarf_split_debug_info.
+       (size_of_aranges, value_format): Call AT_class.  Check AT_index.
+       (output_die_abbrevs): New function.
+       (output_abbrev_section): Call output_die_abbrevs.
+       (new_loc_list): Initialize begin_entry.
+       (output_loc_list): Handle dwarf_split_debug_info.
+       (output_range_list_offset, output_loc_list_offset,
+       output_attr_index_or_value, ): New functions.
+       (output_die): Fix call to dw2_asm_output_data.  Call
+       output_attr_index_or_value and output_range_list_offset.
+       Adjust logic around dw_val_class_str.
+       (add_top_lebel_skeleton_die_attrs, get_skeleton_type_unit,
+       output_skeleton_debug_sections): New functions.
+       (output_comdat_type_unit, output_pubname, output_aranges): Handle
+       dwarf_split_debug_info.
+       (add_ranges_by_labels): New parameter force_direct.
+       (mem_loc_descriptor, loc_descr): Call new_addr_loc_descr.
+       (loc_list_from_tree, add_const_value_attribtue): Use dtprel_bools in
+       place of generic integer.
+       (dwarf2out_vms_debug_main_pointer, gen_entry_point_die, gen_label_die,
+       gen_call_site_die, gen_subprogram_die, gen_variable_die,
+       add_high_low_attributes): Adjust calls to add_AT_lbl_id.
+       (output_macinfo_op): Adjust indirect_string_logic.
+       (save_macinfo_strings): New function.
+       (output_macinfo): Adjust.
+       (dwarf2out_init): Handle dwarf_split_debug_info.
+       (index_string, output_index_string_offset, output_index_string): New
+       functions.
+       (output_indirect_string): Adjust.
+       (output_indirect_strings, output_addr_table_entry, output_addr_table):
+       New functions.
+       (resolve_addr_in_expr, hash_loc_operands): Handle DW_OP_GNU_addr_index
+       and DW_OP_GNU_const_index.  Handle dwarf_split_debug_info.  Call
+       remove_loc_list_addr_table_entries and remove_addr_table_entry.
+       (index_location_lists): New function.
+       (dwarf2out_finish): Handle dwarf_split_debug_info.  New variable
+       main_comp_unit_die.  Adjust calls to add_AT_low_high_pc,
+       add_ranges_by_labels, add_AT_addr, and add_AT_lineptr. Call
+       save_macinfo_strings and output_indirect_strings.
+
 2012-11-06  Gerald Pfeifer  <gerald@pfeifer.com>
 
        * config/i386/i386.c (make_dispatcher_decl): Guard with
index 5b69aff2febbd29ec3904dd2280257c5a5be6298..6b95c0558b7c58dd7054c9a7e91d04a57731f258 100644 (file)
@@ -2314,6 +2314,14 @@ grecord-gcc-switches
 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
index 73363a1a5a6c07de6333ad13a6e75b016ec1245d..55f72297ea0517acd8fb57c4eec733eb1af5d385 100644 (file)
@@ -4846,6 +4846,14 @@ it reasonable to use the optimizer for programs that might have bugs.
 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 9405c34d1e6a3feb06c6bdc2f3a46d64c55804ae..310f2dd5373c41973bd2fdff9e5fd62927609f49 100644 (file)
@@ -147,14 +147,19 @@ static GTY(()) VEC(tree,gc) *decl_scope_table;
 
 /* Pointers to various DWARF2 sections.  */
 static GTY(()) section *debug_info_section;
+static GTY(()) section *debug_skeleton_info_section;
 static GTY(()) section *debug_abbrev_section;
+static GTY(()) section *debug_skeleton_abbrev_section;
 static GTY(()) section *debug_aranges_section;
+static GTY(()) section *debug_addr_section;
 static GTY(()) section *debug_macinfo_section;
 static GTY(()) section *debug_line_section;
+static GTY(()) section *debug_skeleton_line_section;
 static GTY(()) section *debug_loc_section;
 static GTY(()) section *debug_pubnames_section;
 static GTY(()) section *debug_pubtypes_section;
 static GTY(()) section *debug_str_section;
+static GTY(()) section *debug_str_offsets_section;
 static GTY(()) section *debug_ranges_section;
 static GTY(()) section *debug_frame_section;
 
@@ -190,6 +195,9 @@ DEF_VEC_ALLOC_P (dw_fde_ref, gc);
 
 /* A vector for a table that contains frame description
    information for each routine.  */
+#define NOT_INDEXED (-1U)
+#define NO_INDEX_ASSIGNED (-2U)
+
 static GTY(()) VEC(dw_fde_ref, gc) *fde_vec;
 
 struct GTY(()) indirect_string_node {
@@ -197,6 +205,7 @@ struct GTY(()) indirect_string_node {
   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;
@@ -1198,13 +1207,35 @@ static GTY(()) VEC(deferred_locations, gc) *deferred_locations_list;
 DEF_VEC_P(dw_die_ref);
 DEF_VEC_ALLOC_P(dw_die_ref,heap);
 
+/* Describe an entry into the .debug_addr section.  */
+
+enum ate_kind {
+  ate_kind_rtx,
+  ate_kind_rtx_dtprel,
+  ate_kind_label
+};
+
+typedef struct GTY(()) addr_table_entry_struct {
+  enum ate_kind kind;
+  unsigned int refcount;
+  unsigned int index;
+  union addr_table_entry_struct_union
+    {
+      rtx GTY ((tag ("0"))) rtl;
+      char * GTY ((tag ("1"))) label;
+    }
+  GTY ((desc ("%1.kind"))) addr;
+}
+addr_table_entry;
+
 /* Location lists are ranges + location descriptions for that range,
    so you can track variables that are in different places over
    their entire life.  */
 typedef struct GTY(()) dw_loc_list_struct {
   dw_loc_list_ref dw_loc_next;
-  const char *begin; /* Label for begin address of range */
-  const char *end;  /* Label for end address of range */
+  const char *begin; /* Label and addr_entry for start of range */
+  addr_table_entry *begin_entry;
+  const char *end;  /* Label for end of range */
   char *ll_symbol; /* Label for beginning of location list.
                      Only on head of list */
   const char *section; /* Section this loclist is relative to */
@@ -1248,8 +1279,10 @@ new_loc_descr (enum dwarf_location_atom op, unsigned HOST_WIDE_INT oprnd1,
 
   descr->dw_loc_opc = op;
   descr->dw_loc_oprnd1.val_class = dw_val_class_unsigned_const;
+  descr->dw_loc_oprnd1.val_entry = NULL;
   descr->dw_loc_oprnd1.v.val_unsigned = oprnd1;
   descr->dw_loc_oprnd2.val_class = dw_val_class_unsigned_const;
+  descr->dw_loc_oprnd2.val_entry = NULL;
   descr->dw_loc_oprnd2.v.val_unsigned = oprnd2;
 
   return descr;
@@ -1454,6 +1487,11 @@ size_of_loc_descr (dw_loc_descr_ref loc)
     case DW_OP_addr:
       size += DWARF2_ADDR_SIZE;
       break;
+    case DW_OP_GNU_addr_index:
+    case DW_OP_GNU_const_index:
+      gcc_assert (loc->dw_loc_oprnd1.val_entry->index != NO_INDEX_ASSIGNED);
+      size += size_of_uleb128 (loc->dw_loc_oprnd1.val_entry->index);
+      break;
     case DW_OP_const1u:
     case DW_OP_const1s:
       size += 1;
@@ -1890,6 +1928,13 @@ output_loc_operands (dw_loc_descr_ref loc, int for_eh_or_skip)
        }
       break;
 
+    case DW_OP_GNU_addr_index:
+    case DW_OP_GNU_const_index:
+      gcc_assert (loc->dw_loc_oprnd1.val_entry->index != NO_INDEX_ASSIGNED);
+      dw2_asm_output_data_uleb128 (loc->dw_loc_oprnd1.val_entry->index,
+                                   "(index into .debug_addr)");
+      break;
+
     case DW_OP_GNU_implicit_pointer:
       {
        char label[MAX_ARTIFICIAL_LABEL_BYTES
@@ -2065,6 +2110,8 @@ output_loc_operands_raw (dw_loc_descr_ref loc)
   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 ();
@@ -2248,6 +2295,7 @@ build_cfa_loc (dw_cfa_location *cfa, HOST_WIDE_INT offset)
     {
       head = new_reg_loc_descr (cfa->reg, cfa->base_offset);
       head->dw_loc_oprnd1.val_class = dw_val_class_const;
+      head->dw_loc_oprnd1.val_entry = NULL;
       tmp = new_loc_descr (DW_OP_deref, 0, 0);
       add_loc_descr (&head, tmp);
       if (offset != 0)
@@ -2877,6 +2925,7 @@ static tree decl_ultimate_origin (const_tree);
 static tree decl_class_context (tree);
 static void add_dwarf_attr (dw_die_ref, dw_attr_ref);
 static inline enum dw_val_class AT_class (dw_attr_ref);
+static inline unsigned int AT_index (dw_attr_ref);
 static void add_AT_flag (dw_die_ref, enum dwarf_attribute, unsigned);
 static inline unsigned AT_flag (dw_attr_ref);
 static void add_AT_int (dw_die_ref, enum dwarf_attribute, HOST_WIDE_INT);
@@ -2904,7 +2953,9 @@ static inline dw_loc_descr_ref AT_loc (dw_attr_ref);
 static void add_AT_loc_list (dw_die_ref, enum dwarf_attribute,
                             dw_loc_list_ref);
 static inline dw_loc_list_ref AT_loc_list (dw_attr_ref);
-static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx);
+static addr_table_entry *add_addr_table_entry (void *, enum ate_kind);
+static void remove_addr_table_entry (addr_table_entry *);
+static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx, bool);
 static inline rtx AT_addr (dw_attr_ref);
 static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *);
 static void add_AT_lineptr (dw_die_ref, enum dwarf_attribute, const char *);
@@ -2912,7 +2963,7 @@ 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);
@@ -3007,6 +3058,7 @@ static unsigned long size_of_aranges (void);
 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);
@@ -3022,10 +3074,10 @@ static void output_aranges (unsigned long);
 static unsigned int add_ranges_num (int);
 static unsigned int add_ranges (const_tree);
 static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
-                                 bool *);
+                                  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);
@@ -3164,36 +3216,124 @@ static bool generic_type_p (tree);
 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.v.val_addr = addr;
+  ref->dtprel = dtprel;
+  if (dwarf_split_debug_info)
+    ref->dw_loc_oprnd1.val_entry
+        = add_addr_table_entry (addr,
+                                dtprel ? ate_kind_rtx_dtprel : ate_kind_rtx);
+  else
+    ref->dw_loc_oprnd1.val_entry = NULL;
+
+  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"
@@ -3204,43 +3344,62 @@ static void gen_scheduled_generic_parms_dies (void);
 #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
@@ -3254,7 +3413,11 @@ static char cold_text_section_label[MAX_ARTIFICIAL_LABEL_BYTES];
 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];
@@ -3495,6 +3658,21 @@ AT_class (dw_attr_ref a)
   return a->dw_attr_val.val_class;
 }
 
+/* Return the index for any attribute that will be referenced with a
+   DW_FORM_GNU_addr_index or DW_FORM_GNU_str_index.  String indices
+   are stored in dw_attr_val.v.val_str 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 if (a->dw_attr_val.val_entry != NULL)
+    return a->dw_attr_val.val_entry->index;
+  return NOT_INDEXED;
+}
+
 /* Add a flag value attribute to a DIE.  */
 
 static inline void
@@ -3504,6 +3682,7 @@ add_AT_flag (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int flag)
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_flag;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_flag = flag;
   add_dwarf_attr (die, &attr);
 }
@@ -3524,6 +3703,7 @@ add_AT_int (dw_die_ref die, enum dwarf_attribute attr_kind, HOST_WIDE_INT int_va
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_const;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_int = int_val;
   add_dwarf_attr (die, &attr);
 }
@@ -3545,6 +3725,7 @@ add_AT_unsigned (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_unsigned_const;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_unsigned = unsigned_val;
   add_dwarf_attr (die, &attr);
 }
@@ -3566,6 +3747,7 @@ add_AT_double (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_const_double;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_double.high = high;
   attr.dw_attr_val.v.val_double.low = low;
   add_dwarf_attr (die, &attr);
@@ -3581,6 +3763,7 @@ add_AT_vec (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_vec;
+  attr.dw_attr_val.val_entry = NULL;
   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;
@@ -3597,19 +3780,32 @@ add_AT_data8 (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_data8;
+  attr.dw_attr_val.val_entry = NULL;
   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.  When using
+   dwarf_split_debug_info, address attributes in dies destined for the
+   final executable have force_direct set to avoid using indexed
+   references.  */
+
 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;
+  char * lbl_id;
 
+  lbl_id = xstrdup (lbl_low);
   attr.dw_attr = DW_AT_low_pc;
   attr.dw_attr_val.val_class = dw_val_class_lbl_id;
-  attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_low);
+  attr.dw_attr_val.v.val_lbl_id = lbl_id;
+  if (dwarf_split_debug_info && !force_direct)
+    attr.dw_attr_val.val_entry
+        = add_addr_table_entry (lbl_id, ate_kind_label);
+  else
+    attr.dw_attr_val.val_entry = NULL;
   add_dwarf_attr (die, &attr);
 
   attr.dw_attr = DW_AT_high_pc;
@@ -3617,7 +3813,14 @@ add_AT_low_high_pc (dw_die_ref die, const char *lbl_low, const char *lbl_high)
     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.v.val_lbl_id = xstrdup (lbl_high);
+  lbl_id = xstrdup (lbl_high);
+  attr.dw_attr_val.v.val_lbl_id = lbl_id;
+  if (attr.dw_attr_val.val_class == dw_val_class_lbl_id
+      && dwarf_split_debug_info && !force_direct)
+    attr.dw_attr_val.val_entry
+        = add_addr_table_entry (lbl_id, ate_kind_label);
+  else
+    attr.dw_attr_val.val_entry = NULL;
   add_dwarf_attr (die, &attr);
 }
 
@@ -3675,6 +3878,7 @@ add_AT_string (dw_die_ref die, enum dwarf_attribute attr_kind, const char *str)
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_str;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_str = node;
   add_dwarf_attr (die, &attr);
 }
@@ -3686,19 +3890,43 @@ AT_string (dw_attr_ref a)
   return a->dw_attr_val.v.val_str->str;
 }
 
+/* Call this function directly to bypass AT_string_form's logic to put
+   the string inline in the die. */
+
+static void
+set_indirect_string (struct indirect_string_node *node)
+{
+  char label[32];
+  /* Already indirect is a no op.  */
+  if (node->form == DW_FORM_strp || node->form == DW_FORM_GNU_str_index)
+    {
+      gcc_assert (node->label);
+      return;
+    }
+  ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
+  ++dw2_string_counter;
+  node->label = xstrdup (label);
+
+  if (!dwarf_split_debug_info)
+    {
+      node->form = DW_FORM_strp;
+      node->index = NOT_INDEXED;
+    }
+  else
+    {
+      node->form = DW_FORM_GNU_str_index;
+      node->index = NO_INDEX_ASSIGNED;
+    }
+}
+
 /* Find out whether a string should be output inline in DIE
    or out-of-line in .debug_str section.  */
 
 static enum dwarf_form
-AT_string_form (dw_attr_ref a)
+find_string_form (struct indirect_string_node *node)
 {
-  struct indirect_string_node *node;
   unsigned int len;
-  char label[32];
 
-  gcc_assert (a && AT_class (a) == dw_val_class_str);
-
-  node = a->dw_attr_val.v.val_str;
   if (node->form)
     return node->form;
 
@@ -3717,11 +3945,19 @@ AT_string_form (dw_attr_ref a)
       && (len - DWARF_OFFSET_SIZE) * node->refcount <= len))
     return node->form = DW_FORM_string;
 
-  ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
-  ++dw2_string_counter;
-  node->label = xstrdup (label);
+  set_indirect_string (node);
+
+  return node->form;
+}
 
-  return node->form = DW_FORM_strp;
+/* Find out whether the string referenced from the attribute should be
+   output inline in DIE or out-of-line in .debug_str section.  */
+
+static enum dwarf_form
+AT_string_form (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_str);
+  return find_string_form (a->dw_attr_val.v.val_str);
 }
 
 /* Add a DIE reference attribute value to a DIE.  */
@@ -3742,6 +3978,7 @@ add_AT_die_ref (dw_die_ref die, enum dwarf_attribute attr_kind, dw_die_ref targ_
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_die_ref;
+  attr.dw_attr_val.val_entry = NULL;
   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);
@@ -3800,6 +4037,7 @@ add_AT_fde_ref (dw_die_ref die, enum dwarf_attribute attr_kind, unsigned int tar
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_fde_ref;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_fde_index = targ_fde;
   add_dwarf_attr (die, &attr);
 }
@@ -3813,6 +4051,7 @@ add_AT_loc (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_descr_ref loc
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_loc;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_loc = loc;
   add_dwarf_attr (die, &attr);
 }
@@ -3831,6 +4070,7 @@ add_AT_loc_list (dw_die_ref die, enum dwarf_attribute attr_kind, dw_loc_list_ref
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_loc_list;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_loc_list = loc_list;
   add_dwarf_attr (die, &attr);
   have_location_lists = true;
@@ -3850,16 +4090,169 @@ AT_loc_list_ptr (dw_attr_ref a)
   return &a->dw_attr_val.v.val_loc_list;
 }
 
-/* Add an address constant attribute value to a DIE.  */
+/* Table of entries into the .debug_addr section.  */
+
+static GTY ((param_is (addr_table_entry))) htab_t addr_index_table;
+
+/* Hash an address_table_entry.  */
+
+static hashval_t
+addr_table_entry_do_hash (const void *x)
+{
+  const addr_table_entry *a = (const addr_table_entry *) x;
+  switch (a->kind)
+    {
+      case ate_kind_rtx:
+        return iterative_hash_rtx (a->addr.rtl, 0);
+      case ate_kind_rtx_dtprel:
+        return iterative_hash_rtx (a->addr.rtl, 1);
+      case ate_kind_label:
+        return htab_hash_string (a->addr.label);
+      default:
+        gcc_unreachable ();
+    }
+}
+
+/* Determine equality for two address_table_entries.  */
+
+static int
+addr_table_entry_eq (const void *x1, const void *x2)
+{
+  const addr_table_entry *a1 = (const addr_table_entry *) x1;
+  const addr_table_entry *a2 = (const addr_table_entry *) x2;
+
+  if (a1->kind != a2->kind)
+    return 0;
+  switch (a1->kind)
+    {
+      case ate_kind_rtx:
+      case ate_kind_rtx_dtprel:
+        return rtx_equal_p (a1->addr.rtl, a2->addr.rtl);
+      case ate_kind_label:
+        return strcmp (a1->addr.label, a2->addr.label) == 0;
+      default:
+        gcc_unreachable ();
+    }
+}
+
+/* Initialize an addr_table_entry.  */
+
+void
+init_addr_table_entry (addr_table_entry *e, enum ate_kind kind, void *addr)
+{
+  e->kind = kind;
+  switch (kind)
+    {
+      case ate_kind_rtx:
+      case ate_kind_rtx_dtprel:
+        e->addr.rtl = (rtx) addr;
+        break;
+      case ate_kind_label:
+        e->addr.label = (char *) addr;
+        break;
+    }
+  e->refcount = 0;
+  e->index = NO_INDEX_ASSIGNED;
+}
+
+/* Add attr to the address table entry to the table.  Defer setting an
+   index until output time.  */
+
+static addr_table_entry *
+add_addr_table_entry (void *addr, enum ate_kind kind)
+{
+  addr_table_entry *node;
+  addr_table_entry finder;
+  void **slot;
+
+  gcc_assert (dwarf_split_debug_info);
+  if (! addr_index_table)
+    addr_index_table = htab_create_ggc (10, addr_table_entry_do_hash,
+                                        addr_table_entry_eq, NULL);
+  init_addr_table_entry (&finder, kind, addr);
+  slot = htab_find_slot (addr_index_table, &finder, INSERT);
+
+  if (*slot == HTAB_EMPTY_ENTRY)
+    {
+      node = ggc_alloc_cleared_addr_table_entry ();
+      init_addr_table_entry (node, kind, addr);
+      *slot = node;
+    }
+  else
+    node = (addr_table_entry *) *slot;
+
+  node->refcount++;
+  return node;
+}
+
+/* Remove an entry from the addr table by decrementing its refcount.
+   Strictly, decrementing the refcount would be enough, but the
+   assertion that the entry is actually in the table has found
+   bugs.  */
+
+static void
+remove_addr_table_entry (addr_table_entry *entry)
+{
+  addr_table_entry *node;
+
+  gcc_assert (dwarf_split_debug_info && addr_index_table);
+  node = (addr_table_entry *) htab_find (addr_index_table, entry);
+  /* After an index is assigned, the table is frozen.  */
+  gcc_assert (node->refcount > 0 && node->index == NO_INDEX_ASSIGNED);
+  node->refcount--;
+}
+
+/* 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_entry != NULL)
+      {
+        gcc_assert (descr->dw_loc_oprnd1.val_entry->index == NO_INDEX_ASSIGNED);
+        remove_addr_table_entry (descr->dw_loc_oprnd1.val_entry);
+      }
+}
+
+/* A helper function for dwarf2out_finish called through
+   htab_traverse.  Assign an addr_table_entry its index.  All entries
+   must be collected into the table when this function is called,
+   because the indexing code relies on htab_traverse to traverse nodes
+   in the same order for each run. */
+
+static int
+index_addr_table_entry (void **h, void *v)
+{
+  addr_table_entry *node = (addr_table_entry *) *h;
+  unsigned int *index = (unsigned int *) v;
+
+  gcc_assert(node->index == NO_INDEX_ASSIGNED);
+  node->index = *index;
+  *index += 1;
+
+  return 1;
+}
+
+/* Add an address constant attribute value to a DIE.  When using
+   dwarf_split_debug_info, address attributes in dies destined for the
+   final executable should be direct references--setting the parameter
+   force_direct ensures this behavior.  */
 
 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.v.val_addr = addr;
+  if (dwarf_split_debug_info && !force_direct)
+    attr.dw_attr_val.val_entry = add_addr_table_entry (addr, ate_kind_rtx);
+  else
+    attr.dw_attr_val.val_entry = NULL;
   add_dwarf_attr (die, &attr);
 }
 
@@ -3882,6 +4275,7 @@ add_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_file;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_file = fd;
   add_dwarf_attr (die, &attr);
 }
@@ -3905,6 +4299,7 @@ add_AT_vms_delta (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_vms_delta;
+  attr.dw_attr_val.val_entry = NULL;
   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);
@@ -3913,13 +4308,19 @@ add_AT_vms_delta (dw_die_ref die, enum dwarf_attribute attr_kind,
 /* Add a label identifier attribute value to a DIE.  */
 
 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)
 {
   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_entry = NULL;
   attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id);
+  if (dwarf_split_debug_info)
+    attr.dw_attr_val.val_entry
+        = add_addr_table_entry (attr.dw_attr_val.v.val_lbl_id,
+                                ate_kind_label);
   add_dwarf_attr (die, &attr);
 }
 
@@ -3934,6 +4335,7 @@ add_AT_lineptr (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_lineptr;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
   add_dwarf_attr (die, &attr);
 }
@@ -3949,6 +4351,7 @@ add_AT_macptr (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_macptr;
+  attr.dw_attr_val.val_entry = NULL;
   attr.dw_attr_val.v.val_lbl_id = xstrdup (label);
   add_dwarf_attr (die, &attr);
 }
@@ -3963,20 +4366,34 @@ add_AT_offset (dw_die_ref die, enum dwarf_attribute attr_kind,
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_offset;
+  attr.dw_attr_val.val_entry = NULL;
   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.  When using
+   dwarf_split_debug_info, address attributes in dies destined for the
+   final executable should be direct references--setting the parameter
+   force_direct ensures this behavior.  */
+
+#define UNRELOCATED_OFFSET ((addr_table_entry *) 1)
+#define RELOCATED_OFFSET (NULL)
 
 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, use val_entry to store whether the
+     offset should follow split-debug-info or normal semantics.  This
+     value is read in output_range_list_offset.  */
+  if (dwarf_split_debug_info && !force_direct)
+    attr.dw_attr_val.val_entry = UNRELOCATED_OFFSET;
+  else
+    attr.dw_attr_val.val_entry = RELOCATED_OFFSET;
   attr.dw_attr_val.v.val_offset = offset;
   add_dwarf_attr (die, &attr);
 }
@@ -7163,6 +7580,7 @@ size_of_die (dw_die_ref die)
   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)
@@ -7170,7 +7588,13 @@ size_of_die (dw_die_ref die)
       switch (AT_class (a))
        {
        case dw_val_class_addr:
-         size += DWARF2_ADDR_SIZE;
+          if (dwarf_split_debug_info && AT_index (a) != NOT_INDEXED)
+            {
+              gcc_assert (AT_index (a) != NO_INDEX_ASSIGNED);
+              size += size_of_uleb128 (AT_index (a));
+            }
+          else
+            size += DWARF2_ADDR_SIZE;
          break;
        case dw_val_class_offset:
          size += DWARF_OFFSET_SIZE;
@@ -7188,10 +7612,16 @@ size_of_die (dw_die_ref die)
          }
          break;
        case dw_val_class_loc_list:
-         size += DWARF_OFFSET_SIZE;
+          if (dwarf_split_debug_info && AT_index (a) != NOT_INDEXED)
+            {
+              gcc_assert (AT_index (a) != NO_INDEX_ASSIGNED);
+              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));
@@ -7251,15 +7681,24 @@ size_of_die (dw_die_ref die)
          size += DWARF_OFFSET_SIZE;
          break;
        case dw_val_class_lbl_id:
-         size += DWARF2_ADDR_SIZE;
+          if (dwarf_split_debug_info && AT_index (a) != NOT_INDEXED)
+            {
+              gcc_assert (AT_index (a) != NO_INDEX_ASSIGNED);
+              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;
@@ -7444,7 +7883,7 @@ size_of_aranges (void)
 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.  */
@@ -7454,7 +7893,8 @@ value_format (dw_attr_ref a)
        case DW_AT_high_pc:
        case DW_AT_entry_pc:
        case DW_AT_trampoline:
-         return DW_FORM_addr;
+          return (AT_index (a) == NOT_INDEXED
+                  ? DW_FORM_addr : DW_FORM_GNU_addr_index);
        default:
          break;
        }
@@ -7572,7 +8012,8 @@ value_format (dw_attr_ref a)
     case dw_val_class_fde_ref:
       return DW_FORM_data;
     case dw_val_class_lbl_id:
-      return DW_FORM_addr;
+      return (AT_index (a) == NOT_INDEXED
+              ? DW_FORM_addr : DW_FORM_GNU_addr_index);
     case dw_val_class_lineptr:
     case dw_val_class_macptr:
       return dwarf_version >= 4 ? DW_FORM_sec_offset : DW_FORM_data;
@@ -7624,40 +8065,46 @@ output_value_format (dw_attr_ref a)
   dw2_asm_output_data_uleb128 (form, "(%s)", dwarf_form_name (form));
 }
 
-/* Output the .debug_abbrev section which defines the DIE abbreviation
-   table.  */
+/* Given a die and id, produce the appropriate abbreviations.  */
 
 static void
-output_abbrev_section (void)
+output_die_abbrevs (unsigned long abbrev_id, dw_die_ref abbrev)
 {
-  unsigned long abbrev_id;
+  unsigned ix;
+  dw_attr_ref a_attr;
 
-  for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++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++)
     {
-      dw_die_ref abbrev = abbrev_die_table[abbrev_id];
-      unsigned ix;
-      dw_attr_ref a_attr;
+      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_uleb128 (abbrev_id, "(abbrev code)");
-      dw2_asm_output_data_uleb128 (abbrev->die_tag, "(TAG: %s)",
-                                  dwarf_tag_name (abbrev->die_tag));
+  dw2_asm_output_data (1, 0, NULL);
+  dw2_asm_output_data (1, 0, NULL);
+}
 
-      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);
-       }
+/* Output the .debug_abbrev section which defines the DIE abbreviation
+   table.  */
 
-      dw2_asm_output_data (1, 0, NULL);
-      dw2_asm_output_data (1, 0, NULL);
-    }
+static void
+output_abbrev_section (void)
+{
+  unsigned long abbrev_id;
+
+  for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
+    output_die_abbrevs (abbrev_id, abbrev_die_table[abbrev_id]);
 
   /* Terminate the table.  */
   dw2_asm_output_data (1, 0, NULL);
@@ -7694,6 +8141,7 @@ new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
   dw_loc_list_ref retlist = ggc_alloc_cleared_dw_loc_list_node ();
 
   retlist->begin = begin;
+  retlist->begin_entry = NULL;
   retlist->end = end;
   retlist->expr = expr;
   retlist->section = section;
@@ -7738,7 +8186,22 @@ output_loc_list (dw_loc_list_ref list_head)
         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_entry->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)",
@@ -7764,12 +8227,86 @@ output_loc_list (dw_loc_list_ref list_head)
       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 a range_list offset into the debug_range section.  Emit a
+   relocated reference if val_entry is NULL, otherwise, emit an
+   indirect reference.  */
+
+static void
+output_range_list_offset (dw_attr_ref a)
+{
+  const char *name = dwarf_attr_name (a->dw_attr);
+
+  if (a->dw_attr_val.val_entry == RELOCATED_OFFSET)
+    {
+      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) != NOT_INDEXED)
+    {
+      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_high_pc:
+      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.  */
@@ -7810,7 +8347,7 @@ output_die (dw_die_ref die)
       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:
@@ -7819,15 +8356,7 @@ output_die (dw_die_ref die)
          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:
@@ -7883,7 +8412,7 @@ output_die (dw_die_ref die)
              }
 
            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);
          }
@@ -7930,13 +8459,7 @@ output_die (dw_die_ref die)
          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:
@@ -7993,7 +8516,7 @@ output_die (dw_die_ref die)
          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:
@@ -8007,12 +8530,15 @@ output_die (dw_die_ref die)
          break;
 
        case dw_val_class_str:
-         if (AT_string_form (a) == DW_FORM_strp)
-           dw2_asm_output_offset (DWARF_OFFSET_SIZE,
-                                  a->dw_attr_val.v.val_str->label,
-                                  debug_str_section,
-                                  "%s: \"%s\"", name, AT_string (a));
-         else
+          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 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;
 
@@ -8155,6 +8681,96 @@ add_AT_pubnames (dw_die_ref die)
     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
@@ -8182,7 +8798,11 @@ output_comdat_type_unit (comdat_type_node *node)
   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++)
@@ -8208,6 +8828,36 @@ output_comdat_type_unit (comdat_type_node *node)
   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.  */
@@ -8243,7 +8893,7 @@ add_pubname (tree decl, dw_die_ref die)
      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);
@@ -8351,9 +9001,14 @@ output_pubnames (VEC (pubname_entry, gc) * names)
                         "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");
 
@@ -8414,9 +9069,14 @@ output_aranges (unsigned long aranges_length)
                       "Length of Address Ranges Info");
   /* Version number for aranges is still 2, even in DWARF3.  */
   dw2_asm_output_data (2, 2, "DWARF Version");
-  dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
-                        debug_info_section,
-                        "Offset of Compilation Unit Info");
+  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");
 
@@ -8513,12 +9173,14 @@ add_ranges (const_tree block)
   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.
+   When using dwarf_split_debug_info, address attributes in dies destined
+   for the final executable should be direct references--setting the
+   parameter force_direct ensures this behavior.  */
 
 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;
@@ -8541,7 +9203,7 @@ add_ranges_by_labels (dw_die_ref die, const char *begin, const char *end,
   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;
     }
 }
@@ -9078,7 +9740,7 @@ output_one_line_info_table (dw_line_info_table *table)
    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;
@@ -9148,6 +9810,12 @@ output_line_info (void)
   /* 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)
     {
@@ -11452,14 +12120,7 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
          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);
@@ -11471,9 +12132,7 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
        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;
 
@@ -12377,9 +13036,7 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
       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);
        }
@@ -13079,9 +13736,8 @@ loc_list_from_tree (tree loc, int want_address)
       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)
            {
@@ -13098,9 +13754,8 @@ loc_list_from_tree (tree loc, int want_address)
                  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
            {
@@ -13112,8 +13767,7 @@ loc_list_from_tree (tree loc, int want_address)
                 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);
@@ -13126,12 +13780,8 @@ loc_list_from_tree (tree loc, int want_address)
          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;
@@ -13176,11 +13826,7 @@ loc_list_from_tree (tree loc, int want_address)
            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;
@@ -14105,9 +14751,7 @@ add_const_value_attribute (dw_die_ref die, rtx rtl)
          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);
@@ -15627,7 +16271,7 @@ add_name_and_src_coords_attributes (dw_die_ref die, tree decl)
   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 */
@@ -16896,7 +17540,7 @@ gen_call_site_die (tree decl, dw_die_ref subr_die,
       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;
 }
@@ -17089,7 +17733,8 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
          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
            {
@@ -17100,7 +17745,8 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                                           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
@@ -17143,10 +17789,11 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                     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);
                }
@@ -17165,7 +17812,7 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
 
                  /* 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,
@@ -17190,14 +17837,15 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
 
                  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);
@@ -17638,7 +18286,7 @@ gen_variable_die (tree decl, tree origin, dw_die_ref context_die)
            {
              /* 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)
                {
@@ -17825,7 +18473,7 @@ gen_label_die (tree decl, dw_die_ref context_die)
          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);
        }
       else if (insn
               && NOTE_P (insn)
@@ -17833,7 +18481,7 @@ gen_label_die (tree decl, dw_die_ref context_die)
               && 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);
        }
     }
 }
@@ -17874,7 +18522,7 @@ add_high_low_attributes (tree stmt, dw_die_ref die)
        {
          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);
        }
 
       /* Optimize duplicate .debug_ranges lists or even tails of
@@ -17922,12 +18570,13 @@ add_high_low_attributes (tree stmt, dw_die_ref die)
            ++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
@@ -17945,7 +18594,7 @@ add_high_low_attributes (tree stmt, dw_die_ref die)
                                   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);
     }
 }
 
@@ -20539,23 +21188,22 @@ output_macinfo_op (macinfo_entry *ref)
     case DW_MACRO_GNU_define_indirect:
     case DW_MACRO_GNU_undef_indirect:
       node = find_AT_string (ref->info);
-      if (node->form != DW_FORM_strp)
-       {
-         char label[32];
-         ASM_GENERATE_INTERNAL_LABEL (label, "LASF", dw2_string_counter);
-         ++dw2_string_counter;
-         node->label = xstrdup (label);
-         node->form = DW_FORM_strp;
-       }
+      gcc_assert (node
+                  && ((node->form == DW_FORM_strp)
+                      || (node->form == DW_FORM_GNU_str_index)));
       dw2_asm_output_data (1, ref->code,
                           ref->code == DW_MACRO_GNU_define_indirect
                           ? "Define macro indirect"
                           : "Undefine macro indirect");
       dw2_asm_output_data_uleb128 (ref->lineno, "At line number %lu",
                                   (unsigned long) ref->lineno);
-      dw2_asm_output_offset (DWARF_OFFSET_SIZE, node->label,
-                            debug_str_section, "The macro: \"%s\"",
-                            ref->info);
+      if (node->form == DW_FORM_strp)
+        dw2_asm_output_offset (DWARF_OFFSET_SIZE, node->label,
+                               debug_str_section, "The macro: \"%s\"",
+                               ref->info);
+      else
+        dw2_asm_output_data_uleb128 (node->index, "The macro: \"%s\"",
+                                     ref->info);
       break;
     case DW_MACRO_GNU_transparent_include:
       dw2_asm_output_data (1, ref->code, "Transparent include");
@@ -20698,6 +21346,42 @@ optimize_macinfo_range (unsigned int idx, VEC (macinfo_entry, gc) *files,
   return count;
 }
 
+/* Save any strings needed by the macinfo table in the debug str
+   table.  All strings must be collected into the table by the time
+   index_string is called.  */
+
+static void
+save_macinfo_strings (void)
+{
+  unsigned len;
+  unsigned i;
+  macinfo_entry *ref;
+
+  for (i = 0; VEC_iterate (macinfo_entry, macinfo_table, i, ref); i++)
+    {
+      switch (ref->code)
+        {
+          /* Match the logic in output_macinfo_op to decide on
+             indirect strings.  */
+          case DW_MACINFO_define:
+          case DW_MACINFO_undef:
+            len = strlen (ref->info) + 1;
+            if (!dwarf_strict
+                && len > DWARF_OFFSET_SIZE
+                && !DWARF2_INDIRECT_STRING_SUPPORT_MISSING_ON_TARGET
+                && (debug_str_section->common.flags & SECTION_MERGE) != 0)
+              set_indirect_string (find_AT_string (ref->info));
+            break;
+          case DW_MACRO_GNU_define_indirect:
+          case DW_MACRO_GNU_undef_indirect:
+            set_indirect_string (find_AT_string (ref->info));
+            break;
+          default:
+            break;
+        }
+    }
+}
+
 /* Output macinfo section(s).  */
 
 static void
@@ -20726,8 +21410,10 @@ output_macinfo (void)
        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
@@ -20866,20 +21552,54 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
 
   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,
@@ -20905,10 +21625,13 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
                               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);
@@ -20934,6 +21657,71 @@ dwarf2out_assembly_start (void)
     fprintf (asm_out_file, "\t.cfi_sections\t.debug_frame\n");
 }
 
+/* A helper function for dwarf2out_finish called through
+   htab_traverse.  Assign a string its index.  All strings must be
+   collected into the table by the time index_string is called,
+   because the indexing code relies on htab_traverse to traverse nodes
+   in the same order for each run. */
+
+static int
+index_string (void **h, void *v)
+{
+  struct indirect_string_node *node = (struct indirect_string_node *) *h;
+  unsigned int *index = (unsigned int *) v;
+
+  find_string_form (node);
+  if (node->form == DW_FORM_GNU_str_index && node->refcount > 0)
+    {
+      gcc_assert(node->index == NO_INDEX_ASSIGNED);
+      node->index = *index;
+      *index += 1;
+    }
+  return 1;
+}
+
+/* A helper function for output_indirect_strings called through
+   htab_traverse.  Output the offset to a string and update the
+   current offset.  */
+
+static int
+output_index_string_offset (void **h, void *v)
+{
+  struct indirect_string_node *node = (struct indirect_string_node *) *h;
+  unsigned int *offset = (unsigned int *) v;
+
+  if (node->form == DW_FORM_GNU_str_index && node->refcount > 0)
+    {
+      /* Assert that this node has been assigned an index.  */
+      gcc_assert (node->index != NO_INDEX_ASSIGNED
+                  && node->index != NOT_INDEXED);
+      dw2_asm_output_data (DWARF_OFFSET_SIZE, *offset,
+                           "indexed string 0x%x: %s", node->index, node->str);
+      *offset += strlen (node->str) + 1;
+    }
+  return 1;
+}
+
+/* A helper function for dwarf2out_finish called through
+   htab_traverse.  Output the indexed string.  */
+
+static int
+output_index_string (void **h, void *v)
+{
+  struct indirect_string_node *node = (struct indirect_string_node *) *h;
+  unsigned int *cur_idx = (unsigned int *) v;
+
+  if (node->form == DW_FORM_GNU_str_index && node->refcount > 0)
+    {
+      /* Assert that the strings are output in the same order as their
+         indexes were assigned.  */
+      gcc_assert (*cur_idx == node->index);
+      ASM_OUTPUT_LABEL (asm_out_file, node->label);
+      assemble_string (node->str, strlen (node->str) + 1);
+      *cur_idx += 1;
+    }
+  return 1;
+}
+
 /* A helper function for dwarf2out_finish called through
    htab_traverse.  Emit one queued .debug_str string.  */
 
@@ -20942,9 +21730,8 @@ output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED)
 {
   struct indirect_string_node *node = (struct indirect_string_node *) *h;
 
-  if (node->form == DW_FORM_strp)
+  if (node->form == DW_FORM_strp && node->refcount > 0)
     {
-      switch_to_section (debug_str_section);
       ASM_OUTPUT_LABEL (asm_out_file, node->label);
       assemble_string (node->str, strlen (node->str) + 1);
     }
@@ -20952,6 +21739,87 @@ output_indirect_string (void **h, void *v ATTRIBUTE_UNUSED)
   return 1;
 }
 
+/* Output the indexed string table.  */
+
+static void
+output_indirect_strings (void)
+{
+  if (!dwarf_split_debug_info)
+    {
+      switch_to_section (debug_str_section);
+      htab_traverse (debug_str_hash, output_indirect_string, NULL);
+    }
+  else
+    {
+      unsigned int offset = 0;
+      unsigned int cur_idx = 0;
+
+      switch_to_section (debug_str_offsets_section);
+      htab_traverse_noresize (debug_str_hash,
+                              output_index_string_offset,
+                              &offset);
+      switch_to_section (debug_str_section);
+      htab_traverse_noresize (debug_str_hash,
+                              output_index_string,
+                              &cur_idx);
+    }
+}
+
+/* Callback for htab_traverse to assign an index to an entry in the
+   table, and to write that entry to the .debug_addr section.  */
+
+static int
+output_addr_table_entry (void **slot, void *data)
+{
+  addr_table_entry *entry = (addr_table_entry *) *slot;
+  unsigned int *cur_index = (unsigned int *)data;
+
+  if (entry->refcount == 0)
+    {
+      gcc_assert (entry->index == NO_INDEX_ASSIGNED
+                  || entry->index == NOT_INDEXED);
+      return 1;
+    }
+
+  gcc_assert (entry->index == *cur_index);
+  (*cur_index)++;
+
+  switch (entry->kind)
+    {
+      case ate_kind_rtx:
+        dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, entry->addr.rtl,
+                                 "0x%x", entry->index);
+        break;
+      case ate_kind_rtx_dtprel:
+        gcc_assert (targetm.asm_out.output_dwarf_dtprel);
+        targetm.asm_out.output_dwarf_dtprel (asm_out_file,
+                                             DWARF2_ADDR_SIZE,
+                                             entry->addr.rtl);
+        fputc ('\n', asm_out_file);
+        break;
+      case ate_kind_label:
+        dw2_asm_output_addr (DWARF2_ADDR_SIZE, entry->addr.label,
+                                 "0x%x", entry->index);
+        break;
+      default:
+        gcc_unreachable ();
+    }
+  return 1;
+}
+
+/* Produce the .debug_addr section.  */
+
+static void
+output_addr_table (void)
+{
+  unsigned int index = 0;
+  if (addr_index_table == NULL || htab_size (addr_index_table) == 0)
+    return;
+
+  switch_to_section (debug_addr_section);
+  htab_traverse_noresize (addr_index_table, output_addr_table_entry, &index);
+}
+
 #if ENABLE_ASSERT_CHECKING
 /* Verify that all marks are clear.  */
 
@@ -21571,6 +22439,16 @@ resolve_addr_in_expr (dw_loc_descr_ref loc)
        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:
+        {
+          if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
+               || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
+              && resolve_one_addr (&loc->dw_loc_oprnd1.val_entry->addr.rtl,
+                                   NULL))
+            return false;
+        }
+       break;
       case DW_OP_const4u:
       case DW_OP_const8u:
        if (loc->dtprel
@@ -21705,11 +22583,15 @@ resolve_addr (dw_die_ref die)
                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
@@ -21723,6 +22605,8 @@ resolve_addr (dw_die_ref die)
            else
              {
                loc->replaced = 1;
+                if (dwarf_split_debug_info)
+                  remove_loc_list_addr_table_entries (loc->expr);
                loc->dw_loc_next = *start;
              }
          }
@@ -21747,6 +22631,8 @@ resolve_addr (dw_die_ref die)
               || 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--;
            }
@@ -21758,6 +22644,8 @@ resolve_addr (dw_die_ref die)
        if (a->dw_attr == DW_AT_const_value
            && resolve_one_addr (&a->dw_attr_val.v.val_addr, NULL))
          {
+            if (AT_index (a) != NOT_INDEXED)
+              remove_addr_table_entry (a->dw_attr_val.val_entry);
            remove_AT (die, a->dw_attr);
            ix--;
          }
@@ -21781,6 +22669,8 @@ resolve_addr (dw_die_ref die)
              }
            else
              {
+                if (AT_index (a) != NOT_INDEXED)
+                  remove_addr_table_entry (a->dw_attr_val.val_entry);
                remove_AT (die, a->dw_attr);
                ix--;
              }
@@ -21914,6 +22804,17 @@ hash_loc_operands (dw_loc_descr_ref loc, hashval_t hash)
        }
       hash = iterative_hash_rtx (val1->v.val_addr, hash);
       break;
+    case DW_OP_GNU_addr_index:
+    case DW_OP_GNU_const_index:
+      {
+        if (loc->dtprel)
+          {
+            unsigned char dtprel = 0xd1;
+            hash = iterative_hash_object (dtprel, hash);
+          }
+        hash = iterative_hash_rtx (val1->val_entry->addr.rtl, hash);
+      }
+      break;
     case DW_OP_GNU_implicit_pointer:
       hash = iterative_hash_object (val2->v.val_int, hash);
       break;
@@ -22095,9 +22996,12 @@ compare_loc_operands (dw_loc_descr_ref x, dw_loc_descr_ref y)
       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
@@ -22128,6 +23032,13 @@ compare_loc_operands (dw_loc_descr_ref x, dw_loc_descr_ref y)
     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:
+      {
+        rtx ax1 = valx1->val_entry->addr.rtl;
+        rtx ay1 = valy1->val_entry->addr.rtl;
+        return rtx_equal_p (ax1, ay1);
+      }
     case DW_OP_GNU_implicit_pointer:
       return valx1->val_class == dw_val_class_die_ref
             && valx1->val_class == valy1->val_class
@@ -22241,12 +23152,45 @@ optimize_location_lists_1 (dw_die_ref die, htab_t htab)
        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));
 }
 
+
+/* 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)
+          {
+            /* Don't index an entry that has already been indexed
+               or won't be output.  */
+            if (curr->begin_entry != NULL
+                || (strcmp (curr->begin, curr->end) == 0 && !curr->force))
+              continue;
+
+            curr->begin_entry
+                = add_addr_table_entry (xstrdup (curr->begin),
+                                        ate_kind_label);
+          }
+      }
+
+  FOR_EACH_CHILD (die, c, index_location_lists (c));
+}
+
 /* Optimize location lists referenced from DIE
    children and share them whenever possible.  */
 
@@ -22268,6 +23212,7 @@ dwarf2out_finish (const char *filename)
   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.  */
@@ -22420,6 +23365,14 @@ dwarf2out_finish (const char *filename)
   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);
@@ -22436,8 +23389,8 @@ dwarf2out_finish (const char *filename)
     {
       /* 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
     {
@@ -22446,22 +23399,24 @@ dwarf2out_finish (const char *filename)
       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)
@@ -22471,16 +23426,16 @@ dwarf2out_finish (const char *filename)
             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)
@@ -22488,9 +23443,28 @@ dwarf2out_finish (const char *filename)
                   dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
                   macinfo_section_label);
 
+  if (dwarf_split_debug_info && addr_index_table != NULL)
+    {
+      /* optimize_location_lists calculates the size of the lists,
+         so index them first, and assign indices to the entries.
+         Although optimize_location_lists will remove entries from
+         the table, it only does so for duplicates, and therefore
+         only reduces ref_counts to 1.  */
+      unsigned int index = 0;
+      index_location_lists (comp_unit_die ());
+      htab_traverse_noresize (addr_index_table,
+                              index_addr_table_entry, &index);
+    }
   if (have_location_lists)
     optimize_location_lists (comp_unit_die ());
 
+  save_macinfo_strings ();
+  if (dwarf_split_debug_info)
+    {
+      unsigned int index = 0;
+      htab_traverse_noresize (debug_str_hash, index_string, &index);
+    }
+
   /* Output all of the compilation units.  We put the main one last so that
      the offsets are available to output_pubnames.  */
   for (node = limbo_die_list; node; node = node->next)
@@ -22510,19 +23484,58 @@ dwarf2out_finish (const char *filename)
          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);
+
+      switch_to_section (debug_addr_section);
+      ASM_OUTPUT_LABEL (asm_out_file, debug_addr_section_label);
+      output_addr_table ();
+    }
 
   /* 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)
     {
@@ -22536,8 +23549,6 @@ dwarf2out_finish (const char *filename)
     {
       /* 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 ());
     }
@@ -22588,12 +23599,18 @@ dwarf2out_finish (const char *filename)
   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 (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);
+    }
 
-  /* If we emitted any DW_FORM_strp form attribute, output the string
-     table too.  */
+  /* If we emitted any indirect strings, output the string table too.  */
   if (debug_str_hash)
-    htab_traverse (debug_str_hash, output_indirect_string, NULL);
+    output_indirect_strings ();
 }
 
 #include "gt-dwarf2out.h"
index 853dce40dedaad1fe98c60d0616094c6084bb179..7fa625811776d7363c5665a1a9f990a3c2cb6f81 100644 (file)
@@ -167,11 +167,14 @@ typedef struct GTY(()) dw_vec_struct {
 }
 dw_vec_const;
 
+struct addr_table_entry_struct;
+
 /* The dw_val_node describes an attribute's value, as it is
    represented internally.  */
 
 typedef struct GTY(()) dw_val_struct {
   enum dw_val_class val_class;
+  struct addr_table_entry_struct * GTY(()) val_entry;
   union dw_val_struct_union
     {
       rtx GTY ((tag ("dw_val_class_addr"))) val_addr;
index bbca6d84e33a0e904bbf78ab314849fa13d6bbec..a795981f00298eed4a0ee8e99d7a84dd7e3ff951 100644 (file)
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -267,6 +267,7 @@ static const char *compare_debug_dump_opt_spec_function (int, const char **);
 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 **);
 \f
 /* The Specs Language
 
@@ -447,7 +448,7 @@ ignored.  White space may also appear anywhere on the left side of the
 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 @@ proper position among the other output files.  */
 /* 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 @@ static const struct spec_function static_spec_functions[] =
   { "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
@@ -5814,7 +5823,7 @@ process_brace_body (const char *p, const char *atom, const char *end_atom,
    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.  */
 
@@ -5851,7 +5860,7 @@ check_live_switch (int switchnum, int prefix_length)
          }
       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.  */
@@ -8380,3 +8389,33 @@ pass_through_libs_spec_func (int argc, const char **argv)
     }
   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 3160b8cc521ed6e89c42b79f6160535b108f1f96..34c5698ba30e9eb92e2315d74d7d37704fd0ae3d 100644 (file)
@@ -829,6 +829,9 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
     maybe_set_param_value (PARAM_MAX_STORES_TO_SINK, 0,
                            opts->x_param_values, opts_set->x_param_values);
 
+  /* The -gsplit-dwarf option requires -gpubnames.  */
+  if (opts->x_dwarf_split_debug_info)
+    opts->x_debug_generate_pub_sections = 1;
 }
 
 #define LEFT_COLUMN    27
@@ -1705,6 +1708,11 @@ common_handle_option (struct gcc_options *opts,
       set_debug_level (DWARF2_DEBUG, false, "", opts, opts_set, loc);
       break;
 
+    case OPT_gsplit_dwarf:
+      set_debug_level (NO_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 e5d3e87b920833de9c4a0b8d3b2cb1c9c3e2243b..afac33a7aef7c32adcebcdf34f28524426b5aa70 100644 (file)
@@ -1,3 +1,11 @@
+2012-10-29  Sterling Augustine <saugustine@google.com>
+            Cary Coutant <ccoutant@google.com>
+
+       * dwarf2.h (dwarf_location_list_entry_type): New enum with fields
+       DW_LLE_GNU_end_of_list_entry, DW_LLE_GNU_base_address_selection_entry,
+       DW_LLE_GNU_start_end_entry and DW_LLE_GNU_start_length_entry.
+
+
 2012-10-08  Jason Merrill  <jason@redhat.com>
 
        * demangle.h (enum demangle_component_type): Add
index 2c1aeb6b912d969c6baaa913884644987704c0db..e98505ac2f928756b5486704c9f25846dbe9b7b3 100644 (file)
@@ -259,6 +259,17 @@ enum dwarf_line_number_hp_sfc_ops
     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