diff --git a/cmake/Modules/OpmCompile.cmake b/cmake/Modules/OpmCompile.cmake index def4657e56a..b074b7ae983 100644 --- a/cmake/Modules/OpmCompile.cmake +++ b/cmake/Modules/OpmCompile.cmake @@ -1,5 +1,7 @@ # - Compile main library target +include(OpmInterproceduralOptimization) + option (STRIP_DEBUGGING_SYMBOLS "use separate files for the executable code and the debugging symbols" OFF) macro (opm_compile opm) @@ -43,8 +45,11 @@ macro (opm_compile opm) SOVERSION ${${opm}_VERSION} VERSION ${${opm}_VERSION} LINK_FLAGS "${${opm}_LINKER_FLAGS_STR}" - POSITION_INDEPENDENT_CODE TRUE + POSITION_INDEPENDENT_CODE TRUE ) + + opm_interprocedural_optimization(TARGET ${${opm}_TARGET}) + if (${${opm}_LIBRARY_TYPE} STREQUAL "SHARED") # libs that will be linked with the main lib string(REGEX REPLACE "([;^])[^;]+\\.a[;$]" "\\1" _public_libs @@ -70,7 +75,7 @@ macro (opm_compile opm) # unset this variable to signal that no library is generated set (${opm}_TARGET) endif (${opm}_SOURCES) - + # pre-compile common headers; this is setup *after* the library to pick # up extra options set there if (PRECOMPILE_HEADERS) @@ -99,5 +104,5 @@ macro (opm_compile opm) if (${opm}_TARGET) set(${opm}_LIBRARY $) endif (${opm}_TARGET) - + endmacro (opm_compile opm) diff --git a/cmake/Modules/OpmInterproceduralOptimization.cmake b/cmake/Modules/OpmInterproceduralOptimization.cmake new file mode 100644 index 00000000000..3d400851391 --- /dev/null +++ b/cmake/Modules/OpmInterproceduralOptimization.cmake @@ -0,0 +1,161 @@ +include(CheckIPOSupported) +include(TestCXXAcceptsFlag) + +# ------------------------------------------------------------------------------ +# opm_interprocedural_optimization +# +# Configure interprocedural optimization (IPO) for a target. +# +# This function enables IPO (Link Time Optimization) on the specified target. +# It supports the standard CMake IPO interface as well as extended interfaces for +# GCC and Clang settings. +# +# The type of optimization applied is controlled via the cache variable: +# OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE +# which can be one of: +# - CMake: use standard CMake IPO via INTERPROCEDURAL_OPTIMIZATION_ +# - Clang: use Clang's ThinLTO with parallelism and cache configuration +# - GNU: use GNU's incremental LTO with parallelism and cache configuration +# - NONE: disable IPO +# +# Incremental cache directories are automatically managed under: +# ${CMAKE_BINARY_DIR}/LTOCache/ +# +# Usage: +# +# opm_interprocedural_optimization( +# TARGET +# [CONFIGURATION_TYPES ] +# ) +# +# Arguments: +# TARGET (required) Target to which optimization will be applied. +# CONFIGURATION_TYPES (optional) List of configuration names (e.g. Release). +# If omitted, the default list from: +# OPM_INTERPROCEDURAL_OPTIMIZATION_CONFIGURATION_TYPES +# is used. +# +# Cache Variables: +# OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE (CMake, GNU, Clang, NONE) +# OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS (e.g. 1, 8) +# OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY (e.g. "prune_after=604800") +# OPM_INTERPROCEDURAL_OPTIMIZATION_CONFIGURATION_TYPES (e.g. "Release;RelWithDebInfo") +# +# ------------------------------------------------------------------------------ + +set(OPM_INTERPROCEDURAL_OPTIMIZATION_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "Default configuration types to apply OPM interprocedural optimization.") +set(OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS 1 CACHE STRING "Default OPM interprocedural optimization jobs.") +set(OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY "" CACHE STRING "Default OPM interprocedural optimization cache policy.") +mark_as_advanced(OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS) +mark_as_advanced(OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY) + +# define possible values for LTO (order matters: first one in the list is used as default type) +set(opm_ipo_types) +check_ipo_supported(LANGUAGES C CXX RESULT ipo_supported) +if(ipo_supported) + list(PREPEND opm_ipo_types CMake) +endif() + +# Add custom for known compiler versions +if(ipo_supported) + if((CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)) + list(PREPEND opm_ipo_types Clang) + elseif((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.5.4)) + list(PREPEND opm_ipo_types GNU) + endif() +endif() + +# Some linkers decide to discard "unused" functions prematurely and takes long, so disable for now. +list(INSERT opm_ipo_types 0 NONE) + +set(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPES ${opm_ipo_types} CACHE STRING "OPM interprocedural optimization types." FORCE) +mark_as_advanced(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPES) +list(GET OPM_INTERPROCEDURAL_OPTIMIZATION_TYPES 0 opm_ipo_default_type) + +list(JOIN OPM_INTERPROCEDURAL_OPTIMIZATION_TYPES ", " opm_ipo_types_str) +set(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE ${opm_ipo_default_type} CACHE STRING "Default OPM interprocedural optimization type. Possible values: ${opm_ipo_types_str}") +set_property(CACHE OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE PROPERTY STRINGS ${OPM_INTERPROCEDURAL_OPTIMIZATION_TYPES}) + +message(STATUS "OPM interprocedural optimization type: ${OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE}") + +function(opm_interprocedural_optimization) + cmake_parse_arguments(OPM_IPO "" "TARGET" "CONFIGURATION_TYPES" ${ARGN}) + + # if not set, use the default configuration types + if(NOT OPM_IPO_CONFIGURATION_TYPES) + set(OPM_IPO_CONFIGURATION_TYPES ${OPM_INTERPROCEDURAL_OPTIMIZATION_CONFIGURATION_TYPES}) + endif() + + foreach(config ${OPM_IPO_CONFIGURATION_TYPES}) + if((CMAKE_CONFIGURATION_TYPES MATCHES ${config}) OR (CMAKE_BUILD_TYPE MATCHES ${config})) + + set(LTO_CACHE_PATH ${CMAKE_BINARY_DIR}/LTOCache/${config}) + + if(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE STREQUAL CMake) + # Use default CMake IPO + string(TOUPPER ${config} uconfig) + set_target_properties(${OPM_IPO_TARGET} PROPERTIES INTERPROCEDURAL_OPTIMIZATION_${uconfig} ON) + elseif(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE STREQUAL GNU) + if((NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6.4) AND (NOT OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS EQUAL 1)) + # https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Optimize-Options.html + target_compile_options(${OPM_IPO_TARGET} PRIVATE $<$:-flto=${OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS}>) + target_link_options(${OPM_IPO_TARGET} INTERFACE $<$:-flto=${OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS}>) + elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.5.4) + # https://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/Optimize-Options.html + target_compile_options(${OPM_IPO_TARGET} PRIVATE $<$:-flto>) + target_link_options(${OPM_IPO_TARGET} INTERFACE $<$:-flto>) + endif() + + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0.0) + # use incremental LTO https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/Optimize-Options.html + target_compile_options(${OPM_IPO_TARGET} PRIVATE $<$:-flto-incremental=${LTO_CACHE_PATH}>) + target_link_options(${OPM_IPO_TARGET} INTERFACE $<$:-flto-incremental=${LTO_CACHE_PATH}>) + + # Configure cache directory + file(MAKE_DIRECTORY ${LTO_CACHE_PATH}) + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${LTO_CACHE_PATH}) + endif() + + elseif(OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE STREQUAL Clang) + # Use ThinLTO and reuse cache (see https://releases.llvm.org/20.1.0/tools/clang/docs/ThinLTO.html) + + target_compile_options(${OPM_IPO_TARGET} PRIVATE $<$:-flto=thin>) + target_link_options(${OPM_IPO_TARGET} INTERFACE $<$:-flto=thin>) + + # Configure cache directory + file(MAKE_DIRECTORY ${LTO_CACHE_PATH}) + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${LTO_CACHE_PATH}) + + # Parallel and cache options are linker dependent + if(${CMAKE_CXX_COMPILER_LINKER_ID} MATCHES "LLD") + target_link_options(${OPM_IPO_TARGET} INTERFACE + $<$:LINKER:--thinlto-jobs=${OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS}> + $<$:LINKER:--thinlto-cache-dir=${LTO_CACHE_PATH}> + ) + if(OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY) + target_link_options(${OPM_IPO_TARGET} INTERFACE + $<$:LINKER:--thinlto-cache-policy=${OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY}> + ) + endif() + elseif(${CMAKE_CXX_COMPILER_LINKER_ID} MATCHES "GNU|GNUgold|MOLD") + target_link_options(${OPM_IPO_TARGET} INTERFACE + $<$:LINKER:-plugin-opt,jobs=${OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS}> + $<$:LINKER:-plugin-opt,cache-dir=${LTO_CACHE_PATH}> + ) + if(OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY) + target_link_options(${OPM_IPO_TARGET} INTERFACE + $<$:LINKER:-plugin-opt,cache-policy=${OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY}> + ) + endif() + elseif(${CMAKE_CXX_COMPILER_LINKER_ID} MATCHES "AppleClang") + target_link_options(${OPM_IPO_TARGET} INTERFACE + $<$:LINKER:-mllvm,-threads=${OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS}> + $<$:LINKER:-cache_path_lto,${LTO_CACHE_PATH}> + ) + else() + message(DEBUG "IPO for Clang is supported, but linker type '${CMAKE_CXX_COMPILER_LINKER_ID}' is unrecognized. Skip adding parallelism or cache flags.") + endif() + endif() + endif() + endforeach() +endfunction() diff --git a/cmake/Modules/UseOptimization.cmake b/cmake/Modules/UseOptimization.cmake index 60408a0237b..e324f7306a8 100644 --- a/cmake/Modules/UseOptimization.cmake +++ b/cmake/Modules/UseOptimization.cmake @@ -17,34 +17,6 @@ if (CXX_COMPAT_GCC) # extra flags passed for optimization set (_opt_flags "") - # link-time (a.k.a. global) optimizations - # disabled due to widespread bugs in the linker plugin - option (WHOLE_PROG_OPTIM "Whole program optimization (lto)" OFF) - if (WHOLE_PROG_OPTIM) - check_cxx_accepts_flag ("-flto" HAVE_LINK_OPTS) - check_cxx_accepts_flag ("-fuse-linker-plugin" HAVE_LINK_PLUGIN) - if (HAVE_LINK_OPTS) - list (APPEND _opt_flags "-flto") - endif (HAVE_LINK_OPTS) - if (HAVE_LINK_PLUGIN) - list (APPEND _opt_flags "-fuse-linker-plugin") - endif (HAVE_LINK_PLUGIN) - if(HAVE_LINK_OPTS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - string(REPLACE "." ";" VERSION_LIST "${CMAKE_C_COMPILER_VERSION}") - list(GET VERSION_LIST 0 VER_MAJOR) - find_program(LTO_AR_COMMAND NAMES ${CMAKE_C_COMPILER}-ar gcc-ar-${VER_MAJOR} gcc-ar) - find_program(LTO_RANLIB_COMMAND NAMES ${CMAKE_C_COMPILER}-ranlib gcc-ranlib-${VER_MAJOR} gcc-ranlib) - if(LTO_AR_COMMAND) - set(CMAKE_AR ${LTO_AR_COMMAND}) - message(STATUS "Using LTO-enabled ar: ${CMAKE_AR}") - endif() - if(LTO_RANLIB_COMMAND) - set(CMAKE_RANLIB ${LTO_RANLIB_COMMAND}) - message(STATUS "Using LTO-enabled ranlib: ${CMAKE_RANLIB}") - endif() - endif() - endif (WHOLE_PROG_OPTIM) - # native instruction set tuning option (WITH_NATIVE "Use native instruction set" ON) if (WITH_NATIVE) diff --git a/cmake/OPM-CMake.md b/cmake/OPM-CMake.md index 76473b7f678..7bcc1ed541d 100644 --- a/cmake/OPM-CMake.md +++ b/cmake/OPM-CMake.md @@ -100,7 +100,7 @@ to do the necessary modifications. 2. Add the name of the translation unit to the `PROGRAM_SOURCE_FILES` list in `CMakeLists_files.txt`. - + ### Creating a New Module 1. Copy the directory `cmake/` and all sub-directories from opm-core. This @@ -213,15 +213,79 @@ a good idea if you are building the library on the same machine as you will be using the library. Default is ON. - WHOLE_PROG_OPTIM - -Perform an extra round of optimization when linking the program. -(Usually the compiler only optimizes within the translation unit). -If your compiler supports this, it usually leads to a faster runtime. -Default is OFF. + OPM_INTERPROCEDURAL_OPTIMIZATION_TYPE + +Controls the type of interprocedural optimization (IPO, also known as +Link Time Optimization or LTO) applied when linking targets. If +enabled and supported by your compiler, IPO can significantly improve +runtime performance by optimizing across translation units.
+Possible values are: +
    +
  • CMake: Use the standard CMake IPO interface + (INTERPROCEDURAL_OPTIMIZATION property). This is + supported by most modern compilers.
  • +
  • Clang: Use Clang's ThinLTO, enabling parallelism and + cache configuration for faster and more scalable builds. This mode + is selected automatically for Clang 7.0+ if available.
  • +
  • GNU: Use GNU (GCC) incremental LTO with parallelism and + cache configuration. This mode is selected for GCC 4.5+ if available. +
  • +
  • NONE: Disable interprocedural optimization.
  • +
