CIRCT 23.0.0git
Loading...
Searching...
No Matches
FoldAssume.cpp
Go to the documentation of this file.
1//===- FoldAssume.cpp - Fold Assumptions --------------*- 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// Folds all assumes into the enable signal of an assert
9//
10//===----------------------------------------------------------------------===//
11
15
19
21
22using namespace circt;
23
24namespace circt {
25namespace verif {
26#define GEN_PASS_DEF_FOLDASSUMEPASS
27#include "circt/Dialect/Verif/Passes.h.inc"
28} // namespace verif
29} // namespace circt
30
31using namespace mlir;
32
33namespace {
34
35/// Implements assumes manually by combining all of them into a knowledge
36/// caucus, that is then used as the antecedant of every assertion within the
37/// same region. This assumes that `combine-assert-like` is run prior to running
38/// this pass, as it will fail if there is more than one assertion and one
39/// assumption per block. E.g.,
40/// ```mlir
41/// [...]
42/// verif.assume %conjoinedPred : i1
43/// [...]
44/// verif.assert %cond : i1
45/// ```
46/// is converted to
47/// ```mlir
48/// [...]
49/// [...]
50/// verif.assert %cond if %conjoinedPred : i1
51/// ```
52/// This is a semantically preserving transformation in the formal back-ends
53/// as assumes have the following underlying semantics:
54///
55/// {a0, ..., ak} ∈ A
56/// -------------------------------------------------------------------------
57/// Γ; A ⊢ assume {a0, ... ak}; assert a -> assert ((a0 and .. and ak) => a)
58///
59/// Therefore, assertions become trivial when assumptions do no hold for a
60/// concrete value, making them equivalent to an enable signal.
61struct FoldAssumePass : verif::impl::FoldAssumePassBase<FoldAssumePass> {
62
63 /// Only allow scheduling on verif::FormalOp and hw::HWModuleOp
64 bool canScheduleOn(RegisteredOperationName opInfo) const override {
65 return opInfo.getStringRef() == hw::HWModuleOp::getOperationName() ||
66 opInfo.getStringRef() == verif::FormalOp::getOperationName();
67 }
68
69 void runOnOperation() override;
70
71private:
72 /// Keep track of assumptions and assertions on a per block basis
75
76 /// Checks if thee current assertion/assumption is found multiple times in the
77 /// block. If no other is found, save this assumption/assertion.
78 LogicalResult findAssertlikes(
79 Operation *defop,
80 llvm::SmallDenseMap<Block *, llvm::SmallVector<Operation *>> &ops) {
81 Block *blk = defop->getBlock();
82
83 // Ignore assertlikes that contain ltl properties
84 if (isa<ltl::PropertyType, ltl::SequenceType>(
85 defop->getOperand(0).getType()))
86 return success();
87
88 // Make sure the block doesn't have any other entries
89 if (auto it = ops.find(blk); it != ops.end())
90 if ((it->getSecond().size() > 0)) {
91 defop->emitError()
92 << "Multiple " << defop->getName()
93 << " found in the current block! Run `--combine-assert-like` "
94 << "before running --fold-assume.";
95 return failure();
96 }
97
98 // Store the assertlike op
99 ops[blk].push_back(defop);
100 return success();
101 }
102
103 /// Fold all assumes into their block's assert for all blocks given an mlir
104 /// modules' opbuilder.
105 LogicalResult foldAssumesIntoAsserts(OpBuilder &builder) {
106 // boolean type
107 auto i1 = builder.getI1Type();
108 // For each assume, find the assert from the same block and augment it's
109 // enable signal with the assume's condition.
110 for (auto [blk, ops] : assumes) {
111 // Check if the block had any assumptions
112 if (ops.size() == 0)
113 continue;
114
115 // We should fail here if multiple assumptions were stored, we do
116 // not accumulate the assumptions, as that is done by
117 // verif::CombineAssertLikePass
118 if (ops.size() > 1) {
119 ops.back()->emitError(
120 "Multiple assuptions found in the current block! Run "
121 "`--combine-assert-like` before running --fold-assume.");
122 return failure();
123 }
124
125 // Should be an assumption
126 auto assumeOp = cast<verif::AssumeOp>(ops.front());
127 Location loc = assumeOp.getLoc();
128 Value cond = assumeOp.getProperty();
129
130 // Add new ops after the assumption
131 builder.setInsertionPoint(ops.front());
132
133 // Look for a matching assertion, make that list is non-empty
134 if (auto it = asserts.find(blk);
135 it != asserts.end() && it->getSecond().size() > 0) {
136 auto const &assertOps = it->getSecond();
137
138 // We should fail here if multiple assertions were stored, as we
139 // do not fold the assumption into every assertion in the block
140 if (assertOps.size() > 1) {
141 assertOps.back()->emitError(
142 "Multiple assertions found in the current block! Run "
143 "`--combine-assert-like` before running --fold-assume.");
144 return failure();
145 }
146
147 // Should be an assertion now
148 auto assertOp = cast<verif::AssertOp>(assertOps.front());
149 builder.setInsertionPoint(assertOps.front());
150 Location assertLoc = assertOp.getLoc();
151 Value en = assertOp.getEnable();
152
153 // Replace the enable signal with the assumption condition
154 verif::AssertOp::create(
155 builder, assertLoc, assertOp.getProperty(),
156 // Conjoin enable signal with condition if needed
157 en ? comb::AndOp::create(builder, assertLoc, en, cond) : cond,
158 /*label=*/{});
159
160 // Remove the old op
161 assertOp->erase();
162 } else {
163 // If no matching assertion was found, make a trivial assertion
164 // and set the enable signal with the assumption's condition, i.e.
165 // create `assert 1 if %cond`
166 auto tConst =
167 hw::ConstantOp::create(builder, loc, IntegerAttr::get(i1, 1));
168 verif::AssertOp::create(builder, loc, tConst, /*enable=*/cond,
169 /*label=*/{});
170 }
171
172 // Delete the assumption
173 assumeOp->erase();
174 }
175 // Only succeed if all blocks were converted
176 return success();
177 }
178};
179} // namespace
180
181void FoldAssumePass::runOnOperation() {
182 Operation *module = getOperation();
183 OpBuilder builder(module);
184
185 // dispatches the internal matcher on assume ops
186 // Skip any non-module-like op
187 if (!isa<hw::HWModuleOp, verif::FormalOp>(module))
188 return;
189
190 // At this point we can just walk the internal ops
191 WalkResult wr = module->walk([&](Operation *op) {
192 if (isa<verif::AssumeOp, verif::AssertOp>(op))
193 // Group all assertlikes per block
194 if (failed(findAssertlikes(op,
195 isa<verif::AssertOp>(op) ? asserts : assumes)))
196 return WalkResult::interrupt();
197 return WalkResult::advance();
198 });
199
200 // Check if the walk failed
201 if (wr.wasInterrupted())
202 return signalPassFailure();
203
204 // Fold all assumes into their block's assert
205 if (failed(foldAssumesIntoAsserts(builder)))
206 return signalPassFailure();
207
208 // Clear data structures in between modules
209 asserts.clear();
210 assumes.clear();
211}
create(data_type, value)
Definition hw.py:433
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition verif.py:1