diff --git a/.github/workflows/fortran.yml b/.github/workflows/fortran.yml index c047b1ead62..e0dd382fd41 100644 --- a/.github/workflows/fortran.yml +++ b/.github/workflows/fortran.yml @@ -7,6 +7,7 @@ on: paths: - '.github/workflows/fortran.yml' - 'enzyme/Enzyme/**' + - 'enzyme/Fortran/**' - 'enzyme/includes/**' - 'enzyme/test/**' - 'enzyme/tools/**' @@ -15,11 +16,10 @@ on: - 'enzyme/CMakeLists.txt' - 'enzyme/CMakePresets.json' pull_request: - branches: - - main paths: - '.github/workflows/fortran.yml' - 'enzyme/Enzyme/**' + - 'enzyme/Fortran/**' - 'enzyme/includes/**' - 'enzyme/test/**' - 'enzyme/tools/**' @@ -34,50 +34,65 @@ concurrency: # Cancel intermediate builds: only if it is a pull request build. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - + jobs: build-and-test-fortran: - name: Fortran ${{matrix.build}} ${{matrix.os}} ifx ${{matrix.ifx}} + name: Fortran ${{matrix.build}} ${{matrix.os}} ${{matrix.compiler}} ${{matrix.version}} runs-on: ${{matrix.os}} strategy: fail-fast: false matrix: - build: ["Release", "Debug"] + build: [Release, Debug] os: [ubuntu-22.04] - llvm: [15, 17] + compiler: [flang, ifx] + mpi: [2021.7.1] include: - - llvm: 15 - ifx: 2023.0.0 - mpi: 2021.7.1 - - llvm: 17 - ifx: 2023.2.0 - mpi: 2021.7.1 - timeout-minutes: 60 + - compiler: flang + FC: flang-21 + llvm: 21 + version: 21 + - compiler: ifx + FC: ifx + llvm: 15 + version: 2023.0.0 + - compiler: ifx + FC: ifx + llvm: 17 + version: 2023.2.0 + timeout-minutes: 60 steps: - - name: add llvm + - name: add llvm without clang and flang + if: ${{ matrix.compiler == 'ifx' }} run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - sudo apt-add-repository "deb http://apt.llvm.org/`lsb_release -c | cut -f2`/ llvm-toolchain-`lsb_release -c | cut -f2`-${{ matrix.llvm }} main" sudo apt-get update && sudo apt-get install -y cmake gcc g++ llvm-${{ matrix.llvm }}-dev ninja-build pip sudo python3 -m pip install --upgrade pip lit + - name: add llvm with clang and flang + if: ${{ matrix.compiler != 'ifx' }} + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - + sudo apt-add-repository "deb http://apt.llvm.org/`lsb_release -c | cut -f2`/ llvm-toolchain-`lsb_release -c | cut -f2`-${{ matrix.llvm }} main" + sudo apt-get update && sudo apt-get install -y cmake gcc g++ llvm-${{ matrix.llvm }}-dev ninja-build pip clang-${{ matrix.version }} flang-${{ matrix.version }} + sudo python3 -m pip install --upgrade pip lit - name: add intel tools + if: ${{ matrix.compiler == 'ifx' }} run: | wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB sudo echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list - sudo apt-get update && sudo apt-get install -y intel-oneapi-compiler-fortran-${{ matrix.ifx }} intel-oneapi-mpi-${{ matrix.mpi }} intel-oneapi-mpi-devel-${{ matrix.mpi }} + sudo apt-get update && sudo apt-get install -y intel-oneapi-compiler-fortran-${{ matrix.version }} intel-oneapi-mpi-${{ matrix.mpi }} intel-oneapi-mpi-devel-${{ matrix.mpi }} source /opt/intel/oneapi/setvars.sh printenv >> $GITHUB_ENV - uses: actions/checkout@v4 - name: generate build system run: | rm -rf build && mkdir build && cd build - cmake ../enzyme -GNinja -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DLLVM_DIR=/usr/lib/llvm-${{ matrix.llvm }}/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=`which lit` -DENZYME_IFX=ON + cmake ../enzyme -GNinja -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DLLVM_DIR=/usr/lib/llvm-${{ matrix.llvm }}/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=`which lit` -DENZYME_FORTRAN=ON -DCMAKE_Fortran_COMPILER=${{ matrix.FC }} - name: build enzyme working-directory: 'build' run: ninja LLVMEnzyme-${{ matrix.llvm }} - name: run Fortran tests working-directory: 'build' run: ninja check-enzyme-fortran - diff --git a/enzyme/CMakeLists.txt b/enzyme/CMakeLists.txt index ce533e69cfb..da113754287 100644 --- a/enzyme/CMakeLists.txt +++ b/enzyme/CMakeLists.txt @@ -39,7 +39,6 @@ option(ENZYME_CLANG "Build enzyme clang plugin" ON) option(ENZYME_FLANG "Build enzyme flang symlink" OFF) option(ENZYME_MLIR "Build enzyme mlir plugin" OFF) option(ENZYME_IFX "Enable enzyme support for the Intel Fortran compiler IFX" OFF) -option(ENZYME_IFX "Enable enzyme support for the Intel Fortran compiler IFX" OFF) option(ENZYME_EXTERNAL_SHARED_LIB "Build external shared library" OFF) option(ENZYME_APPLE_DYNAMIC_LOOKUP "On Apple, link Enzyme shared library with -flat_namespace/-undefined,dynamic_lookup instead of linking LLVM" @@ -49,6 +48,7 @@ option(ENZYME_WARN_COMPILER "Warn if enzyme detects potentially incompatible com option(ENZYME_ENABLE_REACTANT "Build support library for Reactant C++ frontend" OFF) set(ENZYME_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(ENZYME_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(Fortran_COMPILER ${CMAKE_Fortran_COMPILER}) list(APPEND CMAKE_MODULE_PATH "${ENZYME_SOURCE_DIR}/cmake/modules") set(LLVM_SHLIBEXT "${CMAKE_SHARED_MODULE_SUFFIX}") @@ -298,12 +298,12 @@ add_subdirectory(Enzyme) if (ENZYME_BC_LOADER) add_subdirectory(BCLoad) endif() -if (ENZYME_ENABLE_PLUGINS) - add_subdirectory(test) -endif() if (ENZYME_FORTRAN) add_subdirectory(Fortran) endif() +if (ENZYME_ENABLE_PLUGINS) + add_subdirectory(test) +endif() # The benchmarks data are not in git-exported source archives to minimize size. # Only add the benchmarks if the directory exists. diff --git a/enzyme/Enzyme/ActivityAnalysis.cpp b/enzyme/Enzyme/ActivityAnalysis.cpp index 1c18bb4bb2d..bf5adf136b9 100644 --- a/enzyme/Enzyme/ActivityAnalysis.cpp +++ b/enzyme/Enzyme/ActivityAnalysis.cpp @@ -168,6 +168,8 @@ bool isInactiveCall(CallBase &CI) { // clang-format off const char *KnownInactiveFunctionsStartingWith[] = { "f90io", + "_FortranAProgram", + "_FortranAio", "$ss5print", "strcpy", "_ZTv0_n24_NSoD", //"1Ev, 0Ev diff --git a/enzyme/Fortran/CMakeLists.txt b/enzyme/Fortran/CMakeLists.txt index d3e6fdaa139..aac46086e9d 100644 --- a/enzyme/Fortran/CMakeLists.txt +++ b/enzyme/Fortran/CMakeLists.txt @@ -1,8 +1,15 @@ +message("Building Fortran bindings") + enable_language(Fortran) include(FortranCInterface) FortranCInterface_VERIFY(QUIET) -add_library(EnzymeFortran enzyme.f90) +# Initialize Fortran module build output directory for all subsequent targets +if(NOT DEFINED CMAKE_Fortran_MODULE_DIRECTORY) + set(CMAKE_Fortran_MODULE_DIRECTORY "${CMAKE_BINARY_DIR}/modules") +endif() + +add_library(EnzymeFortran SHARED enzyme.f90 enzyme_function_hooks.f90) # Install library, create target file install( diff --git a/enzyme/Fortran/README.md b/enzyme/Fortran/README.md new file mode 100644 index 00000000000..05f6f4fcbc4 --- /dev/null +++ b/enzyme/Fortran/README.md @@ -0,0 +1,74 @@ +# Fortran bindings for Enzyme + +Source files in this subdirectory provides Fortran bindings for Enzyme, as +detailed in the following. + +## Function hooks + +We provide bindings for the `__enzyme_fwddiff` and `__enzyme_autodiff` function +hooks using implicit interfaces. Some Fortran compilers disallow procedure names +starting with an underscore so we rename the function hooks to remove the +leading double underscore. + +To make use of the `enzyme_autodiff` function hook in your code, import via +```fortran +use enzyme, only: enzyme_autodiff +``` +and call it as a subroutine or function as appropriate. For example, if you have +a function +```fortran + real function square(x) + real, intent(in) :: x + square = x**2 + end function +``` +then you can compute its derivative with reverse mode with the call +```fortran + call enzyme_autodiff(square, x, dx) +``` + +Similarly for +`enzyme_fwddiff`. Thanks to the implicit interface, arbitrary signatures are +supported, with the following caveats. + +> [!NOTE] +> A limitation of the implicit interfacing is that it only works for arguments +> that are passed by reference - the default in Fortran. If you want to pass any +> arguments by value using the `value` attribute then you will need to write an +> explicit interface block to the function hook yourself. + +> [!WARNING] +> The implicit interfacing approach is not supported by the Intel Fortran +> compiler ifx when running without optimizations, i.e., running with `-O0`. If +> you want to use ifx with `-O0` then you will need to write an explicit +> interface block, even if you are only passing arguments by reference. + +> [!WARNING] +> Differentiation with respect to procedures with assumed shape arrays is not +> currently supported when compiling with Flang. It should work with ifx, +> however. + +## Activity descriptors + +We provide bindings for the activity descriptors `enzyme_const`, `enzyme_dup`, +`enzyme_dupnoneed`, and `enzyme_out`, as well as the descriptors +`enzyme_scalar`, `enzyme_width`, and `enzyme_vector`. To make use of these in +your code, import via +```fortran +use enzyme, only: enzyme_const, enzyme_dup +``` +and then include them in calls to function hooks as you would in C or C++. For +example, if you have a subroutine +```fortran + subroutine my_subroutine(n, x, y) + integer, intent(in) :: n + real, dimension(n), intent(in) :: x + real, dimension(n), intent(out) :: y + ! ... + end subroutine my_subroutine +``` +then you can make use of activity descriptors like so: +```fortran + call enzyme_autodiff(my_subroutine, enzyme_const, n, & + enzyme_dup, x, dx, enzyme_dup, y, dy +``` diff --git a/enzyme/Fortran/enzyme.f90 b/enzyme/Fortran/enzyme.f90 index 1e999ff1004..a5bd9bad0de 100644 --- a/enzyme/Fortran/enzyme.f90 +++ b/enzyme/Fortran/enzyme.f90 @@ -22,6 +22,8 @@ ! ===----------------------------------------------------------------------=== ! module enzyme use iso_c_binding, only: c_int + use enzyme_function_hooks, only: enzyme_autodiff => f__enzyme_autodiff, & + enzyme_fwddiff => f__enzyme_fwddiff implicit none private @@ -33,4 +35,8 @@ module enzyme integer(c_int), public, bind(C, name="enzyme_scalar") :: enzyme_scalar integer(c_int), public, bind(C, name="enzyme_width") :: enzyme_width integer(c_int), public, bind(C, name="enzyme_vector") :: enzyme_vector + + ! Bindings for function hooks + public :: enzyme_autodiff + public :: enzyme_fwddiff end module enzyme diff --git a/enzyme/Fortran/enzyme_function_hooks.f90 b/enzyme/Fortran/enzyme_function_hooks.f90 new file mode 100644 index 00000000000..714fd415dbd --- /dev/null +++ b/enzyme/Fortran/enzyme_function_hooks.f90 @@ -0,0 +1,39 @@ +! ===- enzyme_function_hooks.f90 - Fortran bindings function hooks --------=== ! +! +! Enzyme Project +! +! Part of the Enzyme 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 +! +! If using this code in an academic setting, please cite the following: +! @misc{enzymeGithub, +! author = {William S. Moses and Valentin Churavy}, +! title = {Enzyme: High Performance Automatic Differentiation of LLVM}, +! year = {2020}, +! howpublished = {\url{https://github.com/wsmoses/Enzyme}}, +! note = {commit xxxxxxx} +! } +! +! ===----------------------------------------------------------------------=== ! +! +! This file provides Fortran bindings for Enzyme's function hooks. +! +! The fact that the double-underscore function hook names appears in the +! implicit interfaces defined in this module is sufficient to get Enzyme to be +! applied in the appropriate way. We provide cleaner bindings without +! double-underscores in the main Fortran Enzyme module in enzyme.f90. +! +! ===----------------------------------------------------------------------=== ! +module enzyme_function_hooks + implicit none + private + + ! Bindings for function hooks + ! NOTE: Leading underscores are not permitted by some Fortran compilers so we + ! prepend with 'f' in the Fortran versions of the function hooks. + public :: f__enzyme_autodiff + public :: f__enzyme_fwddiff + external :: f__enzyme_autodiff + external :: f__enzyme_fwddiff +end module enzyme_function_hooks diff --git a/enzyme/test/BUILD b/enzyme/test/BUILD index 9ec7dda4641..0b480eb41e0 100644 --- a/enzyme/test/BUILD +++ b/enzyme/test/BUILD @@ -26,6 +26,7 @@ expand_template( "@TARGET_TRIPLE@": "", "@TARGETS_TO_BUILD@": "ALL", "@LLVM_SHLIBEXT@": ".so", + "@Fortran_COMPILER@": "", }, template = "lit.site.cfg.py.in", visibility = [":__subpackages__"], diff --git a/enzyme/test/CMakeLists.txt b/enzyme/test/CMakeLists.txt index 8f781a0f04c..dcfc38492b5 100644 --- a/enzyme/test/CMakeLists.txt +++ b/enzyme/test/CMakeLists.txt @@ -20,7 +20,8 @@ add_custom_target(test-cmake COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/test_cma endif() endif() endif() -if (ENZYME_IFX) +if (ENZYME_FORTRAN) + list(APPEND ENZYME_TEST_DEPS EnzymeFortran) add_subdirectory(Fortran) endif() if (ENZYME_BC_LOADER) diff --git a/enzyme/test/Fortran/CMakeLists.txt b/enzyme/test/Fortran/CMakeLists.txt index 9487564e769..975cca7940b 100644 --- a/enzyme/test/Fortran/CMakeLists.txt +++ b/enzyme/test/Fortran/CMakeLists.txt @@ -1,3 +1,5 @@ +message("Building Fortran tests") + add_subdirectory(ForwardMode) add_subdirectory(ReverseMode) diff --git a/enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_bindings.f90 b/enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_bindings.f90 new file mode 100644 index 00000000000..d61a6d51e10 --- /dev/null +++ b/enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_bindings.f90 @@ -0,0 +1,58 @@ +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the flang compiler at -O0 +! For it to work with the ifx compiler we will need to figure out how to +! handle the indirection involved in the enzyme_fwddiff binding + +module AD + implicit none + + contains + + ! TODO: Switch to assumed shape implementation once + ! https://github.com/EnzymeAD/Enzyme/issues/2820 + ! has been addressed + subroutine selectFirst(n, x, y) + integer, intent(in) :: n + real, intent(in) :: x(n) + real, intent(inout) :: y + y = x(1) + end subroutine +end module + +program app + use AD, only: selectFirst + use enzyme, only: enzyme_const, enzyme_dup, enzyme_fwddiff + implicit none + integer :: n + real, allocatable :: x(:), dx(:) + real :: y, dy + + n = 3 + allocate(x(n)) + allocate(dx(n)) + + x = [2,3,4] + dx = [1,0,0] + y = 0 + dy = 0 + + call enzyme_fwddiff(selectFirst, enzyme_const, n, & + enzyme_dup, x, dx, enzyme_dup, y, dy) + + print *, int(y) + print *, int(dx(1)) + print *, int(dx(2)) + print *, int(dx(3)) + print *, int(dy) +end program + + +! CHECK: 2 +! CHECK-NEXT: 1 +! CHECK-NEXT: 0 +! CHECK-NEXT: 0 +! CHECK-NEXT: 1 diff --git a/enzyme/test/Fortran/ForwardMode/allocatableArraySimple.f90 b/enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_explicit_interface.f90 similarity index 53% rename from enzyme/test/Fortran/ForwardMode/allocatableArraySimple.f90 rename to enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_explicit_interface.f90 index d278626ad1c..4e581030cb9 100644 --- a/enzyme/test/Fortran/ForwardMode/allocatableArraySimple.f90 +++ b/enzyme/test/Fortran/ForwardMode/allocatableArraySimple_with_explicit_interface.f90 @@ -1,7 +1,11 @@ -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O0 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O0 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O1 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O1 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O2 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O2 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O3 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O3 %t -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the ifx compiler +! For it to work with the flang compiler we will need to address +! https://github.com/EnzymeAD/Enzyme/issues/2820 module AD implicit none diff --git a/enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_bindings.f90 b/enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_bindings.f90 new file mode 100644 index 00000000000..46bf1acd37f --- /dev/null +++ b/enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_bindings.f90 @@ -0,0 +1,58 @@ +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the flang compiler at -O0 +! For it to work with the ifx compiler we will need to figure out how to +! handle the indirection involved in the enzyme_autodiff binding + +module AD + implicit none + + contains + + ! TODO: Switch to assumed shape implementation once + ! https://github.com/EnzymeAD/Enzyme/issues/2820 + ! has been addressed + subroutine selectFirst(n, x, y) + integer, intent(in) :: n + real, intent(in) :: x(n) + real, intent(inout) :: y + y = x(1) + end subroutine +end module + +program app + use AD, only: selectFirst + use enzyme, only: enzyme_const, enzyme_dup, enzyme_autodiff + implicit none + integer :: n + real, allocatable :: x(:), dx(:) + real :: y, dy + + n = 3 + allocate(x(n)) + allocate(dx(n)) + + x = [2,3,4] + dx = [0,0,0] + y = 0 + dy = 1 + + call enzyme_autodiff(selectFirst, enzyme_const, n, & + enzyme_dup, x, dx, enzyme_dup, y, dy) + + print *, int(y) + print *, int(dx(1)) + print *, int(dx(2)) + print *, int(dx(3)) + print *, int(dy) +end program + + +! CHECK: 2 +! CHECK-NEXT: 1 +! CHECK-NEXT: 0 +! CHECK-NEXT: 0 +! CHECK-NEXT: 0 diff --git a/enzyme/test/Fortran/ReverseMode/allocatableArraySimple.f90 b/enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_explicit_interface.f90 similarity index 53% rename from enzyme/test/Fortran/ReverseMode/allocatableArraySimple.f90 rename to enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_explicit_interface.f90 index e5c3be3a290..34417e9cd3b 100644 --- a/enzyme/test/Fortran/ReverseMode/allocatableArraySimple.f90 +++ b/enzyme/test/Fortran/ReverseMode/allocatableArraySimple_with_explicit_interface.f90 @@ -1,7 +1,11 @@ -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O0 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O0 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O1 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O1 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O2 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O2 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O3 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O3 %t -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the ifx compiler +! For it to work with the flang compiler we will need to address +! https://github.com/EnzymeAD/Enzyme/issues/2820 module AD implicit none diff --git a/enzyme/test/Fortran/ReverseMode/norm_with_bindings.f90 b/enzyme/test/Fortran/ReverseMode/norm_with_bindings.f90 new file mode 100644 index 00000000000..f842fa83b21 --- /dev/null +++ b/enzyme/test/Fortran/ReverseMode/norm_with_bindings.f90 @@ -0,0 +1,45 @@ +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the flang compiler +! For it to work with the ifx compiler we will need to figure out how to +! handle the indirection involved in the enzyme_autodiff binding + +module math +contains + subroutine norm(n, x, y) + integer, intent(in) :: n + real, dimension(n), intent(in) :: x + real, dimension(n), intent(out) :: y + y(:) = x / sum(x) + end subroutine norm +end module math + +program app + use enzyme, only: enzyme_const, enzyme_dup, enzyme_autodiff + use math, only: norm + implicit none + integer, parameter :: n = 1000000 + integer, parameter :: initial_value = 20 + real :: x(n), dx(n) + real :: y(n), dy(n), yp + + x(:) = initial_value + dy(:) = 1.0 + + call norm(n, x, y) + + ! Rescale the output to avoid compiler-specific output formatting + yp = y(n) * 1.0e+06 + write(*,"(f6.4)") yp + + dx(:) = 0.0 + call enzyme_autodiff(norm, enzyme_const, n, & + enzyme_dup, x, dx, enzyme_dup, y, dy) + write(*,"(f6.4)") dy(n) +end program app + +! CHECK: 1.0000 +! CHECK-NEXT: 0.0000 diff --git a/enzyme/test/Fortran/ReverseMode/norm_with_explicit_interface.f90 b/enzyme/test/Fortran/ReverseMode/norm_with_explicit_interface.f90 new file mode 100644 index 00000000000..2bb2187fe0c --- /dev/null +++ b/enzyme/test/Fortran/ReverseMode/norm_with_explicit_interface.f90 @@ -0,0 +1,60 @@ +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 && %fc == ifx ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the ifx compiler +! For it to work with the flang compiler we will need to address +! https://github.com/EnzymeAD/Enzyme/issues/2820 + +module math + interface + subroutine norm__enzyme_autodiff(sr, x_desc, x, dx, y_desc, y, dy) + interface + subroutine sr_decal(a, b) + real, dimension(:), intent(in) :: a + real, dimension(:), intent(out) :: b + end subroutine + end interface + procedure(sr_decal) :: sr + integer, intent(in) :: x_desc + real, dimension(:), intent(in) :: x + real, dimension(:), intent(in) :: dx + integer, intent(in) :: y_desc + real, dimension(:), intent(inout) :: y + real, dimension(:), intent(inout) :: dy + end subroutine + end interface +contains + subroutine norm(x, y) + real, dimension(:), intent(in) :: x + real, dimension(:), intent(out) :: y + y(:) = x / sum(x) + end subroutine norm +end module math + +program app + use math, only: norm, norm__enzyme_autodiff + use enzyme, only: enzyme_dup + implicit none + integer, parameter :: n = 1000000 + integer, parameter :: initial_value = 20 + real :: x(n), dx(n) + real :: y(n), dy(n), yp + + x(:) = initial_value + dy(:) = 1.0 + + call norm(x, y) + + ! Rescale the output to avoid compiler-specific output formatting + yp = y(n) * 1.0e+06 + write(*,"(f6.4)") yp + + dx(:) = 0.0 + call norm__enzyme_autodiff(norm, enzyme_dup, x, dx, enzyme_dup, y, dy) + write(*,"(f6.4)") dy(n) +end program app + +! CHECK: 1.0000 +! CHECK-NEXT: 0.0000 diff --git a/enzyme/test/Fortran/ReverseMode/square.f90 b/enzyme/test/Fortran/ReverseMode/square.f90 deleted file mode 100644 index 8182b38adda..00000000000 --- a/enzyme/test/Fortran/ReverseMode/square.f90 +++ /dev/null @@ -1,41 +0,0 @@ -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O0 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O0 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O1 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O1 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O2 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O2 %t -o %t1 && %t1 | FileCheck %s; fi -! RUN: if [ %llvmver -ge 13 ]; then ifx -flto -O3 -c %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o %t && ifx -flto -O3 %t -o %t1 && %t1 | FileCheck %s; fi - -module math - interface - subroutine square__enzyme_autodiff(fn, x, dx) - interface - real function fn_decal(a) - real, intent(in) :: a - end function - end interface - procedure(fn_decal) :: fn - real, intent(in) :: x - real, intent(inout) :: dx - end subroutine - end interface -contains - real function square( x ) - real, intent(in) :: x - square = x**2 - end function -end module math - -program app - use math - implicit none - real :: x, dx - - x = 3 - print *, square(x) - - dx = 0 - call square__enzyme_autodiff(square, x, dx); - - print *, dx -end program app - -! CHECK: 9 -! CHECK-NEXT: 6 diff --git a/enzyme/test/Fortran/ReverseMode/square_with_bindings.f90 b/enzyme/test/Fortran/ReverseMode/square_with_bindings.f90 new file mode 100644 index 00000000000..f1e5cb8c8e5 --- /dev/null +++ b/enzyme/test/Fortran/ReverseMode/square_with_bindings.f90 @@ -0,0 +1,34 @@ +! RUN: if [[ %llvmver -ge 13 && %fc != ifx ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [[ %llvmver -ge 13 ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +! NOTE: This test is only configured to run with the flang compiler at -O0 +! For it to work with the ifx compiler we will need to figure out how to +! handle the indirection involved in the enzyme_autodiff binding + +module math +contains + real function square( x ) + real, intent(in) :: x + square = x**2 + end function +end module math + +program app + use enzyme, only: enzyme_autodiff + use math, only: square + implicit none + real :: x, dx + + x = 3 + print *, square(x) + + dx = 0 + call enzyme_autodiff(square, x, dx) + + print *, dx +end program app + +! CHECK: 9 +! CHECK-NEXT: 6 diff --git a/enzyme/test/Fortran/ReverseMode/square_with_explicit_interface.f90 b/enzyme/test/Fortran/ReverseMode/square_with_explicit_interface.f90 new file mode 100644 index 00000000000..38a5cbf02c1 --- /dev/null +++ b/enzyme/test/Fortran/ReverseMode/square_with_explicit_interface.f90 @@ -0,0 +1,41 @@ +! RUN: if [ %llvmver -ge 13 ]]; then %fc -flto -O0 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O0 -S -o %t.ll && %fc -flto -O0 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [ %llvmver -ge 13 ]]; then %fc -flto -O1 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O1 -S -o %t.ll && %fc -flto -O1 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [ %llvmver -ge 13 ]]; then %fc -flto -O2 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O2 -S -o %t.ll && %fc -flto -O2 %t.ll -o %t1 && %t1 | FileCheck %s; fi +! RUN: if [ %llvmver -ge 13 ]]; then %fc -flto -O3 -c %loadFortran %s -o /dev/stdout | %opt %loadEnzyme %enzyme -o /dev/stdout | %opt -O3 -S -o %t.ll && %fc -flto -O3 %t.ll -o %t1 && %t1 | FileCheck %s; fi + +module math + interface + subroutine square__enzyme_autodiff(fn, x, dx) + interface + real function fn_decal(a) + real, intent(in) :: a + end function + end interface + procedure(fn_decal) :: fn + real, intent(in) :: x + real, intent(inout) :: dx + end subroutine + end interface +contains + real function square( x ) + real, intent(in) :: x + square = x**2 + end function +end module math + +program app + use math + implicit none + real :: x, dx + + x = 3 + print *, square(x) + + dx = 0 + call square__enzyme_autodiff(square, x, dx) + + print *, dx +end program app + +! CHECK: 9 +! CHECK-NEXT: 6 diff --git a/enzyme/test/lit.site.cfg.py.in b/enzyme/test/lit.site.cfg.py.in index 0cc5e6f28f3..dafafca769a 100644 --- a/enzyme/test/lit.site.cfg.py.in +++ b/enzyme/test/lit.site.cfg.py.in @@ -97,6 +97,8 @@ config.substitutions.append(('%OPnewLoadEnzyme', newPMOP)) config.substitutions.append(('%enzyme', ('-enzyme' if int(config.llvm_ver) < 16 else '-passes="enzyme"'))) config.substitutions.append(('%simplifycfg', ("simplify-cfg" if int(config.llvm_ver) < 11 else "simplifycfg"))) config.substitutions.append(('%loopmssa', ("loop" if int(config.llvm_ver) < 11 else "loop-mssa"))) +config.substitutions.append(('%fc', '@Fortran_COMPILER@')) +config.substitutions.append(('%loadFortran', '-I@ENZYME_BINARY_DIR@/modules')) config.substitutions.append(('%loadBC', '' + ' @ENZYME_BINARY_DIR@/BCLoad/BCPass-' + config.llvm_ver + config.llvm_shlib_ext