diff --git a/flang/include/flang/Optimizer/Support/FIRContext.h b/flang/include/flang/Optimizer/Support/FIRContext.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/Support/FIRContext.h @@ -0,0 +1,56 @@ +//===-- Optimizer/Support/FIRContext.h --------------------------*- C++ -*-===// +// +// 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/ +// +//===----------------------------------------------------------------------===// +/// Setters and getters for associating context with an instance of a ModuleOp. +/// The context is typically set by the tool and needed in later stages to +/// determine how to correctly generate code. +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_OPTIMIZER_SUPPORT_FIRCONTEXT_H +#define FORTRAN_OPTIMIZER_SUPPORT_FIRCONTEXT_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" + +namespace mlir { +class ModuleOp; +} + +namespace fir { +class KindMapping; +struct NameUniquer; + +/// Set the target triple for the module. `triple` must not be deallocated while +/// module `mod` is still live. +void setTargetTriple(mlir::ModuleOp mod, llvm::StringRef triple); + +/// Get the Triple instance from the Module or return the default Triple. +llvm::Triple getTargetTriple(mlir::ModuleOp mod); + +/// Set the kind mapping for the module. `kindMap` must not be deallocated while +/// module `mod` is still live. +void setKindMapping(mlir::ModuleOp mod, KindMapping &kindMap); + +/// Get the KindMapping instance from the Module. If none was set, returns a +/// default. +KindMapping getKindMapping(mlir::ModuleOp mod); + +/// Helper for determining the target from the host, etc. Tools may use this +/// function to provide a consistent interpretation of the `--target=` +/// command-line option. +/// An empty string ("") or "default" will specify that the default triple +/// should be used. "native" will specify that the host machine be used to +/// construct the triple. +std::string determineTargetTriple(llvm::StringRef triple); + +} // namespace fir + +#endif // FORTRAN_OPTIMIZER_SUPPORT_FIRCONTEXT_H diff --git a/flang/include/flang/Optimizer/Support/InternalNames.h b/flang/include/flang/Optimizer/Support/InternalNames.h --- a/flang/include/flang/Optimizer/Support/InternalNames.h +++ b/flang/include/flang/Optimizer/Support/InternalNames.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef OPTIMIZER_SUPPORT_INTERNALNAMES_H -#define OPTIMIZER_SUPPORT_INTERNALNAMES_H +#ifndef FORTRAN_OPTIMIZER_SUPPORT_INTERNALNAMES_H +#define FORTRAN_OPTIMIZER_SUPPORT_INTERNALNAMES_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -59,58 +59,58 @@ llvm::SmallVector kinds; }; - NameUniquer() = default; - /// Unique a common block name - std::string doCommonBlock(llvm::StringRef name); + static std::string doCommonBlock(llvm::StringRef name); /// Unique a block data unit name - std::string doBlockData(llvm::StringRef name); + static std::string doBlockData(llvm::StringRef name); /// Unique a (global) constant name - std::string doConstant(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name); + static std::string doConstant(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name); /// Unique a dispatch table name - std::string doDispatchTable(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name, - llvm::ArrayRef kinds); + static std::string doDispatchTable(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); /// Unique a compiler generated name - std::string doGenerated(llvm::StringRef name); + static std::string doGenerated(llvm::StringRef name); /// Unique an intrinsic type descriptor - std::string doIntrinsicTypeDescriptor(llvm::ArrayRef modules, - llvm::Optional host, - IntrinsicType type, std::int64_t kind); + static std::string + doIntrinsicTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + IntrinsicType type, std::int64_t kind); /// Unique a procedure name - std::string doProcedure(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name); + static std::string doProcedure(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name); /// Unique a derived type name - std::string doType(llvm::ArrayRef modules, - llvm::Optional host, llvm::StringRef name, - llvm::ArrayRef kinds); + static std::string doType(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); /// Unique a (derived) type descriptor name - std::string doTypeDescriptor(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name, - llvm::ArrayRef kinds); - std::string doTypeDescriptor(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name, - llvm::ArrayRef kinds); + static std::string doTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); + static std::string doTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); /// Unique a (global) variable name. A variable with save attribute /// defined inside a subprogram also needs to be handled here - std::string doVariable(llvm::ArrayRef modules, - llvm::Optional host, - llvm::StringRef name); + static std::string doVariable(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name); /// Entry point for the PROGRAM (called by the runtime) /// Can be overridden with the `--main-entry-name=` option. @@ -121,12 +121,17 @@ deconstruct(llvm::StringRef uniquedName); private: - std::string intAsString(std::int64_t i); - std::string doKind(std::int64_t kind); - std::string doKinds(llvm::ArrayRef kinds); - std::string toLower(llvm::StringRef name); + static std::string intAsString(std::int64_t i); + static std::string doKind(std::int64_t kind); + static std::string doKinds(llvm::ArrayRef kinds); + static std::string toLower(llvm::StringRef name); + + NameUniquer() = delete; + NameUniquer(const NameUniquer &) = delete; + NameUniquer(NameUniquer &&) = delete; + NameUniquer &operator=(const NameUniquer &) = delete; }; } // namespace fir -#endif // OPTIMIZER_SUPPORT_INTERNALNAMES_H +#endif // FORTRAN_OPTIMIZER_SUPPORT_INTERNALNAMES_H diff --git a/flang/include/flang/Optimizer/Support/KindMapping.h b/flang/include/flang/Optimizer/Support/KindMapping.h --- a/flang/include/flang/Optimizer/Support/KindMapping.h +++ b/flang/include/flang/Optimizer/Support/KindMapping.h @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef OPTIMIZER_SUPPORT_KINDMAPPING_H -#define OPTIMIZER_SUPPORT_KINDMAPPING_H +#ifndef FORTRAN_OPTIMIZER_SUPPORT_KINDMAPPING_H +#define FORTRAN_OPTIMIZER_SUPPORT_KINDMAPPING_H #include "mlir/IR/OpDefinition.h" #include "llvm/ADT/DenseMap.h" @@ -52,15 +52,24 @@ using LLVMTypeID = llvm::Type::TypeID; using MatchResult = mlir::ParseResult; - /// KindMapping constructors take an optional `defs` argument to specify the + /// KindMapping constructor with both the kind map and default kinds read from + /// command-line options. + explicit KindMapping(mlir::MLIRContext *context); + /// KindMapping constructor taking a `defs` argument to specify the default + /// kinds for intrinsic types. To set the default kinds, an ArrayRef of 6 + /// KindTy must be passed. The kinds must be the given in the following order: + /// CHARACTER, COMPLEX, DOUBLE PRECISION, INTEGER, LOGICAL, and REAL. The + /// kind map is read from command-line options, if given. + explicit KindMapping(mlir::MLIRContext *context, llvm::ArrayRef defs); + /// KindMapping constructor taking an optional `defs` argument to specify the /// default kinds for intrinsic types. To set the default kinds, an ArrayRef /// of 6 KindTy must be passed. The kinds must be the given in the following /// order: CHARACTER, COMPLEX, DOUBLE PRECISION, INTEGER, LOGICAL, and REAL. - /// If `defs` is not specified, default default kinds will be used. - explicit KindMapping(mlir::MLIRContext *context, - llvm::ArrayRef defs = llvm::None); explicit KindMapping(mlir::MLIRContext *context, llvm::StringRef map, llvm::ArrayRef defs = llvm::None); + explicit KindMapping(mlir::MLIRContext *context, llvm::StringRef map, + llvm::StringRef defs) + : KindMapping{context, map, toDefaultKinds(defs)} {} /// Get the size in bits of !fir.char Bitsize getCharacterBitsize(KindTy kind) const; @@ -85,6 +94,12 @@ /// Get the float semantics of !fir.real const llvm::fltSemantics &getFloatSemantics(KindTy kind) const; + /// Get the default kind map as a string. + static constexpr const char *getDefaultMap() { return ""; } + + /// Convert the current kind map to a string. + std::string mapToString() const; + //===--------------------------------------------------------------------===// // Default kinds of intrinsic types //===--------------------------------------------------------------------===// @@ -96,6 +111,16 @@ KindTy defaultLogicalKind() const; KindTy defaultRealKind() const; + /// Get the default kinds as a string. + static constexpr const char *getDefaultKinds() { return "a1c4d8i4l4r4"; } + + /// Convert the current default kinds to a string. + std::string defaultsToString() const; + + /// Translate a default kinds string into a default kind vector. This vector + /// can be passed to the KindMapping ctor. + static std::vector toDefaultKinds(llvm::StringRef defs); + private: MatchResult badMapString(const llvm::Twine &ptr); MatchResult parse(llvm::StringRef kindMap); @@ -109,4 +134,4 @@ } // namespace fir -#endif // OPTIMIZER_SUPPORT_KINDMAPPING_H +#endif // FORTRAN_OPTIMIZER_SUPPORT_KINDMAPPING_H diff --git a/flang/lib/Optimizer/CMakeLists.txt b/flang/lib/Optimizer/CMakeLists.txt --- a/flang/lib/Optimizer/CMakeLists.txt +++ b/flang/lib/Optimizer/CMakeLists.txt @@ -6,6 +6,7 @@ Dialect/FIROps.cpp Dialect/FIRType.cpp + Support/FIRContext.cpp Support/InternalNames.cpp Support/KindMapping.cpp diff --git a/flang/lib/Optimizer/Support/FIRContext.cpp b/flang/lib/Optimizer/Support/FIRContext.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Support/FIRContext.cpp @@ -0,0 +1,62 @@ +//===-- FIRContext.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/FIRContext.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/BuiltinOps.h" +#include "llvm/Support/Host.h" + +static constexpr const char *tripleName = "fir.triple"; + +void fir::setTargetTriple(mlir::ModuleOp mod, llvm::StringRef triple) { + auto target = fir::determineTargetTriple(triple); + mod->setAttr(tripleName, mlir::StringAttr::get(mod.getContext(), target)); +} + +llvm::Triple fir::getTargetTriple(mlir::ModuleOp mod) { + if (auto target = mod->getAttrOfType(tripleName)) + return llvm::Triple(target.getValue()); + return llvm::Triple(llvm::sys::getDefaultTargetTriple()); +} + +static constexpr const char *kindMapName = "fir.kindmap"; +static constexpr const char *defKindName = "fir.defaultkind"; + +void fir::setKindMapping(mlir::ModuleOp mod, fir::KindMapping &kindMap) { + auto ctx = mod.getContext(); + mod->setAttr(kindMapName, mlir::StringAttr::get(ctx, kindMap.mapToString())); + auto defs = kindMap.defaultsToString(); + mod->setAttr(defKindName, mlir::StringAttr::get(ctx, defs)); +} + +fir::KindMapping fir::getKindMapping(mlir::ModuleOp mod) { + auto ctx = mod.getContext(); + if (auto defs = mod->getAttrOfType(defKindName)) { + auto defVals = fir::KindMapping::toDefaultKinds(defs.getValue()); + if (auto maps = mod->getAttrOfType(kindMapName)) + return fir::KindMapping(ctx, maps.getValue(), defVals); + return fir::KindMapping(ctx, defVals); + } + return fir::KindMapping(ctx); +} + +std::string fir::determineTargetTriple(llvm::StringRef triple) { + // Treat "" or "default" as stand-ins for the default machine. + if (triple.empty() || triple == "default") + return llvm::sys::getDefaultTargetTriple(); + // Treat "native" as stand-in for the host machine. + if (triple == "native") + return llvm::sys::getProcessTriple(); + // TODO: normalize the triple? + return triple.str(); +} diff --git a/flang/lib/Optimizer/Support/InternalNames.cpp b/flang/lib/Optimizer/Support/InternalNames.cpp --- a/flang/lib/Optimizer/Support/InternalNames.cpp +++ b/flang/lib/Optimizer/Support/InternalNames.cpp @@ -5,6 +5,10 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ +// +//===----------------------------------------------------------------------===// #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Dialect/FIRType.h" diff --git a/flang/lib/Optimizer/Support/KindMapping.cpp b/flang/lib/Optimizer/Support/KindMapping.cpp --- a/flang/lib/Optimizer/Support/KindMapping.cpp +++ b/flang/lib/Optimizer/Support/KindMapping.cpp @@ -24,9 +24,27 @@ using LLVMTypeID = fir::KindMapping::LLVMTypeID; using MatchResult = fir::KindMapping::MatchResult; -static llvm::cl::opt clKindMapping( - "kind-mapping", llvm::cl::desc("kind mapping string to set kind precision"), - llvm::cl::value_desc("kind-mapping-string"), llvm::cl::init("")); +static llvm::cl::opt + 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 + 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. @@ -104,32 +122,50 @@ } } -static MatchResult parseCode(char &code, const char *&ptr) { +/// 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 -static MatchResult parseSingleChar(const char *&ptr) { - if (*ptr != 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) { - return parseSingleChar<':'>(ptr); +static MatchResult parseColon(const char *&ptr, const char *endPtr) { + return parseSingleChar<':'>(ptr, endPtr); } -static MatchResult parseComma(const char *&ptr) { - return parseSingleChar<','>(ptr); +static MatchResult parseComma(const char *&ptr, const char *endPtr) { + return parseSingleChar<','>(ptr, endPtr); } -static MatchResult parseInt(unsigned &result, const char *&ptr) { +/// Recognize and parse an unsigned integer. +static MatchResult parseInt(unsigned &result, const char *&ptr, + const char *endPtr) { const char *beg = ptr; - while (*ptr >= '0' && *ptr <= '9') + while (ptr < endPtr && *ptr >= '0' && *ptr <= '9') ptr++; if (beg == ptr) return mlir::failure(); @@ -141,9 +177,9 @@ return mlir::success(); } -static mlir::LogicalResult matchString(const char *&ptr, +static mlir::LogicalResult matchString(const char *&ptr, const char *endPtr, llvm::StringRef literal) { - llvm::StringRef s(ptr); + llvm::StringRef s(ptr, endPtr - ptr); if (s.startswith(literal)) { ptr += literal.size(); return mlir::success(); @@ -151,32 +187,35 @@ return mlir::failure(); } -static MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr) { - if (mlir::succeeded(matchString(ptr, "Half"))) { +/// 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, "BFloat"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwBFloat))) { result = LLVMTypeID::BFloatTyID; return mlir::success(); } - if (mlir::succeeded(matchString(ptr, "Float"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwFloat))) { result = LLVMTypeID::FloatTyID; return mlir::success(); } - if (mlir::succeeded(matchString(ptr, "Double"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwDouble))) { result = LLVMTypeID::DoubleTyID; return mlir::success(); } - if (mlir::succeeded(matchString(ptr, "X86_FP80"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwX86FP80))) { result = LLVMTypeID::X86_FP80TyID; return mlir::success(); } - if (mlir::succeeded(matchString(ptr, "FP128"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwFP128))) { result = LLVMTypeID::FP128TyID; return mlir::success(); } - if (mlir::succeeded(matchString(ptr, "PPC_FP128"))) { + if (mlir::succeeded(matchString(ptr, endPtr, kwPPCFP128))) { result = LLVMTypeID::PPC_FP128TyID; return mlir::success(); } @@ -196,6 +235,9 @@ llvm::ArrayRef 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); @@ -206,28 +248,29 @@ 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) || parseInt(kind, srcPtr)) + 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) || parseInt(bits, srcPtr)) + if (parseColon(srcPtr, endPtr) || parseInt(bits, srcPtr, endPtr)) return badMapString(srcPtr); intMap[std::pair{code, kind}] = bits; } else if (code == 'r' || code == 'c') { LLVMTypeID id{}; - if (parseColon(srcPtr) || parseTypeID(id, srcPtr)) + if (parseColon(srcPtr, endPtr) || parseTypeID(id, srcPtr, endPtr)) return badMapString(srcPtr); floatMap[std::pair{code, kind}] = id; } else { return badMapString(srcPtr); } - if (parseComma(srcPtr)) + if (parseComma(srcPtr, endPtr)) break; } - if (*srcPtr) + if (srcPtr > endPtr) return badMapString(srcPtr); return mlir::success(); } @@ -263,6 +306,51 @@ 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 defs) { if (defs.empty()) { @@ -289,6 +377,52 @@ 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 fir::KindMapping::toDefaultKinds(llvm::StringRef defs) { + std::vector 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()); diff --git a/flang/unittests/Optimizer/CMakeLists.txt b/flang/unittests/Optimizer/CMakeLists.txt --- a/flang/unittests/Optimizer/CMakeLists.txt +++ b/flang/unittests/Optimizer/CMakeLists.txt @@ -6,6 +6,7 @@ ) add_flang_unittest(FlangOptimizerTests + FIRContextTest.cpp InternalNamesTest.cpp KindMappingTest.cpp ) diff --git a/flang/unittests/Optimizer/FIRContextTest.cpp b/flang/unittests/Optimizer/FIRContextTest.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/Optimizer/FIRContextTest.cpp @@ -0,0 +1,57 @@ +//===- FIRContextTest.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 +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Support/FIRContext.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/BuiltinOps.h" +#include "llvm/Support/Host.h" +#include "gtest/gtest.h" +#include + +using namespace fir; + +struct StringAttributesTests : public testing::Test { +public: + void SetUp() { + kindMap = new KindMapping(&context, kindMapInit, "r42a10c14d28i40l41"); + mod = mlir::ModuleOp::create(mlir::UnknownLoc::get(&context)); + } + + void TearDown() { delete kindMap; } + + mlir::MLIRContext context; + KindMapping *kindMap{}; + std::string kindMapInit = + "i10:80,l3:24,a1:8,r54:Double,r62:X86_FP80,r11:PPC_FP128"; + std::string target = "powerpc64le-unknown-linux-gnu"; + mlir::ModuleOp mod; +}; + +TEST_F(StringAttributesTests, moduleStringAttrTest) { + setTargetTriple(mod, target); + setKindMapping(mod, *kindMap); + + auto triple = getTargetTriple(mod); + EXPECT_EQ(triple.getArch(), llvm::Triple::ArchType::ppc64le); + EXPECT_EQ(triple.getOS(), llvm::Triple::OSType::Linux); + + auto map = getKindMapping(mod); + EXPECT_EQ(map.defaultsToString(), "a10c14d28i40l41r42"); + + auto mapStr = map.mapToString(); + EXPECT_EQ(mapStr.size(), kindMapInit.size()); + EXPECT_TRUE(mapStr.find("a1:8") != std::string::npos); + EXPECT_TRUE(mapStr.find("l3:24") != std::string::npos); + EXPECT_TRUE(mapStr.find("i10:80") != std::string::npos); + EXPECT_TRUE(mapStr.find("r11:PPC_FP128") != std::string::npos); + EXPECT_TRUE(mapStr.find("r54:Double") != std::string::npos); + EXPECT_TRUE(mapStr.find("r62:X86_FP80") != std::string::npos); +} + +// main() from gtest_main diff --git a/flang/unittests/Optimizer/InternalNamesTest.cpp b/flang/unittests/Optimizer/InternalNamesTest.cpp --- a/flang/unittests/Optimizer/InternalNamesTest.cpp +++ b/flang/unittests/Optimizer/InternalNamesTest.cpp @@ -47,9 +47,8 @@ } TEST(InternalNamesTest, doBlockDataTest) { - NameUniquer obj; - std::string actual = obj.doBlockData("blockdatatest"); - std::string actualBlank = obj.doBlockData(""); + std::string actual = NameUniquer::doBlockData("blockdatatest"); + std::string actualBlank = NameUniquer::doBlockData(""); std::string expectedMangledName = "_QLblockdatatest"; std::string expectedMangledNameBlank = "_QL"; ASSERT_EQ(actual, expectedMangledName); @@ -57,9 +56,8 @@ } TEST(InternalNamesTest, doCommonBlockTest) { - NameUniquer obj; - std::string actual = obj.doCommonBlock("hello"); - std::string actualBlank = obj.doCommonBlock(""); + std::string actual = NameUniquer::doCommonBlock("hello"); + std::string actualBlank = NameUniquer::doCommonBlock(""); std::string expectedMangledName = "_QBhello"; std::string expectedMangledNameBlank = "_QB"; ASSERT_EQ(actual, expectedMangledName); @@ -67,108 +65,105 @@ } TEST(InternalNamesTest, doGeneratedTest) { - NameUniquer obj; - std::string actual = obj.doGenerated("@MAIN"); + std::string actual = NameUniquer::doGenerated("@MAIN"); std::string expectedMangledName = "_QQ@MAIN"; ASSERT_EQ(actual, expectedMangledName); - std::string actual1 = obj.doGenerated("@_ZNSt8ios_base4InitC1Ev"); + std::string actual1 = NameUniquer::doGenerated("@_ZNSt8ios_base4InitC1Ev"); std::string expectedMangledName1 = "_QQ@_ZNSt8ios_base4InitC1Ev"; ASSERT_EQ(actual1, expectedMangledName1); - std::string actual2 = obj.doGenerated("_QQ@MAIN"); + std::string actual2 = NameUniquer::doGenerated("_QQ@MAIN"); std::string expectedMangledName2 = "_QQ_QQ@MAIN"; ASSERT_EQ(actual2, expectedMangledName2); } TEST(InternalNamesTest, doConstantTest) { - NameUniquer obj; - std::string actual = obj.doConstant({"mod1", "mod2"}, {"foo"}, "Hello"); + std::string actual = + NameUniquer::doConstant({"mod1", "mod2"}, {"foo"}, "Hello"); std::string expectedMangledName = "_QMmod1Smod2FfooEChello"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doProcedureTest) { - NameUniquer obj; - std::string actual = obj.doProcedure({"mod1", "mod2"}, {}, "HeLLo"); + std::string actual = NameUniquer::doProcedure({"mod1", "mod2"}, {}, "HeLLo"); std::string expectedMangledName = "_QMmod1Smod2Phello"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doTypeTest) { - NameUniquer obj; - std::string actual = obj.doType({}, {}, "mytype", {4, -1}); + std::string actual = NameUniquer::doType({}, {}, "mytype", {4, -1}); std::string expectedMangledName = "_QTmytypeK4KN1"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doIntrinsicTypeDescriptorTest) { using IntrinsicType = fir::NameUniquer::IntrinsicType; - NameUniquer obj; std::string actual = - obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::REAL, 42); + NameUniquer::doIntrinsicTypeDescriptor({}, {}, IntrinsicType::REAL, 42); std::string expectedMangledName = "_QCrealK42"; ASSERT_EQ(actual, expectedMangledName); - actual = obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::REAL, {}); + actual = + NameUniquer::doIntrinsicTypeDescriptor({}, {}, IntrinsicType::REAL, {}); expectedMangledName = "_QCrealK0"; ASSERT_EQ(actual, expectedMangledName); - actual = obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::INTEGER, 3); + actual = + NameUniquer::doIntrinsicTypeDescriptor({}, {}, IntrinsicType::INTEGER, 3); expectedMangledName = "_QCintegerK3"; ASSERT_EQ(actual, expectedMangledName); - actual = obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::LOGICAL, 2); + actual = + NameUniquer::doIntrinsicTypeDescriptor({}, {}, IntrinsicType::LOGICAL, 2); expectedMangledName = "_QClogicalK2"; ASSERT_EQ(actual, expectedMangledName); - actual = obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::CHARACTER, 4); + actual = NameUniquer::doIntrinsicTypeDescriptor( + {}, {}, IntrinsicType::CHARACTER, 4); expectedMangledName = "_QCcharacterK4"; ASSERT_EQ(actual, expectedMangledName); - actual = obj.doIntrinsicTypeDescriptor({}, {}, IntrinsicType::COMPLEX, 4); + actual = + NameUniquer::doIntrinsicTypeDescriptor({}, {}, IntrinsicType::COMPLEX, 4); expectedMangledName = "_QCcomplexK4"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doDispatchTableTest) { - NameUniquer obj; - std::string actual = obj.doDispatchTable({}, {}, "MyTYPE", {2, 8, 18}); + std::string actual = + NameUniquer::doDispatchTable({}, {}, "MyTYPE", {2, 8, 18}); std::string expectedMangledName = "_QDTmytypeK2K8K18"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doTypeDescriptorTest) { - NameUniquer obj; - std::string actual = obj.doTypeDescriptor( + std::string actual = NameUniquer::doTypeDescriptor( {StringRef("moD1")}, {StringRef("foo")}, "MyTYPE", {2, 8}); std::string expectedMangledName = "_QMmod1FfooCTmytypeK2K8"; ASSERT_EQ(actual, expectedMangledName); } TEST(InternalNamesTest, doVariableTest) { - NameUniquer obj; - std::string actual = obj.doVariable( + std::string actual = NameUniquer::doVariable( {"mod1", "mod2"}, {""}, "intvar"); // Function is present and is blank. std::string expectedMangledName = "_QMmod1Smod2FEintvar"; ASSERT_EQ(actual, expectedMangledName); - std::string actual2 = obj.doVariable( + std::string actual2 = NameUniquer::doVariable( {"mod1", "mod2"}, {}, "intVariable"); // Function is not present. std::string expectedMangledName2 = "_QMmod1Smod2Eintvariable"; ASSERT_EQ(actual2, expectedMangledName2); } TEST(InternalNamesTest, doProgramEntry) { - NameUniquer obj; - llvm::StringRef actual = obj.doProgramEntry(); + llvm::StringRef actual = NameUniquer::doProgramEntry(); std::string expectedMangledName = "_QQmain"; ASSERT_EQ(actual.str(), expectedMangledName); } TEST(InternalNamesTest, deconstructTest) { - NameUniquer obj; - std::pair actual = obj.deconstruct("_QBhello"); + std::pair actual = NameUniquer::deconstruct("_QBhello"); auto expectedNameKind = NameUniquer::NameKind::COMMON; struct DeconstructedName expectedComponents { {}, {}, "hello", {} @@ -178,39 +173,38 @@ TEST(InternalNamesTest, complexdeconstructTest) { using NameKind = fir::NameUniquer::NameKind; - NameUniquer obj; - std::pair actual = obj.deconstruct("_QMmodSs1modSs2modFsubPfun"); + std::pair actual = NameUniquer::deconstruct("_QMmodSs1modSs2modFsubPfun"); auto expectedNameKind = NameKind::PROCEDURE; struct DeconstructedName expectedComponents = { {"mod", "s1mod", "s2mod"}, {"sub"}, "fun", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QPsub"); + actual = NameUniquer::deconstruct("_QPsub"); expectedNameKind = NameKind::PROCEDURE; expectedComponents = {{}, {}, "sub", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QBvariables"); + actual = NameUniquer::deconstruct("_QBvariables"); expectedNameKind = NameKind::COMMON; expectedComponents = {{}, {}, "variables", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QMmodEintvar"); + actual = NameUniquer::deconstruct("_QMmodEintvar"); expectedNameKind = NameKind::VARIABLE; expectedComponents = {{"mod"}, {}, "intvar", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QMmodECpi"); + actual = NameUniquer::deconstruct("_QMmodECpi"); expectedNameKind = NameKind::CONSTANT; expectedComponents = {{"mod"}, {}, "pi", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QTyourtypeK4KN6"); + actual = NameUniquer::deconstruct("_QTyourtypeK4KN6"); expectedNameKind = NameKind::DERIVED_TYPE; expectedComponents = {{}, {}, "yourtype", {4, -6}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); - actual = obj.deconstruct("_QDTt"); + actual = NameUniquer::deconstruct("_QDTt"); expectedNameKind = NameKind::DISPATCH_TABLE; expectedComponents = {{}, {}, "t", {}}; validateDeconstructedName(actual, expectedNameKind, expectedComponents); diff --git a/flang/unittests/Optimizer/KindMappingTest.cpp b/flang/unittests/Optimizer/KindMappingTest.cpp --- a/flang/unittests/Optimizer/KindMappingTest.cpp +++ b/flang/unittests/Optimizer/KindMappingTest.cpp @@ -176,19 +176,19 @@ } TEST_F(KindDefaultsTests, getIntegerBitsizeTest) { - EXPECT_EQ(defaultDefaultKinds->defaultCharacterKind(), 1u); - EXPECT_EQ(defaultDefaultKinds->defaultComplexKind(), 4u); - EXPECT_EQ(defaultDefaultKinds->defaultDoubleKind(), 8u); - EXPECT_EQ(defaultDefaultKinds->defaultIntegerKind(), 4u); - EXPECT_EQ(defaultDefaultKinds->defaultLogicalKind(), 4u); - EXPECT_EQ(defaultDefaultKinds->defaultRealKind(), 4u); - - EXPECT_EQ(overrideDefaultKinds->defaultCharacterKind(), 20u); - EXPECT_EQ(overrideDefaultKinds->defaultComplexKind(), 121u); - EXPECT_EQ(overrideDefaultKinds->defaultDoubleKind(), 32u); - EXPECT_EQ(overrideDefaultKinds->defaultIntegerKind(), 133u); - EXPECT_EQ(overrideDefaultKinds->defaultLogicalKind(), 44u); - EXPECT_EQ(overrideDefaultKinds->defaultRealKind(), 145u); + EXPECT_EQ(defaultDefaultKinds->defaultCharacterKind(), 1u); + EXPECT_EQ(defaultDefaultKinds->defaultComplexKind(), 4u); + EXPECT_EQ(defaultDefaultKinds->defaultDoubleKind(), 8u); + EXPECT_EQ(defaultDefaultKinds->defaultIntegerKind(), 4u); + EXPECT_EQ(defaultDefaultKinds->defaultLogicalKind(), 4u); + EXPECT_EQ(defaultDefaultKinds->defaultRealKind(), 4u); + + EXPECT_EQ(overrideDefaultKinds->defaultCharacterKind(), 20u); + EXPECT_EQ(overrideDefaultKinds->defaultComplexKind(), 121u); + EXPECT_EQ(overrideDefaultKinds->defaultDoubleKind(), 32u); + EXPECT_EQ(overrideDefaultKinds->defaultIntegerKind(), 133u); + EXPECT_EQ(overrideDefaultKinds->defaultLogicalKind(), 44u); + EXPECT_EQ(overrideDefaultKinds->defaultRealKind(), 145u); } // main() from gtest_main