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]

[PATCH 3.0.4] Add Xtensa port


I would like to add the new Xtensa port into the 3.0.4 release.
Aside from the Xtensa-specific files, there are only two minor
changes required.  I believe both are very safe and they've been
in the 3.1 files for a few days without causing any problems AFAIK.
I also re-ran a bootstrap build on i686-pc-linux-gnu on the 3.0
branch to make sure nothing was broken recently.

First question: Is this OK?

Second question: Assuming someone approves it, I need to update
the top-level config.sub file.  Should I update it to the latest
version from the master file?  That will bring in more changes than
I would personally want to make on a release branch like this.
Otherwise, I can just add the minimal changes needed to add the
xtensa targets.  (See the first patch below.)  What should I do?

Index: config.sub
===================================================================
RCS file: /cvs/gcc/gcc/config.sub,v
retrieving revision 1.51.4.3
diff -c -3 -p -r1.51.4.3 config.sub
*** config.sub	2001/07/16 10:04:04	1.51.4.3
--- config.sub	2002/01/28 22:11:43
*************** case $basic_machine in
*** 239,244 ****
--- 239,245 ----
  		| mips64orion | mips64orionel | mipstx39 | mipstx39el \
  		| mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
  		| mips64vr5000 | mips64vr5000el | mcore | s390 | s390x \
+ 		| xtensa \
  		| sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
  		| v850 | c4x \
  		| thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
*************** case $basic_machine in
*** 285,290 ****
--- 286,292 ----
  	      | mips64el-* | mips64orion-* | mips64orionel-* \
  	      | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
  	      | mipstx39-* | mipstx39el-* | mcore-* \
+ 	      | xtensa-* \
  	      | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
  	      | [cjt]90-* \
  	      | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \


ChangeLog:

        * 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-xtensa: New file.
	* config/xtensa/xm-xtensa.h: New file.
        * config/xtensa/xtensa-config.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.
        * config.gcc (xtensa-*-elf*): New target.
        (xtensa-*-linux*): New target.
        * cse.c (canon_hash): Compare rtx pointers instead of register
        numbers.  This is required for the Xtensa port.
        * integrate.c (copy_insn_list): Handle case where the static
        chain is in memory and the memory address has to be copied to
        a register.
        * doc/invoke.texi (Option Summary): Add Xtensa options.
        (Xtensa Options): New node.
        * doc/md.texi (Machine Constraints): Add Xtensa machine constraints.
        * doc/install.texi (xtensa-*-elf): New target.
        (xtensa-*-linux*): New target.
        * doc/contrib.texi: Add myself.

testsuite ChangeLog:
        * gcc.c-torture/compile/20001226-1.x: xfail for Xtensa.


