xref: /aosp_15_r20/art/runtime/arch/arm64/asm_support_arm64.S (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
18#define ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
19
20#include "asm_support_arm64.h"
21#include "interpreter/cfi_asm_support.h"
22
23// Define special registers.
24
25// Register holding Thread::Current().
26#define xSELF x19
27// Frame Pointer
28#define xFP   x29
29// Link Register
30#define xLR   x30
31// Define the intraprocedural linkage temporary registers.
32#define xIP0 x16
33#define wIP0 w16
34#define xIP1 x17
35#define wIP1 w17
36
37#ifdef RESERVE_MARKING_REGISTER
38// Marking Register, holding Thread::Current()->GetIsGcMarking().
39#define wMR w20
40#endif
41
42// Implicit suspend check register.
43#define xSUSPEND x21
44
45.macro CFI_EXPRESSION_BREG n, b, offset
46    .if (-0x40 <= (\offset)) && ((\offset) < 0x40)
47        CFI_EXPRESSION_BREG_1(\n, \b, \offset)
48    .elseif (-0x2000 <= (\offset)) && ((\offset) < 0x2000)
49        CFI_EXPRESSION_BREG_2(\n, \b, \offset)
50    .else
51        .error "Unsupported offset"
52    .endif
53.endm
54
55.macro CFI_DEF_CFA_BREG_PLUS_UCONST reg, offset, size
56    .if ((\size) < 0)
57        .error "Size should be positive"
58    .endif
59    .if (((\offset) < -0x40) || ((\offset) >= 0x40))
60        .error "Unsupported offset"
61    .endif
62    .if ((\size) < 0x80)
63        CFI_DEF_CFA_BREG_PLUS_UCONST_1_1(\reg, \offset, \size)
64    .elseif ((\size) < 0x4000)
65        CFI_DEF_CFA_BREG_PLUS_UCONST_1_2(\reg, \offset, \size)
66    .else
67        .error "Unsupported size"
68    .endif
69.endm
70
71.macro CFI_REMEMBER_STATE
72    .cfi_remember_state
73.endm
74
75// The spec is not clear whether the CFA is part of the saved state and tools
76// differ in the behaviour, so explicitly set the CFA to avoid any ambiguity.
77// The restored CFA state should match the CFA state during CFI_REMEMBER_STATE.
78.macro CFI_RESTORE_STATE_AND_DEF_CFA reg, offset
79    .cfi_restore_state
80    .cfi_def_cfa \reg, \offset
81.endm
82
83.macro ENTRY_ALIGNED name, alignment
84    .type \name, #function
85    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
86    .global \name
87    .balign \alignment
88\name:
89    .cfi_startproc
90.endm
91
92.macro ENTRY name
93    ENTRY_ALIGNED \name, 16
94.endm
95
96.macro END name
97    .cfi_endproc
98    .size \name, .-\name
99.endm
100
101.macro UNIMPLEMENTED name
102    ENTRY \name
103    brk 0
104    END \name
105.endm
106
107// Macro to poison (negate) the reference for heap poisoning.
108.macro POISON_HEAP_REF rRef
109#ifdef USE_HEAP_POISONING
110    neg \rRef, \rRef
111#endif  // USE_HEAP_POISONING
112.endm
113
114// Macro to unpoison (negate) the reference for heap poisoning.
115.macro UNPOISON_HEAP_REF rRef
116#ifdef USE_HEAP_POISONING
117    neg \rRef, \rRef
118#endif  // USE_HEAP_POISONING
119.endm
120
121.macro INCREASE_FRAME frame_adjustment
122    sub sp, sp, #(\frame_adjustment)
123    .cfi_adjust_cfa_offset (\frame_adjustment)
124.endm
125
126.macro DECREASE_FRAME frame_adjustment
127    add sp, sp, #(\frame_adjustment)
128    .cfi_adjust_cfa_offset -(\frame_adjustment)
129.endm
130
131.macro SAVE_REG reg, offset
132    str \reg, [sp, #(\offset)]
133    .cfi_rel_offset \reg, (\offset)
134.endm
135
136.macro RESTORE_REG_BASE base, reg, offset
137    ldr \reg, [\base, #(\offset)]
138    .cfi_restore \reg
139.endm
140
141.macro RESTORE_REG reg, offset
142    RESTORE_REG_BASE sp, \reg, \offset
143.endm
144
145.macro SAVE_TWO_REGS_BASE base, reg1, reg2, offset
146    stp \reg1, \reg2, [\base, #(\offset)]
147    .cfi_rel_offset \reg1, (\offset)
148    .cfi_rel_offset \reg2, (\offset) + 8
149.endm
150
151.macro SAVE_TWO_REGS reg1, reg2, offset
152    SAVE_TWO_REGS_BASE sp, \reg1, \reg2, \offset
153.endm
154
155.macro SAVE_TWO_REGS_INCREASE_FRAME reg1, reg2, frame_adjustment
156    stp \reg1, \reg2, [sp, #-(\frame_adjustment)]!
157    .cfi_adjust_cfa_offset (\frame_adjustment)
158    .cfi_rel_offset \reg1, 0
159    .cfi_rel_offset \reg2, 8
160.endm
161
162.macro RESTORE_TWO_REGS_BASE base, reg1, reg2, offset
163    ldp \reg1, \reg2, [\base, #(\offset)]
164    .cfi_restore \reg1
165    .cfi_restore \reg2
166.endm
167
168.macro RESTORE_TWO_REGS reg1, reg2, offset
169    RESTORE_TWO_REGS_BASE sp, \reg1, \reg2, \offset
170.endm
171
172.macro RESTORE_TWO_REGS_DECREASE_FRAME reg1, reg2, frame_adjustment
173    ldp \reg1, \reg2, [sp], #(\frame_adjustment)
174    .cfi_restore \reg1
175    .cfi_restore \reg2
176    .cfi_adjust_cfa_offset -(\frame_adjustment)
177.endm
178
179#define ALL_ARGS_SIZE (/*x0-x7*/ 8 * 8 + /*d0-d7*/ 8 * 8)
180
181.macro SAVE_ALL_ARGS_INCREASE_FRAME extra_space
182    // Save register args x0-x7, d0-d7 and return address.
183    stp    x0, x1, [sp, #-(ALL_ARGS_SIZE + \extra_space)]!
184    .cfi_adjust_cfa_offset (ALL_ARGS_SIZE + \extra_space)
185    stp    x2, x3, [sp, #16]
186    stp    x4, x5, [sp, #32]
187    stp    x6, x7, [sp, #48]
188    stp    d0, d1, [sp, #64]
189    stp    d2, d3, [sp, #80]
190    stp    d4, d5, [sp, #96]
191    stp    d6, d7, [sp, #112]
192.endm
193
194.macro RESTORE_ALL_ARGS_DECREASE_FRAME extra_space
195    ldp    x2, x3, [sp, #16]
196    ldp    x4, x5, [sp, #32]
197    ldp    x6, x7, [sp, #48]
198    ldp    d0, d1, [sp, #64]
199    ldp    d2, d3, [sp, #80]
200    ldp    d4, d5, [sp, #96]
201    ldp    d6, d7, [sp, #112]
202    ldp    x0, x1, [sp], #(ALL_ARGS_SIZE + \extra_space)
203    .cfi_adjust_cfa_offset -(ALL_ARGS_SIZE + \extra_space)
204.endm
205
206.macro LOAD_RUNTIME_INSTANCE reg
207#if __has_feature(hwaddress_sanitizer)
208    adrp \reg, :pg_hi21_nc:_ZN3art7Runtime9instance_E
209#else
210    adrp \reg, _ZN3art7Runtime9instance_E
211#endif
212    ldr \reg, [\reg, #:lo12:_ZN3art7Runtime9instance_E]
213.endm
214
215// Macro to refresh the Marking Register (W20).
216//
217// This macro must be called at the end of functions implementing
218// entrypoints that possibly (directly or indirectly) perform a
219// suspend check (before they return).
220.macro REFRESH_MARKING_REGISTER
221#ifdef RESERVE_MARKING_REGISTER
222    ldr wMR, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
223#endif
224.endm
225
226// Macro to refresh the suspend check register.
227//
228// We do not refresh `xSUSPEND` after every transition to Runnable, so there is
229// a chance that an implicit suspend check loads null to xSUSPEND but before
230// causing a SIGSEGV at the next implicit suspend check we make a runtime call
231// that performs the suspend check explicitly. This can cause a spurious fault
232// without a pending suspend check request but it should be rare and the fault
233// overhead was already expected when we triggered the suspend check, we just
234// pay the price later than expected.
235.macro REFRESH_SUSPEND_CHECK_REGISTER
236    ldr xSUSPEND, [xSELF, #THREAD_SUSPEND_TRIGGER_OFFSET]
237.endm
238
239    /*
240     * Macro that sets up the callee save frame to conform with
241     * Runtime::CreateCalleeSaveMethod(kSaveRefsOnly).
242     */
243.macro SETUP_SAVE_REFS_ONLY_FRAME
244    // art::Runtime* xIP0 = art::Runtime::instance_;
245    // Our registers aren't intermixed - just spill in order.
246    LOAD_RUNTIME_INSTANCE xIP0
247
248    // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveRefOnly];
249    ldr xIP0, [xIP0, RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET]
250
251    INCREASE_FRAME 96
252
253    // Ugly compile-time check, but we only have the preprocessor.
254#if (FRAME_SIZE_SAVE_REFS_ONLY != 96)
255#error "FRAME_SIZE_SAVE_REFS_ONLY(ARM64) size not as expected."
256#endif
257
258    // GP callee-saves.
259    // x20 paired with ArtMethod* - see below.
260    SAVE_TWO_REGS x21, x22, 16
261    SAVE_TWO_REGS x23, x24, 32
262    SAVE_TWO_REGS x25, x26, 48
263    SAVE_TWO_REGS x27, x28, 64
264    SAVE_TWO_REGS x29, xLR, 80
265
266    // Store ArtMethod* Runtime::callee_save_methods_[kSaveRefsOnly].
267    // Note: We could avoid saving X20 in the case of Baker read
268    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
269    // later; but it's not worth handling this special case.
270    stp xIP0, x20, [sp]
271    .cfi_rel_offset x20, 8
272
273    // Place sp in Thread::Current()->top_quick_frame.
274    mov xIP0, sp
275    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
276.endm
277
278// TODO: Probably no need to restore registers preserved by aapcs64.
279.macro RESTORE_SAVE_REFS_ONLY_FRAME
280    // Callee-saves.
281    // Note: Likewise, we could avoid restoring X20 in the case of Baker
282    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
283    // later; but it's not worth handling this special case.
284    RESTORE_REG x20, 8
285    RESTORE_TWO_REGS x21, x22, 16
286    RESTORE_TWO_REGS x23, x24, 32
287    RESTORE_TWO_REGS x25, x26, 48
288    RESTORE_TWO_REGS x27, x28, 64
289    RESTORE_TWO_REGS x29, xLR, 80
290
291    DECREASE_FRAME 96
292.endm
293
294.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL base
295    // Ugly compile-time check, but we only have the preprocessor.
296#if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 224)
297#error "FRAME_SIZE_SAVE_REFS_AND_ARGS(ARM64) size not as expected."
298#endif
299
300    // Stack alignment filler [\base, #8].
301    // FP args.
302    stp d0, d1, [\base, #16]
303    stp d2, d3, [\base, #32]
304    stp d4, d5, [\base, #48]
305    stp d6, d7, [\base, #64]
306
307    // Core args.
308    stp x1, x2, [\base, #80]
309    stp x3, x4, [\base, #96]
310    stp x5, x6, [\base, #112]
311
312    // x7, Callee-saves.
313    // Note: We could avoid saving X20 in the case of Baker read
314    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
315    // later; but it's not worth handling this special case.
316    stp x7, x20, [\base, #128]
317    .cfi_rel_offset x20, 136
318    SAVE_TWO_REGS_BASE \base, x21, x22, 144
319    SAVE_TWO_REGS_BASE \base, x23, x24, 160
320    SAVE_TWO_REGS_BASE \base, x25, x26, 176
321    SAVE_TWO_REGS_BASE \base, x27, x28, 192
322
323    // x29(callee-save) and LR.
324    SAVE_TWO_REGS_BASE \base, x29, xLR, 208
325.endm
326
327// TODO: Probably no need to restore registers preserved by aapcs64. (That would require
328// auditing all users to make sure they restore aapcs64 callee-save registers they clobber.)
329.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_INTERNAL base
330    // FP args.
331    ldp d0, d1, [\base, #16]
332    ldp d2, d3, [\base, #32]
333    ldp d4, d5, [\base, #48]
334    ldp d6, d7, [\base, #64]
335
336    // Core args.
337    ldp x1, x2, [\base, #80]
338    ldp x3, x4, [\base, #96]
339    ldp x5, x6, [\base, #112]
340
341    // x7, callee-saves and LR.
342    // Note: Likewise, we could avoid restoring X20 in the case of Baker
343    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
344    // later; but it's not worth handling this special case.
345    ldp x7, x20, [\base, #128]
346    .cfi_restore x20
347    RESTORE_TWO_REGS_BASE \base, x21, x22, 144
348    RESTORE_TWO_REGS_BASE \base, x23, x24, 160
349    RESTORE_TWO_REGS_BASE \base, x25, x26, 176
350    RESTORE_TWO_REGS_BASE \base, x27, x28, 192
351    RESTORE_TWO_REGS_BASE \base, x29, xLR, 208
352.endm
353
354.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME
355    RESTORE_SAVE_REFS_AND_ARGS_FRAME_INTERNAL sp
356    DECREASE_FRAME FRAME_SIZE_SAVE_REFS_AND_ARGS
357.endm
358
359.macro SAVE_ALL_CALLEE_SAVES offset
360    // FP callee-saves.
361    stp d8, d9,   [sp, #(0 + \offset)]
362    stp d10, d11, [sp, #(16 + \offset)]
363    stp d12, d13, [sp, #(32 + \offset)]
364    stp d14, d15, [sp, #(48 + \offset)]
365
366    // GP callee-saves
367    SAVE_TWO_REGS x19, x20, (64 + \offset)
368    SAVE_TWO_REGS x21, x22, (80 + \offset)
369    SAVE_TWO_REGS x23, x24, (96 + \offset)
370    SAVE_TWO_REGS x25, x26, (112 + \offset)
371    SAVE_TWO_REGS x27, x28, (128 + \offset)
372    SAVE_TWO_REGS x29, xLR, (144 + \offset)
373.endm
374
375    /*
376     * Macro that sets up the callee save frame to conform with
377     * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves)
378     */
379.macro SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
380    // art::Runtime* xIP0 = art::Runtime::instance_;
381    // Our registers aren't intermixed - just spill in order.
382    LOAD_RUNTIME_INSTANCE xIP0
383
384    // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveAllCalleeSaves];
385    ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET]
386
387    INCREASE_FRAME 176
388
389    // Ugly compile-time check, but we only have the preprocessor.
390#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 176)
391#error "FRAME_SIZE_SAVE_ALL_CALLEE_SAVES(ARM64) size not as expected."
392#endif
393
394    // Stack alignment filler [sp, #8].
395    SAVE_ALL_CALLEE_SAVES 16
396
397    // Store ArtMethod* Runtime::callee_save_methods_[kSaveAllCalleeSaves].
398    str xIP0, [sp]
399    // Place sp in Thread::Current()->top_quick_frame.
400    mov xIP0, sp
401    str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
402.endm
403
404    /*
405     * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
406     * exception is Thread::Current()->exception_ when the runtime method frame is ready.
407     */
408.macro DELIVER_PENDING_EXCEPTION_FRAME_READY
409    mov x0, xSELF
410
411    // Point of no return.
412    bl artDeliverPendingExceptionFromCode  // artDeliverPendingExceptionFromCode(Thread*)
413    bl art_quick_do_long_jump              // (Context*)
414    brk 0  // Unreached
415.endm
416
417    /*
418     * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
419     * exception is Thread::Current()->exception_.
420     */
421.macro DELIVER_PENDING_EXCEPTION
422    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
423    DELIVER_PENDING_EXCEPTION_FRAME_READY
424.endm
425
426.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg
427    ldr \reg, [xSELF, # THREAD_EXCEPTION_OFFSET]   // Get exception field.
428    cbnz \reg, 1f
429    ret
4301:
431    DELIVER_PENDING_EXCEPTION
432.endm
433
434.macro RETURN_OR_DELIVER_PENDING_EXCEPTION
435    RETURN_OR_DELIVER_PENDING_EXCEPTION_REG xIP0
436.endm
437
438// Locking is needed for both managed code and JNI stubs.
439.macro LOCK_OBJECT_FAST_PATH obj, slow_lock, can_be_null
440    // Use scratch registers x8-x11 as temporaries.
441    ldr    w9, [xSELF, #THREAD_ID_OFFSET]
442    .if \can_be_null
443        cbz    \obj, \slow_lock
444    .endif
445                                      // Exclusive load/store has no immediate anymore.
446    add    x8, \obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET
4471:
448    ldaxr  w10, [x8]                  // Acquire needed only in most common case.
449    eor    w11, w10, w9               // Prepare the value to store if unlocked
450                                      //   (thread id, count of 0 and preserved read barrier bits),
451                                      // or prepare to compare thread id for recursive lock check
452                                      //   (lock_word.ThreadId() ^ self->ThreadId()).
453    tst    w10, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // Test the non-gc bits.
454    b.ne   2f                         // Check if unlocked.
455    // Unlocked case - store w11: original lock word plus thread id, preserved read barrier bits.
456    stxr   w10, w11, [x8]
457    cbnz   w10, 1b                    // If the store failed, retry.
458    ret
4592:  // w10: original lock word, w9: thread id, w11: w10 ^ w9
460                                      // Check lock word state and thread id together,
461    tst    w11, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED)
462    b.ne   \slow_lock
463    add    w11, w10, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // Increment the recursive lock count.
464    tst    w11, #LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED  // Test the new thin lock count.
465    b.eq   \slow_lock                 // Zero as the new count indicates overflow, go slow path.
466    stxr   w10, w11, [x8]
467    cbnz   w10, 1b                    // If the store failed, retry.
468    ret
469.endm
470
471// Unlocking is needed for both managed code and JNI stubs.
472.macro UNLOCK_OBJECT_FAST_PATH obj, slow_unlock, can_be_null
473    // Use scratch registers x8-x11 as temporaries.
474    ldr    w9, [xSELF, #THREAD_ID_OFFSET]
475    .if \can_be_null
476        cbz    \obj, \slow_unlock
477    .endif
478                                      // Exclusive load/store has no immediate anymore.
479    add    x8, \obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET
4801:
481#ifndef USE_READ_BARRIER
482    ldr    w10, [x8]
483#else
484    ldxr   w10, [x8]                  // Need to use atomic instructions for read barrier.
485#endif
486    eor    w11, w10, w9               // Prepare the value to store if simply locked
487                                      //   (mostly 0s, and preserved read barrier bits),
488                                      // or prepare to compare thread id for recursive lock check
489                                      //   (lock_word.ThreadId() ^ self->ThreadId()).
490    tst    w11, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  // Test the non-gc bits.
491    b.ne   2f                         // Locked recursively or by other thread?
492    // Transition to unlocked.
493#ifndef USE_READ_BARRIER
494    stlr   w11, [x8]
495#else
496    stlxr  w10, w11, [x8]             // Need to use atomic instructions for read barrier.
497    cbnz   w10, 1b                    // If the store failed, retry.
498#endif
499    ret
5002:
501                                      // Check lock word state and thread id together.
502    tst    w11, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED)
503    b.ne   \slow_unlock
504    sub    w11, w10, #LOCK_WORD_THIN_LOCK_COUNT_ONE  // decrement count
505#ifndef USE_READ_BARRIER
506    str    w11, [x8]
507#else
508    stxr   w10, w11, [x8]             // Need to use atomic instructions for read barrier.
509    cbnz   w10, 1b                    // If the store failed, retry.
510#endif
511    ret
512.endm
513
514#endif  // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
515