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 AIG dialect.
10// The analysis computes the maximum delay through combinational paths in a
11// circuit where each AIG and-inverter 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_ANALYSIS_AIG_ANALYSIS_H
18#define CIRCT_ANALYSIS_AIG_ANALYSIS_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/JSON.h"
33#include <variant>
34
35namespace mlir {
36class AnalysisManager;
37} // namespace mlir
38namespace circt {
39namespace igraph {
40class InstanceGraph;
41} // namespace igraph
42namespace aig {
43
44// A class represents an object in the dataflow graph.
45// An Object identifies a specific bit of a value at a specific instance path.
46struct Object {
49 Object() = default;
50
51 bool operator==(const Object &other) const {
52 return instancePath == other.instancePath && value == other.value &&
53 bitPos == other.bitPos;
54 }
55
56 void print(llvm::raw_ostream &os) const;
59
60 StringAttr getName() const;
61
63 Value value;
64 size_t bitPos;
65};
66
67// A debug point represents a point in the dataflow graph which carries delay
68// and optional comment. Debug points are used to track the history of a path
69// and provide context for debugging.
70struct DebugPoint {
71 DebugPoint(circt::igraph::InstancePath path, Value value, size_t bitPos,
72 int64_t delay = 0, StringRef comment = "")
73 : object(path, value, bitPos), delay(delay), comment(comment) {}
74
75 // Trait for ImmutableList.
76 void Profile(llvm::FoldingSetNodeID &ID) const {
77 for (auto &inst : object.instancePath) {
78 ID.AddPointer(inst.getAsOpaquePointer());
79 }
80 ID.AddPointer(object.value.getAsOpaquePointer());
81 ID.AddInteger(object.bitPos);
82 ID.AddInteger(delay);
83 }
84
85 void print(llvm::raw_ostream &os) const;
86
88 int64_t delay;
89 StringRef comment;
90};
91
92// An OpenPath represents a path from a fan-in with an associated
93// delay and history of debug points.
94struct OpenPath {
95 OpenPath(circt::igraph::InstancePath path, Value value, size_t bitPos,
96 int64_t delay = 0, llvm::ImmutableList<DebugPoint> history = {})
97 : fanIn(path, value, bitPos), delay(delay), history(history) {}
98 OpenPath() = default;
99
100 const Object &getFanIn() const { return fanIn; }
101 int64_t getDelay() const { return delay; }
102 const llvm::ImmutableList<DebugPoint> &getHistory() const { return history; }
103
104 void print(llvm::raw_ostream &os) const;
105 OpenPath &
107 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
109
111 int64_t delay;
112 // History of debug points represented by linked lists.
113 // The head of the list is the farthest point from the fanIn.
114 llvm::ImmutableList<DebugPoint> history;
115};
116
117// A DataflowPath represents a complete timing path from a fanout to a fanin
118// with associated delay information. This is the primary result type for
119// longest path analysis, containing both endpoints and path history.
121public:
122 // FanOut can be either an internal circuit object or a module output port
123 // This flexibility allows representing both closed paths
124 // (register-to-register) and open paths (register-to-output) in a unified way
125 using OutputPort = std::tuple<hw::HWModuleOp, size_t, size_t>;
126 using FanOutType = std::variant<Object, OutputPort>;
127
128 // Constructor for paths with Object fanout (internal circuit nodes)
131
132 // Constructor for paths with port fanout (module output ports)
135
136 DataflowPath() = default;
137
138 int64_t getDelay() const { return path.delay; }
139 const Object &getFanIn() const { return path.fanIn; }
140 const FanOutType &getFanOut() const { return fanOut; }
141 const Object &getFanOutAsObject() const { return std::get<Object>(fanOut); }
143 return std::get<OutputPort>(fanOut);
144 }
145 hw::HWModuleOp getRoot() const { return root; }
146 const llvm::ImmutableList<DebugPoint> &getHistory() const {
147 return path.history;
148 }
149 const OpenPath &getPath() const { return path; }
150
151 // Get source location for the fanout point (for diagnostics)
152 Location getFanOutLoc();
153
154 void setDelay(int64_t delay) { path.delay = delay; }
155
156 void print(llvm::raw_ostream &os);
157 void printFanOut(llvm::raw_ostream &os);
158
159 // Path elaboration for hierarchical analysis
160 // Prepends instance path information to create full hierarchical paths
163 llvm::ImmutableListFactory<DebugPoint> *debugPointFactory,
165
166private:
167 FanOutType fanOut; // Either Object or (port_index, bit_index)
168 OpenPath path; // The actual timing path with history
169 hw::HWModuleOp root; // Root module for this path
170};
171
172// JSON serialization for DataflowPath
173llvm::json::Value toJSON(const circt::aig::DataflowPath &path);
174
175// Options for the longest path analysis.
184
185// This analysis finds the longest paths in the dataflow graph across modules.
186// Also be aware of the lifetime of the analysis, the results would be
187// invalid if the IR is modified. Currently there is no way to efficiently
188// update the analysis results, so it's recommended to only use this analysis
189// once on a design, and store the results in a separate data structure which
190// users can manage the lifetime.
192public:
193 // Entry points for analysis.
194 LongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am,
195 const LongestPathAnalysisOption &option = {});
197
198 // Return all longest paths to each Fanin for the given value and bit
199 // position.
200 LogicalResult getResults(Value value, size_t bitPos,
201 SmallVectorImpl<DataflowPath> &results) const;
202
203 // Return the maximum delay to the given value for all bit positions.
204 int64_t getMaxDelay(Value value) const;
205
206 // Return the average of the maximum delays across all bits of the given
207 // value, which is useful approximation for the delay of the value. For each
208 // bit position, finds all paths and takes the maximum delay. Then averages
209 // these maximum delays across all bits of the value.
210 int64_t getAverageMaxDelay(Value value) const;
211
212 // Return paths that are closed under the given module. Closed paths are
213 // typically register-to-register paths. A closed path is a path that starts
214 // and ends at sequential elements (registers/flip-flops), forming a complete
215 // timing path through combinational logic. The path may cross module
216 // boundaries but both endpoints are sequential elements, not ports.
217 LogicalResult getClosedPaths(StringAttr moduleName,
218 SmallVectorImpl<DataflowPath> &results,
219 bool elaboratePaths = false) const;
220
221 // Return input-to-internal timing paths for the given module.
222 // These are open paths from module input ports to internal sequential
223 // elements (registers/flip-flops).
225 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
226
227 // Return internal-to-output timing paths for the given module.
228 // These are open paths from internal sequential elements to module output
229 // ports.
231 StringAttr moduleName, SmallVectorImpl<DataflowPath> &results) const;
232
233 // Get all timing paths in the given module including both closed and open
234 // paths. This is a convenience method that combines results from
235 // getClosedPaths, getOpenPathsFromInputPortsToInternal, and
236 // getOpenPathsFromInternalToOutputPorts. If elaboratedPaths is true, paths
237 // include full hierarchical instance information.
238 LogicalResult getAllPaths(StringAttr moduleName,
239 SmallVectorImpl<DataflowPath> &results,
240 bool elaboratePaths = false) const;
241
242 // Return true if the analysis is available for the given module.
243 bool isAnalysisAvailable(StringAttr moduleName) const;
244
245 // Return the top nodes that were used for the analysis.
246 llvm::ArrayRef<hw::HWModuleOp> getTopModules() const;
247
248 // This is the name of the attribute that can be attached to the module
249 // to specify the top module for the analysis. This is optional, if not
250 // specified, the analysis will infer the top module from the instance graph.
251 // However it's recommended to specify it, as the entire module tends to
252 // contain testbench or verification modules, which may have expensive paths
253 // that are not of interest.
254 static StringRef getTopModuleNameAttrName() {
255 return "aig.longest-path-analysis-top";
256 }
257
258 MLIRContext *getContext() const { return ctx; }
259
260protected:
262 struct Impl;
264
265private:
266 mlir::MLIRContext *ctx;
267};
268
269// Incremental version of longest path analysis that supports on-demand
270// computation and tracks IR modifications to maintain validity.
273public:
274 IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am)
275 : LongestPathAnalysis(moduleOp, am,
276 LongestPathAnalysisOption(false, true)) {}
277
278 // Compute maximum delay for specified value and bit.
279 FailureOr<int64_t> getOrComputeMaxDelay(Value value, size_t bitPos);
280
281 // Compute all paths for specified value and bit.
282 FailureOr<ArrayRef<OpenPath>> getOrComputePaths(Value value, size_t bitPos);
283
284 // Check if operation can be safely modified without invalidating analysis.
285 bool isOperationValidToMutate(Operation *op) const;
286
287 // PatternRewriter::Listener interface - called automatically.
288 void notifyOperationModified(Operation *op) override;
289 void notifyOperationReplaced(Operation *op, ValueRange replacement) override;
290 void notifyOperationErased(Operation *op) override;
291
292private:
293 bool isAnalysisValid = true;
294};
295
296// A wrapper class for the longest path analysis that also traces debug points.
297// This is necessary for analysis manager to cache the analysis results.
299public:
300 LongestPathAnalysisWithTrace(Operation *moduleOp, mlir::AnalysisManager &am)
301 : LongestPathAnalysis(moduleOp, am, {true, false}) {}
302};
303
304// A collection of longest paths. The data structure owns the paths, and used
305// for computing statistics and CAPI.
307public:
308 LongestPathCollection(MLIRContext *ctx) : ctx(ctx){};
309 const DataflowPath &getPath(unsigned index) const { return paths[index]; }
310 MLIRContext *getContext() const { return ctx; }
311 llvm::SmallVector<DataflowPath, 64> paths;
312
313 // Sort the paths by delay in descending order.
315
316 // Sort and drop all paths except the longest path per fanout point.
318
319private:
320 MLIRContext *ctx;
321};
322
323// Register prerequisite passes for AIG longest path analysis.
324// This includes the Comb to AIG conversion pass and necessary
325// canonicalization passes to clean up the IR.
327 hw::registerHWAggregateToCombPass();
328 ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
329 return createConvertCombToAIG();
330 });
331 mlir::registerCSEPass();
332 mlir::registerCanonicalizerPass();
333}
334
335} // namespace aig
336} // namespace circt
337
338namespace llvm {
339// Provide DenseMapInfo for Object.
340template <>
343 std::tuple<circt::igraph::InstancePath, mlir::Value, size_t>>;
346 auto [path, value, bitPos] = Info::getEmptyKey();
347 return Object(path, value, bitPos);
348 }
349
351 auto [path, value, bitPos] = Info::getTombstoneKey();
352 return Object(path, value, bitPos);
353 }
354 static llvm::hash_code getHashValue(Object object) {
355 return Info::getHashValue(
356 {object.instancePath, object.value, object.bitPos});
357 }
358 static bool isEqual(const Object &a, const Object &b) {
359 return Info::isEqual({a.instancePath, a.value, a.bitPos},
360 {b.instancePath, b.value, b.bitPos});
361 }
362};
363
364} // namespace llvm
365
366#endif // CIRCT_ANALYSIS_AIG_ANALYSIS_H
const OpenPath & getPath() const
std::tuple< hw::HWModuleOp, size_t, size_t > OutputPort
const OutputPort & getFanOutAsPort() const
const llvm::ImmutableList< DebugPoint > & getHistory() const
const Object & getFanOutAsObject() const
std::variant< Object, OutputPort > FanOutType
const FanOutType & getFanOut() const
DataflowPath & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)
DataflowPath(OutputPort fanOut, OpenPath fanIn, hw::HWModuleOp root)
const Object & getFanIn() const
DataflowPath(Object fanOut, OpenPath fanIn, hw::HWModuleOp root)
void printFanOut(llvm::raw_ostream &os)
void print(llvm::raw_ostream &os)
hw::HWModuleOp getRoot() const
void notifyOperationReplaced(Operation *op, ValueRange replacement) override
FailureOr< ArrayRef< OpenPath > > getOrComputePaths(Value value, size_t bitPos)
IncrementalLongestPathAnalysis(Operation *moduleOp, mlir::AnalysisManager &am)
FailureOr< int64_t > getOrComputeMaxDelay(Value value, size_t bitPos)
LongestPathAnalysisWithTrace(Operation *moduleOp, mlir::AnalysisManager &am)
LogicalResult getResults(Value value, size_t bitPos, SmallVectorImpl< DataflowPath > &results) const
LogicalResult getClosedPaths(StringAttr moduleName, SmallVectorImpl< DataflowPath > &results, bool elaboratePaths=false) const
int64_t getMaxDelay(Value value) const
int64_t getAverageMaxDelay(Value value) const
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
const DataflowPath & getPath(unsigned index) const
llvm::SmallVector< DataflowPath, 64 > paths
This graph tracks modules and where they are instantiated.
An instance path composed of a series of instances.
Definition aig.py:1
void registerAIGAnalysisPrerequisitePasses()
llvm::json::Value toJSON(const circt::aig::DataflowPath &path)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void Profile(llvm::FoldingSetNodeID &ID) const
DebugPoint(circt::igraph::InstancePath path, Value value, size_t bitPos, int64_t delay=0, StringRef comment="")
void print(llvm::raw_ostream &os) const
LongestPathAnalysisOption(bool traceDebugPoints, bool incremental)
circt::igraph::InstancePath instancePath
Object & prependPaths(circt::igraph::InstancePathCache &cache, circt::igraph::InstancePath path)
StringAttr getName() const
bool operator==(const Object &other) const
void print(llvm::raw_ostream &os) const
Object(circt::igraph::InstancePath path, Value value, size_t bitPos)
const Object & getFanIn() 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 & prependPaths(circt::igraph::InstancePathCache &cache, llvm::ImmutableListFactory< DebugPoint > *debugPointFactory, circt::igraph::InstancePath path)
llvm::ImmutableList< DebugPoint > history
const llvm::ImmutableList< DebugPoint > & getHistory() const
A data structure that caches and provides paths to module instances in the IR.
static llvm::hash_code getHashValue(Object object)
static bool isEqual(const Object &a, const Object &b)