From 746259d6befd4328424ef1bcd99fc5c35337526d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 15 Oct 2015 12:16:52 -0400
Subject: [PATCH 14/58] FIXME: compress short ranges into source_location

---
 gcc/input.c                                        |   6 +-
 .../diagnostic_plugin_test_tree_expression_range.c |   8 +-
 gcc/toplev.c                                       |   9 ++
 gcc/tree.c                                         |  22 ++-
 libcpp/include/line-map.h                          |  37 +++--
 libcpp/lex.c                                       |   9 +-
 libcpp/line-map.c                                  | 149 +++++++++++++++++++--
 7 files changed, 208 insertions(+), 32 deletions(-)

diff --git a/gcc/input.c b/gcc/input.c
index bdba20f..5549872 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -908,7 +908,7 @@ write_digit_row (FILE *stream, int indent,
   fprintf (stream, "|");
   for (int column = 1; column < max_col; column++)
     {
-      source_location column_loc = loc + column;
+      source_location column_loc = loc + (column << line_table->range_bits);
       write_digit (stream, column_loc / divisor);
     }
   fprintf (stream, "\n");
@@ -968,8 +968,10 @@ dump_location_info (FILE *stream)
       /* Render the span of source lines that this "map" covers.  */
       for (source_location loc = MAP_START_LOCATION (map);
 	   loc < end_location;
-	   loc++)
+	   loc += (1 << line_table->range_bits) )
 	{
+	  gcc_assert (pure_location_p (line_table, loc) );
+
 	  expanded_location exploc
 	    = linemap_expand_location (line_table, map, loc);
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
index b8a1ab9..4d527e0 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -36,13 +36,7 @@ int plugin_is_GPL_compatible;
 static void
 emit_warning (location_t loc)
 {
-  if (!IS_ADHOC_LOC (loc))
-    {
-      error_at (loc, "ad-hoc location not found");
-      return;
-    }
-
-  source_range src_range = get_range_from_adhoc_loc (line_table, loc);
+  source_range src_range = get_range_from_loc (line_table, loc);
   warning_at (loc, 0,
 	      "tree range %i:%i-%i:%i",
 	      LOCATION_LINE (src_range.m_start),
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 6d740d4..07f804c 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1147,6 +1147,7 @@ general_init (const char *argv0, bool init_signals)
   linemap_init (line_table, BUILTINS_LOCATION);
   line_table->reallocator = realloc_for_line_map;
   line_table->round_alloc_size = ggc_round_alloc_size;
+  line_table->range_bits = 8; // FIXME: maybe from a param?
   init_ttree ();
 
   /* Initialize register usage now so switches may override.  */
@@ -2090,6 +2091,14 @@ toplev::main (int argc, char **argv)
   diagnostic_finish (global_dc);
 
   finalize_plugins ();
+
+#if 0
+  fprintf (stderr, "optimized_ranges: %i\n",
+	   line_table->num_optimized_ranges);
+  fprintf (stderr, "unoptimized_ranges: %i\n",
+	   line_table->num_unoptimized_ranges);
+#endif
+
   location_adhoc_data_fini (line_table);
   if (seen_error () || werrorcount)
     return (FATAL_EXIT_CODE);
diff --git a/gcc/tree.c b/gcc/tree.c
index 0a2db3a..d25e353 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13662,14 +13662,31 @@ set_source_range (tree *expr, location_t start, location_t finish)
   set_source_range (expr, src_range);
 }
 
+static location_t
+get_pure_location (location_t loc)
+{
+  if (IS_ADHOC_LOC (loc))
+    loc
+      = line_table->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].locus;
+
+  if (loc >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table))
+    return loc;
+
+  if (loc < RESERVED_LOCATION_COUNT)
+    return loc;
+
+  return loc & ~((1 << line_table->range_bits) - 1);
+}
+
 void
 set_source_range (tree *expr, source_range src_range)
 {
   if (!EXPR_P (*expr))
     return;
 
+  location_t pure_loc = get_pure_location (EXPR_LOCATION (*expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
-					    EXPR_LOCATION (*expr),
+					    pure_loc,
 					    src_range,
 					    NULL /* FIXME */);
   SET_EXPR_LOCATION (*expr, adhoc);
@@ -13678,8 +13695,9 @@ set_source_range (tree *expr, source_range src_range)
 location_t
 set_block (location_t loc, tree block)
 {
+  location_t pure_loc = get_pure_location (loc);
   source_range src_range = get_range_from_loc (line_table, loc);
-  return COMBINE_LOCATION_DATA (line_table, loc, src_range, block);
+  return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
 
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 155f078..e5a78ac 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -47,7 +47,8 @@ enum lc_reason
 typedef unsigned int linenum_type;
 
 /* The typedef "source_location" is a key within the location database,
-   identifying a source location or macro expansion.
+   identifying a source location or macro expansion, along with range
+   information, and (optionally) a pointer for use by gcc.
 
    This key only has meaning in relation to a line_maps instance.  Within
    gcc there is a single line_maps instance: "line_table", declared in
@@ -215,6 +216,9 @@ struct GTY((tag ("1"))) line_map_ordinary : public line_map {
 
   /* Number of the low-order source_location bits used for a column number.  */
   unsigned int column_bits : 8;
+
+  /* Number of the "column" bits bits used for storing ranges.  */
+  unsigned int range_bits : 8;
 };
 
 /* This is the highest possible source location encoded within an
@@ -565,6 +569,12 @@ struct GTY(()) line_maps {
 
   /* True if we've seen a #line or # 44 "file" directive.  */
   bool seen_line_directive;
+
+  /* FIXME.  */
+  unsigned int range_bits;
+
+  unsigned int num_optimized_ranges;
+  unsigned int num_unoptimized_ranges;
 };
 
 /* Returns the number of allocated maps so far. MAP_KIND shall be TRUE
@@ -808,8 +818,10 @@ extern source_location get_combined_adhoc_loc (struct line_maps *,
 extern void *get_data_from_adhoc_loc (struct line_maps *, source_location);
 extern source_location get_location_from_adhoc_loc (struct line_maps *,
 						    source_location);
-extern source_range get_range_from_adhoc_loc (struct line_maps *,
-					      source_location);
+
+extern source_range
+get_range_from_loc (line_maps *set,
+		    source_location loc);
 
 /* Get whether location LOC is an ad-hoc location.  */
 
@@ -819,14 +831,19 @@ IS_ADHOC_LOC (source_location loc)
   return (loc & MAX_SOURCE_LOCATION) != loc;
 }
 
