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

ahueck / llvm-dimeta / 23300805560

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

push

github

web-flow
Initial Fortran Support (#49)

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

54.78
/lib/type/TBAA.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 "TBAA.h"
9

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

14
#include "llvm/ADT/StringRef.h"
15
#include "llvm/BinaryFormat/Dwarf.h"
16
#include "llvm/IR/Constants.h"
17
#include "llvm/IR/DebugInfoMetadata.h"
18
#include "llvm/IR/Instruction.h"
19
#include "llvm/IR/Instructions.h"
20
#include "llvm/IR/Metadata.h"
21
#include "llvm/Support/Casting.h"
22
#include "llvm/Support/raw_ostream.h"
23

24
#include <cassert>
25
#include <cstddef>
26
#include <iterator>
27
#include <llvm/ADT/STLExtras.h>
28
#include <llvm/ADT/SmallVector.h>
29
#include <llvm/Support/GlobPattern.h>
30
#include <optional>
31
#include <string>
32
#include <utility>
33

34
namespace dimeta::tbaa {
35

36
struct TBAAHandle {
37
  llvm::MDNode* base_ty;
38
  llvm::MDNode* access_ty;
39
  llvm::ConstantInt* offset;
40
  size_t offset_value{};
1,640✔
41

42
  static std::optional<TBAAHandle> create(const llvm::Instruction& store) {
1,686✔
43
    auto* access = store.getMetadata(llvm::StringRef{"tbaa"});
1,686✔
44
    if (!access) {
1,686✔
45
      return {};
46✔
46
    }
47

48
    std::optional<llvm::GlobPattern> glob_pointer_pattern = []() -> std::optional<llvm::GlobPattern> {
3,280✔
49
#if LLVM_VERSION_MAJOR > 18
50
      auto pattern = llvm::GlobPattern::create("p[1-9] *");
1,312✔
51
      if (pattern) {
1,312!
52
        return pattern.get();
1,312!
53
      }
54
      LOG_WARNING("Glob pattern to match TBAA strings failed.")
×
55
#endif
56
      return std::nullopt;
328✔
57
    }();
1,312✔
58

59
    return TBAAHandle(*access, glob_pointer_pattern);
1,640!
60
  }
1,686✔
61

62
  llvm::StringRef base_name() const {
264✔
63
    return llvm::dyn_cast<llvm::MDString>(base_ty->getOperand(0))->getString();
264✔
64
  }
100✔
65

66
  llvm::StringRef access_name() const {
774✔
67
    return llvm::dyn_cast<llvm::MDString>(access_ty->getOperand(0))->getString();
774✔
68
  }
304✔
69

70
  bool access_is_ptr() const {
774✔
71
    return check_if_pointer_type(access_name());
774✔
72
  }
73

74
  bool base_is_ptr() const {
75
    return check_if_pointer_type(base_name());
76
  }
77
  void reset_base(llvm::MDNode* new_base_ty) {
×
78
    LOG_DEBUG("Reset base_type " << log::ditype_str(base_ty) << " to " << log::ditype_str(new_base_ty))
79
    base_ty = new_base_ty;
×
80
  }
×
81

82
  void subtract_offset(size_t offset_) {
×
83
    LOG_DEBUG("Reset offset value " << offset_value << " with subtraction of " << offset_)
84
    assert(offset_ <= offset_value && "New offset should not be greater than current one");
×
85
    offset_value -= offset_;
×
86
  }
×
87

88
 private:
89
  explicit TBAAHandle(llvm::MDNode& tbaa_node, std::optional<llvm::GlobPattern> pattern)
1,640✔
90
      : glob_pointer_pattern_(std::move(pattern)) {
1,640✔
91
    base_ty        = llvm::dyn_cast<llvm::MDNode>(tbaa_node.getOperand(0));
1,640!
92
    access_ty      = llvm::dyn_cast<llvm::MDNode>(tbaa_node.getOperand(1));
1,640!
93
    auto* value_md = llvm::dyn_cast<llvm::ValueAsMetadata>(tbaa_node.getOperand(2))->getValue();
1,640!
94
    assert(value_md && "Offset value may not be null.");
3,280!
95
    offset       = llvm::dyn_cast<llvm::ConstantInt>(value_md);
1,640!
96
    offset_value = offset->getLimitedValue();
1,640!
97
  }
1,640✔
98

99
  bool check_if_pointer_type(llvm::StringRef name) const {
774✔
100
    bool matches_glob = false;
774✔
101
#if LLVM_VERSION_MAJOR > 18
102
    if (glob_pointer_pattern_) {
620!
103
      // Corrected: assign to matches_glob
104
      matches_glob = glob_pointer_pattern_->match(name);
620✔
105
    }
620✔
106
#endif
107
    return name == "any pointer" || matches_glob;
774✔
108
  }
774✔
109

110
  std::optional<llvm::GlobPattern> glob_pointer_pattern_;
111
};
112

113
namespace helper {
114

115
class FindMatchingMember : public visitor::DINodeVisitor<FindMatchingMember> {
116
  llvm::StringRef composite_name_;
117

118
 public:
119
  std::optional<llvm::DICompositeType*> result{};
30✔
120
  explicit FindMatchingMember(llvm::StringRef composite_name) : composite_name_(composite_name) {
30✔
121
  }
30✔
122

123
  bool visitCompositeType(const llvm::DICompositeType* type) {
60✔
124
    const auto id = [](const auto* type) -> llvm::StringRef {
180✔
125
      if (type->getIdentifier().empty()) {
60✔
126
        return type->getName();
40✔
127
      }
128
      return type->getIdentifier();
20✔
129
    }(type);
120✔
130

131
    if (id == composite_name_) {
60!
UNCOV
132
      result = const_cast<llvm::DICompositeType*>(type);
×
UNCOV
133
      return false;
×
134
    }
135

136
    return true;
60✔
137
  }
60✔
138
};
139

140
std::optional<llvm::DICompositeType*> find_composite_with_name(llvm::StringRef composite_name,
30✔
141
                                                               const llvm::DICompositeType* type) {
142
  FindMatchingMember finder{composite_name};
30✔
143
  finder.traverseCompositeType(type);
30!
144
  return finder.result;
30✔
145
}
30✔
146

147
inline size_t tbaa_operand_count(llvm::MDNode* type_node) {
110✔
148
  // Given, e.g., {!"_ZTSNSt12_Vector_baseIPiSaIS0_EE17_Vector_impl_dataE", !749, i64 0, !749, i64 8, !749, i64 16}
149
  // returns number of [type, offsets] pairs: 3
150
  const auto num = type_node->getNumOperands();
110✔
151
  assert(num > 0 && "Operand count must be > 0");
220!
152
  return (num - 1) / 2;
220✔
153
}
110✔
154

155
inline std::optional<llvm::StringRef> tbaa_operand_name(llvm::MDNode* type_node) {
930✔
156
  if (type_node->getNumOperands() == 0) {
930!
157
    return {};
×
158
  }
159
  return llvm::dyn_cast<llvm::MDString>(type_node->getOperand(0))->getString();
930✔
160
}
930✔
161

162
struct TBAADestructure {
163
  llvm::StringRef name;
164
  size_t offset_base_{};
165
  std::optional<llvm::MDNode*> outermost_candidate_{};
30✔
166

167
  bool traverse_tbaa_nodes(llvm::MDNode* new_base_ty) {
930✔
168
    const auto tbaa_name = tbaa_operand_name(new_base_ty).value_or("");
930✔
169
    if (!tbaa_name.empty() && name == tbaa_name) {
930!
170
      LOG_DEBUG("Match found " << log::ditype_str(new_base_ty))
171
      outermost_candidate_.emplace(new_base_ty);
×
172
      return true;
×
173
    }
174

175
    const auto* end_node = std::prev(new_base_ty->op_end());
930✔
176
    for (const auto* iter = new_base_ty->op_begin(); iter != end_node; ++iter) {
2,730✔
177
      const auto& operand = *iter;
1,800✔
178

179
      if (auto* mdnode = llvm::dyn_cast<llvm::MDNode>(operand)) {
2,700!
180
        auto result = traverse_tbaa_nodes(mdnode);
900✔
181

182
        if (result) {
900!
183
          // we found a matching node, we need to set the offset now to re-calculate later
184
          const auto* offset = std::next(iter);
×
185
          if (auto* value_md = llvm::dyn_cast<llvm::ValueAsMetadata>(*offset)) {
×
186
            if (auto* current_offset = llvm::dyn_cast<llvm::ConstantInt>(value_md->getValue())) {
×
187
              this->offset_base_ += current_offset->getLimitedValue();
×
188
            }
×
189
          }
×
190
          break;
191
        }
×
192
      }
900!
193
    }
1,800!
194

195
    return false;
930✔
196
  }
930✔
197
};
198

199
inline std::pair<std::optional<llvm::MDNode*>, size_t> tbaa_sub_node_matches_name(llvm::StringRef name,
30✔
200
                                                                                  llvm::MDNode* new_base_ty) {
201
  TBAADestructure tbaa{name};
30✔
202
  tbaa.traverse_tbaa_nodes(new_base_ty);
30✔
203
  LOG_DEBUG("Found " << log::ditype_str(tbaa.outermost_candidate_.value_or(nullptr)) << " at offset "
204
                     << tbaa.offset_base_)
205
  return {tbaa.outermost_candidate_, tbaa.offset_base_};
30✔
206
}
30✔
207

208
}  // namespace helper
209

210
bool composite_fits_tbaa(const llvm::DICompositeType* composite, const TBAAHandle& tbaa) {
110✔
211
  assert(composite != nullptr);
110!
212
  const auto num_members = di::util::get_num_composite_members(*composite);
110✔
213
  const auto tbaa_count  = helper::tbaa_operand_count(tbaa.base_ty);
110✔
214
  if (num_members != tbaa_count) {
110!
215
    return false;
110✔
216
  }
217

218
  LOG_DEBUG("Type element size " << num_members << " vs. TBAA " << tbaa_count)
219

220
  const auto elements     = di::util::get_composite_members(*composite);
×
221
  size_t element_position = 0;  // Incremented for every TBAA constant int entry
×
222
  // Loop simply checks if the byte offsets are the same (TODO also compare types!)
223
  for (const auto& tbaa_operand : tbaa.base_ty->operands()) {
×
224
    if (auto* value_md = llvm::dyn_cast<llvm::ValueAsMetadata>(tbaa_operand)) {
×
225
      if (auto* current_offset = llvm::dyn_cast<llvm::ConstantInt>(value_md->getValue())) {
×
226
        auto* current_member = elements[element_position];
×
227
        if (!current_offset->equalsInt(current_member->getOffsetInBits() / 8)) {
×
228
          return false;
×
229
        }
230
        ++element_position;
×
231
      }
×
232
    }
×
233
  }
×
234

235
  return true;
×
236
}
110✔
237

238
std::optional<llvm::DIType*> tbaa_resolver(llvm::DIType* root, TBAAHandle& tbaa) {
910✔
239
  const auto find_composite = [](llvm::DIType* root) {
1,820✔
240
    llvm::DIType* type = root;
910✔
241
    while (type && llvm::isa<llvm::DIDerivedType>(type)) {
1,962!
242
      auto ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
1,052✔
243
      type        = ditype->getBaseType();
1,052✔
244
    }
1,052✔
245
    return type;
1,820✔
246
  };
910✔
247

248
  auto* maybe_composite = find_composite(root);
910✔
249
  if (!maybe_composite || !llvm::isa<llvm::DICompositeType>(maybe_composite)) {
910!
250
    LOG_DEBUG("Returning: DIType is not composite required for TBAA descend.")
251
    return root;
726✔
252
  }
253

254
  auto* composite = llvm::dyn_cast<llvm::DICompositeType>(maybe_composite);
184✔
255

256
  LOG_DEBUG("Found maybe legible composite node: " << log::ditype_str(composite))
257

258
  // Cpp TBAA uses identifier, in C we use the name:
259
  const auto struct_name = [](const auto* composite) {
552✔
260
    auto name = std::string{composite->getIdentifier()};
184!
261
    if (name.empty()) {
184✔
262
      name = std::string{composite->getName()};
140!
263
    }
140✔
264
    return name;
184✔
265
  }(composite);
368✔
266

267
  if (struct_name != tbaa.base_name() && !tbaa.base_name().empty()) {
184!
268
    LOG_DEBUG("Names differ. Name of struct: \"" << struct_name << "\" vs name of TBAA \"" << tbaa.base_name() << "\".")
269
    auto result_composite = helper::find_composite_with_name(tbaa.base_name(), composite);
30!
270

271
    if (result_composite) {
30!
272
      LOG_DEBUG("Found matching sub member " << log::ditype_str(result_composite.value()))
UNCOV
273
      composite = result_composite.value();  // const_cast<llvm::DICompositeType*>(result_composite.value());
×
UNCOV
274
      const auto num_members = di::util::get_num_composite_members(*composite);
×
UNCOV
275
      const auto num_tbaa    = helper::tbaa_operand_count(tbaa.base_ty);
×
UNCOV
276
      if (num_members != num_tbaa) {
×
277
        LOG_DEBUG("Mismatch between sub member element count and TBAA base type count "
278
                  << log::ditype_str(tbaa.base_ty))
279
        return root;
×
280
      }
UNCOV
281
    } else {
×
282
      LOG_DEBUG("Did not find matching sub member: " << struct_name)
283
      const auto [new_base, calculated_offset] = helper::tbaa_sub_node_matches_name(struct_name, tbaa.base_ty);
30!
284
      if (!new_base) {
30!
285
        LOG_DEBUG("Did not find new base, returning")
286
        return root;
30✔
287
      }
288
      LOG_DEBUG("New TBAA base " << log::ditype_str(new_base.value()) << " with negative offset " << calculated_offset)
289
      tbaa.reset_base(new_base.value());
×
290
      tbaa.subtract_offset(calculated_offset);
×
291
    }
30!
292
  }
30!
293

294
  if (struct_name.empty()) {
154✔
295
    LOG_DEBUG("No name match possible")
296
    // Here no name matching is possible, is TBAA node referring to current DI type? We need to verify:
297
    if (!composite_fits_tbaa(composite, tbaa)) {
110!
298
      LOG_DEBUG("TBAA of anonymous struct has different offsets to DIComposite type!")
299
      return root;
110✔
300
    }
301
  }
×
302

303
  assert(tbaa.access_is_ptr() && "TBAA access should be pointer");
88!
304

305
  LOG_DEBUG("TBAA tree data.")
306
  LOG_DEBUG("  From ditype: " << log::ditype_str(composite))
307
  LOG_DEBUG("  From TBAA: " << log::ditype_str(tbaa.base_ty))
308
  LOG_DEBUG("  At offset: " << tbaa.offset_value)
309

310
  auto result = di::util::resolve_byte_offset_to_member_of(composite, tbaa.offset_value);
44!
311
  if (result) {
44✔
312
    return result->type_of_member;
14✔
313
  }
314
  return {};
30✔
315
}
910✔
316

317
std::optional<llvm::DIType*> resolve_tbaa(llvm::DIType* root, const llvm::Instruction& instruction) {
1,686✔
318
  LOG_DEBUG("Resolve TBAA of instruction '" << instruction << "' with ditype: " << log::ditype_str(root))
319

320
  auto tbaa = TBAAHandle::create(instruction);
1,686✔
321
  if (!tbaa) {
1,686✔
322
    LOG_DEBUG("Requires TBAA metadata in LLVM IR.")
323
    return root;
46✔
324
  }
325

326
  // assign any ptr to any ptr, e.g., struct A** a; a[0] = malloc(struct A):
327
  if (tbaa->base_ty == tbaa->access_ty && tbaa->access_is_ptr()) {
1,640!
328
    LOG_DEBUG("No work: TBAA base type is same as access type (both ptr).")
329
    return root;
730✔
330
  }
331

332
  const auto node = tbaa_resolver(root, tbaa.value());
910!
333
  if (node.has_value() && node.value() != root) {
910!
334
    LOG_DEBUG("Determined new type with TBAA")
335
  }
14✔
336

337
  return node;
554✔
338
}
1,686✔
339

340
}  // namespace dimeta::tbaa
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