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

ahueck / llvm-dimeta / 15085917963

17 May 2025 02:01PM UTC coverage: 83.355% (+0.06%) from 83.296%
15085917963

push

github

web-flow
Value typing (#42)

1153 of 1664 branches covered (69.29%)

Branch coverage included in aggregate %.

200 of 218 new or added lines in 14 files covered. (91.74%)

1 existing line in 1 file now uncovered.

2112 of 2253 relevant lines covered (93.74%)

4268.46 hits per line

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

81.82
/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,978✔
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,978✔
61
  if (llvm::isa<T>(memory_target)) {
3,978!
62
    return llvm::dyn_cast<T>(memory_target);
753✔
63
  }
64

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

71
  return {};
3,217✔
72
}
3,978✔
73

74
}  // namespace detail
75

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

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

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

90
  if (load_to<llvm::GlobalVariable>(load) || load_to<llvm::AllocaInst>(load)) {
879✔
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;
573✔
102
  }
103

104
  // a (last?) load to a GEP of a composite likely loads the first member in an optimized context:
105
  const bool last_load = path.start_value().value_or(nullptr) == load;
306✔
106
  if (last_load && load_to<llvm::GetElementPtrInst>(load)) {
306!
107
    if (auto* may_be_member_type = llvm::dyn_cast<llvm::DIDerivedType>(type)) {
80!
108
      LOG_DEBUG("Load on GEP, return basetype " << log::ditype_str(may_be_member_type->getBaseType()))
109
      if (di::util::is_member(*may_be_member_type)) {
40!
110
        auto type_of_member = may_be_member_type->getBaseType();
40✔
111
        if (auto member = llvm::dyn_cast<llvm::DIDerivedType>(type_of_member)) {
50✔
112
          may_be_member_type = member;
10✔
113
          if (auto* member_composite_type = llvm::dyn_cast<llvm::DICompositeType>(member->getBaseType())) {
20!
114
            auto members_of_composite_type = di::util::get_composite_members(*member_composite_type);
10✔
115
            assert(members_of_composite_type.size() > 0 && "Load to composite expects at least one member");
20!
116
            return (*members_of_composite_type.begin());
10✔
117
          }
10✔
NEW
118
        }
×
119
      }
40✔
120
    }
30✔
121
  }
30✔
122

123
  if (auto* maybe_ptr_to_type = llvm::dyn_cast<llvm::DIDerivedType>(type)) {
592!
124
    if (di::util::is_pointer(*maybe_ptr_to_type)) {
296✔
125
      LOG_DEBUG("Load of pointer-like " << log::ditype_str(maybe_ptr_to_type))
126
    }
202✔
127

128
    auto* base_type = maybe_ptr_to_type->getBaseType();
296✔
129

130
    if (auto* composite = llvm::dyn_cast<llvm::DICompositeType>(base_type)) {
378✔
131
      LOG_DEBUG("Have ptr to composite " << log::ditype_str(composite))
132
      auto type_tbaa = tbaa::resolve_tbaa(base_type, *load);
82✔
133
      if (type_tbaa) {
82✔
134
        return type_tbaa;
72✔
135
      }
136
    }
10✔
137
    return base_type;
224✔
138
  }
296✔
139

140
  return type;
×
141
}
879✔
142

143
std::optional<llvm::DIType*> reset_store_related_basic(const dataflow::ValuePath&, llvm::DIType* type_to_reset,
1,098✔
144
                                                       const llvm::StoreInst* store_inst) {
145
  auto* type = type_to_reset;
1,098✔
146

147
  if (store_to<llvm::GlobalVariable>(store_inst) || store_to<llvm::AllocaInst>(store_inst)) {
1,098!
148
    // Relevant in "heap_lulesh_mock_char.cpp"
149
    LOG_DEBUG("Store to alloca/global, return " << log::ditype_str(type))
150
    return type;
134✔
151
  }
152

153
  if (!llvm::isa<llvm::DIDerivedType>(type)) {
964✔
154
    LOG_DEBUG("Store resolved, return " << log::ditype_str(type))
155
    return type;
62✔
156
  }
157

158
  auto* derived_type = llvm::cast<llvm::DIDerivedType>(type);
902✔
159

160
  if (di::util::is_non_static_member(*derived_type)) {
902✔
161
    auto* member_base               = derived_type->getBaseType();
434✔
162
    const bool is_array_type_member = member_base->getTag() == llvm::dwarf::DW_TAG_array_type;
434✔
163
    // Need to look at base type for array-type member of struct. Tests w.r.t. gep:
164
    // 1. array_composite.c
165
    // 2. array_composite_offset_zero.c
166
    // 3. array_composite_sub_offset_zero.c
167
    // 4. global_nested.c
168
    if (is_array_type_member) {
434✔
169
      return llvm::cast<llvm::DICompositeType>(member_base)->getBaseType();
108✔
170
    }
171
    return member_base;
326✔
172
  }
434✔
173

174
  if (di::util::is_pointer(*derived_type)) {
468✔
175
    if (auto* may_be_ptr_to_ptr = llvm::dyn_cast<llvm::DIDerivedType>(derived_type->getBaseType())) {
770✔
176
      // Pointer to pointer by default remove one level for RHS assignment type w.r.t. store:
177
      const auto is_ptr_to_ptr = di::util::is_pointer(*may_be_ptr_to_ptr);
322✔
178
      if (is_ptr_to_ptr) {
322✔
179
        LOG_DEBUG("Store to ptr-ptr, return " << log::ditype_str(may_be_ptr_to_ptr))
180
        return may_be_ptr_to_ptr;
302✔
181
      }
182
    }
322✔
183

184
    if (auto* ptr_to_composite = llvm::dyn_cast<llvm::DICompositeType>(derived_type->getBaseType())) {
220✔
185
      if (store_to<llvm::LoadInst>(store_inst)) {
74✔
186
        // Triggers for "heap_lhs_obj_opt.c" (llvm 14/15)
187
        auto composite_members = ptr_to_composite->getElements();
14✔
188
        assert(!composite_members.empty() && "Store to composite assumed to be store to first member!");
28!
189
        // auto store_di_target = llvm::dyn_cast<llvm::DIDerivedType>(composite_members[0])->getBaseType();
190

191
        for (auto* member : composite_members) {
36!
192
          if (di::util::is_non_static_member(*member)) {
22✔
193
            auto* store_di_target = llvm::dyn_cast<llvm::DIDerivedType>(member);
14✔
194
            LOG_DEBUG("Store to a 'load of a composite type', assume first member as target "
195
                      << log::ditype_str(store_di_target))
196
            return store_di_target->getBaseType();
14✔
197
          }
14✔
198
        }
22✔
199
      }
14!
200
    }
60✔
201
  }
132✔
202

203
  LOG_DEBUG("Store resolved, return " << log::ditype_str(type))
204
  return type;
152✔
205
}
1,098✔
206

