23#include "mlir/IR/BuiltinOps.h"
24#include "mlir/Pass/AnalysisManager.h"
25#include "mlir/Support/FileUtilities.h"
26#include "llvm/ADT/ScopeExit.h"
27#include "llvm/Support/JSON.h"
28#include "llvm/Support/ToolOutputFile.h"
32#define GEN_PASS_DEF_PRINTRESOURCEUSAGEANALYSIS
33#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
47 llvm::StringMap<uint64_t> &counts) {
48 if (op->getNumResults() != 1 || !op->getResult(0).getType().isInteger())
50 return TypeSwitch<Operation *, bool>(op)
55 counts[logicOp->getName().getStringRef()] +=
56 (logicOp.getNumOperands() - 1) *
57 logicOp.getType().getIntOrFloatBitWidth();
64 .Case<synth::mig::MajorityInverterOp>([&](
auto logicOp) {
65 uint64_t count = logicOp.getType().getIntOrFloatBitWidth();
67 std::string name = (Twine(logicOp->getName().getStringRef()) +
"_" +
68 Twine(logicOp.getNumOperands()))
70 counts[name] += count;
75 .Case<comb::TruthTableOp>([&](
auto op) {
76 uint64_t count = op.getType().getIntOrFloatBitWidth();
77 counts[op->getName().getStringRef()] += count;
78 std::string bucket = (Twine(op->getName().getStringRef()) +
"_" +
79 Twine(op.getNumOperands()))
81 counts[bucket] += count;
86 .Case<seq::CompRegOp, seq::FirRegOp>([&](
auto op) {
87 uint64_t count = op.getType().getIntOrFloatBitWidth();
88 counts[op->getName().getStringRef()] += count;
91 .Default([](Operation *) {
return false; });
94ResourceUsageAnalysis::ResourceUsageAnalysis(Operation *moduleOp,
95 mlir::AnalysisManager &am)
103 return it->second.get();
118 return cacheIt->second.get();
123 llvm::StringMap<uint64_t> counts;
124 uint64_t unknownOpCount = 0;
125 module->walk([&](Operation *op) {
126 if (accumulateResourceCounts(op, counts))
128 if (op->getNumResults() > 0 && !isa<hw::HWInstanceLike>(op) &&
129 !op->hasTrait<mlir::OpTrait::ConstantLike>()) {
136 if (unknownOpCount > 0)
137 counts[
"<unknown>"] = unknownOpCount;
141 ResourceUsage local(std::move(counts));
142 auto moduleUsage = std::make_unique<ModuleResourceUsage>(
143 module.getModuleNameAttr(), local, local);
146 for (
auto *child : *node) {
147 auto *targetNode = child->getTarget();
149 auto childModule = targetNode->getModule();
151 auto *instanceOp = child->getInstance().getOperation();
153 if (instanceOp->getNumResults() == 0 ||
154 instanceOp->hasAttrOfType<UnitAttr>(
"doNotPrint"))
158 auto *childUsage = getResourceUsage(childModule);
159 moduleUsage->total += childUsage->total;
160 moduleUsage->instances.emplace_back(
161 childModule.getModuleNameAttr(),
162 child->getInstance().getInstanceNameAttr(), childUsage);
166 auto [it, success] = designUsageCache.try_emplace(module.getModuleNameAttr(),
167 std::move(moduleUsage));
168 assert(success &&
"module already exists in cache");
170 return it->second.get();
178static llvm::json::Object
180 llvm::json::Object obj;
181 for (
const auto &count : usage.getCounts())
182 obj[count.getKey()] = count.second;
189 const ResourceUsageAnalysis::ModuleResourceUsage &usage) {
190 llvm::json::Object obj;
191 obj[
"moduleName"] = usage.moduleName.getValue();
196 SmallVector<llvm::json::Value> instances;
197 for (
const auto &instance : usage.instances) {
198 llvm::json::Object child;
199 child[
"instanceName"] = instance.instanceName.getValue();
200 child[
"moduleName"] = instance.moduleName.getValue();
202 instances.push_back(std::move(child));
204 obj[
"instances"] = llvm::json::Array(instances);
210 raw_ostream &os)
const {
215struct PrintResourceUsageAnalysisPass
216 :
public impl::PrintResourceUsageAnalysisBase<
217 PrintResourceUsageAnalysisPass> {
218 using PrintResourceUsageAnalysisBase::PrintResourceUsageAnalysisBase;
220 void runOnOperation()
override;
224 SmallVectorImpl<igraph::ModuleOpInterface> &tops);
228 igraph::ModuleOpInterface top,
229 llvm::raw_ostream *os,
230 llvm::json::OStream *jsonOS);
234LogicalResult PrintResourceUsageAnalysisPass::getTopModules(
236 SmallVectorImpl<igraph::ModuleOpInterface> &tops) {
237 auto mod = getOperation();
239 if (topModuleName.getValue().empty()) {
242 if (failed(topLevelNodes))
243 return mod.emitError()
244 <<
"failed to infer top-level modules from instance graph";
247 for (
auto *node : *topLevelNodes) {
248 if (
auto module = node->getModule())
249 tops.push_back(module);
253 return mod.emitError() <<
"no top-level modules found in instance graph";
257 mlir::StringAttr::get(mod.getContext(), topModuleName.getValue()));
259 return mod.emitError()
260 <<
"top module '" << topModuleName.getValue() <<
"' not found";
262 tops.push_back(node->getModule());
268LogicalResult PrintResourceUsageAnalysisPass::printAnalysisResult(
269 ResourceUsageAnalysis &analysis, igraph::ModuleOpInterface top,
270 llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
271 auto *usage = analysis.getResourceUsage(top);
276 usage->emitJSON(jsonOS->rawValueBegin());
277 jsonOS->rawValueEnd();
280 stream <<
"Resource Usage Analysis for module: "
281 << usage->moduleName.getValue() <<
"\n";
282 stream <<
"========================================\n";
283 stream <<
"Total:\n";
286 SmallVector<std::pair<StringRef, uint64_t>> sortedCounts;
287 for (
const auto &count : usage->getTotal().getCounts())
288 sortedCounts.emplace_back(count.getKey(), count.second);
289 llvm::sort(sortedCounts,
290 [](
const auto &a,
const auto &b) {
return a.first <
b.first; });
293 size_t maxNameLen = 0;
294 for (
const auto &[name, count] : sortedCounts)
295 maxNameLen = std::max(maxNameLen, name.size());
298 for (
const auto &[name, count] : sortedCounts)
299 stream <<
" " << name <<
": "
300 << std::string(maxNameLen - name.size(),
' ') << count <<
"\n";
307void PrintResourceUsageAnalysisPass::runOnOperation() {
308 auto &resourceUsage = getAnalysis<ResourceUsageAnalysis>();
312 SmallVector<igraph::ModuleOpInterface> tops;
314 return signalPassFailure();
318 auto file = mlir::openOutputFile(outputFile.getValue(), &error);
320 llvm::errs() <<
error;
321 return signalPassFailure();
324 auto &os = file->os();
325 std::unique_ptr<llvm::json::OStream> jsonOS;
326 if (emitJSON.getValue()) {
327 jsonOS = std::make_unique<llvm::json::OStream>(os);
328 jsonOS->arrayBegin();
332 auto closeJson = llvm::scope_exit([&]() {
338 for (
auto top : tops) {
339 if (failed(printAnalysisResult(resourceUsage, top, jsonOS ?
nullptr : &os,
341 return signalPassFailure();
345 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