• 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

82.32
/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) {
10,344✔
42
  return gep->getSourceElementType()->isIntegerTy(8);
10,344✔
43
}
44

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

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

62
  llvm::iterator_range<Iter> indices() const {
3,646✔
63
    return llvm::iterator_range<Iter>(indices_);
3,646✔
64
  }
1,144✔
65

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

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

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

78
  bool byte_access() const {
4,406✔
79
    return is_byte_access;
4,406✔
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) {
5,130✔
86
  GepIndices gep_ind;
5,130✔
87
  gep_ind.gep            = inst;
5,130✔
88
  gep_ind.skipped        = skip_first;
5,130✔
89
  gep_ind.is_byte_access = util::is_byte_indexing(gep_ind.gep);
5,130!
90

91
  bool use_zero{true};
5,130✔
92

93
#if LLVM_VERSION_MAJOR > 12
94
  for (const auto& index : inst->indices()) {
14,674!
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) {
9,544✔
100
      skip_first = false;
3,646✔
101
      continue;
3,646✔
102
    }
103
    if (auto* const_idx = llvm::dyn_cast<llvm::ConstantInt>(index.get())) {
11,098!
104
      const int64_t index_ = const_idx->getValue().getSExtValue();
5,200!
105
      gep_ind.indices_.emplace_back(index_);
5,200!
106
    }
5,200✔
107
  }
9,544!
108

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

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

119
  return gep_ind;
5,130✔
120
}
5,130!
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) {
1,138✔
141
  // used to detect the empty base class optimization
142
  auto* derived_type_member      = llvm::dyn_cast<llvm::DIDerivedType>(element);
1,138✔
143
  auto* next_derived_type_member = llvm::dyn_cast<llvm::DIDerivedType>(next_element);
1,138✔
144
  if (derived_type_member != nullptr && next_derived_type_member != nullptr) {
1,138!
145
    LOG_DEBUG("Non-null elements")
146
    if (derived_type_member->getOffsetInBits() == next_derived_type_member->getOffsetInBits()) {
1,138✔
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;
78✔
150
    }
151
  }
1,060✔
152
  return element;
1,060✔
153
}
1,138✔
154

155
template <typename UnlessFn>
156
auto find_non_derived_type_unless(llvm::DIType* root, UnlessFn&& unless) {
10,702✔
157
  llvm::DIType* type = root;
10,702✔
158
  while (type && llvm::isa<llvm::DIDerivedType>(type)) {
21,357!
159
    if (unless(type)) {
13,073!
160
      break;
2,418✔
161
    }
162
    auto* ditype = llvm::dyn_cast<llvm::DIDerivedType>(type);
10,655✔
163
    type         = ditype->getBaseType();
10,655✔
164
  }
10,655✔
165
  return type;
21,404✔
166
}
10,702✔
167

168
inline bool is_ebo_inherited_composite(llvm::DINode* dinode) {
31,390✔
169
  if (auto* derived = llvm::dyn_cast<llvm::DIDerivedType>(dinode)) {
62,694✔
170
    if (!di::util::is_inheritance(*derived)) {
31,304✔
171
      return false;
30,962✔
172
    }
173

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

186
  return false;
86✔
187
}
31,390✔
188

189
}  // namespace detail
190

191
auto find_non_derived_type_unless_ptr(llvm::DIType* root) {
4,362✔
192
  return detail::find_non_derived_type_unless(root, [](auto* val) { return di::util::is_pointer_like(*val); });
7,552✔
193
}
194

195
auto find_non_derived_type(llvm::DIType* root) {
6,204✔
196
  return detail::find_non_derived_type_unless(root, [](auto*) { return false; });
15,681✔
197
}
198

199
llvm::DICompositeType* skip_first_gep_access(llvm::DICompositeType* composite_type) {
1,704✔
200
  using namespace detail;
201
  const auto select_next_member = [&](llvm::DICompositeType* base) -> std::optional<llvm::DIType*> {
1,756✔
202
    auto composite_elements = base->getElements();
52✔
203
    auto* element           = composite_elements[0];
52✔
204
    if (composite_elements.size() > 1) {
52!
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));
52✔
210
  };
