2017-08-23 05:25:51 +08:00
|
|
|
//===- ScopInfo.cpp -------------------------------------------------------===//
|
2011-04-29 14:27:02 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2011-04-29 14:27:02 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Create a polyhedral description for a static control flow region.
|
|
|
|
//
|
|
|
|
// The pass creates a polyhedral description of the Scops detected by the Scop
|
|
|
|
// detection derived from their LLVM-IR code.
|
|
|
|
//
|
2014-10-30 03:58:28 +08:00
|
|
|
// This representation is shared among several tools in the polyhedral
|
2011-04-29 14:27:02 +08:00
|
|
|
// community, which are e.g. Cloog, Pluto, Loopo, Graphite.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-12-21 20:38:56 +08:00
|
|
|
#include "polly/ScopInfo.h"
|
2011-04-29 14:27:02 +08:00
|
|
|
#include "polly/LinkAllPasses.h"
|
2014-06-18 01:31:36 +08:00
|
|
|
#include "polly/Options.h"
|
2016-06-28 09:37:28 +08:00
|
|
|
#include "polly/ScopBuilder.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "polly/ScopDetection.h"
|
2011-04-29 14:27:02 +08:00
|
|
|
#include "polly/Support/GICHelper.h"
|
2017-07-22 07:07:56 +08:00
|
|
|
#include "polly/Support/ISLOStream.h"
|
2018-05-10 00:23:56 +08:00
|
|
|
#include "polly/Support/ISLTools.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "polly/Support/SCEVAffinator.h"
|
2011-11-08 23:41:28 +08:00
|
|
|
#include "polly/Support/SCEVValidator.h"
|
2013-05-07 16:11:54 +08:00
|
|
|
#include "polly/Support/ScopHelper.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/ADT/APInt.h"
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
2015-09-25 17:49:19 +08:00
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
|
|
#include "llvm/ADT/SmallSet.h"
|
2013-05-07 16:11:54 +08:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
2014-09-18 19:17:17 +08:00
|
|
|
#include "llvm/Analysis/AliasAnalysis.h"
|
2017-03-17 21:56:53 +08:00
|
|
|
#include "llvm/Analysis/AssumptionCache.h"
|
2016-04-23 20:59:18 +08:00
|
|
|
#include "llvm/Analysis/Loads.h"
|
2015-05-09 17:13:42 +08:00
|
|
|
#include "llvm/Analysis/LoopInfo.h"
|
2017-10-10 07:49:08 +08:00
|
|
|
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/Analysis/RegionInfo.h"
|
2011-04-29 14:27:02 +08:00
|
|
|
#include "llvm/Analysis/RegionIterator.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/Analysis/ScalarEvolution.h"
|
2013-05-07 16:11:54 +08:00
|
|
|
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/IR/BasicBlock.h"
|
|
|
|
#include "llvm/IR/ConstantRange.h"
|
|
|
|
#include "llvm/IR/DataLayout.h"
|
|
|
|
#include "llvm/IR/DebugLoc.h"
|
|
|
|
#include "llvm/IR/Dominators.h"
|
|
|
|
#include "llvm/IR/Function.h"
|
|
|
|
#include "llvm/IR/InstrTypes.h"
|
|
|
|
#include "llvm/IR/Instruction.h"
|
|
|
|
#include "llvm/IR/Instructions.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "llvm/IR/PassManager.h"
|
|
|
|
#include "llvm/IR/Type.h"
|
|
|
|
#include "llvm/IR/Value.h"
|
|
|
|
#include "llvm/Support/Compiler.h"
|
2011-04-29 14:27:02 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2011-08-18 14:31:50 +08:00
|
|
|
#include "isl/aff.h"
|
2011-10-06 08:03:35 +08:00
|
|
|
#include "isl/local_space.h"
|
2015-05-09 17:13:42 +08:00
|
|
|
#include "isl/map.h"
|
2011-12-07 15:42:51 +08:00
|
|
|
#include "isl/options.h"
|
2015-05-09 17:13:42 +08:00
|
|
|
#include "isl/set.h"
|
2017-08-23 05:25:51 +08:00
|
|
|
#include <cassert>
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace polly;
|
|
|
|
|
2014-04-22 11:30:19 +08:00
|
|
|
#define DEBUG_TYPE "polly-scops"
|
|
|
|
|
2016-11-18 22:37:08 +08:00
|
|
|
STATISTIC(AssumptionsAliasing, "Number of aliasing assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsInbounds, "Number of inbounds assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsWrapping, "Number of wrapping assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsUnsigned, "Number of unsigned assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsComplexity, "Number of too complex SCoPs.");
|
|
|
|
STATISTIC(AssumptionsUnprofitable, "Number of unprofitable SCoPs.");
|
|
|
|
STATISTIC(AssumptionsErrorBlock, "Number of error block assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsInfiniteLoop, "Number of bounded loop assumptions taken.");
|
|
|
|
STATISTIC(AssumptionsInvariantLoad,
|
2016-11-18 05:41:08 +08:00
|
|
|
"Number of invariant loads assumptions taken.");
|
2016-11-18 22:37:08 +08:00
|
|
|
STATISTIC(AssumptionsDelinearization,
|
2016-11-18 05:41:08 +08:00
|
|
|
"Number of delinearization assumptions taken.");
|
|
|
|
|
2017-08-23 21:50:30 +08:00
|
|
|
STATISTIC(NumScops, "Number of feasible SCoPs after ScopInfo");
|
2017-02-17 16:12:36 +08:00
|
|
|
STATISTIC(NumLoopsInScop, "Number of loops in scops");
|
2017-08-23 21:50:30 +08:00
|
|
|
STATISTIC(NumBoxedLoops, "Number of boxed loops in SCoPs after ScopInfo");
|
|
|
|
STATISTIC(NumAffineLoops, "Number of affine loops in SCoPs after ScopInfo");
|
|
|
|
|
2018-04-19 04:03:36 +08:00
|
|
|
STATISTIC(NumScopsDepthZero, "Number of scops with maximal loop depth 0");
|
2017-02-17 16:12:36 +08:00
|
|
|
STATISTIC(NumScopsDepthOne, "Number of scops with maximal loop depth 1");
|
|
|
|
STATISTIC(NumScopsDepthTwo, "Number of scops with maximal loop depth 2");
|
|
|
|
STATISTIC(NumScopsDepthThree, "Number of scops with maximal loop depth 3");
|
|
|
|
STATISTIC(NumScopsDepthFour, "Number of scops with maximal loop depth 4");
|
|
|
|
STATISTIC(NumScopsDepthFive, "Number of scops with maximal loop depth 5");
|
|
|
|
STATISTIC(NumScopsDepthLarger,
|
|
|
|
"Number of scops with maximal loop depth 6 and larger");
|
|
|
|
STATISTIC(MaxNumLoopsInScop, "Maximal number of loops in scops");
|
|
|
|
|
2017-08-23 21:50:30 +08:00
|
|
|
STATISTIC(NumValueWrites, "Number of scalar value writes after ScopInfo");
|
|
|
|
STATISTIC(
|
|
|
|
NumValueWritesInLoops,
|
|
|
|
"Number of scalar value writes nested in affine loops after ScopInfo");
|
|
|
|
STATISTIC(NumPHIWrites, "Number of scalar phi writes after ScopInfo");
|
|
|
|
STATISTIC(NumPHIWritesInLoops,
|
|
|
|
"Number of scalar phi writes nested in affine loops after ScopInfo");
|
|
|
|
STATISTIC(NumSingletonWrites, "Number of singleton writes after ScopInfo");
|
|
|
|
STATISTIC(NumSingletonWritesInLoops,
|
|
|
|
"Number of singleton writes nested in affine loops after ScopInfo");
|
|
|
|
|
2019-06-13 06:40:08 +08:00
|
|
|
int const polly::MaxDisjunctsInDomain = 20;
|
2015-12-20 21:31:48 +08:00
|
|
|
|
2017-02-17 03:11:25 +08:00
|
|
|
// The number of disjunct in the context after which we stop to add more
|
|
|
|
// disjuncts. This parameter is there to avoid exponential growth in the
|
|
|
|
// number of disjunct when adding non-convex sets to the context.
|
|
|
|
static int const MaxDisjunctsInContext = 4;
|
|
|
|
|
2017-05-19 12:01:52 +08:00
|
|
|
static cl::opt<int>
|
|
|
|
OptComputeOut("polly-analysis-computeout",
|
|
|
|
cl::desc("Bound the scop analysis by a maximal amount of "
|
|
|
|
"computational steps (0 means no bound)"),
|
2017-06-23 16:05:27 +08:00
|
|
|
cl::Hidden, cl::init(800000), cl::ZeroOrMore,
|
2017-05-19 12:01:52 +08:00
|
|
|
cl::cat(PollyCategory));
|
2017-05-19 11:45:00 +08:00
|
|
|
|
2016-04-13 00:09:44 +08:00
|
|
|
static cl::opt<bool> PollyRemarksMinimal(
|
|
|
|
"polly-remarks-minimal",
|
|
|
|
cl::desc("Do not emit remarks about assumptions that are known"),
|
|
|
|
cl::Hidden, cl::ZeroOrMore, cl::init(false), cl::cat(PollyCategory));
|
|
|
|
|
2017-06-25 14:32:00 +08:00
|
|
|
static cl::opt<int> RunTimeChecksMaxAccessDisjuncts(
|
|
|
|
"polly-rtc-max-array-disjuncts",
|
|
|
|
cl::desc("The maximal number of disjunts allowed in memory accesses to "
|
|
|
|
"to build RTCs."),
|
|
|
|
cl::Hidden, cl::ZeroOrMore, cl::init(8), cl::cat(PollyCategory));
|
|
|
|
|
2014-09-27 19:02:39 +08:00
|
|
|
static cl::opt<unsigned> RunTimeChecksMaxParameters(
|
|
|
|
"polly-rtc-max-parameters",
|
|
|
|
cl::desc("The maximal number of parameters allowed in RTCs."), cl::Hidden,
|
|
|
|
cl::ZeroOrMore, cl::init(8), cl::cat(PollyCategory));
|
|
|
|
|
2015-03-28 23:11:14 +08:00
|
|
|
static cl::opt<unsigned> RunTimeChecksMaxArraysPerGroup(
|
|
|
|
"polly-rtc-max-arrays-per-group",
|
|
|
|
cl::desc("The maximal number of arrays to compare in each alias group."),
|
|
|
|
cl::Hidden, cl::ZeroOrMore, cl::init(20), cl::cat(PollyCategory));
|
2016-06-02 19:06:54 +08:00
|
|
|
|
2015-08-16 18:19:29 +08:00
|
|
|
static cl::opt<std::string> UserContextStr(
|
|
|
|
"polly-context", cl::value_desc("isl parameter set"),
|
|
|
|
cl::desc("Provide additional constraints on the context parameters"),
|
|
|
|
cl::init(""), cl::cat(PollyCategory));
|
2015-03-28 23:11:14 +08:00
|
|
|
|
2016-04-29 19:43:20 +08:00
|
|
|
static cl::opt<bool>
|
|
|
|
IslOnErrorAbort("polly-on-isl-error-abort",
|
|
|
|
cl::desc("Abort if an isl error is encountered"),
|
|
|
|
cl::init(true), cl::cat(PollyCategory));
|
|
|
|
|
[ScopInfo] Simplify inbounds assumptions under domain constraints
Without this simplification for a loop nest:
void foo(long n1_a, long n1_b, long n1_c, long n1_d,
long p1_b, long p1_c, long p1_d,
float A_1[][p1_b][p1_c][p1_d]) {
for (long i = 0; i < n1_a; i++)
for (long j = 0; j < n1_b; j++)
for (long k = 0; k < n1_c; k++)
for (long l = 0; l < n1_d; l++)
A_1[i][j][k][l] += i + j + k + l;
}
the assumption:
n1_a <= 0 or (n1_a > 0 and n1_b <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c > 0 and n1_d <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c > 0 and n1_d > 0 and
p1_b >= n1_b and p1_c >= n1_c and p1_d >= n1_d)
is taken rather than the simpler assumption:
p9_b >= n9_b and p9_c >= n9_c and p9_d >= n9_d.
The former is less strict, as it allows arbitrary values of p1_* in case, the
loop is not executed at all. However, in practice these precise constraints
explode when combined across different accesses and loops. For now it seems
to make more sense to take less precise, but more scalable constraints by
default. In case we find a practical example where more precise constraints
are needed, we can think about allowing such precise constraints in specific
situations where they help.
This change speeds up the new test case from taking very long (waited at least
a minute, but it probably takes a lot more) to below a second.
llvm-svn: 296456
2017-02-28 17:45:54 +08:00
|
|
|
static cl::opt<bool> PollyPreciseInbounds(
|
|
|
|
"polly-precise-inbounds",
|
|
|
|
cl::desc("Take more precise inbounds assumptions (do not scale well)"),
|
|
|
|
cl::Hidden, cl::init(false), cl::cat(PollyCategory));
|
|
|
|
|
2017-03-17 20:26:58 +08:00
|
|
|
static cl::opt<bool>
|
|
|
|
PollyIgnoreInbounds("polly-ignore-inbounds",
|
|
|
|
cl::desc("Do not take inbounds assumptions at all"),
|
|
|
|
cl::Hidden, cl::init(false), cl::cat(PollyCategory));
|
|
|
|
|
2017-03-17 21:00:53 +08:00
|
|
|
static cl::opt<bool> PollyIgnoreParamBounds(
|
|
|
|
"polly-ignore-parameter-bounds",
|
|
|
|
cl::desc(
|
|
|
|
"Do not add parameter bounds and do no gist simplify sets accordingly"),
|
|
|
|
cl::Hidden, cl::init(false), cl::cat(PollyCategory));
|
|
|
|
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
static cl::opt<bool> PollyPreciseFoldAccesses(
|
|
|
|
"polly-precise-fold-accesses",
|
2017-04-03 20:03:38 +08:00
|
|
|
cl::desc("Fold memory accesses to model more possible delinearizations "
|
|
|
|
"(does not scale well)"),
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
cl::Hidden, cl::init(false), cl::cat(PollyCategory));
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
|
2017-05-06 22:03:58 +08:00
|
|
|
bool polly::UseInstructionNames;
|
2017-08-23 05:25:51 +08:00
|
|
|
|
2017-05-06 22:03:58 +08:00
|
|
|
static cl::opt<bool, true> XUseInstructionNames(
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
"polly-use-llvm-names",
|
2017-05-06 22:03:58 +08:00
|
|
|
cl::desc("Use LLVM-IR names when deriving statement names"),
|
|
|
|
cl::location(UseInstructionNames), cl::Hidden, cl::init(false),
|
|
|
|
cl::ZeroOrMore, cl::cat(PollyCategory));
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
|
2017-05-27 12:40:18 +08:00
|
|
|
static cl::opt<bool> PollyPrintInstructions(
|
|
|
|
"polly-print-instructions", cl::desc("Output instructions per ScopStmt"),
|
|
|
|
cl::Hidden, cl::Optional, cl::init(false), cl::cat(PollyCategory));
|
|
|
|
|
2015-09-10 20:46:52 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-08-10 21:01:57 +08:00
|
|
|
// Create a sequence of two schedules. Either argument may be null and is
|
|
|
|
// interpreted as the empty schedule. Can also return null if both schedules are
|
|
|
|
// empty.
|
2017-11-20 06:13:34 +08:00
|
|
|
static isl::schedule combineInSequence(isl::schedule Prev, isl::schedule Succ) {
|
2015-08-10 21:01:57 +08:00
|
|
|
if (!Prev)
|
|
|
|
return Succ;
|
|
|
|
if (!Succ)
|
|
|
|
return Prev;
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
return Prev.sequence(Succ);
|
2015-08-10 21:01:57 +08:00
|
|
|
}
|
|
|
|
|
2017-05-22 04:23:20 +08:00
|
|
|
static isl::set addRangeBoundsToSet(isl::set S, const ConstantRange &Range,
|
|
|
|
int dim, isl::dim type) {
|
|
|
|
isl::val V;
|
|
|
|
isl::ctx Ctx = S.get_ctx();
|
2015-02-24 19:58:30 +08:00
|
|
|
|
2017-02-17 02:39:14 +08:00
|
|
|
// The upper and lower bound for a parameter value is derived either from
|
|
|
|
// the data type of the parameter or from the - possibly more restrictive -
|
|
|
|
// range metadata.
|
2017-05-22 04:23:20 +08:00
|
|
|
V = valFromAPInt(Ctx.get(), Range.getSignedMin(), true);
|
|
|
|
S = S.lower_bound_val(type, dim, V);
|
|
|
|
V = valFromAPInt(Ctx.get(), Range.getSignedMax(), true);
|
|
|
|
S = S.upper_bound_val(type, dim, V);
|
2017-02-17 02:39:14 +08:00
|
|
|
|
|
|
|
if (Range.isFullSet())
|
|
|
|
return S;
|
|
|
|
|
2018-05-16 22:05:03 +08:00
|
|
|
if (S.n_basic_set() > MaxDisjunctsInContext)
|
2017-02-17 03:11:25 +08:00
|
|
|
return S;
|
|
|
|
|
2017-02-17 02:39:14 +08:00
|
|
|
// In case of signed wrapping, we can refine the set of valid values by
|
|
|
|
// excluding the part not covered by the wrapping range.
|
|
|
|
if (Range.isSignWrappedSet()) {
|
2017-05-22 04:23:20 +08:00
|
|
|
V = valFromAPInt(Ctx.get(), Range.getLower(), true);
|
|
|
|
isl::set SLB = S.lower_bound_val(type, dim, V);
|
2017-02-17 02:39:14 +08:00
|
|
|
|
2017-05-22 04:23:20 +08:00
|
|
|
V = valFromAPInt(Ctx.get(), Range.getUpper(), true);
|
|
|
|
V = V.sub_ui(1);
|
|
|
|
isl::set SUB = S.upper_bound_val(type, dim, V);
|
|
|
|
S = SLB.unite(SUB);
|
2017-02-17 02:39:14 +08:00
|
|
|
}
|
2015-02-24 19:58:30 +08:00
|
|
|
|
2017-02-17 02:39:14 +08:00
|
|
|
return S;
|
2015-02-24 19:58:30 +08:00
|
|
|
}
|
|
|
|
|
2015-08-21 02:04:22 +08:00
|
|
|
static const ScopArrayInfo *identifyBasePtrOriginSAI(Scop *S, Value *BasePtr) {
|
|
|
|
LoadInst *BasePtrLI = dyn_cast<LoadInst>(BasePtr);
|
|
|
|
if (!BasePtrLI)
|
|
|
|
return nullptr;
|
|
|
|
|
2016-05-23 20:40:48 +08:00
|
|
|
if (!S->contains(BasePtrLI))
|
2015-08-21 02:04:22 +08:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
ScalarEvolution &SE = *S->getSE();
|
|
|
|
|
|
|
|
auto *OriginBaseSCEV =
|
|
|
|
SE.getPointerBase(SE.getSCEV(BasePtrLI->getPointerOperand()));
|
|
|
|
if (!OriginBaseSCEV)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto *OriginBaseSCEVUnknown = dyn_cast<SCEVUnknown>(OriginBaseSCEV);
|
|
|
|
if (!OriginBaseSCEVUnknown)
|
|
|
|
return nullptr;
|
|
|
|
|
2015-11-11 01:31:31 +08:00
|
|
|
return S->getScopArrayInfo(OriginBaseSCEVUnknown->getValue(),
|
2017-01-15 04:25:44 +08:00
|
|
|
MemoryKind::Array);
|
2015-08-21 02:04:22 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 01:25:05 +08:00
|
|
|
ScopArrayInfo::ScopArrayInfo(Value *BasePtr, Type *ElementType, isl::ctx Ctx,
|
2017-01-16 00:47:26 +08:00
|
|
|
ArrayRef<const SCEV *> Sizes, MemoryKind Kind,
|
2016-07-30 17:25:51 +08:00
|
|
|
const DataLayout &DL, Scop *S,
|
|
|
|
const char *BaseName)
|
2017-08-23 05:25:51 +08:00
|
|
|
: BasePtr(BasePtr), ElementType(ElementType), Kind(Kind), DL(DL), S(*S) {
|
2015-07-28 22:53:44 +08:00
|
|
|
std::string BasePtrName =
|
2017-01-15 04:25:44 +08:00
|
|
|
BaseName ? BaseName
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
: getIslCompatibleName("MemRef", BasePtr, S->getNextArrayIdx(),
|
|
|
|
Kind == MemoryKind::PHI ? "__phi" : "",
|
|
|
|
UseInstructionNames);
|
2017-08-23 05:25:51 +08:00
|
|
|
Id = isl::id::alloc(Ctx, BasePtrName, this);
|
2015-08-21 02:04:22 +08:00
|
|
|
|
2016-02-15 06:31:39 +08:00
|
|
|
updateSizes(Sizes);
|
2016-07-30 17:25:51 +08:00
|
|
|
|
2017-01-15 04:25:44 +08:00
|
|
|
if (!BasePtr || Kind != MemoryKind::Array) {
|
2016-07-30 17:25:51 +08:00
|
|
|
BasePtrOriginSAI = nullptr;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-21 02:04:22 +08:00
|
|
|
BasePtrOriginSAI = identifyBasePtrOriginSAI(S, BasePtr);
|
|
|
|
if (BasePtrOriginSAI)
|
|
|
|
const_cast<ScopArrayInfo *>(BasePtrOriginSAI)->addDerivedSAI(this);
|
2014-10-05 19:32:18 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
ScopArrayInfo::~ScopArrayInfo() = default;
|
|
|
|
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::space ScopArrayInfo::getSpace() const {
|
|
|
|
auto Space = isl::space(Id.get_ctx(), 0, getNumberOfDimensions());
|
|
|
|
Space = Space.set_tuple_id(isl::dim::set, Id);
|
2015-09-26 16:55:54 +08:00
|
|
|
return Space;
|
|
|
|
}
|
|
|
|
|
2016-09-18 03:22:18 +08:00
|
|
|
bool ScopArrayInfo::isReadOnly() {
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_set WriteSet = S.getWrites().range();
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::space Space = getSpace();
|
2017-05-23 14:41:04 +08:00
|
|
|
WriteSet = WriteSet.extract_set(Space);
|
2016-09-18 03:22:18 +08:00
|
|
|
|
2017-05-23 14:41:04 +08:00
|
|
|
return bool(WriteSet.is_empty());
|
2016-09-18 03:22:18 +08:00
|
|
|
}
|
|
|
|
|
2017-05-10 18:59:58 +08:00
|
|
|
bool ScopArrayInfo::isCompatibleWith(const ScopArrayInfo *Array) const {
|
|
|
|
if (Array->getElementType() != getElementType())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Array->getNumberOfDimensions() != getNumberOfDimensions())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < getNumberOfDimensions(); i++)
|
|
|
|
if (Array->getDimensionSize(i) != getDimensionSize(i))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-02-15 06:31:39 +08:00
|
|
|
void ScopArrayInfo::updateElementType(Type *NewElementType) {
|
|
|
|
if (NewElementType == ElementType)
|
|
|
|
return;
|
|
|
|
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
auto OldElementSize = DL.getTypeAllocSizeInBits(ElementType);
|
|
|
|
auto NewElementSize = DL.getTypeAllocSizeInBits(NewElementType);
|
|
|
|
|
2016-02-25 22:08:48 +08:00
|
|
|
if (NewElementSize == OldElementSize || NewElementSize == 0)
|
2016-02-15 06:31:39 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (NewElementSize % OldElementSize == 0 && NewElementSize < OldElementSize) {
|
|
|
|
ElementType = NewElementType;
|
|
|
|
} else {
|
|
|
|
auto GCD = GreatestCommonDivisor64(NewElementSize, OldElementSize);
|
|
|
|
ElementType = IntegerType::get(ElementType->getContext(), GCD);
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
}
|
2016-02-15 06:31:39 +08:00
|
|
|
}
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
|
2017-05-19 23:07:45 +08:00
|
|
|
/// Make the ScopArrayInfo model a Fortran Array
|
|
|
|
void ScopArrayInfo::applyAndSetFAD(Value *FAD) {
|
|
|
|
assert(FAD && "got invalid Fortran array descriptor");
|
|
|
|
if (this->FAD) {
|
|
|
|
assert(this->FAD == FAD &&
|
|
|
|
"receiving different array descriptors for same array");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(DimensionSizesPw.size() > 0 && !DimensionSizesPw[0]);
|
|
|
|
assert(!this->FAD);
|
|
|
|
this->FAD = FAD;
|
|
|
|
|
2017-05-23 15:07:05 +08:00
|
|
|
isl::space Space(S.getIslCtx(), 1, 0);
|
2017-05-19 23:07:45 +08:00
|
|
|
|
|
|
|
std::string param_name = getName();
|
|
|
|
param_name += "_fortranarr_size";
|
2017-08-23 05:25:51 +08:00
|
|
|
isl::id IdPwAff = isl::id::alloc(S.getIslCtx(), param_name, this);
|
2017-05-19 23:07:45 +08:00
|
|
|
|
2017-05-23 15:07:05 +08:00
|
|
|
Space = Space.set_dim_id(isl::dim::param, 0, IdPwAff);
|
|
|
|
isl::pw_aff PwAff =
|
|
|
|
isl::aff::var_on_domain(isl::local_space(Space), isl::dim::param, 0);
|
2017-05-19 23:07:45 +08:00
|
|
|
|
2017-07-22 07:07:56 +08:00
|
|
|
DimensionSizesPw[0] = PwAff;
|
2017-05-19 23:07:45 +08:00
|
|
|
}
|
|
|
|
|
2016-12-02 16:10:56 +08:00
|
|
|
bool ScopArrayInfo::updateSizes(ArrayRef<const SCEV *> NewSizes,
|
|
|
|
bool CheckConsistency) {
|
2015-09-26 16:55:54 +08:00
|
|
|
int SharedDims = std::min(NewSizes.size(), DimensionSizes.size());
|
|
|
|
int ExtraDimsNew = NewSizes.size() - SharedDims;
|
|
|
|
int ExtraDimsOld = DimensionSizes.size() - SharedDims;
|
2016-09-13 01:08:31 +08:00
|
|
|
|
2016-12-02 16:10:56 +08:00
|
|
|
if (CheckConsistency) {
|
|
|
|
for (int i = 0; i < SharedDims; i++) {
|
|
|
|
auto *NewSize = NewSizes[i + ExtraDimsNew];
|
|
|
|
auto *KnownSize = DimensionSizes[i + ExtraDimsOld];
|
|
|
|
if (NewSize && KnownSize && NewSize != KnownSize)
|
|
|
|
return false;
|
|
|
|
}
|
2015-11-02 19:29:32 +08:00
|
|
|
|
2016-12-02 16:10:56 +08:00
|
|
|
if (DimensionSizes.size() >= NewSizes.size())
|
|
|
|
return true;
|
|
|
|
}
|
2015-09-26 16:55:54 +08:00
|
|
|
|
|
|
|
DimensionSizes.clear();
|
|
|
|
DimensionSizes.insert(DimensionSizes.begin(), NewSizes.begin(),
|
|
|
|
NewSizes.end());
|
|
|
|
DimensionSizesPw.clear();
|
|
|
|
for (const SCEV *Expr : DimensionSizes) {
|
2016-09-13 01:08:31 +08:00
|
|
|
if (!Expr) {
|
|
|
|
DimensionSizesPw.push_back(nullptr);
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::pw_aff Size = S.getPwAffOnly(Expr);
|
2015-09-26 16:55:54 +08:00
|
|
|
DimensionSizesPw.push_back(Size);
|
|
|
|
}
|
2015-11-02 19:29:32 +08:00
|
|
|
return true;
|
2015-09-26 16:55:54 +08:00
|
|
|
}
|
|
|
|
|
2017-07-22 07:07:56 +08:00
|
|
|
std::string ScopArrayInfo::getName() const { return Id.get_name(); }
|
2015-05-20 16:05:31 +08:00
|
|
|
|
|
|
|
int ScopArrayInfo::getElemSizeInBytes() const {
|
2015-11-13 04:15:08 +08:00
|
|
|
return DL.getTypeAllocSize(ElementType);
|
2015-05-20 16:05:31 +08:00
|
|
|
}
|
|
|
|
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::id ScopArrayInfo::getBasePtrId() const { return Id; }
|
2014-10-05 19:32:18 +08:00
|
|
|
|
2017-07-21 23:54:07 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2017-07-21 23:54:13 +08:00
|
|
|
LLVM_DUMP_METHOD void ScopArrayInfo::dump() const { print(errs()); }
|
2017-07-21 23:54:07 +08:00
|
|
|
#endif
|
2014-10-05 19:32:18 +08:00
|
|
|
|
2015-08-12 23:27:16 +08:00
|
|
|
void ScopArrayInfo::print(raw_ostream &OS, bool SizeAsPwAff) const {
|
2015-11-10 22:02:54 +08:00
|
|
|
OS.indent(8) << *getElementType() << " " << getName();
|
2016-09-13 01:08:31 +08:00
|
|
|
unsigned u = 0;
|
2017-05-19 23:07:45 +08:00
|
|
|
// If this is a Fortran array, then we can print the outermost dimension
|
|
|
|
// as a isl_pw_aff even though there is no SCEV information.
|
|
|
|
bool IsOutermostSizeKnown = SizeAsPwAff && FAD;
|
|
|
|
|
|
|
|
if (!IsOutermostSizeKnown && getNumberOfDimensions() > 0 &&
|
|
|
|
!getDimensionSize(0)) {
|
2015-11-10 22:02:54 +08:00
|
|
|
OS << "[*]";
|
2016-09-13 01:08:31 +08:00
|
|
|
u++;
|
|
|
|
}
|
|
|
|
for (; u < getNumberOfDimensions(); u++) {
|
2015-08-12 23:27:16 +08:00
|
|
|
OS << "[";
|
|
|
|
|
2015-11-10 22:24:21 +08:00
|
|
|
if (SizeAsPwAff) {
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::pw_aff Size = getDimensionSizePw(u);
|
2015-11-10 22:24:21 +08:00
|
|
|
OS << " " << Size << " ";
|
|
|
|
} else {
|
|
|
|
OS << *getDimensionSize(u);
|
|
|
|
}
|
2015-08-12 23:27:16 +08:00
|
|
|
|
|
|
|
OS << "]";
|
|
|
|
}
|
|
|
|
|
2015-11-10 22:02:54 +08:00
|
|
|
OS << ";";
|
|
|
|
|
2015-08-21 02:04:22 +08:00
|
|
|
if (BasePtrOriginSAI)
|
|
|
|
OS << " [BasePtrOrigin: " << BasePtrOriginSAI->getName() << "]";
|
|
|
|
|
2015-05-20 16:05:31 +08:00
|
|
|
OS << " // Element size " << getElemSizeInBytes() << "\n";
|
2014-10-05 19:32:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const ScopArrayInfo *
|
2017-07-25 00:22:27 +08:00
|
|
|
ScopArrayInfo::getFromAccessFunction(isl::pw_multi_aff PMA) {
|
|
|
|
isl::id Id = PMA.get_tuple_id(isl::dim::out);
|
|
|
|
assert(!Id.is_null() && "Output dimension didn't have an ID");
|
2014-10-05 19:32:18 +08:00
|
|
|
return getFromId(Id);
|
|
|
|
}
|
|
|
|
|
2017-07-25 00:22:27 +08:00
|
|
|
const ScopArrayInfo *ScopArrayInfo::getFromId(isl::id Id) {
|
|
|
|
void *User = Id.get_user();
|
2014-10-05 19:32:18 +08:00
|
|
|
const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
|
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
2016-04-11 22:34:08 +08:00
|
|
|
void MemoryAccess::wrapConstantDimensions() {
|
|
|
|
auto *SAI = getScopArrayInfo();
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::space ArraySpace = SAI->getSpace();
|
2017-05-22 04:23:23 +08:00
|
|
|
isl::ctx Ctx = ArraySpace.get_ctx();
|
2016-04-11 22:34:08 +08:00
|
|
|
unsigned DimsArray = SAI->getNumberOfDimensions();
|
|
|
|
|
2017-05-22 04:23:23 +08:00
|
|
|
isl::multi_aff DivModAff = isl::multi_aff::identity(
|
|
|
|
ArraySpace.map_from_domain_and_range(ArraySpace));
|
|
|
|
isl::local_space LArraySpace = isl::local_space(ArraySpace);
|
2016-04-11 22:34:08 +08:00
|
|
|
|
|
|
|
// Begin with last dimension, to iteratively carry into higher dimensions.
|
|
|
|
for (int i = DimsArray - 1; i > 0; i--) {
|
|
|
|
auto *DimSize = SAI->getDimensionSize(i);
|
|
|
|
auto *DimSizeCst = dyn_cast<SCEVConstant>(DimSize);
|
|
|
|
|
|
|
|
// This transformation is not applicable to dimensions with dynamic size.
|
|
|
|
if (!DimSizeCst)
|
|
|
|
continue;
|
|
|
|
|
2017-02-17 12:48:52 +08:00
|
|
|
// This transformation is not applicable to dimensions of size zero.
|
|
|
|
if (DimSize->isZero())
|
|
|
|
continue;
|
|
|
|
|
2017-05-22 04:23:23 +08:00
|
|
|
isl::val DimSizeVal =
|
|
|
|
valFromAPInt(Ctx.get(), DimSizeCst->getAPInt(), false);
|
|
|
|
isl::aff Var = isl::aff::var_on_domain(LArraySpace, isl::dim::set, i);
|
|
|
|
isl::aff PrevVar =
|
|
|
|
isl::aff::var_on_domain(LArraySpace, isl::dim::set, i - 1);
|
2016-04-11 22:34:08 +08:00
|
|
|
|
|
|
|
// Compute: index % size
|
|
|
|
// Modulo must apply in the divide of the previous iteration, if any.
|
2017-08-06 23:56:45 +08:00
|
|
|
isl::aff Modulo = Var.mod(DimSizeVal);
|
2017-05-22 04:23:23 +08:00
|
|
|
Modulo = Modulo.pullback(DivModAff);
|
2016-04-11 22:34:08 +08:00
|
|
|
|
|
|
|
// Compute: floor(index / size)
|
2017-05-22 04:23:23 +08:00
|
|
|
isl::aff Divide = Var.div(isl::aff(LArraySpace, DimSizeVal));
|
|
|
|
Divide = Divide.floor();
|
|
|
|
Divide = Divide.add(PrevVar);
|
|
|
|
Divide = Divide.pullback(DivModAff);
|
2016-04-11 22:34:08 +08:00
|
|
|
|
|
|
|
// Apply Modulo and Divide.
|
2017-05-22 04:23:23 +08:00
|
|
|
DivModAff = DivModAff.set_aff(i, Modulo);
|
|
|
|
DivModAff = DivModAff.set_aff(i - 1, Divide);
|
2016-04-11 22:34:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply all modulo/divides on the accesses.
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::map Relation = AccessRelation;
|
2017-05-22 04:23:23 +08:00
|
|
|
Relation = Relation.apply_range(isl::map::from_multi_aff(DivModAff));
|
|
|
|
Relation = Relation.detect_equalities();
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = Relation;
|
2016-04-11 22:34:08 +08:00
|
|
|
}
|
|
|
|
|
2015-09-26 16:55:54 +08:00
|
|
|
void MemoryAccess::updateDimensionality() {
|
2016-02-19 00:50:12 +08:00
|
|
|
auto *SAI = getScopArrayInfo();
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::space ArraySpace = SAI->getSpace();
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::space AccessSpace = AccessRelation.get_space().range();
|
2017-05-22 04:38:33 +08:00
|
|
|
isl::ctx Ctx = ArraySpace.get_ctx();
|
2015-09-26 16:55:54 +08:00
|
|
|
|
2017-05-22 04:38:33 +08:00
|
|
|
auto DimsArray = ArraySpace.dim(isl::dim::set);
|
|
|
|
auto DimsAccess = AccessSpace.dim(isl::dim::set);
|
2015-09-26 16:55:54 +08:00
|
|
|
auto DimsMissing = DimsArray - DimsAccess;
|
|
|
|
|
2016-02-25 06:08:24 +08:00
|
|
|
auto *BB = getStatement()->getEntryBlock();
|
2016-02-22 03:13:19 +08:00
|
|
|
auto &DL = BB->getModule()->getDataLayout();
|
2016-02-19 00:50:12 +08:00
|
|
|
unsigned ArrayElemSize = SAI->getElemSizeInBytes();
|
2016-02-22 03:13:19 +08:00
|
|
|
unsigned ElemBytes = DL.getTypeAllocSize(getElementType());
|
2016-02-19 00:50:12 +08:00
|
|
|
|
2017-05-22 04:38:33 +08:00
|
|
|
isl::map Map = isl::map::from_domain_and_range(
|
|
|
|
isl::set::universe(AccessSpace), isl::set::universe(ArraySpace));
|
2015-09-26 16:55:54 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < DimsMissing; i++)
|
2017-05-22 04:38:33 +08:00
|
|
|
Map = Map.fix_si(isl::dim::out, i, 0);
|
2015-09-26 16:55:54 +08:00
|
|
|
|
|
|
|
for (unsigned i = DimsMissing; i < DimsArray; i++)
|
2017-05-22 04:38:33 +08:00
|
|
|
Map = Map.equate(isl::dim::in, i - DimsMissing, isl::dim::out, i);
|
2015-09-26 16:55:54 +08:00
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.apply_range(Map);
|
2016-01-08 22:01:59 +08:00
|
|
|
|
2016-02-19 00:50:12 +08:00
|
|
|
// For the non delinearized arrays, divide the access function of the last
|
|
|
|
// subscript by the size of the elements in the array.
|
|
|
|
//
|
|
|
|
// A stride one array access in C expressed as A[i] is expressed in
|
|
|
|
// LLVM-IR as something like A[i * elementsize]. This hides the fact that
|
|
|
|
// two subsequent values of 'i' index two values that are stored next to
|
|
|
|
// each other in memory. By this division we make this characteristic
|
|
|
|
// obvious again. If the base pointer was accessed with offsets not divisible
|
2016-08-03 13:28:09 +08:00
|
|
|
// by the accesses element size, we will have chosen a smaller ArrayElemSize
|
2016-02-19 00:50:12 +08:00
|
|
|
// that divides the offsets of all accesses to this base pointer.
|
|
|
|
if (DimsAccess == 1) {
|
2017-05-22 04:38:33 +08:00
|
|
|
isl::val V = isl::val(Ctx, ArrayElemSize);
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.floordiv_val(V);
|
2016-02-19 00:50:12 +08:00
|
|
|
}
|
|
|
|
|
2016-04-11 22:34:08 +08:00
|
|
|
// We currently do this only if we added at least one dimension, which means
|
|
|
|
// some dimension's indices have not been specified, an indicator that some
|
|
|
|
// index values have been added together.
|
|
|
|
// TODO: Investigate general usefulness; Effect on unit tests is to make index
|
|
|
|
// expressions more complicated.
|
|
|
|
if (DimsMissing)
|
|
|
|
wrapConstantDimensions();
|
|
|
|
|
2016-02-19 00:50:12 +08:00
|
|
|
if (!isAffine())
|
|
|
|
computeBoundsOnAccessRelation(ArrayElemSize);
|
|
|
|
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
// Introduce multi-element accesses in case the type loaded by this memory
|
|
|
|
// access is larger than the canonical element type of the array.
|
|
|
|
//
|
|
|
|
// An access ((float *)A)[i] to an array char *A is modeled as
|
|
|
|
// {[i] -> A[o] : 4 i <= o <= 4 i + 3
|
|
|
|
if (ElemBytes > ArrayElemSize) {
|
|
|
|
assert(ElemBytes % ArrayElemSize == 0 &&
|
|
|
|
"Loaded element size should be multiple of canonical element size");
|
2017-05-22 04:38:33 +08:00
|
|
|
isl::map Map = isl::map::from_domain_and_range(
|
|
|
|
isl::set::universe(ArraySpace), isl::set::universe(ArraySpace));
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
for (unsigned i = 0; i < DimsArray - 1; i++)
|
2017-05-22 04:38:33 +08:00
|
|
|
Map = Map.equate(isl::dim::in, i, isl::dim::out, i);
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
|
2017-05-22 04:38:33 +08:00
|
|
|
isl::constraint C;
|
|
|
|
isl::local_space LS;
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
|
2017-05-22 04:38:33 +08:00
|
|
|
LS = isl::local_space(Map.get_space());
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
int Num = ElemBytes / getScopArrayInfo()->getElemSizeInBytes();
|
|
|
|
|
2017-05-22 04:38:33 +08:00
|
|
|
C = isl::constraint::alloc_inequality(LS);
|
|
|
|
C = C.set_constant_val(isl::val(Ctx, Num - 1));
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, 1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, -1);
|
|
|
|
Map = Map.add_constraint(C);
|
|
|
|
|
|
|
|
C = isl::constraint::alloc_inequality(LS);
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, -1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, 1);
|
|
|
|
C = C.set_constant_val(isl::val(Ctx, 0));
|
|
|
|
Map = Map.add_constraint(C);
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.apply_range(Map);
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
}
|
2015-09-26 16:55:54 +08:00
|
|
|
}
|
|
|
|
|
2014-08-01 16:13:25 +08:00
|
|
|
const std::string
|
|
|
|
MemoryAccess::getReductionOperatorStr(MemoryAccess::ReductionType RT) {
|
|
|
|
switch (RT) {
|
|
|
|
case MemoryAccess::RT_NONE:
|
|
|
|
llvm_unreachable("Requested a reduction operator string for a memory "
|
|
|
|
"access which isn't a reduction");
|
|
|
|
case MemoryAccess::RT_ADD:
|
|
|
|
return "+";
|
|
|
|
case MemoryAccess::RT_MUL:
|
|
|
|
return "*";
|
|
|
|
case MemoryAccess::RT_BOR:
|
|
|
|
return "|";
|
|
|
|
case MemoryAccess::RT_BXOR:
|
|
|
|
return "^";
|
|
|
|
case MemoryAccess::RT_BAND:
|
|
|
|
return "&";
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown reduction type");
|
|
|
|
}
|
|
|
|
|
2016-09-02 03:53:31 +08:00
|
|
|
const ScopArrayInfo *MemoryAccess::getOriginalScopArrayInfo() const {
|
2017-07-23 12:08:59 +08:00
|
|
|
isl::id ArrayId = getArrayId();
|
|
|
|
void *User = ArrayId.get_user();
|
2014-10-05 19:32:18 +08:00
|
|
|
const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
|
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
2016-09-02 03:53:31 +08:00
|
|
|
const ScopArrayInfo *MemoryAccess::getLatestScopArrayInfo() const {
|
2017-07-23 12:08:59 +08:00
|
|
|
isl::id ArrayId = getLatestArrayId();
|
|
|
|
void *User = ArrayId.get_user();
|
2016-09-02 03:53:31 +08:00
|
|
|
const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
|
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:59 +08:00
|
|
|
isl::id MemoryAccess::getOriginalArrayId() const {
|
|
|
|
return AccessRelation.get_tuple_id(isl::dim::out);
|
2014-07-29 16:37:55 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:59 +08:00
|
|
|
isl::id MemoryAccess::getLatestArrayId() const {
|
2016-09-02 03:53:31 +08:00
|
|
|
if (!hasNewAccessRelation())
|
|
|
|
return getOriginalArrayId();
|
2017-07-23 12:08:59 +08:00
|
|
|
return NewAccessRelation.get_tuple_id(isl::dim::out);
|
2016-09-02 03:53:31 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:45 +08:00
|
|
|
isl::map MemoryAccess::getAddressFunction() const {
|
|
|
|
return getAccessRelation().lexmin();
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:52 +08:00
|
|
|
isl::pw_multi_aff
|
|
|
|
MemoryAccess::applyScheduleToAccessRelation(isl::union_map USchedule) const {
|
|
|
|
isl::map Schedule, ScheduledAccRel;
|
|
|
|
isl::union_set UDomain;
|
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
UDomain = getStatement()->getDomain();
|
2017-07-23 12:08:52 +08:00
|
|
|
USchedule = USchedule.intersect_domain(UDomain);
|
|
|
|
Schedule = isl::map::from_union_map(USchedule);
|
|
|
|
ScheduledAccRel = getAddressFunction().apply_domain(Schedule);
|
|
|
|
return isl::pw_multi_aff::from_map(ScheduledAccRel);
|
2014-10-13 20:58:03 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:27 +08:00
|
|
|
isl::map MemoryAccess::getOriginalAccessRelation() const {
|
|
|
|
return AccessRelation;
|
2011-10-06 08:04:11 +08:00
|
|
|
}
|
|
|
|
|
2014-10-13 20:58:03 +08:00
|
|
|
std::string MemoryAccess::getOriginalAccessRelationStr() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
return AccessRelation.to_str();
|
2011-10-06 08:04:11 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:27 +08:00
|
|
|
isl::space MemoryAccess::getOriginalAccessRelationSpace() const {
|
|
|
|
return AccessRelation.get_space();
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:38 +08:00
|
|
|
isl::map MemoryAccess::getNewAccessRelation() const {
|
|
|
|
return NewAccessRelation;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2015-09-05 15:46:47 +08:00
|
|
|
std::string MemoryAccess::getNewAccessRelationStr() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
return NewAccessRelation.to_str();
|
2015-09-05 15:46:47 +08:00
|
|
|
}
|
|
|
|
|
2017-07-11 18:10:13 +08:00
|
|
|
std::string MemoryAccess::getAccessRelationStr() const {
|
2017-08-06 19:41:10 +08:00
|
|
|
return getAccessRelation().to_str();
|
2017-07-11 18:10:13 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:17 +08:00
|
|
|
isl::basic_map MemoryAccess::createBasicAccessMap(ScopStmt *Statement) {
|
|
|
|
isl::space Space = isl::space(Statement->getIslCtx(), 0, 1);
|
2017-08-07 00:39:52 +08:00
|
|
|
Space = Space.align_params(Statement->getDomainSpace());
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-07-23 12:08:17 +08:00
|
|
|
return isl::basic_map::from_domain_and_range(
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::basic_set::universe(Statement->getDomainSpace()),
|
2017-07-23 12:08:17 +08:00
|
|
|
isl::basic_set::universe(Space));
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2014-07-03 01:47:48 +08:00
|
|
|
// Formalize no out-of-bound access assumption
|
|
|
|
//
|
|
|
|
// When delinearizing array accesses we optimistically assume that the
|
|
|
|
// delinearized accesses do not access out of bound locations (the subscript
|
|
|
|
// expression of each array evaluates for each statement instance that is
|
|
|
|
// executed to a value that is larger than zero and strictly smaller than the
|
|
|
|
// size of the corresponding dimension). The only exception is the outermost
|
2014-08-04 05:07:30 +08:00
|
|
|
// dimension for which we do not need to assume any upper bound. At this point
|
|
|
|
// we formalize this assumption to ensure that at code generation time the
|
|
|
|
// relevant run-time checks can be generated.
|
2014-07-03 01:47:48 +08:00
|
|
|
//
|
|
|
|
// To find the set of constraints necessary to avoid out of bound accesses, we
|
|
|
|
// first build the set of data locations that are not within array bounds. We
|
|
|
|
// then apply the reverse access relation to obtain the set of iterations that
|
|
|
|
// may contain invalid accesses and reduce this set of iterations to the ones
|
|
|
|
// that are actually executed by intersecting them with the domain of the
|
|
|
|
// statement. If we now project out all loop dimensions, we obtain a set of
|
|
|
|
// parameters that may cause statement instances to be executed that may
|
|
|
|
// possibly yield out of bound memory accesses. The complement of these
|
|
|
|
// constraints is the set of constraints that needs to be assumed to ensure such
|
|
|
|
// statement instances are never executed.
|
2015-09-19 03:59:43 +08:00
|
|
|
void MemoryAccess::assumeNoOutOfBound() {
|
2017-03-17 20:26:58 +08:00
|
|
|
if (PollyIgnoreInbounds)
|
|
|
|
return;
|
2016-02-07 21:57:32 +08:00
|
|
|
auto *SAI = getScopArrayInfo();
|
2017-07-23 12:08:27 +08:00
|
|
|
isl::space Space = getOriginalAccessRelationSpace().range();
|
2017-05-23 15:07:07 +08:00
|
|
|
isl::set Outside = isl::set::empty(Space);
|
|
|
|
for (int i = 1, Size = Space.dim(isl::dim::set); i < Size; ++i) {
|
|
|
|
isl::local_space LS(Space);
|
|
|
|
isl::pw_aff Var = isl::pw_aff::var_on_domain(LS, isl::dim::set, i);
|
|
|
|
isl::pw_aff Zero = isl::pw_aff(LS);
|
|
|
|
|
|
|
|
isl::set DimOutside = Var.lt_set(Zero);
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::pw_aff SizeE = SAI->getDimensionSizePw(i);
|
2017-05-23 15:07:07 +08:00
|
|
|
SizeE = SizeE.add_dims(isl::dim::in, Space.dim(isl::dim::set));
|
|
|
|
SizeE = SizeE.set_tuple_id(isl::dim::in, Space.get_tuple_id(isl::dim::set));
|
|
|
|
DimOutside = DimOutside.unite(SizeE.le_set(Var));
|
|
|
|
|
|
|
|
Outside = Outside.unite(DimOutside);
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:38 +08:00
|
|
|
Outside = Outside.apply(getAccessRelation().reverse());
|
2017-08-07 00:39:52 +08:00
|
|
|
Outside = Outside.intersect(Statement->getDomain());
|
2017-05-23 15:07:07 +08:00
|
|
|
Outside = Outside.params();
|
2015-06-26 20:09:28 +08:00
|
|
|
|
|
|
|
// Remove divs to avoid the construction of overly complicated assumptions.
|
|
|
|
// Doing so increases the set of parameter combinations that are assumed to
|
|
|
|
// not appear. This is always save, but may make the resulting run-time check
|
|
|
|
// bail out more often than strictly necessary.
|
2017-05-23 15:07:07 +08:00
|
|
|
Outside = Outside.remove_divs();
|
|
|
|
Outside = Outside.complement();
|
2016-04-11 21:24:29 +08:00
|
|
|
const auto &Loc = getAccessInstruction()
|
|
|
|
? getAccessInstruction()->getDebugLoc()
|
|
|
|
: DebugLoc();
|
[ScopInfo] Simplify inbounds assumptions under domain constraints
Without this simplification for a loop nest:
void foo(long n1_a, long n1_b, long n1_c, long n1_d,
long p1_b, long p1_c, long p1_d,
float A_1[][p1_b][p1_c][p1_d]) {
for (long i = 0; i < n1_a; i++)
for (long j = 0; j < n1_b; j++)
for (long k = 0; k < n1_c; k++)
for (long l = 0; l < n1_d; l++)
A_1[i][j][k][l] += i + j + k + l;
}
the assumption:
n1_a <= 0 or (n1_a > 0 and n1_b <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c > 0 and n1_d <= 0) or
(n1_a > 0 and n1_b > 0 and n1_c > 0 and n1_d > 0 and
p1_b >= n1_b and p1_c >= n1_c and p1_d >= n1_d)
is taken rather than the simpler assumption:
p9_b >= n9_b and p9_c >= n9_c and p9_d >= n9_d.
The former is less strict, as it allows arbitrary values of p1_* in case, the
loop is not executed at all. However, in practice these precise constraints
explode when combined across different accesses and loops. For now it seems
to make more sense to take less precise, but more scalable constraints by
default. In case we find a practical example where more precise constraints
are needed, we can think about allowing such precise constraints in specific
situations where they help.
This change speeds up the new test case from taking very long (waited at least
a minute, but it probably takes a lot more) to below a second.
llvm-svn: 296456
2017-02-28 17:45:54 +08:00
|
|
|
if (!PollyPreciseInbounds)
|
2017-08-07 00:39:52 +08:00
|
|
|
Outside = Outside.gist_params(Statement->getDomain().params());
|
2017-11-20 06:13:34 +08:00
|
|
|
Statement->getParent()->recordAssumption(INBOUNDS, Outside, Loc,
|
2016-04-12 21:27:35 +08:00
|
|
|
AS_ASSUMPTION);
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
|
|
|
|
2016-02-22 03:13:19 +08:00
|
|
|
void MemoryAccess::buildMemIntrinsicAccessRelation() {
|
2016-11-18 06:11:56 +08:00
|
|
|
assert(isMemoryIntrinsic());
|
2016-09-13 01:08:31 +08:00
|
|
|
assert(Subscripts.size() == 2 && Sizes.size() == 1);
|
2016-02-22 03:13:19 +08:00
|
|
|
|
2017-07-25 00:36:34 +08:00
|
|
|
isl::pw_aff SubscriptPWA = getPwAff(Subscripts[0]);
|
2017-05-23 15:07:09 +08:00
|
|
|
isl::map SubscriptMap = isl::map::from_pw_aff(SubscriptPWA);
|
2016-02-25 22:08:48 +08:00
|
|
|
|
2017-05-23 15:07:09 +08:00
|
|
|
isl::map LengthMap;
|
2016-02-25 22:08:48 +08:00
|
|
|
if (Subscripts[1] == nullptr) {
|
2017-05-23 15:07:09 +08:00
|
|
|
LengthMap = isl::map::universe(SubscriptMap.get_space());
|
2016-02-25 22:08:48 +08:00
|
|
|
} else {
|
2017-07-25 00:36:34 +08:00
|
|
|
isl::pw_aff LengthPWA = getPwAff(Subscripts[1]);
|
2017-05-23 15:07:09 +08:00
|
|
|
LengthMap = isl::map::from_pw_aff(LengthPWA);
|
|
|
|
isl::space RangeSpace = LengthMap.get_space().range();
|
|
|
|
LengthMap = LengthMap.apply_range(isl::map::lex_gt(RangeSpace));
|
2016-02-25 22:08:48 +08:00
|
|
|
}
|
2017-05-23 15:07:09 +08:00
|
|
|
LengthMap = LengthMap.lower_bound_si(isl::dim::out, 0, 0);
|
|
|
|
LengthMap = LengthMap.align_params(SubscriptMap.get_space());
|
|
|
|
SubscriptMap = SubscriptMap.align_params(LengthMap.get_space());
|
|
|
|
LengthMap = LengthMap.sum(SubscriptMap);
|
|
|
|
AccessRelation =
|
2017-08-07 00:39:52 +08:00
|
|
|
LengthMap.set_tuple_id(isl::dim::in, getStatement()->getDomainId());
|
2016-02-22 03:13:19 +08:00
|
|
|
}
|
|
|
|
|
2015-02-24 19:58:30 +08:00
|
|
|
void MemoryAccess::computeBoundsOnAccessRelation(unsigned ElementSize) {
|
|
|
|
ScalarEvolution *SE = Statement->getParent()->getSE();
|
|
|
|
|
2016-02-22 03:13:19 +08:00
|
|
|
auto MAI = MemAccInst(getAccessInstruction());
|
2016-02-27 09:49:58 +08:00
|
|
|
if (isa<MemIntrinsic>(MAI))
|
2016-02-22 03:13:19 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
Value *Ptr = MAI.getPointerOperand();
|
2015-02-24 19:58:30 +08:00
|
|
|
if (!Ptr || !SE->isSCEVable(Ptr->getType()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto *PtrSCEV = SE->getSCEV(Ptr);
|
|
|
|
if (isa<SCEVCouldNotCompute>(PtrSCEV))
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto *BasePtrSCEV = SE->getPointerBase(PtrSCEV);
|
|
|
|
if (BasePtrSCEV && !isa<SCEVCouldNotCompute>(BasePtrSCEV))
|
|
|
|
PtrSCEV = SE->getMinusSCEV(PtrSCEV, BasePtrSCEV);
|
|
|
|
|
|
|
|
const ConstantRange &Range = SE->getSignedRange(PtrSCEV);
|
|
|
|
if (Range.isFullSet())
|
|
|
|
return;
|
|
|
|
|
2019-03-28 02:19:33 +08:00
|
|
|
if (Range.isUpperWrapped() || Range.isSignWrappedSet())
|
2017-02-12 16:11:12 +08:00
|
|
|
return;
|
|
|
|
|
2015-03-09 03:49:50 +08:00
|
|
|
bool isWrapping = Range.isSignWrappedSet();
|
2017-02-12 16:11:12 +08:00
|
|
|
|
2015-02-24 19:58:30 +08:00
|
|
|
unsigned BW = Range.getBitWidth();
|
2016-02-07 21:59:03 +08:00
|
|
|
const auto One = APInt(BW, 1);
|
2015-03-09 03:49:50 +08:00
|
|
|
const auto LB = isWrapping ? Range.getLower() : Range.getSignedMin();
|
2016-02-07 21:59:03 +08:00
|
|
|
const auto UB = isWrapping ? (Range.getUpper() - One) : Range.getSignedMax();
|
2015-03-09 03:49:50 +08:00
|
|
|
|
|
|
|
auto Min = LB.sdiv(APInt(BW, ElementSize));
|
2016-02-07 21:59:03 +08:00
|
|
|
auto Max = UB.sdiv(APInt(BW, ElementSize)) + One;
|
2015-02-24 19:58:30 +08:00
|
|
|
|
2017-02-12 16:11:12 +08:00
|
|
|
assert(Min.sle(Max) && "Minimum expected to be less or equal than max");
|
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::map Relation = AccessRelation;
|
2017-05-22 04:23:20 +08:00
|
|
|
isl::set AccessRange = Relation.range();
|
|
|
|
AccessRange = addRangeBoundsToSet(AccessRange, ConstantRange(Min, Max), 0,
|
|
|
|
isl::dim::set);
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = Relation.intersect_range(AccessRange);
|
2015-02-24 19:58:30 +08:00
|
|
|
}
|
|
|
|
|
2016-12-02 13:21:22 +08:00
|
|
|
void MemoryAccess::foldAccessRelation() {
|
|
|
|
if (Sizes.size() < 2 || isa<SCEVConstant>(Sizes[1]))
|
|
|
|
return;
|
|
|
|
|
2015-09-19 03:59:43 +08:00
|
|
|
int Size = Subscripts.size();
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::map NewAccessRelation = AccessRelation;
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
|
2015-03-31 01:22:28 +08:00
|
|
|
for (int i = Size - 2; i >= 0; --i) {
|
2017-05-23 15:22:56 +08:00
|
|
|
isl::space Space;
|
|
|
|
isl::map MapOne, MapTwo;
|
2017-07-25 00:36:34 +08:00
|
|
|
isl::pw_aff DimSize = getPwAff(Sizes[i + 1]);
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-05-23 15:22:56 +08:00
|
|
|
isl::space SpaceSize = DimSize.get_space();
|
2018-04-29 08:28:26 +08:00
|
|
|
isl::id ParamId = SpaceSize.get_dim_id(isl::dim::param, 0);
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
Space = AccessRelation.get_space();
|
2017-05-23 15:22:56 +08:00
|
|
|
Space = Space.range().map_from_set();
|
|
|
|
Space = Space.align_params(SpaceSize);
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-05-23 15:22:56 +08:00
|
|
|
int ParamLocation = Space.find_dim_by_id(isl::dim::param, ParamId);
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-05-23 15:22:56 +08:00
|
|
|
MapOne = isl::map::universe(Space);
|
2015-03-31 01:22:28 +08:00
|
|
|
for (int j = 0; j < Size; ++j)
|
2017-05-23 15:22:56 +08:00
|
|
|
MapOne = MapOne.equate(isl::dim::in, j, isl::dim::out, j);
|
|
|
|
MapOne = MapOne.lower_bound_si(isl::dim::in, i + 1, 0);
|
2015-03-31 01:22:28 +08:00
|
|
|
|
2017-05-23 15:22:56 +08:00
|
|
|
MapTwo = isl::map::universe(Space);
|
2015-03-31 01:22:28 +08:00
|
|
|
for (int j = 0; j < Size; ++j)
|
|
|
|
if (j < i || j > i + 1)
|
2017-05-23 15:22:56 +08:00
|
|
|
MapTwo = MapTwo.equate(isl::dim::in, j, isl::dim::out, j);
|
|
|
|
|
|
|
|
isl::local_space LS(Space);
|
|
|
|
isl::constraint C;
|
|
|
|
C = isl::constraint::alloc_equality(LS);
|
|
|
|
C = C.set_constant_si(-1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, i, 1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, i, -1);
|
|
|
|
MapTwo = MapTwo.add_constraint(C);
|
|
|
|
C = isl::constraint::alloc_equality(LS);
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, i + 1, 1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, i + 1, -1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::param, ParamLocation, 1);
|
|
|
|
MapTwo = MapTwo.add_constraint(C);
|
|
|
|
MapTwo = MapTwo.upper_bound_si(isl::dim::in, i + 1, -1);
|
|
|
|
|
|
|
|
MapOne = MapOne.unite(MapTwo);
|
|
|
|
NewAccessRelation = NewAccessRelation.apply_range(MapOne);
|
2015-03-31 01:22:28 +08:00
|
|
|
}
|
2016-12-02 13:21:22 +08:00
|
|
|
|
2017-07-22 07:07:56 +08:00
|
|
|
isl::id BaseAddrId = getScopArrayInfo()->getBasePtrId();
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::space Space = Statement->getDomainSpace();
|
2017-05-23 15:22:56 +08:00
|
|
|
NewAccessRelation = NewAccessRelation.set_tuple_id(
|
|
|
|
isl::dim::in, Space.get_tuple_id(isl::dim::set));
|
|
|
|
NewAccessRelation = NewAccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
|
2017-08-07 00:39:52 +08:00
|
|
|
NewAccessRelation = NewAccessRelation.gist_domain(Statement->getDomain());
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
|
|
|
|
// Access dimension folding might in certain cases increase the number of
|
|
|
|
// disjuncts in the memory access, which can possibly complicate the generated
|
|
|
|
// run-time checks and can lead to costly compilation.
|
2017-05-23 15:22:56 +08:00
|
|
|
if (!PollyPreciseFoldAccesses &&
|
2018-06-19 16:13:53 +08:00
|
|
|
NewAccessRelation.n_basic_map() > AccessRelation.n_basic_map()) {
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
} else {
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = NewAccessRelation;
|
[ScopInfo] Disable memory folding in case it results in multi-disjunct relations
Multi-disjunct access maps can easily result in inbound assumptions which
explode in case of many memory accesses and many parameters. This change reduces
compilation time of some larger kernel from over 15 minutes to less than 16
seconds.
Interesting is the test case test/ScopInfo/multidim_param_in_subscript.ll
which has a memory access
[n] -> { Stmt_for_body3[i0, i1] -> MemRef_A[i0, -1 + n - i1] }
which requires folding, but where only a single disjunct remains. We can still
model this test case even when only using limited memory folding.
For people only reading commit messages, here the comment that explains what
memory folding is:
To recover memory accesses with array size parameters in the subscript
expression we post-process the delinearization results.
We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
range of exp1(i) - may be preferrable. Specifically, for cases where we
know exp1(i) is negative, we want to choose the latter expression.
As we commonly do not have any information about the range of exp1(i),
we do not choose one of the two options, but instead create a piecewise
access function that adds the (-1, N) offsets as soon as exp1(i) becomes
negative. For a 2D array such an access function is created by applying
the piecewise map:
[i,j] -> [i, j] : j >= 0
[i,j] -> [i-1, j+N] : j < 0
After this patch we generate only the first case, except for situations where
we can proove the first case to be invalid and can consequently select the
second without introducing disjuncts.
llvm-svn: 296679
2017-03-02 05:11:27 +08:00
|
|
|
}
|
2015-03-31 01:22:28 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Check if @p Expr is divisible by @p Size.
|
2015-11-13 04:15:32 +08:00
|
|
|
static bool isDivisible(const SCEV *Expr, unsigned Size, ScalarEvolution &SE) {
|
2016-02-25 22:08:48 +08:00
|
|
|
assert(Size != 0);
|
2016-02-19 00:50:12 +08:00
|
|
|
if (Size == 1)
|
|
|
|
return true;
|
2015-11-13 04:15:32 +08:00
|
|
|
|
|
|
|
// Only one factor needs to be divisible.
|
|
|
|
if (auto *MulExpr = dyn_cast<SCEVMulExpr>(Expr)) {
|
|
|
|
for (auto *FactorExpr : MulExpr->operands())
|
|
|
|
if (isDivisible(FactorExpr, Size, SE))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For other n-ary expressions (Add, AddRec, Max,...) all operands need
|
2017-06-08 20:06:15 +08:00
|
|
|
// to be divisible.
|
2015-11-13 04:15:32 +08:00
|
|
|
if (auto *NAryExpr = dyn_cast<SCEVNAryExpr>(Expr)) {
|
|
|
|
for (auto *OpExpr : NAryExpr->operands())
|
|
|
|
if (!isDivisible(OpExpr, Size, SE))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto *SizeSCEV = SE.getConstant(Expr->getType(), Size);
|
|
|
|
auto *UDivSCEV = SE.getUDivExpr(Expr, SizeSCEV);
|
|
|
|
auto *MulSCEV = SE.getMulExpr(UDivSCEV, SizeSCEV);
|
|
|
|
return MulSCEV == Expr;
|
|
|
|
}
|
|
|
|
|
2015-09-19 03:59:43 +08:00
|
|
|
void MemoryAccess::buildAccessRelation(const ScopArrayInfo *SAI) {
|
2017-07-23 12:08:22 +08:00
|
|
|
assert(AccessRelation.is_null() && "AccessRelation already built");
|
2014-10-05 19:32:18 +08:00
|
|
|
|
2016-04-23 22:32:34 +08:00
|
|
|
// Initialize the invalid domain which describes all iterations for which the
|
|
|
|
// access relation is not modeled correctly.
|
2017-08-06 23:36:48 +08:00
|
|
|
isl::set StmtInvalidDomain = getStatement()->getInvalidDomain();
|
2017-07-25 04:30:34 +08:00
|
|
|
InvalidDomain = isl::set::empty(StmtInvalidDomain.get_space());
|
2016-04-23 22:32:34 +08:00
|
|
|
|
2017-07-25 04:30:34 +08:00
|
|
|
isl::ctx Ctx = Id.get_ctx();
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::id BaseAddrId = SAI->getBasePtrId();
|
2011-11-10 06:34:34 +08:00
|
|
|
|
2016-11-02 04:53:11 +08:00
|
|
|
if (getAccessInstruction() && isa<MemIntrinsic>(getAccessInstruction())) {
|
|
|
|
buildMemIntrinsicAccessRelation();
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
|
2016-11-02 04:53:11 +08:00
|
|
|
return;
|
|
|
|
}
|
2016-02-22 03:13:19 +08:00
|
|
|
|
2016-11-02 04:53:11 +08:00
|
|
|
if (!isAffine()) {
|
2013-06-23 13:21:18 +08:00
|
|
|
// We overapproximate non-affine accesses with a possible access to the
|
|
|
|
// whole array. For read accesses it does not make a difference, if an
|
|
|
|
// access must or may happen. However, for write accesses it is important to
|
|
|
|
// differentiate between writes that must happen and writes that may happen.
|
2017-07-23 12:08:22 +08:00
|
|
|
if (AccessRelation.is_null())
|
|
|
|
AccessRelation = createBasicAccessMap(Statement);
|
2016-02-22 03:13:19 +08:00
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
|
2011-12-20 18:43:14 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:22 +08:00
|
|
|
isl::space Space = isl::space(Ctx, 0, Statement->getNumIterators(), 0);
|
|
|
|
AccessRelation = isl::map::universe(Space);
|
2014-04-09 05:20:44 +08:00
|
|
|
|
2015-09-19 03:59:43 +08:00
|
|
|
for (int i = 0, Size = Subscripts.size(); i < Size; ++i) {
|
2017-07-25 00:36:34 +08:00
|
|
|
isl::pw_aff Affine = getPwAff(Subscripts[i]);
|
|
|
|
isl::map SubscriptMap = isl::map::from_pw_aff(Affine);
|
|
|
|
AccessRelation = AccessRelation.flat_range_product(SubscriptMap);
|
2014-04-09 05:20:44 +08:00
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
Space = Statement->getDomainSpace();
|
2017-07-23 12:08:22 +08:00
|
|
|
AccessRelation = AccessRelation.set_tuple_id(
|
|
|
|
isl::dim::in, Space.get_tuple_id(isl::dim::set));
|
|
|
|
AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
|
2014-07-29 16:37:55 +08:00
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
AccessRelation = AccessRelation.gist_domain(Statement->getDomain());
|
2011-11-08 23:41:08 +08:00
|
|
|
}
|
2011-08-18 15:51:37 +08:00
|
|
|
|
2015-10-02 21:53:07 +08:00
|
|
|
MemoryAccess::MemoryAccess(ScopStmt *Stmt, Instruction *AccessInst,
|
2016-02-22 03:13:19 +08:00
|
|
|
AccessType AccType, Value *BaseAddress,
|
|
|
|
Type *ElementType, bool Affine,
|
2015-09-19 03:59:43 +08:00
|
|
|
ArrayRef<const SCEV *> Subscripts,
|
|
|
|
ArrayRef<const SCEV *> Sizes, Value *AccessValue,
|
2017-05-03 16:02:32 +08:00
|
|
|
MemoryKind Kind)
|
2017-08-23 05:25:51 +08:00
|
|
|
: Kind(Kind), AccType(AccType), Statement(Stmt), InvalidDomain(nullptr),
|
|
|
|
BaseAddr(BaseAddress), ElementType(ElementType),
|
2017-05-03 15:57:35 +08:00
|
|
|
Sizes(Sizes.begin(), Sizes.end()), AccessInstruction(AccessInst),
|
|
|
|
AccessValue(AccessValue), IsAffine(Affine),
|
2015-09-19 03:59:43 +08:00
|
|
|
Subscripts(Subscripts.begin(), Subscripts.end()), AccessRelation(nullptr),
|
2017-05-10 21:11:20 +08:00
|
|
|
NewAccessRelation(nullptr), FAD(nullptr) {
|
2016-02-20 11:40:15 +08:00
|
|
|
static const std::string TypeStrings[] = {"", "_Read", "_Write", "_MayWrite"};
|
2017-05-03 15:57:35 +08:00
|
|
|
const std::string Access = TypeStrings[AccType] + utostr(Stmt->size());
|
2015-11-06 04:15:37 +08:00
|
|
|
|
2017-05-03 15:57:35 +08:00
|
|
|
std::string IdName = Stmt->getBaseName() + Access;
|
2017-08-23 05:25:51 +08:00
|
|
|
Id = isl::id::alloc(Stmt->getParent()->getIslCtx(), IdName, this);
|
2015-11-06 04:15:37 +08:00
|
|
|
}
|
2015-09-19 03:59:43 +08:00
|
|
|
|
2017-07-25 00:22:32 +08:00
|
|
|
MemoryAccess::MemoryAccess(ScopStmt *Stmt, AccessType AccType, isl::map AccRel)
|
2017-08-23 05:25:51 +08:00
|
|
|
: Kind(MemoryKind::Array), AccType(AccType), Statement(Stmt),
|
|
|
|
InvalidDomain(nullptr), AccessRelation(nullptr),
|
|
|
|
NewAccessRelation(AccRel), FAD(nullptr) {
|
2017-07-25 00:22:27 +08:00
|
|
|
isl::id ArrayInfoId = NewAccessRelation.get_tuple_id(isl::dim::out);
|
2016-09-14 14:26:09 +08:00
|
|
|
auto *SAI = ScopArrayInfo::getFromId(ArrayInfoId);
|
|
|
|
Sizes.push_back(nullptr);
|
|
|
|
for (unsigned i = 1; i < SAI->getNumberOfDimensions(); i++)
|
|
|
|
Sizes.push_back(SAI->getDimensionSize(i));
|
|
|
|
ElementType = SAI->getElementType();
|
|
|
|
BaseAddr = SAI->getBasePtr();
|
|
|
|
static const std::string TypeStrings[] = {"", "_Read", "_Write", "_MayWrite"};
|
2017-05-03 15:57:35 +08:00
|
|
|
const std::string Access = TypeStrings[AccType] + utostr(Stmt->size());
|
2016-09-14 14:26:09 +08:00
|
|
|
|
2017-05-03 15:57:35 +08:00
|
|
|
std::string IdName = Stmt->getBaseName() + Access;
|
2017-08-23 05:25:51 +08:00
|
|
|
Id = isl::id::alloc(Stmt->getParent()->getIslCtx(), IdName, this);
|
2016-09-14 14:26:09 +08:00
|
|
|
}
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
MemoryAccess::~MemoryAccess() = default;
|
|
|
|
|
2011-11-08 23:41:08 +08:00
|
|
|
void MemoryAccess::realignParams() {
|
2017-08-07 03:52:38 +08:00
|
|
|
isl::set Ctx = Statement->getParent()->getContext();
|
2017-07-25 04:30:34 +08:00
|
|
|
InvalidDomain = InvalidDomain.gist_params(Ctx);
|
|
|
|
AccessRelation = AccessRelation.gist_params(Ctx);
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2014-08-01 16:13:25 +08:00
|
|
|
const std::string MemoryAccess::getReductionOperatorStr() const {
|
|
|
|
return MemoryAccess::getReductionOperatorStr(getReductionType());
|
|
|
|
}
|
|
|
|
|
2017-07-23 12:08:11 +08:00
|
|
|
isl::id MemoryAccess::getId() const { return Id; }
|
2015-05-15 17:58:32 +08:00
|
|
|
|
2014-07-02 04:52:51 +08:00
|
|
|
raw_ostream &polly::operator<<(raw_ostream &OS,
|
|
|
|
MemoryAccess::ReductionType RT) {
|
2014-08-01 16:13:25 +08:00
|
|
|
if (RT == MemoryAccess::RT_NONE)
|
2014-07-02 04:52:51 +08:00
|
|
|
OS << "NONE";
|
2014-08-01 16:13:25 +08:00
|
|
|
else
|
|
|
|
OS << MemoryAccess::getReductionOperatorStr(RT);
|
2014-07-02 04:52:51 +08:00
|
|
|
return OS;
|
|
|
|
}
|
|
|
|
|
2017-05-15 16:41:30 +08:00
|
|
|
void MemoryAccess::setFortranArrayDescriptor(Value *FAD) { this->FAD = FAD; }
|
2017-05-10 21:11:20 +08:00
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
void MemoryAccess::print(raw_ostream &OS) const {
|
2014-10-08 18:11:33 +08:00
|
|
|
switch (AccType) {
|
2013-07-14 04:41:24 +08:00
|
|
|
case READ:
|
2014-06-27 02:47:03 +08:00
|
|
|
OS.indent(12) << "ReadAccess :=\t";
|
2013-06-23 13:21:18 +08:00
|
|
|
break;
|
2013-07-14 04:41:24 +08:00
|
|
|
case MUST_WRITE:
|
2014-06-27 02:47:03 +08:00
|
|
|
OS.indent(12) << "MustWriteAccess :=\t";
|
2013-06-23 13:21:18 +08:00
|
|
|
break;
|
2013-07-14 04:41:24 +08:00
|
|
|
case MAY_WRITE:
|
2014-06-27 02:47:03 +08:00
|
|
|
OS.indent(12) << "MayWriteAccess :=\t";
|
2013-06-23 13:21:18 +08:00
|
|
|
break;
|
|
|
|
}
|
2017-05-10 21:11:20 +08:00
|
|
|
|
2015-02-07 04:13:15 +08:00
|
|
|
OS << "[Reduction Type: " << getReductionType() << "] ";
|
2017-05-10 21:11:20 +08:00
|
|
|
|
|
|
|
if (FAD) {
|
|
|
|
OS << "[Fortran array descriptor: " << FAD->getName();
|
|
|
|
OS << "] ";
|
|
|
|
};
|
|
|
|
|
ScopInfo: Harmonize the different array kinds
Over time different vocabulary has been introduced to describe the different
memory objects in Polly, resulting in different - often inconsistent - naming
schemes in different parts of Polly. We now standartize this to the following
scheme:
KindArray, KindValue, KindPHI, KindExitPHI
| ------- isScalar -----------|
In most cases this naming scheme has already been used previously (this
minimizes changes and ensures we remain consistent with previous publications).
The main change is that we remove KindScalar to clearify the difference between
a scalar as a memory object of kind Value, PHI or ExitPHI and a value (former
KindScalar) which is a memory object modeling a llvm::Value.
We also move all documentation to the Kind* enum in the ScopArrayInfo class,
remove the second enum in the MemoryAccess class and update documentation to be
formulated from the perspective of the memory object, rather than the memory
access. The terms "Implicit"/"Explicit", formerly used to describe memory
accesses, have been dropped. From the perspective of memory accesses they
described the different memory kinds well - especially from the perspective of
code generation - but just from the perspective of a memory object it seems more
straightforward to talk about scalars and arrays, rather than explicit and
implicit arrays. The last comment is clearly subjective, though. A less
subjective reason to go for these terms is the historic use both in mailing list
discussions and publications.
llvm-svn: 255467
2015-12-14 03:59:01 +08:00
|
|
|
OS << "[Scalar: " << isScalarKind() << "]\n";
|
2015-12-14 03:35:26 +08:00
|
|
|
OS.indent(16) << getOriginalAccessRelationStr() << ";\n";
|
2015-09-05 15:46:47 +08:00
|
|
|
if (hasNewAccessRelation())
|
|
|
|
OS.indent(11) << "new: " << getNewAccessRelationStr() << ";\n";
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 23:54:07 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2017-07-21 23:54:13 +08:00
|
|
|
LLVM_DUMP_METHOD void MemoryAccess::dump() const { print(errs()); }
|
2017-07-21 23:54:07 +08:00
|
|
|
#endif
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-07-25 00:36:34 +08:00
|
|
|
isl::pw_aff MemoryAccess::getPwAff(const SCEV *E) {
|
2016-04-12 21:26:45 +08:00
|
|
|
auto *Stmt = getStatement();
|
2016-04-23 22:32:34 +08:00
|
|
|
PWACtx PWAC = Stmt->getParent()->getPwAff(E, Stmt->getEntryBlock());
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::set StmtDom = getStatement()->getDomain();
|
2017-07-25 00:36:34 +08:00
|
|
|
StmtDom = StmtDom.reset_tuple_id();
|
2017-12-07 05:02:22 +08:00
|
|
|
isl::set NewInvalidDom = StmtDom.intersect(PWAC.second);
|
2017-07-25 04:30:34 +08:00
|
|
|
InvalidDomain = InvalidDomain.unite(NewInvalidDom);
|
2017-12-07 05:02:22 +08:00
|
|
|
return PWAC.first;
|
2016-04-12 21:26:45 +08:00
|
|
|
}
|
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
// Create a map in the size of the provided set domain, that maps from the
|
|
|
|
// one element of the provided set domain to another element of the provided
|
|
|
|
// set domain.
|
|
|
|
// The mapping is limited to all points that are equal in all but the last
|
|
|
|
// dimension and for which the last dimension of the input is strict smaller
|
|
|
|
// than the last dimension of the output.
|
|
|
|
//
|
|
|
|
// getEqualAndLarger(set[i0, i1, ..., iX]):
|
|
|
|
//
|
|
|
|
// set[i0, i1, ..., iX] -> set[o0, o1, ..., oX]
|
|
|
|
// : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1), iX < oX
|
|
|
|
//
|
2017-07-25 04:50:22 +08:00
|
|
|
static isl::map getEqualAndLarger(isl::space SetDomain) {
|
|
|
|
isl::space Space = SetDomain.map_from_set();
|
|
|
|
isl::map Map = isl::map::universe(Space);
|
|
|
|
unsigned lastDimension = Map.dim(isl::dim::in) - 1;
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
// Set all but the last dimension to be equal for the input and output
|
|
|
|
//
|
|
|
|
// input[i0, i1, ..., iX] -> output[o0, o1, ..., oX]
|
|
|
|
// : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1)
|
2013-10-05 01:14:53 +08:00
|
|
|
for (unsigned i = 0; i < lastDimension; ++i)
|
2017-07-25 04:50:22 +08:00
|
|
|
Map = Map.equate(isl::dim::in, i, isl::dim::out, i);
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
// Set the last dimension of the input to be strict smaller than the
|
|
|
|
// last dimension of the output.
|
|
|
|
//
|
|
|
|
// input[?,?,?,...,iX] -> output[?,?,?,...,oX] : iX < oX
|
2017-07-25 04:50:22 +08:00
|
|
|
Map = Map.order_lt(isl::dim::in, lastDimension, isl::dim::out, lastDimension);
|
2012-02-01 22:23:36 +08:00
|
|
|
return Map;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
isl::set MemoryAccess::getStride(isl::map Schedule) const {
|
|
|
|
isl::map AccessRelation = getAccessRelation();
|
|
|
|
isl::space Space = Schedule.get_space().range();
|
|
|
|
isl::map NextScatt = getEqualAndLarger(Space);
|
2012-12-18 15:46:06 +08:00
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
Schedule = Schedule.reverse();
|
|
|
|
NextScatt = NextScatt.lexmin();
|
2012-12-18 15:46:06 +08:00
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
NextScatt = NextScatt.apply_range(Schedule);
|
|
|
|
NextScatt = NextScatt.apply_range(AccessRelation);
|
|
|
|
NextScatt = NextScatt.apply_domain(Schedule);
|
|
|
|
NextScatt = NextScatt.apply_domain(AccessRelation);
|
2012-12-18 15:46:06 +08:00
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
isl::set Deltas = NextScatt.deltas();
|
2012-12-18 15:46:06 +08:00
|
|
|
return Deltas;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
bool MemoryAccess::isStrideX(isl::map Schedule, int StrideWidth) const {
|
|
|
|
isl::set Stride, StrideX;
|
2012-01-25 00:42:16 +08:00
|
|
|
bool IsStrideX;
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2012-12-18 15:46:06 +08:00
|
|
|
Stride = getStride(Schedule);
|
2017-07-25 04:50:22 +08:00
|
|
|
StrideX = isl::set::universe(Stride.get_space());
|
|
|
|
for (unsigned i = 0; i < StrideX.dim(isl::dim::set) - 1; i++)
|
|
|
|
StrideX = StrideX.fix_si(isl::dim::set, i, 0);
|
|
|
|
StrideX = StrideX.fix_si(isl::dim::set, StrideX.dim(isl::dim::set) - 1,
|
|
|
|
StrideWidth);
|
|
|
|
IsStrideX = Stride.is_subset(StrideX);
|
2011-08-20 19:11:25 +08:00
|
|
|
|
2012-01-25 00:42:16 +08:00
|
|
|
return IsStrideX;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
bool MemoryAccess::isStrideZero(isl::map Schedule) const {
|
2012-12-18 15:46:06 +08:00
|
|
|
return isStrideX(Schedule, 0);
|
2012-01-25 00:42:16 +08:00
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-07-25 04:50:22 +08:00
|
|
|
bool MemoryAccess::isStrideOne(isl::map Schedule) const {
|
2012-12-18 15:46:06 +08:00
|
|
|
return isStrideX(Schedule, 1);
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 03:27:16 +08:00
|
|
|
void MemoryAccess::setAccessRelation(isl::map NewAccess) {
|
|
|
|
AccessRelation = NewAccess;
|
2016-12-02 16:10:56 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 03:27:25 +08:00
|
|
|
void MemoryAccess::setNewAccessRelation(isl::map NewAccess) {
|
2016-09-02 03:16:58 +08:00
|
|
|
assert(NewAccess);
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// Check domain space compatibility.
|
2017-08-03 03:27:25 +08:00
|
|
|
isl::space NewSpace = NewAccess.get_space();
|
|
|
|
isl::space NewDomainSpace = NewSpace.domain();
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::space OriginalDomainSpace = getStatement()->getDomainSpace();
|
2017-08-03 03:27:25 +08:00
|
|
|
assert(OriginalDomainSpace.has_equal_tuples(NewDomainSpace));
|
2016-09-02 03:16:58 +08:00
|
|
|
|
2017-05-22 06:46:57 +08:00
|
|
|
// Reads must be executed unconditionally. Writes might be executed in a
|
|
|
|
// subdomain only.
|
|
|
|
if (isRead()) {
|
|
|
|
// Check whether there is an access for every statement instance.
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::set StmtDomain = getStatement()->getDomain();
|
2017-08-07 04:11:59 +08:00
|
|
|
StmtDomain =
|
|
|
|
StmtDomain.intersect_params(getStatement()->getParent()->getContext());
|
2017-08-03 03:27:25 +08:00
|
|
|
isl::set NewDomain = NewAccess.domain();
|
|
|
|
assert(StmtDomain.is_subset(NewDomain) &&
|
2017-05-22 06:46:57 +08:00
|
|
|
"Partial READ accesses not supported");
|
|
|
|
}
|
2016-09-02 03:16:58 +08:00
|
|
|
|
2017-08-03 03:27:25 +08:00
|
|
|
isl::space NewAccessSpace = NewAccess.get_space();
|
|
|
|
assert(NewAccessSpace.has_tuple_id(isl::dim::set) &&
|
2016-09-02 03:16:58 +08:00
|
|
|
"Must specify the array that is accessed");
|
2017-08-03 03:27:25 +08:00
|
|
|
isl::id NewArrayId = NewAccessSpace.get_tuple_id(isl::dim::set);
|
|
|
|
auto *SAI = static_cast<ScopArrayInfo *>(NewArrayId.get_user());
|
2016-09-02 03:16:58 +08:00
|
|
|
assert(SAI && "Must set a ScopArrayInfo");
|
Relax assert when setting access functions with invariant base pointers
Summary:
Instead of forbidding such access functions completely, we verify that their
base pointer has been hoisted and only assert in case the base pointer was
not hoisted.
I was trying for a little while to get a test case that ensures the assert is
correctly fired in case of invariant load hoisting being disabled, but I could
not find a good way to do so, as llvm-lit immediately aborts if a command
yields a non-zero return value. As we do not generally test our asserts,
not having a test case here seems OK.
This resolves http://llvm.org/PR31494
Suggested-by: Michael Kruse <llvm@meinersbur.de>
Reviewers: efriedma, jdoerfert, Meinersbur, gareevroman, sebpop, zinob, huihuiz, pollydev
Reviewed By: Meinersbur
Differential Revision: https://reviews.llvm.org/D28798
llvm-svn: 292213
2017-01-17 20:00:42 +08:00
|
|
|
|
|
|
|
if (SAI->isArrayKind() && SAI->getBasePtrOriginSAI()) {
|
|
|
|
InvariantEquivClassTy *EqClass =
|
|
|
|
getStatement()->getParent()->lookupInvariantEquivClass(
|
|
|
|
SAI->getBasePtr());
|
|
|
|
assert(EqClass &&
|
|
|
|
"Access functions to indirect arrays must have an invariant and "
|
|
|
|
"hoisted base pointer");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether access dimensions correspond to number of dimensions of the
|
|
|
|
// accesses array.
|
2016-09-02 03:16:58 +08:00
|
|
|
auto Dims = SAI->getNumberOfDimensions();
|
2017-08-03 03:27:25 +08:00
|
|
|
assert(NewAccessSpace.dim(isl::dim::set) == Dims &&
|
2016-09-02 03:16:58 +08:00
|
|
|
"Access dims must match array dims");
|
|
|
|
#endif
|
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
NewAccess = NewAccess.gist_domain(getStatement()->getDomain());
|
2017-08-03 03:27:25 +08:00
|
|
|
NewAccessRelation = NewAccess;
|
2011-07-13 01:14:03 +08:00
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-05-22 06:46:57 +08:00
|
|
|
bool MemoryAccess::isLatestPartialAccess() const {
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::set StmtDom = getStatement()->getDomain();
|
2017-07-23 12:08:38 +08:00
|
|
|
isl::set AccDom = getLatestAccessRelation().domain();
|
2017-05-22 06:46:57 +08:00
|
|
|
|
2018-04-29 08:28:26 +08:00
|
|
|
return !StmtDom.is_subset(AccDom);
|
2017-05-22 06:46:57 +08:00
|
|
|
}
|
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2011-10-06 08:04:05 +08:00
|
|
|
|
2017-08-07 01:45:28 +08:00
|
|
|
isl::map ScopStmt::getSchedule() const {
|
2017-08-14 14:49:06 +08:00
|
|
|
isl::set Domain = getDomain();
|
|
|
|
if (Domain.is_empty())
|
|
|
|
return isl::map::from_aff(isl::aff(isl::local_space(getDomainSpace())));
|
|
|
|
auto Schedule = getParent()->getSchedule();
|
|
|
|
if (!Schedule)
|
2016-09-14 14:26:09 +08:00
|
|
|
return nullptr;
|
2017-08-14 14:49:06 +08:00
|
|
|
Schedule = Schedule.intersect_domain(isl::union_set(Domain));
|
|
|
|
if (Schedule.is_empty())
|
|
|
|
return isl::map::from_aff(isl::aff(isl::local_space(getDomainSpace())));
|
|
|
|
isl::map M = M.from_union_map(Schedule);
|
|
|
|
M = M.coalesce();
|
|
|
|
M = M.gist_domain(Domain);
|
|
|
|
M = M.coalesce();
|
|
|
|
return M;
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
2011-10-06 08:04:05 +08:00
|
|
|
|
2017-08-07 00:11:53 +08:00
|
|
|
void ScopStmt::restrictDomain(isl::set NewDomain) {
|
|
|
|
assert(NewDomain.is_subset(Domain) &&
|
2014-02-21 05:43:54 +08:00
|
|
|
"New domain is not a subset of old domain!");
|
|
|
|
Domain = NewDomain;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-08-08 02:40:29 +08:00
|
|
|
void ScopStmt::addAccess(MemoryAccess *Access, bool Prepend) {
|
2015-10-02 21:53:07 +08:00
|
|
|
Instruction *AccessInst = Access->getAccessInstruction();
|
|
|
|
|
2015-12-23 07:25:11 +08:00
|
|
|
if (Access->isArrayKind()) {
|
|
|
|
MemoryAccessList &MAL = InstructionToAccess[AccessInst];
|
|
|
|
MAL.emplace_front(Access);
|
2016-01-26 21:33:10 +08:00
|
|
|
} else if (Access->isValueKind() && Access->isWrite()) {
|
|
|
|
Instruction *AccessVal = cast<Instruction>(Access->getAccessValue());
|
|
|
|
assert(!ValueWrites.lookup(AccessVal));
|
|
|
|
|
|
|
|
ValueWrites[AccessVal] = Access;
|
2016-01-26 21:33:15 +08:00
|
|
|
} else if (Access->isValueKind() && Access->isRead()) {
|
|
|
|
Value *AccessVal = Access->getAccessValue();
|
|
|
|
assert(!ValueReads.lookup(AccessVal));
|
|
|
|
|
|
|
|
ValueReads[AccessVal] = Access;
|
2016-01-26 21:33:27 +08:00
|
|
|
} else if (Access->isAnyPHIKind() && Access->isWrite()) {
|
2017-02-10 18:09:44 +08:00
|
|
|
PHINode *PHI = cast<PHINode>(Access->getAccessValue());
|
2016-01-26 21:33:27 +08:00
|
|
|
assert(!PHIWrites.lookup(PHI));
|
|
|
|
|
|
|
|
PHIWrites[PHI] = Access;
|
2017-07-21 00:47:57 +08:00
|
|
|
} else if (Access->isAnyPHIKind() && Access->isRead()) {
|
|
|
|
PHINode *PHI = cast<PHINode>(Access->getAccessValue());
|
|
|
|
assert(!PHIReads.lookup(PHI));
|
|
|
|
|
|
|
|
PHIReads[PHI] = Access;
|
2015-12-23 07:25:11 +08:00
|
|
|
}
|
|
|
|
|
2017-08-08 02:40:29 +08:00
|
|
|
if (Prepend) {
|
|
|
|
MemAccs.insert(MemAccs.begin(), Access);
|
|
|
|
return;
|
|
|
|
}
|
2015-12-23 07:25:11 +08:00
|
|
|
MemAccs.push_back(Access);
|
2015-10-02 21:53:07 +08:00
|
|
|
}
|
|
|
|
|
2011-11-08 23:41:08 +08:00
|
|
|
void ScopStmt::realignParams() {
|
2014-06-14 02:01:45 +08:00
|
|
|
for (MemoryAccess *MA : *this)
|
|
|
|
MA->realignParams();
|
2011-11-08 23:41:08 +08:00
|
|
|
|
2017-08-07 03:52:38 +08:00
|
|
|
isl::set Ctx = Parent.getContext();
|
2017-08-06 23:36:48 +08:00
|
|
|
InvalidDomain = InvalidDomain.gist_params(Ctx);
|
2017-08-07 00:11:53 +08:00
|
|
|
Domain = Domain.gist_params(Ctx);
|
2011-11-08 23:41:08 +08:00
|
|
|
}
|
|
|
|
|
2018-06-02 03:12:00 +08:00
|
|
|
/// Add @p BSet to set @p BoundedParts if @p BSet is bounded.
|
|
|
|
static isl::set collectBoundedParts(isl::set S) {
|
|
|
|
isl::set BoundedParts = isl::set::empty(S.get_space());
|
2018-07-17 03:04:16 +08:00
|
|
|
for (isl::basic_set BSet : S.get_basic_set_list())
|
|
|
|
if (BSet.is_bounded())
|
2018-06-02 03:12:00 +08:00
|
|
|
BoundedParts = BoundedParts.unite(isl::set(BSet));
|
2015-09-10 21:00:06 +08:00
|
|
|
return BoundedParts;
|
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Compute the (un)bounded parts of @p S wrt. to dimension @p Dim.
|
2015-09-10 21:00:06 +08:00
|
|
|
///
|
|
|
|
/// @returns A separation of @p S into first an unbounded then a bounded subset,
|
|
|
|
/// both with regards to the dimension @p Dim.
|
2018-06-02 03:12:00 +08:00
|
|
|
static std::pair<isl::set, isl::set> partitionSetParts(isl::set S,
|
|
|
|
unsigned Dim) {
|
|
|
|
for (unsigned u = 0, e = S.n_dim(); u < e; u++)
|
|
|
|
S = S.lower_bound_si(isl::dim::set, u, 0);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2018-06-02 03:12:00 +08:00
|
|
|
unsigned NumDimsS = S.n_dim();
|
|
|
|
isl::set OnlyDimS = S;
|
2015-09-10 21:00:06 +08:00
|
|
|
|
|
|
|
// Remove dimensions that are greater than Dim as they are not interesting.
|
|
|
|
assert(NumDimsS >= Dim + 1);
|
2018-06-02 03:12:00 +08:00
|
|
|
OnlyDimS = OnlyDimS.project_out(isl::dim::set, Dim + 1, NumDimsS - Dim - 1);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
|
|
|
// Create artificial parametric upper bounds for dimensions smaller than Dim
|
|
|
|
// as we are not interested in them.
|
2018-06-02 03:12:00 +08:00
|
|
|
OnlyDimS = OnlyDimS.insert_dims(isl::dim::param, 0, Dim);
|
|
|
|
|
2015-09-10 21:00:06 +08:00
|
|
|
for (unsigned u = 0; u < Dim; u++) {
|
2018-06-02 03:12:00 +08:00
|
|
|
isl::constraint C = isl::constraint::alloc_inequality(
|
|
|
|
isl::local_space(OnlyDimS.get_space()));
|
|
|
|
C = C.set_coefficient_si(isl::dim::param, u, 1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::set, u, -1);
|
|
|
|
OnlyDimS = OnlyDimS.add_constraint(C);
|
2015-09-10 21:00:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all bounded parts of OnlyDimS.
|
2018-06-02 03:12:00 +08:00
|
|
|
isl::set BoundedParts = collectBoundedParts(OnlyDimS);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
|
|
|
// Create the dimensions greater than Dim again.
|
2018-06-02 03:12:00 +08:00
|
|
|
BoundedParts =
|
|
|
|
BoundedParts.insert_dims(isl::dim::set, Dim + 1, NumDimsS - Dim - 1);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
|
|
|
// Remove the artificial upper bound parameters again.
|
2018-06-02 03:12:00 +08:00
|
|
|
BoundedParts = BoundedParts.remove_dims(isl::dim::param, 0, Dim);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2018-06-02 03:12:00 +08:00
|
|
|
isl::set UnboundedParts = S.subtract(BoundedParts);
|
2015-09-10 21:00:06 +08:00
|
|
|
return std::make_pair(UnboundedParts, BoundedParts);
|
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Create the conditions under which @p L @p Pred @p R is true.
|
2018-06-18 20:35:36 +08:00
|
|
|
static isl::set buildConditionSet(ICmpInst::Predicate Pred, isl::pw_aff L,
|
|
|
|
isl::pw_aff R) {
|
2015-08-31 05:13:53 +08:00
|
|
|
switch (Pred) {
|
|
|
|
case ICmpInst::ICMP_EQ:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.eq_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_NE:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.ne_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_SLT:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.lt_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_SLE:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.le_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_SGT:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.gt_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_SGE:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.ge_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_ULT:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.lt_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_UGT:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.gt_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_ULE:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.le_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
case ICmpInst::ICMP_UGE:
|
2018-06-18 20:35:36 +08:00
|
|
|
return L.ge_set(R);
|
2015-08-31 05:13:53 +08:00
|
|
|
default:
|
|
|
|
llvm_unreachable("Non integer predicate not supported");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
/// Compute the isl representation for the SCEV @p E in this BB.
|
|
|
|
///
|
|
|
|
/// @param S The Scop in which @p BB resides in.
|
|
|
|
/// @param BB The BB for which isl representation is to be
|
|
|
|
/// computed.
|
|
|
|
/// @param InvalidDomainMap A map of BB to their invalid domains.
|
|
|
|
/// @param E The SCEV that should be translated.
|
|
|
|
/// @param NonNegative Flag to indicate the @p E has to be non-negative.
|
|
|
|
///
|
|
|
|
/// Note that this function will also adjust the invalid context accordingly.
|
|
|
|
|
|
|
|
__isl_give isl_pw_aff *
|
|
|
|
getPwAff(Scop &S, BasicBlock *BB,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap, const SCEV *E,
|
|
|
|
bool NonNegative = false) {
|
2017-06-29 20:47:41 +08:00
|
|
|
PWACtx PWAC = S.getPwAff(E, BB, NonNegative);
|
2017-12-07 05:02:22 +08:00
|
|
|
InvalidDomainMap[BB] = InvalidDomainMap[BB].unite(PWAC.second);
|
2018-04-29 08:57:38 +08:00
|
|
|
return PWAC.first.release();
|
2017-06-29 20:47:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Build the conditions sets for the switch @p SI in the @p Domain.
|
2015-08-31 05:13:53 +08:00
|
|
|
///
|
|
|
|
/// This will fill @p ConditionSets with the conditions under which control
|
2015-09-28 17:33:22 +08:00
|
|
|
/// will be moved from @p SI to its successors. Hence, @p ConditionSets will
|
|
|
|
/// have as many elements as @p SI has successors.
|
2017-09-24 17:25:30 +08:00
|
|
|
bool buildConditionSets(Scop &S, BasicBlock *BB, SwitchInst *SI, Loop *L,
|
|
|
|
__isl_keep isl_set *Domain,
|
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
|
|
|
|
SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
|
2015-09-28 17:33:22 +08:00
|
|
|
Value *Condition = getConditionFromTerminator(SI);
|
|
|
|
assert(Condition && "No condition for switch");
|
|
|
|
|
|
|
|
ScalarEvolution &SE = *S.getSE();
|
|
|
|
isl_pw_aff *LHS, *RHS;
|
2017-06-29 20:47:41 +08:00
|
|
|
LHS = getPwAff(S, BB, InvalidDomainMap, SE.getSCEVAtScope(Condition, L));
|
2015-09-28 17:33:22 +08:00
|
|
|
|
|
|
|
unsigned NumSuccessors = SI->getNumSuccessors();
|
|
|
|
ConditionSets.resize(NumSuccessors);
|
|
|
|
for (auto &Case : SI->cases()) {
|
|
|
|
unsigned Idx = Case.getSuccessorIndex();
|
|
|
|
ConstantInt *CaseValue = Case.getCaseValue();
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
RHS = getPwAff(S, BB, InvalidDomainMap, SE.getSCEV(CaseValue));
|
2015-09-28 17:33:22 +08:00
|
|
|
isl_set *CaseConditionSet =
|
2018-06-18 20:35:36 +08:00
|
|
|
buildConditionSet(ICmpInst::ICMP_EQ, isl::manage_copy(LHS),
|
|
|
|
isl::manage(RHS))
|
|
|
|
.release();
|
2015-09-28 17:33:22 +08:00
|
|
|
ConditionSets[Idx] = isl_set_coalesce(
|
|
|
|
isl_set_intersect(CaseConditionSet, isl_set_copy(Domain)));
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(ConditionSets[0] == nullptr && "Default condition set was set");
|
|
|
|
isl_set *ConditionSetUnion = isl_set_copy(ConditionSets[1]);
|
|
|
|
for (unsigned u = 2; u < NumSuccessors; u++)
|
|
|
|
ConditionSetUnion =
|
|
|
|
isl_set_union(ConditionSetUnion, isl_set_copy(ConditionSets[u]));
|
2018-03-04 03:27:54 +08:00
|
|
|
ConditionSets[0] = isl_set_subtract(isl_set_copy(Domain), ConditionSetUnion);
|
2015-09-28 17:33:22 +08:00
|
|
|
|
|
|
|
isl_pw_aff_free(LHS);
|
2016-05-10 21:06:42 +08:00
|
|
|
|
|
|
|
return true;
|
2015-09-28 17:33:22 +08:00
|
|
|
}
|
|
|
|
|
2017-07-20 20:37:02 +08:00
|
|
|
/// Build condition sets for unsigned ICmpInst(s).
|
|
|
|
/// Special handling is required for unsigned operands to ensure that if
|
|
|
|
/// MSB (aka the Sign bit) is set for an operands in an unsigned ICmpInst
|
|
|
|
/// it should wrap around.
|
|
|
|
///
|
|
|
|
/// @param IsStrictUpperBound holds information on the predicate relation
|
|
|
|
/// between TestVal and UpperBound, i.e,
|
|
|
|
/// TestVal < UpperBound OR TestVal <= UpperBound
|
2017-09-24 17:25:30 +08:00
|
|
|
__isl_give isl_set *
|
2017-07-20 20:37:02 +08:00
|
|
|
buildUnsignedConditionSets(Scop &S, BasicBlock *BB, Value *Condition,
|
|
|
|
__isl_keep isl_set *Domain, const SCEV *SCEV_TestVal,
|
|
|
|
const SCEV *SCEV_UpperBound,
|
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
|
|
|
|
bool IsStrictUpperBound) {
|
|
|
|
// Do not take NonNeg assumption on TestVal
|
|
|
|
// as it might have MSB (Sign bit) set.
|
|
|
|
isl_pw_aff *TestVal = getPwAff(S, BB, InvalidDomainMap, SCEV_TestVal, false);
|
|
|
|
// Take NonNeg assumption on UpperBound.
|
|
|
|
isl_pw_aff *UpperBound =
|
|
|
|
getPwAff(S, BB, InvalidDomainMap, SCEV_UpperBound, true);
|
|
|
|
|
|
|
|
// 0 <= TestVal
|
|
|
|
isl_set *First =
|
|
|
|
isl_pw_aff_le_set(isl_pw_aff_zero_on_domain(isl_local_space_from_space(
|
|
|
|
isl_pw_aff_get_domain_space(TestVal))),
|
|
|
|
isl_pw_aff_copy(TestVal));
|
|
|
|
|
|
|
|
isl_set *Second;
|
|
|
|
if (IsStrictUpperBound)
|
|
|
|
// TestVal < UpperBound
|
|
|
|
Second = isl_pw_aff_lt_set(TestVal, UpperBound);
|
|
|
|
else
|
|
|
|
// TestVal <= UpperBound
|
|
|
|
Second = isl_pw_aff_le_set(TestVal, UpperBound);
|
|
|
|
|
|
|
|
isl_set *ConsequenceCondSet = isl_set_intersect(First, Second);
|
|
|
|
return ConsequenceCondSet;
|
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Build the conditions sets for the branch condition @p Condition in
|
|
|
|
/// the @p Domain.
|
2015-09-28 17:33:22 +08:00
|
|
|
///
|
|
|
|
/// This will fill @p ConditionSets with the conditions under which control
|
|
|
|
/// will be moved from @p TI to its successors. Hence, @p ConditionSets will
|
2015-11-12 11:25:01 +08:00
|
|
|
/// have as many elements as @p TI has successors. If @p TI is nullptr the
|
|
|
|
/// context under which @p Condition is true/false will be returned as the
|
|
|
|
/// new elements of @p ConditionSets.
|
2017-09-24 17:25:30 +08:00
|
|
|
bool buildConditionSets(Scop &S, BasicBlock *BB, Value *Condition,
|
2018-10-15 18:42:50 +08:00
|
|
|
Instruction *TI, Loop *L, __isl_keep isl_set *Domain,
|
2017-09-24 17:25:30 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
|
|
|
|
SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
|
2017-09-26 04:27:15 +08:00
|
|
|
ScalarEvolution &SE = *S.getSE();
|
2015-08-31 05:13:53 +08:00
|
|
|
isl_set *ConsequenceCondSet = nullptr;
|
2017-09-26 00:37:15 +08:00
|
|
|
|
2017-09-26 04:27:15 +08:00
|
|
|
if (auto Load = dyn_cast<LoadInst>(Condition)) {
|
|
|
|
const SCEV *LHSSCEV = SE.getSCEVAtScope(Load, L);
|
|
|
|
const SCEV *RHSSCEV = SE.getZero(LHSSCEV->getType());
|
|
|
|
bool NonNeg = false;
|
|
|
|
isl_pw_aff *LHS = getPwAff(S, BB, InvalidDomainMap, LHSSCEV, NonNeg);
|
|
|
|
isl_pw_aff *RHS = getPwAff(S, BB, InvalidDomainMap, RHSSCEV, NonNeg);
|
2018-06-18 20:35:36 +08:00
|
|
|
ConsequenceCondSet = buildConditionSet(ICmpInst::ICMP_SLE, isl::manage(LHS),
|
|
|
|
isl::manage(RHS))
|
|
|
|
.release();
|
2017-09-26 04:27:15 +08:00
|
|
|
} else if (auto *PHI = dyn_cast<PHINode>(Condition)) {
|
2017-09-26 00:37:15 +08:00
|
|
|
auto *Unique = dyn_cast<ConstantInt>(
|
|
|
|
getUniqueNonErrorValue(PHI, &S.getRegion(), *S.getLI(), *S.getDT()));
|
|
|
|
|
|
|
|
if (Unique->isZero())
|
|
|
|
ConsequenceCondSet = isl_set_empty(isl_set_get_space(Domain));
|
|
|
|
else
|
|
|
|
ConsequenceCondSet = isl_set_universe(isl_set_get_space(Domain));
|
|
|
|
} else if (auto *CCond = dyn_cast<ConstantInt>(Condition)) {
|
2015-08-31 05:13:53 +08:00
|
|
|
if (CCond->isZero())
|
|
|
|
ConsequenceCondSet = isl_set_empty(isl_set_get_space(Domain));
|
|
|
|
else
|
|
|
|
ConsequenceCondSet = isl_set_universe(isl_set_get_space(Domain));
|
2015-10-11 21:21:03 +08:00
|
|
|
} else if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Condition)) {
|
|
|
|
auto Opcode = BinOp->getOpcode();
|
|
|
|
assert(Opcode == Instruction::And || Opcode == Instruction::Or);
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
bool Valid = buildConditionSets(S, BB, BinOp->getOperand(0), TI, L, Domain,
|
|
|
|
InvalidDomainMap, ConditionSets) &&
|
|
|
|
buildConditionSets(S, BB, BinOp->getOperand(1), TI, L, Domain,
|
|
|
|
InvalidDomainMap, ConditionSets);
|
2016-05-10 22:01:21 +08:00
|
|
|
if (!Valid) {
|
|
|
|
while (!ConditionSets.empty())
|
|
|
|
isl_set_free(ConditionSets.pop_back_val());
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2016-05-10 22:01:21 +08:00
|
|
|
}
|
2015-10-11 21:21:03 +08:00
|
|
|
|
|
|
|
isl_set_free(ConditionSets.pop_back_val());
|
|
|
|
isl_set *ConsCondPart0 = ConditionSets.pop_back_val();
|
|
|
|
isl_set_free(ConditionSets.pop_back_val());
|
|
|
|
isl_set *ConsCondPart1 = ConditionSets.pop_back_val();
|
|
|
|
|
|
|
|
if (Opcode == Instruction::And)
|
|
|
|
ConsequenceCondSet = isl_set_intersect(ConsCondPart0, ConsCondPart1);
|
|
|
|
else
|
|
|
|
ConsequenceCondSet = isl_set_union(ConsCondPart0, ConsCondPart1);
|
2015-08-31 05:13:53 +08:00
|
|
|
} else {
|
|
|
|
auto *ICond = dyn_cast<ICmpInst>(Condition);
|
|
|
|
assert(ICond &&
|
|
|
|
"Condition of exiting branch was neither constant nor ICmp!");
|
|
|
|
|
2017-09-24 17:25:30 +08:00
|
|
|
LoopInfo &LI = *S.getLI();
|
|
|
|
DominatorTree &DT = *S.getDT();
|
|
|
|
Region &R = S.getRegion();
|
|
|
|
|
2015-08-31 05:13:53 +08:00
|
|
|
isl_pw_aff *LHS, *RHS;
|
2016-04-29 18:44:41 +08:00
|
|
|
// For unsigned comparisons we assumed the signed bit of neither operand
|
|
|
|
// to be set. The comparison is equal to a signed comparison under this
|
|
|
|
// assumption.
|
|
|
|
bool NonNeg = ICond->isUnsigned();
|
2017-07-20 20:37:02 +08:00
|
|
|
const SCEV *LeftOperand = SE.getSCEVAtScope(ICond->getOperand(0), L),
|
|
|
|
*RightOperand = SE.getSCEVAtScope(ICond->getOperand(1), L);
|
|
|
|
|
2017-09-24 17:25:30 +08:00
|
|
|
LeftOperand = tryForwardThroughPHI(LeftOperand, R, SE, LI, DT);
|
|
|
|
RightOperand = tryForwardThroughPHI(RightOperand, R, SE, LI, DT);
|
|
|
|
|
2017-07-20 20:37:02 +08:00
|
|
|
switch (ICond->getPredicate()) {
|
|
|
|
case ICmpInst::ICMP_ULT:
|
|
|
|
ConsequenceCondSet =
|
|
|
|
buildUnsignedConditionSets(S, BB, Condition, Domain, LeftOperand,
|
|
|
|
RightOperand, InvalidDomainMap, true);
|
|
|
|
break;
|
|
|
|
case ICmpInst::ICMP_ULE:
|
|
|
|
ConsequenceCondSet =
|
|
|
|
buildUnsignedConditionSets(S, BB, Condition, Domain, LeftOperand,
|
|
|
|
RightOperand, InvalidDomainMap, false);
|
|
|
|
break;
|
|
|
|
case ICmpInst::ICMP_UGT:
|
|
|
|
ConsequenceCondSet =
|
|
|
|
buildUnsignedConditionSets(S, BB, Condition, Domain, RightOperand,
|
|
|
|
LeftOperand, InvalidDomainMap, true);
|
|
|
|
break;
|
|
|
|
case ICmpInst::ICMP_UGE:
|
|
|
|
ConsequenceCondSet =
|
|
|
|
buildUnsignedConditionSets(S, BB, Condition, Domain, RightOperand,
|
|
|
|
LeftOperand, InvalidDomainMap, false);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LHS = getPwAff(S, BB, InvalidDomainMap, LeftOperand, NonNeg);
|
|
|
|
RHS = getPwAff(S, BB, InvalidDomainMap, RightOperand, NonNeg);
|
2018-06-18 20:35:36 +08:00
|
|
|
ConsequenceCondSet = buildConditionSet(ICond->getPredicate(),
|
|
|
|
isl::manage(LHS), isl::manage(RHS))
|
|
|
|
.release();
|
2017-07-20 20:37:02 +08:00
|
|
|
break;
|
|
|
|
}
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2015-11-12 11:25:01 +08:00
|
|
|
// If no terminator was given we are only looking for parameter constraints
|
|
|
|
// under which @p Condition is true/false.
|
|
|
|
if (!TI)
|
|
|
|
ConsequenceCondSet = isl_set_params(ConsequenceCondSet);
|
2015-08-31 05:13:53 +08:00
|
|
|
assert(ConsequenceCondSet);
|
2016-04-04 15:59:41 +08:00
|
|
|
ConsequenceCondSet = isl_set_coalesce(
|
|
|
|
isl_set_intersect(ConsequenceCondSet, isl_set_copy(Domain)));
|
|
|
|
|
2016-04-26 17:20:41 +08:00
|
|
|
isl_set *AlternativeCondSet = nullptr;
|
2016-05-02 20:25:36 +08:00
|
|
|
bool TooComplex =
|
2017-02-17 03:11:33 +08:00
|
|
|
isl_set_n_basic_set(ConsequenceCondSet) >= MaxDisjunctsInDomain;
|
2016-04-26 17:20:41 +08:00
|
|
|
|
2016-05-02 20:25:36 +08:00
|
|
|
if (!TooComplex) {
|
2016-04-04 15:59:41 +08:00
|
|
|
AlternativeCondSet = isl_set_subtract(isl_set_copy(Domain),
|
|
|
|
isl_set_copy(ConsequenceCondSet));
|
2016-05-02 20:25:36 +08:00
|
|
|
TooComplex =
|
2017-02-17 03:11:33 +08:00
|
|
|
isl_set_n_basic_set(AlternativeCondSet) >= MaxDisjunctsInDomain;
|
2016-04-26 17:20:41 +08:00
|
|
|
}
|
|
|
|
|
2016-05-02 20:25:36 +08:00
|
|
|
if (TooComplex) {
|
2017-07-18 07:58:33 +08:00
|
|
|
S.invalidate(COMPLEXITY, TI ? TI->getDebugLoc() : DebugLoc(),
|
|
|
|
TI ? TI->getParent() : nullptr /* BasicBlock */);
|
2016-04-26 17:20:41 +08:00
|
|
|
isl_set_free(AlternativeCondSet);
|
|
|
|
isl_set_free(ConsequenceCondSet);
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2016-04-04 15:59:41 +08:00
|
|
|
}
|
2015-08-31 05:13:53 +08:00
|
|
|
|
2016-04-04 15:59:41 +08:00
|
|
|
ConditionSets.push_back(ConsequenceCondSet);
|
|
|
|
ConditionSets.push_back(isl_set_coalesce(AlternativeCondSet));
|
2016-05-10 21:06:42 +08:00
|
|
|
|
|
|
|
return true;
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Build the conditions sets for the terminator @p TI in the @p Domain.
|
2015-10-11 21:21:03 +08:00
|
|
|
///
|
|
|
|
/// This will fill @p ConditionSets with the conditions under which control
|
|
|
|
/// will be moved from @p TI to its successors. Hence, @p ConditionSets will
|
|
|
|
/// have as many elements as @p TI has successors.
|
2018-10-15 18:42:50 +08:00
|
|
|
bool buildConditionSets(Scop &S, BasicBlock *BB, Instruction *TI, Loop *L,
|
2017-09-24 17:25:30 +08:00
|
|
|
__isl_keep isl_set *Domain,
|
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
|
|
|
|
SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
|
2015-10-11 21:21:03 +08:00
|
|
|
if (SwitchInst *SI = dyn_cast<SwitchInst>(TI))
|
2017-06-29 20:47:41 +08:00
|
|
|
return buildConditionSets(S, BB, SI, L, Domain, InvalidDomainMap,
|
|
|
|
ConditionSets);
|
2015-10-11 21:21:03 +08:00
|
|
|
|
|
|
|
assert(isa<BranchInst>(TI) && "Terminator was neither branch nor switch.");
|
|
|
|
|
|
|
|
if (TI->getNumSuccessors() == 1) {
|
|
|
|
ConditionSets.push_back(isl_set_copy(Domain));
|
2016-05-10 21:06:42 +08:00
|
|
|
return true;
|
2015-10-11 21:21:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Value *Condition = getConditionFromTerminator(TI);
|
|
|
|
assert(Condition && "No condition for Terminator");
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
return buildConditionSets(S, BB, Condition, TI, L, Domain, InvalidDomainMap,
|
|
|
|
ConditionSets);
|
2015-10-11 21:21:03 +08:00
|
|
|
}
|
|
|
|
|
2018-01-18 23:15:38 +08:00
|
|
|
ScopStmt::ScopStmt(Scop &parent, Region &R, StringRef Name,
|
|
|
|
Loop *SurroundingLoop,
|
2017-08-31 11:15:56 +08:00
|
|
|
std::vector<Instruction *> EntryBlockInstructions)
|
2017-08-23 05:25:51 +08:00
|
|
|
: Parent(parent), InvalidDomain(nullptr), Domain(nullptr), R(&R),
|
2018-01-18 23:15:38 +08:00
|
|
|
Build(nullptr), BaseName(Name), SurroundingLoop(SurroundingLoop),
|
|
|
|
Instructions(EntryBlockInstructions) {}
|
2015-02-24 20:00:50 +08:00
|
|
|
|
2018-01-18 23:15:38 +08:00
|
|
|
ScopStmt::ScopStmt(Scop &parent, BasicBlock &bb, StringRef Name,
|
|
|
|
Loop *SurroundingLoop,
|
|
|
|
std::vector<Instruction *> Instructions)
|
2016-04-23 21:02:23 +08:00
|
|
|
: Parent(parent), InvalidDomain(nullptr), Domain(nullptr), BB(&bb),
|
2018-01-18 23:15:38 +08:00
|
|
|
Build(nullptr), BaseName(Name), SurroundingLoop(SurroundingLoop),
|
|
|
|
Instructions(Instructions) {}
|
2015-10-02 21:53:07 +08:00
|
|
|
|
2017-08-07 01:24:59 +08:00
|
|
|
ScopStmt::ScopStmt(Scop &parent, isl::map SourceRel, isl::map TargetRel,
|
|
|
|
isl::set NewDomain)
|
2017-08-23 05:25:51 +08:00
|
|
|
: Parent(parent), InvalidDomain(nullptr), Domain(NewDomain),
|
|
|
|
Build(nullptr) {
|
2016-09-14 14:26:09 +08:00
|
|
|
BaseName = getIslCompatibleName("CopyStmt_", "",
|
|
|
|
std::to_string(parent.getCopyStmtsNum()));
|
2017-08-07 01:24:59 +08:00
|
|
|
isl::id Id = isl::id::alloc(getIslCtx(), getBaseName(), this);
|
|
|
|
Domain = Domain.set_tuple_id(Id);
|
|
|
|
TargetRel = TargetRel.set_tuple_id(isl::dim::in, Id);
|
|
|
|
auto *Access =
|
|
|
|
new MemoryAccess(this, MemoryAccess::AccessType::MUST_WRITE, TargetRel);
|
2016-09-14 14:26:09 +08:00
|
|
|
parent.addAccessFunction(Access);
|
|
|
|
addAccess(Access);
|
2017-08-07 01:24:59 +08:00
|
|
|
SourceRel = SourceRel.set_tuple_id(isl::dim::in, Id);
|
|
|
|
Access = new MemoryAccess(this, MemoryAccess::AccessType::READ, SourceRel);
|
2016-09-14 14:26:09 +08:00
|
|
|
parent.addAccessFunction(Access);
|
|
|
|
addAccess(Access);
|
|
|
|
}
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
ScopStmt::~ScopStmt() = default;
|
|
|
|
|
2017-08-07 00:11:53 +08:00
|
|
|
std::string ScopStmt::getDomainStr() const { return Domain.to_str(); }
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2015-04-21 19:37:25 +08:00
|
|
|
std::string ScopStmt::getScheduleStr() const {
|
2017-08-07 01:45:28 +08:00
|
|
|
auto *S = getSchedule().release();
|
2016-09-14 14:26:09 +08:00
|
|
|
if (!S)
|
2017-08-23 05:25:51 +08:00
|
|
|
return {};
|
2015-07-14 17:33:13 +08:00
|
|
|
auto Str = stringFromIslObj(S);
|
|
|
|
isl_map_free(S);
|
|
|
|
return Str;
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-08-06 23:36:48 +08:00
|
|
|
void ScopStmt::setInvalidDomain(isl::set ID) { InvalidDomain = ID; }
|
2016-04-12 17:57:34 +08:00
|
|
|
|
2016-02-25 06:08:24 +08:00
|
|
|
BasicBlock *ScopStmt::getEntryBlock() const {
|
|
|
|
if (isBlockStmt())
|
|
|
|
return getBasicBlock();
|
|
|
|
return getRegion()->getEntry();
|
|
|
|
}
|
|
|
|
|
2015-02-20 06:16:12 +08:00
|
|
|
unsigned ScopStmt::getNumIterators() const { return NestLoops.size(); }
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
const char *ScopStmt::getBaseName() const { return BaseName.c_str(); }
|
|
|
|
|
2016-05-10 22:00:57 +08:00
|
|
|
Loop *ScopStmt::getLoopForDimension(unsigned Dimension) const {
|
2013-02-16 05:26:44 +08:00
|
|
|
return NestLoops[Dimension];
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::ctx ScopStmt::getIslCtx() const { return Parent.getIslCtx(); }
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::set ScopStmt::getDomain() const { return Domain; }
|
2011-05-07 03:52:19 +08:00
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::space ScopStmt::getDomainSpace() const { return Domain.get_space(); }
|
2012-01-18 04:34:23 +08:00
|
|
|
|
2017-08-07 00:39:52 +08:00
|
|
|
isl::id ScopStmt::getDomainId() const { return Domain.get_tuple_id(); }
|
2012-08-30 19:49:38 +08:00
|
|
|
|
2017-05-27 12:40:18 +08:00
|
|
|
void ScopStmt::printInstructions(raw_ostream &OS) const {
|
|
|
|
OS << "Instructions {\n";
|
|
|
|
|
|
|
|
for (Instruction *Inst : Instructions)
|
|
|
|
OS.indent(16) << *Inst << "\n";
|
|
|
|
|
2017-07-23 00:44:39 +08:00
|
|
|
OS.indent(12) << "}\n";
|
2017-05-27 12:40:18 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 23:35:53 +08:00
|
|
|
void ScopStmt::print(raw_ostream &OS, bool PrintInstructions) const {
|
2011-04-29 14:27:02 +08:00
|
|
|
OS << "\t" << getBaseName() << "\n";
|
|
|
|
OS.indent(12) << "Domain :=\n";
|
|
|
|
|
|
|
|
if (Domain) {
|
|
|
|
OS.indent(16) << getDomainStr() << ";\n";
|
|
|
|
} else
|
|
|
|
OS.indent(16) << "n/a\n";
|
|
|
|
|
2015-04-21 19:37:25 +08:00
|
|
|
OS.indent(12) << "Schedule :=\n";
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
if (Domain) {
|
2015-04-21 19:37:25 +08:00
|
|
|
OS.indent(16) << getScheduleStr() << ";\n";
|
2011-04-29 14:27:02 +08:00
|
|
|
} else
|
|
|
|
OS.indent(16) << "n/a\n";
|
|
|
|
|
2014-06-28 16:59:45 +08:00
|
|
|
for (MemoryAccess *Access : MemAccs)
|
|
|
|
Access->print(OS);
|
2017-05-27 12:40:18 +08:00
|
|
|
|
2017-08-31 11:15:56 +08:00
|
|
|
if (PrintInstructions)
|
2017-05-27 12:40:18 +08:00
|
|
|
printInstructions(OS.indent(12));
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 23:54:07 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2017-07-21 23:54:13 +08:00
|
|
|
LLVM_DUMP_METHOD void ScopStmt::dump() const { print(dbgs(), true); }
|
2017-07-21 23:54:07 +08:00
|
|
|
#endif
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-05-12 06:56:12 +08:00
|
|
|
void ScopStmt::removeAccessData(MemoryAccess *MA) {
|
|
|
|
if (MA->isRead() && MA->isOriginalValueKind()) {
|
|
|
|
bool Found = ValueReads.erase(MA->getAccessValue());
|
|
|
|
(void)Found;
|
|
|
|
assert(Found && "Expected access data not found");
|
|
|
|
}
|
|
|
|
if (MA->isWrite() && MA->isOriginalValueKind()) {
|
|
|
|
bool Found = ValueWrites.erase(cast<Instruction>(MA->getAccessValue()));
|
|
|
|
(void)Found;
|
|
|
|
assert(Found && "Expected access data not found");
|
|
|
|
}
|
|
|
|
if (MA->isWrite() && MA->isOriginalAnyPHIKind()) {
|
|
|
|
bool Found = PHIWrites.erase(cast<PHINode>(MA->getAccessInstruction()));
|
|
|
|
(void)Found;
|
|
|
|
assert(Found && "Expected access data not found");
|
|
|
|
}
|
2017-07-21 00:47:57 +08:00
|
|
|
if (MA->isRead() && MA->isOriginalAnyPHIKind()) {
|
|
|
|
bool Found = PHIReads.erase(cast<PHINode>(MA->getAccessInstruction()));
|
|
|
|
(void)Found;
|
|
|
|
assert(Found && "Expected access data not found");
|
|
|
|
}
|
2017-05-12 06:56:12 +08:00
|
|
|
}
|
|
|
|
|
2016-05-23 22:45:58 +08:00
|
|
|
void ScopStmt::removeMemoryAccess(MemoryAccess *MA) {
|
2017-01-15 04:25:44 +08:00
|
|
|
// Remove the memory accesses from this statement together with all scalar
|
|
|
|
// accesses that were caused by it. MemoryKind::Value READs have no access
|
|
|
|
// instruction, hence would not be removed by this function. However, it is
|
|
|
|
// only used for invariant LoadInst accesses, its arguments are always affine,
|
|
|
|
// hence synthesizable, and therefore there are no MemoryKind::Value READ
|
|
|
|
// accesses to be removed.
|
2016-05-23 22:45:58 +08:00
|
|
|
auto Predicate = [&](MemoryAccess *Acc) {
|
|
|
|
return Acc->getAccessInstruction() == MA->getAccessInstruction();
|
|
|
|
};
|
2017-05-12 06:56:12 +08:00
|
|
|
for (auto *MA : MemAccs) {
|
2017-07-20 01:11:25 +08:00
|
|
|
if (Predicate(MA)) {
|
2017-05-12 06:56:12 +08:00
|
|
|
removeAccessData(MA);
|
2017-07-20 01:11:25 +08:00
|
|
|
Parent.removeAccessData(MA);
|
|
|
|
}
|
2017-05-12 06:56:12 +08:00
|
|
|
}
|
2016-05-23 22:45:58 +08:00
|
|
|
MemAccs.erase(std::remove_if(MemAccs.begin(), MemAccs.end(), Predicate),
|
|
|
|
MemAccs.end());
|
|
|
|
InstructionToAccess.erase(MA->getAccessInstruction());
|
2015-09-30 07:47:21 +08:00
|
|
|
}
|
|
|
|
|
2018-04-10 07:13:05 +08:00
|
|
|
void ScopStmt::removeSingleMemoryAccess(MemoryAccess *MA, bool AfterHoisting) {
|
|
|
|
if (AfterHoisting) {
|
|
|
|
auto MAIt = std::find(MemAccs.begin(), MemAccs.end(), MA);
|
|
|
|
assert(MAIt != MemAccs.end());
|
|
|
|
MemAccs.erase(MAIt);
|
2017-03-11 00:05:24 +08:00
|
|
|
|
2018-04-10 07:13:05 +08:00
|
|
|
removeAccessData(MA);
|
|
|
|
Parent.removeAccessData(MA);
|
|
|
|
}
|
2017-05-12 06:56:12 +08:00
|
|
|
|
2017-03-11 00:05:24 +08:00
|
|
|
auto It = InstructionToAccess.find(MA->getAccessInstruction());
|
|
|
|
if (It != InstructionToAccess.end()) {
|
|
|
|
It->second.remove(MA);
|
|
|
|
if (It->second.empty())
|
|
|
|
InstructionToAccess.erase(MA->getAccessInstruction());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-24 20:43:27 +08:00
|
|
|
MemoryAccess *ScopStmt::ensureValueRead(Value *V) {
|
|
|
|
MemoryAccess *Access = lookupInputAccessOf(V);
|
|
|
|
if (Access)
|
|
|
|
return Access;
|
|
|
|
|
|
|
|
ScopArrayInfo *SAI =
|
|
|
|
Parent.getOrCreateScopArrayInfo(V, V->getType(), {}, MemoryKind::Value);
|
|
|
|
Access = new MemoryAccess(this, nullptr, MemoryAccess::READ, V, V->getType(),
|
|
|
|
true, {}, {}, V, MemoryKind::Value);
|
|
|
|
Parent.addAccessFunction(Access);
|
|
|
|
Access->buildAccessRelation(SAI);
|
|
|
|
addAccess(Access);
|
|
|
|
Parent.addAccessData(Access);
|
|
|
|
return Access;
|
|
|
|
}
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
raw_ostream &polly::operator<<(raw_ostream &OS, const ScopStmt &S) {
|
|
|
|
S.print(OS, PollyPrintInstructions);
|
|
|
|
return OS;
|
2017-07-21 23:35:53 +08:00
|
|
|
}
|
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Scop class implement
|
2011-11-08 23:41:28 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
void Scop::setContext(isl::set NewContext) {
|
|
|
|
Context = NewContext.align_params(Context.get_space());
|
2011-11-15 19:38:44 +08:00
|
|
|
}
|
|
|
|
|
2017-06-21 06:53:02 +08:00
|
|
|
namespace {
|
2017-08-23 05:25:51 +08:00
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Remap parameter values but keep AddRecs valid wrt. invariant loads.
|
2015-11-04 00:47:58 +08:00
|
|
|
struct SCEVSensitiveParameterRewriter
|
2016-11-27 01:58:40 +08:00
|
|
|
: public SCEVRewriteVisitor<SCEVSensitiveParameterRewriter> {
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const ValueToValueMap &VMap;
|
2015-11-04 00:47:58 +08:00
|
|
|
|
|
|
|
public:
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
SCEVSensitiveParameterRewriter(const ValueToValueMap &VMap,
|
|
|
|
ScalarEvolution &SE)
|
2016-11-27 01:58:40 +08:00
|
|
|
: SCEVRewriteVisitor(SE), VMap(VMap) {}
|
2015-11-04 00:47:58 +08:00
|
|
|
|
|
|
|
static const SCEV *rewrite(const SCEV *E, ScalarEvolution &SE,
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const ValueToValueMap &VMap) {
|
2015-11-04 00:47:58 +08:00
|
|
|
SCEVSensitiveParameterRewriter SSPR(VMap, SE);
|
|
|
|
return SSPR.visit(E);
|
|
|
|
}
|
|
|
|
|
|
|
|
const SCEV *visitAddRecExpr(const SCEVAddRecExpr *E) {
|
|
|
|
auto *Start = visit(E->getStart());
|
|
|
|
auto *AddRec = SE.getAddRecExpr(SE.getConstant(E->getType(), 0),
|
|
|
|
visit(E->getStepRecurrence(SE)),
|
|
|
|
E->getLoop(), SCEV::FlagAnyWrap);
|
|
|
|
return SE.getAddExpr(Start, AddRec);
|
|
|
|
}
|
|
|
|
|
|
|
|
const SCEV *visitUnknown(const SCEVUnknown *E) {
|
|
|
|
if (auto *NewValue = VMap.lookup(E->getValue()))
|
|
|
|
return SE.getUnknown(NewValue);
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-06-21 06:53:02 +08:00
|
|
|
/// Check whether we should remap a SCEV expression.
|
|
|
|
struct SCEVFindInsideScop : public SCEVTraversal<SCEVFindInsideScop> {
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const ValueToValueMap &VMap;
|
2017-06-21 06:53:02 +08:00
|
|
|
bool FoundInside = false;
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const Scop *S;
|
2017-06-21 06:53:02 +08:00
|
|
|
|
|
|
|
public:
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
SCEVFindInsideScop(const ValueToValueMap &VMap, ScalarEvolution &SE,
|
|
|
|
const Scop *S)
|
2017-06-21 06:53:02 +08:00
|
|
|
: SCEVTraversal(*this), VMap(VMap), S(S) {}
|
|
|
|
|
|
|
|
static bool hasVariant(const SCEV *E, ScalarEvolution &SE,
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const ValueToValueMap &VMap, const Scop *S) {
|
2017-06-21 06:53:02 +08:00
|
|
|
SCEVFindInsideScop SFIS(VMap, SE, S);
|
|
|
|
SFIS.visitAll(E);
|
|
|
|
return SFIS.FoundInside;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool follow(const SCEV *E) {
|
|
|
|
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(E)) {
|
|
|
|
FoundInside |= S->getRegion().contains(AddRec->getLoop());
|
|
|
|
} else if (auto *Unknown = dyn_cast<SCEVUnknown>(E)) {
|
|
|
|
if (Instruction *I = dyn_cast<Instruction>(Unknown->getValue()))
|
|
|
|
FoundInside |= S->getRegion().contains(I) && !VMap.count(I);
|
|
|
|
}
|
|
|
|
return !FoundInside;
|
|
|
|
}
|
2017-08-23 05:25:51 +08:00
|
|
|
|
2017-06-21 06:53:02 +08:00
|
|
|
bool isDone() { return FoundInside; }
|
|
|
|
};
|
2017-08-23 05:25:51 +08:00
|
|
|
} // end anonymous namespace
|
2017-06-21 06:53:02 +08:00
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
const SCEV *Scop::getRepresentingInvariantLoadSCEV(const SCEV *E) const {
|
2017-06-21 06:53:02 +08:00
|
|
|
// Check whether it makes sense to rewrite the SCEV. (ScalarEvolution
|
|
|
|
// doesn't like addition between an AddRec and an expression that
|
|
|
|
// doesn't have a dominance relationship with it.)
|
|
|
|
if (SCEVFindInsideScop::hasVariant(E, *SE, InvEquivClassVMap, this))
|
|
|
|
return E;
|
|
|
|
|
|
|
|
// Rewrite SCEV.
|
|
|
|
return SCEVSensitiveParameterRewriter::rewrite(E, *SE, InvEquivClassVMap);
|
2015-10-10 01:12:26 +08:00
|
|
|
}
|
|
|
|
|
2017-05-27 23:18:46 +08:00
|
|
|
// This table of function names is used to translate parameter names in more
|
|
|
|
// human-readable names. This makes it easier to interpret Polly analysis
|
|
|
|
// results.
|
|
|
|
StringMap<std::string> KnownNames = {
|
|
|
|
{"_Z13get_global_idj", "global_id"},
|
|
|
|
{"_Z12get_local_idj", "local_id"},
|
|
|
|
{"_Z15get_global_sizej", "global_size"},
|
|
|
|
{"_Z14get_local_sizej", "local_size"},
|
|
|
|
{"_Z12get_work_dimv", "work_dim"},
|
|
|
|
{"_Z17get_global_offsetj", "global_offset"},
|
|
|
|
{"_Z12get_group_idj", "group_id"},
|
|
|
|
{"_Z14get_num_groupsj", "num_groups"},
|
|
|
|
};
|
|
|
|
|
|
|
|
static std::string getCallParamName(CallInst *Call) {
|
|
|
|
std::string Result;
|
|
|
|
raw_string_ostream OS(Result);
|
|
|
|
std::string Name = Call->getCalledFunction()->getName();
|
|
|
|
|
|
|
|
auto Iterator = KnownNames.find(Name);
|
|
|
|
if (Iterator != KnownNames.end())
|
2017-06-01 20:46:51 +08:00
|
|
|
Name = "__" + Iterator->getValue();
|
2017-05-27 23:18:46 +08:00
|
|
|
OS << Name;
|
|
|
|
for (auto &Operand : Call->arg_operands()) {
|
|
|
|
ConstantInt *Op = cast<ConstantInt>(&Operand);
|
|
|
|
OS << "_" << Op->getValue();
|
|
|
|
}
|
|
|
|
OS.flush();
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2016-04-26 00:15:13 +08:00
|
|
|
void Scop::createParameterId(const SCEV *Parameter) {
|
|
|
|
assert(Parameters.count(Parameter));
|
|
|
|
assert(!ParameterIds.count(Parameter));
|
2011-11-07 20:58:59 +08:00
|
|
|
|
2016-04-26 00:15:13 +08:00
|
|
|
std::string ParameterName = "p_" + std::to_string(getNumParams() - 1);
|
2015-11-17 19:54:51 +08:00
|
|
|
|
2017-05-27 23:18:46 +08:00
|
|
|
if (const SCEVUnknown *ValueParameter = dyn_cast<SCEVUnknown>(Parameter)) {
|
|
|
|
Value *Val = ValueParameter->getValue();
|
|
|
|
CallInst *Call = dyn_cast<CallInst>(Val);
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
|
2017-05-27 23:18:46 +08:00
|
|
|
if (Call && isConstCall(Call)) {
|
|
|
|
ParameterName = getCallParamName(Call);
|
|
|
|
} else if (UseInstructionNames) {
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
// If this parameter references a specific Value and this value has a name
|
|
|
|
// we use this name as it is likely to be unique and more useful than just
|
|
|
|
// a number.
|
|
|
|
if (Val->hasName())
|
|
|
|
ParameterName = Val->getName();
|
|
|
|
else if (LoadInst *LI = dyn_cast<LoadInst>(Val)) {
|
|
|
|
auto *LoadOrigin = LI->getPointerOperand()->stripInBoundsOffsets();
|
|
|
|
if (LoadOrigin->hasName()) {
|
|
|
|
ParameterName += "_loaded_from_";
|
|
|
|
ParameterName +=
|
|
|
|
LI->getPointerOperand()->stripInBoundsOffsets()->getName();
|
|
|
|
}
|
2015-11-17 19:54:51 +08:00
|
|
|
}
|
|
|
|
}
|
2011-11-15 19:38:55 +08:00
|
|
|
|
[ScopInfo] Do not use LLVM names to identify statements, arrays, and parameters
LLVM-IR names are commonly available in debug builds, but often not in release
builds. Hence, using LLVM-IR names to identify statements or memory reference
results makes the behavior of Polly depend on the compile mode. This is
undesirable. Hence, we now just number the statements instead of using LLVM-IR
names to identify them (this issue has previously been brought up by Zino
Benaissa).
However, as LLVM-IR names help in making test cases more readable, we add an
option '-polly-use-llvm-names' to still use LLVM-IR names. This flag is by
default set in the polly tests to make test cases more readable.
This change reduces the time in ScopInfo from 32 seconds to 2 seconds for the
following test case provided by Eli Friedman <efriedma@codeaurora.org> (already
used in one of the previous commits):
struct X { int x; };
void a();
#define SIG (int x, X **y, X **z)
typedef void (*fn)SIG;
#define FN { for (int i = 0; i < x; ++i) { (*y)[i].x += (*z)[i].x; } a(); }
#define FN5 FN FN FN FN FN
#define FN25 FN5 FN5 FN5 FN5
#define FN125 FN25 FN25 FN25 FN25 FN25
#define FN250 FN125 FN125
#define FN1250 FN250 FN250 FN250 FN250 FN250
void x SIG { FN1250 }
For a larger benchmark I have on-hand (10000 loops), this reduces the time for
running -polly-scops from 5 minutes to 4 minutes, a reduction by 20%.
The reason for this large speedup is that our previous use of printAsOperand
had a quadratic cost, as for each printed and unnamed operand the full function
was scanned to find the instruction number that identifies the operand.
We do not need to adjust the way memory reference ids are constructured, as
they do not use LLVM values.
Reviewed by: efriedma
Tags: #polly
Differential Revision: https://reviews.llvm.org/D32789
llvm-svn: 302072
2017-05-04 04:08:52 +08:00
|
|
|
ParameterName = getIslCompatibleName("", ParameterName, "");
|
|
|
|
}
|
2016-07-01 21:40:28 +08:00
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
isl::id Id = isl::id::alloc(getIslCtx(), ParameterName,
|
2017-08-14 01:54:51 +08:00
|
|
|
const_cast<void *>((const void *)Parameter));
|
2016-04-26 00:15:13 +08:00
|
|
|
ParameterIds[Parameter] = Id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::addParams(const ParameterSetTy &NewParameters) {
|
|
|
|
for (const SCEV *Parameter : NewParameters) {
|
|
|
|
// Normalize the SCEV to get the representing element for an invariant load.
|
|
|
|
Parameter = extractConstantFactor(Parameter, *SE).second;
|
|
|
|
Parameter = getRepresentingInvariantLoadSCEV(Parameter);
|
|
|
|
|
|
|
|
if (Parameters.insert(Parameter))
|
|
|
|
createParameterId(Parameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-07 03:31:27 +08:00
|
|
|
isl::id Scop::getIdForParam(const SCEV *Parameter) const {
|
2016-04-26 00:15:13 +08:00
|
|
|
// Normalize the SCEV to get the representing element for an invariant load.
|
|
|
|
Parameter = getRepresentingInvariantLoadSCEV(Parameter);
|
2017-08-14 01:54:51 +08:00
|
|
|
return ParameterIds.lookup(Parameter);
|
2011-11-07 20:58:59 +08:00
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-08-07 04:19:26 +08:00
|
|
|
isl::set Scop::addNonEmptyDomainConstraints(isl::set C) const {
|
2018-06-18 20:41:58 +08:00
|
|
|
isl::set DomainContext = getDomains().params();
|
|
|
|
return C.intersect_params(DomainContext);
|
2015-08-21 02:06:30 +08:00
|
|
|
}
|
|
|
|
|
2016-05-23 20:43:44 +08:00
|
|
|
bool Scop::isDominatedBy(const DominatorTree &DT, BasicBlock *BB) const {
|
|
|
|
return DT.dominates(BB, getEntry());
|
|
|
|
}
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
void Scop::addUserAssumptions(
|
|
|
|
AssumptionCache &AC, DominatorTree &DT, LoopInfo &LI,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2017-03-17 21:56:53 +08:00
|
|
|
for (auto &Assumption : AC.assumptions()) {
|
|
|
|
auto *CI = dyn_cast_or_null<CallInst>(Assumption);
|
|
|
|
if (!CI || CI->getNumArgOperands() != 1)
|
|
|
|
continue;
|
2016-05-10 22:00:57 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
bool InScop = contains(CI);
|
|
|
|
if (!InScop && !isDominatedBy(DT, CI->getParent()))
|
2015-11-12 11:25:01 +08:00
|
|
|
continue;
|
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
auto *L = LI.getLoopFor(CI->getParent());
|
|
|
|
auto *Val = CI->getArgOperand(0);
|
|
|
|
ParameterSetTy DetectedParams;
|
|
|
|
if (!isAffineConstraint(Val, &R, L, *SE, DetectedParams)) {
|
2017-07-18 07:58:33 +08:00
|
|
|
ORE.emit(
|
|
|
|
OptimizationRemarkAnalysis(DEBUG_TYPE, "IgnoreUserAssumption", CI)
|
|
|
|
<< "Non-affine user assumption ignored.");
|
2017-03-17 21:56:53 +08:00
|
|
|
continue;
|
|
|
|
}
|
2015-11-12 11:25:01 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
// Collect all newly introduced parameters.
|
|
|
|
ParameterSetTy NewParams;
|
|
|
|
for (auto *Param : DetectedParams) {
|
|
|
|
Param = extractConstantFactor(Param, *SE).second;
|
|
|
|
Param = getRepresentingInvariantLoadSCEV(Param);
|
|
|
|
if (Parameters.count(Param))
|
2016-04-26 02:51:27 +08:00
|
|
|
continue;
|
2017-03-17 21:56:53 +08:00
|
|
|
NewParams.insert(Param);
|
|
|
|
}
|
2016-05-10 21:06:42 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
SmallVector<isl_set *, 2> ConditionSets;
|
|
|
|
auto *TI = InScop ? CI->getParent()->getTerminator() : nullptr;
|
2017-07-26 21:25:28 +08:00
|
|
|
BasicBlock *BB = InScop ? CI->getParent() : getRegion().getEntry();
|
2017-11-20 06:13:34 +08:00
|
|
|
auto *Dom = InScop ? DomainMap[BB].copy() : Context.copy();
|
2017-07-26 21:25:28 +08:00
|
|
|
assert(Dom && "Cannot propagate a nullptr.");
|
|
|
|
bool Valid = buildConditionSets(*this, BB, Val, TI, L, Dom,
|
|
|
|
InvalidDomainMap, ConditionSets);
|
2017-03-17 21:56:53 +08:00
|
|
|
isl_set_free(Dom);
|
2015-11-12 11:25:01 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
if (!Valid)
|
|
|
|
continue;
|
2016-04-26 02:51:27 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
isl_set *AssumptionCtx = nullptr;
|
|
|
|
if (InScop) {
|
|
|
|
AssumptionCtx = isl_set_complement(isl_set_params(ConditionSets[1]));
|
|
|
|
isl_set_free(ConditionSets[0]);
|
|
|
|
} else {
|
|
|
|
AssumptionCtx = isl_set_complement(ConditionSets[1]);
|
|
|
|
AssumptionCtx = isl_set_intersect(AssumptionCtx, ConditionSets[0]);
|
|
|
|
}
|
2016-04-26 02:51:27 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
// Project out newly introduced parameters as they are not otherwise useful.
|
|
|
|
if (!NewParams.empty()) {
|
|
|
|
for (unsigned u = 0; u < isl_set_n_param(AssumptionCtx); u++) {
|
|
|
|
auto *Id = isl_set_get_dim_id(AssumptionCtx, isl_dim_param, u);
|
|
|
|
auto *Param = static_cast<const SCEV *>(isl_id_get_user(Id));
|
|
|
|
isl_id_free(Id);
|
2016-04-26 02:51:27 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
if (!NewParams.count(Param))
|
|
|
|
continue;
|
2016-12-15 17:25:14 +08:00
|
|
|
|
2017-03-17 21:56:53 +08:00
|
|
|
AssumptionCtx =
|
|
|
|
isl_set_project_out(AssumptionCtx, isl_dim_param, u--, 1);
|
2016-04-26 02:51:27 +08:00
|
|
|
}
|
2016-12-15 17:25:14 +08:00
|
|
|
}
|
2017-07-18 07:58:33 +08:00
|
|
|
ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "UserAssumption", CI)
|
|
|
|
<< "Use user assumption: " << stringFromIslObj(AssumptionCtx));
|
2017-11-20 06:13:34 +08:00
|
|
|
Context = Context.intersect(isl::manage(AssumptionCtx));
|
2015-11-12 11:25:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-16 18:19:29 +08:00
|
|
|
void Scop::addUserContext() {
|
|
|
|
if (UserContextStr.empty())
|
|
|
|
return;
|
|
|
|
|
2018-05-28 15:45:25 +08:00
|
|
|
isl::set UserContext = isl::set(getIslCtx(), UserContextStr.c_str());
|
|
|
|
isl::space Space = getParamSpace();
|
|
|
|
if (Space.dim(isl::dim::param) != UserContext.dim(isl::dim::param)) {
|
|
|
|
std::string SpaceStr = Space.to_str();
|
2015-08-16 18:19:29 +08:00
|
|
|
errs() << "Error: the context provided in -polly-context has not the same "
|
|
|
|
<< "number of dimensions than the computed context. Due to this "
|
|
|
|
<< "mismatch, the -polly-context option is ignored. Please provide "
|
|
|
|
<< "the context in the parameter space: " << SpaceStr << ".\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-28 15:45:25 +08:00
|
|
|
for (unsigned i = 0; i < Space.dim(isl::dim::param); i++) {
|
2017-11-20 06:13:34 +08:00
|
|
|
std::string NameContext = Context.get_dim_name(isl::dim::param, i);
|
2018-05-28 15:45:25 +08:00
|
|
|
std::string NameUserContext = UserContext.get_dim_name(isl::dim::param, i);
|
2015-08-16 18:19:29 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
if (NameContext != NameUserContext) {
|
2018-05-28 15:45:25 +08:00
|
|
|
std::string SpaceStr = Space.to_str();
|
2015-08-16 18:19:29 +08:00
|
|
|
errs() << "Error: the name of dimension " << i
|
|
|
|
<< " provided in -polly-context "
|
|
|
|
<< "is '" << NameUserContext << "', but the name in the computed "
|
|
|
|
<< "context is '" << NameContext
|
|
|
|
<< "'. Due to this name mismatch, "
|
|
|
|
<< "the -polly-context option is ignored. Please provide "
|
|
|
|
<< "the context in the parameter space: " << SpaceStr << ".\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-28 15:45:25 +08:00
|
|
|
UserContext = UserContext.set_dim_id(isl::dim::param, i,
|
|
|
|
Space.get_dim_id(isl::dim::param, i));
|
2015-08-16 18:19:29 +08:00
|
|
|
}
|
|
|
|
|
2018-05-28 15:45:25 +08:00
|
|
|
Context = Context.intersect(UserContext);
|
2015-08-16 18:19:29 +08:00
|
|
|
}
|
|
|
|
|
2011-11-08 23:41:13 +08:00
|
|
|
void Scop::buildContext() {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::space Space = isl::space::params_alloc(getIslCtx(), 0);
|
|
|
|
Context = isl::set::universe(Space);
|
|
|
|
InvalidContext = isl::set::empty(Space);
|
|
|
|
AssumedContext = isl::set::universe(Space);
|
2011-10-06 08:03:48 +08:00
|
|
|
}
|
|
|
|
|
2012-05-22 18:47:27 +08:00
|
|
|
void Scop::addParameterBounds() {
|
2016-04-26 00:15:13 +08:00
|
|
|
unsigned PDim = 0;
|
|
|
|
for (auto *Parameter : Parameters) {
|
|
|
|
ConstantRange SRange = SE->getSignedRange(Parameter);
|
2017-11-20 06:13:34 +08:00
|
|
|
Context = addRangeBoundsToSet(Context, SRange, PDim++, isl::dim::param);
|
2012-05-22 18:47:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
static std::vector<isl::id> getFortranArrayIds(Scop::array_range Arrays) {
|
|
|
|
std::vector<isl::id> OutermostSizeIds;
|
2017-05-19 23:07:45 +08:00
|
|
|
for (auto Array : Arrays) {
|
|
|
|
// To check if an array is a Fortran array, we check if it has a isl_pw_aff
|
|
|
|
// for its outermost dimension. Fortran arrays will have this since the
|
|
|
|
// outermost dimension size can be picked up from their runtime description.
|
|
|
|
// TODO: actually need to check if it has a FAD, but for now this works.
|
|
|
|
if (Array->getNumberOfDimensions() > 0) {
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
isl::pw_aff PwAff = Array->getDimensionSizePw(0);
|
2017-05-19 23:07:45 +08:00
|
|
|
if (!PwAff)
|
|
|
|
continue;
|
|
|
|
|
2018-06-18 20:49:47 +08:00
|
|
|
isl::id Id = PwAff.get_dim_id(isl::dim::param, 0);
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
assert(!Id.is_null() &&
|
|
|
|
"Invalid Id for PwAff expression in Fortran array");
|
2017-05-19 23:07:45 +08:00
|
|
|
OutermostSizeIds.push_back(Id);
|
|
|
|
}
|
|
|
|
}
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
return OutermostSizeIds;
|
|
|
|
}
|
2017-05-19 23:07:45 +08:00
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
// The FORTRAN array size parameters are known to be non-negative.
|
2017-11-20 06:13:34 +08:00
|
|
|
static isl::set boundFortranArrayParams(isl::set Context,
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
Scop::array_range Arrays) {
|
|
|
|
std::vector<isl::id> OutermostSizeIds;
|
|
|
|
OutermostSizeIds = getFortranArrayIds(Arrays);
|
2017-05-19 23:07:45 +08:00
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
for (isl::id Id : OutermostSizeIds) {
|
2017-11-20 06:13:34 +08:00
|
|
|
int dim = Context.find_dim_by_id(isl::dim::param, Id);
|
|
|
|
Context = Context.lower_bound_si(isl::dim::param, dim, 0);
|
2017-05-19 23:07:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Context;
|
|
|
|
}
|
|
|
|
|
2011-11-08 23:41:08 +08:00
|
|
|
void Scop::realignParams() {
|
2017-03-17 21:00:53 +08:00
|
|
|
if (PollyIgnoreParamBounds)
|
|
|
|
return;
|
|
|
|
|
2011-11-08 23:41:13 +08:00
|
|
|
// Add all parameters into a common model.
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
isl::space Space = getFullParamSpace();
|
2011-11-08 23:41:13 +08:00
|
|
|
|
|
|
|
// Align the parameters of all data structures to the model.
|
2017-11-20 06:13:34 +08:00
|
|
|
Context = Context.align_params(Space);
|
2011-11-08 23:41:13 +08:00
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
// Bound the size of the fortran array dimensions.
|
|
|
|
Context = boundFortranArrayParams(Context, arrays());
|
2017-05-19 23:07:45 +08:00
|
|
|
|
2016-05-10 20:18:22 +08:00
|
|
|
// As all parameters are known add bounds to them.
|
|
|
|
addParameterBounds();
|
|
|
|
|
2015-05-27 13:16:57 +08:00
|
|
|
for (ScopStmt &Stmt : *this)
|
|
|
|
Stmt.realignParams();
|
2016-06-02 23:07:41 +08:00
|
|
|
// Simplify the schedule according to the context too.
|
2017-11-20 06:13:34 +08:00
|
|
|
Schedule = Schedule.gist_domain_params(getContext());
|
2011-11-08 23:41:08 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
static isl::set simplifyAssumptionContext(isl::set AssumptionContext,
|
|
|
|
const Scop &S) {
|
2017-01-07 01:30:34 +08:00
|
|
|
// If we have modeled all blocks in the SCoP that have side effects we can
|
|
|
|
// simplify the context with the constraints that are needed for anything to
|
|
|
|
// be executed at all. However, if we have error blocks in the SCoP we already
|
|
|
|
// assumed some parameter combinations cannot occur and removed them from the
|
2015-11-09 04:16:39 +08:00
|
|
|
// domains, thus we cannot use the remaining domain to simplify the
|
|
|
|
// assumptions.
|
|
|
|
if (!S.hasErrorBlock()) {
|
2017-11-20 06:13:34 +08:00
|
|
|
auto DomainParameters = S.getDomains().params();
|
|
|
|
AssumptionContext = AssumptionContext.gist_params(DomainParameters);
|
2015-11-09 04:16:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
AssumptionContext = AssumptionContext.gist_params(S.getContext());
|
2015-09-16 06:52:53 +08:00
|
|
|
return AssumptionContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::simplifyContexts() {
|
2014-07-03 01:47:48 +08:00
|
|
|
// The parameter constraints of the iteration domains give us a set of
|
|
|
|
// constraints that need to hold for all cases where at least a single
|
|
|
|
// statement iteration is executed in the whole scop. We now simplify the
|
|
|
|
// assumed context under the assumption that such constraints hold and at
|
|
|
|
// least a single statement iteration is executed. For cases where no
|
|
|
|
// statement instances are executed, the assumptions we have taken about
|
|
|
|
// the executed code do not matter and can be changed.
|
|
|
|
//
|
|
|
|
// WARNING: This only holds if the assumptions we have taken do not reduce
|
|
|
|
// the set of statement instances that are executed. Otherwise we
|
|
|
|
// may run into a case where the iteration domains suggest that
|
2015-02-25 00:00:29 +08:00
|
|
|
// for a certain set of parameter constraints no code is executed,
|
2014-07-03 01:47:48 +08:00
|
|
|
// but in the original program some computation would have been
|
2015-02-25 00:00:29 +08:00
|
|
|
// performed. In such a case, modifying the run-time conditions and
|
|
|
|
// possibly influencing the run-time check may cause certain scops
|
2014-07-03 01:47:48 +08:00
|
|
|
// to not be executed.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// When delinearizing the following code:
|
|
|
|
//
|
|
|
|
// for (long i = 0; i < 100; i++)
|
|
|
|
// for (long j = 0; j < m; j++)
|
|
|
|
// A[i+p][j] = 1.0;
|
|
|
|
//
|
|
|
|
// we assume that the condition m <= 0 or (m >= 1 and p >= 0) holds as
|
2015-02-25 00:00:29 +08:00
|
|
|
// otherwise we would access out of bound data. Now, knowing that code is
|
2014-07-03 01:47:48 +08:00
|
|
|
// only executed for the case m >= 0, it is sufficient to assume p >= 0.
|
2015-09-16 06:52:53 +08:00
|
|
|
AssumedContext = simplifyAssumptionContext(AssumedContext, *this);
|
2017-11-20 06:13:34 +08:00
|
|
|
InvalidContext = InvalidContext.align_params(getParamSpace());
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Add the minimal/maximal access in @p Set to @p User.
|
2018-07-17 14:41:20 +08:00
|
|
|
///
|
|
|
|
/// @return True if more accesses should be added, false if we reached the
|
|
|
|
/// maximal number of run-time checks to be generated.
|
|
|
|
static bool buildMinMaxAccess(isl::set Set,
|
|
|
|
Scop::MinMaxVectorTy &MinMaxAccesses, Scop &S) {
|
2017-06-23 16:05:20 +08:00
|
|
|
isl::pw_multi_aff MinPMA, MaxPMA;
|
|
|
|
isl::pw_aff LastDimAff;
|
|
|
|
isl::aff OneAff;
|
2014-09-18 19:17:17 +08:00
|
|
|
unsigned Pos;
|
|
|
|
|
2017-06-23 16:05:20 +08:00
|
|
|
Set = Set.remove_divs();
|
2018-05-10 00:23:56 +08:00
|
|
|
polly::simplify(Set);
|
2016-04-22 19:38:19 +08:00
|
|
|
|
2018-05-16 22:05:03 +08:00
|
|
|
if (Set.n_basic_set() > RunTimeChecksMaxAccessDisjuncts)
|
2018-05-10 00:23:56 +08:00
|
|
|
Set = Set.simple_hull();
|
2016-04-22 19:38:19 +08:00
|
|
|
|
2014-09-27 19:02:39 +08:00
|
|
|
// Restrict the number of parameters involved in the access as the lexmin/
|
|
|
|
// lexmax computation will take too long if this number is high.
|
|
|
|
//
|
|
|
|
// Experiments with a simple test case using an i7 4800MQ:
|
|
|
|
//
|
|
|
|
// #Parameters involved | Time (in sec)
|
|
|
|
// 6 | 0.01
|
|
|
|
// 7 | 0.04
|
|
|
|
// 8 | 0.12
|
|
|
|
// 9 | 0.40
|
|
|
|
// 10 | 1.54
|
|
|
|
// 11 | 6.78
|
|
|
|
// 12 | 30.38
|
|
|
|
//
|
2017-06-23 16:05:20 +08:00
|
|
|
if (isl_set_n_param(Set.get()) > RunTimeChecksMaxParameters) {
|
2014-09-27 19:02:39 +08:00
|
|
|
unsigned InvolvedParams = 0;
|
2017-06-23 16:05:20 +08:00
|
|
|
for (unsigned u = 0, e = isl_set_n_param(Set.get()); u < e; u++)
|
|
|
|
if (Set.involves_dims(isl::dim::param, u, 1))
|
2014-09-27 19:02:39 +08:00
|
|
|
InvolvedParams++;
|
|
|
|
|
2017-06-23 16:05:20 +08:00
|
|
|
if (InvolvedParams > RunTimeChecksMaxParameters)
|
2018-07-17 14:41:20 +08:00
|
|
|
return false;
|
2014-09-27 19:02:39 +08:00
|
|
|
}
|
|
|
|
|
2017-06-23 16:05:27 +08:00
|
|
|
MinPMA = Set.lexmin_pw_multi_aff();
|
|
|
|
MaxPMA = Set.lexmax_pw_multi_aff();
|
2017-05-19 11:45:00 +08:00
|
|
|
|
2017-06-23 16:05:20 +08:00
|
|
|
MinPMA = MinPMA.coalesce();
|
|
|
|
MaxPMA = MaxPMA.coalesce();
|
2014-10-07 22:37:59 +08:00
|
|
|
|
2014-09-18 19:17:17 +08:00
|
|
|
// Adjust the last dimension of the maximal access by one as we want to
|
|
|
|
// enclose the accessed memory region by MinPMA and MaxPMA. The pointer
|
|
|
|
// we test during code generation might now point after the end of the
|
|
|
|
// allocated array but we will never dereference it anyway.
|
2018-05-10 00:23:56 +08:00
|
|
|
assert((!MaxPMA || MaxPMA.dim(isl::dim::out)) &&
|
|
|
|
"Assumed at least one output dimension");
|
|
|
|
|
2017-06-23 16:05:20 +08:00
|
|
|
Pos = MaxPMA.dim(isl::dim::out) - 1;
|
|
|
|
LastDimAff = MaxPMA.get_pw_aff(Pos);
|
|
|
|
OneAff = isl::aff(isl::local_space(LastDimAff.get_domain_space()));
|
|
|
|
OneAff = OneAff.add_constant_si(1);
|
|
|
|
LastDimAff = LastDimAff.add(OneAff);
|
|
|
|
MaxPMA = MaxPMA.set_pw_aff(Pos, LastDimAff);
|
|
|
|
|
2018-05-10 00:23:56 +08:00
|
|
|
if (!MinPMA || !MaxPMA)
|
2018-07-17 14:41:20 +08:00
|
|
|
return false;
|
2018-05-10 00:23:56 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
MinMaxAccesses.push_back(std::make_pair(MinPMA, MaxPMA));
|
2017-06-23 16:05:20 +08:00
|
|
|
|
2018-07-17 14:41:20 +08:00
|
|
|
return true;
|
2014-09-18 19:17:17 +08:00
|
|
|
}
|
|
|
|
|
2018-06-18 20:53:26 +08:00
|
|
|
static isl::set getAccessDomain(MemoryAccess *MA) {
|
|
|
|
isl::set Domain = MA->getStatement()->getDomain();
|
|
|
|
Domain = Domain.project_out(isl::dim::set, 0, Domain.n_dim());
|
|
|
|
return Domain.reset_tuple_id();
|
2014-10-01 20:42:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Wrapper function to calculate minimal/maximal accesses to each array.
|
2017-01-16 23:49:04 +08:00
|
|
|
static bool calculateMinMaxAccess(Scop::AliasGroupTy AliasGroup, Scop &S,
|
2015-07-26 21:14:38 +08:00
|
|
|
Scop::MinMaxVectorTy &MinMaxAccesses) {
|
2017-06-23 16:05:20 +08:00
|
|
|
MinMaxAccesses.reserve(AliasGroup.size());
|
2017-01-16 23:49:04 +08:00
|
|
|
|
2017-08-07 05:42:25 +08:00
|
|
|
isl::union_set Domains = S.getDomains();
|
2017-08-07 04:11:59 +08:00
|
|
|
isl::union_map Accesses = isl::union_map::empty(S.getParamSpace());
|
2017-01-16 23:49:04 +08:00
|
|
|
|
|
|
|
for (MemoryAccess *MA : AliasGroup)
|
2018-04-29 08:28:26 +08:00
|
|
|
Accesses = Accesses.add_map(MA->getAccessRelation());
|
2017-06-23 16:05:20 +08:00
|
|
|
|
|
|
|
Accesses = Accesses.intersect_domain(Domains);
|
|
|
|
isl::union_set Locations = Accesses.range();
|
|
|
|
|
2018-07-17 14:41:20 +08:00
|
|
|
bool LimitReached = false;
|
|
|
|
for (isl::set Set : Locations.get_set_list()) {
|
|
|
|
LimitReached |= !buildMinMaxAccess(Set, MinMaxAccesses, S);
|
|
|
|
if (LimitReached)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !LimitReached;
|
2015-07-24 01:04:54 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Helper to treat non-affine regions and basic blocks the same.
|
2015-08-31 05:13:53 +08:00
|
|
|
///
|
|
|
|
///{
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Return the block that is the representing block for @p RN.
|
2015-08-31 05:13:53 +08:00
|
|
|
static inline BasicBlock *getRegionNodeBasicBlock(RegionNode *RN) {
|
|
|
|
return RN->isSubRegion() ? RN->getNodeAs<Region>()->getEntry()
|
|
|
|
: RN->getNodeAs<BasicBlock>();
|
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Return the @p idx'th block that is executed after @p RN.
|
2015-09-28 17:33:22 +08:00
|
|
|
static inline BasicBlock *
|
2018-10-15 18:42:50 +08:00
|
|
|
getRegionNodeSuccessor(RegionNode *RN, Instruction *TI, unsigned idx) {
|
2015-08-31 05:13:53 +08:00
|
|
|
if (RN->isSubRegion()) {
|
|
|
|
assert(idx == 0);
|
|
|
|
return RN->getNodeAs<Region>()->getExit();
|
|
|
|
}
|
2015-09-28 17:33:22 +08:00
|
|
|
return TI->getSuccessor(idx);
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Return the smallest loop surrounding @p RN.
|
2015-08-31 05:13:53 +08:00
|
|
|
static inline Loop *getRegionNodeLoop(RegionNode *RN, LoopInfo &LI) {
|
2017-03-08 00:17:55 +08:00
|
|
|
if (!RN->isSubRegion()) {
|
|
|
|
BasicBlock *BB = RN->getNodeAs<BasicBlock>();
|
|
|
|
Loop *L = LI.getLoopFor(BB);
|
|
|
|
|
|
|
|
// Unreachable statements are not considered to belong to a LLVM loop, as
|
|
|
|
// they are not part of an actual loop in the control flow graph.
|
|
|
|
// Nevertheless, we handle certain unreachable statements that are common
|
|
|
|
// when modeling run-time bounds checks as being part of the loop to be
|
|
|
|
// able to model them and to later eliminate the run-time bounds checks.
|
|
|
|
//
|
|
|
|
// Specifically, for basic blocks that terminate in an unreachable and
|
2017-06-08 20:06:15 +08:00
|
|
|
// where the immediate predecessor is part of a loop, we assume these
|
2017-03-08 00:17:55 +08:00
|
|
|
// basic blocks belong to the loop the predecessor belongs to. This
|
|
|
|
// allows us to model the following code.
|
|
|
|
//
|
|
|
|
// for (i = 0; i < N; i++) {
|
|
|
|
// if (i > 1024)
|
|
|
|
// abort(); <- this abort might be translated to an
|
|
|
|
// unreachable
|
|
|
|
//
|
|
|
|
// A[i] = ...
|
|
|
|
// }
|
|
|
|
if (!L && isa<UnreachableInst>(BB->getTerminator()) && BB->getPrevNode())
|
|
|
|
L = LI.getLoopFor(BB->getPrevNode());
|
|
|
|
return L;
|
|
|
|
}
|
2015-08-31 05:13:53 +08:00
|
|
|
|
|
|
|
Region *NonAffineSubRegion = RN->getNodeAs<Region>();
|
|
|
|
Loop *L = LI.getLoopFor(NonAffineSubRegion->getEntry());
|
|
|
|
while (L && NonAffineSubRegion->contains(L))
|
|
|
|
L = L->getParentLoop();
|
|
|
|
return L;
|
|
|
|
}
|
|
|
|
|
2017-03-08 00:17:55 +08:00
|
|
|
/// Get the number of blocks in @p L.
|
|
|
|
///
|
|
|
|
/// The number of blocks in a loop are the number of basic blocks actually
|
|
|
|
/// belonging to the loop, as well as all single basic blocks that the loop
|
|
|
|
/// exits to and which terminate in an unreachable instruction. We do not
|
|
|
|
/// allow such basic blocks in the exit of a scop, hence they belong to the
|
|
|
|
/// scop and represent run-time conditions which we want to model and
|
|
|
|
/// subsequently speculate away.
|
|
|
|
///
|
|
|
|
/// @see getRegionNodeLoop for additional details.
|
2017-06-20 01:44:02 +08:00
|
|
|
unsigned getNumBlocksInLoop(Loop *L) {
|
|
|
|
unsigned NumBlocks = L->getNumBlocks();
|
2017-08-23 05:25:51 +08:00
|
|
|
SmallVector<BasicBlock *, 4> ExitBlocks;
|
2017-03-08 00:17:55 +08:00
|
|
|
L->getExitBlocks(ExitBlocks);
|
|
|
|
|
|
|
|
for (auto ExitBlock : ExitBlocks) {
|
|
|
|
if (isa<UnreachableInst>(ExitBlock->getTerminator()))
|
|
|
|
NumBlocks++;
|
|
|
|
}
|
|
|
|
return NumBlocks;
|
|
|
|
}
|
|
|
|
|
2015-09-10 23:27:46 +08:00
|
|
|
static inline unsigned getNumBlocksInRegionNode(RegionNode *RN) {
|
|
|
|
if (!RN->isSubRegion())
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
Region *R = RN->getNodeAs<Region>();
|
2016-02-01 09:55:08 +08:00
|
|
|
return std::distance(R->block_begin(), R->block_end());
|
2015-09-10 23:27:46 +08:00
|
|
|
}
|
|
|
|
|
2015-10-08 04:32:43 +08:00
|
|
|
static bool containsErrorBlock(RegionNode *RN, const Region &R, LoopInfo &LI,
|
|
|
|
const DominatorTree &DT) {
|
2015-10-02 07:48:18 +08:00
|
|
|
if (!RN->isSubRegion())
|
2015-10-08 04:32:43 +08:00
|
|
|
return isErrorBlock(*RN->getNodeAs<BasicBlock>(), R, LI, DT);
|
2015-10-02 07:48:18 +08:00
|
|
|
for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
|
2015-10-08 04:32:43 +08:00
|
|
|
if (isErrorBlock(*BB, R, LI, DT))
|
2015-10-02 07:48:18 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-31 05:13:53 +08:00
|
|
|
///}
|
|
|
|
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::set Scop::getDomainConditions(const ScopStmt *Stmt) const {
|
2016-02-25 06:08:24 +08:00
|
|
|
return getDomainConditions(Stmt->getEntryBlock());
|
2015-09-16 06:49:04 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::set Scop::getDomainConditions(BasicBlock *BB) const {
|
2016-04-08 18:32:26 +08:00
|
|
|
auto DIt = DomainMap.find(BB);
|
|
|
|
if (DIt != DomainMap.end())
|
2017-08-07 05:42:38 +08:00
|
|
|
return DIt->getSecond();
|
2016-04-08 18:32:26 +08:00
|
|
|
|
|
|
|
auto &RI = *R.getRegionInfo();
|
|
|
|
auto *BBR = RI.getRegionFor(BB);
|
|
|
|
while (BBR->getEntry() == BB)
|
|
|
|
BBR = BBR->getParent();
|
|
|
|
return getDomainConditions(BBR->getEntry());
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2017-07-15 17:01:31 +08:00
|
|
|
bool Scop::buildDomains(Region *R, DominatorTree &DT, LoopInfo &LI,
|
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2016-05-19 20:34:57 +08:00
|
|
|
bool IsOnlyNonAffineRegion = isNonAffineSubRegion(R);
|
2015-08-31 21:56:32 +08:00
|
|
|
auto *EntryBB = R->getEntry();
|
2016-01-26 19:01:41 +08:00
|
|
|
auto *L = IsOnlyNonAffineRegion ? nullptr : LI.getLoopFor(EntryBB);
|
|
|
|
int LD = getRelativeLoopDepth(L);
|
2017-11-20 06:13:34 +08:00
|
|
|
auto *S = isl_set_universe(isl_space_set_alloc(getIslCtx().get(), 0, LD + 1));
|
2015-09-10 23:27:46 +08:00
|
|
|
|
|
|
|
while (LD-- >= 0) {
|
|
|
|
L = L->getParentLoop();
|
|
|
|
}
|
|
|
|
|
2017-07-15 17:01:31 +08:00
|
|
|
InvalidDomainMap[EntryBB] = isl::manage(isl_set_empty(isl_set_get_space(S)));
|
2017-07-15 20:41:32 +08:00
|
|
|
DomainMap[EntryBB] = isl::manage(S);
|
2015-08-31 05:13:53 +08:00
|
|
|
|
2016-01-26 19:01:41 +08:00
|
|
|
if (IsOnlyNonAffineRegion)
|
2016-05-10 20:19:47 +08:00
|
|
|
return !containsErrorBlock(R->getNode(), *R, LI, DT);
|
2016-03-30 04:02:05 +08:00
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!buildDomainsWithBranchConstraints(R, DT, LI, InvalidDomainMap))
|
2016-03-30 04:02:05 +08:00
|
|
|
return false;
|
2015-09-14 19:15:07 +08:00
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!propagateDomainConstraints(R, DT, LI, InvalidDomainMap))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2015-11-22 19:06:51 +08:00
|
|
|
|
|
|
|
// Error blocks and blocks dominated by them have been assumed to never be
|
|
|
|
// executed. Representing them in the Scop does not add any value. In fact,
|
|
|
|
// it is likely to cause issues during construction of the ScopStmts. The
|
2016-04-08 18:30:09 +08:00
|
|
|
// contents of error blocks have not been verified to be expressible and
|
2015-11-22 19:06:51 +08:00
|
|
|
// will cause problems when building up a ScopStmt for them.
|
|
|
|
// Furthermore, basic blocks dominated by error blocks may reference
|
|
|
|
// instructions in the error block which, if the error block is not modeled,
|
2016-04-08 18:30:09 +08:00
|
|
|
// can themselves not be constructed properly. To this end we will replace
|
|
|
|
// the domains of error blocks and those only reachable via error blocks
|
|
|
|
// with an empty set. Additionally, we will record for each block under which
|
2016-04-12 17:57:34 +08:00
|
|
|
// parameter combination it would be reached via an error block in its
|
2016-04-23 21:02:23 +08:00
|
|
|
// InvalidDomain. This information is needed during load hoisting.
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!propagateInvalidStmtDomains(R, DT, LI, InvalidDomainMap))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2016-04-08 18:30:09 +08:00
|
|
|
|
2016-03-30 04:02:05 +08:00
|
|
|
return true;
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Adjust the dimensions of @p Dom that was constructed for @p OldL
|
2016-04-04 15:50:40 +08:00
|
|
|
/// to be compatible to domains constructed for loop @p NewL.
|
|
|
|
///
|
|
|
|
/// This function assumes @p NewL and @p OldL are equal or there is a CFG
|
|
|
|
/// edge from @p OldL to @p NewL.
|
2018-06-18 21:01:52 +08:00
|
|
|
static isl::set adjustDomainDimensions(Scop &S, isl::set Dom, Loop *OldL,
|
|
|
|
Loop *NewL) {
|
2016-04-04 15:50:40 +08:00
|
|
|
// If the loops are the same there is nothing to do.
|
|
|
|
if (NewL == OldL)
|
|
|
|
return Dom;
|
|
|
|
|
|
|
|
int OldDepth = S.getRelativeLoopDepth(OldL);
|
|
|
|
int NewDepth = S.getRelativeLoopDepth(NewL);
|
|
|
|
// If both loops are non-affine loops there is nothing to do.
|
|
|
|
if (OldDepth == -1 && NewDepth == -1)
|
|
|
|
return Dom;
|
|
|
|
|
|
|
|
// Distinguish three cases:
|
|
|
|
// 1) The depth is the same but the loops are not.
|
|
|
|
// => One loop was left one was entered.
|
|
|
|
// 2) The depth increased from OldL to NewL.
|
|
|
|
// => One loop was entered, none was left.
|
|
|
|
// 3) The depth decreased from OldL to NewL.
|
|
|
|
// => Loops were left were difference of the depths defines how many.
|
|
|
|
if (OldDepth == NewDepth) {
|
|
|
|
assert(OldL->getParentLoop() == NewL->getParentLoop());
|
2018-06-18 20:49:47 +08:00
|
|
|
Dom = Dom.project_out(isl::dim::set, NewDepth, 1);
|
|
|
|
Dom = Dom.add_dims(isl::dim::set, 1);
|
2016-04-04 15:50:40 +08:00
|
|
|
} else if (OldDepth < NewDepth) {
|
|
|
|
assert(OldDepth + 1 == NewDepth);
|
|
|
|
auto &R = S.getRegion();
|
|
|
|
(void)R;
|
|
|
|
assert(NewL->getParentLoop() == OldL ||
|
|
|
|
((!OldL || !R.contains(OldL)) && R.contains(NewL)));
|
2018-06-18 20:49:47 +08:00
|
|
|
Dom = Dom.add_dims(isl::dim::set, 1);
|
2016-04-04 15:50:40 +08:00
|
|
|
} else {
|
|
|
|
assert(OldDepth > NewDepth);
|
|
|
|
int Diff = OldDepth - NewDepth;
|
2018-06-18 20:49:47 +08:00
|
|
|
int NumDim = Dom.n_dim();
|
2016-04-04 15:50:40 +08:00
|
|
|
assert(NumDim >= Diff);
|
2018-06-18 20:49:47 +08:00
|
|
|
Dom = Dom.project_out(isl::dim::set, NumDim - Diff, Diff);
|
2016-04-04 15:50:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Dom;
|
|
|
|
}
|
2016-04-04 15:57:39 +08:00
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
bool Scop::propagateInvalidStmtDomains(
|
|
|
|
Region *R, DominatorTree &DT, LoopInfo &LI,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2016-04-08 18:30:09 +08:00
|
|
|
ReversePostOrderTraversal<Region *> RTraversal(R);
|
|
|
|
for (auto *RN : RTraversal) {
|
|
|
|
|
|
|
|
// Recurse for affine subregions but go on for basic blocks and non-affine
|
|
|
|
// subregions.
|
|
|
|
if (RN->isSubRegion()) {
|
|
|
|
Region *SubRegion = RN->getNodeAs<Region>();
|
2016-05-19 20:34:57 +08:00
|
|
|
if (!isNonAffineSubRegion(SubRegion)) {
|
2017-06-29 20:47:41 +08:00
|
|
|
propagateInvalidStmtDomains(SubRegion, DT, LI, InvalidDomainMap);
|
2016-04-08 18:30:09 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ContainsErrorBlock = containsErrorBlock(RN, getRegion(), LI, DT);
|
|
|
|
BasicBlock *BB = getRegionNodeBasicBlock(RN);
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set &Domain = DomainMap[BB];
|
2016-04-08 18:30:09 +08:00
|
|
|
assert(Domain && "Cannot propagate a nullptr");
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set InvalidDomain = InvalidDomainMap[BB];
|
2017-06-29 20:47:41 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
bool IsInvalidBlock = ContainsErrorBlock || Domain.is_subset(InvalidDomain);
|
2016-04-08 18:30:09 +08:00
|
|
|
|
2016-04-23 21:02:23 +08:00
|
|
|
if (!IsInvalidBlock) {
|
2017-07-15 20:41:32 +08:00
|
|
|
InvalidDomain = InvalidDomain.intersect(Domain);
|
2016-04-08 18:30:09 +08:00
|
|
|
} else {
|
2016-04-23 21:02:23 +08:00
|
|
|
InvalidDomain = Domain;
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set DomPar = Domain.params();
|
2017-11-20 06:13:34 +08:00
|
|
|
recordAssumption(ERRORBLOCK, DomPar, BB->getTerminator()->getDebugLoc(),
|
|
|
|
AS_RESTRICTION);
|
2018-08-02 06:28:32 +08:00
|
|
|
Domain = isl::set::empty(Domain.get_space());
|
2016-04-08 18:30:09 +08:00
|
|
|
}
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
if (InvalidDomain.is_empty()) {
|
|
|
|
InvalidDomainMap[BB] = InvalidDomain;
|
2016-04-08 18:30:09 +08:00
|
|
|
continue;
|
2016-04-12 17:57:34 +08:00
|
|
|
}
|
|
|
|
|
2016-04-23 21:02:23 +08:00
|
|
|
auto *BBLoop = getRegionNodeLoop(RN, LI);
|
2016-04-08 18:30:09 +08:00
|
|
|
auto *TI = BB->getTerminator();
|
|
|
|
unsigned NumSuccs = RN->isSubRegion() ? 1 : TI->getNumSuccessors();
|
|
|
|
for (unsigned u = 0; u < NumSuccs; u++) {
|
|
|
|
auto *SuccBB = getRegionNodeSuccessor(RN, TI, u);
|
2016-04-12 17:57:34 +08:00
|
|
|
|
|
|
|
// Skip successors outside the SCoP.
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!contains(SuccBB))
|
2016-04-12 17:57:34 +08:00
|
|
|
continue;
|
|
|
|
|
2016-04-25 21:34:50 +08:00
|
|
|
// Skip backedges.
|
|
|
|
if (DT.dominates(SuccBB, BB))
|
|
|
|
continue;
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
Loop *SuccBBLoop = getFirstNonBoxedLoopFor(SuccBB, LI, getBoxedLoops());
|
|
|
|
|
2018-06-18 21:01:52 +08:00
|
|
|
auto AdjustedInvalidDomain =
|
|
|
|
adjustDomainDimensions(*this, InvalidDomain, BBLoop, SuccBBLoop);
|
2017-06-29 20:47:41 +08:00
|
|
|
|
2018-05-16 22:05:03 +08:00
|
|
|
isl::set SuccInvalidDomain = InvalidDomainMap[SuccBB];
|
|
|
|
SuccInvalidDomain = SuccInvalidDomain.unite(AdjustedInvalidDomain);
|
|
|
|
SuccInvalidDomain = SuccInvalidDomain.coalesce();
|
|
|
|
unsigned NumConjucts = SuccInvalidDomain.n_basic_set();
|
2017-06-29 20:47:41 +08:00
|
|
|
|
2018-05-16 22:05:03 +08:00
|
|
|
InvalidDomainMap[SuccBB] = SuccInvalidDomain;
|
2016-04-08 18:30:09 +08:00
|
|
|
|
2016-05-02 20:25:18 +08:00
|
|
|
// Check if the maximal number of domain disjunctions was reached.
|
2016-04-08 18:30:09 +08:00
|
|
|
// In case this happens we will bail.
|
2017-02-17 03:11:33 +08:00
|
|
|
if (NumConjucts < MaxDisjunctsInDomain)
|
2016-04-08 18:30:09 +08:00
|
|
|
continue;
|
|
|
|
|
Remove freed InvalidDomains from InvalidDomainMap.
Summary:
Since r306667, propagateInvalidStmtDomains gets a reference to an
InvalidDomainMap. As part of the branch leading to return false, the respective
domain is freed. It is, however, not removed from the InvalidDomainMap, leaking
a pointer to a freed object which results in a use-after-free. Fix this be
removing the domain from the map before returning.
We tried to derive a test case that reliably failes, but did not succeed in
producing one. Hence, for now the failures in our LNT bots must be sufficient
to keep this issue tested.
Reviewers: grosser, Meinersbur, bollu
Subscribers: bollu, nandini12396, pollydev, llvm-commits
Differential Revision: https://reviews.llvm.org/D34971
llvm-svn: 307499
2017-07-09 23:47:17 +08:00
|
|
|
InvalidDomainMap.erase(BB);
|
2017-07-18 07:58:33 +08:00
|
|
|
invalidate(COMPLEXITY, TI->getDebugLoc(), TI->getParent());
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2016-04-08 18:30:09 +08:00
|
|
|
}
|
2016-04-23 21:02:23 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
InvalidDomainMap[BB] = InvalidDomain;
|
2016-04-08 18:30:09 +08:00
|
|
|
}
|
2016-05-10 21:06:42 +08:00
|
|
|
|
|
|
|
return true;
|
2016-04-08 18:30:09 +08:00
|
|
|
}
|
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
void Scop::propagateDomainConstraintsToRegionExit(
|
|
|
|
BasicBlock *BB, Loop *BBLoop,
|
2017-06-29 20:47:41 +08:00
|
|
|
SmallPtrSetImpl<BasicBlock *> &FinishedExitBlocks, LoopInfo &LI,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2016-04-04 15:57:39 +08:00
|
|
|
// Check if the block @p BB is the entry of a region. If so we propagate it's
|
|
|
|
// domain to the exit block of the region. Otherwise we are done.
|
|
|
|
auto *RI = R.getRegionInfo();
|
|
|
|
auto *BBReg = RI ? RI->getRegionFor(BB) : nullptr;
|
|
|
|
auto *ExitBB = BBReg ? BBReg->getExit() : nullptr;
|
2016-05-23 20:40:48 +08:00
|
|
|
if (!BBReg || BBReg->getEntry() != BB || !contains(ExitBB))
|
2016-04-04 15:57:39 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Do not propagate the domain if there is a loop backedge inside the region
|
2017-01-07 01:30:34 +08:00
|
|
|
// that would prevent the exit block from being executed.
|
2016-04-04 15:57:39 +08:00
|
|
|
auto *L = BBLoop;
|
2016-05-23 20:40:48 +08:00
|
|
|
while (L && contains(L)) {
|
2016-04-04 15:57:39 +08:00
|
|
|
SmallVector<BasicBlock *, 4> LatchBBs;
|
|
|
|
BBLoop->getLoopLatches(LatchBBs);
|
|
|
|
for (auto *LatchBB : LatchBBs)
|
|
|
|
if (BB != LatchBB && BBReg->contains(LatchBB))
|
|
|
|
return;
|
|
|
|
L = L->getParentLoop();
|
|
|
|
}
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set Domain = DomainMap[BB];
|
2016-04-04 15:57:39 +08:00
|
|
|
assert(Domain && "Cannot propagate a nullptr");
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
Loop *ExitBBLoop = getFirstNonBoxedLoopFor(ExitBB, LI, getBoxedLoops());
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// Since the dimensions of @p BB and @p ExitBB might be different we have to
|
|
|
|
// adjust the domain before we can propagate it.
|
2018-06-18 21:01:52 +08:00
|
|
|
isl::set AdjustedDomain =
|
|
|
|
adjustDomainDimensions(*this, Domain, BBLoop, ExitBBLoop);
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set &ExitDomain = DomainMap[ExitBB];
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// If the exit domain is not yet created we set it otherwise we "add" the
|
|
|
|
// current domain.
|
2017-07-15 20:41:32 +08:00
|
|
|
ExitDomain = ExitDomain ? AdjustedDomain.unite(ExitDomain) : AdjustedDomain;
|
2016-04-04 15:57:39 +08:00
|
|
|
|
2016-04-23 21:02:23 +08:00
|
|
|
// Initialize the invalid domain.
|
2017-07-15 20:41:32 +08:00
|
|
|
InvalidDomainMap[ExitBB] = ExitDomain.empty(ExitDomain.get_space());
|
2016-04-23 21:02:23 +08:00
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
FinishedExitBlocks.insert(ExitBB);
|
|
|
|
}
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
bool Scop::buildDomainsWithBranchConstraints(
|
|
|
|
Region *R, DominatorTree &DT, LoopInfo &LI,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2015-08-31 05:13:53 +08:00
|
|
|
// To create the domain for each block in R we iterate over all blocks and
|
|
|
|
// subregions in R and propagate the conditions under which the current region
|
|
|
|
// element is executed. To this end we iterate in reverse post order over R as
|
|
|
|
// it ensures that we first visit all predecessors of a region node (either a
|
|
|
|
// basic block or a subregion) before we visit the region node itself.
|
|
|
|
// Initially, only the domain for the SCoP region entry block is set and from
|
|
|
|
// there we propagate the current domain to all successors, however we add the
|
|
|
|
// condition that the successor is actually executed next.
|
|
|
|
// As we are only interested in non-loop carried constraints here we can
|
|
|
|
// simply skip loop back edges.
|
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
SmallPtrSet<BasicBlock *, 8> FinishedExitBlocks;
|
2015-08-31 05:13:53 +08:00
|
|
|
ReversePostOrderTraversal<Region *> RTraversal(R);
|
|
|
|
for (auto *RN : RTraversal) {
|
|
|
|
// Recurse for affine subregions but go on for basic blocks and non-affine
|
|
|
|
// subregions.
|
|
|
|
if (RN->isSubRegion()) {
|
|
|
|
Region *SubRegion = RN->getNodeAs<Region>();
|
2016-05-19 20:34:57 +08:00
|
|
|
if (!isNonAffineSubRegion(SubRegion)) {
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!buildDomainsWithBranchConstraints(SubRegion, DT, LI,
|
|
|
|
InvalidDomainMap))
|
2016-03-30 04:02:05 +08:00
|
|
|
return false;
|
2015-08-31 05:13:53 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 16:42:20 +08:00
|
|
|
if (containsErrorBlock(RN, getRegion(), LI, DT))
|
2015-11-09 04:16:39 +08:00
|
|
|
HasErrorBlock = true;
|
2015-10-02 07:48:18 +08:00
|
|
|
|
2015-08-31 05:13:53 +08:00
|
|
|
BasicBlock *BB = getRegionNodeBasicBlock(RN);
|
2018-10-15 18:42:50 +08:00
|
|
|
Instruction *TI = BB->getTerminator();
|
2015-09-11 01:51:27 +08:00
|
|
|
|
2015-11-11 16:42:20 +08:00
|
|
|
if (isa<UnreachableInst>(TI))
|
|
|
|
continue;
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set Domain = DomainMap.lookup(BB);
|
2016-02-27 14:59:30 +08:00
|
|
|
if (!Domain)
|
2015-09-11 01:51:27 +08:00
|
|
|
continue;
|
2017-07-15 20:41:32 +08:00
|
|
|
MaxLoopDepth = std::max(MaxLoopDepth, isl_set_n_dim(Domain.get()));
|
2015-08-31 05:13:53 +08:00
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
auto *BBLoop = getRegionNodeLoop(RN, LI);
|
|
|
|
// Propagate the domain from BB directly to blocks that have a superset
|
|
|
|
// domain, at the moment only region exit nodes of regions that start in BB.
|
2017-06-29 20:47:41 +08:00
|
|
|
propagateDomainConstraintsToRegionExit(BB, BBLoop, FinishedExitBlocks, LI,
|
|
|
|
InvalidDomainMap);
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// If all successors of BB have been set a domain through the propagation
|
|
|
|
// above we do not need to build condition sets but can just skip this
|
|
|
|
// block. However, it is important to note that this is a local property
|
|
|
|
// with regards to the region @p R. To this end FinishedExitBlocks is a
|
|
|
|
// local variable.
|
|
|
|
auto IsFinishedRegionExit = [&FinishedExitBlocks](BasicBlock *SuccBB) {
|
|
|
|
return FinishedExitBlocks.count(SuccBB);
|
|
|
|
};
|
|
|
|
if (std::all_of(succ_begin(BB), succ_end(BB), IsFinishedRegionExit))
|
|
|
|
continue;
|
2015-08-31 05:13:53 +08:00
|
|
|
|
|
|
|
// Build the condition sets for the successor nodes of the current region
|
|
|
|
// node. If it is a non-affine subregion we will always execute the single
|
|
|
|
// exit node, hence the single entry node domain is the condition set. For
|
|
|
|
// basic blocks we use the helper function buildConditionSets.
|
2015-09-28 17:33:22 +08:00
|
|
|
SmallVector<isl_set *, 8> ConditionSets;
|
2015-08-31 05:13:53 +08:00
|
|
|
if (RN->isSubRegion())
|
2017-07-15 20:41:32 +08:00
|
|
|
ConditionSets.push_back(Domain.copy());
|
|
|
|
else if (!buildConditionSets(*this, BB, TI, BBLoop, Domain.get(),
|
2017-06-29 20:47:41 +08:00
|
|
|
InvalidDomainMap, ConditionSets))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2015-08-31 05:13:53 +08:00
|
|
|
|
|
|
|
// Now iterate over the successors and set their initial domain based on
|
|
|
|
// their condition set. We skip back edges here and have to be careful when
|
|
|
|
// we leave a loop not to keep constraints over a dimension that doesn't
|
|
|
|
// exist anymore.
|
2015-09-28 17:33:22 +08:00
|
|
|
assert(RN->isSubRegion() || TI->getNumSuccessors() == ConditionSets.size());
|
2015-08-31 05:13:53 +08:00
|
|
|
for (unsigned u = 0, e = ConditionSets.size(); u < e; u++) {
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set CondSet = isl::manage(ConditionSets[u]);
|
2015-09-28 17:33:22 +08:00
|
|
|
BasicBlock *SuccBB = getRegionNodeSuccessor(RN, TI, u);
|
2015-08-31 05:13:53 +08:00
|
|
|
|
2016-04-19 22:49:05 +08:00
|
|
|
// Skip blocks outside the region.
|
2017-07-15 20:41:32 +08:00
|
|
|
if (!contains(SuccBB))
|
2016-04-19 22:49:05 +08:00
|
|
|
continue;
|
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
// If we propagate the domain of some block to "SuccBB" we do not have to
|
|
|
|
// adjust the domain.
|
2017-07-15 20:41:32 +08:00
|
|
|
if (FinishedExitBlocks.count(SuccBB))
|
2016-04-04 15:57:39 +08:00
|
|
|
continue;
|
|
|
|
|
2015-08-31 05:13:53 +08:00
|
|
|
// Skip back edges.
|
2017-07-15 20:41:32 +08:00
|
|
|
if (DT.dominates(SuccBB, BB))
|
2015-08-31 05:13:53 +08:00
|
|
|
continue;
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
Loop *SuccBBLoop = getFirstNonBoxedLoopFor(SuccBB, LI, getBoxedLoops());
|
|
|
|
|
2018-06-18 20:49:47 +08:00
|
|
|
CondSet = adjustDomainDimensions(*this, CondSet, BBLoop, SuccBBLoop);
|
2015-08-31 05:13:53 +08:00
|
|
|
|
|
|
|
// Set the domain for the successor or merge it with an existing domain in
|
|
|
|
// case there are multiple paths (without loop back edges) to the
|
|
|
|
// successor block.
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set &SuccDomain = DomainMap[SuccBB];
|
2016-03-23 06:05:32 +08:00
|
|
|
|
2016-04-23 21:02:23 +08:00
|
|
|
if (SuccDomain) {
|
2017-07-15 20:41:32 +08:00
|
|
|
SuccDomain = SuccDomain.unite(CondSet).coalesce();
|
2016-04-23 21:02:23 +08:00
|
|
|
} else {
|
|
|
|
// Initialize the invalid domain.
|
2017-07-15 20:41:32 +08:00
|
|
|
InvalidDomainMap[SuccBB] = CondSet.empty(CondSet.get_space());
|
2016-04-23 21:02:23 +08:00
|
|
|
SuccDomain = CondSet;
|
|
|
|
}
|
2016-03-30 04:02:05 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
SuccDomain = SuccDomain.detect_equalities();
|
2017-05-23 12:26:28 +08:00
|
|
|
|
2016-05-02 20:25:18 +08:00
|
|
|
// Check if the maximal number of domain disjunctions was reached.
|
2016-03-30 04:02:05 +08:00
|
|
|
// In case this happens we will clean up and bail.
|
2018-05-16 22:05:03 +08:00
|
|
|
if (SuccDomain.n_basic_set() < MaxDisjunctsInDomain)
|
2016-03-30 04:02:05 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
invalidate(COMPLEXITY, DebugLoc());
|
|
|
|
while (++u < ConditionSets.size())
|
|
|
|
isl_set_free(ConditionSets[u]);
|
|
|
|
return false;
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
}
|
2016-03-30 04:02:05 +08:00
|
|
|
|
|
|
|
return true;
|
2015-08-31 05:13:53 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 01:31:38 +08:00
|
|
|
isl::set Scop::getPredecessorDomainConstraints(BasicBlock *BB, isl::set Domain,
|
|
|
|
DominatorTree &DT,
|
|
|
|
LoopInfo &LI) {
|
2016-04-04 15:57:39 +08:00
|
|
|
// If @p BB is the ScopEntry we are done
|
|
|
|
if (R.getEntry() == BB)
|
2017-08-07 01:31:38 +08:00
|
|
|
return isl::set::universe(Domain.get_space());
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// The region info of this function.
|
|
|
|
auto &RI = *R.getRegionInfo();
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
Loop *BBLoop = getFirstNonBoxedLoopFor(BB, LI, getBoxedLoops());
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// A domain to collect all predecessor domains, thus all conditions under
|
|
|
|
// which the block is executed. To this end we start with the empty domain.
|
2017-08-07 01:31:38 +08:00
|
|
|
isl::set PredDom = isl::set::empty(Domain.get_space());
|
2016-04-04 15:57:39 +08:00
|
|
|
|
|
|
|
// Set of regions of which the entry block domain has been propagated to BB.
|
|
|
|
// all predecessors inside any of the regions can be skipped.
|
|
|
|
SmallSet<Region *, 8> PropagatedRegions;
|
|
|
|
|
|
|
|
for (auto *PredBB : predecessors(BB)) {
|
|
|
|
// Skip backedges.
|
|
|
|
if (DT.dominates(BB, PredBB))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If the predecessor is in a region we used for propagation we can skip it.
|
|
|
|
auto PredBBInRegion = [PredBB](Region *PR) { return PR->contains(PredBB); };
|
|
|
|
if (std::any_of(PropagatedRegions.begin(), PropagatedRegions.end(),
|
|
|
|
PredBBInRegion)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if there is a valid region we can use for propagation, thus look
|
|
|
|
// for a region that contains the predecessor and has @p BB as exit block.
|
|
|
|
auto *PredR = RI.getRegionFor(PredBB);
|
|
|
|
while (PredR->getExit() != BB && !PredR->contains(BB))
|
|
|
|
PredR->getParent();
|
|
|
|
|
|
|
|
// If a valid region for propagation was found use the entry of that region
|
|
|
|
// for propagation, otherwise the PredBB directly.
|
|
|
|
if (PredR->getExit() == BB) {
|
|
|
|
PredBB = PredR->getEntry();
|
|
|
|
PropagatedRegions.insert(PredR);
|
|
|
|
}
|
|
|
|
|
2018-06-18 20:49:47 +08:00
|
|
|
isl::set PredBBDom = getDomainConditions(PredBB);
|
2017-06-29 20:47:41 +08:00
|
|
|
Loop *PredBBLoop = getFirstNonBoxedLoopFor(PredBB, LI, getBoxedLoops());
|
2016-04-04 15:57:39 +08:00
|
|
|
PredBBDom = adjustDomainDimensions(*this, PredBBDom, PredBBLoop, BBLoop);
|
2018-06-18 20:49:47 +08:00
|
|
|
PredDom = PredDom.unite(PredBBDom);
|
2016-04-04 15:57:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return PredDom;
|
|
|
|
}
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
bool Scop::propagateDomainConstraints(
|
|
|
|
Region *R, DominatorTree &DT, LoopInfo &LI,
|
2017-07-15 17:01:31 +08:00
|
|
|
DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2015-09-10 21:00:06 +08:00
|
|
|
// Iterate over the region R and propagate the domain constrains from the
|
|
|
|
// predecessors to the current node. In contrast to the
|
|
|
|
// buildDomainsWithBranchConstraints function, this one will pull the domain
|
|
|
|
// information from the predecessors instead of pushing it to the successors.
|
|
|
|
// Additionally, we assume the domains to be already present in the domain
|
|
|
|
// map here. However, we iterate again in reverse post order so we know all
|
|
|
|
// predecessors have been visited before a block or non-affine subregion is
|
|
|
|
// visited.
|
|
|
|
|
|
|
|
ReversePostOrderTraversal<Region *> RTraversal(R);
|
|
|
|
for (auto *RN : RTraversal) {
|
|
|
|
// Recurse for affine subregions but go on for basic blocks and non-affine
|
|
|
|
// subregions.
|
|
|
|
if (RN->isSubRegion()) {
|
|
|
|
Region *SubRegion = RN->getNodeAs<Region>();
|
2016-05-19 20:34:57 +08:00
|
|
|
if (!isNonAffineSubRegion(SubRegion)) {
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!propagateDomainConstraints(SubRegion, DT, LI, InvalidDomainMap))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2015-09-10 21:00:06 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicBlock *BB = getRegionNodeBasicBlock(RN);
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set &Domain = DomainMap[BB];
|
2016-04-06 00:18:53 +08:00
|
|
|
assert(Domain);
|
2015-10-02 07:48:18 +08:00
|
|
|
|
2015-09-10 21:00:06 +08:00
|
|
|
// Under the union of all predecessor conditions we can reach this block.
|
2017-08-07 01:31:38 +08:00
|
|
|
isl::set PredDom = getPredecessorDomainConstraints(BB, Domain, DT, LI);
|
2017-07-15 20:41:32 +08:00
|
|
|
Domain = Domain.intersect(PredDom).coalesce();
|
2017-08-07 04:11:59 +08:00
|
|
|
Domain = Domain.align_params(getParamSpace());
|
2015-09-11 01:51:27 +08:00
|
|
|
|
2016-04-04 15:57:39 +08:00
|
|
|
Loop *BBLoop = getRegionNodeLoop(RN, LI);
|
2016-05-23 20:40:48 +08:00
|
|
|
if (BBLoop && BBLoop->getHeader() == BB && contains(BBLoop))
|
2017-06-29 20:47:41 +08:00
|
|
|
if (!addLoopBoundsToHeaderDomain(BBLoop, LI, InvalidDomainMap))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2015-09-10 21:00:06 +08:00
|
|
|
}
|
2016-05-10 21:06:42 +08:00
|
|
|
|
|
|
|
return true;
|
2015-09-10 21:00:06 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Create a map to map from a given iteration to a subsequent iteration.
|
|
|
|
///
|
|
|
|
/// This map maps from SetSpace -> SetSpace where the dimensions @p Dim
|
|
|
|
/// is incremented by one and all other dimensions are equal, e.g.,
|
2015-09-10 21:00:06 +08:00
|
|
|
/// [i0, i1, i2, i3] -> [i0, i1, i2 + 1, i3]
|
2016-09-02 14:33:33 +08:00
|
|
|
///
|
|
|
|
/// if @p Dim is 2 and @p SetSpace has 4 dimensions.
|
2018-05-24 02:41:40 +08:00
|
|
|
static isl::map createNextIterationMap(isl::space SetSpace, unsigned Dim) {
|
|
|
|
isl::space MapSpace = SetSpace.map_from_set();
|
|
|
|
isl::map NextIterationMap = isl::map::universe(MapSpace);
|
|
|
|
for (unsigned u = 0; u < NextIterationMap.dim(isl::dim::in); u++)
|
2015-09-10 21:00:06 +08:00
|
|
|
if (u != Dim)
|
|
|
|
NextIterationMap =
|
2018-05-24 02:41:40 +08:00
|
|
|
NextIterationMap.equate(isl::dim::in, u, isl::dim::out, u);
|
|
|
|
isl::constraint C =
|
|
|
|
isl::constraint::alloc_equality(isl::local_space(MapSpace));
|
|
|
|
C = C.set_constant_si(1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, Dim, 1);
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, Dim, -1);
|
|
|
|
NextIterationMap = NextIterationMap.add_constraint(C);
|
2015-09-10 21:00:06 +08:00
|
|
|
return NextIterationMap;
|
|
|
|
}
|
|
|
|
|
2017-06-29 20:47:41 +08:00
|
|
|
bool Scop::addLoopBoundsToHeaderDomain(
|
2017-07-15 17:01:31 +08:00
|
|
|
Loop *L, LoopInfo &LI, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
|
2015-09-21 00:15:32 +08:00
|
|
|
int LoopDepth = getRelativeLoopDepth(L);
|
|
|
|
assert(LoopDepth >= 0 && "Loop in region should have at least depth one");
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2015-09-21 00:15:32 +08:00
|
|
|
BasicBlock *HeaderBB = L->getHeader();
|
|
|
|
assert(DomainMap.count(HeaderBB));
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set &HeaderBBDom = DomainMap[HeaderBB];
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2018-05-24 02:41:40 +08:00
|
|
|
isl::map NextIterationMap =
|
|
|
|
createNextIterationMap(HeaderBBDom.get_space(), LoopDepth);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set UnionBackedgeCondition = HeaderBBDom.empty(HeaderBBDom.get_space());
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
SmallVector<BasicBlock *, 4> LatchBlocks;
|
2015-09-21 00:15:32 +08:00
|
|
|
L->getLoopLatches(LatchBlocks);
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2015-09-21 00:15:32 +08:00
|
|
|
for (BasicBlock *LatchBB : LatchBlocks) {
|
2015-10-02 07:48:18 +08:00
|
|
|
// If the latch is only reachable via error statements we skip it.
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set LatchBBDom = DomainMap.lookup(LatchBB);
|
2015-10-02 07:48:18 +08:00
|
|
|
if (!LatchBBDom)
|
|
|
|
continue;
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set BackedgeCondition = nullptr;
|
2015-09-21 00:15:32 +08:00
|
|
|
|
2018-10-15 18:42:50 +08:00
|
|
|
Instruction *TI = LatchBB->getTerminator();
|
2015-09-28 17:33:22 +08:00
|
|
|
BranchInst *BI = dyn_cast<BranchInst>(TI);
|
2016-11-10 13:20:29 +08:00
|
|
|
assert(BI && "Only branch instructions allowed in loop latches");
|
|
|
|
|
|
|
|
if (BI->isUnconditional())
|
2017-07-15 20:41:32 +08:00
|
|
|
BackedgeCondition = LatchBBDom;
|
2015-09-21 00:15:32 +08:00
|
|
|
else {
|
2015-09-28 17:33:22 +08:00
|
|
|
SmallVector<isl_set *, 8> ConditionSets;
|
2015-09-21 00:15:32 +08:00
|
|
|
int idx = BI->getSuccessor(0) != HeaderBB;
|
2017-07-15 20:41:32 +08:00
|
|
|
if (!buildConditionSets(*this, LatchBB, TI, L, LatchBBDom.get(),
|
|
|
|
InvalidDomainMap, ConditionSets))
|
2016-05-10 21:06:42 +08:00
|
|
|
return false;
|
2015-09-20 23:00:20 +08:00
|
|
|
|
2015-09-21 00:15:32 +08:00
|
|
|
// Free the non back edge condition set as we do not need it.
|
|
|
|
isl_set_free(ConditionSets[1 - idx]);
|
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
BackedgeCondition = isl::manage(ConditionSets[idx]);
|
2015-09-20 23:00:20 +08:00
|
|
|
}
|
2015-09-10 21:00:06 +08:00
|
|
|
|
2015-09-21 00:15:32 +08:00
|
|
|
int LatchLoopDepth = getRelativeLoopDepth(LI.getLoopFor(LatchBB));
|
|
|
|
assert(LatchLoopDepth >= LoopDepth);
|
2017-07-15 20:41:32 +08:00
|
|
|
BackedgeCondition = BackedgeCondition.project_out(
|
|
|
|
isl::dim::set, LoopDepth + 1, LatchLoopDepth - LoopDepth);
|
|
|
|
UnionBackedgeCondition = UnionBackedgeCondition.unite(BackedgeCondition);
|
2015-09-10 21:00:06 +08:00
|
|
|
}
|
2015-09-21 00:15:32 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::map ForwardMap = ForwardMap.lex_le(HeaderBBDom.get_space());
|
2015-09-21 00:15:32 +08:00
|
|
|
for (int i = 0; i < LoopDepth; i++)
|
2017-07-15 20:41:32 +08:00
|
|
|
ForwardMap = ForwardMap.equate(isl::dim::in, i, isl::dim::out, i);
|
2015-09-21 00:15:32 +08:00
|
|
|
|
2017-07-15 20:41:32 +08:00
|
|
|
isl::set UnionBackedgeConditionComplement =
|
|
|
|
UnionBackedgeCondition.complement();
|
2015-09-21 00:15:32 +08:00
|
|
|
UnionBackedgeConditionComplement =
|
2017-07-15 20:41:32 +08:00
|
|
|
UnionBackedgeConditionComplement.lower_bound_si(isl::dim::set, LoopDepth,
|
|
|
|
0);
|
|
|
|
UnionBackedgeConditionComplement =
|
|
|
|
UnionBackedgeConditionComplement.apply(ForwardMap);
|
|
|
|
HeaderBBDom = HeaderBBDom.subtract(UnionBackedgeConditionComplement);
|
|
|
|
HeaderBBDom = HeaderBBDom.apply(NextIterationMap);
|
2015-09-21 00:15:32 +08:00
|
|
|
|
2018-06-02 03:12:00 +08:00
|
|
|
auto Parts = partitionSetParts(HeaderBBDom, LoopDepth);
|
|
|
|
HeaderBBDom = Parts.second;
|
2015-09-21 00:15:32 +08:00
|
|
|
|
2015-09-21 00:59:23 +08:00
|
|
|
// Check if there is a <nsw> tagged AddRec for this loop and if so do not add
|
|
|
|
// the bounded assumptions to the context as they are already implied by the
|
|
|
|
// <nsw> tag.
|
2018-06-02 03:12:00 +08:00
|
|
|
if (Affinator.hasNSWAddRecForLoop(L))
|
2016-05-10 21:06:42 +08:00
|
|
|
return true;
|
2015-09-21 00:59:23 +08:00
|
|
|
|
2018-06-02 03:12:00 +08:00
|
|
|
isl::set UnboundedCtx = Parts.first.params();
|
2016-04-12 21:27:35 +08:00
|
|
|
recordAssumption(INFINITELOOP, UnboundedCtx,
|
|
|
|
HeaderBB->getTerminator()->getDebugLoc(), AS_RESTRICTION);
|
2016-05-10 21:06:42 +08:00
|
|
|
return true;
|
2015-09-10 21:00:06 +08:00
|
|
|
}
|
|
|
|
|
2016-05-23 17:26:46 +08:00
|
|
|
MemoryAccess *Scop::lookupBasePtrAccess(MemoryAccess *MA) {
|
2017-02-09 18:11:58 +08:00
|
|
|
Value *PointerBase = MA->getOriginalBaseAddr();
|
2016-05-23 17:26:46 +08:00
|
|
|
|
2017-02-09 17:34:46 +08:00
|
|
|
auto *PointerBaseInst = dyn_cast<Instruction>(PointerBase);
|
2016-05-23 17:26:46 +08:00
|
|
|
if (!PointerBaseInst)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
auto *BasePtrStmt = getStmtFor(PointerBaseInst);
|
|
|
|
if (!BasePtrStmt)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return BasePtrStmt->getArrayAccessOrNULLFor(PointerBaseInst);
|
|
|
|
}
|
|
|
|
|
2016-06-02 19:06:54 +08:00
|
|
|
bool Scop::buildAliasChecks(AliasAnalysis &AA) {
|
2015-08-21 02:30:08 +08:00
|
|
|
if (!PollyUseRuntimeAliasChecks)
|
2016-06-02 19:06:54 +08:00
|
|
|
return true;
|
2015-08-21 02:30:08 +08:00
|
|
|
|
2016-11-18 05:41:08 +08:00
|
|
|
if (buildAliasGroups(AA)) {
|
|
|
|
// Aliasing assumptions do not go through addAssumption but we still want to
|
|
|
|
// collect statistics so we do it here explicitly.
|
|
|
|
if (MinMaxAliasGroups.size())
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsAliasing++;
|
2016-06-02 19:06:54 +08:00
|
|
|
return true;
|
2016-11-18 05:41:08 +08:00
|
|
|
}
|
2015-08-21 02:30:08 +08:00
|
|
|
|
|
|
|
// If a problem occurs while building the alias groups we need to delete
|
|
|
|
// this SCoP and pretend it wasn't valid in the first place. To this end
|
|
|
|
// we make the assumed context infeasible.
|
2015-12-12 17:52:26 +08:00
|
|
|
invalidate(ALIASING, DebugLoc());
|
2015-08-21 02:30:08 +08:00
|
|
|
|
2018-05-15 21:37:17 +08:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "\n\nNOTE: Run time checks for " << getNameStr()
|
|
|
|
<< " could not be created as the number of parameters involved "
|
|
|
|
"is too high. The SCoP will be "
|
|
|
|
"dismissed.\nUse:\n\t--polly-rtc-max-parameters=X\nto adjust "
|
|
|
|
"the maximal number of parameters but be advised that the "
|
|
|
|
"compile time might increase exponentially.\n\n");
|
2016-06-02 19:06:54 +08:00
|
|
|
return false;
|
2015-08-21 02:30:08 +08:00
|
|
|
}
|
|
|
|
|
2017-02-10 07:12:22 +08:00
|
|
|
std::tuple<Scop::AliasGroupVectorTy, DenseSet<const ScopArrayInfo *>>
|
2017-01-16 22:07:57 +08:00
|
|
|
Scop::buildAliasGroupsForAccesses(AliasAnalysis &AA) {
|
2014-09-18 19:17:17 +08:00
|
|
|
AliasSetTracker AST(AA);
|
|
|
|
|
|
|
|
DenseMap<Value *, MemoryAccess *> PtrToAcc;
|
2017-02-10 07:12:22 +08:00
|
|
|
DenseSet<const ScopArrayInfo *> HasWriteAccess;
|
2015-05-27 13:16:57 +08:00
|
|
|
for (ScopStmt &Stmt : *this) {
|
2014-10-07 01:43:00 +08:00
|
|
|
|
2018-06-18 20:49:47 +08:00
|
|
|
isl::set StmtDomain = Stmt.getDomain();
|
|
|
|
bool StmtDomainEmpty = StmtDomain.is_empty();
|
2017-01-16 22:07:57 +08:00
|
|
|
|
|
|
|
// Statements with an empty domain will never be executed.
|
2014-10-07 01:43:00 +08:00
|
|
|
if (StmtDomainEmpty)
|
|
|
|
continue;
|
|
|
|
|
2015-05-27 13:16:57 +08:00
|
|
|
for (MemoryAccess *MA : Stmt) {
|
ScopInfo: Harmonize the different array kinds
Over time different vocabulary has been introduced to describe the different
memory objects in Polly, resulting in different - often inconsistent - naming
schemes in different parts of Polly. We now standartize this to the following
scheme:
KindArray, KindValue, KindPHI, KindExitPHI
| ------- isScalar -----------|
In most cases this naming scheme has already been used previously (this
minimizes changes and ensures we remain consistent with previous publications).
The main change is that we remove KindScalar to clearify the difference between
a scalar as a memory object of kind Value, PHI or ExitPHI and a value (former
KindScalar) which is a memory object modeling a llvm::Value.
We also move all documentation to the Kind* enum in the ScopArrayInfo class,
remove the second enum in the MemoryAccess class and update documentation to be
formulated from the perspective of the memory object, rather than the memory
access. The terms "Implicit"/"Explicit", formerly used to describe memory
accesses, have been dropped. From the perspective of memory accesses they
described the different memory kinds well - especially from the perspective of
code generation - but just from the perspective of a memory object it seems more
straightforward to talk about scalars and arrays, rather than explicit and
implicit arrays. The last comment is clearly subjective, though. A less
subjective reason to go for these terms is the historic use both in mailing list
discussions and publications.
llvm-svn: 255467
2015-12-14 03:59:01 +08:00
|
|
|
if (MA->isScalarKind())
|
2014-09-18 19:17:17 +08:00
|
|
|
continue;
|
2014-10-01 20:40:46 +08:00
|
|
|
if (!MA->isRead())
|
2017-02-10 07:12:22 +08:00
|
|
|
HasWriteAccess.insert(MA->getScopArrayInfo());
|
2016-01-28 01:09:17 +08:00
|
|
|
MemAccInst Acc(MA->getAccessInstruction());
|
2016-02-27 09:49:58 +08:00
|
|
|
if (MA->isRead() && isa<MemTransferInst>(Acc))
|
2016-10-25 21:37:43 +08:00
|
|
|
PtrToAcc[cast<MemTransferInst>(Acc)->getRawSource()] = MA;
|
2016-02-22 03:13:19 +08:00
|
|
|
else
|
|
|
|
PtrToAcc[Acc.getPointerOperand()] = MA;
|
2014-09-18 19:17:17 +08:00
|
|
|
AST.add(Acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 22:07:57 +08:00
|
|
|
AliasGroupVectorTy AliasGroups;
|
2014-09-18 19:17:17 +08:00
|
|
|
for (AliasSet &AS : AST) {
|
2014-10-08 10:23:48 +08:00
|
|
|
if (AS.isMustAlias() || AS.isForwardingAliasSet())
|
2014-09-18 19:17:17 +08:00
|
|
|
continue;
|
|
|
|
AliasGroupTy AG;
|
2016-02-22 00:37:25 +08:00
|
|
|
for (auto &PR : AS)
|
2014-09-18 19:17:17 +08:00
|
|
|
AG.push_back(PtrToAcc[PR.getValue()]);
|
2016-02-22 03:13:19 +08:00
|
|
|
if (AG.size() < 2)
|
|
|
|
continue;
|
2014-09-18 19:17:17 +08:00
|
|
|
AliasGroups.push_back(std::move(AG));
|
|
|
|
}
|
|
|
|
|
2017-01-16 22:07:57 +08:00
|
|
|
return std::make_tuple(AliasGroups, HasWriteAccess);
|
|
|
|
}
|
|
|
|
|
2017-01-16 22:08:00 +08:00
|
|
|
void Scop::splitAliasGroupsByDomain(AliasGroupVectorTy &AliasGroups) {
|
2014-10-01 20:42:37 +08:00
|
|
|
for (unsigned u = 0; u < AliasGroups.size(); u++) {
|
|
|
|
AliasGroupTy NewAG;
|
|
|
|
AliasGroupTy &AG = AliasGroups[u];
|
|
|
|
AliasGroupTy::iterator AGI = AG.begin();
|
2018-06-18 20:53:26 +08:00
|
|
|
isl::set AGDomain = getAccessDomain(*AGI);
|
2014-10-01 20:42:37 +08:00
|
|
|
while (AGI != AG.end()) {
|
|
|
|
MemoryAccess *MA = *AGI;
|
2018-06-18 20:53:26 +08:00
|
|
|
isl::set MADomain = getAccessDomain(MA);
|
|
|
|
if (AGDomain.is_disjoint(MADomain)) {
|
2014-10-01 20:42:37 +08:00
|
|
|
NewAG.push_back(MA);
|
|
|
|
AGI = AG.erase(AGI);
|
|
|
|
} else {
|
2018-06-18 20:53:26 +08:00
|
|
|
AGDomain = AGDomain.unite(MADomain);
|
2014-10-01 20:42:37 +08:00
|
|
|
AGI++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NewAG.size() > 1)
|
|
|
|
AliasGroups.push_back(std::move(NewAG));
|
|
|
|
}
|
2017-01-16 22:08:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Scop::buildAliasGroups(AliasAnalysis &AA) {
|
|
|
|
// To create sound alias checks we perform the following steps:
|
|
|
|
// o) We partition each group into read only and non read only accesses.
|
|
|
|
// o) For each group with more than one base pointer we then compute minimal
|
|
|
|
// and maximal accesses to each array of a group in read only and non
|
|
|
|
// read only partitions separately.
|
|
|
|
AliasGroupVectorTy AliasGroups;
|
2017-02-10 07:12:22 +08:00
|
|
|
DenseSet<const ScopArrayInfo *> HasWriteAccess;
|
2017-01-16 22:08:00 +08:00
|
|
|
|
|
|
|
std::tie(AliasGroups, HasWriteAccess) = buildAliasGroupsForAccesses(AA);
|
|
|
|
|
|
|
|
splitAliasGroupsByDomain(AliasGroups);
|
2014-10-01 20:42:37 +08:00
|
|
|
|
2014-10-01 20:40:46 +08:00
|
|
|
for (AliasGroupTy &AG : AliasGroups) {
|
2017-06-23 16:05:31 +08:00
|
|
|
if (!hasFeasibleRuntimeContext())
|
|
|
|
return false;
|
|
|
|
|
2017-06-23 16:05:27 +08:00
|
|
|
{
|
2017-11-20 06:13:34 +08:00
|
|
|
IslMaxOperationsGuard MaxOpGuard(getIslCtx().get(), OptComputeOut);
|
2017-06-23 16:05:27 +08:00
|
|
|
bool Valid = buildAliasGroup(AG, HasWriteAccess);
|
|
|
|
if (!Valid)
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-20 06:13:34 +08:00
|
|
|
if (isl_ctx_last_error(getIslCtx().get()) == isl_error_quota) {
|
2017-06-23 16:05:27 +08:00
|
|
|
invalidate(COMPLEXITY, DebugLoc());
|
2017-01-16 23:49:07 +08:00
|
|
|
return false;
|
2017-06-23 16:05:27 +08:00
|
|
|
}
|
2017-01-16 23:49:07 +08:00
|
|
|
}
|
2014-10-01 20:40:46 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-01 20:42:37 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
bool Scop::buildAliasGroup(Scop::AliasGroupTy &AliasGroup,
|
2017-02-10 07:12:22 +08:00
|
|
|
DenseSet<const ScopArrayInfo *> HasWriteAccess) {
|
2017-01-16 23:49:07 +08:00
|
|
|
AliasGroupTy ReadOnlyAccesses;
|
|
|
|
AliasGroupTy ReadWriteAccesses;
|
2017-02-10 07:12:22 +08:00
|
|
|
SmallPtrSet<const ScopArrayInfo *, 4> ReadWriteArrays;
|
2017-02-19 04:51:29 +08:00
|
|
|
SmallPtrSet<const ScopArrayInfo *, 4> ReadOnlyArrays;
|
2014-10-01 20:40:46 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
if (AliasGroup.size() < 2)
|
|
|
|
return true;
|
2014-10-01 20:40:46 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
for (MemoryAccess *Access : AliasGroup) {
|
2017-07-18 07:58:33 +08:00
|
|
|
ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "PossibleAlias",
|
|
|
|
Access->getAccessInstruction())
|
|
|
|
<< "Possibly aliasing pointer, use restrict keyword.");
|
2017-02-10 07:12:22 +08:00
|
|
|
const ScopArrayInfo *Array = Access->getScopArrayInfo();
|
|
|
|
if (HasWriteAccess.count(Array)) {
|
|
|
|
ReadWriteArrays.insert(Array);
|
2017-01-16 23:49:07 +08:00
|
|
|
ReadWriteAccesses.push_back(Access);
|
|
|
|
} else {
|
2017-02-19 04:51:29 +08:00
|
|
|
ReadOnlyArrays.insert(Array);
|
2017-01-16 23:49:07 +08:00
|
|
|
ReadOnlyAccesses.push_back(Access);
|
2016-05-23 17:26:46 +08:00
|
|
|
}
|
2017-01-16 23:49:07 +08:00
|
|
|
}
|
2016-02-25 22:06:11 +08:00
|
|
|
|
2017-01-16 23:49:09 +08:00
|
|
|
// If there are no read-only pointers, and less than two read-write pointers,
|
|
|
|
// no alias check is needed.
|
2017-02-10 07:12:22 +08:00
|
|
|
if (ReadOnlyAccesses.empty() && ReadWriteArrays.size() <= 1)
|
2017-01-16 23:49:07 +08:00
|
|
|
return true;
|
2015-07-24 01:04:54 +08:00
|
|
|
|
2017-01-16 23:49:09 +08:00
|
|
|
// If there is no read-write pointer, no alias check is needed.
|
2017-02-10 07:12:22 +08:00
|
|
|
if (ReadWriteArrays.empty())
|
2017-01-16 23:49:07 +08:00
|
|
|
return true;
|
2014-09-18 19:17:17 +08:00
|
|
|
|
2017-01-16 23:49:09 +08:00
|
|
|
// For non-affine accesses, no alias check can be generated as we cannot
|
|
|
|
// compute a sufficiently tight lower and upper bound: bail out.
|
2017-01-16 23:49:07 +08:00
|
|
|
for (MemoryAccess *MA : AliasGroup) {
|
|
|
|
if (!MA->isAffine()) {
|
2017-07-18 07:58:33 +08:00
|
|
|
invalidate(ALIASING, MA->getAccessInstruction()->getDebugLoc(),
|
|
|
|
MA->getAccessInstruction()->getParent());
|
2017-01-16 23:49:04 +08:00
|
|
|
return false;
|
2017-01-16 23:49:07 +08:00
|
|
|
}
|
2017-01-16 23:49:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that for all memory accesses for which we generate alias checks,
|
|
|
|
// their base pointers are available.
|
|
|
|
for (MemoryAccess *MA : AliasGroup) {
|
2017-01-16 23:49:07 +08:00
|
|
|
if (MemoryAccess *BasePtrMA = lookupBasePtrAccess(MA))
|
|
|
|
addRequiredInvariantLoad(
|
|
|
|
cast<LoadInst>(BasePtrMA->getAccessInstruction()));
|
|
|
|
}
|
2015-07-24 01:04:54 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
MinMaxAliasGroups.emplace_back();
|
|
|
|
MinMaxVectorPairTy &pair = MinMaxAliasGroups.back();
|
|
|
|
MinMaxVectorTy &MinMaxAccessesReadWrite = pair.first;
|
|
|
|
MinMaxVectorTy &MinMaxAccessesReadOnly = pair.second;
|
2015-07-24 01:04:54 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
bool Valid;
|
2014-09-27 19:02:39 +08:00
|
|
|
|
2017-01-16 23:49:07 +08:00
|
|
|
Valid =
|
|
|
|
calculateMinMaxAccess(ReadWriteAccesses, *this, MinMaxAccessesReadWrite);
|
|
|
|
|
|
|
|
if (!Valid)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Bail out if the number of values we need to compare is too large.
|
|
|
|
// This is important as the number of comparisons grows quadratically with
|
|
|
|
// the number of values we need to compare.
|
2017-02-19 04:51:29 +08:00
|
|
|
if (MinMaxAccessesReadWrite.size() + ReadOnlyArrays.size() >
|
2017-01-16 23:49:07 +08:00
|
|
|
RunTimeChecksMaxArraysPerGroup)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Valid =
|
|
|
|
calculateMinMaxAccess(ReadOnlyAccesses, *this, MinMaxAccessesReadOnly);
|
|
|
|
|
|
|
|
if (!Valid)
|
|
|
|
return false;
|
2014-09-27 19:02:39 +08:00
|
|
|
|
2015-03-28 22:50:32 +08:00
|
|
|
return true;
|
2014-09-18 19:17:17 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
/// Get the smallest loop that contains @p S but is not in @p S.
|
2016-05-23 20:42:38 +08:00
|
|
|
static Loop *getLoopSurroundingScop(Scop &S, LoopInfo &LI) {
|
2015-11-22 00:56:13 +08:00
|
|
|
// Start with the smallest loop containing the entry and expand that
|
|
|
|
// loop until it contains all blocks in the region. If there is a loop
|
|
|
|
// containing all blocks in the region check if it is itself contained
|
|
|
|
// and if so take the parent loop as it will be the smallest containing
|
|
|
|
// the region but not contained by it.
|
2016-05-23 20:42:38 +08:00
|
|
|
Loop *L = LI.getLoopFor(S.getEntry());
|
2015-11-22 00:56:13 +08:00
|
|
|
while (L) {
|
|
|
|
bool AllContained = true;
|
2016-05-23 20:42:38 +08:00
|
|
|
for (auto *BB : S.blocks())
|
2015-11-22 00:56:13 +08:00
|
|
|
AllContained &= L->contains(BB);
|
|
|
|
if (AllContained)
|
|
|
|
break;
|
|
|
|
L = L->getParentLoop();
|
|
|
|
}
|
|
|
|
|
2016-05-23 20:42:38 +08:00
|
|
|
return L ? (S.contains(L) ? L->getParentLoop() : L) : nullptr;
|
2015-09-10 23:27:46 +08:00
|
|
|
}
|
|
|
|
|
2017-07-13 00:46:19 +08:00
|
|
|
int Scop::NextScopID = 0;
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
std::string Scop::CurrentFunc;
|
2017-07-13 00:46:19 +08:00
|
|
|
|
|
|
|
int Scop::getNextID(std::string ParentFunc) {
|
|
|
|
if (ParentFunc != CurrentFunc) {
|
|
|
|
CurrentFunc = ParentFunc;
|
|
|
|
NextScopID = 0;
|
|
|
|
}
|
|
|
|
return NextScopID++;
|
|
|
|
}
|
|
|
|
|
2016-05-19 20:34:57 +08:00
|
|
|
Scop::Scop(Region &R, ScalarEvolution &ScalarEvolution, LoopInfo &LI,
|
2017-09-24 17:25:30 +08:00
|
|
|
DominatorTree &DT, ScopDetection::DetectionContext &DC,
|
|
|
|
OptimizationRemarkEmitter &ORE)
|
2017-11-20 06:13:34 +08:00
|
|
|
: IslCtx(isl_ctx_alloc(), isl_ctx_free), SE(&ScalarEvolution), DT(&DT),
|
2018-05-15 22:53:25 +08:00
|
|
|
R(R), name(None), HasSingleExitEdge(R.getExitingBlock()), DC(DC),
|
|
|
|
ORE(ORE), Affinator(this, LI),
|
2017-07-13 00:46:19 +08:00
|
|
|
ID(getNextID((*R.getEntry()->getParent()).getName().str())) {
|
2016-04-29 19:43:20 +08:00
|
|
|
if (IslOnErrorAbort)
|
2017-11-20 06:13:34 +08:00
|
|
|
isl_options_set_on_error(getIslCtx().get(), ISL_ON_ERROR_ABORT);
|
Support accesses with differently sized types to the same array
This allows code such as:
void multiple_types(char *Short, char *Float, char *Double) {
for (long i = 0; i < 100; i++) {
Short[i] = *(short *)&Short[2 * i];
Float[i] = *(float *)&Float[4 * i];
Double[i] = *(double *)&Double[8 * i];
}
}
To model such code we use as canonical element type of the modeled array the
smallest element type of all original array accesses, if type allocation sizes
are multiples of each other. Otherwise, we use a newly created iN type, where N
is the gcd of the allocation size of the types used in the accesses to this
array. Accesses with types larger as the canonical element type are modeled as
multiple accesses with the smaller type.
For example the second load access is modeled as:
{ Stmt_bb2[i0] -> MemRef_Float[o0] : 4i0 <= o0 <= 3 + 4i0 }
To support code-generating these memory accesses, we introduce a new method
getAccessAddressFunction that assigns each statement instance a single memory
location, the address we load from/store to. Currently we obtain this address by
taking the lexmin of the access function. We may consider keeping track of the
memory location more explicitly in the future.
We currently do _not_ handle multi-dimensional arrays and also keep the
restriction of not supporting accesses where the offset expression is not a
multiple of the access element type size. This patch adds tests that ensure
we correctly invalidate a scop in case these accesses are found. Both types of
accesses can be handled using the very same model, but are left to be added in
the future.
We also move the initialization of the scop-context into the constructor to
ensure it is already available when invalidating the scop.
Finally, we add this as a new item to the 2.9 release notes
Reviewers: jdoerfert, Meinersbur
Differential Revision: http://reviews.llvm.org/D16878
llvm-svn: 259784
2016-02-04 21:18:42 +08:00
|
|
|
buildContext();
|
|
|
|
}
|
2015-02-24 20:00:50 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
Scop::~Scop() = default;
|
2017-08-23 05:25:51 +08:00
|
|
|
|
2016-12-02 16:10:56 +08:00
|
|
|
void Scop::foldSizeConstantsToRight() {
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::union_set Accessed = getAccesses().range();
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
for (auto Array : arrays()) {
|
|
|
|
if (Array->getNumberOfDimensions() <= 1)
|
|
|
|
continue;
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::space Space = Array->getSpace();
|
|
|
|
Space = Space.align_params(Accessed.get_space());
|
2016-12-02 16:10:56 +08:00
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
if (!Accessed.contains(Space))
|
2016-12-02 16:10:56 +08:00
|
|
|
continue;
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::set Elements = Accessed.extract_set(Space);
|
|
|
|
isl::map Transform = isl::map::universe(Array->getSpace().map_from_set());
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
std::vector<int> Int;
|
2018-07-05 23:23:28 +08:00
|
|
|
int Dims = Elements.dim(isl::dim::set);
|
2016-12-02 16:10:56 +08:00
|
|
|
for (int i = 0; i < Dims; i++) {
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::set DimOnly = isl::set(Elements).project_out(isl::dim::set, 0, i);
|
|
|
|
DimOnly = DimOnly.project_out(isl::dim::set, 1, Dims - i - 1);
|
|
|
|
DimOnly = DimOnly.lower_bound_si(isl::dim::set, 0, 0);
|
2016-12-02 16:10:56 +08:00
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::basic_set DimHull = DimOnly.affine_hull();
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
if (i == Dims - 1) {
|
|
|
|
Int.push_back(1);
|
2018-07-05 23:23:28 +08:00
|
|
|
Transform = Transform.equate(isl::dim::in, i, isl::dim::out, i);
|
2016-12-02 16:10:56 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
if (DimHull.dim(isl::dim::div) == 1) {
|
|
|
|
isl::aff Diff = DimHull.get_div(0);
|
|
|
|
isl::val Val = Diff.get_denominator_val();
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
int ValInt = 1;
|
2018-07-05 23:23:28 +08:00
|
|
|
if (Val.is_int()) {
|
2018-01-18 05:59:02 +08:00
|
|
|
auto ValAPInt = APIntFromVal(Val);
|
|
|
|
if (ValAPInt.isSignedIntN(32))
|
|
|
|
ValInt = ValAPInt.getSExtValue();
|
|
|
|
} else {
|
|
|
|
}
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
Int.push_back(ValInt);
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::constraint C = isl::constraint::alloc_equality(
|
|
|
|
isl::local_space(Transform.get_space()));
|
|
|
|
C = C.set_coefficient_si(isl::dim::out, i, ValInt);
|
|
|
|
C = C.set_coefficient_si(isl::dim::in, i, -1);
|
|
|
|
Transform = Transform.add_constraint(C);
|
2016-12-02 16:10:56 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::basic_set ZeroSet = isl::basic_set(DimHull);
|
|
|
|
ZeroSet = ZeroSet.fix_si(isl::dim::set, 0, 0);
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
int ValInt = 1;
|
2018-07-05 23:23:28 +08:00
|
|
|
if (ZeroSet.is_equal(DimHull)) {
|
2016-12-02 16:10:56 +08:00
|
|
|
ValInt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Int.push_back(ValInt);
|
2018-07-05 23:23:28 +08:00
|
|
|
Transform = Transform.equate(isl::dim::in, i, isl::dim::out, i);
|
2016-12-02 16:10:56 +08:00
|
|
|
}
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
isl::set MappedElements = isl::map(Transform).domain();
|
|
|
|
if (!Elements.is_subset(MappedElements))
|
2016-12-02 16:10:56 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
bool CanFold = true;
|
|
|
|
if (Int[0] <= 1)
|
|
|
|
CanFold = false;
|
|
|
|
|
|
|
|
unsigned NumDims = Array->getNumberOfDimensions();
|
|
|
|
for (unsigned i = 1; i < NumDims - 1; i++)
|
|
|
|
if (Int[0] != Int[i] && Int[i])
|
|
|
|
CanFold = false;
|
|
|
|
|
2018-07-05 23:23:28 +08:00
|
|
|
if (!CanFold)
|
2016-12-02 16:10:56 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
for (auto &Access : AccessFunctions)
|
|
|
|
if (Access->getScopArrayInfo() == Array)
|
2018-07-05 23:23:28 +08:00
|
|
|
Access->setAccessRelation(
|
|
|
|
Access->getAccessRelation().apply_range(Transform));
|
2016-12-02 16:10:56 +08:00
|
|
|
|
|
|
|
std::vector<const SCEV *> Sizes;
|
|
|
|
for (unsigned i = 0; i < NumDims; i++) {
|
|
|
|
auto Size = Array->getDimensionSize(i);
|
|
|
|
|
|
|
|
if (i == NumDims - 1)
|
|
|
|
Size = SE->getMulExpr(Size, SE->getConstant(Size->getType(), Int[0]));
|
|
|
|
Sizes.push_back(Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
Array->updateSizes(Sizes, false /* CheckConsistency */);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:07:45 +08:00
|
|
|
void Scop::markFortranArrays() {
|
|
|
|
for (ScopStmt &Stmt : Stmts) {
|
|
|
|
for (MemoryAccess *MemAcc : Stmt) {
|
|
|
|
Value *FAD = MemAcc->getFortranArrayDescriptor();
|
|
|
|
if (!FAD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// TODO: const_cast-ing to edit
|
|
|
|
ScopArrayInfo *SAI =
|
|
|
|
const_cast<ScopArrayInfo *>(MemAcc->getLatestScopArrayInfo());
|
|
|
|
assert(SAI && "memory access into a Fortran array does not "
|
|
|
|
"have an associated ScopArrayInfo");
|
|
|
|
SAI->applyAndSetFAD(FAD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-02 13:21:22 +08:00
|
|
|
void Scop::finalizeAccesses() {
|
|
|
|
updateAccessDimensionality();
|
2016-12-02 16:10:56 +08:00
|
|
|
foldSizeConstantsToRight();
|
2016-12-02 13:21:22 +08:00
|
|
|
foldAccessRelations();
|
|
|
|
assumeNoOutOfBounds();
|
2017-05-19 23:07:45 +08:00
|
|
|
markFortranArrays();
|
2016-12-02 13:21:22 +08:00
|
|
|
}
|
|
|
|
|
2015-09-26 16:55:54 +08:00
|
|
|
void Scop::updateAccessDimensionality() {
|
2016-02-19 00:50:12 +08:00
|
|
|
// Check all array accesses for each base pointer and find a (virtual) element
|
|
|
|
// size for the base pointer that divides all access functions.
|
2017-02-10 07:24:54 +08:00
|
|
|
for (ScopStmt &Stmt : *this)
|
|
|
|
for (MemoryAccess *Access : Stmt) {
|
2016-02-19 00:50:12 +08:00
|
|
|
if (!Access->isArrayKind())
|
|
|
|
continue;
|
2017-02-10 07:24:54 +08:00
|
|
|
ScopArrayInfo *Array =
|
2017-02-10 07:24:57 +08:00
|
|
|
const_cast<ScopArrayInfo *>(Access->getScopArrayInfo());
|
|
|
|
|
2017-02-10 07:24:54 +08:00
|
|
|
if (Array->getNumberOfDimensions() != 1)
|
2016-02-19 00:50:12 +08:00
|
|
|
continue;
|
2017-02-10 07:24:54 +08:00
|
|
|
unsigned DivisibleSize = Array->getElemSizeInBytes();
|
|
|
|
const SCEV *Subscript = Access->getSubscript(0);
|
2016-02-19 00:50:12 +08:00
|
|
|
while (!isDivisible(Subscript, DivisibleSize, *SE))
|
|
|
|
DivisibleSize /= 2;
|
|
|
|
auto *Ty = IntegerType::get(SE->getContext(), DivisibleSize * 8);
|
2017-02-10 07:24:54 +08:00
|
|
|
Array->updateElementType(Ty);
|
2016-02-19 00:50:12 +08:00
|
|
|
}
|
|
|
|
|
2015-09-26 16:55:54 +08:00
|
|
|
for (auto &Stmt : *this)
|
|
|
|
for (auto &Access : Stmt)
|
|
|
|
Access->updateDimensionality();
|
|
|
|
}
|
|
|
|
|
2016-12-02 13:21:22 +08:00
|
|
|
void Scop::foldAccessRelations() {
|
|
|
|
for (auto &Stmt : *this)
|
|
|
|
for (auto &Access : Stmt)
|
|
|
|
Access->foldAccessRelation();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::assumeNoOutOfBounds() {
|
|
|
|
for (auto &Stmt : *this)
|
|
|
|
for (auto &Access : Stmt)
|
|
|
|
Access->assumeNoOutOfBound();
|
|
|
|
}
|
|
|
|
|
2017-07-17 07:55:38 +08:00
|
|
|
void Scop::removeFromStmtMap(ScopStmt &Stmt) {
|
2017-08-31 11:15:56 +08:00
|
|
|
for (Instruction *Inst : Stmt.getInstructions())
|
|
|
|
InstStmtMap.erase(Inst);
|
|
|
|
|
|
|
|
if (Stmt.isRegionStmt()) {
|
2017-08-10 00:45:37 +08:00
|
|
|
for (BasicBlock *BB : Stmt.getRegion()->blocks()) {
|
2017-07-17 07:55:38 +08:00
|
|
|
StmtMap.erase(BB);
|
2017-08-31 11:15:56 +08:00
|
|
|
// Skip entry basic block, as its instructions are already deleted as
|
|
|
|
// part of the statement's instruction list.
|
|
|
|
if (BB == Stmt.getEntryBlock())
|
|
|
|
continue;
|
2017-08-10 00:45:37 +08:00
|
|
|
for (Instruction &Inst : *BB)
|
|
|
|
InstStmtMap.erase(&Inst);
|
|
|
|
}
|
2017-08-31 11:15:56 +08:00
|
|
|
} else {
|
2017-09-01 19:36:52 +08:00
|
|
|
auto StmtMapIt = StmtMap.find(Stmt.getBasicBlock());
|
|
|
|
if (StmtMapIt != StmtMap.end())
|
|
|
|
StmtMapIt->second.erase(std::remove(StmtMapIt->second.begin(),
|
|
|
|
StmtMapIt->second.end(), &Stmt),
|
|
|
|
StmtMapIt->second.end());
|
|
|
|
for (Instruction *Inst : Stmt.getInstructions())
|
|
|
|
InstStmtMap.erase(Inst);
|
2017-08-10 00:45:37 +08:00
|
|
|
}
|
2017-07-17 07:55:38 +08:00
|
|
|
}
|
|
|
|
|
2018-04-10 07:13:05 +08:00
|
|
|
void Scop::removeStmts(std::function<bool(ScopStmt &)> ShouldDelete,
|
|
|
|
bool AfterHoisting) {
|
2015-09-30 07:47:21 +08:00
|
|
|
for (auto StmtIt = Stmts.begin(), StmtEnd = Stmts.end(); StmtIt != StmtEnd;) {
|
2017-07-17 07:55:38 +08:00
|
|
|
if (!ShouldDelete(*StmtIt)) {
|
|
|
|
StmtIt++;
|
|
|
|
continue;
|
|
|
|
}
|
2015-10-02 21:53:07 +08:00
|
|
|
|
2018-04-10 07:13:05 +08:00
|
|
|
// Start with removing all of the statement's accesses including erasing it
|
|
|
|
// from all maps that are pointing to them.
|
2018-04-10 09:20:41 +08:00
|
|
|
// Make a temporary copy because removing MAs invalidates the iterator.
|
|
|
|
SmallVector<MemoryAccess *, 16> MAList(StmtIt->begin(), StmtIt->end());
|
|
|
|
for (MemoryAccess *MA : MAList)
|
2018-04-10 07:13:05 +08:00
|
|
|
StmtIt->removeSingleMemoryAccess(MA, AfterHoisting);
|
|
|
|
|
2017-07-17 07:55:38 +08:00
|
|
|
removeFromStmtMap(*StmtIt);
|
|
|
|
StmtIt = Stmts.erase(StmtIt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::removeStmtNotInDomainMap() {
|
|
|
|
auto ShouldDelete = [this](ScopStmt &Stmt) -> bool {
|
2018-08-02 06:28:32 +08:00
|
|
|
isl::set Domain = DomainMap.lookup(Stmt.getEntryBlock());
|
|
|
|
if (!Domain)
|
|
|
|
return true;
|
|
|
|
return Domain.is_empty();
|
2017-07-17 07:55:38 +08:00
|
|
|
};
|
2018-04-10 07:13:05 +08:00
|
|
|
removeStmts(ShouldDelete, false);
|
2017-07-17 07:55:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::simplifySCoP(bool AfterHoisting) {
|
|
|
|
auto ShouldDelete = [AfterHoisting](ScopStmt &Stmt) -> bool {
|
2018-04-21 02:55:44 +08:00
|
|
|
// Never delete statements that contain calls to debug functions.
|
|
|
|
if (hasDebugCall(&Stmt))
|
|
|
|
return false;
|
|
|
|
|
2016-05-10 20:19:47 +08:00
|
|
|
bool RemoveStmt = Stmt.isEmpty();
|
2015-11-04 00:54:49 +08:00
|
|
|
|
2017-07-17 06:44:17 +08:00
|
|
|
// Remove read only statements only after invariant load hoisting.
|
2016-05-10 20:19:47 +08:00
|
|
|
if (!RemoveStmt && AfterHoisting) {
|
2015-11-04 00:54:49 +08:00
|
|
|
bool OnlyRead = true;
|
|
|
|
for (MemoryAccess *MA : Stmt) {
|
|
|
|
if (MA->isRead())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
OnlyRead = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
RemoveStmt = OnlyRead;
|
|
|
|
}
|
2017-07-17 07:55:38 +08:00
|
|
|
return RemoveStmt;
|
|
|
|
};
|
2015-10-04 23:00:05 +08:00
|
|
|
|
2018-04-10 07:13:05 +08:00
|
|
|
removeStmts(ShouldDelete, AfterHoisting);
|
2015-09-30 07:47:21 +08:00
|
|
|
}
|
|
|
|
|
2016-04-27 20:49:11 +08:00
|
|
|
InvariantEquivClassTy *Scop::lookupInvariantEquivClass(Value *Val) {
|
2015-10-18 20:39:19 +08:00
|
|
|
LoadInst *LInst = dyn_cast<LoadInst>(Val);
|
|
|
|
if (!LInst)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (Value *Rep = InvEquivClassVMap.lookup(LInst))
|
|
|
|
LInst = cast<LoadInst>(Rep);
|
|
|
|
|
2016-02-08 01:30:13 +08:00
|
|
|
Type *Ty = LInst->getType();
|
2015-10-18 20:39:19 +08:00
|
|
|
const SCEV *PointerSCEV = SE->getSCEV(LInst->getPointerOperand());
|
2016-03-24 21:22:16 +08:00
|
|
|
for (auto &IAClass : InvariantEquivClasses) {
|
2016-07-11 20:27:04 +08:00
|
|
|
if (PointerSCEV != IAClass.IdentifyingPointer || Ty != IAClass.AccessType)
|
2016-03-24 21:22:16 +08:00
|
|
|
continue;
|
|
|
|
|
2016-07-11 20:15:10 +08:00
|
|
|
auto &MAs = IAClass.InvariantAccesses;
|
2016-03-24 21:22:16 +08:00
|
|
|
for (auto *MA : MAs)
|
|
|
|
if (MA->getAccessInstruction() == Val)
|
|
|
|
return &IAClass;
|
|
|
|
}
|
2015-10-18 20:39:19 +08:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-28 21:02:43 +08:00
|
|
|
ScopArrayInfo *Scop::getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType,
|
|
|
|
ArrayRef<const SCEV *> Sizes,
|
|
|
|
MemoryKind Kind,
|
|
|
|
const char *BaseName) {
|
2016-07-30 17:25:51 +08:00
|
|
|
assert((BasePtr || BaseName) &&
|
|
|
|
"BasePtr and BaseName can not be nullptr at the same time.");
|
|
|
|
assert(!(BasePtr && BaseName) && "BaseName is redundant.");
|
|
|
|
auto &SAI = BasePtr ? ScopArrayInfoMap[std::make_pair(BasePtr, Kind)]
|
|
|
|
: ScopArrayNameMap[BaseName];
|
2015-09-26 16:55:54 +08:00
|
|
|
if (!SAI) {
|
2016-05-23 20:38:05 +08:00
|
|
|
auto &DL = getFunction().getParent()->getDataLayout();
|
2016-02-02 21:22:54 +08:00
|
|
|
SAI.reset(new ScopArrayInfo(BasePtr, ElementType, getIslCtx(), Sizes, Kind,
|
2016-07-30 17:25:51 +08:00
|
|
|
DL, this, BaseName));
|
|
|
|
ScopArrayInfoSet.insert(SAI.get());
|
2015-09-26 16:55:54 +08:00
|
|
|
} else {
|
2016-02-15 06:31:39 +08:00
|
|
|
SAI->updateElementType(ElementType);
|
2015-11-02 19:29:32 +08:00
|
|
|
// In case of mismatching array sizes, we bail out by setting the run-time
|
|
|
|
// context to false.
|
2016-02-15 06:31:39 +08:00
|
|
|
if (!SAI->updateSizes(Sizes))
|
2015-12-12 17:52:26 +08:00
|
|
|
invalidate(DELINEARIZATION, DebugLoc());
|
2015-09-26 16:55:54 +08:00
|
|
|
}
|
2015-05-23 13:58:27 +08:00
|
|
|
return SAI.get();
|
2014-10-05 19:32:18 +08:00
|
|
|
}
|
|
|
|
|
2017-06-28 21:02:43 +08:00
|
|
|
ScopArrayInfo *Scop::createScopArrayInfo(Type *ElementType,
|
|
|
|
const std::string &BaseName,
|
|
|
|
const std::vector<unsigned> &Sizes) {
|
2016-07-30 17:25:51 +08:00
|
|
|
auto *DimSizeType = Type::getInt64Ty(getSE()->getContext());
|
|
|
|
std::vector<const SCEV *> SCEVSizes;
|
|
|
|
|
|
|
|
for (auto size : Sizes)
|
2016-09-13 01:08:31 +08:00
|
|
|
if (size)
|
|
|
|
SCEVSizes.push_back(getSE()->getConstant(DimSizeType, size, false));
|
|
|
|
else
|
|
|
|
SCEVSizes.push_back(nullptr);
|
2016-07-30 17:25:51 +08:00
|
|
|
|
2017-01-15 04:25:44 +08:00
|
|
|
auto *SAI = getOrCreateScopArrayInfo(nullptr, ElementType, SCEVSizes,
|
|
|
|
MemoryKind::Array, BaseName.c_str());
|
2016-07-30 17:25:51 +08:00
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
2017-05-10 18:59:58 +08:00
|
|
|
const ScopArrayInfo *Scop::getScopArrayInfoOrNull(Value *BasePtr,
|
|
|
|
MemoryKind Kind) {
|
2015-11-11 01:31:31 +08:00
|
|
|
auto *SAI = ScopArrayInfoMap[std::make_pair(BasePtr, Kind)].get();
|
2017-05-10 18:59:58 +08:00
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ScopArrayInfo *Scop::getScopArrayInfo(Value *BasePtr, MemoryKind Kind) {
|
|
|
|
auto *SAI = getScopArrayInfoOrNull(BasePtr, Kind);
|
2014-10-05 19:32:18 +08:00
|
|
|
assert(SAI && "No ScopArrayInfo available for this base pointer");
|
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
|
2017-08-07 03:52:38 +08:00
|
|
|
std::string Scop::getContextStr() const { return getContext().to_str(); }
|
2016-02-22 00:37:58 +08:00
|
|
|
|
2014-07-03 01:47:48 +08:00
|
|
|
std::string Scop::getAssumedContextStr() const {
|
2016-03-01 21:06:28 +08:00
|
|
|
assert(AssumedContext && "Assumed context not yet built");
|
2017-11-20 06:13:34 +08:00
|
|
|
return AssumedContext.to_str();
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
2016-02-22 00:37:58 +08:00
|
|
|
|
2016-03-01 21:06:28 +08:00
|
|
|
std::string Scop::getInvalidContextStr() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
return InvalidContext.to_str();
|
2015-09-16 06:52:53 +08:00
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
std::string Scop::getNameStr() const {
|
2017-06-02 16:01:22 +08:00
|
|
|
std::string ExitName, EntryName;
|
|
|
|
std::tie(EntryName, ExitName) = getEntryExitStr();
|
|
|
|
return EntryName + "---" + ExitName;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<std::string, std::string> Scop::getEntryExitStr() const {
|
2011-04-29 14:27:02 +08:00
|
|
|
std::string ExitName, EntryName;
|
|
|
|
raw_string_ostream ExitStr(ExitName);
|
|
|
|
raw_string_ostream EntryStr(EntryName);
|
|
|
|
|
2014-01-09 18:42:15 +08:00
|
|
|
R.getEntry()->printAsOperand(EntryStr, false);
|
2011-04-29 14:27:02 +08:00
|
|
|
EntryStr.str();
|
|
|
|
|
|
|
|
if (R.getExit()) {
|
2014-01-09 18:42:15 +08:00
|
|
|
R.getExit()->printAsOperand(ExitStr, false);
|
2011-04-29 14:27:02 +08:00
|
|
|
ExitStr.str();
|
|
|
|
} else
|
|
|
|
ExitName = "FunctionExit";
|
|
|
|
|
2017-06-02 16:01:22 +08:00
|
|
|
return std::make_pair(EntryName, ExitName);
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::set Scop::getContext() const { return Context; }
|
2017-08-07 04:11:59 +08:00
|
|
|
isl::space Scop::getParamSpace() const { return getContext().get_space(); }
|
2011-10-06 08:03:42 +08:00
|
|
|
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
isl::space Scop::getFullParamSpace() const {
|
|
|
|
std::vector<isl::id> FortranIDs;
|
|
|
|
FortranIDs = getFortranArrayIds(arrays());
|
|
|
|
|
|
|
|
isl::space Space = isl::space::params_alloc(
|
|
|
|
getIslCtx(), ParameterIds.size() + FortranIDs.size());
|
|
|
|
|
|
|
|
unsigned PDim = 0;
|
|
|
|
for (const SCEV *Parameter : Parameters) {
|
2017-08-07 03:31:27 +08:00
|
|
|
isl::id Id = getIdForParam(Parameter);
|
Make sure that all parameter dimensions are set in schedule
Summary:
In case the option -polly-ignore-parameter-bounds is set, not all parameters
will be added to context and domains. This is useful to keep the size of the
sets and maps we work with small. Unfortunately, for AST generation it is
necessary to ensure all parameters are part of the schedule tree. Hence,
we modify the GPGPU code generation to make sure this is the case.
To obtain the necessary information we expose a new function
Scop::getFullParamSpace(). We also make a couple of functions const to be
able to make SCoP::getFullParamSpace() const.
Reviewers: Meinersbur, bollu, gareevroman, efriedma, huihuiz, sebpop, simbuerg
Subscribers: nemanjai, kbarton, pollydev, llvm-commits
Tags: #polly
Differential Revision: https://reviews.llvm.org/D36243
llvm-svn: 309939
2017-08-03 21:51:15 +08:00
|
|
|
Space = Space.set_dim_id(isl::dim::param, PDim++, Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (isl::id Id : FortranIDs)
|
|
|
|
Space = Space.set_dim_id(isl::dim::param, PDim++, Id);
|
|
|
|
|
|
|
|
return Space;
|
|
|
|
}
|
|
|
|
|
2017-08-07 05:42:09 +08:00
|
|
|
isl::set Scop::getAssumedContext() const {
|
2016-03-01 21:06:28 +08:00
|
|
|
assert(AssumedContext && "Assumed context not yet built");
|
2017-11-20 06:13:34 +08:00
|
|
|
return AssumedContext;
|
2013-10-30 05:05:49 +08:00
|
|
|
}
|
|
|
|
|
2017-03-17 21:09:52 +08:00
|
|
|
bool Scop::isProfitable(bool ScalarsAreUnprofitable) const {
|
2016-05-11 00:38:09 +08:00
|
|
|
if (PollyProcessUnprofitable)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (isEmpty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
unsigned OptimizableStmtsOrLoops = 0;
|
|
|
|
for (auto &Stmt : *this) {
|
|
|
|
if (Stmt.getNumIterators() == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool ContainsArrayAccs = false;
|
|
|
|
bool ContainsScalarAccs = false;
|
|
|
|
for (auto *MA : Stmt) {
|
|
|
|
if (MA->isRead())
|
|
|
|
continue;
|
2017-03-17 21:09:52 +08:00
|
|
|
ContainsArrayAccs |= MA->isLatestArrayKind();
|
|
|
|
ContainsScalarAccs |= MA->isLatestScalarKind();
|
2016-05-11 00:38:09 +08:00
|
|
|
}
|
|
|
|
|
2017-03-17 21:09:52 +08:00
|
|
|
if (!ScalarsAreUnprofitable || (ContainsArrayAccs && !ContainsScalarAccs))
|
2016-05-11 00:38:09 +08:00
|
|
|
OptimizableStmtsOrLoops += Stmt.getNumIterators();
|
|
|
|
}
|
|
|
|
|
|
|
|
return OptimizableStmtsOrLoops > 1;
|
|
|
|
}
|
|
|
|
|
2015-08-21 02:06:30 +08:00
|
|
|
bool Scop::hasFeasibleRuntimeContext() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
auto PositiveContext = getAssumedContext();
|
|
|
|
auto NegativeContext = getInvalidContext();
|
|
|
|
PositiveContext = addNonEmptyDomainConstraints(PositiveContext);
|
|
|
|
// addNonEmptyDomainConstraints returns null if ScopStmts have a null domain
|
|
|
|
if (!PositiveContext)
|
2016-03-01 21:06:28 +08:00
|
|
|
return false;
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
bool IsFeasible = !(PositiveContext.is_empty() ||
|
|
|
|
PositiveContext.is_subset(NegativeContext));
|
|
|
|
if (!IsFeasible)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto DomainContext = getDomains().params();
|
|
|
|
IsFeasible = !DomainContext.is_subset(NegativeContext);
|
|
|
|
IsFeasible &= !Context.is_subset(NegativeContext);
|
2016-03-01 21:06:28 +08:00
|
|
|
|
2015-08-20 13:58:56 +08:00
|
|
|
return IsFeasible;
|
|
|
|
}
|
|
|
|
|
2015-11-12 10:33:38 +08:00
|
|
|
static std::string toString(AssumptionKind Kind) {
|
|
|
|
switch (Kind) {
|
|
|
|
case ALIASING:
|
|
|
|
return "No-aliasing";
|
|
|
|
case INBOUNDS:
|
|
|
|
return "Inbounds";
|
|
|
|
case WRAPPING:
|
|
|
|
return "No-overflows";
|
Model zext-extend instructions
A zero-extended value can be interpreted as a piecewise defined signed
value. If the value was non-negative it stays the same, otherwise it
is the sum of the original value and 2^n where n is the bit-width of
the original (or operand) type. Examples:
zext i8 127 to i32 -> { [127] }
zext i8 -1 to i32 -> { [256 + (-1)] } = { [255] }
zext i8 %v to i32 -> [v] -> { [v] | v >= 0; [256 + v] | v < 0 }
However, LLVM/Scalar Evolution uses zero-extend (potentially lead by a
truncate) to represent some forms of modulo computation. The left-hand side
of the condition in the code below would result in the SCEV
"zext i1 <false, +, true>for.body" which is just another description
of the C expression "i & 1 != 0" or, equivalently, "i % 2 != 0".
for (i = 0; i < N; i++)
if (i & 1 != 0 /* == i % 2 */)
/* do something */
If we do not make the modulo explicit but only use the mechanism described
above we will get the very restrictive assumption "N < 3", because for all
values of N >= 3 the SCEVAddRecExpr operand of the zero-extend would wrap.
Alternatively, we can make the modulo in the operand explicit in the
resulting piecewise function and thereby avoid the assumption on N. For the
example this would result in the following piecewise affine function:
{ [i0] -> [(1)] : 2*floor((-1 + i0)/2) = -1 + i0;
[i0] -> [(0)] : 2*floor((i0)/2) = i0 }
To this end we can first determine if the (immediate) operand of the
zero-extend can wrap and, in case it might, we will use explicit modulo
semantic to compute the result instead of emitting non-wrapping assumptions.
Note that operands with large bit-widths are less likely to be negative
because it would result in a very large access offset or loop bound after the
zero-extend. To this end one can optimistically assume the operand to be
positive and avoid the piecewise definition if the bit-width is bigger than
some threshold (here MaxZextSmallBitWidth).
We choose to go with a hybrid solution of all modeling techniques described
above. For small bit-widths (up to MaxZextSmallBitWidth) we will model the
wrapping explicitly and use a piecewise defined function. However, if the
bit-width is bigger than MaxZextSmallBitWidth we will employ overflow
assumptions and assume the "former negative" piece will not exist.
llvm-svn: 267408
2016-04-25 22:01:36 +08:00
|
|
|
case UNSIGNED:
|
|
|
|
return "Signed-unsigned";
|
2016-03-27 00:17:00 +08:00
|
|
|
case COMPLEXITY:
|
|
|
|
return "Low complexity";
|
2016-05-11 00:38:09 +08:00
|
|
|
case PROFITABLE:
|
|
|
|
return "Profitable";
|
2015-11-12 10:33:38 +08:00
|
|
|
case ERRORBLOCK:
|
|
|
|
return "No-error";
|
|
|
|
case INFINITELOOP:
|
|
|
|
return "Finite loop";
|
|
|
|
case INVARIANTLOAD:
|
|
|
|
return "Invariant load";
|
|
|
|
case DELINEARIZATION:
|
|
|
|
return "Delinearization";
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown AssumptionKind!");
|
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
bool Scop::isEffectiveAssumption(isl::set Set, AssumptionSign Sign) {
|
2016-06-06 20:16:10 +08:00
|
|
|
if (Sign == AS_ASSUMPTION) {
|
2017-11-20 06:13:34 +08:00
|
|
|
if (Context.is_subset(Set))
|
2016-06-06 20:16:10 +08:00
|
|
|
return false;
|
2015-11-12 10:33:38 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
if (AssumedContext.is_subset(Set))
|
2016-06-06 20:16:10 +08:00
|
|
|
return false;
|
|
|
|
} else {
|
2017-11-20 06:13:34 +08:00
|
|
|
if (Set.is_disjoint(Context))
|
2016-06-06 20:16:10 +08:00
|
|
|
return false;
|
2016-03-01 21:06:28 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
if (Set.is_subset(InvalidContext))
|
2016-06-06 20:16:10 +08:00
|
|
|
return false;
|
2016-03-01 21:06:28 +08:00
|
|
|
}
|
2016-06-06 20:16:10 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
bool Scop::trackAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
|
|
|
|
AssumptionSign Sign, BasicBlock *BB) {
|
2016-06-06 20:16:10 +08:00
|
|
|
if (PollyRemarksMinimal && !isEffectiveAssumption(Set, Sign))
|
|
|
|
return false;
|
2015-11-12 10:33:38 +08:00
|
|
|
|
2016-11-18 06:08:40 +08:00
|
|
|
// Do never emit trivial assumptions as they only clutter the output.
|
|
|
|
if (!PollyRemarksMinimal) {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::set Univ;
|
2016-11-18 06:08:40 +08:00
|
|
|
if (Sign == AS_ASSUMPTION)
|
2017-11-20 06:13:34 +08:00
|
|
|
Univ = isl::set::universe(Set.get_space());
|
2016-11-18 06:08:40 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
bool IsTrivial = (Sign == AS_RESTRICTION && Set.is_empty()) ||
|
|
|
|
(Sign == AS_ASSUMPTION && Univ.is_equal(Set));
|
2016-11-18 06:08:40 +08:00
|
|
|
|
|
|
|
if (IsTrivial)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-18 05:41:08 +08:00
|
|
|
switch (Kind) {
|
|
|
|
case ALIASING:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsAliasing++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case INBOUNDS:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsInbounds++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case WRAPPING:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsWrapping++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case UNSIGNED:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsUnsigned++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case COMPLEXITY:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsComplexity++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case PROFITABLE:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsUnprofitable++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case ERRORBLOCK:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsErrorBlock++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case INFINITELOOP:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsInfiniteLoop++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case INVARIANTLOAD:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsInvariantLoad++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
case DELINEARIZATION:
|
2016-11-18 22:37:08 +08:00
|
|
|
AssumptionsDelinearization++;
|
2016-11-18 05:41:08 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-03-01 21:06:28 +08:00
|
|
|
auto Suffix = Sign == AS_ASSUMPTION ? " assumption:\t" : " restriction:\t";
|
2017-11-20 06:13:34 +08:00
|
|
|
std::string Msg = toString(Kind) + Suffix + Set.to_str();
|
2017-07-18 07:58:33 +08:00
|
|
|
if (BB)
|
|
|
|
ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "AssumpRestrict", Loc, BB)
|
|
|
|
<< Msg);
|
|
|
|
else
|
|
|
|
ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "AssumpRestrict", Loc,
|
|
|
|
R.getEntry())
|
|
|
|
<< Msg);
|
2016-03-01 21:06:28 +08:00
|
|
|
return true;
|
2015-11-12 10:33:38 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
void Scop::addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
|
|
|
|
AssumptionSign Sign, BasicBlock *BB) {
|
2016-04-12 21:27:35 +08:00
|
|
|
// Simplify the assumptions/restrictions first.
|
2017-11-20 06:13:34 +08:00
|
|
|
Set = Set.gist_params(getContext());
|
2016-04-12 21:27:35 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
if (!trackAssumption(Kind, Set, Loc, Sign, BB))
|
2016-03-01 21:06:28 +08:00
|
|
|
return;
|
2015-11-12 00:22:36 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
if (Sign == AS_ASSUMPTION)
|
|
|
|
AssumedContext = AssumedContext.intersect(Set).coalesce();
|
|
|
|
else
|
|
|
|
InvalidContext = InvalidContext.unite(Set).coalesce();
|
2014-07-03 01:47:48 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
void Scop::recordAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
|
|
|
|
AssumptionSign Sign, BasicBlock *BB) {
|
|
|
|
assert((Set.is_params() || BB) &&
|
2016-11-10 19:44:10 +08:00
|
|
|
"Assumptions without a basic block must be parameter sets");
|
2016-04-12 21:28:39 +08:00
|
|
|
RecordedAssumptions.push_back({Kind, Sign, Set, Loc, BB});
|
2016-04-12 21:27:35 +08:00
|
|
|
}
|
|
|
|
|
2017-07-18 07:58:33 +08:00
|
|
|
void Scop::invalidate(AssumptionKind Kind, DebugLoc Loc, BasicBlock *BB) {
|
2018-05-15 21:37:17 +08:00
|
|
|
LLVM_DEBUG(dbgs() << "Invalidate SCoP because of reason " << Kind << "\n");
|
2017-11-20 06:13:34 +08:00
|
|
|
addAssumption(Kind, isl::set::empty(getParamSpace()), Loc, AS_ASSUMPTION, BB);
|
2015-12-12 17:52:26 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::set Scop::getInvalidContext() const { return InvalidContext; }
|
2015-09-16 06:52:53 +08:00
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
void Scop::printContext(raw_ostream &OS) const {
|
|
|
|
OS << "Context:\n";
|
2016-03-01 21:06:28 +08:00
|
|
|
OS.indent(4) << Context << "\n";
|
2011-11-08 23:41:28 +08:00
|
|
|
|
2014-07-03 01:47:48 +08:00
|
|
|
OS.indent(4) << "Assumed Context:\n";
|
2016-03-01 21:06:28 +08:00
|
|
|
OS.indent(4) << AssumedContext << "\n";
|
2015-09-16 06:52:53 +08:00
|
|
|
|
2016-03-01 21:06:28 +08:00
|
|
|
OS.indent(4) << "Invalid Context:\n";
|
|
|
|
OS.indent(4) << InvalidContext << "\n";
|
2015-09-16 06:52:53 +08:00
|
|
|
|
2016-04-26 00:15:13 +08:00
|
|
|
unsigned Dim = 0;
|
|
|
|
for (const SCEV *Parameter : Parameters)
|
|
|
|
OS.indent(4) << "p" << Dim++ << ": " << *Parameter << "\n";
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2014-09-18 19:17:17 +08:00
|
|
|
void Scop::printAliasAssumptions(raw_ostream &OS) const {
|
2015-07-25 20:31:03 +08:00
|
|
|
int noOfGroups = 0;
|
|
|
|
for (const MinMaxVectorPairTy &Pair : MinMaxAliasGroups) {
|
2015-07-26 21:14:38 +08:00
|
|
|
if (Pair.second.size() == 0)
|
2015-07-24 01:04:54 +08:00
|
|
|
noOfGroups += 1;
|
|
|
|
else
|
2015-07-26 21:14:38 +08:00
|
|
|
noOfGroups += Pair.second.size();
|
2015-07-24 01:04:54 +08:00
|
|
|
}
|
|
|
|
|
2015-07-25 20:31:03 +08:00
|
|
|
OS.indent(4) << "Alias Groups (" << noOfGroups << "):\n";
|
2014-09-18 19:17:17 +08:00
|
|
|
if (MinMaxAliasGroups.empty()) {
|
|
|
|
OS.indent(8) << "n/a\n";
|
|
|
|
return;
|
|
|
|
}
|
2015-07-24 01:04:54 +08:00
|
|
|
|
2015-07-25 20:31:03 +08:00
|
|
|
for (const MinMaxVectorPairTy &Pair : MinMaxAliasGroups) {
|
2015-07-24 01:04:54 +08:00
|
|
|
|
|
|
|
// If the group has no read only accesses print the write accesses.
|
2015-07-26 21:14:38 +08:00
|
|
|
if (Pair.second.empty()) {
|
2015-07-24 01:04:54 +08:00
|
|
|
OS.indent(8) << "[[";
|
2015-07-26 21:14:38 +08:00
|
|
|
for (const MinMaxAccessTy &MMANonReadOnly : Pair.first) {
|
2015-07-25 20:31:03 +08:00
|
|
|
OS << " <" << MMANonReadOnly.first << ", " << MMANonReadOnly.second
|
|
|
|
<< ">";
|
2015-07-24 01:04:54 +08:00
|
|
|
}
|
|
|
|
OS << " ]]\n";
|
|
|
|
}
|
|
|
|
|
2015-07-26 21:14:38 +08:00
|
|
|
for (const MinMaxAccessTy &MMAReadOnly : Pair.second) {
|
2015-07-24 01:04:54 +08:00
|
|
|
OS.indent(8) << "[[";
|
2015-07-25 20:31:03 +08:00
|
|
|
OS << " <" << MMAReadOnly.first << ", " << MMAReadOnly.second << ">";
|
2015-07-26 21:14:38 +08:00
|
|
|
for (const MinMaxAccessTy &MMANonReadOnly : Pair.first) {
|
2015-07-25 20:31:03 +08:00
|
|
|
OS << " <" << MMANonReadOnly.first << ", " << MMANonReadOnly.second
|
|
|
|
<< ">";
|
2015-07-24 01:04:54 +08:00
|
|
|
}
|
|
|
|
OS << " ]]\n";
|
|
|
|
}
|
2014-09-18 19:17:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 23:35:53 +08:00
|
|
|
void Scop::printStatements(raw_ostream &OS, bool PrintInstructions) const {
|
2011-04-29 14:27:02 +08:00
|
|
|
OS << "Statements {\n";
|
|
|
|
|
2017-07-21 23:35:53 +08:00
|
|
|
for (const ScopStmt &Stmt : *this) {
|
|
|
|
OS.indent(4);
|
|
|
|
Stmt.print(OS, PrintInstructions);
|
|
|
|
}
|
2011-04-29 14:27:02 +08:00
|
|
|
|
|
|
|
OS.indent(4) << "}\n";
|
|
|
|
}
|
|
|
|
|
2015-05-20 16:05:31 +08:00
|
|
|
void Scop::printArrayInfo(raw_ostream &OS) const {
|
|
|
|
OS << "Arrays {\n";
|
|
|
|
|
2015-05-23 13:58:27 +08:00
|
|
|
for (auto &Array : arrays())
|
2016-07-30 17:25:51 +08:00
|
|
|
Array->print(OS);
|
2015-05-20 16:05:31 +08:00
|
|
|
|
|
|
|
OS.indent(4) << "}\n";
|
2015-08-12 23:27:16 +08:00
|
|
|
|
|
|
|
OS.indent(4) << "Arrays (Bounds as pw_affs) {\n";
|
|
|
|
|
|
|
|
for (auto &Array : arrays())
|
2016-07-30 17:25:51 +08:00
|
|
|
Array->print(OS, /* SizeAsPwAff */ true);
|
2015-08-12 23:27:16 +08:00
|
|
|
|
|
|
|
OS.indent(4) << "}\n";
|
2015-05-20 16:05:31 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 23:35:53 +08:00
|
|
|
void Scop::print(raw_ostream &OS, bool PrintInstructions) const {
|
2016-05-23 20:38:05 +08:00
|
|
|
OS.indent(4) << "Function: " << getFunction().getName() << "\n";
|
2014-03-19 02:05:38 +08:00
|
|
|
OS.indent(4) << "Region: " << getNameStr() << "\n";
|
2015-01-14 02:31:55 +08:00
|
|
|
OS.indent(4) << "Max Loop Depth: " << getMaxLoopDepth() << "\n";
|
2015-09-30 07:47:21 +08:00
|
|
|
OS.indent(4) << "Invariant Accesses: {\n";
|
2015-10-10 01:12:26 +08:00
|
|
|
for (const auto &IAClass : InvariantEquivClasses) {
|
2016-07-11 20:15:10 +08:00
|
|
|
const auto &MAs = IAClass.InvariantAccesses;
|
2015-10-18 20:39:19 +08:00
|
|
|
if (MAs.empty()) {
|
2016-07-11 20:15:10 +08:00
|
|
|
OS.indent(12) << "Class Pointer: " << *IAClass.IdentifyingPointer << "\n";
|
2015-10-10 01:12:26 +08:00
|
|
|
} else {
|
2015-10-18 20:39:19 +08:00
|
|
|
MAs.front()->print(OS);
|
2016-07-11 20:15:10 +08:00
|
|
|
OS.indent(12) << "Execution Context: " << IAClass.ExecutionContext
|
|
|
|
<< "\n";
|
2015-10-10 01:12:26 +08:00
|
|
|
}
|
2015-09-30 07:47:21 +08:00
|
|
|
}
|
|
|
|
OS.indent(4) << "}\n";
|
2011-04-29 14:27:02 +08:00
|
|
|
printContext(OS.indent(4));
|
2015-05-20 16:05:31 +08:00
|
|
|
printArrayInfo(OS.indent(4));
|
2014-09-18 19:17:17 +08:00
|
|
|
printAliasAssumptions(OS);
|
2017-07-21 23:35:53 +08:00
|
|
|
printStatements(OS.indent(4), PrintInstructions);
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 23:54:07 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
2017-07-21 23:54:13 +08:00
|
|
|
LLVM_DUMP_METHOD void Scop::dump() const { print(dbgs(), true); }
|
2017-07-21 23:54:07 +08:00
|
|
|
#endif
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::ctx Scop::getIslCtx() const { return IslCtx.get(); }
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2016-04-29 18:44:41 +08:00
|
|
|
__isl_give PWACtx Scop::getPwAff(const SCEV *E, BasicBlock *BB,
|
|
|
|
bool NonNegative) {
|
2016-03-27 00:17:00 +08:00
|
|
|
// First try to use the SCEVAffinator to generate a piecewise defined
|
|
|
|
// affine function from @p E in the context of @p BB. If that tasks becomes to
|
|
|
|
// complex the affinator might return a nullptr. In such a case we invalidate
|
|
|
|
// the SCoP and return a dummy value. This way we do not need to add error
|
2017-01-07 01:30:34 +08:00
|
|
|
// handling code to all users of this function.
|
2016-04-23 22:31:17 +08:00
|
|
|
auto PWAC = Affinator.getPwAff(E, BB);
|
2016-04-29 18:44:41 +08:00
|
|
|
if (PWAC.first) {
|
2016-05-10 19:45:46 +08:00
|
|
|
// TODO: We could use a heuristic and either use:
|
|
|
|
// SCEVAffinator::takeNonNegativeAssumption
|
|
|
|
// or
|
|
|
|
// SCEVAffinator::interpretAsUnsigned
|
|
|
|
// to deal with unsigned or "NonNegative" SCEVs.
|
2016-04-29 18:44:41 +08:00
|
|
|
if (NonNegative)
|
|
|
|
Affinator.takeNonNegativeAssumption(PWAC);
|
2016-04-23 22:31:17 +08:00
|
|
|
return PWAC;
|
2016-04-29 18:44:41 +08:00
|
|
|
}
|
2016-03-27 00:17:00 +08:00
|
|
|
|
|
|
|
auto DL = BB ? BB->getTerminator()->getDebugLoc() : DebugLoc();
|
2017-07-18 07:58:33 +08:00
|
|
|
invalidate(COMPLEXITY, DL, BB);
|
2016-03-27 00:17:00 +08:00
|
|
|
return Affinator.getPwAff(SE->getZero(E->getType()), BB);
|
2015-08-12 18:19:50 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 05:42:25 +08:00
|
|
|
isl::union_set Scop::getDomains() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl_space *EmptySpace = isl_space_params_alloc(getIslCtx().get(), 0);
|
2017-03-17 17:02:50 +08:00
|
|
|
isl_union_set *Domain = isl_union_set_empty(EmptySpace);
|
2012-02-14 22:02:40 +08:00
|
|
|
|
2015-07-14 17:33:13 +08:00
|
|
|
for (const ScopStmt &Stmt : *this)
|
2017-08-07 00:39:52 +08:00
|
|
|
Domain = isl_union_set_add_set(Domain, Stmt.getDomain().release());
|
2012-02-14 22:02:40 +08:00
|
|
|
|
2017-08-07 05:42:25 +08:00
|
|
|
return isl::manage(Domain);
|
2012-02-14 22:02:40 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::pw_aff Scop::getPwAffOnly(const SCEV *E, BasicBlock *BB) {
|
2016-04-23 22:31:17 +08:00
|
|
|
PWACtx PWAC = getPwAff(E, BB);
|
2017-12-07 05:02:22 +08:00
|
|
|
return PWAC.first;
|
2016-04-23 22:31:17 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map
|
2015-11-12 22:07:09 +08:00
|
|
|
Scop::getAccessesOfType(std::function<bool(MemoryAccess &)> Predicate) {
|
2017-08-07 04:11:59 +08:00
|
|
|
isl::union_map Accesses = isl::union_map::empty(getParamSpace());
|
2014-07-11 15:12:10 +08:00
|
|
|
|
2015-05-27 13:16:57 +08:00
|
|
|
for (ScopStmt &Stmt : *this) {
|
|
|
|
for (MemoryAccess *MA : Stmt) {
|
2015-11-12 22:07:09 +08:00
|
|
|
if (!Predicate(*MA))
|
2014-07-11 15:12:10 +08:00
|
|
|
continue;
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::set Domain = Stmt.getDomain();
|
|
|
|
isl::map AccessDomain = MA->getAccessRelation();
|
|
|
|
AccessDomain = AccessDomain.intersect_domain(Domain);
|
|
|
|
Accesses = Accesses.add_map(AccessDomain);
|
2014-07-11 15:12:10 +08:00
|
|
|
}
|
|
|
|
}
|
2017-07-25 00:22:27 +08:00
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
return Accesses.coalesce();
|
2014-07-11 15:12:10 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map Scop::getMustWrites() {
|
2015-11-12 22:07:09 +08:00
|
|
|
return getAccessesOfType([](MemoryAccess &MA) { return MA.isMustWrite(); });
|
|
|
|
}
|
2014-07-11 15:12:10 +08:00
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map Scop::getMayWrites() {
|
2015-11-12 22:07:09 +08:00
|
|
|
return getAccessesOfType([](MemoryAccess &MA) { return MA.isMayWrite(); });
|
2014-07-11 15:12:10 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map Scop::getWrites() {
|
2015-11-12 22:07:09 +08:00
|
|
|
return getAccessesOfType([](MemoryAccess &MA) { return MA.isWrite(); });
|
2014-02-21 05:43:54 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map Scop::getReads() {
|
2015-11-12 22:07:09 +08:00
|
|
|
return getAccessesOfType([](MemoryAccess &MA) { return MA.isRead(); });
|
2014-02-21 05:43:54 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 03:22:27 +08:00
|
|
|
isl::union_map Scop::getAccesses() {
|
2015-11-12 22:07:13 +08:00
|
|
|
return getAccessesOfType([](MemoryAccess &MA) { return true; });
|
|
|
|
}
|
|
|
|
|
2017-08-18 06:04:53 +08:00
|
|
|
isl::union_map Scop::getAccesses(ScopArrayInfo *Array) {
|
|
|
|
return getAccessesOfType(
|
|
|
|
[Array](MemoryAccess &MA) { return MA.getScopArrayInfo() == Array; });
|
|
|
|
}
|
|
|
|
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::union_map Scop::getSchedule() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
auto Tree = getScheduleTree();
|
|
|
|
return Tree.get_map();
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
2014-02-21 05:43:54 +08:00
|
|
|
|
2017-08-07 05:42:38 +08:00
|
|
|
isl::schedule Scop::getScheduleTree() const {
|
2017-11-20 06:13:34 +08:00
|
|
|
return Schedule.intersect_domain(getDomains());
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
2014-06-28 16:59:38 +08:00
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
void Scop::setSchedule(isl::union_map NewSchedule) {
|
|
|
|
auto S = isl::schedule::from_domain(getDomains());
|
|
|
|
Schedule = S.insert_partial_schedule(
|
|
|
|
isl::multi_union_pw_aff::from_union_map(NewSchedule));
|
2018-06-07 05:37:35 +08:00
|
|
|
ScheduleModified = true;
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
|
|
|
|
2017-11-20 06:13:34 +08:00
|
|
|
void Scop::setScheduleTree(isl::schedule NewSchedule) {
|
2015-07-14 17:33:13 +08:00
|
|
|
Schedule = NewSchedule;
|
2018-06-07 05:37:35 +08:00
|
|
|
ScheduleModified = true;
|
2014-02-21 05:43:54 +08:00
|
|
|
}
|
|
|
|
|
2017-08-14 14:49:01 +08:00
|
|
|
bool Scop::restrictDomains(isl::union_set Domain) {
|
2014-02-21 05:43:54 +08:00
|
|
|
bool Changed = false;
|
2015-05-27 13:16:57 +08:00
|
|
|
for (ScopStmt &Stmt : *this) {
|
2017-08-14 14:49:01 +08:00
|
|
|
isl::union_set StmtDomain = isl::union_set(Stmt.getDomain());
|
|
|
|
isl::union_set NewStmtDomain = StmtDomain.intersect(Domain);
|
|
|
|
|
|
|
|
if (StmtDomain.is_subset(NewStmtDomain))
|
2014-02-21 05:43:54 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
Changed = true;
|
|
|
|
|
2017-08-14 14:49:01 +08:00
|
|
|
NewStmtDomain = NewStmtDomain.coalesce();
|
2014-02-21 05:43:54 +08:00
|
|
|
|
2017-08-14 14:49:01 +08:00
|
|
|
if (NewStmtDomain.is_empty())
|
2017-08-07 00:39:52 +08:00
|
|
|
Stmt.restrictDomain(isl::set::empty(Stmt.getDomainSpace()));
|
2017-08-14 14:49:01 +08:00
|
|
|
else
|
|
|
|
Stmt.restrictDomain(isl::set(NewStmtDomain));
|
2014-02-21 05:43:54 +08:00
|
|
|
}
|
|
|
|
return Changed;
|
|
|
|
}
|
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
ScalarEvolution *Scop::getSE() const { return SE; }
|
|
|
|
|
2016-09-02 14:33:33 +08:00
|
|
|
// Create an isl_multi_union_aff that defines an identity mapping from the
|
|
|
|
// elements of USet to their N-th dimension.
|
2015-12-22 06:45:53 +08:00
|
|
|
//
|
|
|
|
// # Example:
|
|
|
|
//
|
|
|
|
// Domain: { A[i,j]; B[i,j,k] }
|
|
|
|
// N: 1
|
2015-07-14 17:33:13 +08:00
|
|
|
//
|
2015-12-22 06:45:53 +08:00
|
|
|
// Resulting Mapping: { {A[i,j] -> [(j)]; B[i,j,k] -> [(j)] }
|
|
|
|
//
|
|
|
|
// @param USet A union set describing the elements for which to generate a
|
|
|
|
// mapping.
|
2015-07-14 17:33:13 +08:00
|
|
|
// @param N The dimension to map to.
|
2015-12-22 06:45:53 +08:00
|
|
|
// @returns A mapping from USet to its N-th dimension.
|
2017-05-27 01:22:03 +08:00
|
|
|
static isl::multi_union_pw_aff mapToDimension(isl::union_set USet, int N) {
|
2015-12-22 06:45:53 +08:00
|
|
|
assert(N >= 0);
|
2015-12-22 07:01:53 +08:00
|
|
|
assert(USet);
|
2017-05-29 19:34:29 +08:00
|
|
|
assert(!USet.is_empty());
|
2015-09-10 23:27:46 +08:00
|
|
|
|
2017-05-27 01:22:03 +08:00
|
|
|
auto Result = isl::union_pw_multi_aff::empty(USet.get_space());
|
2015-07-14 17:33:13 +08:00
|
|
|
|
2018-07-17 03:04:16 +08:00
|
|
|
for (isl::set S : USet.get_set_list()) {
|
2017-05-27 01:22:03 +08:00
|
|
|
int Dim = S.dim(isl::dim::set);
|
|
|
|
auto PMA = isl::pw_multi_aff::project_out_map(S.get_space(), isl::dim::set,
|
|
|
|
N, Dim - N);
|
|
|
|
if (N > 1)
|
|
|
|
PMA = PMA.drop_dims(isl::dim::out, 0, N - 1);
|
2015-07-14 17:33:13 +08:00
|
|
|
|
2017-05-27 01:22:03 +08:00
|
|
|
Result = Result.add_pw_multi_aff(PMA);
|
2018-07-17 03:04:16 +08:00
|
|
|
}
|
2015-12-22 06:45:53 +08:00
|
|
|
|
2017-05-27 01:22:03 +08:00
|
|
|
return isl::multi_union_pw_aff(isl::union_pw_multi_aff(Result));
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
|
|
|
|
2018-01-18 23:15:38 +08:00
|
|
|
void Scop::addScopStmt(BasicBlock *BB, StringRef Name, Loop *SurroundingLoop,
|
|
|
|
std::vector<Instruction *> Instructions) {
|
2016-11-22 04:09:40 +08:00
|
|
|
assert(BB && "Unexpected nullptr!");
|
2018-01-18 23:15:38 +08:00
|
|
|
Stmts.emplace_back(*this, *BB, Name, SurroundingLoop, Instructions);
|
2016-11-22 04:09:40 +08:00
|
|
|
auto *Stmt = &Stmts.back();
|
2017-07-18 23:41:49 +08:00
|
|
|
StmtMap[BB].push_back(Stmt);
|
2017-08-10 00:45:37 +08:00
|
|
|
for (Instruction *Inst : Instructions) {
|
|
|
|
assert(!InstStmtMap.count(Inst) &&
|
|
|
|
"Unexpected statement corresponding to the instruction.");
|
|
|
|
InstStmtMap[Inst] = Stmt;
|
|
|
|
}
|
2016-11-22 04:09:40 +08:00
|
|
|
}
|
|
|
|
|
2018-01-18 23:15:38 +08:00
|
|
|
void Scop::addScopStmt(Region *R, StringRef Name, Loop *SurroundingLoop,
|
2017-08-31 11:15:56 +08:00
|
|
|
std::vector<Instruction *> Instructions) {
|
2016-11-22 04:09:40 +08:00
|
|
|
assert(R && "Unexpected nullptr!");
|
2018-01-18 23:15:38 +08:00
|
|
|
Stmts.emplace_back(*this, *R, Name, SurroundingLoop, Instructions);
|
2016-11-22 04:09:40 +08:00
|
|
|
auto *Stmt = &Stmts.back();
|
2017-08-31 11:15:56 +08:00
|
|
|
|
|
|
|
for (Instruction *Inst : Instructions) {
|
|
|
|
assert(!InstStmtMap.count(Inst) &&
|
|
|
|
"Unexpected statement corresponding to the instruction.");
|
|
|
|
InstStmtMap[Inst] = Stmt;
|
|
|
|
}
|
|
|
|
|
2017-08-10 00:45:37 +08:00
|
|
|
for (BasicBlock *BB : R->blocks()) {
|
2017-07-18 23:41:49 +08:00
|
|
|
StmtMap[BB].push_back(Stmt);
|
2017-08-31 11:15:56 +08:00
|
|
|
if (BB == R->getEntry())
|
|
|
|
continue;
|
2017-08-10 00:45:37 +08:00
|
|
|
for (Instruction &Inst : *BB) {
|
|
|
|
assert(!InstStmtMap.count(&Inst) &&
|
|
|
|
"Unexpected statement corresponding to the instruction.");
|
|
|
|
InstStmtMap[&Inst] = Stmt;
|
|
|
|
}
|
|
|
|
}
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 01:24:59 +08:00
|
|
|
ScopStmt *Scop::addScopStmt(isl::map SourceRel, isl::map TargetRel,
|
|
|
|
isl::set Domain) {
|
2016-11-09 12:24:49 +08:00
|
|
|
#ifndef NDEBUG
|
2017-08-07 01:24:59 +08:00
|
|
|
isl::set SourceDomain = SourceRel.domain();
|
|
|
|
isl::set TargetDomain = TargetRel.domain();
|
|
|
|
assert(Domain.is_subset(TargetDomain) &&
|
2016-11-06 05:02:43 +08:00
|
|
|
"Target access not defined for complete statement domain");
|
2017-08-07 01:24:59 +08:00
|
|
|
assert(Domain.is_subset(SourceDomain) &&
|
2016-11-06 05:02:43 +08:00
|
|
|
"Source access not defined for complete statement domain");
|
2016-11-09 12:24:49 +08:00
|
|
|
#endif
|
2016-09-14 14:26:09 +08:00
|
|
|
Stmts.emplace_back(*this, SourceRel, TargetRel, Domain);
|
|
|
|
CopyStmtsNum++;
|
|
|
|
return &(Stmts.back());
|
|
|
|
}
|
|
|
|
|
2016-05-19 20:34:57 +08:00
|
|
|
void Scop::buildSchedule(LoopInfo &LI) {
|
2016-05-23 20:42:38 +08:00
|
|
|
Loop *L = getLoopSurroundingScop(*this, LI);
|
2016-02-01 19:54:13 +08:00
|
|
|
LoopStackTy LoopStack({LoopStackElementTy(L, nullptr, 0)});
|
2016-05-19 20:34:57 +08:00
|
|
|
buildSchedule(getRegion().getNode(), LoopStack, LI);
|
2016-04-04 03:36:52 +08:00
|
|
|
assert(LoopStack.size() == 1 && LoopStack.back().L == L);
|
|
|
|
Schedule = LoopStack[0].Schedule;
|
2016-01-06 20:59:23 +08:00
|
|
|
}
|
|
|
|
|
2016-02-01 19:54:13 +08:00
|
|
|
/// To generate a schedule for the elements in a Region we traverse the Region
|
|
|
|
/// in reverse-post-order and add the contained RegionNodes in traversal order
|
|
|
|
/// to the schedule of the loop that is currently at the top of the LoopStack.
|
|
|
|
/// For loop-free codes, this results in a correct sequential ordering.
|
|
|
|
///
|
|
|
|
/// Example:
|
|
|
|
/// bb1(0)
|
|
|
|
/// / \.
|
|
|
|
/// bb2(1) bb3(2)
|
|
|
|
/// \ / \.
|
|
|
|
/// bb4(3) bb5(4)
|
|
|
|
/// \ /
|
|
|
|
/// bb6(5)
|
|
|
|
///
|
|
|
|
/// Including loops requires additional processing. Whenever a loop header is
|
|
|
|
/// encountered, the corresponding loop is added to the @p LoopStack. Starting
|
|
|
|
/// from an empty schedule, we first process all RegionNodes that are within
|
|
|
|
/// this loop and complete the sequential schedule at this loop-level before
|
|
|
|
/// processing about any other nodes. To implement this
|
|
|
|
/// loop-nodes-first-processing, the reverse post-order traversal is
|
|
|
|
/// insufficient. Hence, we additionally check if the traversal yields
|
|
|
|
/// sub-regions or blocks that are outside the last loop on the @p LoopStack.
|
|
|
|
/// These region-nodes are then queue and only traverse after the all nodes
|
|
|
|
/// within the current loop have been processed.
|
2016-05-19 20:34:57 +08:00
|
|
|
void Scop::buildSchedule(Region *R, LoopStackTy &LoopStack, LoopInfo &LI) {
|
2016-05-23 20:42:38 +08:00
|
|
|
Loop *OuterScopLoop = getLoopSurroundingScop(*this, LI);
|
2016-02-01 19:54:13 +08:00
|
|
|
|
|
|
|
ReversePostOrderTraversal<Region *> RTraversal(R);
|
|
|
|
std::deque<RegionNode *> WorkList(RTraversal.begin(), RTraversal.end());
|
|
|
|
std::deque<RegionNode *> DelayList;
|
|
|
|
bool LastRNWaiting = false;
|
|
|
|
|
|
|
|
// Iterate over the region @p R in reverse post-order but queue
|
|
|
|
// sub-regions/blocks iff they are not part of the last encountered but not
|
|
|
|
// completely traversed loop. The variable LastRNWaiting is a flag to indicate
|
|
|
|
// that we queued the last sub-region/block from the reverse post-order
|
|
|
|
// iterator. If it is set we have to explore the next sub-region/block from
|
|
|
|
// the iterator (if any) to guarantee progress. If it is not set we first try
|
|
|
|
// the next queued sub-region/blocks.
|
|
|
|
while (!WorkList.empty() || !DelayList.empty()) {
|
|
|
|
RegionNode *RN;
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
if ((LastRNWaiting && !WorkList.empty()) || DelayList.empty()) {
|
2016-02-01 19:54:13 +08:00
|
|
|
RN = WorkList.front();
|
|
|
|
WorkList.pop_front();
|
|
|
|
LastRNWaiting = false;
|
|
|
|
} else {
|
|
|
|
RN = DelayList.front();
|
|
|
|
DelayList.pop_front();
|
|
|
|
}
|
|
|
|
|
|
|
|
Loop *L = getRegionNodeLoop(RN, LI);
|
2016-05-23 20:40:48 +08:00
|
|
|
if (!contains(L))
|
2016-02-01 19:54:13 +08:00
|
|
|
L = OuterScopLoop;
|
|
|
|
|
2016-04-04 03:36:52 +08:00
|
|
|
Loop *LastLoop = LoopStack.back().L;
|
2016-02-01 19:54:13 +08:00
|
|
|
if (LastLoop != L) {
|
2016-04-04 07:09:06 +08:00
|
|
|
if (LastLoop && !LastLoop->contains(L)) {
|
2016-02-01 19:54:13 +08:00
|
|
|
LastRNWaiting = true;
|
|
|
|
DelayList.push_back(RN);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
LoopStack.push_back({L, nullptr, 0});
|
|
|
|
}
|
2016-05-19 20:34:57 +08:00
|
|
|
buildSchedule(RN, LoopStack, LI);
|
2016-02-01 19:54:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-19 20:34:57 +08:00
|
|
|
void Scop::buildSchedule(RegionNode *RN, LoopStackTy &LoopStack, LoopInfo &LI) {
|
2016-01-06 23:30:06 +08:00
|
|
|
if (RN->isSubRegion()) {
|
|
|
|
auto *LocalRegion = RN->getNodeAs<Region>();
|
2016-05-19 20:34:57 +08:00
|
|
|
if (!isNonAffineSubRegion(LocalRegion)) {
|
|
|
|
buildSchedule(LocalRegion, LoopStack, LI);
|
2016-01-06 23:30:06 +08:00
|
|
|
return;
|
2015-09-10 23:27:46 +08:00
|
|
|
}
|
2016-01-06 23:30:06 +08:00
|
|
|
}
|
2015-02-24 20:00:50 +08:00
|
|
|
|
2017-11-17 00:35:19 +08:00
|
|
|
assert(LoopStack.rbegin() != LoopStack.rend());
|
|
|
|
auto LoopData = LoopStack.rbegin();
|
|
|
|
LoopData->NumBlocksProcessed += getNumBlocksInRegionNode(RN);
|
2015-07-14 17:33:13 +08:00
|
|
|
|
2017-07-21 01:18:58 +08:00
|
|
|
for (auto *Stmt : getStmtListFor(RN)) {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::union_set UDomain{Stmt->getDomain()};
|
|
|
|
auto StmtSchedule = isl::schedule::from_domain(UDomain);
|
2017-11-17 00:35:19 +08:00
|
|
|
LoopData->Schedule = combineInSequence(LoopData->Schedule, StmtSchedule);
|
2016-01-06 23:30:06 +08:00
|
|
|
}
|
|
|
|
|
2016-02-01 19:54:13 +08:00
|
|
|
// Check if we just processed the last node in this loop. If we did, finalize
|
|
|
|
// the loop by:
|
|
|
|
//
|
|
|
|
// - adding new schedule dimensions
|
|
|
|
// - folding the resulting schedule into the parent loop schedule
|
|
|
|
// - dropping the loop schedule from the LoopStack.
|
|
|
|
//
|
|
|
|
// Then continue to check surrounding loops, which might also have been
|
|
|
|
// completed by this node.
|
2017-11-17 00:35:19 +08:00
|
|
|
size_t Dimension = LoopStack.size();
|
|
|
|
while (LoopData->L &&
|
|
|
|
LoopData->NumBlocksProcessed == getNumBlocksInLoop(LoopData->L)) {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::schedule Schedule = LoopData->Schedule;
|
2017-11-17 00:35:19 +08:00
|
|
|
auto NumBlocksProcessed = LoopData->NumBlocksProcessed;
|
2016-02-01 19:54:13 +08:00
|
|
|
|
2017-11-17 00:35:19 +08:00
|
|
|
assert(std::next(LoopData) != LoopStack.rend());
|
|
|
|
++LoopData;
|
|
|
|
--Dimension;
|
2016-02-01 19:54:13 +08:00
|
|
|
|
|
|
|
if (Schedule) {
|
2017-11-20 06:13:34 +08:00
|
|
|
isl::union_set Domain = Schedule.get_domain();
|
2017-11-17 00:35:19 +08:00
|
|
|
isl::multi_union_pw_aff MUPA = mapToDimension(Domain, Dimension);
|
2017-11-20 06:13:34 +08:00
|
|
|
Schedule = Schedule.insert_partial_schedule(MUPA);
|
2017-11-17 00:35:19 +08:00
|
|
|
LoopData->Schedule = combineInSequence(LoopData->Schedule, Schedule);
|
2016-01-06 23:30:06 +08:00
|
|
|
}
|
2015-12-22 07:01:53 +08:00
|
|
|
|
2017-11-17 00:35:19 +08:00
|
|
|
LoopData->NumBlocksProcessed += NumBlocksProcessed;
|
2015-07-14 17:33:13 +08:00
|
|
|
}
|
2017-11-17 00:35:19 +08:00
|
|
|
// Now pop all loops processed up there from the LoopStack
|
|
|
|
LoopStack.erase(LoopStack.begin() + Dimension, LoopStack.end());
|
2011-04-29 14:27:02 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 01:08:50 +08:00
|
|
|
ArrayRef<ScopStmt *> Scop::getStmtListFor(BasicBlock *BB) const {
|
|
|
|
auto StmtMapIt = StmtMap.find(BB);
|
|
|
|
if (StmtMapIt == StmtMap.end())
|
|
|
|
return {};
|
|
|
|
return StmtMapIt->second;
|
|
|
|
}
|
|
|
|
|
2018-01-24 07:56:36 +08:00
|
|
|
ScopStmt *Scop::getIncomingStmtFor(const Use &U) const {
|
|
|
|
auto *PHI = cast<PHINode>(U.getUser());
|
|
|
|
BasicBlock *IncomingBB = PHI->getIncomingBlock(U);
|
|
|
|
|
|
|
|
// If the value is a non-synthesizable from the incoming block, use the
|
|
|
|
// statement that contains it as user statement.
|
|
|
|
if (auto *IncomingInst = dyn_cast<Instruction>(U.get())) {
|
|
|
|
if (IncomingInst->getParent() == IncomingBB) {
|
|
|
|
if (ScopStmt *IncomingStmt = getStmtFor(IncomingInst))
|
|
|
|
return IncomingStmt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, use the epilogue/last statement.
|
|
|
|
return getLastStmtFor(IncomingBB);
|
|
|
|
}
|
|
|
|
|
2017-07-21 01:08:50 +08:00
|
|
|
ScopStmt *Scop::getLastStmtFor(BasicBlock *BB) const {
|
|
|
|
ArrayRef<ScopStmt *> StmtList = getStmtListFor(BB);
|
2017-08-23 05:25:51 +08:00
|
|
|
if (!StmtList.empty())
|
2017-07-21 01:08:50 +08:00
|
|
|
return StmtList.back();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-07-21 01:18:58 +08:00
|
|
|
ArrayRef<ScopStmt *> Scop::getStmtListFor(RegionNode *RN) const {
|
2016-02-25 06:08:19 +08:00
|
|
|
if (RN->isSubRegion())
|
2017-07-21 01:18:58 +08:00
|
|
|
return getStmtListFor(RN->getNodeAs<Region>());
|
|
|
|
return getStmtListFor(RN->getNodeAs<BasicBlock>());
|
2016-02-25 06:08:19 +08:00
|
|
|
}
|
|
|
|
|
2017-07-21 01:18:58 +08:00
|
|
|
ArrayRef<ScopStmt *> Scop::getStmtListFor(Region *R) const {
|
|
|
|
return getStmtListFor(R->getEntry());
|
2015-12-14 03:21:45 +08:00
|
|
|
}
|
|
|
|
|
2015-08-31 05:13:53 +08:00
|
|
|
int Scop::getRelativeLoopDepth(const Loop *L) const {
|
2017-05-25 02:39:39 +08:00
|
|
|
if (!L || !R.contains(L))
|
2015-08-31 05:13:53 +08:00
|
|
|
return -1;
|
2017-05-25 02:39:39 +08:00
|
|
|
// outermostLoopInRegion always returns nullptr for top level regions
|
|
|
|
if (R.isTopLevelRegion()) {
|
|
|
|
// LoopInfo's depths start at 1, we start at 0
|
|
|
|
return L->getLoopDepth() - 1;
|
|
|
|
} else {
|
|
|
|
Loop *OuterLoop = R.outermostLoopInRegion(const_cast<Loop *>(L));
|
|
|
|
assert(OuterLoop);
|
|
|
|
return L->getLoopDepth() - OuterLoop->getLoopDepth();
|
|
|
|
}
|
2015-08-27 14:53:52 +08:00
|
|
|
}
|
|
|
|
|
2016-07-30 17:25:51 +08:00
|
|
|
ScopArrayInfo *Scop::getArrayInfoByName(const std::string BaseName) {
|
|
|
|
for (auto &SAI : arrays()) {
|
|
|
|
if (SAI->getName() == BaseName)
|
|
|
|
return SAI;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-07-20 01:11:25 +08:00
|
|
|
void Scop::addAccessData(MemoryAccess *Access) {
|
|
|
|
const ScopArrayInfo *SAI = Access->getOriginalScopArrayInfo();
|
|
|
|
assert(SAI && "can only use after access relations have been constructed");
|
|
|
|
|
|
|
|
if (Access->isOriginalValueKind() && Access->isRead())
|
|
|
|
ValueUseAccs[SAI].push_back(Access);
|
|
|
|
else if (Access->isOriginalAnyPHIKind() && Access->isWrite())
|
|
|
|
PHIIncomingAccs[SAI].push_back(Access);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scop::removeAccessData(MemoryAccess *Access) {
|
2017-09-21 22:23:11 +08:00
|
|
|
if (Access->isOriginalValueKind() && Access->isWrite()) {
|
|
|
|
ValueDefAccs.erase(Access->getAccessValue());
|
|
|
|
} else if (Access->isOriginalValueKind() && Access->isRead()) {
|
2017-07-20 01:11:25 +08:00
|
|
|
auto &Uses = ValueUseAccs[Access->getScopArrayInfo()];
|
2018-04-10 07:13:01 +08:00
|
|
|
auto NewEnd = std::remove(Uses.begin(), Uses.end(), Access);
|
|
|
|
Uses.erase(NewEnd, Uses.end());
|
2017-09-21 22:23:11 +08:00
|
|
|
} else if (Access->isOriginalPHIKind() && Access->isRead()) {
|
|
|
|
PHINode *PHI = cast<PHINode>(Access->getAccessInstruction());
|
|
|
|
PHIReadAccs.erase(PHI);
|
2017-07-20 01:11:25 +08:00
|
|
|
} else if (Access->isOriginalAnyPHIKind() && Access->isWrite()) {
|
|
|
|
auto &Incomings = PHIIncomingAccs[Access->getScopArrayInfo()];
|
2018-04-10 07:13:01 +08:00
|
|
|
auto NewEnd = std::remove(Incomings.begin(), Incomings.end(), Access);
|
|
|
|
Incomings.erase(NewEnd, Incomings.end());
|
2017-07-20 01:11:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoryAccess *Scop::getValueDef(const ScopArrayInfo *SAI) const {
|
|
|
|
assert(SAI->isValueKind());
|
|
|
|
|
|
|
|
Instruction *Val = dyn_cast<Instruction>(SAI->getBasePtr());
|
|
|
|
if (!Val)
|
|
|
|
return nullptr;
|
|
|
|
|
2017-09-21 22:23:11 +08:00
|
|
|
return ValueDefAccs.lookup(Val);
|
2017-07-20 01:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef<MemoryAccess *> Scop::getValueUses(const ScopArrayInfo *SAI) const {
|
|
|
|
assert(SAI->isValueKind());
|
|
|
|
auto It = ValueUseAccs.find(SAI);
|
|
|
|
if (It == ValueUseAccs.end())
|
|
|
|
return {};
|
|
|
|
return It->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoryAccess *Scop::getPHIRead(const ScopArrayInfo *SAI) const {
|
|
|
|
assert(SAI->isPHIKind() || SAI->isExitPHIKind());
|
|
|
|
|
|
|
|
if (SAI->isExitPHIKind())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
PHINode *PHI = cast<PHINode>(SAI->getBasePtr());
|
2017-09-21 22:23:11 +08:00
|
|
|
return PHIReadAccs.lookup(PHI);
|
2017-07-20 01:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef<MemoryAccess *> Scop::getPHIIncomings(const ScopArrayInfo *SAI) const {
|
|
|
|
assert(SAI->isPHIKind() || SAI->isExitPHIKind());
|
|
|
|
auto It = PHIIncomingAccs.find(SAI);
|
|
|
|
if (It == PHIIncomingAccs.end())
|
|
|
|
return {};
|
|
|
|
return It->second;
|
|
|
|
}
|
|
|
|
|
2017-07-27 22:39:52 +08:00
|
|
|
bool Scop::isEscaping(Instruction *Inst) {
|
|
|
|
assert(contains(Inst) && "The concept of escaping makes only sense for "
|
|
|
|
"values defined inside the SCoP");
|
|
|
|
|
|
|
|
for (Use &Use : Inst->uses()) {
|
|
|
|
BasicBlock *UserBB = getUseBlock(Use);
|
|
|
|
if (!contains(UserBB))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// When the SCoP region exit needs to be simplified, PHIs in the region exit
|
|
|
|
// move to a new basic block such that its incoming blocks are not in the
|
|
|
|
// SCoP anymore.
|
|
|
|
if (hasSingleExitEdge() && isa<PHINode>(Use.getUser()) &&
|
|
|
|
isExit(cast<PHINode>(Use.getUser())->getParent()))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-23 21:50:30 +08:00
|
|
|
Scop::ScopStatistics Scop::getStatistics() const {
|
|
|
|
ScopStatistics Result;
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
|
|
|
|
auto LoopStat = ScopDetection::countBeneficialLoops(&R, *SE, *getLI(), 0);
|
|
|
|
|
|
|
|
int NumTotalLoops = LoopStat.NumLoops;
|
|
|
|
Result.NumBoxedLoops = getBoxedLoops().size();
|
|
|
|
Result.NumAffineLoops = NumTotalLoops - Result.NumBoxedLoops;
|
|
|
|
|
|
|
|
for (const ScopStmt &Stmt : *this) {
|
|
|
|
isl::set Domain = Stmt.getDomain().intersect_params(getContext());
|
|
|
|
bool IsInLoop = Stmt.getNumIterators() >= 1;
|
|
|
|
for (MemoryAccess *MA : Stmt) {
|
|
|
|
if (!MA->isWrite())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (MA->isLatestValueKind()) {
|
|
|
|
Result.NumValueWrites += 1;
|
|
|
|
if (IsInLoop)
|
|
|
|
Result.NumValueWritesInLoops += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MA->isLatestAnyPHIKind()) {
|
|
|
|
Result.NumPHIWrites += 1;
|
|
|
|
if (IsInLoop)
|
|
|
|
Result.NumPHIWritesInLoops += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
isl::set AccSet =
|
|
|
|
MA->getAccessRelation().intersect_domain(Domain).range();
|
|
|
|
if (AccSet.is_singleton()) {
|
|
|
|
Result.NumSingletonWrites += 1;
|
|
|
|
if (IsInLoop)
|
|
|
|
Result.NumSingletonWritesInLoops += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2017-08-23 05:25:51 +08:00
|
|
|
raw_ostream &polly::operator<<(raw_ostream &OS, const Scop &scop) {
|
|
|
|
scop.print(OS, PollyPrintInstructions);
|
|
|
|
return OS;
|
2017-07-21 23:35:53 +08:00
|
|
|
}
|
|
|
|
|
2016-05-31 17:41:04 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ScopInfoRegionPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
|
|
AU.addRequired<LoopInfoWrapperPass>();
|
|
|
|
AU.addRequired<RegionInfoPass>();
|
|
|
|
AU.addRequired<DominatorTreeWrapperPass>();
|
|
|
|
AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
|
2017-05-12 22:37:29 +08:00
|
|
|
AU.addRequiredTransitive<ScopDetectionWrapperPass>();
|
2016-05-31 17:41:04 +08:00
|
|
|
AU.addRequired<AAResultsWrapperPass>();
|
2017-03-17 21:56:53 +08:00
|
|
|
AU.addRequired<AssumptionCacheTracker>();
|
2017-08-28 22:07:33 +08:00
|
|
|
AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
|
2016-05-31 17:41:04 +08:00
|
|
|
AU.setPreservesAll();
|
|
|
|
}
|
|
|
|
|
2017-08-23 21:50:30 +08:00
|
|
|
void updateLoopCountStatistic(ScopDetection::LoopStats Stats,
|
|
|
|
Scop::ScopStatistics ScopStats) {
|
|
|
|
assert(Stats.NumLoops == ScopStats.NumAffineLoops + ScopStats.NumBoxedLoops);
|
|
|
|
|
|
|
|
NumScops++;
|
2017-02-17 16:12:36 +08:00
|
|
|
NumLoopsInScop += Stats.NumLoops;
|
|
|
|
MaxNumLoopsInScop =
|
|
|
|
std::max(MaxNumLoopsInScop.getValue(), (unsigned)Stats.NumLoops);
|
|
|
|
|
2018-04-19 04:03:36 +08:00
|
|
|
if (Stats.MaxDepth == 0)
|
|
|
|
NumScopsDepthZero++;
|
|
|
|
else if (Stats.MaxDepth == 1)
|
2017-02-17 16:12:36 +08:00
|
|
|
NumScopsDepthOne++;
|
|
|
|
else if (Stats.MaxDepth == 2)
|
|
|
|
NumScopsDepthTwo++;
|
|
|
|
else if (Stats.MaxDepth == 3)
|
|
|
|
NumScopsDepthThree++;
|
|
|
|
else if (Stats.MaxDepth == 4)
|
|
|
|
NumScopsDepthFour++;
|
|
|
|
else if (Stats.MaxDepth == 5)
|
|
|
|
NumScopsDepthFive++;
|
|
|
|
else
|
|
|
|
NumScopsDepthLarger++;
|
2017-08-23 21:50:30 +08:00
|
|
|
|
|
|
|
NumAffineLoops += ScopStats.NumAffineLoops;
|
|
|
|
NumBoxedLoops += ScopStats.NumBoxedLoops;
|
|
|
|
|
|
|
|
NumValueWrites += ScopStats.NumValueWrites;
|
|
|
|
NumValueWritesInLoops += ScopStats.NumValueWritesInLoops;
|
|
|
|
NumPHIWrites += ScopStats.NumPHIWrites;
|
|
|
|
NumPHIWritesInLoops += ScopStats.NumPHIWritesInLoops;
|
|
|
|
NumSingletonWrites += ScopStats.NumSingletonWrites;
|
|
|
|
NumSingletonWritesInLoops += ScopStats.NumSingletonWritesInLoops;
|
2017-02-17 16:12:36 +08:00
|
|
|
}
|
|
|
|
|
2016-05-31 17:41:04 +08:00
|
|
|
bool ScopInfoRegionPass::runOnRegion(Region *R, RGPassManager &RGM) {
|
2017-05-12 22:37:29 +08:00
|
|
|
auto &SD = getAnalysis<ScopDetectionWrapperPass>().getSD();
|
2016-05-31 17:41:04 +08:00
|
|
|
|
|
|
|
if (!SD.isMaxRegionInScop(*R))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Function *F = R->getEntry()->getParent();
|
|
|
|
auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
|
|
|
|
auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
|
|
|
|
auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
|
|
|
|
auto const &DL = F->getParent()->getDataLayout();
|
|
|
|
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
|
2017-03-17 21:56:53 +08:00
|
|
|
auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(*F);
|
2017-08-28 22:07:33 +08:00
|
|
|
auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
|
2016-05-31 17:41:04 +08:00
|
|
|
|
2017-08-28 22:07:33 +08:00
|
|
|
ScopBuilder SB(R, AC, AA, DL, DT, LI, SD, SE, ORE);
|
2016-06-27 17:25:40 +08:00
|
|
|
S = SB.getScop(); // take ownership of scop object
|
2017-02-17 16:12:36 +08:00
|
|
|
|
2017-08-23 21:50:30 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
|
2017-02-17 16:12:36 +08:00
|
|
|
if (S) {
|
|
|
|
ScopDetection::LoopStats Stats =
|
|
|
|
ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
|
2017-08-23 21:50:30 +08:00
|
|
|
updateLoopCountStatistic(Stats, S->getStatistics());
|
2017-02-17 16:12:36 +08:00
|
|
|
}
|
2017-08-23 21:50:30 +08:00
|
|
|
#endif
|
2017-02-17 16:12:36 +08:00
|
|
|
|
2011-04-29 14:27:02 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-05-31 17:41:04 +08:00
|
|
|
void ScopInfoRegionPass::print(raw_ostream &OS, const Module *) const {
|
2016-06-27 17:25:40 +08:00
|
|
|
if (S)
|
2017-07-21 23:35:53 +08:00
|
|
|
S->print(OS, PollyPrintInstructions);
|
2016-06-27 17:25:40 +08:00
|
|
|
else
|
|
|
|
OS << "Invalid Scop!\n";
|
2016-05-31 17:41:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
char ScopInfoRegionPass::ID = 0;
|
2011-04-29 14:27:02 +08:00
|
|
|
|
2016-05-31 17:41:04 +08:00
|
|
|
Pass *polly::createScopInfoRegionPassPass() { return new ScopInfoRegionPass(); }
|
2013-03-23 09:05:07 +08:00
|
|
|
|
2016-05-31 17:41:04 +08:00
|
|
|
INITIALIZE_PASS_BEGIN(ScopInfoRegionPass, "polly-scops",
|
2011-10-08 08:30:40 +08:00
|
|
|
"Polly - Create polyhedral description of Scops", false,
|
2013-03-23 09:05:07 +08:00
|
|
|
false);
|
2015-09-10 06:13:56 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
|
2017-03-17 21:56:53 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker);
|
2015-01-17 22:16:56 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
|
2014-07-20 02:40:17 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
|
2015-08-17 18:57:08 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
|
2017-05-12 22:37:29 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
|
2015-08-31 05:13:53 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
|
2016-05-31 17:41:04 +08:00
|
|
|
INITIALIZE_PASS_END(ScopInfoRegionPass, "polly-scops",
|
2011-10-08 08:30:40 +08:00
|
|
|
"Polly - Create polyhedral description of Scops", false,
|
|
|
|
false)
|
2016-06-27 17:32:30 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2017-05-15 20:55:14 +08:00
|
|
|
ScopInfo::ScopInfo(const DataLayout &DL, ScopDetection &SD, ScalarEvolution &SE,
|
|
|
|
LoopInfo &LI, AliasAnalysis &AA, DominatorTree &DT,
|
2017-08-28 22:07:33 +08:00
|
|
|
AssumptionCache &AC, OptimizationRemarkEmitter &ORE)
|
|
|
|
: DL(DL), SD(SD), SE(SE), LI(LI), AA(AA), DT(DT), AC(AC), ORE(ORE) {
|
2017-08-10 15:43:46 +08:00
|
|
|
recompute();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScopInfo::recompute() {
|
|
|
|
RegionToScopMap.clear();
|
2017-06-08 20:06:15 +08:00
|
|
|
/// Create polyhedral description of scops for all the valid regions of a
|
2017-05-15 20:55:14 +08:00
|
|
|
/// function.
|
|
|
|
for (auto &It : SD) {
|
|
|
|
Region *R = const_cast<Region *>(It);
|
|
|
|
if (!SD.isMaxRegionInScop(*R))
|
|
|
|
continue;
|
|
|
|
|
2017-08-28 22:07:33 +08:00
|
|
|
ScopBuilder SB(R, AC, AA, DL, DT, LI, SD, SE, ORE);
|
2017-05-15 20:55:14 +08:00
|
|
|
std::unique_ptr<Scop> S = SB.getScop();
|
|
|
|
if (!S)
|
|
|
|
continue;
|
2017-08-23 21:50:30 +08:00
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
|
2017-08-02 19:14:41 +08:00
|
|
|
ScopDetection::LoopStats Stats =
|
|
|
|
ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
|
2017-08-23 21:50:30 +08:00
|
|
|
updateLoopCountStatistic(Stats, S->getStatistics());
|
|
|
|
#endif
|
2017-05-15 20:55:14 +08:00
|
|
|
bool Inserted = RegionToScopMap.insert({R, std::move(S)}).second;
|
|
|
|
assert(Inserted && "Building Scop for the same region twice!");
|
|
|
|
(void)Inserted;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-10 15:43:46 +08:00
|
|
|
bool ScopInfo::invalidate(Function &F, const PreservedAnalyses &PA,
|
|
|
|
FunctionAnalysisManager::Invalidator &Inv) {
|
|
|
|
// Check whether the analysis, all analyses on functions have been preserved
|
|
|
|
// or anything we're holding references to is being invalidated
|
|
|
|
auto PAC = PA.getChecker<ScopInfoAnalysis>();
|
|
|
|
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()) ||
|
|
|
|
Inv.invalidate<ScopAnalysis>(F, PA) ||
|
|
|
|
Inv.invalidate<ScalarEvolutionAnalysis>(F, PA) ||
|
|
|
|
Inv.invalidate<LoopAnalysis>(F, PA) ||
|
|
|
|
Inv.invalidate<AAManager>(F, PA) ||
|
|
|
|
Inv.invalidate<DominatorTreeAnalysis>(F, PA) ||
|
|
|
|
Inv.invalidate<AssumptionAnalysis>(F, PA);
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:55:14 +08:00
|
|
|
AnalysisKey ScopInfoAnalysis::Key;
|
|
|
|
|
|
|
|
ScopInfoAnalysis::Result ScopInfoAnalysis::run(Function &F,
|
|
|
|
FunctionAnalysisManager &FAM) {
|
|
|
|
auto &SD = FAM.getResult<ScopAnalysis>(F);
|
|
|
|
auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
|
|
|
|
auto &LI = FAM.getResult<LoopAnalysis>(F);
|
|
|
|
auto &AA = FAM.getResult<AAManager>(F);
|
|
|
|
auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
|
|
|
|
auto &AC = FAM.getResult<AssumptionAnalysis>(F);
|
|
|
|
auto &DL = F.getParent()->getDataLayout();
|
2017-08-28 22:07:33 +08:00
|
|
|
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
|
|
|
|
return {DL, SD, SE, LI, AA, DT, AC, ORE};
|
2017-05-15 20:55:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
PreservedAnalyses ScopInfoPrinterPass::run(Function &F,
|
|
|
|
FunctionAnalysisManager &FAM) {
|
|
|
|
auto &SI = FAM.getResult<ScopInfoAnalysis>(F);
|
2017-08-04 19:28:51 +08:00
|
|
|
// Since the legacy PM processes Scops in bottom up, we print them in reverse
|
|
|
|
// order here to keep the output persistent
|
|
|
|
for (auto &It : reverse(SI)) {
|
2017-05-15 20:55:14 +08:00
|
|
|
if (It.second)
|
2017-07-21 23:35:53 +08:00
|
|
|
It.second->print(Stream, PollyPrintInstructions);
|
2017-05-15 20:55:14 +08:00
|
|
|
else
|
|
|
|
Stream << "Invalid Scop!\n";
|
|
|
|
}
|
|
|
|
return PreservedAnalyses::all();
|
|
|
|
}
|
|
|
|
|
2016-06-27 17:32:30 +08:00
|
|
|
void ScopInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
|
|
AU.addRequired<LoopInfoWrapperPass>();
|
|
|
|
AU.addRequired<RegionInfoPass>();
|
|
|
|
AU.addRequired<DominatorTreeWrapperPass>();
|
|
|
|
AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
|
2017-05-12 22:37:29 +08:00
|
|
|
AU.addRequiredTransitive<ScopDetectionWrapperPass>();
|
2016-06-27 17:32:30 +08:00
|
|
|
AU.addRequired<AAResultsWrapperPass>();
|
2017-03-17 21:56:53 +08:00
|
|
|
AU.addRequired<AssumptionCacheTracker>();
|
2017-08-28 22:07:33 +08:00
|
|
|
AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
|
2016-06-27 17:32:30 +08:00
|
|
|
AU.setPreservesAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScopInfoWrapperPass::runOnFunction(Function &F) {
|
2017-05-12 22:37:29 +08:00
|
|
|
auto &SD = getAnalysis<ScopDetectionWrapperPass>().getSD();
|
2016-06-27 17:32:30 +08:00
|
|
|
auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
|
|
|
|
auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
|
|
|
|
auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
|
|
|
|
auto const &DL = F.getParent()->getDataLayout();
|
|
|
|
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
|
2017-03-17 21:56:53 +08:00
|
|
|
auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
|
2017-08-28 22:07:33 +08:00
|
|
|
auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
|
2016-06-27 17:32:30 +08:00
|
|
|
|
2017-08-28 22:07:33 +08:00
|
|
|
Result.reset(new ScopInfo{DL, SD, SE, LI, AA, DT, AC, ORE});
|
2016-06-27 17:32:30 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScopInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
|
2017-05-15 20:55:14 +08:00
|
|
|
for (auto &It : *Result) {
|
2016-06-27 17:32:30 +08:00
|
|
|
if (It.second)
|
2017-07-21 23:35:53 +08:00
|
|
|
It.second->print(OS, PollyPrintInstructions);
|
2016-06-27 17:32:30 +08:00
|
|
|
else
|
|
|
|
OS << "Invalid Scop!\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char ScopInfoWrapperPass::ID = 0;
|
|
|
|
|
|
|
|
Pass *polly::createScopInfoWrapperPassPass() {
|
|
|
|
return new ScopInfoWrapperPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
INITIALIZE_PASS_BEGIN(
|
|
|
|
ScopInfoWrapperPass, "polly-function-scops",
|
|
|
|
"Polly - Create polyhedral description of all Scops of a function", false,
|
|
|
|
false);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
|
2017-03-17 21:56:53 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker);
|
2016-06-27 17:32:30 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
|
2017-05-12 22:37:29 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
|
2016-06-27 17:32:30 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
|
|
|
|
INITIALIZE_PASS_END(
|
|
|
|
ScopInfoWrapperPass, "polly-function-scops",
|
|
|
|
"Polly - Create polyhedral description of all Scops of a function", false,
|
|
|
|
false)
|