diff mbox series

[v4,1/3,RFC] Provide more contexts for -Warray-bounds, -Wstringop-* warning messages due to code movements from compiler transformation (Part 1) [PR109071, PR85788, PR88771, PR106762, PR108770, PR115274, PR117179]

Message ID 20241105163132.1922052-2-qing.zhao@oracle.com
State New
Headers show
Series Provide more contexts for -Warray-bounds and -Wstringop-* warning messages | expand

Commit Message

Qing Zhao Nov. 5, 2024, 4:31 p.m. UTC
Control this with a new option -fdiagnostics-details.

$ cat t.c
extern void warn(void);
static inline void assign(int val, int *regs, int *index)
{
  if (*index >= 4)
    warn();
  *regs = val;
}
struct nums {int vals[4];};

void sparx5_set (int *ptr, struct nums *sg, int index)
{
  int *val = &sg->vals[index];

  assign(0,    ptr, &index);
  assign(*val, ptr, &index);
}

$ gcc -Wall -O2  -c -o t.o t.c
t.c: In function ‘sparx5_set’:
t.c:12:23: warning: array subscript 4 is above array bounds of ‘int[4]’ [-Warray-bounds=]
   12 |   int *val = &sg->vals[index];
      |               ~~~~~~~~^~~~~~~
t.c:8:18: note: while referencing ‘vals’
    8 | struct nums {int vals[4];};
      |                  ^~~~

In the above, Although the warning is correct in theory, the warning message
itself is confusing to the end-user since there is information that cannot
be connected to the source code directly.

It will be a nice improvement to add more information in the warning message
to report where such index value come from.

In order to achieve this, we add a new data structure "move_history" to record
1. the "condition" that triggers the code movement;
2. whether the code movement is on the true path of the "condition";
3. the "compiler transformation" that triggers the code movement.

Whenever there is a code movement along control flow graph due to some
specific transformations, such as jump threading, path isolation, tree
sinking, etc., a move_history structure is created and attached to the
moved gimple statement.

During array out-of-bound checking or -Wstringop-* warning checking, the
"move_history" that was attached to the gimple statement is used to form
a sequence of diagnostic events that are added to the corresponding rich
location to be used to report the warning message.

This behavior is controled by the new option -fdiagnostics-details
which is off by default.

With this change, by adding -fdiagnostics-details,
the warning message for the above testing case is now:

$ gcc -Wall -O2 -fdiagnostics-details -c -o t.o t.c
t.c: In function ‘sparx5_set’:
t.c:12:23: warning: array subscript 4 is above array bounds of ‘int[4]’ [-Warray-bounds=]
   12 |   int *val = &sg->vals[index];
      |               ~~~~~~~~^~~~~~~
  ‘sparx5_set’: events 1-2
    4 |   if (*index >= 4)
      |      ^
      |      |
      |      (1) when the condition is evaluated to true
......
   12 |   int *val = &sg->vals[index];
      |               ~~~~~~~~~~~~~~~
      |                       |
      |                       (2) out of array bounds here
t.c:8:18: note: while referencing ‘vals’
    8 | struct nums {int vals[4];};
      |                  ^~~~

The change was divided into 3 parts:

Part 1: Add new data structure move_history, record move_history during
	transformation;
Part 2: In warning analysis, Use the new move_history to form a rich
	location with a sequence of events, to report more context info
	of the warnings.
Part 3: Add debugging mechanism for move_history.

	PR tree-optimization/109071
	PR tree-optimization/85788
	PR tree-optimization/88771
	PR tree-optimization/106762
	PR tree-optimization/108770
	PR tree-optimization/115274
	PR tree-optimization/117179

