# Building LuaJIT core: bootstrapping, VM, runtime, JIT compiler.
# Major portions taken verbatim or adapted from the uJIT.
# Copyright (C) 2020-2021 LuaVela Authors.
# Copyright (C) 2015-2020 IPONWEB Ltd.

enable_language(ASM)

include_directories(${CMAKE_CURRENT_SOURCE_DIR})

include(MakeSourceList)

# --- Set OS and arch specific flags -------------------------------------------

include(SetTargetFlags)
include(SetDynASMFlags)

# --- Define source tree -------------------------------------------------------

# Core runtime.
make_source_list(SOURCES_RUNTIME
  SOURCES
    lj_api.c
    lj_bc.c
    lj_buf.c
    lj_debug.c
    lj_dispatch.c
    lj_err.c
    lj_func.c
    lj_gc.c
    lj_lib.c
    lj_load.c
    lj_mapi.c
    lj_meta.c
    lj_obj.c
    lj_state.c
    lj_str.c
    lj_strfmt.c
    lj_strfmt_num.c
    lj_strscan.c
    lj_tab.c
    lj_udata.c
    lj_vmevent.c
    lib_aux.c
    lib_init.c
)

# Lua frontend.
make_source_list(SOURCES_FRONTEND
  SOURCES
    lj_bcread.c
    lj_bcwrite.c
    lj_lex.c
    lj_parse.c
)

make_source_list(SOURCES_UTILS
  SOURCES
    lj_alloc.c
    lj_assert.c
    lj_char.c
    lj_utils_leb128.c
    lj_vmmath.c
    lj_wbuf.c
)

make_source_list(SOURCES_PROFILER
  SOURCES
    lj_memprof.c
    lj_profile.c
    lj_profile_timer.c
    lj_symtab.c
    lj_sysprof.c
)

# Lua standard library + extensions by LuaJIT.
make_source_list(SOURCES_LUA_LIB
  # XXX: Please do not change the order of the libraries
  # (required by buildvm).
  SOURCES
    lib_base.c
    lib_math.c
    lib_bit.c
    lib_string.c
    lib_table.c
    lib_io.c
    lib_os.c
    lib_package.c
    lib_debug.c
    lib_jit.c
    lib_ffi.c
    lib_misc.c
)

# JIT compiler, core part.
make_source_list(SOURCES_JIT_CORE
  SOURCES
    lj_asm.c
    lj_ffrecord.c
    lj_ir.c
    lj_mcode.c
    lj_record.c
    lj_snap.c
    lj_trace.c
)

# JIT compiler, machine-independent optimizations.
make_source_list(SOURCES_JIT_OPT
  SOURCES
    lj_opt_dce.c
    lj_opt_fold.c
    lj_opt_loop.c
    lj_opt_mem.c
    lj_opt_narrow.c
    lj_opt_sink.c
)

make_source_list(SOURCES_FFI
  SOURCES
    lj_carith.c
    lj_ccallback.c
    lj_ccall.c
    lj_cconv.c
    lj_cdata.c
    lj_clib.c
    lj_cparse.c
    lj_crecord.c
    lj_ctype.c
)

make_source_list(SOURCES_JIT
  SOURCES
    ${SOURCES_JIT_CORE}
    ${SOURCES_JIT_OPT}
)

# Everything except FFI and JIT.
make_source_list(SOURCES_CORE_NO_JIT_FFI
  SOURCES
    ${SOURCES_RUNTIME}
    ${SOURCES_LUA_LIB}
    ${SOURCES_FRONTEND}
    ${SOURCES_PROFILER}
    ${SOURCES_UTILS}
)

set(SOURCES_CORE ${SOURCES_CORE_NO_JIT_FFI})

# Build JIT sources if JIT support is enabled.
if(NOT LUAJIT_DISABLE_JIT)
  list(APPEND SOURCES_CORE ${SOURCES_JIT})
  if(LUAJIT_USE_GDBJIT)
    list(APPEND SOURCES_CORE ${CMAKE_CURRENT_SOURCE_DIR}/lj_gdbjit.c)
  endif()
  # The support provided within src/lj_opt_split.c is required
  # only for soft-float targets or for 32 bit CPUs which lack
  # native 64 bit integer operations (the FFI is currently the
  # only emitter for 64 bit integer instructions).
  # Detect the target by the set DYNASM_FLAGS.
  list(FIND DYNASM_FLAGS FPU HASFPU)
  list(FIND DYNASM_FLAGS P64 HASP64)
  # XXX: The CMake condition below should be the same as the
  # following #ifdef condition (LJ_HASJIT is already 'true'):
  # #if LJ_HASJIT && (LJ_SOFTFP || (LJ_32 && LJ_HASFFI))
  if(HASFPU LESS 0 OR HASP64 LESS 0 AND NOT LUAJIT_DISABLE_FFI)
    list(APPEND SOURCES_CORE ${CMAKE_CURRENT_SOURCE_DIR}/lj_opt_split.c)
  endif()
endif()