-inline source_range
-get_range_from_loc (struct line_maps *set,
-		    source_location loc)
+/* Get whether location LOC is a "pure" location, or
+   whether it is an ad-hoc location, or embeds range information.  */
+
+inline bool
+pure_location_p (line_maps *set, source_location loc)
 {
   if (IS_ADHOC_LOC (loc))
-    return get_range_from_adhoc_loc (set, loc);
-  else
-    return source_range::from_location (loc);
+    return false;
+
+  if (loc & ((1U << set->range_bits) - 1))
+    return false;
+
+  return true;
 }
 
 /* Combine LOC and BLOCK, giving a combined adhoc location.  */
@@ -931,7 +948,7 @@ inline linenum_type
 SOURCE_COLUMN (const line_map_ordinary *ord_map, source_location loc)
 {
   return ((loc - ord_map->start_location)
-	  & ((1 << ord_map->column_bits) - 1));
+	  & ((1 << ord_map->column_bits) - 1)) >> ord_map->range_bits;
 }
 
 /* Return the location of the last source line within an ordinary
diff --git a/libcpp/lex.c b/libcpp/lex.c
index f4c964f..6f46a7f 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -2725,9 +2725,12 @@ _cpp_lex_direct (cpp_reader *pfile)
 
   source_range tok_range;
   tok_range.m_start = result->src_loc;
-  tok_range.m_finish =
-    linemap_position_for_column (pfile->line_table,
-				 CPP_BUF_COLUMN (buffer, buffer->cur));
+  if (result->src_loc >= RESERVED_LOCATION_COUNT)
+    tok_range.m_finish =
+      linemap_position_for_column (pfile->line_table,
+				   CPP_BUF_COLUMN (buffer, buffer->cur));
+  else
+    tok_range.m_finish = tok_range.m_start;
 
   result->src_loc = COMBINE_LOCATION_DATA (pfile->line_table,
 					   result->src_loc,
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index 3de7b16..bcf7276 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -29,7 +29,7 @@ along with this program; see the file COPYING3.  If not see
 /* Do not track column numbers higher than this one.  As a result, the
    range of column_bits is [7, 18] (or 0 if column numbers are
    disabled).  */
-const unsigned int LINE_MAP_MAX_COLUMN_NUMBER = (1U << 17);
+const unsigned int LINE_MAP_MAX_COLUMN_NUMBER = (1U << 9);
 
 /* Do not track column numbers if locations get higher than this.  */
 const source_location LINE_MAP_MAX_LOCATION_WITH_COLS = 0x60000000;
