1/* ARM support code for fibers and multithreading.
2   Copyright (C) 2019-2020 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14for more details.
15
16Under Section 7 of GPL version 3, you are granted additional
17permissions described in the GCC Runtime Library Exception, version
183.1, as published by the Free Software Foundation.
19
20You should have received a copy of the GNU General Public License and
21a copy of the GCC Runtime Library Exception along with this program;
22see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23<http://www.gnu.org/licenses/>.  */
24
25#include "../common/threadasm.S"
26
27#if defined(__ARM_EABI__)
28
29/**
30 * Performs a context switch.
31 *
32 * Parameters:
33 * r0 - void** - ptr to old stack pointer
34 * r1 - void*  - new stack pointer
35 *
36 * ARM EABI registers:
37 * r0-r3   : argument/scratch registers
38 * r4-r10  : callee-save registers
39 * r11     : frame pointer (or a callee save register if fp isn't needed)
40 * r12 =ip : inter procedure register. We can treat it like any other scratch
41 *           register
42 * r13 =sp : stack pointer
43 * r14 =lr : link register, it contains the return address (belonging to the
44 *           function which called us)
45 * r15 =pc : program counter
46 *
47 * For floating point registers:
48 * According to AAPCS (version 2.09, section 5.1.2) only the d8-d15 registers
49 * need to be preserved across method calls. This applies to all ARM FPU
50 * variants, whether they have 16 or 32 double registers NEON support or not,
51 * half-float support or not and so on does not matter.
52 *
53 * Note: If this file was compiled with -mfloat-abi=soft but the code runs on a
54 * softfp system with fpu the d8-d15 registers won't be saved (we do not know
55 * that the system has got a fpu in that case) but the registers might actually
56 * be used by other code if it was compiled with -mfloat-abi=softfp.
57 *
58 * Interworking is only supported on ARMv5+, not on ARM v4T as ARM v4t requires
59 * special stubs when changing from thumb to arm mode or the other way round.
60 */
61
62    .text
63#if defined(__ARM_PCS_VFP) || (defined(__ARM_PCS) && !defined(__SOFTFP__))
64    .fpu vfp
65#endif
66    .global CSYM(fiber_switchContext)
67    .type CSYM(fiber_switchContext), %function
68    .align 4
69CSYM(fiber_switchContext):
70    .cfi_sections .debug_frame
71    .cfi_startproc
72    .fnstart
73    push {r4-r11}
74    // update the oldp pointer. Link register and floating point registers
75    // stored later to prevent the GC from scanning them.
76    str sp, [r0]
77    // push r0 (or any other register) as well to keep stack 8byte aligned
78    push {r0, lr}
79
80     // ARM_HardFloat  || ARM_SoftFP
81#if defined(__ARM_PCS_VFP) || (defined(__ARM_PCS) && !defined(__SOFTFP__))
82    vpush {d8-d15}
83    // now switch over to the new stack.
84    // Need to subtract (8*8[d8-d15]+2*4[r0, lr]) to position stack pointer
85    // below the last saved register. Remember we saved the SP before pushing
86    // [r0, lr, d8-d15].
87    sub sp, r1, #72
88    vpop {d8-d15}
89#else
90    sub sp, r1, #8
91#endif
92
93    // we don't really care about r0, we only used that for padding.
94    // r1 is now what used to be in the link register when saving.
95    pop {r0, r1, r4-r11}
96    /**
97     * The link register for the initial jump to fiber_entryPoint must be zero:
98     * The jump actually looks like a normal method call as we jump to the
99     * start of the fiber_entryPoint function. Although fiber_entryPoint never
100     * returns and therefore never accesses lr, it saves lr to the stack.
101     * ARM unwinding will then look at the stack, find lr and think that
102     * fiber_entryPoint was called by the function in lr! So if we have some
103     * address in lr the unwinder will try to continue stack unwinding,
104     * although it's already at the stack base and crash.
105     * In all other cases the content of lr doesn't matter.
106     * Note: If we simply loaded into lr above and then moved lr into pc, the
107     * initial method call to fiber_entryPoint would look as if it was called
108     * from fiber_entryPoint itself, as the fiber_entryPoint address is in lr
109     * on the initial context switch.
110     */
111    mov lr, #0
112    // return by writing lr into pc
113    mov pc, r1
114    .fnend
115    .cfi_endproc
116    .size CSYM(fiber_switchContext),.-CSYM(fiber_switchContext)
117
118#endif
119