blob: d79f28acdee6d302bef8a6513b25a92ca7a629ab [file] [log] [blame]
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// RISC-V's atomic operations have two bits, aq ("acquire") and rl ("release"),
// which may be toggled on and off. Their precise semantics are defined in
// section 6.3 of the specification, but the basic idea is as follows:
//
// - If neither aq nor rl is set, the CPU may reorder the atomic arbitrarily.
// It guarantees only that it will execute atomically.
//
// - If aq is set, the CPU may move the instruction backward, but not forward.
//
// - If rl is set, the CPU may move the instruction forward, but not backward.
//
// - If both are set, the CPU may not reorder the instruction at all.
//
// These four modes correspond to other well-known memory models on other CPUs.
// On ARM, aq corresponds to a dmb ishst, aq+rl corresponds to a dmb ish. On
// Intel, aq corresponds to an lfence, rl to an sfence, and aq+rl to an mfence
// (or a lock prefix).
//
// Go's memory model requires that
// - if a read happens after a write, the read must observe the write, and
// that
// - if a read happens concurrently with a write, the read may observe the
// write.
// aq is sufficient to guarantee this, so that's what we use here. (This jibes
// with ARM, which uses dmb ishst.)
#include "textflag.h"
#define AMOWSC(op,rd,rs1,rs2) WORD $0x0600202f+rd<<7+rs1<<15+rs2<<20+op<<27
#define AMODSC(op,rd,rs1,rs2) WORD $0x0600302f+rd<<7+rs1<<15+rs2<<20+op<<27
#define ADD_ 0
#define SWAP_ 1
#define LR_ 2
#define SC_ 3
#define OR_ 8
#define AND_ 12
#define FENCE WORD $0x0ff0000f
// Atomically:
// if(*val == *old){
// *val = new;
// return 1;
// } else {
// return 0;
// }
TEXT ·Cas(SB), NOSPLIT, $0-17
MOV ptr+0(FP), A0
MOVW old+8(FP), A1
MOVW new+12(FP), A2
cas_again:
AMOWSC(LR_,13,10,0) // lr.w.aq a3,(a0)
BNE A3, A1, cas_fail
AMOWSC(SC_,14,10,12) // sc.w.aq a4,a2,(a0)
BNE A4, ZERO, cas_again
MOV $1, A0
MOVB A0, ret+16(FP)
RET
cas_fail:
MOV $0, A0
MOV A0, ret+16(FP)
RET
// func Cas64(ptr *uint64, old, new uint64) bool
TEXT ·Cas64(SB), NOSPLIT, $0-25
MOV ptr+0(FP), A0
MOV old+8(FP), A1
MOV new+16(FP), A2
cas_again:
AMODSC(LR_,13,10,0) // lr.d.aq a3,(a0)
BNE A3, A1, cas_fail
AMODSC(SC_,14,10,12) // sc.d.aq a4,a2,(a0)
BNE A4, ZERO, cas_again
MOV $1, A0
MOVB A0, ret+24(FP)
RET
cas_fail:
MOVB ZERO, ret+24(FP)
RET
// func Load(ptr *uint32) uint32
TEXT ·Load(SB),NOSPLIT|NOFRAME,$0-12
MOV ptr+0(FP), A0
AMOWSC(LR_,10,10,0)
MOVW A0, ret+8(FP)
RET
// func Load8(ptr *uint8) uint8
TEXT ·Load8(SB),NOSPLIT|NOFRAME,$0-9
MOV ptr+0(FP), A0
FENCE
MOVBU (A0), A1
FENCE
MOVB A1, ret+8(FP)
RET
// func Load64(ptr *uint64) uint64
TEXT ·Load64(SB),NOSPLIT|NOFRAME,$0-16
MOV ptr+0(FP), A0
AMODSC(LR_,10,10,0)
MOV A0, ret+8(FP)
RET
// func Store(ptr *uint32, val uint32)
TEXT ·Store(SB), NOSPLIT, $0-12
MOV ptr+0(FP), A0
MOVW val+8(FP), A1
AMOWSC(SWAP_,0,10,11)
RET
// func Store8(ptr *uint8, val uint8)
TEXT ·Store8(SB), NOSPLIT, $0-9
MOV ptr+0(FP), A0
MOVBU val+8(FP), A1
FENCE
MOVB A1, (A0)
FENCE
RET
// func Store64(ptr *uint64, val uint64)
TEXT ·Store64(SB), NOSPLIT, $0-16
MOV ptr+0(FP), A0
MOV val+8(FP), A1
AMODSC(SWAP_,0,10,11)
RET
TEXT ·Casp1(SB), NOSPLIT, $0-25
JMP ·Cas64(SB)
TEXT ·Casuintptr(SB),NOSPLIT,$0-25
JMP ·Cas64(SB)
TEXT ·CasRel(SB), NOSPLIT, $0-17
JMP ·Cas(SB)
TEXT ·Loaduintptr(SB),NOSPLIT,$0-16
JMP ·Load64(SB)
TEXT ·Storeuintptr(SB),NOSPLIT,$0-16
JMP ·Store64(SB)
TEXT ·Loaduint(SB),NOSPLIT,$0-16
JMP ·Loaduintptr(SB)
TEXT ·Loadint64(SB),NOSPLIT,$0-16
JMP ·Loaduintptr(SB)
TEXT ·Xaddint64(SB),NOSPLIT,$0-24
MOV ptr+0(FP), A0
MOV delta+8(FP), A1
WORD $0x04b5352f // amoadd.d.aq a0,a1,(a0)
ADD A0, A1, A0
MOVW A0, ret+16(FP)
RET
TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$0-12
JMP ·Load(SB)
// func Loadp(ptr unsafe.Pointer) unsafe.Pointer
TEXT ·Loadp(SB),NOSPLIT,$0-16
JMP ·Load64(SB)
// func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
TEXT ·StorepNoWB(SB), NOSPLIT, $0-16
JMP ·Store64(SB)
TEXT ·StoreRel(SB), NOSPLIT, $0-12
JMP ·Store(SB)
// func Xchg(ptr *uint32, new uint32) uint32
TEXT ·Xchg(SB), NOSPLIT, $0-20
MOV ptr+0(FP), A0
MOVW new+8(FP), A1
AMOWSC(SWAP_,11,10,11)
MOVW A1, ret+16(FP)
RET
// func Xchg64(ptr *uint64, new uint64) uint64
TEXT ·Xchg64(SB), NOSPLIT, $0-24
MOV ptr+0(FP), A0
MOV new+8(FP), A1
AMODSC(SWAP_,11,10,11)
MOV A1, ret+16(FP)
RET
// Atomically:
// *val += delta;
// return *val;
// func Xadd(ptr *uint32, delta int32) uint32
TEXT ·Xadd(SB), NOSPLIT, $0-20
MOV ptr+0(FP), A0
MOVW delta+8(FP), A1
AMOWSC(ADD_,12,10,11)
ADD A2,A1,A0
MOVW A0, ret+16(FP)
RET
// func Xadd64(ptr *uint64, delta int64) uint64
TEXT ·Xadd64(SB), NOSPLIT, $0-24
MOV ptr+0(FP), A0
MOV delta+8(FP), A1
AMODSC(ADD_,12,10,11)
ADD A2,A1,A0
MOV A0, ret+16(FP)
RET
// func Xadduintptr(ptr *uintptr, delta uintptr) uintptr
TEXT ·Xadduintptr(SB), NOSPLIT, $0-24
JMP ·Xadd64(SB)
// func Xchguintptr(ptr *uintptr, new uintptr) uintptr
TEXT ·Xchguintptr(SB), NOSPLIT, $0-24
JMP ·Xchg64(SB)
// func And8(ptr *uint8, val uint8)
TEXT ·And8(SB), NOSPLIT, $0-9
MOV ptr+0(FP), A0
MOVBU val+8(FP), A1
AND $3, A0, A2
AND $-4, A0
SLL $3, A2
XOR $255, A1
SLL A2, A1
XOR $-1, A1
AMOWSC(AND_,0,10,11)
RET
// func Or8(ptr *uint8, val uint8)
TEXT ·Or8(SB), NOSPLIT, $0-9
MOV ptr+0(FP), A0
MOVBU val+8(FP), A1
AND $3, A0, A2
AND $-4, A0
SLL $3, A2
SLL A2, A1
AMOWSC(OR_,0,10,11)
RET