Hi, Richard,

Really appreciate for your suggestions.

> On May 30, 2025, at 05:22, Richard Biener <richard.guent...@gmail.com> wrote:
> 
> On Fri, May 23, 2025 at 10:49 PM Qing Zhao <qing.z...@oracle.com> wrote:
>> 
>> Hi, Richard,
>> 
>> Thanks a lot for your comments and questions.
>> Please see my answers embedded below:
>> 
>>> 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?
>> 
>> They are supposed to be released by the following routine:
>> 
>> +void
>> +move_history_finalize (void)
>> +{
>> +  if (move_history_map)
>> +    {
>> +      delete move_history_map;
>> +      move_history_map = NULL;
>> +    }
>> +  obstack_free (&move_history_obstack, NULL);
>> +  return;
>> +}
>> 
>> Is the above “obstack_free” the proper utility routine that are used to free 
>> the whole
>> memory allocated for the“move_history_obstack”?
>> 
>> If not, please advise on how to free the obstacle variables.
> 
> Yes, the above releases them all - I was pointing out that when
> an entry becomes unreachable via remove_move_history the
> entry doesn't get re-used but instead is only released at the end
> of compilation.

Okay, I see. 
So in the following code:

@@ -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);

+/* Remove the move history for STMT.  */
+
+void
+remove_move_history (gimple *stmt)
+{
+  if (!move_history_map)
+    return;
+  move_history_map->remove (stmt);
+  return;
+}

What’s in my original thought is, whenever one gimple stmt is deleted, We just 
break the
 association between It and its move_history,  the real move_history data 
structure will
be released by obstack_free anyway later. 

Yes, you are right, if there are many gimple stmts with move_histories are 
deleted during 
the optimization, it’s better to delete the associated move_history data 
structure too in
“remove_move_history”. 
Will update it.

> 
>>> Do you have any statistics
>>> on the memory usage?
>> No.
>>> Might be an interesting bit to dump with
>>> -fmem-report (the size of the obstack).  Likewise the statistics
>>> on the hash-map are interesting.
>> 
>> Are you suggesting to add the memory usage statistics for both 
>> “move_history_obstack” and
>> “move_history_map” to -fmem_report?
>> If so, I will study a little bit on how to do this. And then add this part.
> 
> Yes.  Basically to get an idea how bad the lack of re-use is.  I could imagine
> the obstack could be per function which might improve peak memory use,
> or adding a ref counter and keeping a freelist.  Without data it's hard to 
> tell
> whether this is worth the trouble.

Sure, I will collect these statistics with GCC build as you suggested. (But 
maybe I don’t need to
collect them anymore due to the new approach you suggested-:). 
> 
>> 
>>> 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'?
>> 
>> Okay, will update the name as you suggested.
>> 
>>> 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?
>> 
>> Yeah, all are good questions.
>> I cannot imagine a concrete situation in which the duplication might happen,
>> But I assume that it (the duplication) might happen (might due to 
>> interaction among
>> different transformations within loops?).
>> Shall we collect some statistics on the potential duplications? And also the 
>> length of the
>> move_history chain?  And then decide the solution to the above questions?
> 
> Yes.
> 
>> If so, do you have a suggestion on what benchmark I should use to collect 
>> such
>> Data? Is SPECCPU good enough for such purpose?
> 
> I think even data from a GCC build is useful enough.
Okay, will collect all the above data with GCC build if needed. (But looks like 
I don’t need 
To do this if the new approach works well)
> 
>>> 
>>> 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?
>> 
>> The major purpose of “is_destination” is:
>> the value of “is_destination” is used to compute  the value of 
>> “is_true_path” as:
>> 
>> +  *is_true_path = !(is_branch_taken ^ is_destination);
>> 
>> As now, only GIMPLE_COND is considered, GIMPLE_SWITCH is not handled.
>> We can extend this work to include GIMPLE_SWITCH if needed. What’s your
>> Suggestions on this?
>> 
>> Since only GIMPLE_COND is handled now,  there will be  two places where the
>> code might move to, one is the “destination” of the current edge, one is not 
>> the
>> “destination” of the current edge, i.e, its on the destination of the new 
>> edge.
>> 
>> Then based on whether the current edge is a taken edge and whether the code 
>> is
>> moved to the destination of this current edge, we can compute whether the 
>> code
>> moved to the destination of this current edge is on the TRUE path of the COND
>> Stmt.  and report such information to the end users.  (Please see my 
>> explanation
>> on the isolate path transformation below, there is a graph to explain this 
>> -:).
>> 
>> Hope this is clear, if it is still not clear, let me know, I will try to 
>> explain more.
> 
> Hmm, I'm not sure yet ;)
> 
>> 
>>> Usually the control edge
>> 
>> What do you mean by the control edge?
> 
> The control edge is the edge out of the control stmt that decides whether the
> outgoing edge (which IIRC is passed here) is executed.  Consider
> 
> if (...)
>   \ e'
>    \
>     if (..)
>     /  \
>   A   B
>     \  /
>     C
>      | e
> 
> here 'e' would be the outgoing edge, but rather than the if() before
> the diamond, the if() controlling e' is the control edge.

