Thanks a lot for the review and comments.
I am on a short vacation this week. And will respond to all your questions 
after I am back the end of this week.

Qing

> On May 19, 2025, at 06:44, Richard Biener <richard.guent...@gmail.com> wrote:
> 
> On Fri, May 16, 2025 at 3:34 PM Qing Zhao <qing.z...@oracle.com> wrote:
>> 
>> 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.
> 
> Thanks for working on this.  I'm pasting my review notes on [1/3] below.
> 
> 
> ove histories are allocated from a global obstack - it seems they are
> never released, just the association between stmt and history is
> eventually broken by remove_move_history?  Do you have any statistics
> on the memory usage?  Might be an interesting bit to dump with
> -fmem-report (the size of the obstack).  Likewise the statistics
> on the hash-map are interesting.
> 
> static bool
> +is_move_history_existed (location_t cond_location, bool is_true_path,
> +                        enum move_reason
> 
> that's a bit of an odd name - 'move_history_exists_p'?  What are
> we supposed to do on a duplicate?  This is a linear search - how
> many do we accumulate in practice?  Does it make sense to put a
> limit on the number of transforms we record?
> 
> static gimple *
> +get_cond_stmt (edge entry, bool is_destination, bool *is_true_path)
> +{
> 
> I don't quite understand the is_destination parameter, likewise
> for the two APIs using this function?  Usually the control edge
> and the BB the stmt is in can be different from edge->dest, and
> I'd expect the callers to know, so I wonder why we would want to
> search here?  In particular the use in path isolation for PHI
> arguments passes in the edge of the problematic PHI arg but
> the first reached gcond * might not be the full controlling
> condition when there is more than two PHI arguments.  Not to
> mention a switch statement might also be the control stmt.
> While this can be extended in future I'd like the caller
> to compute the control dependence that's relevant - using
> an edge (or in future a vector of edges) is fine, it should
> be the edge outgoing from the control stmt.
> 
> @opindex fdiagnostics-details
> +@item -fdiagnostics-details
> 
> Not entirely happy with the name, we have -fdiagnostic-show-*
> options related to events, so maybe -fdiagnostic-show-details
> or -fdiagnostic-show-context?
> 
> @@ -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);
> 
> I'd say make remove_move_history cope with the stmt not being in the
> hash is better, so just call that function here, without get_*()
> 
> @ -712,6 +756,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);
> +       }
> 
> so you represent a more complex control condition by a series of sinkings?
> I guess that's possible in this case at least.  I'll note the
> get_edge_chain function seems to be quite simplistic and likely
> prone to record only a part of the sinkings.
> 
> +         /* 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);
> +           }
> 
> I'm taking this one as example - why are we setting a move history
> on the not copied part?  Shouldn't that be still within the original
> constraints / location?  Otherwise why would(nt) we show the location
> and direction of all control stmts of a stmts we emit a diagnostic on?
> 
> 
> I think it makes sense to squash [3/3] into this patch.
> 
> There is documentation about diagnostics in doc/ux.texi, it would be
> nice to mention move-history there.
> 
> 
> Thanks,
> Richard.
> 
>>        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 --git a/gcc/Makefile.in b/gcc/Makefile.in
>> index 72d132207c0..38dfb688e60 100644
>> --- a/gcc/Makefile.in
>> +++ b/gcc/Makefile.in
>> @@ -1451,6 +1451,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 0e50305dde8..081537c349a 100644
>> --- a/gcc/common.opt
>> +++ b/gcc/common.opt
>> @@ -1613,6 +1613,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.z...@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.z...@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 ee7180110e1..ca04f35d6a1 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
>> @@ -332,6 +332,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}.
>> @@ -5687,6 +5688,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 3af672bf0b9..f1d7ca9c78e 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 4e20b4cc371..811164565bb 100644
>> --- a/gcc/gimple-pretty-print.cc
>> +++ b/gcc/gimple-pretty-print.cc
>> @@ -44,6 +44,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.  */
>> @@ -2734,6 +2735,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 ca1daf1d168..14c86590b17 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 7e457b5168b..7652c222676 100644
>> --- a/gcc/toplev.cc
>> +++ b/gcc/toplev.cc
>> @@ -42,6 +42,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.  */
>> @@ -2436,6 +2437,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 959e0d5c6be..0b3441e894c 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 "tree-eh.h"
>> #include "tree-ssa-live.h"
>> #include "tree-dfa.h"
>> +#include "diagnostic-move-history.h"
>> 
>> /* TODO:
>>    1. Sinking store only using scalar promotion (IE without moving the RHS):
>> @@ -655,6 +656,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
>> @@ -712,6 +756,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 4e5c7566857..789ca1d9422 100644
>> --- a/gcc/tree-ssa-threadupdate.cc
>> +++ b/gcc/tree-ssa-threadupdate.cc
>> @@ -36,6 +36,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
>> @@ -1341,6 +1342,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);
>> @@ -2419,6 +2431,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
>> --
>> 2.31.1


Reply via email to