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

ahueck / llvm-dimeta / 25333120092

04 May 2026 05:25PM UTC coverage: 73.596% (+0.3%) from 73.295%
25333120092

Pull #58

github

web-flow
Merge 9bc7df9fc into 98116ab4b
Pull Request #58: Enable custom allocators

2241 of 3751 branches covered (59.74%)

Branch coverage included in aggregate %.

125 of 139 new or added lines in 5 files covered. (89.93%)

2 existing lines in 2 files now uncovered.

2687 of 2945 relevant lines covered (91.24%)

20073.69 hits per line

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

73.28
/lib/type/Dimeta.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 "Dimeta.h"
9

10
#include "DIFinder.h"
11
#include "DIRootType.h"
12
#include "DITypeExtractor.h"
13
#include "DIUtil.h"
14
#include "DataflowAnalysis.h"
15
#include "DefUseAnalysis.h"
16
#include "DimetaData.h"
17
#include "DimetaParse.h"
18
#include "MemoryOps.h"
19
#include "Util.h"
20
#include "ValuePath.h"
21
#include "support/Logger.h"
22

23
#include "llvm/ADT/ArrayRef.h"
24
#include "llvm/ADT/STLExtras.h"
25
#include "llvm/ADT/SmallVector.h"
26
#include "llvm/ADT/ilist_iterator.h"
27
#include "llvm/BinaryFormat/Dwarf.h"
28
#include "llvm/Config/llvm-config.h"
29
#include "llvm/IR/Argument.h"
30
#include "llvm/IR/Constants.h"
31
#include "llvm/IR/DebugInfoMetadata.h"
32
#include "llvm/IR/Function.h"
33
#include "llvm/IR/GlobalVariable.h"
34
#include "llvm/IR/InstIterator.h"
35
#include "llvm/IR/InstrTypes.h"
36
#include "llvm/IR/Instructions.h"
37
#include "llvm/IR/IntrinsicInst.h"
38
#include "llvm/IR/Metadata.h"
39
#include "llvm/IR/Operator.h"
40
#include "llvm/IR/Value.h"
41
#include "llvm/Support/Casting.h"
42
#include "llvm/Support/Debug.h"
43
#include "llvm/Support/ErrorHandling.h"
44
#include "llvm/Support/raw_ostream.h"
45

46
#include <cassert>
47
#include <iterator>
48
#include <optional>
49
#include <string>
50

51
namespace llvm {
52
class DbgVariableIntrinsic;
53
}  // namespace llvm
54

55
#if LLVM_VERSION_MAJOR == 10
56
// For FindDbgAddrUses:
57
#include "llvm/Transforms/Utils/Local.h"
58
#endif
59

60
namespace dimeta {
61

62
namespace fortran {
63
struct FortranType {
64
  llvm::DIType* type{nullptr};
65
  std::optional<ShapeData> shape_argument;
66
};
67
std::optional<FortranType> di_type_for(const llvm::Value* value, const llvm::CallBase* call = nullptr);
68
}  // namespace fortran
69

70
namespace experimental {
71
std::optional<llvm::DIType*> di_type_for(const llvm::Value* value);
72
}
73

74
std::optional<llvm::DIType*> type_for_malloclike(const llvm::CallBase* call);
75
std::optional<llvm::DIType*> type_for_newlike(const llvm::CallBase* call);
76
auto final_ditype(std::optional<llvm::DIType*> root_ditype) -> std::pair<std::optional<llvm::DIType*>, int>;
77

78
namespace {
79

80
enum class CallBaseTypeSource {
81
  kReturnValueForwardThenBackward,
82
  kArgumentBackward,
83
  kFortranDescriptor,
84
  kHeapAllocSite,
85
};
86

87
struct ResolvedCallBaseTypeConfig {
88
  CallBaseTypeSource source;
89
  unsigned argument_index{0};
90
  int pointer_level_offset{0};
91
};
92

93
std::optional<ResolvedCallBaseTypeConfig> resolve_auto_callbase_config(const llvm::CallBase* call) {
34,394✔
94
  if (call == nullptr) {
34,394!
NEW
95
    return {};
×
96
  }
97

98
  auto* cb_fun = call->getCalledFunction();
34,394✔
99
  if (cb_fun == nullptr) {
34,394!
100
    return {};
8✔
101
  }
102

103
  const dimeta::memory::MemOps mem_ops;
34,386✔
104
  if (!mem_ops.isAlloc(cb_fun->getName())) {
34,386!
105
    LOG_TRACE("Skipping call base: " << cb_fun->getName());
106
    return {};
25,746✔
107
  }
108

109
  if (mem_ops.isFortranLike(cb_fun->getName())) {
8,640!
110
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kFortranDescriptor, 0, 0};
272✔
111
  }
112

113
  if (mem_ops.isCudaLike(cb_fun->getName())) {
8,368!
114
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kArgumentBackward, 0, 0};
552✔
115
  }
116

117
  if (mem_ops.isMpiLike(cb_fun->getName())) {
7,816!
NEW
118
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kArgumentBackward, 2, 0};
×
119
  }
