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