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

ahueck / llvm-dimeta / 19598509829

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

push

github

web-flow
Merge PR #48 from ahueck/devel

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

59.73
/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 "DataflowAnalysis.h"
12
#include "DefUseAnalysis.h"
13
#include "Dimeta.h"
14
#include "MemoryOps.h"
15
#include "Util.h"
16
#include "ValuePath.h"
17
#include "support/Logger.h"
18

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

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

46
namespace dimeta::root {
47

48
namespace helper {
49

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

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

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

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

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

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

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

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

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

141
  return {};
×
142
}
848✔
143

144
}  // namespace helper
145

146
std::optional<llvm::DIType*> find_type_root(const dataflow::CallValuePath& call_path) {
3,162✔
147
  using namespace llvm;
148
  const auto* root_value = call_path.path.value().value_or(nullptr);
3,162✔
149
  if (!root_value) {
3,162!
150
    return {};
×
151
  }
152
  LOG_DEBUG("Root value is " << *root_value)
153

154
  if (const auto* ret = dyn_cast<ReturnInst>(root_value)) {
3,318✔
155
    auto* sub_prog = ret->getFunction()->getSubprogram();
156✔
156
    if (!sub_prog) {
156✔
157
      return {};
12✔
158
    }
159
    auto type_array = sub_prog->getType()->getTypeArray();
144✔
160
    if (type_array.size() > 0) {
144!
161
      return {type_array[0]};
144✔
162
    }
163
    return {};
×
164
  }
156✔
165

166
  if (const auto* alloca = dyn_cast<AllocaInst>(root_value)) {
4,519✔
167
    auto local_di_var = difinder::find_local_variable(alloca);
1,513✔
168
    if (local_di_var) {
1,513!
169
      return local_di_var.value()->getType();
1,513✔
170
    }
171

UNCOV
172
    for (auto user : alloca->users()) {
×
UNCOV
173
      if (auto store = llvm::dyn_cast<llvm::StoreInst>(user)) {
×
UNCOV
174
        if (const auto* argument = llvm::dyn_cast<llvm::Argument>(store->getValueOperand())) {
×
UNCOV
175
          return helper::type_of_argument(*argument);
×
176
        }
177
      }
×
UNCOV
178
    }
×
179

180
    // see test heap_case_inheritance.cpp (e.g., returns several objects as base class pointer):
181
    // TODO: check if that ever applies to C, should probably only execute for C++ codes.
182
    LOG_DEBUG("Dataflow analysis of alloca")
183
    auto paths_from_alloca = dataflow::path_from_alloca(alloca);
×
184
    for (auto& path : paths_from_alloca) {
×
185
      LOG_DEBUG("Path from alloca " << path)
186
      auto type_of_alloca = find_type_root(dataflow::CallValuePath{std::nullopt, path});
×
187
      if (type_of_alloca) {
×
188
        return type_of_alloca;
×
189
      }
190
    }
×
191

192
    return {};
×
193
  }
1,513✔
194

195
  if (const auto* call_inst = llvm::dyn_cast<CallBase>(root_value)) {
1,764✔
196
    LOG_DEBUG("Root is a call")
197
    const auto* called_f = call_inst->getCalledFunction();
271✔
198
    if (called_f == nullptr) {
271!
199
      LOG_DEBUG("Called function not found for call base " << *call_inst)
200
      return {};
×
201
    }
202

203
    dimeta::memory::MemOps ops;
271✔
204
    if (ops.allocKind(called_f->getName())) {
271!
205
      // see test c/heap_tachyon_mock_images.c
206
      LOG_DEBUG("Root is malloc-like call")
207
      // TODO ask for type of newlike call here!
208
      if (call_path.call && (call_inst == call_path.call.value())) {
40!
209
        // Test triggers by cpp/heap_lhs_function_opt_nofwd.cpp and ir/01_endless_recursion.ll
210
        LOG_WARNING("Root value is the same as the initial malloc-like call")
18!
211
        return {};
18✔
212
      }
213
      auto extracted_type = type_for(call_inst);
22!
214
      if (!extracted_type) {
22!
215
        LOG_DEBUG("Failed to collect DI data for " << called_f->getName())
216
        return {};
×
217
      }
218

219
      return extracted_type->entry_type;
22✔
220
    }
22✔
221

222
    const auto& path = call_path.path;
231✔
223

224
    // "a = malloc;", "store a, get_pointer();" -> look at return type of get_pointer()
225
    auto store_function_type = helper::type_of_store_to_call(path, called_f, call_inst);
231!
226
    if (store_function_type) {
231✔
227
      return store_function_type;
88✔
228
    }
229

230
    if (path.previous_value()) {
143!
231
      // foo(malloc(...)); -> look at foo args
232
      auto type_of_call_arg = helper::type_of_call_argument(path, called_f, call_inst);
132!
233
      if (!type_of_call_arg) {
132!
234
        LOG_DEBUG("Did not find arg pos")
235
        return {};
×
236
      }
237
      return type_of_call_arg;
132✔
238
    }
132✔
239

240
    LOG_DEBUG("Trying return type of function " << *called_f)
241
    return helper::get_return_type_of(called_f, call_inst);
11!
242
  }
271✔
243

244
  if (const auto* global_variable = llvm::dyn_cast<llvm::GlobalVariable>(root_value)) {
1,596✔
245
    auto dbg_md = global_variable->getMetadata("dbg");
374✔
246
    if (!dbg_md) {
374!
247
      return {};
×
248
    }
249
    if (auto* global_expression = llvm::dyn_cast<llvm::DIGlobalVariableExpression>(dbg_md)) {
748!
250
      return global_expression->getVariable()->getType();
374✔
251
    }
252
    return {};
×
253
  }
374✔
254

255
  if (const auto* argument = llvm::dyn_cast<llvm::Argument>(root_value)) {
1,696!
256
    return helper::type_of_argument(*argument);
848✔
257
  }
258

259
  if (const auto* const_expr = llvm::dyn_cast<llvm::ConstantExpr>(root_value)) {
×
260
    LOG_DEBUG("ConstantExpr unsupported");
261
  }
×
262

263
  LOG_DEBUG("No matching value found for " << *root_value);
264
  return {};
×
265
}
3,162✔
266

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