CIRCT 22.0.0git
Loading...
Searching...
No Matches
FIRRTLAnnotationHelper.cpp
Go to the documentation of this file.
1//===- FIRRTLAnnotationHelper.cpp - FIRRTL Annotation Lookup ----*- 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 implements helpers mapping annotations to operations.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/ImplicitLocOpBuilder.h"
17#include "llvm/Support/Debug.h"
18
19#define DEBUG_TYPE "lower-annos"
20
21using namespace circt;
22using namespace firrtl;
23using namespace chirrtl;
24
25using llvm::StringRef;
26
27// Some types have been expanded so the first layer of aggregate path is
28// a return value.
29static LogicalResult updateExpandedPort(StringRef field, AnnoTarget &ref) {
30 if (auto mem = dyn_cast<MemOp>(ref.getOp()))
31 for (size_t p = 0, pe = mem.getPortNames().size(); p < pe; ++p)
32 if (mem.getPortName(p) == field) {
33 ref = PortAnnoTarget(mem, p);
34 return success();
35 }
36 ref.getOp()->emitError("Cannot find port with name ") << field;
37 return failure();
38}
39
40/// Try to resolve an non-array aggregate name from a target given the type and
41/// operation of the resolved target. This needs to deal with places where we
42/// represent bundle returns as split into constituent parts.
43static FailureOr<unsigned> findBundleElement(Operation *op, Type type,
44 StringRef field) {
45 auto bundle = type_dyn_cast<BundleType>(type);
46 if (!bundle) {
47 op->emitError("field access '")
48 << field << "' into non-bundle type '" << type << "'";
49 return failure();
50 }
51 auto idx = bundle.getElementIndex(field);
52 if (!idx) {
53 op->emitError("cannot resolve field '")
54 << field << "' in subtype '" << bundle << "'";
55 return failure();
56 }
57 return *idx;
58}
59
60/// Try to resolve an array index from a target given the type of the resolved
61/// target.
62static FailureOr<unsigned> findVectorElement(Operation *op, Type type,
63 StringRef indexStr) {
64 size_t index;
65 if (indexStr.getAsInteger(10, index)) {
66 op->emitError("Cannot convert '") << indexStr << "' to an integer";
67 return failure();
68 }
69 auto vec = type_dyn_cast<FVectorType>(type);
70 if (!vec) {
71 op->emitError("index access '")
72 << index << "' into non-vector type '" << type << "'";
73 return failure();
74 }
75 return index;
76}
77
78static FailureOr<unsigned> findFieldID(AnnoTarget &ref,
79 ArrayRef<TargetToken> tokens) {
80 if (tokens.empty())
81 return 0;
82
83 auto *op = ref.getOp();
84 auto fieldIdx = 0;
85 // The first field for some ops refers to expanded return values.
86 if (isa<MemOp>(op)) {
87 if (failed(updateExpandedPort(tokens.front().name, ref)))
88 return {};
89 tokens = tokens.drop_front();
90 }
91
92 auto type = ref.getType();
93 if (!type)
94 return op->emitError(tokens.front().isIndex ? "index" : "field")
95 << " access in annotation not supported for this operation";
96
97 for (auto token : tokens) {
98 if (token.isIndex) {
99 auto result = findVectorElement(op, type, token.name);
100 if (failed(result))
101 return failure();
102 auto vector = type_cast<FVectorType>(type);
103 type = vector.getElementType();
104 fieldIdx += vector.getFieldID(*result);
105 } else {
106 auto result = findBundleElement(op, type, token.name);
107 if (failed(result))
108 return failure();
109 auto bundle = type_cast<BundleType>(type);
110 type = bundle.getElementType(*result);
111 fieldIdx += bundle.getFieldID(*result);
112 }
113 }
114 return fieldIdx;
115}
116
117void TokenAnnoTarget::toVector(SmallVectorImpl<char> &out) const {
118 out.push_back('~');
119 out.append(circuit.begin(), circuit.end());
120 out.push_back('|');
121 for (auto modInstPair : instances) {
122 out.append(modInstPair.first.begin(), modInstPair.first.end());
123 out.push_back('/');
124 out.append(modInstPair.second.begin(), modInstPair.second.end());
125 out.push_back(':');
126 }
127 out.append(module.begin(), module.end());
128 if (name.empty())
129 return;
130 out.push_back('>');
131 out.append(name.begin(), name.end());
132 for (auto comp : component) {
133 out.push_back(comp.isIndex ? '[' : '.');
134 out.append(comp.name.begin(), comp.name.end());
135 if (comp.isIndex)
136 out.push_back(']');
137 }
138}
139
140std::string firrtl::canonicalizeTarget(StringRef target) {
141
142 if (target.empty())
143 return target.str();
144
145 // If this is a normal Target (not a Named), erase that field in the JSON
146 // object and return that Target.
147 if (target[0] == '~')
148 return target.str();
149
150 // This is a legacy target using the firrtl.annotations.Named type. This
151 // can be trivially canonicalized to a non-legacy target, so we do it with
152 // the following three mappings:
153 // 1. CircuitName => CircuitTarget, e.g., A -> ~A
154 // 2. ModuleName => ModuleTarget, e.g., A.B -> ~A|B
155 // 3. ComponentName => ReferenceTarget, e.g., A.B.C -> ~A|B>C
156 std::string newTarget = ("~" + target).str();
157 auto n = newTarget.find('.');
158 if (n != std::string::npos)
159 newTarget[n] = '|';
160 n = newTarget.find('.');
161 if (n != std::string::npos)
162 newTarget[n] = '>';
163 return newTarget;
164}
165
166std::optional<AnnoPathValue>
168 SymbolTable &symTbl, CircuitTargetCache &cache) {
169 // Validate circuit name.
170 if (!path.circuit.empty() && circuit.getName() != path.circuit) {
171 mlir::emitError(circuit.getLoc())
172 << "circuit name doesn't match annotation '" << path.circuit << '\'';
173 return {};
174 }
175 // Circuit only target.
176 if (path.module.empty()) {
177 assert(path.name.empty() && path.instances.empty() &&
178 path.component.empty());
179 return AnnoPathValue(circuit);
180 }
181
182 // Resolve all instances for non-local paths.
183 SmallVector<InstanceOp> instances;
184 for (auto p : path.instances) {
185 auto mod = symTbl.lookup<FModuleOp>(p.first);
186 if (!mod) {
187 mlir::emitError(circuit.getLoc())
188 << "module doesn't exist '" << p.first << '\'';
189 return {};
190 }
191 auto resolved = cache.lookup(mod, p.second);
192 if (!resolved || !isa<InstanceOp>(resolved.getOp())) {
193 mlir::emitError(circuit.getLoc()) << "cannot find instance '" << p.second
194 << "' in '" << mod.getName() << "'";
195 return {};
196 }
197 instances.push_back(cast<InstanceOp>(resolved.getOp()));
198 }
199 // The final module is where the named target is (or is the named target).
200 auto mod = symTbl.lookup<FModuleLike>(path.module);
201 if (!mod) {
202 mlir::emitError(circuit.getLoc())
203 << "module doesn't exist '" << path.module << '\'';
204 return {};
205 }
206
207 // ClassOps may not participate in annotation targeting. Neither the class
208 // itself, nor any "named thing" defined under it, may be targeted by an anno.
209 if (isa<ClassOp>(mod)) {
210 mlir::emitError(mod.getLoc()) << "annotations cannot target classes";
211 return {};
212 }
213
214 AnnoTarget ref;
215 if (path.name.empty()) {
216 assert(path.component.empty());
217 ref = OpAnnoTarget(mod);
218 } else {
219 ref = cache.lookup(mod, path.name);
220 if (!ref) {
221 mlir::emitError(circuit.getLoc()) << "cannot find name '" << path.name
222 << "' in " << mod.getModuleName();
223 return {};
224 }
225 // AnnoTarget::getType() is not safe (CHIRRTL ops crash, null if instance),
226 // avoid. For now, only references in ports can be targets, check that.
227 // TODO: containsReference().
228 if (isa<PortAnnoTarget>(ref) && isa<RefType>(ref.getType())) {
229 mlir::emitError(circuit.getLoc())
230 << "cannot target reference-type '" << path.name << "' in "
231 << mod.getModuleName();
232 return {};
233 }
234 }
235
236 // If the reference is pointing to an instance op, we have to move the target
237 // to the module. This is done both because it is logical to have one
238 // representation (this effectively canonicalizes a reference target on an
239 // instance into an instance target) and because the SFC has a pass that does
240 // this conversion. E.g., this is converting (where "bar" is an instance):
241 // ~Foo|Foo>bar
242 // Into:
243 // ~Foo|Foo/bar:Bar
244 ArrayRef<TargetToken> component(path.component);
245 if (auto instance = dyn_cast<InstanceOp>(ref.getOp())) {
246 instances.push_back(instance);
247 auto target = cast<FModuleLike>(instance.getReferencedOperation(symTbl));
248 if (component.empty()) {
249 ref = OpAnnoTarget(target);
250 } else if (component.front().isIndex) {
251 mlir::emitError(circuit.getLoc())
252 << "illegal target '" << path.str() << "' indexes into an instance";
253 return {};
254 } else {
255 auto field = component.front().name;
256 ref = AnnoTarget();
257 for (size_t p = 0, pe = getNumPorts(target); p < pe; ++p)
258 if (target.getPortName(p) == field) {
259 ref = PortAnnoTarget(target, p);
260 break;
261 }
262 if (!ref) {
263 mlir::emitError(circuit.getLoc())
264 << "cannot find port '" << field << "' in module "
265 << target.getModuleName();
266 return {};
267 }
268 // TODO: containsReference().
269 if (isa<RefType>(ref.getType())) {
270 mlir::emitError(circuit.getLoc())
271 << "annotation cannot target reference-type port '" << field
272 << "' in module " << target.getModuleName();
273 return {};
274 }
275 component = component.drop_front();
276 }
277 }
278
279 // If we have aggregate specifiers, resolve those now. This call can update
280 // the ref to target a port of a memory.
281 auto result = findFieldID(ref, component);
282 if (failed(result))
283 return {};
284 auto fieldIdx = *result;
285
286 return AnnoPathValue(instances, ref, fieldIdx);
287}
288
289/// split a target string into it constituent parts. This is the primary parser
290/// for targets.
291std::optional<TokenAnnoTarget> firrtl::tokenizePath(StringRef origTarget) {
292 // An empty string is not a legal target.
293 if (origTarget.empty())
294 return {};
295 StringRef target = origTarget;
296 TokenAnnoTarget retval;
297 std::tie(retval.circuit, target) = target.split('|');
298 if (!retval.circuit.empty() && retval.circuit[0] == '~')
299 retval.circuit = retval.circuit.drop_front();
300 while (target.count(':')) {
301 StringRef nla;
302 std::tie(nla, target) = target.split(':');
303 StringRef inst, mod;
304 std::tie(mod, inst) = nla.split('/');
305 retval.instances.emplace_back(mod, inst);
306 }
307 // remove aggregate
308 auto targetBase =
309 target.take_until([](char c) { return c == '.' || c == '['; });
310 auto aggBase = target.drop_front(targetBase.size());
311 std::tie(retval.module, retval.name) = targetBase.split('>');
312 while (!aggBase.empty()) {
313 if (aggBase[0] == '.') {
314 aggBase = aggBase.drop_front();
315 StringRef field = aggBase.take_front(aggBase.find_first_of("[."));
316 aggBase = aggBase.drop_front(field.size());
317 retval.component.push_back({field, false});
318 } else if (aggBase[0] == '[') {
319 aggBase = aggBase.drop_front();
320 StringRef index = aggBase.take_front(aggBase.find_first_of(']'));
321 aggBase = aggBase.drop_front(index.size() + 1);
322 retval.component.push_back({index, true});
323 } else {
324 return {};
325 }
326 }
327
328 return retval;
329}
330
331std::optional<AnnoPathValue> firrtl::resolvePath(StringRef rawPath,
332 CircuitOp circuit,
333 SymbolTable &symTbl,
334 CircuitTargetCache &cache) {
335 auto pathStr = canonicalizeTarget(rawPath);
336 StringRef path{pathStr};
337
338 auto tokens = tokenizePath(path);
339 if (!tokens) {
340 mlir::emitError(circuit.getLoc())
341 << "Cannot tokenize annotation path " << rawPath;
342 return {};
343 }
344
345 return resolveEntities(*tokens, circuit, symTbl, cache);
346}
347
348InstanceOp firrtl::addPortsToModule(FModuleLike mod, InstanceOp instOnPath,
349 FIRRTLType portType, Direction dir,
350 StringRef newName,
351 InstancePathCache &instancePathcache,
352 CircuitTargetCache *targetCaches) {
353 // To store the cloned version of `instOnPath`.
354 InstanceOp clonedInstOnPath;
355 // Get a new port name from the Namespace.
356 auto portName = StringAttr::get(mod.getContext(), newName);
357 // The port number for the new port.
358 unsigned portNo = getNumPorts(mod);
359 PortInfo portInfo = {portName, portType, dir, {}, mod.getLoc()};
360 mod.insertPorts({{portNo, portInfo}});
361 // Now update all the instances of `mod`.
362 for (auto *use : instancePathcache.instanceGraph.lookup(mod)->uses()) {
363 InstanceOp useInst = cast<InstanceOp>(use->getInstance());
364 auto clonedInst =
365 useInst.cloneWithInsertedPortsAndReplaceUses({{portNo, portInfo}});
366 if (useInst == instOnPath)
367 clonedInstOnPath = clonedInst;
368 // Update all occurences of old instance.
369 instancePathcache.replaceInstance(useInst, clonedInst);
370 if (targetCaches)
371 targetCaches->replaceOp(useInst, clonedInst);
372 useInst->erase();
373 }
374 return clonedInstOnPath;
375}
376
377//===----------------------------------------------------------------------===//
378// AnnoTargetCache
379//===----------------------------------------------------------------------===//
380
381void AnnoTargetCache::gatherTargets(FModuleLike mod) {
382 // Add ports
383 for (const auto &p : llvm::enumerate(mod.getPorts()))
384 insertPort(mod, p.index());
385
386 // And named things
387 mod.walk([&](Operation *op) { insertOp(op); });
388}
389
390//===----------------------------------------------------------------------===//
391// HierPathOpCache
392//===----------------------------------------------------------------------===//
393
394HierPathCache::HierPathCache(Operation *op, SymbolTable &symbolTable)
395 : builder(OpBuilder::atBlockBegin(&op->getRegion(0).front())),
396 symbolTable(symbolTable) {
397
398 // Populate the cache with any symbols preexisting.
399 for (auto &region : op->getRegions())
400 for (auto &block : region.getBlocks())
401 for (auto path : block.getOps<hw::HierPathOp>())
402 cache[path.getNamepathAttr()] = path;
403}
404
405hw::HierPathOp HierPathCache::getOpFor(ArrayAttr attr) {
406 auto &op = cache[attr];
407 if (!op) {
408 op = hw::HierPathOp::create(builder, UnknownLoc::get(builder.getContext()),
409 "nla", attr);
410 symbolTable.insert(op);
411 op.setVisibility(SymbolTable::Visibility::Private);
412 }
413 return op;
414}
415
416//===----------------------------------------------------------------------===//
417// Code related to handling Grand Central Mem Taps annotations
418//===----------------------------------------------------------------------===//
419
421 DictionaryAttr anno,
422 ApplyState &state) {
423 auto loc = state.circuit.getLoc();
424
425 auto sourceAttr =
426 tryGetAs<StringAttr>(anno, anno, "source", loc, memTapClass);
427 if (!sourceAttr)
428 return failure();
429
430 auto sourceTargetStr = canonicalizeTarget(sourceAttr.getValue());
431 std::optional<AnnoPathValue> srcTarget = resolvePath(
432 sourceTargetStr, state.circuit, state.symTbl, state.targetCaches);
433 if (!srcTarget)
434 return mlir::emitError(loc, "cannot resolve source target path '")
435 << sourceTargetStr << "'";
436
437 auto tapsAttr = tryGetAs<ArrayAttr>(anno, anno, "sink", loc, memTapClass);
438 if (!tapsAttr || tapsAttr.empty())
439 return mlir::emitError(loc, "sink must have at least one entry");
440
441 auto tap = dyn_cast_or_null<StringAttr>(tapsAttr[0]);
442 if (!tap) {
443 return mlir::emitError(
444 loc, "Annotation '" + Twine(memTapClass) +
445 "' with path '.taps[0" +
446 "]' contained an unexpected type (expected a string).")
447 .attachNote()
448 << "The full Annotation is reprodcued here: " << anno << "\n";
449 }
450
451 auto wireTargetStr = canonicalizeTarget(tap.getValue());
452 if (!tokenizePath(wireTargetStr))
453 return failure();
454 std::optional<AnnoPathValue> wireTarget = resolvePath(
455 wireTargetStr, state.circuit, state.symTbl, state.targetCaches);
456 if (!wireTarget)
457 return mlir::emitError(loc, "Annotation '" + Twine(memTapClass) +
458 "' with path '.taps[0]' contains target '" +
459 wireTargetStr +
460 "' that cannot be resolved.")
461 .attachNote()
462 << "The full Annotation is reproduced here: " << anno << "\n";
463
464 auto combMem = dyn_cast<chirrtl::CombMemOp>(srcTarget->ref.getOp());
465 if (!combMem)
466 return srcTarget->ref.getOp()->emitOpError(
467 "unsupported operation, only CombMem can be used as the source of "
468 "MemTap");
469 if (!combMem.getType().getElementType().isGround())
470 return combMem.emitOpError(
471 "cannot generate MemTap to a memory with aggregate data type");
472 if (tapsAttr.size() != combMem.getType().getNumElements())
473 return mlir::emitError(
474 loc, "sink cannot specify more taps than the depth of the memory");
475 if (srcTarget->instances.empty()) {
476 auto path = state.instancePathCache.getAbsolutePaths(
477 combMem->getParentOfType<FModuleOp>());
478 if (path.size() > 1)
479 return combMem.emitOpError(
480 "cannot be resolved as source for MemTap, multiple paths from top "
481 "exist and unique instance cannot be resolved");
482 srcTarget->instances.append(path.back().begin(), path.back().end());
483 }
484
485 ImplicitLocOpBuilder builder(combMem->getLoc(), combMem);
486
487 // Lower memory taps to real ports on the memory. This is done if the taps
488 // are supposed to be synthesized.
489 if (state.noRefTypePorts) {
490 // Create new ports _after_ all other ports to avoid permuting existing
491 // ports.
492 builder.setInsertionPointToEnd(
493 combMem->getParentOfType<FModuleOp>().getBodyBlock());
494
495 // Determine the clock to use for the debug ports. Error if the same clock
496 // is not used for all ports or if no clock port is found.
497 Value clock;
498 for (auto *portOp : combMem.getResult().getUsers()) {
499 for (auto result : portOp->getResults()) {
500 for (auto *user : result.getUsers()) {
501 auto accessOp = dyn_cast<chirrtl::MemoryPortAccessOp>(user);
502 if (!accessOp)
503 continue;
504 auto newClock = accessOp.getClock();
505 if (clock && clock != newClock)
506 return combMem.emitOpError(
507 "has different clocks on different ports (this is ambiguous "
508 "when compiling without reference types)");
509 clock = newClock;
510 }
511 }
512 }
513 if (!clock)
514 return combMem.emitOpError(
515 "does not have an access port to determine a clock connection (this "
516 "is necessary when compiling without reference types)");
517
518 // Add one port per memory address.
519 SmallVector<Value> data;
520 Type uintType = builder.getType<UIntType>();
521 for (uint64_t i = 0, e = combMem.getType().getNumElements(); i != e; ++i) {
522 auto port = chirrtl::MemoryPortOp::create(
523 builder, combMem.getType().getElementType(),
524 CMemoryPortType::get(builder.getContext()), combMem.getResult(),
525 MemDirAttr::Read, builder.getStringAttr("memTap_" + Twine(i)),
526 builder.getArrayAttr({}));
527 chirrtl::MemoryPortAccessOp::create(
528 builder, port.getPort(),
529 ConstantOp::create(builder, uintType, APSInt::getUnsigned(i)), clock);
530 data.push_back(port.getData());
531 }
532
533 // Package up all the reads into a vector.
534 auto sendVal = VectorCreateOp::create(
535 builder,
536 FVectorType::get(combMem.getType().getElementType(),
537 combMem.getType().getNumElements()),
538 data);
539 auto sink = wireTarget->ref.getOp()->getResult(0);
540
541 // Add a wiring problem to hook up the vector to the destination wire.
542 state.wiringProblems.push_back(
543 {sendVal, sink, "memTap", WiringProblem::RefTypeUsage::Never});
544 return success();
545 }
546
547 // Normal memory handling. Create a debug port.
548 builder.setInsertionPointAfter(combMem);
549 // Construct the type for the debug port.
550 auto debugType = RefType::get(FVectorType::get(
551 combMem.getType().getElementType(), combMem.getType().getNumElements()));
552 Value memDbgPort =
553 chirrtl::MemoryDebugPortOp::create(
554 builder, debugType, combMem,
555 state.getNamespace(srcTarget->ref.getModule()).newName("memTap"))
556 .getResult();
557
558 auto sendVal = memDbgPort;
559 if (wireTarget->ref.getOp()->getResult(0).getType() !=
560 type_cast<RefType>(sendVal.getType()).getType())
561 return wireTarget->ref.getOp()->emitError(
562 "cannot generate the MemTap, wiretap Type does not match the memory "
563 "type");
564 auto sink = wireTarget->ref.getOp()->getResult(0);
565 state.wiringProblems.push_back(
566 {sendVal, sink, "memTap", WiringProblem::RefTypeUsage::Prefer});
567 return success();
568}
assert(baseType &&"element must be base type")
static FailureOr< unsigned > findVectorElement(Operation *op, Type type, StringRef indexStr)
Try to resolve an array index from a target given the type of the resolved target.
static FailureOr< unsigned > findFieldID(AnnoTarget &ref, ArrayRef< TargetToken > tokens)
static FailureOr< unsigned > findBundleElement(Operation *op, Type type, StringRef field)
Try to resolve an non-array aggregate name from a target given the type and operation of the resolved...
static LogicalResult updateExpandedPort(StringRef field, AnnoTarget &ref)
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:87
llvm::iterator_range< UseIterator > uses()
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
Direction
This represents the direction of a single port.
Definition FIRRTLEnums.h:27
std::optional< AnnoPathValue > resolveEntities(TokenAnnoTarget path, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Convert a parsed target string to a resolved target structure.
constexpr const char * memTapClass
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
std::string canonicalizeTarget(StringRef target)
Return an input target string in canonical form.
InstanceOp addPortsToModule(FModuleLike mod, InstanceOp instOnPath, FIRRTLType portType, Direction dir, StringRef newName, InstancePathCache &instancePathcache, CircuitTargetCache *targetCaches=nullptr)
Add ports to the module and all its instances and return the clone for instOnPath.
LogicalResult applyGCTMemTaps(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
std::optional< AnnoPathValue > resolvePath(StringRef rawPath, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Resolve a string path to a named item inside a circuit.
std::optional< TokenAnnoTarget > tokenizePath(StringRef origTarget)
Parse a FIRRTL annotation path into its constituent parts.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void gatherTargets(FModuleLike mod)
Walk the module and add named things to 'targets'.
void insertPort(FModuleLike mod, size_t portNo)
Add a new module port to the target cache.
An annotation target is used to keep track of something that is targeted by an Annotation.
FIRRTLType getType() const
Get the type of the target.
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
InstancePathCache & instancePathCache
hw::InnerSymbolNamespace & getNamespace(FModuleLike module)
Cache AnnoTargets for a circuit's modules, walked as needed.
void replaceOp(Operation *oldOp, Operation *newOp)
Replace oldOp with newOp in the target cache.
AnnoTarget lookup(FModuleLike module, StringRef name)
Lookup the target for 'name' in 'module'.
HierPathCache(Operation *op, SymbolTable &symbolTable)
DenseMap< ArrayAttr, hw::HierPathOp > cache
hw::HierPathOp getOpFor(ArrayAttr attr)
This represents an annotation targeting a specific operation.
This represents an annotation targeting a specific port of a module, memory, or instance.
This holds the name and type that describes the module's ports.
The parsed annotation path.
SmallVector< TargetToken > component
std::string str() const
Convert the annotation path to a string.
void toVector(SmallVectorImpl< char > &out) const
Append the annotation path to the given SmallString or SmallVector.
SmallVector< std::pair< StringRef, StringRef > > instances
A data structure that caches and provides paths to module instances in the IR.
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)
void replaceInstance(InstanceOpInterface oldOp, InstanceOpInterface newOp)
Replace an InstanceOp. This is required to keep the cache updated.
InstanceGraph & instanceGraph
The instance graph of the IR.