Skip to content

Commit

Permalink
[Flang] [FlangRT] Introduce FlangRT project as solution to Flang's ru…
Browse files Browse the repository at this point in the history
…ntime LLVM integration

See discourse thread https://discourse.llvm.org/t/rfc-support-cmake-option-to-control-link-type-built-for-flang-runtime-libraries/71602/18 for full details.

Flang-rt is the new library target for the flang runtime libraries. It builds the Flang-rt library (which contains the sources of FortranRuntime and FortranDecimal) and the Fortran_main library. See documentation in this patch for detailed description (flang-rt/docs/GettingStarted.md).

This patch aims to:
- integrate Flang's runtime into existing llvm infrasturcture so that Flang's runtime can be built similarly to other runtimes via the runtimes target or via the llvm target as an enabled runtime
- decouple the FortranDecimal library sources that were used by both compiler and runtime so that different build configurations can be applied for compiler vs runtime
- add support for running flang-rt testsuites, which were created by migrating relevant tests from `flang/test` and `flang/unittest` to `flang-rt/test` and `flang-rt/unittest`, using a new `check-flang-rt` target.
- provide documentation on how to build and use the new FlangRT runtime

Reviewed By: DanielCChen

Differential Revision: https://reviews.llvm.org/D154869
  • Loading branch information
pscoro authored and madanial0 committed Sep 30, 2023
1 parent d222c5e commit 6403287
Show file tree
Hide file tree
Showing 66 changed files with 1,294 additions and 245 deletions.
40 changes: 26 additions & 14 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,29 +969,41 @@ void tools::addFortranRuntimeLibs(const ToolChain &TC,
llvm::opt::ArgStringList &CmdArgs) {
if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
CmdArgs.push_back("Fortran_main.lib");
CmdArgs.push_back("FortranRuntime.lib");
CmdArgs.push_back("FortranDecimal.lib");
CmdArgs.push_back("flang-rt.lib");
} else {
CmdArgs.push_back("-lFortran_main");
CmdArgs.push_back("-lFortranRuntime");
CmdArgs.push_back("-lFortranDecimal");
CmdArgs.push_back("-lflang-rt");
}
}

void tools::addFortranRuntimeLibraryPath(const ToolChain &TC,
const llvm::opt::ArgList &Args,
ArgStringList &CmdArgs) {
// Default to the <driver-path>/../lib directory. This works fine on the
// platforms that we have tested so far. We will probably have to re-fine
// this in the future. In particular, on some platforms, we may need to use
// lib64 instead of lib.
SmallString<256> DefaultLibPath =
// Default to the <driver-path>/../lib, <driver-path>/../flang-rt/lib, and
// <driver-path>/../runtimes/runtimes-bins/flang-rt/lib directories. This
// works fine on the platforms that we have tested so far. We will probably
// have to re-fine this in the future. In particular, on some platforms, we
// may need to use lib64 instead of lib.
SmallString<256> BuildLibPath =
llvm::sys::path::parent_path(TC.getDriver().Dir);
SmallString<256> FlangRTLibPath =
llvm::sys::path::parent_path(TC.getDriver().Dir);
SmallString<256> RuntimesLibPath =
llvm::sys::path::parent_path(TC.getDriver().Dir);
llvm::sys::path::append(DefaultLibPath, "lib");
if (TC.getTriple().isKnownWindowsMSVCEnvironment())
CmdArgs.push_back(Args.MakeArgString("-libpath:" + DefaultLibPath));
else
CmdArgs.push_back(Args.MakeArgString("-L" + DefaultLibPath));
// Search path for Fortran_main and Flang-rt libraries.
llvm::sys::path::append(BuildLibPath, "lib");
llvm::sys::path::append(FlangRTLibPath, "flang-rt/lib");
llvm::sys::path::append(RuntimesLibPath,
"runtimes/runtimes-bins/flang-rt/lib");
if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
CmdArgs.push_back(Args.MakeArgString("-libpath:" + BuildLibPath));
CmdArgs.push_back(Args.MakeArgString("-libpath:" + FlangRTLibPath));
CmdArgs.push_back(Args.MakeArgString("-libpath:" + RuntimesLibPath));
} else {
CmdArgs.push_back(Args.MakeArgString("-L" + BuildLibPath));
CmdArgs.push_back(Args.MakeArgString("-L" + FlangRTLibPath));
CmdArgs.push_back(Args.MakeArgString("-L" + RuntimesLibPath));
}
}

static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args,
Expand Down
148 changes: 148 additions & 0 deletions flang-rt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# CMake build for the Flang runtime libraries
# The source for the flang runtime libraries (FortranDecimalRT, FortranRuntime)
# exist in the flang top-level directory.
# Flang-rt is only scaffolding and does not provide any additional source files.

