MLIR 24.0.0git
SMTOps.cpp
Go to the documentation of this file.
1//===- SMTOps.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
10#include "mlir/IR/Builders.h"
12#include "llvm/ADT/APSInt.h"
13
14using namespace mlir;
15using namespace smt;
16using namespace mlir;
17
18//===----------------------------------------------------------------------===//
19// BVConstantOp
20//===----------------------------------------------------------------------===//
21
22LogicalResult BVConstantOp::inferReturnTypes(
23 mlir::MLIRContext *context, std::optional<mlir::Location> location,
24 ::mlir::ValueRange operands, ::mlir::DictionaryAttr attributes,
25 ::mlir::PropertyRef properties, ::mlir::RegionRange regions,
26 ::llvm::SmallVectorImpl<::mlir::Type> &inferredReturnTypes) {
27 inferredReturnTypes.push_back(
28 properties.as<Properties *>()->getValue().getType());
29 return success();
30}
31
32void BVConstantOp::getAsmResultNames(
33 function_ref<void(Value, StringRef)> setNameFn) {
34 SmallVector<char, 128> specialNameBuffer;
35 llvm::raw_svector_ostream specialName(specialNameBuffer);
36 specialName << "c" << getValue().getValue() << "_bv"
37 << getValue().getValue().getBitWidth();
38 setNameFn(getResult(), specialName.str());
39}
40
41OpFoldResult BVConstantOp::fold(FoldAdaptor adaptor) {
42 assert(adaptor.getOperands().empty() && "constant has no operands");
43 return getValueAttr();
44}
45
46//===----------------------------------------------------------------------===//
47// DeclareFunOp
48//===----------------------------------------------------------------------===//
49
50void DeclareFunOp::getAsmResultNames(
51 function_ref<void(Value, StringRef)> setNameFn) {
52 setNameFn(getResult(), getNamePrefix().has_value() ? *getNamePrefix() : "");
53}
54
55//===----------------------------------------------------------------------===//
56// SolverOp
57//===----------------------------------------------------------------------===//
58
59LogicalResult SolverOp::verifyRegions() {
60 if (getBody()->getTerminator()->getOperands().getTypes() != getResultTypes())
61 return emitOpError() << "types of yielded values must match return values";
62 if (getBody()->getArgumentTypes() != getInputs().getTypes())
63 return emitOpError()
64 << "block argument types must match the types of the 'inputs'";
65
66 return success();
67}
68
69//===----------------------------------------------------------------------===//
70// CheckOp
71//===----------------------------------------------------------------------===//
72
73LogicalResult CheckOp::verifyRegions() {
74 if (getSatRegion().front().getTerminator()->getOperands().getTypes() !=
75 getResultTypes())
76 return emitOpError() << "types of yielded values in 'sat' region must "
77 "match return values";
78 if (getUnknownRegion().front().getTerminator()->getOperands().getTypes() !=
79 getResultTypes())
80 return emitOpError() << "types of yielded values in 'unknown' region must "
81 "match return values";
82 if (getUnsatRegion().front().getTerminator()->getOperands().getTypes() !=
83 getResultTypes())
84 return emitOpError() << "types of yielded values in 'unsat' region must "
85 "match return values";
86
87 return success();
88}
89
90//===----------------------------------------------------------------------===//
91// EqOp
92//===----------------------------------------------------------------------===//
93
94static LogicalResult
98 SMLoc loc = parser.getCurrentLocation();
99 Type type;
100
101 if (parser.parseOperandList(inputs) ||
102 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
103 parser.parseType(type))
104 return failure();
105
106 result.addTypes(BoolType::get(parser.getContext()));
107 if (parser.resolveOperands(inputs, SmallVector<Type>(inputs.size(), type),
108 loc, result.operands))
109 return failure();
110
111 return success();
112}
113
114ParseResult EqOp::parse(OpAsmParser &parser, OperationState &result) {
116}
117
118void EqOp::print(OpAsmPrinter &printer) {
119 printer << ' ' << getInputs();
120 printer.printOptionalAttrDict(
121 getOperation()->getDiscardableAttrDictionary().getValue());
122 printer << " : " << getInputs().front().getType();
123}
124
125LogicalResult EqOp::verify() {
126 if (getInputs().size() < 2)
127 return emitOpError() << "'inputs' must have at least size 2, but got "
128 << getInputs().size();
129
130 return success();
131}
132
133//===----------------------------------------------------------------------===//
134// DistinctOp
135//===----------------------------------------------------------------------===//
136
137ParseResult DistinctOp::parse(OpAsmParser &parser, OperationState &result) {
139}
140
141void DistinctOp::print(OpAsmPrinter &printer) {
142 printer << ' ' << getInputs();
143 printer.printOptionalAttrDict(
144 getOperation()->getDiscardableAttrDictionary().getValue());
145 printer << " : " << getInputs().front().getType();
146}
147
148LogicalResult DistinctOp::verify() {
149 if (getInputs().size() < 2)
150 return emitOpError() << "'inputs' must have at least size 2, but got "
151 << getInputs().size();
152
153 return success();
154}
155
156//===----------------------------------------------------------------------===//
157// ExtractOp
158//===----------------------------------------------------------------------===//
159
160LogicalResult ExtractOp::verify() {
161 unsigned rangeWidth = getType().getWidth();
162 unsigned inputWidth = cast<BitVectorType>(getInput().getType()).getWidth();
163 if (getLowBit() + rangeWidth > inputWidth)
164 return emitOpError("range to be extracted is too big, expected range "
165 "starting at index ")
166 << getLowBit() << " of length " << rangeWidth
167 << " requires input width of at least " << (getLowBit() + rangeWidth)
168 << ", but the input width is only " << inputWidth;
169 return success();
170}
171
172//===----------------------------------------------------------------------===//
173// ConcatOp
174//===----------------------------------------------------------------------===//
175
176LogicalResult ConcatOp::inferReturnTypes(
177 MLIRContext *context, std::optional<Location> location, ValueRange operands,
178 DictionaryAttr attributes, PropertyRef properties, RegionRange regions,
179 SmallVectorImpl<Type> &inferredReturnTypes) {
180 inferredReturnTypes.push_back(BitVectorType::get(
181 context, cast<BitVectorType>(operands[0].getType()).getWidth() +
182 cast<BitVectorType>(operands[1].getType()).getWidth()));
183 return success();
184}
185
186//===----------------------------------------------------------------------===//
187// RepeatOp
188//===----------------------------------------------------------------------===//
189
190LogicalResult RepeatOp::verify() {
191 unsigned inputWidth = cast<BitVectorType>(getInput().getType()).getWidth();
192 unsigned resultWidth = getType().getWidth();
193 if (resultWidth % inputWidth != 0)
194 return emitOpError() << "result bit-vector width must be a multiple of the "
195 "input bit-vector width";
196
197 return success();
198}
199
200unsigned RepeatOp::getCount() {
201 unsigned inputWidth = cast<BitVectorType>(getInput().getType()).getWidth();
202 unsigned resultWidth = getType().getWidth();
203 return resultWidth / inputWidth;
204}
205
206void RepeatOp::build(OpBuilder &builder, OperationState &state, unsigned count,
207 Value input) {
208 unsigned inputWidth = cast<BitVectorType>(input.getType()).getWidth();
209 Type resultTy = BitVectorType::get(builder.getContext(), inputWidth * count);
210 build(builder, state, resultTy, input);
211}
212
213ParseResult RepeatOp::parse(OpAsmParser &parser, OperationState &result) {
215 Type inputType;
216 llvm::SMLoc countLoc = parser.getCurrentLocation();
217
218 APInt count;
219 if (parser.parseInteger(count) || parser.parseKeyword("times"))
220 return failure();
221
222 if (count.isNonPositive())
223 return parser.emitError(countLoc) << "integer must be positive";
224
225 llvm::SMLoc inputLoc = parser.getCurrentLocation();
226 if (parser.parseOperand(input) ||
227 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
228 parser.parseType(inputType))
229 return failure();
230
231 if (parser.resolveOperand(input, inputType, result.operands))
232 return failure();
233
234 auto bvInputTy = dyn_cast<BitVectorType>(inputType);
235 if (!bvInputTy)
236 return parser.emitError(inputLoc) << "input must have bit-vector type";
237
238 // Make sure no assertions can trigger and no silent overflows can happen
239 // Bit-width is stored as 'int64_t' parameter in 'BitVectorType'
240 const unsigned maxBw = 63;
241 if (count.getActiveBits() > maxBw)
242 return parser.emitError(countLoc)
243 << "integer must fit into " << maxBw << " bits";
244
245 // Store multiplication in an APInt twice the size to not have any overflow
246 // and check if it can be truncated to 'maxBw' bits without cutting of
247 // important bits.
248 APInt resultBw = bvInputTy.getWidth() * count.zext(2 * maxBw);
249 if (resultBw.getActiveBits() > maxBw)
250 return parser.emitError(countLoc)
251 << "result bit-width (provided integer times bit-width of the input "
252 "type) must fit into "
253 << maxBw << " bits";
254
255 Type resultTy =
256 BitVectorType::get(parser.getContext(), resultBw.getZExtValue());
257 result.addTypes(resultTy);
258 return success();
259}
260
261void RepeatOp::print(OpAsmPrinter &printer) {
262 printer << " " << getCount() << " times " << getInput();
263 printer.printOptionalAttrDict(
264 (*this)->getDiscardableAttrDictionary().getValue());
265 printer << " : " << getInput().getType();
266}
267
268//===----------------------------------------------------------------------===//
269// BoolConstantOp
270//===----------------------------------------------------------------------===//
271
272void BoolConstantOp::getAsmResultNames(
273 function_ref<void(Value, StringRef)> setNameFn) {
274 setNameFn(getResult(), getValue() ? "true" : "false");
275}
276
277OpFoldResult BoolConstantOp::fold(FoldAdaptor adaptor) {
278 assert(adaptor.getOperands().empty() && "constant has no operands");
279 return getValueAttr();
280}
281
282//===----------------------------------------------------------------------===//
283// IntConstantOp
284//===----------------------------------------------------------------------===//
285
286void IntConstantOp::getAsmResultNames(
287 function_ref<void(Value, StringRef)> setNameFn) {
288 SmallVector<char, 32> specialNameBuffer;
289 llvm::raw_svector_ostream specialName(specialNameBuffer);
290 specialName << "c" << getValue();
291 setNameFn(getResult(), specialName.str());
292}
293
294OpFoldResult IntConstantOp::fold(FoldAdaptor adaptor) {
295 assert(adaptor.getOperands().empty() && "constant has no operands");
296 return getValueAttr();
297}
298
299void IntConstantOp::print(OpAsmPrinter &p) {
300 p << " " << getValue();
301 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
302}
303
304ParseResult IntConstantOp::parse(OpAsmParser &parser, OperationState &result) {
305 APInt value;
306 if (parser.parseInteger(value))
307 return failure();
308
309 result.getOrAddProperties<Properties>().setValue(
310 IntegerAttr::get(parser.getContext(), APSInt(value)));
311
312 if (parser.parseOptionalAttrDict(result.attributes))
313 return failure();
314
315 result.addTypes(smt::IntType::get(parser.getContext()));
316 return success();
317}
318
319//===----------------------------------------------------------------------===//
320// ForallOp
321//===----------------------------------------------------------------------===//
322
323template <typename QuantifierOp>
324static LogicalResult verifyQuantifierRegions(QuantifierOp op) {
325 if (op.getBoundVarNames() &&
326 op.getBody().getNumArguments() != op.getBoundVarNames()->size())
327 return op.emitOpError(
328 "number of bound variable names must match number of block arguments");
329 if (!llvm::all_of(op.getBody().getArgumentTypes(), isAnyNonFuncSMTValueType))
330 return op.emitOpError()
331 << "bound variables must by any non-function SMT value";
332
333 if (op.getBody().front().getTerminator()->getNumOperands() != 1)
334 return op.emitOpError("must have exactly one yielded value");
335 if (!isa<BoolType>(
336 op.getBody().front().getTerminator()->getOperand(0).getType()))
337 return op.emitOpError("yielded value must be of '!smt.bool' type");
338
339 for (auto regionWithIndex : llvm::enumerate(op.getPatterns())) {
340 unsigned i = regionWithIndex.index();
341 Region &region = regionWithIndex.value();
342
343 if (op.getBody().getArgumentTypes() != region.getArgumentTypes())
344 return op.emitOpError()
345 << "block argument number and types of the 'body' "
346 "and 'patterns' region #"
347 << i << " must match";
348 if (region.front().getTerminator()->getNumOperands() < 1)
349 return op.emitOpError() << "'patterns' region #" << i
350 << " must have at least one yielded value";
351
352 // All operations in the 'patterns' region must be SMT operations.
353 auto result = region.walk([&](Operation *childOp) {
354 if (!isa<SMTDialect>(childOp->getDialect())) {
355 auto diag = op.emitOpError()
356 << "the 'patterns' region #" << i
357 << " may only contain SMT dialect operations";
358 diag.attachNote(childOp->getLoc()) << "first non-SMT operation here";
359 return WalkResult::interrupt();
360 }
361
362 // There may be no quantifier (or other variable binding) operations in
363 // the 'patterns' region.
364 if (isa<ForallOp, ExistsOp>(childOp)) {
365 auto diag = op.emitOpError() << "the 'patterns' region #" << i
366 << " must not contain "
367 "any variable binding operations";
368 diag.attachNote(childOp->getLoc()) << "first violating operation here";
369 return WalkResult::interrupt();
370 }
371
372 return WalkResult::advance();
373 });
374 if (result.wasInterrupted())
375 return failure();
376 }
377
378 return success();
379}
380
381template <typename Properties>
382static void buildQuantifier(
383 OpBuilder &odsBuilder, OperationState &odsState, TypeRange boundVarTypes,
385 std::optional<ArrayRef<StringRef>> boundVarNames,
387 uint32_t weight, bool noPattern) {
388 odsState.addTypes(BoolType::get(odsBuilder.getContext()));
389 if (weight != 0)
390 odsState.getOrAddProperties<Properties>().weight =
391 odsBuilder.getIntegerAttr(odsBuilder.getIntegerType(32), weight);
392 if (noPattern)
393 odsState.getOrAddProperties<Properties>().noPattern =
394 odsBuilder.getUnitAttr();
395 if (boundVarNames.has_value()) {
396 SmallVector<Attribute> boundVarNamesList;
397 for (StringRef str : *boundVarNames)
398 boundVarNamesList.emplace_back(odsBuilder.getStringAttr(str));
399 odsState.getOrAddProperties<Properties>().boundVarNames =
400 odsBuilder.getArrayAttr(boundVarNamesList);
401 }
402 {
403 OpBuilder::InsertionGuard guard(odsBuilder);
404 Region *region = odsState.addRegion();
405 Block *block = odsBuilder.createBlock(region);
406 block->addArguments(
407 boundVarTypes,
408 SmallVector<Location>(boundVarTypes.size(), odsState.location));
409 Value returnVal =
410 bodyBuilder(odsBuilder, odsState.location, block->getArguments());
411 smt::YieldOp::create(odsBuilder, odsState.location, returnVal);
412 }
413 if (patternBuilder) {
414 Region *region = odsState.addRegion();
415 OpBuilder::InsertionGuard guard(odsBuilder);
416 Block *block = odsBuilder.createBlock(region);
417 block->addArguments(
418 boundVarTypes,
419 SmallVector<Location>(boundVarTypes.size(), odsState.location));
420 ValueRange returnVals =
421 patternBuilder(odsBuilder, odsState.location, block->getArguments());
422 smt::YieldOp::create(odsBuilder, odsState.location, returnVals);
423 }
424}
425
426LogicalResult ForallOp::verify() {
427 if (!getPatterns().empty() && getNoPattern())
428 return emitOpError() << "patterns and the no_pattern attribute must not be "
429 "specified at the same time";
430
431 return success();
432}
433
434LogicalResult ForallOp::verifyRegions() {
435 return verifyQuantifierRegions(*this);
436}
437
438void ForallOp::build(
439 OpBuilder &odsBuilder, OperationState &odsState, TypeRange boundVarTypes,
440 function_ref<Value(OpBuilder &, Location, ValueRange)> bodyBuilder,
441 std::optional<ArrayRef<StringRef>> boundVarNames,
442 function_ref<ValueRange(OpBuilder &, Location, ValueRange)> patternBuilder,
443 uint32_t weight, bool noPattern) {
444 buildQuantifier<Properties>(odsBuilder, odsState, boundVarTypes, bodyBuilder,
445 boundVarNames, patternBuilder, weight, noPattern);
446}
447
448//===----------------------------------------------------------------------===//
449// ExistsOp
450//===----------------------------------------------------------------------===//
451
452LogicalResult ExistsOp::verify() {
453 if (!getPatterns().empty() && getNoPattern())
454 return emitOpError() << "patterns and the no_pattern attribute must not be "
455 "specified at the same time";
456
457 return success();
458}
459
460LogicalResult ExistsOp::verifyRegions() {
461 return verifyQuantifierRegions(*this);
462}
463
464void ExistsOp::build(
465 OpBuilder &odsBuilder, OperationState &odsState, TypeRange boundVarTypes,
466 function_ref<Value(OpBuilder &, Location, ValueRange)> bodyBuilder,
467 std::optional<ArrayRef<StringRef>> boundVarNames,
468 function_ref<ValueRange(OpBuilder &, Location, ValueRange)> patternBuilder,
469 uint32_t weight, bool noPattern) {
470 buildQuantifier<Properties>(odsBuilder, odsState, boundVarTypes, bodyBuilder,
471 boundVarNames, patternBuilder, weight, noPattern);
472}
473
474#define GET_OP_CLASSES
475#include "mlir/Dialect/SMT/IR/SMT.cpp.inc"
return success()
static std::string diag(const llvm::Value &value)
static LogicalResult verifyQuantifierRegions(QuantifierOp op)
Definition SMTOps.cpp:324
static LogicalResult parseSameOperandTypeVariadicToBoolOp(OpAsmParser &parser, OperationState &result)
Definition SMTOps.cpp:95
static void buildQuantifier(OpBuilder &odsBuilder, OperationState &odsState, TypeRange boundVarTypes, function_ref< Value(OpBuilder &, Location, ValueRange)> bodyBuilder, std::optional< ArrayRef< StringRef > > boundVarNames, function_ref< ValueRange(OpBuilder &, Location, ValueRange)> patternBuilder, uint32_t weight, bool noPattern)
Definition SMTOps.cpp:382
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
Block represents an ordered list of Operations.
Definition Block.h:33
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Definition Block.cpp:165
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:111
UnitAttr getUnitAttr()
Definition Builders.cpp:106
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
unsigned getNumOperands()
Definition Operation.h:371
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
This class provides an abstraction over the different types of ranges over Regions.
Definition Region.h:378
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
ValueTypeRange< BlockArgListType > getArgumentTypes()
Returns the argument types of the first block within the region.
Definition Region.cpp:36
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
Definition Region.h:312
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
bool isAnyNonFuncSMTValueType(mlir::Type type)
Returns whether the given type is an SMT value type (excluding functions).
Definition SMTTypes.cpp:44
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
This is the representation of an operand reference.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
T & getOrAddProperties()
Get (or create) the properties of the provided type to be set on the operation on creation.
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.