120

121
#ifdef DIMETA_USE_HEAPALLOCSITE
122
  if (mem_ops.isNewLike(cb_fun->getName()) && call->getMetadata("heapallocsite") != nullptr) {
7,816!
123
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kHeapAllocSite, 0, 1};
1,352✔
124
  }
125
#endif
126

127
  return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kReturnValueForwardThenBackward, 0, 0};
6,464✔
128
}
34,394✔
129

130
std::optional<ResolvedCallBaseTypeConfig> resolve_callbase_config(const llvm::CallBase* call,
34,658✔
131
                                                                  const CallBaseTypeConfig& config) {
132
  if (call == nullptr) {
34,658!
NEW
133
    return {};
×
134
  }
135

136
  if (config.dataflow == CallBaseTypeConfig::Dataflow::kAuto) {
34,658✔
137
    return resolve_auto_callbase_config(call);
34,394✔
138
  }
139

140
  if (config.dataflow == CallBaseTypeConfig::Dataflow::kArgumentBackward) {
264✔
141
    if (config.argument_index >= call->arg_size()) {
176!
142
      LOG_DEBUG("Invalid argument index for callbase config: " << config.argument_index);
NEW
143
      return {};
×
144
    }
145
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kArgumentBackward, config.argument_index,
352✔
146
                                      config.pointer_level_offset};
176✔
147
  }
148

149
  if (config.dataflow == CallBaseTypeConfig::Dataflow::kReturnValueForwardThenBackward) {
88!
150
    return ResolvedCallBaseTypeConfig{CallBaseTypeSource::kReturnValueForwardThenBackward, 0,
176✔
151
                                      config.pointer_level_offset};
88✔
152
  }
153

NEW
154
  return {};
×
155
}
34,658✔
156

157
std::optional<DimetaData> type_for_resolved_callbase(const llvm::CallBase* call,
8,904✔
158
                                                     const ResolvedCallBaseTypeConfig& config) {
159
  if (call == nullptr) {
8,904!
NEW
160
    return {};
×
161
  }
162

163
  std::optional<llvm::DIType*> extracted_type{};
8,904✔
164
  std::optional<ShapeData> shape_type{};
8,904✔
165
  int pointer_level_offset{config.pointer_level_offset};
8,904✔
166

167
  switch (config.source) {
8,904!
168
    case CallBaseTypeSource::kArgumentBackward: {
169
      extracted_type = experimental::di_type_for(call->getArgOperand(config.argument_index));
728!
170
      break;
728✔
171
    }
172
    case CallBaseTypeSource::kReturnValueForwardThenBackward: {
173
      extracted_type = type_for_malloclike(call);
6,552!
174
      break;
6,552✔
175
    }
176
    case CallBaseTypeSource::kFortranDescriptor: {
177
      if (call->arg_size() == 0) {
272!
NEW
178
        return {};
×
179
      }
180
      auto fortran_type = fortran::di_type_for(call->getArgOperand(0), call);
272!
181
      if (fortran_type) {
272!
182
        extracted_type = fortran_type->type;
272✔
183
        if (fortran_type->shape_argument) {
272✔
184
          shape_type = fortran_type->shape_argument;
168!
185
        }
168✔
186
      }
272✔
187
      break;
188
    }
272✔
189
    case CallBaseTypeSource::kHeapAllocSite: {
190
      extracted_type = type_for_newlike(call);
1,352!
191
      break;
1,352✔
192
    }
193
  }
194

195
  auto source_loc                        = difinder::find_location(call);
8,904!
196
  const auto [final_type, pointer_level] = final_ditype(extracted_type);
8,904!
197

198
  return DimetaData{DimetaData::MemLoc::kHeap,           {}, extracted_type, final_type, source_loc, shape_type,
13,256!
199
                    pointer_level + pointer_level_offset};
8,904✔
200
}
8,904✔
201

202
}  // namespace
203

