# Determine the host CPU feature set and determine the best set of compiler flags to enable all
# supported SIMD relevant features. Alternatively, the target CPU can be explicitly selected (for
# generating more generic binaries or for targeting a different system). Compilers provide e.g. the
# -march=native flag to achieve a similar result. This fails to address the need for building for a
# different microarchitecture than the current host. The script tries to deduce all settings from
# the model and family numbers of the CPU instead of reading the CPUID flags from e.g.
# /proc/cpuinfo. This makes the detection more independent from the CPUID code in the kernel (e.g.
# avx2 is not listed on older kernels).
#
# Usage: OptimizeForArchitecture() If either of Vc_SSE_INTRINSICS_BROKEN, Vc_AVX_INTRINSICS_BROKEN,
# Vc_AVX2_INTRINSICS_BROKEN is defined and set, the OptimizeForArchitecture macro will consequently
# disable the relevant features via compiler flags.

# =============================================================================
# Copyright 2010-2016 Matthias Kretz <kretz@kde.org>
#
# Redistribution and use in source and binary forms, with or without modification, are permitted
# provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this list of conditions
#   and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, this list of
#   conditions and the following disclaimer in the documentation and/or other materials provided
#   with the distribution.
# * Neither the names of contributing organizations nor the names of its contributors may be used to
#   endorse or promote products derived from this software without specific prior written
#   permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# =============================================================================

get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH)
# include("${_currentDir}/AddCompilerFlag.cmake") include(CheckIncludeFileCXX)

macro(_my_find _list _value _ret)
  list(FIND ${_list} "${_value}" _found)
  if(_found EQUAL -1)
    set(${_ret} FALSE)
  else(_found EQUAL -1)
    set(${_ret} TRUE)
  endif(_found EQUAL -1)
endmacro(_my_find)

