17#include "mlir/IR/IRMapping.h"
23#define GEN_PASS_DEF_LOWERCONTRACTSPASS
24#include "circt/Dialect/Verif/Passes.h.inc"
33struct LowerContractsPass
34 : verif::impl::LowerContractsPassBase<LowerContractsPass> {
35 void runOnOperation()
override;
38Operation *replaceContractOp(OpBuilder &builder, RequireLike op,
39 IRMapping &mapping,
bool assumeContract) {
40 StringAttr labelAttr = op.getLabelAttr();
43 if (
auto enable = op.getEnable())
44 enable = mapping.lookup(enable);
46 auto loc = op.getLoc();
47 auto property = mapping.lookup(op.getProperty());
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);
58LogicalResult cloneOp(OpBuilder &builder, Operation *opToClone,
59 IRMapping &mapping,
bool assumeContract) {
62 if (
auto requireLike = dyn_cast<RequireLike>(*opToClone)) {
63 clonedOp = replaceContractOp(builder, requireLike, mapping, assumeContract);
68 clonedOp = builder.clone(*opToClone, mapping);
73 llvm::zip(opToClone->getResults(), clonedOp->getResults())) {
76 return llvm::success();
79void assumeContractHolds(OpBuilder &builder, IRMapping &mapping,
81 SmallVector<Operation *> &opsToClone,
82 std::queue<Operation *> &workList,
83 DenseSet<Operation *> &seen) {
87 for (
auto result : contract.getResults()) {
89 builder.create<SymbolicValueOp>(result.getLoc(), result.getType());
90 mapping.map(result, sym);
92 auto &contractOps = contract.getBody().front().getOperations();
93 for (
auto it = contractOps.rbegin(); it != contractOps.rend(); ++it) {
94 if (!seen.contains(&*it)) {
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))
108 if (parent && parent->isAncestor(operand.getParentBlock()->getParentOp()))
110 if (
auto *definingOp = operand.getDefiningOp()) {
111 if (!seen.contains(definingOp)) {
112 workList.push(definingOp);
116 auto sym = builder.create<verif::SymbolicValueOp>(operand.getLoc(),
118 mapping.map(operand, sym);
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);
130 while (!workList.empty()) {
131 auto *currentOp = workList.front();
134 if (seen.contains(currentOp))
137 seen.insert(currentOp);
139 auto contract = dyn_cast<ContractOp>(*currentOp);
141 if (contract && (currentOp != contractToClone)) {
142 assumeContractHolds(builder, mapping, contract, opsToClone, workList,
150 buildOpsToClone(builder, mapping, currentOp, opsToClone, workList, seen);
152 currentOp->walk([&](Operation *nestedOp) {
153 buildOpsToClone(builder, mapping, nestedOp, opsToClone, workList, seen,
158 if (currentOp != contractToClone)
159 opsToClone.push_back(currentOp);
163 for (
auto it = opsToClone.rbegin(); it != opsToClone.rend(); ++it) {
164 if (failed(cloneOp(builder, *it, mapping,
true)))
170LogicalResult inlineContract(ContractOp &contract, OpBuilder &builder,
171 bool assumeContract) {
173 DenseSet<Operation *> seen;
174 if (!assumeContract) {
176 if (failed(cloneFanIn(builder, contract, mapping)))
180 if (assumeContract) {
182 for (
auto result : contract.getResults()) {
184 builder.create<SymbolicValueOp>(result.getLoc(), result.getType());
185 mapping.map(result, sym);
189 for (
auto [result, input] :
190 llvm::zip(contract.getResults(), contract.getInputs())) {
191 mapping.map(result, mapping.lookup(input));
196 for (
auto &op : contract.getBody().front().getOperations()) {
197 if (failed(cloneOp(builder, &op, mapping, assumeContract)))
201 if (assumeContract) {
203 for (
auto result : contract.getResults())
204 result.replaceAllUsesWith(mapping.lookup(result));
209LogicalResult runOnHWModule(
HWModuleOp hwModule, ModuleOp mlirModule) {
210 OpBuilder mlirModuleBuilder(mlirModule);
211 mlirModuleBuilder.setInsertionPointAfter(hwModule);
212 OpBuilder hwModuleBuilder(hwModule);
215 SmallVector<ContractOp> contracts;
216 hwModule.walk([&](ContractOp op) { contracts.push_back(op); });
218 for (
unsigned i = 0; i < contracts.size(); i++) {
219 auto contract = contracts[i];
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({}));
228 OpBuilder formalBuilder(formalOp);
229 formalBuilder.createBlock(&formalOp.getBody());
231 if (failed(inlineContract(contract, formalBuilder,
false)))
235 for (
auto contract : contracts) {
237 hwModuleBuilder.setInsertionPointAfter(contract);
238 if (failed(inlineContract(contract, hwModuleBuilder,
true)))
247void LowerContractsPass::runOnOperation() {
248 auto mlirModule = getOperation();
249 for (
auto module : mlirModule.getOps<
HWModuleOp>())
250 if (failed(runOnHWModule(module, mlirModule)))
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.