@@ -1435,6 +1435,7 @@ OBJS = \
df-problems.o \
df-scan.o \
dfp.o \
+ diagnostic-move-history.o \
digraph.o \
dojump.o \
dominance.o \
@@ -1570,6 +1570,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.
new file mode 100644
@@ -0,0 +1,265 @@
+/* Functions to handle move history.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by Qing Zhao <qing.zhao@oracle.com>
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "cfganal.h"
+#include "diagnostic-move-history.h"
+
+/* A mapping from a gimple to a pointer to the move history of it. */
+static move_history_map_t *move_history_map;
+
+/* Obstack for move history. */
+static struct obstack move_history_obstack;
+
+/* Create a new move history. */
+
+move_history_t
+create_move_history (location_t condition,
+ bool is_true_path,
+ enum move_reason reason,
+ move_history_t prev_move)
+{
+ static bool initialized = false;
+
+ if (!initialized)
+ {
+ gcc_obstack_init (&move_history_obstack);
+ initialized = true;
+ }
+
+ move_history_t move_history
+ = (move_history_t) obstack_alloc (&move_history_obstack,
+ sizeof (struct move_history));
+ move_history->condition = condition;
+ move_history->is_true_path = is_true_path;
+ move_history->reason = reason;
+ move_history->prev_move = prev_move;
+ return move_history;
+}
+
+/* Insert the move history for the gimple STMT assuming the linked list
+ of MV_HISTORY does not have duplications. It's the caller's
+ responsibility to make sure that the linked list of MV_HISTORY does
+ not have duplications. */
+
+void
+insert_move_history (gimple *stmt, move_history_t mv_history)
+{
+ if (!move_history_map)
+ move_history_map = new move_history_map_t;
+
+ move_history_map->put (stmt, mv_history);
+ return;
+}
+
+/* Get the move history for the gimple STMT, return NULL when there is
+ no associated move history. */
+
+move_history_t
+get_move_history (const gimple *stmt)
+{
+ if (!move_history_map)
+ return NULL;
+
+ if (const move_history_t *mv_history_p = move_history_map->get (stmt))
+ return *mv_history_p;
+
+ return NULL;
+}
+
+/* Remove the move history for STMT. */
+
+void
+remove_move_history (gimple *stmt)
+{
+ if (!move_history_map)
+ return;
+ move_history_map->remove (stmt);
+ return;
+}
+
+/* Check whether the cond_location, is_true_path and reason existed
+ * in the OLD_MOVE_HISTORY. */
+
+static bool
+is_move_history_existed (location_t cond_location, bool is_true_path,
+ enum move_reason reason,
+ move_history_t old_move_history)
+{
+ for (move_history_t cur_ch = old_move_history; cur_ch;
+ cur_ch = cur_ch->prev_move)
+ if ((cur_ch->condition == cond_location)
+ && (cur_ch->is_true_path == is_true_path)
+ && (cur_ch->reason == reason))
+ return true;
+
+ return false;
+}
+
+/* Set move history for the gimple STMT. Return TRUE when a new move
+ * history is created and inserted. Otherwise return FALSE. */
+
+bool
+set_move_history (gimple *stmt, location_t cond_location,
+ bool is_true_path, enum move_reason reason)
+{
+
+ /* First, get the old move history associated with this STMT. */
+ move_history_t old_mv_history = get_move_history (stmt);
+
+ /* If the current move history is not in the STMT's move history linked
+ list yet, create the new move history, put the old_move_history as the
+ prev_move of it. */
+ move_history_t new_mv_history = NULL;
+ if (!is_move_history_existed (cond_location, is_true_path,
+ reason, old_mv_history))
+ new_mv_history
+ = create_move_history (cond_location, is_true_path,
+ reason, old_mv_history);
+
+ /* Insert the move history into the hash map. */
+ if (new_mv_history)
+ {
+ insert_move_history (stmt, new_mv_history);
+ return true;
+ }
+
+ return false;
+}
+
+/* Reset all state for diagnostic-move-history.cc so that we can rerun the
+ compiler within the same process. For use by toplev::finalize. */
+
+void
+move_history_finalize (void)
+{
+ if (move_history_map)
+ {
+ delete move_history_map;
+ move_history_map = NULL;
+ }
+ obstack_free (&move_history_obstack, NULL);
+ return;
+}
+
+/* Given an edge ENTRY and whether the new code will be moved to the
+ destination of the edge, IS_DISTINATION, return the condition
+ statement in the source of the ENTRY if found. Return NULL otherwise.
+
+ When the condition statement is found, setting IS_TRUE_PATH to true
+ if the destination of the edge is on the true path of the condition.
+
+ IS_TRUE_PATH is only valid when the condition statement is found.
+
+ source
+ | ENTRY
+ V
+ destination
+
+*/
+
+static gimple *
+get_cond_stmt (edge entry, bool is_destination, bool *is_true_path)
+{
+ /* First, get the condition statement in the source of the
+ edge ENTRY. */
+ basic_block cond_block = entry->src;
+ gimple *cond_stmt = NULL;
+ gimple_stmt_iterator gsi;
+ *is_true_path = false;
+
+ /* if the cond_block ends with a conditional statement, get it. */
+ while (!cond_stmt && cond_block)
+ {
+ gsi = gsi_last_bb (cond_block);
+ if (!gsi_end_p (gsi)
+ && gsi_stmt (gsi)
+ && (gimple_code (gsi_stmt (gsi)) == GIMPLE_COND))
+ cond_stmt = gsi_stmt (gsi);
+ /* If there is no cond_stmt in the cond_block, search the single_pred
+ of it. */
+ if (!cond_stmt && single_pred_p (cond_block))
+ {
+ basic_block prev_cond_block = cond_block;
+ cond_block = single_pred (cond_block);
+ entry = find_edge (cond_block, prev_cond_block);
+ }
+ else
+ break;
+ }
+
+ bool is_branch_taken = (cond_stmt && (BRANCH_EDGE (cond_block) == entry));
+ *is_true_path = !(is_branch_taken ^ is_destination);
+
+ return cond_stmt;
+}
+
+/* Set move history to the stmt based on the edge ENTRY and whether this stmt
+ will be in the destination of the ENTRY.
+ The REASON indicates what kind of transformation contributing to the
+ statment movement. Return TRUE when the move history has been set
+ successfully. */
+
+bool
+set_move_history_to_stmt (gimple *stmt, edge entry,
+ bool is_destination, enum move_reason reason)
+{
+ bool is_true_path = false;
+ gimple *cond_stmt = get_cond_stmt (entry, is_destination, &is_true_path);
+
+ if (!cond_stmt)
+ return false;
+
+ set_move_history (stmt, gimple_location (cond_stmt),
+ is_true_path, reason);
+ return true;
+}
+
+/* Set move history to all the stmts in the basic block BB based on
+ the edge ENTRY and whether this basic block will be the destination
+ of the ENTRY.
+ The REASON indicates what kind of transformation contributing to the
+ statement move. Return TRUE when the move history has been set
+ successfully. */
+
+bool
+set_move_history_to_stmts_in_bb (basic_block bb, edge entry,
+ bool is_destination,
+ enum move_reason reason)
+{
+ bool is_true_path = false;
+ gimple_stmt_iterator gsi;
+ gimple *cond_stmt = get_cond_stmt (entry, is_destination, &is_true_path);
+
+ if (!cond_stmt)
+ return false;
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ set_move_history (gsi_stmt (gsi), gimple_location (cond_stmt),
+ is_true_path, reason);
+
+ return true;
+}
new file mode 100644
@@ -0,0 +1,92 @@
+/* Move history associated with a gimple statement to record its history
+ of movement due to different transformations.
+ The move history will be used to construct events for later diagnostic.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by Qing Zhao <qing.zhao@oracle.com>
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef DIAGNOSTIC_MOVE_HISTORY_H
+#define DIAGNOSTIC_MOVE_HISTORY_H
+
+#include "hash-map.h"
+#include "line-map.h"
+
+/* An enum for the reason why this move is made. Right now, there are
+ three reasons, we can add more if needed. */
+enum move_reason {
+ COPY_BY_THREAD_JUMP,
+ COPY_BY_ISOLATE_PATH,
+ MOVE_BY_SINK,
+ COPY_BY_MAX
+};
+
+/* This data structure records the information when a statement is
+ moved along control flow graph during different transformations.
+ Such information will be used by the later diagnostic messages
+ to report more contexts of the warnings or errors. */
+struct move_history {
+ /* The location of the condition statement that triggered the code
+ movement. */
+ location_t condition;
+
+ /* Whether this move is on the TRUE path of the condition. */
+ bool is_true_path;
+
+ /* The reason for the code movement. */
+ enum move_reason reason;
+
+ /* This statement itself might be a previous code movement. */
+ struct move_history *prev_move;
+};
+
+typedef struct move_history *move_history_t;
+
+/* Create a new move history. */
+extern move_history_t create_move_history (location_t, bool,
+ enum move_reason, move_history_t);
+
+typedef hash_map<const gimple *, move_history_t> move_history_map_t;
+
+/* Get the move history for the gimple STMT, return NULL when there is
+ no associated move history. */
+extern move_history_t get_move_history (const gimple *);
+
+/* Remove the move history for STMT from the move_history_map. */
+extern void remove_move_history (gimple *);
+
+/* Set move history for the gimple STMT. */
+extern bool set_move_history (gimple *, location_t,
+ bool, enum move_reason);
+
+/* Reset all state for diagnostic-move-history.cc so that we can rerun the
+ compiler within the same process. For use by toplev::finalize. */
+extern void move_history_finalize (void);
+
+/* Set move history to the stmt based on the edge ENTRY and whether this stmt
+ will be in the destination of the ENTRY. */
+extern bool set_move_history_to_stmt (gimple *, edge,
+ bool, enum move_reason);
+
+/* Set move history to all the stmts in the basic block based on
+ the entry edge and whether this basic block will be the destination
+ of the entry edge. */
+extern bool set_move_history_to_stmts_in_bb (basic_block, edge,
+ bool, enum move_reason);
+
+#endif // DIAGNOSTIC_MOVE_HISTORY_H
@@ -326,6 +326,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}.
@@ -5612,6 +5613,16 @@ left margin.
This option controls the minimum width of the left margin printed by
@option{-fdiagnostics-show-line-numbers}. It defaults to 6.
+@opindex fdiagnostics-details
+@item -fdiagnostics-details
+With this option, the compiler collects more context information for
+diagnostics and emits them to the users to provide more hints on how
+the diagnostics come from, at the cost of a slightly slower compilation.
+Currently, The list of the impacted warning options includes:
+@option{-Warray-bounds}, @option{-Wstringop-overflow},
+@option{-Wstringop-overread}, and @option{-Wstringop-truncation}.
+More warning options might be added to this list in future releases.
+
@opindex fdiagnostics-parseable-fixits
@item -fdiagnostics-parseable-fixits
Emit fix-it hints in a machine-parseable format, suitable for consumption
@@ -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. */
@@ -45,6 +45,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. */
@@ -2687,6 +2688,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:
@@ -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)
new file mode 100644
@@ -0,0 +1,13 @@
+/* PR middle-end/117375 ICE with -fdiagnostics-details patch in sink pass. */
+/* { dg-do compile }
+ { dg-options "-O2 -Wall -fdiagnostics-details" } */
+
+int st, st_0;
+int nbFilledBytes, max;
+void ec_enc_shrink();
+void max_allowed() {
+ int nbAvailableBytes = nbFilledBytes;
+ if (st && st_0)
+ if (max < nbAvailableBytes)
+ ec_enc_shrink();
+}
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
#include "coverage.h"
#include "diagnostic.h"
#include "pretty-print-urlifier.h"
+#include "diagnostic-move-history.h"
#include "varasm.h"
#include "tree-inline.h"
#include "realmpfr.h" /* For GMP/MPFR/MPC versions, in print_version. */
@@ -2437,6 +2438,8 @@ toplev::finalize (void)
reginfo_cc_finalize ();
varasm_cc_finalize ();
+ move_history_finalize ();
+
/* save_decoded_options uses opts_obstack, so these must
be cleaned up together. */
obstack_free (&opts_obstack, NULL);
@@ -37,6 +37,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):
@@ -654,6 +655,49 @@ sink_common_stores_to_bb (basic_block bb)
return todo;
}
+/* Get the edge chain from FROM_BB to TO_BB, FROM_BB dominates TO_BB.
+ B2
+ / \
+ V \
+ B3 \
+ / \ \
+ V \---->V
+ B4 B7
+
+ In the above CFG, FROM_BB is B2, TO_BB is B4. This routine
+ will locate two edges, B2->B3, and B3->B4 and return a vector
+ with these two edges.
+ This routine will return an empty vector if the control flow
+ edge chain from FROM_BB to TO_BB is too complicate. */
+
+static auto_vec<edge>
+get_edge_chain (basic_block from_bb, basic_block to_bb)
+{
+ auto_vec<edge> rev_edge_chain, edge_chain;
+ basic_block imm_dom_bb;
+ edge e;
+ unsigned int i;
+ /* Backtracing from TO_BB to FROM_BB through the dominator tree. */
+ do
+ {
+ imm_dom_bb = get_immediate_dominator (CDI_DOMINATORS, to_bb);
+ if (!imm_dom_bb)
+ return edge_chain;
+ e = find_edge (imm_dom_bb, to_bb);
+ if (!e)
+ return edge_chain;
+ gcc_assert (e);
+ rev_edge_chain.safe_push (e);
+ to_bb = imm_dom_bb;
+ }
+ while (imm_dom_bb != from_bb);
+
+ /* The order of the edges need to be reverted. */
+ FOR_EACH_VEC_ELT_REVERSE (rev_edge_chain, i, e)
+ edge_chain.safe_push (e);
+ return edge_chain;
+}
+
/* Perform code sinking on BB */
static unsigned
@@ -711,6 +755,19 @@ sink_code_in_bb (basic_block bb, virtual_operand_live &vop_live)
bb->index, (gsi_bb (togsi))->index);
}
+ /* Set the move history for the stmt that is sinked from BB to
+ gsi_bb (togsi). This stmt is on the path from BB to
+ gsi_bb (togsi). */
+ if (flag_diagnostics_details)
+ {
+ /* BB might not be the immediate predecessor of gsi_bb (togsi),
+ get the edge chain from BB to gsi_bb (togsi). */
+ auto_vec<edge> edge_chain = get_edge_chain (bb, gsi_bb (togsi));
+
+ for (edge entry : edge_chain)
+ set_move_history_to_stmt (stmt, entry, true, MOVE_BY_SINK);
+ }
+
/* Update virtual operands of statements in the path we
do not sink to. */
if (gimple_vdef (stmt))
@@ -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