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

ahueck / llvm-dimeta / 23296997048

19 Mar 2026 01:23PM UTC coverage: 64.594% (-9.0%) from 73.626%
23296997048

Pull #49

github

web-flow
Merge f45516abb into cefb3414f
Pull Request #49: Initial Fortran Support

1854 of 3670 branches covered (50.52%)

Branch coverage included in aggregate %.

164 of 435 new or added lines in 15 files covered. (37.7%)

20 existing lines in 6 files now uncovered.

2333 of 2812 relevant lines covered (82.97%)

11668.39 hits per line

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

81.75
/lib/type/GEP.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 "GEP.h"
9

10
#include "DIFortranTypeExtractor.h"
11
#include "DIUtil.h"
12
#include "support/Logger.h"
13

14
#include "llvm/ADT/APInt.h"
15
#include "llvm/ADT/STLExtras.h"
16
#include "llvm/ADT/SmallVector.h"
17
#include "llvm/ADT/iterator_range.h"
18
#include "llvm/BinaryFormat/Dwarf.h"
19
#include "llvm/IR/Constants.h"
20
#include "llvm/IR/DebugInfoMetadata.h"
21
#include "llvm/IR/Metadata.h"
22
#include "llvm/IR/Operator.h"
23
#include "llvm/IR/Type.h"
382✔
24
#include "llvm/IR/Use.h"
25
#include "llvm/Support/Casting.h"
26
#include "llvm/Support/Debug.h"
27
#include "llvm/Support/raw_ostream.h"
28

29
#include <algorithm>
30
#include <cassert>
31
#include <cstddef>
32
#include <cstdint>
33
#include <iterator>
34
#include <llvm/IR/Instruction.h>
35
#include <llvm/IR/Instructions.h>
36
#include <optional>
37

