22 #include "llvm/ADT/ScopedHashTable.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Support/Format.h"
25 #include "llvm/Support/raw_ostream.h"
30 using ValueMap = llvm::ScopedHashTable<mlir::Value, std::string>;
32 #define DEBUG_TYPE "export-smtlib"
40 mlir::raw_indented_ostream &> {
52 stream <<
"(_ BitVec " << type.getWidth() <<
")";
57 dispatchSMTTypeVisitor(type.getDomainType(), stream);
59 dispatchSMTTypeVisitor(type.getRangeType(), stream);
65 StringLiteral nextToken =
"";
67 for (
Type domainTy : type.getDomainTypes()) {
69 dispatchSMTTypeVisitor(domainTy, stream);
74 dispatchSMTTypeVisitor(type.getRangeType(), stream);
78 if (!type.getSortParams().empty())
81 stream << type.getIdentifier().getValue();
82 for (
Type paramTy : type.getSortParams()) {
84 dispatchSMTTypeVisitor(paramTy, stream);
87 if (!type.getSortParams().empty())
100 : stream(stream), valueMap(valueMap) {}
102 unsigned indentLevel,
unsigned openParens)
103 : stream(stream), valueMap(valueMap), indentLevel(indentLevel),
104 openParens(openParens) {}
111 unsigned indentLevel = 0;
113 unsigned openParens = 0;
118 struct ExpressionVisitor
123 using Base::visitSMTOp;
128 LogicalResult dispatchSMTOpVisitor(
Operation *op, VisitorInfo &info) {
130 "expression op must have exactly one result value");
142 llvm::raw_string_ostream sstream(str);
145 VisitorInfo newInfo(indentedStream, info.valueMap, info.indentLevel,
147 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
150 info.valueMap.insert(res, str);
157 auto name = names.
newName(
"tmp");
158 info.valueMap.insert(res, name.str());
159 info.stream <<
"(let ((" << name <<
" ";
161 VisitorInfo newInfo(info.stream, info.valueMap,
162 info.indentLevel + 8 + name.size(), 0);
163 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
166 info.stream <<
"))\n";
170 info.indentLevel += 5;
173 info.stream.indent(info.indentLevel);
182 template <
typename Op>
183 LogicalResult printBinaryOp(
Op op, StringRef name, VisitorInfo &info) {
184 info.stream <<
"(" << name <<
" " << info.valueMap.lookup(op.getLhs())
185 <<
" " << info.valueMap.lookup(op.getRhs()) <<
")";
189 template <
typename Op>
190 LogicalResult printVariadicOp(
Op op, StringRef name, VisitorInfo &info) {
191 info.stream <<
"(" << name;
193 info.stream <<
" " << info.valueMap.lookup(val);
198 LogicalResult visitSMTOp(BVNegOp op, VisitorInfo &info) {
199 info.stream <<
"(bvneg " << info.valueMap.lookup(op.getInput()) <<
")";
203 LogicalResult visitSMTOp(BVNotOp op, VisitorInfo &info) {
204 info.stream <<
"(bvnot " << info.valueMap.lookup(op.getInput()) <<
")";
208 #define HANDLE_OP(OPTYPE, NAME, KIND) \
209 LogicalResult visitSMTOp(OPTYPE op, VisitorInfo &info) { \
210 return print##KIND##Op(op, NAME, info); \
228 LogicalResult visitSMTOp(ExtractOp op, VisitorInfo &info) {
229 info.stream <<
"((_ extract "
230 << (op.getLowBit() + op.getType().getWidth() - 1) <<
" "
231 << op.getLowBit() <<
") " << info.valueMap.lookup(op.getInput())
236 LogicalResult visitSMTOp(RepeatOp op, VisitorInfo &info) {
237 info.stream <<
"((_ repeat " << op.getCount() <<
") "
238 << info.valueMap.lookup(op.getInput()) <<
")";
242 LogicalResult visitSMTOp(BVCmpOp op, VisitorInfo &info) {
243 return printBinaryOp(op,
"bv" + stringifyBVCmpPredicate(op.getPred()).str(),
257 LogicalResult visitSMTOp(IntCmpOp op, VisitorInfo &info) {
258 switch (op.getPred()) {
259 case IntPredicate::ge:
260 return printBinaryOp(op,
">=", info);
261 case IntPredicate::le:
262 return printBinaryOp(op,
"<=", info);
263 case IntPredicate::gt:
264 return printBinaryOp(op,
">", info);
265 case IntPredicate::lt:
266 return printBinaryOp(op,
"<", info);
278 LogicalResult visitSMTOp(IteOp op, VisitorInfo &info) {
279 info.stream <<
"(ite " << info.valueMap.lookup(op.getCond()) <<
" "
280 << info.valueMap.lookup(op.getThenValue()) <<
" "
281 << info.valueMap.lookup(op.getElseValue()) <<
")";
285 LogicalResult visitSMTOp(ApplyFuncOp op, VisitorInfo &info) {
286 info.stream <<
"(" << info.valueMap.lookup(op.getFunc());
287 for (
Value arg : op.getArgs())
288 info.stream <<
" " << info.valueMap.lookup(arg);
293 template <
typename OpTy>
294 LogicalResult quantifierHelper(OpTy op, StringRef operatorString,
296 auto weight = op.getWeight();
299 if (op.getNoPattern())
300 return op.emitError() <<
"no-pattern attribute not supported yet";
302 llvm::ScopedHashTableScope<Value, std::string> scope(info.valueMap);
303 info.stream <<
"(" << operatorString <<
" (";
304 StringLiteral delimiter =
"";
311 op.getBoundVarNames()
312 ? cast<StringAttr>(op.getBoundVarNames()->getValue()[i])
315 StringRef name = names.
newName(prefix);
316 argNames.push_back(name);
318 info.valueMap.insert(arg, name.str());
321 info.stream << delimiter <<
"(" << name <<
" ";
322 typeVisitor.dispatchSMTTypeVisitor(arg.getType(), info.stream);
327 info.stream <<
")\n";
333 Value yieldedValue = op.getBody().front().getTerminator()->getOperand(0);
334 worklist.push_back(yieldedValue);
335 unsigned indentExt = operatorString.size() + 2;
336 VisitorInfo newInfo(info.stream, info.valueMap,
337 info.indentLevel + indentExt, 0);
338 if (weight != 0 || !
patterns.empty())
339 newInfo.stream.indent(newInfo.indentLevel);
341 newInfo.stream.indent(info.indentLevel);
343 if (weight != 0 || !
patterns.empty())
344 info.stream <<
"( ! ";
346 if (failed(printExpression(worklist, newInfo)))
349 info.stream << info.valueMap.lookup(yieldedValue);
351 for (
unsigned j = 0;
j < newInfo.openParens; ++
j)
355 info.stream <<
" :weight " << weight;
358 info.stream <<
"\n:pattern (";
366 info.valueMap.insert(arg, argNames[i].str());
371 for (
auto yieldedValue : p.front().getTerminator()->getOperands()) {
373 worklist.push_back(yieldedValue);
374 unsigned indentExt = operatorString.size() + 2;
376 VisitorInfo newInfo2(info.stream, info.valueMap,
377 info.indentLevel + indentExt, 0);
379 info.stream.indent(0);
381 if (failed(printExpression(worklist, newInfo2)))
384 info.stream << info.valueMap.lookup(yieldedValue);
385 for (
unsigned j = 0;
j < newInfo2.openParens; ++
j)
394 if (weight != 0 || !
patterns.empty())
402 LogicalResult visitSMTOp(ForallOp op, VisitorInfo &info) {
403 return quantifierHelper(op,
"forall", info);
406 LogicalResult visitSMTOp(ExistsOp op, VisitorInfo &info) {
407 return quantifierHelper(op,
"exists", info);
410 LogicalResult visitSMTOp(NotOp op, VisitorInfo &info) {
411 info.stream <<
"(not " << info.valueMap.lookup(op.getInput()) <<
")";
424 LogicalResult visitSMTOp(ArrayStoreOp op, VisitorInfo &info) {
425 info.stream <<
"(store " << info.valueMap.lookup(op.getArray()) <<
" "
426 << info.valueMap.lookup(op.getIndex()) <<
" "
427 << info.valueMap.lookup(op.getValue()) <<
")";
431 LogicalResult visitSMTOp(ArraySelectOp op, VisitorInfo &info) {
432 info.stream <<
"(select " << info.valueMap.lookup(op.getArray()) <<
" "
433 << info.valueMap.lookup(op.getIndex()) <<
")";
437 LogicalResult visitSMTOp(ArrayBroadcastOp op, VisitorInfo &info) {
438 info.stream <<
"((as const ";
439 typeVisitor.dispatchSMTTypeVisitor(op.getType(), info.stream);
440 info.stream <<
") " << info.valueMap.lookup(op.getValue()) <<
")";
444 LogicalResult visitUnhandledSMTOp(
Operation *op, VisitorInfo &info) {
454 while (!worklist.empty()) {
455 Value curr = worklist.back();
458 if (info.valueMap.count(curr)) {
465 bool allAvailable =
true;
467 assert(defOp !=
nullptr &&
468 "block arguments must already be in the valueMap");
471 if (!info.valueMap.count(val)) {
472 worklist.push_back(val);
473 allAvailable =
false;
480 if (failed(dispatchSMTOpVisitor(curr.
getDefiningOp(), info)))
492 TypeVisitor typeVisitor;
498 struct StatementVisitor
500 mlir::raw_indented_ostream &, ValueMap &> {
510 valueMap.insert(op.getResult(), op.getValue().getValueAsString());
514 LogicalResult visitSMTOp(BoolConstantOp op,
517 valueMap.insert(op.getResult(), op.getValue() ?
"true" :
"false");
524 op.getValue().toStringSigned(str);
525 valueMap.insert(op.getResult(), str.str().str());
532 names.
newName(op.getNamePrefix() ? *op.getNamePrefix() :
"tmp");
533 valueMap.insert(op.getResult(), name.str());
535 << (isa<SMTFuncType>(op.getType()) ?
"declare-fun "
538 typeVisitor.dispatchSMTTypeVisitor(op.getType(), stream);
545 llvm::ScopedHashTableScope<Value, std::string> scope1(valueMap);
547 worklist.push_back(op.getInput());
548 stream <<
"(assert ";
549 VisitorInfo info(stream, valueMap, 8, 0);
550 if (failed(exprVisitor.printExpression(worklist, info)))
552 stream << valueMap.lookup(op.getInput());
553 for (
unsigned i = 0; i < info.openParens + 1; ++i)
562 stream <<
"(reset)\n";
568 stream <<
"(push " << op.getCount() <<
")\n";
574 stream <<
"(pop " << op.getCount() <<
")\n";
580 if (op->getNumResults() != 0)
581 return op.emitError() <<
"must not have any result values";
583 if (op.getSatRegion().front().getOperations().size() != 1)
584 return op->emitError() <<
"'sat' region must be empty";
585 if (op.getUnknownRegion().front().getOperations().size() != 1)
586 return op->emitError() <<
"'unknown' region must be empty";
587 if (op.getUnsatRegion().front().getOperations().size() != 1)
588 return op->emitError() <<
"'unsat' region must be empty";
590 stream <<
"(check-sat)\n";
596 stream <<
"(set-logic " << op.getLogic() <<
")\n";
600 LogicalResult visitUnhandledSMTOp(
Operation *op,
604 if (isa<smt::Int2BVOp, BV2IntOp>(op))
605 return op->
emitError(
"operation not supported for SMTLIB emission");
613 TypeVisitor typeVisitor;
615 ExpressionVisitor exprVisitor;
627 if (!solver.getInputs().empty() || solver->getNumResults() != 0)
628 return solver->emitError()
629 <<
"solver scopes with inputs or results are not supported";
631 Block *block = solver.getBody();
638 <<
"solver must not contain any non-SMT operations";
641 auto sortTy = dyn_cast<SortType>(resTy);
645 unsigned arity = sortTy.getSortParams().size();
646 if (declaredSorts.contains(sortTy.getIdentifier())) {
647 if (declaredSorts[sortTy.getIdentifier()] != arity)
648 return op->emitError(
"uninterpreted sorts with same identifier but "
649 "different arity found");
654 declaredSorts[sortTy.getIdentifier()] = arity;
655 stream <<
"(declare-sort " << sortTy.getIdentifier().getValue() <<
" "
660 if (result.wasInterrupted())
664 llvm::ScopedHashTableScope<Value, std::string> scope0(valueMap);
666 StatementVisitor visitor(
options, names);
670 result = block->walk([&](
Operation *op) {
671 if (failed(visitor.dispatchSMTOpVisitor(op, stream, valueMap)))
675 if (result.wasInterrupted())
678 stream <<
"(reset)\n";
685 return module->
emitError(
"must have exactly one region");
687 return module->
emitError(
"op region must have exactly one block");
690 unsigned solverIdx = 0;
691 auto result = module->
walk([&](SolverOp solver) {
692 ios <<
"; solver scope " << solverIdx <<
"\n";
699 return failure(result.wasInterrupted());
707 static llvm::cl::opt<bool> inlineSingleUseValues(
708 "smtlibexport-inline-single-use-values",
709 llvm::cl::desc(
"Inline expressions that are used only once rather than "
710 "generating a let-binding"),
711 llvm::cl::init(
false));
713 auto getOptions = [] {
720 "export-smtlib",
"export SMT-LIB",
721 [=](
Operation *module, raw_ostream &output) {
727 registry.insert<mlir::func::FuncDialect, arith::ArithDialect,
static llvm::ManagedStatic< PassManagerOptions > options
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
#define HANDLE_OP(OPTYPE, NAME, KIND)
llvm::ScopedHashTable< mlir::Value, std::string > ValueMap
Block represents an ordered list of Operations.
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
A namespace that is used to store existing names and generate new names in some scope within the IR.
llvm::StringRef newName(const llvm::Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
unsigned getNumRegions()
Returns the number of regions held by this operation.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
unsigned getNumResults()
Return the number of results held by this operation.
bool hasOneBlock()
Return true if this region has exactly one block.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
raw_ostream subclass that simplifies indention a sequence of code.
raw_indented_ostream & indent()
Increases the indent and returning this raw_indented_ostream.
This helps visit SMT nodes.
This helps visit SMT types.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
void registerExportSMTLIBTranslation()
Register the ExportSMTLIB pass.
LogicalResult exportSMTLIB(Operation *module, llvm::raw_ostream &os, const SMTEmissionOptions &options=SMTEmissionOptions())
Run the ExportSMTLIB pass.
Include the generated interface declarations.
const FrozenRewritePatternSet & patterns
Emission options for the ExportSMTLIB pass.
bool inlineSingleUseValues
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.