CIRCT 20.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
20#include "mlir/Pass/Pass.h"
21
22namespace circt {
23namespace sv {
24#define GEN_PASS_DEF_HWCLEANUP
25#include "circt/Dialect/SV/SVPasses.h.inc"
26} // namespace sv
27} // namespace circt
28
29using namespace circt;
30//===----------------------------------------------------------------------===//
31// Helper utilities
32//===----------------------------------------------------------------------===//
33
34namespace {
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.
39struct 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.
77static 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
102namespace {
103struct 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
112private:
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
131void 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.
145void 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.
156void 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.
210void 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
252std::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
Signals that an operations regions are procedural.
Definition SVOps.h:160
std::unique_ptr< mlir::Pass > createHWCleanupPass(bool mergeAlwaysBlocks=true)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sv.py:1