text: //===- AffineToStandard.cpp - Lower affine constructs to primitives -------===//
  //
  // 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
  //
  //===----------------------------------------------------------------------===//
  //
  // This file lowers affine constructs (If and For statements, AffineApply
  // operations) within a function into their standard If and For equivalent ops.
  //
  //===----------------------------------------------------------------------===//
  
  #include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
  
  #include "mlir/Dialect/AffineOps/AffineOps.h"
  #include "mlir/Dialect/LoopOps/LoopOps.h"
  #include "mlir/Dialect/StandardOps/Ops.h"
  #include "mlir/IR/AffineExprVisitor.h"
  #include "mlir/IR/BlockAndValueMapping.h"
  #include "mlir/IR/Builders.h"
  #include "mlir/IR/IntegerSet.h"
  #include "mlir/IR/MLIRContext.h"
  #include "mlir/Pass/Pass.h"
  #include "mlir/Support/Functional.h"
  #include "mlir/Transforms/DialectConversion.h"
  #include "mlir/Transforms/Passes.h"
  
  using namespace mlir;
  
  namespace {
  /// Visit affine expressions recursively and build the sequence of operations
  /// that correspond to it.  Visitation functions return an Value of the
  /// expression subtree they visited or `nullptr` on error.
  class AffineApplyExpander
      : public AffineExprVisitor<AffineApplyExpander, Value> {
  public:
    /// This internal class expects arguments to be non-null, checks must be
    /// performed at the call site.
    AffineApplyExpander(OpBuilder &builder, ValueRange dimValues,
                        ValueRange symbolValues, Location loc)
        : builder(builder), dimValues(dimValues), symbolValues(symbolValues),
          loc(loc) {}
  
    template <typename OpTy> Value buildBinaryExpr(AffineBinaryOpExpr expr) {
      auto lhs = visit(expr.getLHS());
      auto rhs = visit(expr.getRHS());
      if (!lhs || !rhs)
        return nullptr;
      auto op = builder.create<OpTy>(loc, lhs, rhs);
      return op.getResult();
    }
  
    Value visitAddExpr(AffineBinaryOpExpr expr) {
      return buildBinaryExpr<AddIOp>(expr);
    }
  
    Value visitMulExpr(AffineBinaryOpExpr expr) {
      return buildBinaryExpr<MulIOp>(expr);
    }
  
    /// Euclidean modulo operation: negative RHS is not allowed.
    /// Remainder of the euclidean integer division is always non-negative.
    ///
    /// Implemented as
    ///
    ///     a mod b =
    ///         let remainder = srem a, b;
    ///             negative = a < 0 in
    ///         select negative, remainder + b, remainder.
    Value visitModExpr(AffineBinaryOpExpr expr) {
      auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
      if (!rhsConst) {
        emitError(
            loc,
            "semi-affine expressions (modulo by non-const) are not supported");
        return nullptr;
      }
      if (rhsConst.getValue() <= 0) {
        emitError(loc, "modulo by non-positive value is not supported");
        return nullptr;
      }
  
      auto lhs = visit(expr.getLHS());
      auto rhs = visit(expr.getRHS());
      assert(lhs && rhs && "unexpected affine expr lowering failure");
  
      Value remainder = builder.create<SignedRemIOp>(loc, lhs, rhs);
      Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
      Value isRemainderNegative =
          builder.create<CmpIOp>(loc, CmpIPredicate::slt, remainder, zeroCst);
      Value correctedRemainder = builder.create<AddIOp>(loc, remainder, rhs);
      Value result = builder.create<SelectOp>(loc, isRemainderNegative,
                                              correctedRemainder, remainder);
      return result;
    }
  
    /// Floor division operation (rounds towards negative infinity).
    ///
    /// For positive divisors, it can be implemented without branching and with a
    /// single division operation as
    ///
    ///        a floordiv b =
    ///            let negative = a < 0 in
    ///            let absolute = negative ? -a - 1 : a in
    ///            let quotient = absolute / b in
    ///                negative ? -quotient - 1 : quotient
    Value visitFloorDivExpr(AffineBinaryOpExpr expr) {
      auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
      if (!rhsConst) {
        emitError(
            loc,
            "semi-affine expressions (division by non-const) are not supported");
        return nullptr;
      }
      if (rhsConst.getValue() <= 0) {
        emitError(loc, "division by non-positive value is not supported");
        return nullptr;
      }
  
      auto lhs = visit(expr.getLHS());
      auto rhs = visit(expr.getRHS());
      assert(lhs && rhs && "unexpected affine expr lowering failure");
  
      Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
      Value noneCst = builder.create<ConstantIndexOp>(loc, -1);
      Value negative =
          builder.create<CmpIOp>(loc, CmpIPredicate::slt, lhs, zeroCst);
      Value negatedDecremented = builder.create<SubIOp>(loc, noneCst, lhs);
      Value dividend =
          builder.create<SelectOp>(loc, negative, negatedDecremented, lhs);
      Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
      Value correctedQuotient = builder.create<SubIOp>(loc, noneCst, quotient);
      Value result =
          builder.create<SelectOp>(loc, negative, correctedQuotient, quotient);
      return result;
    }
  
    /// Ceiling division operation (rounds towards positive infinity).
    ///
    /// For positive divisors, it can be implemented without branching and with a
    /// single division operation as
    ///
    ///     a ceildiv b =
    ///         let negative = a <= 0 in
    ///         let absolute = negative ? -a : a - 1 in
    ///         let quotient = absolute / b in
    ///             negative ? -quotient : quotient + 1
    Value visitCeilDivExpr(AffineBinaryOpExpr expr) {
      auto rhsConst = expr.getRHS().dyn_cast<AffineConstantExpr>();
      if (!rhsConst) {
        emitError(loc) << "semi-affine expressions (division by non-const) are "
                          "not supported";
        return nullptr;
      }
      if (rhsConst.getValue() <= 0) {
        emitError(loc, "division by non-positive value is not supported");
        return nullptr;
      }
      auto lhs = visit(expr.getLHS());
      auto rhs = visit(expr.getRHS());
      assert(lhs && rhs && "unexpected affine expr lowering failure");
  
      Value zeroCst = builder.create<ConstantIndexOp>(loc, 0);
      Value oneCst = builder.create<ConstantIndexOp>(loc, 1);
      Value nonPositive =
          builder.create<CmpIOp>(loc, CmpIPredicate::sle, lhs, zeroCst);
      Value negated = builder.create<SubIOp>(loc, zeroCst, lhs);
      Value decremented = builder.create<SubIOp>(loc, lhs, oneCst);
      Value dividend =
          builder.create<SelectOp>(loc, nonPositive, negated, decremented);
      Value quotient = builder.create<SignedDivIOp>(loc, dividend, rhs);
      Value negatedQuotient = builder.create<SubIOp>(loc, zeroCst, quotient);
      Value incrementedQuotient = builder.create<AddIOp>(loc, quotient, oneCst);
      Value result = builder.create<SelectOp>(loc, nonPositive, negatedQuotient,
                                              incrementedQuotient);
      return result;
    }
  
    Value visitConstantExpr(AffineConstantExpr expr) {
      auto valueAttr =
          builder.getIntegerAttr(builder.getIndexType(), expr.getValue());
      auto op =
          builder.create<ConstantOp>(loc, builder.getIndexType(), valueAttr);
      return op.getResult();
    }
  
    Value visitDimExpr(AffineDimExpr expr) {
      assert(expr.getPosition() < dimValues.size() &&
             "affine dim position out of range");
      return dimValues[expr.getPosition()];
    }
  
    Value visitSymbolExpr(AffineSymbolExpr expr) {
      assert(expr.getPosition() < symbolValues.size() &&
             "symbol dim position out of range");
      return symbolValues[expr.getPosition()];
    }
  
  private:
    OpBuilder &builder;
    ValueRange dimValues;
    ValueRange symbolValues;
  
    Location loc;
  };
  } // namespace
  
  /// Create a sequence of operations that implement the `expr` applied to the
  /// given dimension and symbol values.
  mlir::Value mlir::expandAffineExpr(OpBuilder &builder, Location loc,
                                     AffineExpr expr, ValueRange dimValues,
                                     ValueRange symbolValues) {
    return AffineApplyExpander(builder, dimValues, symbolValues, loc).visit(expr);
  }
  
  /// Create a sequence of operations that implement the `affineMap` applied to
  /// the given `operands` (as it it were an AffineApplyOp).
  Optional<SmallVector<Value, 8>> static expandAffineMap(OpBuilder &builder,
                                                         Location loc,
                                                         AffineMap affineMap,
                                                         ValueRange operands) {
    auto numDims = affineMap.getNumDims();
    auto expanded = functional::map(
        [numDims, &builder, loc, operands](AffineExpr expr) {
          return expandAffineExpr(builder, loc, expr,
                                  operands.take_front(numDims),
                                  operands.drop_front(numDims));
        },
        affineMap.getResults());
    if (llvm::all_of(expanded, [](Value v) { return v; }))
      return expanded;
    return None;
  }
  
  /// Given a range of values, emit the code that reduces them with "min" or "max"
  /// depending on the provided comparison predicate.  The predicate defines which
  /// comparison to perform, "lt" for "min", "gt" for "max" and is used for the
  /// `cmpi` operation followed by the `select` operation:
  ///
  ///   %cond   = cmpi "predicate" %v0, %v1
  ///   %result = select %cond, %v0, %v1
  ///
  /// Multiple values are scanned in a linear sequence.  This creates a data
  /// dependences that wouldn't exist in a tree reduction, but is easier to
  /// recognize as a reduction by the subsequent passes.
  static Value buildMinMaxReductionSeq(Location loc, CmpIPredicate predicate,
                                       ValueRange values, OpBuilder &builder) {
    assert(!llvm::empty(values) && "empty min/max chain");
  
    auto valueIt = values.begin();
    Value value = *valueIt++;
    for (; valueIt != values.end(); ++valueIt) {
      auto cmpOp = builder.create<CmpIOp>(loc, predicate, value, *valueIt);
      value = builder.create<SelectOp>(loc, cmpOp.getResult(), value, *valueIt);
    }
  
    return value;
  }
  
  /// Emit instructions that correspond to computing the maximum value amoung the
  /// values of a (potentially) multi-output affine map applied to `operands`.
  static Value lowerAffineMapMax(OpBuilder &builder, Location loc, AffineMap map,
                                 ValueRange operands) {
    if (auto values = expandAffineMap(builder, loc, map, operands))
      return buildMinMaxReductionSeq(loc, CmpIPredicate::sgt, *values, builder);
    return nullptr;
  }
  
  /// Emit instructions that correspond to computing the minimum value amoung the
  /// values of a (potentially) multi-output affine map applied to `operands`.
  static Value lowerAffineMapMin(OpBuilder &builder, Location loc, AffineMap map,
                                 ValueRange operands) {
    if (auto values = expandAffineMap(builder, loc, map, operands))
      return buildMinMaxReductionSeq(loc, CmpIPredicate::slt, *values, builder);
    return nullptr;
  }
  
  /// Emit instructions that correspond to the affine map in the upper bound
  /// applied to the respective operands, and compute the minimum value across
  /// the results.
  Value mlir::lowerAffineUpperBound(AffineForOp op, OpBuilder &builder) {
    return lowerAffineMapMin(builder, op.getLoc(), op.getUpperBoundMap(),
                             op.getUpperBoundOperands());
  }
  
  /// Emit instructions that correspond to the affine map in the lower bound
  /// applied to the respective operands, and compute the maximum value across
  /// the results.
  Value mlir::lowerAffineLowerBound(AffineForOp op, OpBuilder &builder) {
    return lowerAffineMapMax(builder, op.getLoc(), op.getLowerBoundMap(),
                             op.getLowerBoundOperands());
  }
  
  namespace {
  class AffineMinLowering : public OpRewritePattern<AffineMinOp> {
  public:
    using OpRewritePattern<AffineMinOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineMinOp op,
                                       PatternRewriter &rewriter) const override {
      Value reduced =
          lowerAffineMapMin(rewriter, op.getLoc(), op.map(), op.operands());
      if (!reduced)
        return matchFailure();
  
      rewriter.replaceOp(op, reduced);
      return matchSuccess();
    }
  };
  
  class AffineMaxLowering : public OpRewritePattern<AffineMaxOp> {
  public:
    using OpRewritePattern<AffineMaxOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineMaxOp op,
                                       PatternRewriter &rewriter) const override {
      Value reduced =
          lowerAffineMapMax(rewriter, op.getLoc(), op.map(), op.operands());
      if (!reduced)
        return matchFailure();
  
      rewriter.replaceOp(op, reduced);
      return matchSuccess();
    }
  };
  
  /// Affine terminators are removed.
  class AffineTerminatorLowering : public OpRewritePattern<AffineTerminatorOp> {
  public:
    using OpRewritePattern<AffineTerminatorOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineTerminatorOp op,
                                       PatternRewriter &rewriter) const override {
      rewriter.replaceOpWithNewOp<loop::TerminatorOp>(op);
      return matchSuccess();
    }
  };
  
  class AffineForLowering : public OpRewritePattern<AffineForOp> {
  public:
    using OpRewritePattern<AffineForOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineForOp op,
                                       PatternRewriter &rewriter) const override {
      Location loc = op.getLoc();
      Value lowerBound = lowerAffineLowerBound(op, rewriter);
      Value upperBound = lowerAffineUpperBound(op, rewriter);
      Value step = rewriter.create<ConstantIndexOp>(loc, op.getStep());
      auto f = rewriter.create<loop::ForOp>(loc, lowerBound, upperBound, step);
      f.region().getBlocks().clear();
      rewriter.inlineRegionBefore(op.region(), f.region(), f.region().end());
      rewriter.eraseOp(op);
      return matchSuccess();
    }
  };
  
  class AffineIfLowering : public OpRewritePattern<AffineIfOp> {
  public:
    using OpRewritePattern<AffineIfOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineIfOp op,
                                       PatternRewriter &rewriter) const override {
      auto loc = op.getLoc();
  
      // Now we just have to handle the condition logic.
      auto integerSet = op.getIntegerSet();
      Value zeroConstant = rewriter.create<ConstantIndexOp>(loc, 0);
      SmallVector<Value, 8> operands(op.getOperands());
      auto operandsRef = llvm::makeArrayRef(operands);
  
      // Calculate cond as a conjunction without short-circuiting.
      Value cond = nullptr;
      for (unsigned i = 0, e = integerSet.getNumConstraints(); i < e; ++i) {
        AffineExpr constraintExpr = integerSet.getConstraint(i);
        bool isEquality = integerSet.isEq(i);
  
        // Build and apply an affine expression
        auto numDims = integerSet.getNumDims();
        Value affResult = expandAffineExpr(rewriter, loc, constraintExpr,
                                           operandsRef.take_front(numDims),
                                           operandsRef.drop_front(numDims));
        if (!affResult)
          return matchFailure();
        auto pred = isEquality ? CmpIPredicate::eq : CmpIPredicate::sge;
        Value cmpVal =
            rewriter.create<CmpIOp>(loc, pred, affResult, zeroConstant);
        cond =
            cond ? rewriter.create<AndOp>(loc, cond, cmpVal).getResult() : cmpVal;
      }
      cond = cond ? cond
                  : rewriter.create<ConstantIntOp>(loc, /*value=*/1, /*width=*/1);
  
      bool hasElseRegion = !op.elseRegion().empty();
      auto ifOp = rewriter.create<loop::IfOp>(loc, cond, hasElseRegion);
      rewriter.inlineRegionBefore(op.thenRegion(), &ifOp.thenRegion().back());
      ifOp.thenRegion().back().erase();
      if (hasElseRegion) {
        rewriter.inlineRegionBefore(op.elseRegion(), &ifOp.elseRegion().back());
        ifOp.elseRegion().back().erase();
      }
  
      // Ok, we're done!
      rewriter.eraseOp(op);
      return matchSuccess();
    }
  };
  
  /// Convert an "affine.apply" operation into a sequence of arithmetic
  /// operations using the StandardOps dialect.
  class AffineApplyLowering : public OpRewritePattern<AffineApplyOp> {
  public:
    using OpRewritePattern<AffineApplyOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineApplyOp op,
                                       PatternRewriter &rewriter) const override {
      auto maybeExpandedMap =
          expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(),
                          llvm::to_vector<8>(op.getOperands()));
      if (!maybeExpandedMap)
        return matchFailure();
      rewriter.replaceOp(op, *maybeExpandedMap);
      return matchSuccess();
    }
  };
  
  /// Apply the affine map from an 'affine.load' operation to its operands, and
  /// feed the results to a newly created 'std.load' operation (which replaces the
  /// original 'affine.load').
  class AffineLoadLowering : public OpRewritePattern<AffineLoadOp> {
  public:
    using OpRewritePattern<AffineLoadOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineLoadOp op,
                                       PatternRewriter &rewriter) const override {
      // Expand affine map from 'affineLoadOp'.
      SmallVector<Value, 8> indices(op.getMapOperands());
      auto resultOperands =
          expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
      if (!resultOperands)
        return matchFailure();
  
      // Build std.load memref[expandedMap.results].
      rewriter.replaceOpWithNewOp<LoadOp>(op, op.getMemRef(), *resultOperands);
      return matchSuccess();
    }
  };
  
  /// Apply the affine map from an 'affine.prefetch' operation to its operands,
  /// and feed the results to a newly created 'std.prefetch' operation (which
  /// replaces the original 'affine.prefetch').
  class AffinePrefetchLowering : public OpRewritePattern<AffinePrefetchOp> {
  public:
    using OpRewritePattern<AffinePrefetchOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffinePrefetchOp op,
                                       PatternRewriter &rewriter) const override {
      // Expand affine map from 'affinePrefetchOp'.
      SmallVector<Value, 8> indices(op.getMapOperands());
      auto resultOperands =
          expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
      if (!resultOperands)
        return matchFailure();
  
      // Build std.prefetch memref[expandedMap.results].
      rewriter.replaceOpWithNewOp<PrefetchOp>(
          op, op.memref(), *resultOperands, op.isWrite(),
          op.localityHint().getZExtValue(), op.isDataCache());
      return matchSuccess();
    }
  };
  
  /// Apply the affine map from an 'affine.store' operation to its operands, and
  /// feed the results to a newly created 'std.store' operation (which replaces
  /// the original 'affine.store').
  class AffineStoreLowering : public OpRewritePattern<AffineStoreOp> {
  public:
    using OpRewritePattern<AffineStoreOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineStoreOp op,
                                       PatternRewriter &rewriter) const override {
      // Expand affine map from 'affineStoreOp'.
      SmallVector<Value, 8> indices(op.getMapOperands());
      auto maybeExpandedMap =
          expandAffineMap(rewriter, op.getLoc(), op.getAffineMap(), indices);
      if (!maybeExpandedMap)
        return matchFailure();
  
      // Build std.store valueToStore, memref[expandedMap.results].
      rewriter.replaceOpWithNewOp<StoreOp>(op, op.getValueToStore(),
                                           op.getMemRef(), *maybeExpandedMap);
      return matchSuccess();
    }
  };
  
  /// Apply the affine maps from an 'affine.dma_start' operation to each of their
  /// respective map operands, and feed the results to a newly created
  /// 'std.dma_start' operation (which replaces the original 'affine.dma_start').
  class AffineDmaStartLowering : public OpRewritePattern<AffineDmaStartOp> {
  public:
    using OpRewritePattern<AffineDmaStartOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineDmaStartOp op,
                                       PatternRewriter &rewriter) const override {
      SmallVector<Value, 8> operands(op.getOperands());
      auto operandsRef = llvm::makeArrayRef(operands);
  
      // Expand affine map for DMA source memref.
      auto maybeExpandedSrcMap = expandAffineMap(
          rewriter, op.getLoc(), op.getSrcMap(),
          operandsRef.drop_front(op.getSrcMemRefOperandIndex() + 1));
      if (!maybeExpandedSrcMap)
        return matchFailure();
      // Expand affine map for DMA destination memref.
      auto maybeExpandedDstMap = expandAffineMap(
          rewriter, op.getLoc(), op.getDstMap(),
          operandsRef.drop_front(op.getDstMemRefOperandIndex() + 1));
      if (!maybeExpandedDstMap)
        return matchFailure();
      // Expand affine map for DMA tag memref.
      auto maybeExpandedTagMap = expandAffineMap(
          rewriter, op.getLoc(), op.getTagMap(),
          operandsRef.drop_front(op.getTagMemRefOperandIndex() + 1));
      if (!maybeExpandedTagMap)
        return matchFailure();
  
      // Build std.dma_start operation with affine map results.
      rewriter.replaceOpWithNewOp<DmaStartOp>(
          op, op.getSrcMemRef(), *maybeExpandedSrcMap, op.getDstMemRef(),
          *maybeExpandedDstMap, op.getNumElements(), op.getTagMemRef(),
          *maybeExpandedTagMap, op.getStride(), op.getNumElementsPerStride());
      return matchSuccess();
    }
  };
  
  /// Apply the affine map from an 'affine.dma_wait' operation tag memref,
  /// and feed the results to a newly created 'std.dma_wait' operation (which
  /// replaces the original 'affine.dma_wait').
  class AffineDmaWaitLowering : public OpRewritePattern<AffineDmaWaitOp> {
  public:
    using OpRewritePattern<AffineDmaWaitOp>::OpRewritePattern;
  
    PatternMatchResult matchAndRewrite(AffineDmaWaitOp op,
                                       PatternRewriter &rewriter) const override {
      // Expand affine map for DMA tag memref.
      SmallVector<Value, 8> indices(op.getTagIndices());
      auto maybeExpandedTagMap =
          expandAffineMap(rewriter, op.getLoc(), op.getTagMap(), indices);
      if (!maybeExpandedTagMap)
        return matchFailure();
  
      // Build std.dma_wait operation with affine map results.
      rewriter.replaceOpWithNewOp<DmaWaitOp>(
          op, op.getTagMemRef(), *maybeExpandedTagMap, op.getNumElements());
      return matchSuccess();
    }
  };
  
  } // end namespace
  
  void mlir::populateAffineToStdConversionPatterns(
      OwningRewritePatternList &patterns, MLIRContext *ctx) {
    // clang-format off
    patterns.insert<
        AffineApplyLowering,
        AffineDmaStartLowering,
        AffineDmaWaitLowering,
        AffineLoadLowering,
        AffineMinLowering,
        AffineMaxLowering,
        AffinePrefetchLowering,
        AffineStoreLowering,
        AffineForLowering,
        AffineIfLowering,
        AffineTerminatorLowering>(ctx);
    // clang-format on
  }
  
  namespace {
  class LowerAffinePass : public FunctionPass<LowerAffinePass> {
    void runOnFunction() override {
      OwningRewritePatternList patterns;
      populateAffineToStdConversionPatterns(patterns, &getContext());
      ConversionTarget target(getContext());
      target.addLegalDialect<loop::LoopOpsDialect, StandardOpsDialect>();
      if (failed(applyPartialConversion(getFunction(), target, patterns)))
        signalPassFailure();
    }
  };
  } // namespace
  
  /// Lowers If and For operations within a function into their lower level CFG
  /// equivalent blocks.
  std::unique_ptr<OpPassBase<FuncOp>> mlir::createLowerAffinePass() {
    return std::make_unique<LowerAffinePass>();
  }
  
  static PassRegistration<LowerAffinePass>
      pass("lower-affine",
           "Lower If, For, AffineApply operations to primitive equivalents");
  