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

tudasc / TypeART / 13887453896

16 Mar 2025 09:15PM UTC coverage: 88.87% (-0.005%) from 88.875%
13887453896

Pull #165

github

web-flow
Merge 574ea8505 into c6563269e
Pull Request #165: LLVM 19 Support

19 of 20 new or added lines in 1 file covered. (95.0%)

1 existing line in 1 file now uncovered.

4200 of 4726 relevant lines covered (88.87%)

259542.25 hits per line

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

87.83
/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) {
21,511✔
55
  using namespace dimeta;
56
  const auto& quals = type.qual;
21,511✔
57
  int count{0};
21,511✔
58
  if (!quals.empty()) {
21,511✔
59
    count = llvm::count_if(quals, [](auto& qual) { return qual == Qualifier::kPtr || qual == Qualifier::kRef; });
15,374✔
60
    if (count > 0) {
6,665✔
61
      return {{TYPEART_POINTER}, count};
3,752✔
62
    }
63
  }
2,913✔
64
  return {{}, count};
17,759✔
65
}
21,511✔
66

67
namespace detail {
68

69
template <typename Type, typename Func>
70
auto apply_function(const Type& type, Func&& handle_qualified_type) {
40,117✔
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);
25,168✔
76
  } else {
77
    return std::visit(
14,949✔
78
        [&](auto&& qualified_type) {
29,898✔
79
          return apply_function(qualified_type, std::forward<Func>(handle_qualified_type));
14,949✔
80
        },
81
        type);
14,949✔
82
  }
83
}
84

85
}  // namespace detail
86

87
namespace workaround {
88
void remove_pointer_level(const llvm::AllocaInst* alloc, dimeta::LocatedType& val) {
5,974✔
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()) {
5,974✔
93
    LOG_DEBUG("Alloca is not a pointer")
94

95
    const auto remove_pointer_level = [](auto& qual) {
5,818✔
96
      auto pointer_like_iter = llvm::find_if(qual, [](auto qualifier) {
6,952✔
97
        switch (qualifier) {
1,134✔
98
          case dimeta::Qualifier::kPtr:
99
          case dimeta::Qualifier::kRef:
100
          case dimeta::Qualifier::kPtrToMember:
101
            return true;
183✔
102
          default:
103
            break;
951✔
104
        }
105
        return false;
951✔
106
      });
1,134✔
107
      if (pointer_like_iter != std::end(qual)) {
5,818✔
108
        LOG_DEBUG("Removing pointer level " << static_cast<int>(*pointer_like_iter))
109
        qual.erase(pointer_like_iter);
183✔
110
      }
183✔
111
    };
5,818✔
112
    std::visit([&](auto&& qualified_type) { remove_pointer_level(qualified_type.qual); }, val.type);
11,636✔
113
  }
5,818✔
114
}
5,974✔
115
}  // namespace workaround
116

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

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

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

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

182
    return array_size_factor;
15,609✔
183
  });
15,654✔
184
}
185

