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) {
49 if (op->getNumResults() != 1 || !op->getResult(0).getType().isInteger())
51 return TypeSwitch<Operation *, bool>(op)
52 .Case<BooleanLogicOpInterface>([&](
auto logicOp) {
53 if (
auto areaCost = logicOp.getLogicAreaCost()) {
54 counts[op->getName().getStringRef()] += *areaCost;
61 .Case<comb::AndOp, comb::OrOp, comb::XorOp>([&](
auto logicOp) {
62 counts[logicOp->getName().getStringRef()] +=
63 static_cast<uint64_t
>(logicOp.getNumOperands() - 1) *
64 logicOp.getType().getIntOrFloatBitWidth();
69 .Case<comb::TruthTableOp>([&](
auto op) {
70 uint64_t count = op.getType().getIntOrFloatBitWidth();
71 counts[op->getName().getStringRef()] += count;
72 std::string bucket = (Twine(op->getName().getStringRef()) +
"_" +
73 Twine(op.getNumOperands()))
75 counts[bucket] += count;
80 .Case<seq::CompRegOp, seq::FirRegOp>([&](
auto op) {
81 uint64_t count = op.getType().getIntOrFloatBitWidth();
82 counts[op->getName().getStringRef()] += count;
85 .Default([](Operation *) {
return false; });
88ResourceUsageAnalysis::ResourceUsageAnalysis(Operation *moduleOp,
89 mlir::AnalysisManager &am)
97 return it->second.get();
112 return cacheIt->second.get();
117 llvm::StringMap<uint64_t> counts;
118 uint64_t unknownOpCount = 0;
119 module->walk([&](Operation *op) {
120 if (accumulateResourceCounts(op, counts))
122 if (op->getNumResults() > 0 && !isa<hw::HWInstanceLike>(op) &&
123 !op->hasTrait<mlir::OpTrait::ConstantLike>()) {
130 if (unknownOpCount > 0)
131 counts[
"<unknown>"] = unknownOpCount;
135 ResourceUsage local(std::move(counts));
136 auto moduleUsage = std::make_unique<ModuleResourceUsage>(
137 module.getModuleNameAttr(), local, local);
140 for (
auto *child : *node) {
141 auto *targetNode = child->getTarget();
143 auto childModule = targetNode->getModule();
145 auto *instanceOp = child->getInstance().getOperation();
147 if (instanceOp->getNumResults() == 0 ||
148 instanceOp->hasAttrOfType<UnitAttr>(
"doNotPrint"))
152 auto *childUsage = getResourceUsage(childModule);
153 moduleUsage->total += childUsage->total;
154 moduleUsage->instances.emplace_back(
155 childModule.getModuleNameAttr(),
156 child->getInstance().getInstanceNameAttr(), childUsage);
160 auto [it, success] = designUsageCache.try_emplace(module.getModuleNameAttr(),
161 std::move(moduleUsage));
162 assert(success &&
"module already exists in cache");
164 return it->second.get();
172static llvm::json::Object
174 llvm::json::Object obj;
175 for (
const auto &count : usage.getCounts())
176 obj[count.getKey()] = count.second;
183 const ResourceUsageAnalysis::ModuleResourceUsage &usage) {
184 llvm::json::Object obj;
185 obj[
"moduleName"] = usage.moduleName.getValue();
190 SmallVector<llvm::json::Value> instances;
191 for (
const auto &instance : usage.instances) {
192 llvm::json::Object child;
193 child[
"instanceName"] = instance.instanceName.getValue();
194 child[
"moduleName"] = instance.moduleName.getValue();
196 instances.push_back(std::move(child));
198 obj[
"instances"] = llvm::json::Array(instances);
204 raw_ostream &os)
const {
209struct PrintResourceUsageAnalysisPass
210 :
public impl::PrintResourceUsageAnalysisBase<
211 PrintResourceUsageAnalysisPass> {
212 using PrintResourceUsageAnalysisBase::PrintResourceUsageAnalysisBase;
214 void runOnOperation()
override;
218 SmallVectorImpl<igraph::ModuleOpInterface> &tops);
222 igraph::ModuleOpInterface top,
223 llvm::raw_ostream *os,
224 llvm::json::OStream *jsonOS);
228LogicalResult PrintResourceUsageAnalysisPass::getTopModules(
230 SmallVectorImpl<igraph::ModuleOpInterface> &tops) {
231 auto mod = getOperation();
233 if (topModuleName.getValue().empty()) {
236 if (failed(topLevelNodes))
237 return mod.emitError()
238 <<
"failed to infer top-level modules from instance graph";
241 for (
auto *node : *topLevelNodes) {
242 if (
auto module = node->getModule())
243 tops.push_back(module);
247 return mod.emitError() <<
"no top-level modules found in instance graph";
251 mlir::StringAttr::get(mod.getContext(), topModuleName.getValue()));
253 return mod.emitError()
254 <<
"top module '" << topModuleName.getValue() <<
"' not found";
256 tops.push_back(node->getModule());
262LogicalResult PrintResourceUsageAnalysisPass::printAnalysisResult(
263 ResourceUsageAnalysis &analysis, igraph::ModuleOpInterface top,
264 llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
265 auto *usage = analysis.getResourceUsage(top);
270 usage->emitJSON(jsonOS->rawValueBegin());
271 jsonOS->rawValueEnd();
274 stream <<
"Resource Usage Analysis for module: "
275 << usage->moduleName.getValue() <<
"\n";
276 stream <<
"========================================\n";
277 stream <<
"Total:\n";
280 SmallVector<std::pair<StringRef, uint64_t>> sortedCounts;
281 for (
const auto &count : usage->getTotal().getCounts())
282 sortedCounts.emplace_back(count.getKey(), count.second);
283 llvm::sort(sortedCounts,
284 [](
const auto &a,
const auto &b) {
return a.first <
b.first; });
287 size_t maxNameLen = 0;
288 for (
const auto &[name, count] : sortedCounts)
289 maxNameLen = std::max(maxNameLen, name.size());
292 for (
const auto &[name, count] : sortedCounts)
293 stream <<
" " << name <<
": "
294 << std::string(maxNameLen - name.size(),
' ') << count <<
"\n";
301void PrintResourceUsageAnalysisPass::runOnOperation() {
302 auto &resourceUsage = getAnalysis<ResourceUsageAnalysis>();
306 SmallVector<igraph::ModuleOpInterface> tops;
308 return signalPassFailure();
312 auto file = mlir::openOutputFile(outputFile.getValue(), &error);
314 llvm::errs() <<
error;
315 return signalPassFailure();
318 auto &os = file->os();
319 std::unique_ptr<llvm::json::OStream> jsonOS;
320 if (emitJSON.getValue()) {
321 jsonOS = std::make_unique<llvm::json::OStream>(os);
322 jsonOS->arrayBegin();
326 auto closeJson = llvm::scope_exit([&]() {
332 for (
auto top : tops) {
333 if (failed(printAnalysisResult(resourceUsage, top, jsonOS ?
nullptr : &os,
335 return signalPassFailure();
339 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