// LA64 lock helper
// there is 2 part: read and write
// write return 0 on success, 1 on fail (value has been changed)

.text
.align 4

.global la64_lock_xchg_b
.global la64_lock_xchg_dd
.global la64_lock_xchg_d
.global la64_lock_storeifnull
.global la64_lock_storeifnull_d
.global la64_lock_storeifref
.global la64_lock_storeifref_d
.global la64_lock_storeifref2_d
.global la64_lock_storeb
.global la64_lock_incif0
.global la64_lock_decifnot0
.global la64_lock_store
.global la64_lock_store_dd
.global la64_lock_cas_b
.global la64_lock_cas_h
.global la64_lock_cas_d
.global la64_lock_cas_dd
.global la64_lock_cas_dq
.global la64_lock_get_b
.global la64_lock_get_d
.global la64_lock_get_dd

la64_lock_xchg_b:
    .word 0x385e1486 // amswap_db.b $a2, $a1, $a0
    move        $a0, $a2
    ret

la64_lock_xchg_dd:
    // address is a0, value is a1, return old value in a0
    amswap_db.d $a2, $a1, $a0
    move        $a0, $a2
    ret

la64_lock_xchg_d:
    // address is a0, value is a1, return old value in a0
    amswap_db.w $a2, $a1, $a0
    move        $a0, $a2
    ret

la64_lock_storeifnull:
    // address is a0, value is a1, a1 store to a0 only if [a0] is 0. return old [a0] value
    dbar 0
1:
    move $a3, $a1
    ll.d $a2, $a0, 0
    bnez $a2, 2f
    sc.d $a3, $a0, 0
    beqz $a3, 1b
2:
    move $a0, $a2
    ret

la64_lock_storeifnull_d:
    // address is a0, value is a1, a1 store to a0 only if [a0] is 0. return old [a0] value
    dbar 0
    move $a3, $a1
1:
    ll.w $a2, $a0, 0
    bnez $a2, 2f
    sc.w $a3, $a0, 0
    beqz $a3, 1b
2:
    move $a0, $a2
    ret

la64_lock_storeifref:
    // address is a0, value is a1, a1 store to a0 only if [a0] is a2. return new [a0] value (so a1 or old value)
    dbar 0
1:
    move $a4, $a1
    ll.d $a3, $a0, 0
    bne  $a2, $a3, 2f
    sc.d $a4, $a0, 0
    beqz $a4, 1b
    move $a0, $a1
    ret
2:
    move $a0, $a3
    ret

la64_lock_storeifref_d:
    // address is a0, value is a1, a1 store to a0 only if [a0] is a2. return new [a0] value (so a1 or old value)
    dbar 0
1:
    move $a4, $a1
    ll.w $a3, $a0, 0
    bne  $a2, $a3, 2f
    sc.w $a4, $a0, 0
    beqz $a4, 1b
    move $a0, $a1
    ret
2:
    move $a0, $a3
    ret

la64_lock_storeifref2_d:
    // address is a0, value is a1, a1 store to a0 only if [a0] is a2. return old [a0] value
    dbar 0
1:
    move $a4, $a1
    ll.w $a3, $a0, 0
    bne  $a2, $a3, 2f
    sc.w $a4, $a0, 0
    beqz $a4, 1b
2:
    move $a0, $a3
    ret

la64_lock_storeb:
    st.b $a1, $a0, 0
    dbar 0
    ret

la64_lock_decifnot0:
    dbar   0
1:
    ll.w   $a1, $a0, 0
    beqz   $a1, 2f
    addi.d $a1, $a1, -1
    move   $a2, $a1
    sc.w   $a2, $a0, 0
    beqz   $a2, 1b
2:
    move   $a0, $a1
    ret

la64_lock_incif0:
    dbar   0
1:
    ll.w   $a1, $a0, 0
    bnez   $a1, 2f
    addi.d $a1, $a1, 1
    move   $a2, $a1
    sc.w   $a2, $a0, 0
    beqz   $a2, 1b
2:
    move   $a0, $a1
    ret

la64_lock_store:
    st.w $a1, $a0, 0
    dbar 0
    ret

la64_lock_store_dd:
    st.d $a1, $a0, 0
    dbar 0
    ret

la64_lock_cas_b:
    ext.w.b     $a3, $a1
    .word 0x385a1885 // amcas_db.b  $a1, $a2, $a0
    xor         $a0, $a1, $a3
    sltu        $a0, $zero, $a0
    ret

la64_lock_cas_h:
    ext.w.h     $a3, $a1
    .word 0x385a9885 // amcas_db.h  $a1, $a2, $a0
    xor         $a0, $a1, $a3
    sltu        $a0, $zero, $a0
    ret

la64_lock_cas_d:
    ll.w $a3, $a0, 0
    bne  $a3, $a1, 1f
    sc.w $a2, $a0, 0
    xori $a0, $a2, 1 // sc return success in $a2, overwriting it! 0 = fail, 1 = success
    ret
1:
    li.d $a0, 1
    ret

la64_lock_cas_dd:
    ll.d $a3, $a0, 0
    bne  $a3, $a1, 1f
    sc.d $a2, $a0, 0
    xori $a0, $a2, 1
1:
    ret
    li.d $a0, 1
    ret

la64_lock_cas_dq:
    ll.d $a4, $a0, 0
    bne  $a4, $a2, 1f
    .word 0x38571c85 // sc.q $a1, $a3, $a0
    beqz $a1, 1f
    xori $a0, $a1, 1
    ret
1:
    li.d $a0, 1
    ret

la64_lock_get_b:
    dbar 0
    ld.b $a0, $a0, 0
    ret

la64_lock_get_d:
    dbar 0
    ld.w $a0, $a0, 0
    ret

la64_lock_get_dd:
    dbar 0
    ld.d $a0, $a0, 0
    ret