41#include "mlir/IR/BuiltinAttributes.h"
42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/Diagnostics.h"
44#include "mlir/IR/Operation.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/IR/Value.h"
47#include "mlir/IR/Visitors.h"
48#include "mlir/Pass/AnalysisManager.h"
49#include "mlir/Pass/PassManager.h"
50#include "mlir/Pass/PassRegistry.h"
51#include "mlir/Support/FileUtilities.h"
52#include "mlir/Support/LLVM.h"
53#include "llvm/ADT//MapVector.h"
54#include "llvm/ADT/ArrayRef.h"
55#include "llvm/ADT/DenseMapInfoVariant.h"
56#include "llvm/ADT/EquivalenceClasses.h"
57#include "llvm/ADT/ImmutableList.h"
58#include "llvm/ADT/MapVector.h"
59#include "llvm/ADT/PostOrderIterator.h"
60#include "llvm/ADT/STLExtras.h"
61#include "llvm/ADT/SmallVector.h"
62#include "llvm/ADT/StringRef.h"
63#include "llvm/Support/Debug.h"
64#include "llvm/Support/ErrorHandling.h"
65#include "llvm/Support/JSON.h"
66#include "llvm/Support/LogicalResult.h"
67#include "llvm/Support/MathExtras.h"
68#include "llvm/Support/Mutex.h"
69#include "llvm/Support/raw_ostream.h"
70#include <condition_variable>
76#define DEBUG_TYPE "aig-longest-path-analysis"
81 if (
auto vecType = dyn_cast<seq::ClockType>(value.getType()))
83 if (
auto memory = dyn_cast<seq::FirMemType>(value.getType()))
84 return memory.getWidth();
85 return hw::getBitWidth(value.getType());
88template <
typename T,
typename Key>
91 llvm::function_ref<Key(
const T &)> keyFn,
92 llvm::function_ref<int64_t(
const T &)> delayFn) {
94 DenseMap<Key, size_t> keyToIndex;
95 for (
size_t i = startIndex; i < results.size(); ++i) {
96 auto &path = results[i];
97 auto key = keyFn(path);
98 auto delay = delayFn(path);
99 auto it = keyToIndex.find(key);
100 if (it == keyToIndex.end()) {
102 size_t newIndex = keyToIndex.size() + startIndex;
103 keyToIndex[key] = newIndex;
104 results[newIndex] = std::move(results[i]);
107 if (delay > delayFn(results[it->second]))
108 results[it->second] = std::move(results[i]);
111 results.resize(keyToIndex.size() + startIndex);
116 bool keepOnlyMaxDelay,
bool isLocalScope) {
122 if (keepOnlyMaxDelay && isLocalScope) {
127 for (
auto &path : results) {
128 if (path.delay > maxDelay.delay)
134 if (maxDelay.delay >= 0)
135 results.push_back(maxDelay);
141 deduplicatePathsImpl<OpenPath, Object>(
142 results, 0, [](
const auto &path) {
return path.startPoint; },
143 [](
const auto &path) {
return path.delay; });
148 if (keepOnlyMaxDelay) {
150 size_t writeIndex = 0;
154 for (
size_t i = 0; i < results.size(); ++i) {
157 if (isa<BlockArgument>(results[i].getStartPoint().value)) {
160 results[writeIndex++] = results[i];
163 if (results[i].delay > maxDelay.delay)
164 maxDelay = results[i];
169 results.resize(writeIndex);
170 if (maxDelay.delay >= 0)
171 results.push_back(maxDelay);
176 size_t startIndex = 0) {
178 std::pair<DataflowPath::EndPointType, Object>>(
181 return std::pair(path.getEndPoint(), path.getStartPoint());
183 [](
const DataflowPath &path) {
return path.getDelay(); });
186static llvm::ImmutableList<DebugPoint>
187mapList(llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
188 llvm::ImmutableList<DebugPoint> list,
192 auto &head = list.getHead();
193 return debugPointFactory->add(fn(head),
194 mapList(debugPointFactory, list.getTail(), fn));
197static llvm::ImmutableList<DebugPoint>
198concatList(llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
199 llvm::ImmutableList<DebugPoint> lhs,
200 llvm::ImmutableList<DebugPoint> rhs) {
203 return debugPointFactory->add(
204 lhs.getHead(),
concatList(debugPointFactory, lhs.getTail(), rhs));
208 if (
auto arg = dyn_cast<BlockArgument>(value)) {
209 auto op = dyn_cast<hw::HWModuleOp>(arg.getParentBlock()->getParentOp());
212 return StringAttr::get(value.getContext(),
"<unknown-argument>");
214 return op.getArgName(arg.getArgNumber());
216 return TypeSwitch<Operation *, StringAttr>(value.getDefiningOp())
218 [](
auto op) {
return op.getNameAttr(); })
219 .Case<hw::InstanceOp>([&](hw::InstanceOp op) {
221 str += op.getInstanceName();
223 str += cast<StringAttr>(
224 op.getResultNamesAttr()[cast<OpResult>(value).getResultNumber()]);
225 return StringAttr::get(op.getContext(), str);
227 .Case<seq::FirMemReadOp>([&](seq::FirMemReadOp op) {
228 llvm::SmallString<16> str;
229 str += op.getMemory().getDefiningOp<seq::FirMemOp>().getNameAttr();
231 return StringAttr::get(value.getContext(), str);
233 .Case<seq::FirMemReadWriteOp>([&](seq::FirMemReadWriteOp op) {
234 llvm::SmallString<16> str;
235 str += op.getMemory().getDefiningOp<seq::FirMemOp>().getNameAttr();
237 return StringAttr::get(value.getContext(), str);
239 .Case<seq::FirMemOp>([&](seq::FirMemOp op) {
240 llvm::SmallString<16> str;
241 str += op.getMemory().getDefiningOp<seq::FirMemOp>().getNameAttr();
242 str +=
".write_port";
243 return StringAttr::get(value.getContext(), str);
245 .Default([&](
auto op) {
246 if (
auto name = op->template getAttrOfType<StringAttr>(
"sv.namehint"))
248 llvm::errs() <<
"Unknown op: " << *op <<
"\n";
249 return StringAttr::get(value.getContext(),
"");
255 llvm::ImmutableList<DebugPoint> history = {},
256 StringRef comment =
"") {
257 std::string pathString;
258 llvm::raw_string_ostream osPath(pathString);
259 object.instancePath.print(osPath);
260 os <<
"Object(" << pathString <<
"." <<
object.getName().getValue() <<
"["
261 <<
object.bitPos <<
"]";
263 os <<
", delay=" << delay;
264 if (!history.isEmpty()) {
266 llvm::interleaveComma(history, os, [&](
DebugPoint p) { p.
print(os); });
269 if (!comment.empty())
270 os <<
", comment=\"" << comment <<
"\"";
276 int64_t maxDelay = 0;
277 for (
auto &path : paths)
278 maxDelay = std::max(maxDelay, path.getDelay());
282using namespace circt;
289void OpenPath::print(llvm::raw_ostream &os)
const {
293void DebugPoint::print(llvm::raw_ostream &os)
const {
302 if (
auto *
object = std::get_if<Object>(&endPoint)) {
305 auto &[module, resultNumber, bitPos] =
306 *std::get_if<DataflowPath::OutputPort>(&endPoint);
307 auto outputPortName =
root.getOutputName(resultNumber);
308 os <<
"Object($root." << outputPortName <<
"[" << bitPos <<
"])";
313 os <<
"root=" <<
root.getModuleName() <<
", ";
327 instancePath = cache.
concatPath(path, instancePath);
333 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
336 if (debugPointFactory)
337 this->history =
mapList(debugPointFactory, this->history,
351 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
353 this->path.prependPaths(cache, debugPointFactory, path);
356 assert(
root &&
"root is not a hw::HWModuleOp");
361 if (
auto *
object = std::get_if<Object>(&endPoint))
362 object->prependPaths(cache, path);
369 if (
auto *
object = std::get_if<Object>(&endPoint))
370 return object->value.getLoc();
373 auto &[module, resultNumber, bitPos] =
374 *std::get_if<DataflowPath::OutputPort>(&endPoint);
375 return module.getOutputLoc(resultNumber);
383 llvm::json::Array result;
384 for (
auto op : path) {
385 llvm::json::Object obj;
386 obj[
"instance_name"] = op.getInstanceName();
387 obj[
"module_name"] = op.getReferencedModuleNames()[0];
388 result.push_back(std::move(obj));
394 return llvm::json::Object{
395 {
"instance_path",
toJSON(
object.instancePath)},
396 {
"name",
object.getName().getValue()},
397 {
"bit_pos",
object.bitPos},
404 if (
auto *
object = std::get_if<circt::synth::Object>(&path))
407 auto &[module, resultNumber, bitPos] =
408 *std::get_if<DataflowPath::OutputPort>(&path);
409 return llvm::json::Object{
410 {
"instance_path", {}},
411 {
"name", root.getOutputName(resultNumber)},
417 return llvm::json::Object{
419 {
"delay", point.
delay},
424static llvm::json::Value
toJSON(
const OpenPath &path) {
425 llvm::json::Array history;
426 for (
auto &point : path.history)
427 history.push_back(
toJSON(point));
428 return llvm::json::Object{{
"start_point",
toJSON(path.startPoint)},
429 {
"delay", path.delay},
430 {
"history", std::move(history)}};
434 return llvm::json::Object{
437 {
"root", path.
getRoot().getModuleName()},
451 const LongestPathAnalysisOptions &option)
452 : instanceGraph(instanceGraph), option(option) {}
454 std::lock_guard<llvm::sys::SmartMutex<true>> lock(mutex);
455 running.insert(name);
456 llvm::dbgs() <<
"[Timing] " << name <<
" started. running=[";
457 for (
auto &name : running)
458 llvm::dbgs() << name <<
" ";
459 llvm::dbgs() <<
"]\n";
463 std::lock_guard<llvm::sys::SmartMutex<true>> lock(mutex);
464 running.remove(name);
466 llvm::dbgs() <<
"[Timing] " << name <<
" finished. running=[";
467 for (
auto &name : running)
468 llvm::dbgs() << name <<
" ";
469 llvm::dbgs() <<
"]\n";
473 const LocalVisitor *getLocalVisitor(StringAttr name)
const;
476 LocalVisitor *getLocalVisitorMutable(StringAttr name)
const;
520 : ctx(nullptr, LongestPathAnalysisOptions(false, true, false)), loc(loc) {
521 mlir::OpBuilder builder(loc->getContext());
522 moduleOp = mlir::ModuleOp::create(builder, loc);
523 emptyName = StringAttr::get(loc->getContext(),
"");
528 LogicalResult initializePipeline();
534 LogicalResult analyzeOperation(
535 OpResult value,
size_t bitPos,
536 SmallVectorImpl<std::tuple<size_t, size_t, int64_t>> &results);
542 FailureOr<LocalVisitor *> getOrComputeLocalVisitor(Operation *op);
547 static mlir::FunctionType getFunctionTypeForOp(Operation *op);
551 llvm::DenseMap<std::pair<mlir::OperationName, mlir::FunctionType>,
552 std::unique_ptr<LocalVisitor>>
557 constexpr static StringRef pipelineStr =
558 "hw.module(hw-aggregate-to-comb,convert-comb-to-synth,cse,canonicalize)";
571 return mlir::FunctionType::get(op->getContext(), op->getOperandTypes(),
572 op->getResultTypes());
582 LogicalResult initializeAndRun();
584 void waitUntilDone()
const;
588 FailureOr<ArrayRef<OpenPath>> getOrComputePaths(Value value,
size_t bitPos);
592 ArrayRef<OpenPath> getCachedPaths(Value value,
size_t bitPos)
const;
597 std::pair<int64_t, llvm::ImmutableList<DebugPoint>>>;
605 return fromInputPortToEndPoint;
608 return fromOutputPortToStartPoint;
613 return instancePathCache.get();
617 return debugPointFactory.get();
621 void putUnclosedResult(
const Object &
object, int64_t delay,
622 llvm::ImmutableList<DebugPoint> history,
623 ObjectToMaxDistance &objectToMaxDistance);
626 llvm::MapVector<std::pair<BlockArgument, size_t>, ObjectToMaxDistance>
633 LogicalResult initializeAndRun(hw::InstanceOp instance);
634 LogicalResult initializeAndRun(hw::OutputOp output);
639 LogicalResult visitValue(Value value,
size_t bitPos,
640 SmallVectorImpl<OpenPath> &results);
642 LogicalResult visit(mlir::BlockArgument argument,
size_t bitPos,
643 SmallVectorImpl<OpenPath> &results);
644 LogicalResult visit(hw::InstanceOp op,
size_t bitPos,
size_t resultNum,
645 SmallVectorImpl<OpenPath> &results);
648 LogicalResult visit(hw::WireOp op,
size_t bitPos,
649 SmallVectorImpl<OpenPath> &results);
651 SmallVectorImpl<OpenPath> &results);
653 SmallVectorImpl<OpenPath> &results);
654 LogicalResult visit(comb::ReplicateOp op,
size_t bitPos,
655 SmallVectorImpl<OpenPath> &results);
658 llvm::EquivalenceClasses<std::pair<Value, size_t>>
ec;
659 DenseMap<std::pair<Value, size_t>, std::pair<Value, size_t>>
ecMap;
660 std::pair<Value, size_t>
findLeader(Value value,
size_t bitpos)
const {
661 return ec.getLeaderValue({value, bitpos});
663 LogicalResult markEquivalent(Value from,
size_t fromBitPos, Value to,
665 SmallVectorImpl<OpenPath> &results);
668 LogicalResult visit(aig::AndInverterOp op,
size_t bitPos,
669 SmallVectorImpl<OpenPath> &results);
670 LogicalResult visit(mig::MajorityInverterOp op,
size_t bitPos,
671 SmallVectorImpl<OpenPath> &results);
673 SmallVectorImpl<OpenPath> &results);
675 SmallVectorImpl<OpenPath> &results);
676 LogicalResult visit(
comb::OrOp op,
size_t bitPos,
677 SmallVectorImpl<OpenPath> &results);
679 SmallVectorImpl<OpenPath> &results);
680 LogicalResult addLogicOp(Operation *op,
size_t bitPos,
681 SmallVectorImpl<OpenPath> &results);
682 LogicalResult visit(comb::TruthTableOp op,
size_t bitPos,
683 SmallVectorImpl<OpenPath> &results);
687 SmallVectorImpl<OpenPath> &results) {
692 LogicalResult
visit(seq::FirRegOp op,
size_t bitPos,
693 SmallVectorImpl<OpenPath> &results) {
694 return markStartPoint(op, bitPos, results);
698 SmallVectorImpl<OpenPath> &results) {
699 return markStartPoint(op, bitPos, results);
702 LogicalResult
visit(seq::FirMemReadOp op,
size_t bitPos,
703 SmallVectorImpl<OpenPath> &results) {
704 return markStartPoint(op, bitPos, results);
707 LogicalResult
visit(seq::FirMemReadWriteOp op,
size_t bitPos,
708 SmallVectorImpl<OpenPath> &results) {
709 return markStartPoint(op, bitPos, results);
712 LogicalResult visitDefault(OpResult result,
size_t bitPos,
713 SmallVectorImpl<OpenPath> &results);
716 LogicalResult addEdge(Value to,
size_t toBitPos, int64_t delay,
717 SmallVectorImpl<OpenPath> &results);
718 LogicalResult markStartPoint(Value value,
size_t bitPos,
719 SmallVectorImpl<OpenPath> &results);
720 LogicalResult markRegEndPoint(Value endPoint, Value start, Value reset = {},
721 Value resetValue = {}, Value enable = {});
733 DenseMap<std::pair<Value, size_t>, std::unique_ptr<SmallVector<OpenPath>>>
744 mutable std::condition_variable
cv;
748 bool topLevel =
false;
752 : module(module), ctx(ctx) {
754 std::make_unique<llvm::ImmutableListFactory<DebugPoint>>();
756 ? std::make_unique<circt::igraph::InstancePathCache>(
765 size_t bitPos)
const {
766 std::pair<Value, size_t> valueAndBitPos(value, bitPos);
767 auto leader =
ec.findLeader(valueAndBitPos);
768 if (leader !=
ec.member_end()) {
769 if (*leader != valueAndBitPos) {
783 llvm::ImmutableList<DebugPoint> history,
785 auto &slot = objectToMaxDistance[object];
786 if (slot.first >= delay && delay != 0)
788 slot = {delay, history};
793 std::unique_lock<std::mutex> lock(
mutex);
794 cv.wait(lock, [
this] {
return done.load(); });
798 Value reset, Value resetValue,
801 auto record = [&](
size_t endPointBitPos, Value value,
size_t bitPos) {
805 for (
auto &path : *result) {
806 if (
auto blockArg = dyn_cast<BlockArgument>(path.startPoint.value)) {
809 {{}, endPoint, endPointBitPos}, path.delay, path.history,
820 for (
size_t i = 0, e = bitWidth; i < e; ++i) {
825 if (failed(record(i, start, i)))
831 for (
size_t i = 0, e = bitWidth; i < e; ++i) {
832 if (failed(record(i, reset, 0)) || failed(record(i, resetValue, i)))
838 for (
size_t i = 0, e = bitWidth; i < e; ++i) {
839 if (failed(record(i, enable, 0)))
847 Value to,
size_t toBitPos,
848 SmallVectorImpl<OpenPath> &results) {
849 [[maybe_unused]]
auto leader =
ec.getOrInsertLeaderValue({to, toBitPos});
851 [[maybe_unused]]
auto newLeader =
852 ec.unionSets({to, toBitPos}, {from, fromBitPos});
853 assert(leader == *newLeader);
858 SmallVectorImpl<OpenPath> &results) {
862 for (
auto &path : *result) {
864 newPath.delay += delay;
865 results.push_back(newPath);
871 SmallVectorImpl<OpenPath> &results) {
877 SmallVectorImpl<OpenPath> &results) {
880 size_t depth = op.getInputs().size() / 2;
881 for (
auto input : op.getInputs()) {
882 if (failed(
addEdge(input, bitPos, depth, results)))
889 SmallVectorImpl<OpenPath> &results) {
894 SmallVectorImpl<OpenPath> &results) {
899 SmallVectorImpl<OpenPath> &results) {
904 SmallVectorImpl<OpenPath> &results) {
906 if (failed(
addEdge(op.getCond(), 0, 1, results)) ||
907 failed(
addEdge(op.getTrueValue(), bitPos, 1, results)) ||
908 failed(
addEdge(op.getFalseValue(), bitPos, 1, results)))
915 SmallVectorImpl<OpenPath> &results) {
916 for (
auto input : op.getInputs()) {
917 if (failed(
addEdge(input, 0, 1, results)))
924 SmallVectorImpl<OpenPath> &results) {
927 bitPos + op.getLowBit(), results);
932 SmallVectorImpl<OpenPath> &results) {
938 SmallVectorImpl<OpenPath> &results) {
944 SmallVectorImpl<OpenPath> &results) {
945 return markEquivalent(op, bitPos, op.getInput(), bitPos, results);
950 SmallVectorImpl<OpenPath> &results) {
951 auto moduleName = op.getReferencedModuleNameAttr();
952 auto value = op->getResult(resultNum);
956 if (!
ctx->instanceGraph)
960 auto *node =
ctx->instanceGraph->lookup(moduleName);
961 assert(node &&
"module not found");
965 if (!isa<hw::HWModuleOp>(node->getModule()))
968 auto *localVisitor =
ctx->getLocalVisitorMutable(moduleName);
970 auto module = localVisitor->getHWModuleOp();
971 auto operand =
module.getBodyBlock()->getTerminator()->getOperand(resultNum);
972 auto result = localVisitor->getOrComputePaths(operand, bitPos);
976 for (
auto &path : *result) {
977 auto delay = path.delay;
978 auto history = path.history;
981 auto startPointPoint = path.startPoint;
983 auto arg = dyn_cast<BlockArgument>(startPointPoint.value);
987 if (
ctx->doTraceDebugPoints()) {
990 p.object.instancePath =
991 instancePathCache->prependInstance(op, p.object.instancePath);
995 DebugPoint({}, value, bitPos, delay,
"output port"), newHistory);
998 results.emplace_back(newPath, startPointPoint.value,
999 startPointPoint.bitPos, delay, newHistory);
1005 startPointPoint.bitPos);
1008 for (
auto path : *result) {
1010 if (
ctx->doTraceDebugPoints()) {
1014 p.object.instancePath =
1015 instancePathCache->prependInstance(op, p.object.instancePath);
1016 p.delay += path.delay;
1019 DebugPoint debugPoint({}, value, bitPos, delay + path.delay,
1024 path.
delay += delay;
1027 results.push_back(path);
1034 SmallVectorImpl<OpenPath> &results) {
1036 size_t newBitPos = bitPos;
1037 for (
auto operand : llvm::reverse(op.getInputs())) {
1039 if (newBitPos >= size) {
1046 llvm::report_fatal_error(
"Should not reach here");
1051 SmallVectorImpl<OpenPath> &results) {
1052 auto size = op->getNumOperands();
1053 auto cost = llvm::Log2_64_Ceil(size);
1055 for (
auto operand : op->getOperands())
1056 if (failed(
addEdge(operand, bitPos, cost, results)))
1063 SmallVectorImpl<OpenPath> &results) {
1064 if (!isa_and_nonnull<hw::HWDialect, comb::CombDialect>(
1065 value.getDefiningOp()->getDialect()))
1069 llvm::dbgs() <<
"Visiting default: ";
1070 llvm::dbgs() <<
" " << value <<
"[" << bitPos <<
"]\n";
1072 SmallVector<std::tuple<size_t, size_t, int64_t>> oracleResults;
1075 if (failed(paths)) {
1077 llvm::dbgs() <<
"Failed to get results for: " << value <<
"[" << bitPos
1082 auto *op = value.getDefiningOp();
1083 for (
auto [inputPortIndex, startPointBitPos, delay] : oracleResults) {
1085 llvm::dbgs() <<
"Adding edge: " << value <<
"[" << bitPos <<
"] -> "
1086 << op->getOperand(inputPortIndex) <<
"[" << startPointBitPos
1087 <<
"] with delay " << delay <<
"\n";
1089 if (failed(
addEdge(op->getOperand(inputPortIndex), startPointBitPos, delay,
1097 SmallVectorImpl<OpenPath> &results) {
1098 assert(arg.getOwner() == module.getBodyBlock());
1101 auto newHistory =
ctx->doTraceDebugPoints()
1103 DebugPoint({}, arg, bitPos, 0,
"input port"), {})
1105 OpenPath newPoint({}, arg, bitPos, 0, newHistory);
1106 results.push_back(newPoint);
1114 return ArrayRef<OpenPath>{};
1116 if (
ec.contains({value, bitPos})) {
1117 auto leader =
ec.findLeader({value, bitPos});
1119 if (*leader != std::pair(value, bitPos)) {
1126 return ArrayRef<OpenPath>(*it->second);
1128 auto results = std::make_unique<SmallVector<OpenPath>>();
1129 if (failed(
visitValue(value, bitPos, *results)))
1135 llvm::dbgs() << value <<
"[" << bitPos <<
"] "
1136 <<
"Found " << results->size() <<
" paths\n";
1137 llvm::dbgs() <<
"====Paths:\n";
1138 for (
auto &path : *results) {
1139 path.print(llvm::dbgs());
1140 llvm::dbgs() <<
"\n";
1142 llvm::dbgs() <<
"====\n";
1145 auto insertedResult =
1146 cachedResults.try_emplace({value, bitPos}, std::move(results));
1147 assert(insertedResult.second);
1148 return ArrayRef<OpenPath>(*insertedResult.first->second);
1152 SmallVectorImpl<OpenPath> &results) {
1154 llvm::dbgs() <<
"Visiting: ";
1155 llvm::dbgs() <<
" " << value <<
"[" << bitPos <<
"]\n";
1158 if (
auto blockArg = dyn_cast<mlir::BlockArgument>(value))
1159 return visit(blockArg, bitPos, results);
1161 auto *op = value.getDefiningOp();
1163 TypeSwitch<Operation *, LogicalResult>(op)
1165 aig::AndInverterOp, mig::MajorityInverterOp,
comb::AndOp,
1168 seq::FirMemReadOp, seq::FirMemReadWriteOp, hw::WireOp>(
1170 size_t idx = results.size();
1171 auto result =
visit(op, bitPos, results);
1172 if (
ctx->doTraceDebugPoints())
1173 if (
auto name = op->template getAttrOfType<StringAttr>(
1176 for (
auto i = idx, e = results.size(); i < e; ++i) {
1177 DebugPoint debugPoint({}, value, bitPos, results[i].delay,
1180 debugPoint, results[i].history);
1181 results[i].history = newHistory;
1186 .Case<hw::InstanceOp>([&](hw::InstanceOp op) {
1187 return visit(op, bitPos, cast<OpResult>(value).getResultNumber(),
1190 .Default([&](
auto op) {
1191 return visitDefault(cast<OpResult>(value), bitPos, results);
1197 const auto *childVisitor =
1198 ctx->getLocalVisitorMutable(instance.getReferencedModuleNameAttr());
1204 for (
const auto &[
object, openPaths] :
1205 childVisitor->getFromInputPortToEndPoint()) {
1206 auto [arg, argBitPos] = object;
1207 for (
auto [point, delayAndHistory] : openPaths) {
1208 auto [instancePath, endPoint, endPointBitPos] = point;
1209 auto [delay, history] = delayAndHistory;
1213 auto computedResults =
1215 if (failed(computedResults))
1218 for (
auto &result : *computedResults) {
1219 auto newHistory =
ctx->doTraceDebugPoints()
1224 p.object.instancePath = newPath;
1225 p.delay += result.delay;
1229 if (
auto newPort = dyn_cast<BlockArgument>(result.startPoint.value)) {
1231 {newPath, endPoint, endPointBitPos}, result.delay + delay,
1236 newPath, result.startPoint.value, result.startPoint.bitPos,
1237 result.delay + delay,
1239 newHistory, result.history)
1247 for (
auto instance : instance->getResults()) {
1248 for (
size_t i = 0, e =
getBitWidth(instance); i < e; ++i) {
1250 if (failed(computedResults))
1258 for (OpOperand &operand : output->getOpOperands()) {
1259 for (
size_t i = 0, e =
getBitWidth(operand.get()); i < e; ++i) {
1260 auto &recordOutput =
1263 if (failed(computedResults))
1265 for (
const auto &result : *computedResults) {
1275 LLVM_DEBUG({
ctx->notifyStart(module.getModuleNameAttr()); });
1276 if (
ctx->doLazyComputation())
1280 for (
auto blockArgument :
module.getBodyBlock()->getArguments())
1281 for (size_t i = 0, e = getBitWidth(blockArgument); i < e; ++i)
1284 auto walkResult =
module->walk([&](Operation *op) {
1286 mlir::TypeSwitch<Operation *, LogicalResult>(op)
1287 .Case<seq::FirRegOp>([&](seq::FirRegOp op) {
1288 return markRegEndPoint(op, op.getNext(), op.getReset(),
1289 op.getResetValue());
1291 .Case<seq::CompRegOp>([&](
auto op) {
1292 return markRegEndPoint(op, op.getInput(), op.getReset(),
1293 op.getResetValue());
1295 .Case<seq::FirMemWriteOp>([&](
auto op) {
1297 return markRegEndPoint(op.getMemory(), op.getData(), {}, {},
1300 .Case<seq::FirMemReadWriteOp>([&](seq::FirMemReadWriteOp op) {
1302 return markRegEndPoint(op.getMemory(), op.getWriteData(), {}, {},
1309 for (
size_t i = 0, e =
getBitWidth(op); i < e; ++i)
1310 if (failed(getOrComputePaths(op, i)))
1314 .Case<hw::InstanceOp, hw::OutputOp>(
1315 [&](
auto op) {
return initializeAndRun(op); })
1316 .Default([](
auto op) {
return success(); });
1318 return WalkResult::interrupt();
1319 return WalkResult::advance();
1323 std::lock_guard<std::mutex> lock(mutex);
1327 LLVM_DEBUG({ ctx->
notifyEnd(module.getModuleNameAttr()); });
1328 return failure(walkResult.wasInterrupted());
1348 return it->second.get();
1355FailureOr<LocalVisitor *>
1358 auto opName = op->getName();
1360 auto key = std::make_pair(opName, functionType);
1361 auto it =
cache.find(key);
1362 if (it !=
cache.end())
1363 return it->second.get();
1365 SmallVector<hw::PortInfo> ports;
1367 auto getType = [&](Type type) -> Type {
1368 if (type.isInteger())
1370 auto bitWidth = hw::getBitWidth(type);
1373 return IntegerType::get(op->getContext(), bitWidth);
1381 portInfo.
type = type;
1382 ports.push_back(portInfo);
1386 for (
auto input : op->getOperands()) {
1387 auto type = getType(input.getType());
1390 addPort(type, hw::ModulePort::Direction::Input);
1394 SmallVector<Type> resultsTypes;
1395 for (Value result : op->getResults()) {
1396 auto type = getType(result.getType());
1399 addPort(type, hw::ModulePort::Direction::Output);
1400 resultsTypes.push_back(type);
1404 OpBuilder builder(op->getContext());
1405 builder.setInsertionPointToEnd(
moduleOp->getBody());
1408 auto moduleName = builder.getStringAttr(
"module_" + Twine(
cache.size()));
1410 hw::HWModuleOp::create(builder, op->getLoc(), moduleName, ports);
1413 builder.setInsertionPointToStart(hwModule.getBodyBlock());
1414 auto *cloned = builder.clone(*op);
1422 for (
auto arg : hwModule.getBodyBlock()->getArguments()) {
1424 auto idx = arg.getArgNumber();
1427 if (input.getType() != cloned->getOperand(idx).getType())
1429 cloned->getOperand(idx).getType(), input);
1431 cloned->setOperand(idx, input);
1436 SmallVector<Value> outputs;
1437 for (
auto result : cloned->getResults()) {
1438 auto idx = result.getResultNumber();
1441 if (result.getType() != resultsTypes[idx])
1446 outputs.push_back(result);
1449 hwModule.getBodyBlock()->getTerminator()->setOperands(outputs);
1453 return mlir::emitError(
loc)
1454 <<
"Failed to initialize pipeline, possibly passes used in the "
1455 "analysis are not registered";
1458 return mlir::emitError(
loc) <<
"Failed to run lowering pipeline";
1461 auto localVisitor = std::make_unique<LocalVisitor>(hwModule, &
ctx);
1462 if (failed(localVisitor->initializeAndRun()))
1466 auto [iterator, inserted] =
cache.insert({key, std::move(localVisitor)});
1467 assert(inserted &&
"Cache insertion must succeed for new key");
1468 return iterator->second.get();
1472 OpResult value,
size_t bitPos,
1473 SmallVectorImpl<std::tuple<size_t, size_t, int64_t>> &results) {
1474 auto *op = value.getDefiningOp();
1476 if (failed(localVisitorResult))
1479 auto *localVisitor = *localVisitorResult;
1483 localVisitor->getHWModuleOp().getBodyBlock()->getTerminator()->getOperand(
1484 value.getResultNumber());
1485 auto openPaths = localVisitor->getOrComputePaths(operand, bitPos);
1486 if (failed(openPaths))
1489 results.reserve(openPaths->size() + results.size());
1490 for (
auto &path : *openPaths) {
1493 BlockArgument blockArg = cast<BlockArgument>(path.startPoint.value);
1494 auto inputPortIndex = blockArg.getArgNumber();
1496 std::make_tuple(inputPortIndex, path.startPoint.bitPos, path.delay));
1503 passManager = std::make_unique<mlir::PassManager>(
loc->getContext());
1517 Impl(Operation *module, mlir::AnalysisManager &am,
1518 const LongestPathAnalysisOptions &option);
1532 SmallVectorImpl<DataflowPath> &results);
1537 template <
bool elaborate>
1540 SmallVectorImpl<DataflowPath> &results)
const;
1545 SmallVectorImpl<DataflowPath> &results)
const;
1551 SmallVectorImpl<DataflowPath> &results)
const;
1560 FailureOr<int64_t>
getMaxDelay(Value value, int64_t bitPos);
1572 SmallVectorImpl<DataflowPath> &results);
1581 Value value,
size_t bitPos, SmallVectorImpl<DataflowPath> &results) {
1586 const Object &originalObject, Value value,
size_t bitPos,
1587 SmallVectorImpl<DataflowPath> &results) {
1588 auto parentHWModule =
1590 if (!parentHWModule)
1591 return mlir::emitError(value.getLoc())
1592 <<
"query value is not in a HWModuleOp";
1593 auto *localVisitor =
1598 auto *instancePathCache = localVisitor->getInstancePathCache();
1599 size_t oldIndex = results.size();
1605 llvm::dbgs() <<
"Running " << parentHWModule.getModuleNameAttr() <<
" "
1606 << value <<
" " << bitPos <<
"\n";
1608 auto paths = localVisitor->getOrComputePaths(value, bitPos);
1612 for (
auto &path : *paths) {
1613 auto arg = dyn_cast<BlockArgument>(path.startPoint.value);
1614 if (!arg || localVisitor->isTopLevel()) {
1616 results.push_back({originalObject, path, parentHWModule});
1620 auto newObject = originalObject;
1621 assert(node &&
"If an instance graph is not available, localVisitor must "
1623 for (
auto *inst : node->uses()) {
1624 auto startIndex = results.size();
1625 if (instancePathCache)
1626 newObject.instancePath = instancePathCache->appendInstance(
1627 originalObject.instancePath, inst->getInstance());
1629 auto result = computeGlobalPaths(
1630 newObject, inst->getInstance()->getOperand(arg.getArgNumber()),
1631 path.startPoint.bitPos, results);
1634 for (
auto i = startIndex, e = results.size(); i < e; ++i)
1635 results[i].setDelay(results[i].getDelay() + path.delay);
1643template <
bool elaborate>
1645 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results)
const {
1646 auto collectClosedPaths = [&](StringAttr name,
1647 SmallVectorImpl<DataflowPath> &localResults,
1649 if (!isAnalysisAvailable(name))
1652 for (
auto &[point, state] : visitor->getEndPointResults()) {
1653 for (
const auto &dataFlow : state) {
1654 if constexpr (elaborate) {
1658 visitor->getHWModuleOp(), top);
1659 for (
auto &instancePath : topToRoot) {
1660 localResults.emplace_back(point, dataFlow,
1662 localResults.back().prependPaths(*visitor->getInstancePathCache(),
1663 visitor->getDebugPointFactory(),
1667 localResults.emplace_back(point, dataFlow, visitor->getHWModuleOp());
1676 llvm::MapVector<StringAttr, SmallVector<DataflowPath>> resultsMap;
1678 for (
auto *child : llvm::post_order(node))
1679 resultsMap[child->getModule().getModuleNameAttr()] = {};
1681 mlir::parallelForEach(
1682 node->getModule().getContext(), resultsMap,
1683 [&](
auto &it) { collectClosedPaths(it.first, it.second, node); });
1685 for (
auto &[name, localResults] : resultsMap)
1686 results.append(localResults.begin(), localResults.end());
1688 collectClosedPaths(moduleName, results);
1695 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results)
const {
1700 for (
auto &[key, value] : visitor->getFromInputPortToEndPoint()) {
1701 auto [arg, argBitPos] = key;
1702 for (
auto [point, delayAndHistory] : value) {
1703 auto [path, start, startBitPos] = point;
1704 auto [delay, history] = delayAndHistory;
1705 results.emplace_back(
Object(path, start, startBitPos),
1706 OpenPath({}, arg, argBitPos, delay, history),
1707 visitor->getHWModuleOp());
1715 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results)
const {
1720 for (
auto &[key, value] : visitor->getFromOutputPortToStartPoint()) {
1721 auto [resultNum, bitPos] = key;
1722 for (
auto [point, delayAndHistory] : value) {
1723 auto [path, start, startBitPos] = point;
1724 auto [delay, history] = delayAndHistory;
1725 results.emplace_back(
1726 std::make_tuple(visitor->getHWModuleOp(), resultNum, bitPos),
1727 OpenPath(path, start, startBitPos, delay, history),
1728 visitor->getHWModuleOp());
1736 const LongestPathAnalysisOptions &option)
1737 : ctx(isa<
mlir::ModuleOp>(moduleOp)
1741 if (
auto module = dyn_cast<mlir::ModuleOp>(moduleOp)) {
1743 llvm::report_fatal_error(
"Failed to run longest path analysis");
1744 }
else if (
auto hwMod = dyn_cast<hw::HWModuleOp>(moduleOp)) {
1746 llvm::report_fatal_error(
"Failed to run longest path analysis");
1748 llvm::report_fatal_error(
"Analysis scheduled on invalid operation");
1756 std::make_unique<LocalVisitor>(module, &ctx)});
1758 it.first->second->setTopLevel();
1759 return it.first->second->initializeAndRun();
1766 llvm::SetVector<Operation *> visited;
1768 if (topNameAttr && topNameAttr.getValue() !=
"") {
1769 auto *topNode = instanceGraph->
lookupOrNull(topNameAttr);
1770 if (!topNode || !topNode->getModule() ||
1771 !isa<hw::HWModuleOp>(topNode->getModule())) {
1772 module.emitError() << "top module not found in instance graph "
1778 auto inferredResults = instanceGraph->getInferredTopLevelNodes();
1779 if (failed(inferredResults))
1780 return inferredResults;
1782 for (
auto *node : *inferredResults) {
1783 if (
auto top = dyn_cast<hw::HWModuleOp>(*node->getModule()))
1784 topModules.push_back(top);
1788 SmallVector<igraph::InstanceGraphNode *> worklist;
1789 for (
auto topNode : topModules)
1790 worklist.push_back(instanceGraph->lookup(topNode.getModuleNameAttr()));
1793 while (!worklist.empty()) {
1794 auto *node = worklist.pop_back_val();
1795 assert(node &&
"node should not be null");
1796 auto op = node->getModule();
1797 if (!isa_and_nonnull<hw::HWModuleOp>(op) || !visited.insert(op))
1800 for (
auto *child : *node) {
1801 auto childOp = child->getInstance();
1802 if (!childOp || childOp->hasAttr(
"doNotPrint"))
1805 worklist.push_back(child->getTarget());
1811 for (
auto module : topModules) {
1812 auto *topNode = instanceGraph->lookup(module.getModuleNameAttr());
1813 for (
auto *node : llvm::post_order(topNode))
1814 if (node && node->getModule())
1815 if (
auto hwMod = dyn_cast<hw::HWModuleOp>(*node->getModule())) {
1816 if (visited.contains(hwMod))
1818 {hwMod.getModuleNameAttr(),
1819 std::make_unique<LocalVisitor>(hwMod, &ctx)});
1822 ctx.
localVisitors[topNode->getModule().getModuleNameAttr()]->setTopLevel();
1825 return mlir::failableParallelForEach(
1827 [&](
auto &it) { return it.second->initializeAndRun(); });
1831 StringAttr moduleName)
const {
1839 SmallVector<DataflowPath> results;
1843 int64_t totalDelay = 0;
1844 for (
size_t i = 0; i < bitWidth; ++i) {
1847 auto result = computeGlobalPaths(value, i, results);
1852 totalDelay += maxDelay;
1854 return llvm::divideCeil(totalDelay, bitWidth);
1859 SmallVector<DataflowPath> results;
1860 auto collectAndFindMax = ([&](int64_t bitPos) -> FailureOr<int64_t> {
1862 auto result = computeGlobalPaths(value, bitPos, results);
1868 return collectAndFindMax(bitPos);
1874 int64_t maxDelay = 0;
1875 for (
size_t i = 0; i < bitWidth; ++i) {
1876 auto result = collectAndFindMax(i);
1879 maxDelay = std::max(maxDelay, *result);
1884FailureOr<ArrayRef<OpenPath>>
1886 auto parentHWModule =
1888 if (!parentHWModule)
1889 return mlir::emitError(value.getLoc())
1890 <<
"query value is not in a HWModuleOp";
1892 "In incremental mode, there should be only one local visitor");
1894 auto *localVisitor =
1897 return mlir::emitError(value.getLoc())
1898 <<
"the local visitor for the given value does not exist";
1906LongestPathAnalysis::~LongestPathAnalysis() {
delete impl; }
1908LongestPathAnalysis::LongestPathAnalysis(
1909 Operation *moduleOp, mlir::AnalysisManager &am,
1911 : impl(new
Impl(moduleOp, am, option)), ctx(moduleOp->getContext()) {
1913 llvm::dbgs() <<
"LongestPathAnalysis created\n";
1915 llvm::dbgs() <<
" - Collecting debug info\n";
1917 llvm::dbgs() <<
" - Lazy computation enabled\n";
1919 llvm::dbgs() <<
" - Keeping only max delay paths\n";
1924 return impl->isAnalysisAvailable(moduleName);
1928 return impl->getAverageMaxDelay(value);
1933 return impl->getMaxDelay(value, bitPos);
1938 SmallVectorImpl<DataflowPath> &results,
1939 bool elaboratePaths)
const {
1943 return impl->collectClosedPaths<
true>(moduleName, results);
1944 return impl->collectClosedPaths<
false>(moduleName, results);
1948 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results)
const {
1952 return impl->collectInputToInternalPaths(moduleName, results);
1956 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results)
const {
1960 return impl->collectInternalToOutputPaths(moduleName, results);
1965 SmallVectorImpl<DataflowPath> &results,
1966 bool elaboratePaths)
const {
1977 return impl->getTopModules();
1980FailureOr<ArrayRef<OpenPath>>
1985 return impl->computeLocalPaths(value, bitPos);
1989 Value value,
size_t bitPos, SmallVectorImpl<DataflowPath> &results) {
1991 return mlir::emitError(value.getLoc()) <<
"analysis has been invalidated";
1993 return impl->computeGlobalPaths(value, bitPos, results);
2001 Operation *op)
const {
2005 auto parentHWModule =
2007 if (!parentHWModule)
2009 auto *localVisitor =
2010 impl->ctx.getLocalVisitor(parentHWModule.getModuleNameAttr());
2016 return llvm::all_of(op->getResults(), [localVisitor](Value value) {
2017 for (int64_t i = 0, e = getBitWidth(value); i < e; ++i) {
2018 auto path = localVisitor->getCachedPaths(value, i);
2031 Operation *op, ValueRange replacement) {
2052 llvm::DenseSet<DataflowPath::EndPointType> seen;
2053 for (
size_t i = 0; i <
paths.size(); ++i) {
2054 if (seen.insert(
paths[i].getEndPoint()).second)
2055 paths[seen.size() - 1] = std::move(
paths[i]);
2058 paths.resize(seen.size());
2060 llvm::DenseSet<Object> seen;
2061 for (
size_t i = 0; i <
paths.size(); ++i) {
2062 if (seen.insert(
paths[i].getStartPoint()).second)
2063 paths[seen.size() - 1] = std::move(
paths[i]);
2065 paths.resize(seen.size());
assert(baseType &&"element must be base type")
static void printObjectImpl(llvm::raw_ostream &os, const Object &object, int64_t delay=-1, llvm::ImmutableList< DebugPoint > history={}, StringRef comment="")
static llvm::ImmutableList< DebugPoint > mapList(llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, llvm::ImmutableList< DebugPoint > list, llvm::function_ref< DebugPoint(DebugPoint)> fn)
static llvm::ImmutableList< DebugPoint > concatList(llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, llvm::ImmutableList< DebugPoint > lhs, llvm::ImmutableList< DebugPoint > rhs)
static void filterPaths(SmallVectorImpl< OpenPath > &results, bool keepOnlyMaxDelay, bool isLocalScope)
static void deduplicatePathsImpl(SmallVectorImpl< T > &results, size_t startIndex, llvm::function_ref< Key(const T &)> keyFn, llvm::function_ref< int64_t(const T &)> delayFn)
static StringAttr getNameImpl(Value value)
static int64_t getMaxDelayInPaths(ArrayRef< T > paths)
This class provides a thread-safe interface to access the analysis results.
circt::igraph::InstanceGraph * instanceGraph
const LocalVisitor * getLocalVisitor(StringAttr name) const
void notifyEnd(StringAttr name)
bool doTraceDebugPoints() const
llvm::sys::SmartMutex< true > mutex
bool doKeepOnlyMaxDelayPaths() const
LongestPathAnalysisOptions option
llvm::MapVector< StringAttr, std::unique_ptr< LocalVisitor > > localVisitors
bool isRunningParallel() const
LocalVisitor * getLocalVisitorMutable(StringAttr name) const
StringAttr getTopModuleName() const
Context(igraph::InstanceGraph *instanceGraph, const LongestPathAnalysisOptions &option)
llvm::SetVector< StringAttr > running
bool isLocalScope() const
bool doLazyComputation() const
void notifyStart(StringAttr name)
hw::HWModuleOp getHWModuleOp() const
ArrayRef< OpenPath > getCachedPaths(Value value, size_t bitPos) const
LogicalResult addEdge(Value to, size_t toBitPos, int64_t delay, SmallVectorImpl< OpenPath > &results)
LogicalResult visitValue(Value value, size_t bitPos, SmallVectorImpl< OpenPath > &results)
LogicalResult addLogicOp(Operation *op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
std::unique_ptr< llvm::ImmutableListFactory< DebugPoint > > debugPointFactory
DenseMap< std::pair< Value, size_t >, std::pair< Value, size_t > > ecMap
llvm::MapVector< Object, std::pair< int64_t, llvm::ImmutableList< DebugPoint > > > ObjectToMaxDistance
LogicalResult markRegEndPoint(Value endPoint, Value start, Value reset={}, Value resetValue={}, Value enable={})
std::pair< Value, size_t > findLeader(Value value, size_t bitpos) const
LogicalResult visitDefault(OpResult result, size_t bitPos, SmallVectorImpl< OpenPath > &results)
FailureOr< ArrayRef< OpenPath > > getOrComputePaths(Value value, size_t bitPos)
const auto & getFromInputPortToEndPoint() const
llvm::ImmutableListFactory< DebugPoint > * getDebugPointFactory() const
LogicalResult visit(seq::FirMemReadOp op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
LogicalResult markStartPoint(Value value, size_t bitPos, SmallVectorImpl< OpenPath > &results)
LogicalResult markEquivalent(Value from, size_t fromBitPos, Value to, size_t toBitPos, SmallVectorImpl< OpenPath > &results)
const auto & getEndPointResults() const
llvm::MapVector< std::pair< BlockArgument, size_t >, ObjectToMaxDistance > fromInputPortToEndPoint
DenseMap< std::pair< Value, size_t >, std::unique_ptr< SmallVector< OpenPath > > > cachedResults
hw::HWModuleOp Context * ctx
llvm::MapVector< std::tuple< size_t, size_t >, ObjectToMaxDistance > fromOutputPortToStartPoint
LogicalResult visit(seq::CompRegOp op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
void getInternalPaths(SmallVectorImpl< DataflowPath > &results) const
DenseMap< Object, SmallVector< OpenPath > > endPointResults
LogicalResult visit(seq::FirRegOp op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
LogicalResult initializeAndRun()
llvm::EquivalenceClasses< std::pair< Value, size_t > > ec
LogicalResult visit(seq::FirMemReadWriteOp op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
LogicalResult visit(mlir::BlockArgument argument, size_t bitPos, SmallVectorImpl< OpenPath > &results)
std::unique_ptr< OperationAnalyzer > operationAnalyzer
LogicalResult visit(hw::ConstantOp op, size_t bitPos, SmallVectorImpl< OpenPath > &results)
std::condition_variable cv
void putUnclosedResult(const Object &object, int64_t delay, llvm::ImmutableList< DebugPoint > history, ObjectToMaxDistance &objectToMaxDistance)
std::unique_ptr< circt::igraph::InstancePathCache > instancePathCache
circt::igraph::InstancePathCache * getInstancePathCache() const
LocalVisitor(hw::HWModuleOp module, Context *ctx)
const auto & getFromOutputPortToStartPoint() const
void waitUntilDone() const
std::unique_ptr< mlir::PassManager > passManager
static constexpr StringRef pipelineStr
mlir::OwningOpRef< mlir::ModuleOp > moduleOp
FailureOr< LocalVisitor * > getOrComputeLocalVisitor(Operation *op)
llvm::DenseMap< std::pair< mlir::OperationName, mlir::FunctionType >, std::unique_ptr< LocalVisitor > > cache
static mlir::FunctionType getFunctionTypeForOp(Operation *op)
LogicalResult analyzeOperation(OpResult value, size_t bitPos, SmallVectorImpl< std::tuple< size_t, size_t, int64_t > > &results)
LogicalResult initializePipeline()
OperationAnalyzer(Location loc)
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
This is a Node in the InstanceGraph.
This graph tracks modules and where they are instantiated.
InstanceGraphNode * lookupOrNull(StringAttr name)
Lookup an module by name.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
An instance path composed of a series of instances.
InstanceOpInterface top() const
std::variant< Object, OutputPort > EndPointType
DataflowPath & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)
const OpenPath & getPath() const
Location getEndPointLoc()
hw::HWModuleOp getRoot() const
void print(llvm::raw_ostream &os)
void printEndPoint(llvm::raw_ostream &os)
const EndPointType & getEndPoint() const
void notifyOperationModified(Operation *op) override
void notifyOperationReplaced(Operation *op, ValueRange replacement) override
void notifyOperationErased(Operation *op) override
bool isOperationValidToMutate(Operation *op) const
FailureOr< int64_t > getAverageMaxDelay(Value value)
LogicalResult computeGlobalPaths(Value value, size_t bitPos, SmallVectorImpl< DataflowPath > &results)
FailureOr< int64_t > getMaxDelay(Value value, int64_t bitPos=-1)
LogicalResult getInternalPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results, bool elaboratePaths=false) const
FailureOr< ArrayRef< OpenPath > > computeLocalPaths(Value value, size_t bitPos)
LogicalResult getAllPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results, bool elaboratePaths=false) const
LogicalResult getOpenPathsFromInternalToOutputPorts(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results) const
llvm::ArrayRef< hw::HWModuleOp > getTopModules() const
bool isAnalysisAvailable(StringAttr moduleName) const
LogicalResult getOpenPathsFromInputPortsToInternal(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results) const
void merge(const LongestPathCollection &other)
llvm::SmallVector< DataflowPath, 64 > paths
void sortInDescendingOrder()
void dropNonCriticalPaths(bool perEndPoint=true)
Impl(int port)
Start a server on the given port. -1 means to let the OS pick a port.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
llvm::json::Value toJSON(const circt::synth::DataflowPath &path)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Internal implementation for LongestPathAnalysis.
LogicalResult collectInternalToOutputPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results) const
Collect open paths from internal sequential sources to module output ports.
LogicalResult computeGlobalPaths(Value value, size_t bitPos, SmallVectorImpl< DataflowPath > &results)
Compute hierarchical timing paths to (value, bitPos) and append to results.
llvm::ArrayRef< hw::HWModuleOp > getTopModules() const
Top modules inferred or specified for this analysis run.
FailureOr< ArrayRef< OpenPath > > computeLocalPaths(Value value, size_t bitPos)
Compute local open paths to (value, bitPos).
friend class IncrementalLongestPathAnalysis
FailureOr< int64_t > getMaxDelay(Value value, int64_t bitPos)
Return the max delay for a value.
LogicalResult collectClosedPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results) const
Collect register-to-register (closed) paths within the module.
bool isAnalysisAvailable(StringAttr moduleName) const
Return true if we have a LocalVisitor for the given HW module.
LogicalResult initializeAndRun(mlir::ModuleOp module)
Initialize and run analysis for a full MLIR module (hierarchical).
FailureOr< int64_t > getAverageMaxDelay(Value value)
Return average of per-bit max delays for a value.
LogicalResult collectInputToInternalPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results) const
Collect open paths from module input ports to internal sequential sinks.
SmallVector< hw::HWModuleOp > topModules
Top-level HW modules that seed hierarchical analysis.
Context ctx
Analysis context.
This holds the name, type, direction of a module's ports.
A data structure that caches and provides paths to module instances in the IR.
ArrayRef< InstancePath > getRelativePaths(ModuleOpInterface op, InstanceGraphNode *node)
InstancePath concatPath(InstancePath path1, InstancePath path2)
Concatenate two paths.
void print(llvm::raw_ostream &os) const
Configuration options for the longest path analysis.
bool collectDebugInfo
Enable collection of debug points along timing paths.
bool lazyComputation
Enable lazy computation mode for on-demand analysis.
bool keepOnlyMaxDelayPaths
Keep only the maximum delay path per end point.
Object & prependPaths(circt::igraph::InstancePathCache &cache, circt::igraph::InstancePath path)
StringAttr getName() const
void print(llvm::raw_ostream &os) const
OpenPath & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)