# Copyright (c) 2025 The YAC Authors
#
# SPDX-License-Identifier: BSD-3-Clause

# This directory contains checks for known MPI implementation bugs Each check
# consists of: - A .c file in config/checksrc/ that tests for the bug - A .txt
# file in config/checkdoc/ that documents the bug
#
# These checks can be disabled with -DYAC_ENABLE_MPI_CHECKS=OFF

# Helper macro to add an MPI bug check
macro(add_mpi_bug_check check_name source description)
  set(options)
  set(oneValueArgs MPI_PROCS)
  set(multiValueArgs)
  cmake_parse_arguments(
    ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
  )

  # Default to 1 MPI process if not specified
  if(NOT DEFINED ARG_MPI_PROCS)
    set(ARG_MPI_PROCS 1)
  endif()

  # Check if we've already run this check
  if(DEFINED CACHE{YAC_MPI_CHECK_${check_name}})
    return()
  endif()

  message(CHECK_START "Checking for ${description}")

  # Check if mpiexec is available
  if(NOT MPIEXEC_EXECUTABLE)
    message(WARNING "Skipping ${description} - MPIEXEC not found")
    return()
  endif()

  # Try to compile the check
  try_compile(
    COMPILE_RESULT ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/${source}
    LINK_LIBRARIES MPI::MPI_C
    COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/${check_name}
    OUTPUT_VARIABLE COMPILE_OUTPUT
  )

  if(NOT COMPILE_RESULT)
    message(
      WARNING "Failed to compile MPI bug check: ${check_name}\n"
              "${COMPILE_OUTPUT}"
    )
  else()
    # makes it possible to pass arguments like "--oversubscribe" via
    # MPIEXEC_EXECUTABLE
    separate_arguments(MPIEXEC_EXECUTABLE)
    # Run the test with mpiexec
    execute_process(
      COMMAND
        ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${ARG_MPI_PROCS}
        ${MPIEXEC_PREFLAGS} ${CMAKE_CURRENT_BINARY_DIR}/${check_name}
        ${MPIEXEC_POSTFLAGS}
      RESULT_VARIABLE MPI_RUN_RESULT
      OUTPUT_VARIABLE MPI_RUN_OUTPUT
      ERROR_VARIABLE MPI_RUN_OUTPUT
      TIMEOUT 30
    )

    if(MPI_RUN_RESULT EQUAL 0)
      message(CHECK_PASS "PASSED")
      set(YAC_MPI_CHECK_${check_name}
          "PASSED"
          CACHE INTERNAL "Result of MPI bug check: ${check_name}"
      )
    else()
      # Read the documentation file
      set(doc_file "${CMAKE_SOURCE_DIR}/config/checkdoc/${check_name}.txt")
      if(EXISTS "${doc_file}")
        file(READ "${doc_file}" doc_content)
      else()
        set(doc_content "Documentation file not found: ${doc_file}")
      endif()
      message(
        CHECK_FAIL
        "
Your MPI implementation appears to be affected by:
${description}

${doc_content}


This may cause incorrect behavior or crashes in YAC.
Consider upgrading your MPI implementation or using a different one.
You can disable this checks with -DYAC_ENABLE_MPI_CHECKS=OFF
"
      )
      message(
        FATAL_ERROR
          "Faulty MPI implementation detected.\n"
          "ERROR: ${MPI_RUN_RESULT}\n"
          "MPIEXEC_EXECUTABLE: ${MPIEXEC_EXECUTABLE}\n"
          "Program output was:\n${MPI_RUN_OUTPUT}"
      )
    endif()
  endif()
endmacro()

# Add checks for known bugs
add_mpi_bug_check(
  0_mpirun_check 0_mpirun_check.c "launching mpi programs works" MPI_PROCS 1
)
add_mpi_bug_check(
  mpich_3.4.x_ddt_hole
  mpich_3.4.x_ddt_hole.c
  "MPICH 3.4.x bug: derived datatypes with holes (padding) not transferred correctly"
  MPI_PROCS
  1
)
add_mpi_bug_check(
  mpich_3.4.x_mpi_waitall
  mpich_3.4.x_mpi_waitall.c
  "MPICH 3.4.x bug: segfault when calling MPI_Waitall after freeing communicator and datatype"
  MPI_PROCS
  1
)
add_mpi_bug_check(
  mpich_4.0_4.0.2_uint64_allreduce
  mpich_4.0_4.0.2_uint64_allreduce.c
  "MPICH 4.0.0-4.0.2 bug: MPI_Allreduce produces incorrect results for MPI_UINT64_T"
  MPI_PROCS
  2
)
add_mpi_bug_check(
  mpich_4.0.x_ddt_transfer
  mpich_4.0.x_ddt_transfer.c
  "MPICH 4.0.x bug: segfault when using multiple derived datatypes in communication"
  MPI_PROCS
  2
)
