24 #include "mlir/IR/ImplicitLocOpBuilder.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"
32 using namespace circt;
33 using namespace firrtl;
43 auto *context = anno.getContext();
45 NamedAttrList trace, dontTouch;
46 for (
auto namedAttr : anno.getValue()) {
47 if (namedAttr.getName() ==
"class") {
52 trace.append(namedAttr);
57 if (namedAttr.getName() ==
"target" &&
58 !target.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>()) {
59 dontTouch.append(namedAttr);
60 state.
addToWorklistFn(DictionaryAttr::getWithSorted(context, dontTouch));
70 using ResolveTracesBase::outputAnnotationFilename;
72 void runOnOperation()
override;
80 hw::InnerSymbolTableCollection *
istc;
84 unsigned symbolIdx = 0;
95 auto iterator = symbolMap.find(attr);
96 if (iterator != symbolMap.end())
97 return iterator->getSecond();
99 auto idx = symbolIdx++;
100 symbolMap.insert({attr, idx});
101 symbols.push_back(attr);
109 auto addSymbol = [&](Attribute attr) ->
void {
110 newTarget.append(
"{{");
111 Twine(getSymbolIndex(attr)).toVector(newTarget);
112 newTarget.append(
"}}");
115 newTarget.append(
"~");
117 path.
ref.
getModule()->getParentOfType<CircuitOp>().getName());
118 newTarget.append(
"|");
125 ->getParentOfType<FModuleLike>()
126 .getModuleNameAttr()));
130 newTarget.append(
"/");
132 inst->getParentOfType<FModuleLike>().getModuleNameAttr(),
133 inst.getInnerSymAttr().getSymName()));
134 newTarget.append(
":");
135 addSymbol(inst.getModuleNameAttr());
141 path.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>())
144 newTarget.append(
">");
146 TypeSwitch<AnnoTarget, StringAttr>(path.
ref)
149 portTarget.getPortNo(), portTarget.getModule(), 0));
155 assert(
false &&
"unexpected annotation target type");
161 auto type = dyn_cast<FIRRTLBaseType>(path.
ref.
getType());
162 assert(type &&
"expected a FIRRTLBaseType");
164 while (targetFieldID) {
166 .
Case<FVectorType>([&](FVectorType vector) {
167 auto index = vector.getIndexForFieldID(targetFieldID);
168 newTarget.append(
"[");
169 Twine(index).toVector(newTarget);
170 newTarget.append(
"]");
171 type = vector.getElementType();
172 targetFieldID -= vector.getFieldID(index);
174 .
template Case<BundleType>([&](BundleType bundle) {
175 auto index = bundle.getIndexForFieldID(targetFieldID);
176 newTarget.append(
".");
177 newTarget.append(bundle.getElementName(index));
178 type = bundle.getElementType(index);
179 targetFieldID -= bundle.getFieldID(index);
181 .Default([&](
auto) { targetFieldID = 0; });
192 SmallString<64> newTarget(
"~");
193 newTarget.append(module->getParentOfType<CircuitOp>().getName());
194 newTarget.append(
"|");
196 SmallVector<InstanceOp> instances;
198 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
199 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
200 for (
auto part : path.getNamepath().getValue().drop_back()) {
201 auto inst = cast<hw::InnerRefAttr>(part);
202 instances.push_back(dyn_cast<InstanceOp>(
203 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
204 .lookupOp(inst.getName())));
218 hw::InnerRefAttr innerRef) {
219 auto type =
getBaseType(type_cast<FIRRTLType>(module.getPortType(portIdx)));
220 return updateTargetImpl(anno, module, type, innerRef,
226 std::optional<AnnoPathValue>
updateTarget(FModuleLike &module, Operation *op,
228 hw::InnerRefAttr innerRef) {
233 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
235 if (is && is.getTargetResult())
236 type = is.getTargetResult().getType();
238 if (op->getNumResults() != 1)
240 type = op->getResultTypes().front();
243 auto baseType =
getBaseType(type_cast<FIRRTLType>(type));
244 return updateTargetImpl(anno, module, baseType, innerRef,
OpAnnoTarget(op));
252 SmallVector<InstanceOp> instances;
254 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
255 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
256 for (
auto part : path.getNamepath().getValue().drop_back()) {
257 auto inst = cast<hw::InnerRefAttr>(part);
258 instances.push_back(cast<InstanceOp>(
259 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
260 .lookupOp(inst.getName())));
272 llvm::dbgs() <<
"==----- Running ResolveTraces "
273 "-----------------------------------------------===\n"
274 <<
"Annotation Modifications:\n");
277 CircuitOp circuit = getOperation();
278 MLIRContext *context = circuit.getContext();
281 nlaTable = &getAnalysis<NLATable>();
282 istc = &getAnalysis<hw::InnerSymbolTableCollection>();
291 auto onModule = [&](FModuleLike moduleLike) {
293 SmallVector<std::pair<Annotation, AnnoPathValue>> outputAnnotations;
296 std::optional<hw::InnerSymbolNamespace> moduleNamespace;
299 auto getNamespace = [&](
auto module) -> hw::InnerSymbolNamespace & {
300 if (!moduleNamespace)
301 moduleNamespace = hw::InnerSymbolNamespace(module);
302 return *moduleNamespace;
310 auto path = updateModuleTarget(moduleLike, anno);
314 outputAnnotations.push_back({anno, *path});
320 moduleLike, [&](
unsigned portIdx,
Annotation anno) {
324 hw::InnerRefAttr innerRef =
326 auto path = updatePortTarget(moduleLike, anno, portIdx, innerRef);
330 outputAnnotations.push_back({anno, *path});
335 moduleLike.walk([&](Operation *component) {
340 hw::InnerRefAttr innerRef =
getInnerRefTo(component, getNamespace);
341 auto path = updateTarget(moduleLike, component, anno, innerRef);
345 outputAnnotations.push_back({anno, *path});
350 return outputAnnotations;
355 auto appendVecs = [](
auto &&a,
auto &&b) {
356 a.append(b.begin(), b.end());
357 return std::forward<decltype(a)>(a);
362 SmallVector<FModuleLike, 0> mods(circuit.getOps<FModuleLike>());
364 context, mods, SmallVector<std::pair<Annotation, AnnoPathValue>>{},
365 appendVecs, onModule);
368 if (outputAnnotations.empty())
369 return markAllAnalysesPreserved();
372 std::string jsonBuffer;
373 llvm::raw_string_ostream jsonStream(jsonBuffer);
374 llvm::json::OStream
json(jsonStream, 2);
376 for (
auto &[anno, path] : outputAnnotations) {
378 json.attribute(
"class", anno.getClass());
379 SmallString<64> targetStr;
380 buildTarget(path, targetStr);
383 <<
" - chiselTarget: "
384 << anno.getDict().getAs<StringAttr>(
"chiselTarget").getValue() <<
"\n"
385 <<
" target: " << targetStr <<
"\n"
386 <<
" translated: " << path <<
"\n";
388 json.attribute(
"target", targetStr);
389 json.attribute(
"chiselTarget",
390 anno.getMember<StringAttr>(
"chiselTarget").getValue());
396 llvm::dbgs() <<
"Symbols:\n";
397 for (
auto [
id, symbol] : llvm::enumerate(symbols))
398 llvm::errs() <<
" - " <<
id <<
": " << symbol <<
"\n";
404 circuit.getBodyBlock());
407 if (this->outputAnnotationFilename.empty())
408 fileAttr =
builder.getStringAttr(circuit.getName() +
".anno.json");
410 fileAttr =
builder.getStringAttr(outputAnnotationFilename);
412 builder.create<emit::FileOp>(fileAttr, [&] {
413 builder.create<sv::VerbatimOp>(jsonBuffer, ValueRange{},
414 builder.getArrayAttr(symbols));
417 return markAllAnalysesPreserved();
420 std::unique_ptr<mlir::Pass>
422 auto pass = std::make_unique<ResolveTracesPass>();
423 if (!outputAnnotationFilename.empty())
424 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.