macro(OFA_AutodetectX86)
  set(_vendor_id)
  set(_cpu_family)
  set(_cpu_model)
  if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    file(READ "/proc/cpuinfo" _cpuinfo)
    string(REGEX REPLACE ".*vendor_id[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _vendor_id
                         "${_cpuinfo}")
    string(REGEX REPLACE ".*cpu family[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _cpu_family
                         "${_cpuinfo}")
    string(REGEX REPLACE ".*model[ \t]*:[ \t]+([a-zA-Z0-9_-]+).*" "\\1" _cpu_model "${_cpuinfo}")
    string(REGEX REPLACE ".*flags[ \t]*:[ \t]+([^\n]+).*" "\\1" _cpu_flags "${_cpuinfo}")
  elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    exec_program("/usr/sbin/sysctl -n machdep.cpu.vendor" OUTPUT_VARIABLE _vendor_id)
    exec_program("/usr/sbin/sysctl -n machdep.cpu.model" OUTPUT_VARIABLE _cpu_model)
    exec_program("/usr/sbin/sysctl -n machdep.cpu.family" OUTPUT_VARIABLE _cpu_family)
    exec_program("/usr/sbin/sysctl -n machdep.cpu.features" OUTPUT_VARIABLE _cpu_flags)
    string(TOLOWER "${_cpu_flags}" _cpu_flags)
    string(REPLACE "." "_" _cpu_flags "${_cpu_flags}")
  elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    get_filename_component(
      _vendor_id
      "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;VendorIdentifier]"
      NAME CACHE)
    get_filename_component(
      _cpu_id "[HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0;Identifier]"
      NAME CACHE)
    mark_as_advanced(_vendor_id _cpu_id)
    string(REGEX REPLACE ".* Family ([0-9]+) .*" "\\1" _cpu_family "${_cpu_id}")
    string(REGEX REPLACE ".* Model ([0-9]+) .*" "\\1" _cpu_model "${_cpu_id}")
  endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  if(_vendor_id STREQUAL "GenuineIntel")
    if(_cpu_family EQUAL 6)
      # taken from the Intel ORM http://www.intel.com/content/www/us/en/processors/architectures-
      # software-developer-manuals.html CPUID Signature Values of Of Recent Intel Microarchitectures
      # 4E 5E       | Skylake microarchitecture 3D 47 56    | Broadwell microarchitecture 3C 45 46
      # 3F | Haswell microarchitecture 3A 3E       | Ivy Bridge microarchitecture 2A 2D       |
      # Sandy Bridge microarchitecture 25 2C 2F    | Intel microarchitecture Westmere 1A 1E 1F 2E |
      # Intel microarchitecture Nehalem 17 1D       | Enhanced Intel Core microarchitecture 0F |
      # Intel Core microarchitecture
      #
      # Intel SDM Vol. 3C 35-1 / December 2016: 57          | Xeon Phi 3200, 5200, 7200  [Knights
      # Landing] 85          | Future Xeon Phi 8E 9E       | 7th gen. Core              [Kaby Lake]
      # 55          | Future Xeon                [Skylake w/ AVX512] 4E 5E       | 6th gen. Core /
      # E3 v5      [Skylake w/o AVX512] 56          | Xeon D-1500                [Broadwell] 4F |
      # Xeon E5 v4, E7 v4, i7-69xx [Broadwell] 47          | 5th gen. Core / Xeon E3 v4 [Broadwell]
      # 3D          | M-5xxx / 5th gen.          [Broadwell] 3F          | Xeon E5 v3, E7 v3,
      # i7-59xx [Haswell-E] 3C 45 46    | 4th gen. Core, Xeon E3 v3  [Haswell] 3E          | Xeon E5
      # v2, E7 v2, i7-49xx [Ivy Bridge-E] 3A          | 3rd gen. Core, Xeon E3 v2  [Ivy Bridge] 2D |
      # Xeon E5, i7-39xx           [Sandy Bridge] 2F          | Xeon E7 2A | Xeon E3, 2nd gen. Core
      # [Sandy Bridge] 2E          | Xeon 7500, 6500 series 25 2C | Xeon 3600, 5600 series, Core i7,
      # i5 and i3
      #
      # Values from the Intel SDE: 5C | Goldmont 5A | Silvermont 57 | Knights Landing 66 |
      # Cannonlake 55 | Skylake Server 4E | Skylake Client 3C | Broadwell (likely a bug in the SDE)
      # 3C | Haswell
      if(_cpu_model EQUAL 87) # 57
        set(TARGET_ARCHITECTURE "knl") # Knights Landing
      elseif(_cpu_model EQUAL 92)
        set(TARGET_ARCHITECTURE "goldmont")
      elseif(_cpu_model EQUAL 90 OR _cpu_model EQUAL 76)
        set(TARGET_ARCHITECTURE "silvermont")
      elseif(_cpu_model EQUAL 102)
        set(TARGET_ARCHITECTURE "cannonlake")
      elseif(_cpu_model EQUAL 142 OR _cpu_model EQUAL 158) # 8E, 9E
        set(TARGET_ARCHITECTURE "kabylake")
      elseif(_cpu_model EQUAL 85) # 55
        set(TARGET_ARCHITECTURE "skylake-avx512")
      elseif(_cpu_model EQUAL 78 OR _cpu_model EQUAL 94) # 4E, 5E
        set(TARGET_ARCHITECTURE "skylake")
      elseif(
        _cpu_model EQUAL 61
        OR _cpu_model EQUAL 71
        OR _cpu_model EQUAL 79
        OR _cpu_model EQUAL 86) # 3D, 47, 4F, 56
        set(TARGET_ARCHITECTURE "broadwell")
      elseif(
        _cpu_model EQUAL 60
        OR _cpu_model EQUAL 69
        OR _cpu_model EQUAL 70
        OR _cpu_model EQUAL 63)
        set(TARGET_ARCHITECTURE "haswell")
      elseif(_cpu_model EQUAL 58 OR _cpu_model EQUAL 62)
        set(TARGET_ARCHITECTURE "ivybridge")
      elseif(_cpu_model EQUAL 42 OR _cpu_model EQUAL 45)
        set(TARGET_ARCHITECTURE "sandybridge")
      elseif(
        _cpu_model EQUAL 37
        OR _cpu_model EQUAL 44
        OR _cpu_model EQUAL 47)
        set(TARGET_ARCHITECTURE "westmere")
      elseif(
        _cpu_model EQUAL 26
        OR _cpu_model EQUAL 30
        OR _cpu_model EQUAL 31
        OR _cpu_model EQUAL 46)
        set(TARGET_ARCHITECTURE "nehalem")
      elseif(_cpu_model EQUAL 23 OR _cpu_model EQUAL 29)
        set(TARGET_ARCHITECTURE "penryn")
      elseif(_cpu_model EQUAL 15)
        set(TARGET_ARCHITECTURE "merom")
      elseif(_cpu_model EQUAL 28)
        set(TARGET_ARCHITECTURE "atom")
      elseif(_cpu_model EQUAL 14)
        set(TARGET_ARCHITECTURE "core")
      elseif(_cpu_model LESS 14)
        message(
          WARNING
            "Your CPU (family ${_cpu_family}, model ${_cpu_model}) is not known. Auto-detection of optimization flags failed and will use the generic CPU settings with SSE2."
        )
        set(TARGET_ARCHITECTURE "generic")
      else()
        message(
          WARNING
            "Your CPU (family ${_cpu_family}, model ${_cpu_model}) is not known. Auto-detection of optimization flags failed and will use the 65nm Core 2 CPU settings."
        )
        set(TARGET_ARCHITECTURE "merom")
      endif()
    elseif(_cpu_family EQUAL 7) # Itanium (not supported)
      message(
        WARNING
          "Your CPU (Itanium: family ${_cpu_family}, model ${_cpu_model}) is not supported by OptimizeForArchitecture.cmake."
      )
    elseif(_cpu_family EQUAL 15) # NetBurst
      list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2")
      if(_cpu_model GREATER 2) # Not sure whether this must be 3 or even 4 instead
        list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3")
      endif(_cpu_model GREATER 2)
    endif(_cpu_family EQUAL 6)
  elseif(_vendor_id STREQUAL "AuthenticAMD")
    if(_cpu_family EQUAL 23)
      set(TARGET_ARCHITECTURE "zen")
    elseif(_cpu_family EQUAL 22) # 16h
      set(TARGET_ARCHITECTURE "AMD 16h")
    elseif(_cpu_family EQUAL 21) # 15h
      if(_cpu_model LESS 2)
        set(TARGET_ARCHITECTURE "bulldozer")
      else()
        set(TARGET_ARCHITECTURE "piledriver")
      endif()
    elseif(_cpu_family EQUAL 20) # 14h
      set(TARGET_ARCHITECTURE "AMD 14h")
    elseif(_cpu_family EQUAL 18) # 12h

    elseif(_cpu_family EQUAL 16) # 10h
      set(TARGET_ARCHITECTURE "barcelona")
    elseif(_cpu_family EQUAL 15)
      set(TARGET_ARCHITECTURE "k8")
      if(_cpu_model GREATER 64) # I don't know the right number to put here. This is just a guess
                                # from the hardware I have access to
        set(TARGET_ARCHITECTURE "k8-sse3")
      endif(_cpu_model GREATER 64)
    endif()
  endif(_vendor_id STREQUAL "GenuineIntel")
