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
60 /// Only allow scheduling on verif::FormalOp and hw::HWModuleOp
61 bool canScheduleOn(RegisteredOperationName opInfo) const override {
62 return opInfo.getStringRef() == hw::HWModuleOp::getOperationName() ||
63 opInfo.getStringRef() == verif::FormalOp::getOperationName();
64 }
65 void runOnOperation() override;
66
67private:
68 /// Maps that store all of the accumulated conditions per block
69 /// for assertions and assumption found during our walk.
70 /// This is then used to create a large conjunction of
71 /// all of them in the end to be used as the only assert/assume.
72 /// We don't expect there to be many asserts/assumes per module, therefore we
73 /// can afford the small map.
76
77 /// Keep track of valid asserts/assumes that will later need to be erased
80
81 /// Accumulates conditions of assertions and assumptions.
82 /// Note that this only considers cases where the conditions are
83 /// of type `i1`, and will not merge LTL properties.
84 template <typename T>
85 LogicalResult accumulateCondition(
86 T &op, llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> &conds,
87 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &opsToErase,
88 OpBuilder &builder) {
89 // Extract the condition and parent block the assertlike belongs to
90 auto condition = op.getProperty();
91 auto defop = op.getOperation();
92 Block *parent = defop->getBlock();
93
94 // Check that our condition isn't an ltl property, if so ignore
95 if (!isa<ltl::PropertyType, ltl::SequenceType>(condition.getType())) {
96
97 // Check for an optional enable signal
98 auto enable = op.getEnable();
99 // For i1 conditions, the enable signal can be folded
100 // directly into the condition
101 if (enable) {
102 // Enable should always be reachable from the op, so it's safe to
103 // accumulate right before the op
104 builder.setInsertionPoint(defop);
105
106 auto andop =
107 comb::AndOp::create(builder, defop->getLoc(), condition, enable);
108
109 // We then only need to store the conjunction not the condition
110 conds[parent].push_back(andop);
111 } else {
112 // If no enable is present, we can directly accumulate the condition
113 conds[parent].push_back(condition);
114 }
115
116 // We no longer need the existing assert/assume so request a removal
117 opsToErase[parent].push_back(defop);
118 }
119 return success();
120 }
121
122 /// Combines all of the conditions in a given list into
123 /// a single large conjunction
124 template <typename AT>
125 LogicalResult conjoinConditions(Operation *assertlike,
126 llvm::SmallVectorImpl<Value> &conds,
127 OpBuilder &builder) {
128
129 // Check that we actually accumulated conditions, otherwise exit
130 if (conds.empty())
131 return success();
132
133 // Set insertion
134 builder.setInsertionPointAfter(assertlike);
135
136 // Create a variadic conjunction
137 auto andop = comb::AndOp::create(builder, assertlike->getLoc(), conds,
138 /*two_state=*/false);
139
140 // Create the final assert/assume using the accumulated condition
141 AT::create(builder, andop.getLoc(), andop, /*enable=*/nullptr,
142 /*label=*/nullptr);
143
144 return success();
145 }
146
147 /// Conjoins all of the conditions in a given map on a per block basis
148 /// then erases all replaced assertlike ops
149 template <typename AT>
150 LogicalResult conjoinAndErase(
151 llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> &conds,
152 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &opsToErase,
153 OpBuilder &builder) {
154 // For each block, conjoin the accumulated conditions
155 for (auto [parent, ops] : opsToErase) {
156 // Only conjoin assertions if there was more than one valid assert-like
157 if (ops.size() > 1) {
158 // Check that some assertions were found
159 if (auto it = conds.find(parent); it != conds.end())
160 // Conjoin the conditions into an assert and an assume respectively
161 if (failed(conjoinConditions<AT>(ops.back(), conds[parent], builder)))
162 return failure();
163
164 // Erase the ops
165 for (auto op : ops)
166 op->erase();
167 }
168 }
169 return success();
170 }
171};
172} // namespace
173
174void CombineAssertLikePass::runOnOperation() {
175 Operation *module = getOperation();
176 OpBuilder builder(module);
177
178 // Walk over all assert-like ops and accumulate their conditions
179 // then create a new comb.and op or two for assertions and
180 // assumptions to conjoin their respective accumulated conditions.
181 module->walk([&](Operation *op) {
182 // Only consider assertions and assumptions, not cover ops
183 if (auto aop = dyn_cast<verif::AssertOp>(op))
184 if (failed(accumulateCondition(aop, assertConditions, assertsToErase,
185 builder)))
186 return signalPassFailure();
187 if (auto aop = dyn_cast<verif::AssumeOp>(op))
188 if (failed(accumulateCondition(aop, assumeConditions, assumesToErase,
189 builder)))
190 return signalPassFailure();
191 });
192
193 // For each block, conjoin the accumulated conditions and get rid of the
194 // replaced operations
195 if (failed(conjoinAndErase<verif::AssertOp>(assertConditions, assertsToErase,
196 builder)))
197 return signalPassFailure();
198
199 // Same for assumptions
200 if (failed(conjoinAndErase<verif::AssumeOp>(assumeConditions, assumesToErase,
201 builder)))
202 return signalPassFailure();
203
204 // Clear the data structures for pass reuse
205 assertConditions.clear();
206 assumeConditions.clear();
207 assertsToErase.clear();
208 assumesToErase.clear();
209}
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
Definition verif.py:1