38
namespace dimeta::gep {
39

40
namespace util {
41
inline bool is_byte_indexing(const llvm::GEPOperator* gep) {
8,032✔
42
  return gep->getSourceElementType()->isIntegerTy(8);
8,032✔
43
}
44

45
inline bool is_first_non_zero_indexing(const llvm::GEPOperator* gep) {
3,982✔
46
  if (auto* const_idx = llvm::dyn_cast<llvm::ConstantInt>((*gep->idx_begin()).get())) {
7,054✔
47
    const int64_t index = const_idx->getValue().getSExtValue();
3,072✔
48
    return index > 0;
3,072✔
49
  }
3,072✔
50
  return false;
910✔
51
}
3,982✔
52
}  // namespace util
53

54
struct GepIndices {
3,982✔
55
  const llvm::GEPOperator* gep;
56
  llvm::SmallVector<uint64_t, 4> indices_;
57
  bool skipped{false};
3,982✔
58
  bool is_byte_access{false};
3,982✔
59
  bool dynamic_access{false};
3,982✔
60
  using Iter = llvm::SmallVector<uint64_t, 4>::const_iterator;
61

62
  llvm::iterator_range<Iter> indices() const {
2,970✔
63
    return llvm::iterator_range<Iter>(indices_);
2,970✔
64
  }
468✔
65

66
  size_t size() const {
12,960✔
67
    return indices_.size();
12,960✔
68
  }
69

70
  bool empty() const {
7,478✔
71
    return size() == 0;
7,478✔
72
  }
73

74
  bool skipped_first() const {
2,970✔
75
    return skipped;
2,970✔
76
  }
77

78
  bool byte_access() const {
3,474✔
79
    return is_byte_access;
3,474✔
80
  }
81

82
  static GepIndices create(const llvm::GEPOperator* inst, bool skip_first = true);
83
};
84

85
GepIndices GepIndices::create(const llvm::GEPOperator* inst, bool skip_first) {
3,982✔
86
  GepIndices gep_ind;
3,982✔
87
  gep_ind.gep            = inst;
3,982✔
88
  gep_ind.skipped        = skip_first;
3,982✔
89
  gep_ind.is_byte_access = util::is_byte_indexing(gep_ind.gep);
3,982!
90

91
  bool use_zero{true};
3,982✔
92

93
#if LLVM_VERSION_MAJOR > 12
94
  for (const auto& index : inst->indices()) {
11,630!
95
#else
96
  for (const auto& index : llvm::make_range(inst->idx_begin(), inst->idx_end())) {
97
#endif
98
    LOG_DEBUG("Iter " << skip_first << " with " << *index.get())
99
    if (skip_first) {
7,648✔
100
      skip_first = false;
2,970✔
101
      continue;
2,970✔
102
    }
103
    if (auto* const_idx = llvm::dyn_cast<llvm::ConstantInt>(index.get())) {
8,870!
104
      const int64_t index_ = const_idx->getValue().getSExtValue();
4,192!
105
      gep_ind.indices_.emplace_back(index_);
4,192!
106
    }
4,192✔
107
  }
7,648!
108

109
  if (!inst->indices().empty() && gep_ind.empty()) {
7,964!
110
    // based on heap_milc_mrecv_mock.c with optim:
111
    // gep_ind.indices_.push_back(0);
112
  }
486✔
113

114
  if (gep_ind.size() == 0 && !inst->indices().empty()) {
3,982!
115
    LOG_DEBUG("Found dynamic GEP access index")
116
    gep_ind.dynamic_access = true;
486✔
117
  }
486✔
118

119
  return gep_ind;
3,982✔
120
}
3,982!
121

122
inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const GepIndices& indices) {
123
  const auto& vec = indices.indices_;
124
  if (vec.empty()) {
125
    os << "[]";
126
    return os;
127
  }
128
  const auto* begin = std::begin(vec);
129
  os << "[" << *begin;
130
  std::for_each(std::next(begin), std::end(vec), [&](const auto value) {
131
    os << ", ";
132
    os << value;
133
  });
134
  os << "]";
135
  return os;
136
}
137

138
namespace detail {
139

140
llvm::DINode* select_non_zero_element(llvm::DINode* element, llvm::DINode* next_element) {
902✔
141
  // used to detect the empty base class optimization
142
  auto* derived_type_member      = llvm::dyn_cast<llvm::DIDerivedType>(element);
902✔
143
  auto* next_derived_type_member = llvm::dyn_cast<llvm::DIDerivedType>(next_element);
902✔
144
  if (derived_type_member != nullptr && next_derived_type_member != nullptr) {
902!
145
    LOG_DEBUG("Non-null elements")
146
    if (derived_type_member->getOffsetInBits() == next_derived_type_member->getOffsetInBits()) {
902✔
147
      LOG_DEBUG("Same offset detected: " << log::ditype_str(derived_type_member) << " and "
148
                                         << log::ditype_str(next_derived_type_member))
149
      return next_element;
66✔
150
    }
151
  }
836✔
152
  return element;
836✔
153
}
902✔
154

155
template <typename UnlessFn>
156
auto find_non_derived_type_unless(llvm::DIType* root, UnlessFn&& unless) {
8,622✔
157
  llvm::DIType* type = root;
8,622✔
158
  while (type && llvm::isa<llvm::DIDerivedType>(type)) {
17,519!
159
    if (unless(type)) {
10,987!
160
      break;
2,090✔
161
    }
162
    auto* ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
8,897✔
163
    type         = ditype->getBaseType();
8,897✔
164
  }
8,897✔
165
  return type;
17,244✔
166
}
8,622✔
167

168
inline bool is_ebo_inherited_composite(llvm::DINode* dinode) {
25,642✔
169
  if (auto* derived = llvm::dyn_cast<llvm::DIDerivedType>(dinode)) {
51,210✔
170
    if (!di::util::is_inheritance(*derived)) {
25,568✔
171
      return false;
25,266✔
172
    }
173

174
    LOG_DEBUG(log::ditype_str(derived))
175
    auto* base = llvm::dyn_cast<llvm::DICompositeType>(derived->getBaseType());
302✔
176
    if (!base) {
302!
177
      LOG_DEBUG("Is not a composite inheritance " << log::ditype_str(derived))
178
      return false;
×
179
    }
180
    const bool has_sized_member =
604✔
181
        llvm::any_of(base->getElements(), [](llvm::DINode* elem) { return di::util::is_non_static_member(*elem); });
858✔
182
    LOG_DEBUG("Has sized mem " << has_sized_member)
183
    return !has_sized_member;
302✔
184
  }
302✔
185

186
  return false;
74✔
187
}
25,642✔
188

189
}  // namespace detail
190

191
auto find_non_derived_type_unless_ptr(llvm::DIType* root) {
3,610✔
192
  return detail::find_non_derived_type_unless(root, [](auto* val) { return di::util::is_pointer_like(*val); });
6,356✔
193
}
194

195
auto find_non_derived_type(llvm::DIType* root) {
4,896✔
196
  return detail::find_non_derived_type_unless(root, [](auto*) { return false; });
12,791✔
197
}
198

199
llvm::DICompositeType* skip_first_gep_access(llvm::DICompositeType* composite_type) {
1,412✔
200
  using namespace detail;
201
  const auto select_next_member = [&](llvm::DICompositeType* base) -> std::optional<llvm::DIType*> {
1,460✔
202
    auto composite_elements = base->getElements();
48✔
203
    auto* element           = composite_elements[0];
48✔
204
    if (composite_elements.size() > 1) {
48!
205
      auto* next_element = composite_elements[1];
×
206
      element            = detail::select_non_zero_element(element, next_element);
×
207
    }
×
208

209
    return find_non_derived_type_unless_ptr(llvm::dyn_cast<llvm::DIType>(element));
48✔
210
  };
48✔
211

212
  const auto should_iterate_next_member = [&](auto* composite_type) {
2,872✔
213
    const auto count_members = llvm::count_if(composite_type->getElements(),
1,460✔
214
                                              [](const auto* elem) { return di::util::is_non_static_member(*elem); });
14,338✔
215
    return count_members == 1;
2,920✔
216
  };
1,460✔
217

218
  while (should_iterate_next_member(composite_type)) {
1,460✔
219
    auto next_di = select_next_member(composite_type);
48✔
220
    if (!next_di || !llvm::isa<llvm::DICompositeType>(next_di.value())) {
48!
221
      LOG_DEBUG("Did not find next member")
222
      break;
×
223
    }
224
    composite_type = llvm::dyn_cast<llvm::DICompositeType>(next_di.value());
48✔
225
    LOG_DEBUG("Found next " << log::ditype_str(composite_type))
226
  }
48!
227

228
  return composite_type;
2,824✔
229
}
1,412✔
230

231
GepIndexToType iterate_gep_index(llvm::DICompositeType* composite_type, const GepIndices& gep_indices) {
2,970✔
232
  const auto has_next_gep_idx = [&gep_indices](size_t pos) { return pos + 1 < gep_indices.size(); };
4,470✔
233

234
  LOG_DEBUG("Iterate over gep: " << gep_indices);
235

236
  const auto is_static_member = [](const llvm::DINode* node) {
6,664✔
237
    if (const auto* derived_type_member = llvm::dyn_cast<llvm::DIDerivedType>(node)) {
7,388!
238
      return derived_type_member->isStaticMember();
3,694✔
239
    }
240
    return false;
×
241
  };
3,694✔
242

243
  for (const auto& enum_index : llvm::enumerate(gep_indices.indices())) {
6,532!
244
    auto gep_index       = enum_index.value();
3,562✔
245
    const auto& elements = composite_type->getElements();
3,562✔
246
    assert(elements.size() > gep_index);
3,562!
247

248
    auto* element = elements[gep_index];
3,562✔
249

250
    if (gep_index == 0 && elements.size() > 1) {
3,562✔
251
      // e.g., LLVM-14: cpp/heap_lhs_function_opt.cpp: vector gep is [0 0 0 0 ...] -> never recurse into EBO
252
      LOG_DEBUG("Check zero-size pattern for " << log::ditype_str(composite_type))
253
      auto* next_element = elements[1];
902✔
254
      element            = detail::select_non_zero_element(element, next_element);
902✔
255
    }
902✔
256

257
    const auto ebo_inheritance_offset = llvm::count_if(
7,124✔
258
        composite_type->getElements(), [&](auto* dinode) { return detail::is_ebo_inherited_composite(dinode); });
29,204✔
259

260
    if (gep_index > 0 && ebo_inheritance_offset > 0 && (gep_index) < elements.size()) {
3,562!
261
      LOG_DEBUG("EBO offset needed " << ebo_inheritance_offset)
262
      gep_index += ebo_inheritance_offset;
76✔
263
      element = elements[gep_index];
76✔
264
    }
76✔
265

266
    if (!llvm::isa<llvm::DIDerivedType>(element)) {
3,562!
267
      LOG_DEBUG("Index shows to non-derived type: " << log::ditype_str(element))
NEW
268
      if (di::util::is_array(*element)) {
×
269
        LOG_DEBUG("Indexing into array, returning base type")
NEW
270
        return GepIndexToType{composite_type->getBaseType()};
×
271
      }
UNCOV
272
    }
×
273

274
    while (gep_index < elements.size() && is_static_member(element)) {
3,694!
275
      LOG_DEBUG("Skipping static member of composite " << log::ditype_str(element))
276
      element = elements[++gep_index];
132✔
277
    }
278

279
    LOG_DEBUG(" element[" << gep_index << "]: " << log::ditype_str(element))
280

281
    if (auto* derived_type_member = llvm::dyn_cast<llvm::DIDerivedType>(element)) {
7,124!
282
      auto* member_type = find_non_derived_type_unless_ptr(derived_type_member->getBaseType());
3,562✔
283

284
      LOG_DEBUG("Looking at " << log::ditype_str(member_type))
285

286
      if (auto* composite_member_type = llvm::dyn_cast<llvm::DICompositeType>(member_type)) {
5,062✔
287
        if (di::util::is_struct_or_class(*composite_member_type)) {
1,500✔
288
          // maybe need to recurse into!
289
          if (has_next_gep_idx(enum_index.index())) {
1,172✔
290
            composite_type = composite_member_type;
592✔
291
            continue;
592✔
292
          }
293
        }
580✔
294
        if (di::util::is_array(*composite_member_type)) {
908✔
295
          if (has_next_gep_idx(enum_index.index())) {
328✔
296
            LOG_DEBUG("Found array that is indexed with next index")
297
            // At end of gep instruction, return basetype:
298
            return GepIndexToType{
312✔
299
                composite_member_type->getBaseType(),
104✔
300
                derived_type_member,
104✔
301
            };
302
          }
303
          // maybe need to recurse into tag_array_type (of non-basic type...)
304
        }
224✔
305
      }
804✔
306

307
      return GepIndexToType{member_type, derived_type_member};
2,866✔
308
    }
3,562✔
309
  }
3,562!
310
  return {};
×
311
}
1,410✔
312

313
GepIndexToType resolve_gep_index_to_type(llvm::DICompositeType* composite_type, const GepIndices& gep_indices) {
3,496✔
314
  if (gep_indices.empty()) {
3,496✔
315
    // this triggers for composite (-array) access without constant index, see "heap_milc_struct_mock.c":
316
    LOG_DEBUG("Gep indices empty")
317
    if (gep_indices.dynamic_access) {
22!
318
      // Test fortran 16_...f90, allocation "ALLOCATE(chunk%tiles(t)%field%density ...)":
319
      if (di::util::is_array(*composite_type)) {
22!
320
        LOG_DEBUG("Assuming array-like access")
NEW
321
        return GepIndexToType{composite_type->getBaseType()};
×
322
      }
323
    }
22✔
324
    return GepIndexToType{composite_type};
44✔
325
  }
326

327
  if (gep_indices.byte_access()) {
3,474✔
328
    LOG_DEBUG("Trying to resolve byte access based on offset " << gep_indices.indices_[0])
329
    auto result = di::util::resolve_byte_offset_to_member_of(composite_type, gep_indices.indices_[0]);
504✔
330
    if (result) {
504✔
331
      return GepIndexToType{result->type_of_member, result->member};
456✔
332
    }
333
    return GepIndexToType{composite_type};
96✔
334
  }
504✔
335

336
  if (gep_indices.skipped_first() && gep_indices.indices_[0] != 0) {
2,970!
337
    // This assumes that a single (and only single) first 0 skips through to the first element with more than one
338
    // member: struct A { struct B { struct C { int, int } } } -> would skip to "struct C" for gep [0 1]
339
    // see test gep/global_nested.c
340
    LOG_DEBUG("Skip single member nested of: " << log::ditype_str(composite_type))
341
    auto* new_composite_type = skip_first_gep_access(composite_type);
1,412✔
342
    if (new_composite_type != composite_type) {
1,412✔
343
      // required for
344
      // - LLVM-18: gep/global_nested & gep/param_first_nested_padding.cpp
345
      // - LLVM-19: gep/global_nested.c
346
      composite_type = new_composite_type;
42✔
347
      LOG_DEBUG("Result of skip: " << log::ditype_str(composite_type))
348
    }
42✔
349
  }
1,412✔
350

351
  return iterate_gep_index(composite_type, gep_indices);
2,970✔
352
}
3,496✔
353

354
std::optional<llvm::DebugLoc> try_resolve_inlined_debug_loc(const llvm::GEPOperator* gep) {
4,940✔
355
  auto gep_ptr = llvm::dyn_cast<llvm::Instruction>(gep);
4,940✔
356

357
  if (!gep_ptr) {
4,940✔
358
    gep_ptr = llvm::dyn_cast<llvm::Instruction>(gep->getPointerOperand());
124✔
359
    if (!gep_ptr) {
124!
360
      LOG_DEBUG("No load for GEP found")
361
      return {};
124✔
362
    }
363
  }
×
364

365
  if (!gep_ptr->getDebugLoc()) {
4,816✔
366
    return {};
20✔
367
  }
368
  const bool is_inlined = gep_ptr->getDebugLoc()->getInlinedAt() != nullptr;
4,796✔
369
  if (!is_inlined) {
4,796✔
370
    LOG_DEBUG("GEP not inlined")
371
    return {};
4,680✔
372
  }
373
  return gep_ptr->getDebugLoc();
116✔
374
}
4,940✔
375

376
std::optional<GepIndexToType> try_resolve_inlined_operator(const llvm::GEPOperator* gep) {
4,940✔
377
  auto debug_loc = try_resolve_inlined_debug_loc(gep);
4,940✔
378

379
  if (!debug_loc) {
4,940✔
380
    return {};
4,824✔
381
  }
382

383
  const auto* const sub_prog = llvm::dyn_cast<llvm::DISubprogram>(debug_loc->getScope());
116!
384
  assert(sub_prog && "Scope does not represent a subprogram");
232!
385

386
  LOG_DEBUG("Looking at " << log::ditype_str(sub_prog))
387

388
  // see cpp/heap_vector_operator.cpp: vector::operator[] returns a reference, that we skip here:
389
  const auto remove_ref = [&](auto* di_type) {
232✔
390
    auto node = detail::find_non_derived_type_unless(di_type, [](auto* node) {
462✔
391
      if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(node)) {
692!
392
        return type->getTag() == llvm::dwarf::DW_TAG_pointer_type ||
346✔
393
               type->getTag() == llvm::dwarf::DW_TAG_ptr_to_member_type;
230✔
394
      }
395
      return false;
×
396
    });
346✔
397
    return node;
232✔
398
  };
116✔
399

400
  if (auto* sub_program_type = sub_prog->getType()) {
232!
401
    // Has return type (not void)?
402
    if (sub_program_type->getTypeArray().size() > 0 && (*sub_program_type->getTypeArray().begin() != nullptr)) {
116!
403
      auto* result = remove_ref(*sub_program_type->getTypeArray().begin());
116!
404
      LOG_DEBUG("Found candidate " << log::ditype_str(*sub_program_type->getTypeArray().begin()) << " with final type "
405
                                   << log::ditype_str(result))
406
      return {GepIndexToType{result}};
232✔
407
    }
116✔
408
  }
×
409

410
  LOG_DEBUG("Could not detect inlined operator")
411
  return {};
×
412
}
4,940✔
413

