1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
|
//===- DenseAnalysis.cpp - Dense data-flow analysis -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
#include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
#include "mlir/Interfaces/CallInterfaces.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
using namespace mlir;
using namespace mlir::dataflow;
//===----------------------------------------------------------------------===//
// AbstractDenseDataFlowAnalysis
//===----------------------------------------------------------------------===//
LogicalResult AbstractDenseDataFlowAnalysis::initialize(Operation *top) {
// Visit every operation and block.
processOperation(top);
for (Region ®ion : top->getRegions()) {
for (Block &block : region) {
visitBlock(&block);
for (Operation &op : block)
if (failed(initialize(&op)))
return failure();
}
}
return success();
}
LogicalResult AbstractDenseDataFlowAnalysis::visit(ProgramPoint point) {
if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
processOperation(op);
else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
visitBlock(block);
else
return failure();
return success();
}
void AbstractDenseDataFlowAnalysis::visitCallOperation(
CallOpInterface call, AbstractDenseLattice *after) {
const auto *predecessors =
getOrCreateFor<PredecessorState>(call.getOperation(), call);
// If not all return sites are known, then conservatively assume we can't
// reason about the data-flow.
if (!predecessors->allPredecessorsKnown())
return setToEntryState(after);
for (Operation *predecessor : predecessors->getKnownPredecessors()) {
// Get the lattices at callee return:
//
// func.func @callee() {
// ...
// return // predecessor
// // latticeAtCalleeReturn
// }
// func.func @caller() {
// ...
// call @callee
// // latticeAfterCall
// ...
// }
AbstractDenseLattice *latticeAfterCall = after;
const AbstractDenseLattice *latticeAtCalleeReturn =
getLatticeFor(call.getOperation(), predecessor);
visitCallControlFlowTransfer(call, CallControlFlowAction::ExitCallee,
*latticeAtCalleeReturn, latticeAfterCall);
}
}
void AbstractDenseDataFlowAnalysis::processOperation(Operation *op) {
// If the containing block is not executable, bail out.
if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
return;
// Get the dense lattice to update.
AbstractDenseLattice *after = getLattice(op);
// Get the dense state before the execution of the op.
const AbstractDenseLattice *before;
if (Operation *prev = op->getPrevNode())
before = getLatticeFor(op, prev);
else
before = getLatticeFor(op, op->getBlock());
// If this op implements region control-flow, then control-flow dictates its
// transfer function.
if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
return visitRegionBranchOperation(op, branch, after);
// If this is a call operation, then join its lattices across known return
// sites.
if (auto call = dyn_cast<CallOpInterface>(op))
return visitCallOperation(call, after);
// Invoke the operation transfer function.
visitOperationImpl(op, *before, after);
}
void AbstractDenseDataFlowAnalysis::visitBlock(Block *block) {
// If the block is not executable, bail out.
if (!getOrCreateFor<Executable>(block, block)->isLive())
return;
// Get the dense lattice to update.
AbstractDenseLattice *after = getLattice(block);
// The dense lattices of entry blocks are set by region control-flow or the
// callgraph.
if (block->isEntryBlock()) {
// Check if this block is the entry block of a callable region.
auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
if (callable && callable.getCallableRegion() == block->getParent()) {
const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
// If not all callsites are known, conservatively mark all lattices as
// having reached their pessimistic fixpoints.
if (!callsites->allPredecessorsKnown())
return setToEntryState(after);
for (Operation *callsite : callsites->getKnownPredecessors()) {
// Get the dense lattice before the callsite.
const AbstractDenseLattice *before;
if (Operation *prev = callsite->getPrevNode())
before = getLatticeFor(block, prev);
else
before = getLatticeFor(block, callsite->getBlock());
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
CallControlFlowAction::EnterCallee,
*before, after);
}
return;
}
// Check if we can reason about the control-flow.
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp()))
return visitRegionBranchOperation(block, branch, after);
// Otherwise, we can't reason about the data-flow.
return setToEntryState(after);
}
// Join the state with the state after the block's predecessors.
for (Block::pred_iterator it = block->pred_begin(), e = block->pred_end();
it != e; ++it) {
// Skip control edges that aren't executable.
Block *predecessor = *it;
if (!getOrCreateFor<Executable>(
block, getProgramPoint<CFGEdge>(predecessor, block))
->isLive())
continue;
// Merge in the state from the predecessor's terminator.
join(after, *getLatticeFor(block, predecessor->getTerminator()));
}
}
void AbstractDenseDataFlowAnalysis::visitRegionBranchOperation(
ProgramPoint point, RegionBranchOpInterface branch,
AbstractDenseLattice *after) {
// Get the terminator predecessors.
const auto *predecessors = getOrCreateFor<PredecessorState>(point, point);
assert(predecessors->allPredecessorsKnown() &&
"unexpected unresolved region successors");
for (Operation *op : predecessors->getKnownPredecessors()) {
const AbstractDenseLattice *before;
// If the predecessor is the parent, get the state before the parent.
if (op == branch) {
if (Operation *prev = op->getPrevNode())
before = getLatticeFor(point, prev);
else
before = getLatticeFor(point, op->getBlock());
// Otherwise, get the state after the terminator.
} else {
before = getLatticeFor(point, op);
}
// This function is called in two cases:
// 1. when visiting the block (point = block);
// 2. when visiting the parent operation (point = parent op).
// In both cases, we are looking for predecessor operations of the point,
// 1. predecessor may be the terminator of another block from another
// region (assuming that the block does belong to another region via an
// assertion) or the parent (when parent can transfer control to this
// region);
// 2. predecessor may be the terminator of a block that exits the
// region (when region transfers control to the parent) or the operation
// before the parent.
// In the latter case, just perform the join as it isn't the control flow
// affected by the region.
std::optional<unsigned> regionFrom =
op == branch ? std::optional<unsigned>()
: op->getBlock()->getParent()->getRegionNumber();
if (auto *toBlock = point.dyn_cast<Block *>()) {
assert(op == branch ||
toBlock->getParent() != op->getBlock()->getParent());
unsigned regionTo = toBlock->getParent()->getRegionNumber();
visitRegionBranchControlFlowTransfer(branch, regionFrom, regionTo,
*before, after);
} else {
assert(point.get<Operation *>() == branch &&
"expected to be visiting the branch itself");
// Only need to call the arc transfer when the predecessor is the region
// or the op itself, not the previous op.
if (op->getParentOp() == branch || op == branch) {
visitRegionBranchControlFlowTransfer(
branch, regionFrom, /*regionTo=*/std::nullopt, *before, after);
} else {
join(after, *before);
}
}
}
}
const AbstractDenseLattice *
AbstractDenseDataFlowAnalysis::getLatticeFor(ProgramPoint dependent,
ProgramPoint point) {
AbstractDenseLattice *state = getLattice(point);
addDependency(state, dependent);
return state;
}
//===----------------------------------------------------------------------===//
// AbstractDenseBackwardDataFlowAnalysis
//===----------------------------------------------------------------------===//
LogicalResult
AbstractDenseBackwardDataFlowAnalysis::initialize(Operation *top) {
// Visit every operation and block.
processOperation(top);
for (Region ®ion : top->getRegions()) {
for (Block &block : region) {
visitBlock(&block);
for (Operation &op : llvm::reverse(block)) {
if (failed(initialize(&op)))
return failure();
}
}
}
return success();
}
LogicalResult AbstractDenseBackwardDataFlowAnalysis::visit(ProgramPoint point) {
if (auto *op = llvm::dyn_cast_if_present<Operation *>(point))
processOperation(op);
else if (auto *block = llvm::dyn_cast_if_present<Block *>(point))
visitBlock(block);
else
return failure();
return success();
}
void AbstractDenseBackwardDataFlowAnalysis::visitCallOperation(
CallOpInterface call, AbstractDenseLattice *before) {
// Find the callee.
Operation *callee = call.resolveCallable(&symbolTable);
auto callable = dyn_cast_or_null<CallableOpInterface>(callee);
if (!callable)
return setToExitState(before);
// No region means the callee is only declared in this module and we shouldn't
// assume anything about it.
Region *region = callable.getCallableRegion();
if (!region || region->empty())
return setToExitState(before);
// Call-level control flow specifies the data flow here.
//
// func.func @callee() {
// ^calleeEntryBlock:
// // latticeAtCalleeEntry
// ...
// }
// func.func @caller() {
// ...
// // latticeBeforeCall
// call @callee
// ...
// }
Block *calleeEntryBlock = ®ion->front();
ProgramPoint calleeEntry = calleeEntryBlock->empty()
? ProgramPoint(calleeEntryBlock)
: &calleeEntryBlock->front();
const AbstractDenseLattice &latticeAtCalleeEntry =
*getLatticeFor(call.getOperation(), calleeEntry);
AbstractDenseLattice *latticeBeforeCall = before;
visitCallControlFlowTransfer(call, CallControlFlowAction::EnterCallee,
latticeAtCalleeEntry, latticeBeforeCall);
}
void AbstractDenseBackwardDataFlowAnalysis::processOperation(Operation *op) {
// If the containing block is not executable, bail out.
if (!getOrCreateFor<Executable>(op, op->getBlock())->isLive())
return;
// Get the dense lattice to update.
AbstractDenseLattice *before = getLattice(op);
// Get the dense state after execution of this op.
const AbstractDenseLattice *after;
if (Operation *next = op->getNextNode())
after = getLatticeFor(op, next);
else
after = getLatticeFor(op, op->getBlock());
// Special cases where control flow may dictate data flow.
if (auto branch = dyn_cast<RegionBranchOpInterface>(op))
return visitRegionBranchOperation(op, branch, std::nullopt, before);
if (auto call = dyn_cast<CallOpInterface>(op))
return visitCallOperation(call, before);
// Invoke the operation transfer function.
visitOperationImpl(op, *after, before);
}
void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
// If the block is not executable, bail out.
if (!getOrCreateFor<Executable>(block, block)->isLive())
return;
AbstractDenseLattice *before = getLattice(block);
// We need "exit" blocks, i.e. the blocks that may return control to the
// parent operation.
auto isExitBlock = [](Block *b) {
// Treat empty and terminator-less blocks as exit blocks.
if (b->empty() || !b->back().mightHaveTrait<OpTrait::IsTerminator>())
return true;
// There may be a weird case where a terminator may be transferring control
// either to the parent or to another block, so exit blocks and successors
// are not mutually exclusive.
Operation *terminator = b->getTerminator();
return terminator && (terminator->hasTrait<OpTrait::ReturnLike>() ||
isa<RegionBranchTerminatorOpInterface>(terminator));
};
if (isExitBlock(block)) {
// If this block is exiting from a callable, the successors of exiting from
// a callable are the successors of all call sites. And the call sites
// themselves are predecessors of the callable.
auto callable = dyn_cast<CallableOpInterface>(block->getParentOp());
if (callable && callable.getCallableRegion() == block->getParent()) {
const auto *callsites = getOrCreateFor<PredecessorState>(block, callable);
// If not all call sites are known, conservative mark all lattices as
// having reached their pessimistic fix points.
if (!callsites->allPredecessorsKnown())
return setToExitState(before);
for (Operation *callsite : callsites->getKnownPredecessors()) {
const AbstractDenseLattice *after;
if (Operation *next = callsite->getNextNode())
after = getLatticeFor(block, next);
else
after = getLatticeFor(block, callsite->getBlock());
visitCallControlFlowTransfer(cast<CallOpInterface>(callsite),
CallControlFlowAction::ExitCallee, *after,
before);
}
return;
}
// If this block is exiting from an operation with region-based control
// flow, propagate the lattice back along the control flow edge.
if (auto branch = dyn_cast<RegionBranchOpInterface>(block->getParentOp())) {
visitRegionBranchOperation(block, branch,
block->getParent()->getRegionNumber(), before);
return;
}
// Cannot reason about successors of an exit block, set the pessimistic
// fixpoint.
return setToExitState(before);
}
// Meet the state with the state before block's successors.
for (Block *successor : block->getSuccessors()) {
if (!getOrCreateFor<Executable>(block,
getProgramPoint<CFGEdge>(block, successor))
->isLive())
continue;
// Merge in the state from the successor: either the first operation, or the
// block itself when empty.
if (successor->empty())
meet(before, *getLatticeFor(block, successor));
else
meet(before, *getLatticeFor(block, &successor->front()));
}
}
void AbstractDenseBackwardDataFlowAnalysis::visitRegionBranchOperation(
ProgramPoint point, RegionBranchOpInterface branch,
std::optional<unsigned> regionNo, AbstractDenseLattice *before) {
// The successors of the operation may be either the first operation of the
// entry block of each possible successor region, or the next operation when
// the branch is a successor of itself.
SmallVector<RegionSuccessor> successors;
branch.getSuccessorRegions(regionNo, successors);
for (const RegionSuccessor &successor : successors) {
const AbstractDenseLattice *after;
if (successor.isParent() || successor.getSuccessor()->empty()) {
if (Operation *next = branch->getNextNode())
after = getLatticeFor(point, next);
else
after = getLatticeFor(point, branch->getBlock());
} else {
Region *successorRegion = successor.getSuccessor();
assert(!successorRegion->empty() && "unexpected empty successor region");
Block *successorBlock = &successorRegion->front();
if (!getOrCreateFor<Executable>(point, successorBlock)->isLive())
continue;
if (successorBlock->empty())
after = getLatticeFor(point, successorBlock);
else
after = getLatticeFor(point, &successorBlock->front());
}
std::optional<unsigned> successorNo =
successor.isParent() ? std::optional<unsigned>()
: successor.getSuccessor()->getRegionNumber();
visitRegionBranchControlFlowTransfer(branch, regionNo, successorNo, *after,
before);
}
}
const AbstractDenseLattice *
AbstractDenseBackwardDataFlowAnalysis::getLatticeFor(ProgramPoint dependent,
ProgramPoint point) {
AbstractDenseLattice *state = getLattice(point);
addDependency(state, dependent);
return state;
}
|