Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Analysis] Add OpCount Analysis #7644

Merged
merged 7 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions include/circt/Analysis/OpCountAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===- OpCountAnalysis.h - operation count analyses -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header file defines prototypes for methods that perform analysis
// involving the frequency of different kinds of operations found in a
// builtin.module.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_ANALYSIS_OPCOUNT_ANALYSIS_H
#define CIRCT_ANALYSIS_OPCOUNT_ANALYSIS_H

#include "circt/Support/LLVM.h"
#include "mlir/IR/Operation.h"
#include "llvm/ADT/DenseMap.h"

namespace mlir {
class AnalysisManager;
} // namespace mlir
namespace circt {
namespace analysis {

struct OpCountAnalysis {
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
OpCountAnalysis(Operation *moduleOp, mlir::AnalysisManager &am);

size_t getOpCount(OperationName opName);
SmallVector<OperationName> getFoundOpNames();
DenseMap<size_t, size_t> getOperandCountMap(OperationName opName);
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved

private:
DenseMap<OperationName, size_t> opCounts;
DenseMap<OperationName, DenseMap<size_t, size_t>> operandCounts;
Comment on lines +43 to +44
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opCounts could be calculated from operandCounts here, but it would require a lot of extra iterating over DenseMaps so went for the faster option since we probably don't want this to be slower than necessary if it's running on huge blocks of IR.

};

} // namespace analysis
} // namespace circt

#endif // CIRCT_ANALYSIS_SCHEDULING_ANALYSIS_H
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(LLVM_OPTIONAL_SOURCES
DebugInfo.cpp
DependenceAnalysis.cpp
FIRRTLInstanceInfo.cpp
OpCountAnalysis.cpp
SchedulingAnalysis.cpp
TestPasses.cpp
)
Expand All @@ -27,6 +28,13 @@ add_circt_library(CIRCTDependenceAnalysis
MLIRTransformUtils
)

add_circt_library(CIRCTOpCountAnalysis
OpCountAnalysis.cpp

LINK_LIBS PUBLIC
MLIRIR
)

