xref: /llvm-project/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp (revision 2e5a5237daf82a657561c490845c406e13657311)
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 
24 namespace fir {
25 
26 /// Calculate offset of any field in the descriptor.
27 template <int DescriptorField>
28 std::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 }
40 template <>
41 std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
42                                     mlir::MLIRContext *context,
43                                     mlir::Type llvmFieldType) {
44   return 0;
45 }
46 
47 DebugTypeGenerator::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 
75 static 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 
83 static 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.
90 mlir::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 
117 mlir::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 
262 std::pair<std::uint64_t, unsigned short>
263 DebugTypeGenerator::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 
275 mlir::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 
424 mlir::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 
460 mlir::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 
523 mlir::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 
556 mlir::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 
613 mlir::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 
643 mlir::LLVM::DITypeAttr
644 DebugTypeGenerator::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