/* ARM support code for fibers and multithreading.
Copyright (C) 2019-2020 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#include "../common/threadasm.S"
#if defined(__ARM_EABI__)
/**
* Performs a context switch.
*
* Parameters:
* r0 - void** - ptr to old stack pointer
* r1 - void* - new stack pointer
*
* ARM EABI registers:
* r0-r3 : argument/scratch registers
* r4-r10 : callee-save registers
* r11 : frame pointer (or a callee save register if fp isn't needed)
* r12 =ip : inter procedure register. We can treat it like any other scratch
* register
* r13 =sp : stack pointer
* r14 =lr : link register, it contains the return address (belonging to the
* function which called us)
* r15 =pc : program counter
*
* For floating point registers:
* According to AAPCS (version 2.09, section 5.1.2) only the d8-d15 registers
* need to be preserved across method calls. This applies to all ARM FPU
* variants, whether they have 16 or 32 double registers NEON support or not,
* half-float support or not and so on does not matter.
*
* Note: If this file was compiled with -mfloat-abi=soft but the code runs on a
* softfp system with fpu the d8-d15 registers won't be saved (we do not know
* that the system has got a fpu in that case) but the registers might actually
* be used by other code if it was compiled with -mfloat-abi=softfp.
*
* Interworking is only supported on ARMv5+, not on ARM v4T as ARM v4t requires
* special stubs when changing from thumb to arm mode or the other way round.
*/
.text
#if defined(__ARM_PCS_VFP) || (defined(__ARM_PCS) && !defined(__SOFTFP__))
.fpu vfp
#endif
.global CSYM(fiber_switchContext)
.type CSYM(fiber_switchContext), %function
.align 4
CSYM(fiber_switchContext):
.cfi_sections .debug_frame
.cfi_startproc
.fnstart
push {r4-r11}
// update the oldp pointer. Link register and floating point registers
// stored later to prevent the GC from scanning them.
str sp, [r0]
// push r0 (or any other register) as well to keep stack 8byte aligned
push {r0, lr}
// ARM_HardFloat || ARM_SoftFP
#if defined(__ARM_PCS_VFP) || (defined(__ARM_PCS) && !defined(__SOFTFP__))
vpush {d8-d15}
// now switch over to the new stack.
// Need to subtract (8*8[d8-d15]+2*4[r0, lr]) to position stack pointer
// below the last saved register. Remember we saved the SP before pushing
// [r0, lr, d8-d15].
sub sp, r1, #72
vpop {d8-d15}
#else
sub sp, r1, #8
#endif
// we don't really care about r0, we only used that for padding.
// r1 is now what used to be in the link register when saving.
pop {r0, r1, r4-r11}
/**
* The link register for the initial jump to fiber_entryPoint must be zero:
* The jump actually looks like a normal method call as we jump to the
* start of the fiber_entryPoint function. Although fiber_entryPoint never
* returns and therefore never accesses lr, it saves lr to the stack.
* ARM unwinding will then look at the stack, find lr and think that
* fiber_entryPoint was called by the function in lr! So if we have some
* address in lr the unwinder will try to continue stack unwinding,
* although it's already at the stack base and crash.
* In all other cases the content of lr doesn't matter.
* Note: If we simply loaded into lr above and then moved lr into pc, the
* initial method call to fiber_entryPoint would look as if it was called
* from fiber_entryPoint itself, as the fiber_entryPoint address is in lr
* on the initial context switch.
*/
mov lr, #0
// return by writing lr into pc
mov pc, r1
.fnend
.cfi_endproc
.size CSYM(fiber_switchContext),.-CSYM(fiber_switchContext)
#endif