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

ahueck / llvm-dimeta / 19598487515

22 Nov 2025 04:54PM UTC coverage: 73.626% (-9.7%) from 83.355%
19598487515

Pull #48

github

web-flow
Merge cefb3414f into e3aac00b8
Pull Request #48: Release 0.4

1746 of 2973 branches covered (58.73%)

Branch coverage included in aggregate %.

207 of 213 new or added lines in 8 files covered. (97.18%)

54 existing lines in 5 files now uncovered.

2232 of 2430 relevant lines covered (91.85%)

8946.18 hits per line

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

57.89
/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{};
948✔
41

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

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

59
    return TBAAHandle(*access, glob_pointer_pattern);
948!
60
  }
958✔
61

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

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

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

74
  bool base_is_ptr() const {
75
    return check_if_pointer_type(base_name());
76
  }
UNCOV
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))
UNCOV
79
    base_ty = new_base_ty;
×
UNCOV
80
  }
×
81

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

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

99
  bool check_if_pointer_type(llvm::StringRef name) const {
622✔
100
    bool matches_glob = false;
622✔
101
#if LLVM_VERSION_MAJOR > 18
102
    if (glob_pointer_pattern_) {
468!
103
      // Corrected: assign to matches_glob
104
      matches_glob = glob_pointer_pattern_->match(name);
468✔
105
    }
468✔
106
#endif
107
    return name == "any pointer" || matches_glob;
622✔
108
  }
622✔
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{};
24✔
120
  explicit FindMatchingMember(llvm::StringRef composite_name) : composite_name_(composite_name) {
24✔
121
  }
24✔
122

123
  bool visitCompositeType(const llvm::DICompositeType* type) {
64✔
124
    const auto id = [](const auto* type) -> llvm::StringRef {
192✔
125
      if (type->getIdentifier().empty()) {
64!
UNCOV
126
        return type->getName();
×
127
      }
128
      return type->getIdentifier();
64✔
129
    }(type);
128✔
130

131
    if (id == composite_name_) {
64✔
132
      result = const_cast<llvm::DICompositeType*>(type);
8✔
133
      return false;
8✔
134
    }
135

136
    return true;
56✔
137
  }
64✔
138
};
139

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

147
inline size_t tbaa_operand_count(llvm::MDNode* type_node) {
24✔
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();
24✔
151
  assert(num > 0 && "Operand count must be > 0");
48!
152
  return (num - 1) / 2;
48✔
153
}
24✔
154

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

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

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

175
    const auto* end_node = std::prev(new_base_ty->op_end());
134✔
176
    for (const auto* iter = new_base_ty->op_begin(); iter != end_node; ++iter) {
370✔
177
      const auto& operand = *iter;
236✔
178

179
      if (auto* mdnode = llvm::dyn_cast<llvm::MDNode>(operand)) {
354!
180
        auto result = traverse_tbaa_nodes(mdnode);
118✔
181

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

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

199
inline std::pair<std::optional<llvm::MDNode*>, size_t> tbaa_sub_node_matches_name(llvm::StringRef name,
16✔
200
                                                                                  llvm::MDNode* new_base_ty) {
201
  TBAADestructure tbaa{name};
16✔
202
  tbaa.traverse_tbaa_nodes(new_base_ty);
16✔
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_};
16✔
206
}
16✔
207

208
}  // namespace helper
209

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

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

UNCOV
220
  const auto elements     = di::util::get_composite_members(*composite);
×
UNCOV
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!)
UNCOV
223
  for (const auto& tbaa_operand : tbaa.base_ty->operands()) {
×
UNCOV
224
    if (auto* value_md = llvm::dyn_cast<llvm::ValueAsMetadata>(tbaa_operand)) {
×
UNCOV
225
      if (auto* current_offset = llvm::dyn_cast<llvm::ConstantInt>(value_md->getValue())) {
×
UNCOV
226
        auto* current_member = elements[element_position];
×
UNCOV
227
        if (!current_offset->equalsInt(current_member->getOffsetInBits() / 8)) {
×
228
          return false;
×
229
        }
UNCOV
230
        ++element_position;
×
UNCOV
231
      }
×
UNCOV
232
    }
×
UNCOV
233
  }
×
234

UNCOV
235
  return true;
×
236
}
16✔
237

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

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

254
  auto* composite = llvm::dyn_cast<llvm::DICompositeType>(maybe_composite);
78✔
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) {
234✔
260
    auto name = std::string{composite->getIdentifier()};
78!
261
    if (name.empty()) {
78✔
262
      name = std::string{composite->getName()};
32!
263
    }
32✔
264
    return name;
78✔
265
  }(composite);
156✔
266

267
  if (struct_name != tbaa.base_name() && !tbaa.base_name().empty()) {
78!
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);
24!
270

271
    if (result_composite) {
24✔
272
      LOG_DEBUG("Found matching sub member " << log::ditype_str(result_composite.value()))
273
      composite = result_composite.value();  // const_cast<llvm::DICompositeType*>(result_composite.value());
8!
274
      const auto num_members = di::util::get_num_composite_members(*composite);
8!
275
      const auto num_tbaa    = helper::tbaa_operand_count(tbaa.base_ty);
8!
276
      if (num_members != num_tbaa) {
8!
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
      }
281
    } else {
8!
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);
16!
284
      if (!new_base) {
16!
285
        LOG_DEBUG("Did not find new base, returning")
286
        return root;
16✔
287
      }
288
      LOG_DEBUG("New TBAA base " << log::ditype_str(new_base.value()) << " with negative offset " << calculated_offset)
UNCOV
289
      tbaa.reset_base(new_base.value());
×
UNCOV
290
      tbaa.subtract_offset(calculated_offset);
×
291
    }
16!
292
  }
24✔
293

294
  if (struct_name.empty()) {
62✔
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)) {
16!
298
      LOG_DEBUG("TBAA of anonymous struct has different offsets to DIComposite type!")
299
      return root;
16✔
300
    }
UNCOV
301
  }
×
302

303
  assert(tbaa.access_is_ptr() && "TBAA access should be pointer");
92!
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);
46!
311
  if (result) {
46✔
312
    return result->type_of_member;
22✔
313
  }
314
  return {};
24✔
315
}
372✔
316

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

320
  auto tbaa = TBAAHandle::create(instruction);
958✔
321
  if (!tbaa) {
958✔
322
    LOG_DEBUG("Requires TBAA metadata in LLVM IR.")
323
    return root;
10✔
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()) {
948!
328
    LOG_DEBUG("No work: TBAA base type is same as access type (both ptr).")
329
    return root;
576✔
330
  }
331

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

337
  return node;
284✔
338
}
958✔
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