16#include "mlir/IR/ImplicitLocOpBuilder.h"
17#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "lower-annos"
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;
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";
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) {
103 type = vector.getElementType();
104 fieldIdx += vector.getFieldID(*result);
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());
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>
170 if (!path.circuit.empty() && circuit.getName() != path.circuit) {
171 mlir::emitError(circuit.getLoc())
172 <<
"circuit name doesn't match annotation '" << path.circuit <<
'\'';
176 if (path.module.empty()) {
177 assert(path.name.empty() && path.instances.empty() &&
178 path.component.empty());
183 SmallVector<InstanceOp> instances;
184 for (
auto p : path.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()) {
216 assert(path.component.empty());
219 ref = cache.
lookup(mod, path.name);
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())),
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();
402 std::optional<AnnoPathValue> srcTarget =
resolvePath(
405 return mlir::emitError(loc,
"cannot resolve source target path '")
406 << sourceTargetStr <<
"'";
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() !=
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
BaseTy type_cast(Type type)
A tryGetAs(DictionaryAttr dict, Attribute root, StringRef key, Location loc, Twine clazz, Twine path=Twine())
Implements the same behavior as DictionaryAttr::getAs to return the value of a specific type associat...
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)
BaseTy type_dyn_cast(Type type)
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
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)