CIRCT 22.0.0git
Loading...
Searching...
No Matches
Types.cpp
Go to the documentation of this file.
1//===- Types.cpp - ESI type system -----------------------------*- 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//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT.
12//
13//===----------------------------------------------------------------------===//
14
15#include "esi/Types.h"
16#include "esi/Values.h"
17#include <algorithm>
18#include <cstring>
19#include <format>
20#include <span>
21#include <sstream>
22
23namespace esi {
24
25// NOLINTNEXTLINE(misc-no-recursion)
26static void dumpType(std::ostream &os, const esi::Type *type, int level = 0,
27 bool oneLine = false) {
28 if (auto *uintType = dynamic_cast<const esi::UIntType *>(type)) {
29 os << "uint" << uintType->getBitWidth();
30 } else if (auto *sintType = dynamic_cast<const esi::SIntType *>(type)) {
31 os << "sint" << sintType->getBitWidth();
32 } else if (auto *bitsType = dynamic_cast<const esi::BitsType *>(type)) {
33 os << "bits" << bitsType->getBitWidth();
34 } else if (dynamic_cast<const esi::VoidType *>(type)) {
35 os << "void";
36 } else if (dynamic_cast<const esi::AnyType *>(type)) {
37 os << "any";
38 } else if (auto *structType = dynamic_cast<const esi::StructType *>(type)) {
39 os << "struct {";
40 const auto &fields = structType->getFields();
41 for (size_t i = 0; i < fields.size(); ++i) {
42 if (!oneLine)
43 os << std::endl << std::string(level + 2, ' ');
44 else if (i > 0)
45 os << " ";
46 const auto &[name, fieldType] = fields[i];
47 os << name << ": ";
48 dumpType(os, fieldType, level + 1, oneLine);
49 if (i < fields.size() - 1)
50 os << ",";
51 }
52 if (!oneLine)
53 os << std::endl << std::string(level, ' ');
54 os << "}";
55 } else if (auto *arrayType = dynamic_cast<const esi::ArrayType *>(type)) {
56 dumpType(os, arrayType->getElementType(), level + 1, oneLine);
57 os << "[" << arrayType->getSize() << "]";
58 } else if (auto *channelType = dynamic_cast<const esi::ChannelType *>(type)) {
59 os << "chan<";
60 dumpType(os, channelType->getInner(), level + 1, oneLine);
61 os << ">";
62 } else if (auto *bundleType = dynamic_cast<const esi::BundleType *>(type)) {
63 os << "bundle {";
64 const auto &channels = bundleType->getChannels();
65 for (size_t i = 0; i < channels.size(); ++i) {
66 if (!oneLine)
67 os << std::endl << std::string(level + 2, ' ');
68 else if (i > 0)
69 os << " ";
70 const auto &[name, direction, fieldType] = channels[i];
71 os << std::format(
72 "{} [{}]: ", direction == BundleType::To ? "to" : "from", name);
73 dumpType(os, fieldType, level + 1, oneLine);
74 if (i < channels.size() - 1)
75 os << ",";
76 }
77 if (!oneLine)
78 os << std::endl << std::string(level, ' ');
79 else
80 os << " ";
81 os << "}";
82 } else if (auto *windowType = dynamic_cast<const esi::WindowType *>(type)) {
83 os << "window[";
84 dumpType(os, windowType->getIntoType(), level + 1, oneLine);
85 os << "]";
86 } else if (auto *listType = dynamic_cast<const esi::ListType *>(type)) {
87 os << "list<";
88 dumpType(os, listType->getElementType(), level + 1, oneLine);
89 os << ">";
90 } else {
91 // For unknown types, just print the type ID
92 os << type->getID();
93 }
94}
95
96void Type::dump(std::ostream &os, bool oneLine) const {
97 dumpType(os, this, oneLine ? 0 : 0, oneLine);
98}
99
100// Recurse through an ESI type and print an elaborated version of it.
101std::string Type::toString(bool oneLine) const {
102 std::stringstream ss;
103 dump(ss, oneLine);
104 return ss.str();
105}
106
107std::pair<const Type *, BundleType::Direction>
108BundleType::findChannel(std::string name) const {
109 for (auto [channelName, dir, type] : channels)
110 if (channelName == name)
111 return std::make_pair(type, dir);
112 throw std::runtime_error(
113 std::format("Channel '{}' not found in bundle", name));
114}
115
116void ChannelType::ensureValid(const std::any &obj) const {
117 return inner->ensureValid(obj);
118}
119
120MutableBitVector ChannelType::serialize(const std::any &obj) const {
121 return inner->serialize(obj);
122}
123
124std::any ChannelType::deserialize(BitVector &data) const {
125 return inner->deserialize(data);
126}
127
128void VoidType::ensureValid(const std::any &obj) const {
129 // Void type should be represented by an empty std::any or nullptr
130 if (!obj.has_value())
131 return;
132
133 try {
134 std::any_cast<std::nullptr_t>(obj);
135 return;
136 } catch (const std::bad_any_cast &) {
137 throw std::runtime_error(
138 std::format("void type must be represented by empty std::any or "
139 "nullptr, but got {}",
140 obj.type().name()));
141 }
142}
143
144std::any VoidType::deserialize(BitVector &data) const {
145 if (data.width() < 8)
146 throw std::runtime_error("void type cannot be represented by empty data");
147 // Extract one byte and return the rest. Check that the byte is 0.
148 BitVector value = data.lsb(8);
149 if (std::ranges::any_of(value, [](auto b) { return b; }))
150 throw std::runtime_error(std::format("void type byte must be 0, got {:b}",
151 value.getSpan().front()));
152
153 data >>= 8;
154 return std::any{};
155}
156
157MutableBitVector VoidType::serialize(const std::any &obj) const {
158 ensureValid(obj);
159 // By convention, void is represented by a single byte of value 0.
160 MutableBitVector bv(8);
161 return bv;
162}
163
164void BitsType::ensureValid(const std::any &obj) const {
165 try {
166 auto data = std::any_cast<std::vector<uint8_t>>(obj);
167 size_t expectedSize = (getWidth() + 7) / 8; // Round up to nearest byte
168 if (data.size() != expectedSize) {
169 throw std::runtime_error("wrong size: expected " +
170 std::to_string(expectedSize) + " bytes, got " +
171 std::to_string(data.size()));
172 }
173 } catch (const std::bad_any_cast &) {
174 throw std::runtime_error(std::format(
175 "must be std::vector<uint8_t>, but got {}", obj.type().name()));
176 }
177}
178
179MutableBitVector BitsType::serialize(const std::any &obj) const {
180 ensureValid(obj);
181 auto bytes = std::any_cast<std::vector<uint8_t>>(obj); // copy
182 return MutableBitVector(std::move(bytes), getWidth());
183}
184
185std::any BitsType::deserialize(BitVector &data) const {
186 uint64_t w = getWidth();
187 if (data.width() < w)
188 throw std::runtime_error(std::format("Insufficient data for bits type. "
189 " Expected {} bits, got {} bits",
190 w, data.width()));
191 BitVector view = data.slice(0, w);
192 // Materialize into byte vector sized to width. This can be shortcut by just
193 // casting the view to a mutable bit vector, and grabbing its storage.
194 MutableBitVector viewCopy(view);
195 data >>= w;
196 return std::any(viewCopy.takeStorage());
197}
198
199// Helper function to extract signed integer-like values from std::any.
200// We here support cstdint int's and esi::Int.
201static Int getIntLikeFromAny(const std::any &obj, unsigned widthHint) {
202 const std::type_info &type = obj.type();
203
204 if (type == typeid(int64_t))
205 return Int(std::any_cast<int64_t>(obj), widthHint);
206 if (type == typeid(int32_t))
207 return Int(static_cast<int64_t>(std::any_cast<int32_t>(obj)), widthHint);
208 if (type == typeid(int16_t))
209 return Int(static_cast<int64_t>(std::any_cast<int16_t>(obj)), widthHint);
210 if (type == typeid(int8_t))
211 return Int(static_cast<int64_t>(std::any_cast<int8_t>(obj)), widthHint);
212 if (type == typeid(esi::Int))
213 return std::any_cast<Int>(obj);
214
215 throw std::bad_any_cast();
216}
217
218// Helper function to extract unsigned integer-like values from std::any.
219// We here support cstdint uint's and esi::UInt.
220static UInt getUIntLikeFromAny(const std::any &obj, unsigned widthHint) {
221 const std::type_info &type = obj.type();
222
223 if (type == typeid(uint64_t))
224 return UInt(std::any_cast<uint64_t>(obj), widthHint);
225 if (type == typeid(uint32_t))
226 return UInt(static_cast<uint64_t>(std::any_cast<uint32_t>(obj)), widthHint);
227 if (type == typeid(uint16_t))
228 return UInt(static_cast<uint64_t>(std::any_cast<uint16_t>(obj)), widthHint);
229 if (type == typeid(uint8_t))
230 return UInt(static_cast<uint64_t>(std::any_cast<uint8_t>(obj)), widthHint);
231 if (type == typeid(esi::UInt))
232 return std::any_cast<UInt>(obj);
233
234 throw std::bad_any_cast();
235}
236
237void SIntType::ensureValid(const std::any &obj) const {
238 try {
239 Int value = getIntLikeFromAny(obj, getWidth());
240 } catch (const std::exception &e) {
241 throw std::runtime_error(std::format(
242 "Unable to convert provided object to a {}-bit wide Int: {}",
243 getWidth(), e.what()));
244 }
245}
246
247MutableBitVector SIntType::serialize(const std::any &obj) const {
248 Int ival = getIntLikeFromAny(obj, getWidth());
249 if (static_cast<uint64_t>(ival.width()) != getWidth())
250 throw std::runtime_error(
251 std::format("Int width mismatch for SIntType serialize. Expected {} "
252 "bits, got {} bits",
253 getWidth(), ival.width()));
254 // Move bits into MutableBitVector.
255 return MutableBitVector(std::move(ival));
256}
257
258std::any SIntType::deserialize(BitVector &data) const {
259 uint64_t w = getWidth();
260 if (data.width() < w)
261 throw std::runtime_error(std::format(
262 "Insufficient data for sint type. Expected {} bits, got {} bits", w,
263 data.width()));
264 Int val(data.slice(0, w));
265 data >>= w;
266 return std::any(val);
267}
268
269void UIntType::ensureValid(const std::any &obj) const {
270 try {
271 UInt value = getUIntLikeFromAny(obj, getWidth());
272 } catch (const std::exception &e) {
273 throw std::runtime_error(std::format(
274 "Unable to convert provided object to a {}-bit wide UInt: {}",
275 getWidth(), e.what()));
276 }
277}
278
279MutableBitVector UIntType::serialize(const std::any &obj) const {
280 UInt uval = getUIntLikeFromAny(obj, getWidth());
281 if (static_cast<uint64_t>(uval.width()) != getWidth())
282 throw std::runtime_error(std::format(
283 "UInt width mismatch for UIntType serialize. Expected {} bits, got {} "
284 "bits",
285 getWidth(), uval.width()));
286 return MutableBitVector(std::move(uval));
287}
288
289std::any UIntType::deserialize(BitVector &data) const {
290 uint64_t w = getWidth();
291 if (data.width() < w)
292 throw std::runtime_error(std::format(
293 "Insufficient data for uint type. Expected {} bits, got {} bits", w,
294 data.width()));
295 UInt val(data.slice(0, w));
296 data >>= w;
297 return std::any(val);
298}
299
300void StructType::ensureValid(const std::any &obj) const {
301 try {
302 auto structData = std::any_cast<std::map<std::string, std::any>>(obj);
303
304 if (structData.size() != fields.size()) {
305 throw std::runtime_error(std::format("struct has {} fields, expected {}",
306 structData.size(), fields.size()));
307 }
308
309 for (const auto &[fieldName, fieldType] : fields) {
310 auto it = structData.find(fieldName);
311 if (it == structData.end())
312 throw std::runtime_error(std::format("missing field '{}'", fieldName));
313
314 try {
315 fieldType->ensureValid(it->second);
316 } catch (const std::runtime_error &e) {
317 throw std::runtime_error(
318 std::format("invalid field '{}': {}", fieldName, e.what()));
319 }
320 }
321 } catch (const std::bad_any_cast &) {
322 throw std::runtime_error(
323 std::format("must be std::map<std::string, std::any>, but got {}",
324 obj.type().name()));
325 }
326}
327
328MutableBitVector StructType::serialize(const std::any &obj) const {
329 ensureValid(obj);
330 auto structData = std::any_cast<std::map<std::string, std::any>>(obj);
331 MutableBitVector out(0);
332 auto handleField = [&](const std::pair<std::string, const Type *> &f) {
333 const auto &name = f.first;
334 const Type *ty = f.second;
335 auto fieldData = ty->serialize(structData.at(name));
336 out <<= fieldData.width();
337 out |= fieldData;
338 };
339 if (isReverse()) {
340 for (const auto &f : fields)
341 handleField(f);
342 } else {
343 for (auto it = fields.rbegin(); it != fields.rend(); ++it)
344 handleField(*it);
345 }
346 return out;
347}
348
349std::any StructType::deserialize(BitVector &data) const {
350 std::map<std::string, std::any> result;
351 auto consumeField = [&](const std::pair<std::string, const Type *> &f) {
352 const auto &name = f.first;
353 const Type *ty = f.second;
354 result[name] = ty->deserialize(data);
355 };
356 if (isReverse()) {
357 for (auto it = fields.rbegin(); it != fields.rend(); ++it)
358 consumeField(*it);
359 } else {
360 for (const auto &f : fields)
361 consumeField(f);
362 }
363 return std::any(result);
364}
365
366void ArrayType::ensureValid(const std::any &obj) const {
367 try {
368 auto arrayData = std::any_cast<std::vector<std::any>>(obj);
369
370 if (arrayData.size() != size) {
371 throw std::runtime_error(std::format("array has {} elements, expected {}",
372 arrayData.size(), size));
373 }
374
375 for (size_t i = 0; i < arrayData.size(); ++i) {
376 try {
377 elementType->ensureValid(arrayData[i]);
378 } catch (const std::runtime_error &e) {
379 throw std::runtime_error(
380 std::format("invalid element {}: {}", i, e.what()));
381 }
382 }
383 } catch (const std::bad_any_cast &) {
384 throw std::runtime_error(std::format(
385 "must be std::vector<std::any>, but got {}", obj.type().name()));
386 }
387}
388
389MutableBitVector ArrayType::serialize(const std::any &obj) const {
390 ensureValid(obj);
391 auto arrayData = std::any_cast<std::vector<std::any>>(obj);
392 MutableBitVector out(0);
393 auto handleElem = [&](const std::any &elem) {
394 auto elemData = elementType->serialize(elem);
395 out <<= elemData.width();
396 out |= elemData;
397 };
398 if (isReverse()) {
399 for (const auto &e : arrayData)
400 handleElem(e);
401 } else {
402 for (auto it = arrayData.rbegin(); it != arrayData.rend(); ++it)
403 handleElem(*it);
404 }
405 return out;
406}
407
408std::any ArrayType::deserialize(BitVector &data) const {
409 std::vector<std::any> result;
410 result.reserve(size);
411 for (uint64_t i = 0; i < size; ++i) {
412 std::any value = elementType->deserialize(data);
413 result.push_back(value);
414 }
415 if (isReverse())
416 std::reverse(result.begin(), result.end());
417 return std::any(result);
418}
419} // namespace esi
The "any" type is a special type which can be used to represent any type, as identified by the type i...
Definition Types.h:150
Arrays have a compile time specified (static) size and an element type.
Definition Types.h:247
uint64_t size
Definition Types.h:270
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:389
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:366
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:408
const Type * elementType
Definition Types.h:269
bool isReverse() const
Definition Types.h:256
uint64_t getWidth() const
Definition Types.h:161
A lightweight, non-owning bit vector view backed by a byte array span.
Definition Values.h:42
size_t width() const
Definition Values.h:54
std::span< const byte > getSpan() const
Return a handle to the underlying span.
Definition Values.h:62
Bits are just an array of bits.
Definition Types.h:170
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:164
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:185
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:179
Bundles represent a collection of channels.
Definition Types.h:97
ChannelVector channels
Definition Types.h:113
std::pair< const Type *, Direction > findChannel(std::string name) const
Definition Types.cpp:108
Channels are the basic communication primitives.
Definition Types.h:118
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:116
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:120
const Type * inner
Definition Types.h:130
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:124
Lists represent variable-length sequences of elements of a single type.
Definition Types.h:319
A mutable bit vector that owns its underlying storage.
Definition Values.h:185
std::vector< uint8_t > takeStorage()
Return and transfer ownership of the underlying storage.
Definition Values.h:219
Signed integer.
Definition Types.h:188
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:258
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:247
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:237
Structs are an ordered collection of fields, each with a name and a type.
Definition Types.h:210
bool isReverse() const
Definition Types.h:239
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:300
FieldVector fields
Definition Types.h:242
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:328
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:349
Root class of the ESI type system.
Definition Types.h:34
virtual void ensureValid(const std::any &obj) const
Ensure that a std::any object is valid for this type.
Definition Types.h:68
std::string toString(bool oneLine=false) const
Definition Types.cpp:101
virtual std::any deserialize(BitVector &data) const
Deserialize from a BitVector stream (LSB-first).
Definition Types.h:54
ID getID() const
Definition Types.h:40
virtual MutableBitVector serialize(const std::any &obj) const
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.h:47
void dump(std::ostream &os, bool oneLine=false) const
Definition Types.cpp:96
Unsigned integer.
Definition Types.h:199
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:289
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:279
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:269
The "void" type is a special type which can be used to represent no type.
Definition Types.h:134
std::any deserialize(BitVector &data) const override
Deserialize from a BitVector stream (LSB-first).
Definition Types.cpp:144
void ensureValid(const std::any &obj) const override
Ensure that a std::any object is valid for this type.
Definition Types.cpp:128
MutableBitVector serialize(const std::any &obj) const override
Serialize an object to a MutableBitVector (LSB-first stream).
Definition Types.cpp:157
Windows represent a fixed-size sliding window over a stream of data.
Definition Types.h:280
Definition esi.py:1
static UInt getUIntLikeFromAny(const std::any &obj, unsigned widthHint)
Definition Types.cpp:220
static Int getIntLikeFromAny(const std::any &obj, unsigned widthHint)
Definition Types.cpp:201
static void dumpType(std::ostream &os, const esi::Type *type, int level=0, bool oneLine=false)
Definition Types.cpp:26