This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.
| Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
|---|---|---|
| Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
| Other format: | [Raw text] | |
Changelog for part2: * config.gcc: Add xtensa-*-elf* and xtensa-*-linux* cases. * config/xtensa/elf.h: New file. * config/xtensa/lib1funcs.asm: New file. * config/xtensa/lib2funcs.S: New file. * config/xtensa/linux.h: New file. * config/xtensa/t-linux: New file. * config/xtensa/t-xtensa: New file. * config/xtensa/xm-xtensa.h: New file. * config/xtensa/xtensa-defaults.h: New file. * config/xtensa/xtensa-protos.h: New file. * config/xtensa/xtensa.c: New file. * config/xtensa/xtensa.h: New file. * config/xtensa/xtensa.md: New file.
Index: gcc/config.gcc
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config.gcc,v
retrieving revision 1.27.2.18
diff -c -3 -p -r1.27.2.18 config.gcc
*** config.gcc 2001/11/18 00:29:45 1.27.2.18
--- config.gcc 2001/11/30 17:40:59
*************** xscale-*-coff)
*** 3442,3447 ****
--- 3442,3463 ----
xm_file=arm/xm-arm.h
md_file=arm/arm.md
;;
+ xtensa-*-elf*)
+ tm_file=xtensa/elf.h
+ with_newlib=yes
+ tmake_file=xtensa/t-xtensa
+ extra_parts="crtbegin.o crtend.o"
+ fixincludes=Makefile.in # newlib headers should be OK
+ ;;
+ xtensa-*-linux*)
+ tm_file=xtensa/linux.h
+ tmake_file="t-linux xtensa/t-linux"
+ extra_parts="crtbegin.o crtend.o"
+ gas=yes gnu_ld=yes
+ if test x$enable_threads = xyes; then
+ thread_file='posix'
+ fi
+ ;;
*)
echo "Configuration $machine not supported" 1>&2
exit 1
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/elf.h gcc-3.0.2/gcc/config/xtensa/elf.h
*** gcc-3.0.2-orig/gcc/config/xtensa/elf.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/elf.h Tue Nov 27 09:30:45 2001
***************
*** 0 ****
--- 1,112 ----
+ /* Xtensa/Elf configuration.
+ Derived from the configuration for GCC for Intel i386 running Linux.
+ Copyright (C) 2001 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ /* Don't assume anything about the header files. */
+ #define NO_IMPLICIT_EXTERN_C
+
+ #undef HAVE_ATEXIT
+ #define HAVE_ATEXIT
+
+ #undef ASM_APP_ON
+ #define ASM_APP_ON "#APP\n"
+
+ #undef ASM_APP_OFF
+ #define ASM_APP_OFF "#NO_APP\n"
+
+ #define SET_ASM_OP ".set"
+
+ /* Debug format: prefer DWARF2, but also support stabs and DWARF 1 */
+ #define DBX_DEBUGGING_INFO 1
+ #define DWARF_DEBUGGING_INFO 1
+ #define DWARF2_DEBUGGING_INFO 1
+ #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+ #include "svr4.h"
+
+ #undef MD_EXEC_PREFIX
+ #undef MD_STARTFILE_PREFIX
+
+ #undef TARGET_VERSION
+ #define TARGET_VERSION fputs (" (Xtensa/ELF)", stderr);
+
+ #undef SIZE_TYPE
+ #define SIZE_TYPE "unsigned int"
+
+ #undef PTRDIFF_TYPE
+ #define PTRDIFF_TYPE "int"
+
+ #undef WCHAR_TYPE
+ #define WCHAR_TYPE "int"
+
+ #undef WCHAR_TYPE_SIZE
+ #define WCHAR_TYPE_SIZE BITS_PER_WORD
+
+ #undef ASM_SPEC
+ #define ASM_SPEC "%{v} %{mno-density:--no-density} \
+ %{mtext-section-literals:--text-section-literals} \
+ %{mno-text-section-literals:--no-text-section-literals} \
+ %{mtarget-align:--target-align} \
+ %{mno-target-align:--no-target-align} \
+ %{mlongcalls:--longcalls} \
+ %{mno-longcalls:--no-longcalls}"
+
+ #undef ASM_FINAL_SPEC
+
+ #undef LIB_SPEC
+ #define LIB_SPEC "-lc -lsim -lc -lhandlers-sim"
+
+ #undef STARTFILE_SPEC
+ #define STARTFILE_SPEC "crt1-sim%O%s crti%O%s crtbegin%O%s _vectors%O%s"
+
+ #undef ENDFILE_SPEC
+ #define ENDFILE_SPEC "crtend%O%s crtn%O%s"
+
+ #undef LINK_SPEC
+ #define LINK_SPEC "%{mno-density:--no-density}"
+
+ #undef CPP_PREDEFINES
+ #define CPP_PREDEFINES "-D__XTENSA__ -D__ELF__ -Acpu=xtensa -Amachine=xtensa"
+
+ /* Local compiler-generated symbols must have a prefix that the assembler
+ understands. By default, this is $, although some targets (e.g.,
+ NetBSD-ELF) need to override this. */
+
+ #ifndef LOCAL_LABEL_PREFIX
+ #define LOCAL_LABEL_PREFIX "."
+ #endif
+
+ /* By default, external symbols do not have an underscore prepended. */
+
+ #ifndef USER_LABEL_PREFIX
+ #define USER_LABEL_PREFIX ""
+ #endif
+
+ /* Define this macro if the assembler does not accept the character
+ "." in label names. By default constructors and destructors in G++
+ have names that use ".". If this macro is defined, these names
+ are rewritten to avoid ".". */
+ #define NO_DOT_IN_LABEL
+
+ /* Define NO_DOLLAR_IN_LABEL in your favorite tm file if your assembler
+ doesn't allow $ in symbol names. */
+ #undef NO_DOLLAR_IN_LABEL
+
+ #include <xtensa/xtensa.h>
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/lib1funcs.asm gcc-3.0.2/gcc/config/xtensa/lib1funcs.asm
*** gcc-3.0.2-orig/gcc/config/xtensa/lib1funcs.asm Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/lib1funcs.asm Mon Nov 12 16:34:25 2001
***************
*** 0 ****
--- 1,408 ----
+ /* Assembly functions for the Xtensa version of libgcc1.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include "xtensa/xtensa-defaults.h"
+
+ #ifdef L_mulsi3
+ .align 4
+ .global __mulsi3
+ .type __mulsi3,@function
+ __mulsi3:
+ entry sp, 16
+
+ #if XCHAL_HAVE_MUL16
+ or a4, a2, a3
+ srai a4, a4, 16
+ bnez a4, .LMUL16
+ mul16u a2, a2, a3
+ retw
+ .LMUL16:
+ srai a4, a2, 16
+ srai a5, a3, 16
+ mul16u a7, a4, a3
+ mul16u a6, a5, a2
+ mul16u a4, a2, a3
+ add a7, a7, a6
+ slli a7, a7, 16
+ add a2, a7, a4
+
+ #elif XCHAL_HAVE_MAC16
+ mul.aa.hl a2, a3
+ mula.aa.lh a2, a3
+ rsr a5, 16 # ACCLO
+ umul.aa.ll a2, a3
+ rsr a4, 16 # ACCLO
+ slli a5, a5, 16
+ add a2, a4, a5
+
+ #else /* !XCHAL_HAVE_MUL16 && !XCHAL_HAVE_MAC16 */
+
+ # Multiply one bit at a time, but unroll the loop 4x to better
+ # exploit the addx instructions.
+
+ # Peel the first iteration to save a cycle on init
+
+ # avoid negative numbers
+
+ xor a5, a2, a3 # top bit is 1 iff one of the inputs is negative
+ abs a3, a3
+ abs a2, a2
+
+ # swap so that second argument is smaller
+ sub a7, a2, a3
+ mov a4, a3
+ movgez a4, a2, a7 # a4 = max(a2, a3)
+ movltz a3, a2, a7 # a3 = min(a2, a3)
+
+ movi a2, 0
+ extui a6, a3, 0, 1
+ movnez a2, a4, a6
+
+ addx2 a7, a4, a2
+ extui a6, a3, 1, 1
+ movnez a2, a7, a6
+
+ addx4 a7, a4, a2
+ extui a6, a3, 2, 1
+ movnez a2, a7, a6
+
+ addx8 a7, a4, a2
+ extui a6, a3, 3, 1
+ movnez a2, a7, a6
+
+ bgeui a3, 16, .Lmult_main_loop
+ neg a3, a2
+ movltz a2, a3, a5
+ retw
+
+
+ .align 4
+ .Lmult_main_loop:
+ srli a3, a3, 4
+ slli a4, a4, 4
+
+ add a7, a4, a2
+ extui a6, a3, 0, 1
+ movnez a2, a7, a6
+
+ addx2 a7, a4, a2
+ extui a6, a3, 1, 1
+ movnez a2, a7, a6
+
+ addx4 a7, a4, a2
+ extui a6, a3, 2, 1
+ movnez a2, a7, a6
+
+ addx8 a7, a4, a2
+ extui a6, a3, 3, 1
+ movnez a2, a7, a6
+
+
+ bgeui a3, 16, .Lmult_main_loop
+
+ neg a3, a2
+ movltz a2, a3, a5
+
+ #endif /* !XCHAL_HAVE_MUL16 && !XCHAL_HAVE_MAC16 */
+
+ retw
+ .Lfe1:
+ .size __mulsi3,.Lfe1-__mulsi3
+
+ #endif /* L_mulsi3 */
+
+
+ #if !XCHAL_HAVE_NSA
+ .section .rodata
+ .align 4
+ .type countLeadingZerosHigh,@object
+ countLeadingZerosHigh:
+ .byte 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4
+ .byte 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
+ .byte 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+ .byte 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+ .byte 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ .byte 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ .byte 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ .byte 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+
+ .macro nsau cnt, val, tmp, a
+ mov \a, \val
+ movi \cnt, 0
+ extui \tmp, \a, 16, 16
+ bnez \tmp, 0f
+ movi \cnt, 16
+ slli \a, \a, 16
+ 0:
+ extui \tmp, \a, 24, 8
+ bnez \tmp, 1f
+ addi \cnt, \cnt, 8
+ slli \a, \a, 8
+ 1:
+ movi \tmp, countLeadingZerosHigh
+ extui \a, \a, 24, 8
+ add \tmp, \tmp, \a
+ l8ui \tmp, \tmp, 0
+ add \cnt, \cnt, \tmp
+ .endm
+
+ .section .text
+ #endif /* !XCHAL_HAVE_NSA */
+
+
+ #ifdef L_udivsi3
+ .align 4
+ .global __udivsi3
+ .type __udivsi3,@function
+ __udivsi3:
+ entry sp, 16
+ bltui a3, 2, .Lle_one # check if the divisor <= 1
+
+ mov a6, a2 # keep dividend in a6
+ #if XCHAL_HAVE_NSA
+ nsau a5, a6 # dividend_shift = nsau(dividend)
+ nsau a4, a3 # divisor_shift = nsau(divisor)
+ #else /* !XCHAL_HAVE_NSA */
+ nsau a5, a6, a2, a7 # dividend_shift = nsau(dividend)
+ nsau a4, a3, a2, a7 # divisor_shift = nsau(divisor)
+ #endif /* !XCHAL_HAVE_NSA */
+ bgeu a5, a4, .Lspecial
+
+ sub a4, a4, a5 # count = divisor_shift - dividend_shift
+ ssl a4
+ sll a3, a3 # divisor <<= count
+ movi a2, 0 # quotient = 0
+
+ # test-subtract-and-shift loop; one quotient bit on each iteration
+ loopnez a4, .Lloopend
+ bltu a6, a3, .Lzerobit
+ sub a6, a6, a3
+ addi a2, a2, 1
+ .Lzerobit:
+ slli a2, a2, 1
+ srli a3, a3, 1
+ .Lloopend:
+
+ bltu a6, a3, .Lreturn
+ addi a2, a2, 1 # increment quotient if dividend >= divisor
+ .Lreturn:
+ retw
+
+ .Lspecial:
+ # return dividend >= divisor
+ movi a2, 0
+ bltu a6, a3, .Lreturn2
+ movi a2, 1
+ .Lreturn2:
+ retw
+
+ .Lle_one:
+ beqz a3, .Lerror # if divisor == 1, return the dividend
+ retw
+ .Lerror:
+ movi a2, 0 # just return 0; could throw an exception
+ retw
+ .Lfe2:
+ .size __udivsi3,.Lfe2-__udivsi3
+
+ #endif /* L_udivsi3 */
+
+
+ #ifdef L_divsi3
+ .align 4
+ .global __divsi3
+ .type __divsi3,@function
+ __divsi3:
+ entry sp, 16
+ xor a7, a2, a3 # sign = dividend ^ divisor
+ abs a6, a2 # udividend = abs(dividend)
+ abs a3, a3 # udivisor = abs(divisor)
+ bltui a3, 2, .Lle_one # check if udivisor <= 1
+ #if XCHAL_HAVE_NSA
+ nsau a5, a6 # udividend_shift = nsau(udividend)
+ nsau a4, a3 # udivisor_shift = nsau(udivisor)
+ #else /* !XCHAL_HAVE_NSA */
+ nsau a5, a6, a2, a8 # udividend_shift = nsau(udividend)
+ nsau a4, a3, a2, a8 # udivisor_shift = nsau(udivisor)
+ #endif /* !XCHAL_HAVE_NSA */
+ bgeu a5, a4, .Lspecial
+
+ sub a4, a4, a5 # count = udivisor_shift - udividend_shift
+ ssl a4
+ sll a3, a3 # udivisor <<= count
+ movi a2, 0 # quotient = 0
+
+ # test-subtract-and-shift loop; one quotient bit on each iteration
+ loopnez a4, .Lloopend
+ bltu a6, a3, .Lzerobit
+ sub a6, a6, a3
+ addi a2, a2, 1
+ .Lzerobit:
+ slli a2, a2, 1
+ srli a3, a3, 1
+ .Lloopend:
+
+ bltu a6, a3, .Lreturn
+ addi a2, a2, 1 # increment quotient if udividend >= udivisor
+ .Lreturn:
+ neg a5, a2
+ movltz a2, a5, a7 # return (sign < 0) ? -quotient : quotient
+ retw
+
+ .Lspecial:
+ movi a2, 0
+ bltu a6, a3, .Lreturn2 # if dividend < divisor, return 0
+ movi a2, 1
+ movi a4, -1
+ movltz a2, a4, a7 # else return (sign < 0) ? -1 : 1
+ .Lreturn2:
+ retw
+
+ .Lle_one:
+ beqz a3, .Lerror
+ neg a2, a6 # if udivisor == 1, then return...
+ movgez a2, a6, a7 # (sign < 0) ? -udividend : udividend
+ retw
+ .Lerror:
+ movi a2, 0 # just return 0; could throw an exception
+ retw
+ .Lfe3:
+ .size __divsi3,.Lfe3-__divsi3
+
+ #endif /* L_divsi3 */
+
+
+ #ifdef L_umodsi3
+ .align 4
+ .global __umodsi3
+ .type __umodsi3,@function
+ __umodsi3:
+ entry sp, 16
+ bltui a3, 2, .Lle_one # check if the divisor is <= 1
+
+ #if XCHAL_HAVE_NSA
+ nsau a5, a2 # dividend_shift = nsau(dividend)
+ nsau a4, a3 # divisor_shift = nsau(divisor)
+ #else /* !XCHAL_HAVE_NSA */
+ nsau a5, a2, a6, a7 # dividend_shift = nsau(dividend)
+ nsau a4, a3, a6, a7 # divisor_shift = nsau(divisor)
+ #endif /* !XCHAL_HAVE_NSA */
+ bgeu a5, a4, .Lspecial
+
+ sub a4, a4, a5 # count = divisor_shift - dividend_shift
+ ssl a4
+ sll a3, a3 # divisor <<= count
+
+ # test-subtract-and-shift loop
+ loopnez a4, .Lloopend
+ bltu a2, a3, .Lzerobit
+ sub a2, a2, a3
+ .Lzerobit:
+ srli a3, a3, 1
+ .Lloopend:
+
+ bltu a2, a3, .Lreturn
+ sub a2, a2, a3 # subtract once more if dividend >= divisor
+ .Lreturn:
+ retw
+
+ .Lspecial:
+ bltu a2, a3, .Lreturn2
+ sub a2, a2, a3 # subtract once if dividend >= divisor
+ .Lreturn2:
+ retw
+
+ .Lle_one:
+ # the divisor is either 0 or 1, so just return 0.
+ # someday we may want to throw an exception if the divisor is 0.
+ movi a2, 0
+ retw
+ .Lfe4:
+ .size __umodsi3,.Lfe4-__umodsi3
+
+ #endif /* L_umodsi3 */
+
+
+ #ifdef L_modsi3
+ .align 4
+ .global __modsi3
+ .type __modsi3,@function
+ __modsi3:
+ entry sp, 16
+ mov a7, a2 # save original (signed) dividend
+ abs a2, a2 # udividend = abs(dividend)
+ abs a3, a3 # udivisor = abs(divisor)
+ bltui a3, 2, .Lle_one # check if udivisor <= 1
+ #if XCHAL_HAVE_NSA
+ nsau a5, a2 # udividend_shift = nsau(udividend)
+ nsau a4, a3 # udivisor_shift = nsau(udivisor)
+ #else /* !XCHAL_HAVE_NSA */
+ nsau a5, a2, a6, a8 # udividend_shift = nsau(udividend)
+ nsau a4, a3, a6, a8 # udivisor_shift = nsau(udivisor)
+ #endif /* !XCHAL_HAVE_NSA */
+ bgeu a5, a4, .Lspecial
+
+ sub a4, a4, a5 # count = udivisor_shift - udividend_shift
+ ssl a4
+ sll a3, a3 # udivisor <<= count
+
+ # test-subtract-and-shift loop
+ loopnez a4, .Lloopend
+ bltu a2, a3, .Lzerobit
+ sub a2, a2, a3
+ .Lzerobit:
+ srli a3, a3, 1
+ .Lloopend:
+
+ bltu a2, a3, .Lreturn
+ sub a2, a2, a3 # subtract once more if udividend >= udivisor
+ .Lreturn:
+ bgez a7, .Lpositive
+ neg a2, a2 # if (dividend < 0), return -udividend
+ .Lpositive:
+ retw
+
+ .Lspecial:
+ bltu a2, a3, .Lreturn2
+ sub a2, a2, a3 # subtract once if dividend >= divisor
+ .Lreturn2:
+ bgez a7, .Lpositive2
+ neg a2, a2 # if (dividend < 0), return -udividend
+ .Lpositive2:
+ retw
+
+ .Lle_one:
+ # udivisor is either 0 or 1, so just return 0.
+ # someday we may want to throw an exception if udivisor is 0.
+ movi a2, 0
+ retw
+ .Lfe5:
+ .size __modsi3,.Lfe5-__modsi3
+
+ #endif /* L_modsi3 */
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/lib2funcs.S gcc-3.0.2/gcc/config/xtensa/lib2funcs.S
*** gcc-3.0.2-orig/gcc/config/xtensa/lib2funcs.S Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/lib2funcs.S Mon Nov 12 16:34:28 2001
***************
*** 0 ****
--- 1,205 ----
+ /* Assembly functions for libgcc2.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include "xtensa/xtensa-defaults.h"
+
+ /* _xtensa_libgcc_window_spill: This function uses a series of nested
+ calls to flush out all but the current register window. This is
+ used to set up the stack so that arbitrary frames can be accessed.
+ The functions used for the nested calls are also reused by the
+ nonlocal goto function below. */
+
+ .align 4
+ .global _xtensa_libgcc_window_spill
+ .type _xtensa_libgcc_window_spill,@function
+ _xtensa_libgcc_window_spill:
+ entry sp, 48
+ call4 .L__wdwspill_assist52 // called with call8, only need a call4
+ retw
+ .size _xtensa_libgcc_window_spill,.-_xtensa_libgcc_window_spill
+
+ .align 4
+ .L__wdwspill_assist56:
+ entry sp, 16
+ call4 .L__wdwspill_assist52
+ retw
+ .align 4
+ .L__wdwspill_assist52:
+ entry sp, 48
+ call12 .L__wdwspill_assist40
+ retw
+ .align 4
+ .L__wdwspill_assist40:
+ entry sp, 48
+ call12 .L__wdwspill_assist28
+ retw
+ .align 4
+ .L__wdwspill_assist28:
+ entry sp, 48
+ call12 .L__wdwspill_assist16
+ retw
+ .align 4
+ .L__wdwspill_assist16:
+ entry sp, 16
+ movi a15, 0
+ retw
+
+
+ /* _xtensa_nonlocal_goto: This code does all the hard work of a
+ nonlocal goto on Xtensa. It is here in the library to avoid the
+ code size bloat of generating it in-line. There are two
+ arguments:
+
+ a2 = frame pointer for the procedure containing the label
+ a3 = goto handler address
+
+ This function never returns to its caller but instead goes directly
+ to the address of the specified goto handler. */
+
+ .align 4
+ .global _xtensa_nonlocal_goto
+ .type _xtensa_nonlocal_goto,@function
+ _xtensa_nonlocal_goto:
+ entry sp, 32
+
+ /* flush registers */
+ call8 .L__wdwspill_assist56
+
+ /* Because the save area for a0-a3 is stored one frame below
+ the one identified by a2, the only way to restore those
+ registers is to unwind the stack. If alloca() were never
+ called, we could just unwind until finding the sp value
+ matching a2. However, a2 is a frame pointer, not a stack
+ pointer, and may not be encountered during the unwinding.
+ The solution is to unwind until going _past_ the value
+ given by a2. This involves keeping three stack pointer
+ values during the unwinding:
+
+ next = sp of frame N-1
+ cur = sp of frame N
+ prev = sp of frame N+1
+
+ When next > a2, the desired save area is stored relative
+ to prev. At this point, cur will be the same as a2
+ except in the alloca() case.
+
+ Besides finding the values to be restored to a0-a3, we also
+ need to find the current window size for the target
+ function. This can be extracted from the high bits of the
+ return address, initially in a0. As the unwinding
+ proceeds, the window size is taken from the value of a0
+ saved _two_ frames below the current frame. */
+
+ addi a5, sp, -16 # a5 = prev - save area
+ l32i a6, a5, 4
+ addi a6, a6, -16 # a6 = cur - save area
+ mov a8, a0 # a8 = return address (for window size)
+ j .Lfirstframe
+
+ .Lnextframe:
+ l32i a8, a5, 0 # next return address (for window size)
+ mov a5, a6 # advance prev
+ addi a6, a7, -16 # advance cur
+ .Lfirstframe:
+ l32i a7, a6, 4 # a7 = next
+ bge a2, a7, .Lnextframe
+
+ /* At this point, prev (a5) points to the save area with the saved
+ values of a0-a3. Copy those values into the save area at the
+ current sp so they will be reloaded when the return from this
+ function underflows. We don't have to worry about exceptions
+ while updating the current save area, because the windows have
+ already been flushed. */
+
+ addi a4, sp, -16 # a4 = save area of this function
+ l32i a6, a5, 0
+ l32i a7, a5, 4
+ s32i a6, a4, 0
+ s32i a7, a4, 4
+ l32i a6, a5, 8
+ l32i a7, a5, 12
+ s32i a6, a4, 8
+ s32i a7, a4, 12
+
+ /* Set return address to goto handler. Use the window size bits
+ from the return address two frames below the target. */
+ extui a8, a8, 30, 2 # get window size from return addr.
+ slli a3, a3, 2 # get goto handler addr. << 2
+ ssai 2
+ src a0, a8, a3 # combine them with a funnel shift
+
+ retw
+ .size _xtensa_nonlocal_goto,.-_xtensa_nonlocal_goto
+
+
+ /* _xtensa_sync_caches: This function is called after writing a trampoline
+ on the stack to force all the data writes to memory and invalidate the
+ instruction cache. a2 is the address of the new trampoline.
+
+ After the trampoline data is written out, it must be flushed out of
+ the data cache into memory. We use DHWB in case we have a writeback
+ cache. At least one DHWB instruction is needed for each data cache
+ line which may be touched by the trampoline. An ISYNC instruction
+ must follow the DHWBs.
+
+ We have to flush the i-cache to make sure that the new values get used.
+ At least one IHI instruction is needed for each i-cache line which may
+ be touched by the trampoline. An ISYNC instruction is also needed to
+ make sure that the modified instructions are loaded into the instruction
+ fetch buffer. */
+
+ #define TRAMPOLINE_SIZE 49
+
+ .text
+ .align 4
+ .global _xtensa_sync_caches
+ .type _xtensa_sync_caches,@function
+ _xtensa_sync_caches:
+ entry sp, 32
+ #if XCHAL_DCACHE_SIZE > 0 && XCHAL_DCACHE_IS_WRITEBACK
+ # Flush the trampoline from the data cache
+ extui a4, a2, 0, XCHAL_DCACHE_LINEWIDTH
+ addi a4, a4, TRAMPOLINE_SIZE
+ addi a4, a4, (1 << XCHAL_DCACHE_LINEWIDTH) - 1
+ srli a4, a4, XCHAL_DCACHE_LINEWIDTH
+ mov a3, a2
+ .Ldcache_loop:
+ dhwb a3, 0
+ addi a3, a3, (1 << XCHAL_DCACHE_LINEWIDTH)
+ addi a4, a4, -1
+ bnez a4, .Ldcache_loop
+ isync
+ #endif
+ #if XCHAL_ICACHE_SIZE > 0
+ # Invalidate the corresponding lines in the instruction cache
+ extui a4, a2, 0, XCHAL_ICACHE_LINEWIDTH
+ addi a4, a4, TRAMPOLINE_SIZE
+ addi a4, a4, (1 << XCHAL_ICACHE_LINEWIDTH) - 1
+ srli a4, a4, XCHAL_ICACHE_LINEWIDTH
+ .Licache_loop:
+ ihi a2, 0
+ addi a2, a2, (1 << XCHAL_ICACHE_LINEWIDTH)
+ addi a4, a4, -1
+ bnez a4, .Licache_loop
+ isync
+ #endif
+ retw
+ .size _xtensa_sync_caches,.-_xtensa_sync_caches
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/linux.h gcc-3.0.2/gcc/config/xtensa/linux.h
*** gcc-3.0.2-orig/gcc/config/xtensa/linux.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/linux.h Tue Nov 27 09:30:49 2001
***************
*** 0 ****
--- 1,67 ----
+ /* Xtensa Linux configuration.
+ Derived from the configuration for GCC for Intel i386 running Linux.
+ Copyright (C) 2001 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include <linux.h>
+
+ #undef TARGET_VERSION
+ #define TARGET_VERSION fputs (" (Xtensa GNU/Linux with ELF)", stderr);
+
+ /* Debug format: prefer DWARF2 */
+ #undef PREFERRED_DEBUGGING_TYPE
+ #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+ #undef ASM_SPEC
+ #define ASM_SPEC "%{v} %{mno-density:--no-density} \
+ %{mtext-section-literals:--text-section-literals} \
+ %{mno-text-section-literals:--no-text-section-literals} \
+ %{mtarget-align:--target-align} \
+ %{mno-target-align:--no-target-align} \
+ %{mlongcalls:--longcalls} \
+ %{mno-longcalls:--no-longcalls}"
+
+ #undef ASM_FINAL_SPEC
+
+ #undef LIB_SPEC
+ #define LIB_SPEC \
+ "%{shared: -lc} \
+ %{!shared: %{pthread:-lpthread} \
+ %{profile:-lc_p} %{!profile: -lc}} -lxtpanic"
+
+ #undef LINK_SPEC
+ #define LINK_SPEC \
+ "%{mno-density:--no-density} \
+ %{shared:-shared} \
+ %{!shared: \
+ %{!ibcs: \
+ %{!static: \
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld.so.1}} \
+ %{static:-static}}}"
+
+ #undef CPP_PREDEFINES
+ #define CPP_PREDEFINES \
+ "-D__XTENSA__ -D__ELF__ -Acpu=xtensa -Amachine=xtensa \
+ -Dunix -Dlinux -Asystem=posix"
+
+ #undef LOCAL_LABEL_PREFIX
+ #define LOCAL_LABEL_PREFIX "."
+
+ #include <xtensa/xtensa.h>
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/t-linux gcc-3.0.2/gcc/config/xtensa/t-linux
*** gcc-3.0.2-orig/gcc/config/xtensa/t-linux Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/t-linux Mon Nov 12 16:35:18 2001
***************
*** 0 ****
--- 1,28 ----
+ # Use GCC's floating-point emulation code
+ LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c
+
+ dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+ fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+ ########################################################################
+
+ # Skip the libgcc1 test.
+ LIBGCC1_TEST =
+
+ # Don't run fixproto
+ STMP_FIXPROTO =
+
+ # Build crtbegin and crtend with the "longcalls" option
+ CRTSTUFF_T_CFLAGS = -mlongcalls
+
+ CROSS_LIBGCC1 = libgcc1-asm.a
+ LIB1ASMSRC = xtensa/lib1funcs.asm
+ LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 _udivsi3 _umodsi3
+
+ TARGET_LIBGCC2_CFLAGS = -mlongcalls
+
+ LIB2FUNCS_EXTRA += $(srcdir)/config/xtensa/lib2funcs.S
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/t-xtensa gcc-3.0.2/gcc/config/xtensa/t-xtensa
*** gcc-3.0.2-orig/gcc/config/xtensa/t-xtensa Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/t-xtensa Mon Nov 12 16:35:18 2001
***************
*** 0 ****
--- 1,28 ----
+ # Use GCC's floating-point emulation code
+ LIB2FUNCS_EXTRA = fp-bit.c dp-bit.c
+
+ dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+ fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+ ########################################################################
+
+ # Skip the libgcc1 test.
+ LIBGCC1_TEST =
+
+ # Don't run fixproto
+ STMP_FIXPROTO =
+
+ # Build crtbegin and crtend with the "longcalls" option
+ CRTSTUFF_T_CFLAGS = -mlongcalls
+
+ CROSS_LIBGCC1 = libgcc1-asm.a
+ LIB1ASMSRC = xtensa/lib1funcs.asm
+ LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 _udivsi3 _umodsi3
+
+ TARGET_LIBGCC2_CFLAGS = -mlongcalls
+
+ LIB2FUNCS_EXTRA += $(srcdir)/config/xtensa/lib2funcs.S
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/xm-xtensa.h gcc-3.0.2/gcc/config/xtensa/xm-xtensa.h
*** gcc-3.0.2-orig/gcc/config/xtensa/xm-xtensa.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/xm-xtensa.h Mon Nov 12 16:35:18 2001
***************
*** 0 ****
--- 1,44 ----
+ /* Xtensa/Elf configuration.
+ Copyright (C) 2001 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ /* #defines that need visibility everywhere. */
+ #define FALSE 0
+ #define TRUE 1
+
+ /* This describes the machine the compiler is hosted on. */
+ #define HOST_BITS_PER_CHAR 8
+ #define HOST_BITS_PER_SHORT 16
+ #define HOST_BITS_PER_INT 32
+ #define HOST_BITS_PER_LONG 32
+ #define HOST_BITS_PER_LONGLONG 64
+
+ /* A code distinguishing the floating point format of the host
+ machine. There are three defined values: IEEE_FLOAT_FORMAT,
+ VAX_FLOAT_FORMAT, and UNKNOWN_FLOAT_FORMAT. */
+
+ #define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT
+
+ /* target machine dependencies.
+ tm.h is a symbolic link to the actual target specific file. */
+ #include "tm.h"
+
+ #define HAVE_VPRINTF
+ #define HAVE_PUTENV
+
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/xtensa-defaults.h gcc-3.0.2/gcc/config/xtensa/xtensa-defaults.h
*** gcc-3.0.2-orig/gcc/config/xtensa/xtensa-defaults.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/xtensa-defaults.h Mon Nov 12 16:35:19 2001
***************
*** 0 ****
--- 1,49 ----
+ /* Default Xtensa-configuration-specific settings for GCC.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #ifndef XTENSA_DEFAULTS_H
+ #define XTENSA_DEFAULTS_H
+
+ #define XCHAL_HAVE_BE 1
+ #define XCHAL_HAVE_DENSITY 1
+ #define XCHAL_HAVE_MAC16 0
+ #define XCHAL_HAVE_MUL16 0
+ #define XCHAL_HAVE_MUL32 0
+ #define XCHAL_HAVE_DIV32 0
+ #define XCHAL_HAVE_NSA 1
+ #define XCHAL_HAVE_MINMAX 0
+ #define XCHAL_HAVE_SEXT 0
+ #define XCHAL_HAVE_BOOLEANS 0
+ #define XCHAL_HAVE_FP 0
+ #define XCHAL_HAVE_FP_DIV 0
+ #define XCHAL_HAVE_FP_RECIP 0
+ #define XCHAL_HAVE_FP_SQRT 0
+ #define XCHAL_HAVE_FP_RSQRT 0
+
+ #define XCHAL_ICACHE_SIZE 8192
+ #define XCHAL_DCACHE_SIZE 8192
+ #define XCHAL_ICACHE_LINESIZE 16
+ #define XCHAL_DCACHE_LINESIZE 16
+ #define XCHAL_ICACHE_LINEWIDTH 4
+ #define XCHAL_DCACHE_LINEWIDTH 4
+ #define XCHAL_DCACHE_IS_WRITEBACK 0
+
+ #endif /* !XTENSA_DEFAULTS_H */
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/xtensa-protos.h gcc-3.0.2/gcc/config/xtensa/xtensa-protos.h
*** gcc-3.0.2-orig/gcc/config/xtensa/xtensa-protos.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/xtensa-protos.h Mon Nov 12 16:35:19 2001
***************
*** 0 ****
--- 1,117 ----
+ /* Prototypes of target machine for GNU compiler for Xtensa.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #ifndef __XTENSA_PROTOS_H__
+ #define __XTENSA_PROTOS_H__
+
+ /* Functions to test whether an immediate fits in a given field. */
+ extern int xtensa_simm7 PARAMS ((int));
+ extern int xtensa_simm8 PARAMS ((int));
+ extern int xtensa_simm8x256 PARAMS ((int));
+ extern int xtensa_simm12b PARAMS ((int));
+ extern int xtensa_uimm8 PARAMS ((int));
+ extern int xtensa_uimm8x2 PARAMS ((int));
+ extern int xtensa_uimm8x4 PARAMS ((int));
+ extern int xtensa_ai4const PARAMS ((int));
+ extern int xtensa_lsi4x4 PARAMS ((int));
+ extern int xtensa_b4const PARAMS ((int));
+ extern int xtensa_b4constu PARAMS ((int));
+ extern int xtensa_tp7 PARAMS ((int));
+
+ /* Functions within xtensa.c that we reference. */
+ #ifdef RTX_CODE
+ extern int xt_true_regnum PARAMS ((rtx));
+ extern int add_operand PARAMS ((rtx, enum machine_mode));
+ extern int arith_operand PARAMS ((rtx, enum machine_mode));
+ extern int nonimmed_operand PARAMS ((rtx, enum machine_mode));
+ extern int mem_operand PARAMS ((rtx, enum machine_mode));
+ extern int non_acc_reg_operand PARAMS ((rtx, enum machine_mode));
+ extern int mask_operand PARAMS ((rtx, enum machine_mode));
+ extern int extui_fldsz_operand PARAMS ((rtx, enum machine_mode));
+ extern int sext_operand PARAMS ((rtx, enum machine_mode));
+ extern int sext_fldsz_operand PARAMS ((rtx, enum machine_mode));
+ extern int lsbitnum_operand PARAMS ((rtx, enum machine_mode));
+ extern int branch_operand PARAMS ((rtx, enum machine_mode));
+ extern int ubranch_operand PARAMS ((rtx, enum machine_mode));
+ extern int call_insn_operand PARAMS ((rtx, enum machine_mode));
+ extern int move_operand PARAMS ((rtx, enum machine_mode));
+ extern int smalloffset_mem_p PARAMS ((rtx));
+ extern int smalloffset_double_mem_p PARAMS ((rtx));
+ extern int constantpool_address_p PARAMS ((rtx));
+ extern int constantpool_mem_p PARAMS ((rtx));
+ extern int non_const_move_operand PARAMS ((rtx, enum machine_mode));
+ extern int const_float_1_operand PARAMS ((rtx, enum machine_mode));
+ extern int fpmem_offset_operand PARAMS ((rtx, enum machine_mode));
+ extern void xtensa_extend_reg PARAMS ((rtx, rtx));
+ extern void xtensa_load_constant PARAMS ((rtx, rtx));
+ extern int branch_operator PARAMS ((rtx, enum machine_mode));
+ extern int ubranch_operator PARAMS ((rtx, enum machine_mode));
+ extern int boolean_operator PARAMS ((rtx, enum machine_mode));
+ extern void xtensa_expand_conditional_branch PARAMS ((rtx *, enum rtx_code));
+ extern int xtensa_expand_conditional_move PARAMS ((rtx *, int));
+ extern int xtensa_expand_scc PARAMS ((rtx *));
+ extern int xtensa_expand_block_move PARAMS ((rtx *));
+ extern int xtensa_emit_move_sequence PARAMS ((rtx *, enum machine_mode));
+ extern void xtensa_emit_block_move PARAMS ((rtx *, rtx *, int));
+ extern void xtensa_expand_nonlocal_goto PARAMS ((rtx *));
+ extern void xtensa_emit_loop_end PARAMS ((rtx, rtx *));
+ extern char * xtensa_emit_call PARAMS ((int, rtx *));
+
+ #ifdef TREE_CODE
+ extern void init_cumulative_args PARAMS ((CUMULATIVE_ARGS *, tree, rtx));
+ extern void xtensa_va_start PARAMS ((int, tree, rtx));
+ extern rtx xtensa_va_arg PARAMS ((tree, tree));
+ #endif /* TREE_CODE */
+
+ extern void abort_with_insn PARAMS ((rtx, const char *));
+ extern void print_operand PARAMS ((FILE *, rtx, int));
+ extern void print_operand_address PARAMS ((FILE *, rtx));
+ extern void xtensa_reorg PARAMS ((rtx));
+ extern rtx xtensa_builtin_saveregs PARAMS ((void));
+ extern enum reg_class xtensa_secondary_reload_class
+ PARAMS ((enum reg_class, enum machine_mode, rtx, int));
+ extern int a7_overlap_mentioned_p PARAMS ((rtx x));
+ #endif /* RTX_CODE */
+
+ #ifdef TREE_CODE
+ extern void function_arg_advance
+ PARAMS ((CUMULATIVE_ARGS *, enum machine_mode, tree));
+ extern struct rtx_def * function_arg
+ PARAMS ((CUMULATIVE_ARGS *, enum machine_mode, tree, int));
+ extern void xtensa_output_double PARAMS ((FILE *, REAL_VALUE_TYPE));
+ extern void xtensa_output_float PARAMS ((FILE *, REAL_VALUE_TYPE));
+ extern tree xtensa_build_va_list PARAMS ((void));
+ #endif /* TREE_CODE */
+
+ extern int xtensa_mask_immediate PARAMS ((int));
+ extern int xtensa_mem_offset PARAMS ((unsigned, enum machine_mode));
+ extern void xtensa_setup_frame_addresses PARAMS ((void));
+ extern int xtensa_dbx_register_number PARAMS ((int));
+ extern void override_options PARAMS ((void));
+ extern void xtensa_declare_object
+ PARAMS ((FILE *, char *, char *, char *, int));
+ extern long compute_frame_size PARAMS ((int));
+ extern int xtensa_frame_pointer_required PARAMS ((void));
+ extern void xtensa_function_prologue PARAMS ((FILE *, int));
+ extern void xtensa_function_epilogue PARAMS ((FILE *, int));
+ extern void order_regs_for_local_alloc PARAMS ((void));
+
+ #endif /* !__XTENSA_PROTOS_H__ */
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/xtensa.c gcc-3.0.2/gcc/config/xtensa/xtensa.c
*** gcc-3.0.2-orig/gcc/config/xtensa/xtensa.c Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/xtensa.c Mon Nov 12 16:35:18 2001
***************
*** 0 ****
--- 1,2662 ----
+ /* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include "config.h"
+ #include "system.h"
+ #include "rtl.h"
+ #include "regs.h"
+ #include "machmode.h"
+ #include "hard-reg-set.h"
+ #include "basic-block.h"
+ #include "real.h"
+ #include "insn-config.h"
+ #include "conditions.h"
+ #include "insn-flags.h"
+ #include "insn-attr.h"
+ #include "insn-codes.h"
+ #include "recog.h"
+ #include "output.h"
+ #include "tree.h"
+ #include "expr.h"
+ #include "flags.h"
+ #include "reload.h"
+ #include "tm_p.h"
+ #include "function.h"
+ #include "toplev.h"
+
+ /* Enumeration for all of the relational tests, so that we can build
+ arrays indexed by the test type, and not worry about the order
+ of EQ, NE, etc. */
+
+ enum internal_test {
+ ITEST_EQ,
+ ITEST_NE,
+ ITEST_GT,
+ ITEST_GE,
+ ITEST_LT,
+ ITEST_LE,
+ ITEST_GTU,
+ ITEST_GEU,
+ ITEST_LTU,
+ ITEST_LEU,
+ ITEST_MAX
+ };
+
+ /* Cached operands, and operator to compare for use in set/branch on
+ condition codes. */
+ rtx branch_cmp[2];
+
+ /* what type of branch to use */
+ enum cmp_type branch_type;
+
+ /* Array giving truth value on whether or not a given hard register
+ can support a given mode. */
+ char xtensa_hard_regno_mode_ok
+ [(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+
+ /* Current frame size calculated by compute_frame_size. */
+ unsigned xtensa_current_frame_size;
+
+ /* Tables of ld/st opcode names for block moves */
+ const char *xtensa_ld_opcodes[(int) MAX_MACHINE_MODE];
+ const char *xtensa_st_opcodes[(int) MAX_MACHINE_MODE];
+ #define LARGEST_MOVE_RATIO 15
+
+ /* Define the structure for the machine field in struct function. */
+ struct machine_function
+ {
+ int accesses_prev_frame;
+ };
+
+ /* Hardware names for the registers. */
+ char xtensa_reg_names[][MAX_REGFILE_NAME_LEN] =
+ {
+ "a0", "sp", "a2", "a3", "a4", "a5", "a6", "a7",
+ "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15",
+ "fp", "argp", "b0",
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "acc",
+ };
+
+ /* Vector, indexed by hard register number, which contains 1 for a
+ register that is allowable in a candidate for leaf function
+ treatment. */
+
+ char xtensa_leaf_regs[] =
+ {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1
+ };
+
+ /* Map hard register number to register class */
+ enum reg_class xtensa_regno_to_class[] =
+ {
+ GR_REGS, SP_REG, GR_REGS, GR_REGS,
+ GR_REGS, GR_REGS, GR_REGS, GR_REGS,
+ GR_REGS, GR_REGS, GR_REGS, GR_REGS,
+ GR_REGS, GR_REGS, GR_REGS, GR_REGS,
+ AR_REGS, AR_REGS, BR_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ ACC_REG,
+ };
+
+ /* Map register constraint character to register class. */
+ enum reg_class xtensa_char_to_class[256] =
+ {
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+ };
+
+
+ static int b4const_or_zero PARAMS ((int));
+ static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
+ static rtx gen_int_relational PARAMS ((enum rtx_code, rtx, rtx, int *));
+ static rtx gen_float_relational PARAMS ((enum rtx_code, rtx, rtx));
+ static rtx gen_conditional_move PARAMS ((rtx));
+ static enum machine_mode xtensa_find_mode_for_size PARAMS ((unsigned));
+ static void xtensa_init_machine_status PARAMS ((struct function *p));
+ static void xtensa_free_machine_status PARAMS ((struct function *p));
+ static void printx PARAMS ((FILE *, signed int));
+ static rtx frame_size_const;
+ static int current_function_arg_words;
+ static int reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER;
+
+
+ /*
+ * Functions to test Xtensa immediate operand validity.
+ */
+
+ int
+ xtensa_b4constu (v)
+ int v;
+ {
+ switch (v)
+ {
+ case 32768:
+ case 65536:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ return 1;
+ }
+ return 0;
+ }
+
+ int
+ xtensa_simm8x256 (v)
+ int v;
+ {
+ return (v & 255) == 0 && (v >= -32768 && v <= 32512);
+ }
+
+ int
+ xtensa_ai4const (v)
+ int v;
+ {
+ return (v == -1 || (v >= 1 && v <= 15));
+ }
+
+ int
+ xtensa_simm7 (v)
+ int v;
+ {
+ return v >= -32 && v <= 95;
+ }
+
+ int
+ xtensa_b4const (v)
+ int v;
+ {
+ switch (v)
+ {
+ case -1:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ return 1;
+ }
+ return 0;
+ }
+
+ int
+ xtensa_simm8 (v)
+ int v;
+ {
+ return v >= -128 && v <= 127;
+ }
+
+ int
+ xtensa_tp7 (v)
+ int v;
+ {
+ return (v >= 7 && v <= 22);
+ }
+
+ int
+ xtensa_lsi4x4 (v)
+ int v;
+ {
+ return (v & 3) == 0 && (v >= 0 && v <= 60);
+ }
+
+ int
+ xtensa_simm12b (v)
+ int v;
+ {
+ return v >= -2048 && v <= 2047;
+ }
+
+ int
+ xtensa_uimm8 (v)
+ int v;
+ {
+ return v >= 0 && v <= 255;
+ }
+
+ int
+ xtensa_uimm8x2 (v)
+ int v;
+ {
+ return (v & 1) == 0 && (v >= 0 && v <= 510);
+ }
+
+ int
+ xtensa_uimm8x4 (v)
+ int v;
+ {
+ return (v & 3) == 0 && (v >= 0 && v <= 1020);
+ }
+
+
+ /* This is just like the standard true_regnum() function except that it
+ works even when reg_renumber is not initialized. */
+
+ int
+ xt_true_regnum (x)
+ rtx x;
+ {
+ if (GET_CODE (x) == REG)
+ {
+ if (reg_renumber
+ && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (x)] >= 0)
+ return reg_renumber[REGNO (x)];
+ return REGNO (x);
+ }
+ if (GET_CODE (x) == SUBREG)
+ {
+ int base = xt_true_regnum (SUBREG_REG (x));
+ if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
+ return SUBREG_WORD (x) + base;
+ }
+ return -1;
+ }
+
+
+ int
+ add_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return (xtensa_simm8 (INTVAL (op)) ||
+ xtensa_simm8x256 (INTVAL (op)));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return xtensa_simm8 (INTVAL (op));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ nonimmed_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ /* We cannot use the standard nonimmediate_operand() predicate because
+ it includes constant pool memory operands. */
+
+ if (memory_operand (op, mode))
+ return !constantpool_address_p (XEXP (op, 0));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ /* We cannot use the standard memory_operand() predicate because
+ it includes constant pool memory operands. */
+
+ if (memory_operand (op, mode))
+ return !constantpool_address_p (XEXP (op, 0));
+
+ return FALSE;
+ }
+
+
+ int
+ non_acc_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (register_operand (op, mode))
+ return !ACC_REG_P (xt_true_regnum (op));
+ return FALSE;
+ }
+
+
+ int
+ mask_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return xtensa_mask_immediate (INTVAL (op));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ extui_fldsz_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ {
+ return ((GET_CODE (op) == CONST_INT)
+ && xtensa_mask_immediate ((1 << INTVAL (op)) - 1));
+ }
+
+
+ int
+ sext_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (TARGET_SEXT) return nonimmed_operand (op, mode);
+ return mem_operand (op, mode);
+ }
+
+
+ int
+ sext_fldsz_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ {
+ return ((GET_CODE (op) == CONST_INT) && xtensa_tp7 (INTVAL (op) - 1));
+ }
+
+
+ int
+ lsbitnum_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ {
+ return (BITS_BIG_ENDIAN
+ ? (INTVAL (op) == BITS_PER_WORD-1)
+ : (INTVAL (op) == 0));
+ }
+ return FALSE;
+ }
+
+
+ static int
+ b4const_or_zero (v)
+ int v;
+ {
+ if (v == 0) return TRUE;
+ return xtensa_b4const (v);
+ }
+
+
+ int
+ branch_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return b4const_or_zero (INTVAL (op));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ ubranch_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return xtensa_b4constu (INTVAL (op));
+
+ return register_operand (op, mode);
+ }
+
+
+ int
+ call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ {
+ if (CONSTANT_ADDRESS_P (op)) return TRUE;
+ if ((GET_CODE (op) == REG)
+ && (op != arg_pointer_rtx)
+ && ((REGNO (op) < FRAME_POINTER_REGNUM)
+ || (REGNO (op) > LAST_VIRTUAL_REGISTER)))
+ return TRUE;
+ return FALSE;
+ }
+
+
+ int
+ move_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (register_operand (op, mode))
+ return TRUE;
+
+ /* Accept CONSTANT_P_RTX, since it will be gone by CSE1 and
+ result in 0/1. */
+ if (GET_CODE (op) == CONSTANT_P_RTX)
+ return TRUE;
+
+ if (GET_CODE (op) == CONST_INT)
+ return xtensa_simm12b (INTVAL (op));
+
+ if (GET_CODE (op) == MEM)
+ return memory_address_p (mode, XEXP (op, 0));
+
+ return FALSE;
+ }
+
+
+ int
+ smalloffset_mem_p (op)
+ rtx op;
+ {
+ if (GET_CODE (op) == MEM)
+ {
+ rtx addr = XEXP (op, 0);
+ if (GET_CODE (addr) == REG)
+ return REG_OK_FOR_BASE_P (addr);
+ if (GET_CODE (addr) == PLUS)
+ {
+ rtx offset = XEXP (addr, 0);
+ if (GET_CODE (offset) != CONST_INT)
+ offset = XEXP (addr, 1);
+ if (GET_CODE (offset) != CONST_INT)
+ return FALSE;
+ return xtensa_lsi4x4 (INTVAL (offset));
+ }
+ }
+ return FALSE;
+ }
+
+
+ int
+ smalloffset_double_mem_p (op)
+ rtx op;
+ {
+ if (!smalloffset_mem_p (op))
+ return FALSE;
+ return smalloffset_mem_p (adj_offsettable_operand (op, 4));
+ }
+
+
+ int
+ constantpool_address_p (addr)
+ rtx addr;
+ {
+ rtx sym = addr;
+
+ if (GET_CODE (addr) == CONST)
+ {
+ rtx offset;
+
+ /* only handle (PLUS (SYM, OFFSET)) form */
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) != PLUS)
+ return FALSE;
+
+ /* make sure the address is word aligned */
+ offset = XEXP (addr, 1);
+ if ((GET_CODE (offset) != CONST_INT)
+ || ((INTVAL (offset) & 3) != 0))
+ return FALSE;
+
+ sym = XEXP (addr, 0);
+ }
+
+ if ((GET_CODE (sym) == SYMBOL_REF)
+ && CONSTANT_POOL_ADDRESS_P (sym))
+ return TRUE;
+ return FALSE;
+ }
+
+
+ int
+ constantpool_mem_p (op)
+ rtx op;
+ {
+ if (GET_CODE (op) == MEM)
+ return constantpool_address_p (XEXP (op, 0));
+ return FALSE;
+ }
+
+
+ int
+ non_const_move_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ if (register_operand (op, mode))
+ return 1;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ if (GET_CODE (op) == MEM)
+ return memory_address_p (mode, XEXP (op, 0));
+ return FALSE;
+ }
+
+
+ /* Accept the floating point constant 1 in the appropriate mode. */
+
+ int
+ const_float_1_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+ {
+ REAL_VALUE_TYPE d;
+ static REAL_VALUE_TYPE onedf;
+ static REAL_VALUE_TYPE onesf;
+ static int one_initialized;
+
+ if ((GET_CODE (op) != CONST_DOUBLE)
+ || (mode != GET_MODE (op))
+ || (mode != DFmode && mode != SFmode))
+ return FALSE;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+ if (! one_initialized)
+ {
+ onedf = REAL_VALUE_ATOF ("1.0", DFmode);
+ onesf = REAL_VALUE_ATOF ("1.0", SFmode);
+ one_initialized = TRUE;
+ }
+
+ if (mode == DFmode)
+ return REAL_VALUES_EQUAL (d, onedf);
+ else
+ return REAL_VALUES_EQUAL (d, onesf);
+ }
+
+
+ int
+ fpmem_offset_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ {
+ if (GET_CODE (op) == CONST_INT)
+ return xtensa_mem_offset (INTVAL (op), SFmode);
+ return 0;
+ }
+
+
+ void
+ xtensa_extend_reg (dst, src)
+ rtx dst;
+ rtx src;
+ {
+ int src_subword = 0;
+ int dst_subword = 0;
+ rtx temp = gen_reg_rtx (SImode);
+ rtx shift = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (GET_MODE (src)));
+
+ if (GET_CODE (src) == SUBREG)
+ {
+ src_subword = SUBREG_WORD (src);
+ src = SUBREG_REG (src);
+ }
+ if (GET_CODE (dst) == SUBREG)
+ {
+ dst_subword = SUBREG_WORD (dst);
+ dst = SUBREG_REG (dst);
+ }
+
+ src = gen_rtx_SUBREG (SImode, src, src_subword);
+ if (GET_MODE (dst) != SImode || dst_subword != 0)
+ dst = gen_rtx_SUBREG (SImode, dst, dst_subword);
+
+ emit_insn (gen_ashlsi3 (temp, src, shift));
+ emit_insn (gen_ashrsi3 (dst, temp, shift));
+ }
+
+
+ void
+ xtensa_load_constant (dst, src)
+ rtx dst;
+ rtx src;
+ {
+ enum machine_mode mode = GET_MODE (dst);
+ src = force_const_mem (SImode, src);
+
+ /* PC-relative loads are always SImode so we have to add a SUBREG if that
+ is not the desired mode */
+
+ if (mode != SImode)
+ {
+ if (register_operand (dst, mode))
+ {
+ int dst_subword = 0;
+ if (GET_CODE(dst) == SUBREG)
+ {
+ dst_subword = SUBREG_WORD (dst);
+ dst = SUBREG_REG (dst);
+ }
+ dst = gen_rtx_SUBREG (SImode, dst, dst_subword);
+ }
+ else
+ {
+ src = force_reg (SImode, src);
+ src = gen_rtx_SUBREG (mode, src, 0);
+ }
+ }
+
+ emit_move_insn (dst, src);
+ }
+
+
+ int
+ branch_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+ {
+ if (GET_MODE (x) != mode)
+ return FALSE;
+
+ switch (GET_CODE (x))
+ {
+ case EQ:
+ case NE:
+ case LT:
+ case GE:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+ }
+
+
+ int
+ ubranch_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+ {
+ if (GET_MODE (x) != mode)
+ return FALSE;
+
+ switch (GET_CODE (x))
+ {
+ case LTU:
+ case GEU:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+ }
+
+
+ int
+ boolean_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+ {
+ if (GET_MODE (x) != mode)
+ return FALSE;
+
+ switch (GET_CODE (x))
+ {
+ case EQ:
+ case NE:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+ }
+
+
+ int
+ xtensa_mask_immediate (v)
+ int v;
+ {
+ #define MAX_MASK_SIZE 16
+ int mask_size;
+
+ for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
+ {
+ if ((v & 1) == 0) return FALSE;
+ v = v >> 1;
+ if (v == 0) return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ int
+ xtensa_mem_offset (v, mode)
+ unsigned v;
+ enum machine_mode mode;
+ {
+ switch (mode)
+ {
+ case BLKmode:
+ /* Handle the worst case for block moves. See xtensa_expand_block_move
+ where we emit an optimized block move operation if the block can be
+ moved in < "move_ratio" pieces. The worst case is when the block is
+ aligned but has a size of (3 mod 4) (does this happen?) so that the
+ last piece requires a byte load/store. */
+ return (xtensa_uimm8 (v) &&
+ xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
+
+ case QImode:
+ return xtensa_uimm8 (v);
+
+ case HImode:
+ return xtensa_uimm8x2 (v);
+
+ case DFmode:
+ return (xtensa_uimm8x4 (v) && xtensa_uimm8x4 (v + 4));
+
+ default:
+ break;
+ }
+
+ return xtensa_uimm8x4 (v);
+ }
+
+
+ /* Make normal rtx_code into something we can index from an array */
+
+ static enum internal_test
+ map_test_to_internal_test (test_code)
+ enum rtx_code test_code;
+ {
+ enum internal_test test = ITEST_MAX;
+
+ switch (test_code)
+ {
+ default: break;
+ case EQ: test = ITEST_EQ; break;
+ case NE: test = ITEST_NE; break;
+ case GT: test = ITEST_GT; break;
+ case GE: test = ITEST_GE; break;
+ case LT: test = ITEST_LT; break;
+ case LE: test = ITEST_LE; break;
+ case GTU: test = ITEST_GTU; break;
+ case GEU: test = ITEST_GEU; break;
+ case LTU: test = ITEST_LTU; break;
+ case LEU: test = ITEST_LEU; break;
+ }
+
+ return test;
+ }
+
+
+ /* Generate the code to compare two integer values. The return value is
+ the comparison expression. */
+
+ static rtx
+ gen_int_relational (test_code, cmp0, cmp1, p_invert)
+ enum rtx_code test_code; /* relational test (EQ, etc) */
+ rtx cmp0; /* first operand to compare */
+ rtx cmp1; /* second operand to compare */
+ int *p_invert; /* whether branch needs to reverse its test */
+ {
+ struct cmp_info {
+ enum rtx_code test_code; /* test code to use in insn */
+ int (*const_range_p) PARAMS ((int)); /* predicate function to check range */
+ int const_add; /* constant to add (convert LE -> LT) */
+ int reverse_regs; /* reverse registers in test */
+ int invert_const; /* != 0 if invert value if cmp1 is constant */
+ int invert_reg; /* != 0 if invert value if cmp1 is register */
+ int unsignedp; /* != 0 for unsigned comparisons. */
+ };
+
+ static struct cmp_info info[ (int)ITEST_MAX ] = {
+
+ { EQ, b4const_or_zero, 0, 0, 0, 0, 0 }, /* EQ */
+ { NE, b4const_or_zero, 0, 0, 0, 0, 0 }, /* NE */
+
+ { LT, b4const_or_zero, 1, 1, 1, 0, 0 }, /* GT */
+ { GE, b4const_or_zero, 0, 0, 0, 0, 0 }, /* GE */
+ { LT, b4const_or_zero, 0, 0, 0, 0, 0 }, /* LT */
+ { GE, b4const_or_zero, 1, 1, 1, 0, 0 }, /* LE */
+
+ { LTU, xtensa_b4constu, 1, 1, 1, 0, 1 }, /* GTU */
+ { GEU, xtensa_b4constu, 0, 0, 0, 0, 1 }, /* GEU */
+ { LTU, xtensa_b4constu, 0, 0, 0, 0, 1 }, /* LTU */
+ { GEU, xtensa_b4constu, 1, 1, 1, 0, 1 }, /* LEU */
+ };
+
+ enum internal_test test;
+ enum machine_mode mode;
+ struct cmp_info *p_info;
+
+ test = map_test_to_internal_test (test_code);
+ if (test == ITEST_MAX)
+ abort ();
+
+ p_info = &info[ (int)test ];
+
+ mode = GET_MODE (cmp0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (cmp1);
+
+ /* Make sure we can handle any constants given to us. */
+ if (GET_CODE (cmp1) == CONST_INT)
+ {
+ HOST_WIDE_INT value = INTVAL (cmp1);
+ unsigned HOST_WIDE_INT uvalue = (unsigned HOST_WIDE_INT)value;
+
+ /* if the immediate overflows or does not fit in the immediate field,
+ spill it to a register */
+
+ if ((p_info->unsignedp ?
+ (uvalue + p_info->const_add > uvalue) :
+ (value + p_info->const_add > value)) != (p_info->const_add > 0))
+ {
+ cmp1 = force_reg (mode, cmp1);
+ }
+ else if (!(p_info->const_range_p)(value + p_info->const_add))
+ {
+ cmp1 = force_reg (mode, cmp1);
+ }
+ }
+ else if ((GET_CODE (cmp1) != REG) && (GET_CODE (cmp1) != SUBREG))
+ {
+ cmp1 = force_reg (mode, cmp1);
+ }
+
+ /* See if we need to invert the result. */
+ *p_invert = ((GET_CODE (cmp1) == CONST_INT)
+ ? p_info->invert_const
+ : p_info->invert_reg);
+
+ /* Comparison to constants, may involve adding 1 to change a LT into LE.
+ Comparison between two registers, may involve switching operands. */
+ if (GET_CODE (cmp1) == CONST_INT)
+ {
+ if (p_info->const_add != 0)
+ cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add);
+
+ }
+ else if (p_info->reverse_regs)
+ {
+ rtx temp = cmp0;
+ cmp0 = cmp1;
+ cmp1 = temp;
+ }
+
+ return gen_rtx (p_info->test_code, VOIDmode, cmp0, cmp1);
+ }
+
+
+ /* Generate the code to compare two float values. The return value is
+ the comparison expression. */
+
+ static rtx
+ gen_float_relational (test_code, cmp0, cmp1)
+ enum rtx_code test_code; /* relational test (EQ, etc) */
+ rtx cmp0; /* first operand to compare */
+ rtx cmp1; /* second operand to compare */
+ {
+ rtx (*gen_fn) PARAMS ((rtx, rtx, rtx));
+ rtx brtmp;
+ int reverse_regs, invert;
+
+ switch (test_code)
+ {
+ case EQ: reverse_regs = 0; invert = 0; gen_fn = gen_seq_sf; break;
+ case NE: reverse_regs = 0; invert = 1; gen_fn = gen_seq_sf; break;
+ case LE: reverse_regs = 0; invert = 0; gen_fn = gen_sle_sf; break;
+ case GT: reverse_regs = 1; invert = 0; gen_fn = gen_slt_sf; break;
+ case LT: reverse_regs = 0; invert = 0; gen_fn = gen_slt_sf; break;
+ case GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break;
+ default:
+ abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
+ reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
+ }
+
+ if (reverse_regs)
+ {
+ rtx temp = cmp0;
+ cmp0 = cmp1;
+ cmp1 = temp;
+ }
+
+ brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM);
+ emit_insn (gen_fn (brtmp, cmp0, cmp1));
+
+ return gen_rtx (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
+ }
+
+
+ void
+ xtensa_expand_conditional_branch (operands, test_code)
+ rtx *operands;
+ enum rtx_code test_code;
+ {
+ enum cmp_type type = branch_type;
+ rtx cmp0 = branch_cmp[0];
+ rtx cmp1 = branch_cmp[1];
+ rtx cmp;
+ int invert;
+ rtx label1, label2;
+
+ switch (type)
+ {
+ case CMP_DF:
+ default:
+ abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
+
+ case CMP_SI:
+ invert = FALSE;
+ cmp = gen_int_relational (test_code, cmp0, cmp1, &invert);
+ break;
+
+ case CMP_SF:
+ if (!TARGET_HARD_FLOAT)
+ abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1),
+ "bad test");
+ invert = FALSE;
+ cmp = gen_float_relational (test_code, cmp0, cmp1);
+ break;
+ }
+
+ /* Generate the branch. */
+
+ label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
+ label2 = pc_rtx;
+
+ if (invert)
+ {
+ label2 = label1;
+ label1 = pc_rtx;
+ }
+
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, cmp,
+ label1,
+ label2)));
+ }
+
+
+ static rtx
+ gen_conditional_move (cmp)
+ rtx cmp;
+ {
+ enum rtx_code code = GET_CODE (cmp);
+ rtx op0 = branch_cmp[0];
+ rtx op1 = branch_cmp[1];
+
+ if (branch_type == CMP_SI)
+ {
+ /* Jump optimization calls get_condition() which canonicalizes
+ comparisons like (GE x <const>) to (GT x <const-1>).
+ Transform those comparisons back to GE, since that is the
+ comparison supported in Xtensa. We shouldn't have to
+ transform <LE x const> comparisons, because neither
+ xtensa_expand_conditional_branch() nor get_condition() will
+ produce them. */
+
+ if ((code == GT) && (op1 == constm1_rtx))
+ {
+ code = GE;
+ op1 = const0_rtx;
+ }
+ cmp = gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
+
+ if (boolean_operator (cmp, VOIDmode))
+ {
+ /* swap the operands to make const0 second */
+ if (op0 == const0_rtx)
+ {
+ op0 = op1;
+ op1 = const0_rtx;
+ }
+
+ /* if not comparing against zero, emit a comparison (subtract) */
+ if (op1 != const0_rtx)
+ {
+ op0 = expand_binop (SImode, sub_optab, op0, op1,
+ 0, 0, OPTAB_LIB_WIDEN);
+ op1 = const0_rtx;
+ }
+ }
+ else if (branch_operator (cmp, VOIDmode))
+ {
+ /* swap the operands to make const0 second */
+ if (op0 == const0_rtx)
+ {
+ op0 = op1;
+ op1 = const0_rtx;
+
+ switch (code)
+ {
+ case LT: code = GE; break;
+ case GE: code = LT; break;
+ default: abort();
+ }
+ }
+
+ if (op1 != const0_rtx)
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+
+ return gen_rtx (code, VOIDmode, op0, op1);
+ }
+
+ if (TARGET_HARD_FLOAT && (branch_type == CMP_SF))
+ {
+ return gen_float_relational (code, op0, op1);
+ }
+
+ return 0;
+ }
+
+
+ int
+ xtensa_expand_conditional_move (operands, isflt)
+ rtx *operands;
+ int isflt;
+ {
+ rtx cmp;
+ rtx (*gen_fn) PARAMS ((rtx, rtx, rtx, rtx, rtx));
+
+ if (!(cmp = gen_conditional_move (operands[1])))
+ return 0;
+
+ if (isflt)
+ gen_fn = (branch_type == CMP_SI
+ ? gen_movsfcc_internal0
+ : gen_movsfcc_internal1);
+ else
+ gen_fn = (branch_type == CMP_SI
+ ? gen_movsicc_internal0
+ : gen_movsicc_internal1);
+
+ emit_insn (gen_fn (operands[0], XEXP (cmp, 0),
+ operands[2], operands[3], cmp));
+ return 1;
+ }
+
+
+ int
+ xtensa_expand_scc (operands)
+ rtx *operands;
+ {
+ rtx dest = operands[0];
+ rtx cmp = operands[1];
+ rtx one_tmp, zero_tmp;
+ rtx (*gen_fn) PARAMS ((rtx, rtx, rtx, rtx, rtx));
+
+ if (!(cmp = gen_conditional_move (cmp)))
+ return 0;
+
+ one_tmp = gen_reg_rtx (SImode);
+ zero_tmp = gen_reg_rtx (SImode);
+ emit_insn (gen_movsi (one_tmp, const_true_rtx));
+ emit_insn (gen_movsi (zero_tmp, const0_rtx));
+
+ gen_fn = (branch_type == CMP_SI
+ ? gen_movsicc_internal0
+ : gen_movsicc_internal1);
+ emit_insn (gen_fn (dest, XEXP (cmp, 0), one_tmp, zero_tmp, cmp));
+ return 1;
+ }
+
+
+ /* Emit insns to move operands[1] into operands[0].
+
+ Return 1 if we have written out everything that needs to be done to
+ do the move. Otherwise, return 0 and the caller will emit the move
+ normally. */
+
+ int
+ xtensa_emit_move_sequence (operands, mode)
+ rtx *operands;
+ enum machine_mode mode;
+ {
+ if (CONSTANT_P (operands[1])
+ && GET_CODE (operands[1]) != CONSTANT_P_RTX
+ && (GET_CODE (operands[1]) != CONST_INT
+ || !xtensa_simm12b (INTVAL (operands[1]))))
+ {
+ xtensa_load_constant (operands[0], operands[1]);
+ return 1;
+ }
+
+ if (!(reload_in_progress | reload_completed))
+ {
+ if (!non_acc_reg_operand (operands[0], mode)
+ && !non_acc_reg_operand (operands[1], mode))
+ operands[1] = force_reg (mode, operands[1]);
+
+ if (a7_overlap_mentioned_p (operands[1]))
+ {
+ switch (mode)
+ {
+ case SImode:
+ emit_insn (gen_movsi_internal (operands[0], operands[1]));
+ break;
+ case HImode:
+ emit_insn (gen_movhi_internal (operands[0], operands[1]));
+ break;
+ case QImode:
+ emit_insn (gen_movqi_internal (operands[0], operands[1]));
+ break;
+ default:
+ abort();
+ }
+ emit_insn (gen_set_frame_ptr ());
+ return 1;
+ }
+ }
+
+ /* During reload we don't want to emit (subreg:X (mem:Y)) since that
+ instruction won't be recognized after reload. So we remove the
+ subreg and adjust mem accordingly. */
+ if (reload_in_progress && GET_CODE (operands[0]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[0])) == REG
+ && REGNO (SUBREG_REG (operands[0])) >= FIRST_PSEUDO_REGISTER)
+ {
+ SUBREG_REG (operands[0]) =
+ reg_equiv_mem[REGNO (SUBREG_REG (operands[0]))];
+ operands[0] = alter_subreg (operands[0]);
+ }
+
+ if (reload_in_progress && GET_CODE (operands[1]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[1])) == REG
+ && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER)
+ {
+ SUBREG_REG (operands[1]) =
+ reg_equiv_mem[REGNO (SUBREG_REG (operands[1]))];
+ operands[1] = alter_subreg (operands[1]);
+ }
+
+ return 0;
+ }
+
+
+ /* Try to expand a block move operation to an RTL block move instruction.
+ If not optimizing or if the block size is not a constant or if the
+ block is small, the expansion fails and GCC falls back to calling
+ memcpy().
+
+ operands[0] is the destination
+ operands[1] is the source
+ operands[2] is the length
+ operands[3] is the alignment */
+
+ int
+ xtensa_expand_block_move (operands)
+ rtx *operands;
+ {
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ int bytes = INTVAL (operands[2]);
+ int align = XINT (operands[3], 0);
+ int num_pieces, move_ratio;
+
+ /* If this is not a fixed size move, just call memcpy */
+ if (!optimize || (GET_CODE (operands[2]) != CONST_INT))
+ return 0;
+
+ /* Anything to move? */
+ if (bytes <= 0)
+ return 1;
+
+ if (align > MOVE_MAX)
+ align = MOVE_MAX;
+
+ /* decide whether to expand inline based on the optimization level */
+ move_ratio = 4;
+ if (optimize > 2)
+ move_ratio = LARGEST_MOVE_RATIO;
+ num_pieces = (bytes / align) + (bytes % align); /* close enough anyway */
+ if (num_pieces >= move_ratio)
+ return 0;
+
+ /* make sure the memory addresses are valid */
+ operands[0] = change_address (dest, VOIDmode, NULL);
+ operands[1] = change_address (src, VOIDmode, NULL);
+
+ emit_insn (gen_movstrsi_internal (operands[0], operands[1],
+ operands[2], operands[3]));
+ return 1;
+ }
+
+
+ /* Emit a sequence of instructions to implement a block move, trying
+ to hide load delay slots as much as possible. Load N values into
+ temporary registers, store those N values, and repeat until the
+ complete block has been moved. N=delay_slots+1 */
+
+ struct meminsnbuf {
+ char template[30];
+ rtx operands[2];
+ };
+
+ void
+ xtensa_emit_block_move (operands, tmpregs, delay_slots)
+ rtx *operands;
+ rtx *tmpregs;
+ int delay_slots;
+ {
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ int bytes = INTVAL (operands[2]);
+ int align = XINT (operands[3], 0);
+ rtx from_addr = XEXP (src, 0);
+ rtx to_addr = XEXP (dest, 0);
+ int from_struct = MEM_IN_STRUCT_P (src);
+ int to_struct = MEM_IN_STRUCT_P (dest);
+ int offset = 0;
+ int chunk_size, item_size;
+ struct meminsnbuf *ldinsns, *stinsns;
+ const char *ldname, *stname;
+ enum machine_mode mode;
+
+ if (align > MOVE_MAX)
+ align = MOVE_MAX;
+ item_size = align;
+ chunk_size = delay_slots + 1;
+
+ ldinsns = (struct meminsnbuf *)
+ alloca (chunk_size * sizeof(struct meminsnbuf));
+ stinsns = (struct meminsnbuf *)
+ alloca (chunk_size * sizeof(struct meminsnbuf));
+
+ mode = xtensa_find_mode_for_size (item_size);
+ item_size = GET_MODE_SIZE (mode);
+ ldname = xtensa_ld_opcodes[(int) mode];
+ stname = xtensa_st_opcodes[(int) mode];
+
+ while (bytes > 0)
+ {
+ int n;
+
+ for (n = 0; n < chunk_size; n++)
+ {
+ rtx addr, mem;
+
+ if (bytes == 0)
+ {
+ chunk_size = n;
+ break;
+ }
+
+ if (bytes < item_size)
+ {
+ /* find a smaller item_size which we can load & store */
+ item_size = bytes;
+ mode = xtensa_find_mode_for_size (item_size);
+ item_size = GET_MODE_SIZE (mode);
+ ldname = xtensa_ld_opcodes[(int) mode];
+ stname = xtensa_st_opcodes[(int) mode];
+ }
+
+ /* record the load instruction opcode and operands */
+ addr = plus_constant (from_addr, offset);
+ mem = gen_rtx_MEM (mode, addr);
+ if (! memory_address_p (mode, addr))
+ abort ();
+ MEM_IN_STRUCT_P (mem) = from_struct;
+ ldinsns[n].operands[0] = tmpregs[n];
+ ldinsns[n].operands[1] = mem;
+ sprintf (ldinsns[n].template, "%s\t%%0, %%1", ldname);
+
+ /* record the store instruction opcode and operands */
+ addr = plus_constant (to_addr, offset);
+ mem = gen_rtx_MEM (mode, addr);
+ if (! memory_address_p (mode, addr))
+ abort ();
+ MEM_IN_STRUCT_P (mem) = to_struct;
+ stinsns[n].operands[0] = tmpregs[n];
+ stinsns[n].operands[1] = mem;
+ sprintf (stinsns[n].template, "%s\t%%0, %%1", stname);
+
+ offset += item_size;
+ bytes -= item_size;
+ }
+
+ /* now output the loads followed by the stores */
+ for (n = 0; n < chunk_size; n++)
+ output_asm_insn(ldinsns[n].template, ldinsns[n].operands);
+ for (n = 0; n < chunk_size; n++)
+ output_asm_insn(stinsns[n].template, stinsns[n].operands);
+ }
+ }
+
+
+ static enum machine_mode
+ xtensa_find_mode_for_size (item_size)
+ unsigned item_size;
+ {
+ enum machine_mode mode, tmode;
+
+ while (1)
+ {
+ mode = VOIDmode;
+
+ /* find mode closest to but not bigger than item_size */
+ for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+ if (GET_MODE_SIZE (tmode) <= item_size)
+ mode = tmode;
+ if (mode == VOIDmode)
+ abort ();
+
+ item_size = GET_MODE_SIZE (mode);
+
+ if (xtensa_ld_opcodes[(int) mode]
+ && xtensa_st_opcodes[(int) mode])
+ break;
+
+ /* cannot load & store this mode; try something smaller */
+ item_size -= 1;
+ }
+
+ return mode;
+ }
+
+
+ void
+ xtensa_expand_nonlocal_goto (operands)
+ rtx *operands;
+ {
+ rtx goto_handler = operands[1];
+ rtx containing_fp = operands[3];
+
+ /* generate a call to "_xtensa_nonlocal_goto" (in libgcc); the code
+ is too big to generate in-line */
+
+ if (GET_CODE (containing_fp) != REG)
+ containing_fp = force_reg (Pmode, containing_fp);
+
+ goto_handler = replace_rtx (copy_rtx (goto_handler),
+ virtual_stack_vars_rtx,
+ containing_fp);
+
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "_xtensa_nonlocal_goto"),
+ 0, VOIDmode, 2,
+ containing_fp, Pmode,
+ goto_handler, Pmode);
+ }
+
+
+ static void
+ xtensa_init_machine_status (p)
+ struct function *p;
+ {
+ p->machine = (struct machine_function *)
+ xcalloc (1, sizeof (struct machine_function));
+ }
+
+
+ static void
+ xtensa_free_machine_status (p)
+ struct function *p;
+ {
+ free (p->machine);
+ p->machine = NULL;
+ }
+
+
+ void
+ xtensa_setup_frame_addresses ()
+ {
+ /* Set flag to cause FRAME_POINTER_REQUIRED to be set. */
+ cfun->machine->accesses_prev_frame = 1;
+
+ emit_library_call
+ (gen_rtx_SYMBOL_REF (Pmode, "_xtensa_libgcc_window_spill"),
+ 0, VOIDmode, 0);
+ }
+
+
+ /* Emit the assembly for the end of a zero-cost loop. Normally we just emit
+ a comment showing where the end of the loop is. However, if there is a
+ label or a branch at the end of the loop then we need to place a nop
+ there. If the loop ends with a label we need the nop so that branches
+ targetting that label will target the nop (and thus remain in the loop),
+ instead of targetting the instruction after the loop (and thus exiting
+ the loop). If the loop ends with a branch, we need the nop in case the
+ branch is targetting a location inside the loop. When the branch
+ executes it will cause the loop count to be decremented even if it is
+ taken (because it is the last instruction in the loop), so we need to
+ nop after the branch to prevent the loop count from being decremented
+ when the branch is taken. */
+
+ void
+ xtensa_emit_loop_end (insn, operands)
+ rtx insn;
+ rtx *operands;
+ {
+ char done = 0;
+
+ for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
+ {
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ case BARRIER:
+ break;
+
+ case CODE_LABEL:
+ output_asm_insn ("nop.n", operands);
+ done = 1;
+ break;
+
+ default:
+ {
+ rtx body = PATTERN (insn);
+
+ if (GET_CODE (body) == JUMP_INSN)
+ {
+ output_asm_insn ("nop.n", operands);
+ done = 1;
+ }
+ else if ((GET_CODE (body) != USE)
+ && (GET_CODE (body) != CLOBBER))
+ done = 1;
+ }
+ break;
+ }
+ }
+
+ output_asm_insn ("# loop end for %0", operands);
+ }
+
+
+ char *
+ xtensa_emit_call (callop, operands)
+ int callop;
+ rtx *operands;
+ {
+ char *result;
+ switch (which_alternative)
+ {
+ case 0:
+ case 3:
+ case 6:
+ result = (char *) malloc (64 * sizeof (char));
+ sprintf (result, "call8\t0x%x", INTVAL (operands[callop]));
+ break;
+
+ case 1:
+ case 4:
+ case 7:
+ result = (char *) ((callop == 0) ? "call8\t%0" : "call8\t%1");
+ break;
+
+ case 2:
+ case 5:
+ case 8:
+ result = (char *) ((callop == 0) ? "callx8\t%0" : "callx8\t%1");
+ break;
+
+ default:
+ abort ();
+ }
+
+ return result;
+ }
+
+
+ /* Return the stabs register number to use for 'regno'. */
+
+ int
+ xtensa_dbx_register_number (regno)
+ int regno;
+ {
+ int first = -1;
+
+ if (GP_REG_P(regno)) {
+ regno -= GP_REG_FIRST;
+ first = 0;
+ }
+ else if (BR_REG_P(regno)) {
+ regno -= BR_REG_FIRST;
+ first = 16;
+ }
+ else if (FP_REG_P(regno)) {
+ regno -= FP_REG_FIRST;
+ /* The current numbering convention is that TIE registers are
+ numbered in libcc order beginning with 256. We can't guarantee
+ that the FP registers will come first, so the following is just
+ a guess. It seems like we should make a special case for FP
+ registers and give them fixed numbers < 256. */
+ first = 256;
+ }
+ else if (ACC_REG_P(regno))
+ {
+ first = 0;
+ regno = -1;
+ }
+
+ /* When optimizing, we sometimes get asked about pseudo-registers
+ that don't represent hard registers. Return 0 for these. */
+ if (first == -1)
+ return 0;
+
+ return first + regno;
+ }
+
+
+ /* Argument support functions. */
+
+ /* Initialize CUMULATIVE_ARGS for a function. */
+
+ void
+ init_cumulative_args (cum, fntype, libname)
+ CUMULATIVE_ARGS *cum; /* argument info to initialize */
+ tree fntype ATTRIBUTE_UNUSED; /* tree ptr for function decl */
+ rtx libname ATTRIBUTE_UNUSED; /* SYMBOL_REF of library name or 0 */
+ {
+ cum->arg_words = 0;
+ }
+
+ /* Advance the argument to the next argument position. */
+
+ void
+ function_arg_advance (cum, mode, type)
+ CUMULATIVE_ARGS *cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ {
+ int words, max;
+ int *arg_words;
+
+ arg_words = &cum->arg_words;
+ max = MAX_ARGS_IN_REGISTERS;
+
+ words = (((mode != BLKmode)
+ ? (int) GET_MODE_SIZE (mode)
+ : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ if ((*arg_words + words > max) && (*arg_words < max))
+ *arg_words = max;
+
+ *arg_words += words;
+ }
+
+
+ /* Return an RTL expression containing the register for the given mode,
+ or 0 if the argument is to be passed on the stack. */
+
+ rtx
+ function_arg (cum, mode, type, incoming_p)
+ CUMULATIVE_ARGS *cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int incoming_p; /* computing the incoming registers? */
+ {
+ int regbase, words, max;
+ int *arg_words;
+ int regno;
+ enum machine_mode result_mode;
+
+ arg_words = &cum->arg_words;
+ regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
+ max = MAX_ARGS_IN_REGISTERS;
+
+ words = (((mode != BLKmode)
+ ? (int) GET_MODE_SIZE (mode)
+ : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
+ *arg_words += (*arg_words & 1);
+
+ if (*arg_words + words > max)
+ return (rtx)0;
+
+ regno = regbase + *arg_words;
+ result_mode = (mode == BLKmode ? TYPE_MODE (type) : mode);
+
+ /* We need to make sure that references to a7 are represented with
+ rtx that is not equal to hard_frame_pointer_rtx. For BLKmode and
+ modes bigger than 2 words (because we only have patterns for
+ modes of 2 words or smaller), we can't control the expansion
+ unless we explicitly list the individual registers in a PARALLEL. */
+
+ if ((mode == BLKmode || words > 2)
+ && regno < A7_REG
+ && regno + words > A7_REG)
+ {
+ rtx result;
+ int n;
+
+ result = gen_rtx_PARALLEL (result_mode, rtvec_alloc (words));
+ for (n = 0; n < words; n++)
+ {
+ XVECEXP (result, 0, n) =
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_raw_REG (SImode, regno + n),
+ GEN_INT (n * UNITS_PER_WORD));
+ }
+ return result;
+ }
+
+ return gen_raw_REG (result_mode, regno);
+ }
+
+
+ /* Abort after printing out a specific insn. */
+
+ void
+ abort_with_insn (insn, reason)
+ rtx insn;
+ const char *reason;
+ {
+ error (reason);
+ debug_rtx (insn);
+ abort ();
+ }
+
+
+ void
+ override_options ()
+ {
+ int regno;
+ enum machine_mode mode;
+
+ if (!TARGET_BOOLEANS && TARGET_HARD_FLOAT)
+ error ("boolean registers required for the floating-point option");
+
+ /* set up the tables of ld/st opcode names for block moves */
+ xtensa_ld_opcodes[(int) SImode] = "l32i";
+ xtensa_ld_opcodes[(int) HImode] = "l16ui";
+ xtensa_ld_opcodes[(int) QImode] = "l8ui";
+ xtensa_st_opcodes[(int) SImode] = "s32i";
+ xtensa_st_opcodes[(int) HImode] = "s16i";
+ xtensa_st_opcodes[(int) QImode] = "s8i";
+
+ xtensa_char_to_class['q'] = SP_REG;
+ xtensa_char_to_class['a'] = GR_REGS;
+ xtensa_char_to_class['b'] = ((TARGET_BOOLEANS) ? BR_REGS : NO_REGS);
+ xtensa_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
+ xtensa_char_to_class['A'] = ((TARGET_MAC16) ? ACC_REG : NO_REGS);
+ xtensa_char_to_class['B'] = ((TARGET_SEXT) ? GR_REGS : NO_REGS);
+ xtensa_char_to_class['C'] = ((TARGET_MUL16) ? GR_REGS: NO_REGS);
+ xtensa_char_to_class['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
+ xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
+
+ /* Set up array giving whether a given register can hold a given mode. */
+ for (mode = VOIDmode;
+ mode != MAX_MACHINE_MODE;
+ mode = (enum machine_mode)((int)mode + 1))
+ {
+ int size = GET_MODE_SIZE (mode);
+ enum mode_class class = GET_MODE_CLASS (mode);
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ int temp;
+
+ if (ACC_REG_P (regno))
+ temp = (TARGET_MAC16 &&
+ (class == MODE_INT) && (size <= UNITS_PER_WORD));
+ else if (GP_REG_P (regno))
+ temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
+ else if (FP_REG_P (regno))
+ temp = (TARGET_HARD_FLOAT && (mode == SFmode));
+ else if (BR_REG_P (regno))
+ temp = (TARGET_BOOLEANS && (mode == CCmode));
+ else
+ temp = FALSE;
+
+ xtensa_hard_regno_mode_ok[(int)mode][regno] = temp;
+ }
+ }
+
+ init_machine_status = xtensa_init_machine_status;
+ free_machine_status = xtensa_free_machine_status;
+ }
+
+
+ /* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand X. X is an RTL
+ expression.
+
+ CODE is a value that can be used to specify one of several ways
+ of printing the operand. It is used when identical operands
+ must be printed differently depending on the context. CODE
+ comes from the '%' specification that was used to request
+ printing of the operand. If the specification was just '%DIGIT'
+ then CODE is 0; if the specification was '%LTR DIGIT' then CODE
+ is the ASCII code for LTR.
+
+ If X is a register, this macro should print the register's name.
+ The names can be found in an array 'reg_names' whose type is
+ 'char *[]'. 'reg_names' is initialized from 'REGISTER_NAMES'.
+
+ When the machine description has a specification '%PUNCT' (a '%'
+ followed by a punctuation character), this macro is called with
+ a null pointer for X and the punctuation character for CODE.
+
+ 'a', 'c', 'l', and 'n' are reserved.
+
+ The Xtensa specific codes are:
+
+ 'd' CONST_INT, print as signed decimal
+ 'x' CONST_INT, print as signed hexadecimal
+ 'S' CONST_INT, print 7-bit hex value using movi.n rule for sign-extension
+ 'F' CONST_INT, print 12-bit value as signed hexadecimal
+ 'M' CONST_INT, print 16-bit value as signed hexadecimal
+ 'K' CONST_INT, print number of bits in mask for EXTUI
+ 'R' CONST_INT, print (X & 0x1f)
+ 'L' CONST_INT, print ((32 - X) & 0x1f)
+ 'D' REG, print second register of double-word register operand
+ 'N' MEM, print address of next word following a memory operand
+ 'v' MEM, if memory reference is volatile, output a MEMW before it
+ */
+
+ static void
+ printx (file, val)
+ FILE *file;
+ signed int val;
+ {
+ /* print a hexadecimal value in a nice way */
+ if ((val > -0xa) && (val < 0xa))
+ fprintf (file, "%d", val);
+ else if (val < 0)
+ fprintf (file, "-0x%x", -val);
+ else
+ fprintf (file, "0x%x", val);
+ }
+
+
+ void
+ print_operand (file, op, letter)
+ FILE *file; /* file to write to */
+ rtx op; /* operand to print */
+ int letter; /* %<letter> or 0 */
+ {
+ register enum rtx_code code;
+
+ if (! op)
+ error ("PRINT_OPERAND null pointer");
+
+ code = GET_CODE (op);
+ switch (code)
+ {
+ case REG:
+ case SUBREG:
+ {
+ int regnum = xt_true_regnum (op);
+ if (letter == 'D')
+ regnum++;
+ fprintf (file, "%s", reg_names[regnum]);
+ break;
+ }
+
+ case MEM:
+ /*
+ * For a volatile memory reference, emit a MEMW before the
+ * load or store.
+ */
+ if (letter == 'v')
+ {
+ if (MEM_VOLATILE_P (op) && TARGET_SERIALIZE_VOLATILE)
+ fprintf (file, "memw\n\t");
+ break;
+ }
+ else if (letter == 'N')
+ op = adj_offsettable_operand (op, 4);
+
+ output_address (XEXP (op, 0));
+ break;
+
+ case CONST_INT:
+ switch (letter)
+ {
+ case 'S':
+ {
+ /* sign extend 7-bit value if top _two_ bits are set */
+ signed int val = INTVAL(op) & 0x7f;
+ if ((val & 0x60) == 0x60)
+ val = (val << 25) >> 25;
+ printx (file, val);
+ break;
+ }
+
+ case 'F':
+ {
+ /* sign extend 12-bit value */
+ signed int val = INTVAL(op);
+ val = (val << 20) >> 20;
+ printx(file, val);
+ break;
+ }
+
+ case 'M':
+ {
+ signed int val = INTVAL(op);
+ val = (val << 16) >> 16;
+ printx(file, val);
+ break;
+ }
+
+ case 'K':
+ {
+ int num_bits = 0;
+ unsigned val = INTVAL(op);
+ while (val & 1)
+ {
+ num_bits += 1;
+ val = val >> 1;
+ }
+ if ((val != 0) || (num_bits == 0) || (num_bits > 16))
+ abort_with_insn (op, "PRINT_OPERAND, invalid mask");
+
+ fprintf (file, "%d", num_bits);
+ break;
+ }
+
+ case 'L':
+ fprintf(file, "%d", (32 - INTVAL(op)) & 0x1f);
+ break;
+
+ case 'R':
+ fprintf(file, "%d", INTVAL(op) & 0x1f);
+ break;
+
+ case 'x':
+ printx(file, INTVAL(op));
+ break;
+
+ case 'd':
+ default:
+ fprintf (file, "%d", INTVAL(op));
+ break;
+
+ }
+ break;
+
+ default:
+ output_addr_const (file, op);
+ }
+ }
+
+
+ /* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand that is a memory
+ reference whose address is ADDR. ADDR is an RTL expression.
+
+ On some machines, the syntax for a symbolic address depends on
+ the section that the address refers to. On these machines,
+ define the macro 'ENCODE_SECTION_INFO' to store the information
+ into the 'symbol_ref', and then check for it here.
+ */
+
+ void
+ print_operand_address (file, addr)
+ FILE *file;
+ rtx addr;
+ {
+ if (!addr)
+ error ("PRINT_OPERAND_ADDRESS, null pointer");
+
+ switch (GET_CODE (addr))
+ {
+ default:
+ abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
+ break;
+
+ case REG:
+ fprintf (file, "%s, 0", reg_names [REGNO (addr)]);
+ break;
+
+ case PLUS:
+ {
+ rtx reg = (rtx)0;
+ rtx offset = (rtx)0;
+ rtx arg0 = XEXP (addr, 0);
+ rtx arg1 = XEXP (addr, 1);
+
+ if (GET_CODE (arg0) == REG)
+ {
+ reg = arg0;
+ offset = arg1;
+ }
+ else if (GET_CODE (arg1) == REG)
+ {
+ reg = arg1;
+ offset = arg0;
+ }
+ else
+ abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
+
+ if (CONSTANT_P (offset))
+ {
+ fprintf (file, "%s, ", reg_names [REGNO (reg)]);
+ output_addr_const (file, offset);
+ }
+ else
+ abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
+ }
+ break;
+
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ output_addr_const (file, addr);
+ break;
+ }
+ }
+
+
+ /* Emit either a label, .comm, or .lcomm directive. */
+
+ void
+ xtensa_declare_object (stream, name, init_string, final_string, size)
+ FILE *stream;
+ char *name;
+ char *init_string;
+ char *final_string;
+ int size;
+ {
+ fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */
+ assemble_name (stream, name);
+ fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */
+ }
+
+
+ /* Output a double precision value to the assembler. If both the
+ host and target are IEEE, emit the values in hex. */
+
+ void
+ xtensa_output_double (stream, value)
+ FILE *stream;
+ REAL_VALUE_TYPE value;
+ {
+ #ifdef REAL_VALUE_TO_TARGET_DOUBLE
+ long value_long[2];
+ REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
+
+ fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n",
+ value_long[0], value, value_long[1]);
+ #else
+ fprintf (stream, "\t.double\t%.20g\n", value);
+ #endif
+ }
+
+
+ /* Output a single precision value to the assembler. If both the
+ host and target are IEEE, emit the values in hex. */
+
+ void
+ xtensa_output_float (stream, value)
+ FILE *stream;
+ REAL_VALUE_TYPE value;
+ {
+ #ifdef REAL_VALUE_TO_TARGET_SINGLE
+ long value_long;
+ REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
+
+ fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n",
+ value_long, value);
+ #else
+ fprintf (stream, "\t.float\t%.12g\n", value);
+ #endif
+ }
+
+
+ /* Return the bytes needed to compute the frame pointer from the current
+ stack pointer. */
+
+ #define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+ #define XTENSA_STACK_ALIGN(LOC) (((LOC) + STACK_BYTES-1) & ~(STACK_BYTES-1))
+
+ long
+ compute_frame_size (size)
+ int size; /* # of var. bytes allocated */
+ {
+ /* add space for the incoming static chain value */
+ if (current_function_needs_context)
+ size += (1 * UNITS_PER_WORD);
+
+ xtensa_current_frame_size =
+ XTENSA_STACK_ALIGN(size
+ + current_function_outgoing_args_size
+ + (WINDOW_SIZE * UNITS_PER_WORD));
+ return xtensa_current_frame_size;
+ }
+
+
+ int
+ xtensa_frame_pointer_required ()
+ {
+ /* The code to expand builtin_frame_addr and builtin_return_addr
+ currently uses the hard_frame_pointer instead of frame_pointer.
+ This seems wrong but maybe it's necessary for other architectures.
+ This function is derived from the i386 code. */
+
+ if (cfun->machine->accesses_prev_frame)
+ return 1;
+
+ return 0;
+ }
+
+
+ void
+ xtensa_reorg (first)
+ rtx first;
+ {
+ rtx insn, set_frame_ptr_insn = 0;
+
+ unsigned long tsize = compute_frame_size (get_frame_size ());
+ if (tsize < (1 << (12+3)))
+ frame_size_const = 0;
+ else
+ {
+ frame_size_const = force_const_mem (SImode, GEN_INT (tsize - 16));;
+
+ /* make sure the constant is used so it doesn't get eliminated
+ from the constant pool */
+ emit_insn_before (gen_rtx_USE (SImode, frame_size_const), first);
+ }
+
+ if (!frame_pointer_needed)
+ return;
+
+ /* Search all instructions, looking for the insn that sets up the
+ frame pointer. This search will fail if the function does not
+ have an incoming argument in $a7, but in that case, we can just
+ set up the frame pointer at the very beginning of the
+ function. */
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx pat;
+
+ if (!INSN_P (insn))
+ continue;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) == UNSPEC_VOLATILE
+ && (XINT (pat, 1) == UNSPECV_SET_FP))
+ {
+ set_frame_ptr_insn = insn;
+ break;
+ }
+ }
+
+ if (set_frame_ptr_insn)
+ {
+ /* for all instructions prior to set_frame_ptr_insn, replace
+ hard_frame_pointer references with stack_pointer */
+ for (insn = first; insn != set_frame_ptr_insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
+ hard_frame_pointer_rtx,
+ stack_pointer_rtx);
+ }
+ }
+ else
+ {
+ /* emit the frame pointer move immediately after the NOTE that starts
+ the function */
+ emit_insn_after (gen_movsi (hard_frame_pointer_rtx,
+ stack_pointer_rtx), first);
+ }
+ }
+
+
+ /* Set up the stack and frame (if desired) for the function. */
+
+ void
+ xtensa_function_prologue (file, size)
+ FILE *file;
+ int size ATTRIBUTE_UNUSED;
+ {
+ unsigned long tsize = compute_frame_size (get_frame_size ());
+
+ if (frame_pointer_needed)
+ fprintf(file, "\t.frame\ta7, %ld\n", tsize);
+ else
+ fprintf(file, "\t.frame\tsp, %ld\n", tsize);
+
+
+ if (tsize < (1 << (12+3)))
+ {
+ fprintf(file, "\tentry\tsp, %ld\n", tsize);
+ }
+ else
+ {
+ fprintf(file, "\tentry\tsp, 16\n");
+
+ /* use a8 as a temporary since a0-a7 may be live */
+ fprintf(file, "\tl32r\ta8, ");
+ print_operand(file, frame_size_const, 0);
+ fprintf(file, "\n\tsub\ta8, sp, a8\n");
+ fprintf(file, "\tmovsp\tsp, a8\n");
+ }
+ }
+
+
+ /* Do any necessary cleanup after a function to restore
+ stack, frame, and regs. */
+
+ void
+ xtensa_function_epilogue (file, size)
+ FILE *file;
+ int size ATTRIBUTE_UNUSED;
+ {
+ rtx insn = get_last_insn ();
+ /* If the last insn was a BARRIER, we don't have to write anything. */
+ if (GET_CODE (insn) == NOTE)
+ insn = prev_nonnote_insn (insn);
+ if (insn == 0 || GET_CODE (insn) != BARRIER)
+ fprintf(file, TARGET_DENSITY ? "\tretw.n\n" : "\tretw\n");
+
+ xtensa_current_frame_size = 0;
+ }
+
+
+ /* Create the va_list data type.
+ This structure is set up by __builtin_saveregs. The __va_reg
+ field points to a stack-allocated region holding the contents of the
+ incoming argument registers. The __va_ndx field is an index initialized
+ to the position of the first unnamed (variable) argument. This same index
+ is also used to address the arguments passed in memory. Thus, the
+ __va_stk field is initialized to point to the position of the first
+ argument in memory offset to account for the arguments passed in
+ registers. E.G., if there are 6 argument registers, and each register is
+ 4 bytes, then __va_stk is set to $sp - (6 * 4); then __va_reg[N*4]
+ references argument word N for 0 <= N < 6, and __va_stk[N*4] references
+ argument word N for N >= 6. */
+
+ tree
+ xtensa_build_va_list (void)
+ {
+ tree f_stk, f_reg, f_ndx, record;
+
+ record = make_node (RECORD_TYPE);
+
+ f_stk = build_decl (FIELD_DECL, get_identifier ("__va_stk"),
+ ptr_type_node);
+ f_reg = build_decl (FIELD_DECL, get_identifier ("__va_reg"),
+ ptr_type_node);
+ f_ndx = build_decl (FIELD_DECL, get_identifier ("__va_ndx"),
+ integer_type_node);
+
+ DECL_FIELD_CONTEXT (f_stk) = record;
+ DECL_FIELD_CONTEXT (f_reg) = record;
+ DECL_FIELD_CONTEXT (f_ndx) = record;
+
+ TYPE_FIELDS (record) = f_stk;
+ TREE_CHAIN (f_stk) = f_reg;
+ TREE_CHAIN (f_reg) = f_ndx;
+
+ layout_type (record);
+ return record;
+ }
+
+
+ /* Save the incoming argument registers on the stack. Returns the
+ address of the saved registers. */
+
+ rtx
+ xtensa_builtin_saveregs ()
+ {
+ rtx gp_regs, dest;
+ int arg_words = current_function_arg_words;
+ int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
+ int i;
+
+ if (gp_left == 0)
+ return const0_rtx;
+
+ /* allocate the general-purpose register space */
+ gp_regs = assign_stack_local
+ (BLKmode, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1);
+ MEM_IN_STRUCT_P (gp_regs) = 1;
+ RTX_UNCHANGING_P (gp_regs) = 1;
+ RTX_UNCHANGING_P (XEXP (gp_regs, 0)) = 1;
+
+ /* Now store the incoming registers. */
+ dest = change_address (gp_regs, SImode,
+ plus_constant (XEXP (gp_regs, 0),
+ arg_words * UNITS_PER_WORD));
+
+ /* Note: Don't use move_block_from_reg() here because the incoming
+ argument in a7 cannot be represented by hard_frame_pointer_rtx.
+ Instead, call gen_raw_REG() directly so that we get a distinct
+ instance of (REG:SI 7). */
+ for (i = 0; i < gp_left; i++)
+ {
+ emit_move_insn (operand_subword (dest, i, 1, BLKmode),
+ gen_raw_REG (SImode, GP_ARG_FIRST + arg_words + i));
+ }
+
+ emit_insn (gen_set_frame_ptr ());
+
+ if (current_function_check_memory_usage)
+ {
+ emit_library_call
+ (chkr_set_right_libfunc, 1, VOIDmode, 3, dest, ptr_mode,
+ GEN_INT (UNITS_PER_WORD * gp_left),
+ TYPE_MODE (sizetype),
+ GEN_INT (MEMORY_USE_RW), QImode);
+ }
+
+ return XEXP (gp_regs, 0);
+ }
+
+
+ /* Implement `va_start' for varargs and stdarg. We look at the
+ current function to fill in an initial va_list. */
+
+ void
+ xtensa_va_start (stdarg_p, valist, nextarg)
+ int stdarg_p ATTRIBUTE_UNUSED;
+ tree valist;
+ rtx nextarg ATTRIBUTE_UNUSED;
+ {
+ tree f_stk, stk;
+ tree f_reg, reg;
+ tree f_ndx, ndx;
+ tree t, u;
+ int arg_words;
+
+ arg_words = current_function_args_info.arg_words;
+
+ f_stk = TYPE_FIELDS (va_list_type_node);
+ f_reg = TREE_CHAIN (f_stk);
+ f_ndx = TREE_CHAIN (f_reg);
+
+ stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
+ reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
+ ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+
+ /* Call __builtin_saveregs; save the result in __va_reg */
+ current_function_arg_words = arg_words;
+ u = make_tree (ptr_type_node, expand_builtin_saveregs ());
+ t = build (MODIFY_EXPR, ptr_type_node, reg, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Set the __va_stk member to $arg_ptr - (size of __va_reg area) */
+ u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
+ u = fold (build (PLUS_EXPR, ptr_type_node, u,
+ build_int_2 (-MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1)));
+ t = build (MODIFY_EXPR, ptr_type_node, stk, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Set the __va_ndx member. */
+ u = build_int_2 (arg_words * UNITS_PER_WORD, 0);
+ t = build (MODIFY_EXPR, integer_type_node, ndx, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+
+
+ /* Implement `va_arg'. */
+
+ rtx
+ xtensa_va_arg (valist, type)
+ tree valist, type;
+ {
+ tree f_stk, stk;
+ tree f_reg, reg;
+ tree f_ndx, ndx;
+ tree tmp, addr_tree;
+ rtx array, orig_ndx, r, addr;
+ HOST_WIDE_INT size, va_size;
+ rtx lab_false, lab_over, lab_false2;
+
+ size = int_size_in_bytes (type);
+ va_size = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+
+ f_stk = TYPE_FIELDS (va_list_type_node);
+ f_reg = TREE_CHAIN (f_stk);
+ f_ndx = TREE_CHAIN (f_reg);
+
+ stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
+ reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
+ ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+
+
+ /* First align __va_ndx to a double word boundary if necessary for this arg:
+
+ if (__alignof__ (TYPE) > 4)
+ (AP).__va_ndx = (((AP).__va_ndx + 7) & -8)
+ */
+
+ if (TYPE_ALIGN (type) > BITS_PER_WORD)
+ {
+ tmp = build (PLUS_EXPR, integer_type_node, ndx,
+ build_int_2 ((2 * UNITS_PER_WORD) - 1, 0));
+ tmp = build (BIT_AND_EXPR, integer_type_node, tmp,
+ build_int_2 (-2 * UNITS_PER_WORD, -1));
+ tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
+ TREE_SIDE_EFFECTS (tmp) = 1;
+ expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+
+
+ /* Increment __va_ndx to point past the argument:
+
+ orig_ndx = (AP).__va_ndx;
+ (AP).__va_ndx += __va_size (TYPE);
+ */
+
+ orig_ndx = gen_reg_rtx (SImode);
+ r = expand_expr (ndx, orig_ndx, SImode, EXPAND_NORMAL);
+ if (r != orig_ndx)
+ emit_move_insn (orig_ndx, r);
+
+ tmp = build (PLUS_EXPR, integer_type_node, ndx, build_int_2 (va_size, 0));
+ tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
+ TREE_SIDE_EFFECTS (tmp) = 1;
+ expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+
+ /* Check if the argument is in registers:
+
+ if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
+ __array = (AP).__va_reg;
+ */
+
+ lab_false = gen_label_rtx ();
+ lab_over = gen_label_rtx ();
+ array = gen_reg_rtx (Pmode);
+
+ emit_cmp_and_jump_insns (expand_expr (ndx, NULL_RTX, SImode, EXPAND_NORMAL),
+ GEN_INT (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD),
+ GT, const1_rtx, SImode, 0, 1, lab_false);
+
+ r = expand_expr (reg, array, Pmode, EXPAND_NORMAL);
+ if (r != array)
+ emit_move_insn (array, r);
+
+ emit_jump_insn (gen_jump (lab_over));
+ emit_barrier ();
+ emit_label (lab_false);
+
+
+ /* ...otherwise, the argument is on the stack (never split between
+ registers and the stack -- change __va_ndx if necessary):
+
+ else
+ {
+ if (orig_ndx < __MAX_ARGS_IN_REGISTERS * 4)
+ (AP).__va_ndx = __MAX_ARGS_IN_REGISTERS * 4 + __va_size (TYPE);
+ __array = (AP).__va_stk;
+ }
+ */
+
+ lab_false2 = gen_label_rtx ();
+ emit_cmp_and_jump_insns (orig_ndx,
+ GEN_INT (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD),
+ GE, const1_rtx, SImode, 0, 1, lab_false2);
+
+ tmp = build_int_2 ((MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD) + va_size, 0);
+ tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
+ TREE_SIDE_EFFECTS (tmp) = 1;
+ expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ emit_label (lab_false2);
+
+ r = expand_expr (stk, array, Pmode, EXPAND_NORMAL);
+ if (r != array)
+ emit_move_insn (array, r);
+
+
+ /* Given the base array pointer (__array) and index to the subsequent
+ argument (__va_ndx), find the address:
+
+ Big-endian:
+ __array + (AP).__va_ndx - sizeof (TYPE)
+
+ Little-endian:
+ __array + (AP).__va_ndx - __va_size (TYPE)
+
+ The results are endian-dependent because values smaller than one word
+ are aligned differently.
+ */
+
+ emit_label (lab_over);
+
+ addr_tree = build (PLUS_EXPR, ptr_type_node,
+ make_tree (ptr_type_node, array),
+ ndx);
+ addr_tree = build (PLUS_EXPR, ptr_type_node,
+ addr_tree,
+ build_int_2 (BYTES_BIG_ENDIAN
+ && size < (PARM_BOUNDARY / BITS_PER_UNIT)
+ ? -size
+ : -va_size, -1));
+ addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
+ addr = copy_to_reg (addr);
+ return addr;
+ }
+
+
+ enum reg_class
+ xtensa_secondary_reload_class (class, mode, x, isoutput)
+ enum reg_class class;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ rtx x;
+ int isoutput;
+ {
+ int regno;
+
+ if (GET_CODE (x) == SIGN_EXTEND)
+ x = XEXP (x, 0);
+ regno = xt_true_regnum (x);
+
+ if (!isoutput)
+ {
+ if (class == FP_REGS && constantpool_mem_p (x))
+ return GR_REGS;
+ }
+
+ if (ACC_REG_P (regno))
+ return (class == GR_REGS ? NO_REGS : GR_REGS);
+ if (class == ACC_REG)
+ return (GP_REG_P (regno) ? NO_REGS : GR_REGS);
+
+ return NO_REGS;
+ }
+
+
+ void
+ order_regs_for_local_alloc ()
+ {
+ if (!leaf_function_p ())
+ {
+ bcopy ((char *) reg_nonleaf_alloc_order,
+ (char *) reg_alloc_order, FIRST_PSEUDO_REGISTER * sizeof(int));
+ }
+ else
+ {
+ int i, num_arg_regs;
+ int nxt = 0;
+
+ /* use the AR registers in increasing order (skipping a0 and a1)
+ but save the incoming argument registers for a last resort */
+ num_arg_regs = current_function_args_info.arg_words;
+ if (num_arg_regs > MAX_ARGS_IN_REGISTERS)
+ num_arg_regs = MAX_ARGS_IN_REGISTERS;
+ for (i = GP_ARG_FIRST; i < 16 - num_arg_regs; i++)
+ reg_alloc_order[nxt++] = i + num_arg_regs;
+ for (i = 0; i < num_arg_regs; i++)
+ reg_alloc_order[nxt++] = GP_ARG_FIRST + i;
+
+ /* list the FP registers in order for now */
+ for (i = 0; i < 16; i++)
+ reg_alloc_order[nxt++] = FP_REG_FIRST + i;
+
+ /* GCC requires that we list *all* the registers.... */
+ reg_alloc_order[nxt++] = 0; /* a0 = return address */
+ reg_alloc_order[nxt++] = 1; /* a1 = stack pointer */
+ reg_alloc_order[nxt++] = 16; /* pseudo frame pointer */
+ reg_alloc_order[nxt++] = 17; /* pseudo arg pointer */
+
+ /* list the coprocessor registers in order */
+ for (i = 0; i < BR_REG_NUM; i++)
+ reg_alloc_order[nxt++] = BR_REG_FIRST + i;
+
+ reg_alloc_order[nxt++] = ACC_REG_FIRST; /* MAC16 accumulator */
+ }
+ }
+
+
+ /* A customized version of reg_overlap_mentioned_p that only looks for
+ references to a7 (as opposed to hard_frame_pointer_rtx). */
+
+ int
+ a7_overlap_mentioned_p (x)
+ rtx x;
+ {
+ int i, j;
+ unsigned int x_regno;
+ const char *fmt;
+
+ if (GET_CODE (x) == REG)
+ {
+ x_regno = REGNO (x);
+ return (x != hard_frame_pointer_rtx
+ && x_regno < A7_REG + 1
+ && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
+ }
+
+ if (GET_CODE (x) == SUBREG
+ && GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
+ {
+ x_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x);
+ return (SUBREG_REG (x) != hard_frame_pointer_rtx
+ && x_regno < A7_REG + 1
+ && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
+ }
+
+ /* X does not match, so try its subexpressions. */
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (a7_overlap_mentioned_p (XEXP (x, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ for (j = XVECLEN (x, i) - 1; j >=0; j--)
+ if (a7_overlap_mentioned_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+
+ return 0;
+ }
diff -rc3pN gcc-3.0.2-orig/gcc/config/xtensa/xtensa.h gcc-3.0.2/gcc/config/xtensa/xtensa.h
*** gcc-3.0.2-orig/gcc/config/xtensa/xtensa.h Wed Dec 31 16:00:00 1969
--- gcc-3.0.2/gcc/config/xtensa/xtensa.h Tue Nov 27 09:30:52 2001
***************
*** 0 ****
--- 1,2452 ----
+ /* Definitions of Tensilica's Xtensa target machine for GNU compiler.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+
+ 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 2, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ /* Get Xtensa configuration default settings */
+ #include "xtensa-defaults.h"
+
+ /* Standard GCC variables that we reference. */
+ extern int current_function_calls_alloca;
+ extern int target_flags;
+ extern int optimize;
+
+ /* External variables defined in xtensa.c. */
+
+ /* comparison type */
+ enum cmp_type {
+ CMP_SI, /* four byte integers */
+ CMP_DI, /* eight byte integers */
+ CMP_SF, /* single precision floats */
+ CMP_DF, /* double precision floats */
+ CMP_MAX /* max comparison type */
+ };
+
+ #define MAX_REGFILE_NAME_LEN 16
+ extern char xtensa_reg_names[][MAX_REGFILE_NAME_LEN]; /* register names */
+ extern struct rtx_def * branch_cmp[2]; /* operands for compare */
+ extern enum cmp_type branch_type; /* what type of branch to use */
+ extern unsigned xtensa_current_frame_size;
+
+ /* Run-time compilation parameters selecting different hardware subsets. */
+
+ #define MASK_BIG_ENDIAN 0x00000001 /* big or little endian */
+ #define MASK_DENSITY 0x00000002 /* code density option */
+ #define MASK_MAC16 0x00000004 /* MAC16 option */
+ #define MASK_MUL16 0x00000008 /* 16-bit integer multiply */
+ #define MASK_MUL32 0x00000010 /* integer multiply/divide */
+ #define MASK_DIV32 0x00000020 /* integer multiply/divide */
+ #define MASK_NSA 0x00000040 /* nsa instruction option */
+ #define MASK_MINMAX 0x00000080 /* min/max instructions */
+ #define MASK_SEXT 0x00000100 /* sign extend insn option */
+ #define MASK_BOOLEANS 0x00000200 /* boolean register option */
+ #define MASK_HARD_FLOAT 0x00000400 /* floating-point option */
+ #define MASK_HARD_FLOAT_DIV 0x00000800 /* floating-point divide */
+ #define MASK_HARD_FLOAT_RECIP 0x00001000 /* floating-point reciprocal */
+ #define MASK_HARD_FLOAT_SQRT 0x00002000 /* floating-point sqrt */
+ #define MASK_HARD_FLOAT_RSQRT 0x00004000 /* floating-point recip sqrt */
+ #define MASK_NO_FUSED_MADD 0x00008000 /* avoid f-p mul/add */
+ #define MASK_SERIALIZE_VOLATILE 0x00010000 /* serialize volatile refs */
+
+ /* Macros used in the machine description to test the flags. */
+
+ #define TARGET_BIG_ENDIAN (target_flags & MASK_BIG_ENDIAN)
+ #define TARGET_DENSITY (target_flags & MASK_DENSITY)
+ #define TARGET_MAC16 (target_flags & MASK_MAC16)
+ #define TARGET_MUL16 (target_flags & MASK_MUL16)
+ #define TARGET_MUL32 (target_flags & MASK_MUL32)
+ #define TARGET_DIV32 (target_flags & MASK_DIV32)
+ #define TARGET_NSA (target_flags & MASK_NSA)
+ #define TARGET_MINMAX (target_flags & MASK_MINMAX)
+ #define TARGET_SEXT (target_flags & MASK_SEXT)
+ #define TARGET_BOOLEANS (target_flags & MASK_BOOLEANS)
+ #define TARGET_HARD_FLOAT (target_flags & MASK_HARD_FLOAT)
+ #define TARGET_HARD_FLOAT_DIV (target_flags & MASK_HARD_FLOAT_DIV)
+ #define TARGET_HARD_FLOAT_RECIP (target_flags & MASK_HARD_FLOAT_RECIP)
+ #define TARGET_HARD_FLOAT_SQRT (target_flags & MASK_HARD_FLOAT_SQRT)
+ #define TARGET_HARD_FLOAT_RSQRT (target_flags & MASK_HARD_FLOAT_RSQRT)
+ #define TARGET_NO_FUSED_MADD (target_flags & MASK_NO_FUSED_MADD)
+ #define TARGET_SERIALIZE_VOLATILE (target_flags & MASK_SERIALIZE_VOLATILE)
+
+ /* Default target_flags if no switches are specified */
+
+ #define TARGET_DEFAULT ( \
+ (XCHAL_HAVE_BE ? MASK_BIG_ENDIAN : 0) | \
+ (XCHAL_HAVE_DENSITY ? MASK_DENSITY : 0) | \
+ (XCHAL_HAVE_MAC16 ? MASK_MAC16 : 0) | \
+ (XCHAL_HAVE_MUL16 ? MASK_MUL16 : 0) | \
+ (XCHAL_HAVE_MUL32 ? MASK_MUL32 : 0) | \
+ (XCHAL_HAVE_DIV32 ? MASK_DIV32 : 0) | \
+ (XCHAL_HAVE_NSA ? MASK_NSA : 0) | \
+ (XCHAL_HAVE_MINMAX ? MASK_MINMAX : 0) | \
+ (XCHAL_HAVE_SEXT ? MASK_SEXT : 0) | \
+ (XCHAL_HAVE_BOOLEANS ? MASK_BOOLEANS : 0) | \
+ (XCHAL_HAVE_FP ? MASK_HARD_FLOAT : 0) | \
+ (XCHAL_HAVE_FP_DIV ? MASK_HARD_FLOAT_DIV : 0) | \
+ (XCHAL_HAVE_FP_RECIP ? MASK_HARD_FLOAT_RECIP : 0) | \
+ (XCHAL_HAVE_FP_SQRT ? MASK_HARD_FLOAT_SQRT : 0) | \
+ (XCHAL_HAVE_FP_RSQRT ? MASK_HARD_FLOAT_RSQRT : 0) | \
+ MASK_SERIALIZE_VOLATILE)
+
+ /* Macro to define tables used to set the flags.
+ This is a list in braces of pairs in braces,
+ each pair being { "NAME", VALUE }
+ where VALUE is the bits to set or minus the bits to clear.
+ An empty string NAME is used to identify the default VALUE. */
+
+ #define TARGET_SWITCHES \
+ { \
+ {"big-endian", MASK_BIG_ENDIAN, \
+ N_("Use big-endian byte order")}, \
+ {"little-endian", -MASK_BIG_ENDIAN, \
+ N_("Use little-endian byte order")}, \
+ {"density", MASK_DENSITY, \
+ N_("Use the Xtensa code density option")}, \
+ {"no-density", -MASK_DENSITY, \
+ N_("Do not use the Xtensa code density option")}, \
+ {"mac16", MASK_MAC16, \
+ N_("Use the Xtensa MAC16 option")}, \
+ {"no-mac16", -MASK_MAC16, \
+ N_("Do not use the Xtensa MAC16 option")}, \
+ {"mul16", MASK_MUL16, \
+ N_("Use the Xtensa MUL16 option")}, \
+ {"no-mul16", -MASK_MUL16, \
+ N_("Do not use the Xtensa MUL16 option")}, \
+ {"mul32", MASK_MUL32, \
+ N_("Use the Xtensa MUL32 option")}, \
+ {"no-mul32", -MASK_MUL32, \
+ N_("Do not use the Xtensa MUL32 option")}, \
+ {"div32", MASK_DIV32, \
+ 0 /* undocumented */}, \
+ {"no-div32", -MASK_DIV32, \
+ 0 /* undocumented */}, \
+ {"nsa", MASK_NSA, \
+ N_("Use the Xtensa NSA option")}, \
+ {"no-nsa", -MASK_NSA, \
+ N_("Do not use the Xtensa NSA option")}, \
+ {"minmax", MASK_MINMAX, \
+ N_("Use the Xtensa MIN/MAX option")}, \
+ {"no-minmax", -MASK_MINMAX, \
+ N_("Do not use the Xtensa MIN/MAX option")}, \
+ {"sext", MASK_SEXT, \
+ N_("Use the Xtensa SEXT option")}, \
+ {"no-sext", -MASK_SEXT, \
+ N_("Do not use the Xtensa SEXT option")}, \
+ {"booleans", MASK_BOOLEANS, \
+ N_("Use the Xtensa boolean register option")}, \
+ {"no-booleans", -MASK_BOOLEANS, \
+ N_("Do not use the Xtensa boolean register option")}, \
+ {"hard-float", MASK_HARD_FLOAT, \
+ N_("Use the Xtensa floating-point unit")}, \
+ {"soft-float", -MASK_HARD_FLOAT, \
+ N_("Do not use the Xtensa floating-point unit")}, \
+ {"hard-float-div", MASK_HARD_FLOAT_DIV, \
+ 0 /* undocumented */}, \
+ {"no-hard-float-div", -MASK_HARD_FLOAT_DIV, \
+ 0 /* undocumented */}, \
+ {"hard-float-recip", MASK_HARD_FLOAT_RECIP, \
+ 0 /* undocumented */}, \
+ {"no-hard-float-recip", -MASK_HARD_FLOAT_RECIP, \
+ 0 /* undocumented */}, \
+ {"hard-float-sqrt", MASK_HARD_FLOAT_SQRT, \
+ 0 /* undocumented */}, \
+ {"no-hard-float-sqrt", -MASK_HARD_FLOAT_SQRT, \
+ 0 /* undocumented */}, \
+ {"hard-float-rsqrt", MASK_HARD_FLOAT_RSQRT, \
+ 0 /* undocumented */}, \
+ {"no-hard-float-rsqrt", -MASK_HARD_FLOAT_RSQRT, \
+ 0 /* undocumented */}, \
+ {"no-fused-madd", MASK_NO_FUSED_MADD, \
+ N_("Disable fused multiply/add and multiply/subtract FP instructions")}, \
+ {"fused-madd", -MASK_NO_FUSED_MADD, \
+ N_("Enable fused multiply/add and multiply/subtract FP instructions")}, \
+ {"serialize-volatile", MASK_SERIALIZE_VOLATILE, \
+ N_("Serialize volatile memory references with MEMW instructions")}, \
+ {"no-serialize-volatile", -MASK_SERIALIZE_VOLATILE, \
+ N_("Do not serialize volatile memory references with MEMW instructions")},\
+ {"text-section-literals", 0, \
+ N_("Intersperse literal pools with code in the text section")}, \
+ {"no-text-section-literals", 0, \
+ N_("Put literal pools in a separate literal section")}, \
+ {"target-align", 0, \
+ N_("Automatically align branch targets to reduce branch penalties")}, \
+ {"no-target-align", 0, \
+ N_("Do not automatically align branch targets")}, \
+ {"longcalls", 0, \
+ N_("Use indirect CALLXn instructions for large programs")}, \
+ {"no-longcalls", 0, \
+ N_("Use direct CALLn instructions for fast calls")}, \
+ {"", TARGET_DEFAULT, 0} \
+ }
+
+ /* Sometimes certain combinations of command options do not make sense
+ on a particular target machine. You can define a macro
+ 'OVERRIDE_OPTIONS' to take account of this. This macro, if
+ defined, is executed once just after all the command options have
+ been parsed. */
+
+ #define OVERRIDE_OPTIONS override_options ()
+
+ #if XCHAL_HAVE_BE
+ #define CPP_ENDIAN_SPEC "\
+ %{mlittle-endian:-D__XTENSA_EL__} \
+ %{!mlittle-endian:-D__XTENSA_EB__} "
+ #else /* !XCHAL_HAVE_BE */
+ #define CPP_ENDIAN_SPEC "\
+ %{mbig-endian:-D__XTENSA_EB__} \
+ %{!mbig-endian:-D__XTENSA_EL__} "
+ #endif /* !XCHAL_HAVE_BE */
+
+ #if XCHAL_HAVE_FP
+ #define CPP_FLOAT_SPEC "%{msoft-float:-D__XTENSA_SOFT_FLOAT__}"
+ #else
+ #define CPP_FLOAT_SPEC "%{!mhard-float:-D__XTENSA_SOFT_FLOAT__}"
+ #endif
+
+ #ifndef CPP_SPEC
+ #define CPP_SPEC CPP_ENDIAN_SPEC CPP_FLOAT_SPEC
+ #endif
+
+ /* Define this to set the endianness to use in libgcc2.c, which can
+ not depend on target_flags. */
+ #define LIBGCC2_WORDS_BIG_ENDIAN XCHAL_HAVE_BE
+
+ /* Show we can debug even without a frame pointer. */
+ #define CAN_DEBUG_WITHOUT_FP
+
+
+ /* Target machine storage layout */
+
+ /* Define in order to support both big and little endian float formats
+ in the same gcc binary. */
+ #define REAL_ARITHMETIC
+
+ /* Define this if most significant bit is lowest numbered
+ in instructions that operate on numbered bit-fields.
+ */
+ #define BITS_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0)
+
+ /* Define this if most significant byte of a word is the lowest numbered. */
+ #define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0)
+
+ /* Define this if most significant word of a multiword number is the lowest. */
+ #define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0)
+
+ /* Number of bits in an addressable storage unit */
+ #define BITS_PER_UNIT 8
+
+ /* Width in bits of a "word", which is the contents of a machine register.
+ Note that this is not necessarily the width of data type 'int';
+ if using 16-bit ints on a 68000, this would still be 32.
+ But on a machine with 16-bit registers, this would be 16. */
+ #define BITS_PER_WORD 32
+ #define MAX_BITS_PER_WORD 32
+
+ /* Width of a word, in units (bytes). */
+ #define UNITS_PER_WORD 4
+ #define MIN_UNITS_PER_WORD 4
+
+ /* Width of a floating point register. */
+ #define UNITS_PER_FPREG 4
+
+ /* A C expression for the size in bits of the type 'int' on the
+ target machine. If you don't define this, the default is one
+ word. */
+ #define INT_TYPE_SIZE 32
+ #define MAX_INT_TYPE_SIZE 32
+
+ /* Tell the preprocessor the maximum size of wchar_t. */
+ #ifndef MAX_WCHAR_TYPE_SIZE
+ #ifndef WCHAR_TYPE_SIZE
+ #define MAX_WCHAR_TYPE_SIZE MAX_INT_TYPE_SIZE
+ #endif
+ #endif
+
+ /* A C expression for the size in bits of the type 'short' on the
+ target machine. If you don't define this, the default is half a
+ word. (If this would be less than one storage unit, it is
+ rounded up to one unit.) */
+ #define SHORT_TYPE_SIZE 16
+
+ /* A C expression for the size in bits of the type 'long' on the
+ target machine. If you don't define this, the default is one
+ word. */
+ #define LONG_TYPE_SIZE 32
+ #define MAX_LONG_TYPE_SIZE 32
+
+ /* A C expression for the size in bits of the type 'long long' on the
+ target machine. If you don't define this, the default is two
+ words. */
+ #define LONG_LONG_TYPE_SIZE 64
+
+ /* A C expression for the size in bits of the type 'char' on the
+ target machine. If you don't define this, the default is one
+ quarter of a word. (If this would be less than one storage unit,
+ it is rounded up to one unit.) */
+ #define CHAR_TYPE_SIZE BITS_PER_UNIT
+
+ /* A C expression for the size in bits of the type 'float' on the
+ target machine. If you don't define this, the default is one
+ word. */
+ #define FLOAT_TYPE_SIZE 32
+
+ /* A C expression for the size in bits of the type 'double' on the
+ target machine. If you don't define this, the default is two
+ words. */
+ #define DOUBLE_TYPE_SIZE 64
+
+ /* A C expression for the size in bits of the type 'long double' on
+ the target machine. If you don't define this, the default is two
+ words. */
+ #define LONG_DOUBLE_TYPE_SIZE 64
+
+ /* Width in bits of a pointer.
+ See also the macro 'Pmode' defined below. */
+ #define POINTER_SIZE 32
+
+ /* Allocation boundary (in *bits*) for storing pointers in memory. */
+ #define POINTER_BOUNDARY 32
+
+ /* Allocation boundary (in *bits*) for storing arguments in argument list. */
+ #define PARM_BOUNDARY 32
+
+ /* Allocation boundary (in *bits*) for the code of a function. */
+ #define FUNCTION_BOUNDARY 32
+
+ /* Alignment of field after 'int : 0' in a structure. */
+ #define EMPTY_FIELD_BOUNDARY 32
+
+ /* Every structure's size must be a multiple of this. */
+ #define STRUCTURE_SIZE_BOUNDARY 8
+
+ /* There is no point aligning anything to a rounder boundary than this. */
+ #define BIGGEST_ALIGNMENT 128
+
+ /* Set this nonzero if move instructions will actually fail to work
+ when given unaligned data. */
+ #define STRICT_ALIGNMENT 1
+
+ /* A macro to update M and UNSIGNEDP when an object whose type is
+ TYPE and which has the specified mode and signedness is to be
+ stored in a register. This macro is only called when TYPE is a
+ scalar type.
+
+ On most RISC machines, which only have operations that operate on a
+ full register, define this macro to set M to `word_mode' if M is an
+ integer mode narrower than `BITS_PER_WORD'. In most cases, only
+ integer modes should be widened because wider-precision
+ floating-point operations are usually more expensive than their
+ narrower counterparts.
+
+ For Xtensa, also set UNSIGNEDP for QImode, because there is no
+ 8-bit load from memory with sign extension. Otherwise, leave
+ UNSIGNEDP alone, since Xtensa has 16-bit loads both with and
+ without sign extension. */
+
+ #define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+ if (GET_MODE_CLASS (MODE) == MODE_INT \
+ && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \
+ { \
+ if ((MODE) == QImode) \
+ (UNSIGNEDP) = 1; \
+ (MODE) = SImode; \
+ }
+
+ /* Define this macro if the promotion described by `PROMOTE_MODE'
+ should also be done for outgoing function arguments. */
+
+ #define PROMOTE_FUNCTION_ARGS
+
+ /* Define this macro if the promotion described by `PROMOTE_MODE'
+ should also be done for the return value of functions.
+
+ If this macro is defined, `FUNCTION_VALUE' must perform the same
+ promotions done by `PROMOTE_MODE'. */
+
+ #define PROMOTE_FUNCTION_RETURN
+
+ /* Define this if you wish to imitate the way many other C compilers
+ handle alignment of bitfields and the structures that contain
+ them.
+
+ The behavior is that the type written for a bitfield ('int',
+ 'short', or other integer type) imposes an alignment for the
+ entire structure, as if the structure