2018-12-04 01:45:35 +08:00
|
|
|
//===- CSE.cpp - Common Sub-expression Elimination ------------------------===//
|
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
|
|
// This transformation pass performs a simple common sub-expression elimination
|
|
|
|
// algorithm on operations within a function.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/Analysis/Dominance.h"
|
|
|
|
#include "mlir/IR/Attributes.h"
|
|
|
|
#include "mlir/IR/Builders.h"
|
2018-12-28 03:07:34 +08:00
|
|
|
#include "mlir/IR/Function.h"
|
2019-02-20 09:17:46 +08:00
|
|
|
#include "mlir/Pass/Pass.h"
|
2018-12-04 01:45:35 +08:00
|
|
|
#include "mlir/Support/Functional.h"
|
|
|
|
#include "mlir/Transforms/Passes.h"
|
|
|
|
#include "mlir/Transforms/Utils.h"
|
|
|
|
#include "llvm/ADT/DenseMapInfo.h"
|
|
|
|
#include "llvm/ADT/Hashing.h"
|
|
|
|
#include "llvm/ADT/ScopedHashTable.h"
|
|
|
|
#include "llvm/Support/Allocator.h"
|
|
|
|
#include "llvm/Support/RecyclingAllocator.h"
|
|
|
|
#include <deque>
|
|
|
|
using namespace mlir;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// TODO(riverriddle) Handle commutative operations.
|
2019-02-05 02:38:47 +08:00
|
|
|
struct SimpleOperationInfo : public llvm::DenseMapInfo<Instruction *> {
|
2019-03-24 06:09:06 +08:00
|
|
|
static unsigned getHashValue(const Instruction *opC) {
|
|
|
|
auto *op = const_cast<Instruction *>(opC);
|
2018-12-04 01:45:35 +08:00
|
|
|
// Hash the operations based upon their:
|
2019-02-05 02:38:47 +08:00
|
|
|
// - Instruction Name
|
2018-12-04 01:45:35 +08:00
|
|
|
// - Attributes
|
|
|
|
// - Result Types
|
|
|
|
// - Operands
|
|
|
|
return hash_combine(
|
|
|
|
op->getName(), op->getAttrs(),
|
|
|
|
hash_combine_range(op->result_type_begin(), op->result_type_end()),
|
|
|
|
hash_combine_range(op->operand_begin(), op->operand_end()));
|
|
|
|
}
|
2019-03-24 06:09:06 +08:00
|
|
|
static bool isEqual(const Instruction *lhsC, const Instruction *rhsC) {
|
|
|
|
auto *lhs = const_cast<Instruction *>(lhsC);
|
|
|
|
auto *rhs = const_cast<Instruction *>(rhsC);
|
2018-12-04 01:45:35 +08:00
|
|
|
if (lhs == rhs)
|
|
|
|
return true;
|
|
|
|
if (lhs == getTombstoneKey() || lhs == getEmptyKey() ||
|
|
|
|
rhs == getTombstoneKey() || rhs == getEmptyKey())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Compare the operation name.
|
|
|
|
if (lhs->getName() != rhs->getName())
|
|
|
|
return false;
|
|
|
|
// Check operand and result type counts.
|
|
|
|
if (lhs->getNumOperands() != rhs->getNumOperands() ||
|
|
|
|
lhs->getNumResults() != rhs->getNumResults())
|
|
|
|
return false;
|
|
|
|
// Compare attributes.
|
|
|
|
if (lhs->getAttrs() != rhs->getAttrs())
|
|
|
|
return false;
|
|
|
|
// Compare operands.
|
|
|
|
if (!std::equal(lhs->operand_begin(), lhs->operand_end(),
|
|
|
|
rhs->operand_begin()))
|
|
|
|
return false;
|
|
|
|
// Compare result types.
|
|
|
|
return std::equal(lhs->result_type_begin(), lhs->result_type_end(),
|
|
|
|
rhs->result_type_begin());
|
|
|
|
}
|
|
|
|
};
|
2018-12-31 15:23:57 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// Simple common sub-expression elimination.
|
2019-02-28 02:59:29 +08:00
|
|
|
struct CSE : public FunctionPass<CSE> {
|
2019-03-27 12:15:54 +08:00
|
|
|
CSE() = default;
|
|
|
|
CSE(const CSE &) {}
|
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
/// Shared implementation of operation elimination and scoped map definitions.
|
2018-12-04 01:45:35 +08:00
|
|
|
using AllocatorTy = llvm::RecyclingAllocator<
|
|
|
|
llvm::BumpPtrAllocator,
|
2019-02-05 02:38:47 +08:00
|
|
|
llvm::ScopedHashTableVal<Instruction *, Instruction *>>;
|
|
|
|
using ScopedMapTy = llvm::ScopedHashTable<Instruction *, Instruction *,
|
2018-12-04 01:45:35 +08:00
|
|
|
SimpleOperationInfo, AllocatorTy>;
|
|
|
|
|
|
|
|
/// Represents a single entry in the depth first traversal of a CFG.
|
|
|
|
struct CFGStackNode {
|
|
|
|
CFGStackNode(ScopedMapTy &knownValues, DominanceInfoNode *node)
|
|
|
|
: scope(knownValues), node(node), childIterator(node->begin()),
|
|
|
|
processed(false) {}
|
|
|
|
|
|
|
|
/// Scope for the known values.
|
|
|
|
ScopedMapTy::ScopeTy scope;
|
|
|
|
|
|
|
|
DominanceInfoNode *node;
|
|
|
|
DominanceInfoNode::iterator childIterator;
|
|
|
|
|
|
|
|
/// If this node has been fully processed yet or not.
|
|
|
|
bool processed;
|
|
|
|
};
|
|
|
|
|
2019-01-26 04:48:25 +08:00
|
|
|
/// Attempt to eliminate a redundant operation. Returns true if the operation
|
|
|
|
/// was marked for removal, false otherwise.
|
2019-02-05 02:38:47 +08:00
|
|
|
bool simplifyOperation(Instruction *op);
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2019-02-27 02:07:27 +08:00
|
|
|
void simplifyBlock(DominanceInfo &domInfo, Block *bb);
|
2019-03-15 01:38:44 +08:00
|
|
|
void simplifyRegion(DominanceInfo &domInfo, Region ®ion);
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2019-03-01 06:50:42 +08:00
|
|
|
void runOnFunction() override;
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
private:
|
|
|
|
/// A scoped hash table of defining operations within a function.
|
|
|
|
ScopedMapTy knownValues;
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
/// Operations marked as dead and to be erased.
|
2019-02-05 02:38:47 +08:00
|
|
|
std::vector<Instruction *> opsToErase;
|
2018-12-31 15:23:57 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
/// Attempt to eliminate a redundant operation.
|
2019-02-05 02:38:47 +08:00
|
|
|
bool CSE::simplifyOperation(Instruction *op) {
|
2019-03-07 03:04:22 +08:00
|
|
|
// Don't simplify operations with nested blocks. We don't currently model
|
|
|
|
// equality comparisons correctly among other things. It is also unclear
|
|
|
|
// whether we would want to CSE such operations.
|
2019-03-15 01:38:44 +08:00
|
|
|
if (op->getNumRegions() != 0)
|
2019-03-07 03:04:22 +08:00
|
|
|
return false;
|
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
// TODO(riverriddle) We currently only eliminate non side-effecting
|
|
|
|
// operations.
|
|
|
|
if (!op->hasNoSideEffect())
|
2019-01-26 04:48:25 +08:00
|
|
|
return false;
|
2018-12-31 15:23:57 +08:00
|
|
|
|
|
|
|
// If the operation is already trivially dead just add it to the erase list.
|
|
|
|
if (op->use_empty()) {
|
|
|
|
opsToErase.push_back(op);
|
2019-01-26 04:48:25 +08:00
|
|
|
return true;
|
2018-12-04 01:45:35 +08:00
|
|
|
}
|
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
// Look for an existing definition for the operation.
|
|
|
|
if (auto *existing = knownValues.lookup(op)) {
|
|
|
|
// If we find one then replace all uses of the current operation with the
|
|
|
|
// existing one and mark it for deletion.
|
|
|
|
for (unsigned i = 0, e = existing->getNumResults(); i != e; ++i)
|
|
|
|
op->getResult(i)->replaceAllUsesWith(existing->getResult(i));
|
|
|
|
opsToErase.push_back(op);
|
|
|
|
|
|
|
|
// If the existing operation has an unknown location and the current
|
|
|
|
// operation doesn't, then set the existing op's location to that of the
|
|
|
|
// current op.
|
|
|
|
if (existing->getLoc().isa<UnknownLoc>() &&
|
|
|
|
!op->getLoc().isa<UnknownLoc>()) {
|
|
|
|
existing->setLoc(op->getLoc());
|
|
|
|
}
|
2019-01-26 04:48:25 +08:00
|
|
|
return true;
|
2018-12-31 15:23:57 +08:00
|
|
|
}
|
2019-01-26 04:48:25 +08:00
|
|
|
|
|
|
|
// Otherwise, we add this operation to the known values map.
|
|
|
|
knownValues.insert(op, op);
|
|
|
|
return false;
|
2018-12-31 15:23:57 +08:00
|
|
|
}
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2019-02-27 02:07:27 +08:00
|
|
|
void CSE::simplifyBlock(DominanceInfo &domInfo, Block *bb) {
|
2018-12-31 15:23:57 +08:00
|
|
|
for (auto &i : *bb) {
|
2019-03-15 01:38:44 +08:00
|
|
|
// If the operation is simplified, we don't process any held regions.
|
2019-02-05 02:38:47 +08:00
|
|
|
if (simplifyOperation(&i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Simplify any held blocks.
|
2019-03-15 01:38:44 +08:00
|
|
|
for (auto ®ion : i.getRegions())
|
|
|
|
simplifyRegion(domInfo, region);
|
2018-12-31 15:23:57 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-27 02:07:27 +08:00
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
void CSE::simplifyRegion(DominanceInfo &domInfo, Region ®ion) {
|
|
|
|
// If the region is empty there is nothing to do.
|
|
|
|
if (region.empty())
|
2019-02-27 02:07:27 +08:00
|
|
|
return;
|
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
// If the region only contains one block, then simplify it directly.
|
|
|
|
if (std::next(region.begin()) == region.end()) {
|
2019-02-27 02:07:27 +08:00
|
|
|
ScopedMapTy::ScopeTy scope(knownValues);
|
2019-03-15 01:38:44 +08:00
|
|
|
simplifyBlock(domInfo, ®ion.front());
|
2019-02-27 02:07:27 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
// Note, deque is being used here because there was significant performance
|
|
|
|
// gains over vector when the container becomes very large due to the
|
|
|
|
// specific access patterns. If/when these performance issues are no
|
|
|
|
// longer a problem we can change this to vector. For more information see
|
|
|
|
// the llvm mailing list discussion on this:
|
|
|
|
// http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120116/135228.html
|
|
|
|
std::deque<std::unique_ptr<CFGStackNode>> stack;
|
|
|
|
|
2019-03-15 01:38:44 +08:00
|
|
|
// Process the nodes of the dom tree for this region.
|
2019-03-19 23:45:06 +08:00
|
|
|
stack.emplace_back(llvm::make_unique<CFGStackNode>(
|
2019-03-15 01:38:44 +08:00
|
|
|
knownValues, domInfo.getRootNode(®ion)));
|
2018-12-31 15:23:57 +08:00
|
|
|
|
|
|
|
while (!stack.empty()) {
|
|
|
|
auto ¤tNode = stack.back();
|
|
|
|
|
|
|
|
// Check to see if we need to process this node.
|
|
|
|
if (!currentNode->processed) {
|
|
|
|
currentNode->processed = true;
|
2019-02-27 02:07:27 +08:00
|
|
|
simplifyBlock(domInfo, currentNode->node->getBlock());
|
2018-12-31 15:23:57 +08:00
|
|
|
}
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
// Otherwise, check to see if we need to process a child node.
|
|
|
|
if (currentNode->childIterator != currentNode->node->end()) {
|
|
|
|
auto *childNode = *(currentNode->childIterator++);
|
|
|
|
stack.emplace_back(
|
2019-03-19 23:45:06 +08:00
|
|
|
llvm::make_unique<CFGStackNode>(knownValues, childNode));
|
2018-12-31 15:23:57 +08:00
|
|
|
} else {
|
|
|
|
// Finally, if the node and all of its children have been processed
|
|
|
|
// then we delete the node.
|
|
|
|
stack.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2019-02-27 02:07:27 +08:00
|
|
|
}
|
|
|
|
|
2019-03-01 06:50:42 +08:00
|
|
|
void CSE::runOnFunction() {
|
2019-03-26 09:02:49 +08:00
|
|
|
simplifyRegion(getAnalysis<DominanceInfo>(), getFunction().getBody());
|
Implement the initial AnalysisManagement infrastructure, with the introduction of the FunctionAnalysisManager and ModuleAnalysisManager classes. These classes provide analysis computation, caching, and invalidation for a specific IR unit. The invalidation is currently limited to either all or none, i.e. you cannot yet preserve specific analyses.
An analysis can be any class, but it must provide the following:
* A constructor for a given IR unit.
struct MyAnalysis {
// Compute this analysis with the provided module.
MyAnalysis(Module *module);
};
Analyses can be accessed from a Pass by calling either the 'getAnalysisResult<AnalysisT>' or 'getCachedAnalysisResult<AnalysisT>' methods. A FunctionPass may query for a cached analysis on the parent module with 'getCachedModuleAnalysisResult'. Similary, a ModulePass may query an analysis, it doesn't need to be cached, on a child function with 'getFunctionAnalysisResult'.
By default, when running a pass all cached analyses are set to be invalidated. If no transformation was performed, a pass can use the method 'markAllAnalysesPreserved' to preserve all analysis results. As noted above, preserving specific analyses is not yet supported.
PiperOrigin-RevId: 236505642
2019-03-03 13:46:58 +08:00
|
|
|
|
|
|
|
// If no operations were erased, then we mark all analyses as preserved.
|
|
|
|
if (opsToErase.empty()) {
|
|
|
|
markAllAnalysesPreserved();
|
|
|
|
return;
|
|
|
|
}
|
2018-12-04 01:45:35 +08:00
|
|
|
|
2018-12-31 15:23:57 +08:00
|
|
|
/// Erase any operations that were marked as dead during simplification.
|
|
|
|
for (auto *op : opsToErase)
|
|
|
|
op->erase();
|
|
|
|
opsToErase.clear();
|
2019-03-07 03:04:22 +08:00
|
|
|
|
|
|
|
// We currently don't remove region operations, so mark dominance as
|
|
|
|
// preserved.
|
|
|
|
markAnalysesPreserved<DominanceInfo, PostDominanceInfo>();
|
2018-12-04 01:45:35 +08:00
|
|
|
}
|
|
|
|
|
2019-02-28 02:59:29 +08:00
|
|
|
FunctionPassBase *mlir::createCSEPass() { return new CSE(); }
|
2018-12-04 01:45:35 +08:00
|
|
|
|
|
|
|
static PassRegistration<CSE>
|
|
|
|
pass("cse", "Eliminate common sub-expressions in functions");
|