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/Dialect/FIRType.h" 15 #include "flang/Optimizer/Support/KindMapping.h" 16 #include "mlir/IR/BuiltinTypes.h" 17 #include "mlir/IR/TypeRange.h" 18 19 #define DEBUG_TYPE "flang-codegen-target" 20 21 using namespace fir; 22 23 // Reduce a REAL/float type to the floating point semantics. 24 static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap, 25 mlir::Type type) { 26 assert(isa_real(type)); 27 if (auto ty = type.dyn_cast<fir::RealType>()) 28 return kindMap.getFloatSemantics(ty.getFKind()); 29 return type.cast<mlir::FloatType>().getFloatSemantics(); 30 } 31 32 namespace { 33 template <typename S> 34 struct GenericTarget : public CodeGenSpecifics { 35 using CodeGenSpecifics::CodeGenSpecifics; 36 using AT = CodeGenSpecifics::Attributes; 37 38 mlir::Type complexMemoryType(mlir::Type eleTy) const override { 39 assert(fir::isa_real(eleTy)); 40 // Use a type that will be translated into LLVM as: 41 // { t, t } struct of 2 eleTy 42 mlir::TypeRange range = {eleTy, eleTy}; 43 return mlir::TupleType::get(eleTy.getContext(), range); 44 } 45 46 mlir::Type boxcharMemoryType(mlir::Type eleTy) const override { 47 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 48 auto ptrTy = fir::ReferenceType::get(eleTy); 49 // Use a type that will be translated into LLVM as: 50 // { t*, index } 51 mlir::TypeRange range = {ptrTy, idxTy}; 52 return mlir::TupleType::get(eleTy.getContext(), range); 53 } 54 55 Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override { 56 CodeGenSpecifics::Marshalling marshal; 57 auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth); 58 auto ptrTy = fir::ReferenceType::get(eleTy); 59 marshal.emplace_back(ptrTy, AT{}); 60 // Return value arguments are grouped as a pair. Others are passed in a 61 // split format with all pointers first (in the declared position) and all 62 // LEN arguments appended after all of the dummy arguments. 63 // NB: Other conventions/ABIs can/should be supported via options. 64 marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false, 65 /*sret=*/sret, /*append=*/!sret}); 66 return marshal; 67 } 68 }; 69 } // namespace 70 71 //===----------------------------------------------------------------------===// 72 // i386 (x86 32 bit) linux target specifics. 73 //===----------------------------------------------------------------------===// 74 75 namespace { 76 struct TargetI386 : public GenericTarget<TargetI386> { 77 using GenericTarget::GenericTarget; 78 79 static constexpr int defaultWidth = 32; 80 81 CodeGenSpecifics::Marshalling 82 complexArgumentType(mlir::Type eleTy) const override { 83 assert(fir::isa_real(eleTy)); 84 CodeGenSpecifics::Marshalling marshal; 85 // Use a type that will be translated into LLVM as: 86 // { t, t } struct of 2 eleTy, byval, align 4 87 mlir::TypeRange range = {eleTy, eleTy}; 88 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 89 marshal.emplace_back(fir::ReferenceType::get(structTy), 90 AT{/*alignment=*/4, /*byval=*/true}); 91 return marshal; 92 } 93 94 CodeGenSpecifics::Marshalling 95 complexReturnType(mlir::Type eleTy) const override { 96 assert(fir::isa_real(eleTy)); 97 CodeGenSpecifics::Marshalling marshal; 98 const auto *sem = &floatToSemantics(kindMap, eleTy); 99 if (sem == &llvm::APFloat::IEEEsingle()) { 100 // i64 pack both floats in a 64-bit GPR 101 marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64), 102 AT{}); 103 } else if (sem == &llvm::APFloat::IEEEdouble()) { 104 // Use a type that will be translated into LLVM as: 105 // { t, t } struct of 2 eleTy, sret, align 4 106 mlir::TypeRange range = {eleTy, eleTy}; 107 auto structTy = mlir::TupleType::get(eleTy.getContext(), range); 108 marshal.emplace_back(fir::ReferenceType::get(structTy), 109 AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true}); 110 } else { 111 llvm::report_fatal_error("complex for this precision not implemented"); 112 } 113 return marshal; 114 } 115 }; 116 } // namespace 117 118 //===----------------------------------------------------------------------===// 119 // x86_64 (x86 64 bit) linux target specifics. 120 //===----------------------------------------------------------------------===// 121 122 namespace { 123 struct TargetX86_64 : public GenericTarget<TargetX86_64> { 124 using GenericTarget::GenericTarget; 125 126 static constexpr int defaultWidth = 64; 127 128 CodeGenSpecifics::Marshalling 129 complexArgumentType(mlir::Type eleTy) const override { 130 CodeGenSpecifics::Marshalling marshal; 131 const auto *sem = &floatToSemantics(kindMap, eleTy); 132 if (sem == &llvm::APFloat::IEEEsingle()) { 133 // <2 x t> vector of 2 eleTy 134 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 135 } else if (sem == &llvm::APFloat::IEEEdouble()) { 136 // two distinct double arguments 137 marshal.emplace_back(eleTy, AT{}); 138 marshal.emplace_back(eleTy, AT{}); 139 } else { 140 llvm::report_fatal_error("complex for this precision not implemented"); 141 } 142 return marshal; 143 } 144 145 CodeGenSpecifics::Marshalling 146 complexReturnType(mlir::Type eleTy) const override { 147 CodeGenSpecifics::Marshalling marshal; 148 const auto *sem = &floatToSemantics(kindMap, eleTy); 149 if (sem == &llvm::APFloat::IEEEsingle()) { 150 // <2 x t> vector of 2 eleTy 151 marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{}); 152 } else if (sem == &llvm::APFloat::IEEEdouble()) { 153 // Use a type that will be translated into LLVM as: 154 // { double, double } struct of 2 double 155 mlir::TypeRange range = {eleTy, eleTy}; 156 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 157 AT{}); 158 } else { 159 llvm::report_fatal_error("complex for this precision not implemented"); 160 } 161 return marshal; 162 } 163 }; 164 } // namespace 165 166 //===----------------------------------------------------------------------===// 167 // AArch64 linux target specifics. 168 //===----------------------------------------------------------------------===// 169 170 namespace { 171 struct TargetAArch64 : public GenericTarget<TargetAArch64> { 172 using GenericTarget::GenericTarget; 173 174 static constexpr int defaultWidth = 64; 175 176 CodeGenSpecifics::Marshalling 177 complexArgumentType(mlir::Type eleTy) const override { 178 CodeGenSpecifics::Marshalling marshal; 179 const auto *sem = &floatToSemantics(kindMap, eleTy); 180 if (sem == &llvm::APFloat::IEEEsingle() || 181 sem == &llvm::APFloat::IEEEdouble()) { 182 // [2 x t] array of 2 eleTy 183 marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{}); 184 } else { 185 llvm::report_fatal_error("complex for this precision not implemented"); 186 } 187 return marshal; 188 } 189 190 CodeGenSpecifics::Marshalling 191 complexReturnType(mlir::Type eleTy) const override { 192 CodeGenSpecifics::Marshalling marshal; 193 const auto *sem = &floatToSemantics(kindMap, eleTy); 194 if (sem == &llvm::APFloat::IEEEsingle() || 195 sem == &llvm::APFloat::IEEEdouble()) { 196 // Use a type that will be translated into LLVM as: 197 // { t, t } struct of 2 eleTy 198 mlir::TypeRange range = {eleTy, eleTy}; 199 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), 200 AT{}); 201 } else { 202 llvm::report_fatal_error("complex for this precision not implemented"); 203 } 204 return marshal; 205 } 206 }; 207 } // namespace 208 209 //===----------------------------------------------------------------------===// 210 // PPC64le linux target specifics. 211 //===----------------------------------------------------------------------===// 212 213 namespace { 214 struct TargetPPC64le : public GenericTarget<TargetPPC64le> { 215 using GenericTarget::GenericTarget; 216 217 static constexpr int defaultWidth = 64; 218 219 CodeGenSpecifics::Marshalling 220 complexArgumentType(mlir::Type eleTy) const override { 221 CodeGenSpecifics::Marshalling marshal; 222 // two distinct element type arguments (re, im) 223 marshal.emplace_back(eleTy, AT{}); 224 marshal.emplace_back(eleTy, AT{}); 225 return marshal; 226 } 227 228 CodeGenSpecifics::Marshalling 229 complexReturnType(mlir::Type eleTy) const override { 230 CodeGenSpecifics::Marshalling marshal; 231 // Use a type that will be translated into LLVM as: 232 // { t, t } struct of 2 element type 233 mlir::TypeRange range = {eleTy, eleTy}; 234 marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{}); 235 return marshal; 236 } 237 }; 238 } // namespace 239 240 // Instantiate the overloaded target instance based on the triple value. 241 // TODO: Add other targets to this file as needed. 242 std::unique_ptr<fir::CodeGenSpecifics> 243 fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp, 244 KindMapping &&kindMap) { 245 switch (trp.getArch()) { 246 default: 247 break; 248 case llvm::Triple::ArchType::x86: 249 switch (trp.getOS()) { 250 default: 251 break; 252 case llvm::Triple::OSType::Linux: 253 case llvm::Triple::OSType::Darwin: 254 case llvm::Triple::OSType::Win32: 255 return std::make_unique<TargetI386>(ctx, std::move(trp), 256 std::move(kindMap)); 257 } 258 break; 259 case llvm::Triple::ArchType::x86_64: 260 switch (trp.getOS()) { 261 default: 262 break; 263 case llvm::Triple::OSType::Linux: 264 case llvm::Triple::OSType::Darwin: 265 case llvm::Triple::OSType::Win32: 266 return std::make_unique<TargetX86_64>(ctx, std::move(trp), 267 std::move(kindMap)); 268 } 269 break; 270 case llvm::Triple::ArchType::aarch64: 271 switch (trp.getOS()) { 272 default: 273 break; 274 case llvm::Triple::OSType::Linux: 275 case llvm::Triple::OSType::Darwin: 276 case llvm::Triple::OSType::Win32: 277 return std::make_unique<TargetAArch64>(ctx, std::move(trp), 278 std::move(kindMap)); 279 } 280 break; 281 case llvm::Triple::ArchType::ppc64le: 282 switch (trp.getOS()) { 283 default: 284 break; 285 case llvm::Triple::OSType::Linux: 286 return std::make_unique<TargetPPC64le>(ctx, std::move(trp), 287 std::move(kindMap)); 288 } 289 break; 290 } 291 llvm::report_fatal_error("target not implemented"); 292 } 293