52✔
211

212
  const auto should_iterate_next_member = [&](auto* composite_type) {
3,460✔
213
    const auto count_members = llvm::count_if(composite_type->getElements(),
1,756✔
214
                                              [](const auto* elem) { return di::util::is_non_static_member(*elem); });
17,654✔
215
    return count_members == 1;
3,512✔
216
  };
1,756✔
217

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

228
  return composite_type;
3,408✔
229
}
1,704✔
230

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

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

236
  const auto is_static_member = [](const llvm::DINode* node) {
8,112✔
237
    if (const auto* derived_type_member = llvm::dyn_cast<llvm::DIDerivedType>(node)) {
8,932!
238
      return derived_type_member->isStaticMember();
4,466✔
239
    }
240
    return false;
×
241
  };
4,466✔
242

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

248
    auto* element = elements[gep_index];
4,310✔
249

250
    if (gep_index == 0 && elements.size() > 1) {
4,310✔
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];
1,138✔
254
      element            = detail::select_non_zero_element(element, next_element);
1,138✔
255
    }
1,138✔
256

257
    const auto ebo_inheritance_offset = llvm::count_if(
8,620✔
258
        composite_type->getElements(), [&](auto* dinode) { return detail::is_ebo_inherited_composite(dinode); });
35,700✔
259

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

266
    if (!llvm::isa<llvm::DIDerivedType>(element)) {
4,310!
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)) {
4,466!
275
      LOG_DEBUG("Skipping static member of composite " << log::ditype_str(element))
276
      element = elements[++gep_index];
156✔
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)) {
8,620!
282
      auto* member_type = find_non_derived_type_unless_ptr(derived_type_member->getBaseType());
4,310✔
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)) {
6,226✔
287
        if (di::util::is_struct_or_class(*composite_member_type)) {
1,916✔
288
          // maybe need to recurse into!
289
          if (has_next_gep_idx(enum_index.index())) {
1,412✔
290
            composite_type = composite_member_type;
664✔
291
            continue;
664✔
292
          }
293
        }
748✔
294
        if (di::util::is_array(*composite_member_type)) {
1,252✔
295
          if (has_next_gep_idx(enum_index.index())) {
504✔
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
        }
400✔
305
      }
1,148✔
306

307
      return GepIndexToType{member_type, derived_type_member};
3,542✔
308
    }
4,310✔
309
  }
4,310!
310
  return {};
×
311
}
2,086✔
312

313
GepIndexToType resolve_gep_index_to_type(llvm::DICompositeType* composite_type, const GepIndices& gep_indices) {
4,552✔
314
  if (gep_indices.empty()) {
4,552✔
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) {
146!
318
      // Test fortran 16_...f90, allocation "ALLOCATE(chunk%tiles(t)%field%density ...)":
319
      if (di::util::is_array(*composite_type)) {
146✔
320
        LOG_DEBUG("Assuming array-like access")
321
        return GepIndexToType{composite_type->getBaseType()};
176✔
322
      }
323
    }
58✔
324
    return GepIndexToType{composite_type};
116✔
325
  }
326

327
  if (gep_indices.byte_access()) {
4,406✔
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]);
760✔
330
    if (result) {
760✔
331
      return GepIndexToType{result->type_of_member, result->member};
700✔
332
    }
333
    return GepIndexToType{composite_type};
120✔
334
  }
760✔
335

336
  if (gep_indices.skipped_first() && gep_indices.indices_[0] != 0) {
3,646!
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,704✔
342
    if (new_composite_type != composite_type) {
1,704✔
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;
46✔
347
      LOG_DEBUG("Result of skip: " << log::ditype_str(composite_type))
348
    }
46✔
349
  }
1,704✔
350

351
  return iterate_gep_index(composite_type, gep_indices);
3,646✔
352
}
4,552✔
353

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

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

