• 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

73.05
/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
llvm::SmallVector<llvm::DIType*, 4> collect_types(const llvm::CallBase* call,
5,028✔
75
                                                  llvm::ArrayRef<dataflow::ValuePath> paths_to_type) {
76
  using namespace llvm;
77
  SmallVector<llvm::DIType*, 4> di_types;
5,028✔
78
  llvm::transform(paths_to_type, dimeta::util::optional_back_inserter(di_types),
5,028!
79
                  [&](const auto& path) { return type::find_type(dataflow::CallValuePath{call, path}); });
10,232!
80
  return di_types;
5,028✔
81
}
5,028!
82

83
auto final_ditype(std::optional<llvm::DIType*> root_ditype) -> std::pair<std::optional<llvm::DIType*>, int> {
17,642✔
84
  if (!root_ditype) {
17,642✔
85
    return {{}, 0};
34✔
86
  }
87
  int level{0};
17,608✔
88
  llvm::DIType* type = *root_ditype;
17,608✔
89
  while (llvm::isa<llvm::DIDerivedType>(type)) {
33,884✔
90
    auto ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
16,528✔
91
    if (di::util::is_pointer(*ditype, false)) {
16,528✔
92
      ++level;
10,834✔
93
    }
10,834✔
94
    // void*-based derived types have basetype=null:
95
    if (ditype->getBaseType() == nullptr) {
16,528✔
96
      return {type, level};
252✔
97
    }
98
    type = ditype->getBaseType();
16,276✔
99
  }
16,528✔
100

101
  return {type, level};
17,356✔
102
}
17,642✔
103

104
std::optional<llvm::DIType*> type_for_malloclike(const llvm::CallBase* call) {
4,712✔
105
  auto local = difinder::get_array_access_assignment(call);
4,712✔
106
  if (local) {
4,712✔
107
    LOG_DEBUG("Call has local variable " << *call)
108
    // LOG_DEBUG("Call type " << log::ditype_str(local.value()->getType()))
109
    auto base_type = local.value().var->getType();
184✔
110
    if (local.value().array_access) {
184✔
111
      if (auto* array_type = llvm::dyn_cast<llvm::DICompositeType>(base_type)) {
168!
112
        LOG_DEBUG("Returning type of access to array " << log::ditype_str(array_type))
113
        return array_type->getBaseType();
84✔
114
      }
115
    }
×
116
    return base_type;
100✔
117
  }
184✔
118

119
  const auto ditype_paths = dataflow::type_for_heap_call(call);
4,528✔
120

121
  LOG_DEBUG("Found paths, now collecting types")
122
  const auto ditypes_vector = collect_types(call, ditype_paths);
4,528!
123
  if (ditypes_vector.empty()) {
4,528!
124
    return {};
34✔
125
  }
126
  return *ditypes_vector.begin();
4,494!
127
}
4,712✔
128

129
std::optional<llvm::DIType*> type_for_newlike(const llvm::CallBase* call) {
1,024✔
130
  auto* heapalloc_md = call->getMetadata("heapallocsite");
1,024✔
131
  assert(heapalloc_md != nullptr && "Missing required heapallocsite metadata.");
2,048!
132
  if (auto* type = llvm::dyn_cast<llvm::DIType>(heapalloc_md)) {
2,048!
133
    //    util::DIPrinter printer(llvm::outs(), call->getParent()->getParent()->getParent());
134
    //    printer.traverseType(type);
135
    //    llvm::dbgs() << "Final Type: " << *type << "\n";
136
    return type;
1,024✔
137
  }
138
  return {};
×
139
}
1,024✔
140

