/* * Copyright (C) 2014 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_ARM64_ASM_SUPPORT_ARM64_S_ #define ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_ #include "asm_support_arm64.h" #include "interpreter/cfi_asm_support.h" // Define special registers. // Register holding Thread::Current(). #define xSELF x19 // Frame Pointer #define xFP x29 // Link Register #define xLR x30 // Define the intraprocedural linkage temporary registers. #define xIP0 x16 #define wIP0 w16 #define xIP1 x17 #define wIP1 w17 #if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) // Marking Register, holding Thread::Current()->GetIsGcMarking(). // Only used with the Concurrent Copying (CC) garbage // collector, with the Baker read barrier configuration. #define wMR w20 #endif .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 ENTRY_ALIGNED name, alignment .type \name, #function .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name // ART-compiled functions have OatQuickMethodHeader but assembly funtions do not. // Prefix the assembly code with 0xFFs, which means there is no method header. .byte 0xFF, 0xFF, 0xFF, 0xFF // Cache alignment for function entry. // NB: 0xFF because there is a bug in balign where 0x00 creates nop instructions. .balign \alignment, 0xFF \name: .cfi_startproc .endm .macro ENTRY name ENTRY_ALIGNED \name, 16 .endm .macro END name .cfi_endproc .size \name, .-\name .endm .macro UNIMPLEMENTED name ENTRY \name brk 0 END \name .endm // Macro to poison (negate) the reference for heap poisoning. .macro POISON_HEAP_REF rRef #ifdef USE_HEAP_POISONING neg \rRef, \rRef #endif // USE_HEAP_POISONING .endm // Macro to unpoison (negate) the reference for heap poisoning. .macro UNPOISON_HEAP_REF rRef #ifdef USE_HEAP_POISONING neg \rRef, \rRef #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 SAVE_REG reg, offset str \reg, [sp, #(\offset)] .cfi_rel_offset \reg, (\offset) .endm .macro RESTORE_REG_BASE base, reg, offset ldr \reg, [\base, #(\offset)] .cfi_restore \reg .endm .macro RESTORE_REG reg, offset RESTORE_REG_BASE sp, \reg, \offset .endm .macro SAVE_TWO_REGS_BASE base, reg1, reg2, offset stp \reg1, \reg2, [\base, #(\offset)] .cfi_rel_offset \reg1, (\offset) .cfi_rel_offset \reg2, (\offset) + 8 .endm .macro SAVE_TWO_REGS reg1, reg2, offset SAVE_TWO_REGS_BASE sp, \reg1, \reg2, \offset .endm .macro RESTORE_TWO_REGS_BASE base, reg1, reg2, offset ldp \reg1, \reg2, [\base, #(\offset)] .cfi_restore \reg1 .cfi_restore \reg2 .endm .macro RESTORE_TWO_REGS reg1, reg2, offset RESTORE_TWO_REGS_BASE sp, \reg1, \reg2, \offset .endm .macro LOAD_RUNTIME_INSTANCE reg #if __has_feature(hwaddress_sanitizer) && __clang_major__ >= 10 adrp \reg, :pg_hi21_nc:_ZN3art7Runtime9instance_E #else adrp \reg, _ZN3art7Runtime9instance_E #endif ldr \reg, [\reg, #:lo12:_ZN3art7Runtime9instance_E] .endm // Macro to refresh the Marking Register (W20). // // 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 #if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER) ldr wMR, [xSELF, #THREAD_IS_GC_MARKING_OFFSET] #endif .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveRefsOnly). */ .macro SETUP_SAVE_REFS_ONLY_FRAME // art::Runtime* xIP0 = art::Runtime::instance_; // Our registers aren't intermixed - just spill in order. LOAD_RUNTIME_INSTANCE xIP0 // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveRefOnly]; ldr xIP0, [xIP0, RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET] INCREASE_FRAME 96 // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_ONLY != 96) #error "FRAME_SIZE_SAVE_REFS_ONLY(ARM64) size not as expected." #endif // GP callee-saves. // x20 paired with ArtMethod* - see below. SAVE_TWO_REGS x21, x22, 16 SAVE_TWO_REGS x23, x24, 32 SAVE_TWO_REGS x25, x26, 48 SAVE_TWO_REGS x27, x28, 64 SAVE_TWO_REGS x29, xLR, 80 // Store ArtMethod* Runtime::callee_save_methods_[kSaveRefsOnly]. // Note: We could avoid saving X20 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. stp xIP0, x20, [sp] .cfi_rel_offset x20, 8 // Place sp in Thread::Current()->top_quick_frame. mov xIP0, sp str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] .endm // TODO: Probably no need to restore registers preserved by aapcs64. .macro RESTORE_SAVE_REFS_ONLY_FRAME // Callee-saves. // Note: Likewise, we could avoid restoring X20 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. RESTORE_REG x20, 8 RESTORE_TWO_REGS x21, x22, 16 RESTORE_TWO_REGS x23, x24, 32 RESTORE_TWO_REGS x25, x26, 48 RESTORE_TWO_REGS x27, x28, 64 RESTORE_TWO_REGS x29, xLR, 80 DECREASE_FRAME 96 .endm .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL base // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 224) #error "FRAME_SIZE_SAVE_REFS_AND_ARGS(ARM64) size not as expected." #endif // Stack alignment filler [\base, #8]. // FP args. stp d0, d1, [\base, #16] stp d2, d3, [\base, #32] stp d4, d5, [\base, #48] stp d6, d7, [\base, #64] // Core args. SAVE_TWO_REGS_BASE \base, x1, x2, 80 SAVE_TWO_REGS_BASE \base, x3, x4, 96 SAVE_TWO_REGS_BASE \base, x5, x6, 112 // x7, Callee-saves. // Note: We could avoid saving X20 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. SAVE_TWO_REGS_BASE \base, x7, x20, 128 SAVE_TWO_REGS_BASE \base, x21, x22, 144 SAVE_TWO_REGS_BASE \base, x23, x24, 160 SAVE_TWO_REGS_BASE \base, x25, x26, 176 SAVE_TWO_REGS_BASE \base, x27, x28, 192 // x29(callee-save) and LR. SAVE_TWO_REGS_BASE \base, x29, xLR, 208 .endm // TODO: Probably no need to restore registers preserved by aapcs64. (That would require // auditing all users to make sure they restore aapcs64 callee-save registers they clobber.) .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_INTERNAL base // FP args. ldp d0, d1, [\base, #16] ldp d2, d3, [\base, #32] ldp d4, d5, [\base, #48] ldp d6, d7, [\base, #64] // Core args. RESTORE_TWO_REGS_BASE \base, x1, x2, 80 RESTORE_TWO_REGS_BASE \base, x3, x4, 96 RESTORE_TWO_REGS_BASE \base, x5, x6, 112 // x7, Callee-saves. // Note: Likewise, we could avoid restoring X20 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. RESTORE_TWO_REGS_BASE \base, x7, x20, 128 RESTORE_TWO_REGS_BASE \base, x21, x22, 144 RESTORE_TWO_REGS_BASE \base, x23, x24, 160 RESTORE_TWO_REGS_BASE \base, x25, x26, 176 RESTORE_TWO_REGS_BASE \base, x27, x28, 192 // x29(callee-save) and LR. RESTORE_TWO_REGS_BASE \base, x29, xLR, 208 .endm .macro RESTORE_SAVE_REFS_AND_ARGS_FRAME RESTORE_SAVE_REFS_AND_ARGS_FRAME_INTERNAL sp DECREASE_FRAME FRAME_SIZE_SAVE_REFS_AND_ARGS .endm .macro SAVE_ALL_CALLEE_SAVES offset // FP callee-saves. stp d8, d9, [sp, #(0 + \offset)] stp d10, d11, [sp, #(16 + \offset)] stp d12, d13, [sp, #(32 + \offset)] stp d14, d15, [sp, #(48 + \offset)] // GP callee-saves SAVE_TWO_REGS x19, x20, (64 + \offset) SAVE_TWO_REGS x21, x22, (80 + \offset) SAVE_TWO_REGS x23, x24, (96 + \offset) SAVE_TWO_REGS x25, x26, (112 + \offset) SAVE_TWO_REGS x27, x28, (128 + \offset) SAVE_TWO_REGS x29, xLR, (144 + \offset) .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves) */ .macro SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // art::Runtime* xIP0 = art::Runtime::instance_; // Our registers aren't intermixed - just spill in order. LOAD_RUNTIME_INSTANCE xIP0 // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveAllCalleeSaves]; ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET] INCREASE_FRAME 176 // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 176) #error "FRAME_SIZE_SAVE_ALL_CALLEE_SAVES(ARM64) size not as expected." #endif // Stack alignment filler [sp, #8]. SAVE_ALL_CALLEE_SAVES 16 // Store ArtMethod* Runtime::callee_save_methods_[kSaveAllCalleeSaves]. str xIP0, [sp] // Place sp in Thread::Current()->top_quick_frame. mov xIP0, sp str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] .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 x0, xSELF // Point of no return. bl artDeliverPendingExceptionFromCode // artDeliverPendingExceptionFromCode(Thread*) brk 0 // 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 DELIVER_PENDING_EXCEPTION_FRAME_READY .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg ldr \reg, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field. cbnz \reg, 1f ret 1: DELIVER_PENDING_EXCEPTION .endm .macro RETURN_OR_DELIVER_PENDING_EXCEPTION RETURN_OR_DELIVER_PENDING_EXCEPTION_REG xIP0 .endm #endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_