gcc/ChangeLog:

	* Makefile.in (OBJS): Add diagnostic-move-history.o.
	* gcc/common.opt (fdiagnostics-details): New option.
	* gcc/doc/invoke.texi (fdiagnostics-details): Add
	documentation for the new option.
	* gimple-iterator.cc (gsi_remove): (gsi_remove): Remove the move
	history when removing the gimple.
	* gimple-pretty-print.cc (pp_gimple_stmt_1): Emit MV_H marking
	if the gimple has a move_history.
	* gimple-ssa-isolate-paths.cc (isolate_path): Set move history
	for the gimples of the duplicated blocks.
        * tree-ssa-sink.cc (sink_code_in_bb): Create move_history for
	stmt when it is sinked.
	* toplev.cc (toplev::finalize):  Call move_history_finalize.
	* tree-ssa-threadupdate.cc (ssa_redirect_edges): Create move_history
	for stmts when they are duplicated.
	(back_jt_path_registry::duplicate_thread_path): Likewise.
	* diagnostic-move-history.cc: New file.
	* diagnostic-move-history.h: New file.

gcc/testsuite/ChangeLog:

	* gcc.dg/pr117375.c: New test.
---
 gcc/Makefile.in                 |   1 +
 gcc/common.opt                  |   4 +
 gcc/diagnostic-move-history.cc  | 265 ++++++++++++++++++++++++++++++++
 gcc/diagnostic-move-history.h   |  92 +++++++++++
 gcc/doc/invoke.texi             |  11 ++
 gcc/gimple-iterator.cc          |   3 +
 gcc/gimple-pretty-print.cc      |   4 +
 gcc/gimple-ssa-isolate-paths.cc |  11 ++
 gcc/testsuite/gcc.dg/pr117375.c |  13 ++
 gcc/toplev.cc                   |   3 +
 gcc/tree-ssa-sink.cc            |  57 +++++++
 gcc/tree-ssa-threadupdate.cc    |  25 +++
 12 files changed, 489 insertions(+)
 create mode 100644 gcc/diagnostic-move-history.cc
 create mode 100644 gcc/diagnostic-move-history.h
 create mode 100644 gcc/testsuite/gcc.dg/pr117375.c
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index eb578210411..68677b6d90b 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1435,6 +1435,7 @@  OBJS = \
 	df-problems.o \
 	df-scan.o \
 	dfp.o \
+	diagnostic-move-history.o \
 	digraph.o \
 	dojump.o \
 	dominance.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 0b1f1ec26e1..65bc8bdf1c5 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1570,6 +1570,10 @@  fdiagnostics-minimum-margin-width=
 Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
 Set minimum width of left margin of source code when showing source.
 
+fdiagnostics-details
+Common Var(flag_diagnostics_details)
+Collect and print more context information for diagnostics.
+
 fdisable-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdisable-[tree|rtl|ipa]-<pass>=range1+range2	Disable an optimization pass.
