The 2nd ping for the following patch: https://gcc.gnu.org/pipermail/gcc-patches/2024-July/657150.html
thanks. Qing > On Jul 22, 2024, at 09:01, Qing Zhao <qing.z...@oracle.com> wrote: > > Hi, Richard, > > Could you please take a look at the patch and let me know any comment you > have (especially on the middle-end part)? > > David, let me know if you have further comment and suggestions. > > Thanks a lot. > > Qing > >> On Jul 12, 2024, at 10:03, Qing Zhao <qing.z...@oracle.com> wrote: >> >> due to code duplication from jump threading [PR109071] >> Control this with a new option -fdiagnostic-explain-harder. >> >> Compared to V1, the major difference are: (address David's comments) >> >> 1. Change the name of the option from: >> >> -fdiagnostic-try-to-explain-harder >> To >> -fdiagnostic-explain-harder >> >> 2. Sync the commit comments with the real output of the compilation message. >> >> 3. Add one more event in the end of the path to repeat the out-of-bound >> issue. >> >> 4. Fixed the unnecessary changes in Makefile.in. >> >> 5. Add new copy_history_diagnostic_path.[cc|h] to implement a new >> class copy_history_diagnostic_path : public diagnostic_path >> >> for copy_history_t. >> >> 6. Only building the rich locaiton and populating the path when warning_at >> is called. >> >> There are two comments from David that I didn't addressed in this version: >> >> 1. Make regenerate-opt-urls. >> will do this in a later version. >> >> 2. Add a ⚠️ emoji for the last event. >> I didn't add this yet since I think the current message is clear enough. >> might not worth the effort to add this emoji (it's not that straightforward >> to add on). >> >> With this new version, the message emitted by GCC: >> >> $gcc -O2 -Wall -fdiagnostics-explain-harder -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];}; >> | ^~~~ >> >> Bootstrapped and regression tested on both aarch64 and x86. no issues. >> >> Let me know any further comments and suggestions. >> >> thanks. >> >> Qing >> >> ================================================== >> $ 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 copy_history to record >> the condition and the transformation that triggered the code duplication. >> Whenever there is a code duplication due to some specific transformations, >> such as jump threading, loop switching, etc, a copy_history structure is >> created and attached to the duplicated gimple statement. >> >> During array out-of-bound checking or other warning checking, the >> copy_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 -fdiagnostic-explain-harder >> which is off by default. >> >> With this change, by adding -fdiagnostic-explain-harder, >> the warning message for the above testing case is now: >> >> $ gcc -Wall -O2 -fdiagnostics-explain-harder -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];}; >> | ^~~~ >> >> gcc/ChangeLog: >> >> PR tree-optimization/109071 >> >> * Makefile.in (OBJS): Add diagnostic-copy-history.o >> and copy-history-diagnostic-path.o. >> * gcc/common.opt (fdiagnostics-explain-harder): New option. >> * gcc/doc/invoke.texi (fdiagnostics-explain-harder): 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 copy_history_diagnostic_path for warning_at. >> (array_bounds_checker::check_array_ref): Use rich location with >> copy_history_diagnostic_path for warning_at. >> (array_bounds_checker::check_mem_ref): Add one new parameter. >> Use rich location with copy_history_diagnostic_path for warning_at. >> (array_bounds_checker::check_addr_expr): Use rich location with >> copy_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 copy >> history when removing the gimple. >> * toplev.cc (toplev::finalize): Call copy_history_finalize. >> * tree-ssa-threadupdate.cc (set_copy_history_to_stmts_in_bb): New >> function. >> (back_jt_path_registry::duplicate_thread_path): Call the new function >> to set copy history for every duplicated gimple. >> * copy-history-diagnostic-path.cc: New file. >> * copy-history-diagnostic-path.h: New file. >> * diagnostic-copy-history.cc: New file. >> * diagnostic-copy-history.h: New file. >> >> gcc/testsuite/ChangeLog: >> >> PR tree-optimization/109071 >> >> * gcc.dg/pr109071.c: New test. >> --- >> gcc/Makefile.in | 2 + >> gcc/common.opt | 4 + >> gcc/copy-history-diagnostic-path.cc | 86 +++++++++++++++ >> gcc/copy-history-diagnostic-path.h | 87 +++++++++++++++ >> gcc/diagnostic-copy-history.cc | 163 ++++++++++++++++++++++++++++ >> gcc/diagnostic-copy-history.h | 80 ++++++++++++++ >> gcc/doc/invoke.texi | 7 ++ >> gcc/gimple-array-bounds.cc | 94 +++++++++++++--- >> gcc/gimple-array-bounds.h | 2 +- >> gcc/gimple-iterator.cc | 3 + >> gcc/testsuite/gcc.dg/pr109071.c | 43 ++++++++ >> gcc/toplev.cc | 3 + >> gcc/tree-ssa-threadupdate.cc | 42 +++++++ >> 13 files changed, 598 insertions(+), 18 deletions(-) >> create mode 100644 gcc/copy-history-diagnostic-path.cc >> create mode 100644 gcc/copy-history-diagnostic-path.h >> create mode 100644 gcc/diagnostic-copy-history.cc >> create mode 100644 gcc/diagnostic-copy-history.h >> create mode 100644 gcc/testsuite/gcc.dg/pr109071.c >> >> diff --git a/gcc/Makefile.in b/gcc/Makefile.in >> index 7d3ea27a6ab..f8bbfda845f 100644 >> --- a/gcc/Makefile.in >> +++ b/gcc/Makefile.in >> @@ -1436,6 +1436,8 @@ OBJS = \ >> df-problems.o \ >> df-scan.o \ >> dfp.o \ >> + diagnostic-copy-history.o \ >> + copy-history-diagnostic-path.o \ >> digraph.o \ >> dojump.o \ >> dominance.o \ >> diff --git a/gcc/common.opt b/gcc/common.opt >> index a300470bbc5..714280390d9 100644 >> --- a/gcc/common.opt >> +++ b/gcc/common.opt >> @@ -1558,6 +1558,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-explain-harder >> +Common Var(flag_diagnostics_explain_harder) >> +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/copy-history-diagnostic-path.cc >> b/gcc/copy-history-diagnostic-path.cc >> new file mode 100644 >> index 00000000000..65e5c056165 >> --- /dev/null >> +++ b/gcc/copy-history-diagnostic-path.cc >> @@ -0,0 +1,86 @@ >> +/* Classes for implementing diagnostic paths for copy_history_t. >> + Copyright (C) 2024-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/>. */ >> + >> +#include "config.h" >> +#include "system.h" >> +#include "coretypes.h" >> +#include "function.h" >> +#include "copy-history-diagnostic-path.h" >> + >> +bool >> +copy_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 >> +copy_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 copy_history. */ >> +void >> +copy_history_diagnostic_path::populate_path_from_copy_history () >> +{ >> + if (!m_copy_history) >> + return; >> + >> + for (copy_history_t cur_ch = m_copy_history; cur_ch; >> + cur_ch = cur_ch->prev_copy) >> + 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; >> +} >> diff --git a/gcc/copy-history-diagnostic-path.h >> b/gcc/copy-history-diagnostic-path.h >> new file mode 100644 >> index 00000000000..377e9136070 >> --- /dev/null >> +++ b/gcc/copy-history-diagnostic-path.h >> @@ -0,0 +1,87 @@ >> +/* Classes for implementing diagnostic paths for copy_history_t. >> + Copyright (C) 2024-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 GCC_COPY_HISTORY_DIAGNOSTIC_PATH_H >> +#define GCC_COPY_HISTORY_DIAGNOSTIC_PATH_H >> + >> +#include "diagnostic-path.h" >> +#include "simple-diagnostic-path.h" >> +#include "diagnostic-copy-history.h" >> + >> +/* An implementation of diagnostic_path for copy_history_t, as a >> + vector of simple_diagnostic_event instances. */ >> + >> +class copy_history_diagnostic_path : public diagnostic_path >> +{ >> + public: >> + copy_history_diagnostic_path (pretty_printer *event_pp, >> + copy_history_t cp_history, >> + location_t loc) >> + : diagnostic_path (), >> + m_thread ("main"), >> + m_event_pp (event_pp), >> + m_copy_history (cp_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_copy_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 copy_history associated with this path. */ >> + copy_history_t m_copy_history; >> + >> + /* The location for the gimple statement where the >> + diagnostic message emitted. */ >> + location_t m_location; >> + >> +}; >> + >> +#endif /* ! GCC_COPY_HISTORY_DIAGNOSTIC_PATH_H */ >> diff --git a/gcc/diagnostic-copy-history.cc b/gcc/diagnostic-copy-history.cc >> new file mode 100644 >> index 00000000000..2733d137622 >> --- /dev/null >> +++ b/gcc/diagnostic-copy-history.cc >> @@ -0,0 +1,163 @@ >> +/* Functions to handle copy history. >> + Copyright (C) 2024-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/>. */ >> + >> +#include "config.h" >> +#include "system.h" >> +#include "coretypes.h" >> +#include "backend.h" >> +#include "tree.h" >> +#include "diagnostic-copy-history.h" >> + >> +/* A mapping from a gimple to a pointer to the copy history of it. */ >> +static copy_history_map_t *copy_history_map; >> + >> +/* Obstack for copy history. */ >> +static struct obstack copy_history_obstack; >> + >> +/* Create a new copy history. */ >> + >> +copy_history_t >> +create_copy_history (location_t condition, >> + bool is_true_path, >> + enum copy_reason reason, >> + copy_history_t prev_copy) >> +{ >> + static bool initialized = false; >> + >> + if (!initialized) >> + { >> + gcc_obstack_init (©_history_obstack); >> + initialized = true; >> + } >> + >> + copy_history_t copy_history >> + = (copy_history_t) obstack_alloc (©_history_obstack, >> + sizeof (struct copy_history)); >> + copy_history->condition = condition; >> + copy_history->is_true_path = is_true_path; >> + copy_history->reason = reason; >> + copy_history->prev_copy = prev_copy; >> + return copy_history; >> +} >> + >> +/* Insert the copy history for the gimple STMT assuming the linked list >> + of CP_HISTORY does not have duplications. It's the caller's >> + responsibility to make sure that the linked list of CP_HISTORY does >> + not have duplications. */ >> + >> +void >> +insert_copy_history (gimple *stmt, copy_history_t cp_history) >> +{ >> + if (!copy_history_map) >> + copy_history_map = new copy_history_map_t; >> + >> + copy_history_map->put (stmt, cp_history); >> + return; >> +} >> + >> +/* Get the copy history for the gimple STMT, return NULL when there is >> + no associated copy history. */ >> + >> +copy_history_t >> +get_copy_history (gimple *stmt) >> +{ >> + if (!copy_history_map) >> + return NULL; >> + >> + if (const copy_history_t *cp_history_p = copy_history_map->get (stmt)) >> + return *cp_history_p; >> + >> + return NULL; >> +} >> + >> +/* Remove the copy history for STMT. */ >> + >> +void >> +remove_copy_history (gimple *stmt) >> +{ >> + if (!copy_history_map) >> + return; >> + copy_history_map->remove (stmt); >> + return; >> +} >> + >> +/* Check whether the cond_location, is_true_path and reason existed >> + * in the OLD_COPY_HISTORY. */ >> + >> +static bool >> +is_copy_history_existed (location_t cond_location, bool is_true_path, >> + enum copy_reason reason, >> + copy_history_t old_copy_history) >> +{ >> + for (copy_history_t cur_ch = old_copy_history; cur_ch; >> + cur_ch = cur_ch->prev_copy) >> + if ((cur_ch->condition == cond_location) >> + && (cur_ch->is_true_path == is_true_path) >> + && (cur_ch->reason == reason)) >> + return true; >> + >> + return false; >> +} >> + >> +/* Set copy history for the gimple STMT. Return TRUE when a new copy >> + * history is created and inserted. Otherwise return FALSE. */ >> + >> +bool >> +set_copy_history (gimple *stmt, location_t cond_location, >> + bool is_true_path, enum copy_reason reason) >> +{ >> + >> + /* First, get the old copy history associated with this STMT. */ >> + copy_history_t old_cp_history = get_copy_history (stmt); >> + >> + /* If the current copy history is not in the STMT's copy history linked >> + list yet, create the new copy history, put the old_copy_history as the >> + prev_copy of it. */ >> + copy_history_t new_cp_history = NULL; >> + if (!is_copy_history_existed (cond_location, is_true_path, >> + reason, old_cp_history)) >> + new_cp_history >> + = create_copy_history (cond_location, is_true_path, >> + reason, old_cp_history); >> + >> + /* Insert the copy history into the hash map. */ >> + if (new_cp_history) >> + { >> + insert_copy_history (stmt, new_cp_history); >> + return true; >> + } >> + >> + return false; >> +} >> + >> +/* Reset all state for diagnostic-copy-history.cc so that we can rerun the >> + compiler within the same process. For use by toplev::finalize. */ >> + >> +void >> +copy_history_finalize (void) >> +{ >> + if (copy_history_map) >> + { >> + delete copy_history_map; >> + copy_history_map = NULL; >> + } >> + obstack_free (©_history_obstack, NULL); >> + return; >> +} >> diff --git a/gcc/diagnostic-copy-history.h b/gcc/diagnostic-copy-history.h >> new file mode 100644 >> index 00000000000..0c2c55a2a81 >> --- /dev/null >> +++ b/gcc/diagnostic-copy-history.h >> @@ -0,0 +1,80 @@ >> +/* Copy history associated with a gimple statement to record its history >> + of duplications due to different transformations. >> + The copy history will be used to construct events for later diagnostic. >> + >> + Copyright (C) 2024-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_COPY_HISTORY_H_INCLUDED >> +#define DIAGNOSTIC_COPY_HISTORY_H_INCLUDED >> + >> +#include "hash-map.h" >> +#include "line-map.h" >> + >> +/* An enum for the reason why this copy is made. Right now, there are >> + three reasons, we can add more if needed. */ >> +enum copy_reason { >> + COPY_BY_THREAD_JUMP, >> + COPY_BY_LOOP_UNSWITCH, >> + COPY_BY_LOOP_UNROLL >> +}; >> + >> +/* This data structure records the information when a statement is >> + duplicated during different transformations. Such information >> + will be used by the later diagnostic messages to report more >> + contexts of the warnings or errors. */ >> +struct copy_history { >> + /* The location of the condition statement that triggered the code >> + duplication. */ >> + location_t condition; >> + >> + /* Whether this copy is on the TRUE path of the condition. */ >> + bool is_true_path; >> + >> + /* The reason for the code duplication. */ >> + enum copy_reason reason; >> + >> + /* This statement itself might be a previous code duplication. */ >> + struct copy_history *prev_copy; >> +}; >> + >> +typedef struct copy_history *copy_history_t; >> + >> +/* Create a new copy history. */ >> +extern copy_history_t create_copy_history (location_t, bool, >> + enum copy_reason, copy_history_t); >> + >> +typedef hash_map<gimple *, copy_history_t> copy_history_map_t; >> + >> +/* Get the copy history for the gimple STMT, return NULL when there is >> + no associated copy history. */ >> +extern copy_history_t get_copy_history (gimple *); >> + >> +/* Remove the copy history for STMT from the copy_history_map. */ >> +extern void remove_copy_history (gimple *); >> + >> +/* Set copy history for the gimple STMT. */ >> +extern bool set_copy_history (gimple *, location_t, >> + bool, enum copy_reason); >> + >> +/* Reset all state for diagnostic-copy-history.cc so that we can rerun the >> + compiler within the same process. For use by toplev::finalize. */ >> +extern void copy_history_finalize (void); >> + >> +#endif // DIAGNOSTIC_COPY_HISTORY_H_INCLUDED >> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi >> index b37c7af7a39..bb0c6a87523 100644 >> --- a/gcc/doc/invoke.texi >> +++ b/gcc/doc/invoke.texi >> @@ -321,6 +321,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-explain-harder >> >> @item Warning Options >> @xref{Warning Options,,Options to Request or Suppress Warnings}. >> @@ -5518,6 +5519,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-explain-harder >> +@item -fdiagnostics-explain-harder >> +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 1637a2fc4f4..ba98ab3b413 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-copy-history.h" >> +#include "copy-history-diagnostic-path.h" >> #include "intl.h" >> #include "tree-vrp.h" >> #include "alloc-pool.h" >> @@ -254,6 +257,25 @@ get_up_bounds_for_array_ref (tree ref, tree *decl, >> return; >> } >> >> +/* Build a rich location for LOCATION, and populate the diagonistic path >> + for it. */ >> + >> +static 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); >> + >> + copy_history_t cp_history = stmt ? get_copy_history (stmt) : NULL; >> + copy_history_diagnostic_path *path >> + = new copy_history_diagnostic_path (global_dc->printer, >> + cp_history, location); >> + path->populate_path_from_copy_history (); >> + >> + richloc->set_path (path); >> + return richloc; >> +} >> + >> /* Given the LOW_SUB_ORG, LOW_SUB and UP_SUB, and the computed UP_BOUND >> and UP_BOUND_P1, check whether the array reference REF is out of bound. >> When out of bounds, set OUT_OF_BOUND to true. >> @@ -262,6 +284,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 +303,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 +326,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 +344,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 +427,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 +461,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 +522,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 +625,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 +652,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 +677,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 +712,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 +758,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 +774,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 +871,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 aa7ca8e9730..2d1d48d1e94 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 93646262eac..472a79360d7 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-copy-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_copy_history (stmt) != NULL) >> + remove_copy_history (stmt); >> } >> >> /* Update the iterator and re-wire the links in I->SEQ. */ >> diff --git a/gcc/testsuite/gcc.dg/pr109071.c >> b/gcc/testsuite/gcc.dg/pr109071.c >> new file mode 100644 >> index 00000000000..95ae576451e >> --- /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-explain-harder" } */ >> +/* { 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/toplev.cc b/gcc/toplev.cc >> index f715f977b72..e9ae55f64be 100644 >> --- a/gcc/toplev.cc >> +++ b/gcc/toplev.cc >> @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see >> #include "cgraph.h" >> #include "coverage.h" >> #include "diagnostic.h" >> +#include "diagnostic-copy-history.h" >> #include "varasm.h" >> #include "tree-inline.h" >> #include "realmpfr.h" /* For GMP/MPFR/MPC versions, in print_version. */ >> @@ -2381,6 +2382,8 @@ toplev::finalize (void) >> tree_cc_finalize (); >> reginfo_cc_finalize (); >> >> + copy_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-threadupdate.cc b/gcc/tree-ssa-threadupdate.cc >> index fa61ba9512b..9292f0c2aa1 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-copy-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 >> @@ -2371,6 +2372,37 @@ back_jt_path_registry::adjust_paths_after_duplication >> (unsigned curr_path_num) >> } >> } >> >> +/* Set copy history to all the stmts in the basic block BB based on >> + the entry edge ENTRY and whether this basic block is on the true >> + path of the condition in the destination of the edge ENTRY. >> + Return TRUE when copy history are set, otherwise return FALSE. */ >> + >> +static bool >> +set_copy_history_to_stmts_in_bb (basic_block bb, edge entry, bool >> is_true_path) >> +{ >> + /* First, get the condition statement in the destination of the >> + edge ENTRY. */ >> + basic_block cond_block = entry->dest; >> + gimple *cond_stmt = NULL; >> + gimple_stmt_iterator gsi; >> + gsi = gsi_last_bb (cond_block); >> + /* if the cond_block ends with a conditional statement, get it. */ >> + if (!gsi_end_p (gsi) >> + && gsi_stmt (gsi) >> + && (gimple_code (gsi_stmt (gsi)) == GIMPLE_COND)) >> + cond_stmt = gsi_stmt (gsi); >> + >> + if (!cond_stmt) >> + return false; >> + >> + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) >> + set_copy_history (gsi_stmt (gsi), >> + gimple_location (cond_stmt), >> + is_true_path, >> + COPY_BY_THREAD_JUMP); >> + return true; >> +} >> + >> /* Duplicates a jump-thread path of N_REGION basic blocks. >> The ENTRY edge is redirected to the duplicate of the region. >> >> @@ -2419,6 +2451,16 @@ 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 copy history for all the stmts in both original and copied >> + basic blocks. The copied regions are in the true path. */ >> + if (flag_diagnostics_explain_harder) >> + for (i = 0; i < n_region; i++) >> + { >> + set_copy_history_to_stmts_in_bb (region[i], entry, false); >> + set_copy_history_to_stmts_in_bb (region_copy[i], entry, true); >> + } >> + >> + >> /* 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 >> >