str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kSaveRefsAndArgs].
// Place sp in Thread::Current()->top_quick_frame.
mov xIP0, sp
str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
.endm
.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_X0
INCREASE_FRAME FRAME_SIZE_SAVE_REFS_AND_ARGS
SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL sp
str x0, [sp, #0] // Store ArtMethod* to bottom of stack.
// Place sp in Thread::Current()->top_quick_frame.
mov xIP0, sp
str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
.endm
/* *Macrothatsetsupthecalleesaveframetoconformwith *Runtime::CreateCalleeSaveMethod(kSaveEverything) *whentheSPhasalreadybeendecrementedbyFRAME_SIZE_SAVE_EVERYTHING *andsavingregistersx29andLRishandledelsewhere.
*/
.macro SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR \
runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
// Ugly compile-time check, but we only have the preprocessor.
#if (FRAME_SIZE_SAVE_EVERYTHING != 512)
#error "FRAME_SIZE_SAVE_EVERYTHING(ARM64) size not as expected."
#endif
/* *CallinstalledbyasignalhandlertocreateanddeliveraNullPointerException.
*/
.extern art_quick_throw_null_pointer_exception_from_signal
ENTRY art_quick_throw_null_pointer_exception_from_signal
// The fault handler pushes the gc map address, i.e. "return address", to stack
// and passes the fault address in LR. So we need to set up the CFI info accordingly.
.cfi_def_cfa_offset __SIZEOF_POINTER__
.cfi_rel_offset lr, 0
// Save all registers as basis for long jump context.
INCREASE_FRAME (FRAME_SIZE_SAVE_EVERYTHING - __SIZEOF_POINTER__)
SAVE_REG x29, (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__) // LR already saved.
SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR
mov x0, lr // pass the fault address stored in LR by the fault handler.
mov x1, xSELF // pass Thread::Current.
CALL_SYMBOL artThrowNullPointerExceptionFromSignal // (arg, Thread*).
CALL_SYMBOL art_quick_do_long_jump // (Context*)
brk 0 // Unreached
END art_quick_throw_null_pointer_exception_from_signal
mov xFP, sp // Use xFP for frame pointer, as it's callee-saved.
.cfi_def_cfa_register xFP
add x10, x2, #(__SIZEOF_POINTER__ + 0xf) // Reserve space for ArtMethod*, arguments and
and x10, x10, # ~0xf // round up for 16-byte stack alignment. sub sp, sp, x10 // Adjust SP for ArtMethod*, args and alignment padding.
mov xSELF, x3 // Move thread pointer into SELF register.
// Copy arguments into stack frame.
// Use simple copy routine for now.
// 4 bytes per slot.
// X1 - source address
// W2 - args length
// X9 - destination address.
// W10 - temporary
add x9, sp, #8 // Destination address is bottom of stack + null.
// Copy parameters into the stack. Use numeric label as this is a macro and Clang's assembler
// does not have unique-id variables.
cbz w2, 2f 1: sub w2, w2, #4 // Need 65536 bytes of range.
ldr w10, [x1, x2]
str w10, [x9, x2]
cbnz w2, 1b
2:
// Store null into ArtMethod* at bottom of frame.
str xzr, [sp]
.endm
// Pop the ArtMethod* (null), arguments and alignment padding from the stack.
mov sp, xFP
.cfi_def_cfa_register sp
// Restore saved registers including value address and shorty address.
RESTORE_REG x19, 24
RESTORE_TWO_REGS x20, x21, 32
RESTORE_TWO_REGS xFP, xLR, 48
RESTORE_TWO_REGS_DECREASE_FRAME x4, x5, SAVE_SIZE
// Store result (w0/x0/s0/d0) appropriately, depending on resultType.
ldrb w10, [x5]
// Check the return type and store the correct register into the jvalue in memory.
// Use numeric label as this is a macro and Clang's assembler does not have unique-id variables.
// Don't set anything for a void type.
cmp w10, #'V'
beq 1f
// Is it a double?
cmp w10, #'D'
beq 2f
// Is it a float?
cmp w10, #'F'
beq 3f
// Just store x0. Doesn't matter if it is 64 or 32 bits.
str x0, [x4]
1: // Finish up.
ret
2: // Store double.
str d0, [x4]
ret
3: // Store float.
str s0, [x4]
ret
.endm
// Macro for loading an argument into a register.
// label - the base name of the label of the load routine,
// reg - the register to load,
// args - pointer to current argument, incremented by size,
// size - the size of the register - 4 or 8 bytes,
// nh4_reg - the register to fill with the address of the next handler for 4-byte values,
// nh4_l - the base name of the label of the next handler for 4-byte values,
// nh8_reg - the register to fill with the address of the next handler for 8-byte values,
// nh8_l - the base name of the label of the next handler for 8-byte values,
// cont - the base name of the label for continuing the shorty processing loop,
// suffix - suffix added to all labels to make labels unique for different users.
.macro INVOKE_STUB_LOAD_REG label, reg, args, size, nh4_reg, nh4_l, nh8_reg, nh8_l, cont, suffix
\label\suffix:
ldr \reg, [\args], #\size
adr \nh4_reg, \nh4_l\suffix
adr \nh8_reg, \nh8_l\suffix
b \cont\suffix
.endm
// Macro for skipping an argument that does not fit into argument registers.
// label - the base name of the label of the skip routine,
// args - pointer to current argument, incremented by size,
// size - the size of the argument - 4 or 8 bytes,
// cont - the base name of the label for continuing the shorty processing loop,
// suffix - suffix added to all labels to make labels unique for different users.
.macro INVOKE_STUB_SKIP_ARG label, args, size, cont, suffix
\label\suffix:
add \args, \args, #\size
b \cont\suffix
.endm
// Fill registers x/w1 to x/w7 and s/d0 to s/d7 with parameters.
// Parse the passed shorty to determine which register to load.
// x5 - shorty,
// x9 - points to arguments on the stack,
// suffix - suffix added to all labels to make labels unique for different users.
.macro INVOKE_STUB_LOAD_ALL_ARGS suffix
add x10, x5, #1 // Load shorty address, plus one to skip the return type.
// Load this (if instance method) and addresses for routines that load WXSD registers.
.ifc \suffix, _instance
ldr w1, [x9], #4 // Load "this" parameter, and increment arg pointer.
adr x11, .Lload_w2\suffix
adr x12, .Lload_x2\suffix
.else
adr x11, .Lload_w1\suffix
adr x12, .Lload_x1\suffix
.endif
adr x13, .Lload_s0\suffix
adr x14, .Lload_d0\suffix
// Loop to fill registers.
.Lfill_regs\suffix:
ldrb w17, [x10], #1 // Load next character in signature, and increment.
cbz w17, .Lcall_method\suffix // Exit at end of signature. Shorty 0 terminated.
cmp w17, #'J' // Is this a long?
beq .Lload_long\suffix
cmp w17, #'F' // Is this a float?
beq .Lload_float\suffix
cmp w17, #'D' // Is this a double?
beq .Lload_double\suffix
// Handlers for skipping arguments that do not fit into registers.
INVOKE_STUB_SKIP_ARG .Lskip4, x9, 4, .Lfill_regs, \suffix
INVOKE_STUB_SKIP_ARG .Lskip8, x9, 8, .Lfill_regs, \suffix
/* *Entryfrommanagedcodethatcalls`artLockObjectFromCode()`,mayblockforGC. *x0holdsthepossiblynullobjecttolock.
*/
.extern artLockObjectFromCode
ENTRY art_quick_lock_object_no_inline
// This is also the slow path for art_quick_lock_object.
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case we block
mov x1, xSELF // pass Thread::Current
CALL_SYMBOL artLockObjectFromCode // (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_INT_RESULT_IS_ZERO_OR_DELIVER
END art_quick_lock_object_no_inline
/* *Entryfrommanagedcodethatcalls`artUnlockObjectFromCode()` *anddeliversexceptiononfailure. *x0holdsthepossiblynullobjecttounlock.
*/
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object_no_inline
// This is also the slow path for art_quick_unlock_object.
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case exception allocation
// triggers GC
mov x1, xSELF // pass Thread::Current
CALL_SYMBOL artUnlockObjectFromCode // (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_INT_RESULT_IS_ZERO_OR_DELIVER
END art_quick_unlock_object_no_inline
/* *EntryfrommanagedcodethatcallsartInstanceOfFromCodeandonfailurecalls *artThrowClassCastExceptionForObject.
*/
.extern artInstanceOfFromCode
.extern artThrowClassCastExceptionForObject
ENTRY art_quick_check_instance_of
// Type check using the bit string passes null as the target class. In that case just throw.
cbz x1, .Lthrow_class_cast_exception_for_bitstring_check
// Store arguments and link register
// Stack needs to be 16B aligned on calls.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
SAVE_REG xLR, 24
// Check for exception
CFI_REMEMBER_STATE
cbz x0, .Lthrow_class_cast_exception
// Restore and return
// TODO: We do not need to restore X0 and X1 on success. We also do not need
// to record CFI for them as the information is not very useful.
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
ret
.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
mov x2, xSELF // pass Thread::Current
CALL_SYMBOL artThrowClassCastExceptionForObject // (Object*, Class*, Thread*)
CALL_SYMBOL art_quick_do_long_jump // (Context*)
brk 0 // Unreached
END art_quick_check_instance_of
// Restore xReg's value from [sp, #offset] if xReg is not the same as xExclude.
.macro POP_REG_NE xReg, offset, xExclude
.ifnc \xReg, \xExclude
ldr \xReg, [sp, #\offset] // restore xReg
.cfi_restore \xReg
.endif
.endm
// Restore xReg1's value from [sp, #offset] if xReg1 is not the same as xExclude.
// Restore xReg2's value from [sp, #(offset + 8)] if xReg2 is not the same as xExclude.
.macro POP_REGS_NE xReg1, xReg2, offset, xExclude
.ifc \xReg1, \xExclude
ldr \xReg2, [sp, #(\offset + 8)] // restore xReg2
.else
.ifc \xReg2, \xExclude
ldr \xReg1, [sp, #\offset] // restore xReg1
.else
ldp \xReg1, \xReg2, [sp, #\offset] // restore xReg1 and xReg2
.endif
.endif
.cfi_restore \xReg1
.cfi_restore \xReg2
.endm
.macro BAKER_RB_LOAD_AND_MARK wDest, xObj, offset, mark_function
ldr \wDest, [\xObj, #\offset] // Heap reference = 32b; zero-extends to xN.
UNPOISON_HEAP_REF \wDest
// Save LR in a register preserved by `art_quick_read_barrier_mark_regNN`
// and unused by the `art_quick_aput_obj`.
mov x5, lr
CALL_SYMBOL \mark_function
mov lr, x5 // Restore LR.
.endm
#else // USE_BAKER_READ_BARRIER
.extern artReadBarrierSlow
.macro READ_BARRIER_SLOW xDest, wDest, xObj, offset
// Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 48
SAVE_TWO_REGS x2, x3, 16
SAVE_TWO_REGS x4, xLR, 32
// mov x0, \xRef // pass ref in x0 (no-op for now since parameter ref is unused)
.ifnc \xObj, x1
mov x1, \xObj // pass xObj
.endif
mov w2, #\offset // pass offset
CALL_SYMBOL artReadBarrierSlow // artReadBarrierSlow(ref, xObj, offset)
// No need to unpoison return value in w0, artReadBarrierSlow() would do the unpoisoning.
.ifnc \wDest, w0
mov \wDest, w0 // save return value in wDest
.endif
// If isInitialized=1 then the compiler assumes the object's class has already been initialized.
// If isInitialized=0 the compiler can only assume it's been at least resolved.
.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name, isInitialized
ENTRY \c_name
// Fast path rosalloc allocation.
// x0: type, xSELF(x19): Thread::Current
// x1-x7: free.
ldr x3, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local
// allocation stack has room.
// ldp won't work due to large offset.
ldr x4, [xSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
cmp x3, x4
bhs .Lslow_path\c_name
ldr w3, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x3)
cmp x3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE // Check if the size is for a thread
// local allocation.
// If the class is not yet visibly initialized, or it is finalizable,
// the object size will be very large to force the branch below to be taken.
//
// See Class::SetStatus() in class.cc for more details.
bhs .Lslow_path\c_name
// Compute the rosalloc bracket index
// from the size. Since the size is
// already aligned we can combine the
// two shifts together.
add x4, xSELF, x3, lsr #(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
// Subtract pointer size since there
// are no runs for 0 byte allocations
// and the size is already aligned.
ldr x4, [x4, #(THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)]
// Load the free list head (x3). This
// will be the return val.
ldr x3, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
cbz x3, .Lslow_path\c_name
// "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
ldr x1, [x3, #ROSALLOC_SLOT_NEXT_OFFSET] // Load the next pointer of the head
// and update the list head with the
// next pointer.
str x1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
// Store the class pointer in the
// header. This also overwrites the
// next pointer. The offsets are
// asserted to match.
#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
#error "Class pointer needs to overwrite next pointer."
#endif
POISON_HEAP_REF w0
str w0, [x3, #MIRROR_OBJECT_CLASS_OFFSET]
// Push the new object onto the thread
// local allocation stack and
// increment the thread local
// allocation stack top.
ldr x1, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
str w3, [x1], #COMPRESSED_REFERENCE_SIZE // (Increment x1 as a side effect.)
str x1, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
// Decrement the size of the free list
// After this "STR" the object is published to the thread local allocation stack,
// and it will be observable from a runtime internal (eg. Heap::VisitObjects) point of view.
// It is not yet visible to the running (user) compiled code until after the return.
//
// To avoid the memory barrier prior to the "STR", a trick is employed, by differentiating
// the state of the allocation stack slot. It can be a pointer to one of:
// 0) Null entry, because the stack was bumped but the new pointer wasn't written yet.
// (The stack initial state is "null" pointers).
// 1) A partially valid object, with an invalid class pointer to the next free rosalloc slot.
// 2) A fully valid object, with a valid class pointer pointing to a real class.
// Other states are not allowed.
//
// An object that is invalid only temporarily, and will eventually become valid.
// The internal runtime code simply checks if the object is not null or is partial and then
// ignores it.
//
// (Note: The actual check is done by seeing if a non-null object has a class pointer pointing
// to ClassClass, and that the ClassClass's class pointer is self-cyclic. A rosalloc free slot
// "next" pointer is not-cyclic.)
//
// See also b/28790624 for a listing of CLs dealing with this race.
ldr w1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)] sub x1, x1, #1
// TODO: consider combining this store
// and the list head store above using
// strd.
str w1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)]
mov x0, x3 // Set the return value and return.
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
// from publishing by the compiler which inserts its own StoreStore barrier.
ret
.Lslow_path\c_name:
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC
mov x1, xSELF // pass Thread::Current
CALL_SYMBOL \cxx_name
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_RESULT_IS_NON_NULL_OR_DELIVER
END \c_name
.endm
// If isInitialized=1 then the compiler assumes the object's class has already been initialized.
// If isInitialized=0 the compiler can only assume it's been at least resolved.
.macro ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED slowPathLabel isInitialized
ldr x4, [xSELF, #THREAD_LOCAL_POS_OFFSET]
ldr x5, [xSELF, #THREAD_LOCAL_END_OFFSET]
ldr w7, [x0, #MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET] // Load the object size (x7).
add x6, x4, x7 // Add object size to tlab pos.
cmp x6, x5 // Check if it fits, overflow works
// since the tlab pos and end are 32
// bit values.
// If the class is not yet visibly initialized, or it is finalizable,
// the object size will be very large to force the branch below to be taken.
//
// See Class::SetStatus() in class.cc for more details.
bhi \slowPathLabel
str x6, [xSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos.
POISON_HEAP_REF w0
str w0, [x4, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
mov x0, x4
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
// from publishing by the compiler which inserts its own StoreStore barrier.
ret
.endm
// The common code for art_quick_alloc_object_*region_tlab
// Currently the implementation ignores isInitialized. TODO(b/172087402): clean this up.
// Caller must execute a constructor fence after this.
.macro GENERATE_ALLOC_OBJECT_RESOLVED_TLAB name, entrypoint, isInitialized
ENTRY \name
// Fast path region tlab allocation.
// x0: type, xSELF(x19): Thread::Current
// x1-x7: free.
ALLOC_OBJECT_TLAB_FAST_PATH_RESOLVED .Lslow_path\name, \isInitialized
.Lslow_path\name:
SETUP_SAVE_REFS_ONLY_FRAME // Save callee saves in case of GC.
mov x1, xSELF // Pass Thread::Current.
CALL_SYMBOL \entrypoint // (mirror::Class*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_RESULT_IS_NON_NULL_OR_DELIVER
END \name
.endm
.macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE \
slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
and \xTemp1, \xTemp1, #OBJECT_ALIGNMENT_MASK_TOGGLED64 // Apply alignment mask
// (addr + 7) & ~7. The mask must
// be 64 bits to keep high bits in
// case of overflow.
// Negative sized arrays are handled here since xCount holds a zero extended 32 bit value.
// Negative ints become large 64 bit unsigned ints which will always be larger than max signed
// 32 bit int. Since the max shift for arrays is 3, it can not become a negative 64 bit int.
cmp \xTemp1, #MIN_LARGE_OBJECT_THRESHOLD // Possibly a large object, go slow
bhs \slowPathLabel // path.
ldr \xTemp0, [xSELF, #THREAD_LOCAL_POS_OFFSET] // Check tlab for space, note that
// we use (end - begin) to handle
// negative size arrays. It is
// assumed that a negative size will
// always be greater unsigned than
// region size.
ldr \xTemp2, [xSELF, #THREAD_LOCAL_END_OFFSET] sub \xTemp2, \xTemp2, \xTemp0
cmp \xTemp1, \xTemp2
// The array class is always initialized here. Unlike new-instance,
// this does not act as a double test.
bhi \slowPathLabel
// "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
// Move old thread_local_pos to x0
// for the return value.
mov x0, \xTemp0
add \xTemp0, \xTemp0, \xTemp1
str \xTemp0, [xSELF, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos.
POISON_HEAP_REF \wClass
str \wClass, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
str \wCount, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length.
// new-array is special. The class is loaded and immediately goes to the Initialized state
// before it is published. Therefore the only fence needed is for the publication of the object.
// See ClassLinker::CreateArrayClass() for more details.
// For publication of the new array, we don't need a 'dmb ishst' here.
// The compiler generates 'dmb ishst' for all new-array insts.
ret
.endm
// Caller must execute a constructor fence after this.
.macro GENERATE_ALLOC_ARRAY_TLAB name, entrypoint, size_setup
ENTRY \name
// Fast path array allocation for region tlab allocation.
// x0: mirror::Class* type
// x1: int32_t component_count
// x2-x7: free.
mov x3, x0
\size_setup x3, w3, x1, w1, x4, w4, x5, w5, x6, w6
ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED_WITH_SIZE \
.Lslow_path\name, x3, w3, x1, w1, x4, w4, x5, w5, x6, w6
.Lslow_path\name:
// x0: mirror::Class* klass
// x1: int32_t component_count
// x2: Thread* self
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC
mov x2, xSELF // pass Thread::Current
CALL_SYMBOL \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_RESULT_IS_NON_NULL_OR_DELIVER
END \name
.endm
.macro COMPUTE_ARRAY_SIZE_UNKNOWN \
xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
// Array classes are never finalizable or uninitialized, no need to check.
ldr \wTemp0, [\xClass, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Load component type
UNPOISON_HEAP_REF \wTemp0
ldr \wTemp0, [\xTemp0, #MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET]
lsr \xTemp0, \xTemp0, #PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT // Component size shift is in high 16
// bits.
// xCount is holding a 32 bit value,
// it can not overflow.
lsl \xTemp1, \xCount, \xTemp0 // Calculate datasize
// Add array data offset and alignment.
add \xTemp1, \xTemp1, #(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK)
#if MIRROR_LONG_ARRAY_DATA_OFFSET != MIRROR_INT_ARRAY_DATA_OFFSET + 4
#error Long array data offset must be 4 greater than int array data offset.
#endif
add \xTemp0, \xTemp0, #1 // Add 4 to the length only if the
// component size shift is 3
// (for 64 bit alignment).
and \xTemp0, \xTemp0, #4
add \xTemp1, \xTemp1, \xTemp0
.endm
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
REFRESH_SUSPEND_CHECK_REGISTER
br lr // Do not use RET as we do not enter the entrypoint with "BL".
/* *Calledbymanagedcodethatisattemptingtocallamethodonaproxyclass.Onentry *x0holdstheproxymethodandx1holdsthereceiver;Theframesizeoftheinvokedproxy *methodagreeswitharefandargscalleesaveframe.
*/
.extern artQuickProxyInvokeHandler
ENTRY art_quick_proxy_invoke_handler
SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_X0
mov x2, xSELF // pass Thread::Current
mov x3, sp // pass SP
CALL_SYMBOL artQuickProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP)
ldr x2, [xSELF, THREAD_EXCEPTION_OFFSET]
CFI_REMEMBER_STATE
cbnz x2, .Lexception_in_proxy // success if no exception is pending
RESTORE_SAVE_REFS_AND_ARGS_FRAME // Restore frame
REFRESH_MARKING_REGISTER
fmov d0, x0 // Store result in d0 in case it was float or double
ret // return on success
.Lexception_in_proxy:
CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
RESTORE_SAVE_REFS_AND_ARGS_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
/* *Calledtoresolveanimtconflict. *x0istheconflictArtMethod. *x15isahiddenargumentthatholdsthetargetinterfacemethod. * *NotethatthisstubwritestoxIP0,andx0.
*/
ENTRY art_quick_imt_conflict_trampoline
ldr xIP0, [x0, #ART_METHOD_JNI_OFFSET_64] // Load ImtConflictTable
ldr x0, [xIP0] // Load first entry in ImtConflictTable.
.Limt_table_iterate:
cmp x0, x15
// Branch if found. Benchmarks have shown doing a branch here is better.
beq .Limt_table_found
// If the entry is null, the interface method is not in the ImtConflictTable.
cbz x0, .Lconflict_trampoline
// Iterate over the entries of the ImtConflictTable.
ldr x0, [xIP0, #(2 * __SIZEOF_POINTER__)]!
b .Limt_table_iterate
.Limt_table_found:
// We successfully hit an entry in the table. Load the target method
// and jump to it.
ldr x0, [xIP0, #__SIZEOF_POINTER__]
ldr xIP0, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
br xIP0
.Lconflict_trampoline:
// Call the runtime stub to populate the ImtConflictTable and jump to the
// resolved method.
mov x0, x15 // Load interface method
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
// Save SP, so we can have static CFI info.
mov x28, sp
.cfi_def_cfa_register x28
mov xIP0, #GENERIC_JNI_TRAMPOLINE_RESERVED_AREA sub sp, sp, xIP0
// prepare for artQuickGenericJniTrampoline call
// (Thread*, managed_sp, reserved_area)
// x0 x1 x2 <= C calling convention
// xSELF x28 sp <= where they are
mov x0, xSELF // Thread*
mov x1, x28 // SP for the managed frame.
mov x2, sp // reserved area for arguments and other saved data (up to managed frame)
CALL_SYMBOL artQuickGenericJniTrampoline // (Thread*, sp)
// The C call will have registered the complete save-frame on success.
// The result of the call is:
// x0: pointer to native code, 0 on error.
// The bottom of the reserved area contains values for arg registers,
// hidden arg register and SP for out args for the call.
// Check for error (class init check or locking for synchronized native method can throw).
cbz x0, .Lexception_in_native
#ifdef ART_USE_SIMULATOR
// x0 as first argument - code pointer.
mov x1, sp
mov x2, xSELF
// Call to a placeholder function, simulator will intercept it and process both the native
// call and a call to artQuickGenericJniEndTrampoline.
CALL_SYMBOL artArm64SimulatorGenericJNIPlaceholder
#else // ART_USE_SIMULATOR
// Save the code pointer
mov xIP0, x0
// Load hidden arg (x15) for @CriticalNative and SP for out args.
ldp x15, xIP1, [sp, #128]
// Apply the new SP for out args, releasing unneeded reserved area.
mov sp, xIP1
blr xIP0 // native call.
// result sign extension is handled in C code
// prepare for artQuickGenericJniEndTrampoline call
// (Thread*, result, result_f)
// x0 x1 x2 <= C calling convention
mov x1, x0 // Result (from saved).
mov x0, xSELF // Thread register.
fmov x2, d0 // d0 will contain floating point result, but needs to go into x2
// Tear down the callee-save frame.
.cfi_def_cfa_register sp
// Restore callee-saves and LR as in `RESTORE_SAVE_REFS_AND_ARGS_FRAME`
// but do not restore argument registers.
// 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.
#if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 224)
#error "FRAME_SIZE_SAVE_REFS_AND_ARGS(ARM64) size not as expected."
#endif
RESTORE_REG x20, 136
RESTORE_TWO_REGS x21, x22, 144
RESTORE_TWO_REGS x23, x24, 160
RESTORE_TWO_REGS x25, x26, 176
RESTORE_TWO_REGS x27, x28, 192
RESTORE_TWO_REGS x29, xLR, 208
// Remove the frame.
DECREASE_FRAME FRAME_SIZE_SAVE_REFS_AND_ARGS
REFRESH_MARKING_REGISTER
// store into fpr, for when it's a fpr return...
fmov d0, x0
ret
.Lexception_in_native:
// Move to x1 then sp to please assembler.
ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
add sp, x1, #-1 // Remove the GenericJNI tag.
CALL_SYMBOL art_deliver_pending_exception
END art_quick_generic_jni_trampoline
ENTRY art_deliver_pending_exception
# This will create a new save-all frame, required by the runtime.
DELIVER_PENDING_EXCEPTION
END art_deliver_pending_exception
/* *CalledtobridgefromthequicktointerpreterABI.Onentrytheargumentsmatchthose *ofaquickcall: *x0=methodbeingcalled/tobridgeto. *x1..x7,d0..d7=argumentstothatmethod.
*/
ENTRY art_quick_to_interpreter_bridge
SETUP_SAVE_REFS_AND_ARGS_FRAME // Set up frame and save arguments.
// x0 will contain mirror::ArtMethod* method.
mov x1, xSELF // How to get Thread::Current() ???
mov x2, sp
.Lmatch_0: sub x0, x0, #6 sub x0, x0, x5
asr x0, x0, #1
ret
.Lmatch_1: sub x0, x0, #4 sub x0, x0, x5
asr x0, x0, #1
ret
.Lmatch_2: sub x0, x0, #2 sub x0, x0, x5
asr x0, x0, #1
ret
.Lmatch_3: sub x0, x0, x5
asr x0, x0, #1
ret
#if (STRING_COMPRESSION_FEATURE) /* *Comparingcompressedstringcharacter-per-characterwith *inputcharacter
*/
.Lstring_indexof_compressed:
add x0, x0, x2 sub x0, x0, #1 sub w2, w3, w2
.Lstring_indexof_compressed_loop:
subs w2, w2, #1
b.lt .Lindexof_nomatch
ldrb w6, [x0, #1]!
cmp w6, w1
b.eq .Lstring_indexof_compressed_matched
b .Lstring_indexof_compressed_loop
.Lstring_indexof_compressed_matched: sub x0, x0, x5
ret
#endif
END art_quick_indexof
.extern artStringBuilderAppend
ENTRY art_quick_string_builder_append
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC
add x1, sp, #(FRAME_SIZE_SAVE_REFS_ONLY + __SIZEOF_POINTER__) // pass args
mov x2, xSELF // pass Thread::Current
CALL_SYMBOL artStringBuilderAppend // (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
RETURN_OR_DEOPT_IF_RESULT_IS_NON_NULL_OR_DELIVER
END art_quick_string_builder_append
/* *Createafunction`name`callingtheReadBarrier::Markroutine, *gettingitsargumentandreturningitsresultthroughWregister *`wreg`(correspondingtoXregister`xreg`),savingandrestoring *allcaller-saveregisters. * *Thegeneratedfunctionfollowsanon-standardruntimecallingconvention: *-register`reg`(whichmaybedifferentfromW0)isusedtopassthe(sole)argument, *-register`reg`(whichmaybedifferentfromW0)isusedtoreturntheresult, *-allotherregistersarecallee-save(thevaluestheyholdarepreserved).
*/
.macro READ_BARRIER_MARK_REG name, wreg, xreg
ENTRY \name
// Reference is null, no work to do at all.
cbz \wreg, .Lret_rb_\name
// Use wIP0 as temp and check the mark bit of the reference. wIP0 is not used by the compiler.
ldr wIP0, [\xreg, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
tbz wIP0, #LOCK_WORD_MARK_BIT_SHIFT, .Lnot_marked_rb_\name
.Lret_rb_\name:
ret
.Lnot_marked_rb_\name:
// Check if the top two bits are one, if this is the case it is a forwarding address.
tst wIP0, wIP0, lsl #1
bmi .Lret_forwarding_address\name
.Lslow_rb_\name: /* *Allocate44stackslots*8=352bytes: *-19slotsforcoreregistersX0-15,X17,X19,LR *-1slotpadding *-24slotsforfloating-pointregistersD0-D7andD16-D31
*/
// We must not clobber IP1 since code emitted for HLoadClass and HLoadString
// relies on IP1 being preserved.
// Save all potentially live caller-save core registers.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 352
SAVE_TWO_REGS x2, x3, 16
SAVE_TWO_REGS x4, x5, 32
SAVE_TWO_REGS x6, x7, 48
SAVE_TWO_REGS x8, x9, 64
SAVE_TWO_REGS x10, x11, 80
SAVE_TWO_REGS x12, x13, 96
SAVE_TWO_REGS x14, x15, 112
SAVE_TWO_REGS x17, x19, 128 // Skip x16, i.e. IP0, and x18, the platform register.
SAVE_REG xLR, 144 // Save also return address.
// Save all potentially live caller-save floating-point registers.
stp d0, d1, [sp, #160]
stp d2, d3, [sp, #176]
stp d4, d5, [sp, #192]
stp d6, d7, [sp, #208]
stp d16, d17, [sp, #224]
stp d18, d19, [sp, #240]
stp d20, d21, [sp, #256]
stp d22, d23, [sp, #272]
stp d24, d25, [sp, #288]
stp d26, d27, [sp, #304]
stp d28, d29, [sp, #320]
stp d30, d31, [sp, #336]
.ifnc \wreg, w0
mov w0, \wreg // Pass arg1 - obj from `wreg`
.endif
CALL_SYMBOL artReadBarrierMark // artReadBarrierMark(obj)
.ifnc \wreg, w0
mov \wreg, w0 // Return result into `wreg`
.endif
// Restore core regs, except `xreg`, as `wreg` is used to return the
// result of this function (simply remove it from the stack instead).
POP_REGS_NE x0, x1, 0, \xreg
POP_REGS_NE x2, x3, 16, \xreg
POP_REGS_NE x4, x5, 32, \xreg
POP_REGS_NE x6, x7, 48, \xreg
POP_REGS_NE x8, x9, 64, \xreg
POP_REGS_NE x10, x11, 80, \xreg
POP_REGS_NE x12, x13, 96, \xreg
POP_REGS_NE x14, x15, 112, \xreg
POP_REGS_NE x17, x19, 128, \xreg
POP_REG_NE xLR, 144, \xreg // Restore also return address.
// Restore floating-point registers.
ldp d0, d1, [sp, #160]
ldp d2, d3, [sp, #176]
ldp d4, d5, [sp, #192]
ldp d6, d7, [sp, #208]
ldp d16, d17, [sp, #224]
ldp d18, d19, [sp, #240]
ldp d20, d21, [sp, #256]
ldp d22, d23, [sp, #272]
ldp d24, d25, [sp, #288]
ldp d26, d27, [sp, #304]
ldp d28, d29, [sp, #320]
ldp d30, d31, [sp, #336]
// Remove frame and return.
DECREASE_FRAME 352
ret
.Lret_forwarding_address\name:
// Shift left by the forwarding address shift. This clears out the state bits since they are
// in the top 2 bits of the lock word.
lsl \wreg, wIP0, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
ret
END \name
.endm
/* *UseintrospectiontoloadareferencefromthesameaddressastheLDR *instructioningeneratedcodewouldload(unlessloadedbythethunk, *seebelow),callReadBarrier::Mark()withthatreferenceifneeded *andreturnitinthesameregisterastheLDRinstructionwouldload. * *Theentrypointiscalledthroughathunkthatdiffersacrossloadkinds. *ForfieldandarrayloadstheLDRinstructioningeneratedcodefollows *thebranchtothethunk,i.e.theLDRisat[LR,#-4],andthethunk *knowstheholderandperformsthegraybitcheck,returningtotheLDR *instructioniftheobjectisnotgray,sothisentrypointnolonger *needstoknowanythingabouttheholder.ForGCrootloads,theLDR *instructioningeneratedcodeprecedesthebranchtothethunk(i.e. *theLDRisat[LR,#-8])andthethunkdoesnotdothegraybitcheck. * *Forfieldaccessesandarrayloadswithaconstantindexthethunkloads *thereferenceintoIP0usingintrospectionandcallsthemainentrypoint, *art_quick_read_barrier_mark_introspection.Withheappoisoningenabled, *thepassedreferenceispoisoned. * *Forarrayaccesseswithnon-constantindex,thethunkinsertsthebits *16-21oftheLDRinstructiontotheentrypointaddress,effectively *calculatingaswitchcaselabelbasedontheindexregister(bits16-20) *andaddinganextraoffset(bit21isset)todifferentiatefromthe *mainentrypoint,thenmovesthebaseregistertoIP0andjumpstothe *switchcase.Thereforeweneedtoalignthemainentrypointto512bytes, *accountingfora256-byteoffsetfollowedby32arrayentrypoints *startingatart_quick_read_barrier_mark_introspection_arrays,each *containinganLDR(register)andabranchtothemainentrypoint. * *ForGCrootaccesseswecannotusethemainentrypointbecauseofthe *differentoffsetwheretheLDRinstructioningeneratedcodeislocated. *(Andevenwithheappoisoningenabled,GCrootsarenotpoisoned.) *Tore-usethesameentrypointpointeringeneratedcode,wemakesure *thatthegcrootentrypoint(acopyoftheentrypointwithadifferent *offsetforintrospectionloads)islocatedataknownoffset(768bytes, *orBAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET)fromthemain *entrypointandtheGCrootthunkadjuststheentrypointpointer,moves *therootregistertoIP0andjumpstothecustomizedentrypoint, *art_quick_read_barrier_mark_introspection_gc_roots.Thethunkalso *performsallthefast-pathchecks,soweneedjusttheslowpath. *TheUnsafeCASObjectintrinsicisalsousingtheGCrootentrypointwith *MOVinsteadofLDR,thedestinationregisterisinthesamebits. * *Thecodestructureis *art_quick_read_barrier_mark_introspection: *Upto256bytesforthemainentrypointcode. *Paddingto256bytesifneeded. *art_quick_read_barrier_mark_introspection_arrays: *Exactly256bytesforarrayloadswitchcases(32x2instructions). *.Lmark_introspection_return_switch: *Exactly256bytesforreturnswitchcases(32x2instructions). *art_quick_read_barrier_mark_introspection_gc_roots: *GCrootentrypointcode.
*/
ENTRY_ALIGNED art_quick_read_barrier_mark_introspection, 512
// At this point, IP0 contains the reference, IP1 can be freely used.
// For heap poisoning, the reference is poisoned, so unpoison it first.
UNPOISON_HEAP_REF wIP0
// If reference is null, just return it in the right register.
cbz wIP0, .Lmark_introspection_return
// Use wIP1 as temp and check the mark bit of the reference.
ldr wIP1, [xIP0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
tbz wIP1, #LOCK_WORD_MARK_BIT_SHIFT, .Lmark_introspection_unmarked
.Lmark_introspection_return:
// Without an extra register for the return switch case address calculation,
// we exploit the high word of the xIP0 to temporarily store the ref_reg*8,
// so the return switch below must move wIP0 instead of xIP0 to the register.
ldr wIP1, [lr, #BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET] // Load the instruction.
bfi xIP0, xIP1, #(32 + 3), #5 // Extract ref_reg*8 to high word in xIP0.
adr xIP1, .Lmark_introspection_return_switch
bfxil xIP1, xIP0, #32, #8 // Calculate return switch case address.
br xIP1
.Lmark_introspection_unmarked:
// Check if the top two bits are one, if this is the case it is a forwarding address.
tst wIP1, wIP1, lsl #1
bmi .Lmark_introspection_forwarding_address
READ_BARRIER_MARK_INTROSPECTION_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET
.Lmark_introspection_forwarding_address:
// Shift left by the forwarding address shift. This clears out the state bits since they are
// in the top 2 bits of the lock word.
lsl wIP0, wIP1, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
b .Lmark_introspection_return
// We're very close to the alloted 256B for the entrypoint code before the
// array switch cases. Should we go a little bit over the limit, we can
// move some code after the array switch cases and return switch cases.
.balign 256
.hidden art_quick_read_barrier_mark_introspection_arrays
.global art_quick_read_barrier_mark_introspection_arrays
art_quick_read_barrier_mark_introspection_arrays:
FOR_XREGISTERS INTROSPECTION_ARRAY_LOAD, BRK0_BRK0
.Lmark_introspection_return_switch:
FOR_WREGISTERS MOV_WIP0_TO_WREG_AND_BL_LR, BRK0_BRK0
.hidden art_quick_read_barrier_mark_introspection_gc_roots
.global art_quick_read_barrier_mark_introspection_gc_roots
art_quick_read_barrier_mark_introspection_gc_roots:
READ_BARRIER_MARK_INTROSPECTION_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET
END art_quick_read_barrier_mark_introspection
.extern artInvokePolymorphic
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
mov x0, x1 // x0 := receiver
mov x1, xSELF // x1 := Thread::Current()
mov x2, sp // x2 := SP
CALL_SYMBOL artInvokePolymorphic // artInvokePolymorphic(receiver, thread, save_area)
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
fmov d0, x0 // Result is in x0. Copy to floating return register.
RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_invoke_polymorphic
/* *SlowpathforMethodHandle.invokeExactintrinsic. *Thatintrinsichasacustomcallingconvention:theargumentallocationdoesn'tstartfrom *thereceiver(MethodHandle)object,butfromtheargumentfollowingit.That'sdonetomatch *expectationoftheunderlyingmethodwhenMethodHandletargetsamethod.Thatalsoaffects *thewayargumentsarespilledontothestack.
*/
.extern artInvokePolymorphicWithHiddenReceiver
ENTRY art_quick_invoke_polymorphic_with_hidden_receiver
// On entry: w0 := receiver
SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
mov x1, xSELF // x1 := Thread::Current()
mov x2, sp // x2 := SP
CALL_SYMBOL artInvokePolymorphicWithHiddenReceiver // invoke with (receiver, thread, save_area)
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
fmov d0, x0 // Result is in x0. Copy to floating return register.
RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_invoke_polymorphic_with_hidden_receiver
.extern artInvokeCustom
ENTRY art_quick_invoke_custom
SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC.
// x0 := call_site_idx
mov x1, xSELF // x1 := Thread::Current()
mov x2, sp // x2 := SP
CALL_SYMBOL artInvokeCustom // artInvokeCustom(call_site_idx, thread, save_area)
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
fmov d0, x0 // Copy result to double result register.
RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_invoke_custom
// On entry, method is at the bottom of the stack.
ENTRY art_quick_compile_optimized
SETUP_SAVE_EVERYTHING_FRAME
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod
mov x1, xSELF // pass Thread::Current
CALL_SYMBOL artCompileOptimized // (ArtMethod*, Thread*)
RESTORE_SAVE_EVERYTHING_FRAME
// We don't need to restore the marking register here, as
// artCompileOptimized doesn't allow thread suspension.
ret
END art_quick_compile_optimized
// On entry, method is at the bottom of the stack.
ENTRY art_quick_compile_baseline
SETUP_SAVE_EVERYTHING_FRAME
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod
mov x1, xSELF // pass Thread::Current
CALL_SYMBOL artCompileBaseline // (ArtMethod*, Thread*)
RESTORE_SAVE_EVERYTHING_FRAME
// We don't need to restore the marking register here, as
// artCompileBaseline doesn't allow thread suspension.
ret
END art_quick_compile_baseline
ENTRY art_quick_record_entry_trace_event
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
// xIP0 has the trace buffer pointer. This is loaded on the fast path before
// checking if we need to call this method. This will be still valid here.
cmp xIP1, xIP0
bhs .Lupdate_entry
mov xIP1, #TRACE_BUFFER_SIZE
add xIP1, xIP0, xIP1
.Lupdate_entry: sub xIP1, xIP1, 8
str x0, [xIP1]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
END art_quick_record_entry_trace_event
ENTRY art_quick_record_exit_trace_event
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
// xIP0 has the trace buffer pointer. This is loaded on the fast path before
// checking if we need to call this method. This will be still valid here.
cmp xIP1, xIP0
bhs .Lupdate_entry_exit
mov xIP1, #TRACE_BUFFER_SIZE
add xIP1, xIP0, xIP1
.Lupdate_entry_exit: sub xIP1, xIP1, 8
mov xIP0, #1
str xIP0, [xIP1]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
END art_quick_record_exit_trace_event
ENTRY art_quick_record_entry_trace_event_with_flush
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
// xIP0 has the trace buffer pointer. This is loaded on the fast path before
// checking if we need to call this method. This will be still valid here. sub xIP1, xIP1, 16
cmp xIP1, xIP0
b.lt .Lhandle_record_entry_overflow
mrs xIP0, cntvct_el0
str x0, [xIP1]
lsl xIP0, xIP0, 2
str xIP0, [xIP1, 8]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
.Lhandle_record_entry_overflow:
SETUP_SAVE_EVERYTHING_FRAME
// x0 already has method*
mov x1, xSELF // pass Thread::Current
mov x2, 1 // set to true for entry events
CALL_SYMBOL artRecordMethodTraceEvent // (ArtMethod*, Thread*, is_entry)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
ret
END art_quick_record_entry_trace_event_with_flush
ENTRY art_quick_record_exit_trace_event_with_flush
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
// xIP0 has the trace buffer pointer. This is loaded on the fast path before
// checking if we need to call this method. This will be still valid here. sub xIP1, xIP1, 8
cmp xIP1, xIP0
b.lt .Lhandle_record_exit_overflow
mrs xIP0, cntvct_el0
lsl xIP0, xIP0, 2
orr xIP0, xIP0, #1
str xIP0, [xIP1]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
.Lhandle_record_exit_overflow:
SETUP_SAVE_EVERYTHING_FRAME
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod*
mov x1, xSELF // pass Thread::Current
mov x2, 0 // set to true for exit events
CALL_SYMBOL artRecordMethodTraceEvent // (ArtMethod*, Thread*, is_entry)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
ret
END art_quick_record_exit_trace_event_with_flush
ENTRY art_quick_record_long_running_entry_trace_event
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET] sub xIP1, xIP1, 16
// xIP0 has the trace buffer pointer. This is loaded on the fast path before
// checking if we need to call this method. This will be still valid here.
cmp xIP0, xIP1
bhi .Lhandle_overflow
// Store the method pointer
str x0, [xIP1, 8]
// Store the timestamp with the last bit set to 0 to indicate method entry event
mrs xIP0, cntvct_el0
bfc xIP0, 0, 1
str xIP0, [xIP1]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
.Lhandle_overflow:
// Call runtime to flush buffer. We expect the frequency of this case to be low. For ANR it
// might be interesting to record the past data instead of overwriting with new entries.
SETUP_SAVE_EVERYTHING_FRAME
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod*
mov x1, xSELF // pass Thread::Current
mov x2, 1 // set to true for entry events
CALL_SYMBOL artRecordLongRunningMethodTraceEvent // (ArtMethod*, Thread*, is_entry)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
ret
END art_quick_record_long_running_entry_trace_event
ENTRY art_quick_record_long_running_exit_trace_event
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
// xIP0 has the trace buffer pointer. This is loaded on the fast path before checking if we
// need to call this method. This will be still valid here.
// We just need one entry for the method exit. We only record the timestamp. Method will be
// available from the corresponding method entry event.
cmp xIP0, xIP1
bhs .Lhandle_overflow_exit
// Get the timestamp of the method exit event.
mrs xIP0, cntvct_el0
// Load the previous event.
ldr xIP1, [xIP1]
// If lsb is set for the previous event, then the previous event is an exit event. This means
// there was a long running method earlier on the call stack. Record the exit event for this
// method to construct the call chain.
tst xIP1, 1
bne .Lrecord_exit_event
// The previous event was an entry event. This exit corresponds to the previous method entry.
// Check if the method is long running by comparing the current timestamp with the previous
// event's timestamp. sub xIP1, xIP0, xIP1
cmp xIP1, #LONG_RUNNING_METHOD_THRESHOLD
bhs .Lrecord_exit_event
// This wasn't a long running method. Erase the previously recorded method entry event. We
// don't need to record entry / exit for this method.
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
add xIP1, xIP1, 16
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
.Lrecord_exit_event:
// For method exits we only record the current timestamp. We can infer the method from the
// corresponding method entry event.
ldr xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET] sub xIP1, xIP1, 8
// Set the lsb of the timestamp to 1 to indicate a method exit event.
orr xIP0, xIP0, #1
str xIP0, [xIP1]
str xIP1, [xSELF, #TRACE_BUFFER_CURRENT_OFFSET]
ret
.Lhandle_overflow_exit:
// Call runtime to flush buffer. We expect the frequency of this case to be low. For ANR it
// might be interesting to record the past data instead of overwriting with new entries.
SETUP_SAVE_EVERYTHING_FRAME
ldr xzr, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass nullptr. ArtMethod* isn't required for exits
mov x1, xSELF // pass Thread::Current
mov x2, 0 // set to false for exit events
CALL_SYMBOL artRecordLongRunningMethodTraceEvent // (ArtMethod*, Thread*, is_entry)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
ret
END art_quick_record_long_running_exit_trace_event
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.