CIRCT 24.0.0git
Loading...
Searching...
No Matches
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 // Validate feedback zeros before creating operations or changing the state.
175 if (info.selfArg.hasOneUse() && hw::getBitWidth(info.selfArg.getType()) < 0)
176 return failure();
177
178 // We can only pull out the enable to the whole arc when all the output
179 // values have the same enable applied to them.
180 // TODO: split the arcs such that there is one for each enable kind,
181 // however, this requires a cost-model to not blow up binary-size too much.
182 if (!enableInfos.empty() &&
183 (info.condition != enableInfos.back().condition ||
184 info.isDisable != enableInfos.back().isDisable))
185 return failure();
186 }
187
188 if (enableInfos.empty())
189 return failure();
190
191 if (!enableInfos[0].condition.hasOneUse())
192 return failure();
193
194 ImplicitLocOpBuilder builder(stateOp.getLoc(), stateOp);
195 SmallVector<Value> inputs(stateOp.getInputs());
196
197 Value enableCond =
198 stateOp.getInputs()[enableInfos[0].condition.getArgNumber()];
199 Value one = hw::ConstantOp::create(builder, builder.getI1Type(), -1);
200 if (enableInfos[0].isDisable) {
201 inputs[enableInfos[0].condition.getArgNumber()] =
202 hw::ConstantOp::create(builder, builder.getI1Type(), 0);
203 enableCond = comb::XorOp::create(builder, enableCond, one);
204 } else {
205 inputs[enableInfos[0].condition.getArgNumber()] = one;
206 }
207
208 if (stateOp.getEnable())
209 enableCond = comb::AndOp::create(builder, stateOp.getEnable(), enableCond);
210
211 stateOp.getEnableMutable().assign(enableCond);
212
213 for (size_t i = 0, e = outputOp.getOutputs().size(); i < e; ++i) {
214 if (enableInfos[i].selfArg.hasOneUse()) {
215 // The feedback value may be an aggregate, but hw.constant only supports
216 // integers. Create an integer zero and bitcast it to the feedback type.
217 auto type = enableInfos[i].selfArg.getType();
218 Value zero = hw::ConstantOp::create(
219 builder, stateOp.getLoc(),
220 builder.getIntegerType(hw::getBitWidth(type)), 0);
221 if (zero.getType() != type)
222 zero = hw::BitcastOp::create(builder, type, zero);
223 inputs[enableInfos[i].selfArg.getArgNumber()] = zero;
224 }
225 }
226
227 stateOp.getInputsMutable().assign(inputs);
228 return success();
229}
230
231//===----------------------------------------------------------------------===//
232// Pattern detectors
233//===----------------------------------------------------------------------===//
234
235//===----------------------------------------------------------------------===//
236// Reset Patterns
237
238/// A reset represented with a single mux operation.
239/// out = mux(resetCondition, 0, arcArgument)
240/// ==>
241/// return arcArgument directly and add resetCondition to the StateOp
242static ResetInfo getIfMuxBasedReset(OpOperand &output) {
243 assert(isa<arc::OutputOp>(output.getOwner()) &&
244 "value has to be returned by the arc");
245
246 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
247 if (!isConstZero(mux.getTrueValue()))
248 return {};
249
250 if (!mux.getResult().hasOneUse())
251 return {};
252
253 if (auto condArg = dyn_cast<BlockArgument>(mux.getCond()))
254 return ResetInfo(mux.getFalseValue(), condArg, true);
255 }
256
257 return {};
258}
259
260/// A reset represented by an AND and XOR operation for i1 values only.
261/// out = and(X); X being a list containing all of
262/// {xor(resetCond, true), arcArgument}
263/// ==>
264/// out = and(X\xor(resetCond, true)) + add resetCond to StateOp
265static ResetInfo getIfAndBasedReset(OpOperand &output) {
266 assert(isa<arc::OutputOp>(output.getOwner()) &&
267 "value has to be returned by the arc");
268
269 if (auto andOp = output.get().getDefiningOp<comb::AndOp>()) {
270 if (!andOp.getResult().getType().isInteger(1))
271 return {};
272
273 if (!andOp.getResult().hasOneUse())
274 return {};
275
276 for (auto &operand : andOp->getOpOperands()) {
277 if (auto xorOp = operand.get().getDefiningOp<comb::XorOp>();
278 xorOp && xorOp->getNumOperands() == 2 &&
279 xorOp.getResult().hasOneUse()) {
280 if (auto condArg = dyn_cast<BlockArgument>(xorOp.getInputs()[0])) {
281 if (xorOp.getInputs().size() != 2 ||
282 !isConstTrue(xorOp.getInputs()[1]))
283 continue;
284
285 const unsigned condOutputNumber = operand.getOperandNumber();
286 auto inputConstructor = [=](OpBuilder &builder) -> Value {
287 if (andOp->getNumOperands() > 2) {
288 builder.setInsertionPoint(andOp);
289 auto copy = cast<comb::AndOp>(builder.clone(*andOp));
290 copy.getInputsMutable().erase(condOutputNumber);
291 return copy->getResult(0);
292 }
293
294 return andOp->getOperand(!condOutputNumber);
295 };
296
297 return ResetInfo(inputConstructor, condArg, true);
298 }
299 }
300 }
301 }
302
303 return {};
304}
305
306//===----------------------------------------------------------------------===//
307// Enable Patterns
308
309/// Just a helper function for the following two patterns.
310static EnableInfo checkOperandsForEnable(arc::StateOp stateOp, Value selfArg,
311 Value cond, unsigned outputNr,
312 bool isDisable) {
313 if (auto trueArg = dyn_cast<BlockArgument>(selfArg)) {
314 if (stateOp.getInputs()[trueArg.getArgNumber()] !=
315 stateOp.getResult(outputNr))
316 return {};
317
318 if (auto condArg = dyn_cast<BlockArgument>(cond))
319 return EnableInfo(selfArg, condArg, trueArg, isDisable);
320 }
321
322 return {};
323}
324
325/// An enable represented by a single mux operation.
326/// out = mux(enableCond, x, arcArgument) where x is the 'out' of the last cycle
327/// ==>
328/// out = arcArgument + set enableCond as enable operand to the StateOp
329static EnableInfo getIfMuxBasedEnable(OpOperand &output, StateOp stateOp) {
330 assert(isa<arc::OutputOp>(output.getOwner()) &&
331 "value has to be returned by the arc");
332
333 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
334 if (!mux.getResult().hasOneUse())
335 return {};
336
337 return checkOperandsForEnable(stateOp, mux.getFalseValue(), mux.getCond(),
338 output.getOperandNumber(), false);
339 }
340
341 return {};
342}
343
344/// A negated enable represented by a single mux operation.
345/// out = mux(enableCond, arcArgument, x) where x is the 'out' of the last cycle
346/// ==>
347/// out = arcArgument + set xor(enableCond, true) as enable operand to the
348/// StateOp
349static EnableInfo getIfMuxBasedDisable(OpOperand &output, StateOp stateOp) {
350 assert(isa<arc::OutputOp>(output.getOwner()) &&
351 "value has to be returned by the arc");
352
353 if (auto mux = output.get().getDefiningOp<comb::MuxOp>()) {
354 if (!mux.getResult().hasOneUse())
355 return {};
356
357 return checkOperandsForEnable(stateOp, mux.getTrueValue(), mux.getCond(),
358 output.getOperandNumber(), true);
359 }
360
361 return {};
362}
363
364//===----------------------------------------------------------------------===//
365// Combine all the patterns
366//===----------------------------------------------------------------------===//
367
368/// Combine all the reset patterns to one.
369ResetInfo computeResetInfoFromPattern(OpOperand &output) {
370 auto resetInfo = getIfMuxBasedReset(output);
371
372 if (!resetInfo)
373 resetInfo = getIfAndBasedReset(output);
374
375 return resetInfo;
376}
377
378/// Combine all the enable patterns to one.
379EnableInfo computeEnableInfoFromPattern(OpOperand &output, StateOp stateOp) {
380 auto enableInfo = getIfMuxBasedEnable(output, stateOp);
381
382 if (!enableInfo)
383 enableInfo = getIfMuxBasedDisable(output, stateOp);
384
385 return enableInfo;
386}
387
388//===----------------------------------------------------------------------===//
389// DetectResets pass
390//===----------------------------------------------------------------------===//
391
392namespace {
393struct InferStatePropertiesPass
394 : public impl::InferStatePropertiesBase<InferStatePropertiesPass> {
395 using InferStatePropertiesBase::InferStatePropertiesBase;
396
397 void runOnOperation() override;
398 void runOnStateOp(arc::StateOp stateOp, arc::DefineOp arc,
399 DenseMap<arc::DefineOp, unsigned> &resetConditionMap);
400};
401} // namespace
402
403void InferStatePropertiesPass::runOnOperation() {
404 SymbolTableCollection symbolTable;
405
406 DenseMap<arc::DefineOp, unsigned> resetConditionMap;
407 getOperation()->walk([&](arc::StateOp stateOp) {
408 auto arc =
409 cast<arc::DefineOp>(cast<mlir::CallOpInterface>(stateOp.getOperation())
410 .resolveCallableInTable(&symbolTable));
411 runOnStateOp(stateOp, arc, resetConditionMap);
412 });
413}
414
415void InferStatePropertiesPass::runOnStateOp(
416 arc::StateOp stateOp, arc::DefineOp arc,
417 DenseMap<arc::DefineOp, unsigned> &resetConditionMap) {
418
419 auto outputOp = cast<arc::OutputOp>(arc.getBodyBlock().getTerminator());
420 static constexpr unsigned visitedNoChange = -1;
421
422 if (detectResets) {
423 // Check for reset patterns, we only have to do this once per arc::DefineOp
424 // and store the result for later arc::StateOps referring to the same arc.
425 if (!resetConditionMap.count(arc)) {
426 SmallVector<ResetInfo> resetInfos;
427 int numResets = 0;
428 for (auto &output : outputOp->getOpOperands()) {
429 auto resetInfo = computeResetInfoFromPattern(output);
430 resetInfos.push_back(resetInfo);
431 if (resetInfo)
432 ++numResets;
433 }
434
435 // Rewrite the arc::DefineOp if valid
436 auto result = applyResetTransformation(arc, resetInfos);
437 if ((succeeded(result) && resetInfos[0]))
438 resetConditionMap[arc] = resetInfos[0].condition.getArgNumber();
439 else
440 resetConditionMap[arc] = visitedNoChange;
441
442 if (failed(result))
443 missedResets += numResets;
444 }
445
446 // Apply resets to the state operation.
447 if (resetConditionMap.count(arc) &&
448 resetConditionMap[arc] != visitedNoChange) {
449 setResetOperandOfStateOp(stateOp, resetConditionMap[arc]);
450 ++addedResets;
451 }
452 }
453
454 if (detectEnables) {
455 // Check for enable patterns.
456 SmallVector<EnableInfo> enableInfos;
457 int numEnables = 0;
458 for (OpOperand &output : outputOp->getOpOperands()) {
459 auto enableInfo = computeEnableInfoFromPattern(output, stateOp);
460 enableInfos.push_back(enableInfo);
461 if (enableInfo)
462 ++numEnables;
463 }
464
465 // Apply enable patterns.
466 if (!failed(applyEnableTransformation(arc, stateOp, enableInfos)))
467 ++addedEnables;
468 else
469 missedEnables += numEnables;
470 }
471}
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.
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
create(data_type, value)
Definition hw.py:444
create(data_type, value)
Definition hw.py:433
Definition arc.py:1
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:122
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.