/* SSA-PRE for trees.
- Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2015 Free Software Foundation, Inc.
Contributed by Daniel Berlin <dan@dberlin.org> and Steven Bosscher
<stevenb@suse.de>
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
#include "basic-block.h"
#include "gimple-pretty-print.h"
#include "tree-inline.h"
-#include "hash-table.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "tree-ssanames.h"
#include "tree-ssa-loop.h"
#include "tree-into-ssa.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "tree-dfa.h"
#include "tree-ssa.h"
#include "alloc-pool.h"
#include "obstack.h"
#include "tree-pass.h"
-#include "flags.h"
#include "langhooks.h"
#include "cfgloop.h"
#include "tree-ssa-sccvn.h"
#include "params.h"
#include "dbgcnt.h"
#include "domwalk.h"
+#include "cgraph.h"
+#include "symbol-summary.h"
#include "ipa-prop.h"
#include "tree-ssa-propagate.h"
+#include "ipa-utils.h"
+#include "tree-cfgcleanup.h"
/* TODO:
vn_reference_t reference;
} pre_expr_union;
-typedef struct pre_expr_d : typed_noop_remove <pre_expr_d>
+typedef struct pre_expr_d : nofree_ptr_hash <pre_expr_d>
{
enum pre_expr_kind kind;
unsigned int id;
pre_expr_union u;
/* hash_table support. */
- typedef pre_expr_d value_type;
- typedef pre_expr_d compare_type;
static inline hashval_t hash (const pre_expr_d *);
static inline int equal (const pre_expr_d *, const pre_expr_d *);
} *pre_expr;
/* Compare E1 and E1 for equality. */
inline int
-pre_expr_d::equal (const value_type *e1, const compare_type *e2)
+pre_expr_d::equal (const pre_expr_d *e1, const pre_expr_d *e2)
{
if (e1->kind != e2->kind)
return false;
/* Hash E. */
inline hashval_t
-pre_expr_d::hash (const value_type *e)
+pre_expr_d::hash (const pre_expr_d *e)
{
switch (e->kind)
{
/* Mapping from expression to id number we can use in bitmap sets. */
static vec<pre_expr> expressions;
-static hash_table <pre_expr_d> expression_to_id;
+static hash_table<pre_expr_d> *expression_to_id;
static vec<unsigned> name_to_id;
/* Allocate an expression id for EXPR. */
{
unsigned version = SSA_NAME_VERSION (PRE_EXPR_NAME (expr));
/* vec::safe_grow_cleared allocates no headroom. Avoid frequent
- re-allocations by using vec::reserve upfront. There is no
- vec::quick_grow_cleared unfortunately. */
+ re-allocations by using vec::reserve upfront. */
unsigned old_len = name_to_id.length ();
name_to_id.reserve (num_ssa_names - old_len);
- name_to_id.safe_grow_cleared (num_ssa_names);
+ name_to_id.quick_grow_cleared (num_ssa_names);
gcc_assert (name_to_id[version] == 0);
name_to_id[version] = expr->id;
}
else
{
- slot = expression_to_id.find_slot (expr, INSERT);
+ slot = expression_to_id->find_slot (expr, INSERT);
gcc_assert (!*slot);
*slot = expr;
}
}
else
{
- slot = expression_to_id.find_slot (expr, NO_INSERT);
+ slot = expression_to_id->find_slot (expr, NO_INSERT);
if (!slot)
return 0;
return ((pre_expr)*slot)->id;
expressions.release ();
}
-static alloc_pool pre_expr_pool;
+static pool_allocator<pre_expr_d> pre_expr_pool ("pre_expr nodes", 30);
/* Given an SSA_NAME NAME, get or create a pre_expr to represent it. */
if (result_id != 0)
return expression_for_id (result_id);
- result = (pre_expr) pool_alloc (pre_expr_pool);
+ result = pre_expr_pool.allocate ();
result->kind = NAME;
PRE_EXPR_NAME (result) = name;
alloc_expression_id (result);
/* A cache for value_dies_in_block_x. */
bitmap expr_dies;
+ /* The live virtual operand on successor edges. */
+ tree vop_on_exit;
+
/* True if we have visited this block during ANTIC calculation. */
unsigned int visited : 1;
- /* True we have deferred processing this block during ANTIC
- calculation until its successor is processed. */
- unsigned int deferred : 1;
-
/* True when the block contains a call that might not return. */
unsigned int contains_may_not_return_call : 1;
} *bb_value_sets_t;
#define NEW_SETS(BB) ((bb_value_sets_t) ((BB)->aux))->new_sets
#define EXPR_DIES(BB) ((bb_value_sets_t) ((BB)->aux))->expr_dies
#define BB_VISITED(BB) ((bb_value_sets_t) ((BB)->aux))->visited
-#define BB_DEFERRED(BB) ((bb_value_sets_t) ((BB)->aux))->deferred
#define BB_MAY_NOTRETURN(BB) ((bb_value_sets_t) ((BB)->aux))->contains_may_not_return_call
+#define BB_LIVE_VOP_ON_EXIT(BB) ((bb_value_sets_t) ((BB)->aux))->vop_on_exit
/* Basic block list in postorder. */
/* We can add and remove elements and entries to and from sets
and hash tables, so we use alloc pools for them. */
-static alloc_pool bitmap_set_pool;
+static pool_allocator<bitmap_set> bitmap_set_pool ("Bitmap sets", 30);
static bitmap_obstack grand_bitmap_obstack;
/* Set of blocks with statements that have had their EH properties changed. */
/* A three tuple {e, pred, v} used to cache phi translations in the
phi_translate_table. */
-typedef struct expr_pred_trans_d : typed_free_remove<expr_pred_trans_d>
+typedef struct expr_pred_trans_d : free_ptr_hash<expr_pred_trans_d>
{
/* The expression. */
pre_expr e;
hashval_t hashcode;
/* hash_table support. */
- typedef expr_pred_trans_d value_type;
- typedef expr_pred_trans_d compare_type;
- static inline hashval_t hash (const value_type *);
- static inline int equal (const value_type *, const compare_type *);
+ static inline hashval_t hash (const expr_pred_trans_d *);
+ static inline int equal (const expr_pred_trans_d *, const expr_pred_trans_d *);
} *expr_pred_trans_t;
typedef const struct expr_pred_trans_d *const_expr_pred_trans_t;
}
inline int
-expr_pred_trans_d::equal (const value_type *ve1,
- const compare_type *ve2)
+expr_pred_trans_d::equal (const expr_pred_trans_d *ve1,
+ const expr_pred_trans_d *ve2)
{
basic_block b1 = ve1->pred;
basic_block b2 = ve2->pred;
/* The phi_translate_table caches phi translations for a given
expression and predecessor. */
-static hash_table <expr_pred_trans_d> phi_translate_table;
+static hash_table<expr_pred_trans_d> *phi_translate_table;
/* Add the tuple mapping from {expression E, basic block PRED} to
the phi translation table and return whether it pre-existed. */
tem.e = e;
tem.pred = pred;
tem.hashcode = hash;
- slot = phi_translate_table.find_slot_with_hash (&tem, hash, INSERT);
+ slot = phi_translate_table->find_slot_with_hash (&tem, hash, INSERT);
if (*slot)
{
*entry = *slot;
static bitmap_set_t
bitmap_set_new (void)
{
- bitmap_set_t ret = (bitmap_set_t) pool_alloc (bitmap_set_pool);
+ bitmap_set_t ret = bitmap_set_pool.allocate ();
bitmap_initialize (&ret->expressions, &grand_bitmap_obstack);
bitmap_initialize (&ret->values, &grand_bitmap_obstack);
return ret;
bitmap_iterator bi, bj;
vec<pre_expr> result;
- /* Pre-allocate roughly enough space for the array. */
- result.create (bitmap_count_bits (&set->values));
+ /* Pre-allocate enough space for the array. */
+ result.create (bitmap_count_bits (&set->expressions));
FOR_EACH_VALUE_ID_IN_SET (set, i, bi)
{
EXECUTE_IF_SET_IN_BITMAP (exprset, 0, j, bj)
{
if (bitmap_bit_p (&set->expressions, j))
- result.safe_push (expression_for_id (j));
+ result.quick_push (expression_for_id (j));
}
}
if (result_id != 0)
return expression_for_id (result_id);
- newexpr = (pre_expr) pool_alloc (pre_expr_pool);
+ newexpr = pre_expr_pool.allocate ();
newexpr->kind = CONSTANT;
PRE_EXPR_CONSTANT (newexpr) = constant;
alloc_expression_id (newexpr);
vn_nary_op_lookup (t, &result);
if (result != NULL)
{
- pre_expr e = (pre_expr) pool_alloc (pre_expr_pool);
+ pre_expr e = pre_expr_pool.allocate ();
e->kind = NARY;
PRE_EXPR_NARY (e) = result;
result_id = lookup_expression_id (e);
if (result_id != 0)
{
- pool_free (pre_expr_pool, e);
+ pre_expr_pool.remove (e);
e = expression_for_id (result_id);
return e;
}
if (result && is_gimple_min_invariant (result))
return get_or_alloc_expr_for_constant (result);
- expr = (pre_expr) pool_alloc (pre_expr_pool);
+ expr = pre_expr_pool.allocate ();
expr->kind = NARY;
expr->id = 0;
if (nary)
tree newvuse = vuse;
vec<vn_reference_op_s> newoperands = vNULL;
bool changed = false, same_valid = true;
- unsigned int i, j, n;
+ unsigned int i, n;
vn_reference_op_t operand;
vn_reference_t newref;
- for (i = 0, j = 0;
- operands.iterate (i, &operand); i++, j++)
+ for (i = 0; operands.iterate (i, &operand); i++)
{
pre_expr opresult;
pre_expr leader;
newoperands.release ();
return NULL;
}
+ if (!changed)
+ continue;
if (!newoperands.exists ())
newoperands = operands.copy ();
/* We may have changed from an SSA_NAME to a constant */
newop.op0 = op[0];
newop.op1 = op[1];
newop.op2 = op[2];
- /* If it transforms a non-constant ARRAY_REF into a constant
- one, adjust the constant offset. */
- if (newop.opcode == ARRAY_REF
- && newop.off == -1
- && TREE_CODE (op[0]) == INTEGER_CST
- && TREE_CODE (op[1]) == INTEGER_CST
- && TREE_CODE (op[2]) == INTEGER_CST)
- {
- offset_int off = ((wi::to_offset (op[0])
- - wi::to_offset (op[1]))
- * wi::to_offset (op[2]));
- if (wi::fits_shwi_p (off))
- newop.off = off.to_shwi ();
- }
- newoperands[j] = newop;
- /* If it transforms from an SSA_NAME to an address, fold with
- a preceding indirect reference. */
- if (j > 0 && op[0] && TREE_CODE (op[0]) == ADDR_EXPR
- && newoperands[j - 1].opcode == MEM_REF)
- vn_reference_fold_indirect (&newoperands, &j);
- }
- if (i != operands.length ())
- {
- newoperands.release ();
- return NULL;
+ newoperands[i] = newop;
}
+ gcc_checking_assert (i == operands.length ());
if (vuse)
{
- newvuse = translate_vuse_through_block (newoperands,
+ newvuse = translate_vuse_through_block (newoperands.exists ()
+ ? newoperands : operands,
ref->set, ref->type,
vuse, phiblock, pred,
&same_valid);
tree result = vn_reference_lookup_pieces (newvuse, ref->set,
ref->type,
- newoperands,
+ newoperands.exists ()
+ ? newoperands : operands,
&newref, VN_WALK);
if (result)
newoperands.release ();
return NULL;
}
- expr = (pre_expr) pool_alloc (pre_expr_pool);
+ expr = pre_expr_pool.allocate ();
expr->kind = REFERENCE;
expr->id = 0;
}
else
new_val_id = ref->value_id;
+ if (!newoperands.exists ())
+ newoperands = operands.copy ();
newref = vn_reference_insert_pieces (newvuse, ref->set,
ref->type,
newoperands,
result, new_val_id);
- newoperands.create (0);
+ newoperands = vNULL;
PRE_EXPR_REFERENCE (expr) = newref;
constant = fully_constant_expression (expr);
if (constant != expr)
return get_or_alloc_expr_for_name (def);
}
- /* Otherwise return it unchanged - it will get cleaned if its
- value is not available in PREDs AVAIL_OUT set of expressions. */
+ /* Otherwise return it unchanged - it will get removed if its
+ value is not available in PREDs AVAIL_OUT set of expressions
+ by the subtraction of TMP_GEN. */
return expr;
}
else
/* Remove failed translations again, they cause insert
iteration to not pick up new opportunities reliably. */
- phi_translate_table.remove_elt_with_hash (slot, slot->hashcode);
+ phi_translate_table->remove_elt_with_hash (slot, slot->hashcode);
}
return phitrans;
For loads/calls, we also see if the vuse is killed in this block. */
static bool
-valid_in_sets (bitmap_set_t set1, bitmap_set_t set2, pre_expr expr,
- basic_block block)
+valid_in_sets (bitmap_set_t set1, bitmap_set_t set2, pre_expr expr)
{
switch (expr->kind)
{
case NAME:
- return bitmap_find_leader (AVAIL_OUT (block),
- get_expr_value_id (expr)) != NULL;
+ /* By construction all NAMEs are available. Non-available
+ NAMEs are removed by subtracting TMP_GEN from the sets. */
+ return true;
case NARY:
{
unsigned int i;
PA_IN. */
static void
-dependent_clean (bitmap_set_t set1, bitmap_set_t set2, basic_block block)
+dependent_clean (bitmap_set_t set1, bitmap_set_t set2)
{
vec<pre_expr> exprs = sorted_array_from_bitmap_set (set1);
pre_expr expr;
FOR_EACH_VEC_ELT (exprs, i, expr)
{
- if (!valid_in_sets (set1, set2, expr, block))
+ if (!valid_in_sets (set1, set2, expr))
bitmap_remove_from_set (set1, expr);
}
exprs.release ();
in SET. */
static void
-clean (bitmap_set_t set, basic_block block)
+clean (bitmap_set_t set)
{
vec<pre_expr> exprs = sorted_array_from_bitmap_set (set);
pre_expr expr;
FOR_EACH_VEC_ELT (exprs, i, expr)
{
- if (!valid_in_sets (set, NULL, expr, block))
+ if (!valid_in_sets (set, NULL, expr))
bitmap_remove_from_set (set, expr);
}
exprs.release ();
static sbitmap changed_blocks;
-/* Decide whether to defer a block for a later iteration, or PHI
- translate SOURCE to DEST using phis in PHIBLOCK. Return false if we
- should defer the block, and true if we processed it. */
-
-static bool
-defer_or_phi_translate_block (bitmap_set_t dest, bitmap_set_t source,
- basic_block block, basic_block phiblock)
-{
- if (!BB_VISITED (phiblock))
- {
- bitmap_set_bit (changed_blocks, block->index);
- BB_VISITED (block) = 0;
- BB_DEFERRED (block) = 1;
- return false;
- }
- else
- phi_translate_set (dest, source, block, phiblock);
- return true;
-}
-
/* Compute the ANTIC set for BLOCK.
If succs(BLOCK) > 1 then
else if (single_succ_p (block))
{
basic_block succ_bb = single_succ (block);
-
- /* We trade iterations of the dataflow equations for having to
- phi translate the maximal set, which is incredibly slow
- (since the maximal set often has 300+ members, even when you
- have a small number of blocks).
- Basically, we defer the computation of ANTIC for this block
- until we have processed it's successor, which will inevitably
- have a *much* smaller set of values to phi translate once
- clean has been run on it.
- The cost of doing this is that we technically perform more
- iterations, however, they are lower cost iterations.
-
- Timings for PRE on tramp3d-v4:
- without maximal set fix: 11 seconds
- with maximal set fix/without deferring: 26 seconds
- with maximal set fix/with deferring: 11 seconds
- */
-
- if (!defer_or_phi_translate_block (ANTIC_OUT, ANTIC_IN (succ_bb),
- block, succ_bb))
- {
- changed = true;
- goto maybe_dump_sets;
- }
+ gcc_assert (BB_VISITED (succ_bb));
+ phi_translate_set (ANTIC_OUT, ANTIC_IN (succ_bb), block, succ_bb);
}
/* If we have multiple successors, we take the intersection of all of
them. Note that in the case of loop exit phi nodes, we may have
worklist.quick_push (e->dest);
}
- /* Of multiple successors we have to have visited one already. */
- if (!first)
- {
- bitmap_set_bit (changed_blocks, block->index);
- BB_VISITED (block) = 0;
- BB_DEFERRED (block) = 1;
- changed = true;
- goto maybe_dump_sets;
- }
+ /* Of multiple successors we have to have visited one already
+ which is guaranteed by iteration order. */
+ gcc_assert (first != NULL);
- if (!gimple_seq_empty_p (phi_nodes (first)))
- phi_translate_set (ANTIC_OUT, ANTIC_IN (first), block, first);
- else
- bitmap_set_copy (ANTIC_OUT, ANTIC_IN (first));
+ phi_translate_set (ANTIC_OUT, ANTIC_IN (first), block, first);
FOR_EACH_VEC_ELT (worklist, i, bprime)
{
bitmap_value_insert_into_set (ANTIC_IN (block),
expression_for_id (bii));
- clean (ANTIC_IN (block), block);
+ clean (ANTIC_IN (block));
if (!bitmap_set_equal (old, ANTIC_IN (block)))
{
maybe_dump_sets:
if (dump_file && (dump_flags & TDF_DETAILS))
{
- if (!BB_DEFERRED (block) || BB_VISITED (block))
- {
- if (ANTIC_OUT)
- print_bitmap_set (dump_file, ANTIC_OUT, "ANTIC_OUT", block->index);
+ if (ANTIC_OUT)
+ print_bitmap_set (dump_file, ANTIC_OUT, "ANTIC_OUT", block->index);
- print_bitmap_set (dump_file, ANTIC_IN (block), "ANTIC_IN",
- block->index);
+ print_bitmap_set (dump_file, ANTIC_IN (block), "ANTIC_IN",
+ block->index);
- if (S)
- print_bitmap_set (dump_file, S, "S", block->index);
- }
- else
- {
- fprintf (dump_file,
- "Block %d was deferred for a future iteration.\n",
- block->index);
- }
+ if (S)
+ print_bitmap_set (dump_file, S, "S", block->index);
}
if (old)
bitmap_set_free (old);
/* PA_IN[block] = PA_IN[block] - ANTIC_IN[block] */
bitmap_set_subtract_values (PA_IN (block), ANTIC_IN (block));
- dependent_clean (PA_IN (block), ANTIC_IN (block), block);
+ dependent_clean (PA_IN (block), ANTIC_IN (block));
if (!bitmap_set_equal (old_PA_IN, PA_IN (block)))
{
}
BB_VISITED (block) = 0;
- BB_DEFERRED (block) = 0;
/* While we are here, give empty ANTIC_IN sets to each block. */
ANTIC_IN (block) = bitmap_set_new ();
(TREE_CODE (fn) == FUNCTION_DECL
? build_fold_addr_expr (fn) : fn),
nargs, args);
+ if (currop->with_bounds)
+ CALL_WITH_BOUNDS_P (folded) = true;
free (args);
if (sc)
CALL_EXPR_STATIC_CHAIN (folded) = sc;
gimple_stmt_iterator gsi;
tree exprtype = type ? type : get_expr_type (expr);
pre_expr nameexpr;
- gimple newstmt;
+ gassign *newstmt;
switch (expr->kind)
{
if (nary->opcode == POINTER_PLUS_EXPR)
{
if (i == 0)
- genop[i] = fold_convert (nary->type, genop[i]);
+ genop[i] = gimple_convert (&forced_stmts,
+ nary->type, genop[i]);
else if (i == 1)
- genop[i] = convert_to_ptrofftype (genop[i]);
+ genop[i] = gimple_convert (&forced_stmts,
+ sizetype, genop[i]);
}
else
- genop[i] = fold_convert (TREE_TYPE (nary->op[i]), genop[i]);
+ genop[i] = gimple_convert (&forced_stmts,
+ TREE_TYPE (nary->op[i]), genop[i]);
}
if (nary->opcode == CONSTRUCTOR)
{
statements.
We have to call unshare_expr because force_gimple_operand may
modify the tree we pass to it. */
- folded = force_gimple_operand (unshare_expr (folded), &forced_stmts,
+ gimple_seq tem = NULL;
+ folded = force_gimple_operand (unshare_expr (folded), &tem,
false, NULL);
+ gimple_seq_add_seq_without_update (&forced_stmts, tem);
/* If we have any intermediate expressions to the value sets, add them
to the value sets and chain them in the instruction stream. */
bitmap_value_replace_in_set (NEW_SETS (block), nameexpr);
bitmap_value_replace_in_set (AVAIL_OUT (block), nameexpr);
}
+
+ gimple_set_vuse (stmt, BB_LIVE_VOP_ON_EXIT (block));
+ gimple_set_modified (stmt, true);
}
gimple_seq_add_seq (stmts, forced_stmts);
}
name = make_temp_ssa_name (exprtype, NULL, "pretmp");
newstmt = gimple_build_assign (name, folded);
+ gimple_set_vuse (newstmt, BB_LIVE_VOP_ON_EXIT (block));
+ gimple_set_modified (newstmt, true);
gimple_set_plf (newstmt, NECESSARY, false);
gimple_seq_add_stmt (stmts, newstmt);
edge_iterator ei;
tree type = get_expr_type (expr);
tree temp;
- gimple phi;
+ gphi *phi;
/* Make sure we aren't creating an induction variable. */
if (bb_loop_depth (block) > 0 && EDGE_COUNT (block->preds) == 2)
bitmap_insert_into_set (NEW_SETS (block),
newphi);
+ /* If we insert a PHI node for a conversion of another PHI node
+ in the same basic-block try to preserve range information.
+ This is important so that followup loop passes receive optimal
+ number of iteration analysis results. See PR61743. */
+ if (expr->kind == NARY
+ && CONVERT_EXPR_CODE_P (expr->u.nary->opcode)
+ && TREE_CODE (expr->u.nary->op[0]) == SSA_NAME
+ && gimple_bb (SSA_NAME_DEF_STMT (expr->u.nary->op[0])) == block
+ && INTEGRAL_TYPE_P (type)
+ && INTEGRAL_TYPE_P (TREE_TYPE (expr->u.nary->op[0]))
+ && (TYPE_PRECISION (type)
+ >= TYPE_PRECISION (TREE_TYPE (expr->u.nary->op[0])))
+ && SSA_NAME_RANGE_INFO (expr->u.nary->op[0]))
+ {
+ wide_int min, max;
+ if (get_range_info (expr->u.nary->op[0], &min, &max) == VR_RANGE
+ && !wi::neg_p (min, SIGNED)
+ && !wi::neg_p (max, SIGNED))
+ /* Just handle extension and sign-changes of all-positive ranges. */
+ set_range_info (temp,
+ SSA_NAME_RANGE_TYPE (expr->u.nary->op[0]),
+ wide_int_storage::from (min, TYPE_PRECISION (type),
+ TYPE_SIGN (type)),
+ wide_int_storage::from (max, TYPE_PRECISION (type),
+ TYPE_SIGN (type)));
+ }
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Created phi ");
bool new_stuff = false;
vec<pre_expr> exprs;
pre_expr expr;
- vec<pre_expr> avail = vNULL;
+ auto_vec<pre_expr> avail;
int i;
exprs = sorted_array_from_bitmap_set (ANTIC_IN (block));
tree temp = make_temp_ssa_name (get_expr_type (expr),
NULL, "pretmp");
- gimple assign = gimple_build_assign (temp,
- edoubleprime->kind == CONSTANT ? PRE_EXPR_CONSTANT (edoubleprime) : PRE_EXPR_NAME (edoubleprime));
+ gassign *assign
+ = gimple_build_assign (temp,
+ edoubleprime->kind == CONSTANT ?
+ PRE_EXPR_CONSTANT (edoubleprime) :
+ PRE_EXPR_NAME (edoubleprime));
gimple_stmt_iterator gsi = gsi_after_labels (block);
gsi_insert_before (&gsi, assign, GSI_NEW_STMT);
son = next_dom_son (CDI_DOMINATORS, son))
worklist[sp++] = son;
+ BB_LIVE_VOP_ON_EXIT (ENTRY_BLOCK_PTR_FOR_FN (cfun))
+ = ssa_default_def (cfun, gimple_vop (cfun));
+
/* Loop until the worklist is empty. */
while (sp)
{
- gimple_stmt_iterator gsi;
gimple stmt;
basic_block dom;
its immediate dominator. */
dom = get_immediate_dominator (CDI_DOMINATORS, block);
if (dom)
- bitmap_set_copy (AVAIL_OUT (block), AVAIL_OUT (dom));
+ {
+ bitmap_set_copy (AVAIL_OUT (block), AVAIL_OUT (dom));
+ BB_LIVE_VOP_ON_EXIT (block) = BB_LIVE_VOP_ON_EXIT (dom);
+ }
/* Generate values for PHI nodes. */
- for (gsi = gsi_start_phis (block); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gphi_iterator gsi = gsi_start_phis (block); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
- tree result = gimple_phi_result (gsi_stmt (gsi));
+ tree result = gimple_phi_result (gsi.phi ());
/* We have no need for virtual phis, as they don't represent
actual computations. */
if (virtual_operand_p (result))
- continue;
+ {
+ BB_LIVE_VOP_ON_EXIT (block) = result;
+ continue;
+ }
pre_expr e = get_or_alloc_expr_for_name (result);
add_to_value (get_expr_value_id (e), e);
/* Now compute value numbers and populate value sets with all
the expressions computed in BLOCK. */
- for (gsi = gsi_start_bb (block); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gimple_stmt_iterator gsi = gsi_start_bb (block); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
ssa_op_iter iter;
tree op;
bitmap_value_insert_into_set (AVAIL_OUT (block), e);
}
+ if (gimple_vdef (stmt))
+ BB_LIVE_VOP_ON_EXIT (block) = gimple_vdef (stmt);
+
if (gimple_has_side_effects (stmt)
|| stmt_could_throw_p (stmt)
|| is_gimple_debug (stmt))
case GIMPLE_CALL:
{
vn_reference_t ref;
+ vn_reference_s ref1;
pre_expr result = NULL;
- auto_vec<vn_reference_op_s> ops;
/* We can value number only calls to real functions. */
if (gimple_call_internal_p (stmt))
continue;
- copy_reference_ops_from_call (stmt, &ops);
- vn_reference_lookup_pieces (gimple_vuse (stmt), 0,
- gimple_expr_type (stmt),
- ops, &ref, VN_NOWALK);
+ vn_reference_lookup_call (as_a <gcall *> (stmt), &ref, &ref1);
if (!ref)
continue;
|| gimple_bb (SSA_NAME_DEF_STMT
(gimple_vuse (stmt))) != block)
{
- result = (pre_expr) pool_alloc (pre_expr_pool);
+ result = pre_expr_pool.allocate ();
result->kind = REFERENCE;
result->id = 0;
PRE_EXPR_REFERENCE (result) = ref;
&& vn_nary_may_trap (nary))
continue;
- result = (pre_expr) pool_alloc (pre_expr_pool);
+ result = pre_expr_pool.allocate ();
result->kind = NARY;
result->id = 0;
PRE_EXPR_NARY (result) = nary;
continue;
}
- result = (pre_expr) pool_alloc (pre_expr_pool);
+ result = pre_expr_pool.allocate ();
result->kind = REFERENCE;
result->id = 0;
PRE_EXPR_REFERENCE (result) = ref;
/* Local state for the eliminate domwalk. */
static vec<gimple> el_to_remove;
-static vec<gimple> el_to_update;
+static vec<gimple> el_to_fixup;
static unsigned int el_todo;
static vec<tree> el_avail;
static vec<tree> el_avail_stack;
{
if (el_avail.length () <= SSA_NAME_VERSION (valnum))
el_avail.safe_grow_cleared (SSA_NAME_VERSION (valnum) + 1);
+ tree pushop = op;
+ if (el_avail[SSA_NAME_VERSION (valnum)])
+ pushop = el_avail[SSA_NAME_VERSION (valnum)];
+ el_avail_stack.safe_push (pushop);
el_avail[SSA_NAME_VERSION (valnum)] = op;
- el_avail_stack.safe_push (op);
}
}
return NULL_TREE;
tree res = make_temp_ssa_name (TREE_TYPE (val), NULL, "pretmp");
- gimple tem = gimple_build_assign (res,
- fold_build1 (TREE_CODE (expr),
- TREE_TYPE (expr), leader));
+ gassign *tem = gimple_build_assign (res,
+ fold_build1 (TREE_CODE (expr),
+ TREE_TYPE (expr), leader));
gsi_insert_before (gsi, tem, GSI_SAME_STMT);
VN_INFO_GET (res)->valnum = val;
class eliminate_dom_walker : public dom_walker
{
public:
- eliminate_dom_walker (cdi_direction direction) : dom_walker (direction) {}
+ eliminate_dom_walker (cdi_direction direction, bool do_pre_)
+ : dom_walker (direction), do_pre (do_pre_) {}
virtual void before_dom_children (basic_block);
virtual void after_dom_children (basic_block);
+
+ bool do_pre;
};
/* Perform elimination for the basic-block B during the domwalk. */
void
eliminate_dom_walker::before_dom_children (basic_block b)
{
- gimple_stmt_iterator gsi;
- gimple stmt;
-
/* Mark new bb. */
el_avail_stack.safe_push (NULL_TREE);
- for (gsi = gsi_start_phis (b); !gsi_end_p (gsi);)
+ /* ??? If we do nothing for unreachable blocks then this will confuse
+ tailmerging. Eventually we can reduce its reliance on SCCVN now
+ that we fully copy/constant-propagate (most) things. */
+
+ for (gphi_iterator gsi = gsi_start_phis (b); !gsi_end_p (gsi);)
{
- gimple stmt, phi = gsi_stmt (gsi);
- tree sprime = NULL_TREE, res = PHI_RESULT (phi);
- gimple_stmt_iterator gsi2;
-
- /* We want to perform redundant PHI elimination. Do so by
- replacing the PHI with a single copy if possible.
- Do not touch inserted, single-argument or virtual PHIs. */
- if (gimple_phi_num_args (phi) == 1
- || virtual_operand_p (res))
- {
- gsi_next (&gsi);
- continue;
- }
+ gphi *phi = gsi.phi ();
+ tree res = PHI_RESULT (phi);
- sprime = eliminate_avail (res);
- if (!sprime
- || sprime == res)
+ if (virtual_operand_p (res))
{
- eliminate_push_avail (res);
gsi_next (&gsi);
continue;
}
- else if (is_gimple_min_invariant (sprime))
- {
- if (!useless_type_conversion_p (TREE_TYPE (res),
- TREE_TYPE (sprime)))
- sprime = fold_convert (TREE_TYPE (res), sprime);
- }
- if (dump_file && (dump_flags & TDF_DETAILS))
+ tree sprime = eliminate_avail (res);
+ if (sprime
+ && sprime != res)
{
- fprintf (dump_file, "Replaced redundant PHI node defining ");
- print_generic_expr (dump_file, res, 0);
- fprintf (dump_file, " with ");
- print_generic_expr (dump_file, sprime, 0);
- fprintf (dump_file, "\n");
- }
-
- remove_phi_node (&gsi, false);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Replaced redundant PHI node defining ");
+ print_generic_expr (dump_file, res, 0);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, sprime, 0);
+ fprintf (dump_file, "\n");
+ }
- if (inserted_exprs
- && !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res))
- && TREE_CODE (sprime) == SSA_NAME)
- gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true);
-
- if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime)))
- sprime = fold_convert (TREE_TYPE (res), sprime);
- stmt = gimple_build_assign (res, sprime);
- gimple_set_plf (stmt, NECESSARY, gimple_plf (phi, NECESSARY));
-
- gsi2 = gsi_after_labels (b);
- gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT);
- /* Queue the copy for eventual removal. */
- el_to_remove.safe_push (stmt);
- /* If we inserted this PHI node ourself, it's not an elimination. */
- if (inserted_exprs
- && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)))
- pre_stats.phis--;
- else
- pre_stats.eliminations++;
- }
+ /* If we inserted this PHI node ourself, it's not an elimination. */
+ if (inserted_exprs
+ && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res)))
+ pre_stats.phis--;
+ else
+ pre_stats.eliminations++;
- for (gsi = gsi_start_bb (b); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- tree lhs = NULL_TREE;
- tree rhs = NULL_TREE;
+ /* If we will propagate into all uses don't bother to do
+ anything. */
+ if (may_propagate_copy (res, sprime))
+ {
+ /* Mark the PHI for removal. */
+ el_to_remove.safe_push (phi);
+ gsi_next (&gsi);
+ continue;
+ }
- stmt = gsi_stmt (gsi);
+ remove_phi_node (&gsi, false);
- if (gimple_has_lhs (stmt))
- lhs = gimple_get_lhs (stmt);
+ if (inserted_exprs
+ && !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (res))
+ && TREE_CODE (sprime) == SSA_NAME)
+ gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true);
- if (gimple_assign_single_p (stmt))
- rhs = gimple_assign_rhs1 (stmt);
+ if (!useless_type_conversion_p (TREE_TYPE (res), TREE_TYPE (sprime)))
+ sprime = fold_convert (TREE_TYPE (res), sprime);
+ gimple stmt = gimple_build_assign (res, sprime);
+ /* ??? It cannot yet be necessary (DOM walk). */
+ gimple_set_plf (stmt, NECESSARY, gimple_plf (phi, NECESSARY));
- /* Lookup the RHS of the expression, see if we have an
- available computation for it. If so, replace the RHS with
- the available computation. */
- if (gimple_has_lhs (stmt)
- && TREE_CODE (lhs) == SSA_NAME
- && !gimple_has_volatile_ops (stmt))
- {
- tree sprime;
- gimple orig_stmt = stmt;
+ gimple_stmt_iterator gsi2 = gsi_after_labels (b);
+ gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT);
+ continue;
+ }
- sprime = eliminate_avail (lhs);
- /* If there is no usable leader mark lhs as leader for its value. */
- if (!sprime)
- eliminate_push_avail (lhs);
+ eliminate_push_avail (res);
+ gsi_next (&gsi);
+ }
+ for (gimple_stmt_iterator gsi = gsi_start_bb (b);
+ !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ tree sprime = NULL_TREE;
+ gimple stmt = gsi_stmt (gsi);
+ tree lhs = gimple_get_lhs (stmt);
+ if (lhs && TREE_CODE (lhs) == SSA_NAME
+ && !gimple_has_volatile_ops (stmt)
/* See PR43491. Do not replace a global register variable when
it is a the RHS of an assignment. Do replace local register
variables since gcc does not guarantee a local variable will
be allocated in register.
- Do not perform copy propagation or undo constant propagation. */
- if (gimple_assign_single_p (stmt)
- && (TREE_CODE (rhs) == SSA_NAME
- || is_gimple_min_invariant (rhs)
- || (TREE_CODE (rhs) == VAR_DECL
- && is_global_var (rhs)
- && DECL_HARD_REGISTER (rhs))))
- continue;
-
+ ??? The fix isn't effective here. This should instead
+ be ensured by not value-numbering them the same but treating
+ them like volatiles? */
+ && !(gimple_assign_single_p (stmt)
+ && (TREE_CODE (gimple_assign_rhs1 (stmt)) == VAR_DECL
+ && DECL_HARD_REGISTER (gimple_assign_rhs1 (stmt))
+ && is_global_var (gimple_assign_rhs1 (stmt)))))
+ {
+ sprime = eliminate_avail (lhs);
if (!sprime)
{
/* If there is no existing usable leader but SCCVN thinks
&& (sprime = eliminate_insert (&gsi, val)) != NULL_TREE)
eliminate_push_avail (sprime);
}
- else if (is_gimple_min_invariant (sprime))
- {
- /* If there is no existing leader but SCCVN knows this
- value is constant, use that constant. */
- if (!useless_type_conversion_p (TREE_TYPE (lhs),
- TREE_TYPE (sprime)))
- sprime = fold_convert (TREE_TYPE (lhs), sprime);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Replaced ");
- print_gimple_expr (dump_file, stmt, 0, 0);
- fprintf (dump_file, " with ");
- print_generic_expr (dump_file, sprime, 0);
- fprintf (dump_file, " in ");
- print_gimple_stmt (dump_file, stmt, 0, 0);
- }
- pre_stats.eliminations++;
- propagate_tree_value_into_stmt (&gsi, sprime);
- stmt = gsi_stmt (gsi);
- update_stmt (stmt);
- /* If we removed EH side-effects from the statement, clean
- its EH information. */
- if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt))
+ /* If this now constitutes a copy duplicate points-to
+ and range info appropriately. This is especially
+ important for inserted code. See tree-ssa-copy.c
+ for similar code. */
+ if (sprime
+ && TREE_CODE (sprime) == SSA_NAME)
+ {
+ basic_block sprime_b = gimple_bb (SSA_NAME_DEF_STMT (sprime));
+ if (POINTER_TYPE_P (TREE_TYPE (lhs))
+ && SSA_NAME_PTR_INFO (lhs)
+ && !SSA_NAME_PTR_INFO (sprime))
{
- bitmap_set_bit (need_eh_cleanup,
- gimple_bb (stmt)->index);
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Removed EH side-effects.\n");
+ duplicate_ssa_name_ptr_info (sprime,
+ SSA_NAME_PTR_INFO (lhs));
+ if (b != sprime_b)
+ mark_ptr_info_alignment_unknown
+ (SSA_NAME_PTR_INFO (sprime));
}
- continue;
+ else if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ && SSA_NAME_RANGE_INFO (lhs)
+ && !SSA_NAME_RANGE_INFO (sprime)
+ && b == sprime_b)
+ duplicate_ssa_name_range_info (sprime,
+ SSA_NAME_RANGE_TYPE (lhs),
+ SSA_NAME_RANGE_INFO (lhs));
}
+ /* Inhibit the use of an inserted PHI on a loop header when
+ the address of the memory reference is a simple induction
+ variable. In other cases the vectorizer won't do anything
+ anyway (either it's loop invariant or a complicated
+ expression). */
if (sprime
- && sprime != lhs
- && (rhs == NULL_TREE
- || TREE_CODE (rhs) != SSA_NAME
- || may_propagate_copy (rhs, sprime)))
+ && TREE_CODE (sprime) == SSA_NAME
+ && do_pre
+ && flag_tree_loop_vectorize
+ && loop_outer (b->loop_father)
+ && has_zero_uses (sprime)
+ && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))
+ && gimple_assign_load_p (stmt))
{
- bool can_make_abnormal_goto
- = is_gimple_call (stmt)
- && stmt_can_make_abnormal_goto (stmt);
-
- gcc_assert (sprime != rhs);
-
- /* Inhibit the use of an inserted PHI on a loop header when
- the address of the memory reference is a simple induction
- variable. In other cases the vectorizer won't do anything
- anyway (either it's loop invariant or a complicated
- expression). */
- if (flag_tree_loop_vectorize
- && gimple_assign_single_p (stmt)
- && TREE_CODE (sprime) == SSA_NAME
- && loop_outer (b->loop_father))
+ gimple def_stmt = SSA_NAME_DEF_STMT (sprime);
+ basic_block def_bb = gimple_bb (def_stmt);
+ if (gimple_code (def_stmt) == GIMPLE_PHI
+ && b->loop_father->header == def_bb)
{
- gimple def_stmt = SSA_NAME_DEF_STMT (sprime);
- basic_block def_bb = gimple_bb (def_stmt);
- if (gimple_code (def_stmt) == GIMPLE_PHI
- && b->loop_father->header == def_bb
- && has_zero_uses (sprime))
+ ssa_op_iter iter;
+ tree op;
+ bool found = false;
+ FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
{
- ssa_op_iter iter;
- tree op;
- bool found = false;
- FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
+ affine_iv iv;
+ def_bb = gimple_bb (SSA_NAME_DEF_STMT (op));
+ if (def_bb
+ && flow_bb_inside_loop_p (b->loop_father, def_bb)
+ && simple_iv (b->loop_father,
+ b->loop_father, op, &iv, true))
{
- affine_iv iv;
- def_bb = gimple_bb (SSA_NAME_DEF_STMT (op));
- if (def_bb
- && flow_bb_inside_loop_p (b->loop_father,
- def_bb)
- && simple_iv (b->loop_father,
- b->loop_father, op, &iv, true))
- {
- found = true;
- break;
- }
+ found = true;
+ break;
}
- if (found)
+ }
+ if (found)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
{
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Not replacing ");
- print_gimple_expr (dump_file, stmt, 0, 0);
- fprintf (dump_file, " with ");
- print_generic_expr (dump_file, sprime, 0);
- fprintf (dump_file, " which would add a loop"
- " carried dependence to loop %d\n",
- b->loop_father->num);
- }
- continue;
+ fprintf (dump_file, "Not replacing ");
+ print_gimple_expr (dump_file, stmt, 0, 0);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, sprime, 0);
+ fprintf (dump_file, " which would add a loop"
+ " carried dependence to loop %d\n",
+ b->loop_father->num);
}
+ /* Don't keep sprime available. */
+ sprime = NULL_TREE;
+ }
+ }
+ }
+
+ if (sprime)
+ {
+ /* If we can propagate the value computed for LHS into
+ all uses don't bother doing anything with this stmt. */
+ if (may_propagate_copy (lhs, sprime))
+ {
+ /* Mark it for removal. */
+ el_to_remove.safe_push (stmt);
+
+ /* ??? Don't count copy/constant propagations. */
+ if (gimple_assign_single_p (stmt)
+ && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
+ || gimple_assign_rhs1 (stmt) == sprime))
+ continue;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Replaced ");
+ print_gimple_expr (dump_file, stmt, 0, 0);
+ fprintf (dump_file, " with ");
+ print_generic_expr (dump_file, sprime, 0);
+ fprintf (dump_file, " in all uses of ");
+ print_gimple_stmt (dump_file, stmt, 0, 0);
}
+
+ pre_stats.eliminations++;
+ continue;
}
+ /* If this is an assignment from our leader (which
+ happens in the case the value-number is a constant)
+ then there is nothing to do. */
+ if (gimple_assign_single_p (stmt)
+ && sprime == gimple_assign_rhs1 (stmt))
+ continue;
+
+ /* Else replace its RHS. */
+ bool can_make_abnormal_goto
+ = is_gimple_call (stmt)
+ && stmt_can_make_abnormal_goto (stmt);
+
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Replaced ");
if (TREE_CODE (sprime) == SSA_NAME)
gimple_set_plf (SSA_NAME_DEF_STMT (sprime),
NECESSARY, true);
- /* We need to make sure the new and old types actually match,
- which may require adding a simple cast, which fold_convert
- will do for us. */
- if ((!rhs || TREE_CODE (rhs) != SSA_NAME)
- && !useless_type_conversion_p (gimple_expr_type (stmt),
- TREE_TYPE (sprime)))
- sprime = fold_convert (gimple_expr_type (stmt), sprime);
pre_stats.eliminations++;
+ gimple orig_stmt = stmt;
+ if (!useless_type_conversion_p (TREE_TYPE (lhs),
+ TREE_TYPE (sprime)))
+ sprime = fold_convert (TREE_TYPE (lhs), sprime);
+ tree vdef = gimple_vdef (stmt);
+ tree vuse = gimple_vuse (stmt);
propagate_tree_value_into_stmt (&gsi, sprime);
stmt = gsi_stmt (gsi);
update_stmt (stmt);
+ if (vdef != gimple_vdef (stmt))
+ VN_INFO (vdef)->valnum = vuse;
/* If we removed EH side-effects from the statement, clean
its EH information. */
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " Removed AB side-effects.\n");
}
+
+ continue;
}
}
+
/* If the statement is a scalar store, see if the expression
- has the same value number as its rhs. If so, the store is
- dead. */
- else if (gimple_assign_single_p (stmt)
- && !gimple_has_volatile_ops (stmt)
- && !is_gimple_reg (gimple_assign_lhs (stmt))
- && (TREE_CODE (rhs) == SSA_NAME
- || is_gimple_min_invariant (rhs)))
- {
- tree val;
- val = vn_reference_lookup (gimple_assign_lhs (stmt),
- gimple_vuse (stmt), VN_WALK, NULL);
- if (TREE_CODE (rhs) == SSA_NAME)
- rhs = VN_INFO (rhs)->valnum;
- if (val
- && operand_equal_p (val, rhs, 0))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Deleted redundant store ");
- print_gimple_stmt (dump_file, stmt, 0, 0);
- }
+ has the same value number as its rhs. If so, the store is
+ dead. */
+ if (gimple_assign_single_p (stmt)
+ && !gimple_has_volatile_ops (stmt)
+ && !is_gimple_reg (gimple_assign_lhs (stmt))
+ && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
+ || is_gimple_min_invariant (gimple_assign_rhs1 (stmt))))
+ {
+ tree val;
+ tree rhs = gimple_assign_rhs1 (stmt);
+ val = vn_reference_lookup (gimple_assign_lhs (stmt),
+ gimple_vuse (stmt), VN_WALK, NULL);
+ if (TREE_CODE (rhs) == SSA_NAME)
+ rhs = VN_INFO (rhs)->valnum;
+ if (val
+ && operand_equal_p (val, rhs, 0))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Deleted redundant store ");
+ print_gimple_stmt (dump_file, stmt, 0, 0);
+ }
+
+ /* Queue stmt for removal. */
+ el_to_remove.safe_push (stmt);
+ continue;
+ }
+ }
- /* Queue stmt for removal. */
- el_to_remove.safe_push (stmt);
- }
- }
- /* Visit COND_EXPRs and fold the comparison with the
- available value-numbers. */
- else if (gimple_code (stmt) == GIMPLE_COND)
+ bool can_make_abnormal_goto = stmt_can_make_abnormal_goto (stmt);
+ bool was_noreturn = (is_gimple_call (stmt)
+ && gimple_call_noreturn_p (stmt));
+ tree vdef = gimple_vdef (stmt);
+ tree vuse = gimple_vuse (stmt);
+
+ /* If we didn't replace the whole stmt (or propagate the result
+ into all uses), replace all uses on this stmt with their
+ leaders. */
+ use_operand_p use_p;
+ ssa_op_iter iter;
+ FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
{
- tree op0 = gimple_cond_lhs (stmt);
- tree op1 = gimple_cond_rhs (stmt);
- tree result;
-
- if (TREE_CODE (op0) == SSA_NAME)
- op0 = VN_INFO (op0)->valnum;
- if (TREE_CODE (op1) == SSA_NAME)
- op1 = VN_INFO (op1)->valnum;
- result = fold_binary (gimple_cond_code (stmt), boolean_type_node,
- op0, op1);
- if (result && TREE_CODE (result) == INTEGER_CST)
+ tree use = USE_FROM_PTR (use_p);
+ /* ??? The call code above leaves stmt operands un-updated. */
+ if (TREE_CODE (use) != SSA_NAME)
+ continue;
+ tree sprime = eliminate_avail (use);
+ if (sprime && sprime != use
+ && may_propagate_copy (use, sprime)
+ /* We substitute into debug stmts to avoid excessive
+ debug temporaries created by removed stmts, but we need
+ to avoid doing so for inserted sprimes as we never want
+ to create debug temporaries for them. */
+ && (!inserted_exprs
+ || TREE_CODE (sprime) != SSA_NAME
+ || !is_gimple_debug (stmt)
+ || !bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (sprime))))
{
- if (integer_zerop (result))
- gimple_cond_make_false (stmt);
- else
- gimple_cond_make_true (stmt);
- update_stmt (stmt);
- el_todo = TODO_cleanup_cfg;
+ propagate_value (use_p, sprime);
+ gimple_set_modified (stmt, true);
+ if (TREE_CODE (sprime) == SSA_NAME
+ && !is_gimple_debug (stmt))
+ gimple_set_plf (SSA_NAME_DEF_STMT (sprime),
+ NECESSARY, true);
}
}
+
/* Visit indirect calls and turn them into direct calls if
- possible. */
- if (is_gimple_call (stmt))
+ possible using the devirtualization machinery. */
+ if (gcall *call_stmt = dyn_cast <gcall *> (stmt))
{
- tree orig_fn = gimple_call_fn (stmt);
- tree fn;
- if (!orig_fn)
- continue;
- if (TREE_CODE (orig_fn) == SSA_NAME)
- fn = VN_INFO (orig_fn)->valnum;
- else if (TREE_CODE (orig_fn) == OBJ_TYPE_REF
- && TREE_CODE (OBJ_TYPE_REF_EXPR (orig_fn)) == SSA_NAME)
+ tree fn = gimple_call_fn (call_stmt);
+ if (fn
+ && flag_devirtualize
+ && virtual_method_call_p (fn))
{
- fn = VN_INFO (OBJ_TYPE_REF_EXPR (orig_fn))->valnum;
- if (!gimple_call_addr_fndecl (fn))
+ tree otr_type = obj_type_ref_class (fn);
+ tree instance;
+ ipa_polymorphic_call_context context (current_function_decl, fn, stmt, &instance);
+ bool final;
+
+ context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt);
+
+ vec <cgraph_node *>targets
+ = possible_polymorphic_call_targets (obj_type_ref_class (fn),
+ tree_to_uhwi
+ (OBJ_TYPE_REF_TOKEN (fn)),
+ context,
+ &final);
+ if (dump_file)
+ dump_possible_polymorphic_call_targets (dump_file,
+ obj_type_ref_class (fn),
+ tree_to_uhwi
+ (OBJ_TYPE_REF_TOKEN (fn)),
+ context);
+ if (final && targets.length () <= 1 && dbg_cnt (devirt))
{
- fn = ipa_intraprocedural_devirtualization (stmt);
- if (fn)
- fn = build_fold_addr_expr (fn);
+ tree fn;
+ if (targets.length () == 1)
+ fn = targets[0]->decl;
+ else
+ fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
+ if (dump_enabled_p ())
+ {
+ location_t loc = gimple_location_safe (stmt);
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+ "converting indirect call to "
+ "function %s\n",
+ cgraph_node::get (fn)->name ());
+ }
+ gimple_call_set_fndecl (call_stmt, fn);
+ maybe_remove_unused_call_args (cfun, call_stmt);
+ gimple_set_modified (stmt, true);
}
}
- else
- continue;
- if (gimple_call_addr_fndecl (fn) != NULL_TREE
- && useless_type_conversion_p (TREE_TYPE (orig_fn),
- TREE_TYPE (fn)))
- {
- bool can_make_abnormal_goto
- = stmt_can_make_abnormal_goto (stmt);
- bool was_noreturn = gimple_call_noreturn_p (stmt);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Replacing call target with ");
- print_generic_expr (dump_file, fn, 0);
- fprintf (dump_file, " in ");
- print_gimple_stmt (dump_file, stmt, 0, 0);
- }
-
- gimple_call_set_fn (stmt, fn);
- el_to_update.safe_push (stmt);
+ }
+ if (gimple_modified_p (stmt))
+ {
+ /* If a formerly non-invariant ADDR_EXPR is turned into an
+ invariant one it was on a separate stmt. */
+ if (gimple_assign_single_p (stmt)
+ && TREE_CODE (gimple_assign_rhs1 (stmt)) == ADDR_EXPR)
+ recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
+ gimple old_stmt = stmt;
+ if (is_gimple_call (stmt))
+ {
+ /* ??? Only fold calls inplace for now, this may create new
+ SSA names which in turn will confuse free_scc_vn SSA name
+ release code. */
+ fold_stmt_inplace (&gsi);
/* When changing a call into a noreturn call, cfg cleanup
is needed to fix up the noreturn call. */
if (!was_noreturn && gimple_call_noreturn_p (stmt))
+ el_to_fixup.safe_push (stmt);
+ }
+ else
+ {
+ fold_stmt (&gsi);
+ stmt = gsi_stmt (gsi);
+ if ((gimple_code (stmt) == GIMPLE_COND
+ && (gimple_cond_true_p (as_a <gcond *> (stmt))
+ || gimple_cond_false_p (as_a <gcond *> (stmt))))
+ || (gimple_code (stmt) == GIMPLE_SWITCH
+ && TREE_CODE (gimple_switch_index (
+ as_a <gswitch *> (stmt)))
+ == INTEGER_CST))
el_todo |= TODO_cleanup_cfg;
+ }
+ /* If we removed EH side-effects from the statement, clean
+ its EH information. */
+ if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
+ {
+ bitmap_set_bit (need_eh_cleanup,
+ gimple_bb (stmt)->index);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Removed EH side-effects.\n");
+ }
+ /* Likewise for AB side-effects. */
+ if (can_make_abnormal_goto
+ && !stmt_can_make_abnormal_goto (stmt))
+ {
+ bitmap_set_bit (need_ab_cleanup,
+ gimple_bb (stmt)->index);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Removed AB side-effects.\n");
+ }
+ update_stmt (stmt);
+ if (vdef != gimple_vdef (stmt))
+ VN_INFO (vdef)->valnum = vuse;
+ }
- /* If we removed EH side-effects from the statement, clean
- its EH information. */
- if (maybe_clean_or_replace_eh_stmt (stmt, stmt))
- {
- bitmap_set_bit (need_eh_cleanup,
- gimple_bb (stmt)->index);
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Removed EH side-effects.\n");
- }
-
- /* Likewise for AB side-effects. */
- if (can_make_abnormal_goto
- && !stmt_can_make_abnormal_goto (stmt))
- {
- bitmap_set_bit (need_ab_cleanup,
- gimple_bb (stmt)->index);
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, " Removed AB side-effects.\n");
- }
+ /* Make new values available - for fully redundant LHS we
+ continue with the next stmt above and skip this. */
+ def_operand_p defp;
+ FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
+ eliminate_push_avail (DEF_FROM_PTR (defp));
+ }
- /* Changing an indirect call to a direct call may
- have exposed different semantics. This may
- require an SSA update. */
- el_todo |= TODO_update_ssa_only_virtuals;
+ /* Replace destination PHI arguments. */
+ edge_iterator ei;
+ edge e;
+ FOR_EACH_EDGE (e, ei, b->succs)
+ {
+ for (gphi_iterator gsi = gsi_start_phis (e->dest);
+ !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gphi *phi = gsi.phi ();
+ use_operand_p use_p = PHI_ARG_DEF_PTR_FROM_EDGE (phi, e);
+ tree arg = USE_FROM_PTR (use_p);
+ if (TREE_CODE (arg) != SSA_NAME
+ || virtual_operand_p (arg))
+ continue;
+ tree sprime = eliminate_avail (arg);
+ if (sprime && may_propagate_copy (arg, sprime))
+ {
+ propagate_value (use_p, sprime);
+ if (TREE_CODE (sprime) == SSA_NAME)
+ gimple_set_plf (SSA_NAME_DEF_STMT (sprime), NECESSARY, true);
}
}
}
{
tree entry;
while ((entry = el_avail_stack.pop ()) != NULL_TREE)
- el_avail[SSA_NAME_VERSION (VN_INFO (entry)->valnum)] = NULL_TREE;
+ {
+ tree valnum = VN_INFO (entry)->valnum;
+ tree old = el_avail[SSA_NAME_VERSION (valnum)];
+ if (old == entry)
+ el_avail[SSA_NAME_VERSION (valnum)] = NULL_TREE;
+ else
+ el_avail[SSA_NAME_VERSION (valnum)] = entry;
+ }
}
/* Eliminate fully redundant computations. */
static unsigned int
-eliminate (void)
+eliminate (bool do_pre)
{
gimple_stmt_iterator gsi;
gimple stmt;
- unsigned i;
need_eh_cleanup = BITMAP_ALLOC (NULL);
need_ab_cleanup = BITMAP_ALLOC (NULL);
el_to_remove.create (0);
- el_to_update.create (0);
+ el_to_fixup.create (0);
el_todo = 0;
- el_avail.create (0);
+ el_avail.create (num_ssa_names);
el_avail_stack.create (0);
- eliminate_dom_walker (CDI_DOMINATORS).walk (cfun->cfg->x_entry_block_ptr);
+ eliminate_dom_walker (CDI_DOMINATORS,
+ do_pre).walk (cfun->cfg->x_entry_block_ptr);
el_avail.release ();
el_avail_stack.release ();
/* We cannot remove stmts during BB walk, especially not release SSA
names there as this confuses the VN machinery. The stmts ending
- up in el_to_remove are either stores or simple copies. */
- FOR_EACH_VEC_ELT (el_to_remove, i, stmt)
+ up in el_to_remove are either stores or simple copies.
+ Remove stmts in reverse order to make debug stmt creation possible. */
+ while (!el_to_remove.is_empty ())
{
- tree lhs = gimple_assign_lhs (stmt);
- tree rhs = gimple_assign_rhs1 (stmt);
- use_operand_p use_p;
- gimple use_stmt;
-
- /* If there is a single use only, propagate the equivalency
- instead of keeping the copy. */
- if (TREE_CODE (lhs) == SSA_NAME
- && TREE_CODE (rhs) == SSA_NAME
- && single_imm_use (lhs, &use_p, &use_stmt)
- && may_propagate_copy (USE_FROM_PTR (use_p), rhs))
+ stmt = el_to_remove.pop ();
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
{
- SET_USE (use_p, rhs);
- update_stmt (use_stmt);
- if (inserted_exprs
- && bitmap_bit_p (inserted_exprs, SSA_NAME_VERSION (lhs))
- && TREE_CODE (rhs) == SSA_NAME)
- gimple_set_plf (SSA_NAME_DEF_STMT (rhs), NECESSARY, true);
+ fprintf (dump_file, "Removing dead stmt ");
+ print_gimple_stmt (dump_file, stmt, 0, 0);
}
- /* If this is a store or a now unused copy, remove it. */
- if (TREE_CODE (lhs) != SSA_NAME
- || has_zero_uses (lhs))
+ tree lhs;
+ if (gimple_code (stmt) == GIMPLE_PHI)
+ lhs = gimple_phi_result (stmt);
+ else
+ lhs = gimple_get_lhs (stmt);
+
+ if (inserted_exprs
+ && TREE_CODE (lhs) == SSA_NAME)
+ bitmap_clear_bit (inserted_exprs, SSA_NAME_VERSION (lhs));
+
+ gsi = gsi_for_stmt (stmt);
+ if (gimple_code (stmt) == GIMPLE_PHI)
+ remove_phi_node (&gsi, true);
+ else
{
basic_block bb = gimple_bb (stmt);
- gsi = gsi_for_stmt (stmt);
unlink_stmt_vdef (stmt);
if (gsi_remove (&gsi, true))
bitmap_set_bit (need_eh_cleanup, bb->index);
- if (inserted_exprs
- && TREE_CODE (lhs) == SSA_NAME)
- bitmap_clear_bit (inserted_exprs, SSA_NAME_VERSION (lhs));
release_defs (stmt);
}
+
+ /* Removing a stmt may expose a forwarder block. */
+ el_todo |= TODO_cleanup_cfg;
}
el_to_remove.release ();
- /* We cannot update call statements with virtual operands during
- SSA walk. This might remove them which in turn makes our
- VN lattice invalid. */
- FOR_EACH_VEC_ELT (el_to_update, i, stmt)
- update_stmt (stmt);
- el_to_update.release ();
+ /* Fixup stmts that became noreturn calls. This may require splitting
+ blocks and thus isn't possible during the dominator walk. Do this
+ in reverse order so we don't inadvertedly remove a stmt we want to
+ fixup by visiting a dominating now noreturn call first. */
+ while (!el_to_fixup.is_empty ())
+ {
+ stmt = el_to_fixup.pop ();
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Fixing up noreturn call ");
+ print_gimple_stmt (dump_file, stmt, 0, 0);
+ }
+
+ if (fixup_noreturn_call (stmt))
+ el_todo |= TODO_cleanup_cfg;
+ }
+ el_to_fixup.release ();
return el_todo;
}
calculate_dominance_info (CDI_DOMINATORS);
bitmap_obstack_initialize (&grand_bitmap_obstack);
- phi_translate_table.create (5110);
- expression_to_id.create (num_ssa_names * 3);
- bitmap_set_pool = create_alloc_pool ("Bitmap sets",
- sizeof (struct bitmap_set), 30);
- pre_expr_pool = create_alloc_pool ("pre_expr nodes",
- sizeof (struct pre_expr_d), 30);
+ phi_translate_table = new hash_table<expr_pred_trans_d> (5110);
+ expression_to_id = new hash_table<pre_expr_d> (num_ssa_names * 3);
FOR_ALL_BB_FN (bb, cfun)
{
EXP_GEN (bb) = bitmap_set_new ();
value_expressions.release ();
BITMAP_FREE (inserted_exprs);
bitmap_obstack_release (&grand_bitmap_obstack);
- free_alloc_pool (bitmap_set_pool);
- free_alloc_pool (pre_expr_pool);
- phi_translate_table.dispose ();
- expression_to_id.dispose ();
+ bitmap_set_pool.release ();
+ pre_expr_pool.release ();
+ delete phi_translate_table;
+ phi_translate_table = NULL;
+ delete expression_to_id;
+ expression_to_id = NULL;
name_to_id.release ();
free_aux_for_blocks ();
GIMPLE_PASS, /* type */
"pre", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_PRE, /* tv_id */
/* PROP_no_crit_edges is ensured by placing pass_split_crit_edges before
pass_pre. */
remove_fake_exit_edges ();
gsi_commit_edge_inserts ();
+ /* Eliminate folds statements which might (should not...) end up
+ not keeping virtual operands up-to-date. */
+ gcc_assert (!need_ssa_update_p (fun));
+
/* Remove all the redundant expressions. */
- todo |= eliminate ();
+ todo |= eliminate (true);
statistics_counter_event (fun, "Insertions", pre_stats.insertions);
statistics_counter_event (fun, "PA inserted", pre_stats.pa_insert);
GIMPLE_PASS, /* type */
"fre", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_FRE, /* tv_id */
( PROP_cfg | PROP_ssa ), /* properties_required */
0, /* properties_provided */
memset (&pre_stats, 0, sizeof (pre_stats));
/* Remove all the redundant expressions. */
- todo |= eliminate ();
+ todo |= eliminate (false);
todo |= fini_eliminate ();