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