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,11 @@ /// Options controlling language dialect. Fortran::frontend::LangOptions langOpts; + // The original invocation of the compiler driver. + // This string will be set as the return value from the COMPILER_OPTIONS + // intrinsic of iso_fortran_env. + std::string allCompilerInvocOpts; + // Semantics context std::unique_ptr semanticsContext; @@ -208,7 +213,8 @@ /// \param [out] res - The resulting invocation. static bool createFromArgs(CompilerInvocation &res, llvm::ArrayRef commandLineArgs, - clang::DiagnosticsEngine &diags); + clang::DiagnosticsEngine &diags, + const char *argv0 = nullptr); // Enables the std=f2018 conformance check void setEnableConformanceChecks() { enableConformanceChecks = true; } 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 @@ -916,6 +916,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 @@ -2070,6 +2072,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()}) { @@ -2158,7 +2166,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 @@ -869,7 +871,7 @@ bool CompilerInvocation::createFromArgs( CompilerInvocation &res, llvm::ArrayRef commandLineArgs, - clang::DiagnosticsEngine &diags) { + clang::DiagnosticsEngine &diags, const char *argv0) { bool success = true; @@ -929,6 +931,23 @@ success &= parseFloatingPointArgs(res, args, diags); + // Set the string to be used as the return value of the COMPILER_OPTIONS + // intrinsic of iso_fortran_env. This is either passed in from the parent + // compiler driver invocation with an environment variable, or failing that + // set to the command line arguments of the frontend driver invocation. + res.allCompilerInvocOpts = std::string(); + llvm::raw_string_ostream os(res.allCompilerInvocOpts); + char *compilerOptsEnv = std::getenv("FLANG_COMPILER_OPTIONS_STRING"); + if (compilerOptsEnv != nullptr) { + os << compilerOptsEnv; + } else { + os << argv0 << ' '; + for (auto it = commandLineArgs.begin(), e = commandLineArgs.end(); it != e; + ++it) { + os << ' ' << *it; + } + } + return success; } @@ -1078,6 +1097,11 @@ semanticsContext->targetCharacteristics().DisableType( Fortran::common::TypeCategory::Real, /*kind=*/10); } + + std::string version = Fortran::common::getFlangFullVersion(); + semanticsContext->targetCharacteristics() + .set_compilerOptionsString(allCompilerInvocOpts) + .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 @@ -73,4 +73,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/Driver/compiler_options.f90 b/flang/test/Driver/compiler_options.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/compiler_options.f90 @@ -0,0 +1,13 @@ +! RUN: %flang -S -emit-llvm -o - %s | FileCheck %s +! Test communication of COMPILER_OPTIONS from flang-new to flang-new -fc1. +! CHECK: [[OPTSVAR:@_QQcl\.[0-9a-f]+]] = linkonce constant [[[OPTSLEN:[0-9]+]] x i8] c"{{.*}}flang-new{{(\.exe)?}} -S -emit-llvm -o - {{.*}}compiler_options.f90" +program main + use ISO_FORTRAN_ENV, only: compiler_options + implicit none + character (len = :), allocatable :: v +! CHECK: call void @llvm.memmove.p0.p0.i64(ptr %16, ptr [[OPTSVAR]], i64 [[OPTSLEN]], i1 false) + v = compiler_options() + print *, v + deallocate(v) + close(1) +end program main diff --git a/flang/test/Evaluate/compiler_options_fc1.f90 b/flang/test/Evaluate/compiler_options_fc1.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Evaluate/compiler_options_fc1.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,28 @@ llvm::SmallVector, 4> failingCommands; + // Set the environment variable, FLANG_COMPILER_OPTIONS_STRING, to contain all + // the compiler options. This is intended for the frontend driver, + // flang-new -fc1, to enable the implementation of the COMPILER_OPTIONS + // intrinsic. To this end, the frontend driver requires the list of the + // original compiler options, which is not available through other means. + // TODO: This way of passing information between the compiler and frontend + // drivers is discouraged. We should find a better way not involving env + // variables. + std::string compilerOptsGathered; + llvm::raw_string_ostream os(compilerOptsGathered); + for (int i = 0; i < argc; ++i) { + os << argv[i]; + if (i < argc - 1) { + os << ' '; + } + } +#ifdef _WIN32 + _putenv_s("FLANG_COMPILER_OPTIONS_STRING", compilerOptsGathered.c_str()); +#else + setenv("FLANG_COMPILER_OPTIONS_STRING", compilerOptsGathered.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, diags, argv0); // Initialize targets first, so that --version shows registered targets. llvm::InitializeAllTargets();