diff --exclude=CVS -c3Pr gcc/config.orig/xtensa/elf.h gcc/config/xtensa/elf.h
*** gcc/config.orig/xtensa/elf.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/elf.h	Mon Jan 28 13:15:57 2002
***************
*** 0 ****
--- 1,138 ----
+ /* 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 ASM_APP_ON
+ #define ASM_APP_ON "#APP\n"
+ 
+ #undef ASM_APP_OFF
+ #define ASM_APP_OFF "#NO_APP\n"
+ 
+ /* Debug format: prefer DWARF2 */
+ #undef PREFERRED_DEBUGGING_TYPE
+ #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+ 
+ #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 \
+  "%{shared:-shared} \
+   %{!shared: \
+     %{!static: \
+       %{rdynamic:-export-dynamic} \
+     %{static:-static}}}"
+ 
+ #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
+ 
+ /* Do not force "-fpic" for this target.  */
+ #define XTENSA_ALWAYS_PIC 0
+ 
+ /* Redefine the standard ELF version of ASM_DECLARE_FUNCTION_SIZE to
+    allow adding the ".end literal_prefix" directive at the end of the
+    function.  */
+ #undef ASM_DECLARE_FUNCTION_SIZE
+ #define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL)		\
+   do								\
+     {								\
+       if (!flag_inhibit_size_directive)				\
+ 	{							\
+ 	  char label[256];					\
+ 	  static int labelno;					\
+ 	  							\
+ 	  labelno++;						\
+ 	  							\
+ 	  ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno);	\
+ 	  ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno);	\
+ 	  							\
+ 	  fprintf (FILE, "%s", SIZE_ASM_OP);			\
+ 	  assemble_name (FILE, (FNAME));			\
+ 	  fprintf (FILE, ",");					\
+ 	  assemble_name (FILE, label);				\
+ 	  fprintf (FILE, "-");					\
+ 	  assemble_name (FILE, (FNAME));			\
+ 	  putc ('\n', FILE);					\
+ 	}							\
+       XTENSA_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL);		\
+     }								\
+   while (0)
diff --exclude=CVS -c3Pr gcc/config.orig/xtensa/lib1funcs.asm gcc/config/xtensa/lib1funcs.asm
*** gcc/config.orig/xtensa/lib1funcs.asm	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/lib1funcs.asm	Mon Jan 28 13:15:57 2002
***************
*** 0 ****
--- 1,419 ----
+ /* 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-config.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
+ .Lfe0:
+ 	.size	__mulsi3,.Lfe0-__mulsi3
+ 
+ #endif /* L_mulsi3 */
+ 
+ 
+ 	# Some Xtensa configurations include the NSAU (unsigned
+ 	# normalize shift amount) instruction which computes the number
+ 	# of leading zero bits.  For other configurations, the "nsau"
+ 	# operation is implemented as a macro.
+ 	
+ #if !XCHAL_HAVE_NSA
+ 	.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, __nsau_data
+ 	extui	\a, \a, 24, 8
+ 	add	\tmp, \tmp, \a
+ 	l8ui	\tmp, \tmp, 0
+ 	add	\cnt, \cnt, \tmp
+ 	.endm
+ #endif /* !XCHAL_HAVE_NSA */
+ 
+ #ifdef L_nsau
+ 	.section .rodata
+ 	.align	4
+ 	.global	__nsau_data
+ 	.type	__nsau_data,@object
+ __nsau_data:	
+ #if !XCHAL_HAVE_NSA
+ 	.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
+ #endif /* !XCHAL_HAVE_NSA */
+ .Lfe1:
+ 	.size	__nsau_data,.Lfe1-__nsau_data
+ 	.hidden	__nsau_data
+ #endif /* L_nsau */
+ 
+ 
+ #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 --exclude=CVS -c3Pr gcc/config.orig/xtensa/lib2funcs.S gcc/config/xtensa/lib2funcs.S
*** gcc/config.orig/xtensa/lib2funcs.S	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/lib2funcs.S	Mon Jan 28 13:15:57 2002
***************
*** 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-config.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 --exclude=CVS -c3Pr gcc/config.orig/xtensa/linux.h gcc/config/xtensa/linux.h
*** gcc/config.orig/xtensa/linux.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/linux.h	Mon Jan 28 13:15:57 2002
***************
*** 0 ****
--- 1,94 ----
+ /* 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.  */
+ 
+ #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}}"
+ 
+ #undef LINK_SPEC
+ #define LINK_SPEC \
+  "%{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	"."
+ 
+ /* Always enable "-fpic" for Xtensa Linux.  */
+ #define XTENSA_ALWAYS_PIC 1
+ 
+ /* Redefine the standard ELF version of ASM_DECLARE_FUNCTION_SIZE to
+    allow adding the ".end literal_prefix" directive at the end of the
+    function.  */
+ #undef ASM_DECLARE_FUNCTION_SIZE
+ #define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL)		\
+   do								\
+     {								\
+       if (!flag_inhibit_size_directive)				\
+ 	{							\
+ 	  char label[256];					\
+ 	  static int labelno;					\
+ 	  							\
+ 	  labelno++;						\
+ 	  							\
+ 	  ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno);	\
+ 	  ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno);	\
+ 	  							\
+ 	  fprintf (FILE, "%s", SIZE_ASM_OP);			\
+ 	  assemble_name (FILE, (FNAME));			\
+ 	  fprintf (FILE, ",");					\
+ 	  assemble_name (FILE, label);				\
+ 	  fprintf (FILE, "-");					\
+ 	  assemble_name (FILE, (FNAME));			\
+ 	  putc ('\n', FILE);					\
+ 	}							\
+       XTENSA_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL);		\
+     }								\
+   while (0)
diff --exclude=CVS -c3Pr gcc/config.orig/xtensa/t-xtensa gcc/config/xtensa/t-xtensa
*** gcc/config.orig/xtensa/t-xtensa	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/t-xtensa	Mon Jan 28 13:15:57 2002
***************
*** 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 _nsau _divsi3 _modsi3 _udivsi3 _umodsi3
+ 
+ TARGET_LIBGCC2_CFLAGS += -mlongcalls
+ 
+ LIB2FUNCS_EXTRA += $(srcdir)/config/xtensa/lib2funcs.S
diff --exclude=CVS -c3Pr gcc/config.orig/xtensa/xm-xtensa.h gcc/config/xtensa/xm-xtensa.h
*** gcc/config.orig/xtensa/xm-xtensa.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/xm-xtensa.h	Mon Jan 28 13:15:57 2002
***************
*** 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 --exclude=CVS -c3Pr gcc/config.orig/xtensa/xtensa-config.h gcc/config/xtensa/xtensa-config.h
*** gcc/config.orig/xtensa/xtensa-config.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/xtensa-config.h	Mon Jan 28 13:16:40 2002
***************
*** 0 ****
--- 1,50 ----
+ /* Xtensa configuration settings.
+    Copyright (C) 2001 Free Software Foundation, Inc.
+    Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
+ 
+ This program 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.
+ 
+ This program 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 this program; if not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+ #ifndef XTENSA_CONFIG_H
+ #define XTENSA_CONFIG_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
+ 
+ #define XCHAL_HAVE_MMU			1
+ #define XCHAL_MMU_MIN_PTE_PAGE_SIZE	12
+ 
+ #endif /* !XTENSA_CONFIG_H */
diff --exclude=CVS -c3Pr gcc/config.orig/xtensa/xtensa-protos.h gcc/config/xtensa/xtensa-protos.h
*** gcc/config.orig/xtensa/xtensa-protos.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/xtensa-protos.h	Mon Jan 28 13:15:57 2002
***************
*** 0 ****
--- 1,116 ----
+ /* 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 print_operand PARAMS ((FILE *, rtx, int));
+ extern void print_operand_address PARAMS ((FILE *, rtx));
+ extern void xtensa_output_literal
+   PARAMS ((FILE *, rtx, enum machine_mode, int labelno));
+ 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 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 --exclude=CVS -c3Pr gcc/config.orig/xtensa/xtensa.c gcc/config/xtensa/xtensa.c
*** gcc/config.orig/xtensa/xtensa.c	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/xtensa.c	Mon Jan 28 13:15:57 2002
***************
*** 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 ((GET_CODE (op) == REG)
+       && (op != arg_pointer_rtx)
+       && ((REGNO (op) < FRAME_POINTER_REGNUM)
+ 	  || (REGNO (op) > LAST_VIRTUAL_REGISTER)))
+     return TRUE;
+ 
+   if (CONSTANT_ADDRESS_P (op))
+     {
+       /* Direct calls only allowed to static functions with PIC.  */
+       return (!flag_pic || (GET_CODE (op) == SYMBOL_REF
+ 			    && SYMBOL_REF_FLAG (op)));
+     }
+ 
+   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: 
+       fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+       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:
+       fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+ 
+     case CMP_SI:
+       invert = FALSE;
+       cmp = gen_int_relational (test_code, cmp0, cmp1, &invert);
+       break;
+ 
+     case CMP_SF:
+       if (!TARGET_HARD_FLOAT)
+ 	fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+       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]);
+ 
+       /* Check if this move is copying an incoming argument in a7.  If
+ 	 so, emit the move, followed by the special "set_frame_ptr"
+ 	 unspec_volatile insn, at the very beginning of the function.
+ 	 This is necessary because the register allocator will ignore
+ 	 conflicts with a7 and may assign some other pseudo to a7.  If
+ 	 that pseudo was assigned prior to this move, it would clobber
+ 	 the incoming argument in a7.  By copying the argument out of
+ 	 a7 as the very first thing, and then immediately following
+ 	 that with an unspec_volatile to keep the scheduler away, we
+ 	 should avoid any problems.  */
+ 
+       if (a7_overlap_mentioned_p (operands[1]))
+ 	{
+ 	  rtx mov;
+ 	  switch (mode)
+ 	    {
+ 	    case SImode:
+ 	      mov = gen_movsi_internal (operands[0], operands[1]);
+ 	      break;
+ 	    case HImode:
+ 	      mov = gen_movhi_internal (operands[0], operands[1]);
+ 	      break;
+ 	    case QImode:
+ 	      mov = gen_movqi_internal (operands[0], operands[1]);
+ 	      break;
+ 	    default:
+ 	      abort ();
+ 	    }
+ 
+ 	  /* Insert the instructions before any other argument copies.
+ 	     (The set_frame_ptr insn comes _after_ the move, so push it
+ 	     out first.)  */
+ 	  push_topmost_sequence ();
+ 	  emit_insn_after (gen_set_frame_ptr (), get_insns ());
+ 	  emit_insn_after (mov, get_insns ());
+ 	  pop_topmost_sequence ();
+ 
+ 	  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 = (char *) malloc (64);
+   rtx tgt = operands[callop];
+ 
+   if (GET_CODE (tgt) == CONST_INT)
+     sprintf (result, "call8\t0x%x", INTVAL (tgt));
+   else if (register_operand (tgt, VOIDmode))
+     sprintf (result, "callx8\t%%%d", callop);
+   else
+     sprintf (result, "call8\t%%%d", callop);
+ 
+   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);
+ }
+ 
+ 
+ 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;
+ 
+   /* Check PIC settings.  There's no need for -fPIC on Xtensa and
+      some targets need to always use PIC.  */
+   if (XTENSA_ALWAYS_PIC)
+     {
+       if (flag_pic)
+ 	warning ("-f%s ignored (all code is position independent)",
+ 		 (flag_pic > 1 ? "PIC" : "pic"));
+       flag_pic = 1;
+     }
+   if (flag_pic > 1)
+     flag_pic = 1;
+ }
+ 
+ 
+ /* 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
+    '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 '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))
+ 	      fatal_insn ("invalid mask", op);
+ 
+ 	    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:
+       fatal_insn ("invalid address", addr);
+       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
+ 	  fatal_insn ("no register in address", addr);
+ 
+ 	if (CONSTANT_P (offset))
+ 	  {
+ 	    fprintf (file, "%s, ", reg_names [REGNO (reg)]);
+ 	    output_addr_const (file, offset);
+ 	  }
+ 	else
+ 	  fatal_insn ("address offset not a constant", addr);
+       }
+       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 (file, name, init_string, final_string, size)
+      FILE *file;
+      char *name;
+      char *init_string;
+      char *final_string;
+      int size;
+ {
+   fputs (init_string, file);		/* "", "\t.comm\t", or "\t.lcomm\t" */
+   assemble_name (file, name);
+   fprintf (file, final_string, size);	/* ":\n", ",%u\n", ",%u\n" */
+ }
+ 
+ 
+ void
+ xtensa_output_literal (file, x, mode, labelno)
+      FILE *file;
+      rtx x;
+      enum machine_mode mode;
+      int labelno;
+ {
+   long value_long[2];
+   union real_extract u;
+   int size;
+ 
+   fprintf (file, "\t.literal .LC%u, ", (unsigned) labelno);
+ 
+   switch (GET_MODE_CLASS (mode))
+     {
+     case MODE_FLOAT:
+       if (GET_CODE (x) != CONST_DOUBLE)
+ 	abort ();
+ 
+       memcpy ((char *) &u, (char *) &CONST_DOUBLE_LOW (x), sizeof u);
+       switch (mode)
+ 	{
+ 	case SFmode:
+ 	  REAL_VALUE_TO_TARGET_SINGLE (u.d, value_long[0]);
+ 	  fprintf (file, "0x%08lx\t\t# %.12g (float)\n", value_long[0], u.d);
+ 	  break;
+ 
+ 	case DFmode:
+ 	  REAL_VALUE_TO_TARGET_DOUBLE (u.d, value_long);
+ 	  fprintf (file, "0x%08lx, 0x%08lx # %.20g (double)\n",
+ 		   value_long[0], value_long[1], u.d);
+ 	  break;
+ 
+ 	default:
+ 	  abort ();
+ 	}
+ 
+       break;
+ 
+     case MODE_INT:
+     case MODE_PARTIAL_INT:
+       size = GET_MODE_SIZE (mode);
+       if (size == 4)
+ 	{
+ 	  output_addr_const (file, x);
+ 	  fputs ("\n", file);
+ 	}
+       else if (size == 8)
+ 	{
+ 	  output_addr_const (file, operand_subword (x, 0, 0, DImode));
+ 	  fputs (", ", file);
+ 	  output_addr_const (file, operand_subword (x, 1, 0, DImode));
+ 	  fputs ("\n", file);
+ 	}
+       else
+ 	abort ();
+       break;
+ 
+     default:
+       abort ();
+     }
+ }
+ 
+ 
+ /* 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));
+     }
+ 
+   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 --exclude=CVS -c3Pr gcc/config.orig/xtensa/xtensa.h gcc/config/xtensa/xtensa.h
*** gcc/config.orig/xtensa/xtensa.h	Wed Dec 31 16:00:00 1969
--- gcc/config/xtensa/xtensa.h	Mon Jan 28 13:15:57 2002
***************
*** 0 ****
--- 1,2431 ----
+ /* 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 settings */
+ #include "xtensa/xtensa-config.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 DOUBL