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