2018-08-22 01:32:24 +08:00
|
|
|
//===- AffineStructures.cpp - MLIR Affine Structures Class-------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
|
|
// Structures for affine/polyhedral analysis of MLIR functions.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/Analysis/AffineStructures.h"
|
2018-09-13 01:21:23 +08:00
|
|
|
#include "mlir/Analysis/AffineAnalysis.h"
|
2018-10-09 02:10:11 +08:00
|
|
|
#include "mlir/IR/AffineExprVisitor.h"
|
2018-10-26 13:39:14 +08:00
|
|
|
#include "mlir/IR/AffineMap.h"
|
2018-10-11 05:23:30 +08:00
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
2018-08-22 01:32:24 +08:00
|
|
|
#include "mlir/IR/IntegerSet.h"
|
2018-10-09 02:10:11 +08:00
|
|
|
#include "mlir/IR/MLValue.h"
|
2018-11-17 12:12:06 +08:00
|
|
|
#include "mlir/IR/Statements.h"
|
2018-10-25 02:30:06 +08:00
|
|
|
#include "mlir/Support/MathExtras.h"
|
2018-10-09 02:10:11 +08:00
|
|
|
#include "llvm/ADT/DenseSet.h"
|
2018-10-25 23:33:02 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2018-10-09 02:10:11 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2018-08-22 01:32:24 +08:00
|
|
|
|
2018-10-25 23:33:02 +08:00
|
|
|
#define DEBUG_TYPE "affine-structures"
|
|
|
|
|
2018-09-05 06:55:38 +08:00
|
|
|
using namespace mlir;
|
2018-10-09 02:10:11 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Affine map composition terminology:
|
|
|
|
// *) current: refers to the target map of the composition operation. It is the
|
|
|
|
// map into which results from the 'input' map are forward substituted.
|
|
|
|
// *) input: refers to the map which is being forward substituted into the
|
|
|
|
// 'current' map.
|
|
|
|
// *) output: refers to the resulting affine map after composition.
|
|
|
|
|
|
|
|
// AffineMapCompositionUpdate encapsulates the state necessary to compose
|
|
|
|
// AffineExprs for two affine maps using AffineExprComposer (see below).
|
|
|
|
struct AffineMapCompositionUpdate {
|
|
|
|
using PositionMap = DenseMap<unsigned, unsigned>;
|
|
|
|
|
2018-10-09 04:47:18 +08:00
|
|
|
explicit AffineMapCompositionUpdate(ArrayRef<AffineExpr> inputResults)
|
2018-10-09 02:10:11 +08:00
|
|
|
: inputResults(inputResults), outputNumDims(0), outputNumSymbols(0) {}
|
|
|
|
|
|
|
|
// Map from 'curr' affine map dim position to 'output' affine map
|
|
|
|
// dim position.
|
|
|
|
PositionMap currDimMap;
|
|
|
|
// Map from dim position of 'curr' affine map to index into 'inputResults'.
|
|
|
|
PositionMap currDimToInputResultMap;
|
|
|
|
// Map from 'curr' affine map symbol position to 'output' affine map
|
|
|
|
// symbol position.
|
|
|
|
PositionMap currSymbolMap;
|
|
|
|
// Map from 'input' affine map dim position to 'output' affine map
|
|
|
|
// dim position.
|
|
|
|
PositionMap inputDimMap;
|
|
|
|
// Map from 'input' affine map symbol position to 'output' affine map
|
|
|
|
// symbol position.
|
|
|
|
PositionMap inputSymbolMap;
|
|
|
|
// Results of 'input' affine map.
|
2018-10-09 04:47:18 +08:00
|
|
|
ArrayRef<AffineExpr> inputResults;
|
2018-10-09 02:10:11 +08:00
|
|
|
// Number of dimension operands for 'output' affine map.
|
|
|
|
unsigned outputNumDims;
|
|
|
|
// Number of symbol operands for 'output' affine map.
|
|
|
|
unsigned outputNumSymbols;
|
|
|
|
};
|
|
|
|
|
|
|
|
// AffineExprComposer composes two AffineExprs as specified by 'mapUpdate'.
|
|
|
|
class AffineExprComposer {
|
|
|
|
public:
|
|
|
|
// Compose two AffineExprs using dimension and symbol position update maps,
|
|
|
|
// as well as input map result AffineExprs specified in 'mapUpdate'.
|
|
|
|
AffineExprComposer(const AffineMapCompositionUpdate &mapUpdate)
|
|
|
|
: mapUpdate(mapUpdate), walkingInputMap(false) {}
|
|
|
|
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr walk(AffineExpr expr) {
|
2018-10-10 01:59:27 +08:00
|
|
|
switch (expr.getKind()) {
|
2018-10-09 02:10:11 +08:00
|
|
|
case AffineExprKind::Add:
|
|
|
|
return walkBinExpr(
|
2018-10-09 04:47:18 +08:00
|
|
|
expr, [](AffineExpr lhs, AffineExpr rhs) { return lhs + rhs; });
|
2018-10-09 02:10:11 +08:00
|
|
|
case AffineExprKind::Mul:
|
|
|
|
return walkBinExpr(
|
2018-10-09 04:47:18 +08:00
|
|
|
expr, [](AffineExpr lhs, AffineExpr rhs) { return lhs * rhs; });
|
2018-10-09 02:10:11 +08:00
|
|
|
case AffineExprKind::Mod:
|
|
|
|
return walkBinExpr(
|
2018-10-09 04:47:18 +08:00
|
|
|
expr, [](AffineExpr lhs, AffineExpr rhs) { return lhs % rhs; });
|
2018-10-09 02:10:11 +08:00
|
|
|
case AffineExprKind::FloorDiv:
|
2018-10-09 04:47:18 +08:00
|
|
|
return walkBinExpr(expr, [](AffineExpr lhs, AffineExpr rhs) {
|
2018-10-09 02:10:11 +08:00
|
|
|
return lhs.floorDiv(rhs);
|
|
|
|
});
|
|
|
|
case AffineExprKind::CeilDiv:
|
2018-10-09 04:47:18 +08:00
|
|
|
return walkBinExpr(expr, [](AffineExpr lhs, AffineExpr rhs) {
|
2018-10-09 02:10:11 +08:00
|
|
|
return lhs.ceilDiv(rhs);
|
|
|
|
});
|
|
|
|
case AffineExprKind::Constant:
|
|
|
|
return expr;
|
|
|
|
case AffineExprKind::DimId: {
|
2018-10-10 01:59:27 +08:00
|
|
|
unsigned dimPosition = expr.cast<AffineDimExpr>().getPosition();
|
2018-10-09 02:10:11 +08:00
|
|
|
if (walkingInputMap) {
|
|
|
|
return getAffineDimExpr(mapUpdate.inputDimMap.lookup(dimPosition),
|
2018-10-10 01:59:27 +08:00
|
|
|
expr.getContext());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
// Check if we are just mapping this dim to another position.
|
|
|
|
if (mapUpdate.currDimMap.count(dimPosition) > 0) {
|
|
|
|
assert(mapUpdate.currDimToInputResultMap.count(dimPosition) == 0);
|
|
|
|
return getAffineDimExpr(mapUpdate.currDimMap.lookup(dimPosition),
|
2018-10-10 01:59:27 +08:00
|
|
|
expr.getContext());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
// We are substituting an input map result at 'dimPositon'
|
|
|
|
// Forward substitute currDimToInputResultMap[dimPosition] into this
|
|
|
|
// map.
|
|
|
|
AffineExprComposer composer(mapUpdate, /*walkingInputMap=*/true);
|
|
|
|
unsigned inputResultIndex =
|
|
|
|
mapUpdate.currDimToInputResultMap.lookup(dimPosition);
|
|
|
|
assert(inputResultIndex < mapUpdate.inputResults.size());
|
|
|
|
return composer.walk(mapUpdate.inputResults[inputResultIndex]);
|
|
|
|
}
|
|
|
|
case AffineExprKind::SymbolId:
|
2018-10-10 01:59:27 +08:00
|
|
|
unsigned symbolPosition = expr.cast<AffineSymbolExpr>().getPosition();
|
2018-10-09 02:10:11 +08:00
|
|
|
if (walkingInputMap) {
|
|
|
|
return getAffineSymbolExpr(
|
2018-10-10 01:59:27 +08:00
|
|
|
mapUpdate.inputSymbolMap.lookup(symbolPosition), expr.getContext());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
return getAffineSymbolExpr(mapUpdate.currSymbolMap.lookup(symbolPosition),
|
2018-10-10 01:59:27 +08:00
|
|
|
expr.getContext());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
AffineExprComposer(const AffineMapCompositionUpdate &mapUpdate,
|
|
|
|
bool walkingInputMap)
|
|
|
|
: mapUpdate(mapUpdate), walkingInputMap(walkingInputMap) {}
|
|
|
|
|
2018-10-09 04:47:18 +08:00
|
|
|
AffineExpr walkBinExpr(AffineExpr expr,
|
|
|
|
std::function<AffineExpr(AffineExpr, AffineExpr)> op) {
|
|
|
|
auto binOpExpr = expr.cast<AffineBinaryOpExpr>();
|
2018-10-10 01:59:27 +08:00
|
|
|
return op(walk(binOpExpr.getLHS()), walk(binOpExpr.getRHS()));
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Map update specifies to dim and symbol postion maps, as well as the input
|
|
|
|
// result AffineExprs to forward subustitute into the input map.
|
|
|
|
const AffineMapCompositionUpdate &mapUpdate;
|
|
|
|
// True if we are walking an AffineExpr in the 'input' map, false if
|
|
|
|
// we are walking the 'input' map.
|
|
|
|
bool walkingInputMap;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
static void
|
2018-10-09 12:02:12 +08:00
|
|
|
forwardSubstituteMutableAffineMap(const AffineMapCompositionUpdate &mapUpdate,
|
|
|
|
MutableAffineMap *map) {
|
2018-10-09 02:10:11 +08:00
|
|
|
for (unsigned i = 0, e = map->getNumResults(); i < e; i++) {
|
|
|
|
AffineExprComposer composer(mapUpdate);
|
|
|
|
map->setResult(i, composer.walk(map->getResult(i)));
|
|
|
|
}
|
|
|
|
// TODO(andydavis) Evaluate if we need to update range sizes here.
|
|
|
|
map->setNumDims(mapUpdate.outputNumDims);
|
|
|
|
map->setNumSymbols(mapUpdate.outputNumSymbols);
|
|
|
|
}
|
2018-11-02 06:41:08 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MutableAffineMap.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-10 07:39:24 +08:00
|
|
|
MutableAffineMap::MutableAffineMap(AffineMap map)
|
|
|
|
: numDims(map.getNumDims()), numSymbols(map.getNumSymbols()),
|
2018-11-01 22:26:00 +08:00
|
|
|
// A map always has at least 1 result by construction
|
2018-10-10 07:39:24 +08:00
|
|
|
context(map.getResult(0).getContext()) {
|
|
|
|
for (auto result : map.getResults())
|
2018-08-22 01:32:24 +08:00
|
|
|
results.push_back(result);
|
2018-10-10 07:39:24 +08:00
|
|
|
for (auto rangeSize : map.getRangeSizes())
|
2018-08-22 01:32:24 +08:00
|
|
|
results.push_back(rangeSize);
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:26:00 +08:00
|
|
|
void MutableAffineMap::reset(AffineMap map) {
|
|
|
|
results.clear();
|
|
|
|
rangeSizes.clear();
|
|
|
|
numDims = map.getNumDims();
|
|
|
|
numSymbols = map.getNumSymbols();
|
|
|
|
// A map always has at least 1 result by construction
|
|
|
|
context = map.getResult(0).getContext();
|
|
|
|
for (auto result : map.getResults())
|
|
|
|
results.push_back(result);
|
|
|
|
for (auto rangeSize : map.getRangeSizes())
|
|
|
|
results.push_back(rangeSize);
|
|
|
|
}
|
|
|
|
|
2018-08-31 08:35:15 +08:00
|
|
|
bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
|
2018-10-10 01:59:27 +08:00
|
|
|
if (results[idx].isMultipleOf(factor))
|
2018-08-31 08:35:15 +08:00
|
|
|
return true;
|
|
|
|
|
2018-09-05 06:55:38 +08:00
|
|
|
// TODO(bondhugula): use simplifyAffineExpr and FlatAffineConstraints to
|
|
|
|
// complete this (for a more powerful analysis).
|
2018-08-31 08:35:15 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-05 06:55:38 +08:00
|
|
|
// Simplifies the result affine expressions of this map. The expressions have to
|
|
|
|
// be pure for the simplification implemented.
|
|
|
|
void MutableAffineMap::simplify() {
|
|
|
|
// Simplify each of the results if possible.
|
2018-10-04 06:39:12 +08:00
|
|
|
// TODO(ntv): functional-style map
|
2018-09-05 06:55:38 +08:00
|
|
|
for (unsigned i = 0, e = getNumResults(); i < e; i++) {
|
2018-10-04 06:39:12 +08:00
|
|
|
results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
|
2018-09-05 06:55:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
AffineMap MutableAffineMap::getAffineMap() const {
|
2018-10-09 04:47:18 +08:00
|
|
|
return AffineMap::get(numDims, numSymbols, results, rangeSizes);
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
|
2018-10-11 00:45:59 +08:00
|
|
|
MutableIntegerSet::MutableIntegerSet(IntegerSet set, MLIRContext *context)
|
|
|
|
: numDims(set.getNumDims()), numSymbols(set.getNumSymbols()),
|
2018-08-31 08:35:15 +08:00
|
|
|
context(context) {
|
2018-08-22 01:32:24 +08:00
|
|
|
// TODO(bondhugula)
|
|
|
|
}
|
|
|
|
|
2018-08-26 08:17:56 +08:00
|
|
|
// Universal set.
|
2018-08-31 08:35:15 +08:00
|
|
|
MutableIntegerSet::MutableIntegerSet(unsigned numDims, unsigned numSymbols,
|
|
|
|
MLIRContext *context)
|
|
|
|
: numDims(numDims), numSymbols(numSymbols), context(context) {}
|
2018-08-26 08:17:56 +08:00
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AffineValueMap.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineValueMap::AffineValueMap(const AffineApplyOp &op)
|
|
|
|
: map(op.getAffineMap()) {
|
2018-10-09 02:10:11 +08:00
|
|
|
for (auto *operand : op.getOperands())
|
|
|
|
operands.push_back(cast<MLValue>(const_cast<SSAValue *>(operand)));
|
|
|
|
for (unsigned i = 0, e = op.getNumResults(); i < e; i++)
|
|
|
|
results.push_back(cast<MLValue>(const_cast<SSAValue *>(op.getResult(i))));
|
|
|
|
}
|
|
|
|
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineValueMap::AffineValueMap(AffineMap map, ArrayRef<MLValue *> operands)
|
|
|
|
: map(map) {
|
2018-10-09 02:10:11 +08:00
|
|
|
for (MLValue *operand : operands) {
|
|
|
|
this->operands.push_back(operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-01 22:26:00 +08:00
|
|
|
void AffineValueMap::reset(AffineMap map, ArrayRef<MLValue *> operands) {
|
|
|
|
this->operands.clear();
|
|
|
|
this->results.clear();
|
|
|
|
this->map.reset(map);
|
|
|
|
for (MLValue *operand : operands) {
|
|
|
|
this->operands.push_back(operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 01:17:14 +08:00
|
|
|
void AffineValueMap::forwardSubstitute(const AffineApplyOp &inputOp) {
|
|
|
|
SmallVector<bool, 4> inputResultsToSubstitute(inputOp.getNumResults());
|
|
|
|
for (unsigned i = 0, e = inputOp.getNumResults(); i < e; i++)
|
|
|
|
inputResultsToSubstitute[i] = true;
|
|
|
|
forwardSubstitute(inputOp, inputResultsToSubstitute);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AffineValueMap::forwardSubstituteSingle(const AffineApplyOp &inputOp,
|
|
|
|
unsigned inputResultIndex) {
|
|
|
|
SmallVector<bool, 4> inputResultsToSubstitute(inputOp.getNumResults(), false);
|
|
|
|
inputResultsToSubstitute[inputResultIndex] = true;
|
|
|
|
forwardSubstitute(inputOp, inputResultsToSubstitute);
|
|
|
|
}
|
|
|
|
|
2018-10-09 02:10:11 +08:00
|
|
|
// Returns true and sets 'indexOfMatch' if 'valueToMatch' is found in
|
|
|
|
// 'valuesToSearch'. Returns false otherwise.
|
|
|
|
static bool findIndex(MLValue *valueToMatch, ArrayRef<MLValue *> valuesToSearch,
|
2018-10-18 09:01:44 +08:00
|
|
|
unsigned *indexOfMatch) {
|
2018-10-09 02:10:11 +08:00
|
|
|
unsigned size = valuesToSearch.size();
|
|
|
|
for (unsigned i = 0; i < size; ++i) {
|
|
|
|
if (valueToMatch == valuesToSearch[i]) {
|
2018-10-18 09:01:44 +08:00
|
|
|
*indexOfMatch = i;
|
2018-10-09 02:10:11 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AffineValueMap forward substitution composes results from the affine map
|
|
|
|
// associated with 'inputOp', with the map it currently represents. This is
|
|
|
|
// accomplished by updating its MutableAffineMap and operand list to represent
|
|
|
|
// a new 'output' map which is the composition of the 'current' and 'input'
|
|
|
|
// maps (see "Affine map composition terminology" above for details).
|
|
|
|
//
|
|
|
|
// Affine map forward substitution is comprised of the following steps:
|
|
|
|
// *) Compute input affine map result indices used by the current map.
|
|
|
|
// *) Gather all dim and symbol positions from all AffineExpr input results
|
|
|
|
// computed in previous step.
|
|
|
|
// *) Build output operand list:
|
|
|
|
// *) Add curr map dim operands:
|
|
|
|
// *) If curr dim operand is being forward substituted by result of input
|
|
|
|
// map, store mapping from curr postion to input result index.
|
|
|
|
// *) Else add curr dim operand to output operand list.
|
|
|
|
// *) Add input map dim operands:
|
|
|
|
// *) If input map dim operand is used (step 2), add to output operand
|
|
|
|
// list (scanning current list for dups before updating mapping).
|
|
|
|
// *) Add curr map dim symbols.
|
|
|
|
// *) Add input map dim symbols (if used from step 2), dedup if needed.
|
|
|
|
// *) Update operands and forward substitute new dim and symbol mappings
|
|
|
|
// into MutableAffineMap 'map'.
|
|
|
|
//
|
|
|
|
// TODO(andydavis) Move this to a function which can be shared with
|
2018-10-09 12:02:12 +08:00
|
|
|
// forwardSubstitute(const AffineValueMap &inputMap).
|
2018-10-16 01:17:14 +08:00
|
|
|
void AffineValueMap::forwardSubstitute(
|
|
|
|
const AffineApplyOp &inputOp, ArrayRef<bool> inputResultsToSubstitute) {
|
2018-10-09 02:10:11 +08:00
|
|
|
unsigned currNumDims = map.getNumDims();
|
|
|
|
unsigned inputNumResults = inputOp.getNumResults();
|
|
|
|
|
|
|
|
// Gather result indices from 'inputOp' used by current map.
|
|
|
|
DenseSet<unsigned> inputResultsUsed;
|
|
|
|
DenseMap<unsigned, unsigned> currOperandToInputResult;
|
|
|
|
for (unsigned i = 0; i < currNumDims; ++i) {
|
|
|
|
for (unsigned j = 0; j < inputNumResults; ++j) {
|
2018-10-16 01:17:14 +08:00
|
|
|
if (!inputResultsToSubstitute[j])
|
|
|
|
continue;
|
2018-10-09 02:10:11 +08:00
|
|
|
if (operands[i] ==
|
|
|
|
cast<MLValue>(const_cast<SSAValue *>(inputOp.getResult(j)))) {
|
|
|
|
currOperandToInputResult[i] = j;
|
|
|
|
inputResultsUsed.insert(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return if there were no uses of 'inputOp' results in 'operands'.
|
|
|
|
if (inputResultsUsed.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
class AffineExprPositionGatherer
|
|
|
|
: public AffineExprVisitor<AffineExprPositionGatherer> {
|
|
|
|
public:
|
|
|
|
unsigned numDims;
|
|
|
|
DenseSet<unsigned> *positions;
|
|
|
|
AffineExprPositionGatherer(unsigned numDims, DenseSet<unsigned> *positions)
|
|
|
|
: numDims(numDims), positions(positions) {}
|
2018-10-09 04:47:18 +08:00
|
|
|
void visitDimExpr(AffineDimExpr expr) {
|
2018-10-10 01:59:27 +08:00
|
|
|
positions->insert(expr.getPosition());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
2018-10-09 04:47:18 +08:00
|
|
|
void visitSymbolExpr(AffineSymbolExpr expr) {
|
2018-10-10 01:59:27 +08:00
|
|
|
positions->insert(numDims + expr.getPosition());
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Gather dim and symbol positions from 'inputOp' on which
|
|
|
|
// 'inputResultsUsed' depend.
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMap inputMap = inputOp.getAffineMap();
|
|
|
|
unsigned inputNumDims = inputMap.getNumDims();
|
2018-10-09 02:10:11 +08:00
|
|
|
DenseSet<unsigned> inputPositionsUsed;
|
|
|
|
AffineExprPositionGatherer gatherer(inputNumDims, &inputPositionsUsed);
|
|
|
|
for (unsigned i = 0; i < inputNumResults; ++i) {
|
|
|
|
if (inputResultsUsed.count(i) == 0)
|
|
|
|
continue;
|
2018-10-10 07:39:24 +08:00
|
|
|
gatherer.walkPostOrder(inputMap.getResult(i));
|
2018-10-09 02:10:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build new output operands list and map update.
|
|
|
|
SmallVector<MLValue *, 4> outputOperands;
|
|
|
|
unsigned outputOperandPosition = 0;
|
2018-10-10 07:39:24 +08:00
|
|
|
AffineMapCompositionUpdate mapUpdate(inputOp.getAffineMap().getResults());
|
2018-10-09 02:10:11 +08:00
|
|
|
|
|
|
|
// Add dim operands from current map.
|
|
|
|
for (unsigned i = 0; i < currNumDims; ++i) {
|
|
|
|
if (currOperandToInputResult.count(i) > 0) {
|
|
|
|
mapUpdate.currDimToInputResultMap[i] = currOperandToInputResult[i];
|
|
|
|
} else {
|
|
|
|
mapUpdate.currDimMap[i] = outputOperandPosition++;
|
|
|
|
outputOperands.push_back(operands[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add dim operands from input map.
|
|
|
|
for (unsigned i = 0; i < inputNumDims; ++i) {
|
|
|
|
// Skip input dim operands that we won't use.
|
|
|
|
if (inputPositionsUsed.count(i) == 0)
|
|
|
|
continue;
|
|
|
|
// Check if input operand has a dup in current operand list.
|
|
|
|
auto *inputOperand =
|
|
|
|
cast<MLValue>(const_cast<SSAValue *>(inputOp.getOperand(i)));
|
|
|
|
unsigned outputIndex;
|
2018-10-18 09:01:44 +08:00
|
|
|
if (findIndex(inputOperand, outputOperands, &outputIndex)) {
|
2018-10-09 02:10:11 +08:00
|
|
|
mapUpdate.inputDimMap[i] = outputIndex;
|
|
|
|
} else {
|
|
|
|
mapUpdate.inputDimMap[i] = outputOperandPosition++;
|
|
|
|
outputOperands.push_back(inputOperand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done adding dimension operands, so store new output num dims.
|
|
|
|
unsigned outputNumDims = outputOperandPosition;
|
|
|
|
|
|
|
|
// Add symbol operands from current map.
|
|
|
|
unsigned currNumOperands = operands.size();
|
|
|
|
for (unsigned i = currNumDims; i < currNumOperands; ++i) {
|
|
|
|
unsigned currSymbolPosition = i - currNumDims;
|
|
|
|
unsigned outputSymbolPosition = outputOperandPosition - outputNumDims;
|
|
|
|
mapUpdate.currSymbolMap[currSymbolPosition] = outputSymbolPosition;
|
|
|
|
outputOperands.push_back(operands[i]);
|
|
|
|
++outputOperandPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add symbol operands from input map.
|
|
|
|
unsigned inputNumOperands = inputOp.getNumOperands();
|
|
|
|
for (unsigned i = inputNumDims; i < inputNumOperands; ++i) {
|
|
|
|
// Skip input symbol operands that we won't use.
|
|
|
|
if (inputPositionsUsed.count(i) == 0)
|
|
|
|
continue;
|
|
|
|
unsigned inputSymbolPosition = i - inputNumDims;
|
|
|
|
// Check if input operand has a dup in current operand list.
|
|
|
|
auto *inputOperand =
|
|
|
|
cast<MLValue>(const_cast<SSAValue *>(inputOp.getOperand(i)));
|
|
|
|
// Find output operand index of 'inputOperand' dup.
|
|
|
|
unsigned outputIndex;
|
2018-10-18 09:01:44 +08:00
|
|
|
if (findIndex(inputOperand, outputOperands, &outputIndex)) {
|
2018-10-09 02:10:11 +08:00
|
|
|
unsigned outputSymbolPosition = outputIndex - outputNumDims;
|
|
|
|
mapUpdate.inputSymbolMap[inputSymbolPosition] = outputSymbolPosition;
|
|
|
|
} else {
|
|
|
|
unsigned outputSymbolPosition = outputOperandPosition - outputNumDims;
|
|
|
|
mapUpdate.inputSymbolMap[inputSymbolPosition] = outputSymbolPosition;
|
|
|
|
outputOperands.push_back(inputOperand);
|
|
|
|
++outputOperandPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set output number of dimension and symbol operands.
|
|
|
|
mapUpdate.outputNumDims = outputNumDims;
|
|
|
|
mapUpdate.outputNumSymbols = outputOperands.size() - outputNumDims;
|
|
|
|
|
|
|
|
// Update 'operands' with new 'outputOperands'.
|
|
|
|
operands.swap(outputOperands);
|
|
|
|
// Forward substitute 'mapUpdate' into 'map'.
|
2018-10-09 12:02:12 +08:00
|
|
|
forwardSubstituteMutableAffineMap(mapUpdate, &map);
|
2018-08-22 01:32:24 +08:00
|
|
|
}
|
|
|
|
|
2018-08-31 08:35:15 +08:00
|
|
|
inline bool AffineValueMap::isMultipleOf(unsigned idx, int64_t factor) const {
|
|
|
|
return map.isMultipleOf(idx, factor);
|
2018-08-22 01:32:24 +08:00
|
|
|
}
|
|
|
|
|
2018-10-18 09:01:44 +08:00
|
|
|
/// This method uses the invariant that operands are always positionally aligned
|
|
|
|
/// with the AffineDimExpr in the underlying AffineMap.
|
|
|
|
bool AffineValueMap::isFunctionOf(unsigned idx, MLValue *value) const {
|
|
|
|
unsigned index;
|
|
|
|
findIndex(value, operands, &index);
|
|
|
|
auto expr = const_cast<AffineValueMap *>(this)->getAffineMap().getResult(idx);
|
|
|
|
// TODO(ntv): this is better implemented on a flattened representation.
|
|
|
|
// At least for now it is conservative.
|
|
|
|
return expr.isFunctionOfDim(index);
|
|
|
|
}
|
|
|
|
|
2018-10-09 02:10:11 +08:00
|
|
|
SSAValue *AffineValueMap::getOperand(unsigned i) const {
|
|
|
|
return static_cast<SSAValue *>(operands[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef<MLValue *> AffineValueMap::getOperands() const {
|
|
|
|
return ArrayRef<MLValue *>(operands);
|
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
AffineMap AffineValueMap::getAffineMap() const { return map.getAffineMap(); }
|
2018-10-09 02:10:11 +08:00
|
|
|
|
2018-08-22 01:32:24 +08:00
|
|
|
AffineValueMap::~AffineValueMap() {}
|
|
|
|
|
2018-10-25 23:33:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// FlatAffineConstraints.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
// Copy constructor.
|
|
|
|
FlatAffineConstraints::FlatAffineConstraints(
|
|
|
|
const FlatAffineConstraints &other) {
|
2018-10-31 04:45:10 +08:00
|
|
|
numReservedCols = other.numReservedCols;
|
2018-10-25 23:33:02 +08:00
|
|
|
numDims = other.getNumDimIds();
|
|
|
|
numSymbols = other.getNumSymbolIds();
|
2018-10-31 04:45:10 +08:00
|
|
|
numIds = other.getNumIds();
|
2018-10-25 23:33:02 +08:00
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
auto otherIds = other.getIds();
|
|
|
|
ids.reserve(numReservedCols);
|
|
|
|
ids.insert(ids.end(), otherIds.begin(), otherIds.end());
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
unsigned numReservedEqualities = other.getNumReservedEqualities();
|
|
|
|
unsigned numReservedInequalities = other.getNumReservedInequalities();
|
|
|
|
|
|
|
|
equalities.reserve(numReservedEqualities * numReservedCols);
|
|
|
|
inequalities.reserve(numReservedInequalities * numReservedCols);
|
2018-10-25 23:33:02 +08:00
|
|
|
|
|
|
|
for (unsigned r = 0, e = other.getNumInequalities(); r < e; r++) {
|
|
|
|
addInequality(other.getInequality(r));
|
|
|
|
}
|
|
|
|
for (unsigned r = 0, e = other.getNumEqualities(); r < e; r++) {
|
|
|
|
addEquality(other.getEquality(r));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clones this object.
|
|
|
|
std::unique_ptr<FlatAffineConstraints> FlatAffineConstraints::clone() const {
|
|
|
|
return std::make_unique<FlatAffineConstraints>(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct from an IntegerSet.
|
2018-10-25 02:30:06 +08:00
|
|
|
FlatAffineConstraints::FlatAffineConstraints(IntegerSet set)
|
2018-10-31 04:45:10 +08:00
|
|
|
: numReservedCols(set.getNumOperands() + 1),
|
2018-10-25 02:30:06 +08:00
|
|
|
numIds(set.getNumDims() + set.getNumSymbols()), numDims(set.getNumDims()),
|
|
|
|
numSymbols(set.getNumSymbols()) {
|
2018-10-31 04:45:10 +08:00
|
|
|
equalities.reserve(set.getNumEqualities() * numReservedCols);
|
|
|
|
inequalities.reserve(set.getNumInequalities() * numReservedCols);
|
2018-11-17 12:12:06 +08:00
|
|
|
ids.resize(numIds, None);
|
2018-10-25 23:33:02 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0, e = set.getNumConstraints(); i < e; ++i) {
|
2018-10-25 02:30:06 +08:00
|
|
|
AffineExpr expr = set.getConstraint(i);
|
|
|
|
SmallVector<int64_t, 4> flattenedExpr;
|
|
|
|
getFlattenedAffineExpr(expr, set.getNumDims(), set.getNumSymbols(),
|
|
|
|
&flattenedExpr);
|
|
|
|
assert(flattenedExpr.size() == getNumCols());
|
|
|
|
if (set.getEqFlags()[i]) {
|
|
|
|
addEquality(flattenedExpr);
|
|
|
|
} else {
|
|
|
|
addInequality(flattenedExpr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-31 04:45:10 +08:00
|
|
|
|
Introduce memref bound checking.
Introduce analysis to check memref accesses (in MLFunctions) for out of bound
ones. It works as follows:
$ mlir-opt -memref-bound-check test/Transforms/memref-bound-check.mlir
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
#map0 = (d0, d1) -> (d0, d1)
#map1 = (d0, d1) -> (d0 * 128 - d1)
mlfunc @test() {
%0 = alloc() : memref<9x9xi32>
%1 = alloc() : memref<128xi32>
for %i0 = -1 to 9 {
for %i1 = -1 to 9 {
%2 = affine_apply #map0(%i0, %i1)
%3 = load %0[%2tensorflow/mlir#0, %2tensorflow/mlir#1] : memref<9x9xi32>
%4 = affine_apply #map1(%i0, %i1)
%5 = load %1[%4] : memref<128xi32>
}
}
return
}
- Improves productivity while manually / semi-automatically developing MLIR for
testing / prototyping; also provides an indirect way to catch errors in
transformations.
- This pass is an easy way to test the underlying affine analysis
machinery including low level routines.
Some code (in getMemoryRegion()) borrowed from @andydavis cl/218263256.
While on this:
- create mlir/Analysis/Passes.h; move Pass.h up from mlir/Transforms/ to mlir/
- fix a bug in AffineAnalysis.cpp::toAffineExpr
TODO: extend to non-constant loop bounds (straightforward). Will transparently
work for all accesses once floordiv, mod, ceildiv are supported in the
AffineMap -> FlatAffineConstraints conversion.
PiperOrigin-RevId: 219397961
2018-10-31 08:43:06 +08:00
|
|
|
void FlatAffineConstraints::reset(unsigned numReservedInequalities,
|
|
|
|
unsigned numReservedEqualities,
|
|
|
|
unsigned newNumReservedCols,
|
|
|
|
unsigned newNumDims, unsigned newNumSymbols,
|
2018-11-17 12:12:06 +08:00
|
|
|
unsigned newNumLocals,
|
|
|
|
ArrayRef<MLValue *> idArgs) {
|
2018-11-02 06:41:08 +08:00
|
|
|
assert(newNumReservedCols >= newNumDims + newNumSymbols + newNumLocals + 1 &&
|
|
|
|
"minimum 1 column");
|
Introduce memref bound checking.
Introduce analysis to check memref accesses (in MLFunctions) for out of bound
ones. It works as follows:
$ mlir-opt -memref-bound-check test/Transforms/memref-bound-check.mlir
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
#map0 = (d0, d1) -> (d0, d1)
#map1 = (d0, d1) -> (d0 * 128 - d1)
mlfunc @test() {
%0 = alloc() : memref<9x9xi32>
%1 = alloc() : memref<128xi32>
for %i0 = -1 to 9 {
for %i1 = -1 to 9 {
%2 = affine_apply #map0(%i0, %i1)
%3 = load %0[%2tensorflow/mlir#0, %2tensorflow/mlir#1] : memref<9x9xi32>
%4 = affine_apply #map1(%i0, %i1)
%5 = load %1[%4] : memref<128xi32>
}
}
return
}
- Improves productivity while manually / semi-automatically developing MLIR for
testing / prototyping; also provides an indirect way to catch errors in
transformations.
- This pass is an easy way to test the underlying affine analysis
machinery including low level routines.
Some code (in getMemoryRegion()) borrowed from @andydavis cl/218263256.
While on this:
- create mlir/Analysis/Passes.h; move Pass.h up from mlir/Transforms/ to mlir/
- fix a bug in AffineAnalysis.cpp::toAffineExpr
TODO: extend to non-constant loop bounds (straightforward). Will transparently
work for all accesses once floordiv, mod, ceildiv are supported in the
AffineMap -> FlatAffineConstraints conversion.
PiperOrigin-RevId: 219397961
2018-10-31 08:43:06 +08:00
|
|
|
numReservedCols = newNumReservedCols;
|
|
|
|
numDims = newNumDims;
|
|
|
|
numSymbols = newNumSymbols;
|
|
|
|
numIds = numDims + numSymbols + newNumLocals;
|
|
|
|
equalities.clear();
|
|
|
|
inequalities.clear();
|
2018-11-02 06:41:08 +08:00
|
|
|
if (numReservedEqualities >= 1)
|
|
|
|
equalities.reserve(newNumReservedCols * numReservedEqualities);
|
|
|
|
if (numReservedInequalities >= 1)
|
|
|
|
inequalities.reserve(newNumReservedCols * numReservedInequalities);
|
2018-11-17 12:12:06 +08:00
|
|
|
ids.clear();
|
|
|
|
if (idArgs.empty()) {
|
|
|
|
ids.resize(numIds, None);
|
|
|
|
} else {
|
|
|
|
ids.reserve(idArgs.size());
|
|
|
|
ids.insert(ids.end(), idArgs.begin(), idArgs.end());
|
|
|
|
}
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::reset(unsigned newNumDims, unsigned newNumSymbols,
|
2018-11-17 12:12:06 +08:00
|
|
|
unsigned newNumLocals,
|
|
|
|
ArrayRef<MLValue *> idArgs) {
|
2018-11-02 06:41:08 +08:00
|
|
|
reset(0, 0, newNumDims + newNumSymbols + newNumLocals + 1, newNumDims,
|
2018-11-17 12:12:06 +08:00
|
|
|
newNumSymbols, newNumLocals, idArgs);
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::append(const FlatAffineConstraints &other) {
|
|
|
|
assert(other.getNumCols() == getNumCols());
|
|
|
|
assert(other.getNumDimIds() == getNumDimIds());
|
|
|
|
|
|
|
|
inequalities.reserve(inequalities.size() +
|
|
|
|
other.getNumInequalities() * numReservedCols);
|
|
|
|
equalities.reserve(equalities.size() +
|
|
|
|
other.getNumEqualities() * numReservedCols);
|
|
|
|
|
|
|
|
for (unsigned r = 0, e = other.getNumInequalities(); r < e; r++) {
|
|
|
|
addInequality(other.getInequality(r));
|
|
|
|
}
|
|
|
|
for (unsigned r = 0, e = other.getNumEqualities(); r < e; r++) {
|
|
|
|
addEquality(other.getEquality(r));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addLocalId(unsigned pos) {
|
|
|
|
addId(IdKind::Local, pos);
|
|
|
|
}
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
void FlatAffineConstraints::addDimId(unsigned pos, MLValue *id) {
|
|
|
|
addId(IdKind::Dimension, pos, id);
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addSymbolId(unsigned pos) {
|
|
|
|
addId(IdKind::Symbol, pos);
|
Introduce memref bound checking.
Introduce analysis to check memref accesses (in MLFunctions) for out of bound
ones. It works as follows:
$ mlir-opt -memref-bound-check test/Transforms/memref-bound-check.mlir
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:10:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#2
%x = load %A[%idxtensorflow/mlir#0, %idxtensorflow/mlir#1] : memref<9 x 9 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of upper bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
/tmp/single.mlir:12:12: error: 'load' op memref out of lower bound access along dimension tensorflow/mlir#1
%y = load %B[%idy] : memref<128 x i32>
^
#map0 = (d0, d1) -> (d0, d1)
#map1 = (d0, d1) -> (d0 * 128 - d1)
mlfunc @test() {
%0 = alloc() : memref<9x9xi32>
%1 = alloc() : memref<128xi32>
for %i0 = -1 to 9 {
for %i1 = -1 to 9 {
%2 = affine_apply #map0(%i0, %i1)
%3 = load %0[%2tensorflow/mlir#0, %2tensorflow/mlir#1] : memref<9x9xi32>
%4 = affine_apply #map1(%i0, %i1)
%5 = load %1[%4] : memref<128xi32>
}
}
return
}
- Improves productivity while manually / semi-automatically developing MLIR for
testing / prototyping; also provides an indirect way to catch errors in
transformations.
- This pass is an easy way to test the underlying affine analysis
machinery including low level routines.
Some code (in getMemoryRegion()) borrowed from @andydavis cl/218263256.
While on this:
- create mlir/Analysis/Passes.h; move Pass.h up from mlir/Transforms/ to mlir/
- fix a bug in AffineAnalysis.cpp::toAffineExpr
TODO: extend to non-constant loop bounds (straightforward). Will transparently
work for all accesses once floordiv, mod, ceildiv are supported in the
AffineMap -> FlatAffineConstraints conversion.
PiperOrigin-RevId: 219397961
2018-10-31 08:43:06 +08:00
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
/// Adds a dimensional identifier. The added column is initialized to
|
|
|
|
/// zero.
|
2018-11-17 12:12:06 +08:00
|
|
|
void FlatAffineConstraints::addId(IdKind kind, unsigned pos, MLValue *id) {
|
2018-11-02 06:41:08 +08:00
|
|
|
if (kind == IdKind::Dimension) {
|
|
|
|
assert(pos <= getNumDimIds());
|
|
|
|
} else if (kind == IdKind::Symbol) {
|
|
|
|
assert(pos <= getNumSymbolIds());
|
|
|
|
} else {
|
|
|
|
assert(pos <= getNumLocalIds());
|
|
|
|
}
|
2018-10-31 04:45:10 +08:00
|
|
|
|
|
|
|
unsigned oldNumReservedCols = numReservedCols;
|
|
|
|
|
|
|
|
// Check if a resize is necessary.
|
2018-11-02 06:41:08 +08:00
|
|
|
if (getNumCols() + 1 > numReservedCols) {
|
2018-10-31 04:45:10 +08:00
|
|
|
equalities.resize(getNumEqualities() * (getNumCols() + 1));
|
|
|
|
inequalities.resize(getNumInequalities() * (getNumCols() + 1));
|
|
|
|
numReservedCols++;
|
|
|
|
}
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
unsigned absolutePos;
|
2018-11-02 06:41:08 +08:00
|
|
|
|
|
|
|
if (kind == IdKind::Dimension) {
|
2018-11-17 12:12:06 +08:00
|
|
|
absolutePos = pos;
|
2018-11-02 06:41:08 +08:00
|
|
|
numDims++;
|
|
|
|
} else if (kind == IdKind::Symbol) {
|
2018-11-17 12:12:06 +08:00
|
|
|
absolutePos = pos + getNumDimIds();
|
2018-11-02 06:41:08 +08:00
|
|
|
numSymbols++;
|
|
|
|
} else {
|
2018-11-17 12:12:06 +08:00
|
|
|
absolutePos = pos + getNumDimIds() + getNumSymbolIds();
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
2018-10-31 04:45:10 +08:00
|
|
|
numIds++;
|
|
|
|
|
|
|
|
// Note that getNumCols() now will already return the new size, which will be
|
|
|
|
// at least one.
|
|
|
|
int numInequalities = static_cast<int>(getNumInequalities());
|
|
|
|
int numEqualities = static_cast<int>(getNumEqualities());
|
|
|
|
int numCols = static_cast<int>(getNumCols());
|
|
|
|
for (int r = numInequalities - 1; r >= 0; r--) {
|
|
|
|
for (int c = numCols - 2; c >= 0; c--) {
|
2018-11-17 12:12:06 +08:00
|
|
|
if (c < absolutePos)
|
2018-10-31 04:45:10 +08:00
|
|
|
atIneq(r, c) = inequalities[r * oldNumReservedCols + c];
|
|
|
|
else
|
|
|
|
atIneq(r, c + 1) = inequalities[r * oldNumReservedCols + c];
|
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
atIneq(r, absolutePos) = 0;
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int r = numEqualities - 1; r >= 0; r--) {
|
|
|
|
for (int c = numCols - 2; c >= 0; c--) {
|
2018-11-17 12:12:06 +08:00
|
|
|
// All values in column absolutePositions < absolutePos have the same
|
|
|
|
// coordinates in the 2-d view of the coefficient buffer.
|
|
|
|
if (c < absolutePos)
|
2018-10-31 04:45:10 +08:00
|
|
|
atEq(r, c) = equalities[r * oldNumReservedCols + c];
|
|
|
|
else
|
2018-11-17 12:12:06 +08:00
|
|
|
// Those at absolutePosition >= absolutePos, get a shifted
|
|
|
|
// absolutePosition.
|
2018-10-31 04:45:10 +08:00
|
|
|
atEq(r, c + 1) = equalities[r * oldNumReservedCols + c];
|
|
|
|
}
|
|
|
|
// Initialize added dimension to zero.
|
2018-11-17 12:12:06 +08:00
|
|
|
atEq(r, absolutePos) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If an 'id' is provided, insert it; otherwise use None.
|
|
|
|
if (id) {
|
|
|
|
ids.insert(ids.begin() + absolutePos, id);
|
|
|
|
} else {
|
|
|
|
ids.insert(ids.begin() + absolutePos, None);
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
assert(ids.size() == getNumIds());
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
// This routine may add additional local variables if the flattened
|
2018-10-31 04:45:10 +08:00
|
|
|
// expression corresponding to the map has such variables due to the presence of
|
|
|
|
// mod's, ceildiv's, and floordiv's.
|
2018-12-04 03:20:10 +08:00
|
|
|
bool FlatAffineConstraints::composeMap(AffineValueMap *vMap, unsigned pos) {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos <= getNumIds() && "invalid position");
|
2018-11-17 12:12:06 +08:00
|
|
|
assert(vMap->getNumSymbols() == getNumSymbolIds());
|
2018-10-31 04:45:10 +08:00
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
AffineMap map = vMap->getAffineMap();
|
|
|
|
|
|
|
|
// We add one equality for each result connecting the result dim of the map to
|
|
|
|
// the other identifiers.
|
2018-11-17 12:12:06 +08:00
|
|
|
// For eg: if the expression is 16*i0 + i1, and this is the r^th
|
|
|
|
// iteration/result of the value map, we are adding the equality:
|
|
|
|
// d_r - 16*i0 - i1 = 0. Hence, when flattening say (i0 + 1, i0 + 8*i2), we
|
|
|
|
// add two equalities overall: d_0 - i0 - 1 == 0, d1 - i0 - 8*i2 == 0.
|
2018-10-31 04:45:10 +08:00
|
|
|
for (unsigned r = 0, e = map.getNumResults(); r < e; r++) {
|
|
|
|
// Add dimension.
|
|
|
|
addDimId(pos + r);
|
|
|
|
SmallVector<int64_t, 4> eq;
|
|
|
|
eq.reserve(getNumCols());
|
2018-11-02 06:41:08 +08:00
|
|
|
FlatAffineConstraints cst;
|
2018-10-31 04:45:10 +08:00
|
|
|
bool ret = getFlattenedAffineExpr(map.getResult(r), map.getNumDims(),
|
2018-11-02 06:41:08 +08:00
|
|
|
map.getNumSymbols(), &eq, &cst);
|
2018-12-04 03:20:10 +08:00
|
|
|
if (!ret) {
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
<< "composition unimplemented for semi-affine maps");
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-02 06:41:08 +08:00
|
|
|
// Make the value map and the flat affine cst dimensions compatible.
|
|
|
|
// A lot of this code will be refactored/cleaned up.
|
|
|
|
for (unsigned l = 0, e = cst.getNumLocalIds(); l < e; l++) {
|
2018-11-17 12:12:06 +08:00
|
|
|
addLocalId(0);
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
2018-11-22 03:12:05 +08:00
|
|
|
// TODO(bondhugula): the next ~20 lines of code is pretty UGLY. This needs
|
|
|
|
// to be factored out into an FlatAffineConstraints::alignAndMerge().
|
2018-11-17 12:12:06 +08:00
|
|
|
for (unsigned t = 0, e = r + 1; t < e; t++) {
|
|
|
|
// TODO: Consider using a batched version to add a range of IDs.
|
2018-11-02 06:41:08 +08:00
|
|
|
cst.addDimId(0);
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
|
|
|
|
assert(cst.getNumDimIds() <= getNumDimIds());
|
|
|
|
for (unsigned t = 0, e = getNumDimIds() - cst.getNumDimIds(); t < e; t++) {
|
2018-11-22 03:12:05 +08:00
|
|
|
// Dimensions that are in 'this' but not in vMap/cst are added at the end.
|
|
|
|
cst.addDimId(cst.getNumDimIds());
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
assert(cst.getNumLocalIds() <= getNumLocalIds());
|
2018-11-02 06:41:08 +08:00
|
|
|
for (unsigned t = 0, e = getNumLocalIds() - cst.getNumLocalIds(); t < e;
|
|
|
|
t++) {
|
2018-11-17 12:12:06 +08:00
|
|
|
cst.addLocalId(cst.getNumLocalIds());
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
|
|
|
/// Finally, append cst to this constraint set.
|
|
|
|
append(cst);
|
2018-11-17 12:12:06 +08:00
|
|
|
|
|
|
|
// eqToAdd is the equality corresponding to the flattened affine expression.
|
|
|
|
SmallVector<int64_t, 8> eqToAdd(getNumCols(), 0);
|
|
|
|
// Set the coefficient for this result to one.
|
|
|
|
eqToAdd[r] = 1;
|
|
|
|
|
|
|
|
// Dims and symbols.
|
|
|
|
for (unsigned i = 0, e = vMap->getNumOperands(); i < e; i++) {
|
|
|
|
unsigned loc;
|
|
|
|
bool ret = findId(*cast<MLValue>(vMap->getOperand(i)), &loc);
|
|
|
|
assert(ret && "id expected, but not found");
|
|
|
|
(void)ret;
|
|
|
|
// We need to negate 'eq' since the newly added dimension is going to be
|
|
|
|
// set to this one.
|
|
|
|
eqToAdd[loc] = -eq[i];
|
|
|
|
}
|
|
|
|
// Local vars common to eq and cst are at the beginning.
|
|
|
|
int j = getNumDimIds() + getNumSymbolIds();
|
|
|
|
int end = eq.size() - 1;
|
|
|
|
for (int i = vMap->getNumOperands(); i < end; i++, j++) {
|
|
|
|
eqToAdd[j] = -eq[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constant term.
|
|
|
|
eqToAdd[getNumCols() - 1] = -eq[eq.size() - 1];
|
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
// Add the equality connecting the result of the map to this constraint set.
|
2018-11-17 12:12:06 +08:00
|
|
|
addEquality(eqToAdd);
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
2018-12-04 03:20:10 +08:00
|
|
|
return true;
|
2018-10-31 04:45:10 +08:00
|
|
|
}
|
|
|
|
|
2018-10-25 02:30:06 +08:00
|
|
|
// Searches for a constraint with a non-zero coefficient at 'colIdx' in
|
|
|
|
// equality (isEq=true) or inequality (isEq=false) constraints.
|
|
|
|
// Returns true and sets row found in search in 'rowIdx'.
|
|
|
|
// Returns false otherwise.
|
|
|
|
static bool
|
|
|
|
findConstraintWithNonZeroAt(const FlatAffineConstraints &constraints,
|
|
|
|
unsigned colIdx, bool isEq, unsigned &rowIdx) {
|
|
|
|
auto at = [&](unsigned rowIdx) -> int64_t {
|
|
|
|
return isEq ? constraints.atEq(rowIdx, colIdx)
|
|
|
|
: constraints.atIneq(rowIdx, colIdx);
|
|
|
|
};
|
|
|
|
unsigned e =
|
|
|
|
isEq ? constraints.getNumEqualities() : constraints.getNumInequalities();
|
|
|
|
for (rowIdx = 0; rowIdx < e; ++rowIdx) {
|
|
|
|
if (at(rowIdx) != 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalizes the coefficient values across all columns in 'rowIDx' by their
|
|
|
|
// GCD in equality or inequality contraints as specified by 'isEq'.
|
|
|
|
static void normalizeConstraintByGCD(FlatAffineConstraints *constraints,
|
|
|
|
unsigned rowIdx, bool isEq) {
|
|
|
|
auto at = [&](unsigned colIdx) -> int64_t {
|
|
|
|
return isEq ? constraints->atEq(rowIdx, colIdx)
|
|
|
|
: constraints->atIneq(rowIdx, colIdx);
|
|
|
|
};
|
|
|
|
uint64_t gcd = std::abs(at(0));
|
|
|
|
for (unsigned j = 1; j < constraints->getNumCols(); ++j) {
|
|
|
|
gcd = llvm::GreatestCommonDivisor64(gcd, std::abs(at(j)));
|
|
|
|
}
|
|
|
|
if (gcd > 0 && gcd != 1) {
|
|
|
|
for (unsigned j = 0; j < constraints->getNumCols(); ++j) {
|
|
|
|
int64_t v = at(j) / static_cast<int64_t>(gcd);
|
|
|
|
isEq ? constraints->atEq(rowIdx, j) = v
|
|
|
|
: constraints->atIneq(rowIdx, j) = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks all rows of equality/inequality constraints for contradictions
|
|
|
|
// (i.e. 1 == 0), which may have surfaced after elimination.
|
|
|
|
// Returns 'true' if a valid constraint is detected. Returns 'false' otherwise.
|
|
|
|
static bool hasInvalidConstraint(const FlatAffineConstraints &constraints) {
|
2018-10-31 04:45:10 +08:00
|
|
|
auto check = [&](bool isEq) -> bool {
|
2018-10-25 02:30:06 +08:00
|
|
|
unsigned numCols = constraints.getNumCols();
|
|
|
|
unsigned numRows = isEq ? constraints.getNumEqualities()
|
|
|
|
: constraints.getNumInequalities();
|
|
|
|
for (unsigned i = 0, e = numRows; i < e; ++i) {
|
|
|
|
unsigned j;
|
|
|
|
for (j = 0; j < numCols - 1; ++j) {
|
|
|
|
int64_t v = isEq ? constraints.atEq(i, j) : constraints.atIneq(i, j);
|
|
|
|
// Skip rows with non-zero variable coefficients.
|
|
|
|
if (v != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j < numCols - 1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Check validity of constant term at 'numCols - 1' w.r.t 'isEq'.
|
|
|
|
// Example invalid constraints include: '1 == 0' or '-1 >= 0'
|
|
|
|
int64_t v = isEq ? constraints.atEq(i, numCols - 1)
|
|
|
|
: constraints.atIneq(i, numCols - 1);
|
|
|
|
if ((isEq && v != 0) || (!isEq && v < 0)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
if (check(/*isEq=*/true))
|
|
|
|
return true;
|
|
|
|
return check(/*isEq=*/false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eliminate identifier from constraint at 'rowIdx' based on coefficient at
|
|
|
|
// pivotRow, pivotCol. Columns in range [elimColStart, pivotCol) will not be
|
|
|
|
// updated as they have already been eliminated.
|
|
|
|
static void eliminateFromConstraint(FlatAffineConstraints *constraints,
|
|
|
|
unsigned rowIdx, unsigned pivotRow,
|
|
|
|
unsigned pivotCol, unsigned elimColStart,
|
|
|
|
bool isEq) {
|
|
|
|
// Skip if equality 'rowIdx' if same as 'pivotRow'.
|
|
|
|
if (isEq && rowIdx == pivotRow)
|
|
|
|
return;
|
|
|
|
auto at = [&](unsigned i, unsigned j) -> int64_t {
|
|
|
|
return isEq ? constraints->atEq(i, j) : constraints->atIneq(i, j);
|
|
|
|
};
|
|
|
|
int64_t leadCoeff = at(rowIdx, pivotCol);
|
|
|
|
// Skip if leading coefficient at 'rowIdx' is already zero.
|
|
|
|
if (leadCoeff == 0)
|
|
|
|
return;
|
|
|
|
int64_t pivotCoeff = constraints->atEq(pivotRow, pivotCol);
|
|
|
|
int64_t sign = (leadCoeff * pivotCoeff > 0) ? -1 : 1;
|
|
|
|
int64_t lcm = mlir::lcm(pivotCoeff, leadCoeff);
|
|
|
|
int64_t pivotMultiplier = sign * (lcm / std::abs(pivotCoeff));
|
|
|
|
int64_t rowMultiplier = lcm / std::abs(leadCoeff);
|
|
|
|
|
|
|
|
unsigned numCols = constraints->getNumCols();
|
|
|
|
for (unsigned j = 0; j < numCols; ++j) {
|
|
|
|
// Skip updating column 'j' if it was just eliminated.
|
|
|
|
if (j >= elimColStart && j < pivotCol)
|
|
|
|
continue;
|
|
|
|
int64_t v = pivotMultiplier * constraints->atEq(pivotRow, j) +
|
|
|
|
rowMultiplier * at(rowIdx, j);
|
|
|
|
isEq ? constraints->atEq(rowIdx, j) = v
|
|
|
|
: constraints->atIneq(rowIdx, j) = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove coefficients in column range [colStart, colLimit) in place.
|
|
|
|
// This removes in data in the specified column range, and copies any
|
|
|
|
// remaining valid data into place.
|
2018-10-31 04:45:10 +08:00
|
|
|
static void shiftColumnsToLeft(FlatAffineConstraints *constraints,
|
|
|
|
unsigned colStart, unsigned colLimit,
|
|
|
|
bool isEq) {
|
|
|
|
assert(colStart >= 0 && colLimit <= constraints->getNumIds());
|
|
|
|
if (colLimit <= colStart)
|
|
|
|
return;
|
|
|
|
|
2018-10-25 02:30:06 +08:00
|
|
|
unsigned numCols = constraints->getNumCols();
|
|
|
|
unsigned numRows = isEq ? constraints->getNumEqualities()
|
|
|
|
: constraints->getNumInequalities();
|
2018-10-31 04:45:10 +08:00
|
|
|
unsigned numToEliminate = colLimit - colStart;
|
|
|
|
for (unsigned r = 0, e = numRows; r < e; ++r) {
|
|
|
|
for (unsigned c = colLimit; c < numCols; ++c) {
|
2018-10-25 02:30:06 +08:00
|
|
|
if (isEq) {
|
2018-10-31 04:45:10 +08:00
|
|
|
constraints->atEq(r, c - numToEliminate) = constraints->atEq(r, c);
|
2018-10-25 02:30:06 +08:00
|
|
|
} else {
|
2018-10-31 04:45:10 +08:00
|
|
|
constraints->atIneq(r, c - numToEliminate) = constraints->atIneq(r, c);
|
2018-10-25 02:30:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
// Removes coefficients in column range [colStart, colLimit), and copies any
|
|
|
|
// remaining valid data into place, and updates member variables.
|
2018-10-25 02:30:06 +08:00
|
|
|
void FlatAffineConstraints::removeColumnRange(unsigned colStart,
|
|
|
|
unsigned colLimit) {
|
2018-10-31 04:45:10 +08:00
|
|
|
assert(colStart >= 0 && colLimit <= getNumCols());
|
2018-10-25 02:30:06 +08:00
|
|
|
// TODO(andydavis) Make 'removeColumns' a lambda called from here.
|
|
|
|
// Remove eliminated columns from equalities.
|
2018-10-31 04:45:10 +08:00
|
|
|
shiftColumnsToLeft(this, colStart, colLimit, /*isEq=*/true);
|
2018-10-25 02:30:06 +08:00
|
|
|
// Remove eliminated columns from inequalities.
|
2018-10-31 04:45:10 +08:00
|
|
|
shiftColumnsToLeft(this, colStart, colLimit, /*isEq=*/false);
|
2018-10-25 02:30:06 +08:00
|
|
|
// Update members numDims, numSymbols and numIds.
|
|
|
|
unsigned numDimsEliminated = 0;
|
|
|
|
if (colStart < numDims) {
|
|
|
|
numDimsEliminated = std::min(numDims, colLimit) - colStart;
|
|
|
|
}
|
|
|
|
unsigned numColsEliminated = colLimit - colStart;
|
|
|
|
unsigned numSymbolsEliminated =
|
|
|
|
std::min(numSymbols, numColsEliminated - numDimsEliminated);
|
|
|
|
numDims -= numDimsEliminated;
|
|
|
|
numSymbols -= numSymbolsEliminated;
|
|
|
|
numIds = numIds - numColsEliminated;
|
2018-11-17 12:12:06 +08:00
|
|
|
ids.erase(ids.begin() + colStart, ids.begin() + colLimit);
|
2018-10-31 04:45:10 +08:00
|
|
|
|
|
|
|
// No resize necessary. numReservedCols remains the same.
|
2018-10-25 02:30:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Performs variable elimination on all identifiers, runs the GCD test on
|
|
|
|
// all equality constraint rows, and checks the constraint validity.
|
|
|
|
// Returns 'true' if the GCD test fails on any row, or if any invalid
|
|
|
|
// constraint is detected. Returns 'false' otherwise.
|
2018-10-25 23:33:02 +08:00
|
|
|
bool FlatAffineConstraints::isEmpty() const {
|
2018-10-26 07:32:53 +08:00
|
|
|
if (isEmptyByGCDTest())
|
|
|
|
return true;
|
2018-10-25 23:33:02 +08:00
|
|
|
auto tmpCst = clone();
|
|
|
|
if (tmpCst->gaussianEliminateIds(0, numIds) < numIds) {
|
|
|
|
for (unsigned i = 0, e = tmpCst->getNumIds(); i < e; i++)
|
2018-11-02 06:41:08 +08:00
|
|
|
tmpCst->FourierMotzkinEliminate(0);
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
|
|
|
if (hasInvalidConstraint(*tmpCst))
|
2018-10-25 02:30:06 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-26 07:32:53 +08:00
|
|
|
// Runs the GCD test on all equality constraints. Returns 'true' if this test
|
|
|
|
// fails on any equality. Returns 'false' otherwise.
|
|
|
|
// This test can be used to disprove the existence of a solution. If it returns
|
|
|
|
// true, no integer solution to the equality constraints can exist.
|
|
|
|
//
|
|
|
|
// GCD test definition:
|
|
|
|
//
|
|
|
|
// The equality constraint:
|
|
|
|
//
|
|
|
|
// c_1*x_1 + c_2*x_2 + ... + c_n*x_n = c_0
|
|
|
|
//
|
|
|
|
// has an integer solution iff:
|
|
|
|
//
|
|
|
|
// GCD of c_1, c_2, ..., c_n divides c_0.
|
|
|
|
//
|
|
|
|
bool FlatAffineConstraints::isEmptyByGCDTest() const {
|
|
|
|
unsigned numCols = getNumCols();
|
|
|
|
for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) {
|
|
|
|
uint64_t gcd = std::abs(atEq(i, 0));
|
|
|
|
for (unsigned j = 1; j < numCols - 1; ++j) {
|
|
|
|
gcd = llvm::GreatestCommonDivisor64(gcd, std::abs(atEq(i, j)));
|
|
|
|
}
|
|
|
|
int64_t v = std::abs(atEq(i, numCols - 1));
|
|
|
|
if (gcd > 0 && (v % gcd != 0)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
/// Tightens inequalities given that we are dealing with integer spaces. This is
|
|
|
|
/// similar to the GCD test but applied to inequalities. The constant term can
|
|
|
|
/// be reduced to the preceding multiple of the GCD of the coefficients, i.e.,
|
|
|
|
/// 64*i - 100 >= 0 => 64*i - 128 >= 0 (since 'i' is an integer). This is a
|
|
|
|
/// fast method - linear in the number of coefficients.
|
|
|
|
// Example on how this affects practical cases: consider the scenario:
|
|
|
|
// 64*i >= 100, j = 64*i; without a tightening, elimination of i would yield
|
|
|
|
// j >= 100 instead of the tighter (exact) j >= 128.
|
|
|
|
void FlatAffineConstraints::GCDTightenInequalities() {
|
|
|
|
unsigned numCols = getNumCols();
|
|
|
|
for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) {
|
|
|
|
uint64_t gcd = std::abs(atIneq(i, 0));
|
|
|
|
for (unsigned j = 1; j < numCols - 1; ++j) {
|
|
|
|
gcd = llvm::GreatestCommonDivisor64(gcd, std::abs(atIneq(i, j)));
|
|
|
|
}
|
|
|
|
if (gcd > 0) {
|
|
|
|
atIneq(i, numCols - 1) =
|
|
|
|
gcd * mlir::floorDiv(atIneq(i, numCols - 1), gcd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 02:30:06 +08:00
|
|
|
// Eliminates a single identifier at 'position' from equality and inequality
|
|
|
|
// constraints. Returns 'true' if the identifier was eliminated.
|
|
|
|
// Returns 'false' otherwise.
|
2018-10-25 23:33:02 +08:00
|
|
|
bool FlatAffineConstraints::gaussianEliminateId(unsigned position) {
|
|
|
|
return gaussianEliminateIds(position, position + 1) == 1;
|
2018-10-25 02:30:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Eliminates all identifer variables in column range [posStart, posLimit).
|
|
|
|
// Returns the number of variables eliminated.
|
2018-10-25 23:33:02 +08:00
|
|
|
unsigned FlatAffineConstraints::gaussianEliminateIds(unsigned posStart,
|
2018-10-25 02:30:06 +08:00
|
|
|
unsigned posLimit) {
|
|
|
|
// Return if identifier positions to eliminate are out of range.
|
2018-10-31 04:45:10 +08:00
|
|
|
assert(posStart >= 0 && posLimit <= numIds);
|
|
|
|
|
|
|
|
if (posStart >= posLimit)
|
2018-10-25 02:30:06 +08:00
|
|
|
return 0;
|
2018-10-31 04:45:10 +08:00
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
GCDTightenInequalities();
|
|
|
|
|
2018-10-25 02:30:06 +08:00
|
|
|
unsigned pivotCol = 0;
|
|
|
|
for (pivotCol = posStart; pivotCol < posLimit; ++pivotCol) {
|
|
|
|
// Find a row which has a non-zero coefficient in column 'j'.
|
|
|
|
unsigned pivotRow;
|
|
|
|
if (!findConstraintWithNonZeroAt(*this, pivotCol, /*isEq=*/true,
|
|
|
|
pivotRow)) {
|
|
|
|
// No pivot row in equalities with non-zero at 'pivotCol'.
|
|
|
|
if (!findConstraintWithNonZeroAt(*this, pivotCol, /*isEq=*/false,
|
|
|
|
pivotRow)) {
|
|
|
|
// If inequalities are also non-zero in 'pivotCol' it can be eliminated.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eliminate identifier at 'pivotCol' from each equality row.
|
|
|
|
for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) {
|
|
|
|
eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart,
|
|
|
|
/*isEq=*/true);
|
|
|
|
normalizeConstraintByGCD(this, i, /*isEq=*/true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eliminate identifier at 'pivotCol' from each inequality row.
|
|
|
|
for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) {
|
|
|
|
eliminateFromConstraint(this, i, pivotRow, pivotCol, posStart,
|
|
|
|
/*isEq=*/false);
|
|
|
|
normalizeConstraintByGCD(this, i, /*isEq=*/false);
|
|
|
|
}
|
|
|
|
removeEquality(pivotRow);
|
|
|
|
}
|
|
|
|
// Update position limit based on number eliminated.
|
|
|
|
posLimit = pivotCol;
|
|
|
|
// Remove eliminated columns from all constraints.
|
|
|
|
removeColumnRange(posStart, posLimit);
|
|
|
|
return posLimit - posStart;
|
|
|
|
}
|
|
|
|
|
2018-08-31 08:35:15 +08:00
|
|
|
void FlatAffineConstraints::addEquality(ArrayRef<int64_t> eq) {
|
|
|
|
assert(eq.size() == getNumCols());
|
|
|
|
unsigned offset = equalities.size();
|
2018-10-31 04:45:10 +08:00
|
|
|
equalities.resize(equalities.size() + numReservedCols);
|
|
|
|
std::copy(eq.begin(), eq.end(), equalities.begin() + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addInequality(ArrayRef<int64_t> inEq) {
|
|
|
|
assert(inEq.size() == getNumCols());
|
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::copy(inEq.begin(), inEq.end(), inequalities.begin() + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addConstantLowerBound(unsigned pos, int64_t lb) {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos < getNumCols());
|
2018-10-31 04:45:10 +08:00
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
inequalities[offset + pos] = 1;
|
|
|
|
inequalities[offset + getNumCols() - 1] = -lb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addConstantUpperBound(unsigned pos, int64_t ub) {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos < getNumCols());
|
2018-10-31 04:45:10 +08:00
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
inequalities[offset + pos] = -1;
|
|
|
|
inequalities[offset + getNumCols() - 1] = ub;
|
|
|
|
}
|
|
|
|
|
2018-11-02 06:41:08 +08:00
|
|
|
void FlatAffineConstraints::addConstantLowerBound(ArrayRef<int64_t> expr,
|
|
|
|
int64_t lb) {
|
|
|
|
assert(expr.size() == getNumCols());
|
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
std::copy(expr.begin(), expr.end(), inequalities.begin() + offset);
|
|
|
|
inequalities[offset + getNumCols() - 1] += -lb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addConstantUpperBound(ArrayRef<int64_t> expr,
|
|
|
|
int64_t ub) {
|
|
|
|
assert(expr.size() == getNumCols());
|
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
for (unsigned i = 0, e = getNumCols(); i < e; i++) {
|
|
|
|
inequalities[offset + i] = -expr[i];
|
|
|
|
}
|
|
|
|
inequalities[offset + getNumCols() - 1] += ub;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addLowerBound(ArrayRef<int64_t> expr,
|
|
|
|
ArrayRef<int64_t> lb) {
|
|
|
|
assert(expr.size() == getNumCols());
|
|
|
|
assert(lb.size() == getNumCols());
|
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
for (unsigned i = 0, e = getNumCols(); i < e; i++) {
|
|
|
|
inequalities[offset + i] = expr[i] - lb[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::addUpperBound(ArrayRef<int64_t> expr,
|
|
|
|
ArrayRef<int64_t> ub) {
|
|
|
|
assert(expr.size() == getNumCols());
|
|
|
|
assert(ub.size() == getNumCols());
|
|
|
|
unsigned offset = inequalities.size();
|
|
|
|
inequalities.resize(inequalities.size() + numReservedCols);
|
|
|
|
std::fill(inequalities.begin() + offset,
|
|
|
|
inequalities.begin() + offset + getNumCols(), 0);
|
|
|
|
for (unsigned i = 0, e = getNumCols(); i < e; i++) {
|
|
|
|
inequalities[offset + i] = ub[i] - expr[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
bool FlatAffineConstraints::findId(const MLValue &operand, unsigned *pos) {
|
|
|
|
unsigned i = 0;
|
|
|
|
for (const auto &mayBeId : ids) {
|
|
|
|
if (mayBeId.hasValue() && mayBeId.getValue() == &operand) {
|
|
|
|
*pos = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(andydavis, bondhugula) AFFINE REFACTOR: merge with loop bounds
|
|
|
|
// code in dependence analysis.
|
|
|
|
bool FlatAffineConstraints::addBoundsFromForStmt(unsigned pos,
|
|
|
|
ForStmt *forStmt) {
|
|
|
|
// Adds a lower or upper bound when the bounds aren't constant.
|
|
|
|
auto addLowerOrUpperBound = [&](bool lower) -> bool {
|
|
|
|
const auto &operands = lower ? forStmt->getLowerBoundOperands()
|
|
|
|
: forStmt->getUpperBoundOperands();
|
|
|
|
SmallVector<unsigned, 8> positions;
|
|
|
|
|
|
|
|
for (const auto &operand : operands) {
|
|
|
|
unsigned loc;
|
|
|
|
// TODO(andydavis, bondhugula) AFFINE REFACTOR: merge with loop bounds
|
|
|
|
// code in dependence analysis.
|
|
|
|
if (!findId(*operand, &loc)) {
|
|
|
|
addDimId(getNumDimIds(), operand);
|
|
|
|
loc = getNumDimIds() - 1;
|
|
|
|
}
|
|
|
|
positions.push_back(loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto boundMap =
|
|
|
|
lower ? forStmt->getLowerBoundMap() : forStmt->getUpperBoundMap();
|
|
|
|
|
|
|
|
for (auto result : boundMap.getResults()) {
|
|
|
|
SmallVector<int64_t, 4> flattenedExpr;
|
|
|
|
SmallVector<int64_t, 4> ineq(getNumCols(), 0);
|
|
|
|
// TODO(andydavis, bondhugula) AFFINE REFACTOR: merge with loop bounds in
|
|
|
|
// dependence analysis.
|
|
|
|
FlatAffineConstraints cst;
|
|
|
|
if (!getFlattenedAffineExpr(result, boundMap.getNumDims(),
|
|
|
|
boundMap.getNumSymbols(), &flattenedExpr,
|
|
|
|
&cst)) {
|
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
|
|
<< "semi-affine expressions not yet supported\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (cst.getNumLocalIds() > 0) {
|
|
|
|
LLVM_DEBUG(
|
|
|
|
llvm::dbgs()
|
|
|
|
<< "loop bounds with mod/floordiv expr's not yet supported\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ineq[pos] = lower ? 1 : -1;
|
|
|
|
for (unsigned j = 0, e = boundMap.getNumInputs(); j < e; j++) {
|
|
|
|
ineq[positions[j]] = lower ? -flattenedExpr[j] : flattenedExpr[j];
|
|
|
|
}
|
|
|
|
// Constant term.
|
2018-11-22 03:12:05 +08:00
|
|
|
ineq[getNumCols() - 1] =
|
|
|
|
lower ? -flattenedExpr[flattenedExpr.size() - 1]
|
|
|
|
// Upper bound in flattenedExpr is an exclusive one.
|
|
|
|
: flattenedExpr[flattenedExpr.size() - 1] - 1;
|
2018-11-17 12:12:06 +08:00
|
|
|
addInequality(ineq);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (forStmt->hasConstantLowerBound()) {
|
|
|
|
addConstantLowerBound(pos, forStmt->getConstantLowerBound());
|
|
|
|
} else {
|
|
|
|
// Non-constant lower bound case.
|
|
|
|
if (!addLowerOrUpperBound(/*lower=*/true))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (forStmt->hasConstantUpperBound()) {
|
|
|
|
addConstantUpperBound(pos, forStmt->getConstantUpperBound() - 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Non-constant upper bound case.
|
|
|
|
return addLowerOrUpperBound(/*lower=*/false);
|
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
/// Sets the specified identifer to a constant value.
|
|
|
|
void FlatAffineConstraints::setIdToConstant(unsigned pos, int64_t val) {
|
|
|
|
unsigned offset = equalities.size();
|
|
|
|
equalities.resize(equalities.size() + numReservedCols);
|
|
|
|
std::fill(equalities.begin() + offset,
|
|
|
|
equalities.begin() + offset + getNumCols(), 0);
|
|
|
|
equalities[offset + pos] = 1;
|
|
|
|
equalities[offset + getNumCols() - 1] = -val;
|
2018-08-31 08:35:15 +08:00
|
|
|
}
|
2018-10-25 02:30:06 +08:00
|
|
|
|
|
|
|
void FlatAffineConstraints::removeEquality(unsigned pos) {
|
|
|
|
unsigned numEqualities = getNumEqualities();
|
|
|
|
assert(pos < numEqualities);
|
2018-10-31 04:45:10 +08:00
|
|
|
unsigned outputIndex = pos * numReservedCols;
|
|
|
|
unsigned inputIndex = (pos + 1) * numReservedCols;
|
2018-11-08 23:08:52 +08:00
|
|
|
unsigned numElemsToCopy = (numEqualities - pos - 1) * numReservedCols;
|
2018-10-31 04:45:10 +08:00
|
|
|
std::copy(equalities.begin() + inputIndex,
|
|
|
|
equalities.begin() + inputIndex + numElemsToCopy,
|
|
|
|
equalities.begin() + outputIndex);
|
|
|
|
equalities.resize(equalities.size() - numReservedCols);
|
2018-10-25 02:30:06 +08:00
|
|
|
}
|
|
|
|
|
2018-11-06 02:12:16 +08:00
|
|
|
bool FlatAffineConstraints::getDimensionBounds(unsigned pos, unsigned num,
|
|
|
|
SmallVectorImpl<AffineMap> *lbs,
|
|
|
|
SmallVectorImpl<AffineMap> *ubs,
|
|
|
|
MLIRContext *context) {
|
|
|
|
assert(pos + num < getNumCols());
|
|
|
|
|
|
|
|
projectOut(0, pos);
|
|
|
|
projectOut(pos + num, getNumIds() - num);
|
|
|
|
|
|
|
|
lbs->resize(num, AffineMap::Null());
|
|
|
|
ubs->resize(num, AffineMap::Null());
|
|
|
|
|
|
|
|
for (int i = static_cast<int>(num) - 1; i >= 0; i--) {
|
2018-11-07 03:58:42 +08:00
|
|
|
// Only constant dim bounds for now.
|
2018-11-06 02:12:16 +08:00
|
|
|
auto lb = getConstantLowerBound(i);
|
|
|
|
auto ub = getConstantUpperBound(i);
|
|
|
|
// TODO(mlir-team): handle arbitrary bounds.
|
|
|
|
if (!lb.hasValue() || !ub.hasValue())
|
|
|
|
return false;
|
|
|
|
(*lbs)[i] = AffineMap::getConstantMap(lb.getValue(), context);
|
|
|
|
(*ubs)[i] = AffineMap::getConstantMap(ub.getValue(), context);
|
|
|
|
projectOut(i, 1);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
Optional<int64_t>
|
|
|
|
FlatAffineConstraints::getConstantLowerBound(unsigned pos) const {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos < getNumCols() - 1);
|
|
|
|
Optional<int64_t> lb = None;
|
|
|
|
for (unsigned r = 0; r < getNumInequalities(); r++) {
|
|
|
|
if (atIneq(r, pos) <= 0)
|
|
|
|
// Not a lower bound.
|
|
|
|
continue;
|
|
|
|
unsigned c;
|
|
|
|
for (c = 0; c < getNumCols() - 1; c++) {
|
|
|
|
if (c != pos && atIneq(r, c) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Not a constant lower bound.
|
|
|
|
if (c < getNumCols() - 1)
|
|
|
|
return None;
|
|
|
|
auto mayLb = mlir::ceilDiv(-atIneq(r, getNumCols() - 1), atIneq(r, pos));
|
2018-11-08 23:08:52 +08:00
|
|
|
if (!lb.hasValue() || mayLb > lb.getValue())
|
2018-11-06 02:12:16 +08:00
|
|
|
lb = mayLb;
|
|
|
|
}
|
|
|
|
// TODO(andydavis,bondhugula): consider equalities (and an equality
|
|
|
|
// contradicting an inequality, i.e, an empty set).
|
|
|
|
return lb;
|
|
|
|
}
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
/// Returns the extent of the specified identifier (upper bound - lower bound)
|
|
|
|
/// if it found to be a constant; returns None if it's not a constant.
|
|
|
|
/// 'lbPosition' is set to the row position of the corresponding lower bound.
|
|
|
|
Optional<int64_t>
|
|
|
|
FlatAffineConstraints::getConstantBoundDifference(unsigned pos,
|
|
|
|
unsigned *lbPosition) const {
|
|
|
|
// Check if the identifier appears at all in any of the inequalities.
|
|
|
|
unsigned r, e;
|
|
|
|
for (r = 0, e = getNumInequalities(); r < e; r++) {
|
|
|
|
if (atIneq(r, pos) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (r == e) {
|
|
|
|
// If it doesn't appear, just remove the column and return.
|
|
|
|
// TODO(andydavis,bondhugula): refactor removeColumns to use it from here.
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Positions of constraints that are lower/upper bounds on the variable.
|
|
|
|
SmallVector<unsigned, 4> lbIndices, ubIndices;
|
|
|
|
|
|
|
|
// Gather all lower bounds and upper bounds of the variable. Since the
|
|
|
|
// canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower
|
|
|
|
// bound for x_i if c_i >= 1, and an upper bound if c_i <= -1.
|
|
|
|
for (unsigned r = 0, e = getNumInequalities(); r < e; r++) {
|
|
|
|
if (atIneq(r, pos) >= 1)
|
|
|
|
// Lower bound.
|
|
|
|
lbIndices.push_back(r);
|
|
|
|
else if (atIneq(r, pos) <= -1)
|
|
|
|
// Upper bound.
|
|
|
|
ubIndices.push_back(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bondhugula): eliminate all variables that aren't part of any of the
|
|
|
|
// lower/upper bounds - to make this more powerful.
|
|
|
|
|
|
|
|
Optional<int64_t> minDiff = None;
|
|
|
|
for (auto ubPos : ubIndices) {
|
|
|
|
for (auto lbPos : lbIndices) {
|
|
|
|
// Look for a lower bound and an upper bound that only differ by a
|
|
|
|
// constant, i.e., pairs of the form 0 <= c_pos - f(c_i's) <= diffConst.
|
|
|
|
// For example, if ii is the pos^th variable, we are looking for
|
|
|
|
// constraints like ii >= i, ii <= ii + 50, 50 being the difference. The
|
|
|
|
// minimum among all such constant differences is kept since that's the
|
|
|
|
// constant bounding the extent of the pos^th variable.
|
|
|
|
unsigned j;
|
|
|
|
for (j = 0; j < getNumCols() - 1; j++)
|
|
|
|
if (atIneq(ubPos, j) != -atIneq(lbPos, j)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j < getNumCols() - 1)
|
|
|
|
continue;
|
|
|
|
int64_t mayDiff =
|
|
|
|
atIneq(ubPos, getNumCols() - 1) + atIneq(lbPos, getNumCols() - 1) + 1;
|
|
|
|
if (minDiff == None || mayDiff < minDiff) {
|
|
|
|
minDiff = mayDiff;
|
|
|
|
*lbPosition = lbPos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return minDiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<int64_t>
|
|
|
|
FlatAffineConstraints::getConstantUpperBound(unsigned pos) const {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos < getNumCols() - 1);
|
|
|
|
Optional<int64_t> ub = None;
|
|
|
|
for (unsigned r = 0; r < getNumInequalities(); r++) {
|
|
|
|
// Not a upper bound.
|
|
|
|
if (atIneq(r, pos) >= 0)
|
|
|
|
continue;
|
|
|
|
unsigned c;
|
|
|
|
for (c = 0; c < getNumCols() - 1; c++) {
|
|
|
|
if (c != pos && atIneq(r, c) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Not a constant upper bound.
|
|
|
|
if (c < getNumCols() - 1)
|
|
|
|
return None;
|
|
|
|
auto mayUb = mlir::floorDiv(atIneq(r, getNumCols() - 1), -atIneq(r, pos));
|
2018-11-08 23:08:52 +08:00
|
|
|
if (!ub.hasValue() || mayUb < ub.getValue())
|
2018-11-06 02:12:16 +08:00
|
|
|
ub = mayUb;
|
|
|
|
}
|
|
|
|
// TODO(andydavis,bondhugula): consider equalities (and an equality
|
|
|
|
// contradicting an inequality, i.e, an empty set).
|
|
|
|
return ub;
|
|
|
|
}
|
|
|
|
|
2018-11-07 03:58:42 +08:00
|
|
|
// A simple (naive and conservative) check for hyper-rectangularlity.
|
|
|
|
bool FlatAffineConstraints::isHyperRectangular(unsigned pos,
|
|
|
|
unsigned num) const {
|
|
|
|
assert(pos < getNumCols() - 1);
|
|
|
|
// Check for two non-zero coefficients in the range [pos, pos + sum).
|
|
|
|
for (unsigned r = 0; r < getNumInequalities(); r++) {
|
|
|
|
unsigned sum = 0;
|
|
|
|
for (unsigned c = pos; c < pos + num; c++) {
|
|
|
|
if (atIneq(r, c) != 0)
|
|
|
|
sum++;
|
|
|
|
}
|
|
|
|
if (sum > 1)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (unsigned r = 0; r < getNumEqualities(); r++) {
|
|
|
|
unsigned sum = 0;
|
|
|
|
for (unsigned c = pos; c < pos + num; c++) {
|
|
|
|
if (atEq(r, c) != 0)
|
|
|
|
sum++;
|
|
|
|
}
|
|
|
|
if (sum > 1)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-10-25 02:30:06 +08:00
|
|
|
void FlatAffineConstraints::print(raw_ostream &os) const {
|
2018-10-31 04:45:10 +08:00
|
|
|
assert(inequalities.size() == getNumInequalities() * numReservedCols);
|
|
|
|
assert(equalities.size() == getNumEqualities() * numReservedCols);
|
2018-11-17 12:12:06 +08:00
|
|
|
assert(ids.size() == getNumIds());
|
2018-11-07 03:58:42 +08:00
|
|
|
os << "\nConstraints (" << getNumDimIds() << " dims, " << getNumSymbolIds()
|
|
|
|
<< " symbols, " << getNumLocalIds() << " locals): \n";
|
2018-11-17 12:12:06 +08:00
|
|
|
os << "(";
|
|
|
|
for (unsigned i = 0, e = getNumIds(); i < e; i++) {
|
|
|
|
if (ids[i] == None)
|
|
|
|
os << "None ";
|
|
|
|
else
|
|
|
|
os << "MLValue ";
|
|
|
|
}
|
|
|
|
os << ")\n";
|
2018-10-25 02:30:06 +08:00
|
|
|
for (unsigned i = 0, e = getNumEqualities(); i < e; ++i) {
|
|
|
|
for (unsigned j = 0; j < getNumCols(); ++j) {
|
|
|
|
os << atEq(i, j) << " ";
|
|
|
|
}
|
|
|
|
os << "= 0\n";
|
|
|
|
}
|
|
|
|
for (unsigned i = 0, e = getNumInequalities(); i < e; ++i) {
|
|
|
|
for (unsigned j = 0; j < getNumCols(); ++j) {
|
|
|
|
os << atIneq(i, j) << " ";
|
|
|
|
}
|
|
|
|
os << ">= 0\n";
|
|
|
|
}
|
|
|
|
os << '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::dump() const { print(llvm::errs()); }
|
2018-10-25 23:33:02 +08:00
|
|
|
|
|
|
|
void FlatAffineConstraints::removeDuplicates() {
|
|
|
|
// TODO: remove redundant constraints.
|
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::clearAndCopyFrom(
|
|
|
|
const FlatAffineConstraints &other) {
|
|
|
|
FlatAffineConstraints copy(other);
|
|
|
|
std::swap(*this, copy);
|
2018-11-17 12:12:06 +08:00
|
|
|
assert(copy.getNumIds() == copy.getIds().size());
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::removeId(unsigned pos) {
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos < getNumIds());
|
2018-10-25 23:33:02 +08:00
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
if (pos < numDims)
|
|
|
|
numDims--;
|
|
|
|
else if (pos < numSymbols)
|
|
|
|
numSymbols--;
|
|
|
|
numIds--;
|
|
|
|
|
|
|
|
for (unsigned r = 0, e = getNumInequalities(); r < e; r++) {
|
|
|
|
for (unsigned c = pos; c < getNumCols(); c++) {
|
2018-10-25 23:33:02 +08:00
|
|
|
atIneq(r, c) = atIneq(r, c + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 04:45:10 +08:00
|
|
|
for (unsigned r = 0, e = getNumEqualities(); r < e; r++) {
|
|
|
|
for (unsigned c = pos; c < getNumCols(); c++) {
|
2018-10-25 23:33:02 +08:00
|
|
|
atEq(r, c) = atEq(r, c + 1);
|
|
|
|
}
|
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
ids.erase(ids.begin() + pos);
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::pair<unsigned, unsigned>
|
|
|
|
getNewNumDimsSymbols(unsigned pos, const FlatAffineConstraints &cst) {
|
|
|
|
unsigned numDims = cst.getNumDimIds();
|
|
|
|
unsigned numSymbols = cst.getNumSymbolIds();
|
|
|
|
unsigned newNumDims, newNumSymbols;
|
|
|
|
if (pos < numDims) {
|
|
|
|
newNumDims = numDims - 1;
|
|
|
|
newNumSymbols = numSymbols;
|
|
|
|
} else if (pos < numDims + numSymbols) {
|
|
|
|
assert(numSymbols >= 1);
|
|
|
|
newNumDims = numDims;
|
|
|
|
newNumSymbols = numSymbols - 1;
|
|
|
|
} else {
|
|
|
|
newNumDims = numDims;
|
|
|
|
newNumSymbols = numSymbols;
|
|
|
|
}
|
|
|
|
return {newNumDims, newNumSymbols};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Eliminates identifier at the specified position using Fourier-Motzkin
|
|
|
|
/// variable elimination. This technique is exact for rational spaces but
|
|
|
|
/// conservative (in "rare" cases) for integer spaces. The operation corresponds
|
|
|
|
/// to a projection operation yielding the (convex) set of integer points
|
|
|
|
/// contained in the rational shadow of the set. An emptiness test that relies
|
|
|
|
/// on this method will guarantee emptiness, i.e., it disproves the existence of
|
|
|
|
/// a solution if it says it's empty.
|
|
|
|
/// If a non-null isResultIntegerExact is passed, it is set to true if the
|
|
|
|
/// result is also integer exact. If it's set to false, the obtained solution
|
|
|
|
/// *may* not be exact, i.e., it may contain integer points that do not have an
|
|
|
|
/// integer pre-image in the original set.
|
|
|
|
///
|
|
|
|
/// Eg:
|
|
|
|
/// j >= 0, j <= i + 1
|
|
|
|
/// i >= 0, i <= N + 1
|
|
|
|
/// Eliminating i yields,
|
|
|
|
/// j >= 0, 0 <= N + 1, j - 1 <= N + 1
|
|
|
|
///
|
|
|
|
/// If darkShadow = true, this method computes the dark shadow on elimination;
|
|
|
|
/// the dark shadow is a convex integer subset of the exact integer shadow. A
|
|
|
|
/// non-empty dark shadow proves the existence of an integer solution. The
|
|
|
|
/// elimination in such a case could however be an under-approximation, and thus
|
|
|
|
/// should not be used for scanning sets or used by itself for dependence
|
|
|
|
/// checking.
|
|
|
|
///
|
|
|
|
/// Eg: 2-d set, * represents grid points, 'o' represents a point in the set.
|
|
|
|
/// ^
|
|
|
|
/// |
|
|
|
|
/// | * * * * o o
|
|
|
|
/// i | * * o o o o
|
|
|
|
/// | o * * * * *
|
|
|
|
/// --------------->
|
|
|
|
/// j ->
|
|
|
|
///
|
|
|
|
/// Eliminating i from this system (projecting on the j dimension):
|
|
|
|
/// rational shadow / integer light shadow: 1 <= j <= 6
|
|
|
|
/// dark shadow: 3 <= j <= 6
|
|
|
|
/// exact integer shadow: j = 1 \union 3 <= j <= 6
|
|
|
|
/// holes/splinters: j = 2
|
|
|
|
///
|
|
|
|
/// darkShadow = false, isResultIntegerExact = nullptr are default values.
|
|
|
|
// TODO(bondhugula): a slight modification to yield dark shadow version of FM
|
|
|
|
// (tightened), which can prove the existence of a solution if there is one.
|
2018-11-02 06:41:08 +08:00
|
|
|
void FlatAffineConstraints::FourierMotzkinEliminate(
|
2018-10-25 23:33:02 +08:00
|
|
|
unsigned pos, bool darkShadow, bool *isResultIntegerExact) {
|
2018-10-31 04:45:10 +08:00
|
|
|
LLVM_DEBUG(llvm::dbgs() << "FM input (eliminate pos " << pos << "):\n");
|
2018-10-25 23:33:02 +08:00
|
|
|
LLVM_DEBUG(dump());
|
2018-11-02 06:41:08 +08:00
|
|
|
assert(pos < getNumIds() && "invalid position");
|
|
|
|
|
|
|
|
// A fast linear time tightening.
|
|
|
|
GCDTightenInequalities();
|
2018-10-25 23:33:02 +08:00
|
|
|
|
|
|
|
// Check if this identifier can be eliminated through a substitution.
|
|
|
|
for (unsigned r = 0; r < getNumEqualities(); r++) {
|
2018-10-30 01:19:21 +08:00
|
|
|
if (atEq(r, pos) != 0) {
|
2018-10-25 23:33:02 +08:00
|
|
|
// Use Gaussian elimination here (since we have an equality).
|
|
|
|
bool ret = gaussianEliminateId(pos);
|
2018-11-02 06:41:08 +08:00
|
|
|
(void)ret;
|
2018-10-25 23:33:02 +08:00
|
|
|
assert(ret && "Gaussian elimination guaranteed to succeed");
|
2018-11-02 06:41:08 +08:00
|
|
|
LLVM_DEBUG(llvm::dbgs() << "FM output:\n");
|
|
|
|
LLVM_DEBUG(dump());
|
|
|
|
return;
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the identifier appears at all in any of the inequalities.
|
|
|
|
unsigned r, e;
|
|
|
|
for (r = 0, e = getNumInequalities(); r < e; r++) {
|
|
|
|
if (atIneq(r, pos) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (r == getNumInequalities()) {
|
|
|
|
// If it doesn't appear, just remove the column and return.
|
|
|
|
// TODO(andydavis,bondhugula): refactor removeColumns to use it from here.
|
|
|
|
removeId(pos);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "FM output:\n");
|
|
|
|
LLVM_DEBUG(dump());
|
2018-11-02 06:41:08 +08:00
|
|
|
return;
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Positions of constraints that are lower bounds on the variable.
|
|
|
|
SmallVector<unsigned, 4> lbIndices;
|
|
|
|
// Positions of constraints that are lower bounds on the variable.
|
|
|
|
SmallVector<unsigned, 4> ubIndices;
|
|
|
|
// Positions of constraints that do not involve the variable.
|
|
|
|
std::vector<unsigned> nbIndices;
|
|
|
|
nbIndices.reserve(getNumInequalities());
|
|
|
|
|
|
|
|
// Gather all lower bounds and upper bounds of the variable. Since the
|
|
|
|
// canonical form c_1*x_1 + c_2*x_2 + ... + c_0 >= 0, a constraint is a lower
|
|
|
|
// bound for x_i if c_i >= 1, and an upper bound if c_i <= -1.
|
|
|
|
for (unsigned r = 0, e = getNumInequalities(); r < e; r++) {
|
|
|
|
if (atIneq(r, pos) == 0) {
|
|
|
|
// Id does not appear in bound.
|
|
|
|
nbIndices.push_back(r);
|
|
|
|
} else if (atIneq(r, pos) >= 1) {
|
|
|
|
// Lower bound.
|
|
|
|
lbIndices.push_back(r);
|
|
|
|
} else {
|
|
|
|
// Upper bound.
|
|
|
|
ubIndices.push_back(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the number of dimensions, symbols in the resulting system.
|
|
|
|
const auto &dimsSymbols = getNewNumDimsSymbols(pos, *this);
|
|
|
|
unsigned newNumDims = dimsSymbols.first;
|
|
|
|
unsigned newNumSymbols = dimsSymbols.second;
|
|
|
|
|
2018-11-17 12:12:06 +08:00
|
|
|
SmallVector<Optional<MLValue *>, 8> newIds;
|
|
|
|
newIds.reserve(numIds - 1);
|
|
|
|
newIds.insert(newIds.end(), ids.begin(), ids.begin() + pos);
|
|
|
|
newIds.insert(newIds.end(), ids.begin() + pos + 1, ids.end());
|
|
|
|
|
2018-10-25 23:33:02 +08:00
|
|
|
/// Create the new system which has one identifier less.
|
|
|
|
FlatAffineConstraints newFac(
|
|
|
|
lbIndices.size() * ubIndices.size() + nbIndices.size(),
|
|
|
|
getNumEqualities(), getNumCols() - 1, newNumDims, newNumSymbols,
|
2018-11-17 12:12:06 +08:00
|
|
|
/*numLocals=*/getNumIds() - 1 - newNumDims - newNumSymbols, newIds);
|
|
|
|
|
|
|
|
assert(newFac.getIds().size() == newFac.getNumIds());
|
2018-10-25 23:33:02 +08:00
|
|
|
|
|
|
|
// This will be used to check if the elimination was integer exact.
|
|
|
|
unsigned lcmProducts = 1;
|
|
|
|
|
|
|
|
// Let x be the variable we are eliminating.
|
|
|
|
// For each lower bound, lb <= c_l*x, and each upper bound c_u*x <= ub, (note
|
|
|
|
// that c_l, c_u >= 1) we have:
|
|
|
|
// lb*lcm(c_l, c_u)/c_l <= lcm(c_l, c_u)*x <= ub*lcm(c_l, c_u)/c_u
|
|
|
|
// We thus generate a constraint:
|
|
|
|
// lcm(c_l, c_u)/c_l*lb <= lcm(c_l, c_u)/c_u*ub.
|
|
|
|
// Note if c_l = c_u = 1, all integer points captured by the resulting
|
|
|
|
// constraint correspond to integer points in the original system (i.e., they
|
|
|
|
// have integer pre-images). Hence, if the lcm's are all 1, the elimination is
|
|
|
|
// integer exact.
|
|
|
|
for (auto ubPos : ubIndices) {
|
|
|
|
for (auto lbPos : lbIndices) {
|
|
|
|
SmallVector<int64_t, 4> ineq;
|
|
|
|
ineq.reserve(newFac.getNumCols());
|
|
|
|
int64_t lbCoeff = atIneq(lbPos, pos);
|
|
|
|
// Note that in the comments above, ubCoeff is the negation of the
|
|
|
|
// coefficient in the canonical form as the view taken here is that of the
|
|
|
|
// term being moved to the other size of '>='.
|
|
|
|
int64_t ubCoeff = -atIneq(ubPos, pos);
|
|
|
|
// TODO(bondhugula): refactor this loop to avoid all branches inside.
|
|
|
|
for (unsigned l = 0, e = getNumCols(); l < e; l++) {
|
|
|
|
if (l == pos)
|
|
|
|
continue;
|
|
|
|
assert(lbCoeff >= 1 && ubCoeff >= 1 && "bounds wrongly identified");
|
|
|
|
int64_t lcm = mlir::lcm(lbCoeff, ubCoeff);
|
|
|
|
ineq.push_back(atIneq(ubPos, l) * (lcm / ubCoeff) +
|
|
|
|
atIneq(lbPos, l) * (lcm / lbCoeff));
|
|
|
|
lcmProducts *= lcm;
|
|
|
|
}
|
|
|
|
if (darkShadow) {
|
|
|
|
// The dark shadow is a convex subset of the exact integer shadow. If
|
|
|
|
// there is a point here, it proves the existence of a solution.
|
|
|
|
ineq[ineq.size() - 1] += lbCoeff * ubCoeff - lbCoeff - ubCoeff + 1;
|
|
|
|
}
|
|
|
|
// TODO: we need to have a way to add inequalities in-place in
|
|
|
|
// FlatAffineConstraints instead of creating and copying over.
|
|
|
|
newFac.addInequality(ineq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lcmProducts == 1 && isResultIntegerExact)
|
|
|
|
*isResultIntegerExact = 1;
|
|
|
|
|
|
|
|
// Copy over the constraints not involving this variable.
|
|
|
|
for (auto nbPos : nbIndices) {
|
|
|
|
SmallVector<int64_t, 4> ineq;
|
|
|
|
ineq.reserve(getNumCols() - 1);
|
|
|
|
for (unsigned l = 0, e = getNumCols(); l < e; l++) {
|
|
|
|
if (l == pos)
|
|
|
|
continue;
|
|
|
|
ineq.push_back(atIneq(nbPos, l));
|
|
|
|
}
|
|
|
|
newFac.addInequality(ineq);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(newFac.getNumConstraints() ==
|
|
|
|
lbIndices.size() * ubIndices.size() + nbIndices.size());
|
|
|
|
|
|
|
|
// Copy over the equalities.
|
|
|
|
for (unsigned r = 0, e = getNumEqualities(); r < e; r++) {
|
|
|
|
SmallVector<int64_t, 4> eq;
|
|
|
|
eq.reserve(newFac.getNumCols());
|
|
|
|
for (unsigned l = 0, e = getNumCols(); l < e; l++) {
|
|
|
|
if (l == pos)
|
|
|
|
continue;
|
|
|
|
eq.push_back(atEq(r, l));
|
|
|
|
}
|
|
|
|
newFac.addEquality(eq);
|
|
|
|
}
|
|
|
|
|
|
|
|
newFac.removeDuplicates();
|
|
|
|
clearAndCopyFrom(newFac);
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "FM output:\n");
|
|
|
|
LLVM_DEBUG(dump());
|
2018-11-02 06:41:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlatAffineConstraints::projectOut(unsigned pos, unsigned num) {
|
|
|
|
// 'pos' can be at most getNumCols() - 2.
|
2018-11-17 12:12:06 +08:00
|
|
|
if (num == 0)
|
|
|
|
return;
|
2018-11-06 02:12:16 +08:00
|
|
|
assert(pos <= getNumCols() - 2 && "invalid position");
|
2018-11-02 06:41:08 +08:00
|
|
|
assert(pos + num < getNumCols() && "invalid range");
|
|
|
|
for (unsigned i = 0; i < num; i++) {
|
|
|
|
FourierMotzkinEliminate(pos);
|
|
|
|
}
|
2018-10-25 23:33:02 +08:00
|
|
|
}
|
2018-11-17 12:12:06 +08:00
|
|
|
|
|
|
|
void FlatAffineConstraints::projectOut(MLValue *id) {
|
|
|
|
unsigned pos;
|
|
|
|
bool ret = findId(*id, &pos);
|
|
|
|
assert(ret);
|
|
|
|
(void)ret;
|
|
|
|
FourierMotzkinEliminate(pos);
|
|
|
|
}
|