diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d932ec9f..5a828ec82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,7 @@ jobs: CMAKE_OPTS: >- -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DTARGET_UARCH=official -DUSE_COMPILE_CACHE=ON -DWANT_DEBUG_CPACK=ON CCACHE_MAXSIZE: 0 @@ -101,6 +102,7 @@ jobs: CMAKE_OPTS: >- -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DTARGET_UARCH=official -DUSE_COMPILE_CACHE=ON -DWANT_DEBUG_CPACK=ON CCACHE_MAXSIZE: 0 @@ -193,6 +195,7 @@ jobs: -Werror=dev -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DTARGET_UARCH=official -DUSE_COMPILE_CACHE=ON CCACHE_MAXSIZE: 0 CCACHE_NOCOMPRESS: 1 @@ -282,6 +285,7 @@ jobs: -Werror=dev -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DTARGET_UARCH=official -DUSE_COMPILE_CACHE=ON CCACHE_MAXSIZE: 0 CCACHE_NOCOMPRESS: 1 @@ -401,6 +405,7 @@ jobs: --toolchain C:/vcpkg/scripts/buildsystems/vcpkg.cmake ` -Werror=dev ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` + -DTARGET_UARCH=official ` -DWANT_QT6=ON ` -DUSE_COMPILE_CACHE=ON ` -DUSE_WERROR=ON ` @@ -441,6 +446,7 @@ jobs: env: CMAKE_OPTS: >- -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DTARGET_UARCH=official -DUSE_COMPILE_CACHE=ON -DCPACK_NSIS_EXECUTABLE=/clang64/bin/makensis.exe CCACHE_MAXSIZE: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d16c6b76..f603ae69a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,11 @@ SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) # Import of windows.h breaks min()/max() ADD_DEFINITIONS(-DNOMINMAX) +# Set symbol visibility +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + # CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES is not set correctly for MinGW until # CMake 3.14.1, so avoid specifying system include directories on affected # versions. Normal include directories are safe, since GCC ignores them if they @@ -110,6 +115,83 @@ option(WANT_CPACK_TARBALL "Request CPack to create a tarball instead of an insta option(WANT_QT6 "Build with experimental Qt6 support" OFF) +# Set the target microarchitecture +if(TARGET_UARCH_FLAGS) + if(TARGET_UARCH AND NOT TARGET_UARCH STREQUAL "custom") + message(WARNING "Ignoring TARGET_UARCH since TARGET_UARCH_FLAGS was set") + endif() + + set(_default_target_uarch "custom") + message(STATUS "Setting target microarchitecture to 'custom'") + message(STATUS "Target microarchitecture flags: ${TARGET_UARCH_FLAGS}") +elseif(TARGET_UARCH) + message(STATUS "Setting target microarchitecture to '${TARGET_UARCH}'") +else() + if(CMAKE_CROSSCOMPILING) + set(_default_target_uarch "none") + else() + set(_default_target_uarch "native") + endif() + message(STATUS "Setting target microarchitecture to '${_default_target_uarch}' as none was specified") +endif() + +if(NOT TARGET_UARCH) + set(TARGET_UARCH ${_default_target_uarch} CACHE STRING "Choose the target microarchitecture" FORCE) + # Accepted options: + # - "none": No microarchitecture is set. Uses the compiler default. + # - "official": Targets the baseline microarchitecture of official LMMS releases. + # - "custom": Targets the microarchitecture specified by TARGET_UARCH_FLAGS. + # - "native": Targets the highest microarchitecture level supported by the build machine. + set_property(CACHE TARGET_UARCH PROPERTY STRINGS "none" "official" "custom" "native") +endif() + +if(TARGET_UARCH STREQUAL "custom" OR TARGET_UARCH_FLAGS) + add_compile_options(${TARGET_UARCH_FLAGS}) +elseif(TARGET_UARCH STREQUAL "none") + # Do nothing, use the compiler default +elseif(TARGET_UARCH STREQUAL "official") + if(LMMS_HOST_X86_64) + if(MSVC) + # MSVC doesn't have /arch:x86-64-v2, but this is pretty close + add_compile_options(/arch:SSE4.2) + else() + add_compile_options(-march=x86-64-v2) + endif() + elseif(LMMS_HOST_ARM64) + if(LMMS_BUILD_WIN64) + # Using Windows 11 minimum requirements + # https://learn.microsoft.com/en-us/windows-hardware/design/minimum/minimum-hardware-requirements-overview + if(MSVC) + add_compile_options(/arch:armv8.1) + else() + add_compile_options(-march=armv8.1-a) + endif() + elseif(LMMS_BUILD_APPLE) + # Chosen for Apple M1 support + add_compile_options(-march=armv8.4-a) + else() + # Chosen for Raspberry Pi 4 support + add_compile_options(-march=armv8-a) + endif() + else() + message(WARNING "There are no official releases for the target architecture, so there is no official baseline microarchitecture") + endif() +elseif(TARGET_UARCH STREQUAL "native") + if(CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "Cannot target native microarchitecture while cross-compiling") + endif() + + if(MSVC) + include(CheckCPUFeatures) + determine_msvc_native_arch(_arch_option) + if(NOT "${_arch_option}" STREQUAL "") + add_compile_options(${_arch_option}) + endif() + else() + add_compile_options(-march=native -mtune=native) + endif() +endif() + IF(LMMS_BUILD_APPLE) # Fix linking on 10.14+. See issue #4762 on github LINK_DIRECTORIES("${APPLE_PREFIX}/lib") @@ -666,10 +748,10 @@ SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DLMMS_DEBUG") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLMMS_DEBUG") if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_definitions(NDEBUG) + add_compile_definitions(NDEBUG) SET(STATUS_ASSERTIONS "Disabled") else() - remove_definitions(-DNDEBUG) + remove_definitions(-DNDEBUG) SET(STATUS_ASSERTIONS "Enabled") endif() diff --git a/cmake/modules/CheckCPUFeatures.cmake b/cmake/modules/CheckCPUFeatures.cmake new file mode 100644 index 000000000..5d3329794 --- /dev/null +++ b/cmake/modules/CheckCPUFeatures.cmake @@ -0,0 +1,152 @@ +# Copyright (c) 2026 Dalton Messmer +# +# Redistribution and use is allowed according to the terms of the New BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +# Determines the newest microarchitecture supported by the build machine using MSVC. +# +# This exists because unlike GCC and Clang, MSVC does not have an equivalent +# to -march=native which targets the latest supported microarchitecture. +# +# Usage: +# determine_msvc_native_arch( +# # The cache variable in which to store the MSVC /arch:foo option +# ) +function(determine_msvc_native_arch _arch_option_out) + # Return if we already know the /arch option + if(NOT "${${_arch_option_out}}" STREQUAL "") + return() + endif() + + if(NOT MSVC) + message(FATAL_ERROR "Only MSVC is supported") + endif() + + if(CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "Cross-compiling is not supported") + endif() + + if(NOT LMMS_HOST_X86_64 AND NOT LMMS_HOST_ARM64) + message(WARNING "CPU feature detection is only implemented for x86_64 and arm64 - using default /arch option.") + set("${_arch_option_out}" "" CACHE INTERNAL "Native /arch option") + return() + endif() + + set(_source [[ + #include + #if defined(_M_X64) + # include + # include + #elif defined(_M_ARM64) + # include + #else + # error Unsupported platform + #endif + + auto main() -> int + { + #if defined(_M_X64) + if (__check_isa_support(__IA_SUPPORT_AVX10_2, 0)) { + std::cout << "/arch:AVX10.2"; + } else if (__check_isa_support(__IA_SUPPORT_AVX10, 0)) { + std::cout << "/arch:AVX10.1"; + } else if (__check_isa_support(__IA_SUPPORT_VECTOR512, 0)) { + std::cout << "/arch:AVX512"; + } else if (__check_isa_support(__IA_SUPPORT_VECTOR256, 0)) { + std::cout << "/arch:AVX2"; + } else if (__check_isa_support(__IA_SUPPORT_SSE42, 0)) { + std::cout << "/arch:SSE4.2"; + } else if (__check_isa_support(__IA_SUPPORT_VECTOR128, 0)) { + std::cout << "/arch:SSE2"; + } else { + std::cout << ""; + } + #else + # if defined(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv9.4"; + } else + # endif + # if defined(PF_ARM_SME_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv9.2"; + } else + # endif + # if defined(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv9.0"; + } else + # endif + # if defined(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) && defined(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) != FALSE + && IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv8.3"; + } else + # endif + # if defined(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != FALSE) { + # if defined(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv8.2+rcpc"; + } else + # endif + { + std::cout << "/arch:armv8.2"; + } + } else + # endif + # if defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != FALSE) { + # if defined(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) + if (IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv8.2+rcpc"; + } else + # endif + { + std::cout << "/arch:armv8.2"; + } + } else + # endif + if (IsProcessorFeaturePresent(PF_ARM_V8_INSTRUCTIONS_AVAILABLE) != FALSE) { + if (IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != FALSE) { + std::cout << "/arch:armv8.0+lse"; + } else { + std::cout << "/arch:armv8.0"; + } + } else { + std::cout << ""; + } + #endif + } + ]]) + + # Write the source code to a temporary file + string(SHA1 _source_hash "${_source}") + set(_source_file "${CMAKE_CURRENT_BINARY_DIR}/${_source_hash}.cpp") + file(WRITE "${_source_file}" "${_source}") + + # Build and run the temporary file to get the /arch option + # TODO CMake 3.25: Use the new signature for try_run which has a NO_CACHE + # option and doesn't require separate file management. + try_run( + _dmna_run_result _dmna_compile_result "${CMAKE_CURRENT_BINARY_DIR}" + SOURCES "${_source_file}" + CMAKE_FLAGS /EHsc /O2 + CXX_STANDARD 20 + RUN_OUTPUT_VARIABLE _run_output + COMPILE_OUTPUT_VARIABLE _compile_output + ) + + # Clean up the temporary file + file(REMOVE "${_source_file}") + + # Set the /arch option if the run was successful, using a cache variable since + # this check may be relatively expensive. Otherwise, log the error + # and inform the user. + if(_dmna_run_result EQUAL "0") + message(STATUS "Determined MSVC native /arch option: '${_run_output}'") + set("${_arch_option_out}" "${_run_output}" CACHE INTERNAL "Native /arch option") + else() + message(DEBUG "${_compile_output}") + endif() +endfunction() diff --git a/include/AutomationTrack.h b/include/AutomationTrack.h index 60a6af677..fa8f87a40 100644 --- a/include/AutomationTrack.h +++ b/include/AutomationTrack.h @@ -32,7 +32,7 @@ namespace lmms { -class AutomationTrack : public Track +class LMMS_EXPORT AutomationTrack : public Track { Q_OBJECT public: