CIRCT 23.0.0git
Loading...
Searching...
No Matches
RTGAttributes.cpp
Go to the documentation of this file.
1//===- RTGAttributes.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
12#include "mlir/IR/Builders.h"
13#include "mlir/IR/DialectImplementation.h"
14#include "llvm/ADT/TypeSwitch.h"
15
16using namespace circt;
17using namespace rtg;
18
19//===----------------------------------------------------------------------===//
20// Helpers
21//===----------------------------------------------------------------------===//
22
23namespace llvm {
24template <typename T>
25// NOLINTNEXTLINE(readability-identifier-naming)
26llvm::hash_code hash_value(const DenseSet<T> &set) {
27 // TODO: improve collision resistance
28 unsigned hash = 0;
29 for (auto element : set)
30 hash ^= element;
31 return hash;
32}
33
34template <typename K, typename V>
35// NOLINTNEXTLINE(readability-identifier-naming)
36llvm::hash_code hash_value(const DenseMap<K, V> &map) {
37 // TODO: improve collision resistance
38 unsigned hash = 0;
39 for (auto [key, value] : map)
40 hash ^= (key ^ value);
41 return hash;
42}
43} // namespace llvm
44
45//===----------------------------------------------------------------------===//
46// SetAttr
47//===----------------------------------------------------------------------===//
48
49LogicalResult
50SetAttr::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
51 rtg::SetType type, const DenseSet<TypedAttr> *elements) {
52
53 // check that all elements have the right type
54 // iterating over the set is fine here because the iteration order is not
55 // visible to the outside (it would not be fine to print the earliest invalid
56 // element)
57 if (!llvm::all_of(*elements, [&](auto element) {
58 return element.getType() == type.getElementType();
59 })) {
60 return emitError() << "all elements must be of the set element type "
61 << type.getElementType();
62 }
63
64 return success();
65}
66
67Attribute SetAttr::parse(AsmParser &odsParser, Type odsType) {
68 DenseSet<TypedAttr> elements;
69 Type elementType;
70 if (odsParser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::LessGreater,
71 [&]() {
72 TypedAttr element;
73 if (odsParser.parseAttribute(element))
74 return failure();
75 elements.insert(element);
76 elementType = element.getType();
77 return success();
78 }))
79 return {};
80
81 auto setType = llvm::dyn_cast_or_null<SetType>(odsType);
82 if (odsType && !setType) {
83 odsParser.emitError(odsParser.getNameLoc())
84 << "type must be a an '!rtg.set' type";
85 return {};
86 }
87
88 if (!setType && elements.empty()) {
89 odsParser.emitError(odsParser.getNameLoc())
90 << "type must be explicitly provided: cannot infer set element type "
91 "from empty set";
92 return {};
93 }
94
95 if (!setType && !elements.empty())
96 setType = SetType::get(elementType);
97
98 return SetAttr::getChecked(
99 odsParser.getEncodedSourceLoc(odsParser.getNameLoc()),
100 odsParser.getContext(), setType, &elements);
101}
102
103void SetAttr::print(AsmPrinter &odsPrinter) const {
104 odsPrinter << "<";
105 // Sort elements lexicographically by their printed string representation
106 SmallVector<std::string> sortedElements;
107 for (auto element : *getElements()) {
108 std::string &elementStr = sortedElements.emplace_back();
109 llvm::raw_string_ostream elementOS(elementStr);
110 element.print(elementOS);
111 }
112 llvm::sort(sortedElements);
113 llvm::interleaveComma(sortedElements, odsPrinter);
114 odsPrinter << ">";
115}
116
117//===----------------------------------------------------------------------===//
118// MapAttr
119//===----------------------------------------------------------------------===//
120
121LogicalResult
122MapAttr::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
123 rtg::MapType type,
124 const DenseMap<TypedAttr, TypedAttr> *entries) {
125
126 // check that all keys and values have the right type
127 if (!llvm::all_of(*entries, [&](auto entry) {
128 return entry.first.getType() == type.getKeyType() &&
129 entry.second.getType() == type.getValueType();
130 })) {
131 return emitError() << "all keys must be of type " << type.getKeyType()
132 << " and all values must be of type "
133 << type.getValueType();
134 }
135
136 return success();
137}
138
139Attribute MapAttr::parse(AsmParser &odsParser, Type odsType) {
140 DenseMap<TypedAttr, TypedAttr> entries;
141 Type keyType, valueType;
142 if (odsParser.parseCommaSeparatedList(
143 mlir::AsmParser::Delimiter::LessGreater, [&]() {
144 TypedAttr key, value;
145 if (odsParser.parseAttribute(key) || odsParser.parseArrow() ||
146 odsParser.parseAttribute(value))
147 return failure();
148 entries.insert({key, value});
149 keyType = key.getType();
150 valueType = value.getType();
151 return success();
152 }))
153 return {};
154
155 auto mapType = llvm::dyn_cast_or_null<MapType>(odsType);
156 if (odsType && !mapType) {
157 odsParser.emitError(odsParser.getNameLoc())
158 << "type must be an '!rtg.map' type";
159 return {};
160 }
161
162 if (!mapType && entries.empty()) {
163 odsParser.emitError(odsParser.getNameLoc())
164 << "type must be explicitly provided: cannot infer map key and value "
165 "types from empty map";
166 return {};
167 }
168
169 if (!mapType && !entries.empty())
170 mapType = MapType::get(keyType, valueType);
171
172 return MapAttr::getChecked(
173 odsParser.getEncodedSourceLoc(odsParser.getNameLoc()),
174 odsParser.getContext(), mapType, &entries);
175}
176
177void MapAttr::print(AsmPrinter &odsPrinter) const {
178 odsPrinter << "<";
179 // Sort entries lexicographically by their printed string representation
180 SmallVector<std::pair<std::string, std::string>> sortedEntries;
181 for (auto [key, value] : *getEntries()) {
182 std::string keyStr, valueStr;
183 llvm::raw_string_ostream keyOS(keyStr);
184 llvm::raw_string_ostream valueOS(valueStr);
185 key.print(keyOS);
186 value.print(valueOS);
187 sortedEntries.emplace_back(std::move(keyStr), std::move(valueStr));
188 }
189 llvm::sort(sortedEntries);
190 llvm::interleaveComma(sortedEntries, odsPrinter, [&](auto &entry) {
191 odsPrinter << entry.first << " -> " << entry.second;
192 });
193 odsPrinter << ">";
194}
195
196//===----------------------------------------------------------------------===//
197// TupleAttr
198//===----------------------------------------------------------------------===//
199
200Type TupleAttr::getType() const {
201 SmallVector<Type> elementTypes(llvm::map_range(
202 getElements(), [](auto element) { return element.getType(); }));
203 return TupleType::get(getContext(), elementTypes);
204}
205
206//===----------------------------------------------------------------------===//
207// ImmediateAttr
208//===----------------------------------------------------------------------===//
209
210namespace circt {
211namespace rtg {
212namespace detail {
213struct ImmediateAttrStorage : public mlir::AttributeStorage {
214 using KeyTy = APInt;
215 ImmediateAttrStorage(APInt value) : value(std::move(value)) {}
216
217 KeyTy getAsKey() const { return value; }
218
219 // NOTE: the implementation of this operator is the reason we need to define
220 // the storage manually. The auto-generated version would just do the direct
221 // equality check of the APInt, but that asserts the bitwidth of both to be
222 // the same, leading to a crash. This implementation, therefore, checks for
223 // matching bit-width beforehand.
224 bool operator==(const KeyTy &key) const {
225 return (value.getBitWidth() == key.getBitWidth() && value == key);
226 }
227
228 static llvm::hash_code hashKey(const KeyTy &key) {
229 return llvm::hash_value(key);
230 }
231
232 static ImmediateAttrStorage *
233 construct(mlir::AttributeStorageAllocator &allocator, KeyTy &&key) {
234 return new (allocator.allocate<ImmediateAttrStorage>())
235 ImmediateAttrStorage(std::move(key));
236 }
237
238 APInt value;
239};
240} // namespace detail
241} // namespace rtg
242} // namespace circt
243
244Type ImmediateAttr::getType() const {
245 return ImmediateType::get(getContext(), getValue().getBitWidth());
246}
247
248APInt ImmediateAttr::getValue() const { return getImpl()->value; }
249
250Attribute ImmediateAttr::parse(AsmParser &odsParser, Type odsType) {
251 llvm::SMLoc loc = odsParser.getCurrentLocation();
252
253 APInt val;
254 uint32_t width; // NOTE: this integer type should match the 'width' parameter
255 // type in immediate type.
256 if (odsParser.parseLess() || odsParser.parseInteger(width) ||
257 odsParser.parseComma() || odsParser.parseInteger(val) ||
258 odsParser.parseGreater())
259 return {};
260
261 // If the attribute type is explicitly given, check that the bit-widths match.
262 if (auto immTy = llvm::dyn_cast_or_null<ImmediateType>(odsType)) {
263 if (immTy.getWidth() != width) {
264 odsParser.emitError(loc) << "explicit immediate type bit-width does not "
265 "match attribute bit-width, "
266 << immTy.getWidth() << " vs " << width;
267 return {};
268 }
269 }
270
271 if (width > val.getBitWidth()) {
272 // sext is always safe here, even for unsigned values, because the
273 // parseOptionalInteger method will return something with a zero in the
274 // top bits if it is a positive number.
275 val = val.sext(width);
276 } else if (width < val.getBitWidth()) {
277 // The parser can return an unnecessarily wide result.
278 // This isn't a problem, but truncating off bits is bad.
279 unsigned neededBits =
280 val.isNegative() ? val.getSignificantBits() : val.getActiveBits();
281 if (width < neededBits) {
282 odsParser.emitError(loc)
283 << "integer value out-of-range for bit-width " << width;
284 return {};
285 }
286 val = val.trunc(width);
287 }
288
289 return ImmediateAttr::get(odsParser.getContext(), val);
290}
291
292void ImmediateAttr::print(AsmPrinter &odsPrinter) const {
293 odsPrinter << "<" << getValue().getBitWidth() << ", " << getValue() << ">";
294}
295
296Type VirtualRegisterConfigAttr::getType() const {
297 return getAllowedRegs()[0].getType();
298}
299
300LogicalResult VirtualRegisterConfigAttr::verify(
301 llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
302 ArrayRef<rtg::RegisterAttrInterface> allowedRegs) {
303 if (allowedRegs.empty())
304 return emitError() << "must have at least one allowed register";
305
306 if (!llvm::all_of(allowedRegs, [&](auto reg) {
307 return reg.getType() == allowedRegs[0].getType();
308 })) {
309 return emitError() << "all allowed registers must be of the same type";
310 }
311
312 return success();
313}
314
315//===----------------------------------------------------------------------===//
316// LabelAttr
317//===----------------------------------------------------------------------===//
318
319Type LabelAttr::getType() const { return LabelType::get(getContext()); }
320
321//===----------------------------------------------------------------------===//
322// TableGen generated logic.
323//===----------------------------------------------------------------------===//
324
325void RTGDialect::registerAttributes() {
326 addAttributes<
327#define GET_ATTRDEF_LIST
328#include "circt/Dialect/RTG/IR/RTGAttributes.cpp.inc"
329 >();
330}
331
332#define GET_ATTRDEF_CLASSES
333#include "circt/Dialect/RTG/IR/RTGAttributes.cpp.inc"
MlirType elementType
Definition CHIRRTL.cpp:29
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::hash_code hash_value(const DenseSet< T > &set)
Definition rtg.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
static llvm::hash_code hashKey(const KeyTy &key)
bool operator==(const KeyTy &key) const
static ImmediateAttrStorage * construct(mlir::AttributeStorageAllocator &allocator, KeyTy &&key)