Home | History | Annotate | Line # | Download | only in xtensa
      1 /* Assembly functions for libgcc2.
      2    Copyright (C) 2001-2024 Free Software Foundation, Inc.
      3    Contributed by Bob Wilson (bwilson (at) tensilica.com) at Tensilica.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 #include "xtensa-config-builtin.h"
     27 
     28 /* An executable stack is *not* required for these functions.  */
     29 #if defined(__ELF__) && defined(__linux__)
     30 .section .note.GNU-stack,"",%progbits
     31 .previous
     32 #endif
     33 
     34 /* __xtensa_libgcc_window_spill: This function flushes out all but the
     35    current register window.  This is used to set up the stack so that
     36    arbitrary frames can be accessed.  */
     37 
     38 #if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
     39 	.align	4
     40 	.global	__xtensa_libgcc_window_spill
     41 	.type	__xtensa_libgcc_window_spill,@function
     42 __xtensa_libgcc_window_spill:
     43 	entry	sp, 48
     44 #if XCHAL_NUM_AREGS > 16
     45 	call12	1f
     46 	retw
     47 	.align	4
     48 1:
     49 	.rept	(XCHAL_NUM_AREGS - 24) / 12
     50 	_entry	sp, 48
     51 	mov	a12, a0
     52 	.endr
     53 	_entry	sp, 16
     54 #if XCHAL_NUM_AREGS % 12 == 0
     55 	mov	a4, a4
     56 #elif XCHAL_NUM_AREGS % 12 == 4
     57 	mov	a8, a8
     58 #elif XCHAL_NUM_AREGS % 12 == 8
     59 	mov	a12, a12
     60 #endif
     61 	retw
     62 #else
     63 	mov	a8, a8
     64 	retw
     65 #endif
     66 	.size	__xtensa_libgcc_window_spill, .-__xtensa_libgcc_window_spill
     67 #endif
     68 
     69 
     70 /* __xtensa_nonlocal_goto: This code does all the hard work of a
     71    nonlocal goto on Xtensa.  It is here in the library to avoid the
     72    code size bloat of generating it in-line.  There are two
     73    arguments:
     74 
     75 	a2 = frame pointer for the procedure containing the label
     76 	a3 = goto handler address
     77 
     78   This function never returns to its caller but instead goes directly
     79   to the address of the specified goto handler.  */
     80 
     81 #if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
     82 	.align	4
     83 	.global	__xtensa_nonlocal_goto
     84 	.type	__xtensa_nonlocal_goto,@function
     85 __xtensa_nonlocal_goto:
     86 	entry	sp, 32
     87 
     88 	/* Flush registers.  */
     89 	call8	__xtensa_libgcc_window_spill
     90 
     91 	/* Because the save area for a0-a3 is stored one frame below
     92 	   the one identified by a2, the only way to restore those
     93 	   registers is to unwind the stack.  If alloca() were never
     94 	   called, we could just unwind until finding the sp value
     95 	   matching a2.  However, a2 is a frame pointer, not a stack
     96 	   pointer, and may not be encountered during the unwinding.
     97 	   The solution is to unwind until going _past_ the value
     98 	   given by a2.  This involves keeping three stack pointer
     99 	   values during the unwinding:
    100 
    101 		next = sp of frame N-1
    102 		cur = sp of frame N
    103 		prev = sp of frame N+1
    104 
    105 	   When next > a2, the desired save area is stored relative
    106 	   to prev.  At this point, cur will be the same as a2
    107 	   except in the alloca() case.
    108 
    109 	   Besides finding the values to be restored to a0-a3, we also
    110 	   need to find the current window size for the target
    111 	   function.  This can be extracted from the high bits of the
    112 	   return address, initially in a0.  As the unwinding
    113 	   proceeds, the window size is taken from the value of a0
    114 	   saved _two_ frames below the current frame.  */
    115 
    116 	addi	a5, sp, -16	/* a5 = prev - save area */
    117 	l32i	a6, a5, 4
    118 	addi	a6, a6, -16	/* a6 = cur - save area */
    119 	mov	a8, a0		/* a8 = return address (for window size) */
    120 	j	.Lfirstframe
    121 
    122 .Lnextframe:
    123 	l32i	a8, a5, 0	/* next return address (for window size) */
    124 	mov	a5, a6		/* advance prev */
    125 	addi	a6, a7, -16	/* advance cur */
    126 .Lfirstframe:
    127 	l32i	a7, a6, 4	/* a7 = next */
    128 	bgeu	a2, a7, .Lnextframe
    129 
    130 	/* At this point, prev (a5) points to the save area with the saved
    131 	   values of a0-a3.  Copy those values into the save area at the
    132 	   current sp so they will be reloaded when the return from this
    133 	   function underflows.  We don't have to worry about exceptions
    134 	   while updating the current save area, because the windows have
    135 	   already been flushed.  */
    136 
    137 	addi	a4, sp, -16	/* a4 = save area of this function */
    138 	l32i	a6, a5, 0
    139 	l32i	a7, a5, 4
    140 	s32i	a6, a4, 0
    141 	s32i	a7, a4, 4
    142 	l32i	a6, a5, 8
    143 	l32i	a7, a5, 12
    144 	s32i	a6, a4, 8
    145 	s32i	a7, a4, 12
    146 
    147 	/* Set return address to goto handler.  Use the window size bits
    148 	   from the return address two frames below the target.  */
    149 	extui	a8, a8, 30, 2	/* get window size from return addr. */
    150 	slli	a3, a3, 2	/* get goto handler addr. << 2 */
    151 	ssai	2
    152 	src	a0, a8, a3	/* combine them with a funnel shift */
    153 
    154 	retw
    155 	.size	__xtensa_nonlocal_goto, .-__xtensa_nonlocal_goto
    156 #endif
    157 
    158 
    159 /* __xtensa_sync_caches: This function is called after writing a trampoline
    160    on the stack to force all the data writes to memory and invalidate the
    161    instruction cache. a2 is the address of the new trampoline.
    162 
    163    After the trampoline data is written out, it must be flushed out of
    164    the data cache into memory.  We use DHWB in case we have a writeback
    165    cache.  At least one DHWB instruction is needed for each data cache
    166    line which may be touched by the trampoline.  An ISYNC instruction
    167    must follow the DHWBs.
    168 
    169    We have to flush the i-cache to make sure that the new values get used.
    170    At least one IHI instruction is needed for each i-cache line which may
    171    be touched by the trampoline.  An ISYNC instruction is also needed to
    172    make sure that the modified instructions are loaded into the instruction
    173    fetch buffer.  */
    174 
    175 /* Use the maximum trampoline size.  Flushing a bit extra is OK.  */
    176 #define TRAMPOLINE_SIZE 60
    177 
    178 	.text
    179 	.align	4
    180 	.global	__xtensa_sync_caches
    181 	.type	__xtensa_sync_caches,@function
    182 __xtensa_sync_caches:
    183 #if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
    184 	entry 	sp, 32
    185 #endif
    186 #if XCHAL_DCACHE_SIZE > 0
    187 	/* Flush the trampoline from the data cache.  */
    188 	extui	a4, a2, 0, XCHAL_DCACHE_LINEWIDTH
    189 	addi	a4, a4, TRAMPOLINE_SIZE
    190 	addi	a4, a4, (1 << XCHAL_DCACHE_LINEWIDTH) - 1
    191 	srli	a4, a4, XCHAL_DCACHE_LINEWIDTH
    192 	mov	a3, a2
    193 .Ldcache_loop:
    194 	dhwb	a3, 0
    195 	addi	a3, a3, (1 << XCHAL_DCACHE_LINEWIDTH)
    196 	addi	a4, a4, -1
    197 	bnez	a4, .Ldcache_loop
    198 	isync
    199 #endif
    200 #if XCHAL_ICACHE_SIZE > 0
    201 	/* Invalidate the corresponding lines in the instruction cache.  */
    202 	extui	a4, a2, 0, XCHAL_ICACHE_LINEWIDTH
    203 	addi	a4, a4, TRAMPOLINE_SIZE
    204 	addi	a4, a4, (1 << XCHAL_ICACHE_LINEWIDTH) - 1
    205 	srli	a4, a4, XCHAL_ICACHE_LINEWIDTH
    206 .Licache_loop:
    207 	ihi	a2, 0
    208 	addi	a2, a2, (1 << XCHAL_ICACHE_LINEWIDTH)
    209 	addi	a4, a4, -1
    210 	bnez	a4, .Licache_loop
    211 #endif
    212 	isync
    213 #if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
    214 	retw
    215 #else
    216 	ret
    217 #endif
    218 	.size	__xtensa_sync_caches, .-__xtensa_sync_caches
    219