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"
38 using namespace circt;
39 using 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;
79 void runOnOperation()
override;
87 hw::InnerSymbolTableCollection *
istc;
91 unsigned symbolIdx = 0;
102 auto iterator = symbolMap.find(attr);
103 if (iterator != symbolMap.end())
104 return iterator->getSecond();
106 auto idx = symbolIdx++;
107 symbolMap.insert({attr, idx});
108 symbols.push_back(attr);
116 auto addSymbol = [&](Attribute attr) ->
void {
117 newTarget.append(
"{{");
118 Twine(getSymbolIndex(attr)).toVector(newTarget);
119 newTarget.append(
"}}");
122 newTarget.append(
"~");
124 path.
ref.
getModule()->getParentOfType<CircuitOp>().getName());
125 newTarget.append(
"|");
132 ->getParentOfType<FModuleLike>()
133 .getModuleNameAttr()));
137 newTarget.append(
"/");
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");
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")) {
206 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
207 for (
auto part : path.getNamepath().getValue().drop_back()) {
208 auto inst = cast<hw::InnerRefAttr>(part);
209 instances.push_back(dyn_cast<InstanceOp>(
210 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
211 .lookupOp(inst.getName())));
225 hw::InnerRefAttr innerRef) {
226 auto type =
getBaseType(type_cast<FIRRTLType>(module.getPortType(portIdx)));
227 return updateTargetImpl(anno, module, type, innerRef,
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));
251 return updateTargetImpl(anno, module, baseType, innerRef,
OpAnnoTarget(op));
259 SmallVector<InstanceOp> instances;
261 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
262 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
263 for (
auto part : path.getNamepath().getValue().drop_back()) {
264 auto inst = cast<hw::InnerRefAttr>(part);
265 instances.push_back(cast<InstanceOp>(
266 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
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;
306 auto getNamespace = [&](
auto module) -> hw::InnerSymbolNamespace & {
307 if (!moduleNamespace)
308 moduleNamespace = hw::InnerSymbolNamespace(module);
309 return *moduleNamespace;
317 auto path = updateModuleTarget(moduleLike, anno);
321 outputAnnotations.push_back({anno, *path});
327 moduleLike, [&](
unsigned portIdx,
Annotation anno) {
331 hw::InnerRefAttr innerRef =
333 auto path = updatePortTarget(moduleLike, anno, portIdx, 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;
387 buildTarget(path, 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();
427 std::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.
static StringAttr getInnerSymbol(Operation *op)
Get InnerSymbol for an operation.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
std::unique_ptr< mlir::Pass > createResolveTracesPass(mlir::StringRef outputAnnotationFilename="")
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...
constexpr const char * dontTouchAnnoClass
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void runOnOperation() override
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 > 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...
NLATable * nlaTable
Stores a pointer to an NLA Table.
hw::InnerSymbolTableCollection * istc
Stores a pointer to an inner symbol table collection.
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.
unsigned getSymbolIndex(Attribute attr)
Get a symbol index and update symbol datastructures.
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 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.