xref: /llvm-project/mlir/test/lib/Analysis/DataFlow/TestDenseBackwardDataFlowAnalysis.cpp (revision 4b3f251bada55cfc20a2c72321fa0bbfd7a759d5)
18a918c54SAlex Zinenko //===- TestDenseBackwardDataFlowAnalysis.cpp - Test pass ------------------===//
28a918c54SAlex Zinenko //
38a918c54SAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48a918c54SAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
58a918c54SAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68a918c54SAlex Zinenko //
78a918c54SAlex Zinenko //===----------------------------------------------------------------------===//
88a918c54SAlex Zinenko //
98a918c54SAlex Zinenko // Test pass for backward dense dataflow analysis.
108a918c54SAlex Zinenko //
118a918c54SAlex Zinenko //===----------------------------------------------------------------------===//
128a918c54SAlex Zinenko 
138a918c54SAlex Zinenko #include "TestDenseDataFlowAnalysis.h"
145d8813deSAlex Zinenko #include "TestDialect.h"
15e95e94adSJeff Niu #include "TestOps.h"
168a918c54SAlex Zinenko #include "mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h"
178a918c54SAlex Zinenko #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
188a918c54SAlex Zinenko #include "mlir/Analysis/DataFlow/DenseAnalysis.h"
198a918c54SAlex Zinenko #include "mlir/Analysis/DataFlowFramework.h"
205d8813deSAlex Zinenko #include "mlir/IR/Builders.h"
218a918c54SAlex Zinenko #include "mlir/IR/SymbolTable.h"
225d8813deSAlex Zinenko #include "mlir/Interfaces/CallInterfaces.h"
235d8813deSAlex Zinenko #include "mlir/Interfaces/ControlFlowInterfaces.h"
248a918c54SAlex Zinenko #include "mlir/Interfaces/SideEffectInterfaces.h"
258a918c54SAlex Zinenko #include "mlir/Pass/Pass.h"
268a918c54SAlex Zinenko #include "mlir/Support/TypeID.h"
278a918c54SAlex Zinenko #include "llvm/Support/raw_ostream.h"
288a918c54SAlex Zinenko 
298a918c54SAlex Zinenko using namespace mlir;
308a918c54SAlex Zinenko using namespace mlir::dataflow;
318a918c54SAlex Zinenko using namespace mlir::dataflow::test;
328a918c54SAlex Zinenko 
338a918c54SAlex Zinenko namespace {
348a918c54SAlex Zinenko 
355d8813deSAlex Zinenko class NextAccess : public AbstractDenseLattice, public AccessLatticeBase {
368a918c54SAlex Zinenko public:
378a918c54SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(NextAccess)
388a918c54SAlex Zinenko 
398a918c54SAlex Zinenko   using dataflow::AbstractDenseLattice::AbstractDenseLattice;
408a918c54SAlex Zinenko 
418a918c54SAlex Zinenko   ChangeResult meet(const AbstractDenseLattice &lattice) override {
425d8813deSAlex Zinenko     return AccessLatticeBase::merge(static_cast<AccessLatticeBase>(
438a918c54SAlex Zinenko         static_cast<const NextAccess &>(lattice)));
448a918c54SAlex Zinenko   }
458a918c54SAlex Zinenko 
468a918c54SAlex Zinenko   void print(raw_ostream &os) const override {
478a918c54SAlex Zinenko     return AccessLatticeBase::print(os);
488a918c54SAlex Zinenko   }
498a918c54SAlex Zinenko };
508a918c54SAlex Zinenko 
518a918c54SAlex Zinenko class NextAccessAnalysis : public DenseBackwardDataFlowAnalysis<NextAccess> {
528a918c54SAlex Zinenko public:
5332a4e3fcSOleksandr "Alex" Zinenko   NextAccessAnalysis(DataFlowSolver &solver, SymbolTableCollection &symbolTable,
5432a4e3fcSOleksandr "Alex" Zinenko                      bool assumeFuncReads = false)
5532a4e3fcSOleksandr "Alex" Zinenko       : DenseBackwardDataFlowAnalysis(solver, symbolTable),
5632a4e3fcSOleksandr "Alex" Zinenko         assumeFuncReads(assumeFuncReads) {}
578a918c54SAlex Zinenko 
5815e915a4SIvan Butygin   LogicalResult visitOperation(Operation *op, const NextAccess &after,
598a918c54SAlex Zinenko                                NextAccess *before) override;
608a918c54SAlex Zinenko 
615d8813deSAlex Zinenko   void visitCallControlFlowTransfer(CallOpInterface call,
625d8813deSAlex Zinenko                                     CallControlFlowAction action,
635d8813deSAlex Zinenko                                     const NextAccess &after,
645d8813deSAlex Zinenko                                     NextAccess *before) override;
655d8813deSAlex Zinenko 
665d8813deSAlex Zinenko   void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch,
674dd744acSMarkus Böck                                             RegionBranchPoint regionFrom,
684dd744acSMarkus Böck                                             RegionBranchPoint regionTo,
695d8813deSAlex Zinenko                                             const NextAccess &after,
705d8813deSAlex Zinenko                                             NextAccess *before) override;
715d8813deSAlex Zinenko 
728a918c54SAlex Zinenko   // TODO: this isn't ideal for the analysis. When there is no next access, it
738a918c54SAlex Zinenko   // means "we don't know what the next access is" rather than "there is no next
748a918c54SAlex Zinenko   // access". But it's unclear how to differentiate the two cases...
758a918c54SAlex Zinenko   void setToExitState(NextAccess *lattice) override {
7632a4e3fcSOleksandr "Alex" Zinenko     propagateIfChanged(lattice, lattice->setKnownToUnknown());
778a918c54SAlex Zinenko   }
7832a4e3fcSOleksandr "Alex" Zinenko 
7932a4e3fcSOleksandr "Alex" Zinenko   const bool assumeFuncReads;
808a918c54SAlex Zinenko };
818a918c54SAlex Zinenko } // namespace
828a918c54SAlex Zinenko 
8315e915a4SIvan Butygin LogicalResult NextAccessAnalysis::visitOperation(Operation *op,
8415e915a4SIvan Butygin                                                  const NextAccess &after,
858a918c54SAlex Zinenko                                                  NextAccess *before) {
868a918c54SAlex Zinenko   auto memory = dyn_cast<MemoryEffectOpInterface>(op);
878a918c54SAlex Zinenko   // If we can't reason about the memory effects, conservatively assume we can't
888a918c54SAlex Zinenko   // say anything about the next access.
8915e915a4SIvan Butygin   if (!memory) {
9015e915a4SIvan Butygin     setToExitState(before);
9115e915a4SIvan Butygin     return success();
9215e915a4SIvan Butygin   }
938a918c54SAlex Zinenko 
948a918c54SAlex Zinenko   SmallVector<MemoryEffects::EffectInstance> effects;
958a918c54SAlex Zinenko   memory.getEffects(effects);
9632a4e3fcSOleksandr "Alex" Zinenko 
9732a4e3fcSOleksandr "Alex" Zinenko   // First, check if all underlying values are already known. Otherwise, avoid
9832a4e3fcSOleksandr "Alex" Zinenko   // propagating and stay in the "undefined" state to avoid incorrectly
9932a4e3fcSOleksandr "Alex" Zinenko   // propagating values that may be overwritten later on as that could be
10032a4e3fcSOleksandr "Alex" Zinenko   // problematic for convergence based on monotonicity of lattice updates.
10132a4e3fcSOleksandr "Alex" Zinenko   SmallVector<Value> underlyingValues;
10232a4e3fcSOleksandr "Alex" Zinenko   underlyingValues.reserve(effects.size());
1038a918c54SAlex Zinenko   for (const MemoryEffects::EffectInstance &effect : effects) {
1048a918c54SAlex Zinenko     Value value = effect.getValue();
1058a918c54SAlex Zinenko 
1068a918c54SAlex Zinenko     // Effects with unspecified value are treated conservatively and we cannot
1078a918c54SAlex Zinenko     // assume anything about the next access.
10815e915a4SIvan Butygin     if (!value) {
10915e915a4SIvan Butygin       setToExitState(before);
11015e915a4SIvan Butygin       return success();
11115e915a4SIvan Butygin     }
1128a918c54SAlex Zinenko 
1135d8813deSAlex Zinenko     // If cannot find the most underlying value, we cannot assume anything about
1145d8813deSAlex Zinenko     // the next accesses.
11532a4e3fcSOleksandr "Alex" Zinenko     std::optional<Value> underlyingValue =
11632a4e3fcSOleksandr "Alex" Zinenko         UnderlyingValueAnalysis::getMostUnderlyingValue(
1178a918c54SAlex Zinenko             value, [&](Value value) {
118*4b3f251bSdonald chen               return getOrCreateFor<UnderlyingValueLattice>(
119*4b3f251bSdonald chen                   getProgramPointBefore(op), value);
1208a918c54SAlex Zinenko             });
12132a4e3fcSOleksandr "Alex" Zinenko 
12232a4e3fcSOleksandr "Alex" Zinenko     // If the underlying value is not known yet, don't propagate.
12332a4e3fcSOleksandr "Alex" Zinenko     if (!underlyingValue)
12415e915a4SIvan Butygin       return success();
12532a4e3fcSOleksandr "Alex" Zinenko 
12632a4e3fcSOleksandr "Alex" Zinenko     underlyingValues.push_back(*underlyingValue);
12732a4e3fcSOleksandr "Alex" Zinenko   }
12832a4e3fcSOleksandr "Alex" Zinenko 
12932a4e3fcSOleksandr "Alex" Zinenko   // Update the state if all underlying values are known.
13032a4e3fcSOleksandr "Alex" Zinenko   ChangeResult result = before->meet(after);
13132a4e3fcSOleksandr "Alex" Zinenko   for (const auto &[effect, value] : llvm::zip(effects, underlyingValues)) {
13232a4e3fcSOleksandr "Alex" Zinenko     // If the underlying value is known to be unknown, set to fixpoint.
13315e915a4SIvan Butygin     if (!value) {
13415e915a4SIvan Butygin       setToExitState(before);
13515e915a4SIvan Butygin       return success();
13615e915a4SIvan Butygin     }
1378a918c54SAlex Zinenko 
1388a918c54SAlex Zinenko     result |= before->set(value, op);
1398a918c54SAlex Zinenko   }
1408a918c54SAlex Zinenko   propagateIfChanged(before, result);
14115e915a4SIvan Butygin   return success();
1428a918c54SAlex Zinenko }
1438a918c54SAlex Zinenko 
1445d8813deSAlex Zinenko void NextAccessAnalysis::visitCallControlFlowTransfer(
1455d8813deSAlex Zinenko     CallOpInterface call, CallControlFlowAction action, const NextAccess &after,
1465d8813deSAlex Zinenko     NextAccess *before) {
14732a4e3fcSOleksandr "Alex" Zinenko   if (action == CallControlFlowAction::ExternalCallee && assumeFuncReads) {
14832a4e3fcSOleksandr "Alex" Zinenko     SmallVector<Value> underlyingValues;
14932a4e3fcSOleksandr "Alex" Zinenko     underlyingValues.reserve(call->getNumOperands());
15032a4e3fcSOleksandr "Alex" Zinenko     for (Value operand : call.getArgOperands()) {
15132a4e3fcSOleksandr "Alex" Zinenko       std::optional<Value> underlyingValue =
15232a4e3fcSOleksandr "Alex" Zinenko           UnderlyingValueAnalysis::getMostUnderlyingValue(
15332a4e3fcSOleksandr "Alex" Zinenko               operand, [&](Value value) {
15432a4e3fcSOleksandr "Alex" Zinenko                 return getOrCreateFor<UnderlyingValueLattice>(
155*4b3f251bSdonald chen                     getProgramPointBefore(call.getOperation()), value);
15632a4e3fcSOleksandr "Alex" Zinenko               });
15732a4e3fcSOleksandr "Alex" Zinenko       if (!underlyingValue)
15832a4e3fcSOleksandr "Alex" Zinenko         return;
15932a4e3fcSOleksandr "Alex" Zinenko       underlyingValues.push_back(*underlyingValue);
16032a4e3fcSOleksandr "Alex" Zinenko     }
16132a4e3fcSOleksandr "Alex" Zinenko 
16232a4e3fcSOleksandr "Alex" Zinenko     ChangeResult result = before->meet(after);
16332a4e3fcSOleksandr "Alex" Zinenko     for (Value operand : underlyingValues) {
16432a4e3fcSOleksandr "Alex" Zinenko       result |= before->set(operand, call);
16532a4e3fcSOleksandr "Alex" Zinenko     }
16632a4e3fcSOleksandr "Alex" Zinenko     return propagateIfChanged(before, result);
16732a4e3fcSOleksandr "Alex" Zinenko   }
1685d8813deSAlex Zinenko   auto testCallAndStore =
1695d8813deSAlex Zinenko       dyn_cast<::test::TestCallAndStoreOp>(call.getOperation());
1705d8813deSAlex Zinenko   if (testCallAndStore && ((action == CallControlFlowAction::EnterCallee &&
1715d8813deSAlex Zinenko                             testCallAndStore.getStoreBeforeCall()) ||
1725d8813deSAlex Zinenko                            (action == CallControlFlowAction::ExitCallee &&
1735d8813deSAlex Zinenko                             !testCallAndStore.getStoreBeforeCall()))) {
17415e915a4SIvan Butygin     (void)visitOperation(call, after, before);
1755d8813deSAlex Zinenko   } else {
1765d8813deSAlex Zinenko     AbstractDenseBackwardDataFlowAnalysis::visitCallControlFlowTransfer(
1775d8813deSAlex Zinenko         call, action, after, before);
1785d8813deSAlex Zinenko   }
1795d8813deSAlex Zinenko }
1805d8813deSAlex Zinenko 
1815d8813deSAlex Zinenko void NextAccessAnalysis::visitRegionBranchControlFlowTransfer(
1824dd744acSMarkus Böck     RegionBranchOpInterface branch, RegionBranchPoint regionFrom,
1834dd744acSMarkus Böck     RegionBranchPoint regionTo, const NextAccess &after, NextAccess *before) {
1845d8813deSAlex Zinenko   auto testStoreWithARegion =
1855d8813deSAlex Zinenko       dyn_cast<::test::TestStoreWithARegion>(branch.getOperation());
1865d8813deSAlex Zinenko 
1875d8813deSAlex Zinenko   if (testStoreWithARegion &&
1884dd744acSMarkus Böck       ((regionTo.isParent() && !testStoreWithARegion.getStoreBeforeRegion()) ||
1894dd744acSMarkus Böck        (regionFrom.isParent() &&
1904dd744acSMarkus Böck         testStoreWithARegion.getStoreBeforeRegion()))) {
19115e915a4SIvan Butygin     (void)visitOperation(branch, static_cast<const NextAccess &>(after),
1925d8813deSAlex Zinenko                          static_cast<NextAccess *>(before));
1935d8813deSAlex Zinenko   } else {
1945d8813deSAlex Zinenko     propagateIfChanged(before, before->meet(after));
1955d8813deSAlex Zinenko   }
1965d8813deSAlex Zinenko }
1975d8813deSAlex Zinenko 
1988a918c54SAlex Zinenko namespace {
1998a918c54SAlex Zinenko struct TestNextAccessPass
2008a918c54SAlex Zinenko     : public PassWrapper<TestNextAccessPass, OperationPass<>> {
20132a4e3fcSOleksandr "Alex" Zinenko   TestNextAccessPass() = default;
20232a4e3fcSOleksandr "Alex" Zinenko   TestNextAccessPass(const TestNextAccessPass &other) : PassWrapper(other) {
20332a4e3fcSOleksandr "Alex" Zinenko     interprocedural = other.interprocedural;
20432a4e3fcSOleksandr "Alex" Zinenko     assumeFuncReads = other.assumeFuncReads;
20532a4e3fcSOleksandr "Alex" Zinenko   }
20632a4e3fcSOleksandr "Alex" Zinenko 
2078a918c54SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestNextAccessPass)
2088a918c54SAlex Zinenko 
2098a918c54SAlex Zinenko   StringRef getArgument() const override { return "test-next-access"; }
2108a918c54SAlex Zinenko 
21132a4e3fcSOleksandr "Alex" Zinenko   Option<bool> interprocedural{
21232a4e3fcSOleksandr "Alex" Zinenko       *this, "interprocedural", llvm::cl::init(true),
21332a4e3fcSOleksandr "Alex" Zinenko       llvm::cl::desc("perform interprocedural analysis")};
21432a4e3fcSOleksandr "Alex" Zinenko   Option<bool> assumeFuncReads{
21532a4e3fcSOleksandr "Alex" Zinenko       *this, "assume-func-reads", llvm::cl::init(false),
21632a4e3fcSOleksandr "Alex" Zinenko       llvm::cl::desc(
21732a4e3fcSOleksandr "Alex" Zinenko           "assume external functions have read effect on all arguments")};
21832a4e3fcSOleksandr "Alex" Zinenko 
2198a918c54SAlex Zinenko   static constexpr llvm::StringLiteral kTagAttrName = "name";
2208a918c54SAlex Zinenko   static constexpr llvm::StringLiteral kNextAccessAttrName = "next_access";
2215d8813deSAlex Zinenko   static constexpr llvm::StringLiteral kAtEntryPointAttrName =
2225d8813deSAlex Zinenko       "next_at_entry_point";
2238a918c54SAlex Zinenko 
2245d8813deSAlex Zinenko   static Attribute makeNextAccessAttribute(Operation *op,
2255d8813deSAlex Zinenko                                            const DataFlowSolver &solver,
2265d8813deSAlex Zinenko                                            const NextAccess *nextAccess) {
2275d8813deSAlex Zinenko     if (!nextAccess)
2285d8813deSAlex Zinenko       return StringAttr::get(op->getContext(), "not computed");
2298a918c54SAlex Zinenko 
23032a4e3fcSOleksandr "Alex" Zinenko     // Note that if the underlying value could not be computed or is unknown, we
23132a4e3fcSOleksandr "Alex" Zinenko     // conservatively treat the result also unknown.
2328a918c54SAlex Zinenko     SmallVector<Attribute> attrs;
2338a918c54SAlex Zinenko     for (Value operand : op->getOperands()) {
23432a4e3fcSOleksandr "Alex" Zinenko       std::optional<Value> underlyingValue =
23532a4e3fcSOleksandr "Alex" Zinenko           UnderlyingValueAnalysis::getMostUnderlyingValue(
2368a918c54SAlex Zinenko               operand, [&](Value value) {
2378a918c54SAlex Zinenko                 return solver.lookupState<UnderlyingValueLattice>(value);
2388a918c54SAlex Zinenko               });
23932a4e3fcSOleksandr "Alex" Zinenko       if (!underlyingValue) {
24032a4e3fcSOleksandr "Alex" Zinenko         attrs.push_back(StringAttr::get(op->getContext(), "unknown"));
24132a4e3fcSOleksandr "Alex" Zinenko         continue;
24232a4e3fcSOleksandr "Alex" Zinenko       }
24332a4e3fcSOleksandr "Alex" Zinenko       Value value = *underlyingValue;
24432a4e3fcSOleksandr "Alex" Zinenko       const AdjacentAccess *nextAcc = nextAccess->getAdjacentAccess(value);
24532a4e3fcSOleksandr "Alex" Zinenko       if (!nextAcc || !nextAcc->isKnown()) {
2468a918c54SAlex Zinenko         attrs.push_back(StringAttr::get(op->getContext(), "unknown"));
2478a918c54SAlex Zinenko         continue;
2488a918c54SAlex Zinenko       }
2498a918c54SAlex Zinenko 
2508a918c54SAlex Zinenko       SmallVector<Attribute> innerAttrs;
25132a4e3fcSOleksandr "Alex" Zinenko       innerAttrs.reserve(nextAcc->get().size());
25232a4e3fcSOleksandr "Alex" Zinenko       for (Operation *nextAccOp : nextAcc->get()) {
2538a918c54SAlex Zinenko         if (auto nextAccTag =
2548a918c54SAlex Zinenko                 nextAccOp->getAttrOfType<StringAttr>(kTagAttrName)) {
2558a918c54SAlex Zinenko           innerAttrs.push_back(nextAccTag);
2568a918c54SAlex Zinenko           continue;
2578a918c54SAlex Zinenko         }
2588a918c54SAlex Zinenko         std::string repr;
2598a918c54SAlex Zinenko         llvm::raw_string_ostream os(repr);
2608a918c54SAlex Zinenko         nextAccOp->print(os);
2618a918c54SAlex Zinenko         innerAttrs.push_back(StringAttr::get(op->getContext(), os.str()));
2628a918c54SAlex Zinenko       }
2638a918c54SAlex Zinenko       attrs.push_back(ArrayAttr::get(op->getContext(), innerAttrs));
2648a918c54SAlex Zinenko     }
2655d8813deSAlex Zinenko     return ArrayAttr::get(op->getContext(), attrs);
2665d8813deSAlex Zinenko   }
2678a918c54SAlex Zinenko 
2685d8813deSAlex Zinenko   void runOnOperation() override {
2695d8813deSAlex Zinenko     Operation *op = getOperation();
2705d8813deSAlex Zinenko     SymbolTableCollection symbolTable;
2715d8813deSAlex Zinenko 
27232a4e3fcSOleksandr "Alex" Zinenko     auto config = DataFlowConfig().setInterprocedural(interprocedural);
27332a4e3fcSOleksandr "Alex" Zinenko     DataFlowSolver solver(config);
2745d8813deSAlex Zinenko     solver.load<DeadCodeAnalysis>();
27532a4e3fcSOleksandr "Alex" Zinenko     solver.load<NextAccessAnalysis>(symbolTable, assumeFuncReads);
2765d8813deSAlex Zinenko     solver.load<SparseConstantPropagation>();
2775d8813deSAlex Zinenko     solver.load<UnderlyingValueAnalysis>();
2785d8813deSAlex Zinenko     if (failed(solver.initializeAndRun(op))) {
2795d8813deSAlex Zinenko       emitError(op->getLoc(), "dataflow solver failed");
2805d8813deSAlex Zinenko       return signalPassFailure();
2815d8813deSAlex Zinenko     }
2825d8813deSAlex Zinenko     op->walk([&](Operation *op) {
2835d8813deSAlex Zinenko       auto tag = op->getAttrOfType<StringAttr>(kTagAttrName);
2845d8813deSAlex Zinenko       if (!tag)
2855d8813deSAlex Zinenko         return;
2865d8813deSAlex Zinenko 
287*4b3f251bSdonald chen       const NextAccess *nextAccess =
288*4b3f251bSdonald chen           solver.lookupState<NextAccess>(solver.getProgramPointAfter(op));
2895d8813deSAlex Zinenko       op->setAttr(kNextAccessAttrName,
2905d8813deSAlex Zinenko                   makeNextAccessAttribute(op, solver, nextAccess));
2915d8813deSAlex Zinenko 
2925d8813deSAlex Zinenko       auto iface = dyn_cast<RegionBranchOpInterface>(op);
2935d8813deSAlex Zinenko       if (!iface)
2945d8813deSAlex Zinenko         return;
2955d8813deSAlex Zinenko 
2965d8813deSAlex Zinenko       SmallVector<Attribute> entryPointNextAccess;
2975d8813deSAlex Zinenko       SmallVector<RegionSuccessor> regionSuccessors;
2984dd744acSMarkus Böck       iface.getSuccessorRegions(RegionBranchPoint::parent(), regionSuccessors);
2995d8813deSAlex Zinenko       for (const RegionSuccessor &successor : regionSuccessors) {
3005d8813deSAlex Zinenko         if (!successor.getSuccessor() || successor.getSuccessor()->empty())
3015d8813deSAlex Zinenko           continue;
3025d8813deSAlex Zinenko         Block &successorBlock = successor.getSuccessor()->front();
303*4b3f251bSdonald chen         ProgramPoint *successorPoint =
304*4b3f251bSdonald chen             solver.getProgramPointBefore(&successorBlock);
3055d8813deSAlex Zinenko         entryPointNextAccess.push_back(makeNextAccessAttribute(
3065d8813deSAlex Zinenko             op, solver, solver.lookupState<NextAccess>(successorPoint)));
3075d8813deSAlex Zinenko       }
3085d8813deSAlex Zinenko       op->setAttr(kAtEntryPointAttrName,
3095d8813deSAlex Zinenko                   ArrayAttr::get(op->getContext(), entryPointNextAccess));
3108a918c54SAlex Zinenko     });
3118a918c54SAlex Zinenko   }
3128a918c54SAlex Zinenko };
3138a918c54SAlex Zinenko } // namespace
3148a918c54SAlex Zinenko 
3158a918c54SAlex Zinenko namespace mlir::test {
3168a918c54SAlex Zinenko void registerTestNextAccessPass() { PassRegistration<TestNextAccessPass>(); }
3178a918c54SAlex Zinenko } // namespace mlir::test
318