Coverage Report

Created: 2020-06-26 05:44

/home/arjun/llvm-project/mlir/lib/IR/AffineMap.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- AffineMap.cpp - MLIR Affine Map Classes ----------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "mlir/IR/AffineMap.h"
10
#include "AffineMapDetail.h"
11
#include "mlir/IR/Attributes.h"
12
#include "mlir/IR/StandardTypes.h"
13
#include "mlir/Support/LogicalResult.h"
14
#include "mlir/Support/MathExtras.h"
15
#include "llvm/ADT/StringRef.h"
16
#include "llvm/Support/raw_ostream.h"
17
18
using namespace mlir;
19
20
namespace {
21
22
// AffineExprConstantFolder evaluates an affine expression using constant
23
// operands passed in 'operandConsts'. Returns an IntegerAttr attribute
24
// representing the constant value of the affine expression evaluated on
25
// constant 'operandConsts', or nullptr if it can't be folded.
26
class AffineExprConstantFolder {
27
public:
28
  AffineExprConstantFolder(unsigned numDims, ArrayRef<Attribute> operandConsts)
29
0
      : numDims(numDims), operandConsts(operandConsts) {}
30
31
  /// Attempt to constant fold the specified affine expr, or return null on
32
  /// failure.
33
0
  IntegerAttr constantFold(AffineExpr expr) {
34
0
    if (auto result = constantFoldImpl(expr))
35
0
      return IntegerAttr::get(IndexType::get(expr.getContext()), *result);
36
0
    return nullptr;
37
0
  }
38
39
private:
40
0
  Optional<int64_t> constantFoldImpl(AffineExpr expr) {
41
0
    switch (expr.getKind()) {
42
0
    case AffineExprKind::Add:
43
0
      return constantFoldBinExpr(
44
0
          expr, [](int64_t lhs, int64_t rhs) { return lhs + rhs; });
45
0
    case AffineExprKind::Mul:
46
0
      return constantFoldBinExpr(
47
0
          expr, [](int64_t lhs, int64_t rhs) { return lhs * rhs; });
48
0
    case AffineExprKind::Mod:
49
0
      return constantFoldBinExpr(
50
0
          expr, [](int64_t lhs, int64_t rhs) { return mod(lhs, rhs); });
51
0
    case AffineExprKind::FloorDiv:
52
0
      return constantFoldBinExpr(
53
0
          expr, [](int64_t lhs, int64_t rhs) { return floorDiv(lhs, rhs); });
54
0
    case AffineExprKind::CeilDiv:
55
0
      return constantFoldBinExpr(
56
0
          expr, [](int64_t lhs, int64_t rhs) { return ceilDiv(lhs, rhs); });
57
0
    case AffineExprKind::Constant:
58
0
      return expr.cast<AffineConstantExpr>().getValue();
59
0
    case AffineExprKind::DimId:
60
0
      if (auto attr = operandConsts[expr.cast<AffineDimExpr>().getPosition()]
61
0
                          .dyn_cast_or_null<IntegerAttr>())
62
0
        return attr.getInt();
63
0
      return llvm::None;
64
0
    case AffineExprKind::SymbolId:
65
0
      if (auto attr = operandConsts[numDims +
66
0
                                    expr.cast<AffineSymbolExpr>().getPosition()]
67
0
                          .dyn_cast_or_null<IntegerAttr>())
68
0
        return attr.getInt();
69
0
      return llvm::None;
70
0
    }
71
0
    llvm_unreachable("Unknown AffineExpr");
72
0
  }
73
74
  // TODO: Change these to operate on APInts too.
75
  Optional<int64_t> constantFoldBinExpr(AffineExpr expr,
76
0
                                        int64_t (*op)(int64_t, int64_t)) {
77
0
    auto binOpExpr = expr.cast<AffineBinaryOpExpr>();
78
0
    if (auto lhs = constantFoldImpl(binOpExpr.getLHS()))
79
0
      if (auto rhs = constantFoldImpl(binOpExpr.getRHS()))
80
0
        return op(*lhs, *rhs);
81
0
    return llvm::None;
82
0
  }
83
84
  // The number of dimension operands in AffineMap containing this expression.
85
  unsigned numDims;
86
  // The constant valued operands used to evaluate this AffineExpr.
87
  ArrayRef<Attribute> operandConsts;
88
};
89
90
} // end anonymous namespace
91
92
/// Returns a single constant result affine map.
93
0
AffineMap AffineMap::getConstantMap(int64_t val, MLIRContext *context) {
94
0
  return get(/*dimCount=*/0, /*symbolCount=*/0,
95
0
             {getAffineConstantExpr(val, context)});
96
0
}
97
98
/// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
99
/// minor dimensions.
100
AffineMap AffineMap::getMinorIdentityMap(unsigned dims, unsigned results,
101
0
                                         MLIRContext *context) {
102
0
  assert(dims >= results && "Dimension mismatch");
103
0
  auto id = AffineMap::getMultiDimIdentityMap(dims, context);
104
0
  return AffineMap::get(dims, 0, id.getResults().take_back(results), context);
105
0
}
106
107
0
bool AffineMap::isMinorIdentity(AffineMap map) {
108
0
  if (!map)
109
0
    return false;
110
0
  return map == getMinorIdentityMap(map.getNumDims(), map.getNumResults(),
111
0
                                    map.getContext());
112
0
}
113
114
/// Returns an AffineMap representing a permutation.
115
AffineMap AffineMap::getPermutationMap(ArrayRef<unsigned> permutation,
116
                                       MLIRContext *context) {
117
  assert(!permutation.empty() &&
118
         "Cannot create permutation map from empty permutation vector");
119
  SmallVector<AffineExpr, 4> affExprs;
120
  for (auto index : permutation)
121
    affExprs.push_back(getAffineDimExpr(index, context));
122
  auto m = std::max_element(permutation.begin(), permutation.end());
123
  auto permutationMap = AffineMap::get(*m + 1, 0, affExprs, context);
124
  assert(permutationMap.isPermutation() && "Invalid permutation vector");
125
  return permutationMap;
126
}
127
128
template <typename AffineExprContainer>
129
static void getMaxDimAndSymbol(ArrayRef<AffineExprContainer> exprsList,
130
0
                               int64_t &maxDim, int64_t &maxSym) {
131
0
  for (const auto &exprs : exprsList) {
132
0
    for (auto expr : exprs) {
133
0
      expr.walk([&maxDim, &maxSym](AffineExpr e) {
134
0
        if (auto d = e.dyn_cast<AffineDimExpr>())
135
0
          maxDim = std::max(maxDim, static_cast<int64_t>(d.getPosition()));
136
0
        if (auto s = e.dyn_cast<AffineSymbolExpr>())
137
0
          maxSym = std::max(maxSym, static_cast<int64_t>(s.getPosition()));
138
0
      });
Unexecuted instantiation: AffineMap.cpp:_ZZL18getMaxDimAndSymbolIN4llvm8ArrayRefIN4mlir10AffineExprEEEEvNS1_IT_EERlS7_ENKUlS3_E_clES3_
Unexecuted instantiation: AffineMap.cpp:_ZZL18getMaxDimAndSymbolIN4llvm11SmallVectorIN4mlir10AffineExprELj4EEEEvNS0_8ArrayRefIT_EERlS8_ENKUlS3_E_clES3_
139
0
    }
140
0
  }
141
0
}
Unexecuted instantiation: AffineMap.cpp:_ZL18getMaxDimAndSymbolIN4llvm8ArrayRefIN4mlir10AffineExprEEEEvNS1_IT_EERlS7_
Unexecuted instantiation: AffineMap.cpp:_ZL18getMaxDimAndSymbolIN4llvm11SmallVectorIN4mlir10AffineExprELj4EEEEvNS0_8ArrayRefIT_EERlS8_
142
143
template <typename AffineExprContainer>
144
static SmallVector<AffineMap, 4>
145
inferFromExprList(ArrayRef<AffineExprContainer> exprsList) {
146
  assert(!exprsList.empty());
147
  assert(!exprsList[0].empty());
148
  auto context = exprsList[0][0].getContext();
149
  int64_t maxDim = -1, maxSym = -1;
150
  getMaxDimAndSymbol(exprsList, maxDim, maxSym);
151
  SmallVector<AffineMap, 4> maps;
152
  maps.reserve(exprsList.size());
153
  for (const auto &exprs : exprsList)
154
    maps.push_back(AffineMap::get(/*dimCount=*/maxDim + 1,
155
                                  /*symbolCount=*/maxSym + 1, exprs, context));
156
  return maps;
157
}
158
159
SmallVector<AffineMap, 4>
160
0
AffineMap::inferFromExprList(ArrayRef<ArrayRef<AffineExpr>> exprsList) {
161
0
  return ::inferFromExprList(exprsList);
162
0
}
163
164
SmallVector<AffineMap, 4>
165
0
AffineMap::inferFromExprList(ArrayRef<SmallVector<AffineExpr, 4>> exprsList) {
166
0
  return ::inferFromExprList(exprsList);
167
0
}
168
169
AffineMap AffineMap::getMultiDimIdentityMap(unsigned numDims,
170
0
                                            MLIRContext *context) {
171
0
  SmallVector<AffineExpr, 4> dimExprs;
172
0
  dimExprs.reserve(numDims);
173
0
  for (unsigned i = 0; i < numDims; ++i)
174
0
    dimExprs.push_back(mlir::getAffineDimExpr(i, context));
175
0
  return get(/*dimCount=*/numDims, /*symbolCount=*/0, dimExprs, context);
176
0
}
177
178
0
MLIRContext *AffineMap::getContext() const { return map->context; }
179
180
0
bool AffineMap::isIdentity() const {
181
0
  if (getNumDims() != getNumResults())
182
0
    return false;
183
0
  ArrayRef<AffineExpr> results = getResults();
184
0
  for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
185
0
    auto expr = results[i].dyn_cast<AffineDimExpr>();
186
0
    if (!expr || expr.getPosition() != i)
187
0
      return false;
188
0
  }
