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 { 142 TODO(loc, "complex for this precision"); 143 } 144 return marshal; 145 } 146 147 CodeGenSpecifics::Marshalling 148 complexReturnType(mlir::Location loc, mlir::Type eleTy) const override { 149 CodeGenSpecifics::Marshalling marshal; 150 const auto *sem = &floatToSemantics(kindMap, eleTy); 151 if (sem == &llvm::APFloat::IEEEsingle()) { 152 // <2 x t> vector of 2 eleTy 153 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 154 } else if (sem == &llvm::APFloat::IEEEdouble()) { 155 // Use a type that will be translated into LLVM as: 156 // { double, double } struct of 2 double 157 mlir::TypeRange range = {eleTy, eleTy}; 158 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 159 AT{}); 160 } else { 161 TODO(loc, "complex for this precision"); 162 } 163 return marshal; 164 } 165 }; 166 } // namespace 167 168 //===----------------------------------------------------------------------===// 169 // AArch64 linux target specifics. 170 //===----------------------------------------------------------------------===// 171 172 namespace { 173 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 174 using GenericTarget::GenericTarget; 175 176 static constexpr int defaultWidth = 64; 177 178 CodeGenSpecifics::Marshalling 179 complexArgumentType(mlir::Location loc, mlir::Type eleTy) const override { 180 CodeGenSpecifics::Marshalling marshal; 181 const auto *sem = &floatToSemantics(kindMap, eleTy); 182 if (sem == &llvm::APFloat::IEEEsingle() || 183 sem == &llvm::APFloat::IEEEdouble()) { 184 // [2 x t] array of 2 eleTy 185 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 186 } else { 187 TODO(loc, "complex for this precision"); 188 } 189 return marshal; 190 } 191 192 CodeGenSpecifics::Marshalling 193 complexReturnType(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 // Use a type that will be translated into LLVM as: 199 // { t, t } struct of 2 eleTy 200 mlir::TypeRange range = {eleTy, eleTy}; 201 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 202 AT{}); 203 } else { 204 TODO(loc, "complex for this precision"); 205 } 206 return marshal; 207 } 208 }; 209 } // namespace 210 211 //===----------------------------------------------------------------------===// 212 // PPC64le linux target specifics. 213 //===----------------------------------------------------------------------===// 214 215 namespace { 216 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 217 using GenericTarget::GenericTarget; 218 219 static constexpr int defaultWidth = 64; 220 221 CodeGenSpecifics::Marshalling 222 complexArgumentType(mlir::Location, mlir::Type eleTy) const override { 223 CodeGenSpecifics::Marshalling marshal; 224 // two distinct element type arguments (re, im) 225 marshal.emplace_back(eleTy, AT{}); 226 marshal.emplace_back(eleTy, AT{}); 227 return marshal; 228 } 229 230 CodeGenSpecifics::Marshalling 231 complexReturnType(mlir::Location, mlir::Type eleTy) const override { 232 CodeGenSpecifics::Marshalling marshal; 233 // Use a type that will be translated into LLVM as: 234 // { t, t } struct of 2 element type 235 mlir::TypeRange range = {eleTy, eleTy}; 236 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 237 return marshal; 238 } 239 }; 240 } // namespace 241 242 // Instantiate the overloaded target instance based on the triple value. 243 // TODO: Add other targets to this file as needed. 244 std::unique_ptr<fir::CodeGenSpecifics> 245 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 246 KindMapping &&kindMap) { 247 switch (trp.getArch()) { 248 default: 249 break; 250 case llvm::Triple::ArchType::x86: 251 switch (trp.getOS()) { 252 default: 253 break; 254 case llvm::Triple::OSType::Linux: 255 case llvm::Triple::OSType::Darwin: 256 case llvm::Triple::OSType::MacOSX: 257 case llvm::Triple::OSType::Win32: 258 return std::make_unique<TargetI386>(ctx, std::move(trp), 259 std::move(kindMap)); 260 } 261 break; 262 case llvm::Triple::ArchType::x86_64: 263 switch (trp.getOS()) { 264 default: 265 break; 266 case llvm::Triple::OSType::Linux: 267 case llvm::Triple::OSType::Darwin: 268 case llvm::Triple::OSType::MacOSX: 269 case llvm::Triple::OSType::Win32: 270 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 271 std::move(kindMap)); 272 } 273 break; 274 case llvm::Triple::ArchType::aarch64: 275 switch (trp.getOS()) { 276 default: 277 break; 278 case llvm::Triple::OSType::Linux: 279 case llvm::Triple::OSType::Darwin: 280 case llvm::Triple::OSType::MacOSX: 281 case llvm::Triple::OSType::Win32: 282 return std::make_unique<TargetAArch64>(ctx, std::move(trp), 283 std::move(kindMap)); 284 } 285 break; 286 case llvm::Triple::ArchType::ppc64le: 287 switch (trp.getOS()) { 288 default: 289 break; 290 case llvm::Triple::OSType::Linux: 291 return std::make_unique<TargetPPC64le>(ctx, std::move(trp), 292 std::move(kindMap)); 293 } 294 break; 295 } 296 TODO(mlir::UnknownLoc::get(ctx), "target not implemented"); 297 } 298