793 lines
25 KiB
C++
793 lines
25 KiB
C++
/* Data references and dependences detectors.
|
||
Copyright (C) 2003-2023 Free Software Foundation, Inc.
|
||
Contributed by Sebastian Pop <pop@cri.ensmp.fr>
|
||
|
||
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/>. */
|
||
|
||
#ifndef GCC_TREE_DATA_REF_H
|
||
#define GCC_TREE_DATA_REF_H
|
||
|
||
#include "graphds.h"
|
||
#include "tree-chrec.h"
|
||
#include "opt-problem.h"
|
||
|
||
/*
|
||
innermost_loop_behavior describes the evolution of the address of the memory
|
||
reference in the innermost enclosing loop. The address is expressed as
|
||
BASE + STEP * # of iteration, and base is further decomposed as the base
|
||
pointer (BASE_ADDRESS), loop invariant offset (OFFSET) and
|
||
constant offset (INIT). Examples, in loop nest
|
||
|
||
for (i = 0; i < 100; i++)
|
||
for (j = 3; j < 100; j++)
|
||
|
||
Example 1 Example 2
|
||
data-ref a[j].b[i][j] *(p + x + 16B + 4B * j)
|
||
|
||
|
||
innermost_loop_behavior
|
||
base_address &a p
|
||
offset i * D_i x
|
||
init 3 * D_j + offsetof (b) 28
|
||
step D_j 4
|
||
|
||
*/
|
||
struct innermost_loop_behavior
|
||
{
|
||
tree base_address;
|
||
tree offset;
|
||
tree init;
|
||
tree step;
|
||
|
||
/* BASE_ADDRESS is known to be misaligned by BASE_MISALIGNMENT bytes
|
||
from an alignment boundary of BASE_ALIGNMENT bytes. For example,
|
||
if we had:
|
||
|
||
struct S __attribute__((aligned(16))) { ... };
|
||
|
||
char *ptr;
|
||
... *(struct S *) (ptr - 4) ...;
|
||
|
||
the information would be:
|
||
|
||
base_address: ptr
|
||
base_aligment: 16
|
||
base_misalignment: 4
|
||
init: -4
|
||
|
||
where init cancels the base misalignment. If instead we had a
|
||
reference to a particular field:
|
||
|
||
struct S __attribute__((aligned(16))) { ... int f; ... };
|
||
|
||
char *ptr;
|
||
... ((struct S *) (ptr - 4))->f ...;
|
||
|
||
the information would be:
|
||
|
||
base_address: ptr
|
||
base_aligment: 16
|
||
base_misalignment: 4
|
||
init: -4 + offsetof (S, f)
|
||
|
||
where base_address + init might also be misaligned, and by a different
|
||
amount from base_address. */
|
||
unsigned int base_alignment;
|
||
unsigned int base_misalignment;
|
||
|
||
/* The largest power of two that divides OFFSET, capped to a suitably
|
||
high value if the offset is zero. This is a byte rather than a bit
|
||
quantity. */
|
||
unsigned int offset_alignment;
|
||
|
||
/* Likewise for STEP. */
|
||
unsigned int step_alignment;
|
||
};
|
||
|
||
/* Describes the evolutions of indices of the memory reference. The indices
|
||
are indices of the ARRAY_REFs, indexes in artificial dimensions
|
||
added for member selection of records and the operands of MEM_REFs.
|
||
BASE_OBJECT is the part of the reference that is loop-invariant
|
||
(note that this reference does not have to cover the whole object
|
||
being accessed, in which case UNCONSTRAINED_BASE is set; hence it is
|
||
not recommended to use BASE_OBJECT in any code generation).
|
||
For the examples above,
|
||
|
||
base_object: a *(p + x + 4B * j_0)
|
||
indices: {j_0, +, 1}_2 {16, +, 4}_2
|
||
4
|
||
{i_0, +, 1}_1
|
||
{j_0, +, 1}_2
|
||
*/
|
||
|
||
struct indices
|
||
{
|
||
/* The object. */
|
||
tree base_object;
|
||
|
||
/* A list of chrecs. Access functions of the indices. */
|
||
vec<tree> access_fns;
|
||
|
||
/* Whether BASE_OBJECT is an access representing the whole object
|
||
or whether the access could not be constrained. */
|
||
bool unconstrained_base;
|
||
};
|
||
|
||
struct dr_alias
|
||
{
|
||
/* The alias information that should be used for new pointers to this
|
||
location. */
|
||
struct ptr_info_def *ptr_info;
|
||
};
|
||
|
||
/* An integer vector. A vector formally consists of an element of a vector
|
||
space. A vector space is a set that is closed under vector addition
|
||
and scalar multiplication. In this vector space, an element is a list of
|
||
integers. */
|
||
typedef HOST_WIDE_INT lambda_int;
|
||
typedef lambda_int *lambda_vector;
|
||
|
||
/* An integer matrix. A matrix consists of m vectors of length n (IE
|
||
all vectors are the same length). */
|
||
typedef lambda_vector *lambda_matrix;
|
||
|
||
|
||
|
||
struct data_reference
|
||
{
|
||
/* A pointer to the statement that contains this DR. */
|
||
gimple *stmt;
|
||
|
||
/* A pointer to the memory reference. */
|
||
tree ref;
|
||
|
||
/* Auxiliary info specific to a pass. */
|
||
void *aux;
|
||
|
||
/* True when the data reference is in RHS of a stmt. */
|
||
bool is_read;
|
||
|
||
/* True when the data reference is conditional within STMT,
|
||
i.e. if it might not occur even when the statement is executed
|
||
and runs to completion. */
|
||
bool is_conditional_in_stmt;
|
||
|
||
/* Alias information for the data reference. */
|
||
struct dr_alias alias;
|
||
|
||
/* Behavior of the memory reference in the innermost loop. */
|
||
struct innermost_loop_behavior innermost;
|
||
|
||
/* Subscripts of this data reference. */
|
||
struct indices indices;
|
||
|
||
/* Alternate subscripts initialized lazily and used by data-dependence
|
||
analysis only when the main indices of two DRs are not comparable.
|
||
Keep last to keep vec_info_shared::check_datarefs happy. */
|
||
struct indices alt_indices;
|
||
};
|
||
|
||
#define DR_STMT(DR) (DR)->stmt
|
||
#define DR_REF(DR) (DR)->ref
|
||
#define DR_BASE_OBJECT(DR) (DR)->indices.base_object
|
||
#define DR_UNCONSTRAINED_BASE(DR) (DR)->indices.unconstrained_base
|
||
#define DR_ACCESS_FNS(DR) (DR)->indices.access_fns
|
||
#define DR_ACCESS_FN(DR, I) DR_ACCESS_FNS (DR)[I]
|
||
#define DR_NUM_DIMENSIONS(DR) DR_ACCESS_FNS (DR).length ()
|
||
#define DR_IS_READ(DR) (DR)->is_read
|
||
#define DR_IS_WRITE(DR) (!DR_IS_READ (DR))
|
||
#define DR_IS_CONDITIONAL_IN_STMT(DR) (DR)->is_conditional_in_stmt
|
||
#define DR_BASE_ADDRESS(DR) (DR)->innermost.base_address
|
||
#define DR_OFFSET(DR) (DR)->innermost.offset
|
||
#define DR_INIT(DR) (DR)->innermost.init
|
||
#define DR_STEP(DR) (DR)->innermost.step
|
||
#define DR_PTR_INFO(DR) (DR)->alias.ptr_info
|
||
#define DR_BASE_ALIGNMENT(DR) (DR)->innermost.base_alignment
|
||
#define DR_BASE_MISALIGNMENT(DR) (DR)->innermost.base_misalignment
|
||
#define DR_OFFSET_ALIGNMENT(DR) (DR)->innermost.offset_alignment
|
||
#define DR_STEP_ALIGNMENT(DR) (DR)->innermost.step_alignment
|
||
#define DR_INNERMOST(DR) (DR)->innermost
|
||
|
||
typedef struct data_reference *data_reference_p;
|
||
|
||
/* This struct is used to store the information of a data reference,
|
||
including the data ref itself and the segment length for aliasing
|
||
checks. This is used to merge alias checks. */
|
||
|
||
class dr_with_seg_len
|
||
{
|
||
public:
|
||
dr_with_seg_len (data_reference_p d, tree len, unsigned HOST_WIDE_INT size,
|
||
unsigned int a)
|
||
: dr (d), seg_len (len), access_size (size), align (a) {}
|
||
|
||
data_reference_p dr;
|
||
/* The offset of the last access that needs to be checked minus
|
||
the offset of the first. */
|
||
tree seg_len;
|
||
/* A value that, when added to abs (SEG_LEN), gives the total number of
|
||
bytes in the segment. */
|
||
poly_uint64 access_size;
|
||
/* The minimum common alignment of DR's start address, SEG_LEN and
|
||
ACCESS_SIZE. */
|
||
unsigned int align;
|
||
};
|
||
|
||
/* Flags that describe a potential alias between two dr_with_seg_lens.
|
||
In general, each pair of dr_with_seg_lens represents a composite of
|
||
multiple access pairs P, so testing flags like DR_IS_READ on the DRs
|
||
does not give meaningful information.
|
||
|
||
DR_ALIAS_RAW:
|
||
There is a pair in P for which the second reference is a read
|
||
and the first is a write.
|
||
|
||
DR_ALIAS_WAR:
|
||
There is a pair in P for which the second reference is a write
|
||
and the first is a read.
|
||
|
||
DR_ALIAS_WAW:
|
||
There is a pair in P for which both references are writes.
|
||
|
||
DR_ALIAS_ARBITRARY:
|
||
Either
|
||
(a) it isn't possible to classify one pair in P as RAW, WAW or WAR; or
|
||
(b) there is a pair in P that breaks the ordering assumption below.
|
||
|
||
This flag overrides the RAW, WAR and WAW flags above.
|
||
|
||
DR_ALIAS_UNSWAPPED:
|
||
DR_ALIAS_SWAPPED:
|
||
Temporary flags that indicate whether there is a pair P whose
|
||
DRs have or haven't been swapped around.
|
||
|
||
DR_ALIAS_MIXED_STEPS:
|
||
The DR_STEP for one of the data references in the pair does not
|
||
accurately describe that reference for all members of P. (Note
|
||
that the flag does not say anything about whether the DR_STEPs
|
||
of the two references in the pair are the same.)
|
||
|
||
The ordering assumption mentioned above is that for every pair
|
||
(DR_A, DR_B) in P:
|
||
|
||
(1) The original code accesses n elements for DR_A and n elements for DR_B,
|
||
interleaved as follows:
|
||
|
||
one access of size DR_A.access_size at DR_A.dr
|
||
one access of size DR_B.access_size at DR_B.dr
|
||
one access of size DR_A.access_size at DR_A.dr + STEP_A
|
||
one access of size DR_B.access_size at DR_B.dr + STEP_B
|
||
one access of size DR_A.access_size at DR_A.dr + STEP_A * 2
|
||
one access of size DR_B.access_size at DR_B.dr + STEP_B * 2
|
||
...
|
||
|
||
(2) The new code accesses the same data in exactly two chunks:
|
||
|
||
one group of accesses spanning |DR_A.seg_len| + DR_A.access_size
|
||
one group of accesses spanning |DR_B.seg_len| + DR_B.access_size
|
||
|
||
A pair might break this assumption if the DR_A and DR_B accesses
|
||
in the original or the new code are mingled in some way. For example,
|
||
if DR_A.access_size represents the effect of two individual writes
|
||
to nearby locations, the pair breaks the assumption if those writes
|
||
occur either side of the access for DR_B.
|
||
|
||
Note that DR_ALIAS_ARBITRARY describes whether the ordering assumption
|
||
fails to hold for any individual pair in P. If the assumption *does*
|
||
hold for every pair in P, it doesn't matter whether it holds for the
|
||
composite pair or not. In other words, P should represent the complete
|
||
set of pairs that the composite pair is testing, so only the ordering
|
||
of two accesses in the same member of P matters. */
|
||
const unsigned int DR_ALIAS_RAW = 1U << 0;
|
||
const unsigned int DR_ALIAS_WAR = 1U << 1;
|
||
const unsigned int DR_ALIAS_WAW = 1U << 2;
|
||
const unsigned int DR_ALIAS_ARBITRARY = 1U << 3;
|
||
const unsigned int DR_ALIAS_SWAPPED = 1U << 4;
|
||
const unsigned int DR_ALIAS_UNSWAPPED = 1U << 5;
|
||
const unsigned int DR_ALIAS_MIXED_STEPS = 1U << 6;
|
||
|
||
/* This struct contains two dr_with_seg_len objects with aliasing data
|
||
refs. Two comparisons are generated from them. */
|
||
|
||
class dr_with_seg_len_pair_t
|
||
{
|
||
public:
|
||
/* WELL_ORDERED indicates that the ordering assumption described above
|
||
DR_ALIAS_ARBITRARY holds. REORDERED indicates that it doesn't. */
|
||
enum sequencing { WELL_ORDERED, REORDERED };
|
||
|
||
dr_with_seg_len_pair_t (const dr_with_seg_len &,
|
||
const dr_with_seg_len &, sequencing);
|
||
|
||
dr_with_seg_len first;
|
||
dr_with_seg_len second;
|
||
unsigned int flags;
|
||
};
|
||
|
||
inline dr_with_seg_len_pair_t::
|
||
dr_with_seg_len_pair_t (const dr_with_seg_len &d1, const dr_with_seg_len &d2,
|
||
sequencing seq)
|
||
: first (d1), second (d2), flags (0)
|
||
{
|
||
if (DR_IS_READ (d1.dr) && DR_IS_WRITE (d2.dr))
|
||
flags |= DR_ALIAS_WAR;
|
||
else if (DR_IS_WRITE (d1.dr) && DR_IS_READ (d2.dr))
|
||
flags |= DR_ALIAS_RAW;
|
||
else if (DR_IS_WRITE (d1.dr) && DR_IS_WRITE (d2.dr))
|
||
flags |= DR_ALIAS_WAW;
|
||
else
|
||
gcc_unreachable ();
|
||
if (seq == REORDERED)
|
||
flags |= DR_ALIAS_ARBITRARY;
|
||
}
|
||
|
||
enum data_dependence_direction {
|
||
dir_positive,
|
||
dir_negative,
|
||
dir_equal,
|
||
dir_positive_or_negative,
|
||
dir_positive_or_equal,
|
||
dir_negative_or_equal,
|
||
dir_star,
|
||
dir_independent
|
||
};
|
||
|
||
/* The description of the grid of iterations that overlap. At most
|
||
two loops are considered at the same time just now, hence at most
|
||
two functions are needed. For each of the functions, we store
|
||
the vector of coefficients, f[0] + x * f[1] + y * f[2] + ...,
|
||
where x, y, ... are variables. */
|
||
|
||
#define MAX_DIM 2
|
||
|
||
/* Special values of N. */
|
||
#define NO_DEPENDENCE 0
|
||
#define NOT_KNOWN (MAX_DIM + 1)
|
||
#define CF_NONTRIVIAL_P(CF) ((CF)->n != NO_DEPENDENCE && (CF)->n != NOT_KNOWN)
|
||
#define CF_NOT_KNOWN_P(CF) ((CF)->n == NOT_KNOWN)
|
||
#define CF_NO_DEPENDENCE_P(CF) ((CF)->n == NO_DEPENDENCE)
|
||
|
||
typedef vec<tree> affine_fn;
|
||
|
||
struct conflict_function
|
||
{
|
||
unsigned n;
|
||
affine_fn fns[MAX_DIM];
|
||
};
|
||
|
||
/* What is a subscript? Given two array accesses a subscript is the
|
||
tuple composed of the access functions for a given dimension.
|
||
Example: Given A[f1][f2][f3] and B[g1][g2][g3], there are three
|
||
subscripts: (f1, g1), (f2, g2), (f3, g3). These three subscripts
|
||
are stored in the data_dependence_relation structure under the form
|
||
of an array of subscripts. */
|
||
|
||
struct subscript
|
||
{
|
||
/* The access functions of the two references. */
|
||
tree access_fn[2];
|
||
|
||
/* A description of the iterations for which the elements are
|
||
accessed twice. */
|
||
conflict_function *conflicting_iterations_in_a;
|
||
conflict_function *conflicting_iterations_in_b;
|
||
|
||
/* This field stores the information about the iteration domain
|
||
validity of the dependence relation. */
|
||
tree last_conflict;
|
||
|
||
/* Distance from the iteration that access a conflicting element in
|
||
A to the iteration that access this same conflicting element in
|
||
B. The distance is a tree scalar expression, i.e. a constant or a
|
||
symbolic expression, but certainly not a chrec function. */
|
||
tree distance;
|
||
};
|
||
|
||
typedef struct subscript *subscript_p;
|
||
|
||
#define SUB_ACCESS_FN(SUB, I) (SUB)->access_fn[I]
|
||
#define SUB_CONFLICTS_IN_A(SUB) (SUB)->conflicting_iterations_in_a
|
||
#define SUB_CONFLICTS_IN_B(SUB) (SUB)->conflicting_iterations_in_b
|
||
#define SUB_LAST_CONFLICT(SUB) (SUB)->last_conflict
|
||
#define SUB_DISTANCE(SUB) (SUB)->distance
|
||
|
||
/* A data_dependence_relation represents a relation between two
|
||
data_references A and B. */
|
||
|
||
struct data_dependence_relation
|
||
{
|
||
|
||
struct data_reference *a;
|
||
struct data_reference *b;
|
||
|
||
/* A "yes/no/maybe" field for the dependence relation:
|
||
|
||
- when "ARE_DEPENDENT == NULL_TREE", there exist a dependence
|
||
relation between A and B, and the description of this relation
|
||
is given in the SUBSCRIPTS array,
|
||
|
||
- when "ARE_DEPENDENT == chrec_known", there is no dependence and
|
||
SUBSCRIPTS is empty,
|
||
|
||
- when "ARE_DEPENDENT == chrec_dont_know", there may be a dependence,
|
||
but the analyzer cannot be more specific. */
|
||
tree are_dependent;
|
||
|
||
/* If nonnull, COULD_BE_INDEPENDENT_P is true and the accesses are
|
||
independent when the runtime addresses of OBJECT_A and OBJECT_B
|
||
are different. The addresses of both objects are invariant in the
|
||
loop nest. */
|
||
tree object_a;
|
||
tree object_b;
|
||
|
||
/* For each subscript in the dependence test, there is an element in
|
||
this array. This is the attribute that labels the edge A->B of
|
||
the data_dependence_relation. */
|
||
vec<subscript_p> subscripts;
|
||
|
||
/* The analyzed loop nest. */
|
||
vec<loop_p> loop_nest;
|
||
|
||
/* The classic direction vector. */
|
||
vec<lambda_vector> dir_vects;
|
||
|
||
/* The classic distance vector. */
|
||
vec<lambda_vector> dist_vects;
|
||
|
||
/* Is the dependence reversed with respect to the lexicographic order? */
|
||
bool reversed_p;
|
||
|
||
/* When the dependence relation is affine, it can be represented by
|
||
a distance vector. */
|
||
bool affine_p;
|
||
|
||
/* Set to true when the dependence relation is on the same data
|
||
access. */
|
||
bool self_reference_p;
|
||
|
||
/* True if the dependence described is conservatively correct rather
|
||
than exact, and if it is still possible for the accesses to be
|
||
conditionally independent. For example, the a and b references in:
|
||
|
||
struct s *a, *b;
|
||
for (int i = 0; i < n; ++i)
|
||
a->f[i] += b->f[i];
|
||
|
||
conservatively have a distance vector of (0), for the case in which
|
||
a == b, but the accesses are independent if a != b. Similarly,
|
||
the a and b references in:
|
||
|
||
struct s *a, *b;
|
||
for (int i = 0; i < n; ++i)
|
||
a[0].f[i] += b[i].f[i];
|
||
|
||
conservatively have a distance vector of (0), but they are indepenent
|
||
when a != b + i. In contrast, the references in:
|
||
|
||
struct s *a;
|
||
for (int i = 0; i < n; ++i)
|
||
a->f[i] += a->f[i];
|
||
|
||
have the same distance vector of (0), but the accesses can never be
|
||
independent. */
|
||
bool could_be_independent_p;
|
||
};
|
||
|
||
typedef struct data_dependence_relation *ddr_p;
|
||
|
||
#define DDR_A(DDR) (DDR)->a
|
||
#define DDR_B(DDR) (DDR)->b
|
||
#define DDR_AFFINE_P(DDR) (DDR)->affine_p
|
||
#define DDR_ARE_DEPENDENT(DDR) (DDR)->are_dependent
|
||
#define DDR_OBJECT_A(DDR) (DDR)->object_a
|
||
#define DDR_OBJECT_B(DDR) (DDR)->object_b
|
||
#define DDR_SUBSCRIPTS(DDR) (DDR)->subscripts
|
||
#define DDR_SUBSCRIPT(DDR, I) DDR_SUBSCRIPTS (DDR)[I]
|
||
#define DDR_NUM_SUBSCRIPTS(DDR) DDR_SUBSCRIPTS (DDR).length ()
|
||
|
||
#define DDR_LOOP_NEST(DDR) (DDR)->loop_nest
|
||
/* The size of the direction/distance vectors: the number of loops in
|
||
the loop nest. */
|
||
#define DDR_NB_LOOPS(DDR) (DDR_LOOP_NEST (DDR).length ())
|
||
#define DDR_SELF_REFERENCE(DDR) (DDR)->self_reference_p
|
||
|
||
#define DDR_DIST_VECTS(DDR) ((DDR)->dist_vects)
|
||
#define DDR_DIR_VECTS(DDR) ((DDR)->dir_vects)
|
||
#define DDR_NUM_DIST_VECTS(DDR) \
|
||
(DDR_DIST_VECTS (DDR).length ())
|
||
#define DDR_NUM_DIR_VECTS(DDR) \
|
||
(DDR_DIR_VECTS (DDR).length ())
|
||
#define DDR_DIR_VECT(DDR, I) \
|
||
DDR_DIR_VECTS (DDR)[I]
|
||
#define DDR_DIST_VECT(DDR, I) \
|
||
DDR_DIST_VECTS (DDR)[I]
|
||
#define DDR_REVERSED_P(DDR) (DDR)->reversed_p
|
||
#define DDR_COULD_BE_INDEPENDENT_P(DDR) (DDR)->could_be_independent_p
|
||
|
||
|
||
opt_result dr_analyze_innermost (innermost_loop_behavior *, tree,
|
||
class loop *, const gimple *);
|
||
extern bool compute_data_dependences_for_loop (class loop *, bool,
|
||
vec<loop_p> *,
|
||
vec<data_reference_p> *,
|
||
vec<ddr_p> *);
|
||
extern void debug_ddrs (vec<ddr_p> );
|
||
extern void dump_data_reference (FILE *, struct data_reference *);
|
||
extern void debug (data_reference &ref);
|
||
extern void debug (data_reference *ptr);
|
||
extern void debug_data_reference (struct data_reference *);
|
||
extern void debug_data_references (vec<data_reference_p> );
|
||
extern void debug (vec<data_reference_p> &ref);
|
||
extern void debug (vec<data_reference_p> *ptr);
|
||
extern void debug_data_dependence_relation (const data_dependence_relation *);
|
||
extern void dump_data_dependence_relations (FILE *, const vec<ddr_p> &);
|
||
extern void debug (vec<ddr_p> &ref);
|
||
extern void debug (vec<ddr_p> *ptr);
|
||
extern void debug_data_dependence_relations (vec<ddr_p> );
|
||
extern void free_dependence_relation (struct data_dependence_relation *);
|
||
extern void free_dependence_relations (vec<ddr_p>& );
|
||
extern void free_data_ref (data_reference_p);
|
||
extern void free_data_refs (vec<data_reference_p>& );
|
||
extern opt_result find_data_references_in_stmt (class loop *, gimple *,
|
||
vec<data_reference_p> *);
|
||
extern bool graphite_find_data_references_in_stmt (edge, loop_p, gimple *,
|
||
vec<data_reference_p> *);
|
||
tree find_data_references_in_loop (class loop *, vec<data_reference_p> *);
|
||
bool loop_nest_has_data_refs (loop_p loop);
|
||
struct data_reference *create_data_ref (edge, loop_p, tree, gimple *, bool,
|
||
bool);
|
||
extern bool find_loop_nest (class loop *, vec<loop_p> *);
|
||
extern struct data_dependence_relation *initialize_data_dependence_relation
|
||
(struct data_reference *, struct data_reference *, vec<loop_p>);
|
||
extern void compute_affine_dependence (struct data_dependence_relation *,
|
||
loop_p);
|
||
extern void compute_self_dependence (struct data_dependence_relation *);
|
||
extern bool compute_all_dependences (const vec<data_reference_p> &,
|
||
vec<ddr_p> *,
|
||
const vec<loop_p> &, bool);
|
||
extern tree find_data_references_in_bb (class loop *, basic_block,
|
||
vec<data_reference_p> *);
|
||
extern unsigned int dr_alignment (innermost_loop_behavior *);
|
||
extern tree get_base_for_alignment (tree, unsigned int *);
|
||
|
||
/* Return the alignment in bytes that DR is guaranteed to have at all
|
||
times. */
|
||
|
||
inline unsigned int
|
||
dr_alignment (data_reference *dr)
|
||
{
|
||
return dr_alignment (&DR_INNERMOST (dr));
|
||
}
|
||
|
||
extern bool dr_may_alias_p (const struct data_reference *,
|
||
const struct data_reference *, class loop *);
|
||
extern bool dr_equal_offsets_p (struct data_reference *,
|
||
struct data_reference *);
|
||
|
||
extern opt_result runtime_alias_check_p (ddr_p, class loop *, bool);
|
||
extern int data_ref_compare_tree (tree, tree);
|
||
extern void prune_runtime_alias_test_list (vec<dr_with_seg_len_pair_t> *,
|
||
poly_uint64);
|
||
extern void create_runtime_alias_checks (class loop *,
|
||
const vec<dr_with_seg_len_pair_t> *,
|
||
tree*);
|
||
extern tree dr_direction_indicator (struct data_reference *);
|
||
extern tree dr_zero_step_indicator (struct data_reference *);
|
||
extern bool dr_known_forward_stride_p (struct data_reference *);
|
||
|
||
/* Return true when the base objects of data references A and B are
|
||
the same memory object. */
|
||
|
||
inline bool
|
||
same_data_refs_base_objects (data_reference_p a, data_reference_p b)
|
||
{
|
||
return DR_NUM_DIMENSIONS (a) == DR_NUM_DIMENSIONS (b)
|
||
&& operand_equal_p (DR_BASE_OBJECT (a), DR_BASE_OBJECT (b), 0);
|
||
}
|
||
|
||
/* Return true when the data references A and B are accessing the same
|
||
memory object with the same access functions. Optionally skip the
|
||
last OFFSET dimensions in the data reference. */
|
||
|
||
inline bool
|
||
same_data_refs (data_reference_p a, data_reference_p b, int offset = 0)
|
||
{
|
||
unsigned int i;
|
||
|
||
/* The references are exactly the same. */
|
||
if (operand_equal_p (DR_REF (a), DR_REF (b), 0))
|
||
return true;
|
||
|
||
if (!same_data_refs_base_objects (a, b))
|
||
return false;
|
||
|
||
for (i = offset; i < DR_NUM_DIMENSIONS (a); i++)
|
||
if (!eq_evolutions_p (DR_ACCESS_FN (a, i), DR_ACCESS_FN (b, i)))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Returns true when all the dependences are computable. */
|
||
|
||
inline bool
|
||
known_dependences_p (vec<ddr_p> dependence_relations)
|
||
{
|
||
ddr_p ddr;
|
||
unsigned int i;
|
||
|
||
FOR_EACH_VEC_ELT (dependence_relations, i, ddr)
|
||
if (DDR_ARE_DEPENDENT (ddr) == chrec_dont_know)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Returns the dependence level for a vector DIST of size LENGTH.
|
||
LEVEL = 0 means a lexicographic dependence, i.e. a dependence due
|
||
to the sequence of statements, not carried by any loop. */
|
||
|
||
inline unsigned
|
||
dependence_level (lambda_vector dist_vect, int length)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < length; i++)
|
||
if (dist_vect[i] != 0)
|
||
return i + 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Return the dependence level for the DDR relation. */
|
||
|
||
inline unsigned
|
||
ddr_dependence_level (ddr_p ddr)
|
||
{
|
||
unsigned vector;
|
||
unsigned level = 0;
|
||
|
||
if (DDR_DIST_VECTS (ddr).exists ())
|
||
level = dependence_level (DDR_DIST_VECT (ddr, 0), DDR_NB_LOOPS (ddr));
|
||
|
||
for (vector = 1; vector < DDR_NUM_DIST_VECTS (ddr); vector++)
|
||
level = MIN (level, dependence_level (DDR_DIST_VECT (ddr, vector),
|
||
DDR_NB_LOOPS (ddr)));
|
||
return level;
|
||
}
|
||
|
||
/* Return the index of the variable VAR in the LOOP_NEST array. */
|
||
|
||
inline int
|
||
index_in_loop_nest (int var, const vec<loop_p> &loop_nest)
|
||
{
|
||
class loop *loopi;
|
||
int var_index;
|
||
|
||
for (var_index = 0; loop_nest.iterate (var_index, &loopi); var_index++)
|
||
if (loopi->num == var)
|
||
return var_index;
|
||
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
/* Returns true when the data reference DR the form "A[i] = ..."
|
||
with a stride equal to its unit type size. */
|
||
|
||
inline bool
|
||
adjacent_dr_p (struct data_reference *dr)
|
||
{
|
||
/* If this is a bitfield store bail out. */
|
||
if (TREE_CODE (DR_REF (dr)) == COMPONENT_REF
|
||
&& DECL_BIT_FIELD (TREE_OPERAND (DR_REF (dr), 1)))
|
||
return false;
|
||
|
||
if (!DR_STEP (dr)
|
||
|| TREE_CODE (DR_STEP (dr)) != INTEGER_CST)
|
||
return false;
|
||
|
||
return tree_int_cst_equal (fold_unary (ABS_EXPR, TREE_TYPE (DR_STEP (dr)),
|
||
DR_STEP (dr)),
|
||
TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (dr))));
|
||
}
|
||
|
||
void split_constant_offset (tree , tree *, tree *);
|
||
|
||
/* Compute the greatest common divisor of a VECTOR of SIZE numbers. */
|
||
|
||
inline lambda_int
|
||
lambda_vector_gcd (lambda_vector vector, int size)
|
||
{
|
||
int i;
|
||
lambda_int gcd1 = 0;
|
||
|
||
if (size > 0)
|
||
{
|
||
gcd1 = vector[0];
|
||
for (i = 1; i < size; i++)
|
||
gcd1 = gcd (gcd1, vector[i]);
|
||
}
|
||
return gcd1;
|
||
}
|
||
|
||
/* Allocate a new vector of given SIZE. */
|
||
|
||
inline lambda_vector
|
||
lambda_vector_new (int size)
|
||
{
|
||
/* ??? We shouldn't abuse the GC allocator here. */
|
||
return ggc_cleared_vec_alloc<lambda_int> (size);
|
||
}
|
||
|
||
/* Clear out vector VEC1 of length SIZE. */
|
||
|
||
inline void
|
||
lambda_vector_clear (lambda_vector vec1, int size)
|
||
{
|
||
memset (vec1, 0, size * sizeof (*vec1));
|
||
}
|
||
|
||
/* Returns true when the vector V is lexicographically positive, in
|
||
other words, when the first nonzero element is positive. */
|
||
|
||
inline bool
|
||
lambda_vector_lexico_pos (lambda_vector v,
|
||
unsigned n)
|
||
{
|
||
unsigned i;
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
if (v[i] == 0)
|
||
continue;
|
||
if (v[i] < 0)
|
||
return false;
|
||
if (v[i] > 0)
|
||
return true;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/* Return true if vector VEC1 of length SIZE is the zero vector. */
|
||
|
||
inline bool
|
||
lambda_vector_zerop (lambda_vector vec1, int size)
|
||
{
|
||
int i;
|
||
for (i = 0; i < size; i++)
|
||
if (vec1[i] != 0)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
/* Allocate a matrix of M rows x N cols. */
|
||
|
||
inline lambda_matrix
|
||
lambda_matrix_new (int m, int n, struct obstack *lambda_obstack)
|
||
{
|
||
lambda_matrix mat;
|
||
int i;
|
||
|
||
mat = XOBNEWVEC (lambda_obstack, lambda_vector, m);
|
||
|
||
for (i = 0; i < m; i++)
|
||
mat[i] = XOBNEWVEC (lambda_obstack, lambda_int, n);
|
||
|
||
return mat;
|
||
}
|
||
|
||
#endif /* GCC_TREE_DATA_REF_H */
|