cmake_minimum_required(VERSION 3.20.0)

#===============================================================================
# Configure CMake
#===============================================================================
set(LLVM_COMMON_CMAKE_UTILS "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
include(${LLVM_COMMON_CMAKE_UTILS}/Modules/CMakePolicy.cmake
NO_POLICY_SCOPE)

set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/flang-rt/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/flang-rt/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/flang-rt/lib)
set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_BINARY_DIR}/flang-rt)

set(FLANG_RT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(FLANG_RT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")

#===============================================================================
# Setup Options and Defaults
#===============================================================================
option(FLANG_RT_ENABLE_SHARED "Build flang-rt as a shared library." OFF)
option(FLANG_RT_ENABLE_STATIC "Build flang-rt as a static library." OFF)

option(FLANG_RT_INCLUDE_TESTS
"Generate build targets for the Flang-rt unit tests." ${LLVM_INCLUDE_TESTS})

# MLIR_DIR must be passed on invocation of flang-rt because it is needed for the Flang package.
if(NOT DEFINED MLIR_DIR OR MLIR_DIR STREQUAL "")
message(FATAL_ERROR "MLIR_DIR must be set to the directory of the MLIRConfig cmake file in order to find the MLIR package.")
endif()
# Flang-rt requires a pre-built/installed version of flang that requires MLIR.
find_package(MLIR REQUIRED HINTS "${MLIR_DIR}")

# FLANG_DIR must be passed on invocation of flang-rt.
if(NOT DEFINED FLANG_DIR OR FLANG_DIR STREQUAL "")
message(FATAL_ERROR "FLANG_DIR must be set to the directory of the FlangConfig cmake file in order to find the Flang package.")
endif()
# Flang-rt requires a pre-built/installed version of flang.
# Flang-rt uses flang/Common headers.
# Finding this package exposes FLANG_SOURCE_DIR, FLANG_BINARY_DIR, and FLANG_INCLUDE_DIRS to Flang-rt
find_package(Flang REQUIRED HINTS "${FLANG_DIR}")
# If the user specifies a relative path to LLVM_DIR, the calls to include
# LLVM modules fail. Append the absolute path to LLVM_DIR instead.
get_filename_component(FLANG_DIR_ABSOLUTE ${FLANG_DIR} REALPATH)
list(APPEND CMAKE_MODULE_PATH ${FLANG_DIR_ABSOLUTE})

set(LLVM_COMMON_CMAKE_UTILS "${FLANG_RT_SOURCE_DIR}/../cmake")
set(LLVM_CMAKE_UTILS "${LLVM_BUILD_MAIN_SOURCE_DIR}/cmake")
set(CLANG_CMAKE_UTILS "${FLANG_RT_SOURCE_DIR}/../clang/cmake")

if (FLANG_RT_INCLUDE_TESTS)
# LLVM_DIR must be passed on invocation of flang-rt when tests are enabled.
if(NOT DEFINED LLVM_DIR OR LLVM_DIR STREQUAL "")
message(FATAL_ERROR "LLVM_DIR must be set to the directory of the LLVMConfig cmake file in order to find the LLVM package.")
endif()
# We need a pre-built/installed version of LLVM for gtest.
find_package(LLVM REQUIRED HINTS "${LLVM_DIR}")
# If the user specifies a relative path to LLVM_DIR, the calls to include
# LLVM modules fail. Append the absolute path to LLVM_DIR instead.
get_filename_component(LLVM_DIR_ABSOLUTE ${LLVM_DIR} REALPATH)
list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR_ABSOLUTE})

add_compile_definitions(FLANG_RT_INCLUDE_TESTS=1)

if (DEFINED FLANG_BINARY_DIR)
set(FLANG_BINARY_DIR ${FLANG_BINARY_DIR} CACHE PATH "Path to the Flang build directory" FORCE)
else()
message(FATAL_ERROR "FLANG_BINARY_DIR must be defined or passed by the user when building tests.")
endif()
set(FLANG_RT_TEST_COMPILER ${FLANG_BINARY_DIR}/bin/flang-new
CACHE PATH "Compiler to use for testing")
set(FLANG_RT_GTEST_AVAIL 1)
endif()

# Add path for custom modules
list(INSERT CMAKE_MODULE_PATH 0
"${FLANG_SOURCE_DIR}/cmake"
"${FLANG_SOURCE_DIR}/cmake/modules"
"${CLANG_CMAKE_UTILS}/modules"
"${LLVM_CMAKE_UTILS}/modules"
)