204
llvm::SmallVector<llvm::DIType*, 4> collect_types(const llvm::CallBase* call,
7,446✔
205
                                                  llvm::ArrayRef<dataflow::ValuePath> paths_to_type) {
206
  using namespace llvm;
207
  SmallVector<llvm::DIType*, 4> di_types;
7,446✔
208
  llvm::transform(paths_to_type, dimeta::util::optional_back_inserter(di_types),
7,446!
209
                  [&](const auto& path) { return type::find_type(dataflow::CallValuePath{call, path}); });
15,030!
210
  return di_types;
7,446✔
211
}
7,446!
212

213
auto final_ditype(std::optional<llvm::DIType*> root_ditype) -> std::pair<std::optional<llvm::DIType*>, int> {
25,320✔
214
  if (!root_ditype) {
25,320✔
215
    return {{}, 0};
152✔
216
  }
217
  int level{0};
25,168✔
218
  llvm::DIType* type = *root_ditype;
25,168✔
219
  while (llvm::isa<llvm::DIDerivedType>(type)) {
48,336✔
220
    auto ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
23,580✔
221
    if (di::util::is_pointer(*ditype, false)) {
23,580✔
222
      ++level;
15,642✔
223
    }
15,642✔
224
    // void*-based derived types have basetype=null:
225
    if (ditype->getBaseType() == nullptr) {
23,580✔
226
      return {type, level};
412✔
227
    }
228
    type = ditype->getBaseType();
23,168✔
229
  }
23,580✔
230

231
  return {type, level};
24,756✔
232
}
25,320✔
233

234
std::optional<llvm::DIType*> type_for_malloclike(const llvm::CallBase* call) {
6,552✔
235
  auto local = difinder::get_array_access_assignment(call);
6,552✔
236
  if (local) {
6,552✔
237
    LOG_DEBUG("Call has local variable " << *call)
238
    // LOG_DEBUG("Call type " << log::ditype_str(local.value()->getType()))
239
    auto base_type = local.value().var->getType();
344✔
240
    if (local.value().array_access) {
344✔
241
      if (auto* array_type = llvm::dyn_cast<llvm::DICompositeType>(base_type)) {
392!
242
        LOG_DEBUG("Returning type of access to array " << log::ditype_str(array_type))
243
        return array_type->getBaseType();
196✔
244
      }
245
    }
×
246
    return base_type;
148✔
247
  }
344✔
248

249
  const auto ditype_paths = dataflow::type_for_heap_call(call);
6,208✔
250

251
  LOG_DEBUG("Found paths, now collecting types")
252
  const auto ditypes_vector = collect_types(call, ditype_paths);
6,208!
253
  if (ditypes_vector.empty()) {
6,208!
254
    return {};
78✔
255
  }
256
  return *ditypes_vector.begin();
6,130!
257
}
6,552✔
258

259
std::optional<llvm::DIType*> type_for_newlike(const llvm::CallBase* call) {
1,352✔
260
  auto* heapalloc_md = call->getMetadata("heapallocsite");
1,352✔
261
  assert(heapalloc_md != nullptr && "Missing required heapallocsite metadata.");
2,704!
262
  if (auto* type = llvm::dyn_cast<llvm::DIType>(heapalloc_md)) {
2,704!
263
    //    util::DIPrinter printer(llvm::outs(), call->getParent()->getParent()->getParent());
264
    //    printer.traverseType(type);
265
    //    llvm::dbgs() << "Final Type: " << *type << "\n";
266
    return type;
1,352✔
267
  }
268
  return {};
×
269
}
1,352✔
270

271
std::optional<DimetaData> type_for(const llvm::CallBase* call, const CallBaseTypeConfig& config) {
34,658✔
272
  auto resolved = resolve_callbase_config(call, config);
34,658✔
273
  if (!resolved) {
34,658✔
274
    if (config.dataflow == CallBaseTypeConfig::Dataflow::kAuto) {
25,754!
275
      LOG_TRACE("Skipping call base in auto mode.");
276
    } else {
25,754✔
277
      LOG_DEBUG("Could not resolve explicit callbase type configuration.");
278
    }
279
    return {};
25,754✔
280
  }
281

282
  auto result = type_for_resolved_callbase(call, *resolved);
8,904✔
283
  if (!result) {
8,904!
UNCOV
284
    return {};
×
285
  }
286

287
  if (config.dataflow != CallBaseTypeConfig::Dataflow::kAuto && !result->entry_type) {
8,904✔
288
    LOG_DEBUG("Explicit callbase type extraction failed.");
289
    return {};
94✔
290
  }
291

292
  return result;
8,810✔
293
}
34,658✔
294

