//===- PresburgerSpace.cpp - MLIR PresburgerSpace Class -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/Analysis/Presburger/PresburgerSpace.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace mlir; using namespace presburger; bool Identifier::isEqual(const Identifier &other) const { if (value == nullptr || other.value == nullptr) return false; assert(value != other.value || (value == other.value && idType == other.idType && "Values of Identifiers are equal but their types do not match.")); return value == other.value; } void Identifier::print(llvm::raw_ostream &os) const { os << "Id<" << value << ">"; } void Identifier::dump() const { print(llvm::errs()); llvm::errs() << "\n"; } PresburgerSpace PresburgerSpace::getDomainSpace() const { PresburgerSpace newSpace = *this; newSpace.removeVarRange(VarKind::Range, 0, getNumRangeVars()); newSpace.convertVarKind(VarKind::Domain, 0, getNumDomainVars(), VarKind::SetDim, 0); return newSpace; } PresburgerSpace PresburgerSpace::getRangeSpace() const { PresburgerSpace newSpace = *this; newSpace.removeVarRange(VarKind::Domain, 0, getNumDomainVars()); return newSpace; } PresburgerSpace PresburgerSpace::getSpaceWithoutLocals() const { PresburgerSpace space = *this; space.removeVarRange(VarKind::Local, 0, getNumLocalVars()); return space; } unsigned PresburgerSpace::getNumVarKind(VarKind kind) const { if (kind == VarKind::Domain) return getNumDomainVars(); if (kind == VarKind::Range) return getNumRangeVars(); if (kind == VarKind::Symbol) return getNumSymbolVars(); if (kind == VarKind::Local) return getNumLocalVars(); llvm_unreachable("VarKind does not exist!"); } unsigned PresburgerSpace::getVarKindOffset(VarKind kind) const { if (kind == VarKind::Domain) return 0; if (kind == VarKind::Range) return getNumDomainVars(); if (kind == VarKind::Symbol) return getNumDimVars(); if (kind == VarKind::Local) return getNumDimAndSymbolVars(); llvm_unreachable("VarKind does not exist!"); } unsigned PresburgerSpace::getVarKindEnd(VarKind kind) const { return getVarKindOffset(kind) + getNumVarKind(kind); } unsigned PresburgerSpace::getVarKindOverlap(VarKind kind, unsigned varStart, unsigned varLimit) const { unsigned varRangeStart = getVarKindOffset(kind); unsigned varRangeEnd = getVarKindEnd(kind); // Compute number of elements in intersection of the ranges [varStart, // varLimit) and [varRangeStart, varRangeEnd). unsigned overlapStart = std::max(varStart, varRangeStart); unsigned overlapEnd = std::min(varLimit, varRangeEnd); if (overlapStart > overlapEnd) return 0; return overlapEnd - overlapStart; } VarKind PresburgerSpace::getVarKindAt(unsigned pos) const { assert(pos < getNumVars() && "`pos` should represent a valid var position"); if (pos < getVarKindEnd(VarKind::Domain)) return VarKind::Domain; if (pos < getVarKindEnd(VarKind::Range)) return VarKind::Range; if (pos < getVarKindEnd(VarKind::Symbol)) return VarKind::Symbol; if (pos < getVarKindEnd(VarKind::Local)) return VarKind::Local; llvm_unreachable("`pos` should represent a valid var position"); } unsigned PresburgerSpace::insertVar(VarKind kind, unsigned pos, unsigned num) { assert(pos <= getNumVarKind(kind)); unsigned absolutePos = getVarKindOffset(kind) + pos; if (kind == VarKind::Domain) numDomain += num; else if (kind == VarKind::Range) numRange += num; else if (kind == VarKind::Symbol) numSymbols += num; else numLocals += num; // Insert NULL identifiers if `usingIds` and variables inserted are // not locals. if (usingIds && kind != VarKind::Local) identifiers.insert(identifiers.begin() + absolutePos, num, Identifier()); return absolutePos; } void PresburgerSpace::removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit) { assert(varLimit <= getNumVarKind(kind) && "invalid var limit"); if (varStart >= varLimit) return; unsigned numVarsEliminated = varLimit - varStart; if (kind == VarKind::Domain) numDomain -= numVarsEliminated; else if (kind == VarKind::Range) numRange -= numVarsEliminated; else if (kind == VarKind::Symbol) numSymbols -= numVarsEliminated; else numLocals -= numVarsEliminated; // Remove identifiers if `usingIds` and variables removed are not // locals. if (usingIds && kind != VarKind::Local) identifiers.erase(identifiers.begin() + getVarKindOffset(kind) + varStart, identifiers.begin() + getVarKindOffset(kind) + varLimit); } void PresburgerSpace::convertVarKind(VarKind srcKind, unsigned srcPos, unsigned num, VarKind dstKind, unsigned dstPos) { assert(srcKind != dstKind && "cannot convert variables to the same kind"); assert(srcPos + num <= getNumVarKind(srcKind) && "invalid range for source variables"); assert(dstPos <= getNumVarKind(dstKind) && "invalid position for destination variables"); // Move identifiers if `usingIds` and variables moved are not locals. unsigned srcOffset = getVarKindOffset(srcKind) + srcPos; unsigned dstOffset = getVarKindOffset(dstKind) + dstPos; if (isUsingIds() && srcKind != VarKind::Local && dstKind != VarKind::Local) { identifiers.insert(identifiers.begin() + dstOffset, num, Identifier()); // Update srcOffset if insertion of new elements invalidates it. if (dstOffset < srcOffset) srcOffset += num; std::move(identifiers.begin() + srcOffset, identifiers.begin() + srcOffset + num, identifiers.begin() + dstOffset); identifiers.erase(identifiers.begin() + srcOffset, identifiers.begin() + srcOffset + num); } else if (isUsingIds() && srcKind != VarKind::Local) { identifiers.erase(identifiers.begin() + srcOffset, identifiers.begin() + srcOffset + num); } else if (isUsingIds() && dstKind != VarKind::Local) { identifiers.insert(identifiers.begin() + dstOffset, num, Identifier()); } auto addVars = [&](VarKind kind, int num) { switch (kind) { case VarKind::Domain: numDomain += num; break; case VarKind::Range: numRange += num; break; case VarKind::Symbol: numSymbols += num; break; case VarKind::Local: numLocals += num; break; } }; addVars(srcKind, -(signed)num); addVars(dstKind, num); } void PresburgerSpace::swapVar(VarKind kindA, VarKind kindB, unsigned posA, unsigned posB) { if (!isUsingIds()) return; if (kindA == VarKind::Local && kindB == VarKind::Local) return; if (kindA == VarKind::Local) { getId(kindB, posB) = Identifier(); return; } if (kindB == VarKind::Local) { getId(kindA, posA) = Identifier(); return; } std::swap(getId(kindA, posA), getId(kindB, posB)); } bool PresburgerSpace::isCompatible(const PresburgerSpace &other) const { return getNumDomainVars() == other.getNumDomainVars() && getNumRangeVars() == other.getNumRangeVars() && getNumSymbolVars() == other.getNumSymbolVars(); } bool PresburgerSpace::isEqual(const PresburgerSpace &other) const { return isCompatible(other) && getNumLocalVars() == other.getNumLocalVars(); } /// Checks if the number of ids of the given kind in the two spaces are /// equal and if the ids are equal. Assumes that both spaces are using /// ids. static bool areIdsEqual(const PresburgerSpace &spaceA, const PresburgerSpace &spaceB, VarKind kind) { assert(spaceA.isUsingIds() && spaceB.isUsingIds() && "Both spaces should be using ids"); if (spaceA.getNumVarKind(kind) != spaceB.getNumVarKind(kind)) return false; if (kind == VarKind::Local) return true; // No ids. return spaceA.getIds(kind) == spaceB.getIds(kind); } bool PresburgerSpace::isAligned(const PresburgerSpace &other) const { // If only one of the spaces is using identifiers, then they are // not aligned. if (isUsingIds() != other.isUsingIds()) return false; // If both spaces are using identifiers, then they are aligned if // their identifiers are equal. Identifiers being equal implies // that the number of variables of each kind is same, which implies // compatiblity, so we do not check for that. if (isUsingIds()) return areIdsEqual(*this, other, VarKind::Domain) && areIdsEqual(*this, other, VarKind::Range) && areIdsEqual(*this, other, VarKind::Symbol); // If neither space is using identifiers, then they are aligned if // they are compatible. return isCompatible(other); } bool PresburgerSpace::isAligned(const PresburgerSpace &other, VarKind kind) const { // If only one of the spaces is using identifiers, then they are // not aligned. if (isUsingIds() != other.isUsingIds()) return false; // If both spaces are using identifiers, then they are aligned if // their identifiers are equal. Identifiers being equal implies // that the number of variables of each kind is same, which implies // compatiblity, so we do not check for that if (isUsingIds()) return areIdsEqual(*this, other, kind); // If neither space is using identifiers, then they are aligned if // the number of variable kind is equal. return getNumVarKind(kind) == other.getNumVarKind(kind); } void PresburgerSpace::setVarSymbolSeperation(unsigned newSymbolCount) { assert(newSymbolCount <= getNumDimAndSymbolVars() && "invalid separation position"); numRange = numRange + numSymbols - newSymbolCount; numSymbols = newSymbolCount; // We do not need to change `identifiers` since the ordering of // `identifiers` remains same. } void PresburgerSpace::mergeAndAlignSymbols(PresburgerSpace &other) { assert(usingIds && other.usingIds && "Both spaces need to have identifers to merge & align"); // First merge & align identifiers into `other` from `this`. unsigned i = 0; for (const Identifier identifier : getIds(VarKind::Symbol)) { // If the identifier exists in `other`, then align it; otherwise insert it // assuming it is a new identifier. Search in `other` starting at position // `i` since the left of `i` is aligned. auto *findBegin = other.getIds(VarKind::Symbol).begin() + i; auto *findEnd = other.getIds(VarKind::Symbol).end(); auto *itr = std::find(findBegin, findEnd, identifier); if (itr != findEnd) { std::swap(findBegin, itr); } else { other.insertVar(VarKind::Symbol, i); other.getId(VarKind::Symbol, i) = identifier; } ++i; } // Finally add identifiers that are in `other`, but not in `this` to `this`. for (unsigned e = other.getNumVarKind(VarKind::Symbol); i < e; ++i) { insertVar(VarKind::Symbol, i); getId(VarKind::Symbol, i) = other.getId(VarKind::Symbol, i); } } void PresburgerSpace::print(llvm::raw_ostream &os) const { os << "Domain: " << getNumDomainVars() << ", " << "Range: " << getNumRangeVars() << ", " << "Symbols: " << getNumSymbolVars() << ", " << "Locals: " << getNumLocalVars() << "\n"; if (isUsingIds()) { auto printIds = [&](VarKind kind) { os << " "; for (Identifier id : getIds(kind)) { if (id.hasValue()) id.print(os); else os << "None"; os << " "; } }; os << "("; printIds(VarKind::Domain); os << ") -> ("; printIds(VarKind::Range); os << ") : ["; printIds(VarKind::Symbol); os << "]"; } } void PresburgerSpace::dump() const { print(llvm::errs()); llvm::errs() << "\n"; }