Skip to content

Commit 7ea6fa7

Browse files
committed
compiler-dependent fma suppression directly from cmake using -DIdefix_SUPPRESS_FMA
1 parent b32c7b4 commit 7ea6fa7

3 files changed

Lines changed: 117 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ option(Idefix_DEBUG "Enable Idefix debug features (makes the code very slow)" OF
1616
option(Idefix_RUNTIME_CHECKS "Enable runtime sanity checks" OFF)
1717
option(Idefix_WERROR "Treat compiler warnings as errors" OFF)
1818
option(Idefix_PYTHON "Enable python bindings (requires pybind11)" OFF)
19+
option(Idefix_SUPPRESS_FMA "Disable FMA (fused multiply-add) contraction/codegen. Useful for code validation accross architectures." OFF)
1920
set(Idefix_PROBLEM_DIR "${CMAKE_BINARY_DIR}" CACHE STRING "Problem directory to build for.")
2021
set(Idefix_CXX_FLAGS "" CACHE STRING "Additional compiler/linker flag")
2122
set(Idefix_DEFS "definitions.hpp" CACHE FILEPATH "Problem definition header file")
@@ -43,6 +44,7 @@ include(AddIdefixSource)
4344
include(SetIdefixProperty)
4445
include(SetRequiredBuildSettingsForGCC8)
4546
include(CheckHdf5ParallelSupport)
47+
include(SuppressFMA)
4648

4749
#Idefix requires Cuda Lambdas (experimental)
4850
if(Kokkos_ENABLE_CUDA)
@@ -58,6 +60,8 @@ include_directories(${Kokkos_INCLUDE_DIRS_RET})
5860
# Add Idefix CXX Flags
5961
add_compile_options(${Idefix_CXX_FLAGS})
6062

63+
64+
6165
# Add filesystem libraries for GCC8
6266
set_required_build_settings_for_GCC8()
6367

@@ -278,6 +282,12 @@ configure_file(
278282
# Make sure the generated header is on the include path
279283
target_include_directories(idefix PRIVATE ${CMAKE_BINARY_DIR}/build/generated)
280284

285+
# disable FMA if needed
286+
if(Idefix_SUPPRESS_FMA)
287+
message(STATUS "FMA (fused multiply-add) contraction/codegen is disabled")
288+
target_suppress_fma(idefix)
289+
endif()
290+
281291
message(STATUS "Idefix final configuration")
282292
if(Idefix_EVOLVE_VECTOR_POTENTIAL)
283293
message(STATUS " MHD: ${Idefix_MHD} (Vector potential)")

cmake/SuppressFMA.cmake

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#[[============================================================================
2+
SuppressFMA.cmake
3+
4+
Provides an option and a function to optionally disable fused
5+
multiply-add (FMA) code generation / contraction for a Kokkos-based CXX
6+
target.
7+
8+
Kokkos wraps the real device compiler behind nvcc_wrapper (CUDA) or
9+
hipcc (HIP), which makes CMAKE_CXX_COMPILER_ID report the *underlying
10+
host* compiler (e.g. "GNU" or "Clang") instead of "NVIDIA" or "Clang
11+
as HIP". Backend detection therefore relies on the Kokkos_ENABLE_*
12+
variables exported by KokkosConfig.cmake / set by the Kokkos build,
13+
and only falls back to CMAKE_CXX_COMPILER_ID for the plain host
14+
compilers (no CUDA/HIP backend active):
15+
16+
- Kokkos_ENABLE_HIP ON -> AMD HIP (hipcc, clang-based) : -ffp-contract=off
17+
- Kokkos_ENABLE_CUDA ON -> NVIDIA nvcc (via nvcc_wrapper) : --fmad=false
18+
- otherwise, CMAKE_CXX_COMPILER_ID selects among:
19+
GNU : gcc/g++
20+
Intel : classic icc/icpc
21+
IntelLLVM : Intel oneAPI icx/icpx
22+
Clang : LLVM clang++
23+
AppleClang : Xcode clang++
24+
NVHPC : NVIDIA HPC SDK (nvc++), e.g. for OpenMPTarget/OpenACC
25+
26+
Usage (after find_package(Kokkos) so Kokkos_ENABLE_* are defined):
27+
include(SuppressFMA.cmake)
28+
add_library(mylib source.cpp)
29+
target_link_libraries(mylib PUBLIC Kokkos::kokkos)
30+
target_suppress_fma(mylib)
31+
32+
FMA suppression is only actually applied if the cache option
33+
SUPPRESS_FMA is ON (default OFF), so the function can be called
34+
unconditionally and toggled at configure time with:
35+
cmake -DSUPPRESS_FMA=ON ..
36+
============================================================================]]
37+
38+
include_guard(GLOBAL)
39+
40+
option(SUPPRESS_FMA "Disable FMA (fused multiply-add) contraction/codegen for CXX where possible" OFF)
41+
42+
# Determine the CXX FMA-suppression flags for the active Kokkos backend /
43+
# CXX compiler. Returns the list of flags (possibly empty) via out_var.
44+
function(_fma_suppression_flags_cxx out_var)
45+
set(flags "")
46+
set(id "${CMAKE_CXX_COMPILER_ID}")
47+
48+
# Kokkos backend takes priority: nvcc_wrapper/hipcc hide the real
49+
# device compiler from CMAKE_CXX_COMPILER_ID.
50+
if(Kokkos_ENABLE_HIP)
51+
set(flags "-ffp-contract=off")
52+
53+
elseif(Kokkos_ENABLE_CUDA)
54+
set(flags "--fmad=false")
55+
56+
elseif(id STREQUAL "GNU")
57+
set(flags "-ffp-contract=off" "-mno-fma")
58+
59+
elseif(id MATCHES "^(Clang|AppleClang)$")
60+
set(flags "-ffp-contract=off")
61+
62+
elseif(id STREQUAL "Intel")
63+
# Intel classic compiler
64+
set(flags "-fp-model=precise" "-no-fma")
65+
66+
elseif(id STREQUAL "IntelLLVM")
67+
# Intel oneAPI compiler (clang-based)
68+
set(flags "-ffp-contract=off" "-fp-model=strict")
69+
70+
elseif(id STREQUAL "NVHPC")
71+
# NVIDIA HPC SDK (formerly PGI), e.g. OpenMPTarget/OpenACC backend
72+
set(flags "-Mnofma") # untested
73+
endif()
74+
75+
set(${out_var} "${flags}" PARENT_SCOPE)
76+
endfunction()
77+
78+
# target_suppress_fma(<target>)
79+
#
80+
# Applies compiler-specific FMA-suppression flags to <target>'s CXX
81+
# sources, but only when the SUPPRESS_FMA option is ON. Safe to call
82+
# unconditionally.
83+
function(target_suppress_fma target)
84+
85+
if(NOT TARGET ${target})
86+
message(FATAL_ERROR "target_suppress_fma: '${target}' is not a target")
87+
endif()
88+
89+
_fma_suppression_flags_cxx(cxx_flags)
90+
91+
if(cxx_flags)
92+
foreach(flag IN LISTS cxx_flags)
93+
target_compile_options(${target} PRIVATE
94+
$<$<COMPILE_LANGUAGE:CXX>:${flag}>
95+
)
96+
endforeach()
97+
else()
98+
message(VERBOSE
99+
"target_suppress_fma: no FMA-suppression flag known for "
100+
"CXX compiler '${CMAKE_CXX_COMPILER_ID}' "
101+
"(Kokkos_ENABLE_CUDA=${Kokkos_ENABLE_CUDA}, "
102+
"Kokkos_ENABLE_HIP=${Kokkos_ENABLE_HIP}) (target ${target})")
103+
endif()
104+
endfunction()

