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