summaryrefslogtreecommitdiffstats
path: root/cmake/tuklib_integer.cmake
blob: 7e1ed3c5f00fadf85b1350ab0c94caf1fbfd9be2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# SPDX-License-Identifier: 0BSD

#############################################################################
#
# tuklib_integer.cmake - see tuklib_integer.m4 for description and comments
#
# Author: Lasse Collin
#
#############################################################################

include("${CMAKE_CURRENT_LIST_DIR}/tuklib_common.cmake")
include(TestBigEndian)
include(CheckCSourceCompiles)
include(CheckIncludeFile)
include(CheckSymbolExists)

function(tuklib_integer TARGET_OR_ALL)
    # Check for endianness. Unlike the Autoconf's AC_C_BIGENDIAN, this doesn't
    # support Apple universal binaries. The CMake module will leave the
    # variable unset so we can catch that situation here instead of continuing
    # as if we were little endian.
    test_big_endian(WORDS_BIGENDIAN)
    if(NOT DEFINED WORDS_BIGENDIAN)
        message(FATAL_ERROR "Cannot determine endianness")
    endif()
    tuklib_add_definition_if("${TARGET_OR_ALL}" WORDS_BIGENDIAN)

    # Look for a byteswapping method.
    check_c_source_compiles("
            int main(void)
            {
                __builtin_bswap16(1);
                __builtin_bswap32(1);
                __builtin_bswap64(1);
                return 0;
            }
        "
        HAVE___BUILTIN_BSWAPXX)
    if(HAVE___BUILTIN_BSWAPXX)
        tuklib_add_definitions("${TARGET_OR_ALL}" HAVE___BUILTIN_BSWAPXX)
    else()
        check_include_file(byteswap.h HAVE_BYTESWAP_H)
        if(HAVE_BYTESWAP_H)
            tuklib_add_definitions("${TARGET_OR_ALL}" HAVE_BYTESWAP_H)
            check_symbol_exists(bswap_16 byteswap.h HAVE_BSWAP_16)
            tuklib_add_definition_if("${TARGET_OR_ALL}" HAVE_BSWAP_16)
            check_symbol_exists(bswap_32 byteswap.h HAVE_BSWAP_32)
            tuklib_add_definition_if("${TARGET_OR_ALL}" HAVE_BSWAP_32)
            check_symbol_exists(bswap_64 byteswap.h HAVE_BSWAP_64)
            tuklib_add_definition_if("${TARGET_OR_ALL}" HAVE_BSWAP_64)
        else()
            check_include_file(sys/endian.h HAVE_SYS_ENDIAN_H)
            if(HAVE_SYS_ENDIAN_H)
                tuklib_add_definitions("${TARGET_OR_ALL}" HAVE_SYS_ENDIAN_H)
            else()
                check_include_file(sys/byteorder.h HAVE_SYS_BYTEORDER_H)
                tuklib_add_definition_if("${TARGET_OR_ALL}"
                                         HAVE_SYS_BYTEORDER_H)
            endif()
        endif()
    endif()

    # Guess that unaligned access is fast on these archs:
    #   - 32/64-bit x86 / x86-64
    #   - 32/64-bit big endian PowerPC
    #   - 64-bit little endian PowerPC
    #   - Some 32-bit ARM
    #   - Some 64-bit ARM64 (AArch64)
    #   - Some 32/64-bit RISC-V
    #
    # CMake doesn't provide a standardized/normalized list of processor arch
    # names. For example, x86-64 may be "x86_64" (Linux), "AMD64" (Windows),
    # or even "EM64T" (64-bit WinXP).
    set(FAST_UNALIGNED_GUESS OFF)
    string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" PROCESSOR)

    # There is no ^ in the first regex branch to allow "i" at the beginning
    # so it can match "i386" to "i786", and "x86_64".
    if(PROCESSOR MATCHES "[x34567]86|^x64|^amd64|^em64t")
        set(FAST_UNALIGNED_GUESS ON)

    elseif(PROCESSOR MATCHES "^powerpc|^ppc")
        if(WORDS_BIGENDIAN OR PROCESSOR MATCHES "64")
            set(FAST_UNALIGNED_GUESS ON)
        endif()

    elseif(PROCESSOR MATCHES "^arm|^aarch64|^riscv")
        # On 32-bit and 64-bit ARM, GCC and Clang
        # #define __ARM_FEATURE_UNALIGNED if
        # unaligned access is supported.
        #
        # Exception: GCC at least up to 13.2.0
        # defines it even when using -mstrict-align
        # so in that case this autodetection goes wrong.
        # Most of the time -mstrict-align isn't used so it
        # shouldn't be a common problem in practice. See:
        # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111555
        #
        # RISC-V C API Specification says that if
        # __riscv_misaligned_fast is defined then
        # unaligned access is known to be fast.
        #
        # MSVC is handled as a special case: We assume that
        # 32/64-bit ARM supports fast unaligned access.
        # If MSVC gets RISC-V support then this will assume
        # fast unaligned access on RISC-V too.
        check_c_source_compiles("
                #if !defined(__ARM_FEATURE_UNALIGNED) \
                        && !defined(__riscv_misaligned_fast) \
                        && !defined(_MSC_VER)
                compile error
                #endif
                int main(void) { return 0; }
            "
            TUKLIB_FAST_UNALIGNED_DEFINED_BY_PREPROCESSOR)
        if(TUKLIB_FAST_UNALIGNED_DEFINED_BY_PREPROCESSOR)
            set(FAST_UNALIGNED_GUESS ON)
        endif()
    endif()

    option(TUKLIB_FAST_UNALIGNED_ACCESS
           "Enable if the system supports *fast* unaligned memory access \
with 16-bit, 32-bit, and 64-bit integers."
           "${FAST_UNALIGNED_GUESS}")
    tuklib_add_definition_if("${TARGET_OR_ALL}" TUKLIB_FAST_UNALIGNED_ACCESS)

    # Unsafe type punning:
    option(TUKLIB_USE_UNSAFE_TYPE_PUNNING
           "This introduces strict aliasing violations and \
may result in broken code. However, this might improve performance \
in some cases, especially with old compilers \
(e.g. GCC 3 and early 4.x on x86, GCC < 6 on ARMv6 and ARMv7)."
           OFF)
    tuklib_add_definition_if("${TARGET_OR_ALL}" TUKLIB_USE_UNSAFE_TYPE_PUNNING)

    # Check for GCC/Clang __builtin_assume_aligned().
    check_c_source_compiles(
        "int main(void) { __builtin_assume_aligned(\"\", 1); return 0; }"
        HAVE___BUILTIN_ASSUME_ALIGNED)
    tuklib_add_definition_if("${TARGET_OR_ALL}" HAVE___BUILTIN_ASSUME_ALIGNED)
endfunction()