• 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

59.9
/lib/type/DIRootType.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 "DIRootType.h"
9

10
#include "DIFinder.h"
11
#include "DITypeExtractor.h"
12
#include "DataflowAnalysis.h"
13
#include "DefUseAnalysis.h"
14
#include "Dimeta.h"
15
#include "MemoryOps.h"
16
#include "Util.h"
17
#include "ValuePath.h"
18
#include "support/Logger.h"
19

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

43
#include <cassert>
44
#include <iterator>
45
#include <optional>
46

47
namespace dimeta::root {
48

49
namespace helper {
50

51
std::optional<llvm::DIType*> get_return_type_of(const llvm::Function* called_function,
117✔
52
                                                const llvm::CallBase* call_instruction) {
53
  if (auto* sub_program = called_function->getSubprogram(); sub_program != nullptr) {
234!
54
    auto types_of_subprog = sub_program->getType()->getTypeArray();
117✔
55
    assert(types_of_subprog.size() > 0 && "Need the return type of the function");
234!
56
    auto* return_type = types_of_subprog[0];
117✔
57
    LOG_DEBUG("Found return type " << log::ditype_str(return_type))
58
    return return_type;
117✔
59
  }
117✔
60
  LOG_DEBUG("Function has no subProgram to query, trying di_local finder")
61
  auto di_local = difinder::find_local_variable(call_instruction);
×
62
  if (di_local) {
×
63
    // TODO: for now we ignore local vars with name "this", as these are auto generated
64
    auto is_this_var = di_local.value()->getName() == "this";
×
65
    if (is_this_var) {
×
66
      LOG_DEBUG("'this' local variable as store target unsupported.")
67
      return {};
×
68
    }
69
    LOG_DEBUG("Found local variable " << log::ditype_str(di_local.value()))
70
    return di_local.value()->getType();
×
71
  }
×
72
  return {};
×
73
}
117✔
74

75
std::optional<llvm::DIType*> type_of_store_to_call(const dataflow::ValuePath& path, const llvm::Function* called_f,
273✔
76
                                                   const llvm::CallBase* call_inst) {
77
  if (auto* leaf_value = path.start_value().value_or(nullptr); leaf_value != nullptr) {
546!
78
    LOG_DEBUG("Looking at start value of path: " << *leaf_value)
79
    // Here we look at if we store to a function that returns pointer/ref,
80
    // indicating return of call is the type we need:
81
    if (auto* store = llvm::dyn_cast<llvm::StoreInst>(leaf_value)) {
377✔
82
      // We have a final store and root w.r.t. call, hence, assume return type is the relevant root DIType:
83
      return get_return_type_of(called_f, call_inst);
104✔
84
    }
85
  }
169✔
86
  return {};
169✔
87
}
273✔
88

89
std::optional<llvm::DIType*> type_of_call_argument(const dataflow::ValuePath& path, const llvm::Function* called_f,
156✔
90
                                                   const llvm::CallBase* call_inst) {
91
  // Argument passed to current call:
92
  const auto* arg_val = path.previous_value().value_or(nullptr);
156✔
93

94
  // if (arg_val == nullptr) {
95
  //   LOG_DEBUG("Previous value should be argument to some function!")
96
  //   return {};
97
  // }
98

99
  assert(arg_val != nullptr && "Previous value should be argument to some function!");
312!
100
  // Argument number:
101
  const auto* const arg_pos =
312✔
102
      llvm::find_if(call_inst->args(), [&arg_val](const auto& arg_use) -> bool { return arg_use.get() == arg_val; });
520✔
103

104
  if (arg_pos == std::end(call_inst->args())) {
156!
105
    LOG_DEBUG("Could not find arg position for " << *arg_val)
106
    return {};
×
107
  }
108

109
  auto arg_num = std::distance(call_inst->arg_begin(), arg_pos);
156✔
110
  LOG_DEBUG("Looking at arg pos " << arg_num)
111
  // Extract debug info from function at arg_num:
112
  if (auto* sub_program = called_f->getSubprogram(); sub_program != nullptr) {
312!
113
    // DI-types of a subprog. include return type at pos 0, hence + 1:
114
    const auto sub_prog_arg_pos = arg_num + 1;
156✔
115
    auto types_of_subprog       = sub_program->getType()->getTypeArray();
156✔
116
    assert((types_of_subprog.size() > sub_prog_arg_pos) && "Type array smaller than arg num!");
312!
117
    auto* type = types_of_subprog[sub_prog_arg_pos];
156✔
118
    LOG_DEBUG("Found DIType at arg pos " << log::ditype_str(type))
119
    return type;
156✔
120
  }
156✔
121
  LOG_DEBUG("Did not find arg pos")
122
  return {};
×
123
}
156✔
124

125
std::optional<llvm::DIType*> type_of_argument(const llvm::Argument& argument) {
1,698✔
126
  if (auto* subprogram = argument.getParent()->getSubprogram(); subprogram != nullptr) {
3,396!
127
    const auto type_array = subprogram->getType()->getTypeArray();
1,698✔
128
    const auto arg_pos    = [&](const auto arg_num) {
5,094✔
129
      if (argument.hasStructRetAttr()) {
1,698✔
130
        // return value is passed as argument at this point
131
        return arg_num;  // see test cpp/heap_lhs_function_opt_nofwd.cpp
12✔
132
      }
133
      return arg_num + 1;
1,686✔
134
    }(argument.getArgNo());
3,396✔
135

136
    LOG_DEBUG(log::ditype_str(subprogram) << " -> " << argument)
137
    LOG_DEBUG("Arg data: " << argument.getArgNo() << " Type num operands: " << type_array->getNumOperands())
138
    assert(arg_pos < type_array.size() && "Arg position greater than DI type array of subprogram!");
3,396!
139
    return type_array[arg_pos];
1,698✔
140
  }
1,698✔
141

142
  return {};
×
143
}
1,698✔
144

145
std::optional<llvm::DIType*> type_of_global(const llvm::GlobalVariable* global_variable) {
514✔
146
  auto dbg_md = global_variable->getMetadata("dbg");
514✔
147
  if (!dbg_md) {
514!
NEW
148
    return {};
×
149
  }
150
  if (auto* global_expression = llvm::dyn_cast<llvm::DIGlobalVariableExpression>(dbg_md)) {
1,028!
151
    return global_expression->getVariable()->getType();
514✔
152
  }
NEW
153
  return {};
×
154
}
514✔
155

156
}  // namespace helper
157

158
std::optional<llvm::DIType*> find_type_root(const dataflow::CallValuePath& call_path) {
5,344✔
159
  using namespace llvm;
160
  const auto* root_value = call_path.path.value().value_or(nullptr);
5,344✔
161
  if (!root_value) {
5,344!
162
    return {};
×
163
  }
164
  LOG_DEBUG("Root value is " << *root_value)
165

166
  if (const auto* ret = dyn_cast<ReturnInst>(root_value)) {
5,524✔
167
    auto* sub_prog = ret->getFunction()->getSubprogram();
180✔
168
    if (!sub_prog) {
180✔
169
      return {};
12✔
170
    }
171
    auto type_array = sub_prog->getType()->getTypeArray();
168✔
172
    if (type_array.size() > 0) {
168!
173
      return {type_array[0]};
168✔
174
    }
175
    return {};
×
176
  }
180✔
177

178
  if (const auto* alloca = dyn_cast<AllocaInst>(root_value)) {
7,547✔
179
    auto local_di_var = difinder::find_local_variable(alloca);
2,383✔
180
    if (local_di_var) {
2,383✔
181
      return local_di_var.value()->getType();
2,303✔
182
    }
183

184
    for (auto user : alloca->users()) {
420!
185
      if (auto store = llvm::dyn_cast<llvm::StoreInst>(user)) {
340!
186
        if (const auto* argument = llvm::dyn_cast<llvm::Argument>(store->getValueOperand())) {
×
187
          return helper::type_of_argument(*argument);
×
188
        }
189
      }
×
190
    }
340!
191

192
    // see test heap_case_inheritance.cpp (e.g., returns several objects as base class pointer):
193
    // TODO: check if that ever applies to C, should probably only execute for C++ codes.
194
    LOG_DEBUG("Dataflow analysis of alloca")
195
    auto paths_from_alloca = dataflow::path_from_alloca(alloca);
80✔
196
    for (auto& path : paths_from_alloca) {
160!
197
      LOG_DEBUG("Path from alloca " << path)
198
      auto type_of_alloca = find_type_root(dataflow::CallValuePath{std::nullopt, path});
80!
199
      if (type_of_alloca) {
80!
200
        return type_of_alloca;
80✔
201
      }
202
    }
80!
203

204
    return {};
×
205
  }
2,383✔
206

207
  if (const auto* call_inst = llvm::dyn_cast<CallBase>(root_value)) {
3,358✔
208
    LOG_DEBUG("Root is a call")
209
    const auto* called_f = call_inst->getCalledFunction();
577✔
210
    if (called_f == nullptr) {
577!
211
      LOG_DEBUG("Called function not found for call base " << *call_inst)
212
      return {};
×
213
    }
214
    {
215
      // Fortran extension
216
      // TODO: handle memcpy indirection
217
      if (MemCpyInst::classof(call_inst)) {
577✔
218
        LOG_DEBUG("Found memcpy, take source " << *call_inst->getArgOperand(1))
219
        if (auto alloca = llvm::dyn_cast<llvm::AllocaInst>(call_inst->getArgOperand(1))) {
160!
220
          // auto type_of_alloca = find_type_root(dataflow::CallValuePath{std::nullopt, call_path.path});
221
          auto local_di_var = difinder::find_local_variable(alloca);
12✔
222
          if (local_di_var) {
12!
223
            LOG_DEBUG("Found type through memcpy")
224
            return local_di_var.value()->getType();
12✔
225
          }
226

NEW
227
          for (auto user : alloca->users()) {
×
NEW
228
            if (auto store = llvm::dyn_cast<llvm::StoreInst>(user)) {
×
NEW
229
              if (const auto* argument = llvm::dyn_cast<llvm::Argument>(store->getValueOperand())) {
×
230
                LOG_DEBUG("Found type through memcpy (argument)")
NEW
231
                return helper::type_of_argument(*argument);
×
232
              }
NEW
233
            }
×
NEW
234
          }
×
NEW
235
          if (call_path.path.contains(alloca)) {
×
236
            LOG_DEBUG("Alloca contained in path, skipping further analysis")
NEW
237
            return {};
×
238
          }
239

240
          LOG_DEBUG("Dataflow analysis of alloca")
NEW
241
          auto paths_from_alloca = dataflow::path_from_alloca(alloca);
×
NEW
242
          for (auto& path : paths_from_alloca) {
×
243
            LOG_DEBUG("Path from alloca " << path)
NEW
244
            auto type_of_alloca = find_type_root(dataflow::CallValuePath{std::nullopt, path});
×
NEW
245
            if (type_of_alloca) {
×
NEW
246
              return type_of_alloca;
×
247
            }
NEW
248
          }
×
249
        } else if (auto* global = llvm::dyn_cast<llvm::GlobalVariable>(call_inst->getArgOperand(1))) {
148!
250
          LOG_DEBUG("Memcpy of global variable detected")
251
          return helper::type_of_global(global);
8✔
252
        } else if (auto* gep = llvm::dyn_cast<llvm::GetElementPtrInst>(call_inst->getArgOperand(1))) {
120!
253
          LOG_DEBUG("Memcpy of GEP detected")
254
          auto backward_paths_from_gep = dataflow::experimental::path_from_value(gep);
60✔
255
          for (auto& path : backward_paths_from_gep) {
120!
256
            LOG_DEBUG("Path from gep " << path)
257
            // Test fortran 16_...f90, allocation "ALLOCATE(chunk%tiles(t)%field%density ...)"
258
            // Use type::find_type to resolve the nested member type from the GEP access path
259
            auto type_of_member = type::find_type(dataflow::CallValuePath{std::nullopt, path});
60!
260
            if (type_of_member) {
60!
261
              LOG_DEBUG("Detected gep member type " << log::ditype_str(type_of_member.value()))
262
              return type_of_member;
60✔
263
            }
264
          }
60!
265
        }
60!
NEW
266
      }
×
267
    }
268

269
    dimeta::memory::MemOps ops;
497✔
270
    if (ops.allocKind(called_f->getName())) {
497!
271
      // see test c/heap_tachyon_mock_images.c
272
      LOG_DEBUG("Root is malloc-like call")
273
      // TODO ask for type of newlike call here!
274
      if (call_path.call && (call_inst == call_path.call.value())) {
224!
275
        // Test triggers by cpp/heap_lhs_function_opt_nofwd.cpp and ir/01_endless_recursion.ll
276
        LOG_WARNING("Root value is the same as the initial malloc-like call")
30!
277
        return {};
30✔
278
      }
279
      auto extracted_type = type_for(call_inst);
194!
280
      if (!extracted_type) {
194!
281
        LOG_DEBUG("Failed to collect DI data for " << called_f->getName())
282
        return {};
×
283
      }
284

285
      return extracted_type->entry_type;
194✔
286
    }
194✔
287

288
    const auto& path = call_path.path;
273✔
289

290
    // "a = malloc;", "store a, get_pointer();" -> look at return type of get_pointer()
291
    auto store_function_type = helper::type_of_store_to_call(path, called_f, call_inst);
273!
292
    if (store_function_type) {
273✔
293
      return store_function_type;
104✔
294
    }
295

296
    if (path.previous_value()) {
169!
297
      // foo(malloc(...)); -> look at foo args
298
      auto type_of_call_arg = helper::type_of_call_argument(path, called_f, call_inst);
156!
299
      if (!type_of_call_arg) {
156!
300
        LOG_DEBUG("Did not find arg pos")
301
        return {};
×
302
      }
303
      return type_of_call_arg;
156✔
304
    }
156✔
305

306
    LOG_DEBUG("Trying return type of function " << *called_f)
307
    return helper::get_return_type_of(called_f, call_inst);
13!
308
  }
577✔
309

310
  if (const auto* global_variable = llvm::dyn_cast<llvm::GlobalVariable>(root_value)) {
2,710✔
311
    return helper::type_of_global(global_variable);
506✔
312
  }
313

314
  if (const auto* argument = llvm::dyn_cast<llvm::Argument>(root_value)) {
3,396!
315
    return helper::type_of_argument(*argument);
1,698✔
316
  }
317

318
  if (const auto* const_expr = llvm::dyn_cast<llvm::ConstantExpr>(root_value)) {
×
319
    LOG_DEBUG("ConstantExpr unsupported");
320
  }
×
321

322
  LOG_DEBUG("No matching value found for " << *root_value);
323
  return {};
×
324
}
5,344✔
325

326
}  // namespace dimeta::root
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