endmacro()

macro(OFA_AutodetectArm)
  message(
    WARNING
      "Architecture auto-detection for CMAKE_SYSTEM_PROCESSOR '${CMAKE_SYSTEM_PROCESSOR}' is not supported by OptimizeForArchitecture.cmake"
  )
endmacro()

macro(OFA_AutodetectHostArchitecture)
  set(TARGET_ARCHITECTURE "generic")
  set(Vc_ARCHITECTURE_FLAGS)
  if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(x86|AMD64)")
    ofa_autodetectx86()
  elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(arm|aarch32|aarch64)")
    ofa_autodetectarm()
  else()
    message(
      FATAL_ERROR
        "OptimizeForArchitecture.cmake does not implement support for CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}"
    )
  endif()
endmacro()

macro(OFA_HandleX86Options)
  set(_march_flag_list)
  set(AVAIL_VECTOR_UNITS_LIST)
  macro(_nehalem)
    list(APPEND _march_flag_list "nehalem")
    list(APPEND _march_flag_list "corei7")
    list(APPEND _march_flag_list "core2")
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4.1"
             "sse4.2")
  endmacro()
  macro(_westmere)
    list(APPEND _march_flag_list "westmere")
    _nehalem()
  endmacro()
  macro(_sandybridge)
    list(APPEND _march_flag_list "sandybridge")
    list(APPEND _march_flag_list "corei7-avx")
    _westmere()
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4.1"
             "sse4.2"
             "avx")
  endmacro()
  macro(_ivybridge)
    list(APPEND _march_flag_list "ivybridge")
    list(APPEND _march_flag_list "core-avx-i")
    _sandybridge()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "rdrnd" "f16c")
  endmacro()
  macro(_haswell)
    list(APPEND _march_flag_list "haswell")
    list(APPEND _march_flag_list "core-avx2")
    _ivybridge()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "avx2" "fma" "bmi" "bmi2")
  endmacro()
  macro(_broadwell)
    list(APPEND _march_flag_list "broadwell")
    _haswell()
  endmacro()
  macro(_skylake)
    list(APPEND _march_flag_list "skylake")
    _broadwell()
  endmacro()
  macro(_skylake_avx512)
    list(APPEND _march_flag_list "skylake-avx512")
    _skylake()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "avx512f" "avx512cd" "avx512dq" "avx512bw" "avx512vl")
  endmacro()
  macro(_cannonlake)
    list(APPEND _march_flag_list "cannonlake")
    _skylake_avx512()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "avx512ifma" "avx512vbmi")
  endmacro()
  macro(_knightslanding)
    list(APPEND _march_flag_list "knl")
    _broadwell()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "avx512f" "avx512pf" "avx512er" "avx512cd")
  endmacro()
  macro(_silvermont)
    list(APPEND _march_flag_list "silvermont")
    _westmere()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "rdrnd")
  endmacro()
  macro(_goldmont)
    list(APPEND _march_flag_list "goldmont")
    _silvermont()
  endmacro()

  if(TARGET_ARCHITECTURE STREQUAL "core")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3")
  elseif(TARGET_ARCHITECTURE STREQUAL "merom")
    list(APPEND _march_flag_list "merom")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "ssse3")
  elseif(TARGET_ARCHITECTURE STREQUAL "penryn")
    list(APPEND _march_flag_list "penryn")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "ssse3")
    message(
      STATUS "Sadly the Penryn architecture exists in variants with SSE4.1 and without SSE4.1.")
    if(_cpu_flags MATCHES "sse4_1")
      message(STATUS "SSE4.1: enabled (auto-detected from this computer's CPU flags)")
      list(APPEND AVAIL_VECTOR_UNITS_LIST "sse4.1")
    else()
      message(STATUS "SSE4.1: disabled (auto-detected from this computer's CPU flags)")
    endif()
  elseif(TARGET_ARCHITECTURE STREQUAL "knl")
    _knightslanding()
  elseif(TARGET_ARCHITECTURE STREQUAL "cannonlake")
    _cannonlake()
  elseif(TARGET_ARCHITECTURE STREQUAL "kabylake")
    _skylake()
  elseif(TARGET_ARCHITECTURE STREQUAL "skylake-xeon" OR TARGET_ARCHITECTURE STREQUAL
                                                        "skylake-avx512")
    _skylake_avx512()
  elseif(TARGET_ARCHITECTURE STREQUAL "skylake")
    _skylake()
  elseif(TARGET_ARCHITECTURE STREQUAL "broadwell")
    _broadwell()
  elseif(TARGET_ARCHITECTURE STREQUAL "haswell")
    _haswell()
  elseif(TARGET_ARCHITECTURE STREQUAL "ivybridge")
    _ivybridge()
  elseif(TARGET_ARCHITECTURE STREQUAL "sandy-bridge")
    _sandybridge()
  elseif(TARGET_ARCHITECTURE STREQUAL "westmere")
    _westmere()
  elseif(TARGET_ARCHITECTURE STREQUAL "nehalem")
    _nehalem()
  elseif(TARGET_ARCHITECTURE STREQUAL "goldmont")
    _goldmont()
  elseif(TARGET_ARCHITECTURE STREQUAL "silvermont")
    _silvermont()
  elseif(TARGET_ARCHITECTURE STREQUAL "atom")
    list(APPEND _march_flag_list "atom")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "ssse3")
  elseif(TARGET_ARCHITECTURE STREQUAL "k8")
    list(APPEND _march_flag_list "k8")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2")
  elseif(TARGET_ARCHITECTURE STREQUAL "k8-sse3")
    list(APPEND _march_flag_list "k8-sse3")
    list(APPEND _march_flag_list "k8")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3")
  elseif(TARGET_ARCHITECTURE STREQUAL "AMD 16h")
    list(APPEND _march_flag_list "btver2")
    list(APPEND _march_flag_list "btver1")
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4a"
             "sse4.1"
             "sse4.2"
             "avx"
             "f16c")
  elseif(TARGET_ARCHITECTURE STREQUAL "AMD 14h")
    list(APPEND _march_flag_list "btver1")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "ssse3" "sse4a")
  elseif(TARGET_ARCHITECTURE STREQUAL "zen")
    list(APPEND _march_flag_list "znver1")
    _skylake()
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse4a")
  elseif(TARGET_ARCHITECTURE STREQUAL "piledriver")
    list(APPEND _march_flag_list "bdver2")
    list(APPEND _march_flag_list "bdver1")
    list(APPEND _march_flag_list "bulldozer")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4a"
             "sse4.1"
             "sse4.2"
             "avx"
             "xop"
             "fma4"
             "fma"
             "f16c")
  elseif(TARGET_ARCHITECTURE STREQUAL "interlagos")
    list(APPEND _march_flag_list "bdver1")
    list(APPEND _march_flag_list "bulldozer")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4a"
             "sse4.1"
             "sse4.2"
             "avx"
             "xop"
             "fma4")
  elseif(TARGET_ARCHITECTURE STREQUAL "bulldozer")
    list(APPEND _march_flag_list "bdver1")
    list(APPEND _march_flag_list "bulldozer")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(
      APPEND AVAIL_VECTOR_UNITS_LIST
             "sse"
             "sse2"
             "sse3"
             "ssse3"
             "sse4a"
             "sse4.1"
             "sse4.2"
             "avx"
             "xop"
             "fma4")
  elseif(TARGET_ARCHITECTURE STREQUAL "barcelona")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "sse4a")
  elseif(TARGET_ARCHITECTURE STREQUAL "istanbul")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "sse4a")
  elseif(TARGET_ARCHITECTURE STREQUAL "magny-cours")
    list(APPEND _march_flag_list "barcelona")
    list(APPEND _march_flag_list "core2")
    list(APPEND AVAIL_VECTOR_UNITS_LIST "sse" "sse2" "sse3" "sse4a")
  elseif(TARGET_ARCHITECTURE STREQUAL "generic")
    list(APPEND _march_flag_list "generic")
  elseif(TARGET_ARCHITECTURE STREQUAL "none")
    # add this clause to remove it from the else clause
  else(TARGET_ARCHITECTURE STREQUAL "core")
    message(
      FATAL_ERROR
        "Unknown target architecture: \"${TARGET_ARCHITECTURE}\". Please set TARGET_ARCHITECTURE to a supported value."
    )
  endif(TARGET_ARCHITECTURE STREQUAL "core")

  if(NOT TARGET_ARCHITECTURE STREQUAL "none")
    set(_disable_vector_unit_list)
    set(_enable_vector_unit_list)
    if(DEFINED Vc_AVX_INTRINSICS_BROKEN AND Vc_AVX_INTRINSICS_BROKEN)
      message(STATUS "AVX disabled because of old/broken toolchain")
      set(_avx_broken true)
      set(_avx2_broken true)
      set(_fma4_broken true)
      set(_xop_broken true)
    else()
      set(_avx_broken false)
      if(DEFINED Vc_FMA4_INTRINSICS_BROKEN AND Vc_FMA4_INTRINSICS_BROKEN)
        message(STATUS "FMA4 disabled because of old/broken toolchain")
        set(_fma4_broken true)
      else()
        set(_fma4_broken false)
      endif()
      if(DEFINED Vc_XOP_INTRINSICS_BROKEN AND Vc_XOP_INTRINSICS_BROKEN)
        message(STATUS "XOP disabled because of old/broken toolchain")
        set(_xop_broken true)
      else()
        set(_xop_broken false)
      endif()
      if(DEFINED Vc_AVX2_INTRINSICS_BROKEN AND Vc_AVX2_INTRINSICS_BROKEN)
        message(STATUS "AVX2 disabled because of old/broken toolchain")
        set(_avx2_broken true)
      else()
        set(_avx2_broken false)
      endif()
    endif()

    macro(_enable_or_disable _name _flag _documentation _broken)
      if(_broken)
        set(_found false)
      else()
        _my_find(AVAIL_VECTOR_UNITS_LIST "${_flag}" _found)
      endif()
      set(USE_${_name}
          ${_found}
          CACHE BOOL "${documentation}" ${_force})
      mark_as_advanced(USE_${_name})
      if(USE_${_name})
        list(APPEND _enable_vector_unit_list "${_flag}")
      else()
        list(APPEND _disable_vector_unit_list "${_flag}")
      endif()
    endmacro()
    _enable_or_disable(
      SSE2 "sse2"
      "Use SSE2. If SSE2 instructions are not enabled the SSE implementation will be disabled."
      false)
    _enable_or_disable(
      SSE3 "sse3" "Use SSE3. If SSE3 instructions are not enabled they will be emulated." false)
    _enable_or_disable(
      SSSE3 "ssse3" "Use SSSE3. If SSSE3 instructions are not enabled they will be emulated." false)
    _enable_or_disable(
      SSE4_1 "sse4.1" "Use SSE4.1. If SSE4.1 instructions are not enabled they will be emulated."
      false)
    _enable_or_disable(
      SSE4_2 "sse4.2" "Use SSE4.2. If SSE4.2 instructions are not enabled they will be emulated."
      false)
    _enable_or_disable(
      SSE4a "sse4a" "Use SSE4a. If SSE4a instructions are not enabled they will be emulated." false)
    _enable_or_disable(
      AVX "avx" "Use AVX. This will all floating-point vector sizes relative to SSE." _avx_broken)
    _enable_or_disable(FMA "fma" "Use FMA." _avx_broken)
    _enable_or_disable(BMI2 "bmi2" "Use BMI2." _avx_broken)
    _enable_or_disable(
      AVX2 "avx2" "Use AVX2. This will double all of the vector sizes relative to SSE."
      _avx2_broken)
    _enable_or_disable(XOP "xop" "Use XOP." _xop_broken)
    _enable_or_disable(FMA4 "fma4" "Use FMA4." _fma4_broken)
    _enable_or_disable(
      AVX512F "avx512f"
      "Use AVX512F. This will double all floating-point vector sizes relative to AVX2." false)
    _enable_or_disable(
      AVX512VL
      "avx512vl"
      "Use AVX512VL. This enables 128- and 256-bit vector length instructions with EVEX coding (improved write-masking & more vector registers)."
      _avx2_broken)
    _enable_or_disable(
      AVX512PF "avx512pf"
      "Use AVX512PF. This enables prefetch instructions for gathers and scatters." false)
    _enable_or_disable(AVX512ER "avx512er"
                       "Use AVX512ER. This enables exponential and reciprocal instructions." false)
    _enable_or_disable(AVX512CD "avx512cd" "Use AVX512CD." false)
    _enable_or_disable(AVX512DQ "avx512dq" "Use AVX512DQ." false)
    _enable_or_disable(AVX512BW "avx512bw" "Use AVX512BW." false)
    _enable_or_disable(AVX512IFMA "avx512ifma" "Use AVX512IFMA." false)
    _enable_or_disable(AVX512VBMI "avx512vbmi" "Use AVX512VBMI." false)

  endif()
