CIRCT 23.0.0git
Loading...
Searching...
No Matches
CombineAssertLike.cpp
Go to the documentation of this file.
1//===- CombineAssertLike.cpp - Combine Assertions --------------*- 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// Combines all assertions and assumptions into single operations
9//
10//===----------------------------------------------------------------------===//
11
15
19
21
22using namespace circt;
23
24namespace circt {
25namespace verif {
26#define GEN_PASS_DEF_COMBINEASSERTLIKEPASS
27#include "circt/Dialect/Verif/Passes.h.inc"
28} // namespace verif
29} // namespace circt
30
31using namespace mlir;
32using namespace verif;
33using namespace hw;
34
35namespace {
36/// Combines all assertions and assumptions by conjoining their conditions into
37/// a single assertion and assumption respectively, e.g.
38/// ```
39/// [...]
40/// verif.assume %pred0 : i1
41/// verif.assume %pred1 : i1
42/// [...]
43/// verif.assert %cond0 : i1
44/// verif.assert %cond1 : i1
45/// ```
46/// will be converted into
47/// ```
48/// [...]
49/// %pred = comb.and bin %pred0, %pred1 : i1
50/// verif.assume %pred : i1
51/// [...]
52/// %cond = comb.and bin %cond0, %cond1 : i1
53/// verif.assert %cond : i1
54/// ```
55/// The accumulation will happen on a per-block basis.
56/// Most of the logic here will be to handle splitting things into blocks
57struct CombineAssertLikePass
58 : verif::impl::CombineAssertLikePassBase<CombineAssertLikePass> {
59 void runOnOperation() override;
60
61private:
62 /// Maps that store all of the accumulated conditions per block
63 /// for assertions and assumption found during our walk.
64 /// This is then used to create a large conjunction of
65 /// all of them in the end to be used as the only assert/assume.
66 /// We don't expect there to be many asserts/assumes per module, therefore we
67 /// can afford the small map.
70
71 /// Keep track of valid asserts/assumes that will later need to be erased
74
75 /// Accumulates conditions of assertions and assumptions.
76 /// Note that this only considers cases where the conditions are
77 /// of type `i1`, and will not merge LTL properties.
78 template <typename T>
79 LogicalResult accumulateCondition(
80 T &op, llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> &conds,
81 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &opsToErase,
82 OpBuilder &builder) {
83 // Extract the condition and parent block the assertlike belongs to
84 auto condition = op.getProperty();
85 auto defop = op.getOperation();
86 Block *parent = defop->getBlock();
87
88 // Check that our condition isn't an ltl property, if so ignore
89 if (!isa<ltl::PropertyType, ltl::SequenceType>(condition.getType())) {
90
91 // Check for an optional enable signal
92 auto enable = op.getEnable();
93 // For i1 conditions, the enable signal can be folded
94 // directly into the condition
95 if (enable) {
96 // Enable should always be reachable from the op, so it's safe to
97 // accumulate right before the op
98 builder.setInsertionPoint(defop);
99
100 auto andop =
101 comb::AndOp::create(builder, defop->getLoc(), condition, enable);
102
103 // We then only need to store the conjunction not the condition
104 conds[parent].push_back(andop);
105 } else {
106 // If no enable is present, we can directly accumulate the condition
107 conds[parent].push_back(condition);
108 }
109
110 // We no longer need the existing assert/assume so request a removal
111 opsToErase[parent].push_back(defop);
112 }
113 return success();
114 }
115
116 /// Combines all of the conditions in a given list into
117 /// a single large conjunction
118 template <typename AT>
119 LogicalResult conjoinConditions(Operation *assertlike,
120 llvm::SmallVectorImpl<Value> &conds,
121 OpBuilder &builder) {
122
123 // Check that we actually accumulated conditions, otherwise exit
124 if (conds.empty())
125 return success();
126
127 // Set insertion
128 builder.setInsertionPointAfter(assertlike);
129
130 // Create a variadic conjunction
131 auto andop = comb::AndOp::create(builder, assertlike->getLoc(), conds,
132 /*two_state=*/false);
133
134 // Create the final assert/assume using the accumulated condition
135 AT::create(builder, andop.getLoc(), andop, /*enable=*/nullptr,
136 /*label=*/nullptr);
137
138 return success();
139 }
140
141 /// Conjoins all of the conditions in a given map on a per block basis
142 /// then erases all replaced assertlike ops
143 template <typename AT>
144 LogicalResult conjoinAndErase(
145 llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> &conds,
146 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &opsToErase,
147 OpBuilder &builder) {
148 // For each block, conjoin the accumulated conditions
149 for (auto [parent, ops] : opsToErase) {
150 // Only conjoin assertions if there was more than one valid assert-like
151 if (ops.size() > 1) {
152 // Check that some assertions were found
153 if (auto it = conds.find(parent); it != conds.end())
154 // Conjoin the conditions into an assert and an assume respectively
155 if (failed(conjoinConditions<AT>(ops.back(), conds[parent], builder)))
156 return failure();
157
158 // Erase the ops
159 for (auto op : ops)
160 op->erase();
161 }
162 }
163 return success();
164 }
165};
166} // namespace
167
168void CombineAssertLikePass::runOnOperation() {
169 hw::HWModuleOp hwModule = getOperation();
170 OpBuilder builder(hwModule);
171
172 // Walk over all assert-like ops and accumulate their conditions
173 // then create a new comb.and op or two for assertions and
174 // assumptions to conjoin their respective accumulated conditions.
175 hwModule.walk([&](Operation *op) {
176 // Only consider assertions and assumptions, not cover ops
177 if (auto aop = dyn_cast<verif::AssertOp>(op))
178 if (failed(accumulateCondition(aop, assertConditions, assertsToErase,
179 builder)))
180 return signalPassFailure();
181 if (auto aop = dyn_cast<verif::AssumeOp>(op))
182 if (failed(accumulateCondition(aop, assumeConditions, assumesToErase,
183 builder)))
184 return signalPassFailure();
185 });
186
187 // For each block, conjoin the accumulated conditions and get rid of the
188 // replaced operations
189 if (failed(conjoinAndErase<verif::AssertOp>(assertConditions, assertsToErase,
190 builder)))
191 return signalPassFailure();
192
193 // Same for assumptions
194 if (failed(conjoinAndErase<verif::AssumeOp>(assumeConditions, assumesToErase,
195 builder)))
196 return signalPassFailure();
197
198 // Clear the data structures for pass reuse
199 assertConditions.clear();
200 assumeConditions.clear();
201 assertsToErase.clear();
202 assumesToErase.clear();
203}
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
Definition verif.py:1