# Build FFI sources if FFI support is enabled.
if(NOT LUAJIT_DISABLE_FFI)
  list(APPEND SOURCES_CORE ${SOURCES_FFI})
  # Build lj_mcode.c if JIT support is disabled since
  # <lj_mcode_sync> is used in src/lj_ccallback.c.
  if(LUAJIT_DISABLE_JIT)
    list(APPEND SOURCES_CORE ${CMAKE_CURRENT_SOURCE_DIR}/lj_mcode.c)
  endif()
endif()

make_source_list(CLI_SOURCES
  SOURCES
    luajit.c
)

# --- Prepare files generated by buildvm ---------------------------------------

add_subdirectory(host)

# VM assembly.
add_custom_command(
  OUTPUT lj_vm.S
  COMMAND $<TARGET_FILE:buildvm> -m ${BUILDVM_MODE} -o lj_vm.S
  DEPENDS buildvm
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Bytecode definitions.
add_custom_command(
  OUTPUT lj_bcdef.h
  COMMAND $<TARGET_FILE:buildvm> -m bcdef -o lj_bcdef.h ${SOURCES_LUA_LIB}
  DEPENDS buildvm ${SOURCES_LUA_LIB}
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Fast function definitions.
add_custom_command(
  OUTPUT lj_ffdef.h
  COMMAND $<TARGET_FILE:buildvm> -m ffdef -o lj_ffdef.h ${SOURCES_LUA_LIB}
  DEPENDS buildvm ${SOURCES_LUA_LIB}
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Library definitions.
add_custom_command(
  OUTPUT lj_libdef.h
  COMMAND $<TARGET_FILE:buildvm> -m libdef -o lj_libdef.h ${SOURCES_LUA_LIB}
  DEPENDS buildvm ${SOURCES_LUA_LIB}
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Recorder definitions.
add_custom_command(
  OUTPUT lj_recdef.h
  COMMAND $<TARGET_FILE:buildvm> -m recdef -o lj_recdef.h ${SOURCES_LUA_LIB}
  DEPENDS buildvm ${SOURCES_LUA_LIB}
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# Fold definitions.
add_custom_command(
  OUTPUT lj_folddef.h
  COMMAND $<TARGET_FILE:buildvm> -m folddef -o lj_folddef.h
          ${CMAKE_CURRENT_SOURCE_DIR}/lj_opt_fold.c
  DEPENDS buildvm lj_opt_fold.c
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

# VM definitions.
add_custom_command(
  OUTPUT jit/vmdef.lua
  COMMAND ${CMAKE_COMMAND} -E make_directory jit
  COMMAND $<TARGET_FILE:buildvm> -m vmdef -o jit/vmdef.lua ${SOURCES_LUA_LIB}
  DEPENDS buildvm ${SOURCES_LUA_LIB}
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

add_custom_target(
  buildvm_output
  DEPENDS lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h
          jit/vmdef.lua
)

# --- Generate core and VM object files ---------------------------------------

# Virtual machine.
add_library(vm_static OBJECT EXCLUDE_FROM_ALL lj_vm.S)
add_library(vm_shared OBJECT EXCLUDE_FROM_ALL lj_vm.S)
set_property(TARGET vm_shared APPEND PROPERTY
  POSITION_INDEPENDENT_CODE ON
)
set_property(TARGET vm_static vm_shared APPEND PROPERTY
  COMPILE_FLAGS "${TARGET_VM_FLAGS}"
)

# Platform core.
add_library(core_static OBJECT EXCLUDE_FROM_ALL ${SOURCES_CORE})
add_library(core_shared OBJECT EXCLUDE_FROM_ALL ${SOURCES_CORE})
set_property(TARGET core_shared APPEND PROPERTY
  POSITION_INDEPENDENT_CODE ON
)
set_property(TARGET core_static core_shared APPEND PROPERTY
  COMPILE_FLAGS "${TARGET_C_FLAGS}"
)
target_include_directories(core_static PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(core_shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
add_dependencies(core_static buildvm_output)
add_dependencies(core_shared buildvm_output)

# --- Generate output ----------------------------------------------------------

# Compiling and linking library binaries (static, shared).

list(APPEND TARGET_LIBS m)

set(LIB_OBJECTS_STATIC
  $<TARGET_OBJECTS:vm_static>
  $<TARGET_OBJECTS:core_static>
)
set(LIB_OBJECTS_SHARED
  $<TARGET_OBJECTS:vm_shared>
  $<TARGET_OBJECTS:core_shared>
)

add_library(libluajit_static STATIC EXCLUDE_FROM_ALL ${LIB_OBJECTS_STATIC})
set_target_properties(libluajit_static PROPERTIES
  OUTPUT_NAME "${LUAJIT_LIB_NAME}"
  COMPILE_FLAGS "${TARGET_C_FLAGS}"
  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
target_link_libraries(libluajit_static ${TARGET_LIBS})

add_library(libluajit_shared SHARED EXCLUDE_FROM_ALL ${LIB_OBJECTS_SHARED})
set_target_properties(libluajit_shared PROPERTIES
  OUTPUT_NAME "${LUAJIT_LIB_NAME}"
  COMPILE_FLAGS "${TARGET_C_FLAGS}"
  LINK_FLAGS "${TARGET_SHARED_FLAGS}"
  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  VERSION "${LUAJIT_VERSION_MAJOR}.${LUAJIT_VERSION_MINOR}.${LUAJIT_VERSION_PATCH}"
  SOVERSION "${LUAJIT_VERSION_MAJOR}"
)
target_link_libraries(libluajit_shared ${TARGET_LIBS})

# Compiling and linking CLIs.

# XXX: Order of the branches below matters, since when BUILDMODE
# is set to "mixed", static binary should be built.
if(NOT BUILDMODE STREQUAL "static")
  set(LIBLUAJIT_SHARED_DEPS libluajit_shared)
  set(LUAJIT_BIN luajit_shared)
  set(LUAJIT_LIB libluajit_shared)
endif()
if(NOT BUILDMODE STREQUAL "dynamic")
  set(LIBLUAJIT_STATIC_DEPS libluajit_static)
  set(LUAJIT_BIN luajit_static)
  set(LUAJIT_LIB libluajit_static)
endif()
# Need for the test linking, so the PARENT_SCOPE option is used.
set(LUAJIT_LIBRARY ${LUAJIT_LIB} PARENT_SCOPE)
set(LIBLUAJIT_DEPS ${LIBLUAJIT_STATIC_DEPS} ${LIBLUAJIT_SHARED_DEPS})

add_executable(${LUAJIT_BIN} EXCLUDE_FROM_ALL ${CLI_SOURCES})
set_target_properties(${LUAJIT_BIN} PROPERTIES
  OUTPUT_NAME "${LUAJIT_CLI_NAME}"
  COMPILE_FLAGS "${TARGET_C_FLAGS}"
  LINK_FLAGS "${TARGET_BIN_FLAGS}"
  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
target_include_directories(${LUAJIT_BIN} PRIVATE
  ${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(${LUAJIT_BIN} ${LUAJIT_LIB} ${TARGET_LIBS})

# XXX: The variable is used in testing, so PARENT_SCOPE option
# is obligatory.
set(LUAJIT_BINARY $<TARGET_FILE:${LUAJIT_BIN}> PARENT_SCOPE)

add_custom_target(libluajit DEPENDS ${LIBLUAJIT_DEPS})
add_custom_target(luajit-main ALL DEPENDS libluajit ${LUAJIT_BIN})

# Unfortunately, CMake provides no guarantees for install commands
# used for the targets excluded from <all> and obliges user to
# handle this manually on his side. Hence check whether the
# targets used below are presented for the chosen build mode.
# See more info in CMake docs below:
# https://cmake.org/cmake/help/v3.1/prop_tgt/EXCLUDE_FROM_ALL.html
if(TARGET ${LUAJIT_BIN})
  install(TARGETS ${LUAJIT_BIN}
    RUNTIME
    DESTINATION bin
    COMPONENT luajit-main
  )
endif()

if(TARGET ${LIBLUAJIT_STATIC_DEPS})
  install(TARGETS ${LIBLUAJIT_STATIC_DEPS}
    ARCHIVE
    DESTINATION lib
    COMPONENT luajit-main
  )
endif()

if(TARGET ${LIBLUAJIT_SHARED_DEPS})
  install(TARGETS ${LIBLUAJIT_SHARED_DEPS}
    LIBRARY
    DESTINATION lib
    COMPONENT luajit-main
  )
endif()

install(FILES
    ${CMAKE_CURRENT_SOURCE_DIR}/lua.h       # C API for Lua
    ${CMAKE_CURRENT_SOURCE_DIR}/lualib.h    # Lua standard libraries
    ${CMAKE_CURRENT_SOURCE_DIR}/lauxlib.h   # Auxiliary library's C API
    ${CMAKE_CURRENT_SOURCE_DIR}/luaconf.h   # Configuration header
    ${CMAKE_CURRENT_SOURCE_DIR}/lua.hpp     # Convenience wrapper for C++
    ${CMAKE_CURRENT_SOURCE_DIR}/luajit.h    # LuaJIT-specific header
    ${CMAKE_CURRENT_SOURCE_DIR}/lmisclib.h  # Miscellaneous C API extensions
  DESTINATION ${LUAJIT_INCLUDEDIR}
  PERMISSIONS
    OWNER_READ OWNER_WRITE
    GROUP_READ
    WORLD_READ
  COMPONENT luajit-main
)

install(FILES
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/bc.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/bcsave.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_arm.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_arm64.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_arm64be.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_mips.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_mips64.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_mips64el.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_mipsel.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_ppc.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_x64.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dis_x86.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/dump.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/p.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/v.lua
    ${CMAKE_CURRENT_SOURCE_DIR}/jit/zone.lua
    ${CMAKE_CURRENT_BINARY_DIR}/jit/vmdef.lua
  DESTINATION ${LUAJIT_DATAROOTDIR}/jit
  PERMISSIONS
    OWNER_READ OWNER_WRITE
    GROUP_READ
    WORLD_READ
  COMPONENT luajit-main
)
