// 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, swapped+12(FP) RET casfail: MOVW $0, R0 MOVBU R0, swapped+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 old_lo+4(FP), R2 MOVW old_hi+8(FP), R3 MOVW new_lo+12(FP), R4 MOVW new_hi+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, swapped+20(FP) RET cas64fail: MOVW $0, R0 MOVBU R0, swapped+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, new+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 delta_lo+4(FP), R2 MOVW delta_hi+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, new_lo+12(FP) MOVW R5, new_hi+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 new_lo+4(FP), R2 MOVW new_hi+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, old_lo+12(FP) MOVW R5, old_hi+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, val_lo+4(FP) MOVW R3, val_hi+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 val_lo+4(FP), R2 MOVW val_hi+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