return const_cast<Block *>(this)->getFunction();
}
+ /// Insert this block (which must not already be in a function) right before
+ /// the specified block.
+ void insertBefore(Block *block);
+
+ /// Unlink this Block from its Function and delete it.
+ void eraseFromFunction();
+
//===--------------------------------------------------------------------===//
// Block argument management
//===--------------------------------------------------------------------===//
// Other
//===--------------------------------------------------------------------===//
- /// Unlink this Block from its Function and delete it.
- void eraseFromFunction();
-
/// Split the block into two blocks before the specified instruction or
/// iterator.
///
/// Note that all instructions BEFORE the specified iterator stay as part of
- /// the original basic block, an unconditional branch is added to the original
- /// block (going to the new block), and the rest of the instructions in the
- /// original block are moved to the new block, including the old terminator.
- /// The newly formed Block is returned.
+ /// the original basic block, and the rest of the instructions in the original
+ /// block are moved to the new block, including the old terminator. The
+ /// original block is left without a terminator.
///
- /// This function invalidates the specified iterator.
+ /// The newly formed Block is returned, and the specified iterator is
+ /// invalidated.
Block *splitBlock(iterator splitBefore);
Block *splitBlock(Instruction *splitBeforeInst) {
return splitBlock(iterator(splitBeforeInst));
ArrayRef<NamedAttribute> attrs = {});
~Function();
- Kind getKind() const { return (Kind)nameAndKind.getInt(); }
+ Kind getKind() const { return nameAndKind.getInt(); }
+ void setKind(Kind kind) { nameAndKind.setInt(kind); }
bool isCFG() const { return getKind() == Kind::CFGFunc; }
bool isML() const { return getKind() == Kind::MLFunc; }
/// Creates a pass which composes all affine maps applied to loads and stores.
FunctionPass *createComposeAffineMapsPass();
-/// Replaces all ML functions in the module with equivalent CFG functions.
-/// Function references are appropriately patched to refer to the newly
-/// generated CFG functions.
-ModulePass *createConvertToCFGPass();
+/// Lowers IfInst and ForInst to the equivalent lower level CFG structures.
+FunctionPass *createLowerIfAndForPass();
/// Creates a pass to perform tiling on loop nests.
FunctionPass *createLoopTilingPass();
/// corresponding hooks and terminates upon error encountered.
PassResult FunctionPass::runOnModule(Module *m) {
for (auto &fn : *m) {
+ // All function passes ignore external functions.
+ if (fn.empty())
+ continue;
+
if (runOnFunction(&fn))
return failure();
}
enum { nameSentinel = ~0U };
- void printBlockName(const Block *block) { os << "^bb" << getBlockID(block); }
+ void printBlockName(const Block *block) {
+ auto id = getBlockID(block);
+ if (id != ~0U)
+ os << "^bb" << id;
+ else
+ os << "^INVALIDBLOCK";
+ }
unsigned getBlockID(const Block *block) {
auto it = blockIDs.find(block);
- assert(it != blockIDs.end() && "Block not in this function?");
- return it->second;
+ return it != blockIDs.end() ? it->second : ~0U;
}
void printSuccessorAndUseList(const OperationInst *term,
} else {
// We want to print the predecessors in increasing numeric order, not in
// whatever order the use-list is in, so gather and sort them.
- SmallVector<unsigned, 4> predIDs;
+ SmallVector<std::pair<unsigned, const Block *>, 4> predIDs;
for (auto *pred : block->getPredecessors())
- predIDs.push_back(getBlockID(pred));
+ predIDs.push_back({getBlockID(pred), pred});
llvm::array_pod_sort(predIDs.begin(), predIDs.end());
os << "\t// " << predIDs.size() << " preds: ";
- interleaveComma(predIDs, [&](unsigned predID) { os << "^bb" << predID; });
+ interleaveComma(predIDs, [&](std::pair<unsigned, const Block *> pred) {
+ printBlockName(pred.second);
+ });
}
os << '\n';
}
#include "mlir/IR/Block.h"
#include "mlir/IR/Builders.h"
-#include "mlir/IR/BuiltinOps.h"
using namespace mlir;
Block::~Block() {
return nullptr;
}
+/// Insert this block (which must not already be in a function) right before
+/// the specified block.
+void Block::insertBefore(Block *block) {
+ assert(!getParent() && "already inserted into a block!");
+ assert(block->getParent() && "cannot insert before a block without a parent");
+ block->getParent()->getBlocks().insert(BlockList::iterator(block), this);
+}
+
+/// Unlink this Block from its Function and delete it.
+void Block::eraseFromFunction() {
+ assert(getFunction() && "Block has no parent");
+ getFunction()->getBlocks().erase(this);
+}
+
/// Returns 'inst' if 'inst' lies in this block, or otherwise finds the
/// ancestor instruction of 'inst' that lies in this block. Returns nullptr if
/// the latter fails.
// Other
//===----------------------------------------------------------------------===//
-/// Unlink this Block from its Function and delete it.
-void Block::eraseFromFunction() {
- assert(getFunction() && "Block has no parent");
- getFunction()->getBlocks().erase(this);
-}
-
-/// Split the basic block into two basic blocks before the specified
-/// instruction or iterator.
+/// Split the block into two blocks before the specified instruction or
+/// iterator.
///
/// Note that all instructions BEFORE the specified iterator stay as part of
-/// the original basic block, an unconditional branch is added to the original
-/// block (going to the new block), and the rest of the instructions in the
-/// original block are moved to the new BB, including the old terminator. The
-/// newly formed Block is returned.
+/// the original basic block, and the rest of the instructions in the original
+/// block are moved to the new block, including the old terminator. The
+/// original block is left without a terminator.
///
-/// This function invalidates the specified iterator.
+/// The newly formed Block is returned, and the specified iterator is
+/// invalidated.
Block *Block::splitBlock(iterator splitBefore) {
// Start by creating a new basic block, and insert it immediate after this
// one in the containing function.
auto newBB = new Block();
getFunction()->getBlocks().insert(++Function::iterator(this), newBB);
- auto branchLoc =
- splitBefore == end() ? getTerminator()->getLoc() : splitBefore->getLoc();
// Move all of the operations from the split point to the end of the function
// into the new block.
newBB->getInstructions().splice(newBB->end(), getInstructions(), splitBefore,
end());
-
- // Create an unconditional branch to the new block, and move our terminator
- // to the new block.
- FuncBuilder(this).create<BranchOp>(branchLoc, newBB);
return newBB;
}
+++ /dev/null
-//===- ConvertToCFG.cpp - ML function to CFG function conversion ----------===//
-//
-// 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.
-// =============================================================================
-//
-// This file implements APIs to convert ML functions into CFG functions.
-//
-//===----------------------------------------------------------------------===//
-
-#include "mlir/IR/Builders.h"
-#include "mlir/IR/BuiltinOps.h"
-#include "mlir/IR/InstVisitor.h"
-#include "mlir/IR/MLIRContext.h"
-#include "mlir/IR/Module.h"
-#include "mlir/Pass.h"
-#include "mlir/StandardOps/StandardOps.h"
-#include "mlir/Support/Functional.h"
-#include "mlir/Transforms/Passes.h"
-#include "mlir/Transforms/Utils.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/Support/CommandLine.h"
-using namespace mlir;
-
-//===----------------------------------------------------------------------===//
-// ML function converter
-//===----------------------------------------------------------------------===//
-
-namespace {
-// Generates CFG function equivalent to the given ML function.
-class FunctionConverter : public InstVisitor<FunctionConverter> {
-public:
- FunctionConverter(Function *cfgFunc) : cfgFunc(cfgFunc), builder(cfgFunc) {}
- Function *convert(Function *mlFunc);
-
- void visitForInst(ForInst *forInst);
- void visitIfInst(IfInst *ifInst);
- void visitOperationInst(OperationInst *opInst);
-
-private:
- Value *getConstantIndexValue(int64_t value);
- void visitBlock(Block *Block);
- Value *buildMinMaxReductionSeq(
- Location loc, CmpIPredicate predicate,
- llvm::iterator_range<OperationInst::result_iterator> values);
-
- Function *cfgFunc;
- FuncBuilder builder;
-
- // Mapping between original Values and lowered Values.
- llvm::DenseMap<const Value *, Value *> valueRemapping;
-};
-} // end anonymous namespace
-
-// Return a vector of OperationInst's arguments as Values. For each
-// instruction operands, represented as Value, lookup its Value conterpart in
-// the valueRemapping table.
-static llvm::SmallVector<mlir::Value *, 4>
-operandsAs(Instruction *opInst,
- const llvm::DenseMap<const Value *, Value *> &valueRemapping) {
- llvm::SmallVector<Value *, 4> operands;
- for (const Value *operand : opInst->getOperands()) {
- assert(valueRemapping.count(operand) != 0 && "operand is not defined");
- operands.push_back(valueRemapping.lookup(operand));
- }
- return operands;
-}
-
-// Convert an operation instruction into an operation instruction.
-//
-// The operation description (name, number and types of operands or results)
-// remains the same but the values must be updated to be Values. Update the
-// mapping Value->Value as the conversion is performed. The operation
-// instruction is appended to current block (end of SESE region).
-void FunctionConverter::visitOperationInst(OperationInst *opInst) {
- // Set up basic operation state (context, name, operands).
- OperationState state(cfgFunc->getContext(), opInst->getLoc(),
- opInst->getName());
- state.addOperands(operandsAs(opInst, valueRemapping));
-
- // Set up operation return types. The corresponding Values will become
- // available after the operation is created.
- state.addTypes(functional::map(
- [](Value *result) { return result->getType(); }, opInst->getResults()));
-
- // Copy attributes.
- for (auto attr : opInst->getAttrs()) {
- state.addAttribute(attr.first.strref(), attr.second);
- }
-
- auto op = builder.createOperation(state);
-
- // Make results of the operation accessible to the following operations
- // through remapping.
- assert(opInst->getNumResults() == op->getNumResults());
- for (unsigned i = 0, n = opInst->getNumResults(); i < n; ++i) {
- valueRemapping.insert(
- std::make_pair(opInst->getResult(i), op->getResult(i)));
- }
-}
-
-// Create a Value for the given integer constant of index type.
-Value *FunctionConverter::getConstantIndexValue(int64_t value) {
- auto op = builder.create<ConstantIndexOp>(builder.getUnknownLoc(), value);
- return op->getResult();
-}
-
-// Visit all instructions in the given instruction block.
-void FunctionConverter::visitBlock(Block *Block) {
- for (auto &inst : *Block)
- this->visit(&inst);
-}
-
-// 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.
-Value *FunctionConverter::buildMinMaxReductionSeq(
- Location loc, CmpIPredicate predicate,
- llvm::iterator_range<OperationInst::result_iterator> values) {
- 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);
- auto selectOp =
- builder.create<SelectOp>(loc, cmpOp->getResult(), value, *valueIt);
- value = selectOp->getResult();
- }
-
- return value;
-}
-
-// Convert a "for" loop to a flow of blocks.
-//
-// Create an SESE region for the loop (including its body) and append it to the
-// end of the current region. The loop region consists of the initialization
-// block that sets up the initial value of the loop induction variable (%iv) and
-// computes the loop bounds that are loop-invariant in MLFunctions; the
-// condition block that checks the exit condition of the loop; the body SESE
-// region; and the end block that post-dominates the loop. The end block of the
-// loop becomes the new end of the current SESE region. The body of the loop is
-// constructed recursively after starting a new region (it may be, for example,
-// a nested loop). Induction variable modification is appended to the body SESE
-// region that always loops back to the condition block.
-//
-// +--------------------------------+
-// | <end of current SESE region> |
-// | <current insertion point> |
-// | br init |
-// +--------------------------------+
-// |
-// v
-// +--------------------------------+
-// | init: |
-// | <start of loop SESE region> |
-// | <compute initial %iv value> |
-// | br cond(%iv) |
-// +--------------------------------+
-// |
-// -------| |
-// | v v
-// | +--------------------------------+
-// | | cond(%iv): |
-// | | <compare %iv to upper bound> |
-// | | cond_br %r, body, end |
-// | +--------------------------------+
-// | | |
-// | | -------------|
-// | v |
-// | +--------------------------------+ |
-// | | body: | |
-// | | <body SESE region start> | |
-// | | <...> | |
-// | +--------------------------------+ |
-// | | |
-// | ... <SESE region of the body> |
-// | | |
-// | v |
-// | +--------------------------------+ |
-// | | body-end: | |
-// | | <body SESE region end> | |
-// | | %new_iv =<add step to %iv> | |
-// | | br cond(%new_iv) | |
-// | +--------------------------------+ |
-// | | |
-// |----------- |--------------------
-// v
-// +--------------------------------+
-// | end: |
-// | <end of loop SESE region> |
-// | <new insertion point> |
-// +--------------------------------+
-//
-void FunctionConverter::visitForInst(ForInst *forInst) {
- // First, store the loop insertion location so that we can go back to it after
- // creating the new blocks (block creation updates the insertion point).
- Block *loopInsertionPoint = builder.getInsertionBlock();
-
- // Create blocks so that they appear in more human-readable order in the
- // output.
- Block *loopInitBlock = builder.createBlock();
- Block *loopConditionBlock = builder.createBlock();
- Block *loopBodyFirstBlock = builder.createBlock();
-
- // At the loop insertion location, branch immediately to the loop init block.
- builder.setInsertionPointToEnd(loopInsertionPoint);
- builder.create<BranchOp>(builder.getUnknownLoc(), loopInitBlock);
-
- // The loop condition block has an argument for loop induction variable.
- // Create it upfront and make the loop induction variable -> block
- // argument remapping available to the following instructions. ForInstruction
- // is-a Value corresponding to the loop induction variable.
- builder.setInsertionPointToEnd(loopConditionBlock);
- Value *iv = loopConditionBlock->addArgument(builder.getIndexType());
- valueRemapping.insert(std::make_pair(forInst, iv));
-
- // Recursively construct loop body region.
- // Walking manually because we need custom logic before and after traversing
- // the list of children.
- builder.setInsertionPointToEnd(loopBodyFirstBlock);
- visitBlock(forInst->getBody());
-
- // Builder point is currently at the last block of the loop body. Append the
- // induction variable stepping to this block and branch back to the exit
- // condition block. Construct an affine map f : (x -> x+step) and apply this
- // map to the induction variable.
- auto affStep = builder.getAffineConstantExpr(forInst->getStep());
- auto affDim = builder.getAffineDimExpr(0);
- auto affStepMap = builder.getAffineMap(1, 0, {affDim + affStep}, {});
- auto stepOp =
- builder.create<AffineApplyOp>(forInst->getLoc(), affStepMap, iv);
- Value *nextIvValue = stepOp->getResult(0);
- builder.create<BranchOp>(builder.getUnknownLoc(), loopConditionBlock,
- nextIvValue);
-
- // Create post-loop block here so that it appears after all loop body blocks.
- Block *postLoopBlock = builder.createBlock();
-
- builder.setInsertionPointToEnd(loopInitBlock);
- // Compute loop bounds using affine_apply after remapping its operands.
- auto remapOperands = [this](const Value *value) -> Value * {
- return valueRemapping.lookup(value);
- };
- auto operands =
- functional::map(remapOperands, forInst->getLowerBoundOperands());
- auto lbAffineApply = builder.create<AffineApplyOp>(
- forInst->getLoc(), forInst->getLowerBoundMap(), operands);
- Value *lowerBound = buildMinMaxReductionSeq(
- forInst->getLoc(), CmpIPredicate::SGT, lbAffineApply->getResults());
- operands = functional::map(remapOperands, forInst->getUpperBoundOperands());
- auto ubAffineApply = builder.create<AffineApplyOp>(
- forInst->getLoc(), forInst->getUpperBoundMap(), operands);
- Value *upperBound = buildMinMaxReductionSeq(
- forInst->getLoc(), CmpIPredicate::SLT, ubAffineApply->getResults());
- builder.create<BranchOp>(builder.getUnknownLoc(), loopConditionBlock,
- lowerBound);
-
- builder.setInsertionPointToEnd(loopConditionBlock);
- auto comparisonOp = builder.create<CmpIOp>(
- forInst->getLoc(), CmpIPredicate::SLT, iv, upperBound);
- auto comparisonResult = comparisonOp->getResult();
- builder.create<CondBranchOp>(builder.getUnknownLoc(), comparisonResult,
- loopBodyFirstBlock, ArrayRef<Value *>(),
- postLoopBlock, ArrayRef<Value *>());
-
- // Finally, make sure building can continue by setting the post-loop block
- // (end of loop SESE region) as the insertion point.
- builder.setInsertionPointToEnd(postLoopBlock);
-}
-
-// Convert an "if" instruction into a flow of basic blocks.
-//
-// Create an SESE region for the if instruction (including its "then" and
-// optional "else" instruction blocks) and append it to the end of the current
-// region. The conditional region consists of a sequence of condition-checking
-// blocks that implement the short-circuit scheme, followed by a "then" SESE
-// region and an "else" SESE region, and the continuation block that
-// post-dominates all blocks of the "if" instruction. The flow of blocks that
-// correspond to the "then" and "else" clauses are constructed recursively,
-// enabling easy nesting of "if" instructions and if-then-else-if chains.
-//
-// +--------------------------------+
-// | <end of current SESE region> |
-// | <current insertion point> |
-// | %zero = constant 0 : index |
-// | %v = affine_apply #expr1(%ops) |
-// | %c = cmpi "sge" %v, %zero |
-// | cond_br %c, %next, %else |
-// +--------------------------------+
-// | |
-// | --------------|
-// v |
-// +--------------------------------+ |
-// | next: | |
-// | <repeat the check for expr2> | |
-// | cond_br %c, %next2, %else | |
-// +--------------------------------+ |
-// | | |
-// ... --------------|
-// | <Per-expression checks> |
-// v |
-// +--------------------------------+ |
-// | last: | |
-// | <repeat the check for exprN> | |
-// | cond_br %c, %then, %else | |
-// +--------------------------------+ |
-// | | |
-// | --------------|
-// v |
-// +--------------------------------+ |
-// | then: | |
-// | <then SESE region> | |
-// +--------------------------------+ |
-// | |
-// ... <SESE region of "then"> |
-// | |
-// v |
-// +--------------------------------+ |
-// | then_end: | |
-// | <then SESE region end> | |
-// | br continue | |
-// +--------------------------------+ |
-// | |
-// |---------- |-------------
-// | V
-// | +--------------------------------+
-// | | else: |
-// | | <else SESE region> |
-// | +--------------------------------+
-// | |
-// | ... <SESE region of "else">
-// | |
-// | v
-// | +--------------------------------+
-// | | else_end: |
-// | | <else SESE region> |
-// | | br continue |
-// | +--------------------------------+
-// | |
-// ------| |
-// v v
-// +--------------------------------+
-// | continue: |
-// | <end of "if" SESE region> |
-// | <new insertion point> |
-// +--------------------------------+
-//
-void FunctionConverter::visitIfInst(IfInst *ifInst) {
- assert(ifInst != nullptr);
-
- auto integerSet = ifInst->getCondition().getIntegerSet();
-
- // Create basic blocks for the 'then' block and for the 'else' block.
- // Although 'else' block may be empty in absence of an 'else' clause, create
- // it anyway for the sake of consistency and output IR readability. Also
- // create extra blocks for condition checking to prepare for short-circuit
- // logic: conditions in the 'if' instruction are conjunctive, so we can jump
- // to the false branch as soon as one condition fails. `cond_br` requires
- // another block as a target when the condition is true, and that block will
- // contain the next condition.
- Block *ifInsertionBlock = builder.getInsertionBlock();
- SmallVector<Block *, 4> ifConditionExtraBlocks;
- unsigned numConstraints = integerSet.getNumConstraints();
- ifConditionExtraBlocks.reserve(numConstraints - 1);
- for (unsigned i = 0, e = numConstraints - 1; i < e; ++i) {
- ifConditionExtraBlocks.push_back(builder.createBlock());
- }
- Block *thenBlock = builder.createBlock();
- Block *elseBlock = builder.createBlock();
- builder.setInsertionPointToEnd(ifInsertionBlock);
-
- // Implement short-circuit logic. For each affine expression in the 'if'
- // condition, convert it into an affine map and call `affine_apply` to obtain
- // the resulting value. Perform the equality or the greater-than-or-equality
- // test between this value and zero depending on the equality flag of the
- // condition. If the test fails, jump immediately to the false branch, which
- // may be the else block if it is present or the continuation block otherwise.
- // If the test succeeds, jump to the next block testing testing the next
- // conjunct of the condition in the similar way. When all conjuncts have been
- // handled, jump to the 'then' block instead.
- Value *zeroConstant = getConstantIndexValue(0);
- ifConditionExtraBlocks.push_back(thenBlock);
- for (auto tuple :
- llvm::zip(integerSet.getConstraints(), integerSet.getEqFlags(),
- ifConditionExtraBlocks)) {
- AffineExpr constraintExpr = std::get<0>(tuple);
- bool isEquality = std::get<1>(tuple);
- Block *nextBlock = std::get<2>(tuple);
-
- // Build and apply an affine map.
- auto affineMap =
- builder.getAffineMap(integerSet.getNumDims(),
- integerSet.getNumSymbols(), constraintExpr, {});
- auto affineApplyOp = builder.create<AffineApplyOp>(
- ifInst->getLoc(), affineMap, operandsAs(ifInst, valueRemapping));
- Value *affResult = affineApplyOp->getResult(0);
-
- // Compare the result of the apply and branch.
- auto comparisonOp = builder.create<CmpIOp>(
- ifInst->getLoc(), isEquality ? CmpIPredicate::EQ : CmpIPredicate::SGE,
- affResult, zeroConstant);
- builder.create<CondBranchOp>(ifInst->getLoc(), comparisonOp->getResult(),
- nextBlock, /*trueArgs*/ ArrayRef<Value *>(),
- elseBlock,
- /*falseArgs*/ ArrayRef<Value *>());
- builder.setInsertionPointToEnd(nextBlock);
- }
- ifConditionExtraBlocks.pop_back();
-
- // Recursively traverse the 'then' block.
- builder.setInsertionPointToEnd(thenBlock);
- visitBlock(ifInst->getThen());
- Block *lastThenBlock = builder.getInsertionBlock();
-
- // Recursively traverse the 'else' block if present.
- builder.setInsertionPointToEnd(elseBlock);
- if (ifInst->hasElse())
- visitBlock(ifInst->getElse());
- Block *lastElseBlock = builder.getInsertionBlock();
-
- // Create the continuation block here so that it appears lexically after the
- // 'then' and 'else' blocks, branch from end of 'then' and 'else' SESE regions
- // to the continuation block.
- Block *continuationBlock = builder.createBlock();
- builder.setInsertionPointToEnd(lastThenBlock);
- builder.create<BranchOp>(ifInst->getLoc(), continuationBlock);
- builder.setInsertionPointToEnd(lastElseBlock);
- builder.create<BranchOp>(ifInst->getLoc(), continuationBlock);
-
- // Make sure building can continue by setting up the continuation block as the
- // insertion point.
- builder.setInsertionPointToEnd(continuationBlock);
-}
-
-// Entry point of the function convertor.
-//
-// Conversion is performed by recursively visiting instructions of a Function.
-// It reasons in terms of single-entry single-exit (SESE) regions that are not
-// materialized in the code. Instead, the pointer to the last block of the
-// region is maintained throughout the conversion as the insertion point of the
-// IR builder since we never change the first block after its creation. "Block"
-// instructions such as loops and branches create new SESE regions for their
-// bodies, and surround them with additional basic blocks for the control flow.
-// Individual operations are simply appended to the end of the last basic block
-// of the current region. The SESE invariant allows us to easily handle nested
-// structures of arbitrary complexity.
-//
-// During the conversion, we maintain a mapping between the Values present in
-// the original function and their Value images in the function under
-// construction. When an Value is used, it gets replaced with the
-// corresponding Value that has been defined previously. The value flow
-// starts with function arguments converted to basic block arguments.
-Function *FunctionConverter::convert(Function *mlFunc) {
- auto outerBlock = builder.createBlock();
-
- // CFGFunctions do not have explicit arguments but use the arguments to the
- // first basic block instead. Create those from the Function arguments and
- // set up the value remapping.
- outerBlock->addArguments(mlFunc->getType().getInputs());
- assert(mlFunc->getNumArguments() == outerBlock->getNumArguments());
- for (unsigned i = 0, n = mlFunc->getNumArguments(); i < n; ++i) {
- const Value *mlArgument = mlFunc->getArgument(i);
- Value *cfgArgument = outerBlock->getArgument(i);
- valueRemapping.insert(std::make_pair(mlArgument, cfgArgument));
- }
-
- // Convert instructions in order.
- for (auto &block : *mlFunc) {
- for (auto &inst : block) {
- visit(&inst);
- }
- }
-
- return cfgFunc;
-}
-
-//===----------------------------------------------------------------------===//
-// Module converter
-//===----------------------------------------------------------------------===//
-
-namespace {
-// ModuleConverter class does CFG conversion for the whole module.
-class ModuleConverter : public ModulePass {
-public:
- explicit ModuleConverter() : ModulePass(&ModuleConverter::passID) {}
-
- PassResult runOnModule(Module *m) override;
-
- static char passID;
-
-private:
- // Generates CFG functions for all ML functions in the module.
- void convertMLFunctions();
- // Generates CFG function for the given ML function.
- Function *convert(Function *mlFunc);
- // Replaces all ML function references in the module
- // with references to the generated CFG functions.
- void replaceReferences();
- // Replaces function references in the given function.
- void replaceReferences(Function *cfgFunc);
- // Replaces MLFunctions with their CFG counterparts in the module.
- void replaceFunctions();
-
- // Map from ML functions to generated CFG functions.
- llvm::DenseMap<Function *, Function *> generatedFuncs;
- Module *module = nullptr;
-};
-} // end anonymous namespace
-
-char ModuleConverter::passID = 0;
-
-// Iterates over all functions in the module generating CFG functions
-// equivalent to ML functions and replacing references to ML functions
-// with references to the generated ML functions. The names of the converted
-// functions match those of the original functions to avoid breaking any
-// external references to the current module. Therefore, converted functions
-// are added to the module at the end of the pass, after removing the original
-// functions to avoid name clashes. Conversion procedure has access to the
-// module as member of ModuleConverter and must not rely on the converted
-// function to belong to the module.
-PassResult ModuleConverter::runOnModule(Module *m) {
- module = m;
- convertMLFunctions();
- replaceReferences();
- replaceFunctions();
-
- return success();
-}
-
-void ModuleConverter::convertMLFunctions() {
- for (Function &fn : *module) {
- if (fn.isML())
- generatedFuncs[&fn] = convert(&fn);
- }
-}
-
-// Creates CFG function equivalent to the given ML function.
-Function *ModuleConverter::convert(Function *mlFunc) {
- // Use the same name as for ML function; do not add the converted function to
- // the module yet to avoid collision.
- auto name = mlFunc->getName().str();
- auto *cfgFunc = new Function(Function::Kind::CFGFunc, mlFunc->getLoc(), name,
- mlFunc->getType(), mlFunc->getAttrs());
-
- // Generates the body of the CFG function.
- return FunctionConverter(cfgFunc).convert(mlFunc);
-}
-
-// Replace references to MLFunctions with the references to the converted
-// CFGFunctions. Since this all MLFunctions are converted at this point, it is
-// unnecessary to replace references in the MLFunctions that are going to be
-// removed anyway. However, it is necessary to replace the references in the
-// converted CFGFunctions that have not been added to the module yet.
-void ModuleConverter::replaceReferences() {
- // Build the remapping between function attributes pointing to ML functions
- // and the newly created function attributes pointing to the converted CFG
- // functions.
- llvm::DenseMap<Attribute, FunctionAttr> remappingTable;
- for (const Function &fn : *module) {
- if (!fn.isML())
- continue;
- Function *convertedFunc = generatedFuncs.lookup(&fn);
- assert(convertedFunc && "ML function was not converted");
-
- MLIRContext *context = module->getContext();
- auto mlFuncAttr = FunctionAttr::get(&fn, context);
- auto cfgFuncAttr = FunctionAttr::get(convertedFunc, module->getContext());
- remappingTable.insert({mlFuncAttr, cfgFuncAttr});
- }
-
- // Remap in existing functions.
- remapFunctionAttrs(*module, remappingTable);
-
- // Remap in generated functions.
- for (auto pair : generatedFuncs) {
- remapFunctionAttrs(*pair.second, remappingTable);
- }
-}
-
-// Replace the value of a function attribute named "name" attached to the
-// operation "op" and containing a Function-typed value with the result of
-// converting "func" to a Function.
-static inline void replaceMLFunctionAttr(
- OperationInst &op, Identifier name, const Function *func,
- const llvm::DenseMap<Function *, Function *> &generatedFuncs) {
- if (!func->isML())
- return;
-
- Builder b(op.getContext());
- auto *cfgFunc = generatedFuncs.lookup(func);
- op.setAttr(name, b.getFunctionAttr(cfgFunc));
-}
-
-// The CFG and ML functions have the same name. First, erase the Function.
-// Then insert the Function at the same place.
-void ModuleConverter::replaceFunctions() {
- for (auto pair : generatedFuncs) {
- auto &functions = module->getFunctions();
- auto it = functions.erase(pair.first);
- functions.insert(it, pair.second);
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Entry point method
-//===----------------------------------------------------------------------===//
-
-/// Replaces all ML functions in the module with equivalent CFG functions.
-/// Function references are appropriately patched to refer to the newly
-/// generated CFG functions. Converted functions have the same names as the
-/// original functions to preserve module linking.
-ModulePass *mlir::createConvertToCFGPass() { return new ModuleConverter(); }
-
-static PassRegistration<ModuleConverter>
- pass("convert-to-cfg",
- "Convert all ML functions in the module to CFG ones");
--- /dev/null
+//===- LowerIfAndFor.cpp - Lower If and For instructions to CFG -----------===//
+//
+// 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.
+// =============================================================================
+//
+// This file lowers If and For instructions within a function into their lower
+// level CFG equivalent blocks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/Pass.h"
+#include "mlir/StandardOps/StandardOps.h"
+#include "mlir/Transforms/Passes.h"
+using namespace mlir;
+
+namespace {
+class LowerIfAndForPass : public FunctionPass {
+public:
+ LowerIfAndForPass() : FunctionPass(&passID) {}
+ PassResult runOnFunction(Function *function) override;
+
+ void lowerForInst(ForInst *forInst);
+ void lowerIfInst(IfInst *ifInst);
+
+ static char passID;
+};
+} // end anonymous namespace
+
+char LowerIfAndForPass::passID = 0;
+
+// 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,
+ llvm::iterator_range<OperationInst::result_iterator> values,
+ FuncBuilder &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;
+}
+
+// Convert a "for" loop to a flow of blocks.
+//
+// Create an SESE region for the loop (including its body) and append it to the
+// end of the current region. The loop region consists of the initialization
+// block that sets up the initial value of the loop induction variable (%iv) and
+// computes the loop bounds that are loop-invariant in MLFunctions; the
+// condition block that checks the exit condition of the loop; the body SESE
+// region; and the end block that post-dominates the loop. The end block of the
+// loop becomes the new end of the current SESE region. The body of the loop is
+// constructed recursively after starting a new region (it may be, for example,
+// a nested loop). Induction variable modification is appended to the body SESE
+// region that always loops back to the condition block.
+//
+// +--------------------------------+
+// | <code before the ForInst> |
+// | <compute initial %iv value> |
+// | br cond(%iv) |
+// +--------------------------------+
+// |
+// -------| |
+// | v v
+// | +--------------------------------+
+// | | cond(%iv): |
+// | | <compare %iv to upper bound> |
+// | | cond_br %r, body, end |
+// | +--------------------------------+
+// | | |
+// | | -------------|
+// | v |
+// | +--------------------------------+ |
+// | | body: | |
+// | | <body contents> | |
+// | | %new_iv =<add step to %iv> | |
+// | | br cond(%new_iv) | |
+// | +--------------------------------+ |
+// | | |
+// |----------- |--------------------
+// v
+// +--------------------------------+
+// | end: |
+// | <code after the ForInst> |
+// +--------------------------------+
+//
+void LowerIfAndForPass::lowerForInst(ForInst *forInst) {
+ auto loc = forInst->getLoc();
+
+ // Start by splitting the block containing the 'for' into two parts. The part
+ // before will get the init code, the part after will be the end point.
+ auto *initBlock = forInst->getBlock();
+ auto *endBlock = initBlock->splitBlock(forInst);
+
+ // Create the condition block, with its argument for the loop induction
+ // variable. We set it up below.
+ auto *conditionBlock = new Block();
+ conditionBlock->insertBefore(endBlock);
+ auto *iv = conditionBlock->addArgument(IndexType::get(forInst->getContext()));
+
+ // Create the body block, moving the body of the forInst over to it.
+ auto *bodyBlock = new Block();
+ bodyBlock->insertBefore(endBlock);
+
+ auto *oldBody = forInst->getBody();
+ bodyBlock->getInstructions().splice(bodyBlock->begin(),
+ oldBody->getInstructions(),
+ oldBody->begin(), oldBody->end());
+
+ // The code in the body of the forInst now uses 'iv' as its indvar.
+ forInst->replaceAllUsesWith(iv);
+
+ // Append the induction variable stepping logic and branch back to the exit
+ // condition block. Construct an affine map f : (x -> x+step) and apply this
+ // map to the induction variable.
+ FuncBuilder builder(bodyBlock);
+ auto affStep = builder.getAffineConstantExpr(forInst->getStep());
+ auto affDim = builder.getAffineDimExpr(0);
+ auto affStepMap = builder.getAffineMap(1, 0, {affDim + affStep}, {});
+ auto stepOp = builder.create<AffineApplyOp>(loc, affStepMap, iv);
+ builder.create<BranchOp>(loc, conditionBlock, stepOp->getResult(0));
+
+ // Now that the body block done, fill in the code to compute the bounds of the
+ // induction variable in the init block.
+ builder.setInsertionPointToEnd(initBlock);
+
+ // Compute loop bounds using an affine_apply.
+ SmallVector<Value *, 8> operands(forInst->getLowerBoundOperands());
+ auto lbAffineApply =
+ builder.create<AffineApplyOp>(loc, forInst->getLowerBoundMap(), operands);
+ Value *lowerBound = buildMinMaxReductionSeq(
+ loc, CmpIPredicate::SGT, lbAffineApply->getResults(), builder);
+
+ operands.assign(forInst->getUpperBoundOperands().begin(),
+ forInst->getUpperBoundOperands().end());
+ auto ubAffineApply =
+ builder.create<AffineApplyOp>(loc, forInst->getUpperBoundMap(), operands);
+ Value *upperBound = buildMinMaxReductionSeq(
+ loc, CmpIPredicate::SLT, ubAffineApply->getResults(), builder);
+ builder.create<BranchOp>(loc, conditionBlock, lowerBound);
+
+ // With the body block done, we can fill in the condition block.
+ builder.setInsertionPointToEnd(conditionBlock);
+ auto comparison =
+ builder.create<CmpIOp>(loc, CmpIPredicate::SLT, iv, upperBound);
+ builder.create<CondBranchOp>(loc, comparison, bodyBlock, ArrayRef<Value *>(),
+ endBlock, ArrayRef<Value *>());
+
+ // Ok, we're done!
+ forInst->erase();
+}
+
+// Convert an "if" instruction into a flow of basic blocks.
+//
+// Create an SESE region for the if instruction (including its "then" and
+// optional "else" instruction blocks) and append it to the end of the current
+// region. The conditional region consists of a sequence of condition-checking
+// blocks that implement the short-circuit scheme, followed by a "then" SESE
+// region and an "else" SESE region, and the continuation block that
+// post-dominates all blocks of the "if" instruction. The flow of blocks that
+// correspond to the "then" and "else" clauses are constructed recursively,
+// enabling easy nesting of "if" instructions and if-then-else-if chains.
+//
+// +--------------------------------+
+// | <code before the IfInst> |
+// | %zero = constant 0 : index |
+// | %v = affine_apply #expr1(%ops) |
+// | %c = cmpi "sge" %v, %zero |
+// | cond_br %c, %next, %else |
+// +--------------------------------+
+// | |
+// | --------------|
+// v |
+// +--------------------------------+ |
+// | next: | |
+// | <repeat the check for expr2> | |
+// | cond_br %c, %next2, %else | |
+// +--------------------------------+ |
+// | | |
+// ... --------------|
+// | <Per-expression checks> |
+// v |
+// +--------------------------------+ |
+// | last: | |
+// | <repeat the check for exprN> | |
+// | cond_br %c, %then, %else | |
+// +--------------------------------+ |
+// | | |
+// | --------------|
+// v |
+// +--------------------------------+ |
+// | then: | |
+// | <then contents> | |
+// | br continue | |
+// +--------------------------------+ |
+// | |
+// |---------- |-------------
+// | V
+// | +--------------------------------+
+// | | else: |
+// | | <else contents> |
+// | | br continue |
+// | +--------------------------------+
+// | |
+// ------| |
+// v v
+// +--------------------------------+
+// | continue: |
+// | <code after the IfInst> |
+// +--------------------------------+
+//
+void LowerIfAndForPass::lowerIfInst(IfInst *ifInst) {
+ auto loc = ifInst->getLoc();
+
+ // Start by splitting the block containing the 'if' into two parts. The part
+ // before will contain the condition, the part after will be the continuation
+ // point.
+ auto *condBlock = ifInst->getBlock();
+ auto *continueBlock = condBlock->splitBlock(ifInst);
+
+ // Create a block for the 'then' code, inserting it between the cond and
+ // continue blocks. Move the instructions over from the IfInst and add a
+ // branch to the continuation point.
+ Block *thenBlock = new Block();
+ thenBlock->insertBefore(continueBlock);
+
+ auto *oldThen = ifInst->getThen();
+ thenBlock->getInstructions().splice(thenBlock->begin(),
+ oldThen->getInstructions(),
+ oldThen->begin(), oldThen->end());
+ FuncBuilder builder(thenBlock);
+ builder.create<BranchOp>(loc, continueBlock);
+
+ // Handle the 'else' block the same way, but we skip it if we have no else
+ // code.
+ Block *elseBlock = continueBlock;
+ if (auto *oldElse = ifInst->getElse()) {
+ elseBlock = new Block();
+ elseBlock->insertBefore(continueBlock);
+
+ elseBlock->getInstructions().splice(elseBlock->begin(),
+ oldElse->getInstructions(),
+ oldElse->begin(), oldElse->end());
+ builder.setInsertionPointToEnd(elseBlock);
+ builder.create<BranchOp>(loc, continueBlock);
+ }
+
+ // Ok, now we just have to handle the condition logic.
+ auto integerSet = ifInst->getCondition().getIntegerSet();
+
+ // Implement short-circuit logic. For each affine expression in the 'if'
+ // condition, convert it into an affine map and call `affine_apply` to obtain
+ // the resulting value. Perform the equality or the greater-than-or-equality
+ // test between this value and zero depending on the equality flag of the
+ // condition. If the test fails, jump immediately to the false branch, which
+ // may be the else block if it is present or the continuation block otherwise.
+ // If the test succeeds, jump to the next block testing the next conjunct of
+ // the condition in the similar way. When all conjuncts have been handled,
+ // jump to the 'then' block instead.
+ builder.setInsertionPointToEnd(condBlock);
+ Value *zeroConstant = builder.create<ConstantIndexOp>(loc, 0);
+
+ for (auto tuple :
+ llvm::zip(integerSet.getConstraints(), integerSet.getEqFlags())) {
+ AffineExpr constraintExpr = std::get<0>(tuple);
+ bool isEquality = std::get<1>(tuple);
+
+ // Create the fall-through block for the next condition right before the
+ // 'thenBlock'.
+ auto *nextBlock = new Block();
+ nextBlock->insertBefore(thenBlock);
+
+ // Build and apply an affine map.
+ auto affineMap =
+ builder.getAffineMap(integerSet.getNumDims(),
+ integerSet.getNumSymbols(), constraintExpr, {});
+ SmallVector<Value *, 8> operands(ifInst->getOperands());
+ auto affineApplyOp =
+ builder.create<AffineApplyOp>(loc, affineMap, operands);
+ Value *affResult = affineApplyOp->getResult(0);
+
+ // Compare the result of the apply and branch.
+ auto comparisonOp = builder.create<CmpIOp>(
+ loc, isEquality ? CmpIPredicate::EQ : CmpIPredicate::SGE, affResult,
+ zeroConstant);
+ builder.create<CondBranchOp>(loc, comparisonOp->getResult(), nextBlock,
+ /*trueArgs*/ ArrayRef<Value *>(), elseBlock,
+ /*falseArgs*/ ArrayRef<Value *>());
+ builder.setInsertionPointToEnd(nextBlock);
+ }
+
+ // We will have ended up with an empty block as our continuation block (or, in
+ // the degenerate case where there were zero conditions, we have the original
+ // condition block). Redirect that to the thenBlock.
+ condBlock = builder.getInsertionBlock();
+ if (condBlock->empty()) {
+ condBlock->replaceAllUsesWith(thenBlock);
+ condBlock->eraseFromFunction();
+ } else {
+ builder.create<BranchOp>(loc, thenBlock);
+ }
+
+ // Ok, we're done!
+ ifInst->erase();
+}
+
+// Entry point of the function convertor.
+//
+// Conversion is performed by recursively visiting instructions of a Function.
+// It reasons in terms of single-entry single-exit (SESE) regions that are not
+// materialized in the code. Instead, the pointer to the last block of the
+// region is maintained throughout the conversion as the insertion point of the
+// IR builder since we never change the first block after its creation. "Block"
+// instructions such as loops and branches create new SESE regions for their
+// bodies, and surround them with additional basic blocks for the control flow.
+// Individual operations are simply appended to the end of the last basic block
+// of the current region. The SESE invariant allows us to easily handle nested
+// structures of arbitrary complexity.
+//
+// During the conversion, we maintain a mapping between the Values present in
+// the original function and their Value images in the function under
+// construction. When an Value is used, it gets replaced with the
+// corresponding Value that has been defined previously. The value flow
+// starts with function arguments converted to basic block arguments.
+PassResult LowerIfAndForPass::runOnFunction(Function *function) {
+ SmallVector<Instruction *, 8> instsToRewrite;
+
+ // Collect all the If and For statements. We do this as a prepass to avoid
+ // invalidating the walker with our rewrite.
+ function->walkInsts([&](Instruction *inst) {
+ if (isa<IfInst>(inst) || isa<ForInst>(inst))
+ instsToRewrite.push_back(inst);
+ });
+
+ // Rewrite all of the ifs and fors. We walked the instructions in preorder,
+ // so we know that we will rewrite them in the same order.
+ for (auto *inst : instsToRewrite)
+ if (auto *ifInst = dyn_cast<IfInst>(inst))
+ lowerIfInst(ifInst);
+ else
+ lowerForInst(cast<ForInst>(inst));
+
+ // Change the kind of the function to indicate it has no If's or For's.
+ function->setKind(Function::Kind::CFGFunc);
+ return success();
+}
+
+/// Lowers If and For instructions within a function into their lower level CFG
+/// equivalent blocks.
+FunctionPass *mlir::createLowerIfAndForPass() {
+ return new LowerIfAndForPass();
+}
+
+static PassRegistration<LowerIfAndForPass>
+ pass("lower-if-and-for",
+ "Lower If and For instructions to CFG equivalents");
+++ /dev/null
-// RUN: mlir-opt -convert-to-cfg %s | FileCheck %s
-
-// CHECK-DAG: [[map0:#map[0-9]+]] = () -> (0)
-// CHECK-DAG: [[map1:#map[0-9]+]] = () -> (1)
-// CHECK-DAG: [[map7:#map[0-9]+]] = () -> (7)
-// CHECK-DAG: [[map18:#map[0-9]+]] = () -> (18)
-// CHECK-DAG: [[map37:#map[0-9]+]] = () -> (37)
-// CHECK-DAG: [[map42:#map[0-9]+]] = () -> (42)
-// CHECK-DAG: [[map56:#map[0-9]+]] = () -> (56)
-// CHECK-DAG: [[map1Sym:#map[0-9]+]] = ()[s0] -> (s0)
-// CHECK-DAG: [[map1Id:#map[0-9]+]] = (d0) -> (d0)
-// CHECK-DAG: [[mapAdd1:#map[0-9]+]] = (d0) -> (d0 + 1)
-// CHECK-DAG: [[mapAdd2:#map[0-9]+]] = (d0) -> (d0 + 2)
-// CHECK-DAG: [[mapAdd3:#map[0-9]+]] = (d0) -> (d0 + 3)
-// CHECK-DAG: [[multiMap1:#map[0-9]+]] = (d0)[s0] -> (d0, d0 * -1 + s0)
-// CHECK-DAG: [[multiMap2:#map[0-9]+]] = (d0)[s0] -> (s0, d0 + 10)
-// CHECK-DAG: [[multi7Map:#map[0-9]+]] = (d0) -> (d0, d0, d0, d0, d0, d0, d0)
-// Maps produced from individual affine expressions that appear in "if" conditions.
-// CHECK-DAG: [[setMap20:#map[0-9]+]] = (d0) -> (d0 * -1 + 20)
-// CHECK-DAG: [[setMap10:#map[0-9]+]] = (d0) -> (d0 - 10)
-// CHECK-DAG: [[setMapDiff:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (d0 * -1 + s0 + 1)
-// CHECK-DAG: [[setMapS0:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s0 - 1)
-// CHECK-DAG: [[setMapS1:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s1 - 1)
-// CHECK-DAG: [[setMapS2:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s2 - 1)
-// CHECK-DAG: [[setMapS3:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s3 - 42)
-
-// CHECK-LABEL: cfgfunc @empty() {
-mlfunc @empty() {
- return // CHECK: return
-} // CHECK: }
-
-extfunc @body(index) -> ()
-
-// Simple loops are properly converted.
-// CHECK-LABEL: cfgfunc @simple_loop() {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %0 = affine_apply [[map1]]()
-// CHECK-NEXT: %1 = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%2: index): // 2 preds: ^bb1, ^bb3
-// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
-// CHECK-NEXT: cond_br %3, ^bb3, ^bb4
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: call @body(%2) : (index) -> ()
-// CHECK-NEXT: %4 = affine_apply [[mapAdd1]](%2)
-// CHECK-NEXT: br ^bb2(%4 : index)
-// CHECK-NEXT: ^bb4: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @simple_loop() {
- for %i = 1 to 42 {
- call @body(%i) : (index) -> ()
- }
- return
-}
-
-// Direct calls get renamed if asked (IR data structures properly updated) and
-// keep the same name otherwise.
-cfgfunc @simple_caller() {
-^bb0:
-// CHECK: call @simple_loop() : () -> ()
- call @simple_loop() : () -> ()
- return
-}
-
-// Constant loads get renamed if asked (IR data structure properly updated) and
-// keep the same name otherwise.
-cfgfunc @simple_indirect_caller() {
-^bb0:
-// CHECK: %f = constant @simple_loop : () -> ()
- %f = constant @simple_loop : () -> ()
- call_indirect %f() : () -> ()
- return
-}
-
-cfgfunc @nested_attributes() {
-^bb0:
- %0 = constant 0 : index
-// CHECK: call @body(%c0) {attr1: [@simple_loop : () -> (), @simple_loop : () -> ()]} : (index) -> ()
- call @body(%0) {attr1: [@simple_loop : () -> (), @simple_loop : () -> ()]} : (index) -> ()
-// Note: the {{\[}} construct is necessary to prevent FileCheck from
-// interpreting [[ as the start of its variable in the pattern below.
-// CHECK: call @body(%c0) {attr2: {{\[}}{{\[}}{{\[}}@simple_loop : () -> ()]], [@simple_loop : () -> ()]]} : (index) -> ()
- call @body(%0) {attr2: [[[@simple_loop : () -> ()]], [@simple_loop : () -> ()]]} : (index) -> ()
- return
-}
-
-// CHECK-LABEL: cfgfunc @ml_caller() {
-mlfunc @ml_caller() {
-// Direct calls inside ML functions are renamed if asked (given that the
-// function itself is also converted).
-// CHECK: call @simple_loop() : () -> ()
- call @simple_loop() : () -> ()
-// Direct calls to not yet declared ML functions are also renamed.
-// CHECK: call @more_imperfectly_nested_loops() : () -> ()
- call @more_imperfectly_nested_loops() : () -> ()
- return
-}
-
-/////////////////////////////////////////////////////////////////////
-
-extfunc @body_args(index) -> (index)
-extfunc @other(index, i32) -> (i32)
-
-// Arguments and return values of the functions are converted.
-// CHECK-LABEL: cfgfunc @mlfunc_args(%arg0: i32, %arg1: i32) -> (i32, i32) {
-// CHECK-NEXT: %c0_i32 = constant 0 : i32
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %0 = affine_apply [[map0]]()
-// CHECK-NEXT: %1 = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%2: index): // 2 preds: ^bb1, ^bb3
-// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
-// CHECK-NEXT: cond_br %3, ^bb3, ^bb4
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: %4 = call @body_args(%2) : (index) -> index
-// CHECK-NEXT: %5 = call @other(%4, %arg0) : (index, i32) -> i32
-// CHECK-NEXT: %6 = call @other(%4, %5) : (index, i32) -> i32
-// CHECK-NEXT: %7 = call @other(%4, %arg1) : (index, i32) -> i32
-// CHECK-NEXT: %8 = affine_apply [[mapAdd1]](%2)
-// CHECK-NEXT: br ^bb2(%8 : index)
-// CHECK-NEXT: ^bb4: // pred: ^bb2
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %9 = call @other(%c0, %c0_i32) : (index, i32) -> i32
-// CHECK-NEXT: return %c0_i32, %9 : i32, i32
-// CHECK-NEXT: }
-mlfunc @mlfunc_args(%a : i32, %b : i32) -> (i32, i32) {
- %r1 = constant 0 : i32
- for %i = 0 to 42 {
- %1 = call @body_args(%i) : (index) -> (index)
- %2 = call @other(%1, %a) : (index, i32) -> (i32)
- %3 = call @other(%1, %2) : (index, i32) -> (i32)
- %4 = call @other(%1, %b) : (index, i32) -> (i32)
- }
- %ri = constant 0 : index
- %r2 = call @other(%ri, %r1) : (index, i32) -> (i32)
- return %r1, %r2 : i32, i32
-}
-
-/////////////////////////////////////////////////////////////////////
-
-extfunc @pre(index) -> ()
-extfunc @body2(index, index) -> ()
-extfunc @post(index) -> ()
-
-// CHECK-LABEL: cfgfunc @imperfectly_nested_loops() {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %0 = affine_apply [[map0]]()
-// CHECK-NEXT: %1 = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%2: index): // 2 preds: ^bb1, ^bb7
-// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
-// CHECK-NEXT: cond_br %3, ^bb3, ^bb8
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: call @pre(%2) : (index) -> ()
-// CHECK-NEXT: br ^bb4
-// CHECK-NEXT: ^bb4: // pred: ^bb3
-// CHECK-NEXT: %4 = affine_apply [[map7]]()
-// CHECK-NEXT: %5 = affine_apply [[map56]]()
-// CHECK-NEXT: br ^bb5(%4 : index)
-// CHECK-NEXT: ^bb5(%6: index): // 2 preds: ^bb4, ^bb6
-// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
-// CHECK-NEXT: cond_br %7, ^bb6, ^bb7
-// CHECK-NEXT: ^bb6: // pred: ^bb5
-// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
-// CHECK-NEXT: %8 = affine_apply [[mapAdd2]](%6)
-// CHECK-NEXT: br ^bb5(%8 : index)
-// CHECK-NEXT: ^bb7: // pred: ^bb5
-// CHECK-NEXT: call @post(%2) : (index) -> ()
-// CHECK-NEXT: %9 = affine_apply [[mapAdd1]](%2)
-// CHECK-NEXT: br ^bb2(%9 : index)
-// CHECK-NEXT: ^bb8: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @imperfectly_nested_loops() {
- for %i = 0 to 42 {
- call @pre(%i) : (index) -> ()
- for %j = 7 to 56 step 2 {
- call @body2(%i, %j) : (index, index) -> ()
- }
- call @post(%i) : (index) -> ()
- }
- return
-}
-
-/////////////////////////////////////////////////////////////////////
-
-extfunc @mid(index) -> ()
-extfunc @body3(index, index) -> ()
-
-// CHECK-LABEL: cfgfunc @more_imperfectly_nested_loops() {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %0 = affine_apply [[map0]]()
-// CHECK-NEXT: %1 = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%2: index): // 2 preds: ^bb1, ^bb11
-// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
-// CHECK-NEXT: cond_br %3, ^bb3, ^bb12
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: call @pre(%2) : (index) -> ()
-// CHECK-NEXT: br ^bb4
-// CHECK-NEXT: ^bb4: // pred: ^bb3
-// CHECK-NEXT: %4 = affine_apply [[map7]]()
-// CHECK-NEXT: %5 = affine_apply [[map56]]()
-// CHECK-NEXT: br ^bb5(%4 : index)
-// CHECK-NEXT: ^bb5(%6: index): // 2 preds: ^bb4, ^bb6
-// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
-// CHECK-NEXT: cond_br %7, ^bb6, ^bb7
-// CHECK-NEXT: ^bb6: // pred: ^bb5
-// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
-// CHECK-NEXT: %8 = affine_apply [[mapAdd2]](%6)
-// CHECK-NEXT: br ^bb5(%8 : index)
-// CHECK-NEXT: ^bb7: // pred: ^bb5
-// CHECK-NEXT: call @mid(%2) : (index) -> ()
-// CHECK-NEXT: br ^bb8
-// CHECK-NEXT: ^bb8: // pred: ^bb7
-// CHECK-NEXT: %9 = affine_apply [[map18]]()
-// CHECK-NEXT: %10 = affine_apply [[map37]]()
-// CHECK-NEXT: br ^bb9(%9 : index)
-// CHECK-NEXT: ^bb9(%11: index): // 2 preds: ^bb8, ^bb10
-// CHECK-NEXT: %12 = cmpi "slt", %11, %10 : index
-// CHECK-NEXT: cond_br %12, ^bb10, ^bb11
-// CHECK-NEXT: ^bb10: // pred: ^bb9
-// CHECK-NEXT: call @body3(%2, %11) : (index, index) -> ()
-// CHECK-NEXT: %13 = affine_apply [[mapAdd3]](%11)
-// CHECK-NEXT: br ^bb9(%13 : index)
-// CHECK-NEXT: ^bb11: // pred: ^bb9
-// CHECK-NEXT: call @post(%2) : (index) -> ()
-// CHECK-NEXT: %14 = affine_apply [[mapAdd1]](%2)
-// CHECK-NEXT: br ^bb2(%14 : index)
-// CHECK-NEXT: ^bb12: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @more_imperfectly_nested_loops() {
- for %i = 0 to 42 {
- call @pre(%i) : (index) -> ()
- for %j = 7 to 56 step 2 {
- call @body2(%i, %j) : (index, index) -> ()
- }
- call @mid(%i) : (index) -> ()
- for %k = 18 to 37 step 3 {
- call @body3(%i, %k) : (index, index) -> ()
- }
- call @post(%i) : (index) -> ()
- }
- return
-}
-
-// CHECK-LABEL: cfgfunc @affine_apply_loops_shorthand(%arg0: index) {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %0 = affine_apply [[map0]]()
-// CHECK-NEXT: %1 = affine_apply [[map1Sym]]()[%arg0]
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%2: index): // 2 preds: ^bb1, ^bb7
-// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
-// CHECK-NEXT: cond_br %3, ^bb3, ^bb8
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: br ^bb4
-// CHECK-NEXT: ^bb4: // pred: ^bb3
-// CHECK-NEXT: %4 = affine_apply [[map1Id]](%2)
-// CHECK-NEXT: %5 = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb5(%4 : index)
-// CHECK-NEXT: ^bb5(%6: index): // 2 preds: ^bb4, ^bb6
-// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
-// CHECK-NEXT: cond_br %7, ^bb6, ^bb7
-// CHECK-NEXT: ^bb6: // pred: ^bb5
-// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
-// CHECK-NEXT: %8 = affine_apply [[mapAdd1]](%6)
-// CHECK-NEXT: br ^bb5(%8 : index)
-// CHECK-NEXT: ^bb7: // pred: ^bb5
-// CHECK-NEXT: %9 = affine_apply [[mapAdd1]](%2)
-// CHECK-NEXT: br ^bb2(%9 : index)
-// CHECK-NEXT: ^bb8: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @affine_apply_loops_shorthand(%N : index) {
- for %i = 0 to %N {
- for %j = %i to 42 {
- call @body2(%i, %j) : (index, index) -> ()
- }
- }
- return
-}
-
-/////////////////////////////////////////////////////////////////////
-
-extfunc @get_idx() -> (index)
-
-#set1 = (d0) : (20 - d0 >= 0)
-#set2 = (d0) : (d0 - 10 >= 0)
-
-// CHECK-LABEL: cfgfunc @if_only() {
-// CHECK-NEXT: %0 = call @get_idx() : () -> index
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
-// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
-// CHECK-NEXT: cond_br %2, [[thenBB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[thenBB]]:
-// CHECK-NEXT: call @body(%0) : (index) -> ()
-// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseBB]]:
-// CHECK-NEXT: br [[endBB]]
-// CHECK-NEXT: [[endBB]]:
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @if_only() {
- %i = call @get_idx() : () -> (index)
- if #set1(%i) {
- call @body(%i) : (index) -> ()
- }
- return
-}
-
-// CHECK-LABEL: cfgfunc @if_else() {
-// CHECK-NEXT: %0 = call @get_idx() : () -> index
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
-// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
-// CHECK-NEXT: cond_br %2, [[thenBB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[thenBB]]:
-// CHECK-NEXT: call @body(%0) : (index) -> ()
-// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseBB]]:
-// CHECK-NEXT: call @mid(%0) : (index) -> ()
-// CHECK-NEXT: br [[endBB]]
-// CHECK-NEXT: [[endBB]]:
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @if_else() {
- %i = call @get_idx() : () -> (index)
- if #set1(%i) {
- call @body(%i) : (index) -> ()
- } else {
- call @mid(%i) : (index) -> ()
- }
- return
-}
-
-// CHECK-LABEL: cfgfunc @nested_ifs() {
-// CHECK-NEXT: %0 = call @get_idx() : () -> index
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
-// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
-// CHECK-NEXT: cond_br %2, [[thenBB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[thenBB]]:
-// CHECK-NEXT: %c0_0 = constant 0 : index
-// CHECK-NEXT: %3 = affine_apply [[setMap10]](%0)
-// CHECK-NEXT: %4 = cmpi "sge", %3, %c0_0 : index
-// CHECK-NEXT: cond_br %4, [[thenThenBB:\^bb[0-9]+]], [[thenElseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseBB]]:
-// CHECK-NEXT: %c0_1 = constant 0 : index
-// CHECK-NEXT: %5 = affine_apply [[setMap10]](%0)
-// CHECK-NEXT: %6 = cmpi "sge", %5, %c0_1 : index
-// CHECK-NEXT: cond_br %6, [[elseThenBB:\^bb[0-9]+]], [[elseElseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[thenThenBB]]:
-// CHECK-NEXT: call @body(%0) : (index) -> ()
-// CHECK-NEXT: br [[thenEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[thenElseBB]]:
-// CHECK-NEXT: br [[thenEndBB]]
-// CHECK-NEXT: [[thenEndBB]]:
-// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseThenBB]]:
-// CHECK-NEXT: call @mid(%0) : (index) -> ()
-// CHECK-NEXT: br [[elseEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseElseBB]]:
-// CHECK-NEXT: br [[elseEndBB]]
-// CHECK-NEXT: [[elseEndBB]]:
-// CHECK-NEXT: br [[endBB]]
-// CHECK-NEXT: [[endBB]]:
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @nested_ifs() {
- %i = call @get_idx() : () -> (index)
- if #set1(%i) {
- if #set2(%i) {
- call @body(%i) : (index) -> ()
- }
- } else {
- if #set2(%i) {
- call @mid(%i) : (index) -> ()
- }
- }
- return
-}
-
-#setN = (d0)[N,M,K,L] : (N - d0 + 1 >= 0, N - 1 >= 0, M - 1 >= 0, K - 1 >= 0, L - 42 == 0)
-
-// CHECK-LABEL: cfgfunc @multi_cond(%arg0: index, %arg1: index, %arg2: index, %arg3: index) {
-// CHECK-NEXT: %0 = call @get_idx() : () -> index
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %1 = affine_apply [[setMapDiff]](%0)[%arg0, %arg1, %arg2, %arg3]
-// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
-// CHECK-NEXT: cond_br %2, [[cond2BB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[cond2BB]]:
-// CHECK-NEXT: %3 = affine_apply [[setMapS0]](%0)[%arg0, %arg1, %arg2, %arg3]
-// CHECK-NEXT: %4 = cmpi "sge", %3, %c0 : index
-// CHECK-NEXT: cond_br %4, [[cond3BB:\^bb[0-9]+]], [[elseBB]]
-// CHECK-NEXT: [[cond3BB]]:
-// CHECK-NEXT: %5 = affine_apply [[setMapS1]](%0)[%arg0, %arg1, %arg2, %arg3]
-// CHECK-NEXT: %6 = cmpi "sge", %5, %c0 : index
-// CHECK-NEXT: cond_br %6, [[cond4BB:\^bb[0-9]+]], [[elseBB]]
-// CHECK-NEXT: [[cond4BB]]:
-// CHECK-NEXT: %7 = affine_apply [[setMapS2]](%0)[%arg0, %arg1, %arg2, %arg3]
-// CHECK-NEXT: %8 = cmpi "sge", %7, %c0 : index
-// CHECK-NEXT: cond_br %8, [[cond5BB:\^bb[0-9]+]], [[elseBB]]
-// CHECK-NEXT: [[cond5BB]]:
-// CHECK-NEXT: %9 = affine_apply [[setMapS3]](%0)[%arg0, %arg1, %arg2, %arg3]
-// CHECK-NEXT: %10 = cmpi "eq", %9, %c0 : index
-// CHECK-NEXT: cond_br %10, [[thenBB:\^bb[0-9]+]], [[elseBB]]
-// CHECK-NEXT: [[thenBB]]:
-// CHECK-NEXT: call @body(%0) : (index) -> ()
-// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[elseBB]]:
-// CHECK-NEXT: call @mid(%0) : (index) -> ()
-// CHECK-NEXT: br [[endBB]]
-// CHECK-NEXT: [[endBB]]:
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @multi_cond(%N : index, %M : index, %K : index, %L : index) {
- %i = call @get_idx() : () -> (index)
- if #setN(%i)[%N,%M,%K,%L] {
- call @body(%i) : (index) -> ()
- } else {
- call @mid(%i) : (index) -> ()
- }
- return
-}
-
-// CHECK-LABEL: cfgfunc @if_for() {
-mlfunc @if_for() {
-// CHECK-NEXT: %0 = call @get_idx() : () -> index
- %i = call @get_idx() : () -> (index)
-// CHECK-NEXT: %c0 = constant 0 : index
-// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
-// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
-// CHECK-NEXT: cond_br %2, [[outerThenBB:\^bb[0-9]+]], [[outerElseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[outerThenBB]]:
-// CHECK-NEXT: br [[midLoopInitBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[outerElseBB]]:
-// CHECK-NEXT: br [[outerEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[midLoopInitBB]]:
-// CHECK-NEXT: %3 = affine_apply [[map0]]()
-// CHECK-NEXT: %4 = affine_apply [[map42]]()
-// CHECK-NEXT: br [[midLoopCondBB:\^bb[0-9]+]](%3 : index)
-// CHECK-NEXT: [[midLoopCondBB]](%5: index):
-// CHECK-NEXT: %6 = cmpi "slt", %5, %4 : index
-// CHECK-NEXT: cond_br %6, [[midLoopBodyBB:\^bb[0-9]+]], [[midLoopEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[midLoopBodyBB]]:
-// CHECK-NEXT: %c0_0 = constant 0 : index
-// CHECK-NEXT: %7 = affine_apply [[setMap10]](%5)
-// CHECK-NEXT: %8 = cmpi "sge", %7, %c0_0 : index
-// CHECK-NEXT: cond_br %8, [[innerThenBB:\^bb[0-9]+]], [[innerElseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[innerThenBB:\^bb[0-9]+]]:
-// CHECK-NEXT: call @body2(%0, %5) : (index, index) -> ()
-// CHECK-NEXT: br [[innerEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[innerElseBB:\^bb[0-9]+]]:
-// CHECK-NEXT: br [[innerEndBB]]
-// CHECK-NEXT: [[innerEndBB]]:
-// CHECK-NEXT: %9 = affine_apply [[mapAdd1]](%5)
-// CHECK-NEXT: br [[midLoopCondBB]](%9 : index)
-// CHECK-NEXT: [[midLoopEndBB]]:
-// CHECK-NEXT: br [[outerEndBB]]
-// CHECK-NEXT: [[outerEndBB]]:
-// CHECK-NEXT: br [[outerLoopInit:\^bb[0-9]+]]
- if #set1(%i) {
- for %j = 0 to 42 {
- if #set2(%j) {
- call @body2(%i, %j) : (index, index) -> ()
- }
- }
- }
-// CHECK-NEXT: [[outerLoopInit]]:
-// CHECK-NEXT: %10 = affine_apply [[map0]]()
-// CHECK-NEXT: %11 = affine_apply [[map42]]()
-// CHECK-NEXT: br [[outerLoopCond:\^bb[0-9]+]](%10 : index)
-// CHECK-NEXT: [[outerLoopCond]](%12: index):
-// CHECK-NEXT: %13 = cmpi "slt", %12, %11 : index
-// CHECK-NEXT: cond_br %13, [[outerLoopBody:\^bb[0-9]+]], [[outerLoopEnd:\^bb[0-9]+]]
-// CHECK-NEXT: [[outerLoopBody]]:
-// CHECK-NEXT: %c0_1 = constant 0 : index
-// CHECK-NEXT: %14 = affine_apply [[setMap10]](%12)
-// CHECK-NEXT: %15 = cmpi "sge", %14, %c0_1 : index
-// CHECK-NEXT: cond_br %15, [[midThenBB:\^bb[0-9]+]], [[midElseBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[midThenBB]]:
-// CHECK-NEXT: br [[innerLoopInitBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[midElseBB]]:
-// CHECK-NEXT: br [[midEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[innerLoopInitBB:\^bb[0-9]+]]:
-// CHECK-NEXT: %16 = affine_apply [[map0]]()
-// CHECK-NEXT: %17 = affine_apply [[map42]]()
-// CHECK-NEXT: br [[innerLoopCondBB:\^bb[0-9]+]](%16 : index)
-// CHECK-NEXT: [[innerLoopCondBB]](%18: index):
-// CHECK-NEXT: %19 = cmpi "slt", %18, %17 : index
-// CHECK-NEXT: cond_br %19, [[innerLoopBodyBB:\^bb[0-9]+]], [[innerLoopEndBB:\^bb[0-9]+]]
-// CHECK-NEXT: [[innerLoopBodyBB]]:
-// CHECK-NEXT: call @body3(%12, %18) : (index, index) -> ()
-// CHECK-NEXT: %20 = affine_apply [[mapAdd1]](%18)
-// CHECK-NEXT: br [[innerLoopCondBB]](%20 : index)
-// CHECK-NEXT: [[innerLoopEndBB]]:
-// CHECK-NEXT: br [[midEndBB]]
-// CHECK-NEXT: [[midEndBB]]:
-// CHECK-NEXT: %21 = affine_apply [[mapAdd1]](%12)
-// CHECK-NEXT: br [[outerLoopCond]](%21 : index)
- for %k = 0 to 42 {
- if #set2(%k) {
- for %l = 0 to 42 {
- call @body3(%k, %l) : (index, index) -> ()
- }
- }
- }
-// CHECK-NEXT: [[outerLoopEnd]]:
-// CHECK-NEXT: return
- return
-}
-
-#lbMultiMap = (d0)[s0] -> (d0, s0 - d0)
-#ubMultiMap = (d0)[s0] -> (s0, d0 + 10)
-
-// CHECK-LABEL: cfgfunc @loop_min_max(%arg0: index) {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map0]]()
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map42]]()
-// CHECK-NEXT: br ^bb2(%{{[0-9]+}} : index)
-// CHECK-NEXT: ^bb2(%{{[0-9]+}}: index): // 2 preds: ^bb1, ^bb7
-// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %{{[0-9]+}} : index
-// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb3, ^bb8
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: br ^bb4
-// CHECK-NEXT: ^bb4: // pred: ^bb3
-// CHECK-NEXT: %[[lb:[0-9]+]] = affine_apply [[multiMap1]](%{{[0-9]+}})[%arg0]
-// CHECK-NEXT: %[[lbc:[0-9]+]] = cmpi "sgt", %[[lb]]#0, %[[lb]]#1 : index
-// CHECK-NEXT: %[[lbv:[0-9]+]] = select %[[lbc]], %[[lb]]#0, %[[lb]]#1 : index
-// CHECK-NEXT: %[[ub:[0-9]+]] = affine_apply [[multiMap2]](%{{[0-9]+}})[%arg0]
-// CHECK-NEXT: %[[ubc:[0-9]+]] = cmpi "slt", %[[ub]]#0, %[[ub]]#1 : index
-// CHECK-NEXT: %[[ubv:[0-9]+]] = select %[[ubc]], %[[ub]]#0, %[[ub]]#1 : index
-// CHECK-NEXT: br ^bb5(%[[lbv]] : index)
-// CHECK-NEXT: ^bb5(%{{[0-9]+}}: index): // 2 preds: ^bb4, ^bb6
-// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %[[ubv]] : index
-// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb6, ^bb7
-// CHECK-NEXT: ^bb6: // pred: ^bb5
-// CHECK-NEXT: call @body2(%{{[0-9]+}}, %{{[0-9]+}}) : (index, index) -> ()
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
-// CHECK-NEXT: br ^bb5(%{{[0-9]+}} : index)
-// CHECK-NEXT: ^bb7: // pred: ^bb5
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
-// CHECK-NEXT: br ^bb2(%{{[0-9]+}} : index)
-// CHECK-NEXT: ^bb8: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @loop_min_max(%N : index) {
- for %i = 0 to 42 {
- for %j = max #lbMultiMap(%i)[%N] to min #ubMultiMap(%i)[%N] {
- call @body2(%i, %j) : (index, index) -> ()
- }
- }
- return
-}
-
-#map_7_values = (i) -> (i, i, i, i, i, i, i)
-
-// Check that the "min" (cmpi "slt" + select) reduction sequence is emitted
-// correctly for a an affine map with 7 results.
-
-// CHECK-LABEL: cfgfunc @min_reduction_tree(%arg0: index) {
-// CHECK-NEXT: br ^bb1
-// CHECK-NEXT: ^bb1: // pred: ^bb0
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map0]]()
-// CHECK-NEXT: %[[applr:[0-9]+]] = affine_apply [[multi7Map]](%arg0)
-// CHECK-NEXT: %[[c01:.+]] = cmpi "slt", %[[applr]]#0, %[[applr]]#1 : index
-// CHECK-NEXT: %[[r01:.+]] = select %[[c01]], %[[applr]]#0, %[[applr]]#1 : index
-// CHECK-NEXT: %[[c012:.+]] = cmpi "slt", %[[r01]], %[[applr]]#2 : index
-// CHECK-NEXT: %[[r012:.+]] = select %[[c012]], %[[r01]], %[[applr]]#2 : index
-// CHECK-NEXT: %[[c0123:.+]] = cmpi "slt", %[[r012]], %[[applr]]#3 : index
-// CHECK-NEXT: %[[r0123:.+]] = select %[[c0123]], %[[r012]], %[[applr]]#3 : index
-// CHECK-NEXT: %[[c01234:.+]] = cmpi "slt", %[[r0123]], %[[applr]]#4 : index
-// CHECK-NEXT: %[[r01234:.+]] = select %[[c01234]], %[[r0123]], %[[applr]]#4 : index
-// CHECK-NEXT: %[[c012345:.+]] = cmpi "slt", %[[r01234]], %[[applr]]#5 : index
-// CHECK-NEXT: %[[r012345:.+]] = select %[[c012345]], %[[r01234]], %[[applr]]#5 : index
-// CHECK-NEXT: %[[c0123456:.+]] = cmpi "slt", %[[r012345]], %[[applr]]#6 : index
-// CHECK-NEXT: %[[r0123456:.+]] = select %[[c0123456]], %[[r012345]], %[[applr]]#6 : index
-// CHECK-NEXT: br ^bb2(%0 : index)
-// CHECK-NEXT: ^bb2(%{{[0-9]+}}: index): // 2 preds: ^bb1, ^bb3
-// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %[[r0123456]] : index
-// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb3, ^bb4
-// CHECK-NEXT: ^bb3: // pred: ^bb2
-// CHECK-NEXT: call @body(%{{[0-9]+}}) : (index) -> ()
-// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
-// CHECK-NEXT: br ^bb2(%{{[0-9]+}} : index)
-// CHECK-NEXT: ^bb4: // pred: ^bb2
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-mlfunc @min_reduction_tree(%v : index) {
- for %i = 0 to min #map_7_values(%v)[] {
- call @body(%i) : (index) -> ()
- }
- return
-}
--- /dev/null
+// RUN: mlir-opt -lower-if-and-for %s | FileCheck %s
+
+// CHECK-DAG: [[map0:#map[0-9]+]] = () -> (0)
+// CHECK-DAG: [[map1:#map[0-9]+]] = () -> (1)
+// CHECK-DAG: [[map7:#map[0-9]+]] = () -> (7)
+// CHECK-DAG: [[map18:#map[0-9]+]] = () -> (18)
+// CHECK-DAG: [[map37:#map[0-9]+]] = () -> (37)
+// CHECK-DAG: [[map42:#map[0-9]+]] = () -> (42)
+// CHECK-DAG: [[map56:#map[0-9]+]] = () -> (56)
+// CHECK-DAG: [[map1Sym:#map[0-9]+]] = ()[s0] -> (s0)
+// CHECK-DAG: [[map1Id:#map[0-9]+]] = (d0) -> (d0)
+// CHECK-DAG: [[mapAdd1:#map[0-9]+]] = (d0) -> (d0 + 1)
+// CHECK-DAG: [[mapAdd2:#map[0-9]+]] = (d0) -> (d0 + 2)
+// CHECK-DAG: [[mapAdd3:#map[0-9]+]] = (d0) -> (d0 + 3)
+// CHECK-DAG: [[multiMap1:#map[0-9]+]] = (d0)[s0] -> (d0, d0 * -1 + s0)
+// CHECK-DAG: [[multiMap2:#map[0-9]+]] = (d0)[s0] -> (s0, d0 + 10)
+// CHECK-DAG: [[multi7Map:#map[0-9]+]] = (d0) -> (d0, d0, d0, d0, d0, d0, d0)
+// Maps produced from individual affine expressions that appear in "if" conditions.
+// CHECK-DAG: [[setMap20:#map[0-9]+]] = (d0) -> (d0 * -1 + 20)
+// CHECK-DAG: [[setMap10:#map[0-9]+]] = (d0) -> (d0 - 10)
+// CHECK-DAG: [[setMapDiff:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (d0 * -1 + s0 + 1)
+// CHECK-DAG: [[setMapS0:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s0 - 1)
+// CHECK-DAG: [[setMapS1:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s1 - 1)
+// CHECK-DAG: [[setMapS2:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s2 - 1)
+// CHECK-DAG: [[setMapS3:#map[0-9]+]] = (d0)[s0, s1, s2, s3] -> (s3 - 42)
+
+// CHECK-LABEL: cfgfunc @empty() {
+mlfunc @empty() {
+ return // CHECK: return
+} // CHECK: }
+
+extfunc @body(index) -> ()
+
+// Simple loops are properly converted.
+// CHECK-LABEL: cfgfunc @simple_loop() {
+// CHECK-NEXT: %0 = affine_apply [[map1]]()
+// CHECK-NEXT: %1 = affine_apply [[map42]]()
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%2: index): // 2 preds: ^bb0, ^bb2
+// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
+// CHECK-NEXT: cond_br %3, ^bb2, ^bb3
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: call @body(%2) : (index) -> ()
+// CHECK-NEXT: %4 = affine_apply [[mapAdd1]](%2)
+// CHECK-NEXT: br ^bb1(%4 : index)
+// CHECK-NEXT: ^bb3: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @simple_loop() {
+ for %i = 1 to 42 {
+ call @body(%i) : (index) -> ()
+ }
+ return
+}
+
+// Direct calls get renamed if asked (IR data structures properly updated) and
+// keep the same name otherwise.
+cfgfunc @simple_caller() {
+^bb0:
+// CHECK: call @simple_loop() : () -> ()
+ call @simple_loop() : () -> ()
+ return
+}
+
+// Constant loads get renamed if asked (IR data structure properly updated) and
+// keep the same name otherwise.
+cfgfunc @simple_indirect_caller() {
+^bb0:
+// CHECK: %f = constant @simple_loop : () -> ()
+ %f = constant @simple_loop : () -> ()
+ call_indirect %f() : () -> ()
+ return
+}
+
+cfgfunc @nested_attributes() {
+^bb0:
+ %0 = constant 0 : index
+// CHECK: call @body(%c0) {attr1: [@simple_loop : () -> (), @simple_loop : () -> ()]} : (index) -> ()
+ call @body(%0) {attr1: [@simple_loop : () -> (), @simple_loop : () -> ()]} : (index) -> ()
+// Note: the {{\[}} construct is necessary to prevent FileCheck from
+// interpreting [[ as the start of its variable in the pattern below.
+// CHECK: call @body(%c0) {attr2: {{\[}}{{\[}}{{\[}}@simple_loop : () -> ()]], [@simple_loop : () -> ()]]} : (index) -> ()
+ call @body(%0) {attr2: [[[@simple_loop : () -> ()]], [@simple_loop : () -> ()]]} : (index) -> ()
+ return
+}
+
+// CHECK-LABEL: cfgfunc @ml_caller() {
+mlfunc @ml_caller() {
+// Direct calls inside ML functions are renamed if asked (given that the
+// function itself is also converted).
+// CHECK: call @simple_loop() : () -> ()
+ call @simple_loop() : () -> ()
+// Direct calls to not yet declared ML functions are also renamed.
+// CHECK: call @more_imperfectly_nested_loops() : () -> ()
+ call @more_imperfectly_nested_loops() : () -> ()
+ return
+}
+
+/////////////////////////////////////////////////////////////////////
+
+extfunc @body_args(index) -> (index)
+extfunc @other(index, i32) -> (i32)
+
+// Arguments and return values of the functions are converted.
+// CHECK-LABEL: cfgfunc @mlfunc_args(%arg0: i32, %arg1: i32) -> (i32, i32) {
+// CHECK-NEXT: %c0_i32 = constant 0 : i32
+// CHECK-NEXT: %0 = affine_apply [[map0]]()
+// CHECK-NEXT: %1 = affine_apply [[map42]]()
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%2: index): // 2 preds: ^bb0, ^bb2
+// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
+// CHECK-NEXT: cond_br %3, ^bb2, ^bb3
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: %4 = call @body_args(%2) : (index) -> index
+// CHECK-NEXT: %5 = call @other(%4, %arg0) : (index, i32) -> i32
+// CHECK-NEXT: %6 = call @other(%4, %5) : (index, i32) -> i32
+// CHECK-NEXT: %7 = call @other(%4, %arg1) : (index, i32) -> i32
+// CHECK-NEXT: %8 = affine_apply [[mapAdd1]](%2)
+// CHECK-NEXT: br ^bb1(%8 : index)
+// CHECK-NEXT: ^bb3: // pred: ^bb1
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %9 = call @other(%c0, %c0_i32) : (index, i32) -> i32
+// CHECK-NEXT: return %c0_i32, %9 : i32, i32
+// CHECK-NEXT: }
+mlfunc @mlfunc_args(%a : i32, %b : i32) -> (i32, i32) {
+ %r1 = constant 0 : i32
+ for %i = 0 to 42 {
+ %1 = call @body_args(%i) : (index) -> (index)
+ %2 = call @other(%1, %a) : (index, i32) -> (i32)
+ %3 = call @other(%1, %2) : (index, i32) -> (i32)
+ %4 = call @other(%1, %b) : (index, i32) -> (i32)
+ }
+ %ri = constant 0 : index
+ %r2 = call @other(%ri, %r1) : (index, i32) -> (i32)
+ return %r1, %r2 : i32, i32
+}
+
+/////////////////////////////////////////////////////////////////////
+
+extfunc @pre(index) -> ()
+extfunc @body2(index, index) -> ()
+extfunc @post(index) -> ()
+
+// CHECK-LABEL: cfgfunc @imperfectly_nested_loops() {
+// CHECK-NEXT: %0 = affine_apply [[map0]]()
+// CHECK-NEXT: %1 = affine_apply [[map42]]()
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%2: index): // 2 preds: ^bb0, ^bb5
+// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
+// CHECK-NEXT: cond_br %3, ^bb2, ^bb6
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: call @pre(%2) : (index) -> ()
+// CHECK-NEXT: %4 = affine_apply [[map7]]()
+// CHECK-NEXT: %5 = affine_apply [[map56]]()
+// CHECK-NEXT: br ^bb3(%4 : index)
+// CHECK-NEXT: ^bb3(%6: index): // 2 preds: ^bb2, ^bb4
+// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
+// CHECK-NEXT: cond_br %7, ^bb4, ^bb5
+// CHECK-NEXT: ^bb4: // pred: ^bb3
+// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
+// CHECK-NEXT: %8 = affine_apply [[mapAdd2]](%6)
+// CHECK-NEXT: br ^bb3(%8 : index)
+// CHECK-NEXT: ^bb5: // pred: ^bb3
+// CHECK-NEXT: call @post(%2) : (index) -> ()
+// CHECK-NEXT: %9 = affine_apply [[mapAdd1]](%2)
+// CHECK-NEXT: br ^bb1(%9 : index)
+// CHECK-NEXT: ^bb6: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @imperfectly_nested_loops() {
+ for %i = 0 to 42 {
+ call @pre(%i) : (index) -> ()
+ for %j = 7 to 56 step 2 {
+ call @body2(%i, %j) : (index, index) -> ()
+ }
+ call @post(%i) : (index) -> ()
+ }
+ return
+}
+
+/////////////////////////////////////////////////////////////////////
+
+extfunc @mid(index) -> ()
+extfunc @body3(index, index) -> ()
+
+// CHECK-LABEL: cfgfunc @more_imperfectly_nested_loops() {
+// CHECK-NEXT: %0 = affine_apply [[map0]]()
+// CHECK-NEXT: %1 = affine_apply [[map42]]()
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%2: index): // 2 preds: ^bb0, ^bb8
+// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
+// CHECK-NEXT: cond_br %3, ^bb2, ^bb9
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: call @pre(%2) : (index) -> ()
+// CHECK-NEXT: %4 = affine_apply [[map7]]()
+// CHECK-NEXT: %5 = affine_apply [[map56]]()
+// CHECK-NEXT: br ^bb3(%4 : index)
+// CHECK-NEXT: ^bb3(%6: index): // 2 preds: ^bb2, ^bb4
+// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
+// CHECK-NEXT: cond_br %7, ^bb4, ^bb5
+// CHECK-NEXT: ^bb4: // pred: ^bb3
+// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
+// CHECK-NEXT: %8 = affine_apply [[mapAdd2]](%6)
+// CHECK-NEXT: br ^bb3(%8 : index)
+// CHECK-NEXT: ^bb5: // pred: ^bb3
+// CHECK-NEXT: call @mid(%2) : (index) -> ()
+// CHECK-NEXT: %9 = affine_apply [[map18]]()
+// CHECK-NEXT: %10 = affine_apply [[map37]]()
+// CHECK-NEXT: br ^bb6(%9 : index)
+// CHECK-NEXT: ^bb6(%11: index): // 2 preds: ^bb5, ^bb7
+// CHECK-NEXT: %12 = cmpi "slt", %11, %10 : index
+// CHECK-NEXT: cond_br %12, ^bb7, ^bb8
+// CHECK-NEXT: ^bb7: // pred: ^bb6
+// CHECK-NEXT: call @body3(%2, %11) : (index, index) -> ()
+// CHECK-NEXT: %13 = affine_apply [[mapAdd3]](%11)
+// CHECK-NEXT: br ^bb6(%13 : index)
+// CHECK-NEXT: ^bb8: // pred: ^bb6
+// CHECK-NEXT: call @post(%2) : (index) -> ()
+// CHECK-NEXT: %14 = affine_apply [[mapAdd1]](%2)
+// CHECK-NEXT: br ^bb1(%14 : index)
+// CHECK-NEXT: ^bb9: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @more_imperfectly_nested_loops() {
+ for %i = 0 to 42 {
+ call @pre(%i) : (index) -> ()
+ for %j = 7 to 56 step 2 {
+ call @body2(%i, %j) : (index, index) -> ()
+ }
+ call @mid(%i) : (index) -> ()
+ for %k = 18 to 37 step 3 {
+ call @body3(%i, %k) : (index, index) -> ()
+ }
+ call @post(%i) : (index) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: cfgfunc @affine_apply_loops_shorthand(%arg0: index) {
+// CHECK-NEXT: %0 = affine_apply #map3()
+// CHECK-NEXT: %1 = affine_apply #map10()[%arg0]
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%2: index): // 2 preds: ^bb0, ^bb5
+// CHECK-NEXT: %3 = cmpi "slt", %2, %1 : index
+// CHECK-NEXT: cond_br %3, ^bb2, ^bb6
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: %4 = affine_apply #map11(%2)
+// CHECK-NEXT: %5 = affine_apply #map1()
+// CHECK-NEXT: br ^bb3(%4 : index)
+// CHECK-NEXT: ^bb3(%6: index): // 2 preds: ^bb2, ^bb4
+// CHECK-NEXT: %7 = cmpi "slt", %6, %5 : index
+// CHECK-NEXT: cond_br %7, ^bb4, ^bb5
+// CHECK-NEXT: ^bb4: // pred: ^bb3
+// CHECK-NEXT: call @body2(%2, %6) : (index, index) -> ()
+// CHECK-NEXT: %8 = affine_apply #map2(%6)
+// CHECK-NEXT: br ^bb3(%8 : index)
+// CHECK-NEXT: ^bb5: // pred: ^bb3
+// CHECK-NEXT: %9 = affine_apply #map2(%2)
+// CHECK-NEXT: br ^bb1(%9 : index)
+// CHECK-NEXT: ^bb6: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @affine_apply_loops_shorthand(%N : index) {
+ for %i = 0 to %N {
+ for %j = %i to 42 {
+ call @body2(%i, %j) : (index, index) -> ()
+ }
+ }
+ return
+}
+
+/////////////////////////////////////////////////////////////////////
+
+extfunc @get_idx() -> (index)
+
+#set1 = (d0) : (20 - d0 >= 0)
+#set2 = (d0) : (d0 - 10 >= 0)
+
+// CHECK-LABEL: cfgfunc @if_only() {
+// CHECK-NEXT: %0 = call @get_idx() : () -> index
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
+// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
+// CHECK-NEXT: cond_br %2, [[thenBB:\^bb[0-9]+]], [[endBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[thenBB]]:
+// CHECK-NEXT: call @body(%0) : (index) -> ()
+// CHECK-NEXT: br [[endBB]]
+// CHECK-NEXT: [[endBB]]:
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @if_only() {
+ %i = call @get_idx() : () -> (index)
+ if #set1(%i) {
+ call @body(%i) : (index) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: cfgfunc @if_else() {
+// CHECK-NEXT: %0 = call @get_idx() : () -> index
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
+// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
+// CHECK-NEXT: cond_br %2, [[thenBB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[thenBB]]:
+// CHECK-NEXT: call @body(%0) : (index) -> ()
+// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[elseBB]]:
+// CHECK-NEXT: call @mid(%0) : (index) -> ()
+// CHECK-NEXT: br [[endBB]]
+// CHECK-NEXT: [[endBB]]:
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @if_else() {
+ %i = call @get_idx() : () -> (index)
+ if #set1(%i) {
+ call @body(%i) : (index) -> ()
+ } else {
+ call @mid(%i) : (index) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: cfgfunc @nested_ifs() {
+// CHECK-NEXT: %0 = call @get_idx() : () -> index
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %1 = affine_apply #map12(%0)
+// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
+// CHECK-NEXT: cond_br %2, ^bb1, ^bb4
+// CHECK-NEXT: ^bb1: // pred: ^bb0
+// CHECK-NEXT: %c0_0 = constant 0 : index
+// CHECK-NEXT: %3 = affine_apply #map13(%0)
+// CHECK-NEXT: %4 = cmpi "sge", %3, %c0_0 : index
+// CHECK-NEXT: cond_br %4, ^bb2, ^bb3
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: call @body(%0) : (index) -> ()
+// CHECK-NEXT: br ^bb3
+// CHECK-NEXT: ^bb3: // 2 preds: ^bb1, ^bb2
+// CHECK-NEXT: br ^bb7
+// CHECK-NEXT: ^bb4: // pred: ^bb0
+// CHECK-NEXT: %c0_1 = constant 0 : index
+// CHECK-NEXT: %5 = affine_apply #map13(%0)
+// CHECK-NEXT: %6 = cmpi "sge", %5, %c0_1 : index
+// CHECK-NEXT: cond_br %6, ^bb5, ^bb6
+// CHECK-NEXT: ^bb5: // pred: ^bb4
+// CHECK-NEXT: call @mid(%0) : (index) -> ()
+// CHECK-NEXT: br ^bb6
+// CHECK-NEXT: ^bb6: // 2 preds: ^bb4, ^bb5
+// CHECK-NEXT: br ^bb7
+// CHECK-NEXT: ^bb7: // 2 preds: ^bb3, ^bb6
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @nested_ifs() {
+ %i = call @get_idx() : () -> (index)
+ if #set1(%i) {
+ if #set2(%i) {
+ call @body(%i) : (index) -> ()
+ }
+ } else {
+ if #set2(%i) {
+ call @mid(%i) : (index) -> ()
+ }
+ }
+ return
+}
+
+#setN = (d0)[N,M,K,L] : (N - d0 + 1 >= 0, N - 1 >= 0, M - 1 >= 0, K - 1 >= 0, L - 42 == 0)
+
+// CHECK-LABEL: cfgfunc @multi_cond(%arg0: index, %arg1: index, %arg2: index, %arg3: index) {
+// CHECK-NEXT: %0 = call @get_idx() : () -> index
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %1 = affine_apply [[setMapDiff]](%0)[%arg0, %arg1, %arg2, %arg3]
+// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
+// CHECK-NEXT: cond_br %2, [[cond2BB:\^bb[0-9]+]], [[elseBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[cond2BB]]:
+// CHECK-NEXT: %3 = affine_apply [[setMapS0]](%0)[%arg0, %arg1, %arg2, %arg3]
+// CHECK-NEXT: %4 = cmpi "sge", %3, %c0 : index
+// CHECK-NEXT: cond_br %4, [[cond3BB:\^bb[0-9]+]], [[elseBB]]
+// CHECK-NEXT: [[cond3BB]]:
+// CHECK-NEXT: %5 = affine_apply [[setMapS1]](%0)[%arg0, %arg1, %arg2, %arg3]
+// CHECK-NEXT: %6 = cmpi "sge", %5, %c0 : index
+// CHECK-NEXT: cond_br %6, [[cond4BB:\^bb[0-9]+]], [[elseBB]]
+// CHECK-NEXT: [[cond4BB]]:
+// CHECK-NEXT: %7 = affine_apply [[setMapS2]](%0)[%arg0, %arg1, %arg2, %arg3]
+// CHECK-NEXT: %8 = cmpi "sge", %7, %c0 : index
+// CHECK-NEXT: cond_br %8, [[cond5BB:\^bb[0-9]+]], [[elseBB]]
+// CHECK-NEXT: [[cond5BB]]:
+// CHECK-NEXT: %9 = affine_apply [[setMapS3]](%0)[%arg0, %arg1, %arg2, %arg3]
+// CHECK-NEXT: %10 = cmpi "eq", %9, %c0 : index
+// CHECK-NEXT: cond_br %10, [[thenBB:\^bb[0-9]+]], [[elseBB]]
+// CHECK-NEXT: [[thenBB]]:
+// CHECK-NEXT: call @body(%0) : (index) -> ()
+// CHECK-NEXT: br [[endBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[elseBB]]:
+// CHECK-NEXT: call @mid(%0) : (index) -> ()
+// CHECK-NEXT: br [[endBB]]
+// CHECK-NEXT: [[endBB]]:
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @multi_cond(%N : index, %M : index, %K : index, %L : index) {
+ %i = call @get_idx() : () -> (index)
+ if #setN(%i)[%N,%M,%K,%L] {
+ call @body(%i) : (index) -> ()
+ } else {
+ call @mid(%i) : (index) -> ()
+ }
+ return
+}
+
+// CHECK-LABEL: cfgfunc @if_for() {
+mlfunc @if_for() {
+// CHECK-NEXT: %0 = call @get_idx() : () -> index
+ %i = call @get_idx() : () -> (index)
+// CHECK-NEXT: %c0 = constant 0 : index
+// CHECK-NEXT: %1 = affine_apply [[setMap20]](%0)
+// CHECK-NEXT: %2 = cmpi "sge", %1, %c0 : index
+// CHECK-NEXT: cond_br %2, [[midLoopInitBB:\^bb[0-9]+]], [[outerEndBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[midLoopInitBB]]:
+// CHECK-NEXT: %3 = affine_apply [[map0]]()
+// CHECK-NEXT: %4 = affine_apply [[map42]]()
+// CHECK-NEXT: br [[midLoopCondBB:\^bb[0-9]+]](%3 : index)
+// CHECK-NEXT: [[midLoopCondBB]](%5: index):
+// CHECK-NEXT: %6 = cmpi "slt", %5, %4 : index
+// CHECK-NEXT: cond_br %6, [[midLoopBodyBB:\^bb[0-9]+]], [[outerEndBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[midLoopBodyBB]]:
+// CHECK-NEXT: %c0_0 = constant 0 : index
+// CHECK-NEXT: %7 = affine_apply [[setMap10]](%5)
+// CHECK-NEXT: %8 = cmpi "sge", %7, %c0_0 : index
+// CHECK-NEXT: cond_br %8, [[innerThenBB:\^bb[0-9]+]], [[innerEndBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[innerThenBB:\^bb[0-9]+]]:
+// CHECK-NEXT: call @body2(%0, %5) : (index, index) -> ()
+// CHECK-NEXT: br [[innerEndBB]]
+// CHECK-NEXT: [[innerEndBB]]:
+// CHECK-NEXT: %9 = affine_apply [[mapAdd1]](%5)
+// CHECK-NEXT: br [[midLoopCondBB]](%9 : index)
+// CHECK-NEXT: [[outerEndBB]]:
+// CHECK-NEXT: br [[outerLoopInit:\^bb[0-9]+]]
+ if #set1(%i) {
+ for %j = 0 to 42 {
+ if #set2(%j) {
+ call @body2(%i, %j) : (index, index) -> ()
+ }
+ }
+ }
+// CHECK-NEXT: [[outerLoopInit]]:
+// CHECK-NEXT: %10 = affine_apply [[map0]]()
+// CHECK-NEXT: %11 = affine_apply [[map42]]()
+// CHECK-NEXT: br [[outerLoopCond:\^bb[0-9]+]](%10 : index)
+// CHECK-NEXT: [[outerLoopCond]](%12: index):
+// CHECK-NEXT: %13 = cmpi "slt", %12, %11 : index
+// CHECK-NEXT: cond_br %13, [[outerLoopBody:\^bb[0-9]+]], [[outerLoopEnd:\^bb[0-9]+]]
+// CHECK-NEXT: [[outerLoopBody]]:
+// CHECK-NEXT: %c0_1 = constant 0 : index
+// CHECK-NEXT: %14 = affine_apply [[setMap10]](%12)
+// CHECK-NEXT: %15 = cmpi "sge", %14, %c0_1 : index
+// CHECK-NEXT: cond_br %15, [[innerLoopInitBB:\^bb[0-9]+]], [[midEndBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[innerLoopInitBB:\^bb[0-9]+]]:
+// CHECK-NEXT: %16 = affine_apply [[map0]]()
+// CHECK-NEXT: %17 = affine_apply [[map42]]()
+// CHECK-NEXT: br [[innerLoopCondBB:\^bb[0-9]+]](%16 : index)
+// CHECK-NEXT: [[innerLoopCondBB]](%18: index):
+// CHECK-NEXT: %19 = cmpi "slt", %18, %17 : index
+// CHECK-NEXT: cond_br %19, [[innerLoopBodyBB:\^bb[0-9]+]], [[innerLoopEndBB:\^bb[0-9]+]]
+// CHECK-NEXT: [[innerLoopBodyBB]]:
+// CHECK-NEXT: call @body3(%12, %18) : (index, index) -> ()
+// CHECK-NEXT: %20 = affine_apply [[mapAdd1]](%18)
+// CHECK-NEXT: br [[innerLoopCondBB]](%20 : index)
+// CHECK-NEXT: [[innerLoopEndBB]]:
+// CHECK-NEXT: br [[midEndBB]]
+// CHECK-NEXT: [[midEndBB]]:
+// CHECK-NEXT: %21 = affine_apply [[mapAdd1]](%12)
+// CHECK-NEXT: br [[outerLoopCond]](%21 : index)
+ for %k = 0 to 42 {
+ if #set2(%k) {
+ for %l = 0 to 42 {
+ call @body3(%k, %l) : (index, index) -> ()
+ }
+ }
+ }
+// CHECK-NEXT: [[outerLoopEnd]]:
+// CHECK-NEXT: return
+ return
+}
+
+#lbMultiMap = (d0)[s0] -> (d0, s0 - d0)
+#ubMultiMap = (d0)[s0] -> (s0, d0 + 10)
+
+// CHECK-LABEL: cfgfunc @loop_min_max(%arg0: index) {
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map0]]()
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map42]]()
+// CHECK-NEXT: br ^bb1(%{{[0-9]+}} : index)
+// CHECK-NEXT: ^bb1(%{{[0-9]+}}: index): // 2 preds: ^bb0, ^bb5
+// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %{{[0-9]+}} : index
+// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb2, ^bb6
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: %[[lb:[0-9]+]] = affine_apply [[multiMap1]](%{{[0-9]+}})[%arg0]
+// CHECK-NEXT: %[[lbc:[0-9]+]] = cmpi "sgt", %[[lb]]#0, %[[lb]]#1 : index
+// CHECK-NEXT: %[[lbv:[0-9]+]] = select %[[lbc]], %[[lb]]#0, %[[lb]]#1 : index
+// CHECK-NEXT: %[[ub:[0-9]+]] = affine_apply [[multiMap2]](%{{[0-9]+}})[%arg0]
+// CHECK-NEXT: %[[ubc:[0-9]+]] = cmpi "slt", %[[ub]]#0, %[[ub]]#1 : index
+// CHECK-NEXT: %[[ubv:[0-9]+]] = select %[[ubc]], %[[ub]]#0, %[[ub]]#1 : index
+// CHECK-NEXT: br ^bb3(%[[lbv]] : index)
+// CHECK-NEXT: ^bb3(%{{[0-9]+}}: index): // 2 preds: ^bb2, ^bb4
+// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %[[ubv]] : index
+// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb4, ^bb5
+// CHECK-NEXT: ^bb4: // pred: ^bb3
+// CHECK-NEXT: call @body2(%{{[0-9]+}}, %{{[0-9]+}}) : (index, index) -> ()
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
+// CHECK-NEXT: br ^bb3(%{{[0-9]+}} : index)
+// CHECK-NEXT: ^bb5: // pred: ^bb3
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
+// CHECK-NEXT: br ^bb1(%{{[0-9]+}} : index)
+// CHECK-NEXT: ^bb6: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @loop_min_max(%N : index) {
+ for %i = 0 to 42 {
+ for %j = max #lbMultiMap(%i)[%N] to min #ubMultiMap(%i)[%N] {
+ call @body2(%i, %j) : (index, index) -> ()
+ }
+ }
+ return
+}
+
+#map_7_values = (i) -> (i, i, i, i, i, i, i)
+
+// Check that the "min" (cmpi "slt" + select) reduction sequence is emitted
+// correctly for a an affine map with 7 results.
+
+// CHECK-LABEL: cfgfunc @min_reduction_tree(%arg0: index) {
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[map0]]()
+// CHECK-NEXT: %[[applr:[0-9]+]] = affine_apply [[multi7Map]](%arg0)
+// CHECK-NEXT: %[[c01:.+]] = cmpi "slt", %[[applr]]#0, %[[applr]]#1 : index
+// CHECK-NEXT: %[[r01:.+]] = select %[[c01]], %[[applr]]#0, %[[applr]]#1 : index
+// CHECK-NEXT: %[[c012:.+]] = cmpi "slt", %[[r01]], %[[applr]]#2 : index
+// CHECK-NEXT: %[[r012:.+]] = select %[[c012]], %[[r01]], %[[applr]]#2 : index
+// CHECK-NEXT: %[[c0123:.+]] = cmpi "slt", %[[r012]], %[[applr]]#3 : index
+// CHECK-NEXT: %[[r0123:.+]] = select %[[c0123]], %[[r012]], %[[applr]]#3 : index
+// CHECK-NEXT: %[[c01234:.+]] = cmpi "slt", %[[r0123]], %[[applr]]#4 : index
+// CHECK-NEXT: %[[r01234:.+]] = select %[[c01234]], %[[r0123]], %[[applr]]#4 : index
+// CHECK-NEXT: %[[c012345:.+]] = cmpi "slt", %[[r01234]], %[[applr]]#5 : index
+// CHECK-NEXT: %[[r012345:.+]] = select %[[c012345]], %[[r01234]], %[[applr]]#5 : index
+// CHECK-NEXT: %[[c0123456:.+]] = cmpi "slt", %[[r012345]], %[[applr]]#6 : index
+// CHECK-NEXT: %[[r0123456:.+]] = select %[[c0123456]], %[[r012345]], %[[applr]]#6 : index
+// CHECK-NEXT: br ^bb1(%0 : index)
+// CHECK-NEXT: ^bb1(%{{[0-9]+}}: index): // 2 preds: ^bb0, ^bb2
+// CHECK-NEXT: %{{[0-9]+}} = cmpi "slt", %{{[0-9]+}}, %[[r0123456]] : index
+// CHECK-NEXT: cond_br %{{[0-9]+}}, ^bb2, ^bb3
+// CHECK-NEXT: ^bb2: // pred: ^bb1
+// CHECK-NEXT: call @body(%{{[0-9]+}}) : (index) -> ()
+// CHECK-NEXT: %{{[0-9]+}} = affine_apply [[mapAdd1]](%{{[0-9]+}})
+// CHECK-NEXT: br ^bb1(%{{[0-9]+}} : index)
+// CHECK-NEXT: ^bb3: // pred: ^bb1
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+mlfunc @min_reduction_tree(%v : index) {
+ for %i = 0 to min #map_7_values(%v)[] {
+ call @body(%i) : (index) -> ()
+ }
+ return
+}