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