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