• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

saitoha / libsixel / 20872729862

10 Jan 2026 04:20AM UTC coverage: 52.212% (-0.01%) from 52.222%
20872729862

push

github

saitoha
fix: fix c99 build in colorspace avx2 paths

20568 of 39393 relevant lines covered (52.21%)

861052.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.56
/src/cpu.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
 * this software and associated documentation files (the "Software"), to deal in
8
 * the Software without restriction, including without limitation the rights to
9
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
 * the Software, and to permit persons to whom the Software is furnished to do so,
11
 * subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 */
23

24
#if defined(HAVE_CONFIG_H)
25
#include "config.h"
26
#endif
27

28
/* STDC_HEADERS */
29
#include <stdlib.h>
30

31
#if HAVE_INTRIN_H && (defined(_WIN32) || defined(_MSC_VER))
32
/*
33
 * Avoid cpuid.h macro clashes on Windows toolchains. Restrict intrin.h to
34
 * Windows targets so platforms such as OpenBSD, whose Clang wrapper tries
35
 * include_next to a non-existent system header, skip it gracefully.
36
 */
37
# include <intrin.h>
38
#endif
39
#if HAVE_CPUID_H && !defined(_WIN32) && \
40
    (defined(__x86_64__) || defined(__i386))
41
# include <cpuid.h>
42
#endif
43
#if defined(HAVE_IMMINTRIN_H) && defined(ENABLE_XSAVE_PROBE) && \
44
    (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
45
     defined(_M_IX86))
46
# include <immintrin.h>
47
#endif
48

49
#include "cpu.h"
50
#include "compat_stub.h"
51

52
static int simd_cached = -1;
53

54
static enum sixel_simd_level
55
sixel_cpu_env_cap(void)
157✔
56
{
57
    char const *env;
157✔
58

59
    env = sixel_compat_getenv("SIXEL_SIMD_LEVEL");
157✔
60
    if (env == NULL || env[0] == '\0') {
157✔
61
        return SIXEL_SIMD_LEVEL_NEON;
62
    }
63
    if (sixel_compat_strcasecmp(env, "auto") == 0) {
×
64
        return SIXEL_SIMD_LEVEL_NEON;
65
    }
66
    if (sixel_compat_strcasecmp(env, "none") == 0 ||
×
67
        sixel_compat_strcasecmp(env, "scalar") == 0) {
×
68
        return SIXEL_SIMD_LEVEL_SCALAR;
×
69
    }
70
    if (sixel_compat_strcasecmp(env, "sse2") == 0) {
×
71
        return SIXEL_SIMD_LEVEL_SSE2;
72
    }
73
    if (sixel_compat_strcasecmp(env, "avx") == 0) {
×
74
        return SIXEL_SIMD_LEVEL_AVX;
75
    }
76
    if (sixel_compat_strcasecmp(env, "avx2") == 0) {
×
77
        return SIXEL_SIMD_LEVEL_AVX2;
78
    }
79
    if (sixel_compat_strcasecmp(env, "avx512") == 0) {
×
80
        return SIXEL_SIMD_LEVEL_AVX512;
81
    }
82
    if (sixel_compat_strcasecmp(env, "neon") == 0) {
×
83
        return SIXEL_SIMD_LEVEL_NEON;
84
    }
85
    return SIXEL_SIMD_LEVEL_NEON;
86
}
87

