23#include "mlir/IR/ImplicitLocOpBuilder.h"
24#include "mlir/Pass/Pass.h"
25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/JSON.h"
29#define DEBUG_TYPE "firrtl-resolve-traces"
33#define GEN_PASS_DEF_RESOLVETRACES
34#include "circt/Dialect/FIRRTL/Passes.h.inc"
39using namespace firrtl;
49 auto *context = anno.getContext();
51 NamedAttrList trace, dontTouch;
52 for (
auto namedAttr : anno.getValue()) {
53 if (namedAttr.getName() ==
"class") {
58 trace.append(namedAttr);
63 if (namedAttr.getName() ==
"target" &&
64 !target.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>()) {
65 dontTouch.append(namedAttr);
66 state.
addToWorklistFn(DictionaryAttr::getWithSorted(context, dontTouch));
77 using ResolveTracesBase::outputAnnotationFilename;
104 return iterator->getSecond();
116 auto addSymbol = [&](Attribute attr) ->
void {
117 newTarget.append(
"{{");
119 newTarget.append(
"}}");
122 newTarget.append(
"~");
124 path.
ref.
getModule()->getParentOfType<CircuitOp>().getName());
125 newTarget.append(
"|");
129 FlatSymbolRefAttr::get(path.
ref.
getModule().getModuleNameAttr()));
131 addSymbol(FlatSymbolRefAttr::get(path.
instances.front()
132 ->getParentOfType<FModuleLike>()
133 .getModuleNameAttr()));
137 newTarget.append(
"/");
138 addSymbol(hw::InnerRefAttr::get(
139 inst->getParentOfType<FModuleLike>().getModuleNameAttr(),
140 inst.getInnerSymAttr().getSymName()));
141 newTarget.append(
":");
142 addSymbol(inst.getModuleNameAttr());
147 if (isa<OpAnnoTarget>(path.
ref) &&
148 path.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>())
151 newTarget.append(
">");
153 TypeSwitch<AnnoTarget, StringAttr>(path.
ref)
156 portTarget.getPortNo(), portTarget.getModule(), 0));
162 assert(
false &&
"unexpected annotation target type");
165 addSymbol(hw::InnerRefAttr::get(path.
ref.
getModule().getModuleNameAttr(),
168 auto type = dyn_cast<FIRRTLBaseType>(path.
ref.
getType());
169 assert(type &&
"expected a FIRRTLBaseType");
171 while (targetFieldID) {
173 .
Case<FVectorType>([&](FVectorType vector) {
174 auto index = vector.getIndexForFieldID(targetFieldID);
175 newTarget.append(
"[");
176 Twine(index).toVector(newTarget);
177 newTarget.append(
"]");
178 type = vector.getElementType();
179 targetFieldID -= vector.getFieldID(index);
181 .
template Case<BundleType>([&](BundleType bundle) {
182 auto index = bundle.getIndexForFieldID(targetFieldID);
183 newTarget.append(
".");
184 newTarget.append(bundle.getElementName(index));
185 type = bundle.getElementType(index);
186 targetFieldID -= bundle.getFieldID(index);
188 .Default([&](
auto) { targetFieldID = 0; });
199 SmallString<64> newTarget(
"~");
200 newTarget.append(module->getParentOfType<CircuitOp>().getName());
201 newTarget.append(
"|");
203 SmallVector<InstanceOp> instances;
205 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
207 for (
auto part : path.getNamepath().getValue().drop_back()) {
208 auto inst = cast<hw::InnerRefAttr>(part);
209 instances.push_back(dyn_cast<InstanceOp>(
211 .lookupOp(inst.getName())));
225 hw::InnerRefAttr innerRef) {
226 auto type =
getBaseType(type_cast<FIRRTLType>(module.getPortType(portIdx)));
233 std::optional<AnnoPathValue>
updateTarget(FModuleLike &module, Operation *op,
235 hw::InnerRefAttr innerRef) {
240 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
242 if (is && is.getTargetResult())
243 type = is.getTargetResult().getType();
245 if (op->getNumResults() != 1)
247 type = op->getResultTypes().front();
250 auto baseType =
getBaseType(type_cast<FIRRTLType>(type));
259 SmallVector<InstanceOp> instances;
261 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
263 for (
auto part : path.getNamepath().getValue().drop_back()) {
264 auto inst = cast<hw::InnerRefAttr>(part);
265 instances.push_back(cast<InstanceOp>(
267 .lookupOp(inst.getName())));
279 llvm::dbgs() <<
"==----- Running ResolveTraces "
280 "-----------------------------------------------===\n"
281 <<
"Annotation Modifications:\n");
284 CircuitOp circuit = getOperation();
285 MLIRContext *context = circuit.getContext();
288 nlaTable = &getAnalysis<NLATable>();
289 istc = &getAnalysis<hw::InnerSymbolTableCollection>();
298 auto onModule = [&](FModuleLike moduleLike) {
300 SmallVector<std::pair<Annotation, AnnoPathValue>> outputAnnotations;
303 std::optional<hw::InnerSymbolNamespace> moduleNamespace;
307 if (!moduleNamespace)
309 return *moduleNamespace;
321 outputAnnotations.push_back({anno, *path});
327 moduleLike, [&](
unsigned portIdx,
Annotation anno) {
331 hw::InnerRefAttr innerRef =
337 outputAnnotations.push_back({anno, *path});
342 moduleLike.walk([&](Operation *component) {
347 hw::InnerRefAttr innerRef =
getInnerRefTo(component, getNamespace);
348 auto path =
updateTarget(moduleLike, component, anno, innerRef);
352 outputAnnotations.push_back({anno, *path});
357 return outputAnnotations;
362 auto appendVecs = [](
auto &&a,
auto &&b) {
363 a.append(b.begin(), b.end());
364 return std::forward<decltype(a)>(a);
369 SmallVector<FModuleLike, 0> mods(circuit.getOps<FModuleLike>());
371 context, mods, SmallVector<std::pair<Annotation, AnnoPathValue>>{},
372 appendVecs, onModule);
375 if (outputAnnotations.empty())
376 return markAllAnalysesPreserved();
379 std::string jsonBuffer;
380 llvm::raw_string_ostream jsonStream(jsonBuffer);
381 llvm::json::OStream
json(jsonStream, 2);
383 for (
auto &[anno, path] : outputAnnotations) {
385 json.attribute(
"class", anno.getClass());
386 SmallString<64> targetStr;
390 <<
" - chiselTarget: "
391 << anno.getDict().getAs<StringAttr>(
"chiselTarget").getValue() <<
"\n"
392 <<
" target: " << targetStr <<
"\n"
393 <<
" translated: " << path <<
"\n";
395 json.attribute(
"target", targetStr);
396 json.attribute(
"chiselTarget",
397 anno.getMember<StringAttr>(
"chiselTarget").getValue());
403 llvm::dbgs() <<
"Symbols:\n";
404 for (
auto [
id, symbol] : llvm::enumerate(
symbols))
405 llvm::errs() <<
" - " <<
id <<
": " << symbol <<
"\n";
410 auto builder = ImplicitLocOpBuilder::atBlockBegin(UnknownLoc::get(context),
411 circuit.getBodyBlock());
414 if (this->outputAnnotationFilename.empty())
415 fileAttr = builder.getStringAttr(circuit.getName() +
".anno.json");
417 fileAttr = builder.getStringAttr(outputAnnotationFilename);
419 builder.create<emit::FileOp>(fileAttr, [&] {
420 builder.create<sv::VerbatimOp>(jsonBuffer, ValueRange{},
421 builder.getArrayAttr(
symbols));
424 return markAllAnalysesPreserved();
427std::unique_ptr<mlir::Pass>
429 auto pass = std::make_unique<ResolveTracesPass>();
430 if (!outputAnnotationFilename.empty())
431 pass->outputAnnotationFilename = outputAnnotationFilename.str();
assert(baseType &&"element must be base type")
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
unsigned getFieldID() const
Get the field id this attribute targets.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This table tracks nlas and what modules participate in them.
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
FModuleLike getModule(StringAttr name)
Resolve a symbol to a Module.
The target of an inner symbol, the entity the symbol is a handle for.
This class represents a collection of InnerSymbolTable's.
static StringAttr getInnerSymbol(Operation *op)
Get InnerSymbol for an operation.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * traceAnnoClass
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
LogicalResult applyTraceName(const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state)
Expand a TraceNameAnnotation (which has don't touch semantics) into a TraceAnnotation (which does NOT...
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
std::unique_ptr< mlir::Pass > createResolveTracesPass(mlir::StringRef outputAnnotationFilename="")
constexpr const char * dontTouchAnnoClass
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::optional< AnnoPathValue > updatePortTarget(FModuleLike &module, Annotation &anno, unsigned portIdx, hw::InnerRefAttr innerRef)
Add a "target" field to a port Annotation that indicates the current location of the port in the circ...
void runOnOperation() override
unsigned symbolIdx
Global symbol index used for substitutions, e.g., "{{42}}".
std::optional< AnnoPathValue > updateTarget(FModuleLike &module, Operation *op, Annotation &anno, hw::InnerRefAttr innerRef)
Add a "target" field to an Annotation that indicates the current location of a component in the circu...
AnnoPathValue updateTargetImpl(Annotation &anno, FModuleLike &module, FIRRTLBaseType type, hw::InnerRefAttr name, AnnoTarget target)
Internal implementation that updates an Annotation to add a "target" field based on the current locat...
std::optional< AnnoPathValue > updateModuleTarget(FModuleLike &module, Annotation &anno)
Add a "target" field to an Annotation on a Module that indicates the current location of the module.
NLATable * nlaTable
Stores a pointer to an NLA Table.
hw::InnerSymbolTableCollection * istc
Stores a pointer to an inner symbol table collection.
unsigned getSymbolIndex(Attribute attr)
Get a symbol index and update symbol datastructures.
void buildTarget(AnnoPathValue &path, SmallString< 64 > &newTarget)
Convert an annotation path to a string with symbol substitutions.
SmallVector< Attribute > symbols
Symbol substitutions for the JSON verbatim op.
DenseMap< Attribute, unsigned > symbolMap
Map of symbol to symbol index.
SmallVector< InstanceOp > instances
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
FModuleLike getModule() const
Get the parent module of the target.
State threaded through functions for resolving and applying annotations.
AddToWorklistFn addToWorklistFn
This represents an annotation targeting a specific operation.
This represents an annotation targeting a specific port of a module, memory, or instance.