365
  if (!gep_ptr->getDebugLoc()) {
6,096✔
366
    return {};
20✔
367
  }
368
  const bool is_inlined = gep_ptr->getDebugLoc()->getInlinedAt() != nullptr;
6,076✔
369
  if (!is_inlined) {
6,076✔
370
    LOG_DEBUG("GEP not inlined")
371
    return {};
5,940✔
372
  }
373
  return gep_ptr->getDebugLoc();
136✔
374
}
6,256✔
375

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

379
  if (!debug_loc) {
6,256✔
380
    return {};
6,120✔
381
  }
382

383
  const auto* const sub_prog = llvm::dyn_cast<llvm::DISubprogram>(debug_loc->getScope());
136!
384
  assert(sub_prog && "Scope does not represent a subprogram");
272!
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) {
272✔
390
    auto node = detail::find_non_derived_type_unless(di_type, [](auto* node) {
542✔
391
      if (const auto* type = llvm::dyn_cast<llvm::DIDerivedType>(node)) {
812!
392
        return type->getTag() == llvm::dwarf::DW_TAG_pointer_type ||
406✔
393
               type->getTag() == llvm::dwarf::DW_TAG_ptr_to_member_type;
270✔
394
      }
395
      return false;
×
396
    });
406✔
397
    return node;
272✔
398
  };
136✔
399

400
  if (auto* sub_program_type = sub_prog->getType()) {
272!
401
    // Has return type (not void)?
402
    if (sub_program_type->getTypeArray().size() > 0 && (*sub_program_type->getTypeArray().begin() != nullptr)) {
136!
403
      auto* result = remove_ref(*sub_program_type->getTypeArray().begin());
136!
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}};
272✔
407
    }
136✔
408
  }
×
409

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

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

417
  auto* const gep_src = inst.getSourceElementType();
6,204✔
418

419
  auto* const base_ty        = find_non_derived_type(root);
6,204✔
420
  auto* const composite_type = llvm::dyn_cast_or_null<DICompositeType>(base_ty);
6,204✔
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();
6,204✔
423

424
  auto debug_loc = try_resolve_inlined_operator(&inst);
6,204✔
425
  if (debug_loc) {
6,204✔
426
    return debug_loc.value();
136✔
427
  }
428

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

434
  if (gep_src->isArrayTy()) {
5,520✔
435
    if (composite_type != nullptr) {
306!
436
      auto* base_type = composite_type->getBaseType();
306✔
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) {
306!
439
        // return GepIndexToType{base_type};
440
      }
306✔
441
    }
306✔
442
    LOG_DEBUG("Gep to array " << log::ditype_str(root));
443
    return GepIndexToType{root};
612✔
444
  }
445

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

452
  const bool byte_indexing = util::is_byte_indexing(&inst);
5,214✔
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)) {
5,214✔
457
    LOG_DEBUG("Gep with byte offset to pointer-like : " << log::ditype_str(root))
458
    return GepIndexToType{root};
64✔
459
  }
460

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

463
  if (composite_type->isForwardDecl()) {
5,182✔
464
    LOG_DEBUG("Trying to resolve forward-declared composite type " << log::ditype_str(composite_type))
465
    if (auto resolved = try_resolve_inlined_operator(&inst)) {
52!
NEW
466
      return resolved.value();
×
467
    }
468
    return GepIndexToType{root};
104✔
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);
5,130✔
475
  const bool single_index       = inst.getNumIndices() == 1;
5,130✔
476
  const bool fortran_descriptor = fortran::is_fortran_descriptor(inst.getSourceElementType());
5,130✔
477

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

488
  auto gep_indices = GepIndices::create(&inst, skip_first);
5,130✔
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) {
5,130!
492
    LOG_DEBUG("Return base type, array-like access " << log::ditype_str(derived_root->getBaseType()))
493
    return GepIndexToType{derived_root->getBaseType()};
578!
494
  }
495

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

498
  return accessed_ditype;
4,552✔
499
}
6,204✔
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