windows-nt/Source/XPSP1/NT/base/ntos/ex/alpha/intrlock.s
2020-09-26 16:20:57 +08:00

1397 lines
42 KiB
ArmAsm
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// TITLE("Interlocked Support")
//++
//
// Copyright (c) 1990 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// intrlock.s
//
// Abstract:
//
// This module implements functions to support interlocked operations.
// Interlocked operations can only operate on nonpaged data and the
// specified spinlock cannot be used for any other purpose.
//
// Author:
//
// David N. Cutler (davec) 26-Mar-1990
//
// Environment:
//
// Kernel mode.
//
// Revision History:
//
// Thomas Van Baak (tvb) 18-May-1992
//
// Adapted for Alpha AXP.
//
//--
#include "ksalpha.h"
SBTTL("Interlocked Add Large Integer")
//++
//
// LARGE_INTEGER
// ExInterlockedAddLargeInteger (
// IN PLARGE_INTEGER Addend,
// IN LARGE_INTEGER Increment,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function performs an interlocked add of an increment value to an
// addend variable of type large integer. The initial value of the addend
// variable is returned as the function value.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// adjusted by the increment value.
//
// Increment (a1) - Supplies the increment value to be added to the
// addend variable.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the addend variable.
//
// Return Value:
//
// The result of the interlocked large integer add.
//
// Implementation Note:
//
// The specification of this function requires that the given lock must be
// used to synchronize the update even though on Alpha the operation can
// actually done atomically without using the specified lock.
//
//--
LEAF_ENTRY(ExInterlockedAddLargeInteger)
10: DISABLE_INTERRUPTS // disable interrupts
#if !defined(NT_UP)
LDP_L t0, 0(a2) // get current lock value - locked
bne t0, 20f // if ne, spin lock owned
mov a2, t0 // set ownership value (lock address)
STP_C t0, 0(a2) // set spin lock owned - conditionally
beq t0, 20f // if eq, conditional store failed
mb // synchronize memory access
#endif
ldq t0, 0(a0) // get addend
addq t0, a1, v0 // do the add
stq v0, 0(a0) // store result
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a2) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // enable interrupts
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
20: ENABLE_INTERRUPTS // enable interrupts
22: LDP t0, 0(a2) // read current lock value
beq t0, 10b // if eq, lock not owned
br zero, 22b // spin in cache until available
#endif
.end ExInterlockedAddLargeInteger
SBTTL("Interlocked Add Large Statistic")
//++
//
// VOID
// ExInterlockedAddLargeStatistic (
// IN PLARGE_INTEGER Addend,
// IN ULONG Increment
// )
//
// Routine Description:
//
// This function performs an interlocked add of an increment value to an
// addend variable of type large integer.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// adjusted by the increment value.
//
// Increment (a1) - Supplies the increment value to be added to the
// addend variable.
//
// Return Value:
//
// None.
//
// Implementation Note:
//
// The specification of this function requires that the given lock must be
// used to synchronize the update even though on Alpha the operation can
// actually done atomically without using the specified lock.
//
//--
LEAF_ENTRY(ExInterlockedAddLargeStatistic)
zap a1, 0xf0, a1 // zero extend increment value
10: ldq_l t0, 0(a0) // get addend
addq t0, a1, t0 // do the add
stq_c t0, 0(a0) // store result
beq t0, 20f // if eq, store conditional failed
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
20: br zero, 10b // try again
.end ExInterlockedAddLargeStatistic
SBTTL("Interlocked Add Unsigned Long")
//++
//
// ULONG
// ExInterlockedAddUlong (
// IN PULONG Addend,
// IN ULONG Increment,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function performs an interlocked add of an increment value to an
// addend variable of type unsigned long. The initial value of the addend
// variable is returned as the function value.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// adjusted by the increment value.
//
// Increment (a1) - Supplies the increment value to be added to the
// addend variable.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the addend variable.
//
// Return Value:
//
// The initial value of the addend variable.
//
// Implementation Note:
//
// The specification of this function requires that the given lock must be
// used to synchronize the update even though on Alpha the operation can
// actually done atomically without using the specified lock.
//
//--
LEAF_ENTRY(ExInterlockedAddUlong)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a2) // get current lock value - locked
bne t0, 20f // if ne, spin lock still owned
mov a2, t0 // set ownership value (lock address)
STP_C t0, 0(a2) // set spin lock owned - conditionally
beq t0, 20f // if eq, conditional store failed
mb // synchronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
ldl t0, 0(a0) // get addend value (return value also)
addl t0, a1, t1 // compute adjusted value
stl t1, 0(a0) // store updated value
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a2) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
mov t0, v0 // set return value
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
20: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
22: LDP t0, 0(a2) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 22b // spin in cache until available
#endif
.end ExInterlockedAddUlong
SBTTL("Interlocked Exchange Unsigned Long")
//++
//
// ULONG
// ExInterlockedExchangeUlong (
// IN PULONG Source,
// IN ULONG Value,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function performs an interlocked exchange of a longword value with
// a longword in memory and returns the memory value.
//
// N.B. There is an alternate entry point provided for this routine which
// is ALPHA target specific and whose prototype does not include the
// spinlock parameter. Since the routine never refers to the spinlock
// parameter, no additional code is required.
//
// Arguments:
//
// Source (a0) - Supplies a pointer to a variable whose value is to be
// exchanged.
//
// Value (a1) - Supplies the value to exchange with the source value.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the source variable.
//
// Return Value:
//
// The source value is returned as the function value.
//
//--
LEAF_ENTRY(ExInterlockedExchangeUlong)
ALTERNATE_ENTRY(ExAlphaInterlockedExchangeUlong)
10: ldl_l v0, 0(a0) // get current source value
bis a1, zero, t0 // set exchange value
stl_c t0, 0(a0) // replace source value
beq t0, 20f // if eq, conditional store failed
ret zero, (ra) // return old value to caller
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
20: br zero,10b // go try spin lock again
.end ExInterlockedExchangeUlong
SBTTL("Interlocked Decrement Long")
//++
//
// INTERLOCKED_RESULT
// ExInterlockedDecrementLong (
// IN PLONG Addend,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function performs an interlocked decrement on an addend variable
// of type signed long. The sign and whether the result is zero is returned
// as the function value.
//
// N.B. There is an alternate entry point provided for this routine which
// is ALPHA target specific and whose prototype does not include the
// spinlock parameter. Since the routine never refers to the spinlock
// parameter, no additional code is required.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// decremented.
//
// Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
// access to the addend variable.
//
// Return Value:
//
// RESULT_NEGATIVE is returned if the resultant addend value is negative.
// RESULT_ZERO is returned if the resultant addend value is zero.
// RESULT_POSITIVE is returned if the resultant addend value is positive.
//
// Implementation Note:
//
// The specification of this function does not require that the given lock
// be used to synchronize the update as long as the update is synchronized
// somehow. On Alpha a single load locked-store conditional does the job.
//
//--
LEAF_ENTRY(ExInterlockedDecrementLong)
ALTERNATE_ENTRY(ExAlphaInterlockedDecrementLong)
10: ldl_l v0, 0(a0) // get current addend value - locked
subl v0, 1, v0 // decrement addend value
mov v0, t0 // copy updated value to t0 for store
stl_c t0, 0(a0) // store updated value - conditionally
beq t0, 20f // if eq, conditional store failed
//
// Determine the INTERLOCKED_RESULT value based on the updated addend value.
// N.B. RESULT_ZERO = 0, RESULT_NEGATIVE = 1, RESULT_POSITIVE = 2.
//
sra v0, 63, t0 // replicate the sign bit to every bit
addl t0, 2, t0 // if t0 = 0 return 2, if -1 return 1
cmovne v0, t0, v0 // if v0 = 0 return 0
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
20: br zero, 10b // go try spin lock again
.end ExInterlockedDecrementLong
SBTTL("Interlocked Increment Long")
//++
//
// INTERLOCKED_RESULT
// ExInterlockedIncrementLong (
// IN PLONG Addend,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function performs an interlocked increment on an addend variable
// of type signed long. The sign and whether the result is zero is returned
// as the function value.
//
// N.B. There is an alternate entry point provided for this routine which
// is ALPHA target specific and whose prototype does not include the
// spinlock parameter. Since the routine never refers to the spinlock
// parameter, no additional code is required.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// incremented.
//
// Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
// access to the addend variable.
//
// Return Value:
//
// RESULT_NEGATIVE is returned if the resultant addend value is negative.
// RESULT_ZERO is returned if the resultant addend value is zero.
// RESULT_POSITIVE is returned if the resultant addend value is positive.
//
// Implementation Note:
//
// The specification of this function does not require that the given lock
// be used to synchronize the update as long as the update is synchronized
// somehow. On Alpha a single load locked-store conditional does the job.
//
//--
LEAF_ENTRY(ExInterlockedIncrementLong)
ALTERNATE_ENTRY(ExAlphaInterlockedIncrementLong)
10: ldl_l v0, 0(a0) // get current addend value - locked
addl v0, 1, v0 // increment addend value
mov v0, t0 // copy updated value to t0 for store
stl_c t0, 0(a0) // store updated value - conditionally
beq t0, 20f // if eq, conditional store failed
//
// Determine the INTERLOCKED_RESULT value based on the updated addend value.
// N.B. RESULT_ZERO = 0, RESULT_NEGATIVE = 1, RESULT_POSITIVE = 2.
//
sra v0, 63, t0 // replicate the sign bit to every bit
addl t0, 2, t0 // if t0 = 0 return 2, if -1 return 1
cmovne v0, t0, v0 // if v0 = 0 return 0
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
20: br zero, 10b // go try spin lock again
.end ExInterlockedIncrementLong
SBTTL("Interlocked Insert Head List")
//++
//
// PLIST_ENTRY
// ExInterlockedInsertHeadList (
// IN PLIST_ENTRY ListHead,
// IN PLIST_ENTRY ListEntry,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function inserts an entry at the head of a doubly linked list
// so that access to the list is synchronized in a multiprocessor system.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the head of the doubly linked
// list into which an entry is to be inserted.
//
// ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
// head of the list.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the list.
//
// Return Value:
//
// Pointer to entry that was at the head of the list or NULL if the list
// was empty.
//
//--
LEAF_ENTRY(ExInterlockedInsertHeadList)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a2) // get current lock value - locked
bne t0, 20f // if ne, spin lock still owned
mov a2, t0 // set ownership value (lock address)
STP_C t0, 0(a2) // set spin lock owned - conditionally
beq t0, 20f // if eq, conditional store failed
mb // synchronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
LDP t0, LsFlink(a0) // get address of next entry (return value also)
STP t0, LsFlink(a1) // store next link in entry
STP a0, LsBlink(a1) // store previous link in entry
STP a1, LsBlink(t0) // store previous link in next
STP a1, LsFlink(a0) // store next link in head
#if !defined(NT_UP)
mb // sychronize memory access
STP zero, 0(a2) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
xor t0, a0, v0 // if t0=a0, list empty, set v0 to NULL
cmovne v0, t0, v0 // else return previous entry at head
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
20: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
22: LDP t0, 0(a2) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 22b // spin in cache until available
#endif
.end ExInterlockedInsertHeadList
SBTTL("Interlocked Insert Tail List")
//++
//
// PLIST_ENTRY
// ExInterlockedInsertTailList (
// IN PLIST_ENTRY ListHead,
// IN PLIST_ENTRY ListEntry,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function inserts an entry at the tail of a doubly linked list
// so that access to the list is synchronized in a multiprocessor system.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the head of the doubly linked
// list into which an entry is to be inserted.
//
// ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
// tail of the list.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the list.
//
// Return Value:
//
// Pointer to entry that was at the tail of the list or NULL if the list
// was empty.
//
//--
LEAF_ENTRY(ExInterlockedInsertTailList)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a2) // get current lock value - locked
bne t0, 20f // if ne, spin lock still owned
mov a2, t0 // set ownership value (lock address)
STP_C t0, 0(a2) // set spin lock owned - conditionally
beq t0, 20f // if eq, conditional store failed
mb // sychronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
LDP t0, LsBlink(a0) // get address of previous entry (return value also)
STP a0, LsFlink(a1) // store next link in entry
STP t0, LsBlink(a1) // store previous link in entry
STP a1, LsBlink(a0) // store previous link in next
STP a1, LsFlink(t0) // store next link in head
#if !defined(NT_UP)
mb // sychronize memory access
STP zero, 0(a2) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
xor t0, a0, v0 // if t0=a0, list empty, set v0 to NULL
cmovne v0, t0, v0 // else return previous entry at tail
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
20: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
22: LDP t0, 0(a2) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 22b // spin in cache until available
#endif
.end ExInterlockedInsertTailList
SBTTL("Interlocked Remove Head List")
//++
//
// PLIST_ENTRY
// ExInterlockedRemoveHeadList (
// IN PLIST_ENTRY ListHead,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function removes an entry from the head of a doubly linked list
// so that access to the list is synchronized in a multiprocessor system.
// If there are no entries in the list, then a value of NULL is returned.
// Otherwise, the address of the entry that is removed is returned as the
// function value.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the head of the doubly linked
// list from which an entry is to be removed.
//
// Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
// access to the list.
//
// Return Value:
//
// The address of the entry removed from the list, or NULL if the list is
// empty.
//
//--
LEAF_ENTRY(ExInterlockedRemoveHeadList)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a1) // get current lock value - locked
bne t0, 30f // if ne, spin lock still owned
mov a1, t0 // set ownership value (lock address)
STP_C t0, 0(a1) // set spin lock owned - conditionally
beq t0, 30f // if eq, conditional store failed
mb // synchronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
LDP t2, LsFlink(a0) // get address of next entry
xor t2, a0, t0 // if t2=a0, list empty, set t0 to NULL
beq t0, 20f // if eq, list is empty
LDP t1, LsFlink(t2) // get address of next entry
STP t1, LsFlink(a0) // store address of next in head
STP a0, LsBlink(t1) // store address of previous in next
mov t2, t0 // return the address of entry removed
20: //
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a1) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
mov t0, v0 // set return value
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
30: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
32: LDP t0, 0(a1) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 32b // spin in cache until available
#endif
.end ExInterlockedRemoveHeadList
SBTTL("Interlocked Pop Entry List")
//++
//
// PSINGLE_LIST_ENTRY
// ExInterlockedPopEntryList (
// IN PSINGLE_LIST_ENTRY ListHead,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function removes an entry from the head of a singly linked list
// so that access to the list is synchronized in a multiprocessor system.
// If there are no entries in the list, then a value of NULL is returned.
// Otherwise, the address of the entry that is removed is returned as the
// function value.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the head of the singly linked
// list from which an entry is to be removed.
//
// Lock (a1) - Supplies a pointer to a spin lock to be used to synchronize
// access to the list.
//
// Return Value:
//
// The address of the entry removed from the list, or NULL if the list is
// empty.
//
//--
LEAF_ENTRY(ExInterlockedPopEntryList)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a1) // get current lock value - locked
bne t0, 30f // if ne, spin lock still owned
mov a1, t0 // set ownership value (lock address)
STP_C t0, 0(a1) // set spin lock owned - conditionally
beq t0, 30f // if eq, conditional store failed
mb // synchronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
LDP t0, 0(a0) // get address of next entry (return value also)
beq t0, 20f // if eq [NULL], list is empty
LDP t1, 0(t0) // get address of next entry
STP t1, 0(a0) // store address of next in head
20: //
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a1) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
mov t0, v0 // set return value
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
30: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
32: LDP t0, 0(a1) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 32b // spin in cache until available
#endif
.end ExInterlockedPopEntryList
SBTTL("Interlocked Push Entry List")
//++
//
// PSINGLE_LIST_ENTRY
// ExInterlockedPushEntryList (
// IN PSINGLE_LIST_ENTRY ListHead,
// IN PSINGLE_LIST_ENTRY ListEntry,
// IN PKSPIN_LOCK Lock
// )
//
// Routine Description:
//
// This function inserts an entry at the head of a singly linked list
// so that access to the list is synchronized in a multiprocessor system.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the head of the singly linked
// list into which an entry is to be inserted.
//
// ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
// head of the list.
//
// Lock (a2) - Supplies a pointer to a spin lock to be used to synchronize
// access to the list.
//
// Return Value:
//
// Previous contents of ListHead. NULL implies list went from empty
// to not empty.
//
//--
LEAF_ENTRY(ExInterlockedPushEntryList)
10: DISABLE_INTERRUPTS // (PALcode) v0 is clobbered
#if !defined(NT_UP)
LDP_L t0, 0(a2) // get current lock value - locked
bne t0, 20f // if ne, spin lock still owned
mov a2, t0 // set ownership value (lock address)
STP_C t0, 0(a2) // set spin lock owned - conditionally
beq t0, 20f // if eq, conditional store failed
mb // synchronize memory access
#endif
//
// Set the return value in t0 for now since PALcode may use v0.
//
LDP t0, 0(a0) // get address of first entry (return value also)
STP t0, 0(a1) // set address of next in new entry
STP a1, 0(a0) // set address of first entry
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a2) // set spin lock not owned
#endif
ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
mov t0, v0 // set return value
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
#if !defined(NT_UP)
20: ENABLE_INTERRUPTS // (PALcode) v0 is clobbered
22: LDP t0, 0(a2) // read current lock value
beq t0, 10b // try spinlock again if available
br zero, 22b // spin in cache until available
#endif
.end ExInterlockedPushEntryList
SBTTL("Interlocked Compare Exchange")
//++
//
// PVOID
// InterlockedCompareExchange (
// IN OUT PVOID *Destination,
// IN PVOID Exchange,
// IN PVOID Comperand
// )
//
// Routine Description:
//
// This function performs an interlocked compare of the destination
// value with the comperand value. If the destination value is equal
// to the comperand value, then the exchange value is stored in the
// destination. Otherwise, no opeation is performed.
//
// Arguments:
//
// Destination (a0) - Supplies a pointer to the destination value.
//
// Exchange (a1) - Supplies the exchange.
//
// Comperand (a2) - Supplies the comperand value.
//
// Return Value:
//
// The initial destination value is returned as the function value.
//
//--
LEAF_ENTRY(InterlockedCompareExchange)
10: //
#if !defined(NT_UP)
mb // synchronize memory access
#endif
ldl_l v0, 0(a0) // get current value
bis a1, zero, t0 // copy exchange value for store
cmpeq v0, a2, t1 // check if operands match
beq t1, 20f // if eq, operands mismatch
stl_c t0, 0(a0) // store updated addend value
beq t0,25f // if eq, store conditional failed
#if !defined(NT_UP)
mb // synchronize memory access
#endif
20: ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
25: br zero, 10b // go try spin lock again
.end InterlockedCompareExchange
SBTTL("Interlocked Exchange Add")
//++
//
// LONG
// ExInterlockedAdd (
// IN PLONG Addend,
// IN ULONG Increment
// )
//
// Routine Description:
//
// This function performs an interlocked add of an increment value to an
// addend variable of type unsigned long. The initial value of the addend
// variable is returned as the function value.
//
// Arguments:
//
// Addend (a0) - Supplies a pointer to a variable whose value is to be
// adjusted by the increment value.
//
// Increment (a1) - Supplies the increment value to be added to the
// addend variable.
//
// Return Value:
//
// The initial value of the addend variable.
//
//--
LEAF_ENTRY(InterlockedExchangeAdd)
10: //
#if !defined(NT_UP)
mb // synchronize memory access
#endif
ldl_l v0, 0(a0) // get current addend value - locked
addl v0, a1, t0 // increment addend value
stl_c t0, 0(a0) // store updated value - conditionally
beq t0, 20f // if eq, conditonal store failed
#if !defined(NT_UP)
mb // synchronize memory access
#endif
ret zero, (ra) // return
//
// We expect the store conditional will usually succeed the first time so it
// is faster to branch forward (predicted not taken) to here and then branch
// backward (predicted taken) to where we wanted to go.
//
20: br zero, 10b // go try spin lock again
.end InterlockedExchangeAdd
SBTTL("Interlocked Pop Entry Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedPopEntrySList (
// IN PSLIST_HEADER ListHead
// )
//
// Routine Description:
//
// This function removes an entry from the front of a sequenced singly
// linked list so that access to the list is synchronized in a MP system.
// If there are no entries in the list, then a value of NULL is returned.
// Otherwise, the address of the entry that is removed is returned as the
// function value.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the sequenced listhead from which
// an entry is to be removed.
//
// Return Value:
//
// The address of the entry removed from the list, or NULL if the list is
// empty.
//
//--
LEAF_ENTRY(ExpInterlockedPopEntrySList)
//
// N.B. The following code is the continuation address should a fault
// occur in the rare case described below.
//
ALTERNATE_ENTRY(ExpInterlockedPopEntrySListResume)
10: ldq t0, 0(a0) // get next entry address and sequence
#if defined(_AXP64_)
sra t0, 63 - 42, v0 // extract next entry address
bic v0, 7, v0 //
beq v0, 30f // if eq, list is empty
bis t0, zero, t1 // copy depth and sequence
#else
addl t0, zero, v0 // sign extend next entry address
beq v0, 30f // if eq, list is empty
srl t0, 32, t1 // shift sequence to low 32-bits
#endif
#if !defined(NT_UP)
mb // synchronize memory access
#endif
//
// N.B. It is possible for the following instruction to fault in the rare
// case where the first entry in the list is allocated on another
// processor and freed between the time the free pointer is read above
// and the following instruction. When this happens, the access fault
// code continues execution above at the resumption address and the
// entire operation is retried.
//
ALTERNATE_ENTRY(ExpInterlockedPopEntrySListFault)
LDP t5, 0(v0) // get address of successor entry
#if defined(_AXP64_)
sll t5, 63 - 42, t2 // shift address into position
#else
zapnot t5, 0xf ,t2 // clear high 32-bits for merge
#endif
ldq_l t3, 0(a0) // reload next entry address and sequence
ldil t5, 0xffff // decrement list depth and
addl t1, t5, t1 // increment sequence number
#if defined(_AXP64_)
zapnot t1, 0x7, t1 // clear upper five bytes
#else
sll t1, 32, t1 // shift depth and sequence into position
#endif
cmpeq t0, t3, t4 // check if listhead has changed
beq t4, 15f // if eq, listhead changed
bis t1, t2, t1 // merge address, depth, and sequence
stq_c t1, 0(a0) // store next entry address and sequence
beq t1, 15f // if eq, store conditional failed
#if !defined(NT_UP)
mb // synchronize memory access
#endif
30: ret zero, (ra) //
//
// Conditional store attempt failed or listhead changed.
//
15: br zero, 10b // retry
.end ExpInterlockedPopEntrySList
SBTTL("Interlocked Push Entry Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedPushEntrySList (
// IN PSLIST_HEADER ListHead,
// IN PSINGLE_LIST_ENTRY ListEntry
// )
//
// Routine Description:
//
// This function inserts an entry at the head of a sequenced singly linked
// list so that access to the list is synchronized in an MP system.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the sequenced listhead into which
// an entry is to be inserted.
//
// ListEntry (a1) - Supplies a pointer to the entry to be inserted at the
// head of the list.
//
// Return Value:
//
// Previous contents of ListHead. NULL implies list went from empty
// to not empty.
//
//--
LEAF_ENTRY(ExpInterlockedPushEntrySList)
10: ldq t0, 0(a0) // get next entry address and sequence
#if defined(_AXP64_)
sra t0, 63 - 42, v0 // extract next entry address
bic v0, 7, v0 //
bis t0, zero, t1 // copy depth and sequence number
#else
addl t0, zero, v0 // sign extend next entry address
srl t0, 32, t1 // shift sequence to low 32-bits
#endif
STP v0, 0(a1) // set next link in new first entry
#if !defined(NT_UP)
mb // synchronize memory access
#endif
#if defined(_AXP64_)
sll a1, 63 - 42, t2 // shift address into position
#else
zapnot a1, 0xf, t2 // zero extend new first entry
#endif
ldq_l t3, 0(a0) // reload next entry address and sequence
ldah t5, 1(zero) // get sequence adjustment value
addl t1, 1, t1 // increment list depth
addl t1, t5, t1 // increment sequence number
#if defined(_AXP64_)
zapnot t1, 0x7, t1 // clear upper five bytes
#else
sll t1, 32, t1 // merge new first entry address and sequence
#endif
cmpeq t0, t3, t4 // check if listhead changed
beq t4, 15f // if eq, listhead changed
bis t1, t2, t2 // merge address, depth, and sequence
stq_c t2, 0(a0) // store next entry address and sequence
beq t2, 15f // if eq, store conditional failed
ret zero, (ra) // return
//
// Conditional store attempt failed or listhead changed.
//
15: br zero, 10b // retry
.end ExpInterlockedPushEntrySList
SBTTL("Interlocked Flush Sequenced List")
//++
//
// PSINGLE_LIST_ENTRY
// ExpInterlockedFlushSList (
// IN PSLIST_HEADER ListHead
// )
//
// Routine Description:
//
// This function flushes the entire list of entries on a sequenced singly
// linked list so that access to the list is synchronized in a MP system.
// If there are no entries in the list, then a value of NULL is returned.
// Otherwise, the address of the 1st entry on the list is returned as the
// function value.
//
// Arguments:
//
// ListHead (a0) - Supplies a pointer to the sequenced listhead from which
// an entry is to be removed.
//
// Return Value:
//
// The address of the entry removed from the list, or NULL if the list is
// empty.
//
//--
LEAF_ENTRY(ExpInterlockedFlushSList)
and t1, zero, t1 // set new listhead value
10: ldq_l t0, 0(a0) // get next entry address and sequence
stq_c t1, 0(a0) // store new listhead value
beq t1, 15f // if eq, store conditional failed
#if defined(_AXP64_)
sra t0, 63 - 42, v0 // extract next entry address
bic v0, 7, v0 //
#else
addl t0, zero, v0 // sign extend next entry address
#endif
ret zero, (ra) // return
//
// Conditional store attempt failed or listhead changed.
//
15: br zero, 10b // retry, store conditional failed
.end ExpInterlockedFlushSList
SBTTL("Interlocked Compare Exchange 64-bits")
//++
//
// LONGLONG
// ExpInterlockedCompareExchange64 (
// IN PLONGLONG Destination,
// IN PLONGLONG Exchange,
// IN PLONGLONG Comperand
// )
//
// Routine Description:
//
// This function performs an interlocked compare and exchange of 64-bits.
//
// Arguments:
//
// Destination (a0) - Supplies a pointer to the destination variable.
//
// Exchange (a1) - Supplies a pointer to the exchange value.
//
// Comperand (a2) - Supplies a pointer to the comperand value.
//
// Return Value:
//
// The current destination value are returned as the function value.
//
//--
LEAF_ENTRY(ExpInterlockedCompareExchange64)
ldq t0, 0(a1) // get exchange value
ldq t1, 0(a2) // get comperand value
10: ldq_l v0, 0(a0) // get current destination value
bis t0, zero, t2 // set exchange value
cmpeq v0, t1, t3 // check if current and comperand match
beq t3, 20f // if eq, current and comperand mismatch
stq_c t2, 0(a0) // store exchange value
beq t2, 30f // if eq, store conditional failed
20: ret zero, (ra)
//
// Conditional store attempt failed.
//
30: br zero, 10b // retry
.end ExpInterlockedCompareExchange64
SBTTL("Interlocked Compare Exchange 64-bits")
//++
//
// LONGLONG
// InterlockedCompareExchange64 (
// IN PLONGLONG Destination,
// IN LONGLONG Exchange,
// IN LONGLONG Comperand
// )
//
// Routine Description:
//
// This function performs an interlocked compare and exchange of 64-bits.
//
// Arguments:
//
// Destination (a0) - Supplies a pointer to the destination variable.
//
// Exchange (a1) - Supplies the exchange value.
//
// Comperand (a2) - Supplies the comperand value.
//
// Return Value:
//
// The current destination value are returned as the function value.
//
//--
#if !defined(_AXP64_)
LEAF_ENTRY(InterlockedCompareExchange64)
10: ldq_l v0, 0(a0) // get current destination value
bis a1, zero, t2 // set exchange value
cmpeq v0, a2, t3 // check if current and comperand match
beq t3, 20f // if eq, current and comperand mismatch
stq_c t2, 0(a0) // store exchange value
beq t2, 10b // if eq, store conditional failed
20: ret zero, (ra)
.end InterlockedCompareExchange64
#endif