207
template <typename Iter, typename Iter2>
208
std::optional<llvm::DIType*> reset_ditype(llvm::DIType* type_to_reset, const dataflow::ValuePath& path,
4,965✔
209
                                          const Iter& path_iter, const Iter2&) {
210
  std::optional<llvm::DIType*> type = type_to_reset;
4,965✔
211
  if (!type) {
4,965!
212
    LOG_DEBUG("No type to reset!")
213
    return {};
×
214
  }
215

216
  const auto& next_value = path_iter;
4,965✔
217
  LOG_DEBUG("Type to reset: " << log::ditype_str(*type));
218
  LOG_DEBUG(">> based on IR: " << **next_value);
219

220
  if (llvm::isa<llvm::GEPOperator>(*next_value)) {
4,965✔
221
    LOG_DEBUG("Reset based on GEP")
222
    auto* gep = llvm::cast<llvm::GEPOperator>(*next_value);
1,206✔
223
    // LOG_DEBUG("Path iter gep for extraction is currently " << *gep);
224
    // TODO: Maybe we could somehow get more info on the underlying type from the dataflow path
225
    //       if this returns an empty result due to forward decls?
226
    const auto gep_result = gep::extract_gep_dereferenced_type(type.value(), *gep);
1,206✔
227
    if (gep_result.member) {
1,206✔
228
      LOG_DEBUG("Using gep member type result")
229
      return gep_result.member;
764✔
230
    }
231
    return gep_result.type;
442✔
232
  }
1,206✔
233

234
  if (const auto* load = llvm::dyn_cast<llvm::LoadInst>(*next_value)) {
4,638✔
235
    // Re-set the DIType from the gep, if presence of:
236
    // - a load after a gep is likely the first element of the composite type
237
    // - a load also resolves to the basetype w.r.t. an array composite
238
    LOG_DEBUG("Reset based on load")
239
    return reset::reset_load_related_basic(path, type.value(), load);
879✔
240
  }
241

242
  if (const auto* store_inst = llvm::dyn_cast<llvm::StoreInst>(*next_value)) {
3,978✔
243
    // - a store with a ditype(array) is likely the first element of the array
244
    LOG_DEBUG("Reset based on store")
245
    return reset::reset_store_related_basic(path, type.value(), store_inst);
1,098✔
246
  }
247

248
  LOG_DEBUG(">> skipping");
249

250
  return type;
1,782✔
251
}
4,965✔
252

253
}  // namespace reset
254

255
std::optional<llvm::DIType*> find_type(const dataflow::CallValuePath& call_path) {
1,388✔
256
  auto type = root::find_type_root(call_path);
1,388✔
257

258
  if (!type) {
1,388✔
259
    LOG_DEBUG("find_type_root failed to find a type for path " << call_path.path)
260
    return {};
10✔
261
  }
262

263
  const auto path_end = call_path.path.path_to_value.rend();
1,378✔
264
  for (auto path_iter = call_path.path.path_to_value.rbegin(); path_iter != path_end; ++path_iter) {
6,343✔
265
    if (!type) {
4,965!
266
      break;
×
267
    }
268
    LOG_DEBUG("Extracted type w.r.t. gep: " << log::ditype_str(*type));
269
    type = reset::reset_ditype(type.value(), call_path.path, path_iter, path_end).value_or(type.value());
4,965✔
270
    LOG_DEBUG("reset_ditype result " << log::ditype_str(type.value_or(nullptr)) << "\n")
271
  }
4,965✔
272

273
  if (type) {
1,378!
274
    // If last node is a store inst, try to extract type via TBAA
275
    const auto* const start_node = llvm::dyn_cast_or_null<llvm::StoreInst>(*call_path.path.start_value());
1,378✔
276
    if (start_node) {
1,378✔
277
      auto type_tbaa = tbaa::resolve_tbaa(type.value(), *llvm::dyn_cast<llvm::Instruction>(start_node));
1,098✔
278
      if (type_tbaa) {
1,098✔
279
        type = type_tbaa.value();
1,078✔
280
      }
1,078✔
281
    }
1,098✔
282
  }
1,378✔
283

284
  return type;
1,378✔
285
}
1,388✔
286
}  // 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