xref: /llvm-project/mlir/test/lib/Analysis/DataFlow/TestDenseDataFlowAnalysis.h (revision b6603e1bf11dee4761e49af6581c8b8f074b705d)
18a918c54SAlex Zinenko //===- TestDenseDataFlowAnalysis.h - Dataflow test utilities ----*- C++ -*-===//
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 #include "mlir/Analysis/DataFlow/SparseAnalysis.h"
108a918c54SAlex Zinenko #include "mlir/Analysis/DataFlowFramework.h"
118a918c54SAlex Zinenko #include "mlir/IR/Value.h"
128a918c54SAlex Zinenko #include "llvm/ADT/DenseMap.h"
138a918c54SAlex Zinenko #include "llvm/Support/raw_ostream.h"
148a918c54SAlex Zinenko #include <optional>
158a918c54SAlex Zinenko 
168a918c54SAlex Zinenko namespace mlir {
178a918c54SAlex Zinenko namespace dataflow {
188a918c54SAlex Zinenko namespace test {
198a918c54SAlex Zinenko 
208a918c54SAlex Zinenko /// This lattice represents a single underlying value for an SSA value.
218a918c54SAlex Zinenko class UnderlyingValue {
228a918c54SAlex Zinenko public:
238a918c54SAlex Zinenko   /// Create an underlying value state with a known underlying value.
248a918c54SAlex Zinenko   explicit UnderlyingValue(std::optional<Value> underlyingValue = std::nullopt)
258a918c54SAlex Zinenko       : underlyingValue(underlyingValue) {}
268a918c54SAlex Zinenko 
278a918c54SAlex Zinenko   /// Whether the state is uninitialized.
288a918c54SAlex Zinenko   bool isUninitialized() const { return !underlyingValue.has_value(); }
298a918c54SAlex Zinenko 
308a918c54SAlex Zinenko   /// Returns the underlying value.
318a918c54SAlex Zinenko   Value getUnderlyingValue() const {
328a918c54SAlex Zinenko     assert(!isUninitialized());
338a918c54SAlex Zinenko     return *underlyingValue;
348a918c54SAlex Zinenko   }
358a918c54SAlex Zinenko 
368a918c54SAlex Zinenko   /// Join two underlying values. If there are conflicting underlying values,
378a918c54SAlex Zinenko   /// go to the pessimistic value.
388a918c54SAlex Zinenko   static UnderlyingValue join(const UnderlyingValue &lhs,
398a918c54SAlex Zinenko                               const UnderlyingValue &rhs) {
408a918c54SAlex Zinenko     if (lhs.isUninitialized())
418a918c54SAlex Zinenko       return rhs;
428a918c54SAlex Zinenko     if (rhs.isUninitialized())
438a918c54SAlex Zinenko       return lhs;
448a918c54SAlex Zinenko     return lhs.underlyingValue == rhs.underlyingValue
458a918c54SAlex Zinenko                ? lhs
468a918c54SAlex Zinenko                : UnderlyingValue(Value{});
478a918c54SAlex Zinenko   }
488a918c54SAlex Zinenko 
498a918c54SAlex Zinenko   /// Compare underlying values.
508a918c54SAlex Zinenko   bool operator==(const UnderlyingValue &rhs) const {
518a918c54SAlex Zinenko     return underlyingValue == rhs.underlyingValue;
528a918c54SAlex Zinenko   }
538a918c54SAlex Zinenko 
548a918c54SAlex Zinenko   void print(raw_ostream &os) const { os << underlyingValue; }
558a918c54SAlex Zinenko 
568a918c54SAlex Zinenko private:
578a918c54SAlex Zinenko   std::optional<Value> underlyingValue;
588a918c54SAlex Zinenko };
598a918c54SAlex Zinenko 
6032a4e3fcSOleksandr "Alex" Zinenko class AdjacentAccess {
6132a4e3fcSOleksandr "Alex" Zinenko public:
6232a4e3fcSOleksandr "Alex" Zinenko   using DeterministicSetVector =
6332a4e3fcSOleksandr "Alex" Zinenko       SetVector<Operation *, SmallVector<Operation *, 2>,
6432a4e3fcSOleksandr "Alex" Zinenko                 SmallPtrSet<Operation *, 2>>;
6532a4e3fcSOleksandr "Alex" Zinenko 
6632a4e3fcSOleksandr "Alex" Zinenko   ArrayRef<Operation *> get() const { return accesses.getArrayRef(); }
6732a4e3fcSOleksandr "Alex" Zinenko   bool isKnown() const { return !unknown; }
6832a4e3fcSOleksandr "Alex" Zinenko 
6932a4e3fcSOleksandr "Alex" Zinenko   ChangeResult merge(const AdjacentAccess &other) {
7032a4e3fcSOleksandr "Alex" Zinenko     if (unknown)
7132a4e3fcSOleksandr "Alex" Zinenko       return ChangeResult::NoChange;
7232a4e3fcSOleksandr "Alex" Zinenko     if (other.unknown) {
7332a4e3fcSOleksandr "Alex" Zinenko       unknown = true;
7432a4e3fcSOleksandr "Alex" Zinenko       accesses.clear();
7532a4e3fcSOleksandr "Alex" Zinenko       return ChangeResult::Change;
7632a4e3fcSOleksandr "Alex" Zinenko     }
7732a4e3fcSOleksandr "Alex" Zinenko 
7832a4e3fcSOleksandr "Alex" Zinenko     size_t sizeBefore = accesses.size();
7932a4e3fcSOleksandr "Alex" Zinenko     accesses.insert(other.accesses.begin(), other.accesses.end());
8032a4e3fcSOleksandr "Alex" Zinenko     return accesses.size() == sizeBefore ? ChangeResult::NoChange
8132a4e3fcSOleksandr "Alex" Zinenko                                          : ChangeResult::Change;
8232a4e3fcSOleksandr "Alex" Zinenko   }
8332a4e3fcSOleksandr "Alex" Zinenko 
8432a4e3fcSOleksandr "Alex" Zinenko   ChangeResult set(Operation *op) {
8532a4e3fcSOleksandr "Alex" Zinenko     if (!unknown && accesses.size() == 1 && *accesses.begin() == op)
8632a4e3fcSOleksandr "Alex" Zinenko       return ChangeResult::NoChange;
8732a4e3fcSOleksandr "Alex" Zinenko 
8832a4e3fcSOleksandr "Alex" Zinenko     unknown = false;
8932a4e3fcSOleksandr "Alex" Zinenko     accesses.clear();
9032a4e3fcSOleksandr "Alex" Zinenko     accesses.insert(op);
9132a4e3fcSOleksandr "Alex" Zinenko     return ChangeResult::Change;
9232a4e3fcSOleksandr "Alex" Zinenko   }
9332a4e3fcSOleksandr "Alex" Zinenko 
9432a4e3fcSOleksandr "Alex" Zinenko   ChangeResult setUnknown() {
9532a4e3fcSOleksandr "Alex" Zinenko     if (unknown)
9632a4e3fcSOleksandr "Alex" Zinenko       return ChangeResult::NoChange;
9732a4e3fcSOleksandr "Alex" Zinenko 
9832a4e3fcSOleksandr "Alex" Zinenko     accesses.clear();
9932a4e3fcSOleksandr "Alex" Zinenko     unknown = true;
10032a4e3fcSOleksandr "Alex" Zinenko     return ChangeResult::Change;
10132a4e3fcSOleksandr "Alex" Zinenko   }
10232a4e3fcSOleksandr "Alex" Zinenko 
10332a4e3fcSOleksandr "Alex" Zinenko   bool operator==(const AdjacentAccess &other) const {
10432a4e3fcSOleksandr "Alex" Zinenko     return unknown == other.unknown && accesses == other.accesses;
10532a4e3fcSOleksandr "Alex" Zinenko   }
10632a4e3fcSOleksandr "Alex" Zinenko 
10732a4e3fcSOleksandr "Alex" Zinenko   bool operator!=(const AdjacentAccess &other) const {
10832a4e3fcSOleksandr "Alex" Zinenko     return !operator==(other);
10932a4e3fcSOleksandr "Alex" Zinenko   }
11032a4e3fcSOleksandr "Alex" Zinenko 
11132a4e3fcSOleksandr "Alex" Zinenko private:
11232a4e3fcSOleksandr "Alex" Zinenko   bool unknown = false;
11332a4e3fcSOleksandr "Alex" Zinenko   DeterministicSetVector accesses;
11432a4e3fcSOleksandr "Alex" Zinenko };
11532a4e3fcSOleksandr "Alex" Zinenko 
1168a918c54SAlex Zinenko /// This lattice represents, for a given memory resource, the potential last
1178a918c54SAlex Zinenko /// operations that modified the resource.
1188a918c54SAlex Zinenko class AccessLatticeBase {
1198a918c54SAlex Zinenko public:
1208a918c54SAlex Zinenko   /// Clear all modifications.
1218a918c54SAlex Zinenko   ChangeResult reset() {
1228a918c54SAlex Zinenko     if (adjAccesses.empty())
1238a918c54SAlex Zinenko       return ChangeResult::NoChange;
1248a918c54SAlex Zinenko     adjAccesses.clear();
1258a918c54SAlex Zinenko     return ChangeResult::Change;
1268a918c54SAlex Zinenko   }
1278a918c54SAlex Zinenko 
1288a918c54SAlex Zinenko   /// Join the last modifications.
1298a918c54SAlex Zinenko   ChangeResult merge(const AccessLatticeBase &rhs) {
1308a918c54SAlex Zinenko     ChangeResult result = ChangeResult::NoChange;
1318a918c54SAlex Zinenko     for (const auto &mod : rhs.adjAccesses) {
13232a4e3fcSOleksandr "Alex" Zinenko       AdjacentAccess &lhsMod = adjAccesses[mod.first];
13332a4e3fcSOleksandr "Alex" Zinenko       result |= lhsMod.merge(mod.second);
1348a918c54SAlex Zinenko     }
1358a918c54SAlex Zinenko     return result;
1368a918c54SAlex Zinenko   }
1378a918c54SAlex Zinenko 
1388a918c54SAlex Zinenko   /// Set the last modification of a value.
1398a918c54SAlex Zinenko   ChangeResult set(Value value, Operation *op) {
14032a4e3fcSOleksandr "Alex" Zinenko     AdjacentAccess &lastMod = adjAccesses[value];
14132a4e3fcSOleksandr "Alex" Zinenko     return lastMod.set(op);
1428a918c54SAlex Zinenko   }
14332a4e3fcSOleksandr "Alex" Zinenko 
14432a4e3fcSOleksandr "Alex" Zinenko   ChangeResult setKnownToUnknown() {
14532a4e3fcSOleksandr "Alex" Zinenko     ChangeResult result = ChangeResult::NoChange;
14632a4e3fcSOleksandr "Alex" Zinenko     for (auto &[value, adjacent] : adjAccesses)
14732a4e3fcSOleksandr "Alex" Zinenko       result |= adjacent.setUnknown();
1488a918c54SAlex Zinenko     return result;
1498a918c54SAlex Zinenko   }
1508a918c54SAlex Zinenko 
1518a918c54SAlex Zinenko   /// Get the adjacent accesses to a value. Returns std::nullopt if they
1528a918c54SAlex Zinenko   /// are not known.
15332a4e3fcSOleksandr "Alex" Zinenko   const AdjacentAccess *getAdjacentAccess(Value value) const {
1548a918c54SAlex Zinenko     auto it = adjAccesses.find(value);
1558a918c54SAlex Zinenko     if (it == adjAccesses.end())
15632a4e3fcSOleksandr "Alex" Zinenko       return nullptr;
15732a4e3fcSOleksandr "Alex" Zinenko     return &it->getSecond();
1588a918c54SAlex Zinenko   }
1598a918c54SAlex Zinenko 
1608a918c54SAlex Zinenko   void print(raw_ostream &os) const {
1618a918c54SAlex Zinenko     for (const auto &lastMod : adjAccesses) {
1628a918c54SAlex Zinenko       os << lastMod.first << ":\n";
16332a4e3fcSOleksandr "Alex" Zinenko       if (!lastMod.second.isKnown()) {
16432a4e3fcSOleksandr "Alex" Zinenko         os << "  <unknown>\n";
16532a4e3fcSOleksandr "Alex" Zinenko         return;
16632a4e3fcSOleksandr "Alex" Zinenko       }
16732a4e3fcSOleksandr "Alex" Zinenko       for (Operation *op : lastMod.second.get())
1688a918c54SAlex Zinenko         os << "  " << *op << "\n";
1698a918c54SAlex Zinenko     }
1708a918c54SAlex Zinenko   }
1718a918c54SAlex Zinenko 
1728a918c54SAlex Zinenko private:
1738a918c54SAlex Zinenko   /// The potential adjacent accesses to a memory resource. Use a set vector to
1748a918c54SAlex Zinenko   /// keep the results deterministic.
17532a4e3fcSOleksandr "Alex" Zinenko   DenseMap<Value, AdjacentAccess> adjAccesses;
1768a918c54SAlex Zinenko };
1778a918c54SAlex Zinenko 
1788a918c54SAlex Zinenko /// Define the lattice class explicitly to provide a type ID.
1798a918c54SAlex Zinenko struct UnderlyingValueLattice : public Lattice<UnderlyingValue> {
1808a918c54SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(UnderlyingValueLattice)
1818a918c54SAlex Zinenko   using Lattice::Lattice;
1828a918c54SAlex Zinenko };
1838a918c54SAlex Zinenko 
1848a918c54SAlex Zinenko /// An analysis that uses forwarding of values along control-flow and callgraph
1858a918c54SAlex Zinenko /// edges to determine single underlying values for block arguments. This
1868a918c54SAlex Zinenko /// analysis exists so that the test analysis and pass can test the behaviour of
1878a918c54SAlex Zinenko /// the dense data-flow analysis on the callgraph.
1888a918c54SAlex Zinenko class UnderlyingValueAnalysis
189b2b7efb9SAlex Zinenko     : public SparseForwardDataFlowAnalysis<UnderlyingValueLattice> {
1908a918c54SAlex Zinenko public:
191b2b7efb9SAlex Zinenko   using SparseForwardDataFlowAnalysis::SparseForwardDataFlowAnalysis;
1928a918c54SAlex Zinenko 
1938a918c54SAlex Zinenko   /// The underlying value of the results of an operation are not known.
19415e915a4SIvan Butygin   LogicalResult
19515e915a4SIvan Butygin   visitOperation(Operation *op,
1968a918c54SAlex Zinenko                  ArrayRef<const UnderlyingValueLattice *> operands,
1978a918c54SAlex Zinenko                  ArrayRef<UnderlyingValueLattice *> results) override {
19815e915a4SIvan Butygin     // Hook to test error propagation from visitOperation.
19915e915a4SIvan Butygin     if (op->hasAttr("always_fail"))
20015e915a4SIvan Butygin       return op->emitError("this op is always fails");
20115e915a4SIvan Butygin 
2028a918c54SAlex Zinenko     setAllToEntryStates(results);
20315e915a4SIvan Butygin     return success();
2048a918c54SAlex Zinenko   }
2058a918c54SAlex Zinenko 
2068a918c54SAlex Zinenko   /// At an entry point, the underlying value of a value is itself.
2078a918c54SAlex Zinenko   void setToEntryState(UnderlyingValueLattice *lattice) override {
2088a918c54SAlex Zinenko     propagateIfChanged(lattice,
209*b6603e1bSdonald chen                        lattice->join(UnderlyingValue{lattice->getAnchor()}));
2108a918c54SAlex Zinenko   }
2118a918c54SAlex Zinenko 
2128a918c54SAlex Zinenko   /// Look for the most underlying value of a value.
21332a4e3fcSOleksandr "Alex" Zinenko   static std::optional<Value>
2148a918c54SAlex Zinenko   getMostUnderlyingValue(Value value,
2158a918c54SAlex Zinenko                          function_ref<const UnderlyingValueLattice *(Value)>
2168a918c54SAlex Zinenko                              getUnderlyingValueFn) {
2178a918c54SAlex Zinenko     const UnderlyingValueLattice *underlying;
2188a918c54SAlex Zinenko     do {
2198a918c54SAlex Zinenko       underlying = getUnderlyingValueFn(value);
2208a918c54SAlex Zinenko       if (!underlying || underlying->getValue().isUninitialized())
22132a4e3fcSOleksandr "Alex" Zinenko         return std::nullopt;
2228a918c54SAlex Zinenko       Value underlyingValue = underlying->getValue().getUnderlyingValue();
2238a918c54SAlex Zinenko       if (underlyingValue == value)
2248a918c54SAlex Zinenko         break;
2258a918c54SAlex Zinenko       value = underlyingValue;
2268a918c54SAlex Zinenko     } while (true);
2278a918c54SAlex Zinenko     return value;
2288a918c54SAlex Zinenko   }
2298a918c54SAlex Zinenko };
2308a918c54SAlex Zinenko 
2318a918c54SAlex Zinenko } // namespace test
2328a918c54SAlex Zinenko } // namespace dataflow
2338a918c54SAlex Zinenko } // namespace mlir
234