diff --git a/gcc/diagnostic-move-history.cc b/gcc/diagnostic-move-history.cc
new file mode 100644
index 00000000000..83d8a42b577
--- /dev/null
+++ b/gcc/diagnostic-move-history.cc
@@ -0,0 +1,265 @@ 
+/* Functions to handle move history.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Qing Zhao <qing.zhao@oracle.com>
+
+   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/>.  */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cfganal.h"
+#include "diagnostic-move-history.h"
+
+/* A mapping from a gimple to a pointer to the move history of it.  */
+static move_history_map_t *move_history_map;
+
+/* Obstack for move history.  */
+static struct obstack move_history_obstack;
+
+/* Create a new move history.  */
+
+move_history_t
+create_move_history (location_t condition,
+		     bool is_true_path,
+		     enum move_reason reason,
+		     move_history_t prev_move)
+{
+  static bool initialized = false;
+
+  if (!initialized)
+    {
+      gcc_obstack_init (&move_history_obstack);
+      initialized = true;
+    }
+
+  move_history_t move_history
+    = (move_history_t) obstack_alloc (&move_history_obstack,
+				      sizeof (struct move_history));
+  move_history->condition = condition;
+  move_history->is_true_path = is_true_path;
+  move_history->reason = reason;
+  move_history->prev_move = prev_move;
+  return move_history;
+}
+
+/* Insert the move history for the gimple STMT assuming the linked list
+   of MV_HISTORY does not have duplications.  It's the caller's
+   responsibility to make sure that the linked list of MV_HISTORY does
+   not have duplications.  */
+
+void
+insert_move_history (gimple *stmt, move_history_t mv_history)
+{
+  if (!move_history_map)
+    move_history_map = new move_history_map_t;
+
+  move_history_map->put (stmt, mv_history);
+  return;
+}
+
+/* Get the move history for the gimple STMT, return NULL when there is
+   no associated move history.  */
+
+move_history_t
+get_move_history (const gimple *stmt)
+{
+  if (!move_history_map)
+    return NULL;
+
+  if (const move_history_t *mv_history_p = move_history_map->get (stmt))
+    return *mv_history_p;
+
+  return NULL;
+}
+
+/* Remove the move history for STMT.  */
+
+void
+remove_move_history (gimple *stmt)
+{
+  if (!move_history_map)
+    return;
+  move_history_map->remove (stmt);
+  return;
+}
+
+/* Check whether the cond_location, is_true_path and reason existed
+ * in the OLD_MOVE_HISTORY.  */
+
+static bool
+is_move_history_existed (location_t cond_location, bool is_true_path,
+			 enum move_reason reason,
+			 move_history_t old_move_history)
+{
+  for (move_history_t cur_ch = old_move_history; cur_ch;
+       cur_ch = cur_ch->prev_move)
+    if ((cur_ch->condition == cond_location)
+	&& (cur_ch->is_true_path == is_true_path)
+	&& (cur_ch->reason == reason))
+      return true;
+
+  return false;
+}
+
+/* Set move history for the gimple STMT.  Return TRUE when a new move
+ * history is created and inserted.  Otherwise return FALSE.  */
+
+bool
+set_move_history (gimple *stmt, location_t cond_location,
+		  bool is_true_path, enum move_reason reason)
+{
+
+  /* First, get the old move history associated with this STMT.  */
+  move_history_t old_mv_history = get_move_history (stmt);
+
+  /* If the current move history is not in the STMT's move history linked
+     list yet, create the new move history, put the old_move_history as the
+     prev_move of it.  */
+  move_history_t new_mv_history = NULL;
+  if (!is_move_history_existed (cond_location, is_true_path,
+				reason, old_mv_history))
+    new_mv_history
+      = create_move_history (cond_location, is_true_path,
+			     reason, old_mv_history);
+
+  /* Insert the move history into the hash map.  */
+  if (new_mv_history)
+    {
+      insert_move_history (stmt, new_mv_history);
+      return true;
+    }
+
+  return false;
+}
+
+/* Reset all state for diagnostic-move-history.cc so that we can rerun the
+   compiler within the same process.  For use by toplev::finalize.  */
+
+void
+move_history_finalize (void)
+{
+  if (move_history_map)
+    {
+      delete move_history_map;
+      move_history_map = NULL;
+    }
+  obstack_free (&move_history_obstack, NULL);
+  return;
+}
+
+/* Given an edge ENTRY and whether the new code will be moved to the
+   destination of the edge, IS_DISTINATION, return the condition
+   statement in the source of the ENTRY if found.  Return NULL otherwise.
+
+   When the condition statement is found, setting IS_TRUE_PATH to true
+   if the destination of the edge is on the true path of the condition.
+
+   IS_TRUE_PATH is only valid when the condition statement is found.
+
+    source
+       |  ENTRY
+       V
+    destination
+
+*/
+
+static gimple *
+get_cond_stmt (edge entry, bool is_destination, bool *is_true_path)
+{
+  /* First, get the condition statement in the source of the
+     edge ENTRY.  */
+  basic_block cond_block = entry->src;
+  gimple *cond_stmt = NULL;
+  gimple_stmt_iterator gsi;
+  *is_true_path = false;
+
+  /* if the cond_block ends with a conditional statement, get it.  */
+  while (!cond_stmt && cond_block)
+    {
+      gsi = gsi_last_bb (cond_block);
+      if (!gsi_end_p (gsi)
+	  && gsi_stmt (gsi)
+	  && (gimple_code (gsi_stmt (gsi)) == GIMPLE_COND))
+	cond_stmt = gsi_stmt (gsi);
+      /* If there is no cond_stmt in the cond_block, search the single_pred
+	of it.  */
+      if (!cond_stmt && single_pred_p (cond_block))
+	{
+	  basic_block prev_cond_block = cond_block;
+	  cond_block = single_pred (cond_block);
+	  entry = find_edge (cond_block, prev_cond_block);
+	}
+      else
+	break;
+    }
+
+  bool is_branch_taken = (cond_stmt && (BRANCH_EDGE (cond_block) == entry));
+  *is_true_path = !(is_branch_taken ^ is_destination);
+
+  return cond_stmt;
+}
+
+/* Set move history to the stmt based on the edge ENTRY and whether this stmt
+   will be in the destination of the ENTRY.
+   The REASON indicates what kind of transformation contributing to the
+   statment movement.  Return TRUE when the move history has been set
+   successfully.  */
+
+bool
+set_move_history_to_stmt (gimple *stmt, edge entry,
+			  bool is_destination, enum move_reason reason)
+{
+  bool is_true_path = false;
+  gimple *cond_stmt = get_cond_stmt (entry, is_destination, &is_true_path);
+
+  if (!cond_stmt)
+    return false;
+
+  set_move_history (stmt, gimple_location (cond_stmt),
+		    is_true_path, reason);
+  return true;
+}
+
+/* Set move history to all the stmts in the basic block BB based on
+   the edge ENTRY and whether this basic block will be the destination
+   of the ENTRY.
+   The REASON indicates what kind of transformation contributing to the
+   statement move.  Return TRUE when the move history has been set
+   successfully.  */
+
+bool
+set_move_history_to_stmts_in_bb (basic_block bb, edge entry,
+				 bool is_destination,
+				 enum move_reason reason)
+{
+  bool is_true_path = false;
+  gimple_stmt_iterator gsi;
+  gimple *cond_stmt = get_cond_stmt (entry, is_destination, &is_true_path);
+
+  if (!cond_stmt)
+    return false;
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+    set_move_history (gsi_stmt (gsi), gimple_location (cond_stmt),
+		      is_true_path, reason);
+
+  return true;
+}
diff --git a/gcc/diagnostic-move-history.h b/gcc/diagnostic-move-history.h
new file mode 100644
index 00000000000..9a58766d544
--- /dev/null
+++ b/gcc/diagnostic-move-history.h
@@ -0,0 +1,92 @@ 
+/* Move history associated with a gimple statement to record its history
+   of movement due to different transformations.
+   The move history will be used to construct events for later diagnostic.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Qing Zhao <qing.zhao@oracle.com>
+
+   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/>.  */
+
+#ifndef DIAGNOSTIC_MOVE_HISTORY_H
+#define DIAGNOSTIC_MOVE_HISTORY_H
+
+#include "hash-map.h"
+#include "line-map.h"
+
+/* An enum for the reason why this move is made.  Right now, there are
+   three reasons, we can add more if needed.  */
+enum move_reason {
+  COPY_BY_THREAD_JUMP,
+  COPY_BY_ISOLATE_PATH,
+  MOVE_BY_SINK,
+  COPY_BY_MAX
+};
+
+/* This data structure records the information when a statement is
+   moved along control flow graph during different transformations.
+   Such information will be used by the later diagnostic messages
+   to report more contexts of the warnings or errors.  */
+struct move_history {
+  /* The location of the condition statement that triggered the code
+     movement.  */
+  location_t condition;
+
+  /* Whether this move is on the TRUE path of the condition.  */
+  bool is_true_path;
+
+  /* The reason for the code movement.  */
+  enum move_reason reason;
+
+  /* This statement itself might be a previous code movement.  */
+  struct move_history *prev_move;
+};
+
+typedef struct move_history *move_history_t;
+
+/* Create a new move history.  */
+extern move_history_t create_move_history (location_t, bool,
+					   enum move_reason, move_history_t);
+
+typedef hash_map<const gimple *, move_history_t> move_history_map_t;
+
+/* Get the move history for the gimple STMT, return NULL when there is
+   no associated move history.  */
+extern move_history_t get_move_history (const gimple *);
+
+/* Remove the move history for STMT from the move_history_map.  */
+extern void remove_move_history (gimple *);
+
+/* Set move history for the gimple STMT.  */
+extern bool set_move_history (gimple *, location_t,
+			      bool, enum move_reason);
+
+/* Reset all state for diagnostic-move-history.cc so that we can rerun the
+   compiler within the same process.  For use by toplev::finalize.  */
+extern void move_history_finalize (void);
+
+/* Set move history to the stmt based on the edge ENTRY and whether this stmt
+   will be in the destination of the ENTRY.  */
+extern bool set_move_history_to_stmt (gimple *, edge,
+				      bool, enum move_reason);
+
+/* Set move history to all the stmts in the basic block based on
+   the entry edge and whether this basic block will be the destination
+   of the entry edge.  */
+extern bool set_move_history_to_stmts_in_bb (basic_block, edge,
+					     bool, enum move_reason);
+
+#endif // DIAGNOSTIC_MOVE_HISTORY_H
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index fd6c0c44709..9b1aeeac707 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -326,6 +326,7 @@  Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-column-origin=@var{origin}
 -fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}
 -fdiagnostics-text-art-charset=@r{[}none@r{|}ascii@r{|}unicode@r{|}emoji@r{]}}
