/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_S_ #define ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_S_ #include "asm_support_arm.h" #include "interpreter/cfi_asm_support.h" // Define special registers. // Register holding suspend check count down. #define rSUSPEND r4 // Register holding Thread::Current(). #define rSELF r9 #ifdef RESERVE_MARKING_REGISTER // Marking Register, holding Thread::Current()->GetIsGcMarking(). #define rMR r8 #endif .syntax unified .arch armv7-a .arch_extension idiv .thumb .macro CFI_EXPRESSION_BREG n, b, offset .if (-0x40 <= (\offset)) && ((\offset) < 0x40) CFI_EXPRESSION_BREG_1(\n, \b, \offset) .elseif (-0x2000 <= (\offset)) && ((\offset) < 0x2000) CFI_EXPRESSION_BREG_2(\n, \b, \offset) .else .error "Unsupported offset" .endif .endm .macro CFI_DEF_CFA_BREG_PLUS_UCONST reg, offset, size .if ((\size) < 0) .error "Size should be positive" .endif .if (((\offset) < -0x40) || ((\offset) >= 0x40)) .error "Unsupported offset" .endif .if ((\size) < 0x80) CFI_DEF_CFA_BREG_PLUS_UCONST_1_1(\reg, \offset, \size) .elseif ((\size) < 0x4000) CFI_DEF_CFA_BREG_PLUS_UCONST_1_2(\reg, \offset, \size) .else .error "Unsupported size" .endif .endm .macro CFI_REMEMBER_STATE .cfi_remember_state .endm // The spec is not clear whether the CFA is part of the saved state and tools // differ in the behaviour, so explicitly set the CFA to avoid any ambiguity. // The restored CFA state should match the CFA state during CFI_REMEMBER_STATE. .macro CFI_RESTORE_STATE_AND_DEF_CFA reg, offset .cfi_restore_state .cfi_def_cfa \reg, \offset .endm // Common ENTRY declaration code for ARM and thumb, an ENTRY should always be paired with an END. .macro DEF_ENTRY thumb_or_arm, name, alignment \thumb_or_arm // Clang ignores .thumb_func and requires an explicit .thumb. Investigate whether we should still // carry around the .thumb_func. .ifc \thumb_or_arm, .thumb_func .thumb .endif .type \name, #function .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name .balign \alignment \name: .cfi_startproc .fnstart .endm // A thumb2 style ENTRY. .macro ENTRY name DEF_ENTRY .thumb_func, \name, 16 .endm .macro ENTRY_ALIGNED name, alignment DEF_ENTRY .thumb_func, \name, \alignment .endm // A ARM style ENTRY. .macro ARM_ENTRY name DEF_ENTRY .arm, \name, 16 .endm // Terminate an ENTRY. .macro END name .fnend .cfi_endproc .size \name, .-\name .endm // Declare an unimplemented ENTRY that will halt a debugger. .macro UNIMPLEMENTED name ENTRY \name bkpt bkpt END \name .endm // Macro to poison (negate) the reference for heap poisoning. .macro POISON_HEAP_REF rRef #ifdef USE_HEAP_POISONING rsb \rRef, \rRef, #0 #endif // USE_HEAP_POISONING .endm // Macro to unpoison (negate) the reference for heap poisoning. .macro UNPOISON_HEAP_REF rRef #ifdef USE_HEAP_POISONING rsb \rRef, \rRef, #0 #endif // USE_HEAP_POISONING .endm .macro INCREASE_FRAME frame_adjustment sub sp, sp, #(\frame_adjustment) .cfi_adjust_cfa_offset (\frame_adjustment) .endm .macro DECREASE_FRAME frame_adjustment add sp, sp, #(\frame_adjustment) .cfi_adjust_cfa_offset -(\frame_adjustment) .endm .macro LOAD_RUNTIME_INSTANCE rDest movw \rDest, #:lower16:(_ZN3art7Runtime9instance_E - (. + 12)) movt \rDest, #:upper16:(_ZN3art7Runtime9instance_E - (. + 8)) add \rDest, pc ldr \rDest, [\rDest] .endm // Macro to refresh the Marking Register (R8). // // This macro must be called at the end of functions implementing // entrypoints that possibly (directly or indirectly) perform a // suspend check (before they return). .macro REFRESH_MARKING_REGISTER #ifdef RESERVE_MARKING_REGISTER ldr rMR, [rSELF, #THREAD_IS_GC_MARKING_OFFSET] #endif .endm .macro CONDITIONAL_CBZ reg, reg_if, dest .ifc \reg, \reg_if cbz \reg, \dest .endif .endm .macro CONDITIONAL_CMPBZ reg, reg_if, dest .ifc \reg, \reg_if cmp \reg, #0 beq \dest .endif .endm // Use CBZ if the register is in {r0, r7} otherwise compare and branch. .macro SMART_CBZ reg, dest CONDITIONAL_CBZ \reg, r0, \dest CONDITIONAL_CBZ \reg, r1, \dest CONDITIONAL_CBZ \reg, r2, \dest CONDITIONAL_CBZ \reg, r3, \dest CONDITIONAL_CBZ \reg, r4, \dest CONDITIONAL_CBZ \reg, r5, \dest CONDITIONAL_CBZ \reg, r6, \dest CONDITIONAL_CBZ \reg, r7, \dest CONDITIONAL_CMPBZ \reg, r8, \dest CONDITIONAL_CMPBZ \reg, r9, \dest CONDITIONAL_CMPBZ \reg, r10, \dest CONDITIONAL_CMPBZ \reg, r11, \dest CONDITIONAL_CMPBZ \reg, r12, \dest CONDITIONAL_CMPBZ \reg, r13, \dest CONDITIONAL_CMPBZ \reg, r14, \dest CONDITIONAL_CMPBZ \reg, r15, \dest .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs), except for storing the method. */ .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY // Note: We could avoid saving R8 in the case of Baker read // barriers, as it is overwritten by REFRESH_MARKING_REGISTER // later; but it's not worth handling this special case. push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves and args. .cfi_adjust_cfa_offset 40 .cfi_rel_offset r5, 12 .cfi_rel_offset r6, 16 .cfi_rel_offset r7, 20 .cfi_rel_offset r8, 24 .cfi_rel_offset r10, 28 .cfi_rel_offset r11, 32 .cfi_rel_offset lr, 36 vpush {s0-s15} @ 16 words of float args. .cfi_adjust_cfa_offset 64 sub sp, #8 @ 2 words of space, alignment padding and Method* .cfi_adjust_cfa_offset 8 // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 40 + 64 + 8) #error "FRAME_SIZE_SAVE_REFS_AND_ARGS(ARM) size not as expected." #endif .endm .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME add sp, #8 @ rewind sp .cfi_adjust_cfa_offset -8 vpop {s0-s15} .cfi_adjust_cfa_offset -64 // Note: Likewise, we could avoid restoring R8 in the case of Baker // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER // later; but it's not worth handling this special case. pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves and args. .cfi_restore r5 .cfi_restore r6 .cfi_restore r7 .cfi_restore r8 .cfi_restore r10 .cfi_restore r11 .cfi_restore lr .cfi_adjust_cfa_offset -40 .endm /* * Macro to spill the GPRs. */ .macro SPILL_ALL_CALLEE_SAVE_GPRS push {r4-r11, lr} @ 9 words (36 bytes) of callee saves. .cfi_adjust_cfa_offset 36 .cfi_rel_offset r4, 0 .cfi_rel_offset r5, 4 .cfi_rel_offset r6, 8 .cfi_rel_offset r7, 12 .cfi_rel_offset r8, 16 .cfi_rel_offset r9, 20 .cfi_rel_offset r10, 24 .cfi_rel_offset r11, 28 .cfi_rel_offset lr, 32 .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves) */ .macro SETUP_SAVE_ALL_CALLEE_SAVES_FRAME rTemp SPILL_ALL_CALLEE_SAVE_GPRS @ 9 words (36 bytes) of callee saves. vpush {s16-s31} @ 16 words (64 bytes) of floats. .cfi_adjust_cfa_offset 64 sub sp, #12 @ 3 words of space, bottom word will hold Method* .cfi_adjust_cfa_offset 12 LOAD_RUNTIME_INSTANCE \rTemp @ Load Runtime::Current into rTemp. @ Load kSaveAllCalleeSaves Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 36 + 64 + 12) #error "FRAME_SIZE_SAVE_ALL_CALLEE_SAVES(ARM) size not as expected." #endif .endm /* * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending * exception is Thread::Current()->exception_ when the runtime method frame is ready. */ .macro DELIVER_PENDING_EXCEPTION_FRAME_READY mov r0, rSELF @ pass Thread::Current bl artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*) bl art_quick_do_long_jump @ (Context*) bkpt // Unreached .endm /* * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending * exception is Thread::Current()->exception_. */ .macro DELIVER_PENDING_EXCEPTION SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0 @ save callee saves for throw DELIVER_PENDING_EXCEPTION_FRAME_READY .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg ldr \reg, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field. cbnz \reg, 1f bx lr 1: DELIVER_PENDING_EXCEPTION .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field. cmp ip, #0 bne 1f bx lr 1: DELIVER_PENDING_EXCEPTION .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveRefsOnly). */ .macro SETUP_SAVE_REFS_ONLY_FRAME rTemp // Note: We could avoid saving R8 in the case of Baker read // barriers, as it is overwritten by REFRESH_MARKING_REGISTER // later; but it's not worth handling this special case. push {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_adjust_cfa_offset 28 .cfi_rel_offset r5, 0 .cfi_rel_offset r6, 4 .cfi_rel_offset r7, 8 .cfi_rel_offset r8, 12 .cfi_rel_offset r10, 16 .cfi_rel_offset r11, 20 .cfi_rel_offset lr, 24 sub sp, #4 @ bottom word will hold Method* .cfi_adjust_cfa_offset 4 LOAD_RUNTIME_INSTANCE \rTemp @ Load Runtime::Current into rTemp. @ Load kSaveRefsOnly Method* into rTemp. ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_ONLY != 28 + 4) #error "FRAME_SIZE_SAVE_REFS_ONLY(ARM) size not as expected." #endif .endm .macro RESTORE_SAVE_REFS_ONLY_FRAME add sp, #4 @ bottom word holds Method* .cfi_adjust_cfa_offset -4 // Note: Likewise, we could avoid restoring R8 in the case of Baker // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER // later; but it's not worth handling this special case. pop {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_restore r5 .cfi_restore r6 .cfi_restore r7 .cfi_restore r8 .cfi_restore r10 .cfi_restore r11 .cfi_restore lr .cfi_adjust_cfa_offset -28 .endm // Locking is needed for both managed code and JNI stubs. .macro LOCK_OBJECT_FAST_PATH obj, tmp1, tmp2, tmp3, slow_lock, can_be_null ldr \tmp1, [rSELF, #THREAD_ID_OFFSET] .if \can_be_null cbz \obj, \slow_lock .endif 1: ldrex \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] eor \tmp3, \tmp2, \tmp1 @ Prepare the value to store if unlocked @ (thread id, count of 0 and preserved read barrier bits), @ or prepare to compare thread id for recursive lock check @ (lock_word.ThreadId() ^ self->ThreadId()). ands ip, \tmp2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. bne 2f @ Check if unlocked. @ unlocked case - store tmp3: original lock word plus thread id, preserved read barrier bits. strex \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz \tmp2, 3f @ If store failed, retry. dmb ish @ Full (LoadLoad|LoadStore) memory barrier. bx lr 2: @ tmp2: original lock word, tmp1: thread_id, tmp3: tmp2 ^ tmp1 #if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT #error "Expecting thin lock count and gc state in consecutive bits." #endif @ Check lock word state and thread id together. bfc \tmp3, \ #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, \ #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) cbnz \tmp3, \slow_lock @ if either of the top two bits are set, or the lock word's @ thread id did not match, go slow path. add \tmp3, \tmp2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Increment the recursive lock count. @ Extract the new thin lock count for overflow check. ubfx \tmp2, \tmp3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #LOCK_WORD_THIN_LOCK_COUNT_SIZE cbz \tmp2, \slow_lock @ Zero as the new count indicates overflow, go slow path. @ strex necessary for read barrier bits. strex \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz \tmp2, 3f @ If strex failed, retry. bx lr 3: b 1b @ retry .endm // Unlocking is needed for both managed code and JNI stubs. .macro UNLOCK_OBJECT_FAST_PATH obj, tmp1, tmp2, tmp3, slow_unlock, can_be_null ldr \tmp1, [rSELF, #THREAD_ID_OFFSET] .if \can_be_null cbz \obj, \slow_unlock .endif 1: #ifndef USE_READ_BARRIER ldr \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else @ Need to use atomic instructions for read barrier. ldrex \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #endif eor \tmp3, \tmp2, \tmp1 @ Prepare the value to store if simply locked @ (mostly 0s, and preserved read barrier bits), @ or prepare to compare thread id for recursive lock check @ (lock_word.ThreadId() ^ self->ThreadId()). ands ip, \tmp3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED @ Test the non-gc bits. bne 2f @ Locked recursively or by other thread? @ Transition to unlocked. dmb ish @ Full (LoadStore|StoreStore) memory barrier. #ifndef USE_READ_BARRIER str \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else @ strex necessary for read barrier bits strex \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz \tmp2, 3f @ If the store failed, retry. #endif bx lr 2: @ tmp2: original lock word, tmp1: thread_id, tmp3: tmp2 ^ tmp1 #if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT #error "Expecting thin lock count and gc state in consecutive bits." #endif @ Check lock word state and thread id together, bfc \tmp3, \ #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, \ #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE) cbnz \tmp3, \slow_unlock @ if either of the top two bits are set, or the lock word's @ thread id did not match, go slow path. sub \tmp3, \tmp2, #LOCK_WORD_THIN_LOCK_COUNT_ONE @ Decrement recursive lock count. #ifndef USE_READ_BARRIER str \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] #else @ strex necessary for read barrier bits. strex \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz \tmp2, 3f @ If the store failed, retry. #endif bx lr 3: b 1b @ retry .endm #endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_