blob: bafd3d159c5aacf3823b7ddf3bc446d18b4348b4 [file] [log] [blame]
Richard Henderson88ca8e82016-08-29 11:46:12 -07001/*
2 * Simple C functions to supplement the C library
3 *
4 * Copyright (c) 2006 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all 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,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "qemu/osdep.h"
25#include "qemu-common.h"
26#include "qemu/cutils.h"
Richard Henderson5e33a872016-08-29 11:46:15 -070027#include "qemu/bswap.h"
Richard Henderson88ca8e82016-08-29 11:46:12 -070028
29
30/* vector definitions */
Richard Henderson5e33a872016-08-29 11:46:15 -070031
32extern void link_error(void);
33
34#define ACCEL_BUFFER_ZERO(NAME, SIZE, VECTYPE, NONZERO) \
35static bool NAME(const void *buf, size_t len) \
36{ \
37 const void *end = buf + len; \
38 do { \
39 const VECTYPE *p = buf; \
40 VECTYPE t; \
41 if (SIZE == sizeof(VECTYPE) * 4) { \
42 t = (p[0] | p[1]) | (p[2] | p[3]); \
43 } else if (SIZE == sizeof(VECTYPE) * 8) { \
44 t = p[0] | p[1]; \
45 t |= p[2] | p[3]; \
46 t |= p[4] | p[5]; \
47 t |= p[6] | p[7]; \
48 } else { \
49 link_error(); \
50 } \
51 if (unlikely(NONZERO(t))) { \
52 return false; \
53 } \
54 buf += SIZE; \
55 } while (buf < end); \
56 return true; \
57}
58
59static bool
60buffer_zero_int(const void *buf, size_t len)
61{
62 if (unlikely(len < 8)) {
63 /* For a very small buffer, simply accumulate all the bytes. */
64 const unsigned char *p = buf;
65 const unsigned char *e = buf + len;
66 unsigned char t = 0;
67
68 do {
69 t |= *p++;
70 } while (p < e);
71
72 return t == 0;
73 } else {
74 /* Otherwise, use the unaligned memory access functions to
75 handle the beginning and end of the buffer, with a couple
76 of loops handling the middle aligned section. */
77 uint64_t t = ldq_he_p(buf);
78 const uint64_t *p = (uint64_t *)(((uintptr_t)buf + 8) & -8);
79 const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8);
80
81 for (; p + 8 <= e; p += 8) {
82 __builtin_prefetch(p + 8);
83 if (t) {
84 return false;
85 }
86 t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
87 }
88 while (p < e) {
89 t |= *p++;
90 }
91 t |= ldq_he_p(buf + len - 8);
92
93 return t == 0;
94 }
95}
96
Richard Henderson43ff5e02016-08-29 11:46:20 -070097#if defined(CONFIG_AVX2_OPT) || (defined(CONFIG_CPUID_H) && defined(__SSE2__))
Richard Henderson5e33a872016-08-29 11:46:15 -070098#include <cpuid.h>
99
100/* Do not use push_options pragmas unnecessarily, because clang
101 * does not support them.
102 */
103#ifndef __SSE2__
104#pragma GCC push_options
105#pragma GCC target("sse2")
106#endif
Richard Henderson88ca8e82016-08-29 11:46:12 -0700107#include <emmintrin.h>
Richard Henderson5e33a872016-08-29 11:46:15 -0700108#define SSE2_NONZERO(X) \
109 (_mm_movemask_epi8(_mm_cmpeq_epi8((X), _mm_setzero_si128())) != 0xFFFF)
110ACCEL_BUFFER_ZERO(buffer_zero_sse2, 64, __m128i, SSE2_NONZERO)
111#ifndef __SSE2__
112#pragma GCC pop_options
Richard Henderson88ca8e82016-08-29 11:46:12 -0700113#endif
114
Richard Henderson5e33a872016-08-29 11:46:15 -0700115#ifdef CONFIG_AVX2_OPT
Richard Henderson88ca8e82016-08-29 11:46:12 -0700116#pragma GCC push_options
Paolo Bonzini86444f02016-09-13 17:04:52 +0200117#pragma GCC target("sse4")
118#include <smmintrin.h>
119#define SSE4_NONZERO(X) !_mm_testz_si128((X), (X))
120ACCEL_BUFFER_ZERO(buffer_zero_sse4, 64, __m128i, SSE4_NONZERO)
121#pragma GCC pop_options
122
123#pragma GCC push_options
Richard Henderson88ca8e82016-08-29 11:46:12 -0700124#pragma GCC target("avx2")
Richard Henderson88ca8e82016-08-29 11:46:12 -0700125#include <immintrin.h>
Richard Henderson5e33a872016-08-29 11:46:15 -0700126#define AVX2_NONZERO(X) !_mm256_testz_si256((X), (X))
127ACCEL_BUFFER_ZERO(buffer_zero_avx2, 128, __m256i, AVX2_NONZERO)
Richard Henderson88ca8e82016-08-29 11:46:12 -0700128#pragma GCC pop_options
Richard Henderson5e33a872016-08-29 11:46:15 -0700129#endif
130
131#define CACHE_AVX2 2
132#define CACHE_AVX1 4
133#define CACHE_SSE4 8
134#define CACHE_SSE2 16
135
136static unsigned cpuid_cache;
137
138static void __attribute__((constructor)) init_cpuid_cache(void)
Richard Henderson88ca8e82016-08-29 11:46:12 -0700139{
Richard Henderson5e33a872016-08-29 11:46:15 -0700140 int max = __get_cpuid_max(0, NULL);
141 int a, b, c, d;
142 unsigned cache = 0;
143
144 if (max >= 1) {
145 __cpuid(1, a, b, c, d);
146 if (d & bit_SSE2) {
147 cache |= CACHE_SSE2;
148 }
149#ifdef CONFIG_AVX2_OPT
150 if (c & bit_SSE4_1) {
151 cache |= CACHE_SSE4;
152 }
153
154 /* We must check that AVX is not just available, but usable. */
155 if ((c & bit_OSXSAVE) && (c & bit_AVX)) {
156 __asm("xgetbv" : "=a"(a), "=d"(d) : "c"(0));
157 if ((a & 6) == 6) {
158 cache |= CACHE_AVX1;
159 if (max >= 7) {
160 __cpuid_count(7, 0, a, b, c, d);
161 if (b & bit_AVX2) {
162 cache |= CACHE_AVX2;
163 }
164 }
165 }
166 }
167#endif
168 }
169 cpuid_cache = cache;
Richard Henderson88ca8e82016-08-29 11:46:12 -0700170}
171
Richard Hendersonefad6682016-08-29 11:46:16 -0700172#define HAVE_NEXT_ACCEL
173bool test_buffer_is_zero_next_accel(void)
174{
175 /* If no bits set, we just tested buffer_zero_int, and there
176 are no more acceleration options to test. */
177 if (cpuid_cache == 0) {
178 return false;
179 }
180 /* Disable the accelerator we used before and select a new one. */
181 cpuid_cache &= cpuid_cache - 1;
182 return true;
183}
184
Richard Henderson5e33a872016-08-29 11:46:15 -0700185static bool select_accel_fn(const void *buf, size_t len)
Richard Henderson88ca8e82016-08-29 11:46:12 -0700186{
Richard Henderson5e33a872016-08-29 11:46:15 -0700187 uintptr_t ibuf = (uintptr_t)buf;
188#ifdef CONFIG_AVX2_OPT
189 if (len % 128 == 0 && ibuf % 32 == 0 && (cpuid_cache & CACHE_AVX2)) {
190 return buffer_zero_avx2(buf, len);
191 }
Paolo Bonzini86444f02016-09-13 17:04:52 +0200192 if (len % 64 == 0 && ibuf % 16 == 0 && (cpuid_cache & CACHE_SSE4)) {
193 return buffer_zero_sse4(buf, len);
194 }
Richard Henderson5e33a872016-08-29 11:46:15 -0700195#endif
196 if (len % 64 == 0 && ibuf % 16 == 0 && (cpuid_cache & CACHE_SSE2)) {
197 return buffer_zero_sse2(buf, len);
198 }
199 return buffer_zero_int(buf, len);
Richard Henderson88ca8e82016-08-29 11:46:12 -0700200}
Richard Henderson5e33a872016-08-29 11:46:15 -0700201
Richard Henderson5e33a872016-08-29 11:46:15 -0700202#else
203#define select_accel_fn buffer_zero_int
Richard Henderson88ca8e82016-08-29 11:46:12 -0700204#endif
205
Richard Hendersonefad6682016-08-29 11:46:16 -0700206#ifndef HAVE_NEXT_ACCEL
207bool test_buffer_is_zero_next_accel(void)
208{
209 return false;
210}
211#endif
212
Richard Henderson88ca8e82016-08-29 11:46:12 -0700213/*
214 * Checks if a buffer is all zeroes
Richard Henderson88ca8e82016-08-29 11:46:12 -0700215 */
216bool buffer_is_zero(const void *buf, size_t len)
217{
Richard Henderson5e33a872016-08-29 11:46:15 -0700218 if (unlikely(len == 0)) {
219 return true;
Richard Henderson88ca8e82016-08-29 11:46:12 -0700220 }
221
Richard Henderson5e33a872016-08-29 11:46:15 -0700222 /* Use an optimized zero check if possible. Note that this also
223 includes a check for an unrolled loop over 64-bit integers. */
224 return select_accel_fn(buf, len);
Richard Henderson88ca8e82016-08-29 11:46:12 -0700225}