-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[MLIR][LLVM]: Add an IR utility to perform slice walking #103053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c458ec8
MLIR, LLVM: Add an IR utility to perform proper slicing
Dinistro 8433b18
turn into a general slice walk
Dinistro 6e19a0d
add llvm region inlining test
Dinistro 8dbfe3c
rename to SliceWalk.*
Dinistro 9269aee
address review comments
Dinistro af86f42
move to analysis directory
Dinistro d25434c
address review comments
Dinistro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
//===- SliceWalk.h - Helpers for performing IR slice walks ---*- C++ -*-===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef MLIR_ANALYSIS_SLICEWALK_H | ||
#define MLIR_ANALYSIS_SLICEWALK_H | ||
|
||
#include "mlir/IR/ValueRange.h" | ||
|
||
namespace mlir { | ||
|
||
/// A class to signal how to proceed with the walk of the backward slice: | ||
/// - Interrupt: Stops the walk. | ||
/// - AdvanceTo: Continues the walk to user-specified values. | ||
/// - Skip: Continues the walk, but skips the predecessors of the current value. | ||
class WalkContinuation { | ||
public: | ||
enum class WalkAction { | ||
/// Stops the walk. | ||
Interrupt, | ||
/// Continues the walk to user-specified values. | ||
AdvanceTo, | ||
/// Continues the walk, but skips the predecessors of the current value. | ||
Skip | ||
}; | ||
|
||
WalkContinuation(WalkAction action, mlir::ValueRange nextValues) | ||
: action(action), nextValues(nextValues) {} | ||
|
||
/// Allows diagnostics to interrupt the walk. | ||
explicit WalkContinuation(mlir::Diagnostic &&) | ||
: action(WalkAction::Interrupt) {} | ||
|
||
/// Allows diagnostics to interrupt the walk. | ||
explicit WalkContinuation(mlir::InFlightDiagnostic &&) | ||
: action(WalkAction::Interrupt) {} | ||
|
||
/// Creates a continuation that interrupts the walk. | ||
static WalkContinuation interrupt() { | ||
return WalkContinuation(WalkAction::Interrupt, {}); | ||
} | ||
|
||
/// Creates a continuation that adds the user-specified `nextValues` to the | ||
/// work list and advances the walk. | ||
static WalkContinuation advanceTo(mlir::ValueRange nextValues) { | ||
return WalkContinuation(WalkAction::AdvanceTo, nextValues); | ||
} | ||
|
||
/// Creates a continuation that advances the walk without adding any | ||
/// predecessor values to the work list. | ||
static WalkContinuation skip() { | ||
return WalkContinuation(WalkAction::Skip, {}); | ||
} | ||
|
||
/// Returns true if the walk was interrupted. | ||
bool wasInterrupted() const { return action == WalkAction::Interrupt; } | ||
|
||
/// Returns true if the walk was skipped. | ||
bool wasSkipped() const { return action == WalkAction::Skip; } | ||
|
||
/// Returns true if the walk was advanced to user-specified values. | ||
bool wasAdvancedTo() const { return action == WalkAction::AdvanceTo; } | ||
|
||
/// Returns the next values to continue the walk with. | ||
mlir::ArrayRef<mlir::Value> getNextValues() const { return nextValues; } | ||
|
||
private: | ||
WalkAction action; | ||
/// The next values to continue the walk with. | ||
mlir::SmallVector<mlir::Value> nextValues; | ||
}; | ||
|
||
/// A callback that is invoked for each value encountered during the walk of the | ||
/// slice. The callback takes the current value, and returns the walk | ||
/// continuation, which determines if the walk should proceed and if yes, with | ||
/// which values. | ||
using WalkCallback = mlir::function_ref<WalkContinuation(mlir::Value)>; | ||
|
||
/// Walks the slice starting from the `rootValues` using a depth-first | ||
/// traversal. The walk calls the provided `walkCallback` for each value | ||
/// encountered in the slice and uses the returned walk continuation to | ||
/// determine how to proceed. | ||
WalkContinuation walkSlice(mlir::ValueRange rootValues, | ||
WalkCallback walkCallback); | ||
|
||
/// Computes a vector of all control predecessors of `value`. Relies on | ||
/// RegionBranchOpInterface and BranchOpInterface to determine predecessors. | ||
/// Returns nullopt if `value` has no predecessors or when the relevant | ||
/// operations are missing the interface implementations. | ||
std::optional<SmallVector<Value>> getControlFlowPredecessors(Value value); | ||
|
||
} // namespace mlir | ||
|
||
#endif // MLIR_ANALYSIS_SLICEWALK_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
#include "mlir/Analysis/SliceWalk.h" | ||
#include "mlir/Interfaces/ControlFlowInterfaces.h" | ||
|
||
using namespace mlir; | ||
|
||
WalkContinuation mlir::walkSlice(ValueRange rootValues, | ||
WalkCallback walkCallback) { | ||
// Search the backward slice starting from the root values. | ||
SmallVector<Value> workList = rootValues; | ||
llvm::SmallDenseSet<Value, 16> seenValues; | ||
while (!workList.empty()) { | ||
// Search the backward slice of the current value. | ||
Value current = workList.pop_back_val(); | ||
|
||
// Skip the current value if it has already been seen. | ||
if (!seenValues.insert(current).second) | ||
continue; | ||
|
||
// Call the walk callback with the current value. | ||
WalkContinuation continuation = walkCallback(current); | ||
if (continuation.wasInterrupted()) | ||
return continuation; | ||
if (continuation.wasSkipped()) | ||
continue; | ||
|
||
assert(continuation.wasAdvancedTo()); | ||
// Add the next values to the work list if the walk should continue. | ||
workList.append(continuation.getNextValues().begin(), | ||
continuation.getNextValues().end()); | ||
} | ||
|
||
return WalkContinuation::skip(); | ||
} | ||
|
||
/// Returns the operands from all predecessor regions that match `operandNumber` | ||
/// for the `successor` region within `regionOp`. | ||
static SmallVector<Value> | ||
getRegionPredecessorOperands(RegionBranchOpInterface regionOp, | ||
RegionSuccessor successor, | ||
unsigned operandNumber) { | ||
SmallVector<Value> predecessorOperands; | ||
|
||
// Returns true if `successors` contains `successor`. | ||
auto isContained = [](ArrayRef<RegionSuccessor> successors, | ||
RegionSuccessor successor) { | ||
auto *it = llvm::find_if(successors, [&successor](RegionSuccessor curr) { | ||
return curr.getSuccessor() == successor.getSuccessor(); | ||
}); | ||
return it != successors.end(); | ||
}; | ||
|
||
// Search the operand ranges on the region operation itself. | ||
SmallVector<Attribute> operandAttributes(regionOp->getNumOperands()); | ||
SmallVector<RegionSuccessor> successors; | ||
regionOp.getEntrySuccessorRegions(operandAttributes, successors); | ||
if (isContained(successors, successor)) { | ||
OperandRange operands = regionOp.getEntrySuccessorOperands(successor); | ||
predecessorOperands.push_back(operands[operandNumber]); | ||
} | ||
|
||
// Search the operand ranges on region terminators. | ||
for (Region ®ion : regionOp->getRegions()) { | ||
for (Block &block : region) { | ||
auto terminatorOp = | ||
dyn_cast<RegionBranchTerminatorOpInterface>(block.getTerminator()); | ||
if (!terminatorOp) | ||
continue; | ||
SmallVector<Attribute> operandAttributes(terminatorOp->getNumOperands()); | ||
SmallVector<RegionSuccessor> successors; | ||
terminatorOp.getSuccessorRegions(operandAttributes, successors); | ||
if (isContained(successors, successor)) { | ||
OperandRange operands = terminatorOp.getSuccessorOperands(successor); | ||
predecessorOperands.push_back(operands[operandNumber]); | ||
} | ||
} | ||
} | ||
|
||
return predecessorOperands; | ||
} | ||
|
||
/// Returns the predecessor branch operands that match `blockArg`, or nullopt if | ||
/// some of the predecessor terminators do not implement the BranchOpInterface. | ||
static std::optional<SmallVector<Value>> | ||
getBlockPredecessorOperands(BlockArgument blockArg) { | ||
Block *block = blockArg.getOwner(); | ||
|
||
// Search the predecessor operands for all predecessor terminators. | ||
SmallVector<Value> predecessorOperands; | ||
for (auto it = block->pred_begin(); it != block->pred_end(); ++it) { | ||
Block *predecessor = *it; | ||
auto branchOp = dyn_cast<BranchOpInterface>(predecessor->getTerminator()); | ||
if (!branchOp) | ||
return std::nullopt; | ||
SuccessorOperands successorOperands = | ||
branchOp.getSuccessorOperands(it.getSuccessorIndex()); | ||
// Store the predecessor operand if the block argument matches an operand | ||
// and is not produced by the terminator. | ||
if (Value operand = successorOperands[blockArg.getArgNumber()]) | ||
predecessorOperands.push_back(operand); | ||
} | ||
|
||
return predecessorOperands; | ||
} | ||
|
||
std::optional<SmallVector<Value>> | ||
mlir::getControlFlowPredecessors(Value value) { | ||
SmallVector<Value> result; | ||
if (OpResult opResult = dyn_cast<OpResult>(value)) { | ||
auto regionOp = dyn_cast<RegionBranchOpInterface>(opResult.getOwner()); | ||
// If the interface is not implemented, there are no control flow | ||
// predecessors to work with. | ||
if (!regionOp) | ||
return std::nullopt; | ||
// Add the control flow predecessor operands to the work list. | ||
RegionSuccessor region(regionOp->getResults()); | ||
SmallVector<Value> predecessorOperands = getRegionPredecessorOperands( | ||
regionOp, region, opResult.getResultNumber()); | ||
return predecessorOperands; | ||
} | ||
|
||
auto blockArg = cast<BlockArgument>(value); | ||
Block *block = blockArg.getOwner(); | ||
// Search the region predecessor operands for structured control flow. | ||
if (block->isEntryBlock()) { | ||
if (auto regionBranchOp = | ||
dyn_cast<RegionBranchOpInterface>(block->getParentOp())) { | ||
RegionSuccessor region(blockArg.getParentRegion()); | ||
SmallVector<Value> predecessorOperands = getRegionPredecessorOperands( | ||
regionBranchOp, region, blockArg.getArgNumber()); | ||
return predecessorOperands; | ||
} | ||
// If the interface is not implemented, there are no control flow | ||
// predecessors to work with. | ||
return std::nullopt; | ||
} | ||
|
||
// Search the block predecessor operands for unstructured control flow. | ||
return getBlockPredecessorOperands(blockArg); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test case that exercise the failure case?