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 | |
28 | namespace fir { |
29 | |
30 | LLVMTypeConverter::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. |
156 | mlir::Type LLVMTypeConverter::offsetType() const { |
157 | return mlir::IntegerType::get(&getContext(), 32); |
158 | } |
159 | |
160 | // i64 can be used to index into aggregates like arrays |
161 | mlir::Type LLVMTypeConverter::indexType() const { |
162 | return mlir::IntegerType::get(&getContext(), 64); |
163 | } |
164 | |
165 | // fir.type<name(p : TY'...){f : TY...}> --> llvm<"%name = { ty... }"> |
166 | std::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. |
197 | bool 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. |
204 | mlir::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. |
273 | mlir::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* }"> |
280 | mlir::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 | |
288 | unsigned 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"> |
294 | mlir::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 |
303 | mlir::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]]"> |
309 | mlir::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). |
331 | mlir::Type |
332 | LLVMTypeConverter::convertTypeDescType(mlir::MLIRContext *ctx) const { |
333 | return mlir::LLVM::LLVMPointerType::get(ctx); |
334 | } |
335 | |
336 | // Relay TBAA tag attachment to TBAABuilder. |
337 | void 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 | |