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

ahueck / llvm-dimeta / 23299097010

19 Mar 2026 02:11PM UTC coverage: 73.206% (-0.4%) from 73.626%
23299097010

Pull #49

github

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

2176 of 3670 branches covered (59.29%)

Branch coverage included in aggregate %.

388 of 445 new or added lines in 15 files covered. (87.19%)

19 existing lines in 5 files now uncovered.

2578 of 2824 relevant lines covered (91.29%)

14323.15 hits per line

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

60.9
/lib/type/DIUtil.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 "DIUtil.h"
9

10
#include "DIVisitor.h"
11
#include "support/Logger.h"
12

13
#include "llvm/IR/DebugInfoMetadata.h"
14
#include "llvm/IR/Metadata.h"
15

16
#include <llvm/BinaryFormat/Dwarf.h>
17
#include <optional>
18

19
namespace dimeta::di::util {
20

21
namespace printer {
22

23
class DIPrinter : public visitor::DINodeVisitor<DIPrinter> {
24
 private:
25
  llvm::raw_ostream& outp_;
26
  std::optional<const llvm::Module*> module_;
27

28
  std::string no_pointer_str(const llvm::Metadata& type) {
104✔
29
    std::string view;
104✔
30
    llvm::raw_string_ostream rso(view);
104!
31
    type.print(rso, module_.value_or(nullptr));
104!
32

33
    if (module_) {
104!
34
      return rso.str();
104!
35
    }
36
    const llvm::StringRef ref(rso.str());
×
37
    const auto a_pos = ref.find("=");
×
38
    if (a_pos == llvm::StringRef::npos || (a_pos + 2) > ref.size()) {
×
39
      return ref.str();
×
40
    }
41

42
    return std::string{ref.substr(a_pos + 2)};
×
43
  }
104✔
44

45
  [[nodiscard]] unsigned width() const {
104✔
46
    return depth() == 1 ? 0 : depth();
104✔
47
  }
48

49
 public:
50
  explicit DIPrinter(llvm::raw_ostream& outp, const llvm::Module* mod = nullptr) : outp_(outp), module_(mod) {
13✔
51
  }
13✔
52

53
  bool visitVariable(const llvm::DIVariable* var) {
13✔
54
    outp_ << llvm::left_justify("", width()) << no_pointer_str(*var) << "\n";
13!
55
    return true;
13✔
56
  }
×
57

58
  bool visitNode(const llvm::DINode* var) {
×
59
    outp_ << llvm::left_justify("", width() + 3) << no_pointer_str(*var) << "\n";
×
60
    return true;
×
61
  }
×
62

63
  bool visitBasicType(const llvm::DIBasicType* basic_type) {
×
64
    outp_ << llvm::left_justify("", width() + 3) << no_pointer_str(*basic_type) << "\n";
×
65
    return true;
×
66
  }
×
67

NEW
68
  bool visitStringType(const llvm::DIStringType* string_type) {
×
NEW
69
    outp_ << llvm::left_justify("", width() + 3) << no_pointer_str(*string_type) << "\n";
×
NEW
70
    return true;
×
NEW
71
  }
×
72

73
  bool visitDerivedType(const llvm::DIDerivedType* derived_type) {
52✔
74
    outp_ << llvm::left_justify("", width()) << no_pointer_str(*derived_type) << "\n";
52!
75
    return true;
52✔
76
  }
×
77

78
  bool visitCompositeType(const llvm::DICompositeType* composite_type) {
26✔
79
    outp_ << llvm::left_justify("", width()) << no_pointer_str(*composite_type) << "\n";
26!
80
    return true;
26✔
81
  }
×
82

83
  bool visitRecurringCompositeType(const llvm::DICompositeType* composite_type) {
13✔
84
    outp_ << llvm::left_justify("", width()) << "**" << no_pointer_str(*composite_type) << "\n";
13!
85
    return true;
13✔
86
  }
×
87
};
88

89
}  // namespace printer
90

91
void print_dinode(llvm::DINode* node, llvm::raw_ostream& outs, llvm::Module* module) {
13✔
92
  printer::DIPrinter printer{outs, module};
13✔
93
  assert((llvm::isa<llvm::DIVariable>(node) || llvm::isa<llvm::DIType>(node)) && "Can only print variable or type");
26!
94
  printer.traverseNode(node);
13!
95
}
13✔
96

97
struct DestructureComposite : visitor::DINodeVisitor<DestructureComposite> {
98
  explicit DestructureComposite(const size_t index) : byte_index_{index} {
1,512✔
99
  }
1,512✔
100

101
  [[nodiscard]] std::optional<StructMember> result() const {
1,512✔
102
    return this->outermost_candidate_;
1,512✔
103
  }
704✔
104

105
  bool visitCompositeType(const llvm::DICompositeType* composite) const {
4,522✔
106
    LOG_DEBUG("visitCompositeType: " << log::ditype_str(composite) << ": " << composite->getName()
107
                                     << " index: " << byte_index_ << " offset base: " << this->offset_base_);
108
    return true;
4,522✔
109
  }
110

111
  bool visitDerivedType(const llvm::DIDerivedType* derived_ty) {
17,960✔
112
    // if (derived_ty->getTag() != llvm::dwarf::DW_TAG_member) {
113
    if (!util::is_non_static_member(*derived_ty)) {
17,960✔
114
      return true;
4,638✔
115
    }
116
    // assert(derived_ty->getTag() == llvm::dwarf::DW_TAG_member && "Expected member element in composite ty");
117
    LOG_DEBUG("looking @ member: " << derived_ty->getName() << " offset: " << derived_ty->getOffsetInBits() / 8
118
                                   << " size: " << derived_ty->getSizeInBits() / 8);
119

120
    const auto deriv_offset = (derived_ty->getOffsetInBits() / 8);
13,322✔
121
    const auto deriv_size   = (derived_ty->getSizeInBits() / 8);
13,322✔
122
    const auto offset       = this->offset_base_ + deriv_offset;
13,322✔
123
    const auto lower_bound  = offset;
13,322✔
124
    const auto upper_bound  = offset + deriv_size;
13,322✔
125

126
    if (byte_index_ >= lower_bound && byte_index_ < upper_bound) {
13,322✔
127
      auto* const member_base_type = derived_ty->getBaseType();
2,100✔
128

129
      LOG_DEBUG("saving candidate member type " << log::ditype_str(member_base_type));
130

131
      this->outermost_candidate_.emplace(StructMember{const_cast<llvm::DIDerivedType*>(derived_ty), member_base_type});
2,100✔
132

133
      if (is_pointer_like(*member_base_type) || is_array(*member_base_type)) {
2,100✔
134
        LOG_DEBUG("Terminating recursion, found pointer-like " << is_pointer_like(*member_base_type)
135
                                                               << " or array-like " << is_array(*member_base_type))
136
        return false;  // if offset matches, and its a pointer-like, we do not need to recurse.
1,414✔
137
      }
138

139
      // We should only ever be able to recurse into one composite type where the offset condition holds, so
140
      // save the offset base for that member.
141
      if (llvm::isa<llvm::DICompositeType>(member_base_type)) {
686✔
142
        LOG_DEBUG("setting offset base to: " << offset);
143
        this->offset_base_ = offset;
520✔
144
      }
520✔
145
    }
2,100✔
146
    return true;
11,908✔
147
  }
17,960✔
148

149
 private:
150
  size_t byte_index_;
151
  size_t offset_base_{};
1,512✔
152
  std::optional<StructMember> outermost_candidate_{};
1,512✔
153
};
154

155
std::optional<StructMember> resolve_byte_offset_to_member_of(const llvm::DICompositeType* composite, size_t offset) {
1,512✔
156
  DestructureComposite visitor{offset};
1,512✔
157
  visitor.traverseCompositeType(composite);
1,512!
158
  return visitor.result();
1,512!
159
}
1,512✔
160

161
bool is_pointer(const llvm::DIType& di_type, bool count_reference) {
27,116✔
162
  if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(&di_type)) {
54,184!
163
    return (type->getTag() == llvm::dwarf::DW_TAG_reference_type && count_reference) ||
27,068✔
164
           type->getTag() == llvm::dwarf::DW_TAG_pointer_type;
26,884✔
165
  }
166
  return false;
48✔
167
}
27,116✔
168

