CIRCT 22.0.0git
Loading...
Searching...
No Matches
LongestPathAnalysis.h
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 file defines the longest path analysis for the Synth dialect.
10// The analysis computes the maximum delay through combinational paths in a
11// circuit where each primitive operation is considered to have a unit
12// delay. It handles module hierarchies and provides detailed path information
13// including sources, sinks, delays, and debug points.
14//
15//===----------------------------------------------------------------------===//
16
17#ifndef CIRCT_DIALECT_SYNTH_ANALYSIS_LONGESTPATHANALYSIS_H
18#define CIRCT_DIALECT_SYNTH_ANALYSIS_LONGESTPATHANALYSIS_H
19
24#include "circt/Support/LLVM.h"
25#include "mlir/IR/BuiltinOps.h"
26#include "mlir/IR/MLIRContext.h"
27#include "mlir/IR/Operation.h"
28#include "mlir/Transforms/Passes.h"
29#include "llvm/ADT/ArrayRef.h"
30#include "llvm/ADT/ImmutableList.h"
31#include "llvm/ADT/SmallVector.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/JSON.h"
34#include <variant>
35
36namespace mlir {
37class AnalysisManager;
38} // namespace mlir
39namespace circt {
40namespace igraph {
41class InstanceGraph;
42} // namespace igraph
43namespace synth {
44
45// A class represents an object in the dataflow graph.
46// An Object identifies a specific bit of a value at a specific instance path.
47struct Object {
50 Object() = default;
51
52 bool operator==(const Object &other) const {
53 return instancePath == other.instancePath && value == other.value &&
54 bitPos == other.bitPos;
55 }
56
57 void print(llvm::raw_ostream &os) const;
60
61 StringAttr getName() const;
62
64 Value value;
65 size_t bitPos;
66};
67
68// A debug point represents a point in the dataflow graph which carries delay
69// and optional comment. Debug points are used to track the history of a path
70// and provide context for debugging.
71struct DebugPoint {
72 DebugPoint(circt::igraph::InstancePath path, Value value, size_t bitPos,
73 int64_t delay = 0, StringRef comment = "")
74 : object(path, value, bitPos), delay(delay), comment(comment) {}
75
76 // Trait for ImmutableList.
77 void Profile(llvm::FoldingSetNodeID &ID) const {
78 for (auto &inst : object.instancePath) {
79 ID.AddPointer(inst.getAsOpaquePointer());
80 }
81 ID.AddPointer(object.value.getAsOpaquePointer());
82 ID.AddInteger(object.bitPos);
83 ID.AddInteger(delay);
84 }
85
86 void print(llvm::raw_ostream &os) const;
87
89 int64_t delay;
90 StringRef comment;
91};
92
93// An OpenPath represents a path from a start point with an associated
94// delay and history of debug points.
95struct OpenPath {
96 OpenPath(circt::igraph::InstancePath path, Value value, size_t bitPos,
97 int64_t delay = 0, llvm::ImmutableList<DebugPoint> history = {})
98 : startPoint(path, value, bitPos), delay(delay), history(history) {}
99 OpenPath() = default;
100
101 const Object &getStartPoint() const { return startPoint; }
102 int64_t getDelay() const { return delay; }
103 const llvm::ImmutableList<DebugPoint> &getHistory() const { return history; }
104
105 void print(llvm::raw_ostream &os) const;
106 OpenPath &
108 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
110
112 int64_t delay;
113 // History of debug points represented by linked lists.
114 // The head of the list is the farthest point from the start point.
115 llvm::ImmutableList<DebugPoint> history;
116};
117
118// A DataflowPath represents a complete timing path from a end point to a
119// start point with associated delay information. This is the primary result
120// type for longest path analysis, containing both end points and path history.
122public:
123 // end point can be either an internal circuit object or a module output port
124 // This flexibility allows representing both closed paths
125 // (register-to-register) and open paths (register-to-output) in a unified way
126 using OutputPort = std::tuple<hw::HWModuleOp, size_t, size_t>;
127 using EndPointType = std::variant<Object, OutputPort>;
128
129 // Constructor for paths with Object end point (internal circuit nodes)
132
133 // Constructor for paths with port end point (module output ports)
136
137 DataflowPath() = default;
138
139 int64_t getDelay() const { return path.delay; }
140 const Object &getStartPoint() const { return path.startPoint; }
141 const EndPointType &getEndPoint() const { return endPoint; }
143 return std::get<Object>(endPoint);
144 }
146 return std::get<OutputPort>(endPoint);
147 }
148 hw::HWModuleOp getRoot() const { return root; }
149 const llvm::ImmutableList<DebugPoint> &getHistory() const {
150 return path.history;
151 }
152 const OpenPath &getPath() const { return path; }
153
154 // Get source location for the end point (for diagnostics)
155 Location getEndPointLoc();
156
157 void setDelay(int64_t delay) { path.delay = delay; }
158
159 void print(llvm::raw_ostream &os);
160 void printEndPoint(llvm::raw_ostream &os);
161
162 // Path elaboration for hierarchical analysis
163 // Prepends instance path information to create full hierarchical paths
166 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
168
169private:
170 EndPointType endPoint; // Either Object or (port_index, bit_index)
171 OpenPath path; // The actual timing path with history
172 hw::HWModuleOp root; // Root module for this path
173};
174
175// JSON serialization for DataflowPath
176llvm::json::Value toJSON(const circt::synth::DataflowPath &path);
177
178/// Configuration options for the longest path analysis.
179///
180/// This struct controls various aspects of the analysis behavior, including
181/// debugging features, computation modes, and optimization settings. Different
182/// combinations of options are suitable for different use cases.
183///
184/// Example usage:
185/// // For lazily computing paths with debug info
186/// LongestPathAnalysisOption options(true, true, false);
187///
188/// // For fast critical path identification only
189/// LongestPathAnalysisOption options(false, false, true);
191 /// Enable collection of debug points along timing paths.
192 /// When enabled, records intermediate points with delay values and comments
193 /// for debugging, visualization, and understanding delay contributions.
194 /// Moderate performance impact.
195 bool collectDebugInfo = false;
196
197 /// Enable lazy computation mode for on-demand analysis.
198 /// Performs delay computations lazily and caches results, tracking IR
199 /// changes. Better for iterative workflows where only specific paths
200 /// are queried. Disables parallel processing.
201 bool lazyComputation = false;
202
203 /// Keep only the maximum delay path per end point.
204 /// Focuses on finding maximum delays, discarding non-critical paths.
205 /// Significantly faster and uses less memory when only delay bounds
206 /// are needed rather than complete path enumeration.
208
209 /// Name of the top module for the analysis.
210 /// If empty, the top module is inferred from the instance graph.
211 StringAttr topModuleName = {};
212
213 /// Construct analysis options with the specified settings.
221};
222
223// This analysis finds the longest paths in the dataflow graph across modules.
224// Also be aware of the lifetime of the analysis, the results would be
225// invalid if the IR is modified. Currently there is no way to efficiently
226// update the analysis results, so it's recommended to only use this analysis
227// once on a design, and store the results in a separate data structure which
228// users can manage the lifetime.
230public:
231 // Entry points for analysis.
232 LongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am,
233 const LongestPathAnalysisOptions &option = {});
235
236 // Return all longest paths to each start point for the given value and bit
237 // position.
238 LogicalResult computeGlobalPaths(Value value, size_t bitPos,
239 SmallVectorImpl<DataflowPath> &results);
240
241 // Compute local paths for specified value and bit. Local paths are paths
242 // that are fully contained within a module.
243 FailureOr<ArrayRef<OpenPath>> computeLocalPaths(Value value, size_t bitPos);
244
245 // Return the maximum delay to the given value and bit position. If bitPos is
246 // negative, then return the maximum delay across all bits.
247 FailureOr<int64_t> getMaxDelay(Value value, int64_t bitPos = -1);
248
249 // Return the average of the maximum delays across all bits of the given
250 // value, which is useful approximation for the delay of the value. For each
251 // bit position, finds all paths and takes the maximum delay. Then averages
252 // these maximum delays across all bits of the value.
253 FailureOr<int64_t> getAverageMaxDelay(Value value);
254
255 // Return paths that are closed under the given module. Closed paths are
256 // typically register-to-register paths. A closed path is a path that starts
257 // and ends at sequential elements (registers/flip-flops), forming a complete
258 // timing path through combinational logic. The path may cross module
259 // boundaries but both end points are sequential elements, not ports.
260 LogicalResult getClosedPaths(StringAttr moduleName,
261 SmallVectorImpl<DataflowPath> &results,
262 bool elaboratePaths = false) const;
263
264 // Return input-to-internal timing paths for the given module.
265 // These are open paths from module input ports to internal sequential
266 // elements (registers/flip-flops).
268 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
269
270 // Return internal-to-output timing paths for the given module.
271 // These are open paths from internal sequential elements to module output
272 // ports.
274 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
275
276 // Get all timing paths in the given module including both closed and open
277 // paths. This is a convenience method that combines results from
278 // getClosedPaths, getOpenPathsFromInputPortsToInternal, and
279 // getOpenPathsFromInternalToOutputPorts. If elaboratedPaths is true, paths
280 // include full hierarchical instance information.
281 LogicalResult getAllPaths(StringAttr moduleName,
282 SmallVectorImpl<DataflowPath> &results,
283 bool elaboratePaths = false) const;
284
285 // Return true if the analysis is available for the given module.
286 bool isAnalysisAvailable(StringAttr moduleName) const;
287
288 // Return the top nodes that were used for the analysis.
289 llvm::ArrayRef<hw::HWModuleOp> getTopModules() const;
290
291 MLIRContext *getContext() const { return ctx; }
292
293protected:
295 struct Impl;
297
298private:
299 mlir::MLIRContext *ctx;
300 bool isAnalysisValid = true;
301};
302
303// Incremental version of longest path analysis that supports on-demand
304// computation and tracks IR modifications to maintain validity.
307public:
308 IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am)
310 moduleOp, am,
311 LongestPathAnalysisOptions(/*collectDebugInfo=*/false,
312 /*lazyComputation=*/true,
313 /*keepOnlyMaxDelayPaths=*/true)) {}
314
315 IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am,
316 const LongestPathAnalysisOptions &option)
317 : LongestPathAnalysis(moduleOp, am, option) {
318 assert(option.lazyComputation && "Lazy computation must be enabled");
319 }
320
323
324 // Check if operation can be safely modified without invalidating analysis.
325 bool isOperationValidToMutate(Operation *op) const;
326
327 // PatternRewriter::Listener interface - called automatically.
328 void notifyOperationModified(Operation *op) override;
329 void notifyOperationReplaced(Operation *op, ValueRange replacement) override;
330 void notifyOperationErased(Operation *op) override;
331};
332
333// A collection of longest paths. The data structure owns the paths, and used
334// for computing statistics and CAPI.
336public:
337 LongestPathCollection(MLIRContext *ctx) : ctx(ctx){};
338 const DataflowPath &getPath(unsigned index) const { return paths[index]; }
339 MLIRContext *getContext() const { return ctx; }
340 llvm::SmallVector<DataflowPath, 64> paths;
341
342 // Sort the paths by delay in descending order.
344
345 // Sort and drop all paths except the longest path per end point.
347
348 // Merge another collection into this one.
349 void merge(const LongestPathCollection &other);
350
351private:
352 MLIRContext *ctx;
353};
354
355// Register prerequisite passes for Synthth longest path analysis.
356// This includes the Comb to Synth conversion pass and necessary
357// canonicalization passes to clean up the IR.
359 hw::registerHWAggregateToCombPass();
360 ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
361 return createConvertCombToSynth();
362 });
363 mlir::registerCSEPass();
364 mlir::registerCanonicalizerPass();
365}
366
367} // namespace synth
368} // namespace circt
369
370namespace llvm {
371// Provide DenseMapInfo for Object.
372template <>
375 std::tuple<circt::igraph::InstancePath, mlir::Value, size_t>>;
378 auto [path, value, bitPos] = Info::getEmptyKey();
379 return Object(path, value, bitPos);
380 }
381
383 auto [path, value, bitPos] = Info::getTombstoneKey();
384 return Object(path, value, bitPos);
385 }
386 static llvm::hash_code getHashValue(Object object) {
387 return Info::getHashValue(
388 {object.instancePath, object.value, object.bitPos});
389 }
390 static bool isEqual(const Object &a, const Object &b) {
391 return Info::isEqual({a.instancePath, a.value, a.bitPos},
392 {b.instancePath, b.value, b.bitPos});
393 }
394};
395
396} // namespace llvm
397
398#endif // CIRCT_DIALECT_SYNTH_ANALYSIS_LONGESTPATHANALYSIS_H
assert(baseType &&"element must be base type")
This graph tracks modules and where they are instantiated.
An instance path composed of a series of instances.
const Object & getStartPoint() const
const OutputPort & getEndPointAsPort() const
std::variant< Object, OutputPort > EndPointType
const Object & getEndPointAsObject() const
DataflowPath & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)
const OpenPath & getPath() const
const llvm::ImmutableList< DebugPoint > & getHistory() const
hw::HWModuleOp getRoot() const
std::tuple< hw::HWModuleOp, size_t, size_t > OutputPort
DataflowPath(OutputPort endPoint, OpenPath startPoint, hw::HWModuleOp root)
DataflowPath(Object endPoint, OpenPath startPoint, hw::HWModuleOp root)
void print(llvm::raw_ostream &os)
void printEndPoint(llvm::raw_ostream &os)
const EndPointType & getEndPoint() const
void notifyOperationReplaced(Operation *op, ValueRange replacement) override
IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am, const LongestPathAnalysisOptions &option)
IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am)
FailureOr< int64_t > getAverageMaxDelay(Value value)
LogicalResult computeGlobalPaths(Value value, size_t bitPos, SmallVectorImpl< DataflowPath > &results)
LogicalResult getClosedPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results, bool elaboratePaths=false) const
FailureOr< int64_t > getMaxDelay(Value value, int64_t bitPos=-1)
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)
const DataflowPath & getPath(unsigned index) const
llvm::SmallVector< DataflowPath, 64 > paths
llvm::json::Value toJSON(const circt::synth::DataflowPath &path)
void registerSynthAnalysisPrerequisitePasses()
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.
DebugPoint(circt::igraph::InstancePath path, Value value, size_t bitPos, int64_t delay=0, StringRef comment="")
void Profile(llvm::FoldingSetNodeID &ID) const
void print(llvm::raw_ostream &os) const
Configuration options for the longest path analysis.
LongestPathAnalysisOptions(bool collectDebugInfo=false, bool lazyComputation=false, bool keepOnlyMaxDelayPaths=false, StringAttr topModuleName={})
Construct analysis options with the specified settings.
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.
StringAttr topModuleName
Name of the top module for the analysis.
Object(circt::igraph::InstancePath path, Value value, size_t bitPos)
Object & prependPaths(circt::igraph::InstancePathCache &cache, circt::igraph::InstancePath path)
circt::igraph::InstancePath instancePath
bool operator==(const Object &other) const
void print(llvm::raw_ostream &os) const
OpenPath(circt::igraph::InstancePath path, Value value, size_t bitPos, int64_t delay=0, llvm::ImmutableList< DebugPoint > history={})
const Object & getStartPoint() const
void print(llvm::raw_ostream &os) const
const llvm::ImmutableList< DebugPoint > & getHistory() const
OpenPath & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)
llvm::ImmutableList< DebugPoint > history
static bool isEqual(const Object &a, const Object &b)
static llvm::hash_code getHashValue(Object object)