LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
SMTAttributes.cpp
Go to the documentation of this file.
1//===- SMTAttributes.cpp - Implement SMT attributes -------------*- C++ -*-===//
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
13
14#include <mlir/IR/Builders.h>
15#include <mlir/IR/DialectImplementation.h>
16
17#include <llvm/ADT/StringExtras.h>
18#include <llvm/ADT/TypeSwitch.h>
19
20using namespace mlir;
21using namespace llzk::smt;
22
23static bool isValidSMTLibAtomChar(char ch) {
24 return llvm::isAlnum(ch) || ch == '_' || ch == '.' || ch == '$' || ch == '-' || ch == '!';
25}
26
27static LogicalResult verifySMTLibSymbolText(
28 function_ref<InFlightDiagnostic()> emitError, StringRef text, bool requireLeadingColon
29) {
30 if (text.empty()) {
31 return emitError() << "symbol text must not be empty";
32 }
33 if (requireLeadingColon) {
34 if (!text.starts_with(':')) {
35 return emitError() << "keyword must start with ':'";
36 }
37 text = text.drop_front();
38 if (text.empty()) {
39 return emitError() << "keyword must contain at least one character after ':'";
40 }
41 } else if (text.starts_with(':')) {
42 return emitError() << "symbol must not start with ':'";
43 }
44
45 for (char ch : text) {
46 if (!isValidSMTLibAtomChar(ch)) {
47 return emitError() << "invalid SMT-LIB symbol character '" << ch << '\'';
48 }
49 }
50 return success();
51}
52
53//===----------------------------------------------------------------------===//
54// BitVectorAttr
55//===----------------------------------------------------------------------===//
56
57LogicalResult BitVectorAttr::verify(
58 function_ref<InFlightDiagnostic()> emitError,
59 APInt value // NOLINT(performance-unnecessary-value-param)
60) {
61 if (value.getBitWidth() < 1) {
62 return emitError() << "bit-width must be at least 1, but got " << value.getBitWidth();
63 }
64 return success();
65}
66
67std::string BitVectorAttr::getValueAsString(bool prefix) const {
68 unsigned width = getValue().getBitWidth();
69 SmallVector<char> toPrint;
70 StringRef pref = prefix ? "#" : "";
71 if (width % 4 == 0) {
72 getValue().toString(toPrint, 16, false, false, false);
73 // APInt's 'toString' omits leading zeros. However, those are critical here
74 // because they determine the bit-width of the bit-vector.
75 SmallVector<char> leadingZeros(width / 4 - toPrint.size(), '0');
76 return (pref + "x" + Twine(leadingZeros) + toPrint).str();
77 }
78
79 getValue().toString(toPrint, 2, false, false, false);
80 // APInt's 'toString' omits leading zeros
81 SmallVector<char> leadingZeros(width - toPrint.size(), '0');
82 return (pref + "b" + Twine(leadingZeros) + toPrint).str();
83}
84
86static FailureOr<APInt>
87parseBitVectorString(function_ref<InFlightDiagnostic()> emitError, StringRef value) {
88 auto reportError = [emitError](StringRef msg) -> FailureOr<APInt> {
89 if (emitError) {
90 return emitError() << msg;
91 }
92 return failure();
93 };
94
95 if (value[0] != '#') {
96 return reportError("expected '#'");
97 }
98
99 if (value.size() < 3) {
100 return reportError("expected at least one digit");
101 }
102
103 if (value[1] == 'b') {
104 return APInt(value.size() - 2, std::string(value.begin() + 2, value.end()), 2);
105 }
106
107 if (value[1] == 'x') {
108 return APInt((value.size() - 2) * 4, std::string(value.begin() + 2, value.end()), 16);
109 }
110
111 return reportError("expected either 'b' or 'x'");
112}
113
114BitVectorAttr BitVectorAttr::get(MLIRContext *context, StringRef value) {
115 auto maybeValue = parseBitVectorString(nullptr, value);
116
117 assert(succeeded(maybeValue) && "string must have SMT-LIB format");
118 return Base::get(context, *maybeValue);
119}
120
121BitVectorAttr BitVectorAttr::getChecked(
122 function_ref<InFlightDiagnostic()> emitError, MLIRContext *context, StringRef value
123) {
124 auto maybeValue = parseBitVectorString(emitError, value);
125 if (failed(maybeValue)) {
126 return {};
127 }
128
129 return Base::getChecked(emitError, context, *maybeValue);
130}
131
132BitVectorAttr BitVectorAttr::get(MLIRContext *context, uint64_t value, unsigned width) {
133 return Base::get(context, APInt(width, value));
134}
135
136BitVectorAttr BitVectorAttr::getChecked(
137 function_ref<InFlightDiagnostic()> emitError, MLIRContext *context, uint64_t value,
138 unsigned width
139) {
140 if (width < 64 && value >= (UINT64_C(1) << width)) {
141 emitError() << "value does not fit in a bit-vector of desired width";
142 return {};
143 }
144 return Base::getChecked(emitError, context, APInt(width, value));
145}
146
147Attribute BitVectorAttr::parse(AsmParser &odsParser, Type odsType) {
148 llvm::SMLoc loc = odsParser.getCurrentLocation();
149
150 APInt val;
151 if (odsParser.parseLess() || odsParser.parseInteger(val) || odsParser.parseGreater()) {
152 return {};
153 }
154
155 // Requires the use of `quantified(<attr>)` in operation assembly formats.
156 if (!odsType || !llvm::isa<BitVectorType>(odsType)) {
157 odsParser.emitError(loc) << "explicit bit-vector type required";
158 return {};
159 }
160
161 unsigned width = llvm::cast<BitVectorType>(odsType).getWidth();
162
163 if (width > val.getBitWidth()) {
164 // sext is always safe here, even for unsigned values, because the
165 // parseOptionalInteger method will return something with a zero in the
166 // top bits if it is a positive number.
167 val = val.sext(width);
168 } else if (width < val.getBitWidth()) {
169 // The parser can return an unnecessarily wide result.
170 // This isn't a problem, but truncating off bits is bad.
171 unsigned neededBits = val.isNegative() ? val.getSignificantBits() : val.getActiveBits();
172 if (width < neededBits) {
173 odsParser.emitError(loc) << "integer value out of range for given bit-vector type "
174 << odsType;
175 return {};
176 }
177 val = val.trunc(width);
178 }
179
180 return BitVectorAttr::get(odsParser.getContext(), val);
181}
182
183void BitVectorAttr::print(AsmPrinter &odsPrinter) const {
184 // This printer only works for the extended format where the MLIR
185 // infrastructure prints the type for us. This means, the attribute should
186 // never be used without `quantified` in an assembly format.
187 odsPrinter << "<" << getValue() << ">";
188}
189
190Type BitVectorAttr::getType() const {
191 return BitVectorType::get(getContext(), getValue().getBitWidth());
192}
193
194//===----------------------------------------------------------------------===//
195// KeywordAttr / SymbolAttr
196//===----------------------------------------------------------------------===//
197
198LogicalResult KeywordAttr::verify(function_ref<InFlightDiagnostic()> emitError, StringRef value) {
199 return verifySMTLibSymbolText(emitError, value, /*requireLeadingColon=*/true);
200}
201
202Attribute KeywordAttr::parse(AsmParser &parser, Type) {
203 SMLoc loc = parser.getCurrentLocation();
204 StringRef keyword;
205 if (parser.parseLess() || parser.parseColon() || parser.parseKeyword(&keyword) ||
206 parser.parseGreater()) {
207 return {};
208 }
209 return parser.getChecked<KeywordAttr>(loc, parser.getContext(), (":" + keyword).str());
210}
211
212void KeywordAttr::print(AsmPrinter &printer) const { printer << '<' << getValue() << '>'; }
213
214LogicalResult SymbolAttr::verify(function_ref<InFlightDiagnostic()> emitError, StringRef value) {
215 return verifySMTLibSymbolText(emitError, value, /*requireLeadingColon=*/false);
216}
217
218Attribute SymbolAttr::parse(AsmParser &parser, Type) {
219 SMLoc loc = parser.getCurrentLocation();
220 StringRef symbol;
221 if (parser.parseLess() || parser.parseKeyword(&symbol) || parser.parseGreater()) {
222 return {};
223 }
224 return parser.getChecked<SymbolAttr>(loc, parser.getContext(), symbol.str());
225}
226
227void SymbolAttr::print(AsmPrinter &printer) const { printer << '<' << getValue() << '>'; }
228
229//===----------------------------------------------------------------------===//
230// ODS Boilerplate
231//===----------------------------------------------------------------------===//
232
233#define GET_ATTRDEF_CLASSES
235
237 // clang-format off
238 // Suppress false positive from `clang-tidy`
239 // NOLINTNEXTLINE(clang-analyzer-core.StackAddressEscape)
240 addAttributes<
241 #define GET_ATTRDEF_LIST
243 >();
244 // clang-format on
245}
static BitVectorType get(::mlir::MLIRContext *context, int64_t width)