add_circt_library(CIRCTSchedulingAnalysis
SchedulingAnalysis.cpp

Expand All @@ -50,6 +58,7 @@ add_circt_library(CIRCTAnalysisTestPasses
LINK_LIBS PUBLIC
CIRCTDebugAnalysis
CIRCTDependenceAnalysis
CIRCTOpCountAnalysis
CIRCTFIRRTLAnalysis
CIRCTSchedulingAnalysis
CIRCTHW
Expand Down
62 changes: 62 additions & 0 deletions lib/Analysis/OpCountAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===- OpCountAnalysis.cpp - operation count analyses -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the op count analysis. This is an analysis that
// provides information about the frequency of different kinds of operations
// found in a builtin.module.
//
//===----------------------------------------------------------------------===//

#include "circt/Analysis/OpCountAnalysis.h"
#include "mlir/IR/Operation.h"

using namespace circt;
using namespace analysis;

OpCountAnalysis::OpCountAnalysis(Operation *moduleOp,
mlir::AnalysisManager &am) {
moduleOp->walk([&](Operation *op) {
auto opName = op->getName();
// Update opCounts
if (opCounts.find(opName) == opCounts.end())
opCounts[opName] = 1;
else
opCounts[op->getName()]++;

// Update operandCounts
if (operandCounts.find(opName) == operandCounts.end())
operandCounts[opName] = DenseMap<size_t, size_t>();
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
if (operandCounts[opName].find(op->getNumOperands()) ==
operandCounts[opName].end())
operandCounts[opName][op->getNumOperands()] = 1;
else
operandCounts[opName][op->getNumOperands()]++;
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
});
}

SmallVector<OperationName> OpCountAnalysis::getFoundOpNames() {
SmallVector<OperationName> opNames;
for (auto pair : opCounts)
opNames.push_back(pair.first);
return opNames;
}

size_t OpCountAnalysis::getOpCount(OperationName opName) {
size_t count = 0;
if (opCounts.find(opName) != opCounts.end())
count = opCounts[opName];
return count;
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
}

DenseMap<size_t, size_t>
OpCountAnalysis::getOperandCountMap(OperationName opName) {
auto map = DenseMap<size_t, size_t>();
if (operandCounts.find(opName) != operandCounts.end())
map = operandCounts[opName];
return map;
TaoBi22 marked this conversation as resolved.
Show resolved Hide resolved
}
47 changes: 47 additions & 0 deletions lib/Analysis/TestPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "circt/Analysis/DebugAnalysis.h"
#include "circt/Analysis/DependenceAnalysis.h"
#include "circt/Analysis/FIRRTLInstanceInfo.h"
#include "circt/Analysis/OpCountAnalysis.h"
#include "circt/Analysis/SchedulingAnalysis.h"
#include "circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h"
#include "circt/Dialect/HW/HWInstanceGraph.h"
Expand Down Expand Up @@ -250,6 +251,49 @@ void FIRRTLInstanceInfoPass::runOnOperation() {
printModuleInfo(op, iInfo);
}

//===----------------------------------------------------------------------===//
// Op Count
//===----------------------------------------------------------------------===//

namespace {
struct TestOpCountAnalysisPass
: public PassWrapper<TestOpCountAnalysisPass, OperationPass<ModuleOp>> {
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOpCountAnalysisPass)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you plan to add a proper pass to print it as JSON, why do we even need a separate test pass? That proper pass could support two formats (a human-readable one and an easy-to-parse one) like the upstream variant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - I just put this in so we had something upstream quickly to start getting data while I did the JSON pass, but definitely agree it would make sense to have the JSON pass also do a human readable form.


void runOnOperation() override;
StringRef getArgument() const override { return "test-op-count-analysis"; }
StringRef getDescription() const override {
return "Run OpCountAnalysis and show the results. This pass "
"is intended to be used for testing purposes only.";
}
};
} // namespace

void printOpAndOperandCounts(OpCountAnalysis &opCount) {
auto opNames = opCount.getFoundOpNames();
// Sort to account for non-deterministic DenseMap ordering
llvm::sort(opNames, [](OperationName name1, OperationName name2) {
return name1.getStringRef() < name2.getStringRef();
});
for (auto opName : opNames) {
llvm::errs() << opName << ": " << opCount.getOpCount(opName) << "\n";
auto operandMap = opCount.getOperandCountMap(opName);
// Sort for determinism again
llvm::SmallVector<size_t> keys;
for (auto pair : operandMap)
keys.push_back(pair.first);
llvm::sort(keys);
for (auto num : keys)
llvm::errs() << " with " << num << " operands: " << operandMap[num]
<< "\n";
}
Comment on lines +278 to +289
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this format to you, particularly since you mentioned bridging this to JSON or something like that.

I tend to actually export this information directly as YAML (manually, not using the internal library). You may be able to go to JSON directly here if you want.

Regardless, I think the format should be less wordy eventually, e.g.:

operations:
  - name: builtin.module
    count: 1
  - name: comb.add
    count: 4
    operandCounts:
      - operands: 1
        count: 2
      - operands: 2
        count: 4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I just put this in for testing so figured something readable in case of debugging failure wouldn't hurt. Happy to trim it down if preferred though.

}

void TestOpCountAnalysisPass::runOnOperation() {
auto &opCount = getAnalysis<OpCountAnalysis>();
printOpAndOperandCounts(opCount);
}

//===----------------------------------------------------------------------===//
// Pass registration
//===----------------------------------------------------------------------===//
Expand All @@ -272,6 +316,9 @@ void registerAnalysisTestPasses() {
registerPass([]() -> std::unique_ptr<Pass> {
return std::make_unique<FIRRTLInstanceInfoPass>();
});
registerPass([]() -> std::unique_ptr<Pass> {
return std::make_unique<TestOpCountAnalysisPass>();
});
}
} // namespace test
} // namespace circt
36 changes: 36 additions & 0 deletions test/Analysis/op-count-analysis.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: circt-opt -test-op-count-analysis %s 2>&1 | FileCheck %s

// CHECK: builtin.module: 1
// CHECK: with 0 operands: 1
// CHECK: comb.add: 4
// CHECK: with 1 operands: 1
// CHECK: with 2 operands: 1
// CHECK: with 3 operands: 2
// CHECK: comb.icmp: 1
// CHECK: with 2 operands: 1
// CHECK: comb.xor: 1
// CHECK: with 1 operands: 1
// CHECK: hw.module: 1
// CHECK: with 0 operands: 1
// CHECK: hw.output: 1
// CHECK: with 0 operands: 1
// CHECK: scf.if: 1
// CHECK: with 1 operands: 1
// CHECK: scf.yield: 2
// CHECK: with 1 operands: 2

module {
hw.module @bar(in %in1: i8, in %in2: i8, in %in3: i8) {
%add2 = comb.add %in1, %in2 : i8
%add3 = comb.add %in1, %in2, %in3 : i8
%add3again = comb.add %in1, %in3, %in3 : i8
%gt = comb.icmp ult %in1, %in2 : i8
%x = scf.if %gt -> (i8) {
%add1 = comb.add %in1 : i8
scf.yield %add1 : i8
} else {
%xor = comb.xor %add2 : i8
scf.yield %xor : i8
}
}
}
Loading