endmacro()

macro(OFA_HandleArmOptions)
  option(USE_NEON "Enable use of NEON instructions" ON)
  if(USE_NEON)
    addcompilerflag(-mfloat-abi=softfp CXX_FLAGS Vc_ARCHITECTURE_FLAGS)
    addcompilerflag(-mfpu=neon CXX_FLAGS Vc_ARCHITECTURE_FLAGS)
  endif()
endmacro()

macro(GetVectorMemoryAlignmentBytes)
  set(MEMORY_ALIGN_BYTES "")

  if("avx512" IN_LIST AVAIL_VECTOR_UNITS_LIST)
    set(MEMORY_ALIGN_BYTES 64)
  elseif("avx" IN_LIST AVAIL_VECTOR_UNITS_LIST)
    set(MEMORY_ALIGN_BYTES 32)
  elseif("sse" IN_LIST AVAIL_VECTOR_UNITS_LIST)
    set(MEMORY_ALIGN_BYTES 16)
  endif()

endmacro()

macro(OptimizeForArchitecture)
  if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(x86|AMD64)")
    set(TARGET_ARCHITECTURE
        "auto"
        CACHE
          STRING
          "CPU architecture to optimize for. \
   Using an incorrect setting here can result in crashes of the resulting binary because of invalid instructions used. \
   Setting the value to \"auto\" will try to optimize for the architecture where cmake is called. \
   Other supported values are: \"none\", \"generic\", \"core\", \"merom\" (65nm Core2), \
   \"penryn\" (45nm Core2), \"nehalem\", \"westmere\", \"sandy-bridge\", \"ivy-bridge\", \
   \"haswell\", \"broadwell\", \"skylake\", \"skylake-xeon\", \"kabylake\", \"cannonlake\", \"silvermont\", \
   \"goldmont\", \"knl\" (Knights Landing), \"atom\", \"k8\", \"k8-sse3\", \"barcelona\", \
   \"istanbul\", \"magny-cours\", \"bulldozer\", \"interlagos\", \"piledriver\", \
   \"AMD 14h\", \"AMD 16h\", \"zen\".")
  elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(arm|aarch32|aarch64)")
    set(TARGET_ARCHITECTURE
        "auto"
        CACHE
          STRING
          "CPU architecture to optimize for. \
      Using an incorrect setting here can result in crashes of the resulting binary because of invalid instructions used. \
      Setting the value to \"auto\" will try to optimize for the architecture where cmake is called. \
      Other supported values are: \"none\", \"generic\", TODO...")
  else()
    message(
      WARNING
        "The CMAKE_SYSTEM_PROCESSOR '${CMAKE_SYSTEM_PROCESSOR}' is not supported by OptimizeForArchitecture.cmake"
    )
  endif()
  set(_force)
  # if((NOT _last_target_arch STREQUAL "${TARGET_ARCHITECTURE}")) message(STATUS "target changed
  # from \"${_last_target_arch}\" to \"${TARGET_ARCHITECTURE}\"") set(_force FORCE) endif()
  set(_last_target_arch
      "${TARGET_ARCHITECTURE}"
      CACHE STRING "" FORCE)
  mark_as_advanced(_last_target_arch)
  string(TOLOWER "${TARGET_ARCHITECTURE}" TARGET_ARCHITECTURE)

  if(TARGET_ARCHITECTURE STREQUAL "auto")
    ofa_autodetecthostarchitecture()
    message(STATUS "Detected Host CPU: ${TARGET_ARCHITECTURE}")
  endif()

  if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(x86|AMD64)")
    ofa_handlex86options()
  elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(arm|aarch32|aarch64)")
    ofa_handlearmoptions()
  endif()

  getvectormemoryalignmentbytes()
  message(STATUS "Available Vector Instructions: ${AVAIL_VECTOR_UNITS_LIST}")
  message(STATUS "Vector Memory Alignment: ${MEMORY_ALIGN_BYTES} bytes")
endmacro(OptimizeForArchitecture)
