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

ahueck / llvm-dimeta / 13933486559

18 Mar 2025 09:06PM UTC coverage: 83.197% (-0.4%) from 83.637%
13933486559

push

github

web-flow
Refactoring (#35)

1027 of 1482 branches covered (69.3%)

Branch coverage included in aggregate %.

86 of 87 new or added lines in 10 files covered. (98.85%)

17 existing lines in 7 files now uncovered.

1924 of 2065 relevant lines covered (93.17%)

3950.95 hits per line

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

83.26
/lib/type/DITypeExtractor.cpp
1

2
//  llvm-dimeta library
3
//  Copyright (c) 2022-2025 llvm-dimeta authors
4
//  Distributed under the BSD 3-Clause license.
5
//  (See accompanying file LICENSE)
6
//  SPDX-License-Identifier: BSD-3-Clause
7
//
8

9
#include "DIFinder.h"
10
#include "DIRootType.h"
11
#include "DIUtil.h"
12
#include "DataflowAnalysis.h"
13
#include "DefUseAnalysis.h"
14
#include "GEP.h"
15
#include "TBAA.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 <type_traits>
46
#include <unordered_map>
47

48
namespace dimeta::type {
49

50
namespace reset {
51

52
using GepToDIMemberMap = std::unordered_map<const llvm::GEPOperator*, llvm::DIDerivedType*>;
53

54
namespace detail {
55

56
template <typename T, typename InstTy>
57
std::optional<const T*> get_operand_to(const InstTy* memory_instruction) {
3,374✔
58
  static_assert(std::is_same_v<InstTy, llvm::StoreInst> || std::is_same_v<InstTy, llvm::LoadInst>,
59
                "Expected load or store instruction");
60
  const auto* memory_target = memory_instruction->getPointerOperand();
3,374✔
61
  if (llvm::isa<T>(memory_target)) {
3,374!
62
    return llvm::dyn_cast<T>(memory_target);
578✔
63
  }
64

65
  if (auto bcast = llvm::dyn_cast<llvm::BitCastInst>(memory_target)) {
3,064!
66
    if (llvm::isa<T>(bcast->getOperand(0))) {
268!
67
      return llvm::dyn_cast<T>(bcast->getOperand(0));
×
68
    }
69
  }
268✔
70

71
  return {};
2,796✔
72
}
3,374✔
73

74
}  // namespace detail
75

76
template <typename T>
77
bool store_to(const llvm::StoreInst* store) {
2,092✔
78
  return detail::get_operand_to<T>(store).has_value();
2,092✔
79
}
80

81
template <typename T>
82
bool load_to(const llvm::LoadInst* load) {
1,282✔
83
  return detail::get_operand_to<T>(load).has_value();
1,282✔
84
}
85

86
std::optional<llvm::DIType*> reset_load_related_basic(const dataflow::ValuePath&, llvm::DIType* type_to_reset,
686✔
87
                                                      const llvm::LoadInst* load) {
88
  auto* type = type_to_reset;
686✔
89

90
  if (load_to<llvm::GlobalVariable>(load) || load_to<llvm::AllocaInst>(load)) {
686✔
91
    // if (auto* maybe_ptr_to_type = llvm::dyn_cast<llvm::DIDerivedType>(type)) {
92
    //   if ((maybe_ptr_to_type->getTag() == llvm::dwarf::DW_TAG_pointer_type ||
93
    //        maybe_ptr_to_type->getTag() == llvm::dwarf::DW_TAG_reference_type)) {
94
    //     LOG_DEBUG("Load of pointer-like " << log::ditype_str(maybe_ptr_to_type))
95
    //     if (load->getPointerOperandType()->isOpaquePointerTy()) {
96
    //       return maybe_ptr_to_type->getBaseType();
97
    //     }
98
    //   }
99
    // }
100
    LOG_DEBUG("Do not reset DIType based on load to global,alloca")
101
    return type;
440✔
102
  }
103

104
  if (auto* maybe_ptr_to_type = llvm::dyn_cast<llvm::DIDerivedType>(type)) {
492!
105
    if (di::util::is_pointer(*maybe_ptr_to_type)) {
246✔
106
      LOG_DEBUG("Load of pointer-like " << log::ditype_str(maybe_ptr_to_type))
107
    }
192✔
108

109
    auto* base_type = maybe_ptr_to_type->getBaseType();
246✔
110

111
    if (auto* composite = llvm::dyn_cast<llvm::DICompositeType>(base_type)) {
318✔
112
      LOG_DEBUG("Have ptr to composite " << log::ditype_str(composite))
113
      auto type_tbaa = tbaa::resolve_tbaa(base_type, *load);
72✔
114
      if (type_tbaa) {
72!
115
        return type_tbaa;
72✔
116
      }
UNCOV
117
    }
×
118
    return base_type;
174✔
119
  }
246✔
120

121
  return type;
×
122
}
686✔
123

124
std::optional<llvm::DIType*> reset_store_related_basic(const dataflow::ValuePath&, llvm::DIType* type_to_reset,
1,014✔
125
                                                       const llvm::StoreInst* store_inst) {
126
  auto* type = type_to_reset;
1,014✔
127

128
  if (store_to<llvm::GlobalVariable>(store_inst) || store_to<llvm::AllocaInst>(store_inst)) {
1,014!
129
    // Relevant in "heap_lulesh_mock_char.cpp"
130
    LOG_DEBUG("Store to alloca/global, return " << log::ditype_str(type))
131
    return type;
124✔
132
  }
133

134
  if (!llvm::isa<llvm::DIDerivedType>(type)) {
890✔
135
    LOG_DEBUG("Store resolved, return " << log::ditype_str(type))
136
    return type;
56✔
137
  }
138

139
  auto* derived_type = llvm::cast<llvm::DIDerivedType>(type);
834✔
140

141
  if (di::util::is_non_static_member(*derived_type)) {
834✔
142
    auto* member_base               = derived_type->getBaseType();
420✔
143
    const bool is_array_type_member = member_base->getTag() == llvm::dwarf::DW_TAG_array_type;
420✔
144
    // Need to look at base type for array-type member of struct. Tests w.r.t. gep:
145
    // 1. array_composite.c
146
    // 2. array_composite_offset_zero.c
147
    // 3. array_composite_sub_offset_zero.c
148
    // 4. global_nested.c
149
    if (is_array_type_member) {
420✔
150
      return llvm::cast<llvm::DICompositeType>(member_base)->getBaseType();
98✔
151
    }
152
    return member_base;
322✔
153
  }
420✔
154

155
  if (di::util::is_pointer(*derived_type)) {
414✔
156
    if (auto* may_be_ptr_to_ptr = llvm::dyn_cast<llvm::DIDerivedType>(derived_type->getBaseType())) {
696✔
157
      // Pointer to pointer by default remove one level for RHS assignment type w.r.t. store:
158
      const auto is_ptr_to_ptr = di::util::is_pointer(*may_be_ptr_to_ptr);
302✔
159
      if (is_ptr_to_ptr) {
302✔
160
        LOG_DEBUG("Store to ptr-ptr, return " << log::ditype_str(may_be_ptr_to_ptr))
161
        return may_be_ptr_to_ptr;
282✔
162
      }
163
    }
302✔
164

165
    if (auto* ptr_to_composite = llvm::dyn_cast<llvm::DICompositeType>(derived_type->getBaseType())) {
176✔
166
      if (store_to<llvm::LoadInst>(store_inst)) {
64✔
167
        // Triggers for "heap_lhs_obj_opt.c" (llvm 14/15)
168
        auto composite_members = ptr_to_composite->getElements();
14✔
169
        assert(!composite_members.empty() && "Store to composite assumed to be store to first member!");
28!
170
        // auto store_di_target = llvm::dyn_cast<llvm::DIDerivedType>(composite_members[0])->getBaseType();
171

172
        for (auto* member : composite_members) {
36!
173
          if (di::util::is_non_static_member(*member)) {
22✔
174
            auto* store_di_target = llvm::dyn_cast<llvm::DIDerivedType>(member);
14✔
175
            LOG_DEBUG("Store to a 'load of a composite type', assume first member as target "
176
                      << log::ditype_str(store_di_target))
177
            return store_di_target->getBaseType();
14✔
178
          }
14✔
179
        }
22✔
180
      }
14!
181
    }
50✔
182
  }
98✔
183

184
  LOG_DEBUG("Store resolved, return " << log::ditype_str(type))
185
  return type;
118✔
186
}
1,014✔
187

188
template <typename Iter, typename Iter2>
189
std::optional<llvm::DIType*> reset_ditype(llvm::DIType* type_to_reset, const dataflow::ValuePath& path,
4,236✔
190
                                          const Iter& path_iter, const Iter2&) {
191
  std::optional<llvm::DIType*> type = type_to_reset;
4,236✔
192
  if (!type) {
4,236!
193
    LOG_DEBUG("No type to reset!")
194
    return {};
×
195
  }
196

197
  const auto& next_value = path_iter;
4,236✔
198
  LOG_DEBUG("Type to reset: " << log::ditype_str(*type));
199
  LOG_DEBUG(">> based on IR: " << **next_value);
200

201
  if (llvm::isa<llvm::GEPOperator>(*next_value)) {
4,236✔
202
    LOG_DEBUG("Reset based on GEP")
203
    auto* gep = llvm::cast<llvm::GEPOperator>(*next_value);
1,018✔
204
    // LOG_DEBUG("Path iter gep for extraction is currently " << *gep);
205
    // TODO: Maybe we could somehow get more info on the underlying type from the dataflow path
206
    //       if this returns an empty result due to forward decls?
207
    const auto gep_result = gep::extract_gep_dereferenced_type(type.value(), *gep);
1,018✔
208
    if (gep_result.member) {
1,018✔
209
      LOG_DEBUG("Using gep member type result")
210
      return gep_result.member;
660✔
211
    }
212
    return gep_result.type;
358✔
213
  }
1,018✔
214

215
  if (const auto* load = llvm::dyn_cast<llvm::LoadInst>(*next_value)) {
3,904✔
216
    // Re-set the DIType from the gep, if presence of:
217
    // - a load after a gep is likely the first element of the composite type
218
    // - a load also resolves to the basetype w.r.t. an array composite
219
    LOG_DEBUG("Reset based on load")
220
    return reset::reset_load_related_basic(path, type.value(), load);
686✔
221
  }
222

223
  if (const auto* store_inst = llvm::dyn_cast<llvm::StoreInst>(*next_value)) {
3,546✔
224
    // - a store with a ditype(array) is likely the first element of the array
225
    LOG_DEBUG("Reset based on store")
226
    return reset::reset_store_related_basic(path, type.value(), store_inst);
1,014✔
227
  }
228

229
  LOG_DEBUG(">> skipping");
230

231
  return type;
1,518✔
232
}
4,236✔
233

234
}  // namespace reset
235

236
std::optional<llvm::DIType*> find_type(const dataflow::CallValuePath& call_path) {
1,174✔
237
  auto type = root::find_type_root(call_path);
1,174✔
238

239
  if (!type) {
1,174✔
240
    LOG_DEBUG("find_type_root failed to find a type for path " << call_path.path)
241
    return {};
10✔
242
  }
243

244
  const auto path_end = call_path.path.path_to_value.rend();
1,164✔
245
  for (auto path_iter = call_path.path.path_to_value.rbegin(); path_iter != path_end; ++path_iter) {
5,400✔
246
    if (!type) {
4,236!
247
      break;
×
248
    }
249
    LOG_DEBUG("Extracted type w.r.t. gep: " << log::ditype_str(*type));
250
    type = reset::reset_ditype(type.value(), call_path.path, path_iter, path_end).value_or(type.value());
4,236✔
251
    LOG_DEBUG("reset_ditype result " << log::ditype_str(type.value_or(nullptr)) << "\n")
252
  }
4,236✔
253

254
  if (type) {
1,164!
255
    // If last node is a store inst, try to extract type via TBAA
256
    const auto* const start_node = llvm::dyn_cast_or_null<llvm::StoreInst>(*call_path.path.start_value());
1,164✔
257
    if (start_node) {
1,164✔
258
      auto type_tbaa = tbaa::resolve_tbaa(type.value(), *llvm::dyn_cast<llvm::Instruction>(start_node));
1,014✔
259
      if (type_tbaa) {
1,014✔
260
        type = type_tbaa.value();
994✔
261
      }
994✔
262
    }
1,014✔
263
  }
1,164✔
264

265
  return type;
1,164✔
266
}
1,174✔
267
}  // namespace dimeta::type
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