1//===-- TypeConverter.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-type-conversion"
14
15#include "flang/Optimizer/CodeGen/TypeConverter.h"
16#include "DescriptorModel.h"
17#include "flang/Optimizer/Builder/Todo.h" // remove when TODO's are done
18#include "flang/Optimizer/CodeGen/TBAABuilder.h"
19#include "flang/Optimizer/CodeGen/Target.h"
20#include "flang/Optimizer/Dialect/FIRType.h"
21#include "flang/Optimizer/Dialect/Support/FIRContext.h"
22#include "flang/Optimizer/Dialect/Support/KindMapping.h"
23#include "flang/Optimizer/Support/InternalNames.h"
24#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
25#include "llvm/ADT/ScopeExit.h"
26#include "llvm/Support/Debug.h"
27
28namespace fir {
29
30LLVMTypeConverter::LLVMTypeConverter(mlir::ModuleOp module, bool applyTBAA,
31 bool forceUnifiedTBAATree,
32 const mlir::DataLayout &dl)
33 : mlir::LLVMTypeConverter(module.getContext()),
34 kindMapping(getKindMapping(module)),
35 specifics(CodeGenSpecifics::get(
36 module.getContext(), getTargetTriple(module), getKindMapping(module),
37 getTargetCPU(module), getTargetFeatures(module), dl)),
38 tbaaBuilder(std::make_unique<TBAABuilder>(module->getContext(), applyTBAA,
39 forceUnifiedTBAATree)) {
40 LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");
41
42 // Each conversion should return a value of type mlir::Type.
43 addConversion([&](BoxType box) { return convertBoxType(box); });
44 addConversion([&](BoxCharType boxchar) {
45 LLVM_DEBUG(llvm::dbgs() << "type convert: " << boxchar << '\n');
46 return convertType(specifics->boxcharMemoryType(boxchar.getEleTy()));
47 });
48 addConversion([&](BoxProcType boxproc) {
49 // TODO: Support for this type will be added later when the Fortran 2003
50 // procedure pointer feature is implemented.
51 return std::nullopt;
52 });
53 addConversion(
54 [&](fir::ClassType classTy) { return convertBoxType(classTy); });
55 addConversion(
56 [&](fir::CharacterType charTy) { return convertCharType(charTy); });
57 addConversion(
58 [&](fir::ComplexType cmplx) { return convertComplexType(cmplx); });
59 addConversion([&](fir::FieldType field) {
60 // Convert to i32 because of LLVM GEP indexing restriction.
61 return mlir::IntegerType::get(field.getContext(), 32);
62 });
63 addConversion([&](HeapType heap) { return convertPointerLike(heap); });
64 addConversion([&](fir::IntegerType intTy) {
65 return mlir::IntegerType::get(
66 &getContext(), kindMapping.getIntegerBitsize(intTy.getFKind()));
67 });
68 addConversion([&](fir::LenType field) {
69 // Get size of len paramter from the descriptor.
70 return getModel<Fortran::runtime::typeInfo::TypeParameterValue>()(
71 &getContext());
72 });
73 addConversion([&](fir::LogicalType boolTy) {
74 return mlir::IntegerType::get(
75 &getContext(), kindMapping.getLogicalBitsize(boolTy.getFKind()));
76 });
77 addConversion([&](fir::LLVMPointerType pointer) {
78 return convertPointerLike(pointer);
79 });
80 addConversion(
81 [&](fir::PointerType pointer) { return convertPointerLike(pointer); });
82 addConversion(
83 [&](fir::RecordType derived, llvm::SmallVectorImpl<mlir::Type> &results) {
84 return convertRecordType(derived, results);
85 });
86 addConversion(
87 [&](fir::RealType real) { return convertRealType(real.getFKind()); });
88 addConversion(
89 [&](fir::ReferenceType ref) { return convertPointerLike(ref); });
90 addConversion([&](fir::SequenceType sequence) {
91 return convertSequenceType(sequence);
92 });
93 addConversion([&](fir::TypeDescType tdesc) {
94 return convertTypeDescType(tdesc.getContext());
95 });
96 addConversion([&](fir::VectorType vecTy) {
97 return mlir::VectorType::get(llvm::ArrayRef<int64_t>(vecTy.getLen()),
98 convertType(vecTy.getEleTy()));
99 });
100 addConversion([&](mlir::TupleType tuple) {
101 LLVM_DEBUG(llvm::dbgs() << "type convert: " << tuple << '\n');
102 llvm::SmallVector<mlir::Type> members;
103 for (auto mem : tuple.getTypes()) {
104 // Prevent fir.box from degenerating to a pointer to a descriptor in the
105 // context of a tuple type.
106 if (auto box = mem.dyn_cast<fir::BaseBoxType>())
107 members.push_back(convertBoxTypeAsStruct(box));
108 else
109 members.push_back(convertType(mem).cast<mlir::Type>());
110 }
111 return mlir::LLVM::LLVMStructType::getLiteral(&getContext(), members,
112 /*isPacked=*/false);
113 });
114 addConversion([&](mlir::NoneType none) {
115 return mlir::LLVM::LLVMStructType::getLiteral(
116 none.getContext(), std::nullopt, /*isPacked=*/false);
117 });
118 // FIXME: https://reviews.llvm.org/D82831 introduced an automatic
119 // materialization of conversion around function calls that is not working
120 // well with fir lowering to llvm (incorrect llvm.mlir.cast are inserted).
121 // Workaround until better analysis: register a handler that does not insert
122 // any conversions.
123 addSourceMaterialization(
124 [&](mlir::OpBuilder &builder, mlir::Type resultType,
125 mlir::ValueRange inputs,
126 mlir::Location loc) -> std::optional<mlir::Value> {
127 if (inputs.size() != 1)
128 return std::nullopt;
129 return inputs[0];
130 });
131 // Similar FIXME workaround here (needed for compare.fir/select-type.fir
132 // as well as rebox-global.fir tests). This is needed to cope with the
133 // the fact that codegen does not lower some operation results to the LLVM
134 // type produced by this LLVMTypeConverter. For instance, inside FIR
135 // globals, fir.box are lowered to llvm.struct, while the fir.box type
136 // conversion translates it into an llvm.ptr<llvm.struct<>> because
137 // descriptors are manipulated in memory outside of global initializers
138 // where this is not possible. Hence, MLIR inserts
139 // builtin.unrealized_conversion_cast after the translation of operations
140 // producing fir.box in fir.global codegen. addSourceMaterialization and
141 // addTargetMaterialization allow ignoring these ops and removing them
142 // after codegen assuming the type discrepencies are intended (like for
143 // fir.box inside globals).
144 addTargetMaterialization(
145 [&](mlir::OpBuilder &builder, mlir::Type resultType,
146 mlir::ValueRange inputs,
147 mlir::Location loc) -> std::optional<mlir::Value> {
148 if (inputs.size() != 1)
149 return std::nullopt;
150 return inputs[0];
151 });
152}
153
154// i32 is used here because LLVM wants i32 constants when indexing into struct
155// types. Indexing into other aggregate types is more flexible.
156mlir::Type LLVMTypeConverter::offsetType() const {
157 return mlir::IntegerType::get(&getContext(), 32);
158}
159
160// i64 can be used to index into aggregates like arrays
161mlir::Type LLVMTypeConverter::indexType() const {
162 return mlir::IntegerType::get(&getContext(), 64);
163}
164
165// fir.type<name(p : TY'...){f : TY...}> --> llvm<"%name = { ty... }">
166std::optional<mlir::LogicalResult> LLVMTypeConverter::convertRecordType(
167 fir::RecordType derived, llvm::SmallVectorImpl<mlir::Type> &results) {
168 auto name = fir::NameUniquer::dropTypeConversionMarkers(derived.getName());
169 auto st = mlir::LLVM::LLVMStructType::getIdentified(&getContext(), name);
170
171 auto &callStack = getCurrentThreadRecursiveStack();
172 if (llvm::count(callStack, derived)) {
173 results.push_back(st);
174 return mlir::success();
175 }
176 callStack.push_back(derived);
177 auto popConversionCallStack =
178 llvm::make_scope_exit([&callStack]() { callStack.pop_back(); });
179
180 llvm::SmallVector<mlir::Type> members;
181 for (auto mem : derived.getTypeList()) {
182 // Prevent fir.box from degenerating to a pointer to a descriptor in the
183 // context of a record type.
184 if (auto box = mem.second.dyn_cast<fir::BaseBoxType>())
185 members.push_back(convertBoxTypeAsStruct(box));
186 else
187 members.push_back(convertType(mem.second).cast<mlir::Type>());
188 }
189 if (mlir::failed(st.setBody(members, /*isPacked=*/false)))
190 return mlir::failure();
191 results.push_back(st);
192 return mlir::success();
193}
194
195// Is an extended descriptor needed given the element type of a fir.box type ?
196// Extended descriptors are required for derived types.
197bool LLVMTypeConverter::requiresExtendedDesc(mlir::Type boxElementType) const {
198 auto eleTy = fir::unwrapSequenceType(boxElementType);
199 return eleTy.isa<fir::RecordType>();
200}
201
202// This corresponds to the descriptor as defined in ISO_Fortran_binding.h and
203// the addendum defined in descriptor.h.
204mlir::Type LLVMTypeConverter::convertBoxTypeAsStruct(BaseBoxType box,
205 int rank) const {
206 // (base_addr*, elem_len, version, rank, type, attribute, f18Addendum, [dim]
207 llvm::SmallVector<mlir::Type> dataDescFields;
208 mlir::Type ele = box.getEleTy();
209 // remove fir.heap/fir.ref/fir.ptr
210 if (auto removeIndirection = fir::dyn_cast_ptrEleTy(ele))
211 ele = removeIndirection;
212 auto eleTy = convertType(ele);
213 // base_addr*
214 if (ele.isa<SequenceType>() && eleTy.isa<mlir::LLVM::LLVMPointerType>())
215 dataDescFields.push_back(eleTy);
216 else
217 dataDescFields.push_back(
218 mlir::LLVM::LLVMPointerType::get(eleTy.getContext()));
219 // elem_len
220 dataDescFields.push_back(
221 getDescFieldTypeModel<kElemLenPosInBox>()(&getContext()));
222 // version
223 dataDescFields.push_back(
224 getDescFieldTypeModel<kVersionPosInBox>()(&getContext()));
225 // rank
226 dataDescFields.push_back(
227 getDescFieldTypeModel<kRankPosInBox>()(&getContext()));
228 // type
229 dataDescFields.push_back(
230 getDescFieldTypeModel<kTypePosInBox>()(&getContext()));
231 // attribute
232 dataDescFields.push_back(
233 getDescFieldTypeModel<kAttributePosInBox>()(&getContext()));
234 // f18Addendum
235 dataDescFields.push_back(
236 getDescFieldTypeModel<kF18AddendumPosInBox>()(&getContext()));
237 // [dims]
238 if (rank == unknownRank()) {
239 if (auto seqTy = ele.dyn_cast<SequenceType>())
240 rank = seqTy.getDimension();
241 else
242 rank = 0;
243 }
244 if (rank > 0) {
245 auto rowTy = getDescFieldTypeModel<kDimsPosInBox>()(&getContext());
246 dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, rank));
247 }
248 // opt-type-ptr: i8* (see fir.tdesc)
249 if (requiresExtendedDesc(ele) || fir::isUnlimitedPolymorphicType(box)) {
250 dataDescFields.push_back(
251 getExtendedDescFieldTypeModel<kOptTypePtrPosInBox>()(&getContext()));
252 auto rowTy =
253 getExtendedDescFieldTypeModel<kOptRowTypePosInBox>()(&getContext());
254 dataDescFields.push_back(mlir::LLVM::LLVMArrayType::get(rowTy, 1));
255 if (auto recTy = fir::unwrapSequenceType(ele).dyn_cast<fir::RecordType>())
256 if (recTy.getNumLenParams() > 0) {
257 // The descriptor design needs to be clarified regarding the number of
258 // length parameters in the addendum. Since it can change for
259 // polymorphic allocatables, it seems all length parameters cannot
260 // always possibly be placed in the addendum.
261 TODO_NOLOC("extended descriptor derived with length parameters");
262 unsigned numLenParams = recTy.getNumLenParams();
263 dataDescFields.push_back(
264 mlir::LLVM::LLVMArrayType::get(rowTy, numLenParams));
265 }
266 }
267 return mlir::LLVM::LLVMStructType::getLiteral(&getContext(), dataDescFields,
268 /*isPacked=*/false);
269}
270
271/// Convert fir.box type to the corresponding llvm struct type instead of a
272/// pointer to this struct type.
273mlir::Type LLVMTypeConverter::convertBoxType(BaseBoxType box, int rank) const {
274 // TODO: send the box type and the converted LLVM structure layout
275 // to tbaaBuilder for proper creation of TBAATypeDescriptorOp.
276 return mlir::LLVM::LLVMPointerType::get(box.getContext());
277}
278
279// fir.boxproc<any> --> llvm<"{ any*, i8* }">
280mlir::Type LLVMTypeConverter::convertBoxProcType(BoxProcType boxproc) const {
281 auto funcTy = convertType(boxproc.getEleTy());
282 auto voidPtrTy = mlir::LLVM::LLVMPointerType::get(boxproc.getContext());
283 llvm::SmallVector<mlir::Type, 2> tuple = {funcTy, voidPtrTy};
284 return mlir::LLVM::LLVMStructType::getLiteral(boxproc.getContext(), tuple,
285 /*isPacked=*/false);
286}
287
288unsigned LLVMTypeConverter::characterBitsize(fir::CharacterType charTy) const {
289 return kindMapping.getCharacterBitsize(charTy.getFKind());
290}
291
292// fir.char<k,?> --> llvm<"ix"> where ix is scaled by kind mapping
293// fir.char<k,n> --> llvm.array<n x "ix">
294mlir::Type LLVMTypeConverter::convertCharType(fir::CharacterType charTy) const {
295 auto iTy = mlir::IntegerType::get(&getContext(), characterBitsize(charTy));
296 if (charTy.getLen() == fir::CharacterType::unknownLen())
297 return iTy;
298 return mlir::LLVM::LLVMArrayType::get(iTy, charTy.getLen());
299}
300
301// convert a front-end kind value to either a std or LLVM IR dialect type
302// fir.real<n> --> llvm.anyfloat where anyfloat is a kind mapping
303mlir::Type LLVMTypeConverter::convertRealType(fir::KindTy kind) const {
304 return fir::fromRealTypeID(&getContext(), kindMapping.getRealTypeID(kind),
305 kind);
306}
307
308// fir.array<c ... :any> --> llvm<"[...[c x any]]">
309mlir::Type LLVMTypeConverter::convertSequenceType(SequenceType seq) const {
310 auto baseTy = convertType(seq.getEleTy());
311 if (characterWithDynamicLen(seq.getEleTy()))
312 return baseTy;
313 auto shape = seq.getShape();
314 auto constRows = seq.getConstantRows();
315 if (constRows) {
316 decltype(constRows) i = constRows;
317 for (auto e : shape) {
318 baseTy = mlir::LLVM::LLVMArrayType::get(baseTy, e);
319 if (--i == 0)
320 break;
321 }
322 if (!seq.hasDynamicExtents())
323 return baseTy;
324 }
325 return baseTy;
326}
327
328// fir.tdesc<any> --> llvm<"i8*">
329// TODO: For now use a void*, however pointer identity is not sufficient for
330// the f18 object v. class distinction (F2003).
331mlir::Type
332LLVMTypeConverter::convertTypeDescType(mlir::MLIRContext *ctx) const {
333 return mlir::LLVM::LLVMPointerType::get(ctx);
334}
335
336// Relay TBAA tag attachment to TBAABuilder.
337void LLVMTypeConverter::attachTBAATag(mlir::LLVM::AliasAnalysisOpInterface op,
338 mlir::Type baseFIRType,
339 mlir::Type accessFIRType,
340 mlir::LLVM::GEPOp gep) const {
341 tbaaBuilder->attachTBAATag(op, baseFIRType, accessFIRType, gep);
342}
343
344} // namespace fir
345

source code of flang/lib/Optimizer/CodeGen/TypeConverter.cpp