Yes, that’s what in my mind too. 
> 
>>> 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?
>> 
>> Not quite understand the question.  Is my graph below for the isolate path
>> transformation answer this question?
> 
> I think how it actually works depends on the code transform, but see one of
> my last comments where the abstraction presented is maybe not best.
> 
>>> 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.
>> 
>> For path isolation, I have the following patch:
>> 
>> @@ -170,6 +171,16 @@ isolate_path (basic_block bb, basic_block duplicate, 
>> edge e.
>>     }
>>   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);
>> +    }
>> +
>> 
>> From my understanding:
>> the arguments “bb”, “duplicate” and “e” of the routine “isolate_path” can be 
>> illustrated as following:
>> 
>> OLD:
>>                 cond_bb
>>                \      |      /
>>             e1  \   | e /  e2
>>                      V
>>                     bb
>> 
>> NEW:
>>                      cond_bb
>>        \      /          |
>>      e1\  /  e2      | e
>>           V            V
>>        bb          duplicate
>> 
>> Is the above transformation correct?
> 
> more-or-less, yes.  path isolation only handles the case where there's
> a gimple-cond on the incoming
> edge to BB that will cause UB in BB.  So we want to remove that edge
> from BB and instead have a
> copy which we can prune at/after the UB stmt.

Yes. That’s right. 
> 
>> If yes, then the “duplicate” is the destination of the edge “e”, but the 
>> “bb” is not the destination of the edge “e”.
>> Therefore:
>> 
>> +      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);
>> 
>> Then, the above two calls to the routine “set_move_history_to_stmts_in_bb” 
>> will get the cond stmt “cond” in the
>> source basic block “cond_bb” of the edge “e” if the condition stmt can be 
>> found. And then set the move_history
>> for each stmt in the block “bb” or “duplicate” properly.
>> (If the condition stmt cannot be found, we give up).
>> 
>> Is the above correct?
> 
> So what I wondered is why we record the stmts in 'bb' to be "moved"?  Or why 
> we
> record the stmts in 'duplicate' to be "moved"?  That is, what's the
> condition that's
> no longer visible in the IL that we want to preserve this way?
> 
> I think both that in 'bb' 'cond' is false and that in 'duplicate'
> 'cond' is true is still
> obviously present, so it's redundant to note this?  So what we get from 
> marking
> the stmts is just that "stmt somehow participates in an optimization
> where 'cond'
> was involved"?
> 
> That is, I wonder whether if -fdiagnostic-show-context would simply display
> the (first) control stmt (that is still in the IL!) of its execution
> context, whether
> that would fix all the testcases you include?
Oh, yeah, all the above sound very reasonable and natural to me -:) 
I guess that I was misleaded by the compiler transformations….

Maybe Not only the “first” control stmt, tracking back along the CFG to the 
first joint place, i.e:

+       B2
+       /  \
+      V    \
+     B3     \
+     / \     \
+    V   \---->V
+   B4        B7
+

If the diagnostic happens in B4, and there is only one incoming edge to B4, 
will track back to B3 and then back to B2. 
Report the condition on B2, B3, and whether B3 and B4 is on the TRUE/FALSE 
patch of the corresponding edges. 

On the other hand, if the diagnostic happens in B7, since there are multiple 
incoming edges to B7, -fdiagnostic-show-context
will give up on such case.

