#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "ggc.h"
#include "tree.h"
#include "basic-block.h"
#include "diagnostic.h"
#include "tree-inline.h"
#include "tree-flow.h"
#include "tree-gimple.h"
#include "tree-dump.h"
#include "timevar.h"
#include "fibheap.h"
#include "hashtab.h"
#include "tree-iterator.h"
#include "real.h"
#include "alloc-pool.h"
#include "tree-pass.h"
#include "flags.h"
#include "bitmap.h"
#include "langhooks.h"
#include "cfgloop.h"
static struct
{
int sunk;
} sink_stats;
static basic_block
find_bb_for_arg (tree phi, tree def)
{
int i;
bool foundone = false;
basic_block result = NULL;
for (i = 0; i < PHI_NUM_ARGS (phi); i++)
if (PHI_ARG_DEF (phi, i) == def)
{
if (foundone)
return NULL;
foundone = true;
result = PHI_ARG_EDGE (phi, i)->src;
}
return result;
}
static bool
all_immediate_uses_same_place (tree stmt)
{
tree firstuse = NULL_TREE;
ssa_op_iter op_iter;
imm_use_iterator imm_iter;
use_operand_p use_p;
tree var;
FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
{
FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
{
if (firstuse == NULL_TREE)
firstuse = USE_STMT (use_p);
else
if (firstuse != USE_STMT (use_p))
return false;
}
}
return true;
}
bool
is_hidden_global_store (tree stmt)
{
if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_VIRTUAL_DEFS))
{
tree lhs;
gcc_assert (TREE_CODE (stmt) == MODIFY_EXPR);
lhs = TREE_OPERAND (stmt, 0);
if (REFERENCE_CLASS_P (lhs))
lhs = get_base_address (lhs);
if (lhs == NULL_TREE)
{
return true;
}
else if (DECL_P (lhs))
{
if (is_global_var (lhs))
return true;
}
else if (INDIRECT_REF_P (lhs))
{
tree ptr = TREE_OPERAND (lhs, 0);
struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
tree nmt = (pi) ? pi->name_mem_tag : NULL_TREE;
tree smt = var_ann (SSA_NAME_VAR (ptr))->symbol_mem_tag;
if ((nmt && is_global_var (nmt))
|| (smt && is_global_var (smt)))
{
return true;
}
}
else
gcc_unreachable ();
}
return false;
}
static basic_block
nearest_common_dominator_of_uses (tree stmt)
{
bitmap blocks = BITMAP_ALLOC (NULL);
basic_block commondom;
unsigned int j;
bitmap_iterator bi;
ssa_op_iter op_iter;
imm_use_iterator imm_iter;
use_operand_p use_p;
tree var;
bitmap_clear (blocks);
FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
{
FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
{
tree usestmt = USE_STMT (use_p);
basic_block useblock;
if (TREE_CODE (usestmt) == PHI_NODE)
{
int idx = PHI_ARG_INDEX_FROM_USE (use_p);
useblock = PHI_ARG_EDGE (usestmt, idx)->src;
}
else
{
useblock = bb_for_stmt (usestmt);
}
if (useblock == ENTRY_BLOCK_PTR)
{
BITMAP_FREE (blocks);
return NULL;
}
bitmap_set_bit (blocks, useblock->index);
}
}
commondom = BASIC_BLOCK (bitmap_first_set_bit (blocks));
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, j, bi)
commondom = nearest_common_dominator (CDI_DOMINATORS, commondom,
BASIC_BLOCK (j));
BITMAP_FREE (blocks);
return commondom;
}
static tree
statement_sink_location (tree stmt, basic_block frombb)
{
tree use, def;
use_operand_p one_use = NULL_USE_OPERAND_P;
basic_block sinkbb;
use_operand_p use_p;
def_operand_p def_p;
ssa_op_iter iter;
stmt_ann_t ann;
tree rhs;
imm_use_iterator imm_iter;
FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
{
FOR_EACH_IMM_USE_FAST (one_use, imm_iter, def)
{
break;
}
if (one_use != NULL_USE_OPERAND_P)
break;
}
if (one_use == NULL_USE_OPERAND_P)
return NULL;
if (TREE_CODE (stmt) != MODIFY_EXPR)
return NULL;
rhs = TREE_OPERAND (stmt, 1);
ann = stmt_ann (stmt);
if (stmt_ends_bb_p (stmt)
|| TREE_SIDE_EFFECTS (rhs)
|| TREE_CODE (rhs) == EXC_PTR_EXPR
|| TREE_CODE (rhs) == FILTER_EXPR
|| is_hidden_global_store (stmt)
|| ann->has_volatile_ops
|| !ZERO_SSA_OPERANDS (stmt, SSA_OP_VUSE))
return NULL;
FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_ALL_DEFS)
{
tree def = DEF_FROM_PTR (def_p);
if (is_global_var (SSA_NAME_VAR (def))
|| SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def))
return NULL;
}
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
{
tree use = USE_FROM_PTR (use_p);
if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use))
return NULL;
}
if (!all_immediate_uses_same_place (stmt))
{
basic_block commondom = nearest_common_dominator_of_uses (stmt);
if (commondom == frombb)
return NULL;
if (!dominated_by_p (CDI_DOMINATORS, commondom, frombb))
return NULL;
if (dominated_by_p (CDI_POST_DOMINATORS, frombb, commondom))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Not moving store, common dominator post-dominates from block.\n");
return NULL;
}
if (commondom == frombb || commondom->loop_depth > frombb->loop_depth)
return NULL;
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Common dominator of all uses is %d\n",
commondom->index);
}
return first_stmt (commondom);
}
use = USE_STMT (one_use);
if (TREE_CODE (use) != PHI_NODE)
{
sinkbb = bb_for_stmt (use);
if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
|| sinkbb->loop_father != frombb->loop_father)
return NULL;
return use;
}
FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
break;
sinkbb = find_bb_for_arg (use, def);
if (!sinkbb)
return NULL;
if (bb_for_stmt (use) == frombb)
return NULL;
if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
|| sinkbb->loop_father != frombb->loop_father)
return NULL;
return first_stmt (sinkbb);
}
static void
sink_code_in_bb (basic_block bb)
{
basic_block son;
block_stmt_iterator bsi;
edge_iterator ei;
edge e;
if (first_dom_son (CDI_DOMINATORS, bb) == NULL)
goto earlyout;
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_ABNORMAL)
goto earlyout;
for (bsi = bsi_last (bb); !bsi_end_p (bsi);)
{
tree stmt = bsi_stmt (bsi);
block_stmt_iterator tobsi;
tree sinkstmt;
sinkstmt = statement_sink_location (stmt, bb);
if (!sinkstmt)
{
if (!bsi_end_p (bsi))
bsi_prev (&bsi);
continue;
}
if (dump_file)
{
fprintf (dump_file, "Sinking ");
print_generic_expr (dump_file, stmt, TDF_VOPS);
fprintf (dump_file, " from bb %d to bb %d\n",
bb->index, bb_for_stmt (sinkstmt)->index);
}
tobsi = bsi_for_stmt (sinkstmt);
while (!bsi_end_p (tobsi)
&& TREE_CODE (bsi_stmt (tobsi)) == LABEL_EXPR)
bsi_next (&tobsi);
if (bsi_end_p (tobsi))
bsi_move_to_bb_end (&bsi, bb_for_stmt (sinkstmt));
else
bsi_move_before (&bsi, &tobsi);
sink_stats.sunk++;
if (!bsi_end_p (bsi))
bsi_prev (&bsi);
}
earlyout:
for (son = first_dom_son (CDI_POST_DOMINATORS, bb);
son;
son = next_dom_son (CDI_POST_DOMINATORS, son))
{
sink_code_in_bb (son);
}
}
static void
execute_sink_code (void)
{
struct loops *loops = loop_optimizer_init (LOOPS_NORMAL);
connect_infinite_loops_to_exit ();
memset (&sink_stats, 0, sizeof (sink_stats));
calculate_dominance_info (CDI_DOMINATORS);
calculate_dominance_info (CDI_POST_DOMINATORS);
sink_code_in_bb (EXIT_BLOCK_PTR);
if (dump_file && (dump_flags & TDF_STATS))
fprintf (dump_file, "Sunk statements:%d\n", sink_stats.sunk);
free_dominance_info (CDI_POST_DOMINATORS);
remove_fake_exit_edges ();
loop_optimizer_finalize (loops);
}
static unsigned int
do_sink (void)
{
execute_sink_code ();
return 0;
}
static bool
gate_sink (void)
{
return flag_tree_sink != 0;
}
struct tree_opt_pass pass_sink_code =
{
"sink",
gate_sink,
do_sink,
NULL,
NULL,
0,
TV_TREE_SINK,
PROP_no_crit_edges | PROP_cfg
| PROP_ssa | PROP_alias,
0,
0,
0,
TODO_update_ssa
| TODO_dump_func
| TODO_ggc_collect
| TODO_verify_ssa,
0
};