//===- SDBMExpr.cpp - MLIR SDBM Expression implementation -----------------===// // // 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 // //===----------------------------------------------------------------------===// // // A striped difference-bound matrix (SDBM) expression is a constant expression, // an identifier, a binary expression with constant RHS and +, stripe operators // or a difference expression between two identifiers. // //===----------------------------------------------------------------------===// #include "mlir/Dialect/SDBM/SDBMExpr.h" #include "SDBMExprDetail.h" #include "mlir/Dialect/SDBM/SDBMDialect.h" #include "mlir/IR/AffineExpr.h" #include "mlir/IR/AffineExprVisitor.h" #include "llvm/Support/raw_ostream.h" using namespace mlir; namespace { /// A simple compositional matcher for AffineExpr /// /// Example usage: /// /// ```c++ /// AffineExprMatcher x, C, m; /// AffineExprMatcher pattern1 = ((x % C) * m) + x; /// AffineExprMatcher pattern2 = x + ((x % C) * m); /// if (pattern1.match(expr) || pattern2.match(expr)) { /// ... /// } /// ``` class AffineExprMatcherStorage; class AffineExprMatcher { public: AffineExprMatcher(); AffineExprMatcher(const AffineExprMatcher &other); AffineExprMatcher operator+(AffineExprMatcher other) { return AffineExprMatcher(AffineExprKind::Add, *this, other); } AffineExprMatcher operator*(AffineExprMatcher other) { return AffineExprMatcher(AffineExprKind::Mul, *this, other); } AffineExprMatcher floorDiv(AffineExprMatcher other) { return AffineExprMatcher(AffineExprKind::FloorDiv, *this, other); } AffineExprMatcher ceilDiv(AffineExprMatcher other) { return AffineExprMatcher(AffineExprKind::CeilDiv, *this, other); } AffineExprMatcher operator%(AffineExprMatcher other) { return AffineExprMatcher(AffineExprKind::Mod, *this, other); } AffineExpr match(AffineExpr expr); AffineExpr matched(); Optional getMatchedConstantValue(); private: AffineExprMatcher(AffineExprKind k, AffineExprMatcher a, AffineExprMatcher b); AffineExprKind kind; // only used to match in binary op cases. // A shared_ptr allows multiple references to same matcher storage without // worrying about ownership or dealing with an arena. To be cleaned up if we // go with this. std::shared_ptr storage; }; class AffineExprMatcherStorage { public: AffineExprMatcherStorage() {} AffineExprMatcherStorage(const AffineExprMatcherStorage &other) : subExprs(other.subExprs.begin(), other.subExprs.end()), matched(other.matched) {} AffineExprMatcherStorage(ArrayRef exprs) : subExprs(exprs.begin(), exprs.end()) {} AffineExprMatcherStorage(AffineExprMatcher &a, AffineExprMatcher &b) : subExprs({a, b}) {} SmallVector subExprs; AffineExpr matched; }; } // namespace AffineExprMatcher::AffineExprMatcher() : kind(AffineExprKind::Constant), storage(new AffineExprMatcherStorage()) {} AffineExprMatcher::AffineExprMatcher(const AffineExprMatcher &other) : kind(other.kind), storage(other.storage) {} Optional AffineExprMatcher::getMatchedConstantValue() { if (auto cst = storage->matched.dyn_cast()) return cst.getValue(); return None; } AffineExpr AffineExprMatcher::match(AffineExpr expr) { if (kind > AffineExprKind::LAST_AFFINE_BINARY_OP) { if (storage->matched) if (storage->matched != expr) return AffineExpr(); storage->matched = expr; return storage->matched; } if (kind != expr.getKind()) { return AffineExpr(); } if (auto bin = expr.dyn_cast()) { if (!storage->subExprs.empty() && !storage->subExprs[0].match(bin.getLHS())) { return AffineExpr(); } if (!storage->subExprs.empty() && !storage->subExprs[1].match(bin.getRHS())) { return AffineExpr(); } if (storage->matched) if (storage->matched != expr) return AffineExpr(); storage->matched = expr; return storage->matched; } llvm_unreachable("binary expected"); } AffineExpr AffineExprMatcher::matched() { return storage->matched; } AffineExprMatcher::AffineExprMatcher(AffineExprKind k, AffineExprMatcher a, AffineExprMatcher b) : kind(k), storage(new AffineExprMatcherStorage(a, b)) { storage->subExprs.push_back(a); storage->subExprs.push_back(b); } //===----------------------------------------------------------------------===// // SDBMExpr //===----------------------------------------------------------------------===// SDBMExprKind SDBMExpr::getKind() const { return impl->getKind(); } MLIRContext *SDBMExpr::getContext() const { return impl->dialect->getContext(); } SDBMDialect *SDBMExpr::getDialect() const { return impl->dialect; } void SDBMExpr::print(raw_ostream &os) const { struct Printer : public SDBMVisitor { Printer(raw_ostream &ostream) : prn(ostream) {} void visitSum(SDBMSumExpr expr) { visit(expr.getLHS()); prn << " + "; visit(expr.getRHS()); } void visitDiff(SDBMDiffExpr expr) { visit(expr.getLHS()); prn << " - "; visit(expr.getRHS()); } void visitDim(SDBMDimExpr expr) { prn << 'd' << expr.getPosition(); } void visitSymbol(SDBMSymbolExpr expr) { prn << 's' << expr.getPosition(); } void visitStripe(SDBMStripeExpr expr) { SDBMDirectExpr lhs = expr.getLHS(); bool isTerm = lhs.isa(); if (!isTerm) prn << '('; visit(lhs); if (!isTerm) prn << ')'; prn << " # "; visitConstant(expr.getStripeFactor()); } void visitNeg(SDBMNegExpr expr) { bool isSum = expr.getVar().isa(); prn << '-'; if (isSum) prn << '('; visit(expr.getVar()); if (isSum) prn << ')'; } void visitConstant(SDBMConstantExpr expr) { prn << expr.getValue(); } raw_ostream &prn; }; Printer printer(os); printer.visit(*this); } void SDBMExpr::dump() const { print(llvm::errs()); llvm::errs() << '\n'; } namespace { // Helper class to perform negation of an SDBM expression. struct SDBMNegator : public SDBMVisitor { // Any term expression is wrapped into a negation expression. // -(x) = -x SDBMExpr visitDirect(SDBMDirectExpr expr) { return SDBMNegExpr::get(expr); } // A negation expression is unwrapped. // -(-x) = x SDBMExpr visitNeg(SDBMNegExpr expr) { return expr.getVar(); } // The value of the constant is negated. SDBMExpr visitConstant(SDBMConstantExpr expr) { return SDBMConstantExpr::get(expr.getDialect(), -expr.getValue()); } // Terms of a difference are interchanged. Since only the LHS of a diff // expression is allowed to be a sum with a constant, we need to recreate the // sum with the negated value: // -((x + C) - y) = (y - C) - x. SDBMExpr visitDiff(SDBMDiffExpr expr) { // If the LHS is just a term, we can do straightforward interchange. if (auto term = expr.getLHS().dyn_cast()) return SDBMDiffExpr::get(expr.getRHS(), term); auto sum = expr.getLHS().cast(); auto cst = visitConstant(sum.getRHS()).cast(); return SDBMDiffExpr::get(SDBMSumExpr::get(expr.getRHS(), cst), sum.getLHS()); } }; } // namespace SDBMExpr SDBMExpr::operator-() { return SDBMNegator().visit(*this); } //===----------------------------------------------------------------------===// // SDBMSumExpr //===----------------------------------------------------------------------===// SDBMSumExpr SDBMSumExpr::get(SDBMTermExpr lhs, SDBMConstantExpr rhs) { assert(lhs && "expected SDBM variable expression"); assert(rhs && "expected SDBM constant"); // If LHS of a sum is another sum, fold the constant RHS parts. if (auto lhsSum = lhs.dyn_cast()) { lhs = lhsSum.getLHS(); rhs = SDBMConstantExpr::get(rhs.getDialect(), rhs.getValue() + lhsSum.getRHS().getValue()); } StorageUniquer &uniquer = lhs.getDialect()->getUniquer(); return uniquer.get( /*initFn=*/{}, static_cast(SDBMExprKind::Add), lhs, rhs); } SDBMTermExpr SDBMSumExpr::getLHS() const { return static_cast(impl)->lhs.cast(); } SDBMConstantExpr SDBMSumExpr::getRHS() const { return static_cast(impl)->rhs; } AffineExpr SDBMExpr::getAsAffineExpr() const { struct Converter : public SDBMVisitor { AffineExpr visitSum(SDBMSumExpr expr) { AffineExpr lhs = visit(expr.getLHS()), rhs = visit(expr.getRHS()); return lhs + rhs; } AffineExpr visitStripe(SDBMStripeExpr expr) { AffineExpr lhs = visit(expr.getLHS()), rhs = visit(expr.getStripeFactor()); return lhs - (lhs % rhs); } AffineExpr visitDiff(SDBMDiffExpr expr) { AffineExpr lhs = visit(expr.getLHS()), rhs = visit(expr.getRHS()); return lhs - rhs; } AffineExpr visitDim(SDBMDimExpr expr) { return getAffineDimExpr(expr.getPosition(), expr.getContext()); } AffineExpr visitSymbol(SDBMSymbolExpr expr) { return getAffineSymbolExpr(expr.getPosition(), expr.getContext()); } AffineExpr visitNeg(SDBMNegExpr expr) { return getAffineBinaryOpExpr(AffineExprKind::Mul, getAffineConstantExpr(-1, expr.getContext()), visit(expr.getVar())); } AffineExpr visitConstant(SDBMConstantExpr expr) { return getAffineConstantExpr(expr.getValue(), expr.getContext()); } } converter; return converter.visit(*this); } // Given a direct expression `expr`, add the given constant to it and pass the // resulting expression to `builder` before returning its result. If the // expression is already a sum expression, update its constant and extract the // LHS if the constant becomes zero. Otherwise, construct a sum expression. template static Result addConstantAndSink(SDBMDirectExpr expr, int64_t constant, bool negated, function_ref builder) { SDBMDialect *dialect = expr.getDialect(); if (auto sumExpr = expr.dyn_cast()) { if (negated) constant = sumExpr.getRHS().getValue() - constant; else constant += sumExpr.getRHS().getValue(); if (constant != 0) { auto sum = SDBMSumExpr::get(sumExpr.getLHS(), SDBMConstantExpr::get(dialect, constant)); return builder(sum); } else { return builder(sumExpr.getLHS()); } } if (constant != 0) return builder(SDBMSumExpr::get( expr.cast(), SDBMConstantExpr::get(dialect, negated ? -constant : constant))); return expr; } // Construct an expression lhs + constant while maintaining the canonical form // of the SDBM expressions, in particular sink the constant expression to the // nearest sum expression in the left subtree of the expression tree. static SDBMExpr addConstant(SDBMVaryingExpr lhs, int64_t constant) { if (auto lhsDiff = lhs.dyn_cast()) return addConstantAndSink( lhsDiff.getLHS(), constant, /*negated=*/false, [lhsDiff](SDBMDirectExpr e) { return SDBMDiffExpr::get(e, lhsDiff.getRHS()); }); if (auto lhsNeg = lhs.dyn_cast()) return addConstantAndSink( lhsNeg.getVar(), constant, /*negated=*/true, [](SDBMDirectExpr e) { return SDBMNegExpr::get(e); }); if (auto lhsSum = lhs.dyn_cast()) return addConstantAndSink(lhsSum, constant, /*negated=*/false, [](SDBMDirectExpr e) { return e; }); if (constant != 0) return SDBMSumExpr::get(lhs.cast(), SDBMConstantExpr::get(lhs.getDialect(), constant)); return lhs; } // Build a difference expression given a direct expression and a negation // expression. static SDBMExpr buildDiffExpr(SDBMDirectExpr lhs, SDBMNegExpr rhs) { // Fold (x + C) - (x + D) = C - D. if (lhs.getTerm() == rhs.getVar().getTerm()) return SDBMConstantExpr::get( lhs.getDialect(), lhs.getConstant() - rhs.getVar().getConstant()); return SDBMDiffExpr::get( addConstantAndSink(lhs, -rhs.getVar().getConstant(), /*negated=*/false, [](SDBMDirectExpr e) { return e; }), rhs.getVar().getTerm()); } // Try folding an expression (lhs + rhs) where at least one of the operands // contains a negated variable, i.e. is a negation or a difference expression. static SDBMExpr foldSumDiff(SDBMExpr lhs, SDBMExpr rhs) { // If exactly one of LHS, RHS is a negation expression, we can construct // a difference expression, which is a special kind in SDBM. auto lhsDirect = lhs.dyn_cast(); auto rhsDirect = rhs.dyn_cast(); auto lhsNeg = lhs.dyn_cast(); auto rhsNeg = rhs.dyn_cast(); if (lhsDirect && rhsNeg) return buildDiffExpr(lhsDirect, rhsNeg); if (lhsNeg && rhsDirect) return buildDiffExpr(rhsDirect, lhsNeg); // If a subexpression appears in a diff expression on the LHS(RHS) of a // sum expression where it also appears on the RHS(LHS) with the opposite // sign, we can simplify it away and obtain the SDBM form. auto lhsDiff = lhs.dyn_cast(); auto rhsDiff = rhs.dyn_cast(); // -(x + A) + ((x + B) - y) = -(y + (A - B)) if (lhsNeg && rhsDiff && lhsNeg.getVar().getTerm() == rhsDiff.getLHS().getTerm()) { int64_t constant = lhsNeg.getVar().getConstant() - rhsDiff.getLHS().getConstant(); // RHS of the diff is a term expression, its sum with a constant is a direct // expression. return SDBMNegExpr::get( addConstant(rhsDiff.getRHS(), constant).cast()); } // (x + A) + ((y + B) - x) = (y + B) + A. if (lhsDirect && rhsDiff && lhsDirect.getTerm() == rhsDiff.getRHS()) return addConstant(rhsDiff.getLHS(), lhsDirect.getConstant()); // ((x + A) - y) + (-(x + B)) = -(y + (B - A)). if (lhsDiff && rhsNeg && lhsDiff.getLHS().getTerm() == rhsNeg.getVar().getTerm()) { int64_t constant = rhsNeg.getVar().getConstant() - lhsDiff.getLHS().getConstant(); // RHS of the diff is a term expression, its sum with a constant is a direct // expression. return SDBMNegExpr::get( addConstant(lhsDiff.getRHS(), constant).cast()); } // ((x + A) - y) + (y + B) = (x + A) + B. if (rhsDirect && lhsDiff && rhsDirect.getTerm() == lhsDiff.getRHS()) return addConstant(lhsDiff.getLHS(), rhsDirect.getConstant()); return {}; } Optional SDBMExpr::tryConvertAffineExpr(AffineExpr affine) { struct Converter : public AffineExprVisitor { SDBMExpr visitAddExpr(AffineBinaryOpExpr expr) { auto lhs = visit(expr.getLHS()), rhs = visit(expr.getRHS()); if (!lhs || !rhs) return {}; // In a "add" AffineExpr, the constant always appears on the right. If // there were two constants, they would have been folded away. assert(!lhs.isa() && "non-canonical affine expression"); // If RHS is a constant, we can always extend the SDBM expression to // include it by sinking the constant into the nearest sum expression. if (auto rhsConstant = rhs.dyn_cast()) { int64_t constant = rhsConstant.getValue(); auto varying = lhs.dyn_cast(); assert(varying && "unexpected uncanonicalized sum of constants"); return addConstant(varying, constant); } // Try building a difference expression if one of the values is negated, // or check if a difference on either hand side cancels out the outer term // so as to remain correct within SDBM. Return null otherwise. return foldSumDiff(lhs, rhs); } SDBMExpr visitMulExpr(AffineBinaryOpExpr expr) { // Attempt to recover a stripe expression "x # C = (x floordiv C) * C". AffineExprMatcher x, C; AffineExprMatcher pattern = (x.floorDiv(C)) * C; if (pattern.match(expr)) { if (SDBMExpr converted = visit(x.matched())) { if (auto varConverted = converted.dyn_cast()) // TODO: return varConverted.stripe(C.getConstantValue()); return SDBMStripeExpr::get( varConverted, SDBMConstantExpr::get(dialect, C.getMatchedConstantValue().getValue())); } } auto lhs = visit(expr.getLHS()), rhs = visit(expr.getRHS()); if (!lhs || !rhs) return {}; // In a "mul" AffineExpr, the constant always appears on the right. If // there were two constants, they would have been folded away. assert(!lhs.isa() && "non-canonical affine expression"); auto rhsConstant = rhs.dyn_cast(); if (!rhsConstant) return {}; // The only supported "multiplication" expression is an SDBM is dimension // negation, that is a product of dimension and constant -1. if (rhsConstant.getValue() != -1) return {}; if (auto lhsVar = lhs.dyn_cast()) return SDBMNegExpr::get(lhsVar); if (auto lhsDiff = lhs.dyn_cast()) return SDBMNegator().visitDiff(lhsDiff); // Other multiplications are not allowed in SDBM. return {}; } SDBMExpr visitModExpr(AffineBinaryOpExpr expr) { auto lhs = visit(expr.getLHS()), rhs = visit(expr.getRHS()); if (!lhs || !rhs) return {}; // 'mod' can only be converted to SDBM if its LHS is a direct expression // and its RHS is a constant. Then it `x mod c = x - x stripe c`. auto rhsConstant = rhs.dyn_cast(); auto lhsVar = lhs.dyn_cast(); if (!lhsVar || !rhsConstant) return {}; return SDBMDiffExpr::get(lhsVar, SDBMStripeExpr::get(lhsVar, rhsConstant)); } // `a floordiv b = (a stripe b) / b`, but we have no division in SDBM SDBMExpr visitFloorDivExpr(AffineBinaryOpExpr expr) { return {}; } SDBMExpr visitCeilDivExpr(AffineBinaryOpExpr expr) { return {}; } // Dimensions, symbols and constants are converted trivially. SDBMExpr visitConstantExpr(AffineConstantExpr expr) { return SDBMConstantExpr::get(dialect, expr.getValue()); } SDBMExpr visitDimExpr(AffineDimExpr expr) { return SDBMDimExpr::get(dialect, expr.getPosition()); } SDBMExpr visitSymbolExpr(AffineSymbolExpr expr) { return SDBMSymbolExpr::get(dialect, expr.getPosition()); } SDBMDialect *dialect; } converter; converter.dialect = affine.getContext()->getOrLoadDialect(); if (auto result = converter.visit(affine)) return result; return None; } //===----------------------------------------------------------------------===// // SDBMDiffExpr //===----------------------------------------------------------------------===// SDBMDiffExpr SDBMDiffExpr::get(SDBMDirectExpr lhs, SDBMTermExpr rhs) { assert(lhs && "expected SDBM dimension"); assert(rhs && "expected SDBM dimension"); StorageUniquer &uniquer = lhs.getDialect()->getUniquer(); return uniquer.get(/*initFn=*/{}, lhs, rhs); } SDBMDirectExpr SDBMDiffExpr::getLHS() const { return static_cast(impl)->lhs; } SDBMTermExpr SDBMDiffExpr::getRHS() const { return static_cast(impl)->rhs; } //===----------------------------------------------------------------------===// // SDBMDirectExpr //===----------------------------------------------------------------------===// SDBMTermExpr SDBMDirectExpr::getTerm() { if (auto sum = dyn_cast()) return sum.getLHS(); return cast(); } int64_t SDBMDirectExpr::getConstant() { if (auto sum = dyn_cast()) return sum.getRHS().getValue(); return 0; } //===----------------------------------------------------------------------===// // SDBMStripeExpr //===----------------------------------------------------------------------===// SDBMStripeExpr SDBMStripeExpr::get(SDBMDirectExpr var, SDBMConstantExpr stripeFactor) { assert(var && "expected SDBM variable expression"); assert(stripeFactor && "expected non-null stripe factor"); if (stripeFactor.getValue() <= 0) llvm::report_fatal_error("non-positive stripe factor"); StorageUniquer &uniquer = var.getDialect()->getUniquer(); return uniquer.get( /*initFn=*/{}, static_cast(SDBMExprKind::Stripe), var, stripeFactor); } SDBMDirectExpr SDBMStripeExpr::getLHS() const { if (SDBMVaryingExpr lhs = static_cast(impl)->lhs) return lhs.cast(); return {}; } SDBMConstantExpr SDBMStripeExpr::getStripeFactor() const { return static_cast(impl)->rhs; } //===----------------------------------------------------------------------===// // SDBMInputExpr //===----------------------------------------------------------------------===// unsigned SDBMInputExpr::getPosition() const { return static_cast(impl)->position; } //===----------------------------------------------------------------------===// // SDBMDimExpr //===----------------------------------------------------------------------===// SDBMDimExpr SDBMDimExpr::get(SDBMDialect *dialect, unsigned position) { assert(dialect && "expected non-null dialect"); auto assignDialect = [dialect](detail::SDBMTermExprStorage *storage) { storage->dialect = dialect; }; StorageUniquer &uniquer = dialect->getUniquer(); return uniquer.get( assignDialect, static_cast(SDBMExprKind::DimId), position); } //===----------------------------------------------------------------------===// // SDBMSymbolExpr //===----------------------------------------------------------------------===// SDBMSymbolExpr SDBMSymbolExpr::get(SDBMDialect *dialect, unsigned position) { assert(dialect && "expected non-null dialect"); auto assignDialect = [dialect](detail::SDBMTermExprStorage *storage) { storage->dialect = dialect; }; StorageUniquer &uniquer = dialect->getUniquer(); return uniquer.get( assignDialect, static_cast(SDBMExprKind::SymbolId), position); } //===----------------------------------------------------------------------===// // SDBMConstantExpr //===----------------------------------------------------------------------===// SDBMConstantExpr SDBMConstantExpr::get(SDBMDialect *dialect, int64_t value) { assert(dialect && "expected non-null dialect"); auto assignCtx = [dialect](detail::SDBMConstantExprStorage *storage) { storage->dialect = dialect; }; StorageUniquer &uniquer = dialect->getUniquer(); return uniquer.get(assignCtx, value); } int64_t SDBMConstantExpr::getValue() const { return static_cast(impl)->constant; } //===----------------------------------------------------------------------===// // SDBMNegExpr //===----------------------------------------------------------------------===// SDBMNegExpr SDBMNegExpr::get(SDBMDirectExpr var) { assert(var && "expected non-null SDBM direct expression"); StorageUniquer &uniquer = var.getDialect()->getUniquer(); return uniquer.get(/*initFn=*/{}, var); } SDBMDirectExpr SDBMNegExpr::getVar() const { return static_cast(impl)->expr; } SDBMExpr mlir::ops_assertions::operator+(SDBMExpr lhs, SDBMExpr rhs) { if (auto folded = foldSumDiff(lhs, rhs)) return folded; assert(!(lhs.isa() && rhs.isa()) && "a sum of negated expressions is a negation of a sum of variables and " "not a correct SDBM"); // Fold (x - y) + (y - x) = 0. auto lhsDiff = lhs.dyn_cast(); auto rhsDiff = rhs.dyn_cast(); if (lhsDiff && rhsDiff) { if (lhsDiff.getLHS() == rhsDiff.getRHS() && lhsDiff.getRHS() == rhsDiff.getLHS()) return SDBMConstantExpr::get(lhs.getDialect(), 0); } // If LHS is a constant and RHS is not, swap the order to get into a supported // sum case. From now on, RHS must be a constant. auto lhsConstant = lhs.dyn_cast(); auto rhsConstant = rhs.dyn_cast(); if (!rhsConstant && lhsConstant) { std::swap(lhs, rhs); std::swap(lhsConstant, rhsConstant); } assert(rhsConstant && "at least one operand must be a constant"); // Constant-fold if LHS is also a constant. if (lhsConstant) return SDBMConstantExpr::get(lhs.getDialect(), lhsConstant.getValue() + rhsConstant.getValue()); return addConstant(lhs.cast(), rhsConstant.getValue()); } SDBMExpr mlir::ops_assertions::operator-(SDBMExpr lhs, SDBMExpr rhs) { // Fold x - x == 0. if (lhs == rhs) return SDBMConstantExpr::get(lhs.getDialect(), 0); // LHS and RHS may be constants. auto lhsConstant = lhs.dyn_cast(); auto rhsConstant = rhs.dyn_cast(); // Constant fold if both LHS and RHS are constants. if (lhsConstant && rhsConstant) return SDBMConstantExpr::get(lhs.getDialect(), lhsConstant.getValue() - rhsConstant.getValue()); // Replace a difference with a sum with a negated value if one of LHS and RHS // is a constant: // x - C == x + (-C); // C - x == -x + C. // This calls into operator+ for further simplification. if (rhsConstant) return lhs + (-rhsConstant); if (lhsConstant) return -rhs + lhsConstant; return buildDiffExpr(lhs.cast(), (-rhs).cast()); } SDBMExpr mlir::ops_assertions::stripe(SDBMExpr expr, SDBMExpr factor) { auto constantFactor = factor.cast(); assert(constantFactor.getValue() > 0 && "non-positive stripe"); // Fold x # 1 = x. if (constantFactor.getValue() == 1) return expr; return SDBMStripeExpr::get(expr.cast(), constantFactor); }