michael@0: ! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: ! michael@0: ! This Source Code Form is subject to the terms of the Mozilla Public michael@0: ! License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: ! file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: ! michael@0: ! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc) michael@0: ! using CAS (compare-and-swap) atomic instructions michael@0: ! michael@0: ! this MUST be compiled with an ultrasparc-aware assembler michael@0: ! michael@0: ! standard asm linkage macros; this module must be compiled michael@0: ! with the -P option (use C preprocessor) michael@0: michael@0: #include michael@0: michael@0: ! ====================================================================== michael@0: ! michael@0: ! Perform the sequence a = a + 1 atomically with respect to other michael@0: ! fetch-and-adds to location a in a wait-free fashion. michael@0: ! michael@0: ! usage : val = PR_AtomicIncrement(address) michael@0: ! return: current value (you'd think this would be old val) michael@0: ! michael@0: ! ----------------------- michael@0: ! Note on REGISTER USAGE: michael@0: ! as this is a LEAF procedure, a new stack frame is not created; michael@0: ! we use the caller's stack frame so what would normally be %i (input) michael@0: ! registers are actually %o (output registers). Also, we must not michael@0: ! overwrite the contents of %l (local) registers as they are not michael@0: ! assumed to be volatile during calls. michael@0: ! michael@0: ! So, the registers used are: michael@0: ! %o0 [input] - the address of the value to increment michael@0: ! %o1 [local] - work register michael@0: ! %o2 [local] - work register michael@0: ! %o3 [local] - work register michael@0: ! ----------------------- michael@0: michael@0: ENTRY(PR_AtomicIncrement) ! standard assembler/ELF prologue michael@0: michael@0: retryAI: michael@0: ld [%o0], %o2 ! set o2 to the current value michael@0: add %o2, 0x1, %o3 ! calc the new value michael@0: mov %o3, %o1 ! save the return value michael@0: cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed michael@0: cmp %o2, %o3 ! see if we set the value michael@0: bne retryAI ! if not, try again michael@0: nop ! empty out the branch pipeline michael@0: retl ! return back to the caller michael@0: mov %o1, %o0 ! set the return code to the new value michael@0: michael@0: SET_SIZE(PR_AtomicIncrement) ! standard assembler/ELF epilogue michael@0: michael@0: ! michael@0: ! end michael@0: ! michael@0: ! ====================================================================== michael@0: ! michael@0: michael@0: ! ====================================================================== michael@0: ! michael@0: ! Perform the sequence a = a - 1 atomically with respect to other michael@0: ! fetch-and-decs to location a in a wait-free fashion. michael@0: ! michael@0: ! usage : val = PR_AtomicDecrement(address) michael@0: ! return: current value (you'd think this would be old val) michael@0: ! michael@0: ! ----------------------- michael@0: ! Note on REGISTER USAGE: michael@0: ! as this is a LEAF procedure, a new stack frame is not created; michael@0: ! we use the caller's stack frame so what would normally be %i (input) michael@0: ! registers are actually %o (output registers). Also, we must not michael@0: ! overwrite the contents of %l (local) registers as they are not michael@0: ! assumed to be volatile during calls. michael@0: ! michael@0: ! So, the registers used are: michael@0: ! %o0 [input] - the address of the value to increment michael@0: ! %o1 [local] - work register michael@0: ! %o2 [local] - work register michael@0: ! %o3 [local] - work register michael@0: ! ----------------------- michael@0: michael@0: ENTRY(PR_AtomicDecrement) ! standard assembler/ELF prologue michael@0: michael@0: retryAD: michael@0: ld [%o0], %o2 ! set o2 to the current value michael@0: sub %o2, 0x1, %o3 ! calc the new value michael@0: mov %o3, %o1 ! save the return value michael@0: cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed michael@0: cmp %o2, %o3 ! see if we set the value michael@0: bne retryAD ! if not, try again michael@0: nop ! empty out the branch pipeline michael@0: retl ! return back to the caller michael@0: mov %o1, %o0 ! set the return code to the new value michael@0: michael@0: SET_SIZE(PR_AtomicDecrement) ! standard assembler/ELF epilogue michael@0: michael@0: ! michael@0: ! end michael@0: ! michael@0: ! ====================================================================== michael@0: ! michael@0: michael@0: ! ====================================================================== michael@0: ! michael@0: ! Perform the sequence a = b atomically with respect to other michael@0: ! fetch-and-stores to location a in a wait-free fashion. michael@0: ! michael@0: ! usage : old_val = PR_AtomicSet(address, newval) michael@0: ! michael@0: ! ----------------------- michael@0: ! Note on REGISTER USAGE: michael@0: ! as this is a LEAF procedure, a new stack frame is not created; michael@0: ! we use the caller's stack frame so what would normally be %i (input) michael@0: ! registers are actually %o (output registers). Also, we must not michael@0: ! overwrite the contents of %l (local) registers as they are not michael@0: ! assumed to be volatile during calls. michael@0: ! michael@0: ! So, the registers used are: michael@0: ! %o0 [input] - the address of the value to increment michael@0: ! %o1 [input] - the new value to set for [%o0] michael@0: ! %o2 [local] - work register michael@0: ! %o3 [local] - work register michael@0: ! ----------------------- michael@0: michael@0: ENTRY(PR_AtomicSet) ! standard assembler/ELF prologue michael@0: michael@0: retryAS: michael@0: ld [%o0], %o2 ! set o2 to the current value michael@0: mov %o1, %o3 ! set up the new value michael@0: cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed michael@0: cmp %o2, %o3 ! see if we set the value michael@0: bne retryAS ! if not, try again michael@0: nop ! empty out the branch pipeline michael@0: retl ! return back to the caller michael@0: mov %o3, %o0 ! set the return code to the prev value michael@0: michael@0: SET_SIZE(PR_AtomicSet) ! standard assembler/ELF epilogue michael@0: michael@0: ! michael@0: ! end michael@0: ! michael@0: ! ====================================================================== michael@0: ! michael@0: michael@0: ! ====================================================================== michael@0: ! michael@0: ! Perform the sequence a = a + b atomically with respect to other michael@0: ! fetch-and-adds to location a in a wait-free fashion. michael@0: ! michael@0: ! usage : newval = PR_AtomicAdd(address, val) michael@0: ! return: the value after addition michael@0: ! michael@0: ENTRY(PR_AtomicAdd) ! standard assembler/ELF prologue michael@0: michael@0: retryAA: michael@0: ld [%o0], %o2 ! set o2 to the current value michael@0: add %o2, %o1, %o3 ! calc the new value michael@0: mov %o3, %o4 ! save the return value michael@0: cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed michael@0: cmp %o2, %o3 ! see if we set the value michael@0: bne retryAA ! if not, try again michael@0: nop ! empty out the branch pipeline michael@0: retl ! return back to the caller michael@0: mov %o4, %o0 ! set the return code to the new value michael@0: michael@0: SET_SIZE(PR_AtomicAdd) ! standard assembler/ELF epilogue michael@0: michael@0: ! michael@0: ! end michael@0: !