CIRCT 24.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.
74 llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> assertConditions;
75 llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> assumeConditions;
76
77 /// Keep track of valid asserts/assumes that will later need to be erased
78 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> assertsToErase;
79 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> assumesToErase;
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 // An enabled assert-like only constrains its condition when the
107 // enable holds, so accumulate `!enable | condition`
108 Value notEnable =
109 comb::createOrFoldNot(builder, defop->getLoc(), enable);
110 auto orop =
111 comb::OrOp::create(builder, defop->getLoc(), notEnable, condition);
112
113 // We then only need to store the implication not the condition
114 conds[parent].push_back(orop);
115 } else {
116 // If no enable is present, we can directly accumulate the condition
117 conds[parent].push_back(condition);
118 }
119
120 // We no longer need the existing assert/assume so request a removal
121 opsToErase[parent].push_back(defop);
122 }
123 return success();
124 }
125
126 /// Combines all of the conditions in a given list into
127 /// a single large conjunction
128 template <typename AT>
129 LogicalResult conjoinConditions(Operation *assertlike,
130 llvm::SmallVectorImpl<Value> &conds,
131 OpBuilder &builder) {
132
133 // Check that we actually accumulated conditions, otherwise exit
134 if (conds.empty())
135 return success();
136
137 // Set insertion
138 builder.setInsertionPointAfter(assertlike);
139
140 // Create a variadic conjunction
141 auto andop = comb::AndOp::create(builder, assertlike->getLoc(), conds,
142 /*two_state=*/false);
143
144 // Create the final assert/assume using the accumulated condition
145 AT::create(builder, andop.getLoc(), andop, /*enable=*/nullptr,
146 /*label=*/nullptr);
147
148 return success();
149 }
150
151 /// Conjoins all of the conditions in a given map on a per block basis
152 /// then erases all replaced assertlike ops
153 template <typename AT>
154 LogicalResult conjoinAndErase(
155 llvm::SmallDenseMap<Block *, llvm::SmallVector<Value>> &conds,
156 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &opsToErase,
157 OpBuilder &builder) {
158 // For each block, conjoin the accumulated conditions
159 for (auto [parent, ops] : opsToErase) {
160 // Only conjoin assertions if there was more than one valid assert-like
161 if (ops.size() > 1) {
162 // Check that some assertions were found
163 if (auto it = conds.find(parent); it != conds.end())
164 // Conjoin the conditions into an assert and an assume respectively
165 if (failed(conjoinConditions<AT>(ops.back(), conds[parent], builder)))
166 return failure();
167
168 // Erase the ops
169 for (auto *op : ops)
170 op->erase();
171 }
172 }
173 return success();
174 }
175};
176} // namespace
177
178void CombineAssertLikePass::runOnOperation() {
179 Operation *module = getOperation();
180 OpBuilder builder(module);
181
182 // Walk over all assert-like ops and accumulate their conditions
183 // then create a new comb.and op or two for assertions and
184 // assumptions to conjoin their respective accumulated conditions.
185 module->walk([&](Operation *op) {
186 // Only consider assertions and assumptions, not cover ops
187 if (auto aop = dyn_cast<verif::AssertOp>(op))
188 if (failed(accumulateCondition(aop, assertConditions, assertsToErase,
189 builder)))
190 return signalPassFailure();
191 if (auto aop = dyn_cast<verif::AssumeOp>(op))
192 if (failed(accumulateCondition(aop, assumeConditions, assumesToErase,
193 builder)))
194 return signalPassFailure();
195 });
196
197 // For each block, conjoin the accumulated conditions and get rid of the
198 // replaced operations
199 if (failed(conjoinAndErase<verif::AssertOp>(assertConditions, assertsToErase,
200 builder)))
201 return signalPassFailure();
202
203 // Same for assumptions
204 if (failed(conjoinAndErase<verif::AssumeOp>(assumeConditions, assumesToErase,
205 builder)))
206 return signalPassFailure();
207
208 // Clear the data structures for pass reuse
209 assertConditions.clear();
210 assumeConditions.clear();
211 assertsToErase.clear();
212 assumesToErase.clear();
213}
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
Definition verif.py:1