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

ahueck / llvm-dimeta / 23296997048

19 Mar 2026 01:23PM UTC coverage: 64.594% (-9.0%) from 73.626%
23296997048

Pull #49

github

web-flow
Merge f45516abb into cefb3414f
Pull Request #49: Initial Fortran Support

1854 of 3670 branches covered (50.52%)

Branch coverage included in aggregate %.

164 of 435 new or added lines in 15 files covered. (37.7%)

20 existing lines in 6 files now uncovered.

2333 of 2812 relevant lines covered (82.97%)

11668.39 hits per line

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

74.53
/lib/type/DimetaParse.cpp
1
//  llvm-dimeta library
2
//  Copyright (c) 2022-2025 llvm-dimeta authors
3
//  Distributed under the BSD 3-Clause license.
4
//  (See accompanying file LICENSE)
5
//  SPDX-License-Identifier: BSD-3-Clause
6
//
7

8
#include "DimetaParse.h"
9

10
#include "DIParser.h"
11
#include "DIVisitor.h"
12
#include "DimetaData.h"
13
#include "support/Logger.h"
14

15
#include "llvm/ADT/STLExtras.h"
16

17
#include <iterator>
18
#include <llvm/BinaryFormat/Dwarf.h>
19
#include <llvm/IR/DebugInfoMetadata.h>
20
#include <numeric>  // for std::accumulate
21
#include <tuple>
22
#include <type_traits>
23

