blob: 204bbcb21bb5ed9ccdf07aa2585ab2d923319ba9 [file]
//===- AffineExpr.cpp - MLIR Affine Expr Classes --------------------------===//
//
// 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.
// =============================================================================
#include "mlir/IR/AffineExpr.h"
#include "mlir/Support/STLExtras.h"
#include "llvm/ADT/STLExtras.h"
using namespace mlir;
using namespace mlir::detail;
/// Returns true if this expression is made out of only symbols and
/// constants (no dimensional identifiers).
bool AffineExprClass::isSymbolicOrConstant() {
switch (getKind()) {
case AffineExprKind::Constant:
return true;
case AffineExprKind::DimId:
return false;
case AffineExprKind::SymbolId:
return true;
case AffineExprKind::Add:
case AffineExprKind::Mul:
case AffineExprKind::FloorDiv:
case AffineExprKind::CeilDiv:
case AffineExprKind::Mod: {
auto *expr = cast<AffineBinaryOpExprClass>(this);
return expr->getLHS()->isSymbolicOrConstant() &&
expr->getRHS()->isSymbolicOrConstant();
}
}
}
////////////////////////////////// Details /////////////////////////////////////
AffineBinaryOpExprClass::AffineBinaryOpExprClass(AffineExprKind kind,
AffineExpr lhs, AffineExpr rhs)
: AffineExprClass(kind, lhs->getContext()), lhs(lhs), rhs(rhs) {
// We verify affine op expr forms at construction time.
switch (kind) {
case AffineExprKind::Add:
assert(!lhs.isa<AffineConstantExpr>());
break;
case AffineExprKind::Mul:
assert(!lhs.isa<AffineConstantExpr>());
assert(AffineExpr(rhs)->isSymbolicOrConstant());
break;
case AffineExprKind::FloorDiv:
assert(AffineExpr(rhs)->isSymbolicOrConstant());
break;
case AffineExprKind::CeilDiv:
assert(AffineExpr(rhs)->isSymbolicOrConstant());
break;
case AffineExprKind::Mod:
assert(AffineExpr(rhs)->isSymbolicOrConstant());
break;
default:
llvm_unreachable("unexpected binary affine expr");
}
}
AffineExpr AffineBinaryOpExprClass::getSub(AffineExpr lhs, AffineExpr rhs) {
return getAdd(lhs, getMul(rhs, getAffineConstantExpr(-1, lhs->getContext())));
}
AffineExpr AffineBinaryOpExprClass::getAdd(AffineExpr expr, int64_t rhs) {
return get(AffineExprKind::Add, expr,
getAffineConstantExpr(rhs, expr->getContext()));
}
AffineExpr AffineBinaryOpExprClass::getMul(AffineExpr expr, int64_t rhs) {
return get(AffineExprKind::Mul, expr,
getAffineConstantExpr(rhs, expr->getContext()));
}
AffineExpr AffineBinaryOpExprClass::getFloorDiv(AffineExpr lhs, uint64_t rhs) {
return get(AffineExprKind::FloorDiv, lhs,
getAffineConstantExpr(rhs, lhs->getContext()));
}
AffineExpr AffineBinaryOpExprClass::getCeilDiv(AffineExpr lhs, uint64_t rhs) {
return get(AffineExprKind::CeilDiv, lhs,
getAffineConstantExpr(rhs, lhs->getContext()));
}
AffineExpr AffineBinaryOpExprClass::getMod(AffineExpr lhs, uint64_t rhs) {
return get(AffineExprKind::Mod, lhs,
getAffineConstantExpr(rhs, lhs->getContext()));
}
/// Returns true if this is a pure affine expression, i.e., multiplication,
/// floordiv, ceildiv, and mod is only allowed w.r.t constants.
bool AffineExprClass::isPureAffine() {
switch (getKind()) {
case AffineExprKind::SymbolId:
case AffineExprKind::DimId:
case AffineExprKind::Constant:
return true;
case AffineExprKind::Add: {
auto *op = cast<AffineBinaryOpExprClass>(this);
return op->getLHS()->isPureAffine() && op->getRHS()->isPureAffine();
}
case AffineExprKind::Mul: {
// TODO: Canonicalize the constants in binary operators to the RHS when
// possible, allowing this to merge into the next case.
auto *op = cast<AffineBinaryOpExprClass>(this);
return op->getLHS()->isPureAffine() && op->getRHS()->isPureAffine() &&
(op->getLHS().isa<AffineConstantExpr>() ||
op->getRHS().isa<AffineConstantExpr>());
}
case AffineExprKind::FloorDiv:
case AffineExprKind::CeilDiv:
case AffineExprKind::Mod: {
auto *op = cast<AffineBinaryOpExprClass>(this);
return op->getLHS()->isPureAffine() &&
op->getRHS().isa<AffineConstantExpr>();
}
}
}
/// Returns the greatest known integral divisor of this affine expression.
uint64_t AffineExprClass::getLargestKnownDivisor() {
AffineBinaryOpExpr binExpr;
switch (getKind()) {
case AffineExprKind::SymbolId:
LLVM_FALLTHROUGH;
case AffineExprKind::DimId:
return 1;
case AffineExprKind::Constant:
return std::abs(cast<AffineConstantExprClass>(this)->getValue());
case AffineExprKind::Mul: {
binExpr = cast<AffineBinaryOpExprClass>(this);
return binExpr->getLHS()->getLargestKnownDivisor() *
binExpr->getRHS()->getLargestKnownDivisor();
}
case AffineExprKind::Add:
LLVM_FALLTHROUGH;
case AffineExprKind::FloorDiv:
case AffineExprKind::CeilDiv:
case AffineExprKind::Mod: {
binExpr = cast<AffineBinaryOpExprClass>(this);
return llvm::GreatestCommonDivisor64(
binExpr->getLHS()->getLargestKnownDivisor(),
binExpr->getRHS()->getLargestKnownDivisor());
}
}
}
bool AffineExprClass::isMultipleOf(int64_t factor) {
AffineBinaryOpExprClass *binExpr;
uint64_t l, u;
switch (getKind()) {
case AffineExprKind::SymbolId:
LLVM_FALLTHROUGH;
case AffineExprKind::DimId:
return factor * factor == 1;
case AffineExprKind::Constant:
return cast<AffineConstantExprClass>(this)->getValue() % factor == 0;
case AffineExprKind::Mul: {
binExpr = cast<AffineBinaryOpExprClass>(this);
// It's probably not worth optimizing this further (to not traverse the
// whole sub-tree under - it that would require a version of isMultipleOf
// that on a 'false' return also returns the largest known divisor).
return (l = binExpr->getLHS()->getLargestKnownDivisor()) % factor == 0 ||
(u = binExpr->getRHS()->getLargestKnownDivisor()) % factor == 0 ||
(l * u) % factor == 0;
}
case AffineExprKind::Add:
case AffineExprKind::FloorDiv:
case AffineExprKind::CeilDiv:
case AffineExprKind::Mod: {
binExpr = cast<AffineBinaryOpExprClass>(this);
return llvm::GreatestCommonDivisor64(
binExpr->getLHS()->getLargestKnownDivisor(),
binExpr->getRHS()->getLargestKnownDivisor()) %
factor ==
0;
}
}
}
MLIRContext *AffineExprClass::getContext() { return context; }
///////////////////////////// Done with details ///////////////////////////////
template <> AffineExpr AffineExpr::operator+(int64_t v) const {
return AffineBinaryOpExprClass::getAdd(expr, v);
}
template <> AffineExpr AffineExpr::operator+(AffineExpr other) const {
return AffineBinaryOpExprClass::getAdd(expr, other.expr);
}
template <> AffineExpr AffineExpr::operator*(int64_t v) const {
return AffineBinaryOpExprClass::getMul(expr, v);
}
template <> AffineExpr AffineExpr::operator*(AffineExpr other) const {
return AffineBinaryOpExprClass::getMul(expr, other.expr);
}
// Unary minus, delegate to operator*.
template <> AffineExpr AffineExpr::operator-() const {
return AffineBinaryOpExprClass::getMul(expr, -1);
}
// Delegate to operator+.
template <> AffineExpr AffineExpr::operator-(int64_t v) const {
return *this + (-v);
}
template <> AffineExpr AffineExpr::operator-(AffineExpr other) const {
return *this + (-other);
}
template <> AffineExpr AffineExpr::floorDiv(uint64_t v) const {
return AffineBinaryOpExprClass::getFloorDiv(expr, v);
}
template <> AffineExpr AffineExpr::floorDiv(AffineExpr other) const {
return AffineBinaryOpExprClass::getFloorDiv(expr, other.expr);
}
template <> AffineExpr AffineExpr::ceilDiv(uint64_t v) const {
return AffineBinaryOpExprClass::getCeilDiv(expr, v);
}
template <> AffineExpr AffineExpr::ceilDiv(AffineExpr other) const {
return AffineBinaryOpExprClass::getCeilDiv(expr, other.expr);
}
template <> AffineExpr AffineExpr::operator%(uint64_t v) const {
return AffineBinaryOpExprClass::getMod(expr, v);
}
template <> AffineExpr AffineExpr::operator%(AffineExpr other) const {
return AffineBinaryOpExprClass::getMod(expr, other.expr);
}
AffineExpr operator+(int64_t val, AffineExpr expr) {
return expr + val; // AffineBinaryOpExpr asserts !lhs.isa<AffineConstantExpr>
}
AffineExpr operator-(int64_t val, AffineExpr expr) { return expr * (-1) + val; }
AffineExpr operator*(int64_t val, AffineExpr expr) {
return expr * val; // AffineBinaryOpExpr asserts !lhs.isa<AffineConstantExpr>
}