pytools/idfx_test.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,19 @@ def _genCmakeCommand(self, definitionFile=""):
225225

226226
if self.cuda:
227227
comm.append("-DKokkos_ENABLE_CUDA=ON")
228-
# disable fmad operations on Cuda to make it compatible with CPU arithmetics
229-
comm.append("-DIdefix_CXX_FLAGS=--fmad=false")
230228
# disable Async cuda malloc for tests performed on old UCX implementations
231229
comm.append("-DKokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC=OFF")
232230

233231
if self.intel:
234232
# disable fmad operations on Cuda to make it compatible with CPU arithmetics
235-
comm.append("-DIdefix_CXX_FLAGS=-fp-model=strict")
236233
comm.append("-DCMAKE_CXX_COMPILER=icpx")
237234
comm.append("-DCMAKE_C_COMPILER=icx")
238235

239236
if self.hip:
240237
comm.append("-DKokkos_ENABLE_HIP=ON")
241-
# disable fmad operations on HIP to make it compatible with CPU arithmetics
242-
comm.append("-DIdefix_CXX_FLAGS=-ffp-contract=off")
238+
239+
# disable FMA for testing so that we have the same results on CPU and GPU (otherwise, the results are not bitwise identical)
240+
comm.append("-DIdefix_SUPPRESS_FMA=ON")
243241

244242
# if we use single precision
245243
if self.single:

0 commit comments

Comments
 (0)