> 
> Now, when we actually elide a control stmt, that's the point where we lose
> information, but the transforms altered, jump threading, path
> isolation and sinking,
> don't actually do this?
Yes. 
> 
> And if we record the stmt "move" for both edges don't we end up creating
> too much noise that will be perceived as fals positive / misleading when
> we extend the use of this?
> 
> So like, when path isolation isolates a ptr == 0 we should end up with IL
> like
> 
> if (ptr != 0)
>    |   \
>    |     good code
>    |
>  *0 = x;
> 
> where the last stmt is eventually diagnosed.  But the test on ptr
> will have survived, so if we'd diagnose the path to it that would
> help already, no need for any magic stmt duplication/move marking?

Right, that should be enough.

> 
> I'm not saying that for some cases we will need that, but the testcaes
> seem to be too simple to prove this?
> 
> That said, we might be able to do with the 2nd patch improving
> the actual diagnostic but simply by making it show a path
> with -fdiagnostic-show-context?

I will try this approach to see whether it can catch all the cases. 
And whether there is any issue we miss at this moment. 
> 
>>> 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.
>> 
>> Yes, that’s the edge outgoing from the cond 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?
>> 
>> -fdiagnostic-show-context is better?
> 
> I'd think so.
> 
>>> 
>>> @@ -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_*()
>> 
>> Okay.
>>> 
>>> @ -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?
>> 
>> If the sinking happens along a path that contain multiple edges such as the 
>> following
>> B2 to B4:
>> 
>> +/* 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.  */
>> +
>> 
>> Then both the edges B2->B3 and B3->B4 will be recorded. And the sinking from 
>> B2->B3
>> is on the destination of this edge, and then sinking from B3->B4 also is on 
>> the destination
>> of this edge too.
>> Therefore, we have:
>> 
>> +         for (edge entry : edge_chain)
>> +           set_move_history_to_stmt (stmt, entry, true, MOVE_BY_SINK);
>> +       }
>> 
>> 
>>> 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.
>> 
>> Could you please explain a bit more here? Do you mean that the current
>> “get_edge_chain” might miss some more complicate cases? If so, could you
>> please provide more details on what’s the more complicate cases look like?
>> Then I will enhance this routine to handle them.
>>> 
>>> +         /* 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 that the following should answer the above questions:
>> https://gcc.gnu.org/pipermail/gcc-patches/2024-October/666870.html
>> 
>> "
>> The key feature of the compiler transformation that might provide more
>> accurate information to the value-range analysis is: moving a statement
>> from the joint point of one specific condition to this condition's
>> TRUE or FALSE path.
>> 
>> For example, threadjump and isolate-path transformation make a duplicated
>> basic block “Ba’  " of the original basic block "Ba" that on the joint
>> point of a condition "cond", and then move the two basic block “Ba' "
>> and "Ba" to the TRUE path and FALSE path of the condition "cond"; on the
>> otherhand, tree sink transformation just move some of the statements from
>> the joint point of a condition "cond" to one specific path of this
>> condition.
>> 
>> So, the new data structure "move_history" will include the following
>> information:
>> A. the "condition" that triggers the code movement;
>> B. whether the code movement is on the true path of the "condition";
>> C. the "compiler transformation" that triggers the code movement.
>> "
>> 
>> Does the above answer your question?
> 
> Yes, I understand what we record.  But I still wonder if we need this.
> What we do not record reliably is when if (A) eventually is optimized
> to if (1) or if (0) that the stmts on the path that remains are subject
> to A == 1 (or 0).

Yes, I understand what you suggested and I like this idea. That’s much simple
and natural to this problem. 
I will try to implement this approach in the next version to see whether there 
is any
potential issue with it. 

Really appreciate for your suggestions and help. 

FYI, I will be on vacation next week, so will try the new approach when I am 
back
on the week of 6/9. 

Thanks again.

Qing


> Richard.
> 
>>> 
>>> 
>>> I think it makes sense to squash [3/3] into this patch.
>> 
>> Okay, will do that,
>>> 
>>> There is documentation about diagnostics in doc/ux.texi, it would be
>>> nice to mention move-history there.
>> 
>> Okay, will study a little bit and add it.
>> 
>> Thanks again for your time and comments.
>> 
>> Qing
>>> 
>>> 
>>> 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