Files
ARMSX2/cmake/BuildParameters.cmake
T
Brian Degenhardt 47fa49f3d2 Build: add source-based coverage for the ARM64 recompilers
USE_COVERAGE instruments the build with clang's -fprofile-instr-generate
-fcoverage-mapping, exposed as the clang-coverage preset (build-coverage/,
inheriting clang-devel so the dev asserts stay on, Qt off since nothing in
the test path needs it).

The instrumentation is tree-wide rather than scoped to pcsx2/arm64: much of
the JIT is inline code living in headers that get pulled into core and common
translation units, so narrowing at build time would drop counters for exactly
the code we care about. tools/coverage.sh narrows at report time instead,
where the filter is exact.

The script builds the five gtest binaries, runs them with per-process profile
files, merges, and reports scoped to pcsx2/arm64/ (--scope all widens to the
other ARM64-only sources). Two hazards it defends against:

  - cmake --preset takes the source dir from the working directory and ignores
    -S, so running this through the /home/bmd/ARMSX2 symlink bakes the
    symlinked path into every coverage mapping. It cds first.
  - llvm-cov does not error when --sources matches nothing; it reports every
    file it has data for, which reads as a plausible whole-tree number. The
    filter prefix is read back from CMAKE_HOME_DIRECTORY so it always matches
    what the compiler recorded, and a row-count tripwire fails the run if the
    report escapes its scope anyway.

Baseline for pcsx2/arm64/: 74.65% lines, 79.94% functions, 77.70% regions.
2026-07-28 15:24:36 -07:00

441 lines
18 KiB
CMake

