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 // Instantiate the overloaded target instance based on the triple value. 257 // TODO: Add other targets to this file as needed. 258 std::unique_ptr<fir::CodeGenSpecifics> 259 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 260 KindMapping &&kindMap) { 261 switch (trp.getArch()) { 262 default: 263 break; 264 case llvm::Triple::ArchType::x86: 265 switch (trp.getOS()) { 266 default: 267 break; 268 case llvm::Triple::OSType::Linux: 269 case llvm::Triple::OSType::Darwin: 270 case llvm::Triple::OSType::MacOSX: 271 case llvm::Triple::OSType::Solaris: 272 case llvm::Triple::OSType::Win32: 273 return std::make_unique<TargetI386>(ctx, std::move(trp), 274 std::move(kindMap)); 275 } 276 break; 277 case llvm::Triple::ArchType::x86_64: 278 switch (trp.getOS()) { 279 default: 280 break; 281 case llvm::Triple::OSType::Linux: 282 case llvm::Triple::OSType::Darwin: 283 case llvm::Triple::OSType::MacOSX: 284 case llvm::Triple::OSType::Solaris: 285 case llvm::Triple::OSType::Win32: 286 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 287 std::move(kindMap)); 288 } 289 break; 290 case llvm::Triple::ArchType::aarch64: 291 switch (trp.getOS()) { 292 default: 293 break; 294 case llvm::Triple::OSType::Linux: 295 case llvm::Triple::OSType::Darwin: 296 case llvm::Triple::OSType::MacOSX: 297 case llvm::Triple::OSType::Win32: 298 return std::make_unique<TargetAArch64>(ctx, std::move(trp), 299 std::move(kindMap)); 300 } 301 break; 302 case llvm::Triple::ArchType::ppc64le: 303 switch (trp.getOS()) { 304 default: 305 break; 306 case llvm::Triple::OSType::Linux: 307 return std::make_unique<TargetPPC64le>(ctx, std::move(trp), 308 std::move(kindMap)); 309 } 310 break; 311 } 312 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 313 } 314