1540 lines
40 KiB
C
1540 lines
40 KiB
C
/* Convert a program in SSA form into Normal form.
|
|
Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
|
Contributed by Andrew Macleod <amacleod@redhat.com>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "tree.h"
|
|
#include "ggc.h"
|
|
#include "basic-block.h"
|
|
#include "diagnostic.h"
|
|
#include "bitmap.h"
|
|
#include "tree-flow.h"
|
|
#include "timevar.h"
|
|
#include "tree-dump.h"
|
|
#include "tree-ssa-live.h"
|
|
#include "tree-pass.h"
|
|
#include "toplev.h"
|
|
|
|
|
|
/* Used to hold all the components required to do SSA PHI elimination.
|
|
The node and pred/succ list is a simple linear list of nodes and
|
|
edges represented as pairs of nodes.
|
|
|
|
The predecessor and successor list: Nodes are entered in pairs, where
|
|
[0] ->PRED, [1]->SUCC. All the even indexes in the array represent
|
|
predecessors, all the odd elements are successors.
|
|
|
|
Rationale:
|
|
When implemented as bitmaps, very large programs SSA->Normal times were
|
|
being dominated by clearing the interference graph.
|
|
|
|
Typically this list of edges is extremely small since it only includes
|
|
PHI results and uses from a single edge which have not coalesced with
|
|
each other. This means that no virtual PHI nodes are included, and
|
|
empirical evidence suggests that the number of edges rarely exceed
|
|
3, and in a bootstrap of GCC, the maximum size encountered was 7.
|
|
This also limits the number of possible nodes that are involved to
|
|
rarely more than 6, and in the bootstrap of gcc, the maximum number
|
|
of nodes encountered was 12. */
|
|
|
|
typedef struct _elim_graph {
|
|
/* Size of the elimination vectors. */
|
|
int size;
|
|
|
|
/* List of nodes in the elimination graph. */
|
|
VEC(tree,heap) *nodes;
|
|
|
|
/* The predecessor and successor edge list. */
|
|
VEC(int,heap) *edge_list;
|
|
|
|
/* Visited vector. */
|
|
sbitmap visited;
|
|
|
|
/* Stack for visited nodes. */
|
|
VEC(int,heap) *stack;
|
|
|
|
/* The variable partition map. */
|
|
var_map map;
|
|
|
|
/* Edge being eliminated by this graph. */
|
|
edge e;
|
|
|
|
/* List of constant copies to emit. These are pushed on in pairs. */
|
|
VEC(tree,heap) *const_copies;
|
|
} *elim_graph;
|
|
|
|
|
|
/* Create a temporary variable based on the type of variable T. Use T's name
|
|
as the prefix. */
|
|
|
|
static tree
|
|
create_temp (tree t)
|
|
{
|
|
tree tmp;
|
|
const char *name = NULL;
|
|
tree type;
|
|
|
|
if (TREE_CODE (t) == SSA_NAME)
|
|
t = SSA_NAME_VAR (t);
|
|
|
|
gcc_assert (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL);
|
|
|
|
type = TREE_TYPE (t);
|
|
tmp = DECL_NAME (t);
|
|
if (tmp)
|
|
name = IDENTIFIER_POINTER (tmp);
|
|
|
|
if (name == NULL)
|
|
name = "temp";
|
|
tmp = create_tmp_var (type, name);
|
|
|
|
if (DECL_DEBUG_EXPR_IS_FROM (t) && DECL_DEBUG_EXPR (t))
|
|
{
|
|
SET_DECL_DEBUG_EXPR (tmp, DECL_DEBUG_EXPR (t));
|
|
DECL_DEBUG_EXPR_IS_FROM (tmp) = 1;
|
|
}
|
|
else if (!DECL_IGNORED_P (t))
|
|
{
|
|
SET_DECL_DEBUG_EXPR (tmp, t);
|
|
DECL_DEBUG_EXPR_IS_FROM (tmp) = 1;
|
|
}
|
|
DECL_ARTIFICIAL (tmp) = DECL_ARTIFICIAL (t);
|
|
DECL_IGNORED_P (tmp) = DECL_IGNORED_P (t);
|
|
DECL_GIMPLE_REG_P (tmp) = DECL_GIMPLE_REG_P (t);
|
|
add_referenced_var (tmp);
|
|
|
|
/* add_referenced_var will create the annotation and set up some
|
|
of the flags in the annotation. However, some flags we need to
|
|
inherit from our original variable. */
|
|
set_symbol_mem_tag (tmp, symbol_mem_tag (t));
|
|
if (is_call_clobbered (t))
|
|
mark_call_clobbered (tmp, var_ann (t)->escape_mask);
|
|
if (bitmap_bit_p (gimple_call_used_vars (cfun), DECL_UID (t)))
|
|
bitmap_set_bit (gimple_call_used_vars (cfun), DECL_UID (tmp));
|
|
|
|
return tmp;
|
|
}
|
|
|
|
|
|
/* This helper function fill insert a copy from a constant or variable SRC to
|
|
variable DEST on edge E. */
|
|
|
|
static void
|
|
insert_copy_on_edge (edge e, tree dest, tree src)
|
|
{
|
|
gimple copy;
|
|
|
|
copy = gimple_build_assign (dest, src);
|
|
set_is_used (dest);
|
|
|
|
if (TREE_CODE (src) == ADDR_EXPR)
|
|
src = TREE_OPERAND (src, 0);
|
|
if (TREE_CODE (src) == VAR_DECL || TREE_CODE (src) == PARM_DECL)
|
|
set_is_used (src);
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file,
|
|
"Inserting a copy on edge BB%d->BB%d :",
|
|
e->src->index,
|
|
e->dest->index);
|
|
print_gimple_stmt (dump_file, copy, 0, dump_flags);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
gsi_insert_on_edge (e, copy);
|
|
}
|
|
|
|
|
|
/* Create an elimination graph with SIZE nodes and associated data
|
|
structures. */
|
|
|
|
static elim_graph
|
|
new_elim_graph (int size)
|
|
{
|
|
elim_graph g = (elim_graph) xmalloc (sizeof (struct _elim_graph));
|
|
|
|
g->nodes = VEC_alloc (tree, heap, 30);
|
|
g->const_copies = VEC_alloc (tree, heap, 20);
|
|
g->edge_list = VEC_alloc (int, heap, 20);
|
|
g->stack = VEC_alloc (int, heap, 30);
|
|
|
|
g->visited = sbitmap_alloc (size);
|
|
|
|
return g;
|
|
}
|
|
|
|
|
|
/* Empty elimination graph G. */
|
|
|
|
static inline void
|
|
clear_elim_graph (elim_graph g)
|
|
{
|
|
VEC_truncate (tree, g->nodes, 0);
|
|
VEC_truncate (int, g->edge_list, 0);
|
|
}
|
|
|
|
|
|
/* Delete elimination graph G. */
|
|
|
|
static inline void
|
|
delete_elim_graph (elim_graph g)
|
|
{
|
|
sbitmap_free (g->visited);
|
|
VEC_free (int, heap, g->stack);
|
|
VEC_free (int, heap, g->edge_list);
|
|
VEC_free (tree, heap, g->const_copies);
|
|
VEC_free (tree, heap, g->nodes);
|
|
free (g);
|
|
}
|
|
|
|
|
|
/* Return the number of nodes in graph G. */
|
|
|
|
static inline int
|
|
elim_graph_size (elim_graph g)
|
|
{
|
|
return VEC_length (tree, g->nodes);
|
|
}
|
|
|
|
|
|
/* Add NODE to graph G, if it doesn't exist already. */
|
|
|
|
static inline void
|
|
elim_graph_add_node (elim_graph g, tree node)
|
|
{
|
|
int x;
|
|
tree t;
|
|
|
|
for (x = 0; VEC_iterate (tree, g->nodes, x, t); x++)
|
|
if (t == node)
|
|
return;
|
|
VEC_safe_push (tree, heap, g->nodes, node);
|
|
}
|
|
|
|
|
|
/* Add the edge PRED->SUCC to graph G. */
|
|
|
|
static inline void
|
|
elim_graph_add_edge (elim_graph g, int pred, int succ)
|
|
{
|
|
VEC_safe_push (int, heap, g->edge_list, pred);
|
|
VEC_safe_push (int, heap, g->edge_list, succ);
|
|
}
|
|
|
|
|
|
/* Remove an edge from graph G for which NODE is the predecessor, and
|
|
return the successor node. -1 is returned if there is no such edge. */
|
|
|
|
static inline int
|
|
elim_graph_remove_succ_edge (elim_graph g, int node)
|
|
{
|
|
int y;
|
|
unsigned x;
|
|
for (x = 0; x < VEC_length (int, g->edge_list); x += 2)
|
|
if (VEC_index (int, g->edge_list, x) == node)
|
|
{
|
|
VEC_replace (int, g->edge_list, x, -1);
|
|
y = VEC_index (int, g->edge_list, x + 1);
|
|
VEC_replace (int, g->edge_list, x + 1, -1);
|
|
return y;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Find all the nodes in GRAPH which are successors to NODE in the
|
|
edge list. VAR will hold the partition number found. CODE is the
|
|
code fragment executed for every node found. */
|
|
|
|
#define FOR_EACH_ELIM_GRAPH_SUCC(GRAPH, NODE, VAR, CODE) \
|
|
do { \
|
|
unsigned x_; \
|
|
int y_; \
|
|
for (x_ = 0; x_ < VEC_length (int, (GRAPH)->edge_list); x_ += 2) \
|
|
{ \
|
|
y_ = VEC_index (int, (GRAPH)->edge_list, x_); \
|
|
if (y_ != (NODE)) \
|
|
continue; \
|
|
(VAR) = VEC_index (int, (GRAPH)->edge_list, x_ + 1); \
|
|
CODE; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
/* Find all the nodes which are predecessors of NODE in the edge list for
|
|
GRAPH. VAR will hold the partition number found. CODE is the
|
|
code fragment executed for every node found. */
|
|
|
|
#define FOR_EACH_ELIM_GRAPH_PRED(GRAPH, NODE, VAR, CODE) \
|
|
do { \
|
|
unsigned x_; \
|
|
int y_; \
|
|
for (x_ = 0; x_ < VEC_length (int, (GRAPH)->edge_list); x_ += 2) \
|
|
{ \
|
|
y_ = VEC_index (int, (GRAPH)->edge_list, x_ + 1); \
|
|
if (y_ != (NODE)) \
|
|
continue; \
|
|
(VAR) = VEC_index (int, (GRAPH)->edge_list, x_); \
|
|
CODE; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
/* Add T to elimination graph G. */
|
|
|
|
static inline void
|
|
eliminate_name (elim_graph g, tree T)
|
|
{
|
|
elim_graph_add_node (g, T);
|
|
}
|
|
|
|
|
|
/* Build elimination graph G for basic block BB on incoming PHI edge
|
|
G->e. */
|
|
|
|
static void
|
|
eliminate_build (elim_graph g, basic_block B)
|
|
{
|
|
tree T0, Ti;
|
|
int p0, pi;
|
|
gimple_stmt_iterator gsi;
|
|
|
|
clear_elim_graph (g);
|
|
|
|
for (gsi = gsi_start_phis (B); !gsi_end_p (gsi); gsi_next (&gsi))
|
|
{
|
|
gimple phi = gsi_stmt (gsi);
|
|
|
|
T0 = var_to_partition_to_var (g->map, gimple_phi_result (phi));
|
|
|
|
/* Ignore results which are not in partitions. */
|
|
if (T0 == NULL_TREE)
|
|
continue;
|
|
|
|
Ti = PHI_ARG_DEF (phi, g->e->dest_idx);
|
|
|
|
/* If this argument is a constant, or a SSA_NAME which is being
|
|
left in SSA form, just queue a copy to be emitted on this
|
|
edge. */
|
|
if (!phi_ssa_name_p (Ti)
|
|
|| (TREE_CODE (Ti) == SSA_NAME
|
|
&& var_to_partition (g->map, Ti) == NO_PARTITION))
|
|
{
|
|
/* Save constant copies until all other copies have been emitted
|
|
on this edge. */
|
|
VEC_safe_push (tree, heap, g->const_copies, T0);
|
|
VEC_safe_push (tree, heap, g->const_copies, Ti);
|
|
}
|
|
else
|
|
{
|
|
Ti = var_to_partition_to_var (g->map, Ti);
|
|
if (T0 != Ti)
|
|
{
|
|
eliminate_name (g, T0);
|
|
eliminate_name (g, Ti);
|
|
p0 = var_to_partition (g->map, T0);
|
|
pi = var_to_partition (g->map, Ti);
|
|
elim_graph_add_edge (g, p0, pi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Push successors of T onto the elimination stack for G. */
|
|
|
|
static void
|
|
elim_forward (elim_graph g, int T)
|
|
{
|
|
int S;
|
|
SET_BIT (g->visited, T);
|
|
FOR_EACH_ELIM_GRAPH_SUCC (g, T, S,
|
|
{
|
|
if (!TEST_BIT (g->visited, S))
|
|
elim_forward (g, S);
|
|
});
|
|
VEC_safe_push (int, heap, g->stack, T);
|
|
}
|
|
|
|
|
|
/* Return 1 if there unvisited predecessors of T in graph G. */
|
|
|
|
static int
|
|
elim_unvisited_predecessor (elim_graph g, int T)
|
|
{
|
|
int P;
|
|
FOR_EACH_ELIM_GRAPH_PRED (g, T, P,
|
|
{
|
|
if (!TEST_BIT (g->visited, P))
|
|
return 1;
|
|
});
|
|
return 0;
|
|
}
|
|
|
|
/* Process predecessors first, and insert a copy. */
|
|
|
|
static void
|
|
elim_backward (elim_graph g, int T)
|
|
{
|
|
int P;
|
|
SET_BIT (g->visited, T);
|
|
FOR_EACH_ELIM_GRAPH_PRED (g, T, P,
|
|
{
|
|
if (!TEST_BIT (g->visited, P))
|
|
{
|
|
elim_backward (g, P);
|
|
insert_copy_on_edge (g->e,
|
|
partition_to_var (g->map, P),
|
|
partition_to_var (g->map, T));
|
|
}
|
|
});
|
|
}
|
|
|
|
/* Insert required copies for T in graph G. Check for a strongly connected
|
|
region, and create a temporary to break the cycle if one is found. */
|
|
|
|
static void
|
|
elim_create (elim_graph g, int T)
|
|
{
|
|
tree U;
|
|
int P, S;
|
|
|
|
if (elim_unvisited_predecessor (g, T))
|
|
{
|
|
U = create_temp (partition_to_var (g->map, T));
|
|
insert_copy_on_edge (g->e, U, partition_to_var (g->map, T));
|
|
FOR_EACH_ELIM_GRAPH_PRED (g, T, P,
|
|
{
|
|
if (!TEST_BIT (g->visited, P))
|
|
{
|
|
elim_backward (g, P);
|
|
insert_copy_on_edge (g->e, partition_to_var (g->map, P), U);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
S = elim_graph_remove_succ_edge (g, T);
|
|
if (S != -1)
|
|
{
|
|
SET_BIT (g->visited, T);
|
|
insert_copy_on_edge (g->e,
|
|
partition_to_var (g->map, T),
|
|
partition_to_var (g->map, S));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* Eliminate all the phi nodes on edge E in graph G. */
|
|
|
|
static void
|
|
eliminate_phi (edge e, elim_graph g)
|
|
{
|
|
int x;
|
|
basic_block B = e->dest;
|
|
|
|
gcc_assert (VEC_length (tree, g->const_copies) == 0);
|
|
|
|
/* Abnormal edges already have everything coalesced. */
|
|
if (e->flags & EDGE_ABNORMAL)
|
|
return;
|
|
|
|
g->e = e;
|
|
|
|
eliminate_build (g, B);
|
|
|
|
if (elim_graph_size (g) != 0)
|
|
{
|
|
tree var;
|
|
|
|
sbitmap_zero (g->visited);
|
|
VEC_truncate (int, g->stack, 0);
|
|
|
|
for (x = 0; VEC_iterate (tree, g->nodes, x, var); x++)
|
|
{
|
|
int p = var_to_partition (g->map, var);
|
|
if (!TEST_BIT (g->visited, p))
|
|
elim_forward (g, p);
|
|
}
|
|
|
|
sbitmap_zero (g->visited);
|
|
while (VEC_length (int, g->stack) > 0)
|
|
{
|
|
x = VEC_pop (int, g->stack);
|
|
if (!TEST_BIT (g->visited, x))
|
|
elim_create (g, x);
|
|
}
|
|
}
|
|
|
|
/* If there are any pending constant copies, issue them now. */
|
|
while (VEC_length (tree, g->const_copies) > 0)
|
|
{
|
|
tree src, dest;
|
|
src = VEC_pop (tree, g->const_copies);
|
|
dest = VEC_pop (tree, g->const_copies);
|
|
insert_copy_on_edge (e, dest, src);
|
|
}
|
|
}
|
|
|
|
|
|
/* Take the ssa-name var_map MAP, and assign real variables to each
|
|
partition. */
|
|
|
|
static void
|
|
assign_vars (var_map map)
|
|
{
|
|
int x, num;
|
|
tree var, root;
|
|
var_ann_t ann;
|
|
|
|
num = num_var_partitions (map);
|
|
for (x = 0; x < num; x++)
|
|
{
|
|
var = partition_to_var (map, x);
|
|
if (TREE_CODE (var) != SSA_NAME)
|
|
{
|
|
ann = var_ann (var);
|
|
/* It must already be coalesced. */
|
|
gcc_assert (ann->out_of_ssa_tag == 1);
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "partition %d already has variable ", x);
|
|
print_generic_expr (dump_file, var, TDF_SLIM);
|
|
fprintf (dump_file, " assigned to it.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
root = SSA_NAME_VAR (var);
|
|
ann = var_ann (root);
|
|
/* If ROOT is already associated, create a new one. */
|
|
if (ann->out_of_ssa_tag)
|
|
{
|
|
root = create_temp (root);
|
|
ann = var_ann (root);
|
|
}
|
|
/* ROOT has not been coalesced yet, so use it. */
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Partition %d is assigned to var ", x);
|
|
print_generic_stmt (dump_file, root, TDF_SLIM);
|
|
}
|
|
change_partition_var (map, root, x);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Replace use operand P with whatever variable it has been rewritten to based
|
|
on the partitions in MAP. EXPR is an optional expression vector over SSA
|
|
versions which is used to replace P with an expression instead of a variable.
|
|
If the stmt is changed, return true. */
|
|
|
|
static inline bool
|
|
replace_use_variable (var_map map, use_operand_p p, gimple *expr)
|
|
{
|
|
tree new_var;
|
|
tree var = USE_FROM_PTR (p);
|
|
|
|
/* Check if we are replacing this variable with an expression. */
|
|
if (expr)
|
|
{
|
|
int version = SSA_NAME_VERSION (var);
|
|
if (expr[version])
|
|
{
|
|
SET_USE (p, gimple_assign_rhs_to_tree (expr[version]));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
new_var = var_to_partition_to_var (map, var);
|
|
if (new_var)
|
|
{
|
|
SET_USE (p, new_var);
|
|
set_is_used (new_var);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Replace def operand DEF_P with whatever variable it has been rewritten to
|
|
based on the partitions in MAP. EXPR is an optional expression vector over
|
|
SSA versions which is used to replace DEF_P with an expression instead of a
|
|
variable. If the stmt is changed, return true. */
|
|
|
|
static inline bool
|
|
replace_def_variable (var_map map, def_operand_p def_p, tree *expr)
|
|
{
|
|
tree new_var;
|
|
tree var = DEF_FROM_PTR (def_p);
|
|
|
|
/* Do nothing if we are replacing this variable with an expression. */
|
|
if (expr && expr[SSA_NAME_VERSION (var)])
|
|
return true;
|
|
|
|
new_var = var_to_partition_to_var (map, var);
|
|
if (new_var)
|
|
{
|
|
SET_DEF (def_p, new_var);
|
|
set_is_used (new_var);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Remove each argument from PHI. If an arg was the last use of an SSA_NAME,
|
|
check to see if this allows another PHI node to be removed. */
|
|
|
|
static void
|
|
remove_gimple_phi_args (gimple phi)
|
|
{
|
|
use_operand_p arg_p;
|
|
ssa_op_iter iter;
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Removing Dead PHI definition: ");
|
|
print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
|
|
}
|
|
|
|
FOR_EACH_PHI_ARG (arg_p, phi, iter, SSA_OP_USE)
|
|
{
|
|
tree arg = USE_FROM_PTR (arg_p);
|
|
if (TREE_CODE (arg) == SSA_NAME)
|
|
{
|
|
/* Remove the reference to the existing argument. */
|
|
SET_USE (arg_p, NULL_TREE);
|
|
if (has_zero_uses (arg))
|
|
{
|
|
gimple stmt;
|
|
gimple_stmt_iterator gsi;
|
|
|
|
stmt = SSA_NAME_DEF_STMT (arg);
|
|
|
|
/* Also remove the def if it is a PHI node. */
|
|
if (gimple_code (stmt) == GIMPLE_PHI)
|
|
{
|
|
remove_gimple_phi_args (stmt);
|
|
gsi = gsi_for_stmt (stmt);
|
|
remove_phi_node (&gsi, true);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove any PHI node which is a virtual PHI, or a PHI with no uses. */
|
|
|
|
static void
|
|
eliminate_useless_phis (void)
|
|
{
|
|
basic_block bb;
|
|
gimple_stmt_iterator gsi;
|
|
tree result;
|
|
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); )
|
|
{
|
|
gimple phi = gsi_stmt (gsi);
|
|
result = gimple_phi_result (phi);
|
|
if (!is_gimple_reg (SSA_NAME_VAR (result)))
|
|
{
|
|
#ifdef ENABLE_CHECKING
|
|
size_t i;
|
|
/* There should be no arguments which are not virtual, or the
|
|
results will be incorrect. */
|
|
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
|
{
|
|
tree arg = PHI_ARG_DEF (phi, i);
|
|
if (TREE_CODE (arg) == SSA_NAME
|
|
&& is_gimple_reg (SSA_NAME_VAR (arg)))
|
|
{
|
|
fprintf (stderr, "Argument of PHI is not virtual (");
|
|
print_generic_expr (stderr, arg, TDF_SLIM);
|
|
fprintf (stderr, "), but the result is :");
|
|
print_gimple_stmt (stderr, phi, 0, TDF_SLIM);
|
|
internal_error ("SSA corruption");
|
|
}
|
|
}
|
|
#endif
|
|
remove_phi_node (&gsi, true);
|
|
}
|
|
else
|
|
{
|
|
/* Also remove real PHIs with no uses. */
|
|
if (has_zero_uses (result))
|
|
{
|
|
remove_gimple_phi_args (phi);
|
|
remove_phi_node (&gsi, true);
|
|
}
|
|
else
|
|
gsi_next (&gsi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* This function will rewrite the current program using the variable mapping
|
|
found in MAP. If the replacement vector VALUES is provided, any
|
|
occurrences of partitions with non-null entries in the vector will be
|
|
replaced with the expression in the vector instead of its mapped
|
|
variable. */
|
|
|
|
static void
|
|
rewrite_trees (var_map map, gimple *values)
|
|
{
|
|
elim_graph g;
|
|
basic_block bb;
|
|
gimple_stmt_iterator gsi;
|
|
edge e;
|
|
gimple_seq phi;
|
|
bool changed;
|
|
|
|
#ifdef ENABLE_CHECKING
|
|
/* Search for PHIs where the destination has no partition, but one
|
|
or more arguments has a partition. This should not happen and can
|
|
create incorrect code. */
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
|
{
|
|
gimple phi = gsi_stmt (gsi);
|
|
tree T0 = var_to_partition_to_var (map, gimple_phi_result (phi));
|
|
if (T0 == NULL_TREE)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
|
{
|
|
tree arg = PHI_ARG_DEF (phi, i);
|
|
|
|
if (TREE_CODE (arg) == SSA_NAME
|
|
&& var_to_partition (map, arg) != NO_PARTITION)
|
|
{
|
|
fprintf (stderr, "Argument of PHI is in a partition :(");
|
|
print_generic_expr (stderr, arg, TDF_SLIM);
|
|
fprintf (stderr, "), but the result is not :");
|
|
print_gimple_stmt (stderr, phi, 0, TDF_SLIM);
|
|
internal_error ("SSA corruption");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Replace PHI nodes with any required copies. */
|
|
g = new_elim_graph (map->num_partitions);
|
|
g->map = map;
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
|
|
{
|
|
gimple stmt = gsi_stmt (gsi);
|
|
use_operand_p use_p, copy_use_p;
|
|
def_operand_p def_p;
|
|
bool remove = false, is_copy = false;
|
|
int num_uses = 0;
|
|
ssa_op_iter iter;
|
|
|
|
changed = false;
|
|
|
|
if (gimple_assign_copy_p (stmt))
|
|
is_copy = true;
|
|
|
|
copy_use_p = NULL_USE_OPERAND_P;
|
|
FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
|
|
{
|
|
if (replace_use_variable (map, use_p, values))
|
|
changed = true;
|
|
copy_use_p = use_p;
|
|
num_uses++;
|
|
}
|
|
|
|
if (num_uses != 1)
|
|
is_copy = false;
|
|
|
|
def_p = SINGLE_SSA_DEF_OPERAND (stmt, SSA_OP_DEF);
|
|
|
|
if (def_p != NULL)
|
|
{
|
|
/* Mark this stmt for removal if it is the list of replaceable
|
|
expressions. */
|
|
if (values && values[SSA_NAME_VERSION (DEF_FROM_PTR (def_p))])
|
|
remove = true;
|
|
else
|
|
{
|
|
if (replace_def_variable (map, def_p, NULL))
|
|
changed = true;
|
|
/* If both SSA_NAMEs coalesce to the same variable,
|
|
mark the now redundant copy for removal. */
|
|
if (is_copy)
|
|
{
|
|
gcc_assert (copy_use_p != NULL_USE_OPERAND_P);
|
|
if (DEF_FROM_PTR (def_p) == USE_FROM_PTR (copy_use_p))
|
|
remove = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_DEF)
|
|
if (replace_def_variable (map, def_p, NULL))
|
|
changed = true;
|
|
|
|
/* Remove any stmts marked for removal. */
|
|
if (remove)
|
|
gsi_remove (&gsi, true);
|
|
else
|
|
{
|
|
if (changed)
|
|
if (maybe_clean_or_replace_eh_stmt (stmt, stmt))
|
|
gimple_purge_dead_eh_edges (bb);
|
|
gsi_next (&gsi);
|
|
}
|
|
}
|
|
|
|
phi = phi_nodes (bb);
|
|
if (phi)
|
|
{
|
|
edge_iterator ei;
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
eliminate_phi (e, g);
|
|
}
|
|
}
|
|
|
|
delete_elim_graph (g);
|
|
}
|
|
|
|
/* These are the local work structures used to determine the best place to
|
|
insert the copies that were placed on edges by the SSA->normal pass.. */
|
|
static VEC(edge,heap) *edge_leader;
|
|
static VEC(gimple_seq,heap) *stmt_list;
|
|
static bitmap leader_has_match = NULL;
|
|
static edge leader_match = NULL;
|
|
|
|
|
|
/* Pass this function to make_forwarder_block so that all the edges with
|
|
matching PENDING_STMT lists to 'curr_stmt_list' get redirected. E is the
|
|
edge to test for a match. */
|
|
|
|
static inline bool
|
|
same_stmt_list_p (edge e)
|
|
{
|
|
return (e->aux == (PTR) leader_match) ? true : false;
|
|
}
|
|
|
|
|
|
/* Return TRUE if S1 and S2 are equivalent copies. */
|
|
|
|
static inline bool
|
|
identical_copies_p (const_gimple s1, const_gimple s2)
|
|
{
|
|
#ifdef ENABLE_CHECKING
|
|
gcc_assert (is_gimple_assign (s1));
|
|
gcc_assert (is_gimple_assign (s2));
|
|
gcc_assert (DECL_P (gimple_assign_lhs (s1)));
|
|
gcc_assert (DECL_P (gimple_assign_lhs (s2)));
|
|
#endif
|
|
|
|
if (gimple_assign_lhs (s1) != gimple_assign_lhs (s2))
|
|
return false;
|
|
|
|
if (gimple_assign_rhs1 (s1) != gimple_assign_rhs1 (s2))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Compare the PENDING_STMT list for edges E1 and E2. Return true if the lists
|
|
contain the same sequence of copies. */
|
|
|
|
static inline bool
|
|
identical_stmt_lists_p (const_edge e1, const_edge e2)
|
|
{
|
|
gimple_seq t1 = PENDING_STMT (e1);
|
|
gimple_seq t2 = PENDING_STMT (e2);
|
|
gimple_stmt_iterator gsi1, gsi2;
|
|
|
|
for (gsi1 = gsi_start (t1), gsi2 = gsi_start (t2);
|
|
!gsi_end_p (gsi1) && !gsi_end_p (gsi2);
|
|
gsi_next (&gsi1), gsi_next (&gsi2))
|
|
{
|
|
if (!identical_copies_p (gsi_stmt (gsi1), gsi_stmt (gsi2)))
|
|
break;
|
|
}
|
|
|
|
if (!gsi_end_p (gsi1) || !gsi_end_p (gsi2))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Allocate data structures used in analyze_edges_for_bb. */
|
|
|
|
static void
|
|
init_analyze_edges_for_bb (void)
|
|
{
|
|
edge_leader = VEC_alloc (edge, heap, 25);
|
|
stmt_list = VEC_alloc (gimple_seq, heap, 25);
|
|
leader_has_match = BITMAP_ALLOC (NULL);
|
|
}
|
|
|
|
|
|
/* Free data structures used in analyze_edges_for_bb. */
|
|
|
|
static void
|
|
fini_analyze_edges_for_bb (void)
|
|
{
|
|
VEC_free (edge, heap, edge_leader);
|
|
VEC_free (gimple_seq, heap, stmt_list);
|
|
BITMAP_FREE (leader_has_match);
|
|
}
|
|
|
|
/* A helper function to be called via walk_tree. Return DATA if it is
|
|
contained in subtree TP. */
|
|
|
|
static tree
|
|
contains_tree_r (tree * tp, int *walk_subtrees, void *data)
|
|
{
|
|
if (*tp == data)
|
|
{
|
|
*walk_subtrees = 0;
|
|
return (tree) data;
|
|
}
|
|
else
|
|
return NULL_TREE;
|
|
}
|
|
|
|
/* A threshold for the number of insns contained in the latch block.
|
|
It is used to prevent blowing the loop with too many copies from
|
|
the latch. */
|
|
#define MAX_STMTS_IN_LATCH 2
|
|
|
|
/* Return TRUE if the stmts on SINGLE-EDGE can be moved to the
|
|
body of the loop. This should be permitted only if SINGLE-EDGE is a
|
|
single-basic-block latch edge and thus cleaning the latch will help
|
|
to create a single-basic-block loop. Otherwise return FALSE. */
|
|
|
|
static bool
|
|
process_single_block_loop_latch (edge single_edge)
|
|
{
|
|
gimple_seq stmts;
|
|
basic_block b_exit, b_pheader, b_loop = single_edge->src;
|
|
edge_iterator ei;
|
|
edge e;
|
|
gimple_stmt_iterator gsi, gsi_exit;
|
|
gimple_stmt_iterator tsi;
|
|
tree expr;
|
|
gimple stmt;
|
|
unsigned int count = 0;
|
|
|
|
if (single_edge == NULL || (single_edge->dest != single_edge->src)
|
|
|| (EDGE_COUNT (b_loop->succs) != 2)
|
|
|| (EDGE_COUNT (b_loop->preds) != 2))
|
|
return false;
|
|
|
|
/* Get the stmts on the latch edge. */
|
|
stmts = PENDING_STMT (single_edge);
|
|
|
|
/* Find the successor edge which is not the latch edge. */
|
|
FOR_EACH_EDGE (e, ei, b_loop->succs)
|
|
if (e->dest != b_loop)
|
|
break;
|
|
|
|
b_exit = e->dest;
|
|
|
|
/* Check that the exit block has only the loop as a predecessor,
|
|
and that there are no pending stmts on that edge as well. */
|
|
if (EDGE_COUNT (b_exit->preds) != 1 || PENDING_STMT (e))
|
|
return false;
|
|
|
|
/* Find the predecessor edge which is not the latch edge. */
|
|
FOR_EACH_EDGE (e, ei, b_loop->preds)
|
|
if (e->src != b_loop)
|
|
break;
|
|
|
|
b_pheader = e->src;
|
|
|
|
if (b_exit == b_pheader || b_exit == b_loop || b_pheader == b_loop)
|
|
return false;
|
|
|
|
gsi_exit = gsi_after_labels (b_exit);
|
|
|
|
/* Get the last stmt in the loop body. */
|
|
gsi = gsi_last_bb (single_edge->src);
|
|
stmt = gsi_stmt (gsi);
|
|
|
|
if (gimple_code (stmt) != GIMPLE_COND)
|
|
return false;
|
|
|
|
|
|
expr = build2 (gimple_cond_code (stmt), boolean_type_node,
|
|
gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
|
|
/* Iterate over the insns on the latch and count them. */
|
|
for (tsi = gsi_start (stmts); !gsi_end_p (tsi); gsi_next (&tsi))
|
|
{
|
|
gimple stmt1 = gsi_stmt (tsi);
|
|
tree var;
|
|
|
|
count++;
|
|
/* Check that the condition does not contain any new definition
|
|
created in the latch as the stmts from the latch intended
|
|
to precede it. */
|
|
if (gimple_code (stmt1) != GIMPLE_ASSIGN)
|
|
return false;
|
|
var = gimple_assign_lhs (stmt1);
|
|
if (TREE_THIS_VOLATILE (var)
|
|
|| TYPE_VOLATILE (TREE_TYPE (var))
|
|
|| walk_tree (&expr, contains_tree_r, var, NULL))
|
|
return false;
|
|
}
|
|
/* Check that the latch does not contain more than MAX_STMTS_IN_LATCH
|
|
insns. The purpose of this restriction is to prevent blowing the
|
|
loop with too many copies from the latch. */
|
|
if (count > MAX_STMTS_IN_LATCH)
|
|
return false;
|
|
|
|
/* Apply the transformation - clean up the latch block:
|
|
|
|
var = something;
|
|
L1:
|
|
x1 = expr;
|
|
if (cond) goto L2 else goto L3;
|
|
L2:
|
|
var = x1;
|
|
goto L1
|
|
L3:
|
|
...
|
|
|
|
==>
|
|
|
|
var = something;
|
|
L1:
|
|
x1 = expr;
|
|
tmp_var = var;
|
|
var = x1;
|
|
if (cond) goto L1 else goto L2;
|
|
L2:
|
|
var = tmp_var;
|
|
...
|
|
*/
|
|
for (tsi = gsi_start (stmts); !gsi_end_p (tsi); gsi_next (&tsi))
|
|
{
|
|
gimple stmt1 = gsi_stmt (tsi);
|
|
tree var, tmp_var;
|
|
gimple copy;
|
|
|
|
/* Create a new variable to load back the value of var in case
|
|
we exit the loop. */
|
|
var = gimple_assign_lhs (stmt1);
|
|
tmp_var = create_temp (var);
|
|
copy = gimple_build_assign (tmp_var, var);
|
|
set_is_used (tmp_var);
|
|
gsi_insert_before (&gsi, copy, GSI_SAME_STMT);
|
|
copy = gimple_build_assign (var, tmp_var);
|
|
gsi_insert_before (&gsi_exit, copy, GSI_SAME_STMT);
|
|
}
|
|
|
|
PENDING_STMT (single_edge) = 0;
|
|
/* Insert the new stmts to the loop body. */
|
|
gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
|
|
|
|
if (dump_file)
|
|
fprintf (dump_file,
|
|
"\nCleaned-up latch block of loop with single BB: %d\n\n",
|
|
single_edge->dest->index);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Look at all the incoming edges to block BB, and decide where the best place
|
|
to insert the stmts on each edge are, and perform those insertions. */
|
|
|
|
static void
|
|
analyze_edges_for_bb (basic_block bb)
|
|
{
|
|
edge e;
|
|
edge_iterator ei;
|
|
int count;
|
|
unsigned int x;
|
|
bool have_opportunity;
|
|
gimple_stmt_iterator gsi;
|
|
gimple stmt;
|
|
edge single_edge = NULL;
|
|
bool is_label;
|
|
edge leader;
|
|
|
|
count = 0;
|
|
|
|
/* Blocks which contain at least one abnormal edge cannot use
|
|
make_forwarder_block. Look for these blocks, and commit any PENDING_STMTs
|
|
found on edges in these block. */
|
|
have_opportunity = true;
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
if (e->flags & EDGE_ABNORMAL)
|
|
{
|
|
have_opportunity = false;
|
|
break;
|
|
}
|
|
|
|
if (!have_opportunity)
|
|
{
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
if (PENDING_STMT (e))
|
|
gsi_commit_one_edge_insert (e, NULL);
|
|
return;
|
|
}
|
|
|
|
/* Find out how many edges there are with interesting pending stmts on them.
|
|
Commit the stmts on edges we are not interested in. */
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
{
|
|
gcc_assert (!(e->flags & EDGE_ABNORMAL));
|
|
if (e->flags & EDGE_FALLTHRU)
|
|
{
|
|
gsi = gsi_start_bb (e->src);
|
|
if (!gsi_end_p (gsi))
|
|
{
|
|
stmt = gsi_stmt (gsi);
|
|
gsi_next (&gsi);
|
|
gcc_assert (stmt != NULL);
|
|
is_label = (gimple_code (stmt) == GIMPLE_LABEL);
|
|
/* Punt if it has non-label stmts, or isn't local. */
|
|
if (!is_label
|
|
|| DECL_NONLOCAL (gimple_label_label (stmt))
|
|
|| !gsi_end_p (gsi))
|
|
{
|
|
gsi_commit_one_edge_insert (e, NULL);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
single_edge = e;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/* If there aren't at least 2 edges, no sharing will happen. */
|
|
if (count < 2)
|
|
{
|
|
if (single_edge)
|
|
{
|
|
/* Add stmts to the edge unless processed specially as a
|
|
single-block loop latch edge. */
|
|
if (!process_single_block_loop_latch (single_edge))
|
|
gsi_commit_one_edge_insert (single_edge, NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Ensure that we have empty worklists. */
|
|
#ifdef ENABLE_CHECKING
|
|
gcc_assert (VEC_length (edge, edge_leader) == 0);
|
|
gcc_assert (VEC_length (gimple_seq, stmt_list) == 0);
|
|
gcc_assert (bitmap_empty_p (leader_has_match));
|
|
#endif
|
|
|
|
/* Find the "leader" block for each set of unique stmt lists. Preference is
|
|
given to FALLTHRU blocks since they would need a GOTO to arrive at another
|
|
block. The leader edge destination is the block which all the other edges
|
|
with the same stmt list will be redirected to. */
|
|
have_opportunity = false;
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
{
|
|
bool found = false;
|
|
|
|
/* Look for the same stmt list in edge leaders list. */
|
|
for (x = 0; VEC_iterate (edge, edge_leader, x, leader); x++)
|
|
{
|
|
if (identical_stmt_lists_p (leader, e))
|
|
{
|
|
/* Give this edge the same stmt list pointer. */
|
|
PENDING_STMT (e) = NULL;
|
|
e->aux = leader;
|
|
bitmap_set_bit (leader_has_match, x);
|
|
have_opportunity = found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If no similar stmt list, add this edge to the leader list. */
|
|
if (!found)
|
|
{
|
|
VEC_safe_push (edge, heap, edge_leader, e);
|
|
VEC_safe_push (gimple_seq, heap, stmt_list, PENDING_STMT (e));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If there are no similar lists, just issue the stmts. */
|
|
if (!have_opportunity)
|
|
{
|
|
for (x = 0; VEC_iterate (edge, edge_leader, x, leader); x++)
|
|
gsi_commit_one_edge_insert (leader, NULL);
|
|
VEC_truncate (edge, edge_leader, 0);
|
|
VEC_truncate (gimple_seq, stmt_list, 0);
|
|
bitmap_clear (leader_has_match);
|
|
return;
|
|
}
|
|
|
|
if (dump_file)
|
|
fprintf (dump_file, "\nOpportunities in BB %d for stmt/block reduction:\n",
|
|
bb->index);
|
|
|
|
/* For each common list, create a forwarding block and issue the stmt's
|
|
in that block. */
|
|
for (x = 0; VEC_iterate (edge, edge_leader, x, leader); x++)
|
|
if (bitmap_bit_p (leader_has_match, x))
|
|
{
|
|
edge new_edge;
|
|
gimple_stmt_iterator gsi;
|
|
gimple_seq curr_stmt_list;
|
|
|
|
leader_match = leader;
|
|
|
|
/* The tree_* cfg manipulation routines use the PENDING_EDGE field
|
|
for various PHI manipulations, so it gets cleared when calls are
|
|
made to make_forwarder_block(). So make sure the edge is clear,
|
|
and use the saved stmt list. */
|
|
PENDING_STMT (leader) = NULL;
|
|
leader->aux = leader;
|
|
curr_stmt_list = VEC_index (gimple_seq, stmt_list, x);
|
|
|
|
new_edge = make_forwarder_block (leader->dest, same_stmt_list_p,
|
|
NULL);
|
|
bb = new_edge->dest;
|
|
if (dump_file)
|
|
{
|
|
fprintf (dump_file, "Splitting BB %d for Common stmt list. ",
|
|
leader->dest->index);
|
|
fprintf (dump_file, "Original block is now BB%d.\n", bb->index);
|
|
print_gimple_seq (dump_file, curr_stmt_list, 0, TDF_VOPS);
|
|
}
|
|
|
|
FOR_EACH_EDGE (e, ei, new_edge->src->preds)
|
|
{
|
|
e->aux = NULL;
|
|
if (dump_file)
|
|
fprintf (dump_file, " Edge (%d->%d) lands here.\n",
|
|
e->src->index, e->dest->index);
|
|
}
|
|
|
|
gsi = gsi_last_bb (leader->dest);
|
|
gsi_insert_seq_after (&gsi, curr_stmt_list, GSI_NEW_STMT);
|
|
|
|
leader_match = NULL;
|
|
/* We should never get a new block now. */
|
|
}
|
|
else
|
|
{
|
|
PENDING_STMT (leader) = VEC_index (gimple_seq, stmt_list, x);
|
|
gsi_commit_one_edge_insert (leader, NULL);
|
|
}
|
|
|
|
|
|
/* Clear the working data structures. */
|
|
VEC_truncate (edge, edge_leader, 0);
|
|
VEC_truncate (gimple_seq, stmt_list, 0);
|
|
bitmap_clear (leader_has_match);
|
|
}
|
|
|
|
|
|
/* This function will analyze the insertions which were performed on edges,
|
|
and decide whether they should be left on that edge, or whether it is more
|
|
efficient to emit some subset of them in a single block. All stmts are
|
|
inserted somewhere. */
|
|
|
|
static void
|
|
perform_edge_inserts (void)
|
|
{
|
|
basic_block bb;
|
|
|
|
if (dump_file)
|
|
fprintf(dump_file, "Analyzing Edge Insertions.\n");
|
|
|
|
/* analyze_edges_for_bb calls make_forwarder_block, which tries to
|
|
incrementally update the dominator information. Since we don't
|
|
need dominator information after this pass, go ahead and free the
|
|
dominator information. */
|
|
free_dominance_info (CDI_DOMINATORS);
|
|
free_dominance_info (CDI_POST_DOMINATORS);
|
|
|
|
/* Allocate data structures used in analyze_edges_for_bb. */
|
|
init_analyze_edges_for_bb ();
|
|
|
|
FOR_EACH_BB (bb)
|
|
analyze_edges_for_bb (bb);
|
|
|
|
analyze_edges_for_bb (EXIT_BLOCK_PTR);
|
|
|
|
/* Free data structures used in analyze_edges_for_bb. */
|
|
fini_analyze_edges_for_bb ();
|
|
|
|
#ifdef ENABLE_CHECKING
|
|
{
|
|
edge_iterator ei;
|
|
edge e;
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
error (" Pending stmts not issued on PRED edge (%d, %d)\n",
|
|
e->src->index, e->dest->index);
|
|
}
|
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
error (" Pending stmts not issued on SUCC edge (%d, %d)\n",
|
|
e->src->index, e->dest->index);
|
|
}
|
|
}
|
|
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
error (" Pending stmts not issued on ENTRY edge (%d, %d)\n",
|
|
e->src->index, e->dest->index);
|
|
}
|
|
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
|
|
{
|
|
if (PENDING_STMT (e))
|
|
error (" Pending stmts not issued on EXIT edge (%d, %d)\n",
|
|
e->src->index, e->dest->index);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Remove the ssa-names in the current function and translate them into normal
|
|
compiler variables. PERFORM_TER is true if Temporary Expression Replacement
|
|
should also be used. */
|
|
|
|
static void
|
|
remove_ssa_form (bool perform_ter)
|
|
{
|
|
basic_block bb;
|
|
gimple *values = NULL;
|
|
var_map map;
|
|
gimple_stmt_iterator gsi;
|
|
|
|
map = coalesce_ssa_name ();
|
|
|
|
/* Return to viewing the variable list as just all reference variables after
|
|
coalescing has been performed. */
|
|
partition_view_normal (map, false);
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "After Coalescing:\n");
|
|
dump_var_map (dump_file, map);
|
|
}
|
|
|
|
if (perform_ter)
|
|
{
|
|
values = find_replaceable_exprs (map);
|
|
if (values && dump_file && (dump_flags & TDF_DETAILS))
|
|
dump_replaceable_exprs (dump_file, values);
|
|
}
|
|
|
|
/* Assign real variables to the partitions now. */
|
|
assign_vars (map);
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "After Base variable replacement:\n");
|
|
dump_var_map (dump_file, map);
|
|
}
|
|
|
|
rewrite_trees (map, values);
|
|
|
|
if (values)
|
|
free (values);
|
|
|
|
/* Remove PHI nodes which have been translated back to real variables. */
|
|
FOR_EACH_BB (bb)
|
|
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);)
|
|
remove_phi_node (&gsi, true);
|
|
|
|
/* If any copies were inserted on edges, analyze and insert them now. */
|
|
perform_edge_inserts ();
|
|
|
|
delete_var_map (map);
|
|
}
|
|
|
|
|
|
/* Search every PHI node for arguments associated with backedges which
|
|
we can trivially determine will need a copy (the argument is either
|
|
not an SSA_NAME or the argument has a different underlying variable
|
|
than the PHI result).
|
|
|
|
Insert a copy from the PHI argument to a new destination at the
|
|
end of the block with the backedge to the top of the loop. Update
|
|
the PHI argument to reference this new destination. */
|
|
|
|
static void
|
|
insert_backedge_copies (void)
|
|
{
|
|
basic_block bb;
|
|
gimple_stmt_iterator gsi;
|
|
|
|
FOR_EACH_BB (bb)
|
|
{
|
|
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
|
{
|
|
gimple phi = gsi_stmt (gsi);
|
|
tree result = gimple_phi_result (phi);
|
|
tree result_var;
|
|
size_t i;
|
|
|
|
if (!is_gimple_reg (result))
|
|
continue;
|
|
|
|
result_var = SSA_NAME_VAR (result);
|
|
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
|
{
|
|
tree arg = gimple_phi_arg_def (phi, i);
|
|
edge e = gimple_phi_arg_edge (phi, i);
|
|
|
|
/* If the argument is not an SSA_NAME, then we will need a
|
|
constant initialization. If the argument is an SSA_NAME with
|
|
a different underlying variable then a copy statement will be
|
|
needed. */
|
|
if ((e->flags & EDGE_DFS_BACK)
|
|
&& (TREE_CODE (arg) != SSA_NAME
|
|
|| SSA_NAME_VAR (arg) != result_var))
|
|
{
|
|
tree name;
|
|
gimple stmt, last = NULL;
|
|
gimple_stmt_iterator gsi2;
|
|
|
|
gsi2 = gsi_last_bb (gimple_phi_arg_edge (phi, i)->src);
|
|
if (!gsi_end_p (gsi2))
|
|
last = gsi_stmt (gsi2);
|
|
|
|
/* In theory the only way we ought to get back to the
|
|
start of a loop should be with a COND_EXPR or GOTO_EXPR.
|
|
However, better safe than sorry.
|
|
If the block ends with a control statement or
|
|
something that might throw, then we have to
|
|
insert this assignment before the last
|
|
statement. Else insert it after the last statement. */
|
|
if (last && stmt_ends_bb_p (last))
|
|
{
|
|
/* If the last statement in the block is the definition
|
|
site of the PHI argument, then we can't insert
|
|
anything after it. */
|
|
if (TREE_CODE (arg) == SSA_NAME
|
|
&& SSA_NAME_DEF_STMT (arg) == last)
|
|
continue;
|
|
}
|
|
|
|
/* Create a new instance of the underlying variable of the
|
|
PHI result. */
|
|
stmt = gimple_build_assign (result_var,
|
|
gimple_phi_arg_def (phi, i));
|
|
name = make_ssa_name (result_var, stmt);
|
|
gimple_assign_set_lhs (stmt, name);
|
|
|
|
/* Insert the new statement into the block and update
|
|
the PHI node. */
|
|
if (last && stmt_ends_bb_p (last))
|
|
gsi_insert_before (&gsi2, stmt, GSI_NEW_STMT);
|
|
else
|
|
gsi_insert_after (&gsi2, stmt, GSI_NEW_STMT);
|
|
SET_PHI_ARG_DEF (phi, i, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Take the current function out of SSA form, translating PHIs as described in
|
|
R. Morgan, ``Building an Optimizing Compiler'',
|
|
Butterworth-Heinemann, Boston, MA, 1998. pp 176-186. */
|
|
|
|
static unsigned int
|
|
rewrite_out_of_ssa (void)
|
|
{
|
|
/* If elimination of a PHI requires inserting a copy on a backedge,
|
|
then we will have to split the backedge which has numerous
|
|
undesirable performance effects.
|
|
|
|
A significant number of such cases can be handled here by inserting
|
|
copies into the loop itself. */
|
|
insert_backedge_copies ();
|
|
|
|
|
|
/* Eliminate PHIs which are of no use, such as virtual or dead phis. */
|
|
eliminate_useless_phis ();
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
gimple_dump_cfg (dump_file, dump_flags & ~TDF_DETAILS);
|
|
|
|
remove_ssa_form (flag_tree_ter && !flag_mudflap);
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
gimple_dump_cfg (dump_file, dump_flags & ~TDF_DETAILS);
|
|
|
|
cfun->gimple_df->in_ssa_p = false;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Define the parameters of the out of SSA pass. */
|
|
|
|
struct gimple_opt_pass pass_del_ssa =
|
|
{
|
|
{
|
|
GIMPLE_PASS,
|
|
"optimized", /* name */
|
|
NULL, /* gate */
|
|
rewrite_out_of_ssa, /* execute */
|
|
NULL, /* sub */
|
|
NULL, /* next */
|
|
0, /* static_pass_number */
|
|
TV_TREE_SSA_TO_NORMAL, /* tv_id */
|
|
PROP_cfg | PROP_ssa, /* properties_required */
|
|
0, /* properties_provided */
|
|
/* ??? If TER is enabled, we also kill gimple. */
|
|
PROP_ssa, /* properties_destroyed */
|
|
TODO_verify_ssa | TODO_verify_flow
|
|
| TODO_verify_stmts, /* todo_flags_start */
|
|
TODO_dump_func
|
|
| TODO_ggc_collect
|
|
| TODO_remove_unused_locals /* todo_flags_finish */
|
|
}
|
|
};
|