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