Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PrintLongestPathAnalysis.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass prints the longest path analysis results to a file.
10//
11//===----------------------------------------------------------------------===//
12
17#include "circt/Support/LLVM.h"
18#include "mlir/Support/FileUtilities.h"
19#include "mlir/Support/LLVM.h"
20#include "llvm/ADT/DenseMapInfoVariant.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/Support/Format.h"
23#include "llvm/Support/JSON.h"
24#include "llvm/Support/ToolOutputFile.h"
25#include "llvm/Support/raw_ostream.h"
26#include <memory>
27
28#define DEBUG_TYPE "synth-longest-path-analysis"
29using namespace circt;
30using namespace synth;
31
32namespace circt {
33namespace synth {
34#define GEN_PASS_DEF_PRINTLONGESTPATHANALYSIS
35#include "circt/Dialect/Synth/Transforms/SynthPasses.h.inc"
36} // namespace synth
37} // namespace circt
38
39//===----------------------------------------------------------------------===//
40// PrintLongestPathAnalysisPass
41//===----------------------------------------------------------------------===//
42
43namespace {
44struct PrintLongestPathAnalysisPass
45 : public impl::PrintLongestPathAnalysisBase<PrintLongestPathAnalysisPass> {
46 void runOnOperation() override;
47 using PrintLongestPathAnalysisBase::PrintLongestPathAnalysisBase;
48 LogicalResult printAnalysisResult(const LongestPathAnalysis &analysis,
50 hw::HWModuleOp top, llvm::raw_ostream *os,
51 llvm::json::OStream *jsonOS);
52
53private:
54 /// Print timing level statistics showing delay distribution
55 void printTimingLevelStatistics(SmallVectorImpl<DataflowPath> &allTimingPaths,
56 llvm::raw_ostream *os,
57 llvm::json::OStream *jsonOS);
58
59 /// Print detailed information for the top K critical paths
60 void printTopKPathDetails(SmallVectorImpl<DataflowPath> &allTimingPaths,
61 hw::HWModuleOp top, llvm::raw_ostream *os,
62 llvm::json::OStream *jsonOS);
63
64 /// Print detailed history of a timing path showing intermediate debug points
65 void printPathHistory(const OpenPath &timingPath, llvm::raw_ostream &os);
66};
67
68} // namespace
69
70/// Main method to print comprehensive longest path analysis results.
71LogicalResult PrintLongestPathAnalysisPass::printAnalysisResult(
72 const LongestPathAnalysis &analysis, igraph::InstancePathCache &pathCache,
73 hw::HWModuleOp top, llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
74 auto moduleName = top.getModuleNameAttr();
75
76 LongestPathCollection collection(top.getContext());
77
78 // Get all timing paths with full hierarchical elaboration
79 if (failed(analysis.getAllPaths(moduleName, collection.paths, true)))
80 return failure();
81
82 // Emit diagnostics if testing is enabled (for regression testing)
83 if (test) {
84 for (auto &result : collection.paths) {
85 auto fanOutLoc = result.getFanOutLoc();
86 auto diag = mlir::emitRemark(fanOutLoc);
87 SmallString<128> buf;
88 llvm::raw_svector_ostream os(buf);
89 result.print(os);
90 diag << buf;
91 }
92 }
93
94 size_t oldPathCount = collection.paths.size();
95 collection.sortAndDropNonCriticalPathsPerFanOut();
96 auto &longestPathForEachFanOut = collection.paths;
97
98 // Print analysis header
99 if (os) {
100 *os << "# Longest Path Analysis result for " << top.getModuleNameAttr()
101 << "\n"
102 << "Found " << oldPathCount << " paths\n"
103 << "Found " << longestPathForEachFanOut.size()
104 << " unique fanout points\n"
105 << "Maximum path delay: "
106 << (longestPathForEachFanOut.empty()
107 ? 0
108 : longestPathForEachFanOut.front().getDelay())
109 << "\n";
110
111 *os << "## Showing Levels\n";
112 }
113
114 // Handle JSON output.
115 if (jsonOS) {
116 jsonOS->objectBegin();
117 jsonOS->attribute("module_name", top.getModuleNameAttr().getValue());
118 }
119
120 auto deferClose = llvm::make_scope_exit([&]() {
121 if (jsonOS)
122 jsonOS->objectEnd();
123 });
124
125 // Print timing distribution statistics (histogram of delay levels)
126 printTimingLevelStatistics(longestPathForEachFanOut, os, jsonOS);
127
128 // Print detailed information for top K critical paths if requested
129 if (showTopKPercent.getValue() > 0)
130 printTopKPathDetails(longestPathForEachFanOut, top, os, jsonOS);
131
132 return success();
133}
134
135/// Print timing level statistics showing delay distribution across all paths.
136/// This provides a histogram-like view of timing levels in the design, showing
137/// how many paths exist at each delay level and the cumulative percentage.
138/// This is useful for understanding the overall timing characteristics of the
139/// design.
140void PrintLongestPathAnalysisPass::printTimingLevelStatistics(
141 SmallVectorImpl<DataflowPath> &allTimingPaths, llvm::raw_ostream *os,
142 llvm::json::OStream *jsonOS) {
143
144 int64_t totalTimingPoints = allTimingPaths.size();
145 int64_t cumulativeCount = 0;
146
147 if (jsonOS) {
148 jsonOS->attributeBegin("timing_levels");
149 jsonOS->arrayBegin();
150 }
151 auto closeJson = llvm::make_scope_exit([&]() {
152 if (jsonOS) {
153 jsonOS->arrayEnd();
154 jsonOS->attributeEnd();
155 }
156 });
157
158 // Process paths grouped by delay level (paths are already sorted by delay)
159 if (allTimingPaths.empty())
160 return;
161 for (int64_t index = allTimingPaths.size() - 1; index >= 0;) {
162 int64_t oldIndex = index;
163 auto currentDelay = allTimingPaths[index--].getDelay();
164
165 // Count all paths with the same delay value to create histogram bins
166 while (index >= 0 && allTimingPaths[index].getDelay() == currentDelay)
167 --index;
168
169 int64_t pathsWithSameDelay = oldIndex - index;
170
171 cumulativeCount += pathsWithSameDelay;
172
173 // Calculate cumulative percentage to show timing distribution
174 double cumulativePercentage =
175 (double)cumulativeCount / totalTimingPoints * 100.0;
176
177 // Print formatted timing level statistics in tabular format
178 // Format: Level = delay_value . Count = path_count . percentage%
179 if (os)
180 *os << llvm::format("Level = %-10d. Count = %-10d. %-10.2f%%\n",
181 currentDelay, pathsWithSameDelay,
182 cumulativePercentage);
183 if (jsonOS) {
184 jsonOS->objectBegin();
185 jsonOS->attribute("level", currentDelay);
186 jsonOS->attribute("count", pathsWithSameDelay);
187 jsonOS->attribute("percentage", cumulativePercentage);
188 jsonOS->objectEnd();
189 }
190 }
191}
192
193/// Print detailed information for the top K critical paths.
194/// This shows the most critical timing paths in the design, providing detailed
195/// information about each path including fanout/fanin points and path history.
196void PrintLongestPathAnalysisPass::printTopKPathDetails(
197 SmallVectorImpl<DataflowPath> &allTimingPaths, hw::HWModuleOp top,
198 llvm::raw_ostream *os, llvm::json::OStream *jsonOS) {
199
200 auto topKCount = static_cast<uint64_t>(allTimingPaths.size()) *
201 std::clamp(showTopKPercent.getValue(), 0, 100) / 100;
202
203 if (os)
204 *os << "## Top " << topKCount << " (out of " << allTimingPaths.size()
205 << ") fan-out points\n\n";
206
207 if (jsonOS) {
208 jsonOS->attributeBegin("top_paths");
209 jsonOS->arrayBegin();
210 }
211 auto closeJson = llvm::make_scope_exit([&]() {
212 if (jsonOS) {
213 jsonOS->arrayEnd();
214 jsonOS->attributeEnd();
215 }
216 });
217
218 // Process paths from highest delay to lowest (reverse order since paths are
219 // sorted ascending)
220 for (size_t i = 0; i < std::min<size_t>(topKCount, allTimingPaths.size());
221 ++i) {
222 auto &path = allTimingPaths[i];
223 if (jsonOS)
224 jsonOS->value(toJSON(path));
225
226 if (!os)
227 continue;
228 // Print path header with ranking and delay information
229 *os << "==============================================\n"
230 << "#" << i + 1 << ": Distance=" << path.getDelay() << "\n";
231
232 // Print fanout point (where the critical path starts)
233 *os << "FanOut=";
234 path.printFanOut(*os);
235
236 // Print fanin point (where the critical path ends)
237 *os << "\n"
238 << "FanIn=";
239 path.getFanIn().print(*os);
240 *os << "\n";
241
242 // Print detailed path history showing intermediate logic stages
243 printPathHistory(path.getPath(), *os);
244 }
245}
246
247/// Print detailed history of a timing path showing intermediate debug points.
248/// This traces the path from fanout to fanin, showing each logic stage and
249/// the delay contribution of each stage. This is crucial for understanding
250/// where delay is being accumulated along the critical path and identifying
251/// optimization opportunities.
252void PrintLongestPathAnalysisPass::printPathHistory(const OpenPath &timingPath,
253 llvm::raw_ostream &os) {
254 int64_t remainingDelay = timingPath.getDelay();
255
256 if (!timingPath.getHistory().isEmpty()) {
257 os << "== History Start (closer to fanout) ==\n";
258
259 // Walk through debug points in order from fanout to fanin
260 for (auto &debugPoint : timingPath.getHistory()) {
261 // Calculate delay contribution of this logic stage
262 int64_t stepDelay = remainingDelay - debugPoint.delay;
263 remainingDelay = debugPoint.delay;
264
265 // Show the delay contribution and the debug point information
266 os << "<--- (logic delay " << stepDelay << ") ---\n";
267 debugPoint.print(os);
268 os << "\n";
269 }
270
271 os << "== History End (closer to fanin) ==\n";
272 }
273}
274
275void PrintLongestPathAnalysisPass::runOnOperation() {
276 auto am = getAnalysisManager();
277 LongestPathAnalysis analysis(
278 getOperation(), am,
279 LongestPathAnalysisOptions(
280 /*collectDebugInfo=*/showTopKPercent.getValue() > 0,
281 /*lazyComputation=*/false,
282 /*keepOnlyMaxDelayPaths=*/
283 !test));
284
286 getAnalysis<circt::igraph::InstanceGraph>());
287 auto outputFileVal = outputFile.getValue();
288
289 std::string error;
290 auto file = mlir::openOutputFile(outputFile.getValue(), &error);
291 if (!file) {
292 llvm::errs() << error;
293 return signalPassFailure();
294 }
295
296 auto &os = file->os();
297 std::unique_ptr<llvm::json::OStream> jsonOS;
298 if (emitJSON.getValue()) {
299 jsonOS = std::make_unique<llvm::json::OStream>(os);
300 jsonOS->arrayBegin();
301 }
302
303 auto closeJson = llvm::make_scope_exit([&]() {
304 if (jsonOS)
305 jsonOS->arrayEnd();
306 });
307
308 for (auto top : analysis.getTopModules())
309 if (failed(printAnalysisResult(analysis, pathCache, top,
310 jsonOS ? nullptr : &os, jsonOS.get())))
311 return signalPassFailure();
312 file->keep();
313 return markAllAnalysesPreserved();
314}
static llvm::json::Value toJSON(const circt::igraph::InstancePath &path)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
void error(Twine message)
Definition LSPUtils.cpp:16
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition synth.py:1
A data structure that caches and provides paths to module instances in the IR.