+-fdiagnostics-details
 
 @item Warning Options
 @xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5612,6 +5613,16 @@  left margin.
 This option controls the minimum width of the left margin printed by
 @option{-fdiagnostics-show-line-numbers}.  It defaults to 6.
 
+@opindex fdiagnostics-details
+@item -fdiagnostics-details
+With this option, the compiler collects more context information for
+diagnostics and emits them to the users to provide more hints on how
+the diagnostics come from, at the cost of a slightly slower compilation.
+Currently, The list of the impacted warning options includes:
+@option{-Warray-bounds}, @option{-Wstringop-overflow},
+@option{-Wstringop-overread}, and @option{-Wstringop-truncation}.
+More warning options might be added to this list in future releases.
+
 @opindex fdiagnostics-parseable-fixits
 @item -fdiagnostics-parseable-fixits
 Emit fix-it hints in a machine-parseable format, suitable for consumption
diff --git a/gcc/gimple-iterator.cc b/gcc/gimple-iterator.cc
index 93646262eac..acd6788ae1a 100644
--- a/gcc/gimple-iterator.cc
+++ b/gcc/gimple-iterator.cc
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa.h"
 #include "value-prof.h"
 #include "gimplify.h"
+#include "diagnostic-move-history.h"
 
 
 /* Mark the statement STMT as modified, and update it.  */
