xref: /llvm-project/mlir/test/lib/Analysis/DataFlow/TestDenseForwardDataFlowAnalysis.cpp (revision 4b3f251bada55cfc20a2c72321fa0bbfd7a759d5)
1b2b7efb9SAlex Zinenko //===- TestDenseForwardDataFlowAnalysis.cpp -------------------------------===//
2b2b7efb9SAlex Zinenko //
3b2b7efb9SAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b2b7efb9SAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
5b2b7efb9SAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b2b7efb9SAlex Zinenko //
7b2b7efb9SAlex Zinenko //===----------------------------------------------------------------------===//
8b2b7efb9SAlex Zinenko //
9b2b7efb9SAlex Zinenko // Implementation of tests passes exercising dense forward data flow analysis.
10b2b7efb9SAlex Zinenko //
11b2b7efb9SAlex Zinenko //===----------------------------------------------------------------------===//
12b2b7efb9SAlex Zinenko 
13b2b7efb9SAlex Zinenko #include "TestDenseDataFlowAnalysis.h"
14b2b7efb9SAlex Zinenko #include "TestDialect.h"
15e95e94adSJeff Niu #include "TestOps.h"
16b2b7efb9SAlex Zinenko #include "mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h"
17b2b7efb9SAlex Zinenko #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
18b2b7efb9SAlex Zinenko #include "mlir/Analysis/DataFlow/DenseAnalysis.h"
19b2b7efb9SAlex Zinenko #include "mlir/Interfaces/SideEffectInterfaces.h"
20b2b7efb9SAlex Zinenko #include "mlir/Pass/Pass.h"
21a9d0f5e2Svic #include "mlir/Support/LLVM.h"
22a9d0f5e2Svic #include "llvm/ADT/TypeSwitch.h"
23b2b7efb9SAlex Zinenko #include <optional>
24b2b7efb9SAlex Zinenko 
25b2b7efb9SAlex Zinenko using namespace mlir;
26b2b7efb9SAlex Zinenko using namespace mlir::dataflow;
27b2b7efb9SAlex Zinenko using namespace mlir::dataflow::test;
28b2b7efb9SAlex Zinenko 
29b2b7efb9SAlex Zinenko namespace {
30b2b7efb9SAlex Zinenko 
31b2b7efb9SAlex Zinenko /// This lattice represents, for a given memory resource, the potential last
32b2b7efb9SAlex Zinenko /// operations that modified the resource.
33b2b7efb9SAlex Zinenko class LastModification : public AbstractDenseLattice, public AccessLatticeBase {
34b2b7efb9SAlex Zinenko public:
35b2b7efb9SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LastModification)
36b2b7efb9SAlex Zinenko 
37b2b7efb9SAlex Zinenko   using AbstractDenseLattice::AbstractDenseLattice;
38b2b7efb9SAlex Zinenko 
39b2b7efb9SAlex Zinenko   /// Join the last modifications.
40b2b7efb9SAlex Zinenko   ChangeResult join(const AbstractDenseLattice &lattice) override {
41b2b7efb9SAlex Zinenko     return AccessLatticeBase::merge(static_cast<AccessLatticeBase>(
42b2b7efb9SAlex Zinenko         static_cast<const LastModification &>(lattice)));
43b2b7efb9SAlex Zinenko   }
44b2b7efb9SAlex Zinenko 
45b2b7efb9SAlex Zinenko   void print(raw_ostream &os) const override {
46b2b7efb9SAlex Zinenko     return AccessLatticeBase::print(os);
47b2b7efb9SAlex Zinenko   }
48b2b7efb9SAlex Zinenko };
49b2b7efb9SAlex Zinenko 
50b2b7efb9SAlex Zinenko class LastModifiedAnalysis
51b2b7efb9SAlex Zinenko     : public DenseForwardDataFlowAnalysis<LastModification> {
52b2b7efb9SAlex Zinenko public:
5332a4e3fcSOleksandr "Alex" Zinenko   explicit LastModifiedAnalysis(DataFlowSolver &solver, bool assumeFuncWrites)
5432a4e3fcSOleksandr "Alex" Zinenko       : DenseForwardDataFlowAnalysis(solver),
5532a4e3fcSOleksandr "Alex" Zinenko         assumeFuncWrites(assumeFuncWrites) {}
56b2b7efb9SAlex Zinenko 
57b2b7efb9SAlex Zinenko   /// Visit an operation. If the operation has no memory effects, then the state
58b2b7efb9SAlex Zinenko   /// is propagated with no change. If the operation allocates a resource, then
59b2b7efb9SAlex Zinenko   /// its reaching definitions is set to empty. If the operation writes to a
60b2b7efb9SAlex Zinenko   /// resource, then its reaching definition is set to the written value.
6115e915a4SIvan Butygin   LogicalResult visitOperation(Operation *op, const LastModification &before,
62b2b7efb9SAlex Zinenko                                LastModification *after) override;
63b2b7efb9SAlex Zinenko 
64b2b7efb9SAlex Zinenko   void visitCallControlFlowTransfer(CallOpInterface call,
65b2b7efb9SAlex Zinenko                                     CallControlFlowAction action,
66b2b7efb9SAlex Zinenko                                     const LastModification &before,
67b2b7efb9SAlex Zinenko                                     LastModification *after) override;
68b2b7efb9SAlex Zinenko 
69b2b7efb9SAlex Zinenko   void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch,
70b2b7efb9SAlex Zinenko                                             std::optional<unsigned> regionFrom,
71b2b7efb9SAlex Zinenko                                             std::optional<unsigned> regionTo,
72b2b7efb9SAlex Zinenko                                             const LastModification &before,
73b2b7efb9SAlex Zinenko                                             LastModification *after) override;
74b2b7efb9SAlex Zinenko 
75b2b7efb9SAlex Zinenko   /// At an entry point, the last modifications of all memory resources are
76b2b7efb9SAlex Zinenko   /// unknown.
77b2b7efb9SAlex Zinenko   void setToEntryState(LastModification *lattice) override {
78b2b7efb9SAlex Zinenko     propagateIfChanged(lattice, lattice->reset());
79b2b7efb9SAlex Zinenko   }
8032a4e3fcSOleksandr "Alex" Zinenko 
8132a4e3fcSOleksandr "Alex" Zinenko private:
8232a4e3fcSOleksandr "Alex" Zinenko   const bool assumeFuncWrites;
83b2b7efb9SAlex Zinenko };
84b2b7efb9SAlex Zinenko } // end anonymous namespace
85b2b7efb9SAlex Zinenko 
8615e915a4SIvan Butygin LogicalResult LastModifiedAnalysis::visitOperation(
8715e915a4SIvan Butygin     Operation *op, const LastModification &before, LastModification *after) {
88b2b7efb9SAlex Zinenko   auto memory = dyn_cast<MemoryEffectOpInterface>(op);
89b2b7efb9SAlex Zinenko   // If we can't reason about the memory effects, then conservatively assume we
90b2b7efb9SAlex Zinenko   // can't deduce anything about the last modifications.
9115e915a4SIvan Butygin   if (!memory) {
9215e915a4SIvan Butygin     setToEntryState(after);
9315e915a4SIvan Butygin     return success();
9415e915a4SIvan Butygin   }
95b2b7efb9SAlex Zinenko 
96b2b7efb9SAlex Zinenko   SmallVector<MemoryEffects::EffectInstance> effects;
97b2b7efb9SAlex Zinenko   memory.getEffects(effects);
98b2b7efb9SAlex Zinenko 
9932a4e3fcSOleksandr "Alex" Zinenko   // First, check if all underlying values are already known. Otherwise, avoid
10032a4e3fcSOleksandr "Alex" Zinenko   // propagating and stay in the "undefined" state to avoid incorrectly
10132a4e3fcSOleksandr "Alex" Zinenko   // propagating values that may be overwritten later on as that could be
10232a4e3fcSOleksandr "Alex" Zinenko   // problematic for convergence based on monotonicity of lattice updates.
10332a4e3fcSOleksandr "Alex" Zinenko   SmallVector<Value> underlyingValues;
10432a4e3fcSOleksandr "Alex" Zinenko   underlyingValues.reserve(effects.size());
105b2b7efb9SAlex Zinenko   for (const auto &effect : effects) {
106b2b7efb9SAlex Zinenko     Value value = effect.getValue();
107b2b7efb9SAlex Zinenko 
108b2b7efb9SAlex Zinenko     // If we see an effect on anything other than a value, assume we can't
109b2b7efb9SAlex Zinenko     // deduce anything about the last modifications.
11015e915a4SIvan Butygin     if (!value) {
11115e915a4SIvan Butygin       setToEntryState(after);
11215e915a4SIvan Butygin       return success();
11315e915a4SIvan Butygin     }
114b2b7efb9SAlex Zinenko 
115b2b7efb9SAlex Zinenko     // If we cannot find the underlying value, we shouldn't just propagate the
116b2b7efb9SAlex Zinenko     // effects through, return the pessimistic state.
11732a4e3fcSOleksandr "Alex" Zinenko     std::optional<Value> underlyingValue =
11832a4e3fcSOleksandr "Alex" Zinenko         UnderlyingValueAnalysis::getMostUnderlyingValue(
119b2b7efb9SAlex Zinenko             value, [&](Value value) {
120*4b3f251bSdonald chen               return getOrCreateFor<UnderlyingValueLattice>(
121*4b3f251bSdonald chen                   getProgramPointAfter(op), value);
122b2b7efb9SAlex Zinenko             });
12332a4e3fcSOleksandr "Alex" Zinenko 
12432a4e3fcSOleksandr "Alex" Zinenko     // If the underlying value is not yet known, don't propagate yet.
12532a4e3fcSOleksandr "Alex" Zinenko     if (!underlyingValue)
12615e915a4SIvan Butygin       return success();
12732a4e3fcSOleksandr "Alex" Zinenko 
12832a4e3fcSOleksandr "Alex" Zinenko     underlyingValues.push_back(*underlyingValue);
12932a4e3fcSOleksandr "Alex" Zinenko   }
13032a4e3fcSOleksandr "Alex" Zinenko 
13132a4e3fcSOleksandr "Alex" Zinenko   // Update the state when all underlying values are known.
13232a4e3fcSOleksandr "Alex" Zinenko   ChangeResult result = after->join(before);
13332a4e3fcSOleksandr "Alex" Zinenko   for (const auto &[effect, value] : llvm::zip(effects, underlyingValues)) {
13432a4e3fcSOleksandr "Alex" Zinenko     // If the underlying value is known to be unknown, set to fixpoint state.
13515e915a4SIvan Butygin     if (!value) {
13615e915a4SIvan Butygin       setToEntryState(after);
13715e915a4SIvan Butygin       return success();
13815e915a4SIvan Butygin     }
139b2b7efb9SAlex Zinenko 
140b2b7efb9SAlex Zinenko     // Nothing to do for reads.
141b2b7efb9SAlex Zinenko     if (isa<MemoryEffects::Read>(effect.getEffect()))
142b2b7efb9SAlex Zinenko       continue;
143b2b7efb9SAlex Zinenko 
144b2b7efb9SAlex Zinenko     result |= after->set(value, op);
145b2b7efb9SAlex Zinenko   }
146b2b7efb9SAlex Zinenko   propagateIfChanged(after, result);
14715e915a4SIvan Butygin   return success();
148b2b7efb9SAlex Zinenko }
149b2b7efb9SAlex Zinenko 
150b2b7efb9SAlex Zinenko void LastModifiedAnalysis::visitCallControlFlowTransfer(
151b2b7efb9SAlex Zinenko     CallOpInterface call, CallControlFlowAction action,
152b2b7efb9SAlex Zinenko     const LastModification &before, LastModification *after) {
15332a4e3fcSOleksandr "Alex" Zinenko   if (action == CallControlFlowAction::ExternalCallee && assumeFuncWrites) {
15432a4e3fcSOleksandr "Alex" Zinenko     SmallVector<Value> underlyingValues;
15532a4e3fcSOleksandr "Alex" Zinenko     underlyingValues.reserve(call->getNumOperands());
15632a4e3fcSOleksandr "Alex" Zinenko     for (Value operand : call.getArgOperands()) {
15732a4e3fcSOleksandr "Alex" Zinenko       std::optional<Value> underlyingValue =
15832a4e3fcSOleksandr "Alex" Zinenko           UnderlyingValueAnalysis::getMostUnderlyingValue(
15932a4e3fcSOleksandr "Alex" Zinenko               operand, [&](Value value) {
16032a4e3fcSOleksandr "Alex" Zinenko                 return getOrCreateFor<UnderlyingValueLattice>(
161*4b3f251bSdonald chen                     getProgramPointAfter(call.getOperation()), value);
16232a4e3fcSOleksandr "Alex" Zinenko               });
16332a4e3fcSOleksandr "Alex" Zinenko       if (!underlyingValue)
16432a4e3fcSOleksandr "Alex" Zinenko         return;
16532a4e3fcSOleksandr "Alex" Zinenko       underlyingValues.push_back(*underlyingValue);
16632a4e3fcSOleksandr "Alex" Zinenko     }
16732a4e3fcSOleksandr "Alex" Zinenko 
16832a4e3fcSOleksandr "Alex" Zinenko     ChangeResult result = after->join(before);
16932a4e3fcSOleksandr "Alex" Zinenko     for (Value operand : underlyingValues)
17032a4e3fcSOleksandr "Alex" Zinenko       result |= after->set(operand, call);
17132a4e3fcSOleksandr "Alex" Zinenko     return propagateIfChanged(after, result);
17232a4e3fcSOleksandr "Alex" Zinenko   }
173b2b7efb9SAlex Zinenko   auto testCallAndStore =
174b2b7efb9SAlex Zinenko       dyn_cast<::test::TestCallAndStoreOp>(call.getOperation());
175b2b7efb9SAlex Zinenko   if (testCallAndStore && ((action == CallControlFlowAction::EnterCallee &&
176b2b7efb9SAlex Zinenko                             testCallAndStore.getStoreBeforeCall()) ||
177b2b7efb9SAlex Zinenko                            (action == CallControlFlowAction::ExitCallee &&
178b2b7efb9SAlex Zinenko                             !testCallAndStore.getStoreBeforeCall()))) {
17915e915a4SIvan Butygin     (void)visitOperation(call, before, after);
18015e915a4SIvan Butygin     return;
181b2b7efb9SAlex Zinenko   }
182b2b7efb9SAlex Zinenko   AbstractDenseForwardDataFlowAnalysis::visitCallControlFlowTransfer(
183b2b7efb9SAlex Zinenko       call, action, before, after);
184b2b7efb9SAlex Zinenko }
185b2b7efb9SAlex Zinenko 
186b2b7efb9SAlex Zinenko void LastModifiedAnalysis::visitRegionBranchControlFlowTransfer(
187b2b7efb9SAlex Zinenko     RegionBranchOpInterface branch, std::optional<unsigned> regionFrom,
188b2b7efb9SAlex Zinenko     std::optional<unsigned> regionTo, const LastModification &before,
189b2b7efb9SAlex Zinenko     LastModification *after) {
190a9d0f5e2Svic   auto defaultHandling = [&]() {
191b2b7efb9SAlex Zinenko     AbstractDenseForwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer(
192b2b7efb9SAlex Zinenko         branch, regionFrom, regionTo, before, after);
193a9d0f5e2Svic   };
194a9d0f5e2Svic   TypeSwitch<Operation *>(branch.getOperation())
195a9d0f5e2Svic       .Case<::test::TestStoreWithARegion, ::test::TestStoreWithALoopRegion>(
196a9d0f5e2Svic           [=](auto storeWithRegion) {
197a9d0f5e2Svic             if ((!regionTo && !storeWithRegion.getStoreBeforeRegion()) ||
198a9d0f5e2Svic                 (!regionFrom && storeWithRegion.getStoreBeforeRegion()))
19915e915a4SIvan Butygin               (void)visitOperation(branch, before, after);
200a9d0f5e2Svic             defaultHandling();
201a9d0f5e2Svic           })
202a9d0f5e2Svic       .Default([=](auto) { defaultHandling(); });
203b2b7efb9SAlex Zinenko }
204b2b7efb9SAlex Zinenko 
205b2b7efb9SAlex Zinenko namespace {
206b2b7efb9SAlex Zinenko struct TestLastModifiedPass
207b2b7efb9SAlex Zinenko     : public PassWrapper<TestLastModifiedPass, OperationPass<>> {
208b2b7efb9SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestLastModifiedPass)
209b2b7efb9SAlex Zinenko 
21032a4e3fcSOleksandr "Alex" Zinenko   TestLastModifiedPass() = default;
21132a4e3fcSOleksandr "Alex" Zinenko   TestLastModifiedPass(const TestLastModifiedPass &other) : PassWrapper(other) {
21232a4e3fcSOleksandr "Alex" Zinenko     interprocedural = other.interprocedural;
21332a4e3fcSOleksandr "Alex" Zinenko     assumeFuncWrites = other.assumeFuncWrites;
21432a4e3fcSOleksandr "Alex" Zinenko   }
21532a4e3fcSOleksandr "Alex" Zinenko 
216b2b7efb9SAlex Zinenko   StringRef getArgument() const override { return "test-last-modified"; }
217b2b7efb9SAlex Zinenko 
21832a4e3fcSOleksandr "Alex" Zinenko   Option<bool> interprocedural{
21932a4e3fcSOleksandr "Alex" Zinenko       *this, "interprocedural", llvm::cl::init(true),
22032a4e3fcSOleksandr "Alex" Zinenko       llvm::cl::desc("perform interprocedural analysis")};
22132a4e3fcSOleksandr "Alex" Zinenko   Option<bool> assumeFuncWrites{
22232a4e3fcSOleksandr "Alex" Zinenko       *this, "assume-func-writes", llvm::cl::init(false),
22332a4e3fcSOleksandr "Alex" Zinenko       llvm::cl::desc(
22432a4e3fcSOleksandr "Alex" Zinenko           "assume external functions have write effect on all arguments")};
22532a4e3fcSOleksandr "Alex" Zinenko 
226b2b7efb9SAlex Zinenko   void runOnOperation() override {
227b2b7efb9SAlex Zinenko     Operation *op = getOperation();
228b2b7efb9SAlex Zinenko 
22932a4e3fcSOleksandr "Alex" Zinenko     DataFlowSolver solver(DataFlowConfig().setInterprocedural(interprocedural));
230b2b7efb9SAlex Zinenko     solver.load<DeadCodeAnalysis>();
231b2b7efb9SAlex Zinenko     solver.load<SparseConstantPropagation>();
23232a4e3fcSOleksandr "Alex" Zinenko     solver.load<LastModifiedAnalysis>(assumeFuncWrites);
233b2b7efb9SAlex Zinenko     solver.load<UnderlyingValueAnalysis>();
234b2b7efb9SAlex Zinenko     if (failed(solver.initializeAndRun(op)))
235b2b7efb9SAlex Zinenko       return signalPassFailure();
236b2b7efb9SAlex Zinenko 
237b2b7efb9SAlex Zinenko     raw_ostream &os = llvm::errs();
238b2b7efb9SAlex Zinenko 
23932a4e3fcSOleksandr "Alex" Zinenko     // Note that if the underlying value could not be computed or is unknown, we
24032a4e3fcSOleksandr "Alex" Zinenko     // conservatively treat the result also unknown.
241b2b7efb9SAlex Zinenko     op->walk([&](Operation *op) {
242b2b7efb9SAlex Zinenko       auto tag = op->getAttrOfType<StringAttr>("tag");
243b2b7efb9SAlex Zinenko       if (!tag)
244b2b7efb9SAlex Zinenko         return;
245b2b7efb9SAlex Zinenko       os << "test_tag: " << tag.getValue() << ":\n";
246b2b7efb9SAlex Zinenko       const LastModification *lastMods =
247*4b3f251bSdonald chen           solver.lookupState<LastModification>(solver.getProgramPointAfter(op));
248b2b7efb9SAlex Zinenko       assert(lastMods && "expected a dense lattice");
249b2b7efb9SAlex Zinenko       for (auto [index, operand] : llvm::enumerate(op->getOperands())) {
250b2b7efb9SAlex Zinenko         os << " operand #" << index << "\n";
25132a4e3fcSOleksandr "Alex" Zinenko         std::optional<Value> underlyingValue =
25232a4e3fcSOleksandr "Alex" Zinenko             UnderlyingValueAnalysis::getMostUnderlyingValue(
253b2b7efb9SAlex Zinenko                 operand, [&](Value value) {
254b2b7efb9SAlex Zinenko                   return solver.lookupState<UnderlyingValueLattice>(value);
255b2b7efb9SAlex Zinenko                 });
25632a4e3fcSOleksandr "Alex" Zinenko         if (!underlyingValue) {
25732a4e3fcSOleksandr "Alex" Zinenko           os << " - <unknown>\n";
25832a4e3fcSOleksandr "Alex" Zinenko           continue;
25932a4e3fcSOleksandr "Alex" Zinenko         }
26032a4e3fcSOleksandr "Alex" Zinenko         Value value = *underlyingValue;
261b2b7efb9SAlex Zinenko         assert(value && "expected an underlying value");
26232a4e3fcSOleksandr "Alex" Zinenko         if (const AdjacentAccess *lastMod =
263b2b7efb9SAlex Zinenko                 lastMods->getAdjacentAccess(value)) {
26432a4e3fcSOleksandr "Alex" Zinenko           if (!lastMod->isKnown()) {
26532a4e3fcSOleksandr "Alex" Zinenko             os << " - <unknown>\n";
26632a4e3fcSOleksandr "Alex" Zinenko           } else {
26732a4e3fcSOleksandr "Alex" Zinenko             for (Operation *lastModifier : lastMod->get()) {
268b2b7efb9SAlex Zinenko               if (auto tagName =
269b2b7efb9SAlex Zinenko                       lastModifier->getAttrOfType<StringAttr>("tag_name")) {
270b2b7efb9SAlex Zinenko                 os << "  - " << tagName.getValue() << "\n";
271b2b7efb9SAlex Zinenko               } else {
272b2b7efb9SAlex Zinenko                 os << "  - " << lastModifier->getName() << "\n";
273b2b7efb9SAlex Zinenko               }
274b2b7efb9SAlex Zinenko             }
27532a4e3fcSOleksandr "Alex" Zinenko           }
276b2b7efb9SAlex Zinenko         } else {
277b2b7efb9SAlex Zinenko           os << "  - <unknown>\n";
278b2b7efb9SAlex Zinenko         }
279b2b7efb9SAlex Zinenko       }
280b2b7efb9SAlex Zinenko     });
281b2b7efb9SAlex Zinenko   }
282b2b7efb9SAlex Zinenko };
283b2b7efb9SAlex Zinenko } // end anonymous namespace
284b2b7efb9SAlex Zinenko 
285b2b7efb9SAlex Zinenko namespace mlir {
286b2b7efb9SAlex Zinenko namespace test {
287b2b7efb9SAlex Zinenko void registerTestLastModifiedPass() {
288b2b7efb9SAlex Zinenko   PassRegistration<TestLastModifiedPass>();
289b2b7efb9SAlex Zinenko }
290b2b7efb9SAlex Zinenko } // end namespace test
291b2b7efb9SAlex Zinenko } // end namespace mlir
292