@@ -112,6 +112,49 @@ rebuild_location_adhoc_htab (struct line_maps *set)
 		    set->location_adhoc_data_map.data + i, INSERT);
 }
 
+/* Helper function for get_combined_adhoc_loc.
+   Can the given LOCUS + SRC_RANGE and DATA pointer be stored compactly
+   within a source_location, without needing to use an ad-hoc location.  */
+
+static bool
+can_be_stored_compactly_p (struct line_maps *set,
+			   source_location locus,
+			   source_range src_range,
+			   void *data)
+{
+  /* If there's an ad-hoc pointer, we can't store it directly in the
+     source_location, we need the lookaside.  */
+  if (data)
+    return false;
+
+  /* We only store ranges that begin at the locus and that are sufficientl
+     "sane".  */
+  if (src_range.m_start != locus)
+    return false;
+
+  if (src_range.m_finish < src_range.m_start)
+    return false;
+
+  if (src_range.m_start < RESERVED_LOCATION_COUNT)
+    return false;
+
+  if (locus >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return false;
+
+  /* All 3 locations must be within ordinary maps, typically, the same
+     ordinary map.  */
+  source_location lowest_macro_loc = LINEMAPS_MACRO_LOWEST_LOCATION (set);
+  if (locus >= lowest_macro_loc)
+    return false;
+  if (src_range.m_start >= lowest_macro_loc)
+    return false;
+  if (src_range.m_finish >= lowest_macro_loc)
+    return false;
+
+  /* Passed all tests.  */
+  return true;
+}
+
 /* Combine LOCUS and DATA to a combined adhoc loc.  */
 
 source_location
@@ -128,6 +171,58 @@ get_combined_adhoc_loc (struct line_maps *set,
       = set->location_adhoc_data_map.data[locus & MAX_SOURCE_LOCATION].locus;
   if (locus == 0 && data == NULL)
     return 0;
+
+  /* Any ordinary locations ought to be "pure" at this point: no
+     compressed ranges.  */
+  linemap_assert (locus < RESERVED_LOCATION_COUNT
+		  || locus >= LINE_MAP_MAX_LOCATION_WITH_COLS
+		  || locus >= LINEMAPS_MACRO_LOWEST_LOCATION (set)
+		  || pure_location_p (set, locus));
+
+#define DEBUG_PACKING 0
+
+#if DEBUG_PACKING
+  fprintf (stderr, "get_combined_adhoc_loc: %x %x %x\n",
+	   locus, src_range.m_start, src_range.m_finish);
+#endif
+
+  /* Consider short-range optimization.  */
+  if (can_be_stored_compactly_p (set, locus, src_range, data))
+    {
+      /* The low bits ought to be clear.  */
+      linemap_assert (pure_location_p (set, locus));
+      unsigned int int_diff = src_range.m_finish - src_range.m_start;
+      unsigned int col_diff = (int_diff >> set->range_bits);
+      if (col_diff < (1U << set->range_bits))
+	{
+	  source_location packed = locus | col_diff;
+	  set->num_optimized_ranges++;
+#if DEBUG_PACKING
+	  fprintf (stderr, "  optimized to %x\n", packed);
+#endif
+	  return packed;
+	}
+    }
+
+  /* We can also compactly store the reserved locations
+     when locus == start == finish (and data is NULL).  */
+  if (locus < RESERVED_LOCATION_COUNT
+      && locus == src_range.m_start
+      && locus == src_range.m_finish
+      && !data)
+    {
+#if DEBUG_PACKING
+      fprintf (stderr, "  using reserved location: %x\n", locus);
+#endif
+      return locus;
+    }
+
+#if DEBUG_PACKING
+  fprintf (stderr, "  unoptimized\n");
+#endif
+  if (!data)
+    set->num_unoptimized_ranges++;
+
   lb.locus = locus;
   lb.src_range = src_range;
   lb.data = data;
@@ -184,13 +279,38 @@ get_location_from_adhoc_loc (struct line_maps *set, source_location loc)
   return set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].locus;
 }
 
-source_range
+static source_range
 get_range_from_adhoc_loc (struct line_maps *set, source_location loc)
 {
   linemap_assert (IS_ADHOC_LOC (loc));
   return set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].src_range;
 }
 
