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