Home | History | Annotate | Line # | Download | only in arm
      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