CIRCT  20.0.0git
HWCleanup.cpp
Go to the documentation of this file.
1 //===- HWCleanup.cpp - HW Cleanup Pass ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This transformation pass performs various cleanups and canonicalization
10 // transformations for hw.module bodies. This is intended to be used early in
11 // the HW/SV pipeline to expose optimization opportunities that require global
12 // analysis.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Dialect/SV/SVOps.h"
20 #include "mlir/Pass/Pass.h"
21 
22 namespace circt {
23 namespace sv {
24 #define GEN_PASS_DEF_HWCLEANUP
25 #include "circt/Dialect/SV/SVPasses.h.inc"
26 } // namespace sv
27 } // namespace circt
28 
29 using namespace circt;
30 //===----------------------------------------------------------------------===//
31 // Helper utilities
32 //===----------------------------------------------------------------------===//
33 
34 namespace {
35 
36 /// Check the equivalence of operations by doing a deep comparison of operands
37 /// and attributes, but does not compare the content of any regions attached to
38 /// each op.
39 struct AlwaysLikeOpInfo : public llvm::DenseMapInfo<Operation *> {
40  static unsigned getHashValue(const Operation *opC) {
41  return mlir::OperationEquivalence::computeHash(
42  const_cast<Operation *>(opC),
43  /*hashOperands=*/mlir::OperationEquivalence::directHashValue,
44  /*hashResults=*/mlir::OperationEquivalence::ignoreHashValue,
45  mlir::OperationEquivalence::IgnoreLocations);
46  }
47  static bool isEqual(const Operation *lhsC, const Operation *rhsC) {
48  auto *lhs = const_cast<Operation *>(lhsC);
49  auto *rhs = const_cast<Operation *>(rhsC);
50  // Trivially the same.
51  if (lhs == rhs)
52  return true;
53  // Filter out tombstones and empty ops.
54  if (lhs == getTombstoneKey() || lhs == getEmptyKey() ||
55  rhs == getTombstoneKey() || rhs == getEmptyKey())
56  return false;
57  // Compare attributes.
58  if (lhs->getName() != rhs->getName() ||
59  lhs->getAttrDictionary() != rhs->getAttrDictionary() ||
60  lhs->getNumOperands() != rhs->getNumOperands())
61  return false;
62  // Compare operands.
63  for (auto operandPair : llvm::zip(lhs->getOperands(), rhs->getOperands())) {
64  Value lhsOperand = std::get<0>(operandPair);
65  Value rhsOperand = std::get<1>(operandPair);
66  if (lhsOperand != rhsOperand)
67  return false;
68  }
69  // The two AlwaysOps are similar enough to be combined.
70  return true;
71  }
72 };
73 
74 } // end anonymous namespace
75 
76 // Merge two regions together. These regions must only have a one block.
77 static void mergeRegions(Region *region1, Region *region2) {
78  assert(region1->getBlocks().size() <= 1 && region2->getBlocks().size() <= 1 &&
79  "Can only merge regions with a single block");
80  if (region1->empty()) {
81  // If both regions are empty, move on to the next pair of regions
82  if (region2->empty())
83  return;
84  // If the first region has no block, move the second region's block over.
85  region1->getBlocks().splice(region1->end(), region2->getBlocks());
86  return;
87  }
88 
89  // If the second region is not empty, splice its block into the start of the
90  // first region.
91  if (!region2->empty()) {
92  auto &block1 = region1->front();
93  auto &block2 = region2->front();
94  block1.getOperations().splice(block1.begin(), block2.getOperations());
95  }
96 }
97 
98 //===----------------------------------------------------------------------===//
99 // HWCleanupPass
100 //===----------------------------------------------------------------------===//
101 
102 namespace {
103 struct HWCleanupPass : public circt::sv::impl::HWCleanupBase<HWCleanupPass> {
104  using sv::impl::HWCleanupBase<HWCleanupPass>::mergeAlwaysBlocks;
105 
106  void runOnOperation() override;
107 
108  void runOnRegionsInOp(Operation &op);
109  void runOnGraphRegion(Region &region);
110  void runOnProceduralRegion(Region &region);
111 
112 private:
113  /// Inline all regions from the second operation into the first and delete the
114  /// second operation.
115  void mergeOperationsIntoFrom(Operation *op1, Operation *op2) {
116  // If either op1 or op2 has SV attributues, we cannot merge the ops.
117  if (sv::hasSVAttributes(op1) || sv::hasSVAttributes(op2))
118  return;
119  assert(op1 != op2 && "Cannot merge an op into itself");
120  for (size_t i = 0, e = op1->getNumRegions(); i != e; ++i)
121  mergeRegions(&op1->getRegion(i), &op2->getRegion(i));
122 
123  op2->erase();
124  anythingChanged = true;
125  }
126 
127  bool anythingChanged;
128 };
129 } // end anonymous namespace
130 
131 void HWCleanupPass::runOnOperation() {
132  // Keeps track if anything changed during this pass, used to determine if
133  // the analyses were preserved.
134  anythingChanged = false;
135  runOnGraphRegion(getOperation().getBody());
136 
137  // If we did not change anything in the graph mark all analysis as
138  // preserved.
139  if (!anythingChanged)
140  markAllAnalysesPreserved();
141 }
142 
143 /// Recursively process all of the regions in the specified op, dispatching to
144 /// graph or procedural processing as appropriate.
145 void HWCleanupPass::runOnRegionsInOp(Operation &op) {
146  if (op.hasTrait<sv::ProceduralRegion>()) {
147  for (auto &region : op.getRegions())
148  runOnProceduralRegion(region);
149  } else {
150  for (auto &region : op.getRegions())
151  runOnGraphRegion(region);
152  }
153 }
154 
155 /// Run simplifications on the specified graph region.
156 void HWCleanupPass::runOnGraphRegion(Region &region) {
157  if (region.getBlocks().size() != 1)
158  return;
159  Block &body = region.front();
160 
161  // A set of operations in the current block which are mergable. Any
162  // operation in this set is a candidate for another similar operation to
163  // merge in to.
164  DenseSet<Operation *, AlwaysLikeOpInfo> alwaysFFOpsSeen;
166  sv::InitialOp initialOpSeen;
167 
168  for (Operation &op : llvm::make_early_inc_range(body)) {
169  // Merge alwaysff and always operations by hashing them to check to see if
170  // we've already encountered one. If so, merge them and reprocess the body.
171  if (isa<sv::AlwaysOp, sv::AlwaysFFOp>(op) && mergeAlwaysBlocks) {
172  // Merge identical alwaysff's together and delete the old operation.
173  auto itAndInserted = alwaysFFOpsSeen.insert(&op);
174  if (itAndInserted.second)
175  continue;
176  auto *existingAlways = *itAndInserted.first;
177  mergeOperationsIntoFrom(&op, existingAlways);
178 
179  *itAndInserted.first = &op;
180  continue;
181  }
182 
183  // Merge graph ifdefs anywhere in the module.
184  if (auto ifdefOp = dyn_cast<sv::IfDefOp>(op)) {
185  auto *&entry = ifdefOps[ifdefOp.getCondAttr()];
186  if (entry)
187  mergeOperationsIntoFrom(ifdefOp, entry);
188 
189  entry = ifdefOp;
190  continue;
191  }
192 
193  // Merge initial ops anywhere in the module.
194  if (auto initialOp = dyn_cast<sv::InitialOp>(op)) {
195  if (initialOpSeen)
196  mergeOperationsIntoFrom(initialOp, initialOpSeen);
197  initialOpSeen = initialOp;
198  continue;
199  }
200  }
201 
202  for (Operation &op : llvm::make_early_inc_range(body)) {
203  // Recursively process any regions in the op.
204  if (op.getNumRegions() != 0)
205  runOnRegionsInOp(op);
206  }
207 }
208 
209 /// Run simplifications on the specified procedural region.
210 void HWCleanupPass::runOnProceduralRegion(Region &region) {
211  if (region.getBlocks().size() != 1)
212  return;
213  Block &body = region.front();
214 
215  Operation *lastSideEffectingOp = nullptr;
216  for (Operation &op : llvm::make_early_inc_range(body)) {
217  // Merge procedural ifdefs with neighbors in the procedural region.
218  if (auto ifdef = dyn_cast<sv::IfDefProceduralOp>(op)) {
219  if (auto prevIfDef =
220  dyn_cast_or_null<sv::IfDefProceduralOp>(lastSideEffectingOp)) {
221  if (ifdef.getCond() == prevIfDef.getCond()) {
222  // We know that there are no side effective operations between the
223  // two, so merge the first one into this one.
224  mergeOperationsIntoFrom(ifdef, prevIfDef);
225  }
226  }
227  }
228 
229  // Merge 'if' operations with the same condition.
230  if (auto ifop = dyn_cast<sv::IfOp>(op)) {
231  if (auto prevIf = dyn_cast_or_null<sv::IfOp>(lastSideEffectingOp)) {
232  if (ifop.getCond() == prevIf.getCond()) {
233  // We know that there are no side effective operations between the
234  // two, so merge the first one into this one.
235  mergeOperationsIntoFrom(ifop, prevIf);
236  }
237  }
238  }
239 
240  // Keep track of the last side effecting operation we've seen.
241  if (!mlir::isMemoryEffectFree(&op))
242  lastSideEffectingOp = &op;
243  }
244 
245  for (Operation &op : llvm::make_early_inc_range(body)) {
246  // Recursively process any regions in the op.
247  if (op.getNumRegions() != 0)
248  runOnRegionsInOp(op);
249  }
250 }
251 
252 std::unique_ptr<Pass> circt::sv::createHWCleanupPass(bool mergeAlwaysBlocks) {
253  auto pass = std::make_unique<HWCleanupPass>();
254  pass->mergeAlwaysBlocks = mergeAlwaysBlocks;
255  return pass;
256 }
assert(baseType &&"element must be base type")
static void mergeRegions(Region *region1, Region *region2)
Definition: HWCleanup.cpp:77
bool hasSVAttributes(mlir::Operation *op)
Helper functions to handle SV attributes.
std::unique_ptr< mlir::Pass > createHWCleanupPass(bool mergeAlwaysBlocks=true)
Definition: HWCleanup.cpp:252
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: sv.py:1