Jlm
types.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <jlm/llvm/ir/types.hpp>
8 #include <jlm/util/Hash.hpp>
9 #include <jlm/util/Math.hpp>
10 #include <jlm/util/strfmt.hpp>
11 
12 #include <numeric>
13 #include <unordered_map>
14 
15 namespace jlm::llvm
16 {
17 
18 PointerType::~PointerType() noexcept = default;
19 
20 std::string
21 PointerType::debug_string() const
22 {
23  return "ptr";
24 }
25 
26 bool
27 PointerType::operator==(const jlm::rvsdg::Type & other) const noexcept
28 {
29  return jlm::rvsdg::is<PointerType>(other);
30 }
31 
32 std::size_t
33 PointerType::ComputeHash() const noexcept
34 {
35  return typeid(PointerType).hash_code();
36 }
37 
39 PointerType::Kind() const noexcept
40 {
42 }
43 
44 std::shared_ptr<const PointerType>
46 {
47  static const PointerType instance;
48  return std::shared_ptr<const PointerType>(std::shared_ptr<void>(), &instance);
49 }
50 
51 ArrayType::~ArrayType() noexcept = default;
52 
53 std::string
54 ArrayType::debug_string() const
55 {
56  return util::strfmt("[ ", nelements(), " x ", type_->debug_string(), " ]");
57 }
58 
59 bool
60 ArrayType::operator==(const Type & other) const noexcept
61 {
62  const auto type = dynamic_cast<const ArrayType *>(&other);
63  return type && type->element_type() == element_type() && type->nelements() == nelements();
64 }
65 
66 std::size_t
67 ArrayType::ComputeHash() const noexcept
68 {
69  const auto typeHash = typeid(ArrayType).hash_code();
70  const auto numElementsHash = std::hash<std::size_t>()(nelements_);
71  return util::CombineHashes(typeHash, type_->ComputeHash(), numElementsHash);
72 }
73 
75 ArrayType::Kind() const noexcept
76 {
78 }
79 
80 FloatingPointType::~FloatingPointType() noexcept = default;
81 
82 std::string
83 FloatingPointType::debug_string() const
84 {
85  static std::unordered_map<fpsize, std::string> map({ { fpsize::half, "half" },
86  { fpsize::flt, "float" },
87  { fpsize::dbl, "double" },
88  { fpsize::x86fp80, "x86fp80" },
89  { fpsize::fp128, "fp128" } });
90 
91  JLM_ASSERT(map.find(size()) != map.end());
92  return map[size()];
93 }
94 
95 bool
96 FloatingPointType::operator==(const Type & other) const noexcept
97 {
98  const auto type = dynamic_cast<const FloatingPointType *>(&other);
99  return type && type->size() == size();
100 }
101 
102 std::size_t
104 {
105  const auto typeHash = typeid(FloatingPointType).hash_code();
106  const auto sizeHash = std::hash<fpsize>()(size_);
107  return util::CombineHashes(typeHash, sizeHash);
108 }
109 
111 FloatingPointType::Kind() const noexcept
112 {
113  return rvsdg::TypeKind::Value;
114 }
115 
116 std::shared_ptr<const FloatingPointType>
118 {
119  switch (size)
120  {
121  case fpsize::half:
122  {
123  static const FloatingPointType instance(fpsize::half);
124  return std::shared_ptr<const FloatingPointType>(std::shared_ptr<void>(), &instance);
125  }
126  case fpsize::flt:
127  {
128  static const FloatingPointType instance(fpsize::flt);
129  return std::shared_ptr<const FloatingPointType>(std::shared_ptr<void>(), &instance);
130  }
131  case fpsize::dbl:
132  {
133  static const FloatingPointType instance(fpsize::dbl);
134  return std::shared_ptr<const FloatingPointType>(std::shared_ptr<void>(), &instance);
135  }
136  case fpsize::x86fp80:
137  {
138  static const FloatingPointType instance(fpsize::x86fp80);
139  return std::shared_ptr<const FloatingPointType>(std::shared_ptr<void>(), &instance);
140  }
141  case fpsize::fp128:
142  {
143  static const FloatingPointType instance(fpsize::fp128);
144  return std::shared_ptr<const FloatingPointType>(std::shared_ptr<void>(), &instance);
145  }
146  default:
147  {
148  JLM_UNREACHABLE("unknown fpsize");
149  }
150  }
151 }
152 
154 
155 bool
156 VariableArgumentType::operator==(const Type & other) const noexcept
157 {
158  return dynamic_cast<const VariableArgumentType *>(&other) != nullptr;
159 }
160 
161 std::size_t
163 {
164  return typeid(VariableArgumentType).hash_code();
165 }
166 
169 {
170  return rvsdg::TypeKind::State;
171 }
172 
173 std::string
175 {
176  return "vararg";
177 }
178 
179 std::shared_ptr<const VariableArgumentType>
181 {
182  static const VariableArgumentType instance;
183  return std::shared_ptr<const VariableArgumentType>(std::shared_ptr<void>(), &instance);
184 }
185 
186 StructType::~StructType() noexcept = default;
187 
188 bool
189 StructType::operator==(const Type & other) const noexcept
190 {
191  const auto type = dynamic_cast<const StructType *>(&other);
192  if (!type || type->isPacked_ != isPacked_ || type->isLiteral_ != isLiteral_
193  || type->name_ != name_ || type->numElements() != numElements())
194  return false;
195 
196  for (size_t n = 0; n < numElements(); n++)
197  {
198  if (*types_[n] != *type->types_[n])
199  return false;
200  }
201 
202  return true;
203 }
204 
205 std::size_t
206 StructType::ComputeHash() const noexcept
207 {
208  const auto typeHash = typeid(StructType).hash_code();
209  const auto isLiteralHash = std::hash<bool>()(isLiteral_);
210  const auto isPackedHash = std::hash<bool>()(isPacked_);
211  const auto nameHash = std::hash<std::string>()(name_);
212 
213  auto hash = util::CombineHashes(typeHash, isLiteralHash, isPackedHash, nameHash);
214  for (auto & type : types_)
215  {
216  hash = util::CombineHashes(hash, type->ComputeHash());
217  }
218 
219  return hash;
220 }
221 
223 StructType::Kind() const noexcept
224 {
225  return rvsdg::TypeKind::Value;
226 }
227 
228 std::string
230 {
231  return "struct";
232 }
233 
234 size_t
235 StructType::GetFieldOffset(size_t fieldIndex) const
236 {
237  const auto isPacked = IsPacked();
238 
239  size_t offset = 0;
240 
241  for (size_t i = 0; i < numElements(); i++)
242  {
243  auto field = getElementType(i);
244 
245  // First round up to the alignment of the field
246  auto fieldAlignment = isPacked ? 1 : GetTypeAlignment(*field);
247  offset = util::RoundUpToMultipleOf(offset, fieldAlignment);
248 
249  if (i == fieldIndex)
250  return offset;
251 
252  // Add the size of the field
253  offset += GetTypeAllocSize(*field);
254  }
255 
256  JLM_UNREACHABLE("Invalid fieldIndex in GetStructFieldOffset");
257 }
258 
259 bool
260 VectorType::operator==(const rvsdg::Type & other) const noexcept
261 {
262  const auto type = dynamic_cast<const VectorType *>(&other);
263  return type && type->size_ == size_ && *type->type_ == *type_;
264 }
265 
267 VectorType::Kind() const noexcept
268 {
269  return rvsdg::TypeKind::Value;
270 }
271 
272 FixedVectorType::~FixedVectorType() noexcept = default;
273 
274 bool
275 FixedVectorType::operator==(const jlm::rvsdg::Type & other) const noexcept
276 {
277  return VectorType::operator==(other);
278 }
279 
280 std::size_t
282 {
283  auto typeHash = typeid(FixedVectorType).hash_code();
284  auto sizeHash = std::hash<size_t>()(size());
285  return util::CombineHashes(typeHash, sizeHash, Type()->ComputeHash());
286 }
287 
288 std::string
290 {
291  return util::strfmt("fixedvector[", type().debug_string(), ":", size(), "]");
292 }
293 
294 ScalableVectorType::~ScalableVectorType() noexcept = default;
295 
296 bool
297 ScalableVectorType::operator==(const jlm::rvsdg::Type & other) const noexcept
298 {
299  return VectorType::operator==(other);
300 }
301 
302 std::size_t
304 {
305  const auto typeHash = typeid(ScalableVectorType).hash_code();
306  const auto sizeHash = std::hash<size_t>()(size());
307  return util::CombineHashes(typeHash, sizeHash, Type()->ComputeHash());
308 }
309 
310 std::string
312 {
313  return util::strfmt("scalablevector[", type().debug_string(), ":", size(), "]");
314 }
315 
316 IOStateType::~IOStateType() noexcept = default;
317 
318 bool
319 IOStateType::operator==(const Type & other) const noexcept
320 {
321  return jlm::rvsdg::is<IOStateType>(other);
322 }
323 
324 std::size_t
325 IOStateType::ComputeHash() const noexcept
326 {
327  return typeid(IOStateType).hash_code();
328 }
329 
331 IOStateType::Kind() const noexcept
332 {
333  return rvsdg::TypeKind::State;
334 }
335 
336 std::string
338 {
339  return "iostate";
340 }
341 
342 std::shared_ptr<const IOStateType>
344 {
345  static const IOStateType instance;
346  return std::shared_ptr<const IOStateType>(std::shared_ptr<void>(), &instance);
347 }
348 
352 MemoryStateType::~MemoryStateType() noexcept = default;
353 
354 std::string
355 MemoryStateType::debug_string() const
356 {
357  return "mem";
358 }
359 
360 bool
361 MemoryStateType::operator==(const jlm::rvsdg::Type & other) const noexcept
362 {
363  return jlm::rvsdg::is<MemoryStateType>(other);
364 }
365 
366 std::size_t
368 {
369  return typeid(MemoryStateType).hash_code();
370 }
371 
373 MemoryStateType::Kind() const noexcept
374 {
375  return rvsdg::TypeKind::State;
376 }
377 
378 std::shared_ptr<const MemoryStateType>
380 {
381  static const MemoryStateType instance;
382  return std::shared_ptr<const MemoryStateType>(std::shared_ptr<void>(), &instance);
383 }
384 
385 size_t
387 {
388  if (const auto bits = dynamic_cast<const rvsdg::BitType *>(&type))
389  {
390  // Assume 8 bits per byte, and round up to nearest whole byte
391  const auto bytes = (bits->nbits() + 7) / 8;
392  return bytes;
393  }
394  if (jlm::rvsdg::is<PointerType>(type))
395  {
396  // FIXME: Use the target information in the module to find the actual size of pointers
397  return 8;
398  }
399  if (const auto arrayType = dynamic_cast<const ArrayType *>(&type))
400  {
401  return arrayType->nelements() * GetTypeAllocSize(*arrayType->GetElementType());
402  }
403  if (const auto floatType = dynamic_cast<const FloatingPointType *>(&type))
404  {
405  switch (floatType->size())
406  {
407  case fpsize::half:
408  return 2;
409  case fpsize::flt:
410  return 4;
411  case fpsize::dbl:
412  return 8;
413  case fpsize::fp128:
414  return 16;
415  case fpsize::x86fp80:
416  return 10;
417  default:
418  JLM_UNREACHABLE("Unknown float size");
419  }
420  }
421  if (const auto structType = dynamic_cast<const StructType *>(&type))
422  {
423  size_t totalSize = 0;
424  size_t alignment = 1;
425 
426  // A packed struct has alignment 1, and all fields are tightly packed
427  const auto isPacked = structType->IsPacked();
428 
429  for (size_t i = 0; i < structType->numElements(); i++)
430  {
431  auto field = structType->getElementType(i);
432 
433  // Note: It is correct to use the TypeAllocSize here.
434  // An unpacked LLVM struct { i8, i33, i8 } takes up 24 bytes in store size.
435  // Even in a packed struct <{ i8, i33, i8 }>, it still takes up 10 bytes.
436  auto fieldSize = GetTypeAllocSize(*field);
437  auto fieldAlignment = isPacked ? 1 : GetTypeAlignment(*field);
438 
439  // First add any padding needed to align the start of the field
440  totalSize = util::RoundUpToMultipleOf(totalSize, fieldAlignment);
441  // Add the size of the field
442  totalSize += fieldSize;
443 
444  // The struct as a whole must be at least as aligned as each field
445  alignment = std::lcm(alignment, fieldAlignment);
446  }
447 
448  // Round size up to a multiple of alignment
449  totalSize = util::RoundUpToMultipleOf(totalSize, alignment);
450 
451  // If the struct has 0 fields, its size becomes 0.
452  // In C++, where types of size 0 are forbidden, clang will have inserted a dummy i8 field.
453 
454  return totalSize;
455  }
456  if (const auto vectorType = dynamic_cast<const VectorType *>(&type))
457  {
458  // In LLVM, vectors always have alignment >= the number of bytes of data stored in the vector
459  const auto bytesNeeded = vectorType->size() * GetTypeAllocSize(*vectorType->Type());
460  return bytesNeeded;
461  }
462  if (rvsdg::is<rvsdg::FunctionType>(type))
463  {
464  // Functions should never read from or written to, so give them size 0
465  // Note: this is not the same as a function pointer, which is a PointerType
466  return 0;
467  }
468 
469  JLM_UNREACHABLE(util::strfmt("Unknown type: ", typeid(type).name()).c_str());
470 }
471 
472 size_t
474 {
475  if (rvsdg::is<StructType>(type) || rvsdg::is<ArrayType>(type))
476  {
477  // These types already have aligned store size, so skip calculating alignment
478  return GetTypeStoreSize(type);
479  }
480 
482 }
483 
484 size_t
486 {
487  if (jlm::rvsdg::is<rvsdg::BitType>(type) || jlm::rvsdg::is<PointerType>(type)
488  || jlm::rvsdg::is<FloatingPointType>(type) || jlm::rvsdg::is<VectorType>(type))
489  {
490  // These types all have alignment equal to their byte size, rounded up to a power of two
492  }
493  if (const auto arrayType = dynamic_cast<const ArrayType *>(&type))
494  {
495  return GetTypeAlignment(*arrayType->GetElementType());
496  }
497  if (const auto structType = dynamic_cast<const StructType *>(&type))
498  {
499  // A packed struct has alignment 1, and all fields are tightly packed
500  if (structType->IsPacked())
501  return 1;
502 
503  size_t alignment = 1;
504 
505  for (size_t i = 0; i < structType->numElements(); i++)
506  {
507  auto field = structType->getElementType(i);
508  auto fieldAlignment = GetTypeAlignment(*field);
509 
510  // The struct as a whole must be at least as aligned as each field
511  alignment = std::lcm(alignment, fieldAlignment);
512  }
513 
514  return alignment;
515  }
516  if (rvsdg::is<rvsdg::FunctionType>(type))
517  {
518  // While the ABI might enforce some sort of function alignment,
519  // this is irrelevant to memory operations in the program or struct layouts
520  return 1;
521  }
522 
523  JLM_UNREACHABLE("Unknown type");
524 }
525 
526 }
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:75
std::shared_ptr< const rvsdg::Type > type_
Definition: types.hpp:105
ArrayType(std::shared_ptr< const Type > type, size_t nelements)
Definition: types.hpp:52
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:67
~ArrayType() noexcept override
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: types.cpp:60
std::string debug_string() const override
Definition: types.cpp:289
~FixedVectorType() noexcept override
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:281
FixedVectorType(std::shared_ptr< const rvsdg::Type > type, size_t size)
Definition: types.hpp:388
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: types.cpp:96
static std::shared_ptr< const FloatingPointType > Create(fpsize size)
Definition: types.cpp:117
FloatingPointType(const fpsize &size)
Definition: types.hpp:124
~FloatingPointType() noexcept override
const fpsize & size() const noexcept
Definition: types.hpp:141
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:103
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:111
Input/Output state type.
Definition: types.hpp:438
constexpr IOStateType() noexcept=default
~IOStateType() noexcept override
static std::shared_ptr< const IOStateType > Create()
Definition: types.cpp:343
std::string debug_string() const override
Definition: types.cpp:337
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:325
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:331
Memory state type class.
Definition: types.hpp:466
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:373
constexpr MemoryStateType() noexcept=default
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: types.cpp:361
~MemoryStateType() noexcept override
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:367
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
PointerType class.
Definition: types.hpp:25
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
~PointerType() noexcept override
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: types.cpp:27
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:33
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:39
std::string debug_string() const override
Definition: types.cpp:311
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:303
ScalableVectorType(std::shared_ptr< const rvsdg::Type > type, size_t size)
Definition: types.hpp:413
~ScalableVectorType() noexcept override
StructType class.
Definition: types.hpp:184
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:206
std::string debug_string() const override
Definition: types.cpp:229
size_t numElements() const noexcept
Definition: types.hpp:219
std::shared_ptr< const Type > getElementType(const size_t index) const noexcept
Definition: types.hpp:225
StructType(std::string name, std::vector< std::shared_ptr< const Type >> types, const bool isPacked, const bool isLiteral)
Definition: types.hpp:188
std::string name_
Definition: types.hpp:330
bool IsPacked() const noexcept
Definition: types.hpp:262
std::vector< std::shared_ptr< const Type > > types_
Definition: types.hpp:331
size_t GetFieldOffset(size_t fieldIndex) const
Definition: types.cpp:235
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:223
~StructType() noexcept override
~VariableArgumentType() noexcept override
std::size_t ComputeHash() const noexcept override
Definition: types.cpp:162
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:168
std::string debug_string() const override
Definition: types.cpp:174
constexpr VariableArgumentType()=default
static std::shared_ptr< const VariableArgumentType > Create()
Definition: types.cpp:180
bool operator==(const jlm::rvsdg::Type &other) const noexcept override
Definition: types.cpp:260
size_t size() const noexcept
Definition: types.hpp:361
const rvsdg::Type & type() const noexcept
Definition: types.hpp:367
rvsdg::TypeKind Kind() const noexcept override
Return the kind of this type.
Definition: types.cpp:267
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition: types.hpp:373
#define JLM_ASSERT(x)
Definition: common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition: common.hpp:43
Global memory state passed between functions.
size_t GetTypeAlignment(const rvsdg::Type &type)
Definition: types.cpp:485
size_t GetTypeAllocSize(const rvsdg::Type &type)
Definition: types.cpp:473
size_t GetTypeStoreSize(const rvsdg::Type &type)
Definition: types.cpp:386
static std::string type(const Node *n)
Definition: view.cpp:255
TypeKind
The kinds of types supported in rvsdg.
Definition: type.hpp:22
@ State
Designate a state type.
@ Value
Designate a value type.
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35
static constexpr T RoundUpToMultipleOf(T value, T multiple)
Definition: Math.hpp:72
static constexpr T RoundUpToPowerOf2(T value)
Definition: Math.hpp:48
std::size_t CombineHashes(std::size_t hash, Args... args)
Definition: Hash.hpp:63