Skip to content

Commit

Permalink
[mlir][loops] Add getters for multi dim loop variables in `LoopLikeOp…
Browse files Browse the repository at this point in the history
…Interface` (#94516)

This patch adds `getLoopInductionVars`, `getLoopLowerBounds`,
`getLoopBounds`, `getLoopSteps` interface methods to
`LoopLIkeOpInterface`. The corresponding single value versions have been
moved to shared class declaration and have been implemented based on the
new interface methods.
  • Loading branch information
srcarroll authored Jun 7, 2024
1 parent 33f4a77 commit 6b4c122
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 125 deletions.
4 changes: 2 additions & 2 deletions mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ def AffineForOp : Affine_Op<"for",
[AttrSizedOperandSegments, AutomaticAllocationScope,
ImplicitAffineTerminator, ConditionallySpeculatable,
RecursiveMemoryEffects, DeclareOpInterfaceMethods<LoopLikeOpInterface,
["getSingleInductionVar", "getSingleLowerBound", "getSingleStep",
"getSingleUpperBound", "getYieldedValuesMutable",
["getLoopInductionVars", "getLoopLowerBounds", "getLoopSteps",
"getLoopUpperBounds", "getYieldedValuesMutable",
"replaceWithAdditionalYields"]>,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getEntrySuccessorOperands"]>]> {
Expand Down
50 changes: 29 additions & 21 deletions mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ def ExecuteRegionOp : SCF_Op<"execute_region", [
def ForOp : SCF_Op<"for",
[AutomaticAllocationScope, DeclareOpInterfaceMethods<LoopLikeOpInterface,
["getInitsMutable", "getLoopResults", "getRegionIterArgs",
"getSingleInductionVar", "getSingleLowerBound", "getSingleStep",
"getSingleUpperBound", "getYieldedValuesMutable",
"getLoopInductionVars", "getLoopLowerBounds", "getLoopSteps",
"getLoopUpperBounds", "getYieldedValuesMutable",
"promoteIfSingleIteration", "replaceWithAdditionalYields",
"yieldTiledValuesAndReplace"]>,
AllTypesMatch<["lowerBound", "upperBound", "step"]>,
Expand Down Expand Up @@ -301,8 +301,8 @@ def ForallOp : SCF_Op<"forall", [
AttrSizedOperandSegments,
AutomaticAllocationScope,
DeclareOpInterfaceMethods<LoopLikeOpInterface,
["getInitsMutable", "getRegionIterArgs", "getSingleInductionVar",
"getSingleLowerBound", "getSingleUpperBound", "getSingleStep",
["getInitsMutable", "getRegionIterArgs", "getLoopInductionVars",
"getLoopLowerBounds", "getLoopUpperBounds", "getLoopSteps",
"promoteIfSingleIteration", "yieldTiledValuesAndReplace"]>,
RecursiveMemoryEffects,
SingleBlockImplicitTerminator<"scf::InParallelOp">,
Expand Down Expand Up @@ -510,22 +510,31 @@ def ForallOp : SCF_Op<"forall", [
];

let extraClassDeclaration = [{
// Get lower bounds as OpFoldResult.
/// Get induction variables.
SmallVector<Value> getInductionVars() {
std::optional<SmallVector<Value>> maybeInductionVars = getLoopInductionVars();
assert(maybeInductionVars.has_value() && "expected values");
return *maybeInductionVars;
}
/// Get lower bounds as OpFoldResult.
SmallVector<OpFoldResult> getMixedLowerBound() {
Builder b(getOperation()->getContext());
return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(), b);
std::optional<SmallVector<OpFoldResult>> maybeLowerBounds = getLoopLowerBounds();
assert(maybeLowerBounds.has_value() && "expected values");
return *maybeLowerBounds;
}

// Get upper bounds as OpFoldResult.
/// Get upper bounds as OpFoldResult.
SmallVector<OpFoldResult> getMixedUpperBound() {
Builder b(getOperation()->getContext());
return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(), b);
std::optional<SmallVector<OpFoldResult>> maybeUpperBounds = getLoopUpperBounds();
assert(maybeUpperBounds.has_value() && "expected values");
return *maybeUpperBounds;
}

// Get steps as OpFoldResult.
/// Get steps as OpFoldResult.
SmallVector<OpFoldResult> getMixedStep() {
Builder b(getOperation()->getContext());
return getMixedValues(getStaticStep(), getDynamicStep(), b);
std::optional<SmallVector<OpFoldResult>> maybeSteps = getLoopSteps();
assert(maybeSteps.has_value() && "expected values");
return *maybeSteps;
}

/// Get lower bounds as values.
Expand Down Expand Up @@ -584,10 +593,6 @@ def ForallOp : SCF_Op<"forall", [
getNumDynamicControlOperands() + getRank());
}

::mlir::ValueRange getInductionVars() {
return getBody()->getArguments().take_front(getRank());
}

::mlir::Value getInductionVar(int64_t idx) {
return getInductionVars()[idx];
}
Expand Down Expand Up @@ -765,8 +770,8 @@ def IfOp : SCF_Op<"if", [DeclareOpInterfaceMethods<RegionBranchOpInterface, [
def ParallelOp : SCF_Op<"parallel",
[AutomaticAllocationScope,
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<LoopLikeOpInterface, ["getSingleInductionVar",
"getSingleLowerBound", "getSingleUpperBound", "getSingleStep"]>,
DeclareOpInterfaceMethods<LoopLikeOpInterface, ["getLoopInductionVars",
"getLoopLowerBounds", "getLoopUpperBounds", "getLoopSteps"]>,
RecursiveMemoryEffects,
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
SingleBlockImplicitTerminator<"scf::ReduceOp">,
Expand Down Expand Up @@ -846,8 +851,11 @@ def ParallelOp : SCF_Op<"parallel",
];

let extraClassDeclaration = [{
ValueRange getInductionVars() {
return getBody()->getArguments();
/// Get induction variables.
SmallVector<Value> getInductionVars() {
std::optional<SmallVector<Value>> maybeInductionVars = getLoopInductionVars();;
assert(maybeInductionVars.has_value() && "expected values");
return *maybeInductionVars;
}
unsigned getNumLoops() { return getStep().size(); }
unsigned getNumReductions() { return getInitVals().size(); }
Expand Down
81 changes: 61 additions & 20 deletions mlir/include/mlir/Interfaces/LoopLikeInterface.td
Original file line number Diff line number Diff line change
Expand Up @@ -93,51 +93,59 @@ def LoopLikeOpInterface : OpInterface<"LoopLikeOpInterface"> {
}]
>,
InterfaceMethod<[{
If there is a single induction variable return it, otherwise return
std::nullopt.
Return all induction variables, if they exist. If the op has no notion of
induction variable, then return std::nullopt. If it does have
a notion but an instance doesn't have induction variables, then
return empty vector.
}],
/*retTy=*/"::std::optional<::mlir::Value>",
/*methodName=*/"getSingleInductionVar",
/*retTy=*/"::std::optional<::llvm::SmallVector<::mlir::Value>>",
/*methodName=*/"getLoopInductionVars",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/[{
return std::nullopt;
return ::std::nullopt;
}]
>,
InterfaceMethod<[{
Return the single lower bound value or attribute if it exists, otherwise
return std::nullopt.
Return all lower bounds, if they exist. If the op has no notion of
lower bounds, then return std::nullopt. If it does have
a notion but an instance doesn't have lower bounds, then
return empty vector.
}],
/*retTy=*/"::std::optional<::mlir::OpFoldResult>",
/*methodName=*/"getSingleLowerBound",
/*retTy=*/"::std::optional<::llvm::SmallVector<::mlir::OpFoldResult>>",
/*methodName=*/"getLoopLowerBounds",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/[{
return std::nullopt;
return ::std::nullopt;
}]
>,
InterfaceMethod<[{
Return the single step value or attribute if it exists, otherwise
return std::nullopt.
Return all steps, if they exist. If the op has no notion of
steps, then return std::nullopt. If it does have
a notion but an instance doesn't have steps, then
return empty vector.
}],
/*retTy=*/"::std::optional<::mlir::OpFoldResult>",
/*methodName=*/"getSingleStep",
/*retTy=*/"::std::optional<::llvm::SmallVector<::mlir::OpFoldResult>>",
/*methodName=*/"getLoopSteps",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/[{
return std::nullopt;
return ::std::nullopt;
}]
>,
InterfaceMethod<[{
Return the single upper bound value or attribute if it exists, otherwise
return std::nullopt.
Return all upper bounds, if they exist. If the op has no notion of
lower bounds, then return std::nullopt. If it does have
a notion but an instance doesn't have lower bounds, then
return empty vector.
}],
/*retTy=*/"::std::optional<::mlir::OpFoldResult>",
/*methodName=*/"getSingleUpperBound",
/*retTy=*/"::std::optional<::llvm::SmallVector<::mlir::OpFoldResult>>",
/*methodName=*/"getLoopUpperBounds",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/[{
return std::nullopt;
return ::std::nullopt;
}]
>,
InterfaceMethod<[{
Expand Down Expand Up @@ -235,6 +243,39 @@ def LoopLikeOpInterface : OpInterface<"LoopLikeOpInterface"> {
}];

let extraSharedClassDeclaration = [{
/// If there is a single induction variable return it, otherwise return
/// std::nullopt.
::std::optional<::mlir::Value> getSingleInductionVar() {
auto inductionVars = this->getLoopInductionVars();
if (inductionVars.has_value() && (*inductionVars).size() == 1)
return (*inductionVars)[0];
return std::nullopt;
}
/// Return the single lower bound value or attribute if it exists, otherwise
/// return std::nullopt.
::std::optional<::mlir::OpFoldResult> getSingleLowerBound() {
auto lowerBounds = this->getLoopLowerBounds();
if (lowerBounds.has_value() && (*lowerBounds).size() == 1)
return (*lowerBounds)[0];
return std::nullopt;
}
/// Return the single step value or attribute if it exists, otherwise
/// return std::nullopt.
::std::optional<::mlir::OpFoldResult> getSingleStep() {
auto steps = this->getLoopSteps();
if (steps.has_value() && (*steps).size() == 1)
return (*steps)[0];
return std::nullopt;
}
/// Return the single upper bound value or attribute if it exists, otherwise
/// return std::nullopt.
::std::optional<::mlir::OpFoldResult> getSingleUpperBound() {
auto upperBounds = this->getLoopUpperBounds();
if (upperBounds.has_value() && (*upperBounds).size() == 1)
return (*upperBounds)[0];
return std::nullopt;
}

/// Append the specified additional "init" operands: replace this loop with
/// a new loop that has the additional init operands. The loop body of this
/// loop is moved over to the new loop.
Expand Down
21 changes: 12 additions & 9 deletions mlir/lib/Dialect/Affine/IR/AffineOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2454,27 +2454,30 @@ bool AffineForOp::matchingBoundOperandList() {

SmallVector<Region *> AffineForOp::getLoopRegions() { return {&getRegion()}; }

std::optional<Value> AffineForOp::getSingleInductionVar() {
return getInductionVar();
std::optional<SmallVector<Value>> AffineForOp::getLoopInductionVars() {
return SmallVector<Value>{getInductionVar()};
}

std::optional<OpFoldResult> AffineForOp::getSingleLowerBound() {
std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopLowerBounds() {
if (!hasConstantLowerBound())
return std::nullopt;
OpBuilder b(getContext());
return OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound()));
return SmallVector<OpFoldResult>{
OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound()))};
}

std::optional<OpFoldResult> AffineForOp::getSingleStep() {
std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopSteps() {
OpBuilder b(getContext());
return OpFoldResult(b.getI64IntegerAttr(getStepAsInt()));
return SmallVector<OpFoldResult>{
OpFoldResult(b.getI64IntegerAttr(getStepAsInt()))};
}

std::optional<OpFoldResult> AffineForOp::getSingleUpperBound() {
std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopUpperBounds() {
if (!hasConstantUpperBound())
return std::nullopt;
return {};
OpBuilder b(getContext());
return OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound()));
return SmallVector<OpFoldResult>{
OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound()))};
}

FailureOr<LoopLikeOpInterface> AffineForOp::replaceWithAdditionalYields(
Expand Down
3 changes: 1 addition & 2 deletions mlir/lib/Dialect/Linalg/Transforms/Loops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,7 @@ static void replaceIndexOpsByInductionVariables(RewriterBase &rewriter,
for (Operation *loopOp : loopOps) {
llvm::TypeSwitch<Operation *>(loopOp)
.Case([&](scf::ParallelOp parallelOp) {
allIvs.append(parallelOp.getInductionVars().begin(),
parallelOp.getInductionVars().end());
allIvs.append(parallelOp.getInductionVars());
})
.Case([&](scf::ForOp forOp) {
allIvs.push_back(forOp.getInductionVar());
Expand Down
6 changes: 3 additions & 3 deletions mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ static void calculateTileOffsetsAndSizes(
OpBuilder::InsertionGuard g(b);
b.setInsertionPointToStart(forallOp.getBody(0));

ValueRange threadIds = forallOp.getInductionVars();
SmallVector<Value> threadIds = forallOp.getInductionVars();
SmallVector<OpFoldResult> nonZeroNumThreads =
llvm::to_vector(llvm::make_filter_range(numThreads, [](OpFoldResult ofr) {
return !isConstantIntValue(ofr, 0);
Expand Down Expand Up @@ -746,7 +746,7 @@ FailureOr<linalg::ForallReductionTilingResult> linalg::tileReductionUsingForall(
b.getIndexAttr(0));
SmallVector<OpFoldResult> sizes = tiledSizes;
sizes[reductionDim] = b.getIndexAttr(1);
outOffsets[reductionDim] = forallOp.getInductionVars().front();
outOffsets[reductionDim] = forallOp.getInductionVars()[0];
// TODO: use SubsetExtractOpInterface once it is available.
tiledDpsInitOperands.push_back(b.create<tensor::ExtractSliceOp>(
loc, cast<RankedTensorType>(initOperand.getType()),
Expand Down Expand Up @@ -814,7 +814,7 @@ FailureOr<linalg::ForallReductionTilingResult> linalg::tileReductionUsingForall(
int64_t sizeIdx = 0;
for (int64_t i = 0, e = numThreads.size(); i < e; ++i) {
if (i == reductionDim) {
resultOffsetsRank.push_back(forallOp.getInductionVars().front());
resultOffsetsRank.push_back(forallOp.getInductionVars()[0]);
resultSizesRank.push_back(b.getIndexAttr(1));
continue;
}
Expand Down
Loading

0 comments on commit 6b4c122

Please sign in to comment.