24
namespace dimeta::parser {
25
namespace helper {
26

27
template <typename T, typename... Args>
28
inline std::shared_ptr<Member> make_member(std::string_view name, Args&&... args) {
13,503✔
29
  static_assert(std::is_same_v<T, QualifiedCompound> || std::is_same_v<T, QualifiedFundamental>, "Wrong qualtype.");
30
  return std::make_shared<Member>(Member{name.data(), T{std::forward<Args>(args)...}});
13,503!
31
}
2,382✔
32

33
template <typename T>
34
inline std::shared_ptr<BaseClass> make_base(T&& compound) {  //, BaseClass::VTable&& vtable = {}) {
1,480✔
35
  static_assert(std::is_same_v<typename std::remove_cv<T>::type, QualifiedCompound>, "Need a qualified compound.");
36
  return std::make_shared<BaseClass>(
1,480!
37
      BaseClass{std::forward<T>(compound)});  //, std::forward<BaseClass::VTable>(vtable)});
1,480✔
38
}
262✔
39

40
inline CompoundType::Tag dwarf2compound(const llvm::dwarf::Tag tag) {
6,661✔
41
  using namespace llvm::dwarf;
42
  switch (tag) {
6,661!
43
    case DW_TAG_enumeration_type:
44
      return dimeta::CompoundType::Tag::kEnum;
33✔
45
    case DW_TAG_class_type:
46
      return dimeta::CompoundType::Tag::kClass;
1,973✔
47
    case DW_TAG_structure_type:
48
      return dimeta::CompoundType::Tag::kStruct;
4,644✔
49
    case DW_TAG_union_type:
50
      return dimeta::CompoundType::Tag::kUnion;
11✔
51
    default:
52
      return dimeta::CompoundType::Tag::kUnknown;
×
53
  }
54
}
6,661✔
55

56
inline FundamentalType::Encoding dwarf2encoding(const unsigned di_encoding) {
16,249✔
57
  using namespace llvm::dwarf;
58
  switch (di_encoding) {
16,249!
59
    case DW_ATE_float:
60
      return FundamentalType::Encoding::kFloat;
4,876✔
61
    case DW_ATE_signed:
62
      return FundamentalType::Encoding::kSignedInt;
9,239✔
63
    case DW_ATE_unsigned:
64
      return FundamentalType::Encoding::kUnsignedInt;
635✔
65
    case DW_ATE_unsigned_char:
66
      return FundamentalType::Encoding::kUnsignedChar;
157✔
67
    case DW_ATE_signed_char:
68
      return FundamentalType::Encoding::kSignedChar;
1,192✔
69
    case DW_ATE_boolean:
70
      return FundamentalType::Encoding::kBool;
84✔
71
    case DW_ATE_UTF:
72
      return FundamentalType::Encoding::kUTFChar;
33✔
73
    case DW_ATE_complex_float:
74
      return FundamentalType::Encoding::kComplex;
33✔
75
    default:
76
      return FundamentalType::Encoding::kUnknown;
×
77
  }
78
}
16,249✔
79

80
inline Qualifier dwarf2qual(unsigned tag) {
28,636✔
81
  using namespace llvm::dwarf;
82
  switch (tag) {
28,636!
83
    case DW_TAG_pointer_type:
84
      return Qualifier::kPtr;
23,490✔
85
    case DW_TAG_reference_type:
86
      return Qualifier::kRef;
712✔
87
    case DW_TAG_const_type:
88
      return Qualifier::kConst;
820✔
89
    case DW_TAG_ptr_to_member_type:
90
      return Qualifier::kPtrToMember;
88✔
91
    case DW_TAG_array_type:
92
      return Qualifier::kArray;
3,306✔
93
    case diparser::state::CustomDwarfTag::kVector:
94
      return Qualifier::kVector;
220✔
95
    default:
96
      return Qualifier::kNone;
×
97
  }
98
}
28,636✔
99

100
inline Qualifiers make_qualifiers(const llvm::SmallVector<unsigned, 8>& tag_collector) {
24,338✔
101
  llvm::SmallVector<unsigned, 8> dwarf_quals;
24,338✔
102
  llvm::copy_if(tag_collector, std::back_inserter(dwarf_quals), [&](const auto& tag) {
38,656!
103
    const auto tag_qual = helper::dwarf2qual(tag);
14,318✔
104
    return tag_qual != Qualifier::kNone;
28,636✔
105
  });
14,318✔
106

107
  Qualifiers quals;
24,338✔
108
  quals.reserve(tag_collector.size());
24,338!
109
  llvm::transform(dwarf_quals, std::back_inserter(quals), [&](const auto& tag) -> Qualifier {
38,656!
110
    const auto tag_qual = helper::dwarf2qual(tag);
14,318✔
111
    return Qualifier{tag_qual};
28,636✔
112
  });
14,318✔
113

114
  return quals;
24,338✔
115
}
24,338!
116

117
template <typename Type>
118
inline ArraySizeList make_array_sizes(const Type&,
24,338✔
119
                                      const std::vector<diparser::state::MetaData::ArrayData>& meta_array_data) {
120
  ArraySizeList list;
24,338✔
121
  if (meta_array_data.empty()) {
24,338✔
122
    return list;
22,619✔
123
  }
124
  // const auto array_size_calc = [&type](const diparser::state::MetaData::ArrayData& array, bool is_last) {
125
  //   const auto array_byte_size = (array.array_size_bits / 8);
126
  //   // is an array of pointers:
127
  //   if (array.array_of_pointer > 0) {
128
  //     return array_byte_size / (array.array_of_pointer / 8);
129
  //   }
130
  //   // is an array of the "type":
131
  //   if (is_last && type.extent > 0) {
132
  //     return array_byte_size / type.extent;
133
  //   }
134
  //   return array_byte_size;
135
  // };
136
  const auto array_size_calc_sub = [](const diparser::state::MetaData::ArrayData& array, bool) {
3,482✔
137
    // LOG_FATAL(array.subranges.size());
138
    ArraySize sum =
3,526✔
139
        std::accumulate(array.subranges.begin(), array.subranges.end(), ArraySize{1}, std::multiplies<ArraySize>());
1,763✔
140
    return sum;
3,526✔
141
  };
1,763✔
142

143
  for (auto it = meta_array_data.begin(); it != meta_array_data.end(); ++it) {
3,482✔
144
    const auto& array          = *it;
1,763✔
145
    const bool is_last_element = (it == std::prev(std::end(meta_array_data)));
1,763!
146
    const auto size            = array_size_calc_sub(array, is_last_element);
1,763!
147
    list.emplace_back(size);
1,763!
148
    // LOG_FATAL("Array: " << size);
149
  }
1,763✔
150

151
  return list;
1,719✔
152
}
24,338!
153

154
template <typename T>
155
inline QualType<T> make_qual_type(const T& type, const diparser::state::MetaData& meta_) {
24,338✔
156
  static_assert(std::is_same_v<T, CompoundType> || std::is_same_v<T, FundamentalType>, "Wrong type.");
157

158
  Qualifiers quals = helper::make_qualifiers(meta_.dwarf_tags);
24,338✔
159
  if (meta_.is_member_static) {
24,338✔
160
    // TODO should this be the last, or should it be position dependent w.r.t. dwarf_tags?
161
    quals.emplace_back(Qualifier::kStatic);
246!
162
  }
246✔
163
  const auto array_size   = helper::make_array_sizes(type, meta_.arrays);
24,338!
164
  const auto typedef_name = meta_.typedef_names.empty() ? std::string{} : *meta_.typedef_names.begin();
24,338!
165

166
  Extent vec_size{0};
24,338✔
167
  for (const auto& array_data : meta_.arrays) {
26,101✔
168
    if (array_data.is_vector) {
1,763!
169
      assert(vec_size == 0 && "Multiple vectors detected in arrays.");
220!
170
      vec_size = array_data.array_size_bits / 8;
110✔
171
    }
110✔
172
  }
1,763✔
173

174
  return QualType<T>{
24,338✔
175
      type, array_size, quals, typedef_name, vec_size, meta_.is_vector, meta_.is_forward_decl, meta_.is_recurring};
24,338!
176
}
24,338✔
177

178
inline CompoundType make_compound(const llvm::DICompositeType* composite_type) {
6,661✔
179
  auto compound =
6,661✔
180
      [](llvm::StringRef compound_name, llvm::StringRef compound_identifier, llvm::dwarf::Tag tag,
19,983✔
181
         Extent size_in_bits) {
182
        return CompoundType{std::string{compound_name}, std::string{compound_identifier}, dwarf2compound(tag),
6,661!
183
                            size_in_bits / 8};
39,966✔
184
      }(composite_type->getName(), composite_type->getIdentifier(),
7,819✔
185
        static_cast<llvm::dwarf::Tag>(composite_type->getTag()), composite_type->getSizeInBits());
6,661✔
186
  return compound;
6,661✔
187
}
6,661!
188

189
inline QualifiedCompound make_qualified_compound(const diparser::state::MetaData& meta_) {
6,661✔
190
  return make_qual_type<CompoundType>(make_compound(llvm::dyn_cast<llvm::DICompositeType>(meta_.type)), meta_);
6,661!
191
}
1,158✔
192

193
QualifiedFundamental make_qualified_fundamental(const diparser::state::MetaData& meta_, std::string_view name,
17,677✔
194
                                                FundamentalType::Encoding encoding) {
195
  const auto size = [&]() {
35,354✔
196
    auto size = (meta_.type->getSizeInBits() / 8);
17,677✔
197
    if (size == 0) {
17,677✔
198
      if (encoding == FundamentalType::Encoding::kNullptr || encoding == FundamentalType::Encoding::kFunctionPtr) {
394!
199
        // sizeof std::nullptr == sizeof void*
200
        size =
394✔
201
            meta_.member_size > 0 ? meta_.member_size : (meta_.derived_size > 0 ? meta_.derived_size : sizeof(void*));
394✔
202
      } else if (encoding == FundamentalType::Encoding::kString) {
394!
203
        // TODO: fortran, each character is 1 byte of storage (?)
NEW
204
        size = 1;
×
UNCOV
205
      }
×
206
    }
394✔
207
    return size;
35,354✔
208
  }();
17,677✔
209
  auto fundamental = FundamentalType{std::string{name}, size, encoding};
17,677!
210
  return make_qual_type<FundamentalType>(fundamental, meta_);
17,677!
211
}
17,677✔
212

213
}  // namespace helper
214

215
class DITypeParser final : public diparser::DIParseEvents {
9,049✔
216
  using CompoundStack = llvm::SmallVector<QualifiedCompound, 4>;
217
  DimetaParseResult result_;
218
  CompoundStack composite_stack_;
219

220
 public:
221
  [[nodiscard]] const DimetaParseResult& getParsedType() const {
9,049✔
222
    return result_;
9,049✔
223
  }
224

225
  template <typename QualType>
226
  void emplace_result(QualType&& type) {
9,355✔
227
    result_.type_.emplace<QualType>(std::forward<QualType>(type));
9,355✔
228
  }
9,355✔
229

230
  template <typename QualType>
231
  void emplace_member(QualType&& type, const diparser::state::MetaData& meta_) {
13,503✔
232
    static_assert(std::is_same_v<QualType, QualifiedCompound> || std::is_same_v<QualType, QualifiedFundamental>,
233
                  "Wrong QualType for member.");
234
    assert(!composite_stack_.empty() && "Member requires composite on stack");
27,006!
235
    auto& containing_composite = composite_stack_.back().type;
13,503✔
236
    if (meta_.is_member_static) {
13,503✔
237
      containing_composite.static_members.emplace_back(
492!
238
          helper::make_member<QualType>(meta_.member_name, std::forward<QualType>(type)));
246✔
239
    } else {
246✔
240
      containing_composite.offsets.emplace_back(meta_.member_offset);
13,257✔
241
      containing_composite.sizes.emplace_back(meta_.member_size);
13,257✔
242
      containing_composite.members.emplace_back(
26,514!
243
          helper::make_member<QualType>(meta_.member_name, std::forward<QualType>(type)));
13,257✔
244
    }
245
  }
13,503✔
246

247
  void emplace_fundamental(const diparser::state::MetaData& meta_, std::string_view name,
17,677✔
248
                           FundamentalType::Encoding encoding = FundamentalType::kUnknown) {
249
    auto qual_type_fundamental = helper::make_qualified_fundamental(meta_, name, encoding);
17,677✔
250

251
    if (meta_.is_member) {
17,677✔
252
      emplace_member(std::move(qual_type_fundamental), meta_);
11,495!
253
      return;
11,495✔
254
    }
255

256
    emplace_result<QualifiedFundamental>(std::move(qual_type_fundamental));
6,182!
257
  }
17,677!
258

259
  void make_fundamental(const diparser::state::MetaData& meta_) override {
16,205✔
260
    const auto* basic_type = llvm::dyn_cast<llvm::DIBasicType>(meta_.type);
16,205✔
261
    assert(basic_type != nullptr && "DIBasicType should not be null at this point");
32,410!
262
    const auto name = basic_type->getName();
16,205✔
263

264
    const auto encoding = (!name.empty() && name.contains("nullptr"))
16,205!
265
                              ? FundamentalType::Encoding::kNullptr
266
                              : helper::dwarf2encoding(basic_type->getEncoding());
16,183✔
267

268
    emplace_fundamental(meta_, basic_type->getName(), encoding);
16,205✔
269
  }
16,205✔
270

271
  void make_function_ptr(const diparser::state::MetaData& meta_) override {
372✔
272
    emplace_fundamental(meta_, "", FundamentalType::kFunctionPtr);
372✔
273
  }
372✔
274

275
  void make_void_ptr(const diparser::state::MetaData& meta_) override {
706✔
276
    const auto* derived_type = llvm::dyn_cast<llvm::DIDerivedType>(meta_.type);
706✔
277
    assert(derived_type != nullptr && "Type void* should be a derived type");
1,412!
278
    emplace_fundamental(meta_, "void", FundamentalType::Encoding::kVoid);
706✔
279
  }
706✔
280

NEW
281
  void make_string(const diparser::state::MetaData& meta_) override {
×
NEW
282
    const auto* string_type = llvm::dyn_cast<llvm::DIStringType>(meta_.type);
×
NEW
283
    assert(string_type != nullptr && "Type should be a string type");
×
NEW
284
    auto name = string_type->getName();
×
NEW
285
    emplace_fundamental(meta_, name.empty() ? "character" : name, FundamentalType::Encoding::kString);
×
NEW
286
  }
×
287

288
  void make_vtable(const diparser::state::MetaData& meta_) override {
328✔
289
    const auto* derived_type = llvm::dyn_cast<llvm::DIDerivedType>(meta_.type);
328✔
290
    assert(derived_type != nullptr && "Vtable should be a derived type");
656!
291
    assert(meta_.is_member && "Vtable should be a member of composite");
656!
292
    emplace_fundamental(meta_, derived_type->getName(), FundamentalType::Encoding::kVtablePtr);
328✔
293
  }
328✔
294

295
  void make_enum_member(const diparser::state::MetaData& meta_) override {
66✔
296
    const auto* basic_type = llvm::dyn_cast<llvm::DIBasicType>(meta_.type);
66✔
297
    assert(basic_type != nullptr && "DIBasicType should not be null for enum value");
132!
298
    assert(!composite_stack_.empty() && "Requires a composite type on stack");
132!
299
    assert((composite_stack_.back().type.type == CompoundType::Tag::kEnumClass ||
132!
300
            composite_stack_.back().type.type == CompoundType::Tag::kEnum) &&
301
           "Requires a enum type on stack");
302

303
    emplace_fundamental(meta_, basic_type->getName(), helper::dwarf2encoding(basic_type->getEncoding()));
66✔
304

305
    auto& enum_type = composite_stack_.back().type;
66✔
306
    if (enum_type.sizes.size() > 1) {
66✔
307
      // emplace_fundamental adds enum value as "member" with offset in enum compound, but we want it to be "1" member
308
      // only:
309
      enum_type.sizes.erase(std::next(std::begin(enum_type.sizes)), std::end(enum_type.sizes));
44✔
310
    }
44✔
311
    if (enum_type.offsets.size() > 1) {
66✔
312
      // emplace_fundamental adds enum value as "member" with offset in enum compound, but we want it to be "1" member
313
      // only:
314
      enum_type.offsets.erase(std::next(std::begin(enum_type.offsets)), std::end(enum_type.offsets));
44✔
315
    }
44✔
316
  }
66✔
317

318
  void make_composite(const diparser::state::MetaData& meta_) override {
6,661✔
319
    assert(llvm::dyn_cast<llvm::DICompositeType>(meta_.type) != nullptr);
6,661!
320

321
    const QualifiedCompound q_compound = helper::make_qualified_compound(meta_);
6,661✔
322
    composite_stack_.emplace_back(std::move(q_compound));
6,661!
323
  }
6,661✔
324

325
  void finalize_composite(const diparser::state::MetaData& current_meta) override {
6,661✔
326
    assert(!composite_stack_.empty() && "Requires a composite type on stack");
13,322!
327
    auto finalized_composite = composite_stack_.pop_back_val();
6,661✔
328

329
    if (current_meta.is_member) {
6,661✔
330
      emplace_member(std::move(finalized_composite), current_meta);
2,008!
331
      return;
2,008✔
332
    }
333

334
    if (current_meta.is_base_class) {
4,653✔
335
      const auto base          = helper::make_base(std::move(finalized_composite));
1,480!
336
      const bool size_one      = base->base.type.extent == 1;
1,480✔
337
      const bool empty_members = base->base.type.members.empty();
1,480✔
338
      // const bool ebo_base        = base->base.type.bases.size() > 1 &&
339
      // base->base.type.bases.front()->empty_base_class;
340
      base->is_empty_base_class  = size_one && empty_members;
1,480✔
341
      base->offset               = current_meta.member_offset;
1,480✔
342
      auto& containing_composite = composite_stack_.back().type;
1,480!
343
      // if (!base->is_empty_base_class) {
344
      //   containing_composite.offsets.emplace_back(current_meta.member_offset);
345
      //   containing_composite.sizes.emplace_back(finalized_composite.type.extent);
346
      // }
347
      containing_composite.bases.emplace_back(std::move(base));
1,480!
348
      return;
349
    }
1,480✔
350

351
    assert(composite_stack_.empty() && "Assumes top level compound here");
6,346!
352
    emplace_result<QualifiedCompound>(std::move(finalized_composite));
3,173!
353
  }
6,661!
354
};
355

356
std::optional<DimetaParseResult> make_dimetadata(const llvm::DINode* node) {
9,049✔
357
  if (!(llvm::isa<llvm::DIVariable>(node) || llvm::isa<llvm::DIType>(node))) {
9,049!
358
    return {};
×
359
  }
360
  DITypeParser parser;
9,049✔
361
  diparser::visit_node(node, parser);
9,049!
362
  return parser.getParsedType();
9,049!
363
}
9,049✔
364

365
}  // namespace dimeta::parser
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