# Extra preprocessor definitions that will be added to all pcsx2 builds
set(PCSX2_DEFS "")
include(GNUInstallDirs)
#-------------------------------------------------------------------------------
# Misc option
#-------------------------------------------------------------------------------
option(ENABLE_TESTS "Enables building the unit tests" ON)
option(ENABLE_RECOMPILER_TEST_HOOKS
"Compile harness hooks (recEeExecuteBlock, recEeIsBlockLinked, etc.) into the EE recompiler. Required by tests/ctest/core/recompilers; release builds should turn this off."
${ENABLE_TESTS})
option(ENABLE_QT_UI "Enables building the PCSX2 Qt interface." ON)
option(ENABLE_GSRUNNER "Enables building the GSRunner by default. It can still be built with `make pcsx2-gsrunner` otherwise." OFF)
option(ENABLE_VURUNNER "Enables building pcsx2-vurunner (headless VU microprogram replayer for codegen iteration). Requires ENABLE_RECOMPILER_TEST_HOOKS=ON." OFF)
option(ENABLE_EERUNNER "Enables building pcsx2-eerunner (headless EE JIT-vs-interpreter divergence localizer) by default. It can still be built with `make pcsx2-eerunner` otherwise." OFF)
option(ENABLE_SDL_FRONTEND "Enables building the SDL3 / kmsdrm frontend (pcsx2-sdl) by default. It can still be built with `make pcsx2-sdl` otherwise." OFF)
option(ENABLE_LIBRETRO "Build the libretro core (pcsx2-libretro / armsx2_libretro.so). Opt-in: when OFF the subdirectory is not added at all, so it never gets built or installed alongside the Qt/SDL frontends. Reconfigure with -DENABLE_LIBRETRO=ON to build it." OFF)
option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else")
option(USE_VTUNE "Plug VTUNE to profile GS JIT.")
option(USE_PERF_JITDUMP "Emit Linux perf jitdump (jit-<pid>.dump) for recompiled JIT blocks; use with perf record/inject." OFF)
option(USE_PERF_MAP "Emit simple /tmp/perf-<pid>.map symbol table for recompiled JIT blocks." OFF)
option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)")
option(BUNDLE_EMOJI_FONT "Bundles Noto Color Emoji for systems whose system emoji font isn't usable by freetype" ON)
option(POSITION_INDEPENDENT_CODE "Generate position-independent code. It is recommended that you leave this on." ON)
#-------------------------------------------------------------------------------
# Graphical option
#-------------------------------------------------------------------------------
if(NOT APPLE)
option(USE_OPENGL "Enable OpenGL GS renderer" ON)
endif()
option(USE_VULKAN "Enable Vulkan GS renderer" ON)
#-------------------------------------------------------------------------------
# Path and lib option
#-------------------------------------------------------------------------------
if(UNIX AND NOT APPLE)
option(ENABLE_SETCAP "Enable networking capability for DEV9" OFF)
option(X11_API "Enable X11 support" ON)
option(WAYLAND_API "Enable Wayland support" ON)
option(USE_BACKTRACE "Enable libbacktrace support" ON)
endif()
if(UNIX)
option(USE_LINKED_FFMPEG "Links with ffmpeg instead of using dynamic loading" OFF)
endif()
if(APPLE)
option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF)
endif()
#-------------------------------------------------------------------------------
# Compiler extra
#-------------------------------------------------------------------------------
option(USE_ASAN "Enable address sanitizer")
option(USE_COVERAGE "Instrument the build with clang source-based coverage (-fprofile-instr-generate -fcoverage-mapping). Use with tools/coverage.sh; requires clang." OFF)
#-------------------------------------------------------------------------------
# if no build type is set, use Devel as default
# Note without the CMAKE_BUILD_TYPE options the value is still defined to ""
# Ensure that the value set by the User is correct to avoid some bad behavior later
#-------------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug|Devel|MinSizeRel|RelWithDebInfo|Release")
set(CMAKE_BUILD_TYPE Devel)
message(STATUS "BuildType set to ${CMAKE_BUILD_TYPE} by default")
endif()
# Add Devel build type
set(CMAKE_C_FLAGS_DEVEL "${CMAKE_C_FLAGS_RELWITHDEBINFO}"
CACHE STRING "Flags used by the C compiler during development builds" FORCE)
set(CMAKE_CXX_FLAGS_DEVEL "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}"
CACHE STRING "Flags used by the C++ compiler during development builds" FORCE)
set(CMAKE_LINKER_FLAGS_DEVEL "${CMAKE_LINKER_FLAGS_RELWITHDEBINFO}"
CACHE STRING "Flags used for linking binaries during development builds" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_DEVEL "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}"
CACHE STRING "Flags used for linking shared libraries during development builds" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_DEVEL "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}"
CACHE STRING "Flags used for linking executables during development builds" FORCE)
# Exclude Debug from the configurations we can import from
set(CMAKE_MAP_IMPORTED_CONFIG_DEVEL "RelWithDebInfo" "Release" "MinSizeRel" "None" "NoConfig" ""
CACHE STRING "Configurations used when importing packages for development builds" FORCE)
if(CMAKE_CONFIGURATION_TYPES)
list(INSERT CMAKE_CONFIGURATION_TYPES 0 Devel)
endif()
mark_as_advanced(CMAKE_C_FLAGS_DEVEL CMAKE_CXX_FLAGS_DEVEL CMAKE_LINKER_FLAGS_DEVEL CMAKE_SHARED_LINKER_FLAGS_DEVEL CMAKE_EXE_LINKER_FLAGS_DEVEL CMAKE_MAP_IMPORTED_CONFIG_DEVEL)
#-------------------------------------------------------------------------------
# Select the architecture
#-------------------------------------------------------------------------------
# Detection keys off the *target* processor (CMAKE_SYSTEM_PROCESSOR), not the
# host, so both native and cross builds select the right arch. The spelling
# varies by toolchain (native MSVC reports "ARM64"/"AMD64"; other toolchains use
# lowercase "arm64"/"aarch64"), so each branch matches all the casings.
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "amd64" OR
"${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64" OR "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
# Multi-ISA only exists on x86.
option(DISABLE_ADVANCE_SIMD "Disable advance use of SIMD (SSE2+ & AVX)" OFF)
list(APPEND PCSX2_DEFS _M_X86=1)
set(ARCH_X86 TRUE)
if(DISABLE_ADVANCE_SIMD)
message(STATUS "Building for x86-64 (Multi-ISA).")
else()
message(STATUS "Building for x86-64.")
endif()
if(MSVC)
# SSE4.1 is not set by MSVC, it uses _M_SSE instead.
list(APPEND PCSX2_DEFS __SSE4_1__=1)
if(USE_CLANG_CL)
# clang-cl => need to explicitly enable SSE4.1.
add_compile_options("-msse4.1")
endif()
else()
# Multi-ISA => SSE4, otherwise native.
if (DISABLE_ADVANCE_SIMD)
add_compile_options("-msse" "-msse2" "-msse4.1" "-mfxsr")
else()
# Can't use march=native on Apple Silicon.
if(NOT APPLE OR "${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
add_compile_options("-march=native")
endif()
endif()
endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ARM64" OR
"${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AARCH64" OR
"${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
message(STATUS "Building for ARM64.")
set(ARCH_ARM64 TRUE)
if(APPLE)
# Min spec is an M1
add_compile_options("-march=armv8.4-a" "-mcpu=apple-m1")
elseif(NOT MSVC)
# Require atomic rmw instructions (LSE, ARMv8.1+). This is the upstream
# default and targets the broad arm64 ecosystem. MSVC (and clang-cl)
# reject -march; their arm64 baseline already includes what we need.
# In-order ARMv8.0 cores without LSE (e.g. Cortex-A53 handhelds, RK3562)
# must build with -march=armv8-a (+ -moutline-atomics) in CMAKE_CXX_FLAGS
# — LSE atomics fault on them. Only apply the v8.1 default when the user
# hasn't chosen an -march: add_compile_options lands AFTER CMAKE_CXX_FLAGS
# on the compile line, so unconditionally adding it here silently
# overrides any user -march (proven by a casal SIGILL on a real A53 device).
if(NOT CMAKE_CXX_FLAGS MATCHES "-march=")
add_compile_options("-march=armv8.1-a")
endif()
endif()
# If we're running on Linux, we need to detect the page/cache line size.
# It could be a virtual machine with 4K pages, or 16K with Asahi.
if(LINUX)
detect_page_size()
list(APPEND PCSX2_DEFS OVERRIDE_HOST_PAGE_SIZE=${HOST_PAGE_SIZE})
detect_cache_line_size()
list(APPEND PCSX2_DEFS OVERRIDE_HOST_CACHE_LINE_SIZE=${HOST_CACHE_LINE_SIZE})
endif()
# Windows page size matches x86-64 (4K).
if(WIN32)
list(APPEND PCSX2_DEFS OVERRIDE_HOST_PAGE_SIZE=0x1000)
# Ship a single unified binary that is optimal on both 64- and 128-byte
# cache line machines. Cache line size is only used for alignas() on hot
# cross-thread structures to avoid false sharing, so over-aligning is a
# strict superset: 128-aligned data is also 64-aligned, avoiding false
# sharing on 64-byte hosts too, at the cost of a few padding bytes.
# Compiling with the smaller value (64) would instead cause real false
# sharing when run on a 128-byte host (e.g. Windows-on-ARM in an Apple
# Silicon VM), so we always target the larger line size here.
list(APPEND PCSX2_DEFS OVERRIDE_HOST_CACHE_LINE_SIZE=128)
endif()
else()
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
# The Qt debugger UI depends on KDDockWidgets. Handheld/ARM64 targets don't ship
# the debugger, so default it off there to drop the dependency; on elsewhere to
# match upstream. Only meaningful when ENABLE_QT_UI is on.
if(ARCH_ARM64)
set(_ENABLE_QT_DEBUGGER_DEFAULT OFF)
else()
set(_ENABLE_QT_DEBUGGER_DEFAULT ON)
endif()
option(ENABLE_QT_DEBUGGER "Build the Qt debugger UI (requires KDDockWidgets)." ${_ENABLE_QT_DEBUGGER_DEFAULT})
# Mobile-GPU GameDB overlay. bin/resources-overlay/armsx2_overrides.yaml carries
# GS/JIT tuning that is correct on tiler GPUs (Adreno/Mali/PowerVR) but would
# regress a desktop/immediate-mode GPU. Android and iOS pack it via their own
# asset build; on ARM64 Linux — where nearly every target is a Qualcomm/Mali
# handheld (Rocknix/Batocera) or a Raspberry Pi tiler — ship it too, regardless
# of frontend (both Qt and SDL Linux builds run on handhelds). Off on
# x86/Windows/macOS. The GameDatabase override loader reads it from
# EmuFolders::Resources and no-ops when it is absent, so this only gates the copy.
if(ARCH_ARM64 AND UNIX AND NOT APPLE)
set(_ENABLE_MOBILE_GAMEDB_OVERLAY_DEFAULT ON)
else()
set(_ENABLE_MOBILE_GAMEDB_OVERLAY_DEFAULT OFF)
endif()
option(ENABLE_MOBILE_GAMEDB_OVERLAY "Ship the mobile-GPU GameDB overlay next to the executable (ARM64 Linux handheld tilers). Default ON for ARM64 Linux, OFF elsewhere." ${_ENABLE_MOBILE_GAMEDB_OVERLAY_DEFAULT})
# Require C++20.
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC AND NOT USE_CLANG_CL)
add_compile_options(
"$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>"
"$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>"
"$<$<COMPILE_LANGUAGE:CXX>:/permissive->"
"$<$<COMPILE_LANGUAGE:CXX>:/Zc:preprocessor>"
# C/C++ codegen flags only: the armasm64 assembler (ASM_MARMASM, used for
# the arm64 FastJmp) rejects /Zo and doesn't want /utf-8.
"$<$<COMPILE_LANGUAGE:C,CXX>:/Zo>"
"$<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>"
)
endif()
if(MSVC)
# Disable Exceptions
string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
else()
add_compile_options(-pipe -fvisibility=hidden -pthread)
add_compile_options(
"$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>"
)
# GCC/Clang warn on every __fi (always_inline) function definition that lacks
# an explicit `inline` keyword. PCSX2 deliberately defines __fi without
# `inline` so that .cpp-defined __fi functions still emit a strong external
# symbol; adding `inline` to the macro globally breaks linkage for those. The
# attribute itself works correctly either way, so suppress the noise.
# Unconditional (not the GNU-only DEFAULT_WARNINGS entry below) because the
# primary toolchain here is Clang, which the GCC-gated list does not cover.
add_compile_options(-Wno-attributes)
endif()
set(CONFIG_REL_NO_DEB $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>)
set(CONFIG_ANY_REL $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>)
if(WIN32)
add_compile_definitions(
$<$<CONFIG:Debug>:_ITERATOR_DEBUG_LEVEL=2>
$<$<CONFIG:Devel>:_ITERATOR_DEBUG_LEVEL=1>
$<${CONFIG_ANY_REL}:_ITERATOR_DEBUG_LEVEL=0>
_HAS_EXCEPTIONS=0
)
list(APPEND PCSX2_DEFS
_CRT_NONSTDC_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
CRT_SECURE_NO_DEPRECATE
_SCL_SECURE_NO_WARNINGS
_UNICODE
UNICODE
)
else()
# Assume everything else is POSIX.
list(APPEND PCSX2_DEFS
__POSIX__
)
endif()
# Enable debug information in release builds for Linux.
# Makes the backtrace actually meaningful.
if(LINUX)
add_compile_options($<$<CONFIG:Release>:-g1>)
endif()
if(MSVC)
# Enable PDB generation in release builds (C/C++ only; armasm64 doesn't take /Zi)
add_compile_options(
$<$<AND:${CONFIG_REL_NO_DEB},$<COMPILE_LANGUAGE:C,CXX>>:/Zi>
)
add_link_options(
$<${CONFIG_REL_NO_DEB}:/DEBUG>
$<${CONFIG_REL_NO_DEB}:/OPT:REF>
$<${CONFIG_REL_NO_DEB}:/OPT:ICF>
)
endif()
if(PACKAGE_MODE)
file(RELATIVE_PATH relative_datadir ${CMAKE_INSTALL_FULL_BINDIR} ${CMAKE_INSTALL_FULL_DATADIR}/PCSX2)
# Compile all source codes with those defines
list(APPEND PCSX2_DEFS
PCSX2_APP_DATADIR="${relative_datadir}")
endif()
if(USE_VTUNE)
list(APPEND PCSX2_DEFS ENABLE_VTUNE)
endif()
if(USE_PERF_JITDUMP AND USE_PERF_MAP)
message(FATAL_ERROR "USE_PERF_JITDUMP and USE_PERF_MAP are mutually exclusive; pick one.")
endif()
if(USE_PERF_JITDUMP)
if(NOT UNIX OR APPLE)
message(FATAL_ERROR "USE_PERF_JITDUMP is Linux-only.")
endif()
list(APPEND PCSX2_DEFS ENABLE_PERF_JITDUMP)
endif()
if(USE_PERF_MAP)
if(NOT UNIX OR APPLE)
message(FATAL_ERROR "USE_PERF_MAP is Linux-only.")
endif()
list(APPEND PCSX2_DEFS ENABLE_PERF_MAP)
endif()
if(USE_OPENGL)
list(APPEND PCSX2_DEFS ENABLE_OPENGL)
endif()
if(USE_VULKAN)
list(APPEND PCSX2_DEFS ENABLE_VULKAN)
endif()
if(X11_API)
list(APPEND PCSX2_DEFS X11_API)
endif()
if(WAYLAND_API)
list(APPEND PCSX2_DEFS WAYLAND_API)
endif()
if(ENABLE_SDL_FRONTEND)
list(APPEND PCSX2_DEFS ENABLE_SDL_FRONTEND)
endif()
# -Wno-attributes: "always_inline function might not be inlinable" <= real spam (thousand of warnings!!!)
# -Wno-missing-field-initializers: standard allow to init only the begin of struct/array in static init. Just a silly warning.
# -Wno-unused-function: warn for function not used in release build
if (MSVC)
set(DEFAULT_WARNINGS)
else()
set(DEFAULT_WARNINGS -Wall -Wextra -Wno-unused-function -Wno-unused-parameter -Wno-missing-field-initializers)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND DEFAULT_WARNINGS -Wno-attributes)
endif()
if (USE_PGO_GENERATE OR USE_PGO_OPTIMIZE)
add_compile_options("-fprofile-dir=${CMAKE_SOURCE_DIR}/profile")
endif()
if (USE_PGO_GENERATE)
add_compile_options(-fprofile-generate)
endif()
if(USE_PGO_OPTIMIZE)
add_compile_options(-fprofile-use)
endif()
list(APPEND PCSX2_DEFS
"$<$<CONFIG:Debug>:PCSX2_DEVBUILD;PCSX2_DEBUG;_DEBUG>"
"$<$<CONFIG:Devel>:PCSX2_DEVBUILD;_DEVEL>")
if (USE_ASAN)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
list(APPEND PCSX2_DEFS ASAN_WORKAROUND)
endif()
if (USE_COVERAGE)
if(NOT USE_CLANG)
message(FATAL_ERROR "USE_COVERAGE requires clang (source-based coverage). Configure with the clang-coverage preset.")
endif()
# Instrument everything rather than just pcsx2/arm64: the emitters are reached
# through core and common call paths, and scoping the *report* (tools/coverage.sh
# passes -sources) is exact where scoping the *build* would silently drop
# counters for inline code that lives in headers outside the filter.
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate)
endif()
if(USE_CLANG AND TIMETRACE)
add_compile_options(-ftime-trace)
endif()
set(PCSX2_WARNINGS ${DEFAULT_WARNINGS})
if(POSITION_INDEPENDENT_CODE)
# Make sure position-independent code is enabled properly.
# Without this check, on some platforms (e.g. Fedora 43) the right flags
# won't be passed to the linker, resulting in a broken build when link time
# optimization is enabled (even with a cmake version >= 3.14).
if(NOT MSVC)
include(CheckPIESupported)
check_pie_supported(OUTPUT_VARIABLE PIE_SUPPORTED_OUTPUT LANGUAGES C CXX)
if((NOT CMAKE_C_LINK_PIE_SUPPORTED) OR (NOT CMAKE_CXX_LINK_PIE_SUPPORTED))
message(WARNING
"The POSITION_INDEPENDENT_CODE option is enabled but is not "
"supported at link time:\n${PIE_SUPPORTED_OUTPUT}")
endif()
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
else()
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
message(WARNING
"The CMAKE_INTERPROCEDURAL_OPTIMIZATION option is enabled but the "
"POSITION_INDEPENDENT_CODE option is disabled. This has been found "
"to result in broken builds on certain platforms.")
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
endif()
#-------------------------------------------------------------------------------
# MacOS-specific things
#-------------------------------------------------------------------------------
if(NOT CMAKE_GENERATOR MATCHES "Xcode")
# Assume Xcode builds aren't being used for distribution
# Helpful because Xcode builds don't build multiple metallibs for different macOS versions
# Also helpful because Xcode's interactive shader debugger requires apps be built for the latest macOS
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
endif()
# CMake defaults the suffix for modules to .so on macOS but wx tells us that the
# extension is .dylib (so that's what we search for)
if(APPLE)
set(CMAKE_SHARED_MODULE_SUFFIX ".dylib")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
if(NOT OSX_USE_DEFAULT_SEARCH_PATH)
# Hack up the path to prioritize the path to built-in OS libraries to
# increase the chance of not depending on a bunch of copies of them
# installed by MacPorts, Fink, Homebrew, etc, and ending up copying
# them into the bundle. Since we depend on libraries which are not
# part of OS X (wx, etc.), however, don't remove the default path
# entirely. This is still kinda evil, since it defeats the user's
# path settings...
# See http://www.cmake.org/cmake/help/v3.0/command/find_program.html
list(APPEND CMAKE_PREFIX_PATH "/usr")
endif()
add_link_options(-Wl,-dead_strip,-dead_strip_dylibs)
endif()