Files
psxrecomp/runtime/guest_implementation.cmake
T

121 lines
6.1 KiB
CMake

include_guard(GLOBAL)
# Bind native guest-function replacements at build time. The original generated
# bodies remain buildable as LLE, and are renamed only in their defining shards
# for HLE reference tests. Dispatch declarations keep the original symbols.
# Every target sharing these generated sources must use the same selection.
function(psxrecomp_select_guest_implementation)
cmake_parse_arguments(IMPL "" "NAME;IMPLEMENTATION;SNAPSHOT_TAG;REFERENCE_PREFIX"
"TARGETS;GENERATED_SOURCES;HLE_SOURCES;SYMBOLS" ${ARGN})
if(IMPL_UNPARSED_ARGUMENTS OR NOT IMPL_NAME MATCHES "^[A-Za-z_][A-Za-z0-9_]*$")
message(FATAL_ERROR "Guest implementation needs a C-identifier NAME and valid arguments")
endif()
if(NOT IMPL_IMPLEMENTATION MATCHES "^(HLE|LLE)$")
message(FATAL_ERROR "${IMPL_NAME}: IMPLEMENTATION must be HLE or LLE")
endif()
if(NOT IMPL_TARGETS OR NOT IMPL_GENERATED_SOURCES OR NOT IMPL_SYMBOLS)
message(FATAL_ERROR "${IMPL_NAME}: TARGETS, GENERATED_SOURCES and SYMBOLS are required")
endif()
if(NOT IMPL_REFERENCE_PREFIX)
set(IMPL_REFERENCE_PREFIX "psx_lle_${IMPL_NAME}_")
endif()
if(NOT IMPL_REFERENCE_PREFIX MATCHES "^[A-Za-z_][A-Za-z0-9_]*$")
message(FATAL_ERROR "${IMPL_NAME}: invalid REFERENCE_PREFIX")
endif()
set(_tag 0)
if(IMPL_IMPLEMENTATION STREQUAL "HLE")
if(NOT IMPL_HLE_SOURCES OR NOT IMPL_SNAPSHOT_TAG MATCHES "^(0[xX][0-9A-Fa-f]+|[0-9]+)$")
message(FATAL_ERROR "${IMPL_NAME}: HLE needs HLE_SOURCES and a numeric nonzero SNAPSHOT_TAG")
endif()
math(EXPR _tag "${IMPL_SNAPSHOT_TAG}")
if(_tag LESS 1 OR _tag GREATER 4294967295)
message(FATAL_ERROR "${IMPL_NAME}: SNAPSHOT_TAG must fit a nonzero uint32_t")
endif()
endif()
foreach(_symbol IN LISTS IMPL_SYMBOLS)
if(NOT _symbol MATCHES "^[A-Za-z_][A-Za-z0-9_]*$")
message(FATAL_ERROR "${IMPL_NAME}: invalid guest symbol ${_symbol}")
endif()
set(_definition_count_${_symbol} 0)
endforeach()
foreach(_source IN LISTS IMPL_GENERATED_SOURCES)
get_filename_component(_source "${_source}" ABSOLUTE)
if(NOT EXISTS "${_source}")
message(FATAL_ERROR "${IMPL_NAME}: missing generated source ${_source}")
endif()
file(READ "${_source}" _text)
foreach(_symbol IN LISTS IMPL_SYMBOLS)
string(REGEX MATCHALL "(^|\n)[ \t]*void[ \t\r\n]+${_symbol}[ \t\r\n]*\\([^)]*\\)[ \t\r\n]*\\{" _definitions "${_text}")
list(LENGTH _definitions _count)
math(EXPR _definition_count_${_symbol} "${_definition_count_${_symbol}} + ${_count}")
if(_count GREATER 0 AND IMPL_IMPLEMENTATION STREQUAL "HLE")
get_property(_claimed SOURCE "${_source}" PROPERTY "PSX_GUEST_BINDING_${_symbol}")
if(_claimed)
message(FATAL_ERROR "${IMPL_NAME}: ${_symbol} is already bound by ${_claimed}")
endif()
set_property(SOURCE "${_source}" PROPERTY "PSX_GUEST_BINDING_${_symbol}" "${IMPL_NAME}")
set_property(SOURCE "${_source}" APPEND PROPERTY COMPILE_DEFINITIONS
"${_symbol}=${IMPL_REFERENCE_PREFIX}${_symbol}")
endif()
endforeach()
endforeach()
foreach(_symbol IN LISTS IMPL_SYMBOLS)
if(NOT _definition_count_${_symbol} EQUAL 1)
message(FATAL_ERROR "${IMPL_NAME}: expected one definition of ${_symbol}, found ${_definition_count_${_symbol}}")
endif()
endforeach()
set(_identity "${CMAKE_CURRENT_BINARY_DIR}/guest-implementations/${IMPL_NAME}.c")
file(GENERATE OUTPUT "${_identity}" CONTENT
"#include \"mod_plugins.h\"\n#include <stdio.h>\nPSX_MOD_CONSTRUCTOR(psx_impl_${IMPL_NAME}) { fprintf(stdout, \"psxrecomp guest implementation: ${IMPL_NAME}=${IMPL_IMPLEMENTATION}\\n\"); }\n")
foreach(_target IN LISTS IMPL_TARGETS)
if(NOT TARGET ${_target})
message(FATAL_ERROR "${IMPL_NAME}: unknown target ${_target}")
endif()
get_target_property(_names ${_target} PSX_GUEST_IMPLEMENTATION_NAMES)
if(NOT _names)
set(_names "")
endif()
if(IMPL_NAME IN_LIST _names)
message(FATAL_ERROR "${_target}: duplicate guest implementation ${IMPL_NAME}")
endif()
list(APPEND _names "${IMPL_NAME}")
set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATION_NAMES "${_names}")
get_target_property(_selections ${_target} PSX_GUEST_IMPLEMENTATIONS)
if(NOT _selections)
set(_selections "")
endif()
list(APPEND _selections "${IMPL_NAME}=${IMPL_IMPLEMENTATION}")
set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATIONS "${_selections}")
list(JOIN _selections "," _selection_text)
get_target_property(_tags ${_target} PSX_GUEST_IMPLEMENTATION_TAGS)
if(NOT _tags)
set(_tags "")
endif()
if(_tag GREATER 0 AND _tag IN_LIST _tags)
message(FATAL_ERROR "${_target}: each HLE family needs a distinct SNAPSHOT_TAG")
endif()
list(APPEND _tags "${_tag}")
set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATION_TAGS "${_tags}")
set(_combined_tag 0)
foreach(_family_tag IN LISTS _tags)
math(EXPR _combined_tag "${_combined_tag} ^ ${_family_tag}")
endforeach()
get_target_property(_definitions ${_target} COMPILE_DEFINITIONS)
if(NOT _definitions)
set(_definitions "")
endif()
list(FILTER _definitions EXCLUDE REGEX "^PSX_(SAVESTATE_IMPL_TAG|BUILD_IMPLEMENTATIONS)=")
set_property(TARGET ${_target} PROPERTY COMPILE_DEFINITIONS "${_definitions}")
target_compile_definitions(${_target} PRIVATE
"PSX_SAVESTATE_IMPL_TAG=${_combined_tag}u"
"PSX_BUILD_IMPLEMENTATIONS=\"${_selection_text}\"")
target_sources(${_target} PRIVATE "${_identity}")
if(IMPL_IMPLEMENTATION STREQUAL "HLE")
target_sources(${_target} PRIVATE ${IMPL_HLE_SOURCES})
endif()
endforeach()
message(STATUS "psxrecomp guest implementation: ${IMPL_NAME}=${IMPL_IMPLEMENTATION}, tag=${_tag}")
endfunction()