640296c367
Use a dtor to automatically remove ITER from IMM_USE list in FOR_EACH_IMM_USE_STMT. for gcc/ChangeLog * ssa-iterators.h (end_imm_use_stmt_traverse): Forward declare. (auto_end_imm_use_stmt_traverse): New struct. (FOR_EACH_IMM_USE_STMT): Use it. (BREAK_FROM_IMM_USE_STMT, RETURN_FROM_IMM_USE_STMT): Remove, along with uses... * gimple-ssa-strength-reduction.c: ... here, ... * graphite-scop-detection.c: ... here, ... * ipa-modref.c, ipa-pure-const.c, ipa-sra.c: ... here, ... * tree-predcom.c, tree-ssa-ccp.c: ... here, ... * tree-ssa-dce.c, tree-ssa-dse.c: ... here, ... * tree-ssa-loop-ivopts.c, tree-ssa-math-opts.c: ... here, ... * tree-ssa-phiprop.c, tree-ssa.c: ... here, ... * tree-vect-slp.c: ... and here, ... * doc/tree-ssa.texi: ... and the example here.
4072 lines
121 KiB
C
4072 lines
121 KiB
C
/* Straight-line strength reduction.
|
||
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||
Contributed by Bill Schmidt, IBM <wschmidt@linux.ibm.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/>. */
|
||
|
||
/* There are many algorithms for performing strength reduction on
|
||
loops. This is not one of them. IVOPTS handles strength reduction
|
||
of induction variables just fine. This pass is intended to pick
|
||
up the crumbs it leaves behind, by considering opportunities for
|
||
strength reduction along dominator paths.
|
||
|
||
Strength reduction addresses explicit multiplies, and certain
|
||
multiplies implicit in addressing expressions. It would also be
|
||
possible to apply strength reduction to divisions and modulos,
|
||
but such opportunities are relatively uncommon.
|
||
|
||
Strength reduction is also currently restricted to integer operations.
|
||
If desired, it could be extended to floating-point operations under
|
||
control of something like -funsafe-math-optimizations. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "backend.h"
|
||
#include "rtl.h"
|
||
#include "tree.h"
|
||
#include "gimple.h"
|
||
#include "cfghooks.h"
|
||
#include "tree-pass.h"
|
||
#include "ssa.h"
|
||
#include "expmed.h"
|
||
#include "gimple-pretty-print.h"
|
||
#include "fold-const.h"
|
||
#include "gimple-iterator.h"
|
||
#include "gimplify-me.h"
|
||
#include "stor-layout.h"
|
||
#include "cfgloop.h"
|
||
#include "tree-cfg.h"
|
||
#include "domwalk.h"
|
||
#include "tree-ssa-address.h"
|
||
#include "tree-affine.h"
|
||
#include "tree-eh.h"
|
||
#include "builtins.h"
|
||
|
||
/* Information about a strength reduction candidate. Each statement
|
||
in the candidate table represents an expression of one of the
|
||
following forms (the special case of CAND_REF will be described
|
||
later):
|
||
|
||
(CAND_MULT) S1: X = (B + i) * S
|
||
(CAND_ADD) S1: X = B + (i * S)
|
||
|
||
Here X and B are SSA names, i is an integer constant, and S is
|
||
either an SSA name or a constant. We call B the "base," i the
|
||
"index", and S the "stride."
|
||
|
||
Any statement S0 that dominates S1 and is of the form:
|
||
|
||
(CAND_MULT) S0: Y = (B + i') * S
|
||
(CAND_ADD) S0: Y = B + (i' * S)
|
||
|
||
is called a "basis" for S1. In both cases, S1 may be replaced by
|
||
|
||
S1': X = Y + (i - i') * S,
|
||
|
||
where (i - i') * S is folded to the extent possible.
|
||
|
||
All gimple statements are visited in dominator order, and each
|
||
statement that may contribute to one of the forms of S1 above is
|
||
given at least one entry in the candidate table. Such statements
|
||
include addition, pointer addition, subtraction, multiplication,
|
||
negation, copies, and nontrivial type casts. If a statement may
|
||
represent more than one expression of the forms of S1 above,
|
||
multiple "interpretations" are stored in the table and chained
|
||
together. Examples:
|
||
|
||
* An add of two SSA names may treat either operand as the base.
|
||
* A multiply of two SSA names, likewise.
|
||
* A copy or cast may be thought of as either a CAND_MULT with
|
||
i = 0 and S = 1, or as a CAND_ADD with i = 0 or S = 0.
|
||
|
||
Candidate records are allocated from an obstack. They are addressed
|
||
both from a hash table keyed on S1, and from a vector of candidate
|
||
pointers arranged in predominator order.
|
||
|
||
Opportunity note
|
||
----------------
|
||
Currently we don't recognize:
|
||
|
||
S0: Y = (S * i') - B
|
||
S1: X = (S * i) - B
|
||
|
||
as a strength reduction opportunity, even though this S1 would
|
||
also be replaceable by the S1' above. This can be added if it
|
||
comes up in practice.
|
||
|
||
Strength reduction in addressing
|
||
--------------------------------
|
||
There is another kind of candidate known as CAND_REF. A CAND_REF
|
||
describes a statement containing a memory reference having
|
||
complex addressing that might benefit from strength reduction.
|
||
Specifically, we are interested in references for which
|
||
get_inner_reference returns a base address, offset, and bitpos as
|
||
follows:
|
||
|
||
base: MEM_REF (T1, C1)
|
||
offset: MULT_EXPR (PLUS_EXPR (T2, C2), C3)
|
||
bitpos: C4 * BITS_PER_UNIT
|
||
|
||
Here T1 and T2 are arbitrary trees, and C1, C2, C3, C4 are
|
||
arbitrary integer constants. Note that C2 may be zero, in which
|
||
case the offset will be MULT_EXPR (T2, C3).
|
||
|
||
When this pattern is recognized, the original memory reference
|
||
can be replaced with:
|
||
|
||
MEM_REF (POINTER_PLUS_EXPR (T1, MULT_EXPR (T2, C3)),
|
||
C1 + (C2 * C3) + C4)
|
||
|
||
which distributes the multiply to allow constant folding. When
|
||
two or more addressing expressions can be represented by MEM_REFs
|
||
of this form, differing only in the constants C1, C2, and C4,
|
||
making this substitution produces more efficient addressing during
|
||
the RTL phases. When there are not at least two expressions with
|
||
the same values of T1, T2, and C3, there is nothing to be gained
|
||
by the replacement.
|
||
|
||
Strength reduction of CAND_REFs uses the same infrastructure as
|
||
that used by CAND_MULTs and CAND_ADDs. We record T1 in the base (B)
|
||
field, MULT_EXPR (T2, C3) in the stride (S) field, and
|
||
C1 + (C2 * C3) + C4 in the index (i) field. A basis for a CAND_REF
|
||
is thus another CAND_REF with the same B and S values. When at
|
||
least two CAND_REFs are chained together using the basis relation,
|
||
each of them is replaced as above, resulting in improved code
|
||
generation for addressing.
|
||
|
||
Conditional candidates
|
||
======================
|
||
|
||
Conditional candidates are best illustrated with an example.
|
||
Consider the code sequence:
|
||
|
||
(1) x_0 = ...;
|
||
(2) a_0 = x_0 * 5; MULT (B: x_0; i: 0; S: 5)
|
||
if (...)
|
||
(3) x_1 = x_0 + 1; ADD (B: x_0, i: 1; S: 1)
|
||
(4) x_2 = PHI <x_0, x_1>; PHI (B: x_0, i: 0, S: 1)
|
||
(5) x_3 = x_2 + 1; ADD (B: x_2, i: 1, S: 1)
|
||
(6) a_1 = x_3 * 5; MULT (B: x_2, i: 1; S: 5)
|
||
|
||
Here strength reduction is complicated by the uncertain value of x_2.
|
||
A legitimate transformation is:
|
||
|
||
(1) x_0 = ...;
|
||
(2) a_0 = x_0 * 5;
|
||
if (...)
|
||
{
|
||
(3) [x_1 = x_0 + 1;]
|
||
(3a) t_1 = a_0 + 5;
|
||
}
|
||
(4) [x_2 = PHI <x_0, x_1>;]
|
||
(4a) t_2 = PHI <a_0, t_1>;
|
||
(5) [x_3 = x_2 + 1;]
|
||
(6r) a_1 = t_2 + 5;
|
||
|
||
where the bracketed instructions may go dead.
|
||
|
||
To recognize this opportunity, we have to observe that statement (6)
|
||
has a "hidden basis" (2). The hidden basis is unlike a normal basis
|
||
in that the statement and the hidden basis have different base SSA
|
||
names (x_2 and x_0, respectively). The relationship is established
|
||
when a statement's base name (x_2) is defined by a phi statement (4),
|
||
each argument of which (x_0, x_1) has an identical "derived base name."
|
||
If the argument is defined by a candidate (as x_1 is by (3)) that is a
|
||
CAND_ADD having a stride of 1, the derived base name of the argument is
|
||
the base name of the candidate (x_0). Otherwise, the argument itself
|
||
is its derived base name (as is the case with argument x_0).
|
||
|
||
The hidden basis for statement (6) is the nearest dominating candidate
|
||
whose base name is the derived base name (x_0) of the feeding phi (4),
|
||
and whose stride is identical to that of the statement. We can then
|
||
create the new "phi basis" (4a) and feeding adds along incoming arcs (3a),
|
||
allowing the final replacement of (6) by the strength-reduced (6r).
|
||
|
||
To facilitate this, a new kind of candidate (CAND_PHI) is introduced.
|
||
A CAND_PHI is not a candidate for replacement, but is maintained in the
|
||
candidate table to ease discovery of hidden bases. Any phi statement
|
||
whose arguments share a common derived base name is entered into the
|
||
table with the derived base name, an (arbitrary) index of zero, and a
|
||
stride of 1. A statement with a hidden basis can then be detected by
|
||
simply looking up its feeding phi definition in the candidate table,
|
||
extracting the derived base name, and searching for a basis in the
|
||
usual manner after substituting the derived base name.
|
||
|
||
Note that the transformation is only valid when the original phi and
|
||
the statements that define the phi's arguments are all at the same
|
||
position in the loop hierarchy. */
|
||
|
||
|
||
/* Index into the candidate vector, offset by 1. VECs are zero-based,
|
||
while cand_idx's are one-based, with zero indicating null. */
|
||
typedef unsigned cand_idx;
|
||
|
||
/* The kind of candidate. */
|
||
enum cand_kind
|
||
{
|
||
CAND_MULT,
|
||
CAND_ADD,
|
||
CAND_REF,
|
||
CAND_PHI
|
||
};
|
||
|
||
class slsr_cand_d
|
||
{
|
||
public:
|
||
/* The candidate statement S1. */
|
||
gimple *cand_stmt;
|
||
|
||
/* The base expression B: often an SSA name, but not always. */
|
||
tree base_expr;
|
||
|
||
/* The stride S. */
|
||
tree stride;
|
||
|
||
/* The index constant i. */
|
||
widest_int index;
|
||
|
||
/* The type of the candidate. This is normally the type of base_expr,
|
||
but casts may have occurred when combining feeding instructions.
|
||
A candidate can only be a basis for candidates of the same final type.
|
||
(For CAND_REFs, this is the type to be used for operand 1 of the
|
||
replacement MEM_REF.) */
|
||
tree cand_type;
|
||
|
||
/* The type to be used to interpret the stride field when the stride
|
||
is not a constant. Normally the same as the type of the recorded
|
||
stride, but when the stride has been cast we need to maintain that
|
||
knowledge in order to make legal substitutions without losing
|
||
precision. When the stride is a constant, this will be sizetype. */
|
||
tree stride_type;
|
||
|
||
/* The kind of candidate (CAND_MULT, etc.). */
|
||
enum cand_kind kind;
|
||
|
||
/* Index of this candidate in the candidate vector. */
|
||
cand_idx cand_num;
|
||
|
||
/* Index of the next candidate record for the same statement.
|
||
A statement may be useful in more than one way (e.g., due to
|
||
commutativity). So we can have multiple "interpretations"
|
||
of a statement. */
|
||
cand_idx next_interp;
|
||
|
||
/* Index of the first candidate record in a chain for the same
|
||
statement. */
|
||
cand_idx first_interp;
|
||
|
||
/* Index of the basis statement S0, if any, in the candidate vector. */
|
||
cand_idx basis;
|
||
|
||
/* First candidate for which this candidate is a basis, if one exists. */
|
||
cand_idx dependent;
|
||
|
||
/* Next candidate having the same basis as this one. */
|
||
cand_idx sibling;
|
||
|
||
/* If this is a conditional candidate, the CAND_PHI candidate
|
||
that defines the base SSA name B. */
|
||
cand_idx def_phi;
|
||
|
||
/* Savings that can be expected from eliminating dead code if this
|
||
candidate is replaced. */
|
||
int dead_savings;
|
||
|
||
/* For PHI candidates, use a visited flag to keep from processing the
|
||
same PHI twice from multiple paths. */
|
||
int visited;
|
||
|
||
/* We sometimes have to cache a phi basis with a phi candidate to
|
||
avoid processing it twice. Valid only if visited==1. */
|
||
tree cached_basis;
|
||
};
|
||
|
||
typedef class slsr_cand_d slsr_cand, *slsr_cand_t;
|
||
typedef const class slsr_cand_d *const_slsr_cand_t;
|
||
|
||
/* Pointers to candidates are chained together as part of a mapping
|
||
from base expressions to the candidates that use them. */
|
||
|
||
struct cand_chain_d
|
||
{
|
||
/* Base expression for the chain of candidates: often, but not
|
||
always, an SSA name. */
|
||
tree base_expr;
|
||
|
||
/* Pointer to a candidate. */
|
||
slsr_cand_t cand;
|
||
|
||
/* Chain pointer. */
|
||
struct cand_chain_d *next;
|
||
|
||
};
|
||
|
||
typedef struct cand_chain_d cand_chain, *cand_chain_t;
|
||
typedef const struct cand_chain_d *const_cand_chain_t;
|
||
|
||
/* Information about a unique "increment" associated with candidates
|
||
having an SSA name for a stride. An increment is the difference
|
||
between the index of the candidate and the index of its basis,
|
||
i.e., (i - i') as discussed in the module commentary.
|
||
|
||
When we are not going to generate address arithmetic we treat
|
||
increments that differ only in sign as the same, allowing sharing
|
||
of the cost of initializers. The absolute value of the increment
|
||
is stored in the incr_info. */
|
||
|
||
class incr_info_d
|
||
{
|
||
public:
|
||
/* The increment that relates a candidate to its basis. */
|
||
widest_int incr;
|
||
|
||
/* How many times the increment occurs in the candidate tree. */
|
||
unsigned count;
|
||
|
||
/* Cost of replacing candidates using this increment. Negative and
|
||
zero costs indicate replacement should be performed. */
|
||
int cost;
|
||
|
||
/* If this increment is profitable but is not -1, 0, or 1, it requires
|
||
an initializer T_0 = stride * incr to be found or introduced in the
|
||
nearest common dominator of all candidates. This field holds T_0
|
||
for subsequent use. */
|
||
tree initializer;
|
||
|
||
/* If the initializer was found to already exist, this is the block
|
||
where it was found. */
|
||
basic_block init_bb;
|
||
};
|
||
|
||
typedef class incr_info_d incr_info, *incr_info_t;
|
||
|
||
/* Candidates are maintained in a vector. If candidate X dominates
|
||
candidate Y, then X appears before Y in the vector; but the
|
||
converse does not necessarily hold. */
|
||
static vec<slsr_cand_t> cand_vec;
|
||
|
||
enum cost_consts
|
||
{
|
||
COST_NEUTRAL = 0,
|
||
COST_INFINITE = 1000
|
||
};
|
||
|
||
enum stride_status
|
||
{
|
||
UNKNOWN_STRIDE = 0,
|
||
KNOWN_STRIDE = 1
|
||
};
|
||
|
||
enum phi_adjust_status
|
||
{
|
||
NOT_PHI_ADJUST = 0,
|
||
PHI_ADJUST = 1
|
||
};
|
||
|
||
enum count_phis_status
|
||
{
|
||
DONT_COUNT_PHIS = 0,
|
||
COUNT_PHIS = 1
|
||
};
|
||
|
||
/* Constrain how many PHI nodes we will visit for a conditional
|
||
candidate (depth and breadth). */
|
||
const int MAX_SPREAD = 16;
|
||
|
||
/* Pointer map embodying a mapping from statements to candidates. */
|
||
static hash_map<gimple *, slsr_cand_t> *stmt_cand_map;
|
||
|
||
/* Obstack for candidates. */
|
||
static struct obstack cand_obstack;
|
||
|
||
/* Obstack for candidate chains. */
|
||
static struct obstack chain_obstack;
|
||
|
||
/* An array INCR_VEC of incr_infos is used during analysis of related
|
||
candidates having an SSA name for a stride. INCR_VEC_LEN describes
|
||
its current length. MAX_INCR_VEC_LEN is used to avoid costly
|
||
pathological cases. */
|
||
static incr_info_t incr_vec;
|
||
static unsigned incr_vec_len;
|
||
const int MAX_INCR_VEC_LEN = 16;
|
||
|
||
/* For a chain of candidates with unknown stride, indicates whether or not
|
||
we must generate pointer arithmetic when replacing statements. */
|
||
static bool address_arithmetic_p;
|
||
|
||
/* Forward function declarations. */
|
||
static slsr_cand_t base_cand_from_table (tree);
|
||
static tree introduce_cast_before_cand (slsr_cand_t, tree, tree);
|
||
static bool legal_cast_p_1 (tree, tree);
|
||
|
||
/* Produce a pointer to the IDX'th candidate in the candidate vector. */
|
||
|
||
static slsr_cand_t
|
||
lookup_cand (cand_idx idx)
|
||
{
|
||
return cand_vec[idx];
|
||
}
|
||
|
||
/* Helper for hashing a candidate chain header. */
|
||
|
||
struct cand_chain_hasher : nofree_ptr_hash <cand_chain>
|
||
{
|
||
static inline hashval_t hash (const cand_chain *);
|
||
static inline bool equal (const cand_chain *, const cand_chain *);
|
||
};
|
||
|
||
inline hashval_t
|
||
cand_chain_hasher::hash (const cand_chain *p)
|
||
{
|
||
tree base_expr = p->base_expr;
|
||
return iterative_hash_expr (base_expr, 0);
|
||
}
|
||
|
||
inline bool
|
||
cand_chain_hasher::equal (const cand_chain *chain1, const cand_chain *chain2)
|
||
{
|
||
return operand_equal_p (chain1->base_expr, chain2->base_expr, 0);
|
||
}
|
||
|
||
/* Hash table embodying a mapping from base exprs to chains of candidates. */
|
||
static hash_table<cand_chain_hasher> *base_cand_map;
|
||
|
||
/* Pointer map used by tree_to_aff_combination_expand. */
|
||
static hash_map<tree, name_expansion *> *name_expansions;
|
||
/* Pointer map embodying a mapping from bases to alternative bases. */
|
||
static hash_map<tree, tree> *alt_base_map;
|
||
|
||
/* Given BASE, use the tree affine combiniation facilities to
|
||
find the underlying tree expression for BASE, with any
|
||
immediate offset excluded.
|
||
|
||
N.B. we should eliminate this backtracking with better forward
|
||
analysis in a future release. */
|
||
|
||
static tree
|
||
get_alternative_base (tree base)
|
||
{
|
||
tree *result = alt_base_map->get (base);
|
||
|
||
if (result == NULL)
|
||
{
|
||
tree expr;
|
||
aff_tree aff;
|
||
|
||
tree_to_aff_combination_expand (base, TREE_TYPE (base),
|
||
&aff, &name_expansions);
|
||
aff.offset = 0;
|
||
expr = aff_combination_to_tree (&aff);
|
||
|
||
gcc_assert (!alt_base_map->put (base, base == expr ? NULL : expr));
|
||
|
||
return expr == base ? NULL : expr;
|
||
}
|
||
|
||
return *result;
|
||
}
|
||
|
||
/* Look in the candidate table for a CAND_PHI that defines BASE and
|
||
return it if found; otherwise return NULL. */
|
||
|
||
static cand_idx
|
||
find_phi_def (tree base)
|
||
{
|
||
slsr_cand_t c;
|
||
|
||
if (TREE_CODE (base) != SSA_NAME)
|
||
return 0;
|
||
|
||
c = base_cand_from_table (base);
|
||
|
||
if (!c || c->kind != CAND_PHI
|
||
|| SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (c->cand_stmt)))
|
||
return 0;
|
||
|
||
return c->cand_num;
|
||
}
|
||
|
||
/* Determine whether all uses of NAME are directly or indirectly
|
||
used by STMT. That is, we want to know whether if STMT goes
|
||
dead, the definition of NAME also goes dead. */
|
||
static bool
|
||
uses_consumed_by_stmt (tree name, gimple *stmt, unsigned recurse = 0)
|
||
{
|
||
gimple *use_stmt;
|
||
imm_use_iterator iter;
|
||
bool retval = true;
|
||
|
||
FOR_EACH_IMM_USE_STMT (use_stmt, iter, name)
|
||
{
|
||
if (use_stmt == stmt || is_gimple_debug (use_stmt))
|
||
continue;
|
||
|
||
if (!is_gimple_assign (use_stmt)
|
||
|| !gimple_get_lhs (use_stmt)
|
||
|| !is_gimple_reg (gimple_get_lhs (use_stmt))
|
||
|| recurse >= 10
|
||
|| !uses_consumed_by_stmt (gimple_get_lhs (use_stmt), stmt,
|
||
recurse + 1))
|
||
{
|
||
retval = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
/* Helper routine for find_basis_for_candidate. May be called twice:
|
||
once for the candidate's base expr, and optionally again either for
|
||
the candidate's phi definition or for a CAND_REF's alternative base
|
||
expression. */
|
||
|
||
static slsr_cand_t
|
||
find_basis_for_base_expr (slsr_cand_t c, tree base_expr)
|
||
{
|
||
cand_chain mapping_key;
|
||
cand_chain_t chain;
|
||
slsr_cand_t basis = NULL;
|
||
|
||
// Limit potential of N^2 behavior for long candidate chains.
|
||
int iters = 0;
|
||
int max_iters = param_max_slsr_candidate_scan;
|
||
|
||
mapping_key.base_expr = base_expr;
|
||
chain = base_cand_map->find (&mapping_key);
|
||
|
||
for (; chain && iters < max_iters; chain = chain->next, ++iters)
|
||
{
|
||
slsr_cand_t one_basis = chain->cand;
|
||
|
||
if (one_basis->kind != c->kind
|
||
|| one_basis->cand_stmt == c->cand_stmt
|
||
|| !operand_equal_p (one_basis->stride, c->stride, 0)
|
||
|| !types_compatible_p (one_basis->cand_type, c->cand_type)
|
||
|| !types_compatible_p (one_basis->stride_type, c->stride_type)
|
||
|| !dominated_by_p (CDI_DOMINATORS,
|
||
gimple_bb (c->cand_stmt),
|
||
gimple_bb (one_basis->cand_stmt)))
|
||
continue;
|
||
|
||
tree lhs = gimple_assign_lhs (one_basis->cand_stmt);
|
||
if (lhs && TREE_CODE (lhs) == SSA_NAME
|
||
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
|
||
continue;
|
||
|
||
if (!basis || basis->cand_num < one_basis->cand_num)
|
||
basis = one_basis;
|
||
}
|
||
|
||
return basis;
|
||
}
|
||
|
||
/* Use the base expr from candidate C to look for possible candidates
|
||
that can serve as a basis for C. Each potential basis must also
|
||
appear in a block that dominates the candidate statement and have
|
||
the same stride and type. If more than one possible basis exists,
|
||
the one with highest index in the vector is chosen; this will be
|
||
the most immediately dominating basis. */
|
||
|
||
static int
|
||
find_basis_for_candidate (slsr_cand_t c)
|
||
{
|
||
slsr_cand_t basis = find_basis_for_base_expr (c, c->base_expr);
|
||
|
||
/* If a candidate doesn't have a basis using its base expression,
|
||
it may have a basis hidden by one or more intervening phis. */
|
||
if (!basis && c->def_phi)
|
||
{
|
||
basic_block basis_bb, phi_bb;
|
||
slsr_cand_t phi_cand = lookup_cand (c->def_phi);
|
||
basis = find_basis_for_base_expr (c, phi_cand->base_expr);
|
||
|
||
if (basis)
|
||
{
|
||
/* A hidden basis must dominate the phi-definition of the
|
||
candidate's base name. */
|
||
phi_bb = gimple_bb (phi_cand->cand_stmt);
|
||
basis_bb = gimple_bb (basis->cand_stmt);
|
||
|
||
if (phi_bb == basis_bb
|
||
|| !dominated_by_p (CDI_DOMINATORS, phi_bb, basis_bb))
|
||
{
|
||
basis = NULL;
|
||
c->basis = 0;
|
||
}
|
||
|
||
/* If we found a hidden basis, estimate additional dead-code
|
||
savings if the phi and its feeding statements can be removed. */
|
||
tree feeding_var = gimple_phi_result (phi_cand->cand_stmt);
|
||
if (basis && uses_consumed_by_stmt (feeding_var, c->cand_stmt))
|
||
c->dead_savings += phi_cand->dead_savings;
|
||
}
|
||
}
|
||
|
||
if (flag_expensive_optimizations && !basis && c->kind == CAND_REF)
|
||
{
|
||
tree alt_base_expr = get_alternative_base (c->base_expr);
|
||
if (alt_base_expr)
|
||
basis = find_basis_for_base_expr (c, alt_base_expr);
|
||
}
|
||
|
||
if (basis)
|
||
{
|
||
c->sibling = basis->dependent;
|
||
basis->dependent = c->cand_num;
|
||
return basis->cand_num;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Record a mapping from BASE to C, indicating that C may potentially serve
|
||
as a basis using that base expression. BASE may be the same as
|
||
C->BASE_EXPR; alternatively BASE can be a different tree that share the
|
||
underlining expression of C->BASE_EXPR. */
|
||
|
||
static void
|
||
record_potential_basis (slsr_cand_t c, tree base)
|
||
{
|
||
cand_chain_t node;
|
||
cand_chain **slot;
|
||
|
||
gcc_assert (base);
|
||
|
||
node = (cand_chain_t) obstack_alloc (&chain_obstack, sizeof (cand_chain));
|
||
node->base_expr = base;
|
||
node->cand = c;
|
||
node->next = NULL;
|
||
slot = base_cand_map->find_slot (node, INSERT);
|
||
|
||
if (*slot)
|
||
{
|
||
cand_chain_t head = (cand_chain_t) (*slot);
|
||
node->next = head->next;
|
||
head->next = node;
|
||
}
|
||
else
|
||
*slot = node;
|
||
}
|
||
|
||
/* Allocate storage for a new candidate and initialize its fields.
|
||
Attempt to find a basis for the candidate.
|
||
|
||
For CAND_REF, an alternative base may also be recorded and used
|
||
to find a basis. This helps cases where the expression hidden
|
||
behind BASE (which is usually an SSA_NAME) has immediate offset,
|
||
e.g.
|
||
|
||
a2[i][j] = 1;
|
||
a2[i + 20][j] = 2; */
|
||
|
||
static slsr_cand_t
|
||
alloc_cand_and_find_basis (enum cand_kind kind, gimple *gs, tree base,
|
||
const widest_int &index, tree stride, tree ctype,
|
||
tree stype, unsigned savings)
|
||
{
|
||
slsr_cand_t c = (slsr_cand_t) obstack_alloc (&cand_obstack,
|
||
sizeof (slsr_cand));
|
||
c->cand_stmt = gs;
|
||
c->base_expr = base;
|
||
c->stride = stride;
|
||
c->index = index;
|
||
c->cand_type = ctype;
|
||
c->stride_type = stype;
|
||
c->kind = kind;
|
||
c->cand_num = cand_vec.length ();
|
||
c->next_interp = 0;
|
||
c->first_interp = c->cand_num;
|
||
c->dependent = 0;
|
||
c->sibling = 0;
|
||
c->def_phi = kind == CAND_MULT ? find_phi_def (base) : 0;
|
||
c->dead_savings = savings;
|
||
c->visited = 0;
|
||
c->cached_basis = NULL_TREE;
|
||
|
||
cand_vec.safe_push (c);
|
||
|
||
if (kind == CAND_PHI)
|
||
c->basis = 0;
|
||
else
|
||
c->basis = find_basis_for_candidate (c);
|
||
|
||
record_potential_basis (c, base);
|
||
if (flag_expensive_optimizations && kind == CAND_REF)
|
||
{
|
||
tree alt_base = get_alternative_base (base);
|
||
if (alt_base)
|
||
record_potential_basis (c, alt_base);
|
||
}
|
||
|
||
return c;
|
||
}
|
||
|
||
/* Determine the target cost of statement GS when compiling according
|
||
to SPEED. */
|
||
|
||
static int
|
||
stmt_cost (gimple *gs, bool speed)
|
||
{
|
||
tree lhs, rhs1, rhs2;
|
||
machine_mode lhs_mode;
|
||
|
||
gcc_assert (is_gimple_assign (gs));
|
||
lhs = gimple_assign_lhs (gs);
|
||
rhs1 = gimple_assign_rhs1 (gs);
|
||
lhs_mode = TYPE_MODE (TREE_TYPE (lhs));
|
||
|
||
switch (gimple_assign_rhs_code (gs))
|
||
{
|
||
case MULT_EXPR:
|
||
rhs2 = gimple_assign_rhs2 (gs);
|
||
|
||
if (tree_fits_shwi_p (rhs2))
|
||
return mult_by_coeff_cost (tree_to_shwi (rhs2), lhs_mode, speed);
|
||
|
||
gcc_assert (TREE_CODE (rhs1) != INTEGER_CST);
|
||
return mul_cost (speed, lhs_mode);
|
||
|
||
case PLUS_EXPR:
|
||
case POINTER_PLUS_EXPR:
|
||
case MINUS_EXPR:
|
||
return add_cost (speed, lhs_mode);
|
||
|
||
case NEGATE_EXPR:
|
||
return neg_cost (speed, lhs_mode);
|
||
|
||
CASE_CONVERT:
|
||
return convert_cost (lhs_mode, TYPE_MODE (TREE_TYPE (rhs1)), speed);
|
||
|
||
/* Note that we don't assign costs to copies that in most cases
|
||
will go away. */
|
||
case SSA_NAME:
|
||
return 0;
|
||
|
||
default:
|
||
;
|
||
}
|
||
|
||
gcc_unreachable ();
|
||
return 0;
|
||
}
|
||
|
||
/* Look up the defining statement for BASE_IN and return a pointer
|
||
to its candidate in the candidate table, if any; otherwise NULL.
|
||
Only CAND_ADD and CAND_MULT candidates are returned. */
|
||
|
||
static slsr_cand_t
|
||
base_cand_from_table (tree base_in)
|
||
{
|
||
slsr_cand_t *result;
|
||
|
||
gimple *def = SSA_NAME_DEF_STMT (base_in);
|
||
if (!def)
|
||
return (slsr_cand_t) NULL;
|
||
|
||
result = stmt_cand_map->get (def);
|
||
|
||
if (result && (*result)->kind != CAND_REF)
|
||
return *result;
|
||
|
||
return (slsr_cand_t) NULL;
|
||
}
|
||
|
||
/* Add an entry to the statement-to-candidate mapping. */
|
||
|
||
static void
|
||
add_cand_for_stmt (gimple *gs, slsr_cand_t c)
|
||
{
|
||
gcc_assert (!stmt_cand_map->put (gs, c));
|
||
}
|
||
|
||
/* Given PHI which contains a phi statement, determine whether it
|
||
satisfies all the requirements of a phi candidate. If so, create
|
||
a candidate. Note that a CAND_PHI never has a basis itself, but
|
||
is used to help find a basis for subsequent candidates. */
|
||
|
||
static void
|
||
slsr_process_phi (gphi *phi, bool speed)
|
||
{
|
||
unsigned i;
|
||
tree arg0_base = NULL_TREE, base_type;
|
||
slsr_cand_t c;
|
||
class loop *cand_loop = gimple_bb (phi)->loop_father;
|
||
unsigned savings = 0;
|
||
|
||
/* A CAND_PHI requires each of its arguments to have the same
|
||
derived base name. (See the module header commentary for a
|
||
definition of derived base names.) Furthermore, all feeding
|
||
definitions must be in the same position in the loop hierarchy
|
||
as PHI. */
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
slsr_cand_t arg_cand;
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
tree derived_base_name = NULL_TREE;
|
||
gimple *arg_stmt = NULL;
|
||
basic_block arg_bb = NULL;
|
||
|
||
if (TREE_CODE (arg) != SSA_NAME)
|
||
return;
|
||
|
||
arg_cand = base_cand_from_table (arg);
|
||
|
||
if (arg_cand)
|
||
{
|
||
while (arg_cand->kind != CAND_ADD && arg_cand->kind != CAND_PHI)
|
||
{
|
||
if (!arg_cand->next_interp)
|
||
return;
|
||
|
||
arg_cand = lookup_cand (arg_cand->next_interp);
|
||
}
|
||
|
||
if (!integer_onep (arg_cand->stride))
|
||
return;
|
||
|
||
derived_base_name = arg_cand->base_expr;
|
||
arg_stmt = arg_cand->cand_stmt;
|
||
arg_bb = gimple_bb (arg_stmt);
|
||
|
||
/* Gather potential dead code savings if the phi statement
|
||
can be removed later on. */
|
||
if (uses_consumed_by_stmt (arg, phi))
|
||
{
|
||
if (gimple_code (arg_stmt) == GIMPLE_PHI)
|
||
savings += arg_cand->dead_savings;
|
||
else
|
||
savings += stmt_cost (arg_stmt, speed);
|
||
}
|
||
}
|
||
else if (SSA_NAME_IS_DEFAULT_DEF (arg))
|
||
{
|
||
derived_base_name = arg;
|
||
arg_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
|
||
}
|
||
|
||
if (!arg_bb || arg_bb->loop_father != cand_loop)
|
||
return;
|
||
|
||
if (i == 0)
|
||
arg0_base = derived_base_name;
|
||
else if (!operand_equal_p (derived_base_name, arg0_base, 0))
|
||
return;
|
||
}
|
||
|
||
/* Create the candidate. "alloc_cand_and_find_basis" is named
|
||
misleadingly for this case, as no basis will be sought for a
|
||
CAND_PHI. */
|
||
base_type = TREE_TYPE (arg0_base);
|
||
|
||
c = alloc_cand_and_find_basis (CAND_PHI, phi, arg0_base,
|
||
0, integer_one_node, base_type,
|
||
sizetype, savings);
|
||
|
||
/* Add the candidate to the statement-candidate mapping. */
|
||
add_cand_for_stmt (phi, c);
|
||
}
|
||
|
||
/* Given PBASE which is a pointer to tree, look up the defining
|
||
statement for it and check whether the candidate is in the
|
||
form of:
|
||
|
||
X = B + (1 * S), S is integer constant
|
||
X = B + (i * S), S is integer one
|
||
|
||
If so, set PBASE to the candidate's base_expr and return double
|
||
int (i * S).
|
||
Otherwise, just return double int zero. */
|
||
|
||
static widest_int
|
||
backtrace_base_for_ref (tree *pbase)
|
||
{
|
||
tree base_in = *pbase;
|
||
slsr_cand_t base_cand;
|
||
|
||
STRIP_NOPS (base_in);
|
||
|
||
/* Strip off widening conversion(s) to handle cases where
|
||
e.g. 'B' is widened from an 'int' in order to calculate
|
||
a 64-bit address. */
|
||
if (CONVERT_EXPR_P (base_in)
|
||
&& legal_cast_p_1 (TREE_TYPE (base_in),
|
||
TREE_TYPE (TREE_OPERAND (base_in, 0))))
|
||
base_in = get_unwidened (base_in, NULL_TREE);
|
||
|
||
if (TREE_CODE (base_in) != SSA_NAME)
|
||
return 0;
|
||
|
||
base_cand = base_cand_from_table (base_in);
|
||
|
||
while (base_cand && base_cand->kind != CAND_PHI)
|
||
{
|
||
if (base_cand->kind == CAND_ADD
|
||
&& base_cand->index == 1
|
||
&& TREE_CODE (base_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* X = B + (1 * S), S is integer constant. */
|
||
*pbase = base_cand->base_expr;
|
||
return wi::to_widest (base_cand->stride);
|
||
}
|
||
else if (base_cand->kind == CAND_ADD
|
||
&& TREE_CODE (base_cand->stride) == INTEGER_CST
|
||
&& integer_onep (base_cand->stride))
|
||
{
|
||
/* X = B + (i * S), S is integer one. */
|
||
*pbase = base_cand->base_expr;
|
||
return base_cand->index;
|
||
}
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Look for the following pattern:
|
||
|
||
*PBASE: MEM_REF (T1, C1)
|
||
|
||
*POFFSET: MULT_EXPR (T2, C3) [C2 is zero]
|
||
or
|
||
MULT_EXPR (PLUS_EXPR (T2, C2), C3)
|
||
or
|
||
MULT_EXPR (MINUS_EXPR (T2, -C2), C3)
|
||
|
||
*PINDEX: C4 * BITS_PER_UNIT
|
||
|
||
If not present, leave the input values unchanged and return FALSE.
|
||
Otherwise, modify the input values as follows and return TRUE:
|
||
|
||
*PBASE: T1
|
||
*POFFSET: MULT_EXPR (T2, C3)
|
||
*PINDEX: C1 + (C2 * C3) + C4
|
||
|
||
When T2 is recorded by a CAND_ADD in the form of (T2' + C5), it
|
||
will be further restructured to:
|
||
|
||
*PBASE: T1
|
||
*POFFSET: MULT_EXPR (T2', C3)
|
||
*PINDEX: C1 + (C2 * C3) + C4 + (C5 * C3) */
|
||
|
||
static bool
|
||
restructure_reference (tree *pbase, tree *poffset, widest_int *pindex,
|
||
tree *ptype)
|
||
{
|
||
tree base = *pbase, offset = *poffset;
|
||
widest_int index = *pindex;
|
||
tree mult_op0, t1, t2, type;
|
||
widest_int c1, c2, c3, c4, c5;
|
||
offset_int mem_offset;
|
||
|
||
if (!base
|
||
|| !offset
|
||
|| TREE_CODE (base) != MEM_REF
|
||
|| !mem_ref_offset (base).is_constant (&mem_offset)
|
||
|| TREE_CODE (offset) != MULT_EXPR
|
||
|| TREE_CODE (TREE_OPERAND (offset, 1)) != INTEGER_CST
|
||
|| wi::umod_floor (index, BITS_PER_UNIT) != 0)
|
||
return false;
|
||
|
||
t1 = TREE_OPERAND (base, 0);
|
||
c1 = widest_int::from (mem_offset, SIGNED);
|
||
type = TREE_TYPE (TREE_OPERAND (base, 1));
|
||
|
||
mult_op0 = TREE_OPERAND (offset, 0);
|
||
c3 = wi::to_widest (TREE_OPERAND (offset, 1));
|
||
|
||
if (TREE_CODE (mult_op0) == PLUS_EXPR)
|
||
|
||
if (TREE_CODE (TREE_OPERAND (mult_op0, 1)) == INTEGER_CST)
|
||
{
|
||
t2 = TREE_OPERAND (mult_op0, 0);
|
||
c2 = wi::to_widest (TREE_OPERAND (mult_op0, 1));
|
||
}
|
||
else
|
||
return false;
|
||
|
||
else if (TREE_CODE (mult_op0) == MINUS_EXPR)
|
||
|
||
if (TREE_CODE (TREE_OPERAND (mult_op0, 1)) == INTEGER_CST)
|
||
{
|
||
t2 = TREE_OPERAND (mult_op0, 0);
|
||
c2 = -wi::to_widest (TREE_OPERAND (mult_op0, 1));
|
||
}
|
||
else
|
||
return false;
|
||
|
||
else
|
||
{
|
||
t2 = mult_op0;
|
||
c2 = 0;
|
||
}
|
||
|
||
c4 = index >> LOG2_BITS_PER_UNIT;
|
||
c5 = backtrace_base_for_ref (&t2);
|
||
|
||
*pbase = t1;
|
||
*poffset = fold_build2 (MULT_EXPR, sizetype, fold_convert (sizetype, t2),
|
||
wide_int_to_tree (sizetype, c3));
|
||
*pindex = c1 + c2 * c3 + c4 + c5 * c3;
|
||
*ptype = type;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Given GS which contains a data reference, create a CAND_REF entry in
|
||
the candidate table and attempt to find a basis. */
|
||
|
||
static void
|
||
slsr_process_ref (gimple *gs)
|
||
{
|
||
tree ref_expr, base, offset, type;
|
||
poly_int64 bitsize, bitpos;
|
||
machine_mode mode;
|
||
int unsignedp, reversep, volatilep;
|
||
slsr_cand_t c;
|
||
|
||
if (gimple_vdef (gs))
|
||
ref_expr = gimple_assign_lhs (gs);
|
||
else
|
||
ref_expr = gimple_assign_rhs1 (gs);
|
||
|
||
if (!handled_component_p (ref_expr)
|
||
|| TREE_CODE (ref_expr) == BIT_FIELD_REF
|
||
|| (TREE_CODE (ref_expr) == COMPONENT_REF
|
||
&& DECL_BIT_FIELD (TREE_OPERAND (ref_expr, 1))))
|
||
return;
|
||
|
||
base = get_inner_reference (ref_expr, &bitsize, &bitpos, &offset, &mode,
|
||
&unsignedp, &reversep, &volatilep);
|
||
HOST_WIDE_INT cbitpos;
|
||
if (reversep || !bitpos.is_constant (&cbitpos))
|
||
return;
|
||
widest_int index = cbitpos;
|
||
|
||
if (!restructure_reference (&base, &offset, &index, &type))
|
||
return;
|
||
|
||
c = alloc_cand_and_find_basis (CAND_REF, gs, base, index, offset,
|
||
type, sizetype, 0);
|
||
|
||
/* Add the candidate to the statement-candidate mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
|
||
/* Create a candidate entry for a statement GS, where GS multiplies
|
||
two SSA names BASE_IN and STRIDE_IN. Propagate any known information
|
||
about the two SSA names into the new candidate. Return the new
|
||
candidate. */
|
||
|
||
static slsr_cand_t
|
||
create_mul_ssa_cand (gimple *gs, tree base_in, tree stride_in, bool speed)
|
||
{
|
||
tree base = NULL_TREE, stride = NULL_TREE, ctype = NULL_TREE;
|
||
tree stype = NULL_TREE;
|
||
widest_int index;
|
||
unsigned savings = 0;
|
||
slsr_cand_t c;
|
||
slsr_cand_t base_cand = base_cand_from_table (base_in);
|
||
|
||
/* Look at all interpretations of the base candidate, if necessary,
|
||
to find information to propagate into this candidate. */
|
||
while (base_cand && !base && base_cand->kind != CAND_PHI)
|
||
{
|
||
|
||
if (base_cand->kind == CAND_MULT && integer_onep (base_cand->stride))
|
||
{
|
||
/* Y = (B + i') * 1
|
||
X = Y * Z
|
||
================
|
||
X = (B + i') * Z */
|
||
base = base_cand->base_expr;
|
||
index = base_cand->index;
|
||
stride = stride_in;
|
||
ctype = base_cand->cand_type;
|
||
stype = TREE_TYPE (stride_in);
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
else if (base_cand->kind == CAND_ADD
|
||
&& TREE_CODE (base_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* Y = B + (i' * S), S constant
|
||
X = Y * Z
|
||
============================
|
||
X = B + ((i' * S) * Z) */
|
||
base = base_cand->base_expr;
|
||
index = base_cand->index * wi::to_widest (base_cand->stride);
|
||
stride = stride_in;
|
||
ctype = base_cand->cand_type;
|
||
stype = TREE_TYPE (stride_in);
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
|
||
if (!base)
|
||
{
|
||
/* No interpretations had anything useful to propagate, so
|
||
produce X = (Y + 0) * Z. */
|
||
base = base_in;
|
||
index = 0;
|
||
stride = stride_in;
|
||
ctype = TREE_TYPE (base_in);
|
||
stype = TREE_TYPE (stride_in);
|
||
}
|
||
|
||
c = alloc_cand_and_find_basis (CAND_MULT, gs, base, index, stride,
|
||
ctype, stype, savings);
|
||
return c;
|
||
}
|
||
|
||
/* Create a candidate entry for a statement GS, where GS multiplies
|
||
SSA name BASE_IN by constant STRIDE_IN. Propagate any known
|
||
information about BASE_IN into the new candidate. Return the new
|
||
candidate. */
|
||
|
||
static slsr_cand_t
|
||
create_mul_imm_cand (gimple *gs, tree base_in, tree stride_in, bool speed)
|
||
{
|
||
tree base = NULL_TREE, stride = NULL_TREE, ctype = NULL_TREE;
|
||
widest_int index, temp;
|
||
unsigned savings = 0;
|
||
slsr_cand_t c;
|
||
slsr_cand_t base_cand = base_cand_from_table (base_in);
|
||
|
||
/* Look at all interpretations of the base candidate, if necessary,
|
||
to find information to propagate into this candidate. */
|
||
while (base_cand && !base && base_cand->kind != CAND_PHI)
|
||
{
|
||
if (base_cand->kind == CAND_MULT
|
||
&& TREE_CODE (base_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* Y = (B + i') * S, S constant
|
||
X = Y * c
|
||
============================
|
||
X = (B + i') * (S * c) */
|
||
temp = wi::to_widest (base_cand->stride) * wi::to_widest (stride_in);
|
||
if (wi::fits_to_tree_p (temp, TREE_TYPE (stride_in)))
|
||
{
|
||
base = base_cand->base_expr;
|
||
index = base_cand->index;
|
||
stride = wide_int_to_tree (TREE_TYPE (stride_in), temp);
|
||
ctype = base_cand->cand_type;
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
}
|
||
else if (base_cand->kind == CAND_ADD && integer_onep (base_cand->stride))
|
||
{
|
||
/* Y = B + (i' * 1)
|
||
X = Y * c
|
||
===========================
|
||
X = (B + i') * c */
|
||
base = base_cand->base_expr;
|
||
index = base_cand->index;
|
||
stride = stride_in;
|
||
ctype = base_cand->cand_type;
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
else if (base_cand->kind == CAND_ADD
|
||
&& base_cand->index == 1
|
||
&& TREE_CODE (base_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* Y = B + (1 * S), S constant
|
||
X = Y * c
|
||
===========================
|
||
X = (B + S) * c */
|
||
base = base_cand->base_expr;
|
||
index = wi::to_widest (base_cand->stride);
|
||
stride = stride_in;
|
||
ctype = base_cand->cand_type;
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
|
||
if (!base)
|
||
{
|
||
/* No interpretations had anything useful to propagate, so
|
||
produce X = (Y + 0) * c. */
|
||
base = base_in;
|
||
index = 0;
|
||
stride = stride_in;
|
||
ctype = TREE_TYPE (base_in);
|
||
}
|
||
|
||
c = alloc_cand_and_find_basis (CAND_MULT, gs, base, index, stride,
|
||
ctype, sizetype, savings);
|
||
return c;
|
||
}
|
||
|
||
/* Given GS which is a multiply of scalar integers, make an appropriate
|
||
entry in the candidate table. If this is a multiply of two SSA names,
|
||
create two CAND_MULT interpretations and attempt to find a basis for
|
||
each of them. Otherwise, create a single CAND_MULT and attempt to
|
||
find a basis. */
|
||
|
||
static void
|
||
slsr_process_mul (gimple *gs, tree rhs1, tree rhs2, bool speed)
|
||
{
|
||
slsr_cand_t c, c2;
|
||
|
||
/* If this is a multiply of an SSA name with itself, it is highly
|
||
unlikely that we will get a strength reduction opportunity, so
|
||
don't record it as a candidate. This simplifies the logic for
|
||
finding a basis, so if this is removed that must be considered. */
|
||
if (rhs1 == rhs2)
|
||
return;
|
||
|
||
if (TREE_CODE (rhs2) == SSA_NAME)
|
||
{
|
||
/* Record an interpretation of this statement in the candidate table
|
||
assuming RHS1 is the base expression and RHS2 is the stride. */
|
||
c = create_mul_ssa_cand (gs, rhs1, rhs2, speed);
|
||
|
||
/* Add the first interpretation to the statement-candidate mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
|
||
/* Record another interpretation of this statement assuming RHS1
|
||
is the stride and RHS2 is the base expression. */
|
||
c2 = create_mul_ssa_cand (gs, rhs2, rhs1, speed);
|
||
c->next_interp = c2->cand_num;
|
||
c2->first_interp = c->cand_num;
|
||
}
|
||
else if (TREE_CODE (rhs2) == INTEGER_CST && !integer_zerop (rhs2))
|
||
{
|
||
/* Record an interpretation for the multiply-immediate. */
|
||
c = create_mul_imm_cand (gs, rhs1, rhs2, speed);
|
||
|
||
/* Add the interpretation to the statement-candidate mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
}
|
||
|
||
/* Create a candidate entry for a statement GS, where GS adds two
|
||
SSA names BASE_IN and ADDEND_IN if SUBTRACT_P is false, and
|
||
subtracts ADDEND_IN from BASE_IN otherwise. Propagate any known
|
||
information about the two SSA names into the new candidate.
|
||
Return the new candidate. */
|
||
|
||
static slsr_cand_t
|
||
create_add_ssa_cand (gimple *gs, tree base_in, tree addend_in,
|
||
bool subtract_p, bool speed)
|
||
{
|
||
tree base = NULL_TREE, stride = NULL_TREE, ctype = NULL_TREE;
|
||
tree stype = NULL_TREE;
|
||
widest_int index;
|
||
unsigned savings = 0;
|
||
slsr_cand_t c;
|
||
slsr_cand_t base_cand = base_cand_from_table (base_in);
|
||
slsr_cand_t addend_cand = base_cand_from_table (addend_in);
|
||
|
||
/* The most useful transformation is a multiply-immediate feeding
|
||
an add or subtract. Look for that first. */
|
||
while (addend_cand && !base && addend_cand->kind != CAND_PHI)
|
||
{
|
||
if (addend_cand->kind == CAND_MULT
|
||
&& addend_cand->index == 0
|
||
&& TREE_CODE (addend_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* Z = (B + 0) * S, S constant
|
||
X = Y +/- Z
|
||
===========================
|
||
X = Y + ((+/-1 * S) * B) */
|
||
base = base_in;
|
||
index = wi::to_widest (addend_cand->stride);
|
||
if (subtract_p)
|
||
index = -index;
|
||
stride = addend_cand->base_expr;
|
||
ctype = TREE_TYPE (base_in);
|
||
stype = addend_cand->cand_type;
|
||
if (has_single_use (addend_in))
|
||
savings = (addend_cand->dead_savings
|
||
+ stmt_cost (addend_cand->cand_stmt, speed));
|
||
}
|
||
|
||
addend_cand = lookup_cand (addend_cand->next_interp);
|
||
}
|
||
|
||
while (base_cand && !base && base_cand->kind != CAND_PHI)
|
||
{
|
||
if (base_cand->kind == CAND_ADD
|
||
&& (base_cand->index == 0
|
||
|| operand_equal_p (base_cand->stride,
|
||
integer_zero_node, 0)))
|
||
{
|
||
/* Y = B + (i' * S), i' * S = 0
|
||
X = Y +/- Z
|
||
============================
|
||
X = B + (+/-1 * Z) */
|
||
base = base_cand->base_expr;
|
||
index = subtract_p ? -1 : 1;
|
||
stride = addend_in;
|
||
ctype = base_cand->cand_type;
|
||
stype = (TREE_CODE (addend_in) == INTEGER_CST ? sizetype
|
||
: TREE_TYPE (addend_in));
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
else if (subtract_p)
|
||
{
|
||
slsr_cand_t subtrahend_cand = base_cand_from_table (addend_in);
|
||
|
||
while (subtrahend_cand && !base && subtrahend_cand->kind != CAND_PHI)
|
||
{
|
||
if (subtrahend_cand->kind == CAND_MULT
|
||
&& subtrahend_cand->index == 0
|
||
&& TREE_CODE (subtrahend_cand->stride) == INTEGER_CST)
|
||
{
|
||
/* Z = (B + 0) * S, S constant
|
||
X = Y - Z
|
||
===========================
|
||
Value: X = Y + ((-1 * S) * B) */
|
||
base = base_in;
|
||
index = wi::to_widest (subtrahend_cand->stride);
|
||
index = -index;
|
||
stride = subtrahend_cand->base_expr;
|
||
ctype = TREE_TYPE (base_in);
|
||
stype = subtrahend_cand->cand_type;
|
||
if (has_single_use (addend_in))
|
||
savings = (subtrahend_cand->dead_savings
|
||
+ stmt_cost (subtrahend_cand->cand_stmt, speed));
|
||
}
|
||
|
||
subtrahend_cand = lookup_cand (subtrahend_cand->next_interp);
|
||
}
|
||
}
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
|
||
if (!base)
|
||
{
|
||
/* No interpretations had anything useful to propagate, so
|
||
produce X = Y + (1 * Z). */
|
||
base = base_in;
|
||
index = subtract_p ? -1 : 1;
|
||
stride = addend_in;
|
||
ctype = TREE_TYPE (base_in);
|
||
stype = (TREE_CODE (addend_in) == INTEGER_CST ? sizetype
|
||
: TREE_TYPE (addend_in));
|
||
}
|
||
|
||
c = alloc_cand_and_find_basis (CAND_ADD, gs, base, index, stride,
|
||
ctype, stype, savings);
|
||
return c;
|
||
}
|
||
|
||
/* Create a candidate entry for a statement GS, where GS adds SSA
|
||
name BASE_IN to constant INDEX_IN. Propagate any known information
|
||
about BASE_IN into the new candidate. Return the new candidate. */
|
||
|
||
static slsr_cand_t
|
||
create_add_imm_cand (gimple *gs, tree base_in, const widest_int &index_in,
|
||
bool speed)
|
||
{
|
||
enum cand_kind kind = CAND_ADD;
|
||
tree base = NULL_TREE, stride = NULL_TREE, ctype = NULL_TREE;
|
||
tree stype = NULL_TREE;
|
||
widest_int index, multiple;
|
||
unsigned savings = 0;
|
||
slsr_cand_t c;
|
||
slsr_cand_t base_cand = base_cand_from_table (base_in);
|
||
|
||
while (base_cand && !base && base_cand->kind != CAND_PHI)
|
||
{
|
||
signop sign = TYPE_SIGN (TREE_TYPE (base_cand->stride));
|
||
|
||
if (TREE_CODE (base_cand->stride) == INTEGER_CST
|
||
&& wi::multiple_of_p (index_in, wi::to_widest (base_cand->stride),
|
||
sign, &multiple))
|
||
{
|
||
/* Y = (B + i') * S, S constant, c = kS for some integer k
|
||
X = Y + c
|
||
============================
|
||
X = (B + (i'+ k)) * S
|
||
OR
|
||
Y = B + (i' * S), S constant, c = kS for some integer k
|
||
X = Y + c
|
||
============================
|
||
X = (B + (i'+ k)) * S */
|
||
kind = base_cand->kind;
|
||
base = base_cand->base_expr;
|
||
index = base_cand->index + multiple;
|
||
stride = base_cand->stride;
|
||
ctype = base_cand->cand_type;
|
||
stype = base_cand->stride_type;
|
||
if (has_single_use (base_in))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
}
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
|
||
if (!base)
|
||
{
|
||
/* No interpretations had anything useful to propagate, so
|
||
produce X = Y + (c * 1). */
|
||
kind = CAND_ADD;
|
||
base = base_in;
|
||
index = index_in;
|
||
stride = integer_one_node;
|
||
ctype = TREE_TYPE (base_in);
|
||
stype = sizetype;
|
||
}
|
||
|
||
c = alloc_cand_and_find_basis (kind, gs, base, index, stride,
|
||
ctype, stype, savings);
|
||
return c;
|
||
}
|
||
|
||
/* Given GS which is an add or subtract of scalar integers or pointers,
|
||
make at least one appropriate entry in the candidate table. */
|
||
|
||
static void
|
||
slsr_process_add (gimple *gs, tree rhs1, tree rhs2, bool speed)
|
||
{
|
||
bool subtract_p = gimple_assign_rhs_code (gs) == MINUS_EXPR;
|
||
slsr_cand_t c = NULL, c2;
|
||
|
||
if (TREE_CODE (rhs2) == SSA_NAME)
|
||
{
|
||
/* First record an interpretation assuming RHS1 is the base expression
|
||
and RHS2 is the stride. But it doesn't make sense for the
|
||
stride to be a pointer, so don't record a candidate in that case. */
|
||
if (!POINTER_TYPE_P (TREE_TYPE (rhs2)))
|
||
{
|
||
c = create_add_ssa_cand (gs, rhs1, rhs2, subtract_p, speed);
|
||
|
||
/* Add the first interpretation to the statement-candidate
|
||
mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
|
||
/* If the two RHS operands are identical, or this is a subtract,
|
||
we're done. */
|
||
if (operand_equal_p (rhs1, rhs2, 0) || subtract_p)
|
||
return;
|
||
|
||
/* Otherwise, record another interpretation assuming RHS2 is the
|
||
base expression and RHS1 is the stride, again provided that the
|
||
stride is not a pointer. */
|
||
if (!POINTER_TYPE_P (TREE_TYPE (rhs1)))
|
||
{
|
||
c2 = create_add_ssa_cand (gs, rhs2, rhs1, false, speed);
|
||
if (c)
|
||
{
|
||
c->next_interp = c2->cand_num;
|
||
c2->first_interp = c->cand_num;
|
||
}
|
||
else
|
||
add_cand_for_stmt (gs, c2);
|
||
}
|
||
}
|
||
else if (TREE_CODE (rhs2) == INTEGER_CST)
|
||
{
|
||
/* Record an interpretation for the add-immediate. */
|
||
widest_int index = wi::to_widest (rhs2);
|
||
if (subtract_p)
|
||
index = -index;
|
||
|
||
c = create_add_imm_cand (gs, rhs1, index, speed);
|
||
|
||
/* Add the interpretation to the statement-candidate mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
}
|
||
|
||
/* Given GS which is a negate of a scalar integer, make an appropriate
|
||
entry in the candidate table. A negate is equivalent to a multiply
|
||
by -1. */
|
||
|
||
static void
|
||
slsr_process_neg (gimple *gs, tree rhs1, bool speed)
|
||
{
|
||
/* Record a CAND_MULT interpretation for the multiply by -1. */
|
||
slsr_cand_t c = create_mul_imm_cand (gs, rhs1, integer_minus_one_node, speed);
|
||
|
||
/* Add the interpretation to the statement-candidate mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
|
||
/* Help function for legal_cast_p, operating on two trees. Checks
|
||
whether it's allowable to cast from RHS to LHS. See legal_cast_p
|
||
for more details. */
|
||
|
||
static bool
|
||
legal_cast_p_1 (tree lhs_type, tree rhs_type)
|
||
{
|
||
unsigned lhs_size, rhs_size;
|
||
bool lhs_wraps, rhs_wraps;
|
||
|
||
lhs_size = TYPE_PRECISION (lhs_type);
|
||
rhs_size = TYPE_PRECISION (rhs_type);
|
||
lhs_wraps = ANY_INTEGRAL_TYPE_P (lhs_type) && TYPE_OVERFLOW_WRAPS (lhs_type);
|
||
rhs_wraps = ANY_INTEGRAL_TYPE_P (rhs_type) && TYPE_OVERFLOW_WRAPS (rhs_type);
|
||
|
||
if (lhs_size < rhs_size
|
||
|| (rhs_wraps && !lhs_wraps)
|
||
|| (rhs_wraps && lhs_wraps && rhs_size != lhs_size))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Return TRUE if GS is a statement that defines an SSA name from
|
||
a conversion and is legal for us to combine with an add and multiply
|
||
in the candidate table. For example, suppose we have:
|
||
|
||
A = B + i;
|
||
C = (type) A;
|
||
D = C * S;
|
||
|
||
Without the type-cast, we would create a CAND_MULT for D with base B,
|
||
index i, and stride S. We want to record this candidate only if it
|
||
is equivalent to apply the type cast following the multiply:
|
||
|
||
A = B + i;
|
||
E = A * S;
|
||
D = (type) E;
|
||
|
||
We will record the type with the candidate for D. This allows us
|
||
to use a similar previous candidate as a basis. If we have earlier seen
|
||
|
||
A' = B + i';
|
||
C' = (type) A';
|
||
D' = C' * S;
|
||
|
||
we can replace D with
|
||
|
||
D = D' + (i - i') * S;
|
||
|
||
But if moving the type-cast would change semantics, we mustn't do this.
|
||
|
||
This is legitimate for casts from a non-wrapping integral type to
|
||
any integral type of the same or larger size. It is not legitimate
|
||
to convert a wrapping type to a non-wrapping type, or to a wrapping
|
||
type of a different size. I.e., with a wrapping type, we must
|
||
assume that the addition B + i could wrap, in which case performing
|
||
the multiply before or after one of the "illegal" type casts will
|
||
have different semantics. */
|
||
|
||
static bool
|
||
legal_cast_p (gimple *gs, tree rhs)
|
||
{
|
||
if (!is_gimple_assign (gs)
|
||
|| !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (gs)))
|
||
return false;
|
||
|
||
return legal_cast_p_1 (TREE_TYPE (gimple_assign_lhs (gs)), TREE_TYPE (rhs));
|
||
}
|
||
|
||
/* Given GS which is a cast to a scalar integer type, determine whether
|
||
the cast is legal for strength reduction. If so, make at least one
|
||
appropriate entry in the candidate table. */
|
||
|
||
static void
|
||
slsr_process_cast (gimple *gs, tree rhs1, bool speed)
|
||
{
|
||
tree lhs, ctype;
|
||
slsr_cand_t base_cand, c = NULL, c2;
|
||
unsigned savings = 0;
|
||
|
||
if (!legal_cast_p (gs, rhs1))
|
||
return;
|
||
|
||
lhs = gimple_assign_lhs (gs);
|
||
base_cand = base_cand_from_table (rhs1);
|
||
ctype = TREE_TYPE (lhs);
|
||
|
||
if (base_cand && base_cand->kind != CAND_PHI)
|
||
{
|
||
slsr_cand_t first_cand = NULL;
|
||
|
||
while (base_cand)
|
||
{
|
||
/* Propagate all data from the base candidate except the type,
|
||
which comes from the cast, and the base candidate's cast,
|
||
which is no longer applicable. */
|
||
if (has_single_use (rhs1))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
|
||
c = alloc_cand_and_find_basis (base_cand->kind, gs,
|
||
base_cand->base_expr,
|
||
base_cand->index, base_cand->stride,
|
||
ctype, base_cand->stride_type,
|
||
savings);
|
||
if (!first_cand)
|
||
first_cand = c;
|
||
|
||
if (first_cand != c)
|
||
c->first_interp = first_cand->cand_num;
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* If nothing is known about the RHS, create fresh CAND_ADD and
|
||
CAND_MULT interpretations:
|
||
|
||
X = Y + (0 * 1)
|
||
X = (Y + 0) * 1
|
||
|
||
The first of these is somewhat arbitrary, but the choice of
|
||
1 for the stride simplifies the logic for propagating casts
|
||
into their uses. */
|
||
c = alloc_cand_and_find_basis (CAND_ADD, gs, rhs1, 0,
|
||
integer_one_node, ctype, sizetype, 0);
|
||
c2 = alloc_cand_and_find_basis (CAND_MULT, gs, rhs1, 0,
|
||
integer_one_node, ctype, sizetype, 0);
|
||
c->next_interp = c2->cand_num;
|
||
c2->first_interp = c->cand_num;
|
||
}
|
||
|
||
/* Add the first (or only) interpretation to the statement-candidate
|
||
mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
|
||
/* Given GS which is a copy of a scalar integer type, make at least one
|
||
appropriate entry in the candidate table.
|
||
|
||
This interface is included for completeness, but is unnecessary
|
||
if this pass immediately follows a pass that performs copy
|
||
propagation, such as DOM. */
|
||
|
||
static void
|
||
slsr_process_copy (gimple *gs, tree rhs1, bool speed)
|
||
{
|
||
slsr_cand_t base_cand, c = NULL, c2;
|
||
unsigned savings = 0;
|
||
|
||
base_cand = base_cand_from_table (rhs1);
|
||
|
||
if (base_cand && base_cand->kind != CAND_PHI)
|
||
{
|
||
slsr_cand_t first_cand = NULL;
|
||
|
||
while (base_cand)
|
||
{
|
||
/* Propagate all data from the base candidate. */
|
||
if (has_single_use (rhs1))
|
||
savings = (base_cand->dead_savings
|
||
+ stmt_cost (base_cand->cand_stmt, speed));
|
||
|
||
c = alloc_cand_and_find_basis (base_cand->kind, gs,
|
||
base_cand->base_expr,
|
||
base_cand->index, base_cand->stride,
|
||
base_cand->cand_type,
|
||
base_cand->stride_type, savings);
|
||
if (!first_cand)
|
||
first_cand = c;
|
||
|
||
if (first_cand != c)
|
||
c->first_interp = first_cand->cand_num;
|
||
|
||
base_cand = lookup_cand (base_cand->next_interp);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* If nothing is known about the RHS, create fresh CAND_ADD and
|
||
CAND_MULT interpretations:
|
||
|
||
X = Y + (0 * 1)
|
||
X = (Y + 0) * 1
|
||
|
||
The first of these is somewhat arbitrary, but the choice of
|
||
1 for the stride simplifies the logic for propagating casts
|
||
into their uses. */
|
||
c = alloc_cand_and_find_basis (CAND_ADD, gs, rhs1, 0,
|
||
integer_one_node, TREE_TYPE (rhs1),
|
||
sizetype, 0);
|
||
c2 = alloc_cand_and_find_basis (CAND_MULT, gs, rhs1, 0,
|
||
integer_one_node, TREE_TYPE (rhs1),
|
||
sizetype, 0);
|
||
c->next_interp = c2->cand_num;
|
||
c2->first_interp = c->cand_num;
|
||
}
|
||
|
||
/* Add the first (or only) interpretation to the statement-candidate
|
||
mapping. */
|
||
add_cand_for_stmt (gs, c);
|
||
}
|
||
|
||
class find_candidates_dom_walker : public dom_walker
|
||
{
|
||
public:
|
||
find_candidates_dom_walker (cdi_direction direction)
|
||
: dom_walker (direction) {}
|
||
virtual edge before_dom_children (basic_block);
|
||
};
|
||
|
||
/* Find strength-reduction candidates in block BB. */
|
||
|
||
edge
|
||
find_candidates_dom_walker::before_dom_children (basic_block bb)
|
||
{
|
||
bool speed = optimize_bb_for_speed_p (bb);
|
||
|
||
for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
|
||
gsi_next (&gsi))
|
||
slsr_process_phi (gsi.phi (), speed);
|
||
|
||
for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
|
||
gsi_next (&gsi))
|
||
{
|
||
gimple *gs = gsi_stmt (gsi);
|
||
|
||
if (stmt_could_throw_p (cfun, gs))
|
||
continue;
|
||
|
||
if (gimple_vuse (gs) && gimple_assign_single_p (gs))
|
||
slsr_process_ref (gs);
|
||
|
||
else if (is_gimple_assign (gs)
|
||
&& (INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (gs)))
|
||
|| POINTER_TYPE_P (TREE_TYPE (gimple_assign_lhs (gs)))))
|
||
{
|
||
tree rhs1 = NULL_TREE, rhs2 = NULL_TREE;
|
||
|
||
switch (gimple_assign_rhs_code (gs))
|
||
{
|
||
case MULT_EXPR:
|
||
case PLUS_EXPR:
|
||
rhs1 = gimple_assign_rhs1 (gs);
|
||
rhs2 = gimple_assign_rhs2 (gs);
|
||
/* Should never happen, but currently some buggy situations
|
||
in earlier phases put constants in rhs1. */
|
||
if (TREE_CODE (rhs1) != SSA_NAME)
|
||
continue;
|
||
break;
|
||
|
||
/* Possible future opportunity: rhs1 of a ptr+ can be
|
||
an ADDR_EXPR. */
|
||
case POINTER_PLUS_EXPR:
|
||
case MINUS_EXPR:
|
||
rhs2 = gimple_assign_rhs2 (gs);
|
||
gcc_fallthrough ();
|
||
|
||
CASE_CONVERT:
|
||
case SSA_NAME:
|
||
case NEGATE_EXPR:
|
||
rhs1 = gimple_assign_rhs1 (gs);
|
||
if (TREE_CODE (rhs1) != SSA_NAME)
|
||
continue;
|
||
break;
|
||
|
||
default:
|
||
;
|
||
}
|
||
|
||
switch (gimple_assign_rhs_code (gs))
|
||
{
|
||
case MULT_EXPR:
|
||
slsr_process_mul (gs, rhs1, rhs2, speed);
|
||
break;
|
||
|
||
case PLUS_EXPR:
|
||
case POINTER_PLUS_EXPR:
|
||
case MINUS_EXPR:
|
||
slsr_process_add (gs, rhs1, rhs2, speed);
|
||
break;
|
||
|
||
case NEGATE_EXPR:
|
||
slsr_process_neg (gs, rhs1, speed);
|
||
break;
|
||
|
||
CASE_CONVERT:
|
||
slsr_process_cast (gs, rhs1, speed);
|
||
break;
|
||
|
||
case SSA_NAME:
|
||
slsr_process_copy (gs, rhs1, speed);
|
||
break;
|
||
|
||
default:
|
||
;
|
||
}
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/* Dump a candidate for debug. */
|
||
|
||
static void
|
||
dump_candidate (slsr_cand_t c)
|
||
{
|
||
fprintf (dump_file, "%3d [%d] ", c->cand_num,
|
||
gimple_bb (c->cand_stmt)->index);
|
||
print_gimple_stmt (dump_file, c->cand_stmt, 0);
|
||
switch (c->kind)
|
||
{
|
||
case CAND_MULT:
|
||
fputs (" MULT : (", dump_file);
|
||
print_generic_expr (dump_file, c->base_expr);
|
||
fputs (" + ", dump_file);
|
||
print_decs (c->index, dump_file);
|
||
fputs (") * ", dump_file);
|
||
if (TREE_CODE (c->stride) != INTEGER_CST
|
||
&& c->stride_type != TREE_TYPE (c->stride))
|
||
{
|
||
fputs ("(", dump_file);
|
||
print_generic_expr (dump_file, c->stride_type);
|
||
fputs (")", dump_file);
|
||
}
|
||
print_generic_expr (dump_file, c->stride);
|
||
fputs (" : ", dump_file);
|
||
break;
|
||
case CAND_ADD:
|
||
fputs (" ADD : ", dump_file);
|
||
print_generic_expr (dump_file, c->base_expr);
|
||
fputs (" + (", dump_file);
|
||
print_decs (c->index, dump_file);
|
||
fputs (" * ", dump_file);
|
||
if (TREE_CODE (c->stride) != INTEGER_CST
|
||
&& c->stride_type != TREE_TYPE (c->stride))
|
||
{
|
||
fputs ("(", dump_file);
|
||
print_generic_expr (dump_file, c->stride_type);
|
||
fputs (")", dump_file);
|
||
}
|
||
print_generic_expr (dump_file, c->stride);
|
||
fputs (") : ", dump_file);
|
||
break;
|
||
case CAND_REF:
|
||
fputs (" REF : ", dump_file);
|
||
print_generic_expr (dump_file, c->base_expr);
|
||
fputs (" + (", dump_file);
|
||
print_generic_expr (dump_file, c->stride);
|
||
fputs (") + ", dump_file);
|
||
print_decs (c->index, dump_file);
|
||
fputs (" : ", dump_file);
|
||
break;
|
||
case CAND_PHI:
|
||
fputs (" PHI : ", dump_file);
|
||
print_generic_expr (dump_file, c->base_expr);
|
||
fputs (" + (unknown * ", dump_file);
|
||
print_generic_expr (dump_file, c->stride);
|
||
fputs (") : ", dump_file);
|
||
break;
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
print_generic_expr (dump_file, c->cand_type);
|
||
fprintf (dump_file, "\n basis: %d dependent: %d sibling: %d\n",
|
||
c->basis, c->dependent, c->sibling);
|
||
fprintf (dump_file,
|
||
" next-interp: %d first-interp: %d dead-savings: %d\n",
|
||
c->next_interp, c->first_interp, c->dead_savings);
|
||
if (c->def_phi)
|
||
fprintf (dump_file, " phi: %d\n", c->def_phi);
|
||
fputs ("\n", dump_file);
|
||
}
|
||
|
||
/* Dump the candidate vector for debug. */
|
||
|
||
static void
|
||
dump_cand_vec (void)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t c;
|
||
|
||
fprintf (dump_file, "\nStrength reduction candidate vector:\n\n");
|
||
|
||
FOR_EACH_VEC_ELT (cand_vec, i, c)
|
||
if (c != NULL)
|
||
dump_candidate (c);
|
||
}
|
||
|
||
/* Callback used to dump the candidate chains hash table. */
|
||
|
||
int
|
||
ssa_base_cand_dump_callback (cand_chain **slot, void *ignored ATTRIBUTE_UNUSED)
|
||
{
|
||
const_cand_chain_t chain = *slot;
|
||
cand_chain_t p;
|
||
|
||
print_generic_expr (dump_file, chain->base_expr);
|
||
fprintf (dump_file, " -> %d", chain->cand->cand_num);
|
||
|
||
for (p = chain->next; p; p = p->next)
|
||
fprintf (dump_file, " -> %d", p->cand->cand_num);
|
||
|
||
fputs ("\n", dump_file);
|
||
return 1;
|
||
}
|
||
|
||
/* Dump the candidate chains. */
|
||
|
||
static void
|
||
dump_cand_chains (void)
|
||
{
|
||
fprintf (dump_file, "\nStrength reduction candidate chains:\n\n");
|
||
base_cand_map->traverse_noresize <void *, ssa_base_cand_dump_callback>
|
||
(NULL);
|
||
fputs ("\n", dump_file);
|
||
}
|
||
|
||
/* Dump the increment vector for debug. */
|
||
|
||
static void
|
||
dump_incr_vec (void)
|
||
{
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
unsigned i;
|
||
|
||
fprintf (dump_file, "\nIncrement vector:\n\n");
|
||
|
||
for (i = 0; i < incr_vec_len; i++)
|
||
{
|
||
fprintf (dump_file, "%3d increment: ", i);
|
||
print_decs (incr_vec[i].incr, dump_file);
|
||
fprintf (dump_file, "\n count: %d", incr_vec[i].count);
|
||
fprintf (dump_file, "\n cost: %d", incr_vec[i].cost);
|
||
fputs ("\n initializer: ", dump_file);
|
||
print_generic_expr (dump_file, incr_vec[i].initializer);
|
||
fputs ("\n\n", dump_file);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Replace *EXPR in candidate C with an equivalent strength-reduced
|
||
data reference. */
|
||
|
||
static void
|
||
replace_ref (tree *expr, slsr_cand_t c)
|
||
{
|
||
tree add_expr, mem_ref, acc_type = TREE_TYPE (*expr);
|
||
unsigned HOST_WIDE_INT misalign;
|
||
unsigned align;
|
||
|
||
/* Ensure the memory reference carries the minimum alignment
|
||
requirement for the data type. See PR58041. */
|
||
get_object_alignment_1 (*expr, &align, &misalign);
|
||
if (misalign != 0)
|
||
align = least_bit_hwi (misalign);
|
||
if (align < TYPE_ALIGN (acc_type))
|
||
acc_type = build_aligned_type (acc_type, align);
|
||
|
||
add_expr = fold_build2 (POINTER_PLUS_EXPR, c->cand_type,
|
||
c->base_expr, c->stride);
|
||
mem_ref = fold_build2 (MEM_REF, acc_type, add_expr,
|
||
wide_int_to_tree (c->cand_type, c->index));
|
||
|
||
/* Gimplify the base addressing expression for the new MEM_REF tree. */
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
TREE_OPERAND (mem_ref, 0)
|
||
= force_gimple_operand_gsi (&gsi, TREE_OPERAND (mem_ref, 0),
|
||
/*simple_p=*/true, NULL,
|
||
/*before=*/true, GSI_SAME_STMT);
|
||
copy_ref_info (mem_ref, *expr);
|
||
*expr = mem_ref;
|
||
update_stmt (c->cand_stmt);
|
||
}
|
||
|
||
/* Return true if CAND_REF candidate C is a valid memory reference. */
|
||
|
||
static bool
|
||
valid_mem_ref_cand_p (slsr_cand_t c)
|
||
{
|
||
if (TREE_CODE (TREE_OPERAND (c->stride, 1)) != INTEGER_CST)
|
||
return false;
|
||
|
||
struct mem_address addr
|
||
= { NULL_TREE, c->base_expr, TREE_OPERAND (c->stride, 0),
|
||
TREE_OPERAND (c->stride, 1), wide_int_to_tree (sizetype, c->index) };
|
||
|
||
return
|
||
valid_mem_ref_p (TYPE_MODE (c->cand_type), TYPE_ADDR_SPACE (c->cand_type),
|
||
&addr);
|
||
}
|
||
|
||
/* Replace CAND_REF candidate C, each sibling of candidate C, and each
|
||
dependent of candidate C with an equivalent strength-reduced data
|
||
reference. */
|
||
|
||
static void
|
||
replace_refs (slsr_cand_t c)
|
||
{
|
||
/* Replacing a chain of only 2 candidates which are valid memory references
|
||
is generally counter-productive because you cannot recoup the additional
|
||
calculation added in front of them. */
|
||
if (c->basis == 0
|
||
&& c->dependent
|
||
&& !lookup_cand (c->dependent)->dependent
|
||
&& valid_mem_ref_cand_p (c)
|
||
&& valid_mem_ref_cand_p (lookup_cand (c->dependent)))
|
||
return;
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("Replacing reference: ", dump_file);
|
||
print_gimple_stmt (dump_file, c->cand_stmt, 0);
|
||
}
|
||
|
||
if (gimple_vdef (c->cand_stmt))
|
||
{
|
||
tree *lhs = gimple_assign_lhs_ptr (c->cand_stmt);
|
||
replace_ref (lhs, c);
|
||
}
|
||
else
|
||
{
|
||
tree *rhs = gimple_assign_rhs1_ptr (c->cand_stmt);
|
||
replace_ref (rhs, c);
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("With: ", dump_file);
|
||
print_gimple_stmt (dump_file, c->cand_stmt, 0);
|
||
fputs ("\n", dump_file);
|
||
}
|
||
|
||
if (c->sibling)
|
||
replace_refs (lookup_cand (c->sibling));
|
||
|
||
if (c->dependent)
|
||
replace_refs (lookup_cand (c->dependent));
|
||
}
|
||
|
||
/* Return TRUE if candidate C is dependent upon a PHI. */
|
||
|
||
static bool
|
||
phi_dependent_cand_p (slsr_cand_t c)
|
||
{
|
||
/* A candidate is not necessarily dependent upon a PHI just because
|
||
it has a phi definition for its base name. It may have a basis
|
||
that relies upon the same phi definition, in which case the PHI
|
||
is irrelevant to this candidate. */
|
||
return (c->def_phi
|
||
&& c->basis
|
||
&& lookup_cand (c->basis)->def_phi != c->def_phi);
|
||
}
|
||
|
||
/* Calculate the increment required for candidate C relative to
|
||
its basis. */
|
||
|
||
static widest_int
|
||
cand_increment (slsr_cand_t c)
|
||
{
|
||
slsr_cand_t basis;
|
||
|
||
/* If the candidate doesn't have a basis, just return its own
|
||
index. This is useful in record_increments to help us find
|
||
an existing initializer. Also, if the candidate's basis is
|
||
hidden by a phi, then its own index will be the increment
|
||
from the newly introduced phi basis. */
|
||
if (!c->basis || phi_dependent_cand_p (c))
|
||
return c->index;
|
||
|
||
basis = lookup_cand (c->basis);
|
||
gcc_assert (operand_equal_p (c->base_expr, basis->base_expr, 0));
|
||
return c->index - basis->index;
|
||
}
|
||
|
||
/* Calculate the increment required for candidate C relative to
|
||
its basis. If we aren't going to generate pointer arithmetic
|
||
for this candidate, return the absolute value of that increment
|
||
instead. */
|
||
|
||
static inline widest_int
|
||
cand_abs_increment (slsr_cand_t c)
|
||
{
|
||
widest_int increment = cand_increment (c);
|
||
|
||
if (!address_arithmetic_p && wi::neg_p (increment))
|
||
increment = -increment;
|
||
|
||
return increment;
|
||
}
|
||
|
||
/* Return TRUE iff candidate C has already been replaced under
|
||
another interpretation. */
|
||
|
||
static inline bool
|
||
cand_already_replaced (slsr_cand_t c)
|
||
{
|
||
return (gimple_bb (c->cand_stmt) == 0);
|
||
}
|
||
|
||
/* Common logic used by replace_unconditional_candidate and
|
||
replace_conditional_candidate. */
|
||
|
||
static void
|
||
replace_mult_candidate (slsr_cand_t c, tree basis_name, widest_int bump)
|
||
{
|
||
tree target_type = TREE_TYPE (gimple_assign_lhs (c->cand_stmt));
|
||
enum tree_code cand_code = gimple_assign_rhs_code (c->cand_stmt);
|
||
|
||
/* It is not useful to replace casts, copies, negates, or adds of
|
||
an SSA name and a constant. */
|
||
if (cand_code == SSA_NAME
|
||
|| CONVERT_EXPR_CODE_P (cand_code)
|
||
|| cand_code == PLUS_EXPR
|
||
|| cand_code == POINTER_PLUS_EXPR
|
||
|| cand_code == MINUS_EXPR
|
||
|| cand_code == NEGATE_EXPR)
|
||
return;
|
||
|
||
enum tree_code code = PLUS_EXPR;
|
||
tree bump_tree;
|
||
gimple *stmt_to_print = NULL;
|
||
|
||
if (wi::neg_p (bump))
|
||
{
|
||
code = MINUS_EXPR;
|
||
bump = -bump;
|
||
}
|
||
|
||
/* It is possible that the resulting bump doesn't fit in target_type.
|
||
Abandon the replacement in this case. This does not affect
|
||
siblings or dependents of C. */
|
||
if (bump != wi::ext (bump, TYPE_PRECISION (target_type),
|
||
TYPE_SIGN (target_type)))
|
||
return;
|
||
|
||
bump_tree = wide_int_to_tree (target_type, bump);
|
||
|
||
/* If the basis name and the candidate's LHS have incompatible types,
|
||
introduce a cast. */
|
||
if (!useless_type_conversion_p (target_type, TREE_TYPE (basis_name)))
|
||
basis_name = introduce_cast_before_cand (c, target_type, basis_name);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("Replacing: ", dump_file);
|
||
print_gimple_stmt (dump_file, c->cand_stmt, 0);
|
||
}
|
||
|
||
if (bump == 0)
|
||
{
|
||
tree lhs = gimple_assign_lhs (c->cand_stmt);
|
||
gassign *copy_stmt = gimple_build_assign (lhs, basis_name);
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_set_location (copy_stmt, gimple_location (c->cand_stmt));
|
||
gsi_replace (&gsi, copy_stmt, false);
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = copy_stmt;
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
stmt_to_print = copy_stmt;
|
||
}
|
||
else
|
||
{
|
||
tree rhs1, rhs2;
|
||
if (cand_code != NEGATE_EXPR) {
|
||
rhs1 = gimple_assign_rhs1 (c->cand_stmt);
|
||
rhs2 = gimple_assign_rhs2 (c->cand_stmt);
|
||
}
|
||
if (cand_code != NEGATE_EXPR
|
||
&& ((operand_equal_p (rhs1, basis_name, 0)
|
||
&& operand_equal_p (rhs2, bump_tree, 0))
|
||
|| (operand_equal_p (rhs1, bump_tree, 0)
|
||
&& operand_equal_p (rhs2, basis_name, 0))))
|
||
{
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("(duplicate, not actually replacing)", dump_file);
|
||
stmt_to_print = c->cand_stmt;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_assign_set_rhs_with_ops (&gsi, code, basis_name, bump_tree);
|
||
update_stmt (gsi_stmt (gsi));
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = gsi_stmt (gsi);
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
stmt_to_print = gsi_stmt (gsi);
|
||
}
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("With: ", dump_file);
|
||
print_gimple_stmt (dump_file, stmt_to_print, 0);
|
||
fputs ("\n", dump_file);
|
||
}
|
||
}
|
||
|
||
/* Replace candidate C with an add or subtract. Note that we only
|
||
operate on CAND_MULTs with known strides, so we will never generate
|
||
a POINTER_PLUS_EXPR. Each candidate X = (B + i) * S is replaced by
|
||
X = Y + ((i - i') * S), as described in the module commentary. The
|
||
folded value ((i - i') * S) is referred to here as the "bump." */
|
||
|
||
static void
|
||
replace_unconditional_candidate (slsr_cand_t c)
|
||
{
|
||
slsr_cand_t basis;
|
||
|
||
if (cand_already_replaced (c))
|
||
return;
|
||
|
||
basis = lookup_cand (c->basis);
|
||
widest_int bump = cand_increment (c) * wi::to_widest (c->stride);
|
||
|
||
replace_mult_candidate (c, gimple_assign_lhs (basis->cand_stmt), bump);
|
||
}
|
||
|
||
/* Return the index in the increment vector of the given INCREMENT,
|
||
or -1 if not found. The latter can occur if more than
|
||
MAX_INCR_VEC_LEN increments have been found. */
|
||
|
||
static inline int
|
||
incr_vec_index (const widest_int &increment)
|
||
{
|
||
unsigned i;
|
||
|
||
for (i = 0; i < incr_vec_len && increment != incr_vec[i].incr; i++)
|
||
;
|
||
|
||
if (i < incr_vec_len)
|
||
return i;
|
||
else
|
||
return -1;
|
||
}
|
||
|
||
/* Create a new statement along edge E to add BASIS_NAME to the product
|
||
of INCREMENT and the stride of candidate C. Create and return a new
|
||
SSA name from *VAR to be used as the LHS of the new statement.
|
||
KNOWN_STRIDE is true iff C's stride is a constant. */
|
||
|
||
static tree
|
||
create_add_on_incoming_edge (slsr_cand_t c, tree basis_name,
|
||
widest_int increment, edge e, location_t loc,
|
||
bool known_stride)
|
||
{
|
||
tree lhs, basis_type;
|
||
gassign *new_stmt, *cast_stmt = NULL;
|
||
|
||
/* If the add candidate along this incoming edge has the same
|
||
index as C's hidden basis, the hidden basis represents this
|
||
edge correctly. */
|
||
if (increment == 0)
|
||
return basis_name;
|
||
|
||
basis_type = TREE_TYPE (basis_name);
|
||
lhs = make_temp_ssa_name (basis_type, NULL, "slsr");
|
||
|
||
/* Occasionally people convert integers to pointers without a
|
||
cast, leading us into trouble if we aren't careful. */
|
||
enum tree_code plus_code
|
||
= POINTER_TYPE_P (basis_type) ? POINTER_PLUS_EXPR : PLUS_EXPR;
|
||
|
||
if (known_stride)
|
||
{
|
||
tree bump_tree;
|
||
enum tree_code code = plus_code;
|
||
widest_int bump = increment * wi::to_widest (c->stride);
|
||
if (wi::neg_p (bump) && !POINTER_TYPE_P (basis_type))
|
||
{
|
||
code = MINUS_EXPR;
|
||
bump = -bump;
|
||
}
|
||
|
||
tree stride_type = POINTER_TYPE_P (basis_type) ? sizetype : basis_type;
|
||
bump_tree = wide_int_to_tree (stride_type, bump);
|
||
new_stmt = gimple_build_assign (lhs, code, basis_name, bump_tree);
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
bool negate_incr = !POINTER_TYPE_P (basis_type) && wi::neg_p (increment);
|
||
i = incr_vec_index (negate_incr ? -increment : increment);
|
||
gcc_assert (i >= 0);
|
||
|
||
if (incr_vec[i].initializer)
|
||
{
|
||
enum tree_code code = negate_incr ? MINUS_EXPR : plus_code;
|
||
new_stmt = gimple_build_assign (lhs, code, basis_name,
|
||
incr_vec[i].initializer);
|
||
}
|
||
else {
|
||
tree stride;
|
||
|
||
if (!types_compatible_p (TREE_TYPE (c->stride), c->stride_type))
|
||
{
|
||
tree cast_stride = make_temp_ssa_name (c->stride_type, NULL,
|
||
"slsr");
|
||
cast_stmt = gimple_build_assign (cast_stride, NOP_EXPR,
|
||
c->stride);
|
||
stride = cast_stride;
|
||
}
|
||
else
|
||
stride = c->stride;
|
||
|
||
if (increment == 1)
|
||
new_stmt = gimple_build_assign (lhs, plus_code, basis_name, stride);
|
||
else if (increment == -1)
|
||
new_stmt = gimple_build_assign (lhs, MINUS_EXPR, basis_name, stride);
|
||
else
|
||
gcc_unreachable ();
|
||
}
|
||
}
|
||
|
||
if (cast_stmt)
|
||
{
|
||
gimple_set_location (cast_stmt, loc);
|
||
gsi_insert_on_edge (e, cast_stmt);
|
||
}
|
||
|
||
gimple_set_location (new_stmt, loc);
|
||
gsi_insert_on_edge (e, new_stmt);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
if (cast_stmt)
|
||
{
|
||
fprintf (dump_file, "Inserting cast on edge %d->%d: ",
|
||
e->src->index, e->dest->index);
|
||
print_gimple_stmt (dump_file, cast_stmt, 0);
|
||
}
|
||
fprintf (dump_file, "Inserting on edge %d->%d: ", e->src->index,
|
||
e->dest->index);
|
||
print_gimple_stmt (dump_file, new_stmt, 0);
|
||
}
|
||
|
||
return lhs;
|
||
}
|
||
|
||
/* Clear the visited field for a tree of PHI candidates. */
|
||
|
||
static void
|
||
clear_visited (gphi *phi)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
if (phi_cand->visited)
|
||
{
|
||
phi_cand->visited = 0;
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
clear_visited (as_a <gphi *> (arg_def));
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Recursive helper function for create_phi_basis. */
|
||
|
||
static tree
|
||
create_phi_basis_1 (slsr_cand_t c, gimple *from_phi, tree basis_name,
|
||
location_t loc, bool known_stride)
|
||
{
|
||
int i;
|
||
tree name, phi_arg;
|
||
gphi *phi;
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
int nargs = gimple_phi_num_args (from_phi);
|
||
basic_block phi_bb = gimple_bb (from_phi);
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (from_phi);
|
||
auto_vec<tree> phi_args (nargs);
|
||
|
||
if (phi_cand->visited)
|
||
return phi_cand->cached_basis;
|
||
phi_cand->visited = 1;
|
||
|
||
/* Process each argument of the existing phi that represents
|
||
conditionally-executed add candidates. */
|
||
for (i = 0; i < nargs; i++)
|
||
{
|
||
edge e = (*phi_bb->preds)[i];
|
||
tree arg = gimple_phi_arg_def (from_phi, i);
|
||
tree feeding_def;
|
||
|
||
/* If the phi argument is the base name of the CAND_PHI, then
|
||
this incoming arc should use the hidden basis. */
|
||
if (operand_equal_p (arg, phi_cand->base_expr, 0))
|
||
if (basis->index == 0)
|
||
feeding_def = gimple_assign_lhs (basis->cand_stmt);
|
||
else
|
||
{
|
||
widest_int incr = -basis->index;
|
||
feeding_def = create_add_on_incoming_edge (c, basis_name, incr,
|
||
e, loc, known_stride);
|
||
}
|
||
else
|
||
{
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
/* If there is another phi along this incoming edge, we must
|
||
process it in the same fashion to ensure that all basis
|
||
adjustments are made along its incoming edges. */
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
feeding_def = create_phi_basis_1 (c, arg_def, basis_name,
|
||
loc, known_stride);
|
||
else
|
||
{
|
||
slsr_cand_t arg_cand = base_cand_from_table (arg);
|
||
widest_int diff = arg_cand->index - basis->index;
|
||
feeding_def = create_add_on_incoming_edge (c, basis_name, diff,
|
||
e, loc, known_stride);
|
||
}
|
||
}
|
||
|
||
/* Because of recursion, we need to save the arguments in a vector
|
||
so we can create the PHI statement all at once. Otherwise the
|
||
storage for the half-created PHI can be reclaimed. */
|
||
phi_args.safe_push (feeding_def);
|
||
}
|
||
|
||
/* Create the new phi basis. */
|
||
name = make_temp_ssa_name (TREE_TYPE (basis_name), NULL, "slsr");
|
||
phi = create_phi_node (name, phi_bb);
|
||
SSA_NAME_DEF_STMT (name) = phi;
|
||
|
||
FOR_EACH_VEC_ELT (phi_args, i, phi_arg)
|
||
{
|
||
edge e = (*phi_bb->preds)[i];
|
||
add_phi_arg (phi, phi_arg, e, loc);
|
||
}
|
||
|
||
update_stmt (phi);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("Introducing new phi basis: ", dump_file);
|
||
print_gimple_stmt (dump_file, phi, 0);
|
||
}
|
||
|
||
phi_cand->cached_basis = name;
|
||
return name;
|
||
}
|
||
|
||
/* Given a candidate C with BASIS_NAME being the LHS of C's basis which
|
||
is hidden by the phi node FROM_PHI, create a new phi node in the same
|
||
block as FROM_PHI. The new phi is suitable for use as a basis by C,
|
||
with its phi arguments representing conditional adjustments to the
|
||
hidden basis along conditional incoming paths. Those adjustments are
|
||
made by creating add statements (and sometimes recursively creating
|
||
phis) along those incoming paths. LOC is the location to attach to
|
||
the introduced statements. KNOWN_STRIDE is true iff C's stride is a
|
||
constant. */
|
||
|
||
static tree
|
||
create_phi_basis (slsr_cand_t c, gimple *from_phi, tree basis_name,
|
||
location_t loc, bool known_stride)
|
||
{
|
||
tree retval = create_phi_basis_1 (c, from_phi, basis_name, loc,
|
||
known_stride);
|
||
gcc_assert (retval);
|
||
clear_visited (as_a <gphi *> (from_phi));
|
||
return retval;
|
||
}
|
||
|
||
/* Given a candidate C whose basis is hidden by at least one intervening
|
||
phi, introduce a matching number of new phis to represent its basis
|
||
adjusted by conditional increments along possible incoming paths. Then
|
||
replace C as though it were an unconditional candidate, using the new
|
||
basis. */
|
||
|
||
static void
|
||
replace_conditional_candidate (slsr_cand_t c)
|
||
{
|
||
tree basis_name, name;
|
||
slsr_cand_t basis;
|
||
location_t loc;
|
||
|
||
/* Look up the LHS SSA name from C's basis. This will be the
|
||
RHS1 of the adds we will introduce to create new phi arguments. */
|
||
basis = lookup_cand (c->basis);
|
||
basis_name = gimple_assign_lhs (basis->cand_stmt);
|
||
|
||
/* Create a new phi statement which will represent C's true basis
|
||
after the transformation is complete. */
|
||
loc = gimple_location (c->cand_stmt);
|
||
name = create_phi_basis (c, lookup_cand (c->def_phi)->cand_stmt,
|
||
basis_name, loc, KNOWN_STRIDE);
|
||
|
||
/* Replace C with an add of the new basis phi and a constant. */
|
||
widest_int bump = c->index * wi::to_widest (c->stride);
|
||
|
||
replace_mult_candidate (c, name, bump);
|
||
}
|
||
|
||
/* Recursive helper function for phi_add_costs. SPREAD is a measure of
|
||
how many PHI nodes we have visited at this point in the tree walk. */
|
||
|
||
static int
|
||
phi_add_costs_1 (gimple *phi, slsr_cand_t c, int one_add_cost, int *spread)
|
||
{
|
||
unsigned i;
|
||
int cost = 0;
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
if (phi_cand->visited)
|
||
return 0;
|
||
|
||
phi_cand->visited = 1;
|
||
(*spread)++;
|
||
|
||
/* If we work our way back to a phi that isn't dominated by the hidden
|
||
basis, this isn't a candidate for replacement. Indicate this by
|
||
returning an unreasonably high cost. It's not easy to detect
|
||
these situations when determining the basis, so we defer the
|
||
decision until now. */
|
||
basic_block phi_bb = gimple_bb (phi);
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
basic_block basis_bb = gimple_bb (basis->cand_stmt);
|
||
|
||
if (phi_bb == basis_bb || !dominated_by_p (CDI_DOMINATORS, phi_bb, basis_bb))
|
||
return COST_INFINITE;
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
|
||
if (arg != phi_cand->base_expr)
|
||
{
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
{
|
||
cost += phi_add_costs_1 (arg_def, c, one_add_cost, spread);
|
||
|
||
if (cost >= COST_INFINITE || *spread > MAX_SPREAD)
|
||
return COST_INFINITE;
|
||
}
|
||
else
|
||
{
|
||
slsr_cand_t arg_cand = base_cand_from_table (arg);
|
||
|
||
if (arg_cand->index != c->index)
|
||
cost += one_add_cost;
|
||
}
|
||
}
|
||
}
|
||
|
||
return cost;
|
||
}
|
||
|
||
/* Compute the expected costs of inserting basis adjustments for
|
||
candidate C with phi-definition PHI. The cost of inserting
|
||
one adjustment is given by ONE_ADD_COST. If PHI has arguments
|
||
which are themselves phi results, recursively calculate costs
|
||
for those phis as well. */
|
||
|
||
static int
|
||
phi_add_costs (gimple *phi, slsr_cand_t c, int one_add_cost)
|
||
{
|
||
int spread = 0;
|
||
int retval = phi_add_costs_1 (phi, c, one_add_cost, &spread);
|
||
clear_visited (as_a <gphi *> (phi));
|
||
return retval;
|
||
}
|
||
/* For candidate C, each sibling of candidate C, and each dependent of
|
||
candidate C, determine whether the candidate is dependent upon a
|
||
phi that hides its basis. If not, replace the candidate unconditionally.
|
||
Otherwise, determine whether the cost of introducing compensation code
|
||
for the candidate is offset by the gains from strength reduction. If
|
||
so, replace the candidate and introduce the compensation code. */
|
||
|
||
static void
|
||
replace_uncond_cands_and_profitable_phis (slsr_cand_t c)
|
||
{
|
||
if (phi_dependent_cand_p (c))
|
||
{
|
||
/* A multiply candidate with a stride of 1 is just an artifice
|
||
of a copy or cast; there is no value in replacing it. */
|
||
if (c->kind == CAND_MULT && wi::to_widest (c->stride) != 1)
|
||
{
|
||
/* A candidate dependent upon a phi will replace a multiply by
|
||
a constant with an add, and will insert at most one add for
|
||
each phi argument. Add these costs with the potential dead-code
|
||
savings to determine profitability. */
|
||
bool speed = optimize_bb_for_speed_p (gimple_bb (c->cand_stmt));
|
||
int mult_savings = stmt_cost (c->cand_stmt, speed);
|
||
gimple *phi = lookup_cand (c->def_phi)->cand_stmt;
|
||
tree phi_result = gimple_phi_result (phi);
|
||
int one_add_cost = add_cost (speed,
|
||
TYPE_MODE (TREE_TYPE (phi_result)));
|
||
int add_costs = one_add_cost + phi_add_costs (phi, c, one_add_cost);
|
||
int cost = add_costs - mult_savings - c->dead_savings;
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fprintf (dump_file, " Conditional candidate %d:\n", c->cand_num);
|
||
fprintf (dump_file, " add_costs = %d\n", add_costs);
|
||
fprintf (dump_file, " mult_savings = %d\n", mult_savings);
|
||
fprintf (dump_file, " dead_savings = %d\n", c->dead_savings);
|
||
fprintf (dump_file, " cost = %d\n", cost);
|
||
if (cost <= COST_NEUTRAL)
|
||
fputs (" Replacing...\n", dump_file);
|
||
else
|
||
fputs (" Not replaced.\n", dump_file);
|
||
}
|
||
|
||
if (cost <= COST_NEUTRAL)
|
||
replace_conditional_candidate (c);
|
||
}
|
||
}
|
||
else
|
||
replace_unconditional_candidate (c);
|
||
|
||
if (c->sibling)
|
||
replace_uncond_cands_and_profitable_phis (lookup_cand (c->sibling));
|
||
|
||
if (c->dependent)
|
||
replace_uncond_cands_and_profitable_phis (lookup_cand (c->dependent));
|
||
}
|
||
|
||
/* Count the number of candidates in the tree rooted at C that have
|
||
not already been replaced under other interpretations. */
|
||
|
||
static int
|
||
count_candidates (slsr_cand_t c)
|
||
{
|
||
unsigned count = cand_already_replaced (c) ? 0 : 1;
|
||
|
||
if (c->sibling)
|
||
count += count_candidates (lookup_cand (c->sibling));
|
||
|
||
if (c->dependent)
|
||
count += count_candidates (lookup_cand (c->dependent));
|
||
|
||
return count;
|
||
}
|
||
|
||
/* Increase the count of INCREMENT by one in the increment vector.
|
||
INCREMENT is associated with candidate C. If INCREMENT is to be
|
||
conditionally executed as part of a conditional candidate replacement,
|
||
IS_PHI_ADJUST is true, otherwise false. If an initializer
|
||
T_0 = stride * I is provided by a candidate that dominates all
|
||
candidates with the same increment, also record T_0 for subsequent use. */
|
||
|
||
static void
|
||
record_increment (slsr_cand_t c, widest_int increment, bool is_phi_adjust)
|
||
{
|
||
bool found = false;
|
||
unsigned i;
|
||
|
||
/* Treat increments that differ only in sign as identical so as to
|
||
share initializers, unless we are generating pointer arithmetic. */
|
||
if (!address_arithmetic_p && wi::neg_p (increment))
|
||
increment = -increment;
|
||
|
||
for (i = 0; i < incr_vec_len; i++)
|
||
{
|
||
if (incr_vec[i].incr == increment)
|
||
{
|
||
incr_vec[i].count++;
|
||
found = true;
|
||
|
||
/* If we previously recorded an initializer that doesn't
|
||
dominate this candidate, it's not going to be useful to
|
||
us after all. */
|
||
if (incr_vec[i].initializer
|
||
&& !dominated_by_p (CDI_DOMINATORS,
|
||
gimple_bb (c->cand_stmt),
|
||
incr_vec[i].init_bb))
|
||
{
|
||
incr_vec[i].initializer = NULL_TREE;
|
||
incr_vec[i].init_bb = NULL;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!found && incr_vec_len < MAX_INCR_VEC_LEN - 1)
|
||
{
|
||
/* The first time we see an increment, create the entry for it.
|
||
If this is the root candidate which doesn't have a basis, set
|
||
the count to zero. We're only processing it so it can possibly
|
||
provide an initializer for other candidates. */
|
||
incr_vec[incr_vec_len].incr = increment;
|
||
incr_vec[incr_vec_len].count = c->basis || is_phi_adjust ? 1 : 0;
|
||
incr_vec[incr_vec_len].cost = COST_INFINITE;
|
||
|
||
/* Optimistically record the first occurrence of this increment
|
||
as providing an initializer (if it does); we will revise this
|
||
opinion later if it doesn't dominate all other occurrences.
|
||
Exception: increments of 0, 1 never need initializers;
|
||
and phi adjustments don't ever provide initializers. */
|
||
if (c->kind == CAND_ADD
|
||
&& !is_phi_adjust
|
||
&& c->index == increment
|
||
&& (increment > 1 || increment < 0)
|
||
&& (gimple_assign_rhs_code (c->cand_stmt) == PLUS_EXPR
|
||
|| gimple_assign_rhs_code (c->cand_stmt) == POINTER_PLUS_EXPR))
|
||
{
|
||
tree t0 = NULL_TREE;
|
||
tree rhs1 = gimple_assign_rhs1 (c->cand_stmt);
|
||
tree rhs2 = gimple_assign_rhs2 (c->cand_stmt);
|
||
if (operand_equal_p (rhs1, c->base_expr, 0))
|
||
t0 = rhs2;
|
||
else if (operand_equal_p (rhs2, c->base_expr, 0))
|
||
t0 = rhs1;
|
||
if (t0
|
||
&& SSA_NAME_DEF_STMT (t0)
|
||
&& gimple_bb (SSA_NAME_DEF_STMT (t0)))
|
||
{
|
||
incr_vec[incr_vec_len].initializer = t0;
|
||
incr_vec[incr_vec_len++].init_bb
|
||
= gimple_bb (SSA_NAME_DEF_STMT (t0));
|
||
}
|
||
else
|
||
{
|
||
incr_vec[incr_vec_len].initializer = NULL_TREE;
|
||
incr_vec[incr_vec_len++].init_bb = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
incr_vec[incr_vec_len].initializer = NULL_TREE;
|
||
incr_vec[incr_vec_len++].init_bb = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Recursive helper function for record_phi_increments. */
|
||
|
||
static void
|
||
record_phi_increments_1 (slsr_cand_t basis, gimple *phi)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
if (phi_cand->visited)
|
||
return;
|
||
phi_cand->visited = 1;
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
record_phi_increments_1 (basis, arg_def);
|
||
else
|
||
{
|
||
widest_int diff;
|
||
|
||
if (operand_equal_p (arg, phi_cand->base_expr, 0))
|
||
{
|
||
diff = -basis->index;
|
||
record_increment (phi_cand, diff, PHI_ADJUST);
|
||
}
|
||
else
|
||
{
|
||
slsr_cand_t arg_cand = base_cand_from_table (arg);
|
||
diff = arg_cand->index - basis->index;
|
||
record_increment (arg_cand, diff, PHI_ADJUST);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Given phi statement PHI that hides a candidate from its BASIS, find
|
||
the increments along each incoming arc (recursively handling additional
|
||
phis that may be present) and record them. These increments are the
|
||
difference in index between the index-adjusting statements and the
|
||
index of the basis. */
|
||
|
||
static void
|
||
record_phi_increments (slsr_cand_t basis, gimple *phi)
|
||
{
|
||
record_phi_increments_1 (basis, phi);
|
||
clear_visited (as_a <gphi *> (phi));
|
||
}
|
||
|
||
/* Determine how many times each unique increment occurs in the set
|
||
of candidates rooted at C's parent, recording the data in the
|
||
increment vector. For each unique increment I, if an initializer
|
||
T_0 = stride * I is provided by a candidate that dominates all
|
||
candidates with the same increment, also record T_0 for subsequent
|
||
use. */
|
||
|
||
static void
|
||
record_increments (slsr_cand_t c)
|
||
{
|
||
if (!cand_already_replaced (c))
|
||
{
|
||
if (!phi_dependent_cand_p (c))
|
||
record_increment (c, cand_increment (c), NOT_PHI_ADJUST);
|
||
else
|
||
{
|
||
/* A candidate with a basis hidden by a phi will have one
|
||
increment for its relationship to the index represented by
|
||
the phi, and potentially additional increments along each
|
||
incoming edge. For the root of the dependency tree (which
|
||
has no basis), process just the initial index in case it has
|
||
an initializer that can be used by subsequent candidates. */
|
||
record_increment (c, c->index, NOT_PHI_ADJUST);
|
||
|
||
if (c->basis)
|
||
record_phi_increments (lookup_cand (c->basis),
|
||
lookup_cand (c->def_phi)->cand_stmt);
|
||
}
|
||
}
|
||
|
||
if (c->sibling)
|
||
record_increments (lookup_cand (c->sibling));
|
||
|
||
if (c->dependent)
|
||
record_increments (lookup_cand (c->dependent));
|
||
}
|
||
|
||
/* Recursive helper function for phi_incr_cost. */
|
||
|
||
static int
|
||
phi_incr_cost_1 (slsr_cand_t c, const widest_int &incr, gimple *phi,
|
||
int *savings)
|
||
{
|
||
unsigned i;
|
||
int cost = 0;
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
if (phi_cand->visited)
|
||
return 0;
|
||
phi_cand->visited = 1;
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
{
|
||
int feeding_savings = 0;
|
||
tree feeding_var = gimple_phi_result (arg_def);
|
||
cost += phi_incr_cost_1 (c, incr, arg_def, &feeding_savings);
|
||
if (uses_consumed_by_stmt (feeding_var, phi))
|
||
*savings += feeding_savings;
|
||
}
|
||
else
|
||
{
|
||
widest_int diff;
|
||
slsr_cand_t arg_cand;
|
||
|
||
/* When the PHI argument is just a pass-through to the base
|
||
expression of the hidden basis, the difference is zero minus
|
||
the index of the basis. There is no potential savings by
|
||
eliminating a statement in this case. */
|
||
if (operand_equal_p (arg, phi_cand->base_expr, 0))
|
||
{
|
||
arg_cand = (slsr_cand_t)NULL;
|
||
diff = -basis->index;
|
||
}
|
||
else
|
||
{
|
||
arg_cand = base_cand_from_table (arg);
|
||
diff = arg_cand->index - basis->index;
|
||
}
|
||
|
||
if (incr == diff)
|
||
{
|
||
tree basis_lhs = gimple_assign_lhs (basis->cand_stmt);
|
||
cost += add_cost (true, TYPE_MODE (TREE_TYPE (basis_lhs)));
|
||
if (arg_cand)
|
||
{
|
||
tree lhs = gimple_assign_lhs (arg_cand->cand_stmt);
|
||
if (uses_consumed_by_stmt (lhs, phi))
|
||
*savings += stmt_cost (arg_cand->cand_stmt, true);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cost;
|
||
}
|
||
|
||
/* Add up and return the costs of introducing add statements that
|
||
require the increment INCR on behalf of candidate C and phi
|
||
statement PHI. Accumulate into *SAVINGS the potential savings
|
||
from removing existing statements that feed PHI and have no other
|
||
uses. */
|
||
|
||
static int
|
||
phi_incr_cost (slsr_cand_t c, const widest_int &incr, gimple *phi,
|
||
int *savings)
|
||
{
|
||
int retval = phi_incr_cost_1 (c, incr, phi, savings);
|
||
clear_visited (as_a <gphi *> (phi));
|
||
return retval;
|
||
}
|
||
|
||
/* Return the first candidate in the tree rooted at C that has not
|
||
already been replaced, favoring siblings over dependents. */
|
||
|
||
static slsr_cand_t
|
||
unreplaced_cand_in_tree (slsr_cand_t c)
|
||
{
|
||
if (!cand_already_replaced (c))
|
||
return c;
|
||
|
||
if (c->sibling)
|
||
{
|
||
slsr_cand_t sib = unreplaced_cand_in_tree (lookup_cand (c->sibling));
|
||
if (sib)
|
||
return sib;
|
||
}
|
||
|
||
if (c->dependent)
|
||
{
|
||
slsr_cand_t dep = unreplaced_cand_in_tree (lookup_cand (c->dependent));
|
||
if (dep)
|
||
return dep;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/* Return TRUE if the candidates in the tree rooted at C should be
|
||
optimized for speed, else FALSE. We estimate this based on the block
|
||
containing the most dominant candidate in the tree that has not yet
|
||
been replaced. */
|
||
|
||
static bool
|
||
optimize_cands_for_speed_p (slsr_cand_t c)
|
||
{
|
||
slsr_cand_t c2 = unreplaced_cand_in_tree (c);
|
||
gcc_assert (c2);
|
||
return optimize_bb_for_speed_p (gimple_bb (c2->cand_stmt));
|
||
}
|
||
|
||
/* Add COST_IN to the lowest cost of any dependent path starting at
|
||
candidate C or any of its siblings, counting only candidates along
|
||
such paths with increment INCR. Assume that replacing a candidate
|
||
reduces cost by REPL_SAVINGS. Also account for savings from any
|
||
statements that would go dead. If COUNT_PHIS is true, include
|
||
costs of introducing feeding statements for conditional candidates. */
|
||
|
||
static int
|
||
lowest_cost_path (int cost_in, int repl_savings, slsr_cand_t c,
|
||
const widest_int &incr, bool count_phis)
|
||
{
|
||
int local_cost, sib_cost, savings = 0;
|
||
widest_int cand_incr = cand_abs_increment (c);
|
||
|
||
if (cand_already_replaced (c))
|
||
local_cost = cost_in;
|
||
else if (incr == cand_incr)
|
||
local_cost = cost_in - repl_savings - c->dead_savings;
|
||
else
|
||
local_cost = cost_in - c->dead_savings;
|
||
|
||
if (count_phis
|
||
&& phi_dependent_cand_p (c)
|
||
&& !cand_already_replaced (c))
|
||
{
|
||
gimple *phi = lookup_cand (c->def_phi)->cand_stmt;
|
||
local_cost += phi_incr_cost (c, incr, phi, &savings);
|
||
|
||
if (uses_consumed_by_stmt (gimple_phi_result (phi), c->cand_stmt))
|
||
local_cost -= savings;
|
||
}
|
||
|
||
if (c->dependent)
|
||
local_cost = lowest_cost_path (local_cost, repl_savings,
|
||
lookup_cand (c->dependent), incr,
|
||
count_phis);
|
||
|
||
if (c->sibling)
|
||
{
|
||
sib_cost = lowest_cost_path (cost_in, repl_savings,
|
||
lookup_cand (c->sibling), incr,
|
||
count_phis);
|
||
local_cost = MIN (local_cost, sib_cost);
|
||
}
|
||
|
||
return local_cost;
|
||
}
|
||
|
||
/* Compute the total savings that would accrue from all replacements
|
||
in the candidate tree rooted at C, counting only candidates with
|
||
increment INCR. Assume that replacing a candidate reduces cost
|
||
by REPL_SAVINGS. Also account for savings from statements that
|
||
would go dead. */
|
||
|
||
static int
|
||
total_savings (int repl_savings, slsr_cand_t c, const widest_int &incr,
|
||
bool count_phis)
|
||
{
|
||
int savings = 0;
|
||
widest_int cand_incr = cand_abs_increment (c);
|
||
|
||
if (incr == cand_incr && !cand_already_replaced (c))
|
||
savings += repl_savings + c->dead_savings;
|
||
|
||
if (count_phis
|
||
&& phi_dependent_cand_p (c)
|
||
&& !cand_already_replaced (c))
|
||
{
|
||
int phi_savings = 0;
|
||
gimple *phi = lookup_cand (c->def_phi)->cand_stmt;
|
||
savings -= phi_incr_cost (c, incr, phi, &phi_savings);
|
||
|
||
if (uses_consumed_by_stmt (gimple_phi_result (phi), c->cand_stmt))
|
||
savings += phi_savings;
|
||
}
|
||
|
||
if (c->dependent)
|
||
savings += total_savings (repl_savings, lookup_cand (c->dependent), incr,
|
||
count_phis);
|
||
|
||
if (c->sibling)
|
||
savings += total_savings (repl_savings, lookup_cand (c->sibling), incr,
|
||
count_phis);
|
||
|
||
return savings;
|
||
}
|
||
|
||
/* Use target-specific costs to determine and record which increments
|
||
in the current candidate tree are profitable to replace, assuming
|
||
MODE and SPEED. FIRST_DEP is the first dependent of the root of
|
||
the candidate tree.
|
||
|
||
One slight limitation here is that we don't account for the possible
|
||
introduction of casts in some cases. See replace_one_candidate for
|
||
the cases where these are introduced. This should probably be cleaned
|
||
up sometime. */
|
||
|
||
static void
|
||
analyze_increments (slsr_cand_t first_dep, machine_mode mode, bool speed)
|
||
{
|
||
unsigned i;
|
||
|
||
for (i = 0; i < incr_vec_len; i++)
|
||
{
|
||
HOST_WIDE_INT incr = incr_vec[i].incr.to_shwi ();
|
||
|
||
/* If somehow this increment is bigger than a HWI, we won't
|
||
be optimizing candidates that use it. And if the increment
|
||
has a count of zero, nothing will be done with it. */
|
||
if (!wi::fits_shwi_p (incr_vec[i].incr) || !incr_vec[i].count)
|
||
incr_vec[i].cost = COST_INFINITE;
|
||
|
||
/* Increments of 0, 1, and -1 are always profitable to replace,
|
||
because they always replace a multiply or add with an add or
|
||
copy, and may cause one or more existing instructions to go
|
||
dead. Exception: -1 can't be assumed to be profitable for
|
||
pointer addition. */
|
||
else if (incr == 0
|
||
|| incr == 1
|
||
|| (incr == -1
|
||
&& !POINTER_TYPE_P (first_dep->cand_type)))
|
||
incr_vec[i].cost = COST_NEUTRAL;
|
||
|
||
/* If we need to add an initializer, give up if a cast from the
|
||
candidate's type to its stride's type can lose precision.
|
||
Note that this already takes into account that the stride may
|
||
have been cast to a wider type, in which case this test won't
|
||
fire. Example:
|
||
|
||
short int _1;
|
||
_2 = (int) _1;
|
||
_3 = _2 * 10;
|
||
_4 = x + _3; ADD: x + (10 * (int)_1) : int
|
||
_5 = _2 * 15;
|
||
_6 = x + _5; ADD: x + (15 * (int)_1) : int
|
||
|
||
Although the stride was a short int initially, the stride
|
||
used in the analysis has been widened to an int, and such
|
||
widening will be done in the initializer as well. */
|
||
else if (!incr_vec[i].initializer
|
||
&& TREE_CODE (first_dep->stride) != INTEGER_CST
|
||
&& !legal_cast_p_1 (first_dep->stride_type,
|
||
TREE_TYPE (gimple_assign_lhs
|
||
(first_dep->cand_stmt))))
|
||
incr_vec[i].cost = COST_INFINITE;
|
||
|
||
/* If we need to add an initializer, make sure we don't introduce
|
||
a multiply by a pointer type, which can happen in certain cast
|
||
scenarios. */
|
||
else if (!incr_vec[i].initializer
|
||
&& TREE_CODE (first_dep->stride) != INTEGER_CST
|
||
&& POINTER_TYPE_P (first_dep->stride_type))
|
||
incr_vec[i].cost = COST_INFINITE;
|
||
|
||
/* For any other increment, if this is a multiply candidate, we
|
||
must introduce a temporary T and initialize it with
|
||
T_0 = stride * increment. When optimizing for speed, walk the
|
||
candidate tree to calculate the best cost reduction along any
|
||
path; if it offsets the fixed cost of inserting the initializer,
|
||
replacing the increment is profitable. When optimizing for
|
||
size, instead calculate the total cost reduction from replacing
|
||
all candidates with this increment. */
|
||
else if (first_dep->kind == CAND_MULT)
|
||
{
|
||
int cost = mult_by_coeff_cost (incr, mode, speed);
|
||
int repl_savings;
|
||
|
||
if (tree_fits_shwi_p (first_dep->stride))
|
||
{
|
||
HOST_WIDE_INT hwi_stride = tree_to_shwi (first_dep->stride);
|
||
repl_savings = mult_by_coeff_cost (hwi_stride, mode, speed);
|
||
}
|
||
else
|
||
repl_savings = mul_cost (speed, mode);
|
||
repl_savings -= add_cost (speed, mode);
|
||
|
||
if (speed)
|
||
cost = lowest_cost_path (cost, repl_savings, first_dep,
|
||
incr_vec[i].incr, COUNT_PHIS);
|
||
else
|
||
cost -= total_savings (repl_savings, first_dep, incr_vec[i].incr,
|
||
COUNT_PHIS);
|
||
|
||
incr_vec[i].cost = cost;
|
||
}
|
||
|
||
/* If this is an add candidate, the initializer may already
|
||
exist, so only calculate the cost of the initializer if it
|
||
doesn't. We are replacing one add with another here, so the
|
||
known replacement savings is zero. We will account for removal
|
||
of dead instructions in lowest_cost_path or total_savings. */
|
||
else
|
||
{
|
||
int cost = 0;
|
||
if (!incr_vec[i].initializer)
|
||
cost = mult_by_coeff_cost (incr, mode, speed);
|
||
|
||
if (speed)
|
||
cost = lowest_cost_path (cost, 0, first_dep, incr_vec[i].incr,
|
||
DONT_COUNT_PHIS);
|
||
else
|
||
cost -= total_savings (0, first_dep, incr_vec[i].incr,
|
||
DONT_COUNT_PHIS);
|
||
|
||
incr_vec[i].cost = cost;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Return the nearest common dominator of BB1 and BB2. If the blocks
|
||
are identical, return the earlier of C1 and C2 in *WHERE. Otherwise,
|
||
if the NCD matches BB1, return C1 in *WHERE; if the NCD matches BB2,
|
||
return C2 in *WHERE; and if the NCD matches neither, return NULL in
|
||
*WHERE. Note: It is possible for one of C1 and C2 to be NULL. */
|
||
|
||
static basic_block
|
||
ncd_for_two_cands (basic_block bb1, basic_block bb2,
|
||
slsr_cand_t c1, slsr_cand_t c2, slsr_cand_t *where)
|
||
{
|
||
basic_block ncd;
|
||
|
||
if (!bb1)
|
||
{
|
||
*where = c2;
|
||
return bb2;
|
||
}
|
||
|
||
if (!bb2)
|
||
{
|
||
*where = c1;
|
||
return bb1;
|
||
}
|
||
|
||
ncd = nearest_common_dominator (CDI_DOMINATORS, bb1, bb2);
|
||
|
||
/* If both candidates are in the same block, the earlier
|
||
candidate wins. */
|
||
if (bb1 == ncd && bb2 == ncd)
|
||
{
|
||
if (!c1 || (c2 && c2->cand_num < c1->cand_num))
|
||
*where = c2;
|
||
else
|
||
*where = c1;
|
||
}
|
||
|
||
/* Otherwise, if one of them produced a candidate in the
|
||
dominator, that one wins. */
|
||
else if (bb1 == ncd)
|
||
*where = c1;
|
||
|
||
else if (bb2 == ncd)
|
||
*where = c2;
|
||
|
||
/* If neither matches the dominator, neither wins. */
|
||
else
|
||
*where = NULL;
|
||
|
||
return ncd;
|
||
}
|
||
|
||
/* Consider all candidates that feed PHI. Find the nearest common
|
||
dominator of those candidates requiring the given increment INCR.
|
||
Further find and return the nearest common dominator of this result
|
||
with block NCD. If the returned block contains one or more of the
|
||
candidates, return the earliest candidate in the block in *WHERE. */
|
||
|
||
static basic_block
|
||
ncd_with_phi (slsr_cand_t c, const widest_int &incr, gphi *phi,
|
||
basic_block ncd, slsr_cand_t *where)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
ncd = ncd_with_phi (c, incr, as_a <gphi *> (arg_def), ncd, where);
|
||
else
|
||
{
|
||
widest_int diff;
|
||
|
||
if (operand_equal_p (arg, phi_cand->base_expr, 0))
|
||
diff = -basis->index;
|
||
else
|
||
{
|
||
slsr_cand_t arg_cand = base_cand_from_table (arg);
|
||
diff = arg_cand->index - basis->index;
|
||
}
|
||
|
||
basic_block pred = gimple_phi_arg_edge (phi, i)->src;
|
||
|
||
if ((incr == diff) || (!address_arithmetic_p && incr == -diff))
|
||
ncd = ncd_for_two_cands (ncd, pred, *where, NULL, where);
|
||
}
|
||
}
|
||
|
||
return ncd;
|
||
}
|
||
|
||
/* Consider the candidate C together with any candidates that feed
|
||
C's phi dependence (if any). Find and return the nearest common
|
||
dominator of those candidates requiring the given increment INCR.
|
||
If the returned block contains one or more of the candidates,
|
||
return the earliest candidate in the block in *WHERE. */
|
||
|
||
static basic_block
|
||
ncd_of_cand_and_phis (slsr_cand_t c, const widest_int &incr, slsr_cand_t *where)
|
||
{
|
||
basic_block ncd = NULL;
|
||
|
||
if (cand_abs_increment (c) == incr)
|
||
{
|
||
ncd = gimple_bb (c->cand_stmt);
|
||
*where = c;
|
||
}
|
||
|
||
if (phi_dependent_cand_p (c))
|
||
ncd = ncd_with_phi (c, incr,
|
||
as_a <gphi *> (lookup_cand (c->def_phi)->cand_stmt),
|
||
ncd, where);
|
||
|
||
return ncd;
|
||
}
|
||
|
||
/* Consider all candidates in the tree rooted at C for which INCR
|
||
represents the required increment of C relative to its basis.
|
||
Find and return the basic block that most nearly dominates all
|
||
such candidates. If the returned block contains one or more of
|
||
the candidates, return the earliest candidate in the block in
|
||
*WHERE. */
|
||
|
||
static basic_block
|
||
nearest_common_dominator_for_cands (slsr_cand_t c, const widest_int &incr,
|
||
slsr_cand_t *where)
|
||
{
|
||
basic_block sib_ncd = NULL, dep_ncd = NULL, this_ncd = NULL, ncd;
|
||
slsr_cand_t sib_where = NULL, dep_where = NULL, this_where = NULL, new_where;
|
||
|
||
/* First find the NCD of all siblings and dependents. */
|
||
if (c->sibling)
|
||
sib_ncd = nearest_common_dominator_for_cands (lookup_cand (c->sibling),
|
||
incr, &sib_where);
|
||
if (c->dependent)
|
||
dep_ncd = nearest_common_dominator_for_cands (lookup_cand (c->dependent),
|
||
incr, &dep_where);
|
||
if (!sib_ncd && !dep_ncd)
|
||
{
|
||
new_where = NULL;
|
||
ncd = NULL;
|
||
}
|
||
else if (sib_ncd && !dep_ncd)
|
||
{
|
||
new_where = sib_where;
|
||
ncd = sib_ncd;
|
||
}
|
||
else if (dep_ncd && !sib_ncd)
|
||
{
|
||
new_where = dep_where;
|
||
ncd = dep_ncd;
|
||
}
|
||
else
|
||
ncd = ncd_for_two_cands (sib_ncd, dep_ncd, sib_where,
|
||
dep_where, &new_where);
|
||
|
||
/* If the candidate's increment doesn't match the one we're interested
|
||
in (and nor do any increments for feeding defs of a phi-dependence),
|
||
then the result depends only on siblings and dependents. */
|
||
this_ncd = ncd_of_cand_and_phis (c, incr, &this_where);
|
||
|
||
if (!this_ncd || cand_already_replaced (c))
|
||
{
|
||
*where = new_where;
|
||
return ncd;
|
||
}
|
||
|
||
/* Otherwise, compare this candidate with the result from all siblings
|
||
and dependents. */
|
||
ncd = ncd_for_two_cands (ncd, this_ncd, new_where, this_where, where);
|
||
|
||
return ncd;
|
||
}
|
||
|
||
/* Return TRUE if the increment indexed by INDEX is profitable to replace. */
|
||
|
||
static inline bool
|
||
profitable_increment_p (unsigned index)
|
||
{
|
||
return (incr_vec[index].cost <= COST_NEUTRAL);
|
||
}
|
||
|
||
/* For each profitable increment in the increment vector not equal to
|
||
0 or 1 (or -1, for non-pointer arithmetic), find the nearest common
|
||
dominator of all statements in the candidate chain rooted at C
|
||
that require that increment, and insert an initializer
|
||
T_0 = stride * increment at that location. Record T_0 with the
|
||
increment record. */
|
||
|
||
static void
|
||
insert_initializers (slsr_cand_t c)
|
||
{
|
||
unsigned i;
|
||
|
||
for (i = 0; i < incr_vec_len; i++)
|
||
{
|
||
basic_block bb;
|
||
slsr_cand_t where = NULL;
|
||
gassign *init_stmt;
|
||
gassign *cast_stmt = NULL;
|
||
tree new_name, incr_tree, init_stride;
|
||
widest_int incr = incr_vec[i].incr;
|
||
|
||
if (!profitable_increment_p (i)
|
||
|| incr == 1
|
||
|| (incr == -1
|
||
&& (!POINTER_TYPE_P (lookup_cand (c->basis)->cand_type)))
|
||
|| incr == 0)
|
||
continue;
|
||
|
||
/* We may have already identified an existing initializer that
|
||
will suffice. */
|
||
if (incr_vec[i].initializer)
|
||
{
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("Using existing initializer: ", dump_file);
|
||
print_gimple_stmt (dump_file,
|
||
SSA_NAME_DEF_STMT (incr_vec[i].initializer),
|
||
0, TDF_NONE);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
/* Find the block that most closely dominates all candidates
|
||
with this increment. If there is at least one candidate in
|
||
that block, the earliest one will be returned in WHERE. */
|
||
bb = nearest_common_dominator_for_cands (c, incr, &where);
|
||
|
||
/* If the NCD is not dominated by the block containing the
|
||
definition of the stride, we can't legally insert a
|
||
single initializer. Mark the increment as unprofitable
|
||
so we don't make any replacements. FIXME: Multiple
|
||
initializers could be placed with more analysis. */
|
||
gimple *stride_def = SSA_NAME_DEF_STMT (c->stride);
|
||
basic_block stride_bb = gimple_bb (stride_def);
|
||
|
||
if (stride_bb && !dominated_by_p (CDI_DOMINATORS, bb, stride_bb))
|
||
{
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
fprintf (dump_file,
|
||
"Initializer #%d cannot be legally placed\n", i);
|
||
incr_vec[i].cost = COST_INFINITE;
|
||
continue;
|
||
}
|
||
|
||
/* If the nominal stride has a different type than the recorded
|
||
stride type, build a cast from the nominal stride to that type. */
|
||
if (!types_compatible_p (TREE_TYPE (c->stride), c->stride_type))
|
||
{
|
||
init_stride = make_temp_ssa_name (c->stride_type, NULL, "slsr");
|
||
cast_stmt = gimple_build_assign (init_stride, NOP_EXPR, c->stride);
|
||
}
|
||
else
|
||
init_stride = c->stride;
|
||
|
||
/* Create a new SSA name to hold the initializer's value. */
|
||
new_name = make_temp_ssa_name (c->stride_type, NULL, "slsr");
|
||
incr_vec[i].initializer = new_name;
|
||
|
||
/* Create the initializer and insert it in the latest possible
|
||
dominating position. */
|
||
incr_tree = wide_int_to_tree (c->stride_type, incr);
|
||
init_stmt = gimple_build_assign (new_name, MULT_EXPR,
|
||
init_stride, incr_tree);
|
||
if (where)
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (where->cand_stmt);
|
||
location_t loc = gimple_location (where->cand_stmt);
|
||
|
||
if (cast_stmt)
|
||
{
|
||
gsi_insert_before (&gsi, cast_stmt, GSI_SAME_STMT);
|
||
gimple_set_location (cast_stmt, loc);
|
||
}
|
||
|
||
gsi_insert_before (&gsi, init_stmt, GSI_SAME_STMT);
|
||
gimple_set_location (init_stmt, loc);
|
||
}
|
||
else
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_last_bb (bb);
|
||
gimple *basis_stmt = lookup_cand (c->basis)->cand_stmt;
|
||
location_t loc = gimple_location (basis_stmt);
|
||
|
||
if (!gsi_end_p (gsi) && stmt_ends_bb_p (gsi_stmt (gsi)))
|
||
{
|
||
if (cast_stmt)
|
||
{
|
||
gsi_insert_before (&gsi, cast_stmt, GSI_SAME_STMT);
|
||
gimple_set_location (cast_stmt, loc);
|
||
}
|
||
gsi_insert_before (&gsi, init_stmt, GSI_SAME_STMT);
|
||
}
|
||
else
|
||
{
|
||
if (cast_stmt)
|
||
{
|
||
gsi_insert_after (&gsi, cast_stmt, GSI_NEW_STMT);
|
||
gimple_set_location (cast_stmt, loc);
|
||
}
|
||
gsi_insert_after (&gsi, init_stmt, GSI_NEW_STMT);
|
||
}
|
||
|
||
gimple_set_location (init_stmt, gimple_location (basis_stmt));
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
if (cast_stmt)
|
||
{
|
||
fputs ("Inserting stride cast: ", dump_file);
|
||
print_gimple_stmt (dump_file, cast_stmt, 0);
|
||
}
|
||
fputs ("Inserting initializer: ", dump_file);
|
||
print_gimple_stmt (dump_file, init_stmt, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Recursive helper function for all_phi_incrs_profitable. */
|
||
|
||
static bool
|
||
all_phi_incrs_profitable_1 (slsr_cand_t c, gphi *phi, int *spread)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
slsr_cand_t phi_cand = *stmt_cand_map->get (phi);
|
||
|
||
if (phi_cand->visited)
|
||
return true;
|
||
|
||
phi_cand->visited = 1;
|
||
(*spread)++;
|
||
|
||
/* If the basis doesn't dominate the PHI (including when the PHI is
|
||
in the same block as the basis), we won't be able to create a PHI
|
||
using the basis here. */
|
||
basic_block basis_bb = gimple_bb (basis->cand_stmt);
|
||
basic_block phi_bb = gimple_bb (phi);
|
||
|
||
if (phi_bb == basis_bb
|
||
|| !dominated_by_p (CDI_DOMINATORS, phi_bb, basis_bb))
|
||
return false;
|
||
|
||
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||
{
|
||
/* If the PHI arg resides in a block not dominated by the basis,
|
||
we won't be able to create a PHI using the basis here. */
|
||
basic_block pred_bb = gimple_phi_arg_edge (phi, i)->src;
|
||
|
||
if (!dominated_by_p (CDI_DOMINATORS, pred_bb, basis_bb))
|
||
return false;
|
||
|
||
tree arg = gimple_phi_arg_def (phi, i);
|
||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||
|
||
if (gimple_code (arg_def) == GIMPLE_PHI)
|
||
{
|
||
if (!all_phi_incrs_profitable_1 (c, as_a <gphi *> (arg_def), spread)
|
||
|| *spread > MAX_SPREAD)
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
int j;
|
||
widest_int increment;
|
||
|
||
if (operand_equal_p (arg, phi_cand->base_expr, 0))
|
||
increment = -basis->index;
|
||
else
|
||
{
|
||
slsr_cand_t arg_cand = base_cand_from_table (arg);
|
||
increment = arg_cand->index - basis->index;
|
||
}
|
||
|
||
if (!address_arithmetic_p && wi::neg_p (increment))
|
||
increment = -increment;
|
||
|
||
j = incr_vec_index (increment);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fprintf (dump_file, " Conditional candidate %d, phi: ",
|
||
c->cand_num);
|
||
print_gimple_stmt (dump_file, phi, 0);
|
||
fputs (" increment: ", dump_file);
|
||
print_decs (increment, dump_file);
|
||
if (j < 0)
|
||
fprintf (dump_file,
|
||
"\n Not replaced; incr_vec overflow.\n");
|
||
else {
|
||
fprintf (dump_file, "\n cost: %d\n", incr_vec[j].cost);
|
||
if (profitable_increment_p (j))
|
||
fputs (" Replacing...\n", dump_file);
|
||
else
|
||
fputs (" Not replaced.\n", dump_file);
|
||
}
|
||
}
|
||
|
||
if (j < 0 || !profitable_increment_p (j))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Return TRUE iff all required increments for candidates feeding PHI
|
||
are profitable (and legal!) to replace on behalf of candidate C. */
|
||
|
||
static bool
|
||
all_phi_incrs_profitable (slsr_cand_t c, gphi *phi)
|
||
{
|
||
int spread = 0;
|
||
bool retval = all_phi_incrs_profitable_1 (c, phi, &spread);
|
||
clear_visited (phi);
|
||
return retval;
|
||
}
|
||
|
||
/* Create a NOP_EXPR that copies FROM_EXPR into a new SSA name of
|
||
type TO_TYPE, and insert it in front of the statement represented
|
||
by candidate C. Use *NEW_VAR to create the new SSA name. Return
|
||
the new SSA name. */
|
||
|
||
static tree
|
||
introduce_cast_before_cand (slsr_cand_t c, tree to_type, tree from_expr)
|
||
{
|
||
tree cast_lhs;
|
||
gassign *cast_stmt;
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
|
||
cast_lhs = make_temp_ssa_name (to_type, NULL, "slsr");
|
||
cast_stmt = gimple_build_assign (cast_lhs, NOP_EXPR, from_expr);
|
||
gimple_set_location (cast_stmt, gimple_location (c->cand_stmt));
|
||
gsi_insert_before (&gsi, cast_stmt, GSI_SAME_STMT);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs (" Inserting: ", dump_file);
|
||
print_gimple_stmt (dump_file, cast_stmt, 0);
|
||
}
|
||
|
||
return cast_lhs;
|
||
}
|
||
|
||
/* Replace the RHS of the statement represented by candidate C with
|
||
NEW_CODE, NEW_RHS1, and NEW_RHS2, provided that to do so doesn't
|
||
leave C unchanged or just interchange its operands. The original
|
||
operation and operands are in OLD_CODE, OLD_RHS1, and OLD_RHS2.
|
||
If the replacement was made and we are doing a details dump,
|
||
return the revised statement, else NULL. */
|
||
|
||
static gimple *
|
||
replace_rhs_if_not_dup (enum tree_code new_code, tree new_rhs1, tree new_rhs2,
|
||
enum tree_code old_code, tree old_rhs1, tree old_rhs2,
|
||
slsr_cand_t c)
|
||
{
|
||
if (new_code != old_code
|
||
|| ((!operand_equal_p (new_rhs1, old_rhs1, 0)
|
||
|| !operand_equal_p (new_rhs2, old_rhs2, 0))
|
||
&& (!operand_equal_p (new_rhs1, old_rhs2, 0)
|
||
|| !operand_equal_p (new_rhs2, old_rhs1, 0))))
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_assign_set_rhs_with_ops (&gsi, new_code, new_rhs1, new_rhs2);
|
||
update_stmt (gsi_stmt (gsi));
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = gsi_stmt (gsi);
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
return gsi_stmt (gsi);
|
||
}
|
||
|
||
else if (dump_file && (dump_flags & TDF_DETAILS))
|
||
fputs (" (duplicate, not actually replacing)\n", dump_file);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/* Strength-reduce the statement represented by candidate C by replacing
|
||
it with an equivalent addition or subtraction. I is the index into
|
||
the increment vector identifying C's increment. NEW_VAR is used to
|
||
create a new SSA name if a cast needs to be introduced. BASIS_NAME
|
||
is the rhs1 to use in creating the add/subtract. */
|
||
|
||
static void
|
||
replace_one_candidate (slsr_cand_t c, unsigned i, tree basis_name)
|
||
{
|
||
gimple *stmt_to_print = NULL;
|
||
tree orig_rhs1, orig_rhs2;
|
||
tree rhs2;
|
||
enum tree_code orig_code, repl_code;
|
||
widest_int cand_incr;
|
||
|
||
orig_code = gimple_assign_rhs_code (c->cand_stmt);
|
||
orig_rhs1 = gimple_assign_rhs1 (c->cand_stmt);
|
||
orig_rhs2 = gimple_assign_rhs2 (c->cand_stmt);
|
||
cand_incr = cand_increment (c);
|
||
|
||
/* If orig_rhs2 is NULL, we have already replaced this in situ with
|
||
a copy statement under another interpretation. */
|
||
if (!orig_rhs2)
|
||
return;
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
fputs ("Replacing: ", dump_file);
|
||
print_gimple_stmt (dump_file, c->cand_stmt, 0);
|
||
stmt_to_print = c->cand_stmt;
|
||
}
|
||
|
||
if (address_arithmetic_p)
|
||
repl_code = POINTER_PLUS_EXPR;
|
||
else
|
||
repl_code = PLUS_EXPR;
|
||
|
||
/* If the increment has an initializer T_0, replace the candidate
|
||
statement with an add of the basis name and the initializer. */
|
||
if (incr_vec[i].initializer)
|
||
{
|
||
tree init_type = TREE_TYPE (incr_vec[i].initializer);
|
||
tree orig_type = TREE_TYPE (orig_rhs2);
|
||
|
||
if (types_compatible_p (orig_type, init_type))
|
||
rhs2 = incr_vec[i].initializer;
|
||
else
|
||
rhs2 = introduce_cast_before_cand (c, orig_type,
|
||
incr_vec[i].initializer);
|
||
|
||
if (incr_vec[i].incr != cand_incr)
|
||
{
|
||
gcc_assert (repl_code == PLUS_EXPR);
|
||
repl_code = MINUS_EXPR;
|
||
}
|
||
|
||
stmt_to_print = replace_rhs_if_not_dup (repl_code, basis_name, rhs2,
|
||
orig_code, orig_rhs1, orig_rhs2,
|
||
c);
|
||
}
|
||
|
||
/* Otherwise, the increment is one of -1, 0, and 1. Replace
|
||
with a subtract of the stride from the basis name, a copy
|
||
from the basis name, or an add of the stride to the basis
|
||
name, respectively. It may be necessary to introduce a
|
||
cast (or reuse an existing cast). */
|
||
else if (cand_incr == 1)
|
||
{
|
||
tree stride_type = TREE_TYPE (c->stride);
|
||
tree orig_type = TREE_TYPE (orig_rhs2);
|
||
|
||
if (types_compatible_p (orig_type, stride_type))
|
||
rhs2 = c->stride;
|
||
else
|
||
rhs2 = introduce_cast_before_cand (c, orig_type, c->stride);
|
||
|
||
stmt_to_print = replace_rhs_if_not_dup (repl_code, basis_name, rhs2,
|
||
orig_code, orig_rhs1, orig_rhs2,
|
||
c);
|
||
}
|
||
|
||
else if (cand_incr == -1)
|
||
{
|
||
tree stride_type = TREE_TYPE (c->stride);
|
||
tree orig_type = TREE_TYPE (orig_rhs2);
|
||
gcc_assert (repl_code != POINTER_PLUS_EXPR);
|
||
|
||
if (types_compatible_p (orig_type, stride_type))
|
||
rhs2 = c->stride;
|
||
else
|
||
rhs2 = introduce_cast_before_cand (c, orig_type, c->stride);
|
||
|
||
if (orig_code != MINUS_EXPR
|
||
|| !operand_equal_p (basis_name, orig_rhs1, 0)
|
||
|| !operand_equal_p (rhs2, orig_rhs2, 0))
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_assign_set_rhs_with_ops (&gsi, MINUS_EXPR, basis_name, rhs2);
|
||
update_stmt (gsi_stmt (gsi));
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = gsi_stmt (gsi);
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
stmt_to_print = gsi_stmt (gsi);
|
||
}
|
||
else if (dump_file && (dump_flags & TDF_DETAILS))
|
||
fputs (" (duplicate, not actually replacing)\n", dump_file);
|
||
}
|
||
|
||
else if (cand_incr == 0)
|
||
{
|
||
tree lhs = gimple_assign_lhs (c->cand_stmt);
|
||
tree lhs_type = TREE_TYPE (lhs);
|
||
tree basis_type = TREE_TYPE (basis_name);
|
||
|
||
if (types_compatible_p (lhs_type, basis_type))
|
||
{
|
||
gassign *copy_stmt = gimple_build_assign (lhs, basis_name);
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_set_location (copy_stmt, gimple_location (c->cand_stmt));
|
||
gsi_replace (&gsi, copy_stmt, false);
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = copy_stmt;
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
stmt_to_print = copy_stmt;
|
||
}
|
||
else
|
||
{
|
||
gimple_stmt_iterator gsi = gsi_for_stmt (c->cand_stmt);
|
||
gassign *cast_stmt = gimple_build_assign (lhs, NOP_EXPR, basis_name);
|
||
slsr_cand_t cc = lookup_cand (c->first_interp);
|
||
gimple_set_location (cast_stmt, gimple_location (c->cand_stmt));
|
||
gsi_replace (&gsi, cast_stmt, false);
|
||
while (cc)
|
||
{
|
||
cc->cand_stmt = cast_stmt;
|
||
cc = lookup_cand (cc->next_interp);
|
||
}
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
stmt_to_print = cast_stmt;
|
||
}
|
||
}
|
||
else
|
||
gcc_unreachable ();
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS) && stmt_to_print)
|
||
{
|
||
fputs ("With: ", dump_file);
|
||
print_gimple_stmt (dump_file, stmt_to_print, 0);
|
||
fputs ("\n", dump_file);
|
||
}
|
||
}
|
||
|
||
/* For each candidate in the tree rooted at C, replace it with
|
||
an increment if such has been shown to be profitable. */
|
||
|
||
static void
|
||
replace_profitable_candidates (slsr_cand_t c)
|
||
{
|
||
if (!cand_already_replaced (c))
|
||
{
|
||
widest_int increment = cand_abs_increment (c);
|
||
enum tree_code orig_code = gimple_assign_rhs_code (c->cand_stmt);
|
||
int i;
|
||
|
||
i = incr_vec_index (increment);
|
||
|
||
/* Only process profitable increments. Nothing useful can be done
|
||
to a cast or copy. */
|
||
if (i >= 0
|
||
&& profitable_increment_p (i)
|
||
&& orig_code != SSA_NAME
|
||
&& !CONVERT_EXPR_CODE_P (orig_code))
|
||
{
|
||
if (phi_dependent_cand_p (c))
|
||
{
|
||
gphi *phi = as_a <gphi *> (lookup_cand (c->def_phi)->cand_stmt);
|
||
|
||
if (all_phi_incrs_profitable (c, phi))
|
||
{
|
||
/* Look up the LHS SSA name from C's basis. This will be
|
||
the RHS1 of the adds we will introduce to create new
|
||
phi arguments. */
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
tree basis_name = gimple_assign_lhs (basis->cand_stmt);
|
||
|
||
/* Create a new phi statement that will represent C's true
|
||
basis after the transformation is complete. */
|
||
location_t loc = gimple_location (c->cand_stmt);
|
||
tree name = create_phi_basis (c, phi, basis_name,
|
||
loc, UNKNOWN_STRIDE);
|
||
|
||
/* Replace C with an add of the new basis phi and the
|
||
increment. */
|
||
replace_one_candidate (c, i, name);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
slsr_cand_t basis = lookup_cand (c->basis);
|
||
tree basis_name = gimple_assign_lhs (basis->cand_stmt);
|
||
replace_one_candidate (c, i, basis_name);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (c->sibling)
|
||
replace_profitable_candidates (lookup_cand (c->sibling));
|
||
|
||
if (c->dependent)
|
||
replace_profitable_candidates (lookup_cand (c->dependent));
|
||
}
|
||
|
||
/* Analyze costs of related candidates in the candidate vector,
|
||
and make beneficial replacements. */
|
||
|
||
static void
|
||
analyze_candidates_and_replace (void)
|
||
{
|
||
unsigned i;
|
||
slsr_cand_t c;
|
||
|
||
/* Each candidate that has a null basis and a non-null
|
||
dependent is the root of a tree of related statements.
|
||
Analyze each tree to determine a subset of those
|
||
statements that can be replaced with maximum benefit.
|
||
|
||
Note the first NULL element is skipped. */
|
||
FOR_EACH_VEC_ELT_FROM (cand_vec, i, c, 1)
|
||
{
|
||
slsr_cand_t first_dep;
|
||
|
||
if (c->basis != 0 || c->dependent == 0)
|
||
continue;
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
fprintf (dump_file, "\nProcessing dependency tree rooted at %d.\n",
|
||
c->cand_num);
|
||
|
||
first_dep = lookup_cand (c->dependent);
|
||
|
||
/* If this is a chain of CAND_REFs, unconditionally replace
|
||
each of them with a strength-reduced data reference. */
|
||
if (c->kind == CAND_REF)
|
||
replace_refs (c);
|
||
|
||
/* If the common stride of all related candidates is a known
|
||
constant, each candidate without a phi-dependence can be
|
||
profitably replaced. Each replaces a multiply by a single
|
||
add, with the possibility that a feeding add also goes dead.
|
||
A candidate with a phi-dependence is replaced only if the
|
||
compensation code it requires is offset by the strength
|
||
reduction savings. */
|
||
else if (TREE_CODE (c->stride) == INTEGER_CST)
|
||
replace_uncond_cands_and_profitable_phis (first_dep);
|
||
|
||
/* When the stride is an SSA name, it may still be profitable
|
||
to replace some or all of the dependent candidates, depending
|
||
on whether the introduced increments can be reused, or are
|
||
less expensive to calculate than the replaced statements. */
|
||
else
|
||
{
|
||
machine_mode mode;
|
||
bool speed;
|
||
|
||
/* Determine whether we'll be generating pointer arithmetic
|
||
when replacing candidates. */
|
||
address_arithmetic_p = (c->kind == CAND_ADD
|
||
&& POINTER_TYPE_P (c->cand_type));
|
||
|
||
/* If all candidates have already been replaced under other
|
||
interpretations, nothing remains to be done. */
|
||
if (!count_candidates (c))
|
||
continue;
|
||
|
||
/* Construct an array of increments for this candidate chain. */
|
||
incr_vec = XNEWVEC (incr_info, MAX_INCR_VEC_LEN);
|
||
incr_vec_len = 0;
|
||
record_increments (c);
|
||
|
||
/* Determine which increments are profitable to replace. */
|
||
mode = TYPE_MODE (TREE_TYPE (gimple_assign_lhs (c->cand_stmt)));
|
||
speed = optimize_cands_for_speed_p (c);
|
||
analyze_increments (first_dep, mode, speed);
|
||
|
||
/* Insert initializers of the form T_0 = stride * increment
|
||
for use in profitable replacements. */
|
||
insert_initializers (first_dep);
|
||
dump_incr_vec ();
|
||
|
||
/* Perform the replacements. */
|
||
replace_profitable_candidates (first_dep);
|
||
free (incr_vec);
|
||
}
|
||
}
|
||
|
||
/* For conditional candidates, we may have uncommitted insertions
|
||
on edges to clean up. */
|
||
gsi_commit_edge_inserts ();
|
||
}
|
||
|
||
namespace {
|
||
|
||
const pass_data pass_data_strength_reduction =
|
||
{
|
||
GIMPLE_PASS, /* type */
|
||
"slsr", /* name */
|
||
OPTGROUP_NONE, /* optinfo_flags */
|
||
TV_GIMPLE_SLSR, /* tv_id */
|
||
( PROP_cfg | PROP_ssa ), /* properties_required */
|
||
0, /* properties_provided */
|
||
0, /* properties_destroyed */
|
||
0, /* todo_flags_start */
|
||
0, /* todo_flags_finish */
|
||
};
|
||
|
||
class pass_strength_reduction : public gimple_opt_pass
|
||
{
|
||
public:
|
||
pass_strength_reduction (gcc::context *ctxt)
|
||
: gimple_opt_pass (pass_data_strength_reduction, ctxt)
|
||
{}
|
||
|
||
/* opt_pass methods: */
|
||
virtual bool gate (function *) { return flag_tree_slsr; }
|
||
virtual unsigned int execute (function *);
|
||
|
||
}; // class pass_strength_reduction
|
||
|
||
unsigned
|
||
pass_strength_reduction::execute (function *fun)
|
||
{
|
||
/* Create the obstack where candidates will reside. */
|
||
gcc_obstack_init (&cand_obstack);
|
||
|
||
/* Allocate the candidate vector and initialize the first NULL element. */
|
||
cand_vec.create (128);
|
||
cand_vec.safe_push (NULL);
|
||
|
||
/* Allocate the mapping from statements to candidate indices. */
|
||
stmt_cand_map = new hash_map<gimple *, slsr_cand_t>;
|
||
|
||
/* Create the obstack where candidate chains will reside. */
|
||
gcc_obstack_init (&chain_obstack);
|
||
|
||
/* Allocate the mapping from base expressions to candidate chains. */
|
||
base_cand_map = new hash_table<cand_chain_hasher> (500);
|
||
|
||
/* Allocate the mapping from bases to alternative bases. */
|
||
alt_base_map = new hash_map<tree, tree>;
|
||
|
||
/* Initialize the loop optimizer. We need to detect flow across
|
||
back edges, and this gives us dominator information as well. */
|
||
loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
|
||
|
||
/* Walk the CFG in predominator order looking for strength reduction
|
||
candidates. */
|
||
find_candidates_dom_walker (CDI_DOMINATORS)
|
||
.walk (fun->cfg->x_entry_block_ptr);
|
||
|
||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||
{
|
||
dump_cand_vec ();
|
||
dump_cand_chains ();
|
||
}
|
||
|
||
delete alt_base_map;
|
||
free_affine_expand_cache (&name_expansions);
|
||
|
||
/* Analyze costs and make appropriate replacements. */
|
||
analyze_candidates_and_replace ();
|
||
|
||
loop_optimizer_finalize ();
|
||
delete base_cand_map;
|
||
base_cand_map = NULL;
|
||
obstack_free (&chain_obstack, NULL);
|
||
delete stmt_cand_map;
|
||
cand_vec.release ();
|
||
obstack_free (&cand_obstack, NULL);
|
||
|
||
return 0;
|
||
}
|
||
|
||
} // anon namespace
|
||
|
||
gimple_opt_pass *
|
||
make_pass_strength_reduction (gcc::context *ctxt)
|
||
{
|
||
return new pass_strength_reduction (ctxt);
|
||
}
|