cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(sesum_impl LANGUAGES CXX)

if(NOT APPLE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static")
endif()

add_library(sesum_sparse_core_obj OBJECT
    sesum_sparse_core.cpp
    sesum_sparse_conversion.cpp
    sesum_sparse_conversion_sort.cpp
    sesum_sparse_conversion_sort.h
    sesum_sparse_boolean.cpp
    sesum_sparse_pair_sum.cpp
    sesum_sparse_pair_sum_impl.h
    sesum_sparse_pair_sum_single_type.h
    sesum_sparse_pair_sum_u64.cpp
    sesum_sparse_pair_sum_u64_uint64.cpp
    sesum_sparse_pair_sum_u64_fp64.cpp
    sesum_sparse_pair_sum_u64_cfp64.cpp
    sesum_sparse_pair_sum_u128.cpp
    sesum_sparse_pair_sum_u128_uint64.cpp
    sesum_sparse_pair_sum_u128_fp64.cpp
    sesum_sparse_pair_sum_u128_cfp64.cpp
    sesum_sparse_pair_sum_u64_uint8.cpp
    sesum_sparse_pair_sum_u64_uint16.cpp
    sesum_sparse_pair_sum_u64_uint32.cpp
    sesum_sparse_pair_sum_u64_uint128.cpp
    sesum_sparse_pair_sum_u64_bigint.cpp
    sesum_sparse_pair_sum_u64_fp32.cpp
    sesum_sparse_pair_sum_u64_cfp32.cpp
    sesum_sparse_pair_sum_u128_uint8.cpp
    sesum_sparse_pair_sum_u128_uint16.cpp
    sesum_sparse_pair_sum_u128_uint32.cpp
    sesum_sparse_pair_sum_u128_uint128.cpp
    sesum_sparse_pair_sum_u128_bigint.cpp
    sesum_sparse_pair_sum_u128_fp32.cpp
    sesum_sparse_pair_sum_u128_cfp32.cpp
    sesum_sparse_cost_model.cpp
)
target_include_directories(sesum_sparse_core_obj PUBLIC ./)
target_link_libraries(sesum_sparse_core_obj PUBLIC sring contract sparse)

# Runtime-selected trace, timing, validation, and fingerprint code is useful
# while developing sparse algorithms, but it is duplicated in every templated
# key/value instantiation.  Keep it enabled by default so the compact build has
# exactly the same diagnostic behavior as the original implementation.  Size-
# constrained private builds may opt out explicitly without changing the
# computational implementation.
option(SESUM_SPARSE_DIAGNOSTICS
       "Compile native sparse trace, timing, validation, and fingerprint paths"
       ON)
if(SESUM_SPARSE_DIAGNOSTICS)
    set(SESUM_SPARSE_DIAGNOSTICS_VALUE 1)
else()
    set(SESUM_SPARSE_DIAGNOSTICS_VALUE 0)
endif()
target_compile_definitions(sesum_sparse_core_obj PRIVATE
    SESUM_SPARSE_DIAGNOSTICS=${SESUM_SPARSE_DIAGNOSTICS_VALUE})

# The project-wide release flags favor dense arithmetic and explicitly enable
# full loop unrolling.  The native sparse implementation contains large
# runtime-selected join strategies that are instantiated for every key/value
# combination.  Unrolling every loop in those strategies multiplies code size
# and instruction-cache pressure without changing the algorithms.  Keep the
# dense/SRing flags untouched, but use the compact form for sparse orchestration
# by default.  This remains configurable so a platform can be benchmarked with
# the former behavior when needed.
option(SESUM_COMPACT_SPARSE_CODE
       "Disable blanket loop unrolling for native sparse translation units"
       ON)
if(SESUM_COMPACT_SPARSE_CODE
        AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    target_compile_options(sesum_sparse_core_obj PRIVATE -fno-unroll-loops)
endif()

# GCC's link-time optimizer can merge repeated control flow across the many
# key/value Sparse instantiations.  Keep fat objects so libsesum_impl.a remains
# a complete, ordinarily linkable static archive, while the final Python
# library also receives those objects directly for whole-Sparse LTO.  This
# direct-object route is required by Darwin GCC builds whose system linker has
# no GCC linker plugin, and works with the older GCC used by the Linux builds.
option(SESUM_SPARSE_DIRECT_FAT_LTO
       "Use direct fat LTO for native Sparse code"
       ON)
set(SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE OFF)
set(SESUM_SPARSE_DIRECT_FAT_LTO_FLAG "")
if(SESUM_SPARSE_DIRECT_FAT_LTO
        AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE ON)
    # MinGW's GCC driver launches one LTRANS process per logical CPU for
    # `-flto=auto`.  On the Windows build VM that expands this library into
    # well over one hundred concurrent compiler processes and exhausts memory.
    # A single LTRANS worker retains whole-program optimization and produces
    # the same portable ISA contract, while keeping Release builds reliable.
    if(MINGW)
        set(SESUM_SPARSE_DIRECT_FAT_LTO_FLAG -flto=1)
    elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "10")
        set(SESUM_SPARSE_DIRECT_FAT_LTO_FLAG -flto=auto)
    else()
        set(SESUM_SPARSE_DIRECT_FAT_LTO_FLAG -flto)
    endif()
    target_compile_definitions(sesum_sparse_core_obj PRIVATE
        SESUM_SPARSE_DIRECT_FAT_LTO=1)
    target_compile_options(sesum_sparse_core_obj PRIVATE
        ${SESUM_SPARSE_DIRECT_FAT_LTO_FLAG} -ffat-lto-objects)
endif()
set(SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE
    "${SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE}" PARENT_SCOPE)
set(SESUM_SPARSE_DIRECT_FAT_LTO_FLAG
    "${SESUM_SPARSE_DIRECT_FAT_LTO_FLAG}" PARENT_SCOPE)

# Keeping all Sparse LTO code in one LTRANS partition changes neither the
# generated algorithms nor the ISA profile, but groups the many related typed
# instantiations together.  Besides a small text reduction, this makes the
# shared library substantially more compressible in the all-backend wheel.
# MinGW remains on its memory-bounded default partitioner: GCC 11 rejects the
# combined Sparse partition because duplicate LTO sections are emitted.
# Intel-mac AVX-512 also keeps the balanced partitioner: focused alternating
# runs found a repeatable 1.0--1.7% native Sparse FP64 regression with one
# partition.  Both exclusions are repeated in the effective flag below so a
# stale option value from a reused CMake cache cannot re-enable either path.
set(SESUM_SPARSE_SINGLE_LTO_PARTITION_DEFAULT ON)
if(MINGW OR (APPLE AND SESUM_BUILD_X86_VARIANT STREQUAL "avx512"))
    set(SESUM_SPARSE_SINGLE_LTO_PARTITION_DEFAULT OFF)
endif()
option(SESUM_SPARSE_SINGLE_LTO_PARTITION
       "Place native Sparse LTO code in one final link partition"
       ${SESUM_SPARSE_SINGLE_LTO_PARTITION_DEFAULT})
set(SESUM_SPARSE_LTO_PARTITION_FLAG "")
if(SESUM_SPARSE_DIRECT_FAT_LTO_ACTIVE
        AND SESUM_SPARSE_SINGLE_LTO_PARTITION
        AND NOT MINGW
        AND NOT (APPLE
            AND SESUM_BUILD_X86_VARIANT STREQUAL "avx512"))
    set(SESUM_SPARSE_LTO_PARTITION_FLAG -flto-partition=one)
endif()
set(SESUM_SPARSE_LTO_PARTITION_FLAG
    "${SESUM_SPARSE_LTO_PARTITION_FLAG}" PARENT_SCOPE)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    target_compile_definitions(
        sesum_sparse_core_obj PRIVATE SESUM_USE_GNU_PARALLEL_SORT=1)
endif()

add_library(sesum_impl STATIC
    sesum_impl.h
    sesum_impl_internal.h
    sesum_integer_parse.h
    sesum_impl_pch.h
    sesum_runtime.h
    sesum_runtime.cpp
    sesum_impl.cpp
    $<TARGET_OBJECTS:sesum_sparse_core_obj>
)
target_include_directories(sesum_impl PUBLIC ./)
target_link_libraries(sesum_impl PUBLIC sring contract sparse)
target_compile_definitions(sesum_impl PRIVATE
    SESUM_SPARSE_DIAGNOSTICS=${SESUM_SPARSE_DIAGNOSTICS_VALUE})

set(SESUM_ENABLE_PCH_DEFAULT ON)
if(MINGW)
    set(SESUM_ENABLE_PCH_DEFAULT OFF)
endif()
option(SESUM_ENABLE_PCH "Enable SESUM precompiled headers"
       ${SESUM_ENABLE_PCH_DEFAULT})
option(SESUM_PCH_INCLUDE_SPARSE_D2 "Include sparse_d2.h in the SESUM PCH" OFF)

if(SESUM_ENABLE_PCH AND COMMAND target_precompile_headers)
    if(SESUM_PCH_INCLUDE_SPARSE_D2)
        target_compile_definitions(sesum_impl PRIVATE SESUM_PCH_INCLUDE_SPARSE_D2)
    endif()
    target_precompile_headers(sesum_impl PRIVATE sesum_impl_pch.h)
    target_precompile_headers(sesum_sparse_core_obj PRIVATE sesum_sparse_pch.h)
    set_source_files_properties(sesum_runtime.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endif()