189
0
  return true;
190
0
}
191
192
0
bool AffineMap::isEmpty() const {
193
0
  return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
194
0
}
195
196
0
bool AffineMap::isSingleConstant() const {
197
0
  return getNumResults() == 1 && getResult(0).isa<AffineConstantExpr>();
198
0
}
199
200
0
int64_t AffineMap::getSingleConstantResult() const {
201
0
  assert(isSingleConstant() && "map must have a single constant result");
202
0
  return getResult(0).cast<AffineConstantExpr>().getValue();
203
0
}
204
205
0
unsigned AffineMap::getNumDims() const {
206
0
  assert(map && "uninitialized map storage");
207
0
  return map->numDims;
208
0
}
209
0
unsigned AffineMap::getNumSymbols() const {
210
0
  assert(map && "uninitialized map storage");
211
0
  return map->numSymbols;
212
0
}
213
0
unsigned AffineMap::getNumResults() const {
214
0
  assert(map && "uninitialized map storage");
215
0
  return map->results.size();
216
0
}
217
0
unsigned AffineMap::getNumInputs() const {
218
0
  assert(map && "uninitialized map storage");
219
0
  return map->numDims + map->numSymbols;
220
0
}
221
222
0
ArrayRef<AffineExpr> AffineMap::getResults() const {
223
0
  assert(map && "uninitialized map storage");
224
0
  return map->results;
225
0
}
226
0
AffineExpr AffineMap::getResult(unsigned idx) const {
227
0
  assert(map && "uninitialized map storage");
228
0
  return map->results[idx];
229
0
}
230
231
/// Folds the results of the application of an affine map on the provided
232
/// operands to a constant if possible. Returns false if the folding happens,
233
/// true otherwise.
234
LogicalResult
235
AffineMap::constantFold(ArrayRef<Attribute> operandConstants,
236
0
                        SmallVectorImpl<Attribute> &results) const {
237
0
  // Attempt partial folding.
238
0
  SmallVector<int64_t, 2> integers;
239
0
  partialConstantFold(operandConstants, &integers);
240
0
241
0
  // If all expressions folded to a constant, populate results with attributes
242
0
  // containing those constants.
243
0
  if (integers.empty())
244
0
    return failure();
245
0
246
0
  auto range = llvm::map_range(integers, [this](int64_t i) {
247
0
    return IntegerAttr::get(IndexType::get(getContext()), i);
248
0
  });
249
0
  results.append(range.begin(), range.end());
250
0
  return success();
251
0
}
252
253
AffineMap
254
AffineMap::partialConstantFold(ArrayRef<Attribute> operandConstants,
255
0
                               SmallVectorImpl<int64_t> *results) const {
256
0
  assert(getNumInputs() == operandConstants.size());
257
0
258
0
  // Fold each of the result expressions.
259
0
  AffineExprConstantFolder exprFolder(getNumDims(), operandConstants);
260
0
  SmallVector<AffineExpr, 4> exprs;
261
0
  exprs.reserve(getNumResults());
262
0
263
0
  for (auto expr : getResults()) {
264
0
    auto folded = exprFolder.constantFold(expr);
265
0
    // If did not fold to a constant, keep the original expression, and clear
266
0
    // the integer results vector.
267
0
    if (folded) {
268
0
      exprs.push_back(
269
0
          getAffineConstantExpr(folded.getInt(), folded.getContext()));
270
0
      if (results)
271
0
        results->push_back(folded.getInt());
272
0
    } else {
273
0
      exprs.push_back(expr);
274
0
      if (results) {
275
0
        results->clear();
276
0
        results = nullptr;
277
0
      }
278
0
    }
279
0
  }
280
0
281
0
  return get(getNumDims(), getNumSymbols(), exprs, getContext());
282
0
}
283
284
/// Walk all of the AffineExpr's in this mapping. Each node in an expression
285
/// tree is visited in postorder.
286
0
void AffineMap::walkExprs(std::function<void(AffineExpr)> callback) const {
287
0
  for (auto expr : getResults())
288
0
    expr.walk(callback);
289
0
}
290
291
/// This method substitutes any uses of dimensions and symbols (e.g.
292
/// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
293
/// expression mapping.  Because this can be used to eliminate dims and
294
/// symbols, the client needs to specify the number of dims and symbols in
295
/// the result.  The returned map always has the same number of results.
296
AffineMap AffineMap::replaceDimsAndSymbols(ArrayRef<AffineExpr> dimReplacements,
297
                                           ArrayRef<AffineExpr> symReplacements,
298
                                           unsigned numResultDims,
299
0
                                           unsigned numResultSyms) const {
300
0
  SmallVector<AffineExpr, 8> results;
301
0
  results.reserve(getNumResults());
302
0
  for (auto expr : getResults())
303
0
    results.push_back(
304
0
        expr.replaceDimsAndSymbols(dimReplacements, symReplacements));
305
0
306
0
  return get(numResultDims, numResultSyms, results, getContext());
307
0
}
308
309
0
AffineMap AffineMap::compose(AffineMap map) {
310
0
  assert(getNumDims() == map.getNumResults() && "Number of results mismatch");
311
0
  // Prepare `map` by concatenating the symbols and rewriting its exprs.
312
0
  unsigned numDims = map.getNumDims();
313
0
  unsigned numSymbolsThisMap = getNumSymbols();
314
0
  unsigned numSymbols = numSymbolsThisMap + map.getNumSymbols();
315
0
  SmallVector<AffineExpr, 8> newDims(numDims);
316
0
  for (unsigned idx = 0; idx < numDims; ++idx) {
317
0
    newDims[idx] = getAffineDimExpr(idx, getContext());
318
0
  }
319
0
  SmallVector<AffineExpr, 8> newSymbols(numSymbols);
320
0
  for (unsigned idx = numSymbolsThisMap; idx < numSymbols; ++idx) {
321
0
    newSymbols[idx - numSymbolsThisMap] =
322
0
        getAffineSymbolExpr(idx, getContext());
323
0
  }
324
0
  auto newMap =
325
0
      map.replaceDimsAndSymbols(newDims, newSymbols, numDims, numSymbols);
326
0
  SmallVector<AffineExpr, 8> exprs;
327
0
  exprs.reserve(getResults().size());
328
0
  for (auto expr : getResults())
329
0
    exprs.push_back(expr.compose(newMap));
330
0
  return AffineMap::get(numDims, numSymbols, exprs, map.getContext());
331
0
}
332
333
0
bool AffineMap::isProjectedPermutation() {
334
0
  if (getNumSymbols() > 0)
335
0
    return false;
336
0
  SmallVector<bool, 8> seen(getNumInputs(), false);
337
0
  for (auto expr : getResults()) {
338
0
    if (auto dim = expr.dyn_cast<AffineDimExpr>()) {
339
0
      if (seen[dim.getPosition()])
340
0
        return false;
341
0
      seen[dim.getPosition()] = true;
342
0
      continue;
343
0
    }
344
0
    return false;
345
0
  }
346
0
  return true;
347
0
}
348
349
0
bool AffineMap::isPermutation() {
350
0
  if (getNumDims() != getNumResults())
351
0
    return false;
352
0
  return isProjectedPermutation();
353
0
}
354
355
0
AffineMap AffineMap::getSubMap(ArrayRef<unsigned> resultPos) {
356
0
  SmallVector<AffineExpr, 4> exprs;
357
0
  exprs.reserve(resultPos.size());
358
0
  for (auto idx : resultPos) {
359
0
    exprs.push_back(getResult(idx));
360
0
  }
361
0
  return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
362
0
}
363
364
0
AffineMap mlir::simplifyAffineMap(AffineMap map) {
365
0
  SmallVector<AffineExpr, 8> exprs;
366
0
  for (auto e : map.getResults()) {
367
0
    exprs.push_back(
368
0
        simplifyAffineExpr(e, map.getNumDims(), map.getNumSymbols()));
369
0
  }
370
0
  return AffineMap::get(map.getNumDims(), map.getNumSymbols(), exprs,
371
0
                        map.getContext());
372
0
}
373
374
0
AffineMap mlir::removeDuplicateExprs(AffineMap map) {
375
0
  auto results = map.getResults();
376
0
  SmallVector<AffineExpr, 4> uniqueExprs(results.begin(), results.end());
377
0
  uniqueExprs.erase(std::unique(uniqueExprs.begin(), uniqueExprs.end()),
378
0
                    uniqueExprs.end());
379
0
  return AffineMap::get(map.getNumDims(), map.getNumSymbols(), uniqueExprs,
380
0
                        map.getContext());
381
0
}
382
383
0
AffineMap mlir::inversePermutation(AffineMap map) {
384
0
  if (map.isEmpty())
385
0
    return map;
386
0
  assert(map.getNumSymbols() == 0 && "expected map without symbols");
387
0
  SmallVector<AffineExpr, 4> exprs(map.getNumDims());
388
0
  for (auto en : llvm::enumerate(map.getResults())) {
389
0
    auto expr = en.value();
390
0
    // Skip non-permutations.
391
0
    if (auto d = expr.dyn_cast<AffineDimExpr>()) {
392
0
      if (exprs[d.getPosition()])
393
0
        continue;
394
0
      exprs[d.getPosition()] = getAffineDimExpr(en.index(), d.getContext());
395
0
    }
396
0
  }
397
0
  SmallVector<AffineExpr, 4> seenExprs;
398
0
  seenExprs.reserve(map.getNumDims());
399
0
  for (auto expr : exprs)
400
0
    if (expr)
401
0
      seenExprs.push_back(expr);
402
0
  if (seenExprs.size() != map.getNumInputs())
403
0
    return AffineMap();
404
0
  return AffineMap::get(map.getNumResults(), 0, seenExprs, map.getContext());
405
0
}
406
407
0
AffineMap mlir::concatAffineMaps(ArrayRef<AffineMap> maps) {
408
0
  unsigned numResults = 0;
409
0
  for (auto m : maps)
410
0
    numResults += m.getNumResults();
411
0
  unsigned numDims = 0;
412
0
  SmallVector<AffineExpr, 8> results;
413
0
  results.reserve(numResults);
414
0
  for (auto m : maps) {
415
0
    assert(m.getNumSymbols() == 0 && "expected map without symbols");
416
0
    results.append(m.getResults().begin(), m.getResults().end());
417
0
    numDims = std::max(m.getNumDims(), numDims);
418
0
  }
419
0
  return AffineMap::get(numDims, /*numSymbols=*/0, results,
420
0
                        maps.front().getContext());
421
0
}
422
423
//===----------------------------------------------------------------------===//
424
// MutableAffineMap.
425
//===----------------------------------------------------------------------===//
426
427
MutableAffineMap::MutableAffineMap(AffineMap map)
428
    : numDims(map.getNumDims()), numSymbols(map.getNumSymbols()),
