forked from revng/revng
-
Notifications
You must be signed in to change notification settings - Fork 1
Add Clift beautification passes for SwitchOps and variable scope tightening #2
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
Open
MrIndeciso
wants to merge
2
commits into
vasama-work:feature/clift-transforms
Choose a base branch
from
MrIndeciso:feature/clift-beautifiers
base: feature/clift-transforms
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
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
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
165 changes: 165 additions & 0 deletions
165
lib/mlir/Dialect/Clift/Transforms/Beautify/TightenVariableScopes.cpp
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,165 @@ | ||
| // | ||
| // This file is distributed under the MIT License. See LICENSE.md for details. | ||
| // | ||
|
|
||
| #include "llvm/ADT/SmallVector.h" | ||
|
|
||
| #include "mlir/Pass/Pass.h" | ||
|
|
||
| #include "revng/mlir/Dialect/Clift/IR/CliftOpHelpers.h" | ||
| #include "revng/mlir/Dialect/Clift/Transforms/Passes.h" | ||
|
|
||
| namespace mlir { | ||
| namespace clift { | ||
| #define GEN_PASS_DEF_CLIFTTIGHTENVARIABLESCOPES | ||
| #include "revng/mlir/Dialect/Clift/Transforms/Passes.h.inc" | ||
| } // namespace clift | ||
| } // namespace mlir | ||
|
|
||
| namespace clift = mlir::clift; | ||
|
|
||
| namespace { | ||
|
|
||
| // Stores the position and nesting level for each local variable definition | ||
| struct LocalVariableLocation { | ||
| clift::BlockPosition Position; | ||
| unsigned NestingLevel = 0; | ||
| }; | ||
|
|
||
| // Custom walk function that provides nesting level | ||
| template<typename CallbackT> | ||
| static void walkWithNestingLevel(mlir::Region *Region, | ||
| CallbackT Callback, | ||
| unsigned NestingLevel = 0) { | ||
| for (mlir::Block &Block : *Region) { | ||
| for (mlir::Operation &Op : Block) { | ||
| // Post-order walk through the nested regions, incrementing the nesting | ||
| // level for inner region | ||
| for (mlir::Region &NestedRegion : Op.getRegions()) { | ||
| walkWithNestingLevel(&NestedRegion, Callback, NestingLevel + 1); | ||
| } | ||
|
|
||
| // Then call the callback on the operation itself | ||
| Callback(&Op, NestingLevel); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Merges the current location for a variable definition with the new use | ||
| // position | ||
| static void updateVariableLocation(LocalVariableLocation &VarLoc, | ||
| clift::BlockPosition NewPosition, | ||
| unsigned NewLevel) { | ||
| mlir::Region *CurrentRegion = VarLoc.Position.Block->getParent(); | ||
| mlir::Region *NewPosRegion = NewPosition.Block->getParent(); | ||
|
|
||
| unsigned CurrentLevel = VarLoc.NestingLevel; | ||
|
|
||
| // If the new position is at a lower nesting level, we surely need to | ||
| // go up from the current region at least until the levels are equal, | ||
| // in order to find the common ancestor | ||
| while (CurrentLevel > NewLevel) { | ||
| CurrentRegion = CurrentRegion->getParentRegion(); | ||
| CurrentLevel--; | ||
| } | ||
|
|
||
| // Conversely, if the new position is at a higher nesting level, | ||
| // we need to go up from it until we reach the current region's level | ||
| while (NewLevel > CurrentLevel) { | ||
| NewPosRegion = NewPosRegion->getParentRegion(); | ||
| NewLevel--; | ||
| } | ||
|
|
||
| // We can now check if the current region is identical to the | ||
| // parent region of the new position | ||
| while (CurrentRegion != NewPosRegion) { | ||
| // If they are not the same, we need to go up from both of them | ||
| // until we reach the common ancestor region | ||
| CurrentRegion = CurrentRegion->getParentRegion(); | ||
| NewPosRegion = NewPosRegion->getParentRegion(); | ||
| CurrentLevel--; | ||
| } | ||
|
|
||
| // Now we can find the common ancestor operation that is the closest to the | ||
| // current variable location by walking up the tree until we match the | ||
| // nesting level | ||
| mlir::Operation *CurrentOp = VarLoc.Position.getOperation(); | ||
| for (unsigned i = 0; i < (VarLoc.NestingLevel - CurrentLevel); ++i) { | ||
| CurrentOp = CurrentOp->getParentOp(); | ||
| } | ||
|
|
||
| // Update the variable location to the newfound common ancestor | ||
| VarLoc.Position = clift::BlockPosition::get(CurrentOp); | ||
| VarLoc.NestingLevel = CurrentLevel; | ||
| } | ||
|
|
||
| struct TightenVariableScopePass | ||
| : clift::impl::CliftTightenVariableScopesBase<TightenVariableScopePass> { | ||
| void runOnOperation() override { | ||
| clift::FunctionOp FunctionOp = getOperation(); | ||
|
|
||
| // Store the function's local variables in a map, associated with their | ||
| // optimal position in the MLIR tree | ||
| llvm::SmallDenseMap<clift::LocalVariableOp, LocalVariableLocation> | ||
| LocalVariables; | ||
|
|
||
| auto WalkCallback = [&](mlir::Operation *Op, unsigned OpNestingLevel) { | ||
| // For each operand of the operation, we check if it is a local | ||
| // variable. | ||
| for (mlir::Value Operand : Op->getOperands()) { | ||
| auto LocalVarOp = Operand.getDefiningOp<clift::LocalVariableOp>(); | ||
|
|
||
| // If the defining op is not a local variable, we can skip it | ||
| if (not LocalVarOp) { | ||
| continue; | ||
| } | ||
|
|
||
| // Local variables with an initializer cannot be moved | ||
| if (not LocalVarOp.getInitializer().empty()) { | ||
| continue; | ||
| } | ||
|
|
||
| auto [Iterator, Inserted] = LocalVariables.try_emplace(LocalVarOp); | ||
|
|
||
| if (Inserted) { | ||
| // If the local variable was not already in the map, mark | ||
| // its optimal position as right before the parent operation | ||
| // of the current user. | ||
| Iterator->second | ||
| .Position = clift::BlockPosition::get(Op->getParentOp()); | ||
| Iterator->second.NestingLevel = OpNestingLevel - 1; | ||
| } else { | ||
| // If the local variable is already in the map, update its | ||
| // location to find the common ancestor position that covers | ||
| // the new user | ||
| auto OpPosition = clift::BlockPosition::get(Op); | ||
| updateVariableLocation(Iterator->second, OpPosition, OpNestingLevel); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| // Walk the function body to identify local variable uses, along with their | ||
| // nesting levels. | ||
| walkWithNestingLevel(&FunctionOp.getBody(), WalkCallback); | ||
|
|
||
| // Move each local variable to its optimal position | ||
| for (const auto &[LocalVarOp, Location] : LocalVariables) { | ||
| // Get the target operation where we want to insert the variable | ||
| mlir::Operation *TargetOp = Location.Position.getOperation(); | ||
|
|
||
| // Check if moving would be a no-op (already in the right place) | ||
| if (TargetOp->getPrevNode() == LocalVarOp) { | ||
| continue; | ||
| } | ||
|
|
||
| // Move the local variable declaration to the optimal position | ||
| LocalVarOp->moveBefore(TargetOp); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| } // namespace | ||
|
|
||
| clift::PassPtr<clift::FunctionOp> clift::createTightenVariableScopePass() { | ||
| return std::make_unique<TightenVariableScopePass>(); | ||
| } | ||
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.
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.
Uh oh!
There was an error while loading. Please reload this page.