141
std::optional<DimetaData> type_for(const llvm::CallBase* call) {
25,640✔
142
  using namespace llvm;
143
  const dimeta::memory::MemOps mem_ops;
25,640✔
144

145
  auto* cb_fun = call->getCalledFunction();
25,640!
146
  if (!cb_fun) {
25,640!
147
    return {};
8✔
148
  }
149

150
  if (!mem_ops.isAlloc(cb_fun->getName())) {
25,632!
151
    LOG_TRACE("Skipping call base: " << cb_fun->getName());
152
    return {};
19,578✔
153
  }
154

155
  std::optional<llvm::DIType*> extracted_type{};
6,054✔
156
  std::optional<ShapeData> shape_type{};
6,054✔
157
  int pointer_level_offset{0};
6,054✔
158

159
  const auto is_fortran_like = mem_ops.isFortranLike(cb_fun->getName());
6,054!
160
  if (is_fortran_like) {
6,054✔
161
    LOG_DEBUG("Type for fortran-like " << cb_fun->getName())
162
    auto fortran_type = fortran::di_type_for(call->getOperand(0), call);
120!
163
    if (fortran_type) {
120!
164
      extracted_type = fortran_type->type;
120✔
165
      if (fortran_type->shape_argument) {
120✔
166
        shape_type = fortran_type->shape_argument;
72!
167
      }
72✔
168
    }
120✔
169
  }
120✔
170

171
  const auto is_cuda_like = mem_ops.isCudaLike(cb_fun->getName());
6,054!
172
  if (is_cuda_like) {
6,054✔
173
    LOG_DEBUG("Type for cuda-like " << cb_fun->getName())
174
    extracted_type = experimental::di_type_for(call->getOperand(0));
198!
175

176
    // when wrapped in, e.g., cudaMalloc<float>(float**, ...), we remove one pointer level:
177
    // auto* parent    = call->getFunction();
178
    // const auto name = std::string{cb_fun->getName()} + "<";
179
    // LOG_DEBUG(name << " vs. " << util::try_demangle(*makeparent))
180
    // if (extracted_type && util::try_demangle(*parent).find(name) != std::string::npos) {
181
    //   LOG_DEBUG("Reset cuda-like pointer level")
182
    //   auto ditype = llvm::dyn_cast<llvm::DIDerivedType>(extracted_type.value());
183
    //   if (ditype->getTag() == llvm::dwarf::DW_TAG_pointer_type) {
184
    //     extracted_type = ditype->getBaseType();
185
    //   }
186
    // }
187
  }
198✔
188

189
  const auto is_cxx_new = mem_ops.isNewLike(cb_fun->getName());
6,054!
190

191
#ifdef DIMETA_USE_HEAPALLOCSITE
192
  if (is_cxx_new) {
6,054✔
193
    if (call->getMetadata("heapallocsite")) {
1,070!
194
      LOG_TRACE("Type for new-like " << cb_fun->getName())
195
      extracted_type = type_for_newlike(call);
1,024!
196
      // !heapallocsite gives the type after "new", i.e., new int -> int, new int*[n] -> int*.
197
      // Our malloc-related algorithm would return int* and int** respectively, however, hence:
198
      pointer_level_offset += 1;
1,024✔
199
    } else {
1,024✔
200
      LOG_DEBUG("new-like allocation does not have heapallocsite metadata.")
201
    }
202
  }
1,070✔
203
#endif
204

205
  if (!extracted_type) {
6,054✔
206
    LOG_DEBUG("Type for malloc-like: " << cb_fun->getName())
207
    extracted_type = type_for_malloclike(call);
4,712!
208
  }
4,712✔
209
  auto source_loc                        = difinder::find_location(call);
6,054!
210
  const auto [final_type, pointer_level] = final_ditype(extracted_type);
6,054!
211
  const auto meta =
6,054✔
212
      DimetaData{DimetaData::MemLoc::kHeap,           {}, extracted_type, final_type, source_loc, shape_type,
10,100!
213
                 pointer_level + pointer_level_offset};
6,054✔
214
  return meta;
6,054!
215
}
25,640✔
216

217
std::optional<DimetaData> type_for(const llvm::AllocaInst* ai) {
11,013✔
218
  const auto local_di_var = difinder::find_local_variable(ai);
11,013✔
219

220
  const auto passed = dataflow::fortran::passed_to_fortran_helper(ai);
11,013✔
221
  if (passed) {
11,013✔
222
    LOG_DEBUG("Skip allocation passed to Flang intrinsic")
223
    return {};
36✔
224
  }
225

226
  if (local_di_var) {
10,977✔
227
    auto extracted_type                    = local_di_var.value()->getType();
10,510✔
228
    auto source_loc                        = difinder::find_location(ai);
10,510✔
229
    const auto [final_type, pointer_level] = final_ditype(extracted_type);
25,146✔
230
    const auto meta =
10,510✔
231
        DimetaData{DimetaData::MemLoc::kStack, local_di_var, extracted_type, final_type, source_loc, {}, pointer_level};
25,146✔
232
    return meta;
10,510!
233
  }
10,510✔
234

235
  LOG_DEBUG("No local_variable for " << *ai)
236

237
  return {};
467✔
238
}
11,013✔
239

