21#include "mlir/Analysis/TopologicalSortUtils.h"
22#include "mlir/IR/BuiltinAttributes.h"
23#include "mlir/IR/Operation.h"
24#include "mlir/IR/Visitors.h"
25#include "mlir/Support/LLVM.h"
26#include "mlir/Transforms/RegionUtils.h"
27#include "llvm/ADT/DenseMap.h"
28#include "llvm/ADT/DenseMapInfo.h"
29#include "llvm/ADT/PointerIntPair.h"
30#include "llvm/Support/DebugLog.h"
31#include "llvm/Support/LogicalResult.h"
33#define DEBUG_TYPE "synth-structural-hash"
37#define GEN_PASS_DEF_STRUCTURALHASH
38#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
55 llvm::SmallVector<llvm::PointerIntPair<Value, 1>, 3> inps)
75 hash = llvm::hash_combine(hash, operand.getOpaqueValue());
76 return static_cast<unsigned>(hash);
88struct StructuralHashPass
89 :
public impl::StructuralHashBase<StructuralHashPass> {
90 void runOnOperation()
override;
100class StructuralHashDriver {
102 StructuralHashDriver() =
default;
103 void visitOp(Operation *op, ArrayRef<bool> inverted);
104 void visitUnaryOp(Operation *op,
bool inverted);
105 void visitVariadicOp(Operation *op, ArrayRef<bool> inverted);
106 uint64_t getNumber(Value v);
115 DenseMap<Value, uint64_t> valueNumber;
116 uint64_t constantCounter = 0;
119 DenseMap<StructuralHashKey, Operation *> hashTable;
129 DenseMap<Value, Value> inversion;
133void StructuralHashDriver::visitOp(Operation *op, ArrayRef<bool> inverted) {
137 if (op->getNumOperands() == 1) {
138 visitUnaryOp(op, inverted[0]);
141 visitVariadicOp(op, inverted);
148void StructuralHashDriver::visitUnaryOp(Operation *op,
bool inverted) {
150 op->replaceAllUsesWith(ArrayRef<Value>{op->getOperand(0)});
155 auto operand = op->getOperand(0);
156 auto it = inversion.find(operand);
157 if (it != inversion.end()) {
159 op->replaceAllUsesWith(ArrayRef<Value>{it->second});
163 inversion[op->getResult(0)] = operand;
169void StructuralHashDriver::visitVariadicOp(Operation *op,
170 ArrayRef<bool> inverted) {
174 for (
auto [input, inverted] :
llvm::zip(op->getOperands(), inverted)) {
175 bool isInverted = inverted;
177 auto it = inversion.find(input);
178 if (it != inversion.end()) {
181 isInverted = !isInverted;
184 key.operandPairs.push_back(
185 llvm::PointerIntPair<Value, 1>(input, isInverted));
188 (void)getNumber(input);
192 llvm::sort(key.operandPairs, [&](
auto a,
auto b) {
193 size_t aNum = getNumber(a.getPointer());
194 size_t bNum = getNumber(b.getPointer());
197 return a.getInt() < b.getInt();
201 auto [it, inserted] = hashTable.try_emplace(key, op);
204 op->setOperands(llvm::to_vector<3>(llvm::map_range(
205 key.operandPairs, [](
auto p) { return p.getPointer(); })));
206 SmallVector<bool, 3> newInversion(
207 llvm::map_range(key.operandPairs, [](
auto p) { return p.getInt(); }));
208 op->setAttr(
"inverted",
209 mlir::DenseBoolArrayAttr::get(op->getContext(), newInversion));
211 (void)getNumber(op->getResult(0));
213 LDBG() <<
"Structural Hash: Replacing " << *op <<
" with " << *(it->second)
218 if (name && !it->second->hasAttr(
"sv.namehint"))
219 it->second->setAttr(
"sv.namehint", name);
220 op->replaceAllUsesWith(it->second);
227uint64_t StructuralHashDriver::getNumber(Value v) {
228 auto it = valueNumber.find(v);
229 if (it != valueNumber.end())
234 if (
auto *op = v.getDefiningOp();
235 op && op->hasTrait<mlir::OpTrait::ConstantLike>()) {
236 auto [it, inserted] = valueNumber.try_emplace(
237 v, std::numeric_limits<uint64_t>::max() - constantCounter++);
241 return valueNumber.try_emplace(v, valueNumber.size() - constantCounter)
245llvm::LogicalResult StructuralHashDriver::run(
hw::HWModuleOp moduleOp) {
246 auto isOperationReady = [&](Value value, Operation *op) ->
bool {
248 return !isa<circt::synth::aig::AndInverterOp,
249 circt::synth::mig::MajorityInverterOp>(op);
252 if (!mlir::sortTopologically(moduleOp.getBodyBlock(), isOperationReady))
255 for (
auto arg : moduleOp.
getBodyBlock()->getArguments())
256 (void)getNumber(arg);
263 mlir::TypeSwitch<Operation *>(&op)
264 .Case<circt::synth::aig::AndInverterOp,
265 circt::synth::mig::MajorityInverterOp>([&](
auto invertibleOp) {
266 visitOp(invertibleOp, invertibleOp.getInverted());
268 .Default([&](Operation *op) {});
272 mlir::PatternRewriter rewriter(moduleOp.getContext());
273 (void)mlir::runRegionDCE(rewriter, moduleOp->getRegions());
274 return mlir::success();
277void StructuralHashPass::runOnOperation() {
278 auto topOp = getOperation();
279 StructuralHashDriver driver;
280 if (failed(driver.run(topOp)))
281 return signalPassFailure();
static Block * getBodyBlock(FModuleLike mod)
static llvm::hash_code hash_value(const ModulePort &port)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
StringRef chooseName(StringRef a, StringRef b)
Choose a good name for an item from two options.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
A struct that represents the key used for structural hashing.
StructuralHashKey(OperationName name, llvm::SmallVector< llvm::PointerIntPair< Value, 1 >, 3 > inps)
Constructor.
llvm::SmallVector< llvm::PointerIntPair< Value, 1 >, 3 > operandPairs
static StructuralHashKey getTombstoneKey()
static bool isEqual(const StructuralHashKey &lhs, const StructuralHashKey &rhs)
static StructuralHashKey getEmptyKey()
static unsigned getHashValue(const StructuralHashKey &key)