+The default is NONE.
+ + + + + OPM_INTERPROCEDURAL_OPTIMIZATION_CONFIGURATION_TYPES + +A semicolon-separated list of build configurations for which +interprocedural optimization is enabled (e.g., +Release;RelWithDebInfo). If not set, the default is +Release;RelWithDebInfo.
+This controls which CMake build types (such as Release or RelWithDebInfo) +will have IPO/LTO enabled. You can override this to enable or disable +IPO for custom configurations. + + + + + OPM_INTERPROCEDURAL_OPTIMIZATION_JOBS + +Controls the degree of parallelism for IPO/LTO linking and optimization. +The effect depends on the selected IPO type and compiler: +
    +
  • For Clang ThinLTO, sets the number of parallel jobs for + the ThinLTO backend (passed to the linker as --thinlto-jobs + or equivalent).
  • +
  • For GNU (GCC), sets the number of parallel LTO jobs (passed + as -flto=N).
  • +
+If set to 0, the build system or linker may attempt to use +all available cores, which can be too aggressive if the build is already +parallelized (e.g., with make -j or Ninja).
+The default is 1. + + + + + OPM_INTERPROCEDURAL_OPTIMIZATION_CACHE_POLICY + +Specifies the cache policy for IPO/LTO cache directories, if supported +by the selected compiler and linker. For example, with Clang ThinLTO +and the LLD linker, you can set prune_after=604800 to prune +cache files older than one week. This value is passed to the linker if +supported, allowing you to control cache size and cleanup behavior.
+The default is empty (no explicit policy).
+Cache directories are automatically created under +${CMAKE_BINARY_DIR}/LTOCache/<config> for each +configuration. + + + - + ## Modules Reference ### Project-specific Files @@ -391,7 +455,7 @@ must always be done from the beginning in order to link correctly. Write .la file which will make libtool find our library. This enables users of our library to use libtool even if we did not do so ourselves. - + ### Build System Modules @@ -717,7 +781,7 @@ translation. Textual concatenation of all components of the version number (see below) with a dot inbetween. This form of version number can be compared using -CMake VERSION_{LESS|EQUAL|GREATER} operators. +CMake VERSION_{LESS|EQUAL|GREATER} operators. _VERSION_MAJOR