mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-20 08:10:28 +08:00
re PR tree-optimization/71437 (Performance regression after r235817)
PR tree-optimization/71437 * tree-ssa-dom.c (struct cond_equivalence): Moved from here into tree-ssa-scopedtables. (lookup_avail_expr, build_and_record_new_cond): Likewise. (record_conditions, record_cond, vuse_eq): Likewise. (record_edge_info): Adjust to API tweak of record_conditions. (simplify_stmt_for_jump_threading): Similarly for lookup_avail_expr. (record_temporary_equivalences, optimize_stmt): Likewise. (eliminate_redundant_computations): Likewise. (record_equivalences_from_stmt): Likewise. * tree-ssa-scopedtables.c: Include options.h and params.h. (vuse_eq): New function, moved from tree-ssa-dom.c (build_and_record_new_cond): Likewise. (record_conditions): Likewise. Accept vector of conditions rather than edge_equivalence structure for first argument. for the first argument. (avail_exprs_stack::lookup_avail_expr): New member function, moved from tree-ssa-dom.c. (avail_exprs_stack::record_cond): Likewise. * tree-ssa-scopedtables.h (struct cond_equivalence): Moved here from tree-ssa-dom.c. (avail_exprs_stack): Add new member functions lookup_avail_expr and record_cond. (record_conditions): Declare. From-SVN: r246186
This commit is contained in:
parent
47ee2e0741
commit
a3d514f231
@ -1,3 +1,30 @@
|
||||
2017-03-15 Jeff Law <law@redhat.com>
|
||||
|
||||
PR tree-optimization/71437
|
||||
* tree-ssa-dom.c (struct cond_equivalence): Moved from here into
|
||||
tree-ssa-scopedtables.
|
||||
(lookup_avail_expr, build_and_record_new_cond): Likewise.
|
||||
(record_conditions, record_cond, vuse_eq): Likewise.
|
||||
(record_edge_info): Adjust to API tweak of record_conditions.
|
||||
(simplify_stmt_for_jump_threading): Similarly for lookup_avail_expr.
|
||||
(record_temporary_equivalences, optimize_stmt): Likewise.
|
||||
(eliminate_redundant_computations): Likewise.
|
||||
(record_equivalences_from_stmt): Likewise.
|
||||
* tree-ssa-scopedtables.c: Include options.h and params.h.
|
||||
(vuse_eq): New function, moved from tree-ssa-dom.c
|
||||
(build_and_record_new_cond): Likewise.
|
||||
(record_conditions): Likewise. Accept vector of conditions rather
|
||||
than edge_equivalence structure for first argument.
|
||||
for the first argument.
|
||||
(avail_exprs_stack::lookup_avail_expr): New member function, moved
|
||||
from tree-ssa-dom.c.
|
||||
(avail_exprs_stack::record_cond): Likewise.
|
||||
* tree-ssa-scopedtables.h (struct cond_equivalence): Moved here
|
||||
from tree-ssa-dom.c.
|
||||
(avail_exprs_stack): Add new member functions lookup_avail_expr
|
||||
and record_cond.
|
||||
(record_conditions): Declare.
|
||||
|
||||
2017-03-15 Vladimir Makarov <vmakarov@redhat.com>
|
||||
|
||||
PR target/80017
|
||||
|
@ -48,15 +48,6 @@ along with GCC; see the file COPYING3. If not see
|
||||
|
||||
/* This file implements optimizations on the dominator tree. */
|
||||
|
||||
/* Structure for recording known values of a conditional expression
|
||||
at the exits from its block. */
|
||||
|
||||
struct cond_equivalence
|
||||
{
|
||||
struct hashable_expr cond;
|
||||
tree value;
|
||||
};
|
||||
|
||||
/* Structure for recording edge equivalences.
|
||||
|
||||
Computing and storing the edge equivalences instead of creating
|
||||
@ -103,9 +94,6 @@ static struct opt_stats_d opt_stats;
|
||||
static edge optimize_stmt (basic_block, gimple_stmt_iterator,
|
||||
class const_and_copies *,
|
||||
class avail_exprs_stack *);
|
||||
static tree lookup_avail_expr (gimple *, bool, class avail_exprs_stack *,
|
||||
bool = true);
|
||||
static void record_cond (cond_equivalence *, class avail_exprs_stack *);
|
||||
static void record_equality (tree, tree, class const_and_copies *);
|
||||
static void record_equivalences_from_phis (basic_block);
|
||||
static void record_equivalences_from_incoming_edge (basic_block,
|
||||
@ -175,148 +163,6 @@ free_all_edge_infos (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a cond_equivalence record indicating that the comparison
|
||||
CODE holds between operands OP0 and OP1 and push it to **P. */
|
||||
|
||||
static void
|
||||
build_and_record_new_cond (enum tree_code code,
|
||||
tree op0, tree op1,
|
||||
vec<cond_equivalence> *p,
|
||||
bool val = true)
|
||||
{
|
||||
cond_equivalence c;
|
||||
struct hashable_expr *cond = &c.cond;
|
||||
|
||||
gcc_assert (TREE_CODE_CLASS (code) == tcc_comparison);
|
||||
|
||||
cond->type = boolean_type_node;
|
||||
cond->kind = EXPR_BINARY;
|
||||
cond->ops.binary.op = code;
|
||||
cond->ops.binary.opnd0 = op0;
|
||||
cond->ops.binary.opnd1 = op1;
|
||||
|
||||
c.value = val ? boolean_true_node : boolean_false_node;
|
||||
p->safe_push (c);
|
||||
}
|
||||
|
||||
/* Record that COND is true and INVERTED is false into the edge information
|
||||
structure. Also record that any conditions dominated by COND are true
|
||||
as well.
|
||||
|
||||
For example, if a < b is true, then a <= b must also be true. */
|
||||
|
||||
static void
|
||||
record_conditions (struct edge_info *edge_info, tree cond, tree inverted)
|
||||
{
|
||||
tree op0, op1;
|
||||
cond_equivalence c;
|
||||
|
||||
if (!COMPARISON_CLASS_P (cond))
|
||||
return;
|
||||
|
||||
op0 = TREE_OPERAND (cond, 0);
|
||||
op1 = TREE_OPERAND (cond, 1);
|
||||
|
||||
switch (TREE_CODE (cond))
|
||||
{
|
||||
case LT_EXPR:
|
||||
case GT_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (LTGT_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
}
|
||||
|
||||
build_and_record_new_cond ((TREE_CODE (cond) == LT_EXPR
|
||||
? LE_EXPR : GE_EXPR),
|
||||
op0, op1, &edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (EQ_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences, false);
|
||||
break;
|
||||
|
||||
case GE_EXPR:
|
||||
case LE_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
}
|
||||
break;
|
||||
|
||||
case EQ_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
}
|
||||
build_and_record_new_cond (LE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (GE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
break;
|
||||
|
||||
case UNORDERED_EXPR:
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNLE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNGE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNEQ_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNLT_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNGT_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
break;
|
||||
|
||||
case UNLT_EXPR:
|
||||
case UNGT_EXPR:
|
||||
build_and_record_new_cond ((TREE_CODE (cond) == UNLT_EXPR
|
||||
? UNLE_EXPR : UNGE_EXPR),
|
||||
op0, op1, &edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
break;
|
||||
|
||||
case UNEQ_EXPR:
|
||||
build_and_record_new_cond (UNLE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (UNGE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
break;
|
||||
|
||||
case LTGT_EXPR:
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1,
|
||||
&edge_info->cond_equivalences);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now store the original true and false conditions into the first
|
||||
two slots. */
|
||||
initialize_expr_from_cond (cond, &c.cond);
|
||||
c.value = boolean_true_node;
|
||||
edge_info->cond_equivalences.safe_push (c);
|
||||
|
||||
/* It is possible for INVERTED to be the negation of a comparison,
|
||||
and not a valid RHS or GIMPLE_COND condition. This happens because
|
||||
invert_truthvalue may return such an expression when asked to invert
|
||||
a floating-point comparison. These comparisons are not assumed to
|
||||
obey the trichotomy law. */
|
||||
initialize_expr_from_cond (inverted, &c.cond);
|
||||
c.value = boolean_false_node;
|
||||
edge_info->cond_equivalences.safe_push (c);
|
||||
}
|
||||
|
||||
/* We have finished optimizing BB, record any information implied by
|
||||
taking a specific outgoing edge from BB. */
|
||||
|
||||
@ -435,7 +281,7 @@ record_edge_info (basic_block bb)
|
||||
struct edge_info *edge_info;
|
||||
|
||||
edge_info = allocate_edge_info (true_edge);
|
||||
record_conditions (edge_info, cond, inverted);
|
||||
record_conditions (&edge_info->cond_equivalences, cond, inverted);
|
||||
|
||||
if (can_infer_simple_equiv && code == EQ_EXPR)
|
||||
{
|
||||
@ -444,7 +290,7 @@ record_edge_info (basic_block bb)
|
||||
}
|
||||
|
||||
edge_info = allocate_edge_info (false_edge);
|
||||
record_conditions (edge_info, inverted, cond);
|
||||
record_conditions (&edge_info->cond_equivalences, inverted, cond);
|
||||
|
||||
if (can_infer_simple_equiv && TREE_CODE (inverted) == EQ_EXPR)
|
||||
{
|
||||
@ -465,7 +311,7 @@ record_edge_info (basic_block bb)
|
||||
struct edge_info *edge_info;
|
||||
|
||||
edge_info = allocate_edge_info (true_edge);
|
||||
record_conditions (edge_info, cond, inverted);
|
||||
record_conditions (&edge_info->cond_equivalences, cond, inverted);
|
||||
|
||||
if (can_infer_simple_equiv && code == EQ_EXPR)
|
||||
{
|
||||
@ -474,7 +320,7 @@ record_edge_info (basic_block bb)
|
||||
}
|
||||
|
||||
edge_info = allocate_edge_info (false_edge);
|
||||
record_conditions (edge_info, inverted, cond);
|
||||
record_conditions (&edge_info->cond_equivalences, inverted, cond);
|
||||
|
||||
if (can_infer_simple_equiv && TREE_CODE (inverted) == EQ_EXPR)
|
||||
{
|
||||
@ -760,7 +606,7 @@ simplify_stmt_for_jump_threading (gimple *stmt,
|
||||
gimple *within_stmt ATTRIBUTE_UNUSED,
|
||||
class avail_exprs_stack *avail_exprs_stack)
|
||||
{
|
||||
return lookup_avail_expr (stmt, false, avail_exprs_stack);
|
||||
return avail_exprs_stack->lookup_avail_expr (stmt, false, true);
|
||||
}
|
||||
|
||||
/* Valueize hook for gimple_fold_stmt_to_constant_1. */
|
||||
@ -865,7 +711,7 @@ record_temporary_equivalences (edge e,
|
||||
/* If we have 0 = COND or 1 = COND equivalences, record them
|
||||
into our expression hash tables. */
|
||||
for (i = 0; edge_info->cond_equivalences.iterate (i, &eq); ++i)
|
||||
record_cond (eq, avail_exprs_stack);
|
||||
avail_exprs_stack->record_cond (eq);
|
||||
|
||||
tree lhs = edge_info->lhs;
|
||||
if (!lhs || TREE_CODE (lhs) != SSA_NAME)
|
||||
@ -1105,29 +951,6 @@ dump_dominator_optimization_stats (FILE *file,
|
||||
}
|
||||
|
||||
|
||||
/* Enter condition equivalence P into AVAIL_EXPRS_HASH.
|
||||
|
||||
This indicates that a conditional expression has a known
|
||||
boolean value. */
|
||||
|
||||
static void
|
||||
record_cond (cond_equivalence *p,
|
||||
class avail_exprs_stack *avail_exprs_stack)
|
||||
{
|
||||
class expr_hash_elt *element = new expr_hash_elt (&p->cond, p->value);
|
||||
expr_hash_elt **slot;
|
||||
|
||||
hash_table<expr_elt_hasher> *avail_exprs = avail_exprs_stack->avail_exprs ();
|
||||
slot = avail_exprs->find_slot_with_hash (element, element->hash (), INSERT);
|
||||
if (*slot == NULL)
|
||||
{
|
||||
*slot = element;
|
||||
avail_exprs_stack->record_expr (element, NULL, '1');
|
||||
}
|
||||
else
|
||||
delete element;
|
||||
}
|
||||
|
||||
/* Similarly, but assume that X and Y are the two operands of an EQ_EXPR.
|
||||
This constrains the cases in which we may treat this as assignment. */
|
||||
|
||||
@ -1426,7 +1249,7 @@ eliminate_redundant_computations (gimple_stmt_iterator* gsi,
|
||||
insert = false;
|
||||
|
||||
/* Check if the expression has been computed before. */
|
||||
cached_lhs = lookup_avail_expr (stmt, insert, avail_exprs_stack);
|
||||
cached_lhs = avail_exprs_stack->lookup_avail_expr (stmt, insert, true);
|
||||
|
||||
opt_stats.num_exprs_considered++;
|
||||
|
||||
@ -1611,7 +1434,7 @@ record_equivalences_from_stmt (gimple *stmt, int may_optimize_p,
|
||||
|
||||
/* Finally enter the statement into the available expression
|
||||
table. */
|
||||
lookup_avail_expr (new_stmt, true, avail_exprs_stack);
|
||||
avail_exprs_stack->lookup_avail_expr (new_stmt, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1865,8 +1688,8 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator si,
|
||||
else
|
||||
new_stmt = gimple_build_assign (rhs, lhs);
|
||||
gimple_set_vuse (new_stmt, gimple_vuse (stmt));
|
||||
cached_lhs = lookup_avail_expr (new_stmt, false, avail_exprs_stack,
|
||||
false);
|
||||
cached_lhs = avail_exprs_stack->lookup_avail_expr (new_stmt, false,
|
||||
false);
|
||||
if (cached_lhs
|
||||
&& rhs == cached_lhs)
|
||||
{
|
||||
@ -1942,125 +1765,3 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator si,
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Helper for walk_non_aliased_vuses. Determine if we arrived at
|
||||
the desired memory state. */
|
||||
|
||||
static void *
|
||||
vuse_eq (ao_ref *, tree vuse1, unsigned int cnt, void *data)
|
||||
{
|
||||
tree vuse2 = (tree) data;
|
||||
if (vuse1 == vuse2)
|
||||
return data;
|
||||
|
||||
/* This bounds the stmt walks we perform on reference lookups
|
||||
to O(1) instead of O(N) where N is the number of dominating
|
||||
stores leading to a candidate. We re-use the SCCVN param
|
||||
for this as it is basically the same complexity. */
|
||||
if (cnt > (unsigned) PARAM_VALUE (PARAM_SCCVN_MAX_ALIAS_QUERIES_PER_ACCESS))
|
||||
return (void *)-1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for an existing instance of STMT in the AVAIL_EXPRS_STACK table.
|
||||
If found, return its LHS. Otherwise insert STMT in the table and
|
||||
return NULL_TREE.
|
||||
|
||||
Also, when an expression is first inserted in the table, it is also
|
||||
is also added to AVAIL_EXPRS_STACK, so that it can be removed when
|
||||
we finish processing this block and its children. */
|
||||
|
||||
static tree
|
||||
lookup_avail_expr (gimple *stmt, bool insert,
|
||||
class avail_exprs_stack *avail_exprs_stack, bool tbaa_p)
|
||||
{
|
||||
expr_hash_elt **slot;
|
||||
tree lhs;
|
||||
|
||||
/* Get LHS of phi, assignment, or call; else NULL_TREE. */
|
||||
if (gimple_code (stmt) == GIMPLE_PHI)
|
||||
lhs = gimple_phi_result (stmt);
|
||||
else
|
||||
lhs = gimple_get_lhs (stmt);
|
||||
|
||||
class expr_hash_elt element (stmt, lhs);
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "LKUP ");
|
||||
element.print (dump_file);
|
||||
}
|
||||
|
||||
/* Don't bother remembering constant assignments and copy operations.
|
||||
Constants and copy operations are handled by the constant/copy propagator
|
||||
in optimize_stmt. */
|
||||
if (element.expr()->kind == EXPR_SINGLE
|
||||
&& (TREE_CODE (element.expr()->ops.single.rhs) == SSA_NAME
|
||||
|| is_gimple_min_invariant (element.expr()->ops.single.rhs)))
|
||||
return NULL_TREE;
|
||||
|
||||
/* Finally try to find the expression in the main expression hash table. */
|
||||
hash_table<expr_elt_hasher> *avail_exprs = avail_exprs_stack->avail_exprs ();
|
||||
slot = avail_exprs->find_slot (&element, (insert ? INSERT : NO_INSERT));
|
||||
if (slot == NULL)
|
||||
{
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (*slot == NULL)
|
||||
{
|
||||
class expr_hash_elt *element2 = new expr_hash_elt (element);
|
||||
*slot = element2;
|
||||
|
||||
avail_exprs_stack->record_expr (element2, NULL, '2');
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* If we found a redundant memory operation do an alias walk to
|
||||
check if we can re-use it. */
|
||||
if (gimple_vuse (stmt) != (*slot)->vop ())
|
||||
{
|
||||
tree vuse1 = (*slot)->vop ();
|
||||
tree vuse2 = gimple_vuse (stmt);
|
||||
/* If we have a load of a register and a candidate in the
|
||||
hash with vuse1 then try to reach its stmt by walking
|
||||
up the virtual use-def chain using walk_non_aliased_vuses.
|
||||
But don't do this when removing expressions from the hash. */
|
||||
ao_ref ref;
|
||||
if (!(vuse1 && vuse2
|
||||
&& gimple_assign_single_p (stmt)
|
||||
&& TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME
|
||||
&& (ao_ref_init (&ref, gimple_assign_rhs1 (stmt)),
|
||||
ref.base_alias_set = ref.ref_alias_set = tbaa_p ? -1 : 0, true)
|
||||
&& walk_non_aliased_vuses (&ref, vuse2,
|
||||
vuse_eq, NULL, NULL, vuse1) != NULL))
|
||||
{
|
||||
if (insert)
|
||||
{
|
||||
class expr_hash_elt *element2 = new expr_hash_elt (element);
|
||||
|
||||
/* Insert the expr into the hash by replacing the current
|
||||
entry and recording the value to restore in the
|
||||
avail_exprs_stack. */
|
||||
avail_exprs_stack->record_expr (element2, *slot, '2');
|
||||
*slot = element2;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the LHS of the assignment so that it can be used as the current
|
||||
definition of another variable. */
|
||||
lhs = (*slot)->lhs ();
|
||||
|
||||
lhs = dom_valueize (lhs);
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "FIND: ");
|
||||
print_generic_expr (dump_file, lhs, 0);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "tree-eh.h"
|
||||
#include "internal-fn.h"
|
||||
#include "tree-dfa.h"
|
||||
#include "options.h"
|
||||
#include "params.h"
|
||||
|
||||
static bool hashable_expr_equal_p (const struct hashable_expr *,
|
||||
const struct hashable_expr *);
|
||||
@ -94,6 +96,153 @@ avail_exprs_stack::record_expr (class expr_hash_elt *elt1,
|
||||
m_stack.safe_push (std::pair<expr_hash_elt_t, expr_hash_elt_t> (elt1, elt2));
|
||||
}
|
||||
|
||||
/* Helper for walk_non_aliased_vuses. Determine if we arrived at
|
||||
the desired memory state. */
|
||||
|
||||
static void *
|
||||
vuse_eq (ao_ref *, tree vuse1, unsigned int cnt, void *data)
|
||||
{
|
||||
tree vuse2 = (tree) data;
|
||||
if (vuse1 == vuse2)
|
||||
return data;
|
||||
|
||||
/* This bounds the stmt walks we perform on reference lookups
|
||||
to O(1) instead of O(N) where N is the number of dominating
|
||||
stores leading to a candidate. We re-use the SCCVN param
|
||||
for this as it is basically the same complexity. */
|
||||
if (cnt > (unsigned) PARAM_VALUE (PARAM_SCCVN_MAX_ALIAS_QUERIES_PER_ACCESS))
|
||||
return (void *)-1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for an existing instance of STMT in the AVAIL_EXPRS_STACK table.
|
||||
If found, return its LHS. Otherwise insert STMT in the table and
|
||||
return NULL_TREE.
|
||||
|
||||
Also, when an expression is first inserted in the table, it is also
|
||||
is also added to AVAIL_EXPRS_STACK, so that it can be removed when
|
||||
we finish processing this block and its children. */
|
||||
|
||||
tree
|
||||
avail_exprs_stack::lookup_avail_expr (gimple *stmt, bool insert, bool tbaa_p)
|
||||
{
|
||||
expr_hash_elt **slot;
|
||||
tree lhs;
|
||||
|
||||
/* Get LHS of phi, assignment, or call; else NULL_TREE. */
|
||||
if (gimple_code (stmt) == GIMPLE_PHI)
|
||||
lhs = gimple_phi_result (stmt);
|
||||
else
|
||||
lhs = gimple_get_lhs (stmt);
|
||||
|
||||
class expr_hash_elt element (stmt, lhs);
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "LKUP ");
|
||||
element.print (dump_file);
|
||||
}
|
||||
|
||||
/* Don't bother remembering constant assignments and copy operations.
|
||||
Constants and copy operations are handled by the constant/copy propagator
|
||||
in optimize_stmt. */
|
||||
if (element.expr()->kind == EXPR_SINGLE
|
||||
&& (TREE_CODE (element.expr()->ops.single.rhs) == SSA_NAME
|
||||
|| is_gimple_min_invariant (element.expr()->ops.single.rhs)))
|
||||
return NULL_TREE;
|
||||
|
||||
/* Finally try to find the expression in the main expression hash table. */
|
||||
slot = m_avail_exprs->find_slot (&element, (insert ? INSERT : NO_INSERT));
|
||||
if (slot == NULL)
|
||||
{
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (*slot == NULL)
|
||||
{
|
||||
class expr_hash_elt *element2 = new expr_hash_elt (element);
|
||||
*slot = element2;
|
||||
|
||||
record_expr (element2, NULL, '2');
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* If we found a redundant memory operation do an alias walk to
|
||||
check if we can re-use it. */
|
||||
if (gimple_vuse (stmt) != (*slot)->vop ())
|
||||
{
|
||||
tree vuse1 = (*slot)->vop ();
|
||||
tree vuse2 = gimple_vuse (stmt);
|
||||
/* If we have a load of a register and a candidate in the
|
||||
hash with vuse1 then try to reach its stmt by walking
|
||||
up the virtual use-def chain using walk_non_aliased_vuses.
|
||||
But don't do this when removing expressions from the hash. */
|
||||
ao_ref ref;
|
||||
if (!(vuse1 && vuse2
|
||||
&& gimple_assign_single_p (stmt)
|
||||
&& TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME
|
||||
&& (ao_ref_init (&ref, gimple_assign_rhs1 (stmt)),
|
||||
ref.base_alias_set = ref.ref_alias_set = tbaa_p ? -1 : 0, true)
|
||||
&& walk_non_aliased_vuses (&ref, vuse2,
|
||||
vuse_eq, NULL, NULL, vuse1) != NULL))
|
||||
{
|
||||
if (insert)
|
||||
{
|
||||
class expr_hash_elt *element2 = new expr_hash_elt (element);
|
||||
|
||||
/* Insert the expr into the hash by replacing the current
|
||||
entry and recording the value to restore in the
|
||||
avail_exprs_stack. */
|
||||
record_expr (element2, *slot, '2');
|
||||
*slot = element2;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the LHS of the assignment so that it can be used as the current
|
||||
definition of another variable. */
|
||||
lhs = (*slot)->lhs ();
|
||||
|
||||
/* Valueize the result. */
|
||||
if (TREE_CODE (lhs) == SSA_NAME)
|
||||
{
|
||||
tree tem = SSA_NAME_VALUE (lhs);
|
||||
if (tem)
|
||||
lhs = tem;
|
||||
}
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "FIND: ");
|
||||
print_generic_expr (dump_file, lhs, 0);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/* Enter condition equivalence P into the hash table.
|
||||
|
||||
This indicates that a conditional expression has a known
|
||||
boolean value. */
|
||||
|
||||
void
|
||||
avail_exprs_stack::record_cond (cond_equivalence *p)
|
||||
{
|
||||
class expr_hash_elt *element = new expr_hash_elt (&p->cond, p->value);
|
||||
expr_hash_elt **slot;
|
||||
|
||||
slot = m_avail_exprs->find_slot_with_hash (element, element->hash (), INSERT);
|
||||
if (*slot == NULL)
|
||||
{
|
||||
*slot = element;
|
||||
record_expr (element, NULL, '1');
|
||||
}
|
||||
else
|
||||
delete element;
|
||||
}
|
||||
|
||||
/* Generate a hash value for a pair of expressions. This can be used
|
||||
iteratively by passing a previous result in HSTATE.
|
||||
|
||||
@ -798,3 +947,125 @@ initialize_expr_from_cond (tree cond, struct hashable_expr *expr)
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Build a cond_equivalence record indicating that the comparison
|
||||
CODE holds between operands OP0 and OP1 and push it to **P. */
|
||||
|
||||
static void
|
||||
build_and_record_new_cond (enum tree_code code,
|
||||
tree op0, tree op1,
|
||||
vec<cond_equivalence> *p,
|
||||
bool val = true)
|
||||
{
|
||||
cond_equivalence c;
|
||||
struct hashable_expr *cond = &c.cond;
|
||||
|
||||
gcc_assert (TREE_CODE_CLASS (code) == tcc_comparison);
|
||||
|
||||
cond->type = boolean_type_node;
|
||||
cond->kind = EXPR_BINARY;
|
||||
cond->ops.binary.op = code;
|
||||
cond->ops.binary.opnd0 = op0;
|
||||
cond->ops.binary.opnd1 = op1;
|
||||
|
||||
c.value = val ? boolean_true_node : boolean_false_node;
|
||||
p->safe_push (c);
|
||||
}
|
||||
|
||||
/* Record that COND is true and INVERTED is false into the edge information
|
||||
structure. Also record that any conditions dominated by COND are true
|
||||
as well.
|
||||
|
||||
For example, if a < b is true, then a <= b must also be true. */
|
||||
|
||||
void
|
||||
record_conditions (vec<cond_equivalence> *p, tree cond, tree inverted)
|
||||
{
|
||||
tree op0, op1;
|
||||
cond_equivalence c;
|
||||
|
||||
if (!COMPARISON_CLASS_P (cond))
|
||||
return;
|
||||
|
||||
op0 = TREE_OPERAND (cond, 0);
|
||||
op1 = TREE_OPERAND (cond, 1);
|
||||
|
||||
switch (TREE_CODE (cond))
|
||||
{
|
||||
case LT_EXPR:
|
||||
case GT_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (LTGT_EXPR, op0, op1, p);
|
||||
}
|
||||
|
||||
build_and_record_new_cond ((TREE_CODE (cond) == LT_EXPR
|
||||
? LE_EXPR : GE_EXPR),
|
||||
op0, op1, p);
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (EQ_EXPR, op0, op1, p, false);
|
||||
break;
|
||||
|
||||
case GE_EXPR:
|
||||
case LE_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case EQ_EXPR:
|
||||
if (FLOAT_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1, p);
|
||||
}
|
||||
build_and_record_new_cond (LE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (GE_EXPR, op0, op1, p);
|
||||
break;
|
||||
|
||||
case UNORDERED_EXPR:
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNLE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNGE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNEQ_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNLT_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNGT_EXPR, op0, op1, p);
|
||||
break;
|
||||
|
||||
case UNLT_EXPR:
|
||||
case UNGT_EXPR:
|
||||
build_and_record_new_cond ((TREE_CODE (cond) == UNLT_EXPR
|
||||
? UNLE_EXPR : UNGE_EXPR),
|
||||
op0, op1, p);
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1, p);
|
||||
break;
|
||||
|
||||
case UNEQ_EXPR:
|
||||
build_and_record_new_cond (UNLE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (UNGE_EXPR, op0, op1, p);
|
||||
break;
|
||||
|
||||
case LTGT_EXPR:
|
||||
build_and_record_new_cond (NE_EXPR, op0, op1, p);
|
||||
build_and_record_new_cond (ORDERED_EXPR, op0, op1, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now store the original true and false conditions into the first
|
||||
two slots. */
|
||||
initialize_expr_from_cond (cond, &c.cond);
|
||||
c.value = boolean_true_node;
|
||||
p->safe_push (c);
|
||||
|
||||
/* It is possible for INVERTED to be the negation of a comparison,
|
||||
and not a valid RHS or GIMPLE_COND condition. This happens because
|
||||
invert_truthvalue may return such an expression when asked to invert
|
||||
a floating-point comparison. These comparisons are not assumed to
|
||||
obey the trichotomy law. */
|
||||
initialize_expr_from_cond (inverted, &c.cond);
|
||||
c.value = boolean_false_node;
|
||||
p->safe_push (c);
|
||||
}
|
||||
|
@ -47,6 +47,20 @@ struct hashable_expr
|
||||
} ops;
|
||||
};
|
||||
|
||||
/* Structure for recording known value of a conditional expression.
|
||||
|
||||
Clients build vectors of these objects to record known values
|
||||
that occur on edges. */
|
||||
|
||||
struct cond_equivalence
|
||||
{
|
||||
/* The condition, in a HASHABLE_EXPR form. */
|
||||
struct hashable_expr cond;
|
||||
|
||||
/* The result of the condition (true or false. */
|
||||
tree value;
|
||||
};
|
||||
|
||||
/* Structure for entries in the expression hash table. */
|
||||
|
||||
typedef class expr_hash_elt * expr_hash_elt_t;
|
||||
@ -132,6 +146,12 @@ class avail_exprs_stack
|
||||
hash_table<expr_elt_hasher> *avail_exprs (void)
|
||||
{ return m_avail_exprs; }
|
||||
|
||||
/* Lookup and conditionally insert an expression into the table,
|
||||
recording enough information to unwind as needed. */
|
||||
tree lookup_avail_expr (gimple *, bool, bool);
|
||||
|
||||
void record_cond (cond_equivalence *);
|
||||
|
||||
private:
|
||||
vec<std::pair<expr_hash_elt_t, expr_hash_elt_t> > m_stack;
|
||||
hash_table<expr_elt_hasher> *m_avail_exprs;
|
||||
@ -182,5 +202,6 @@ class const_and_copies
|
||||
};
|
||||
|
||||
void initialize_expr_from_cond (tree cond, struct hashable_expr *expr);
|
||||
void record_conditions (vec<cond_equivalence> *p, tree, tree);
|
||||
|
||||
#endif /* GCC_TREE_SSA_SCOPED_TABLES_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user