Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Lint.cpp
Go to the documentation of this file.
1//===- Lint.cpp -------------------------------------------------*- C++ -*-===//
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
13#include "circt/Support/Utils.h"
14#include "mlir/Pass/Pass.h"
15#include "llvm/ADT/APSInt.h"
16
17namespace circt {
18namespace firrtl {
19#define GEN_PASS_DEF_LINT
20#include "circt/Dialect/FIRRTL/Passes.h.inc"
21} // namespace firrtl
22} // namespace circt
23
24using namespace mlir;
25using namespace circt;
26using namespace firrtl;
27
28namespace {
29/// Class that stores state related to linting. This exists to avoid needing to
30/// clear members of `LintPass` and instead just rely on `Linter` objects being
31/// deleted.
32class Linter {
33
34public:
35 Linter(FModuleOp fModule, InstanceInfo &instanceInfo,
36 const LintOptions &options)
37 : fModule(fModule), instanceInfo(instanceInfo), options(options){};
38
39 /// Lint the specified module.
40 LogicalResult lint() {
41 bool failed = false;
42 fModule.walk<WalkOrder::PreOrder>([&](Operation *op) {
43 if (isa<WhenOp>(op))
44 return WalkResult::skip();
45 if (isa<AssertOp, VerifAssertIntrinsicOp>(op))
46 if (options.lintStaticAsserts && checkAssert(op).failed())
47 failed = true;
48
49 if (auto xmrDerefOp = dyn_cast<XMRDerefOp>(op))
50 if (options.lintXmrsInDesign && checkXmr(xmrDerefOp).failed())
51 failed = true;
52
53 return WalkResult::advance();
54 });
55
56 if (failed)
57 return failure();
58
59 return success();
60 }
61
62private:
63 FModuleOp fModule;
64 InstanceInfo &instanceInfo;
65 const LintOptions &options;
66
67 LogicalResult checkAssert(Operation *op) {
68 Value predicate;
69 if (auto a = dyn_cast<AssertOp>(op)) {
70 if (auto constant = a.getEnable().getDefiningOp<firrtl::ConstantOp>())
71 if (constant.getValue().isOne()) {
72 predicate = a.getPredicate();
73 }
74 } else if (auto a = dyn_cast<VerifAssertIntrinsicOp>(op))
75 predicate = a.getProperty();
76
77 if (!predicate)
78 return success();
79 if (auto constant = predicate.getDefiningOp<firrtl::ConstantOp>())
80 if (constant.getValue().isZero())
81 return op->emitOpError(
82 "is guaranteed to fail simulation, as the predicate is "
83 "constant false")
84 .attachNote(constant.getLoc())
85 << "constant defined here";
86
87 if (auto reset = predicate.getDefiningOp<firrtl::AsUIntPrimOp>())
88 if (firrtl::type_isa<ResetType, AsyncResetType>(
89 reset.getInput().getType()))
90 return op->emitOpError("is guaranteed to fail simulation, as the "
91 "predicate is a reset signal")
92 .attachNote(reset.getInput().getLoc())
93 << "reset signal defined here";
94
95 return success();
96 }
97
98 LogicalResult checkXmr(XMRDerefOp op) {
99 // XMRs under layers are okay.
100 if (op->getParentOfType<LayerBlockOp>() ||
101 op->getParentOfType<sv::IfDefOp>())
102 return success();
103
104 // The XMR is not under a layer. This module must never be instantiated in
105 // the design. Intentionally do NOT use "effective" design as this could
106 // lead to false positives.
107 if (!instanceInfo.anyInstanceInDesign(fModule))
108 return success();
109
110 // If all users are connect sources, and each connect destinations is to an
111 // instance which is marked `lowerToBind`, then this is a pattern for
112 // inlining the XMR into the bound instance site. This pattern is used by
113 // Grand Central, but not elsewhere.
114 //
115 // If there are _no_ users, this is also okay as this expression will not be
116 // emitted.
117 auto boundInstancePortUser = [&](auto user) {
118 auto connect = dyn_cast<MatchingConnectOp>(user);
119 if (connect && connect.getSrc() == op.getResult())
120 if (auto *definingOp = connect.getDest().getDefiningOp())
121 if (auto instanceOp = dyn_cast<InstanceOp>(definingOp))
122 if (instanceOp->hasAttr("lowerToBind"))
123 return true;
124 return false;
125 };
126 if (llvm::all_of(op.getResult().getUsers(), boundInstancePortUser))
127 return success();
128
129 auto diag =
130 op.emitOpError()
131 << "is in the design. (Did you forget to put it under a layer?)";
132 diag.attachNote(fModule.getLoc()) << "op is instantiated in this module";
133
134 return failure();
135 }
136};
137
138struct LintPass : public circt::firrtl::impl::LintBase<LintPass> {
139 using LintBase::LintBase;
140
141 void runOnOperation() override {
142
143 CircuitOp circuitOp = getOperation();
144 auto instanceInfo = getAnalysis<InstanceInfo>();
145
146 auto reduce = [](LogicalResult a, LogicalResult b) -> LogicalResult {
147 if (succeeded(a) && succeeded(b))
148 return success();
149 return failure();
150 };
151 auto transform = [&](FModuleOp moduleOp) -> LogicalResult {
152 return Linter(moduleOp, instanceInfo,
153 {lintStaticAsserts, lintXmrsInDesign})
154 .lint();
155 };
156
157 SmallVector<FModuleOp> modules(circuitOp.getOps<FModuleOp>());
158 if (failed(transformReduce(circuitOp.getContext(), modules, success(),
159 reduce, transform)))
160 return signalPassFailure();
161
162 markAllAnalysesPreserved();
163 };
164};
165} // namespace
bool anyInstanceInDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the design.
connect(destination, source)
Definition support.py:39
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static ResultTy transformReduce(MLIRContext *context, IterTy begin, IterTy end, ResultTy init, ReduceFuncTy reduce, TransformFuncTy transform)
Wrapper for llvm::parallelTransformReduce that performs the transform_reduce serially when MLIR multi...
Definition Utils.h:40