]> pd.if.org Git - liblfds/blob - liblfds/liblfds6.0.1/liblfds601/src/lfds601_abstraction/lfds601_abstraction_dcas.c
Initial import (all versions, including the new 7.1.0)
[liblfds] / liblfds / liblfds6.0.1 / liblfds601 / src / lfds601_abstraction / lfds601_abstraction_dcas.c
1 #include "lfds601_abstraction_internal.h"\r
2 \r
3 \r
4 \r
5 \r
6 \r
7 /****************************************************************************/\r
8 #if (defined _WIN64 && defined _MSC_VER)\r
9 \r
10   /* TRD : 64 bit Windows (user-mode or kernel) on any CPU with the Microsoft C compiler\r
11 \r
12            _WIN64    indicates 64 bit Windows\r
13            _MSC_VER  indicates Microsoft C compiler\r
14   */\r
15 \r
16   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
17   {\r
18     unsigned char\r
19       cas_result;\r
20 \r
21     assert( destination != NULL );\r
22     assert( exchange != NULL );\r
23     assert( compare != NULL );\r
24 \r
25     cas_result = _InterlockedCompareExchange128( (volatile __int64 *) destination, (__int64) *(exchange+1), (__int64) *exchange, (__int64 *) compare );\r
26 \r
27     return( cas_result );\r
28   }\r
29 \r
30 #endif\r
31 \r
32 \r
33 \r
34 \r
35 \r
36 /****************************************************************************/\r
37 #if (!defined _WIN64 && defined _WIN32 && defined _MSC_VER)\r
38 \r
39   /* TRD : 32 bit Windows (user-mode or kernel) on any CPU with the Microsoft C compiler\r
40 \r
41            (!defined _WIN64 && defined _WIN32)  indicates 32 bit Windows\r
42            _MSC_VER                             indicates Microsoft C compiler\r
43   */\r
44 \r
45   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
46   {\r
47     __int64\r
48       original_compare;\r
49 \r
50     assert( destination != NULL );\r
51     assert( exchange != NULL );\r
52     assert( compare != NULL );\r
53 \r
54     *(__int64 *) &original_compare = *(__int64 *) compare;\r
55 \r
56     *(__int64 *) compare = _InterlockedCompareExchange64( (volatile __int64 *) destination, *(__int64 *) exchange, *(__int64 *) compare );\r
57 \r
58     return( (unsigned char) (*(__int64 *) compare == *(__int64 *) &original_compare) );\r
59   }\r
60 \r
61 #endif\r
62 \r
63 \r
64 \r
65 \r
66 \r
67 /****************************************************************************/\r
68 #if (defined __x86_64__ && __GNUC__ && !defined __pic__)\r
69 \r
70   /* TRD : any OS on x64 with GCC for statically linked code\r
71 \r
72            __x86_64__  indicates x64\r
73            __GNUC__    indicates GCC\r
74   */\r
75 \r
76   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
77   {\r
78     unsigned char\r
79       cas_result;\r
80 \r
81     assert( destination != NULL );\r
82     assert( exchange != NULL );\r
83     assert( compare != NULL );\r
84 \r
85     __asm__ __volatile__\r
86     (\r
87       "lock;"           // make cmpxchg16b atomic\r
88       "cmpxchg16b %0;"  // cmpxchg16b sets ZF on success\r
89       "setz       %3;"  // if ZF set, set cas_result to 1\r
90 \r
91       // output\r
92       : "+m" (*(volatile lfds601_atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)\r
93 \r
94       // input\r
95       : "b" (*exchange), "c" (*(exchange+1))\r
96 \r
97       // clobbered\r
98       : "cc", "memory"\r
99     );\r
100 \r
101     return( cas_result );\r
102   }\r
103 \r
104 #endif\r
105 \r
106 \r
107 \r
108 \r
109 \r
110 \r
111 /****************************************************************************/\r
112 #if (defined __i686__ && __GNUC__ && !defined __pic__)\r
113 \r
114   /* TRD : any OS on x86 with GCC for statically linked code\r
115 \r
116            __i686__  indicates x86\r
117            __GNUC__  indicates GCC\r
118   */\r
119 \r
120   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
121   {\r
122     unsigned char\r
123       cas_result;\r
124 \r
125     assert( destination != NULL );\r
126     assert( exchange != NULL );\r
127     assert( compare != NULL );\r
128 \r
129     __asm__ __volatile__\r
130     (\r
131       "lock;"          // make cmpxchg8b atomic\r
132       "cmpxchg8b %0;"  // cmpxchg8b sets ZF on success\r
133       "setz      %3;"  // if ZF set, set cas_result to 1\r
134 \r
135       // output\r
136       : "+m" (*(volatile lfds601_atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)\r
137 \r
138       // input\r
139       : "b" (*exchange), "c" (*(exchange+1))\r
140 \r
141       // clobbered\r
142       : "cc", "memory"\r
143     );\r
144 \r
145     return( cas_result );\r
146   }\r
147 \r
148 #endif\r
149 \r
150 \r
151 \r
152 \r
153 \r
154 /****************************************************************************/\r
155 #if (defined __x86_64__ && __GNUC__ && defined __pic__)\r
156 \r
157   /* TRD : any OS on x64 with GCC for position independent code (e.g. a shared object)\r
158 \r
159            __x86_64__  indicates x64\r
160            __GNUC__    indicates GCC\r
161   */\r
162 \r
163   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
164   {\r
165     unsigned char\r
166       cas_result;\r
167 \r
168     assert( destination != NULL );\r
169     assert( exchange != NULL );\r
170     assert( compare != NULL );\r
171 \r
172     /* TRD : with a shared object, we cannot clobber RBX\r
173              as such, we borrow RSI - we load half of the exchange value into it\r
174              then swap it with RBX\r
175              then do the compare-and-swap\r
176              then swap the original value of RBX back from RSI\r
177     */\r
178 \r
179     __asm__ __volatile__\r
180     (\r
181       "xchg %%rsi, %%rbx;"  // swap RBI and RBX \r
182       "lock;"               // make cmpxchg16b atomic\r
183       "cmpxchg16b %0;"      // cmpxchg16b sets ZF on success\r
184       "setz       %3;"      // if ZF set, set cas_result to 1\r
185       "xchg %%rbx, %%rsi;"  // re-swap RBI and RBX\r
186 \r
187       // output\r
188       : "+m" (*(volatile lfds601_atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)\r
189 \r
190       // input\r
191       : "S" (*exchange), "c" (*(exchange+1))\r
192 \r
193       // clobbered\r
194       : "cc", "memory"\r
195     );\r
196 \r
197     return( cas_result );\r
198   }\r
199 \r
200 #endif\r
201 \r
202 \r
203 \r
204 \r
205 \r
206 \r
207 /****************************************************************************/\r
208 #if (defined __i686__ && __GNUC__ && defined __pic__)\r
209 \r
210   /* TRD : any OS on x86 with GCC for position independent code (e.g. a shared object)\r
211 \r
212            __i686__  indicates x86\r
213            __GNUC__  indicates GCC\r
214   */\r
215 \r
216   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
217   {\r
218     unsigned char\r
219       cas_result;\r
220 \r
221     assert( destination != NULL );\r
222     assert( exchange != NULL );\r
223     assert( compare != NULL );\r
224 \r
225     /* TRD : with a shared object, we cannot clobber EBX\r
226              as such, we borrow ESI - we load half of the exchange value into it\r
227              then swap it with EBX\r
228              then do the compare-and-swap\r
229              then swap the original value of EBX back from ESI\r
230     */\r
231 \r
232     __asm__ __volatile__\r
233     (\r
234       "xchg %%esi, %%ebx;"  // swap EBI and EBX\r
235       "lock;"               // make cmpxchg8b atomic\r
236       "cmpxchg8b %0;"       // cmpxchg8b sets ZF on success\r
237       "setz      %3;"       // if ZF set, set cas_result to 1\r
238       "xchg %%ebx, %%esi;"  // re-swap EBI and EBX\r
239 \r
240       // output\r
241       : "+m" (*(volatile lfds601_atom_t (*)[2]) destination), "+a" (*compare), "+d" (*(compare+1)), "=q" (cas_result)\r
242 \r
243       // input\r
244       : "S" (*exchange), "c" (*(exchange+1))\r
245 \r
246       // clobbered\r
247       : "cc", "memory"\r
248     );\r
249 \r
250     return( cas_result );\r
251   }\r
252 \r
253 #endif\r
254 \r
255 \r
256 \r
257 \r
258 \r
259 /****************************************************************************/\r
260 #if (defined __arm__ && __GNUC__)\r
261 \r
262   /* TRD : any OS on any ARM with GCC\r
263 \r
264            Remember however we need to set into compare the original value of destination.\r
265 \r
266            __arm__   indicates ARM\r
267            __GNUC__  indicates GCC\r
268   */\r
269 \r
270   LFDS601_INLINE unsigned char lfds601_abstraction_dcas( volatile lfds601_atom_t *destination, lfds601_atom_t *exchange, lfds601_atom_t *compare )\r
271   {\r
272     lfds601_atom_t\r
273       *local_compare = compare,\r
274       stored_flag = 1;\r
275 \r
276     register lfds601_atom_t\r
277       local_exchange_a __asm("r2"),\r
278       local_exchange_b __asm("r3"),\r
279       local_compare_a __asm("r4"),\r
280       local_compare_b __asm("r5"),\r
281       original_destination_a __asm("r6"),\r
282       original_destination_b __asm("r7");\r
283 \r
284     assert( destination != NULL );\r
285     assert( exchange != NULL );\r
286     assert( compare != NULL );\r
287 \r
288     /* TRD : some notes\r
289 \r
290              the double word ldr and str instructions require contigous registers\r
291              where the first register is an even number\r
292 \r
293              honouring this requirement requires us to specifically specify\r
294              the registers to use (which is why we're using register __asm("rN")\r
295              in the declerations above\r
296 \r
297              the arguments to the function occupy registers r0, r1 and r2\r
298 \r
299              we can use up to and including r8, but r9 can have a frame pointer in it\r
300 \r
301              so we make a copy of compare (freeing up r2, so we can use it for a double\r
302              word load) but use destination (r0) and exchange (r1) directly\r
303 \r
304              note LDRD and STRD became available in armv6k\r
305 \r
306              apologies for the trickery with the mcr register variable - the code runs\r
307              out of registers on armv6k\r
308     */\r
309 \r
310     __asm__ __volatile__\r
311     (\r
312       "  mov     %[stored_flag], #1;"                                                                // put 1 into stored_flag\r
313       "  mov     %[local_exchange_a], #0;"                                                           // borrow local_exchange_a for mcr, to save a register\r
314       "  mcr     p15, 0, %[local_exchange_a], c7, c10, 5;"                                           // memory barrier (ARM v6 compatible)\r
315       "  ldrd    %[local_exchange_a], %[local_exchange_b], [%[exchange]];"                           // load exchange into local_exchange_a and local_exchange_b (which are r2 and r3, respectively)\r
316       "  ldrd    %[local_compare_a], %[local_compare_b], [%[local_compare]];"                        // load compare into local_compare_a and local_compare_b (which are r4 and r5, respectively)\r
317       "atomic_dcas:;"\r
318       "  ldrexd  %[original_destination_a], %[original_destination_b], [%[destination]];"            // load destination into original_destination_a and original_destination_b (which are r6 and r7, respectively)\r
319       "  teq     %[original_destination_a], %[local_compare_a];"                                     // compare the first word of destination with the first word of compare\r
320       "  teqeq   %[original_destination_b], %[local_compare_b];"                                     // if they're equal, compare the second word of destination with the second word of compare\r
321       "  bne     exit;"                                                                              // if either word of destination does not match its respective word of compare, exit\r
322       "  strexd  %[stored_flag], %[local_exchange_a], %[local_exchange_b], [%[destination]];"        // if both words were equal, try to store local_exchange_a and local_exchange_b into *destination (on success, strexed puts 0 into stored_flag)\r
323       "  teq     %[stored_flag], #0;"                                                                // check if stored_flag is 0\r
324       "  bne     atomic_dcas;"                                                                       // if not 0, retry (someone else touched *destination after we loaded but before we stored)\r
325       "exit:;"\r
326       "  strd    %[original_destination_a], %[original_destination_b], [%[local_compare]];"          // whether or not the CAS swapped, we always write the original value of destination into *compare\r
327       "  mov     %[local_exchange_a], #0;"                                                           // borrow local_exchange_a for mcr, to save a register\r
328       "  mcr     p15, 0, %[local_exchange_a], c7, c10, 5;"                                           // memory barrier (ARM v6 compatible)\r
329 \r
330       // output\r
331       : "+m" (*(volatile lfds601_atom_t (*)[2]) destination), "+m" (*(lfds601_atom_t (*)[2]) local_compare),\r
332         [stored_flag] "+&r" (stored_flag),\r
333         [original_destination_a] "+&r" (original_destination_a), [original_destination_b] "+&r" (original_destination_b),\r
334         [local_compare_a] "+&r" (local_compare_a), [local_compare_b] "+&r" (local_compare_b),\r
335         [local_exchange_a] "+&r" (local_exchange_a), [local_exchange_b] "+&r" (local_exchange_b)\r
336 \r
337       // input\r
338       : "m" (*(lfds601_atom_t (*)[2]) exchange),\r
339         [destination] "r" (destination),\r
340         [local_compare] "r" (local_compare),\r
341         [exchange] "r" (exchange)\r
342 \r
343       // clobbered\r
344       : "cc", "memory"                                                                               // memory is clobbered because we issue a memory barrier\r
345     );\r
346 \r
347     /* TRD : stored_flag is set to 0 on store, 1 on fail\r
348              we need to return 1 on success, 0 on fail\r
349     */\r
350 \r
351     return( (unsigned char) !stored_flag  );\r
352   }\r
353 \r
354 #endif\r
355 \r
356 \r