1//===-- DebugTypeGenerator.cpp -- type conversion ---------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10//
11//===----------------------------------------------------------------------===//
12
13#define DEBUG_TYPE "flang-debug-type-generator"
14
15#include "DebugTypeGenerator.h"
16#include "flang/Optimizer/CodeGen/DescriptorModel.h"
17#include "flang/Optimizer/Support/InternalNames.h"
18#include "flang/Optimizer/Support/Utils.h"
19#include "mlir/Pass/Pass.h"
20#include "llvm/ADT/ScopeExit.h"
21#include "llvm/BinaryFormat/Dwarf.h"
22#include "llvm/Support/Debug.h"
23
24namespace fir {
25
26/// Calculate offset of any field in the descriptor.
27template <int DescriptorField>
28std::uint64_t getComponentOffset(const mlir::DataLayout &dl,
29 mlir::MLIRContext *context,
30 mlir::Type llvmFieldType) {
31 static_assert(DescriptorField > 0 && DescriptorField < 10);
32 mlir::Type previousFieldType =
33 getDescFieldTypeModel<DescriptorField - 1>()(context);
34 std::uint64_t previousOffset =
35 getComponentOffset<DescriptorField - 1>(dl, context, previousFieldType);
36 std::uint64_t offset = previousOffset + dl.getTypeSize(previousFieldType);
37 std::uint64_t fieldAlignment = dl.getTypeABIAlignment(llvmFieldType);
38 return llvm::alignTo(offset, fieldAlignment);
39}
40template <>
41std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
42 mlir::MLIRContext *context,
43 mlir::Type llvmFieldType) {
44 return 0;
45}
46
47DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
48 mlir::SymbolTable *symbolTable_,
49 const mlir::DataLayout &dl)
50 : module(m), symbolTable(symbolTable_), dataLayout{&dl},
51 kindMapping(getKindMapping(m)), llvmTypeConverter(m, false, false, dl),
52 derivedTypeDepth(0) {
53 LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");
54
55 mlir::MLIRContext *context = module.getContext();
56
57 // The debug information requires the offset of certain fields in the
58 // descriptors like lower_bound and extent for each dimension.
59 mlir::Type llvmDimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
60 mlir::Type llvmPtrType = getDescFieldTypeModel<kAddrPosInBox>()(context);
61 mlir::Type llvmLenType = getDescFieldTypeModel<kElemLenPosInBox>()(context);
62 mlir::Type llvmRankType = getDescFieldTypeModel<kRankPosInBox>()(context);
63
64 dimsOffset =
65 getComponentOffset<kDimsPosInBox>(*dataLayout, context, llvmDimsType);
66 dimsSize = dataLayout->getTypeSize(llvmDimsType);
67 ptrSize = dataLayout->getTypeSize(llvmPtrType);
68 rankSize = dataLayout->getTypeSize(llvmRankType);
69 lenOffset =
70 getComponentOffset<kElemLenPosInBox>(*dataLayout, context, llvmLenType);
71 rankOffset =
72 getComponentOffset<kRankPosInBox>(*dataLayout, context, llvmRankType);
73}
74
75static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context,
76 mlir::StringAttr name,
77 unsigned bitSize,
78 unsigned decoding) {
79 return mlir::LLVM::DIBasicTypeAttr::get(
80 context, llvm::dwarf::DW_TAG_base_type, name, bitSize, decoding);
81}
82
83static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) {
84 return genBasicType(context, mlir::StringAttr::get(context, "integer"),
85 /*bitSize=*/32, llvm::dwarf::DW_ATE_signed);
86}
87
88// Helper function to create DILocalVariableAttr and DbgValueOp when information
89// about the size or dimension of a variable etc lives in an mlir::Value.
90mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
91 mlir::MLIRContext *context, mlir::Value val,
92 mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope,
93 fir::cg::XDeclareOp declOp) {
94 // There can be multiple artificial variable for a single declOp. To help
95 // distinguish them, we pad the name with a counter. The counter is the
96 // position of 'val' in the operands of declOp.
97 auto varID = std::distance(
98 declOp.getOperands().begin(),
99 std::find(declOp.getOperands().begin(), declOp.getOperands().end(), val));
100 mlir::OpBuilder builder(context);
101 auto name = mlir::StringAttr::get(context, "." + declOp.getUniqName().str() +
102 std::to_string(varID));
103 builder.setInsertionPoint(declOp);
104 mlir::Type type = val.getType();
105 if (!mlir::isa<mlir::IntegerType>(type) || !type.isSignlessInteger()) {
106 type = builder.getIntegerType(64);
107 val = builder.create<fir::ConvertOp>(declOp.getLoc(), type, val);
108 }
109 mlir::LLVM::DITypeAttr Ty = convertType(type, fileAttr, scope, declOp);
110 auto lvAttr = mlir::LLVM::DILocalVariableAttr::get(
111 context, scope, name, fileAttr, /*line=*/0, /*argNo=*/0,
112 /*alignInBits=*/0, Ty, mlir::LLVM::DIFlags::Artificial);
113 builder.create<mlir::LLVM::DbgValueOp>(declOp.getLoc(), val, lvAttr, nullptr);
114 return lvAttr;
115}
116
117mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
118 fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
119 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp,
120 bool genAllocated, bool genAssociated) {
121
122 mlir::MLIRContext *context = module.getContext();
123 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
124 llvm::SmallVector<mlir::LLVM::DIExpressionElemAttr> ops;
125 auto addOp = [&](unsigned opc, llvm::ArrayRef<uint64_t> vals) {
126 ops.push_back(mlir::LLVM::DIExpressionElemAttr::get(context, opc, vals));
127 };
128
129 addOp(llvm::dwarf::DW_OP_push_object_address, {});
130 addOp(llvm::dwarf::DW_OP_deref, {});
131
132 // dataLocation = *base_addr
133 mlir::LLVM::DIExpressionAttr dataLocation =
134 mlir::LLVM::DIExpressionAttr::get(context, ops);
135 ops.clear();
136
137 mlir::LLVM::DITypeAttr elemTy =
138 convertType(seqTy.getEleTy(), fileAttr, scope, declOp);
139
140 // Assumed-rank arrays
141 if (seqTy.hasUnknownShape()) {
142 addOp(llvm::dwarf::DW_OP_push_object_address, {});
143 addOp(llvm::dwarf::DW_OP_plus_uconst, {rankOffset});
144 addOp(llvm::dwarf::DW_OP_deref_size, {rankSize});
145 mlir::LLVM::DIExpressionAttr rank =
146 mlir::LLVM::DIExpressionAttr::get(context, ops);
147 ops.clear();
148
149 auto genSubrangeOp = [&](unsigned field) -> mlir::LLVM::DIExpressionAttr {
150 // The dwarf expression for generic subrange assumes that dimension for
151 // which it is being generated is already pushed on the stack. Here is the
152 // formula we will use to calculate count for example.
153 // *(base_addr + offset_count_0 + (dimsSize x dimension_number)).
154 // where offset_count_0 is offset of the count field for the 0th dimension
155 addOp(llvm::dwarf::DW_OP_push_object_address, {});
156 addOp(llvm::dwarf::DW_OP_over, {});
157 addOp(llvm::dwarf::DW_OP_constu, {dimsSize});
158 addOp(llvm::dwarf::DW_OP_mul, {});
159 addOp(llvm::dwarf::DW_OP_plus_uconst,
160 {dimsOffset + ((dimsSize / 3) * field)});
161 addOp(llvm::dwarf::DW_OP_plus, {});
162 addOp(llvm::dwarf::DW_OP_deref, {});
163 mlir::LLVM::DIExpressionAttr attr =
164 mlir::LLVM::DIExpressionAttr::get(context, ops);
165 ops.clear();
166 return attr;
167 };
168
169 mlir::LLVM::DIExpressionAttr lowerAttr = genSubrangeOp(kDimLowerBoundPos);
170 mlir::LLVM::DIExpressionAttr countAttr = genSubrangeOp(kDimExtentPos);
171 mlir::LLVM::DIExpressionAttr strideAttr = genSubrangeOp(kDimStridePos);
172
173 auto subrangeTy = mlir::LLVM::DIGenericSubrangeAttr::get(
174 context, countAttr, lowerAttr, /*upperBound=*/nullptr, strideAttr);
175 elements.push_back(subrangeTy);
176
177 return mlir::LLVM::DICompositeTypeAttr::get(
178 context, llvm::dwarf::DW_TAG_array_type, /*name=*/nullptr,
179 /*file=*/nullptr, /*line=*/0, /*scope=*/nullptr, elemTy,
180 mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0, /*alignInBits=*/0,
181 elements, dataLocation, rank, /*allocated=*/nullptr,
182 /*associated=*/nullptr);
183 }
184
185 addOp(llvm::dwarf::DW_OP_push_object_address, {});
186 addOp(llvm::dwarf::DW_OP_deref, {});
187 addOp(llvm::dwarf::DW_OP_lit0, {});
188 addOp(llvm::dwarf::DW_OP_ne, {});
189
190 // allocated = associated = (*base_addr != 0)
191 mlir::LLVM::DIExpressionAttr valid =
192 mlir::LLVM::DIExpressionAttr::get(context, ops);
193 mlir::LLVM::DIExpressionAttr allocated = genAllocated ? valid : nullptr;
194 mlir::LLVM::DIExpressionAttr associated = genAssociated ? valid : nullptr;
195 ops.clear();
196
197 unsigned offset = dimsOffset;
198 unsigned index = 0;
199 mlir::IntegerType intTy = mlir::IntegerType::get(context, 64);
200 const unsigned indexSize = dimsSize / 3;
201 for ([[maybe_unused]] auto _ : seqTy.getShape()) {
202 // For each dimension, find the offset of count, lower bound and stride in
203 // the descriptor and generate the dwarf expression to extract it.
204 mlir::Attribute lowerAttr = nullptr;
205 // If declaration has a lower bound, use it.
206 if (declOp && declOp.getShift().size() > index) {
207 if (std::optional<std::int64_t> optint =
208 getIntIfConstant(declOp.getShift()[index]))
209 lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, *optint));
210 else
211 lowerAttr = generateArtificialVariable(
212 context, declOp.getShift()[index], fileAttr, scope, declOp);
213 }
214 // FIXME: If `indexSize` happens to be bigger than address size on the
215 // system then we may have to change 'DW_OP_deref' here.
216 addOp(llvm::dwarf::DW_OP_push_object_address, {});
217 addOp(llvm::dwarf::DW_OP_plus_uconst,
218 {offset + (indexSize * kDimExtentPos)});
219 addOp(llvm::dwarf::DW_OP_deref, {});
220 // count[i] = *(base_addr + offset + (indexSize * kDimExtentPos))
221 // where 'offset' is dimsOffset + (i * dimsSize)
222 mlir::LLVM::DIExpressionAttr countAttr =
223 mlir::LLVM::DIExpressionAttr::get(context, ops);
224 ops.clear();
225
226 // If a lower bound was not found in the declOp, then we will get them from
227 // descriptor only for pointer and allocatable case. DWARF assumes lower
228 // bound of 1 when this attribute is missing.
229 if (!lowerAttr && (genAllocated || genAssociated)) {
230 addOp(llvm::dwarf::DW_OP_push_object_address, {});
231 addOp(llvm::dwarf::DW_OP_plus_uconst,
232 {offset + (indexSize * kDimLowerBoundPos)});
233 addOp(llvm::dwarf::DW_OP_deref, {});
234 // lower_bound[i] = *(base_addr + offset + (indexSize *
235 // kDimLowerBoundPos))
236 lowerAttr = mlir::LLVM::DIExpressionAttr::get(context, ops);
237 ops.clear();
238 }
239
240 addOp(llvm::dwarf::DW_OP_push_object_address, {});
241 addOp(llvm::dwarf::DW_OP_plus_uconst,
242 {offset + (indexSize * kDimStridePos)});
243 addOp(llvm::dwarf::DW_OP_deref, {});
244 // stride[i] = *(base_addr + offset + (indexSize * kDimStridePos))
245 mlir::LLVM::DIExpressionAttr strideAttr =
246 mlir::LLVM::DIExpressionAttr::get(context, ops);
247 ops.clear();
248
249 offset += dimsSize;
250 mlir::LLVM::DISubrangeAttr subrangeTy = mlir::LLVM::DISubrangeAttr::get(
251 context, countAttr, lowerAttr, /*upperBound=*/nullptr, strideAttr);
252 elements.push_back(subrangeTy);
253 ++index;
254 }
255 return mlir::LLVM::DICompositeTypeAttr::get(
256 context, llvm::dwarf::DW_TAG_array_type, /*name=*/nullptr,
257 /*file=*/nullptr, /*line=*/0, /*scope=*/nullptr, elemTy,
258 mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0, /*alignInBits=*/0, elements,
259 dataLocation, /*rank=*/nullptr, allocated, associated);
260}
261
262std::pair<std::uint64_t, unsigned short>
263DebugTypeGenerator::getFieldSizeAndAlign(mlir::Type fieldTy) {
264 mlir::Type llvmTy;
265 if (auto boxTy = mlir::dyn_cast_if_present<fir::BaseBoxType>(fieldTy))
266 llvmTy = llvmTypeConverter.convertBoxTypeAsStruct(boxTy, getBoxRank(boxTy));
267 else
268 llvmTy = llvmTypeConverter.convertType(fieldTy);
269
270 uint64_t byteSize = dataLayout->getTypeSize(llvmTy);
271 unsigned short byteAlign = dataLayout->getTypeABIAlignment(llvmTy);
272 return std::pair{byteSize, byteAlign};
273}
274
275mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
276 fir::RecordType Ty, mlir::LLVM::DIFileAttr fileAttr,
277 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
278 // Check if this type has already been converted.
279 auto iter = typeCache.find(Ty);
280 if (iter != typeCache.end())
281 return iter->second;
282
283 bool canCacheThisType = true;
284 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
285 mlir::MLIRContext *context = module.getContext();
286 auto recId = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
287 // Generate a place holder TypeAttr which will be used if a member
288 // references the parent type.
289 auto comAttr = mlir::LLVM::DICompositeTypeAttr::get(
290 context, recId, /*isRecSelf=*/true, llvm::dwarf::DW_TAG_structure_type,
291 mlir::StringAttr::get(context, ""), fileAttr, /*line=*/0, scope,
292 /*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0,
293 /*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
294 /*allocated=*/nullptr, /*associated=*/nullptr);
295 typeCache[Ty] = comAttr;
296
297 auto result = fir::NameUniquer::deconstruct(Ty.getName());
298 if (result.first != fir::NameUniquer::NameKind::DERIVED_TYPE)
299 return genPlaceholderType(context);
300
301 fir::TypeInfoOp tiOp = symbolTable->lookup<fir::TypeInfoOp>(Ty.getName());
302 unsigned line = (tiOp) ? getLineFromLoc(tiOp.getLoc()) : 1;
303
304 mlir::OpBuilder builder(context);
305 mlir::IntegerType intTy = mlir::IntegerType::get(context, 64);
306 std::uint64_t offset = 0;
307 for (auto [fieldName, fieldTy] : Ty.getTypeList()) {
308 auto [byteSize, byteAlign] = getFieldSizeAndAlign(fieldTy);
309 std::optional<llvm::ArrayRef<int64_t>> lowerBounds =
310 fir::getComponentLowerBoundsIfNonDefault(Ty, fieldName, module,
311 symbolTable);
312 auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(fieldTy);
313
314 // For members of the derived types, the information about the shift in
315 // lower bounds is not part of the declOp but has to be extracted from the
316 // TypeInfoOp (using getComponentLowerBoundsIfNonDefault).
317 mlir::LLVM::DITypeAttr elemTy;
318 if (lowerBounds && seqTy &&
319 lowerBounds->size() == seqTy.getShape().size()) {
320 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
321 for (auto [bound, dim] :
322 llvm::zip_equal(*lowerBounds, seqTy.getShape())) {
323 auto countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, dim));
324 auto lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, bound));
325 auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
326 context, countAttr, lowerAttr, /*upperBound=*/nullptr,
327 /*stride=*/nullptr);
328 elements.push_back(subrangeTy);
329 }
330 elemTy = mlir::LLVM::DICompositeTypeAttr::get(
331 context, llvm::dwarf::DW_TAG_array_type, /*name=*/nullptr,
332 /*file=*/nullptr, /*line=*/0, /*scope=*/nullptr,
333 convertType(seqTy.getEleTy(), fileAttr, scope, declOp),
334 mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0, /*alignInBits=*/0,
335 elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
336 /*allocated=*/nullptr, /*associated=*/nullptr);
337 } else
338 elemTy = convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr);
339 offset = llvm::alignTo(offset, byteAlign);
340 mlir::LLVM::DIDerivedTypeAttr tyAttr = mlir::LLVM::DIDerivedTypeAttr::get(
341 context, llvm::dwarf::DW_TAG_member,
342 mlir::StringAttr::get(context, fieldName), elemTy, byteSize * 8,
343 byteAlign * 8, offset * 8, /*optional<address space>=*/std::nullopt,
344 /*extra data=*/nullptr);
345 elements.push_back(tyAttr);
346 offset += llvm::alignTo(byteSize, byteAlign);
347
348 // Currently, the handling of recursive debug type in mlir has some
349 // limitations that were discussed at the end of the thread for following
350 // PR.
351 // https://github.com/llvm/llvm-project/pull/106571
352 //
353 // Problem could be explained with the following example code:
354 // type t2
355 // type(t1), pointer :: p1
356 // end type
357 // type t1
358 // type(t2), pointer :: p2
359 // end type
360 // In the description below, type_self means a temporary type that is
361 // generated
362 // as a place holder while the members of that type are being processed.
363 //
364 // If we process t1 first then we will have the following structure after
365 // it has been processed.
366 // t1 -> t2 -> t1_self
367 // This is because when we started processing t2, we did not have the
368 // complete t1 but its place holder t1_self.
369 // Now if some entity requires t2, we will already have that in cache and
370 // will return it. But this t2 refers to t1_self and not to t1. In mlir
371 // handling, only those types are allowed to have _self reference which are
372 // wrapped by entity whose reference it is. So t1 -> t2 -> t1_self is ok
373 // because the t1_self reference can be resolved by the outer t1. But
374 // standalone t2 is not because there will be no way to resolve it. Until
375 // this is fixed in mlir, we avoid caching such types. Please see
376 // DebugTranslation::translateRecursive for details on how mlir handles
377 // recursive types.
378 // The code below checks for situation where it will be unsafe to cache
379 // a type to avoid this problem. We do that in 2 situations.
380 // 1. If a member is record type, then its type would have been processed
381 // before reaching here. If it is not in the cache, it means that it was
382 // found to be unsafe to cache. So any type containing it will also not
383 // be cached
384 // 2. The type of the member is found in the cache but it is a place holder.
385 // In this case, its recID should match the recID of the type we are
386 // processing. This helps us to cache the following type.
387 // type t
388 // type(t), allocatable :: p
389 // end type
390 mlir::Type baseTy = getDerivedType(fieldTy);
391 if (auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy)) {
392 auto iter = typeCache.find(recTy);
393 if (iter == typeCache.end())
394 canCacheThisType = false;
395 else {
396 if (auto tyAttr =
397 mlir::dyn_cast<mlir::LLVM::DICompositeTypeAttr>(iter->second)) {
398 if (tyAttr.getIsRecSelf() && tyAttr.getRecId() != recId)
399 canCacheThisType = false;
400 }
401 }
402 }
403 }
404
405 auto finalAttr = mlir::LLVM::DICompositeTypeAttr::get(
406 context, recId, /*isRecSelf=*/false, llvm::dwarf::DW_TAG_structure_type,
407 mlir::StringAttr::get(context, result.second.name), fileAttr, line, scope,
408 /*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
409 /*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
410 /*allocated=*/nullptr, /*associated=*/nullptr);
411
412 // derivedTypeDepth == 1 means that it is a top level type which is safe to
413 // cache.
414 if (canCacheThisType || derivedTypeDepth == 1) {
415 typeCache[Ty] = finalAttr;
416 } else {
417 auto iter = typeCache.find(Ty);
418 if (iter != typeCache.end())
419 typeCache.erase(iter);
420 }
421 return finalAttr;
422}
423
424mlir::LLVM::DITypeAttr DebugTypeGenerator::convertTupleType(
425 mlir::TupleType Ty, mlir::LLVM::DIFileAttr fileAttr,
426 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
427 // Check if this type has already been converted.
428 auto iter = typeCache.find(Ty);
429 if (iter != typeCache.end())
430 return iter->second;
431
432 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
433 mlir::MLIRContext *context = module.getContext();
434
435 std::uint64_t offset = 0;
436 for (auto fieldTy : Ty.getTypes()) {
437 auto [byteSize, byteAlign] = getFieldSizeAndAlign(fieldTy);
438 mlir::LLVM::DITypeAttr elemTy =
439 convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr);
440 offset = llvm::alignTo(offset, byteAlign);
441 mlir::LLVM::DIDerivedTypeAttr tyAttr = mlir::LLVM::DIDerivedTypeAttr::get(
442 context, llvm::dwarf::DW_TAG_member, mlir::StringAttr::get(context, ""),
443 elemTy, byteSize * 8, byteAlign * 8, offset * 8,
444 /*optional<address space>=*/std::nullopt,
445 /*extra data=*/nullptr);
446 elements.push_back(tyAttr);
447 offset += llvm::alignTo(byteSize, byteAlign);
448 }
449
450 auto typeAttr = mlir::LLVM::DICompositeTypeAttr::get(
451 context, llvm::dwarf::DW_TAG_structure_type,
452 mlir::StringAttr::get(context, ""), fileAttr, /*line=*/0, scope,
453 /*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
454 /*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
455 /*allocated=*/nullptr, /*associated=*/nullptr);
456 typeCache[Ty] = typeAttr;
457 return typeAttr;
458}
459
460mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(
461 fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
462 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
463 mlir::MLIRContext *context = module.getContext();
464
465 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
466 mlir::LLVM::DITypeAttr elemTy =
467 convertType(seqTy.getEleTy(), fileAttr, scope, declOp);
468
469 unsigned index = 0;
470 auto intTy = mlir::IntegerType::get(context, 64);
471 for (fir::SequenceType::Extent dim : seqTy.getShape()) {
472 mlir::Attribute lowerAttr = nullptr;
473 mlir::Attribute countAttr = nullptr;
474 // If declOp is present, we use the shift in it to get the lower bound of
475 // the array. If it is constant, that is used. If it is not constant, we
476 // create a variable that represents its location and use that as lower
477 // bound. As an optimization, we don't create a lower bound when shift is a
478 // constant 1 as that is the default.
479 if (declOp && declOp.getShift().size() > index) {
480 if (std::optional<std::int64_t> optint =
481 getIntIfConstant(declOp.getShift()[index])) {
482 if (*optint != 1)
483 lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, *optint));
484 } else
485 lowerAttr = generateArtificialVariable(
486 context, declOp.getShift()[index], fileAttr, scope, declOp);
487 }
488
489 if (dim == seqTy.getUnknownExtent()) {
490 // This path is taken for both assumed size array or when the size of the
491 // array is variable. In the case of variable size, we create a variable
492 // to use as countAttr. Note that fir has a constant size of -1 for
493 // assumed size array. So !optint check makes sure we don't generate
494 // variable in that case.
495 if (declOp && declOp.getShape().size() > index) {
496 std::optional<std::int64_t> optint =
497 getIntIfConstant(declOp.getShape()[index]);
498 if (!optint)
499 countAttr = generateArtificialVariable(
500 context, declOp.getShape()[index], fileAttr, scope, declOp);
501 }
502 } else
503 countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, dim));
504
505 auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
506 context, countAttr, lowerAttr, /*upperBound=*/nullptr,
507 /*stride=*/nullptr);
508 elements.push_back(subrangeTy);
509 ++index;
510 }
511 // Apart from arrays, the `DICompositeTypeAttr` is used for other things like
512 // structure types. Many of its fields which are not applicable to arrays
513 // have been set to some valid default values.
514
515 return mlir::LLVM::DICompositeTypeAttr::get(
516 context, llvm::dwarf::DW_TAG_array_type, /*name=*/nullptr,
517 /*file=*/nullptr, /*line=*/0, /*scope=*/nullptr, elemTy,
518 mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0, /*alignInBits=*/0, elements,
519 /*dataLocation=*/nullptr, /*rank=*/nullptr, /*allocated=*/nullptr,
520 /*associated=*/nullptr);
521}
522
523mlir::LLVM::DITypeAttr DebugTypeGenerator::convertVectorType(
524 fir::VectorType vecTy, mlir::LLVM::DIFileAttr fileAttr,
525 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
526 mlir::MLIRContext *context = module.getContext();
527
528 llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
529 mlir::LLVM::DITypeAttr elemTy =
530 convertType(vecTy.getEleTy(), fileAttr, scope, declOp);
531 auto intTy = mlir::IntegerType::get(context, 64);
532 auto countAttr =
533 mlir::IntegerAttr::get(intTy, llvm::APInt(64, vecTy.getLen()));
534 auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
535 context, countAttr, /*lowerBound=*/nullptr, /*upperBound=*/nullptr,
536 /*stride=*/nullptr);
537 elements.push_back(subrangeTy);
538 mlir::Type llvmTy = llvmTypeConverter.convertType(vecTy.getEleTy());
539 uint64_t sizeInBits = dataLayout->getTypeSize(llvmTy) * vecTy.getLen() * 8;
540 std::string name("vector");
541 // The element type of the vector must be integer or real so it will be a
542 // DIBasicTypeAttr.
543 if (auto ty = mlir::dyn_cast_if_present<mlir::LLVM::DIBasicTypeAttr>(elemTy))
544 name += " " + ty.getName().str();
545
546 name += " (" + std::to_string(vecTy.getLen()) + ")";
547 return mlir::LLVM::DICompositeTypeAttr::get(
548 context, llvm::dwarf::DW_TAG_array_type,
549 mlir::StringAttr::get(context, name),
550 /*file=*/nullptr, /*line=*/0, /*scope=*/nullptr, elemTy,
551 mlir::LLVM::DIFlags::Vector, sizeInBits, /*alignInBits=*/0, elements,
552 /*dataLocation=*/nullptr, /*rank=*/nullptr, /*allocated=*/nullptr,
553 /*associated=*/nullptr);
554}
555
556mlir::LLVM::DITypeAttr DebugTypeGenerator::convertCharacterType(
557 fir::CharacterType charTy, mlir::LLVM::DIFileAttr fileAttr,
558 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp,
559 bool hasDescriptor) {
560 mlir::MLIRContext *context = module.getContext();
561
562 // DWARF 5 says the following about the character encoding in 5.1.1.2.
563 // "DW_ATE_ASCII and DW_ATE_UCS specify encodings for the Fortran 2003
564 // string kinds ASCII (ISO/IEC 646:1991) and ISO_10646 (UCS-4 in ISO/IEC
565 // 10646:2000)."
566 unsigned encoding = llvm::dwarf::DW_ATE_ASCII;
567 if (charTy.getFKind() != 1)
568 encoding = llvm::dwarf::DW_ATE_UCS;
569
570 uint64_t sizeInBits = 0;
571 mlir::LLVM::DIExpressionAttr lenExpr = nullptr;
572 mlir::LLVM::DIExpressionAttr locExpr = nullptr;
573 mlir::LLVM::DIVariableAttr varAttr = nullptr;
574
575 if (hasDescriptor) {
576 llvm::SmallVector<mlir::LLVM::DIExpressionElemAttr> ops;
577 auto addOp = [&](unsigned opc, llvm::ArrayRef<uint64_t> vals) {
578 ops.push_back(mlir::LLVM::DIExpressionElemAttr::get(context, opc, vals));
579 };
580 addOp(llvm::dwarf::DW_OP_push_object_address, {});
581 addOp(llvm::dwarf::DW_OP_plus_uconst, {lenOffset});
582 lenExpr = mlir::LLVM::DIExpressionAttr::get(context, ops);
583 ops.clear();
584
585 addOp(llvm::dwarf::DW_OP_push_object_address, {});
586 addOp(llvm::dwarf::DW_OP_deref, {});
587 locExpr = mlir::LLVM::DIExpressionAttr::get(context, ops);
588 } else if (charTy.hasConstantLen()) {
589 sizeInBits =
590 charTy.getLen() * kindMapping.getCharacterBitsize(charTy.getFKind());
591 } else {
592 // In assumed length string, the len of the character is not part of the
593 // type but can be found at the runtime. Here we create an artificial
594 // variable that will contain that length. This variable is used as
595 // 'stringLength' in DIStringTypeAttr.
596 if (declOp && !declOp.getTypeparams().empty()) {
597 mlir::LLVM::DILocalVariableAttr lvAttr = generateArtificialVariable(
598 context, declOp.getTypeparams()[0], fileAttr, scope, declOp);
599 varAttr = mlir::cast<mlir::LLVM::DIVariableAttr>(lvAttr);
600 }
601 }
602
603 // FIXME: Currently the DIStringType in llvm does not have the option to set
604 // type of the underlying character. This restricts out ability to represent
605 // string with non-default characters. Please see issue #95440 for more
606 // details.
607 return mlir::LLVM::DIStringTypeAttr::get(
608 context, llvm::dwarf::DW_TAG_string_type,
609 mlir::StringAttr::get(context, ""), sizeInBits, /*alignInBits=*/0,
610 /*stringLength=*/varAttr, lenExpr, locExpr, encoding);
611}
612
613mlir::LLVM::DITypeAttr DebugTypeGenerator::convertPointerLikeType(
614 mlir::Type elTy, mlir::LLVM::DIFileAttr fileAttr,
615 mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp,
616 bool genAllocated, bool genAssociated) {
617 mlir::MLIRContext *context = module.getContext();
618
619 // Arrays and character need different treatment because DWARF have special
620 // constructs for them to get the location from the descriptor. Rest of
621 // types are handled like pointer to underlying type.
622 if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(elTy))
623 return convertBoxedSequenceType(seqTy, fileAttr, scope, declOp,
624 genAllocated, genAssociated);
625 if (auto charTy = mlir::dyn_cast_if_present<fir::CharacterType>(elTy))
626 return convertCharacterType(charTy, fileAttr, scope, declOp,
627 /*hasDescriptor=*/true);
628
629 // If elTy is null or none then generate a void*
630 mlir::LLVM::DITypeAttr elTyAttr;
631 if (!elTy || mlir::isa<mlir::NoneType>(elTy))
632 elTyAttr = mlir::LLVM::DINullTypeAttr::get(context);
633 else
634 elTyAttr = convertType(elTy, fileAttr, scope, declOp);
635
636 return mlir::LLVM::DIDerivedTypeAttr::get(
637 context, llvm::dwarf::DW_TAG_pointer_type,
638 mlir::StringAttr::get(context, ""), elTyAttr, /*sizeInBits=*/ptrSize * 8,
639 /*alignInBits=*/0, /*offset=*/0,
640 /*optional<address space>=*/std::nullopt, /*extra data=*/nullptr);
641}
642
643mlir::LLVM::DITypeAttr
644DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
645 mlir::LLVM::DIScopeAttr scope,
646 fir::cg::XDeclareOp declOp) {
647 mlir::MLIRContext *context = module.getContext();
648 if (Ty.isInteger()) {
649 return genBasicType(context, mlir::StringAttr::get(context, "integer"),
650 Ty.getIntOrFloatBitWidth(), llvm::dwarf::DW_ATE_signed);
651 } else if (mlir::isa<mlir::FloatType>(Ty)) {
652 return genBasicType(context, mlir::StringAttr::get(context, "real"),
653 Ty.getIntOrFloatBitWidth(), llvm::dwarf::DW_ATE_float);
654 } else if (auto logTy = mlir::dyn_cast_if_present<fir::LogicalType>(Ty)) {
655 return genBasicType(context,
656 mlir::StringAttr::get(context, logTy.getMnemonic()),
657 kindMapping.getLogicalBitsize(logTy.getFKind()),
658 llvm::dwarf::DW_ATE_boolean);
659 } else if (auto cplxTy = mlir::dyn_cast_if_present<mlir::ComplexType>(Ty)) {
660 auto floatTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
661 unsigned bitWidth = floatTy.getWidth();
662 return genBasicType(context, mlir::StringAttr::get(context, "complex"),
663 bitWidth * 2, llvm::dwarf::DW_ATE_complex_float);
664 } else if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(Ty)) {
665 return convertSequenceType(seqTy, fileAttr, scope, declOp);
666 } else if (auto charTy = mlir::dyn_cast_if_present<fir::CharacterType>(Ty)) {
667 return convertCharacterType(charTy, fileAttr, scope, declOp,
668 /*hasDescriptor=*/false);
669 } else if (auto recTy = mlir::dyn_cast_if_present<fir::RecordType>(Ty)) {
670 // For nested derived types like shown below, the call sequence of the
671 // convertRecordType will look something like as follows:
672 // convertRecordType (t1)
673 // convertRecordType (t2)
674 // convertRecordType (t3)
675 // We need to recognize when we are processing the top level type like t1
676 // to make caching decision. The variable `derivedTypeDepth` is used for
677 // this purpose and maintains the current depth of derived type processing.
678 // type t1
679 // type(t2), pointer :: p1
680 // end type
681 // type t2
682 // type(t3), pointer :: p2
683 // end type
684 // type t2
685 // integer a
686 // end type
687 derivedTypeDepth++;
688 auto result = convertRecordType(recTy, fileAttr, scope, declOp);
689 derivedTypeDepth--;
690 return result;
691 } else if (auto tupleTy = mlir::dyn_cast_if_present<mlir::TupleType>(Ty)) {
692 return convertTupleType(tupleTy, fileAttr, scope, declOp);
693 } else if (auto refTy = mlir::dyn_cast_if_present<fir::ReferenceType>(Ty)) {
694 auto elTy = refTy.getEleTy();
695 return convertPointerLikeType(elTy, fileAttr, scope, declOp,
696 /*genAllocated=*/false,
697 /*genAssociated=*/false);
698 } else if (auto vecTy = mlir::dyn_cast_if_present<fir::VectorType>(Ty)) {
699 return convertVectorType(vecTy, fileAttr, scope, declOp);
700 } else if (mlir::isa<mlir::IndexType>(Ty)) {
701 return genBasicType(context, mlir::StringAttr::get(context, "integer"),
702 llvmTypeConverter.getIndexTypeBitwidth(),
703 llvm::dwarf::DW_ATE_signed);
704 } else if (auto boxTy = mlir::dyn_cast_if_present<fir::BaseBoxType>(Ty)) {
705 auto elTy = boxTy.getEleTy();
706 if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(elTy))
707 return convertBoxedSequenceType(seqTy, fileAttr, scope, declOp, false,
708 false);
709 if (auto heapTy = mlir::dyn_cast_if_present<fir::HeapType>(elTy))
710 return convertPointerLikeType(heapTy.getElementType(), fileAttr, scope,
711 declOp, /*genAllocated=*/true,
712 /*genAssociated=*/false);
713 if (auto ptrTy = mlir::dyn_cast_if_present<fir::PointerType>(elTy))
714 return convertPointerLikeType(ptrTy.getElementType(), fileAttr, scope,
715 declOp, /*genAllocated=*/false,
716 /*genAssociated=*/true);
717 return convertPointerLikeType(elTy, fileAttr, scope, declOp,
718 /*genAllocated=*/false,
719 /*genAssociated=*/false);
720 } else {
721 // FIXME: These types are currently unhandled. We are generating a
722 // placeholder type to allow us to test supported bits.
723 return genPlaceholderType(context);
724 }
725}
726
727} // namespace fir
728

source code of flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp