• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tudasc / TypeART / 13528988609

25 Feb 2025 07:06PM UTC coverage: 88.854% (-1.9%) from 90.735%
13528988609

Pull #163

github

web-flow
Merge e4a2d80f6 into d2e14acc5
Pull Request #163: LLVM 18 support

974 of 1122 new or added lines in 38 files covered. (86.81%)

30 existing lines in 6 files now uncovered.

4201 of 4728 relevant lines covered (88.85%)

190054.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/lib/passes/typegen/dimeta/DimetaTypeGen.cpp
1
// TypeART library
2
//
3
// Copyright (c) 2017-2025 TypeART Authors
4
// Distributed under the BSD 3-Clause license.
5
// (See accompanying file LICENSE.txt or copy at
6
// https://opensource.org/licenses/BSD-3-Clause)
7
//
8
// Project home: https://github.com/tudasc/TypeART
9
//
10
// SPDX-License-Identifier: BSD-3-Clause
11
//
12

13
#include "../TypeIDGenerator.h"
14
#include "Dimeta.h"
15
#include "DimetaData.h"
16
#include "support/Logger.h"
17
#include "typegen/TypeGenerator.h"
18
#include "typelib/TypeDatabase.h"
19
#include "typelib/TypeInterface.h"
20

21
#include "llvm/ADT/StringExtras.h"
22
#include "llvm/Support/MD5.h"
23

24
#include <cassert>
25
#include <cstddef>
26
#include <cstdint>
27
#include <iterator>
28
#include <llvm/ADT/STLExtras.h>
29
#include <llvm/IR/GlobalVariable.h>
30
#include <llvm/IR/InstrTypes.h>
31
#include <llvm/IR/Instructions.h>
32
#include <llvm/Support/Casting.h>
33
#include <memory>
34
#include <string>
35
#include <type_traits>
36

37
namespace llvm {
38
class DataLayout;
39
class StructType;
40
class Type;
41
class VectorType;
42
}  // namespace llvm
43