186
template <typename Type>
187
std::string name_or_typedef_of(const Type& type) {
9,460✔
188
  return detail::apply_function(type, [](const auto& qual_type) {
18,920✔
189
    const bool no_name = qual_type.type.name.empty();
9,460✔
190
    if constexpr (std::is_same_v<Type, typename dimeta::QualifiedCompound>) {
191
      const bool no_identifier = qual_type.type.identifier.empty();
9,460✔
192
      const bool no_typedef    = qual_type.typedef_name.empty();
9,460✔
193
      if (no_identifier && no_name && no_typedef) {
9,460✔
194
        return get_anon_struct_identifier(qual_type);
399✔
195
      }
196
      if (no_identifier && no_name) {
9,061✔
197
        return qual_type.typedef_name;
83✔
198
      }
199
      if (no_identifier) {
8,978✔
200
        return qual_type.type.name;
1,173✔
201
      }
202
      return qual_type.type.identifier;
7,805✔
203
    }
204

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

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

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

219
  const auto extent   = type.type.extent;
10,107✔
220
  const auto encoding = type.type.encoding;
10,107✔
221

222
  switch (encoding) {
10,107✔
223
    case FundamentalType::Encoding::kUnknown:
224
      return TYPEART_UNKNOWN_TYPE;
×
225
    case FundamentalType::Encoding::kVoid:
226
      return TYPEART_VOID;
105✔
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:
NEW
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;
2,076✔
244
    case FundamentalType::Encoding::kUnsignedChar:
245
      return TYPEART_UCHAR_8;
27✔
246
    case FundamentalType::Encoding::kBool:
247
      return TYPEART_BOOL;
114✔
248
    case FundamentalType::Encoding::kUnsignedInt: {
249
      switch (extent) {
3,365✔
250
        case 4:
251
          return TYPEART_UINT_32;
1,515✔
252
        case 8:
253
          return TYPEART_UINT_64;
1,832✔
254
        case 2:
255
          return TYPEART_UINT_16;
18✔
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) {
3,470✔
266
        case 4:
267
          return TYPEART_INT_32;
3,299✔
268
        case 8:
269
          return TYPEART_INT_64;
117✔
270
        case 2:
271
          return TYPEART_INT_16;
54✔
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) {
950✔
294
        case 4:
295
          return TYPEART_FLOAT_32;
340✔
296
        case 8:
297
          return TYPEART_FLOAT_64;
610✔
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
}
12,369✔
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) {
9,043✔
321
    if (auto it = structMap.find(name); it != structMap.end()) {
9,043✔
322
      const auto type_id = it->second;
5,476✔
323
      return type_id;
5,476✔
324
    }
325
    return {};
3,567✔
326
  }
9,043✔
327

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

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

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

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

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

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

361
    return id;
54✔
362
  }
126✔
363

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

373
    const auto& compound            = q.type;
26,976✔
374
    const auto [ptr_type_id, count] = typeid_if_ptr(q);
35,614✔
375

376
    if ((top_level && count > 1) || (!top_level && count > 0)) {
9,142✔
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();
9,142✔
381
    }
382

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

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

404
    struct_info.extent = compound.extent;
3,557✔
405

406
    size_t num_bases{0};
3,557✔
407
    for (const auto& base : compound.bases) {
4,556✔
408
      if (base->is_empty_base_class) {
999✔
409
        continue;
294✔
410
      }
411
      num_bases++;
705✔
412
      struct_info.member_types.push_back(getOrRegister(base->base));
705✔
413
      struct_info.array_sizes.push_back(array_size(base->base));
705✔
414
      struct_info.offsets.push_back(base->offset);
705✔
415
    }
416

417
    struct_info.num_members = compound.members.size() + num_bases;
3,557✔
418
    struct_info.offsets.insert(std::end(struct_info.offsets),  //
3,557✔
419
                               std::begin(compound.offsets),   //
3,557✔
420
                               std::end(compound.offsets));
3,557✔
421

422
    for (const auto& member : compound.members) {
9,302✔
423
      struct_info.member_types.push_back(getOrRegister(member->member));
5,745✔
424
      struct_info.array_sizes.push_back(array_size(member->member));
5,745✔
425
    }
426

427
    LOG_DEBUG("Registering struct-like type with id " << id)
428
    typeDB->registerStruct(struct_info, is_forward_declaration);
3,557✔
429
    structMap.insert({struct_info.name, id});
3,557✔
430

431
    return id;
3,557✔
432
  }
45,128✔
433

434
  int getOrRegister(const dimeta::QualifiedType& type, bool top_level = false) {
21,901✔
435
    auto type_id = std::visit(
21,901✔
436
        overload{[&](const dimeta::QualifiedFundamental& f) -> int {
34,342✔
437
                   LOG_DEBUG("QualifiedFundamental \"" << f.type.name << "\"");
438
                   if (f.is_vector) {
12,441✔
439
                     return registerVectorType(f, top_level);
126✔
440
                   }
441
                   return get_builtin_typeid(f, top_level).value();
12,315✔
442
                 },
12,441✔
443
                 [&](const dimeta::QualifiedCompound& q) -> int { return registerStructType(q, top_level); }},
31,361✔
444
        type);
21,901✔
445

446
    LOG_DEBUG("Returning type-id " << type_id);
447
    return type_id;
21,901✔
448
  }
449

450
  [[nodiscard]] TypeIdentifier getOrRegisterTypeValue(llvm::Value* type) {
13,501✔
451
    if (auto call = llvm::dyn_cast<llvm::CallBase>(type)) {
13,501✔
452
      // LOG_DEBUG(*type)
453
      auto val = dimeta::located_type_for(call);
1,310✔
454

455
      if (val) {
1,310✔
456
        LOG_DEBUG("Registering malloc-like")
457

458
        return {getOrRegister(val->type, true), array_size(val->type)};
1,292✔
459
      }
460
    } else if (auto* alloc = llvm::dyn_cast<llvm::AllocaInst>(type)) {
13,501✔
461
      LOG_DEBUG("Alloca found")
462
      auto val = dimeta::located_type_for(alloc);
7,500✔
463
      if (val) {
7,500✔
464
        LOG_DEBUG("Registering alloca")
465
        workaround::remove_pointer_level(alloc, val.value());
5,974✔
466
        const auto type_id        = getOrRegister(val->type, false);
5,974✔
467
        const auto array_size_val = array_size(val->type);
5,974✔
468
        LOG_DEBUG(array_size_val)
469
        return {type_id, array_size_val};
5,974✔
470
      }
471
    } else if (auto* global = llvm::dyn_cast<llvm::GlobalVariable>(type)) {
12,191✔
472
      auto val = dimeta::located_type_for(global);
4,691✔
473
      if (val) {
4,691✔
474
        LOG_DEBUG("Registering global")
475

476
        return {getOrRegister(val->type, true), array_size(val->type)};
1,938✔
477
      }
478
    }
4,691✔
479
    return {TYPEART_UNKNOWN_TYPE, 0};
4,297✔
480
  }
13,501✔
481

482
  void registerModule(const ModuleData& module) override {
2,378✔
483
    using namespace dimeta;
484
    // std::optional<CompileUnitTypeList> compile_unit_types(const llvm::Module*)
485
    LOG_DEBUG("Register module types")
486
    auto cu_types_list = dimeta::compile_unit_types(module.module).value_or(dimeta::CompileUnitTypeList{});
2,378✔
487

488
    for (const auto& cu : cu_types_list) {
4,747✔
489
      const QualifiedTypeList& list = cu.types;
2,369✔
490
      for (const auto& cu_type : list) {
8,616✔
491
        getOrRegister(cu_type);
6,247✔
492
      }
493
    }
494
    LOG_DEBUG("Done: Register module types")
495
  }
2,378✔
496

497
  TypeIdentifier getOrRegisterType(const MallocData& data) override {
1,310✔
498
    // LOG_INFO("Start register malloc-like \"" << *data.call << "\"")
499
    return getOrRegisterTypeValue(data.call);
1,310✔
500
  }
501

502
  TypeIdentifier getOrRegisterType(const AllocaData& data) override {
7,500✔
503
    // LOG_INFO("Start register alloca \"" << *data.alloca << "\"")
504
    const auto alloc_type = getOrRegisterTypeValue(data.alloca);
7,500✔
505
    return {alloc_type.type_id, alloc_type.num_elements};
7,500✔
506
  }
507

508
  TypeIdentifier getOrRegisterType(const GlobalData& data) override {
4,691✔
509
    // LOG_INFO("Start register global \"" << *data.global << IR"\"")
510
    return getOrRegisterTypeValue(data.global);
4,691✔
511
  }
512

513
  ~DimetaTypeManager() = default;
4,756✔
514
};
515

516
std::unique_ptr<typeart::TypeGenerator> make_dimeta_typeidgen(std::string_view file,
2,378✔
517
                                                              std::unique_ptr<TypeDatabase> database_of_types) {
518
  return std::make_unique<typeart::types::DimetaTypeManager>(std::string{file}, std::move(database_of_types));
2,378✔
519
}
×
520

521
}  // 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