1 //===- DenseAnalysis.h - Dense data-flow analysis -------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements dense data-flow analysis using the data-flow analysis 10 // framework. The analysis is forward and conditional and uses the results of 11 // dead code analysis to prune dead code during the analysis. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H 16 #define MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H 17 18 #include "mlir/Analysis/DataFlowFramework.h" 19 #include "mlir/IR/SymbolTable.h" 20 #include "mlir/Interfaces/CallInterfaces.h" 21 #include "mlir/Interfaces/ControlFlowInterfaces.h" 22 23 namespace mlir { 24 namespace dataflow { 25 26 //===----------------------------------------------------------------------===// 27 // CallControlFlowAction 28 //===----------------------------------------------------------------------===// 29 30 /// Indicates whether the control enters, exits, or skips over the callee (in 31 /// the case of external functions). 32 enum class CallControlFlowAction { EnterCallee, ExitCallee, ExternalCallee }; 33 34 //===----------------------------------------------------------------------===// 35 // AbstractDenseLattice 36 //===----------------------------------------------------------------------===// 37 38 /// This class represents a dense lattice. A dense lattice is attached to 39 /// program point to represent the program state at the program point. 40 /// lattice is propagated through the IR by dense data-flow analysis. 41 class AbstractDenseLattice : public AnalysisState { 42 public: 43 /// A dense lattice can only be created for operations and blocks. 44 using AnalysisState::AnalysisState; 45 46 /// Join the lattice across control-flow or callgraph edges. 47 virtual ChangeResult join(const AbstractDenseLattice &rhs) { 48 return ChangeResult::NoChange; 49 } 50 51 virtual ChangeResult meet(const AbstractDenseLattice &rhs) { 52 return ChangeResult::NoChange; 53 } 54 }; 55 56 //===----------------------------------------------------------------------===// 57 // AbstractDenseForwardDataFlowAnalysis 58 //===----------------------------------------------------------------------===// 59 60 /// Base class for dense forward data-flow analyses. Dense data-flow analysis 61 /// attaches a lattice to program points and implements a transfer function from 62 /// the lattice before each operation to the lattice after. The lattice contains 63 /// information about the state of the program at that program point. 64 /// 65 /// Visit a program point in forward dense data-flow analysis will invoke the 66 /// transfer function of the operation preceding the program point iterator. 67 /// Visit a program point at the begining of block will visit the block itself. 68 class AbstractDenseForwardDataFlowAnalysis : public DataFlowAnalysis { 69 public: 70 using DataFlowAnalysis::DataFlowAnalysis; 71 72 /// Initialize the analysis by visiting every program point whose execution 73 /// may modify the program state; that is, every operation and block. 74 LogicalResult initialize(Operation *top) override; 75 76 /// Visit a program point that modifies the state of the program. If the 77 /// program point is at the beginning of a block, then the state is propagated 78 /// from control-flow predecessors or callsites. If the operation before 79 /// program point iterator is a call operation or region control-flow 80 /// operation, then the state after the execution of the operation is set by 81 /// control-flow or the callgraph. Otherwise, this function invokes the 82 /// operation transfer function before the program point iterator. 83 LogicalResult visit(ProgramPoint *point) override; 84 85 protected: 86 /// Propagate the dense lattice before the execution of an operation to the 87 /// lattice after its execution. 88 virtual LogicalResult visitOperationImpl(Operation *op, 89 const AbstractDenseLattice &before, 90 AbstractDenseLattice *after) = 0; 91 92 /// Get the dense lattice on the given lattice anchor. 93 virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0; 94 95 /// Get the dense lattice on the given lattice anchor and add dependent as its 96 /// dependency. That is, every time the lattice after anchor is updated, the 97 /// dependent program point must be visited, and the newly triggered visit 98 /// might update the lattice on dependent. 99 const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent, 100 LatticeAnchor anchor); 101 102 /// Set the dense lattice at control flow entry point and propagate an update 103 /// if it changed. 104 virtual void setToEntryState(AbstractDenseLattice *lattice) = 0; 105 106 /// Join a lattice with another and propagate an update if it changed. 107 void join(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) { 108 propagateIfChanged(lhs, lhs->join(rhs)); 109 } 110 111 /// Visit an operation. If this is a call operation or region control-flow 112 /// operation, then the state after the execution of the operation is set by 113 /// control-flow or the callgraph. Otherwise, this function invokes the 114 /// operation transfer function. 115 virtual LogicalResult processOperation(Operation *op); 116 117 /// Propagate the dense lattice forward along the control flow edge from 118 /// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt` 119 /// values correspond to control flow branches originating at or targeting the 120 /// `branch` operation itself. Default implementation just joins the states, 121 /// meaning that operations implementing `RegionBranchOpInterface` don't have 122 /// any effect on the lattice that isn't already expressed by the interface 123 /// itself. 124 virtual void visitRegionBranchControlFlowTransfer( 125 RegionBranchOpInterface branch, std::optional<unsigned> regionFrom, 126 std::optional<unsigned> regionTo, const AbstractDenseLattice &before, 127 AbstractDenseLattice *after) { 128 join(after, before); 129 } 130 131 /// Propagate the dense lattice forward along the call control flow edge, 132 /// which can be either entering or exiting the callee. Default implementation 133 /// for enter and exit callee actions just meets the states, meaning that 134 /// operations implementing `CallOpInterface` don't have any effect on the 135 /// lattice that isn't already expressed by the interface itself. Default 136 /// implementation for the external callee action additionally sets the 137 /// "after" lattice to the entry state. 138 virtual void visitCallControlFlowTransfer(CallOpInterface call, 139 CallControlFlowAction action, 140 const AbstractDenseLattice &before, 141 AbstractDenseLattice *after) { 142 join(after, before); 143 // Note that `setToEntryState` may be a "partial fixpoint" for some 144 // lattices, e.g., lattices that are lists of maps of other lattices will 145 // only set fixpoint for "known" lattices. 146 if (action == CallControlFlowAction::ExternalCallee) 147 setToEntryState(after); 148 } 149 150 /// Visit a program point within a region branch operation with predecessors 151 /// in it. This can either be an entry block of one of the regions of the 152 /// parent operation itself. 153 void visitRegionBranchOperation(ProgramPoint *point, 154 RegionBranchOpInterface branch, 155 AbstractDenseLattice *after); 156 157 private: 158 /// Visit a block. The state at the start of the block is propagated from 159 /// control-flow predecessors or callsites. 160 void visitBlock(Block *block); 161 162 /// Visit an operation for which the data flow is described by the 163 /// `CallOpInterface`. 164 void visitCallOperation(CallOpInterface call, 165 const AbstractDenseLattice &before, 166 AbstractDenseLattice *after); 167 }; 168 169 //===----------------------------------------------------------------------===// 170 // DenseForwardDataFlowAnalysis 171 //===----------------------------------------------------------------------===// 172 173 /// A dense forward data-flow analysis for propagating lattices before and 174 /// after the execution of every operation across the IR by implementing 175 /// transfer functions for operations. 176 /// 177 /// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`. 178 template <typename LatticeT> 179 class DenseForwardDataFlowAnalysis 180 : public AbstractDenseForwardDataFlowAnalysis { 181 static_assert( 182 std::is_base_of<AbstractDenseLattice, LatticeT>::value, 183 "analysis state class expected to subclass AbstractDenseLattice"); 184 185 public: 186 using AbstractDenseForwardDataFlowAnalysis:: 187 AbstractDenseForwardDataFlowAnalysis; 188 189 /// Visit an operation with the dense lattice before its execution. This 190 /// function is expected to set the dense lattice after its execution and 191 /// trigger change propagation in case of change. 192 virtual LogicalResult visitOperation(Operation *op, const LatticeT &before, 193 LatticeT *after) = 0; 194 195 /// Hook for customizing the behavior of lattice propagation along the call 196 /// control flow edges. Two types of (forward) propagation are possible here: 197 /// - `action == CallControlFlowAction::Enter` indicates that: 198 /// - `before` is the state before the call operation; 199 /// - `after` is the state at the beginning of the callee entry block; 200 /// - `action == CallControlFlowAction::Exit` indicates that: 201 /// - `before` is the state at the end of a callee exit block; 202 /// - `after` is the state after the call operation. 203 /// By default, the `after` state is simply joined with the `before` state. 204 /// Concrete analyses can override this behavior or delegate to the parent 205 /// call for the default behavior. Specifically, if the `call` op may affect 206 /// the lattice prior to entering the callee, the custom behavior can be added 207 /// for `action == CallControlFlowAction::Enter`. If the `call` op may affect 208 /// the lattice post exiting the callee, the custom behavior can be added for 209 /// `action == CallControlFlowAction::Exit`. 210 virtual void visitCallControlFlowTransfer(CallOpInterface call, 211 CallControlFlowAction action, 212 const LatticeT &before, 213 LatticeT *after) { 214 AbstractDenseForwardDataFlowAnalysis::visitCallControlFlowTransfer( 215 call, action, before, after); 216 } 217 218 /// Hook for customizing the behavior of lattice propagation along the control 219 /// flow edges between regions and their parent op. The control flows from 220 /// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the 221 /// parent op. The lattice is propagated forward along this edge. The lattices 222 /// are as follows: 223 /// - `before:` 224 /// - if `regionFrom` is a region, this is the lattice at the end of the 225 /// block that exits the region; note that for multi-exit regions, the 226 /// lattices are equal at the end of all exiting blocks, but they are 227 /// associated with different program points. 228 /// - otherwise, this is the lattice before the parent op. 229 /// - `after`: 230 /// - if `regionTo` is a region, this is the lattice at the beginning of 231 /// the entry block of that region; 232 /// - otherwise, this is the lattice after the parent op. 233 /// By default, the `after` state is simply joined with the `before` state. 234 /// Concrete analyses can override this behavior or delegate to the parent 235 /// call for the default behavior. Specifically, if the `branch` op may affect 236 /// the lattice before entering any region, the custom behavior can be added 237 /// for `regionFrom == nullopt`. If the `branch` op may affect the lattice 238 /// after all terminated, the custom behavior can be added for `regionTo == 239 /// nullptr`. The behavior can be further refined for specific pairs of "from" 240 /// and "to" regions. 241 virtual void visitRegionBranchControlFlowTransfer( 242 RegionBranchOpInterface branch, std::optional<unsigned> regionFrom, 243 std::optional<unsigned> regionTo, const LatticeT &before, 244 LatticeT *after) { 245 AbstractDenseForwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer( 246 branch, regionFrom, regionTo, before, after); 247 } 248 249 protected: 250 /// Get the dense lattice on this lattice anchor. 251 LatticeT *getLattice(LatticeAnchor anchor) override { 252 return getOrCreate<LatticeT>(anchor); 253 } 254 255 /// Set the dense lattice at control flow entry point and propagate an update 256 /// if it changed. 257 virtual void setToEntryState(LatticeT *lattice) = 0; 258 void setToEntryState(AbstractDenseLattice *lattice) override { 259 setToEntryState(static_cast<LatticeT *>(lattice)); 260 } 261 262 /// Type-erased wrappers that convert the abstract dense lattice to a derived 263 /// lattice and invoke the virtual hooks operating on the derived lattice. 264 LogicalResult visitOperationImpl(Operation *op, 265 const AbstractDenseLattice &before, 266 AbstractDenseLattice *after) final { 267 return visitOperation(op, static_cast<const LatticeT &>(before), 268 static_cast<LatticeT *>(after)); 269 } 270 void visitCallControlFlowTransfer(CallOpInterface call, 271 CallControlFlowAction action, 272 const AbstractDenseLattice &before, 273 AbstractDenseLattice *after) final { 274 visitCallControlFlowTransfer(call, action, 275 static_cast<const LatticeT &>(before), 276 static_cast<LatticeT *>(after)); 277 } 278 void visitRegionBranchControlFlowTransfer(RegionBranchOpInterface branch, 279 std::optional<unsigned> regionFrom, 280 std::optional<unsigned> regionTo, 281 const AbstractDenseLattice &before, 282 AbstractDenseLattice *after) final { 283 visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo, 284 static_cast<const LatticeT &>(before), 285 static_cast<LatticeT *>(after)); 286 } 287 }; 288 289 //===----------------------------------------------------------------------===// 290 // AbstractDenseBackwardDataFlowAnalysis 291 //===----------------------------------------------------------------------===// 292 293 /// Base class for dense backward dataflow analyses. Such analyses attach a 294 /// lattice to program point and implement a transfer function from the lattice 295 /// after the operation to the lattice before it, thus propagating backward. 296 /// 297 /// Visit a program point in dense backward data-flow analysis will invoke the 298 /// transfer function of the operation following the program point iterator. 299 /// Visit a program point at the end of block will visit the block itself. 300 class AbstractDenseBackwardDataFlowAnalysis : public DataFlowAnalysis { 301 public: 302 /// Construct the analysis in the given solver. Takes a symbol table 303 /// collection that is used to cache symbol resolution in interprocedural part 304 /// of the analysis. The symbol table need not be prefilled. 305 AbstractDenseBackwardDataFlowAnalysis(DataFlowSolver &solver, 306 SymbolTableCollection &symbolTable) 307 : DataFlowAnalysis(solver), symbolTable(symbolTable) {} 308 309 /// Initialize the analysis by visiting every program point whose execution 310 /// may modify the program state; that is, every operation and block. 311 LogicalResult initialize(Operation *top) override; 312 313 /// Visit a program point that modifies the state of the program. The state is 314 /// propagated along control flow directions for branch-, region- and 315 /// call-based control flow using the respective interfaces. For other 316 /// operations, the state is propagated using the transfer function 317 /// (visitOperation). 318 /// 319 /// Note: the transfer function is currently *not* invoked before operations 320 /// with region or call interface, but *is* invoked before block terminators. 321 LogicalResult visit(ProgramPoint *point) override; 322 323 protected: 324 /// Propagate the dense lattice after the execution of an operation to the 325 /// lattice before its execution. 326 virtual LogicalResult visitOperationImpl(Operation *op, 327 const AbstractDenseLattice &after, 328 AbstractDenseLattice *before) = 0; 329 330 /// Get the dense lattice before the execution of the lattice anchor. That is, 331 /// before the execution of the given operation or after the execution of the 332 /// block. 333 virtual AbstractDenseLattice *getLattice(LatticeAnchor anchor) = 0; 334 335 /// Get the dense lattice on the given lattice anchor and add dependent as its 336 /// dependency. That is, every time the lattice after anchor is updated, the 337 /// dependent program point must be visited, and the newly triggered visit 338 /// might update the lattice before dependent. 339 const AbstractDenseLattice *getLatticeFor(ProgramPoint *dependent, 340 LatticeAnchor anchor); 341 342 /// Set the dense lattice before at the control flow exit point and propagate 343 /// the update if it changed. 344 virtual void setToExitState(AbstractDenseLattice *lattice) = 0; 345 346 /// Meet a lattice with another lattice and propagate an update if it changed. 347 void meet(AbstractDenseLattice *lhs, const AbstractDenseLattice &rhs) { 348 propagateIfChanged(lhs, lhs->meet(rhs)); 349 } 350 351 /// Visit an operation. Dispatches to specialized methods for call or region 352 /// control-flow operations. Otherwise, this function invokes the operation 353 /// transfer function. 354 virtual LogicalResult processOperation(Operation *op); 355 356 /// Propagate the dense lattice backwards along the control flow edge from 357 /// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt` 358 /// values correspond to control flow branches originating at or targeting the 359 /// `branch` operation itself. Default implementation just meets the states, 360 /// meaning that operations implementing `RegionBranchOpInterface` don't have 361 /// any effect on the lattice that isn't already expressed by the interface 362 /// itself. 363 virtual void visitRegionBranchControlFlowTransfer( 364 RegionBranchOpInterface branch, RegionBranchPoint regionFrom, 365 RegionBranchPoint regionTo, const AbstractDenseLattice &after, 366 AbstractDenseLattice *before) { 367 meet(before, after); 368 } 369 370 /// Propagate the dense lattice backwards along the call control flow edge, 371 /// which can be either entering or exiting the callee. Default implementation 372 /// for enter and exit callee action just meets the states, meaning that 373 /// operations implementing `CallOpInterface` don't have any effect on the 374 /// lattice that isn't already expressed by the interface itself. Default 375 /// implementation for external callee action additional sets the result to 376 /// the exit (fixpoint) state. 377 virtual void visitCallControlFlowTransfer(CallOpInterface call, 378 CallControlFlowAction action, 379 const AbstractDenseLattice &after, 380 AbstractDenseLattice *before) { 381 meet(before, after); 382 383 // Note that `setToExitState` may be a "partial fixpoint" for some lattices, 384 // e.g., lattices that are lists of maps of other lattices will only 385 // set fixpoint for "known" lattices. 386 if (action == CallControlFlowAction::ExternalCallee) 387 setToExitState(before); 388 } 389 390 private: 391 /// Visit a block. The state and the end of the block is propagated from 392 /// control-flow successors of the block or callsites. 393 void visitBlock(Block *block); 394 395 /// Visit a program point within a region branch operation with successors 396 /// (from which the state is propagated) in or after it. `regionNo` indicates 397 /// the region that contains the successor, `nullopt` indicating the successor 398 /// of the branch operation itself. 399 void visitRegionBranchOperation(ProgramPoint *point, 400 RegionBranchOpInterface branch, 401 RegionBranchPoint branchPoint, 402 AbstractDenseLattice *before); 403 404 /// Visit an operation for which the data flow is described by the 405 /// `CallOpInterface`. Performs inter-procedural data flow as follows: 406 /// 407 /// - find the callable (resolve via the symbol table), 408 /// - get the entry block of the callable region, 409 /// - take the state before the first operation if present or at block end 410 /// otherwise, 411 /// - meet that state with the state before the call-like op, or use the 412 /// custom logic if overridden by concrete analyses. 413 void visitCallOperation(CallOpInterface call, 414 const AbstractDenseLattice &after, 415 AbstractDenseLattice *before); 416 417 /// Symbol table for call-level control flow. 418 SymbolTableCollection &symbolTable; 419 }; 420 421 //===----------------------------------------------------------------------===// 422 // DenseBackwardDataFlowAnalysis 423 //===----------------------------------------------------------------------===// 424 425 /// A dense backward dataflow analysis propagating lattices after and before the 426 /// execution of every operation across the IR by implementing transfer 427 /// functions for opreations. 428 /// 429 /// `LatticeT` is expected to be a subclass of `AbstractDenseLattice`. 430 template <typename LatticeT> 431 class DenseBackwardDataFlowAnalysis 432 : public AbstractDenseBackwardDataFlowAnalysis { 433 static_assert(std::is_base_of_v<AbstractDenseLattice, LatticeT>, 434 "analysis state expected to subclass AbstractDenseLattice"); 435 436 public: 437 using AbstractDenseBackwardDataFlowAnalysis:: 438 AbstractDenseBackwardDataFlowAnalysis; 439 440 /// Transfer function. Visits an operation with the dense lattice after its 441 /// execution. This function is expected to set the dense lattice before its 442 /// execution and trigger propagation in case of change. 443 virtual LogicalResult visitOperation(Operation *op, const LatticeT &after, 444 LatticeT *before) = 0; 445 446 /// Hook for customizing the behavior of lattice propagation along the call 447 /// control flow edges. Two types of (back) propagation are possible here: 448 /// - `action == CallControlFlowAction::Enter` indicates that: 449 /// - `after` is the state at the top of the callee entry block; 450 /// - `before` is the state before the call operation; 451 /// - `action == CallControlFlowAction::Exit` indicates that: 452 /// - `after` is the state after the call operation; 453 /// - `before` is the state of exit blocks of the callee. 454 /// By default, the `before` state is simply met with the `after` state. 455 /// Concrete analyses can override this behavior or delegate to the parent 456 /// call for the default behavior. Specifically, if the `call` op may affect 457 /// the lattice prior to entering the callee, the custom behavior can be added 458 /// for `action == CallControlFlowAction::Enter`. If the `call` op may affect 459 /// the lattice post exiting the callee, the custom behavior can be added for 460 /// `action == CallControlFlowAction::Exit`. 461 virtual void visitCallControlFlowTransfer(CallOpInterface call, 462 CallControlFlowAction action, 463 const LatticeT &after, 464 LatticeT *before) { 465 AbstractDenseBackwardDataFlowAnalysis::visitCallControlFlowTransfer( 466 call, action, after, before); 467 } 468 469 /// Hook for customizing the behavior of lattice propagation along the control 470 /// flow edges between regions and their parent op. The control flows from 471 /// `regionFrom` to `regionTo`, both of which may be `nullopt` to indicate the 472 /// parent op. The lattice is propagated back along this edge. The lattices 473 /// are as follows: 474 /// - `after`: 475 /// - if `regionTo` is a region, this is the lattice at the beginning of 476 /// the entry block of that region; 477 /// - otherwise, this is the lattice after the parent op. 478 /// - `before:` 479 /// - if `regionFrom` is a region, this is the lattice at the end of the 480 /// block that exits the region; note that for multi-exit regions, the 481 /// lattices are equal at the end of all exiting blocks, but they are 482 /// associated with different program points. 483 /// - otherwise, this is the lattice before the parent op. 484 /// By default, the `before` state is simply met with the `after` state. 485 /// Concrete analyses can override this behavior or delegate to the parent 486 /// call for the default behavior. Specifically, if the `branch` op may affect 487 /// the lattice before entering any region, the custom behavior can be added 488 /// for `regionFrom == nullopt`. If the `branch` op may affect the lattice 489 /// after all terminated, the custom behavior can be added for `regionTo == 490 /// nullptr`. The behavior can be further refined for specific pairs of "from" 491 /// and "to" regions. 492 virtual void visitRegionBranchControlFlowTransfer( 493 RegionBranchOpInterface branch, RegionBranchPoint regionFrom, 494 RegionBranchPoint regionTo, const LatticeT &after, LatticeT *before) { 495 AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchControlFlowTransfer( 496 branch, regionFrom, regionTo, after, before); 497 } 498 499 protected: 500 /// Get the dense lattice at the given lattice anchor. 501 LatticeT *getLattice(LatticeAnchor anchor) override { 502 return getOrCreate<LatticeT>(anchor); 503 } 504 505 /// Set the dense lattice at control flow exit point (after the terminator) 506 /// and propagate an update if it changed. 507 virtual void setToExitState(LatticeT *lattice) = 0; 508 void setToExitState(AbstractDenseLattice *lattice) final { 509 setToExitState(static_cast<LatticeT *>(lattice)); 510 } 511 512 /// Type-erased wrappers that convert the abstract dense lattice to a derived 513 /// lattice and invoke the virtual hooks operating on the derived lattice. 514 LogicalResult visitOperationImpl(Operation *op, 515 const AbstractDenseLattice &after, 516 AbstractDenseLattice *before) final { 517 return visitOperation(op, static_cast<const LatticeT &>(after), 518 static_cast<LatticeT *>(before)); 519 } 520 void visitCallControlFlowTransfer(CallOpInterface call, 521 CallControlFlowAction action, 522 const AbstractDenseLattice &after, 523 AbstractDenseLattice *before) final { 524 visitCallControlFlowTransfer(call, action, 525 static_cast<const LatticeT &>(after), 526 static_cast<LatticeT *>(before)); 527 } 528 void visitRegionBranchControlFlowTransfer( 529 RegionBranchOpInterface branch, RegionBranchPoint regionForm, 530 RegionBranchPoint regionTo, const AbstractDenseLattice &after, 531 AbstractDenseLattice *before) final { 532 visitRegionBranchControlFlowTransfer(branch, regionForm, regionTo, 533 static_cast<const LatticeT &>(after), 534 static_cast<LatticeT *>(before)); 535 } 536 }; 537 538 } // end namespace dataflow 539 } // end namespace mlir 540 541 #endif // MLIR_ANALYSIS_DENSEDATAFLOWANALYSIS_H 542