24#include "mlir/IR/BuiltinOps.h"
25#include "mlir/Pass/AnalysisManager.h"
26#include "mlir/Support/FileUtilities.h"
27#include "llvm/ADT/ScopeExit.h"
28#include "llvm/Support/JSON.h"
29#include "llvm/Support/ToolOutputFile.h"
33#define GEN_PASS_DEF_PRINTRESOURCEUSAGEANALYSIS
34#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
48 llvm::StringMap<uint64_t> &counts) {
51 if (
auto memory = dyn_cast<seq::FirMemOp>(op)) {
52 auto type = memory.getMemory().getType();
53 counts[op->getName().getStringRef()] += type.getDepth() * type.getWidth();
56 if (isa<seq::FirMemReadOp, seq::FirMemWriteOp, seq::FirMemReadWriteOp>(op))
58 if (op->getNumResults() != 1 || !op->getResult(0).getType().isInteger())
60 return TypeSwitch<Operation *, bool>(op)
61 .Case<BooleanLogicOpInterface>([&](
auto logicOp) {
62 if (
auto areaCost = logicOp.getLogicAreaCost()) {
63 counts[op->getName().getStringRef()] += *areaCost;
70 .Case<comb::AndOp, comb::OrOp, comb::XorOp>([&](
auto logicOp) {
71 counts[logicOp->getName().getStringRef()] +=
72 static_cast<uint64_t
>(logicOp.getNumOperands() - 1) *
73 logicOp.getType().getIntOrFloatBitWidth();
78 .Case<comb::TruthTableOp>([&](
auto op) {
79 uint64_t count = op.getType().getIntOrFloatBitWidth();
80 counts[op->getName().getStringRef()] += count;
81 std::string bucket = (Twine(op->getName().getStringRef()) +
"_" +
82 Twine(op.getNumOperands()))
84 counts[bucket] += count;
89 .Case<seq::CompRegOp, seq::FirRegOp>([&](
auto op) {
90 uint64_t count = op.getType().getIntOrFloatBitWidth();
91 counts[op->getName().getStringRef()] += count;
94 .Default([](Operation *) {
return false; });
98 mlir::AnalysisManager &am)
106 return it->second.get();
121 return cacheIt->second.get();
123 auto *node =
instanceGraph->lookup(module.getModuleNameAttr());
126 llvm::StringMap<uint64_t> counts;
127 uint64_t unknownOpCount = 0;
128 module->walk([&](Operation *op) {
129 if (accumulateResourceCounts(op, counts))
131 if (op->getNumResults() > 0 && !isa<hw::HWInstanceLike>(op) &&
132 !op->hasTrait<mlir::OpTrait::ConstantLike>()) {
139 if (unknownOpCount > 0)
140 counts[
"<unknown>"] = unknownOpCount;
144 ResourceUsage local(std::move(counts));
145 auto moduleUsage = std::make_unique<ModuleResourceUsage>(
146 module.getModuleNameAttr(), local, local);
149 for (
auto *child : *node) {
150 auto *targetNode = child->getTarget();
152 auto childModule = targetNode->getModule();
154 auto *instanceOp = child->getInstance().getOperation();
156 if (instanceOp->getNumResults() == 0 ||
157 instanceOp->hasAttrOfType<UnitAttr>(
"doNotPrint"))
161 auto *childUsage = getResourceUsage(childModule);
162 moduleUsage->total += childUsage->total;
163 moduleUsage->instances.emplace_back(
164 childModule.getModuleNameAttr(),
165 child->getInstance().getInstanceNameAttr(), childUsage);
169 auto [it,
success] = designUsageCache.try_emplace(module.getModuleNameAttr(),
170 std::move(moduleUsage));
173 return it->second.get();
181static llvm::json::Object
183 llvm::json::Object obj;
184 for (
const auto &count : usage.getCounts())
185 obj[count.getKey()] = count.second;
192 const ResourceUsageAnalysis::ModuleResourceUsage &usage) {
193 llvm::json::Object obj;
194 obj[
"moduleName"] = usage.moduleName.getValue();
199 SmallVector<llvm::json::Value> instances;
200 for (
const auto &instance : usage.instances) {
201 llvm::json::Object child;
202 child[
"instanceName"] = instance.instanceName.getValue();
203 child[
"moduleName"] = instance.moduleName.getValue();
205 instances.push_back(std::move(child));
207 obj[
"instances"] = llvm::json::Array(instances);
213 raw_ostream &os)
const {
218struct PrintResourceUsageAnalysisPass
219 :
public impl::PrintResourceUsageAnalysisBase<
220 PrintResourceUsageAnalysisPass> {
221 using PrintResourceUsageAnalysisBase::PrintResourceUsageAnalysisBase;
223 void runOnOperation()
override;
227 SmallVectorImpl<igraph::ModuleOpInterface> &tops);
231 igraph::ModuleOpInterface top,
232 llvm::raw_ostream *os,
233 llvm::json::OStream *jsonOS);
237LogicalResult PrintResourceUsageAnalysisPass::getTopModules(
239 SmallVectorImpl<igraph::ModuleOpInterface> &tops) {
240 auto mod = getOperation();
242 if (topModuleName.getValue().empty()) {
245 if (failed(topLevelNodes))
246 return mod.emitError()
247 <<
"failed to infer top-level modules from instance graph";
250 for (
auto *node : *topLevelNodes) {
251 if (
auto module = node->getModule())
252 tops.push_back(module);
256 return mod.emitError() <<
"no top-level modules found in instance graph";
259 auto *node = instanceGraph->
lookup(
260 mlir::StringAttr::get(mod.getContext(), topModuleName.getValue()));
262 return mod.emitError()
263 <<
"top module '" << topModuleName.getValue() <<
"' not found";
265 tops.push_back(node->getModule());
271LogicalResult PrintResourceUsageAnalysisPass::printAnalysisResult(
272 ResourceUsageAnalysis &analysis, igraph::ModuleOpInterface top,
273 llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
274 auto *usage = analysis.getResourceUsage(top);
279 usage->emitJSON(jsonOS->rawValueBegin());
280 jsonOS->rawValueEnd();
283 stream <<
"Resource Usage Analysis for module: "
284 << usage->moduleName.getValue() <<
"\n";
285 stream <<
"========================================\n";
286 stream <<
"Total:\n";
289 SmallVector<std::pair<StringRef, uint64_t>> sortedCounts;
290 for (
const auto &count : usage->getTotal().getCounts())
291 sortedCounts.emplace_back(count.getKey(), count.second);
292 llvm::sort(sortedCounts,
293 [](
const auto &a,
const auto &b) {
return a.first < b.first; });
296 size_t maxNameLen = 0;
297 for (
const auto &[name, count] : sortedCounts)
298 maxNameLen = std::max(maxNameLen, name.size());
301 for (
const auto &[name, count] : sortedCounts)
302 stream <<
" " << name <<
": "
303 << std::string(maxNameLen - name.size(),
' ') << count <<
"\n";
310void PrintResourceUsageAnalysisPass::runOnOperation() {
311 auto &resourceUsage = getAnalysis<ResourceUsageAnalysis>();
312 auto *instanceGraph = resourceUsage.getInstanceGraph();
315 SmallVector<igraph::ModuleOpInterface> tops;
316 if (failed(getTopModules(instanceGraph, tops)))
317 return signalPassFailure();
321 auto file = mlir::openOutputFile(outputFile.getValue(), &error);
323 llvm::errs() <<
error;
324 return signalPassFailure();
327 auto &os = file->os();
328 std::unique_ptr<llvm::json::OStream> jsonOS;
329 if (emitJSON.getValue()) {
330 jsonOS = std::make_unique<llvm::json::OStream>(os);
331 jsonOS->arrayBegin();
335 auto closeJson = llvm::scope_exit([&]() {
341 for (
auto top : tops) {
342 if (failed(printAnalysisResult(resourceUsage, top, jsonOS ?
nullptr : &os,
344 return signalPassFailure();
348 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static llvm::json::Object getModuleResourceUsageJSON(const ResourceUsageAnalysis::ResourceUsage &usage)
Convert ResourceUsage to JSON object.
static bool accumulateResourceCounts(Operation *op, llvm::StringMap< uint64_t > &counts)
Accumulate resource counts for an operation if it's a tracked resource type.
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
This graph tracks modules and where they are instantiated.
FailureOr< llvm::ArrayRef< InstanceGraphNode * > > getInferredTopLevelNodes()
Get the nodes corresponding to the inferred top-level modules of a circuit.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
DenseMap< StringAttr, std::unique_ptr< ModuleResourceUsage > > designUsageCache
Cache of computed resource usage per module.
ResourceUsageAnalysis(mlir::Operation *moduleOp, mlir::AnalysisManager &am)
ModuleResourceUsage * getResourceUsage(igraph::ModuleOpInterface module)
Get resource usage for a module.
igraph::InstanceGraph * instanceGraph
Instance graph for module hierarchy traversal.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Resource usage for a single module, including local and total counts.
void emitJSON(raw_ostream &os) const