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 // PPC64 (AIX 64 bit) target specifics. 227 //===----------------------------------------------------------------------===// 228 229 namespace { 230 struct TargetPPC64 : public GenericTarget<TargetPPC64> { 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 // PPC64le linux target specifics. 258 //===----------------------------------------------------------------------===// 259 260 namespace { 261 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 262 using GenericTarget::GenericTarget; 263 264 static constexpr int defaultWidth = 64; 265 266 CodeGenSpecifics::Marshalling 267 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 268 CodeGenSpecifics::Marshalling marshal; 269 // two distinct element type arguments (re, im) 270 marshal.emplace_back(eleTy, AT{}); 271 marshal.emplace_back(eleTy, AT{}); 272 return marshal; 273 } 274 275 CodeGenSpecifics::Marshalling 276 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 277 CodeGenSpecifics::Marshalling marshal; 278 // Use a type that will be translated into LLVM as: 279 // { t, t } struct of 2 element type 280 mlir::TypeRange range = {eleTy, eleTy}; 281 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 282 return marshal; 283 } 284 }; 285 } // namespace 286 287 //===----------------------------------------------------------------------===// 288 // sparc (sparc 32 bit) target specifics. 289 //===----------------------------------------------------------------------===// 290 291 namespace { 292 struct TargetSparc : public GenericTarget<TargetSparc> { 293 using GenericTarget::GenericTarget; 294 295 static constexpr int defaultWidth = 32; 296 297 CodeGenSpecifics::Marshalling 298 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 299 assert(fir::isa_real(eleTy)); 300 CodeGenSpecifics::Marshalling marshal; 301 // Use a type that will be translated into LLVM as: 302 // { t, t } struct of 2 eleTy 303 mlir::TypeRange range = {eleTy, eleTy}; 304 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 305 marshal.emplace_back(fir::ReferenceType::get(structTy), AT{}); 306 return marshal; 307 } 308 309 CodeGenSpecifics::Marshalling 310 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 311 assert(fir::isa_real(eleTy)); 312 CodeGenSpecifics::Marshalling marshal; 313 // Use a type that will be translated into LLVM as: 314 // { t, t } struct of 2 eleTy, byval 315 mlir::TypeRange range = {eleTy, eleTy}; 316 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 317 marshal.emplace_back(fir::ReferenceType::get(structTy), 318 AT{/*alignment=*/0, /*byval=*/true}); 319 return marshal; 320 } 321 }; 322 } // namespace 323 324 //===----------------------------------------------------------------------===// 325 // sparcv9 (sparc 64 bit) target specifics. 326 //===----------------------------------------------------------------------===// 327 328 namespace { 329 struct TargetSparcV9 : public GenericTarget<TargetSparcV9> { 330 using GenericTarget::GenericTarget; 331 332 static constexpr int defaultWidth = 64; 333 334 CodeGenSpecifics::Marshalling 335 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 336 CodeGenSpecifics::Marshalling marshal; 337 const auto *sem = &floatToSemantics(kindMap, eleTy); 338 if (sem == &llvm::APFloat::IEEEsingle() || 339 sem == &llvm::APFloat::IEEEdouble()) { 340 // two distinct float, double arguments 341 marshal.emplace_back(eleTy, AT{}); 342 marshal.emplace_back(eleTy, AT{}); 343 } else if (sem == &llvm::APFloat::IEEEquad()) { 344 // Use a type that will be translated into LLVM as: 345 // { fp128, fp128 } struct of 2 fp128, byval, align 16 346 mlir::TypeRange range = {eleTy, eleTy}; 347 marshal.emplace_back(fir::ReferenceType::get( 348 mlir::TupleType::get(eleTy.getContext(), range)), 349 AT{/*align=*/16, /*byval=*/true}); 350 } else { 351 TODO(loc, "complex for this precision"); 352 } 353 return marshal; 354 } 355 356 CodeGenSpecifics::Marshalling 357 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 358 CodeGenSpecifics::Marshalling marshal; 359 // Use a type that will be translated into LLVM as: 360 // { eleTy, eleTy } struct of 2 eleTy 361 mlir::TypeRange range = {eleTy, eleTy}; 362 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 363 return marshal; 364 } 365 }; 366 } // namespace 367 368 //===----------------------------------------------------------------------===// 369 // RISCV64 linux target specifics. 370 //===----------------------------------------------------------------------===// 371 372 namespace { 373 struct TargetRISCV64 : public GenericTarget<TargetRISCV64> { 374 using GenericTarget::GenericTarget; 375 376 static constexpr int defaultWidth = 64; 377 378 CodeGenSpecifics::Marshalling 379 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 380 CodeGenSpecifics::Marshalling marshal; 381 const auto *sem = &floatToSemantics(kindMap, eleTy); 382 if (sem == &llvm::APFloat::IEEEsingle() || 383 sem == &llvm::APFloat::IEEEdouble()) { 384 // Two distinct element type arguments (re, im) 385 marshal.emplace_back(eleTy, AT{}); 386 marshal.emplace_back(eleTy, AT{}); 387 } else { 388 TODO(loc, "complex for this precision"); 389 } 390 return marshal; 391 } 392 393 CodeGenSpecifics::Marshalling 394 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 395 CodeGenSpecifics::Marshalling marshal; 396 const auto *sem = &floatToSemantics(kindMap, eleTy); 397 if (sem == &llvm::APFloat::IEEEsingle() || 398 sem == &llvm::APFloat::IEEEdouble()) { 399 // Use a type that will be translated into LLVM as: 400 // { t, t } struct of 2 eleTy, byVal 401 mlir::TypeRange range = {eleTy, eleTy}; 402 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 403 AT{/*alignment=*/0, /*byval=*/true}); 404 } else { 405 TODO(loc, "complex for this precision"); 406 } 407 return marshal; 408 } 409 }; 410 } // namespace 411 412 // Instantiate the overloaded target instance based on the triple value. 413 // TODO: Add other targets to this file as needed. 414 std::unique_ptr<fir::CodeGenSpecifics> 415 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 416 KindMapping &&kindMap) { 417 switch (trp.getArch()) { 418 default: 419 break; 420 case llvm::Triple::ArchType::x86: 421 return std::make_unique<TargetI386>(ctx, std::move(trp), 422 std::move(kindMap)); 423 case llvm::Triple::ArchType::x86_64: 424 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 425 std::move(kindMap)); 426 case llvm::Triple::ArchType::aarch64: 427 return std::make_unique<TargetAArch64>(ctx, std::move(trp), 428 std::move(kindMap)); 429 case llvm::Triple::ArchType::ppc64: 430 return std::make_unique<TargetPPC64>(ctx, std::move(trp), 431 std::move(kindMap)); 432 case llvm::Triple::ArchType::ppc64le: 433 return std::make_unique<TargetPPC64le>(ctx, std::move(trp), 434 std::move(kindMap)); 435 case llvm::Triple::ArchType::sparc: 436 return std::make_unique<TargetSparc>(ctx, std::move(trp), 437 std::move(kindMap)); 438 case llvm::Triple::ArchType::sparcv9: 439 return std::make_unique<TargetSparcV9>(ctx, std::move(trp), 440 std::move(kindMap)); 441 case llvm::Triple::ArchType::riscv64: 442 return std::make_unique<TargetRISCV64>(ctx, std::move(trp), 443 std::move(kindMap)); 444 } 445 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 446 } 447