include(AddClang)
include(AddFlang)
include(TestBigEndian)
test_big_endian(IS_BIGENDIAN)
if (IS_BIGENDIAN)
add_compile_definitions(FLANG_BIG_ENDIAN=1)
else ()
add_compile_definitions(FLANG_LITTLE_ENDIAN=1)
endif ()

# Flang's include directories are needed for flang/Common.
include_directories(SYSTEM ${FLANG_INCLUDE_DIRS})

# LLVM's include directories are needed for gtest.
include_directories(SYSTEM ${LLVM_INCLUDE_DIRS})

#===============================================================================
# Add Subdirectories
#===============================================================================
set(FORTRAN_DECIMAL_SRC "${FLANG_SOURCE_DIR}/lib/Decimal")
set(FORTRAN_RUNTIME_SRC "${FLANG_SOURCE_DIR}/runtime")
set(FORTRAN_MAIN_SRC "${FLANG_SOURCE_DIR}/runtime/FortranMain")

add_subdirectory(${FORTRAN_DECIMAL_SRC} FortranDecimalRT)
add_subdirectory(${FORTRAN_RUNTIME_SRC} FortranRuntime)
add_subdirectory(${FORTRAN_MAIN_SRC} FortranMain)

if (FLANG_RT_INCLUDE_TESTS)
add_subdirectory(test)
if (FLANG_RT_GTEST_AVAIL)
add_subdirectory(unittests)
endif()
endif()

#===============================================================================
# Create Flang-rt wrapper library
#===============================================================================
# Build as shared by default if no linkage type option set.
if (NOT FLANG_RT_ENABLE_SHARED AND NOT FLANG_RT_ENABLE_STATIC)
add_library(flang-rt SHARED $<TARGET_OBJECTS:obj.FortranDecimalRT>
$<TARGET_OBJECTS:obj.FortranRuntime>)
endif()
if (FLANG_RT_ENABLE_SHARED)
add_library(flang-rt SHARED $<TARGET_OBJECTS:obj.FortranDecimalRT>
$<TARGET_OBJECTS:obj.FortranRuntime>)
endif()
if (FLANG_RT_ENABLE_STATIC AND NOT FLANG_RT_ENABLE_SHARED)
add_library(flang-rt STATIC $<TARGET_OBJECTS:obj.FortranDecimalRT>
$<TARGET_OBJECTS:obj.FortranRuntime>)
endif()
# When building both static and shared, we need to append _static to the name
# to avoid naming conflicts.
if (FLANG_RT_ENABLE_STATIC AND FLANG_RT_ENABLE_SHARED)
add_library(flang-rt_static STATIC $<TARGET_OBJECTS:obj.FortranDecimalRT>
$<TARGET_OBJECTS:obj.FortranRuntime>)
endif()
156 changes: 156 additions & 0 deletions flang-rt/docs/GettingStarted.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<!--===- docs/GettingStarted.md
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
-->

# Flang-rt Runtime Library

```eval_rst
.. contents::
:local:
```
## What is Flang-rt
Flang-rt is the runtime library project for Flang. The Flang driver requires
the Fortran_main and Flang-rt libraries at runtime in order to generate
user executables. Building this Flang-rt project will build both Fortran_main
and the Flang-rt library, which is comprised of the FortranRuntime and
FortranDecimalRT libraries.

### Fortran_main
Fortran_main is left out of the Flang-rt library because it is required to
always be static unlike the link type of the Flang-rt library which can be
configured. Fortran_main implements the main entry point into Fortran's
`PROGRAM` in Flang by being the bridge between object files generated by Flang
and the C runtime that takes care of program set-up at system-level. For
every Fortran `PROGRAM`, Flang generates the `_QQmain` function.
Fortran_main implements the C `main` function that simply calls
`_QQmain`.

