diff --git a/flang/include/flang/Common/Version.h b/flang/include/flang/Common/Version.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Common/Version.h @@ -0,0 +1,56 @@ +//===- Version.h - Flang Version Number ---------------------*- Fortran -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines version macros and version-related utility functions +/// for Flang. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FLANG_COMMON_VERSION_H +#define LLVM_FLANG_COMMON_VERSION_H + +#include "flang/Version.inc" +#include "llvm/ADT/StringRef.h" + +namespace Fortran::common { +/// Retrieves the repository path (e.g., Git path) that +/// identifies the particular Flang branch, tag, or trunk from which this +/// Flang was built. +std::string getFlangRepositoryPath(); + +/// Retrieves the repository path from which LLVM was built. +/// +/// This supports LLVM residing in a separate repository from flang. +std::string getLLVMRepositoryPath(); + +/// Retrieves the repository revision number (or identifier) from which +/// this Flang was built. +std::string getFlangRevision(); + +/// Retrieves the repository revision number (or identifier) from which +/// LLVM was built. +/// +/// If Flang and LLVM are in the same repository, this returns the same +/// string as getFlangRevision. +std::string getLLVMRevision(); + +/// Retrieves the full repository version that is an amalgamation of +/// the information in getFlangRepositoryPath() and getFlangRevision(). +std::string getFlangFullRepositoryVersion(); + +/// Retrieves a string representing the complete flang version, +/// which includes the flang version number, the repository version, +/// and the vendor tag. +std::string getFlangFullVersion(); + +/// Like getFlangFullVersion(), but with a custom tool name. +std::string getFlangToolFullVersion(llvm::StringRef ToolName); +} // namespace Fortran::common + +#endif // LLVM_FLANG_COMMON_VERSION_H diff --git a/flang/include/flang/Evaluate/target.h b/flang/include/flang/Evaluate/target.h --- a/flang/include/flang/Evaluate/target.h +++ b/flang/include/flang/Evaluate/target.h @@ -75,6 +75,22 @@ static Rounding defaultRounding; + const std::string &compilerOptionsString() const { + return compilerOptionsString_; + }; + TargetCharacteristics &set_compilerOptionsString(std::string x) { + compilerOptionsString_ = x; + return *this; + } + + const std::string &compilerVersionString() const { + return compilerVersionString_; + }; + TargetCharacteristics &set_compilerVersionString(std::string x) { + compilerVersionString_ = x; + return *this; + } + private: static constexpr int maxKind{32}; std::uint8_t byteSize_[common::TypeCategory_enumSize][maxKind]{}; @@ -87,6 +103,8 @@ std::size_t descriptorAlignment_{8}; std::size_t maxByteSize_{8 /*at least*/}; std::size_t maxAlignment_{8 /*at least*/}; + std::string compilerOptionsString_; + std::string compilerVersionString_; }; } // namespace Fortran::evaluate diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h --- a/flang/include/flang/Frontend/CompilerInvocation.h +++ b/flang/include/flang/Frontend/CompilerInvocation.h @@ -82,6 +82,8 @@ /// Options controlling language dialect. Fortran::frontend::LangOptions langOpts; + std::string compilerOptionsString; + // Semantics context std::unique_ptr semanticsContext; @@ -204,6 +206,7 @@ /// \param [out] res - The resulting invocation. static bool createFromArgs(CompilerInvocation &res, llvm::ArrayRef commandLineArgs, + const char *commandLineArg0, clang::DiagnosticsEngine &diags); // Enables the std=f2018 conformance check diff --git a/flang/lib/Common/CMakeLists.txt b/flang/lib/Common/CMakeLists.txt --- a/flang/lib/Common/CMakeLists.txt +++ b/flang/lib/Common/CMakeLists.txt @@ -1,9 +1,45 @@ +find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc) +find_first_existing_vc_file("${FLANG_SOURCE_DIR}" flang_vc) + +# The VC revision include that we want to generate. +set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc") + +set(generate_vcs_version_script "${LLVM_CMAKE_DIR}/GenerateVersionFromVCS.cmake") + +if(llvm_vc AND LLVM_APPEND_VC_REV) + set(llvm_source_dir ${LLVM_MAIN_SRC_DIR}) +endif() +if(flang_vc AND LLVM_APPEND_VC_REV) + set(flang_source_dir ${FLANG_SOURCE_DIR}) +endif() + +# Create custom target to generate the VC revision include. +add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${llvm_vc}" "${flang_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=\"LLVM;FLANG\"" + "-DLLVM_SOURCE_DIR=${llvm_source_dir}" + "-DFLANG_SOURCE_DIR=${flang_source_dir}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") + +# Mark the generated header as being generated. +set_source_files_properties("${version_inc}" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) + +if(FLANG_VENDOR) + set_source_files_properties(Version.cpp + PROPERTIES COMPILE_DEFINITIONS "FLANG_VENDOR=\"${FLANG_VENDOR} \"") +endif() + add_flang_library(FortranCommon Fortran.cpp Fortran-features.cpp default-kinds.cpp idioms.cpp + Version.cpp + ${version_inc} LINK_COMPONENTS Support diff --git a/flang/lib/Common/Version.cpp b/flang/lib/Common/Version.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Common/Version.cpp @@ -0,0 +1,104 @@ +//===- Version.cpp - Flang Version Number -------------------*- Fortran -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines several version-related utility functions for Flang. +// +//===----------------------------------------------------------------------===// + +#include "flang/Common/Version.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +#include "VCSVersion.inc" + +namespace Fortran::common { + +std::string getFlangRepositoryPath() { +#if defined(FLANG_REPOSITORY_STRING) + return FLANG_REPOSITORY_STRING; +#else +#ifdef FLANG_REPOSITORY + return FLANG_REPOSITORY; +#else + return ""; +#endif +#endif +} + +std::string getLLVMRepositoryPath() { +#ifdef LLVM_REPOSITORY + return LLVM_REPOSITORY; +#else + return ""; +#endif +} + +std::string getFlangRevision() { +#ifdef FLANG_REVISION + return FLANG_REVISION; +#else + return ""; +#endif +} + +std::string getLLVMRevision() { +#ifdef LLVM_REVISION + return LLVM_REVISION; +#else + return ""; +#endif +} + +std::string getFlangFullRepositoryVersion() { + std::string buf; + llvm::raw_string_ostream OS(buf); + std::string Path = getFlangRepositoryPath(); + std::string Revision = getFlangRevision(); + if (!Path.empty() || !Revision.empty()) { + OS << '('; + if (!Path.empty()) + OS << Path; + if (!Revision.empty()) { + if (!Path.empty()) + OS << ' '; + OS << Revision; + } + OS << ')'; + } + // Support LLVM in a separate repository. + std::string LLVMRev = getLLVMRevision(); + if (!LLVMRev.empty() && LLVMRev != Revision) { + OS << " ("; + std::string LLVMRepo = getLLVMRepositoryPath(); + if (!LLVMRepo.empty()) + OS << LLVMRepo << ' '; + OS << LLVMRev << ')'; + } + return buf; +} + +std::string getFlangFullVersion() { return getFlangToolFullVersion("flang"); } + +std::string getFlangToolFullVersion(llvm::StringRef ToolName) { + std::string buf; + llvm::raw_string_ostream OS(buf); +#ifdef FLANG_VENDOR + OS << FLANG_VENDOR; +#endif + OS << ToolName << " version " FLANG_VERSION_STRING; + + std::string repo = getFlangFullRepositoryVersion(); + if (!repo.empty()) { + OS << " " << repo; + } + + return buf; +} + +} // end namespace Fortran::common diff --git a/flang/lib/Evaluate/fold-character.cpp b/flang/lib/Evaluate/fold-character.cpp --- a/flang/lib/Evaluate/fold-character.cpp +++ b/flang/lib/Evaluate/fold-character.cpp @@ -118,6 +118,12 @@ return Expr{Constant{ CharacterUtils::TRIM(std::get>(*scalar))}}; } + } else if (name == "__builtin_compiler_options") { + auto &o = context.targetCharacteristics().compilerOptionsString(); + return Expr{Constant{StringType(o.begin(), o.end())}}; + } else if (name == "__builtin_compiler_version") { + auto &v = context.targetCharacteristics().compilerVersionString(); + return Expr{Constant{StringType(v.begin(), v.end())}}; } return Expr{std::move(funcRef)}; } diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -913,6 +913,8 @@ {"__builtin_ieee_support_underflow_control", {{"x", AnyReal, Rank::elemental, Optionality::optional}}, DefaultLogical}, + {"__builtin_compiler_options", {}, DefaultChar}, + {"__builtin_compiler_version", {}, DefaultChar}, }; // TODO: Coarray intrinsic functions @@ -2058,6 +2060,12 @@ resultType = DynamicType{TypeCategory::Logical, defaults.GetDefaultKind(TypeCategory::Logical)}; break; + case KindCode::defaultCharKind: + CHECK(result.categorySet == CharType); + CHECK(*category == TypeCategory::Character); + resultType = DynamicType{TypeCategory::Character, + defaults.GetDefaultKind(TypeCategory::Character)}; + break; case KindCode::same: CHECK(sameArg); if (std::optional aType{sameArg->GetType()}) { @@ -2146,7 +2154,6 @@ case KindCode::exactKind: resultType = DynamicType{*category, result.exactKindValue}; break; - case KindCode::defaultCharKind: case KindCode::typeless: case KindCode::any: case KindCode::kindArg: diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -12,6 +12,7 @@ #include "flang/Frontend/CompilerInvocation.h" #include "flang/Common/Fortran-features.h" +#include "flang/Common/Version.h" #include "flang/Frontend/CodeGenOptions.h" #include "flang/Frontend/PreprocessorOptions.h" #include "flang/Frontend/TargetOptions.h" @@ -36,6 +37,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Triple.h" +#include #include #include @@ -815,7 +817,7 @@ bool CompilerInvocation::createFromArgs( CompilerInvocation &res, llvm::ArrayRef commandLineArgs, - clang::DiagnosticsEngine &diags) { + const char *commandLineArg0, clang::DiagnosticsEngine &diags) { bool success = true; @@ -875,6 +877,19 @@ success &= parseFloatingPointArgs(res, args, diags); + res.compilerOptionsString = std::string(); + llvm::raw_string_ostream os(res.compilerOptionsString); + char *compiler_options_string = std::getenv("FLANG_COMPILER_OPTIONS_STRING"); + if (compiler_options_string != nullptr) { + os << compiler_options_string; + } else { + os << commandLineArg0 << ' '; + for (auto it = commandLineArgs.begin(), e = commandLineArgs.end(); it != e; + ++it) { + os << ' ' << *it; + } + } + return success; } @@ -1023,6 +1038,11 @@ semanticsContext->targetCharacteristics().DisableType( Fortran::common::TypeCategory::Real, /*kind=*/10); } + + std::string version = Fortran::common::getFlangFullVersion(); + semanticsContext->targetCharacteristics() + .set_compilerOptionsString(compilerOptionsString) + .set_compilerVersionString(version); } /// Set \p loweringOptions controlling lowering behavior based diff --git a/flang/module/__fortran_builtins.f90 b/flang/module/__fortran_builtins.f90 --- a/flang/module/__fortran_builtins.f90 +++ b/flang/module/__fortran_builtins.f90 @@ -65,4 +65,6 @@ type(__builtin_team_type) :: team_type end type + intrinsic :: __builtin_compiler_options, __builtin_compiler_version + end module diff --git a/flang/module/iso_fortran_env.f90 b/flang/module/iso_fortran_env.f90 --- a/flang/module/iso_fortran_env.f90 +++ b/flang/module/iso_fortran_env.f90 @@ -18,7 +18,9 @@ lock_type => __builtin_lock_type, & team_type => __builtin_team_type, & atomic_int_kind => __builtin_atomic_int_kind, & - atomic_logical_kind => __builtin_atomic_logical_kind + atomic_logical_kind => __builtin_atomic_logical_kind, & + compiler_options => __builtin_compiler_options, & + compiler_version => __builtin_compiler_version implicit none @@ -145,14 +147,4 @@ integer, parameter :: stat_unlocked = FORTRAN_RUNTIME_STAT_UNLOCKED integer, parameter :: stat_unlocked_failed_image = FORTRAN_RUNTIME_STAT_UNLOCKED_FAILED_IMAGE - interface compiler_options - character(len=80) function compiler_options_1() - end function compiler_options_1 - end interface compiler_options - - interface compiler_version - character(len=80) function compiler_version_1() - end function compiler_version_1 - end interface compiler_version - end module iso_fortran_env diff --git a/flang/test/Evaluate/compiler_options.f90 b/flang/test/Evaluate/compiler_options.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Evaluate/compiler_options.f90 @@ -0,0 +1,12 @@ +! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s + +program main + use ISO_FORTRAN_ENV, only: compiler_options + implicit none + character (len = :), allocatable :: v +! CHECK: v="{{.*}}flang{{.*}} -fdebug-unparse {{.*}}" + v = compiler_options() + print *, v + deallocate(v) + close(1) +end program main diff --git a/flang/test/Evaluate/compiler_version.f90 b/flang/test/Evaluate/compiler_version.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Evaluate/compiler_version.f90 @@ -0,0 +1,12 @@ +! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s + +program main + use ISO_FORTRAN_ENV, only: compiler_version + implicit none + character (len = :), allocatable :: v +! CHECK: v="flang version {{.*}}" + v = compiler_version() + print *, v + deallocate(v) + close(1) +end program main diff --git a/flang/tools/flang-driver/driver.cpp b/flang/tools/flang-driver/driver.cpp --- a/flang/tools/flang-driver/driver.cpp +++ b/flang/tools/flang-driver/driver.cpp @@ -28,7 +28,9 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" +#include using llvm::StringRef; @@ -135,6 +137,20 @@ llvm::SmallVector, 4> failingCommands; + // Pass the COMPILER_OPTIONS string by environment variable. + std::string compiler_options_string; + llvm::raw_string_ostream os(compiler_options_string); + for (int i = 0; i < argc; ++i) { + os << argv[i]; + if (i < argc - 1) { + os << ' '; + } + } +#ifdef _WIN32 + _putenv_s("FLANG_COMPILER_OPTIONS_STRING", compiler_options_string.c_str()); +#else + setenv("FLANG_COMPILER_OPTIONS_STRING", compiler_options_string.c_str(), 1); +#endif // Run the driver int res = 1; bool isCrash = false; diff --git a/flang/tools/flang-driver/fc1_main.cpp b/flang/tools/flang-driver/fc1_main.cpp --- a/flang/tools/flang-driver/fc1_main.cpp +++ b/flang/tools/flang-driver/fc1_main.cpp @@ -50,8 +50,8 @@ llvm::IntrusiveRefCntPtr diagOpts = new clang::DiagnosticOptions(); clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagsBuffer); - bool success = - CompilerInvocation::createFromArgs(flang->getInvocation(), argv, diags); + bool success = CompilerInvocation::createFromArgs(flang->getInvocation(), + argv, argv0, diags); // Initialize targets first, so that --version shows registered targets. llvm::InitializeAllTargets();