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 for (
const auto &p : llvm::enumerate(mod.getPorts()))
358 mod.walk([&](Operation *op) {
insertOp(op); });
366 : builder(OpBuilder::atBlockBegin(&op->getRegion(0).front())),
367 symbolTable(symbolTable) {
370 for (
auto ®ion : op->getRegions())
371 for (
auto &block : region.getBlocks())
372 for (
auto path : block.getOps<hw::HierPathOp>())
373 cache[path.getNamepathAttr()] = path;
377 auto &op =
cache[attr];
379 op = hw::HierPathOp::create(
builder, UnknownLoc::get(
builder.getContext()),
382 op.setVisibility(SymbolTable::Visibility::Private);
394 auto loc = state.
circuit.getLoc();
397 tryGetAs<StringAttr>(anno, anno,
"source", loc, memTapAnnoClass);
402 std::optional<AnnoPathValue> srcTarget =
resolvePath(
405 return mlir::emitError(loc,
"cannot resolve source target path '")
406 << sourceTargetStr <<
"'";
408 auto tapsAttr = tryGetAs<ArrayAttr>(anno, anno,
"sink", loc, memTapAnnoClass);
409 if (!tapsAttr || tapsAttr.empty())
410 return mlir::emitError(loc,
"sink must have at least one entry");
412 auto tap = dyn_cast_or_null<StringAttr>(tapsAttr[0]);
414 return mlir::emitError(
415 loc,
"Annotation '" + Twine(memTapAnnoClass) +
416 "' with path '.taps[0" +
417 "]' contained an unexpected type (expected a string).")
419 <<
"The full Annotation is reprodcued here: " << anno <<
"\n";
425 std::optional<AnnoPathValue> wireTarget =
resolvePath(
428 return mlir::emitError(loc,
"Annotation '" + Twine(memTapAnnoClass) +
429 "' with path '.taps[0]' contains target '" +
431 "' that cannot be resolved.")
433 <<
"The full Annotation is reproduced here: " << anno <<
"\n";
435 auto combMem = dyn_cast<chirrtl::CombMemOp>(srcTarget->ref.getOp());
437 return srcTarget->ref.getOp()->emitOpError(
438 "unsupported operation, only CombMem can be used as the source of "
440 if (!combMem.getType().getElementType().isGround())
441 return combMem.emitOpError(
442 "cannot generate MemTap to a memory with aggregate data type");
443 if (tapsAttr.size() != combMem.getType().getNumElements())
444 return mlir::emitError(
445 loc,
"sink cannot specify more taps than the depth of the memory");
446 if (srcTarget->instances.empty()) {
448 combMem->getParentOfType<FModuleOp>());
450 return combMem.emitOpError(
451 "cannot be resolved as source for MemTap, multiple paths from top "
452 "exist and unique instance cannot be resolved");
453 srcTarget->instances.append(path.back().begin(), path.back().end());
456 ImplicitLocOpBuilder builder(combMem->getLoc(), combMem);
463 builder.setInsertionPointToEnd(
464 combMem->getParentOfType<FModuleOp>().getBodyBlock());
469 for (
auto *portOp : combMem.getResult().getUsers()) {
470 for (
auto result : portOp->getResults()) {
471 for (
auto *user : result.getUsers()) {
472 auto accessOp = dyn_cast<chirrtl::MemoryPortAccessOp>(user);
475 auto newClock = accessOp.getClock();
476 if (clock && clock != newClock)
477 return combMem.emitOpError(
478 "has different clocks on different ports (this is ambiguous "
479 "when compiling without reference types)");
485 return combMem.emitOpError(
486 "does not have an access port to determine a clock connection (this "
487 "is necessary when compiling without reference types)");
490 SmallVector<Value>
data;
491 Type uintType = builder.getType<UIntType>();
492 for (uint64_t i = 0, e = combMem.getType().getNumElements(); i != e; ++i) {
493 auto port = chirrtl::MemoryPortOp::create(
494 builder, combMem.getType().getElementType(),
495 CMemoryPortType::get(builder.getContext()), combMem.getResult(),
496 MemDirAttr::Read, builder.getStringAttr(
"memTap_" + Twine(i)),
497 builder.getArrayAttr({}));
498 chirrtl::MemoryPortAccessOp::create(
499 builder, port.getPort(),
500 ConstantOp::create(builder, uintType, APSInt::getUnsigned(i)), clock);
501 data.push_back(port.getData());
505 auto sendVal = VectorCreateOp::create(
507 FVectorType::get(combMem.getType().getElementType(),
508 combMem.getType().getNumElements()),
510 auto sink = wireTarget->ref.getOp()->getResult(0);
519 builder.setInsertionPointAfter(combMem);
521 auto debugType = RefType::get(FVectorType::get(
522 combMem.getType().getElementType(), combMem.getType().getNumElements()));
524 chirrtl::MemoryDebugPortOp::create(
525 builder, debugType, combMem,
529 auto sendVal = memDbgPort;
530 if (wireTarget->ref.getOp()->getResult(0).getType() !=
531 type_cast<RefType>(sendVal.getType()).getType())
532 return wireTarget->ref.getOp()->emitError(
533 "cannot generate the MemTap, wiretap Type does not match the memory "
535 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.
std::optional< AnnoPathValue > resolveEntities(TokenAnnoTarget path, CircuitOp circuit, SymbolTable &symTbl, CircuitTargetCache &cache)
Convert a parsed target string to a resolved target structure.
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.
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.
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.
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
ArrayRef< InstancePath > getAbsolutePaths(ModuleOpInterface op)