• 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

77.22
/lib/type/DIFortranTypeExtractor.cpp
1
#include "DIFinder.h"
2
#include "DIPath.h"
3
#include "DIRootType.h"
4
#include "DIUtil.h"
5
#include "DataflowAnalysis.h"
6
#include "GEP.h"
7
#include "TBAA.h"
8
#include "ValuePath.h"
9
#include "support/Logger.h"
10

11
#include "llvm/IR/DebugInfoMetadata.h"
12
#include "llvm/IR/Function.h"
13
#include "llvm/IR/GlobalVariable.h"
14
#include "llvm/IR/InstrTypes.h"
15
#include "llvm/IR/Instructions.h"
16
#include "llvm/IR/IntrinsicInst.h"
17
#include "llvm/IR/Metadata.h"
18
#include "llvm/IR/Operator.h"
19
#include "llvm/IR/Value.h"
20
#include "llvm/Support/Casting.h"
21
#include "llvm/Support/Debug.h"
22
#include "llvm/Support/raw_ostream.h"
23

24
#include <cassert>
25
#include <llvm/IR/DerivedTypes.h>
26
#include <optional>
27

28
namespace dimeta::fortran {
29

30
struct TypeDescritor {
31
  // { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }
32
};
33

34
bool is_fortran_descriptor(llvm::Type* type) {
12,790✔
35
  LOG_DEBUG("Analyzing " << *type)
36
  // Detects if the given type is a Fortran descriptor structure.
37
  // Expected pattern:
38
  // - Array: { ptr, i64, i32, i8, i8, i8, i8, [n x [3 x i64]], ptr, [n x i64] } (10 elements)
39
  // - Scalar: { ptr, i64, i32, i8, i8, i8, i8, ptr, [n x i64] } (9 elements)
40
  auto* struct_type = llvm::dyn_cast_or_null<llvm::StructType>(type);
12,790✔
41
  if (!struct_type) {
12,790✔
42
    return false;
3,200✔
43
  }
44
  const auto num_elements = struct_type->getNumElements();
9,590✔
45
  if (num_elements != 10 && num_elements != 9) {
9,590✔
46
    return false;
7,714✔
47
  }
48
  const bool is_scalar = (num_elements == 9);
1,876✔
49
  // 0: ptr (base_addr)
50
  if (!struct_type->getElementType(0)->isPointerTy()) {
1,876✔
51
    return false;
1,724✔
52
  }
53
  // 1: i64 (elem_len)
54
  if (!struct_type->getElementType(1)->isIntegerTy(64)) {
152!
NEW
55
    return false;
×
56
  }
57
  // 2: i32 (version)
58
  if (!struct_type->getElementType(2)->isIntegerTy(32)) {
152!
NEW
59
    return false;
×
60
  }
61
  // 3-6: i8 (rank, type, attribute, f18Addendum)
62
  for (unsigned i = 3; i <= 6; ++i) {
760!
63
    if (!struct_type->getElementType(i)->isIntegerTy(8)) {
608!
NEW
64
      return false;
×
65
    }
66
  }
608✔
67
  unsigned current_idx     = 7;
152✔
68
  llvm::ArrayType* dim_arr = nullptr;
152✔
69
  if (!is_scalar) {
152✔
70
    // 7: [n x [3 x i64]] (dimensions)
71
    dim_arr = llvm::dyn_cast<llvm::ArrayType>(struct_type->getElementType(current_idx++));
120✔
72
    if (!dim_arr) {
120!
NEW
73
      return false;
×
74
    }
75
    auto* inner_dim_arr = llvm::dyn_cast<llvm::ArrayType>(dim_arr->getElementType());
120✔
76
    if (!inner_dim_arr || inner_dim_arr->getNumElements() != 3 || !inner_dim_arr->getElementType()->isIntegerTy(64)) {
120!
NEW
77
      return false;
×
78
    }
79
  }
120!
80
  // 8 (or 7): ptr (type descriptor pointer)
81
  if (!struct_type->getElementType(current_idx++)->isPointerTy()) {
152!
NEW
82
    return false;
×
83
  }
84
  // 9 (or 8): [n x i64] (addendum)
85
  auto* addendum_arr = llvm::dyn_cast<llvm::ArrayType>(struct_type->getElementType(current_idx++));
152✔
86
  if (!addendum_arr || !addendum_arr->getElementType()->isIntegerTy(64)) {
152!
NEW
87
    return false;
×
88
  }
89
  if (dim_arr && dim_arr->getNumElements() != addendum_arr->getNumElements()) {
152!
NEW
90
    return false;
×
91
  }
92
  return true;
152✔
93
}
12,790✔
94

95
template <typename Iter>
96
std::optional<llvm::DIType*> reset_ditype(llvm::DIType* type_to_reset, const dataflow::ValuePath& path,
164✔
97
                                          const Iter& path_iter, type::dipath::ValueToDiPath& logged_dipath) {
98
  std::optional<llvm::DIType*> type = type_to_reset;
164✔
99

100
  const auto& current_value = path_iter;
164✔
101
  LOG_DEBUG("Type to reset: " << log::ditype_str(*type));
102
  LOG_DEBUG(">> based on IR: " << **current_value);
103

104
  if (llvm::isa<llvm::GEPOperator>(*current_value)) {
164✔
105
    LOG_DEBUG("Reset based on GEP")
106
    auto* gep                     = llvm::cast<llvm::GEPOperator>(*current_value);
32✔
107
    const bool fortran_descriptor = fortran::is_fortran_descriptor(gep->getSourceElementType());
32✔
108
    if (!fortran_descriptor) {
32✔
109
      const auto gep_result = gep::extract_gep_dereferenced_type(type.value(), *gep);
24✔
110
      if (gep_result.member && !gep_result.use_type) {
24!
111
        LOG_DEBUG("Using gep member type result")
112
        type = gep_result.member;
24✔
113
      } else {
24✔
NEW
114
        type = gep_result.type;
×
115
      }
116
    }
24✔
117
  } else if (const auto* load = llvm::dyn_cast<llvm::LoadInst>(*current_value)) {
296✔
118
    LOG_DEBUG("Reset based on load " << *load)
119
    // TODO
120
  } else if (const auto* store_inst = llvm::dyn_cast<llvm::StoreInst>(*current_value)) {
252!
121
    LOG_DEBUG("Reset based on store " << *store_inst)
122

NEW
123
  } else {
×
124
    LOG_DEBUG(">> skipping: " << **current_value);
125
  }
126

127
  logged_dipath.emplace_back(*current_value, type.value_or(nullptr));
164!
128

129
  return type;
130
}
164✔
131

132
std::optional<llvm::DIType*> extract(const dataflow::CallValuePath& call_path, std::optional<llvm::DIType*> type) {
120✔
133
  assert(call_path.call.has_value() && "Expected a Fortran call handle");
240!
134
  // Fortran workaround:
135
  // If _FortranAAllocatableAllocate called on a global directly, assume first member is actually allocated, see
136
  // test 08_bounds_nogep.f90 vs. 08_bounds.f90:
137
  // @_QMtea_moduleEchunk = global %_QMtea_moduleTchunktype
138
  // call i32 @_FortranAAllocatableAllocate(ptr @_QMtea_moduleEchunk, ...), !dbg !30
139

140
  LOG_DEBUG("Fortran workaround for _FortranAAllocatableAllocate")
141

142
  type::dipath::ValueToDiPath dipath;
120✔
143

144
  const auto path_end = call_path.path.path_to_value.rend();
120!
145
  for (auto path_iter = call_path.path.path_to_value.rbegin(); path_iter != path_end; ++path_iter) {
284!
146
    LOG_DEBUG("Extracted type: " << log::ditype_str(*type));
147
    type = reset_ditype(type.value(), call_path.path, path_iter, dipath).value_or(type.value());
164!
148
    LOG_DEBUG("reset_ditype result " << log::ditype_str(type.value_or(nullptr)) << "\n")
149
    if (!type) {
164!
NEW
150
      break;
×
151
    }
152
  }
164✔
153

154
  auto* ditype_final = type.value();
120!
155

156
  const bool only_global = [&]() -> bool {
240!
157
    bool is_global_target = llvm::isa<llvm::GlobalVariable>(*call_path.path.value()) && call_path.path.size() == 1;
120✔
158

159
    // 09_local_bounds.f90 optimized: (alloca -> memcpy of global) [->] allocatable:
160
    for (const auto* user : call_path.path.value().value()->users()) {
780✔
161
      if (llvm::MemCpyInst::classof(user)) {
660✔
162
        is_global_target = llvm::isa<llvm::GlobalVariable>(llvm::cast<llvm::MemCpyInst>(user)->getSource());
112✔
163
      }
112✔
164
    }
660✔
165
    return is_global_target;
240✔
166
  }();
120✔
167

168
  if (only_global && llvm::isa<llvm::DICompositeType>(ditype_final)) {
120!
169
    LOG_DEBUG("Reset fortran allocated type " << log::ditype_str(ditype_final))
170

171
    auto struct_mem = di::util::resolve_byte_offset_to_member_of(llvm::cast<llvm::DICompositeType>(ditype_final), 0);
12!
172
    if (struct_mem) {
12!
173
      dipath.emplace_back(nullptr, struct_mem->type_of_member.value_or(ditype_final), "Allocatable of global type");
12!
174
    }
12✔
175
  }
12✔
176

177
  const auto global_inheritance = [&]() -> std::optional<llvm::GlobalVariable*> {
240✔
178
    const auto* global_target = llvm::dyn_cast<llvm::GlobalVariable>(*call_path.path.value());
120✔
179
    if (!global_target) {
120✔
180
      return {};
56✔
181
    }
182
    for (const auto* user : global_target->users()) {
256✔
183
      if (auto* call = llvm::dyn_cast<llvm::CallBase>(user)) {
336✔
184
        auto target    = call->getCalledFunction();
144✔
185
        auto is_target = target ? target->getName() == ("_FortranAAllocatableInitDerivedForAllocate") : false;
144!
186
        if (is_target) {
144✔
187
          auto global = llvm::dyn_cast<llvm::GlobalVariable>(call->getOperand(1));
24✔
188
          if (global) {
24!
189
            return {global};
24✔
190
          }
NEW
191
          break;
×
192
        }
24✔
193
      }
144✔
194
    }
192✔
195
    return {};
40✔
196
  };
120✔
197
  auto type_inheritance = global_inheritance();
120!
198
  if (type_inheritance) {
120✔
199
    LOG_DEBUG("Found inheritance, type descriptor: " << **type_inheritance)
200
  }
24✔
201

202
  LOG_DEBUG("Final mapping\n" << dipath)
203

204
  return dipath.final_type();
120!
205
}
120✔
206
}  // namespace dimeta::fortran
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