429
0
      context(map.getContext()) {
430
0
  for (auto result : map.getResults())
431
0
    results.push_back(result);
432
0
}
433
434
0
void MutableAffineMap::reset(AffineMap map) {
435
0
  results.clear();
436
0
  numDims = map.getNumDims();
437
0
  numSymbols = map.getNumSymbols();
438
0
  context = map.getContext();
439
0
  for (auto result : map.getResults())
440
0
    results.push_back(result);
441
0
}
442
443
0
bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
444
0
  if (results[idx].isMultipleOf(factor))
445
0
    return true;
446
0
447
0
  // TODO(bondhugula): use simplifyAffineExpr and FlatAffineConstraints to
448
0
  // complete this (for a more powerful analysis).
449
0
  return false;
450
0
}
451
452
// Simplifies the result affine expressions of this map. The expressions have to
453
// be pure for the simplification implemented.
454
0
void MutableAffineMap::simplify() {
455
0
  // Simplify each of the results if possible.
456
0
  // TODO(ntv): functional-style map
457
0
  for (unsigned i = 0, e = getNumResults(); i < e; i++) {
458
0
    results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
459
0
  }
460
0
}
461
462
0
AffineMap MutableAffineMap::getAffineMap() const {
463
0
  return AffineMap::get(numDims, numSymbols, results, context);
464
0
}