23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 #include "llvm/ADT/APSInt.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"
31 using namespace circt;
32 using namespace firrtl;
42 auto *context = anno.getContext();
44 NamedAttrList trace, dontTouch;
45 for (
auto namedAttr : anno.getValue()) {
46 if (namedAttr.getName() ==
"class") {
51 trace.append(namedAttr);
56 if (namedAttr.getName() ==
"target" &&
57 !target.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>()) {
58 dontTouch.append(namedAttr);
59 state.
addToWorklistFn(DictionaryAttr::getWithSorted(context, dontTouch));
69 using ResolveTracesBase::outputAnnotationFilename;
71 void runOnOperation()
override;
79 hw::InnerSymbolTableCollection *
istc;
83 unsigned symbolIdx = 0;
94 auto iterator = symbolMap.find(attr);
95 if (iterator != symbolMap.end())
96 return iterator->getSecond();
98 auto idx = symbolIdx++;
99 symbolMap.insert({attr, idx});
100 symbols.push_back(attr);
108 auto addSymbol = [&](Attribute attr) ->
void {
109 newTarget.append(
"{{");
110 Twine(getSymbolIndex(attr)).toVector(newTarget);
111 newTarget.append(
"}}");
114 newTarget.append(
"~");
116 path.
ref.
getModule()->getParentOfType<CircuitOp>().getName());
117 newTarget.append(
"|");
124 ->getParentOfType<FModuleLike>()
125 .getModuleNameAttr()));
129 newTarget.append(
"/");
131 inst->getParentOfType<FModuleLike>().getModuleNameAttr(),
132 inst.getInnerSymAttr().getSymName()));
133 newTarget.append(
":");
134 addSymbol(inst.getModuleNameAttr());
140 path.
isOpOfType<FModuleOp, FExtModuleOp, InstanceOp>())
143 newTarget.append(
">");
145 TypeSwitch<AnnoTarget, StringAttr>(path.
ref)
148 portTarget.getPortNo(), portTarget.getModule(), 0));
154 assert(
false &&
"unexpected annotation target type");
160 auto type = dyn_cast<FIRRTLBaseType>(path.
ref.
getType());
161 assert(type &&
"expected a FIRRTLBaseType");
163 while (targetFieldID) {
165 .
Case<FVectorType>([&](FVectorType vector) {
166 auto index = vector.getIndexForFieldID(targetFieldID);
167 newTarget.append(
"[");
168 Twine(index).toVector(newTarget);
169 newTarget.append(
"]");
170 type = vector.getElementType();
171 targetFieldID -= vector.getFieldID(index);
173 .
template Case<BundleType>([&](BundleType bundle) {
174 auto index = bundle.getIndexForFieldID(targetFieldID);
175 newTarget.append(
".");
176 newTarget.append(bundle.getElementName(index));
177 type = bundle.getElementType(index);
178 targetFieldID -= bundle.getFieldID(index);
180 .Default([&](
auto) { targetFieldID = 0; });
191 SmallString<64> newTarget(
"~");
192 newTarget.append(module->getParentOfType<CircuitOp>().getName());
193 newTarget.append(
"|");
195 SmallVector<InstanceOp> instances;
197 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
198 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
199 for (
auto part : path.getNamepath().getValue().drop_back()) {
200 auto inst = cast<hw::InnerRefAttr>(part);
201 instances.push_back(dyn_cast<InstanceOp>(
202 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
203 .lookupOp(inst.getName())));
217 hw::InnerRefAttr innerRef) {
218 auto type =
getBaseType(type_cast<FIRRTLType>(module.getPortType(portIdx)));
219 return updateTargetImpl(anno, module, type, innerRef,
225 std::optional<AnnoPathValue>
updateTarget(FModuleLike &module, Operation *op,
227 hw::InnerRefAttr innerRef) {
232 auto is = dyn_cast<hw::InnerSymbolOpInterface>(op);
234 if (is && is.getTargetResult())
235 type = is.getTargetResult().getType();
237 if (op->getNumResults() != 1)
239 type = op->getResultTypes().front();
242 auto baseType =
getBaseType(type_cast<FIRRTLType>(type));
243 return updateTargetImpl(anno, module, baseType, innerRef,
OpAnnoTarget(op));
251 SmallVector<InstanceOp> instances;
253 if (
auto nla = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal")) {
254 hw::HierPathOp path = nlaTable->
getNLA(nla.getAttr());
255 for (
auto part : path.getNamepath().getValue().drop_back()) {
256 auto inst = cast<hw::InnerRefAttr>(part);
257 instances.push_back(cast<InstanceOp>(
258 istc->getInnerSymbolTable(nlaTable->
getModule(inst.getModule()))
259 .lookupOp(inst.getName())));
271 llvm::dbgs() <<
"==----- Running ResolveTraces "
272 "-----------------------------------------------===\n"
273 <<
"Annotation Modifications:\n");
276 CircuitOp circuit = getOperation();
277 MLIRContext *context = circuit.getContext();
280 nlaTable = &getAnalysis<NLATable>();
281 istc = &getAnalysis<hw::InnerSymbolTableCollection>();
290 auto onModule = [&](FModuleLike moduleLike) {
292 SmallVector<std::pair<Annotation, AnnoPathValue>> outputAnnotations;
295 std::optional<hw::InnerSymbolNamespace> moduleNamespace;
298 auto getNamespace = [&](
auto module) -> hw::InnerSymbolNamespace & {
299 if (!moduleNamespace)
300 moduleNamespace = hw::InnerSymbolNamespace(module);
301 return *moduleNamespace;
309 auto path = updateModuleTarget(moduleLike, anno);
313 outputAnnotations.push_back({anno, *path});
319 moduleLike, [&](
unsigned portIdx,
Annotation anno) {
323 hw::InnerRefAttr innerRef =
325 auto path = updatePortTarget(moduleLike, anno, portIdx, innerRef);
329 outputAnnotations.push_back({anno, *path});
334 moduleLike.walk([&](Operation *component) {
339 hw::InnerRefAttr innerRef =
getInnerRefTo(component, getNamespace);
340 auto path = updateTarget(moduleLike, component, anno, innerRef);
344 outputAnnotations.push_back({anno, *path});
349 return outputAnnotations;
354 auto appendVecs = [](
auto &&a,
auto &&b) {
355 a.append(b.begin(), b.end());
356 return std::forward<decltype(a)>(a);
361 SmallVector<FModuleLike, 0> mods(circuit.getOps<FModuleLike>());
363 context, mods, SmallVector<std::pair<Annotation, AnnoPathValue>>{},
364 appendVecs, onModule);
367 if (outputAnnotations.empty())
368 return markAllAnalysesPreserved();
371 std::string jsonBuffer;
372 llvm::raw_string_ostream jsonStream(jsonBuffer);
373 llvm::json::OStream
json(jsonStream, 2);
375 for (
auto &[anno, path] : outputAnnotations) {
377 json.attribute(
"class", anno.getClass());
378 SmallString<64> targetStr;
379 buildTarget(path, targetStr);
382 <<
" - chiselTarget: "
383 << anno.getDict().getAs<StringAttr>(
"chiselTarget").getValue() <<
"\n"
384 <<
" target: " << targetStr <<
"\n"
385 <<
" translated: " << path <<
"\n";
387 json.attribute(
"target", targetStr);
388 json.attribute(
"chiselTarget",
389 anno.getMember<StringAttr>(
"chiselTarget").getValue());
396 for (
auto [
id, symbol] : llvm::enumerate(symbols))
397 llvm::errs() <<
" - " <<
id <<
": " << symbol <<
"\n";
403 auto b = OpBuilder::atBlockBegin(circuit.getBodyBlock());
404 auto verbatimOp = b.create<sv::VerbatimOp>(
405 b.getUnknownLoc(), jsonBuffer, ValueRange{}, b.getArrayAttr(symbols));
406 hw::OutputFileAttr fileAttr;
407 if (this->outputAnnotationFilename.empty())
408 fileAttr = hw::OutputFileAttr::getFromFilename(
409 context, circuit.getName() +
".anno.json",
412 fileAttr = hw::OutputFileAttr::getFromFilename(
413 context, outputAnnotationFilename,
415 verbatimOp->setAttr(
"output_file", fileAttr);
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
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
mlir::raw_indented_ostream & errs()
mlir::raw_indented_ostream & dbgs()
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.