-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Arc] Add Initial Cost Model (#7360)
* [Arc] Add Initial Cost Model * [Arc] Add the DummyAnalysisTester pass * [Arc] Fix clang-format failure * [Arc] Address @fabianschuiki comments * [Arc] Remove DummyAnalysisTester.cpp
- Loading branch information
Showing
7 changed files
with
273 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
//===- ArcCostModel.h -----------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef CIRCT_DIALECT_ARC_ARCCOSTMODEL_H | ||
#define CIRCT_DIALECT_ARC_ARCCOSTMODEL_H | ||
|
||
#include "circt/Dialect/Arc/ArcOps.h" | ||
#include "mlir/IR/Operation.h" | ||
#include "mlir/Pass/AnalysisManager.h" | ||
|
||
using namespace mlir; | ||
|
||
namespace circt { | ||
namespace arc { | ||
|
||
struct OperationCosts { | ||
size_t normalCost{0}; | ||
size_t packingCost{0}; | ||
size_t shufflingCost{0}; | ||
size_t vectorizeOpsBodyCost{0}; | ||
size_t totalCost() const { | ||
return normalCost + packingCost + shufflingCost + vectorizeOpsBodyCost; | ||
} | ||
OperationCosts &operator+=(const OperationCosts &other) { | ||
this->normalCost += other.normalCost; | ||
this->packingCost += other.packingCost; | ||
this->shufflingCost += other.shufflingCost; | ||
this->vectorizeOpsBodyCost += other.vectorizeOpsBodyCost; | ||
return *this; | ||
} | ||
}; | ||
|
||
class ArcCostModel { | ||
public: | ||
OperationCosts getCost(Operation *op); | ||
|
||
private: | ||
OperationCosts computeOperationCost(Operation *op); | ||
|
||
// gets the cost to pack the vectors we have some cases we need to consider: | ||
// 1: the input is scalar so we can give it a cost of 1 | ||
// 2: the input is a result of another vector but with no shuffling so the | ||
// is 0 | ||
// 3: the input is a result of another vector but with some shuffling so | ||
// the cost is the (number of out of order elements) * 2 | ||
// 4: the input is a mix of some vectors: | ||
// a) same order we multiply by 2 | ||
// b) shuffling we multiply by 3 | ||
OperationCosts getInputVectorsCost(VectorizeOp vecOp); | ||
size_t getShufflingCost(const ValueRange &inputVec, bool isSame = false); | ||
DenseMap<Operation *, OperationCosts> opCostCache; | ||
}; | ||
|
||
} // namespace arc | ||
} // namespace circt | ||
|
||
#endif // CIRCT_DIALECT_ARC_ARCCOSTMODEL_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
//===- ArcCostModel.cpp ---------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "circt/Dialect/Arc/ArcCostModel.h" | ||
#include "circt/Dialect/Comb/CombOps.h" | ||
#include "mlir/Dialect/Func/IR/FuncOps.h" | ||
#include <algorithm> | ||
|
||
using namespace llvm; | ||
using namespace circt; | ||
using namespace arc; | ||
using namespace std; | ||
|
||
// FIXME: May be refined and we have more accurate operation costs | ||
enum class OperationCost : size_t { | ||
NOCOST, | ||
NORMALCOST, | ||
PACKCOST = 2, | ||
EXTRACTCOST = 3, | ||
CONCATCOST = 3, | ||
SAMEVECTORNOSHUFFLE = 0, | ||
SAMEVECTORSHUFFLECOST = 2, | ||
DIFFERENTVECTORNOSHUFFLE = 2, | ||
DIFFERENTVECTORSHUFFLECOST = 3 | ||
}; | ||
|
||
OperationCosts ArcCostModel::getCost(Operation *op) { | ||
return computeOperationCost(op); | ||
} | ||
|
||
OperationCosts ArcCostModel::computeOperationCost(Operation *op) { | ||
if (auto it = opCostCache.find(op); it != opCostCache.end()) | ||
return it->second; | ||
|
||
OperationCosts costs; | ||
|
||
if (isa<circt::comb::ConcatOp>(op)) | ||
costs.normalCost = size_t(OperationCost::CONCATCOST); | ||
else if (isa<circt::comb::ExtractOp>(op)) | ||
costs.normalCost = size_t(OperationCost::EXTRACTCOST); | ||
else if (auto vecOp = dyn_cast<arc::VectorizeOp>(op)) { | ||
// VectorizeOpCost = packingCost + shufflingCost + bodyCost | ||
OperationCosts inputVecCosts = getInputVectorsCost(vecOp); | ||
costs.packingCost += inputVecCosts.packingCost; | ||
costs.shufflingCost += inputVecCosts.shufflingCost; | ||
|
||
for (auto ®ion : op->getRegions()) { | ||
for (auto &block : region) { | ||
for (auto &innerOp : block) { | ||
OperationCosts innerCosts = computeOperationCost(&innerOp); | ||
costs.vectorizeOpsBodyCost += innerCosts.totalCost(); | ||
} | ||
} | ||
} | ||
} else if (auto callableOp = dyn_cast<CallOpInterface>(op)) { | ||
// Callable Op? then resolve! | ||
if (auto *calledOp = callableOp.resolveCallable()) | ||
return opCostCache[callableOp] = computeOperationCost(calledOp); | ||
} else if (isa<func::FuncOp, arc::DefineOp, mlir::ModuleOp>(op)) { | ||
// Get the body cost | ||
for (auto ®ion : op->getRegions()) | ||
for (auto &block : region) | ||
for (auto &innerOp : block) | ||
costs += computeOperationCost(&innerOp); | ||
} else | ||
costs.normalCost = size_t(OperationCost::NORMALCOST); | ||
|
||
return opCostCache[op] = costs; | ||
} | ||
|
||
OperationCosts ArcCostModel::getInputVectorsCost(VectorizeOp vecOp) { | ||
OperationCosts costs; | ||
for (auto inputVec : vecOp.getInputs()) { | ||
if (auto otherVecOp = inputVec[0].getDefiningOp<VectorizeOp>(); | ||
all_of(inputVec.begin(), inputVec.end(), [&](auto element) { | ||
return element.template getDefiningOp<VectorizeOp>() == otherVecOp; | ||
})) { | ||
// This means that they came from the same vector or | ||
// VectorizeOp == null so they are all scalars | ||
|
||
// Check if they all scalars we multiply by the PACKCOST (SHL/R + OR) | ||
if (!otherVecOp) | ||
costs.packingCost += inputVec.size() * size_t(OperationCost::PACKCOST); | ||
else | ||
costs.shufflingCost += inputVec == otherVecOp.getResults() | ||
? size_t(OperationCost::SAMEVECTORNOSHUFFLE) | ||
: getShufflingCost(inputVec, true); | ||
} else | ||
// inputVector consists of elements from different vectotrize ops and | ||
// may have scalars as well. | ||
costs.shufflingCost += getShufflingCost(inputVec); | ||
} | ||
|
||
return costs; | ||
} | ||
|
||
size_t ArcCostModel::getShufflingCost(const ValueRange &inputVec, bool isSame) { | ||
size_t totalCost = 0; | ||
if (isSame) { | ||
auto vecOp = inputVec[0].getDefiningOp<VectorizeOp>(); | ||
for (auto [elem, orig] : llvm::zip(inputVec, vecOp.getResults())) | ||
if (elem != orig) | ||
++totalCost; | ||
|
||
return totalCost * size_t(OperationCost::SAMEVECTORSHUFFLECOST); | ||
} | ||
|
||
for (size_t i = 0; i < inputVec.size(); ++i) { | ||
auto otherVecOp = inputVec[i].getDefiningOp<VectorizeOp>(); | ||
// If the element is not a result of a vector operation then it's a result | ||
// of a scalar operation, then it just needs to be packed into the vector. | ||
if (!otherVecOp) | ||
totalCost += size_t(OperationCost::PACKCOST); | ||
else { | ||
// If it's a result of a vector operation, then we have two cases: | ||
// (1) Its order in `inputVec` is the same as its order in the result of | ||
// the defining op. | ||
// (2) the order is different. | ||
size_t idx = find(otherVecOp.getResults().begin(), | ||
otherVecOp.getResults().end(), inputVec[i]) - | ||
otherVecOp.getResults().begin(); | ||
totalCost += i == idx ? size_t(OperationCost::DIFFERENTVECTORNOSHUFFLE) | ||
: size_t(OperationCost::DIFFERENTVECTORSHUFFLECOST); | ||
} | ||
} | ||
return totalCost; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//===- DummyAnalysisTester.cpp --------------------------------------------===// | ||
// | ||
// 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 is a dummy pass to test the cost model results it doesn't do any thing. | ||
// just walks over the ops to compute some statistics. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "circt/Dialect/Arc/ArcCostModel.h" | ||
#include "circt/Dialect/Arc/ArcPasses.h" | ||
#include "circt/Dialect/HW/HWOps.h" | ||
#include "mlir/IR/MLIRContext.h" | ||
#include "mlir/Pass/Pass.h" | ||
|
||
#define DEBUG_TYPE "arc-print-cost-model" | ||
|
||
namespace circt { | ||
namespace arc { | ||
#define GEN_PASS_DEF_PRINTCOSTMODEL | ||
#include "circt/Dialect/Arc/ArcPasses.h.inc" | ||
} // namespace arc | ||
} // namespace circt | ||
|
||
using namespace circt; | ||
using namespace arc; | ||
|
||
namespace { | ||
struct PrintCostModelPass | ||
: public arc::impl::PrintCostModelBase<PrintCostModelPass> { | ||
void runOnOperation() override; | ||
}; | ||
} // namespace | ||
|
||
void PrintCostModelPass::runOnOperation() { | ||
OperationCosts statVars; | ||
ArcCostModel arcCostModel; | ||
for (auto moduleOp : getOperation().getOps<hw::HWModuleOp>()) { | ||
moduleOp.walk([&](Operation *op) { statVars += arcCostModel.getCost(op); }); | ||
} | ||
|
||
moduleCost = statVars.totalCost(); | ||
packingCost = statVars.packingCost; | ||
shufflingCost = statVars.shufflingCost; | ||
vectoroizeOpsBodyCost = statVars.vectorizeOpsBodyCost; | ||
allVectorizeOpsCost = statVars.packingCost + statVars.shufflingCost + | ||
statVars.vectorizeOpsBodyCost; | ||
} | ||
|
||
std::unique_ptr<Pass> arc::createPrintCostModelPass() { | ||
return std::make_unique<PrintCostModelPass>(); | ||
} |