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