Another minor optimization of x86-64 pthread_cond_wait.
[glibc/pb-stable.git] / nptl / sysdeps / unix / sysv / linux / x86_64 / pthread_cond_wait.S
blobf5b929ea71b1aeea15f5e9fe7fce6f666721afee
1 /* Copyright (C) 2002-2007, 2009 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
20 #include <sysdep.h>
21 #include <shlib-compat.h>
22 #include <lowlevellock.h>
23 #include <lowlevelcond.h>
24 #include <tcb-offsets.h>
25 #include <pthread-pi-defines.h>
27 #include <kernel-features.h>
30         .text
32 /* int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)  */
33         .globl  __pthread_cond_wait
34         .type   __pthread_cond_wait, @function
35         .align  16
36 __pthread_cond_wait:
37 .LSTARTCODE:
38         cfi_startproc
39 #ifdef SHARED
40         cfi_personality(DW_EH_PE_pcrel | DW_EH_PE_sdata4 | DW_EH_PE_indirect,
41                         DW.ref.__gcc_personality_v0)
42         cfi_lsda(DW_EH_PE_pcrel | DW_EH_PE_sdata4, .LexceptSTART)
43 #else
44         cfi_personality(DW_EH_PE_udata4, __gcc_personality_v0)
45         cfi_lsda(DW_EH_PE_udata4, .LexceptSTART)
46 #endif
48 #define FRAME_SIZE 32
49         leaq    -FRAME_SIZE(%rsp), %rsp
50         cfi_adjust_cfa_offset(FRAME_SIZE)
52         /* Stack frame:
54            rsp + 32
55                     +--------------------------+
56            rsp + 24 | old wake_seq value       |
57                     +--------------------------+
58            rsp + 16 | mutex pointer            |
59                     +--------------------------+
60            rsp +  8 | condvar pointer          |
61                     +--------------------------+
62            rsp +  4 | old broadcast_seq value  |
63                     +--------------------------+
64            rsp +  0 | old cancellation mode    |
65                     +--------------------------+
66         */
68         cmpq    $-1, dep_mutex(%rdi)
70                 /* Prepare structure passed to cancellation handler.  */
71         movq    %rdi, 8(%rsp)
72         movq    %rsi, 16(%rsp)
74         je      15f
75         movq    %rsi, dep_mutex(%rdi)
77         /* Get internal lock.  */
78 15:     movl    $1, %esi
79         xorl    %eax, %eax
80         LOCK
81 #if cond_lock == 0
82         cmpxchgl %esi, (%rdi)
83 #else
84         cmpxchgl %esi, cond_lock(%rdi)
85 #endif
86         jne     1f
88         /* Unlock the mutex.  */
89 2:      movq    16(%rsp), %rdi
90         xorl    %esi, %esi
91         callq   __pthread_mutex_unlock_usercnt
93         testl   %eax, %eax
94         jne     12f
96         movq    8(%rsp), %rdi
97         incq    total_seq(%rdi)
98         incl    cond_futex(%rdi)
99         addl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
101         /* Get and store current wakeup_seq value.  */
102         movq    8(%rsp), %rdi
103         movq    wakeup_seq(%rdi), %r9
104         movl    broadcast_seq(%rdi), %edx
105         movq    %r9, 24(%rsp)
106         movl    %edx, 4(%rsp)
108         /* Unlock.  */
109 8:      movl    cond_futex(%rdi), %edx
110         LOCK
111 #if cond_lock == 0
112         decl    (%rdi)
113 #else
114         decl    cond_lock(%rdi)
115 #endif
116         jne     3f
118 .LcleanupSTART:
119 4:      callq   __pthread_enable_asynccancel
120         movl    %eax, (%rsp)
122         xorq    %r10, %r10
123         cmpq    $-1, dep_mutex(%rdi)
124         leaq    cond_futex(%rdi), %rdi
125         movl    $FUTEX_WAIT, %esi
126         je      60f
128         movq    dep_mutex-cond_futex(%rdi), %r8
129         /* Requeue to a non-robust PI mutex if the PI bit is set and
130         the robust bit is not set.  */
131         movl    MUTEX_KIND(%r8), %eax
132         andl    $(ROBUST_BIT|PI_BIT), %eax
133         cmpl    $PI_BIT, %eax
134         jne     61f
136         movl    $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
137         movl    $SYS_futex, %eax
138         syscall
140         movl    $1, %r8d
141 #ifdef __ASSUME_REQUEUE_PI
142         jmp     62f
143 #else
144         cmpq    $-4095, %rax
145         jnae    62f
147 # ifndef __ASSUME_PRIVATE_FUTEX
148         movl    $FUTEX_WAIT, %esi
149 # endif
150 #endif
153 #ifdef __ASSUME_PRIVATE_FUTEX
154         movl    $(FUTEX_WAIT|FUTEX_PRIVATE_FLAG), %esi
155 #else
156         orl     %fs:PRIVATE_FUTEX, %esi
157 #endif
158 60:     xorl    %r8d, %r8d
159         movl    $SYS_futex, %eax
160         syscall
162 62:     movl    (%rsp), %edi
163         callq   __pthread_disable_asynccancel
164 .LcleanupEND:
166         /* Lock.  */
167         movq    8(%rsp), %rdi
168         movl    $1, %esi
169         xorl    %eax, %eax
170         LOCK
171 #if cond_lock == 0
172         cmpxchgl %esi, (%rdi)
173 #else
174         cmpxchgl %esi, cond_lock(%rdi)
175 #endif
176         jnz     5f
178 6:      movl    broadcast_seq(%rdi), %edx
180         movq    woken_seq(%rdi), %rax
182         movq    wakeup_seq(%rdi), %r9
184         cmpl    4(%rsp), %edx
185         jne     16f
187         cmpq    24(%rsp), %r9
188         jbe     8b
190         cmpq    %rax, %r9
191         jna     8b
193         incq    woken_seq(%rdi)
195         /* Unlock */
196 16:     subl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
198         /* Wake up a thread which wants to destroy the condvar object.  */
199         cmpq    $0xffffffffffffffff, total_seq(%rdi)
200         jne     17f
201         movl    cond_nwaiters(%rdi), %eax
202         andl    $~((1 << nwaiters_shift) - 1), %eax
203         jne     17f
205         addq    $cond_nwaiters, %rdi
206         cmpq    $-1, dep_mutex-cond_nwaiters(%rdi)
207         movl    $1, %edx
208 #ifdef __ASSUME_PRIVATE_FUTEX
209         movl    $FUTEX_WAKE, %eax
210         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
211         cmove   %eax, %esi
212 #else
213         movl    $0, %eax
214         movl    %fs:PRIVATE_FUTEX, %esi
215         cmove   %eax, %esi
216         orl     $FUTEX_WAKE, %esi
217 #endif
218         movl    $SYS_futex, %eax
219         syscall
220         subq    $cond_nwaiters, %rdi
222 17:     LOCK
223 #if cond_lock == 0
224         decl    (%rdi)
225 #else
226         decl    cond_lock(%rdi)
227 #endif
228         jne     10f
230         /* If requeue_pi is used the kernel performs the locking of the
231            mutex. */
232 11:     movq    16(%rsp), %rdi
233         testl   %r8d, %r8d
234         jnz     18f
236         callq   __pthread_mutex_cond_lock
238 14:     leaq    FRAME_SIZE(%rsp), %rsp
239         cfi_adjust_cfa_offset(-FRAME_SIZE)
241         /* We return the result of the mutex_lock operation.  */
242         retq
244         cfi_adjust_cfa_offset(FRAME_SIZE)
246 18:     callq   __pthread_mutex_cond_lock_adjust
247         xorl    %eax, %eax
248         jmp     14b
250         /* Initial locking failed.  */
252 #if cond_lock != 0
253         addq    $cond_lock, %rdi
254 #endif
255         cmpq    $-1, dep_mutex-cond_lock(%rdi)
256         movl    $LLL_PRIVATE, %eax
257         movl    $LLL_SHARED, %esi
258         cmovne  %eax, %esi
259         callq   __lll_lock_wait
260         jmp     2b
262         /* Unlock in loop requires wakeup.  */
264 #if cond_lock != 0
265         addq    $cond_lock, %rdi
266 #endif
267         cmpq    $-1, dep_mutex-cond_lock(%rdi)
268         movl    $LLL_PRIVATE, %eax
269         movl    $LLL_SHARED, %esi
270         cmovne  %eax, %esi
271         /* The call preserves %rdx.  */
272         callq   __lll_unlock_wake
273 #if cond_lock != 0
274         subq    $cond_lock, %rdi
275 #endif
276         jmp     4b
278         /* Locking in loop failed.  */
280 #if cond_lock != 0
281         addq    $cond_lock, %rdi
282 #endif
283         cmpq    $-1, dep_mutex-cond_lock(%rdi)
284         movl    $LLL_PRIVATE, %eax
285         movl    $LLL_SHARED, %esi
286         cmovne  %eax, %esi
287         callq   __lll_lock_wait
288 #if cond_lock != 0
289         subq    $cond_lock, %rdi
290 #endif
291         jmp     6b
293         /* Unlock after loop requires wakeup.  */
295 #if cond_lock != 0
296         addq    $cond_lock, %rdi
297 #endif
298         cmpq    $-1, dep_mutex-cond_lock(%rdi)
299         movl    $LLL_PRIVATE, %eax
300         movl    $LLL_SHARED, %esi
301         cmovne  %eax, %esi
302         callq   __lll_unlock_wake
303         jmp     11b
305         /* The initial unlocking of the mutex failed.  */
306 12:     movq    %rax, %r10
307         movq    8(%rsp), %rdi
308         LOCK
309 #if cond_lock == 0
310         decl    (%rdi)
311 #else
312         decl    cond_lock(%rdi)
313 #endif
314         je      13f
316 #if cond_lock != 0
317         addq    $cond_lock, %rdi
318 #endif
319         cmpq    $-1, dep_mutex-cond_lock(%rdi)
320         movl    $LLL_PRIVATE, %eax
321         movl    $LLL_SHARED, %esi
322         cmovne  %eax, %esi
323         callq   __lll_unlock_wake
325 13:     movq    %r10, %rax
326         jmp     14b
327         .size   __pthread_cond_wait, .-__pthread_cond_wait
328 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
329                   GLIBC_2_3_2)
332         .align  16
333         .type   __condvar_cleanup1, @function
334         .globl  __condvar_cleanup1
335         .hidden __condvar_cleanup1
336 __condvar_cleanup1:
337         /* Stack frame:
339            rsp + 32
340                     +--------------------------+
341            rsp + 24 | unused                   |
342                     +--------------------------+
343            rsp + 16 | mutex pointer            |
344                     +--------------------------+
345            rsp +  8 | condvar pointer          |
346                     +--------------------------+
347            rsp +  4 | old broadcast_seq value  |
348                     +--------------------------+
349            rsp +  0 | old cancellation mode    |
350                     +--------------------------+
351         */
353         movq    %rax, 24(%rsp)
355         /* Get internal lock.  */
356         movq    8(%rsp), %rdi
357         movl    $1, %esi
358         xorl    %eax, %eax
359         LOCK
360 #if cond_lock == 0
361         cmpxchgl %esi, (%rdi)
362 #else
363         cmpxchgl %esi, cond_lock(%rdi)
364 #endif
365         jz      1f
367 #if cond_lock != 0
368         addq    $cond_lock, %rdi
369 #endif
370         cmpq    $-1, dep_mutex-cond_lock(%rdi)
371         movl    $LLL_PRIVATE, %eax
372         movl    $LLL_SHARED, %esi
373         cmovne  %eax, %esi
374         callq   __lll_lock_wait
375 #if cond_lock != 0
376         subq    $cond_lock, %rdi
377 #endif
379 1:      movl    broadcast_seq(%rdi), %edx
380         cmpl    4(%rsp), %edx
381         jne     3f
383         /* We increment the wakeup_seq counter only if it is lower than
384            total_seq.  If this is not the case the thread was woken and
385            then canceled.  In this case we ignore the signal.  */
386         movq    total_seq(%rdi), %rax
387         cmpq    wakeup_seq(%rdi), %rax
388         jbe     6f
389         incq    wakeup_seq(%rdi)
390         incl    cond_futex(%rdi)
391 6:      incq    woken_seq(%rdi)
393 3:      subl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
395         /* Wake up a thread which wants to destroy the condvar object.  */
396         xorl    %ecx, %ecx
397         cmpq    $0xffffffffffffffff, total_seq(%rdi)
398         jne     4f
399         movl    cond_nwaiters(%rdi), %eax
400         andl    $~((1 << nwaiters_shift) - 1), %eax
401         jne     4f
403         cmpq    $-1, dep_mutex(%rdi)
404         leaq    cond_nwaiters(%rdi), %rdi
405         movl    $1, %edx
406 #ifdef __ASSUME_PRIVATE_FUTEX
407         movl    $FUTEX_WAKE, %eax
408         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
409         cmove   %eax, %esi
410 #else
411         movl    $0, %eax
412         movl    %fs:PRIVATE_FUTEX, %esi
413         cmove   %eax, %esi
414         orl     $FUTEX_WAKE, %esi
415 #endif
416         movl    $SYS_futex, %eax
417         syscall
418         subq    $cond_nwaiters, %rdi
419         movl    $1, %ecx
421 4:      LOCK
422 #if cond_lock == 0
423         decl    (%rdi)
424 #else
425         decl    cond_lock(%rdi)
426 #endif
427         je      2f
428 #if cond_lock != 0
429         addq    $cond_lock, %rdi
430 #endif
431         cmpq    $-1, dep_mutex-cond_lock(%rdi)
432         movl    $LLL_PRIVATE, %eax
433         movl    $LLL_SHARED, %esi
434         cmovne  %eax, %esi
435         /* The call preserves %rcx.  */
436         callq   __lll_unlock_wake
438         /* Wake up all waiters to make sure no signal gets lost.  */
439 2:      testl   %ecx, %ecx
440         jnz     5f
441         addq    $cond_futex, %rdi
442         cmpq    $-1, dep_mutex-cond_futex(%rdi)
443         movl    $0x7fffffff, %edx
444 #ifdef __ASSUME_PRIVATE_FUTEX
445         movl    $FUTEX_WAKE, %eax
446         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
447         cmove   %eax, %esi
448 #else
449         movl    $0, %eax
450         movl    %fs:PRIVATE_FUTEX, %esi
451         cmove   %eax, %esi
452         orl     $FUTEX_WAKE, %esi
453 #endif
454         movl    $SYS_futex, %eax
455         syscall
457 5:      movq    16(%rsp), %rdi
458         callq   __pthread_mutex_cond_lock
460         movq    24(%rsp), %rdi
461 .LcallUR:
462         call    _Unwind_Resume@PLT
463         hlt
464 .LENDCODE:
465         cfi_endproc
466         .size   __condvar_cleanup1, .-__condvar_cleanup1
469         .section .gcc_except_table,"a",@progbits
470 .LexceptSTART:
471         .byte   DW_EH_PE_omit                   # @LPStart format
472         .byte   DW_EH_PE_omit                   # @TType format
473         .byte   DW_EH_PE_uleb128                # call-site format
474         .uleb128 .Lcstend-.Lcstbegin
475 .Lcstbegin:
476         .uleb128 .LcleanupSTART-.LSTARTCODE
477         .uleb128 .LcleanupEND-.LcleanupSTART
478         .uleb128 __condvar_cleanup1-.LSTARTCODE
479         .uleb128  0
480         .uleb128 .LcallUR-.LSTARTCODE
481         .uleb128 .LENDCODE-.LcallUR
482         .uleb128 0
483         .uleb128  0
484 .Lcstend:
487 #ifdef SHARED
488         .hidden DW.ref.__gcc_personality_v0
489         .weak   DW.ref.__gcc_personality_v0
490         .section .gnu.linkonce.d.DW.ref.__gcc_personality_v0,"aw",@progbits
491         .align  8
492         .type   DW.ref.__gcc_personality_v0, @object
493         .size   DW.ref.__gcc_personality_v0, 8
494 DW.ref.__gcc_personality_v0:
495         .quad   __gcc_personality_v0
496 #endif