| 1 | //===-- KindMapping.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 "flang/Optimizer/Dialect/Support/KindMapping.h" |
| 14 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| 15 | #include "llvm/Support/CommandLine.h" |
| 16 | |
| 17 | /// Allow the user to set the FIR intrinsic type kind value to LLVM type |
| 18 | /// mappings. Note that these are not mappings from kind values to any |
| 19 | /// other MLIR dialect, only to LLVM IR. The default values follow the f18 |
| 20 | /// front-end kind mappings. |
| 21 | |
| 22 | using Bitsize = fir::KindMapping::Bitsize; |
| 23 | using KindTy = fir::KindMapping::KindTy; |
| 24 | using LLVMTypeID = fir::KindMapping::LLVMTypeID; |
| 25 | using MatchResult = fir::KindMapping::MatchResult; |
| 26 | |
| 27 | static llvm::cl::opt<std::string> |
| 28 | clKindMapping("kind-mapping" , |
| 29 | llvm::cl::desc("kind mapping string to set kind precision" ), |
| 30 | llvm::cl::value_desc("kind-mapping-string" ), |
| 31 | llvm::cl::init(fir::KindMapping::getDefaultMap())); |
| 32 | |
| 33 | static llvm::cl::opt<std::string> |
| 34 | clDefaultKinds("default-kinds" , |
| 35 | llvm::cl::desc("string to set default kind values" ), |
| 36 | llvm::cl::value_desc("default-kind-string" ), |
| 37 | llvm::cl::init(fir::KindMapping::getDefaultKinds())); |
| 38 | |
| 39 | // Keywords for the floating point types. |
| 40 | |
| 41 | static constexpr const char *kwHalf = "Half" ; |
| 42 | static constexpr const char *kwBFloat = "BFloat" ; |
| 43 | static constexpr const char *kwFloat = "Float" ; |
| 44 | static constexpr const char *kwDouble = "Double" ; |
| 45 | static constexpr const char *kwX86FP80 = "X86_FP80" ; |
| 46 | static constexpr const char *kwFP128 = "FP128" ; |
| 47 | static constexpr const char *kwPPCFP128 = "PPC_FP128" ; |
| 48 | |
| 49 | /// Integral types default to the kind value being the size of the value in |
| 50 | /// bytes. The default is to scale from bytes to bits. |
| 51 | static Bitsize defaultScalingKind(KindTy kind) { |
| 52 | const unsigned bitsInByte = 8; |
| 53 | return kind * bitsInByte; |
| 54 | } |
| 55 | |
| 56 | /// Floating-point types default to the kind value being the size of the value |
| 57 | /// in bytes. The default is to translate kinds of 2, 3, 4, 8, 10, and 16 to a |
| 58 | /// valid llvm::Type::TypeID value. Otherwise, the default is FloatTyID. |
| 59 | static LLVMTypeID defaultRealKind(KindTy kind) { |
| 60 | switch (kind) { |
| 61 | case 2: |
| 62 | return LLVMTypeID::HalfTyID; |
| 63 | case 3: |
| 64 | return LLVMTypeID::BFloatTyID; |
| 65 | case 4: |
| 66 | return LLVMTypeID::FloatTyID; |
| 67 | case 8: |
| 68 | return LLVMTypeID::DoubleTyID; |
| 69 | case 10: |
| 70 | return LLVMTypeID::X86_FP80TyID; |
| 71 | case 16: |
| 72 | return LLVMTypeID::FP128TyID; |
| 73 | default: |
| 74 | return LLVMTypeID::FloatTyID; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | // lookup the kind-value given the defaults, the mappings, and a KIND key |
| 79 | template <typename RT, char KEY> |
| 80 | static RT doLookup(std::function<RT(KindTy)> def, |
| 81 | const llvm::DenseMap<std::pair<char, KindTy>, RT> &map, |
| 82 | KindTy kind) { |
| 83 | std::pair<char, KindTy> key{KEY, kind}; |
| 84 | auto iter = map.find(key); |
| 85 | if (iter != map.end()) |
| 86 | return iter->second; |
| 87 | return def(kind); |
| 88 | } |
| 89 | |
| 90 | // do a lookup for INTEGER, LOGICAL, or CHARACTER |
| 91 | template <char KEY, typename MAP> |
| 92 | static Bitsize getIntegerLikeBitsize(KindTy kind, const MAP &map) { |
| 93 | return doLookup<Bitsize, KEY>(defaultScalingKind, map, kind); |
| 94 | } |
| 95 | |
| 96 | // do a lookup for REAL or COMPLEX |
| 97 | template <char KEY, typename MAP> |
| 98 | static LLVMTypeID getFloatLikeTypeID(KindTy kind, const MAP &map) { |
| 99 | return doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind); |
| 100 | } |
| 101 | |
| 102 | template <char KEY, typename MAP> |
| 103 | static const llvm::fltSemantics &getFloatSemanticsOfKind(KindTy kind, |
| 104 | const MAP &map) { |
| 105 | switch (doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind)) { |
| 106 | case LLVMTypeID::HalfTyID: |
| 107 | return llvm::APFloat::IEEEhalf(); |
| 108 | case LLVMTypeID::BFloatTyID: |
| 109 | return llvm::APFloat::BFloat(); |
| 110 | case LLVMTypeID::FloatTyID: |
| 111 | return llvm::APFloat::IEEEsingle(); |
| 112 | case LLVMTypeID::DoubleTyID: |
| 113 | return llvm::APFloat::IEEEdouble(); |
| 114 | case LLVMTypeID::X86_FP80TyID: |
| 115 | return llvm::APFloat::x87DoubleExtended(); |
| 116 | case LLVMTypeID::FP128TyID: |
| 117 | return llvm::APFloat::IEEEquad(); |
| 118 | case LLVMTypeID::PPC_FP128TyID: |
| 119 | return llvm::APFloat::PPCDoubleDouble(); |
| 120 | default: |
| 121 | llvm_unreachable("Invalid floating type" ); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | /// Parse an intrinsic type code. The codes are ('a', CHARACTER), ('c', |
| 126 | /// COMPLEX), ('i', INTEGER), ('l', LOGICAL), and ('r', REAL). |
| 127 | static MatchResult parseCode(char &code, const char *&ptr, const char *endPtr) { |
| 128 | if (ptr >= endPtr) |
| 129 | return mlir::failure(); |
| 130 | if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r') |
| 131 | return mlir::failure(); |
| 132 | code = *ptr++; |
| 133 | return mlir::success(); |
| 134 | } |
| 135 | |
| 136 | /// Same as `parseCode` but adds the ('d', DOUBLE PRECISION) code. |
| 137 | static MatchResult parseDefCode(char &code, const char *&ptr, |
| 138 | const char *endPtr) { |
| 139 | if (ptr >= endPtr) |
| 140 | return mlir::failure(); |
| 141 | if (*ptr == 'd') { |
| 142 | code = *ptr++; |
| 143 | return mlir::success(); |
| 144 | } |
| 145 | return parseCode(code, ptr, endPtr); |
| 146 | } |
| 147 | |
| 148 | template <char ch> |
| 149 | static MatchResult parseSingleChar(const char *&ptr, const char *endPtr) { |
| 150 | if (ptr >= endPtr || *ptr != ch) |
| 151 | return mlir::failure(); |
| 152 | ++ptr; |
| 153 | return mlir::success(); |
| 154 | } |
| 155 | |
| 156 | static MatchResult parseColon(const char *&ptr, const char *endPtr) { |
| 157 | return parseSingleChar<':'>(ptr, endPtr); |
| 158 | } |
| 159 | |
| 160 | static MatchResult parseComma(const char *&ptr, const char *endPtr) { |
| 161 | return parseSingleChar<','>(ptr, endPtr); |
| 162 | } |
| 163 | |
| 164 | /// Recognize and parse an unsigned integer. |
| 165 | static MatchResult parseInt(unsigned &result, const char *&ptr, |
| 166 | const char *endPtr) { |
| 167 | const char *beg = ptr; |
| 168 | while (ptr < endPtr && *ptr >= '0' && *ptr <= '9') |
| 169 | ptr++; |
| 170 | if (beg == ptr) |
| 171 | return mlir::failure(); |
| 172 | llvm::StringRef ref(beg, ptr - beg); |
| 173 | int temp; |
| 174 | if (ref.consumeInteger(10, temp)) |
| 175 | return mlir::failure(); |
| 176 | result = temp; |
| 177 | return mlir::success(); |
| 178 | } |
| 179 | |
| 180 | static llvm::LogicalResult matchString(const char *&ptr, const char *endPtr, |
| 181 | llvm::StringRef literal) { |
| 182 | llvm::StringRef s(ptr, endPtr - ptr); |
| 183 | if (s.starts_with(Prefix: literal)) { |
| 184 | ptr += literal.size(); |
| 185 | return mlir::success(); |
| 186 | } |
| 187 | return mlir::failure(); |
| 188 | } |
| 189 | |
| 190 | /// Recognize and parse the various floating-point keywords. These follow the |
| 191 | /// LLVM naming convention. |
| 192 | static MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr, |
| 193 | const char *endPtr) { |
| 194 | if (mlir::succeeded(matchString(ptr, endPtr, kwHalf))) { |
| 195 | result = LLVMTypeID::HalfTyID; |
| 196 | return mlir::success(); |
| 197 | } |
| 198 | if (mlir::succeeded(matchString(ptr, endPtr, kwBFloat))) { |
| 199 | result = LLVMTypeID::BFloatTyID; |
| 200 | return mlir::success(); |
| 201 | } |
| 202 | if (mlir::succeeded(matchString(ptr, endPtr, kwFloat))) { |
| 203 | result = LLVMTypeID::FloatTyID; |
| 204 | return mlir::success(); |
| 205 | } |
| 206 | if (mlir::succeeded(matchString(ptr, endPtr, kwDouble))) { |
| 207 | result = LLVMTypeID::DoubleTyID; |
| 208 | return mlir::success(); |
| 209 | } |
| 210 | if (mlir::succeeded(matchString(ptr, endPtr, kwX86FP80))) { |
| 211 | result = LLVMTypeID::X86_FP80TyID; |
| 212 | return mlir::success(); |
| 213 | } |
| 214 | if (mlir::succeeded(matchString(ptr, endPtr, kwFP128))) { |
| 215 | result = LLVMTypeID::FP128TyID; |
| 216 | return mlir::success(); |
| 217 | } |
| 218 | if (mlir::succeeded(matchString(ptr, endPtr, kwPPCFP128))) { |
| 219 | result = LLVMTypeID::PPC_FP128TyID; |
| 220 | return mlir::success(); |
| 221 | } |
| 222 | return mlir::failure(); |
| 223 | } |
| 224 | |
| 225 | fir::KindMapping::KindMapping(mlir::MLIRContext *context, llvm::StringRef map, |
| 226 | llvm::ArrayRef<KindTy> defs) |
| 227 | : context{context} { |
| 228 | if (mlir::failed(setDefaultKinds(defs))) |
| 229 | llvm::report_fatal_error("bad default kinds" ); |
| 230 | if (mlir::failed(parse(map))) |
| 231 | llvm::report_fatal_error("could not parse kind map" ); |
| 232 | } |
| 233 | |
| 234 | fir::KindMapping::KindMapping(mlir::MLIRContext *context, |
| 235 | llvm::ArrayRef<KindTy> defs) |
| 236 | : KindMapping{context, clKindMapping, defs} {} |
| 237 | |
| 238 | fir::KindMapping::KindMapping(mlir::MLIRContext *context) |
| 239 | : KindMapping{context, clKindMapping, clDefaultKinds} {} |
| 240 | |
| 241 | MatchResult fir::KindMapping::badMapString(const llvm::Twine &ptr) { |
| 242 | auto unknown = mlir::UnknownLoc::get(context); |
| 243 | mlir::emitError(unknown, ptr); |
| 244 | return mlir::failure(); |
| 245 | } |
| 246 | |
| 247 | MatchResult fir::KindMapping::parse(llvm::StringRef kindMap) { |
| 248 | if (kindMap.empty()) |
| 249 | return mlir::success(); |
| 250 | const char *srcPtr = kindMap.begin(); |
| 251 | const char *endPtr = kindMap.end(); |
| 252 | while (true) { |
| 253 | char code = '\0'; |
| 254 | KindTy kind = 0; |
| 255 | if (parseCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr)) |
| 256 | return badMapString(srcPtr); |
| 257 | if (code == 'a' || code == 'i' || code == 'l') { |
| 258 | Bitsize bits = 0; |
| 259 | if (parseColon(srcPtr, endPtr) || parseInt(bits, srcPtr, endPtr)) |
| 260 | return badMapString(srcPtr); |
| 261 | intMap[std::pair<char, KindTy>{code, kind}] = bits; |
| 262 | } else if (code == 'r' || code == 'c') { |
| 263 | LLVMTypeID id{}; |
| 264 | if (parseColon(srcPtr, endPtr) || parseTypeID(id, srcPtr, endPtr)) |
| 265 | return badMapString(srcPtr); |
| 266 | floatMap[std::pair<char, KindTy>{code, kind}] = id; |
| 267 | } else { |
| 268 | return badMapString(srcPtr); |
| 269 | } |
| 270 | if (parseComma(srcPtr, endPtr)) |
| 271 | break; |
| 272 | } |
| 273 | if (srcPtr > endPtr) |
| 274 | return badMapString(srcPtr); |
| 275 | return mlir::success(); |
| 276 | } |
| 277 | |
| 278 | Bitsize fir::KindMapping::getCharacterBitsize(KindTy kind) const { |
| 279 | return getIntegerLikeBitsize<'a'>(kind, intMap); |
| 280 | } |
| 281 | |
| 282 | Bitsize fir::KindMapping::getIntegerBitsize(KindTy kind) const { |
| 283 | return getIntegerLikeBitsize<'i'>(kind, intMap); |
| 284 | } |
| 285 | |
| 286 | Bitsize fir::KindMapping::getLogicalBitsize(KindTy kind) const { |
| 287 | return getIntegerLikeBitsize<'l'>(kind, intMap); |
| 288 | } |
| 289 | |
| 290 | LLVMTypeID fir::KindMapping::getRealTypeID(KindTy kind) const { |
| 291 | return getFloatLikeTypeID<'r'>(kind, floatMap); |
| 292 | } |
| 293 | |
| 294 | LLVMTypeID fir::KindMapping::getComplexTypeID(KindTy kind) const { |
| 295 | return getFloatLikeTypeID<'c'>(kind, floatMap); |
| 296 | } |
| 297 | |
| 298 | Bitsize fir::KindMapping::getRealBitsize(KindTy kind) const { |
| 299 | auto typeId = getFloatLikeTypeID<'r'>(kind, floatMap); |
| 300 | llvm::LLVMContext llCtxt; // FIXME |
| 301 | return llvm::Type::getPrimitiveType(llCtxt, typeId)->getPrimitiveSizeInBits(); |
| 302 | } |
| 303 | |
| 304 | const llvm::fltSemantics & |
| 305 | fir::KindMapping::getFloatSemantics(KindTy kind) const { |
| 306 | return getFloatSemanticsOfKind<'r'>(kind, floatMap); |
| 307 | } |
| 308 | |
| 309 | std::string fir::KindMapping::mapToString() const { |
| 310 | std::string result; |
| 311 | bool addComma = false; |
| 312 | for (auto [k, v] : intMap) { |
| 313 | if (addComma) |
| 314 | result.append("," ); |
| 315 | else |
| 316 | addComma = true; |
| 317 | result += k.first + std::to_string(k.second) + ":" + std::to_string(v); |
| 318 | } |
| 319 | for (auto [k, v] : floatMap) { |
| 320 | if (addComma) |
| 321 | result.append("," ); |
| 322 | else |
| 323 | addComma = true; |
| 324 | result.append(k.first + std::to_string(k.second) + ":" ); |
| 325 | switch (v) { |
| 326 | default: |
| 327 | llvm_unreachable("unhandled type-id" ); |
| 328 | case LLVMTypeID::HalfTyID: |
| 329 | result.append(kwHalf); |
| 330 | break; |
| 331 | case LLVMTypeID::BFloatTyID: |
| 332 | result.append(kwBFloat); |
| 333 | break; |
| 334 | case LLVMTypeID::FloatTyID: |
| 335 | result.append(kwFloat); |
| 336 | break; |
| 337 | case LLVMTypeID::DoubleTyID: |
| 338 | result.append(kwDouble); |
| 339 | break; |
| 340 | case LLVMTypeID::X86_FP80TyID: |
| 341 | result.append(kwX86FP80); |
| 342 | break; |
| 343 | case LLVMTypeID::FP128TyID: |
| 344 | result.append(kwFP128); |
| 345 | break; |
| 346 | case LLVMTypeID::PPC_FP128TyID: |
| 347 | result.append(kwPPCFP128); |
| 348 | break; |
| 349 | } |
| 350 | } |
| 351 | return result; |
| 352 | } |
| 353 | |
| 354 | llvm::LogicalResult |
| 355 | fir::KindMapping::setDefaultKinds(llvm::ArrayRef<KindTy> defs) { |
| 356 | if (defs.empty()) { |
| 357 | // generic front-end defaults |
| 358 | const KindTy genericKind = 4; |
| 359 | defaultMap.insert({'a', 1}); |
| 360 | defaultMap.insert({'c', genericKind}); |
| 361 | defaultMap.insert({'d', 2 * genericKind}); |
| 362 | defaultMap.insert({'i', genericKind}); |
| 363 | defaultMap.insert({'l', genericKind}); |
| 364 | defaultMap.insert({'r', genericKind}); |
| 365 | return mlir::success(); |
| 366 | } |
| 367 | if (defs.size() != 6) |
| 368 | return mlir::failure(); |
| 369 | |
| 370 | // defaults determined after command-line processing |
| 371 | defaultMap.insert({'a', defs[0]}); |
| 372 | defaultMap.insert({'c', defs[1]}); |
| 373 | defaultMap.insert({'d', defs[2]}); |
| 374 | defaultMap.insert({'i', defs[3]}); |
| 375 | defaultMap.insert({'l', defs[4]}); |
| 376 | defaultMap.insert({'r', defs[5]}); |
| 377 | return mlir::success(); |
| 378 | } |
| 379 | |
| 380 | std::string fir::KindMapping::defaultsToString() const { |
| 381 | return std::string("a" ) + std::to_string(defaultMap.find('a')->second) + |
| 382 | std::string("c" ) + std::to_string(defaultMap.find('c')->second) + |
| 383 | std::string("d" ) + std::to_string(defaultMap.find('d')->second) + |
| 384 | std::string("i" ) + std::to_string(defaultMap.find('i')->second) + |
| 385 | std::string("l" ) + std::to_string(defaultMap.find('l')->second) + |
| 386 | std::string("r" ) + std::to_string(defaultMap.find('r')->second); |
| 387 | } |
| 388 | |
| 389 | /// Convert a default intrinsic code into the proper position in the array. The |
| 390 | /// default kinds have a precise ordering. |
| 391 | static int codeToIndex(char code) { |
| 392 | switch (code) { |
| 393 | case 'a': |
| 394 | return 0; |
| 395 | case 'c': |
| 396 | return 1; |
| 397 | case 'd': |
| 398 | return 2; |
| 399 | case 'i': |
| 400 | return 3; |
| 401 | case 'l': |
| 402 | return 4; |
| 403 | case 'r': |
| 404 | return 5; |
| 405 | } |
| 406 | llvm_unreachable("invalid default kind intrinsic code" ); |
| 407 | } |
| 408 | |
| 409 | std::vector<KindTy> fir::KindMapping::toDefaultKinds(llvm::StringRef defs) { |
| 410 | std::vector<KindTy> result(6); |
| 411 | char code; |
| 412 | KindTy kind; |
| 413 | if (defs.empty()) |
| 414 | defs = clDefaultKinds; |
| 415 | const char *srcPtr = defs.begin(); |
| 416 | const char *endPtr = defs.end(); |
| 417 | while (srcPtr < endPtr) { |
| 418 | if (parseDefCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr)) |
| 419 | llvm::report_fatal_error("invalid default kind code" ); |
| 420 | result[codeToIndex(code)] = kind; |
| 421 | } |
| 422 | assert(srcPtr == endPtr); |
| 423 | return result; |
| 424 | } |
| 425 | |
| 426 | KindTy fir::KindMapping::defaultCharacterKind() const { |
| 427 | auto iter = defaultMap.find('a'); |
| 428 | assert(iter != defaultMap.end()); |
| 429 | return iter->second; |
| 430 | } |
| 431 | |
| 432 | KindTy fir::KindMapping::defaultComplexKind() const { |
| 433 | auto iter = defaultMap.find('c'); |
| 434 | assert(iter != defaultMap.end()); |
| 435 | return iter->second; |
| 436 | } |
| 437 | |
| 438 | KindTy fir::KindMapping::defaultDoubleKind() const { |
| 439 | auto iter = defaultMap.find('d'); |
| 440 | assert(iter != defaultMap.end()); |
| 441 | return iter->second; |
| 442 | } |
| 443 | |
| 444 | KindTy fir::KindMapping::defaultIntegerKind() const { |
| 445 | auto iter = defaultMap.find('i'); |
| 446 | assert(iter != defaultMap.end()); |
| 447 | return iter->second; |
| 448 | } |
| 449 | |
| 450 | KindTy fir::KindMapping::defaultLogicalKind() const { |
| 451 | auto iter = defaultMap.find('l'); |
| 452 | assert(iter != defaultMap.end()); |
| 453 | return iter->second; |
| 454 | } |
| 455 | |
| 456 | KindTy fir::KindMapping::defaultRealKind() const { |
| 457 | auto iter = defaultMap.find('r'); |
| 458 | assert(iter != defaultMap.end()); |
| 459 | return iter->second; |
| 460 | } |
| 461 | |