xref: /aosp_15_r20/art/runtime/arch/arm/asm_support_arm.S (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1/*
2 * Copyright (C) 2013 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_ARM_ASM_SUPPORT_ARM_S_
18#define ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_S_
19
20#include "asm_support_arm.h"
21#include "interpreter/cfi_asm_support.h"
22
23// Define special registers.
24
25// Register holding suspend check count down.
26#define rSUSPEND r4
27// Register holding Thread::Current().
28#define rSELF r9
29
30#ifdef RESERVE_MARKING_REGISTER
31// Marking Register, holding Thread::Current()->GetIsGcMarking().
32#define rMR r8
33#endif
34
35.syntax unified
36.arch armv7-a
37.arch_extension idiv
38.thumb
39
40.macro CFI_EXPRESSION_BREG n, b, offset
41    .if (-0x40 <= (\offset)) && ((\offset) < 0x40)
42        CFI_EXPRESSION_BREG_1(\n, \b, \offset)
43    .elseif (-0x2000 <= (\offset)) && ((\offset) < 0x2000)
44        CFI_EXPRESSION_BREG_2(\n, \b, \offset)
45    .else
46        .error "Unsupported offset"
47    .endif
48.endm
49
50.macro CFI_DEF_CFA_BREG_PLUS_UCONST reg, offset, size
51    .if ((\size) < 0)
52        .error "Size should be positive"
53    .endif
54    .if (((\offset) < -0x40) || ((\offset) >= 0x40))
55        .error "Unsupported offset"
56    .endif
57    .if ((\size) < 0x80)
58        CFI_DEF_CFA_BREG_PLUS_UCONST_1_1(\reg, \offset, \size)
59    .elseif ((\size) < 0x4000)
60        CFI_DEF_CFA_BREG_PLUS_UCONST_1_2(\reg, \offset, \size)
61    .else
62        .error "Unsupported size"
63    .endif
64.endm
65
66.macro CFI_REMEMBER_STATE
67    .cfi_remember_state
68.endm
69
70// The spec is not clear whether the CFA is part of the saved state and tools
71// differ in the behaviour, so explicitly set the CFA to avoid any ambiguity.
72// The restored CFA state should match the CFA state during CFI_REMEMBER_STATE.
73.macro CFI_RESTORE_STATE_AND_DEF_CFA reg, offset
74    .cfi_restore_state
75    .cfi_def_cfa \reg, \offset
76.endm
77
78// Common ENTRY declaration code for ARM and thumb, an ENTRY should always be paired with an END.
79.macro DEF_ENTRY thumb_or_arm, name, alignment
80    \thumb_or_arm
81// Clang ignores .thumb_func and requires an explicit .thumb. Investigate whether we should still
82// carry around the .thumb_func.
83    .ifc \thumb_or_arm, .thumb_func
84        .thumb
85    .endif
86    .type \name, #function
87    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
88    .global \name
89    .balign \alignment
90\name:
91    .cfi_startproc
92    .fnstart
93.endm
94
95// A thumb2 style ENTRY.
96.macro ENTRY name
97    DEF_ENTRY .thumb_func, \name, 16
98.endm
99.macro ENTRY_ALIGNED name, alignment
100    DEF_ENTRY .thumb_func, \name, \alignment
101.endm
102
103// A ARM style ENTRY.
104.macro ARM_ENTRY name
105    DEF_ENTRY .arm, \name, 16
106.endm
107
108// Terminate an ENTRY.
109.macro END name
110    .fnend
111    .cfi_endproc
112    .size \name, .-\name
113.endm
114
115// Declare an unimplemented ENTRY that will halt a debugger.
116.macro UNIMPLEMENTED name
117    ENTRY \name
118    bkpt
119    bkpt
120    END \name
121.endm
122
123// Macro to poison (negate) the reference for heap poisoning.
124.macro POISON_HEAP_REF rRef
125#ifdef USE_HEAP_POISONING
126    rsb \rRef, \rRef, #0
127#endif  // USE_HEAP_POISONING
128.endm
129
130// Macro to unpoison (negate) the reference for heap poisoning.
131.macro UNPOISON_HEAP_REF rRef
132#ifdef USE_HEAP_POISONING
133    rsb \rRef, \rRef, #0
134#endif  // USE_HEAP_POISONING
135.endm
136
137.macro INCREASE_FRAME frame_adjustment
138    sub sp, sp, #(\frame_adjustment)
139    .cfi_adjust_cfa_offset (\frame_adjustment)
140.endm
141
142.macro DECREASE_FRAME frame_adjustment
143    add sp, sp, #(\frame_adjustment)
144    .cfi_adjust_cfa_offset -(\frame_adjustment)
145.endm
146
147.macro LOAD_RUNTIME_INSTANCE rDest
148    movw \rDest, #:lower16:(_ZN3art7Runtime9instance_E - (. + 12))
149    movt \rDest, #:upper16:(_ZN3art7Runtime9instance_E - (. + 8))
150    add \rDest, pc
151    ldr \rDest, [\rDest]
152.endm
153
154// Macro to refresh the Marking Register (R8).
155//
156// This macro must be called at the end of functions implementing
157// entrypoints that possibly (directly or indirectly) perform a
158// suspend check (before they return).
159.macro REFRESH_MARKING_REGISTER
160#ifdef RESERVE_MARKING_REGISTER
161    ldr rMR, [rSELF, #THREAD_IS_GC_MARKING_OFFSET]
162#endif
163.endm
164
165.macro CONDITIONAL_CBZ reg, reg_if, dest
166.ifc \reg, \reg_if
167    cbz \reg, \dest
168.endif
169.endm
170
171.macro CONDITIONAL_CMPBZ reg, reg_if, dest
172.ifc \reg, \reg_if
173    cmp \reg, #0
174    beq \dest
175.endif
176.endm
177
178// Use CBZ if the register is in {r0, r7} otherwise compare and branch.
179.macro SMART_CBZ reg, dest
180    CONDITIONAL_CBZ \reg, r0, \dest
181    CONDITIONAL_CBZ \reg, r1, \dest
182    CONDITIONAL_CBZ \reg, r2, \dest
183    CONDITIONAL_CBZ \reg, r3, \dest
184    CONDITIONAL_CBZ \reg, r4, \dest
185    CONDITIONAL_CBZ \reg, r5, \dest
186    CONDITIONAL_CBZ \reg, r6, \dest
187    CONDITIONAL_CBZ \reg, r7, \dest
188    CONDITIONAL_CMPBZ \reg, r8, \dest
189    CONDITIONAL_CMPBZ \reg, r9, \dest
190    CONDITIONAL_CMPBZ \reg, r10, \dest
191    CONDITIONAL_CMPBZ \reg, r11, \dest
192    CONDITIONAL_CMPBZ \reg, r12, \dest
193    CONDITIONAL_CMPBZ \reg, r13, \dest
194    CONDITIONAL_CMPBZ \reg, r14, \dest
195    CONDITIONAL_CMPBZ \reg, r15, \dest
196.endm
197
198    /*
199     * Macro that sets up the callee save frame to conform with
200     * Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs), except for storing the method.
201     */
202.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY
203    // Note: We could avoid saving R8 in the case of Baker read
204    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
205    // later; but it's not worth handling this special case.
206    push {r1-r3, r5-r8, r10-r11, lr}   @ 10 words of callee saves and args.
207    .cfi_adjust_cfa_offset 40
208    .cfi_rel_offset r5, 12
209    .cfi_rel_offset r6, 16
210    .cfi_rel_offset r7, 20
211    .cfi_rel_offset r8, 24
212    .cfi_rel_offset r10, 28
213    .cfi_rel_offset r11, 32
214    .cfi_rel_offset lr, 36
215    vpush {s0-s15}                     @ 16 words of float args.
216    .cfi_adjust_cfa_offset 64
217    sub sp, #8                         @ 2 words of space, alignment padding and Method*
218    .cfi_adjust_cfa_offset 8
219    // Ugly compile-time check, but we only have the preprocessor.
220#if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 40 + 64 + 8)
221#error "FRAME_SIZE_SAVE_REFS_AND_ARGS(ARM) size not as expected."
222#endif
223.endm
224
225.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME
226    add  sp, #8                      @ rewind sp
227    .cfi_adjust_cfa_offset -8
228    vpop {s0-s15}
229    .cfi_adjust_cfa_offset -64
230    // Note: Likewise, we could avoid restoring R8 in the case of Baker
231    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
232    // later; but it's not worth handling this special case.
233    pop {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves and args.
234    .cfi_restore r5
235    .cfi_restore r6
236    .cfi_restore r7
237    .cfi_restore r8
238    .cfi_restore r10
239    .cfi_restore r11
240    .cfi_restore lr
241    .cfi_adjust_cfa_offset -40
242.endm
243
244    /*
245     * Macro to spill the GPRs.
246     */
247.macro SPILL_ALL_CALLEE_SAVE_GPRS
248    push {r4-r11, lr}                             @ 9 words (36 bytes) of callee saves.
249    .cfi_adjust_cfa_offset 36
250    .cfi_rel_offset r4, 0
251    .cfi_rel_offset r5, 4
252    .cfi_rel_offset r6, 8
253    .cfi_rel_offset r7, 12
254    .cfi_rel_offset r8, 16
255    .cfi_rel_offset r9, 20
256    .cfi_rel_offset r10, 24
257    .cfi_rel_offset r11, 28
258    .cfi_rel_offset lr, 32
259.endm
260
261    /*
262     * Macro that sets up the callee save frame to conform with
263     * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves)
264     */
265.macro SETUP_SAVE_ALL_CALLEE_SAVES_FRAME rTemp
266    SPILL_ALL_CALLEE_SAVE_GPRS                    @ 9 words (36 bytes) of callee saves.
267    vpush {s16-s31}                               @ 16 words (64 bytes) of floats.
268    .cfi_adjust_cfa_offset 64
269    sub sp, #12                                   @ 3 words of space, bottom word will hold Method*
270    .cfi_adjust_cfa_offset 12
271    LOAD_RUNTIME_INSTANCE \rTemp                  @ Load Runtime::Current into rTemp.
272    @ Load kSaveAllCalleeSaves Method* into rTemp.
273    ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET]
274    str \rTemp, [sp, #0]                          @ Place Method* at bottom of stack.
275    str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
276
277     // Ugly compile-time check, but we only have the preprocessor.
278#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 36 + 64 + 12)
279#error "FRAME_SIZE_SAVE_ALL_CALLEE_SAVES(ARM) size not as expected."
280#endif
281.endm
282
283    /*
284     * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
285     * exception is Thread::Current()->exception_ when the runtime method frame is ready.
286     */
287.macro DELIVER_PENDING_EXCEPTION_FRAME_READY
288    mov    r0, rSELF                           @ pass Thread::Current
289    bl     artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*)
290    bl art_quick_do_long_jump                  @ (Context*)
291    bkpt  // Unreached
292.endm
293
294    /*
295     * Macro that calls through to artDeliverPendingExceptionFromCode, where the pending
296     * exception is Thread::Current()->exception_.
297     */
298.macro DELIVER_PENDING_EXCEPTION
299    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0       @ save callee saves for throw
300    DELIVER_PENDING_EXCEPTION_FRAME_READY
301.endm
302
303.macro  RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg
304    ldr \reg, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ Get exception field.
305    cbnz \reg, 1f
306    bx lr
3071:
308    DELIVER_PENDING_EXCEPTION
309.endm
310
311.macro  RETURN_OR_DELIVER_PENDING_EXCEPTION
312    ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET]  @ Get exception field.
313    cmp ip, #0
314    bne 1f
315    bx lr
3161:
317    DELIVER_PENDING_EXCEPTION
318.endm
319
320    /*
321     * Macro that sets up the callee save frame to conform with
322     * Runtime::CreateCalleeSaveMethod(kSaveRefsOnly).
323     */
324.macro SETUP_SAVE_REFS_ONLY_FRAME rTemp
325    // Note: We could avoid saving R8 in the case of Baker read
326    // barriers, as it is overwritten by REFRESH_MARKING_REGISTER
327    // later; but it's not worth handling this special case.
328    push {r5-r8, r10-r11, lr}                     @ 7 words of callee saves
329    .cfi_adjust_cfa_offset 28
330    .cfi_rel_offset r5, 0
331    .cfi_rel_offset r6, 4
332    .cfi_rel_offset r7, 8
333    .cfi_rel_offset r8, 12
334    .cfi_rel_offset r10, 16
335    .cfi_rel_offset r11, 20
336    .cfi_rel_offset lr, 24
337    sub sp, #4                                    @ bottom word will hold Method*
338    .cfi_adjust_cfa_offset 4
339    LOAD_RUNTIME_INSTANCE \rTemp                  @ Load Runtime::Current into rTemp.
340    @ Load kSaveRefsOnly Method* into rTemp.
341    ldr \rTemp, [\rTemp, #RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET]
342    str \rTemp, [sp, #0]                          @ Place Method* at bottom of stack.
343    str sp, [rSELF, #THREAD_TOP_QUICK_FRAME_OFFSET]  @ Place sp in Thread::Current()->top_quick_frame.
344
345    // Ugly compile-time check, but we only have the preprocessor.
346#if (FRAME_SIZE_SAVE_REFS_ONLY != 28 + 4)
347#error "FRAME_SIZE_SAVE_REFS_ONLY(ARM) size not as expected."
348#endif
349.endm
350
351.macro RESTORE_SAVE_REFS_ONLY_FRAME
352    add sp, #4               @ bottom word holds Method*
353    .cfi_adjust_cfa_offset -4
354    // Note: Likewise, we could avoid restoring R8 in the case of Baker
355    // read barriers, as it is overwritten by REFRESH_MARKING_REGISTER
356    // later; but it's not worth handling this special case.
357    pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
358    .cfi_restore r5
359    .cfi_restore r6
360    .cfi_restore r7
361    .cfi_restore r8
362    .cfi_restore r10
363    .cfi_restore r11
364    .cfi_restore lr
365    .cfi_adjust_cfa_offset -28
366.endm
367
368// Locking is needed for both managed code and JNI stubs.
369.macro LOCK_OBJECT_FAST_PATH obj, tmp1, tmp2, tmp3, slow_lock, can_be_null
370    ldr    \tmp1, [rSELF, #THREAD_ID_OFFSET]
371    .if \can_be_null
372        cbz \obj, \slow_lock
373    .endif
3741:
375    ldrex  \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
376    eor    \tmp3, \tmp2, \tmp1        @ Prepare the value to store if unlocked
377                                      @   (thread id, count of 0 and preserved read barrier bits),
378                                      @ or prepare to compare thread id for recursive lock check
379                                      @   (lock_word.ThreadId() ^ self->ThreadId()).
380    ands   ip, \tmp2, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ Test the non-gc bits.
381    bne    2f                         @ Check if unlocked.
382    @ unlocked case - store tmp3: original lock word plus thread id, preserved read barrier bits.
383    strex  \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
384    cbnz   \tmp2, 3f                   @ If store failed, retry.
385    dmb    ish                        @ Full (LoadLoad|LoadStore) memory barrier.
386    bx lr
3872:  @ tmp2: original lock word, tmp1: thread_id, tmp3: tmp2 ^ tmp1
388#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT
389#error "Expecting thin lock count and gc state in consecutive bits."
390#endif
391                                      @ Check lock word state and thread id together.
392    bfc    \tmp3, \
393           #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, \
394           #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE)
395    cbnz   \tmp3, \slow_lock          @ if either of the top two bits are set, or the lock word's
396                                      @ thread id did not match, go slow path.
397    add    \tmp3, \tmp2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ Increment the recursive lock count.
398                                      @ Extract the new thin lock count for overflow check.
399    ubfx   \tmp2, \tmp3, #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, #LOCK_WORD_THIN_LOCK_COUNT_SIZE
400    cbz    \tmp2, \slow_lock          @ Zero as the new count indicates overflow, go slow path.
401                                      @ strex necessary for read barrier bits.
402    strex  \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
403    cbnz   \tmp2, 3f                  @ If strex failed, retry.
404    bx lr
4053:
406    b      1b                         @ retry
407.endm
408
409// Unlocking is needed for both managed code and JNI stubs.
410.macro UNLOCK_OBJECT_FAST_PATH obj, tmp1, tmp2, tmp3, slow_unlock, can_be_null
411    ldr    \tmp1, [rSELF, #THREAD_ID_OFFSET]
412    .if \can_be_null
413        cbz    \obj, \slow_unlock
414    .endif
4151:
416#ifndef USE_READ_BARRIER
417    ldr    \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
418#else
419                                      @ Need to use atomic instructions for read barrier.
420    ldrex  \tmp2, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
421#endif
422    eor    \tmp3, \tmp2, \tmp1        @ Prepare the value to store if simply locked
423                                      @   (mostly 0s, and preserved read barrier bits),
424                                      @ or prepare to compare thread id for recursive lock check
425                                      @   (lock_word.ThreadId() ^ self->ThreadId()).
426    ands   ip, \tmp3, #LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED  @ Test the non-gc bits.
427    bne    2f                         @ Locked recursively or by other thread?
428    @ Transition to unlocked.
429    dmb    ish                        @ Full (LoadStore|StoreStore) memory barrier.
430#ifndef USE_READ_BARRIER
431    str    \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
432#else
433                                      @ strex necessary for read barrier bits
434    strex  \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
435    cbnz   \tmp2, 3f                  @ If the store failed, retry.
436#endif
437    bx     lr
4382:  @ tmp2: original lock word, tmp1: thread_id, tmp3: tmp2 ^ tmp1
439#if LOCK_WORD_THIN_LOCK_COUNT_SHIFT + LOCK_WORD_THIN_LOCK_COUNT_SIZE != LOCK_WORD_GC_STATE_SHIFT
440#error "Expecting thin lock count and gc state in consecutive bits."
441#endif
442                                      @ Check lock word state and thread id together,
443    bfc    \tmp3, \
444           #LOCK_WORD_THIN_LOCK_COUNT_SHIFT, \
445           #(LOCK_WORD_THIN_LOCK_COUNT_SIZE + LOCK_WORD_GC_STATE_SIZE)
446    cbnz   \tmp3, \slow_unlock        @ if either of the top two bits are set, or the lock word's
447                                      @ thread id did not match, go slow path.
448    sub    \tmp3, \tmp2, #LOCK_WORD_THIN_LOCK_COUNT_ONE  @ Decrement recursive lock count.
449#ifndef USE_READ_BARRIER
450    str    \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
451#else
452                                      @ strex necessary for read barrier bits.
453    strex  \tmp2, \tmp3, [\obj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
454    cbnz   \tmp2, 3f                  @ If the store failed, retry.
455#endif
456    bx     lr
4573:
458    b      1b                         @ retry
459.endm
460
461#endif  // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_
462