ebedc9a341
This patch combines: [PATCH 05/10] Add ranges to libcpp tokens (via ad-hoc data, unoptimized) [PATCH 06/10] Track expression ranges in C frontend [PATCH 07/10] Add plugin to recursively dump the source-ranges in a tree (v2) [PATCH 08/10] Wire things up so that libcpp users get token underlines [PATCH 09/10] Delay some resolution of ad-hoc locations, preserving ranges [PATCH 10/10] Compress short ranges into source_location [PATCH] libcpp: add examples to source_location description along with fixes for the nits identified during review. gcc/ChangeLog: * Makefile.in (OBJS): Add gcc-rich-location.o. * diagnostic.c (diagnostic_append_note): Pass line_table to rich_location ctor. (emit_diagnostic): Likewise. (inform): Likewise. (inform_n): Likewise. (warning): Likewise. (warning_at): Likewise. (warning_n): Likewise. (pedwarn): Likewise. (permerror): Likewise. (error): Likewise. (error_n): Likewise. (error_at): Likewise. (sorry): Likewise. (fatal_error): Likewise. (internal_error): Likewise. (internal_error_no_backtrace): Likewise. (source_range::debug): Likewise. * gcc-rich-location.c: New file. * gcc-rich-location.h: New file. * genmatch.c (fatal_at): Pass line_table to rich_location ctor. (warning_at): Likewise. * gimple.h (gimple_set_block): Use set_block function. * input.c (dump_line_table_statistics): Dump stats on how many ranges were optimized vs how many needed ad-hoc table. (write_digit_row): Add "map" param; use its range_bits to calculate the per-character offset. (dump_location_info): Print the range and column bits for each ordinary map. Use the range bits to calculate the per-character offset. Pass the map as a new param to the various calls to write_digit_row. Eliminate uses of ORDINARY_MAP_NUMBER_OF_COLUMN_BITS. * print-tree.c (print_node): Print any source range information. * rtl-error.c (diagnostic_for_asm): Likewise. * toplev.c (general_init): Initialize line_table's default_range_bits. * tree-cfg.c (move_block_to_fn): Likewise. (move_block_to_fn): Likewise. * tree-inline.c (copy_phis_for_bb): Likewise. * tree.c (tree_set_block): Likewise. (get_pure_location): New function. (set_source_range): New functions. (set_block): New function. (set_source_range): New functions. * tree.h (CAN_HAVE_RANGE_P): New. (EXPR_LOCATION_RANGE): New. (EXPR_HAS_RANGE): New. (get_expr_source_range): New inline function. (DECL_LOCATION_RANGE): New. (set_source_range): New decls. (get_decl_source_range): New inline function. gcc/ada/ChangeLog: * gcc-interface/trans.c (Sloc_to_locus): Add line_table param when calling linemap_position_for_line_and_column. gcc/c-family/ChangeLog: * c-common.c (c_fully_fold_internal): Capture existing souce_range, and store it on the result. * c-opts.c (c_common_init_options): Set global_dc->colorize_source_p. gcc/c/ChangeLog: * c-decl.c (warn_defaults_to): Pass line_table to rich_location ctor. * c-errors.c (pedwarn_c99): Likewise. (pedwarn_c90): Likewise. * c-parser.c (set_c_expr_source_range): New functions. (c_token::get_range): New method. (c_token::get_finish): New method. (c_parser_expr_no_commas): Call set_c_expr_source_range on the ret based on the range from the start of the LHS to the end of the RHS. (c_parser_conditional_expression): Likewise, based on the range from the start of the cond.value to the end of exp2.value. (c_parser_binary_expression): Call set_c_expr_source_range on the stack values for TRUTH_ANDIF_EXPR and TRUTH_ORIF_EXPR. (c_parser_cast_expression): Call set_c_expr_source_range on ret based on the cast_loc through to the end of the expr. (c_parser_unary_expression): Likewise, based on the op_loc through to the end of op. (c_parser_sizeof_expression) Likewise, based on the start of the sizeof token through to either the closing paren or the end of expr. (c_parser_postfix_expression): Likewise, using the token range, or from the open paren through to the close paren for parenthesized expressions. (c_parser_postfix_expression_after_primary): Likewise, for various kinds of expression. * c-tree.h (struct c_expr): Add field "src_range". (c_expr::get_start): New method. (c_expr::get_finish): New method. (set_c_expr_source_range): New decls. * c-typeck.c (parser_build_unary_op): Call set_c_expr_source_range on ret for prefix unary ops. (parser_build_binary_op): Likewise, running from the start of arg1.value through to the end of arg2.value. gcc/cp/ChangeLog: * error.c (pedwarn_cxx98): Pass line_table to rich_location ctor. gcc/fortran/ChangeLog: * error.c (gfc_warning): Pass line_table to rich_location ctor. (gfc_warning_now_at): Likewise. (gfc_warning_now): Likewise. (gfc_error_now): Likewise. (gfc_fatal_error): Likewise. (gfc_error): Likewise. (gfc_internal_error): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/diagnostic-token-ranges.c: New file. * gcc.dg/diagnostic-tree-expr-ranges-2.c: New file. * gcc.dg/plugin/diagnostic-test-expressions-1.c: New file. * gcc.dg/plugin/diagnostic-test-show-trees-1.c: New file. * gcc.dg/plugin/diagnostic_plugin_show_trees.c: New file. * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c (get_loc): Add line_table param when calling linemap_position_for_line_and_column. (test_show_locus): Pass line_table to rich_location ctors. (plugin_init): Remove setting of global_dc->colorize_source_p. * gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c: New file. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add diagnostic_plugin_test_tree_expression_range.c, diagnostic-test-expressions-1.c, diagnostic_plugin_show_trees.c, and diagnostic-test-show-trees-1.c. libcpp/ChangeLog: * errors.c (cpp_diagnostic): Pass pfile->line_table to rich_location ctor. (cpp_diagnostic_with_line): Likewise. * include/cpplib.h (struct cpp_token): Update comment for src_loc to indicate that the range of the token is "baked into" the source_location. * include/line-map.h (source_location): Update the descriptive comment to reflect the packing scheme for short ranges, adding worked examples of location encoding. (struct line_map_ordinary): Drop field "column_bits" in favor of field "m_column_and_range_bits"; add field "m_range_bits". (ORDINARY_MAP_NUMBER_OF_COLUMN_BITS): Delete. (location_adhoc_data): Add source_range field. (struct line_maps): Add fields "default_range_bits", "num_optimized_ranges" and "num_unoptimized_ranges". (get_combined_adhoc_loc): Add source_range param. (get_range_from_loc): New declaration. (pure_location_p): New prototype. (COMBINE_LOCATION_DATA): Add source_range param. (SOURCE_LINE): Update for renaming of column_bits. (SOURCE_COLUMN): Likewise. Shift the column right by the map's range_bits. (LAST_SOURCE_LINE_LOCATION): Update for renaming of column_bits. (linemap_position_for_line_and_column): Add line_maps * params. (rich_location::rich_location): Likewise. * lex.c (_cpp_lex_direct): Capture the range of the token, baking it into token->src_loc via a call to COMBINE_LOCATION_DATA. * line-map.c (LINE_MAP_MAX_COLUMN_NUMBER): Reduce from 1U << 17 to 1U << 12. (location_adhoc_data_hash): Add the src_range into the hash value. (location_adhoc_data_eq): Require equality of the src_range values. (can_be_stored_compactly_p): New function. (get_combined_adhoc_loc): Add src_range param, and store it, via a bit-packing scheme for short ranges, otherwise within the lookaside table. Remove the requirement that data is non-NULL. (get_range_from_adhoc_loc): New function. (get_range_from_loc): New function. (pure_location_p): New function. (linemap_add): Ensure that start_location has zero for the range_bits, unless we're past LINE_MAP_MAX_LOCATION_WITH_COLS. Initialize range_bits to zero. Assert that the start_location is "pure". (linemap_line_start): Assert that the column_and_range_bits >= range_bits. Update determinination of whether we need to start a new map using the effective column bits, without the range bits. Use the set's default_range_bits in new maps, apart from those with column_bits == 0, which should also have 0 range_bits. Increase the column bits for new maps by the range bits. When adding lines to an existing map, use set->highest_line directly rather than offsetting highest by SOURCE_COLUMN. Add assertions to sanity-check the return value. (linemap_position_for_column): Offset to_column by range_bits. Update set->highest_location if necessary. (linemap_position_for_line_and_column): Add line_maps * param. Update the calculation to offset the column by range_bits, and conditionalize it on being <= LINE_MAP_MAX_LOCATION_WITH_COLS. Bound it by LINEMAPS_MACRO_LOWEST_LOCATION. Update set->highest_location if necessary. (linemap_position_for_loc_and_offset): Handle ad-hoc locations; pass "set" to linemap_position_for_line_and_column. (linemap_macro_map_loc_unwind_toward_spelling): Add line_maps param. Handle ad-hoc locations. (linemap_location_in_system_header_p): Pass on "set" to call to linemap_macro_map_loc_unwind_toward_spelling. (linemap_macro_loc_to_spelling_point): Retain ad-hoc locations. Pass on "set" to call to linemap_macro_map_loc_unwind_toward_spelling. (linemap_resolve_location): Retain ad-hoc locations. Pass on "set" to call to linemap_macro_map_loc_unwind_toward_spelling. (linemap_unwind_toward_expansion): Pass on "set" to call to linemap_macro_map_loc_unwind_toward_spelling. (linemap_expand_location): Extract the data pointer before extracting the location. (rich_location::rich_location): Add line_maps param; use it to extract the range from the source_location. * location-example.txt: Regenerate, showing new representation. From-SVN: r230331
1129 lines
35 KiB
C
1129 lines
35 KiB
C
/* Data and functions related to line maps and input files.
|
|
Copyright (C) 2004-2015 Free Software Foundation, Inc.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "intl.h"
|
|
#include "diagnostic-core.h"
|
|
|
|
/* This is a cache used by get_next_line to store the content of a
|
|
file to be searched for file lines. */
|
|
struct fcache
|
|
{
|
|
/* These are information used to store a line boundary. */
|
|
struct line_info
|
|
{
|
|
/* The line number. It starts from 1. */
|
|
size_t line_num;
|
|
|
|
/* The position (byte count) of the beginning of the line,
|
|
relative to the file data pointer. This starts at zero. */
|
|
size_t start_pos;
|
|
|
|
/* The position (byte count) of the last byte of the line. This
|
|
normally points to the '\n' character, or to one byte after the
|
|
last byte of the file, if the file doesn't contain a '\n'
|
|
character. */
|
|
size_t end_pos;
|
|
|
|
line_info (size_t l, size_t s, size_t e)
|
|
: line_num (l), start_pos (s), end_pos (e)
|
|
{}
|
|
|
|
line_info ()
|
|
:line_num (0), start_pos (0), end_pos (0)
|
|
{}
|
|
};
|
|
|
|
/* The number of time this file has been accessed. This is used
|
|
to designate which file cache to evict from the cache
|
|
array. */
|
|
unsigned use_count;
|
|
|
|
const char *file_path;
|
|
|
|
FILE *fp;
|
|
|
|
/* This points to the content of the file that we've read so
|
|
far. */
|
|
char *data;
|
|
|
|
/* The size of the DATA array above.*/
|
|
size_t size;
|
|
|
|
/* The number of bytes read from the underlying file so far. This
|
|
must be less (or equal) than SIZE above. */
|
|
size_t nb_read;
|
|
|
|
/* The index of the beginning of the current line. */
|
|
size_t line_start_idx;
|
|
|
|
/* The number of the previous line read. This starts at 1. Zero
|
|
means we've read no line so far. */
|
|
size_t line_num;
|
|
|
|
/* This is the total number of lines of the current file. At the
|
|
moment, we try to get this information from the line map
|
|
subsystem. Note that this is just a hint. When using the C++
|
|
front-end, this hint is correct because the input file is then
|
|
completely tokenized before parsing starts; so the line map knows
|
|
the number of lines before compilation really starts. For e.g,
|
|
the C front-end, it can happen that we start emitting diagnostics
|
|
before the line map has seen the end of the file. */
|
|
size_t total_lines;
|
|
|
|
/* This is a record of the beginning and end of the lines we've seen
|
|
while reading the file. This is useful to avoid walking the data
|
|
from the beginning when we are asked to read a line that is
|
|
before LINE_START_IDX above. Note that the maximum size of this
|
|
record is fcache_line_record_size, so that the memory consumption
|
|
doesn't explode. We thus scale total_lines down to
|
|
fcache_line_record_size. */
|
|
vec<line_info, va_heap> line_record;
|
|
|
|
fcache ();
|
|
~fcache ();
|
|
};
|
|
|
|
/* Current position in real source file. */
|
|
|
|
location_t input_location = UNKNOWN_LOCATION;
|
|
|
|
struct line_maps *line_table;
|
|
|
|
static fcache *fcache_tab;
|
|
static const size_t fcache_tab_size = 16;
|
|
static const size_t fcache_buffer_size = 4 * 1024;
|
|
static const size_t fcache_line_record_size = 100;
|
|
|
|
/* Expand the source location LOC into a human readable location. If
|
|
LOC resolves to a builtin location, the file name of the readable
|
|
location is set to the string "<built-in>". If EXPANSION_POINT_P is
|
|
TRUE and LOC is virtual, then it is resolved to the expansion
|
|
point of the involved macro. Otherwise, it is resolved to the
|
|
spelling location of the token.
|
|
|
|
When resolving to the spelling location of the token, if the
|
|
resulting location is for a built-in location (that is, it has no
|
|
associated line/column) in the context of a macro expansion, the
|
|
returned location is the first one (while unwinding the macro
|
|
location towards its expansion point) that is in real source
|
|
code. */
|
|
|
|
static expanded_location
|
|
expand_location_1 (source_location loc,
|
|
bool expansion_point_p)
|
|
{
|
|
expanded_location xloc;
|
|
const line_map_ordinary *map;
|
|
enum location_resolution_kind lrk = LRK_MACRO_EXPANSION_POINT;
|
|
tree block = NULL;
|
|
|
|
if (IS_ADHOC_LOC (loc))
|
|
{
|
|
block = LOCATION_BLOCK (loc);
|
|
loc = LOCATION_LOCUS (loc);
|
|
}
|
|
|
|
memset (&xloc, 0, sizeof (xloc));
|
|
|
|
if (loc >= RESERVED_LOCATION_COUNT)
|
|
{
|
|
if (!expansion_point_p)
|
|
{
|
|
/* We want to resolve LOC to its spelling location.
|
|
|
|
But if that spelling location is a reserved location that
|
|
appears in the context of a macro expansion (like for a
|
|
location for a built-in token), let's consider the first
|
|
location (toward the expansion point) that is not reserved;
|
|
that is, the first location that is in real source code. */
|
|
loc = linemap_unwind_to_first_non_reserved_loc (line_table,
|
|
loc, NULL);
|
|
lrk = LRK_SPELLING_LOCATION;
|
|
}
|
|
loc = linemap_resolve_location (line_table, loc,
|
|
lrk, &map);
|
|
xloc = linemap_expand_location (line_table, map, loc);
|
|
}
|
|
|
|
xloc.data = block;
|
|
if (loc <= BUILTINS_LOCATION)
|
|
xloc.file = loc == UNKNOWN_LOCATION ? NULL : _("<built-in>");
|
|
|
|
return xloc;
|
|
}
|
|
|
|
/* Initialize the set of cache used for files accessed by caret
|
|
diagnostic. */
|
|
|
|
static void
|
|
diagnostic_file_cache_init (void)
|
|
{
|
|
if (fcache_tab == NULL)
|
|
fcache_tab = new fcache[fcache_tab_size];
|
|
}
|
|
|
|
/* Free the resources used by the set of cache used for files accessed
|
|
by caret diagnostic. */
|
|
|
|
void
|
|
diagnostic_file_cache_fini (void)
|
|
{
|
|
if (fcache_tab)
|
|
{
|
|
delete [] (fcache_tab);
|
|
fcache_tab = NULL;
|
|
}
|
|
}
|
|
|
|
/* Return the total lines number that have been read so far by the
|
|
line map (in the preprocessor) so far. For languages like C++ that
|
|
entirely preprocess the input file before starting to parse, this
|
|
equals the actual number of lines of the file. */
|
|
|
|
static size_t
|
|
total_lines_num (const char *file_path)
|
|
{
|
|
size_t r = 0;
|
|
source_location l = 0;
|
|
if (linemap_get_file_highest_location (line_table, file_path, &l))
|
|
{
|
|
gcc_assert (l >= RESERVED_LOCATION_COUNT);
|
|
expanded_location xloc = expand_location (l);
|
|
r = xloc.line;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/* Lookup the cache used for the content of a given file accessed by
|
|
caret diagnostic. Return the found cached file, or NULL if no
|
|
cached file was found. */
|
|
|
|
static fcache*
|
|
lookup_file_in_cache_tab (const char *file_path)
|
|
{
|
|
if (file_path == NULL)
|
|
return NULL;
|
|
|
|
diagnostic_file_cache_init ();
|
|
|
|
/* This will contain the found cached file. */
|
|
fcache *r = NULL;
|
|
for (unsigned i = 0; i < fcache_tab_size; ++i)
|
|
{
|
|
fcache *c = &fcache_tab[i];
|
|
if (c->file_path && !strcmp (c->file_path, file_path))
|
|
{
|
|
++c->use_count;
|
|
r = c;
|
|
}
|
|
}
|
|
|
|
if (r)
|
|
++r->use_count;
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Return the file cache that has been less used, recently, or the
|
|
first empty one. If HIGHEST_USE_COUNT is non-null,
|
|
*HIGHEST_USE_COUNT is set to the highest use count of the entries
|
|
in the cache table. */
|
|
|
|
static fcache*
|
|
evicted_cache_tab_entry (unsigned *highest_use_count)
|
|
{
|
|
diagnostic_file_cache_init ();
|
|
|
|
fcache *to_evict = &fcache_tab[0];
|
|
unsigned huc = to_evict->use_count;
|
|
for (unsigned i = 1; i < fcache_tab_size; ++i)
|
|
{
|
|
fcache *c = &fcache_tab[i];
|
|
bool c_is_empty = (c->file_path == NULL);
|
|
|
|
if (c->use_count < to_evict->use_count
|
|
|| (to_evict->file_path && c_is_empty))
|
|
/* We evict C because it's either an entry with a lower use
|
|
count or one that is empty. */
|
|
to_evict = c;
|
|
|
|
if (huc < c->use_count)
|
|
huc = c->use_count;
|
|
|
|
if (c_is_empty)
|
|
/* We've reached the end of the cache; subsequent elements are
|
|
all empty. */
|
|
break;
|
|
}
|
|
|
|
if (highest_use_count)
|
|
*highest_use_count = huc;
|
|
|
|
return to_evict;
|
|
}
|
|
|
|
/* Create the cache used for the content of a given file to be
|
|
accessed by caret diagnostic. This cache is added to an array of
|
|
cache and can be retrieved by lookup_file_in_cache_tab. This
|
|
function returns the created cache. Note that only the last
|
|
fcache_tab_size files are cached. */
|
|
|
|
static fcache*
|
|
add_file_to_cache_tab (const char *file_path)
|
|
{
|
|
|
|
FILE *fp = fopen (file_path, "r");
|
|
if (fp == NULL)
|
|
return NULL;
|
|
|
|
unsigned highest_use_count = 0;
|
|
fcache *r = evicted_cache_tab_entry (&highest_use_count);
|
|
r->file_path = file_path;
|
|
if (r->fp)
|
|
fclose (r->fp);
|
|
r->fp = fp;
|
|
r->nb_read = 0;
|
|
r->line_start_idx = 0;
|
|
r->line_num = 0;
|
|
r->line_record.truncate (0);
|
|
/* Ensure that this cache entry doesn't get evicted next time
|
|
add_file_to_cache_tab is called. */
|
|
r->use_count = ++highest_use_count;
|
|
r->total_lines = total_lines_num (file_path);
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Lookup the cache used for the content of a given file accessed by
|
|
caret diagnostic. If no cached file was found, create a new cache
|
|
for this file, add it to the array of cached file and return
|
|
it. */
|
|
|
|
static fcache*
|
|
lookup_or_add_file_to_cache_tab (const char *file_path)
|
|
{
|
|
fcache *r = lookup_file_in_cache_tab (file_path);
|
|
if (r == NULL)
|
|
r = add_file_to_cache_tab (file_path);
|
|
return r;
|
|
}
|
|
|
|
/* Default constructor for a cache of file used by caret
|
|
diagnostic. */
|
|
|
|
fcache::fcache ()
|
|
: use_count (0), file_path (NULL), fp (NULL), data (0),
|
|
size (0), nb_read (0), line_start_idx (0), line_num (0),
|
|
total_lines (0)
|
|
{
|
|
line_record.create (0);
|
|
}
|
|
|
|
/* Destructor for a cache of file used by caret diagnostic. */
|
|
|
|
fcache::~fcache ()
|
|
{
|
|
if (fp)
|
|
{
|
|
fclose (fp);
|
|
fp = NULL;
|
|
}
|
|
if (data)
|
|
{
|
|
XDELETEVEC (data);
|
|
data = 0;
|
|
}
|
|
line_record.release ();
|
|
}
|
|
|
|
/* Returns TRUE iff the cache would need to be filled with data coming
|
|
from the file. That is, either the cache is empty or full or the
|
|
current line is empty. Note that if the cache is full, it would
|
|
need to be extended and filled again. */
|
|
|
|
static bool
|
|
needs_read (fcache *c)
|
|
{
|
|
return (c->nb_read == 0
|
|
|| c->nb_read == c->size
|
|
|| (c->line_start_idx >= c->nb_read - 1));
|
|
}
|
|
|
|
/* Return TRUE iff the cache is full and thus needs to be
|
|
extended. */
|
|
|
|
static bool
|
|
needs_grow (fcache *c)
|
|
{
|
|
return c->nb_read == c->size;
|
|
}
|
|
|
|
/* Grow the cache if it needs to be extended. */
|
|
|
|
static void
|
|
maybe_grow (fcache *c)
|
|
{
|
|
if (!needs_grow (c))
|
|
return;
|
|
|
|
size_t size = c->size == 0 ? fcache_buffer_size : c->size * 2;
|
|
c->data = XRESIZEVEC (char, c->data, size + 1);
|
|
c->size = size;
|
|
}
|
|
|
|
/* Read more data into the cache. Extends the cache if need be.
|
|
Returns TRUE iff new data could be read. */
|
|
|
|
static bool
|
|
read_data (fcache *c)
|
|
{
|
|
if (feof (c->fp) || ferror (c->fp))
|
|
return false;
|
|
|
|
maybe_grow (c);
|
|
|
|
char * from = c->data + c->nb_read;
|
|
size_t to_read = c->size - c->nb_read;
|
|
size_t nb_read = fread (from, 1, to_read, c->fp);
|
|
|
|
if (ferror (c->fp))
|
|
return false;
|
|
|
|
c->nb_read += nb_read;
|
|
return !!nb_read;
|
|
}
|
|
|
|
/* Read new data iff the cache needs to be filled with more data
|
|
coming from the file FP. Return TRUE iff the cache was filled with
|
|
mode data. */
|
|
|
|
static bool
|
|
maybe_read_data (fcache *c)
|
|
{
|
|
if (!needs_read (c))
|
|
return false;
|
|
return read_data (c);
|
|
}
|
|
|
|
/* Read a new line from file FP, using C as a cache for the data
|
|
coming from the file. Upon successful completion, *LINE is set to
|
|
the beginning of the line found. Space for that line has been
|
|
allocated in the cache thus *LINE has the same life time as C.
|
|
*LINE_LEN is set to the length of the line. Note that the line
|
|
does not contain any terminal delimiter. This function returns
|
|
true if some data was read or process from the cache, false
|
|
otherwise. Note that subsequent calls to get_next_line return the
|
|
next lines of the file and might overwrite the content of
|
|
*LINE. */
|
|
|
|
static bool
|
|
get_next_line (fcache *c, char **line, ssize_t *line_len)
|
|
{
|
|
/* Fill the cache with data to process. */
|
|
maybe_read_data (c);
|
|
|
|
size_t remaining_size = c->nb_read - c->line_start_idx;
|
|
if (remaining_size == 0)
|
|
/* There is no more data to process. */
|
|
return false;
|
|
|
|
char *line_start = c->data + c->line_start_idx;
|
|
|
|
char *next_line_start = NULL;
|
|
size_t len = 0;
|
|
char *line_end = (char *) memchr (line_start, '\n', remaining_size);
|
|
if (line_end == NULL)
|
|
{
|
|
/* We haven't found the end-of-line delimiter in the cache.
|
|
Fill the cache with more data from the file and look for the
|
|
'\n'. */
|
|
while (maybe_read_data (c))
|
|
{
|
|
line_start = c->data + c->line_start_idx;
|
|
remaining_size = c->nb_read - c->line_start_idx;
|
|
line_end = (char *) memchr (line_start, '\n', remaining_size);
|
|
if (line_end != NULL)
|
|
{
|
|
next_line_start = line_end + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (line_end == NULL)
|
|
/* We've loadded all the file into the cache and still no
|
|
'\n'. Let's say the line ends up at one byte passed the
|
|
end of the file. This is to stay consistent with the case
|
|
of when the line ends up with a '\n' and line_end points to
|
|
that terminal '\n'. That consistency is useful below in
|
|
the len calculation. */
|
|
line_end = c->data + c->nb_read ;
|
|
}
|
|
else
|
|
next_line_start = line_end + 1;
|
|
|
|
if (ferror (c->fp))
|
|
return -1;
|
|
|
|
/* At this point, we've found the end of the of line. It either
|
|
points to the '\n' or to one byte after the last byte of the
|
|
file. */
|
|
gcc_assert (line_end != NULL);
|
|
|
|
len = line_end - line_start;
|
|
|
|
if (c->line_start_idx < c->nb_read)
|
|
*line = line_start;
|
|
|
|
++c->line_num;
|
|
|
|
/* Before we update our line record, make sure the hint about the
|
|
total number of lines of the file is correct. If it's not, then
|
|
we give up recording line boundaries from now on. */
|
|
bool update_line_record = true;
|
|
if (c->line_num > c->total_lines)
|
|
update_line_record = false;
|
|
|
|
/* Now update our line record so that re-reading lines from the
|
|
before c->line_start_idx is faster. */
|
|
if (update_line_record
|
|
&& c->line_record.length () < fcache_line_record_size)
|
|
{
|
|
/* If the file lines fits in the line record, we just record all
|
|
its lines ...*/
|
|
if (c->total_lines <= fcache_line_record_size
|
|
&& c->line_num > c->line_record.length ())
|
|
c->line_record.safe_push (fcache::line_info (c->line_num,
|
|
c->line_start_idx,
|
|
line_end - c->data));
|
|
else if (c->total_lines > fcache_line_record_size)
|
|
{
|
|
/* ... otherwise, we just scale total_lines down to
|
|
(fcache_line_record_size lines. */
|
|
size_t n = (c->line_num * fcache_line_record_size) / c->total_lines;
|
|
if (c->line_record.length () == 0
|
|
|| n >= c->line_record.length ())
|
|
c->line_record.safe_push (fcache::line_info (c->line_num,
|
|
c->line_start_idx,
|
|
line_end - c->data));
|
|
}
|
|
}
|
|
|
|
/* Update c->line_start_idx so that it points to the next line to be
|
|
read. */
|
|
if (next_line_start)
|
|
c->line_start_idx = next_line_start - c->data;
|
|
else
|
|
/* We didn't find any terminal '\n'. Let's consider that the end
|
|
of line is the end of the data in the cache. The next
|
|
invocation of get_next_line will either read more data from the
|
|
underlying file or return false early because we've reached the
|
|
end of the file. */
|
|
c->line_start_idx = c->nb_read;
|
|
|
|
*line_len = len;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Reads the next line from FILE into *LINE. If *LINE is too small
|
|
(or NULL) it is allocated (or extended) to have enough space to
|
|
containe the line. *LINE_LENGTH must contain the size of the
|
|
initial*LINE buffer. It's then updated by this function to the
|
|
actual length of the returned line. Note that the returned line
|
|
can contain several zero bytes. Also note that the returned string
|
|
is allocated in static storage that is going to be re-used by
|
|
subsequent invocations of read_line. */
|
|
|
|
static bool
|
|
read_next_line (fcache *cache, char ** line, ssize_t *line_len)
|
|
{
|
|
char *l = NULL;
|
|
ssize_t len = 0;
|
|
|
|
if (!get_next_line (cache, &l, &len))
|
|
return false;
|
|
|
|
if (*line == NULL)
|
|
*line = XNEWVEC (char, len);
|
|
else
|
|
if (*line_len < len)
|
|
*line = XRESIZEVEC (char, *line, len);
|
|
|
|
memcpy (*line, l, len);
|
|
*line_len = len;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Consume the next bytes coming from the cache (or from its
|
|
underlying file if there are remaining unread bytes in the file)
|
|
until we reach the next end-of-line (or end-of-file). There is no
|
|
copying from the cache involved. Return TRUE upon successful
|
|
completion. */
|
|
|
|
static bool
|
|
goto_next_line (fcache *cache)
|
|
{
|
|
char *l;
|
|
ssize_t len;
|
|
|
|
return get_next_line (cache, &l, &len);
|
|
}
|
|
|
|
/* Read an arbitrary line number LINE_NUM from the file cached in C.
|
|
The line is copied into *LINE. *LINE_LEN must have been set to the
|
|
length of *LINE. If *LINE is too small (or NULL) it's extended (or
|
|
allocated) and *LINE_LEN is adjusted accordingly. *LINE ends up
|
|
with a terminal zero byte and can contain additional zero bytes.
|
|
This function returns bool if a line was read. */
|
|
|
|
static bool
|
|
read_line_num (fcache *c, size_t line_num,
|
|
char ** line, ssize_t *line_len)
|
|
{
|
|
gcc_assert (line_num > 0);
|
|
|
|
if (line_num <= c->line_num)
|
|
{
|
|
/* We've been asked to read lines that are before c->line_num.
|
|
So lets use our line record (if it's not empty) to try to
|
|
avoid re-reading the file from the beginning again. */
|
|
|
|
if (c->line_record.is_empty ())
|
|
{
|
|
c->line_start_idx = 0;
|
|
c->line_num = 0;
|
|
}
|
|
else
|
|
{
|
|
fcache::line_info *i = NULL;
|
|
if (c->total_lines <= fcache_line_record_size)
|
|
{
|
|
/* In languages where the input file is not totally
|
|
preprocessed up front, the c->total_lines hint
|
|
can be smaller than the number of lines of the
|
|
file. In that case, only the first
|
|
c->total_lines have been recorded.
|
|
|
|
Otherwise, the first c->total_lines we've read have
|
|
their start/end recorded here. */
|
|
i = (line_num <= c->total_lines)
|
|
? &c->line_record[line_num - 1]
|
|
: &c->line_record[c->total_lines - 1];
|
|
gcc_assert (i->line_num <= line_num);
|
|
}
|
|
else
|
|
{
|
|
/* So the file had more lines than our line record
|
|
size. Thus the number of lines we've recorded has
|
|
been scaled down to fcache_line_reacord_size. Let's
|
|
pick the start/end of the recorded line that is
|
|
closest to line_num. */
|
|
size_t n = (line_num <= c->total_lines)
|
|
? line_num * fcache_line_record_size / c->total_lines
|
|
: c ->line_record.length () - 1;
|
|
if (n < c->line_record.length ())
|
|
{
|
|
i = &c->line_record[n];
|
|
gcc_assert (i->line_num <= line_num);
|
|
}
|
|
}
|
|
|
|
if (i && i->line_num == line_num)
|
|
{
|
|
/* We have the start/end of the line. Let's just copy
|
|
it again and we are done. */
|
|
ssize_t len = i->end_pos - i->start_pos + 1;
|
|
if (*line_len < len)
|
|
*line = XRESIZEVEC (char, *line, len);
|
|
memmove (*line, c->data + i->start_pos, len);
|
|
(*line)[len - 1] = '\0';
|
|
*line_len = --len;
|
|
return true;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
c->line_start_idx = i->start_pos;
|
|
c->line_num = i->line_num - 1;
|
|
}
|
|
else
|
|
{
|
|
c->line_start_idx = 0;
|
|
c->line_num = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Let's walk from line c->line_num up to line_num - 1, without
|
|
copying any line. */
|
|
while (c->line_num < line_num - 1)
|
|
if (!goto_next_line (c))
|
|
return false;
|
|
|
|
/* The line we want is the next one. Let's read and copy it back to
|
|
the caller. */
|
|
return read_next_line (c, line, line_len);
|
|
}
|
|
|
|
/* Return the physical source line that corresponds to FILE_PATH/LINE in a
|
|
buffer that is statically allocated. The newline is replaced by
|
|
the null character. Note that the line can contain several null
|
|
characters, so LINE_LEN, if non-null, points to the actual length
|
|
of the line. */
|
|
|
|
const char *
|
|
location_get_source_line (const char *file_path, int line,
|
|
int *line_len)
|
|
{
|
|
static char *buffer;
|
|
static ssize_t len;
|
|
|
|
if (line == 0)
|
|
return NULL;
|
|
|
|
fcache *c = lookup_or_add_file_to_cache_tab (file_path);
|
|
if (c == NULL)
|
|
return NULL;
|
|
|
|
bool read = read_line_num (c, line, &buffer, &len);
|
|
|
|
if (read && line_len)
|
|
*line_len = len;
|
|
|
|
return read ? buffer : NULL;
|
|
}
|
|
|
|
/* Test if the location originates from the spelling location of a
|
|
builtin-tokens. That is, return TRUE if LOC is a (possibly
|
|
virtual) location of a built-in token that appears in the expansion
|
|
list of a macro. Please note that this function also works on
|
|
tokens that result from built-in tokens. For instance, the
|
|
function would return true if passed a token "4" that is the result
|
|
of the expansion of the built-in __LINE__ macro. */
|
|
bool
|
|
is_location_from_builtin_token (source_location loc)
|
|
{
|
|
const line_map_ordinary *map = NULL;
|
|
loc = linemap_resolve_location (line_table, loc,
|
|
LRK_SPELLING_LOCATION, &map);
|
|
return loc == BUILTINS_LOCATION;
|
|
}
|
|
|
|
/* Expand the source location LOC into a human readable location. If
|
|
LOC is virtual, it resolves to the expansion point of the involved
|
|
macro. If LOC resolves to a builtin location, the file name of the
|
|
readable location is set to the string "<built-in>". */
|
|
|
|
expanded_location
|
|
expand_location (source_location loc)
|
|
{
|
|
return expand_location_1 (loc, /*expansion_point_p=*/true);
|
|
}
|
|
|
|
/* Expand the source location LOC into a human readable location. If
|
|
LOC is virtual, it resolves to the expansion location of the
|
|
relevant macro. If LOC resolves to a builtin location, the file
|
|
name of the readable location is set to the string
|
|
"<built-in>". */
|
|
|
|
expanded_location
|
|
expand_location_to_spelling_point (source_location loc)
|
|
{
|
|
return expand_location_1 (loc, /*expansion_point_p=*/false);
|
|
}
|
|
|
|
/* The rich_location class within libcpp requires a way to expand
|
|
source_location instances, and relies on the client code
|
|
providing a symbol named
|
|
linemap_client_expand_location_to_spelling_point
|
|
to do this.
|
|
|
|
This is the implementation for libcommon.a (all host binaries),
|
|
which simply calls into expand_location_to_spelling_point. */
|
|
|
|
expanded_location
|
|
linemap_client_expand_location_to_spelling_point (source_location loc)
|
|
{
|
|
return expand_location_to_spelling_point (loc);
|
|
}
|
|
|
|
|
|
/* If LOCATION is in a system header and if it is a virtual location for
|
|
a token coming from the expansion of a macro, unwind it to the
|
|
location of the expansion point of the macro. Otherwise, just return
|
|
LOCATION.
|
|
|
|
This is used for instance when we want to emit diagnostics about a
|
|
token that may be located in a macro that is itself defined in a
|
|
system header, for example, for the NULL macro. In such a case, if
|
|
LOCATION were passed directly to diagnostic functions such as
|
|
warning_at, the diagnostic would be suppressed (unless
|
|
-Wsystem-headers). */
|
|
|
|
source_location
|
|
expansion_point_location_if_in_system_header (source_location location)
|
|
{
|
|
if (in_system_header_at (location))
|
|
location = linemap_resolve_location (line_table, location,
|
|
LRK_MACRO_EXPANSION_POINT,
|
|
NULL);
|
|
return location;
|
|
}
|
|
|
|
#define ONE_K 1024
|
|
#define ONE_M (ONE_K * ONE_K)
|
|
|
|
/* Display a number as an integer multiple of either:
|
|
- 1024, if said integer is >= to 10 K (in base 2)
|
|
- 1024 * 1024, if said integer is >= 10 M in (base 2)
|
|
*/
|
|
#define SCALE(x) ((unsigned long) ((x) < 10 * ONE_K \
|
|
? (x) \
|
|
: ((x) < 10 * ONE_M \
|
|
? (x) / ONE_K \
|
|
: (x) / ONE_M)))
|
|
|
|
/* For a given integer, display either:
|
|
- the character 'k', if the number is higher than 10 K (in base 2)
|
|
but strictly lower than 10 M (in base 2)
|
|
- the character 'M' if the number is higher than 10 M (in base2)
|
|
- the charcter ' ' if the number is strictly lower than 10 K */
|
|
#define STAT_LABEL(x) ((x) < 10 * ONE_K ? ' ' : ((x) < 10 * ONE_M ? 'k' : 'M'))
|
|
|
|
/* Display an integer amount as multiple of 1K or 1M (in base 2).
|
|
Display the correct unit (either k, M, or ' ') after the amout, as
|
|
well. */
|
|
#define FORMAT_AMOUNT(size) SCALE (size), STAT_LABEL (size)
|
|
|
|
/* Dump statistics to stderr about the memory usage of the line_table
|
|
set of line maps. This also displays some statistics about macro
|
|
expansion. */
|
|
|
|
void
|
|
dump_line_table_statistics (void)
|
|
{
|
|
struct linemap_stats s;
|
|
long total_used_map_size,
|
|
macro_maps_size,
|
|
total_allocated_map_size;
|
|
|
|
memset (&s, 0, sizeof (s));
|
|
|
|
linemap_get_statistics (line_table, &s);
|
|
|
|
macro_maps_size = s.macro_maps_used_size
|
|
+ s.macro_maps_locations_size;
|
|
|
|
total_allocated_map_size = s.ordinary_maps_allocated_size
|
|
+ s.macro_maps_allocated_size
|
|
+ s.macro_maps_locations_size;
|
|
|
|
total_used_map_size = s.ordinary_maps_used_size
|
|
+ s.macro_maps_used_size
|
|
+ s.macro_maps_locations_size;
|
|
|
|
fprintf (stderr, "Number of expanded macros: %5ld\n",
|
|
s.num_expanded_macros);
|
|
if (s.num_expanded_macros != 0)
|
|
fprintf (stderr, "Average number of tokens per macro expansion: %5ld\n",
|
|
s.num_macro_tokens / s.num_expanded_macros);
|
|
fprintf (stderr,
|
|
"\nLine Table allocations during the "
|
|
"compilation process\n");
|
|
fprintf (stderr, "Number of ordinary maps used: %5ld%c\n",
|
|
SCALE (s.num_ordinary_maps_used),
|
|
STAT_LABEL (s.num_ordinary_maps_used));
|
|
fprintf (stderr, "Ordinary map used size: %5ld%c\n",
|
|
SCALE (s.ordinary_maps_used_size),
|
|
STAT_LABEL (s.ordinary_maps_used_size));
|
|
fprintf (stderr, "Number of ordinary maps allocated: %5ld%c\n",
|
|
SCALE (s.num_ordinary_maps_allocated),
|
|
STAT_LABEL (s.num_ordinary_maps_allocated));
|
|
fprintf (stderr, "Ordinary maps allocated size: %5ld%c\n",
|
|
SCALE (s.ordinary_maps_allocated_size),
|
|
STAT_LABEL (s.ordinary_maps_allocated_size));
|
|
fprintf (stderr, "Number of macro maps used: %5ld%c\n",
|
|
SCALE (s.num_macro_maps_used),
|
|
STAT_LABEL (s.num_macro_maps_used));
|
|
fprintf (stderr, "Macro maps used size: %5ld%c\n",
|
|
SCALE (s.macro_maps_used_size),
|
|
STAT_LABEL (s.macro_maps_used_size));
|
|
fprintf (stderr, "Macro maps locations size: %5ld%c\n",
|
|
SCALE (s.macro_maps_locations_size),
|
|
STAT_LABEL (s.macro_maps_locations_size));
|
|
fprintf (stderr, "Macro maps size: %5ld%c\n",
|
|
SCALE (macro_maps_size),
|
|
STAT_LABEL (macro_maps_size));
|
|
fprintf (stderr, "Duplicated maps locations size: %5ld%c\n",
|
|
SCALE (s.duplicated_macro_maps_locations_size),
|
|
STAT_LABEL (s.duplicated_macro_maps_locations_size));
|
|
fprintf (stderr, "Total allocated maps size: %5ld%c\n",
|
|
SCALE (total_allocated_map_size),
|
|
STAT_LABEL (total_allocated_map_size));
|
|
fprintf (stderr, "Total used maps size: %5ld%c\n",
|
|
SCALE (total_used_map_size),
|
|
STAT_LABEL (total_used_map_size));
|
|
fprintf (stderr, "Ad-hoc table size: %5ld%c\n",
|
|
SCALE (s.adhoc_table_size),
|
|
STAT_LABEL (s.adhoc_table_size));
|
|
fprintf (stderr, "Ad-hoc table entries used: %5ld\n",
|
|
s.adhoc_table_entries_used);
|
|
fprintf (stderr, "optimized_ranges: %i\n",
|
|
line_table->num_optimized_ranges);
|
|
fprintf (stderr, "unoptimized_ranges: %i\n",
|
|
line_table->num_unoptimized_ranges);
|
|
|
|
fprintf (stderr, "\n");
|
|
}
|
|
|
|
/* Get location one beyond the final location in ordinary map IDX. */
|
|
|
|
static source_location
|
|
get_end_location (struct line_maps *set, unsigned int idx)
|
|
{
|
|
if (idx == LINEMAPS_ORDINARY_USED (set) - 1)
|
|
return set->highest_location;
|
|
|
|
struct line_map *next_map = LINEMAPS_ORDINARY_MAP_AT (set, idx + 1);
|
|
return MAP_START_LOCATION (next_map);
|
|
}
|
|
|
|
/* Helper function for write_digit_row. */
|
|
|
|
static void
|
|
write_digit (FILE *stream, int digit)
|
|
{
|
|
fputc ('0' + (digit % 10), stream);
|
|
}
|
|
|
|
/* Helper function for dump_location_info.
|
|
Write a row of numbers to STREAM, numbering a source line,
|
|
giving the units, tens, hundreds etc of the column number. */
|
|
|
|
static void
|
|
write_digit_row (FILE *stream, int indent,
|
|
const line_map_ordinary *map,
|
|
source_location loc, int max_col, int divisor)
|
|
{
|
|
fprintf (stream, "%*c", indent, ' ');
|
|
fprintf (stream, "|");
|
|
for (int column = 1; column < max_col; column++)
|
|
{
|
|
source_location column_loc = loc + (column << map->m_range_bits);
|
|
write_digit (stream, column_loc / divisor);
|
|
}
|
|
fprintf (stream, "\n");
|
|
}
|
|
|
|
/* Write a half-closed (START) / half-open (END) interval of
|
|
source_location to STREAM. */
|
|
|
|
static void
|
|
dump_location_range (FILE *stream,
|
|
source_location start, source_location end)
|
|
{
|
|
fprintf (stream,
|
|
" source_location interval: %u <= loc < %u\n",
|
|
start, end);
|
|
}
|
|
|
|
/* Write a labelled description of a half-closed (START) / half-open (END)
|
|
interval of source_location to STREAM. */
|
|
|
|
static void
|
|
dump_labelled_location_range (FILE *stream,
|
|
const char *name,
|
|
source_location start, source_location end)
|
|
{
|
|
fprintf (stream, "%s\n", name);
|
|
dump_location_range (stream, start, end);
|
|
fprintf (stream, "\n");
|
|
}
|
|
|
|
/* Write a visualization of the locations in the line_table to STREAM. */
|
|
|
|
void
|
|
dump_location_info (FILE *stream)
|
|
{
|
|
/* Visualize the reserved locations. */
|
|
dump_labelled_location_range (stream, "RESERVED LOCATIONS",
|
|
0, RESERVED_LOCATION_COUNT);
|
|
|
|
/* Visualize the ordinary line_map instances, rendering the sources. */
|
|
for (unsigned int idx = 0; idx < LINEMAPS_ORDINARY_USED (line_table); idx++)
|
|
{
|
|
source_location end_location = get_end_location (line_table, idx);
|
|
/* half-closed: doesn't include this one. */
|
|
|
|
const line_map_ordinary *map
|
|
= LINEMAPS_ORDINARY_MAP_AT (line_table, idx);
|
|
fprintf (stream, "ORDINARY MAP: %i\n", idx);
|
|
dump_location_range (stream,
|
|
MAP_START_LOCATION (map), end_location);
|
|
fprintf (stream, " file: %s\n", ORDINARY_MAP_FILE_NAME (map));
|
|
fprintf (stream, " starting at line: %i\n",
|
|
ORDINARY_MAP_STARTING_LINE_NUMBER (map));
|
|
fprintf (stream, " column and range bits: %i\n",
|
|
map->m_column_and_range_bits);
|
|
fprintf (stream, " column bits: %i\n",
|
|
map->m_column_and_range_bits - map->m_range_bits);
|
|
fprintf (stream, " range bits: %i\n",
|
|
map->m_range_bits);
|
|
|
|
/* Render the span of source lines that this "map" covers. */
|
|
for (source_location loc = MAP_START_LOCATION (map);
|
|
loc < end_location;
|
|
loc += (1 << map->m_range_bits) )
|
|
{
|
|
gcc_assert (pure_location_p (line_table, loc) );
|
|
|
|
expanded_location exploc
|
|
= linemap_expand_location (line_table, map, loc);
|
|
|
|
if (0 == exploc.column)
|
|
{
|
|
/* Beginning of a new source line: draw the line. */
|
|
|
|
int line_size;
|
|
const char *line_text = location_get_source_line (exploc.file,
|
|
exploc.line,
|
|
&line_size);
|
|
if (!line_text)
|
|
break;
|
|
fprintf (stream,
|
|
"%s:%3i|loc:%5i|%.*s\n",
|
|
exploc.file, exploc.line,
|
|
loc,
|
|
line_size, line_text);
|
|
|
|
/* "loc" is at column 0, which means "the whole line".
|
|
Render the locations *within* the line, by underlining
|
|
it, showing the source_location numeric values
|
|
at each column. */
|
|
int max_col = (1 << map->m_column_and_range_bits) - 1;
|
|
if (max_col > line_size)
|
|
max_col = line_size + 1;
|
|
|
|
int indent = 14 + strlen (exploc.file);
|
|
|
|
/* Thousands. */
|
|
if (end_location > 999)
|
|
write_digit_row (stream, indent, map, loc, max_col, 1000);
|
|
|
|
/* Hundreds. */
|
|
if (end_location > 99)
|
|
write_digit_row (stream, indent, map, loc, max_col, 100);
|
|
|
|
/* Tens. */
|
|
write_digit_row (stream, indent, map, loc, max_col, 10);
|
|
|
|
/* Units. */
|
|
write_digit_row (stream, indent, map, loc, max_col, 1);
|
|
}
|
|
}
|
|
fprintf (stream, "\n");
|
|
}
|
|
|
|
/* Visualize unallocated values. */
|
|
dump_labelled_location_range (stream, "UNALLOCATED LOCATIONS",
|
|
line_table->highest_location,
|
|
LINEMAPS_MACRO_LOWEST_LOCATION (line_table));
|
|
|
|
/* Visualize the macro line_map instances, rendering the sources. */
|
|
for (unsigned int i = 0; i < LINEMAPS_MACRO_USED (line_table); i++)
|
|
{
|
|
/* Each macro map that is allocated owns source_location values
|
|
that are *lower* that the one before them.
|
|
Hence it's meaningful to view them either in order of ascending
|
|
source locations, or in order of ascending macro map index. */
|
|
const bool ascending_source_locations = true;
|
|
unsigned int idx = (ascending_source_locations
|
|
? (LINEMAPS_MACRO_USED (line_table) - (i + 1))
|
|
: i);
|
|
const line_map_macro *map = LINEMAPS_MACRO_MAP_AT (line_table, idx);
|
|
fprintf (stream, "MACRO %i: %s (%u tokens)\n",
|
|
idx,
|
|
linemap_map_get_macro_name (map),
|
|
MACRO_MAP_NUM_MACRO_TOKENS (map));
|
|
dump_location_range (stream,
|
|
map->start_location,
|
|
(map->start_location
|
|
+ MACRO_MAP_NUM_MACRO_TOKENS (map)));
|
|
inform (MACRO_MAP_EXPANSION_POINT_LOCATION (map),
|
|
"expansion point is location %i",
|
|
MACRO_MAP_EXPANSION_POINT_LOCATION (map));
|
|
fprintf (stream, " map->start_location: %u\n",
|
|
map->start_location);
|
|
|
|
fprintf (stream, " macro_locations:\n");
|
|
for (unsigned int i = 0; i < MACRO_MAP_NUM_MACRO_TOKENS (map); i++)
|
|
{
|
|
source_location x = MACRO_MAP_LOCATIONS (map)[2 * i];
|
|
source_location y = MACRO_MAP_LOCATIONS (map)[(2 * i) + 1];
|
|
|
|
/* linemap_add_macro_token encodes token numbers in an expansion
|
|
by putting them after MAP_START_LOCATION. */
|
|
|
|
/* I'm typically seeing 4 uninitialized entries at the end of
|
|
0xafafafaf.
|
|
This appears to be due to macro.c:replace_args
|
|
adding 2 extra args for padding tokens; presumably there may
|
|
be a leading and/or trailing padding token injected,
|
|
each for 2 more location slots.
|
|
This would explain there being up to 4 source_locations slots
|
|
that may be uninitialized. */
|
|
|
|
fprintf (stream, " %u: %u, %u\n",
|
|
i,
|
|
x,
|
|
y);
|
|
if (x == y)
|
|
{
|
|
if (x < MAP_START_LOCATION (map))
|
|
inform (x, "token %u has x-location == y-location == %u", i, x);
|
|
else
|
|
fprintf (stream,
|
|
"x-location == y-location == %u encodes token # %u\n",
|
|
x, x - MAP_START_LOCATION (map));
|
|
}
|
|
else
|
|
{
|
|
inform (x, "token %u has x-location == %u", i, x);
|
|
inform (x, "token %u has y-location == %u", i, y);
|
|
}
|
|
}
|
|
fprintf (stream, "\n");
|
|
}
|
|
|
|
/* It appears that MAX_SOURCE_LOCATION itself is never assigned to a
|
|
macro map, presumably due to an off-by-one error somewhere
|
|
between the logic in linemap_enter_macro and
|
|
LINEMAPS_MACRO_LOWEST_LOCATION. */
|
|
dump_labelled_location_range (stream, "MAX_SOURCE_LOCATION",
|
|
MAX_SOURCE_LOCATION,
|
|
MAX_SOURCE_LOCATION + 1);
|
|
|
|
/* Visualize ad-hoc values. */
|
|
dump_labelled_location_range (stream, "AD-HOC LOCATIONS",
|
|
MAX_SOURCE_LOCATION + 1, UINT_MAX);
|
|
}
|