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
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 Base::Base;
105 using sv::impl::HWCleanupBase<HWCleanupPass>::mergeAlwaysBlocks;
106
107 void runOnOperation() override;
108
109 void runOnRegionsInOp(Operation &op);
110 void runOnGraphRegion(Region &region);
111 void runOnProceduralRegion(Region &region);
112
113private:
114 /// Inline all regions from the second operation into the first and delete the
115 /// second operation.
116 void mergeOperationsIntoFrom(Operation *op1, Operation *op2) {
117 // If either op1 or op2 has SV attributues, we cannot merge the ops.
118 if (sv::hasSVAttributes(op1) || sv::hasSVAttributes(op2))
119 return;
120 assert(op1 != op2 && "Cannot merge an op into itself");
121 for (size_t i = 0, e = op1->getNumRegions(); i != e; ++i)
122 mergeRegions(&op1->getRegion(i), &op2->getRegion(i));
123
124 op2->erase();
125 anythingChanged = true;
126 }
127
128 bool anythingChanged;
129};
130} // end anonymous namespace
131
132void HWCleanupPass::runOnOperation() {
133 // Keeps track if anything changed during this pass, used to determine if
134 // the analyses were preserved.
135 anythingChanged = false;
136 runOnGraphRegion(getOperation().getBody());
137
138 // If we did not change anything in the graph mark all analysis as
139 // preserved.
140 if (!anythingChanged)
141 markAllAnalysesPreserved();
142}
143
144/// Recursively process all of the regions in the specified op, dispatching to
145/// graph or procedural processing as appropriate.
146void HWCleanupPass::runOnRegionsInOp(Operation &op) {
147 if (op.hasTrait<sv::ProceduralRegion>()) {
148 for (auto &region : op.getRegions())
149 runOnProceduralRegion(region);
150 } else {
151 for (auto &region : op.getRegions())
152 runOnGraphRegion(region);
153 }
154}
155
156/// Run simplifications on the specified graph region.
157void HWCleanupPass::runOnGraphRegion(Region &region) {
158 if (region.getBlocks().size() != 1)
159 return;
160 Block &body = region.front();
161
162 // A set of operations in the current block which are mergable. Any
163 // operation in this set is a candidate for another similar operation to
164 // merge in to.
165 DenseSet<Operation *, AlwaysLikeOpInfo> alwaysFFOpsSeen;
167 sv::InitialOp initialOpSeen;
168
169 for (Operation &op : llvm::make_early_inc_range(body)) {
170 // Merge alwaysff and always operations by hashing them to check to see if
171 // we've already encountered one. If so, merge them and reprocess the body.
172 if (isa<sv::AlwaysOp, sv::AlwaysFFOp>(op) && mergeAlwaysBlocks) {
173 // Merge identical alwaysff's together and delete the old operation.
174 auto itAndInserted = alwaysFFOpsSeen.insert(&op);
175 if (itAndInserted.second)
176 continue;
177 auto *existingAlways = *itAndInserted.first;
178 mergeOperationsIntoFrom(&op, existingAlways);
179
180 *itAndInserted.first = &op;
181 continue;
182 }
183
184 // Merge graph ifdefs anywhere in the module.
185 if (auto ifdefOp = dyn_cast<sv::IfDefOp>(op)) {
186 auto *&entry = ifdefOps[ifdefOp.getCondAttr()];
187 if (entry)
188 mergeOperationsIntoFrom(ifdefOp, entry);
189
190 entry = ifdefOp;
191 continue;
192 }
193
194 // Merge initial ops anywhere in the module.
195 if (auto initialOp = dyn_cast<sv::InitialOp>(op)) {
196 if (initialOpSeen)
197 mergeOperationsIntoFrom(initialOp, initialOpSeen);
198 initialOpSeen = initialOp;
199 continue;
200 }
201 }
202
203 for (Operation &op : llvm::make_early_inc_range(body)) {
204 // Recursively process any regions in the op.
205 if (op.getNumRegions() != 0)
206 runOnRegionsInOp(op);
207 }
208}
209
210/// Run simplifications on the specified procedural region.
211void HWCleanupPass::runOnProceduralRegion(Region &region) {
212 if (region.getBlocks().size() != 1)
213 return;
214 Block &body = region.front();
215
216 Operation *lastSideEffectingOp = nullptr;
217 for (Operation &op : llvm::make_early_inc_range(body)) {
218 // Merge procedural ifdefs with neighbors in the procedural region.
219 if (auto ifdef = dyn_cast<sv::IfDefProceduralOp>(op)) {
220 if (auto prevIfDef =
221 dyn_cast_or_null<sv::IfDefProceduralOp>(lastSideEffectingOp)) {
222 if (ifdef.getCond() == prevIfDef.getCond()) {
223 // We know that there are no side effective operations between the
224 // two, so merge the first one into this one.
225 mergeOperationsIntoFrom(ifdef, prevIfDef);
226 }
227 }
228 }
229
230 // Merge 'if' operations with the same condition.
231 if (auto ifop = dyn_cast<sv::IfOp>(op)) {
232 if (auto prevIf = dyn_cast_or_null<sv::IfOp>(lastSideEffectingOp)) {
233 if (ifop.getCond() == prevIf.getCond()) {
234 // We know that there are no side effective operations between the
235 // two, so merge the first one into this one.
236 mergeOperationsIntoFrom(ifop, prevIf);
237 }
238 }
239 }
240
241 // Keep track of the last side effecting operation we've seen.
242 if (!mlir::isMemoryEffectFree(&op))
243 lastSideEffectingOp = &op;
244 }
245
246 for (Operation &op : llvm::make_early_inc_range(body)) {
247 // Recursively process any regions in the op.
248 if (op.getNumRegions() != 0)
249 runOnRegionsInOp(op);
250 }
251}
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:176
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sv.py:1