Flang implementation for COMPILER_VERSION and COMPILER_OPTIONS intrinsics

This revision implements the Fortran intrinsic procedures COMPILER_VERSION and COMPILER_OPTIONS from the iso_fortran_env module.
To be able to set the COMPILER_OPTIONS string according to the original compiler driver invocation, a string is passed to the frontend driver using the environment variable FLANG_COMPILER_OPTIONS_STRING, for lack of a better mechanism.

Fixes #59233

Reviewed By: awarzynski

Differential Revision: https://reviews.llvm.org/D140524
This commit is contained in:
Hussain Kadhem 2023-06-01 12:31:51 -04:00
parent 2129cc1b3a
commit 541f5c4a6d
15 changed files with 328 additions and 16 deletions

View File

@ -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

View File

@ -75,6 +75,22 @@ public:
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 @@ private:
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

View File

@ -82,6 +82,11 @@ class CompilerInvocation : public CompilerInvocationBase {
/// 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<Fortran::semantics::SemanticsContext> semanticsContext;
@ -208,7 +213,8 @@ public:
/// \param [out] res - The resulting invocation.
static bool createFromArgs(CompilerInvocation &res,
llvm::ArrayRef<const char *> commandLineArgs,
clang::DiagnosticsEngine &diags);
clang::DiagnosticsEngine &diags,
const char *argv0 = nullptr);
// Enables the std=f2018 conformance check
void setEnableConformanceChecks() { enableConformanceChecks = true; }

View File

@ -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

View File

@ -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 <cstdlib>
#include <cstring>
#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

View File

@ -118,6 +118,12 @@ Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
return Expr<T>{Constant<T>{
CharacterUtils<KIND>::TRIM(std::get<Scalar<T>>(*scalar))}};
}
} else if (name == "__builtin_compiler_options") {
auto &o = context.targetCharacteristics().compilerOptionsString();
return Expr<T>{Constant<T>{StringType(o.begin(), o.end())}};
} else if (name == "__builtin_compiler_version") {
auto &v = context.targetCharacteristics().compilerVersionString();
return Expr<T>{Constant<T>{StringType(v.begin(), v.end())}};
}
return Expr<T>{std::move(funcRef)};
}

View File

@ -916,6 +916,8 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"__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 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
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<DynamicType> aType{sameArg->GetType()}) {
@ -2158,7 +2166,6 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
case KindCode::exactKind:
resultType = DynamicType{*category, result.exactKindValue};
break;
case KindCode::defaultCharKind:
case KindCode::typeless:
case KindCode::any:
case KindCode::kindArg:

View File

@ -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 <cstdlib>
#include <memory>
#include <optional>
@ -869,7 +871,7 @@ static bool parseFloatingPointArgs(CompilerInvocation &invoc,
bool CompilerInvocation::createFromArgs(
CompilerInvocation &res, llvm::ArrayRef<const char *> commandLineArgs,
clang::DiagnosticsEngine &diags) {
clang::DiagnosticsEngine &diags, const char *argv0) {
bool success = true;
@ -929,6 +931,23 @@ bool CompilerInvocation::createFromArgs(
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 @@ void CompilerInvocation::setSemanticsOpts(
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

View File

@ -73,4 +73,6 @@ module __Fortran_builtins
type(__builtin_team_type) :: team_type
end type
intrinsic :: __builtin_compiler_options, __builtin_compiler_version
end module

View File

@ -18,7 +18,9 @@ module iso_fortran_env
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 @@ module iso_fortran_env
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <stdlib.h>
using llvm::StringRef;
@ -135,6 +137,28 @@ int main(int argc, const char **argv) {
llvm::SmallVector<std::pair<int, const clang::driver::Command *>, 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;

View File

@ -50,8 +50,8 @@ int fc1_main(llvm::ArrayRef<const char *> argv, const char *argv0) {
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> 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();