blob: d35ea2a4e0b40059e2172b36fce5c062ba2885ba [file] [log] [blame]
// Copyright 2011 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.
// +build !race
#include "textflag.h"
// ARM atomic operations, for use by asm_$(GOOS)_arm.s.
#define DMB_ISHST_7 \
MOVB runtime·goarm(SB), R11; \
CMP $7, R11; \
BLT 2(PC); \
WORD $0xf57ff05a // dmb ishst
#define DMB_ISH_7 \
MOVB runtime·goarm(SB), R11; \
CMP $7, R11; \
BLT 2(PC); \
WORD $0xf57ff05b // dmb ish
TEXT ·armCompareAndSwapUint32(SB),NOSPLIT,$0-13
MOVW addr+0(FP), R1
MOVW old+4(FP), R2
MOVW new+8(FP), R3
casloop:
// LDREX and STREX were introduced in ARMv6.
LDREX (R1), R0
CMP R0, R2
BNE casfail
DMB_ISHST_7
STREX R3, (R1), R0
CMP $0, R0
BNE casloop
MOVW $1, R0
DMB_ISH_7
MOVBU R0, ret+12(FP)
RET
casfail:
MOVW $0, R0
MOVBU R0, ret+12(FP)
RET
TEXT ·armCompareAndSwapUint64(SB),NOSPLIT,$0-21
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
AND.S $7, R1, R2
BEQ 2(PC)
MOVW R2, (R2)
MOVW oldlo+4(FP), R2
MOVW oldhi+8(FP), R3
MOVW newlo+12(FP), R4
MOVW newhi+16(FP), R5
cas64loop:
// LDREXD and STREXD were introduced in ARMv6k.
LDREXD (R1), R6 // loads R6 and R7
CMP R2, R6
BNE cas64fail
CMP R3, R7
BNE cas64fail
DMB_ISHST_7
STREXD R4, (R1), R0 // stores R4 and R5
CMP $0, R0
BNE cas64loop
MOVW $1, R0
DMB_ISH_7
MOVBU R0, ret+20(FP)
RET
cas64fail:
MOVW $0, R0
MOVBU R0, ret+20(FP)
RET
TEXT ·armAddUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R1
MOVW delta+4(FP), R2
addloop:
// LDREX and STREX were introduced in ARMv6.
LDREX (R1), R3
ADD R2, R3
DMB_ISHST_7
STREX R3, (R1), R0
CMP $0, R0
BNE addloop
DMB_ISH_7
MOVW R3, ret+8(FP)
RET
TEXT ·armAddUint64(SB),NOSPLIT,$0-20
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
AND.S $7, R1, R2
BEQ 2(PC)
MOVW R2, (R2)
MOVW deltalo+4(FP), R2
MOVW deltahi+8(FP), R3
add64loop:
// LDREXD and STREXD were introduced in ARMv6k.
LDREXD (R1), R4 // loads R4 and R5
ADD.S R2, R4
ADC R3, R5
DMB_ISHST_7
STREXD R4, (R1), R0 // stores R4 and R5
CMP $0, R0
BNE add64loop
DMB_ISH_7
MOVW R4, retlo+12(FP)
MOVW R5, rethi+16(FP)
RET
TEXT ·armSwapUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R1
MOVW new+4(FP), R2
swaploop:
// LDREX and STREX were introduced in ARMv6.
LDREX (R1), R3
DMB_ISHST_7
STREX R2, (R1), R0
CMP $0, R0
BNE swaploop
DMB_ISH_7
MOVW R3, old+8(FP)
RET
TEXT ·armSwapUint64(SB),NOSPLIT,$0-20
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
AND.S $7, R1, R2
BEQ 2(PC)
MOVW R2, (R2)
MOVW newlo+4(FP), R2
MOVW newhi+8(FP), R3
swap64loop:
// LDREXD and STREXD were introduced in ARMv6k.
LDREXD (R1), R4 // loads R4 and R5
DMB_ISHST_7
STREXD R2, (R1), R0 // stores R2 and R3
CMP $0, R0
BNE swap64loop
DMB_ISH_7
MOVW R4, oldlo+12(FP)
MOVW R5, oldhi+16(FP)
RET
TEXT ·armLoadUint64(SB),NOSPLIT,$0-12
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
AND.S $7, R1, R2
BEQ 2(PC)
MOVW R2, (R2)
load64loop:
LDREXD (R1), R2 // loads R2 and R3
DMB_ISHST_7
STREXD R2, (R1), R0 // stores R2 and R3
CMP $0, R0
BNE load64loop
DMB_ISH_7
MOVW R2, vallo+4(FP)
MOVW R3, valhi+8(FP)
RET
TEXT ·armStoreUint64(SB),NOSPLIT,$0-12
BL fastCheck64<>(SB)
MOVW addr+0(FP), R1
// make unaligned atomic access panic
AND.S $7, R1, R2
BEQ 2(PC)
MOVW R2, (R2)
MOVW vallo+4(FP), R2
MOVW valhi+8(FP), R3
store64loop:
LDREXD (R1), R4 // loads R4 and R5
DMB_ISHST_7
STREXD R2, (R1), R0 // stores R2 and R3
CMP $0, R0
BNE store64loop
DMB_ISH_7
RET
// Check for broken 64-bit LDREXD as found in QEMU.
// LDREXD followed by immediate STREXD should succeed.
// If it fails, try a few times just to be sure (maybe our thread got
// rescheduled between the two instructions) and then panic.
// A bug in some copies of QEMU makes STREXD never succeed,
// which will make uses of the 64-bit atomic operations loop forever.
// If things are working, set okLDREXD to avoid future checks.
// https://bugs.launchpad.net/qemu/+bug/670883.
TEXT check64<>(SB),NOSPLIT,$16-0
MOVW $10, R1
// 8-aligned stack address scratch space.
MOVW $8(R13), R5
AND $~7, R5
loop:
LDREXD (R5), R2
STREXD R2, (R5), R0
CMP $0, R0
BEQ ok
SUB $1, R1
CMP $0, R1
BNE loop
// Must be buggy QEMU.
BL ·panic64(SB)
ok:
RET
// Fast, cached version of check. No frame, just MOVW CMP RET after first time.
TEXT fastCheck64<>(SB),NOSPLIT,$-4
MOVW ok64<>(SB), R0
CMP $0, R0 // have we been here before?
RET.NE
B slowCheck64<>(SB)
TEXT slowCheck64<>(SB),NOSPLIT,$0-0
BL check64<>(SB)
// Still here, must be okay.
MOVW $1, R0
MOVW R0, ok64<>(SB)
RET
GLOBL ok64<>(SB), NOPTR, $4