• 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

60.26
/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) {
88✔
29
    std::string view;
88✔
30
    llvm::raw_string_ostream rso(view);
88!
31
    type.print(rso, module_.value_or(nullptr));
88!
32

33
    if (module_) {
88!
34
      return rso.str();
88!
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
  }
88✔
44

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

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

53
  bool visitVariable(const llvm::DIVariable* var) {
11✔
54
    outp_ << llvm::left_justify("", width()) << no_pointer_str(*var) << "\n";
11!
55
    return true;
11✔
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) {
44✔
74
    outp_ << llvm::left_justify("", width()) << no_pointer_str(*derived_type) << "\n";
44!
75
    return true;
44✔
76
  }
×
77

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

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

89
}  // namespace printer
90

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

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

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

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

111
  bool visitDerivedType(const llvm::DIDerivedType* derived_ty) {
12,432✔
112
    // if (derived_ty->getTag() != llvm::dwarf::DW_TAG_member) {
113
    if (!util::is_non_static_member(*derived_ty)) {
12,432✔
114
      return true;
3,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);
8,794✔
121
    const auto deriv_size   = (derived_ty->getSizeInBits() / 8);
8,794✔
122
    const auto offset       = this->offset_base_ + deriv_offset;
8,794✔
123
    const auto lower_bound  = offset;
8,794✔
124
    const auto upper_bound  = offset + deriv_size;
8,794✔
125

126
    if (byte_index_ >= lower_bound && byte_index_ < upper_bound) {
8,794✔
127
      auto* const member_base_type = derived_ty->getBaseType();
1,504✔
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});
1,504✔
132

133
      if (is_pointer_like(*member_base_type) || is_array(*member_base_type)) {
1,504✔
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,010✔
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)) {
494✔
142
        LOG_DEBUG("setting offset base to: " << offset);
143
        this->offset_base_ = offset;
360✔
144
      }
360✔
145
    }
1,504✔
146
    return true;
7,784✔
147
  }
12,432✔
148

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

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

161
bool is_pointer(const llvm::DIType& di_type, bool count_reference) {
22,788✔
162
  if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(&di_type)) {
45,576!
163
    return (type->getTag() == llvm::dwarf::DW_TAG_reference_type && count_reference) ||
22,788✔
164
           type->getTag() == llvm::dwarf::DW_TAG_pointer_type;
22,636✔
165
  }
166
  return false;
×
167
}
22,788✔
168

169
bool is_pointer_like(const llvm::DIType& di_type) {
5,864✔
170
  if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(&di_type)) {
11,122✔
171
    return type->getTag() == llvm::dwarf::DW_TAG_array_type || type->getTag() == llvm::dwarf::DW_TAG_reference_type ||
10,516!
172
           type->getTag() == llvm::dwarf::DW_TAG_pointer_type ||
5,258✔
173
           type->getTag() == llvm::dwarf::DW_TAG_ptr_to_member_type;
844✔
174
  }
175
  return false;
606✔
176
}
5,864✔
177

178
bool is_non_static_member(const llvm::DINode& elem) {
33,220✔
179
  return elem.getTag() == llvm::dwarf::DW_TAG_member &&
60,354✔
180
         llvm::cast<llvm::DIType>(elem).getFlags() != llvm::DINode::DIFlags::FlagStaticMember;
27,134✔
181
}
182

183
bool is_member(const llvm::DINode& elem) {
13,058✔
184
  const auto* type = llvm::dyn_cast<llvm::DIType>(&elem);
13,058✔
185
  return elem.getTag() == llvm::dwarf::DW_TAG_member ||
20,682✔
186
         ((type != nullptr) && (type->getFlags() == llvm::DINode::DIFlags::FlagStaticMember));
7,624!
187
}
13,058✔
188

189
size_t get_num_composite_members(const llvm::DICompositeType& composite) {
88✔
190
  const auto num_members =
176✔
191
      llvm::count_if(composite.getElements(), [&](const auto* node) { return is_non_static_member(*node); });
832✔
192
  return num_members;
176✔
193
}
88✔
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,066✔
206
  llvm::DIType* type = &qualified_composite;
3,066✔
207
  int reached_level{0};
3,066✔
208
  while (type && llvm::isa<llvm::DIDerivedType>(type)) {
9,134✔
209
    if (reached_level > pointer_level) {
6,388✔
210
      break;
320✔
211
    }
212
    auto* ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
6,068✔
213
    if (is_pointer(*ditype)) {
6,068✔
214
      reached_level++;
3,766✔
215
    }
3,766✔
216
    type = ditype->getBaseType();
6,068✔
217
  }
6,068✔
218

219
  if (auto* comp = llvm::dyn_cast_or_null<llvm::DICompositeType>(type)) {
3,916✔
220
    return comp;
850✔
221
  }
222
  return {};
2,216✔
223
}
3,066✔
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) {
7,170✔
231
  return is_member(elem) &&
11,894✔
232
         llvm::dyn_cast<llvm::DIDerivedType>(&elem)->getBaseType()->getTag() == llvm::dwarf::DW_TAG_array_type;
4,724✔
233
}
234

235
bool is_array(const llvm::DINode& elem) {
5,114✔
236
  auto comp = llvm::dyn_cast<llvm::DICompositeType>(&elem);
5,114✔
237
  return (comp != nullptr) && comp->getTag() == llvm::dwarf::DW_TAG_array_type;
5,114✔
238
}
5,114✔
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) {
25,568✔
245
  return elem.getTag() == llvm::dwarf::DW_TAG_inheritance;
25,568✔
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,500✔
253
  return elem.getTag() == llvm::dwarf::DW_TAG_structure_type || elem.getTag() == llvm::dwarf::DW_TAG_class_type;
1,500✔
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