+/* Get the source_range of location LOC, either from the ad-hoc
+   lookaside table, or embedded inside LOC itself.  */
+
+source_range
+get_range_from_loc (struct line_maps *set,
+		    source_location loc)
+{
+  if (IS_ADHOC_LOC (loc))
+    return get_range_from_adhoc_loc (set, loc);
+
+  /* For ordinary maps, extract packed range.  */
+  if (loc >= RESERVED_LOCATION_COUNT
+      && loc < LINEMAPS_MACRO_LOWEST_LOCATION (set)
+      && loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    {
+      source_range result;
+      int offset = loc & ((1 << set->range_bits) - 1);
+      result.m_start = loc - offset;
+      result.m_finish = result.m_start + (offset << set->range_bits);
+      return result;
+    }
+
+  return source_range::from_location (loc);
+}
+
 /* Finalize the location_adhoc_data structure.  */
 void
 location_adhoc_data_fini (struct line_maps *set)
@@ -333,7 +453,12 @@ const struct line_map *
 linemap_add (struct line_maps *set, enum lc_reason reason,
 	     unsigned int sysp, const char *to_file, linenum_type to_line)
 {
-  source_location start_location = set->highest_location + 1;
+  source_location start_location =
+    set->highest_location + (1 << set->range_bits);
+
+  if (set->range_bits)
+    start_location &= ~((1 << set->range_bits) - 1);
+  linemap_assert (0 == (start_location & ((1 << set->range_bits) - 1)));
 
   linemap_assert (!(LINEMAPS_ORDINARY_USED (set)
 		    && (start_location
@@ -408,11 +533,13 @@ linemap_add (struct line_maps *set, enum lc_reason reason,
     }
 
   map->sysp = sysp;
+  linemap_assert (pure_location_p (set, start_location));
   map->start_location = start_location;
   map->to_file = to_file;
   map->to_line = to_line;
   LINEMAPS_ORDINARY_CACHE (set) = LINEMAPS_ORDINARY_USED (set) - 1;
   map->column_bits = 0;
+  map->range_bits = set->range_bits;
   set->highest_location = start_location;
   set->highest_line = start_location;
   set->max_column_hint = 0;
@@ -569,7 +696,8 @@ linemap_line_start (struct line_maps *set, linenum_type to_line,
 	  && line_delta * ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map) > 1000)
       || (max_column_hint >= (1U << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map)))
       || (max_column_hint <= 80
-	  && ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map) >= 10)
+	  && (ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map)
+	      - set->range_bits) >= 10)
       || (highest > LINE_MAP_MAX_LOCATION_WITH_COLS
 	  && (set->max_column_hint || highest >= LINE_MAP_MAX_SOURCE_LOCATION)))
     add_map = true;
@@ -594,6 +722,7 @@ linemap_line_start (struct line_maps *set, linenum_type to_line,
 	  while (max_column_hint >= (1U << column_bits))
 	    column_bits++;
 	  max_column_hint = 1U << column_bits;
+	  column_bits += set->range_bits;
 	}
       /* Allocate the new line_map.  However, if the current map only has a
 	 single line we can sometimes just increase its column_bits instead. */
@@ -612,18 +741,21 @@ linemap_line_start (struct line_maps *set, linenum_type to_line,
 	      << column_bits));
     }
   else
-    r = highest - SOURCE_COLUMN (map, highest)
-      + (line_delta << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map));
+    r = (set->highest_line
+	 + (line_delta << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (map)));
 
   /* Locations of ordinary tokens are always lower than locations of
      macro tokens.  */
   if (r >= LINEMAPS_MACRO_LOWEST_LOCATION (set))
     return 0;
 
+  linemap_assert (pure_location_p (set, r)
+		  || r >= LINE_MAP_MAX_LOCATION_WITH_COLS);
   set->highest_line = r;
   if (r > set->highest_location)
     set->highest_location = r;
   set->max_column_hint = max_column_hint;
+  linemap_assert (SOURCE_LINE (map, r) == to_line);
   return r;
 }
 
@@ -654,7 +786,7 @@ linemap_position_for_column (struct line_maps *set, unsigned int to_column)
 	  r = linemap_line_start (set, SOURCE_LINE (map, r), to_column + 50);
 	}
     }
-  r = r + to_column;
+  r = r + (to_column << set->range_bits);
   if (r >= set->highest_location)
     set->highest_location = r;
   return r;
@@ -673,7 +805,8 @@ linemap_position_for_line_and_column (const line_map_ordinary *ord_map,
   return (MAP_START_LOCATION (ord_map)
 	  + ((line - ORDINARY_MAP_STARTING_LINE_NUMBER (ord_map))
 	     << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (ord_map))
-	  + (column & ((1 << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (ord_map)) - 1)));
+	  + ((column & ((1 << ORDINARY_MAP_NUMBER_OF_COLUMN_BITS (ord_map)) - 1))
+	     << ord_map->range_bits));
 }
 
 /* Encode and return a source_location starting from location LOC and
-- 
1.8.5.3

