1 /* ARM support code for fibers and multithreading. 2 Copyright (C) 2019-2022 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 6 GCC is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free 8 Software Foundation; either version 3, or (at your option) any later 9 version. 10 11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 for more details. 15 16 Under Section 7 of GPL version 3, you are granted additional 17 permissions described in the GCC Runtime Library Exception, version 18 3.1, as published by the Free Software Foundation. 19 20 You should have received a copy of the GNU General Public License and 21 a copy of the GCC Runtime Library Exception along with this program; 22 see 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 69 CSYM(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