MLIR 22.0.0git
ExportSMTLIB.cpp
Go to the documentation of this file.
1//===- ExportSMTLIB.cpp - SMT-LIB Emitter -----=---------------------------===//
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// This is the main SMT-LIB emitter implementation.
10//
11//===----------------------------------------------------------------------===//
12
14
22#include "llvm/ADT/ScopedHashTable.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/Support/raw_ostream.h"
25
26using namespace mlir;
27using namespace smt;
28
29using ValueMap = llvm::ScopedHashTable<mlir::Value, std::string>;
30
31#define DEBUG_TYPE "export-smtlib"
32
33namespace {
34
35/// A visitor to print the SMT dialect types as SMT-LIB formatted sorts.
36/// Printing nested types use recursive calls since nestings of a depth that
37/// could lead to problems should not occur in practice.
38struct TypeVisitor : public smt::SMTTypeVisitor<TypeVisitor, void,
39 mlir::raw_indented_ostream &> {
40 TypeVisitor(const SMTEmissionOptions &options) : options(options) {}
41
42 void visitSMTType(BoolType type, mlir::raw_indented_ostream &stream) {
43 stream << "Bool";
44 }
45
46 void visitSMTType(IntType type, mlir::raw_indented_ostream &stream) {
47 stream << "Int";
48 }
49
50 void visitSMTType(BitVectorType type, mlir::raw_indented_ostream &stream) {
51 stream << "(_ BitVec " << type.getWidth() << ")";
52 }
53
54 void visitSMTType(ArrayType type, mlir::raw_indented_ostream &stream) {
55 stream << "(Array ";
56 dispatchSMTTypeVisitor(type.getDomainType(), stream);
57 stream << " ";
58 dispatchSMTTypeVisitor(type.getRangeType(), stream);
59 stream << ")";
60 }
61
62 void visitSMTType(SMTFuncType type, mlir::raw_indented_ostream &stream) {
63 stream << "(";
64 StringLiteral nextToken = "";
65
66 for (Type domainTy : type.getDomainTypes()) {
67 stream << nextToken;
68 dispatchSMTTypeVisitor(domainTy, stream);
69 nextToken = " ";
70 }
71
72 stream << ") ";
73 dispatchSMTTypeVisitor(type.getRangeType(), stream);
74 }
75
76 void visitSMTType(SortType type, mlir::raw_indented_ostream &stream) {
77 if (!type.getSortParams().empty())
78 stream << "(";
79
80 stream << type.getIdentifier().getValue();
81 for (Type paramTy : type.getSortParams()) {
82 stream << " ";
83 dispatchSMTTypeVisitor(paramTy, stream);
84 }
85
86 if (!type.getSortParams().empty())
87 stream << ")";
88 }
89
90private:
91 // A reference to the emission options for easy use in the visitor methods.
92 [[maybe_unused]] const SMTEmissionOptions &options;
93};
94
95/// Contains the informations passed to the ExpressionVisitor methods. Makes it
96/// easier to add more information.
97struct VisitorInfo {
98 VisitorInfo(mlir::raw_indented_ostream &stream, ValueMap &valueMap)
99 : stream(stream), valueMap(valueMap) {}
100 VisitorInfo(mlir::raw_indented_ostream &stream, ValueMap &valueMap,
101 unsigned indentLevel, unsigned openParens)
102 : stream(stream), valueMap(valueMap), indentLevel(indentLevel),
103 openParens(openParens) {}
104
105 // Stream to print to.
107 // Mapping from SSA values to SMT-LIB expressions.
108 ValueMap &valueMap;
109 // Total number of spaces currently indented.
110 unsigned indentLevel = 0;
111 // Number of parentheses that have been opened but not closed yet.
112 unsigned openParens = 0;
113};
114
115/// A visitor to print SMT dialect operations with exactly one result value as
116/// the equivalent operator in SMT-LIB.
117struct ExpressionVisitor
118 : public smt::SMTOpVisitor<ExpressionVisitor, LogicalResult,
119 VisitorInfo &> {
120 using Base =
122 using Base::visitSMTOp;
123
124 ExpressionVisitor(const SMTEmissionOptions &options, Namespace &names)
125 : options(options), typeVisitor(options), names(names) {}
126
127 LogicalResult dispatchSMTOpVisitor(Operation *op, VisitorInfo &info) {
128 assert(op->getNumResults() == 1 &&
129 "expression op must have exactly one result value");
130
131 // Print the expression inlined if it is only used once and the
132 // corresponding emission option is enabled. This can lead to bad
133 // performance for big inputs since the inlined expression is stored as a
134 // string in the value mapping where otherwise only the symbol names of free
135 // and bound variables are stored, and due to a lot of string concatenation
136 // (thus it's off by default and just intended to print small examples in a
137 // more human-readable format).
138 Value res = op->getResult(0);
139 if (res.hasOneUse() && options.inlineSingleUseValues) {
140 std::string str;
141 llvm::raw_string_ostream sstream(str);
142 mlir::raw_indented_ostream indentedStream(sstream);
143
144 VisitorInfo newInfo(indentedStream, info.valueMap, info.indentLevel,
145 info.openParens);
146 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
147 return failure();
148
149 info.valueMap.insert(res, str);
150 return success();
151 }
152
153 // Generate a let binding for the current expression being processed and
154 // store the sybmol in the value map. Indent the expressions for easier
155 // readability.
156 auto name = names.newName("tmp");
157 info.valueMap.insert(res, name.str());
158 info.stream << "(let ((" << name << " ";
159
160 VisitorInfo newInfo(info.stream, info.valueMap,
161 info.indentLevel + 8 + name.size(), 0);
162 if (failed(Base::dispatchSMTOpVisitor(op, newInfo)))
163 return failure();
164
165 info.stream << "))\n";
166
167 if (options.indentLetBody) {
168 // Five spaces to align with the opening parenthesis
169 info.indentLevel += 5;
170 }
171 ++info.openParens;
172 info.stream.indent(info.indentLevel);
173
174 return success();
175 }
176
177 //===--------------------------------------------------------------------===//
178 // Bit-vector theory operation visitors
179 //===--------------------------------------------------------------------===//
180
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()) << ")";
185 return success();
186 }
187
188 template <typename Op>
189 LogicalResult printVariadicOp(Op op, StringRef name, VisitorInfo &info) {
190 info.stream << "(" << name;
191 for (Value val : op.getOperands())
192 info.stream << " " << info.valueMap.lookup(val);
193 info.stream << ")";
194 return success();
195 }
196
197 LogicalResult visitSMTOp(BVNegOp op, VisitorInfo &info) {
198 info.stream << "(bvneg " << info.valueMap.lookup(op.getInput()) << ")";
199 return success();
200 }
201
202 LogicalResult visitSMTOp(BVNotOp op, VisitorInfo &info) {
203 info.stream << "(bvnot " << info.valueMap.lookup(op.getInput()) << ")";
204 return success();
205 }
206
207#define HANDLE_OP(OPTYPE, NAME, KIND) \
208 LogicalResult visitSMTOp(OPTYPE op, VisitorInfo &info) { \
209 return print##KIND##Op(op, NAME, info); \
210 }
211
212 HANDLE_OP(BVAddOp, "bvadd", Binary);
213 HANDLE_OP(BVMulOp, "bvmul", Binary);
214 HANDLE_OP(BVURemOp, "bvurem", Binary);
215 HANDLE_OP(BVSRemOp, "bvsrem", Binary);
216 HANDLE_OP(BVSModOp, "bvsmod", Binary);
217 HANDLE_OP(BVShlOp, "bvshl", Binary);
218 HANDLE_OP(BVLShrOp, "bvlshr", Binary);
219 HANDLE_OP(BVAShrOp, "bvashr", Binary);
220 HANDLE_OP(BVUDivOp, "bvudiv", Binary);
221 HANDLE_OP(BVSDivOp, "bvsdiv", Binary);
222 HANDLE_OP(BVAndOp, "bvand", Binary);
223 HANDLE_OP(BVOrOp, "bvor", Binary);
224 HANDLE_OP(BVXOrOp, "bvxor", Binary);
225 HANDLE_OP(ConcatOp, "concat", Binary);
226
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())
231 << ")";
232 return success();
233 }
234
235 LogicalResult visitSMTOp(RepeatOp op, VisitorInfo &info) {
236 info.stream << "((_ repeat " << op.getCount() << ") "
237 << info.valueMap.lookup(op.getInput()) << ")";
238 return success();
239 }
240
241 LogicalResult visitSMTOp(BVCmpOp op, VisitorInfo &info) {
242 return printBinaryOp(op, "bv" + stringifyBVCmpPredicate(op.getPred()).str(),
243 info);
244 }
245
246 //===--------------------------------------------------------------------===//
247 // Int theory operation visitors
248 //===--------------------------------------------------------------------===//
249
250 HANDLE_OP(IntAddOp, "+", Variadic);
251 HANDLE_OP(IntMulOp, "*", Variadic);
252 HANDLE_OP(IntSubOp, "-", Binary);
253 HANDLE_OP(IntDivOp, "div", Binary);
254 HANDLE_OP(IntModOp, "mod", Binary);
255
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);
266 }
267 return failure();
268 }
269
270 //===--------------------------------------------------------------------===//
271 // Core theory operation visitors
272 //===--------------------------------------------------------------------===//
273
274 HANDLE_OP(EqOp, "=", Variadic);
275 HANDLE_OP(DistinctOp, "distinct", Variadic);
276
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()) << ")";
281 return success();
282 }
283
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);
288 info.stream << ")";
289 return success();
290 }
291
292 template <typename OpTy>
293 LogicalResult quantifierHelper(OpTy op, StringRef operatorString,
294 VisitorInfo &info) {
295 auto weight = op.getWeight();
296 auto patterns = op.getPatterns();
297 // TODO: add support
298 if (op.getNoPattern())
299 return op.emitError() << "no-pattern attribute not supported yet";
300
301 llvm::ScopedHashTableScope<Value, std::string> scope(info.valueMap);
302 info.stream << "(" << operatorString << " (";
303 StringLiteral delimiter = "";
304
305 SmallVector<StringRef> argNames;
306
307 for (auto [i, arg] : llvm::enumerate(op.getBody().getArguments())) {
308 // Generate and register a new unique name.
309 StringRef prefix =
310 op.getBoundVarNames()
311 ? cast<StringAttr>(op.getBoundVarNames()->getValue()[i])
312 .getValue()
313 : "tmp";
314 StringRef name = names.newName(prefix);
315 argNames.push_back(name);
316
317 info.valueMap.insert(arg, name.str());
318
319 // Print the bound variable declaration.
320 info.stream << delimiter << "(" << name << " ";
321 typeVisitor.dispatchSMTTypeVisitor(arg.getType(), info.stream);
322 info.stream << ")";
323 delimiter = " ";
324 }
325
326 info.stream << ")\n";
327
328 // Print the quantifier body. This assumes that quantifiers are not deeply
329 // nested (at least not enough that recursive calls could become a problem).
330
331 SmallVector<Value> worklist;
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);
339 else
340 newInfo.stream.indent(info.indentLevel);
341
342 if (weight != 0 || !patterns.empty())
343 info.stream << "( ! ";
344
345 if (failed(printExpression(worklist, newInfo)))
346 return failure();
347
348 info.stream << info.valueMap.lookup(yieldedValue);
349
350 for (unsigned j = 0; j < newInfo.openParens; ++j)
351 info.stream << ")";
352
353 if (weight != 0)
354 info.stream << " :weight " << weight;
355 if (!patterns.empty()) {
356 bool first = true;
357 info.stream << "\n:pattern (";
358 for (auto &p : patterns) {
359
360 if (!first)
361 info.stream << " ";
362
363 // retrieve argument name from the body region
364 for (auto [i, arg] : llvm::enumerate(p.getArguments()))
365 info.valueMap.insert(arg, argNames[i].str());
366
367 SmallVector<Value> worklist;
368
369 // retrieve all yielded operands in pattern region
370 for (auto yieldedValue : p.front().getTerminator()->getOperands()) {
371
372 worklist.push_back(yieldedValue);
373 unsigned indentExt = operatorString.size() + 2;
374
375 VisitorInfo newInfo2(info.stream, info.valueMap,
376 info.indentLevel + indentExt, 0);
377
378 info.stream.indent(0);
379
380 if (failed(printExpression(worklist, newInfo2)))
381 return failure();
382
383 info.stream << info.valueMap.lookup(yieldedValue);
384 for (unsigned j = 0; j < newInfo2.openParens; ++j)
385 info.stream << ")";
386 }
387
388 first = false;
389 }
390 info.stream << ")";
391 }
392
393 if (weight != 0 || !patterns.empty())
394 info.stream << ")";
395
396 info.stream << ")";
397
398 return success();
399 }
400
401 LogicalResult visitSMTOp(ForallOp op, VisitorInfo &info) {
402 return quantifierHelper(op, "forall", info);
403 }
404
405 LogicalResult visitSMTOp(ExistsOp op, VisitorInfo &info) {
406 return quantifierHelper(op, "exists", info);
407 }
408
409 LogicalResult visitSMTOp(NotOp op, VisitorInfo &info) {
410 info.stream << "(not " << info.valueMap.lookup(op.getInput()) << ")";
411 return success();
412 }
413
414 HANDLE_OP(AndOp, "and", Variadic);
415 HANDLE_OP(OrOp, "or", Variadic);
416 HANDLE_OP(XOrOp, "xor", Variadic);
417 HANDLE_OP(ImpliesOp, "=>", Binary);
418
419 //===--------------------------------------------------------------------===//
420 // Array theory operation visitors
421 //===--------------------------------------------------------------------===//
422
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()) << ")";
427 return success();
428 }
429
430 LogicalResult visitSMTOp(ArraySelectOp op, VisitorInfo &info) {
431 info.stream << "(select " << info.valueMap.lookup(op.getArray()) << " "
432 << info.valueMap.lookup(op.getIndex()) << ")";
433 return success();
434 }
435
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()) << ")";
440 return success();
441 }
442
443 LogicalResult visitUnhandledSMTOp(Operation *op, VisitorInfo &info) {
444 return success();
445 }
446
447#undef HANDLE_OP
448
449 /// Print an expression transitively. The root node should be added to the
450 /// 'worklist' before calling.
451 LogicalResult printExpression(SmallVector<Value> &worklist,
452 VisitorInfo &info) {
453 while (!worklist.empty()) {
454 Value curr = worklist.back();
455
456 // If we already have a let-binding for the value, just print it.
457 if (info.valueMap.count(curr)) {
458 worklist.pop_back();
459 continue;
460 }
461
462 // Traverse until we reach a value/operation that has all operands
463 // available and can thus be printed.
464 bool allAvailable = true;
465 Operation *defOp = curr.getDefiningOp();
466 assert(defOp != nullptr &&
467 "block arguments must already be in the valueMap");
468
469 for (Value val : defOp->getOperands()) {
470 if (!info.valueMap.count(val)) {
471 worklist.push_back(val);
472 allAvailable = false;
473 }
474 }
475
476 if (!allAvailable)
477 continue;
478
479 if (failed(dispatchSMTOpVisitor(curr.getDefiningOp(), info)))
480 return failure();
481
482 worklist.pop_back();
483 }
484
485 return success();
486 }
487
488private:
489 // A reference to the emission options for easy use in the visitor methods.
490 [[maybe_unused]] const SMTEmissionOptions &options;
491 TypeVisitor typeVisitor;
492 Namespace &names;
493};
494
495/// A visitor to print SMT dialect operations with zero result values or
496/// ones that have to initialize some global state.
497struct StatementVisitor
498 : public smt::SMTOpVisitor<StatementVisitor, LogicalResult,
499 mlir::raw_indented_ostream &, ValueMap &> {
500 using smt::SMTOpVisitor<StatementVisitor, LogicalResult,
501 mlir::raw_indented_ostream &, ValueMap &>::visitSMTOp;
502
503 StatementVisitor(const SMTEmissionOptions &options, Namespace &names)
504 : options(options), typeVisitor(options), names(names),
505 exprVisitor(options, names) {}
506
507 LogicalResult visitSMTOp(BVConstantOp op, mlir::raw_indented_ostream &stream,
508 ValueMap &valueMap) {
509 valueMap.insert(op.getResult(), op.getValue().getValueAsString());
510 return success();
511 }
512
513 LogicalResult visitSMTOp(BoolConstantOp op,
514 mlir::raw_indented_ostream &stream,
515 ValueMap &valueMap) {
516 valueMap.insert(op.getResult(), op.getValue() ? "true" : "false");
517 return success();
518 }
519
520 LogicalResult visitSMTOp(IntConstantOp op, mlir::raw_indented_ostream &stream,
521 ValueMap &valueMap) {
522 SmallString<16> str;
523 op.getValue().toStringSigned(str);
524 valueMap.insert(op.getResult(), str.str().str());
525 return success();
526 }
527
528 LogicalResult visitSMTOp(DeclareFunOp op, mlir::raw_indented_ostream &stream,
529 ValueMap &valueMap) {
530 StringRef name =
531 names.newName(op.getNamePrefix() ? *op.getNamePrefix() : "tmp");
532 valueMap.insert(op.getResult(), name.str());
533 stream << "("
534 << (isa<SMTFuncType>(op.getType()) ? "declare-fun "
535 : "declare-const ")
536 << name << " ";
537 typeVisitor.dispatchSMTTypeVisitor(op.getType(), stream);
538 stream << ")\n";
539 return success();
540 }
541
542 LogicalResult visitSMTOp(AssertOp op, mlir::raw_indented_ostream &stream,
543 ValueMap &valueMap) {
544 llvm::ScopedHashTableScope<Value, std::string> scope1(valueMap);
545 SmallVector<Value> worklist;
546 worklist.push_back(op.getInput());
547 stream << "(assert ";
548 VisitorInfo info(stream, valueMap, 8, 0);
549 if (failed(exprVisitor.printExpression(worklist, info)))
550 return failure();
551 stream << valueMap.lookup(op.getInput());
552 for (unsigned i = 0; i < info.openParens + 1; ++i)
553 stream << ")";
554 stream << "\n";
555 stream.indent(0);
556 return success();
557 }
558
559 LogicalResult visitSMTOp(ResetOp op, mlir::raw_indented_ostream &stream,
560 ValueMap &valueMap) {
561 stream << "(reset)\n";
562 return success();
563 }
564
565 LogicalResult visitSMTOp(PushOp op, mlir::raw_indented_ostream &stream,
566 ValueMap &valueMap) {
567 stream << "(push " << op.getCount() << ")\n";
568 return success();
569 }
570
571 LogicalResult visitSMTOp(PopOp op, mlir::raw_indented_ostream &stream,
572 ValueMap &valueMap) {
573 stream << "(pop " << op.getCount() << ")\n";
574 return success();
575 }
576
577 LogicalResult visitSMTOp(CheckOp op, mlir::raw_indented_ostream &stream,
578 ValueMap &valueMap) {
579 if (op->getNumResults() != 0)
580 return op.emitError() << "must not have any result values";
581
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";
588
589 stream << "(check-sat)\n";
590 return success();
591 }
592
593 LogicalResult visitSMTOp(SetLogicOp op, mlir::raw_indented_ostream &stream,
594 ValueMap &valueMap) {
595 stream << "(set-logic " << op.getLogic() << ")\n";
596 return success();
597 }
598
599 LogicalResult visitUnhandledSMTOp(Operation *op,
600 mlir::raw_indented_ostream &stream,
601 ValueMap &valueMap) {
602 // Ignore operations which are handled in the Expression Visitor.
603 if (isa<smt::Int2BVOp, BV2IntOp>(op))
604 return op->emitError("operation not supported for SMTLIB emission");
605
606 return success();
607 }
608
609private:
610 // A reference to the emission options for easy use in the visitor methods.
611 [[maybe_unused]] const SMTEmissionOptions &options;
612 TypeVisitor typeVisitor;
613 Namespace &names;
614 ExpressionVisitor exprVisitor;
615};
616
617} // namespace
618
619//===----------------------------------------------------------------------===//
620// Unified Emitter implementation
621//===----------------------------------------------------------------------===//
622
623/// Emit the SMT operations in the given 'solver' to the 'stream'.
624static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options,
626 if (!solver.getInputs().empty() || solver->getNumResults() != 0)
627 return solver->emitError()
628 << "solver scopes with inputs or results are not supported";
629
630 Block *block = solver.getBody();
631
632 // Declare uninterpreted sorts.
633 DenseMap<StringAttr, unsigned> declaredSorts;
634 auto result = block->walk([&](Operation *op) -> WalkResult {
635 if (!isa<SMTDialect>(op->getDialect()))
636 return op->emitError()
637 << "solver must not contain any non-SMT operations";
638
639 for (Type resTy : op->getResultTypes()) {
640 auto sortTy = dyn_cast<SortType>(resTy);
641 if (!sortTy)
642 continue;
643
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");
649
650 continue;
651 }
652
653 declaredSorts[sortTy.getIdentifier()] = arity;
654 stream << "(declare-sort " << sortTy.getIdentifier().getValue() << " "
655 << arity << ")\n";
656 }
657 return WalkResult::advance();
658 });
659 if (result.wasInterrupted())
660 return failure();
661
662 ValueMap valueMap;
663 llvm::ScopedHashTableScope<Value, std::string> scope0(valueMap);
664 Namespace names;
665 StatementVisitor visitor(options, names);
666
667 // Collect all statement operations (ops with no result value).
668 // Declare constants and then only refer to them by identifier later on.
669 result = block->walk([&](Operation *op) {
670 if (failed(visitor.dispatchSMTOpVisitor(op, stream, valueMap)))
671 return WalkResult::interrupt();
672 return WalkResult::advance();
673 });
674 if (result.wasInterrupted())
675 return failure();
676
677 stream << "(reset)\n";
678 return success();
679}
680
681LogicalResult smt::exportSMTLIB(Operation *module, llvm::raw_ostream &os,
683 if (module->getNumRegions() != 1)
684 return module->emitError("must have exactly one region");
685 if (!module->getRegion(0).hasOneBlock())
686 return module->emitError("op region must have exactly one block");
687
689 unsigned solverIdx = 0;
690 auto result = module->walk([&](SolverOp solver) {
691 ios << "; solver scope " << solverIdx << "\n";
692 if (failed(emit(solver, options, ios)))
693 return WalkResult::interrupt();
694 ++solverIdx;
695 return WalkResult::advance();
696 });
697
698 return failure(result.wasInterrupted());
699}
700
701//===----------------------------------------------------------------------===//
702// mlir-translate registration
703//===----------------------------------------------------------------------===//
704
705void smt::registerExportSMTLIBTranslation() {
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));
711
712 auto getOptions = [] {
713 SMTEmissionOptions opts;
714 opts.inlineSingleUseValues = inlineSingleUseValues;
715 return opts;
716 };
717
718 static mlir::TranslateFromMLIRRegistration toSMTLIB(
719 "export-smtlib", "export SMT-LIB",
720 [=](Operation *module, raw_ostream &output) {
721 return smt::exportSMTLIB(module, output, getOptions());
722 },
723 [](mlir::DialectRegistry &registry) {
724 // Register the 'func' and 'HW' dialects to support printing solver
725 // scopes nested in functions and modules.
726 registry.insert<mlir::func::FuncDialect, arith::ArithDialect,
727 smt::SMTDialect>();
728 });
729}
return success()
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.
Definition Block.h:33
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
Definition Block.h:308
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:29
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.
Definition Namespace.h:86
This provides public APIs that all operations should have.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:220
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:686
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:407
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:674
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
result_type_range getResultTypes()
Definition Operation.h:428
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:378
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:404
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
bool hasOneUse() const
Returns true if this value has exactly one use.
Definition Value.h:197
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
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.
Definition SMTVisitors.h:25
LogicalResult dispatchSMTOpVisitor(Operation *op, ExtraArgs... args)
Definition SMTVisitors.h:27
This helps visit SMT types.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:561
LogicalResult exportSMTLIB(Operation *module, llvm::raw_ostream &os, const SMTEmissionOptions &options=SMTEmissionOptions())
Run the ExportSMTLIB pass.
Include the generated interface declarations.
const FrozenRewritePatternSet & patterns
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
Emission options for the ExportSMTLIB pass.