44
namespace typeart::types {
45

46
template <class... Ts>
47
struct overload : Ts... {
48
  using Ts::operator()...;
49
};
50
template <class... Ts>
51
overload(Ts...) -> overload<Ts...>;
52

53
template <typename Type>
54
std::pair<std::optional<typeart_builtin_type>, int> typeid_if_ptr(const Type& type) {
14,063✔
55
  using namespace dimeta;
56
  const auto& quals = type.qual;
14,063✔
57
  int count{0};
14,063✔
58
  if (!quals.empty()) {
14,063✔
59
    count = llvm::count_if(quals, [](auto& qual) { return qual == Qualifier::kPtr || qual == Qualifier::kRef; });
10,483✔
60
    if (count > 0) {
4,482✔
61
      return {{TYPEART_POINTER}, count};
2,322✔
62
    }
63
  }
2,160✔
64
  return {{}, count};
11,741✔
65
}
14,063✔
66

67
namespace detail {
68

69
template <typename Type, typename Func>
70
auto apply_function(const Type& type, Func&& handle_qualified_type) {
25,758✔
71
  using namespace dimeta;
72

73
  if constexpr (std::is_same_v<Type, typename dimeta::QualifiedCompound> ||
74
                std::is_same_v<Type, typename dimeta::QualifiedFundamental>) {
75
    return std::forward<Func>(handle_qualified_type)(type);
16,265✔
76
  } else {
77
    return std::visit(
9,493✔
78
        [&](auto&& qualified_type) {
18,986✔
79
          return apply_function(qualified_type, std::forward<Func>(handle_qualified_type));
9,493✔
80
        },
81
        type);
9,493✔
82
  }
83
}
84

85
}  // namespace detail
86

87
namespace workaround {
88
void remove_pointer_level(const llvm::AllocaInst* alloc, dimeta::LocatedType& val) {
3,825✔
89
  // If the alloca instruction is not a pointer, but the located_type has a pointer-like qualifier, we remove it.
90
  // Workaround for inlining issue, see test typemapping/05_milc_inline_metadata.c
91
  // TODO Should be removed if dimeta fixes it.
92
  if (!alloc->getAllocatedType()->isPointerTy()) {
3,825✔
93
    LOG_DEBUG("Alloca is not a pointer")
94

95
    const auto remove_pointer_level = [](auto& qual) {
3,720✔
96
      auto pointer_like_iter = llvm::find_if(qual, [](auto qualifier) {
4,481✔
97
        switch (qualifier) {
761✔
98
          case dimeta::Qualifier::kPtr:
99
          case dimeta::Qualifier::kRef:
100
          case dimeta::Qualifier::kPtrToMember:
101
            return true;
123✔
102
          default:
103
            break;
638✔
104
        }
105
        return false;
638✔
106
      });
761✔
107
      if (pointer_like_iter != std::end(qual)) {
3,720✔
108
        LOG_DEBUG("Removing pointer level " << static_cast<int>(*pointer_like_iter))
109
        qual.erase(pointer_like_iter);
123✔
110
      }
123✔
111
    };
3,720✔
112
    std::visit([&](auto&& qualified_type) { remove_pointer_level(qualified_type.qual); }, val.type);
7,440✔
113
  }
3,720✔
114
}
3,825✔
115
}  // namespace workaround
116

117
template <typename Type>
118
dimeta::ArraySize vector_num_elements(const Type& type) {
36✔
119
  return detail::apply_function(type, [](const auto& t) -> dimeta::Extent {
72✔
120
    if (t.is_vector) {
36✔
121
      int pos{-1};
36✔
122
      // Find kVector tag-position to determine vector size
123
      for (const auto& qualifier : t.qual) {
54✔
124
        if (qualifier == dimeta::Qualifier::kVector) {
54✔
125
          pos++;
36✔
126
          break;
36✔
127
        }
128
        if (qualifier == dimeta::Qualifier::kArray) {
18✔
129
          pos++;
×
130
        }
×
131
      }
132
      if (pos == -1) {
36✔
133
        return 1;
×
134
      }
135
      return t.array_size.at(pos);
36✔
136
    }
137

138
    return 1;
×
139
  });
36✔
140
}
141

142
std::string get_anon_struct_identifier(const dimeta::QualifiedCompound& compound) {
294✔
143
  llvm::MD5 compound_hash;
294✔
144
  if (compound.type.members.empty()) {
294✔
145
    LOG_WARNING("Anonymous struct has no members")
246✔
146
  }
246✔
147
  for (const auto& [member, offset, size] :
432✔
148
       llvm::zip(compound.type.members, compound.type.offsets, compound.type.sizes)) {
294✔
149
    compound_hash.update(member->name);
138✔
150
    compound_hash.update(offset);
138✔
151
    compound_hash.update(size);
138✔
152
    compound_hash.update(std::visit(overload{[&](const dimeta::QualifiedFundamental& member_fundamental) {
327✔
153
                                               return std::to_string(
120✔
154
                                                          static_cast<int>(member_fundamental.type.encoding)) +
240✔
155
                                                      std::to_string(static_cast<int>(member_fundamental.type.extent));
120✔
NEW
156
                                             },
×
157
                                             [&](const dimeta::QualifiedCompound& member_compound) {
18✔
158
                                               return get_anon_struct_identifier(member_compound);
18✔
159
                                             }},
160
                                    member->member));
138✔
161
    compound_hash.update("\0");
138✔
162
  }
163
  compound_hash.update(compound.type.extent);
294✔
164
  compound_hash.update("\0");
294✔
165
  llvm::MD5::MD5Result hash_result;
166
  compound_hash.final(hash_result);
294✔
167
  return "anonymous_compound_" + std::string(hash_result.digest().str());
294✔
NEW
168
}
×
169

170
template <typename Type>
171
dimeta::ArraySize array_size(const Type& type) {
10,117✔
172
  return detail::apply_function(type, [](const auto& t) -> dimeta::Extent {
20,234✔
173
    if (t.array_size.size() > 1 || (t.is_vector && t.array_size.size() > 2)) {
10,117✔
174
      LOG_ERROR("Unsupported array size number count > 1 for array type or > 2 for vector")
36✔
175
    }
36✔
176
    // Vector array-size does not count towards array type-size
177
    if (t.is_vector && t.array_size.size() == 1) {
10,117✔
178
      return 1;
30✔
179
    }
180
    const auto array_size_factor = t.array_size.empty() ? 1 : t.array_size.at(0);
10,087✔
181

182
    return array_size_factor;
10,087✔
183
  });
10,117✔
184
}
185

186
template <typename Type>
187
std::string name_or_typedef_of(const Type& type) {
6,112✔
188
  return detail::apply_function(type, [](const auto& qual_type) {
12,224✔
189
    const bool no_name = qual_type.type.name.empty();
6,112✔
190
    if constexpr (std::is_same_v<Type, typename dimeta::QualifiedCompound>) {
191
      const bool no_identifier = qual_type.type.identifier.empty();
6,112✔
192
      const bool no_typedef    = qual_type.typedef_name.empty();
6,112✔
193
      if (no_identifier && no_name && no_typedef) {
6,112✔
194
        return get_anon_struct_identifier(qual_type);
276✔
195
      }
196
      if (no_identifier && no_name) {
5,836✔
197
        return qual_type.typedef_name;
59✔
198
      }
199
      if (no_identifier) {
5,777✔
200
        return qual_type.type.name;
588✔
201
      }
202
      return qual_type.type.identifier;
5,189✔
203
    }
204

205
    return no_name ? qual_type.typedef_name : qual_type.type.name;
206
  });
6,112✔
207
}
208

209
std::optional<typeart_builtin_type> get_builtin_typeid(const dimeta::QualifiedFundamental& type,
11,257✔
210
                                                       bool top_level = false) {
211
  using namespace dimeta;
212
  const auto [ptr_type_id, count] = typeid_if_ptr(type);
17,888✔
213

214
  if ((top_level && count > 1) || (!top_level && count > 0)) {
11,257✔
215
    LOG_DEBUG((top_level ? "Top level, ptr ptr" : "NOT Top level, ptr ptr"));
216
    return ptr_type_id.value();
11,952✔
217
  }
218

219
  const auto extent   = type.type.extent;
6,863✔
220
  const auto encoding = type.type.encoding;
6,863✔
221

222
  switch (encoding) {
6,863✔
223
    case FundamentalType::Encoding::kUnknown:
224
      return TYPEART_UNKNOWN_TYPE;
×
225
    case FundamentalType::Encoding::kVoid:
226
      return TYPEART_VOID;
72✔
227
    case FundamentalType::kNullptr:
228
      return TYPEART_NULLPOINTER;
×
229
    case FundamentalType::Encoding::kUTFChar: {
230
      switch (extent) {
×
231
        case 4:
232
          return TYPEART_UTF_CHAR_32;
×
233
        case 2:
234
          return TYPEART_UTF_CHAR_16;
×
235
        case 1:
236
          return TYPEART_UTF_CHAR_8;
×
237
        default:
238
          return TYPEART_UNKNOWN_TYPE;
×
239
      }
240
    }
241
    case FundamentalType::Encoding::kChar:
242
    case FundamentalType::Encoding::kSignedChar:
243
      return TYPEART_CHAR_8;
1,076✔
244
    case FundamentalType::Encoding::kUnsignedChar:
245
      return TYPEART_UCHAR_8;
12✔
246
    case FundamentalType::Encoding::kBool:
247
      return TYPEART_BOOL;
108✔
248
    case FundamentalType::Encoding::kUnsignedInt: {
249
      switch (extent) {
2,529✔
250
        case 4:
251
          return TYPEART_UINT_32;
1,058✔
252
        case 8:
253
          return TYPEART_UINT_64;
1,459✔
254
        case 2:
255
          return TYPEART_UINT_16;
12✔
256
        case 1:
257
          return TYPEART_UINT_8;
×
258
        case 16:
259
          return TYPEART_UINT_128;
×
260
        default:
261
          return TYPEART_UNKNOWN_TYPE;
×
262
      }
263
    }
264
    case FundamentalType::Encoding::kSignedInt: {
265
      switch (extent) {
2,461✔
266
        case 4:
267
          return TYPEART_INT_32;
2,347✔
268
        case 8:
269
          return TYPEART_INT_64;
78✔
270
        case 2:
271
          return TYPEART_INT_16;
36✔
272
        case 1:
273
          return TYPEART_INT_8;
×
274
        case 16:
275
          return TYPEART_INT_128;
×
276
        default:
277
          return TYPEART_UNKNOWN_TYPE;
×
278
      }
279
    }
280
    case FundamentalType::Encoding::kComplex: {
281
      switch (extent) {
×
282
        case 2:
283
          return TYPEART_COMPLEX_64;
×
284
        case 4:
285
          return TYPEART_COMPLEX_128;
×
286
        case 1:
287
          return TYPEART_COMPLEX_256;
×
288
        default:
289
          return TYPEART_UNKNOWN_TYPE;
×
290
      }
291
    }
292
    case FundamentalType::Encoding::kFloat: {
293
      switch (extent) {
605✔
294
        case 4:
295
          return TYPEART_FLOAT_32;
226✔
296
        case 8:
297
          return TYPEART_FLOAT_64;
379✔
298
        case 2:
299
          return TYPEART_FLOAT_16;
×
300
        case 16:
301
          return TYPEART_FLOAT_128;
×
302
        default:
303
          return TYPEART_UNKNOWN_TYPE;
×
304
      }
305
    }
306
    default:
307
      break;
×
308
  }
309

310
  return TYPEART_UNKNOWN_TYPE;
×
311
}
8,173✔
312

313
class DimetaTypeManager final : public TypeIDGenerator {
314
 public:
315
  // explicit DimetaTypeManager(std::string file_) : TypeIDGenerator(std::move(file_)) {
316
  // }
317

318
  using TypeIDGenerator::TypeIDGenerator;
319

320
  std::optional<int> fetch_id(const std::string& name) {
5,821✔
321
    if (auto it = structMap.find(name); it != structMap.end()) {
5,821✔
322
      const auto type_id = it->second;
3,676✔
323
      return type_id;
3,676✔
324
    }
325
    return {};
2,145✔
326
  }
5,821✔
327

328
  int registerVectorType(const dimeta::QualifiedFundamental& f, bool top_level) {
84✔
329
    LOG_DEBUG("Vector-like type found")
330
    assert(!f.typedef_name.empty() && "Vector types need to be typedef'ed.");
168✔
331

332
    const auto vec_name    = f.typedef_name;
84✔
333
    const auto existing_id = fetch_id(vec_name);
84✔
334
    if (existing_id) {
84✔
335
      LOG_DEBUG("Registered type found with id " << existing_id.value())
336
      return existing_id.value();
48✔
337
    }
338

339
    const int id = reserveNextTypeId();
36✔
340
    StructTypeInfo struct_info;
36✔
341
    struct_info.type_id     = id;
36✔
342
    struct_info.name        = vec_name;
36✔
343
    const auto num_elements = vector_num_elements(f);
36✔
344
    struct_info.extent      = f.vector_size;  // f.type.extent * array_size_factor;
36✔
345

346
    const auto vec_member_id = get_builtin_typeid(f, top_level).value();
36✔
347
    // FIXME assume vector offsets are "packed":
348
    for (std::uint64_t i = 0; i < num_elements; ++i) {
186✔
349
      struct_info.offsets.push_back(i * f.type.extent);
150✔
350
      struct_info.array_sizes.push_back(1);
150✔
351
      struct_info.member_types.push_back(vec_member_id);
150✔
352
    }
150✔
353

354
    struct_info.num_members = num_elements;
36✔
355
    struct_info.flag        = StructTypeFlag::LLVM_VECTOR;
36✔
356

357
    LOG_DEBUG("Registering vector-like type with id " << id)
358
    typeDB->registerStruct(struct_info);
36✔
359
    structMap.insert({struct_info.name, id});
36✔
360

361
    return id;
36✔
362
  }
84✔
363

364
  int registerStructType(const dimeta::QualifiedCompound& q, bool top_level) {
17,586✔
365
    const std::string name_or_typedef = name_or_typedef_of(q);
17,586✔
366
    LOG_DEBUG("QualifiedCompound \"" << name_or_typedef << "\"");
367
    using namespace dimeta;
368
    if (q.type.name.empty() && q.type.type == CompoundType::Tag::kUnknown) {
17,586✔
369
      LOG_DEBUG("Potentially pointer to (member) function, skipping.")
370
      return TYPEART_UNKNOWN_TYPE;
222✔
371
    }
372

373
    const auto& compound            = q.type;
17,364✔
374
    const auto [ptr_type_id, count] = typeid_if_ptr(q);
22,919✔
375

376
    if ((top_level && count > 1) || (!top_level && count > 0)) {
5,890✔
377
      // First: if top level allocation then the first pointer can be ignored, but
378
      // the second is not. Second: if it's not a top level allocation, we assume
379
      // it's a pointer type
380
      return ptr_type_id.value();
5,890✔
381
    }
382

383
    bool is_forward_declaration = false;
×
384
    const auto existing_id      = fetch_id(name_or_typedef);
×
385
    if (existing_id) {
5,737✔
386
      const auto* struct_info = typeDB->getStructInfo(existing_id.value());
3,628✔
387
      if (struct_info->flag == StructTypeFlag::FWD_DECL) {
3,628✔
388
        is_forward_declaration = true;
40✔
389
      } else {
40✔
390
        return existing_id.value();
3,588✔
391
      }
392
    }
40✔
393

394
    const int id = is_forward_declaration ? existing_id.value() : reserveNextTypeId();
2,149✔
395
    StructTypeInfo struct_info;
2,149✔
396
    struct_info.type_id = id;
2,149✔
397
    struct_info.name    = name_or_typedef;
2,149✔
398
    if (q.is_forward_decl) {
2,149✔
399
      struct_info.flag = StructTypeFlag::FWD_DECL;
78✔
400
    } else {
78✔
401
      struct_info.flag = StructTypeFlag::USER_DEFINED;
2,071✔
402
    }
403

404
    struct_info.extent      = compound.extent;
2,149✔
405
    struct_info.offsets     = compound.offsets;
2,149✔
406
    struct_info.num_members = compound.bases.size() + compound.members.size();
2,149✔
407

408
    for (const auto& base : compound.bases) {
2,773✔
409
      struct_info.member_types.push_back(getOrRegister(base->base));
624✔
410
      struct_info.array_sizes.push_back(array_size(base->base));
624✔
411
    }
412
    for (const auto& member : compound.members) {
5,934✔
413
      struct_info.member_types.push_back(getOrRegister(member->member));
3,785✔
414
      struct_info.array_sizes.push_back(array_size(member->member));
3,785✔
415
    }
416

417
    LOG_DEBUG("Registering struct-like type with id " << id)
418
    typeDB->registerStruct(struct_info, is_forward_declaration);
2,149✔
419
    structMap.insert({struct_info.name, id});
2,149✔
420

421
    return id;
2,149✔
422
  }
29,060✔
423

424
  int getOrRegister(const dimeta::QualifiedType& type, bool top_level = false) {
14,333✔
425
    auto type_id = std::visit(
14,333✔
426
        overload{[&](const dimeta::QualifiedFundamental& f) -> int {
22,554✔
427
                   LOG_DEBUG("QualifiedFundamental \"" << f.type.name << "\"");
428
                   if (f.is_vector) {
8,221✔
429
                     return registerVectorType(f, top_level);
84✔
430
                   }
431
                   return get_builtin_typeid(f, top_level).value();
8,137✔
432
                 },
8,221✔
433
                 [&](const dimeta::QualifiedCompound& q) -> int { return registerStructType(q, top_level); }},
20,445✔
434
        type);
14,333✔
435

436
    LOG_DEBUG("Returning type-id " << type_id);
437
    return type_id;
14,333✔
438
  }
439

440
  [[nodiscard]] TypeIdentifier getOrRegisterTypeValue(llvm::Value* type) {
8,770✔
441
    if (auto call = llvm::dyn_cast<llvm::CallBase>(type)) {
8,770✔
442
      // LOG_DEBUG(*type)
443
      auto val = dimeta::located_type_for(call);
889✔
444

445
      if (val) {
889✔
446
        LOG_DEBUG("Registering malloc-like")
447

448
        return {getOrRegister(val->type, true), array_size(val->type)};
877✔
449
      }
450
    } else if (auto* alloc = llvm::dyn_cast<llvm::AllocaInst>(type)) {
8,770✔
451
      LOG_DEBUG("Alloca found")
452
      auto val = dimeta::located_type_for(alloc);
4,856✔
453
      if (val) {
4,856✔
454
        LOG_DEBUG("Registering alloca")
455
        workaround::remove_pointer_level(alloc, val.value());
3,825✔
456
        const auto type_id        = getOrRegister(val->type, false);
3,825✔
457
        const auto array_size_val = array_size(val->type);
3,825✔
458
        LOG_DEBUG(array_size_val)
459
        return {type_id, array_size_val};
3,825✔
460
      }
461
    } else if (auto* global = llvm::dyn_cast<llvm::GlobalVariable>(type)) {
7,881✔
462
      auto val = dimeta::located_type_for(global);
3,025✔
463
      if (val) {
3,025✔
464
        LOG_DEBUG("Registering global")
465

466
        return {getOrRegister(val->type, true), array_size(val->type)};
1,006✔
467
      }
468
    }
3,025✔
469
    return {TYPEART_UNKNOWN_TYPE, 0};
3,062✔
470
  }
8,770✔
471

472
  void registerModule(const ModuleData& module) override {
1,646✔
473
    using namespace dimeta;
474
    // std::optional<CompileUnitTypeList> compile_unit_types(const llvm::Module*)
475
    LOG_DEBUG("Register module types")
476
    auto cu_types_list = dimeta::compile_unit_types(module.module).value_or(dimeta::CompileUnitTypeList{});
1,646✔
477

478
    for (const auto& cu : cu_types_list) {
3,286✔
479
      const QualifiedTypeList& list = cu.types;
1,640✔
480
      for (const auto& cu_type : list) {
5,856✔
481
        getOrRegister(cu_type);
4,216✔
482
      }
483
    }
484
    LOG_DEBUG("Done: Register module types")
485
  }
1,646✔
486

487
  TypeIdentifier getOrRegisterType(const MallocData& data) override {
889✔
488
    // LOG_INFO("Start register malloc-like \"" << *data.call << "\"")
489
    return getOrRegisterTypeValue(data.call);
889✔
490
  }
491

492
  TypeIdentifier getOrRegisterType(const AllocaData& data) override {
4,856✔
493
    // LOG_INFO("Start register alloca \"" << *data.alloca << "\"")
494
    const auto alloc_type = getOrRegisterTypeValue(data.alloca);
4,856✔
495
    return {alloc_type.type_id, alloc_type.num_elements};
4,856✔
496
  }
497

498
  TypeIdentifier getOrRegisterType(const GlobalData& data) override {
3,025✔
499
    // LOG_INFO("Start register global \"" << *data.global << IR"\"")
500
    return getOrRegisterTypeValue(data.global);
3,025✔
501
  }
502

503
  ~DimetaTypeManager() = default;
3,292✔
504
};
505

506
std::unique_ptr<typeart::TypeGenerator> make_dimeta_typeidgen(std::string_view file,
1,646✔
507
                                                              std::unique_ptr<TypeDatabase> database_of_types) {
508
  return std::make_unique<typeart::types::DimetaTypeManager>(std::string{file}, std::move(database_of_types));
1,646✔
509
}
×
510

511
}  // namespace typeart::types
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc