CIRCT 20.0.0git
Loading...
Searching...
No Matches
LowerContracts.cpp
Go to the documentation of this file.
1//===- LowerContracts.cpp - Formal Preparations -----------------*- 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//
9// Lower contracts into verif.formal tests.
10//
11//===----------------------------------------------------------------------===//
12
13#include <queue>
14
17#include "mlir/IR/IRMapping.h"
18
19using namespace circt;
20
21namespace circt {
22namespace verif {
23#define GEN_PASS_DEF_LOWERCONTRACTSPASS
24#include "circt/Dialect/Verif/Passes.h.inc"
25} // namespace verif
26} // namespace circt
27
28using namespace mlir;
29using namespace verif;
30using namespace hw;
31
32namespace {
33struct LowerContractsPass
34 : verif::impl::LowerContractsPassBase<LowerContractsPass> {
35 void runOnOperation() override;
36};
37
38Operation *replaceContractOp(OpBuilder &builder, RequireLike op,
39 IRMapping &mapping, bool assumeContract) {
40 StringAttr labelAttr = op.getLabelAttr();
41
42 Value enableValue;
43 if (auto enable = op.getEnable())
44 enable = mapping.lookup(enable);
45
46 auto loc = op.getLoc();
47 auto property = mapping.lookup(op.getProperty());
48
49 if ((isa<EnsureOp>(op) && !assumeContract) ||
50 (isa<RequireOp>(op) && assumeContract))
51 return builder.create<AssertOp>(loc, property, enableValue, labelAttr);
52 if ((isa<EnsureOp>(op) && assumeContract) ||
53 (isa<RequireOp>(op) && !assumeContract))
54 return builder.create<AssumeOp>(loc, property, enableValue, labelAttr);
55 return nullptr;
56}
57
58LogicalResult cloneOp(OpBuilder &builder, Operation *opToClone,
59 IRMapping &mapping, bool assumeContract) {
60 Operation *clonedOp;
61 // Replace ensure/require ops, otherwise clone
62 if (auto requireLike = dyn_cast<RequireLike>(*opToClone)) {
63 clonedOp = replaceContractOp(builder, requireLike, mapping, assumeContract);
64 if (!clonedOp) {
65 return failure();
66 }
67 } else {
68 clonedOp = builder.clone(*opToClone, mapping);
69 }
70
71 // Add mappings for results
72 for (auto [x, y] :
73 llvm::zip(opToClone->getResults(), clonedOp->getResults())) {
74 mapping.map(x, y);
75 }
76 return llvm::success();
77}
78
79void assumeContractHolds(OpBuilder &builder, IRMapping &mapping,
80 ContractOp contract,
81 SmallVector<Operation *> &opsToClone,
82 std::queue<Operation *> &workList,
83 DenseSet<Operation *> &seen) {
84 // Assume it holds by creating symbolic values for the results and inlining
85 // the contract ops into the worklist, by default the clone logic replaces
86 // contract ops with the assume variant
87 for (auto result : contract.getResults()) {
88 auto sym =
89 builder.create<SymbolicValueOp>(result.getLoc(), result.getType());
90 mapping.map(result, sym);
91 }
92 auto &contractOps = contract.getBody().front().getOperations();
93 for (auto it = contractOps.rbegin(); it != contractOps.rend(); ++it) {
94 if (!seen.contains(&*it)) {
95 workList.push(&*it);
96 }
97 }
98}
99
100void buildOpsToClone(OpBuilder &builder, IRMapping &mapping, Operation *op,
101 SmallVector<Operation *> &opsToClone,
102 std::queue<Operation *> &workList,
103 DenseSet<Operation *> &seen, Operation *parent = nullptr) {
104 for (auto operand : op->getOperands()) {
105 if (mapping.contains(operand))
106 continue;
107 // Don't need to clone if defining op is within parent op
108 if (parent && parent->isAncestor(operand.getParentBlock()->getParentOp()))
109 continue;
110 if (auto *definingOp = operand.getDefiningOp()) {
111 if (!seen.contains(definingOp)) {
112 workList.push(definingOp);
113 }
114 } else {
115 // Create symbolic values for arguments
116 auto sym = builder.create<verif::SymbolicValueOp>(operand.getLoc(),
117 operand.getType());
118 mapping.map(operand, sym);
119 }
120 }
121}
122
123LogicalResult cloneFanIn(OpBuilder &builder, Operation *contractToClone,
124 IRMapping &mapping) {
125 DenseSet<Operation *> seen;
126 SmallVector<Operation *> opsToClone;
127 std::queue<Operation *> workList;
128 workList.push(contractToClone);
129
130 while (!workList.empty()) {
131 auto *currentOp = workList.front();
132 workList.pop();
133
134 if (seen.contains(currentOp))
135 continue;
136
137 seen.insert(currentOp);
138
139 auto contract = dyn_cast<ContractOp>(*currentOp);
140 // If it's not the contract being cloned, assume it holds
141 if (contract && (currentOp != contractToClone)) {
142 assumeContractHolds(builder, mapping, contract, opsToClone, workList,
143 seen);
144 // If a contract is assumed, it's nested ops are inlined into the worklist
145 // so no need to walk the body or clone the op
146 continue;
147 }
148
149 // Clone fan in for operands
150 buildOpsToClone(builder, mapping, currentOp, opsToClone, workList, seen);
151 // Clone fan in for nested ops
152 currentOp->walk([&](Operation *nestedOp) {
153 buildOpsToClone(builder, mapping, nestedOp, opsToClone, workList, seen,
154 currentOp);
155 });
156
157 // Source contract is cloned externally
158 if (currentOp != contractToClone)
159 opsToClone.push_back(currentOp);
160 }
161
162 // Clone the ops in reverse order so mapping is available for operands
163 for (auto it = opsToClone.rbegin(); it != opsToClone.rend(); ++it) {
164 if (failed(cloneOp(builder, *it, mapping, true)))
165 return failure();
166 }
167 return success();
168}
169
170LogicalResult inlineContract(ContractOp &contract, OpBuilder &builder,
171 bool assumeContract) {
172 IRMapping mapping;
173 DenseSet<Operation *> seen;
174 if (!assumeContract) {
175 // Inlining into formal op, need to clone fan in cone for contract
176 if (failed(cloneFanIn(builder, contract, mapping)))
177 return failure();
178 }
179
180 if (assumeContract) {
181 // Create symbolic values for results
182 for (auto result : contract.getResults()) {
183 auto sym =
184 builder.create<SymbolicValueOp>(result.getLoc(), result.getType());
185 mapping.map(result, sym);
186 }
187 } else {
188 // Map results by looking up the input mappings
189 for (auto [result, input] :
190 llvm::zip(contract.getResults(), contract.getInputs())) {
191 mapping.map(result, mapping.lookup(input));
192 }
193 }
194
195 // Clone body of the contract
196 for (auto &op : contract.getBody().front().getOperations()) {
197 if (failed(cloneOp(builder, &op, mapping, assumeContract)))
198 return failure();
199 }
200
201 if (assumeContract) {
202 // Inlining into hw module, need to update the results using the mapping
203 for (auto result : contract.getResults())
204 result.replaceAllUsesWith(mapping.lookup(result));
205 }
206 return success();
207}
208
209LogicalResult runOnHWModule(HWModuleOp hwModule, ModuleOp mlirModule) {
210 OpBuilder mlirModuleBuilder(mlirModule);
211 mlirModuleBuilder.setInsertionPointAfter(hwModule);
212 OpBuilder hwModuleBuilder(hwModule);
213
214 // Collect contract ops
215 SmallVector<ContractOp> contracts;
216 hwModule.walk([&](ContractOp op) { contracts.push_back(op); });
217
218 for (unsigned i = 0; i < contracts.size(); i++) {
219 auto contract = contracts[i];
220
221 // Create verif.formal op
222 auto name = mlirModuleBuilder.getStringAttr(
223 hwModule.getNameAttr().getValue() + "_CheckContract_" + Twine(i));
224 auto formalOp = mlirModuleBuilder.create<verif::FormalOp>(
225 contract.getLoc(), name, mlirModuleBuilder.getDictionaryAttr({}));
226
227 // Fill in verif.formal body
228 OpBuilder formalBuilder(formalOp);
229 formalBuilder.createBlock(&formalOp.getBody());
230
231 if (failed(inlineContract(contract, formalBuilder, false)))
232 return failure();
233 }
234
235 for (auto contract : contracts) {
236 // Inline contract into hwModule
237 hwModuleBuilder.setInsertionPointAfter(contract);
238 if (failed(inlineContract(contract, hwModuleBuilder, true)))
239 return failure();
240 contract.erase();
241 }
242
243 return success();
244}
245} // namespace
246
247void LowerContractsPass::runOnOperation() {
248 auto mlirModule = getOperation();
249 for (auto module : mlirModule.getOps<HWModuleOp>())
250 if (failed(runOnHWModule(module, mlirModule)))
251 signalPassFailure();
252}
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition hw.py:1
Definition verif.py:1