23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/APSInt.h"
26 #include "llvm/ADT/TypeSwitch.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Support/JSON.h"
30 #define DEBUG_TYPE "firrtl-resolve-traces"
34 #define GEN_PASS_DEF_RESOLVETRACES
35 #include "circt/Dialect/FIRRTL/Passes.h.inc"
39 using namespace circt;
40 using namespace firrtl;
50 auto *context = anno.getContext();
52 NamedAttrList trace, dontTouch;
53 for (
auto namedAttr : anno.getValue()) {
54 if (namedAttr.getName() ==
"class") {
59 trace.append(namedAttr);
64 if (namedAttr.getName() ==
"target" &&
65 !target.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>()) {
66 dontTouch.append(namedAttr);
67 state.
addToWorklistFn(DictionaryAttr::getWithSorted(context, dontTouch));
78 using ResolveTracesBase::outputAnnotationFilename;
80 void runOnOperation()
override;
88 hw::InnerSymbolTableCollection *
istc;
92 unsigned symbolIdx = 0;
103 auto iterator = symbolMap.find(attr);
104 if (iterator != symbolMap.end())
105 return iterator->getSecond();
107 auto idx = symbolIdx++;
108 symbolMap.insert({attr, idx});
109 symbols.push_back(attr);
117 auto addSymbol = [&](Attribute attr) ->
void {
118 newTarget.append(
"{{");
119 Twine(getSymbolIndex(attr)).toVector(newTarget);
120 newTarget.append(
"}}");
123 newTarget.append(
"~");
125 path.
ref.
getModule()->getParentOfType<CircuitOp>().getName());
126 newTarget.append(
"|");
133 ->getParentOfType<FModuleLike>()
134 .getModuleNameAttr()));
138 newTarget.append(
"/");
140 inst->getParentOfType<FModuleLike>().getModuleNameAttr(),
141 inst.getInnerSymAttr().getSymName()));
142 newTarget.append(
":");
143 addSymbol(inst.getModuleNameAttr());
148 if (isa<OpAnnoTarget>(path.
ref) &&
149 path.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>())
152 newTarget.append(
">");
154 TypeSwitch<AnnoTarget, StringAttr>(path.
ref)
157 portTarget.getPortNo(), portTarget.getModule(), 0));
163 assert(
false &&
"unexpected annotation target type");
169 auto type = dyn_cast<FIRRTLBaseType>(path.
ref.
getType());
170 assert(type &&
"expected a FIRRTLBaseType");
172 while (targetFieldID) {
174 .
Case<FVectorType>([&](FVectorType vector) {
175 auto index = vector.getIndexForFieldID(targetFieldID);
176 newTarget.append(
"[");
177 Twine(index).toVector(newTarget);
178 newTarget.append(
"]");
179 type = vector.getElementType();
180 targetFieldID -= vector.getFieldID(index);
182 .
template Case<BundleType>([&](BundleType bundle) {
183 auto index = bundle.getIndexForFieldID(targetFieldID);
184 newTarget.append(
".");
185 newTarget.append(bundle.getElementName(index));
186 type = bundle.getElementType(index);
187 targetFieldID -= bundle.getFieldID(index);
189 .Default([&](
auto) { targetFieldID = 0; });
200 SmallString<64> newTarget(
"~");
201 newTarget.append(module->getParentOfType<CircuitOp>().getName());
202 newTarget.append(
"|");
204 SmallVector<InstanceOp> instances;
206 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
207 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
208 for (
auto part : path.getNamepath().getValue().drop_back()) {
209 auto inst = cast<hw::InnerRefAttr>(part);
210 instances.push_back(dyn_cast<InstanceOp>(
211 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
212 .lookupOp(inst.getName())));
226 hw::InnerRefAttr innerRef) {
227 auto type =
getBaseType(type_cast<FIRRTLType>(module.getPortType(portIdx)));
228 return updateTargetImpl(anno, module, type, innerRef,
234 std::optional<AnnoPathValue>
updateTarget(FModuleLike &module, Operation *op,
236 hw::InnerRefAttr innerRef) {
241 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
243 if (is && is.getTargetResult())
244 type = is.getTargetResult().getType();
246 if (op->getNumResults() != 1)
248 type = op->getResultTypes().front();
251 auto baseType =
getBaseType(type_cast<FIRRTLType>(type));
252 return updateTargetImpl(anno, module, baseType, innerRef,
OpAnnoTarget(op));
260 SmallVector<InstanceOp> instances;
262 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
263 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
264 for (
auto part : path.getNamepath().getValue().drop_back()) {
265 auto inst = cast<hw::InnerRefAttr>(part);
266 instances.push_back(cast<InstanceOp>(
267 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
268 .lookupOp(inst.getName())));
280 llvm::dbgs() <<
"==----- Running ResolveTraces "
281 "-----------------------------------------------===\n"
282 <<
"Annotation Modifications:\n");
285 CircuitOp circuit = getOperation();
286 MLIRContext *context = circuit.getContext();
289 nlaTable = &getAnalysis<NLATable>();
290 istc = &getAnalysis<hw::InnerSymbolTableCollection>();
299 auto onModule = [&](FModuleLike moduleLike) {
301 SmallVector<std::pair<Annotation, AnnoPathValue>> outputAnnotations;
304 std::optional<hw::InnerSymbolNamespace> moduleNamespace;
307 auto getNamespace = [&](
auto module) -> hw::InnerSymbolNamespace & {
308 if (!moduleNamespace)
309 moduleNamespace = hw::InnerSymbolNamespace(module);
310 return *moduleNamespace;
318 auto path = updateModuleTarget(moduleLike, anno);
322 outputAnnotations.push_back({anno, *path});
328 moduleLike, [&](
unsigned portIdx,
Annotation anno) {
332 hw::InnerRefAttr innerRef =
334 auto path = updatePortTarget(moduleLike, anno, portIdx, innerRef);
338 outputAnnotations.push_back({anno, *path});
343 moduleLike.walk([&](Operation *component) {
348 hw::InnerRefAttr innerRef =
getInnerRefTo(component, getNamespace);
349 auto path = updateTarget(moduleLike, component, anno, innerRef);
353 outputAnnotations.push_back({anno, *path});
358 return outputAnnotations;
363 auto appendVecs = [](
auto &&a,
auto &&b) {
364 a.append(b.begin(), b.end());
365 return std::forward<decltype(a)>(a);
370 SmallVector<FModuleLike, 0> mods(circuit.getOps<FModuleLike>());
372 context, mods, SmallVector<std::pair<Annotation, AnnoPathValue>>{},
373 appendVecs, onModule);
376 if (outputAnnotations.empty())
377 return markAllAnalysesPreserved();
380 std::string jsonBuffer;
381 llvm::raw_string_ostream jsonStream(jsonBuffer);
382 llvm::json::OStream
json(jsonStream, 2);
384 for (
auto &[anno, path] : outputAnnotations) {
386 json.attribute(
"class", anno.getClass());
387 SmallString<64> targetStr;
388 buildTarget(path, targetStr);
391 <<
" - chiselTarget: "
392 << anno.getDict().getAs<StringAttr>(
"chiselTarget").getValue() <<
"\n"
393 <<
" target: " << targetStr <<
"\n"
394 <<
" translated: " << path <<
"\n";
396 json.attribute(
"target", targetStr);
397 json.attribute(
"chiselTarget",
398 anno.getMember<StringAttr>(
"chiselTarget").getValue());
404 llvm::dbgs() <<
"Symbols:\n";
405 for (
auto [
id, symbol] : llvm::enumerate(symbols))
406 llvm::errs() <<
" - " <<
id <<
": " << symbol <<
"\n";
411 auto builder = ImplicitLocOpBuilder::atBlockBegin(
UnknownLoc::get(context),
412 circuit.getBodyBlock());
415 if (this->outputAnnotationFilename.empty())
416 fileAttr = builder.getStringAttr(circuit.getName() +
".anno.json");
418 fileAttr = builder.getStringAttr(outputAnnotationFilename);
420 builder.create<emit::FileOp>(fileAttr, [&] {
421 builder.create<sv::VerbatimOp>(jsonBuffer, ValueRange{},
422 builder.getArrayAttr(symbols));
425 return markAllAnalysesPreserved();
428 std::unique_ptr<mlir::Pass>
430 auto pass = std::make_unique<ResolveTracesPass>();
431 if (!outputAnnotationFilename.empty())
432 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.