295
std::optional<DimetaData> type_for(const llvm::AllocaInst* ai) {
15,348✔
296
  const auto local_di_var = difinder::find_local_variable(ai);
15,348✔
297

298
  const auto passed = dataflow::fortran::passed_to_fortran_helper(ai);
15,348✔
299
  if (passed) {
15,348✔
300
    LOG_DEBUG("Skip allocation passed to Flang intrinsic")
301
    return {};
80✔
302
  }
303

304
  if (local_di_var) {
15,268✔
305
    auto extracted_type                    = local_di_var.value()->getType();
14,532✔
306
    auto source_loc                        = difinder::find_location(ai);
14,532✔
307
    const auto [final_type, pointer_level] = final_ditype(extracted_type);
29,612✔
308
    const auto meta =
14,532✔
309
        DimetaData{DimetaData::MemLoc::kStack, local_di_var, extracted_type, final_type, source_loc, {}, pointer_level};
29,612✔
310
    return meta;
14,532!
311
  }
14,532✔
312

313
  LOG_DEBUG("No local_variable for " << *ai)
314

315
  return {};
736✔
316
}
15,348✔
317

318
std::optional<DimetaData> type_for(const llvm::GlobalVariable* gv) {
4,609✔
319
  llvm::SmallVector<llvm::DIGlobalVariableExpression*, 2> dbg_info;
4,609✔
320
  gv->getDebugInfo(dbg_info);
4,609!
321
  if (!dbg_info.empty()) {
4,609!
322
    auto gv_expr                           = *dbg_info.begin();
1,884!
323
    auto gv_type                           = gv_expr->getVariable()->getType();
1,884!
324
    const auto [final_type, pointer_level] = final_ditype(gv_type);
1,884!
325
    return DimetaData{DimetaData::MemLoc::kGlobal, gv_expr->getVariable(), gv_type, final_type, {}, {}, pointer_level};
1,884!
326
  }
1,884✔
327
  return {};
2,725✔
328
}
4,609✔
329

330
std::optional<CompileUnitTypeList> compile_unit_types(const llvm::Module* module) {
4,346✔
331
  CompileUnitTypeList list;
4,346✔
332
  for (auto* compile_unit : module->debug_compile_units()) {
8,663!
333
    CompileUnitTypes current_cu;
4,317✔
334
    current_cu.name = compile_unit->getFilename();
4,317!
335
    for (auto* retained_type : compile_unit->getRetainedTypes()) {
7,198!
336
      if (auto* type = llvm::dyn_cast<llvm::DIType>(retained_type)) {
5,762!
337
        auto dimeta_result = parser::make_dimetadata(type);
2,881!
338
        if (!dimeta_result) {
2,881!
339
          continue;
×
340
        }
341
        current_cu.types.push_back(dimeta_result->type_);
2,881!
342
      }
2,881!
343
    }
2,881!
344
    list.push_back(current_cu);
4,317!
345
  }
4,317✔
346
  return (list.empty() ? std::optional<CompileUnitTypeList>{} : list);
4,346!
347
}
4,346✔
348

349
namespace fortran {
350
std::optional<FortranType> di_type_for(const llvm::Value* value, const llvm::CallBase* call) {
272✔
351
  assert(value != nullptr);
272!
352
  auto shape = dataflow::fortran::shape_from_value(value);
272✔
353

354
  auto paths                = dataflow::experimental::path_from_value(value);
272!
355
  const auto ditypes_vector = collect_types(call, paths);
272!
356
  if (ditypes_vector.empty()) {
272!
357
    return {};
×
358
  }
359

360
  return FortranType{*ditypes_vector.begin(), shape};
272!
361
}
272✔
362
}  // namespace fortran
363

364
namespace experimental {
365
std::optional<llvm::DIType*> di_type_for(const llvm::Value* value) {
728✔
366
  auto paths                = dataflow::experimental::path_from_value(value);
728✔
367
  const auto ditypes_vector = collect_types(nullptr, paths);
728!
368
  if (ditypes_vector.empty()) {
728!
369
    return {};
74✔
370
  }
371

372
  return *ditypes_vector.begin();
654!
373
}
728✔
374

375
std::optional<QualifiedType> type_for(const llvm::Value* value) {
238✔
376
  auto paths                = dataflow::experimental::path_from_value(value);
238✔
377
  const auto ditypes_vector = collect_types(nullptr, paths);
238!
378
  if (ditypes_vector.empty()) {
238!
379
    return {};
×
380
  }
381

382
  for (const auto& type : ditypes_vector) {
476!
383
    auto dimeta_result = parser::make_dimetadata(type);
238!
384
    if (!dimeta_result) {
238!
385
      continue;
×
386
    }
387
    return dimeta_result->type_;
238!
388
  }
238!
389

390
  return {};
×
391
}
238✔
392

393
}  // namespace experimental
394

395
}  // namespace dimeta
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