//===- Builders.cpp - MLIR Declarative Builder Classes --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/Dialect/SCF/EDSC/Builders.h" #include "mlir/Dialect/SCF/SCF.h" #include "mlir/IR/AffineExpr.h" #include "mlir/IR/AffineMap.h" using namespace mlir; using namespace mlir::edsc; mlir::edsc::ParallelLoopNestBuilder::ParallelLoopNestBuilder( MutableArrayRef ivs, ArrayRef lbs, ArrayRef ubs, ArrayRef steps) { assert(ivs.size() == lbs.size() && "Mismatch in number of arguments"); assert(ivs.size() == ubs.size() && "Mismatch in number of arguments"); assert(ivs.size() == steps.size() && "Mismatch in number of arguments"); loops.emplace_back(makeParallelLoopBuilder(ivs, lbs, ubs, steps)); } void mlir::edsc::ParallelLoopNestBuilder::operator()( function_ref fun) { if (fun) fun(); // Iterate on the calling operator() on all the loops in the nest. // The iteration order is from innermost to outermost because enter/exit needs // to be asymmetric (i.e. enter() occurs on LoopBuilder construction, exit() // occurs on calling operator()). The asymmetry is required for properly // nesting imperfectly nested regions (see LoopBuilder::operator()). for (auto lit = loops.rbegin(), eit = loops.rend(); lit != eit; ++lit) (*lit)(); } mlir::edsc::LoopNestBuilder::LoopNestBuilder(MutableArrayRef ivs, ArrayRef lbs, ArrayRef ubs, ArrayRef steps) { assert(ivs.size() == lbs.size() && "expected size of ivs and lbs to match"); assert(ivs.size() == ubs.size() && "expected size of ivs and ubs to match"); assert(ivs.size() == steps.size() && "expected size of ivs and steps to match"); loops.reserve(ivs.size()); for (auto it : llvm::zip(ivs, lbs, ubs, steps)) loops.emplace_back(makeLoopBuilder(&std::get<0>(it), std::get<1>(it), std::get<2>(it), std::get<3>(it))); assert(loops.size() == ivs.size() && "Mismatch loops vs ivs size"); } mlir::edsc::LoopNestBuilder::LoopNestBuilder( Value *iv, Value lb, Value ub, Value step, MutableArrayRef iterArgsHandles, ValueRange iterArgsInitValues) { assert(iterArgsInitValues.size() == iterArgsHandles.size() && "expected size of arguments and argument_handles to match"); loops.emplace_back( makeLoopBuilder(iv, lb, ub, step, iterArgsHandles, iterArgsInitValues)); } mlir::edsc::LoopNestBuilder::LoopNestBuilder(Value *iv, Value lb, Value ub, Value step) { SmallVector noArgs; loops.emplace_back(makeLoopBuilder(iv, lb, ub, step, noArgs, {})); } ValueRange mlir::edsc::LoopNestBuilder::LoopNestBuilder::operator()( std::function fun) { if (fun) fun(); for (auto &lit : reverse(loops)) lit({}); if (!loops.empty()) return loops[0].getOp()->getResults(); return {}; } LoopBuilder mlir::edsc::makeParallelLoopBuilder(MutableArrayRef ivs, ArrayRef lbs, ArrayRef ubs, ArrayRef steps) { scf::ParallelOp parallelOp = OperationBuilder( SmallVector(lbs.begin(), lbs.end()), SmallVector(ubs.begin(), ubs.end()), SmallVector(steps.begin(), steps.end())); for (size_t i = 0, e = ivs.size(); i < e; ++i) ivs[i] = parallelOp.getBody()->getArgument(i); LoopBuilder result; result.enter(parallelOp.getBody()); return result; } mlir::edsc::LoopBuilder mlir::edsc::makeLoopBuilder(Value *iv, Value lb, Value ub, Value step, MutableArrayRef iterArgsHandles, ValueRange iterArgsInitValues) { mlir::edsc::LoopBuilder result; scf::ForOp forOp = OperationBuilder(lb, ub, step, iterArgsInitValues); *iv = Value(forOp.getInductionVar()); auto *body = scf::getForInductionVarOwner(*iv).getBody(); for (size_t i = 0, e = iterArgsHandles.size(); i < e; ++i) { // Skipping the induction variable. iterArgsHandles[i] = body->getArgument(i + 1); } result.setOp(forOp); result.enter(body); return result; } mlir::scf::ValueVector mlir::edsc::loopNestBuilder(ValueRange lbs, ValueRange ubs, ValueRange steps, function_ref fun) { // Delegates actual construction to scf::buildLoopNest by wrapping `fun` into // the expected function interface. assert(ScopedContext::getContext() && "EDSC ScopedContext not set up"); return mlir::scf::buildLoopNest( ScopedContext::getBuilderRef(), ScopedContext::getLocation(), lbs, ubs, steps, [&](OpBuilder &builder, Location loc, ValueRange ivs) { ScopedContext context(builder, loc); if (fun) fun(ivs); }); } mlir::scf::ValueVector mlir::edsc::loopNestBuilder(Value lb, Value ub, Value step, function_ref fun) { // Delegates to the ValueRange-based version by wrapping the lambda. auto wrapper = [&](ValueRange ivs) { assert(ivs.size() == 1); if (fun) fun(ivs[0]); }; return loopNestBuilder(ValueRange(lb), ValueRange(ub), ValueRange(step), wrapper); } mlir::scf::ValueVector mlir::edsc::loopNestBuilder( Value lb, Value ub, Value step, ValueRange iterArgInitValues, function_ref fun) { // Delegates actual construction to scf::buildLoopNest by wrapping `fun` into // the expected function interface. assert(ScopedContext::getContext() && "EDSC ScopedContext not set up"); return mlir::scf::buildLoopNest( ScopedContext::getBuilderRef(), ScopedContext::getLocation(), lb, ub, step, iterArgInitValues, [&](OpBuilder &builder, Location loc, ValueRange ivs, ValueRange args) { assert(ivs.size() == 1 && "expected one induction variable"); ScopedContext context(builder, loc); if (fun) return fun(ivs[0], args); return scf::ValueVector(iterArgInitValues.begin(), iterArgInitValues.end()); }); }