• 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

7.17
/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) {
9,958✔
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);
9,958✔
41
  if (!struct_type) {
9,958✔
42
    return false;
2,384✔
43
  }
44
  const auto num_elements = struct_type->getNumElements();
7,574✔
45
  if (num_elements != 10 && num_elements != 9) {
7,574!
46
    return false;
6,282✔
47
  }
48
  const bool is_scalar = (num_elements == 9);
1,292✔
49
  // 0: ptr (base_addr)
50
  if (!struct_type->getElementType(0)->isPointerTy()) {
1,292!
51
    return false;
1,292✔
52
  }
53
  // 1: i64 (elem_len)
NEW
54
  if (!struct_type->getElementType(1)->isIntegerTy(64)) {
×
NEW
55
    return false;
×
56
  }
57
  // 2: i32 (version)
NEW
58
  if (!struct_type->getElementType(2)->isIntegerTy(32)) {
×
NEW
59
    return false;
×
60
  }
61
  // 3-6: i8 (rank, type, attribute, f18Addendum)
NEW
62
  for (unsigned i = 3; i <= 6; ++i) {
×
NEW
63
    if (!struct_type->getElementType(i)->isIntegerTy(8)) {
×
NEW
64
      return false;
×
65
    }
NEW
66
  }
×
NEW
67
  unsigned current_idx     = 7;
×
NEW
68
  llvm::ArrayType* dim_arr = nullptr;
×
NEW
69
  if (!is_scalar) {
×
70
    // 7: [n x [3 x i64]] (dimensions)
NEW
71
    dim_arr = llvm::dyn_cast<llvm::ArrayType>(struct_type->getElementType(current_idx++));
×
NEW
72
    if (!dim_arr) {
×
NEW
73
      return false;
×
74
    }
NEW
75
    auto* inner_dim_arr = llvm::dyn_cast<llvm::ArrayType>(dim_arr->getElementType());
×
NEW
76
    if (!inner_dim_arr || inner_dim_arr->getNumElements() != 3 || !inner_dim_arr->getElementType()->isIntegerTy(64)) {
×
NEW
77
      return false;
×
78
    }
NEW
79
  }
×
80
  // 8 (or 7): ptr (type descriptor pointer)
NEW
81
  if (!struct_type->getElementType(current_idx++)->isPointerTy()) {
×
NEW
82
    return false;
×
83
  }
84
  // 9 (or 8): [n x i64] (addendum)
NEW
85
  auto* addendum_arr = llvm::dyn_cast<llvm::ArrayType>(struct_type->getElementType(current_idx++));
×
NEW
86
  if (!addendum_arr || !addendum_arr->getElementType()->isIntegerTy(64)) {
×
NEW
87
    return false;
×
88
  }
NEW
89
  if (dim_arr && dim_arr->getNumElements() != addendum_arr->getNumElements()) {
×
NEW
90
    return false;
×
91
  }
NEW
92
  return true;
×
93
}
9,958✔
94

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

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

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

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

NEW
127
  logged_dipath.emplace_back(*current_value, type.value_or(nullptr));
×
128

129
  return type;
NEW
130
}
×
131

NEW
132
std::optional<llvm::DIType*> extract(const dataflow::CallValuePath& call_path, std::optional<llvm::DIType*> type) {
×
NEW
133
  assert(call_path.call.has_value() && "Expected a Fortran call handle");
×
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

NEW
142
  type::dipath::ValueToDiPath dipath;
×
143

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

NEW
154
  auto* ditype_final = type.value();
×
155

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

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

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

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

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

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

NEW
204
  return dipath.final_type();
×
NEW
205
}
×
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