> On Oct 30, 2024, at 13:48, Sam James <[email protected]> wrote:
>
> Qing Zhao <[email protected]> writes:
>
>> 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];};
>> | ^~~~
>>
>> PR tree-optimization/109071
>>
>> gcc/ChangeLog:
>>
>> * Makefile.in (OBJS): Add diagnostic-move-history.o
>> and move-history-diagnostic-path.o.
>> * gcc/common.opt (fdiagnostics-details): New option.
>> * gcc/doc/invoke.texi (fdiagnostics-details): Add
>> documentation for the new option.
>> * gimple-array-bounds.cc (build_rich_location_with_diagnostic_path):
>> New function.
>> (check_out_of_bounds_and_warn): Add one new parameter. Use rich
>> location with move_history_diagnostic_path for warning_at.
>> (array_bounds_checker::check_array_ref): Use rich location with
>> move_history_diagnostic_path for warning_at.
>> (array_bounds_checker::check_mem_ref): Add one new parameter.
>> Use rich location with move_history_diagnostic_path for warning_at.
>> (array_bounds_checker::check_addr_expr): Use rich location with
>> move_history_diagnostic_path for warning_at.
>> (array_bounds_checker::check_array_bounds): Call check_mem_ref with
>> one more parameter.
>> * gimple-array-bounds.h: Update prototype for check_mem_ref.
>> * 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.
>> * gimple-ssa-warn-restrict.cc (maybe_diag_access_bounds): Use
>> rich location with move_history_diagnostic_path for warning_at.
>> * gimple-ssa-warn-access.cc (warn_string_no_nul): Likewise.
>> (maybe_warn_nonstring_arg): Likewise.
>> (maybe_warn_for_bound): Likewise.
>> (warn_for_access): Likewise.
>> (check_access): Likewise.
>> (pass_waccess::check_strncat): Likewise.
>> (pass_waccess::maybe_check_access_sizes): Likewise.
>> * 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.
>> * move-history-diagnostic-path.cc: New file.
>> * move-history-diagnostic-path.h: New file.
>> * diagnostic-move-history.cc: New file.
>> * diagnostic-move-history.h: New file.
>>
>> gcc/testsuite/ChangeLog:
>>
>> PR tree-optimization/109071
>>
>> * gcc.dg/pr109071.c: New test.
>> * gcc.dg/pr109071_1.c: New test.
>> * gcc.dg/pr109071_2.c: New test.
>> * gcc.dg/pr109071_3.c: New test.
>> * gcc.dg/pr109071_4.c: New test.
>> * gcc.dg/pr109071_5.c: New test.
>> * gcc.dg/pr109071_6.c: New test.
>> ---
>> gcc/Makefile.in | 2 +
>> gcc/common.opt | 4 +
>> gcc/diagnostic-move-history.cc | 264 ++++++++++++++++++++++++++++
>> gcc/diagnostic-move-history.h | 92 ++++++++++
>> gcc/doc/invoke.texi | 7 +
>> gcc/gimple-array-bounds.cc | 75 ++++++--
>> gcc/gimple-array-bounds.h | 2 +-
>> gcc/gimple-iterator.cc | 3 +
>> gcc/gimple-pretty-print.cc | 4 +
>> gcc/gimple-ssa-isolate-paths.cc | 11 ++
>> gcc/gimple-ssa-warn-access.cc | 153 ++++++++++------
>> gcc/gimple-ssa-warn-restrict.cc | 27 +--
>> gcc/move-history-diagnostic-path.cc | 119 +++++++++++++
>> gcc/move-history-diagnostic-path.h | 96 ++++++++++
>> gcc/testsuite/gcc.dg/pr109071.c | 43 +++++
>> gcc/testsuite/gcc.dg/pr109071_1.c | 36 ++++
>> gcc/testsuite/gcc.dg/pr109071_2.c | 50 ++++++
>> gcc/testsuite/gcc.dg/pr109071_3.c | 42 +++++
>> gcc/testsuite/gcc.dg/pr109071_4.c | 41 +++++
>> gcc/testsuite/gcc.dg/pr109071_5.c | 33 ++++
>> gcc/testsuite/gcc.dg/pr109071_6.c | 49 ++++++
>> gcc/toplev.cc | 3 +
>> gcc/tree-ssa-sink.cc | 10 ++
>> gcc/tree-ssa-threadupdate.cc | 25 +++
>> 24 files changed, 1105 insertions(+), 86 deletions(-)
>> create mode 100644 gcc/diagnostic-move-history.cc
>> create mode 100644 gcc/diagnostic-move-history.h
>> create mode 100644 gcc/move-history-diagnostic-path.cc
>> create mode 100644 gcc/move-history-diagnostic-path.h
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_1.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_2.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_3.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_4.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_5.c
>> create mode 100644 gcc/testsuite/gcc.dg/pr109071_6.c
>>
>> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
>> index 059cf2e8f79f..0d119ba46e1b 100644
>> --- a/gcc/Makefile.in
>> +++ b/gcc/Makefile.in
>> @@ -1432,6 +1432,8 @@ OBJS = \
>> df-problems.o \
>> df-scan.o \
>> dfp.o \
>> + diagnostic-move-history.o \
>> + move-history-diagnostic-path.o \
>> digraph.o \
>> dojump.o \
>> dominance.o \
>> diff --git a/gcc/common.opt b/gcc/common.opt
>> index 12b25ff486de..84ebef080143 100644
>> --- a/gcc/common.opt
>> +++ b/gcc/common.opt
>> @@ -1566,6 +1566,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 000000000000..b0e8308dbf6b
>> --- /dev/null
>> +++ b/gcc/diagnostic-move-history.cc
>> @@ -0,0 +1,264 @@
>> +/* Functions to handle move history.
>> + Copyright (C) 2024-2024 Free Software Foundation, Inc.
>
> Just '2024'.
>
>> + Contributed by Qing Zhao <[email protected]>
>> +
>> [...]
>> diff --git a/gcc/diagnostic-move-history.h b/gcc/diagnostic-move-history.h
>> new file mode 100644
>> index 000000000000..cac9cb1e2675
>> --- /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-2024 Free Software Foundation, Inc.
>> + Contributed by Qing Zhao <[email protected]>
>> +
>> + 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_INCLUDED
>> +#define DIAGNOSTIC_MOVE_HISTORY_H_INCLUDED
>
> I think usually the guards lack _INCLUDED.
Just checked GCC’s source code, yes, only “diagnostic-spec.h”,
“simple-predicate-analysis.h” use _INCLUDED to guard.
Other header files do not add _INCLUDED.
I can remove it in the next version.
>
>> +
>> +#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_INCLUDED
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index 987b63601520..8bb7568d0e30 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
>> @@ -324,6 +324,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}.
>> @@ -5609,6 +5610,12 @@ 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.
>> +
>
> Two comments:
> 1) I don't know if we should note here that it might be a bit slower as it
> requires that collection or if it's implied/obvious?
I think that adding such notes should not hurt anything.
>
> 2) Should we mention a list of warnings we know are wired up to it?
> (either here, or in the docs for the warnings themselves)
Yes, this is reasonable to clarify on both sides.
I will update this.
Thanks for the comments and suggestions.
Qing
>
>> @opindex fdiagnostics-parseable-fixits
>> @item -fdiagnostics-parseable-fixits
>> Emit fix-it hints in a machine-parseable format, suitable for consumption
>> [...]
>
> thanks,
> sam