From: Lewis Hyatt Date: Sat, 11 Jul 2026 18:21:12 +0000 (-0400) Subject: lto: Overhaul approach to location streaming [PR65536] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5ca252314bf097bfec299ea8564edc4885c1bd6f;p=thirdparty%2Fgcc.git lto: Overhaul approach to location streaming [PR65536] PR65536 is about location-related issues that arise when the LTO front end reads one or several large object files and runs out of location_t space to encode all the locations. The main reason for the potential problem is that the libcpp linemap is designed to be used for incrementally reading source files (and their included header files) in the natural order. When it is used in another way, such as by LTO, which adds locations in the order in which it happened to read different entities, then the assumptions that justify its design are no longer applicable, and it is not hard to run out of location_t values (e.g., because of a large number of file changes, or lines being added out of order.) The PR remains open because it is not theoretically resolved, but it has been resolved for all practical purposes by the following two improvements: 1) The lto_location_cache class now tries hard to optimize the number of maps that it creates, especially by sorting the locations before adding them. This approach only goes so far, because it can only work at the LTO section level, so each function is handled independently of the others, but it helps a lot with reducing the number of maps required for a single function. 2) We moved to 64-bit location_t, which means everything is a lot more forgiving of wasting location_t space. Point 2) made this not much of an issue in practice, although sufficiently large files (especially with very long lines) could still trigger a problem in theory. While locations are now by and large working fine in LTO, there was an interesting discussion on PR65536, starting around: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65536#c8 about how the location streaming might be optimized by streaming the linemap structure itself, rather than a (file name, line number, column number) triplet for each location. In the end, this approach was not taken because solution 1) above was adequate and less disruptive. But the arguments presented in favor of streaming the linemap directly are still interesting. That discussion did not touch on another topic, namely the need to make `#pragma GCC diagnostic' usable in LTO. Right now, it does not work, because the diagnostic pragmas are not streamed out. But even if they were streamed out, the existing approach to locations in the LTO front end is not compatible with enforcing the pragmas. Diagnostic pragmas require a global ordering on location_t values, so that the machinery in option-classifier.cc can determine which pragmas are in force at each location. This requires remembering the order in which each source line was encountered, and that is exactly what is encoded in the linemap. This cannot be reconstructed just from source file names and line numbers; consider, for example, that the same file might be used in different translation units or multiple times in the same translation unit with different diagnostic pragmas in place each time. These considerations tip the scales in favor of adding linemap streaming as Manuel proposed on the PR. This patch prepares to support `#pragma GCC diagnostic' in the LTO front end by changing the approach to location streaming along these lines. The new approach has these general characteristics: o There is a new LTO section (LTO_section_linemap) that contains the information needed to reconstruct the linemap. There is an entry for each line_map_ordinary object that was used by at least one streamed-out location. When the LTO front end reads one of these map entries, it adds a new map with the corresponding properties to its own linemap using the new line_map_add_raw_map interface in libcpp. o When a location needs to be output, we stream out two integers: one to identify which linemap contains the location, and one containing the offset from the start of that map to the location. o When the language front ends stream out their data, they produce a single linemap section (labeled as linemap.0) that applies to the whole translation unit. When WPA prepares partitioned files for LTRANS, it may copy function bodies into the LTRANS files without reading them, so it needs also to copy the linemap sections they refer to. Since the same linemap section will often be needed by more than one partition, this is done by putting all of the linemap sections into one additional object file, which is provided as input to each LTRANS process via the new option -fltrans-linemap-file. o The naming of the LTRANS linemap sections is stable so as not to inhibit incremental LTO. o Once the reader has processed the linemap sections, there is no further overhead associated with inputting a location, unless it is an adhoc location for the purpose of associating a discriminator or a tree with the location. For the adhoc location case, the existing lto_location_cache setup is still useful to avoid creating unneeded adhoc locations, so this has been left in place as before. The restriction that there be only one lto_location_cache at a time is no longer strictly necessary; but it is still useful so that the IPA passes can access the currently active one simply, so I have not changed this for now. With this new setup, PR65536 can be closed. The subsequent patches in this series will enable support for diagnostic pragmas. gcc/lto/ChangeLog: PR lto/65536 * lang.opt: Add -fltrans-linemap-file. * lto-common.cc (lto_read_in_decl_state): Read the linemap ID from function sections. (linemap_section_id): New function. (create_subid_section_table): Note how many linemap sections were found in each input section. (loc_map_decl_data): New GC root. (lto_file_read): Make ORDER into a static variable so it counts continuously across all files and sub-files provided on the command line. (lto_file_finalize): Handle reading the new LTRANS linemap file. (read_cgraph_and_symbols): Likewise. Also stop freeing ALL_FILE_DECL_DATA at the end; the ordered list of files is now useful later on for lto_copy_linemaps(). * lto.cc (stream_out_linemaps): New function. (lto_wpa_write_files): Stream out the linemaps for LTRANS to use. gcc/testsuite/ChangeLog: PR lto/65536 * gcc.misc-tests/outputs.exp: Adjust LTO -save-temps tests to expect the new linemap file. gcc/ChangeLog: PR lto/65536 * doc/lto.texi: Document the new LTO_section_linemap and the new option -fltrans-linemap-file. * lto-opts.cc (lto_write_options): Handle the new option. * opts.cc (gen_command_line_string): Likewise. * lto-section-in.cc (lto_section_name[]): Add new name for LTO_section_linemap. * lto-streamer-in.cc (get_location_from_idx): New function. (lto_location_cache::cmp_loc): Remove. (bp_unpack_delta): New function. (create_loc_map): New function. (get_loc_map): New function. (lto_location_cache::override_loc_map): New function. (lto_location_cache::apply_location_cache): Pervasive changes to implement new location streaming format. (lto_location_cache::input_location_and_block): Likewise. (lto_location_cache::input_location): Rename argument LOC -> DEST for clarity. (lto_read_body_or_constructor): Handle LINEMAP_ID in the decl state. (lto_data_in_create): Add NEED_LOCATION_CACHE argument. Rename local variable DATA_IN to D to avoid clash with the type name. * lto-streamer-out.cc (clear_line_info): Update for change to class output_block. (compute_map_hash): New function. (class location_output): New class. (location_output::record_location): New function. (bp_pack_delta): New function. (location_output::produce_linemap_section): New function. (lto_output_location_1): Pervasive changes to implement new location streaming format. (copy_function_or_variable): Set the LINEMAP_ID in the decl state. (copy_linemap_section): New function. (copy_linemap_sections): New function. (lto_copy_linemaps): New function. (lto_register_linemap_for_output): New function. (lto_output_decl_state_refs): Output the LINEMAP_ID for function decls. (lto_out_decl_state_written_size): Adapt for new LINEMAP_ID output. (produce_asm_for_decls): Output the linemap section when needed. * lto-streamer.cc (lto_get_section_name): Handle linemap sections, which need an order suffix. * lto-streamer.h (enum lto_section_type): Fix typo in the comment. Add LTO_section_linemap. (struct lto_loc_map): New struct. (class lto_location_cache): Pervasive changes to implement new location streaming format. (struct lto_in_decl_state): Add LINEMAP_ID member. (struct lto_out_decl_state): Likewise. (struct lto_file_decl_data): Add LOC_MAP_DECL_DATA, LOC_MAPS, and NUM_LINEMAP_SECTIONS members. (lto_linemap_output_id): New function. (struct output_block): Adjust members for new streaming format. (data_in::data_in): New function. (lto_data_in_create): Adjust prototype for new NEED_LOCATION_CACHE argument. (lto_register_linemap_for_output): Declare. (lto_copy_linemaps): Declare. * lto-wrapper.cc (run_gcc): Pass new argument -fltrans-linemap-file. * timevar.def (TV_IPA_LTO_LINEMAP_IN): New timevar. (TV_IPA_LTO_LINEMAP_OUT): New timevar. (TV_IPA_LTO_LINEMAP_COPY): New timevar. --- diff --git a/gcc/doc/lto.texi b/gcc/doc/lto.texi index ed3180f38c0..3f3b326b497 100644 --- a/gcc/doc/lto.texi +++ b/gcc/doc/lto.texi @@ -258,6 +258,15 @@ hooks: one for writing the summary and another for reading it in. The format of these sections is entirely up to each individual pass. The only requirement is that the writer and reader hooks agree on the format. + +@item Linemap sections (@code{.gnu.lto_.linemap.}) + +These sections contain data necessary to reconstruct the linemap +structure, which provides information about the source location for +each entity in the LTO stream. A typical LTO object contains just one +linemap section (@code{linemap.0}), but WPA will create an object +containing more than one linemap section if it has copied function +bodies from more than one translation unit into a single partition. @end itemize @@ -575,6 +584,13 @@ local-transformation (LTRANS) mode, which reads in output from a previous run of the LTO in WPA mode. In the LTRANS mode, LTO optimizes an object and produces the final assembly. +@opindex fltrans-linemap-file +@item -fltrans-linemap-file=@var{file} +This option specifies an object file (output during the WPA phase) +containing additional linemap sections to be used during LTRANS. It +is used internally by WPA to avoid copying the same linemap data into +multiple LTRANS partitions. + @opindex fltrans-output-list @item -fltrans-output-list=@var{file} This option specifies a file to which the names of LTRANS output diff --git a/gcc/lto-opts.cc b/gcc/lto-opts.cc index a71c8a1bbd4..ce3e79d8452 100644 --- a/gcc/lto-opts.cc +++ b/gcc/lto-opts.cc @@ -154,6 +154,7 @@ lto_write_options (void) case OPT_fcanon_prefix_map: case OPT_fwhole_program: case OPT_fltrans_output_list_: + case OPT_fltrans_linemap_file_: case OPT_flto_incremental_: case OPT_flto_incremental_cache_size_: continue; diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc index c57f84f02bd..04fafae0273 100644 --- a/gcc/lto-section-in.cc +++ b/gcc/lto-section-in.cc @@ -57,6 +57,7 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] = "ipa_sra", "odr_types", "ipa_modref", + "linemap", }; /* Hooks so that the ipa passes can call into the lto front end to get diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc index 92f83ebbbb0..7906260af19 100644 --- a/gcc/lto-streamer-in.cc +++ b/gcc/lto-streamer-in.cc @@ -376,113 +376,49 @@ canon_file_name (const char *relative_prefix, const char *string) } } -/* Pointer to currently alive instance of lto_location_cache. */ - -lto_location_cache *lto_location_cache::current_cache; +/* Given a map index and offset from the streamed data, convert it to a + location_t in the global line map. */ -/* Sort locations in source order. Start with file from last application. */ - -int -lto_location_cache::cmp_loc (const void *pa, const void *pb) +static location_t +get_location_from_idx (size_t map_idx, location_t offset, + const lto_loc_map *loc_map) { - const cached_location *a = ((const cached_location *)pa); - const cached_location *b = ((const cached_location *)pb); - const char *current_file = current_cache->current_file; - int current_line = current_cache->current_line; - - if (a->file == current_file && b->file != current_file) - return -1; - if (a->file != current_file && b->file == current_file) - return 1; - if (a->file == current_file && b->file == current_file) - { - if (a->line == current_line && b->line != current_line) - return -1; - if (a->line != current_line && b->line == current_line) - return 1; - } - if (a->file != b->file) - return strcmp (a->file, b->file); - if (a->sysp != b->sysp) - return a->sysp ? 1 : -1; - if (a->line != b->line) - return a->line - b->line; - if (a->col != b->col) - return a->col - b->col; - if (a->discr != b->discr) - return a->discr - b->discr; - if ((a->block == NULL_TREE) != (b->block == NULL_TREE)) - return a->block ? 1 : -1; - if (a->block) - { - if (BLOCK_NUMBER (a->block) < BLOCK_NUMBER (b->block)) - return -1; - if (BLOCK_NUMBER (a->block) > BLOCK_NUMBER (b->block)) - return 1; - } - return 0; + if (!map_idx) + return offset; + gcc_checking_assert (loc_map); + --map_idx; + gcc_assert (map_idx < loc_map->nmaps); + const auto map = LINEMAPS_ORDINARY_MAP_AT (line_table, loc_map->map[map_idx]); + const location_t res = map->start_location + offset; + gcc_checking_assert (res < (map == LINEMAPS_LAST_ORDINARY_MAP (line_table) + ? line_table->highest_location + 1 + : map[1].start_location)); + return res; } +/* Pointer to currently alive instance of lto_location_cache. */ + +lto_location_cache *lto_location_cache::current_cache; + /* Apply all changes in location cache. Add locations into linemap and patch trees. */ bool lto_location_cache::apply_location_cache () { - static const char *prev_file; - if (!loc_cache.length ()) - return false; - if (loc_cache.length () > 1) - loc_cache.qsort (cmp_loc); + if (loc_cache.is_empty ()) + return true; - for (unsigned int i = 0; i < loc_cache.length (); i++) + for (const auto &cloc: loc_cache) { - struct cached_location loc = loc_cache[i]; - - if (current_file != loc.file) - linemap_add (line_table, prev_file ? LC_RENAME : LC_ENTER, - loc.sysp, loc.file, loc.line); - else if (current_line != loc.line) - { - int max = loc.col; - - for (unsigned int j = i + 1; j < loc_cache.length (); j++) - if (loc.file != loc_cache[j].file - || loc.line != loc_cache[j].line) - break; - else if (max < loc_cache[j].col) - max = loc_cache[j].col; - linemap_line_start (line_table, loc.line, max + 1); - } - gcc_assert (*loc.loc == BUILTINS_LOCATION + 1); - if (current_file != loc.file - || current_line != loc.line - || current_col != loc.col) - { - current_loc = linemap_position_for_column (line_table, loc.col); - if (loc.block) - current_loc = set_block (current_loc, loc.block); - if (loc.discr) - current_loc = location_with_discriminator (current_loc, loc.discr); - } - else if (current_block != loc.block) - { - if (loc.block) - current_loc = set_block (current_loc, loc.block); - else - current_loc = LOCATION_LOCUS (current_loc); - if (loc.discr) - current_loc = location_with_discriminator (current_loc, loc.discr); - } - else if (current_discr != loc.discr) - current_loc = location_with_discriminator (current_loc, loc.discr); - *loc.loc = current_loc; - current_line = loc.line; - prev_file = current_file = loc.file; - current_col = loc.col; - current_block = loc.block; - current_discr = loc.discr; + const location_t loc = cloc.loc; + const auto range = source_range::from_location (loc); + *cloc.dest = line_table->get_or_create_combined_loc (loc, range, + cloc.block, + cloc.discr); } + current_cloc = loc_cache[loc_cache.length () - 1]; + current_loc = *current_cloc.dest; loc_cache.truncate (0); accepted_length = 0; return true; @@ -505,125 +441,215 @@ lto_location_cache::revert_location_cache () loc_cache.truncate (accepted_length); } -/* Read a location bitpack from bit pack BP and either update *LOC directly - or add it to the location cache. If IB is non-NULL, stream in a block - afterwards. - It is necessary to call apply_location_cache to get *LOC updated. */ - -void -lto_location_cache::input_location_and_block (location_t *loc, - struct bitpack_d *bp, - class lto_input_block *ib, - class data_in *data_in) +/* Bitpack packing is more efficient for small values since every 4th bit is a + continuation bit, so it helps to pack values as the delta from the previous + if they can get large. */ +template +static Int +bp_unpack_delta (bitpack_d *bp, Int &prev) { - static const char *stream_file; - static int stream_line; - static int stream_col; - static bool stream_sysp; - static tree stream_block; - static unsigned stream_discr; - static const char *stream_relative_path_prefix; + const bool decrease = bp_unpack_value (bp, 1); + const Int delta = bp_unpack_var_len_unsigned (bp); + return prev = (decrease ? prev - delta : prev + delta); +} - gcc_assert (current_cache == this); +/* Read the linemap section of order LINEMAP_ID from FILE_DATA and create an + lto_loc_map object that can be used to find which ordinary map in the + global line_table corresponds to a given streamed-out map index. Always + either returns a non-NULL pointer, or calls fatal_error(). */ + +static lto_loc_map * +create_loc_map (lto_file_decl_data *file_data, unsigned linemap_id) +{ + timevar_push (TV_IPA_LTO_LINEMAP_IN); - *loc = bp_unpack_int_in_range (bp, "location", 0, - RESERVED_LOCATION_COUNT + 1); + size_t len; + const auto data = lto_get_section_data (file_data, LTO_section_linemap, + nullptr, linemap_id, &len, true); + if (!data) + fatal_error (UNKNOWN_LOCATION, + "linemap section #%u not found in file %qs", + linemap_id, file_data->file_name); + + const auto header = (const lto_simple_header_with_strings *) data; + const char *const main_begin = data + sizeof (*header); + const auto data_in + = lto_data_in_create (file_data, main_begin + header->main_size, + header->string_size, vNULL, false); + lto_input_block ib (main_begin, header->main_size, file_data); + bitpack_d bp = streamer_read_bitpack (&ib); - if (*loc < RESERVED_LOCATION_COUNT) + /* Create the location map object. */ + const size_t nmaps = bp_unpack_var_len_unsigned (&bp); + size_t loc_map_sz = sizeof (lto_loc_map); + if (nmaps > 1) + loc_map_sz += (nmaps - 1) * sizeof (size_t); + const auto loc_map = (lto_loc_map *) ggc_internal_alloc (loc_map_sz); + loc_map->nmaps = nmaps; + + /* Read the line map data. */ + const char *stream_relative_path_prefix = nullptr; + const char *stream_file = nullptr; + size_t cur_map_idx = 0; + linenum_type cur_line = 0; + for (size_t i = 0; i != nmaps; ++i) { - if (ib) + const size_t map_idx = bp_unpack_delta (&bp, cur_map_idx); + gcc_assert (map_idx && map_idx <= nmaps); + const line_map_uint_t num_lines = bp_unpack_var_len_unsigned (&bp); + const bool sysp = bp_unpack_value (&bp, 1); + const unsigned int column_bits = bp_unpack_value (&bp, 8); + const linenum_type to_line = bp_unpack_delta (&bp, cur_line); + const bool file_change = bp_unpack_value (&bp, 1); + if (file_change) { - bool block_change = bp_unpack_value (bp, 1); - if (block_change) - stream_block = stream_read_tree (ib, data_in); - if (stream_block) - *loc = set_block (*loc, stream_block); + const bool pwd_change = bp_unpack_value (&bp, 1); + if (pwd_change) + { + const char *pwd = bp_unpack_string (data_in, &bp); + const char *src_pwd = get_src_pwd (); + stream_relative_path_prefix + = strcmp (pwd, src_pwd) == 0 ? nullptr + : canon_relative_path_prefix (pwd, src_pwd); + } + stream_file = canon_file_name (stream_relative_path_prefix, + bp_unpack_string (data_in, &bp)); + } + gcc_assert (stream_file); + const auto map + = linemap_add_raw_map (line_table, + line_table->depth ? LC_RENAME : LC_ENTER, + line_table->highest_location + 1, sysp, + column_bits, 0, stream_file, to_line, num_lines); + if (!map) + fatal_error (UNKNOWN_LOCATION, + "Ran out of locations while reading linemap section #%u", + linemap_id); + loc_map->map[map_idx - 1] = map - line_table->info_ordinary.maps; + if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO) + { + const auto out_id = lto_linemap_output_id (linemap_id, file_data); + lto_register_linemap_for_output (map_idx, out_id); } - return; } - bool file_change = (*loc == RESERVED_LOCATION_COUNT + 1); - /* Keep value RESERVED_LOCATION_COUNT in *loc as linemap lookups will - ICE on it. */ - *loc = RESERVED_LOCATION_COUNT; - bool line_change = bp_unpack_value (bp, 1); - bool column_change = bp_unpack_value (bp, 1); - bool discr_change = bp_unpack_value (bp, 1); + lto_data_in_delete (data_in); + lto_free_section_data (file_data, LTO_section_linemap, nullptr, + data, len, true); + + timevar_pop (TV_IPA_LTO_LINEMAP_IN); + return loc_map; +} + +/* Return the location map with given ID, reading it from the LTO object if + necessary. */ + +static const lto_loc_map * +get_loc_map (lto_file_decl_data *file_data, unsigned linemap_id) +{ + /* During LTRANS, the location maps are stored in a different object. */ + while (file_data->loc_map_decl_data) + file_data = file_data->loc_map_decl_data; + + if (vec_safe_length (file_data->loc_maps) <= linemap_id) + vec_safe_grow_cleared (file_data->loc_maps, linemap_id + 1); + auto &res = (*file_data->loc_maps)[linemap_id]; + if (!res) + res = create_loc_map (file_data, linemap_id); + return res; +} + + +/* Read a location bitpack from bit pack BP and either update *LOC directly or + add it to the location cache. It is necessary to call apply_location_cache + to guarantee that *LOC has been updated. */ + +void +lto_location_cache::input_location_and_block (location_t *dest, + struct bitpack_d *bp, + class lto_input_block *ib, + class data_in *data_in) +{ + gcc_checking_assert (decl_data); - if (file_change) + location_t loc; + unsigned this_discr; + const bool is_reserved = bp_unpack_value (bp, 1); + if (is_reserved) { - bool pwd_change = bp_unpack_value (bp, 1); - if (pwd_change) + loc = bp_unpack_int_in_range (bp, "loc", 0, RESERVED_LOCATION_COUNT - 1); + this_discr = 0; + } + else + { + const bool linemap_change = bp_unpack_value (bp, 1); + if (linemap_change) { - const char *pwd = bp_unpack_string (data_in, bp); - const char *src_pwd = get_src_pwd (); - if (strcmp (pwd, src_pwd) == 0) - stream_relative_path_prefix = NULL; - else - stream_relative_path_prefix - = canon_relative_path_prefix (pwd, src_pwd); + const unsigned linemap_id = bp_unpack_var_len_unsigned (bp); + cur_loc_map = get_loc_map (decl_data, linemap_id + linemap_offset); } - stream_file = canon_file_name (stream_relative_path_prefix, - bp_unpack_string (data_in, bp)); - stream_sysp = bp_unpack_value (bp, 1); + const auto prev_idx = stream_map_idx; + bp_unpack_delta (bp, stream_map_idx); + bp_unpack_delta (bp, stream_loc_offset); + if (stream_map_idx != prev_idx) + /* The hash code is only there for the benefit of -flto-incremental; + we don't need to do anything with it here. */ + bp_unpack_var_len_unsigned (bp); + + loc = get_location_from_idx (stream_map_idx, stream_loc_offset, + cur_loc_map); + if (bp_unpack_value (bp, 1)) + stream_discr = bp_unpack_var_len_unsigned (bp); + this_discr = stream_discr; } - if (line_change) - stream_line = bp_unpack_var_len_unsigned (bp); - - if (column_change) - stream_col = bp_unpack_var_len_unsigned (bp); - - if (discr_change) - stream_discr = bp_unpack_var_len_unsigned (bp); - - tree block = NULL_TREE; + tree this_block = NULL_TREE; if (ib) { - bool block_change = bp_unpack_value (bp, 1); - if (block_change) + if (bp_unpack_value (bp, 1)) stream_block = stream_read_tree (ib, data_in); - block = stream_block; + this_block = stream_block; + } + + /* If this location can be ordinary rather than ad-hoc, then there is no more + overhead associated with it, so we may as well finalize it now. */ + if (!(this_discr || this_block)) + { + *dest = loc; + return; } /* This optimization saves location cache operations during gimple streaming. */ - - if (current_file == stream_file - && current_line == stream_line - && current_col == stream_col - && current_sysp == stream_sysp - && current_discr == stream_discr) + if (loc == current_cloc.loc + && this_block == current_cloc.block + && this_discr == current_cloc.discr) { - if (current_block == block) - *loc = current_loc; - else if (block) - *loc = set_block (current_loc, block); - else - *loc = LOCATION_LOCUS (current_loc); + *dest = current_loc; return; } - struct cached_location entry - = {stream_file, loc, stream_line, stream_col, stream_sysp, block, stream_discr}; + /* Keep value RESERVED_LOCATION_COUNT in *dest so linemap lookups will ICE on + it if we erroneously try to use the location before it is ready. */ + *dest = RESERVED_LOCATION_COUNT; + + /* This location needs to be ad-hoc; remember the details for now and create + the ad-hoc location later once we know we need it. */ + cached_location entry = {}; + entry.dest = dest; + entry.loc = loc; + entry.block = this_block; + entry.discr = this_discr; loc_cache.safe_push (entry); } -/* Read a location bitpack from bit pack BP and either update *LOC directly - or add it to the location cache. - It is necessary to call apply_location_cache to get *LOC updated. */ - void -lto_location_cache::input_location (location_t *loc, struct bitpack_d *bp, +lto_location_cache::input_location (location_t *dest, struct bitpack_d *bp, class data_in *data_in) { - return input_location_and_block (loc, bp, NULL, data_in); + return input_location_and_block (dest, bp, NULL, data_in); } -/* Read a location bitpack from input block IB and either update *LOC directly - or add it to the location cache. - It is necessary to call apply_location_cache to get *LOC updated. */ - void lto_input_location (location_t *loc, struct bitpack_d *bp, class data_in *data_in) @@ -1634,7 +1660,7 @@ lto_read_body_or_constructor (struct lto_file_decl_data *file_data, struct symta decl_state = lto_get_function_in_decl_state (file_data, fn_decl); gcc_assert (decl_state); file_data->current_decl_state = decl_state; - + data_in->location_cache.set_linemap_offset (decl_state->linemap_id); /* Set up the struct function. */ from = data_in->reader_cache->nodes.length (); @@ -2201,15 +2227,16 @@ lto_free_file_name_hash (void) class data_in * lto_data_in_create (struct lto_file_decl_data *file_data, const char *strings, unsigned len, - vec resolutions) + vec resolutions, + bool need_location_cache) { - class data_in *data_in = new (class data_in); - data_in->file_data = file_data; - data_in->strings = strings; - data_in->strings_len = len; - data_in->globals_resolution = resolutions; - data_in->reader_cache = streamer_tree_cache_create (false, false, true); - return data_in; + const auto d = new data_in{need_location_cache ? file_data : nullptr}; + d->file_data = file_data; + d->strings = strings; + d->strings_len = len; + d->globals_resolution = resolutions; + d->reader_cache = streamer_tree_cache_create (false, false, true); + return d; } diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc index 87f9612b3d7..a88c1ee94f3 100644 --- a/gcc/lto-streamer-out.cc +++ b/gcc/lto-streamer-out.cc @@ -57,17 +57,14 @@ static void lto_write_tree (struct output_block*, tree, bool); static void clear_line_info (struct output_block *ob) { - ob->current_file = NULL; - ob->current_line = 0; - ob->current_col = 0; - ob->current_sysp = false; - ob->reset_locus = true; - ob->emit_pwd = true; /* Initialize to something that will never appear as block, so that the first location with block in a function etc. always streams a change_block bit and the first block. */ ob->current_block = void_node; ob->current_discr = UINT_MAX; + ob->current_map_idx = 0; + ob->current_loc_offset = 0; + ob->current_linemap_id = -1U; } @@ -181,86 +178,258 @@ tree_is_indexable (tree t) return (IS_TYPE_OR_DECL_P (t) || TREE_CODE (t) == SSA_NAME); } +namespace { -/* Output info about new location into bitpack BP. - After outputting bitpack, lto_output_location_data has - to be done to output actual data. */ +/* Compute a 32-bit hash of the linemap details. This is currently used to + ensure that WPA streamed output will change whenever the linemap details + change, otherwise there could be potential issues with the validity of + the cache used to implement -flto-incremental. */ -static void -lto_output_location_1 (struct output_block *ob, struct bitpack_d *bp, - location_t orig_loc, bool block_p) +hashval_t +compute_map_hash (const line_map_ordinary *map) +{ + inchash::hash h; + h.add_int (map->sysp); + h.add_int (map->m_column_and_range_bits - map->m_range_bits); + h.add_int (map->to_line); + h.add_object (map->included_from); + h.add (map->to_file, strlen (map->to_file)); + return h.end (); +} + +class location_output { - location_t loc = LOCATION_LOCUS (orig_loc); +public: - if (loc >= RESERVED_LOCATION_COUNT) + struct map_id_t + { + size_t idx; + unsigned linemap_id; + hashval_t hash; + }; + + struct location_id_t + { + map_id_t map_id; + location_t offset; + }; + + location_id_t record_location (location_t loc); + + void register_map_id (size_t map_idx, unsigned linemap_id) + { + gcc_checking_assert (LINEMAPS_ORDINARY_USED (line_table) + == orig_map_ids.length () + 1); + const auto map = LINEMAPS_LAST_ORDINARY_MAP (line_table); + const hashval_t hash = compute_map_hash (map); + orig_map_ids.safe_push (map_id_t{map_idx, linemap_id, hash}); + } + + void produce_linemap_section (); + +private: + struct map_data + { + size_t idx; + hashval_t hash; + location_t highest_location; + }; + hash_map, map_data> map_data_map; + vec orig_map_ids = {}; +}; + +location_output::location_id_t +location_output::record_location (location_t loc) +{ + /* Strip away any macro expansion data or embedded range information. */ + loc = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT, + nullptr); + loc = get_pure_location (loc); + location_id_t loc_id = {}; + if (loc < RESERVED_LOCATION_COUNT) { - expanded_location xloc = expand_location (loc); - unsigned discr = get_discriminator_from_loc (orig_loc); + /* IDX 0 is for reserved locations. */ + loc_id.offset = loc; + return loc_id; + } - if (ob->reset_locus) - { - if (xloc.file == NULL) - ob->current_file = ""; - if (xloc.line == 0) - ob->current_line = 1; - if (xloc.column == 0) - ob->current_col = 1; - ob->reset_locus = false; - } + const auto map = linemap_check_ordinary (linemap_lookup (line_table, loc)); - /* As RESERVED_LOCATION_COUNT is 2, we can use the spare value of - 3 without wasting additional bits to signalize file change. - If RESERVED_LOCATION_COUNT changes, reconsider this. */ - gcc_checking_assert (RESERVED_LOCATION_COUNT == 2); - bp_pack_int_in_range (bp, 0, RESERVED_LOCATION_COUNT + 1, - RESERVED_LOCATION_COUNT - + (ob->current_file != xloc.file)); + if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO) + { + /* In these modes, we don't create new locations, we only work with what + was read, so we will re-output the locations in the same way. */ + gcc_checking_assert (orig_map_ids.length () + == LINEMAPS_ORDINARY_USED (line_table)); + gcc_checking_assert (!map->m_range_bits); + loc_id.map_id = orig_map_ids[map - line_table->info_ordinary.maps]; + loc_id.offset = loc - map->start_location; + return loc_id; + } - bp_pack_value (bp, ob->current_line != xloc.line, 1); - bp_pack_value (bp, ob->current_col != xloc.column, 1); - bp_pack_value (bp, ob->current_discr != discr, 1); + /* Represent each location_t as the offset from the map start, + without any range bits. */ + map_data &md = map_data_map.get_or_insert (map); + if (!md.idx) + { + md.idx = map_data_map.elements (); + md.hash = compute_map_hash (map); + } + md.highest_location = MAX (md.highest_location, loc); + loc_id.map_id.idx = md.idx; + loc_id.map_id.hash = md.hash; + loc_id.offset = (loc - map->start_location) >> map->m_range_bits; + return loc_id; +} - if (ob->current_file != xloc.file) +/* Bitpack packing is more efficient for small values since every 4th bit is a + continuation bit, so it helps to pack values as the delta from the previous + if they can get large. */ +template +static void +bp_pack_delta (bitpack_d *bp, Int val, Int &prev) +{ + const bool decrease = (val < prev); + bp_pack_value (bp, decrease, 1); + bp_pack_var_len_unsigned (bp, decrease ? prev - val : val - prev); + prev = val; +} + +void +location_output::produce_linemap_section () +{ + timevar_push (TV_IPA_LTO_LINEMAP_OUT); + + const auto ob = create_output_block (LTO_section_linemap); + auto bp = bitpack_create (ob->main_stream); + + /* Sort the maps in the order they need to be inserted later. */ + const size_t nmaps = map_data_map.elements (); + using KV = std::pair; + const std::unique_ptr sorted_maps{new KV[nmaps]}; + size_t map_i = 0; + for (auto iter = map_data_map.begin (), end = map_data_map.end (); + iter != end; ++iter) + sorted_maps[map_i++] = *iter; + gcc_qsort (sorted_maps.get (), nmaps, sizeof (KV), + [] (const void *p1, const void *p2) + { + const auto map1 = static_cast (p1)->first; + const auto map2 = static_cast (p2)->first; + const location_t loc1 = map1->start_location; + const location_t loc2 = map2->start_location; + return (loc2 < loc1) - (loc1 < loc2); + }); + + /* Output the maps. */ + bp_pack_var_len_unsigned (&bp, nmaps); + const char *current_file = nullptr; + bool emit_pwd = true; + size_t prev_idx = 0; + linenum_type prev_line = 0; + for (map_i = 0; map_i != nmaps; ++map_i) + { + const auto map = sorted_maps[map_i].first; + const map_data &md = sorted_maps[map_i].second; + bp_pack_delta (&bp, md.idx, prev_idx); + const auto num_lines + = 1 + ((md.highest_location - map->start_location) + >> map->m_column_and_range_bits); + bp_pack_var_len_unsigned (&bp, num_lines); + bp_pack_value (&bp, map->sysp != 0, 1); + bp_pack_value (&bp, map->m_column_and_range_bits - map->m_range_bits, 8); + bp_pack_delta (&bp, map->to_line, prev_line); + const bool file_change = (map->to_file != current_file); + bp_pack_value (&bp, file_change, 1); + if (file_change) { bool stream_pwd = false; - const char *remapped = remap_debug_filename (xloc.file); - if (ob->emit_pwd && remapped && !IS_ABSOLUTE_PATH (remapped)) + const char *remapped = remap_debug_filename (map->to_file); + if (emit_pwd && remapped && !IS_ABSOLUTE_PATH (remapped)) { stream_pwd = true; - ob->emit_pwd = false; + emit_pwd = false; } - bp_pack_value (bp, stream_pwd, 1); + bp_pack_value (&bp, stream_pwd, 1); if (stream_pwd) - bp_pack_string (ob, bp, get_src_pwd (), true); - bp_pack_string (ob, bp, remapped, true); - bp_pack_value (bp, xloc.sysp, 1); + bp_pack_string (ob, &bp, get_src_pwd (), true); + bp_pack_string (ob, &bp, remapped, true); } - ob->current_file = xloc.file; - ob->current_sysp = xloc.sysp; + } - if (ob->current_line != xloc.line) - bp_pack_var_len_unsigned (bp, xloc.line); - ob->current_line = xloc.line; + /* Finalize the section. */ + streamer_write_bitpack (&bp); + { + const auto section_name + = lto_get_section_name (LTO_section_linemap, nullptr, 0, nullptr); + lto_begin_section (section_name, true); + free (section_name); + } - if (ob->current_col != xloc.column) - bp_pack_var_len_unsigned (bp, xloc.column); - ob->current_col = xloc.column; + lto_simple_header_with_strings header = {}; + header.main_size = ob->main_stream->total_size; + header.string_size = ob->string_stream->total_size; + lto_write_data (&header, sizeof header); + lto_write_stream (ob->main_stream); + lto_write_stream (ob->string_stream); + lto_end_section (); + destroy_output_block (ob); - if (ob->current_discr != discr) - bp_pack_var_len_unsigned (bp, discr); - ob->current_discr = discr; + timevar_pop (TV_IPA_LTO_LINEMAP_OUT); +} + +location_output loc_output; + +} /* unnamed namespace */ + +/* Get a mapping index for LOC and stream it along with optional ancillary + data. */ + +static void +lto_output_location_1 (struct output_block *ob, struct bitpack_d *bp, + location_t loc, bool block_p) +{ + const auto loc_id = loc_output.record_location (loc); + if (!loc_id.map_id.idx) + { + bp_pack_value (bp, true, 1); + bp_pack_int_in_range (bp, 0, RESERVED_LOCATION_COUNT - 1, loc_id.offset); } else - bp_pack_int_in_range (bp, 0, RESERVED_LOCATION_COUNT + 1, loc); + { + bp_pack_value (bp, false, 1); + if (loc_id.map_id.linemap_id == ob->current_linemap_id) + bp_pack_value (bp, false, 1); + else + { + bp_pack_value (bp, true, 1); + bp_pack_var_len_unsigned (bp, loc_id.map_id.linemap_id); + ob->current_linemap_id = loc_id.map_id.linemap_id; + } + const bool is_new_map = (loc_id.map_id.idx != ob->current_map_idx); + bp_pack_delta (bp, loc_id.map_id.idx, ob->current_map_idx); + bp_pack_delta (bp, loc_id.offset, ob->current_loc_offset); + if (is_new_map) + bp_pack_var_len_unsigned (bp, loc_id.map_id.hash); + const unsigned discr = get_discriminator_from_loc (loc); + bp_pack_value (bp, ob->current_discr != discr, 1); + if (ob->current_discr != discr) + { + bp_pack_var_len_unsigned (bp, discr); + ob->current_discr = discr; + } + } if (block_p) { - tree block = LOCATION_BLOCK (orig_loc); + tree block = LOCATION_BLOCK (loc); bp_pack_value (bp, ob->current_block != block, 1); streamer_write_bitpack (bp); if (ob->current_block != block) - lto_output_tree (ob, block, true, true); - ob->current_block = block; + { + lto_output_tree (ob, block, true, true); + ob->current_block = block; + } } } @@ -2664,6 +2833,10 @@ copy_function_or_variable (struct symtab_node *node, int output_order) lto_free_raw_section_data (file_data, LTO_section_function_body, name, data, len); lto_end_section (); + + /* Make sure the reader knows which linemap section to use. */ + out_state->linemap_id = lto_linemap_output_id (in_state->linemap_id, + file_data); } /* Wrap symbol references in *TP inside a type-preserving MEM_REF. */ @@ -2738,6 +2911,64 @@ produce_lto_section () destroy_output_block (ob); } +/* If we have copied a function, we need to make sure the linemap it refers to + is also streamed out. */ + +static void +copy_linemap_section (lto_file_decl_data *file_data, unsigned order) +{ + const auto in_section_name + = lto_get_section_name (LTO_section_linemap, nullptr, order, file_data); + const auto out_section_name + = lto_get_section_name (LTO_section_linemap, nullptr, + lto_linemap_output_id (order, file_data), nullptr); + + if (streamer_dump_file) + fprintf (streamer_dump_file, "Copying linemap section from %s to %s\n", + in_section_name, out_section_name); + + size_t len; + const auto data = lto_get_raw_section_data (file_data, LTO_section_linemap, + nullptr, order, &len); + gcc_assert (data); + lto_begin_section (out_section_name, false); + free (in_section_name); + free (out_section_name); + lto_write_raw_data (data, len); + lto_free_raw_section_data (file_data, LTO_section_linemap, + nullptr, data, len); + lto_end_section (); +} + +static void +copy_linemap_sections (lto_file_decl_data *file_data) +{ + for (unsigned i = 0; i != file_data->num_linemap_sections; ++i) + copy_linemap_section (file_data, i); +} + +/* Copy all the linemaps we read into an object file so LTRANS can find them + later. */ + +void +lto_copy_linemaps () +{ + timevar_push (TV_IPA_LTO_LINEMAP_COPY); + lto_streamer_init (); + lto_push_out_decl_state (nullptr); + + /* In WPA, we write these out to a standalone file; give it an LTO header + section. */ + if (flag_wpa) + produce_lto_section (); + + for (auto file_data = lto_get_file_decl_data (); *file_data; ++file_data) + copy_linemap_sections (*file_data); + + lto_pop_out_decl_state (); + timevar_pop (TV_IPA_LTO_LINEMAP_COPY); +} + /* Compare symbols to get them sorted by filename (to optimize streaming) */ static int @@ -2806,6 +3037,22 @@ create_order_remap (lto_symtab_encoder_t encoder) } } +/* When WPA writes out its outputs, there are two different ways a location + may be streamed out: either streamed freshly, e.g. when trees are + streamed out in the decls section, or else copied verbatim from the input + files. In the former case, we need to remember how a location_t was + originally referenced in the input data, so we can stream it out the same + way; otherwise it would be necessary to stream out a new linemap section + just for WPA-generated locations, which would a) be wasteful given that + all locations originated from one of the linemap sections that already + exists and b) inhibit incremental LTO since a change to one partition + would affect the linemap for all of them. */ + +void lto_register_linemap_for_output (size_t map_idx, unsigned linemap_id) +{ + loc_output.register_map_id (map_idx, linemap_id); +} + /* Main entry point from the pass manager. */ void @@ -3013,17 +3260,21 @@ lto_output_decl_state_refs (struct output_block *ob, struct lto_out_decl_state *state) { unsigned i; - unsigned ref; + uint32_t ref, lm_order; tree decl; /* Write reference to FUNCTION_DECL. If there is not function, write reference to void_type_node. */ decl = (state->fn_decl) ? state->fn_decl : void_type_node; streamer_tree_cache_lookup (ob->writer_cache, decl, &ref); - gcc_assert (ref != (unsigned)-1); + gcc_assert (ref != (uint32_t)-1); ref = ref * 2 + (state->compressed ? 1 : 0); lto_write_data (&ref, sizeof (uint32_t)); - + if (state->fn_decl) + { + lm_order = state->linemap_id; + lto_write_data (&lm_order, sizeof (lm_order)); + } for (i = 0; i < LTO_N_DECL_STREAMS; i++) write_global_references (ob, &state->streams[i]); } @@ -3038,6 +3289,8 @@ lto_out_decl_state_written_size (struct lto_out_decl_state *state) size_t size; size = sizeof (int32_t); /* fn_ref. */ + if (state->fn_decl) + size += sizeof (int32_t); /* linemap_id. */ for (i = 0; i < LTO_N_DECL_STREAMS; i++) { size += sizeof (int32_t); /* vector size. */ @@ -3487,4 +3740,16 @@ produce_asm_for_decls (void) destroy_output_block (ob); if (lto_stream_offload_p) lto_write_mode_table (); + + /* Copy or write the linemap section(s) as needed. For incremental LTO, we + copy them into the output file now. For WPA, we will copy them into a + dedicated file later. Otherwise, we create a new one from scratch and + output it now. */ + if (flag_incremental_link == INCREMENTAL_LINK_LTO) + lto_copy_linemaps (); + else if (!flag_wpa) + { + gcc_checking_assert (!in_lto_p); + loc_output.produce_linemap_section (); + } } diff --git a/gcc/lto-streamer.cc b/gcc/lto-streamer.cc index 28b8cfc788e..078832fed4d 100644 --- a/gcc/lto-streamer.cc +++ b/gcc/lto-streamer.cc @@ -94,10 +94,10 @@ lto_tag_name (enum LTO_tags tag) } -/* Get a section name for a particular type or name. The NAME field - is only used if SECTION_TYPE is LTO_section_function_body. For all - others it is ignored. The callee of this function is responsible - to free the returned name. */ +/* Get a section name for a particular type or name. The NAME and NODE_ORDER + fields are only used if SECTION_TYPE is LTO_section_function_body or + LTO_section_linemap. The callee of this function is responsible for freeing + the returned name. */ char * lto_get_section_name (int section_type, const char *name, @@ -105,7 +105,7 @@ lto_get_section_name (int section_type, const char *name, { const char *add; char post[32]; - const char *sep; + const char *sep = "."; char *buffer = NULL; if (section_type == LTO_section_function_body) @@ -120,11 +120,16 @@ lto_get_section_name (int section_type, const char *name, add = buffer; sep = ""; } - else if (section_type < LTO_N_SECTION_TYPES) + else if (section_type == LTO_section_linemap) { - add = lto_section_name[section_type]; - sep = "."; + gcc_checking_assert (!name); + const auto section_name = lto_section_name[section_type]; + buffer = XNEWVEC (char, strlen (section_name) + 32); + sprintf (buffer, "%s.%d", section_name, node_order); + add = buffer; } + else if (section_type < LTO_N_SECTION_TYPES) + add = lto_section_name[section_type]; else internal_error ("bytecode stream: unexpected LTO section %s", name); diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h index b09869195fc..27126a42185 100644 --- a/gcc/lto-streamer.h +++ b/gcc/lto-streamer.h @@ -116,7 +116,7 @@ along with GCC; see the file COPYING3. If not see to terminate the statements and exception regions within this block. - 12) STRINGS + 13) STRINGS String are represented in the table as pairs, a length in ULEB128 form followed by the data for the string. */ @@ -225,6 +225,7 @@ enum lto_section_type LTO_section_ipa_sra, LTO_section_odr_types, LTO_section_ipa_modref, + LTO_section_linemap, LTO_N_SECTION_TYPES /* Must be last. */ }; @@ -258,10 +259,18 @@ typedef void (lto_free_section_data_f) (struct lto_file_decl_data *, const char *, size_t); -/* The location cache holds expanded locations for streamed in trees. - This is done to reduce memory usage of libcpp linemap that strongly prefers - locations to be inserted in the source order. */ +/* Structure to map ordinary_map indices from streamed-in data to + the linemap they correspond to. */ +struct GTY(()) lto_loc_map +{ + size_t nmaps; + size_t map[1]; +}; +/* The location cache holds the ancillary data for streamed in trees. This is + done to reduce memory usage in the libcpp linemap; we don't need to create an + adhoc location and store the ancillary data if the tree ends up being + merged. */ class lto_location_cache { public: @@ -273,65 +282,67 @@ public: void accept_location_cache (); /* Tree merging did succeed; throw away recent changes. */ void revert_location_cache (); - void input_location (location_t *loc, struct bitpack_d *bp, + void input_location (location_t *dest, struct bitpack_d *bp, class data_in *data_in); - void input_location_and_block (location_t *loc, struct bitpack_d *bp, + void input_location_and_block (location_t *dest, struct bitpack_d *bp, class lto_input_block *ib, class data_in *data_in); - lto_location_cache () - : loc_cache (), accepted_length (0), current_file (NULL), current_line (0), - current_col (0), current_sysp (false), current_loc (UNKNOWN_LOCATION), - current_block (NULL_TREE) + void set_linemap_offset (unsigned offset) { linemap_offset = offset; } + + explicit lto_location_cache (lto_file_decl_data *loc_data) + : decl_data{loc_data} { - gcc_assert (!current_cache); - current_cache = this; + if (decl_data) + { + gcc_assert (!current_cache); + current_cache = this; + } } ~lto_location_cache () { - apply_location_cache (); - gcc_assert (current_cache == this); - current_cache = NULL; + if (decl_data) + { + apply_location_cache (); + gcc_assert (current_cache == this); + current_cache = NULL; + } } - /* There can be at most one instance of location cache (combining multiple - would bring it out of sync with libcpp linemap); point to current - one. */ + /* There can be at most one active instance of location_cache, so that IPA + passes can find it if needed; point to current one. */ static lto_location_cache *current_cache; private: - static int cmp_loc (const void *pa, const void *pb); - struct cached_location { - const char *file; - location_t *loc; - int line, col; - bool sysp; + location_t *dest; + location_t loc; tree block; unsigned discr; }; - /* The location cache. */ + /* Which linemap section we are using. */ + lto_file_decl_data *const decl_data; + const lto_loc_map *cur_loc_map = nullptr; + unsigned linemap_offset = 0; + /* The location cache. */ auto_vec loc_cache; - /* Accepted entries are ones used by trees that are known to be not unified - by tree merging. */ - - int accepted_length; + /* These keep track of most recently seen values as they are streamed in. */ + tree stream_block = NULL_TREE; + unsigned stream_discr = 0; + size_t stream_map_idx = 0; + location_t stream_loc_offset = 0; + unsigned stream_linemap_order = -1U; - /* Bookkeeping to remember state in between calls to lto_apply_location_cache - When streaming gimple, the location cache is not used and thus - lto_apply_location_cache happens per location basis. It is then - useful to avoid redundant calls of linemap API. */ + /* These keep track of most recently accepted values. */ + cached_location current_cloc = {}; + location_t current_loc = UNKNOWN_LOCATION; - const char *current_file; - int current_line; - int current_col; - bool current_sysp; - location_t current_loc; - tree current_block; - unsigned current_discr; + /* Accepted entries are ones used by trees that are known to be not unified + by tree merging. */ + unsigned accepted_length = 0; }; /* Structure used as buffer for reading an LTO file. */ @@ -507,6 +518,10 @@ struct GTY((for_user)) lto_in_decl_state /* True if decl state is compressed. */ bool compressed; + + /* Order of the linemap section to be used for interpreting the locations + streamed for this decl. */ + unsigned linemap_id; }; typedef struct lto_in_decl_state *lto_in_decl_state_ptr; @@ -546,6 +561,10 @@ struct lto_out_decl_state /* True if offload tables should be output. */ bool output_offload_tables_p; + + /* Order of the linemap section to be used for interpreting the locations + streamed for this decl. */ + unsigned linemap_id; }; typedef struct lto_out_decl_state *lto_out_decl_state_ptr; @@ -617,8 +636,25 @@ struct GTY(()) lto_file_decl_data int unit_base; unsigned mode_bits; + + /* Number of linemap sections contained in this file. There is typically only + 1, but WPA and INCREMENTAL_LINK_LTO modes may create files with more than + one linemap. */ + unsigned num_linemap_sections; + + /* Location maps for locations streamed in this file. */ + lto_file_decl_data *loc_map_decl_data; + vec *loc_maps; }; +/* Given an input linemap ID, compute how it will be known in the output, given + that the output could be the combination of multiple input files. */ +inline unsigned +lto_linemap_output_id (unsigned linemap_id, const lto_file_decl_data *file_data) +{ + return linemap_id + file_data->order; +} + typedef struct lto_file_decl_data *lto_file_decl_data_ptr; struct lto_char_ptr_base @@ -728,17 +764,13 @@ struct output_block if we are serializing something else. */ symtab_node *symbol; - /* These are the last file and line that were seen in the stream. - If the current node differs from these, it needs to insert - something into the stream and fix these up. */ - const char *current_file; - int current_line; - int current_col; - bool current_sysp; - bool reset_locus; - bool emit_pwd; + /* These record the most-recently-streamed values, to avoid streaming + them unnecessarily. */ tree current_block; unsigned current_discr; + size_t current_map_idx; + location_t current_loc_offset; + unsigned current_linemap_id; /* Cache of nodes written in this section. */ struct streamer_tree_cache_d *writer_cache; @@ -773,6 +805,10 @@ public: /* Cache of source code location. */ lto_location_cache location_cache; + + explicit data_in (lto_file_decl_data *data_for_loc) + : location_cache{data_for_loc} + {} }; @@ -869,8 +905,9 @@ extern void lto_input_constructors_and_inits (struct lto_file_decl_data *, extern void lto_input_toplevel_asms (struct lto_file_decl_data *, int); extern void lto_input_mode_table (struct lto_file_decl_data *); extern class data_in *lto_data_in_create (struct lto_file_decl_data *, - const char *, unsigned, - vec ); + const char *, unsigned, + vec, + bool need_location_cache = true); extern void lto_data_in_delete (class data_in *); extern void lto_input_data_block (class lto_input_block *, void *, size_t); void lto_input_location (location_t *, struct bitpack_d *, class data_in *); @@ -902,6 +939,8 @@ extern void lto_output_toplevel_asms (lto_symtab_encoder_t); extern void produce_asm (struct output_block *ob); extern void lto_output (); extern void produce_asm_for_decls (); +void lto_register_linemap_for_output (size_t, unsigned); +void lto_copy_linemaps (); void lto_output_decl_state_streams (struct output_block *, struct lto_out_decl_state *); void lto_output_decl_state_refs (struct output_block *, diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc index ca39f51e186..bd25185c218 100644 --- a/gcc/lto-wrapper.cc +++ b/gcc/lto-wrapper.cc @@ -1926,6 +1926,7 @@ cont1: for (i = 0; i < ltoobj_argc; ++i) obstack_ptr_grow (&argv_obstack, ltoobj_argv[i]); obstack_ptr_grow (&argv_obstack, NULL); + obstack_ptr_grow (&argv_obstack, NULL); new_argv = XOBFINISH (&argv_obstack, const char **); argv_ptr = &new_argv[new_head_argc]; @@ -1975,6 +1976,8 @@ cont1: /* Parse the list of LTRANS inputs from the WPA stage. */ obstack_init (&env_obstack); nr = 0; + const char *linemap_name = nullptr; + char *linemap_arg = nullptr; for (;;) { const unsigned piece = 32; @@ -2003,6 +2006,15 @@ cont: } input_name[len - 1] = '\0'; + if (!linemap_arg) + { + constexpr auto lf_opt = "-fltrans-linemap-file="; + linemap_arg = concat (lf_opt, input_name, nullptr); + free (input_name); + linemap_name = linemap_arg + strlen (lf_opt); + continue; + } + if (input_name[0] == '*') output_name = &input_name[1]; @@ -2133,7 +2145,8 @@ cont: argv_ptr[2] = "-o"; argv_ptr[3] = output_name; argv_ptr[4] = input_name; - argv_ptr[5] = NULL; + argv_ptr[5] = linemap_arg; + argv_ptr[6] = NULL; if (parallel) { fprintf (mstream, "%s:\n\t@%s ", output_name, new_argv[0]); @@ -2252,6 +2265,16 @@ cont: if (early_debug_object_names[i] != NULL) printf ("%s\n", early_debug_object_names[i]); } + + if (linemap_arg) + { + /* This should be done even if(ltrans_cache), since the linemap file + changes when any of the inputs changes and is not part of the + cache. */ + maybe_unlink (linemap_name); + free (linemap_arg); + } + nr = 0; free (ltrans_priorities); free (output_names); diff --git a/gcc/lto/lang.opt b/gcc/lto/lang.opt index 16a2415f8c4..bdced64c153 100644 --- a/gcc/lto/lang.opt +++ b/gcc/lto/lang.opt @@ -57,6 +57,10 @@ fltrans-output-list= LTO Joined Var(ltrans_output_list) Specify a file to which a list of files output by LTRANS is written. +fltrans-linemap-file= +LTO Joined Var(ltrans_linemap_file) +Specify the object file containing copied linemap sections; used internally during LTRANS phase. + fresolution= LTO Joined The resolution file. diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc index 16301516bd8..96bd97f1374 100644 --- a/gcc/lto/lto-common.cc +++ b/gcc/lto/lto-common.cc @@ -195,6 +195,9 @@ lto_read_in_decl_state (class data_in *data_in, const uint32_t *data, gcc_assert (decl == void_type_node); decl = NULL_TREE; } + else + state->linemap_id = *data++; + state->fn_decl = decl; for (i = 0; i < LTO_N_DECL_STREAMS; i++) @@ -2203,6 +2206,25 @@ lto_section_with_id (const char *name, unsigned HOST_WIDE_INT *id) return sscanf (s, "." HOST_WIDE_INT_PRINT_HEX_PURE, id) == 1; } +/* Return the linemap ID if NAME refers to an LTO linemap section, + otherwise -1U. */ + +static unsigned linemap_section_id (const char *name) +{ + const int prefix_len = strlen (section_name_prefix); + gcc_checking_assert (!strncmp (name, section_name_prefix, prefix_len)); + name += prefix_len; + if (*name++ != '.') + return -1U; + const auto lm_name = lto_section_name[LTO_section_linemap]; + const int lm_len = strlen (lm_name); + if (strncmp (name, lm_name, lm_len)) + return -1U; + name += lm_len; + unsigned linemap_id; + return sscanf (name, ".%u", &linemap_id) == 1 ? linemap_id : -1U; +} + /* Create file_data of each sub file id. */ static int @@ -2242,6 +2264,10 @@ create_subid_section_table (struct lto_section_slot *ls, splay_tree file_ids, list->last = file_data; } + const auto lsid = linemap_section_id (ls->name); + file_data->num_linemap_sections = MAX (file_data->num_linemap_sections, + lsid + 1); + /* Copy section into sub module hash table. */ new_name = XDUPVEC (char, ls->name, strlen (ls->name) + 1); s_slot.name = new_name; @@ -2254,6 +2280,10 @@ create_subid_section_table (struct lto_section_slot *ls, splay_tree file_ids, return 1; } +/* In LTRANS, the linemap data is all stored in a separate object, namely this + one. */ +static GTY(()) lto_file_decl_data *loc_map_decl_data; + /* Read declarations and other initializations for a FILE_DATA. */ static void @@ -2274,7 +2304,6 @@ lto_file_finalize (struct lto_file_decl_data *file_data, lto_file *file, resolutions[rp->index] = rp->res; file_data->respairs.release (); - file_data->renaming_hash_table = lto_create_renaming_table (); file_data->file_name = file->filename; file_data->order = order; @@ -2292,6 +2321,18 @@ lto_file_finalize (struct lto_file_decl_data *file_data, lto_file *file, file_data->lto_section_header.minor_version, file_data->file_name); + if (flag_ltrans) + { + if (!loc_map_decl_data) + /* This is not a real LTRANS file; it is just here to hold the line + maps. */ + return; + else + file_data->loc_map_decl_data = loc_map_decl_data; + } + + file_data->renaming_hash_table = lto_create_renaming_table (); + #ifdef ACCEL_COMPILER lto_input_mode_table (file_data); #else @@ -2369,10 +2410,13 @@ lto_file_read (lto_file *file, FILE *resolution_file, int *count) lto_resolution_read (file_ids, resolution_file, file); /* Finalize each lto file for each submodule in the merged object. */ - int order = 0; + static int order = 0; for (file_data = file_list.first; file_data != NULL; file_data = file_data->next) - lto_create_files_from_ids (file, file_data, count, order++); + { + lto_create_files_from_ids (file, file_data, count, order); + order += file_data->num_linemap_sections; + } splay_tree_delete (file_ids); htab_delete (section_hash_table); @@ -2812,6 +2856,31 @@ read_cgraph_and_symbols (unsigned nfiles, const char **fnames) if (!quiet_flag) fprintf (stderr, "Reading object files:"); + if (flag_ltrans) + { + if (!ltrans_linemap_file) + fatal_error (UNKNOWN_LOCATION, + "%<-fltrans-linemap-file%> is required with %<-fltrans%>"); + if (!quiet_flag) + { + fprintf (stderr, " %s", ltrans_linemap_file); + fflush (stderr); + } + current_lto_file = lto_obj_file_open (ltrans_linemap_file, false); + int lm_count = 0; + if (!(current_lto_file + && (loc_map_decl_data = lto_file_read (current_lto_file, resolution, + &lm_count)) + && lm_count == 1 + && loc_map_decl_data->next == nullptr)) + fatal_error (UNKNOWN_LOCATION, + "unexpected error reading LTRANS linemap section from %qs", + ltrans_linemap_file); + lto_obj_file_close (current_lto_file); + free (current_lto_file); + current_lto_file = nullptr; + } + /* Read all of the object files specified on the command line. */ for (i = 0, last_file_ix = 0; i < nfiles; ++i) { @@ -3030,8 +3099,9 @@ read_cgraph_and_symbols (unsigned nfiles, const char **fnames) /* Indicate that the cgraph is built and ready. */ symtab->function_flags_ready = true; - ggc_free (all_file_decl_data); - all_file_decl_data = NULL; + + /* N.B. No longer calling ggc_free (all_file_decl_data) here; it is helpful to + keep the ordered list of files available e.g. for lto_copy_linemaps(). */ } diff --git a/gcc/lto/lto.cc b/gcc/lto/lto.cc index 46f3799c4a0..76837c27be8 100644 --- a/gcc/lto/lto.cc +++ b/gcc/lto/lto.cc @@ -166,6 +166,22 @@ materialize_cgraph (void) timevar_pop (lto_timer); } +/* Stream out all the linemap sections so they are available for LTRANS. */ + +static void +stream_out_linemaps (const char *filename) +{ + const auto file = lto_obj_file_open (filename, true); + if (!file) + fatal_error (input_location, "% failed"); + lto_set_current_out_file (file); + lto_copy_linemaps (); + free (const_cast (file->filename)); + lto_set_current_out_file (nullptr); + lto_obj_file_close (file); + free (file); +} + /* Actually stream out ENCODER into TEMP_FILENAME. */ static void @@ -357,6 +373,12 @@ lto_wpa_write_files (void) sets_per_worker = (n_sets + lto_parallelism - 1) / lto_parallelism; } + /* Write out all the linemaps since they will be needed during LTRANS. */ + sprintf (temp_filename + blen, "%u.o", n_sets); + const auto linemap_filename = xstrdup (temp_filename); + stream_out_linemaps (linemap_filename); + + /* Write out the partitions. */ for (i = 0; i < n_sets; i++) { ltrans_partition part = ltrans_partitions[i]; @@ -428,6 +450,8 @@ lto_wpa_write_files (void) if (ltrans_output_list_stream == NULL) fatal_error (input_location, "opening LTRANS output list %s: %m", ltrans_output_list); + fprintf (ltrans_output_list_stream, "0\n%s\n", linemap_filename); + free (linemap_filename); for (i = 0; i < n_sets; i++) { unsigned int len = strlen (temp_filenames[i]); diff --git a/gcc/opts.cc b/gcc/opts.cc index 0d96fb6d78e..88d533c6641 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -3964,6 +3964,7 @@ gen_command_line_string (cl_decoded_option *options, case OPT_nostdinc__: case OPT_fpreprocessed: case OPT_fltrans_output_list_: + case OPT_fltrans_linemap_file_: case OPT_fresolution_: case OPT_fdebug_prefix_map_: case OPT_fmacro_prefix_map_: diff --git a/gcc/testsuite/gcc.misc-tests/outputs.exp b/gcc/testsuite/gcc.misc-tests/outputs.exp index 0561dd58b60..5a2d2f7fdbf 100644 --- a/gcc/testsuite/gcc.misc-tests/outputs.exp +++ b/gcc/testsuite/gcc.misc-tests/outputs.exp @@ -729,7 +729,7 @@ outest "$b-291 lto mult named-2" $mult "-o $b.exe -O2 -flto -fno-use-linker-plug outest "$b-292 lto sing nameddir-2" $sing "-o dir/$b.exe -O2 -flto -fno-use-linker-plugin -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--0.c.???i.icf --0.c.???r.final .wpa.???i.icf .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .exe} {}} outest "$b-293 lto mult nameddir-2" $mult "-o dir/$b.exe -O2 -flto -fno-use-linker-plugin -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--1.c.???i.icf --1.c.???r.final --2.c.???i.icf --2.c.???r.final .wpa.???i.icf .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .exe} {}} if !$skip_atsave { -outest "$b-294 lto sing unnamed-3" $sing "@$devnull -O2 -flto -fno-use-linker-plugin -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage -save-temps $oaout" {} {{a--0.c.???i.icf a--0.c.???r.final a.wpa.???i.icf a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a--0.o a--0.s a--0.i a.ltrans0.o a.ltrans.out a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.args.0 a.ltrans0.ltrans.s a.wpa.args.0 a.lto_args a.ld1_args a.ltrans_args a.ltrans0.ltrans.args.0 a.ld_args $aout}} +outest "$b-294 lto sing unnamed-3" $sing "@$devnull -O2 -flto -fno-use-linker-plugin -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage -save-temps $oaout" {} {{a--0.c.???i.icf a--0.c.???r.final a.wpa.???i.icf a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a--0.o a--0.s a--0.i a.ltrans0.o a.ltrans1.o a.ltrans.out a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.args.0 a.ltrans0.ltrans.s a.wpa.args.0 a.lto_args a.ld1_args a.ltrans_args a.ltrans0.ltrans.args.0 a.ld_args $aout}} } } @@ -796,20 +796,20 @@ outest "$b-335 lto sing empty dumpdir empty dumpbase namedb" $sing "-dumpdir \"\ outest "$b-336 lto mult empty dumpdir empty dumpbase namedb" $mult "-dumpdir \"\" -dumpbase \"\" -o dir/$b.exe -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{.exe} {-1.c.???i.icf !$ltop -1.c.???r.final !0 -2.c.???i.icf !$ltop -2.c.???r.final !0 .wpa.???i.icf .ltrans0.ltrans.???r.final .ltrans0.ltrans.su}} # Now -flto with -save-temps, not exhaustive. -outest "$b-337 lto st sing empty dumpbase unnamed" $sing "-dumpbase \"\" -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} -outest "$b-338 lto st mult empty dumpbase unnamed" $mult "-dumpbase \"\" -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} -outest "$b-339 lto st sing dumpdir empty dumpbase named" $sing "-dumpdir dir/ -dumpbase \"\" -o $b-0.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop -0.lto_wrapper_args !0 -0.wpa.???i.icf -0.ltrans.out -0.ltrans_args !!$ltop -0.res !0 -0.ltrans0.o -0.ltrans0.ltrans.???r.final -0.ltrans0.ltrans.su -0.ltrans0.ltrans.s -0.ltrans0.ltrans.o -0.ltrans0.ltrans_args -0.ltrans0.ltrans.args.0 -0.wpa.args.0} {-0.exe}} -outest "$b-340 lto st mult dumpdir empty dumpbase named" $mult "-dumpdir dir/ -dumpbase \"\" -o $b-1.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop -1.lto_wrapper_args !0 -1.wpa.???i.icf -1.ltrans.out -1.ltrans_args !!$ltop -1.res !0 -1.ltrans0.o -1.ltrans0.ltrans.???r.final -1.ltrans0.ltrans.su -1.ltrans0.ltrans.s -1.ltrans0.ltrans.o -1.ltrans0.ltrans_args -1.ltrans0.ltrans.args.0 -1.wpa.args.0} {-1.exe}} -outest "$b-341 lto st sing empty dumpbase namedb" $sing "-dumpbase \"\" -o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} -outest "$b-342 lto st mult empty dumpbase namedb" $mult "-dumpbase \"\" -o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} +outest "$b-337 lto st sing empty dumpbase unnamed" $sing "-dumpbase \"\" -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans1.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} +outest "$b-338 lto st mult empty dumpbase unnamed" $mult "-dumpbase \"\" -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans1.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} +outest "$b-339 lto st sing dumpdir empty dumpbase named" $sing "-dumpdir dir/ -dumpbase \"\" -o $b-0.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop -0.lto_wrapper_args !0 -0.wpa.???i.icf -0.ltrans.out -0.ltrans_args !!$ltop -0.res !0 -0.ltrans0.o -0.ltrans1.o -0.ltrans0.ltrans.???r.final -0.ltrans0.ltrans.su -0.ltrans0.ltrans.s -0.ltrans0.ltrans.o -0.ltrans0.ltrans_args -0.ltrans0.ltrans.args.0 -0.wpa.args.0} {-0.exe}} +outest "$b-340 lto st mult dumpdir empty dumpbase named" $mult "-dumpdir dir/ -dumpbase \"\" -o $b-1.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop -1.lto_wrapper_args !0 -1.wpa.???i.icf -1.ltrans.out -1.ltrans_args !!$ltop -1.res !0 -1.ltrans0.o -1.ltrans1.o -1.ltrans0.ltrans.???r.final -1.ltrans0.ltrans.su -1.ltrans0.ltrans.s -1.ltrans0.ltrans.o -1.ltrans0.ltrans_args -1.ltrans0.ltrans.args.0 -1.wpa.args.0} {-1.exe}} +outest "$b-341 lto st sing empty dumpbase namedb" $sing "-dumpbase \"\" -o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-0.i -0.s -0.o -0.c.???i.icf !$ltop -0.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans1.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} +outest "$b-342 lto st mult empty dumpbase namedb" $mult "-dumpbase \"\" -o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{-1.i -1.s -1.o -1.c.???i.icf !$ltop -1.c.???r.final !0 -2.i -2.s -2.o -2.c.???i.icf !$ltop -2.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans1.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} # lto save-temps without -dumpbase. -outest "$b-343 lto st sing unnamed" $sing "-save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{a--0.i a--0.s a--0.o a--0.c.???i.icf !$ltop a--0.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} -outest "$b-344 lto st mult unnamed" $mult "-save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{a--1.i a--1.s a--1.o a--1.c.???i.icf !$ltop a--1.c.???r.final !0 a--2.i a--2.s a--2.o a--2.c.???i.icf !$ltop a--2.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} -outest "$b-345 lto st sing dumpdir named" $sing "-dumpdir dir/$b- -o $b-0.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--0.i --0.s --0.o --0.c.???i.icf !$ltop --0.c.???r.final !!$ltop -lto_wrapper_args !0 -wpa.???i.icf -ltrans.out -ltrans_args !!$ltop -res !0 -ltrans0.o -ltrans0.ltrans.???r.final -ltrans0.ltrans.su -ltrans0.ltrans.s -ltrans0.ltrans.o -ltrans0.ltrans_args -ltrans0.ltrans.args.0 -wpa.args.0} {-0.exe}} -outest "$b-346 lto st mult dumpdir named" $mult "-dumpdir dir/$b- -o $b-1.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--1.i --1.s --1.o --1.c.???i.icf !$ltop --1.c.???r.final !0 --2.i --2.s --2.o --2.c.???i.icf !$ltop --2.c.???r.final !!$ltop -lto_wrapper_args !0 -wpa.???i.icf -ltrans.out -ltrans_args !!$ltop -res !0 -ltrans0.o -ltrans0.ltrans.???r.final -ltrans0.ltrans.su -ltrans0.ltrans.s -ltrans0.ltrans.o -ltrans0.ltrans_args -ltrans0.ltrans.args.0 -wpa.args.0} {-1.exe}} -outest "$b-347 lto st sing namedb" $sing "-o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--0.i --0.s --0.o --0.c.???i.icf !$ltop --0.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} -outest "$b-348 lto st mult namedb" $mult "-o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--1.i --1.s --1.o --1.c.???i.icf !$ltop --1.c.???r.final !0 --2.i --2.s --2.o --2.c.???i.icf !$ltop --2.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} +outest "$b-343 lto st sing unnamed" $sing "-save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{a--0.i a--0.s a--0.o a--0.c.???i.icf !$ltop a--0.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans1.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} +outest "$b-344 lto st mult unnamed" $mult "-save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage $oaout" {} {{a--1.i a--1.s a--1.o a--1.c.???i.icf !$ltop a--1.c.???r.final !0 a--2.i a--2.s a--2.o a--2.c.???i.icf !$ltop a--2.c.???r.final !!$ltop a.lto_wrapper_args !0 a.wpa.???i.icf a.ltrans.out a.ltrans_args !!$ltop a.res !0 a.ltrans0.o a.ltrans1.o a.ltrans0.ltrans.???r.final a.ltrans0.ltrans.su a.ltrans0.ltrans.s a.ltrans0.ltrans.o a.ltrans0.ltrans_args a.ltrans0.ltrans.args.0 a.wpa.args.0 $aout}} +outest "$b-345 lto st sing dumpdir named" $sing "-dumpdir dir/$b- -o $b-0.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--0.i --0.s --0.o --0.c.???i.icf !$ltop --0.c.???r.final !!$ltop -lto_wrapper_args !0 -wpa.???i.icf -ltrans.out -ltrans_args !!$ltop -res !0 -ltrans0.o -ltrans1.o -ltrans0.ltrans.???r.final -ltrans0.ltrans.su -ltrans0.ltrans.s -ltrans0.ltrans.o -ltrans0.ltrans_args -ltrans0.ltrans.args.0 -wpa.args.0} {-0.exe}} +outest "$b-346 lto st mult dumpdir named" $mult "-dumpdir dir/$b- -o $b-1.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--1.i --1.s --1.o --1.c.???i.icf !$ltop --1.c.???r.final !0 --2.i --2.s --2.o --2.c.???i.icf !$ltop --2.c.???r.final !!$ltop -lto_wrapper_args !0 -wpa.???i.icf -ltrans.out -ltrans_args !!$ltop -res !0 -ltrans0.o -ltrans1.o -ltrans0.ltrans.???r.final -ltrans0.ltrans.su -ltrans0.ltrans.s -ltrans0.ltrans.o -ltrans0.ltrans_args -ltrans0.ltrans.args.0 -wpa.args.0} {-1.exe}} +outest "$b-347 lto st sing namedb" $sing "-o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--0.i --0.s --0.o --0.c.???i.icf !$ltop --0.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans1.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} +outest "$b-348 lto st mult namedb" $mult "-o dir/$b.exe -save-temps -O2 -flto -flto-partition=one -fdump-ipa-icf-optimized -fdump-rtl-final -fstack-usage" {dir/} {{--1.i --1.s --1.o --1.c.???i.icf !$ltop --1.c.???r.final !0 --2.i --2.s --2.o --2.c.???i.icf !$ltop --2.c.???r.final !!$ltop .lto_wrapper_args !0 .wpa.???i.icf .ltrans.out .ltrans_args !!$ltop .res !0 .ltrans0.o .ltrans1.o .ltrans0.ltrans.???r.final .ltrans0.ltrans.su .ltrans0.ltrans.s .ltrans0.ltrans.o .ltrans0.ltrans_args .ltrans0.ltrans.args.0 .wpa.args.0 .exe} {}} # !$skip_lto } diff --git a/gcc/timevar.def b/gcc/timevar.def index c7020997609..fc78600b652 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -94,6 +94,9 @@ DEFTIMEVAR (TV_IPA_LTO_CTORS_OUT , "ipa lto constructors out") DEFTIMEVAR (TV_IPA_LTO_CGRAPH_IO , "ipa lto cgraph I/O") DEFTIMEVAR (TV_IPA_LTO_DECL_MERGE , "ipa lto decl merge") DEFTIMEVAR (TV_IPA_LTO_CGRAPH_MERGE , "ipa lto cgraph merge") +DEFTIMEVAR (TV_IPA_LTO_LINEMAP_IN , "ipa lto linemap in") +DEFTIMEVAR (TV_IPA_LTO_LINEMAP_OUT , "ipa lto linemap out") +DEFTIMEVAR (TV_IPA_LTO_LINEMAP_COPY , "ipa lto linemap copy") DEFTIMEVAR (TV_LTO , "lto") DEFTIMEVAR (TV_WHOPR_WPA , "whopr wpa") DEFTIMEVAR (TV_WHOPR_WPA_IO , "whopr wpa I/O")