CIRCT  19.0.0git
VerifToSMT.cpp
Go to the documentation of this file.
1 //===- VerifToSMT.cpp -----------------------------------------------------===//
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 
13 #include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
14 #include "mlir/Dialect/Arith/IR/Arith.h"
15 #include "mlir/Pass/Pass.h"
16 #include "mlir/Transforms/DialectConversion.h"
17 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
18 
19 namespace circt {
20 #define GEN_PASS_DEF_CONVERTVERIFTOSMT
21 #include "circt/Conversion/Passes.h.inc"
22 } // namespace circt
23 
24 using namespace mlir;
25 using namespace circt;
26 using namespace hw;
27 
28 //===----------------------------------------------------------------------===//
29 // Conversion patterns
30 //===----------------------------------------------------------------------===//
31 
32 namespace {
33 /// Lower a verif::AssertOp operation with an i1 operand to a smt::AssertOp.
34 struct VerifAssertOpConversion : OpConversionPattern<verif::AssertOp> {
36 
37  LogicalResult
38  matchAndRewrite(verif::AssertOp op, OpAdaptor adaptor,
39  ConversionPatternRewriter &rewriter) const override {
40  Value cond = typeConverter->materializeTargetConversion(
41  rewriter, op.getLoc(), smt::BoolType::get(getContext()),
42  adaptor.getProperty());
43  rewriter.replaceOpWithNewOp<smt::AssertOp>(op, cond);
44  return success();
45  }
46 };
47 
48 /// Lower a verif::LecOp operation to a miter circuit encoded in SMT.
49 /// More information on miter circuits can be found, e.g., in this paper:
50 /// Brand, D., 1993, November. Verification of large synthesized designs. In
51 /// Proceedings of 1993 International Conference on Computer Aided Design
52 /// (ICCAD) (pp. 534-537). IEEE.
53 struct LogicEquivalenceCheckingOpConversion
54  : OpConversionPattern<verif::LogicEquivalenceCheckingOp> {
55  using OpConversionPattern<
56  verif::LogicEquivalenceCheckingOp>::OpConversionPattern;
57 
58  LogicalResult
59  matchAndRewrite(verif::LogicEquivalenceCheckingOp op, OpAdaptor adaptor,
60  ConversionPatternRewriter &rewriter) const override {
61  Location loc = op.getLoc();
62  auto *firstOutputs = adaptor.getFirstCircuit().front().getTerminator();
63  auto *secondOutputs = adaptor.getSecondCircuit().front().getTerminator();
64 
65  if (firstOutputs->getNumOperands() == 0) {
66  // Trivially equivalent
67  Value trueVal =
68  rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(true));
69  rewriter.replaceOp(op, trueVal);
70  return success();
71  }
72 
73  smt::SolverOp solver =
74  rewriter.create<smt::SolverOp>(loc, rewriter.getI1Type(), ValueRange{});
75  rewriter.createBlock(&solver.getBodyRegion());
76 
77  // First, convert the block arguments of the miter bodies.
78  if (failed(rewriter.convertRegionTypes(&adaptor.getFirstCircuit(),
79  *typeConverter)))
80  return failure();
81  if (failed(rewriter.convertRegionTypes(&adaptor.getSecondCircuit(),
82  *typeConverter)))
83  return failure();
84 
85  // Second, create the symbolic values we replace the block arguments with
86  SmallVector<Value> inputs;
87  for (auto arg : adaptor.getFirstCircuit().getArguments())
88  inputs.push_back(rewriter.create<smt::DeclareFunOp>(loc, arg.getType()));
89 
90  // Third, inline the blocks
91  // Note: the argument value replacement does not happen immediately, but
92  // only after all the operations are already legalized.
93  // Also, it has to be ensured that the original argument type and the type
94  // of the value with which is is to be replaced match. The value is looked
95  // up (transitively) in the replacement map at the time the replacement
96  // pattern is committed.
97  rewriter.mergeBlocks(&adaptor.getFirstCircuit().front(), solver.getBody(),
98  inputs);
99  rewriter.mergeBlocks(&adaptor.getSecondCircuit().front(), solver.getBody(),
100  inputs);
101  rewriter.setInsertionPointToEnd(solver.getBody());
102 
103  // Fourth, convert the yielded values back to the source type system (since
104  // the operations of the inlined blocks will be converted by other patterns
105  // later on and we should make sure the IR is well-typed after each pattern
106  // application), and build the 'assert'.
107  SmallVector<Value> outputsDifferent;
108  for (auto [out1, out2] :
109  llvm::zip(firstOutputs->getOperands(), secondOutputs->getOperands())) {
110  Value o1 = typeConverter->materializeTargetConversion(
111  rewriter, loc, typeConverter->convertType(out1.getType()), out1);
112  Value o2 = typeConverter->materializeTargetConversion(
113  rewriter, loc, typeConverter->convertType(out1.getType()), out2);
114  outputsDifferent.emplace_back(
115  rewriter.create<smt::DistinctOp>(loc, o1, o2));
116  }
117 
118  rewriter.eraseOp(firstOutputs);
119  rewriter.eraseOp(secondOutputs);
120 
121  Value toAssert;
122  if (outputsDifferent.size() == 1)
123  toAssert = outputsDifferent[0];
124  else
125  toAssert = rewriter.create<smt::OrOp>(loc, outputsDifferent);
126 
127  rewriter.create<smt::AssertOp>(loc, toAssert);
128 
129  // Fifth, check for satisfiablility and report the result back.
130  Value falseVal =
131  rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(false));
132  Value trueVal =
133  rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(true));
134  auto checkOp = rewriter.create<smt::CheckOp>(loc, rewriter.getI1Type());
135  rewriter.createBlock(&checkOp.getSatRegion());
136  rewriter.create<smt::YieldOp>(loc, falseVal);
137  rewriter.createBlock(&checkOp.getUnknownRegion());
138  rewriter.create<smt::YieldOp>(loc, falseVal);
139  rewriter.createBlock(&checkOp.getUnsatRegion());
140  rewriter.create<smt::YieldOp>(loc, trueVal);
141  rewriter.setInsertionPointAfter(checkOp);
142  rewriter.create<smt::YieldOp>(loc, checkOp->getResults());
143 
144  rewriter.replaceOp(op, solver->getResults());
145  return success();
146  }
147 };
148 
149 } // namespace
150 
151 //===----------------------------------------------------------------------===//
152 // Convert Verif to SMT pass
153 //===----------------------------------------------------------------------===//
154 
155 namespace {
156 struct ConvertVerifToSMTPass
157  : public circt::impl::ConvertVerifToSMTBase<ConvertVerifToSMTPass> {
158  void runOnOperation() override;
159 };
160 } // namespace
161 
162 void circt::populateVerifToSMTConversionPatterns(TypeConverter &converter,
163  RewritePatternSet &patterns) {
164  patterns.add<VerifAssertOpConversion, LogicEquivalenceCheckingOpConversion>(
165  converter, patterns.getContext());
166 }
167 
168 void ConvertVerifToSMTPass::runOnOperation() {
169  ConversionTarget target(getContext());
170  target.addIllegalDialect<verif::VerifDialect>();
171  target.addLegalDialect<smt::SMTDialect, arith::ArithDialect>();
172  target.addLegalOp<UnrealizedConversionCastOp>();
173 
174  RewritePatternSet patterns(&getContext());
175  TypeConverter converter;
176  populateHWToSMTTypeConverter(converter);
178 
179  if (failed(mlir::applyPartialConversion(getOperation(), target,
180  std::move(patterns))))
181  return signalPassFailure();
182 
183  // Cleanup as many unrealized conversion casts as possible. This is applied
184  // separately because we would otherwise need to make them entirely illegal,
185  // but we want to allow them such that we can run a series of conversion
186  // passes to SMT converting different dialects. Also, not marking the
187  // unrealized conversion casts legal above (but adding the simplification
188  // patterns) does not work, because the dialect conversion framework adds
189  // IRRewrite patterns to replace values which are only applied after all
190  // operations are legalized. This means, folding the casts away will not be
191  // possible in many cases (especially the explicitly inserted target
192  // materializations in the lowering of the 'miter' operation).
193  RewritePatternSet cleanupPatterns(&getContext());
194  populateReconcileUnrealizedCastsPatterns(cleanupPatterns);
195 
196  if (failed(mlir::applyPatternsAndFoldGreedily(getOperation(),
197  std::move(cleanupPatterns))))
198  return signalPassFailure();
199 }
llvm::SmallVector< StringAttr > inputs
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
void populateVerifToSMTConversionPatterns(TypeConverter &converter, RewritePatternSet &patterns)
Get the Verif to SMT conversion patterns.
Definition: VerifToSMT.cpp:162
void populateHWToSMTTypeConverter(TypeConverter &converter)
Get the HW to SMT type conversions.
Definition: HWToSMT.cpp:108
Definition: hw.py:1