diff --git a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td index 3640055ea8da8f..dbec741cf1b1f3 100644 --- a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td +++ b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td @@ -118,8 +118,8 @@ def AffineForOp : Affine_Op<"for", [AttrSizedOperandSegments, AutomaticAllocationScope, ImplicitAffineTerminator, ConditionallySpeculatable, RecursiveMemoryEffects, DeclareOpInterfaceMethods, DeclareOpInterfaceMethods]> { diff --git a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td index 0b063aa772bab0..f35ea962bea168 100644 --- a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td +++ b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td @@ -136,8 +136,8 @@ def ExecuteRegionOp : SCF_Op<"execute_region", [ def ForOp : SCF_Op<"for", [AutomaticAllocationScope, DeclareOpInterfaceMethods, AllTypesMatch<["lowerBound", "upperBound", "step"]>, @@ -301,8 +301,8 @@ def ForallOp : SCF_Op<"forall", [ AttrSizedOperandSegments, AutomaticAllocationScope, DeclareOpInterfaceMethods, RecursiveMemoryEffects, SingleBlockImplicitTerminator<"scf::InParallelOp">, @@ -510,22 +510,31 @@ def ForallOp : SCF_Op<"forall", [ ]; let extraClassDeclaration = [{ - // Get lower bounds as OpFoldResult. + /// Get induction variables. + SmallVector getInductionVars() { + std::optional> maybeInductionVars = getLoopInductionVars(); + assert(maybeInductionVars.has_value() && "expected values"); + return *maybeInductionVars; + } + /// Get lower bounds as OpFoldResult. SmallVector getMixedLowerBound() { - Builder b(getOperation()->getContext()); - return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(), b); + std::optional> maybeLowerBounds = getLoopLowerBounds(); + assert(maybeLowerBounds.has_value() && "expected values"); + return *maybeLowerBounds; } - // Get upper bounds as OpFoldResult. + /// Get upper bounds as OpFoldResult. SmallVector getMixedUpperBound() { - Builder b(getOperation()->getContext()); - return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(), b); + std::optional> maybeUpperBounds = getLoopUpperBounds(); + assert(maybeUpperBounds.has_value() && "expected values"); + return *maybeUpperBounds; } - // Get steps as OpFoldResult. + /// Get steps as OpFoldResult. SmallVector getMixedStep() { - Builder b(getOperation()->getContext()); - return getMixedValues(getStaticStep(), getDynamicStep(), b); + std::optional> maybeSteps = getLoopSteps(); + assert(maybeSteps.has_value() && "expected values"); + return *maybeSteps; } /// Get lower bounds as values. @@ -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]; } @@ -765,8 +770,8 @@ def IfOp : SCF_Op<"if", [DeclareOpInterfaceMethods, + DeclareOpInterfaceMethods, RecursiveMemoryEffects, DeclareOpInterfaceMethods, SingleBlockImplicitTerminator<"scf::ReduceOp">, @@ -846,8 +851,11 @@ def ParallelOp : SCF_Op<"parallel", ]; let extraClassDeclaration = [{ - ValueRange getInductionVars() { - return getBody()->getArguments(); + /// Get induction variables. + SmallVector getInductionVars() { + std::optional> maybeInductionVars = getLoopInductionVars();; + assert(maybeInductionVars.has_value() && "expected values"); + return *maybeInductionVars; } unsigned getNumLoops() { return getStep().size(); } unsigned getNumReductions() { return getInitVals().size(); } diff --git a/mlir/include/mlir/Interfaces/LoopLikeInterface.td b/mlir/include/mlir/Interfaces/LoopLikeInterface.td index f0dc6e60eba584..b748d5e29114a1 100644 --- a/mlir/include/mlir/Interfaces/LoopLikeInterface.td +++ b/mlir/include/mlir/Interfaces/LoopLikeInterface.td @@ -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<[{ @@ -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. diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp index 2e31487bd55a0a..0a58d2fdb02f5c 100644 --- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp +++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp @@ -2454,27 +2454,30 @@ bool AffineForOp::matchingBoundOperandList() { SmallVector AffineForOp::getLoopRegions() { return {&getRegion()}; } -std::optional AffineForOp::getSingleInductionVar() { - return getInductionVar(); +std::optional> AffineForOp::getLoopInductionVars() { + return SmallVector{getInductionVar()}; } -std::optional AffineForOp::getSingleLowerBound() { +std::optional> AffineForOp::getLoopLowerBounds() { if (!hasConstantLowerBound()) return std::nullopt; OpBuilder b(getContext()); - return OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound())); + return SmallVector{ + OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound()))}; } -std::optional AffineForOp::getSingleStep() { +std::optional> AffineForOp::getLoopSteps() { OpBuilder b(getContext()); - return OpFoldResult(b.getI64IntegerAttr(getStepAsInt())); + return SmallVector{ + OpFoldResult(b.getI64IntegerAttr(getStepAsInt()))}; } -std::optional AffineForOp::getSingleUpperBound() { +std::optional> AffineForOp::getLoopUpperBounds() { if (!hasConstantUpperBound()) - return std::nullopt; + return {}; OpBuilder b(getContext()); - return OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound())); + return SmallVector{ + OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound()))}; } FailureOr AffineForOp::replaceWithAdditionalYields( diff --git a/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp b/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp index b0a4de2da1e869..8b0e04fb61b1bd 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp @@ -184,8 +184,7 @@ static void replaceIndexOpsByInductionVariables(RewriterBase &rewriter, for (Operation *loopOp : loopOps) { llvm::TypeSwitch(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()); diff --git a/mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp b/mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp index fd314ef9f81347..a0a0e11a6903d7 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Tiling.cpp @@ -243,7 +243,7 @@ static void calculateTileOffsetsAndSizes( OpBuilder::InsertionGuard g(b); b.setInsertionPointToStart(forallOp.getBody(0)); - ValueRange threadIds = forallOp.getInductionVars(); + SmallVector threadIds = forallOp.getInductionVars(); SmallVector nonZeroNumThreads = llvm::to_vector(llvm::make_filter_range(numThreads, [](OpFoldResult ofr) { return !isConstantIntValue(ofr, 0); @@ -746,7 +746,7 @@ FailureOr linalg::tileReductionUsingForall( b.getIndexAttr(0)); SmallVector 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( loc, cast(initOperand.getType()), @@ -814,7 +814,7 @@ FailureOr 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; } diff --git a/mlir/lib/Dialect/SCF/IR/SCF.cpp b/mlir/lib/Dialect/SCF/IR/SCF.cpp index 107fd0690f193e..5e94f4dc612a7b 100644 --- a/mlir/lib/Dialect/SCF/IR/SCF.cpp +++ b/mlir/lib/Dialect/SCF/IR/SCF.cpp @@ -378,20 +378,20 @@ LogicalResult ForOp::verifyRegions() { return success(); } -std::optional ForOp::getSingleInductionVar() { - return getInductionVar(); +std::optional> ForOp::getLoopInductionVars() { + return SmallVector{getInductionVar()}; } -std::optional ForOp::getSingleLowerBound() { - return OpFoldResult(getLowerBound()); +std::optional> ForOp::getLoopLowerBounds() { + return SmallVector{OpFoldResult(getLowerBound())}; } -std::optional ForOp::getSingleStep() { - return OpFoldResult(getStep()); +std::optional> ForOp::getLoopSteps() { + return SmallVector{OpFoldResult(getStep())}; } -std::optional ForOp::getSingleUpperBound() { - return OpFoldResult(getUpperBound()); +std::optional> ForOp::getLoopUpperBounds() { + return SmallVector{OpFoldResult(getUpperBound())}; } std::optional ForOp::getLoopResults() { return getResults(); } @@ -1428,28 +1428,26 @@ SmallVector ForallOp::getCombiningOps(BlockArgument bbArg) { return storeOps; } -std::optional ForallOp::getSingleInductionVar() { - if (getRank() != 1) - return std::nullopt; - return getInductionVar(0); +std::optional> ForallOp::getLoopInductionVars() { + return SmallVector{getBody()->getArguments().take_front(getRank())}; } -std::optional ForallOp::getSingleLowerBound() { - if (getRank() != 1) - return std::nullopt; - return getMixedLowerBound()[0]; +// Get lower bounds as OpFoldResult. +std::optional> ForallOp::getLoopLowerBounds() { + Builder b(getOperation()->getContext()); + return getMixedValues(getStaticLowerBound(), getDynamicLowerBound(), b); } -std::optional ForallOp::getSingleUpperBound() { - if (getRank() != 1) - return std::nullopt; - return getMixedUpperBound()[0]; +// Get upper bounds as OpFoldResult. +std::optional> ForallOp::getLoopUpperBounds() { + Builder b(getOperation()->getContext()); + return getMixedValues(getStaticUpperBound(), getDynamicUpperBound(), b); } -std::optional ForallOp::getSingleStep() { - if (getRank() != 1) - return std::nullopt; - return getMixedStep()[0]; +// Get steps as OpFoldResult. +std::optional> ForallOp::getLoopSteps() { + Builder b(getOperation()->getContext()); + return getMixedValues(getStaticStep(), getDynamicStep(), b); } ForallOp mlir::scf::getForallOpThreadIndexOwner(Value val) { @@ -3008,28 +3006,20 @@ void ParallelOp::print(OpAsmPrinter &p) { SmallVector ParallelOp::getLoopRegions() { return {&getRegion()}; } -std::optional ParallelOp::getSingleInductionVar() { - if (getNumLoops() != 1) - return std::nullopt; - return getBody()->getArgument(0); +std::optional> ParallelOp::getLoopInductionVars() { + return SmallVector{getBody()->getArguments()}; } -std::optional ParallelOp::getSingleLowerBound() { - if (getNumLoops() != 1) - return std::nullopt; - return getLowerBound()[0]; +std::optional> ParallelOp::getLoopLowerBounds() { + return getLowerBound(); } -std::optional ParallelOp::getSingleUpperBound() { - if (getNumLoops() != 1) - return std::nullopt; - return getUpperBound()[0]; +std::optional> ParallelOp::getLoopUpperBounds() { + return getUpperBound(); } -std::optional ParallelOp::getSingleStep() { - if (getNumLoops() != 1) - return std::nullopt; - return getStep()[0]; +std::optional> ParallelOp::getLoopSteps() { + return getStep(); } ParallelOp mlir::scf::getParallelForInductionVarOwner(Value val) { diff --git a/mlir/lib/Dialect/SCF/Transforms/ForallToFor.cpp b/mlir/lib/Dialect/SCF/Transforms/ForallToFor.cpp index 198cb2e6cc69ef..5da1b76e929be4 100644 --- a/mlir/lib/Dialect/SCF/Transforms/ForallToFor.cpp +++ b/mlir/lib/Dialect/SCF/Transforms/ForallToFor.cpp @@ -34,12 +34,9 @@ mlir::scf::forallToForLoop(RewriterBase &rewriter, scf::ForallOp forallOp, rewriter.setInsertionPoint(forallOp); Location loc = forallOp.getLoc(); - SmallVector lbs = getValueOrCreateConstantIndexOp( - rewriter, loc, forallOp.getMixedLowerBound()); - SmallVector ubs = getValueOrCreateConstantIndexOp( - rewriter, loc, forallOp.getMixedUpperBound()); - SmallVector steps = - getValueOrCreateConstantIndexOp(rewriter, loc, forallOp.getMixedStep()); + SmallVector lbs = forallOp.getLowerBound(rewriter); + SmallVector ubs = forallOp.getUpperBound(rewriter); + SmallVector steps = forallOp.getStep(rewriter); LoopNest loopNest = scf::buildLoopNest(rewriter, loc, lbs, ubs, steps); SmallVector ivs = llvm::map_to_vector( diff --git a/mlir/lib/Dialect/SCF/Transforms/ForallToParallel.cpp b/mlir/lib/Dialect/SCF/Transforms/ForallToParallel.cpp index 1fc0331300379b..44e6840b03a3df 100644 --- a/mlir/lib/Dialect/SCF/Transforms/ForallToParallel.cpp +++ b/mlir/lib/Dialect/SCF/Transforms/ForallToParallel.cpp @@ -35,12 +35,9 @@ LogicalResult mlir::scf::forallToParallelLoop(RewriterBase &rewriter, "only fully bufferized scf.forall ops can be lowered to scf.parallel"); // Convert mixed bounds and steps to SSA values. - SmallVector lbs = getValueOrCreateConstantIndexOp( - rewriter, loc, forallOp.getMixedLowerBound()); - SmallVector ubs = getValueOrCreateConstantIndexOp( - rewriter, loc, forallOp.getMixedUpperBound()); - SmallVector steps = - getValueOrCreateConstantIndexOp(rewriter, loc, forallOp.getMixedStep()); + SmallVector lbs = forallOp.getLowerBound(rewriter); + SmallVector ubs = forallOp.getUpperBound(rewriter); + SmallVector steps = forallOp.getStep(rewriter); // Create empty scf.parallel op. auto parallelOp = rewriter.create(loc, lbs, ubs, steps); diff --git a/mlir/unittests/Dialect/SCF/LoopLikeSCFOpsTest.cpp b/mlir/unittests/Dialect/SCF/LoopLikeSCFOpsTest.cpp index 6bc0fd6113b9bb..53a4af14d119a4 100644 --- a/mlir/unittests/Dialect/SCF/LoopLikeSCFOpsTest.cpp +++ b/mlir/unittests/Dialect/SCF/LoopLikeSCFOpsTest.cpp @@ -27,27 +27,65 @@ class SCFLoopLikeTest : public ::testing::Test { } void checkUnidimensional(LoopLikeOpInterface loopLikeOp) { - std::optional maybeLb = loopLikeOp.getSingleLowerBound(); - EXPECT_TRUE(maybeLb.has_value()); - std::optional maybeUb = loopLikeOp.getSingleUpperBound(); - EXPECT_TRUE(maybeUb.has_value()); - std::optional maybeStep = loopLikeOp.getSingleStep(); - EXPECT_TRUE(maybeStep.has_value()); - std::optional maybeIndVar = + std::optional maybeSingleLb = + loopLikeOp.getSingleLowerBound(); + EXPECT_TRUE(maybeSingleLb.has_value()); + std::optional maybeSingleUb = + loopLikeOp.getSingleUpperBound(); + EXPECT_TRUE(maybeSingleUb.has_value()); + std::optional maybeSingleStep = loopLikeOp.getSingleStep(); + EXPECT_TRUE(maybeSingleStep.has_value()); + std::optional maybeSingleIndVar = loopLikeOp.getSingleInductionVar(); - EXPECT_TRUE(maybeIndVar.has_value()); + EXPECT_TRUE(maybeSingleIndVar.has_value()); + + std::optional> maybeLb = + loopLikeOp.getLoopLowerBounds(); + ASSERT_TRUE(maybeLb.has_value()); + EXPECT_EQ((*maybeLb).size(), 1u); + std::optional> maybeUb = + loopLikeOp.getLoopUpperBounds(); + ASSERT_TRUE(maybeUb.has_value()); + EXPECT_EQ((*maybeUb).size(), 1u); + std::optional> maybeStep = + loopLikeOp.getLoopSteps(); + ASSERT_TRUE(maybeStep.has_value()); + EXPECT_EQ((*maybeStep).size(), 1u); + std::optional> maybeInductionVars = + loopLikeOp.getLoopInductionVars(); + ASSERT_TRUE(maybeInductionVars.has_value()); + EXPECT_EQ((*maybeInductionVars).size(), 1u); } void checkMultidimensional(LoopLikeOpInterface loopLikeOp) { - std::optional maybeLb = loopLikeOp.getSingleLowerBound(); - EXPECT_FALSE(maybeLb.has_value()); - std::optional maybeUb = loopLikeOp.getSingleUpperBound(); - EXPECT_FALSE(maybeUb.has_value()); - std::optional maybeStep = loopLikeOp.getSingleStep(); - EXPECT_FALSE(maybeStep.has_value()); - std::optional maybeIndVar = + std::optional maybeSingleLb = + loopLikeOp.getSingleLowerBound(); + EXPECT_FALSE(maybeSingleLb.has_value()); + std::optional maybeSingleUb = + loopLikeOp.getSingleUpperBound(); + EXPECT_FALSE(maybeSingleUb.has_value()); + std::optional maybeSingleStep = loopLikeOp.getSingleStep(); + EXPECT_FALSE(maybeSingleStep.has_value()); + std::optional maybeSingleIndVar = loopLikeOp.getSingleInductionVar(); - EXPECT_FALSE(maybeIndVar.has_value()); + EXPECT_FALSE(maybeSingleIndVar.has_value()); + + std::optional> maybeLb = + loopLikeOp.getLoopLowerBounds(); + ASSERT_TRUE(maybeLb.has_value()); + EXPECT_EQ((*maybeLb).size(), 2u); + std::optional> maybeUb = + loopLikeOp.getLoopUpperBounds(); + ASSERT_TRUE(maybeUb.has_value()); + EXPECT_EQ((*maybeUb).size(), 2u); + std::optional> maybeStep = + loopLikeOp.getLoopSteps(); + ASSERT_TRUE(maybeStep.has_value()); + EXPECT_EQ((*maybeStep).size(), 2u); + std::optional> maybeInductionVars = + loopLikeOp.getLoopInductionVars(); + ASSERT_TRUE(maybeInductionVars.has_value()); + EXPECT_EQ((*maybeInductionVars).size(), 2u); } MLIRContext context;