-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CIR] Split cir-simplify into two passes
This patch splits the old cir-simplify pass into two new passes, namely cir-canonicalize and cir-simplify (the new cir-simplify). The cir-canonicalize pass runs transformations that do not affect CIR-to-source fidelity much, such as operation folding and redundant operation elimination. On the other hand, the new cir-simplify pass runs transformations that may significantly change the code and break high-level code analysis passes, such as more aggresive code optimizations. This patch also updates the CIR-to-CIR pipeline to fit these two new passes. The cir-canonicalize pass is moved to the very front of the pipeline, while the new cir-simplify pass is moved to the back of the pipeline (but still before lowering prepare of course). Additionally, the new cir-simplify now only runs when the user specifies a non-zero optimization level on the frontend.
- Loading branch information
Showing
20 changed files
with
250 additions
and
173 deletions.
There are no files selected for viewing
This file contains 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 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 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 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 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,187 @@ | ||
//===- CIRSimplify.cpp - performs CIR canonicalization --------------------===// | ||
// | ||
// 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 "PassDetail.h" | ||
#include "mlir/Dialect/Func/IR/FuncOps.h" | ||
#include "mlir/IR/Block.h" | ||
#include "mlir/IR/Operation.h" | ||
#include "mlir/IR/PatternMatch.h" | ||
#include "mlir/IR/Region.h" | ||
#include "mlir/Support/LogicalResult.h" | ||
#include "mlir/Transforms/GreedyPatternRewriteDriver.h" | ||
#include "clang/CIR/Dialect/IR/CIRDialect.h" | ||
#include "clang/CIR/Dialect/Passes.h" | ||
|
||
using namespace mlir; | ||
using namespace cir; | ||
|
||
namespace { | ||
|
||
/// Removes branches between two blocks if it is the only branch. | ||
/// | ||
/// From: | ||
/// ^bb0: | ||
/// cir.br ^bb1 | ||
/// ^bb1: // pred: ^bb0 | ||
/// cir.return | ||
/// | ||
/// To: | ||
/// ^bb0: | ||
/// cir.return | ||
struct RemoveRedundantBranches : public OpRewritePattern<BrOp> { | ||
using OpRewritePattern<BrOp>::OpRewritePattern; | ||
|
||
LogicalResult matchAndRewrite(BrOp op, | ||
PatternRewriter &rewriter) const final { | ||
Block *block = op.getOperation()->getBlock(); | ||
Block *dest = op.getDest(); | ||
|
||
if (isa<mlir::cir::LabelOp>(dest->front())) | ||
return failure(); | ||
|
||
// Single edge between blocks: merge it. | ||
if (block->getNumSuccessors() == 1 && | ||
dest->getSinglePredecessor() == block) { | ||
rewriter.eraseOp(op); | ||
rewriter.mergeBlocks(dest, block); | ||
return success(); | ||
} | ||
|
||
return failure(); | ||
} | ||
}; | ||
|
||
struct RemoveEmptyScope : public OpRewritePattern<ScopeOp> { | ||
using OpRewritePattern<ScopeOp>::OpRewritePattern; | ||
|
||
LogicalResult match(ScopeOp op) const final { | ||
return success(op.getRegion().empty() || | ||
(op.getRegion().getBlocks().size() == 1 && | ||
op.getRegion().front().empty())); | ||
} | ||
|
||
void rewrite(ScopeOp op, PatternRewriter &rewriter) const final { | ||
rewriter.eraseOp(op); | ||
} | ||
}; | ||
|
||
struct RemoveEmptySwitch : public OpRewritePattern<SwitchOp> { | ||
using OpRewritePattern<SwitchOp>::OpRewritePattern; | ||
|
||
LogicalResult match(SwitchOp op) const final { | ||
return success(op.getRegions().empty()); | ||
} | ||
|
||
void rewrite(SwitchOp op, PatternRewriter &rewriter) const final { | ||
rewriter.eraseOp(op); | ||
} | ||
}; | ||
|
||
struct RemoveTrivialTry : public OpRewritePattern<TryOp> { | ||
using OpRewritePattern<TryOp>::OpRewritePattern; | ||
|
||
LogicalResult match(TryOp op) const final { | ||
// FIXME: also check all catch regions are empty | ||
// return success(op.getTryRegion().hasOneBlock()); | ||
return mlir::failure(); | ||
} | ||
|
||
void rewrite(TryOp op, PatternRewriter &rewriter) const final { | ||
// Move try body to the parent. | ||
assert(op.getTryRegion().hasOneBlock()); | ||
|
||
Block *parentBlock = op.getOperation()->getBlock(); | ||
mlir::Block *tryBody = &op.getTryRegion().getBlocks().front(); | ||
YieldOp y = dyn_cast<YieldOp>(tryBody->getTerminator()); | ||
assert(y && "expected well wrapped up try block"); | ||
y->erase(); | ||
|
||
rewriter.inlineBlockBefore(tryBody, parentBlock, Block::iterator(op)); | ||
rewriter.eraseOp(op); | ||
} | ||
}; | ||
|
||
// Remove call exception with empty cleanups | ||
struct SimplifyCallOp : public OpRewritePattern<CallOp> { | ||
using OpRewritePattern<CallOp>::OpRewritePattern; | ||
|
||
LogicalResult match(CallOp op) const final { | ||
// Applicable to cir.call exception ... clean { cir.yield } | ||
mlir::Region *r = &op.getCleanup(); | ||
if (r->empty() || !r->hasOneBlock()) | ||
return failure(); | ||
|
||
mlir::Block *b = &r->getBlocks().back(); | ||
if (&b->back() != &b->front()) | ||
return failure(); | ||
|
||
return success(isa<YieldOp>(&b->getOperations().back())); | ||
} | ||
|
||
void rewrite(CallOp op, PatternRewriter &rewriter) const final { | ||
mlir::Block *b = &op.getCleanup().back(); | ||
rewriter.eraseOp(&b->back()); | ||
rewriter.eraseBlock(b); | ||
} | ||
}; | ||
|
||
//===----------------------------------------------------------------------===// | ||
// CIRCanonicalizePass | ||
//===----------------------------------------------------------------------===// | ||
|
||
struct CIRCanonicalizePass : public CIRCanonicalizeBase<CIRCanonicalizePass> { | ||
using CIRCanonicalizeBase::CIRCanonicalizeBase; | ||
|
||
// The same operation rewriting done here could have been performed | ||
// by CanonicalizerPass (adding hasCanonicalizer for target Ops and | ||
// implementing the same from above in CIRDialects.cpp). However, it's | ||
// currently too aggressive for static analysis purposes, since it might | ||
// remove things where a diagnostic can be generated. | ||
// | ||
// FIXME: perhaps we can add one more mode to GreedyRewriteConfig to | ||
// disable this behavior. | ||
void runOnOperation() override; | ||
}; | ||
|
||
void populateCIRCanonicalizePatterns(RewritePatternSet &patterns) { | ||
// clang-format off | ||
patterns.add< | ||
RemoveRedundantBranches, | ||
RemoveEmptyScope, | ||
RemoveEmptySwitch, | ||
RemoveTrivialTry, | ||
SimplifyCallOp | ||
>(patterns.getContext()); | ||
// clang-format on | ||
} | ||
|
||
void CIRCanonicalizePass::runOnOperation() { | ||
// Collect rewrite patterns. | ||
RewritePatternSet patterns(&getContext()); | ||
populateCIRCanonicalizePatterns(patterns); | ||
|
||
// Collect operations to apply patterns. | ||
SmallVector<Operation *, 16> ops; | ||
getOperation()->walk([&](Operation *op) { | ||
// CastOp here is to perform a manual `fold` in | ||
// applyOpPatternsAndFold | ||
if (isa<BrOp, BrCondOp, ScopeOp, SwitchOp, CastOp, TryOp, UnaryOp, SelectOp, | ||
ComplexCreateOp, ComplexRealOp, ComplexImagOp, CallOp>(op)) | ||
ops.push_back(op); | ||
}); | ||
|
||
// Apply patterns. | ||
if (applyOpPatternsAndFold(ops, std::move(patterns)).failed()) | ||
signalPassFailure(); | ||
} | ||
|
||
} // namespace | ||
|
||
std::unique_ptr<Pass> mlir::createCIRCanonicalizePass() { | ||
return std::make_unique<CIRCanonicalizePass>(); | ||
} |
Oops, something went wrong.