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"
48static std::optional<std::pair<std::string, uint64_t>>
50 if (op->getNumResults() != 1 || !op->getResult(0).getType().isInteger())
52 return TypeSwitch<Operation *,
53 std::optional<std::pair<std::string, uint64_t>>>(op)
56 .Case<synth::aig::AndInverterOp, comb::AndOp, comb::OrOp, comb::XorOp>(
58 return std::make_pair(
59 logicOp->getName().getStringRef(),
60 (logicOp.getNumOperands() - 1) *
61 logicOp.getType().getIntOrFloatBitWidth());
67 .Case<synth::mig::MajorityInverterOp>([](
auto logicOp) {
68 uint64_t count = logicOp.getType().getIntOrFloatBitWidth();
70 std::string name = (Twine(logicOp->getName().getStringRef()) +
"_" +
71 Twine(logicOp.getNumOperands()))
73 return std::make_pair(std::move(name), count);
76 .Case<comb::TruthTableOp>([](
auto op) {
77 uint64_t count = op.getType().getIntOrFloatBitWidth();
79 std::string name = (Twine(op->getName().getStringRef()) +
"_" +
80 Twine(op.getNumOperands()))
82 return std::make_pair(std::move(name), count);
86 .Case<seq::CompRegOp, seq::FirRegOp>([](
auto op) {
87 uint64_t count = op.getType().getIntOrFloatBitWidth();
88 return std::make_pair(op->getName().getStringRef().str(), count);
90 .Default([](Operation *) {
return std::nullopt; });
93ResourceUsageAnalysis::ResourceUsageAnalysis(Operation *moduleOp,
94 mlir::AnalysisManager &am)
102 return it->second.get();
109 auto module = dyn_cast_or_null<igraph::ModuleOpInterface>(node->getModule());
121 return cacheIt->second.get();
126 llvm::StringMap<uint64_t> counts;
127 uint64_t unknownOpCount = 0;
128 module->walk([&](Operation *op) {
129 if (auto resource = getResourceCount(op))
130 counts[resource->first] += resource->second;
131 else 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 dyn_cast_or_null<igraph::ModuleOpInterface>(targetNode->getModule());
156 auto *instanceOp = child->getInstance().getOperation();
158 if (instanceOp->getNumResults() == 0 ||
159 instanceOp->hasAttrOfType<UnitAttr>(
"doNotPrint"))
163 auto *childUsage = getResourceUsage(childModule);
164 moduleUsage->total += childUsage->total;
165 moduleUsage->instances.emplace_back(
166 childModule.getModuleNameAttr(),
167 child->getInstance().getInstanceNameAttr(), childUsage);
171 auto [it, success] = designUsageCache.try_emplace(module.getModuleNameAttr(),
172 std::move(moduleUsage));
173 assert(success &&
"module already exists in cache");
175 return it->second.get();
183static llvm::json::Object
185 llvm::json::Object obj;
186 for (
const auto &count : usage.getCounts())
187 obj[count.getKey()] = count.second;
194 const ResourceUsageAnalysis::ModuleResourceUsage &usage) {
195 llvm::json::Object obj;
196 obj[
"moduleName"] = usage.moduleName.getValue();
201 SmallVector<llvm::json::Value> instances;
202 for (
const auto &instance : usage.instances) {
203 llvm::json::Object child;
204 child[
"instanceName"] = instance.instanceName.getValue();
205 child[
"moduleName"] = instance.moduleName.getValue();
207 instances.push_back(std::move(child));
209 obj[
"instances"] = llvm::json::Array(instances);
215 raw_ostream &os)
const {
220struct PrintResourceUsageAnalysisPass
221 :
public impl::PrintResourceUsageAnalysisBase<
222 PrintResourceUsageAnalysisPass> {
223 using PrintResourceUsageAnalysisBase::PrintResourceUsageAnalysisBase;
225 void runOnOperation()
override;
229 SmallVectorImpl<igraph::ModuleOpInterface> &tops);
233 igraph::ModuleOpInterface top,
234 llvm::raw_ostream *os,
235 llvm::json::OStream *jsonOS);
239LogicalResult PrintResourceUsageAnalysisPass::getTopModules(
241 SmallVectorImpl<igraph::ModuleOpInterface> &tops) {
242 auto mod = getOperation();
244 if (topModuleName.getValue().empty()) {
247 if (failed(topLevelNodes))
248 return mod.emitError()
249 <<
"failed to infer top-level modules from instance graph";
252 for (
auto *node : *topLevelNodes) {
253 if (
auto module = dyn_cast<igraph::ModuleOpInterface>(node->getModule()))
254 tops.push_back(module);
258 return mod.emitError() <<
"no top-level modules found in instance graph";
262 mlir::StringAttr::get(mod.getContext(), topModuleName.getValue()));
264 return mod.emitError()
265 <<
"top module '" << topModuleName.getValue() <<
"' not found";
267 auto top = dyn_cast_or_null<igraph::ModuleOpInterface>(node->getModule());
269 return mod.emitError() <<
"module '" << topModuleName.getValue()
270 <<
"' is not a ModuleOpInterface";
277LogicalResult PrintResourceUsageAnalysisPass::printAnalysisResult(
278 ResourceUsageAnalysis &analysis, igraph::ModuleOpInterface top,
279 llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
280 auto *usage = analysis.getResourceUsage(top);
285 usage->emitJSON(jsonOS->rawValueBegin());
286 jsonOS->rawValueEnd();
289 stream <<
"Resource Usage Analysis for module: "
290 << usage->moduleName.getValue() <<
"\n";
291 stream <<
"========================================\n";
292 stream <<
"Total:\n";
295 SmallVector<std::pair<StringRef, uint64_t>> sortedCounts;
296 for (
const auto &count : usage->getTotal().getCounts())
297 sortedCounts.emplace_back(count.getKey(), count.second);
298 llvm::sort(sortedCounts,
299 [](
const auto &a,
const auto &b) {
return a.first <
b.first; });
302 size_t maxNameLen = 0;
303 for (
const auto &[name, count] : sortedCounts)
304 maxNameLen = std::max(maxNameLen, name.size());
307 for (
const auto &[name, count] : sortedCounts)
308 stream <<
" " << name <<
": "
309 << std::string(maxNameLen - name.size(),
' ') << count <<
"\n";
316void PrintResourceUsageAnalysisPass::runOnOperation() {
317 auto &resourceUsage = getAnalysis<ResourceUsageAnalysis>();
321 SmallVector<igraph::ModuleOpInterface> tops;
323 return signalPassFailure();
327 auto file = mlir::openOutputFile(outputFile.getValue(), &error);
329 llvm::errs() <<
error;
330 return signalPassFailure();
333 auto &os = file->os();
334 std::unique_ptr<llvm::json::OStream> jsonOS;
335 if (emitJSON.getValue()) {
336 jsonOS = std::make_unique<llvm::json::OStream>(os);
337 jsonOS->arrayBegin();
341 auto closeJson = llvm::scope_exit([&]() {
347 for (
auto top : tops) {
348 if (failed(printAnalysisResult(resourceUsage, top, jsonOS ?
nullptr : &os,
350 return signalPassFailure();
354 markAllAnalysesPreserved();
assert(baseType &&"element must be base type")
static std::optional< std::pair< std::string, uint64_t > > getResourceCount(Operation *op)
Get resource count for an operation if it's a tracked resource type.
static llvm::json::Object getModuleResourceUsageJSON(const ResourceUsageAnalysis::ResourceUsage &usage)
Convert ResourceUsage to JSON object.
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