| //===-- KindMapping.cpp ---------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Optimizer/Support/KindMapping.h" |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| #include "llvm/Support/CommandLine.h" |
| |
| /// Allow the user to set the FIR intrinsic type kind value to LLVM type |
| /// mappings. Note that these are not mappings from kind values to any |
| /// other MLIR dialect, only to LLVM IR. The default values follow the f18 |
| /// front-end kind mappings. |
| |
| using Bitsize = fir::KindMapping::Bitsize; |
| using KindTy = fir::KindMapping::KindTy; |
| using LLVMTypeID = fir::KindMapping::LLVMTypeID; |
| using MatchResult = fir::KindMapping::MatchResult; |
| |
| static llvm::cl::opt<std::string> |
| clKindMapping("kind-mapping", |
| llvm::cl::desc("kind mapping string to set kind precision"), |
| llvm::cl::value_desc("kind-mapping-string"), |
| llvm::cl::init(fir::KindMapping::getDefaultMap())); |
| |
| static llvm::cl::opt<std::string> |
| clDefaultKinds("default-kinds", |
| llvm::cl::desc("string to set default kind values"), |
| llvm::cl::value_desc("default-kind-string"), |
| llvm::cl::init(fir::KindMapping::getDefaultKinds())); |
| |
| // Keywords for the floating point types. |
| |
| static constexpr const char *kwHalf = "Half"; |
| static constexpr const char *kwBFloat = "BFloat"; |
| static constexpr const char *kwFloat = "Float"; |
| static constexpr const char *kwDouble = "Double"; |
| static constexpr const char *kwX86FP80 = "X86_FP80"; |
| static constexpr const char *kwFP128 = "FP128"; |
| static constexpr const char *kwPPCFP128 = "PPC_FP128"; |
| |
| /// Integral types default to the kind value being the size of the value in |
| /// bytes. The default is to scale from bytes to bits. |
| static Bitsize defaultScalingKind(KindTy kind) { |
| const unsigned bitsInByte = 8; |
| return kind * bitsInByte; |
| } |
| |
| /// Floating-point types default to the kind value being the size of the value |
| /// in bytes. The default is to translate kinds of 2, 3, 4, 8, 10, and 16 to a |
| /// valid llvm::Type::TypeID value. Otherwise, the default is FloatTyID. |
| static LLVMTypeID defaultRealKind(KindTy kind) { |
| switch (kind) { |
| case 2: |
| return LLVMTypeID::HalfTyID; |
| case 3: |
| return LLVMTypeID::BFloatTyID; |
| case 4: |
| return LLVMTypeID::FloatTyID; |
| case 8: |
| return LLVMTypeID::DoubleTyID; |
| case 10: |
| return LLVMTypeID::X86_FP80TyID; |
| case 16: |
| return LLVMTypeID::FP128TyID; |
| default: |
| return LLVMTypeID::FloatTyID; |
| } |
| } |
| |
| // lookup the kind-value given the defaults, the mappings, and a KIND key |
| template <typename RT, char KEY> |
| static RT doLookup(std::function<RT(KindTy)> def, |
| const llvm::DenseMap<std::pair<char, KindTy>, RT> &map, |
| KindTy kind) { |
| std::pair<char, KindTy> key{KEY, kind}; |
| auto iter = map.find(key); |
| if (iter != map.end()) |
| return iter->second; |
| return def(kind); |
| } |
| |
| // do a lookup for INTERGER, LOGICAL, or CHARACTER |
| template <char KEY, typename MAP> |
| static Bitsize getIntegerLikeBitsize(KindTy kind, const MAP &map) { |
| return doLookup<Bitsize, KEY>(defaultScalingKind, map, kind); |
| } |
| |
| // do a lookup for REAL or COMPLEX |
| template <char KEY, typename MAP> |
| static LLVMTypeID getFloatLikeTypeID(KindTy kind, const MAP &map) { |
| return doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind); |
| } |
| |
| template <char KEY, typename MAP> |
| static const llvm::fltSemantics &getFloatSemanticsOfKind(KindTy kind, |
| const MAP &map) { |
| switch (doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind)) { |
| case LLVMTypeID::HalfTyID: |
| return llvm::APFloat::IEEEhalf(); |
| case LLVMTypeID::BFloatTyID: |
| return llvm::APFloat::BFloat(); |
| case LLVMTypeID::FloatTyID: |
| return llvm::APFloat::IEEEsingle(); |
| case LLVMTypeID::DoubleTyID: |
| return llvm::APFloat::IEEEdouble(); |
| case LLVMTypeID::X86_FP80TyID: |
| return llvm::APFloat::x87DoubleExtended(); |
| case LLVMTypeID::FP128TyID: |
| return llvm::APFloat::IEEEquad(); |
| case LLVMTypeID::PPC_FP128TyID: |
| return llvm::APFloat::PPCDoubleDouble(); |
| default: |
| llvm_unreachable("Invalid floating type"); |
| } |
| } |
| |
| /// Parse an intrinsic type code. The codes are ('a', CHARACTER), ('c', |
| /// COMPLEX), ('i', INTEGER), ('l', LOGICAL), and ('r', REAL). |
| static MatchResult parseCode(char &code, const char *&ptr, const char *endPtr) { |
| if (ptr >= endPtr) |
| return mlir::failure(); |
| if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r') |
| return mlir::failure(); |
| code = *ptr++; |
| return mlir::success(); |
| } |
| |
| /// Same as `parseCode` but adds the ('d', DOUBLE PRECISION) code. |
| static MatchResult parseDefCode(char &code, const char *&ptr, |
| const char *endPtr) { |
| if (ptr >= endPtr) |
| return mlir::failure(); |
| if (*ptr == 'd') { |
| code = *ptr++; |
| return mlir::success(); |
| } |
| return parseCode(code, ptr, endPtr); |
| } |
| |
| template <char ch> |
| static MatchResult parseSingleChar(const char *&ptr, const char *endPtr) { |
| if (ptr >= endPtr || *ptr != ch) |
| return mlir::failure(); |
| ++ptr; |
| return mlir::success(); |
| } |
| |
| static MatchResult parseColon(const char *&ptr, const char *endPtr) { |
| return parseSingleChar<':'>(ptr, endPtr); |
| } |
| |
| static MatchResult parseComma(const char *&ptr, const char *endPtr) { |
| return parseSingleChar<','>(ptr, endPtr); |
| } |
| |
| /// Recognize and parse an unsigned integer. |
| static MatchResult parseInt(unsigned &result, const char *&ptr, |
| const char *endPtr) { |
| const char *beg = ptr; |
| while (ptr < endPtr && *ptr >= '0' && *ptr <= '9') |
| ptr++; |
| if (beg == ptr) |
| return mlir::failure(); |
| llvm::StringRef ref(beg, ptr - beg); |
| int temp; |
| if (ref.consumeInteger(10, temp)) |
| return mlir::failure(); |
| result = temp; |
| return mlir::success(); |
| } |
| |
| static mlir::LogicalResult matchString(const char *&ptr, const char *endPtr, |
| llvm::StringRef literal) { |
| llvm::StringRef s(ptr, endPtr - ptr); |
| if (s.startswith(literal)) { |
| ptr += literal.size(); |
| return mlir::success(); |
| } |
| return mlir::failure(); |
| } |
| |
| /// Recognize and parse the various floating-point keywords. These follow the |
| /// LLVM naming convention. |
| static MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr, |
| const char *endPtr) { |
| if (mlir::succeeded(matchString(ptr, endPtr, kwHalf))) { |
| result = LLVMTypeID::HalfTyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwBFloat))) { |
| result = LLVMTypeID::BFloatTyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwFloat))) { |
| result = LLVMTypeID::FloatTyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwDouble))) { |
| result = LLVMTypeID::DoubleTyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwX86FP80))) { |
| result = LLVMTypeID::X86_FP80TyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwFP128))) { |
| result = LLVMTypeID::FP128TyID; |
| return mlir::success(); |
| } |
| if (mlir::succeeded(matchString(ptr, endPtr, kwPPCFP128))) { |
| result = LLVMTypeID::PPC_FP128TyID; |
| return mlir::success(); |
| } |
| return mlir::failure(); |
| } |
| |
| fir::KindMapping::KindMapping(mlir::MLIRContext *context, llvm::StringRef map, |
| llvm::ArrayRef<KindTy> defs) |
| : context{context} { |
| if (mlir::failed(setDefaultKinds(defs))) |
| llvm::report_fatal_error("bad default kinds"); |
| if (mlir::failed(parse(map))) |
| llvm::report_fatal_error("could not parse kind map"); |
| } |
| |
| fir::KindMapping::KindMapping(mlir::MLIRContext *context, |
| llvm::ArrayRef<KindTy> defs) |
| : KindMapping{context, clKindMapping, defs} {} |
| |
| fir::KindMapping::KindMapping(mlir::MLIRContext *context) |
| : KindMapping{context, clKindMapping, clDefaultKinds} {} |
| |
| MatchResult fir::KindMapping::badMapString(const llvm::Twine &ptr) { |
| auto unknown = mlir::UnknownLoc::get(context); |
| mlir::emitError(unknown, ptr); |
| return mlir::failure(); |
| } |
| |
| MatchResult fir::KindMapping::parse(llvm::StringRef kindMap) { |
| if (kindMap.empty()) |
| return mlir::success(); |
| const char *srcPtr = kindMap.begin(); |
| const char *endPtr = kindMap.end(); |
| while (true) { |
| char code = '\0'; |
| KindTy kind = 0; |
| if (parseCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr)) |
| return badMapString(srcPtr); |
| if (code == 'a' || code == 'i' || code == 'l') { |
| Bitsize bits = 0; |
| if (parseColon(srcPtr, endPtr) || parseInt(bits, srcPtr, endPtr)) |
| return badMapString(srcPtr); |
| intMap[std::pair<char, KindTy>{code, kind}] = bits; |
| } else if (code == 'r' || code == 'c') { |
| LLVMTypeID id{}; |
| if (parseColon(srcPtr, endPtr) || parseTypeID(id, srcPtr, endPtr)) |
| return badMapString(srcPtr); |
| floatMap[std::pair<char, KindTy>{code, kind}] = id; |
| } else { |
| return badMapString(srcPtr); |
| } |
| if (parseComma(srcPtr, endPtr)) |
| break; |
| } |
| if (srcPtr > endPtr) |
| return badMapString(srcPtr); |
| return mlir::success(); |
| } |
| |
| Bitsize fir::KindMapping::getCharacterBitsize(KindTy kind) const { |
| return getIntegerLikeBitsize<'a'>(kind, intMap); |
| } |
| |
| Bitsize fir::KindMapping::getIntegerBitsize(KindTy kind) const { |
| return getIntegerLikeBitsize<'i'>(kind, intMap); |
| } |
| |
| Bitsize fir::KindMapping::getLogicalBitsize(KindTy kind) const { |
| return getIntegerLikeBitsize<'l'>(kind, intMap); |
| } |
| |
| LLVMTypeID fir::KindMapping::getRealTypeID(KindTy kind) const { |
| return getFloatLikeTypeID<'r'>(kind, floatMap); |
| } |
| |
| LLVMTypeID fir::KindMapping::getComplexTypeID(KindTy kind) const { |
| return getFloatLikeTypeID<'c'>(kind, floatMap); |
| } |
| |
| Bitsize fir::KindMapping::getRealBitsize(KindTy kind) const { |
| auto typeId = getFloatLikeTypeID<'r'>(kind, floatMap); |
| llvm::LLVMContext llCtxt; // FIXME |
| return llvm::Type::getPrimitiveType(llCtxt, typeId)->getPrimitiveSizeInBits(); |
| } |
| |
| const llvm::fltSemantics & |
| fir::KindMapping::getFloatSemantics(KindTy kind) const { |
| return getFloatSemanticsOfKind<'r'>(kind, floatMap); |
| } |
| |
| std::string fir::KindMapping::mapToString() const { |
| std::string result; |
| bool addComma = false; |
| for (auto [k, v] : intMap) { |
| if (addComma) |
| result.append(","); |
| else |
| addComma = true; |
| result += k.first + std::to_string(k.second) + ":" + std::to_string(v); |
| } |
| for (auto [k, v] : floatMap) { |
| if (addComma) |
| result.append(","); |
| else |
| addComma = true; |
| result.append(k.first + std::to_string(k.second) + ":"); |
| switch (v) { |
| default: |
| llvm_unreachable("unhandled type-id"); |
| case LLVMTypeID::HalfTyID: |
| result.append(kwHalf); |
| break; |
| case LLVMTypeID::BFloatTyID: |
| result.append(kwBFloat); |
| break; |
| case LLVMTypeID::FloatTyID: |
| result.append(kwFloat); |
| break; |
| case LLVMTypeID::DoubleTyID: |
| result.append(kwDouble); |
| break; |
| case LLVMTypeID::X86_FP80TyID: |
| result.append(kwX86FP80); |
| break; |
| case LLVMTypeID::FP128TyID: |
| result.append(kwFP128); |
| break; |
| case LLVMTypeID::PPC_FP128TyID: |
| result.append(kwPPCFP128); |
| break; |
| } |
| } |
| return result; |
| } |
| |
| mlir::LogicalResult |
| fir::KindMapping::setDefaultKinds(llvm::ArrayRef<KindTy> defs) { |
| if (defs.empty()) { |
| // generic front-end defaults |
| const KindTy genericKind = 4; |
| defaultMap.insert({'a', 1}); |
| defaultMap.insert({'c', genericKind}); |
| defaultMap.insert({'d', 2 * genericKind}); |
| defaultMap.insert({'i', genericKind}); |
| defaultMap.insert({'l', genericKind}); |
| defaultMap.insert({'r', genericKind}); |
| return mlir::success(); |
| } |
| if (defs.size() != 6) |
| return mlir::failure(); |
| |
| // defaults determined after command-line processing |
| defaultMap.insert({'a', defs[0]}); |
| defaultMap.insert({'c', defs[1]}); |
| defaultMap.insert({'d', defs[2]}); |
| defaultMap.insert({'i', defs[3]}); |
| defaultMap.insert({'l', defs[4]}); |
| defaultMap.insert({'r', defs[5]}); |
| return mlir::success(); |
| } |
| |
| std::string fir::KindMapping::defaultsToString() const { |
| return std::string("a") + std::to_string(defaultMap.find('a')->second) + |
| std::string("c") + std::to_string(defaultMap.find('c')->second) + |
| std::string("d") + std::to_string(defaultMap.find('d')->second) + |
| std::string("i") + std::to_string(defaultMap.find('i')->second) + |
| std::string("l") + std::to_string(defaultMap.find('l')->second) + |
| std::string("r") + std::to_string(defaultMap.find('r')->second); |
| } |
| |
| /// Convert a default intrinsic code into the proper position in the array. The |
| /// default kinds have a precise ordering. |
| static int codeToIndex(char code) { |
| switch (code) { |
| case 'a': |
| return 0; |
| case 'c': |
| return 1; |
| case 'd': |
| return 2; |
| case 'i': |
| return 3; |
| case 'l': |
| return 4; |
| case 'r': |
| return 5; |
| } |
| llvm_unreachable("invalid default kind intrinsic code"); |
| } |
| |
| std::vector<KindTy> fir::KindMapping::toDefaultKinds(llvm::StringRef defs) { |
| std::vector<KindTy> result(6); |
| char code; |
| KindTy kind; |
| if (defs.empty()) |
| defs = clDefaultKinds; |
| const char *srcPtr = defs.begin(); |
| const char *endPtr = defs.end(); |
| while (srcPtr < endPtr) { |
| if (parseDefCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr)) |
| llvm::report_fatal_error("invalid default kind code"); |
| result[codeToIndex(code)] = kind; |
| } |
| assert(srcPtr == endPtr); |
| return result; |
| } |
| |
| KindTy fir::KindMapping::defaultCharacterKind() const { |
| auto iter = defaultMap.find('a'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |
| |
| KindTy fir::KindMapping::defaultComplexKind() const { |
| auto iter = defaultMap.find('c'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |
| |
| KindTy fir::KindMapping::defaultDoubleKind() const { |
| auto iter = defaultMap.find('d'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |
| |
| KindTy fir::KindMapping::defaultIntegerKind() const { |
| auto iter = defaultMap.find('i'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |
| |
| KindTy fir::KindMapping::defaultLogicalKind() const { |
| auto iter = defaultMap.find('l'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |
| |
| KindTy fir::KindMapping::defaultRealKind() const { |
| auto iter = defaultMap.find('r'); |
| assert(iter != defaultMap.end()); |
| return iter->second; |
| } |