88
static enum sixel_simd_level
89
sixel_cpu_detect_native(void)
157✔
90
{
91
    enum sixel_simd_level level;
157✔
92

93
    level = SIXEL_SIMD_LEVEL_SCALAR;
157✔
94
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
95
    defined(_M_IX86)
96
# if HAVE_BUILTIN_CPU_INIT && !defined(_MSC_VER) && !defined(__PCC__)
97
    __builtin_cpu_init();
157✔
98
# endif
99
# if HAVE_BUILTIN_CPU_SUPPORTS && !defined(_MSC_VER) && !defined(__PCC__)
100
    /*
101
     * clang-cl accepts __builtin_cpu_supports() but MSVC-style
102
     * toolchains do not ship the runtime helpers (e.g.
103
     * __cpu_indicator_init). Skip the builtin path there and use the
104
     * intrinsics-based detection instead.
105
     */
106
    if (__builtin_cpu_supports("avx512f")) {
157✔
107
        level = SIXEL_SIMD_LEVEL_AVX512;
108
    } else if (__builtin_cpu_supports("avx2")) {
×
109
        level = SIXEL_SIMD_LEVEL_AVX2;
110
    } else if (__builtin_cpu_supports("avx")) {
×
111
        level = SIXEL_SIMD_LEVEL_AVX;
112
    } else if (__builtin_cpu_supports("sse2")) {
×
113
        level = SIXEL_SIMD_LEVEL_SSE2;
×
114
    }
115
# elif defined(_MSC_VER) && HAVE_INTRIN_H
116
    int cpu_info[4];
117
    int osxsave;
118
    int avx_capable;
119

120
    __cpuid(cpu_info, 1);
121
    osxsave = (cpu_info[2] & (1 << 27));
122
    avx_capable = (cpu_info[2] & (1 << 28));
123
    if (osxsave && avx_capable) {
124
#  if defined(ENABLE_XSAVE_PROBE)
125
        unsigned long long xcr0;
126

127
        xcr0 = _xgetbv(0);
128
        if ((xcr0 & 0x6) == 0x6) {
129
            int extended[4];
130

131
            __cpuidex(extended, 7, 0);
132
            if ((extended[1] & (1 << 16)) != 0) {
133
                level = SIXEL_SIMD_LEVEL_AVX512;
134
            } else if ((extended[1] & (1 << 5)) != 0) {
135
                level = SIXEL_SIMD_LEVEL_AVX2;
136
            } else {
137
                level = SIXEL_SIMD_LEVEL_AVX;
138
            }
139
        }
140
#  endif
141
    }
142
    if (level == SIXEL_SIMD_LEVEL_SCALAR && (cpu_info[3] & (1 << 26))) {
143
        level = SIXEL_SIMD_LEVEL_SSE2;
144
    }
145
# elif HAVE_CPUID_H
146
    unsigned int eax;
147
    unsigned int ebx;
148
    unsigned int ecx;
149
    unsigned int edx;
150
    int osxsave;
151
    int avx_capable;
152

153
    if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) != 0) {
154
        /*
155
         * DragonFlyBSD kernels expose AVX/AVX2/AVX512 bits even when the
156
         * OS has not enabled XSAVE. Guard against executing AVX-class
157
         * instructions by checking OSXSAVE and XCR0 before raising the
158
         * SIMD level. Fall back to SSE2 when the OS cannot preserve the
159
         * extended state. The XGETBV probe is optional so toolchains that
160
         * lack the intrinsic still succeed.
161
         */
162
        osxsave = (ecx & (1U << 27)) != 0;
163
        avx_capable = (ecx & (1U << 28)) != 0;
164
#  if defined(HAVE_IMMINTRIN_H)
165
        /*
166
         * The XGETBV probe is optional. It is only enabled when the
167
         * toolchain can emit the intrinsic without extra target flags (see
168
         * ENABLE_XSAVE_PROBE/HAVE_XGETBV_INTRIN). Toolchains that cannot
169
         * support it will skip this block and fall back to SSE2.
170
         */
171
#  if defined(ENABLE_XSAVE_PROBE)
172
        if (osxsave != 0 && avx_capable != 0) {
173
            unsigned long long xcr0;
174

175
            xcr0 = _xgetbv(0);
176
            if ((xcr0 & 0x6) == 0x6) {
177
                if (__get_cpuid_max(0, NULL) >= 7 &&
178
                    __get_cpuid_count(7, 0, &eax, &ebx, &ecx,
179
                                      &edx) != 0) {
180
                    if ((ebx & (1U << 16)) != 0) {
181
                        level = SIXEL_SIMD_LEVEL_AVX512;
182
                    } else if ((ebx & (1U << 5)) != 0) {
183
                        level = SIXEL_SIMD_LEVEL_AVX2;
184
                    } else {
185
                        level = SIXEL_SIMD_LEVEL_AVX;
186
                    }
187
                } else {
188
                    level = SIXEL_SIMD_LEVEL_AVX;
189
                }
190
            }
191
        }
192
#  endif
193
#  endif
194
        if (level == SIXEL_SIMD_LEVEL_SCALAR &&
195
            (edx & (1U << 26)) != 0) {
196
            level = SIXEL_SIMD_LEVEL_SSE2;
197
        }
198
    }
199
# endif
200
#elif defined(__aarch64__) || defined(__ARM_NEON) || defined(__ARM_NEON__)
201
    level = SIXEL_SIMD_LEVEL_NEON;
202
#endif
203
    return level;
157✔
204
}
205

206
static enum sixel_simd_level
207
sixel_cpu_min(enum sixel_simd_level lhs, enum sixel_simd_level rhs)
157✔
208
{
209
    if (lhs < rhs) {
157✔
210
        return lhs;
211
    }
212
    return rhs;
213
}
214

215
int
216
sixel_cpu_simd_level(void)
197✔
217
{
218
    enum sixel_simd_level env_cap;
197✔
219
    enum sixel_simd_level native;
197✔
220

221
    if (simd_cached >= 0) {
197✔
222
        return simd_cached;
223
    }
224

225
    env_cap = sixel_cpu_env_cap();
157✔
226
    native = sixel_cpu_detect_native();
157✔
227
    simd_cached = (int)sixel_cpu_min(env_cap, native);
157✔
228
    return simd_cached;
157✔
229
}
230

231
void
232
sixel_cpu_reset_simd_cache(void)
×
233
{
234
    simd_cached = -1;
×
235
}
×
236

237
/* emacs Local Variables:      */
238
/* emacs mode: c               */
239
/* emacs tab-width: 4          */
240
/* emacs indent-tabs-mode: nil */
241
/* emacs c-basic-offset: 4     */
242
/* emacs End:                  */
243
/* vim: set expandtab ts=4 sts=4 sw=4 : */
244
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc