CIRCT 24.0.0git
Loading...
Searching...
No Matches
TestPasses.cpp
Go to the documentation of this file.
1//===- TestPasses.cpp - Test passes for the analysis infrastructure -------===//
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 implements test passes for the analysis infrastructure.
10//
11//===----------------------------------------------------------------------===//
12
22#include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h"
23#include "mlir/Analysis/DataFlow/IntegerRangeAnalysis.h"
24#include "mlir/Dialect/Affine/IR/AffineMemoryOpInterfaces.h"
25#include "mlir/Dialect/Affine/IR/AffineOps.h"
26#include "mlir/Dialect/Func/IR/FuncOps.h"
27#include "mlir/IR/BuiltinOps.h"
28#include "mlir/IR/Value.h"
29#include "mlir/Pass/Pass.h"
30#include "llvm/ADT/DepthFirstIterator.h"
31#include "llvm/Support/Debug.h"
32
33using namespace mlir;
34using namespace mlir::affine;
35using namespace mlir::dataflow;
36using namespace circt;
37using namespace circt::analysis;
38using namespace circt::scheduling;
39
40//===----------------------------------------------------------------------===//
41// DebugAnalysis
42//===----------------------------------------------------------------------===//
43
44namespace {
45struct TestDebugAnalysisPass
46 : public PassWrapper<TestDebugAnalysisPass, OperationPass<mlir::ModuleOp>> {
47 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestDebugAnalysisPass)
48
49 void runOnOperation() override;
50 StringRef getArgument() const override { return "test-debug-analysis"; }
51 StringRef getDescription() const override {
52 return "Perform debug analysis and emit results as attributes";
53 }
54};
55} // namespace
56
57void TestDebugAnalysisPass::runOnOperation() {
58 auto *context = &getContext();
59 auto &analysis = getAnalysis<DebugAnalysis>();
60 for (auto *op : analysis.debugOps) {
61 op->setAttr("debug.only", UnitAttr::get(context));
62 }
63}
64
65//===----------------------------------------------------------------------===//
66// DependenceAnalysis
67//===----------------------------------------------------------------------===//
68
69namespace {
70struct TestDependenceAnalysisPass
71 : public PassWrapper<TestDependenceAnalysisPass,
72 OperationPass<func::FuncOp>> {
73 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestDependenceAnalysisPass)
74
75 void runOnOperation() override;
76 StringRef getArgument() const override { return "test-dependence-analysis"; }
77 StringRef getDescription() const override {
78 return "Perform dependence analysis and emit results as attributes";
79 }
80};
81} // namespace
82
83void TestDependenceAnalysisPass::runOnOperation() {
84 MLIRContext *context = &getContext();
85
86 MemoryDependenceAnalysis analysis(getOperation());
87
88 getOperation().walk([&](Operation *op) {
89 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(op))
90 return;
91
92 SmallVector<Attribute> deps;
93
94 for (auto dep : analysis.getDependences(op)) {
95 if (dep.dependenceType != DependenceResult::HasDependence)
96 continue;
97
98 SmallVector<Attribute> comps;
99 for (auto comp : dep.dependenceComponents) {
100 SmallVector<Attribute> vector;
101 vector.push_back(
102 IntegerAttr::get(IntegerType::get(context, 64), *comp.lb));
103 vector.push_back(
104 IntegerAttr::get(IntegerType::get(context, 64), *comp.ub));
105 comps.push_back(ArrayAttr::get(context, vector));
106 }
107
108 deps.push_back(ArrayAttr::get(context, comps));
109 }
110
111 auto dependences = ArrayAttr::get(context, deps);
112 op->setAttr("dependences", dependences);
113 });
114}
115
116//===----------------------------------------------------------------------===//
117// SchedulingAnalysis
118//===----------------------------------------------------------------------===//
119
120namespace {
121struct TestSchedulingAnalysisPass
122 : public PassWrapper<TestSchedulingAnalysisPass,
123 OperationPass<func::FuncOp>> {
124 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestSchedulingAnalysisPass)
125
126 void runOnOperation() override;
127 StringRef getArgument() const override { return "test-scheduling-analysis"; }
128 StringRef getDescription() const override {
129 return "Perform scheduling analysis and emit results as attributes";
130 }
131};
132} // namespace
133
134void TestSchedulingAnalysisPass::runOnOperation() {
135 MLIRContext *context = &getContext();
136
137 CyclicSchedulingAnalysis analysis = getAnalysis<CyclicSchedulingAnalysis>();
138
139 getOperation().walk([&](AffineForOp forOp) {
140 if (isa<AffineForOp>(forOp.getBody()->front()))
141 return;
142 CyclicProblem problem = analysis.getProblem(forOp);
143 forOp.getBody()->walk([&](Operation *op) {
144 for (auto dep : problem.getDependences(op)) {
145 assert(!dep.isInvalid());
146 if (dep.isAuxiliary())
147 op->setAttr("dependence", UnitAttr::get(context));
148 }
149 });
150 });
151}
152
153//===----------------------------------------------------------------------===//
154// InstanceGraph
155//===----------------------------------------------------------------------===//
156
157namespace {
158struct InferTopModulePass
159 : public PassWrapper<InferTopModulePass, OperationPass<mlir::ModuleOp>> {
160 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(InferTopModulePass)
161
162 void runOnOperation() override;
163 StringRef getArgument() const override { return "test-infer-top-level"; }
164 StringRef getDescription() const override {
165 return "Perform top level module inference and emit results as attributes "
166 "on the enclosing module.";
167 }
168};
169} // namespace
170
171void InferTopModulePass::runOnOperation() {
172 circt::hw::InstanceGraph &analysis = getAnalysis<circt::hw::InstanceGraph>();
173 auto res = analysis.getInferredTopLevelNodes();
174 if (failed(res)) {
175 signalPassFailure();
176 return;
177 }
178
179 llvm::SmallVector<Attribute, 4> attrs;
180 for (auto *node : *res)
181 attrs.push_back(node->getModule().getModuleNameAttr());
182
183 analysis.getParent()->setAttr("test.top",
184 ArrayAttr::get(&getContext(), attrs));
185}
186
187//===----------------------------------------------------------------------===//
188// FIRRTL Instance Info
189//===----------------------------------------------------------------------===//
190
191namespace {
192struct FIRRTLInstanceInfoPass
193 : public PassWrapper<FIRRTLInstanceInfoPass,
194 OperationPass<firrtl::CircuitOp>> {
195 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(FIRRTLInstanceInfoPass)
196
197 void runOnOperation() override;
198 StringRef getArgument() const override { return "test-firrtl-instance-info"; }
199 StringRef getDescription() const override {
200 return "Run firrtl::InstanceInfo analysis and show the results. This pass "
201 "is intended to be used for testing purposes only.";
202 }
203};
204} // namespace
205
206static llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const bool a) {
207 if (a)
208 return os << "true";
209 return os << "false";
210}
211
212static void printCircuitInfo(firrtl::CircuitOp op,
213 firrtl::InstanceInfo &iInfo) {
214 OpPrintingFlags flags;
215 flags.skipRegions();
216 llvm::errs() << " - operation: ";
217 op->print(llvm::errs(), flags);
218 llvm::errs() << "\n"
219 << " hasDut: " << iInfo.hasDut() << "\n"
220 << " dut: ";
221 if (auto dutNode = iInfo.getDut())
222 dutNode->print(llvm::errs(), flags);
223 else
224 llvm::errs() << "null";
225 llvm::errs() << "\n"
226 << " effectiveDut: ";
227 iInfo.getEffectiveDut()->print(llvm::errs(), flags);
228 llvm::errs() << "\n";
229}
230
231static void printModuleInfo(igraph::ModuleOpInterface op,
232 firrtl::InstanceInfo &iInfo) {
233 OpPrintingFlags flags;
234 flags.skipRegions();
235 llvm::errs() << " - operation: ";
236 op->print(llvm::errs(), flags);
237 llvm::errs()
238 << "\n"
239 << " isDut: " << iInfo.isDut(op) << "\n"
240 << " anyInstanceUnderDut: " << iInfo.anyInstanceUnderDut(op) << "\n"
241 << " allInstancesUnderDut: " << iInfo.allInstancesUnderDut(op) << "\n"
242 << " anyInstanceUnderEffectiveDut: "
243 << iInfo.anyInstanceUnderEffectiveDut(op) << "\n"
244 << " allInstancesUnderEffectiveDut: "
245 << iInfo.allInstancesUnderEffectiveDut(op) << "\n"
246 << " anyInstanceUnderLayer: " << iInfo.anyInstanceUnderLayer(op)
247 << "\n"
248 << " allInstancesUnderLayer: " << iInfo.allInstancesUnderLayer(op)
249 << "\n"
250 << " anyInstanceInDesign: " << iInfo.anyInstanceInDesign(op) << "\n"
251 << " allInstancesInDesign: " << iInfo.allInstancesInDesign(op) << "\n"
252 << " anyInstanceInEffectiveDesign: "
253 << iInfo.anyInstanceInEffectiveDesign(op) << "\n"
254 << " allInstancesInEffectiveDesign: "
255 << iInfo.allInstancesInEffectiveDesign(op) << "\n"
256 << " anyInstanceInInstanceChoice: "
257 << iInfo.anyInstanceInInstanceChoice(op) << "\n"
258 << " moduleContainsProperties: " << iInfo.moduleContainsProperties(op)
259 << "\n";
260}
261
262void FIRRTLInstanceInfoPass::runOnOperation() {
263 auto &iInfo = getAnalysis<firrtl::InstanceInfo>();
264
265 printCircuitInfo(getOperation(), iInfo);
266 for (auto op :
267 getOperation().getBodyBlock()->getOps<igraph::ModuleOpInterface>())
268 printModuleInfo(op, iInfo);
269}
270
271//===----------------------------------------------------------------------===//
272// FIRRTL GatedClockConversion
273//===----------------------------------------------------------------------===//
274
275namespace {
276struct FIRRTLGatedClockConversionPass
277 : public PassWrapper<FIRRTLGatedClockConversionPass,
278 OperationPass<firrtl::CircuitOp>> {
279 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(FIRRTLGatedClockConversionPass)
280
281 void runOnOperation() override;
282 StringRef getArgument() const override {
283 return "test-firrtl-gated-clock-conversion";
284 }
285 StringRef getDescription() const override {
286 return "Run firrtl::GatedClockConversion utility and show the results. "
287 "This pass is intended to be used for testing purposes only.";
288 }
289};
290} // namespace
291
292void FIRRTLGatedClockConversionPass::runOnOperation() {
293 auto circuit = getOperation();
294 auto &instanceGraph = getAnalysis<firrtl::InstanceGraph>();
295 firrtl::GatedClockConversion converter(instanceGraph);
296
297 // Collect all register and ref force/release operations
298 circuit.walk([&](Operation *op) {
299 if (isa<firrtl::RegOp, firrtl::RegResetOp, firrtl::RefForceOp,
300 firrtl::RefReleaseOp>(op)) {
301 if (failed(converter.addRoot(op)))
302 return signalPassFailure();
303 }
304 });
305
306 // Run the conversion
307 if (failed(converter.run()))
308 return signalPassFailure();
309}
310
311//===----------------------------------------------------------------------===//
312// Comb IntRange Analysis
313//===----------------------------------------------------------------------===//
314
315namespace {
316struct TestCombIntegerRangeAnalysisPass
317 : public PassWrapper<TestCombIntegerRangeAnalysisPass,
318 OperationPass<mlir::ModuleOp>> {
319 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestCombIntegerRangeAnalysisPass)
320
321 void runOnOperation() override;
322 StringRef getArgument() const override {
323 return "test-comb-int-range-analysis";
324 }
325 StringRef getDescription() const override {
326 return "Perform integer range analysis on comb dialect and set results as "
327 "attributes.";
328 }
329};
330} // namespace
331
332void TestCombIntegerRangeAnalysisPass::runOnOperation() {
333 Operation *op = getOperation();
334 MLIRContext *ctx = op->getContext();
335 DataFlowSolver solver;
336 solver.load<DeadCodeAnalysis>();
337 solver.load<IntegerRangeAnalysis>();
338 if (failed(solver.initializeAndRun(op)))
339 return signalPassFailure();
340
341 // Append the integer range analysis as an operation attribute.
342 op->walk([&](Operation *op) {
343 for (auto value : op->getResults()) {
344 if (auto *range = solver.lookupState<IntegerValueRangeLattice>(value)) {
345 // All analyzed comb operations should return a single result.
346 assert(op->getResults().size() == 1 &&
347 "Expected a single result for the operation analysis");
348 assert(!range->getValue().isUninitialized() &&
349 "Expected a valid range for the value");
350 auto interval = range->getValue().getValue();
351 auto smax = interval.smax();
352 auto smaxAttr =
353 IntegerAttr::get(IntegerType::get(ctx, smax.getBitWidth()), smax);
354 op->setAttr("smax", smaxAttr);
355 auto smin = interval.smin();
356 auto sminAttr =
357 IntegerAttr::get(IntegerType::get(ctx, smin.getBitWidth()), smin);
358 op->setAttr("smin", sminAttr);
359 auto umax = interval.umax();
360 auto umaxAttr = IntegerAttr::get(
361 IntegerType::get(ctx, umax.getBitWidth(), IntegerType::Unsigned),
362 umax);
363 op->setAttr("umax", umaxAttr);
364 auto umin = interval.umin();
365 auto uminAttr = IntegerAttr::get(
366 IntegerType::get(ctx, umin.getBitWidth(), IntegerType::Unsigned),
367 umin);
368 op->setAttr("umin", uminAttr);
369 }
370 }
371 });
372}
373
374//===----------------------------------------------------------------------===//
375// Pass registration
376//===----------------------------------------------------------------------===//
377
378namespace circt {
379namespace test {
381 registerPass([]() -> std::unique_ptr<Pass> {
382 return std::make_unique<TestDependenceAnalysisPass>();
383 });
384 registerPass([]() -> std::unique_ptr<Pass> {
385 return std::make_unique<TestSchedulingAnalysisPass>();
386 });
387 registerPass([]() -> std::unique_ptr<Pass> {
388 return std::make_unique<TestDebugAnalysisPass>();
389 });
390 registerPass([]() -> std::unique_ptr<Pass> {
391 return std::make_unique<InferTopModulePass>();
392 });
393 registerPass([]() -> std::unique_ptr<Pass> {
394 return std::make_unique<FIRRTLInstanceInfoPass>();
395 });
396 registerPass([]() -> std::unique_ptr<Pass> {
397 return std::make_unique<FIRRTLGatedClockConversionPass>();
398 });
399 registerPass([]() -> std::unique_ptr<Pass> {
400 return std::make_unique<TestCombIntegerRangeAnalysisPass>();
401 });
402}
403} // namespace test
404} // namespace circt
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static Block * getBodyBlock(FModuleLike mod)
static void printModuleInfo(igraph::ModuleOpInterface op, firrtl::InstanceInfo &iInfo)
static void printCircuitInfo(firrtl::CircuitOp op, firrtl::InstanceInfo &iInfo)
Sink gated-clock enables into ops across module boundaries.
bool allInstancesUnderLayer(igraph::ModuleOpInterface op)
Return true if all instances of this module are under (or transitively under) layer blocks.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
bool moduleContainsProperties(igraph::ModuleOpInterface op)
Return true if this module contains (or its children transitively contain) any property operations,...
bool hasDut()
Return true if this circuit has a design-under-test.
bool allInstancesInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if all instances of this module are within (or transitively within) the effective design.
bool isDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test.
bool anyInstanceUnderDut(igraph::ModuleOpInterface op)
Return true if at least one instance of this module is under (or transitively under) the design-under...
bool anyInstanceUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if at least one instance is under (or transitively under) the effective design-under-test...
bool allInstancesUnderEffectiveDut(igraph::ModuleOpInterface op)
Return true if all instances are under (or transitively under) the effective design-under-test.
igraph::ModuleOpInterface getEffectiveDut()
Return the "effective" design-under-test.
bool allInstancesUnderDut(igraph::ModuleOpInterface op)
Return true if all instances of this module are under (or transitively under) the design-under-test.
bool anyInstanceInInstanceChoice(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) an instance choice.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
bool allInstancesInDesign(igraph::ModuleOpInterface op)
Return true if all instances of this module are within (or transitively within) the design.
bool anyInstanceUnderLayer(igraph::ModuleOpInterface op)
Return true if at least one instance of this module is under (or transitively under) a layer.
bool anyInstanceInDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the design.
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
Operation * getParent()
Return the parent under which all nodes are nested.
FailureOr< llvm::ArrayRef< InstanceGraphNode * > > getInferredTopLevelNodes()
Get the nodes corresponding to the inferred top-level modules of a circuit.
This class models a cyclic scheduling problem.
Definition Problems.h:359
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
void registerAnalysisTestPasses()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
CyclicSchedulingAnalysis constructs a CyclicProblem for each AffineForOp by performing a memory depen...
scheduling::CyclicProblem & getProblem(mlir::affine::AffineForOp forOp)
MemoryDependenceAnalysis traverses any AffineForOps in the FuncOp body and checks for affine memory a...