22 #include "llvm/ADT/ScopedHashTable.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Support/raw_ostream.h"
29 using ValueMap = llvm::ScopedHashTable<mlir::Value, std::string>;
31 #define DEBUG_TYPE "export-smtlib"
39 mlir::raw_indented_ostream &> {
51 stream <<
"(_ BitVec " << type.getWidth() <<
")";
56 dispatchSMTTypeVisitor(type.getDomainType(), stream);
58 dispatchSMTTypeVisitor(type.getRangeType(), stream);
64 StringLiteral nextToken =
"";
66 for (
Type domainTy : type.getDomainTypes()) {
68 dispatchSMTTypeVisitor(domainTy, stream);
73 dispatchSMTTypeVisitor(type.getRangeType(), stream);
77 if (!type.getSortParams().empty())
80 stream << type.getIdentifier().getValue();
81 for (
Type paramTy : type.getSortParams()) {
83 dispatchSMTTypeVisitor(paramTy, stream);
86 if (!type.getSortParams().empty())
99 : stream(stream), valueMap(valueMap) {}
101 unsigned indentLevel,
unsigned openParens)
102 : stream(stream), valueMap(valueMap), indentLevel(indentLevel),
103 openParens(openParens) {}
110 unsigned indentLevel = 0;
112 unsigned openParens = 0;
117 struct ExpressionVisitor
122 using Base::visitSMTOp;
127 LogicalResult dispatchSMTOpVisitor(
Operation *op, VisitorInfo &info) {
129 "expression op must have exactly one result value");
141 llvm::raw_string_ostream sstream(str);
144 VisitorInfo newInfo(indentedStream, info.valueMap, info.indentLevel,
146 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
149 info.valueMap.insert(res, str);
156 auto name = names.
newName(
"tmp");
157 info.valueMap.insert(res, name.str());
158 info.stream <<
"(let ((" << name <<
" ";
160 VisitorInfo newInfo(info.stream, info.valueMap,
161 info.indentLevel + 8 + name.size(), 0);
162 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
165 info.stream <<
"))\n";
169 info.indentLevel += 5;
172 info.stream.indent(info.indentLevel);
181 template <
typename Op>
182 LogicalResult printBinaryOp(
Op op, StringRef name, VisitorInfo &info) {
183 info.stream <<
"(" << name <<
" " << info.valueMap.lookup(op.getLhs())
184 <<
" " << info.valueMap.lookup(op.getRhs()) <<
")";
188 template <
typename Op>
189 LogicalResult printVariadicOp(
Op op, StringRef name, VisitorInfo &info) {
190 info.stream <<
"(" << name;
192 info.stream <<
" " << info.valueMap.lookup(val);
197 LogicalResult visitSMTOp(BVNegOp op, VisitorInfo &info) {
198 info.stream <<
"(bvneg " << info.valueMap.lookup(op.getInput()) <<
")";
202 LogicalResult visitSMTOp(BVNotOp op, VisitorInfo &info) {
203 info.stream <<
"(bvnot " << info.valueMap.lookup(op.getInput()) <<
")";
207 #define HANDLE_OP(OPTYPE, NAME, KIND) \
208 LogicalResult visitSMTOp(OPTYPE op, VisitorInfo &info) { \
209 return print##KIND##Op(op, NAME, info); \
227 LogicalResult visitSMTOp(ExtractOp op, VisitorInfo &info) {
228 info.stream <<
"((_ extract "
229 << (op.getLowBit() + op.getType().getWidth() - 1) <<
" "
230 << op.getLowBit() <<
") " << info.valueMap.lookup(op.getInput())
235 LogicalResult visitSMTOp(RepeatOp op, VisitorInfo &info) {
236 info.stream <<
"((_ repeat " << op.getCount() <<
") "
237 << info.valueMap.lookup(op.getInput()) <<
")";
241 LogicalResult visitSMTOp(BVCmpOp op, VisitorInfo &info) {
242 return printBinaryOp(op,
"bv" + stringifyBVCmpPredicate(op.getPred()).str(),
256 LogicalResult visitSMTOp(IntCmpOp op, VisitorInfo &info) {
257 switch (op.getPred()) {
258 case IntPredicate::ge:
259 return printBinaryOp(op,
">=", info);
260 case IntPredicate::le:
261 return printBinaryOp(op,
"<=", info);
262 case IntPredicate::gt:
263 return printBinaryOp(op,
">", info);
264 case IntPredicate::lt:
265 return printBinaryOp(op,
"<", info);
277 LogicalResult visitSMTOp(IteOp op, VisitorInfo &info) {
278 info.stream <<
"(ite " << info.valueMap.lookup(op.getCond()) <<
" "
279 << info.valueMap.lookup(op.getThenValue()) <<
" "
280 << info.valueMap.lookup(op.getElseValue()) <<
")";
284 LogicalResult visitSMTOp(ApplyFuncOp op, VisitorInfo &info) {
285 info.stream <<
"(" << info.valueMap.lookup(op.getFunc());
286 for (
Value arg : op.getArgs())
287 info.stream <<
" " << info.valueMap.lookup(arg);
292 template <
typename OpTy>
293 LogicalResult quantifierHelper(OpTy op, StringRef operatorString,
295 auto weight = op.getWeight();
298 if (op.getNoPattern())
299 return op.emitError() <<
"no-pattern attribute not supported yet";
301 llvm::ScopedHashTableScope<Value, std::string> scope(info.valueMap);
302 info.stream <<
"(" << operatorString <<
" (";
303 StringLiteral delimiter =
"";
310 op.getBoundVarNames()
311 ? cast<StringAttr>(op.getBoundVarNames()->getValue()[i])
314 StringRef name = names.
newName(prefix);
315 argNames.push_back(name);
317 info.valueMap.insert(arg, name.str());
320 info.stream << delimiter <<
"(" << name <<
" ";
321 typeVisitor.dispatchSMTTypeVisitor(arg.getType(), info.stream);
326 info.stream <<
")\n";
332 Value yieldedValue = op.getBody().front().getTerminator()->getOperand(0);
333 worklist.push_back(yieldedValue);
334 unsigned indentExt = operatorString.size() + 2;
335 VisitorInfo newInfo(info.stream, info.valueMap,
336 info.indentLevel + indentExt, 0);
337 if (weight != 0 || !
patterns.empty())
338 newInfo.stream.indent(newInfo.indentLevel);
340 newInfo.stream.indent(info.indentLevel);
342 if (weight != 0 || !
patterns.empty())
343 info.stream <<
"( ! ";
345 if (failed(printExpression(worklist, newInfo)))
348 info.stream << info.valueMap.lookup(yieldedValue);
350 for (
unsigned j = 0;
j < newInfo.openParens; ++
j)
354 info.stream <<
" :weight " << weight;
357 info.stream <<
"\n:pattern (";
365 info.valueMap.insert(arg, argNames[i].str());
370 for (
auto yieldedValue : p.front().getTerminator()->getOperands()) {
372 worklist.push_back(yieldedValue);
373 unsigned indentExt = operatorString.size() + 2;
375 VisitorInfo newInfo2(info.stream, info.valueMap,
376 info.indentLevel + indentExt, 0);
378 info.stream.indent(0);
380 if (failed(printExpression(worklist, newInfo2)))
383 info.stream << info.valueMap.lookup(yieldedValue);
384 for (
unsigned j = 0;
j < newInfo2.openParens; ++
j)
393 if (weight != 0 || !
patterns.empty())
401 LogicalResult visitSMTOp(ForallOp op, VisitorInfo &info) {
402 return quantifierHelper(op,
"forall", info);
405 LogicalResult visitSMTOp(ExistsOp op, VisitorInfo &info) {
406 return quantifierHelper(op,
"exists", info);
409 LogicalResult visitSMTOp(NotOp op, VisitorInfo &info) {
410 info.stream <<
"(not " << info.valueMap.lookup(op.getInput()) <<
")";
423 LogicalResult visitSMTOp(ArrayStoreOp op, VisitorInfo &info) {
424 info.stream <<
"(store " << info.valueMap.lookup(op.getArray()) <<
" "
425 << info.valueMap.lookup(op.getIndex()) <<
" "
426 << info.valueMap.lookup(op.getValue()) <<
")";
430 LogicalResult visitSMTOp(ArraySelectOp op, VisitorInfo &info) {
431 info.stream <<
"(select " << info.valueMap.lookup(op.getArray()) <<
" "
432 << info.valueMap.lookup(op.getIndex()) <<
")";
436 LogicalResult visitSMTOp(ArrayBroadcastOp op, VisitorInfo &info) {
437 info.stream <<
"((as const ";
438 typeVisitor.dispatchSMTTypeVisitor(op.getType(), info.stream);
439 info.stream <<
") " << info.valueMap.lookup(op.getValue()) <<
")";
443 LogicalResult visitUnhandledSMTOp(
Operation *op, VisitorInfo &info) {
453 while (!worklist.empty()) {
454 Value curr = worklist.back();
457 if (info.valueMap.count(curr)) {
464 bool allAvailable =
true;
466 assert(defOp !=
nullptr &&
467 "block arguments must already be in the valueMap");
470 if (!info.valueMap.count(val)) {
471 worklist.push_back(val);
472 allAvailable =
false;
479 if (failed(dispatchSMTOpVisitor(curr.
getDefiningOp(), info)))
491 TypeVisitor typeVisitor;
497 struct StatementVisitor
499 mlir::raw_indented_ostream &, ValueMap &> {
509 valueMap.insert(op.getResult(), op.getValue().getValueAsString());
513 LogicalResult visitSMTOp(BoolConstantOp op,
516 valueMap.insert(op.getResult(), op.getValue() ?
"true" :
"false");
523 op.getValue().toStringSigned(str);
524 valueMap.insert(op.getResult(), str.str().str());
531 names.
newName(op.getNamePrefix() ? *op.getNamePrefix() :
"tmp");
532 valueMap.insert(op.getResult(), name.str());
534 << (isa<SMTFuncType>(op.getType()) ?
"declare-fun "
537 typeVisitor.dispatchSMTTypeVisitor(op.getType(), stream);
544 llvm::ScopedHashTableScope<Value, std::string> scope1(valueMap);
546 worklist.push_back(op.getInput());
547 stream <<
"(assert ";
548 VisitorInfo info(stream, valueMap, 8, 0);
549 if (failed(exprVisitor.printExpression(worklist, info)))
551 stream << valueMap.lookup(op.getInput());
552 for (
unsigned i = 0; i < info.openParens + 1; ++i)
561 stream <<
"(reset)\n";
567 stream <<
"(push " << op.getCount() <<
")\n";
573 stream <<
"(pop " << op.getCount() <<
")\n";
579 if (op->getNumResults() != 0)
580 return op.emitError() <<
"must not have any result values";
582 if (op.getSatRegion().front().getOperations().size() != 1)
583 return op->emitError() <<
"'sat' region must be empty";
584 if (op.getUnknownRegion().front().getOperations().size() != 1)
585 return op->emitError() <<
"'unknown' region must be empty";
586 if (op.getUnsatRegion().front().getOperations().size() != 1)
587 return op->emitError() <<
"'unsat' region must be empty";
589 stream <<
"(check-sat)\n";
595 stream <<
"(set-logic " << op.getLogic() <<
")\n";
599 LogicalResult visitUnhandledSMTOp(
Operation *op,
603 if (isa<smt::Int2BVOp, BV2IntOp>(op))
604 return op->
emitError(
"operation not supported for SMTLIB emission");
612 TypeVisitor typeVisitor;
614 ExpressionVisitor exprVisitor;
626 if (!solver.getInputs().empty() || solver->getNumResults() != 0)
627 return solver->emitError()
628 <<
"solver scopes with inputs or results are not supported";
630 Block *block = solver.getBody();
637 <<
"solver must not contain any non-SMT operations";
640 auto sortTy = dyn_cast<SortType>(resTy);
644 unsigned arity = sortTy.getSortParams().size();
645 if (declaredSorts.contains(sortTy.getIdentifier())) {
646 if (declaredSorts[sortTy.getIdentifier()] != arity)
647 return op->emitError(
"uninterpreted sorts with same identifier but "
648 "different arity found");
653 declaredSorts[sortTy.getIdentifier()] = arity;
654 stream <<
"(declare-sort " << sortTy.getIdentifier().getValue() <<
" "
659 if (result.wasInterrupted())
663 llvm::ScopedHashTableScope<Value, std::string> scope0(valueMap);
665 StatementVisitor visitor(
options, names);
669 result = block->walk([&](
Operation *op) {
670 if (failed(visitor.dispatchSMTOpVisitor(op, stream, valueMap)))
674 if (result.wasInterrupted())
677 stream <<
"(reset)\n";
684 return module->
emitError(
"must have exactly one region");
686 return module->
emitError(
"op region must have exactly one block");
689 unsigned solverIdx = 0;
690 auto result = module->
walk([&](SolverOp solver) {
691 ios <<
"; solver scope " << solverIdx <<
"\n";
698 return failure(result.wasInterrupted());
706 static llvm::cl::opt<bool> inlineSingleUseValues(
707 "smtlibexport-inline-single-use-values",
708 llvm::cl::desc(
"Inline expressions that are used only once rather than "
709 "generating a let-binding"),
710 llvm::cl::init(
false));
712 auto getOptions = [] {
719 "export-smtlib",
"export SMT-LIB",
720 [=](
Operation *module, raw_ostream &output) {
726 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.