Skip to content

[mlir][loops] Add getters for multi dim loop variables in LoopLikeOpInterface #94516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 7, 2024
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
Loading