@@ -581,6 +582,8 @@  gsi_remove (gimple_stmt_iterator *i, bool remove_permanently)
 	cfun->debug_marker_count--;
       require_eh_edge_purge = remove_stmt_from_eh_lp (stmt);
       gimple_remove_stmt_histograms (cfun, stmt);
+      if (get_move_history (stmt) != NULL)
+	remove_move_history (stmt);
     }
 
   /* Update the iterator and re-wire the links in I->SEQ.  */
diff --git a/gcc/gimple-pretty-print.cc b/gcc/gimple-pretty-print.cc
index 8bd1a6c45c5..ee4e41afeaf 100644
--- a/gcc/gimple-pretty-print.cc
+++ b/gcc/gimple-pretty-print.cc
@@ -45,6 +45,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "cfgloop.h"
 #include "gimple-range.h"
+#include "diagnostic-move-history.h"
 
 /* Disable warnings about quoting issues in the pp_xxx calls below
    that (intentionally) don't follow GCC diagnostic conventions.  */
@@ -2687,6 +2688,9 @@  pp_gimple_stmt_1 (pretty_printer *pp, const gimple *gs, int spc,
       && (flags & TDF_ALIAS))
     dump_ssaname_info (pp, gimple_get_lhs (gs), spc);
 
+  if (get_move_history (gs))
+    pp_printf (pp, "[MV_H] ");
+
   switch (gimple_code (gs))
     {
     case GIMPLE_ASM:
diff --git a/gcc/gimple-ssa-isolate-paths.cc b/gcc/gimple-ssa-isolate-paths.cc
index 55a516987db..a79b512f63b 100644
--- a/gcc/gimple-ssa-isolate-paths.cc
+++ b/gcc/gimple-ssa-isolate-paths.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "cfganal.h"
 #include "intl.h"
+#include "diagnostic-move-history.h"
 
 
 static bool cfg_altered;
@@ -170,6 +171,16 @@  isolate_path (basic_block bb, basic_block duplicate,
     }
   bb->count -= count;
 
+  /* Set the move history for all the stmts in both original and copied
+     basic blocks.  The duplicated block will be the destination of the
+     incoming edge.  */
+  if (flag_diagnostics_details)
+    {
+      set_move_history_to_stmts_in_bb (bb, e, false, COPY_BY_ISOLATE_PATH);
+      set_move_history_to_stmts_in_bb (duplicate, e,
+				       true, COPY_BY_ISOLATE_PATH);
+    }
+
   /* Complete the isolation step by redirecting E to reach DUPLICATE.  */
   e2 = redirect_edge_and_branch (e, duplicate);
   if (e2)
diff --git a/gcc/testsuite/gcc.dg/pr117375.c b/gcc/testsuite/gcc.dg/pr117375.c
new file mode 100644
index 00000000000..beb685afc3b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr117375.c
@@ -0,0 +1,13 @@ 
+/* PR middle-end/117375 ICE with -fdiagnostics-details patch in sink pass.  */ 
+/*    { dg-do compile }
+      { dg-options "-O2 -Wall -fdiagnostics-details" } */
+
+int st, st_0;
+int nbFilledBytes, max;
+void ec_enc_shrink();
+void max_allowed() {
+  int nbAvailableBytes = nbFilledBytes;
+  if (st && st_0)
+    if (max < nbAvailableBytes)
+      ec_enc_shrink();
+}
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 779049674b4..3bd968dbb68 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -43,6 +43,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "coverage.h"
 #include "diagnostic.h"
 #include "pretty-print-urlifier.h"
+#include "diagnostic-move-history.h"
 #include "varasm.h"
 #include "tree-inline.h"
 #include "realmpfr.h"	/* For GMP/MPFR/MPC versions, in print_version.  */
@@ -2437,6 +2438,8 @@  toplev::finalize (void)
   reginfo_cc_finalize ();
   varasm_cc_finalize ();
 
+  move_history_finalize ();
+
   /* save_decoded_options uses opts_obstack, so these must
      be cleaned up together.  */
   obstack_free (&opts_obstack, NULL);
diff --git a/gcc/tree-ssa-sink.cc b/gcc/tree-ssa-sink.cc
index 740f239a4a7..b4d9e991573 100644
--- a/gcc/tree-ssa-sink.cc
+++ b/gcc/tree-ssa-sink.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "tree-eh.h"
 #include "tree-ssa-live.h"
+#include "diagnostic-move-history.h"
 
 /* TODO:
    1. Sinking store only using scalar promotion (IE without moving the RHS):
@@ -654,6 +655,49 @@  sink_common_stores_to_bb (basic_block bb)
   return todo;
 }
 
+/* Get the edge chain from FROM_BB to TO_BB, FROM_BB dominates TO_BB.
+	B2
+       /  \
+      V    \
+     B3     \
+     / \     \
+    V   \---->V
+   B4	      B7
+
+   In the above CFG, FROM_BB is B2, TO_BB is B4.  This routine
+   will locate two edges, B2->B3, and B3->B4 and return a vector
+   with these two edges.
+   This routine will return an empty vector if the control flow
+   edge chain from FROM_BB to TO_BB is too complicate.  */
+
+static auto_vec<edge>
+get_edge_chain (basic_block from_bb, basic_block to_bb)
+{
+  auto_vec<edge> rev_edge_chain, edge_chain;
+  basic_block imm_dom_bb;
+  edge e;
+  unsigned int i;
+  /* Backtracing from TO_BB to FROM_BB through the dominator tree.  */
+  do
+    {
+      imm_dom_bb = get_immediate_dominator (CDI_DOMINATORS, to_bb);
+      if (!imm_dom_bb)
+	return edge_chain;
+      e = find_edge (imm_dom_bb, to_bb);
+      if (!e)
+	return edge_chain;
+      gcc_assert (e);
+      rev_edge_chain.safe_push (e);
+      to_bb = imm_dom_bb;
+    }
+  while (imm_dom_bb != from_bb);
+
+  /* The order of the edges need to be reverted.  */
+  FOR_EACH_VEC_ELT_REVERSE (rev_edge_chain, i, e)
+    edge_chain.safe_push (e);
+  return edge_chain;
+}
+
 /* Perform code sinking on BB */
 
 static unsigned
@@ -711,6 +755,19 @@  sink_code_in_bb (basic_block bb, virtual_operand_live &vop_live)
 		   bb->index, (gsi_bb (togsi))->index);
 	}
 
