Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
InferStateProperties.cpp
Go to the documentation of this file.
1//===- InferStateProperties.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
13#include "circt/Support/LLVM.h"
14#include "mlir/Pass/Pass.h"
15
16#define DEBUG_TYPE "arc-infer-state-properties"
17
18namespace circt {
19namespace arc {
20#define GEN_PASS_DEF_INFERSTATEPROPERTIES
21#include "circt/Dialect/Arc/ArcPasses.h.inc"
22} // namespace arc
23} // namespace circt
24
25using namespace circt;
26using namespace arc;
27
28//===----------------------------------------------------------------------===//
29// Helpers
30//===----------------------------------------------------------------------===//
31
32static bool isConstZero(Value value) {
33 if (auto constOp = value.getDefiningOp<hw::ConstantOp>())
34 return constOp.getValue().isZero();
35
36 return false;
37}
38
39static bool isConstTrue(Value value) {
40 if (auto constOp = value.getDefiningOp<hw::ConstantOp>()) {
41 return constOp.getValue().getBitWidth() == 1 &&
42 constOp.getValue().isAllOnes();
43 }
44 return false;
45}
46
47//===----------------------------------------------------------------------===//
48// Reset and Enable property storages
49//===----------------------------------------------------------------------===//
50
51namespace {
52/// Contains all the information needed to pass a detected reset to the rewriter
53/// function.
54struct ResetInfo {
55 ResetInfo() = default;
56 ResetInfo(std::function<Value(OpBuilder &)> &&constructInput,
57 BlockArgument condition, bool isZeroReset)
58 : constructInput(constructInput), condition(condition),
59 isZeroReset(isZeroReset) {}
60
61 ResetInfo(Value input, BlockArgument condition, bool isZeroReset)
62 : ResetInfo([=](OpBuilder &) { return input; }, condition, isZeroReset) {}
63
64 std::function<Value(OpBuilder &)> constructInput;
65 BlockArgument condition;
66 bool isZeroReset;
67
68 operator bool() { return constructInput && condition; }
69};
70
71/// Contains all the information needed to pass a detected enable to the
72/// rewriter function.
73struct EnableInfo {
74 EnableInfo() = default;
75 EnableInfo(std::function<Value(OpBuilder &)> &&constructInput,
76 BlockArgument condition, BlockArgument selfArg, bool isDisable)
77 : constructInput(constructInput), condition(condition), selfArg(selfArg),
78 isDisable(isDisable) {}
79
80 EnableInfo(Value input, BlockArgument condition, BlockArgument selfArg,
81 bool isDisable)
82 : EnableInfo([=](OpBuilder &) { return input; }, condition, selfArg,
83 isDisable) {}
84
85 std::function<Value(OpBuilder &)> constructInput;
86 BlockArgument condition;
87 BlockArgument selfArg;
88 bool isDisable;
89
90 operator bool() { return constructInput && condition && selfArg; }
91};
92} // namespace
93
94//===----------------------------------------------------------------------===//
95// Rewriter functions
96//===----------------------------------------------------------------------===//
97
98/// Take an arc and a detected reset per output value and apply it to the arc if
99/// applicable (but does not change the state ops referring to the arc).
100static LogicalResult applyResetTransformation(arc::DefineOp arcOp,
101 ArrayRef<ResetInfo> resetInfos) {
102 auto outputOp = cast<arc::OutputOp>(arcOp.getBodyBlock().getTerminator());
103
104 assert(outputOp.getOutputs().size() == resetInfos.size() &&
105 "required to pass the same amount of resets as outputs of the arc");
106
107 for (auto info : resetInfos) {
108 if (!info)
109 return failure();
110
111 // We can only pull out the reset to the whole arc when all the output
112 // values have the same reset applied to them.
113 // TODO: split the arcs such that there is one for each reset kind, however,
114 // that requires a cost-model to not blow up binary-size too much
115 if (!resetInfos.empty() &&
116 (info.condition != resetInfos.back().condition ||
117 info.isZeroReset != resetInfos.back().isZeroReset))
118 return failure();
119
120 // TODO: arc.state operation only supports resets to zero at the moment.
121 if (!info.isZeroReset)
122 return failure();
123 }
124
125 if (resetInfos.empty())
126 return failure();
127
128 OpBuilder builder(outputOp);
129
130 for (size_t i = 0, e = outputOp.getOutputs().size(); i < e; ++i) {
131 auto *defOp = outputOp.getOperands()[i].getDefiningOp();
132 outputOp.getOperands()[i].replaceUsesWithIf(
133 resetInfos[i].constructInput(builder),
134 [](OpOperand &op) { return isa<arc::OutputOp>(op.getOwner()); });
135
136 if (defOp && defOp->getResult(0).use_empty())
137 defOp->erase();
138 }
139
140 return success();
141}
142
143/// Transform the given state operation to match the changes done to the arc in
144/// 'applyResetTransformation' without any additional checks.
145static void setResetOperandOfStateOp(arc::StateOp stateOp,
146 unsigned resetConditionIndex) {
147 Value resetCond = stateOp.getInputs()[resetConditionIndex];
148 ImplicitLocOpBuilder builder(stateOp.getLoc(), stateOp);
149
150 if (stateOp.getEnable())
151 resetCond = comb::AndOp::create(builder, stateOp.getEnable(), resetCond);
152
153 if (stateOp.getReset())
154 resetCond = comb::OrOp::create(builder, stateOp.getReset(), resetCond);
155
156 stateOp.getResetMutable().assign(resetCond);
157}
158
159/// Take an arc and a detected enable per output value and apply it to the given
160/// state if applicable (no changes required to the arc::DefineOp operation for
161/// enables).
162static LogicalResult
163applyEnableTransformation(arc::DefineOp arcOp, arc::StateOp stateOp,
164 ArrayRef<EnableInfo> enableInfos) {
165 auto outputOp = cast<arc::OutputOp>(arcOp.getBodyBlock().getTerminator());
166
167 assert(outputOp.getOutputs().size() == enableInfos.size() &&
168 "required to pass the same amount of enables as outputs of the arc");
169
170 for (auto info : enableInfos) {
171 if (!info)
172 return failure();
173
174 // We can only pull out the enable to the whole arc when all the output
175 // values have the same enable applied to them.
176 // TODO: split the arcs such that there is one for each enable kind,
177 // however, this requires a cost-model to not blow up binary-size too much.
178 if (!enableInfos.empty() &&
179 (info.condition != enableInfos.back().condition ||
180 info.isDisable != enableInfos.back().isDisable))
181 return failure();
182 }
183
184 if (enableInfos.empty())
185 return failure();
186
187 if (!enableInfos[0].condition.hasOneUse())
188 return failure();
189
190 ImplicitLocOpBuilder builder(stateOp.getLoc(), stateOp);
191 SmallVector<Value> inputs(stateOp.getInputs());
192
193 Value enableCond =
194 stateOp.getInputs()[enableInfos[0].condition.getArgNumber()];
195 Value one = hw::ConstantOp::create(builder, builder.getI1Type(), -1);
196 if (enableInfos[0].isDisable) {
197 inputs[enableInfos[0].condition.getArgNumber()] =
198 hw::ConstantOp::create(builder, builder.getI1Type(), 0);
199 enableCond = comb::XorOp::create(builder, enableCond, one);
200 } else {
201 inputs[enableInfos[0].condition.getArgNumber()] = one;
202 }
203
204 if (stateOp.getEnable())
205 enableCond = comb::AndOp::create(builder, stateOp.getEnable(), enableCond);
206
207 stateOp.getEnableMutable().assign(enableCond);
208
209 for (size_t i = 0, e = outputOp.getOutputs().size(); i < e; ++i) {
210 if (enableInfos[i].selfArg.hasOneUse())
211 inputs[enableInfos[i].selfArg.getArgNumber()] = hw::ConstantOp::create(
212 builder, stateOp.getLoc(), enableInfos[i].selfArg.getType(), 0);
213 }
214
215 stateOp.getInputsMutable().assign(inputs);
216 return success();
217}
218
219//===----------------------------------------------------------------------===//
220// Pattern detectors
221//===----------------------------------------------------------------------===//
222
223//===----------------------------------------------------------------------===//
224// Reset Patterns
225
226/// A reset represented with a single mux operation.
227/// out = mux(resetCondition, 0, arcArgument)
228/// ==>
229/// return arcArgument directly and add resetCondition to the StateOp
230static ResetInfo getIfMuxBasedReset(OpOperand &output) {
231 assert(isa<arc::OutputOp>(output.getOwner()) &&
232 "value has to be returned by the arc");
233
234 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
235 if (!isConstZero(mux.getTrueValue()))
236 return {};
237
238 if (!mux.getResult().hasOneUse())
239 return {};
240
241 if (auto condArg = dyn_cast<BlockArgument>(mux.getCond()))
242 return ResetInfo(mux.getFalseValue(), condArg, true);
243 }
244
245 return {};
246}
247
248/// A reset represented by an AND and XOR operation for i1 values only.
249/// out = and(X); X being a list containing all of
250/// {xor(resetCond, true), arcArgument}
251/// ==>
252/// out = and(X\xor(resetCond, true)) + add resetCond to StateOp
253static ResetInfo getIfAndBasedReset(OpOperand &output) {
254 assert(isa<arc::OutputOp>(output.getOwner()) &&
255 "value has to be returned by the arc");
256
257 if (auto andOp = output.get().getDefiningOp<comb::AndOp>()) {
258 if (!andOp.getResult().getType().isInteger(1))
259 return {};
260
261 if (!andOp.getResult().hasOneUse())
262 return {};
263
264 for (auto &operand : andOp->getOpOperands()) {
265 if (auto xorOp = operand.get().getDefiningOp<comb::XorOp>();
266 xorOp && xorOp->getNumOperands() == 2 &&
267 xorOp.getResult().hasOneUse()) {
268 if (auto condArg = dyn_cast<BlockArgument>(xorOp.getInputs()[0])) {
269 if (xorOp.getInputs().size() != 2 ||
270 !isConstTrue(xorOp.getInputs()[1]))
271 continue;
272
273 const unsigned condOutputNumber = operand.getOperandNumber();
274 auto inputConstructor = [=](OpBuilder &builder) -> Value {
275 if (andOp->getNumOperands() > 2) {
276 builder.setInsertionPoint(andOp);
277 auto copy = cast<comb::AndOp>(builder.clone(*andOp));
278 copy.getInputsMutable().erase(condOutputNumber);
279 return copy->getResult(0);
280 }
281
282 return andOp->getOperand(!condOutputNumber);
283 };
284
285 return ResetInfo(inputConstructor, condArg, true);
286 }
287 }
288 }
289 }
290
291 return {};
292}
293
294//===----------------------------------------------------------------------===//
295// Enable Patterns
296
297/// Just a helper function for the following two patterns.
298static EnableInfo checkOperandsForEnable(arc::StateOp stateOp, Value selfArg,
299 Value cond, unsigned outputNr,
300 bool isDisable) {
301 if (auto trueArg = dyn_cast<BlockArgument>(selfArg)) {
302 if (stateOp.getInputs()[trueArg.getArgNumber()] !=
303 stateOp.getResult(outputNr))
304 return {};
305
306 if (auto condArg = dyn_cast<BlockArgument>(cond))
307 return EnableInfo(selfArg, condArg, trueArg, isDisable);
308 }
309
310 return {};
311}
312
313/// An enable represented by a single mux operation.
314/// out = mux(enableCond, x, arcArgument) where x is the 'out' of the last cycle
315/// ==>
316/// out = arcArgument + set enableCond as enable operand to the StateOp
317static EnableInfo getIfMuxBasedEnable(OpOperand &output, StateOp stateOp) {
318 assert(isa<arc::OutputOp>(output.getOwner()) &&
319 "value has to be returned by the arc");
320
321 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
322 if (!mux.getResult().hasOneUse())
323 return {};
324
325 return checkOperandsForEnable(stateOp, mux.getFalseValue(), mux.getCond(),
326 output.getOperandNumber(), false);
327 }
328
329 return {};
330}
331
332/// A negated enable represented by a single mux operation.
333/// out = mux(enableCond, arcArgument, x) where x is the 'out' of the last cycle
334/// ==>
335/// out = arcArgument + set xor(enableCond, true) as enable operand to the
336/// StateOp
337static EnableInfo getIfMuxBasedDisable(OpOperand &output, StateOp stateOp) {
338 assert(isa<arc::OutputOp>(output.getOwner()) &&
339 "value has to be returned by the arc");
340
341 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
342 if (!mux.getResult().hasOneUse())
343 return {};
344
345 return checkOperandsForEnable(stateOp, mux.getTrueValue(), mux.getCond(),
346 output.getOperandNumber(), true);
347 }
348
349 return {};
350}
351
352//===----------------------------------------------------------------------===//
353// Combine all the patterns
354//===----------------------------------------------------------------------===//
355
356/// Combine all the reset patterns to one.
357ResetInfo computeResetInfoFromPattern(OpOperand &output) {
358 auto resetInfo = getIfMuxBasedReset(output);
359
360 if (!resetInfo)
361 resetInfo = getIfAndBasedReset(output);
362
363 return resetInfo;
364}
365
366/// Combine all the enable patterns to one.
367EnableInfo computeEnableInfoFromPattern(OpOperand &output, StateOp stateOp) {
368 auto enableInfo = getIfMuxBasedEnable(output, stateOp);
369
370 if (!enableInfo)
371 enableInfo = getIfMuxBasedDisable(output, stateOp);
372
373 return enableInfo;
374}
375
376//===----------------------------------------------------------------------===//
377// DetectResets pass
378//===----------------------------------------------------------------------===//
379
380namespace {
381struct InferStatePropertiesPass
382 : public impl::InferStatePropertiesBase<InferStatePropertiesPass> {
383 using InferStatePropertiesBase::InferStatePropertiesBase;
384
385 void runOnOperation() override;
386 void runOnStateOp(arc::StateOp stateOp, arc::DefineOp arc,
387 DenseMap<arc::DefineOp, unsigned> &resetConditionMap);
388};
389} // namespace
390
391void InferStatePropertiesPass::runOnOperation() {
392 SymbolTableCollection symbolTable;
393
394 DenseMap<arc::DefineOp, unsigned> resetConditionMap;
395 getOperation()->walk([&](arc::StateOp stateOp) {
396 auto arc =
397 cast<arc::DefineOp>(cast<mlir::CallOpInterface>(stateOp.getOperation())
398 .resolveCallableInTable(&symbolTable));
399 runOnStateOp(stateOp, arc, resetConditionMap);
400 });
401}
402
403void InferStatePropertiesPass::runOnStateOp(
404 arc::StateOp stateOp, arc::DefineOp arc,
405 DenseMap<arc::DefineOp, unsigned> &resetConditionMap) {
406
407 auto outputOp = cast<arc::OutputOp>(arc.getBodyBlock().getTerminator());
408 static constexpr unsigned visitedNoChange = -1;
409
410 if (detectResets) {
411 // Check for reset patterns, we only have to do this once per arc::DefineOp
412 // and store the result for later arc::StateOps referring to the same arc.
413 if (!resetConditionMap.count(arc)) {
414 SmallVector<ResetInfo> resetInfos;
415 int numResets = 0;
416 for (auto &output : outputOp->getOpOperands()) {
417 auto resetInfo = computeResetInfoFromPattern(output);
418 resetInfos.push_back(resetInfo);
419 if (resetInfo)
420 ++numResets;
421 }
422
423 // Rewrite the arc::DefineOp if valid
424 auto result = applyResetTransformation(arc, resetInfos);
425 if ((succeeded(result) && resetInfos[0]))
426 resetConditionMap[arc] = resetInfos[0].condition.getArgNumber();
427 else
428 resetConditionMap[arc] = visitedNoChange;
429
430 if (failed(result))
431 missedResets += numResets;
432 }
433
434 // Apply resets to the state operation.
435 if (resetConditionMap.count(arc) &&
436 resetConditionMap[arc] != visitedNoChange) {
437 setResetOperandOfStateOp(stateOp, resetConditionMap[arc]);
438 ++addedResets;
439 }
440 }
441
442 if (detectEnables) {
443 // Check for enable patterns.
444 SmallVector<EnableInfo> enableInfos;
445 int numEnables = 0;
446 for (OpOperand &output : outputOp->getOpOperands()) {
447 auto enableInfo = computeEnableInfoFromPattern(output, stateOp);
448 enableInfos.push_back(enableInfo);
449 if (enableInfo)
450 ++numEnables;
451 }
452
453 // Apply enable patterns.
454 if (!failed(applyEnableTransformation(arc, stateOp, enableInfos)))
455 ++addedEnables;
456 else
457 missedEnables += numEnables;
458 }
459}
assert(baseType &&"element must be base type")
static bool isConstTrue(Value value)
static EnableInfo checkOperandsForEnable(arc::StateOp stateOp, Value selfArg, Value cond, unsigned outputNr, bool isDisable)
Just a helper function for the following two patterns.
static EnableInfo getIfMuxBasedDisable(OpOperand &output, StateOp stateOp)
A negated enable represented by a single mux operation.
static ResetInfo getIfAndBasedReset(OpOperand &output)
A reset represented by an AND and XOR operation for i1 values only.
static LogicalResult applyEnableTransformation(arc::DefineOp arcOp, arc::StateOp stateOp, ArrayRef< EnableInfo > enableInfos)
Take an arc and a detected enable per output value and apply it to the given state if applicable (no ...
static bool isConstZero(Value value)
static LogicalResult applyResetTransformation(arc::DefineOp arcOp, ArrayRef< ResetInfo > resetInfos)
Take an arc and a detected reset per output value and apply it to the arc if applicable (but does not...
EnableInfo computeEnableInfoFromPattern(OpOperand &output, StateOp stateOp)
Combine all the enable patterns to one.
static void setResetOperandOfStateOp(arc::StateOp stateOp, unsigned resetConditionIndex)
Transform the given state operation to match the changes done to the arc in 'applyResetTransformation...
ResetInfo computeResetInfoFromPattern(OpOperand &output)
Combine all the reset patterns to one.
static EnableInfo getIfMuxBasedEnable(OpOperand &output, StateOp stateOp)
An enable represented by a single mux operation.
static ResetInfo getIfMuxBasedReset(OpOperand &output)
A reset represented with a single mux operation.
create(data_type, value)
Definition hw.py:433
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.