1 //===-- Target.cpp --------------------------------------------------------===// 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 #include "Target.h" 14 #include "flang/Optimizer/Builder/Todo.h" 15 #include "flang/Optimizer/Dialect/FIRType.h" 16 #include "flang/Optimizer/Support/FatalError.h" 17 #include "flang/Optimizer/Support/KindMapping.h" 18 #include "mlir/IR/BuiltinTypes.h" 19 #include "mlir/IR/TypeRange.h" 20 21 #define DEBUG_TYPE "flang-codegen-target" 22 23 using namespace fir; 24 25 // Reduce a REAL/float type to the floating point semantics. 26 static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, 27 mlir::Type type) { 28 assert(isa_real(type)); 29 if (auto ty = type.dyn_cast<fir::RealType>()) 30 return kindMap.getFloatSemantics(ty.getFKind()); 31 return type.cast<mlir::FloatType>().getFloatSemantics(); 32 } 33 34 namespace { 35 template <typename S> 36 struct GenericTarget : public CodeGenSpecifics { 37 using CodeGenSpecifics::CodeGenSpecifics; 38 using AT = CodeGenSpecifics::Attributes; 39 40 mlir::Type complexMemoryType(mlir::Type eleTy) const override { 41 assert(fir::isa_real(eleTy)); 42 // Use a type that will be translated into LLVM as: 43 // { t, t } struct of 2 eleTy 44 mlir::TypeRange range = {eleTy, eleTy}; 45 return mlir::TupleType::get(eleTy.getContext(), range); 46 } 47 48 mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { 49 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 50 auto ptrTy = fir::ReferenceType::get(eleTy); 51 // Use a type that will be translated into LLVM as: 52 // { t*, index } 53 mlir::TypeRange range = {ptrTy, idxTy}; 54 return mlir::TupleType::get(eleTy.getContext(), range); 55 } 56 57 Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { 58 CodeGenSpecifics::Marshalling marshal; 59 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 60 auto ptrTy = fir::ReferenceType::get(eleTy); 61 marshal.emplace_back(ptrTy, AT{}); 62 // Return value arguments are grouped as a pair. Others are passed in a 63 // split format with all pointers first (in the declared position) and all 64 // LEN arguments appended after all of the dummy arguments. 65 // NB: Other conventions/ABIs can/should be supported via options. 66 marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, 67 /*sret=*/sret, /*append=*/!sret}); 68 return marshal; 69 } 70 }; 71 } // namespace 72 73 //===----------------------------------------------------------------------===// 74 // i386 (x86 32 bit) linux target specifics. 75 //===----------------------------------------------------------------------===// 76 77 namespace { 78 struct TargetI386 : public GenericTarget<TargetI386> { 79 using GenericTarget::GenericTarget; 80 81 static constexpr int defaultWidth = 32; 82 83 CodeGenSpecifics::Marshalling 84 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 85 assert(fir::isa_real(eleTy)); 86 CodeGenSpecifics::Marshalling marshal; 87 // Use a type that will be translated into LLVM as: 88 // { t, t } struct of 2 eleTy, byval, align 4 89 mlir::TypeRange range = {eleTy, eleTy}; 90 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 91 marshal.emplace_back(fir::ReferenceType::get(structTy), 92 AT{/*alignment=*/4, /*byval=*/true}); 93 return marshal; 94 } 95 96 CodeGenSpecifics::Marshalling 97 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 98 assert(fir::isa_real(eleTy)); 99 CodeGenSpecifics::Marshalling marshal; 100 const auto *sem = &floatToSemantics(kindMap, eleTy); 101 if (sem == &llvm::APFloat::IEEEsingle()) { 102 // i64 pack both floats in a 64-bit GPR 103 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 104 AT{}); 105 } else if (sem == &llvm::APFloat::IEEEdouble()) { 106 // Use a type that will be translated into LLVM as: 107 // { t, t } struct of 2 eleTy, sret, align 4 108 mlir::TypeRange range = {eleTy, eleTy}; 109 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 110 marshal.emplace_back(fir::ReferenceType::get(structTy), 111 AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); 112 } else { 113 TODO(loc, "complex for this precision"); 114 } 115 return marshal; 116 } 117 }; 118 } // namespace 119 120 //===----------------------------------------------------------------------===// 121 // x86_64 (x86 64 bit) linux target specifics. 122 //===----------------------------------------------------------------------===// 123 124 namespace { 125 struct TargetX86_64 : public GenericTarget<TargetX86_64> { 126 using GenericTarget::GenericTarget; 127 128 static constexpr int defaultWidth = 64; 129 130 CodeGenSpecifics::Marshalling 131 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 132 CodeGenSpecifics::Marshalling marshal; 133 const auto *sem = &floatToSemantics(kindMap, eleTy); 134 if (sem == &llvm::APFloat::IEEEsingle()) { 135 // <2 x t> vector of 2 eleTy 136 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 137 } else if (sem == &llvm::APFloat::IEEEdouble()) { 138 // two distinct double arguments 139 marshal.emplace_back(eleTy, AT{}); 140 marshal.emplace_back(eleTy, AT{}); 141 } else if (sem == &llvm::APFloat::IEEEquad()) { 142 // Use a type that will be translated into LLVM as: 143 // { fp128, fp128 } struct of 2 fp128, byval, align 16 144 mlir::TypeRange range = {eleTy, eleTy}; 145 marshal.emplace_back(fir::ReferenceType::get( 146 mlir::TupleType::get(eleTy.getContext(), range)), 147 AT{/*align=*/16, /*byval=*/true}); 148 } else { 149 TODO(loc, "complex for this precision"); 150 } 151 return marshal; 152 } 153 154 CodeGenSpecifics::Marshalling 155 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 156 CodeGenSpecifics::Marshalling marshal; 157 const auto *sem = &floatToSemantics(kindMap, eleTy); 158 if (sem == &llvm::APFloat::IEEEsingle()) { 159 // <2 x t> vector of 2 eleTy 160 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 161 } else if (sem == &llvm::APFloat::IEEEdouble()) { 162 // Use a type that will be translated into LLVM as: 163 // { double, double } struct of 2 double 164 mlir::TypeRange range = {eleTy, eleTy}; 165 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 166 AT{}); 167 } else if (sem == &llvm::APFloat::IEEEquad()) { 168 // Use a type that will be translated into LLVM as: 169 // { fp128, fp128 } struct of 2 fp128, sret, align 16 170 mlir::TypeRange range = {eleTy, eleTy}; 171 marshal.emplace_back(fir::ReferenceType::get( 172 mlir::TupleType::get(eleTy.getContext(), range)), 173 AT{/*align=*/16, /*byval=*/false, /*sret=*/true}); 174 } else { 175 TODO(loc, "complex for this precision"); 176 } 177 return marshal; 178 } 179 }; 180 } // namespace 181 182 //===----------------------------------------------------------------------===// 183 // AArch64 linux target specifics. 184 //===----------------------------------------------------------------------===// 185 186 namespace { 187 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 188 using GenericTarget::GenericTarget; 189 190 static constexpr int defaultWidth = 64; 191 192 CodeGenSpecifics::Marshalling 193 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 194 CodeGenSpecifics::Marshalling marshal; 195 const auto *sem = &floatToSemantics(kindMap, eleTy); 196 if (sem == &llvm::APFloat::IEEEsingle() || 197 sem == &llvm::APFloat::IEEEdouble()) { 198 // [2 x t] array of 2 eleTy 199 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 200 } else { 201 TODO(loc, "complex for this precision"); 202 } 203 return marshal; 204 } 205 206 CodeGenSpecifics::Marshalling 207 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 208 CodeGenSpecifics::Marshalling marshal; 209 const auto *sem = &floatToSemantics(kindMap, eleTy); 210 if (sem == &llvm::APFloat::IEEEsingle() || 211 sem == &llvm::APFloat::IEEEdouble()) { 212 // Use a type that will be translated into LLVM as: 213 // { t, t } struct of 2 eleTy 214 mlir::TypeRange range = {eleTy, eleTy}; 215 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 216 AT{}); 217 } else { 218 TODO(loc, "complex for this precision"); 219 } 220 return marshal; 221 } 222 }; 223 } // namespace 224 225 //===----------------------------------------------------------------------===// 226 // PPC64le linux target specifics. 227 //===----------------------------------------------------------------------===// 228 229 namespace { 230 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 231 using GenericTarget::GenericTarget; 232 233 static constexpr int defaultWidth = 64; 234 235 CodeGenSpecifics::Marshalling 236 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 237 CodeGenSpecifics::Marshalling marshal; 238 // two distinct element type arguments (re, im) 239 marshal.emplace_back(eleTy, AT{}); 240 marshal.emplace_back(eleTy, AT{}); 241 return marshal; 242 } 243 244 CodeGenSpecifics::Marshalling 245 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 246 CodeGenSpecifics::Marshalling marshal; 247 // Use a type that will be translated into LLVM as: 248 // { t, t } struct of 2 element type 249 mlir::TypeRange range = {eleTy, eleTy}; 250 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 251 return marshal; 252 } 253 }; 254 } // namespace 255 256 //===----------------------------------------------------------------------===// 257 // sparc (sparc 32 bit) target specifics. 258 //===----------------------------------------------------------------------===// 259 260 namespace { 261 struct TargetSparc : public GenericTarget<TargetSparc> { 262 using GenericTarget::GenericTarget; 263 264 static constexpr int defaultWidth = 32; 265 266 CodeGenSpecifics::Marshalling 267 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 268 assert(fir::isa_real(eleTy)); 269 CodeGenSpecifics::Marshalling marshal; 270 // Use a type that will be translated into LLVM as: 271 // { t, t } struct of 2 eleTy 272 mlir::TypeRange range = {eleTy, eleTy}; 273 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 274 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); 275 return marshal; 276 } 277 278 CodeGenSpecifics::Marshalling 279 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 280 assert(fir::isa_real(eleTy)); 281 CodeGenSpecifics::Marshalling marshal; 282 // Use a type that will be translated into LLVM as: 283 // { t, t } struct of 2 eleTy, byval 284 mlir::TypeRange range = {eleTy, eleTy}; 285 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 286 marshal.emplace_back(fir::ReferenceType::get(structTy), 287 AT{/*alignment=*/0, /*byval=*/true}); 288 return marshal; 289 } 290 }; 291 } // namespace 292 293 //===----------------------------------------------------------------------===// 294 // sparcv9 (sparc 64 bit) target specifics. 295 //===----------------------------------------------------------------------===// 296 297 namespace { 298 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { 299 using GenericTarget::GenericTarget; 300 301 static constexpr int defaultWidth = 64; 302 303 CodeGenSpecifics::Marshalling 304 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 305 CodeGenSpecifics::Marshalling marshal; 306 const auto *sem = &floatToSemantics(kindMap, eleTy); 307 if (sem == &llvm::APFloat::IEEEsingle() || 308 sem == &llvm::APFloat::IEEEdouble()) { 309 // two distinct float, double arguments 310 marshal.emplace_back(eleTy, AT{}); 311 marshal.emplace_back(eleTy, AT{}); 312 } else if (sem == &llvm::APFloat::IEEEquad()) { 313 // Use a type that will be translated into LLVM as: 314 // { fp128, fp128 } struct of 2 fp128, byval, align 16 315 mlir::TypeRange range = {eleTy, eleTy}; 316 marshal.emplace_back(fir::ReferenceType::get( 317 mlir::TupleType::get(eleTy.getContext(), range)), 318 AT{/*align=*/16, /*byval=*/true}); 319 } else { 320 TODO(loc, "complex for this precision"); 321 } 322 return marshal; 323 } 324 325 CodeGenSpecifics::Marshalling 326 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 327 CodeGenSpecifics::Marshalling marshal; 328 // Use a type that will be translated into LLVM as: 329 // { eleTy, eleTy } struct of 2 eleTy 330 mlir::TypeRange range = {eleTy, eleTy}; 331 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 332 return marshal; 333 } 334 }; 335 } // namespace 336 337 //===----------------------------------------------------------------------===// 338 // RISCV64 linux target specifics. 339 //===----------------------------------------------------------------------===// 340 341 namespace { 342 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { 343 using GenericTarget::GenericTarget; 344 345 static constexpr int defaultWidth = 64; 346 347 CodeGenSpecifics::Marshalling 348 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 349 CodeGenSpecifics::Marshalling marshal; 350 const auto *sem = &floatToSemantics(kindMap, eleTy); 351 if (sem == &llvm::APFloat::IEEEsingle() || 352 sem == &llvm::APFloat::IEEEdouble()) { 353 // Two distinct element type arguments (re, im) 354 marshal.emplace_back(eleTy, AT{}); 355 marshal.emplace_back(eleTy, AT{}); 356 } else { 357 TODO(loc, "complex for this precision"); 358 } 359 return marshal; 360 } 361 362 CodeGenSpecifics::Marshalling 363 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 364 CodeGenSpecifics::Marshalling marshal; 365 const auto *sem = &floatToSemantics(kindMap, eleTy); 366 if (sem == &llvm::APFloat::IEEEsingle() || 367 sem == &llvm::APFloat::IEEEdouble()) { 368 // Use a type that will be translated into LLVM as: 369 // { t, t } struct of 2 eleTy, byVal 370 mlir::TypeRange range = {eleTy, eleTy}; 371 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 372 AT{/*alignment=*/0, /*byval=*/true}); 373 } else { 374 TODO(loc, "complex for this precision"); 375 } 376 return marshal; 377 } 378 }; 379 } // namespace 380 381 // Instantiate the overloaded target instance based on the triple value. 382 // TODO: Add other targets to this file as needed. 383 std::unique_ptr<fir::CodeGenSpecifics> 384 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 385 KindMapping &&kindMap) { 386 switch (trp.getArch()) { 387 default: 388 break; 389 case llvm::Triple::ArchType::x86: 390 return std::make_unique<TargetI386>(ctx, std::move(trp), 391 std::move(kindMap)); 392 case llvm::Triple::ArchType::x86_64: 393 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 394 std::move(kindMap)); 395 case llvm::Triple::ArchType::aarch64: 396 return std::make_unique<TargetAArch64>(ctx, std::move(trp), 397 std::move(kindMap)); 398 case llvm::Triple::ArchType::ppc64le: 399 return std::make_unique<TargetPPC64le>(ctx, std::move(trp), 400 std::move(kindMap)); 401 case llvm::Triple::ArchType::sparc: 402 return std::make_unique<TargetSparc>(ctx, std::move(trp), 403 std::move(kindMap)); 404 case llvm::Triple::ArchType::sparcv9: 405 return std::make_unique<TargetSparcV9>(ctx, std::move(trp), 406 std::move(kindMap)); 407 case llvm::Triple::ArchType::riscv64: 408 return std::make_unique<TargetRISCV64>(ctx, std::move(trp), 409 std::move(kindMap)); 410 } 411 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 412 } 413