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.
+ 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/>. */
+
+#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 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
+
+#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.
+
@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-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 1637a2fc4f49..50df91a63d9f 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -31,6 +31,9 @@ along with GCC; see the file COPYING3. If not see
#include "tree-dfa.h"
#include "fold-const.h"
#include "diagnostic-core.h"
+#include "simple-diagnostic-path.h"
+#include "diagnostic-move-history.h"
+#include "move-history-diagnostic-path.h"
#include "intl.h"
#include "tree-vrp.h"
#include "alloc-pool.h"
@@ -262,6 +265,7 @@ get_up_bounds_for_array_ref (tree ref, tree *decl,
static bool
check_out_of_bounds_and_warn (location_t location, tree ref,
+ gimple *stmt,
tree low_sub_org, tree low_sub, tree up_sub,
tree up_bound, tree up_bound_p1,
const irange *vr,
@@ -280,9 +284,13 @@ check_out_of_bounds_and_warn (location_t location, tree
ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %E is outside array"
" bounds of %qT", low_sub_org, artype);
+ }
}
if (warned)
@@ -299,10 +307,14 @@ check_out_of_bounds_and_warn (location_t location, tree
ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript [%E, %E] is outside "
"array bounds of %qT",
low_sub, up_sub, artype);
+ }
}
}
else if (up_bound
@@ -313,18 +325,26 @@ check_out_of_bounds_and_warn (location_t location, tree
ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %E is above array bounds of %qT",
up_sub, artype);
+ }
}
else if (TREE_CODE (low_sub) == INTEGER_CST
&& tree_int_cst_lt (low_sub, low_bound))
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %E is below array bounds of %qT",
low_sub, artype);
+ }
}
return warned;
}
@@ -388,21 +408,24 @@ array_bounds_checker::check_array_ref (location_t
location, tree ref,
}
}
- warned = check_out_of_bounds_and_warn (location, ref,
+ warned = check_out_of_bounds_and_warn (location, ref, stmt,
low_sub_org, low_sub, up_sub,
up_bound, up_bound_p1, &vr,
ignore_off_by_one, warn_array_bounds,
&out_of_bound);
-
if (!warned && sam == special_array_member::int_0)
- warned = warning_at (location, OPT_Wzero_length_bounds,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Wzero_length_bounds,
(TREE_CODE (low_sub) == INTEGER_CST
? G_("array subscript %E is outside the bounds "
"of an interior zero-length array %qT")
: G_("array subscript %qE is outside the bounds "
"of an interior zero-length array %qT")),
low_sub, artype);
+ }
if (warned && dump_file && (dump_flags & TDF_DETAILS))
{
@@ -419,8 +442,10 @@ array_bounds_checker::check_array_ref (location_t
location, tree ref,
|| sam == special_array_member::trail_n)
&& DECL_NOT_FLEXARRAY (afield_decl))
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
bool warned1
- = warning_at (location, OPT_Wstrict_flex_arrays,
+ = warning_at (richloc, OPT_Wstrict_flex_arrays,
"trailing array %qT should not be used as "
"a flexible array member",
artype);
@@ -478,6 +503,7 @@ array_bounds_checker::check_array_ref (location_t location,
tree ref,
bool
array_bounds_checker::check_mem_ref (location_t location, tree ref,
+ gimple *stmt,
bool ignore_off_by_one)
{
if (warning_suppressed_p (ref, OPT_Warray_bounds_))
@@ -580,16 +606,24 @@ array_bounds_checker::check_mem_ref (location_t location,
tree ref,
if (lboob)
{
if (offrange[0] == offrange[1])
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %wi is outside array bounds "
"of %qT",
offrange[0].to_shwi (), reftype);
+ }
else
- warned = warning_at (location, OPT_Warray_bounds_,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript [%wi, %wi] is outside "
"array bounds of %qT",
offrange[0].to_shwi (),
offrange[1].to_shwi (), reftype);
+ }
}
else if (uboob && !ignore_off_by_one)
{
@@ -599,8 +633,9 @@ array_bounds_checker::check_mem_ref (location_t location,
tree ref,
it were an untyped array of bytes. */
backtype = build_array_type_nelts (unsigned_char_type_node,
aref.sizrng[1].to_uhwi ());
-
- warned = warning_at (location, OPT_Warray_bounds_,
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %<%T[%wi]%> is partly "
"outside array bounds of %qT",
axstype, offrange[0].to_shwi (), backtype);
@@ -623,7 +658,9 @@ array_bounds_checker::check_mem_ref (location_t location,
tree ref,
{
HOST_WIDE_INT tmpidx = (aref.offmax[i] / eltsize).to_shwi ();
- if (warning_at (location, OPT_Warray_bounds_,
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ if (warning_at (richloc, OPT_Warray_bounds_,
"intermediate array offset %wi is outside array bounds "
"of %qT", tmpidx, reftype))
{
@@ -656,7 +693,7 @@ array_bounds_checker::check_addr_expr (location_t location,
tree t,
ignore_off_by_one = false;
}
else if (TREE_CODE (t) == MEM_REF)
- warned = check_mem_ref (location, t, ignore_off_by_one);
+ warned = check_mem_ref (location, t, stmt, ignore_off_by_one);
if (warned)
suppress_warning (t, OPT_Warray_bounds_);
@@ -702,7 +739,9 @@ array_bounds_checker::check_addr_expr (location_t location,
tree t,
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warned = warning_at (location, OPT_Warray_bounds_,
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %wi is below "
"array bounds of %qT",
idx.to_shwi (), TREE_TYPE (tem));
@@ -716,7 +755,9 @@ array_bounds_checker::check_addr_expr (location_t location,
tree t,
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warned = warning_at (location, OPT_Warray_bounds_,
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (location, stmt);
+ warned = warning_at (richloc, OPT_Warray_bounds_,
"array subscript %wu is above "
"array bounds of %qT",
idx.to_uhwi (), TREE_TYPE (tem));
@@ -811,7 +852,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int
*walk_subtree,
warned = checker->check_array_ref (location, t, wi->stmt,
false/*ignore_off_by_one*/);
else if (TREE_CODE (t) == MEM_REF)
- warned = checker->check_mem_ref (location, t,
+ warned = checker->check_mem_ref (location, t, wi->stmt,
false /*ignore_off_by_one*/);
else if (TREE_CODE (t) == ADDR_EXPR)
{
diff --git a/gcc/gimple-array-bounds.h b/gcc/gimple-array-bounds.h
index aa7ca8e9730f..2d1d48d1e945 100644
--- a/gcc/gimple-array-bounds.h
+++ b/gcc/gimple-array-bounds.h
@@ -33,7 +33,7 @@ public:
private:
static tree check_array_bounds (tree *tp, int *walk_subtree, void *data);
bool check_array_ref (location_t, tree, gimple *, bool ignore_off_by_one);
- bool check_mem_ref (location_t, tree, bool ignore_off_by_one);
+ bool check_mem_ref (location_t, tree, gimple *, bool ignore_off_by_one);
void check_addr_expr (location_t, tree, gimple *);
void get_value_range (irange &r, const_tree op, gimple *);
diff --git a/gcc/gimple-iterator.cc b/gcc/gimple-iterator.cc
index 93646262eac5..acd6788ae1a9 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 01d7c9f6eebd..4d9afa11622e 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. */
@@ -2686,6 +2687,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 55a516987dbe..a79b512f63bd 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/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 950d96bf9d62..4ceb0a9838c0 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -56,6 +56,7 @@
#include "attr-fnspec.h"
#include "pointer-query.h"
#include "pretty-print-markup.h"
+#include "move-history-diagnostic-path.h"
/* Return true if tree node X has an associated location. */
@@ -168,17 +169,20 @@ warn_string_no_nul (location_t loc, GimpleOrTree expr,
const char *fname,
if (expr)
{
tree func = get_callee_fndecl (expr);
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, expr);
+
if (bndrng)
{
if (wi::ltu_p (maxsiz, bndrng[0]))
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD specified bound %s exceeds "
"maximum object size %E",
func, bndstr, maxobjsize);
else
{
bool maybe = wi::to_wide (size) == bndrng[0];
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
exact
? G_("%qD specified bound %s exceeds "
"the size %E of unterminated array")
@@ -193,7 +197,7 @@ warn_string_no_nul (location_t loc, GimpleOrTree expr,
const char *fname,
}
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD argument missing terminating nul",
func);
}
@@ -485,14 +489,17 @@ maybe_warn_nonstring_arg (tree fndecl, GimpleOrTree exp)
tree maxobjsize = max_object_size ();
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+
bool warned = false;
if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (richloc, OPT_Wstringop_overread,
"%qD specified bound %E "
"exceeds maximum object size %E",
fndecl, bndrng[0], maxobjsize);
else
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (richloc, OPT_Wstringop_overread,
"%qD specified bound [%E, %E] "
"exceeds maximum object size %E",
fndecl, bndrng[0], bndrng[1],
@@ -639,20 +646,22 @@ maybe_warn_nonstring_arg (tree fndecl, GimpleOrTree exp)
auto_diagnostic_group d;
if (wi::ltu_p (asize, wibnd))
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
if (bndrng[0] == bndrng[1])
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than the specified "
"bound %wu",
fndecl, argno + 1, wibnd.to_uhwi ());
else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than "
"the specified bound [%E, %E]",
fndecl, argno + 1, bndrng[0], bndrng[1]);
else
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> may be smaller than "
"the specified bound [%E, %E]",
@@ -724,16 +733,18 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
auto_diagnostic_group d;
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
if (bndrng[0] == bndrng[1])
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound %E may "
"exceed maximum object size %E")
: G_("%qD specified bound %E "
"exceeds maximum object size %E")),
func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound %E may "
"exceed maximum object size %E")
@@ -742,7 +753,7 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
bndrng[0], maxobjsize));
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may "
"exceed maximum object size %E")
@@ -750,7 +761,7 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
"exceeds maximum object size %E")),
func,
bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound [%E, %E] may "
"exceed maximum object size %E")
@@ -761,37 +772,45 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
else if (!size || tree_int_cst_le (bndrng[0], size))
return false;
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+ warned = (func
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound %E may exceed "
"source size %E")
: G_("%qD specified bound %E exceeds "
"source size %E")),
func, bndrng[0], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound %E may exceed "
"source size %E")
: G_("specified bound %E exceeds "
"source size %E")),
bndrng[0], size));
+ }
else
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+ warned = (func
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may "
"exceed source size %E")
: G_("%qD specified bound [%E, %E] exceeds "
"source size %E")),
func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound [%E, %E] may exceed "
"source size %E")
: G_("specified bound [%E, %E] exceeds "
"source size %E")),
bndrng[0], bndrng[1], size));
+ }
if (warned)
{
if (pad && pad->src.ref
@@ -816,16 +835,18 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
}
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
if (bndrng[0] == bndrng[1])
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified size %E may "
"exceed maximum object size %E")
: G_("%qD specified size %E "
"exceeds maximum object size %E")),
func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified size %E may exceed "
"maximum object size %E")
@@ -834,14 +855,14 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
bndrng[0], maxobjsize));
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified size between %E and %E "
"may exceed maximum object size %E")
: G_("%qD specified size between %E and %E "
"exceeds maximum object size %E")),
func, bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified size between %E and %E "
"may exceed maximum object size %E")
@@ -852,37 +873,45 @@ maybe_warn_for_bound (opt_code opt, location_t loc,
GimpleOrTree exp, tree func,
else if (!size || tree_int_cst_le (bndrng[0], size))
return false;
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+ warned = (func
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound %E may exceed "
"destination size %E")
: G_("%qD specified bound %E exceeds "
"destination size %E")),
func, bndrng[0], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound %E may exceed "
"destination size %E")
: G_("specified bound %E exceeds "
"destination size %E")),
bndrng[0], size));
+ }
else
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+ warned = (func
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may exceed "
"destination size %E")
: G_("%qD specified bound [%E, %E] exceeds "
"destination size %E")),
func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("specified bound [%E, %E] exceeds "
"destination size %E")
: G_("specified bound [%E, %E] exceeds "
"destination size %E")),
bndrng[0], bndrng[1], size));
+ }
if (warned)
{
@@ -927,11 +956,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
{
bool warned = false;
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+
if (write && read)
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+ ? warning_n (richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("%qD may access %E byte in a region "
"of size %E")
@@ -943,7 +975,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
: G_ ("%qD accessing %E bytes in a region "
"of size %E")),
func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
+ : warning_n (richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("may access %E byte in a region "
"of size %E")
@@ -959,14 +991,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD may access %E or more bytes "
"in a region of size %E")
: G_("%qD accessing %E or more bytes "
"in a region of size %E")),
func, range[0], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("may access %E or more bytes "
"in a region of size %E")
@@ -976,14 +1008,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
}
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD may access between %E and %E "
"bytes in a region of size %E")
: G_("%qD accessing between %E and %E "
"bytes in a region of size %E")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("may access between %E and %E bytes "
"in a region of size %E")
@@ -997,7 +1029,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+ ? warning_n (richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("%qD may write %E byte into a region "
"of size %E")
@@ -1009,7 +1041,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
: G_("%qD writing %E bytes into a region "
"of size %E overflows the destination")),
func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
+ : warning_n (richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("may write %E byte into a region "
"of size %E")
@@ -1025,7 +1057,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD may write %E or more bytes "
"into a region of size %E")
@@ -1033,7 +1065,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
"into a region of size %E overflows "
"the destination")),
func, range[0], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("may write %E or more bytes into "
"a region of size %E")
@@ -1044,7 +1076,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
}
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
(maybe
? G_("%qD may write between %E and %E bytes "
"into a region of size %E")
@@ -1052,7 +1084,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
"into a region of size %E overflows "
"the destination")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("may write between %E and %E bytes "
"into a region of size %E")
@@ -1067,7 +1099,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
+ ? warning_n (richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
(maybe
? G_("%qD may read %E byte from a region "
@@ -1080,7 +1112,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree
exp, int opt,
: G_("%qD reading %E bytes from a region "
"of size %E")),
func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
+ : warning_n (richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
(maybe
? G_("may read %E byte from a region "
@@ -1097,14 +1129,14 @@ warn_for_access (location_t loc, tree func,
GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (richloc, OPT_Wstringop_overread,
(maybe
? G_("%qD may read %E or more bytes "
"from a region of size %E")
: G_("%qD reading %E or more bytes "
"from a region of size %E")),
func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (richloc, OPT_Wstringop_overread,
(maybe
? G_("may read %E or more bytes "
"from a region of size %E")
@@ -1114,14 +1146,14 @@ warn_for_access (location_t loc, tree func,
GimpleOrTree exp, int opt,
}
else
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (richloc, OPT_Wstringop_overread,
(maybe
? G_("%qD may read between %E and %E bytes "
"from a region of size %E")
: G_("%qD reading between %E and %E bytes "
"from a region of size %E")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
(maybe
? G_("may read between %E and %E bytes "
"from a region of size %E")
@@ -1138,12 +1170,12 @@ warn_for_access (location_t loc, tree func,
GimpleOrTree exp, int opt,
if (tree_int_cst_equal (range[0], range[1])
|| tree_int_cst_sign_bit (range[1]))
warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
+ ? warning_n (richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
"%qD expecting %E byte in a region of size %E",
"%qD expecting %E bytes in a region of size %E",
func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
+ : warning_n (richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
"expecting %E byte in a region of size %E",
"expecting %E bytes in a region of size %E",
@@ -1152,22 +1184,22 @@ warn_for_access (location_t loc, tree func,
GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (richloc, OPT_Wstringop_overread,
"%qD expecting %E or more bytes in a region "
"of size %E",
func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (richloc, OPT_Wstringop_overread,
"expecting %E or more bytes in a region "
"of size %E",
range[0], size));
}
else
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (richloc, OPT_Wstringop_overread,
"%qD expecting between %E and %E bytes in "
"a region of size %E",
func, range[0], range[1], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (richloc, OPT_Wstringop_overread,
"expecting between %E and %E bytes in "
"a region of size %E",
range[0], range[1], size));
@@ -1398,6 +1430,9 @@ check_access (GimpleOrTree exp, tree dstwrite,
auto_diagnostic_group d;
location_t loc = get_location (exp);
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, exp);
+
bool warned = false;
if (dstwrite == slen && at_least_one)
{
@@ -1405,12 +1440,12 @@ check_access (GimpleOrTree exp, tree dstwrite,
and a source of unknown length. The call will write
at least one byte past the end of the destination. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (richloc, opt,
"%qD writing %E or more bytes into "
"a region of size %E overflows "
"the destination",
func, range[0], dstsize)
- : warning_at (loc, opt,
+ : warning_at (richloc, opt,
"writing %E or more bytes into "
"a region of size %E overflows "
"the destination",
@@ -2564,7 +2599,9 @@ pass_waccess::check_strncat (gcall *stmt)
&& tree_int_cst_equal (destsize, maxread))
{
location_t loc = get_location (stmt);
- warning_at (loc, OPT_Wstringop_overflow_,
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, stmt);
+ warning_at (richloc, OPT_Wstringop_overflow_,
"%qD specified bound %E equals destination size",
get_callee_fndecl (stmt), maxread);
@@ -3445,13 +3482,15 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm,
tree fndecl, tree fntype,
&& tree_int_cst_sgn (sizrng[0]) < 0
&& tree_int_cst_sgn (sizrng[1]) < 0)
{
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, stmt);
/* Warn about negative sizes. */
if (access.second.internal_p)
{
const std::string argtypestr
= access.second.array_as_string (ptrtype);
- if (warning_at (loc, OPT_Wstringop_overflow_,
+ if (warning_at (richloc, OPT_Wstringop_overflow_,
"bound argument %i value %s is "
"negative for a variable length array "
"argument %i of type %s",
@@ -3459,7 +3498,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm,
tree fndecl, tree fntype,
ptridx + 1, argtypestr.c_str ()))
arg_warned = OPT_Wstringop_overflow_;
}
- else if (warning_at (loc, OPT_Wstringop_overflow_,
+ else if (warning_at (richloc, OPT_Wstringop_overflow_,
"argument %i value %s is negative",
sizidx + 1, sizstr))
arg_warned = OPT_Wstringop_overflow_;
diff --git a/gcc/gimple-ssa-warn-restrict.cc b/gcc/gimple-ssa-warn-restrict.cc
index d71f1331c854..3bb926ac159f 100644
--- a/gcc/gimple-ssa-warn-restrict.cc
+++ b/gcc/gimple-ssa-warn-restrict.cc
@@ -40,6 +40,8 @@
#include "tree-object-size.h"
#include "calls.h"
#include "cfgloop.h"
+#include "diagnostic-move-history.h"
+#include "move-history-diagnostic-path.h"
#include "intl.h"
#include "gimple-range.h"
@@ -1693,6 +1695,9 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
location_t loc = gimple_location (call);
const offset_int maxobjsize = ref.maxobjsize;
+ rich_location *richloc
+ = build_rich_location_with_diagnostic_path (loc, call);
+
/* Check for excessive size first and regardless of warning options
since the result is used to make codegen decisions. */
if (ref.sizrange[0] > maxobjsize)
@@ -1709,13 +1714,13 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
if (warn_stringop_overflow)
{
if (ref.sizrange[0] == ref.sizrange[1])
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD specified bound %wu "
"exceeds maximum object size %wu",
func, ref.sizrange[0].to_uhwi (),
maxobjsize.to_uhwi ());
else
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD specified bound between %wu and %wu "
"exceeds maximum object size %wu",
func, ref.sizrange[0].to_uhwi (),
@@ -1776,7 +1781,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
&& TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
{
auto_diagnostic_group d;
- if (warning_at (loc, opt,
+ if (warning_at (richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s accessing array %qD with type %qT",
func, rangestr[0], rangestr[1], ref.base, type))
@@ -1786,13 +1791,13 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
warned = true;
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s",
func, rangestr[0], rangestr[1]);
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s",
func, rangestr[0], rangestr[1]);
@@ -1808,7 +1813,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
{
auto_diagnostic_group d;
if ((ref.basesize < maxobjsize
- && warning_at (loc, opt,
+ && warning_at (richloc, opt,
form
? G_("%qD forming offset %s is out of "
"the bounds [0, %wu] of object %qD with "
@@ -1817,7 +1822,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
"[0, %wu] of object %qD with type %qT"),
func, rangestr[0], ref.basesize.to_uhwi (),
ref.base, TREE_TYPE (ref.base)))
- || warning_at (loc, opt,
+ || warning_at (richloc, opt,
form
? G_("%qD forming offset %s is out of "
"the bounds of object %qD with type %qT")
@@ -1832,7 +1837,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
}
}
else if (ref.basesize < maxobjsize)
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
form
? G_("%qD forming offset %s is out "
"of the bounds [0, %wu]")
@@ -1840,7 +1845,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
"of the bounds [0, %wu]"),
func, rangestr[0], ref.basesize.to_uhwi ());
else
- warned = warning_at (loc, opt,
+ warned = warning_at (richloc, opt,
form
? G_("%qD forming offset %s is out of bounds")
: G_("%qD offset %s is out of bounds"),
@@ -1854,7 +1859,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
- if (warning_at (loc, opt,
+ if (warning_at (richloc, opt,
"%qD offset %s from the object at %qE is out "
"of the bounds of %qT",
func, rangestr[0], ref.base, type))
@@ -1872,7 +1877,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int
strict,
tree refop = TREE_OPERAND (ref.ref, 0);
tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
- if (warning_at (loc, opt,
+ if (warning_at (richloc, opt,
"%qD offset %s from the object at %qE is out "
"of the bounds of referenced subobject %qD with "
"type %qT at offset %wi",
diff --git a/gcc/move-history-diagnostic-path.cc
b/gcc/move-history-diagnostic-path.cc
new file mode 100644
index 000000000000..ab29893d1f61
--- /dev/null
+++ b/gcc/move-history-diagnostic-path.cc
@@ -0,0 +1,119 @@
+/* Classes for implementing diagnostic paths for move_history_t.
+ 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "function.h"
+#include "move-history-diagnostic-path.h"
+
+bool
+move_history_diagnostic_path::same_function_p (int event_idx_a,
+ int event_idx_b) const
+{
+ return (m_events[event_idx_a]->get_fndecl ()
+ == m_events[event_idx_b]->get_fndecl ());
+}
+
+/* Add an event to this path at LOC within function FNDECL at
+ stack depth DEPTH.
+
+ Use m_context's printer to format FMT, as the text of the new
+ event. */
+
+void
+move_history_diagnostic_path::add_event (location_t loc, tree fndecl, int
depth,
+ const char *fmt, ...)
+{
+ pretty_printer *pp = m_event_pp;
+ pp_clear_output_area (pp);
+
+ rich_location rich_loc (line_table, UNKNOWN_LOCATION);
+
+ va_list ap;
+
+ va_start (ap, fmt);
+
+ text_info ti (fmt, &ap, 0, nullptr, &rich_loc);
+ pp_format (pp, &ti);
+ pp_output_formatted_text (pp);
+
+ va_end (ap);
+
+ simple_diagnostic_event *new_event
+ = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp));
+ m_events.safe_push (new_event);
+
+ pp_clear_output_area (pp);
+
+ return;
+}
+
+/* Populate the diagnostic_path from the move_history. */
+void
+move_history_diagnostic_path::populate_path_from_move_history ()
+{
+ if (!m_move_history)
+ return;
+
+ for (move_history_t cur_ch = m_move_history; cur_ch;
+ cur_ch = cur_ch->prev_move)
+ add_event (cur_ch->condition, cfun->decl, 1,
+ "when the condition is evaluated to %s",
+ cur_ch->is_true_path ? "true" : "false");
+
+ /* Add an end of path warning event in the end of the path. */
+ add_event (m_location, cfun->decl, 1,
+ "out of array bounds here");
+ return;
+}
+
+/* Build a rich location for LOCATION, and populate the diagonistic path
+ for it corresponding to the containing gimple stmt. */
+
+rich_location *
+build_rich_location_with_diagnostic_path (location_t location, gimple *stmt)
+{
+ /* Generate a rich location for this location. */
+ rich_location *richloc = new rich_location (line_table, location);
+
+ move_history_t mv_history = stmt ? get_move_history (stmt) : NULL;
+ move_history_diagnostic_path *path
+ = new move_history_diagnostic_path (global_dc->m_printer,
+ mv_history, location);
+ path->populate_path_from_move_history ();
+
+ richloc->set_path (path);
+ return richloc;
+}
+
+/* Overload of the build_rich_location_with_diagnostic_patch for TREE node. */
+
+rich_location *
+build_rich_location_with_diagnostic_path (location_t location,
+ tree exp ATTRIBUTE_UNUSED)
+{
+ /* Generate a rich location for this location.
+ Right now, there is no move_history_t data structure attached to TREE,
+ therefore no move_history_diagnostic_path is generated. */
+ rich_location *richloc = new rich_location (line_table, location);
+
+ return richloc;
+}
diff --git a/gcc/move-history-diagnostic-path.h
b/gcc/move-history-diagnostic-path.h
new file mode 100644
index 000000000000..d04337ea377c
--- /dev/null
+++ b/gcc/move-history-diagnostic-path.h
@@ -0,0 +1,96 @@
+/* Classes for implementing diagnostic paths for move_history_t.
+ 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 GCC_MOVE_HISTORY_DIAGNOSTIC_PATH_H
+#define GCC_MOVE_HISTORY_DIAGNOSTIC_PATH_H
+
+#include "diagnostic-path.h"
+#include "simple-diagnostic-path.h"
+#include "diagnostic-move-history.h"
+
+/* An implementation of diagnostic_path for move_history_t, as a
+ vector of simple_diagnostic_event instances. */
+
+class move_history_diagnostic_path : public diagnostic_path
+{
+ public:
+ move_history_diagnostic_path (pretty_printer *event_pp,
+ move_history_t mv_history,
+ location_t loc)
+ : diagnostic_path (),
+ m_thread ("main"),
+ m_event_pp (event_pp),
+ m_move_history (mv_history),
+ m_location (loc)
+ {}
+
+ unsigned num_events () const final override
+ {
+ return m_events.length ();
+ }
+ const diagnostic_event & get_event (int idx) const final override
+ {
+ return *m_events[idx];
+ }
+ unsigned num_threads () const final override
+ {
+ return 1;
+ }
+ const diagnostic_thread &
+ get_thread (diagnostic_thread_id_t) const final override
+ {
+ return m_thread;
+ }
+ bool
+ same_function_p (int event_idx_a,
+ int event_idx_b) const final override;
+
+ void add_event (location_t loc, tree fndecl, int depth,
+ const char *fmt, ...);
+
+ void populate_path_from_move_history ();
+
+ private:
+ simple_diagnostic_thread m_thread;
+
+ /* The events that have occurred along this path. */
+ auto_delete_vec<simple_diagnostic_event> m_events;
+
+ pretty_printer *m_event_pp;
+
+ /* The move_history associated with this path. */
+ move_history_t m_move_history;
+
+ /* The location for the gimple statement where the
+ diagnostic message emitted. */
+ location_t m_location;
+
+};
+
+/* Build a rich location for LOCATION, and populate the diagonistic path
+ for it corresponding to the containing gimple stmt. */
+extern rich_location *
+build_rich_location_with_diagnostic_path (location_t, gimple *);
+
+/* Overload of the build_rich_location_with_diagnostic_patch for TREE node. */
+extern rich_location *
+build_rich_location_with_diagnostic_path (location_t, tree ATTRIBUTE_UNUSED);
+
+#endif /* ! GCC_MOVE_HISTORY_DIAGNOSTIC_PATH_H */
diff --git a/gcc/testsuite/gcc.dg/pr109071.c b/gcc/testsuite/gcc.dg/pr109071.c
new file mode 100644
index 000000000000..ea647247a014
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071.c
@@ -0,0 +1,43 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading. */
+/* { dg-options "-O2 -Wall -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+
+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]; /* { dg-warning "is above array bounds" } */
+
+ assign(0, ptr, index);
+ assign(*val, ptr, index);
+}
+/* { dg-begin-multiline-output "" }
+ NN | int *val = &sg->vals[index];
+ | ~~~~~~~~^~~~~~~
+ 'sparx5_set': events 1-2
+ NN | if (index >= 4)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ | ~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | struct nums {int vals[4];};
+ | ^~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_1.c
b/gcc/testsuite/gcc.dg/pr109071_1.c
new file mode 100644
index 000000000000..7a562941e0cb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_1.c
@@ -0,0 +1,36 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR88771, which is a duplication of PR109071. */
+/* { dg-options "-O2 -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+typedef struct {
+ int a;
+} * b;
+
+char *c, *x;
+int f;
+
+void d() {
+ b e;
+ char a = f + 1 ?: f;
+ __builtin_strncpy(c, x, f); /* { dg-warning "exceeds maximum object size" }
*/
+ if (a)
+ e->a = 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | __builtin_strncpy(c, x, f);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'd': events 1-2
+ NN | char a = f + 1 ?: f;
+ | ^
+ | |
+ | (1) when the condition is evaluated to false
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | __builtin_strncpy(c, x, f);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_2.c
b/gcc/testsuite/gcc.dg/pr109071_2.c
new file mode 100644
index 000000000000..9cbac28a7276
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_2.c
@@ -0,0 +1,50 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR85788, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+int b=10;
+int *d = &b, *e;
+void a(void *k, long l) {
+ long f = __builtin_object_size(k, 0);
+ __builtin___memset_chk(k, b, l, f); /* { dg-warning "is out of the bounds" }
*/
+}
+typedef struct {
+ int g;
+ int h;
+ char i[8000 * 8];
+} j;
+static void make_str_raster(j *k) {
+ int *c = d;
+ for (; c; c = e)
+ k->g = k->h = 32767;
+
+ a(k->i, k->g / 8 * k->h);
+ for (; d;)
+ ;
+}
+j m;
+void n() { make_str_raster(&m); }
+/* { dg-begin-multiline-output "" }
+ NN | __builtin___memset_chk(k, b, l, f);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'n': events 1-2
+ NN | __builtin___memset_chk(k, b, l, f);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | for (; c; c = e)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | j m;
+ | ^
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_3.c
b/gcc/testsuite/gcc.dg/pr109071_3.c
new file mode 100644
index 000000000000..0461bbf5f924
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_3.c
@@ -0,0 +1,42 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR108770, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+extern void put(int i);
+int check_idx(int i) {
+ if (i > 1)
+ put(i);
+ return i;
+}
+const char *arr[] = {"A", 0};
+void init() {
+ int i = 0;
+ while (arr[check_idx(i)] != 0) { /* { dg-warning "is above array bounds of"
} */
+ if (arr[check_idx(i)]) {}
+ i++;
+ }
+}
+/* { dg-begin-multiline-output "" }
+ NN | while (arr[check_idx(i)] != 0) {
+ | ~~~^~~~~~~~~~~~~~
+ 'init': events 1-2
+ NN | if (i > 1)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | while (arr[check_idx(i)] != 0) {
+ | ~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | const char *arr[] = {"A", 0};
+ | ^~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_4.c
b/gcc/testsuite/gcc.dg/pr109071_4.c
new file mode 100644
index 000000000000..10118104ab72
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_4.c
@@ -0,0 +1,41 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR106762, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+typedef long unsigned int size_t;
+
+struct obj_t { size_t field0; size_t field1; };
+struct obj_array_t { size_t objcnt; struct obj_t* objary; };
+
+extern void *memset (void *__s, int __c, size_t __n) __attribute__
((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__(1)));
+
+void bug(struct obj_array_t* ary)
+{
+ size_t idx = 0;
+ struct obj_t* obj;
+ if (idx < ary->objcnt)
+ obj = &ary->objary[idx];
+ else
+ obj = 0;
+ memset(&obj->field1, 0xff, sizeof(obj->field1)); /* { dg-warning "is out of
the bounds" } */
+ obj->field0 = 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | memset(&obj->field1, 0xff, sizeof(obj->field1));
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'bug': events 1-2
+ NN | if (idx < ary->objcnt)
+ | ^
+ | |
+ | (1) when the condition is evaluated to false
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | memset(&obj->field1, 0xff, sizeof(obj->field1));
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_5.c
b/gcc/testsuite/gcc.dg/pr109071_5.c
new file mode 100644
index 000000000000..fc1a8a31c5a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_5.c
@@ -0,0 +1,33 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR115274, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Wstringop-overread -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+#include <string.h>
+char *c;
+void a();
+int b(char *d) { return strlen(d); } /* { dg-warning "or more bytes from a
region of size 0" } */
+void e() {
+ long f = 1;
+ f = b(c + f);
+ if (c == 0)
+ a(f);
+}
+/* { dg-begin-multiline-output "" }
+ NN | int b(char *d) { return strlen(d); }
+ | ^~~~~~~~~
+ 'e': events 1-2
+ NN | int b(char *d) { return strlen(d); }
+ | ~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | if (c == 0)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr109071_6.c
b/gcc/testsuite/gcc.dg/pr109071_6.c
new file mode 100644
index 000000000000..6d0565bda3e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr109071_6.c
@@ -0,0 +1,49 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR117179, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+const char* commands[] = {"a", "b"};
+
+int setval_internal(int comind)
+{
+ if (comind > sizeof(commands)/sizeof(commands[0])) {
+ return 0;
+ }
+
+ return 1;
+}
+
+_Bool setval_internal_tilde(int comind, const char *com,
+ const char *val)
+{
+ int ret = setval_internal(comind);
+ if (commands[comind] == "b" && /* { dg-warning "is outside array bounds of"
} */
+
+ ret)
+ return 1;
+ return 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | if (commands[comind] == "b" &&
+ | ~~~~~~~~^~~~~~~~
+ 'setval_internal_tilde': events 1-2
+ NN | if (comind > sizeof(commands)/sizeof(commands[0])) {
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | if (commands[comind] == "b" &&
+ | ~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | const char* commands[] = {"a", "b"};
+ | ^~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 5df59b79c803..0bb9a3317536 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "cgraph.h"
#include "coverage.h"
#include "diagnostic.h"
+#include "diagnostic-move-history.h"
#include "varasm.h"
#include "tree-inline.h"
#include "realmpfr.h" /* For GMP/MPFR/MPC versions, in print_version. */
@@ -2434,6 +2435,8 @@ toplev::finalize (void)
tree_cc_finalize ();
reginfo_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 8c551e42a4d2..2fddb1a63268 100644
--- a/gcc/tree-ssa-sink.cc
+++ b/gcc/tree-ssa-sink.cc
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see
#include "cfgloop.h"
#include "tree-eh.h"
#include "tree-ssa-live.h"
+#include "diagnostic-move-history.h"
/* TODO:
1. Sinking store only using scalar promotion (IE without moving the RHS):
@@ -710,6 +711,15 @@ 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)
+ {
+ edge entry = find_edge (bb, gsi_bb (togsi));
+ 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 c88cc1d6aac9..4f5a878686f1 100644
--- a/gcc/tree-ssa-threadupdate.cc
+++ b/gcc/tree-ssa-threadupdate.cc
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-cfg.h"
#include "tree-vectorizer.h"
#include "tree-pass.h"
+#include "diagnostic-move-history.h"
/* Given a block B, update the CFG and SSA graph to reflect redirecting
one or more in-edges to B to instead reach the destination of an
@@ -1342,6 +1343,17 @@ ssa_redirect_edges (struct redirection_data **slot,
{
edge e2;
+ /* Set the move history for all the stmts in both original and copied
+ basic blocks. The duplicated block will be the destination of the
+ incoming edge. */
+ if (flag_diagnostics_details)
+ {
+ set_move_history_to_stmts_in_bb (e->dest, e, false,
+ COPY_BY_THREAD_JUMP);
+ set_move_history_to_stmts_in_bb (rd->dup_blocks[0], e,
+ true, COPY_BY_THREAD_JUMP);
+ }
+
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Threaded jump %d --> %d to %d\n",
e->src->index, e->dest->index, rd->dup_blocks[0]->index);
@@ -2420,6 +2432,19 @@ back_jt_path_registry::duplicate_thread_path (edge entry,
copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop,
split_edge_bb_loc (entry), false);
+ /* Set the move history for all the stmts in both original and copied
+ basic blocks. The copied regions will be the destination of the
+ entry edge. */
+ for (i = 0; i < n_region; i++)
+ if (flag_diagnostics_details)
+ {
+ set_move_history_to_stmts_in_bb (region[i], entry, false,
+ COPY_BY_THREAD_JUMP);
+ set_move_history_to_stmts_in_bb (region_copy[i], entry,
+ true, COPY_BY_THREAD_JUMP);
+ }
+
+
/* Fix up: copy_bbs redirects all edges pointing to copied blocks. The
following code ensures that all the edges exiting the jump-thread path are
redirected back to the original code: these edges are exceptions
--
2.31.1