414
GepIndexToType extract_gep_dereferenced_type(llvm::DIType* root, const llvm::GEPOperator& inst) {
4,896✔
415
  using namespace llvm;
416

417
  auto* const gep_src = inst.getSourceElementType();
4,896✔
418

419
  auto* const base_ty        = find_non_derived_type(root);
4,896✔
420
  auto* const composite_type = llvm::dyn_cast_or_null<DICompositeType>(base_ty);
4,896✔
421
  // see test cpp/heap_vector_opt.cpp: GEP on pointer (of inlined operator[])
422
  const bool may_be_inlined_operator = (composite_type != nullptr) && composite_type->isForwardDecl();
4,896✔
423

424
  auto debug_loc = try_resolve_inlined_operator(&inst);
4,896✔
425
  if (debug_loc) {
4,896✔
426
    return debug_loc.value();
116✔
427
  }
428

429
  if (gep_src->isPointerTy() && !may_be_inlined_operator) {
4,780!
430
    LOG_DEBUG("Gep to ptr " << log::ditype_str(root));
431
    return GepIndexToType{root};
936✔
432
  }
433

434
  if (gep_src->isArrayTy()) {
4,312✔
435
    if (composite_type != nullptr) {
262!
436
      auto* base_type = composite_type->getBaseType();
262✔
437
      LOG_DEBUG("Gep to array of DI composite, with base type " << log::ditype_str(base_type));
438
      if (composite_type->getTag() == llvm::dwarf::DW_TAG_array_type) {
262!
439
        // return GepIndexToType{base_type};
440
      }
262✔
441
    }
262✔
442
    LOG_DEBUG("Gep to array " << log::ditype_str(root));
443
    return GepIndexToType{root};
524✔
444
  }
445

446
  const auto* derived_root = llvm::dyn_cast<DIDerivedType>(root);
4,050✔
447
  const bool is_derived_root_pointer_type =
7,898✔
448
      (derived_root != nullptr) && derived_root->getTag() == dwarf::DW_TAG_pointer_type;
4,050✔
449
  const bool is_derived_root_base_pointer =
7,898✔
450
      (derived_root != nullptr) && derived_root->getBaseType()->getTag() == dwarf::DW_TAG_pointer_type;
4,050✔
451

452
  const bool byte_indexing = util::is_byte_indexing(&inst);
4,050✔
453

454
  // Handle byte-indexed GEPs that might not target a composite type directly.
455
  // This early exit prevents further composite type assertions if not applicable.
456
  if (byte_indexing && (!composite_type || is_derived_root_base_pointer)) {
4,050✔
457
    LOG_DEBUG("Gep with byte offset to pointer-like : " << log::ditype_str(root))
458
    return GepIndexToType{root};
48✔
459
  }
460

461
  assert(composite_type != nullptr && "Root should be a struct-like type.");
8,052!
462

463
  if (composite_type->isForwardDecl()) {
4,026✔
464
    LOG_DEBUG("Trying to resolve forward-declared composite type " << log::ditype_str(composite_type))
465
    if (auto resolved = try_resolve_inlined_operator(&inst)) {
44!
NEW
466
      return resolved.value();
×
467
    }
468
    return GepIndexToType{root};
88✔
469
  }
470

471
  LOG_DEBUG("Gep to DI composite: " << log::ditype_str(composite_type))
472

473
  // Should skip GEP index? Default: skip the first index if it's a zero-index
474
  bool skip_first               = !util::is_first_non_zero_indexing(&inst);
3,982✔
475
  const bool single_index       = inst.getNumIndices() == 1;
3,982✔
476
  const bool fortran_descriptor = fortran::is_fortran_descriptor(inst.getSourceElementType());
3,982✔
477

478
  if (single_index && !fortran_descriptor) {
3,982!
479
    LOG_DEBUG("Single index GEP on non-descriptor, assuming array indexing (no skip)")
480
    skip_first = false;
1,012✔
481
  }
1,012✔
482
  //  If it's a byte-indexed GEP, the first index is a meaningful offset:
483
  if (byte_indexing) {
3,982✔
484
    LOG_DEBUG("Access based on i8 ptr, assuming byte offsetting into composite member")
485
    skip_first = false;
526✔
486
  }
526✔
487

488
  auto gep_indices = GepIndices::create(&inst, skip_first);
3,982✔
489

490
  // This may be a direct array-like access to the base type of a pointer.
491
  if (!byte_indexing && !skip_first && single_index && is_derived_root_pointer_type) {
3,982!
492
    LOG_DEBUG("Return base type, array-like access " << log::ditype_str(derived_root->getBaseType()))
493
    return GepIndexToType{derived_root->getBaseType()};
486!
494
  }
495

496
  auto accessed_ditype = resolve_gep_index_to_type(composite_type, gep_indices);
3,496!
497

498
  return accessed_ditype;
3,496✔
499
}
4,896✔
500

501
}  // namespace dimeta::gep
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