CIRCT 23.0.0git
Loading...
Searching...
No Matches
ArcFolds.cpp
Go to the documentation of this file.
1//===- ArcFolds.cpp -------------------------------------------------------===//
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
11#include "mlir/IR/PatternMatch.h"
12#include "mlir/Support/LogicalResult.h"
13
14using namespace circt;
15using namespace arc;
16using namespace mlir;
17
18//===----------------------------------------------------------------------===//
19// Helpers
20//===----------------------------------------------------------------------===//
21
22static bool isAlways(Attribute attr, bool expected) {
23 if (auto enable = dyn_cast_or_null<IntegerAttr>(attr))
24 return enable.getValue().getBoolValue() == expected;
25 return false;
26}
27
28//===----------------------------------------------------------------------===//
29// StateOp
30//===----------------------------------------------------------------------===//
31
32LogicalResult StateOp::fold(FoldAdaptor adaptor,
33 SmallVectorImpl<OpFoldResult> &results) {
34
35 if (getNumResults() > 0 && !getOperation()->hasAttr("name") &&
36 !getOperation()->hasAttr("names")) {
37 bool hasExplicitInitials = !getInitials().empty();
38 bool allInitialsConstant =
39 !hasExplicitInitials ||
40 llvm::all_of(adaptor.getInitials(),
41 [&](Attribute attr) { return !!attr; });
42 if (isAlways(adaptor.getEnable(), false) && allInitialsConstant) {
43 // Fold to the explicit or implicit initial value if
44 // the state is never enabled and the initial values
45 // are compile-time constants.
46 if (hasExplicitInitials)
47 results.append(adaptor.getInitials().begin(),
48 adaptor.getInitials().end());
49 else
50 for (auto resTy : getResultTypes())
51 results.push_back(IntegerAttr::get(resTy, 0));
52 return success();
53 }
54 if (!hasExplicitInitials && isAlways(adaptor.getReset(), true)) {
55 // We assume both the implicit initial value and the
56 // implicit (synchronous) reset value to be zero.
57 for (auto resTy : getResultTypes())
58 results.push_back(IntegerAttr::get(resTy, 0));
59 return success();
60 }
61 }
62
63 // Remove operand when input is default value.
64 if (isAlways(adaptor.getReset(), false))
65 return getResetMutable().clear(), success();
66
67 // Remove operand when input is default value.
68 if (isAlways(adaptor.getEnable(), true))
69 return getEnableMutable().clear(), success();
70
71 return failure();
72}
73
74LogicalResult StateOp::canonicalize(StateOp op, PatternRewriter &rewriter) {
75 // When there are no names attached, the state is not externaly observable.
76 // When there are also no internal users, we can remove it.
77 if (op->use_empty() && !op->hasAttr("name") && !op->hasAttr("names")) {
78 rewriter.eraseOp(op);
79 return success();
80 }
81
82 return failure();
83}
84
85//===----------------------------------------------------------------------===//
86// StorageGetOp
87//===----------------------------------------------------------------------===//
88
89LogicalResult StorageGetOp::canonicalize(StorageGetOp op,
90 PatternRewriter &rewriter) {
91 if (auto pred = op.getStorage().getDefiningOp<StorageGetOp>()) {
92 rewriter.modifyOpInPlace(op, [&] {
93 op.getStorageMutable().assign(pred.getStorage());
94 op.setOffset(op.getOffset() + pred.getOffset());
95 });
96 return success();
97 }
98 return failure();
99}
100
101//===----------------------------------------------------------------------===//
102// ClockDomainOp
103//===----------------------------------------------------------------------===//
104
105static bool removeUnusedClockDomainInputs(ClockDomainOp op,
106 PatternRewriter &rewriter) {
107 BitVector toDelete(op.getBodyBlock().getNumArguments());
108 for (auto arg : llvm::reverse(op.getBodyBlock().getArguments())) {
109 if (arg.use_empty()) {
110 auto i = arg.getArgNumber();
111 toDelete.set(i);
112 rewriter.modifyOpInPlace(op, [&] { op.getInputsMutable().erase(i); });
113 }
114 }
115 if (toDelete.any()) {
116 rewriter.modifyOpInPlace(
117 op, [&] { op.getBodyBlock().eraseArguments(toDelete); });
118 return true;
119 }
120 return false;
121}
122
123static bool removeUnusedClockDomainOutputs(ClockDomainOp op,
124 PatternRewriter &rewriter) {
125 SmallVector<Type> resultTypes;
126 for (auto res : llvm::reverse(op->getResults())) {
127 if (res.use_empty()) {
128 auto *terminator = op.getBodyBlock().getTerminator();
129 rewriter.modifyOpInPlace(
130 terminator, [&] { terminator->eraseOperand(res.getResultNumber()); });
131 } else {
132 resultTypes.push_back(res.getType());
133 }
134 }
135
136 // Nothing is changed.
137 if (resultTypes.size() == op->getNumResults())
138 return false;
139
140 rewriter.setInsertionPoint(op);
141
142 auto newDomain = ClockDomainOp::create(rewriter, op.getLoc(), resultTypes,
143 op.getInputs(), op.getClock());
144 rewriter.inlineRegionBefore(op.getBody(), newDomain.getBody(),
145 newDomain->getRegion(0).begin());
146
147 unsigned currIdx = 0;
148 for (auto result : op.getOutputs()) {
149 if (!result.use_empty())
150 rewriter.replaceAllUsesWith(result, newDomain->getResult(currIdx++));
151 }
152
153 rewriter.eraseOp(op);
154 return true;
155}
156
157LogicalResult ClockDomainOp::canonicalize(ClockDomainOp op,
158 PatternRewriter &rewriter) {
159 rewriter.setInsertionPointToStart(&op.getBodyBlock());
160
161 // Canonicalize inputs
162 DenseMap<Value, unsigned> seenArgs;
163 for (auto arg :
164 llvm::make_early_inc_range(op.getBodyBlock().getArguments())) {
165 auto i = arg.getArgNumber();
166 auto inputVal = op.getInputs()[i];
167
168 if (arg.use_empty())
169 continue;
170
171 // Remove duplicate inputs
172 if (seenArgs.count(inputVal)) {
173 rewriter.replaceAllUsesWith(
174 arg, op.getBodyBlock().getArgument(seenArgs[inputVal]));
175 continue;
176 }
177
178 // Pull in memories that are only used in this clock domain and clone
179 // constants into the clock domain.
180 if (auto *inputOp = inputVal.getDefiningOp()) {
181 bool isConstant = inputOp->hasTrait<OpTrait::ConstantLike>();
182 bool hasOneUse = inputVal.hasOneUse();
183 if (isConstant || (isa<MemoryOp>(inputOp) && hasOneUse)) {
184 auto resultNumber = cast<OpResult>(inputVal).getResultNumber();
185 auto *clone = rewriter.clone(*inputOp);
186 rewriter.replaceAllUsesWith(arg, clone->getResult(resultNumber));
187 if (hasOneUse && inputOp->getNumResults() == 1) {
188 inputVal.dropAllUses();
189 rewriter.eraseOp(inputOp);
190 }
191 continue;
192 }
193 }
194
195 seenArgs[op.getInputs()[i]] = i;
196 }
197
198 auto didCanonicalizeInput = removeUnusedClockDomainInputs(op, rewriter);
199
200 // Canonicalize outputs
201 for (auto [result, terminatorOperand] : llvm::zip(
202 op.getOutputs(), op.getBodyBlock().getTerminator()->getOperands())) {
203 // Replace results which are just passed-through inputs with the input
204 // directly. This makes this result unused and is thus removed later on.
205 if (isa<BlockArgument>(terminatorOperand))
206 rewriter.replaceAllUsesWith(
207 result, op.getInputs()[cast<BlockArgument>(terminatorOperand)
208 .getArgNumber()]);
209
210 // Outputs that are just constant operations can be replaced by a clone of
211 // the constant outside of the clock domain. This makes the result unused
212 // and is thus removed later on.
213 // TODO: we could also push out all operations that are not clocked/don't
214 // have side-effects. If there are long chains of such operations this can
215 // lead to long canonicalizer runtimes though, so we need to be careful here
216 // and maybe do it as a separate pass (or make sure that such chains are
217 // never pulled into the clock domain in the first place).
218 if (auto *defOp = terminatorOperand.getDefiningOp();
219 defOp && defOp->hasTrait<OpTrait::ConstantLike>() &&
220 !result.use_empty()) {
221 rewriter.setInsertionPointAfter(op);
222 unsigned resultIdx = cast<OpResult>(terminatorOperand).getResultNumber();
223 auto *clone = rewriter.clone(*defOp);
224 if (defOp->hasOneUse()) {
225 defOp->dropAllUses();
226 rewriter.eraseOp(defOp);
227 }
228 rewriter.replaceAllUsesWith(result, clone->getResult(resultIdx));
229 }
230 }
231
232 auto didCanoncalizeOutput = removeUnusedClockDomainOutputs(op, rewriter);
233
234 return success(didCanonicalizeInput || didCanoncalizeOutput);
235}
static bool isAlways(Attribute attr, bool expected)
Definition ArcFolds.cpp:22
static bool removeUnusedClockDomainInputs(ClockDomainOp op, PatternRewriter &rewriter)
Definition ArcFolds.cpp:105
static bool removeUnusedClockDomainOutputs(ClockDomainOp op, PatternRewriter &rewriter)
Definition ArcFolds.cpp:123
static Block * getBodyBlock(FModuleLike mod)
Definition arc.py:1
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.