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