169
bool is_pointer_like(const llvm::DIType& di_type) {
7,196✔
170
  if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(&di_type)) {
13,410✔
171
    return type->getTag() == llvm::dwarf::DW_TAG_array_type || type->getTag() == llvm::dwarf::DW_TAG_reference_type ||
12,428!
172
           type->getTag() == llvm::dwarf::DW_TAG_pointer_type ||
6,214✔
173
           type->getTag() == llvm::dwarf::DW_TAG_ptr_to_member_type;
996✔
174
  }
175
  return false;
982✔
176
}
7,196✔
177

178
bool is_non_static_member(const llvm::DINode& elem) {
43,270✔
179
  return elem.getTag() == llvm::dwarf::DW_TAG_member &&
79,006✔
180
         llvm::cast<llvm::DIType>(elem).getFlags() != llvm::DINode::DIFlags::FlagStaticMember;
35,736✔
181
}
182

183
bool is_member(const llvm::DINode& elem) {
15,724✔
184
  const auto* type = llvm::dyn_cast<llvm::DIType>(&elem);
15,724✔
185
  return elem.getTag() == llvm::dwarf::DW_TAG_member ||
24,860✔
186
         ((type != nullptr) && (type->getFlags() == llvm::DINode::DIFlags::FlagStaticMember));
9,136!
187
}
15,724✔
188

