CIRCT 24.0.0git
Loading...
Searching...
No Matches
FIRRTLInstanceInfo.cpp
Go to the documentation of this file.
1//===- FIRRTLInstanceInfo.cpp - Instance info analysis ----------*- C++ -*-===//
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 file defines the InstanceInfo analysis. This is an analysis that
10// depends on the InstanceGraph analysis, but provides additional information
11// about FIRRTL operations. This is useful if you find yourself needing to
12// selectively iterate over parts of the design.
13//
14//===----------------------------------------------------------------------===//
15
20#include "circt/Support/Debug.h"
22#include "llvm/ADT/PostOrderIterator.h"
23#include "llvm/Support/Debug.h"
24
25#ifndef NDEBUG
26#include "llvm/ADT/DepthFirstIterator.h"
27#endif
28
29#define DEBUG_TYPE "firrtl-analysis-instanceinfo"
30
31using namespace circt;
32using namespace firrtl;
33
35 return inst.getLowerToBind() || inst.getDoNotPrint() ||
36 inst->getParentOfType<LayerBlockOp>() ||
37 inst->getParentOfType<sv::IfDefOp>();
38}
39
40bool InstanceInfo::isInstanceUnderLayer(InstanceChoiceOp inst) {
41 return inst->getParentOfType<LayerBlockOp>() ||
42 inst->getParentOfType<sv::IfDefOp>();
43}
44
46
47bool InstanceInfo::LatticeValue::isConstant() const { return kind == Constant; }
48
49bool InstanceInfo::LatticeValue::isMixed() const { return kind == Mixed; }
50
53 return value;
54}
55
57 kind = Constant;
58 value = constant;
59}
60
62
64 if (kind > that.kind)
65 return;
66
67 if (kind < that.kind) {
68 kind = that.kind;
69 value = that.value;
70 return;
71 }
72
73 if (isConstant() && getConstant() != that.value)
74 kind = Mixed;
75}
76
78 LatticeValue latticeValue;
79 latticeValue.markConstant(value);
80 mergeIn(latticeValue);
81}
82
84 if (isUnknown() || isMixed())
85 return *this;
86
87 auto invert = LatticeValue();
88 invert.markConstant(!getConstant());
89 return invert;
90}
91
92InstanceInfo::InstanceInfo(Operation *op, mlir::AnalysisManager &am) {
93 auto &iGraph = am.getAnalysis<InstanceGraph>();
94
95 // Setup circuit attributes based on presence of annotations.
96 circuitAttributes.effectiveDut = iGraph.getTopLevelNode()->getModule();
97 for (auto *node : iGraph) {
98 auto moduleOp = node->getModule();
99 AnnotationSet annotations(moduleOp);
100 if (annotations.hasAnnotation(markDUTAnnoClass)) {
101 circuitAttributes.dut = moduleOp;
103 }
104 }
105
106 // Setup boundary conditions for any modules without users.
107 for (auto *node : iGraph) {
108 if (!node->noUses())
109 continue;
110
111 auto moduleOp = node->getModule();
112 ModuleAttributes &attributes = moduleAttributes[moduleOp];
113
114 attributes.underDut.mergeIn(isDut(moduleOp));
115 attributes.inDesign.mergeIn(isDut(moduleOp));
116 attributes.inEffectiveDesign.mergeIn(isEffectiveDut(moduleOp) || !hasDut());
117 attributes.underLayer.mergeIn(false);
118 attributes.inInstanceChoice.mergeIn(false);
119 }
120
121 // Visit modules in reverse post-order (visit parents before children) to
122 // merge parent attributes and per-instance attributes into children.
123 iGraph.walkInversePostOrder([&](igraph::InstanceGraphNode &modIt) {
124 auto moduleOp = modIt.getModule();
125 ModuleAttributes &attributes = moduleAttributes[moduleOp];
126
127 AnnotationSet annotations(moduleOp);
128 auto isDut = annotations.hasAnnotation(markDUTAnnoClass);
129 auto isGCCompanion = annotations.hasAnnotation(companionAnnoClass);
130
131 if (isDut) {
132 attributes.underDut.markConstant(true);
133 attributes.inDesign.markConstant(true);
134 attributes.inEffectiveDesign.markConstant(true);
135 }
136
137 if (isGCCompanion) {
138 attributes.inDesign.mergeIn(false);
139 attributes.inEffectiveDesign.mergeIn(false);
140 attributes.underLayer.mergeIn(true);
141 }
142
143 // Merge in values based on the instantiations of this module.
144 for (auto *useIt : modIt.uses()) {
145 auto parentOp = useIt->getParent()->getModule();
146 auto parentAttrs = moduleAttributes.find(parentOp)->getSecond();
147
148 // Update underDut.
149 if (!isDut)
150 attributes.underDut.mergeIn(parentAttrs.underDut);
151
152 // Update underLayer.
153 bool underLayer = false;
154 if (auto instanceOp = useIt->getInstance<InstanceOp>())
155 underLayer = InstanceInfo::isInstanceUnderLayer(instanceOp);
156
157 // Update inInstanceChoice.
158 if (auto instanceChoiceOp = useIt->getInstance<InstanceChoiceOp>()) {
159 attributes.inInstanceChoice.mergeIn(true);
160 underLayer = isInstanceUnderLayer(instanceChoiceOp);
161 } else {
162 attributes.inInstanceChoice.mergeIn(parentAttrs.inInstanceChoice);
163 }
164
165 if (!isGCCompanion) {
166 if (underLayer)
167 attributes.underLayer.mergeIn(true);
168 else
169 attributes.underLayer.mergeIn(parentAttrs.underLayer);
170 }
171
172 // Update inDesign and inEffectiveDesign.
173 if (underLayer) {
174 attributes.inDesign.mergeIn(false);
175 attributes.inEffectiveDesign.mergeIn(false);
176 } else if (!isDut && !isGCCompanion) {
177 attributes.inDesign.mergeIn(parentAttrs.inDesign);
178 attributes.inEffectiveDesign.mergeIn(parentAttrs.inEffectiveDesign);
179 }
180 }
181 });
182
183 // Visit modules in post-order (visit children before parents) to aggregate
184 // information into parents about themselves and their children.
185 //
186 // This walk is _more expensive_ than the earlier walk as this, at worst,
187 // needs to do a full IR walk. Mitigate this via short circuiting when we
188 // have enough information to interrupt the walk or to skip it entirely.
189 iGraph.walkPostOrder([&](igraph::InstanceGraphNode &modIt) {
190 auto moduleOp = modIt.getModule();
191 ModuleAttributes &attributes = moduleAttributes[moduleOp];
192
193 // Merge in attributes of instances within the module.
194 for (auto *instIt : modIt) {
195 attributes.hasProperties |=
196 moduleAttributes[instIt->getTarget()->getModule()].hasProperties;
197 if (attributes.postOrderSaturated())
198 break;
199 }
200
201 // Early exit if there is no body to examine or if the module cannot be
202 // public.
203 auto moduleLike = modIt.getModule<FModuleLike>();
204 if (!moduleLike)
205 return;
206
207 // Merge in attributes of the module.
208 attributes.hasProperties |=
209 cast<mlir::SymbolOpInterface>(moduleLike.getOperation()).isPublic();
210
211 // If the module is classlike, it is a property. Walk the ports and update
212 // attributes for each.
213 attributes.hasProperties |= isa<ClassLike>(moduleLike.getOperation());
214 for (auto port : moduleLike.getPorts()) {
215 attributes.hasProperties |= isa<PropertyType>(port.type);
216 if (attributes.postOrderSaturated())
217 break;
218 }
219
220 // Early exit if the attributes can no longer change. This avoids needing
221 // to do a walk of the module body.
222 if (attributes.postOrderSaturated())
223 return;
224
225 // Walk the ops to populate information, short circuiting as soon as
226 // we've gathered enough information to stop the walk. Only FModuleOp has
227 // a body to walk; external/intrinsic modules are fully characterized by
228 // their ports, already checked above.
229 if (auto fmodule = dyn_cast<FModuleOp>(moduleLike.getOperation())) {
230 auto isPropertyType = [](Type t) { return isa<PropertyType>(t); };
231 fmodule.walk([&](Operation *op) {
232 if (attributes.hasProperties)
233 return WalkResult::interrupt();
234 attributes.hasProperties |=
235 llvm::any_of(op->getOperandTypes(), isPropertyType) ||
236 llvm::any_of(op->getResultTypes(), isPropertyType);
237 return WalkResult::advance();
238 });
239 }
240 });
241
242 LLVM_DEBUG({
243 mlir::OpPrintingFlags flags;
244 flags.skipRegions();
245 debugHeader("FIRRTL InstanceInfo Analysis")
246 << "\n"
247 << llvm::indent(2) << "circuit attributes:\n"
248 << llvm::indent(4) << "hasDut: " << (hasDut() ? "true" : "false")
249 << "\n"
250 << llvm::indent(4) << "dut: ";
251 if (auto dut = circuitAttributes.dut)
252 dut->print(llvm::dbgs(), flags);
253 else
254 llvm::dbgs() << "null";
255 llvm::dbgs() << "\n" << llvm::indent(4) << "effectiveDut: ";
256 circuitAttributes.effectiveDut->print(llvm::dbgs(), flags);
257 llvm::dbgs() << "\n" << llvm::indent(2) << "module attributes:\n";
258 iGraph.walkInversePostOrder([&](auto &modIt) {
259 auto moduleOp = modIt.getModule();
260 auto attributes = moduleAttributes[moduleOp];
261 llvm::dbgs().indent(4)
262 << "- module: " << moduleOp.getModuleName() << "\n"
263 << llvm::indent(6)
264 << "isDut: " << (isDut(moduleOp) ? "true" : "false") << "\n"
265 << llvm::indent(6)
266 << "isEffectiveDut: " << (isEffectiveDut(moduleOp) ? "true" : "false")
267 << "\n"
268 << llvm::indent(6) << "underDut: " << attributes.underDut << "\n"
269 << llvm::indent(6) << "underLayer: " << attributes.underLayer << "\n"
270 << llvm::indent(6) << "inDesign: " << attributes.inDesign << "\n"
271 << llvm::indent(6)
272 << "inEffectiveDesign: " << attributes.inEffectiveDesign << "\n"
273 << llvm::indent(6)
274 << "inInstanceChoice: " << attributes.inInstanceChoice << "\n"
275 << llvm::indent(6)
276 << "hasProperties: " << (attributes.hasProperties ? "true" : "false")
277 << "\n";
278 });
279 });
280}
281
283InstanceInfo::getModuleAttributes(igraph::ModuleOpInterface op) {
284 return moduleAttributes.find(op)->getSecond();
285}
286
288
289bool InstanceInfo::isDut(igraph::ModuleOpInterface op) {
290 if (hasDut())
291 return op == circuitAttributes.dut;
292 return false;
293}
294
295bool InstanceInfo::isEffectiveDut(igraph::ModuleOpInterface op) {
296 if (hasDut())
297 return isDut(op);
298 return op == circuitAttributes.effectiveDut;
299}
300
301igraph::ModuleOpInterface InstanceInfo::getDut() {
302 return circuitAttributes.dut;
303}
304
305igraph::ModuleOpInterface InstanceInfo::getEffectiveDut() {
307}
308
309bool InstanceInfo::anyInstanceUnderDut(igraph::ModuleOpInterface op) {
310 auto underDut = getModuleAttributes(op).underDut;
311 return underDut.isMixed() || allInstancesUnderDut(op);
312}
313
314bool InstanceInfo::allInstancesUnderDut(igraph::ModuleOpInterface op) {
315 auto underDut = getModuleAttributes(op).underDut;
316 return underDut.isConstant() && underDut.getConstant();
317}
318
319bool InstanceInfo::anyInstanceUnderEffectiveDut(igraph::ModuleOpInterface op) {
320 return !hasDut() || anyInstanceUnderDut(op);
321}
322
323bool InstanceInfo::allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op) {
324 return !hasDut() || allInstancesUnderDut(op);
325}
326
327bool InstanceInfo::anyInstanceUnderLayer(igraph::ModuleOpInterface op) {
328 auto underLayer = getModuleAttributes(op).underLayer;
329 return underLayer.isMixed() || allInstancesUnderLayer(op);
330}
331
332bool InstanceInfo::allInstancesUnderLayer(igraph::ModuleOpInterface op) {
333 auto underLayer = getModuleAttributes(op).underLayer;
334 return underLayer.isConstant() && underLayer.getConstant();
335}
336
337bool InstanceInfo::anyInstanceInDesign(igraph::ModuleOpInterface op) {
338 auto inDesign = getModuleAttributes(op).inDesign;
339 return inDesign.isMixed() || allInstancesInDesign(op);
340}
341
342bool InstanceInfo::allInstancesInDesign(igraph::ModuleOpInterface op) {
343 auto inDesign = getModuleAttributes(op).inDesign;
344 return inDesign.isConstant() && inDesign.getConstant();
345}
346
347bool InstanceInfo::anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op) {
348 auto inEffectiveDesign = getModuleAttributes(op).inEffectiveDesign;
349 return inEffectiveDesign.isMixed() || allInstancesInEffectiveDesign(op);
350}
351
352bool InstanceInfo::allInstancesInEffectiveDesign(igraph::ModuleOpInterface op) {
353 auto inEffectiveDesign = getModuleAttributes(op).inEffectiveDesign;
354 return inEffectiveDesign.isConstant() && inEffectiveDesign.getConstant();
355}
356
357bool InstanceInfo::anyInstanceInInstanceChoice(igraph::ModuleOpInterface op) {
358 auto inInstanceChoice = getModuleAttributes(op).inInstanceChoice;
359 return inInstanceChoice.isMixed() ||
360 (inInstanceChoice.isConstant() && inInstanceChoice.getConstant());
361}
362
363bool InstanceInfo::moduleContainsProperties(igraph::ModuleOpInterface op) {
365}
assert(baseType &&"element must be base type")
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
This graph tracks modules and where they are instantiated.
A lattice value to record the value of a property.
LatticeValue operator!()
Invert the lattice value.
void mergeIn(LatticeValue that)
Merge attributes from another LatticeValue into this one.
bool value
The value of the property if kind is Constant.
Kind kind
Whether or not the property holds.
bool getConstant() const
Return the value. This should only be used if the kind is Constant.
void markMixed()
Set this LatticeValue to mixed.
bool isConstant() const
Return true if the kind is Constant.
bool isUnknown() const
Return true if the kind is Unknown.
void markConstant(bool constant)
Set this LatticeValue to a constant.
bool isMixed() const
Return true if the kind is Mixed.
InstanceInfo::LatticeValue inInstanceChoice
Indicates if this module is instantiated within (or transitively within) an instance choice operation...
InstanceInfo::LatticeValue inDesign
Indicates if this module is instantiated in the design.
InstanceInfo::LatticeValue underDut
Indicates if this module is instantiated under the design-under-test.
InstanceInfo::LatticeValue inEffectiveDesign
Indicates if this modules is instantiated in the effective design.
bool postOrderSaturated()
Return true if the product of post-order information is saturated (cannot ever change).
InstanceInfo::LatticeValue underLayer
Indicates if this module is instantiated under a layer.
bool hasProperties
Indicates if this module has any property operations within (or transitively within) it,...
bool allInstancesUnderLayer(igraph::ModuleOpInterface op)
Return true if all instances of this module are under (or transitively under) layer blocks.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool moduleContainsProperties(igraph::ModuleOpInterface op)
Return true if this module contains (or its children transitively contain) any property operations,...
bool isEffectiveDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test and the circuit has a design-under-test.
static bool isInstanceUnderLayer(InstanceOp inst)
Return true if an instance op should be considered "under a layer" for the purposes of metadata emiss...
CircuitAttributes circuitAttributes
Stores circuit-level attributes.
bool hasDut()
Return true if this circuit has a design-under-test.
bool allInstancesInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if all instances of this module are within (or transitively within) the effective design.
bool isDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test.
bool anyInstanceUnderDut(igraph::ModuleOpInterface op)
Return true if at least one instance of this module is under (or transitively under) the design-under...
bool anyInstanceUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if at least one instance is under (or transitively under) the effective design-under-test...
bool allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if all instances are under (or transitively under) the effective design-under-test.
DenseMap< Operation *, ModuleAttributes > moduleAttributes
Internal mapping of operations to module attributes.
igraph::ModuleOpInterface getEffectiveDut()
Return the "effective" design-under-test.
InstanceInfo(Operation *op, mlir::AnalysisManager &am)
bool allInstancesUnderDut(igraph::ModuleOpInterface op)
Return true if all instances of this module are under (or transitively under) the design-under-test.
const ModuleAttributes & getModuleAttributes(igraph::ModuleOpInterface op)
Return the module attributes associated with a module.
bool anyInstanceInInstanceChoice(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) an instance choice.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
bool allInstancesInDesign(igraph::ModuleOpInterface op)
Return true if all instances of this module are within (or transitively within) the design.
bool anyInstanceUnderLayer(igraph::ModuleOpInterface op)
Return true if at least one instance of this module is under (or transitively under) a layer.
bool anyInstanceInDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the design.
This is a Node in the InstanceGraph.
llvm::iterator_range< UseIterator > uses()
auto getModule()
Get the module that this node is tracking.
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(const llvm::Twine &str, unsigned width=80)
Write a "header"-like string to the debug stream with a certain width.
Definition Debug.cpp:17
igraph::ModuleOpInterface dut
The design-under-test if one is defined.
igraph::ModuleOpInterface effectiveDut
The design-under-test if one is defined or the top module.