### FortranDecimalRT
In order to decouple the common dependency between compiler and runtime,
[FortranDecimal's sources](../../flang/lib/Decimal/CMakeLists.txt) are built
separately for the compiler and the runtime. When the library is built for
Flang-rt, the name FortranDecimalRT is used to avoid naming conflicts and
confusion.

### FortranRuntime
This is the core runtime library in Flang-rt. The sources for this library
currently still exist in the
[Flang source directory](../../flang/runtime/CMakeLists.txt). We hope to
migrate the sources to the Flang-rt directory in the future in order to further
decouple the runtime from the Flang compiler.

## Building Flang-rt
Like other LLVM runtimes, Flang-rt can be built by targetting the
[runtimes LLVM target](../../runtimes/CMakelists.txt). It can also be built
when targetting the [llvm target](../../llvm/CMakeLists.txt) as an enabled
runtime. Flang-rt will implicitly be added as an enabled runtime when Flang
is an enabled project built by llvm. Flang-rt does not support standalone
builds.

In the future, we may be interested in supporting in optionally building
Flang-rt when doing a Flang standalone build.

### Building with the llvm target
Assuming you are building Flang-rt to use with Flang, see
[Flang's Getting Started guide](../../flang/docs/GettingStarted.md) for more
information. To build Flang-rt when building the Flang compiler, once you have
the llvm-project source ready, make a clean build directory. Let root be the
root directory that you cloned llvm-project into.
```bash
cd root
rm -rf build
mkdir build
cd build
```
Now invoke the cmake configuration command for llvm that would build Flang with
Flang-rt.
```bash
cmake \
-G Ninja \
-DLLVM_ENABLE_RUNTIMES="compiler-rt;flang-rt" \
-DCMAKE_CXX_STANDARD=17 \
-DLLVM_INSTALL_UTILS=On \
# New Flang-rt flags for enabled link types
-DFLANG_RT_ENABLE_STATIC=On \
-DFLANG_RT_ENABLE_SHARED=On \
# We need to enable GTest if we want to run Flang-rt's testsuites
-DLLVM_INSTALL_GTEST=On \
-DFLANG_ENABLE_WERROR=On \
-DLLVM_LIT_ARGS=-v \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_PROJECTS="clang;flang;lld;mlir;openmp" \
../llvm-project/llvm
```
Flang requires other llvm projects (see LLVM_ENABLE_PROJECTS and the [Flang
Getting Started Guide](../../flang/docs/GettingStarted.md) for more specifics
on building Flang.

By targetting the LLVM project, we are letting LLVM infrastructure handle
invoking the runtimes target that will build Flang-rt. This includes finding
the cmake packages for Clang, LLVM, Flang and MLIR that Flang-rt depends on.

### Building with the runtimes target
If you already have a pre-built/installed version of LLVM, Flang, Clang and
MLIR, and would like to build Flang-rt without rebuilding the sources for these
other projects. You can simply target the runtimes project directly and passing
the paths to the directories of these files. If you built LLVM as we did above
with default build directories, your runtimes invocation should look something
like:
```bash
cd build
BUILDDIR=`pwd`

cmake \
-G Ninja \
-DCMAKE_CXX_STANDARD=17 \
# New Flang-rt flags for enabled link types
-DFLANG_RT_ENABLE_SHARED=On \
-DFLANG_RT_ENABLE_STATIC=On \
-DLLVM_LIT_ARGS=-v \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,$LD_LIBRARY_PATH" \
-DLLVM_TARGETS_TO_BUILD=host \
-DLLVM_EXTERNAL_LIT=$BUILD_DIR/bin/llvm-lit \
# We need to specify the paths to the cmake packages of the dependencies
-DLLVM_DIR=$BUILD_DIR/lib/cmake/llvm \
-DMLIR_DIR=$BUILD_DIR/lib/cmake/mlir \
-DFLANG_DIR=$BUILD_DIR/lib/cmake/flang \
-DLLVM_ENABLE_RUNTIMES="flang-rt" \
../llvm-project/runtimes/
```

## Library locations
When building the llvm target with flang as an enabled project, the Flang-rt
library will be built to `$BUILDDIR/runtimes/runtimes-bins/flang-rt/lib`. When
building the runtimes target with flang-rt as an enabled runtime, the libraries
will be built to `$BUILDDIR/flang-rt/lib` by default. In either configuration,
the Fortran_main library will be built to `$BUILDDIR/lib` by default.

## Using Flang-rt
The two build paths mentioned above get implicitly added as library paths at the
invocation of the driver. If Flang-rt is a shared library, you must make the
dynamic linker aware of where to look. One method to do so is to set the
environment variable `LD_LIBRARY_PATH` include the path to Flang-rt's directory.

## Options
Flang-rt introduces 2 CMake options used to configure the library's link type:
```
option(FLANG_RT_ENABLE_SHARED "Build flang-rt as a shared library." OFF)
option(FLANG_RT_ENABLE_STATIC "Build flang-rt as a static library." OFF)
```
Both can be specified if you want to build both shared and static versions of
the Flang-rt runtime. If both are specified, the static library will be named
Flang-rt_static.a to avoid naming conflicts, as per the LLVM standard.

## Usage Examples
```bash
# Example of using Flang with the shared Flang-rt runtime
# First we need to explicitly tell the dynamic linker where to find Flang-rt
# since it was built as shared.
$ $ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$BUILDDIR/runtimes/runtimes-bins/flang-rt/lib"
$ flang-new -ffree-form hello.f95 -o hello
```
Loading

0 comments on commit 6403287

Please sign in to comment.