189
size_t get_num_composite_members(const llvm::DICompositeType& composite) {
110✔
190
  const auto num_members =
220✔
191
      llvm::count_if(composite.getElements(), [&](const auto* node) { return is_non_static_member(*node); });
1,040✔
192
  return num_members;
220✔
193
}
110✔
194

195
llvm::SmallVector<llvm::DIDerivedType*, 4> get_composite_members(const llvm::DICompositeType& composite) {
×
196
  llvm::SmallVector<llvm::DIDerivedType*, 4> members;
×
197
  for (auto* member : composite.getElements()) {
×
198
    if (is_non_static_member(*member)) {
×
199
      members.push_back(llvm::dyn_cast<llvm::DIDerivedType>(member));
×
200
    }
×
201
  }
×
202
  return members;
×
203
}
×
204

205
std::optional<llvm::DICompositeType*> desugar(llvm::DIType& qualified_composite, int pointer_level) {
3,670✔
206
  llvm::DIType* type = &qualified_composite;
3,670✔
207
  int reached_level{0};
3,670✔
208
  while (type && llvm::isa<llvm::DIDerivedType>(type)) {
10,838✔
209
    if (reached_level > pointer_level) {
7,540✔
210
      break;
372✔
211
    }
212
    auto* ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
7,168✔
213
    if (is_pointer(*ditype)) {
7,168✔
214
      reached_level++;
4,442✔
215
    }
4,442✔
216
    type = ditype->getBaseType();
7,168✔
217
  }
7,168✔
218

219
  if (auto* comp = llvm::dyn_cast_or_null<llvm::DICompositeType>(type)) {
4,728✔
220
    return comp;
1,058✔
221
  }
222
  return {};
2,612✔
223
}
3,670✔
224

225
// bool has_tbaa(const llvm::Instruction& inst) {
226
//   auto* access = inst.getMetadata(llvm::StringRef{"tbaa"});
227
//   return access != nullptr;
228
// }
229

230
bool is_array_member(const llvm::DINode& elem) {
8,646✔
231
  return is_member(elem) &&
14,350✔
232
         llvm::dyn_cast<llvm::DIDerivedType>(&elem)->getBaseType()->getTag() == llvm::dwarf::DW_TAG_array_type;
5,704✔
233
}
234

235
bool is_array(const llvm::DINode& elem) {
6,730✔
236
  auto comp = llvm::dyn_cast<llvm::DICompositeType>(&elem);
6,730✔
237
  return (comp != nullptr) && comp->getTag() == llvm::dwarf::DW_TAG_array_type;
6,730✔
238
}
6,730✔
239

NEW
240
bool is_string(const llvm::DINode& elem) {
×
NEW
241
  return elem.getTag() == llvm::dwarf::DW_TAG_string_type;
×
242
}
243

244
bool is_inheritance(const llvm::DINode& elem) {
31,304✔
245
  return elem.getTag() == llvm::dwarf::DW_TAG_inheritance;
31,304✔
246
}
247

NEW
248
bool is_enum(const llvm::DINode& elem) {
×
NEW
249
  return elem.getTag() == llvm::dwarf::DW_TAG_enumeration_type;
×
250
}
251

252
bool is_struct_or_class(const llvm::DINode& elem) {
1,916✔
253
  return elem.getTag() == llvm::dwarf::DW_TAG_structure_type || elem.getTag() == llvm::dwarf::DW_TAG_class_type;
1,916✔
254
}
255

NEW
256
bool is_typedef(const llvm::DINode& elem) {
×
NEW
257
  return elem.getTag() == llvm::dwarf::DW_TAG_typedef;
×
258
}
259

NEW
260
bool is_union(const llvm::DINode& elem) {
×
NEW
261
  return elem.getTag() == llvm::dwarf::DW_TAG_union_type;
×
262
}
263

264
}  // namespace dimeta::di::util
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