Line data Source code
1 : /****************************************************************************
2 : **
3 : ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 : ** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
5 : ** Contact: http://www.qt-project.org/legal
6 : **
7 : ** This file is part of the QtCore module of the Qt Toolkit.
8 : **
9 : ** $QT_BEGIN_LICENSE:LGPL$
10 : ** Commercial License Usage
11 : ** Licensees holding valid commercial Qt licenses may use this file in
12 : ** accordance with the commercial license agreement provided with the
13 : ** Software or, alternatively, in accordance with the terms contained in
14 : ** a written agreement between you and Digia. For licensing terms and
15 : ** conditions see http://qt.digia.com/licensing. For further information
16 : ** use the contact form at http://qt.digia.com/contact-us.
17 : **
18 : ** GNU Lesser General Public License Usage
19 : ** Alternatively, this file may be used under the terms of the GNU Lesser
20 : ** General Public License version 2.1 as published by the Free Software
21 : ** Foundation and appearing in the file LICENSE.LGPL included in the
22 : ** packaging of this file. Please review the following information to
23 : ** ensure the GNU Lesser General Public License version 2.1 requirements
24 : ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 : **
26 : ** In addition, as a special exception, Digia gives you certain additional
27 : ** rights. These rights are described in the Digia Qt LGPL Exception
28 : ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 : **
30 : ** GNU General Public License Usage
31 : ** Alternatively, this file may be used under the terms of the GNU
32 : ** General Public License version 3.0 as published by the Free Software
33 : ** Foundation and appearing in the file LICENSE.GPL included in the
34 : ** packaging of this file. Please review the following information to
35 : ** ensure the GNU General Public License version 3.0 requirements will be
36 : ** met: http://www.gnu.org/copyleft/gpl.html.
37 : **
38 : **
39 : ** $QT_END_LICENSE$
40 : **
41 : ****************************************************************************/
42 :
43 : #ifndef QATOMIC_X86_H
44 : #define QATOMIC_X86_H
45 :
46 : #include <QtCore/qgenericatomic.h>
47 :
48 : QT_BEGIN_NAMESPACE
49 :
50 : #if 0
51 : // silence syncqt warnings
52 : QT_END_NAMESPACE
53 : #pragma qt_sync_skip_header_check
54 : #pragma qt_sync_stop_processing
55 : #endif
56 :
57 : #define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
58 : #define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE
59 :
60 : #define Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE
61 : #define Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE
62 :
63 : #define Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE
64 : #define Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE
65 :
66 : #define Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE
67 : #define Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE
68 :
69 : #define Q_ATOMIC_INT32_IS_SUPPORTED
70 :
71 : #define Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
72 : #define Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_WAIT_FREE
73 :
74 : #define Q_ATOMIC_INT32_TEST_AND_SET_IS_ALWAYS_NATIVE
75 : #define Q_ATOMIC_INT32_TEST_AND_SET_IS_WAIT_FREE
76 :
77 : #define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_ALWAYS_NATIVE
78 : #define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_WAIT_FREE
79 :
80 : #define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_ALWAYS_NATIVE
81 : #define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_WAIT_FREE
82 :
83 : #define Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
84 : #define Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE
85 :
86 : #define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE
87 : #define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE
88 :
89 : #define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE
90 : #define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE
91 :
92 : template <int size> struct QBasicAtomicOps: QGenericAtomicOps<QBasicAtomicOps<size> >
93 : {
94 : static inline Q_DECL_CONSTEXPR bool isReferenceCountingNative() Q_DECL_NOTHROW { return true; }
95 : static inline Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree() Q_DECL_NOTHROW { return true; }
96 : template <typename T> static bool ref(T &_q_value) Q_DECL_NOTHROW;
97 : template <typename T> static bool deref(T &_q_value) Q_DECL_NOTHROW;
98 :
99 : static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; }
100 : static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return true; }
101 : template <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
102 : template <typename T> static bool
103 : testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW;
104 :
105 : static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; }
106 : static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return true; }
107 : template <typename T> static T fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW;
108 :
109 : static inline Q_DECL_CONSTEXPR bool isFetchAndAddNative() Q_DECL_NOTHROW { return true; }
110 : static inline Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() Q_DECL_NOTHROW { return true; }
111 : template <typename T> static
112 : T fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) Q_DECL_NOTHROW;
113 : };
114 :
115 : template <typename T> struct QAtomicOps : QBasicAtomicOps<sizeof(T)>
116 : {
117 : typedef T Type;
118 : };
119 :
120 : #if defined(Q_CC_GNU)
121 :
122 : template<> struct QAtomicOpsSupport<1> { enum { IsSupported = 1 }; };
123 : template<> struct QAtomicOpsSupport<2> { enum { IsSupported = 1 }; };
124 : template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; };
125 :
126 : /*
127 : * Guide for the inline assembly below:
128 : *
129 : * x86 instructions are in the form "{opcode}{length} {source}, {destination}",
130 : * where the length is one of the letters "b" (byte), "w" (word, 16-bit), "l"
131 : * (dword, 32-bit), "q" (qword, 64-bit).
132 : *
133 : * In most cases, we can omit the length because it's inferred from one of the
134 : * registers. For example, "xchg %0,%1" doesn't need the length suffix because
135 : * we can only exchange data of the same size and one of the operands must be a
136 : * register.
137 : *
138 : * The exception is the increment and decrement functions, where we add and
139 : * subtract an immediate value (1). For those, we need to specify the length.
140 : * GCC and ICC support the syntax "add%z0 $1, %0", where "%z0" expands to the
141 : * length of the operand. Unfortunately, clang as of 3.0 doesn't support that.
142 : * For that reason, the ref() and deref() functions are rolled out for all
143 : * sizes.
144 : *
145 : * The functions are also rolled out for the 1-byte operations since those
146 : * require a special register constraint "q" to force the compiler to schedule
147 : * one of the 8-bit registers. It's probably a compiler bug that it tries to
148 : * use a register that doesn't exist.
149 : *
150 : * Finally, 64-bit operations are supported via the cmpxchg8b instruction on
151 : * 32-bit processors, via specialisation below.
152 : */
153 :
154 : template<> template<typename T> inline
155 : bool QBasicAtomicOps<1>::ref(T &_q_value) Q_DECL_NOTHROW
156 : {
157 : unsigned char ret;
158 : asm volatile("lock\n"
159 : "addb $1, %0\n"
160 : "setne %1"
161 : : "=m" (_q_value), "=qm" (ret)
162 : : "m" (_q_value)
163 : : "memory");
164 : return ret != 0;
165 : }
166 :
167 : template<> template<typename T> inline
168 : bool QBasicAtomicOps<2>::ref(T &_q_value) Q_DECL_NOTHROW
169 : {
170 : unsigned char ret;
171 : asm volatile("lock\n"
172 : "incw %0\n"
173 : "setne %1"
174 : : "=m" (_q_value), "=qm" (ret)
175 : : "m" (_q_value)
176 : : "memory");
177 : return ret != 0;
178 : }
179 :
180 : template<> template<typename T> inline
181 125 : bool QBasicAtomicOps<4>::ref(T &_q_value) Q_DECL_NOTHROW
182 : {
183 : unsigned char ret;
184 : asm volatile("lock\n"
185 : "addl $1, %0\n"
186 : "setne %1"
187 : : "=m" (_q_value), "=qm" (ret)
188 : : "m" (_q_value)
189 125 : : "memory");
190 125 : return ret != 0;
191 : }
192 :
193 : template<> template <typename T> inline
194 : bool QBasicAtomicOps<1>::deref(T &_q_value) Q_DECL_NOTHROW
195 : {
196 : unsigned char ret;
197 : asm volatile("lock\n"
198 : "subb $1, %0\n"
199 : "setne %1"
200 : : "=m" (_q_value), "=qm" (ret)
201 : : "m" (_q_value)
202 : : "memory");
203 : return ret != 0;
204 : }
205 :
206 : template<> template <typename T> inline
207 : bool QBasicAtomicOps<2>::deref(T &_q_value) Q_DECL_NOTHROW
208 : {
209 : unsigned char ret;
210 : asm volatile("lock\n"
211 : "decw %0\n"
212 : "setne %1"
213 : : "=m" (_q_value), "=qm" (ret)
214 : : "m" (_q_value)
215 : : "memory");
216 : return ret != 0;
217 : }
218 : template<> template <typename T> inline
219 336 : bool QBasicAtomicOps<4>::deref(T &_q_value) Q_DECL_NOTHROW
220 : {
221 : unsigned char ret;
222 : asm volatile("lock\n"
223 : "subl $1, %0\n"
224 : "setne %1"
225 : : "=m" (_q_value), "=qm" (ret)
226 : : "m" (_q_value)
227 336 : : "memory");
228 336 : return ret != 0;
229 : }
230 :
231 : template<int size> template <typename T> inline
232 : bool QBasicAtomicOps<size>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
233 : {
234 : unsigned char ret;
235 : asm volatile("lock\n"
236 : "cmpxchg %3,%2\n"
237 : "sete %1\n"
238 : : "=a" (newValue), "=qm" (ret), "+m" (_q_value)
239 : : "r" (newValue), "0" (expectedValue)
240 : : "memory");
241 : return ret != 0;
242 : }
243 :
244 : template<> template <typename T> inline
245 : bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
246 : {
247 : unsigned char ret;
248 : asm volatile("lock\n"
249 : "cmpxchg %3,%2\n"
250 : "sete %1\n"
251 : : "=a" (newValue), "=qm" (ret), "+m" (_q_value)
252 : : "q" (newValue), "0" (expectedValue)
253 : : "memory");
254 : return ret != 0;
255 : }
256 :
257 : template<int size> template <typename T> inline
258 : bool QBasicAtomicOps<size>::testAndSetRelaxed(T &_q_value, T expectedValue,
259 : T newValue, T *currentValue) Q_DECL_NOTHROW
260 : {
261 : unsigned char ret;
262 : asm volatile("lock\n"
263 : "cmpxchg %3,%2\n"
264 : "sete %1\n"
265 : : "=a" (newValue), "=qm" (ret), "+m" (_q_value)
266 : : "r" (newValue), "0" (expectedValue)
267 : : "memory");
268 : *currentValue = newValue;
269 : return ret != 0;
270 : }
271 :
272 : template<> template <typename T> inline
273 : bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue,
274 : T newValue, T *currentValue) Q_DECL_NOTHROW
275 : {
276 : unsigned char ret;
277 : asm volatile("lock\n"
278 : "cmpxchg %3,%2\n"
279 : "sete %1\n"
280 : : "=a" (newValue), "=qm" (ret), "+m" (_q_value)
281 : : "q" (newValue), "0" (expectedValue)
282 : : "memory");
283 : *currentValue = newValue;
284 : return ret != 0;
285 : }
286 :
287 : template<int size> template <typename T> inline
288 : T QBasicAtomicOps<size>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
289 : {
290 : asm volatile("xchg %0,%1"
291 : : "=r" (newValue), "+m" (_q_value)
292 : : "0" (newValue)
293 : : "memory");
294 : return newValue;
295 : }
296 :
297 : template<> template <typename T> inline
298 : T QBasicAtomicOps<1>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
299 : {
300 : asm volatile("xchg %0,%1"
301 : : "=q" (newValue), "+m" (_q_value)
302 : : "0" (newValue)
303 : : "memory");
304 : return newValue;
305 : }
306 :
307 : template<int size> template <typename T> inline
308 : T QBasicAtomicOps<size>::fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) Q_DECL_NOTHROW
309 : {
310 : T result;
311 : asm volatile("lock\n"
312 : "xadd %0,%1"
313 : : "=r" (result), "+m" (_q_value)
314 : : "0" (T(valueToAdd * QAtomicAdditiveType<T>::AddScale))
315 : : "memory");
316 : return result;
317 : }
318 :
319 : template<> template <typename T> inline
320 : T QBasicAtomicOps<1>::fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) Q_DECL_NOTHROW
321 : {
322 : T result;
323 : asm volatile("lock\n"
324 : "xadd %0,%1"
325 : : "=q" (result), "+m" (_q_value)
326 : : "0" (T(valueToAdd * QAtomicAdditiveType<T>::AddScale))
327 : : "memory");
328 : return result;
329 : }
330 :
331 : #define Q_ATOMIC_INT8_IS_SUPPORTED
332 :
333 : #define Q_ATOMIC_INT8_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
334 : #define Q_ATOMIC_INT8_REFERENCE_COUNTING_IS_WAIT_FREE
335 :
336 : #define Q_ATOMIC_INT8_TEST_AND_SET_IS_ALWAYS_NATIVE
337 : #define Q_ATOMIC_INT8_TEST_AND_SET_IS_WAIT_FREE
338 :
339 : #define Q_ATOMIC_INT8_FETCH_AND_STORE_IS_ALWAYS_NATIVE
340 : #define Q_ATOMIC_INT8_FETCH_AND_STORE_IS_WAIT_FREE
341 :
342 : #define Q_ATOMIC_INT8_FETCH_AND_ADD_IS_ALWAYS_NATIVE
343 : #define Q_ATOMIC_INT8_FETCH_AND_ADD_IS_WAIT_FREE
344 :
345 : #define Q_ATOMIC_INT16_IS_SUPPORTED
346 :
347 : #define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
348 : #define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_WAIT_FREE
349 :
350 : #define Q_ATOMIC_INT16_TEST_AND_SET_IS_ALWAYS_NATIVE
351 : #define Q_ATOMIC_INT16_TEST_AND_SET_IS_WAIT_FREE
352 :
353 : #define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_ALWAYS_NATIVE
354 : #define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_WAIT_FREE
355 :
356 : #define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_ALWAYS_NATIVE
357 : #define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_WAIT_FREE
358 :
359 : #ifdef Q_PROCESSOR_X86_64
360 :
361 : #define Q_ATOMIC_INT64_IS_SUPPORTED
362 :
363 : #define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
364 : #define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_WAIT_FREE
365 :
366 : #define Q_ATOMIC_INT64_TEST_AND_SET_IS_ALWAYS_NATIVE
367 : #define Q_ATOMIC_INT64_TEST_AND_SET_IS_WAIT_FREE
368 :
369 : #define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_ALWAYS_NATIVE
370 : #define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_WAIT_FREE
371 :
372 : #define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_ALWAYS_NATIVE
373 : #define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_WAIT_FREE
374 :
375 : // native support for 64-bit types
376 : template<> template<typename T> inline
377 : bool QBasicAtomicOps<8>::ref(T &_q_value) Q_DECL_NOTHROW
378 : {
379 : unsigned char ret;
380 : asm volatile("lock\n"
381 : "addq $1, %0\n"
382 : "setne %1"
383 : : "=m" (_q_value), "=qm" (ret)
384 : : "m" (_q_value)
385 : : "memory");
386 : return ret != 0;
387 : }
388 :
389 : template<> template <typename T> inline
390 : bool QBasicAtomicOps<8>::deref(T &_q_value) Q_DECL_NOTHROW
391 : {
392 : unsigned char ret;
393 : asm volatile("lock\n"
394 : "subq $1, %0\n"
395 : "setne %1"
396 : : "=m" (_q_value), "=qm" (ret)
397 : : "m" (_q_value)
398 : : "memory");
399 : return ret != 0;
400 : }
401 : #else
402 : // i386 architecture, emulate 64-bit support via cmpxchg8b
403 : template <> struct QBasicAtomicOps<8>: QGenericAtomicOps<QBasicAtomicOps<8> >
404 : {
405 : static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; }
406 : static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return true; }
407 : template <typename T> static inline
408 : bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
409 : {
410 : #ifdef __PIC__
411 : # define EBX_reg "r"
412 : # define EBX_load(reg) "xchg " reg ", %%ebx\n"
413 : #else
414 : # define EBX_reg "b"
415 : # define EBX_load(reg)
416 : #endif
417 : quint32 highExpectedValue = quint32(newValue >> 32); // ECX
418 : asm volatile(EBX_load("%3")
419 : "lock\n"
420 : "cmpxchg8b %0\n"
421 : EBX_load("%3")
422 : "sete %%cl\n"
423 : : "+m" (_q_value), "+c" (highExpectedValue), "+&A" (expectedValue)
424 : : EBX_reg (quint32(newValue & 0xffffffff))
425 : : "memory");
426 : // if the comparison failed, expectedValue here contains the current value
427 : return quint8(highExpectedValue) != 0;
428 : #undef EBX_reg
429 : #undef EBX_load
430 : }
431 : };
432 : #endif
433 :
434 : #else
435 : # error "This compiler for x86 is not supported"
436 : #endif
437 :
438 :
439 : QT_END_NAMESPACE
440 :
441 : #endif // QATOMIC_X86_H
|