/* Iterator routines for GIMPLE statements.
- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2007-2019 Free Software Foundation, Inc.
Contributed by Aldy Hernandez <aldy@quesejoda.com>
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
#include "tree.h"
#include "gimple.h"
-#include "tree-flow.h"
+#include "cfghooks.h"
+#include "ssa.h"
+#include "cgraph.h"
+#include "tree-eh.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "tree-ssa.h"
#include "value-prof.h"
/* Mark the statement STMT as modified, and update it. */
static inline void
-update_modified_stmt (gimple stmt)
+update_modified_stmt (gimple *stmt)
{
- if (!ssa_operands_active ())
+ if (!ssa_operands_active (cfun))
return;
update_stmt_if_modified (stmt);
}
/* Mark the statements in SEQ as modified, and update them. */
-static void
+void
update_modified_stmts (gimple_seq seq)
{
gimple_stmt_iterator gsi;
-
- if (!ssa_operands_active ())
- return;
+
+ if (!ssa_operands_active (cfun))
+ return;
for (gsi = gsi_start (seq); !gsi_end_p (gsi); gsi_next (&gsi))
update_stmt_if_modified (gsi_stmt (gsi));
}
starting at FIRST and LAST. */
static void
-update_bb_for_stmts (gimple_seq_node first, basic_block bb)
+update_bb_for_stmts (gimple_seq_node first, gimple_seq_node last,
+ basic_block bb)
{
gimple_seq_node n;
-
+
for (n = first; n; n = n->next)
- gimple_set_bb (n->stmt, bb);
+ {
+ gimple_set_bb (n, bb);
+ if (n == last)
+ break;
+ }
}
+/* Set the frequencies for the cgraph_edges for each of the calls
+ starting at FIRST for their new position within BB. */
+
+static void
+update_call_edge_frequencies (gimple_seq_node first, basic_block bb)
+{
+ struct cgraph_node *cfun_node = NULL;
+ gimple_seq_node n;
+
+ for (n = first; n ; n = n->next)
+ if (is_gimple_call (n))
+ {
+ struct cgraph_edge *e;
+
+ /* These function calls are expensive enough that we want
+ to avoid calling them if we never see any calls. */
+ if (cfun_node == NULL)
+ cfun_node = cgraph_node::get (current_function_decl);
+
+ e = cfun_node->get_edge (n);
+ if (e != NULL)
+ e->count = bb->count;
+ }
+}
/* Insert the sequence delimited by nodes FIRST and LAST before
iterator I. M specifies how to update iterator I after insertion
basic_block bb;
gimple_seq_node cur = i->ptr;
+ gcc_assert (!cur || cur->prev);
+
if ((bb = gsi_bb (*i)) != NULL)
- update_bb_for_stmts (first, bb);
+ update_bb_for_stmts (first, last, bb);
/* Link SEQ before CUR in the sequence. */
if (cur)
{
first->prev = cur->prev;
- if (first->prev)
+ if (first->prev->next)
first->prev->next = first;
else
gimple_seq_set_first (i->seq, first);
}
else
{
- gimple_seq_node itlast = gimple_seq_last (i->seq);
+ gimple_seq_node itlast = gimple_seq_last (*i->seq);
/* If CUR is NULL, we link at the end of the sequence (this case happens
when gsi_after_labels is called for a basic block that contains only
labels, so it returns an iterator after the end of the block, and
we need to insert before it; it might be cleaner to add a flag to the
iterator saying whether we are at the start or end of the list). */
- first->prev = itlast;
+ last->next = NULL;
if (itlast)
- itlast->next = first;
+ {
+ first->prev = itlast;
+ itlast->next = first;
+ }
else
gimple_seq_set_first (i->seq, first);
gimple_seq_set_last (i->seq, last);
return;
/* Don't allow inserting a sequence into itself. */
- gcc_assert (seq != i->seq);
+ gcc_assert (seq != *i->seq);
first = gimple_seq_first (seq);
last = gimple_seq_last (seq);
- gimple_seq_set_first (seq, NULL);
- gimple_seq_set_last (seq, NULL);
- gimple_seq_free (seq);
-
/* Empty sequences need no work. */
if (!first || !last)
{
basic_block bb;
gimple_seq_node cur = i->ptr;
+ gcc_assert (!cur || cur->prev);
+
/* If the iterator is inside a basic block, we need to update the
basic block information for all the nodes between FIRST and LAST. */
if ((bb = gsi_bb (*i)) != NULL)
- update_bb_for_stmts (first, bb);
+ update_bb_for_stmts (first, last, bb);
/* Link SEQ after CUR. */
if (cur)
{
last->next = cur->next;
if (last->next)
- last->next->prev = last;
+ {
+ last->next->prev = last;
+ }
else
gimple_seq_set_last (i->seq, last);
first->prev = cur;
}
else
{
- gcc_assert (!gimple_seq_last (i->seq));
+ gcc_assert (!gimple_seq_last (*i->seq));
+ last->next = NULL;
gimple_seq_set_first (i->seq, first);
gimple_seq_set_last (i->seq, last);
}
return;
/* Don't allow inserting a sequence into itself. */
- gcc_assert (seq != i->seq);
+ gcc_assert (seq != *i->seq);
first = gimple_seq_first (seq);
last = gimple_seq_last (seq);
- gimple_seq_set_first (seq, NULL);
- gimple_seq_set_last (seq, NULL);
- gimple_seq_free (seq);
-
/* Empty sequences need no work. */
if (!first || !last)
{
gsi_split_seq_after (gimple_stmt_iterator i)
{
gimple_seq_node cur, next;
- gimple_seq old_seq, new_seq;
+ gimple_seq *pold_seq, new_seq;
cur = i.ptr;
gcc_assert (cur && cur->next);
next = cur->next;
- old_seq = i.seq;
- new_seq = gimple_seq_alloc ();
+ pold_seq = i.seq;
- gimple_seq_set_first (new_seq, next);
- gimple_seq_set_last (new_seq, gimple_seq_last (old_seq));
- gimple_seq_set_last (old_seq, cur);
+ gimple_seq_set_first (&new_seq, next);
+ gimple_seq_set_last (&new_seq, gimple_seq_last (*pold_seq));
+ gimple_seq_set_last (pold_seq, cur);
cur->next = NULL;
- next->prev = NULL;
return new_seq;
}
+/* Set the statement to which GSI points to STMT. This only updates
+ the iterator and the gimple sequence, it doesn't do the bookkeeping
+ of gsi_replace. */
+
+void
+gsi_set_stmt (gimple_stmt_iterator *gsi, gimple *stmt)
+{
+ gimple *orig_stmt = gsi_stmt (*gsi);
+ gimple *prev, *next;
+
+ stmt->next = next = orig_stmt->next;
+ stmt->prev = prev = orig_stmt->prev;
+ /* Note how we don't clear next/prev of orig_stmt. This is so that
+ copies of *GSI our callers might still hold (to orig_stmt)
+ can be advanced as if they too were replaced. */
+ if (prev->next)
+ prev->next = stmt;
+ else
+ gimple_seq_set_first (gsi->seq, stmt);
+ if (next)
+ next->prev = stmt;
+ else
+ gimple_seq_set_last (gsi->seq, stmt);
+
+ gsi->ptr = stmt;
+}
+
+
/* Move all statements in the sequence before I to a new sequence.
Return this new sequence. I is set to the head of the new list. */
-gimple_seq
-gsi_split_seq_before (gimple_stmt_iterator *i)
+void
+gsi_split_seq_before (gimple_stmt_iterator *i, gimple_seq *pnew_seq)
{
gimple_seq_node cur, prev;
- gimple_seq old_seq, new_seq;
+ gimple_seq old_seq;
cur = i->ptr;
gcc_assert (cur);
prev = cur->prev;
- old_seq = i->seq;
- new_seq = gimple_seq_alloc ();
- i->seq = new_seq;
+ old_seq = *i->seq;
+ if (!prev->next)
+ *i->seq = NULL;
+ i->seq = pnew_seq;
/* Set the limits on NEW_SEQ. */
- gimple_seq_set_first (new_seq, cur);
- gimple_seq_set_last (new_seq, gimple_seq_last (old_seq));
+ gimple_seq_set_first (pnew_seq, cur);
+ gimple_seq_set_last (pnew_seq, gimple_seq_last (old_seq));
/* Cut OLD_SEQ before I. */
- gimple_seq_set_last (old_seq, prev);
- cur->prev = NULL;
- if (prev)
+ gimple_seq_set_last (&old_seq, prev);
+ if (prev->next)
prev->next = NULL;
- else
- gimple_seq_set_first (old_seq, NULL);
-
- return new_seq;
}
/* Replace the statement pointed-to by GSI to STMT. If UPDATE_EH_INFO
is true, the exception handling information of the original
- statement is moved to the new statement. */
+ statement is moved to the new statement. Assignments must only be
+ replaced with assignments to the same LHS. Returns whether EH edge
+ cleanup is required. */
-void
-gsi_replace (gimple_stmt_iterator *gsi, gimple stmt, bool update_eh_info)
+bool
+gsi_replace (gimple_stmt_iterator *gsi, gimple *stmt, bool update_eh_info)
{
- int eh_region;
- gimple orig_stmt = gsi_stmt (*gsi);
+ gimple *orig_stmt = gsi_stmt (*gsi);
+ bool require_eh_edge_purge = false;
if (stmt == orig_stmt)
- return;
+ return false;
+
+ gcc_assert (!gimple_has_lhs (orig_stmt) || !gimple_has_lhs (stmt)
+ || gimple_get_lhs (orig_stmt) == gimple_get_lhs (stmt));
gimple_set_location (stmt, gimple_location (orig_stmt));
gimple_set_bb (stmt, gsi_bb (*gsi));
/* Preserve EH region information from the original statement, if
requested by the caller. */
if (update_eh_info)
- {
- eh_region = lookup_stmt_eh_region (orig_stmt);
- if (eh_region >= 0)
- {
- remove_stmt_from_eh_region (orig_stmt);
- add_stmt_to_eh_region (stmt, eh_region);
- }
- }
+ require_eh_edge_purge = maybe_clean_or_replace_eh_stmt (orig_stmt, stmt);
gimple_duplicate_stmt_histograms (cfun, stmt, cfun, orig_stmt);
+
+ /* Free all the data flow information for ORIG_STMT. */
+ gimple_set_bb (orig_stmt, NULL);
gimple_remove_stmt_histograms (cfun, orig_stmt);
delink_stmt_imm_use (orig_stmt);
- *gsi_stmt_ptr (gsi) = stmt;
+
+ gsi_set_stmt (gsi, stmt);
gimple_set_modified (stmt, true);
update_modified_stmt (stmt);
+ return require_eh_edge_purge;
+}
+
+
+/* Replace the statement pointed-to by GSI with the sequence SEQ.
+ If UPDATE_EH_INFO is true, the exception handling information of
+ the original statement is moved to the last statement of the new
+ sequence. If the old statement is an assignment, then so must
+ be the last statement of the new sequence, and they must have the
+ same LHS. */
+
+void
+gsi_replace_with_seq (gimple_stmt_iterator *gsi, gimple_seq seq,
+ bool update_eh_info)
+{
+ gimple_stmt_iterator seqi;
+ gimple *last;
+ if (gimple_seq_empty_p (seq))
+ {
+ gsi_remove (gsi, true);
+ return;
+ }
+ seqi = gsi_last (seq);
+ last = gsi_stmt (seqi);
+ gsi_remove (&seqi, false);
+ gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+ gsi_replace (gsi, last, update_eh_info);
}
should use gsi_insert_before. */
void
-gsi_insert_before_without_update (gimple_stmt_iterator *i, gimple stmt,
+gsi_insert_before_without_update (gimple_stmt_iterator *i, gimple *stmt,
enum gsi_iterator_update m)
{
- gimple_seq_node n;
-
- n = GGC_NEW (struct gimple_seq_node_d);
- n->prev = n->next = NULL;
- n->stmt = stmt;
- gsi_insert_seq_nodes_before (i, n, n, m);
+ gsi_insert_seq_nodes_before (i, stmt, stmt, m);
}
/* Insert statement STMT before the statement pointed-to by iterator I.
gsi_iterator_update). */
void
-gsi_insert_before (gimple_stmt_iterator *i, gimple stmt,
+gsi_insert_before (gimple_stmt_iterator *i, gimple *stmt,
enum gsi_iterator_update m)
{
update_modified_stmt (stmt);
should use gsi_insert_after. */
void
-gsi_insert_after_without_update (gimple_stmt_iterator *i, gimple stmt,
+gsi_insert_after_without_update (gimple_stmt_iterator *i, gimple *stmt,
enum gsi_iterator_update m)
{
- gimple_seq_node n;
-
- n = GGC_NEW (struct gimple_seq_node_d);
- n->prev = n->next = NULL;
- n->stmt = stmt;
- gsi_insert_seq_nodes_after (i, n, n, m);
+ gsi_insert_seq_nodes_after (i, stmt, stmt, m);
}
gsi_iterator_update). */
void
-gsi_insert_after (gimple_stmt_iterator *i, gimple stmt,
+gsi_insert_after (gimple_stmt_iterator *i, gimple *stmt,
enum gsi_iterator_update m)
{
update_modified_stmt (stmt);
REMOVE_PERMANENTLY is true when the statement is going to be removed
from the IL and not reinserted elsewhere. In that case we remove the
statement pointed to by iterator I from the EH tables, and free its
- operand caches. Otherwise we do not modify this information. */
+ operand caches. Otherwise we do not modify this information. Returns
+ true whether EH edge cleanup is required. */
-void
+bool
gsi_remove (gimple_stmt_iterator *i, bool remove_permanently)
{
gimple_seq_node cur, next, prev;
- gimple stmt = gsi_stmt (*i);
+ gimple *stmt = gsi_stmt (*i);
+ bool require_eh_edge_purge = false;
+
+ if (gimple_code (stmt) != GIMPLE_PHI)
+ insert_debug_temps_for_defs (i);
/* Free all the data flow information for STMT. */
gimple_set_bb (stmt, NULL);
if (remove_permanently)
{
- remove_stmt_from_eh_region (stmt);
+ if (gimple_debug_nonbind_marker_p (stmt))
+ /* We don't need this to be exact, but try to keep it at least
+ close. */
+ cfun->debug_marker_count--;
+ require_eh_edge_purge = remove_stmt_from_eh_lp (stmt);
gimple_remove_stmt_histograms (cfun, stmt);
}
cur = i->ptr;
next = cur->next;
prev = cur->prev;
-
- if (prev)
- prev->next = next;
- else
- gimple_seq_set_first (i->seq, next);
+ /* See gsi_set_stmt for why we don't reset prev/next of STMT. */
if (next)
+ /* Cur is not last. */
next->prev = prev;
- else
+ else if (prev->next)
+ /* Cur is last but not first. */
gimple_seq_set_last (i->seq, prev);
+ if (prev->next)
+ /* Cur is not first. */
+ prev->next = next;
+ else
+ /* Cur is first. */
+ *i->seq = next;
+
i->ptr = next;
+
+ return require_eh_edge_purge;
}
/* Finds iterator for STMT. */
gimple_stmt_iterator
-gsi_for_stmt (gimple stmt)
+gsi_for_stmt (gimple *stmt)
{
gimple_stmt_iterator i;
basic_block bb = gimple_bb (stmt);
else
i = gsi_start_bb (bb);
- for (; !gsi_end_p (i); gsi_next (&i))
- if (gsi_stmt (i) == stmt)
- return i;
+ i.ptr = stmt;
+ return i;
+}
+
+/* Get an iterator for STMT, which is known to belong to SEQ. This is
+ equivalent to starting at the beginning of SEQ and searching forward
+ until STMT is found. */
- gcc_unreachable ();
+gimple_stmt_iterator
+gsi_for_stmt (gimple *stmt, gimple_seq *seq)
+{
+ gimple_stmt_iterator i = gsi_start_1 (seq);
+ i.ptr = stmt;
+ return i;
}
+/* Finds iterator for PHI. */
+
+gphi_iterator
+gsi_for_phi (gphi *phi)
+{
+ gphi_iterator i;
+ basic_block bb = gimple_bb (phi);
+
+ i = gsi_start_phis (bb);
+ i.ptr = phi;
+
+ return i;
+}
/* Move the statement at FROM so it comes right after the statement at TO. */
void
gsi_move_after (gimple_stmt_iterator *from, gimple_stmt_iterator *to)
{
- gimple stmt = gsi_stmt (*from);
+ gimple *stmt = gsi_stmt (*from);
gsi_remove (from, false);
/* We must have GSI_NEW_STMT here, as gsi_move_after is sometimes used to
void
gsi_move_before (gimple_stmt_iterator *from, gimple_stmt_iterator *to)
{
- gimple stmt = gsi_stmt (*from);
+ gimple *stmt = gsi_stmt (*from);
gsi_remove (from, false);
/* For consistency with gsi_move_after, it might be better to have
gsi_move_to_bb_end (gimple_stmt_iterator *from, basic_block bb)
{
gimple_stmt_iterator last = gsi_last_bb (bb);
-#ifdef ENABLE_CHECKING
- gcc_assert (gsi_bb (last) == bb);
-#endif
+ gcc_checking_assert (gsi_bb (last) == bb);
/* Have to check gsi_end_p because it could be an empty block. */
if (!gsi_end_p (last) && is_ctrl_stmt (gsi_stmt (last)))
made until a call to gsi_commit_edge_inserts () is made. */
void
-gsi_insert_on_edge (edge e, gimple stmt)
+gsi_insert_on_edge (edge e, gimple *stmt)
{
gimple_seq_add_stmt (&PENDING_STMT (e), stmt);
}
gimple_seq_add_seq (&PENDING_STMT (e), seq);
}
+/* Return a new iterator pointing to the first statement in sequence of
+ statements on edge E. Such statements need to be subsequently moved into a
+ basic block by calling gsi_commit_edge_inserts. */
+
+gimple_stmt_iterator
+gsi_start_edge (edge e)
+{
+ return gsi_start (PENDING_STMT (e));
+}
/* Insert the statement pointed-to by GSI into edge E. Every attempt
is made to place the statement in an existing basic block, but
In all cases, the returned *GSI points to the correct location. The
return value is true if insertion should be done after the location,
- or false if it should be done before the location. If new basic block
+ or false if it should be done before the location. If a new basic block
has to be created, it is stored in *NEW_BB. */
static bool
basic_block *new_bb)
{
basic_block dest, src;
- gimple tmp;
+ gimple *tmp;
dest = e->dest;
would have to examine the PHIs to prove that none of them used
the value set by the statement we want to insert on E. That
hardly seems worth the effort. */
-restart:
+ restart:
if (single_pred_p (dest)
- && ! phi_nodes (dest)
- && dest != EXIT_BLOCK_PTR)
+ && gimple_seq_empty_p (phi_nodes (dest))
+ && dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
{
*gsi = gsi_start_bb (dest);
if (gsi_end_p (*gsi))
Except for the entry block. */
src = e->src;
if ((e->flags & EDGE_ABNORMAL) == 0
- && single_succ_p (src)
- && src != ENTRY_BLOCK_PTR)
+ && (single_succ_p (src)
+ /* Do not count a fake edge as successor as added to infinite
+ loops by connect_infinite_loops_to_exit. */
+ || (EDGE_COUNT (src->succs) == 2
+ && (EDGE_SUCC (src, 0)->flags & EDGE_FAKE
+ || EDGE_SUCC (src, 1)->flags & EDGE_FAKE)))
+ && src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
{
*gsi = gsi_last_bb (src);
if (gsi_end_p (*gsi))
return true;
tmp = gsi_stmt (*gsi);
- if (!stmt_ends_bb_p (tmp))
+ if (is_gimple_debug (tmp))
+ {
+ gimple_stmt_iterator si = *gsi;
+ gsi_prev_nondebug (&si);
+ if (!gsi_end_p (si))
+ tmp = gsi_stmt (si);
+ /* If we don't have a BB-ending nondebug stmt, we want to
+ insert after the trailing debug stmts. Otherwise, we may
+ insert before the BB-ending nondebug stmt, or split the
+ edge. */
+ if (!stmt_ends_bb_p (tmp))
+ return true;
+ *gsi = si;
+ }
+ else if (!stmt_ends_bb_p (tmp))
return true;
- if (gimple_code (tmp) == GIMPLE_RETURN)
- {
- gsi_prev (gsi);
- return true;
+ switch (gimple_code (tmp))
+ {
+ case GIMPLE_RETURN:
+ case GIMPLE_RESX:
+ return false;
+ default:
+ break;
}
}
block has to be created, it is returned. */
basic_block
-gsi_insert_on_edge_immediate (edge e, gimple stmt)
+gsi_insert_on_edge_immediate (edge e, gimple *stmt)
{
gimple_stmt_iterator gsi;
basic_block new_bb = NULL;
+ bool ins_after;
gcc_assert (!PENDING_STMT (e));
- if (gimple_find_edge_insert_loc (e, &gsi, &new_bb))
+ ins_after = gimple_find_edge_insert_loc (e, &gsi, &new_bb);
+
+ update_call_edge_frequencies (stmt, gsi.bb);
+
+ if (ins_after)
gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
else
gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
{
gimple_stmt_iterator gsi;
basic_block new_bb = NULL;
+ bool ins_after;
gcc_assert (!PENDING_STMT (e));
- if (gimple_find_edge_insert_loc (e, &gsi, &new_bb))
+ ins_after = gimple_find_edge_insert_loc (e, &gsi, &new_bb);
+ update_call_edge_frequencies (gimple_seq_first (stmts), gsi.bb);
+
+ if (ins_after)
gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
else
gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
edge e;
edge_iterator ei;
- gsi_commit_one_edge_insert (single_succ_edge (ENTRY_BLOCK_PTR), NULL);
+ gsi_commit_one_edge_insert (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+ NULL);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
FOR_EACH_EDGE (e, ei, bb->succs)
gsi_commit_one_edge_insert (e, NULL);
}
{
gimple_stmt_iterator gsi;
gimple_seq seq = PENDING_STMT (e);
+ bool ins_after;
PENDING_STMT (e) = NULL;
- if (gimple_find_edge_insert_loc (e, &gsi, new_bb))
+ ins_after = gimple_find_edge_insert_loc (e, &gsi, new_bb);
+ update_call_edge_frequencies (gimple_seq_first (seq), gsi.bb);
+
+ if (ins_after)
gsi_insert_seq_after (&gsi, seq, GSI_NEW_STMT);
else
gsi_insert_seq_before (&gsi, seq, GSI_NEW_STMT);
/* Returns iterator at the start of the list of phi nodes of BB. */
-gimple_stmt_iterator
+gphi_iterator
gsi_start_phis (basic_block bb)
{
- return gsi_start (phi_nodes (bb));
+ gimple_seq *pseq = phi_nodes_ptr (bb);
+
+ /* Adapted from gsi_start_1. */
+ gphi_iterator i;
+
+ i.ptr = gimple_seq_first (*pseq);
+ i.seq = pseq;
+ i.bb = i.ptr ? gimple_bb (i.ptr) : NULL;
+
+ return i;
}