1 //===-- TypeConverter.h -- 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 #ifndef FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
14 #define FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
15 
16 #include "DescriptorModel.h"
17 #include "Target.h"
18 #include "flang/Optimizer/Builder/Todo.h" // remove when TODO's are done
19 #include "flang/Optimizer/Dialect/FIRType.h"
20 #include "flang/Optimizer/Support/FIRContext.h"
21 #include "flang/Optimizer/Support/KindMapping.h"
22 #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
23 #include "llvm/Support/Debug.h"
24 
25 // Position of the different values in a `fir.box`.
26 static constexpr unsigned kAddrPosInBox = 0;
27 static constexpr unsigned kElemLenPosInBox = 1;
28 static constexpr unsigned kVersionPosInBox = 2;
29 static constexpr unsigned kRankPosInBox = 3;
30 static constexpr unsigned kTypePosInBox = 4;
31 static constexpr unsigned kAttributePosInBox = 5;
32 static constexpr unsigned kF18AddendumPosInBox = 6;
33 static constexpr unsigned kDimsPosInBox = 7;
34 static constexpr unsigned kOptTypePtrPosInBox = 8;
35 static constexpr unsigned kOptRowTypePosInBox = 9;
36 
37 // Position of the different values in [dims]
38 static constexpr unsigned kDimLowerBoundPos = 0;
39 static constexpr unsigned kDimExtentPos = 1;
40 static constexpr unsigned kDimStridePos = 2;
41 
42 namespace fir {
43 
44 /// FIR type converter
45 /// This converts FIR types to LLVM types (for now)
46 class LLVMTypeConverter : public mlir::LLVMTypeConverter {
47 public:
LLVMTypeConverter(mlir::ModuleOp module)48   LLVMTypeConverter(mlir::ModuleOp module)
49       : mlir::LLVMTypeConverter(module.getContext()),
50         kindMapping(getKindMapping(module)),
51         specifics(CodeGenSpecifics::get(module.getContext(),
52                                         getTargetTriple(module),
53                                         getKindMapping(module))) {
54     LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");
55 
56     // Each conversion should return a value of type mlir::Type.
57     addConversion([&](BoxType box) { return convertBoxType(box); });
58     addConversion([&](BoxCharType boxchar) {
59       LLVM_DEBUG(llvm::dbgs() << "type convert: " << boxchar << '\n');
60       return convertType(specifics->boxcharMemoryType(boxchar.getEleTy()));
61     });
62     addConversion([&](BoxProcType boxproc) {
63       // TODO: Support for this type will be added later when the Fortran 2003
64       // procedure pointer feature is implemented.
65       return llvm::None;
66     });
67     addConversion(
68         [&](fir::CharacterType charTy) { return convertCharType(charTy); });
69     addConversion(
70         [&](fir::ComplexType cmplx) { return convertComplexType(cmplx); });
71     addConversion([&](fir::FieldType field) {
72       // Convert to i32 because of LLVM GEP indexing restriction.
73       return mlir::IntegerType::get(field.getContext(), 32);
74     });
75     addConversion([&](HeapType heap) { return convertPointerLike(heap); });
76     addConversion([&](fir::IntegerType intTy) {
77       return mlir::IntegerType::get(
78           &getContext(), kindMapping.getIntegerBitsize(intTy.getFKind()));
79     });
80     addConversion([&](fir::LenType field) {
81       // Get size of len paramter from the descriptor.
82       return getModel<Fortran::runtime::typeInfo::TypeParameterValue>()(
83           &getContext());
84     });
85     addConversion([&](fir::LogicalType boolTy) {
86       return mlir::IntegerType::get(
87           &getContext(), kindMapping.getLogicalBitsize(boolTy.getFKind()));
88     });
89     addConversion([&](fir::LLVMPointerType pointer) {
90       return convertPointerLike(pointer);
91     });
92     addConversion(
93         [&](fir::PointerType pointer) { return convertPointerLike(pointer); });
94     addConversion([&](fir::RecordType derived,
95                       llvm::SmallVectorImpl<mlir::Type> &results,
96                       llvm::ArrayRef<mlir::Type> callStack) {
97       return convertRecordType(derived, results, callStack);
98     });
99     addConversion(
100         [&](fir::RealType real) { return convertRealType(real.getFKind()); });
101     addConversion(
102         [&](fir::ReferenceType ref) { return convertPointerLike(ref); });
103     addConversion([&](fir::SequenceType sequence) {
104       return convertSequenceType(sequence);
105     });
106     addConversion([&](fir::TypeDescType tdesc) {
107       return convertTypeDescType(tdesc.getContext());
108     });
109     addConversion([&](fir::VectorType vecTy) {
110       return mlir::VectorType::get(llvm::ArrayRef<int64_t>(vecTy.getLen()),
111                                    convertType(vecTy.getEleTy()));
112     });
113     addConversion([&](mlir::TupleType tuple) {
114       LLVM_DEBUG(llvm::dbgs() << "type convert: " << tuple << '\n');
115       llvm::SmallVector<mlir::Type> members;
116       for (auto mem : tuple.getTypes()) {
117         // Prevent fir.box from degenerating to a pointer to a descriptor in the
118         // context of a tuple type.
119         if (auto box = mem.dyn_cast<fir::BoxType>())
120           members.push_back(convertBoxTypeAsStruct(box));
121         else
122           members.push_back(convertType(mem).cast<mlir::Type>());
123       }
124       return mlir::LLVM::LLVMStructType::getLiteral(&getContext(), members,
125                                                     /*isPacked=*/false);
126     });
127     addConversion([&](mlir::NoneType none) {
128       return mlir::LLVM::LLVMStructType::getLiteral(
129           none.getContext(), llvm::None, /*isPacked=*/false);
130     });
131     // FIXME: https://reviews.llvm.org/D82831 introduced an automatic
132     // materialization of conversion around function calls that is not working
133     // well with fir lowering to llvm (incorrect llvm.mlir.cast are inserted).
134     // Workaround until better analysis: register a handler that does not insert
135     // any conversions.
136     addSourceMaterialization(
137         [&](mlir::OpBuilder &builder, mlir::Type resultType,
138             mlir::ValueRange inputs,
139             mlir::Location loc) -> llvm::Optional<mlir::Value> {
140           if (inputs.size() != 1)
141             return llvm::None;
142           return inputs[0];
143         });
144     // Similar FIXME workaround here (needed for compare.fir/select-type.fir
145     // tests).
146     addTargetMaterialization(
147         [&](mlir::OpBuilder &builder, mlir::Type resultType,
148             mlir::ValueRange inputs,
149             mlir::Location loc) -> llvm::Optional<mlir::Value> {
150           if (inputs.size() != 1)
151             return llvm::None;
152           return inputs[0];
153         });
154   }
155 
156   // i32 is used here because LLVM wants i32 constants when indexing into struct
157   // types. Indexing into other aggregate types is more flexible.
offsetType()158   mlir::Type offsetType() { return mlir::IntegerType::get(&getContext(), 32); }
159 
160   // i64 can be used to index into aggregates like arrays
indexType()161   mlir::Type indexType() { return mlir::IntegerType::get(&getContext(), 64); }
162 
163   // fir.type<name(p : TY'...){f : TY...}>  -->  llvm<"%name = { ty... }">
164   llvm::Optional<mlir::LogicalResult>
convertRecordType(fir::RecordType derived,llvm::SmallVectorImpl<mlir::Type> & results,llvm::ArrayRef<mlir::Type> callStack)165   convertRecordType(fir::RecordType derived,
166                     llvm::SmallVectorImpl<mlir::Type> &results,
167                     llvm::ArrayRef<mlir::Type> callStack) {
168     auto name = derived.getName();
169     auto st = mlir::LLVM::LLVMStructType::getIdentified(&getContext(), name);
170     if (llvm::count(callStack, derived) > 1) {
171       results.push_back(st);
172       return mlir::success();
173     }
174     llvm::SmallVector<mlir::Type> members;
175     for (auto mem : derived.getTypeList()) {
176       // Prevent fir.box from degenerating to a pointer to a descriptor in the
177       // context of a record type.
178       if (auto box = mem.second.dyn_cast<fir::BoxType>())
179         members.push_back(convertBoxTypeAsStruct(box));
180       else
181         members.push_back(convertType(mem.second).cast<mlir::Type>());
182     }
183     if (mlir::failed(st.setBody(members, /*isPacked=*/false)))
184       return mlir::failure();
185     results.push_back(st);
186     return mlir::success();
187   }
188 
189   // Is an extended descriptor needed given the element type of a fir.box type ?
190   // Extended descriptors are required for derived types.
requiresExtendedDesc(mlir::Type boxElementType)191   bool requiresExtendedDesc(mlir::Type boxElementType) {
192     auto eleTy = fir::unwrapSequenceType(boxElementType);
193     return eleTy.isa<fir::RecordType>();
194   }
195 
196   // Magic value to indicate we do not know the rank of an entity, either
197   // because it is assumed rank or because we have not determined it yet.
unknownRank()198   static constexpr int unknownRank() { return -1; }
199 
200   // This corresponds to the descriptor as defined in ISO_Fortran_binding.h and
201   // the addendum defined in descriptor.h.
202   mlir::Type convertBoxType(BoxType box, int rank = unknownRank()) {
203     // (base_addr*, elem_len, version, rank, type, attribute, f18Addendum, [dim]
204     llvm::SmallVector<mlir::Type> dataDescFields;
205     mlir::Type ele = box.getEleTy();
206     // remove fir.heap/fir.ref/fir.ptr
207     if (auto removeIndirection = fir::dyn_cast_ptrEleTy(ele))
208       ele = removeIndirection;
209     auto eleTy = convertType(ele);
210     // base_addr*
211     if (ele.isa<SequenceType>() && eleTy.isa<mlir::LLVM::LLVMPointerType>())
212       dataDescFields.push_back(eleTy);
213     else
214       dataDescFields.push_back(mlir::LLVM::LLVMPointerType::get(eleTy));
215     // elem_len
216     dataDescFields.push_back(
217         getDescFieldTypeModel<kElemLenPosInBox>()(&getContext()));
218     // version
219     dataDescFields.push_back(
220         getDescFieldTypeModel<kVersionPosInBox>()(&getContext()));
221     // rank
222     dataDescFields.push_back(
223         getDescFieldTypeModel<kRankPosInBox>()(&getContext()));
224     // type
225     dataDescFields.push_back(
226         getDescFieldTypeModel<kTypePosInBox>()(&getContext()));
227     // attribute
228     dataDescFields.push_back(
229         getDescFieldTypeModel<kAttributePosInBox>()(&getContext()));
230     // f18Addendum
231     dataDescFields.push_back(
232         getDescFieldTypeModel<kF18AddendumPosInBox>()(&getContext()));
233     // [dims]
234     if (rank == unknownRank()) {
235       if (auto seqTy = ele.dyn_cast<SequenceType>())
236         rank = seqTy.getDimension();
237       else
238         rank = 0;
239     }
240     if (rank > 0) {
241       auto rowTy = getDescFieldTypeModel<kDimsPosInBox>()(&getContext());
242       dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, rank));
243     }
244     // opt-type-ptr: i8* (see fir.tdesc)
245     if (requiresExtendedDesc(ele)) {
246       dataDescFields.push_back(
247           getExtendedDescFieldTypeModel<kOptTypePtrPosInBox>()(&getContext()));
248       auto rowTy =
249           getExtendedDescFieldTypeModel<kOptRowTypePosInBox>()(&getContext());
250       dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, 1));
251       if (auto recTy = fir::unwrapSequenceType(ele).dyn_cast<fir::RecordType>())
252         if (recTy.getNumLenParams() > 0) {
253           // The descriptor design needs to be clarified regarding the number of
254           // length parameters in the addendum. Since it can change for
255           // polymorphic allocatables, it seems all length parameters cannot
256           // always possibly be placed in the addendum.
257           TODO_NOLOC("extended descriptor derived with length parameters");
258           unsigned numLenParams = recTy.getNumLenParams();
259           dataDescFields.push_back(
260               mlir::LLVM::LLVMArrayType::get(rowTy, numLenParams));
261         }
262     }
263     return mlir::LLVM::LLVMPointerType::get(
264         mlir::LLVM::LLVMStructType::getLiteral(&getContext(), dataDescFields,
265                                                /*isPacked=*/false));
266   }
267 
268   /// Convert fir.box type to the corresponding llvm struct type instead of a
269   /// pointer to this struct type.
convertBoxTypeAsStruct(BoxType box)270   mlir::Type convertBoxTypeAsStruct(BoxType box) {
271     return convertBoxType(box)
272         .cast<mlir::LLVM::LLVMPointerType>()
273         .getElementType();
274   }
275 
276   // fir.boxproc<any>  -->  llvm<"{ any*, i8* }">
convertBoxProcType(BoxProcType boxproc)277   mlir::Type convertBoxProcType(BoxProcType boxproc) {
278     auto funcTy = convertType(boxproc.getEleTy());
279     auto i8PtrTy = mlir::LLVM::LLVMPointerType::get(
280         mlir::IntegerType::get(&getContext(), 8));
281     llvm::SmallVector<mlir::Type, 2> tuple = {funcTy, i8PtrTy};
282     return mlir::LLVM::LLVMStructType::getLiteral(&getContext(), tuple,
283                                                   /*isPacked=*/false);
284   }
285 
characterBitsize(fir::CharacterType charTy)286   unsigned characterBitsize(fir::CharacterType charTy) {
287     return kindMapping.getCharacterBitsize(charTy.getFKind());
288   }
289 
290   // fir.char<k,?>  -->  llvm<"ix">          where ix is scaled by kind mapping
291   // fir.char<k,n>  -->  llvm.array<n x "ix">
convertCharType(fir::CharacterType charTy)292   mlir::Type convertCharType(fir::CharacterType charTy) {
293     auto iTy = mlir::IntegerType::get(&getContext(), characterBitsize(charTy));
294     if (charTy.getLen() == fir::CharacterType::unknownLen())
295       return iTy;
296     return mlir::LLVM::LLVMArrayType::get(iTy, charTy.getLen());
297   }
298 
299   // Use the target specifics to figure out how to map complex to LLVM IR. The
300   // use of complex values in function signatures is handled before conversion
301   // to LLVM IR dialect here.
302   //
303   // fir.complex<T> | std.complex<T>    --> llvm<"{t,t}">
304   template <typename C>
convertComplexType(C cmplx)305   mlir::Type convertComplexType(C cmplx) {
306     LLVM_DEBUG(llvm::dbgs() << "type convert: " << cmplx << '\n');
307     auto eleTy = cmplx.getElementType();
308     return convertType(specifics->complexMemoryType(eleTy));
309   }
310 
311   template <typename A>
convertPointerLike(A & ty)312   mlir::Type convertPointerLike(A &ty) {
313     mlir::Type eleTy = ty.getEleTy();
314     // A sequence type is a special case. A sequence of runtime size on its
315     // interior dimensions lowers to a memory reference. In that case, we
316     // degenerate the array and do not want a the type to become `T**` but
317     // merely `T*`.
318     if (auto seqTy = eleTy.dyn_cast<fir::SequenceType>()) {
319       if (!seqTy.hasConstantShape() ||
320           characterWithDynamicLen(seqTy.getEleTy())) {
321         if (seqTy.hasConstantInterior())
322           return convertType(seqTy);
323         eleTy = seqTy.getEleTy();
324       }
325     }
326     // fir.ref<fir.box> is a special case because fir.box type is already
327     // a pointer to a Fortran descriptor at the LLVM IR level. This implies
328     // that a fir.ref<fir.box>, that is the address of fir.box is actually
329     // the same as a fir.box at the LLVM level.
330     // The distinction is kept in fir to denote when a descriptor is expected
331     // to be mutable (fir.ref<fir.box>) and when it is not (fir.box).
332     if (eleTy.isa<fir::BoxType>())
333       return convertType(eleTy);
334 
335     return mlir::LLVM::LLVMPointerType::get(convertType(eleTy));
336   }
337 
338   // convert a front-end kind value to either a std or LLVM IR dialect type
339   // fir.real<n>  -->  llvm.anyfloat  where anyfloat is a kind mapping
convertRealType(fir::KindTy kind)340   mlir::Type convertRealType(fir::KindTy kind) {
341     return fir::fromRealTypeID(&getContext(), kindMapping.getRealTypeID(kind),
342                                kind);
343   }
344 
345   // fir.array<c ... :any>  -->  llvm<"[...[c x any]]">
convertSequenceType(SequenceType seq)346   mlir::Type convertSequenceType(SequenceType seq) {
347     auto baseTy = convertType(seq.getEleTy());
348     if (characterWithDynamicLen(seq.getEleTy()))
349       return mlir::LLVM::LLVMPointerType::get(baseTy);
350     auto shape = seq.getShape();
351     auto constRows = seq.getConstantRows();
352     if (constRows) {
353       decltype(constRows) i = constRows;
354       for (auto e : shape) {
355         baseTy = mlir::LLVM::LLVMArrayType::get(baseTy, e);
356         if (--i == 0)
357           break;
358       }
359       if (seq.hasConstantShape())
360         return baseTy;
361     }
362     return mlir::LLVM::LLVMPointerType::get(baseTy);
363   }
364 
365   // fir.tdesc<any>  -->  llvm<"i8*">
366   // TODO: For now use a void*, however pointer identity is not sufficient for
367   // the f18 object v. class distinction (F2003).
convertTypeDescType(mlir::MLIRContext * ctx)368   mlir::Type convertTypeDescType(mlir::MLIRContext *ctx) {
369     return mlir::LLVM::LLVMPointerType::get(
370         mlir::IntegerType::get(&getContext(), 8));
371   }
372 
getKindMap()373   KindMapping &getKindMap() { return kindMapping; }
374 
375 private:
376   KindMapping kindMapping;
377   std::unique_ptr<CodeGenSpecifics> specifics;
378 };
379 
380 } // namespace fir
381 
382 #endif // FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
383