+      /* Set the move history for the stmt that is sinked from BB to
+	 gsi_bb (togsi). This stmt is on the path from BB to
+	 gsi_bb (togsi).  */
+      if (flag_diagnostics_details)
+	{
+	  /* BB might not be the immediate predecessor of gsi_bb (togsi),
+	     get the edge chain from BB to gsi_bb (togsi).  */
+	  auto_vec<edge> edge_chain = get_edge_chain (bb, gsi_bb (togsi));
+
+	  for (edge entry : edge_chain)
+	    set_move_history_to_stmt (stmt, entry, true, MOVE_BY_SINK);
+	}
+
       /* Update virtual operands of statements in the path we
          do not sink to.  */
       if (gimple_vdef (stmt))
diff --git a/gcc/tree-ssa-threadupdate.cc b/gcc/tree-ssa-threadupdate.cc
index c88cc1d6aac..4f5a878686f 100644
--- a/gcc/tree-ssa-threadupdate.cc
+++ b/gcc/tree-ssa-threadupdate.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "tree-vectorizer.h"
 #include "tree-pass.h"
+#include "diagnostic-move-history.h"
 
 /* Given a block B, update the CFG and SSA graph to reflect redirecting
    one or more in-edges to B to instead reach the destination of an
@@ -1342,6 +1343,17 @@  ssa_redirect_edges (struct redirection_data **slot,
 	{
 	  edge e2;
 
+	  /* Set the move history for all the stmts in both original and copied
+	     basic blocks.  The duplicated block will be the destination of the
+	     incoming edge.  */
+	  if (flag_diagnostics_details)
+	    {
+	      set_move_history_to_stmts_in_bb (e->dest, e, false,
+					       COPY_BY_THREAD_JUMP);
+	      set_move_history_to_stmts_in_bb (rd->dup_blocks[0], e,
+					       true, COPY_BY_THREAD_JUMP);
+	    }
+
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    fprintf (dump_file, "  Threaded jump %d --> %d to %d\n",
 		     e->src->index, e->dest->index, rd->dup_blocks[0]->index);
@@ -2420,6 +2432,19 @@  back_jt_path_registry::duplicate_thread_path (edge entry,
   copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop,
 	    split_edge_bb_loc (entry), false);
 
+  /* Set the move history for all the stmts in both original and copied
+     basic blocks.  The copied regions will be the destination of the
+     entry edge.  */
+  for (i = 0; i < n_region; i++)
+    if (flag_diagnostics_details)
+      {
+	set_move_history_to_stmts_in_bb (region[i], entry, false,
+					 COPY_BY_THREAD_JUMP);
+	set_move_history_to_stmts_in_bb (region_copy[i], entry,
+					 true, COPY_BY_THREAD_JUMP);
+      }
+
+
   /* Fix up: copy_bbs redirects all edges pointing to copied blocks.  The
      following code ensures that all the edges exiting the jump-thread path are
      redirected back to the original code: these edges are exceptions