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; });
97ResourceUsageAnalysis::ResourceUsageAnalysis(Operation *moduleOp,
98 mlir::AnalysisManager &am)
106 return it->second.get();
121 return cacheIt->second.get();
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));
171 assert(success &&
"module already exists in cache");
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";
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>();
315 SmallVector<igraph::ModuleOpInterface> 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")
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.
Analysis that computes resource usage for Synth dialect operations.
DenseMap< StringAttr, std::unique_ptr< ModuleResourceUsage > > designUsageCache
Cache of computed resource usage per module.
ModuleResourceUsage * getResourceUsage(igraph::ModuleOpInterface module)
Get resource usage for a module.
igraph::InstanceGraph * instanceGraph
Instance graph for module hierarchy traversal.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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