240
std::optional<DimetaData> type_for(const llvm::GlobalVariable* gv) {
2,535✔
241
  llvm::SmallVector<llvm::DIGlobalVariableExpression*, 2> dbg_info;
2,535✔
242
  gv->getDebugInfo(dbg_info);
2,535!
243
  if (!dbg_info.empty()) {
2,535!
244
    auto gv_expr                           = *dbg_info.begin();
1,078!
245
    auto gv_type                           = gv_expr->getVariable()->getType();
1,078!
246
    const auto [final_type, pointer_level] = final_ditype(gv_type);
1,078!
247
    return DimetaData{DimetaData::MemLoc::kGlobal, gv_expr->getVariable(), gv_type, final_type, {}, {}, pointer_level};
1,078!
248
  }
1,078✔
249
  return {};
1,457✔
250
}
2,535✔
251

252
std::optional<CompileUnitTypeList> compile_unit_types(const llvm::Module* module) {
3,019✔
253
  CompileUnitTypeList list;
3,019✔
254
  for (auto* compile_unit : module->debug_compile_units()) {
6,021!
255
    CompileUnitTypes current_cu;
3,002✔
256
    current_cu.name = compile_unit->getFilename();
3,002!
257
    for (auto* retained_type : compile_unit->getRetainedTypes()) {
4,996!
258
      if (auto* type = llvm::dyn_cast<llvm::DIType>(retained_type)) {
3,988!
259
        auto dimeta_result = parser::make_dimetadata(type);
1,994!
260
        if (!dimeta_result) {
1,994!
261
          continue;
×
262
        }
263
        current_cu.types.push_back(dimeta_result->type_);
1,994!
264
      }
1,994!
265
    }
1,994!
266
    list.push_back(current_cu);
3,002!
267
  }
3,002✔
268
  return (list.empty() ? std::optional<CompileUnitTypeList>{} : list);
3,019!
269
}
3,019✔
270

271
namespace fortran {
272
std::optional<FortranType> di_type_for(const llvm::Value* value, const llvm::CallBase* call) {
120✔
273
  assert(value != nullptr);
120!
274
  auto shape = dataflow::fortran::shape_from_value(value);
120✔
275

276
  auto paths                = dataflow::experimental::path_from_value(value);
120!
277
  const auto ditypes_vector = collect_types(call, paths);
120!
278
  if (ditypes_vector.empty()) {
120!
NEW
279
    return {};
×
280
  }
281

282
  return FortranType{*ditypes_vector.begin(), shape};
120!
283
}
120✔
284
}  // namespace fortran
285

286
namespace experimental {
287
std::optional<llvm::DIType*> di_type_for(const llvm::Value* value) {
198✔
288
  auto paths                = dataflow::experimental::path_from_value(value);
198✔
289
  const auto ditypes_vector = collect_types(nullptr, paths);
198!
290
  if (ditypes_vector.empty()) {
198!
291
    return {};
×
292
  }
293

294
  return *ditypes_vector.begin();
198!
295
}
198✔
296

297
std::optional<QualifiedType> type_for(const llvm::Value* value) {
182✔
298
  auto paths                = dataflow::experimental::path_from_value(value);
182✔
299
  const auto ditypes_vector = collect_types(nullptr, paths);
182!
300
  if (ditypes_vector.empty()) {
182!
301
    return {};
×
302
  }
303

304
  for (const auto& type : ditypes_vector) {
364!
305
    auto dimeta_result = parser::make_dimetadata(type);
182!
306
    if (!dimeta_result) {
182!
307
      continue;
×
308
    }
309
    return dimeta_result->type_;
182!
310
  }
182!
311

312
  return {};
×
313
}
182✔
314

315
}  // namespace experimental
316

317
}  // 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