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 : OpenPath(Object(path, value, bitPos), delay, history) {}
100 llvm::ImmutableList<DebugPoint> history = {})
102 OpenPath() = default;
103
104 const Object &getStartPoint() const { return startPoint; }
105 int64_t getDelay() const { return delay; }
106 const llvm::ImmutableList<DebugPoint> &getHistory() const { return history; }
107
108 void print(llvm::raw_ostream &os) const;
109 OpenPath &
111 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
113
115 int64_t delay;
116 // History of debug points represented by linked lists.
117 // The head of the list is the farthest point from the start point.
118 llvm::ImmutableList<DebugPoint> history;
119};
120
121// A DataflowPath represents a complete timing path from a end point to a
122// start point with associated delay information. This is the primary result
123// type for longest path analysis, containing both end points and path history.
125public:
126 // end point can be either an internal circuit object or a module output port
127 // This flexibility allows representing both closed paths
128 // (register-to-register) and open paths (register-to-output) in a unified way
129 using OutputPort = std::tuple<hw::HWModuleOp, size_t, size_t>;
130 using EndPointType = std::variant<Object, OutputPort>;
131
132 // Constructor for paths with Object end point (internal circuit nodes)
135
136 // Constructor for paths with port end point (module output ports)
139
140 DataflowPath() = default;
141
142 int64_t getDelay() const { return path.delay; }
143 const Object &getStartPoint() const { return path.startPoint; }
144 const EndPointType &getEndPoint() const { return endPoint; }
146 return std::get<Object>(endPoint);
147 }
149 return std::get<OutputPort>(endPoint);
150 }
151 hw::HWModuleOp getRoot() const { return root; }
152 const llvm::ImmutableList<DebugPoint> &getHistory() const {
153 return path.history;
154 }
155 const OpenPath &getPath() const { return path; }
156
157 // Get source location for the end point (for diagnostics)
158 Location getEndPointLoc();
159
160 void setDelay(int64_t delay) { path.delay = delay; }
161
162 void print(llvm::raw_ostream &os);
163 void printEndPoint(llvm::raw_ostream &os);
164
165 // Path elaboration for hierarchical analysis
166 // Prepends instance path information to create full hierarchical paths
169 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
171
172private:
173 EndPointType endPoint; // Either Object or (port_index, bit_index)
174 OpenPath path; // The actual timing path with history
175 hw::HWModuleOp root; // Root module for this path
176};
177
178// JSON serialization for DataflowPath
179llvm::json::Value toJSON(const circt::synth::DataflowPath &path);
180
181/// Configuration options for the longest path analysis.
182///
183/// This struct controls various aspects of the analysis behavior, including
184/// debugging features, computation modes, and optimization settings. Different
185/// combinations of options are suitable for different use cases.
186///
187/// Example usage:
188/// // For lazily computing paths with debug info
189/// LongestPathAnalysisOption options(true, true, false);
190///
191/// // For fast critical path identification only
192/// LongestPathAnalysisOption options(false, false, true);
194 /// Enable collection of debug points along timing paths.
195 /// When enabled, records intermediate points with delay values and comments
196 /// for debugging, visualization, and understanding delay contributions.
197 /// Moderate performance impact.
198 bool collectDebugInfo = false;
199
200 /// Enable lazy computation mode for on-demand analysis.
201 /// Performs delay computations lazily and caches results, tracking IR
202 /// changes. Better for iterative workflows where only specific paths
203 /// are queried. Disables parallel processing.
204 bool lazyComputation = false;
205
206 /// Keep only the maximum delay path per end point.
207 /// Focuses on finding maximum delays, discarding non-critical paths.
208 /// Significantly faster and uses less memory when only delay bounds
209 /// are needed rather than complete path enumeration.
211
212 /// Name of the top module for the analysis.
213 /// If empty, the top module is inferred from the instance graph.
214 StringAttr topModuleName = {};
215
216 /// Construct analysis options with the specified settings.
224};
225
226// This analysis finds the longest paths in the dataflow graph across modules.
227// Also be aware of the lifetime of the analysis, the results would be
228// invalid if the IR is modified. Currently there is no way to efficiently
229// update the analysis results, so it's recommended to only use this analysis
230// once on a design, and store the results in a separate data structure which
231// users can manage the lifetime.
233public:
234 // Entry points for analysis.
235 LongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am,
236 const LongestPathAnalysisOptions &option = {});
238
239 // Return all longest paths to each start point for the given value and bit
240 // position.
241 LogicalResult computeGlobalPaths(Value value, size_t bitPos,
242 SmallVectorImpl<DataflowPath> &results);
243
244 // Compute local paths for specified value and bit. Local paths are paths
245 // that are fully contained within a module.
246 FailureOr<ArrayRef<OpenPath>> computeLocalPaths(Value value, size_t bitPos);
247
248 // Return the maximum delay to the given value and bit position. If bitPos is
249 // negative, then return the maximum delay across all bits.
250 FailureOr<int64_t> getMaxDelay(Value value, int64_t bitPos = -1);
251
252 // Return the average of the maximum delays across all bits of the given
253 // value, which is useful approximation for the delay of the value. For each
254 // bit position, finds all paths and takes the maximum delay. Then averages
255 // these maximum delays across all bits of the value.
256 FailureOr<int64_t> getAverageMaxDelay(Value value);
257
258 // Return paths that are closed under the given module. Closed paths are
259 // typically register-to-register paths. A closed path is a path that starts
260 // and ends at sequential elements (registers/flip-flops), forming a complete
261 // timing path through combinational logic. The path may cross module
262 // boundaries but both end points are sequential elements, not ports.
263 LogicalResult getInternalPaths(StringAttr moduleName,
264 SmallVectorImpl<DataflowPath> &results,
265 bool elaboratePaths = false) const;
266
267 // Return input-to-internal timing paths for the given module.
268 // These are open paths from module input ports to internal sequential
269 // elements (registers/flip-flops).
271 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
272
273 // Return internal-to-output timing paths for the given module.
274 // These are open paths from internal sequential elements to module output
275 // ports.
277 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
278
279 // Get all timing paths in the given module including both closed and open
280 // paths. This is a convenience method that combines results from
281 // getInternalPaths, getOpenPathsFromInputPortsToInternal, and
282 // getOpenPathsFromInternalToOutputPorts. If elaboratedPaths is true, paths
283 // include full hierarchical instance information.
284 LogicalResult getAllPaths(StringAttr moduleName,
285 SmallVectorImpl<DataflowPath> &results,
286 bool elaboratePaths = false) const;
287
288 // Return true if the analysis is available for the given module.
289 bool isAnalysisAvailable(StringAttr moduleName) const;
290
291 // Return the top nodes that were used for the analysis.
292 llvm::ArrayRef<hw::HWModuleOp> getTopModules() const;
293
294 MLIRContext *getContext() const { return ctx; }
295
296protected:
298 struct Impl;
300
301private:
302 mlir::MLIRContext *ctx;
303 bool isAnalysisValid = true;
304};
305
306// Incremental version of longest path analysis that supports on-demand
307// computation and tracks IR modifications to maintain validity.
310public:
311 IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am)
313 moduleOp, am,
314 LongestPathAnalysisOptions(/*collectDebugInfo=*/false,
315 /*lazyComputation=*/true,
316 /*keepOnlyMaxDelayPaths=*/true)) {}
317
318 IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am,
319 const LongestPathAnalysisOptions &option)
320 : LongestPathAnalysis(moduleOp, am, option) {
321 assert(option.lazyComputation && "Lazy computation must be enabled");
322 }
323
326
327 // Check if operation can be safely modified without invalidating analysis.
328 bool isOperationValidToMutate(Operation *op) const;
329
330 // PatternRewriter::Listener interface - called automatically.
331 void notifyOperationModified(Operation *op) override;
332 void notifyOperationReplaced(Operation *op, ValueRange replacement) override;
333 void notifyOperationErased(Operation *op) override;
334};
335
336// A collection of longest paths. The data structure owns the paths, and used
337// for computing statistics and CAPI.
339public:
340 LongestPathCollection(MLIRContext *ctx) : ctx(ctx){};
341 const DataflowPath &getPath(unsigned index) const { return paths[index]; }
342 MLIRContext *getContext() const { return ctx; }
343 llvm::SmallVector<DataflowPath, 64> paths;
344
345 // Sort the paths by delay in descending order.
347
348 // Drop all paths except the longest path per end point or start point.
349 void dropNonCriticalPaths(bool perEndPoint = true);
350
351 // Merge another collection into this one.
352 void merge(const LongestPathCollection &other);
353
354private:
355 MLIRContext *ctx;
356};
357
358// Register prerequisite passes for Synthth longest path analysis.
359// This includes the Comb to Synth conversion pass and necessary
360// canonicalization passes to clean up the IR.
362 hw::registerHWAggregateToCombPass();
363 ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
364 return createConvertCombToSynth();
365 });
366 mlir::registerCSEPass();
367 mlir::registerCanonicalizerPass();
368}
369
370} // namespace synth
371} // namespace circt
372
373namespace llvm {
374// Provide DenseMapInfo for Object.
375template <>
378 std::tuple<circt::igraph::InstancePath, mlir::Value, size_t>>;
381 auto [path, value, bitPos] = Info::getEmptyKey();
382 return Object(path, value, bitPos);
383 }
384
386 auto [path, value, bitPos] = Info::getTombstoneKey();
387 return Object(path, value, bitPos);
388 }
389 static llvm::hash_code getHashValue(Object object) {
390 return Info::getHashValue(
391 {object.instancePath, object.value, object.bitPos});
392 }
393 static bool isEqual(const Object &a, const Object &b) {
394 return Info::isEqual({a.instancePath, a.value, a.bitPos},
395 {b.instancePath, b.value, b.bitPos});
396 }
397};
398
399} // namespace llvm
400
401#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)
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)
const DataflowPath & getPath(unsigned index) const
llvm::SmallVector< DataflowPath, 64 > paths
void dropNonCriticalPaths(bool perEndPoint=true)
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={})
OpenPath(Object startPoint, 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)