1 // Copyright (c) 2011 Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google Inc. nor the name Chromium Embedded
14 // Framework nor the names of its contributors may be used to endorse
15 // or promote products derived from this software without specific prior
16 // written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // Do not include this header file directly. Use base/cef_atomicops.h
31 // instead.
32
33 #ifndef CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
34 #define CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
35
36 // This struct is not part of the public API of this module; clients may not
37 // use it.
38 // Features of this x86. Values may not be correct before main() is run,
39 // but are set conservatively.
40 struct AtomicOps_x86CPUFeatureStruct {
41 bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
42 // after acquire compare-and-swap.
43 };
44 extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
45
46 #define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
47
48 namespace base {
49 namespace subtle {
50
51 // 32-bit low-level operations on any platform.
52
NoBarrier_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)53 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
54 Atomic32 old_value,
55 Atomic32 new_value) {
56 Atomic32 prev;
57 __asm__ __volatile__("lock; cmpxchgl %1,%2"
58 : "=a"(prev)
59 : "q"(new_value), "m"(*ptr), "0"(old_value)
60 : "memory");
61 return prev;
62 }
63
NoBarrier_AtomicExchange(volatile Atomic32 * ptr,Atomic32 new_value)64 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
65 Atomic32 new_value) {
66 __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
67 : "=r"(new_value)
68 : "m"(*ptr), "0"(new_value)
69 : "memory");
70 return new_value; // Now it's the previous value.
71 }
72
NoBarrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)73 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
74 Atomic32 increment) {
75 Atomic32 temp = increment;
76 __asm__ __volatile__("lock; xaddl %0,%1"
77 : "+r"(temp), "+m"(*ptr)
78 :
79 : "memory");
80 // temp now holds the old value of *ptr
81 return temp + increment;
82 }
83
Barrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)84 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
85 Atomic32 increment) {
86 Atomic32 temp = increment;
87 __asm__ __volatile__("lock; xaddl %0,%1"
88 : "+r"(temp), "+m"(*ptr)
89 :
90 : "memory");
91 // temp now holds the old value of *ptr
92 if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
93 __asm__ __volatile__("lfence" : : : "memory");
94 }
95 return temp + increment;
96 }
97
Acquire_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)98 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
99 Atomic32 old_value,
100 Atomic32 new_value) {
101 Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
102 if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
103 __asm__ __volatile__("lfence" : : : "memory");
104 }
105 return x;
106 }
107
Release_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)108 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
109 Atomic32 old_value,
110 Atomic32 new_value) {
111 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
112 }
113
NoBarrier_Store(volatile Atomic32 * ptr,Atomic32 value)114 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
115 *ptr = value;
116 }
117
MemoryBarrier()118 inline void MemoryBarrier() {
119 __asm__ __volatile__("mfence" : : : "memory");
120 }
121
Acquire_Store(volatile Atomic32 * ptr,Atomic32 value)122 inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
123 *ptr = value;
124 MemoryBarrier();
125 }
126
Release_Store(volatile Atomic32 * ptr,Atomic32 value)127 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
128 ATOMICOPS_COMPILER_BARRIER();
129 *ptr = value; // An x86 store acts as a release barrier.
130 // See comments in Atomic64 version of Release_Store(), below.
131 }
132
NoBarrier_Load(volatile const Atomic32 * ptr)133 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
134 return *ptr;
135 }
136
Acquire_Load(volatile const Atomic32 * ptr)137 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
138 Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
139 // See comments in Atomic64 version of Release_Store(), below.
140 ATOMICOPS_COMPILER_BARRIER();
141 return value;
142 }
143
Release_Load(volatile const Atomic32 * ptr)144 inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
145 MemoryBarrier();
146 return *ptr;
147 }
148
149 #if defined(__x86_64__)
150
151 // 64-bit low-level operations on 64-bit platform.
152
NoBarrier_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)153 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
154 Atomic64 old_value,
155 Atomic64 new_value) {
156 Atomic64 prev;
157 __asm__ __volatile__("lock; cmpxchgq %1,%2"
158 : "=a"(prev)
159 : "q"(new_value), "m"(*ptr), "0"(old_value)
160 : "memory");
161 return prev;
162 }
163
NoBarrier_AtomicExchange(volatile Atomic64 * ptr,Atomic64 new_value)164 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
165 Atomic64 new_value) {
166 __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
167 : "=r"(new_value)
168 : "m"(*ptr), "0"(new_value)
169 : "memory");
170 return new_value; // Now it's the previous value.
171 }
172
NoBarrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)173 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
174 Atomic64 increment) {
175 Atomic64 temp = increment;
176 __asm__ __volatile__("lock; xaddq %0,%1"
177 : "+r"(temp), "+m"(*ptr)
178 :
179 : "memory");
180 // temp now contains the previous value of *ptr
181 return temp + increment;
182 }
183
Barrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)184 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
185 Atomic64 increment) {
186 Atomic64 temp = increment;
187 __asm__ __volatile__("lock; xaddq %0,%1"
188 : "+r"(temp), "+m"(*ptr)
189 :
190 : "memory");
191 // temp now contains the previous value of *ptr
192 if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
193 __asm__ __volatile__("lfence" : : : "memory");
194 }
195 return temp + increment;
196 }
197
NoBarrier_Store(volatile Atomic64 * ptr,Atomic64 value)198 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
199 *ptr = value;
200 }
201
Acquire_Store(volatile Atomic64 * ptr,Atomic64 value)202 inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
203 *ptr = value;
204 MemoryBarrier();
205 }
206
Release_Store(volatile Atomic64 * ptr,Atomic64 value)207 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
208 ATOMICOPS_COMPILER_BARRIER();
209
210 *ptr = value; // An x86 store acts as a release barrier
211 // for current AMD/Intel chips as of Jan 2008.
212 // See also Acquire_Load(), below.
213
214 // When new chips come out, check:
215 // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
216 // System Programming Guide, Chatper 7: Multiple-processor management,
217 // Section 7.2, Memory Ordering.
218 // Last seen at:
219 // http://developer.intel.com/design/pentium4/manuals/index_new.htm
220 //
221 // x86 stores/loads fail to act as barriers for a few instructions (clflush
222 // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
223 // not generated by the compiler, and are rare. Users of these instructions
224 // need to know about cache behaviour in any case since all of these involve
225 // either flushing cache lines or non-temporal cache hints.
226 }
227
NoBarrier_Load(volatile const Atomic64 * ptr)228 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
229 return *ptr;
230 }
231
Acquire_Load(volatile const Atomic64 * ptr)232 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
233 Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
234 // for current AMD/Intel chips as of Jan 2008.
235 // See also Release_Store(), above.
236 ATOMICOPS_COMPILER_BARRIER();
237 return value;
238 }
239
Release_Load(volatile const Atomic64 * ptr)240 inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
241 MemoryBarrier();
242 return *ptr;
243 }
244
Acquire_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)245 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
246 Atomic64 old_value,
247 Atomic64 new_value) {
248 Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
249 if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
250 __asm__ __volatile__("lfence" : : : "memory");
251 }
252 return x;
253 }
254
Release_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)255 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
256 Atomic64 old_value,
257 Atomic64 new_value) {
258 return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
259 }
260
261 #endif // defined(__x86_64__)
262
263 } // namespace base::subtle
264 } // namespace base
265
266 #undef ATOMICOPS_COMPILER_BARRIER
267
268 #endif // CEF_INCLUDE_BASE_INTERNAL_CEF_ATOMICOPS_X86_GCC_H_
269