
This PR simplifies how we enable the different fma configs for riscv: 1. Removes __LIBC_RISCV_USE_FMA define 2. Checks if __riscv_flen is defined to set LIBC_TARGET_CPU_HAS_FMA As a bonus, we enable *FMA_OPT tests for rv32, so any rv32 hardware that doesn't implement the f/d extensions is also covered by the tests.
308 lines
10 KiB
CMake
308 lines
10 KiB
CMake
# In general, a flag is a string provided for supported functions under the
|
|
# multi-valued option `FLAGS`. It should be one of the following forms:
|
|
# FLAG_NAME
|
|
# FLAG_NAME__NO
|
|
# FLAG_NAME__ONLY
|
|
# A target will inherit all the flags of its upstream dependency.
|
|
#
|
|
# When we create a target `TARGET_NAME` with a flag using (add_header_library,
|
|
# add_object_library, ...), its behavior will depend on the flag form as follow:
|
|
# - FLAG_NAME: The following 2 targets will be generated:
|
|
# `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property.
|
|
# `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if
|
|
# `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS`
|
|
# property.
|
|
# - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME`
|
|
# in its `FLAGS` property.
|
|
# - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME` that depends on
|
|
# `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME`
|
|
# in its `FLAGS` property.
|
|
#
|
|
# To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake.
|
|
# To show all the targets' dependency and flags, pass
|
|
# SHOW_INTERMEDIATE_OBJECTS=DEPS to cmake.
|
|
#
|
|
# To completely disable a flag FLAG_NAME expansion, set the variable
|
|
# SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file.
|
|
|
|
|
|
function(extract_flag_modifier input_flag output_flag modifier)
|
|
if(${input_flag} MATCHES "__NO$")
|
|
string(REGEX REPLACE "__NO$" "" flag "${input_flag}")
|
|
set(${output_flag} ${flag} PARENT_SCOPE)
|
|
set(${modifier} "NO" PARENT_SCOPE)
|
|
elseif(${input_flag} MATCHES "__ONLY$")
|
|
string(REGEX REPLACE "__ONLY$" "" flag "${input_flag}")
|
|
set(${output_flag} ${flag} PARENT_SCOPE)
|
|
set(${modifier} "ONLY" PARENT_SCOPE)
|
|
else()
|
|
set(${output_flag} ${input_flag} PARENT_SCOPE)
|
|
set(${modifier} "" PARENT_SCOPE)
|
|
endif()
|
|
endfunction(extract_flag_modifier)
|
|
|
|
function(remove_duplicated_flags input_flags output_flags)
|
|
set(out_flags "")
|
|
foreach(input_flag IN LISTS input_flags)
|
|
if(NOT input_flag)
|
|
continue()
|
|
endif()
|
|
|
|
extract_flag_modifier(${input_flag} flag modifier)
|
|
|
|
# Check if the flag is skipped.
|
|
if(${SKIP_FLAG_EXPANSION_${flag}})
|
|
if("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
|
|
message(STATUS " Flag ${flag} is ignored.")
|
|
endif()
|
|
continue()
|
|
endif()
|
|
|
|
set(found FALSE)
|
|
foreach(out_flag IN LISTS out_flags)
|
|
extract_flag_modifier(${out_flag} o_flag o_modifier)
|
|
if("${flag}" STREQUAL "${o_flag}")
|
|
set(found TRUE)
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
if(NOT found)
|
|
list(APPEND out_flags ${input_flag})
|
|
endif()
|
|
endforeach()
|
|
|
|
set(${output_flags} "${out_flags}" PARENT_SCOPE)
|
|
endfunction(remove_duplicated_flags)
|
|
|
|
# Collect flags from dependency list. To see which flags come with each
|
|
# dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake.
|
|
function(get_flags_from_dep_list output_list)
|
|
set(flag_list "")
|
|
foreach(dep IN LISTS ARGN)
|
|
if(NOT dep)
|
|
continue()
|
|
endif()
|
|
|
|
get_fq_dep_name(fq_dep_name ${dep})
|
|
|
|
if(NOT TARGET ${fq_dep_name})
|
|
continue()
|
|
endif()
|
|
|
|
get_target_property(flags ${fq_dep_name} "FLAGS")
|
|
|
|
if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")
|
|
message(STATUS " FLAGS from dependency ${fq_dep_name} are ${flags}")
|
|
endif()
|
|
|
|
foreach(flag IN LISTS flags)
|
|
if(flag)
|
|
list(APPEND flag_list ${flag})
|
|
endif()
|
|
endforeach()
|
|
endforeach(dep)
|
|
|
|
list(REMOVE_DUPLICATES flag_list)
|
|
|
|
set(${output_list} ${flag_list} PARENT_SCOPE)
|
|
endfunction(get_flags_from_dep_list)
|
|
|
|
# Given a `flag` without modifier, scan through the list of dependency, append
|
|
# `.__NO_flag` to any target that has `flag` in its FLAGS property.
|
|
function(get_fq_dep_list_without_flag output_list flag)
|
|
set(fq_dep_no_flag_list "")
|
|
foreach(dep IN LISTS ARGN)
|
|
get_fq_dep_name(fq_dep_name ${dep})
|
|
if(TARGET ${fq_dep_name})
|
|
get_target_property(dep_flags ${fq_dep_name} "FLAGS")
|
|
# Only target with `flag` has `.__NO_flag` target, `flag__NO` and
|
|
# `flag__ONLY` do not.
|
|
if(${flag} IN_LIST dep_flags)
|
|
list(APPEND fq_dep_no_flag_list "${fq_dep_name}.__NO_${flag}")
|
|
else()
|
|
list(APPEND fq_dep_no_flag_list ${fq_dep_name})
|
|
endif()
|
|
else()
|
|
list(APPEND fq_dep_no_flag_list ${fq_dep_name})
|
|
endif()
|
|
endforeach(dep)
|
|
set(${output_list} ${fq_dep_no_flag_list} PARENT_SCOPE)
|
|
endfunction(get_fq_dep_list_without_flag)
|
|
|
|
# Check if a `flag` is set
|
|
function(check_flag result flag_name)
|
|
list(FIND ARGN ${flag_name} has_flag)
|
|
if(${has_flag} LESS 0)
|
|
list(FIND ARGN "${flag_name}__ONLY" has_flag)
|
|
endif()
|
|
if(${has_flag} GREATER -1)
|
|
set(${result} TRUE PARENT_SCOPE)
|
|
else()
|
|
set(${result} FALSE PARENT_SCOPE)
|
|
endif()
|
|
endfunction(check_flag)
|
|
|
|
# Generate all flags' combinations and call the corresponding function provided
|
|
# by `CREATE_TARGET` to create a target for each combination.
|
|
function(expand_flags_for_target target_name flags)
|
|
cmake_parse_arguments(
|
|
"EXPAND_FLAGS"
|
|
"" # Optional arguments
|
|
"CREATE_TARGET" # Single-value arguments
|
|
"DEPENDS;FLAGS" # Multi-value arguments
|
|
${ARGN}
|
|
)
|
|
|
|
list(LENGTH flags nflags)
|
|
if(NOT ${nflags})
|
|
cmake_language(CALL ${EXPAND_FLAGS_CREATE_TARGET}
|
|
${target_name}
|
|
${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
|
|
DEPENDS ${EXPAND_FLAGS_DEPENDS}
|
|
FLAGS ${EXPAND_FLAGS_FLAGS}
|
|
)
|
|
return()
|
|
endif()
|
|
|
|
list(GET flags 0 flag)
|
|
list(REMOVE_AT flags 0)
|
|
extract_flag_modifier(${flag} real_flag modifier)
|
|
|
|
if(NOT "${modifier}" STREQUAL "NO")
|
|
expand_flags_for_target(
|
|
${target_name}
|
|
"${flags}"
|
|
DEPENDS ${EXPAND_FLAGS_DEPENDS}
|
|
FLAGS ${EXPAND_FLAGS_FLAGS}
|
|
CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
|
|
${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
|
|
)
|
|
endif()
|
|
|
|
if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
|
|
return()
|
|
endif()
|
|
|
|
set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
|
|
list(REMOVE_ITEM NEW_FLAGS ${flag})
|
|
get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
|
|
|
|
# Only target with `flag` has `.__NO_flag` target, `flag__NO` and
|
|
# `flag__ONLY` do not.
|
|
if("${modifier}" STREQUAL "")
|
|
set(TARGET_NAME "${target_name}.__NO_${flag}")
|
|
else()
|
|
set(TARGET_NAME "${target_name}")
|
|
endif()
|
|
|
|
expand_flags_for_target(
|
|
${TARGET_NAME}
|
|
"${flags}"
|
|
DEPENDS ${NEW_DEPS}
|
|
FLAGS ${NEW_FLAGS}
|
|
CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}
|
|
${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
|
|
)
|
|
endfunction(expand_flags_for_target)
|
|
|
|
# Collect all flags from a target's dependency, and then forward to
|
|
# `expand_flags_for_target to generate all flags' combinations and call
|
|
# the corresponding function provided by `CREATE_TARGET` to create a target for
|
|
# each combination.
|
|
function(add_target_with_flags target_name)
|
|
cmake_parse_arguments(
|
|
"ADD_TO_EXPAND"
|
|
"" # Optional arguments
|
|
"CREATE_TARGET;" # Single value arguments
|
|
"DEPENDS;FLAGS;ADD_FLAGS" # Multi-value arguments
|
|
${ARGN}
|
|
)
|
|
|
|
if(NOT target_name)
|
|
message(FATAL_ERROR "Bad target name")
|
|
endif()
|
|
|
|
if(NOT ADD_TO_EXPAND_CREATE_TARGET)
|
|
message(FATAL_ERROR "Missing function to create targets. Please specify "
|
|
"`CREATE_TARGET <function>`")
|
|
endif()
|
|
|
|
get_fq_target_name(${target_name} fq_target_name)
|
|
|
|
if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
|
|
message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
|
|
endif()
|
|
|
|
get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
|
|
get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
|
|
|
|
# Appending ADD_FLAGS before flags from dependency.
|
|
if(ADD_TO_EXPAND_ADD_FLAGS)
|
|
list(APPEND ADD_TO_EXPAND_FLAGS ${ADD_TO_EXPAND_ADD_FLAGS})
|
|
endif()
|
|
list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
|
|
remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
|
|
list(SORT flags)
|
|
|
|
if(SHOW_INTERMEDIATE_OBJECTS AND flags)
|
|
message(STATUS "Target ${fq_target_name} has FLAGS: ${flags}")
|
|
endif()
|
|
|
|
expand_flags_for_target(
|
|
${fq_target_name}
|
|
"${flags}"
|
|
DEPENDS "${fq_deps_list}"
|
|
FLAGS "${flags}"
|
|
CREATE_TARGET ${ADD_TO_EXPAND_CREATE_TARGET}
|
|
${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
|
|
)
|
|
endfunction(add_target_with_flags)
|
|
|
|
# Special flags
|
|
set(FMA_OPT_FLAG "FMA_OPT")
|
|
set(ROUND_OPT_FLAG "ROUND_OPT")
|
|
# This flag controls whether we use explicit SIMD instructions or not.
|
|
set(EXPLICIT_SIMD_OPT_FLAG "EXPLICIT_SIMD_OPT")
|
|
# This flag controls whether we use compiler builtin functions to implement
|
|
# various basic math operations or not.
|
|
set(MISC_MATH_BASIC_OPS_OPT_FLAG "MISC_MATH_BASIC_OPS_OPT")
|
|
|
|
# Skip FMA_OPT flag for targets that don't support fma.
|
|
if(NOT DEFINED SKIP_FLAG_EXPANSION_FMA_OPT)
|
|
if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR
|
|
LIBC_TARGET_ARCHITECTURE_IS_ANY_RISCV))
|
|
set(SKIP_FLAG_EXPANSION_FMA_OPT TRUE)
|
|
endif()
|
|
endif()
|
|
|
|
# Skip EXPLICIT_SIMD_OPT flag for targets that don't support SSE2.
|
|
# Note: one may want to revisit it if they want to control other explicit SIMD
|
|
if(NOT DEFINED SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT)
|
|
if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))
|
|
set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)
|
|
endif()
|
|
endif()
|
|
|
|
# Skip ROUND_OPT flag for targets that don't support rounding instructions. On
|
|
# x86, these are SSE4.1 instructions, but we already had code to check for
|
|
# SSE4.2 support.
|
|
if(NOT DEFINED SKIP_FLAG_EXPANSION_ROUND_OPT)
|
|
if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) OR
|
|
LIBC_TARGET_ARCHITECTURE_IS_AARCH64 OR LIBC_TARGET_OS_IS_GPU))
|
|
set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)
|
|
endif()
|
|
endif()
|
|
|
|
# Choose whether time_t is 32- or 64-bit, based on target architecture
|
|
# and config options. This will be used to set a #define during the
|
|
# library build, and also to select the right version of time_t.h for
|
|
# the output headers.
|
|
if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT))
|
|
# Set time_t to 32 bit for compatibility with glibc, unless
|
|
# configuration says otherwise
|
|
set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE)
|
|
else()
|
|
# Other platforms default to 64-bit time_t
|
|
set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)
|
|
endif()
|