16#include "mlir/IR/ImplicitLocOpBuilder.h"
17#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "lower-annos"
22using namespace firrtl;
23using namespace chirrtl;
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) {
36 ref.
getOp()->emitError(
"Cannot find port with name ") << field;
45 auto bundle = type_dyn_cast<BundleType>(type);
47 op->emitError(
"field access '")
48 << field <<
"' into non-bundle type '" << type <<
"'";
51 auto idx = bundle.getElementIndex(field);
53 op->emitError(
"cannot resolve field '")
54 << field <<
"' in subtype '" << bundle <<
"'";
65 if (indexStr.getAsInteger(10, index)) {
66 op->emitError(
"Cannot convert '") << indexStr <<
"' to an integer";
69 auto vec = type_dyn_cast<FVectorType>(type);
71 op->emitError(
"index access '")
72 << index <<
"' into non-vector type '" << type <<
"'";
79 ArrayRef<TargetToken> tokens) {
83 auto *op = ref.
getOp();
89 tokens = tokens.drop_front();
94 return op->emitError(tokens.front().isIndex ?
"index" :
"field")
95 <<
" access in annotation not supported for this operation";
97 for (
auto token : tokens) {
102 auto vector = type_cast<FVectorType>(type);
103 type = vector.getElementType();
104 fieldIdx += vector.getFieldID(*result);
109 auto bundle = type_cast<BundleType>(type);
110 type = bundle.getElementType(*result);
111 fieldIdx += bundle.getFieldID(*result);
122 out.append(modInstPair.first.begin(), modInstPair.first.end());
124 out.append(modInstPair.second.begin(), modInstPair.second.end());
127 out.append(module.begin(),
module.end());
131 out.append(
name.begin(),
name.end());
133 out.push_back(comp.isIndex ?
'[' :
'.');
134 out.append(comp.name.begin(), comp.name.end());
147 if (target[0] ==
'~')
156 std::string newTarget = (
"~" + target).str();
157 auto n = newTarget.find(
'.');
158 if (n != std::string::npos)
160 n = newTarget.find(
'.');
161 if (n != std::string::npos)
166std::optional<AnnoPathValue>
171 mlir::emitError(circuit.getLoc())
172 <<
"circuit name doesn't match annotation '" << path.
circuit <<
'\'';
176 if (path.module.empty()) {
183 SmallVector<InstanceOp> instances;
185 auto mod = symTbl.lookup<FModuleOp>(p.first);
187 mlir::emitError(circuit.getLoc())
188 <<
"module doesn't exist '" << p.first <<
'\'';
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() <<
"'";
197 instances.push_back(cast<InstanceOp>(resolved.getOp()));
200 auto mod = symTbl.lookup<FModuleLike>(path.module);
202 mlir::emitError(circuit.getLoc())
203 <<
"module doesn't exist '" << path.module <<
'\'';
209 if (isa<ClassOp>(mod)) {
210 mlir::emitError(mod.getLoc()) <<
"annotations cannot target classes";
215 if (path.
name.empty()) {
221 mlir::emitError(circuit.getLoc()) <<
"cannot find name '" << path.
name
222 <<
"' in " << mod.getModuleName();
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();
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()) {
250 }
else if (component.front().isIndex) {
251 mlir::emitError(circuit.getLoc())
252 <<
"illegal target '" << path.
str() <<
"' indexes into an instance";
255 auto field = component.front().name;
257 for (
size_t p = 0, pe =
getNumPorts(target); p < pe; ++p)
258 if (target.getPortName(p) == field) {
263 mlir::emitError(circuit.getLoc())
264 <<
"cannot find port '" << field <<
"' in module "
265 << target.getModuleName();
269 if (isa<RefType>(ref.
getType())) {
270 mlir::emitError(circuit.getLoc())
271 <<
"annotation cannot target reference-type port '" << field
272 <<
"' in module " << target.getModuleName();
275 component = component.drop_front();
284 auto fieldIdx = *result;
293 if (origTarget.empty())
295 StringRef target = origTarget;
297 std::tie(retval.
circuit, target) = target.split(
'|');
300 while (target.count(
':')) {
302 std::tie(nla, target) = target.split(
':');
304 std::tie(mod, inst) = nla.split(
'/');
305 retval.
instances.emplace_back(mod, inst);
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});
336 StringRef path{pathStr};
340 mlir::emitError(circuit.getLoc())
341 <<
"Cannot tokenize annotation path " << rawPath;
354 InstanceOp clonedInstOnPath;
356 auto portName = StringAttr::get(mod.getContext(), newName);
359 PortInfo portInfo = {portName, portType, dir, {}, mod.getLoc()};
360 mod.insertPorts({{portNo, portInfo}});
363 InstanceOp useInst = cast<InstanceOp>(use->getInstance());
365 useInst.cloneWithInsertedPortsAndReplaceUses({{portNo, portInfo}});
366 if (useInst == instOnPath)
367 clonedInstOnPath = clonedInst;
371 targetCaches->
replaceOp(useInst, clonedInst);
374 return clonedInstOnPath;
383 for (
const auto &p : llvm::enumerate(mod.getPorts()))
387 mod.walk([&](Operation *op) {
insertOp(op); });
395 : builder(OpBuilder::atBlockBegin(&op->getRegion(0).front())),
396 symbolTable(symbolTable) {
399 for (
auto ®ion : op->getRegions())
400 for (
auto &block : region.getBlocks())
401 for (
auto path : block.getOps<hw::HierPathOp>())
402 cache[path.getNamepathAttr()] = path;
406 auto &op =
cache[attr];
408 op = hw::HierPathOp::create(
builder, UnknownLoc::get(
builder.getContext()),
411 op.setVisibility(SymbolTable::Visibility::Private);
423 auto loc = state.
circuit.getLoc();
426 tryGetAs<StringAttr>(anno, anno,
"source", loc,
memTapClass);
431 std::optional<AnnoPathValue> srcTarget =
resolvePath(
434 return mlir::emitError(loc,
"cannot resolve source target path '")
435 << sourceTargetStr <<
"'";
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");
441 auto tap = dyn_cast_or_null<StringAttr>(tapsAttr[0]);
443 return mlir::emitError(
445 "' with path '.taps[0" +
446 "]' contained an unexpected type (expected a string).")
448 <<
"The full Annotation is reprodcued here: " << anno <<
"\n";
454 std::optional<AnnoPathValue> wireTarget =
resolvePath(
457 return mlir::emitError(loc,
"Annotation '" + Twine(
memTapClass) +
458 "' with path '.taps[0]' contains target '" +
460 "' that cannot be resolved.")
462 <<
"The full Annotation is reproduced here: " << anno <<
"\n";
464 auto combMem = dyn_cast<chirrtl::CombMemOp>(srcTarget->ref.getOp());
466 return srcTarget->ref.getOp()->emitOpError(
467 "unsupported operation, only CombMem can be used as the source of "
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()) {
477 combMem->getParentOfType<FModuleOp>());
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());
485 ImplicitLocOpBuilder builder(combMem->getLoc(), combMem);
492 builder.setInsertionPointToEnd(
493 combMem->getParentOfType<FModuleOp>().getBodyBlock());
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);
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)");
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)");
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());
534 auto sendVal = VectorCreateOp::create(
536 FVectorType::get(combMem.getType().getElementType(),
537 combMem.getType().getNumElements()),
539 auto sink = wireTarget->ref.getOp()->getResult(0);
548 builder.setInsertionPointAfter(combMem);
550 auto debugType = RefType::get(FVectorType::get(
551 combMem.getType().getElementType(), combMem.getType().getNumElements()));
553 chirrtl::MemoryDebugPortOp::create(
554 builder, debugType, combMem,
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 "
564 auto sink = wireTarget->ref.getOp()->getResult(0);
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.
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.
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 insertOp(Operation *op)
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.
Operation * getOp() const
State threaded through functions for resolving and applying annotations.
SmallVector< WiringProblem > wiringProblems
InstancePathCache & instancePathCache
CircuitTargetCache targetCaches
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)
SymbolTable & symbolTable
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.