]> git.pld-linux.org Git - packages/gcc.git/commitdiff
- GCC extension for protecting applications from stack-smashing attacks
authorRafał Janiczek <rafal.janiczek@gmail.com>
Fri, 12 Sep 2003 11:30:29 +0000 (11:30 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    gcc-3.3.1-propolice.patch -> 1.1

gcc-3.3.1-propolice.patch [new file with mode: 0644]

diff --git a/gcc-3.3.1-propolice.patch b/gcc-3.3.1-propolice.patch
new file mode 100644 (file)
index 0000000..5036cef
--- /dev/null
@@ -0,0 +1,39955 @@
+diff -ruN gcc-3.3.1/gcc/Makefile.in gcc-3.3.1.pp/gcc/Makefile.in
+--- gcc-3.3.1/gcc/Makefile.in  2003-08-03 15:48:36.000000000 +0000
++++ gcc-3.3.1.pp/gcc/Makefile.in       2003-09-05 12:00:02.000000000 +0000
+@@ -387,7 +387,7 @@
+ # Options to use when compiling libgcc2.a.
+ #
+ LIBGCC2_DEBUG_CFLAGS = -g
+-LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED @inhibit_libc@
++LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED @inhibit_libc@ -fno-stack-protector
+ # Additional options to use when compiling libgcc2.a.
+ # Some targets override this to -isystem include
+@@ -764,7 +764,7 @@
+  sibcall.o simplify-rtx.o ssa.o ssa-ccp.o ssa-dce.o stmt.o               \
+  stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \
+  tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \
+- et-forest.o $(GGC) $(out_object_file) $(EXTRA_OBJS)
++ et-forest.o protector.o $(GGC) $(out_object_file) $(EXTRA_OBJS)
+ BACKEND = main.o libbackend.a
+@@ -798,7 +798,7 @@
+ LIB2FUNCS_2 = _floatdixf _fixunsxfsi _fixtfdi _fixunstfdi _floatditf \
+     _clear_cache _trampoline __main _exit _absvsi2 _absvdi2 _addvsi3 \
+-    _addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors
++    _addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors _stack_smash_handler
+ # Defined in libgcc2.c, included only in the static library.
+ LIB2FUNCS_ST = _eprintf _bb __gcc_bcmp
+@@ -1410,7 +1410,7 @@
+    ssa.h $(PARAMS_H) $(TM_P_H) reload.h dwarf2asm.h $(TARGET_H) \
+    langhooks.h insn-flags.h options.h cfglayout.h real.h
+       $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+-        -DTARGET_NAME=\"$(target_alias)\" \
++        -DSTACK_PROTECTOR -DTARGET_NAME=\"$(target_alias)\" \
+         -c $(srcdir)/toplev.c $(OUTPUT_OPTION)
+ main.o : main.c $(CONFIG_H) $(SYSTEM_H) toplev.h
+@@ -1665,6 +1665,7 @@
+    output.h except.h $(TM_P_H) real.h
+ params.o : params.c $(CONFIG_H) $(SYSTEM_H) $(PARAMS_H) toplev.h
+ hooks.o: hooks.c $(CONFIG_H) $(SYSTEM_H) $(HOOKS_H)
++protector.o: protector.c $(CONFIG_H)
+ $(out_object_file): $(out_file) $(CONFIG_H) $(TREE_H) $(GGC_H) \
+    $(RTL_H) $(REGS_H) hard-reg-set.h real.h insn-config.h conditions.h \
+diff -ruN gcc-3.3.1/gcc/Makefile.in.orig gcc-3.3.1.pp/gcc/Makefile.in.orig
+--- gcc-3.3.1/gcc/Makefile.in.orig     1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/Makefile.in.orig  2003-09-05 11:58:58.000000000 +0000
+@@ -0,0 +1,3754 @@
++# Makefile for GNU C compiler.
++#   Copyright (C) 1987, 1988, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
++#   1998, 1999, 2000, 2001, 2002, 2003 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.
++
++# The targets for external use include:
++# all, doc, proto, install, install-cross, install-cross-rest,
++# uninstall, TAGS, mostlyclean, clean, distclean, maintainer-clean,
++# stage1, stage2, stage3, stage4.
++
++# This is the default target.
++all:
++
++# Suppress smart makes who think they know how to automake Yacc files
++.y.c:
++
++# Directory where sources are, from where we are.
++srcdir = @srcdir@
++VPATH = @srcdir@
++
++# Pointer to the GCC Project website
++website=http://gcc.gnu.org
++
++# Variables that exist for you to override.
++# See below for how to change them for certain systems.
++
++# List of language subdirectories.
++# This is overridden by configure.
++SUBDIRS =@subdirs@
++
++# Selection of languages to be made.
++# This is overridden by configure.
++CONFIG_LANGUAGES = @all_languages@
++LANGUAGES = c gcov$(exeext) $(CONFIG_LANGUAGES)
++
++# Selection of languages to be made during stage1 build.
++# This is overridden by configure.
++BOOT_LANGUAGES = c @all_boot_languages@
++
++# Various ways of specifying flags for compilations:
++# CFLAGS is for the user to override to, e.g., do a cross build with -O2.
++# For recursive  bootstrap builds CFLAGS is used to pass in STAGE1_CFLAGS
++# or BOOT_CFLAGS
++# STAGE1_CFLAGS is set by configure on some targets or passed from toplevel
++# and sets the CFLAGS passed to stage1 of a bootstrap compilation.
++# BOOT_CFLAGS is the value of CFLAGS to pass to the stage2, stage3 and stage4
++# bootstrap compilations.
++# XCFLAGS is used for most compilations but not when using the GCC just built.
++# TCFLAGS is used for compilations with the GCC just built.
++XCFLAGS =
++TCFLAGS =
++CFLAGS = -g
++STAGE1_CFLAGS = -g @stage1_cflags@
++BOOT_CFLAGS = -g -O2
++
++# Flags to determine code coverage. When coverage is disabled, this will
++# contain the optimization flags, as you normally want code coverage
++# without optimization. The -dumpbase $@ makes sure that the auxilary
++# files end up near the object files.
++COVERAGE_FLAGS = @coverage_flags@
++coverageexts = .{da,bb,bbg}
++
++# The warning flags are separate from BOOT_CFLAGS because people tend to
++# override optimization flags and we'd like them to still have warnings
++# turned on.  These flags are also used to pass other stage dependent
++# flags from configure.  The user is free to explicitly turn these flags
++# off if they wish.
++# LOOSE_WARN are the warning flags to use when compiling something
++# which is only compiled with gcc, such as libgcc and the frontends
++# other than C.
++# STRICT_WARN and STRICT2_WARN are the additional warning flags to
++# apply to the back end and the C front end, which may be compiled
++# with other compilers.  This is partially controlled by configure in
++# stage1, as not all versions of gcc understand -Wno-long-long.
++LOOSE_WARN = -W -Wall -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes
++STRICT_WARN = -Wtraditional @strict1_warn@
++STRICT2_WARN = -Wtraditional -pedantic -Wno-long-long
++
++# This is set by --enable-checking.  The idea is to catch forgotten
++# "extern" tags in header files.
++NOCOMMON_FLAG = @nocommon_flag@
++
++# These are set by --enable-checking=valgrind.
++RUN_GEN = @valgrind_command@
++VALGRIND_DRIVER_DEFINES = @valgrind_path_defines@
++
++# This is how we control whether or not the additional warnings are applied.
++.-warn = $(STRICT_WARN)
++GCC_WARN_CFLAGS = $(LOOSE_WARN) $($(@D)-warn) $(NOCOMMON_FLAG)
++
++# All warnings have to be shut off in stage1 if the compiler used then
++# isn't gcc; configure determines that.  WARN_CFLAGS will be either
++# $(GCC_WARN_CFLAGS), or nothing.
++WARN_CFLAGS = @warn_cflags@
++
++# These exists to be overridden by the x-* and t-* files, respectively.
++X_CFLAGS =
++T_CFLAGS =
++
++X_CPPFLAGS =
++T_CPPFLAGS =
++
++ADAC = @ADAC@
++AWK = @AWK@
++CC = @CC@
++BISON = @BISON@
++BISONFLAGS =
++FLEX = @FLEX@
++FLEXFLAGS =
++AR = ar
++AR_FLAGS = rc
++DLLTOOL = dlltool
++RANLIB = @RANLIB@
++SHELL = @SHELL@
++# pwd command to use.  Allow user to override default by setting PWDCMD in
++# the environment to account for automounters.  The make variable must not
++# be called PWDCMD, otherwise the value set here is passed to make
++# subprocesses and overrides the setting from the user's environment.
++# Don't use PWD since it is a common shell environment variable and we
++# don't want to corrupt it.
++PWD_COMMAND = $${PWDCMD-pwd}
++# on sysV, define this as cp.
++INSTALL = @INSTALL@
++# Some systems may be missing symbolic links, regular links, or both.
++# Allow configure to check this and use "ln -s", "ln", or "cp" as appropriate.
++LN=@LN@
++LN_S=@LN_S@
++# These permit overriding just for certain files.
++INSTALL_PROGRAM = @INSTALL_PROGRAM@
++INSTALL_DATA = @INSTALL_DATA@
++INSTALL_SCRIPT = @INSTALL@
++MAKEINFO = @MAKEINFO@
++MAKEINFOFLAGS =
++TEXI2DVI = texi2dvi
++TEXI2POD = perl $(srcdir)/../contrib/texi2pod.pl
++POD2MAN = pod2man --center="GNU" --release="gcc-$(version)"
++# For GNUmake: let us decide what gets passed to recursive makes.
++MAKEOVERRIDES =
++@SET_MAKE@
++# Some compilers can't handle cc -c blah.c -o foo/blah.o.
++# In stage2 and beyond, we force this to "-o $@" since we know we're using gcc.
++OUTPUT_OPTION = @OUTPUT_OPTION@
++
++# Some versions of `touch' (such as the version on Solaris 2.8)
++# do not correctly set the timestamp due to buggy versions of `utime'
++# in the kernel.  So, we use `echo' instead.
++STAMP = echo timestamp >
++
++# This is where we get zlib from.  zlibdir is -L../zlib and zlibinc is
++# -I../zlib, unless we were configured with --with-system-zlib, in which
++# case both are empty.
++ZLIB = @zlibdir@ -lz
++ZLIBINC = @zlibinc@
++
++# Substitution type for target's getgroups 2nd arg.
++TARGET_GETGROUPS_T = @TARGET_GETGROUPS_T@
++
++# Target to use when installing include directory.  Either
++# install-headers-tar, install-headers-cpio or install-headers-cp.
++INSTALL_HEADERS_DIR = @build_install_headers_dir@
++
++# Header files that are made available under the same name
++# to programs compiled with GCC.
++USER_H = $(srcdir)/ginclude/float.h \
++       $(srcdir)/ginclude/iso646.h \
++       $(srcdir)/ginclude/stdarg.h \
++       $(srcdir)/ginclude/stdbool.h \
++       $(srcdir)/ginclude/stddef.h \
++       $(srcdir)/ginclude/varargs.h \
++       $(srcdir)/unwind.h \
++       $(EXTRA_HEADERS)
++
++# The GCC to use for compiling libgcc.a and crt*.o.
++# Usually the one we just built.
++# Don't use this as a dependency--use $(GCC_PASSES) or $(GCC_PARTS).
++GCC_FOR_TARGET = $(STAGE_CC_WRAPPER) ./xgcc -B./ -B$(build_tooldir)/bin/ -isystem $(build_tooldir)/include -isystem $(build_tooldir)/sys-include
++
++# This is used instead of ALL_CFLAGS when compiling with GCC_FOR_TARGET.
++# It omits XCFLAGS, and specifies -B./.
++# It also specifies -isystem ./include to find, e.g., stddef.h.
++GCC_CFLAGS=$(INTERNAL_CFLAGS) $(X_CFLAGS) $(T_CFLAGS) $(LOOSE_WARN) -isystem ./include $(TCFLAGS)
++
++# Sed command to transform gcc to installed name.  Overwritten by configure.
++program_transform_name = @program_transform_name@
++program_transform_cross_name = s,^,$(target_alias)-,
++
++build_canonical = @build_canonical@
++host_canonical = @host_canonical@
++
++# Tools to use when building a cross-compiler.
++# These are used because `configure' appends `cross-make'
++# to the makefile when making a cross-compiler.
++
++# Use the tools from the build tree, if they are available.
++
++# objdir is set by configure.
++objdir = @objdir@
++
++AR_FOR_TARGET = ` \
++  if [ -f $(objdir)/../binutils/ar ] ; then \
++    echo $(objdir)/../binutils/ar ; \
++  else \
++    if [ "$(host_canonical)" = "$(target)" ] ; then \
++      echo ar; \
++    else \
++       t='$(program_transform_cross_name)'; echo ar | sed -e $$t ; \
++    fi; \
++  fi`
++AR_FLAGS_FOR_TARGET =
++AR_CREATE_FOR_TARGET = $(AR_FOR_TARGET) $(AR_FLAGS_FOR_TARGET) rc
++AR_EXTRACT_FOR_TARGET = $(AR_FOR_TARGET) $(AR_FLAGS_FOR_TARGET) x
++RANLIB_FOR_TARGET = ` \
++  if [ -f $(objdir)/../binutils/ranlib ] ; then \
++    echo $(objdir)/../binutils/ranlib ; \
++  else \
++    if [ "$(host_canonical)" = "$(target)" ] ; then \
++      echo ranlib; \
++    else \
++       t='$(program_transform_cross_name)'; echo ranlib | sed -e $$t ; \
++    fi; \
++  fi`
++RANLIB_TEST_FOR_TARGET = \
++  [ -f $(RANLIB_FOR_TARGET) ] \
++  || ( [ "$(host_canonical)" = "$(target)" ] \
++       && [ -f /usr/bin/ranlib -o -f /bin/ranlib ] )
++NM_FOR_TARGET = ` \
++  if [ -f ./nm ] ; then \
++    echo ./nm ; \
++  elif [ -f $(objdir)/../binutils/nm-new ] ; then \
++    echo $(objdir)/../binutils/nm-new ; \
++  else \
++    if [ "$(host_canonical)" = "$(target)" ] ; then \
++      echo nm; \
++    else \
++       t='$(program_transform_cross_name)'; echo nm | sed -e $$t ; \
++    fi; \
++  fi`
++
++# Where to find some libiberty headers.
++HASHTAB_H   = $(srcdir)/../include/hashtab.h
++OBSTACK_H   = $(srcdir)/../include/obstack.h
++SPLAY_TREE_H= $(srcdir)/../include/splay-tree.h
++FIBHEAP_H   = $(srcdir)/../include/fibheap.h
++PARTITION_H = $(srcdir)/../include/partition.h
++
++# Default native SYSTEM_HEADER_DIR, to be overridden by targets.
++NATIVE_SYSTEM_HEADER_DIR = /usr/include
++# Default cross SYSTEM_HEADER_DIR, to be overridden by targets.
++CROSS_SYSTEM_HEADER_DIR = $(build_tooldir)/sys-include
++
++# autoconf sets SYSTEM_HEADER_DIR to one of the above.
++SYSTEM_HEADER_DIR = @SYSTEM_HEADER_DIR@
++
++# Control whether to run fixproto and fixincludes.
++STMP_FIXPROTO = @STMP_FIXPROTO@
++STMP_FIXINC = @STMP_FIXINC@
++
++# Test to see whether <limits.h> exists in the system header files.
++LIMITS_H_TEST = [ -f $(SYSTEM_HEADER_DIR)/limits.h ]
++
++target=@target@
++target_alias=@target_alias@
++xmake_file=@dep_host_xmake_file@
++tmake_file=@dep_tmake_file@
++out_file=$(srcdir)/config/@out_file@
++out_object_file=@out_object_file@
++md_file=$(srcdir)/config/@md_file@
++tm_defines=@tm_defines@
++tm_p_file_list=@tm_p_file_list@
++tm_p_file=@tm_p_file@
++build_xm_file_list=@build_xm_file_list@
++build_xm_file=@build_xm_file@
++build_xm_defines=@build_xm_defines@
++host_xm_file_list=@host_xm_file_list@
++host_xm_file=@host_xm_file@
++host_xm_defines=@host_xm_defines@
++xm_file=@xm_file@
++xm_defines=@xm_defines@
++lang_specs_files=@lang_specs_files@
++lang_options_files=@lang_options_files@
++lang_tree_files=@lang_tree_files@
++target_cpu_default=@target_cpu_default@
++GCC_THREAD_FILE=@thread_file@
++OBJC_BOEHM_GC=@objc_boehm_gc@
++GTHREAD_FLAGS=@gthread_flags@
++# Be prepared for gcc2 merges.
++gcc_version=@gcc_version@
++gcc_version_trigger=@gcc_version_trigger@
++version=$(gcc_version)
++mainversion=`grep version_string $(srcdir)/version.c | sed -e 's/.*\"\([0-9]*\.[0-9]*\).*/\1/'`
++
++# Common prefix for installation directories.
++# NOTE: This directory must exist when you start installation.
++prefix = @prefix@
++# Directory in which to put localized header files. On the systems with
++# gcc as the native cc, `local_prefix' may not be `prefix' which is
++# `/usr'.
++# NOTE: local_prefix *should not* default from prefix.
++local_prefix = @local_prefix@
++# Directory in which to put host dependent programs and libraries
++exec_prefix = @exec_prefix@
++# Directory in which to put the executable for the command `gcc'
++bindir = @bindir@
++# Directory in which to put the directories used by the compiler.
++libdir = @libdir@
++# Directory in which the compiler finds executables, libraries, etc.
++libsubdir = $(libdir)/gcc-lib/$(target_alias)/$(version)
++# Used to produce a relative $(gcc_tooldir) in gcc.o
++unlibsubdir = ../../..
++# Directory in which to find other cross-compilation tools and headers.
++dollar = @dollar@
++# Used in install-cross.
++gcc_tooldir = @gcc_tooldir@
++# Used to install the shared libgcc.
++slibdir = @slibdir@
++# Since gcc_tooldir does not exist at build-time, use -B$(build_tooldir)/bin/
++build_tooldir = $(exec_prefix)/$(target_alias)
++# Directory in which the compiler finds target-independent g++ includes.
++gcc_gxx_include_dir = @gcc_gxx_include_dir@
++# Directory to search for site-specific includes.
++local_includedir = $(local_prefix)/include
++includedir = $(prefix)/include
++# where the info files go
++infodir = @infodir@
++# Where cpp should go besides $prefix/bin if necessary
++cpp_install_dir = @cpp_install_dir@
++# where the locale files go
++datadir = @datadir@
++localedir = $(datadir)/locale
++# Extension (if any) to put in installed man-page filename.
++man1ext = .1
++man7ext = .7
++objext = .o
++exeext = @host_exeext@
++build_exeext = @build_exeext@
++
++# Directory in which to put man pages.
++mandir = @mandir@
++man1dir = $(mandir)/man1
++man7dir = $(mandir)/man7
++# Dir for temp files.
++tmpdir = /tmp
++
++# Top build directory, relative to here.
++top_builddir = .
++
++# Whether we were configured with NLS.
++USE_NLS = @USE_NLS@
++
++# Internationalization library.
++INTLLIBS = @INTLLIBS@
++INTLDEPS = @INTLDEPS@
++
++# Character encoding conversion library.
++LIBICONV = @LIBICONV@
++
++# List of internationalization subdirectories.
++INTL_SUBDIRS = intl
++
++# The GC method to be used on this system.
++GGC=@GGC@.o
++
++# If a supplementary library is being used for the GC.
++GGC_LIB=
++
++# libgcc.a may be built directly or via stmp-multilib,
++# and installed likewise.  Overridden by t-fragment.
++LIBGCC = libgcc.a
++INSTALL_LIBGCC = install-libgcc
++
++# Options to use when compiling libgcc2.a.
++#
++LIBGCC2_DEBUG_CFLAGS = -g
++LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED @inhibit_libc@
++
++# Additional options to use when compiling libgcc2.a.
++# Some targets override this to -isystem include
++LIBGCC2_INCLUDES =
++
++# Additional target-dependent options for compiling libgcc2.a.
++TARGET_LIBGCC2_CFLAGS =
++
++# Options to use when compiling crtbegin/end.
++CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \
++  -finhibit-size-directive -fno-inline-functions -fno-exceptions \
++  -fno-zero-initialized-in-bss
++
++# Additional sources to handle exceptions; overridden on ia64.
++LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
++  $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
++LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h
++
++# nm flags to list global symbols in libgcc object files.
++SHLIB_NM_FLAGS = -pg
++
++# List of extra executables that should be compiled for this target machine
++# that are used for compiling from source code to object code.
++# The rules for compiling them should be in the t-* file for the machine.
++EXTRA_PASSES =@extra_passes@
++
++# Like EXTRA_PASSES, but these are used when linking.
++EXTRA_PROGRAMS = @extra_programs@
++
++# List of extra object files that should be compiled for this target machine.
++# The rules for compiling them should be in the t-* file for the machine.
++EXTRA_PARTS = @extra_parts@
++
++# List of extra object files that should be compiled and linked with
++# compiler proper (cc1, cc1obj, cc1plus).
++EXTRA_OBJS = @extra_objs@
++
++# List of extra object files that should be compiled and linked with
++# the gcc driver.
++EXTRA_GCC_OBJS =@host_extra_gcc_objs@
++
++# List of additional header files to install.
++# Often this is edited directly by `configure'.
++EXTRA_HEADERS =@extra_headers_list@
++
++# It is convenient for configure to add the assignment at the beginning,
++# so don't override it here.
++USE_COLLECT2 = collect2$(exeext)
++
++# List of extra C and assembler files to add to static and shared libgcc2.
++# Assembler files should have names ending in `.asm'.
++LIB2FUNCS_EXTRA =
++
++# List of extra C and assembler files to add to static libgcc2.
++# Assembler files should have names ending in `.asm'.
++LIB2FUNCS_STATIC_EXTRA =
++
++# Program to convert libraries.
++LIBCONVERT =
++
++# Control whether header files are installed.
++INSTALL_HEADERS=install-headers install-mkheaders
++
++# Control whether Info documentation is built and installed.
++BUILD_INFO = @BUILD_INFO@
++
++# Control whether manpages generated by texi2pod.pl can be rebuilt.
++GENERATED_MANPAGES = @GENERATED_MANPAGES@
++
++# Additional directories of header files to run fixincludes on.
++# These should be directories searched automatically by default
++# just as /usr/include is.
++# *Do not* use this for directories that happen to contain
++# header files, but are not searched automatically by default.
++# On most systems, this is empty.
++OTHER_FIXINCLUDES_DIRS=
++
++# A list of all the language-specific executables.
++# This is overridden by configure.
++COMPILERS = cc1$(exeext) @all_compilers@
++
++# List of things which should already be built whenever we try to use xgcc
++# to compile anything (without linking).
++GCC_PASSES=xgcc$(exeext) cc1$(exeext) specs $(EXTRA_PASSES)
++
++# List of things which should already be built whenever we try to use xgcc
++# to link anything.
++GCC_PARTS=$(GCC_PASSES) $(LIBGCC) $(EXTRA_PROGRAMS) $(USE_COLLECT2) $(EXTRA_PARTS)
++
++# Directory to link to, when using the target `maketest'.
++DIR = ../gcc
++
++# Flags to use when cross-building GCC.
++# Prefix to apply to names of object files when using them
++# to run on the machine we are compiling on.
++BUILD_PREFIX = @BUILD_PREFIX@
++# Prefix to apply to names of object files when compiling them
++# to run on the machine we are compiling on.
++# The default for this variable is chosen to keep these rules
++# out of the way of the other rules for compiling the same source files.
++BUILD_PREFIX_1 = @BUILD_PREFIX_1@
++# Native compiler for the build machine and its switches.
++HOST_CC = @HOST_CC@
++HOST_CFLAGS= @HOST_CFLAGS@ -DGENERATOR_FILE
++
++# Native linker and preprocessor flags.  For x-fragment overrides.
++HOST_LDFLAGS=$(LDFLAGS)
++HOST_CPPFLAGS=$(ALL_CPPFLAGS)
++
++# Actual name to use when installing a native compiler.
++GCC_INSTALL_NAME = `echo gcc|sed '$(program_transform_name)'`
++GCC_TARGET_INSTALL_NAME = $(target_alias)-`echo gcc|sed '$(program_transform_name)'`
++CPP_INSTALL_NAME = `echo cpp|sed '$(program_transform_name)'`
++PROTOIZE_INSTALL_NAME = `echo protoize|sed '$(program_transform_name)'`
++UNPROTOIZE_INSTALL_NAME = `echo unprotoize|sed '$(program_transform_name)'`
++GCOV_INSTALL_NAME = `echo gcov|sed '$(program_transform_name)'`
++GCCBUG_INSTALL_NAME = `echo gccbug|sed '$(program_transform_name)'`
++
++# Actual name to use when installing a cross-compiler.
++GCC_CROSS_NAME = `echo gcc|sed '$(program_transform_cross_name)'`
++CPP_CROSS_NAME = `echo cpp|sed '$(program_transform_cross_name)'`
++PROTOIZE_CROSS_NAME = `echo protoize|sed '$(program_transform_cross_name)'`
++UNPROTOIZE_CROSS_NAME = `echo unprotoize|sed '$(program_transform_cross_name)'`
++
++# Set by autoconf to "all.internal" for a native build, or
++# "all.cross" to build a cross compiler.
++ALL = @ALL@
++
++# Setup the testing framework, if you have one
++EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \
++            echo $${rootme}/../expect/expect ; \
++          else echo expect ; fi`
++
++RUNTEST = `if [ -f $${srcdir}/../dejagnu/runtest ] ; then \
++             echo $${srcdir}/../dejagnu/runtest ; \
++          else echo runtest; fi`
++RUNTESTFLAGS =
++
++# Extra symbols for fixproto to define when parsing headers.
++FIXPROTO_DEFINES =
++
++# Extra flags to use when compiling crt{begin,end}.o.
++CRTSTUFF_T_CFLAGS =
++
++# Extra flags to use when compiling [m]crt0.o.
++CRT0STUFF_T_CFLAGS =
++
++# "t" or nothing, for building multilibbed versions of, say, crtbegin.o.
++T =
++
++# Should T contain a `=', libgcc.mk will make T_TARGET, setting
++# $(T_TARGET) to the name of the actual target filename.
++T_TARGET =
++T_TARGET : $(T_TARGET)
++
++# End of variables for you to override.
++
++# Definition of `all' is here so that new rules inserted by sed
++# do not specify the default target.
++# The real definition is under `all.internal' (for native compilers)
++# or `all.cross' (for cross compilers).
++all: all.indirect
++
++# This tells GNU Make version 3 not to put all variables in the environment.
++.NOEXPORT:
++
++# GCONFIG_H lists the config files that the generator files depend on, while
++# CONFIG_H lists the ones ordinary gcc files depend on, which includes
++# several files generated by those generators.
++GCONFIG_H = config.h $(host_xm_file_list)
++HCONFIG_H = hconfig.h $(build_xm_file_list)
++CONFIG_H = $(GCONFIG_H) insn-constants.h insn-flags.h
++TCONFIG_H = tconfig.h $(xm_file_list)
++TARGET_H = target.h
++HOOKS_H = hooks.h
++LANGHOOKS_DEF_H = langhooks-def.h $(HOOKS_H)
++TARGET_DEF_H = target-def.h $(HOOKS_H)
++TM_P_H = tm_p.h $(tm_p_file_list) tm-preds.h
++
++MACHMODE_H = machmode.h machmode.def @extra_modes_file@
++RTL_BASE_H = rtl.h rtl.def $(MACHMODE_H)
++RTL_H = $(RTL_BASE_H) genrtl.h
++PARAMS_H = params.h params.def
++TREE_H = tree.h tree.def $(MACHMODE_H) tree-check.h version.h builtins.def \
++          location.h
++BASIC_BLOCK_H = basic-block.h bitmap.h sbitmap.h varray.h $(PARTITION_H) \
++          hard-reg-set.h
++DEMANGLE_H = $(srcdir)/../include/demangle.h
++RECOG_H = recog.h
++EXPR_H = expr.h
++OPTABS_H = optabs.h insn-codes.h
++REGS_H = regs.h varray.h $(MACHMODE_H)
++INTEGRATE_H = integrate.h varray.h
++LOOP_H = loop.h varray.h bitmap.h
++GCC_H = gcc.h version.h
++GGC_H = ggc.h varray.h gtype-desc.h
++TIMEVAR_H = timevar.h timevar.def
++INSN_ATTR_H = insn-attr.h $(srcdir)/insn-addr.h $(srcdir)/varray.h
++C_COMMON_H = c-common.h $(SPLAY_TREE_H) $(CPPLIB_H)
++C_TREE_H = c-tree.h $(C_COMMON_H)
++SYSTEM_H = system.h hwint.h $(srcdir)/../include/libiberty.h
++PREDICT_H = predict.h predict.def
++CPPLIB_H = cpplib.h line-map.h
++
++# sed inserts variable overrides after the following line.
++####target overrides
++@target_overrides@
++
++####host overrides
++@host_overrides@
++#\f
++# Now figure out from those variables how to compile and link.
++
++all.indirect: $(ALL)
++
++# IN_GCC distinguishes between code compiled into GCC itself and other
++# programs built during a bootstrap.
++# autoconf inserts -DCROSS_COMPILE if we are building a cross compiler.
++INTERNAL_CFLAGS = -DIN_GCC @CROSS@
++
++# This is the variable actually used when we compile.
++# If you change this line, you probably also need to change the definition
++# of HOST_CFLAGS in build-make to match.
++ALL_CFLAGS = $(X_CFLAGS) $(T_CFLAGS) \
++  $(CFLAGS) $(INTERNAL_CFLAGS) $(COVERAGE_FLAGS) $(WARN_CFLAGS) $(XCFLAGS) @DEFS@
++
++# Likewise.
++ALL_CPPFLAGS = $(CPPFLAGS) $(X_CPPFLAGS) $(T_CPPFLAGS)
++
++# Build and host support libraries.  FORBUILD is either
++# .. or ../$(build_alias) depending on whether host != build.
++LIBIBERTY = ../libiberty/libiberty.a
++BUILD_LIBIBERTY = @FORBUILD@/libiberty/libiberty.a
++
++# Dependencies on the intl and portability libraries.
++LIBDEPS= $(INTLDEPS) $(LIBIBERTY)
++
++# Likewise, for use in the tools that must run on this machine
++# even if we are cross-building GCC.
++HOST_LIBDEPS= $(BUILD_LIBIBERTY)
++
++# How to link with both our special library facilities
++# and the system's installed libraries.
++LIBS =        $(INTLLIBS) @LIBS@ $(LIBIBERTY)
++
++# Any system libraries needed just for GNAT.
++SYSLIBS = @GNAT_LIBEXC@
++
++# Libs needed (at present) just for jcf-dump.
++LDEXP_LIB = @LDEXP_LIB@
++
++# Likewise, for use in the tools that must run on this machine
++# even if we are cross-building GCC.
++HOST_LIBS = $(BUILD_LIBIBERTY)
++
++HOST_RTL = $(BUILD_PREFIX)rtl.o read-rtl.o $(BUILD_PREFIX)bitmap.o \
++              $(BUILD_PREFIX)ggc-none.o
++HOST_SUPPORT = gensupport.o insn-conditions.o
++HOST_EARLY_SUPPORT = gensupport.o dummy-conditions.o
++
++HOST_PRINT = print-rtl1.o
++HOST_ERRORS = $(BUILD_PREFIX)errors.o
++HOST_VARRAY = $(BUILD_PREFIX)varray.o
++
++# Specify the directories to be searched for header files.
++# Both . and srcdir are used, in that order,
++# so that *config.h will be found in the compilation
++# subdirectory rather than in the source directory.
++# -I$(@D) and -I$(srcdir)/$(@D) cause the subdirectory of the file
++# currently being compiled, in both source trees, to be examined as well.
++INCLUDES = -I. -I$(@D) -I$(srcdir) -I$(srcdir)/$(@D) \
++         -I$(srcdir)/config -I$(srcdir)/../include
++
++# Always use -I$(srcdir)/config when compiling.
++.c.o:
++      $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++
++# This tells GNU make version 3 not to export all the variables
++# defined in this file into the environment.
++.NOEXPORT:
++#\f
++# Support for additional languages (other than c and objc).
++# ??? objc can be supported this way too (leave for later).
++
++# These next lines are overridden by configure.
++LANG_MAKEFILES = @all_lang_makefiles@
++LANG_STAGESTUFF = @all_stagestuff@
++
++# Flags to pass to recursive makes.
++# CC is set by configure.  Hosts without symlinks need special handling
++# because we need CC="stage1/xgcc -Bstage1/" to work in the language
++# subdirectories.
++# ??? The choices here will need some experimenting with.
++ORDINARY_FLAGS_TO_PASS = \
++      "AR=$(AR)" \
++      "AR_FLAGS_FOR_TARGET=$(AR_FLAGS_FOR_TARGET)" \
++      "AR_CREATE_FOR_TARGET=$(AR_CREATE_FOR_TARGET)" \
++      "AR_EXTRACT_FOR_TARGET=$(AR_EXTRACT_FOR_TARGET)" \
++      "AR_FOR_TARGET=$(AR_FOR_TARGET)" \
++      "BISON=$(BISON)" \
++      "BISONFLAGS=$(BISONFLAGS)" \
++      "CFLAGS=$(CFLAGS) $(WARN_CFLAGS)" \
++      "DESTDIR=$(DESTDIR)" \
++      "GCC_FOR_TARGET=$(GCC_FOR_TARGET)" \
++      "LDFLAGS=$(LDFLAGS)" \
++      "FLEX=$(FLEX)" \
++      "FLEXFLAGS=$(FLEXFLAGS)" \
++      "LN=$(LN)" \
++      "LN_S=$(LN_S)" \
++      "MAKEINFO=$(MAKEINFO)" \
++      "MAKEINFOFLAGS=$(MAKEINFOFLAGS)" \
++      "MAKEOVERRIDES=" \
++      "RANLIB_FOR_TARGET=$(RANLIB_FOR_TARGET)" \
++      "RANLIB_TEST_FOR_TARGET=$(RANLIB_TEST_FOR_TARGET)" \
++      "SHELL=$(SHELL)" \
++      "exeext=$(exeext)" \
++      "build_exeext=$(build_exeext)" \
++      "objext=$(objext)" \
++      "exec_prefix=$(exec_prefix)" \
++      "prefix=$(prefix)" \
++      "local_prefix=$(local_prefix)" \
++      "gxx_include_dir=$(gcc_gxx_include_dir)" \
++      "build_tooldir=$(build_tooldir)" \
++      "gcc_tooldir=$(gcc_tooldir)" \
++      "bindir=$(bindir)" \
++      "libsubdir=$(libsubdir)" \
++      "datadir=$(datadir)" \
++      "localedir=$(localedir)"
++FLAGS_TO_PASS = $(ORDINARY_FLAGS_TO_PASS) "CC=@cc_set_by_configure@" \
++      "STAGE_PREFIX=@stage_prefix_set_by_configure@"
++PREPEND_DOTDOT_TO_RELATIVE_PATHS = sed \
++      -e 's|^ *[^ /][^ /]*/|%&|' \
++      -e 's| -B| -B%|g' \
++      -e 's|% *[^- /]|%&|g' \
++      -e 's|%% *|../|g' \
++      -e 's|%||g'
++SUBDIR_FLAGS_TO_PASS = $(ORDINARY_FLAGS_TO_PASS) \
++      "CC=`echo @quoted_cc_set_by_configure@ | $(PREPEND_DOTDOT_TO_RELATIVE_PATHS)`" \
++      "STAGE_PREFIX=`echo @quoted_stage_prefix_set_by_configure@ | $(PREPEND_DOTDOT_TO_RELATIVE_PATHS)`"
++#\f
++# Lists of files for various purposes.
++
++# Target specific, C specific object file
++C_TARGET_OBJS=@c_target_objs@
++
++# Target specific, C++ specific object file
++CXX_TARGET_OBJS=@cxx_target_objs@
++
++# Language-specific object files for C and Objective C.
++C_AND_OBJC_OBJS = attribs.o c-errors.o c-lex.o c-pragma.o c-decl.o c-typeck.o \
++  c-convert.o c-aux-info.o c-common.o c-opts.o c-format.o c-semantics.o \
++  c-objc-common.o c-dump.o libcpp.a $(C_TARGET_OBJS)
++
++# Language-specific object files for C.
++C_OBJS = c-parse.o c-lang.o c-pretty-print.o $(C_AND_OBJC_OBJS)
++
++# Language-independent object files.
++
++OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o    \
++ cfg.o cfganal.o cfgbuild.o cfgcleanup.o cfglayout.o cfgloop.o                   \
++ cfgrtl.o combine.o conflict.o convert.o cse.o cselib.o dbxout.o         \
++ debug.o df.o diagnostic.o doloop.o dominance.o                                  \
++ dwarf2asm.o dwarf2out.o dwarfout.o emit-rtl.o except.o explow.o         \
++ expmed.o expr.o final.o flow.o fold-const.o function.o gcse.o                   \
++ genrtl.o ggc-common.o global.o graph.o gtype-desc.o                     \
++ haifa-sched.o hashtable.o hooks.o ifcvt.o insn-attrtab.o insn-emit.o    \
++ insn-extract.o insn-opinit.o insn-output.o insn-peep.o insn-recog.o     \
++ integrate.o intl.o jump.o  langhooks.o lcm.o lists.o local-alloc.o      \
++ loop.o mbchar.o optabs.o params.o predict.o print-rtl.o print-tree.o    \
++ profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o         \
++ real.o recog.o reg-stack.o regclass.o regmove.o regrename.o             \
++ reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o       \
++ sbitmap.o sched-deps.o sched-ebb.o sched-rgn.o sched-vis.o sdbout.o     \
++ sibcall.o simplify-rtx.o ssa.o ssa-ccp.o ssa-dce.o stmt.o               \
++ stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \
++ tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \
++ et-forest.o protector.o $(GGC) $(out_object_file) $(EXTRA_OBJS)
++
++BACKEND = main.o libbackend.a
++
++# Files to be copied away after each stage in building.
++STAGESTUFF = *$(objext) insn-flags.h insn-config.h insn-codes.h \
++ insn-output.c insn-recog.c insn-emit.c insn-extract.c insn-peep.c \
++ insn-attr.h insn-attrtab.c insn-opinit.c insn-constants.h tm-preds.h \
++ tree-check.h insn-conditions.c \
++ s-flags s-config s-codes s-mlib s-genrtl s-gtype gtyp-gen.h \
++ s-output s-recog s-emit s-extract s-peep s-check s-conditions \
++ s-attr s-attrtab s-opinit s-preds s-constants s-crt0 \
++ genemit$(build_exeext) genoutput$(build_exeext) genrecog$(build_exeext) \
++ genextract$(build_exeext) genflags$(build_exeext) gencodes$(build_exeext) \
++ genconfig$(build_exeext) genpeep$(build_exeext) genattrtab$(build_exeext) \
++ genattr$(build_exeext) genopinit$(build_exeext) gengenrtl$(build_exeext) \
++ gencheck$(build_exeext) genpreds$(build_exeext) genconstants$(build_exeext) \
++ gengtype$(build_exeext) genconditions$(build_exeext) \
++ genrtl.c genrtl.h gt-*.h gtype-*.h gtype-desc.c \
++ xgcc$(exeext) cpp$(exeext) cc1$(exeext) $(EXTRA_PASSES) \
++ $(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) cc1obj$(exeext) \
++ protoize$(exeext) unprotoize$(exeext) \
++ specs collect2$(exeext) $(USE_COLLECT2) \
++ gcov$(exeext) *.[0-9][0-9].* *.[si] libcpp.a libbackend.a libgcc.mk \
++ $(LANG_STAGESTUFF)
++
++# Library members defined in libgcc2.c.
++# Variable length limited to 255 charactes when passed to a shell script.
++LIB2FUNCS_1 = _muldi3 _negdi2 _lshrdi3 _ashldi3 _ashrdi3 _ffsdi2 _clz \
++    _cmpdi2 _ucmpdi2 _floatdidf _floatdisf _fixunsdfsi _fixunssfsi \
++    _fixunsdfdi _fixdfdi _fixunssfdi _fixsfdi _fixxfdi _fixunsxfdi
++
++LIB2FUNCS_2 = _floatdixf _fixunsxfsi _fixtfdi _fixunstfdi _floatditf \
++    _clear_cache _trampoline __main _exit _absvsi2 _absvdi2 _addvsi3 \
++    _addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors _stack_smash_handler
++
++# Defined in libgcc2.c, included only in the static library.
++LIB2FUNCS_ST = _eprintf _bb __gcc_bcmp
++
++FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \
++    _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
++    _lt_sf _le_sf _unord_sf _si_to_sf _sf_to_si _negate_sf _make_sf \
++    _sf_to_df _sf_to_tf _thenan_sf _sf_to_usi _usi_to_sf
++
++DPBIT_FUNCS = _pack_df _unpack_df _addsub_df _mul_df _div_df \
++    _fpcmp_parts_df _compare_df _eq_df _ne_df _gt_df _ge_df \
++    _lt_df _le_df _unord_df _si_to_df _df_to_si _negate_df _make_df \
++    _df_to_sf _df_to_tf _thenan_df _df_to_usi _usi_to_df
++
++TPBIT_FUNCS = _pack_tf _unpack_tf _addsub_tf _mul_tf _div_tf \
++    _fpcmp_parts_tf _compare_tf _eq_tf _ne_tf _gt_tf _ge_tf \
++    _lt_tf _le_tf _unord_tf _si_to_tf _tf_to_si _negate_tf _make_tf \
++    _tf_to_df _tf_to_sf _thenan_tf _tf_to_usi _usi_to_tf
++
++# These might cause a divide overflow trap and so are compiled with
++# unwinder info.
++LIB2_DIVMOD_FUNCS = _divdi3 _moddi3 _udivdi3 _umoddi3 _udiv_w_sdiv _udivmoddi4
++
++# The only suffixes we want for implicit rules are .c and .o, so clear
++# the list and add them.  This speeds up GNU Make, and allows -r to work.
++# For i18n support, we also need .gmo, .po, .pox.
++# This must come before the language makefile fragments to allow them to
++# add suffixes and rules of their own.
++.SUFFIXES:
++.SUFFIXES: .c .o .po .pox .gmo
++
++#\f
++# Language makefile fragments.
++
++# The following targets define the interface between us and the languages.
++#
++# all.cross, start.encap, rest.encap,
++# info, dvi,
++# install-normal, install-common, install-info, install-man,
++# uninstall,
++# mostlyclean, clean, distclean, extraclean, maintainer-clean,
++# stage1, stage2, stage3, stage4
++#
++# Each language is linked in with a series of hooks (since we can't use `::'
++# targets).  The name of each hooked is "lang.${target_name}" (eg: lang.info).
++# Configure computes and adds these here.
++
++####language hooks
++@language_hooks@
++
++# sed inserts language fragments after the following line.
++####language fragments
++@language_fragments@
++
++# End of language makefile fragments.
++#\f
++
++Makefile: $(srcdir)/Makefile.in config.status $(srcdir)/version.c \
++   $(xmake_file) $(tmake_file) $(LANG_MAKEFILES)
++      $(SHELL) $(srcdir)/configure.frag $(srcdir) "$(SUBDIRS)" \
++              "$(xmake_file)" "$(tmake_file)"
++      cp config.status config.run
++      LANGUAGES="$(CONFIG_LANGUAGES)" $(SHELL) config.run
++      rm -f config.run
++
++config.h: cs-config.h ; @true
++hconfig.h: cs-hconfig.h ; @true
++tconfig.h: cs-tconfig.h ; @true
++tm_p.h: cs-tm_p.h ; @true
++
++cs-config.h: Makefile
++      TM_DEFINES="$(tm_defines)" \
++      HEADERS="$(host_xm_file)" XM_DEFINES="$(host_xm_defines)" \
++      TARGET_CPU_DEFAULT="$(target_cpu_default)" \
++      $(SHELL) $(srcdir)/mkconfig.sh config.h
++
++cs-hconfig.h: Makefile
++      TM_DEFINES="$(tm_defines)" \
++      HEADERS="$(build_xm_file)" XM_DEFINES="$(build_xm_defines)" \
++      TARGET_CPU_DEFAULT="$(target_cpu_default)" \
++      $(SHELL) $(srcdir)/mkconfig.sh hconfig.h
++
++cs-tconfig.h: Makefile
++      TM_DEFINES="$(tm_defines)" \
++      HEADERS="$(xm_file)" XM_DEFINES="$(xm_defines)" \
++      TARGET_CPU_DEFAULT="" \
++      $(SHELL) $(srcdir)/mkconfig.sh tconfig.h
++
++cs-tm_p.h: Makefile
++      TM_DEFINES="" \
++      HEADERS="$(tm_p_file)" XM_DEFINES="" TARGET_CPU_DEFAULT="" \
++      $(SHELL) $(srcdir)/mkconfig.sh tm_p.h
++
++# Don't automatically run autoconf, since configure.in might be accidentally
++# newer than configure.  Also, this writes into the source directory which
++# might be on a read-only file system.  If configured for maintainer mode
++# then do allow autoconf to be run.
++
++$(srcdir)/configure: @MAINT@ $(srcdir)/configure.in
++      (cd $(srcdir) && autoconf)
++
++gccbug:       $(srcdir)/gccbug.in
++      CONFIG_FILES=gccbug CONFIG_HEADERS= ./config.status
++
++mklibgcc: $(srcdir)/mklibgcc.in
++      CONFIG_FILES=mklibgcc CONFIG_HEADERS= ./config.status
++
++mkheaders: $(srcdir)/mkheaders.in
++      CONFIG_FILES=mkheaders CONFIG_HEADERS= ./config.status
++
++# cstamp-h.in controls rebuilding of config.in.
++# It is named cstamp-h.in and not stamp-h.in so the mostlyclean rule doesn't
++# delete it.  A stamp file is needed as autoheader won't update the file if
++# nothing has changed.
++# It remains in the source directory and is part of the distribution.
++# This follows what is done in shellutils, fileutils, etc.
++# "echo timestamp" is used instead of touch to be consistent with other
++# packages that use autoconf (??? perhaps also to avoid problems with patch?).
++# ??? Newer versions have a maintainer mode that may be useful here.
++
++# Don't run autoheader automatically either.
++# Only run it if maintainer mode is enabled.
++@MAINT@ $(srcdir)/config.in: $(srcdir)/cstamp-h.in
++@MAINT@ $(srcdir)/cstamp-h.in: $(srcdir)/configure.in $(srcdir)/acconfig.h
++@MAINT@       (cd $(srcdir) && autoheader)
++@MAINT@       @rm -f $(srcdir)/cstamp-h.in
++@MAINT@       echo timestamp > $(srcdir)/cstamp-h.in
++auto-host.h: cstamp-h ; @true
++cstamp-h: config.in config.status
++      CONFIG_HEADERS=auto-host.h:config.in LANGUAGES="$(CONFIG_LANGUAGES)" $(SHELL) config.status
++
++# Really, really stupid make features, such as SUN's KEEP_STATE, may force
++# a target to build even if it is up-to-date.  So we must verify that
++# config.status does not exist before failing.
++config.status: $(srcdir)/configure $(srcdir)/config.gcc version.c
++      @if [ ! -f config.status ] ; then \
++        echo You must configure gcc.  Look at http://gcc.gnu.org/install/ for details.; \
++        false; \
++      else \
++        LANGUAGES="$(CONFIG_LANGUAGES)" $(SHELL) config.status --recheck; \
++      fi
++
++all.internal: start.encap rest.encap doc
++# This is what to compile if making a cross-compiler.
++all.cross: native gcc-cross cpp$(exeext) specs \
++      $(LIBGCC) $(EXTRA_PARTS) lang.all.cross doc
++# This is what must be made before installing GCC and converting libraries.
++start.encap: native xgcc$(exeext) cpp$(exeext) specs \
++      xlimits.h lang.start.encap
++# These can't be made until after GCC can run.
++rest.encap: $(STMP_FIXPROTO) $(LIBGCC) $(EXTRA_PARTS) lang.rest.encap
++# This is what is made with the host's compiler
++# whether making a cross compiler or not.
++native: config.status auto-host.h intl.all build-@POSUB@ $(LANGUAGES) \
++      $(EXTRA_PASSES) $(EXTRA_PROGRAMS) $(USE_COLLECT2)
++
++# Define the names for selecting languages in LANGUAGES.
++C c: cc1$(exeext)
++PROTO: proto
++
++# Tell GNU make these are phony targets.
++.PHONY: C c PROTO proto
++
++# On the target machine, finish building a cross compiler.
++# This does the things that can't be done on the host machine.
++rest.cross: $(LIBGCC) specs
++
++# Recompile all the language-independent object files.
++# This is used only if the user explicitly asks for it.
++compilations: $(BACKEND)
++
++# Like libcpp.a, this archive is strictly for the host.
++libbackend.a: $(OBJS)
++      -rm -rf libbackend.a
++      $(AR) $(AR_FLAGS) libbackend.a $(OBJS)
++      -$(RANLIB) libbackend.a
++
++# We call this executable `xgcc' rather than `gcc'
++# to avoid confusion if the current directory is in the path
++# and CC is `gcc'.  It is renamed to `gcc' when it is installed.
++xgcc$(exeext): gcc.o gccspec.o version.o intl.o prefix.o \
++   version.o $(LIBDEPS) $(EXTRA_GCC_OBJS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ gcc.o gccspec.o intl.o \
++        prefix.o version.o $(EXTRA_GCC_OBJS) $(LIBS)
++
++# cpp is to cpp0 as gcc is to cc1.
++# The only difference from xgcc is that it's linked with cppspec.o
++# instead of gccspec.o.
++cpp$(exeext): gcc.o cppspec.o version.o intl.o prefix.o \
++   version.o $(LIBDEPS) $(EXTRA_GCC_OBJS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ gcc.o cppspec.o intl.o \
++        prefix.o version.o $(EXTRA_GCC_OBJS) $(LIBS)
++
++# Dump a specs file to make -B./ read these specs over installed ones.
++specs: xgcc$(exeext)
++      $(GCC_FOR_TARGET) -dumpspecs > tmp-specs
++      mv tmp-specs specs
++
++# We do want to create an executable named `xgcc', so we can use it to
++# compile libgcc2.a.
++# Also create gcc-cross, so that install-common will install properly.
++gcc-cross: xgcc$(exeext)
++      cp xgcc$(exeext) gcc-cross$(exeext)
++
++cc1$(exeext): $(C_OBJS) $(BACKEND) $(LIBDEPS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o cc1$(exeext) \
++              $(C_OBJS) $(BACKEND) $(LIBS)
++
++# Build the version of limits.h that we will install.
++xlimits.h: glimits.h limitx.h limity.h
++      if $(LIMITS_H_TEST) ; then \
++        cat $(srcdir)/limitx.h $(srcdir)/glimits.h $(srcdir)/limity.h > tmp-xlimits.h; \
++      else \
++        cat $(srcdir)/glimits.h > tmp-xlimits.h; \
++      fi
++      mv tmp-xlimits.h xlimits.h
++#\f
++# Build libgcc.a.
++
++LIB2ADD = $(LIB2FUNCS_EXTRA)
++LIB2ADD_ST = $(LIB2FUNCS_STATIC_EXTRA)
++
++libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) $(LIB2ADD_ST) xgcc$(exeext) specs
++      objext='$(objext)' \
++      LIB1ASMFUNCS='$(LIB1ASMFUNCS)' \
++      LIB2FUNCS_1='$(LIB2FUNCS_1)' \
++      LIB2FUNCS_2='$(LIB2FUNCS_2)' \
++      LIB2FUNCS_ST='$(LIB2FUNCS_ST)' \
++      LIB2ADD='$(LIB2ADD)' \
++      LIB2ADD_ST='$(LIB2ADD_ST)' \
++      LIB2ADDEH='$(LIB2ADDEH)' \
++      LIB2ADDEHDEP='$(LIB2ADDEHDEP)' \
++      FPBIT='$(FPBIT)' \
++      FPBIT_FUNCS='$(FPBIT_FUNCS)' \
++      LIB2_DIVMOD_FUNCS='$(LIB2_DIVMOD_FUNCS)' \
++      DPBIT='$(DPBIT)' \
++      DPBIT_FUNCS='$(DPBIT_FUNCS)' \
++      TPBIT='$(TPBIT)' \
++      TPBIT_FUNCS='$(TPBIT_FUNCS)' \
++      MULTILIBS=`$(GCC_FOR_TARGET) --print-multi-lib` \
++      EXTRA_MULTILIB_PARTS='$(EXTRA_MULTILIB_PARTS)' \
++      SHLIB_LINK='$(SHLIB_LINK)' \
++      SHLIB_INSTALL='$(SHLIB_INSTALL)' \
++      SHLIB_EXT='$(SHLIB_EXT)' \
++      SHLIB_MULTILIB='$(SHLIB_MULTILIB)' \
++      SHLIB_MKMAP='$(SHLIB_MKMAP)' \
++      SHLIB_MKMAP_OPTS='$(SHLIB_MKMAP_OPTS)' \
++      SHLIB_MAPFILES='$(SHLIB_MAPFILES)' \
++      SHLIB_NM_FLAGS='$(SHLIB_NM_FLAGS)' \
++      MULTILIB_OSDIRNAMES='$(MULTILIB_OSDIRNAMES)' \
++      mkinstalldirs='$(SHELL) $(srcdir)/mkinstalldirs' \
++        $(SHELL) mklibgcc > tmp-libgcc.mk
++      mv tmp-libgcc.mk libgcc.mk
++
++# All the things that might cause us to want to recompile bits of libgcc.
++LIBGCC_DEPS = $(GCC_PASSES) $(LANGUAGES) stmp-int-hdrs $(STMP_FIXPROTO) \
++      libgcc.mk $(srcdir)/libgcc2.c $(TCONFIG_H) \
++      $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs \
++      tsystem.h $(FPBIT) $(DPBIT) $(TPBIT) $(LIB2ADD) \
++      $(LIB2ADD_ST) $(LIB2ADDEH) $(LIB2ADDEHDEP) $(EXTRA_PARTS) \
++      $(srcdir)/config/$(LIB1ASMSRC)
++
++libgcc.a: $(LIBGCC_DEPS)
++      $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \
++        BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \
++        AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++        AR_CREATE_FOR_TARGET="$(AR_CREATE_FOR_TARGET)" \
++        AR_FLAGS_FOR_TARGET="$(AR_FLAGS_FOR_TARGET)" \
++        CFLAGS="$(CFLAGS) $(WARN_CFLAGS)" \
++        RANLIB_FOR_TARGET="$(RANLIB_FOR_TARGET)" \
++        RANLIB_TEST_FOR_TARGET="$(RANLIB_TEST_FOR_TARGET)" \
++        NM_FOR_TARGET="$(NM_FOR_TARGET)" AWK="$(AWK)" \
++        LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" \
++        INCLUDES="$(INCLUDES)" \
++        CONFIG_H="$(TCONFIG_H)" MACHMODE_H="$(MACHMODE_H)" \
++        LIB1ASMSRC='$(LIB1ASMSRC)' \
++        MAKEOVERRIDES= \
++        -f libgcc.mk all
++
++# Use the genmultilib shell script to generate the information the gcc
++# driver program needs to select the library directory based on the
++# switches.
++multilib.h: s-mlib; @true
++s-mlib: $(srcdir)/genmultilib Makefile
++      if test @enable_multilib@ = yes \
++         || test -n "$(MULTILIB_OSDIRNAMES)"; then \
++        $(SHELL) $(srcdir)/genmultilib \
++          "$(MULTILIB_OPTIONS)" \
++          "$(MULTILIB_DIRNAMES)" \
++          "$(MULTILIB_MATCHES)" \
++          "$(MULTILIB_EXCEPTIONS)" \
++          "$(MULTILIB_EXTRA_OPTS)" \
++          "$(MULTILIB_EXCLUSIONS)" \
++          "$(MULTILIB_OSDIRNAMES)" \
++          "@enable_multilib@" \
++          > tmp-mlib.h; \
++      else \
++        $(SHELL) $(srcdir)/genmultilib '' '' '' '' '' '' '' no \
++          > tmp-mlib.h; \
++      fi
++      $(SHELL) $(srcdir)/move-if-change tmp-mlib.h multilib.h
++      $(STAMP) s-mlib
++
++# Build multiple copies of libgcc.a, one for each target switch.
++stmp-multilib: $(LIBGCC_DEPS)
++      $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \
++        BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \
++        AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++        AR_CREATE_FOR_TARGET="$(AR_CREATE_FOR_TARGET)" \
++        AR_FLAGS_FOR_TARGET="$(AR_FLAGS_FOR_TARGET)" \
++        CFLAGS="$(CFLAGS) $(WARN_CFLAGS)" \
++        RANLIB_FOR_TARGET="$(RANLIB_FOR_TARGET)" \
++        RANLIB_TEST_FOR_TARGET="$(RANLIB_TEST_FOR_TARGET)" \
++        NM_FOR_TARGET="$(NM_FOR_TARGET)" AWK="$(AWK)" \
++        LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" \
++        INCLUDES="$(INCLUDES)" \
++        CONFIG_H="$(CONFIG_H)" MACHMODE_H="$(MACHMODE_H)" \
++        LIB1ASMSRC='$(LIB1ASMSRC)' \
++        MAKEOVERRIDES= \
++        -f libgcc.mk all
++      $(STAMP) stmp-multilib
++
++# Compile two additional files that are linked with every program
++# linked using GCC on systems using COFF or ELF, for the sake of C++
++# constructors.
++$(T)crtbegin.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
++  gbl-ctors.h stmp-int-hdrs tsystem.h
++      $(GCC_FOR_TARGET) $(CRTSTUFF_CFLAGS) $(CRTSTUFF_T_CFLAGS) \
++        @inhibit_libc@ -c $(srcdir)/crtstuff.c -DCRT_BEGIN \
++        -o $(T)crtbegin$(objext)
++
++$(T)crtend.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
++  gbl-ctors.h stmp-int-hdrs tsystem.h
++      $(GCC_FOR_TARGET) $(CRTSTUFF_CFLAGS) $(CRTSTUFF_T_CFLAGS) \
++        @inhibit_libc@ -c $(srcdir)/crtstuff.c -DCRT_END \
++        -o $(T)crtend$(objext)
++
++# These are versions of crtbegin and crtend for shared libraries.
++$(T)crtbeginS.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
++  gbl-ctors.h stmp-int-hdrs tsystem.h
++      $(GCC_FOR_TARGET) $(CRTSTUFF_CFLAGS) $(CRTSTUFF_T_CFLAGS_S) \
++        @inhibit_libc@ -c $(srcdir)/crtstuff.c -DCRT_BEGIN -DCRTSTUFFS_O \
++        -o $(T)crtbeginS$(objext)
++
++$(T)crtendS.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
++  gbl-ctors.h stmp-int-hdrs tsystem.h
++      $(GCC_FOR_TARGET) $(CRTSTUFF_CFLAGS) $(CRTSTUFF_T_CFLAGS_S) \
++        @inhibit_libc@ -c $(srcdir)/crtstuff.c -DCRT_END -DCRTSTUFFS_O \
++        -o $(T)crtendS$(objext)
++
++# This is a version of crtbegin for -static links.
++$(T)crtbeginT.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \
++  gbl-ctors.h stmp-int-hdrs tsystem.h
++      $(GCC_FOR_TARGET) $(CRTSTUFF_CFLAGS) $(CRTSTUFF_T_CFLAGS) \
++        @inhibit_libc@ -c $(srcdir)/crtstuff.c -DCRT_BEGIN -DCRTSTUFFT_O \
++        -o $(T)crtbeginT$(objext)
++
++# Compile the start modules crt0.o and mcrt0.o that are linked with
++# every program
++crt0.o: s-crt0 ; @true
++mcrt0.o: s-crt0; @true
++
++s-crt0:       $(CRT0_S) $(MCRT0_S) $(GCC_PASSES) $(CONFIG_H)
++      $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(CRT0STUFF_T_CFLAGS) \
++        -o crt0.o -c $(CRT0_S)
++      $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(CRT0STUFF_T_CFLAGS) \
++        -o mcrt0.o -c $(MCRT0_S)
++      $(STAMP) s-crt0
++#\f
++# Compiling object files from source files.
++
++# Note that dependencies on obstack.h are not written
++# because that file is not part of GCC.
++
++# C language specific files.
++
++c-errors.o: c-errors.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) flags.h \
++    diagnostic.h $(TM_P_H)
++c-parse.o : $(srcdir)/c-parse.c $(CONFIG_H) $(TREE_H) $(GGC_H) intl.h \
++    $(C_TREE_H) input.h flags.h $(SYSTEM_H) toplev.h output.h $(CPPLIB_H) \
++    gt-c-parse.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++             -c $(srcdir)/c-parse.c $(OUTPUT_OPTION)
++
++$(srcdir)/c-parse.c: $(srcdir)/c-parse.y
++      cd $(srcdir) && \
++      if $(BISON) $(BISONFLAGS) -o c-p$$$$.c c-parse.y; then \
++        test -f c-p$$$$.output && mv -f c-p$$$$.output c-parse.output ; \
++        mv -f c-p$$$$.c c-parse.c ; \
++      else \
++        rm -f c-p$$$$.* ; \
++        false ; \
++      fi
++
++$(srcdir)/c-parse.y: c-parse.in
++      echo '/*WARNING: This file is automatically generated!*/' >tmp-c-parse.y
++      sed -e "/^ifobjc$$/,/^end ifobjc$$/d" \
++        -e "/^ifc$$/d" -e "/^end ifc$$/d" \
++        $(srcdir)/c-parse.in >>tmp-c-parse.y
++      $(SHELL) $(srcdir)/move-if-change tmp-c-parse.y $(srcdir)/c-parse.y
++
++c-decl.o : c-decl.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) $(C_TREE_H) \
++    $(GGC_H) $(TARGET_H) flags.h function.h output.h $(EXPR_H) \
++    debug.h toplev.h intl.h $(TM_P_H) tree-inline.h $(TIMEVAR_H) c-pragma.h \
++    gt-c-decl.h libfuncs.h except.h
++c-typeck.o : c-typeck.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
++    $(TARGET_H) flags.h intl.h output.h $(EXPR_H) $(RTL_H) toplev.h $(TM_P_H)
++c-lang.o : c-lang.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
++    $(GGC_H) langhooks.h $(LANGHOOKS_DEF_H) c-common.h gtype-c.h
++c-lex.o : c-lex.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) \
++    debug.h $(C_TREE_H) c-common.h real.h \
++    c-pragma.h input.h intl.h flags.h toplev.h output.h \
++    mbchar.h $(CPPLIB_H) $(EXPR_H) $(TM_P_H)
++c-objc-common.o : c-objc-common.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
++    $(C_TREE_H) $(RTL_H) insn-config.h integrate.h $(EXPR_H) $(C_TREE_H) \
++    flags.h toplev.h tree-inline.h diagnostic.h integrate.h $(VARRAY_H) \
++    langhooks.h $(GGC_H) gt-c-objc-common.h $(TARGET_H)
++c-aux-info.o : c-aux-info.c  $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
++    flags.h toplev.h
++c-convert.o : c-convert.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h toplev.h \
++    $(C_COMMON_H)
++c-pragma.o: c-pragma.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) function.h \
++    c-pragma.h toplev.h output.h $(GGC_H) $(TM_P_H) $(C_COMMON_H) gt-c-pragma.h
++mbchar.o: mbchar.c $(CONFIG_H) $(SYSTEM_H) mbchar.h
++graph.o: graph.c $(CONFIG_H) $(SYSTEM_H) toplev.h flags.h output.h $(RTL_H) \
++    function.h hard-reg-set.h $(BASIC_BLOCK_H) graph.h
++sbitmap.o: sbitmap.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h hard-reg-set.h \
++    $(BASIC_BLOCK_H)
++
++COLLECT2_OBJS = collect2.o tlink.o intl.o version.o
++COLLECT2_LIBS = @COLLECT2_LIBS@
++collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS)
++# Don't try modifying collect2 (aka ld) in place--it might be linking this.
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o T$@ \
++              $(COLLECT2_OBJS) $(LIBS) $(COLLECT2_LIBS)
++      mv -f T$@ $@
++
++collect2.o : collect2.c $(CONFIG_H) $(SYSTEM_H) gstab.h intl.h \
++      $(OBSTACK_H) $(DEMANGLE_H) collect2.h version.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES)  \
++      -DTARGET_MACHINE=\"$(target_alias)\" \
++      -c $(srcdir)/collect2.c $(OUTPUT_OPTION)
++
++tlink.o: tlink.c $(DEMANGLE_H) $(HASHTAB_H) $(CONFIG_H) $(SYSTEM_H) \
++    $(OBSTACK_H) collect2.h intl.h
++
++# A file used by all variants of C.
++
++c-common.o : c-common.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(OBSTACK_H) \
++      $(C_COMMON_H) flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
++      $(EXPR_H) $(TM_P_H) builtin-types.def builtin-attrs.def $(TARGET_H) \
++      diagnostic.h except.h gt-c-common.h real.h langhooks.h c-tree.h
++c-pretty-print.o : c-pretty-print.c c-pretty-print.h pretty-print.h \
++      $(C_COMMON_H) $(CONFIG_H) $(SYSTEM_H) real.h
++
++c-opts.o : c-opts.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_COMMON_H) \
++        c-pragma.h flags.h toplev.h langhooks.h tree-inline.h diagnostic.h \
++      intl.h
++
++# A file used by all variants of C and some other languages.
++
++attribs.o : attribs.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h \
++      toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) $(EXPR_H) $(TM_P_H) \
++      builtin-types.def $(TARGET_H) langhooks.h
++
++c-format.o : c-format.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) langhooks.h \
++      $(C_COMMON_H) flags.h toplev.h intl.h diagnostic.h
++
++c-semantics.o : c-semantics.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
++      flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
++      $(EXPR_H) $(PREDICT_H)
++
++c-dump.o : c-dump.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) tree-dump.h
++
++# Language-independent files.
++
++DRIVER_DEFINES = \
++  -DSTANDARD_STARTFILE_PREFIX=\"$(unlibsubdir)/\" \
++  -DSTANDARD_EXEC_PREFIX=\"$(libdir)/gcc-lib/\" \
++  -DDEFAULT_TARGET_VERSION=\"$(version)\" \
++  -DDEFAULT_TARGET_MACHINE=\"$(target_alias)\" \
++  -DSTANDARD_BINDIR_PREFIX=\"$(bindir)/\" \
++  -DTOOLDIR_BASE_PREFIX=\"$(unlibsubdir)/../\" \
++  $(VALGRIND_DRIVER_DEFINES) \
++  `test "X$${SHLIB_LINK}" = "X" || test "@enable_shared@" != "yes" || echo "-DENABLE_SHARED_LIBGCC"` \
++  `test "X$${SHLIB_MULTILIB}" = "X" || echo "-DNO_SHARED_LIBGCC_MULTILIB"`
++
++gcc.o: gcc.c $(CONFIG_H) $(SYSTEM_H) intl.h multilib.h \
++    Makefile $(lang_specs_files) specs.h prefix.h $(GCC_H)
++      (SHLIB_LINK='$(SHLIB_LINK)' \
++      SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++  $(DRIVER_DEFINES) \
++  -c $(srcdir)/gcc.c $(OUTPUT_OPTION))
++
++gccspec.o: gccspec.c $(CONFIG_H) $(SYSTEM_H) $(GCC_H)
++      (SHLIB_LINK='$(SHLIB_LINK)' \
++      SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++  $(DRIVER_DEFINES) \
++  -c $(srcdir)/gccspec.c $(OUTPUT_OPTION))
++
++cppspec.o: cppspec.c $(CONFIG_H) $(SYSTEM_H) $(GCC_H)
++
++tree-check.h: s-check ; @true
++s-check : gencheck$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./gencheck$(build_exeext) > tmp-check.h
++      $(SHELL) $(srcdir)/move-if-change tmp-check.h tree-check.h
++      $(STAMP) s-check
++
++gencheck$(build_exeext) : gencheck.o $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       gencheck.o $(HOST_LIBS)
++
++gencheck.o : gencheck.c gencheck.h tree.def $(HCONFIG_H) $(SYSTEM_H) \
++             $(lang_tree_files)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++        $(srcdir)/gencheck.c $(OUTPUT_OPTION)
++
++gencheck.h : s-gencheck ; @true
++s-gencheck : Makefile
++      ltf="$(lang_tree_files)"; for f in $$ltf; do \
++          echo "#include \"$$f\""; \
++      done | sed 's|$(srcdir)/||' > tmp-gencheck.h
++      $(SHELL) $(srcdir)/move-if-change tmp-gencheck.h gencheck.h
++      $(STAMP) s-gencheck
++
++options.h : s-options ; @true
++s-options : Makefile
++      lof="$(lang_options_files)"; for f in $$lof; do \
++          echo "#include \"$$f\""; \
++      done | sed 's|$(srcdir)/||' > tmp-options.h
++      $(SHELL) $(srcdir)/move-if-change tmp-options.h options.h
++      $(STAMP) s-options
++
++specs.h : s-specs ; @true
++s-specs : Makefile
++      lsf="$(lang_specs_files)"; for f in $$lsf; do \
++          echo "#include \"$$f\""; \
++      done | sed 's|$(srcdir)/||' > tmp-specs.h
++      $(SHELL) $(srcdir)/move-if-change tmp-specs.h specs.h
++      $(STAMP) s-specs
++
++dumpvers: dumpvers.c
++
++version.o: version.c version.h
++
++gtype-desc.o: gtype-desc.c $(CONFIG_H) $(SYSTEM_H) varray.h $(HASHTAB_H) \
++      $(TREE_H) $(RTL_H) function.h insn-config.h $(EXPR_H) $(OPTABS_H) \
++      libfuncs.h debug.h $(GGC_H) bitmap.h $(BASIC_BLOCK_H) hard-reg-set.h \
++      ssa.h cselib.h insn-addr.h
++
++ggc-common.o: ggc-common.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
++      flags.h $(GGC_H) varray.h $(HASHTAB_H) $(TM_P_H) langhooks.h \
++      $(PARAMS_H)
++
++ggc-simple.o: ggc-simple.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++      $(GGC_H) varray.h $(TIMEVAR_H) $(TM_P_H) $(PARAMS_H)
++
++ggc-page.o: ggc-page.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++      toplev.h $(GGC_H) $(TIMEVAR_H) $(TM_P_H)
++
++stringpool.o: stringpool.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(OBSTACK_H) \
++      flags.h toplev.h $(GGC_H)
++
++hashtable.o: hashtable.c hashtable.h $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H)
++
++line-map.o: line-map.c line-map.h intl.h $(CONFIG_H) $(SYSTEM_H)
++
++ggc-none.o: ggc-none.c $(GCONFIG_H) $(SYSTEM_H) $(GGC_H)
++      $(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++
++prefix.o: prefix.c $(CONFIG_H) $(SYSTEM_H) Makefile prefix.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++      -DPREFIX=\"$(prefix)\" \
++        -c $(srcdir)/prefix.c $(OUTPUT_OPTION)
++
++convert.o: convert.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h convert.h \
++   toplev.h langhooks.h
++
++langhooks.o : langhooks.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) toplev.h \
++   tree-inline.h $(RTL_H) insn-config.h integrate.h langhooks.h \
++   $(LANGHOOKS_DEF_H) flags.h
++tree.o : tree.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h function.h toplev.h \
++   $(GGC_H) $(HASHTAB_H) $(TARGET_H) output.h $(TM_P_H) langhooks.h \
++   real.h gt-tree.h
++tree-dump.o: tree-dump.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
++   flags.h langhooks.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
++   $(EXPR_H) $(SPLAY_TREE_H) tree-dump.h
++tree-inline.o : tree-inline.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) \
++   expr.h flags.h params.h input.h insn-config.h $(INTEGRATE_H) \
++   $(VARRAY_H) $(HASHTAB_H) $(SPLAY_TREE_H) toplev.h langhooks.h \
++   $(C_COMMON_H) tree-inline.h
++print-tree.o : print-tree.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(GGC_H) \
++   langhooks.h real.h
++stor-layout.o : stor-layout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h \
++   function.h $(EXPR_H) $(RTL_H) toplev.h $(GGC_H) $(TM_P_H) $(TARGET_H) \
++   langhooks.h
++fold-const.o : fold-const.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h real.h \
++   toplev.h $(HASHTAB_H) $(EXPR_H) $(RTL_H) $(GGC_H) $(TM_P_H) langhooks.h
++diagnostic.o : diagnostic.c diagnostic.h real.h diagnostic.def \
++   $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TM_P_H) flags.h $(GGC_H) \
++   input.h toplev.h intl.h langhooks.h $(LANGHOOKS_DEF_H)
++toplev.o : toplev.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) function.h \
++   flags.h xcoffout.h input.h $(INSN_ATTR_H) output.h diagnostic.h \
++   debug.h insn-config.h intl.h $(RECOG_H) Makefile toplev.h \
++   dwarf2out.h sdbout.h dbxout.h $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) \
++   graph.h $(LOOP_H) except.h $(REGS_H) $(TIMEVAR_H) $(lang_options_files) \
++   ssa.h $(PARAMS_H) $(TM_P_H) reload.h dwarf2asm.h $(TARGET_H) \
++   langhooks.h insn-flags.h options.h cfglayout.h real.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        -DTARGET_NAME=\"$(target_alias)\" \
++        -c $(srcdir)/toplev.c $(OUTPUT_OPTION)
++main.o : main.c $(CONFIG_H) $(SYSTEM_H) toplev.h
++
++rtl-error.o: rtl-error.c system.h $(RTL_H) $(INSN_ATTR_H) insn-config.h \
++   input.h toplev.h intl.h diagnostic.h $(CONFIG_H)
++
++rtl.o : rtl.c $(GCONFIG_H) $(SYSTEM_H) $(RTL_H) real.h $(GGC_H) errors.h
++      $(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++
++print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
++    hard-reg-set.h $(BASIC_BLOCK_H) real.h
++rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) toplev.h $(RTL_H) \
++   hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) real.h flags.h
++
++errors.o : errors.c $(GCONFIG_H) $(SYSTEM_H) errors.h
++      $(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++
++varasm.o : varasm.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) flags.h \
++   function.h $(EXPR_H) hard-reg-set.h $(REGS_H) \
++   output.h c-pragma.h toplev.h xcoffout.h debug.h $(GGC_H) $(TM_P_H) \
++   $(HASHTAB_H) $(TARGET_H) langhooks.h gt-varasm.h real.h
++function.o : function.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   function.h $(EXPR_H) libfuncs.h $(REGS_H) hard-reg-set.h \
++   insn-config.h $(RECOG_H) output.h toplev.h except.h $(HASHTAB_H) $(GGC_H) \
++   $(TM_P_H) langhooks.h gt-function.h
++stmt.o : stmt.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h function.h  \
++   insn-config.h hard-reg-set.h $(EXPR_H) libfuncs.h except.h \
++   $(LOOP_H) $(RECOG_H) toplev.h output.h varray.h $(GGC_H) $(TM_P_H) \
++   langhooks.h $(PREDICT_H) gt-stmt.h
++except.o : except.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   except.h function.h $(EXPR_H) libfuncs.h integrate.h langhooks.h \
++   insn-config.h hard-reg-set.h $(BASIC_BLOCK_H) output.h \
++   dwarf2asm.h dwarf2out.h toplev.h $(HASHTAB_H) intl.h $(GGC_H) \
++   gt-except.h
++expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h function.h \
++   $(REGS_H) $(EXPR_H) $(OPTABS_H) libfuncs.h insn-attr.h insn-config.h \
++   $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
++   except.h reload.h $(GGC_H) langhooks.h intl.h $(TM_P_H) real.h
++builtins.o : builtins.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   $(TARGET_H) function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) insn-config.h \
++   $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \
++   except.h $(TM_P_H) $(PREDICT_H) libfuncs.h real.h langhooks.h
++calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   $(EXPR_H) langhooks.h $(TARGET_H) \
++   libfuncs.h $(REGS_H) toplev.h output.h function.h $(TIMEVAR_H) $(TM_P_H) \
++   except.h
++expmed.o : expmed.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h  \
++   insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) real.h \
++   toplev.h $(TM_P_H) langhooks.h
++explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   hard-reg-set.h insn-config.h $(EXPR_H) $(OPTABS_H) $(RECOG_H) \
++   toplev.h function.h ggc.h $(TM_P_H) gt-explow.h
++optabs.o : optabs.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h  \
++   insn-config.h $(EXPR_H) $(OPTABS_H) libfuncs.h $(RECOG_H) reload.h \
++   toplev.h $(GGC_H) real.h $(TM_P_H) except.h gt-optabs.h $(BASIC_BLOCK_H)
++dbxout.o : dbxout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) flags.h \
++    $(REGS_H) debug.h $(TM_P_H) $(TARGET_H) function.h langhooks.h \
++   insn-config.h reload.h gstab.h xcoffout.h output.h dbxout.h toplev.h
++debug.o : debug.c debug.h $(CONFIG_H) $(SYSTEM_H)
++sdbout.o : sdbout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) flags.h \
++   function.h $(EXPR_H) output.h hard-reg-set.h $(REGS_H) real.h \
++   insn-config.h xcoffout.h c-pragma.h ggc.h \
++   sdbout.h toplev.h $(TM_P_H) except.h debug.h langhooks.h gt-sdbout.h
++dwarfout.o : dwarfout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) dwarf.h \
++   flags.h insn-config.h reload.h output.h toplev.h $(TM_P_H) \
++   debug.h langhooks.h
++dwarf2out.o : dwarf2out.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) dwarf2.h \
++   debug.h flags.h insn-config.h reload.h output.h diagnostic.h real.h \
++   hard-reg-set.h $(REGS_H) $(EXPR_H) libfuncs.h toplev.h dwarf2out.h varray.h \
++   $(GGC_H) except.h dwarf2asm.h $(TM_P_H) langhooks.h $(HASHTAB_H) gt-dwarf2out.h
++dwarf2asm.o : dwarf2asm.c $(CONFIG_H) $(SYSTEM_H) flags.h $(RTL_H) $(TREE_H) \
++   output.h dwarf2asm.h $(TM_P_H) $(GGC_H)
++vmsdbgout.o : vmsdbgout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) flags.h \
++   output.h vmsdbg.h debug.h langhooks.h function.h
++xcoffout.o : xcoffout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) xcoffout.h \
++   flags.h toplev.h output.h dbxout.h $(GGC_H) $(TARGET_H)
++emit-rtl.o : emit-rtl.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   function.h $(REGS_H) insn-config.h $(RECOG_H) real.h $(GGC_H) \
++   $(EXPR_H) $(srcdir)/../include/obstack.h hard-reg-set.h bitmap.h toplev.h \
++   $(HASHTAB_H) $(TM_P_H) debug.h langhooks.h
++real.o : real.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) toplev.h $(TM_P_H)
++integrate.o : integrate.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   debug.h $(INTEGRATE_H) insn-config.h $(EXPR_H) real.h $(REGS_H) \
++   intl.h function.h output.h $(RECOG_H) except.h toplev.h $(LOOP_H) \
++   $(PARAMS_H) $(TM_P_H) $(TARGET_H) langhooks.h gt-integrate.h
++jump.o : jump.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h hard-reg-set.h $(REGS_H) \
++   insn-config.h $(RECOG_H) $(EXPR_H) real.h except.h function.h \
++   toplev.h $(INSN_ATTR_H) $(TM_P_H) reload.h $(PREDICT_H)
++
++simplify-rtx.o : simplify-rtx.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) \
++   hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
++   output.h function.h $(GGC_H) $(OBSTACK_H) $(TM_P_H) $(TREE_H)
++cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) \
++   hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
++   output.h function.h cselib.h $(GGC_H) $(TM_P_H) gt-cselib.h
++cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
++   real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h output.h function.h \
++   $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H)
++gcse.o : gcse.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) hard-reg-set.h \
++   flags.h real.h insn-config.h ggc.h $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) \
++   function.h output.h toplev.h $(TM_P_H) $(PARAMS_H) except.h gt-gcse.h
++sibcall.o : sibcall.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) function.h \
++   hard-reg-set.h flags.h insn-config.h $(RECOG_H) $(BASIC_BLOCK_H)
++resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h $(SYSTEM_H) \
++   $(BASIC_BLOCK_H) $(REGS_H) flags.h output.h resource.h function.h toplev.h \
++   $(INSN_ATTR_H) except.h $(PARAMS_H) $(TM_P_H)
++lcm.o : lcm.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
++   real.h insn-config.h $(INSN_ATTR_H) $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) \
++   $(TM_P_H) df.h
++ssa.o : ssa.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) varray.h $(EXPR_H) \
++   hard-reg-set.h flags.h function.h real.h insn-config.h $(RECOG_H)  \
++   $(BASIC_BLOCK_H) output.h ssa.h
++ssa-dce.o : ssa-dce.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) hard-reg-set.h \
++   $(BASIC_BLOCK_H) ssa.h insn-config.h $(RECOG_H) output.h
++ssa-ccp.o : ssa-ccp.c $(CONFIG_H) system.h $(RTL_H) hard-reg-set.h \
++    $(BASIC_BLOCK_H) ssa.h insn-config.h $(RECOG_H) output.h \
++    errors.h $(GGC_H) df.h function.h
++df.o : df.c $(CONFIG_H) system.h $(RTL_H) insn-config.h $(RECOG_H) \
++   function.h $(REGS_H) $(OBSTACK_H) hard-reg-set.h $(BASIC_BLOCK_H) df.h \
++   $(FIBHEAP_H)
++conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \
++   $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
++profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   insn-config.h output.h $(REGS_H) $(EXPR_H) function.h \
++   gcov-io.h toplev.h $(GGC_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TARGET_H) \
++   langhooks.h profile.h libfuncs.h gt-profile.h
++loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h $(LOOP_H) \
++   insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \
++   real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h \
++   toplev.h varray.h except.h cselib.h $(OPTABS_H) $(TM_P_H)
++doloop.o : doloop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h $(LOOP_H) \
++   $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TM_P_H) toplev.h
++unroll.o : unroll.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h function.h \
++   $(INTEGRATE_H) $(REGS_H) $(RECOG_H) flags.h $(EXPR_H) $(LOOP_H) toplev.h \
++   hard-reg-set.h varray.h $(BASIC_BLOCK_H) $(TM_P_H) $(PREDICT_H) $(PARAMS_H)
++flow.o : flow.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h insn-config.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
++   function.h except.h $(EXPR_H) ssa.h $(GGC_H) $(TM_P_H)
++cfg.o : cfg.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h insn-config.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
++   function.h except.h $(GGC_H) $(TM_P_H)
++cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h insn-config.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
++   function.h except.h $(GGC_H) $(TM_P_H) insn-config.h
++cfganal.o : cfganal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(BASIC_BLOCK_H) \
++   hard-reg-set.h insn-config.h $(RECOG_H) $(GGC_H) $(TM_P_H)
++cfgbuild.o : cfgbuild.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h insn-config.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
++   function.h except.h $(GGC_H)
++cfgcleanup.o : cfgcleanup.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TIMEVAR_H)\
++   $(BASIC_BLOCK_H) hard-reg-set.h output.h flags.h $(RECOG_H) toplev.h \
++   $(GGC_H) insn-config.h cselib.h $(TARGET_H) $(TM_P_H) $(PARAMS_H)
++cfgloop.o : cfgloop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
++   $(BASIC_BLOCK_H) hard-reg-set.h
++dominance.o : dominance.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) hard-reg-set.h \
++   $(BASIC_BLOCK_H) et-forest.h
++et-forest.o : et-forest.c $(CONFIG_H) $(SYSTEM_H) et-forest.h
++combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h function.h \
++   insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
++   $(BASIC_BLOCK_H) $(RECOG_H) real.h hard-reg-set.h toplev.h $(TM_P_H)
++regclass.o : regclass.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) hard-reg-set.h flags.h \
++   $(BASIC_BLOCK_H) $(REGS_H) insn-config.h $(RECOG_H) reload.h real.h \
++   toplev.h function.h output.h $(GGC_H) $(TM_P_H) $(EXPR_H)
++local-alloc.o : local-alloc.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h insn-config.h $(RECOG_H) \
++   output.h function.h $(INSN_ATTR_H) toplev.h  except.h $(TM_P_H)
++bitmap.o : bitmap.c $(GCONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h \
++   $(BASIC_BLOCK_H) $(REGS_H) $(GGC_H)
++      $(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++global.o : global.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h reload.h function.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h insn-config.h output.h toplev.h \
++   $(TM_P_H)
++varray.o : varray.c $(CONFIG_H) $(SYSTEM_H) varray.h $(GGC_H) errors.h
++ra.o : ra.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H)  $(TM_P_H) insn-config.h \
++   $(RECOG_H) integrate.h function.h $(REGS_H) $(OBSTACK_H) hard-reg-set.h \
++   $(BASIC_BLOCK_H) df.h expr.h output.h toplev.h flags.h reload.h ra.h
++ra-build.o : ra-build.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
++   insn-config.h $(RECOG_H) function.h $(REGS_H) hard-reg-set.h \
++   $(BASIC_BLOCK_H) df.h output.h ggc.h ra.h gt-ra-build.h reload.h
++ra-colorize.o : ra-colorize.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
++    function.h $(REGS_H) hard-reg-set.h $(BASIC_BLOCK_H) df.h output.h ra.h
++ra-debug.o : ra-debug.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H)  insn-config.h \
++   $(RECOG_H) function.h hard-reg-set.h $(BASIC_BLOCK_H) df.h output.h ra.h \
++   $(TM_P_H)
++ra-rewrite.o : ra-rewrite.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_P_H) \
++   function.h $(REGS_H) hard-reg-set.h $(BASIC_BLOCK_H) df.h expr.h \
++   output.h except.h ra.h reload.h insn-config.h
++reload.o : reload.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h output.h \
++   $(EXPR_H) $(OPTABS_H) reload.h $(RECOG_H) hard-reg-set.h insn-config.h \
++   $(REGS_H) function.h real.h toplev.h $(TM_P_H)
++reload1.o : reload1.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) real.h flags.h \
++   $(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
++   $(BASIC_BLOCK_H) $(RECOG_H) output.h function.h toplev.h cselib.h $(TM_P_H) \
++   except.h $(TREE_H)
++caller-save.o : caller-save.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h \
++   $(REGS_H) hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) function.h \
++   $(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H)
++reorg.o : reorg.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) conditions.h hard-reg-set.h \
++   $(BASIC_BLOCK_H) $(REGS_H) insn-config.h $(INSN_ATTR_H) except.h \
++   $(RECOG_H) function.h flags.h output.h $(EXPR_H) toplev.h $(PARAMS_H) $(TM_P_H)
++alias.o : alias.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h hard-reg-set.h \
++   $(BASIC_BLOCK_H) $(REGS_H) toplev.h output.h $(EXPR_H) \
++   $(GGC_H) function.h cselib.h $(TREE_H) $(TM_P_H) langhooks.h $(TARGET_H) \
++   gt-alias.h
++regmove.o : regmove.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h \
++   $(RECOG_H) output.h $(REGS_H) hard-reg-set.h flags.h function.h \
++   $(EXPR_H) $(BASIC_BLOCK_H) toplev.h $(TM_P_H) except.h reload.h
++haifa-sched.o : haifa-sched.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
++   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H)
++sched-deps.o : sched-deps.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
++   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h cselib.h $(PARAMS_H) $(TM_P_H)
++sched-rgn.o : sched-rgn.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
++   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H)
++sched-ebb.o : sched-ebb.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
++   $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H)
++sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
++   hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H) $(TM_P_H) \
++   $(TARGET_H) real.h
++final.o : final.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h intl.h \
++   $(REGS_H) $(RECOG_H) conditions.h insn-config.h $(INSN_ATTR_H) function.h \
++   real.h output.h hard-reg-set.h except.h debug.h xcoffout.h profile.h \
++   toplev.h reload.h dwarf2out.h $(BASIC_BLOCK_H) $(TM_P_H) $(TARGET_H) $(EXPR_H)
++recog.o : recog.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) function.h $(BASIC_BLOCK_H) \
++   $(REGS_H) $(RECOG_H) $(EXPR_H) hard-reg-set.h flags.h insn-config.h \
++   $(INSN_ATTR_H) real.h toplev.h output.h reload.h $(TM_P_H)
++reg-stack.o : reg-stack.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) $(RECOG_H) \
++   $(REGS_H) hard-reg-set.h flags.h insn-config.h toplev.h reload.h \
++   varray.h function.h $(TM_P_H) $(GGC_H) gt-reg-stack.h
++predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
++   insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \
++   $(RECOG_H) function.h except.h $(EXPR_H) $(TM_P_H) $(PREDICT_H) real.h \
++   $(PARAMS_H) $(TARGET_H)
++lists.o: lists.c $(CONFIG_H) $(SYSTEM_H) toplev.h $(RTL_H) $(GGC_H)
++bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
++   flags.h $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h $(TARGET_H)
++tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
++   $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \
++   $(PARAMS_H) profile.h
++cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) \
++   insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h function.h \
++   cfglayout.h
++timevar.o : timevar.c $(CONFIG_H) $(SYSTEM_H) $(TIMEVAR_H) flags.h intl.h
++regrename.o : regrename.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) insn-config.h \
++   $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h $(RECOG_H) function.h \
++   resource.h $(OBSTACK_H) flags.h $(TM_P_H)
++ifcvt.o : ifcvt.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(REGS_H) toplev.h \
++   flags.h insn-config.h function.h $(RECOG_H) $(BASIC_BLOCK_H) $(EXPR_H) \
++   output.h except.h $(TM_P_H) real.h
++params.o : params.c $(CONFIG_H) $(SYSTEM_H) $(PARAMS_H) toplev.h
++hooks.o: hooks.c $(CONFIG_H) $(SYSTEM_H) $(HOOKS_H)
++protector.o: protector.c $(CONFIG_H)
++
++$(out_object_file): $(out_file) $(CONFIG_H) $(TREE_H) $(GGC_H) \
++   $(RTL_H) $(REGS_H) hard-reg-set.h real.h insn-config.h conditions.h \
++   output.h $(INSN_ATTR_H) $(SYSTEM_H) toplev.h $(TARGET_H) libfuncs.h \
++   $(TARGET_DEF_H) function.h sched-int.h $(TM_P_H) $(EXPR_H) $(OPTABS_H) \
++   langhooks.h
++      $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++              $(out_file) $(OUTPUT_OPTION)
++
++# Build auxiliary files that support ecoff format.
++mips-tfile: mips-tfile.o version.o $(LIBDEPS)
++      $(CC) $(CFLAGS) $(LDFLAGS) -o $@ mips-tfile.o version.o $(LIBS)
++
++mips-tfile.o : mips-tfile.c $(CONFIG_H) $(RTL_H) $(SYSTEM_H) version.h
++
++mips-tdump: mips-tdump.o version.o $(LIBDEPS)
++      $(CC) $(CFLAGS) $(LDFLAGS) -o $@ mips-tdump.o version.o $(LIBS)
++
++mips-tdump.o : mips-tdump.c $(CONFIG_H) $(RTL_H) $(SYSTEM_H)
++
++#\f
++# Generate header and source files from the machine description,
++# and compile them.
++
++.PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
++  insn-emit.c insn-recog.c insn-extract.c insn-output.c insn-peep.c \
++  insn-attr.h insn-attrtab.c
++
++# The following pair of rules has this effect:
++# genconfig is run only if the md has changed since genconfig was last run;
++# but the file insn-config.h is touched only when its contents actually change.
++
++# Each of the other insn-* files is handled by a similar pair of rules.
++
++# This causes an anomaly in the results of make -n
++# because insn-* is older than s-*
++# and thus make -n thinks that insn-* will be updated
++# and force recompilation of things that depend on it.
++# We use move-if-change precisely to avoid such recompilation.
++# But there is no way to teach make -n that it will be avoided.
++
++# Each of the insn-*.[ch] rules has a semicolon at the end,
++# for otherwise the system Make on SunOS 4.1 never tries
++# to recompile insn-*.o.  To avoid problems and extra noise from
++# versions of make which don't like empty commands (nothing after the
++# trailing `;'), we call true for each.
++
++insn-config.h: s-config ; @true
++s-config : $(md_file) genconfig$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genconfig$(build_exeext) $(md_file) > tmp-config.h
++      $(SHELL) $(srcdir)/move-if-change tmp-config.h insn-config.h
++      $(STAMP) s-config
++
++insn-conditions.c: s-conditions ; @true
++s-conditions : $(md_file) genconditions$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genconditions$(build_exeext) $(md_file) > tmp-conditions.c
++      $(SHELL) $(srcdir)/move-if-change tmp-conditions.c insn-conditions.c
++      $(STAMP) s-conditions
++
++insn-conditions.o : insn-conditions.c $(GCONFIG_H) $(SYSTEM_H) $(RTL_H) \
++  $(TM_P_H) $(REGS_H) function.h $(RECOG_H) real.h output.h flags.h \
++  hard-reg-set.h resource.h toplev.h reload.h gensupport.h insn-constants.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) insn-conditions.c
++
++dummy-conditions.o : dummy-conditions.c $(HCONFIG_H) $(SYSTEM_H) gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++          $(srcdir)/dummy-conditions.c $(OUTPUT_OPTION)
++
++insn-flags.h: s-flags ; @true
++s-flags : $(md_file) genflags$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genflags$(build_exeext) $(md_file) > tmp-flags.h
++      $(SHELL) $(srcdir)/move-if-change tmp-flags.h insn-flags.h
++      $(STAMP) s-flags
++
++insn-codes.h: s-codes ; @true
++s-codes : $(md_file) gencodes$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./gencodes$(build_exeext) $(md_file) > tmp-codes.h
++      $(SHELL) $(srcdir)/move-if-change tmp-codes.h insn-codes.h
++      $(STAMP) s-codes
++
++insn-constants.h: s-constants ; @true
++s-constants : $(md_file) genconstants$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genconstants$(build_exeext) $(md_file) > tmp-constants.h
++      $(SHELL) $(srcdir)/move-if-change tmp-constants.h insn-constants.h
++      $(STAMP) s-constants
++
++insn-emit.o : insn-emit.c $(CONFIG_H) $(RTL_H) $(EXPR_H) real.h output.h \
++  insn-config.h $(OPTABS_H) $(SYSTEM_H) reload.h $(RECOG_H) toplev.h \
++  function.h flags.h hard-reg-set.h resource.h $(TM_P_H)
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-emit.c \
++        $(OUTPUT_OPTION)
++
++insn-emit.c: s-emit ; @true
++s-emit : $(md_file) genemit$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genemit$(build_exeext) $(md_file) > tmp-emit.c
++      $(SHELL) $(srcdir)/move-if-change tmp-emit.c insn-emit.c
++      $(STAMP) s-emit
++
++insn-recog.o : insn-recog.c $(CONFIG_H) $(RTL_H) insn-config.h $(RECOG_H) \
++  real.h output.h flags.h $(SYSTEM_H) function.h hard-reg-set.h resource.h \
++  $(TM_P_H) toplev.h reload.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-recog.c \
++        $(OUTPUT_OPTION)
++
++insn-recog.c: s-recog ; @true
++s-recog : $(md_file) genrecog$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genrecog$(build_exeext) $(md_file) > tmp-recog.c
++      $(SHELL) $(srcdir)/move-if-change tmp-recog.c insn-recog.c
++      $(STAMP) s-recog
++
++insn-opinit.o : insn-opinit.c $(CONFIG_H) $(RTL_H) \
++  insn-config.h flags.h $(RECOG_H) $(EXPR_H) $(OPTABS_H) reload.h $(SYSTEM_H)
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-opinit.c \
++        $(OUTPUT_OPTION)
++
++insn-opinit.c: s-opinit ; @true
++s-opinit : $(md_file) genopinit$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genopinit$(build_exeext) $(md_file) > tmp-opinit.c
++      $(SHELL) $(srcdir)/move-if-change tmp-opinit.c insn-opinit.c
++      $(STAMP) s-opinit
++
++insn-extract.o : insn-extract.c $(CONFIG_H) $(RTL_H) $(SYSTEM_H) toplev.h \
++  insn-config.h $(RECOG_H)
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-extract.c \
++        $(OUTPUT_OPTION)
++
++insn-extract.c: s-extract ; @true
++s-extract : $(md_file) genextract$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genextract$(build_exeext) $(md_file) > tmp-extract.c
++      $(SHELL) $(srcdir)/move-if-change tmp-extract.c insn-extract.c
++      $(STAMP) s-extract
++
++insn-peep.o : insn-peep.c $(CONFIG_H) $(RTL_H) $(REGS_H) output.h real.h \
++      $(SYSTEM_H) insn-config.h $(RECOG_H) except.h function.h $(TM_P_H)
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-peep.c \
++        $(OUTPUT_OPTION)
++
++insn-peep.c: s-peep ; @true
++s-peep : $(md_file) genpeep$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genpeep$(build_exeext) $(md_file) > tmp-peep.c
++      $(SHELL) $(srcdir)/move-if-change tmp-peep.c insn-peep.c
++      $(STAMP) s-peep
++
++insn-attrtab.o : insn-attrtab.c $(CONFIG_H) $(RTL_H) $(REGS_H) real.h \
++    output.h $(INSN_ATTR_H) insn-config.h $(SYSTEM_H) toplev.h $(RECOG_H) \
++      $(TM_P_H) flags.h
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-attrtab.c \
++        $(OUTPUT_OPTION)
++
++insn-attr.h: s-attr ; @true
++s-attr : $(md_file) genattr$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genattr$(build_exeext) $(md_file) > tmp-attr.h
++      $(SHELL) $(srcdir)/move-if-change tmp-attr.h insn-attr.h
++      $(STAMP) s-attr
++
++insn-attrtab.c: s-attrtab ; @true
++s-attrtab : $(md_file) genattrtab$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genattrtab$(build_exeext) $(md_file) > tmp-attrtab.c
++      $(SHELL) $(srcdir)/move-if-change tmp-attrtab.c insn-attrtab.c
++      $(STAMP) s-attrtab
++
++insn-output.o : insn-output.c $(CONFIG_H) $(RTL_H) $(GGC_H) $(REGS_H) real.h \
++    conditions.h hard-reg-set.h insn-config.h $(INSN_ATTR_H) $(EXPR_H) \
++    output.h $(RECOG_H) function.h $(SYSTEM_H) toplev.h flags.h \
++    insn-codes.h $(TM_P_H)
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -c insn-output.c \
++        $(OUTPUT_OPTION)
++
++insn-output.c: s-output ; @true
++s-output : $(md_file) genoutput$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genoutput$(build_exeext) $(md_file) > tmp-output.c
++      $(SHELL) $(srcdir)/move-if-change tmp-output.c insn-output.c
++      $(STAMP) s-output
++
++genrtl.o : genrtl.c $(CONFIG_H) $(RTL_H) $(SYSTEM_H) $(GGC_H)
++genrtl.c genrtl.h : s-genrtl
++      @true   # force gnu make to recheck modification times.
++
++s-genrtl: gengenrtl$(build_exeext) $(srcdir)/move-if-change $(RTL_BASE_H)
++      $(RUN_GEN) ./gengenrtl$(build_exeext) -h > tmp-genrtl.h
++      $(SHELL) $(srcdir)/move-if-change tmp-genrtl.h genrtl.h
++      $(RUN_GEN) ./gengenrtl$(build_exeext) > tmp-genrtl.c
++      $(SHELL) $(srcdir)/move-if-change tmp-genrtl.c genrtl.c
++      $(STAMP) s-genrtl
++
++tm-preds.h: s-preds; @true
++
++s-preds: genpreds$(build_exeext) $(srcdir)/move-if-change
++      $(RUN_GEN) ./genpreds$(build_exeext) > tmp-preds.h
++      $(SHELL) $(srcdir)/move-if-change tmp-preds.h tm-preds.h
++      $(STAMP) s-preds
++
++GTFILES = $(GCONFIG_H) $(srcdir)/location.h \
++  $(HASHTAB_H) \
++  $(srcdir)/bitmap.h $(srcdir)/function.h  $(srcdir)/rtl.h $(srcdir)/optabs.h \
++  $(srcdir)/tree.h $(srcdir)/libfuncs.h $(srcdir)/hashtable.h $(srcdir)/real.h \
++  $(srcdir)/varray.h $(srcdir)/ssa.h $(srcdir)/insn-addr.h $(srcdir)/cselib.h \
++  $(srcdir)/c-common.h $(srcdir)/c-tree.h \
++  $(srcdir)/basic-block.h \
++  $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c \
++  $(srcdir)/dwarf2out.c $(srcdir)/emit-rtl.c \
++  $(srcdir)/except.c $(srcdir)/explow.c $(srcdir)/expr.c \
++  $(srcdir)/fold-const.c $(srcdir)/function.c \
++  $(srcdir)/gcse.c $(srcdir)/integrate.c $(srcdir)/lists.c $(srcdir)/optabs.c \
++  $(srcdir)/profile.c $(srcdir)/ra-build.c $(srcdir)/regclass.c \
++  $(srcdir)/reg-stack.c \
++  $(srcdir)/sdbout.c $(srcdir)/stmt.c $(srcdir)/stor-layout.c \
++  $(srcdir)/tree.c $(srcdir)/varasm.c \
++  $(out_file) \
++  @all_gtfiles@
++
++GTFILES_FILES_LANGS = @all_gtfiles_files_langs@
++GTFILES_FILES_FILES = @all_gtfiles_files_files@
++GTFILES_LANG_DIR_NAMES = @subdirs@
++GTFILES_SRCDIR = @srcdir@
++
++gtype-desc.h gtype-desc.c gt-except.h gt-function.h : s-gtype; @true
++gt-integrate.h gt-stmt.h gt-tree.h gt-varasm.h gt-emit-rtl.h : s-gtype; @true
++gt-explow.h gt-stor-layout.h gt-regclass.h gt-lists.h : s-gtype; @true
++gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h gt-profile.h : s-gtype; @true
++gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dwarf2out.h : s-gtype ; @true
++gt-ra-build.h gt-reg-stack.h : s-gtype ; @true
++gt-c-common.h gt-c-decl.h gt-c-parse.h gt-c-pragma.h : s-gtype; @true
++gt-c-objc-common.h gtype-c.h gt-location.h : s-gtype ; @true
++
++gtyp-gen.h: Makefile
++      echo "/* This file is machine generated.  Do not edit.  */" > tmp-gtyp.h
++      echo "static const char *srcdir = "  >> tmp-gtyp.h
++      echo "\"$(GTFILES_SRCDIR)\"" >> tmp-gtyp.h
++      echo ";" >> tmp-gtyp.h
++      echo "static const char *lang_files[] = {" >> tmp-gtyp.h
++      ll="$(GTFILES_FILES_FILES)"; \
++      for f in $$ll; do \
++      echo "\"$$f\", "; done >> tmp-gtyp.h
++      echo "NULL};" >> tmp-gtyp.h
++      echo "static const char *langs_for_lang_files[] = {" >> tmp-gtyp.h
++      ff="$(GTFILES_FILES_LANGS)"; \
++      for f in $$ff; do \
++      echo "\"$$f\", " ; done  >> tmp-gtyp.h
++      echo "NULL};" >> tmp-gtyp.h
++      echo "static const char *all_files[] = {" >> tmp-gtyp.h
++      gf="$(GTFILES)"; \
++      for f in $$gf; do \
++      echo "\"$$f\", "; done >> tmp-gtyp.h
++      echo " NULL};" >> tmp-gtyp.h
++      echo "static const char *lang_dir_names[] = { \"c\", " >> tmp-gtyp.h
++      gf="$(GTFILES_LANG_DIR_NAMES)"; \
++      for l in $$gf; do \
++      echo "\"$$l\", "; done >> tmp-gtyp.h
++      echo "NULL};" >> tmp-gtyp.h
++      $(SHELL) $(srcdir)/move-if-change tmp-gtyp.h gtyp-gen.h 
++
++s-gtype: gengtype$(build_exeext) $(GTFILES)
++      $(RUN_GEN) ./gengtype
++      $(STAMP) s-gtype
++
++#\f
++# Compile the programs that generate insn-* from the machine description.
++# They are compiled with $(HOST_CC), and associated libraries,
++# since they need to run on this machine
++# even if GCC is being compiled to run on some other machine.
++
++# $(CONFIG_H) is omitted from the deps of the gen*.o
++# because these programs don't really depend on anything
++# about the target machine.  They do depend on config.h itself,
++# since that describes the host machine.
++
++read-rtl.o: read-rtl.c $(HCONFIG_H) $(SYSTEM_H) $(RTL_H) \
++  $(OBSTACK_H) $(HASHTAB_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/read-rtl.c $(OUTPUT_OPTION)
++
++gensupport.o: gensupport.c $(RTL_H) $(OBSTACK_H) $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/gensupport.c $(OUTPUT_OPTION)
++
++genconfig$(build_exeext) : genconfig.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++        genconfig.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genconfig.o : genconfig.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genconfig.c $(OUTPUT_OPTION)
++
++genflags$(build_exeext) : genflags.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genflags.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genflags.o : genflags.c $(RTL_H) $(OBSTACK_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genflags.c $(OUTPUT_OPTION)
++
++gencodes$(build_exeext) : gencodes.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       gencodes.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++gencodes.o : gencodes.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/gencodes.c $(OUTPUT_OPTION)
++
++genconstants$(build_exeext) : genconstants.o $(HOST_RTL) $(HOST_EARLY_SUPPORT) \
++  $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genconstants.o $(HOST_EARLY_SUPPORT) $(HOST_RTL) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genconstants.o : genconstants.c $(RTL_H) $(HCONFIG_H) $(SYSTEM_H) errors.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genconstants.c $(OUTPUT_OPTION)
++
++genemit$(build_exeext) : genemit.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genemit.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genemit.o : genemit.c $(RTL_H) $(HCONFIG_H) $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genemit.c $(OUTPUT_OPTION)
++
++genopinit$(build_exeext) : genopinit.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genopinit.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genopinit.o : genopinit.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genopinit.c $(OUTPUT_OPTION)
++
++genrecog$(build_exeext) : genrecog.o $(HOST_RTL) $(HOST_SUPPORT) \
++    $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genrecog.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genrecog.o : genrecog.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genrecog.c $(OUTPUT_OPTION)
++
++genextract$(build_exeext) : genextract.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genextract.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genextract.o : genextract.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) insn-config.h errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genextract.c $(OUTPUT_OPTION)
++
++genpeep$(build_exeext) : genpeep.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genpeep.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genpeep.o : genpeep.c $(RTL_H) $(HCONFIG_H) $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genpeep.c $(OUTPUT_OPTION)
++
++genattr$(build_exeext) : genattr.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genattr.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genattr.o : genattr.c $(RTL_H) $(HCONFIG_H) $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genattr.c $(OUTPUT_OPTION)
++
++genattrtab$(build_exeext) : genattrtab.o genautomata.o \
++  $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) $(HOST_ERRORS) $(HOST_VARRAY) \
++  $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genattrtab.o genautomata.o \
++       $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) $(HOST_ERRORS) \
++       $(HOST_VARRAY) $(HOST_LIBS) -lm
++
++genattrtab.o : genattrtab.c $(RTL_H) $(OBSTACK_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h $(GGC_H) gensupport.h genattrtab.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genattrtab.c $(OUTPUT_OPTION)
++
++genautomata.o : genautomata.c $(RTL_H) $(OBSTACK_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h varray.h genattrtab.h $(HASHTAB_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genautomata.c $(OUTPUT_OPTION)
++
++genoutput$(build_exeext) : genoutput.o $(HOST_RTL) $(HOST_SUPPORT) \
++  $(HOST_PRINT) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genoutput.o $(HOST_RTL) $(HOST_SUPPORT) $(HOST_PRINT) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genoutput.o : genoutput.c $(RTL_H) $(HCONFIG_H) \
++  $(SYSTEM_H) errors.h gensupport.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genoutput.c $(OUTPUT_OPTION)
++
++gengenrtl$(build_exeext) : gengenrtl.o $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       gengenrtl.o $(HOST_LIBS)
++
++gengenrtl.o : gengenrtl.c $(RTL_BASE_H) $(HCONFIG_H) $(SYSTEM_H) real.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/gengenrtl.c $(OUTPUT_OPTION)
++
++genpreds$(build_exeext) : genpreds.o $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genpreds.o $(HOST_LIBS)
++
++genpreds.o : genpreds.c $(RTL_BASE_H) $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/genpreds.c $(OUTPUT_OPTION)
++
++gengtype$(build_exeext) : gengtype.o gengtype-lex.o gengtype-yacc.o \
++  $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       gengtype.o gengtype-lex.o gengtype-yacc.o $(HOST_LIBS)
++
++gengtype.o : gengtype.c gengtype.h $(HCONFIG_H) $(SYSTEM_H) real.h rtl.def \
++  gtyp-gen.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++        $(srcdir)/gengtype.c $(OUTPUT_OPTION)
++
++gengtype-lex.o : $(srcdir)/gengtype-lex.c gengtype.h $(srcdir)/gengtype-yacc.c \
++  $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++        $(srcdir)/gengtype-lex.c $(OUTPUT_OPTION)
++
++gengtype-yacc.o : $(srcdir)/gengtype-yacc.c gengtype.h $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++        $(srcdir)/gengtype-yacc.c $(OUTPUT_OPTION)
++
++# The sed command works around a bug in flex-2.5.4.
++$(srcdir)/gengtype-lex.c : $(srcdir)/gengtype-lex.l
++      cd $(srcdir) && \
++      $(FLEX) $(FLEXFLAGS) -t -o$@ gengtype-lex.l | \
++        sed 's/^\(char msg\[\];\)/yyconst \1/' > g-$$$$ ; \
++      if test $$? -eq 0 ; then \
++        mv -f g-$$$$ gengtype-lex.c ; \
++      else \
++        rm -f g-$$$$.* ; \
++        false ; \
++      fi
++
++$(srcdir)/gengtype-yacc.c: $(srcdir)/gengtype-yacc.y
++      (cd $(srcdir) && \
++       $(BISON) $(BISONFLAGS) -d -o gengtype-yacc.c gengtype-yacc.y || \
++       ( rm -f $@ && false ) )
++
++genconditions$(build_exeext) : genconditions.o $(HOST_EARLY_SUPPORT) \
++  $(HOST_RTL) $(HOST_ERRORS) $(HOST_LIBDEPS)
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++       genconditions.o $(HOST_EARLY_SUPPORT) $(HOST_RTL) \
++          $(HOST_ERRORS) $(HOST_LIBS)
++
++genconditions.o : genconditions.c $(RTL_H) $(HCONFIG_H) $(SYSTEM_H) errors.h
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++              $(srcdir)/genconditions.c $(OUTPUT_OPTION)
++
++#\f
++# Compile the libraries to be used by gen*.
++# If we are not cross-building, gen* use the same .o's that cc1 will use,
++# and BUILD_PREFIX_1 is `loser-', just to ensure these rules don't conflict
++# with the rules for rtl.o, etc.
++$(BUILD_PREFIX_1)rtl.o: $(srcdir)/rtl.c $(HCONFIG_H) $(SYSTEM_H) $(RTL_H) \
++  real.h $(GGC_H) errors.h
++      rm -f $(BUILD_PREFIX)rtl.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/rtl.c > $(BUILD_PREFIX)rtl.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(BUILD_PREFIX)rtl.c $(OUTPUT_OPTION)
++
++print-rtl1.o: $(srcdir)/print-rtl.c $(HCONFIG_H) \
++  $(RTL_H) $(TREE_H) hard-reg-set.h $(BASIC_BLOCK_H)
++      rm -f print-rtl1.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/print-rtl.c > print-rtl1.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) print-rtl1.c $(OUTPUT_OPTION)
++
++$(BUILD_PREFIX_1)bitmap.o: $(srcdir)/bitmap.c $(HCONFIG_H) $(SYSTEM_H) \
++  $(RTL_H) flags.h $(BASIC_BLOCK_H) $(REGS_H) $(GGC_H)
++      rm -f $(BUILD_PREFIX)bitmap.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/bitmap.c > $(BUILD_PREFIX)bitmap.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(BUILD_PREFIX)bitmap.c $(OUTPUT_OPTION)
++
++$(BUILD_PREFIX_1)errors.o: errors.c $(HCONFIG_H) $(SYSTEM_H) errors.h
++      rm -f $(BUILD_PREFIX)errors.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/errors.c > $(BUILD_PREFIX)errors.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(BUILD_PREFIX)errors.c $(OUTPUT_OPTION)
++
++$(BUILD_PREFIX_1)varray.o: varray.c $(HCONFIG_H) $(SYSTEM_H) varray.h \
++  $(RTL_H) $(GGC_H) $(TREE_H) bitmap.h errors.h
++      rm -f $(BUILD_PREFIX)varray.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/varray.c > \
++              $(BUILD_PREFIX)varray.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) \
++              $(BUILD_PREFIX)varray.c $(OUTPUT_OPTION)
++
++$(BUILD_PREFIX_1)ggc-none.o: ggc-none.c $(HCONFIG_H) $(SYSTEM_H) $(GGC_H)
++      rm -f $(BUILD_PREFIX)ggc-none.c
++      sed -e 's/config[.]h/hconfig.h/' $(srcdir)/ggc-none.c > $(BUILD_PREFIX)ggc-none.c
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(BUILD_PREFIX)ggc-none.c $(OUTPUT_OPTION)
++
++#\f
++# Remake internationalization support.
++intl.o: intl.c $(CONFIG_H) system.h intl.h Makefile
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        -DLOCALEDIR=\"$(localedir)\" \
++        -c $(srcdir)/intl.c $(OUTPUT_OPTION)
++
++$(top_builddir)/intl/libintl.a: intl.all
++
++intl.all intl.install intl.uninstall \
++  intl.mostlyclean intl.clean intl.distclean intl.maintainer-clean:
++      @for d in $(INTL_SUBDIRS); do \
++        target=`expr $@ : 'intl.\(.*\)'` && \
++        echo "(cd $$d && $(MAKE) $$target)" && \
++        (cd $$d && AWK='$(AWK)' $(MAKE) $(SUBDIR_FLAGS_TO_PASS) $$target); \
++        if [ $$? -eq 0 ] ; then true ; else exit 1 ; fi ; \
++      done
++
++# intl.all and intl.install need config.h to exist, and the files it includes.
++# (FIXME: intl/*.c shouldn't need to see insn-foo.h!)
++intl.all intl.install: config.h insn-flags.h insn-constants.h
++
++# Make-lang.in should add dependencies of po-generated on any generated
++# files which need to be scanned by gettext (usually Yacc-generated parsers).
++po-generated: c-parse.c
++
++#\f
++# Remake cpp and protoize.
++
++PREPROCESSOR_DEFINES = \
++  -DGCC_INCLUDE_DIR=\"$(libsubdir)/include\" \
++  -DGPLUSPLUS_INCLUDE_DIR=\"$(gcc_gxx_include_dir)\" \
++  -DGPLUSPLUS_TOOL_INCLUDE_DIR=\"$(gcc_gxx_include_dir)/$(target_alias)\" \
++  -DGPLUSPLUS_BACKWARD_INCLUDE_DIR=\"$(gcc_gxx_include_dir)/backward\" \
++  -DLOCAL_INCLUDE_DIR=\"$(local_includedir)\" \
++  -DCROSS_INCLUDE_DIR=\"$(gcc_tooldir)/sys-include\" \
++  -DTOOL_INCLUDE_DIR=\"$(gcc_tooldir)/include\"
++
++LIBCPP_OBJS = cpplib.o cpplex.o cppmacro.o cppexp.o cppfiles.o cpptrad.o \
++              cpphash.o cpperror.o cppinit.o cppdefault.o cppmain.o \
++              hashtable.o line-map.o mkdeps.o prefix.o mbchar.o
++
++LIBCPP_DEPS = $(CPPLIB_H) cpphash.h line-map.h hashtable.h intl.h \
++              $(OBSTACK_H) $(SYSTEM_H)
++
++# Most of the other archives built/used by this makefile are for
++# targets.  This one is strictly for the host.
++libcpp.a: $(LIBCPP_OBJS)
++      -rm -rf libcpp.a
++      $(AR) $(AR_FLAGS) libcpp.a $(LIBCPP_OBJS)
++      -$(RANLIB) libcpp.a
++
++cppmain.o:  cppmain.c  $(CONFIG_H) $(LIBCPP_DEPS)
++
++cpperror.o: cpperror.c $(CONFIG_H) $(LIBCPP_DEPS)
++cppexp.o:   cppexp.c   $(CONFIG_H) $(LIBCPP_DEPS)
++cpplex.o:   cpplex.c   $(CONFIG_H) $(LIBCPP_DEPS) mbchar.h
++cppmacro.o: cppmacro.c $(CONFIG_H) $(LIBCPP_DEPS)
++cpplib.o:   cpplib.c   $(CONFIG_H) $(LIBCPP_DEPS)
++cpphash.o:  cpphash.c  $(CONFIG_H) $(LIBCPP_DEPS)
++cpptrad.o:  cpptrad.c  $(CONFIG_H) $(LIBCPP_DEPS)
++cppfiles.o: cppfiles.c $(CONFIG_H) $(LIBCPP_DEPS) $(SPLAY_TREE_H) mkdeps.h
++cppinit.o:  cppinit.c  $(CONFIG_H) $(LIBCPP_DEPS) cppdefault.h \
++              mkdeps.h prefix.h
++
++cppdefault.o: cppdefault.c $(CONFIG_H) $(SYSTEM_H) cppdefault.h Makefile
++      $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        $(PREPROCESSOR_DEFINES) \
++        -c $(srcdir)/cppdefault.c $(OUTPUT_OPTION)
++
++mkdeps.o: mkdeps.c $(CONFIG_H) $(SYSTEM_H) mkdeps.h
++
++# Note for the stamp targets, we run the program `true' instead of
++# having an empty command (nothing following the semicolon).
++
++proto: config.status protoize$(exeext) unprotoize$(exeext) SYSCALLS.c.X
++
++PROTO_OBJS = intl.o version.o cppdefault.o
++
++protoize$(exeext): protoize.o $(PROTO_OBJS) $(LIBDEPS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ protoize.o $(PROTO_OBJS) $(LIBS)
++
++unprotoize$(exeext): unprotoize.o $(PROTO_OBJS) $(LIBDEPS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ unprotoize.o $(PROTO_OBJS) $(LIBS)
++
++protoize.o: protoize.c $(srcdir)/../include/getopt.h $(CONFIG_H) $(SYSTEM_H) \
++   Makefile version.h
++      (SHLIB_LINK='$(SHLIB_LINK)' \
++      SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
++      $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        $(DRIVER_DEFINES) \
++        $(srcdir)/protoize.c $(OUTPUT_OPTION))
++
++unprotoize.o: protoize.c $(srcdir)/../include/getopt.h \
++   $(CONFIG_H) $(SYSTEM_H) Makefile version.h
++      (SHLIB_LINK='$(SHLIB_LINK)' \
++      SHLIB_MULTILIB='$(SHLIB_MULTILIB)'; \
++      $(CC) -c -DUNPROTOIZE $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        $(DRIVER_DEFINES) \
++        $(srcdir)/protoize.c $(OUTPUT_OPTION))
++
++# This info describes the target machine, so compile with GCC just built.
++SYSCALLS.c.X: $(srcdir)/sys-types.h $(srcdir)/sys-protos.h $(GCC_PASSES) \
++   stmp-int-hdrs
++      -rm -f SYSCALLS.c tmp-SYSCALLS.s
++      sed -e s/TARGET_GETGROUPS_T/$(TARGET_GETGROUPS_T)/ \
++        $(srcdir)/sys-types.h $(srcdir)/sys-protos.h > SYSCALLS.c
++      $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
++        -aux-info $@ -S -o tmp-SYSCALLS.s SYSCALLS.c
++      -rm -f SYSCALLS.c tmp-SYSCALLS.s
++
++
++test-protoize-simple: ./protoize ./unprotoize $(GCC_PASSES)
++      -rm -f tmp-proto.[cso]
++      cp $(srcdir)/protoize.c tmp-proto.c
++      chmod u+w tmp-proto.c
++      ./protoize -N -B ./ -x getopt.h -c "-B./ -Wall -Wwrite-strings \
++        $(GCC_CFLAGS) $(INCLUDES) \
++        -DGCC_INCLUDE_DIR=0 \
++        -DGPLUSPLUS_INCLUDE_DIR=0 \
++        -DCROSS_INCLUDE_DIR=0 \
++        -DTOOL_INCLUDE_DIR=0 \
++        -DSTANDARD_EXEC_PREFIX=0 \
++        -DDEFAULT_TARGET_MACHINE=0 \
++        -DDEFAULT_TARGET_VERSION=0" tmp-proto.c
++      @echo '**********' Expect 400 lines of differences.
++      -diff $(srcdir)/protoize.c tmp-proto.c > tmp-proto.diff
++      -wc -l tmp-proto.diff
++      ./unprotoize -N -x getopt.h -c "-B./ -Wall -Wwrite-strings \
++        $(GCC_CFLAGS) $(INCLUDES) \
++        -DGCC_INCLUDE_DIR=0 \
++        -DGPLUSPLUS_INCLUDE_DIR=0 \
++        -DCROSS_INCLUDE_DIR=0 \
++        -DTOOL_INCLUDE_DIR=0 \
++        -DSTANDARD_EXEC_PREFIX=0 \
++        -DDEFAULT_TARGET_MACHINE=0 \
++        -DDEFAULT_TARGET_VERSION=0" tmp-proto.c
++      @echo Expect zero differences.
++      diff $(srcdir)/protoize.c tmp-proto.c | cat
++      -rm -f tmp-proto.[cs] tmp-proto$(objext)
++
++gcov.o: gcov.c gcov-io.h intl.h $(SYSTEM_H) $(CONFIG_H)
++
++# Only one of 'gcov' or 'gcov.exe' is actually built, depending
++# upon whether $(exeext) is empty or not.
++GCOV_OBJS = gcov.o intl.o version.o
++gcov$(exeext): $(GCOV_OBJS) $(LIBDEPS)
++      $(CC) $(ALL_CFLAGS) $(LDFLAGS) $(GCOV_OBJS) $(LIBS) -o $@
++#\f
++# Build the include directory.  The stamp files are stmp-* rather than
++# s-* so that mostlyclean does not force the include directory to
++# be rebuilt.
++
++# Build the include directory
++stmp-int-hdrs: $(STMP_FIXINC) $(USER_H) xlimits.h
++# Copy in the headers provided with gcc.
++# The sed command gets just the last file name component;
++# this is necessary because VPATH could add a dirname.
++# Using basename would be simpler, but some systems don't have it.
++# The touch command is here to workaround an AIX/Linux NFS bug.
++      -if [ -d include ] ; then true; else mkdir include; chmod a+rx include; fi
++      for file in .. $(USER_H); do \
++        if [ X$$file != X.. ]; then \
++          realfile=`echo $$file | sed -e 's|.*/\([^/]*\)$$|\1|'`; \
++          $(STAMP) include/$$realfile; \
++          rm -f include/$$realfile; \
++          cp $$file include; \
++          chmod a+r include/$$realfile; \
++        fi; \
++      done
++      rm -f include/limits.h
++      cp xlimits.h include/limits.h
++      chmod a+r include/limits.h
++# Install the README
++      rm -f include/README
++      cp $(srcdir)/README-fixinc include/README
++      chmod a+r include/README
++      $(STAMP) $@
++
++# fixinc.sh depends on this, not on specs directly.
++# The idea is to make sure specs gets built, but not rerun fixinc.sh
++# after each stage just because specs' mtime has changed.
++specs.ready: specs
++      -if [ -f specs.ready ] ; then \
++              true; \
++      else \
++              $(STAMP) specs.ready; \
++      fi
++
++FIXINCSRCDIR=$(srcdir)/fixinc
++fixinc.sh: $(FIXINCSRCDIR)/mkfixinc.sh $(FIXINCSRCDIR)/fixincl.c \
++      $(FIXINCSRCDIR)/procopen.c $(FIXINCSRCDIR)/gnu-regex.c \
++      $(FIXINCSRCDIR)/server.c $(FIXINCSRCDIR)/gnu-regex.h \
++      $(FIXINCSRCDIR)/server.h $(FIXINCSRCDIR)/inclhack.def specs.ready
++      (MAKE="$(MAKE)"; srcdir=`cd $(srcdir)/fixinc && ${PWD_COMMAND}` ; \
++      CC="$(HOST_CC)"; CFLAGS="$(HOST_CFLAGS)"; LDFLAGS="$(HOST_LDFLAGS)"; \
++      WARN_CFLAGS="$(WARN_CFLAGS)"; \
++      export MAKE srcdir CC CFLAGS LDFLAGS WARN_CFLAGS; cd ./fixinc && \
++      $(SHELL) $${srcdir}/mkfixinc.sh $(build_canonical) $(target))
++
++# Build fixed copies of system files.
++stmp-fixinc: fixinc.sh gsyslimits.h
++      rm -rf include; mkdir include
++      -chmod a+rx include
++      (TARGET_MACHINE='$(target)'; srcdir=`cd $(srcdir); ${PWD_COMMAND}`; \
++      SHELL='$(SHELL)' ;\
++      export TARGET_MACHINE srcdir SHELL ; \
++      $(SHELL) ./fixinc.sh `${PWD_COMMAND}`/include $(SYSTEM_HEADER_DIR) $(OTHER_FIXINCLUDES_DIRS); \
++      rm -f include/syslimits.h; \
++      if [ -f include/limits.h ]; then \
++        mv include/limits.h include/syslimits.h; \
++      else \
++        cp $(srcdir)/gsyslimits.h include/syslimits.h; \
++      fi; \
++      chmod a+r include/syslimits.h)
++# If $(SYSTEM_HEADER_DIR) is $(build_tooldir)/sys-include, and
++# that directory exists, then make sure that $(libsubdir) exists.
++# This is because cpp is compiled to find $(gcc_tooldir)/include via
++# $(libsubdir)/$(unlibsubdir), which will only work if $(libsubdir)
++# exists.
++# ??? Better would be to use -isystem $(build_tooldir)/sys-include,
++# but fixincludes does not take such arguments.
++      if [ "$(SYSTEM_HEADER_DIR)" = "$(build_tooldir)/sys-include" ] \
++         && [ -d $(build_tooldir)/sys-include ]; then \
++        if [ -d $(libdir) ] ; then true ; else mkdir $(libdir) ; fi; \
++        if [ -d $(libdir)/gcc-lib ] ; then true ; else mkdir $(libdir)/gcc-lib; fi; \
++        if [ -d $(libdir)/gcc-lib/$(target_alias) ] ; then true ; else mkdir $(libdir)/gcc-lib/$(target_alias) ; fi; \
++        if [ -d $(libdir)/gcc-lib/$(target_alias)/$(version) ] ; then true ; else mkdir $(libdir)/gcc-lib/$(target_alias)/$(version) ; fi; \
++      else true; fi
++      $(STAMP) stmp-fixinc
++
++# Files related to the fixproto script.
++# gen-protos and fix-header are compiled with HOST_CC, but they are only
++# used in native and host-x-target builds, so it's safe to link them with
++# libiberty.a.
++
++deduced.h: $(GCC_PASSES) $(srcdir)/scan-types.sh stmp-int-hdrs
++      if [ -d $(SYSTEM_HEADER_DIR) ]; \
++      then \
++        CC="$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(ALL_CPPFLAGS) -I. -I$(srcdir) -isystem include -isystem ${SYSTEM_HEADER_DIR}"; \
++        export CC; \
++        $(SHELL) $(srcdir)/scan-types.sh "$(srcdir)" >tmp-deduced.h; \
++        mv tmp-deduced.h deduced.h; \
++      else \
++        $(STAMP) deduced.h; \
++      fi
++
++GEN_PROTOS_OBJS = gen-protos.o scan.o
++gen-protos$(build_exeext): $(GEN_PROTOS_OBJS)
++      ${HOST_CC} $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ \
++        $(GEN_PROTOS_OBJS) $(HOST_LIBS)
++
++gen-protos.o: gen-protos.c scan.h $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/gen-protos.c $(OUTPUT_OPTION)
++
++scan.o: scan.c scan.h $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/scan.c $(OUTPUT_OPTION)
++
++xsys-protos.h: $(GCC_PASSES) $(srcdir)/sys-protos.h deduced.h gen-protos$(build_exeext) Makefile
++      sed -e s/TARGET_GETGROUPS_T/$(TARGET_GETGROUPS_T)/ \
++        deduced.h $(srcdir)/sys-protos.h > tmp-fixtmp.c
++      mv tmp-fixtmp.c fixtmp.c
++      $(GCC_FOR_TARGET) fixtmp.c -w -U__SIZE_TYPE__ -U__PTRDIFF_TYPE__ -U__WCHAR_TYPE__ -E \
++        | sed -e 's/  / /g' -e 's/ *(/ (/g' -e 's/ [ ]*/ /g' -e 's/( )/()/' \
++        | $(RUN_GEN) ./gen-protos >xsys-protos.hT
++      mv xsys-protos.hT xsys-protos.h
++      rm -rf fixtmp.c
++
++# This is nominally a 'build' program, but it's run only when host==build,
++# so we can (indeed, must) use $(LIBDEPS) and $(LIBS).
++fix-header$(build_exeext): fix-header.o scan-decls.o scan.o xsys-protos.h \
++           $(LIBDEPS) libcpp.a
++      $(HOST_CC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $@ fix-header.o \
++         scan-decls.o scan.o libcpp.a $(LIBS)
++
++fix-header.o: fix-header.c $(OBSTACK_H) scan.h \
++      xsys-protos.h $(HCONFIG_H) $(SYSTEM_H) $(CPPLIB_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/fix-header.c $(OUTPUT_OPTION)
++
++scan-decls.o: scan-decls.c scan.h $(CPPLIB_H) $(HCONFIG_H) $(SYSTEM_H)
++      $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(srcdir)/scan-decls.c $(OUTPUT_OPTION)
++
++# stmp-fixproto depends on this, not on fix-header directly.
++# The idea is to make sure fix-header gets built,
++# but not rerun fixproto after each stage
++# just because fix-header's mtime has changed.
++fixhdr.ready: fix-header$(build_exeext)
++      -if [ -f fixhdr.ready ] ; then \
++              true; \
++      else \
++              $(STAMP) fixhdr.ready; \
++      fi
++
++# stmp-int-headers is to make sure fixincludes has already finished.
++# The if statement is so that we don't run fixproto a second time
++# if it has already been run on the files in `include'.
++stmp-fixproto: fixhdr.ready fixproto stmp-int-hdrs
++      if [ -f include/fixed ] ; then true; \
++      else \
++        : This line works around a 'make' bug in BSDI 1.1.; \
++        FIXPROTO_DEFINES="$(FIXPROTO_DEFINES)"; export FIXPROTO_DEFINES; \
++        mkinstalldirs="$(SHELL) $(srcdir)/mkinstalldirs"; \
++          export mkinstalldirs; \
++        if [ -d $(SYSTEM_HEADER_DIR) ] ; then \
++          $(SHELL) ${srcdir}/fixproto include include $(SYSTEM_HEADER_DIR); \
++          if [ $$? -eq 0 ] ; then true ; else exit 1 ; fi ; \
++        else true; fi; \
++        $(STAMP) include/fixed; \
++      fi
++      $(STAMP) stmp-fixproto
++#\f
++# Remake the info files.
++
++docdir = $(srcdir)/doc
++
++doc: $(BUILD_INFO) $(GENERATED_MANPAGES) gccbug
++info: $(docdir)/cpp.info $(docdir)/gcc.info $(docdir)/gccint.info $(docdir)/gccinstall.info lang.info $(docdir)/cppinternals.info
++
++TEXI_CPP_FILES = $(docdir)/cpp.texi $(docdir)/include/fdl.texi \
++  $(docdir)/cppenv.texi $(docdir)/cppopts.texi
++
++TEXI_GCC_FILES = $(docdir)/gcc.texi $(docdir)/include/gcc-common.texi \
++       $(docdir)/frontends.texi $(docdir)/standards.texi \
++       $(docdir)/invoke.texi $(docdir)/extend.texi $(docdir)/md.texi \
++       $(docdir)/objc.texi $(docdir)/gcov.texi $(docdir)/trouble.texi \
++       $(docdir)/bugreport.texi $(docdir)/service.texi \
++       $(docdir)/contribute.texi $(docdir)/compat.texi \
++       $(docdir)/include/funding.texi $(docdir)/gnu.texi \
++       $(docdir)/include/gpl.texi $(docdir)/include/fdl.texi \
++       $(docdir)/contrib.texi $(docdir)/cppenv.texi $(docdir)/cppopts.texi
++
++TEXI_GCCINT_FILES = $(docdir)/gccint.texi \
++       $(docdir)/include/gcc-common.texi $(docdir)/contribute.texi \
++       $(docdir)/makefile.texi $(docdir)/configterms.texi \
++       $(docdir)/portability.texi $(docdir)/interface.texi \
++       $(docdir)/passes.texi $(docdir)/c-tree.texi \
++       $(docdir)/rtl.texi $(docdir)/md.texi $(docdir)/tm.texi \
++       $(docdir)/hostconfig.texi $(docdir)/fragments.texi \
++       $(docdir)/configfiles.texi $(docdir)/collect2.texi \
++       $(docdir)/headerdirs.texi $(docdir)/include/funding.texi \
++       $(docdir)/gnu.texi $(docdir)/include/gpl.texi \
++       $(docdir)/include/fdl.texi $(docdir)/contrib.texi \
++       $(docdir)/languages.texi $(docdir)/sourcebuild.texi \
++       $(docdir)/gty.texi
++
++TEXI_GCCINSTALL_FILES = $(docdir)/install.texi $(docdir)/install-old.texi \
++       $(docdir)/include/fdl.texi
++
++TEXI_CPPINT_FILES = $(docdir)/cppinternals.texi
++
++$(docdir)/cpp.info: $(TEXI_CPP_FILES)
++      cd $(srcdir) && $(MAKEINFO) $(MAKEINFOFLAGS) -I doc -I doc/include -o doc/cpp.info doc/cpp.texi
++
++$(docdir)/gcc.info: $(TEXI_GCC_FILES)
++      cd $(srcdir) && $(MAKEINFO) $(MAKEINFOFLAGS) -I doc -I doc/include -o doc/gcc.info doc/gcc.texi
++
++$(docdir)/gccint.info: $(TEXI_GCCINT_FILES)
++      cd $(srcdir) && $(MAKEINFO) $(MAKEINFOFLAGS) -I doc -I doc/include -o doc/gccint.info doc/gccint.texi
++
++$(docdir)/gccinstall.info: $(TEXI_GCCINSTALL_FILES)
++      cd $(srcdir) && $(MAKEINFO) $(MAKEINFOFLAGS) -I doc -I doc/include -o doc/gccinstall.info doc/install.texi
++
++$(docdir)/cppinternals.info: $(TEXI_CPPINT_FILES)
++      cd $(srcdir) && $(MAKEINFO) $(MAKEINFOFLAGS) -I doc -I doc/include -o doc/cppinternals.info \
++              doc/cppinternals.texi
++
++dvi: gcc.dvi gccint.dvi gccinstall.dvi cpp.dvi lang.dvi cppinternals.dvi
++
++# This works with GNU Make's default rule.
++cpp.dvi: $(TEXI_CPP_FILES)
++      $(TEXI2DVI) -I $(docdir) -I $(docdir)/include $(docdir)/cpp.texi
++
++gcc.dvi: $(TEXI_GCC_FILES)
++      $(TEXI2DVI) -I $(docdir) -I $(docdir)/include $(docdir)/gcc.texi
++
++gccint.dvi: $(TEXI_GCCINT_FILES)
++      $(TEXI2DVI) -I $(docdir) -I $(docdir)/include $(docdir)/gccint.texi
++
++gccinstall.dvi: $(TEXI_GCCINSTALL_FILES)
++      s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
++      $(TEXI2DVI) -I $$s/doc -I $$s/doc/include -o $@ $$s/doc/install.texi
++
++cppinternals.dvi: $(TEXI_CPPINT_FILES)
++      $(TEXI2DVI) -I $(docdir) -I $(docdir)/include $(docdir)/cppinternals.texi
++
++generated-manpages: $(docdir)/gcov.1 $(docdir)/cpp.1 $(docdir)/gcc.1 \
++       $(docdir)/gfdl.7 $(docdir)/gpl.7 $(docdir)/fsf-funding.7 \
++       lang.generated-manpages
++
++$(docdir)/gcov.1: $(docdir)/gcov.texi
++      $(STAMP) $(docdir)/gcov.1
++      -$(TEXI2POD) $(docdir)/gcov.texi > gcov.pod
++      -($(POD2MAN) --section=1 gcov.pod > $(docdir)/gcov.1.T$$$$ && \
++              mv -f $(docdir)/gcov.1.T$$$$ $(docdir)/gcov.1) || \
++              (rm -f $(docdir)/gcov.1.T$$$$ && exit 1)
++      -rm -f gcov.pod
++
++$(docdir)/cpp.1: $(docdir)/cpp.texi $(docdir)/cppenv.texi \
++  $(docdir)/cppopts.texi
++      $(STAMP) $(docdir)/cpp.1
++      -$(TEXI2POD) $(docdir)/cpp.texi > cpp.pod
++      -($(POD2MAN) --section=1 cpp.pod > $(docdir)/cpp.1.T$$$$ && \
++              mv -f $(docdir)/cpp.1.T$$$$ $(docdir)/cpp.1) || \
++              (rm -f $(docdir)/cpp.1.T$$$$ && exit 1)
++      -rm -f cpp.pod
++
++$(docdir)/gcc.1: $(docdir)/invoke.texi $(docdir)/cppenv.texi \
++  $(docdir)/cppopts.texi
++      $(STAMP) $(docdir)/gcc.1
++      -$(TEXI2POD) $(docdir)/invoke.texi > gcc.pod
++      -($(POD2MAN) --section=1 gcc.pod > $(docdir)/gcc.1.T$$$$ && \
++              mv -f $(docdir)/gcc.1.T$$$$ $(docdir)/gcc.1) || \
++              (rm -f $(docdir)/gcc.1.T$$$$ && exit 1)
++      -rm -f gcc.pod
++
++$(docdir)/gfdl.7: $(docdir)/include/fdl.texi
++      $(STAMP) $(docdir)/gfdl.7
++      -$(TEXI2POD) $(docdir)/include/fdl.texi > gfdl.pod
++      -($(POD2MAN) --section=7 gfdl.pod > $(docdir)/gfdl.7.T$$$$ && \
++              mv -f $(docdir)/gfdl.7.T$$$$ $(docdir)/gfdl.7) || \
++              (rm -f $(docdir)/gfdl.7.T$$$$ && exit 1)
++      -rm -f gfdl.pod
++
++$(docdir)/gpl.7: $(docdir)/include/gpl.texi
++      $(STAMP) $(docdir)/gpl.7
++      -$(TEXI2POD) $(docdir)/include/gpl.texi > gpl.pod
++      -($(POD2MAN) --section=7 gpl.pod > $(docdir)/gpl.7.T$$$$ && \
++              mv -f $(docdir)/gpl.7.T$$$$ $(docdir)/gpl.7) || \
++              (rm -f $(docdir)/gpl.7.T$$$$ && exit 1)
++      -rm -f gpl.pod
++
++$(docdir)/fsf-funding.7: $(docdir)/include/funding.texi
++      $(STAMP) $(docdir)/fsf-funding.7
++      -$(TEXI2POD) $(docdir)/include/funding.texi > fsf-funding.pod
++      -($(POD2MAN) --section=7 fsf-funding.pod \
++              > $(docdir)/fsf-funding.7.T$$$$ && \
++          mv -f $(docdir)/fsf-funding.7.T$$$$ $(docdir)/fsf-funding.7) || \
++          (rm -f $(docdir)/fsf-funding.7.T$$$$ && exit 1)
++      -rm -f fsf-funding.pod
++
++#\f
++# Deletion of files made during compilation.
++# There are four levels of this:
++#   `mostlyclean', `clean', `distclean' and `maintainer-clean'.
++# `mostlyclean' is useful while working on a particular type of machine.
++# It deletes most, but not all, of the files made by compilation.
++# It does not delete libgcc.a or its parts, so it won't have to be recompiled.
++# `clean' deletes everything made by running `make all'.
++# `distclean' also deletes the files made by config.
++# `maintainer-clean' also deletes everything that could be regenerated
++# automatically, except for `configure'.
++# We remove as much from the language subdirectories as we can
++# (less duplicated code).
++
++INTL_MOSTLYCLEAN = intl.mostlyclean
++mostlyclean: $(INTL_MOSTLYCLEAN) lang.mostlyclean
++      -rm -f $(STAGESTUFF)
++      -rm -f *$(coverageexts)
++      -rm -rf libgcc
++# Delete the temporary source copies for cross compilation.
++      -rm -f $(BUILD_PREFIX_1)rtl.c $(BUILD_PREFIX_1)print-rtl.c
++      -rm -f $(BUILD_PREFIX_1)bitmap.c $(BUILD_PREFIX_1)errors.c
++      -rm -f $(BUILD_PREFIX_1)ggc-none.c
++# Delete the temp files made in the course of building libgcc.a.
++      -rm -f xlimits.h
++# Delete other built files.
++      -rm -f xsys-protos.hT
++      -rm -f specs.h options.h gencheck.h
++# Delete the stamp and temporary files.
++      -rm -f s-* tmp-* stamp-* stmp-*
++      -rm -f */stamp-* */tmp-*
++# Delete debugging dump files.
++      -rm -f *.[0-9][0-9].* */*.[0-9][0-9].*
++# Delete some files made during installation.
++      -rm -f specs SYSCALLS.c.X SYSCALLS.c
++      -rm -f collect collect2 mips-tfile mips-tdump
++# Delete files generated for fixproto
++      -rm -rf fix-header$(build_exeext) xsys-protos.h deduced.h tmp-deduced.h \
++        gen-protos$(build_exeext) fixproto.list fixtmp.* fixhdr.ready
++# Delete files generated for fixincl
++      -rm -rf fixincl fixinc.sh specs.ready
++      (cd fixinc && $(MAKE) clean)
++# Delete unwanted output files from TeX.
++      -rm -f *.toc *.log *.vr *.fn *.cp *.tp *.ky *.pg
++      -rm -f */*.toc */*.log */*.vr */*.fn */*.cp */*.tp */*.ky */*.pg
++# Delete sorted indices we don't actually use.
++      -rm -f gcc.vrs gcc.kys gcc.tps gcc.pgs gcc.fns
++# Delete core dumps.
++      -rm -f core */core
++# Delete file generated for gengtype.c
++      -rm -f gtyp-gen.h
++# Delete files generated by gengtype.c
++      -rm -f gtype-*
++      -rm -f gt-*
++
++# Delete all files made by compilation
++# that don't exist in the distribution.
++INTL_CLEAN = intl.clean
++clean: mostlyclean $(INTL_CLEAN) lang.clean
++      -rm -f libgcc.a libgcc_eh.a libgcc_s$(SHLIB_EXT) libgcc_s$(SHLIB_EXT).1
++      -rm -f config.h tconfig.h hconfig.h tm_p.h
++      -rm -f cs-*
++      -rm -rf libgcc
++      -rm -f *.dvi
++      -rm -f */*.dvi
++# Delete the include directory.
++      -rm -rf include
++# Delete files used by the "multilib" facility (including libgcc subdirs).
++      -rm -f multilib.h tmpmultilib*
++      -if [ "x$(MULTILIB_DIRNAMES)" != x ] ; then \
++        rm -rf $(MULTILIB_DIRNAMES); \
++      else if [ "x$(MULTILIB_OPTIONS)" != x ] ; then \
++        rm -rf `echo $(MULTILIB_OPTIONS) | sed -e 's/\// /g'`; \
++      fi ; fi
++      -rm -fr stage1 stage2 stage3 stage4
++# Delete stamps of bootstrap stages
++      -rm -f stage?_*
++      -rm -f clean?_*
++      -rm -f stage_last
++
++# Delete all files that users would normally create
++# while building and installing GCC.
++INTL_DISTCLEAN = intl.distclean
++distclean: clean $(INTL_DISTCLEAN) lang.distclean
++      -rm -f auto-host.h auto-build.h
++      -rm -f cstamp-h
++      -rm -f config.status config.run config.cache config.bak
++      -rm -f Make-lang Make-hooks Make-host Make-target
++      -rm -f Makefile *.oaux
++      -rm -f gthr-default.h
++      -rm -f */stage1 */stage2 */stage3 */stage4 */include
++      -rm -f c-parse.output
++      -rm -f *.asm
++      -rm -f site.exp site.bak testsuite/site.exp testsuite/site.bak
++      -rm -f testsuite/*.log testsuite/*.sum
++      -cd testsuite && rm -f x *.x *.x? *.exe *.rpo *.o *.s *.S *.c
++      -cd testsuite && rm -f *.out *.gcov *.bb *.bbg
++      -rm -rf ${QMTEST_DIR} stamp-qmtest
++      -rm -f intl/libintl.h libintl.h
++      -rm -f cxxmain.c
++      -rm -f mklibgcc mkheaders gccbug .gdbinit configargs.h
++      -rm -f gcov.pod
++      -rm -f fixinc/Makefile
++# Delete po/*.gmo only if we are not building in the source directory.
++      -if [ ! -f po/exgettext ]; then rm -f po/*.gmo; fi
++      -rmdir ada cp f java objc fixinc intl po testsuite 2>/dev/null
++
++# Delete anything likely to be found in the source directory
++# that shouldn't be in the distribution.
++extraclean: distclean lang.extraclean
++      -rm -rf =* ./"#"* *~* config/=* config/"#"* config/*~*
++      -rm -f patch* *.orig *.rej config/patch* config/*.orig config/*.rej
++      -rm -f config/*/=* config/*/"#"* config/*/*~*
++      -rm -f config/*/*.orig config/*/*.rej
++      -rm -f *.dvi *.ps *.oaux *.d *.[zZ] *.gz
++      -rm -f *.tar *.xtar *diff *.diff.* *.tar.* *.xtar.* *diffs
++      -rm -f *lose config/*lose config/*/*lose
++      -rm -f *.s *.s[0-9] *.i config/ChangeLog
++      -rm -f y.tab.c yacc.*
++      -rm -f */=* */"#"* */*~*
++      -rm -f */patch* */*.orig */*.rej
++      -rm -f */*.dvi */*.oaux */*.d */*.[zZ] */*.gz
++      -rm -f */*.tar */*.xtar */*diff */*.diff.* */*.tar.* */*.xtar.* */*diffs
++      -rm -f */*lose */*.s */*.s[0-9] */*.i
++
++# Get rid of every file that's generated from some other file, except for `configure'.
++# Most of these files ARE PRESENT in the GCC distribution.
++# We define INTL_DISTCLEAN, INTL_CLEAN & INTL_MOSTLYCLEAN to be empty in the
++# submake, so that we don't descend into intl after its makefile has been
++# removed.
++maintainer-clean:
++      @echo 'This command is intended for maintainers to use; it'
++      @echo 'deletes files that may need special tools to rebuild.'
++      $(MAKE) INTL_DISTCLEAN= INTL_CLEAN= INTL_MOSTLYCLEAN= \
++              intl.maintainer-clean lang.maintainer-clean distclean
++      -rm -f c-parse.y c-parse.c c-parse.output TAGS
++      -rm -f cpp.??s cpp.*aux
++      -rm -f gcc.??s gcc.*aux
++      -rm -f $(docdir)/cpp.info* $(docdir)/gcc.info* $(docdir)/gccint.info*
++      -rm -f $(docdir)/cppinternals.info*
++      -rm -f $(docdir)/gcov.1 $(docdir)/cpp.1 $(docdir)/gcc.1
++      -rm -f $(docdir)/fsf-funding.7 $(docdir)/gfdl.7 $(docdir)/gpl.7
++#\f
++# Entry points `install' and `uninstall'.
++# Also use `install-collect2' to install collect2 when the config files don't.
++
++# Copy the compiler files into directories where they will be run.
++# Install the driver last so that the window when things are
++# broken is small.
++install: install-common $(INSTALL_HEADERS) $(INSTALL_LIBGCC) \
++    install-cpp install-man install-info intl.install install-@POSUB@ \
++    lang.install-normal install-driver
++
++# Handle cpp installation.
++install-cpp: cpp$(exeext)
++      -if [ -f gcc-cross$(exeext) ] ; then \
++        rm -f $(DESTDIR)$(bindir)/$(CPP_CROSS_NAME)$(exeext); \
++        $(INSTALL_PROGRAM) -m 755 cpp$(exeext) $(DESTDIR)$(bindir)/$(CPP_CROSS_NAME)$(exeext); \
++        if [ x$(cpp_install_dir) != x ]; then \
++          rm -f $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_CROSS_NAME)$(exeext); \
++          $(INSTALL_PROGRAM) -m 755 cpp$(exeext) $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_CROSS_NAME)$(exeext); \
++        else true; fi; \
++      else \
++        rm -f $(DESTDIR)$(bindir)/$(CPP_INSTALL_NAME)$(exeext); \
++        $(INSTALL_PROGRAM) -m 755 cpp$(exeext) $(DESTDIR)$(bindir)/$(CPP_INSTALL_NAME)$(exeext); \
++        if [ x$(cpp_install_dir) != x ]; then \
++          rm -f $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_INSTALL_NAME)$(exeext); \
++          $(INSTALL_PROGRAM) -m 755 cpp$(exeext) $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_INSTALL_NAME)$(exeext); \
++        else true; fi; \
++      fi
++
++# Create the installation directories.
++# $(libdir)/gcc-lib/include isn't currently searched by cpp.
++installdirs:
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(libsubdir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(includedir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(infodir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(slibdir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(man1dir)
++      $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(man7dir)
++
++# Install the compiler executables built during cross compilation.
++install-common: native $(EXTRA_PARTS) lang.install-common
++      for file in $(COMPILERS); do \
++        if [ -f $$file ] ; then \
++          rm -f $(DESTDIR)$(libsubdir)/$$file; \
++          $(INSTALL_PROGRAM) $$file $(DESTDIR)$(libsubdir)/$$file; \
++        else true; \
++        fi; \
++      done
++      for file in $(EXTRA_PASSES) $(EXTRA_PROGRAMS) $(USE_COLLECT2) ..; do \
++        if [ x"$$file" != x.. ]; then \
++          rm -f $(DESTDIR)$(libsubdir)/$$file; \
++          $(INSTALL_PROGRAM) $$file $(DESTDIR)$(libsubdir)/$$file; \
++        else true; fi; \
++      done
++      for file in $(EXTRA_PARTS) ..; do \
++        if [ x"$$file" != x.. ]; then \
++          rm -f $(DESTDIR)$(libsubdir)/$$file; \
++          $(INSTALL_DATA) $$file $(DESTDIR)$(libsubdir)/$$file; \
++          chmod a-x $(DESTDIR)$(libsubdir)/$$file; \
++        else true; fi; \
++      done
++# Don't mess with specs if it doesn't exist yet.
++      -if [ -f specs ] ; then \
++        rm -f $(DESTDIR)$(libsubdir)/specs; \
++        $(INSTALL_DATA) specs $(DESTDIR)$(libsubdir)/specs; \
++        chmod a-x $(DESTDIR)$(libsubdir)/specs; \
++      fi
++# Install protoize if it was compiled.
++      -if [ -f protoize$(exeext) ]; \
++      then \
++          if [ -f gcc-cross$(exeext) ] ; then \
++              rm -f $(DESTDIR)$(bindir)/$(PROTOIZE_CROSS_NAME)$(exeext); \
++              $(INSTALL_PROGRAM) protoize$(exeext) $(DESTDIR)$(bindir)/$(PROTOIZE_CROSS_NAME)$(exeext); \
++              rm -f $(DESTDIR)$(bindir)/$(UNPROTOIZE_CROSS_NAME)$(exeext); \
++              $(INSTALL_PROGRAM) unprotoize$(exeext) $(DESTDIR)$(bindir)/$(UNPROTOIZE_CROSS_NAME)$(exeext); \
++          else \
++              rm -f $(DESTDIR)$(bindir)/$(PROTOIZE_INSTALL_NAME)$(exeext); \
++              $(INSTALL_PROGRAM) protoize$(exeext) $(DESTDIR)$(bindir)/$(PROTOIZE_INSTALL_NAME)$(exeext); \
++              rm -f $(DESTDIR)$(bindir)/$(UNPROTOIZE_INSTALL_NAME)$(exeext); \
++              $(INSTALL_PROGRAM) unprotoize$(exeext) $(DESTDIR)$(bindir)/$(UNPROTOIZE_INSTALL_NAME)$(exeext); \
++          fi ; \
++          rm -f $(DESTDIR)$(libsubdir)/SYSCALLS.c.X; \
++          $(INSTALL_DATA) SYSCALLS.c.X $(DESTDIR)$(libsubdir)/SYSCALLS.c.X; \
++          chmod a-x $(DESTDIR)$(libsubdir)/SYSCALLS.c.X; \
++      fi
++# Install gcov if it was compiled.
++      -if [ -f gcov$(exeext) ]; \
++      then \
++          rm -f $(DESTDIR)$(bindir)/gcov$(exeext); \
++          $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
++      fi
++      $(INSTALL_SCRIPT) gccbug $(DESTDIR)$(bindir)/$(GCCBUG_INSTALL_NAME)
++
++# Install the driver program as $(target_alias)-gcc, 
++# $(target-alias)-gcc-$(version)
++# and also as either gcc (if native) or $(gcc_tooldir)/bin/gcc.
++install-driver: installdirs xgcc$(exeext)
++      -if [ -f gcc-cross$(exeext) ] ; then \
++        rm -f $(DESTDIR)$(bindir)/$(GCC_CROSS_NAME)$(exeext); \
++        $(INSTALL_PROGRAM) gcc-cross$(exeext) $(DESTDIR)$(bindir)/$(GCC_CROSS_NAME)$(exeext); \
++        rm -f $(DESTDIR)$(bindir)/$(target_alias)-gcc-$(version); \
++        ( cd $(DESTDIR)$(bindir) && \
++          $(LN) $(GCC_CROSS_NAME)$(exeext) $(target_alias)-gcc-$(version) ); \
++        if [ -d $(DESTDIR)$(gcc_tooldir)/bin/. ] ; then \
++          rm -f $(DESTDIR)$(gcc_tooldir)/bin/gcc$(exeext); \
++          $(INSTALL_PROGRAM) gcc-cross$(exeext) $(DESTDIR)$(gcc_tooldir)/bin/gcc$(exeext); \
++        else true; fi; \
++      else \
++        rm -f $(DESTDIR)$(bindir)/$(GCC_INSTALL_NAME)$(exeext); \
++        $(INSTALL_PROGRAM) xgcc$(exeext) $(DESTDIR)$(bindir)/$(GCC_INSTALL_NAME)$(exeext); \
++        rm -f $(DESTDIR)$(bindir)/$(target_alias)-gcc-$(version); \
++        ( cd $(DESTDIR)$(bindir) && \
++          $(LN) $(GCC_INSTALL_NAME)$(exeext) $(target_alias)-gcc-$(version) ); \
++        rm -f $(DESTDIR)$(bindir)/$(target_alias)-gcc-tmp$(exeext); \
++        ( cd $(DESTDIR)$(bindir) && \
++          $(LN) $(GCC_INSTALL_NAME)$(exeext) $(target_alias)-gcc-tmp$(exeext) && \
++          mv -f $(target_alias)-gcc-tmp$(exeext) $(GCC_TARGET_INSTALL_NAME)$(exeext) ); \
++      fi
++
++# Install the info files.
++# $(INSTALL_DATA) might be a relative pathname, so we can't cd into srcdir
++# to do the install.
++install-info: doc installdirs lang.install-info
++      -rm -f $(DESTDIR)$(infodir)/cpp.info* $(DESTDIR)$(infodir)/gcc.info*
++      -rm -f $(DESTDIR)$(infodir)/cppinternals.info* $(DESTDIR)$(infodir)/gccint.info*
++      if [ -f $(docdir)/gcc.info ]; then \
++        for f in $(docdir)/cpp.info* $(docdir)/gcc.info* \
++              $(docdir)/cppinternals.info* $(docdir)/gccint.info*; do \
++          realfile=`echo $$f | sed -e 's|.*/\([^/]*\)$$|\1|'`; \
++          $(INSTALL_DATA) $$f $(DESTDIR)$(infodir)/$$realfile; \
++        done; \
++      else true; fi
++      -if $(SHELL) -c 'install-info --version' >/dev/null 2>&1; then \
++        if [ -f $(DESTDIR)$(infodir)/dir ] ; then \
++          for f in cpp.info gcc.info gccint.info cppinternals.info; do \
++              if [ -f $(DESTDIR)$(infodir)/$$f ]; then \
++                install-info --dir-file=$(DESTDIR)$(infodir)/dir $(DESTDIR)$(infodir)/$$f; \
++              else true; fi; \
++          done; \
++        else true; fi; \
++      else true; fi;
++      -chmod a-x $(DESTDIR)$(infodir)/cpp.info* $(DESTDIR)$(infodir)/gcc.info*
++      -chmod a-x $(DESTDIR)$(infodir)/cppinternals.info* $(DESTDIR)$(infodir)/gccint.info*
++
++# Install the man pages.
++install-man: installdirs $(GENERATED_MANPAGES) lang.install-man
++      -if [ -f gcc-cross$(exeext) ] ; then \
++        rm -f $(DESTDIR)$(man1dir)/$(GCC_CROSS_NAME)$(man1ext); \
++        $(INSTALL_DATA) $(docdir)/gcc.1 $(DESTDIR)$(man1dir)/$(GCC_CROSS_NAME)$(man1ext); \
++        chmod a-x $(DESTDIR)$(man1dir)/$(GCC_CROSS_NAME)$(man1ext); \
++      else \
++        rm -f $(DESTDIR)$(man1dir)/$(GCC_INSTALL_NAME)$(man1ext); \
++        $(INSTALL_DATA) $(docdir)/gcc.1 $(DESTDIR)$(man1dir)/$(GCC_INSTALL_NAME)$(man1ext); \
++        chmod a-x $(DESTDIR)$(man1dir)/$(GCC_INSTALL_NAME)$(man1ext); \
++      fi
++      -rm -f $(DESTDIR)$(man1dir)/cpp$(man1ext)
++      -$(INSTALL_DATA) $(docdir)/cpp.1 $(DESTDIR)$(man1dir)/cpp$(man1ext)
++      -chmod a-x $(DESTDIR)$(man1dir)/cpp$(man1ext)
++      -rm -f $(DESTDIR)$(man1dir)/gcov$(man1ext)
++      -$(INSTALL_DATA) $(docdir)/gcov.1 $(DESTDIR)$(man1dir)/gcov$(man1ext)
++      -chmod a-x $(DESTDIR)$(man1dir)/gcov$(man1ext)
++      -rm -f $(DESTDIR)$(man7dir)/fsf-funding$(man7ext)
++      -$(INSTALL_DATA) $(docdir)/fsf-funding.7 $(DESTDIR)$(man7dir)/fsf-funding$(man7ext)
++      -chmod a-x $(DESTDIR)$(man7dir)/fsf-funding$(man7ext)
++      -rm -f $(DESTDIR)$(man7dir)/gfdl$(man7ext)
++      -$(INSTALL_DATA) $(docdir)/gfdl.7 $(DESTDIR)$(man7dir)/gfdl$(man7ext)
++      -chmod a-x $(DESTDIR)$(man7dir)/gfdl$(man7ext)
++      -rm -f $(DESTDIR)$(man7dir)/gpl$(man7ext)
++      -$(INSTALL_DATA) $(docdir)/gpl.7 $(DESTDIR)$(man7dir)/gpl$(man7ext)
++      -chmod a-x $(DESTDIR)$(man7dir)/gpl$(man7ext)
++
++# Install the library.
++install-libgcc: libgcc.mk libgcc.a installdirs
++      if $(RANLIB_TEST_FOR_TARGET); then \
++        r_f_t=$(RANLIB_FOR_TARGET); \
++      else \
++        r_f_t=: ; \
++      fi; \
++      $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \
++        BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \
++        AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++        AR_CREATE_FOR_TARGET="$(AR_CREATE_FOR_TARGET)" \
++        AR_FLAGS_FOR_TARGET="$(AR_FLAGS_FOR_TARGET)" \
++        CFLAGS="$(CFLAGS) $(WARN_CFLAGS)" \
++        RANLIB_TEST_FOR_TARGET="$(RANLIB_TEST_FOR_TARGET)" \
++        NM_FOR_TARGET="$(NM_FOR_TARGET)" AWK="$(AWK)" \
++        LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" \
++        INCLUDES="$(INCLUDES)" \
++        CONFIG_H="$(TCONFIG_H)" MACHMODE_H="$(MACHMODE_H)" \
++        LIB1ASMSRC='$(LIB1ASMSRC)' \
++        MAKEOVERRIDES= \
++        INSTALL_DATA="$(INSTALL_DATA)" \
++        RANLIB_FOR_TARGET="$$r_f_t" \
++        DESTDIR="$(DESTDIR)" \
++        libsubdir="$(libsubdir)" \
++        slibdir="$(slibdir)" \
++        -f libgcc.mk install
++
++# Install multiple versions of libgcc.a.
++install-multilib: stmp-multilib installdirs
++      if $(RANLIB_TEST_FOR_TARGET); then \
++        r_f_t=$(RANLIB_FOR_TARGET); \
++      else \
++        r_f_t=: ; \
++      fi; \
++      $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \
++        BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \
++        AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++        AR_CREATE_FOR_TARGET="$(AR_CREATE_FOR_TARGET)" \
++        AR_FLAGS_FOR_TARGET="$(AR_FLAGS_FOR_TARGET)" \
++        CFLAGS="$(CFLAGS) $(WARN_CFLAGS)" \
++        RANLIB_TEST_FOR_TARGET="$(RANLIB_TEST_FOR_TARGET)" \
++        NM_FOR_TARGET="$(NM_FOR_TARGET)" AWK="$(AWK)" \
++        LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" \
++        INCLUDES="$(INCLUDES)" \
++        CONFIG_H="$(CONFIG_H)" MACHMODE_H="$(MACHMODE_H)" \
++        LIB1ASMSRC='$(LIB1ASMSRC)' \
++        MAKEOVERRIDES= \
++        INSTALL_DATA="$(INSTALL_DATA)" \
++        RANLIB_FOR_TARGET="$$r_f_t" \
++        DESTDIR="$(DESTDIR)" \
++        libsubdir="$(libsubdir)" \
++        slibdir="$(slibdir)" \
++        -f libgcc.mk install
++
++# Install all the header files built in the include subdirectory.
++install-headers: $(INSTALL_HEADERS_DIR)
++# Fix symlinks to absolute paths in the installed include directory to
++# point to the installed directory, not the build directory.
++# Don't need to use LN_S here since we really do need ln -s and no substitutes.
++      -files=`cd $(DESTDIR)$(libsubdir)/include; find . -type l -print 2>/dev/null`; \
++      if [ $$? -eq 0 ]; then \
++        dir=`cd include; ${PWD_COMMAND}`; \
++        for i in $$files; do \
++          dest=`ls -ld $(DESTDIR)$(libsubdir)/include/$$i | sed -n 's/.*-> //p'`; \
++          if expr "$$dest" : "$$dir.*" > /dev/null; then \
++            rm -f $(DESTDIR)$(libsubdir)/include/$$i; \
++            ln -s `echo $$i | sed "s|/[^/]*|/..|g" | sed 's|/..$$||'``echo "$$dest" | sed "s|$$dir||"` $(DESTDIR)$(libsubdir)/include/$$i; \
++          fi; \
++        done; \
++      fi
++
++# Create or recreate the gcc private include file directory.
++install-include-dir: installdirs
++      -rm -rf $(DESTDIR)$(libsubdir)/include
++      mkdir $(DESTDIR)$(libsubdir)/include
++      -chmod a+rx $(DESTDIR)$(libsubdir)/include
++
++# Install the include directory using tar.
++install-headers-tar: stmp-int-hdrs $(STMP_FIXPROTO) install-include-dir
++# We use `pwd`/include instead of just include to problems with CDPATH
++# Unless a full pathname is provided, some shells would print the new CWD,
++# found in CDPATH, corrupting the output.  We could just redirect the
++# output of `cd', but some shells lose on redirection within `()'s
++      (cd `${PWD_COMMAND}`/include ; \
++       tar -cf - .; exit 0) | (cd $(DESTDIR)$(libsubdir)/include; tar xpf - )
++# /bin/sh on some systems returns the status of the first tar,
++# and that can lose with GNU tar which always writes a full block.
++# So use `exit 0' to ignore its exit status.
++
++# Install the include directory using cpio.
++install-headers-cpio: stmp-int-hdrs $(STMP_FIXPROTO) install-include-dir
++# See discussion about the use of `pwd` above
++      cd `${PWD_COMMAND}`/include ; \
++      find . -print | cpio -pdum $(DESTDIR)$(libsubdir)/include
++
++# Install the include directory using cp.
++install-headers-cp: stmp-int-hdrs $(STMP_FIXPROTO) install-include-dir
++      cp -p -r include $(DESTDIR)$(libsubdir)
++
++itoolsdir = $(libsubdir)/install-tools
++# Don't install the headers.  Instead, install appropriate scripts
++# and supporting files for fixincludes to be run later.
++install-mkheaders: stmp-int-hdrs $(STMP_FIXPROTO) install-include-dir \
++    mkheaders xlimits.h
++      -rm -rf $(DESTDIR)$(itoolsdir)
++      $(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$(itoolsdir)/include
++      for file in $(USER_H); do \
++        realfile=`echo $$file | sed -e 's|.*/\([^/]*\)$$|\1|'`; \
++        $(INSTALL_DATA) $$file \
++          $(DESTDIR)$(itoolsdir)/include/$$realfile ; \
++      done
++      $(INSTALL_DATA) xlimits.h $(DESTDIR)$(itoolsdir)/include/limits.h
++      if [ x$(STMP_FIXINC) != x ] ; then \
++        $(INSTALL_DATA) $(srcdir)/README-fixinc \
++          $(DESTDIR)$(itoolsdir)/include/README ; \
++        $(INSTALL_PROGRAM) fixinc.sh $(DESTDIR)$(itoolsdir)/fixinc.sh ; \
++        $(INSTALL_PROGRAM) fixinc/fixincl $(DESTDIR)$(itoolsdir)/fixincl ; \
++        $(INSTALL_DATA) $(srcdir)/gsyslimits.h $(DESTDIR)$(itoolsdir)/gsyslimits.h ; \
++      else :; fi
++      if [ x$(STMP_FIXPROTO) != x ] ; then \
++        $(INSTALL_PROGRAM) $(srcdir)/mkinstalldirs \
++              $(DESTDIR)$(itoolsdir)/mkinstalldirs ; \
++        $(INSTALL_PROGRAM) $(srcdir)/fixproto $(DESTDIR)$(itoolsdir)/fixproto ; \
++        $(INSTALL_PROGRAM) fix-header$(build_exeext) \
++              $(DESTDIR)$(itoolsdir)/fix-header$(build_exeext) ; \
++      else :; fi
++      $(INSTALL_PROGRAM) mkheaders $(DESTDIR)$(itoolsdir)/mkheaders
++      echo 'SYSTEM_HEADER_DIR="$(SYSTEM_HEADER_DIR)"' \
++              > $(DESTDIR)$(itoolsdir)/mkheaders.conf
++      echo 'OTHER_FIXINCLUDES_DIRS="$(OTHER_FIXINCLUDES_DIRS)"' \
++              >> $(DESTDIR)$(itoolsdir)/mkheaders.conf
++      echo 'FIXPROTO_DEFINES="$(FIXPROTO_DEFINES)"' \
++              >> $(DESTDIR)$(itoolsdir)/mkheaders.conf
++      echo 'STMP_FIXPROTO="$(STMP_FIXPROTO)"' >> $(DESTDIR)$(itoolsdir)/mkheaders.conf
++      echo 'STMP_FIXINC="$(STMP_FIXINC)"' >> $(DESTDIR)$(itoolsdir)/mkheaders.conf
++
++# Use this target to install the program `collect2' under the name `collect2'.
++install-collect2: collect2 installdirs
++      $(INSTALL_PROGRAM) collect2$(exeext) $(DESTDIR)$(libsubdir)/collect2$(exeext)
++# Install the driver program as $(libsubdir)/gcc for collect2.
++      $(INSTALL_PROGRAM) xgcc$(exeext) $(DESTDIR)$(libsubdir)/gcc$(exeext)
++
++# Cancel installation by deleting the installed files.
++uninstall: intl.uninstall lang.uninstall
++      -rm -rf $(DESTDIR)$(libsubdir)
++      -rm -rf $(DESTDIR)$(bindir)/$(GCC_INSTALL_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(bindir)/$(GCC_CROSS_NAME)$(exeext)
++      -rm -f $(DESTDIR)$(bindir)/$(CPP_INSTALL_NAME)$(exeext)
++      -rm -f $(DESTDIR)$(bindir)/$(CPP_CROSS_NAME)$(exeext)
++      -if [ x$(cpp_install_dir) != x ]; then \
++        rm -f $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_INSTALL_NAME)$(exeext); \
++        rm -f $(DESTDIR)$(prefix)/$(cpp_install_dir)/$(CPP_CROSS_NAME)$(exeext); \
++      else true; fi
++      -rm -rf $(DESTDIR)$(bindir)/$(PROTOIZE_INSTALL_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(bindir)/$(PROTOIZE_CROSS_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(bindir)/$(UNPROTOIZE_INSTALL_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(bindir)/$(UNPROTOIZE_CROSS_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext)
++      -rm -rf $(DESTDIR)$(man1dir)/$(GCC_INSTALL_NAME)$(man1ext)
++      -rm -rf $(DESTDIR)$(man1dir)/$(GCC_CROSS_NAME)$(man1ext)
++      -rm -rf $(DESTDIR)$(man1dir)/cpp$(man1ext)
++      -rm -rf $(DESTDIR)$(man1dir)/protoize$(man1ext)
++      -rm -rf $(DESTDIR)$(man1dir)/unprotoize$(man1ext)
++      -rm -f $(DESTDIR)$(infodir)/cpp.info* $(DESTDIR)$(infodir)/gcc.info*
++      -rm -f $(DESTDIR)$(infodir)/cppinternals.info* $(DESTDIR)$(infodir)/gccint.info*
++#\f
++# These targets are for the dejagnu testsuites. The file site.exp
++# contains global variables that all the testsuites will use.
++
++# Set to $(target_alias)/ for cross.
++target_subdir = @target_subdir@
++
++site.exp: ./config.status Makefile
++      @echo "Making a new config file..."
++      -@rm -f ./tmp?
++      @$(STAMP) site.exp
++      -@mv site.exp site.bak
++      @echo "## these variables are automatically generated by make ##" > ./tmp0
++      @echo "# Do not edit here. If you wish to override these values" >> ./tmp0
++      @echo "# add them to the last section" >> ./tmp0
++      @echo "set rootme \"`${PWD_COMMAND}`\"" >> ./tmp0
++      @echo "set srcdir \"`cd ${srcdir}; ${PWD_COMMAND}`\"" >> ./tmp0
++      @echo "set host_triplet $(host_canonical)" >> ./tmp0
++      @echo "set build_triplet $(build_canonical)" >> ./tmp0
++      @echo "set target_triplet $(target)" >> ./tmp0
++      @echo "set target_alias $(target_alias)" >> ./tmp0
++# CFLAGS is set even though it's empty to show we reserve the right to set it.
++      @echo "set CFLAGS \"\"" >> ./tmp0
++      @echo "set CXXFLAGS \"-I$(objdir)/../$(target_subdir)libio -I\$$srcdir/../libg++/src -I\$$srcdir/../libio -I\$$srcdir/../libstdc++ -I\$$srcdir/../libstdc++/stl -L$(objdir)/../$(target_subdir)libg++ -L$(objdir)/../$(target_subdir)libstdc++\"" >> ./tmp0
++      @echo "set TESTING_IN_BUILD_TREE 1" >> ./tmp0
++      @echo "set HAVE_LIBSTDCXX_V3 1" >> ./tmp0
++# If newlib has been configured, we need to pass -B to gcc so it can find
++# newlib's crt0.o if it exists.  This will cause a "path prefix not used"
++# message if it doesn't, but the testsuite is supposed to ignore the message -
++# it's too difficult to tell when to and when not to pass -B (not all targets
++# have crt0's).  We could only add the -B if ../newlib/crt0.o exists, but that
++# seems like too selective a test.
++# ??? Another way to solve this might be to rely on linker scripts.  Then
++# theoretically the -B won't be needed.
++# We also need to pass -L ../ld so that the linker can find ldscripts.
++      @if [ -d $(objdir)/../$(target_subdir)newlib ] ; then \
++        echo "set newlib_cflags \"-I$(objdir)/../$(target_subdir)newlib/targ-include -I\$$srcdir/../newlib/libc/include\"" >> ./tmp0; \
++        echo "set newlib_ldflags \"-B$(objdir)/../$(target_subdir)newlib/\"" >> ./tmp0; \
++        echo "append CFLAGS \" \$$newlib_cflags\"" >> ./tmp0; \
++        echo "append CXXFLAGS \" \$$newlib_cflags\"" >> ./tmp0; \
++        echo "append LDFLAGS \" \$$newlib_ldflags\"" >> ./tmp0; \
++      else true; \
++      fi
++      @if [ -d $(objdir)/../ld ] ; then \
++        echo "append LDFLAGS \" -L$(objdir)/../ld\"" >> ./tmp0; \
++      else true; \
++      fi
++      echo "set tmpdir $(objdir)/testsuite" >> ./tmp0
++      @echo "set srcdir \"\$${srcdir}/testsuite\"" >> ./tmp0
++      @if [ "X$(ALT_CXX_UNDER_TEST)" != "X" ] ; then \
++        echo "set ALT_CXX_UNDER_TEST $(ALT_CXX_UNDER_TEST)" >> ./tmp0; \
++      else true; \
++      fi
++      @if [ "X$(COMPAT_OPTIONS)" != "X" ] ; then \
++        echo "set COMPAT_OPTIONS $(COMPAT_OPTIONS)" >> ./tmp0; \
++      else true; \
++      fi
++      @echo "## All variables above are generated by configure. Do Not Edit ##" >> ./tmp0
++      @cat ./tmp0 > site.exp
++      @cat site.bak | sed \
++              -e '1,/^## All variables above are.*##/ d' >> site.exp
++      -@rm -f ./tmp?
++
++CHECK_TARGETS = check-gcc @check_languages@
++
++check-c++ : check-g++
++check-f77 : check-g77
++check-java :
++
++check: $(CHECK_TARGETS)
++
++# The idea is to parallelize testing of multilibs, for example:
++#   make -j3 check-gcc//sh-hms-sim/{-m1,-m2,-m3,-m3e,-m4}/{,-nofpu}
++# will run 3 concurrent sessions of check-gcc, eventually testing
++# all 10 combinations.  GNU make is required, as is a shell that expands
++# alternations within braces.
++check-gcc//% check-g++//% check-g77//% check-objc//%: site.exp
++      target=`echo "$@" | sed 's,//.*,,'`; \
++      variant=`echo "$@" | sed 's,^[^/]*//,,'`; \
++      vardots=`echo "$$variant" | sed 's,/,.,g'`; \
++      $(MAKE) TESTSUITEDIR="testsuite.$$vardots" \
++        RUNTESTFLAGS="--target_board=$$variant $(RUNTESTFLAGS)" \
++        "$$target"
++
++TESTSUITEDIR = testsuite
++
++$(TESTSUITEDIR)/site.exp: site.exp
++      test -d $(TESTSUITEDIR) || mkdir $(TESTSUITEDIR)
++      -rm -f $@
++      sed '/set tmpdir/ s|testsuite|$(TESTSUITEDIR)|' < site.exp > $@
++
++check-g++: $(TESTSUITEDIR)/site.exp
++      -(rootme=`${PWD_COMMAND}`; export rootme; \
++      srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \
++      cd $(TESTSUITEDIR); \
++      EXPECT=${EXPECT} ; export EXPECT ; \
++      if [ -f $${rootme}/../expect/expect ] ; then  \
++         TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWD_COMMAND}` ; \
++          export TCL_LIBRARY ; fi ; \
++      $(RUNTEST) --tool g++ $(RUNTESTFLAGS))
++
++check-gcc: $(TESTSUITEDIR)/site.exp
++      -(rootme=`${PWD_COMMAND}`; export rootme; \
++      srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \
++      cd $(TESTSUITEDIR); \
++      EXPECT=${EXPECT} ; export EXPECT ; \
++      if [ -f $${rootme}/../expect/expect ] ; then  \
++         TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWD_COMMAND}` ; \
++         export TCL_LIBRARY ; fi ; \
++      $(RUNTEST) --tool gcc $(RUNTESTFLAGS))
++
++check-g77: $(TESTSUITEDIR)/site.exp
++      -(rootme=`${PWD_COMMAND}`; export rootme; \
++      srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \
++      cd $(TESTSUITEDIR); \
++      EXPECT=${EXPECT} ; export EXPECT ; \
++      if [ -f $${rootme}/../expect/expect ] ; then  \
++         TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWD_COMMAND}` ; \
++         export TCL_LIBRARY ; fi ; \
++      $(RUNTEST) --tool g77 $(RUNTESTFLAGS))
++
++check-objc: $(TESTSUITEDIR)/site.exp
++      -(rootme=`${PWD_COMMAND}`; export rootme; \
++      srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \
++      cd $(TESTSUITEDIR); \
++      EXPECT=${EXPECT} ; export EXPECT ; \
++      if [ -f $${rootme}/../expect/expect ] ; then  \
++         TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWD_COMMAND}` ; \
++          export TCL_LIBRARY ; fi ; \
++      $(RUNTEST) --tool objc $(RUNTESTFLAGS))
++
++check-consistency: testsuite/site.exp
++      -rootme=`${PWD_COMMAND}`; export rootme; \
++      srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \
++      cd testsuite; \
++      EXPECT=${EXPECT} ; export EXPECT ; \
++      if [ -f $${rootme}/../expect/expect ] ; then  \
++         TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWD_COMMAND}` ; \
++         export TCL_LIBRARY ; fi ; \
++      $(RUNTEST) --tool consistency $(RUNTESTFLAGS)
++
++# QMTest targets
++
++# The path to qmtest.
++QMTEST_PATH=qmtest
++
++# The flags to pass to qmtest.
++QMTESTFLAGS=
++
++# The flags to pass to "qmtest run".
++QMTESTRUNFLAGS=
++
++# The command to use to invoke qmtest.
++QMTEST=${QMTEST_PATH} ${QMTESTFLAGS}
++
++# The tests (or suites) to run.
++QMTEST_GPP_TESTS=gpp
++
++# The subdirectory of the OBJDIR that will be used to store the QMTest
++# test database configuration and that will be used for temporary
++# scratch space during QMTest's execution.
++QMTEST_DIR=qmtestsuite
++
++# Create the QMTest database configuration.
++${QMTEST_DIR} stamp-qmtest:
++      debug_options=""; \
++          ${STAMP} empty.C; \
++          for option in \
++              -gdwarf-2 -gstabs -gstabs+ -gxcoff -gxcoff+ -gcoff; do \
++              (./cc1plus -q $${option} empty.C 2>&1 | \
++                   grep "unknown or unsupported -g option" > /dev/null) || \
++              debug_options="$${debug_options}$${option} "; done; \
++          ${QMTEST} -D ${QMTEST_DIR} create-tdb \
++              -c gcc_database.GCCDatabase \
++              -a GCCDatabase.testsuite_root=`cd ${srcdir}/testsuite && pwd` \
++              -a GCCDatabase.debug_options="$${debug_options}"
++      rm -f empty.C empty.s
++      $(STAMP) stamp-qmtest
++
++# Create the QMTest context file.
++${QMTEST_DIR}/context: stamp-qmtest
++      echo "GCCTest.flags=-B${objdir}" >> $@
++      echo "GCCTest.objdir=${objdir}/.." >> $@
++      echo "GCCTest.host=${host_canonical}" >> $@
++      echo "GCCTest.target=${target}" >> $@
++      echo "GCCTest.gcov=${objdir}/gcov" >> $@
++      echo "GPPTest.gpp=${objdir}/g++" >> $@
++      echo "DGTest.demangler=${objdir}/c++filt" >> $@
++
++# Run the G++ testsuite using QMTest.
++qmtest-g++: ${QMTEST_DIR}/context ${QMTEST_DIR}/gpp-expected.qmr
++      cd ${QMTEST_DIR} && ${QMTEST} run ${QMTESTRUNFLAGS} -C context \
++         -o gpp.qmr -O gpp-expected.qmr \
++         ${QMTEST_GPP_TESTS}
++
++# Use the QMTest GUI.
++qmtest-gui: ${QMTEST_DIR}/context
++      cd ${QMTEST_DIR} && ${QMTEST} gui -C context
++
++# Build the set of expected G++ failures.
++${QMTEST_DIR}/gpp-expected.qmr: ${QMTEST_DIR}/context
++      echo "Determining expected results..."
++      cd ${QMTEST_DIR} && ${QMTEST} run ${QMTESTRUNFLAGS} -C context \
++           -c "GCCTest.generate_xfails=1" -o gpp-expected.qmr \
++         ${QMTEST_GPP_TESTS} \
++         > /dev/null
++
++.PHONY: qmtest-g++
++
++# Run Paranoia on real.c.
++
++paranoia.o: $(srcdir)/../contrib/paranoia.cc $(CONFIG_H) $(SYSTEM_H) \
++  real.h $(TREE_H)
++      g++ -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
++
++paranoia: paranoia.o real.o $(LIBIBERTY)
++      g++ -o $@ paranoia.o real.o $(LIBIBERTY)
++
++# These exist for maintenance purposes.
++
++# Update the tags table.
++TAGS: force
++      (cd $(srcdir);                                                  \
++      mkdir tmp-tags;                                                 \
++      mv -f c-parse.[ch] =*.[chy] tmp-tags;                           \
++      etags *.y *.h *.c;                                              \
++      mv tmp-tags/* .;                                                \
++      rmdir tmp-tags)
++
++# A list of files to be destroyed during "lean" builds.
++VOL_FILES=`echo $(BACKEND) $(OBJS) $(C_OBJS) $(LIBCPP_OBJS) *.c *.h gen*`
++
++# Flags to pass to stage2 and later recursive makes.  Note that the
++# WARN_CFLAGS setting can't be to the expansion of GCC_WARN_CFLAGS in
++# the context of the stage_x rule.
++STAGE2_FLAGS_TO_PASS = \
++      ADAC="\$$(CC)" \
++      AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++      RANLIB_FOR_TARGET="$(RANLIB_FOR_TARGET)" \
++      CFLAGS="$(BOOT_CFLAGS)" \
++      LDFLAGS="$(BOOT_LDFLAGS)" \
++      WARN_CFLAGS="\$$(GCC_WARN_CFLAGS)" \
++      STRICT_WARN="$(STRICT2_WARN)" \
++      libdir=$(libdir) \
++      LANGUAGES="$(LANGUAGES)" \
++      MAKEOVERRIDES= \
++      OUTPUT_OPTION="-o \$$@"
++
++# Only build the C compiler for stage1, because that is the only one that
++# we can guarantee will build with the native compiler, and also it is the
++# only thing useful for building stage2. STAGE1_CFLAGS (via CFLAGS),
++# MAKEINFO and MAKEINFOFLAGS are explicitly passed here to make them
++# overrideable (for a bootstrap build stage1 also builds gcc.info).
++stage1_build:
++      $(MAKE) AR_FOR_TARGET="$(AR_FOR_TARGET)" \
++              RANLIB_FOR_TARGET="$(RANLIB_FOR_TARGET)" \
++              CC="$(CC)" libdir=$(libdir) LANGUAGES="$(BOOT_LANGUAGES)" \
++              CFLAGS="$(STAGE1_CFLAGS)" MAKEINFO="$(MAKEINFO)" \
++              MAKEINFOFLAGS="$(MAKEINFOFLAGS)" COVERAGE_FLAGS=
++      $(STAMP) stage1_build
++      echo stage1_build > stage_last
++
++stage1_copy: stage1_build
++      $(MAKE) stage1
++      $(STAMP) stage1_copy
++      echo stage2_build > stage_last
++
++stage2_build: stage1_copy
++      $(MAKE) CC="$(STAGE_CC_WRAPPER) stage1/xgcc$(exeext) -Bstage1/ -B$(build_tooldir)/bin/" \
++               STAGE_PREFIX=stage1/ \
++               $(STAGE2_FLAGS_TO_PASS)
++      $(STAMP) stage2_build
++      echo stage2_build > stage_last
++
++stage2_copy: stage2_build
++      $(MAKE) stage2
++      $(STAMP) stage2_copy
++      echo stage3_build > stage_last
++
++stage3_build: stage2_copy
++      $(MAKE) CC="$(STAGE_CC_WRAPPER) stage2/xgcc$(exeext) -Bstage2/ -B$(build_tooldir)/bin/" \
++               STAGE_PREFIX=stage2/ \
++               $(STAGE2_FLAGS_TO_PASS)
++      $(STAMP) stage3_build
++      echo stage3_build > stage_last
++
++# For bootstrap4:
++stage3_copy: stage3_build
++      $(MAKE) stage3
++      $(STAMP) stage3_copy
++      echo stage4_build > stage_last
++
++stage4_build: stage3_copy
++      $(MAKE) CC="$(STAGE_CC_WRAPPER) stage3/xgcc$(exeext) -Bstage3/ -B$(build_tooldir)/bin/" \
++               STAGE_PREFIX=stage3/ \
++               $(STAGE2_FLAGS_TO_PASS)
++      $(STAMP) stage4_build
++      echo stage4_build > stage_last
++
++# Additional steps for *-lean targets:
++clean_s1: stage1_copy
++      -(cd stage1 && rm -f $(VOL_FILES))
++      $(STAMP) clean_s1
++
++clean_s2: stage2_copy
++      -rm -rf stage1
++      $(STAMP) clean_s2
++
++# The various entry points for bootstrapping.
++
++bootstrap: stage3_build
++      @echo
++      @echo Bootstrap complete - make \"quickstrap\" to redo last build,
++      @echo \"restage1\" through \"restage3\" to rebuild specific stages,
++      @echo \"restrap\" to redo the bootstrap from stage1, or
++      @echo \"cleanstrap\" to redo the bootstrap from scratch.
++
++bootstrap-lean : clean_s1 clean_s2 stage3_build
++      @echo
++      @echo Bootstrap complete - make \"quickstrap\" to redo last build,
++      @echo or \"cleanstrap\" to redo the bootstrap from scratch.
++
++bootstrap2: bootstrap
++
++bootstrap2-lean : bootstrap-lean
++
++bootstrap3 bootstrap3-lean: bootstrap
++
++bootstrap4 bootstrap4-lean: stage4_build
++
++unstage1 unstage2 unstage3 unstage4:
++      -set -vx; stage=`echo $@ | sed -e 's/un//'`; \
++      rm -f $$stage/as$(exeext); \
++      rm -f $$stage/ld$(exeext); \
++      rm -f $$stage/collect-ld$(exeext); \
++      if test -d $$stage; then \
++        mv $$stage/* . 2>/dev/null; \
++        for i in `cd $$stage; echo *` ; do \
++          if test -d $$stage/$$i; then \
++            mv $$stage/$$i/* $$i/. 2>/dev/null; \
++          else \
++            mv $$stage/$$i .; \
++          fi; \
++        done \
++      fi ; \
++      rm -f $${stage}_build $${stage}_copy ;\
++      echo $${stage}_build > stage_last
++
++restage1: unstage1
++      $(MAKE) stage1_build
++
++restage2: unstage2
++      $(MAKE) LANGUAGES="$(LANGUAGES)" stage2_build
++
++restage3: unstage3
++      $(MAKE) LANGUAGES="$(LANGUAGES)" stage3_build
++
++restage4: unstage4
++      $(MAKE) LANGUAGES="$(LANGUAGES)" stage4_build
++
++bubblestrap:
++      if test -f stage3_build; then true; else \
++        echo; echo You must \"make bootstrap\" first.; \
++        exit 1; \
++      fi
++      for i in stage3 \
++              unstage1 stage1_build stage1_copy \
++              unstage2 stage2_build stage2_copy \
++              unstage3 stage3_build ; \
++      do \
++        $(MAKE) LANGUAGES="$(LANGUAGES)" $$i || exit 1 ; \
++      done
++
++quickstrap:
++      if test -f stage_last ; then \
++        LAST=`cat stage_last`; rm $$LAST; $(MAKE) LANGUAGES="$(LANGUAGES)" $$LAST; \
++      else \
++        $(MAKE) stage1_build; \
++      fi
++
++cleanstrap:
++      -$(MAKE) clean
++      $(MAKE) LANGUAGES="$(LANGUAGES)" bootstrap
++
++unstrap:
++      -rm -rf stage[234]*
++      $(MAKE) unstage1
++
++# Differs from cleanstrap in that it starts from the earlier stage1 build,
++# not from scratch.
++restrap:
++      $(MAKE) unstrap
++      $(MAKE) LANGUAGES="$(LANGUAGES)" bootstrap
++
++# Compare the object files in the current directory with those in the
++# stage2 directory.
++
++# ./ avoids bug in some versions of tail.
++compare compare3 compare4 compare-lean compare3-lean compare4-lean: force
++      -rm -f .bad_compare
++      case "$@" in compare | compare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^compare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      for file in *$(objext); do \
++        tail +16c ./$$file > tmp-foo1; \
++        tail +16c stage$$stage/$$file > tmp-foo2 \
++          && (cmp tmp-foo1 tmp-foo2 > /dev/null 2>&1 || echo $$file differs >> .bad_compare) || true; \
++      done
++      case "$@" in compare | compare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^compare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      for dir in tmp-foo intl $(SUBDIRS); do \
++        if [ "`echo $$dir/*$(objext)`" != "$$dir/*$(objext)" ] ; then \
++          for file in $$dir/*$(objext); do \
++            tail +16c ./$$file > tmp-foo1; \
++            tail +16c stage$$stage/$$file > tmp-foo2 \
++              && (cmp tmp-foo1 tmp-foo2 > /dev/null 2>&1 || echo $$file differs >> .bad_compare) || true; \
++          done; \
++        else true; fi; \
++      done
++      -rm -f tmp-foo*
++      case "$@" in compare | compare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^compare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      if [ -f .bad_compare ]; then \
++        echo "Bootstrap comparison failure!"; \
++        cat .bad_compare; \
++        exit 1; \
++      else \
++        case "$@" in \
++          *-lean ) rm -rf stage$$stage ;; \
++          *) ;; \
++        esac; true; \
++      fi
++
++# Compare the object files in the current directory with those in the
++# stage2 directory.  Use gnu cmp (diffutils v2.4 or later) to avoid
++# running tail and the overhead of twice copying each object file.
++# An exit status of 1 is precisely the result we're looking for (other
++# values mean other problems).
++gnucompare gnucompare3 gnucompare4 gnucompare-lean gnucompare3-lean gnucompare4-lean: force
++      -rm -f .bad_compare
++      case "$@" in gnucompare | gnucompare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^gnucompare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      for file in *$(objext); do \
++        cmp --ignore-initial=16 $$file stage$$stage/$$file > /dev/null 2>&1; \
++        test $$? -eq 1 && echo $$file differs >> .bad_compare || true; \
++      done
++      case "$@" in gnucompare | gnucompare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^gnucompare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      for dir in tmp-foo intl $(SUBDIRS); do \
++        if [ "`echo $$dir/*$(objext)`" != "$$dir/*$(objext)" ] ; then \
++          for file in $$dir/*$(objext); do \
++            cmp --ignore-initial=16 $$file stage$$stage/$$file > /dev/null 2>&1; \
++            test $$? -eq 1 && echo $$file differs >> .bad_compare || true; \
++          done; \
++        else true; fi; \
++      done
++      case "$@" in gnucompare | gnucompare-lean ) stage=2 ;; * ) stage=`echo $@ | sed -e 's,^gnucompare\([0-9][0-9]*\).*,\1,'` ;; esac; \
++      if [ -f .bad_compare ]; then \
++        echo "Bootstrap comparison failure!"; \
++        cat .bad_compare; \
++        exit 1; \
++      else \
++        case "$@" in \
++          *-lean ) rm -rf stage$$stage ;; \
++        esac; true; \
++      fi
++
++# Copy the object files from a particular stage into a subdirectory.
++stage1-start:
++      -if [ -d stage1 ] ; then true ; else mkdir stage1 ; fi
++      $(MAKE) -f libgcc.mk libgcc-stage-start stage=stage1
++      -for dir in intl $(SUBDIRS) ; \
++       do \
++         if [ -d stage1/$$dir ] ; then true ; else mkdir stage1/$$dir ; fi ; \
++       done
++      -mv $(STAGESTUFF) stage1
++      -mv intl/*$(objext) stage1/intl
++# Copy as/ld if they exist to stage dir, so that running xgcc from the stage
++# dir will work properly.
++      -if [ -f as$(exeext) ] ; then (cd stage1 && $(LN_S) ../as$(exeext) .) ; else true ; fi
++      -if [ -f ld$(exeext) ] ; then (cd stage1 && $(LN_S) ../ld$(exeext) .) ; else true ; fi
++      -if [ -f collect-ld$(exeext) ] ; then (cd stage1 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi
++      -rm -f stage1/libgcc.a stage1/libgcc_eh.a
++      -cp libgcc.a stage1
++      -if $(RANLIB_TEST_FOR_TARGET) ; then \
++        $(RANLIB_FOR_TARGET) stage1/libgcc.a; \
++      else true; fi
++      -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage1; \
++        if $(RANLIB_TEST_FOR_TARGET) ; then \
++         $(RANLIB_FOR_TARGET) stage1/libgcc_eh.a; \
++        else true; fi; fi
++      -for f in .. $(EXTRA_MULTILIB_PARTS); do if [ x$${f} != x.. ]; then \
++        cp stage1/$${f} . ; \
++      else true; \
++      fi; done
++stage1: force stage1-start lang.stage1
++
++stage2-start:
++      -if [ -d stage2 ] ; then true ; else mkdir stage2 ; fi
++      $(MAKE) -f libgcc.mk libgcc-stage-start stage=stage2
++      -for dir in intl $(SUBDIRS) ; \
++       do \
++         if [ -d stage2/$$dir ] ; then true ; else mkdir stage2/$$dir ; fi ; \
++       done
++      -mv $(STAGESTUFF) stage2
++      -mv intl/*$(objext) stage2/intl
++# Copy as/ld if they exist to stage dir, so that running xgcc from the stage
++# dir will work properly.
++      -if [ -f as$(exeext) ] ; then (cd stage2 && $(LN_S) ../as$(exeext) .) ; else true ; fi
++      -if [ -f ld$(exeext) ] ; then (cd stage2 && $(LN_S) ../ld$(exeext) .) ; else true ; fi
++      -if [ -f collect-ld$(exeext) ] ; then (cd stage2 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi
++      -rm -f stage2/libgcc.a stage2/libgcc_eh.a
++      -cp libgcc.a stage2
++      -if $(RANLIB_TEST_FOR_TARGET) ; then \
++        $(RANLIB_FOR_TARGET) stage2/libgcc.a; \
++      else true; fi
++      -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage2; \
++        if $(RANLIB_TEST_FOR_TARGET) ; then \
++         $(RANLIB_FOR_TARGET) stage2/libgcc_eh.a; \
++        else true; fi; fi
++      -for f in .. $(EXTRA_MULTILIB_PARTS); do if [ x$${f} != x.. ]; then \
++        cp stage2/$${f} . ; \
++      else true; \
++      fi; done
++stage2: force stage2-start lang.stage2
++
++stage3-start:
++      -if [ -d stage3 ] ; then true ; else mkdir stage3 ; fi
++      $(MAKE) -f libgcc.mk libgcc-stage-start stage=stage3
++      -for dir in intl $(SUBDIRS) ; \
++       do \
++         if [ -d stage3/$$dir ] ; then true ; else mkdir stage3/$$dir ; fi ; \
++       done
++      -mv $(STAGESTUFF) stage3
++      -mv intl/*$(objext) stage3/intl
++# Copy as/ld if they exist to stage dir, so that running xgcc from the stage
++# dir will work properly.
++      -if [ -f as$(exeext) ] ; then (cd stage3 && $(LN_S) ../as$(exeext) .) ; else true ; fi
++      -if [ -f ld$(exeext) ] ; then (cd stage3 && $(LN_S) ../ld$(exeext) .) ; else true ; fi
++      -if [ -f collect-ld$(exeext) ] ; then (cd stage3 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi
++      -rm -f stage3/libgcc.a stage3/libgcc_eh.a
++      -cp libgcc.a stage3
++      -if $(RANLIB_TEST_FOR_TARGET) ; then \
++        $(RANLIB_FOR_TARGET) stage3/libgcc.a; \
++      else true; fi
++      -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage3; \
++        if $(RANLIB_TEST_FOR_TARGET) ; then \
++         $(RANLIB_FOR_TARGET) stage3/libgcc_eh.a; \
++        else true; fi; fi
++      -for f in .. $(EXTRA_MULTILIB_PARTS); do if [ x$${f} != x.. ]; then \
++        cp stage3/$${f} . ; \
++      else true; \
++      fi; done
++stage3: force stage3-start lang.stage3
++
++stage4-start:
++      -if [ -d stage4 ] ; then true ; else mkdir stage4 ; fi
++      $(MAKE) -f libgcc.mk libgcc-stage-start stage=stage4
++      -for dir in intl $(SUBDIRS) ; \
++       do \
++         if [ -d stage4/$$dir ] ; then true ; else mkdir stage4/$$dir ; fi ; \
++       done
++      -mv $(STAGESTUFF) stage4
++      -mv intl/*$(objext) stage4/intl
++# Copy as/ld if they exist to stage dir, so that running xgcc from the stage
++# dir will work properly.
++      -if [ -f as$(exeext) ] ; then (cd stage4 && $(LN_S) ../as$(exeext) .) ; else true ; fi
++      -if [ -f ld$(exeext) ] ; then (cd stage4 && $(LN_S) ../ld$(exeext) .) ; else true ; fi
++      -if [ -f collect-ld$(exeext) ] ; then (cd stage4 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi
++      -rm -f stage4/libgcc.a stage4/libgcc_eh.a
++      -cp libgcc.a stage4
++      -if $(RANLIB_TEST_FOR_TARGET) ; then \
++        $(RANLIB_FOR_TARGET) stage4/libgcc.a; \
++      else true; fi
++      -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage4; \
++        if $(RANLIB_TEST_FOR_TARGET) ; then \
++         $(RANLIB_FOR_TARGET) stage4/libgcc_eh.a; \
++        else true; fi; fi
++      -for f in .. $(EXTRA_MULTILIB_PARTS); do if [ x$${f} != x.. ]; then \
++        cp stage4/$${f} . ; \
++      else true; \
++      fi; done
++stage4: force stage4-start lang.stage4
++
++# Copy just the executable files from a particular stage into a subdirectory,
++# and delete the object files.  Use this if you're just verifying a version
++# that is pretty sure to work, and you are short of disk space.
++risky-stage1: stage1
++      -$(MAKE) clean
++
++risky-stage2: stage2
++      -$(MAKE) clean
++
++risky-stage3: stage3
++      -$(MAKE) clean
++
++risky-stage4: stage4
++      -$(MAKE) clean
++
++#In GNU Make, ignore whether `stage*' exists.
++.PHONY: stage1 stage2 stage3 stage4 clean maintainer-clean TAGS bootstrap
++.PHONY: risky-stage1 risky-stage2 risky-stage3 risky-stage4
++
++force:
++
++# Rules for generating translated message descriptions.
++# Disabled by autoconf if the tools are not available.
++
++XGETTEXT = @XGETTEXT@
++GMSGFMT = @GMSGFMT@
++MSGMERGE = msgmerge
++
++PACKAGE = @PACKAGE@
++CATALOGS = @CATALOGS@
++
++.PHONY: build- install- build-po install-po update-po
++
++# Dummy rules to deal with dependencies produced by use of
++# "build-@POSUB@" and "install-@POSUB@" above, when NLS is disabled.
++build-: ; @true
++install-: ; @true
++
++build-po: $(CATALOGS)
++
++# This notation should be acceptable to all Make implementations used
++# by people who are interested in updating .po files.
++update-po: $(CATALOGS:.gmo=.pox)
++
++# N.B. We do not attempt to copy these into $(srcdir).  The snapshot
++# script does that.
++.po.gmo:
++      -test -d po || mkdir po
++      $(GMSGFMT) --statistics -o $@ $<
++
++# The new .po has to be gone over by hand, so we deposit it into
++# build/po with a different extension.
++# If build/po/$(PACKAGE).pot exists, use it (it was just created),
++# else use the one in srcdir.
++.po.pox:
++      -test -d po || mkdir po
++      $(MSGMERGE) $< `if test -f po/$(PACKAGE).pot; \
++                      then echo po/$(PACKAGE).pot; \
++                      else echo $(srcdir)/po/$(PACKAGE).pot; fi` -o $@
++
++# This rule has to look for .gmo modules in both srcdir and
++# the cwd, and has to check that we actually have a catalog
++# for each language, in case they weren't built or included
++# with the distribution.
++install-po:
++      $(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$(datadir)
++      for cat in $(CATALOGS); do \
++        lang=`basename $$cat | sed 's/\.gmo$$//'`; \
++        if [ -f $$cat ]; then :; \
++        elif [ -f $(srcdir)/$$cat ]; then cat=$(srcdir)/$$cat; \
++        else continue; \
++        fi; \
++        dir=$(localedir)/$$lang/LC_MESSAGES; \
++        echo $(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$$dir; \
++        $(SHELL) $(srcdir)/mkinstalldirs $(DESTDIR)$$dir || exit 1; \
++        echo $(INSTALL_DATA) $$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
++        $(INSTALL_DATA) $$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
++      done
++
++# Rule for regenerating the message template (gcc.pot).
++# Instead of forcing everyone to edit POTFILES.in, which proved impractical,
++# this rule has no dependencies and always regenerates gcc.pot.  This is
++# relatively harmless since the .po files do not directly depend on it.
++# Note that exgettext has an awk script embedded in it which requires a
++# fairly modern (POSIX-compliant) awk.
++# The .pot file is left in the build directory.
++$(PACKAGE).pot: po/$(PACKAGE).pot
++po/$(PACKAGE).pot: force
++      -test -d po || mkdir po
++      $(MAKE) po-generated
++      AWK=$(AWK) $(SHELL) $(srcdir)/po/exgettext \
++              $(XGETTEXT) $(PACKAGE) $(srcdir)
+diff -ruN gcc-3.3.1/gcc/calls.c gcc-3.3.1.pp/gcc/calls.c
+--- gcc-3.3.1/gcc/calls.c      2003-07-18 02:58:25.000000000 +0000
++++ gcc-3.3.1.pp/gcc/calls.c   2003-09-05 11:58:58.000000000 +0000
+@@ -2327,7 +2327,7 @@
+           /* For variable-sized objects, we must be called with a target
+              specified.  If we were to allocate space on the stack here,
+              we would have no way of knowing when to free it.  */
+-          rtx d = assign_temp (TREE_TYPE (exp), 1, 1, 1);
++          rtx d = assign_temp (TREE_TYPE (exp), 5, 1, 1);
+           mark_temp_addr_taken (d);
+           structure_value_addr = XEXP (d, 0);
+diff -ruN gcc-3.3.1/gcc/calls.c.orig gcc-3.3.1.pp/gcc/calls.c.orig
+--- gcc-3.3.1/gcc/calls.c.orig 1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/calls.c.orig      2003-07-18 02:58:25.000000000 +0000
+@@ -0,0 +1,4667 @@
++/* Convert function calls to rtl insns, for GNU C compiler.
++   Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
++   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify it under
++the terms of the GNU General Public License as published by the Free
++Software Foundation; either version 2, or (at your option) any later
++version.
++
++GCC is distributed in the hope that it will be useful, but WITHOUT ANY
++WARRANTY; without even the implied warranty of MERCHANTABILITY or
++FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING.  If not, write to the Free
++Software Foundation, 59 Temple Place - Suite 330, Boston, MA
++02111-1307, USA.  */
++
++#include "config.h"
++#include "system.h"
++#include "rtl.h"
++#include "tree.h"
++#include "flags.h"
++#include "expr.h"
++#include "libfuncs.h"
++#include "function.h"
++#include "regs.h"
++#include "toplev.h"
++#include "output.h"
++#include "tm_p.h"
++#include "timevar.h"
++#include "sbitmap.h"
++#include "langhooks.h"
++#include "target.h"
++#include "except.h"
++
++#if !defined FUNCTION_OK_FOR_SIBCALL
++#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
++#endif
++
++/* Decide whether a function's arguments should be processed
++   from first to last or from last to first.
++
++   They should if the stack and args grow in opposite directions, but
++   only if we have push insns.  */
++
++#ifdef PUSH_ROUNDING
++
++#ifndef PUSH_ARGS_REVERSED
++#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
++#define PUSH_ARGS_REVERSED  PUSH_ARGS
++#endif
++#endif
++
++#endif
++
++#ifndef PUSH_ARGS_REVERSED
++#define PUSH_ARGS_REVERSED 0
++#endif
++
++#ifndef STACK_POINTER_OFFSET
++#define STACK_POINTER_OFFSET    0
++#endif
++
++/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
++#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
++
++/* Data structure and subroutines used within expand_call.  */
++
++struct arg_data
++{
++  /* Tree node for this argument.  */
++  tree tree_value;
++  /* Mode for value; TYPE_MODE unless promoted.  */
++  enum machine_mode mode;
++  /* Current RTL value for argument, or 0 if it isn't precomputed.  */
++  rtx value;
++  /* Initially-compute RTL value for argument; only for const functions.  */
++  rtx initial_value;
++  /* Register to pass this argument in, 0 if passed on stack, or an
++     PARALLEL if the arg is to be copied into multiple non-contiguous
++     registers.  */
++  rtx reg;
++  /* Register to pass this argument in when generating tail call sequence.
++     This is not the same register as for normal calls on machines with
++     register windows.  */
++  rtx tail_call_reg;
++  /* If REG was promoted from the actual mode of the argument expression,
++     indicates whether the promotion is sign- or zero-extended.  */
++  int unsignedp;
++  /* Number of registers to use.  0 means put the whole arg in registers.
++     Also 0 if not passed in registers.  */
++  int partial;
++  /* Nonzero if argument must be passed on stack.
++     Note that some arguments may be passed on the stack
++     even though pass_on_stack is zero, just because FUNCTION_ARG says so.
++     pass_on_stack identifies arguments that *cannot* go in registers.  */
++  int pass_on_stack;
++  /* Offset of this argument from beginning of stack-args.  */
++  struct args_size offset;
++  /* Similar, but offset to the start of the stack slot.  Different from
++     OFFSET if this arg pads downward.  */
++  struct args_size slot_offset;
++  /* Size of this argument on the stack, rounded up for any padding it gets,
++     parts of the argument passed in registers do not count.
++     If REG_PARM_STACK_SPACE is defined, then register parms
++     are counted here as well.  */
++  struct args_size size;
++  /* Location on the stack at which parameter should be stored.  The store
++     has already been done if STACK == VALUE.  */
++  rtx stack;
++  /* Location on the stack of the start of this argument slot.  This can
++     differ from STACK if this arg pads downward.  This location is known
++     to be aligned to FUNCTION_ARG_BOUNDARY.  */
++  rtx stack_slot;
++  /* Place that this stack area has been saved, if needed.  */
++  rtx save_area;
++  /* If an argument's alignment does not permit direct copying into registers,
++     copy in smaller-sized pieces into pseudos.  These are stored in a
++     block pointed to by this field.  The next field says how many
++     word-sized pseudos we made.  */
++  rtx *aligned_regs;
++  int n_aligned_regs;
++  /* The amount that the stack pointer needs to be adjusted to
++     force alignment for the next argument.  */
++  struct args_size alignment_pad;
++};
++
++/* A vector of one char per byte of stack space.  A byte if nonzero if
++   the corresponding stack location has been used.
++   This vector is used to prevent a function call within an argument from
++   clobbering any stack already set up.  */
++static char *stack_usage_map;
++
++/* Size of STACK_USAGE_MAP.  */
++static int highest_outgoing_arg_in_use;
++
++/* A bitmap of virtual-incoming stack space.  Bit is set if the corresponding
++   stack location's tail call argument has been already stored into the stack.
++   This bitmap is used to prevent sibling call optimization if function tries
++   to use parent's incoming argument slots when they have been already
++   overwritten with tail call arguments.  */
++static sbitmap stored_args_map;
++
++/* stack_arg_under_construction is nonzero when an argument may be
++   initialized with a constructor call (including a C function that
++   returns a BLKmode struct) and expand_call must take special action
++   to make sure the object being constructed does not overlap the
++   argument list for the constructor call.  */
++int stack_arg_under_construction;
++
++static int calls_function     PARAMS ((tree, int));
++static int calls_function_1   PARAMS ((tree, int));
++
++/* Nonzero if this is a call to a `const' function.  */
++#define ECF_CONST             1
++/* Nonzero if this is a call to a `volatile' function.  */
++#define ECF_NORETURN          2
++/* Nonzero if this is a call to malloc or a related function.  */
++#define ECF_MALLOC            4
++/* Nonzero if it is plausible that this is a call to alloca.  */
++#define ECF_MAY_BE_ALLOCA     8
++/* Nonzero if this is a call to a function that won't throw an exception.  */
++#define ECF_NOTHROW           16
++/* Nonzero if this is a call to setjmp or a related function.  */
++#define ECF_RETURNS_TWICE     32
++/* Nonzero if this is a call to `longjmp'.  */
++#define ECF_LONGJMP           64
++/* Nonzero if this is a syscall that makes a new process in the image of
++   the current one.  */
++#define ECF_FORK_OR_EXEC      128
++#define ECF_SIBCALL           256
++/* Nonzero if this is a call to "pure" function (like const function,
++   but may read memory.  */
++#define ECF_PURE              512
++/* Nonzero if this is a call to a function that returns with the stack
++   pointer depressed.  */
++#define ECF_SP_DEPRESSED      1024
++/* Nonzero if this call is known to always return.  */
++#define ECF_ALWAYS_RETURN     2048
++/* Create libcall block around the call.  */
++#define ECF_LIBCALL_BLOCK     4096
++
++static void emit_call_1               PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
++                                       HOST_WIDE_INT, HOST_WIDE_INT, rtx,
++                                       rtx, int, rtx, int,
++                                       CUMULATIVE_ARGS *));
++static void precompute_register_parameters    PARAMS ((int,
++                                                       struct arg_data *,
++                                                       int *));
++static int store_one_arg      PARAMS ((struct arg_data *, rtx, int, int,
++                                       int));
++static void store_unaligned_arguments_into_pseudos PARAMS ((struct arg_data *,
++                                                          int));
++static int finalize_must_preallocate          PARAMS ((int, int,
++                                                       struct arg_data *,
++                                                       struct args_size *));
++static void precompute_arguments              PARAMS ((int, int,
++                                                       struct arg_data *));
++static int compute_argument_block_size                PARAMS ((int,
++                                                       struct args_size *,
++                                                       int));
++static void initialize_argument_information   PARAMS ((int,
++                                                       struct arg_data *,
++                                                       struct args_size *,
++                                                       int, tree, tree,
++                                                       CUMULATIVE_ARGS *,
++                                                       int, rtx *, int *,
++                                                       int *, int *));
++static void compute_argument_addresses                PARAMS ((struct arg_data *,
++                                                       rtx, int));
++static rtx rtx_for_function_call              PARAMS ((tree, tree));
++static void load_register_parameters          PARAMS ((struct arg_data *,
++                                                       int, rtx *, int));
++static rtx emit_library_call_value_1          PARAMS ((int, rtx, rtx,
++                                                       enum libcall_type,
++                                                       enum machine_mode,
++                                                       int, va_list));
++static int special_function_p                 PARAMS ((tree, int));
++static int flags_from_decl_or_type            PARAMS ((tree));
++static rtx try_to_integrate                   PARAMS ((tree, tree, rtx,
++                                                       int, tree, rtx));
++static int check_sibcall_argument_overlap_1   PARAMS ((rtx));
++static int check_sibcall_argument_overlap     PARAMS ((rtx, struct arg_data *));
++
++static int combine_pending_stack_adjustment_and_call
++                                                PARAMS ((int, struct args_size *, int));
++static tree fix_unsafe_tree           PARAMS ((tree));
++
++#ifdef REG_PARM_STACK_SPACE
++static rtx save_fixed_argument_area   PARAMS ((int, rtx, int *, int *));
++static void restore_fixed_argument_area       PARAMS ((rtx, rtx, int, int));
++#endif
++\f
++/* If WHICH is 1, return 1 if EXP contains a call to the built-in function
++   `alloca'.
++
++   If WHICH is 0, return 1 if EXP contains a call to any function.
++   Actually, we only need return 1 if evaluating EXP would require pushing
++   arguments on the stack, but that is too difficult to compute, so we just
++   assume any function call might require the stack.  */
++
++static tree calls_function_save_exprs;
++
++static int
++calls_function (exp, which)
++     tree exp;
++     int which;
++{
++  int val;
++
++  calls_function_save_exprs = 0;
++  val = calls_function_1 (exp, which);
++  calls_function_save_exprs = 0;
++  return val;
++}
++
++/* Recursive function to do the work of above function.  */
++
++static int
++calls_function_1 (exp, which)
++     tree exp;
++     int which;
++{
++  int i;
++  enum tree_code code = TREE_CODE (exp);
++  int class = TREE_CODE_CLASS (code);
++  int length = first_rtl_op (code);
++
++  /* If this code is language-specific, we don't know what it will do.  */
++  if ((int) code >= NUM_TREE_CODES)
++    return 1;
++
++  switch (code)
++    {
++    case CALL_EXPR:
++      if (which == 0)
++      return 1;
++      else if ((TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
++              == FUNCTION_TYPE)
++             && (TYPE_RETURNS_STACK_DEPRESSED
++                 (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
++      return 1;
++      else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
++             && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
++                 == FUNCTION_DECL)
++             && (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                                     0)
++                 & ECF_MAY_BE_ALLOCA))
++      return 1;
++
++      break;
++
++    case CONSTRUCTOR:
++      {
++      tree tem;
++
++      for (tem = CONSTRUCTOR_ELTS (exp); tem != 0; tem = TREE_CHAIN (tem))
++        if (calls_function_1 (TREE_VALUE (tem), which))
++          return 1;
++      }
++
++      return 0;
++
++    case SAVE_EXPR:
++      if (SAVE_EXPR_RTL (exp) != 0)
++      return 0;
++      if (value_member (exp, calls_function_save_exprs))
++      return 0;
++      calls_function_save_exprs = tree_cons (NULL_TREE, exp,
++                                           calls_function_save_exprs);
++      return (TREE_OPERAND (exp, 0) != 0
++            && calls_function_1 (TREE_OPERAND (exp, 0), which));
++
++    case BLOCK:
++      {
++      tree local;
++      tree subblock;
++
++      for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
++        if (DECL_INITIAL (local) != 0
++            && calls_function_1 (DECL_INITIAL (local), which))
++          return 1;
++
++      for (subblock = BLOCK_SUBBLOCKS (exp);
++           subblock;
++           subblock = TREE_CHAIN (subblock))
++        if (calls_function_1 (subblock, which))
++          return 1;
++      }
++      return 0;
++
++    case TREE_LIST:
++      for (; exp != 0; exp = TREE_CHAIN (exp))
++      if (calls_function_1 (TREE_VALUE (exp), which))
++        return 1;
++      return 0;
++
++    default:
++      break;
++    }
++
++  /* Only expressions, references, and blocks can contain calls.  */
++  if (! IS_EXPR_CODE_CLASS (class) && class != 'r' && class != 'b')
++    return 0;
++
++  for (i = 0; i < length; i++)
++    if (TREE_OPERAND (exp, i) != 0
++      && calls_function_1 (TREE_OPERAND (exp, i), which))
++      return 1;
++
++  return 0;
++}
++\f
++/* Force FUNEXP into a form suitable for the address of a CALL,
++   and return that as an rtx.  Also load the static chain register
++   if FNDECL is a nested function.
++
++   CALL_FUSAGE points to a variable holding the prospective
++   CALL_INSN_FUNCTION_USAGE information.  */
++
++rtx
++prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen, sibcallp)
++     rtx funexp;
++     tree fndecl;
++     rtx *call_fusage;
++     int reg_parm_seen;
++     int sibcallp;
++{
++  rtx static_chain_value = 0;
++
++  funexp = protect_from_queue (funexp, 0);
++
++  if (fndecl != 0)
++    /* Get possible static chain value for nested function in C.  */
++    static_chain_value = lookup_static_chain (fndecl);
++
++  /* Make a valid memory address and copy constants thru pseudo-regs,
++     but not for a constant address if -fno-function-cse.  */
++  if (GET_CODE (funexp) != SYMBOL_REF)
++    /* If we are using registers for parameters, force the
++       function address into a register now.  */
++    funexp = ((SMALL_REGISTER_CLASSES && reg_parm_seen)
++            ? force_not_mem (memory_address (FUNCTION_MODE, funexp))
++            : memory_address (FUNCTION_MODE, funexp));
++  else if (! sibcallp)
++    {
++#ifndef NO_FUNCTION_CSE
++      if (optimize && ! flag_no_function_cse)
++#ifdef NO_RECURSIVE_FUNCTION_CSE
++      if (fndecl != current_function_decl)
++#endif
++        funexp = force_reg (Pmode, funexp);
++#endif
++    }
++
++  if (static_chain_value != 0)
++    {
++      emit_move_insn (static_chain_rtx, static_chain_value);
++
++      if (GET_CODE (static_chain_rtx) == REG)
++      use_reg (call_fusage, static_chain_rtx);
++    }
++
++  return funexp;
++}
++
++/* Generate instructions to call function FUNEXP,
++   and optionally pop the results.
++   The CALL_INSN is the first insn generated.
++
++   FNDECL is the declaration node of the function.  This is given to the
++   macro RETURN_POPS_ARGS to determine whether this function pops its own args.
++
++   FUNTYPE is the data type of the function.  This is given to the macro
++   RETURN_POPS_ARGS to determine whether this function pops its own args.
++   We used to allow an identifier for library functions, but that doesn't
++   work when the return type is an aggregate type and the calling convention
++   says that the pointer to this aggregate is to be popped by the callee.
++
++   STACK_SIZE is the number of bytes of arguments on the stack,
++   ROUNDED_STACK_SIZE is that number rounded up to
++   PREFERRED_STACK_BOUNDARY; zero if the size is variable.  This is
++   both to put into the call insn and to generate explicit popping
++   code if necessary.
++
++   STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value.
++   It is zero if this call doesn't want a structure value.
++
++   NEXT_ARG_REG is the rtx that results from executing
++     FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1)
++   just after all the args have had their registers assigned.
++   This could be whatever you like, but normally it is the first
++   arg-register beyond those used for args in this call,
++   or 0 if all the arg-registers are used in this call.
++   It is passed on to `gen_call' so you can put this info in the call insn.
++
++   VALREG is a hard register in which a value is returned,
++   or 0 if the call does not return a value.
++
++   OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before
++   the args to this call were processed.
++   We restore `inhibit_defer_pop' to that value.
++
++   CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that
++   denote registers used by the called function.  */
++
++static void
++emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
++           struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop,
++           call_fusage, ecf_flags, args_so_far)
++     rtx funexp;
++     tree fndecl ATTRIBUTE_UNUSED;
++     tree funtype ATTRIBUTE_UNUSED;
++     HOST_WIDE_INT stack_size ATTRIBUTE_UNUSED;
++     HOST_WIDE_INT rounded_stack_size;
++     HOST_WIDE_INT struct_value_size ATTRIBUTE_UNUSED;
++     rtx next_arg_reg ATTRIBUTE_UNUSED;
++     rtx valreg;
++     int old_inhibit_defer_pop;
++     rtx call_fusage;
++     int ecf_flags;
++     CUMULATIVE_ARGS *args_so_far ATTRIBUTE_UNUSED;
++{
++  rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
++  rtx call_insn;
++  int already_popped = 0;
++  HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
++#if defined (HAVE_call) && defined (HAVE_call_value)
++  rtx struct_value_size_rtx;
++  struct_value_size_rtx = GEN_INT (struct_value_size);
++#endif
++
++#ifdef CALL_POPS_ARGS
++  n_popped += CALL_POPS_ARGS (* args_so_far);
++#endif
++  
++  /* Ensure address is valid.  SYMBOL_REF is already valid, so no need,
++     and we don't want to load it into a register as an optimization,
++     because prepare_call_address already did it if it should be done.  */
++  if (GET_CODE (funexp) != SYMBOL_REF)
++    funexp = memory_address (FUNCTION_MODE, funexp);
++
++#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop)
++  if ((ecf_flags & ECF_SIBCALL)
++      && HAVE_sibcall_pop && HAVE_sibcall_value_pop
++      && (n_popped > 0 || stack_size == 0))
++    {
++      rtx n_pop = GEN_INT (n_popped);
++      rtx pat;
++
++      /* If this subroutine pops its own args, record that in the call insn
++       if possible, for the sake of frame pointer elimination.  */
++
++      if (valreg)
++      pat = GEN_SIBCALL_VALUE_POP (valreg,
++                                   gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                   rounded_stack_size_rtx, next_arg_reg,
++                                   n_pop);
++      else
++      pat = GEN_SIBCALL_POP (gen_rtx_MEM (FUNCTION_MODE, funexp),
++                             rounded_stack_size_rtx, next_arg_reg, n_pop);
++
++      emit_call_insn (pat);
++      already_popped = 1;
++    }
++  else
++#endif
++
++#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
++  /* If the target has "call" or "call_value" insns, then prefer them
++     if no arguments are actually popped.  If the target does not have
++     "call" or "call_value" insns, then we must use the popping versions
++     even if the call has no arguments to pop.  */
++#if defined (HAVE_call) && defined (HAVE_call_value)
++  if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
++      && n_popped > 0 && ! (ecf_flags & ECF_SP_DEPRESSED))
++#else
++  if (HAVE_call_pop && HAVE_call_value_pop)
++#endif
++    {
++      rtx n_pop = GEN_INT (n_popped);
++      rtx pat;
++
++      /* If this subroutine pops its own args, record that in the call insn
++       if possible, for the sake of frame pointer elimination.  */
++
++      if (valreg)
++      pat = GEN_CALL_VALUE_POP (valreg,
++                                gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                rounded_stack_size_rtx, next_arg_reg, n_pop);
++      else
++      pat = GEN_CALL_POP (gen_rtx_MEM (FUNCTION_MODE, funexp),
++                          rounded_stack_size_rtx, next_arg_reg, n_pop);
++
++      emit_call_insn (pat);
++      already_popped = 1;
++    }
++  else
++#endif
++
++#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value)
++  if ((ecf_flags & ECF_SIBCALL)
++      && HAVE_sibcall && HAVE_sibcall_value)
++    {
++      if (valreg)
++      emit_call_insn (GEN_SIBCALL_VALUE (valreg,
++                                         gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                         rounded_stack_size_rtx,
++                                         next_arg_reg, NULL_RTX));
++      else
++      emit_call_insn (GEN_SIBCALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                   rounded_stack_size_rtx, next_arg_reg,
++                                   struct_value_size_rtx));
++    }
++  else
++#endif
++
++#if defined (HAVE_call) && defined (HAVE_call_value)
++  if (HAVE_call && HAVE_call_value)
++    {
++      if (valreg)
++      emit_call_insn (GEN_CALL_VALUE (valreg,
++                                      gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                      rounded_stack_size_rtx, next_arg_reg,
++                                      NULL_RTX));
++      else
++      emit_call_insn (GEN_CALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
++                                rounded_stack_size_rtx, next_arg_reg,
++                                struct_value_size_rtx));
++    }
++  else
++#endif
++    abort ();
++
++  /* Find the CALL insn we just emitted.  */
++  for (call_insn = get_last_insn ();
++       call_insn && GET_CODE (call_insn) != CALL_INSN;
++       call_insn = PREV_INSN (call_insn))
++    ;
++
++  if (! call_insn)
++    abort ();
++
++  /* Mark memory as used for "pure" function call.  */
++  if (ecf_flags & ECF_PURE)
++    call_fusage
++      = gen_rtx_EXPR_LIST
++      (VOIDmode,
++       gen_rtx_USE (VOIDmode,
++                    gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))),
++       call_fusage);
++
++  /* Put the register usage information on the CALL.  If there is already
++     some usage information, put ours at the end.  */
++  if (CALL_INSN_FUNCTION_USAGE (call_insn))
++    {
++      rtx link;
++
++      for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
++         link = XEXP (link, 1))
++      ;
++
++      XEXP (link, 1) = call_fusage;
++    }
++  else
++    CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
++
++  /* If this is a const call, then set the insn's unchanging bit.  */
++  if (ecf_flags & (ECF_CONST | ECF_PURE))
++    CONST_OR_PURE_CALL_P (call_insn) = 1;
++
++  /* If this call can't throw, attach a REG_EH_REGION reg note to that
++     effect.  */
++  if (ecf_flags & ECF_NOTHROW)
++    REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
++                                             REG_NOTES (call_insn));
++  else
++    note_eh_region_may_contain_throw ();
++
++  if (ecf_flags & ECF_NORETURN)
++    REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
++                                             REG_NOTES (call_insn));
++  if (ecf_flags & ECF_ALWAYS_RETURN)
++    REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_ALWAYS_RETURN, const0_rtx,
++                                             REG_NOTES (call_insn));
++
++  if (ecf_flags & ECF_RETURNS_TWICE)
++    {
++      REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_SETJMP, const0_rtx,
++                                               REG_NOTES (call_insn));
++      current_function_calls_setjmp = 1;
++    }
++
++  SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
++
++  /* Restore this now, so that we do defer pops for this call's args
++     if the context of the call as a whole permits.  */
++  inhibit_defer_pop = old_inhibit_defer_pop;
++
++  if (n_popped > 0)
++    {
++      if (!already_popped)
++      CALL_INSN_FUNCTION_USAGE (call_insn)
++        = gen_rtx_EXPR_LIST (VOIDmode,
++                             gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx),
++                             CALL_INSN_FUNCTION_USAGE (call_insn));
++      rounded_stack_size -= n_popped;
++      rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
++      stack_pointer_delta -= n_popped;
++    }
++
++  if (!ACCUMULATE_OUTGOING_ARGS)
++    {
++      /* If returning from the subroutine does not automatically pop the args,
++       we need an instruction to pop them sooner or later.
++       Perhaps do it now; perhaps just record how much space to pop later.
++
++       If returning from the subroutine does pop the args, indicate that the
++       stack pointer will be changed.  */
++
++      if (rounded_stack_size != 0)
++      {
++        if (ecf_flags & ECF_SP_DEPRESSED)
++          /* Just pretend we did the pop.  */
++          stack_pointer_delta -= rounded_stack_size;
++        else if (flag_defer_pop && inhibit_defer_pop == 0
++            && ! (ecf_flags & (ECF_CONST | ECF_PURE)))
++          pending_stack_adjust += rounded_stack_size;
++        else
++          adjust_stack (rounded_stack_size_rtx);
++      }
++    }
++  /* When we accumulate outgoing args, we must avoid any stack manipulations.
++     Restore the stack pointer to its original value now.  Usually
++     ACCUMULATE_OUTGOING_ARGS targets don't get here, but there are exceptions.
++     On  i386 ACCUMULATE_OUTGOING_ARGS can be enabled on demand, and
++     popping variants of functions exist as well.
++
++     ??? We may optimize similar to defer_pop above, but it is
++     probably not worthwhile.
++
++     ??? It will be worthwhile to enable combine_stack_adjustments even for
++     such machines.  */
++  else if (n_popped)
++    anti_adjust_stack (GEN_INT (n_popped));
++}
++
++/* Determine if the function identified by NAME and FNDECL is one with
++   special properties we wish to know about.
++
++   For example, if the function might return more than one time (setjmp), then
++   set RETURNS_TWICE to a nonzero value.
++
++   Similarly set LONGJMP for if the function is in the longjmp family.
++
++   Set MALLOC for any of the standard memory allocation functions which
++   allocate from the heap.
++
++   Set MAY_BE_ALLOCA for any memory allocation function that might allocate
++   space from the stack such as alloca.  */
++
++static int
++special_function_p (fndecl, flags)
++     tree fndecl;
++     int flags;
++{
++  if (! (flags & ECF_MALLOC)
++      && fndecl && DECL_NAME (fndecl)
++      && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
++      /* Exclude functions not at the file scope, or not `extern',
++       since they are not the magic functions we would otherwise
++       think they are.  */
++      && DECL_CONTEXT (fndecl) == NULL_TREE && TREE_PUBLIC (fndecl))
++    {
++      const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
++      const char *tname = name;
++
++      /* We assume that alloca will always be called by name.  It
++       makes no sense to pass it as a pointer-to-function to
++       anything that does not understand its behavior.  */
++      if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
++          && name[0] == 'a'
++          && ! strcmp (name, "alloca"))
++         || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
++             && name[0] == '_'
++             && ! strcmp (name, "__builtin_alloca"))))
++      flags |= ECF_MAY_BE_ALLOCA;
++
++      /* Disregard prefix _, __ or __x.  */
++      if (name[0] == '_')
++      {
++        if (name[1] == '_' && name[2] == 'x')
++          tname += 3;
++        else if (name[1] == '_')
++          tname += 2;
++        else
++          tname += 1;
++      }
++
++      if (tname[0] == 's')
++      {
++        if ((tname[1] == 'e'
++             && (! strcmp (tname, "setjmp")
++                 || ! strcmp (tname, "setjmp_syscall")))
++            || (tname[1] == 'i'
++                && ! strcmp (tname, "sigsetjmp"))
++            || (tname[1] == 'a'
++                && ! strcmp (tname, "savectx")))
++          flags |= ECF_RETURNS_TWICE;
++
++        if (tname[1] == 'i'
++            && ! strcmp (tname, "siglongjmp"))
++          flags |= ECF_LONGJMP;
++      }
++      else if ((tname[0] == 'q' && tname[1] == 's'
++              && ! strcmp (tname, "qsetjmp"))
++             || (tname[0] == 'v' && tname[1] == 'f'
++                 && ! strcmp (tname, "vfork")))
++      flags |= ECF_RETURNS_TWICE;
++
++      else if (tname[0] == 'l' && tname[1] == 'o'
++             && ! strcmp (tname, "longjmp"))
++      flags |= ECF_LONGJMP;
++
++      else if ((tname[0] == 'f' && tname[1] == 'o'
++              && ! strcmp (tname, "fork"))
++             /* Linux specific: __clone.  check NAME to insist on the
++                leading underscores, to avoid polluting the ISO / POSIX
++                namespace.  */
++             || (name[0] == '_' && name[1] == '_'
++                 && ! strcmp (tname, "clone"))
++             || (tname[0] == 'e' && tname[1] == 'x' && tname[2] == 'e'
++                 && tname[3] == 'c' && (tname[4] == 'l' || tname[4] == 'v')
++                 && (tname[5] == '\0'
++                     || ((tname[5] == 'p' || tname[5] == 'e')
++                         && tname[6] == '\0'))))
++      flags |= ECF_FORK_OR_EXEC;
++
++      /* Do not add any more malloc-like functions to this list,
++         instead mark them as malloc functions using the malloc attribute.
++         Note, realloc is not suitable for attribute malloc since
++         it may return the same address across multiple calls.
++         C++ operator new is not suitable because it is not required
++         to return a unique pointer; indeed, the standard placement new
++       just returns its argument.  */
++      else if (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == Pmode
++             && (! strcmp (tname, "malloc")
++                 || ! strcmp (tname, "calloc")
++                 || ! strcmp (tname, "strdup")))
++      flags |= ECF_MALLOC;
++    }
++  return flags;
++}
++
++/* Return nonzero when tree represent call to longjmp.  */
++
++int
++setjmp_call_p (fndecl)
++     tree fndecl;
++{
++  return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
++}
++
++/* Return true when exp contains alloca call.  */
++bool
++alloca_call_p (exp)
++     tree exp;
++{
++  if (TREE_CODE (exp) == CALL_EXPR
++      && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
++      && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
++        == FUNCTION_DECL)
++      && (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                            0) & ECF_MAY_BE_ALLOCA))
++    return true;
++  return false;
++}
++
++/* Detect flags (function attributes) from the function decl or type node.  */
++
++static int
++flags_from_decl_or_type (exp)
++     tree exp;
++{
++  int flags = 0;
++  tree type = exp;
++  /* ??? We can't set IS_MALLOC for function types?  */
++  if (DECL_P (exp))
++    {
++      type = TREE_TYPE (exp);
++
++      /* The function exp may have the `malloc' attribute.  */
++      if (DECL_P (exp) && DECL_IS_MALLOC (exp))
++      flags |= ECF_MALLOC;
++
++      /* The function exp may have the `pure' attribute.  */
++      if (DECL_P (exp) && DECL_IS_PURE (exp))
++      flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
++
++      if (TREE_NOTHROW (exp))
++      flags |= ECF_NOTHROW;
++
++      if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
++      flags |= ECF_LIBCALL_BLOCK;
++    }
++
++  if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
++    flags |= ECF_CONST;
++
++  if (TREE_THIS_VOLATILE (exp))
++    flags |= ECF_NORETURN;
++
++  /* Mark if the function returns with the stack pointer depressed.   We
++     cannot consider it pure or constant in that case.  */
++  if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
++    {
++      flags |= ECF_SP_DEPRESSED;
++      flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
++    }
++
++  return flags;
++}
++
++/* Precompute all register parameters as described by ARGS, storing values
++   into fields within the ARGS array.
++
++   NUM_ACTUALS indicates the total number elements in the ARGS array.
++
++   Set REG_PARM_SEEN if we encounter a register parameter.  */
++
++static void
++precompute_register_parameters (num_actuals, args, reg_parm_seen)
++     int num_actuals;
++     struct arg_data *args;
++     int *reg_parm_seen;
++{
++  int i;
++
++  *reg_parm_seen = 0;
++
++  for (i = 0; i < num_actuals; i++)
++    if (args[i].reg != 0 && ! args[i].pass_on_stack)
++      {
++      *reg_parm_seen = 1;
++
++      if (args[i].value == 0)
++        {
++          push_temp_slots ();
++          args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
++                                       VOIDmode, 0);
++          preserve_temp_slots (args[i].value);
++          pop_temp_slots ();
++
++          /* ANSI doesn't require a sequence point here,
++             but PCC has one, so this will avoid some problems.  */
++          emit_queue ();
++        }
++
++      /* If the value is a non-legitimate constant, force it into a
++         pseudo now.  TLS symbols sometimes need a call to resolve.  */
++      if (CONSTANT_P (args[i].value)
++          && !LEGITIMATE_CONSTANT_P (args[i].value))
++        args[i].value = force_reg (args[i].mode, args[i].value);
++
++      /* If we are to promote the function arg to a wider mode,
++         do it now.  */
++
++      if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
++        args[i].value
++          = convert_modes (args[i].mode,
++                           TYPE_MODE (TREE_TYPE (args[i].tree_value)),
++                           args[i].value, args[i].unsignedp);
++
++      /* If the value is expensive, and we are inside an appropriately
++         short loop, put the value into a pseudo and then put the pseudo
++         into the hard reg.
++
++         For small register classes, also do this if this call uses
++         register parameters.  This is to avoid reload conflicts while
++         loading the parameters registers.  */
++
++      if ((! (GET_CODE (args[i].value) == REG
++              || (GET_CODE (args[i].value) == SUBREG
++                  && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
++          && args[i].mode != BLKmode
++          && rtx_cost (args[i].value, SET) > COSTS_N_INSNS (1)
++          && ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
++              || preserve_subexpressions_p ()))
++        args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
++      }
++}
++
++#ifdef REG_PARM_STACK_SPACE
++
++  /* The argument list is the property of the called routine and it
++     may clobber it.  If the fixed area has been used for previous
++     parameters, we must save and restore it.  */
++
++static rtx
++save_fixed_argument_area (reg_parm_stack_space, argblock,
++                        low_to_save, high_to_save)
++     int reg_parm_stack_space;
++     rtx argblock;
++     int *low_to_save;
++     int *high_to_save;
++{
++  int i;
++  rtx save_area = NULL_RTX;
++
++  /* Compute the boundary of the that needs to be saved, if any.  */
++#ifdef ARGS_GROW_DOWNWARD
++  for (i = 0; i < reg_parm_stack_space + 1; i++)
++#else
++  for (i = 0; i < reg_parm_stack_space; i++)
++#endif
++    {
++      if (i >= highest_outgoing_arg_in_use
++        || stack_usage_map[i] == 0)
++      continue;
++
++      if (*low_to_save == -1)
++      *low_to_save = i;
++
++      *high_to_save = i;
++    }
++
++  if (*low_to_save >= 0)
++    {
++      int num_to_save = *high_to_save - *low_to_save + 1;
++      enum machine_mode save_mode
++      = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
++      rtx stack_area;
++
++      /* If we don't have the required alignment, must do this in BLKmode.  */
++      if ((*low_to_save & (MIN (GET_MODE_SIZE (save_mode),
++                              BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
++      save_mode = BLKmode;
++
++#ifdef ARGS_GROW_DOWNWARD
++      stack_area
++      = gen_rtx_MEM (save_mode,
++                     memory_address (save_mode,
++                                     plus_constant (argblock,
++                                                    - *high_to_save)));
++#else
++      stack_area = gen_rtx_MEM (save_mode,
++                              memory_address (save_mode,
++                                              plus_constant (argblock,
++                                                             *low_to_save)));
++#endif
++
++      set_mem_align (stack_area, PARM_BOUNDARY);
++      if (save_mode == BLKmode)
++      {
++        save_area = assign_stack_temp (BLKmode, num_to_save, 0);
++        emit_block_move (validize_mem (save_area), stack_area,
++                         GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
++      }
++      else
++      {
++        save_area = gen_reg_rtx (save_mode);
++        emit_move_insn (save_area, stack_area);
++      }
++    }
++
++  return save_area;
++}
++
++static void
++restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save)
++     rtx save_area;
++     rtx argblock;
++     int high_to_save;
++     int low_to_save;
++{
++  enum machine_mode save_mode = GET_MODE (save_area);
++#ifdef ARGS_GROW_DOWNWARD
++  rtx stack_area
++    = gen_rtx_MEM (save_mode,
++                 memory_address (save_mode,
++                                 plus_constant (argblock,
++                                                - high_to_save)));
++#else
++  rtx stack_area
++    = gen_rtx_MEM (save_mode,
++                 memory_address (save_mode,
++                                 plus_constant (argblock,
++                                                low_to_save)));
++#endif
++
++  if (save_mode != BLKmode)
++    emit_move_insn (stack_area, save_area);
++  else
++    emit_block_move (stack_area, validize_mem (save_area),
++                   GEN_INT (high_to_save - low_to_save + 1),
++                   BLOCK_OP_CALL_PARM);
++}
++#endif /* REG_PARM_STACK_SPACE */
++
++/* If any elements in ARGS refer to parameters that are to be passed in
++   registers, but not in memory, and whose alignment does not permit a
++   direct copy into registers.  Copy the values into a group of pseudos
++   which we will later copy into the appropriate hard registers.
++
++   Pseudos for each unaligned argument will be stored into the array
++   args[argnum].aligned_regs.  The caller is responsible for deallocating
++   the aligned_regs array if it is nonzero.  */
++
++static void
++store_unaligned_arguments_into_pseudos (args, num_actuals)
++     struct arg_data *args;
++     int num_actuals;
++{
++  int i, j;
++
++  for (i = 0; i < num_actuals; i++)
++    if (args[i].reg != 0 && ! args[i].pass_on_stack
++      && args[i].mode == BLKmode
++      && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
++          < (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
++      {
++      int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
++      int big_endian_correction = 0;
++
++      args[i].n_aligned_regs
++        = args[i].partial ? args[i].partial
++          : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
++
++      args[i].aligned_regs = (rtx *) xmalloc (sizeof (rtx)
++                                              * args[i].n_aligned_regs);
++
++      /* Structures smaller than a word are aligned to the least
++         significant byte (to the right).  On a BYTES_BIG_ENDIAN machine,
++         this means we must skip the empty high order bytes when
++         calculating the bit offset.  */
++      if (BYTES_BIG_ENDIAN
++          && bytes < UNITS_PER_WORD)
++        big_endian_correction = (BITS_PER_WORD  - (bytes * BITS_PER_UNIT));
++
++      for (j = 0; j < args[i].n_aligned_regs; j++)
++        {
++          rtx reg = gen_reg_rtx (word_mode);
++          rtx word = operand_subword_force (args[i].value, j, BLKmode);
++          int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD);
++
++          args[i].aligned_regs[j] = reg;
++
++          /* There is no need to restrict this code to loading items
++             in TYPE_ALIGN sized hunks.  The bitfield instructions can
++             load up entire word sized registers efficiently.
++
++             ??? This may not be needed anymore.
++             We use to emit a clobber here but that doesn't let later
++             passes optimize the instructions we emit.  By storing 0 into
++             the register later passes know the first AND to zero out the
++             bitfield being set in the register is unnecessary.  The store
++             of 0 will be deleted as will at least the first AND.  */
++
++          emit_move_insn (reg, const0_rtx);
++
++          bytes -= bitsize / BITS_PER_UNIT;
++          store_bit_field (reg, bitsize, big_endian_correction, word_mode,
++                           extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
++                                              word_mode, word_mode,
++                                              BITS_PER_WORD),
++                           BITS_PER_WORD);
++        }
++      }
++}
++
++/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
++   ACTPARMS.
++
++   NUM_ACTUALS is the total number of parameters.
++
++   N_NAMED_ARGS is the total number of named arguments.
++
++   FNDECL is the tree code for the target of this call (if known)
++
++   ARGS_SO_FAR holds state needed by the target to know where to place
++   the next argument.
++
++   REG_PARM_STACK_SPACE is the number of bytes of stack space reserved
++   for arguments which are passed in registers.
++
++   OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level
++   and may be modified by this routine.
++
++   OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer
++   flags which may may be modified by this routine.  */
++
++static void
++initialize_argument_information (num_actuals, args, args_size, n_named_args,
++                               actparms, fndecl, args_so_far,
++                               reg_parm_stack_space, old_stack_level,
++                               old_pending_adj, must_preallocate,
++                               ecf_flags)
++     int num_actuals ATTRIBUTE_UNUSED;
++     struct arg_data *args;
++     struct args_size *args_size;
++     int n_named_args ATTRIBUTE_UNUSED;
++     tree actparms;
++     tree fndecl;
++     CUMULATIVE_ARGS *args_so_far;
++     int reg_parm_stack_space;
++     rtx *old_stack_level;
++     int *old_pending_adj;
++     int *must_preallocate;
++     int *ecf_flags;
++{
++  /* 1 if scanning parms front to back, -1 if scanning back to front.  */
++  int inc;
++
++  /* Count arg position in order args appear.  */
++  int argpos;
++
++  struct args_size alignment_pad;
++  int i;
++  tree p;
++
++  args_size->constant = 0;
++  args_size->var = 0;
++
++  /* In this loop, we consider args in the order they are written.
++     We fill up ARGS from the front or from the back if necessary
++     so that in any case the first arg to be pushed ends up at the front.  */
++
++  if (PUSH_ARGS_REVERSED)
++    {
++      i = num_actuals - 1, inc = -1;
++      /* In this case, must reverse order of args
++       so that we compute and push the last arg first.  */
++    }
++  else
++    {
++      i = 0, inc = 1;
++    }
++
++  /* I counts args in order (to be) pushed; ARGPOS counts in order written.  */
++  for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
++    {
++      tree type = TREE_TYPE (TREE_VALUE (p));
++      int unsignedp;
++      enum machine_mode mode;
++
++      args[i].tree_value = TREE_VALUE (p);
++
++      /* Replace erroneous argument with constant zero.  */
++      if (type == error_mark_node || !COMPLETE_TYPE_P (type))
++      args[i].tree_value = integer_zero_node, type = integer_type_node;
++
++      /* If TYPE is a transparent union, pass things the way we would
++       pass the first field of the union.  We have already verified that
++       the modes are the same.  */
++      if (TREE_CODE (type) == UNION_TYPE && TYPE_TRANSPARENT_UNION (type))
++      type = TREE_TYPE (TYPE_FIELDS (type));
++
++      /* Decide where to pass this arg.
++
++       args[i].reg is nonzero if all or part is passed in registers.
++
++       args[i].partial is nonzero if part but not all is passed in registers,
++       and the exact value says how many words are passed in registers.
++
++       args[i].pass_on_stack is nonzero if the argument must at least be
++       computed on the stack.  It may then be loaded back into registers
++       if args[i].reg is nonzero.
++
++       These decisions are driven by the FUNCTION_... macros and must agree
++       with those made by function.c.  */
++
++      /* See if this argument should be passed by invisible reference.  */
++      if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
++         && contains_placeholder_p (TYPE_SIZE (type)))
++        || TREE_ADDRESSABLE (type)
++#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
++        || FUNCTION_ARG_PASS_BY_REFERENCE (*args_so_far, TYPE_MODE (type),
++                                           type, argpos < n_named_args)
++#endif
++        )
++      {
++        /* If we're compiling a thunk, pass through invisible
++             references instead of making a copy.  */
++        if (current_function_is_thunk
++#ifdef FUNCTION_ARG_CALLEE_COPIES
++            || (FUNCTION_ARG_CALLEE_COPIES (*args_so_far, TYPE_MODE (type),
++                                           type, argpos < n_named_args)
++                /* If it's in a register, we must make a copy of it too.  */
++                /* ??? Is this a sufficient test?  Is there a better one? */
++                && !(TREE_CODE (args[i].tree_value) == VAR_DECL
++                     && REG_P (DECL_RTL (args[i].tree_value)))
++                && ! TREE_ADDRESSABLE (type))
++#endif
++            )
++          {
++            /* C++ uses a TARGET_EXPR to indicate that we want to make a
++               new object from the argument.  If we are passing by
++               invisible reference, the callee will do that for us, so we
++               can strip off the TARGET_EXPR.  This is not always safe,
++               but it is safe in the only case where this is a useful
++               optimization; namely, when the argument is a plain object.
++               In that case, the frontend is just asking the backend to
++               make a bitwise copy of the argument.  */
++
++            if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
++                && (DECL_P (TREE_OPERAND (args[i].tree_value, 1)))
++                && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
++              args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
++
++            args[i].tree_value = build1 (ADDR_EXPR,
++                                         build_pointer_type (type),
++                                         args[i].tree_value);
++            type = build_pointer_type (type);
++          }
++        else if (TREE_CODE (args[i].tree_value) == TARGET_EXPR)
++          {
++            /* In the V3 C++ ABI, parameters are destroyed in the caller.
++               We implement this by passing the address of the temporary
++               rather than expanding it into another allocated slot.  */
++            args[i].tree_value = build1 (ADDR_EXPR,
++                                         build_pointer_type (type),
++                                         args[i].tree_value);
++            type = build_pointer_type (type);
++          }
++        else
++          {
++            /* We make a copy of the object and pass the address to the
++               function being called.  */
++            rtx copy;
++
++            if (!COMPLETE_TYPE_P (type)
++                || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
++                || (flag_stack_check && ! STACK_CHECK_BUILTIN
++                    && (0 < compare_tree_int (TYPE_SIZE_UNIT (type),
++                                              STACK_CHECK_MAX_VAR_SIZE))))
++              {
++                /* This is a variable-sized object.  Make space on the stack
++                   for it.  */
++                rtx size_rtx = expr_size (TREE_VALUE (p));
++
++                if (*old_stack_level == 0)
++                  {
++                    emit_stack_save (SAVE_BLOCK, old_stack_level, NULL_RTX);
++                    *old_pending_adj = pending_stack_adjust;
++                    pending_stack_adjust = 0;
++                  }
++
++                copy = gen_rtx_MEM (BLKmode,
++                                    allocate_dynamic_stack_space
++                                    (size_rtx, NULL_RTX, TYPE_ALIGN (type)));
++                set_mem_attributes (copy, type, 1);
++              }
++            else
++              copy = assign_temp (type, 0, 1, 0);
++
++            store_expr (args[i].tree_value, copy, 0);
++            *ecf_flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
++
++            args[i].tree_value = build1 (ADDR_EXPR,
++                                         build_pointer_type (type),
++                                         make_tree (type, copy));
++            type = build_pointer_type (type);
++          }
++      }
++
++      mode = TYPE_MODE (type);
++      unsignedp = TREE_UNSIGNED (type);
++
++#ifdef PROMOTE_FUNCTION_ARGS
++      mode = promote_mode (type, mode, &unsignedp, 1);
++#endif
++
++      args[i].unsignedp = unsignedp;
++      args[i].mode = mode;
++
++      args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
++                                argpos < n_named_args);
++#ifdef FUNCTION_INCOMING_ARG
++      /* If this is a sibling call and the machine has register windows, the
++       register window has to be unwinded before calling the routine, so
++       arguments have to go into the incoming registers.  */
++      args[i].tail_call_reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
++                                                   argpos < n_named_args);
++#else
++      args[i].tail_call_reg = args[i].reg;
++#endif
++
++#ifdef FUNCTION_ARG_PARTIAL_NREGS
++      if (args[i].reg)
++      args[i].partial
++        = FUNCTION_ARG_PARTIAL_NREGS (*args_so_far, mode, type,
++                                      argpos < n_named_args);
++#endif
++
++      args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
++
++      /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
++       it means that we are to pass this arg in the register(s) designated
++       by the PARALLEL, but also to pass it in the stack.  */
++      if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
++        && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
++      args[i].pass_on_stack = 1;
++
++      /* If this is an addressable type, we must preallocate the stack
++       since we must evaluate the object into its final location.
++
++       If this is to be passed in both registers and the stack, it is simpler
++       to preallocate.  */
++      if (TREE_ADDRESSABLE (type)
++        || (args[i].pass_on_stack && args[i].reg != 0))
++      *must_preallocate = 1;
++
++      /* If this is an addressable type, we cannot pre-evaluate it.  Thus,
++       we cannot consider this function call constant.  */
++      if (TREE_ADDRESSABLE (type))
++      *ecf_flags &= ~ECF_LIBCALL_BLOCK;
++
++      /* Compute the stack-size of this argument.  */
++      if (args[i].reg == 0 || args[i].partial != 0
++        || reg_parm_stack_space > 0
++        || args[i].pass_on_stack)
++      locate_and_pad_parm (mode, type,
++#ifdef STACK_PARMS_IN_REG_PARM_AREA
++                           1,
++#else
++                           args[i].reg != 0,
++#endif
++                           fndecl, args_size, &args[i].offset,
++                           &args[i].size, &alignment_pad);
++
++#ifndef ARGS_GROW_DOWNWARD
++      args[i].slot_offset = *args_size;
++#endif
++
++      args[i].alignment_pad = alignment_pad;
++
++      /* If a part of the arg was put into registers,
++       don't include that part in the amount pushed.  */
++      if (reg_parm_stack_space == 0 && ! args[i].pass_on_stack)
++      args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
++                                / (PARM_BOUNDARY / BITS_PER_UNIT)
++                                * (PARM_BOUNDARY / BITS_PER_UNIT));
++
++      /* Update ARGS_SIZE, the total stack space for args so far.  */
++
++      args_size->constant += args[i].size.constant;
++      if (args[i].size.var)
++      {
++        ADD_PARM_SIZE (*args_size, args[i].size.var);
++      }
++
++      /* Since the slot offset points to the bottom of the slot,
++       we must record it after incrementing if the args grow down.  */
++#ifdef ARGS_GROW_DOWNWARD
++      args[i].slot_offset = *args_size;
++
++      args[i].slot_offset.constant = -args_size->constant;
++      if (args_size->var)
++      SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
++#endif
++
++      /* Increment ARGS_SO_FAR, which has info about which arg-registers
++       have been used, etc.  */
++
++      FUNCTION_ARG_ADVANCE (*args_so_far, TYPE_MODE (type), type,
++                          argpos < n_named_args);
++    }
++}
++
++/* Update ARGS_SIZE to contain the total size for the argument block.
++   Return the original constant component of the argument block's size.
++
++   REG_PARM_STACK_SPACE holds the number of bytes of stack space reserved
++   for arguments passed in registers.  */
++
++static int
++compute_argument_block_size (reg_parm_stack_space, args_size,
++                           preferred_stack_boundary)
++     int reg_parm_stack_space;
++     struct args_size *args_size;
++     int preferred_stack_boundary ATTRIBUTE_UNUSED;
++{
++  int unadjusted_args_size = args_size->constant;
++
++  /* For accumulate outgoing args mode we don't need to align, since the frame
++     will be already aligned.  Align to STACK_BOUNDARY in order to prevent
++     backends from generating misaligned frame sizes.  */
++  if (ACCUMULATE_OUTGOING_ARGS && preferred_stack_boundary > STACK_BOUNDARY)
++    preferred_stack_boundary = STACK_BOUNDARY;
++
++  /* Compute the actual size of the argument block required.  The variable
++     and constant sizes must be combined, the size may have to be rounded,
++     and there may be a minimum required size.  */
++
++  if (args_size->var)
++    {
++      args_size->var = ARGS_SIZE_TREE (*args_size);
++      args_size->constant = 0;
++
++      preferred_stack_boundary /= BITS_PER_UNIT;
++      if (preferred_stack_boundary > 1)
++      {
++        /* We don't handle this case yet.  To handle it correctly we have
++           to add the delta, round and subtract the delta.
++           Currently no machine description requires this support.  */
++        if (stack_pointer_delta & (preferred_stack_boundary - 1))
++          abort ();
++        args_size->var = round_up (args_size->var, preferred_stack_boundary);
++      }
++
++      if (reg_parm_stack_space > 0)
++      {
++        args_size->var
++          = size_binop (MAX_EXPR, args_size->var,
++                        ssize_int (reg_parm_stack_space));
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++        /* The area corresponding to register parameters is not to count in
++           the size of the block we need.  So make the adjustment.  */
++        args_size->var
++          = size_binop (MINUS_EXPR, args_size->var,
++                        ssize_int (reg_parm_stack_space));
++#endif
++      }
++    }
++  else
++    {
++      preferred_stack_boundary /= BITS_PER_UNIT;
++      if (preferred_stack_boundary < 1)
++      preferred_stack_boundary = 1;
++      args_size->constant = (((args_size->constant
++                             + stack_pointer_delta
++                             + preferred_stack_boundary - 1)
++                            / preferred_stack_boundary
++                            * preferred_stack_boundary)
++                           - stack_pointer_delta);
++
++      args_size->constant = MAX (args_size->constant,
++                               reg_parm_stack_space);
++
++#ifdef MAYBE_REG_PARM_STACK_SPACE
++      if (reg_parm_stack_space == 0)
++      args_size->constant = 0;
++#endif
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++      args_size->constant -= reg_parm_stack_space;
++#endif
++    }
++  return unadjusted_args_size;
++}
++
++/* Precompute parameters as needed for a function call.
++
++   FLAGS is mask of ECF_* constants.
++
++   NUM_ACTUALS is the number of arguments.
++
++   ARGS is an array containing information for each argument; this
++   routine fills in the INITIAL_VALUE and VALUE fields for each
++   precomputed argument.  */
++
++static void
++precompute_arguments (flags, num_actuals, args)
++     int flags;
++     int num_actuals;
++     struct arg_data *args;
++{
++  int i;
++
++  /* If this function call is cse'able, precompute all the parameters.
++     Note that if the parameter is constructed into a temporary, this will
++     cause an additional copy because the parameter will be constructed
++     into a temporary location and then copied into the outgoing arguments.
++     If a parameter contains a call to alloca and this function uses the
++     stack, precompute the parameter.  */
++
++  /* If we preallocated the stack space, and some arguments must be passed
++     on the stack, then we must precompute any parameter which contains a
++     function call which will store arguments on the stack.
++     Otherwise, evaluating the parameter may clobber previous parameters
++     which have already been stored into the stack.  (we have code to avoid
++     such case by saving the outgoing stack arguments, but it results in
++     worse code)  */
++
++  for (i = 0; i < num_actuals; i++)
++    if ((flags & ECF_LIBCALL_BLOCK)
++      || calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
++      {
++      enum machine_mode mode;
++
++      /* If this is an addressable type, we cannot pre-evaluate it.  */
++      if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
++        abort ();
++
++      args[i].value
++        = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
++
++      /* ANSI doesn't require a sequence point here,
++         but PCC has one, so this will avoid some problems.  */
++      emit_queue ();
++
++      args[i].initial_value = args[i].value
++        = protect_from_queue (args[i].value, 0);
++
++      mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
++      if (mode != args[i].mode)
++        {
++          args[i].value
++            = convert_modes (args[i].mode, mode,
++                             args[i].value, args[i].unsignedp);
++#ifdef PROMOTE_FOR_CALL_ONLY
++          /* CSE will replace this only if it contains args[i].value
++             pseudo, so convert it down to the declared mode using
++             a SUBREG.  */
++          if (GET_CODE (args[i].value) == REG
++              && GET_MODE_CLASS (args[i].mode) == MODE_INT)
++            {
++              args[i].initial_value
++                = gen_lowpart_SUBREG (mode, args[i].value);
++              SUBREG_PROMOTED_VAR_P (args[i].initial_value) = 1;
++              SUBREG_PROMOTED_UNSIGNED_SET (args[i].initial_value,
++                args[i].unsignedp);
++            }
++#endif
++        }
++      }
++}
++
++/* Given the current state of MUST_PREALLOCATE and information about
++   arguments to a function call in NUM_ACTUALS, ARGS and ARGS_SIZE,
++   compute and return the final value for MUST_PREALLOCATE.  */
++
++static int
++finalize_must_preallocate (must_preallocate, num_actuals, args, args_size)
++     int must_preallocate;
++     int num_actuals;
++     struct arg_data *args;
++     struct args_size *args_size;
++{
++  /* See if we have or want to preallocate stack space.
++
++     If we would have to push a partially-in-regs parm
++     before other stack parms, preallocate stack space instead.
++
++     If the size of some parm is not a multiple of the required stack
++     alignment, we must preallocate.
++
++     If the total size of arguments that would otherwise create a copy in
++     a temporary (such as a CALL) is more than half the total argument list
++     size, preallocation is faster.
++
++     Another reason to preallocate is if we have a machine (like the m88k)
++     where stack alignment is required to be maintained between every
++     pair of insns, not just when the call is made.  However, we assume here
++     that such machines either do not have push insns (and hence preallocation
++     would occur anyway) or the problem is taken care of with
++     PUSH_ROUNDING.  */
++
++  if (! must_preallocate)
++    {
++      int partial_seen = 0;
++      int copy_to_evaluate_size = 0;
++      int i;
++
++      for (i = 0; i < num_actuals && ! must_preallocate; i++)
++      {
++        if (args[i].partial > 0 && ! args[i].pass_on_stack)
++          partial_seen = 1;
++        else if (partial_seen && args[i].reg == 0)
++          must_preallocate = 1;
++
++        if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
++            && (TREE_CODE (args[i].tree_value) == CALL_EXPR
++                || TREE_CODE (args[i].tree_value) == TARGET_EXPR
++                || TREE_CODE (args[i].tree_value) == COND_EXPR
++                || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
++          copy_to_evaluate_size
++            += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
++      }
++
++      if (copy_to_evaluate_size * 2 >= args_size->constant
++        && args_size->constant > 0)
++      must_preallocate = 1;
++    }
++  return must_preallocate;
++}
++
++/* If we preallocated stack space, compute the address of each argument
++   and store it into the ARGS array.
++
++   We need not ensure it is a valid memory address here; it will be
++   validized when it is used.
++
++   ARGBLOCK is an rtx for the address of the outgoing arguments.  */
++
++static void
++compute_argument_addresses (args, argblock, num_actuals)
++     struct arg_data *args;
++     rtx argblock;
++     int num_actuals;
++{
++  if (argblock)
++    {
++      rtx arg_reg = argblock;
++      int i, arg_offset = 0;
++
++      if (GET_CODE (argblock) == PLUS)
++      arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
++
++      for (i = 0; i < num_actuals; i++)
++      {
++        rtx offset = ARGS_SIZE_RTX (args[i].offset);
++        rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
++        rtx addr;
++
++        /* Skip this parm if it will not be passed on the stack.  */
++        if (! args[i].pass_on_stack && args[i].reg != 0)
++          continue;
++
++        if (GET_CODE (offset) == CONST_INT)
++          addr = plus_constant (arg_reg, INTVAL (offset));
++        else
++          addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
++
++        addr = plus_constant (addr, arg_offset);
++        args[i].stack = gen_rtx_MEM (args[i].mode, addr);
++        set_mem_align (args[i].stack, PARM_BOUNDARY);
++        set_mem_attributes (args[i].stack,
++                            TREE_TYPE (args[i].tree_value), 1);
++
++        if (GET_CODE (slot_offset) == CONST_INT)
++          addr = plus_constant (arg_reg, INTVAL (slot_offset));
++        else
++          addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
++
++        addr = plus_constant (addr, arg_offset);
++        args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
++        set_mem_align (args[i].stack_slot, PARM_BOUNDARY);
++        set_mem_attributes (args[i].stack_slot,
++                            TREE_TYPE (args[i].tree_value), 1);
++
++        /* Function incoming arguments may overlap with sibling call
++           outgoing arguments and we cannot allow reordering of reads
++           from function arguments with stores to outgoing arguments
++           of sibling calls.  */
++        set_mem_alias_set (args[i].stack, 0);
++        set_mem_alias_set (args[i].stack_slot, 0);
++      }
++    }
++}
++
++/* Given a FNDECL and EXP, return an rtx suitable for use as a target address
++   in a call instruction.
++
++   FNDECL is the tree node for the target function.  For an indirect call
++   FNDECL will be NULL_TREE.
++
++   ADDR is the operand 0 of CALL_EXPR for this call.  */
++
++static rtx
++rtx_for_function_call (fndecl, addr)
++     tree fndecl;
++     tree addr;
++{
++  rtx funexp;
++
++  /* Get the function to call, in the form of RTL.  */
++  if (fndecl)
++    {
++      /* If this is the first use of the function, see if we need to
++       make an external definition for it.  */
++      if (! TREE_USED (fndecl))
++      {
++        assemble_external (fndecl);
++        TREE_USED (fndecl) = 1;
++      }
++
++      /* Get a SYMBOL_REF rtx for the function address.  */
++      funexp = XEXP (DECL_RTL (fndecl), 0);
++    }
++  else
++    /* Generate an rtx (probably a pseudo-register) for the address.  */
++    {
++      rtx funaddr;
++      push_temp_slots ();
++      funaddr = funexp
++      = expand_expr (addr, NULL_RTX, VOIDmode, 0);
++      pop_temp_slots ();      /* FUNEXP can't be BLKmode.  */
++      emit_queue ();
++    }
++  return funexp;
++}
++
++/* Do the register loads required for any wholly-register parms or any
++   parms which are passed both on the stack and in a register.  Their
++   expressions were already evaluated.
++
++   Mark all register-parms as living through the call, putting these USE
++   insns in the CALL_INSN_FUNCTION_USAGE field.  */
++
++static void
++load_register_parameters (args, num_actuals, call_fusage, flags)
++     struct arg_data *args;
++     int num_actuals;
++     rtx *call_fusage;
++     int flags;
++{
++  int i, j;
++
++#ifdef LOAD_ARGS_REVERSED
++  for (i = num_actuals - 1; i >= 0; i--)
++#else
++  for (i = 0; i < num_actuals; i++)
++#endif
++    {
++      rtx reg = ((flags & ECF_SIBCALL)
++               ? args[i].tail_call_reg : args[i].reg);
++      int partial = args[i].partial;
++      int nregs;
++
++      if (reg)
++      {
++        /* Set to non-negative if must move a word at a time, even if just
++           one word (e.g, partial == 1 && mode == DFmode).  Set to -1 if
++           we just use a normal move insn.  This value can be zero if the
++           argument is a zero size structure with no fields.  */
++        nregs = (partial ? partial
++                 : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
++                    ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
++                        + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
++                    : -1));
++
++        /* Handle calls that pass values in multiple non-contiguous
++           locations.  The Irix 6 ABI has examples of this.  */
++
++        if (GET_CODE (reg) == PARALLEL)
++          emit_group_load (reg, args[i].value,
++                           int_size_in_bytes (TREE_TYPE (args[i].tree_value)));
++
++        /* If simple case, just do move.  If normal partial, store_one_arg
++           has already loaded the register for us.  In all other cases,
++           load the register(s) from memory.  */
++
++        else if (nregs == -1)
++          emit_move_insn (reg, args[i].value);
++
++        /* If we have pre-computed the values to put in the registers in
++           the case of non-aligned structures, copy them in now.  */
++
++        else if (args[i].n_aligned_regs != 0)
++          for (j = 0; j < args[i].n_aligned_regs; j++)
++            emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
++                            args[i].aligned_regs[j]);
++
++        else if (partial == 0 || args[i].pass_on_stack)
++          move_block_to_reg (REGNO (reg),
++                             validize_mem (args[i].value), nregs,
++                             args[i].mode);
++
++        /* Handle calls that pass values in multiple non-contiguous
++           locations.  The Irix 6 ABI has examples of this.  */
++        if (GET_CODE (reg) == PARALLEL)
++          use_group_regs (call_fusage, reg);
++        else if (nregs == -1)
++          use_reg (call_fusage, reg);
++        else
++          use_regs (call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
++      }
++    }
++}
++
++/* Try to integrate function.  See expand_inline_function for documentation
++   about the parameters.  */
++
++static rtx
++try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
++     tree fndecl;
++     tree actparms;
++     rtx target;
++     int ignore;
++     tree type;
++     rtx structure_value_addr;
++{
++  rtx temp;
++  rtx before_call;
++  int i;
++  rtx old_stack_level = 0;
++  int reg_parm_stack_space = 0;
++
++#ifdef REG_PARM_STACK_SPACE
++#ifdef MAYBE_REG_PARM_STACK_SPACE
++  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
++#else
++  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
++#endif
++#endif
++
++  before_call = get_last_insn ();
++
++  timevar_push (TV_INTEGRATION);
++
++  temp = expand_inline_function (fndecl, actparms, target,
++                               ignore, type,
++                               structure_value_addr);
++
++  timevar_pop (TV_INTEGRATION);
++
++  /* If inlining succeeded, return.  */
++  if (temp != (rtx) (size_t) - 1)
++    {
++      if (ACCUMULATE_OUTGOING_ARGS)
++      {
++        /* If the outgoing argument list must be preserved, push
++           the stack before executing the inlined function if it
++           makes any calls.  */
++
++        for (i = reg_parm_stack_space - 1; i >= 0; i--)
++          if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
++            break;
++
++        if (stack_arg_under_construction || i >= 0)
++          {
++            rtx first_insn
++              = before_call ? NEXT_INSN (before_call) : get_insns ();
++            rtx insn = NULL_RTX, seq;
++
++            /* Look for a call in the inline function code.
++               If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
++               nonzero then there is a call and it is not necessary
++               to scan the insns.  */
++
++            if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
++              for (insn = first_insn; insn; insn = NEXT_INSN (insn))
++                if (GET_CODE (insn) == CALL_INSN)
++                  break;
++
++            if (insn)
++              {
++                /* Reserve enough stack space so that the largest
++                   argument list of any function call in the inline
++                   function does not overlap the argument list being
++                   evaluated.  This is usually an overestimate because
++                   allocate_dynamic_stack_space reserves space for an
++                   outgoing argument list in addition to the requested
++                   space, but there is no way to ask for stack space such
++                   that an argument list of a certain length can be
++                   safely constructed.
++
++                   Add the stack space reserved for register arguments, if
++                   any, in the inline function.  What is really needed is the
++                   largest value of reg_parm_stack_space in the inline
++                   function, but that is not available.  Using the current
++                   value of reg_parm_stack_space is wrong, but gives
++                   correct results on all supported machines.  */
++
++                int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
++                              + reg_parm_stack_space);
++
++                start_sequence ();
++                emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
++                allocate_dynamic_stack_space (GEN_INT (adjust),
++                                              NULL_RTX, BITS_PER_UNIT);
++                seq = get_insns ();
++                end_sequence ();
++                emit_insn_before (seq, first_insn);
++                emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
++              }
++          }
++      }
++
++      /* If the result is equivalent to TARGET, return TARGET to simplify
++         checks in store_expr.  They can be equivalent but not equal in the
++         case of a function that returns BLKmode.  */
++      if (temp != target && rtx_equal_p (temp, target))
++      return target;
++      return temp;
++    }
++
++  /* If inlining failed, mark FNDECL as needing to be compiled
++     separately after all.  If function was declared inline,
++     give a warning.  */
++  if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
++      && optimize > 0 && !TREE_ADDRESSABLE (fndecl))
++    {
++      warning_with_decl (fndecl, "inlining failed in call to `%s'");
++      warning ("called from here");
++    }
++  (*lang_hooks.mark_addressable) (fndecl);
++  return (rtx) (size_t) - 1;
++}
++
++/* We need to pop PENDING_STACK_ADJUST bytes.  But, if the arguments
++   wouldn't fill up an even multiple of PREFERRED_UNIT_STACK_BOUNDARY
++   bytes, then we would need to push some additional bytes to pad the
++   arguments.  So, we compute an adjust to the stack pointer for an
++   amount that will leave the stack under-aligned by UNADJUSTED_ARGS_SIZE
++   bytes.  Then, when the arguments are pushed the stack will be perfectly
++   aligned.  ARGS_SIZE->CONSTANT is set to the number of bytes that should
++   be popped after the call.  Returns the adjustment.  */
++
++static int
++combine_pending_stack_adjustment_and_call (unadjusted_args_size,
++                                         args_size,
++                                         preferred_unit_stack_boundary)
++     int unadjusted_args_size;
++     struct args_size *args_size;
++     int preferred_unit_stack_boundary;
++{
++  /* The number of bytes to pop so that the stack will be
++     under-aligned by UNADJUSTED_ARGS_SIZE bytes.  */
++  HOST_WIDE_INT adjustment;
++  /* The alignment of the stack after the arguments are pushed, if we
++     just pushed the arguments without adjust the stack here.  */
++  HOST_WIDE_INT unadjusted_alignment;
++
++  unadjusted_alignment
++    = ((stack_pointer_delta + unadjusted_args_size)
++       % preferred_unit_stack_boundary);
++
++  /* We want to get rid of as many of the PENDING_STACK_ADJUST bytes
++     as possible -- leaving just enough left to cancel out the
++     UNADJUSTED_ALIGNMENT.  In other words, we want to ensure that the
++     PENDING_STACK_ADJUST is non-negative, and congruent to
++     -UNADJUSTED_ALIGNMENT modulo the PREFERRED_UNIT_STACK_BOUNDARY.  */
++
++  /* Begin by trying to pop all the bytes.  */
++  unadjusted_alignment
++    = (unadjusted_alignment
++       - (pending_stack_adjust % preferred_unit_stack_boundary));
++  adjustment = pending_stack_adjust;
++  /* Push enough additional bytes that the stack will be aligned
++     after the arguments are pushed.  */
++  if (preferred_unit_stack_boundary > 1)
++    {
++      if (unadjusted_alignment > 0)
++      adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
++      else
++      adjustment += unadjusted_alignment;
++    }
++
++  /* Now, sets ARGS_SIZE->CONSTANT so that we pop the right number of
++     bytes after the call.  The right number is the entire
++     PENDING_STACK_ADJUST less our ADJUSTMENT plus the amount required
++     by the arguments in the first place.  */
++  args_size->constant
++    = pending_stack_adjust - adjustment + unadjusted_args_size;
++
++  return adjustment;
++}
++
++/* Scan X expression if it does not dereference any argument slots
++   we already clobbered by tail call arguments (as noted in stored_args_map
++   bitmap).
++   Return nonzero if X expression dereferences such argument slots,
++   zero otherwise.  */
++
++static int
++check_sibcall_argument_overlap_1 (x)
++     rtx x;
++{
++  RTX_CODE code;
++  int i, j;
++  unsigned int k;
++  const char *fmt;
++
++  if (x == NULL_RTX)
++    return 0;
++
++  code = GET_CODE (x);
++
++  if (code == MEM)
++    {
++      if (XEXP (x, 0) == current_function_internal_arg_pointer)
++      i = 0;
++      else if (GET_CODE (XEXP (x, 0)) == PLUS
++             && XEXP (XEXP (x, 0), 0) ==
++                current_function_internal_arg_pointer
++             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
++      i = INTVAL (XEXP (XEXP (x, 0), 1));
++      else
++      return 0;
++
++#ifdef ARGS_GROW_DOWNWARD
++      i = -i - GET_MODE_SIZE (GET_MODE (x));
++#endif
++
++      for (k = 0; k < GET_MODE_SIZE (GET_MODE (x)); k++)
++      if (i + k < stored_args_map->n_bits
++          && TEST_BIT (stored_args_map, i + k))
++        return 1;
++
++      return 0;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    {
++      if (*fmt == 'e')
++      {
++        if (check_sibcall_argument_overlap_1 (XEXP (x, i)))
++          return 1;
++      }
++      else if (*fmt == 'E')
++      {
++        for (j = 0; j < XVECLEN (x, i); j++)
++          if (check_sibcall_argument_overlap_1 (XVECEXP (x, i, j)))
++            return 1;
++      }
++    }
++  return 0;
++}
++
++/* Scan sequence after INSN if it does not dereference any argument slots
++   we already clobbered by tail call arguments (as noted in stored_args_map
++   bitmap).  Add stack slots for ARG to stored_args_map bitmap afterwards.
++   Return nonzero if sequence after INSN dereferences such argument slots,
++   zero otherwise.  */
++
++static int
++check_sibcall_argument_overlap (insn, arg)
++     rtx insn;
++     struct arg_data *arg;
++{
++  int low, high;
++
++  if (insn == NULL_RTX)
++    insn = get_insns ();
++  else
++    insn = NEXT_INSN (insn);
++
++  for (; insn; insn = NEXT_INSN (insn))
++    if (INSN_P (insn)
++      && check_sibcall_argument_overlap_1 (PATTERN (insn)))
++      break;
++
++#ifdef ARGS_GROW_DOWNWARD
++  low = -arg->slot_offset.constant - arg->size.constant;
++#else
++  low = arg->slot_offset.constant;
++#endif
++
++  for (high = low + arg->size.constant; low < high; low++)
++    SET_BIT (stored_args_map, low);
++  return insn != NULL_RTX;
++}
++
++static tree
++fix_unsafe_tree (t)
++     tree t;
++{
++  switch (unsafe_for_reeval (t))
++    {
++    case 0: /* Safe.  */
++      break;
++
++    case 1: /* Mildly unsafe.  */
++      t = unsave_expr (t);
++      break;
++
++    case 2: /* Wildly unsafe.  */
++      {
++      tree var = build_decl (VAR_DECL, NULL_TREE,
++                             TREE_TYPE (t));
++      SET_DECL_RTL (var,
++                    expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL));
++      t = var;
++      }
++      break;
++
++    default:
++      abort ();
++    }
++  return t;
++}
++
++/* Generate all the code for a function call
++   and return an rtx for its value.
++   Store the value in TARGET (specified as an rtx) if convenient.
++   If the value is stored in TARGET then TARGET is returned.
++   If IGNORE is nonzero, then we ignore the value of the function call.  */
++
++rtx
++expand_call (exp, target, ignore)
++     tree exp;
++     rtx target;
++     int ignore;
++{
++  /* Nonzero if we are currently expanding a call.  */
++  static int currently_expanding_call = 0;
++
++  /* List of actual parameters.  */
++  tree actparms = TREE_OPERAND (exp, 1);
++  /* RTX for the function to be called.  */
++  rtx funexp;
++  /* Sequence of insns to perform a tail recursive "call".  */
++  rtx tail_recursion_insns = NULL_RTX;
++  /* Sequence of insns to perform a normal "call".  */
++  rtx normal_call_insns = NULL_RTX;
++  /* Sequence of insns to perform a tail recursive "call".  */
++  rtx tail_call_insns = NULL_RTX;
++  /* Data type of the function.  */
++  tree funtype;
++  /* Declaration of the function being called,
++     or 0 if the function is computed (not known by name).  */
++  tree fndecl = 0;
++  rtx insn;
++  int try_tail_call = 1;
++  int try_tail_recursion = 1;
++  int pass;
++
++  /* Register in which non-BLKmode value will be returned,
++     or 0 if no value or if value is BLKmode.  */
++  rtx valreg;
++  /* Address where we should return a BLKmode value;
++     0 if value not BLKmode.  */
++  rtx structure_value_addr = 0;
++  /* Nonzero if that address is being passed by treating it as
++     an extra, implicit first parameter.  Otherwise,
++     it is passed by being copied directly into struct_value_rtx.  */
++  int structure_value_addr_parm = 0;
++  /* Size of aggregate value wanted, or zero if none wanted
++     or if we are using the non-reentrant PCC calling convention
++     or expecting the value in registers.  */
++  HOST_WIDE_INT struct_value_size = 0;
++  /* Nonzero if called function returns an aggregate in memory PCC style,
++     by returning the address of where to find it.  */
++  int pcc_struct_value = 0;
++
++  /* Number of actual parameters in this call, including struct value addr.  */
++  int num_actuals;
++  /* Number of named args.  Args after this are anonymous ones
++     and they must all go on the stack.  */
++  int n_named_args;
++
++  /* Vector of information about each argument.
++     Arguments are numbered in the order they will be pushed,
++     not the order they are written.  */
++  struct arg_data *args;
++
++  /* Total size in bytes of all the stack-parms scanned so far.  */
++  struct args_size args_size;
++  struct args_size adjusted_args_size;
++  /* Size of arguments before any adjustments (such as rounding).  */
++  int unadjusted_args_size;
++  /* Data on reg parms scanned so far.  */
++  CUMULATIVE_ARGS args_so_far;
++  /* Nonzero if a reg parm has been scanned.  */
++  int reg_parm_seen;
++  /* Nonzero if this is an indirect function call.  */
++
++  /* Nonzero if we must avoid push-insns in the args for this call.
++     If stack space is allocated for register parameters, but not by the
++     caller, then it is preallocated in the fixed part of the stack frame.
++     So the entire argument block must then be preallocated (i.e., we
++     ignore PUSH_ROUNDING in that case).  */
++
++  int must_preallocate = !PUSH_ARGS;
++
++  /* Size of the stack reserved for parameter registers.  */
++  int reg_parm_stack_space = 0;
++
++  /* Address of space preallocated for stack parms
++     (on machines that lack push insns), or 0 if space not preallocated.  */
++  rtx argblock = 0;
++
++  /* Mask of ECF_ flags.  */
++  int flags = 0;
++  /* Nonzero if this is a call to an inline function.  */
++  int is_integrable = 0;
++#ifdef REG_PARM_STACK_SPACE
++  /* Define the boundary of the register parm stack space that needs to be
++     save, if any.  */
++  int low_to_save = -1, high_to_save;
++  rtx save_area = 0;          /* Place that it is saved */
++#endif
++
++  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
++  char *initial_stack_usage_map = stack_usage_map;
++  int old_stack_arg_under_construction = 0;
++
++  rtx old_stack_level = 0;
++  int old_pending_adj = 0;
++  int old_inhibit_defer_pop = inhibit_defer_pop;
++  int old_stack_allocated;
++  rtx call_fusage;
++  tree p = TREE_OPERAND (exp, 0);
++  tree addr = TREE_OPERAND (exp, 0);
++  int i;
++  /* The alignment of the stack, in bits.  */
++  HOST_WIDE_INT preferred_stack_boundary;
++  /* The alignment of the stack, in bytes.  */
++  HOST_WIDE_INT preferred_unit_stack_boundary;
++
++  /* See if this is "nothrow" function call.  */
++  if (TREE_NOTHROW (exp))
++    flags |= ECF_NOTHROW;
++
++  /* See if we can find a DECL-node for the actual function.
++     As a result, decide whether this is a call to an integrable function.  */
++
++  fndecl = get_callee_fndecl (exp);
++  if (fndecl)
++    {
++      if (!flag_no_inline
++        && fndecl != current_function_decl
++        && DECL_INLINE (fndecl)
++        && DECL_SAVED_INSNS (fndecl)
++        && DECL_SAVED_INSNS (fndecl)->inlinable)
++      is_integrable = 1;
++      else if (! TREE_ADDRESSABLE (fndecl))
++      {
++        /* In case this function later becomes inlinable,
++           record that there was already a non-inline call to it.
++
++           Use abstraction instead of setting TREE_ADDRESSABLE
++           directly.  */
++        if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
++            && optimize > 0)
++          {
++            warning_with_decl (fndecl, "can't inline call to `%s'");
++            warning ("called from here");
++          }
++        (*lang_hooks.mark_addressable) (fndecl);
++      }
++
++      flags |= flags_from_decl_or_type (fndecl);
++    }
++
++  /* If we don't have specific function to call, see if we have a
++     attributes set in the type.  */
++  else
++    flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
++
++#ifdef REG_PARM_STACK_SPACE
++#ifdef MAYBE_REG_PARM_STACK_SPACE
++  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
++#else
++  reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
++#endif
++#endif
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++  if (reg_parm_stack_space > 0 && PUSH_ARGS)
++    must_preallocate = 1;
++#endif
++
++  /* Warn if this value is an aggregate type,
++     regardless of which calling convention we are using for it.  */
++  if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
++    warning ("function call has aggregate value");
++
++  /* Set up a place to return a structure.  */
++
++  /* Cater to broken compilers.  */
++  if (aggregate_value_p (exp))
++    {
++      /* This call returns a big structure.  */
++      flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
++
++#ifdef PCC_STATIC_STRUCT_RETURN
++      {
++      pcc_struct_value = 1;
++      /* Easier than making that case work right.  */
++      if (is_integrable)
++        {
++          /* In case this is a static function, note that it has been
++             used.  */
++          if (! TREE_ADDRESSABLE (fndecl))
++            (*lang_hooks.mark_addressable) (fndecl);
++          is_integrable = 0;
++        }
++      }
++#else /* not PCC_STATIC_STRUCT_RETURN */
++      {
++      struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
++
++      if (target && GET_CODE (target) == MEM)
++        structure_value_addr = XEXP (target, 0);
++      else
++        {
++          /* For variable-sized objects, we must be called with a target
++             specified.  If we were to allocate space on the stack here,
++             we would have no way of knowing when to free it.  */
++          rtx d = assign_temp (TREE_TYPE (exp), 1, 1, 1);
++
++          mark_temp_addr_taken (d);
++          structure_value_addr = XEXP (d, 0);
++          target = 0;
++        }
++      }
++#endif /* not PCC_STATIC_STRUCT_RETURN */
++    }
++
++  /* If called function is inline, try to integrate it.  */
++
++  if (is_integrable)
++    {
++      rtx temp = try_to_integrate (fndecl, actparms, target,
++                                 ignore, TREE_TYPE (exp),
++                                 structure_value_addr);
++      if (temp != (rtx) (size_t) - 1)
++      return temp;
++    }
++
++  /* Figure out the amount to which the stack should be aligned.  */
++  preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
++
++  /* Operand 0 is a pointer-to-function; get the type of the function.  */
++  funtype = TREE_TYPE (addr);
++  if (! POINTER_TYPE_P (funtype))
++    abort ();
++  funtype = TREE_TYPE (funtype);
++
++  /* See if this is a call to a function that can return more than once
++     or a call to longjmp or malloc.  */
++  flags |= special_function_p (fndecl, flags);
++
++  if (flags & ECF_MAY_BE_ALLOCA)
++    current_function_calls_alloca = 1;
++
++  /* If struct_value_rtx is 0, it means pass the address
++     as if it were an extra parameter.  */
++  if (structure_value_addr && struct_value_rtx == 0)
++    {
++      /* If structure_value_addr is a REG other than
++       virtual_outgoing_args_rtx, we can use always use it.  If it
++       is not a REG, we must always copy it into a register.
++       If it is virtual_outgoing_args_rtx, we must copy it to another
++       register in some cases.  */
++      rtx temp = (GET_CODE (structure_value_addr) != REG
++                || (ACCUMULATE_OUTGOING_ARGS
++                    && stack_arg_under_construction
++                    && structure_value_addr == virtual_outgoing_args_rtx)
++                ? copy_addr_to_reg (structure_value_addr)
++                : structure_value_addr);
++
++      actparms
++      = tree_cons (error_mark_node,
++                   make_tree (build_pointer_type (TREE_TYPE (funtype)),
++                              temp),
++                   actparms);
++      structure_value_addr_parm = 1;
++    }
++
++  /* Count the arguments and set NUM_ACTUALS.  */
++  for (p = actparms, num_actuals = 0; p; p = TREE_CHAIN (p))
++    num_actuals++;
++
++  /* Compute number of named args.
++     Normally, don't include the last named arg if anonymous args follow.
++     We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero.
++     (If no anonymous args follow, the result of list_length is actually
++     one too large.  This is harmless.)
++
++     If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is
++     zero, this machine will be able to place unnamed args that were
++     passed in registers into the stack.  So treat all args as named.
++     This allows the insns emitting for a specific argument list to be
++     independent of the function declaration.
++
++     If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any
++     reliable way to pass unnamed args in registers, so we must force
++     them into memory.  */
++
++  if ((STRICT_ARGUMENT_NAMING
++       || ! PRETEND_OUTGOING_VARARGS_NAMED)
++      && TYPE_ARG_TYPES (funtype) != 0)
++    n_named_args
++      = (list_length (TYPE_ARG_TYPES (funtype))
++       /* Don't include the last named arg.  */
++       - (STRICT_ARGUMENT_NAMING ? 0 : 1)
++       /* Count the struct value address, if it is passed as a parm.  */
++       + structure_value_addr_parm);
++  else
++    /* If we know nothing, treat all args as named.  */
++    n_named_args = num_actuals;
++
++  /* Start updating where the next arg would go.
++
++     On some machines (such as the PA) indirect calls have a different
++     calling convention than normal calls.  The last argument in
++     INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
++     or not.  */
++  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
++
++  /* Make a vector to hold all the information about each arg.  */
++  args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
++  memset ((char *) args, 0, num_actuals * sizeof (struct arg_data));
++
++  /* Build up entries in the ARGS array, compute the size of the
++     arguments into ARGS_SIZE, etc.  */
++  initialize_argument_information (num_actuals, args, &args_size,
++                                 n_named_args, actparms, fndecl,
++                                 &args_so_far, reg_parm_stack_space,
++                                 &old_stack_level, &old_pending_adj,
++                                 &must_preallocate, &flags);
++
++  if (args_size.var)
++    {
++      /* If this function requires a variable-sized argument list, don't
++       try to make a cse'able block for this call.  We may be able to
++       do this eventually, but it is too complicated to keep track of
++       what insns go in the cse'able block and which don't.  */
++
++      flags &= ~ECF_LIBCALL_BLOCK;
++      must_preallocate = 1;
++    }
++
++  /* Now make final decision about preallocating stack space.  */
++  must_preallocate = finalize_must_preallocate (must_preallocate,
++                                              num_actuals, args,
++                                              &args_size);
++
++  /* If the structure value address will reference the stack pointer, we
++     must stabilize it.  We don't need to do this if we know that we are
++     not going to adjust the stack pointer in processing this call.  */
++
++  if (structure_value_addr
++      && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr)
++        || reg_mentioned_p (virtual_outgoing_args_rtx,
++                            structure_value_addr))
++      && (args_size.var
++        || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
++    structure_value_addr = copy_to_reg (structure_value_addr);
++
++  /* Tail calls can make things harder to debug, and we're traditionally
++     pushed these optimizations into -O2.  Don't try if we're already
++     expanding a call, as that means we're an argument.  Don't try if
++     there's cleanups, as we know there's code to follow the call.
++
++     If rtx_equal_function_value_matters is false, that means we've
++     finished with regular parsing.  Which means that some of the
++     machinery we use to generate tail-calls is no longer in place.
++     This is most often true of sjlj-exceptions, which we couldn't
++     tail-call to anyway.  */
++
++  if (currently_expanding_call++ != 0
++      || !flag_optimize_sibling_calls
++      || !rtx_equal_function_value_matters
++      || any_pending_cleanups (1)
++      || args_size.var)
++    try_tail_call = try_tail_recursion = 0;
++
++  /* Tail recursion fails, when we are not dealing with recursive calls.  */
++  if (!try_tail_recursion
++      || TREE_CODE (addr) != ADDR_EXPR
++      || TREE_OPERAND (addr, 0) != current_function_decl)
++    try_tail_recursion = 0;
++
++  /*  Rest of purposes for tail call optimizations to fail.  */
++  if (
++#ifdef HAVE_sibcall_epilogue
++      !HAVE_sibcall_epilogue
++#else
++      1
++#endif
++      || !try_tail_call
++      /* Doing sibling call optimization needs some work, since
++       structure_value_addr can be allocated on the stack.
++       It does not seem worth the effort since few optimizable
++       sibling calls will return a structure.  */
++      || structure_value_addr != NULL_RTX
++      /* If the register holding the address is a callee saved
++       register, then we lose.  We have no way to prevent that,
++       so we only allow calls to named functions.  */
++      /* ??? This could be done by having the insn constraints
++       use a register class that is all call-clobbered.  Any
++       reload insns generated to fix things up would appear
++       before the sibcall_epilogue.  */
++      || fndecl == NULL_TREE
++      || (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP | ECF_NORETURN))
++      || !FUNCTION_OK_FOR_SIBCALL (fndecl)
++      /* If this function requires more stack slots than the current
++       function, we cannot change it into a sibling call.  */
++      || args_size.constant > current_function_args_size
++      /* If the callee pops its own arguments, then it must pop exactly
++       the same number of arguments as the current function.  */
++      || (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
++        != RETURN_POPS_ARGS (current_function_decl,
++                             TREE_TYPE (current_function_decl),
++                             current_function_args_size))
++      || !(*lang_hooks.decls.ok_for_sibcall) (fndecl))
++    try_tail_call = 0;
++
++  if (try_tail_call || try_tail_recursion)
++    {
++      int end, inc;
++      actparms = NULL_TREE;
++      /* Ok, we're going to give the tail call the old college try.
++       This means we're going to evaluate the function arguments
++       up to three times.  There are two degrees of badness we can
++       encounter, those that can be unsaved and those that can't.
++       (See unsafe_for_reeval commentary for details.)
++
++       Generate a new argument list.  Pass safe arguments through
++       unchanged.  For the easy badness wrap them in UNSAVE_EXPRs.
++       For hard badness, evaluate them now and put their resulting
++       rtx in a temporary VAR_DECL.
++
++       initialize_argument_information has ordered the array for the
++       order to be pushed, and we must remember this when reconstructing
++       the original argument order.  */
++
++      if (PUSH_ARGS_REVERSED)
++      {
++        inc = 1;
++        i = 0;
++        end = num_actuals;
++      }
++      else
++      {
++        inc = -1;
++        i = num_actuals - 1;
++        end = -1;
++      }
++
++      for (; i != end; i += inc)
++      {
++          args[i].tree_value = fix_unsafe_tree (args[i].tree_value);
++        /* We need to build actparms for optimize_tail_recursion.  We can
++           safely trash away TREE_PURPOSE, since it is unused by this
++           function.  */
++        if (try_tail_recursion)
++          actparms = tree_cons (NULL_TREE, args[i].tree_value, actparms);
++      }
++      /* Do the same for the function address if it is an expression. */
++      if (!fndecl)
++        addr = fix_unsafe_tree (addr);
++      /* Expanding one of those dangerous arguments could have added
++       cleanups, but otherwise give it a whirl.  */
++      if (any_pending_cleanups (1))
++      try_tail_call = try_tail_recursion = 0;
++    }
++
++  /* Generate a tail recursion sequence when calling ourselves.  */
++
++  if (try_tail_recursion)
++    {
++      /* We want to emit any pending stack adjustments before the tail
++       recursion "call".  That way we know any adjustment after the tail
++       recursion call can be ignored if we indeed use the tail recursion
++       call expansion.  */
++      int save_pending_stack_adjust = pending_stack_adjust;
++      int save_stack_pointer_delta = stack_pointer_delta;
++
++      /* Emit any queued insns now; otherwise they would end up in
++       only one of the alternates.  */
++      emit_queue ();
++
++      /* Use a new sequence to hold any RTL we generate.  We do not even
++       know if we will use this RTL yet.  The final decision can not be
++       made until after RTL generation for the entire function is
++       complete.  */
++      start_sequence ();
++      /* If expanding any of the arguments creates cleanups, we can't
++       do a tailcall.  So, we'll need to pop the pending cleanups
++       list.  If, however, all goes well, and there are no cleanups
++       then the call to expand_start_target_temps will have no
++       effect.  */
++      expand_start_target_temps ();
++      if (optimize_tail_recursion (actparms, get_last_insn ()))
++      {
++        if (any_pending_cleanups (1))
++          try_tail_call = try_tail_recursion = 0;
++        else
++          tail_recursion_insns = get_insns ();
++      }
++      expand_end_target_temps ();
++      end_sequence ();
++
++      /* Restore the original pending stack adjustment for the sibling and
++       normal call cases below.  */
++      pending_stack_adjust = save_pending_stack_adjust;
++      stack_pointer_delta = save_stack_pointer_delta;
++    }
++
++  if (profile_arc_flag && (flags & ECF_FORK_OR_EXEC))
++    {
++      /* A fork duplicates the profile information, and an exec discards
++       it.  We can't rely on fork/exec to be paired.  So write out the
++       profile information we have gathered so far, and clear it.  */
++      /* ??? When Linux's __clone is called with CLONE_VM set, profiling
++       is subject to race conditions, just as with multithreaded
++       programs.  */
++
++      emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"),
++                       LCT_ALWAYS_RETURN,
++                       VOIDmode, 0);
++    }
++
++  /* Ensure current function's preferred stack boundary is at least
++     what we need.  We don't have to increase alignment for recursive
++     functions.  */
++  if (cfun->preferred_stack_boundary < preferred_stack_boundary
++      && fndecl != current_function_decl)
++    cfun->preferred_stack_boundary = preferred_stack_boundary;
++
++  preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
++
++  function_call_count++;
++
++  /* We want to make two insn chains; one for a sibling call, the other
++     for a normal call.  We will select one of the two chains after
++     initial RTL generation is complete.  */
++  for (pass = 0; pass < 2; pass++)
++    {
++      int sibcall_failure = 0;
++      /* We want to emit any pending stack adjustments before the tail
++       recursion "call".  That way we know any adjustment after the tail
++       recursion call can be ignored if we indeed use the tail recursion
++       call expansion.  */
++      int save_pending_stack_adjust = 0;
++      int save_stack_pointer_delta = 0;
++      rtx insns;
++      rtx before_call, next_arg_reg;
++
++      if (pass == 0)
++      {
++        if (! try_tail_call)
++          continue;
++
++        /* Emit any queued insns now; otherwise they would end up in
++             only one of the alternates.  */
++        emit_queue ();
++
++        /* State variables we need to save and restore between
++           iterations.  */
++        save_pending_stack_adjust = pending_stack_adjust;
++        save_stack_pointer_delta = stack_pointer_delta;
++      }
++      if (pass)
++      flags &= ~ECF_SIBCALL;
++      else
++      flags |= ECF_SIBCALL;
++
++      /* Other state variables that we must reinitialize each time
++       through the loop (that are not initialized by the loop itself).  */
++      argblock = 0;
++      call_fusage = 0;
++
++      /* Start a new sequence for the normal call case.
++
++       From this point on, if the sibling call fails, we want to set
++       sibcall_failure instead of continuing the loop.  */
++      start_sequence ();
++
++      if (pass == 0)
++      {
++        /* We know at this point that there are not currently any
++           pending cleanups.  If, however, in the process of evaluating
++           the arguments we were to create some, we'll need to be
++           able to get rid of them.  */
++        expand_start_target_temps ();
++      }
++
++      /* Don't let pending stack adjusts add up to too much.
++       Also, do all pending adjustments now if there is any chance
++       this might be a call to alloca or if we are expanding a sibling
++       call sequence or if we are calling a function that is to return
++       with stack pointer depressed.  */
++      if (pending_stack_adjust >= 32
++        || (pending_stack_adjust > 0
++            && (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
++        || pass == 0)
++      do_pending_stack_adjust ();
++
++      /* When calling a const function, we must pop the stack args right away,
++       so that the pop is deleted or moved with the call.  */
++      if (pass && (flags & ECF_LIBCALL_BLOCK))
++      NO_DEFER_POP;
++
++#ifdef FINAL_REG_PARM_STACK_SPACE
++      reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
++                                                       args_size.var);
++#endif
++      /* Precompute any arguments as needed.  */
++      if (pass)
++      precompute_arguments (flags, num_actuals, args);
++
++      /* Now we are about to start emitting insns that can be deleted
++       if a libcall is deleted.  */
++      if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
++      start_sequence ();
++
++      adjusted_args_size = args_size;
++      /* Compute the actual size of the argument block required.  The variable
++       and constant sizes must be combined, the size may have to be rounded,
++       and there may be a minimum required size.  When generating a sibcall
++       pattern, do not round up, since we'll be re-using whatever space our
++       caller provided.  */
++      unadjusted_args_size
++      = compute_argument_block_size (reg_parm_stack_space,
++                                     &adjusted_args_size,
++                                     (pass == 0 ? 0
++                                      : preferred_stack_boundary));
++
++      old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
++
++      /* The argument block when performing a sibling call is the
++         incoming argument block.  */
++      if (pass == 0)
++      {
++        argblock = virtual_incoming_args_rtx;
++        argblock
++#ifdef STACK_GROWS_DOWNWARD
++          = plus_constant (argblock, current_function_pretend_args_size);
++#else
++          = plus_constant (argblock, -current_function_pretend_args_size);
++#endif
++        stored_args_map = sbitmap_alloc (args_size.constant);
++        sbitmap_zero (stored_args_map);
++      }
++
++      /* If we have no actual push instructions, or shouldn't use them,
++       make space for all args right now.  */
++      else if (adjusted_args_size.var != 0)
++      {
++        if (old_stack_level == 0)
++          {
++            emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
++            old_pending_adj = pending_stack_adjust;
++            pending_stack_adjust = 0;
++            /* stack_arg_under_construction says whether a stack arg is
++               being constructed at the old stack level.  Pushing the stack
++               gets a clean outgoing argument block.  */
++            old_stack_arg_under_construction = stack_arg_under_construction;
++            stack_arg_under_construction = 0;
++          }
++        argblock = push_block (ARGS_SIZE_RTX (adjusted_args_size), 0, 0);
++      }
++      else
++      {
++        /* Note that we must go through the motions of allocating an argument
++           block even if the size is zero because we may be storing args
++           in the area reserved for register arguments, which may be part of
++           the stack frame.  */
++
++        int needed = adjusted_args_size.constant;
++
++        /* Store the maximum argument space used.  It will be pushed by
++           the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow
++           checking).  */
++
++        if (needed > current_function_outgoing_args_size)
++          current_function_outgoing_args_size = needed;
++
++        if (must_preallocate)
++          {
++            if (ACCUMULATE_OUTGOING_ARGS)
++              {
++                /* Since the stack pointer will never be pushed, it is
++                   possible for the evaluation of a parm to clobber
++                   something we have already written to the stack.
++                   Since most function calls on RISC machines do not use
++                   the stack, this is uncommon, but must work correctly.
++
++                   Therefore, we save any area of the stack that was already
++                   written and that we are using.  Here we set up to do this
++                   by making a new stack usage map from the old one.  The
++                   actual save will be done by store_one_arg.
++
++                   Another approach might be to try to reorder the argument
++                   evaluations to avoid this conflicting stack usage.  */
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++                /* Since we will be writing into the entire argument area,
++                   the map must be allocated for its entire size, not just
++                   the part that is the responsibility of the caller.  */
++                needed += reg_parm_stack_space;
++#endif
++
++#ifdef ARGS_GROW_DOWNWARD
++                highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
++                                                   needed + 1);
++#else
++                highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
++                                                   needed);
++#endif
++                stack_usage_map
++                  = (char *) alloca (highest_outgoing_arg_in_use);
++
++                if (initial_highest_arg_in_use)
++                  memcpy (stack_usage_map, initial_stack_usage_map,
++                          initial_highest_arg_in_use);
++
++                if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
++                  memset (&stack_usage_map[initial_highest_arg_in_use], 0,
++                         (highest_outgoing_arg_in_use
++                          - initial_highest_arg_in_use));
++                needed = 0;
++
++                /* The address of the outgoing argument list must not be
++                   copied to a register here, because argblock would be left
++                   pointing to the wrong place after the call to
++                   allocate_dynamic_stack_space below.  */
++
++                argblock = virtual_outgoing_args_rtx;
++              }
++            else
++              {
++                if (inhibit_defer_pop == 0)
++                  {
++                    /* Try to reuse some or all of the pending_stack_adjust
++                       to get this space.  */
++                    needed
++                      = (combine_pending_stack_adjustment_and_call
++                         (unadjusted_args_size,
++                          &adjusted_args_size,
++                          preferred_unit_stack_boundary));
++
++                    /* combine_pending_stack_adjustment_and_call computes
++                       an adjustment before the arguments are allocated.
++                       Account for them and see whether or not the stack
++                       needs to go up or down.  */
++                    needed = unadjusted_args_size - needed;
++
++                    if (needed < 0)
++                      {
++                        /* We're releasing stack space.  */
++                        /* ??? We can avoid any adjustment at all if we're
++                           already aligned.  FIXME.  */
++                        pending_stack_adjust = -needed;
++                        do_pending_stack_adjust ();
++                        needed = 0;
++                      }
++                    else
++                      /* We need to allocate space.  We'll do that in
++                         push_block below.  */
++                      pending_stack_adjust = 0;
++                  }
++
++                /* Special case this because overhead of `push_block' in
++                   this case is non-trivial.  */
++                if (needed == 0)
++                  argblock = virtual_outgoing_args_rtx;
++                else
++                  argblock = push_block (GEN_INT (needed), 0, 0);
++
++                /* We only really need to call `copy_to_reg' in the case
++                   where push insns are going to be used to pass ARGBLOCK
++                   to a function call in ARGS.  In that case, the stack
++                   pointer changes value from the allocation point to the
++                   call point, and hence the value of
++                   VIRTUAL_OUTGOING_ARGS_RTX changes as well.  But might
++                   as well always do it.  */
++                argblock = copy_to_reg (argblock);
++
++                /* The save/restore code in store_one_arg handles all
++                   cases except one: a constructor call (including a C
++                   function returning a BLKmode struct) to initialize
++                   an argument.  */
++                if (stack_arg_under_construction)
++                  {
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++                    rtx push_size = GEN_INT (reg_parm_stack_space
++                                             + adjusted_args_size.constant);
++#else
++                    rtx push_size = GEN_INT (adjusted_args_size.constant);
++#endif
++                    if (old_stack_level == 0)
++                      {
++                        emit_stack_save (SAVE_BLOCK, &old_stack_level,
++                                         NULL_RTX);
++                        old_pending_adj = pending_stack_adjust;
++                        pending_stack_adjust = 0;
++                        /* stack_arg_under_construction says whether a stack
++                           arg is being constructed at the old stack level.
++                           Pushing the stack gets a clean outgoing argument
++                           block.  */
++                        old_stack_arg_under_construction
++                          = stack_arg_under_construction;
++                        stack_arg_under_construction = 0;
++                        /* Make a new map for the new argument list.  */
++                        stack_usage_map = (char *)
++                          alloca (highest_outgoing_arg_in_use);
++                        memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
++                        highest_outgoing_arg_in_use = 0;
++                      }
++                    allocate_dynamic_stack_space (push_size, NULL_RTX,
++                                                  BITS_PER_UNIT);
++                  }
++                /* If argument evaluation might modify the stack pointer,
++                   copy the address of the argument list to a register.  */
++                for (i = 0; i < num_actuals; i++)
++                  if (args[i].pass_on_stack)
++                    {
++                      argblock = copy_addr_to_reg (argblock);
++                      break;
++                    }
++              }
++          }
++      }
++
++      compute_argument_addresses (args, argblock, num_actuals);
++
++      /* If we push args individually in reverse order, perform stack alignment
++       before the first push (the last arg).  */
++      if (PUSH_ARGS_REVERSED && argblock == 0
++        && adjusted_args_size.constant != unadjusted_args_size)
++      {
++        /* When the stack adjustment is pending, we get better code
++           by combining the adjustments.  */
++        if (pending_stack_adjust
++            && ! (flags & ECF_LIBCALL_BLOCK)
++            && ! inhibit_defer_pop)
++          {
++            pending_stack_adjust
++              = (combine_pending_stack_adjustment_and_call
++                 (unadjusted_args_size,
++                  &adjusted_args_size,
++                  preferred_unit_stack_boundary));
++            do_pending_stack_adjust ();
++          }
++        else if (argblock == 0)
++          anti_adjust_stack (GEN_INT (adjusted_args_size.constant
++                                      - unadjusted_args_size));
++      }
++      /* Now that the stack is properly aligned, pops can't safely
++       be deferred during the evaluation of the arguments.  */
++      NO_DEFER_POP;
++
++      funexp = rtx_for_function_call (fndecl, addr);
++
++      /* Figure out the register where the value, if any, will come back.  */
++      valreg = 0;
++      if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
++        && ! structure_value_addr)
++      {
++        if (pcc_struct_value)
++          valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
++                                        fndecl, (pass == 0));
++        else
++          valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
++      }
++
++      /* Precompute all register parameters.  It isn't safe to compute anything
++       once we have started filling any specific hard regs.  */
++      precompute_register_parameters (num_actuals, args, &reg_parm_seen);
++
++#ifdef REG_PARM_STACK_SPACE
++      /* Save the fixed argument area if it's part of the caller's frame and
++       is clobbered by argument setup for this call.  */
++      if (ACCUMULATE_OUTGOING_ARGS && pass)
++      save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
++                                            &low_to_save, &high_to_save);
++#endif
++
++      /* Now store (and compute if necessary) all non-register parms.
++       These come before register parms, since they can require block-moves,
++       which could clobber the registers used for register parms.
++       Parms which have partial registers are not stored here,
++       but we do preallocate space here if they want that.  */
++
++      for (i = 0; i < num_actuals; i++)
++      if (args[i].reg == 0 || args[i].pass_on_stack)
++        {
++          rtx before_arg = get_last_insn ();
++
++          if (store_one_arg (&args[i], argblock, flags,
++                             adjusted_args_size.var != 0,
++                             reg_parm_stack_space)
++              || (pass == 0
++                  && check_sibcall_argument_overlap (before_arg,
++                                                     &args[i])))
++            sibcall_failure = 1;
++        }
++
++      /* If we have a parm that is passed in registers but not in memory
++       and whose alignment does not permit a direct copy into registers,
++       make a group of pseudos that correspond to each register that we
++       will later fill.  */
++      if (STRICT_ALIGNMENT)
++      store_unaligned_arguments_into_pseudos (args, num_actuals);
++
++      /* Now store any partially-in-registers parm.
++       This is the last place a block-move can happen.  */
++      if (reg_parm_seen)
++      for (i = 0; i < num_actuals; i++)
++        if (args[i].partial != 0 && ! args[i].pass_on_stack)
++          {
++            rtx before_arg = get_last_insn ();
++
++            if (store_one_arg (&args[i], argblock, flags,
++                               adjusted_args_size.var != 0,
++                               reg_parm_stack_space)
++                || (pass == 0
++                    && check_sibcall_argument_overlap (before_arg,
++                                                       &args[i])))
++              sibcall_failure = 1;
++          }
++
++      /* If we pushed args in forward order, perform stack alignment
++       after pushing the last arg.  */
++      if (!PUSH_ARGS_REVERSED && argblock == 0)
++      anti_adjust_stack (GEN_INT (adjusted_args_size.constant
++                                  - unadjusted_args_size));
++
++      /* If register arguments require space on the stack and stack space
++       was not preallocated, allocate stack space here for arguments
++       passed in registers.  */
++#ifdef OUTGOING_REG_PARM_STACK_SPACE
++      if (!ACCUMULATE_OUTGOING_ARGS
++        && must_preallocate == 0 && reg_parm_stack_space > 0)
++      anti_adjust_stack (GEN_INT (reg_parm_stack_space));
++#endif
++
++      /* Pass the function the address in which to return a
++       structure value.  */
++      if (pass != 0 && structure_value_addr && ! structure_value_addr_parm)
++      {
++        emit_move_insn (struct_value_rtx,
++                        force_reg (Pmode,
++                                   force_operand (structure_value_addr,
++                                                  NULL_RTX)));
++
++        if (GET_CODE (struct_value_rtx) == REG)
++          use_reg (&call_fusage, struct_value_rtx);
++      }
++
++      funexp = prepare_call_address (funexp, fndecl, &call_fusage,
++                                   reg_parm_seen, pass == 0);
++
++      load_register_parameters (args, num_actuals, &call_fusage, flags);
++
++      /* Perform postincrements before actually calling the function.  */
++      emit_queue ();
++
++      /* Save a pointer to the last insn before the call, so that we can
++       later safely search backwards to find the CALL_INSN.  */
++      before_call = get_last_insn ();
++
++      /* Set up next argument register.  For sibling calls on machines
++       with register windows this should be the incoming register.  */
++#ifdef FUNCTION_INCOMING_ARG
++      if (pass == 0)
++      next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
++                                            void_type_node, 1);
++      else
++#endif
++      next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
++                                   void_type_node, 1);
++
++      /* All arguments and registers used for the call must be set up by
++       now!  */
++
++      /* Stack must be properly aligned now.  */
++      if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
++      abort ();
++
++      /* Generate the actual call instruction.  */
++      emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
++                 adjusted_args_size.constant, struct_value_size,
++                 next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
++                 flags, & args_so_far);
++
++      /* Verify that we've deallocated all the stack we used.  */
++      if (pass
++        && old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
++      abort ();
++
++      /* If call is cse'able, make appropriate pair of reg-notes around it.
++       Test valreg so we don't crash; may safely ignore `const'
++       if return type is void.  Disable for PARALLEL return values, because
++       we have no way to move such values into a pseudo register.  */
++      if (pass && (flags & ECF_LIBCALL_BLOCK))
++      {
++        rtx insns;
++
++        if (valreg == 0 || GET_CODE (valreg) == PARALLEL)
++          {
++            insns = get_insns ();
++            end_sequence ();
++            emit_insn (insns);
++          }
++        else
++          {
++            rtx note = 0;
++            rtx temp = gen_reg_rtx (GET_MODE (valreg));
++
++            /* Mark the return value as a pointer if needed.  */
++            if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
++              mark_reg_pointer (temp,
++                                TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
++
++            /* Construct an "equal form" for the value which mentions all the
++               arguments in order as well as the function name.  */
++            for (i = 0; i < num_actuals; i++)
++              note = gen_rtx_EXPR_LIST (VOIDmode,
++                                        args[i].initial_value, note);
++            note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
++
++            insns = get_insns ();
++            end_sequence ();
++
++            if (flags & ECF_PURE)
++              note = gen_rtx_EXPR_LIST (VOIDmode,
++                      gen_rtx_USE (VOIDmode,
++                                   gen_rtx_MEM (BLKmode,
++                                                gen_rtx_SCRATCH (VOIDmode))),
++                      note);
++
++            emit_libcall_block (insns, temp, valreg, note);
++
++            valreg = temp;
++          }
++      }
++      else if (pass && (flags & ECF_MALLOC))
++      {
++        rtx temp = gen_reg_rtx (GET_MODE (valreg));
++        rtx last, insns;
++
++        /* The return value from a malloc-like function is a pointer.  */
++        if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
++          mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
++
++        emit_move_insn (temp, valreg);
++
++        /* The return value from a malloc-like function can not alias
++           anything else.  */
++        last = get_last_insn ();
++        REG_NOTES (last) =
++          gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
++
++        /* Write out the sequence.  */
++        insns = get_insns ();
++        end_sequence ();
++        emit_insn (insns);
++        valreg = temp;
++      }
++
++      /* For calls to `setjmp', etc., inform flow.c it should complain
++       if nonvolatile values are live.  For functions that cannot return,
++       inform flow that control does not fall through.  */
++
++      if ((flags & (ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
++      {
++        /* The barrier must be emitted
++           immediately after the CALL_INSN.  Some ports emit more
++           than just a CALL_INSN above, so we must search for it here.  */
++
++        rtx last = get_last_insn ();
++        while (GET_CODE (last) != CALL_INSN)
++          {
++            last = PREV_INSN (last);
++            /* There was no CALL_INSN?  */
++            if (last == before_call)
++              abort ();
++          }
++
++        emit_barrier_after (last);
++      }
++
++      if (flags & ECF_LONGJMP)
++      current_function_calls_longjmp = 1;
++
++      /* If this function is returning into a memory location marked as
++       readonly, it means it is initializing that location.  But we normally
++       treat functions as not clobbering such locations, so we need to
++       specify that this one does.  */
++      if (target != 0 && GET_CODE (target) == MEM
++        && structure_value_addr != 0 && RTX_UNCHANGING_P (target))
++      emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
++
++      /* If value type not void, return an rtx for the value.  */
++
++      /* If there are cleanups to be called, don't use a hard reg as target.
++       We need to double check this and see if it matters anymore.  */
++      if (any_pending_cleanups (1))
++      {
++        if (target && REG_P (target)
++            && REGNO (target) < FIRST_PSEUDO_REGISTER)
++          target = 0;
++        sibcall_failure = 1;
++      }
++
++      if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
++        || ignore)
++      target = const0_rtx;
++      else if (structure_value_addr)
++      {
++        if (target == 0 || GET_CODE (target) != MEM)
++          {
++            target
++              = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
++                             memory_address (TYPE_MODE (TREE_TYPE (exp)),
++                                             structure_value_addr));
++            set_mem_attributes (target, exp, 1);
++          }
++      }
++      else if (pcc_struct_value)
++      {
++        /* This is the special C++ case where we need to
++           know what the true target was.  We take care to
++           never use this value more than once in one expression.  */
++        target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
++                              copy_to_reg (valreg));
++        set_mem_attributes (target, exp, 1);
++      }
++      /* Handle calls that return values in multiple non-contiguous locations.
++       The Irix 6 ABI has examples of this.  */
++      else if (GET_CODE (valreg) == PARALLEL)
++      {
++        if (target == 0)
++          {
++            /* This will only be assigned once, so it can be readonly.  */
++            tree nt = build_qualified_type (TREE_TYPE (exp),
++                                            (TYPE_QUALS (TREE_TYPE (exp))
++                                             | TYPE_QUAL_CONST));
++
++            target = assign_temp (nt, 0, 1, 1);
++            preserve_temp_slots (target);
++          }
++
++        if (! rtx_equal_p (target, valreg))
++          emit_group_store (target, valreg,
++                            int_size_in_bytes (TREE_TYPE (exp)));
++
++        /* We can not support sibling calls for this case.  */
++        sibcall_failure = 1;
++      }
++      else if (target
++             && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
++             && GET_MODE (target) == GET_MODE (valreg))
++      {
++        /* TARGET and VALREG cannot be equal at this point because the
++           latter would not have REG_FUNCTION_VALUE_P true, while the
++           former would if it were referring to the same register.
++
++           If they refer to the same register, this move will be a no-op,
++           except when function inlining is being done.  */
++        emit_move_insn (target, valreg);
++      }
++      else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
++      {
++        target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
++
++        /* We can not support sibling calls for this case.  */
++        sibcall_failure = 1;
++      }
++      else
++      target = copy_to_reg (valreg);
++
++#ifdef PROMOTE_FUNCTION_RETURN
++      /* If we promoted this return value, make the proper SUBREG.  TARGET
++       might be const0_rtx here, so be careful.  */
++      if (GET_CODE (target) == REG
++        && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
++        && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
++      {
++        tree type = TREE_TYPE (exp);
++        int unsignedp = TREE_UNSIGNED (type);
++        int offset = 0;
++
++        /* If we don't promote as expected, something is wrong.  */
++        if (GET_MODE (target)
++            != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
++          abort ();
++
++      if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
++          && GET_MODE_SIZE (GET_MODE (target))
++             > GET_MODE_SIZE (TYPE_MODE (type)))
++        {
++          offset = GET_MODE_SIZE (GET_MODE (target))
++                   - GET_MODE_SIZE (TYPE_MODE (type));
++          if (! BYTES_BIG_ENDIAN)
++            offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
++          else if (! WORDS_BIG_ENDIAN)
++            offset %= UNITS_PER_WORD;
++        }
++        target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
++        SUBREG_PROMOTED_VAR_P (target) = 1;
++        SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
++      }
++#endif
++
++      /* If size of args is variable or this was a constructor call for a stack
++       argument, restore saved stack-pointer value.  */
++
++      if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
++      {
++        emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
++        pending_stack_adjust = old_pending_adj;
++        stack_arg_under_construction = old_stack_arg_under_construction;
++        highest_outgoing_arg_in_use = initial_highest_arg_in_use;
++        stack_usage_map = initial_stack_usage_map;
++        sibcall_failure = 1;
++      }
++      else if (ACCUMULATE_OUTGOING_ARGS && pass)
++      {
++#ifdef REG_PARM_STACK_SPACE
++        if (save_area)
++          {
++            restore_fixed_argument_area (save_area, argblock,
++                                         high_to_save, low_to_save);
++          }
++#endif
++
++        /* If we saved any argument areas, restore them.  */
++        for (i = 0; i < num_actuals; i++)
++          if (args[i].save_area)
++            {
++              enum machine_mode save_mode = GET_MODE (args[i].save_area);
++              rtx stack_area
++                = gen_rtx_MEM (save_mode,
++                               memory_address (save_mode,
++                                               XEXP (args[i].stack_slot, 0)));
++
++              if (save_mode != BLKmode)
++                emit_move_insn (stack_area, args[i].save_area);
++              else
++                emit_block_move (stack_area, args[i].save_area,
++                                 GEN_INT (args[i].size.constant),
++                                 BLOCK_OP_CALL_PARM);
++            }
++
++        highest_outgoing_arg_in_use = initial_highest_arg_in_use;
++        stack_usage_map = initial_stack_usage_map;
++      }
++
++      /* If this was alloca, record the new stack level for nonlocal gotos.
++       Check for the handler slots since we might not have a save area
++       for non-local gotos.  */
++
++      if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
++      emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
++
++      /* Free up storage we no longer need.  */
++      for (i = 0; i < num_actuals; ++i)
++      if (args[i].aligned_regs)
++        free (args[i].aligned_regs);
++
++      if (pass == 0)
++      {
++        /* Undo the fake expand_start_target_temps we did earlier.  If
++           there had been any cleanups created, we've already set
++           sibcall_failure.  */
++        expand_end_target_temps ();
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      if (pass == 0)
++      {
++        tail_call_insns = insns;
++
++        /* Restore the pending stack adjustment now that we have
++           finished generating the sibling call sequence.  */
++
++        pending_stack_adjust = save_pending_stack_adjust;
++        stack_pointer_delta = save_stack_pointer_delta;
++
++        /* Prepare arg structure for next iteration.  */
++        for (i = 0; i < num_actuals; i++)
++          {
++            args[i].value = 0;
++            args[i].aligned_regs = 0;
++            args[i].stack = 0;
++          }
++
++        sbitmap_free (stored_args_map);
++      }
++      else
++      normal_call_insns = insns;
++
++      /* If something prevents making this a sibling call,
++       zero out the sequence.  */
++      if (sibcall_failure)
++      tail_call_insns = NULL_RTX;
++    }
++
++  /* The function optimize_sibling_and_tail_recursive_calls doesn't
++     handle CALL_PLACEHOLDERs inside other CALL_PLACEHOLDERs.  This
++     can happen if the arguments to this function call an inline
++     function who's expansion contains another CALL_PLACEHOLDER.
++
++     If there are any C_Ps in any of these sequences, replace them
++     with their normal call.  */
++
++  for (insn = normal_call_insns; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == CALL_INSN
++      && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++      replace_call_placeholder (insn, sibcall_use_normal);
++
++  for (insn = tail_call_insns; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == CALL_INSN
++      && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++      replace_call_placeholder (insn, sibcall_use_normal);
++
++  for (insn = tail_recursion_insns; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == CALL_INSN
++      && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++      replace_call_placeholder (insn, sibcall_use_normal);
++
++  /* If this was a potential tail recursion site, then emit a
++     CALL_PLACEHOLDER with the normal and the tail recursion streams.
++     One of them will be selected later.  */
++  if (tail_recursion_insns || tail_call_insns)
++    {
++      /* The tail recursion label must be kept around.  We could expose
++       its use in the CALL_PLACEHOLDER, but that creates unwanted edges
++       and makes determining true tail recursion sites difficult.
++
++       So we set LABEL_PRESERVE_P here, then clear it when we select
++       one of the call sequences after rtl generation is complete.  */
++      if (tail_recursion_insns)
++      LABEL_PRESERVE_P (tail_recursion_label) = 1;
++      emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns,
++                                              tail_call_insns,
++                                              tail_recursion_insns,
++                                              tail_recursion_label));
++    }
++  else
++    emit_insn (normal_call_insns);
++
++  currently_expanding_call--;
++
++  /* If this function returns with the stack pointer depressed, ensure
++     this block saves and restores the stack pointer, show it was
++     changed, and adjust for any outgoing arg space.  */
++  if (flags & ECF_SP_DEPRESSED)
++    {
++      clear_pending_stack_adjust ();
++      emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx));
++      emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
++      save_stack_pointer ();
++    }
++
++  return target;
++}
++\f
++/* Output a library call to function FUN (a SYMBOL_REF rtx).
++   The RETVAL parameter specifies whether return value needs to be saved, other
++   parameters are documented in the emit_library_call function below.  */
++
++static rtx
++emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
++     int retval;
++     rtx orgfun;
++     rtx value;
++     enum libcall_type fn_type;
++     enum machine_mode outmode;
++     int nargs;
++     va_list p;
++{
++  /* Total size in bytes of all the stack-parms scanned so far.  */
++  struct args_size args_size;
++  /* Size of arguments before any adjustments (such as rounding).  */
++  struct args_size original_args_size;
++  int argnum;
++  rtx fun;
++  int inc;
++  int count;
++  struct args_size alignment_pad;
++  rtx argblock = 0;
++  CUMULATIVE_ARGS args_so_far;
++  struct arg
++  {
++    rtx value;
++    enum machine_mode mode;
++    rtx reg;
++    int partial;
++    struct args_size offset;
++    struct args_size size;
++    rtx save_area;
++  };
++  struct arg *argvec;
++  int old_inhibit_defer_pop = inhibit_defer_pop;
++  rtx call_fusage = 0;
++  rtx mem_value = 0;
++  rtx valreg;
++  int pcc_struct_value = 0;
++  int struct_value_size = 0;
++  int flags;
++  int reg_parm_stack_space = 0;
++  int needed;
++  rtx before_call;
++  tree tfom;                  /* type_for_mode (outmode, 0) */
++
++#ifdef REG_PARM_STACK_SPACE
++  /* Define the boundary of the register parm stack space that needs to be
++     save, if any.  */
++  int low_to_save = -1, high_to_save = 0;
++  rtx save_area = 0;            /* Place that it is saved.  */
++#endif
++
++  /* Size of the stack reserved for parameter registers.  */
++  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
++  char *initial_stack_usage_map = stack_usage_map;
++
++#ifdef REG_PARM_STACK_SPACE
++#ifdef MAYBE_REG_PARM_STACK_SPACE
++  reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
++#else
++  reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
++#endif
++#endif
++
++  /* By default, library functions can not throw.  */
++  flags = ECF_NOTHROW;
++
++  switch (fn_type)
++    {
++    case LCT_NORMAL:
++      break;
++    case LCT_CONST:
++      flags |= ECF_CONST;
++      break;
++    case LCT_PURE:
++      flags |= ECF_PURE;
++      break;
++    case LCT_CONST_MAKE_BLOCK:
++      flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
++      break;
++    case LCT_PURE_MAKE_BLOCK:
++      flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
++      break;
++    case LCT_NORETURN:
++      flags |= ECF_NORETURN;
++      break;
++    case LCT_THROW:
++      flags = ECF_NORETURN;
++      break;
++    case LCT_ALWAYS_RETURN:
++      flags = ECF_ALWAYS_RETURN;
++      break;
++    case LCT_RETURNS_TWICE:
++      flags = ECF_RETURNS_TWICE;
++      break;
++    }
++  fun = orgfun;
++
++  /* Ensure current function's preferred stack boundary is at least
++     what we need.  */
++  if (cfun->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
++    cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
++
++  /* If this kind of value comes back in memory,
++     decide where in memory it should come back.  */
++  if (outmode != VOIDmode)
++    {
++      tfom = (*lang_hooks.types.type_for_mode) (outmode, 0);
++      if (aggregate_value_p (tfom))
++      {
++#ifdef PCC_STATIC_STRUCT_RETURN
++        rtx pointer_reg
++          = hard_function_value (build_pointer_type (tfom), 0, 0);
++        mem_value = gen_rtx_MEM (outmode, pointer_reg);
++        pcc_struct_value = 1;
++        if (value == 0)
++          value = gen_reg_rtx (outmode);
++#else /* not PCC_STATIC_STRUCT_RETURN */
++        struct_value_size = GET_MODE_SIZE (outmode);
++        if (value != 0 && GET_CODE (value) == MEM)
++          mem_value = value;
++        else
++          mem_value = assign_temp (tfom, 0, 1, 1);
++#endif
++        /* This call returns a big structure.  */
++        flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
++      }
++    }
++  else
++    tfom = void_type_node;
++
++  /* ??? Unfinished: must pass the memory address as an argument.  */
++
++  /* Copy all the libcall-arguments out of the varargs data
++     and into a vector ARGVEC.
++
++     Compute how to pass each argument.  We only support a very small subset
++     of the full argument passing conventions to limit complexity here since
++     library functions shouldn't have many args.  */
++
++  argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
++  memset ((char *) argvec, 0, (nargs + 1) * sizeof (struct arg));
++
++#ifdef INIT_CUMULATIVE_LIBCALL_ARGS
++  INIT_CUMULATIVE_LIBCALL_ARGS (args_so_far, outmode, fun);
++#else
++  INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
++#endif
++
++  args_size.constant = 0;
++  args_size.var = 0;
++
++  count = 0;
++
++  /* Now we are about to start emitting insns that can be deleted
++     if a libcall is deleted.  */
++  if (flags & ECF_LIBCALL_BLOCK)
++    start_sequence ();
++
++  push_temp_slots ();
++
++  /* If there's a structure value address to be passed,
++     either pass it in the special place, or pass it as an extra argument.  */
++  if (mem_value && struct_value_rtx == 0 && ! pcc_struct_value)
++    {
++      rtx addr = XEXP (mem_value, 0);
++      nargs++;
++
++      /* Make sure it is a reasonable operand for a move or push insn.  */
++      if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM
++        && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr)))
++      addr = force_operand (addr, NULL_RTX);
++
++      argvec[count].value = addr;
++      argvec[count].mode = Pmode;
++      argvec[count].partial = 0;
++
++      argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
++#ifdef FUNCTION_ARG_PARTIAL_NREGS
++      if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, Pmode, NULL_TREE, 1))
++      abort ();
++#endif
++
++      locate_and_pad_parm (Pmode, NULL_TREE,
++#ifdef STACK_PARMS_IN_REG_PARM_AREA
++                           1,
++#else
++                         argvec[count].reg != 0,
++#endif
++                         NULL_TREE, &args_size, &argvec[count].offset,
++                         &argvec[count].size, &alignment_pad);
++
++      if (argvec[count].reg == 0 || argvec[count].partial != 0
++        || reg_parm_stack_space > 0)
++      args_size.constant += argvec[count].size.constant;
++
++      FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree) 0, 1);
++
++      count++;
++    }
++
++  for (; count < nargs; count++)
++    {
++      rtx val = va_arg (p, rtx);
++      enum machine_mode mode = va_arg (p, enum machine_mode);
++
++      /* We cannot convert the arg value to the mode the library wants here;
++       must do it earlier where we know the signedness of the arg.  */
++      if (mode == BLKmode
++        || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode))
++      abort ();
++
++      /* On some machines, there's no way to pass a float to a library fcn.
++       Pass it as a double instead.  */
++#ifdef LIBGCC_NEEDS_DOUBLE
++      if (LIBGCC_NEEDS_DOUBLE && mode == SFmode)
++      val = convert_modes (DFmode, SFmode, val, 0), mode = DFmode;
++#endif
++
++      /* There's no need to call protect_from_queue, because
++       either emit_move_insn or emit_push_insn will do that.  */
++
++      /* Make sure it is a reasonable operand for a move or push insn.  */
++      if (GET_CODE (val) != REG && GET_CODE (val) != MEM
++        && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
++      val = force_operand (val, NULL_RTX);
++
++#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
++      if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
++      {
++        rtx slot;
++        int must_copy = 1
++#ifdef FUNCTION_ARG_CALLEE_COPIES       
++          && ! FUNCTION_ARG_CALLEE_COPIES (args_so_far, mode,
++                                           NULL_TREE, 1)
++#endif
++          ;
++
++        /* loop.c won't look at CALL_INSN_FUNCTION_USAGE of const/pure
++           functions, so we have to pretend this isn't such a function.  */
++        if (flags & ECF_LIBCALL_BLOCK)
++          {
++            rtx insns = get_insns ();
++            end_sequence ();
++            emit_insn (insns);
++          }
++        flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
++
++        /* If this was a CONST function, it is now PURE since
++           it now reads memory.  */
++        if (flags & ECF_CONST)
++          {
++            flags &= ~ECF_CONST;
++            flags |= ECF_PURE;
++          }
++
++        if (GET_MODE (val) == MEM && ! must_copy)
++          slot = val;
++        else if (must_copy)
++          {
++            slot = assign_temp ((*lang_hooks.types.type_for_mode) (mode, 0),
++                                0, 1, 1);
++            emit_move_insn (slot, val);
++          }
++        else
++          {
++            tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
++
++            slot = gen_rtx_MEM (mode,
++                                expand_expr (build1 (ADDR_EXPR,
++                                                     build_pointer_type
++                                                     (type),
++                                                     make_tree (type, val)),
++                                             NULL_RTX, VOIDmode, 0));
++          }
++
++        call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
++                                         gen_rtx_USE (VOIDmode, slot),
++                                         call_fusage);
++        if (must_copy)
++          call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
++                                           gen_rtx_CLOBBER (VOIDmode,
++                                                            slot),
++                                           call_fusage);
++
++        mode = Pmode;
++        val = force_operand (XEXP (slot, 0), NULL_RTX);
++      }
++#endif
++
++      argvec[count].value = val;
++      argvec[count].mode = mode;
++
++      argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
++
++#ifdef FUNCTION_ARG_PARTIAL_NREGS
++      argvec[count].partial
++      = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
++#else
++      argvec[count].partial = 0;
++#endif
++
++      locate_and_pad_parm (mode, NULL_TREE,
++#ifdef STACK_PARMS_IN_REG_PARM_AREA
++                         1,
++#else
++                         argvec[count].reg != 0,
++#endif
++                         NULL_TREE, &args_size, &argvec[count].offset,
++                         &argvec[count].size, &alignment_pad);
++
++      if (argvec[count].size.var)
++      abort ();
++
++      if (reg_parm_stack_space == 0 && argvec[count].partial)
++      argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
++
++      if (argvec[count].reg == 0 || argvec[count].partial != 0
++        || reg_parm_stack_space > 0)
++      args_size.constant += argvec[count].size.constant;
++
++      FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
++    }
++
++#ifdef FINAL_REG_PARM_STACK_SPACE
++  reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
++                                                   args_size.var);
++#endif
++  /* If this machine requires an external definition for library
++     functions, write one out.  */
++  assemble_external_libcall (fun);
++
++  original_args_size = args_size;
++  args_size.constant = (((args_size.constant
++                        + stack_pointer_delta
++                        + STACK_BYTES - 1)
++                        / STACK_BYTES
++                        * STACK_BYTES)
++                       - stack_pointer_delta);
++
++  args_size.constant = MAX (args_size.constant,
++                          reg_parm_stack_space);
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++  args_size.constant -= reg_parm_stack_space;
++#endif
++
++  if (args_size.constant > current_function_outgoing_args_size)
++    current_function_outgoing_args_size = args_size.constant;
++
++  if (ACCUMULATE_OUTGOING_ARGS)
++    {
++      /* Since the stack pointer will never be pushed, it is possible for
++       the evaluation of a parm to clobber something we have already
++       written to the stack.  Since most function calls on RISC machines
++       do not use the stack, this is uncommon, but must work correctly.
++
++       Therefore, we save any area of the stack that was already written
++       and that we are using.  Here we set up to do this by making a new
++       stack usage map from the old one.
++
++       Another approach might be to try to reorder the argument
++       evaluations to avoid this conflicting stack usage.  */
++
++      needed = args_size.constant;
++
++#ifndef OUTGOING_REG_PARM_STACK_SPACE
++      /* Since we will be writing into the entire argument area, the
++       map must be allocated for its entire size, not just the part that
++       is the responsibility of the caller.  */
++      needed += reg_parm_stack_space;
++#endif
++
++#ifdef ARGS_GROW_DOWNWARD
++      highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
++                                       needed + 1);
++#else
++      highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
++                                       needed);
++#endif
++      stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
++
++      if (initial_highest_arg_in_use)
++      memcpy (stack_usage_map, initial_stack_usage_map,
++              initial_highest_arg_in_use);
++
++      if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
++      memset (&stack_usage_map[initial_highest_arg_in_use], 0,
++             highest_outgoing_arg_in_use - initial_highest_arg_in_use);
++      needed = 0;
++
++      /* We must be careful to use virtual regs before they're instantiated,
++         and real regs afterwards.  Loop optimization, for example, can create
++       new libcalls after we've instantiated the virtual regs, and if we
++       use virtuals anyway, they won't match the rtl patterns.  */
++
++      if (virtuals_instantiated)
++      argblock = plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET);
++      else
++      argblock = virtual_outgoing_args_rtx;
++    }
++  else
++    {
++      if (!PUSH_ARGS)
++      argblock = push_block (GEN_INT (args_size.constant), 0, 0);
++    }
++
++  /* If we push args individually in reverse order, perform stack alignment
++     before the first push (the last arg).  */
++  if (argblock == 0 && PUSH_ARGS_REVERSED)
++    anti_adjust_stack (GEN_INT (args_size.constant
++                              - original_args_size.constant));
++
++  if (PUSH_ARGS_REVERSED)
++    {
++      inc = -1;
++      argnum = nargs - 1;
++    }
++  else
++    {
++      inc = 1;
++      argnum = 0;
++    }
++
++#ifdef REG_PARM_STACK_SPACE
++  if (ACCUMULATE_OUTGOING_ARGS)
++    {
++      /* The argument list is the property of the called routine and it
++       may clobber it.  If the fixed area has been used for previous
++       parameters, we must save and restore it.
++
++       Here we compute the boundary of the that needs to be saved, if any.  */
++
++#ifdef ARGS_GROW_DOWNWARD
++      for (count = 0; count < reg_parm_stack_space + 1; count++)
++#else
++      for (count = 0; count < reg_parm_stack_space; count++)
++#endif
++      {
++        if (count >= highest_outgoing_arg_in_use
++            || stack_usage_map[count] == 0)
++          continue;
++
++        if (low_to_save == -1)
++          low_to_save = count;
++
++        high_to_save = count;
++      }
++
++      if (low_to_save >= 0)
++      {
++        int num_to_save = high_to_save - low_to_save + 1;
++        enum machine_mode save_mode
++          = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
++        rtx stack_area;
++
++        /* If we don't have the required alignment, must do this in BLKmode.  */
++        if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
++                                 BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
++          save_mode = BLKmode;
++
++#ifdef ARGS_GROW_DOWNWARD
++        stack_area = gen_rtx_MEM (save_mode,
++                                  memory_address (save_mode,
++                                                  plus_constant (argblock,
++                                                                 -high_to_save)));
++#else
++        stack_area = gen_rtx_MEM (save_mode,
++                                  memory_address (save_mode,
++                                                  plus_constant (argblock,
++                                                                 low_to_save)));
++#endif
++        if (save_mode == BLKmode)
++          {
++            save_area = assign_stack_temp (BLKmode, num_to_save, 0);
++            set_mem_align (save_area, PARM_BOUNDARY);
++            emit_block_move (save_area, stack_area, GEN_INT (num_to_save),
++                             BLOCK_OP_CALL_PARM);
++          }
++        else
++          {
++            save_area = gen_reg_rtx (save_mode);
++            emit_move_insn (save_area, stack_area);
++          }
++      }
++    }
++#endif
++
++  /* Push the args that need to be pushed.  */
++
++  /* ARGNUM indexes the ARGVEC array in the order in which the arguments
++     are to be pushed.  */
++  for (count = 0; count < nargs; count++, argnum += inc)
++    {
++      enum machine_mode mode = argvec[argnum].mode;
++      rtx val = argvec[argnum].value;
++      rtx reg = argvec[argnum].reg;
++      int partial = argvec[argnum].partial;
++      int lower_bound = 0, upper_bound = 0, i;
++
++      if (! (reg != 0 && partial == 0))
++      {
++        if (ACCUMULATE_OUTGOING_ARGS)
++          {
++            /* If this is being stored into a pre-allocated, fixed-size,
++               stack area, save any previous data at that location.  */
++
++#ifdef ARGS_GROW_DOWNWARD
++            /* stack_slot is negative, but we want to index stack_usage_map
++               with positive values.  */
++            upper_bound = -argvec[argnum].offset.constant + 1;
++            lower_bound = upper_bound - argvec[argnum].size.constant;
++#else
++            lower_bound = argvec[argnum].offset.constant;
++            upper_bound = lower_bound + argvec[argnum].size.constant;
++#endif
++
++            for (i = lower_bound; i < upper_bound; i++)
++              if (stack_usage_map[i]
++                  /* Don't store things in the fixed argument area at this
++                     point; it has already been saved.  */
++                  && i > reg_parm_stack_space)
++                break;
++
++            if (i != upper_bound)
++              {
++                /* We need to make a save area.  See what mode we can make
++                   it.  */
++                enum machine_mode save_mode
++                  = mode_for_size (argvec[argnum].size.constant
++                                   * BITS_PER_UNIT,
++                                   MODE_INT, 1);
++                rtx stack_area
++                  = gen_rtx_MEM
++                    (save_mode,
++                     memory_address
++                     (save_mode,
++                      plus_constant (argblock,
++                                     argvec[argnum].offset.constant)));
++                argvec[argnum].save_area = gen_reg_rtx (save_mode);
++
++                emit_move_insn (argvec[argnum].save_area, stack_area);
++              }
++          }
++
++        emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
++                        partial, reg, 0, argblock,
++                        GEN_INT (argvec[argnum].offset.constant),
++                        reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
++
++        /* Now mark the segment we just used.  */
++        if (ACCUMULATE_OUTGOING_ARGS)
++          for (i = lower_bound; i < upper_bound; i++)
++            stack_usage_map[i] = 1;
++
++        NO_DEFER_POP;
++      }
++    }
++
++  /* If we pushed args in forward order, perform stack alignment
++     after pushing the last arg.  */
++  if (argblock == 0 && !PUSH_ARGS_REVERSED)
++    anti_adjust_stack (GEN_INT (args_size.constant
++                              - original_args_size.constant));
++
++  if (PUSH_ARGS_REVERSED)
++    argnum = nargs - 1;
++  else
++    argnum = 0;
++
++  fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0, 0);
++
++  /* Now load any reg parms into their regs.  */
++
++  /* ARGNUM indexes the ARGVEC array in the order in which the arguments
++     are to be pushed.  */
++  for (count = 0; count < nargs; count++, argnum += inc)
++    {
++      rtx val = argvec[argnum].value;
++      rtx reg = argvec[argnum].reg;
++      int partial = argvec[argnum].partial;
++
++      /* Handle calls that pass values in multiple non-contiguous
++       locations.  The PA64 has examples of this for library calls.  */
++      if (reg != 0 && GET_CODE (reg) == PARALLEL)
++      emit_group_load (reg, val, GET_MODE_SIZE (GET_MODE (val)));
++      else if (reg != 0 && partial == 0)
++      emit_move_insn (reg, val);
++
++      NO_DEFER_POP;
++    }
++
++  /* Any regs containing parms remain in use through the call.  */
++  for (count = 0; count < nargs; count++)
++    {
++      rtx reg = argvec[count].reg;
++      if (reg != 0 && GET_CODE (reg) == PARALLEL)
++      use_group_regs (&call_fusage, reg);
++      else if (reg != 0)
++      use_reg (&call_fusage, reg);
++    }
++
++  /* Pass the function the address in which to return a structure value.  */
++  if (mem_value != 0 && struct_value_rtx != 0 && ! pcc_struct_value)
++    {
++      emit_move_insn (struct_value_rtx,
++                    force_reg (Pmode,
++                               force_operand (XEXP (mem_value, 0),
++                                              NULL_RTX)));
++      if (GET_CODE (struct_value_rtx) == REG)
++      use_reg (&call_fusage, struct_value_rtx);
++    }
++
++  /* Don't allow popping to be deferred, since then
++     cse'ing of library calls could delete a call and leave the pop.  */
++  NO_DEFER_POP;
++  valreg = (mem_value == 0 && outmode != VOIDmode
++          ? hard_libcall_value (outmode) : NULL_RTX);
++
++  /* Stack must be properly aligned now.  */
++  if (stack_pointer_delta & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1))
++    abort ();
++
++  before_call = get_last_insn ();
++
++  /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
++     will set inhibit_defer_pop to that value.  */
++  /* The return type is needed to decide how many bytes the function pops.
++     Signedness plays no role in that, so for simplicity, we pretend it's
++     always signed.  We also assume that the list of arguments passed has
++     no impact, so we pretend it is unknown.  */
++
++  emit_call_1 (fun,
++             get_identifier (XSTR (orgfun, 0)),
++             build_function_type (tfom, NULL_TREE),
++             original_args_size.constant, args_size.constant,
++             struct_value_size,
++             FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
++             valreg,
++             old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far);
++
++  /* For calls to `setjmp', etc., inform flow.c it should complain
++     if nonvolatile values are live.  For functions that cannot return,
++     inform flow that control does not fall through.  */
++
++  if (flags & (ECF_NORETURN | ECF_LONGJMP))
++    {
++      /* The barrier note must be emitted
++       immediately after the CALL_INSN.  Some ports emit more than
++       just a CALL_INSN above, so we must search for it here.  */
++
++      rtx last = get_last_insn ();
++      while (GET_CODE (last) != CALL_INSN)
++      {
++        last = PREV_INSN (last);
++        /* There was no CALL_INSN?  */
++        if (last == before_call)
++          abort ();
++      }
++
++      emit_barrier_after (last);
++    }
++
++  /* Now restore inhibit_defer_pop to its actual original value.  */
++  OK_DEFER_POP;
++
++  /* If call is cse'able, make appropriate pair of reg-notes around it.
++     Test valreg so we don't crash; may safely ignore `const'
++     if return type is void.  Disable for PARALLEL return values, because
++     we have no way to move such values into a pseudo register.  */
++  if (flags & ECF_LIBCALL_BLOCK)
++    {
++      rtx insns;
++
++      if (valreg == 0)
++      {
++        insns = get_insns ();
++        end_sequence ();
++        emit_insn (insns);
++      }
++      else
++      {
++        rtx note = 0;
++        rtx temp;
++        int i;
++
++        if (GET_CODE (valreg) == PARALLEL)
++          {
++            temp = gen_reg_rtx (outmode);
++            emit_group_store (temp, valreg, outmode);
++            valreg = temp;
++          }
++
++        temp = gen_reg_rtx (GET_MODE (valreg));
++
++        /* Construct an "equal form" for the value which mentions all the
++           arguments in order as well as the function name.  */
++        for (i = 0; i < nargs; i++)
++          note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
++        note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
++
++        insns = get_insns ();
++        end_sequence ();
++
++        if (flags & ECF_PURE)
++          note = gen_rtx_EXPR_LIST (VOIDmode,
++                      gen_rtx_USE (VOIDmode,
++                                   gen_rtx_MEM (BLKmode,
++                                                gen_rtx_SCRATCH (VOIDmode))),
++                      note);
++
++        emit_libcall_block (insns, temp, valreg, note);
++
++        valreg = temp;
++      }
++    }
++  pop_temp_slots ();
++
++  /* Copy the value to the right place.  */
++  if (outmode != VOIDmode && retval)
++    {
++      if (mem_value)
++      {
++        if (value == 0)
++          value = mem_value;
++        if (value != mem_value)
++          emit_move_insn (value, mem_value);
++      }
++      else if (GET_CODE (valreg) == PARALLEL)
++      {
++        if (value == 0)
++          value = gen_reg_rtx (outmode);
++        emit_group_store (value, valreg, outmode);
++      }
++      else if (value != 0)
++      emit_move_insn (value, valreg);
++      else
++      value = valreg;
++    }
++
++  if (ACCUMULATE_OUTGOING_ARGS)
++    {
++#ifdef REG_PARM_STACK_SPACE
++      if (save_area)
++      {
++        enum machine_mode save_mode = GET_MODE (save_area);
++#ifdef ARGS_GROW_DOWNWARD
++        rtx stack_area
++          = gen_rtx_MEM (save_mode,
++                         memory_address (save_mode,
++                                         plus_constant (argblock,
++                                                        - high_to_save)));
++#else
++        rtx stack_area
++          = gen_rtx_MEM (save_mode,
++                         memory_address (save_mode,
++                                         plus_constant (argblock, low_to_save)));
++#endif
++
++        set_mem_align (stack_area, PARM_BOUNDARY);
++        if (save_mode != BLKmode)
++          emit_move_insn (stack_area, save_area);
++        else
++          emit_block_move (stack_area, save_area,
++                           GEN_INT (high_to_save - low_to_save + 1),
++                           BLOCK_OP_CALL_PARM);
++      }
++#endif
++
++      /* If we saved any argument areas, restore them.  */
++      for (count = 0; count < nargs; count++)
++      if (argvec[count].save_area)
++        {
++          enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
++          rtx stack_area
++            = gen_rtx_MEM (save_mode,
++                           memory_address
++                           (save_mode,
++                            plus_constant (argblock,
++                                           argvec[count].offset.constant)));
++
++          emit_move_insn (stack_area, argvec[count].save_area);
++        }
++
++      highest_outgoing_arg_in_use = initial_highest_arg_in_use;
++      stack_usage_map = initial_stack_usage_map;
++    }
++
++  return value;
++
++}
++\f
++/* Output a library call to function FUN (a SYMBOL_REF rtx)
++   (emitting the queue unless NO_QUEUE is nonzero),
++   for a value of mode OUTMODE,
++   with NARGS different arguments, passed as alternating rtx values
++   and machine_modes to convert them to.
++   The rtx values should have been passed through protect_from_queue already.
++
++   FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for `const'
++   calls, LCT_PURE for `pure' calls, LCT_CONST_MAKE_BLOCK for `const' calls
++   which should be enclosed in REG_LIBCALL/REG_RETVAL notes,
++   LCT_PURE_MAKE_BLOCK for `purep' calls which should be enclosed in
++   REG_LIBCALL/REG_RETVAL notes with extra (use (memory (scratch)),
++   or other LCT_ value for other types of library calls.  */
++
++void
++emit_library_call VPARAMS((rtx orgfun, enum libcall_type fn_type,
++                         enum machine_mode outmode, int nargs, ...))
++{
++  VA_OPEN (p, nargs);
++  VA_FIXEDARG (p, rtx, orgfun);
++  VA_FIXEDARG (p, int, fn_type);
++  VA_FIXEDARG (p, enum machine_mode, outmode);
++  VA_FIXEDARG (p, int, nargs);
++
++  emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
++
++  VA_CLOSE (p);
++}
++\f
++/* Like emit_library_call except that an extra argument, VALUE,
++   comes second and says where to store the result.
++   (If VALUE is zero, this function chooses a convenient way
++   to return the value.
++
++   This function returns an rtx for where the value is to be found.
++   If VALUE is nonzero, VALUE is returned.  */
++
++rtx
++emit_library_call_value VPARAMS((rtx orgfun, rtx value,
++                               enum libcall_type fn_type,
++                               enum machine_mode outmode, int nargs, ...))
++{
++  rtx result;
++  
++  VA_OPEN (p, nargs);
++  VA_FIXEDARG (p, rtx, orgfun);
++  VA_FIXEDARG (p, rtx, value);
++  VA_FIXEDARG (p, int, fn_type);
++  VA_FIXEDARG (p, enum machine_mode, outmode);
++  VA_FIXEDARG (p, int, nargs);
++
++  result = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode,
++                                    nargs, p);
++
++  VA_CLOSE (p);
++
++  return result;
++}
++\f
++/* Store a single argument for a function call
++   into the register or memory area where it must be passed.
++   *ARG describes the argument value and where to pass it.
++
++   ARGBLOCK is the address of the stack-block for all the arguments,
++   or 0 on a machine where arguments are pushed individually.
++
++   MAY_BE_ALLOCA nonzero says this could be a call to `alloca'
++   so must be careful about how the stack is used.
++
++   VARIABLE_SIZE nonzero says that this was a variable-sized outgoing
++   argument stack.  This is used if ACCUMULATE_OUTGOING_ARGS to indicate
++   that we need not worry about saving and restoring the stack.
++
++   FNDECL is the declaration of the function we are calling.
++
++   Return nonzero if this arg should cause sibcall failure,
++   zero otherwise.  */
++
++static int
++store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
++     struct arg_data *arg;
++     rtx argblock;
++     int flags;
++     int variable_size ATTRIBUTE_UNUSED;
++     int reg_parm_stack_space;
++{
++  tree pval = arg->tree_value;
++  rtx reg = 0;
++  int partial = 0;
++  int used = 0;
++  int i, lower_bound = 0, upper_bound = 0;
++  int sibcall_failure = 0;
++
++  if (TREE_CODE (pval) == ERROR_MARK)
++    return 1;
++
++  /* Push a new temporary level for any temporaries we make for
++     this argument.  */
++  push_temp_slots ();
++
++  if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL))
++    {
++      /* If this is being stored into a pre-allocated, fixed-size, stack area,
++       save any previous data at that location.  */
++      if (argblock && ! variable_size && arg->stack)
++      {
++#ifdef ARGS_GROW_DOWNWARD
++        /* stack_slot is negative, but we want to index stack_usage_map
++           with positive values.  */
++        if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
++          upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
++        else
++          upper_bound = 0;
++
++        lower_bound = upper_bound - arg->size.constant;
++#else
++        if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
++          lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
++        else
++          lower_bound = 0;
++
++        upper_bound = lower_bound + arg->size.constant;
++#endif
++
++        for (i = lower_bound; i < upper_bound; i++)
++          if (stack_usage_map[i]
++              /* Don't store things in the fixed argument area at this point;
++                 it has already been saved.  */
++              && i > reg_parm_stack_space)
++            break;
++
++        if (i != upper_bound)
++          {
++            /* We need to make a save area.  See what mode we can make it.  */
++            enum machine_mode save_mode
++              = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
++            rtx stack_area
++              = gen_rtx_MEM (save_mode,
++                             memory_address (save_mode,
++                                             XEXP (arg->stack_slot, 0)));
++
++            if (save_mode == BLKmode)
++              {
++                tree ot = TREE_TYPE (arg->tree_value);
++                tree nt = build_qualified_type (ot, (TYPE_QUALS (ot)
++                                                     | TYPE_QUAL_CONST));
++
++                arg->save_area = assign_temp (nt, 0, 1, 1);
++                preserve_temp_slots (arg->save_area);
++                emit_block_move (validize_mem (arg->save_area), stack_area,
++                                 expr_size (arg->tree_value),
++                                 BLOCK_OP_CALL_PARM);
++              }
++            else
++              {
++                arg->save_area = gen_reg_rtx (save_mode);
++                emit_move_insn (arg->save_area, stack_area);
++              }
++          }
++      }
++    }
++
++  /* If this isn't going to be placed on both the stack and in registers,
++     set up the register and number of words.  */
++  if (! arg->pass_on_stack)
++    {
++      if (flags & ECF_SIBCALL)
++      reg = arg->tail_call_reg;
++      else
++      reg = arg->reg;
++      partial = arg->partial;
++    }
++
++  if (reg != 0 && partial == 0)
++    /* Being passed entirely in a register.  We shouldn't be called in
++       this case.  */
++    abort ();
++
++  /* If this arg needs special alignment, don't load the registers
++     here.  */
++  if (arg->n_aligned_regs != 0)
++    reg = 0;
++
++  /* If this is being passed partially in a register, we can't evaluate
++     it directly into its stack slot.  Otherwise, we can.  */
++  if (arg->value == 0)
++    {
++      /* stack_arg_under_construction is nonzero if a function argument is
++       being evaluated directly into the outgoing argument list and
++       expand_call must take special action to preserve the argument list
++       if it is called recursively.
++
++       For scalar function arguments stack_usage_map is sufficient to
++       determine which stack slots must be saved and restored.  Scalar
++       arguments in general have pass_on_stack == 0.
++
++       If this argument is initialized by a function which takes the
++       address of the argument (a C++ constructor or a C function
++       returning a BLKmode structure), then stack_usage_map is
++       insufficient and expand_call must push the stack around the
++       function call.  Such arguments have pass_on_stack == 1.
++
++       Note that it is always safe to set stack_arg_under_construction,
++       but this generates suboptimal code if set when not needed.  */
++
++      if (arg->pass_on_stack)
++      stack_arg_under_construction++;
++
++      arg->value = expand_expr (pval,
++                              (partial
++                               || TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
++                              ? NULL_RTX : arg->stack,
++                              VOIDmode, EXPAND_STACK_PARM);
++
++      /* If we are promoting object (or for any other reason) the mode
++       doesn't agree, convert the mode.  */
++
++      if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
++      arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
++                                  arg->value, arg->unsignedp);
++
++      if (arg->pass_on_stack)
++      stack_arg_under_construction--;
++    }
++
++  /* Don't allow anything left on stack from computation
++     of argument to alloca.  */
++  if (flags & ECF_MAY_BE_ALLOCA)
++    do_pending_stack_adjust ();
++
++  if (arg->value == arg->stack)
++    /* If the value is already in the stack slot, we are done.  */
++    ;
++  else if (arg->mode != BLKmode)
++    {
++      int size;
++
++      /* Argument is a scalar, not entirely passed in registers.
++       (If part is passed in registers, arg->partial says how much
++       and emit_push_insn will take care of putting it there.)
++
++       Push it, and if its size is less than the
++       amount of space allocated to it,
++       also bump stack pointer by the additional space.
++       Note that in C the default argument promotions
++       will prevent such mismatches.  */
++
++      size = GET_MODE_SIZE (arg->mode);
++      /* Compute how much space the push instruction will push.
++       On many machines, pushing a byte will advance the stack
++       pointer by a halfword.  */
++#ifdef PUSH_ROUNDING
++      size = PUSH_ROUNDING (size);
++#endif
++      used = size;
++
++      /* Compute how much space the argument should get:
++       round up to a multiple of the alignment for arguments.  */
++      if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)))
++      used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1)
++               / (PARM_BOUNDARY / BITS_PER_UNIT))
++              * (PARM_BOUNDARY / BITS_PER_UNIT));
++
++      /* This isn't already where we want it on the stack, so put it there.
++       This can either be done with push or copy insns.  */
++      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, 
++                    PARM_BOUNDARY, partial, reg, used - size, argblock,
++                    ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
++                    ARGS_SIZE_RTX (arg->alignment_pad));
++
++      /* Unless this is a partially-in-register argument, the argument is now
++       in the stack.  */
++      if (partial == 0)
++      arg->value = arg->stack;
++    }
++  else
++    {
++      /* BLKmode, at least partly to be pushed.  */
++
++      unsigned int parm_align;
++      int excess;
++      rtx size_rtx;
++
++      /* Pushing a nonscalar.
++       If part is passed in registers, PARTIAL says how much
++       and emit_push_insn will take care of putting it there.  */
++
++      /* Round its size up to a multiple
++       of the allocation unit for arguments.  */
++
++      if (arg->size.var != 0)
++      {
++        excess = 0;
++        size_rtx = ARGS_SIZE_RTX (arg->size);
++      }
++      else
++      {
++        /* PUSH_ROUNDING has no effect on us, because
++           emit_push_insn for BLKmode is careful to avoid it.  */
++        excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
++                  + partial * UNITS_PER_WORD);
++        size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
++                                NULL_RTX, TYPE_MODE (sizetype), 0);
++      }
++
++      /* Some types will require stricter alignment, which will be
++       provided for elsewhere in argument layout.  */
++      parm_align = MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval)));
++
++      /* When an argument is padded down, the block is aligned to
++       PARM_BOUNDARY, but the actual argument isn't.  */
++      if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
++      {
++        if (arg->size.var)
++          parm_align = BITS_PER_UNIT;
++        else if (excess)
++          {
++            unsigned int excess_align = (excess & -excess) * BITS_PER_UNIT;
++            parm_align = MIN (parm_align, excess_align);
++          }
++      }
++
++      if ((flags & ECF_SIBCALL) && GET_CODE (arg->value) == MEM)
++      {
++        /* emit_push_insn might not work properly if arg->value and
++           argblock + arg->offset areas overlap.  */
++        rtx x = arg->value;
++        int i = 0;
++
++        if (XEXP (x, 0) == current_function_internal_arg_pointer
++            || (GET_CODE (XEXP (x, 0)) == PLUS
++                && XEXP (XEXP (x, 0), 0) ==
++                   current_function_internal_arg_pointer
++                && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
++          {
++            if (XEXP (x, 0) != current_function_internal_arg_pointer)
++              i = INTVAL (XEXP (XEXP (x, 0), 1));
++
++            /* expand_call should ensure this */
++            if (arg->offset.var || GET_CODE (size_rtx) != CONST_INT)
++              abort ();
++
++            if (arg->offset.constant > i)
++              {
++                if (arg->offset.constant < i + INTVAL (size_rtx))
++                  sibcall_failure = 1;
++              }
++            else if (arg->offset.constant < i)
++              {
++                if (i < arg->offset.constant + INTVAL (size_rtx))
++                  sibcall_failure = 1;
++              }
++          }
++      }
++
++      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
++                    parm_align, partial, reg, excess, argblock,
++                    ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
++                    ARGS_SIZE_RTX (arg->alignment_pad));
++
++      /* Unless this is a partially-in-register argument, the argument is now
++       in the stack.
++
++       ??? Unlike the case above, in which we want the actual
++       address of the data, so that we can load it directly into a
++       register, here we want the address of the stack slot, so that
++       it's properly aligned for word-by-word copying or something
++       like that.  It's not clear that this is always correct.  */
++      if (partial == 0)
++      arg->value = arg->stack_slot;
++    }
++
++  /* Mark all slots this store used.  */
++  if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
++      && argblock && ! variable_size && arg->stack)
++    for (i = lower_bound; i < upper_bound; i++)
++      stack_usage_map[i] = 1;
++
++  /* Once we have pushed something, pops can't safely
++     be deferred during the rest of the arguments.  */
++  NO_DEFER_POP;
++
++  /* ANSI doesn't require a sequence point here,
++     but PCC has one, so this will avoid some problems.  */
++  emit_queue ();
++
++  /* Free any temporary slots made in processing this argument.  Show
++     that we might have taken the address of something and pushed that
++     as an operand.  */
++  preserve_temp_slots (NULL_RTX);
++  free_temp_slots ();
++  pop_temp_slots ();
++
++  return sibcall_failure;
++}
+diff -ruN gcc-3.3.1/gcc/combine.c gcc-3.3.1.pp/gcc/combine.c
+--- gcc-3.3.1/gcc/combine.c    2003-03-24 11:37:32.000000000 +0000
++++ gcc-3.3.1.pp/gcc/combine.c 2003-09-05 11:58:59.000000000 +0000
+@@ -3859,7 +3859,17 @@
+         rtx inner_op0 = XEXP (XEXP (x, 0), 1);
+         rtx inner_op1 = XEXP (x, 1);
+         rtx inner;
+-
++        
++#ifndef FRAME_GROWS_DOWNWARD
++        if (flag_propolice_protection
++            && code == PLUS
++            && other == frame_pointer_rtx
++            && GET_CODE (inner_op0) == CONST_INT
++            && GET_CODE (inner_op1) == CONST_INT
++            && INTVAL (inner_op0) > 0
++            && INTVAL (inner_op0) + INTVAL (inner_op1) <= 0)
++          return x;
++#endif
+         /* Make sure we pass the constant operand if any as the second
+            one if this is a commutative operation.  */
+         if (CONSTANT_P (inner_op0) && GET_RTX_CLASS (code) == 'c')
+@@ -4272,6 +4282,11 @@
+        they are now checked elsewhere.  */
+       if (GET_CODE (XEXP (x, 0)) == PLUS
+         && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
++#ifndef FRAME_GROWS_DOWNWARD
++      if (! (flag_propolice_protection
++             && XEXP (XEXP (x, 0), 0) == frame_pointer_rtx
++             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
++#endif
+       return gen_binary (PLUS, mode,
+                          gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0),
+                                      XEXP (x, 1)),
+@@ -4400,7 +4415,10 @@
+       /* Canonicalize (minus A (plus B C)) to (minus (minus A B) C) for
+        integers.  */
+-      if (GET_CODE (XEXP (x, 1)) == PLUS && INTEGRAL_MODE_P (mode))
++      if (GET_CODE (XEXP (x, 1)) == PLUS && INTEGRAL_MODE_P (mode)
++        && (! (flag_propolice_protection
++               && XEXP (XEXP (x, 1), 0) == frame_pointer_rtx
++               && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT)))
+       return gen_binary (MINUS, mode,
+                          gen_binary (MINUS, mode, XEXP (x, 0),
+                                      XEXP (XEXP (x, 1), 0)),
+diff -ruN gcc-3.3.1/gcc/config/t-linux gcc-3.3.1.pp/gcc/config/t-linux
+--- gcc-3.3.1/gcc/config/t-linux       2003-06-04 16:56:11.000000000 +0000
++++ gcc-3.3.1.pp/gcc/config/t-linux    2003-09-05 11:58:59.000000000 +0000
+@@ -4,7 +4,7 @@
+ # Compile crtbeginS.o and crtendS.o with pic.
+ CRTSTUFF_T_CFLAGS_S = $(CRTSTUFF_T_CFLAGS) -fPIC
+ # Compile libgcc2.a with pic.
+-TARGET_LIBGCC2_CFLAGS = -fPIC
++TARGET_LIBGCC2_CFLAGS = -fPIC -DHAVE_SYSLOG
+ # Override t-slibgcc-elf-ver to export some libgcc symbols with
+ # the symbol versions that glibc used.
+diff -ruN gcc-3.3.1/gcc/cse.c gcc-3.3.1.pp/gcc/cse.c
+--- gcc-3.3.1/gcc/cse.c        2003-04-29 19:16:40.000000000 +0000
++++ gcc-3.3.1.pp/gcc/cse.c     2003-09-05 11:58:59.000000000 +0000
+@@ -4288,7 +4288,14 @@
+             if (new_const == 0)
+               break;
+-
++#ifndef FRAME_GROWS_DOWNWARD
++            if (flag_propolice_protection
++                && GET_CODE (y) == PLUS
++                && XEXP (y, 0) == frame_pointer_rtx
++                && INTVAL (inner_const) > 0
++                && INTVAL (new_const) <= 0)
++              break;
++#endif
+             /* If we are associating shift operations, don't let this
+                produce a shift of the size of the object or larger.
+                This could occur when we follow a sign-extend by a right
+@@ -4823,6 +4830,13 @@
+       if (SET_DEST (x) == pc_rtx
+         && GET_CODE (SET_SRC (x)) == LABEL_REF)
+       ;
++      /* cut the reg propagation of stack-protected argument */
++      else if (x->volatil) {
++      rtx x1 = SET_DEST (x);
++      if (GET_CODE (x1) == SUBREG && GET_CODE (SUBREG_REG (x1)) == REG)
++        x1 = SUBREG_REG (x1);
++      make_new_qty (REGNO (x1), GET_MODE (x1));
++      }
+       /* Don't count call-insns, (set (reg 0) (call ...)), as a set.
+        The hard function value register is used only once, to copy to
+diff -ruN gcc-3.3.1/gcc/explow.c gcc-3.3.1.pp/gcc/explow.c
+--- gcc-3.3.1/gcc/explow.c     2003-04-07 22:58:12.000000000 +0000
++++ gcc-3.3.1.pp/gcc/explow.c  2003-09-05 11:58:59.000000000 +0000
+@@ -86,7 +86,8 @@
+   rtx tem;
+   int all_constant = 0;
+-  if (c == 0)
++  if (c == 0
++      && !(flag_propolice_protection && x == virtual_stack_vars_rtx))
+     return x;
+  restart:
+@@ -187,7 +188,8 @@
+       break;
+     }
+-  if (c != 0)
++  if (c != 0
++      || (flag_propolice_protection && x == virtual_stack_vars_rtx))
+     x = gen_rtx_PLUS (mode, x, GEN_INT (c));
+   if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+@@ -531,6 +533,21 @@
+        in certain cases.  This is not necessary since the code
+        below can handle all possible cases, but machine-dependent
+        transformations can make better code.  */
++      if (flag_propolice_protection)
++      {
++#define FRAMEADDR_P(X) (GET_CODE (X) == PLUS                          \
++                      && XEXP (X, 0) == virtual_stack_vars_rtx        \
++                      && GET_CODE (XEXP (X, 1)) == CONST_INT)
++        rtx y;
++        if (FRAMEADDR_P (x)) goto win;
++        for (y=x; y!=0 && GET_CODE (y)==PLUS; y = XEXP (y, 0))
++          {
++            if (FRAMEADDR_P (XEXP (y, 0)))
++              XEXP (y, 0) = force_reg (GET_MODE (XEXP (y, 0)), XEXP (y, 0));
++            if (FRAMEADDR_P (XEXP (y, 1)))
++              XEXP (y, 1) = force_reg (GET_MODE (XEXP (y, 1)), XEXP (y, 1));
++          }
++      }
+       LEGITIMIZE_ADDRESS (x, oldx, mode, win);
+       /* PLUS and MULT can appear in special ways
+diff -ruN gcc-3.3.1/gcc/expr.c gcc-3.3.1.pp/gcc/expr.c
+--- gcc-3.3.1/gcc/expr.c       2003-07-24 19:11:20.000000000 +0000
++++ gcc-3.3.1.pp/gcc/expr.c    2003-09-05 11:58:59.000000000 +0000
+@@ -45,6 +45,7 @@
+ #include "langhooks.h"
+ #include "intl.h"
+ #include "tm_p.h"
++#include "protector.h"
+ /* Decide whether a function's arguments should be processed
+    from first to last or from last to first.
+@@ -1518,7 +1519,7 @@
+       if (USE_LOAD_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_from)
+       {
+-        data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len));
++        data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len-GET_MODE_SIZE (mode)));
+         data.autinc_from = 1;
+         data.explicit_inc_from = -1;
+       }
+@@ -1532,7 +1533,7 @@
+       data.from_addr = copy_addr_to_reg (from_addr);
+       if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to)
+       {
+-        data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
++        data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len-GET_MODE_SIZE (mode)));
+         data.autinc_to = 1;
+         data.explicit_inc_to = -1;
+       }
+@@ -1649,11 +1650,13 @@
+       from1 = adjust_address (data->from, mode, data->offset);
+       if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
+-      emit_insn (gen_add2_insn (data->to_addr,
+-                                GEN_INT (-(HOST_WIDE_INT)size)));
++      if (data->explicit_inc_to < -1)
++        emit_insn (gen_add2_insn (data->to_addr,
++                                  GEN_INT (-(HOST_WIDE_INT)size)));
+       if (HAVE_PRE_DECREMENT && data->explicit_inc_from < 0)
+-      emit_insn (gen_add2_insn (data->from_addr,
+-                                GEN_INT (-(HOST_WIDE_INT)size)));
++      if (data->explicit_inc_from < -1)
++        emit_insn (gen_add2_insn (data->from_addr,
++                                  GEN_INT (-(HOST_WIDE_INT)size)));
+       if (data->to)
+       emit_insn ((*genfun) (to1, from1));
+@@ -2826,7 +2829,7 @@
+       if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to)
+       {
+-        data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len));
++        data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len-GET_MODE_SIZE (mode)));
+         data->autinc_to = 1;
+         data->explicit_inc_to = -1;
+       }
+@@ -2897,8 +2900,9 @@
+       to1 = adjust_address (data->to, mode, data->offset);
+       if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
+-      emit_insn (gen_add2_insn (data->to_addr,
+-                                GEN_INT (-(HOST_WIDE_INT) size)));
++      if (data->explicit_inc_to < -1)
++        emit_insn (gen_add2_insn (data->to_addr,
++                                  GEN_INT (-(HOST_WIDE_INT) size)));
+       cst = (*data->constfun) (data->constfundata, data->offset, mode);
+       emit_insn ((*genfun) (to1, cst));
+@@ -5894,7 +5898,9 @@
+         && GET_CODE (XEXP (value, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG
+         && REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER
+-        && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER)
++        && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER
++        && (!flag_propolice_protection
++            || XEXP (XEXP (value, 0), 0) != virtual_stack_vars_rtx))
+       {
+         rtx temp = expand_simple_binop (GET_MODE (value), code,
+                                         XEXP (XEXP (value, 0), 0), op2,
+@@ -8070,7 +8076,8 @@
+       /* If adding to a sum including a constant,
+        associate it to put the constant outside.  */
+       if (GET_CODE (op1) == PLUS
+-        && CONSTANT_P (XEXP (op1, 1)))
++        && CONSTANT_P (XEXP (op1, 1))
++        && !(flag_propolice_protection && (contains_fp (op0) || contains_fp (op1))))
+       {
+         rtx constant_term = const0_rtx;
+diff -ruN gcc-3.3.1/gcc/expr.c.orig gcc-3.3.1.pp/gcc/expr.c.orig
+--- gcc-3.3.1/gcc/expr.c.orig  1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/expr.c.orig       2003-07-24 19:11:20.000000000 +0000
+@@ -0,0 +1,11287 @@
++/* Convert tree expression to rtl instructions, for GNU compiler.
++   Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
++   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify it under
++the terms of the GNU General Public License as published by the Free
++Software Foundation; either version 2, or (at your option) any later
++version.
++
++GCC is distributed in the hope that it will be useful, but WITHOUT ANY
++WARRANTY; without even the implied warranty of MERCHANTABILITY or
++FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING.  If not, write to the Free
++Software Foundation, 59 Temple Place - Suite 330, Boston, MA
++02111-1307, USA.  */
++
++#include "config.h"
++#include "system.h"
++#include "machmode.h"
++#include "real.h"
++#include "rtl.h"
++#include "tree.h"
++#include "flags.h"
++#include "regs.h"
++#include "hard-reg-set.h"
++#include "except.h"
++#include "function.h"
++#include "insn-config.h"
++#include "insn-attr.h"
++/* Include expr.h after insn-config.h so we get HAVE_conditional_move.  */
++#include "expr.h"
++#include "optabs.h"
++#include "libfuncs.h"
++#include "recog.h"
++#include "reload.h"
++#include "output.h"
++#include "typeclass.h"
++#include "toplev.h"
++#include "ggc.h"
++#include "langhooks.h"
++#include "intl.h"
++#include "tm_p.h"
++
++/* Decide whether a function's arguments should be processed
++   from first to last or from last to first.
++
++   They should if the stack and args grow in opposite directions, but
++   only if we have push insns.  */
++
++#ifdef PUSH_ROUNDING
++
++#ifndef PUSH_ARGS_REVERSED
++#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
++#define PUSH_ARGS_REVERSED    /* If it's last to first.  */
++#endif
++#endif
++
++#endif
++
++#ifndef STACK_PUSH_CODE
++#ifdef STACK_GROWS_DOWNWARD
++#define STACK_PUSH_CODE PRE_DEC
++#else
++#define STACK_PUSH_CODE PRE_INC
++#endif
++#endif
++
++/* Assume that case vectors are not pc-relative.  */
++#ifndef CASE_VECTOR_PC_RELATIVE
++#define CASE_VECTOR_PC_RELATIVE 0
++#endif
++
++/* Convert defined/undefined to boolean.  */
++#ifdef TARGET_MEM_FUNCTIONS
++#undef TARGET_MEM_FUNCTIONS
++#define TARGET_MEM_FUNCTIONS 1
++#else
++#define TARGET_MEM_FUNCTIONS 0
++#endif
++
++
++/* If this is nonzero, we do not bother generating VOLATILE
++   around volatile memory references, and we are willing to
++   output indirect addresses.  If cse is to follow, we reject
++   indirect addresses so a useful potential cse is generated;
++   if it is used only once, instruction combination will produce
++   the same indirect address eventually.  */
++int cse_not_expected;
++
++/* Chain of pending expressions for PLACEHOLDER_EXPR to replace.  */
++static tree placeholder_list = 0;
++
++/* This structure is used by move_by_pieces to describe the move to
++   be performed.  */
++struct move_by_pieces
++{
++  rtx to;
++  rtx to_addr;
++  int autinc_to;
++  int explicit_inc_to;
++  rtx from;
++  rtx from_addr;
++  int autinc_from;
++  int explicit_inc_from;
++  unsigned HOST_WIDE_INT len;
++  HOST_WIDE_INT offset;
++  int reverse;
++};
++
++/* This structure is used by store_by_pieces to describe the clear to
++   be performed.  */
++
++struct store_by_pieces
++{
++  rtx to;
++  rtx to_addr;
++  int autinc_to;
++  int explicit_inc_to;
++  unsigned HOST_WIDE_INT len;
++  HOST_WIDE_INT offset;
++  rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
++  PTR constfundata;
++  int reverse;
++};
++
++static rtx enqueue_insn               PARAMS ((rtx, rtx));
++static unsigned HOST_WIDE_INT move_by_pieces_ninsns
++                              PARAMS ((unsigned HOST_WIDE_INT,
++                                       unsigned int));
++static void move_by_pieces_1  PARAMS ((rtx (*) (rtx, ...), enum machine_mode,
++                                       struct move_by_pieces *));
++static bool block_move_libcall_safe_for_call_parm PARAMS ((void));
++static bool emit_block_move_via_movstr PARAMS ((rtx, rtx, rtx, unsigned));
++static rtx emit_block_move_via_libcall PARAMS ((rtx, rtx, rtx));
++static tree emit_block_move_libcall_fn PARAMS ((int));
++static void emit_block_move_via_loop PARAMS ((rtx, rtx, rtx, unsigned));
++static rtx clear_by_pieces_1  PARAMS ((PTR, HOST_WIDE_INT,
++                                       enum machine_mode));
++static void clear_by_pieces   PARAMS ((rtx, unsigned HOST_WIDE_INT,
++                                       unsigned int));
++static void store_by_pieces_1 PARAMS ((struct store_by_pieces *,
++                                       unsigned int));
++static void store_by_pieces_2 PARAMS ((rtx (*) (rtx, ...),
++                                       enum machine_mode,
++                                       struct store_by_pieces *));
++static bool clear_storage_via_clrstr PARAMS ((rtx, rtx, unsigned));
++static rtx clear_storage_via_libcall PARAMS ((rtx, rtx));
++static tree clear_storage_libcall_fn PARAMS ((int));
++static rtx compress_float_constant PARAMS ((rtx, rtx));
++static rtx get_subtarget      PARAMS ((rtx));
++static int is_zeros_p         PARAMS ((tree));
++static int mostly_zeros_p     PARAMS ((tree));
++static void store_constructor_field PARAMS ((rtx, unsigned HOST_WIDE_INT,
++                                           HOST_WIDE_INT, enum machine_mode,
++                                           tree, tree, int, int));
++static void store_constructor PARAMS ((tree, rtx, int, HOST_WIDE_INT));
++static rtx store_field                PARAMS ((rtx, HOST_WIDE_INT,
++                                       HOST_WIDE_INT, enum machine_mode,
++                                       tree, enum machine_mode, int, tree,
++                                       int));
++static rtx var_rtx            PARAMS ((tree));
++static HOST_WIDE_INT highest_pow2_factor PARAMS ((tree));
++static HOST_WIDE_INT highest_pow2_factor_for_type PARAMS ((tree, tree));
++static int is_aligning_offset PARAMS ((tree, tree));
++static rtx expand_increment   PARAMS ((tree, int, int));
++static void do_jump_by_parts_greater PARAMS ((tree, int, rtx, rtx));
++static void do_jump_by_parts_equality PARAMS ((tree, rtx, rtx));
++static void do_compare_and_jump       PARAMS ((tree, enum rtx_code, enum rtx_code,
++                                       rtx, rtx));
++static rtx do_store_flag      PARAMS ((tree, rtx, enum machine_mode, int));
++#ifdef PUSH_ROUNDING
++static void emit_single_push_insn PARAMS ((enum machine_mode, rtx, tree));
++#endif
++static void do_tablejump PARAMS ((rtx, enum machine_mode, rtx, rtx, rtx));
++static rtx const_vector_from_tree PARAMS ((tree));
++
++/* Record for each mode whether we can move a register directly to or
++   from an object of that mode in memory.  If we can't, we won't try
++   to use that mode directly when accessing a field of that mode.  */
++
++static char direct_load[NUM_MACHINE_MODES];
++static char direct_store[NUM_MACHINE_MODES];
++
++/* Record for each mode whether we can float-extend from memory.  */
++
++static bool float_extend_from_mem[NUM_MACHINE_MODES][NUM_MACHINE_MODES];
++
++/* If a memory-to-memory move would take MOVE_RATIO or more simple
++   move-instruction sequences, we will do a movstr or libcall instead.  */
++
++#ifndef MOVE_RATIO
++#if defined (HAVE_movstrqi) || defined (HAVE_movstrhi) || defined (HAVE_movstrsi) || defined (HAVE_movstrdi) || defined (HAVE_movstrti)
++#define MOVE_RATIO 2
++#else
++/* If we are optimizing for space (-Os), cut down the default move ratio.  */
++#define MOVE_RATIO (optimize_size ? 3 : 15)
++#endif
++#endif
++
++/* This macro is used to determine whether move_by_pieces should be called
++   to perform a structure copy.  */
++#ifndef MOVE_BY_PIECES_P
++#define MOVE_BY_PIECES_P(SIZE, ALIGN) \
++  (move_by_pieces_ninsns (SIZE, ALIGN) < (unsigned int) MOVE_RATIO)
++#endif
++
++/* If a clear memory operation would take CLEAR_RATIO or more simple
++   move-instruction sequences, we will do a clrstr or libcall instead.  */
++
++#ifndef CLEAR_RATIO
++#if defined (HAVE_clrstrqi) || defined (HAVE_clrstrhi) || defined (HAVE_clrstrsi) || defined (HAVE_clrstrdi) || defined (HAVE_clrstrti)
++#define CLEAR_RATIO 2
++#else
++/* If we are optimizing for space, cut down the default clear ratio.  */
++#define CLEAR_RATIO (optimize_size ? 3 : 15)
++#endif
++#endif
++
++/* This macro is used to determine whether clear_by_pieces should be
++   called to clear storage.  */
++#ifndef CLEAR_BY_PIECES_P
++#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \
++  (move_by_pieces_ninsns (SIZE, ALIGN) < (unsigned int) CLEAR_RATIO)
++#endif
++
++/* This array records the insn_code of insns to perform block moves.  */
++enum insn_code movstr_optab[NUM_MACHINE_MODES];
++
++/* This array records the insn_code of insns to perform block clears.  */
++enum insn_code clrstr_optab[NUM_MACHINE_MODES];
++
++/* SLOW_UNALIGNED_ACCESS is nonzero if unaligned accesses are very slow.  */
++
++#ifndef SLOW_UNALIGNED_ACCESS
++#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
++#endif
++\f
++/* This is run once per compilation to set up which modes can be used
++   directly in memory and to initialize the block move optab.  */
++
++void
++init_expr_once ()
++{
++  rtx insn, pat;
++  enum machine_mode mode;
++  int num_clobbers;
++  rtx mem, mem1;
++  rtx reg;
++
++  /* Try indexing by frame ptr and try by stack ptr.
++     It is known that on the Convex the stack ptr isn't a valid index.
++     With luck, one or the other is valid on any machine.  */
++  mem = gen_rtx_MEM (VOIDmode, stack_pointer_rtx);
++  mem1 = gen_rtx_MEM (VOIDmode, frame_pointer_rtx);
++
++  /* A scratch register we can modify in-place below to avoid
++     useless RTL allocations.  */
++  reg = gen_rtx_REG (VOIDmode, -1);
++
++  insn = rtx_alloc (INSN);
++  pat = gen_rtx_SET (0, NULL_RTX, NULL_RTX);
++  PATTERN (insn) = pat;
++
++  for (mode = VOIDmode; (int) mode < NUM_MACHINE_MODES;
++       mode = (enum machine_mode) ((int) mode + 1))
++    {
++      int regno;
++
++      direct_load[(int) mode] = direct_store[(int) mode] = 0;
++      PUT_MODE (mem, mode);
++      PUT_MODE (mem1, mode);
++      PUT_MODE (reg, mode);
++
++      /* See if there is some register that can be used in this mode and
++       directly loaded or stored from memory.  */
++
++      if (mode != VOIDmode && mode != BLKmode)
++      for (regno = 0; regno < FIRST_PSEUDO_REGISTER
++           && (direct_load[(int) mode] == 0 || direct_store[(int) mode] == 0);
++           regno++)
++        {
++          if (! HARD_REGNO_MODE_OK (regno, mode))
++            continue;
++
++          REGNO (reg) = regno;
++
++          SET_SRC (pat) = mem;
++          SET_DEST (pat) = reg;
++          if (recog (pat, insn, &num_clobbers) >= 0)
++            direct_load[(int) mode] = 1;
++
++          SET_SRC (pat) = mem1;
++          SET_DEST (pat) = reg;
++          if (recog (pat, insn, &num_clobbers) >= 0)
++            direct_load[(int) mode] = 1;
++
++          SET_SRC (pat) = reg;
++          SET_DEST (pat) = mem;
++          if (recog (pat, insn, &num_clobbers) >= 0)
++            direct_store[(int) mode] = 1;
++
++          SET_SRC (pat) = reg;
++          SET_DEST (pat) = mem1;
++          if (recog (pat, insn, &num_clobbers) >= 0)
++            direct_store[(int) mode] = 1;
++        }
++    }
++
++  mem = gen_rtx_MEM (VOIDmode, gen_rtx_raw_REG (Pmode, 10000));
++
++  for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
++       mode = GET_MODE_WIDER_MODE (mode))
++    {
++      enum machine_mode srcmode;
++      for (srcmode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); srcmode != mode;
++         srcmode = GET_MODE_WIDER_MODE (srcmode))
++      {
++        enum insn_code ic;
++
++        ic = can_extend_p (mode, srcmode, 0);
++        if (ic == CODE_FOR_nothing)
++          continue;
++
++        PUT_MODE (mem, srcmode);
++
++        if ((*insn_data[ic].operand[1].predicate) (mem, srcmode))
++          float_extend_from_mem[mode][srcmode] = true;
++      }
++    }
++}
++
++/* This is run at the start of compiling a function.  */
++
++void
++init_expr ()
++{
++  cfun->expr = (struct expr_status *) ggc_alloc (sizeof (struct expr_status));
++
++  pending_chain = 0;
++  pending_stack_adjust = 0;
++  stack_pointer_delta = 0;
++  inhibit_defer_pop = 0;
++  saveregs_value = 0;
++  apply_args_value = 0;
++  forced_labels = 0;
++}
++
++/* Small sanity check that the queue is empty at the end of a function.  */
++
++void
++finish_expr_for_function ()
++{
++  if (pending_chain)
++    abort ();
++}
++\f
++/* Manage the queue of increment instructions to be output
++   for POSTINCREMENT_EXPR expressions, etc.  */
++
++/* Queue up to increment (or change) VAR later.  BODY says how:
++   BODY should be the same thing you would pass to emit_insn
++   to increment right away.  It will go to emit_insn later on.
++
++   The value is a QUEUED expression to be used in place of VAR
++   where you want to guarantee the pre-incrementation value of VAR.  */
++
++static rtx
++enqueue_insn (var, body)
++     rtx var, body;
++{
++  pending_chain = gen_rtx_QUEUED (GET_MODE (var), var, NULL_RTX, NULL_RTX,
++                                body, pending_chain);
++  return pending_chain;
++}
++
++/* Use protect_from_queue to convert a QUEUED expression
++   into something that you can put immediately into an instruction.
++   If the queued incrementation has not happened yet,
++   protect_from_queue returns the variable itself.
++   If the incrementation has happened, protect_from_queue returns a temp
++   that contains a copy of the old value of the variable.
++
++   Any time an rtx which might possibly be a QUEUED is to be put
++   into an instruction, it must be passed through protect_from_queue first.
++   QUEUED expressions are not meaningful in instructions.
++
++   Do not pass a value through protect_from_queue and then hold
++   on to it for a while before putting it in an instruction!
++   If the queue is flushed in between, incorrect code will result.  */
++
++rtx
++protect_from_queue (x, modify)
++     rtx x;
++     int modify;
++{
++  RTX_CODE code = GET_CODE (x);
++
++#if 0  /* A QUEUED can hang around after the queue is forced out.  */
++  /* Shortcut for most common case.  */
++  if (pending_chain == 0)
++    return x;
++#endif
++
++  if (code != QUEUED)
++    {
++      /* A special hack for read access to (MEM (QUEUED ...)) to facilitate
++       use of autoincrement.  Make a copy of the contents of the memory
++       location rather than a copy of the address, but not if the value is
++       of mode BLKmode.  Don't modify X in place since it might be
++       shared.  */
++      if (code == MEM && GET_MODE (x) != BLKmode
++        && GET_CODE (XEXP (x, 0)) == QUEUED && !modify)
++      {
++        rtx y = XEXP (x, 0);
++        rtx new = replace_equiv_address_nv (x, QUEUED_VAR (y));
++
++        if (QUEUED_INSN (y))
++          {
++            rtx temp = gen_reg_rtx (GET_MODE (x));
++
++            emit_insn_before (gen_move_insn (temp, new),
++                              QUEUED_INSN (y));
++            return temp;
++          }
++
++        /* Copy the address into a pseudo, so that the returned value
++           remains correct across calls to emit_queue.  */
++        return replace_equiv_address (new, copy_to_reg (XEXP (new, 0)));
++      }
++
++      /* Otherwise, recursively protect the subexpressions of all
++       the kinds of rtx's that can contain a QUEUED.  */
++      if (code == MEM)
++      {
++        rtx tem = protect_from_queue (XEXP (x, 0), 0);
++        if (tem != XEXP (x, 0))
++          {
++            x = copy_rtx (x);
++            XEXP (x, 0) = tem;
++          }
++      }
++      else if (code == PLUS || code == MULT)
++      {
++        rtx new0 = protect_from_queue (XEXP (x, 0), 0);
++        rtx new1 = protect_from_queue (XEXP (x, 1), 0);
++        if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
++          {
++            x = copy_rtx (x);
++            XEXP (x, 0) = new0;
++            XEXP (x, 1) = new1;
++          }
++      }
++      return x;
++    }
++  /* If the increment has not happened, use the variable itself.  Copy it
++     into a new pseudo so that the value remains correct across calls to
++     emit_queue.  */
++  if (QUEUED_INSN (x) == 0)
++    return copy_to_reg (QUEUED_VAR (x));
++  /* If the increment has happened and a pre-increment copy exists,
++     use that copy.  */
++  if (QUEUED_COPY (x) != 0)
++    return QUEUED_COPY (x);
++  /* The increment has happened but we haven't set up a pre-increment copy.
++     Set one up now, and use it.  */
++  QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x)));
++  emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)),
++                  QUEUED_INSN (x));
++  return QUEUED_COPY (x);
++}
++
++/* Return nonzero if X contains a QUEUED expression:
++   if it contains anything that will be altered by a queued increment.
++   We handle only combinations of MEM, PLUS, MINUS and MULT operators
++   since memory addresses generally contain only those.  */
++
++int
++queued_subexp_p (x)
++     rtx x;
++{
++  enum rtx_code code = GET_CODE (x);
++  switch (code)
++    {
++    case QUEUED:
++      return 1;
++    case MEM:
++      return queued_subexp_p (XEXP (x, 0));
++    case MULT:
++    case PLUS:
++    case MINUS:
++      return (queued_subexp_p (XEXP (x, 0))
++            || queued_subexp_p (XEXP (x, 1)));
++    default:
++      return 0;
++    }
++}
++
++/* Perform all the pending incrementations.  */
++
++void
++emit_queue ()
++{
++  rtx p;
++  while ((p = pending_chain))
++    {
++      rtx body = QUEUED_BODY (p);
++
++      switch (GET_CODE (body))
++      {
++      case INSN:
++      case JUMP_INSN:
++      case CALL_INSN:
++      case CODE_LABEL:
++      case BARRIER:
++      case NOTE:
++        QUEUED_INSN (p) = body;
++        emit_insn (body);
++        break;
++
++#ifdef ENABLE_CHECKING
++      case SEQUENCE:
++        abort ();
++        break;
++#endif
++
++      default:
++        QUEUED_INSN (p) = emit_insn (body);
++        break;
++      }
++
++      pending_chain = QUEUED_NEXT (p);
++    }
++}
++\f
++/* Copy data from FROM to TO, where the machine modes are not the same.
++   Both modes may be integer, or both may be floating.
++   UNSIGNEDP should be nonzero if FROM is an unsigned type.
++   This causes zero-extension instead of sign-extension.  */
++
++void
++convert_move (to, from, unsignedp)
++     rtx to, from;
++     int unsignedp;
++{
++  enum machine_mode to_mode = GET_MODE (to);
++  enum machine_mode from_mode = GET_MODE (from);
++  int to_real = GET_MODE_CLASS (to_mode) == MODE_FLOAT;
++  int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT;
++  enum insn_code code;
++  rtx libcall;
++
++  /* rtx code for making an equivalent value.  */
++  enum rtx_code equiv_code = (unsignedp < 0 ? UNKNOWN
++                            : (unsignedp ? ZERO_EXTEND : SIGN_EXTEND));
++
++  to = protect_from_queue (to, 1);
++  from = protect_from_queue (from, 0);
++
++  if (to_real != from_real)
++    abort ();
++
++  /* If FROM is a SUBREG that indicates that we have already done at least
++     the required extension, strip it.  We don't handle such SUBREGs as
++     TO here.  */
++
++  if (GET_CODE (from) == SUBREG && SUBREG_PROMOTED_VAR_P (from)
++      && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (from)))
++        >= GET_MODE_SIZE (to_mode))
++      && SUBREG_PROMOTED_UNSIGNED_P (from) == unsignedp)
++    from = gen_lowpart (to_mode, from), from_mode = to_mode;
++
++  if (GET_CODE (to) == SUBREG && SUBREG_PROMOTED_VAR_P (to))
++    abort ();
++
++  if (to_mode == from_mode
++      || (from_mode == VOIDmode && CONSTANT_P (from)))
++    {
++      emit_move_insn (to, from);
++      return;
++    }
++
++  if (VECTOR_MODE_P (to_mode) || VECTOR_MODE_P (from_mode))
++    {
++      if (GET_MODE_BITSIZE (from_mode) != GET_MODE_BITSIZE (to_mode))
++      abort ();
++
++      if (VECTOR_MODE_P (to_mode))
++      from = simplify_gen_subreg (to_mode, from, GET_MODE (from), 0);
++      else
++      to = simplify_gen_subreg (from_mode, to, GET_MODE (to), 0);
++
++      emit_move_insn (to, from);
++      return;
++    }
++
++  if (to_real != from_real)
++    abort ();
++
++  if (to_real)
++    {
++      rtx value, insns;
++
++      if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode))
++      {
++        /* Try converting directly if the insn is supported.  */
++        if ((code = can_extend_p (to_mode, from_mode, 0))
++            != CODE_FOR_nothing)
++          {
++            emit_unop_insn (code, to, from, UNKNOWN);
++            return;
++          }
++      }
++
++#ifdef HAVE_trunchfqf2
++      if (HAVE_trunchfqf2 && from_mode == HFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunchfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctqfqf2
++      if (HAVE_trunctqfqf2 && from_mode == TQFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctqfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncsfqf2
++      if (HAVE_truncsfqf2 && from_mode == SFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncsfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncdfqf2
++      if (HAVE_truncdfqf2 && from_mode == DFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncdfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncxfqf2
++      if (HAVE_truncxfqf2 && from_mode == XFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncxfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctfqf2
++      if (HAVE_trunctfqf2 && from_mode == TFmode && to_mode == QFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctfqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++
++#ifdef HAVE_trunctqfhf2
++      if (HAVE_trunctqfhf2 && from_mode == TQFmode && to_mode == HFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctqfhf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncsfhf2
++      if (HAVE_truncsfhf2 && from_mode == SFmode && to_mode == HFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncsfhf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncdfhf2
++      if (HAVE_truncdfhf2 && from_mode == DFmode && to_mode == HFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncdfhf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncxfhf2
++      if (HAVE_truncxfhf2 && from_mode == XFmode && to_mode == HFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncxfhf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctfhf2
++      if (HAVE_trunctfhf2 && from_mode == TFmode && to_mode == HFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctfhf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++
++#ifdef HAVE_truncsftqf2
++      if (HAVE_truncsftqf2 && from_mode == SFmode && to_mode == TQFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncsftqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncdftqf2
++      if (HAVE_truncdftqf2 && from_mode == DFmode && to_mode == TQFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncdftqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncxftqf2
++      if (HAVE_truncxftqf2 && from_mode == XFmode && to_mode == TQFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncxftqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctftqf2
++      if (HAVE_trunctftqf2 && from_mode == TFmode && to_mode == TQFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctftqf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++
++#ifdef HAVE_truncdfsf2
++      if (HAVE_truncdfsf2 && from_mode == DFmode && to_mode == SFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncdfsf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncxfsf2
++      if (HAVE_truncxfsf2 && from_mode == XFmode && to_mode == SFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncxfsf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctfsf2
++      if (HAVE_trunctfsf2 && from_mode == TFmode && to_mode == SFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctfsf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_truncxfdf2
++      if (HAVE_truncxfdf2 && from_mode == XFmode && to_mode == DFmode)
++      {
++        emit_unop_insn (CODE_FOR_truncxfdf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++#ifdef HAVE_trunctfdf2
++      if (HAVE_trunctfdf2 && from_mode == TFmode && to_mode == DFmode)
++      {
++        emit_unop_insn (CODE_FOR_trunctfdf2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++
++      libcall = (rtx) 0;
++      switch (from_mode)
++      {
++      case SFmode:
++        switch (to_mode)
++          {
++          case DFmode:
++            libcall = extendsfdf2_libfunc;
++            break;
++
++          case XFmode:
++            libcall = extendsfxf2_libfunc;
++            break;
++
++          case TFmode:
++            libcall = extendsftf2_libfunc;
++            break;
++
++          default:
++            break;
++          }
++        break;
++
++      case DFmode:
++        switch (to_mode)
++          {
++          case SFmode:
++            libcall = truncdfsf2_libfunc;
++            break;
++
++          case XFmode:
++            libcall = extenddfxf2_libfunc;
++            break;
++
++          case TFmode:
++            libcall = extenddftf2_libfunc;
++            break;
++
++          default:
++            break;
++          }
++        break;
++
++      case XFmode:
++        switch (to_mode)
++          {
++          case SFmode:
++            libcall = truncxfsf2_libfunc;
++            break;
++
++          case DFmode:
++            libcall = truncxfdf2_libfunc;
++            break;
++
++          default:
++            break;
++          }
++        break;
++
++      case TFmode:
++        switch (to_mode)
++          {
++          case SFmode:
++            libcall = trunctfsf2_libfunc;
++            break;
++
++          case DFmode:
++            libcall = trunctfdf2_libfunc;
++            break;
++
++          default:
++            break;
++          }
++        break;
++
++      default:
++        break;
++      }
++
++      if (libcall == (rtx) 0)
++      /* This conversion is not implemented yet.  */
++      abort ();
++
++      start_sequence ();
++      value = emit_library_call_value (libcall, NULL_RTX, LCT_CONST, to_mode,
++                                     1, from, from_mode);
++      insns = get_insns ();
++      end_sequence ();
++      emit_libcall_block (insns, to, value, gen_rtx_FLOAT_TRUNCATE (to_mode,
++                                                                  from));
++      return;
++    }
++
++  /* Now both modes are integers.  */
++
++  /* Handle expanding beyond a word.  */
++  if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode)
++      && GET_MODE_BITSIZE (to_mode) > BITS_PER_WORD)
++    {
++      rtx insns;
++      rtx lowpart;
++      rtx fill_value;
++      rtx lowfrom;
++      int i;
++      enum machine_mode lowpart_mode;
++      int nwords = CEIL (GET_MODE_SIZE (to_mode), UNITS_PER_WORD);
++
++      /* Try converting directly if the insn is supported.  */
++      if ((code = can_extend_p (to_mode, from_mode, unsignedp))
++        != CODE_FOR_nothing)
++      {
++        /* If FROM is a SUBREG, put it into a register.  Do this
++           so that we always generate the same set of insns for
++           better cse'ing; if an intermediate assignment occurred,
++           we won't be doing the operation directly on the SUBREG.  */
++        if (optimize > 0 && GET_CODE (from) == SUBREG)
++          from = force_reg (from_mode, from);
++        emit_unop_insn (code, to, from, equiv_code);
++        return;
++      }
++      /* Next, try converting via full word.  */
++      else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD
++             && ((code = can_extend_p (to_mode, word_mode, unsignedp))
++                 != CODE_FOR_nothing))
++      {
++        if (GET_CODE (to) == REG)
++          emit_insn (gen_rtx_CLOBBER (VOIDmode, to));
++        convert_move (gen_lowpart (word_mode, to), from, unsignedp);
++        emit_unop_insn (code, to,
++                        gen_lowpart (word_mode, to), equiv_code);
++        return;
++      }
++
++      /* No special multiword conversion insn; do it by hand.  */
++      start_sequence ();
++
++      /* Since we will turn this into a no conflict block, we must ensure
++       that the source does not overlap the target.  */
++
++      if (reg_overlap_mentioned_p (to, from))
++      from = force_reg (from_mode, from);
++
++      /* Get a copy of FROM widened to a word, if necessary.  */
++      if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD)
++      lowpart_mode = word_mode;
++      else
++      lowpart_mode = from_mode;
++
++      lowfrom = convert_to_mode (lowpart_mode, from, unsignedp);
++
++      lowpart = gen_lowpart (lowpart_mode, to);
++      emit_move_insn (lowpart, lowfrom);
++
++      /* Compute the value to put in each remaining word.  */
++      if (unsignedp)
++      fill_value = const0_rtx;
++      else
++      {
++#ifdef HAVE_slt
++        if (HAVE_slt
++            && insn_data[(int) CODE_FOR_slt].operand[0].mode == word_mode
++            && STORE_FLAG_VALUE == -1)
++          {
++            emit_cmp_insn (lowfrom, const0_rtx, NE, NULL_RTX,
++                           lowpart_mode, 0);
++            fill_value = gen_reg_rtx (word_mode);
++            emit_insn (gen_slt (fill_value));
++          }
++        else
++#endif
++          {
++            fill_value
++              = expand_shift (RSHIFT_EXPR, lowpart_mode, lowfrom,
++                              size_int (GET_MODE_BITSIZE (lowpart_mode) - 1),
++                              NULL_RTX, 0);
++            fill_value = convert_to_mode (word_mode, fill_value, 1);
++          }
++      }
++
++      /* Fill the remaining words.  */
++      for (i = GET_MODE_SIZE (lowpart_mode) / UNITS_PER_WORD; i < nwords; i++)
++      {
++        int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
++        rtx subword = operand_subword (to, index, 1, to_mode);
++
++        if (subword == 0)
++          abort ();
++
++        if (fill_value != subword)
++          emit_move_insn (subword, fill_value);
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      emit_no_conflict_block (insns, to, from, NULL_RTX,
++                            gen_rtx_fmt_e (equiv_code, to_mode, copy_rtx (from)));
++      return;
++    }
++
++  /* Truncating multi-word to a word or less.  */
++  if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD
++      && GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD)
++    {
++      if (!((GET_CODE (from) == MEM
++           && ! MEM_VOLATILE_P (from)
++           && direct_load[(int) to_mode]
++           && ! mode_dependent_address_p (XEXP (from, 0)))
++          || GET_CODE (from) == REG
++          || GET_CODE (from) == SUBREG))
++      from = force_reg (from_mode, from);
++      convert_move (to, gen_lowpart (word_mode, from), 0);
++      return;
++    }
++
++  /* Handle pointer conversion.  */                   /* SPEE 900220.  */
++  if (to_mode == PQImode)
++    {
++      if (from_mode != QImode)
++      from = convert_to_mode (QImode, from, unsignedp);
++
++#ifdef HAVE_truncqipqi2
++      if (HAVE_truncqipqi2)
++      {
++        emit_unop_insn (CODE_FOR_truncqipqi2, to, from, UNKNOWN);
++        return;
++      }
++#endif /* HAVE_truncqipqi2 */
++      abort ();
++    }
++
++  if (from_mode == PQImode)
++    {
++      if (to_mode != QImode)
++      {
++        from = convert_to_mode (QImode, from, unsignedp);
++        from_mode = QImode;
++      }
++      else
++      {
++#ifdef HAVE_extendpqiqi2
++        if (HAVE_extendpqiqi2)
++          {
++            emit_unop_insn (CODE_FOR_extendpqiqi2, to, from, UNKNOWN);
++            return;
++          }
++#endif /* HAVE_extendpqiqi2 */
++        abort ();
++      }
++    }
++
++  if (to_mode == PSImode)
++    {
++      if (from_mode != SImode)
++      from = convert_to_mode (SImode, from, unsignedp);
++
++#ifdef HAVE_truncsipsi2
++      if (HAVE_truncsipsi2)
++      {
++        emit_unop_insn (CODE_FOR_truncsipsi2, to, from, UNKNOWN);
++        return;
++      }
++#endif /* HAVE_truncsipsi2 */
++      abort ();
++    }
++
++  if (from_mode == PSImode)
++    {
++      if (to_mode != SImode)
++      {
++        from = convert_to_mode (SImode, from, unsignedp);
++        from_mode = SImode;
++      }
++      else
++      {
++#ifdef HAVE_extendpsisi2
++        if (! unsignedp && HAVE_extendpsisi2)
++          {
++            emit_unop_insn (CODE_FOR_extendpsisi2, to, from, UNKNOWN);
++            return;
++          }
++#endif /* HAVE_extendpsisi2 */
++#ifdef HAVE_zero_extendpsisi2
++        if (unsignedp && HAVE_zero_extendpsisi2)
++          {
++            emit_unop_insn (CODE_FOR_zero_extendpsisi2, to, from, UNKNOWN);
++            return;
++          }
++#endif /* HAVE_zero_extendpsisi2 */
++        abort ();
++      }
++    }
++
++  if (to_mode == PDImode)
++    {
++      if (from_mode != DImode)
++      from = convert_to_mode (DImode, from, unsignedp);
++
++#ifdef HAVE_truncdipdi2
++      if (HAVE_truncdipdi2)
++      {
++        emit_unop_insn (CODE_FOR_truncdipdi2, to, from, UNKNOWN);
++        return;
++      }
++#endif /* HAVE_truncdipdi2 */
++      abort ();
++    }
++
++  if (from_mode == PDImode)
++    {
++      if (to_mode != DImode)
++      {
++        from = convert_to_mode (DImode, from, unsignedp);
++        from_mode = DImode;
++      }
++      else
++      {
++#ifdef HAVE_extendpdidi2
++        if (HAVE_extendpdidi2)
++          {
++            emit_unop_insn (CODE_FOR_extendpdidi2, to, from, UNKNOWN);
++            return;
++          }
++#endif /* HAVE_extendpdidi2 */
++        abort ();
++      }
++    }
++
++  /* Now follow all the conversions between integers
++     no more than a word long.  */
++
++  /* For truncation, usually we can just refer to FROM in a narrower mode.  */
++  if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode)
++      && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode),
++                              GET_MODE_BITSIZE (from_mode)))
++    {
++      if (!((GET_CODE (from) == MEM
++           && ! MEM_VOLATILE_P (from)
++           && direct_load[(int) to_mode]
++           && ! mode_dependent_address_p (XEXP (from, 0)))
++          || GET_CODE (from) == REG
++          || GET_CODE (from) == SUBREG))
++      from = force_reg (from_mode, from);
++      if (GET_CODE (from) == REG && REGNO (from) < FIRST_PSEUDO_REGISTER
++        && ! HARD_REGNO_MODE_OK (REGNO (from), to_mode))
++      from = copy_to_reg (from);
++      emit_move_insn (to, gen_lowpart (to_mode, from));
++      return;
++    }
++
++  /* Handle extension.  */
++  if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode))
++    {
++      /* Convert directly if that works.  */
++      if ((code = can_extend_p (to_mode, from_mode, unsignedp))
++        != CODE_FOR_nothing)
++      {
++        if (flag_force_mem)
++          from = force_not_mem (from);
++
++        emit_unop_insn (code, to, from, equiv_code);
++        return;
++      }
++      else
++      {
++        enum machine_mode intermediate;
++        rtx tmp;
++        tree shift_amount;
++
++        /* Search for a mode to convert via.  */
++        for (intermediate = from_mode; intermediate != VOIDmode;
++             intermediate = GET_MODE_WIDER_MODE (intermediate))
++          if (((can_extend_p (to_mode, intermediate, unsignedp)
++                != CODE_FOR_nothing)
++               || (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (intermediate)
++                   && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode),
++                                             GET_MODE_BITSIZE (intermediate))))
++              && (can_extend_p (intermediate, from_mode, unsignedp)
++                  != CODE_FOR_nothing))
++            {
++              convert_move (to, convert_to_mode (intermediate, from,
++                                                 unsignedp), unsignedp);
++              return;
++            }
++
++        /* No suitable intermediate mode.
++           Generate what we need with shifts.  */
++        shift_amount = build_int_2 (GET_MODE_BITSIZE (to_mode)
++                                    - GET_MODE_BITSIZE (from_mode), 0);
++        from = gen_lowpart (to_mode, force_reg (from_mode, from));
++        tmp = expand_shift (LSHIFT_EXPR, to_mode, from, shift_amount,
++                            to, unsignedp);
++        tmp = expand_shift (RSHIFT_EXPR, to_mode, tmp, shift_amount,
++                            to, unsignedp);
++        if (tmp != to)
++          emit_move_insn (to, tmp);
++        return;
++      }
++    }
++
++  /* Support special truncate insns for certain modes.  */
++
++  if (from_mode == DImode && to_mode == SImode)
++    {
++#ifdef HAVE_truncdisi2
++      if (HAVE_truncdisi2)
++      {
++        emit_unop_insn (CODE_FOR_truncdisi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == DImode && to_mode == HImode)
++    {
++#ifdef HAVE_truncdihi2
++      if (HAVE_truncdihi2)
++      {
++        emit_unop_insn (CODE_FOR_truncdihi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == DImode && to_mode == QImode)
++    {
++#ifdef HAVE_truncdiqi2
++      if (HAVE_truncdiqi2)
++      {
++        emit_unop_insn (CODE_FOR_truncdiqi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == SImode && to_mode == HImode)
++    {
++#ifdef HAVE_truncsihi2
++      if (HAVE_truncsihi2)
++      {
++        emit_unop_insn (CODE_FOR_truncsihi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == SImode && to_mode == QImode)
++    {
++#ifdef HAVE_truncsiqi2
++      if (HAVE_truncsiqi2)
++      {
++        emit_unop_insn (CODE_FOR_truncsiqi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == HImode && to_mode == QImode)
++    {
++#ifdef HAVE_trunchiqi2
++      if (HAVE_trunchiqi2)
++      {
++        emit_unop_insn (CODE_FOR_trunchiqi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == TImode && to_mode == DImode)
++    {
++#ifdef HAVE_trunctidi2
++      if (HAVE_trunctidi2)
++      {
++        emit_unop_insn (CODE_FOR_trunctidi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == TImode && to_mode == SImode)
++    {
++#ifdef HAVE_trunctisi2
++      if (HAVE_trunctisi2)
++      {
++        emit_unop_insn (CODE_FOR_trunctisi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == TImode && to_mode == HImode)
++    {
++#ifdef HAVE_trunctihi2
++      if (HAVE_trunctihi2)
++      {
++        emit_unop_insn (CODE_FOR_trunctihi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  if (from_mode == TImode && to_mode == QImode)
++    {
++#ifdef HAVE_trunctiqi2
++      if (HAVE_trunctiqi2)
++      {
++        emit_unop_insn (CODE_FOR_trunctiqi2, to, from, UNKNOWN);
++        return;
++      }
++#endif
++      convert_move (to, force_reg (from_mode, from), unsignedp);
++      return;
++    }
++
++  /* Handle truncation of volatile memrefs, and so on;
++     the things that couldn't be truncated directly,
++     and for which there was no special instruction.  */
++  if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode))
++    {
++      rtx temp = force_reg (to_mode, gen_lowpart (to_mode, from));
++      emit_move_insn (to, temp);
++      return;
++    }
++
++  /* Mode combination is not recognized.  */
++  abort ();
++}
++
++/* Return an rtx for a value that would result
++   from converting X to mode MODE.
++   Both X and MODE may be floating, or both integer.
++   UNSIGNEDP is nonzero if X is an unsigned value.
++   This can be done by referring to a part of X in place
++   or by copying to a new temporary with conversion.
++
++   This function *must not* call protect_from_queue
++   except when putting X into an insn (in which case convert_move does it).  */
++
++rtx
++convert_to_mode (mode, x, unsignedp)
++     enum machine_mode mode;
++     rtx x;
++     int unsignedp;
++{
++  return convert_modes (mode, VOIDmode, x, unsignedp);
++}
++
++/* Return an rtx for a value that would result
++   from converting X from mode OLDMODE to mode MODE.
++   Both modes may be floating, or both integer.
++   UNSIGNEDP is nonzero if X is an unsigned value.
++
++   This can be done by referring to a part of X in place
++   or by copying to a new temporary with conversion.
++
++   You can give VOIDmode for OLDMODE, if you are sure X has a nonvoid mode.
++
++   This function *must not* call protect_from_queue
++   except when putting X into an insn (in which case convert_move does it).  */
++
++rtx
++convert_modes (mode, oldmode, x, unsignedp)
++     enum machine_mode mode, oldmode;
++     rtx x;
++     int unsignedp;
++{
++  rtx temp;
++
++  /* If FROM is a SUBREG that indicates that we have already done at least
++     the required extension, strip it.  */
++
++  if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x)
++      && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) >= GET_MODE_SIZE (mode)
++      && SUBREG_PROMOTED_UNSIGNED_P (x) == unsignedp)
++    x = gen_lowpart (mode, x);
++
++  if (GET_MODE (x) != VOIDmode)
++    oldmode = GET_MODE (x);
++
++  if (mode == oldmode)
++    return x;
++
++  /* There is one case that we must handle specially: If we are converting
++     a CONST_INT into a mode whose size is twice HOST_BITS_PER_WIDE_INT and
++     we are to interpret the constant as unsigned, gen_lowpart will do
++     the wrong if the constant appears negative.  What we want to do is
++     make the high-order word of the constant zero, not all ones.  */
++
++  if (unsignedp && GET_MODE_CLASS (mode) == MODE_INT
++      && GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT
++      && GET_CODE (x) == CONST_INT && INTVAL (x) < 0)
++    {
++      HOST_WIDE_INT val = INTVAL (x);
++
++      if (oldmode != VOIDmode
++        && HOST_BITS_PER_WIDE_INT > GET_MODE_BITSIZE (oldmode))
++      {
++        int width = GET_MODE_BITSIZE (oldmode);
++
++        /* We need to zero extend VAL.  */
++        val &= ((HOST_WIDE_INT) 1 << width) - 1;
++      }
++
++      return immed_double_const (val, (HOST_WIDE_INT) 0, mode);
++    }
++
++  /* We can do this with a gen_lowpart if both desired and current modes
++     are integer, and this is either a constant integer, a register, or a
++     non-volatile MEM.  Except for the constant case where MODE is no
++     wider than HOST_BITS_PER_WIDE_INT, we must be narrowing the operand.  */
++
++  if ((GET_CODE (x) == CONST_INT
++       && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
++      || (GET_MODE_CLASS (mode) == MODE_INT
++        && GET_MODE_CLASS (oldmode) == MODE_INT
++        && (GET_CODE (x) == CONST_DOUBLE
++            || (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (oldmode)
++                && ((GET_CODE (x) == MEM && ! MEM_VOLATILE_P (x)
++                     && direct_load[(int) mode])
++                    || (GET_CODE (x) == REG
++                        && (! HARD_REGISTER_P (x)
++                            || HARD_REGNO_MODE_OK (REGNO (x), mode))
++                        && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
++                                                  GET_MODE_BITSIZE (GET_MODE (x)))))))))
++    {
++      /* ?? If we don't know OLDMODE, we have to assume here that
++       X does not need sign- or zero-extension.   This may not be
++       the case, but it's the best we can do.  */
++      if (GET_CODE (x) == CONST_INT && oldmode != VOIDmode
++        && GET_MODE_SIZE (mode) > GET_MODE_SIZE (oldmode))
++      {
++        HOST_WIDE_INT val = INTVAL (x);
++        int width = GET_MODE_BITSIZE (oldmode);
++
++        /* We must sign or zero-extend in this case.  Start by
++           zero-extending, then sign extend if we need to.  */
++        val &= ((HOST_WIDE_INT) 1 << width) - 1;
++        if (! unsignedp
++            && (val & ((HOST_WIDE_INT) 1 << (width - 1))))
++          val |= (HOST_WIDE_INT) (-1) << width;
++
++        return gen_int_mode (val, mode);
++      }
++
++      return gen_lowpart (mode, x);
++    }
++
++  temp = gen_reg_rtx (mode);
++  convert_move (temp, x, unsignedp);
++  return temp;
++}
++\f
++/* This macro is used to determine what the largest unit size that
++   move_by_pieces can use is.  */
++
++/* MOVE_MAX_PIECES is the number of bytes at a time which we can
++   move efficiently, as opposed to  MOVE_MAX which is the maximum
++   number of bytes we can move with a single instruction.  */
++
++#ifndef MOVE_MAX_PIECES
++#define MOVE_MAX_PIECES   MOVE_MAX
++#endif
++
++/* STORE_MAX_PIECES is the number of bytes at a time that we can
++   store efficiently.  Due to internal GCC limitations, this is
++   MOVE_MAX_PIECES limited by the number of bytes GCC can represent
++   for an immediate constant.  */
++
++#define STORE_MAX_PIECES  MIN (MOVE_MAX_PIECES, 2 * sizeof (HOST_WIDE_INT))
++
++/* Generate several move instructions to copy LEN bytes from block FROM to
++   block TO.  (These are MEM rtx's with BLKmode).  The caller must pass FROM
++   and TO through protect_from_queue before calling.
++
++   If PUSH_ROUNDING is defined and TO is NULL, emit_single_push_insn is
++   used to push FROM to the stack.
++
++   ALIGN is maximum alignment we can assume.  */
++
++void
++move_by_pieces (to, from, len, align)
++     rtx to, from;
++     unsigned HOST_WIDE_INT len;
++     unsigned int align;
++{
++  struct move_by_pieces data;
++  rtx to_addr, from_addr = XEXP (from, 0);
++  unsigned int max_size = MOVE_MAX_PIECES + 1;
++  enum machine_mode mode = VOIDmode, tmode;
++  enum insn_code icode;
++
++  data.offset = 0;
++  data.from_addr = from_addr;
++  if (to)
++    {
++      to_addr = XEXP (to, 0);
++      data.to = to;
++      data.autinc_to
++      = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
++         || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
++      data.reverse
++      = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
++    }
++  else
++    {
++      to_addr = NULL_RTX;
++      data.to = NULL_RTX;
++      data.autinc_to = 1;
++#ifdef STACK_GROWS_DOWNWARD
++      data.reverse = 1;
++#else
++      data.reverse = 0;
++#endif
++    }
++  data.to_addr = to_addr;
++  data.from = from;
++  data.autinc_from
++    = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC
++       || GET_CODE (from_addr) == POST_INC
++       || GET_CODE (from_addr) == POST_DEC);
++
++  data.explicit_inc_from = 0;
++  data.explicit_inc_to = 0;
++  if (data.reverse) data.offset = len;
++  data.len = len;
++
++  /* If copying requires more than two move insns,
++     copy addresses to registers (to make displacements shorter)
++     and use post-increment if available.  */
++  if (!(data.autinc_from && data.autinc_to)
++      && move_by_pieces_ninsns (len, align) > 2)
++    {
++      /* Find the mode of the largest move...  */
++      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++         tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++      if (GET_MODE_SIZE (tmode) < max_size)
++        mode = tmode;
++
++      if (USE_LOAD_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_from)
++      {
++        data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len));
++        data.autinc_from = 1;
++        data.explicit_inc_from = -1;
++      }
++      if (USE_LOAD_POST_INCREMENT (mode) && ! data.autinc_from)
++      {
++        data.from_addr = copy_addr_to_reg (from_addr);
++        data.autinc_from = 1;
++        data.explicit_inc_from = 1;
++      }
++      if (!data.autinc_from && CONSTANT_P (from_addr))
++      data.from_addr = copy_addr_to_reg (from_addr);
++      if (USE_STORE_PRE_DECREMENT (mode) && data.reverse && ! data.autinc_to)
++      {
++        data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
++        data.autinc_to = 1;
++        data.explicit_inc_to = -1;
++      }
++      if (USE_STORE_POST_INCREMENT (mode) && ! data.reverse && ! data.autinc_to)
++      {
++        data.to_addr = copy_addr_to_reg (to_addr);
++        data.autinc_to = 1;
++        data.explicit_inc_to = 1;
++      }
++      if (!data.autinc_to && CONSTANT_P (to_addr))
++      data.to_addr = copy_addr_to_reg (to_addr);
++    }
++
++  if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
++      || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
++    align = MOVE_MAX * BITS_PER_UNIT;
++
++  /* First move what we can in the largest integer mode, then go to
++     successively smaller modes.  */
++
++  while (max_size > 1)
++    {
++      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++         tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++      if (GET_MODE_SIZE (tmode) < max_size)
++        mode = tmode;
++
++      if (mode == VOIDmode)
++      break;
++
++      icode = mov_optab->handlers[(int) mode].insn_code;
++      if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
++      move_by_pieces_1 (GEN_FCN (icode), mode, &data);
++
++      max_size = GET_MODE_SIZE (mode);
++    }
++
++  /* The code above should have handled everything.  */
++  if (data.len > 0)
++    abort ();
++}
++
++/* Return number of insns required to move L bytes by pieces.
++   ALIGN (in bits) is maximum alignment we can assume.  */
++
++static unsigned HOST_WIDE_INT
++move_by_pieces_ninsns (l, align)
++     unsigned HOST_WIDE_INT l;
++     unsigned int align;
++{
++  unsigned HOST_WIDE_INT n_insns = 0;
++  unsigned HOST_WIDE_INT max_size = MOVE_MAX + 1;
++
++  if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
++      || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
++    align = MOVE_MAX * BITS_PER_UNIT;
++
++  while (max_size > 1)
++    {
++      enum machine_mode mode = VOIDmode, tmode;
++      enum insn_code icode;
++
++      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++         tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++      if (GET_MODE_SIZE (tmode) < max_size)
++        mode = tmode;
++
++      if (mode == VOIDmode)
++      break;
++
++      icode = mov_optab->handlers[(int) mode].insn_code;
++      if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
++      n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode);
++
++      max_size = GET_MODE_SIZE (mode);
++    }
++
++  if (l)
++    abort ();
++  return n_insns;
++}
++
++/* Subroutine of move_by_pieces.  Move as many bytes as appropriate
++   with move instructions for mode MODE.  GENFUN is the gen_... function
++   to make a move insn for that mode.  DATA has all the other info.  */
++
++static void
++move_by_pieces_1 (genfun, mode, data)
++     rtx (*genfun) PARAMS ((rtx, ...));
++     enum machine_mode mode;
++     struct move_by_pieces *data;
++{
++  unsigned int size = GET_MODE_SIZE (mode);
++  rtx to1 = NULL_RTX, from1;
++
++  while (data->len >= size)
++    {
++      if (data->reverse)
++      data->offset -= size;
++
++      if (data->to)
++      {
++        if (data->autinc_to)
++          to1 = adjust_automodify_address (data->to, mode, data->to_addr,
++                                           data->offset);
++        else
++          to1 = adjust_address (data->to, mode, data->offset);
++      }
++
++      if (data->autinc_from)
++      from1 = adjust_automodify_address (data->from, mode, data->from_addr,
++                                         data->offset);
++      else
++      from1 = adjust_address (data->from, mode, data->offset);
++
++      if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
++      emit_insn (gen_add2_insn (data->to_addr,
++                                GEN_INT (-(HOST_WIDE_INT)size)));
++      if (HAVE_PRE_DECREMENT && data->explicit_inc_from < 0)
++      emit_insn (gen_add2_insn (data->from_addr,
++                                GEN_INT (-(HOST_WIDE_INT)size)));
++
++      if (data->to)
++      emit_insn ((*genfun) (to1, from1));
++      else
++      {
++#ifdef PUSH_ROUNDING
++        emit_single_push_insn (mode, from1, NULL);
++#else
++        abort ();
++#endif
++      }
++
++      if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
++      emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
++      if (HAVE_POST_INCREMENT && data->explicit_inc_from > 0)
++      emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size)));
++
++      if (! data->reverse)
++      data->offset += size;
++
++      data->len -= size;
++    }
++}
++\f
++/* Emit code to move a block Y to a block X.  This may be done with
++   string-move instructions, with multiple scalar move instructions,
++   or with a library call.
++
++   Both X and Y must be MEM rtx's (perhaps inside VOLATILE) with mode BLKmode.
++   SIZE is an rtx that says how long they are.
++   ALIGN is the maximum alignment we can assume they have.
++   METHOD describes what kind of copy this is, and what mechanisms may be used.
++
++   Return the address of the new block, if memcpy is called and returns it,
++   0 otherwise.  */
++
++rtx
++emit_block_move (x, y, size, method)
++     rtx x, y, size;
++     enum block_op_methods method;
++{
++  bool may_use_call;
++  rtx retval = 0;
++  unsigned int align;
++
++  switch (method)
++    {
++    case BLOCK_OP_NORMAL:
++      may_use_call = true;
++      break;
++
++    case BLOCK_OP_CALL_PARM:
++      may_use_call = block_move_libcall_safe_for_call_parm ();
++
++      /* Make inhibit_defer_pop nonzero around the library call
++       to force it to pop the arguments right away.  */
++      NO_DEFER_POP;
++      break;
++
++    case BLOCK_OP_NO_LIBCALL:
++      may_use_call = false;
++      break;
++
++    default:
++      abort ();
++    }
++
++  align = MIN (MEM_ALIGN (x), MEM_ALIGN (y));
++
++  if (GET_MODE (x) != BLKmode)
++    abort ();
++  if (GET_MODE (y) != BLKmode)
++    abort ();
++
++  x = protect_from_queue (x, 1);
++  y = protect_from_queue (y, 0);
++  size = protect_from_queue (size, 0);
++
++  if (GET_CODE (x) != MEM)
++    abort ();
++  if (GET_CODE (y) != MEM)
++    abort ();
++  if (size == 0)
++    abort ();
++
++  /* Set MEM_SIZE as appropriate for this block copy.  The main place this
++     can be incorrect is coming from __builtin_memcpy.  */
++  if (GET_CODE (size) == CONST_INT)
++    {
++      x = shallow_copy_rtx (x);
++      y = shallow_copy_rtx (y);
++      set_mem_size (x, size);
++      set_mem_size (y, size);
++    }
++
++  if (GET_CODE (size) == CONST_INT && MOVE_BY_PIECES_P (INTVAL (size), align))
++    move_by_pieces (x, y, INTVAL (size), align);
++  else if (emit_block_move_via_movstr (x, y, size, align))
++    ;
++  else if (may_use_call)
++    retval = emit_block_move_via_libcall (x, y, size);
++  else
++    emit_block_move_via_loop (x, y, size, align);
++
++  if (method == BLOCK_OP_CALL_PARM)
++    OK_DEFER_POP;
++
++  return retval;
++}
++
++/* A subroutine of emit_block_move.  Returns true if calling the 
++   block move libcall will not clobber any parameters which may have
++   already been placed on the stack.  */
++
++static bool
++block_move_libcall_safe_for_call_parm ()
++{
++  if (PUSH_ARGS)
++    return true;
++  else
++    {
++      /* Check to see whether memcpy takes all register arguments.  */
++      static enum {
++      takes_regs_uninit, takes_regs_no, takes_regs_yes
++      } takes_regs = takes_regs_uninit;
++
++      switch (takes_regs)
++      {
++      case takes_regs_uninit:
++        {
++          CUMULATIVE_ARGS args_so_far;
++          tree fn, arg;
++
++          fn = emit_block_move_libcall_fn (false);
++          INIT_CUMULATIVE_ARGS (args_so_far, TREE_TYPE (fn), NULL_RTX, 0);
++
++          arg = TYPE_ARG_TYPES (TREE_TYPE (fn));
++          for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg))
++            {
++              enum machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
++              rtx tmp = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
++              if (!tmp || !REG_P (tmp))
++                goto fail_takes_regs;
++#ifdef FUNCTION_ARG_PARTIAL_NREGS
++              if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode,
++                                              NULL_TREE, 1))
++                goto fail_takes_regs;
++#endif
++              FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
++            }
++        }
++        takes_regs = takes_regs_yes;
++        /* FALLTHRU */
++
++      case takes_regs_yes:
++        return true;
++
++      fail_takes_regs:
++        takes_regs = takes_regs_no;
++        /* FALLTHRU */
++      case takes_regs_no:
++        return false;
++
++      default:
++        abort ();
++      }
++    }
++}
++
++/* A subroutine of emit_block_move.  Expand a movstr pattern; 
++   return true if successful.  */
++
++static bool
++emit_block_move_via_movstr (x, y, size, align)
++     rtx x, y, size;
++     unsigned int align;
++{
++  /* Try the most limited insn first, because there's no point
++     including more than one in the machine description unless
++     the more limited one has some advantage.  */
++
++  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
++  enum machine_mode mode;
++
++  /* Since this is a move insn, we don't care about volatility.  */
++  volatile_ok = 1;
++
++  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
++       mode = GET_MODE_WIDER_MODE (mode))
++    {
++      enum insn_code code = movstr_optab[(int) mode];
++      insn_operand_predicate_fn pred;
++
++      if (code != CODE_FOR_nothing
++        /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
++           here because if SIZE is less than the mode mask, as it is
++           returned by the macro, it will definitely be less than the
++           actual mode mask.  */
++        && ((GET_CODE (size) == CONST_INT
++             && ((unsigned HOST_WIDE_INT) INTVAL (size)
++                 <= (GET_MODE_MASK (mode) >> 1)))
++            || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
++        && ((pred = insn_data[(int) code].operand[0].predicate) == 0
++            || (*pred) (x, BLKmode))
++        && ((pred = insn_data[(int) code].operand[1].predicate) == 0
++            || (*pred) (y, BLKmode))
++        && ((pred = insn_data[(int) code].operand[3].predicate) == 0
++            || (*pred) (opalign, VOIDmode)))
++      {
++        rtx op2;
++        rtx last = get_last_insn ();
++        rtx pat;
++
++        op2 = convert_to_mode (mode, size, 1);
++        pred = insn_data[(int) code].operand[2].predicate;
++        if (pred != 0 && ! (*pred) (op2, mode))
++          op2 = copy_to_mode_reg (mode, op2);
++
++        /* ??? When called via emit_block_move_for_call, it'd be
++           nice if there were some way to inform the backend, so
++           that it doesn't fail the expansion because it thinks
++           emitting the libcall would be more efficient.  */
++
++        pat = GEN_FCN ((int) code) (x, y, op2, opalign);
++        if (pat)
++          {
++            emit_insn (pat);
++            volatile_ok = 0;
++            return true;
++          }
++        else
++          delete_insns_since (last);
++      }
++    }
++
++  volatile_ok = 0;
++  return false;
++}
++
++/* A subroutine of emit_block_move.  Expand a call to memcpy or bcopy.
++   Return the return value from memcpy, 0 otherwise.  */
++
++static rtx
++emit_block_move_via_libcall (dst, src, size)
++     rtx dst, src, size;
++{
++  tree call_expr, arg_list, fn, src_tree, dst_tree, size_tree;
++  enum machine_mode size_mode;
++  rtx retval;
++
++  /* DST, SRC, or SIZE may have been passed through protect_from_queue.
++
++     It is unsafe to save the value generated by protect_from_queue
++     and reuse it later.  Consider what happens if emit_queue is
++     called before the return value from protect_from_queue is used.
++
++     Expansion of the CALL_EXPR below will call emit_queue before
++     we are finished emitting RTL for argument setup.  So if we are
++     not careful we could get the wrong value for an argument.
++
++     To avoid this problem we go ahead and emit code to copy X, Y &
++     SIZE into new pseudos.  We can then place those new pseudos
++     into an RTL_EXPR and use them later, even after a call to
++     emit_queue.
++
++     Note this is not strictly needed for library calls since they
++     do not call emit_queue before loading their arguments.  However,
++     we may need to have library calls call emit_queue in the future
++     since failing to do so could cause problems for targets which
++     define SMALL_REGISTER_CLASSES and pass arguments in registers.  */
++
++  dst = copy_to_mode_reg (Pmode, XEXP (dst, 0));
++  src = copy_to_mode_reg (Pmode, XEXP (src, 0));
++
++  if (TARGET_MEM_FUNCTIONS)
++    size_mode = TYPE_MODE (sizetype);
++  else
++    size_mode = TYPE_MODE (unsigned_type_node);
++  size = convert_to_mode (size_mode, size, 1);
++  size = copy_to_mode_reg (size_mode, size);
++
++  /* It is incorrect to use the libcall calling conventions to call
++     memcpy in this context.  This could be a user call to memcpy and
++     the user may wish to examine the return value from memcpy.  For
++     targets where libcalls and normal calls have different conventions
++     for returning pointers, we could end up generating incorrect code.
++
++     For convenience, we generate the call to bcopy this way as well.  */
++
++  dst_tree = make_tree (ptr_type_node, dst);
++  src_tree = make_tree (ptr_type_node, src);
++  if (TARGET_MEM_FUNCTIONS)
++    size_tree = make_tree (sizetype, size);
++  else
++    size_tree = make_tree (unsigned_type_node, size);
++
++  fn = emit_block_move_libcall_fn (true);
++  arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
++  if (TARGET_MEM_FUNCTIONS)
++    {
++      arg_list = tree_cons (NULL_TREE, src_tree, arg_list);
++      arg_list = tree_cons (NULL_TREE, dst_tree, arg_list);
++    }
++  else
++    {
++      arg_list = tree_cons (NULL_TREE, dst_tree, arg_list);
++      arg_list = tree_cons (NULL_TREE, src_tree, arg_list);
++    }
++
++  /* Now we have to build up the CALL_EXPR itself.  */
++  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
++  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
++                   call_expr, arg_list, NULL_TREE);
++  TREE_SIDE_EFFECTS (call_expr) = 1;
++
++  retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
++
++  /* If we are initializing a readonly value, show the above call
++     clobbered it.  Otherwise, a load from it may erroneously be
++     hoisted from a loop.  */
++  if (RTX_UNCHANGING_P (dst))
++    emit_insn (gen_rtx_CLOBBER (VOIDmode, dst));
++
++  return (TARGET_MEM_FUNCTIONS ? retval : NULL_RTX);
++}
++
++/* A subroutine of emit_block_move_via_libcall.  Create the tree node
++   for the function we use for block copies.  The first time FOR_CALL
++   is true, we call assemble_external.  */
++
++static GTY(()) tree block_move_fn;
++
++static tree
++emit_block_move_libcall_fn (for_call)
++      int for_call;
++{
++  static bool emitted_extern;
++  tree fn = block_move_fn, args;
++
++  if (!fn)
++    {
++      if (TARGET_MEM_FUNCTIONS)
++      {
++        fn = get_identifier ("memcpy");
++        args = build_function_type_list (ptr_type_node, ptr_type_node,
++                                         const_ptr_type_node, sizetype,
++                                         NULL_TREE);
++      }
++      else
++      {
++        fn = get_identifier ("bcopy");
++        args = build_function_type_list (void_type_node, const_ptr_type_node,
++                                         ptr_type_node, unsigned_type_node,
++                                         NULL_TREE);
++      }
++
++      fn = build_decl (FUNCTION_DECL, fn, args);
++      DECL_EXTERNAL (fn) = 1;
++      TREE_PUBLIC (fn) = 1;
++      DECL_ARTIFICIAL (fn) = 1;
++      TREE_NOTHROW (fn) = 1;
++
++      block_move_fn = fn;
++    }
++
++  if (for_call && !emitted_extern)
++    {
++      emitted_extern = true;
++      make_decl_rtl (fn, NULL);
++      assemble_external (fn);
++    }
++
++  return fn;
++}
++
++/* A subroutine of emit_block_move.  Copy the data via an explicit
++   loop.  This is used only when libcalls are forbidden.  */
++/* ??? It'd be nice to copy in hunks larger than QImode.  */
++
++static void
++emit_block_move_via_loop (x, y, size, align)
++     rtx x, y, size;
++     unsigned int align ATTRIBUTE_UNUSED;
++{
++  rtx cmp_label, top_label, iter, x_addr, y_addr, tmp;
++  enum machine_mode iter_mode;
++
++  iter_mode = GET_MODE (size);
++  if (iter_mode == VOIDmode)
++    iter_mode = word_mode;
++
++  top_label = gen_label_rtx ();
++  cmp_label = gen_label_rtx ();
++  iter = gen_reg_rtx (iter_mode);
++
++  emit_move_insn (iter, const0_rtx);
++
++  x_addr = force_operand (XEXP (x, 0), NULL_RTX);
++  y_addr = force_operand (XEXP (y, 0), NULL_RTX);
++  do_pending_stack_adjust ();
++
++  emit_note (NULL, NOTE_INSN_LOOP_BEG);
++
++  emit_jump (cmp_label);
++  emit_label (top_label);
++
++  tmp = convert_modes (Pmode, iter_mode, iter, true);
++  x_addr = gen_rtx_PLUS (Pmode, x_addr, tmp);
++  y_addr = gen_rtx_PLUS (Pmode, y_addr, tmp);
++  x = change_address (x, QImode, x_addr);
++  y = change_address (y, QImode, y_addr);
++
++  emit_move_insn (x, y);
++
++  tmp = expand_simple_binop (iter_mode, PLUS, iter, const1_rtx, iter,
++                           true, OPTAB_LIB_WIDEN);
++  if (tmp != iter)
++    emit_move_insn (iter, tmp);
++
++  emit_note (NULL, NOTE_INSN_LOOP_CONT);
++  emit_label (cmp_label);
++
++  emit_cmp_and_jump_insns (iter, size, LT, NULL_RTX, iter_mode,
++                         true, top_label);
++
++  emit_note (NULL, NOTE_INSN_LOOP_END);
++}
++\f
++/* Copy all or part of a value X into registers starting at REGNO.
++   The number of registers to be filled is NREGS.  */
++
++void
++move_block_to_reg (regno, x, nregs, mode)
++     int regno;
++     rtx x;
++     int nregs;
++     enum machine_mode mode;
++{
++  int i;
++#ifdef HAVE_load_multiple
++  rtx pat;
++  rtx last;
++#endif
++
++  if (nregs == 0)
++    return;
++
++  if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
++    x = validize_mem (force_const_mem (mode, x));
++
++  /* See if the machine can do this with a load multiple insn.  */
++#ifdef HAVE_load_multiple
++  if (HAVE_load_multiple)
++    {
++      last = get_last_insn ();
++      pat = gen_load_multiple (gen_rtx_REG (word_mode, regno), x,
++                             GEN_INT (nregs));
++      if (pat)
++      {
++        emit_insn (pat);
++        return;
++      }
++      else
++      delete_insns_since (last);
++    }
++#endif
++
++  for (i = 0; i < nregs; i++)
++    emit_move_insn (gen_rtx_REG (word_mode, regno + i),
++                  operand_subword_force (x, i, mode));
++}
++
++/* Copy all or part of a BLKmode value X out of registers starting at REGNO.
++   The number of registers to be filled is NREGS.  SIZE indicates the number
++   of bytes in the object X.  */
++
++void
++move_block_from_reg (regno, x, nregs, size)
++     int regno;
++     rtx x;
++     int nregs;
++     int size;
++{
++  int i;
++#ifdef HAVE_store_multiple
++  rtx pat;
++  rtx last;
++#endif
++  enum machine_mode mode;
++
++  if (nregs == 0)
++    return;
++
++  /* If SIZE is that of a mode no bigger than a word, just use that
++     mode's store operation.  */
++  if (size <= UNITS_PER_WORD
++      && (mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0)) != BLKmode)
++    {
++      emit_move_insn (adjust_address (x, mode, 0), gen_rtx_REG (mode, regno));
++      return;
++    }
++
++  /* Blocks smaller than a word on a BYTES_BIG_ENDIAN machine must be aligned
++     to the left before storing to memory.  Note that the previous test
++     doesn't handle all cases (e.g. SIZE == 3).  */
++  if (size < UNITS_PER_WORD && BYTES_BIG_ENDIAN)
++    {
++      rtx tem = operand_subword (x, 0, 1, BLKmode);
++      rtx shift;
++
++      if (tem == 0)
++      abort ();
++
++      shift = expand_shift (LSHIFT_EXPR, word_mode,
++                          gen_rtx_REG (word_mode, regno),
++                          build_int_2 ((UNITS_PER_WORD - size)
++                                       * BITS_PER_UNIT, 0), NULL_RTX, 0);
++      emit_move_insn (tem, shift);
++      return;
++    }
++
++  /* See if the machine can do this with a store multiple insn.  */
++#ifdef HAVE_store_multiple
++  if (HAVE_store_multiple)
++    {
++      last = get_last_insn ();
++      pat = gen_store_multiple (x, gen_rtx_REG (word_mode, regno),
++                              GEN_INT (nregs));
++      if (pat)
++      {
++        emit_insn (pat);
++        return;
++      }
++      else
++      delete_insns_since (last);
++    }
++#endif
++
++  for (i = 0; i < nregs; i++)
++    {
++      rtx tem = operand_subword (x, i, 1, BLKmode);
++
++      if (tem == 0)
++      abort ();
++
++      emit_move_insn (tem, gen_rtx_REG (word_mode, regno + i));
++    }
++}
++
++/* Generate a PARALLEL rtx for a new non-consecutive group of registers from
++   ORIG, where ORIG is a non-consecutive group of registers represented by
++   a PARALLEL.  The clone is identical to the original except in that the
++   original set of registers is replaced by a new set of pseudo registers.
++   The new set has the same modes as the original set.  */
++
++rtx
++gen_group_rtx (orig)
++     rtx orig;
++{
++  int i, length;
++  rtx *tmps;
++
++  if (GET_CODE (orig) != PARALLEL)
++    abort ();
++
++  length = XVECLEN (orig, 0);
++  tmps = (rtx *) alloca (sizeof (rtx) * length);
++
++  /* Skip a NULL entry in first slot.  */
++  i = XEXP (XVECEXP (orig, 0, 0), 0) ? 0 : 1;
++
++  if (i)
++    tmps[0] = 0;
++
++  for (; i < length; i++)
++    {
++      enum machine_mode mode = GET_MODE (XEXP (XVECEXP (orig, 0, i), 0));
++      rtx offset = XEXP (XVECEXP (orig, 0, i), 1);
++
++      tmps[i] = gen_rtx_EXPR_LIST (VOIDmode, gen_reg_rtx (mode), offset);
++    }
++
++  return gen_rtx_PARALLEL (GET_MODE (orig), gen_rtvec_v (length, tmps));
++}
++
++/* Emit code to move a block SRC to a block DST, where DST is non-consecutive
++   registers represented by a PARALLEL.  SSIZE represents the total size of
++   block SRC in bytes, or -1 if not known.  */
++/* ??? If SSIZE % UNITS_PER_WORD != 0, we make the blatant assumption that
++   the balance will be in what would be the low-order memory addresses, i.e.
++   left justified for big endian, right justified for little endian.  This
++   happens to be true for the targets currently using this support.  If this
++   ever changes, a new target macro along the lines of FUNCTION_ARG_PADDING
++   would be needed.  */
++
++void
++emit_group_load (dst, orig_src, ssize)
++     rtx dst, orig_src;
++     int ssize;
++{
++  rtx *tmps, src;
++  int start, i;
++
++  if (GET_CODE (dst) != PARALLEL)
++    abort ();
++
++  /* Check for a NULL entry, used to indicate that the parameter goes
++     both on the stack and in registers.  */
++  if (XEXP (XVECEXP (dst, 0, 0), 0))
++    start = 0;
++  else
++    start = 1;
++
++  tmps = (rtx *) alloca (sizeof (rtx) * XVECLEN (dst, 0));
++
++  /* Process the pieces.  */
++  for (i = start; i < XVECLEN (dst, 0); i++)
++    {
++      enum machine_mode mode = GET_MODE (XEXP (XVECEXP (dst, 0, i), 0));
++      HOST_WIDE_INT bytepos = INTVAL (XEXP (XVECEXP (dst, 0, i), 1));
++      unsigned int bytelen = GET_MODE_SIZE (mode);
++      int shift = 0;
++
++      /* Handle trailing fragments that run over the size of the struct.  */
++      if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
++      {
++        shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
++        bytelen = ssize - bytepos;
++        if (bytelen <= 0)
++          abort ();
++      }
++
++      /* If we won't be loading directly from memory, protect the real source
++       from strange tricks we might play; but make sure that the source can
++       be loaded directly into the destination.  */
++      src = orig_src;
++      if (GET_CODE (orig_src) != MEM
++        && (!CONSTANT_P (orig_src)
++            || (GET_MODE (orig_src) != mode
++                && GET_MODE (orig_src) != VOIDmode)))
++      {
++        if (GET_MODE (orig_src) == VOIDmode)
++          src = gen_reg_rtx (mode);
++        else
++          src = gen_reg_rtx (GET_MODE (orig_src));
++
++        emit_move_insn (src, orig_src);
++      }
++
++      /* Optimize the access just a bit.  */
++      if (GET_CODE (src) == MEM
++        && MEM_ALIGN (src) >= GET_MODE_ALIGNMENT (mode)
++        && bytepos * BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
++        && bytelen == GET_MODE_SIZE (mode))
++      {
++        tmps[i] = gen_reg_rtx (mode);
++        emit_move_insn (tmps[i], adjust_address (src, mode, bytepos));
++      }
++      else if (GET_CODE (src) == CONCAT)
++      {
++        unsigned int slen = GET_MODE_SIZE (GET_MODE (src));
++        unsigned int slen0 = GET_MODE_SIZE (GET_MODE (XEXP (src, 0)));
++
++        if ((bytepos == 0 && bytelen == slen0)
++            || (bytepos != 0 && bytepos + bytelen <= slen))
++          {
++            /* The following assumes that the concatenated objects all
++               have the same size.  In this case, a simple calculation
++               can be used to determine the object and the bit field
++               to be extracted.  */
++            tmps[i] = XEXP (src, bytepos / slen0);
++            if (! CONSTANT_P (tmps[i])
++                && (GET_CODE (tmps[i]) != REG || GET_MODE (tmps[i]) != mode))
++              tmps[i] = extract_bit_field (tmps[i], bytelen * BITS_PER_UNIT,
++                                           (bytepos % slen0) * BITS_PER_UNIT,
++                                           1, NULL_RTX, mode, mode, ssize);
++          }
++        else if (bytepos == 0)
++          {
++            rtx mem = assign_stack_temp (GET_MODE (src), slen, 0);
++            emit_move_insn (mem, src);
++            tmps[i] = adjust_address (mem, mode, 0);
++          }
++        else
++          abort ();
++      }
++      else if (CONSTANT_P (src)
++             || (GET_CODE (src) == REG && GET_MODE (src) == mode))
++      tmps[i] = src;
++      else
++      tmps[i] = extract_bit_field (src, bytelen * BITS_PER_UNIT,
++                                   bytepos * BITS_PER_UNIT, 1, NULL_RTX,
++                                   mode, mode, ssize);
++
++      if (BYTES_BIG_ENDIAN && shift)
++      expand_binop (mode, ashl_optab, tmps[i], GEN_INT (shift),
++                    tmps[i], 0, OPTAB_WIDEN);
++    }
++
++  emit_queue ();
++
++  /* Copy the extracted pieces into the proper (probable) hard regs.  */
++  for (i = start; i < XVECLEN (dst, 0); i++)
++    emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]);
++}
++
++/* Emit code to move a block SRC to block DST, where SRC and DST are
++   non-consecutive groups of registers, each represented by a PARALLEL.  */
++
++void
++emit_group_move (dst, src)
++     rtx dst, src;
++{
++  int i;
++
++  if (GET_CODE (src) != PARALLEL
++      || GET_CODE (dst) != PARALLEL
++      || XVECLEN (src, 0) != XVECLEN (dst, 0))
++    abort ();
++
++  /* Skip first entry if NULL.  */
++  for (i = XEXP (XVECEXP (src, 0, 0), 0) ? 0 : 1; i < XVECLEN (src, 0); i++)
++    emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0),
++                  XEXP (XVECEXP (src, 0, i), 0));
++}
++
++/* Emit code to move a block SRC to a block DST, where SRC is non-consecutive
++   registers represented by a PARALLEL.  SSIZE represents the total size of
++   block DST, or -1 if not known.  */
++
++void
++emit_group_store (orig_dst, src, ssize)
++     rtx orig_dst, src;
++     int ssize;
++{
++  rtx *tmps, dst;
++  int start, i;
++
++  if (GET_CODE (src) != PARALLEL)
++    abort ();
++
++  /* Check for a NULL entry, used to indicate that the parameter goes
++     both on the stack and in registers.  */
++  if (XEXP (XVECEXP (src, 0, 0), 0))
++    start = 0;
++  else
++    start = 1;
++
++  tmps = (rtx *) alloca (sizeof (rtx) * XVECLEN (src, 0));
++
++  /* Copy the (probable) hard regs into pseudos.  */
++  for (i = start; i < XVECLEN (src, 0); i++)
++    {
++      rtx reg = XEXP (XVECEXP (src, 0, i), 0);
++      tmps[i] = gen_reg_rtx (GET_MODE (reg));
++      emit_move_insn (tmps[i], reg);
++    }
++  emit_queue ();
++
++  /* If we won't be storing directly into memory, protect the real destination
++     from strange tricks we might play.  */
++  dst = orig_dst;
++  if (GET_CODE (dst) == PARALLEL)
++    {
++      rtx temp;
++
++      /* We can get a PARALLEL dst if there is a conditional expression in
++       a return statement.  In that case, the dst and src are the same,
++       so no action is necessary.  */
++      if (rtx_equal_p (dst, src))
++      return;
++
++      /* It is unclear if we can ever reach here, but we may as well handle
++       it.  Allocate a temporary, and split this into a store/load to/from
++       the temporary.  */
++
++      temp = assign_stack_temp (GET_MODE (dst), ssize, 0);
++      emit_group_store (temp, src, ssize);
++      emit_group_load (dst, temp, ssize);
++      return;
++    }
++  else if (GET_CODE (dst) != MEM && GET_CODE (dst) != CONCAT)
++    {
++      dst = gen_reg_rtx (GET_MODE (orig_dst));
++      /* Make life a bit easier for combine.  */
++      emit_move_insn (dst, CONST0_RTX (GET_MODE (orig_dst)));
++    }
++
++  /* Process the pieces.  */
++  for (i = start; i < XVECLEN (src, 0); i++)
++    {
++      HOST_WIDE_INT bytepos = INTVAL (XEXP (XVECEXP (src, 0, i), 1));
++      enum machine_mode mode = GET_MODE (tmps[i]);
++      unsigned int bytelen = GET_MODE_SIZE (mode);
++      rtx dest = dst;
++
++      /* Handle trailing fragments that run over the size of the struct.  */
++      if (ssize >= 0 && bytepos + (HOST_WIDE_INT) bytelen > ssize)
++      {
++        if (BYTES_BIG_ENDIAN)
++          {
++            int shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
++            expand_binop (mode, ashr_optab, tmps[i], GEN_INT (shift),
++                          tmps[i], 0, OPTAB_WIDEN);
++          }
++        bytelen = ssize - bytepos;
++      }
++
++      if (GET_CODE (dst) == CONCAT)
++      {
++        if (bytepos + bytelen <= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0))))
++          dest = XEXP (dst, 0);
++        else if (bytepos >= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0))))
++          {
++            bytepos -= GET_MODE_SIZE (GET_MODE (XEXP (dst, 0)));
++            dest = XEXP (dst, 1);
++          }
++        else if (bytepos == 0 && XVECLEN (src, 0))
++          {
++            dest = assign_stack_temp (GET_MODE (dest),
++                                      GET_MODE_SIZE (GET_MODE (dest)), 0);
++            emit_move_insn (adjust_address (dest, GET_MODE (tmps[i]), bytepos),
++                            tmps[i]);
++            dst = dest;
++            break;
++          }
++        else
++          abort ();
++      }
++
++      /* Optimize the access just a bit.  */
++      if (GET_CODE (dest) == MEM
++        && MEM_ALIGN (dest) >= GET_MODE_ALIGNMENT (mode)
++        && bytepos * BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
++        && bytelen == GET_MODE_SIZE (mode))
++      emit_move_insn (adjust_address (dest, mode, bytepos), tmps[i]);
++      else
++      store_bit_field (dest, bytelen * BITS_PER_UNIT, bytepos * BITS_PER_UNIT,
++                       mode, tmps[i], ssize);
++    }
++
++  emit_queue ();
++
++  /* Copy from the pseudo into the (probable) hard reg.  */
++  if (orig_dst != dst)
++    emit_move_insn (orig_dst, dst);
++}
++
++/* Generate code to copy a BLKmode object of TYPE out of a
++   set of registers starting with SRCREG into TGTBLK.  If TGTBLK
++   is null, a stack temporary is created.  TGTBLK is returned.
++
++   The primary purpose of this routine is to handle functions
++   that return BLKmode structures in registers.  Some machines
++   (the PA for example) want to return all small structures
++   in registers regardless of the structure's alignment.  */
++
++rtx
++copy_blkmode_from_reg (tgtblk, srcreg, type)
++     rtx tgtblk;
++     rtx srcreg;
++     tree type;
++{
++  unsigned HOST_WIDE_INT bytes = int_size_in_bytes (type);
++  rtx src = NULL, dst = NULL;
++  unsigned HOST_WIDE_INT bitsize = MIN (TYPE_ALIGN (type), BITS_PER_WORD);
++  unsigned HOST_WIDE_INT bitpos, xbitpos, big_endian_correction = 0;
++
++  if (tgtblk == 0)
++    {
++      tgtblk = assign_temp (build_qualified_type (type,
++                                                (TYPE_QUALS (type)
++                                                 | TYPE_QUAL_CONST)),
++                          0, 1, 1);
++      preserve_temp_slots (tgtblk);
++    }
++
++  /* This code assumes srcreg is at least a full word.  If it isn't, copy it
++     into a new pseudo which is a full word.  */
++
++  if (GET_MODE (srcreg) != BLKmode
++      && GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD)
++    srcreg = convert_to_mode (word_mode, srcreg, TREE_UNSIGNED (type));
++
++  /* Structures whose size is not a multiple of a word are aligned
++     to the least significant byte (to the right).  On a BYTES_BIG_ENDIAN
++     machine, this means we must skip the empty high order bytes when
++     calculating the bit offset.  */
++  if (BYTES_BIG_ENDIAN
++      && bytes % UNITS_PER_WORD)
++    big_endian_correction
++      = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) * BITS_PER_UNIT));
++
++  /* Copy the structure BITSIZE bites at a time.
++
++     We could probably emit more efficient code for machines which do not use
++     strict alignment, but it doesn't seem worth the effort at the current
++     time.  */
++  for (bitpos = 0, xbitpos = big_endian_correction;
++       bitpos < bytes * BITS_PER_UNIT;
++       bitpos += bitsize, xbitpos += bitsize)
++    {
++      /* We need a new source operand each time xbitpos is on a
++       word boundary and when xbitpos == big_endian_correction
++       (the first time through).  */
++      if (xbitpos % BITS_PER_WORD == 0
++        || xbitpos == big_endian_correction)
++      src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD,
++                                   GET_MODE (srcreg));
++
++      /* We need a new destination operand each time bitpos is on
++       a word boundary.  */
++      if (bitpos % BITS_PER_WORD == 0)
++      dst = operand_subword (tgtblk, bitpos / BITS_PER_WORD, 1, BLKmode);
++
++      /* Use xbitpos for the source extraction (right justified) and
++       xbitpos for the destination store (left justified).  */
++      store_bit_field (dst, bitsize, bitpos % BITS_PER_WORD, word_mode,
++                     extract_bit_field (src, bitsize,
++                                        xbitpos % BITS_PER_WORD, 1,
++                                        NULL_RTX, word_mode, word_mode,
++                                        BITS_PER_WORD),
++                     BITS_PER_WORD);
++    }
++
++  return tgtblk;
++}
++
++/* Add a USE expression for REG to the (possibly empty) list pointed
++   to by CALL_FUSAGE.  REG must denote a hard register.  */
++
++void
++use_reg (call_fusage, reg)
++     rtx *call_fusage, reg;
++{
++  if (GET_CODE (reg) != REG
++      || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
++    abort ();
++
++  *call_fusage
++    = gen_rtx_EXPR_LIST (VOIDmode,
++                       gen_rtx_USE (VOIDmode, reg), *call_fusage);
++}
++
++/* Add USE expressions to *CALL_FUSAGE for each of NREGS consecutive regs,
++   starting at REGNO.  All of these registers must be hard registers.  */
++
++void
++use_regs (call_fusage, regno, nregs)
++     rtx *call_fusage;
++     int regno;
++     int nregs;
++{
++  int i;
++
++  if (regno + nregs > FIRST_PSEUDO_REGISTER)
++    abort ();
++
++  for (i = 0; i < nregs; i++)
++    use_reg (call_fusage, regno_reg_rtx[regno + i]);
++}
++
++/* Add USE expressions to *CALL_FUSAGE for each REG contained in the
++   PARALLEL REGS.  This is for calls that pass values in multiple
++   non-contiguous locations.  The Irix 6 ABI has examples of this.  */
++
++void
++use_group_regs (call_fusage, regs)
++     rtx *call_fusage;
++     rtx regs;
++{
++  int i;
++
++  for (i = 0; i < XVECLEN (regs, 0); i++)
++    {
++      rtx reg = XEXP (XVECEXP (regs, 0, i), 0);
++
++      /* A NULL entry means the parameter goes both on the stack and in
++       registers.  This can also be a MEM for targets that pass values
++       partially on the stack and partially in registers.  */
++      if (reg != 0 && GET_CODE (reg) == REG)
++      use_reg (call_fusage, reg);
++    }
++}
++\f
++
++/* Determine whether the LEN bytes generated by CONSTFUN can be
++   stored to memory using several move instructions.  CONSTFUNDATA is
++   a pointer which will be passed as argument in every CONSTFUN call.
++   ALIGN is maximum alignment we can assume.  Return nonzero if a
++   call to store_by_pieces should succeed.  */
++
++int
++can_store_by_pieces (len, constfun, constfundata, align)
++     unsigned HOST_WIDE_INT len;
++     rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
++     PTR constfundata;
++     unsigned int align;
++{
++  unsigned HOST_WIDE_INT max_size, l;
++  HOST_WIDE_INT offset = 0;
++  enum machine_mode mode, tmode;
++  enum insn_code icode;
++  int reverse;
++  rtx cst;
++
++  if (len == 0)
++    return 1;
++
++  if (! MOVE_BY_PIECES_P (len, align))
++    return 0;
++
++  if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
++      || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
++    align = MOVE_MAX * BITS_PER_UNIT;
++
++  /* We would first store what we can in the largest integer mode, then go to
++     successively smaller modes.  */
++
++  for (reverse = 0;
++       reverse <= (HAVE_PRE_DECREMENT || HAVE_POST_DECREMENT);
++       reverse++)
++    {
++      l = len;
++      mode = VOIDmode;
++      max_size = STORE_MAX_PIECES + 1;
++      while (max_size > 1)
++      {
++        for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++             tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++          if (GET_MODE_SIZE (tmode) < max_size)
++            mode = tmode;
++
++        if (mode == VOIDmode)
++          break;
++
++        icode = mov_optab->handlers[(int) mode].insn_code;
++        if (icode != CODE_FOR_nothing
++            && align >= GET_MODE_ALIGNMENT (mode))
++          {
++            unsigned int size = GET_MODE_SIZE (mode);
++
++            while (l >= size)
++              {
++                if (reverse)
++                  offset -= size;
++
++                cst = (*constfun) (constfundata, offset, mode);
++                if (!LEGITIMATE_CONSTANT_P (cst))
++                  return 0;
++
++                if (!reverse)
++                  offset += size;
++
++                l -= size;
++              }
++          }
++
++        max_size = GET_MODE_SIZE (mode);
++      }
++
++      /* The code above should have handled everything.  */
++      if (l != 0)
++      abort ();
++    }
++
++  return 1;
++}
++
++/* Generate several move instructions to store LEN bytes generated by
++   CONSTFUN to block TO.  (A MEM rtx with BLKmode).  CONSTFUNDATA is a
++   pointer which will be passed as argument in every CONSTFUN call.
++   ALIGN is maximum alignment we can assume.  */
++
++void
++store_by_pieces (to, len, constfun, constfundata, align)
++     rtx to;
++     unsigned HOST_WIDE_INT len;
++     rtx (*constfun) PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode));
++     PTR constfundata;
++     unsigned int align;
++{
++  struct store_by_pieces data;
++
++  if (len == 0)
++    return;
++
++  if (! MOVE_BY_PIECES_P (len, align))
++    abort ();
++  to = protect_from_queue (to, 1);
++  data.constfun = constfun;
++  data.constfundata = constfundata;
++  data.len = len;
++  data.to = to;
++  store_by_pieces_1 (&data, align);
++}
++
++/* Generate several move instructions to clear LEN bytes of block TO.  (A MEM
++   rtx with BLKmode).  The caller must pass TO through protect_from_queue
++   before calling. ALIGN is maximum alignment we can assume.  */
++
++static void
++clear_by_pieces (to, len, align)
++     rtx to;
++     unsigned HOST_WIDE_INT len;
++     unsigned int align;
++{
++  struct store_by_pieces data;
++
++  if (len == 0)
++    return;
++
++  data.constfun = clear_by_pieces_1;
++  data.constfundata = NULL;
++  data.len = len;
++  data.to = to;
++  store_by_pieces_1 (&data, align);
++}
++
++/* Callback routine for clear_by_pieces.
++   Return const0_rtx unconditionally.  */
++
++static rtx
++clear_by_pieces_1 (data, offset, mode)
++     PTR data ATTRIBUTE_UNUSED;
++     HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
++     enum machine_mode mode ATTRIBUTE_UNUSED;
++{
++  return const0_rtx;
++}
++
++/* Subroutine of clear_by_pieces and store_by_pieces.
++   Generate several move instructions to store LEN bytes of block TO.  (A MEM
++   rtx with BLKmode).  The caller must pass TO through protect_from_queue
++   before calling.  ALIGN is maximum alignment we can assume.  */
++
++static void
++store_by_pieces_1 (data, align)
++     struct store_by_pieces *data;
++     unsigned int align;
++{
++  rtx to_addr = XEXP (data->to, 0);
++  unsigned HOST_WIDE_INT max_size = STORE_MAX_PIECES + 1;
++  enum machine_mode mode = VOIDmode, tmode;
++  enum insn_code icode;
++
++  data->offset = 0;
++  data->to_addr = to_addr;
++  data->autinc_to
++    = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
++       || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
++
++  data->explicit_inc_to = 0;
++  data->reverse
++    = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
++  if (data->reverse)
++    data->offset = data->len;
++
++  /* If storing requires more than two move insns,
++     copy addresses to registers (to make displacements shorter)
++     and use post-increment if available.  */
++  if (!data->autinc_to
++      && move_by_pieces_ninsns (data->len, align) > 2)
++    {
++      /* Determine the main mode we'll be using.  */
++      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++         tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++      if (GET_MODE_SIZE (tmode) < max_size)
++        mode = tmode;
++
++      if (USE_STORE_PRE_DECREMENT (mode) && data->reverse && ! data->autinc_to)
++      {
++        data->to_addr = copy_addr_to_reg (plus_constant (to_addr, data->len));
++        data->autinc_to = 1;
++        data->explicit_inc_to = -1;
++      }
++
++      if (USE_STORE_POST_INCREMENT (mode) && ! data->reverse
++        && ! data->autinc_to)
++      {
++        data->to_addr = copy_addr_to_reg (to_addr);
++        data->autinc_to = 1;
++        data->explicit_inc_to = 1;
++      }
++
++      if ( !data->autinc_to && CONSTANT_P (to_addr))
++      data->to_addr = copy_addr_to_reg (to_addr);
++    }
++
++  if (! SLOW_UNALIGNED_ACCESS (word_mode, align)
++      || align > MOVE_MAX * BITS_PER_UNIT || align >= BIGGEST_ALIGNMENT)
++    align = MOVE_MAX * BITS_PER_UNIT;
++
++  /* First store what we can in the largest integer mode, then go to
++     successively smaller modes.  */
++
++  while (max_size > 1)
++    {
++      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
++         tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
++      if (GET_MODE_SIZE (tmode) < max_size)
++        mode = tmode;
++
++      if (mode == VOIDmode)
++      break;
++
++      icode = mov_optab->handlers[(int) mode].insn_code;
++      if (icode != CODE_FOR_nothing && align >= GET_MODE_ALIGNMENT (mode))
++      store_by_pieces_2 (GEN_FCN (icode), mode, data);
++
++      max_size = GET_MODE_SIZE (mode);
++    }
++
++  /* The code above should have handled everything.  */
++  if (data->len != 0)
++    abort ();
++}
++
++/* Subroutine of store_by_pieces_1.  Store as many bytes as appropriate
++   with move instructions for mode MODE.  GENFUN is the gen_... function
++   to make a move insn for that mode.  DATA has all the other info.  */
++
++static void
++store_by_pieces_2 (genfun, mode, data)
++     rtx (*genfun) PARAMS ((rtx, ...));
++     enum machine_mode mode;
++     struct store_by_pieces *data;
++{
++  unsigned int size = GET_MODE_SIZE (mode);
++  rtx to1, cst;
++
++  while (data->len >= size)
++    {
++      if (data->reverse)
++      data->offset -= size;
++
++      if (data->autinc_to)
++      to1 = adjust_automodify_address (data->to, mode, data->to_addr,
++                                       data->offset);
++      else
++      to1 = adjust_address (data->to, mode, data->offset);
++
++      if (HAVE_PRE_DECREMENT && data->explicit_inc_to < 0)
++      emit_insn (gen_add2_insn (data->to_addr,
++                                GEN_INT (-(HOST_WIDE_INT) size)));
++
++      cst = (*data->constfun) (data->constfundata, data->offset, mode);
++      emit_insn ((*genfun) (to1, cst));
++
++      if (HAVE_POST_INCREMENT && data->explicit_inc_to > 0)
++      emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
++
++      if (! data->reverse)
++      data->offset += size;
++
++      data->len -= size;
++    }
++}
++\f
++/* Write zeros through the storage of OBJECT.  If OBJECT has BLKmode, SIZE is
++   its length in bytes.  */
++
++rtx
++clear_storage (object, size)
++     rtx object;
++     rtx size;
++{
++  rtx retval = 0;
++  unsigned int align = (GET_CODE (object) == MEM ? MEM_ALIGN (object)
++                      : GET_MODE_ALIGNMENT (GET_MODE (object)));
++
++  /* If OBJECT is not BLKmode and SIZE is the same size as its mode,
++     just move a zero.  Otherwise, do this a piece at a time.  */
++  if (GET_MODE (object) != BLKmode
++      && GET_CODE (size) == CONST_INT
++      && INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (object)))
++    emit_move_insn (object, CONST0_RTX (GET_MODE (object)));
++  else
++    {
++      object = protect_from_queue (object, 1);
++      size = protect_from_queue (size, 0);
++
++      if (GET_CODE (size) == CONST_INT && INTVAL (size) == 0)
++      ;
++      else if (GET_CODE (size) == CONST_INT
++        && CLEAR_BY_PIECES_P (INTVAL (size), align))
++      clear_by_pieces (object, INTVAL (size), align);
++      else if (clear_storage_via_clrstr (object, size, align))
++      ;
++      else
++      retval = clear_storage_via_libcall (object, size);
++    }
++
++  return retval;
++}
++
++/* A subroutine of clear_storage.  Expand a clrstr pattern;
++   return true if successful.  */
++
++static bool
++clear_storage_via_clrstr (object, size, align)
++     rtx object, size;
++     unsigned int align;
++{
++  /* Try the most limited insn first, because there's no point
++     including more than one in the machine description unless
++     the more limited one has some advantage.  */
++
++  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
++  enum machine_mode mode;
++
++  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
++       mode = GET_MODE_WIDER_MODE (mode))
++    {
++      enum insn_code code = clrstr_optab[(int) mode];
++      insn_operand_predicate_fn pred;
++
++      if (code != CODE_FOR_nothing
++        /* We don't need MODE to be narrower than
++           BITS_PER_HOST_WIDE_INT here because if SIZE is less than
++           the mode mask, as it is returned by the macro, it will
++           definitely be less than the actual mode mask.  */
++        && ((GET_CODE (size) == CONST_INT
++             && ((unsigned HOST_WIDE_INT) INTVAL (size)
++                 <= (GET_MODE_MASK (mode) >> 1)))
++            || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
++        && ((pred = insn_data[(int) code].operand[0].predicate) == 0
++            || (*pred) (object, BLKmode))
++        && ((pred = insn_data[(int) code].operand[2].predicate) == 0
++            || (*pred) (opalign, VOIDmode)))
++      {
++        rtx op1;
++        rtx last = get_last_insn ();
++        rtx pat;
++
++        op1 = convert_to_mode (mode, size, 1);
++        pred = insn_data[(int) code].operand[1].predicate;
++        if (pred != 0 && ! (*pred) (op1, mode))
++          op1 = copy_to_mode_reg (mode, op1);
++
++        pat = GEN_FCN ((int) code) (object, op1, opalign);
++        if (pat)
++          {
++            emit_insn (pat);
++            return true;
++          }
++        else
++          delete_insns_since (last);
++      }
++    }
++
++  return false;
++}
++
++/* A subroutine of clear_storage.  Expand a call to memset or bzero.
++   Return the return value of memset, 0 otherwise.  */
++
++static rtx
++clear_storage_via_libcall (object, size)
++     rtx object, size;
++{
++  tree call_expr, arg_list, fn, object_tree, size_tree;
++  enum machine_mode size_mode;
++  rtx retval;
++
++  /* OBJECT or SIZE may have been passed through protect_from_queue.
++
++     It is unsafe to save the value generated by protect_from_queue
++     and reuse it later.  Consider what happens if emit_queue is
++     called before the return value from protect_from_queue is used.
++
++     Expansion of the CALL_EXPR below will call emit_queue before
++     we are finished emitting RTL for argument setup.  So if we are
++     not careful we could get the wrong value for an argument.
++
++     To avoid this problem we go ahead and emit code to copy OBJECT
++     and SIZE into new pseudos.  We can then place those new pseudos
++     into an RTL_EXPR and use them later, even after a call to
++     emit_queue.
++
++     Note this is not strictly needed for library calls since they
++     do not call emit_queue before loading their arguments.  However,
++     we may need to have library calls call emit_queue in the future
++     since failing to do so could cause problems for targets which
++     define SMALL_REGISTER_CLASSES and pass arguments in registers.  */
++
++  object = copy_to_mode_reg (Pmode, XEXP (object, 0));
++
++  if (TARGET_MEM_FUNCTIONS)
++    size_mode = TYPE_MODE (sizetype);
++  else
++    size_mode = TYPE_MODE (unsigned_type_node);
++  size = convert_to_mode (size_mode, size, 1);
++  size = copy_to_mode_reg (size_mode, size);
++
++  /* It is incorrect to use the libcall calling conventions to call
++     memset in this context.  This could be a user call to memset and
++     the user may wish to examine the return value from memset.  For
++     targets where libcalls and normal calls have different conventions
++     for returning pointers, we could end up generating incorrect code.
++
++     For convenience, we generate the call to bzero this way as well.  */
++
++  object_tree = make_tree (ptr_type_node, object);
++  if (TARGET_MEM_FUNCTIONS)
++    size_tree = make_tree (sizetype, size);
++  else
++    size_tree = make_tree (unsigned_type_node, size);
++
++  fn = clear_storage_libcall_fn (true);
++  arg_list = tree_cons (NULL_TREE, size_tree, NULL_TREE);
++  if (TARGET_MEM_FUNCTIONS)
++    arg_list = tree_cons (NULL_TREE, integer_zero_node, arg_list);
++  arg_list = tree_cons (NULL_TREE, object_tree, arg_list);
++
++  /* Now we have to build up the CALL_EXPR itself.  */
++  call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
++  call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
++                   call_expr, arg_list, NULL_TREE);
++  TREE_SIDE_EFFECTS (call_expr) = 1;
++
++  retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
++
++  /* If we are initializing a readonly value, show the above call
++     clobbered it.  Otherwise, a load from it may erroneously be
++     hoisted from a loop.  */
++  if (RTX_UNCHANGING_P (object))
++    emit_insn (gen_rtx_CLOBBER (VOIDmode, object));
++
++  return (TARGET_MEM_FUNCTIONS ? retval : NULL_RTX);
++}
++
++/* A subroutine of clear_storage_via_libcall.  Create the tree node
++   for the function we use for block clears.  The first time FOR_CALL
++   is true, we call assemble_external.  */
++
++static GTY(()) tree block_clear_fn;
++
++static tree
++clear_storage_libcall_fn (for_call)
++     int for_call;
++{
++  static bool emitted_extern;
++  tree fn = block_clear_fn, args;
++
++  if (!fn)
++    {
++      if (TARGET_MEM_FUNCTIONS)
++      {
++        fn = get_identifier ("memset");
++        args = build_function_type_list (ptr_type_node, ptr_type_node,
++                                         integer_type_node, sizetype,
++                                         NULL_TREE);
++      }
++      else
++      {
++        fn = get_identifier ("bzero");
++        args = build_function_type_list (void_type_node, ptr_type_node,
++                                         unsigned_type_node, NULL_TREE);
++      }
++
++      fn = build_decl (FUNCTION_DECL, fn, args);
++      DECL_EXTERNAL (fn) = 1;
++      TREE_PUBLIC (fn) = 1;
++      DECL_ARTIFICIAL (fn) = 1;
++      TREE_NOTHROW (fn) = 1;
++
++      block_clear_fn = fn;
++    }
++
++  if (for_call && !emitted_extern)
++    {
++      emitted_extern = true;
++      make_decl_rtl (fn, NULL);
++      assemble_external (fn);
++    }
++
++  return fn;
++}
++\f
++/* Generate code to copy Y into X.
++   Both Y and X must have the same mode, except that
++   Y can be a constant with VOIDmode.
++   This mode cannot be BLKmode; use emit_block_move for that.
++
++   Return the last instruction emitted.  */
++
++rtx
++emit_move_insn (x, y)
++     rtx x, y;
++{
++  enum machine_mode mode = GET_MODE (x);
++  rtx y_cst = NULL_RTX;
++  rtx last_insn;
++
++  x = protect_from_queue (x, 1);
++  y = protect_from_queue (y, 0);
++
++  if (mode == BLKmode || (GET_MODE (y) != mode && GET_MODE (y) != VOIDmode))
++    abort ();
++
++  /* Never force constant_p_rtx to memory.  */
++  if (GET_CODE (y) == CONSTANT_P_RTX)
++    ;
++  else if (CONSTANT_P (y))
++    {
++      if (optimize
++        && SCALAR_FLOAT_MODE_P (GET_MODE (x))
++        && (last_insn = compress_float_constant (x, y)))
++      return last_insn;
++
++      if (!LEGITIMATE_CONSTANT_P (y))
++      {
++        y_cst = y;
++        y = force_const_mem (mode, y);
++
++        /* If the target's cannot_force_const_mem prevented the spill,
++           assume that the target's move expanders will also take care
++           of the non-legitimate constant.  */
++        if (!y)
++          y = y_cst;
++      }
++    }
++
++  /* If X or Y are memory references, verify that their addresses are valid
++     for the machine.  */
++  if (GET_CODE (x) == MEM
++      && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
++         && ! push_operand (x, GET_MODE (x)))
++        || (flag_force_addr
++            && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
++    x = validize_mem (x);
++
++  if (GET_CODE (y) == MEM
++      && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
++        || (flag_force_addr
++            && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
++    y = validize_mem (y);
++
++  if (mode == BLKmode)
++    abort ();
++
++  last_insn = emit_move_insn_1 (x, y);
++
++  if (y_cst && GET_CODE (x) == REG)
++    set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
++
++  return last_insn;
++}
++
++/* Low level part of emit_move_insn.
++   Called just like emit_move_insn, but assumes X and Y
++   are basically valid.  */
++
++rtx
++emit_move_insn_1 (x, y)
++     rtx x, y;
++{
++  enum machine_mode mode = GET_MODE (x);
++  enum machine_mode submode;
++  enum mode_class class = GET_MODE_CLASS (mode);
++
++  if ((unsigned int) mode >= (unsigned int) MAX_MACHINE_MODE)
++    abort ();
++
++  if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    return
++      emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
++
++  /* Expand complex moves by moving real part and imag part, if possible.  */
++  else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
++         && BLKmode != (submode = GET_MODE_INNER (mode))
++         && (mov_optab->handlers[(int) submode].insn_code
++             != CODE_FOR_nothing))
++    {
++      /* Don't split destination if it is a stack push.  */
++      int stack = push_operand (x, GET_MODE (x));
++
++#ifdef PUSH_ROUNDING
++      /* In case we output to the stack, but the size is smaller machine can
++       push exactly, we need to use move instructions.  */
++      if (stack
++        && (PUSH_ROUNDING (GET_MODE_SIZE (submode))
++            != GET_MODE_SIZE (submode)))
++      {
++        rtx temp;
++        HOST_WIDE_INT offset1, offset2;
++
++        /* Do not use anti_adjust_stack, since we don't want to update
++           stack_pointer_delta.  */
++        temp = expand_binop (Pmode,
++#ifdef STACK_GROWS_DOWNWARD
++                             sub_optab,
++#else
++                             add_optab,
++#endif
++                             stack_pointer_rtx,
++                             GEN_INT
++                               (PUSH_ROUNDING
++                                (GET_MODE_SIZE (GET_MODE (x)))),
++                             stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
++
++        if (temp != stack_pointer_rtx)
++          emit_move_insn (stack_pointer_rtx, temp);
++
++#ifdef STACK_GROWS_DOWNWARD
++        offset1 = 0;
++        offset2 = GET_MODE_SIZE (submode);
++#else
++        offset1 = -PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)));
++        offset2 = (-PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)))
++                   + GET_MODE_SIZE (submode));
++#endif
++
++        emit_move_insn (change_address (x, submode,
++                                        gen_rtx_PLUS (Pmode,
++                                                      stack_pointer_rtx,
++                                                      GEN_INT (offset1))),
++                        gen_realpart (submode, y));
++        emit_move_insn (change_address (x, submode,
++                                        gen_rtx_PLUS (Pmode,
++                                                      stack_pointer_rtx,
++                                                      GEN_INT (offset2))),
++                        gen_imagpart (submode, y));
++      }
++      else
++#endif
++      /* If this is a stack, push the highpart first, so it
++       will be in the argument order.
++
++       In that case, change_address is used only to convert
++       the mode, not to change the address.  */
++      if (stack)
++      {
++        /* Note that the real part always precedes the imag part in memory
++           regardless of machine's endianness.  */
++#ifdef STACK_GROWS_DOWNWARD
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (gen_rtx_MEM (submode, XEXP (x, 0)),
++                    gen_imagpart (submode, y)));
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (gen_rtx_MEM (submode, XEXP (x, 0)),
++                    gen_realpart (submode, y)));
++#else
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (gen_rtx_MEM (submode, XEXP (x, 0)),
++                    gen_realpart (submode, y)));
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (gen_rtx_MEM (submode, XEXP (x, 0)),
++                    gen_imagpart (submode, y)));
++#endif
++      }
++      else
++      {
++        rtx realpart_x, realpart_y;
++        rtx imagpart_x, imagpart_y;
++
++        /* If this is a complex value with each part being smaller than a
++           word, the usual calling sequence will likely pack the pieces into
++           a single register.  Unfortunately, SUBREG of hard registers only
++           deals in terms of words, so we have a problem converting input
++           arguments to the CONCAT of two registers that is used elsewhere
++           for complex values.  If this is before reload, we can copy it into
++           memory and reload.  FIXME, we should see about using extract and
++           insert on integer registers, but complex short and complex char
++           variables should be rarely used.  */
++        if (GET_MODE_BITSIZE (mode) < 2 * BITS_PER_WORD
++            && (reload_in_progress | reload_completed) == 0)
++          {
++            int packed_dest_p
++              = (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER);
++            int packed_src_p
++              = (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER);
++
++            if (packed_dest_p || packed_src_p)
++              {
++                enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT)
++                                             ? MODE_FLOAT : MODE_INT);
++
++                enum machine_mode reg_mode
++                  = mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1);
++
++                if (reg_mode != BLKmode)
++                  {
++                    rtx mem = assign_stack_temp (reg_mode,
++                                                 GET_MODE_SIZE (mode), 0);
++                    rtx cmem = adjust_address (mem, mode, 0);
++
++                    cfun->cannot_inline
++                      = N_("function using short complex types cannot be inline");
++
++                    if (packed_dest_p)
++                      {
++                        rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
++
++                        emit_move_insn_1 (cmem, y);
++                        return emit_move_insn_1 (sreg, mem);
++                      }
++                    else
++                      {
++                        rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
++
++                        emit_move_insn_1 (mem, sreg);
++                        return emit_move_insn_1 (x, cmem);
++                      }
++                  }
++              }
++          }
++
++        realpart_x = gen_realpart (submode, x);
++        realpart_y = gen_realpart (submode, y);
++        imagpart_x = gen_imagpart (submode, x);
++        imagpart_y = gen_imagpart (submode, y);
++
++        /* Show the output dies here.  This is necessary for SUBREGs
++           of pseudos since we cannot track their lifetimes correctly;
++           hard regs shouldn't appear here except as return values.
++           We never want to emit such a clobber after reload.  */
++        if (x != y
++            && ! (reload_in_progress || reload_completed)
++            && (GET_CODE (realpart_x) == SUBREG
++                || GET_CODE (imagpart_x) == SUBREG))
++          emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
++
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (realpart_x, realpart_y));
++        emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
++                   (imagpart_x, imagpart_y));
++      }
++
++      return get_last_insn ();
++    }
++
++  /* This will handle any multi-word or full-word mode that lacks a move_insn
++     pattern.  However, you will get better code if you define such patterns,
++     even if they must turn into multiple assembler instructions.  */
++  else if (GET_MODE_SIZE (mode) >= UNITS_PER_WORD)
++    {
++      rtx last_insn = 0;
++      rtx seq, inner;
++      int need_clobber;
++      int i;
++
++#ifdef PUSH_ROUNDING
++
++      /* If X is a push on the stack, do the push now and replace
++       X with a reference to the stack pointer.  */
++      if (push_operand (x, GET_MODE (x)))
++      {
++        rtx temp;
++        enum rtx_code code;
++
++        /* Do not use anti_adjust_stack, since we don't want to update
++           stack_pointer_delta.  */
++        temp = expand_binop (Pmode,
++#ifdef STACK_GROWS_DOWNWARD
++                             sub_optab,
++#else
++                             add_optab,
++#endif
++                             stack_pointer_rtx,
++                             GEN_INT
++                               (PUSH_ROUNDING
++                                (GET_MODE_SIZE (GET_MODE (x)))),
++                             stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
++
++        if (temp != stack_pointer_rtx)
++          emit_move_insn (stack_pointer_rtx, temp);
++
++        code = GET_CODE (XEXP (x, 0));
++
++        /* Just hope that small offsets off SP are OK.  */
++        if (code == POST_INC)
++          temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
++                              GEN_INT (-((HOST_WIDE_INT)
++                                         GET_MODE_SIZE (GET_MODE (x)))));
++        else if (code == POST_DEC)
++          temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
++                              GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
++        else
++          temp = stack_pointer_rtx;
++
++        x = change_address (x, VOIDmode, temp);
++      }
++#endif
++
++      /* If we are in reload, see if either operand is a MEM whose address
++       is scheduled for replacement.  */
++      if (reload_in_progress && GET_CODE (x) == MEM
++        && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
++      x = replace_equiv_address_nv (x, inner);
++      if (reload_in_progress && GET_CODE (y) == MEM
++        && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
++      y = replace_equiv_address_nv (y, inner);
++
++      start_sequence ();
++
++      need_clobber = 0;
++      for (i = 0;
++         i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
++         i++)
++      {
++        rtx xpart = operand_subword (x, i, 1, mode);
++        rtx ypart = operand_subword (y, i, 1, mode);
++
++        /* If we can't get a part of Y, put Y into memory if it is a
++           constant.  Otherwise, force it into a register.  If we still
++           can't get a part of Y, abort.  */
++        if (ypart == 0 && CONSTANT_P (y))
++          {
++            y = force_const_mem (mode, y);
++            ypart = operand_subword (y, i, 1, mode);
++          }
++        else if (ypart == 0)
++          ypart = operand_subword_force (y, i, mode);
++
++        if (xpart == 0 || ypart == 0)
++          abort ();
++
++        need_clobber |= (GET_CODE (xpart) == SUBREG);
++
++        last_insn = emit_move_insn (xpart, ypart);
++      }
++
++      seq = get_insns ();
++      end_sequence ();
++
++      /* Show the output dies here.  This is necessary for SUBREGs
++       of pseudos since we cannot track their lifetimes correctly;
++       hard regs shouldn't appear here except as return values.
++       We never want to emit such a clobber after reload.  */
++      if (x != y
++        && ! (reload_in_progress || reload_completed)
++        && need_clobber != 0)
++      emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
++
++      emit_insn (seq);
++
++      return last_insn;
++    }
++  else
++    abort ();
++}
++
++/* If Y is representable exactly in a narrower mode, and the target can
++   perform the extension directly from constant or memory, then emit the
++   move as an extension.  */
++
++static rtx
++compress_float_constant (x, y)
++     rtx x, y;
++{
++  enum machine_mode dstmode = GET_MODE (x);
++  enum machine_mode orig_srcmode = GET_MODE (y);
++  enum machine_mode srcmode;
++  REAL_VALUE_TYPE r;
++
++  REAL_VALUE_FROM_CONST_DOUBLE (r, y);
++
++  for (srcmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_srcmode));
++       srcmode != orig_srcmode;
++       srcmode = GET_MODE_WIDER_MODE (srcmode))
++    {
++      enum insn_code ic;
++      rtx trunc_y, last_insn;
++
++      /* Skip if the target can't extend this way.  */
++      ic = can_extend_p (dstmode, srcmode, 0);
++      if (ic == CODE_FOR_nothing)
++      continue;
++
++      /* Skip if the narrowed value isn't exact.  */
++      if (! exact_real_truncate (srcmode, &r))
++      continue;
++
++      trunc_y = CONST_DOUBLE_FROM_REAL_VALUE (r, srcmode);
++
++      if (LEGITIMATE_CONSTANT_P (trunc_y))
++      {
++        /* Skip if the target needs extra instructions to perform
++           the extension.  */
++        if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
++          continue;
++      }
++      else if (float_extend_from_mem[dstmode][srcmode])
++      trunc_y = validize_mem (force_const_mem (srcmode, trunc_y));
++      else
++      continue;
++
++      emit_unop_insn (ic, x, trunc_y, UNKNOWN);
++      last_insn = get_last_insn ();
++
++      if (GET_CODE (x) == REG)
++      REG_NOTES (last_insn)
++        = gen_rtx_EXPR_LIST (REG_EQUAL, y, REG_NOTES (last_insn));
++
++      return last_insn;
++    }
++
++  return NULL_RTX;
++}
++\f
++/* Pushing data onto the stack.  */
++
++/* Push a block of length SIZE (perhaps variable)
++   and return an rtx to address the beginning of the block.
++   Note that it is not possible for the value returned to be a QUEUED.
++   The value may be virtual_outgoing_args_rtx.
++
++   EXTRA is the number of bytes of padding to push in addition to SIZE.
++   BELOW nonzero means this padding comes at low addresses;
++   otherwise, the padding comes at high addresses.  */
++
++rtx
++push_block (size, extra, below)
++     rtx size;
++     int extra, below;
++{
++  rtx temp;
++
++  size = convert_modes (Pmode, ptr_mode, size, 1);
++  if (CONSTANT_P (size))
++    anti_adjust_stack (plus_constant (size, extra));
++  else if (GET_CODE (size) == REG && extra == 0)
++    anti_adjust_stack (size);
++  else
++    {
++      temp = copy_to_mode_reg (Pmode, size);
++      if (extra != 0)
++      temp = expand_binop (Pmode, add_optab, temp, GEN_INT (extra),
++                           temp, 0, OPTAB_LIB_WIDEN);
++      anti_adjust_stack (temp);
++    }
++
++#ifndef STACK_GROWS_DOWNWARD
++  if (0)
++#else
++  if (1)
++#endif
++    {
++      temp = virtual_outgoing_args_rtx;
++      if (extra != 0 && below)
++      temp = plus_constant (temp, extra);
++    }
++  else
++    {
++      if (GET_CODE (size) == CONST_INT)
++      temp = plus_constant (virtual_outgoing_args_rtx,
++                            -INTVAL (size) - (below ? 0 : extra));
++      else if (extra != 0 && !below)
++      temp = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
++                           negate_rtx (Pmode, plus_constant (size, extra)));
++      else
++      temp = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
++                           negate_rtx (Pmode, size));
++    }
++
++  return memory_address (GET_CLASS_NARROWEST_MODE (MODE_INT), temp);
++}
++
++#ifdef PUSH_ROUNDING
++
++/* Emit single push insn.  */
++
++static void
++emit_single_push_insn (mode, x, type)
++     rtx x;
++     enum machine_mode mode;
++     tree type;
++{
++  rtx dest_addr;
++  unsigned rounded_size = PUSH_ROUNDING (GET_MODE_SIZE (mode));
++  rtx dest;
++  enum insn_code icode;
++  insn_operand_predicate_fn pred;
++
++  stack_pointer_delta += PUSH_ROUNDING (GET_MODE_SIZE (mode));
++  /* If there is push pattern, use it.  Otherwise try old way of throwing
++     MEM representing push operation to move expander.  */
++  icode = push_optab->handlers[(int) mode].insn_code;
++  if (icode != CODE_FOR_nothing)
++    {
++      if (((pred = insn_data[(int) icode].operand[0].predicate)
++         && !((*pred) (x, mode))))
++      x = force_reg (mode, x);
++      emit_insn (GEN_FCN (icode) (x));
++      return;
++    }
++  if (GET_MODE_SIZE (mode) == rounded_size)
++    dest_addr = gen_rtx_fmt_e (STACK_PUSH_CODE, Pmode, stack_pointer_rtx);
++  else
++    {
++#ifdef STACK_GROWS_DOWNWARD
++      dest_addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
++                              GEN_INT (-(HOST_WIDE_INT) rounded_size));
++#else
++      dest_addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
++                              GEN_INT (rounded_size));
++#endif
++      dest_addr = gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx, dest_addr);
++    }
++
++  dest = gen_rtx_MEM (mode, dest_addr);
++
++  if (type != 0)
++    {
++      set_mem_attributes (dest, type, 1);
++
++      if (flag_optimize_sibling_calls)
++      /* Function incoming arguments may overlap with sibling call
++         outgoing arguments and we cannot allow reordering of reads
++         from function arguments with stores to outgoing arguments
++         of sibling calls.  */
++      set_mem_alias_set (dest, 0);
++    }
++  emit_move_insn (dest, x);
++}
++#endif
++
++/* Generate code to push X onto the stack, assuming it has mode MODE and
++   type TYPE.
++   MODE is redundant except when X is a CONST_INT (since they don't
++   carry mode info).
++   SIZE is an rtx for the size of data to be copied (in bytes),
++   needed only if X is BLKmode.
++
++   ALIGN (in bits) is maximum alignment we can assume.
++
++   If PARTIAL and REG are both nonzero, then copy that many of the first
++   words of X into registers starting with REG, and push the rest of X.
++   The amount of space pushed is decreased by PARTIAL words,
++   rounded *down* to a multiple of PARM_BOUNDARY.
++   REG must be a hard register in this case.
++   If REG is zero but PARTIAL is not, take any all others actions for an
++   argument partially in registers, but do not actually load any
++   registers.
++
++   EXTRA is the amount in bytes of extra space to leave next to this arg.
++   This is ignored if an argument block has already been allocated.
++
++   On a machine that lacks real push insns, ARGS_ADDR is the address of
++   the bottom of the argument block for this call.  We use indexing off there
++   to store the arg.  On machines with push insns, ARGS_ADDR is 0 when a
++   argument block has not been preallocated.
++
++   ARGS_SO_FAR is the size of args previously pushed for this call.
++
++   REG_PARM_STACK_SPACE is nonzero if functions require stack space
++   for arguments passed in registers.  If nonzero, it will be the number
++   of bytes required.  */
++
++void
++emit_push_insn (x, mode, type, size, align, partial, reg, extra,
++              args_addr, args_so_far, reg_parm_stack_space,
++              alignment_pad)
++     rtx x;
++     enum machine_mode mode;
++     tree type;
++     rtx size;
++     unsigned int align;
++     int partial;
++     rtx reg;
++     int extra;
++     rtx args_addr;
++     rtx args_so_far;
++     int reg_parm_stack_space;
++     rtx alignment_pad;
++{
++  rtx xinner;
++  enum direction stack_direction
++#ifdef STACK_GROWS_DOWNWARD
++    = downward;
++#else
++    = upward;
++#endif
++
++  /* Decide where to pad the argument: `downward' for below,
++     `upward' for above, or `none' for don't pad it.
++     Default is below for small data on big-endian machines; else above.  */
++  enum direction where_pad = FUNCTION_ARG_PADDING (mode, type);
++
++  /* Invert direction if stack is post-decrement.
++     FIXME: why?  */
++  if (STACK_PUSH_CODE == POST_DEC)
++    if (where_pad != none)
++      where_pad = (where_pad == downward ? upward : downward);
++
++  xinner = x = protect_from_queue (x, 0);
++
++  if (mode == BLKmode)
++    {
++      /* Copy a block into the stack, entirely or partially.  */
++
++      rtx temp;
++      int used = partial * UNITS_PER_WORD;
++      int offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
++      int skip;
++
++      if (size == 0)
++      abort ();
++
++      used -= offset;
++
++      /* USED is now the # of bytes we need not copy to the stack
++       because registers will take care of them.  */
++
++      if (partial != 0)
++      xinner = adjust_address (xinner, BLKmode, used);
++
++      /* If the partial register-part of the arg counts in its stack size,
++       skip the part of stack space corresponding to the registers.
++       Otherwise, start copying to the beginning of the stack space,
++       by setting SKIP to 0.  */
++      skip = (reg_parm_stack_space == 0) ? 0 : used;
++
++#ifdef PUSH_ROUNDING
++      /* Do it with several push insns if that doesn't take lots of insns
++       and if there is no difficulty with push insns that skip bytes
++       on the stack for alignment purposes.  */
++      if (args_addr == 0
++        && PUSH_ARGS
++        && GET_CODE (size) == CONST_INT
++        && skip == 0
++        && (MOVE_BY_PIECES_P ((unsigned) INTVAL (size) - used, align))
++        /* Here we avoid the case of a structure whose weak alignment
++           forces many pushes of a small amount of data,
++           and such small pushes do rounding that causes trouble.  */
++        && ((! SLOW_UNALIGNED_ACCESS (word_mode, align))
++            || align >= BIGGEST_ALIGNMENT
++            || (PUSH_ROUNDING (align / BITS_PER_UNIT)
++                == (align / BITS_PER_UNIT)))
++        && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size))
++      {
++        /* Push padding now if padding above and stack grows down,
++           or if padding below and stack grows up.
++           But if space already allocated, this has already been done.  */
++        if (extra && args_addr == 0
++            && where_pad != none && where_pad != stack_direction)
++          anti_adjust_stack (GEN_INT (extra));
++
++        move_by_pieces (NULL, xinner, INTVAL (size) - used, align);
++      }
++      else
++#endif /* PUSH_ROUNDING  */
++      {
++        rtx target;
++
++        /* Otherwise make space on the stack and copy the data
++           to the address of that space.  */
++
++        /* Deduct words put into registers from the size we must copy.  */
++        if (partial != 0)
++          {
++            if (GET_CODE (size) == CONST_INT)
++              size = GEN_INT (INTVAL (size) - used);
++            else
++              size = expand_binop (GET_MODE (size), sub_optab, size,
++                                   GEN_INT (used), NULL_RTX, 0,
++                                   OPTAB_LIB_WIDEN);
++          }
++
++        /* Get the address of the stack space.
++           In this case, we do not deal with EXTRA separately.
++           A single stack adjust will do.  */
++        if (! args_addr)
++          {
++            temp = push_block (size, extra, where_pad == downward);
++            extra = 0;
++          }
++        else if (GET_CODE (args_so_far) == CONST_INT)
++          temp = memory_address (BLKmode,
++                                 plus_constant (args_addr,
++                                                skip + INTVAL (args_so_far)));
++        else
++          temp = memory_address (BLKmode,
++                                 plus_constant (gen_rtx_PLUS (Pmode,
++                                                              args_addr,
++                                                              args_so_far),
++                                                skip));
++
++        if (!ACCUMULATE_OUTGOING_ARGS)
++          {
++            /* If the source is referenced relative to the stack pointer,
++               copy it to another register to stabilize it.  We do not need
++               to do this if we know that we won't be changing sp.  */
++
++            if (reg_mentioned_p (virtual_stack_dynamic_rtx, temp)
++                || reg_mentioned_p (virtual_outgoing_args_rtx, temp))
++              temp = copy_to_reg (temp);
++          }
++
++        target = gen_rtx_MEM (BLKmode, temp);
++
++        if (type != 0)
++          {
++            set_mem_attributes (target, type, 1);
++            /* Function incoming arguments may overlap with sibling call
++               outgoing arguments and we cannot allow reordering of reads
++               from function arguments with stores to outgoing arguments
++               of sibling calls.  */
++            set_mem_alias_set (target, 0);
++          }
++
++        /* ALIGN may well be better aligned than TYPE, e.g. due to
++           PARM_BOUNDARY.  Assume the caller isn't lying.  */
++        set_mem_align (target, align);
++
++        emit_block_move (target, xinner, size, BLOCK_OP_CALL_PARM);
++      }
++    }
++  else if (partial > 0)
++    {
++      /* Scalar partly in registers.  */
++
++      int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
++      int i;
++      int not_stack;
++      /* # words of start of argument
++       that we must make space for but need not store.  */
++      int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD);
++      int args_offset = INTVAL (args_so_far);
++      int skip;
++
++      /* Push padding now if padding above and stack grows down,
++       or if padding below and stack grows up.
++       But if space already allocated, this has already been done.  */
++      if (extra && args_addr == 0
++        && where_pad != none && where_pad != stack_direction)
++      anti_adjust_stack (GEN_INT (extra));
++
++      /* If we make space by pushing it, we might as well push
++       the real data.  Otherwise, we can leave OFFSET nonzero
++       and leave the space uninitialized.  */
++      if (args_addr == 0)
++      offset = 0;
++
++      /* Now NOT_STACK gets the number of words that we don't need to
++       allocate on the stack.  */
++      not_stack = partial - offset;
++
++      /* If the partial register-part of the arg counts in its stack size,
++       skip the part of stack space corresponding to the registers.
++       Otherwise, start copying to the beginning of the stack space,
++       by setting SKIP to 0.  */
++      skip = (reg_parm_stack_space == 0) ? 0 : not_stack;
++
++      if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
++      x = validize_mem (force_const_mem (mode, x));
++
++      /* If X is a hard register in a non-integer mode, copy it into a pseudo;
++       SUBREGs of such registers are not allowed.  */
++      if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER
++         && GET_MODE_CLASS (GET_MODE (x)) != MODE_INT))
++      x = copy_to_reg (x);
++
++      /* Loop over all the words allocated on the stack for this arg.  */
++      /* We can do it by words, because any scalar bigger than a word
++       has a size a multiple of a word.  */
++#ifndef PUSH_ARGS_REVERSED
++      for (i = not_stack; i < size; i++)
++#else
++      for (i = size - 1; i >= not_stack; i--)
++#endif
++      if (i >= not_stack + offset)
++        emit_push_insn (operand_subword_force (x, i, mode),
++                        word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX,
++                        0, args_addr,
++                        GEN_INT (args_offset + ((i - not_stack + skip)
++                                                * UNITS_PER_WORD)),
++                        reg_parm_stack_space, alignment_pad);
++    }
++  else
++    {
++      rtx addr;
++      rtx target = NULL_RTX;
++      rtx dest;
++
++      /* Push padding now if padding above and stack grows down,
++       or if padding below and stack grows up.
++       But if space already allocated, this has already been done.  */
++      if (extra && args_addr == 0
++        && where_pad != none && where_pad != stack_direction)
++      anti_adjust_stack (GEN_INT (extra));
++
++#ifdef PUSH_ROUNDING
++      if (args_addr == 0 && PUSH_ARGS)
++      emit_single_push_insn (mode, x, type);
++      else
++#endif
++      {
++        if (GET_CODE (args_so_far) == CONST_INT)
++          addr
++            = memory_address (mode,
++                              plus_constant (args_addr,
++                                             INTVAL (args_so_far)));
++        else
++          addr = memory_address (mode, gen_rtx_PLUS (Pmode, args_addr,
++                                                     args_so_far));
++        target = addr;
++        dest = gen_rtx_MEM (mode, addr);
++        if (type != 0)
++          {
++            set_mem_attributes (dest, type, 1);
++            /* Function incoming arguments may overlap with sibling call
++               outgoing arguments and we cannot allow reordering of reads
++               from function arguments with stores to outgoing arguments
++               of sibling calls.  */
++            set_mem_alias_set (dest, 0);
++          }
++
++        emit_move_insn (dest, x);
++      }
++    }
++
++  /* If part should go in registers, copy that part
++     into the appropriate registers.  Do this now, at the end,
++     since mem-to-mem copies above may do function calls.  */
++  if (partial > 0 && reg != 0)
++    {
++      /* Handle calls that pass values in multiple non-contiguous locations.
++       The Irix 6 ABI has examples of this.  */
++      if (GET_CODE (reg) == PARALLEL)
++      emit_group_load (reg, x, -1);  /* ??? size? */
++      else
++      move_block_to_reg (REGNO (reg), x, partial, mode);
++    }
++
++  if (extra && args_addr == 0 && where_pad == stack_direction)
++    anti_adjust_stack (GEN_INT (extra));
++
++  if (alignment_pad && args_addr == 0)
++    anti_adjust_stack (alignment_pad);
++}
++\f
++/* Return X if X can be used as a subtarget in a sequence of arithmetic
++   operations.  */
++
++static rtx
++get_subtarget (x)
++     rtx x;
++{
++  return ((x == 0
++         /* Only registers can be subtargets.  */
++         || GET_CODE (x) != REG
++         /* If the register is readonly, it can't be set more than once.  */
++         || RTX_UNCHANGING_P (x)
++         /* Don't use hard regs to avoid extending their life.  */
++         || REGNO (x) < FIRST_PSEUDO_REGISTER
++         /* Avoid subtargets inside loops,
++            since they hide some invariant expressions.  */
++         || preserve_subexpressions_p ())
++        ? 0 : x);
++}
++
++/* Expand an assignment that stores the value of FROM into TO.
++   If WANT_VALUE is nonzero, return an rtx for the value of TO.
++   (This may contain a QUEUED rtx;
++   if the value is constant, this rtx is a constant.)
++   Otherwise, the returned value is NULL_RTX.
++
++   SUGGEST_REG is no longer actually used.
++   It used to mean, copy the value through a register
++   and return that register, if that is possible.
++   We now use WANT_VALUE to decide whether to do this.  */
++
++rtx
++expand_assignment (to, from, want_value, suggest_reg)
++     tree to, from;
++     int want_value;
++     int suggest_reg ATTRIBUTE_UNUSED;
++{
++  rtx to_rtx = 0;
++  rtx result;
++
++  /* Don't crash if the lhs of the assignment was erroneous.  */
++
++  if (TREE_CODE (to) == ERROR_MARK)
++    {
++      result = expand_expr (from, NULL_RTX, VOIDmode, 0);
++      return want_value ? result : NULL_RTX;
++    }
++
++  /* Assignment of a structure component needs special treatment
++     if the structure component's rtx is not simply a MEM.
++     Assignment of an array element at a constant index, and assignment of
++     an array element in an unaligned packed structure field, has the same
++     problem.  */
++
++  if (TREE_CODE (to) == COMPONENT_REF || TREE_CODE (to) == BIT_FIELD_REF
++      || TREE_CODE (to) == ARRAY_REF || TREE_CODE (to) == ARRAY_RANGE_REF
++      || TREE_CODE (TREE_TYPE (to)) == ARRAY_TYPE)
++    {
++      enum machine_mode mode1;
++      HOST_WIDE_INT bitsize, bitpos;
++      rtx orig_to_rtx;
++      tree offset;
++      int unsignedp;
++      int volatilep = 0;
++      tree tem;
++
++      push_temp_slots ();
++      tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1,
++                               &unsignedp, &volatilep);
++
++      /* If we are going to use store_bit_field and extract_bit_field,
++       make sure to_rtx will be safe for multiple use.  */
++
++      if (mode1 == VOIDmode && want_value)
++      tem = stabilize_reference (tem);
++
++      orig_to_rtx = to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0);
++
++      if (offset != 0)
++      {
++        rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, EXPAND_SUM);
++
++        if (GET_CODE (to_rtx) != MEM)
++          abort ();
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++        if (GET_MODE (offset_rtx) != Pmode)
++          offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
++#else
++        if (GET_MODE (offset_rtx) != ptr_mode)
++          offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
++#endif
++
++        /* A constant address in TO_RTX can have VOIDmode, we must not try
++           to call force_reg for that case.  Avoid that case.  */
++        if (GET_CODE (to_rtx) == MEM
++            && GET_MODE (to_rtx) == BLKmode
++            && GET_MODE (XEXP (to_rtx, 0)) != VOIDmode
++            && bitsize > 0
++            && (bitpos % bitsize) == 0
++            && (bitsize % GET_MODE_ALIGNMENT (mode1)) == 0
++            && MEM_ALIGN (to_rtx) == GET_MODE_ALIGNMENT (mode1))
++          {
++            to_rtx = adjust_address (to_rtx, mode1, bitpos / BITS_PER_UNIT);
++            bitpos = 0;
++          }
++
++        to_rtx = offset_address (to_rtx, offset_rtx,
++                                 highest_pow2_factor_for_type (TREE_TYPE (to),
++                                                               offset));
++      }
++
++      if (GET_CODE (to_rtx) == MEM)
++      {
++        /* If the field is at offset zero, we could have been given the
++           DECL_RTX of the parent struct.  Don't munge it.  */
++        to_rtx = shallow_copy_rtx (to_rtx);
++
++        set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
++      }
++
++      /* Deal with volatile and readonly fields.  The former is only done
++       for MEM.  Also set MEM_KEEP_ALIAS_SET_P if needed.  */
++      if (volatilep && GET_CODE (to_rtx) == MEM)
++      {
++        if (to_rtx == orig_to_rtx)
++          to_rtx = copy_rtx (to_rtx);
++        MEM_VOLATILE_P (to_rtx) = 1;
++      }
++
++      if (TREE_CODE (to) == COMPONENT_REF
++        && TREE_READONLY (TREE_OPERAND (to, 1)))
++      {
++        if (to_rtx == orig_to_rtx)
++          to_rtx = copy_rtx (to_rtx);
++        RTX_UNCHANGING_P (to_rtx) = 1;
++      }
++
++      if (GET_CODE (to_rtx) == MEM && ! can_address_p (to))
++      {
++        if (to_rtx == orig_to_rtx)
++          to_rtx = copy_rtx (to_rtx);
++        MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
++      }
++
++      result = store_field (to_rtx, bitsize, bitpos, mode1, from,
++                          (want_value
++                           /* Spurious cast for HPUX compiler.  */
++                           ? ((enum machine_mode)
++                              TYPE_MODE (TREE_TYPE (to)))
++                           : VOIDmode),
++                          unsignedp, TREE_TYPE (tem), get_alias_set (to));
++
++      preserve_temp_slots (result);
++      free_temp_slots ();
++      pop_temp_slots ();
++
++      /* If the value is meaningful, convert RESULT to the proper mode.
++       Otherwise, return nothing.  */
++      return (want_value ? convert_modes (TYPE_MODE (TREE_TYPE (to)),
++                                        TYPE_MODE (TREE_TYPE (from)),
++                                        result,
++                                        TREE_UNSIGNED (TREE_TYPE (to)))
++            : NULL_RTX);
++    }
++
++  /* If the rhs is a function call and its value is not an aggregate,
++     call the function before we start to compute the lhs.
++     This is needed for correct code for cases such as
++     val = setjmp (buf) on machines where reference to val
++     requires loading up part of an address in a separate insn.
++
++     Don't do this if TO is a VAR_DECL or PARM_DECL whose DECL_RTL is REG
++     since it might be a promoted variable where the zero- or sign- extension
++     needs to be done.  Handling this in the normal way is safe because no
++     computation is done before the call.  */
++  if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from)
++      && TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) == INTEGER_CST
++      && ! ((TREE_CODE (to) == VAR_DECL || TREE_CODE (to) == PARM_DECL)
++          && GET_CODE (DECL_RTL (to)) == REG))
++    {
++      rtx value;
++
++      push_temp_slots ();
++      value = expand_expr (from, NULL_RTX, VOIDmode, 0);
++      if (to_rtx == 0)
++      to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
++
++      /* Handle calls that return values in multiple non-contiguous locations.
++       The Irix 6 ABI has examples of this.  */
++      if (GET_CODE (to_rtx) == PARALLEL)
++      emit_group_load (to_rtx, value, int_size_in_bytes (TREE_TYPE (from)));
++      else if (GET_MODE (to_rtx) == BLKmode)
++      emit_block_move (to_rtx, value, expr_size (from), BLOCK_OP_NORMAL);
++      else
++      {
++#ifdef POINTERS_EXTEND_UNSIGNED
++        if (POINTER_TYPE_P (TREE_TYPE (to))
++            && GET_MODE (to_rtx) != GET_MODE (value))
++          value = convert_memory_address (GET_MODE (to_rtx), value);
++#endif
++        emit_move_insn (to_rtx, value);
++      }
++      preserve_temp_slots (to_rtx);
++      free_temp_slots ();
++      pop_temp_slots ();
++      return want_value ? to_rtx : NULL_RTX;
++    }
++
++  /* Ordinary treatment.  Expand TO to get a REG or MEM rtx.
++     Don't re-expand if it was expanded already (in COMPONENT_REF case).  */
++
++  if (to_rtx == 0)
++    to_rtx = expand_expr (to, NULL_RTX, VOIDmode, EXPAND_WRITE);
++
++  /* Don't move directly into a return register.  */
++  if (TREE_CODE (to) == RESULT_DECL
++      && (GET_CODE (to_rtx) == REG || GET_CODE (to_rtx) == PARALLEL))
++    {
++      rtx temp;
++
++      push_temp_slots ();
++      temp = expand_expr (from, 0, GET_MODE (to_rtx), 0);
++
++      if (GET_CODE (to_rtx) == PARALLEL)
++      emit_group_load (to_rtx, temp, int_size_in_bytes (TREE_TYPE (from)));
++      else
++      emit_move_insn (to_rtx, temp);
++
++      preserve_temp_slots (to_rtx);
++      free_temp_slots ();
++      pop_temp_slots ();
++      return want_value ? to_rtx : NULL_RTX;
++    }
++
++  /* In case we are returning the contents of an object which overlaps
++     the place the value is being stored, use a safe function when copying
++     a value through a pointer into a structure value return block.  */
++  if (TREE_CODE (to) == RESULT_DECL && TREE_CODE (from) == INDIRECT_REF
++      && current_function_returns_struct
++      && !current_function_returns_pcc_struct)
++    {
++      rtx from_rtx, size;
++
++      push_temp_slots ();
++      size = expr_size (from);
++      from_rtx = expand_expr (from, NULL_RTX, VOIDmode, 0);
++
++      if (TARGET_MEM_FUNCTIONS)
++      emit_library_call (memmove_libfunc, LCT_NORMAL,
++                         VOIDmode, 3, XEXP (to_rtx, 0), Pmode,
++                         XEXP (from_rtx, 0), Pmode,
++                         convert_to_mode (TYPE_MODE (sizetype),
++                                          size, TREE_UNSIGNED (sizetype)),
++                         TYPE_MODE (sizetype));
++      else
++        emit_library_call (bcopy_libfunc, LCT_NORMAL,
++                         VOIDmode, 3, XEXP (from_rtx, 0), Pmode,
++                         XEXP (to_rtx, 0), Pmode,
++                         convert_to_mode (TYPE_MODE (integer_type_node),
++                                          size,
++                                          TREE_UNSIGNED (integer_type_node)),
++                         TYPE_MODE (integer_type_node));
++
++      preserve_temp_slots (to_rtx);
++      free_temp_slots ();
++      pop_temp_slots ();
++      return want_value ? to_rtx : NULL_RTX;
++    }
++
++  /* Compute FROM and store the value in the rtx we got.  */
++
++  push_temp_slots ();
++  result = store_expr (from, to_rtx, want_value);
++  preserve_temp_slots (result);
++  free_temp_slots ();
++  pop_temp_slots ();
++  return want_value ? result : NULL_RTX;
++}
++
++/* Generate code for computing expression EXP,
++   and storing the value into TARGET.
++   TARGET may contain a QUEUED rtx.
++
++   If WANT_VALUE & 1 is nonzero, return a copy of the value
++   not in TARGET, so that we can be sure to use the proper
++   value in a containing expression even if TARGET has something
++   else stored in it.  If possible, we copy the value through a pseudo
++   and return that pseudo.  Or, if the value is constant, we try to
++   return the constant.  In some cases, we return a pseudo
++   copied *from* TARGET.
++
++   If the mode is BLKmode then we may return TARGET itself.
++   It turns out that in BLKmode it doesn't cause a problem.
++   because C has no operators that could combine two different
++   assignments into the same BLKmode object with different values
++   with no sequence point.  Will other languages need this to
++   be more thorough?
++
++   If WANT_VALUE & 1 is 0, we return NULL, to make sure
++   to catch quickly any cases where the caller uses the value
++   and fails to set WANT_VALUE.
++
++   If WANT_VALUE & 2 is set, this is a store into a call param on the
++   stack, and block moves may need to be treated specially.  */
++
++rtx
++store_expr (exp, target, want_value)
++     tree exp;
++     rtx target;
++     int want_value;
++{
++  rtx temp;
++  int dont_return_target = 0;
++  int dont_store_target = 0;
++
++  if (VOID_TYPE_P (TREE_TYPE (exp)))
++    {
++      /* C++ can generate ?: expressions with a throw expression in one
++       branch and an rvalue in the other. Here, we resolve attempts to
++       store the throw expression's nonexistant result. */
++      if (want_value)
++      abort ();
++      expand_expr (exp, const0_rtx, VOIDmode, 0);
++      return NULL_RTX;
++    }
++  if (TREE_CODE (exp) == COMPOUND_EXPR)
++    {
++      /* Perform first part of compound expression, then assign from second
++       part.  */
++      expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
++                 want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
++      emit_queue ();
++      return store_expr (TREE_OPERAND (exp, 1), target, want_value);
++    }
++  else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
++    {
++      /* For conditional expression, get safe form of the target.  Then
++       test the condition, doing the appropriate assignment on either
++       side.  This avoids the creation of unnecessary temporaries.
++       For non-BLKmode, it is more efficient not to do this.  */
++
++      rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx ();
++
++      emit_queue ();
++      target = protect_from_queue (target, 1);
++
++      do_pending_stack_adjust ();
++      NO_DEFER_POP;
++      jumpifnot (TREE_OPERAND (exp, 0), lab1);
++      start_cleanup_deferral ();
++      store_expr (TREE_OPERAND (exp, 1), target, want_value & 2);
++      end_cleanup_deferral ();
++      emit_queue ();
++      emit_jump_insn (gen_jump (lab2));
++      emit_barrier ();
++      emit_label (lab1);
++      start_cleanup_deferral ();
++      store_expr (TREE_OPERAND (exp, 2), target, want_value & 2);
++      end_cleanup_deferral ();
++      emit_queue ();
++      emit_label (lab2);
++      OK_DEFER_POP;
++
++      return want_value & 1 ? target : NULL_RTX;
++    }
++  else if (queued_subexp_p (target))
++    /* If target contains a postincrement, let's not risk
++       using it as the place to generate the rhs.  */
++    {
++      if (GET_MODE (target) != BLKmode && GET_MODE (target) != VOIDmode)
++      {
++        /* Expand EXP into a new pseudo.  */
++        temp = gen_reg_rtx (GET_MODE (target));
++        temp = expand_expr (exp, temp, GET_MODE (target),
++                            (want_value & 2
++                             ? EXPAND_STACK_PARM : EXPAND_NORMAL));
++      }
++      else
++      temp = expand_expr (exp, NULL_RTX, GET_MODE (target),
++                          (want_value & 2
++                           ? EXPAND_STACK_PARM : EXPAND_NORMAL));
++
++      /* If target is volatile, ANSI requires accessing the value
++       *from* the target, if it is accessed.  So make that happen.
++       In no case return the target itself.  */
++      if (! MEM_VOLATILE_P (target) && (want_value & 1) != 0)
++      dont_return_target = 1;
++    }
++  else if ((want_value & 1) != 0
++         && GET_CODE (target) == MEM
++         && ! MEM_VOLATILE_P (target)
++         && GET_MODE (target) != BLKmode)
++    /* If target is in memory and caller wants value in a register instead,
++       arrange that.  Pass TARGET as target for expand_expr so that,
++       if EXP is another assignment, WANT_VALUE will be nonzero for it.
++       We know expand_expr will not use the target in that case.
++       Don't do this if TARGET is volatile because we are supposed
++       to write it and then read it.  */
++    {
++      temp = expand_expr (exp, target, GET_MODE (target),
++                        want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
++      if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode)
++      {
++        /* If TEMP is already in the desired TARGET, only copy it from
++           memory and don't store it there again.  */
++        if (temp == target
++            || (rtx_equal_p (temp, target)
++                && ! side_effects_p (temp) && ! side_effects_p (target)))
++          dont_store_target = 1;
++        temp = copy_to_reg (temp);
++      }
++      dont_return_target = 1;
++    }
++  else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
++    /* If this is a scalar in a register that is stored in a wider mode
++       than the declared mode, compute the result into its declared mode
++       and then convert to the wider mode.  Our value is the computed
++       expression.  */
++    {
++      rtx inner_target = 0;
++
++      /* If we don't want a value, we can do the conversion inside EXP,
++       which will often result in some optimizations.  Do the conversion
++       in two steps: first change the signedness, if needed, then
++       the extend.  But don't do this if the type of EXP is a subtype
++       of something else since then the conversion might involve
++       more than just converting modes.  */
++      if ((want_value & 1) == 0
++        && INTEGRAL_TYPE_P (TREE_TYPE (exp))
++        && TREE_TYPE (TREE_TYPE (exp)) == 0)
++      {
++        if (TREE_UNSIGNED (TREE_TYPE (exp))
++            != SUBREG_PROMOTED_UNSIGNED_P (target))
++          exp = convert
++            ((*lang_hooks.types.signed_or_unsigned_type)
++             (SUBREG_PROMOTED_UNSIGNED_P (target), TREE_TYPE (exp)), exp);
++
++        exp = convert ((*lang_hooks.types.type_for_mode)
++                       (GET_MODE (SUBREG_REG (target)),
++                        SUBREG_PROMOTED_UNSIGNED_P (target)),
++                       exp);
++
++        inner_target = SUBREG_REG (target);
++      }
++
++      temp = expand_expr (exp, inner_target, VOIDmode,
++                        want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
++
++      /* If TEMP is a volatile MEM and we want a result value, make
++       the access now so it gets done only once.  Likewise if
++       it contains TARGET.  */
++      if (GET_CODE (temp) == MEM && (want_value & 1) != 0
++        && (MEM_VOLATILE_P (temp)
++            || reg_mentioned_p (SUBREG_REG (target), XEXP (temp, 0))))
++      temp = copy_to_reg (temp);
++
++      /* If TEMP is a VOIDmode constant, use convert_modes to make
++       sure that we properly convert it.  */
++      if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
++      {
++        temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
++                              temp, SUBREG_PROMOTED_UNSIGNED_P (target));
++        temp = convert_modes (GET_MODE (SUBREG_REG (target)),
++                              GET_MODE (target), temp,
++                              SUBREG_PROMOTED_UNSIGNED_P (target));
++      }
++
++      convert_move (SUBREG_REG (target), temp,
++                  SUBREG_PROMOTED_UNSIGNED_P (target));
++
++      /* If we promoted a constant, change the mode back down to match
++       target.  Otherwise, the caller might get confused by a result whose
++       mode is larger than expected.  */
++
++      if ((want_value & 1) != 0 && GET_MODE (temp) != GET_MODE (target))
++      {
++        if (GET_MODE (temp) != VOIDmode)
++          {
++            temp = gen_lowpart_SUBREG (GET_MODE (target), temp);
++            SUBREG_PROMOTED_VAR_P (temp) = 1;
++            SUBREG_PROMOTED_UNSIGNED_SET (temp,
++              SUBREG_PROMOTED_UNSIGNED_P (target));
++          }
++        else
++          temp = convert_modes (GET_MODE (target),
++                                GET_MODE (SUBREG_REG (target)),
++                                temp, SUBREG_PROMOTED_UNSIGNED_P (target));
++      }
++
++      return want_value & 1 ? temp : NULL_RTX;
++    }
++  else
++    {
++      temp = expand_expr (exp, target, GET_MODE (target),
++                        want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
++      /* Return TARGET if it's a specified hardware register.
++       If TARGET is a volatile mem ref, either return TARGET
++       or return a reg copied *from* TARGET; ANSI requires this.
++
++       Otherwise, if TEMP is not TARGET, return TEMP
++       if it is constant (for efficiency),
++       or if we really want the correct value.  */
++      if (!(target && GET_CODE (target) == REG
++          && REGNO (target) < FIRST_PSEUDO_REGISTER)
++        && !(GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
++        && ! rtx_equal_p (temp, target)
++        && (CONSTANT_P (temp) || (want_value & 1) != 0))
++      dont_return_target = 1;
++    }
++
++  /* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
++     the same as that of TARGET, adjust the constant.  This is needed, for
++     example, in case it is a CONST_DOUBLE and we want only a word-sized
++     value.  */
++  if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode
++      && TREE_CODE (exp) != ERROR_MARK
++      && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
++    temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
++                        temp, TREE_UNSIGNED (TREE_TYPE (exp)));
++
++  /* If value was not generated in the target, store it there.
++     Convert the value to TARGET's type first if necessary.
++     If TEMP and TARGET compare equal according to rtx_equal_p, but
++     one or both of them are volatile memory refs, we have to distinguish
++     two cases:
++     - expand_expr has used TARGET.  In this case, we must not generate
++       another copy.  This can be detected by TARGET being equal according
++       to == .
++     - expand_expr has not used TARGET - that means that the source just
++       happens to have the same RTX form.  Since temp will have been created
++       by expand_expr, it will compare unequal according to == .
++       We must generate a copy in this case, to reach the correct number
++       of volatile memory references.  */
++
++  if ((! rtx_equal_p (temp, target)
++       || (temp != target && (side_effects_p (temp)
++                            || side_effects_p (target))))
++      && TREE_CODE (exp) != ERROR_MARK
++      && ! dont_store_target
++       /* If store_expr stores a DECL whose DECL_RTL(exp) == TARGET,
++          but TARGET is not valid memory reference, TEMP will differ
++          from TARGET although it is really the same location.  */
++      && (TREE_CODE_CLASS (TREE_CODE (exp)) != 'd'
++        || target != DECL_RTL_IF_SET (exp))
++      /* If there's nothing to copy, don't bother.  Don't call expr_size
++       unless necessary, because some front-ends (C++) expr_size-hook
++       aborts on objects that are not supposed to be bit-copied or
++       bit-initialized.  */
++      && expr_size (exp) != const0_rtx)
++    {
++      target = protect_from_queue (target, 1);
++      if (GET_MODE (temp) != GET_MODE (target)
++        && GET_MODE (temp) != VOIDmode)
++      {
++        int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
++        if (dont_return_target)
++          {
++            /* In this case, we will return TEMP,
++               so make sure it has the proper mode.
++               But don't forget to store the value into TARGET.  */
++            temp = convert_to_mode (GET_MODE (target), temp, unsignedp);
++            emit_move_insn (target, temp);
++          }
++        else
++          convert_move (target, temp, unsignedp);
++      }
++
++      else if (GET_MODE (temp) == BLKmode && TREE_CODE (exp) == STRING_CST)
++      {
++        /* Handle copying a string constant into an array.  The string
++           constant may be shorter than the array.  So copy just the string's
++           actual length, and clear the rest.  First get the size of the data
++           type of the string, which is actually the size of the target.  */
++        rtx size = expr_size (exp);
++
++        if (GET_CODE (size) == CONST_INT
++            && INTVAL (size) < TREE_STRING_LENGTH (exp))
++          emit_block_move (target, temp, size,
++                           (want_value & 2
++                            ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++        else
++          {
++            /* Compute the size of the data to copy from the string.  */
++            tree copy_size
++              = size_binop (MIN_EXPR,
++                            make_tree (sizetype, size),
++                            size_int (TREE_STRING_LENGTH (exp)));
++            rtx copy_size_rtx
++              = expand_expr (copy_size, NULL_RTX, VOIDmode,
++                             (want_value & 2
++                              ? EXPAND_STACK_PARM : EXPAND_NORMAL));
++            rtx label = 0;
++
++            /* Copy that much.  */
++            copy_size_rtx = convert_to_mode (ptr_mode, copy_size_rtx,
++                                             TREE_UNSIGNED (sizetype));
++            emit_block_move (target, temp, copy_size_rtx,
++                             (want_value & 2
++                              ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++
++            /* Figure out how much is left in TARGET that we have to clear.
++               Do all calculations in ptr_mode.  */
++            if (GET_CODE (copy_size_rtx) == CONST_INT)
++              {
++                size = plus_constant (size, -INTVAL (copy_size_rtx));
++                target = adjust_address (target, BLKmode,
++                                         INTVAL (copy_size_rtx));
++              }
++            else
++              {
++                size = expand_binop (TYPE_MODE (sizetype), sub_optab, size,
++                                     copy_size_rtx, NULL_RTX, 0,
++                                     OPTAB_LIB_WIDEN);
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++                if (GET_MODE (copy_size_rtx) != Pmode)
++                  copy_size_rtx = convert_to_mode (Pmode, copy_size_rtx,
++                                                   TREE_UNSIGNED (sizetype));
++#endif
++
++                target = offset_address (target, copy_size_rtx,
++                                         highest_pow2_factor (copy_size));
++                label = gen_label_rtx ();
++                emit_cmp_and_jump_insns (size, const0_rtx, LT, NULL_RTX,
++                                         GET_MODE (size), 0, label);
++              }
++
++            if (size != const0_rtx)
++              clear_storage (target, size);
++
++            if (label)
++              emit_label (label);
++          }
++      }
++      /* Handle calls that return values in multiple non-contiguous locations.
++       The Irix 6 ABI has examples of this.  */
++      else if (GET_CODE (target) == PARALLEL)
++      emit_group_load (target, temp, int_size_in_bytes (TREE_TYPE (exp)));
++      else if (GET_MODE (temp) == BLKmode)
++      emit_block_move (target, temp, expr_size (exp),
++                       (want_value & 2
++                        ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++      else
++      emit_move_insn (target, temp);
++    }
++
++  /* If we don't want a value, return NULL_RTX.  */
++  if ((want_value & 1) == 0)
++    return NULL_RTX;
++
++  /* If we are supposed to return TEMP, do so as long as it isn't a MEM.
++     ??? The latter test doesn't seem to make sense.  */
++  else if (dont_return_target && GET_CODE (temp) != MEM)
++    return temp;
++
++  /* Return TARGET itself if it is a hard register.  */
++  else if ((want_value & 1) != 0
++         && GET_MODE (target) != BLKmode
++         && ! (GET_CODE (target) == REG
++               && REGNO (target) < FIRST_PSEUDO_REGISTER))
++    return copy_to_reg (target);
++
++  else
++    return target;
++}
++\f
++/* Return 1 if EXP just contains zeros.  */
++
++static int
++is_zeros_p (exp)
++     tree exp;
++{
++  tree elt;
++
++  switch (TREE_CODE (exp))
++    {
++    case CONVERT_EXPR:
++    case NOP_EXPR:
++    case NON_LVALUE_EXPR:
++    case VIEW_CONVERT_EXPR:
++      return is_zeros_p (TREE_OPERAND (exp, 0));
++
++    case INTEGER_CST:
++      return integer_zerop (exp);
++
++    case COMPLEX_CST:
++      return
++      is_zeros_p (TREE_REALPART (exp)) && is_zeros_p (TREE_IMAGPART (exp));
++
++    case REAL_CST:
++      return REAL_VALUES_IDENTICAL (TREE_REAL_CST (exp), dconst0);
++
++    case VECTOR_CST:
++      for (elt = TREE_VECTOR_CST_ELTS (exp); elt;
++         elt = TREE_CHAIN (elt))
++      if (!is_zeros_p (TREE_VALUE (elt)))
++        return 0;
++
++      return 1;
++
++    case CONSTRUCTOR:
++      if (TREE_TYPE (exp) && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
++      return CONSTRUCTOR_ELTS (exp) == NULL_TREE;
++      for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
++      if (! is_zeros_p (TREE_VALUE (elt)))
++        return 0;
++
++      return 1;
++
++    default:
++      return 0;
++    }
++}
++
++/* Return 1 if EXP contains mostly (3/4)  zeros.  */
++
++static int
++mostly_zeros_p (exp)
++     tree exp;
++{
++  if (TREE_CODE (exp) == CONSTRUCTOR)
++    {
++      int elts = 0, zeros = 0;
++      tree elt = CONSTRUCTOR_ELTS (exp);
++      if (TREE_TYPE (exp) && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
++      {
++        /* If there are no ranges of true bits, it is all zero.  */
++        return elt == NULL_TREE;
++      }
++      for (; elt; elt = TREE_CHAIN (elt))
++      {
++        /* We do not handle the case where the index is a RANGE_EXPR,
++           so the statistic will be somewhat inaccurate.
++           We do make a more accurate count in store_constructor itself,
++           so since this function is only used for nested array elements,
++           this should be close enough.  */
++        if (mostly_zeros_p (TREE_VALUE (elt)))
++          zeros++;
++        elts++;
++      }
++
++      return 4 * zeros >= 3 * elts;
++    }
++
++  return is_zeros_p (exp);
++}
++\f
++/* Helper function for store_constructor.
++   TARGET, BITSIZE, BITPOS, MODE, EXP are as for store_field.
++   TYPE is the type of the CONSTRUCTOR, not the element type.
++   CLEARED is as for store_constructor.
++   ALIAS_SET is the alias set to use for any stores.
++
++   This provides a recursive shortcut back to store_constructor when it isn't
++   necessary to go through store_field.  This is so that we can pass through
++   the cleared field to let store_constructor know that we may not have to
++   clear a substructure if the outer structure has already been cleared.  */
++
++static void
++store_constructor_field (target, bitsize, bitpos, mode, exp, type, cleared,
++                       alias_set)
++     rtx target;
++     unsigned HOST_WIDE_INT bitsize;
++     HOST_WIDE_INT bitpos;
++     enum machine_mode mode;
++     tree exp, type;
++     int cleared;
++     int alias_set;
++{
++  if (TREE_CODE (exp) == CONSTRUCTOR
++      && bitpos % BITS_PER_UNIT == 0
++      /* If we have a nonzero bitpos for a register target, then we just
++       let store_field do the bitfield handling.  This is unlikely to
++       generate unnecessary clear instructions anyways.  */
++      && (bitpos == 0 || GET_CODE (target) == MEM))
++    {
++      if (GET_CODE (target) == MEM)
++      target
++        = adjust_address (target,
++                          GET_MODE (target) == BLKmode
++                          || 0 != (bitpos
++                                   % GET_MODE_ALIGNMENT (GET_MODE (target)))
++                          ? BLKmode : VOIDmode, bitpos / BITS_PER_UNIT);
++
++
++      /* Update the alias set, if required.  */
++      if (GET_CODE (target) == MEM && ! MEM_KEEP_ALIAS_SET_P (target)
++        && MEM_ALIAS_SET (target) != 0)
++      {
++        target = copy_rtx (target);
++        set_mem_alias_set (target, alias_set);
++      }
++
++      store_constructor (exp, target, cleared, bitsize / BITS_PER_UNIT);
++    }
++  else
++    store_field (target, bitsize, bitpos, mode, exp, VOIDmode, 0, type,
++               alias_set);
++}
++
++/* Store the value of constructor EXP into the rtx TARGET.
++   TARGET is either a REG or a MEM; we know it cannot conflict, since
++   safe_from_p has been called.
++   CLEARED is true if TARGET is known to have been zero'd.
++   SIZE is the number of bytes of TARGET we are allowed to modify: this
++   may not be the same as the size of EXP if we are assigning to a field
++   which has been packed to exclude padding bits.  */
++
++static void
++store_constructor (exp, target, cleared, size)
++     tree exp;
++     rtx target;
++     int cleared;
++     HOST_WIDE_INT size;
++{
++  tree type = TREE_TYPE (exp);
++#ifdef WORD_REGISTER_OPERATIONS
++  HOST_WIDE_INT exp_size = int_size_in_bytes (type);
++#endif
++
++  if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
++      || TREE_CODE (type) == QUAL_UNION_TYPE)
++    {
++      tree elt;
++
++      /* If size is zero or the target is already cleared, do nothing.  */
++      if (size == 0 || cleared)
++      cleared = 1;
++      /* We either clear the aggregate or indicate the value is dead.  */
++      else if ((TREE_CODE (type) == UNION_TYPE
++              || TREE_CODE (type) == QUAL_UNION_TYPE)
++             && ! CONSTRUCTOR_ELTS (exp))
++      /* If the constructor is empty, clear the union.  */
++      {
++        clear_storage (target, expr_size (exp));
++        cleared = 1;
++      }
++
++      /* If we are building a static constructor into a register,
++       set the initial value as zero so we can fold the value into
++       a constant.  But if more than one register is involved,
++       this probably loses.  */
++      else if (GET_CODE (target) == REG && TREE_STATIC (exp)
++             && GET_MODE_SIZE (GET_MODE (target)) <= UNITS_PER_WORD)
++      {
++        emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
++        cleared = 1;
++      }
++
++      /* If the constructor has fewer fields than the structure
++       or if we are initializing the structure to mostly zeros,
++       clear the whole structure first.  Don't do this if TARGET is a
++       register whose mode size isn't equal to SIZE since clear_storage
++       can't handle this case.  */
++      else if (((list_length (CONSTRUCTOR_ELTS (exp)) != fields_length (type))
++              || mostly_zeros_p (exp))
++             && (GET_CODE (target) != REG
++                 || ((HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (target))
++                     == size)))
++      {
++        clear_storage (target, GEN_INT (size));
++        cleared = 1;
++      }
++
++      if (! cleared)
++      emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
++
++      /* Store each element of the constructor into
++       the corresponding field of TARGET.  */
++
++      for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
++      {
++        tree field = TREE_PURPOSE (elt);
++        tree value = TREE_VALUE (elt);
++        enum machine_mode mode;
++        HOST_WIDE_INT bitsize;
++        HOST_WIDE_INT bitpos = 0;
++        int unsignedp;
++        tree offset;
++        rtx to_rtx = target;
++
++        /* Just ignore missing fields.
++           We cleared the whole structure, above,
++           if any fields are missing.  */
++        if (field == 0)
++          continue;
++
++        if (cleared && is_zeros_p (value))
++          continue;
++
++        if (host_integerp (DECL_SIZE (field), 1))
++          bitsize = tree_low_cst (DECL_SIZE (field), 1);
++        else
++          bitsize = -1;
++
++        unsignedp = TREE_UNSIGNED (field);
++        mode = DECL_MODE (field);
++        if (DECL_BIT_FIELD (field))
++          mode = VOIDmode;
++
++        offset = DECL_FIELD_OFFSET (field);
++        if (host_integerp (offset, 0)
++            && host_integerp (bit_position (field), 0))
++          {
++            bitpos = int_bit_position (field);
++            offset = 0;
++          }
++        else
++          bitpos = tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 0);
++
++        if (offset)
++          {
++            rtx offset_rtx;
++
++            if (contains_placeholder_p (offset))
++              offset = build (WITH_RECORD_EXPR, sizetype,
++                              offset, make_tree (TREE_TYPE (exp), target));
++
++            offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0);
++            if (GET_CODE (to_rtx) != MEM)
++              abort ();
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++            if (GET_MODE (offset_rtx) != Pmode)
++              offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
++#else
++            if (GET_MODE (offset_rtx) != ptr_mode)
++              offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
++#endif
++
++            to_rtx = offset_address (to_rtx, offset_rtx,
++                                     highest_pow2_factor (offset));
++          }
++
++        if (TREE_READONLY (field))
++          {
++            if (GET_CODE (to_rtx) == MEM)
++              to_rtx = copy_rtx (to_rtx);
++
++            RTX_UNCHANGING_P (to_rtx) = 1;
++          }
++
++#ifdef WORD_REGISTER_OPERATIONS
++        /* If this initializes a field that is smaller than a word, at the
++           start of a word, try to widen it to a full word.
++           This special case allows us to output C++ member function
++           initializations in a form that the optimizers can understand.  */
++        if (GET_CODE (target) == REG
++            && bitsize < BITS_PER_WORD
++            && bitpos % BITS_PER_WORD == 0
++            && GET_MODE_CLASS (mode) == MODE_INT
++            && TREE_CODE (value) == INTEGER_CST
++            && exp_size >= 0
++            && bitpos + BITS_PER_WORD <= exp_size * BITS_PER_UNIT)
++          {
++            tree type = TREE_TYPE (value);
++
++            if (TYPE_PRECISION (type) < BITS_PER_WORD)
++              {
++                type = (*lang_hooks.types.type_for_size)
++                  (BITS_PER_WORD, TREE_UNSIGNED (type));
++                value = convert (type, value);
++              }
++
++            if (BYTES_BIG_ENDIAN)
++              value
++                = fold (build (LSHIFT_EXPR, type, value,
++                               build_int_2 (BITS_PER_WORD - bitsize, 0)));
++            bitsize = BITS_PER_WORD;
++            mode = word_mode;
++          }
++#endif
++
++        if (GET_CODE (to_rtx) == MEM && !MEM_KEEP_ALIAS_SET_P (to_rtx)
++            && DECL_NONADDRESSABLE_P (field))
++          {
++            to_rtx = copy_rtx (to_rtx);
++            MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
++          }
++
++        store_constructor_field (to_rtx, bitsize, bitpos, mode,
++                                 value, type, cleared,
++                                 get_alias_set (TREE_TYPE (field)));
++      }
++    }
++  else if (TREE_CODE (type) == ARRAY_TYPE
++         || TREE_CODE (type) == VECTOR_TYPE)
++    {
++      tree elt;
++      int i;
++      int need_to_clear;
++      tree domain = TYPE_DOMAIN (type);
++      tree elttype = TREE_TYPE (type);
++      int const_bounds_p;
++      HOST_WIDE_INT minelt = 0;
++      HOST_WIDE_INT maxelt = 0;
++
++      /* Vectors are like arrays, but the domain is stored via an array
++       type indirectly.  */
++      if (TREE_CODE (type) == VECTOR_TYPE)
++      {
++        /* Note that although TYPE_DEBUG_REPRESENTATION_TYPE uses
++           the same field as TYPE_DOMAIN, we are not guaranteed that
++           it always will.  */
++        domain = TYPE_DEBUG_REPRESENTATION_TYPE (type);
++        domain = TYPE_DOMAIN (TREE_TYPE (TYPE_FIELDS (domain)));
++      }
++
++      const_bounds_p = (TYPE_MIN_VALUE (domain)
++                      && TYPE_MAX_VALUE (domain)
++                      && host_integerp (TYPE_MIN_VALUE (domain), 0)
++                      && host_integerp (TYPE_MAX_VALUE (domain), 0));
++
++      /* If we have constant bounds for the range of the type, get them.  */
++      if (const_bounds_p)
++      {
++        minelt = tree_low_cst (TYPE_MIN_VALUE (domain), 0);
++        maxelt = tree_low_cst (TYPE_MAX_VALUE (domain), 0);
++      }
++
++      /* If the constructor has fewer elements than the array,
++         clear the whole array first.  Similarly if this is
++         static constructor of a non-BLKmode object.  */
++      if (cleared || (GET_CODE (target) == REG && TREE_STATIC (exp)))
++      need_to_clear = 1;
++      else
++      {
++        HOST_WIDE_INT count = 0, zero_count = 0;
++        need_to_clear = ! const_bounds_p;
++
++        /* This loop is a more accurate version of the loop in
++           mostly_zeros_p (it handles RANGE_EXPR in an index).
++           It is also needed to check for missing elements.  */
++        for (elt = CONSTRUCTOR_ELTS (exp);
++             elt != NULL_TREE && ! need_to_clear;
++             elt = TREE_CHAIN (elt))
++          {
++            tree index = TREE_PURPOSE (elt);
++            HOST_WIDE_INT this_node_count;
++
++            if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
++              {
++                tree lo_index = TREE_OPERAND (index, 0);
++                tree hi_index = TREE_OPERAND (index, 1);
++
++                if (! host_integerp (lo_index, 1)
++                    || ! host_integerp (hi_index, 1))
++                  {
++                    need_to_clear = 1;
++                    break;
++                  }
++
++                this_node_count = (tree_low_cst (hi_index, 1)
++                                   - tree_low_cst (lo_index, 1) + 1);
++              }
++            else
++              this_node_count = 1;
++
++            count += this_node_count;
++            if (mostly_zeros_p (TREE_VALUE (elt)))
++              zero_count += this_node_count;
++          }
++
++        /* Clear the entire array first if there are any missing elements,
++           or if the incidence of zero elements is >= 75%.  */
++        if (! need_to_clear
++            && (count < maxelt - minelt + 1 || 4 * zero_count >= 3 * count))
++          need_to_clear = 1;
++      }
++
++      if (need_to_clear && size > 0)
++      {
++        if (! cleared)
++          {
++            if (REG_P (target))
++              emit_move_insn (target,  CONST0_RTX (GET_MODE (target)));
++            else
++              clear_storage (target, GEN_INT (size));
++          }
++        cleared = 1;
++      }
++      else if (REG_P (target))
++      /* Inform later passes that the old value is dead.  */
++      emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
++
++      /* Store each element of the constructor into
++       the corresponding element of TARGET, determined
++       by counting the elements.  */
++      for (elt = CONSTRUCTOR_ELTS (exp), i = 0;
++         elt;
++         elt = TREE_CHAIN (elt), i++)
++      {
++        enum machine_mode mode;
++        HOST_WIDE_INT bitsize;
++        HOST_WIDE_INT bitpos;
++        int unsignedp;
++        tree value = TREE_VALUE (elt);
++        tree index = TREE_PURPOSE (elt);
++        rtx xtarget = target;
++
++        if (cleared && is_zeros_p (value))
++          continue;
++
++        unsignedp = TREE_UNSIGNED (elttype);
++        mode = TYPE_MODE (elttype);
++        if (mode == BLKmode)
++          bitsize = (host_integerp (TYPE_SIZE (elttype), 1)
++                     ? tree_low_cst (TYPE_SIZE (elttype), 1)
++                     : -1);
++        else
++          bitsize = GET_MODE_BITSIZE (mode);
++
++        if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
++          {
++            tree lo_index = TREE_OPERAND (index, 0);
++            tree hi_index = TREE_OPERAND (index, 1);
++            rtx index_r, pos_rtx, hi_r, loop_top, loop_end;
++            struct nesting *loop;
++            HOST_WIDE_INT lo, hi, count;
++            tree position;
++
++            /* If the range is constant and "small", unroll the loop.  */
++            if (const_bounds_p
++                && host_integerp (lo_index, 0)
++                && host_integerp (hi_index, 0)
++                && (lo = tree_low_cst (lo_index, 0),
++                    hi = tree_low_cst (hi_index, 0),
++                    count = hi - lo + 1,
++                    (GET_CODE (target) != MEM
++                     || count <= 2
++                     || (host_integerp (TYPE_SIZE (elttype), 1)
++                         && (tree_low_cst (TYPE_SIZE (elttype), 1) * count
++                             <= 40 * 8)))))
++              {
++                lo -= minelt;  hi -= minelt;
++                for (; lo <= hi; lo++)
++                  {
++                    bitpos = lo * tree_low_cst (TYPE_SIZE (elttype), 0);
++
++                    if (GET_CODE (target) == MEM
++                        && !MEM_KEEP_ALIAS_SET_P (target)
++                        && TREE_CODE (type) == ARRAY_TYPE
++                        && TYPE_NONALIASED_COMPONENT (type))
++                      {
++                        target = copy_rtx (target);
++                        MEM_KEEP_ALIAS_SET_P (target) = 1;
++                      }
++
++                    store_constructor_field
++                      (target, bitsize, bitpos, mode, value, type, cleared,
++                       get_alias_set (elttype));
++                  }
++              }
++            else
++              {
++                hi_r = expand_expr (hi_index, NULL_RTX, VOIDmode, 0);
++                loop_top = gen_label_rtx ();
++                loop_end = gen_label_rtx ();
++
++                unsignedp = TREE_UNSIGNED (domain);
++
++                index = build_decl (VAR_DECL, NULL_TREE, domain);
++
++                index_r
++                  = gen_reg_rtx (promote_mode (domain, DECL_MODE (index),
++                                               &unsignedp, 0));
++                SET_DECL_RTL (index, index_r);
++                if (TREE_CODE (value) == SAVE_EXPR
++                    && SAVE_EXPR_RTL (value) == 0)
++                  {
++                    /* Make sure value gets expanded once before the
++                         loop.  */
++                    expand_expr (value, const0_rtx, VOIDmode, 0);
++                    emit_queue ();
++                  }
++                store_expr (lo_index, index_r, 0);
++                loop = expand_start_loop (0);
++
++                /* Assign value to element index.  */
++                position
++                  = convert (ssizetype,
++                             fold (build (MINUS_EXPR, TREE_TYPE (index),
++                                          index, TYPE_MIN_VALUE (domain))));
++                position = size_binop (MULT_EXPR, position,
++                                       convert (ssizetype,
++                                                TYPE_SIZE_UNIT (elttype)));
++
++                pos_rtx = expand_expr (position, 0, VOIDmode, 0);
++                xtarget = offset_address (target, pos_rtx,
++                                          highest_pow2_factor (position));
++                xtarget = adjust_address (xtarget, mode, 0);
++                if (TREE_CODE (value) == CONSTRUCTOR)
++                  store_constructor (value, xtarget, cleared,
++                                     bitsize / BITS_PER_UNIT);
++                else
++                  store_expr (value, xtarget, 0);
++
++                expand_exit_loop_if_false (loop,
++                                           build (LT_EXPR, integer_type_node,
++                                                  index, hi_index));
++
++                expand_increment (build (PREINCREMENT_EXPR,
++                                         TREE_TYPE (index),
++                                         index, integer_one_node), 0, 0);
++                expand_end_loop ();
++                emit_label (loop_end);
++              }
++          }
++        else if ((index != 0 && ! host_integerp (index, 0))
++                 || ! host_integerp (TYPE_SIZE (elttype), 1))
++          {
++            tree position;
++
++            if (index == 0)
++              index = ssize_int (1);
++
++            if (minelt)
++              index = convert (ssizetype,
++                               fold (build (MINUS_EXPR, index,
++                                            TYPE_MIN_VALUE (domain))));
++
++            position = size_binop (MULT_EXPR, index,
++                                   convert (ssizetype,
++                                            TYPE_SIZE_UNIT (elttype)));
++            xtarget = offset_address (target,
++                                      expand_expr (position, 0, VOIDmode, 0),
++                                      highest_pow2_factor (position));
++            xtarget = adjust_address (xtarget, mode, 0);
++            store_expr (value, xtarget, 0);
++          }
++        else
++          {
++            if (index != 0)
++              bitpos = ((tree_low_cst (index, 0) - minelt)
++                        * tree_low_cst (TYPE_SIZE (elttype), 1));
++            else
++              bitpos = (i * tree_low_cst (TYPE_SIZE (elttype), 1));
++
++            if (GET_CODE (target) == MEM && !MEM_KEEP_ALIAS_SET_P (target)
++                && TREE_CODE (type) == ARRAY_TYPE
++                && TYPE_NONALIASED_COMPONENT (type))
++              {
++                target = copy_rtx (target);
++                MEM_KEEP_ALIAS_SET_P (target) = 1;
++              }
++
++            store_constructor_field (target, bitsize, bitpos, mode, value,
++                                     type, cleared, get_alias_set (elttype));
++
++          }
++      }
++    }
++
++  /* Set constructor assignments.  */
++  else if (TREE_CODE (type) == SET_TYPE)
++    {
++      tree elt = CONSTRUCTOR_ELTS (exp);
++      unsigned HOST_WIDE_INT nbytes = int_size_in_bytes (type), nbits;
++      tree domain = TYPE_DOMAIN (type);
++      tree domain_min, domain_max, bitlength;
++
++      /* The default implementation strategy is to extract the constant
++       parts of the constructor, use that to initialize the target,
++       and then "or" in whatever non-constant ranges we need in addition.
++
++       If a large set is all zero or all ones, it is
++       probably better to set it using memset (if available) or bzero.
++       Also, if a large set has just a single range, it may also be
++       better to first clear all the first clear the set (using
++       bzero/memset), and set the bits we want.  */
++
++      /* Check for all zeros.  */
++      if (elt == NULL_TREE && size > 0)
++      {
++        if (!cleared)
++          clear_storage (target, GEN_INT (size));
++        return;
++      }
++
++      domain_min = convert (sizetype, TYPE_MIN_VALUE (domain));
++      domain_max = convert (sizetype, TYPE_MAX_VALUE (domain));
++      bitlength = size_binop (PLUS_EXPR,
++                            size_diffop (domain_max, domain_min),
++                            ssize_int (1));
++
++      nbits = tree_low_cst (bitlength, 1);
++
++      /* For "small" sets, or "medium-sized" (up to 32 bytes) sets that
++       are "complicated" (more than one range), initialize (the
++       constant parts) by copying from a constant.  */
++      if (GET_MODE (target) != BLKmode || nbits <= 2 * BITS_PER_WORD
++        || (nbytes <= 32 && TREE_CHAIN (elt) != NULL_TREE))
++      {
++        unsigned int set_word_size = TYPE_ALIGN (TREE_TYPE (exp));
++        enum machine_mode mode = mode_for_size (set_word_size, MODE_INT, 1);
++        char *bit_buffer = (char *) alloca (nbits);
++        HOST_WIDE_INT word = 0;
++        unsigned int bit_pos = 0;
++        unsigned int ibit = 0;
++        unsigned int offset = 0;  /* In bytes from beginning of set.  */
++
++        elt = get_set_constructor_bits (exp, bit_buffer, nbits);
++        for (;;)
++          {
++            if (bit_buffer[ibit])
++              {
++                if (BYTES_BIG_ENDIAN)
++                  word |= (1 << (set_word_size - 1 - bit_pos));
++                else
++                  word |= 1 << bit_pos;
++              }
++
++            bit_pos++;  ibit++;
++            if (bit_pos >= set_word_size || ibit == nbits)
++              {
++                if (word != 0 || ! cleared)
++                  {
++                    rtx datum = GEN_INT (word);
++                    rtx to_rtx;
++
++                    /* The assumption here is that it is safe to use
++                       XEXP if the set is multi-word, but not if
++                       it's single-word.  */
++                    if (GET_CODE (target) == MEM)
++                      to_rtx = adjust_address (target, mode, offset);
++                    else if (offset == 0)
++                      to_rtx = target;
++                    else
++                      abort ();
++                    emit_move_insn (to_rtx, datum);
++                  }
++
++                if (ibit == nbits)
++                  break;
++                word = 0;
++                bit_pos = 0;
++                offset += set_word_size / BITS_PER_UNIT;
++              }
++          }
++      }
++      else if (!cleared)
++      /* Don't bother clearing storage if the set is all ones.  */
++      if (TREE_CHAIN (elt) != NULL_TREE
++          || (TREE_PURPOSE (elt) == NULL_TREE
++              ? nbits != 1
++              : ( ! host_integerp (TREE_VALUE (elt), 0)
++                 || ! host_integerp (TREE_PURPOSE (elt), 0)
++                 || (tree_low_cst (TREE_VALUE (elt), 0)
++                     - tree_low_cst (TREE_PURPOSE (elt), 0) + 1
++                     != (HOST_WIDE_INT) nbits))))
++        clear_storage (target, expr_size (exp));
++
++      for (; elt != NULL_TREE; elt = TREE_CHAIN (elt))
++      {
++        /* Start of range of element or NULL.  */
++        tree startbit = TREE_PURPOSE (elt);
++        /* End of range of element, or element value.  */
++        tree endbit   = TREE_VALUE (elt);
++        HOST_WIDE_INT startb, endb;
++        rtx bitlength_rtx, startbit_rtx, endbit_rtx, targetx;
++
++        bitlength_rtx = expand_expr (bitlength,
++                                     NULL_RTX, MEM, EXPAND_CONST_ADDRESS);
++
++        /* Handle non-range tuple element like [ expr ].  */
++        if (startbit == NULL_TREE)
++          {
++            startbit = save_expr (endbit);
++            endbit = startbit;
++          }
++
++        startbit = convert (sizetype, startbit);
++        endbit = convert (sizetype, endbit);
++        if (! integer_zerop (domain_min))
++          {
++            startbit = size_binop (MINUS_EXPR, startbit, domain_min);
++            endbit = size_binop (MINUS_EXPR, endbit, domain_min);
++          }
++        startbit_rtx = expand_expr (startbit, NULL_RTX, MEM,
++                                    EXPAND_CONST_ADDRESS);
++        endbit_rtx = expand_expr (endbit, NULL_RTX, MEM,
++                                  EXPAND_CONST_ADDRESS);
++
++        if (REG_P (target))
++          {
++            targetx
++              = assign_temp
++                ((build_qualified_type ((*lang_hooks.types.type_for_mode)
++                                        (GET_MODE (target), 0),
++                                        TYPE_QUAL_CONST)),
++                 0, 1, 1);
++            emit_move_insn (targetx, target);
++          }
++
++        else if (GET_CODE (target) == MEM)
++          targetx = target;
++        else
++          abort ();
++
++        /* Optimization:  If startbit and endbit are constants divisible
++           by BITS_PER_UNIT, call memset instead.  */
++        if (TARGET_MEM_FUNCTIONS
++            && TREE_CODE (startbit) == INTEGER_CST
++            && TREE_CODE (endbit) == INTEGER_CST
++            && (startb = TREE_INT_CST_LOW (startbit)) % BITS_PER_UNIT == 0
++            && (endb = TREE_INT_CST_LOW (endbit) + 1) % BITS_PER_UNIT == 0)
++          {
++            emit_library_call (memset_libfunc, LCT_NORMAL,
++                               VOIDmode, 3,
++                               plus_constant (XEXP (targetx, 0),
++                                              startb / BITS_PER_UNIT),
++                               Pmode,
++                               constm1_rtx, TYPE_MODE (integer_type_node),
++                               GEN_INT ((endb - startb) / BITS_PER_UNIT),
++                               TYPE_MODE (sizetype));
++          }
++        else
++          emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__setbits"),
++                             LCT_NORMAL, VOIDmode, 4, XEXP (targetx, 0),
++                             Pmode, bitlength_rtx, TYPE_MODE (sizetype),
++                             startbit_rtx, TYPE_MODE (sizetype),
++                             endbit_rtx, TYPE_MODE (sizetype));
++
++        if (REG_P (target))
++          emit_move_insn (target, targetx);
++      }
++    }
++
++  else
++    abort ();
++}
++
++/* Store the value of EXP (an expression tree)
++   into a subfield of TARGET which has mode MODE and occupies
++   BITSIZE bits, starting BITPOS bits from the start of TARGET.
++   If MODE is VOIDmode, it means that we are storing into a bit-field.
++
++   If VALUE_MODE is VOIDmode, return nothing in particular.
++   UNSIGNEDP is not used in this case.
++
++   Otherwise, return an rtx for the value stored.  This rtx
++   has mode VALUE_MODE if that is convenient to do.
++   In this case, UNSIGNEDP must be nonzero if the value is an unsigned type.
++
++   TYPE is the type of the underlying object,
++
++   ALIAS_SET is the alias set for the destination.  This value will
++   (in general) be different from that for TARGET, since TARGET is a
++   reference to the containing structure.  */
++
++static rtx
++store_field (target, bitsize, bitpos, mode, exp, value_mode, unsignedp, type,
++           alias_set)
++     rtx target;
++     HOST_WIDE_INT bitsize;
++     HOST_WIDE_INT bitpos;
++     enum machine_mode mode;
++     tree exp;
++     enum machine_mode value_mode;
++     int unsignedp;
++     tree type;
++     int alias_set;
++{
++  HOST_WIDE_INT width_mask = 0;
++
++  if (TREE_CODE (exp) == ERROR_MARK)
++    return const0_rtx;
++
++  /* If we have nothing to store, do nothing unless the expression has
++     side-effects.  */
++  if (bitsize == 0)
++    return expand_expr (exp, const0_rtx, VOIDmode, 0);
++  else if (bitsize >=0 && bitsize < HOST_BITS_PER_WIDE_INT)
++    width_mask = ((HOST_WIDE_INT) 1 << bitsize) - 1;
++
++  /* If we are storing into an unaligned field of an aligned union that is
++     in a register, we may have the mode of TARGET being an integer mode but
++     MODE == BLKmode.  In that case, get an aligned object whose size and
++     alignment are the same as TARGET and store TARGET into it (we can avoid
++     the store if the field being stored is the entire width of TARGET).  Then
++     call ourselves recursively to store the field into a BLKmode version of
++     that object.  Finally, load from the object into TARGET.  This is not
++     very efficient in general, but should only be slightly more expensive
++     than the otherwise-required unaligned accesses.  Perhaps this can be
++     cleaned up later.  */
++
++  if (mode == BLKmode
++      && (GET_CODE (target) == REG || GET_CODE (target) == SUBREG))
++    {
++      rtx object
++      = assign_temp
++        (build_qualified_type (type, TYPE_QUALS (type) | TYPE_QUAL_CONST),
++         0, 1, 1);
++      rtx blk_object = adjust_address (object, BLKmode, 0);
++
++      if (bitsize != (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (target)))
++      emit_move_insn (object, target);
++
++      store_field (blk_object, bitsize, bitpos, mode, exp, VOIDmode, 0, type,
++                 alias_set);
++
++      emit_move_insn (target, object);
++
++      /* We want to return the BLKmode version of the data.  */
++      return blk_object;
++    }
++
++  if (GET_CODE (target) == CONCAT)
++    {
++      /* We're storing into a struct containing a single __complex.  */
++
++      if (bitpos != 0)
++      abort ();
++      return store_expr (exp, target, 0);
++    }
++
++  /* If the structure is in a register or if the component
++     is a bit field, we cannot use addressing to access it.
++     Use bit-field techniques or SUBREG to store in it.  */
++
++  if (mode == VOIDmode
++      || (mode != BLKmode && ! direct_store[(int) mode]
++        && GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
++        && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
++      || GET_CODE (target) == REG
++      || GET_CODE (target) == SUBREG
++      /* If the field isn't aligned enough to store as an ordinary memref,
++       store it as a bit field.  */
++      || (mode != BLKmode && SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (target))
++        && (MEM_ALIGN (target) < GET_MODE_ALIGNMENT (mode)
++            || bitpos % GET_MODE_ALIGNMENT (mode)))
++      /* If the RHS and field are a constant size and the size of the
++       RHS isn't the same size as the bitfield, we must use bitfield
++       operations.  */
++      || (bitsize >= 0
++        && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
++        && compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)), bitsize) != 0))
++    {
++      rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
++
++      /* If BITSIZE is narrower than the size of the type of EXP
++       we will be narrowing TEMP.  Normally, what's wanted are the
++       low-order bits.  However, if EXP's type is a record and this is
++       big-endian machine, we want the upper BITSIZE bits.  */
++      if (BYTES_BIG_ENDIAN && GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT
++        && bitsize < (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (temp))
++        && TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
++      temp = expand_shift (RSHIFT_EXPR, GET_MODE (temp), temp,
++                           size_int (GET_MODE_BITSIZE (GET_MODE (temp))
++                                     - bitsize),
++                           temp, 1);
++
++      /* Unless MODE is VOIDmode or BLKmode, convert TEMP to
++       MODE.  */
++      if (mode != VOIDmode && mode != BLKmode
++        && mode != TYPE_MODE (TREE_TYPE (exp)))
++      temp = convert_modes (mode, TYPE_MODE (TREE_TYPE (exp)), temp, 1);
++
++      /* If the modes of TARGET and TEMP are both BLKmode, both
++       must be in memory and BITPOS must be aligned on a byte
++       boundary.  If so, we simply do a block copy.  */
++      if (GET_MODE (target) == BLKmode && GET_MODE (temp) == BLKmode)
++      {
++        if (GET_CODE (target) != MEM || GET_CODE (temp) != MEM
++            || bitpos % BITS_PER_UNIT != 0)
++          abort ();
++
++        target = adjust_address (target, VOIDmode, bitpos / BITS_PER_UNIT);
++        emit_block_move (target, temp,
++                         GEN_INT ((bitsize + BITS_PER_UNIT - 1)
++                                  / BITS_PER_UNIT),
++                         BLOCK_OP_NORMAL);
++
++        return value_mode == VOIDmode ? const0_rtx : target;
++      }
++
++      /* Store the value in the bitfield.  */
++      store_bit_field (target, bitsize, bitpos, mode, temp,
++                     int_size_in_bytes (type));
++
++      if (value_mode != VOIDmode)
++      {
++        /* The caller wants an rtx for the value.
++           If possible, avoid refetching from the bitfield itself.  */
++        if (width_mask != 0
++            && ! (GET_CODE (target) == MEM && MEM_VOLATILE_P (target)))
++          {
++            tree count;
++            enum machine_mode tmode;
++
++            tmode = GET_MODE (temp);
++            if (tmode == VOIDmode)
++              tmode = value_mode;
++
++            if (unsignedp)
++              return expand_and (tmode, temp,
++                                 gen_int_mode (width_mask, tmode),
++                                 NULL_RTX);
++
++            count = build_int_2 (GET_MODE_BITSIZE (tmode) - bitsize, 0);
++            temp = expand_shift (LSHIFT_EXPR, tmode, temp, count, 0, 0);
++            return expand_shift (RSHIFT_EXPR, tmode, temp, count, 0, 0);
++          }
++
++        return extract_bit_field (target, bitsize, bitpos, unsignedp,
++                                  NULL_RTX, value_mode, VOIDmode,
++                                  int_size_in_bytes (type));
++      }
++      return const0_rtx;
++    }
++  else
++    {
++      rtx addr = XEXP (target, 0);
++      rtx to_rtx = target;
++
++      /* If a value is wanted, it must be the lhs;
++       so make the address stable for multiple use.  */
++
++      if (value_mode != VOIDmode && GET_CODE (addr) != REG
++        && ! CONSTANT_ADDRESS_P (addr)
++        /* A frame-pointer reference is already stable.  */
++        && ! (GET_CODE (addr) == PLUS
++              && GET_CODE (XEXP (addr, 1)) == CONST_INT
++              && (XEXP (addr, 0) == virtual_incoming_args_rtx
++                  || XEXP (addr, 0) == virtual_stack_vars_rtx)))
++      to_rtx = replace_equiv_address (to_rtx, copy_to_reg (addr));
++
++      /* Now build a reference to just the desired component.  */
++
++      to_rtx = adjust_address (target, mode, bitpos / BITS_PER_UNIT);
++
++      if (to_rtx == target)
++      to_rtx = copy_rtx (to_rtx);
++
++      MEM_SET_IN_STRUCT_P (to_rtx, 1);
++      if (!MEM_KEEP_ALIAS_SET_P (to_rtx) && MEM_ALIAS_SET (to_rtx) != 0)
++      set_mem_alias_set (to_rtx, alias_set);
++
++      return store_expr (exp, to_rtx, value_mode != VOIDmode);
++    }
++}
++\f
++/* Given an expression EXP that may be a COMPONENT_REF, a BIT_FIELD_REF,
++   an ARRAY_REF, or an ARRAY_RANGE_REF, look for nested operations of these
++   codes and find the ultimate containing object, which we return.
++
++   We set *PBITSIZE to the size in bits that we want, *PBITPOS to the
++   bit position, and *PUNSIGNEDP to the signedness of the field.
++   If the position of the field is variable, we store a tree
++   giving the variable offset (in units) in *POFFSET.
++   This offset is in addition to the bit position.
++   If the position is not variable, we store 0 in *POFFSET.
++
++   If any of the extraction expressions is volatile,
++   we store 1 in *PVOLATILEP.  Otherwise we don't change that.
++
++   If the field is a bit-field, *PMODE is set to VOIDmode.  Otherwise, it
++   is a mode that can be used to access the field.  In that case, *PBITSIZE
++   is redundant.
++
++   If the field describes a variable-sized object, *PMODE is set to
++   VOIDmode and *PBITSIZE is set to -1.  An access cannot be made in
++   this case, but the address of the object can be found.  */
++
++tree
++get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
++                   punsignedp, pvolatilep)
++     tree exp;
++     HOST_WIDE_INT *pbitsize;
++     HOST_WIDE_INT *pbitpos;
++     tree *poffset;
++     enum machine_mode *pmode;
++     int *punsignedp;
++     int *pvolatilep;
++{
++  tree size_tree = 0;
++  enum machine_mode mode = VOIDmode;
++  tree offset = size_zero_node;
++  tree bit_offset = bitsize_zero_node;
++  tree placeholder_ptr = 0;
++  tree tem;
++
++  /* First get the mode, signedness, and size.  We do this from just the
++     outermost expression.  */
++  if (TREE_CODE (exp) == COMPONENT_REF)
++    {
++      size_tree = DECL_SIZE (TREE_OPERAND (exp, 1));
++      if (! DECL_BIT_FIELD (TREE_OPERAND (exp, 1)))
++      mode = DECL_MODE (TREE_OPERAND (exp, 1));
++
++      *punsignedp = TREE_UNSIGNED (TREE_OPERAND (exp, 1));
++    }
++  else if (TREE_CODE (exp) == BIT_FIELD_REF)
++    {
++      size_tree = TREE_OPERAND (exp, 1);
++      *punsignedp = TREE_UNSIGNED (exp);
++    }
++  else
++    {
++      mode = TYPE_MODE (TREE_TYPE (exp));
++      *punsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
++
++      if (mode == BLKmode)
++      size_tree = TYPE_SIZE (TREE_TYPE (exp));
++      else
++      *pbitsize = GET_MODE_BITSIZE (mode);
++    }
++
++  if (size_tree != 0)
++    {
++      if (! host_integerp (size_tree, 1))
++      mode = BLKmode, *pbitsize = -1;
++      else
++      *pbitsize = tree_low_cst (size_tree, 1);
++    }
++
++  /* Compute cumulative bit-offset for nested component-refs and array-refs,
++     and find the ultimate containing object.  */
++  while (1)
++    {
++      if (TREE_CODE (exp) == BIT_FIELD_REF)
++      bit_offset = size_binop (PLUS_EXPR, bit_offset, TREE_OPERAND (exp, 2));
++      else if (TREE_CODE (exp) == COMPONENT_REF)
++      {
++        tree field = TREE_OPERAND (exp, 1);
++        tree this_offset = DECL_FIELD_OFFSET (field);
++
++        /* If this field hasn't been filled in yet, don't go
++           past it.  This should only happen when folding expressions
++           made during type construction.  */
++        if (this_offset == 0)
++          break;
++        else if (! TREE_CONSTANT (this_offset)
++                 && contains_placeholder_p (this_offset))
++          this_offset = build (WITH_RECORD_EXPR, sizetype, this_offset, exp);
++
++        offset = size_binop (PLUS_EXPR, offset, this_offset);
++        bit_offset = size_binop (PLUS_EXPR, bit_offset,
++                                 DECL_FIELD_BIT_OFFSET (field));
++
++        /* ??? Right now we don't do anything with DECL_OFFSET_ALIGN.  */
++      }
++
++      else if (TREE_CODE (exp) == ARRAY_REF
++             || TREE_CODE (exp) == ARRAY_RANGE_REF)
++      {
++        tree index = TREE_OPERAND (exp, 1);
++        tree array = TREE_OPERAND (exp, 0);
++        tree domain = TYPE_DOMAIN (TREE_TYPE (array));
++        tree low_bound = (domain ? TYPE_MIN_VALUE (domain) : 0);
++        tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array)));
++
++        /* We assume all arrays have sizes that are a multiple of a byte.
++           First subtract the lower bound, if any, in the type of the
++           index, then convert to sizetype and multiply by the size of the
++           array element.  */
++        if (low_bound != 0 && ! integer_zerop (low_bound))
++          index = fold (build (MINUS_EXPR, TREE_TYPE (index),
++                               index, low_bound));
++
++        /* If the index has a self-referential type, pass it to a
++           WITH_RECORD_EXPR; if the component size is, pass our
++           component to one.  */
++        if (! TREE_CONSTANT (index)
++            && contains_placeholder_p (index))
++          index = build (WITH_RECORD_EXPR, TREE_TYPE (index), index, exp);
++        if (! TREE_CONSTANT (unit_size)
++            && contains_placeholder_p (unit_size))
++          unit_size = build (WITH_RECORD_EXPR, sizetype, unit_size, array);
++
++        offset = size_binop (PLUS_EXPR, offset,
++                             size_binop (MULT_EXPR,
++                                         convert (sizetype, index),
++                                         unit_size));
++      }
++
++      else if (TREE_CODE (exp) == PLACEHOLDER_EXPR)
++      {
++        tree new = find_placeholder (exp, &placeholder_ptr);
++
++        /* If we couldn't find the replacement, return the PLACEHOLDER_EXPR.
++           We might have been called from tree optimization where we
++           haven't set up an object yet.  */
++        if (new == 0)
++          break;
++        else
++          exp = new;
++
++        continue;
++      }
++      else if (TREE_CODE (exp) != NON_LVALUE_EXPR
++             && TREE_CODE (exp) != VIEW_CONVERT_EXPR
++             && ! ((TREE_CODE (exp) == NOP_EXPR
++                    || TREE_CODE (exp) == CONVERT_EXPR)
++                   && (TYPE_MODE (TREE_TYPE (exp))
++                       == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
++      break;
++
++      /* If any reference in the chain is volatile, the effect is volatile.  */
++      if (TREE_THIS_VOLATILE (exp))
++      *pvolatilep = 1;
++
++      exp = TREE_OPERAND (exp, 0);
++    }
++
++  /* If OFFSET is constant, see if we can return the whole thing as a
++     constant bit position.  Otherwise, split it up.  */
++  if (host_integerp (offset, 0)
++      && 0 != (tem = size_binop (MULT_EXPR, convert (bitsizetype, offset),
++                               bitsize_unit_node))
++      && 0 != (tem = size_binop (PLUS_EXPR, tem, bit_offset))
++      && host_integerp (tem, 0))
++    *pbitpos = tree_low_cst (tem, 0), *poffset = 0;
++  else
++    *pbitpos = tree_low_cst (bit_offset, 0), *poffset = offset;
++
++  *pmode = mode;
++  return exp;
++}
++
++/* Return 1 if T is an expression that get_inner_reference handles.  */
++
++int
++handled_component_p (t)
++     tree t;
++{
++  switch (TREE_CODE (t))
++    {
++    case BIT_FIELD_REF:
++    case COMPONENT_REF:
++    case ARRAY_REF:
++    case ARRAY_RANGE_REF:
++    case NON_LVALUE_EXPR:
++    case VIEW_CONVERT_EXPR:
++      return 1;
++
++    case NOP_EXPR:
++    case CONVERT_EXPR:
++      return (TYPE_MODE (TREE_TYPE (t))
++            == TYPE_MODE (TREE_TYPE (TREE_OPERAND (t, 0))));
++
++    default:
++      return 0;
++    }
++}
++\f
++/* Given an rtx VALUE that may contain additions and multiplications, return
++   an equivalent value that just refers to a register, memory, or constant.
++   This is done by generating instructions to perform the arithmetic and
++   returning a pseudo-register containing the value.
++
++   The returned value may be a REG, SUBREG, MEM or constant.  */
++
++rtx
++force_operand (value, target)
++     rtx value, target;
++{
++  rtx op1, op2;
++  /* Use subtarget as the target for operand 0 of a binary operation.  */
++  rtx subtarget = get_subtarget (target);
++  enum rtx_code code = GET_CODE (value);
++
++  /* Check for a PIC address load.  */
++  if ((code == PLUS || code == MINUS)
++      && XEXP (value, 0) == pic_offset_table_rtx
++      && (GET_CODE (XEXP (value, 1)) == SYMBOL_REF
++        || GET_CODE (XEXP (value, 1)) == LABEL_REF
++        || GET_CODE (XEXP (value, 1)) == CONST))
++    {
++      if (!subtarget)
++      subtarget = gen_reg_rtx (GET_MODE (value));
++      emit_move_insn (subtarget, value);
++      return subtarget;
++    }
++
++  if (code == ZERO_EXTEND || code == SIGN_EXTEND)
++    {
++      if (!target)
++      target = gen_reg_rtx (GET_MODE (value));
++      convert_move (target, force_operand (XEXP (value, 0), NULL),
++                  code == ZERO_EXTEND);
++      return target;
++    }
++
++  if (GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c')
++    {
++      op2 = XEXP (value, 1);
++      if (!CONSTANT_P (op2) && !(GET_CODE (op2) == REG && op2 != subtarget))
++      subtarget = 0;
++      if (code == MINUS && GET_CODE (op2) == CONST_INT)
++      {
++        code = PLUS;
++        op2 = negate_rtx (GET_MODE (value), op2);
++      }
++
++      /* Check for an addition with OP2 a constant integer and our first
++         operand a PLUS of a virtual register and something else.  In that
++         case, we want to emit the sum of the virtual register and the
++         constant first and then add the other value.  This allows virtual
++         register instantiation to simply modify the constant rather than
++         creating another one around this addition.  */
++      if (code == PLUS && GET_CODE (op2) == CONST_INT
++        && GET_CODE (XEXP (value, 0)) == PLUS
++        && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG
++        && REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER
++        && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER)
++      {
++        rtx temp = expand_simple_binop (GET_MODE (value), code,
++                                        XEXP (XEXP (value, 0), 0), op2,
++                                        subtarget, 0, OPTAB_LIB_WIDEN);
++        return expand_simple_binop (GET_MODE (value), code, temp,
++                                    force_operand (XEXP (XEXP (value,
++                                                               0), 1), 0),
++                                    target, 0, OPTAB_LIB_WIDEN);
++      }
++
++      op1 = force_operand (XEXP (value, 0), subtarget);
++      op2 = force_operand (op2, NULL_RTX);
++      switch (code)
++      {
++      case MULT:
++        return expand_mult (GET_MODE (value), op1, op2, target, 1);
++      case DIV:
++        if (!INTEGRAL_MODE_P (GET_MODE (value)))
++          return expand_simple_binop (GET_MODE (value), code, op1, op2,
++                                      target, 1, OPTAB_LIB_WIDEN);
++        else
++          return expand_divmod (0,
++                                FLOAT_MODE_P (GET_MODE (value))
++                                ? RDIV_EXPR : TRUNC_DIV_EXPR,
++                                GET_MODE (value), op1, op2, target, 0);
++        break;
++      case MOD:
++        return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value), op1, op2,
++                              target, 0);
++        break;
++      case UDIV:
++        return expand_divmod (0, TRUNC_DIV_EXPR, GET_MODE (value), op1, op2,
++                              target, 1);
++        break;
++      case UMOD:
++        return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value), op1, op2,
++                              target, 1);
++        break;
++      case ASHIFTRT:
++        return expand_simple_binop (GET_MODE (value), code, op1, op2,
++                                    target, 0, OPTAB_LIB_WIDEN);
++        break;
++      default:
++        return expand_simple_binop (GET_MODE (value), code, op1, op2,
++                                    target, 1, OPTAB_LIB_WIDEN);
++      }
++    }
++  if (GET_RTX_CLASS (code) == '1')
++    {
++      op1 = force_operand (XEXP (value, 0), NULL_RTX);
++      return expand_simple_unop (GET_MODE (value), code, op1, target, 0);
++    }
++
++#ifdef INSN_SCHEDULING
++  /* On machines that have insn scheduling, we want all memory reference to be
++     explicit, so we need to deal with such paradoxical SUBREGs.  */
++  if (GET_CODE (value) == SUBREG && GET_CODE (SUBREG_REG (value)) == MEM
++      && (GET_MODE_SIZE (GET_MODE (value))
++        > GET_MODE_SIZE (GET_MODE (SUBREG_REG (value)))))
++    value
++      = simplify_gen_subreg (GET_MODE (value),
++                           force_reg (GET_MODE (SUBREG_REG (value)),
++                                      force_operand (SUBREG_REG (value),
++                                                     NULL_RTX)),
++                           GET_MODE (SUBREG_REG (value)),
++                           SUBREG_BYTE (value));
++#endif
++
++  return value;
++}
++\f
++/* Subroutine of expand_expr: return nonzero iff there is no way that
++   EXP can reference X, which is being modified.  TOP_P is nonzero if this
++   call is going to be used to determine whether we need a temporary
++   for EXP, as opposed to a recursive call to this function.
++
++   It is always safe for this routine to return zero since it merely
++   searches for optimization opportunities.  */
++
++int
++safe_from_p (x, exp, top_p)
++     rtx x;
++     tree exp;
++     int top_p;
++{
++  rtx exp_rtl = 0;
++  int i, nops;
++  static tree save_expr_list;
++
++  if (x == 0
++      /* If EXP has varying size, we MUST use a target since we currently
++       have no way of allocating temporaries of variable size
++       (except for arrays that have TYPE_ARRAY_MAX_SIZE set).
++       So we assume here that something at a higher level has prevented a
++       clash.  This is somewhat bogus, but the best we can do.  Only
++       do this when X is BLKmode and when we are at the top level.  */
++      || (top_p && TREE_TYPE (exp) != 0 && COMPLETE_TYPE_P (TREE_TYPE (exp))
++        && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST
++        && (TREE_CODE (TREE_TYPE (exp)) != ARRAY_TYPE
++            || TYPE_ARRAY_MAX_SIZE (TREE_TYPE (exp)) == NULL_TREE
++            || TREE_CODE (TYPE_ARRAY_MAX_SIZE (TREE_TYPE (exp)))
++            != INTEGER_CST)
++        && GET_MODE (x) == BLKmode)
++      /* If X is in the outgoing argument area, it is always safe.  */
++      || (GET_CODE (x) == MEM
++        && (XEXP (x, 0) == virtual_outgoing_args_rtx
++            || (GET_CODE (XEXP (x, 0)) == PLUS
++                && XEXP (XEXP (x, 0), 0) == virtual_outgoing_args_rtx))))
++    return 1;
++
++  /* If this is a subreg of a hard register, declare it unsafe, otherwise,
++     find the underlying pseudo.  */
++  if (GET_CODE (x) == SUBREG)
++    {
++      x = SUBREG_REG (x);
++      if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
++      return 0;
++    }
++
++  /* A SAVE_EXPR might appear many times in the expression passed to the
++     top-level safe_from_p call, and if it has a complex subexpression,
++     examining it multiple times could result in a combinatorial explosion.
++     E.g. on an Alpha running at least 200MHz, a Fortran test case compiled
++     with optimization took about 28 minutes to compile -- even though it was
++     only a few lines long.  So we mark each SAVE_EXPR we see with TREE_PRIVATE
++     and turn that off when we are done.  We keep a list of the SAVE_EXPRs
++     we have processed.  Note that the only test of top_p was above.  */
++
++  if (top_p)
++    {
++      int rtn;
++      tree t;
++
++      save_expr_list = 0;
++
++      rtn = safe_from_p (x, exp, 0);
++
++      for (t = save_expr_list; t != 0; t = TREE_CHAIN (t))
++      TREE_PRIVATE (TREE_PURPOSE (t)) = 0;
++
++      return rtn;
++    }
++
++  /* Now look at our tree code and possibly recurse.  */
++  switch (TREE_CODE_CLASS (TREE_CODE (exp)))
++    {
++    case 'd':
++      exp_rtl = DECL_RTL_IF_SET (exp);
++      break;
++
++    case 'c':
++      return 1;
++
++    case 'x':
++      if (TREE_CODE (exp) == TREE_LIST)
++      {
++        while (1)
++          {
++            if (TREE_VALUE (exp) && !safe_from_p (x, TREE_VALUE (exp), 0))
++              return 0;
++            exp = TREE_CHAIN (exp);
++            if (!exp)
++              return 1;
++            if (TREE_CODE (exp) != TREE_LIST)
++              return safe_from_p (x, exp, 0);
++          }
++      }
++      else if (TREE_CODE (exp) == ERROR_MARK)
++      return 1;       /* An already-visited SAVE_EXPR? */
++      else
++      return 0;
++
++    case '2':
++    case '<':
++      if (!safe_from_p (x, TREE_OPERAND (exp, 1), 0))
++      return 0;
++      /* FALLTHRU */
++
++    case '1':
++      return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
++
++    case 'e':
++    case 'r':
++      /* Now do code-specific tests.  EXP_RTL is set to any rtx we find in
++       the expression.  If it is set, we conflict iff we are that rtx or
++       both are in memory.  Otherwise, we check all operands of the
++       expression recursively.  */
++
++      switch (TREE_CODE (exp))
++      {
++      case ADDR_EXPR:
++        /* If the operand is static or we are static, we can't conflict.
++           Likewise if we don't conflict with the operand at all.  */
++        if (staticp (TREE_OPERAND (exp, 0))
++            || TREE_STATIC (exp)
++            || safe_from_p (x, TREE_OPERAND (exp, 0), 0))
++          return 1;
++
++        /* Otherwise, the only way this can conflict is if we are taking
++           the address of a DECL a that address if part of X, which is
++           very rare.  */
++        exp = TREE_OPERAND (exp, 0);
++        if (DECL_P (exp))
++          {
++            if (!DECL_RTL_SET_P (exp)
++                || GET_CODE (DECL_RTL (exp)) != MEM)
++              return 0;
++            else
++              exp_rtl = XEXP (DECL_RTL (exp), 0);
++          }
++        break;
++
++      case INDIRECT_REF:
++        if (GET_CODE (x) == MEM
++            && alias_sets_conflict_p (MEM_ALIAS_SET (x),
++                                      get_alias_set (exp)))
++          return 0;
++        break;
++
++      case CALL_EXPR:
++        /* Assume that the call will clobber all hard registers and
++           all of memory.  */
++        if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
++            || GET_CODE (x) == MEM)
++          return 0;
++        break;
++
++      case RTL_EXPR:
++        /* If a sequence exists, we would have to scan every instruction
++           in the sequence to see if it was safe.  This is probably not
++           worthwhile.  */
++        if (RTL_EXPR_SEQUENCE (exp))
++          return 0;
++
++        exp_rtl = RTL_EXPR_RTL (exp);
++        break;
++
++      case WITH_CLEANUP_EXPR:
++        exp_rtl = WITH_CLEANUP_EXPR_RTL (exp);
++        break;
++
++      case CLEANUP_POINT_EXPR:
++        return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
++
++      case SAVE_EXPR:
++        exp_rtl = SAVE_EXPR_RTL (exp);
++        if (exp_rtl)
++          break;
++
++        /* If we've already scanned this, don't do it again.  Otherwise,
++           show we've scanned it and record for clearing the flag if we're
++           going on.  */
++        if (TREE_PRIVATE (exp))
++          return 1;
++
++        TREE_PRIVATE (exp) = 1;
++        if (! safe_from_p (x, TREE_OPERAND (exp, 0), 0))
++          {
++            TREE_PRIVATE (exp) = 0;
++            return 0;
++          }
++
++        save_expr_list = tree_cons (exp, NULL_TREE, save_expr_list);
++        return 1;
++
++      case BIND_EXPR:
++        /* The only operand we look at is operand 1.  The rest aren't
++           part of the expression.  */
++        return safe_from_p (x, TREE_OPERAND (exp, 1), 0);
++
++      case METHOD_CALL_EXPR:
++        /* This takes an rtx argument, but shouldn't appear here.  */
++        abort ();
++
++      default:
++        break;
++      }
++
++      /* If we have an rtx, we do not need to scan our operands.  */
++      if (exp_rtl)
++      break;
++
++      nops = first_rtl_op (TREE_CODE (exp));
++      for (i = 0; i < nops; i++)
++      if (TREE_OPERAND (exp, i) != 0
++          && ! safe_from_p (x, TREE_OPERAND (exp, i), 0))
++        return 0;
++
++      /* If this is a language-specific tree code, it may require
++       special handling.  */
++      if ((unsigned int) TREE_CODE (exp)
++        >= (unsigned int) LAST_AND_UNUSED_TREE_CODE
++        && !(*lang_hooks.safe_from_p) (x, exp))
++      return 0;
++    }
++
++  /* If we have an rtl, find any enclosed object.  Then see if we conflict
++     with it.  */
++  if (exp_rtl)
++    {
++      if (GET_CODE (exp_rtl) == SUBREG)
++      {
++        exp_rtl = SUBREG_REG (exp_rtl);
++        if (GET_CODE (exp_rtl) == REG
++            && REGNO (exp_rtl) < FIRST_PSEUDO_REGISTER)
++          return 0;
++      }
++
++      /* If the rtl is X, then it is not safe.  Otherwise, it is unless both
++       are memory and they conflict.  */
++      return ! (rtx_equal_p (x, exp_rtl)
++              || (GET_CODE (x) == MEM && GET_CODE (exp_rtl) == MEM
++                  && true_dependence (exp_rtl, VOIDmode, x,
++                                      rtx_addr_varies_p)));
++    }
++
++  /* If we reach here, it is safe.  */
++  return 1;
++}
++
++/* Subroutine of expand_expr: return rtx if EXP is a
++   variable or parameter; else return 0.  */
++
++static rtx
++var_rtx (exp)
++     tree exp;
++{
++  STRIP_NOPS (exp);
++  switch (TREE_CODE (exp))
++    {
++    case PARM_DECL:
++    case VAR_DECL:
++      return DECL_RTL (exp);
++    default:
++      return 0;
++    }
++}
++
++#ifdef MAX_INTEGER_COMPUTATION_MODE
++
++void
++check_max_integer_computation_mode (exp)
++     tree exp;
++{
++  enum tree_code code;
++  enum machine_mode mode;
++
++  /* Strip any NOPs that don't change the mode.  */
++  STRIP_NOPS (exp);
++  code = TREE_CODE (exp);
++
++  /* We must allow conversions of constants to MAX_INTEGER_COMPUTATION_MODE.  */
++  if (code == NOP_EXPR
++      && TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST)
++    return;
++
++  /* First check the type of the overall operation.   We need only look at
++     unary, binary and relational operations.  */
++  if (TREE_CODE_CLASS (code) == '1'
++      || TREE_CODE_CLASS (code) == '2'
++      || TREE_CODE_CLASS (code) == '<')
++    {
++      mode = TYPE_MODE (TREE_TYPE (exp));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && mode > MAX_INTEGER_COMPUTATION_MODE)
++      internal_error ("unsupported wide integer operation");
++    }
++
++  /* Check operand of a unary op.  */
++  if (TREE_CODE_CLASS (code) == '1')
++    {
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && mode > MAX_INTEGER_COMPUTATION_MODE)
++      internal_error ("unsupported wide integer operation");
++    }
++
++  /* Check operands of a binary/comparison op.  */
++  if (TREE_CODE_CLASS (code) == '2' || TREE_CODE_CLASS (code) == '<')
++    {
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && mode > MAX_INTEGER_COMPUTATION_MODE)
++      internal_error ("unsupported wide integer operation");
++
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && mode > MAX_INTEGER_COMPUTATION_MODE)
++      internal_error ("unsupported wide integer operation");
++    }
++}
++#endif
++\f
++/* Return the highest power of two that EXP is known to be a multiple of.
++   This is used in updating alignment of MEMs in array references.  */
++
++static HOST_WIDE_INT
++highest_pow2_factor (exp)
++     tree exp;
++{
++  HOST_WIDE_INT c0, c1;
++
++  switch (TREE_CODE (exp))
++    {
++    case INTEGER_CST:
++      /* We can find the lowest bit that's a one.  If the low
++       HOST_BITS_PER_WIDE_INT bits are zero, return BIGGEST_ALIGNMENT.
++       We need to handle this case since we can find it in a COND_EXPR,
++       a MIN_EXPR, or a MAX_EXPR.  If the constant overlows, we have an
++       erroneous program, so return BIGGEST_ALIGNMENT to avoid any
++       later ICE.  */
++      if (TREE_CONSTANT_OVERFLOW (exp))
++      return BIGGEST_ALIGNMENT;
++      else
++      {
++        /* Note: tree_low_cst is intentionally not used here,
++           we don't care about the upper bits.  */
++        c0 = TREE_INT_CST_LOW (exp);
++        c0 &= -c0;
++        return c0 ? c0 : BIGGEST_ALIGNMENT;
++      }
++      break;
++
++    case PLUS_EXPR:  case MINUS_EXPR:  case MIN_EXPR:  case MAX_EXPR:
++      c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
++      c1 = highest_pow2_factor (TREE_OPERAND (exp, 1));
++      return MIN (c0, c1);
++
++    case MULT_EXPR:
++      c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
++      c1 = highest_pow2_factor (TREE_OPERAND (exp, 1));
++      return c0 * c1;
++
++    case ROUND_DIV_EXPR:  case TRUNC_DIV_EXPR:  case FLOOR_DIV_EXPR:
++    case CEIL_DIV_EXPR:
++      if (integer_pow2p (TREE_OPERAND (exp, 1))
++        && host_integerp (TREE_OPERAND (exp, 1), 1))
++      {
++        c0 = highest_pow2_factor (TREE_OPERAND (exp, 0));
++        c1 = tree_low_cst (TREE_OPERAND (exp, 1), 1);
++        return MAX (1, c0 / c1);
++      }
++      break;
++
++    case NON_LVALUE_EXPR:  case NOP_EXPR:  case CONVERT_EXPR:
++    case SAVE_EXPR: case WITH_RECORD_EXPR:
++      return highest_pow2_factor (TREE_OPERAND (exp, 0));
++
++    case COMPOUND_EXPR:
++      return highest_pow2_factor (TREE_OPERAND (exp, 1));
++
++    case COND_EXPR:
++      c0 = highest_pow2_factor (TREE_OPERAND (exp, 1));
++      c1 = highest_pow2_factor (TREE_OPERAND (exp, 2));
++      return MIN (c0, c1);
++
++    default:
++      break;
++    }
++
++  return 1;
++}
++
++/* Similar, except that it is known that the expression must be a multiple
++   of the alignment of TYPE.  */
++
++static HOST_WIDE_INT
++highest_pow2_factor_for_type (type, exp)
++     tree type;
++     tree exp;
++{
++  HOST_WIDE_INT type_align, factor;
++
++  factor = highest_pow2_factor (exp);
++  type_align = TYPE_ALIGN (type) / BITS_PER_UNIT;
++  return MAX (factor, type_align);
++}
++\f
++/* Return an object on the placeholder list that matches EXP, a
++   PLACEHOLDER_EXPR.  An object "matches" if it is of the type of the
++   PLACEHOLDER_EXPR or a pointer type to it.  For further information, see
++   tree.def.  If no such object is found, return 0.  If PLIST is nonzero, it
++   is a location which initially points to a starting location in the
++   placeholder list (zero means start of the list) and where a pointer into
++   the placeholder list at which the object is found is placed.  */
++
++tree
++find_placeholder (exp, plist)
++     tree exp;
++     tree *plist;
++{
++  tree type = TREE_TYPE (exp);
++  tree placeholder_expr;
++
++  for (placeholder_expr
++       = plist && *plist ? TREE_CHAIN (*plist) : placeholder_list;
++       placeholder_expr != 0;
++       placeholder_expr = TREE_CHAIN (placeholder_expr))
++    {
++      tree need_type = TYPE_MAIN_VARIANT (type);
++      tree elt;
++
++      /* Find the outermost reference that is of the type we want.  If none,
++       see if any object has a type that is a pointer to the type we
++       want.  */
++      for (elt = TREE_PURPOSE (placeholder_expr); elt != 0;
++         elt = ((TREE_CODE (elt) == COMPOUND_EXPR
++                 || TREE_CODE (elt) == COND_EXPR)
++                ? TREE_OPERAND (elt, 1)
++                : (TREE_CODE_CLASS (TREE_CODE (elt)) == 'r'
++                   || TREE_CODE_CLASS (TREE_CODE (elt)) == '1'
++                   || TREE_CODE_CLASS (TREE_CODE (elt)) == '2'
++                   || TREE_CODE_CLASS (TREE_CODE (elt)) == 'e')
++                ? TREE_OPERAND (elt, 0) : 0))
++      if (TYPE_MAIN_VARIANT (TREE_TYPE (elt)) == need_type)
++        {
++          if (plist)
++            *plist = placeholder_expr;
++          return elt;
++        }
++
++      for (elt = TREE_PURPOSE (placeholder_expr); elt != 0;
++         elt
++         = ((TREE_CODE (elt) == COMPOUND_EXPR
++             || TREE_CODE (elt) == COND_EXPR)
++            ? TREE_OPERAND (elt, 1)
++            : (TREE_CODE_CLASS (TREE_CODE (elt)) == 'r'
++               || TREE_CODE_CLASS (TREE_CODE (elt)) == '1'
++               || TREE_CODE_CLASS (TREE_CODE (elt)) == '2'
++               || TREE_CODE_CLASS (TREE_CODE (elt)) == 'e')
++            ? TREE_OPERAND (elt, 0) : 0))
++      if (POINTER_TYPE_P (TREE_TYPE (elt))
++          && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (elt)))
++              == need_type))
++        {
++          if (plist)
++            *plist = placeholder_expr;
++          return build1 (INDIRECT_REF, need_type, elt);
++        }
++    }
++
++  return 0;
++}
++\f
++/* expand_expr: generate code for computing expression EXP.
++   An rtx for the computed value is returned.  The value is never null.
++   In the case of a void EXP, const0_rtx is returned.
++
++   The value may be stored in TARGET if TARGET is nonzero.
++   TARGET is just a suggestion; callers must assume that
++   the rtx returned may not be the same as TARGET.
++
++   If TARGET is CONST0_RTX, it means that the value will be ignored.
++
++   If TMODE is not VOIDmode, it suggests generating the
++   result in mode TMODE.  But this is done only when convenient.
++   Otherwise, TMODE is ignored and the value generated in its natural mode.
++   TMODE is just a suggestion; callers must assume that
++   the rtx returned may not have mode TMODE.
++
++   Note that TARGET may have neither TMODE nor MODE.  In that case, it
++   probably will not be used.
++
++   If MODIFIER is EXPAND_SUM then when EXP is an addition
++   we can return an rtx of the form (MULT (REG ...) (CONST_INT ...))
++   or a nest of (PLUS ...) and (MINUS ...) where the terms are
++   products as above, or REG or MEM, or constant.
++   Ordinarily in such cases we would output mul or add instructions
++   and then return a pseudo reg containing the sum.
++
++   EXPAND_INITIALIZER is much like EXPAND_SUM except that
++   it also marks a label as absolutely required (it can't be dead).
++   It also makes a ZERO_EXTEND or SIGN_EXTEND instead of emitting extend insns.
++   This is used for outputting expressions used in initializers.
++
++   EXPAND_CONST_ADDRESS says that it is okay to return a MEM
++   with a constant address even if that address is not normally legitimate.
++   EXPAND_INITIALIZER and EXPAND_SUM also have this effect.
++
++   EXPAND_STACK_PARM is used when expanding to a TARGET on the stack for
++   a call parameter.  Such targets require special care as we haven't yet
++   marked TARGET so that it's safe from being trashed by libcalls.  We
++   don't want to use TARGET for anything but the final result;
++   Intermediate values must go elsewhere.   Additionally, calls to
++   emit_block_move will be flagged with BLOCK_OP_CALL_PARM.  */
++
++rtx
++expand_expr (exp, target, tmode, modifier)
++     tree exp;
++     rtx target;
++     enum machine_mode tmode;
++     enum expand_modifier modifier;
++{
++  rtx op0, op1, temp;
++  tree type = TREE_TYPE (exp);
++  int unsignedp = TREE_UNSIGNED (type);
++  enum machine_mode mode;
++  enum tree_code code = TREE_CODE (exp);
++  optab this_optab;
++  rtx subtarget, original_target;
++  int ignore;
++  tree context;
++
++  /* Handle ERROR_MARK before anybody tries to access its type.  */
++  if (TREE_CODE (exp) == ERROR_MARK || TREE_CODE (type) == ERROR_MARK)
++    {
++      op0 = CONST0_RTX (tmode);
++      if (op0 != 0)
++      return op0;
++      return const0_rtx;
++    }
++
++  mode = TYPE_MODE (type);
++  /* Use subtarget as the target for operand 0 of a binary operation.  */
++  subtarget = get_subtarget (target);
++  original_target = target;
++  ignore = (target == const0_rtx
++          || ((code == NON_LVALUE_EXPR || code == NOP_EXPR
++               || code == CONVERT_EXPR || code == REFERENCE_EXPR
++               || code == COND_EXPR || code == VIEW_CONVERT_EXPR)
++              && TREE_CODE (type) == VOID_TYPE));
++
++  /* If we are going to ignore this result, we need only do something
++     if there is a side-effect somewhere in the expression.  If there
++     is, short-circuit the most common cases here.  Note that we must
++     not call expand_expr with anything but const0_rtx in case this
++     is an initial expansion of a size that contains a PLACEHOLDER_EXPR.  */
++
++  if (ignore)
++    {
++      if (! TREE_SIDE_EFFECTS (exp))
++      return const0_rtx;
++
++      /* Ensure we reference a volatile object even if value is ignored, but
++       don't do this if all we are doing is taking its address.  */
++      if (TREE_THIS_VOLATILE (exp)
++        && TREE_CODE (exp) != FUNCTION_DECL
++        && mode != VOIDmode && mode != BLKmode
++        && modifier != EXPAND_CONST_ADDRESS)
++      {
++        temp = expand_expr (exp, NULL_RTX, VOIDmode, modifier);
++        if (GET_CODE (temp) == MEM)
++          temp = copy_to_reg (temp);
++        return const0_rtx;
++      }
++
++      if (TREE_CODE_CLASS (code) == '1' || code == COMPONENT_REF
++        || code == INDIRECT_REF || code == BUFFER_REF)
++      return expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
++                          modifier);
++
++      else if (TREE_CODE_CLASS (code) == '2' || TREE_CODE_CLASS (code) == '<'
++             || code == ARRAY_REF || code == ARRAY_RANGE_REF)
++      {
++        expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
++        expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
++        return const0_rtx;
++      }
++      else if ((code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
++             && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 1)))
++      /* If the second operand has no side effects, just evaluate
++         the first.  */
++      return expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
++                          modifier);
++      else if (code == BIT_FIELD_REF)
++      {
++        expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
++        expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
++        expand_expr (TREE_OPERAND (exp, 2), const0_rtx, VOIDmode, modifier);
++        return const0_rtx;
++      }
++
++      target = 0;
++    }
++
++#ifdef MAX_INTEGER_COMPUTATION_MODE
++  /* Only check stuff here if the mode we want is different from the mode
++     of the expression; if it's the same, check_max_integer_computiation_mode
++     will handle it.  Do we really need to check this stuff at all?  */
++
++  if (target
++      && GET_MODE (target) != mode
++      && TREE_CODE (exp) != INTEGER_CST
++      && TREE_CODE (exp) != PARM_DECL
++      && TREE_CODE (exp) != ARRAY_REF
++      && TREE_CODE (exp) != ARRAY_RANGE_REF
++      && TREE_CODE (exp) != COMPONENT_REF
++      && TREE_CODE (exp) != BIT_FIELD_REF
++      && TREE_CODE (exp) != INDIRECT_REF
++      && TREE_CODE (exp) != CALL_EXPR
++      && TREE_CODE (exp) != VAR_DECL
++      && TREE_CODE (exp) != RTL_EXPR)
++    {
++      enum machine_mode mode = GET_MODE (target);
++
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && mode > MAX_INTEGER_COMPUTATION_MODE)
++      internal_error ("unsupported wide integer operation");
++    }
++
++  if (tmode != mode
++      && TREE_CODE (exp) != INTEGER_CST
++      && TREE_CODE (exp) != PARM_DECL
++      && TREE_CODE (exp) != ARRAY_REF
++      && TREE_CODE (exp) != ARRAY_RANGE_REF
++      && TREE_CODE (exp) != COMPONENT_REF
++      && TREE_CODE (exp) != BIT_FIELD_REF
++      && TREE_CODE (exp) != INDIRECT_REF
++      && TREE_CODE (exp) != VAR_DECL
++      && TREE_CODE (exp) != CALL_EXPR
++      && TREE_CODE (exp) != RTL_EXPR
++      && GET_MODE_CLASS (tmode) == MODE_INT
++      && tmode > MAX_INTEGER_COMPUTATION_MODE)
++    internal_error ("unsupported wide integer operation");
++
++  check_max_integer_computation_mode (exp);
++#endif
++
++  /* If will do cse, generate all results into pseudo registers
++     since 1) that allows cse to find more things
++     and 2) otherwise cse could produce an insn the machine
++     cannot support.  And exception is a CONSTRUCTOR into a multi-word
++     MEM: that's much more likely to be most efficient into the MEM.  */
++
++  if (! cse_not_expected && mode != BLKmode && target
++      && (GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER)
++      && ! (code == CONSTRUCTOR && GET_MODE_SIZE (mode) > UNITS_PER_WORD))
++    target = 0;
++
++  switch (code)
++    {
++    case LABEL_DECL:
++      {
++      tree function = decl_function_context (exp);
++      /* Handle using a label in a containing function.  */
++      if (function != current_function_decl
++          && function != inline_function_decl && function != 0)
++        {
++          struct function *p = find_function_data (function);
++          p->expr->x_forced_labels
++            = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (exp),
++                                 p->expr->x_forced_labels);
++        }
++      else
++        {
++          if (modifier == EXPAND_INITIALIZER)
++            forced_labels = gen_rtx_EXPR_LIST (VOIDmode,
++                                               label_rtx (exp),
++                                               forced_labels);
++        }
++
++      temp = gen_rtx_MEM (FUNCTION_MODE,
++                          gen_rtx_LABEL_REF (Pmode, label_rtx (exp)));
++      if (function != current_function_decl
++          && function != inline_function_decl && function != 0)
++        LABEL_REF_NONLOCAL_P (XEXP (temp, 0)) = 1;
++      return temp;
++      }
++
++    case PARM_DECL:
++      if (!DECL_RTL_SET_P (exp))
++      {
++        error_with_decl (exp, "prior parameter's size depends on `%s'");
++        return CONST0_RTX (mode);
++      }
++
++      /* ... fall through ...  */
++
++    case VAR_DECL:
++      /* If a static var's type was incomplete when the decl was written,
++       but the type is complete now, lay out the decl now.  */
++      if (DECL_SIZE (exp) == 0
++        && COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (TREE_TYPE (exp))
++        && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
++      layout_decl (exp, 0);
++
++      /* ... fall through ...  */
++
++    case FUNCTION_DECL:
++    case RESULT_DECL:
++      if (DECL_RTL (exp) == 0)
++      abort ();
++
++      /* Ensure variable marked as used even if it doesn't go through
++       a parser.  If it hasn't be used yet, write out an external
++       definition.  */
++      if (! TREE_USED (exp))
++      {
++        assemble_external (exp);
++        TREE_USED (exp) = 1;
++      }
++
++      /* Show we haven't gotten RTL for this yet.  */
++      temp = 0;
++
++      /* Handle variables inherited from containing functions.  */
++      context = decl_function_context (exp);
++
++      /* We treat inline_function_decl as an alias for the current function
++       because that is the inline function whose vars, types, etc.
++       are being merged into the current function.
++       See expand_inline_function.  */
++
++      if (context != 0 && context != current_function_decl
++        && context != inline_function_decl
++        /* If var is static, we don't need a static chain to access it.  */
++        && ! (GET_CODE (DECL_RTL (exp)) == MEM
++              && CONSTANT_P (XEXP (DECL_RTL (exp), 0))))
++      {
++        rtx addr;
++
++        /* Mark as non-local and addressable.  */
++        DECL_NONLOCAL (exp) = 1;
++        if (DECL_NO_STATIC_CHAIN (current_function_decl))
++          abort ();
++        (*lang_hooks.mark_addressable) (exp);
++        if (GET_CODE (DECL_RTL (exp)) != MEM)
++          abort ();
++        addr = XEXP (DECL_RTL (exp), 0);
++        if (GET_CODE (addr) == MEM)
++          addr
++            = replace_equiv_address (addr,
++                                     fix_lexical_addr (XEXP (addr, 0), exp));
++        else
++          addr = fix_lexical_addr (addr, exp);
++
++        temp = replace_equiv_address (DECL_RTL (exp), addr);
++      }
++
++      /* This is the case of an array whose size is to be determined
++       from its initializer, while the initializer is still being parsed.
++       See expand_decl.  */
++
++      else if (GET_CODE (DECL_RTL (exp)) == MEM
++             && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG)
++      temp = validize_mem (DECL_RTL (exp));
++
++      /* If DECL_RTL is memory, we are in the normal case and either
++       the address is not valid or it is not a register and -fforce-addr
++       is specified, get the address into a register.  */
++
++      else if (GET_CODE (DECL_RTL (exp)) == MEM
++             && modifier != EXPAND_CONST_ADDRESS
++             && modifier != EXPAND_SUM
++             && modifier != EXPAND_INITIALIZER
++             && (! memory_address_p (DECL_MODE (exp),
++                                     XEXP (DECL_RTL (exp), 0))
++                 || (flag_force_addr
++                     && GET_CODE (XEXP (DECL_RTL (exp), 0)) != REG)))
++      temp = replace_equiv_address (DECL_RTL (exp),
++                                    copy_rtx (XEXP (DECL_RTL (exp), 0)));
++
++      /* If we got something, return it.  But first, set the alignment
++       if the address is a register.  */
++      if (temp != 0)
++      {
++        if (GET_CODE (temp) == MEM && GET_CODE (XEXP (temp, 0)) == REG)
++          mark_reg_pointer (XEXP (temp, 0), DECL_ALIGN (exp));
++
++        return temp;
++      }
++
++      /* If the mode of DECL_RTL does not match that of the decl, it
++       must be a promoted value.  We return a SUBREG of the wanted mode,
++       but mark it so that we know that it was already extended.  */
++
++      if (GET_CODE (DECL_RTL (exp)) == REG
++        && GET_MODE (DECL_RTL (exp)) != DECL_MODE (exp))
++      {
++        /* Get the signedness used for this variable.  Ensure we get the
++           same mode we got when the variable was declared.  */
++        if (GET_MODE (DECL_RTL (exp))
++            != promote_mode (type, DECL_MODE (exp), &unsignedp,
++                             (TREE_CODE (exp) == RESULT_DECL ? 1 : 0)))
++          abort ();
++
++        temp = gen_lowpart_SUBREG (mode, DECL_RTL (exp));
++        SUBREG_PROMOTED_VAR_P (temp) = 1;
++        SUBREG_PROMOTED_UNSIGNED_SET (temp, unsignedp);
++        return temp;
++      }
++
++      return DECL_RTL (exp);
++
++    case INTEGER_CST:
++      temp = immed_double_const (TREE_INT_CST_LOW (exp),
++                               TREE_INT_CST_HIGH (exp), mode);
++
++      /* ??? If overflow is set, fold will have done an incomplete job,
++       which can result in (plus xx (const_int 0)), which can get
++       simplified by validate_replace_rtx during virtual register
++       instantiation, which can result in unrecognizable insns.
++       Avoid this by forcing all overflows into registers.  */
++      if (TREE_CONSTANT_OVERFLOW (exp)
++        && modifier != EXPAND_INITIALIZER)
++      temp = force_reg (mode, temp);
++
++      return temp;
++
++    case VECTOR_CST:
++      return const_vector_from_tree (exp);
++
++    case CONST_DECL:
++      return expand_expr (DECL_INITIAL (exp), target, VOIDmode, modifier);
++
++    case REAL_CST:
++      /* If optimized, generate immediate CONST_DOUBLE
++       which will be turned into memory by reload if necessary.
++
++       We used to force a register so that loop.c could see it.  But
++       this does not allow gen_* patterns to perform optimizations with
++       the constants.  It also produces two insns in cases like "x = 1.0;".
++       On most machines, floating-point constants are not permitted in
++       many insns, so we'd end up copying it to a register in any case.
++
++       Now, we do the copying in expand_binop, if appropriate.  */
++      return CONST_DOUBLE_FROM_REAL_VALUE (TREE_REAL_CST (exp),
++                                         TYPE_MODE (TREE_TYPE (exp)));
++
++    case COMPLEX_CST:
++    case STRING_CST:
++      if (! TREE_CST_RTL (exp))
++      output_constant_def (exp, 1);
++
++      /* TREE_CST_RTL probably contains a constant address.
++       On RISC machines where a constant address isn't valid,
++       make some insns to get that address into a register.  */
++      if (GET_CODE (TREE_CST_RTL (exp)) == MEM
++        && modifier != EXPAND_CONST_ADDRESS
++        && modifier != EXPAND_INITIALIZER
++        && modifier != EXPAND_SUM
++        && (! memory_address_p (mode, XEXP (TREE_CST_RTL (exp), 0))
++            || (flag_force_addr
++                && GET_CODE (XEXP (TREE_CST_RTL (exp), 0)) != REG)))
++      return replace_equiv_address (TREE_CST_RTL (exp),
++                                    copy_rtx (XEXP (TREE_CST_RTL (exp), 0)));
++      return TREE_CST_RTL (exp);
++
++    case EXPR_WITH_FILE_LOCATION:
++      {
++      rtx to_return;
++      const char *saved_input_filename = input_filename;
++      int saved_lineno = lineno;
++      input_filename = EXPR_WFL_FILENAME (exp);
++      lineno = EXPR_WFL_LINENO (exp);
++      if (EXPR_WFL_EMIT_LINE_NOTE (exp))
++        emit_line_note (input_filename, lineno);
++      /* Possibly avoid switching back and forth here.  */
++      to_return = expand_expr (EXPR_WFL_NODE (exp), target, tmode, modifier);
++      input_filename = saved_input_filename;
++      lineno = saved_lineno;
++      return to_return;
++      }
++
++    case SAVE_EXPR:
++      context = decl_function_context (exp);
++
++      /* If this SAVE_EXPR was at global context, assume we are an
++       initialization function and move it into our context.  */
++      if (context == 0)
++      SAVE_EXPR_CONTEXT (exp) = current_function_decl;
++
++      /* We treat inline_function_decl as an alias for the current function
++       because that is the inline function whose vars, types, etc.
++       are being merged into the current function.
++       See expand_inline_function.  */
++      if (context == current_function_decl || context == inline_function_decl)
++      context = 0;
++
++      /* If this is non-local, handle it.  */
++      if (context)
++      {
++        /* The following call just exists to abort if the context is
++           not of a containing function.  */
++        find_function_data (context);
++
++        temp = SAVE_EXPR_RTL (exp);
++        if (temp && GET_CODE (temp) == REG)
++          {
++            put_var_into_stack (exp, /*rescan=*/true);
++            temp = SAVE_EXPR_RTL (exp);
++          }
++        if (temp == 0 || GET_CODE (temp) != MEM)
++          abort ();
++        return
++          replace_equiv_address (temp,
++                                 fix_lexical_addr (XEXP (temp, 0), exp));
++      }
++      if (SAVE_EXPR_RTL (exp) == 0)
++      {
++        if (mode == VOIDmode)
++          temp = const0_rtx;
++        else
++          temp = assign_temp (build_qualified_type (type,
++                                                    (TYPE_QUALS (type)
++                                                     | TYPE_QUAL_CONST)),
++                              3, 0, 0);
++
++        SAVE_EXPR_RTL (exp) = temp;
++        if (!optimize && GET_CODE (temp) == REG)
++          save_expr_regs = gen_rtx_EXPR_LIST (VOIDmode, temp,
++                                              save_expr_regs);
++
++        /* If the mode of TEMP does not match that of the expression, it
++           must be a promoted value.  We pass store_expr a SUBREG of the
++           wanted mode but mark it so that we know that it was already
++           extended.  */
++
++        if (GET_CODE (temp) == REG && GET_MODE (temp) != mode)
++          {
++            temp = gen_lowpart_SUBREG (mode, SAVE_EXPR_RTL (exp));
++            promote_mode (type, mode, &unsignedp, 0);
++            SUBREG_PROMOTED_VAR_P (temp) = 1;
++            SUBREG_PROMOTED_UNSIGNED_SET (temp, unsignedp);
++          }
++
++        if (temp == const0_rtx)
++          expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
++        else
++          store_expr (TREE_OPERAND (exp, 0), temp,
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++
++        TREE_USED (exp) = 1;
++      }
++
++      /* If the mode of SAVE_EXPR_RTL does not match that of the expression, it
++       must be a promoted value.  We return a SUBREG of the wanted mode,
++       but mark it so that we know that it was already extended.  */
++
++      if (GET_CODE (SAVE_EXPR_RTL (exp)) == REG
++        && GET_MODE (SAVE_EXPR_RTL (exp)) != mode)
++      {
++        /* Compute the signedness and make the proper SUBREG.  */
++        promote_mode (type, mode, &unsignedp, 0);
++        temp = gen_lowpart_SUBREG (mode, SAVE_EXPR_RTL (exp));
++        SUBREG_PROMOTED_VAR_P (temp) = 1;
++        SUBREG_PROMOTED_UNSIGNED_SET (temp, unsignedp);
++        return temp;
++      }
++
++      return SAVE_EXPR_RTL (exp);
++
++    case UNSAVE_EXPR:
++      {
++      rtx temp;
++      temp = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
++      TREE_OPERAND (exp, 0)
++        = (*lang_hooks.unsave_expr_now) (TREE_OPERAND (exp, 0));
++      return temp;
++      }
++
++    case PLACEHOLDER_EXPR:
++      {
++      tree old_list = placeholder_list;
++      tree placeholder_expr = 0;
++
++      exp = find_placeholder (exp, &placeholder_expr);
++      if (exp == 0)
++        abort ();
++
++      placeholder_list = TREE_CHAIN (placeholder_expr);
++      temp = expand_expr (exp, original_target, tmode, modifier);
++      placeholder_list = old_list;
++      return temp;
++      }
++
++    case WITH_RECORD_EXPR:
++      /* Put the object on the placeholder list, expand our first operand,
++       and pop the list.  */
++      placeholder_list = tree_cons (TREE_OPERAND (exp, 1), NULL_TREE,
++                                  placeholder_list);
++      target = expand_expr (TREE_OPERAND (exp, 0), original_target, tmode,
++                          modifier);
++      placeholder_list = TREE_CHAIN (placeholder_list);
++      return target;
++
++    case GOTO_EXPR:
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == LABEL_DECL)
++      expand_goto (TREE_OPERAND (exp, 0));
++      else
++      expand_computed_goto (TREE_OPERAND (exp, 0));
++      return const0_rtx;
++
++    case EXIT_EXPR:
++      expand_exit_loop_if_false (NULL,
++                               invert_truthvalue (TREE_OPERAND (exp, 0)));
++      return const0_rtx;
++
++    case LABELED_BLOCK_EXPR:
++      if (LABELED_BLOCK_BODY (exp))
++      expand_expr_stmt_value (LABELED_BLOCK_BODY (exp), 0, 1);
++      /* Should perhaps use expand_label, but this is simpler and safer.  */
++      do_pending_stack_adjust ();
++      emit_label (label_rtx (LABELED_BLOCK_LABEL (exp)));
++      return const0_rtx;
++
++    case EXIT_BLOCK_EXPR:
++      if (EXIT_BLOCK_RETURN (exp))
++      sorry ("returned value in block_exit_expr");
++      expand_goto (LABELED_BLOCK_LABEL (EXIT_BLOCK_LABELED_BLOCK (exp)));
++      return const0_rtx;
++
++    case LOOP_EXPR:
++      push_temp_slots ();
++      expand_start_loop (1);
++      expand_expr_stmt_value (TREE_OPERAND (exp, 0), 0, 1);
++      expand_end_loop ();
++      pop_temp_slots ();
++
++      return const0_rtx;
++
++    case BIND_EXPR:
++      {
++      tree vars = TREE_OPERAND (exp, 0);
++      int vars_need_expansion = 0;
++
++      /* Need to open a binding contour here because
++         if there are any cleanups they must be contained here.  */
++      expand_start_bindings (2);
++
++      /* Mark the corresponding BLOCK for output in its proper place.  */
++      if (TREE_OPERAND (exp, 2) != 0
++          && ! TREE_USED (TREE_OPERAND (exp, 2)))
++        (*lang_hooks.decls.insert_block) (TREE_OPERAND (exp, 2));
++
++      /* If VARS have not yet been expanded, expand them now.  */
++      while (vars)
++        {
++          if (!DECL_RTL_SET_P (vars))
++            {
++              vars_need_expansion = 1;
++              expand_decl (vars);
++            }
++          expand_decl_init (vars);
++          vars = TREE_CHAIN (vars);
++        }
++
++      temp = expand_expr (TREE_OPERAND (exp, 1), target, tmode, modifier);
++
++      expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0);
++
++      return temp;
++      }
++
++    case RTL_EXPR:
++      if (RTL_EXPR_SEQUENCE (exp))
++      {
++        if (RTL_EXPR_SEQUENCE (exp) == const0_rtx)
++          abort ();
++        emit_insn (RTL_EXPR_SEQUENCE (exp));
++        RTL_EXPR_SEQUENCE (exp) = const0_rtx;
++      }
++      preserve_rtl_expr_result (RTL_EXPR_RTL (exp));
++      free_temps_for_rtl_expr (exp);
++      return RTL_EXPR_RTL (exp);
++
++    case CONSTRUCTOR:
++      /* If we don't need the result, just ensure we evaluate any
++       subexpressions.  */
++      if (ignore)
++      {
++        tree elt;
++
++        for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
++          expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0);
++
++        return const0_rtx;
++      }
++
++      /* All elts simple constants => refer to a constant in memory.  But
++       if this is a non-BLKmode mode, let it store a field at a time
++       since that should make a CONST_INT or CONST_DOUBLE when we
++       fold.  Likewise, if we have a target we can use, it is best to
++       store directly into the target unless the type is large enough
++       that memcpy will be used.  If we are making an initializer and
++       all operands are constant, put it in memory as well.
++
++      FIXME: Avoid trying to fill vector constructors piece-meal.
++      Output them with output_constant_def below unless we're sure
++      they're zeros.  This should go away when vector initializers
++      are treated like VECTOR_CST instead of arrays.
++      */
++      else if ((TREE_STATIC (exp)
++              && ((mode == BLKmode
++                   && ! (target != 0 && safe_from_p (target, exp, 1)))
++                  || TREE_ADDRESSABLE (exp)
++                  || (host_integerp (TYPE_SIZE_UNIT (type), 1)
++                      && (! MOVE_BY_PIECES_P
++                          (tree_low_cst (TYPE_SIZE_UNIT (type), 1),
++                           TYPE_ALIGN (type)))
++                      && ((TREE_CODE (type) == VECTOR_TYPE
++                           && !is_zeros_p (exp))
++                          || ! mostly_zeros_p (exp)))))
++             || (modifier == EXPAND_INITIALIZER && TREE_CONSTANT (exp)))
++      {
++        rtx constructor = output_constant_def (exp, 1);
++
++        if (modifier != EXPAND_CONST_ADDRESS
++            && modifier != EXPAND_INITIALIZER
++            && modifier != EXPAND_SUM)
++          constructor = validize_mem (constructor);
++
++        return constructor;
++      }
++      else
++      {
++        /* Handle calls that pass values in multiple non-contiguous
++           locations.  The Irix 6 ABI has examples of this.  */
++        if (target == 0 || ! safe_from_p (target, exp, 1)
++            || GET_CODE (target) == PARALLEL
++            || modifier == EXPAND_STACK_PARM)
++          target
++            = assign_temp (build_qualified_type (type,
++                                                 (TYPE_QUALS (type)
++                                                  | (TREE_READONLY (exp)
++                                                     * TYPE_QUAL_CONST))),
++                           0, TREE_ADDRESSABLE (exp), 1);
++
++        store_constructor (exp, target, 0, int_expr_size (exp));
++        return target;
++      }
++
++    case INDIRECT_REF:
++      {
++      tree exp1 = TREE_OPERAND (exp, 0);
++      tree index;
++      tree string = string_constant (exp1, &index);
++
++      /* Try to optimize reads from const strings.  */
++      if (string
++          && TREE_CODE (string) == STRING_CST
++          && TREE_CODE (index) == INTEGER_CST
++          && compare_tree_int (index, TREE_STRING_LENGTH (string)) < 0
++          && GET_MODE_CLASS (mode) == MODE_INT
++          && GET_MODE_SIZE (mode) == 1
++          && modifier != EXPAND_WRITE)
++        return gen_int_mode (TREE_STRING_POINTER (string)
++                             [TREE_INT_CST_LOW (index)], mode);
++
++      op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM);
++      op0 = memory_address (mode, op0);
++      temp = gen_rtx_MEM (mode, op0);
++      set_mem_attributes (temp, exp, 0);
++
++      /* If we are writing to this object and its type is a record with
++         readonly fields, we must mark it as readonly so it will
++         conflict with readonly references to those fields.  */
++      if (modifier == EXPAND_WRITE && readonly_fields_p (type))
++        RTX_UNCHANGING_P (temp) = 1;
++
++      return temp;
++      }
++
++    case ARRAY_REF:
++      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) != ARRAY_TYPE)
++      abort ();
++
++      {
++      tree array = TREE_OPERAND (exp, 0);
++      tree domain = TYPE_DOMAIN (TREE_TYPE (array));
++      tree low_bound = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node;
++      tree index = convert (sizetype, TREE_OPERAND (exp, 1));
++      HOST_WIDE_INT i;
++
++      /* Optimize the special-case of a zero lower bound.
++
++         We convert the low_bound to sizetype to avoid some problems
++         with constant folding.  (E.g. suppose the lower bound is 1,
++         and its mode is QI.  Without the conversion,  (ARRAY
++         +(INDEX-(unsigned char)1)) becomes ((ARRAY+(-(unsigned char)1))
++         +INDEX), which becomes (ARRAY+255+INDEX).  Oops!)  */
++
++      if (! integer_zerop (low_bound))
++        index = size_diffop (index, convert (sizetype, low_bound));
++
++      /* Fold an expression like: "foo"[2].
++         This is not done in fold so it won't happen inside &.
++         Don't fold if this is for wide characters since it's too
++         difficult to do correctly and this is a very rare case.  */
++
++      if (modifier != EXPAND_CONST_ADDRESS
++          && modifier != EXPAND_INITIALIZER
++          && modifier != EXPAND_MEMORY
++          && TREE_CODE (array) == STRING_CST
++          && TREE_CODE (index) == INTEGER_CST
++          && compare_tree_int (index, TREE_STRING_LENGTH (array)) < 0
++          && GET_MODE_CLASS (mode) == MODE_INT
++          && GET_MODE_SIZE (mode) == 1)
++        return gen_int_mode (TREE_STRING_POINTER (array)
++                             [TREE_INT_CST_LOW (index)], mode);
++
++      /* If this is a constant index into a constant array,
++         just get the value from the array.  Handle both the cases when
++         we have an explicit constructor and when our operand is a variable
++         that was declared const.  */
++
++      if (modifier != EXPAND_CONST_ADDRESS
++          && modifier != EXPAND_INITIALIZER
++          && modifier != EXPAND_MEMORY
++          && TREE_CODE (array) == CONSTRUCTOR
++          && ! TREE_SIDE_EFFECTS (array)
++          && TREE_CODE (index) == INTEGER_CST
++          && 0 > compare_tree_int (index,
++                                   list_length (CONSTRUCTOR_ELTS
++                                                (TREE_OPERAND (exp, 0)))))
++        {
++          tree elem;
++
++          for (elem = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)),
++               i = TREE_INT_CST_LOW (index);
++               elem != 0 && i != 0; i--, elem = TREE_CHAIN (elem))
++            ;
++
++          if (elem)
++            return expand_expr (fold (TREE_VALUE (elem)), target, tmode,
++                                modifier);
++        }
++
++      else if (optimize >= 1
++               && modifier != EXPAND_CONST_ADDRESS
++               && modifier != EXPAND_INITIALIZER
++               && modifier != EXPAND_MEMORY
++               && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array)
++               && TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array)
++               && TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK)
++        {
++          if (TREE_CODE (index) == INTEGER_CST)
++            {
++              tree init = DECL_INITIAL (array);
++
++              if (TREE_CODE (init) == CONSTRUCTOR)
++                {
++                  tree elem;
++
++                  for (elem = CONSTRUCTOR_ELTS (init);
++                       (elem
++                        && !tree_int_cst_equal (TREE_PURPOSE (elem), index));
++                       elem = TREE_CHAIN (elem))
++                    ;
++
++                  if (elem && !TREE_SIDE_EFFECTS (TREE_VALUE (elem)))
++                    return expand_expr (fold (TREE_VALUE (elem)), target,
++                                        tmode, modifier);
++                }
++              else if (TREE_CODE (init) == STRING_CST
++                       && 0 > compare_tree_int (index,
++                                                TREE_STRING_LENGTH (init)))
++                {
++                  tree type = TREE_TYPE (TREE_TYPE (init));
++                  enum machine_mode mode = TYPE_MODE (type);
++
++                  if (GET_MODE_CLASS (mode) == MODE_INT
++                      && GET_MODE_SIZE (mode) == 1)
++                    return gen_int_mode (TREE_STRING_POINTER (init)
++                                         [TREE_INT_CST_LOW (index)], mode);
++                }
++            }
++        }
++      }
++      /* Fall through.  */
++
++    case COMPONENT_REF:
++    case BIT_FIELD_REF:
++    case ARRAY_RANGE_REF:
++      /* If the operand is a CONSTRUCTOR, we can just extract the
++       appropriate field if it is present.  Don't do this if we have
++       already written the data since we want to refer to that copy
++       and varasm.c assumes that's what we'll do.  */
++      if (code == COMPONENT_REF
++        && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR
++        && TREE_CST_RTL (TREE_OPERAND (exp, 0)) == 0)
++      {
++        tree elt;
++
++        for (elt = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); elt;
++             elt = TREE_CHAIN (elt))
++          if (TREE_PURPOSE (elt) == TREE_OPERAND (exp, 1)
++              /* We can normally use the value of the field in the
++                 CONSTRUCTOR.  However, if this is a bitfield in
++                 an integral mode that we can fit in a HOST_WIDE_INT,
++                 we must mask only the number of bits in the bitfield,
++                 since this is done implicitly by the constructor.  If
++                 the bitfield does not meet either of those conditions,
++                 we can't do this optimization.  */
++              && (! DECL_BIT_FIELD (TREE_PURPOSE (elt))
++                  || ((GET_MODE_CLASS (DECL_MODE (TREE_PURPOSE (elt)))
++                       == MODE_INT)
++                      && (GET_MODE_BITSIZE (DECL_MODE (TREE_PURPOSE (elt)))
++                          <= HOST_BITS_PER_WIDE_INT))))
++            {
++              if (DECL_BIT_FIELD (TREE_PURPOSE (elt))
++                  && modifier == EXPAND_STACK_PARM)
++                target = 0;
++              op0 = expand_expr (TREE_VALUE (elt), target, tmode, modifier);
++              if (DECL_BIT_FIELD (TREE_PURPOSE (elt)))
++                {
++                  HOST_WIDE_INT bitsize
++                    = TREE_INT_CST_LOW (DECL_SIZE (TREE_PURPOSE (elt)));
++                  enum machine_mode imode
++                    = TYPE_MODE (TREE_TYPE (TREE_PURPOSE (elt)));
++
++                  if (TREE_UNSIGNED (TREE_TYPE (TREE_PURPOSE (elt))))
++                    {
++                      op1 = GEN_INT (((HOST_WIDE_INT) 1 << bitsize) - 1);
++                      op0 = expand_and (imode, op0, op1, target);
++                    }
++                  else
++                    {
++                      tree count
++                        = build_int_2 (GET_MODE_BITSIZE (imode) - bitsize,
++                                       0);
++
++                      op0 = expand_shift (LSHIFT_EXPR, imode, op0, count,
++                                          target, 0);
++                      op0 = expand_shift (RSHIFT_EXPR, imode, op0, count,
++                                          target, 0);
++                    }
++                }
++
++              return op0;
++            }
++      }
++
++      {
++      enum machine_mode mode1;
++      HOST_WIDE_INT bitsize, bitpos;
++      tree offset;
++      int volatilep = 0;
++      tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
++                                      &mode1, &unsignedp, &volatilep);
++      rtx orig_op0;
++
++      /* If we got back the original object, something is wrong.  Perhaps
++         we are evaluating an expression too early.  In any event, don't
++         infinitely recurse.  */
++      if (tem == exp)
++        abort ();
++
++      /* If TEM's type is a union of variable size, pass TARGET to the inner
++         computation, since it will need a temporary and TARGET is known
++         to have to do.  This occurs in unchecked conversion in Ada.  */
++
++      orig_op0 = op0
++        = expand_expr (tem,
++                       (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE
++                        && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem)))
++                            != INTEGER_CST)
++                        && modifier != EXPAND_STACK_PARM
++                        ? target : NULL_RTX),
++                       VOIDmode,
++                       (modifier == EXPAND_INITIALIZER
++                        || modifier == EXPAND_CONST_ADDRESS
++                        || modifier == EXPAND_STACK_PARM)
++                       ? modifier : EXPAND_NORMAL);
++
++      /* If this is a constant, put it into a register if it is a
++         legitimate constant and OFFSET is 0 and memory if it isn't.  */
++      if (CONSTANT_P (op0))
++        {
++          enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem));
++          if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0)
++              && offset == 0)
++            op0 = force_reg (mode, op0);
++          else
++            op0 = validize_mem (force_const_mem (mode, op0));
++        }
++
++      if (offset != 0)
++        {
++          rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode,
++                                        EXPAND_SUM);
++
++          /* If this object is in a register, put it into memory.
++             This case can't occur in C, but can in Ada if we have
++             unchecked conversion of an expression from a scalar type to
++             an array or record type.  */
++          if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
++              || GET_CODE (op0) == CONCAT || GET_CODE (op0) == ADDRESSOF)
++            {
++              /* If the operand is a SAVE_EXPR, we can deal with this by
++                 forcing the SAVE_EXPR into memory.  */
++              if (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR)
++                {
++                  put_var_into_stack (TREE_OPERAND (exp, 0), 
++                                      /*rescan=*/true);
++                  op0 = SAVE_EXPR_RTL (TREE_OPERAND (exp, 0));
++                }
++              else
++                {
++                  tree nt
++                    = build_qualified_type (TREE_TYPE (tem),
++                                            (TYPE_QUALS (TREE_TYPE (tem))
++                                             | TYPE_QUAL_CONST));
++                  rtx memloc = assign_temp (nt, 1, 1, 1);
++
++                  emit_move_insn (memloc, op0);
++                  op0 = memloc;
++                }
++            }
++
++          if (GET_CODE (op0) != MEM)
++            abort ();
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++          if (GET_MODE (offset_rtx) != Pmode)
++            offset_rtx = convert_to_mode (Pmode, offset_rtx, 0);
++#else
++          if (GET_MODE (offset_rtx) != ptr_mode)
++            offset_rtx = convert_to_mode (ptr_mode, offset_rtx, 0);
++#endif
++
++          /* A constant address in OP0 can have VOIDmode, we must not try
++             to call force_reg for that case.  Avoid that case.  */
++          if (GET_CODE (op0) == MEM
++              && GET_MODE (op0) == BLKmode
++              && GET_MODE (XEXP (op0, 0)) != VOIDmode
++              && bitsize != 0
++              && (bitpos % bitsize) == 0
++              && (bitsize % GET_MODE_ALIGNMENT (mode1)) == 0
++              && MEM_ALIGN (op0) == GET_MODE_ALIGNMENT (mode1))
++            {
++              op0 = adjust_address (op0, mode1, bitpos / BITS_PER_UNIT);
++              bitpos = 0;
++            }
++
++          op0 = offset_address (op0, offset_rtx,
++                                highest_pow2_factor (offset));
++        }
++
++      /* If OFFSET is making OP0 more aligned than BIGGEST_ALIGNMENT,
++         record its alignment as BIGGEST_ALIGNMENT.  */
++      if (GET_CODE (op0) == MEM && bitpos == 0 && offset != 0
++          && is_aligning_offset (offset, tem))
++        set_mem_align (op0, BIGGEST_ALIGNMENT);
++
++      /* Don't forget about volatility even if this is a bitfield.  */
++      if (GET_CODE (op0) == MEM && volatilep && ! MEM_VOLATILE_P (op0))
++        {
++          if (op0 == orig_op0)
++            op0 = copy_rtx (op0);
++
++          MEM_VOLATILE_P (op0) = 1;
++        }
++
++      /* The following code doesn't handle CONCAT.
++         Assume only bitpos == 0 can be used for CONCAT, due to
++         one element arrays having the same mode as its element.  */
++      if (GET_CODE (op0) == CONCAT)
++        {
++          if (bitpos != 0 || bitsize != GET_MODE_BITSIZE (GET_MODE (op0)))
++            abort ();
++          return op0;
++        }
++
++      /* In cases where an aligned union has an unaligned object
++         as a field, we might be extracting a BLKmode value from
++         an integer-mode (e.g., SImode) object.  Handle this case
++         by doing the extract into an object as wide as the field
++         (which we know to be the width of a basic mode), then
++         storing into memory, and changing the mode to BLKmode.  */
++      if (mode1 == VOIDmode
++          || GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
++          || (mode1 != BLKmode && ! direct_load[(int) mode1]
++              && GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
++              && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT
++              && modifier != EXPAND_CONST_ADDRESS
++              && modifier != EXPAND_INITIALIZER)
++          /* If the field isn't aligned enough to fetch as a memref,
++             fetch it as a bit field.  */
++          || (mode1 != BLKmode
++              && SLOW_UNALIGNED_ACCESS (mode1, MEM_ALIGN (op0))
++              && ((TYPE_ALIGN (TREE_TYPE (tem))
++                   < GET_MODE_ALIGNMENT (mode))
++                  || (bitpos % GET_MODE_ALIGNMENT (mode) != 0)))
++          /* If the type and the field are a constant size and the
++             size of the type isn't the same size as the bitfield,
++             we must use bitfield operations.  */
++          || (bitsize >= 0
++              && (TREE_CODE (TYPE_SIZE (TREE_TYPE (exp)))
++                  == INTEGER_CST)
++              && 0 != compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)),
++                                        bitsize)))
++        {
++          enum machine_mode ext_mode = mode;
++
++          if (ext_mode == BLKmode
++              && ! (target != 0 && GET_CODE (op0) == MEM
++                    && GET_CODE (target) == MEM
++                    && bitpos % BITS_PER_UNIT == 0))
++            ext_mode = mode_for_size (bitsize, MODE_INT, 1);
++
++          if (ext_mode == BLKmode)
++            {
++              /* In this case, BITPOS must start at a byte boundary and
++                 TARGET, if specified, must be a MEM.  */
++              if (GET_CODE (op0) != MEM
++                  || (target != 0 && GET_CODE (target) != MEM)
++                  || bitpos % BITS_PER_UNIT != 0)
++                abort ();
++
++              op0 = adjust_address (op0, VOIDmode, bitpos / BITS_PER_UNIT);
++              if (target == 0)
++                target = assign_temp (type, 0, 1, 1);
++
++              emit_block_move (target, op0,
++                               GEN_INT ((bitsize + BITS_PER_UNIT - 1)
++                                        / BITS_PER_UNIT),
++                               (modifier == EXPAND_STACK_PARM
++                                ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++
++              return target;
++            }
++
++          op0 = validize_mem (op0);
++
++          if (GET_CODE (op0) == MEM && GET_CODE (XEXP (op0, 0)) == REG)
++            mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
++
++          op0 = extract_bit_field (op0, bitsize, bitpos, unsignedp,
++                                   (modifier == EXPAND_STACK_PARM
++                                    ? NULL_RTX : target),
++                                   ext_mode, ext_mode,
++                                   int_size_in_bytes (TREE_TYPE (tem)));
++
++          /* If the result is a record type and BITSIZE is narrower than
++             the mode of OP0, an integral mode, and this is a big endian
++             machine, we must put the field into the high-order bits.  */
++          if (TREE_CODE (type) == RECORD_TYPE && BYTES_BIG_ENDIAN
++              && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
++              && bitsize < (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (op0)))
++            op0 = expand_shift (LSHIFT_EXPR, GET_MODE (op0), op0,
++                                size_int (GET_MODE_BITSIZE (GET_MODE (op0))
++                                          - bitsize),
++                                op0, 1);
++
++          if (mode == BLKmode)
++            {
++              rtx new = assign_temp (build_qualified_type
++                                     ((*lang_hooks.types.type_for_mode)
++                                      (ext_mode, 0),
++                                      TYPE_QUAL_CONST), 0, 1, 1);
++
++              emit_move_insn (new, op0);
++              op0 = copy_rtx (new);
++              PUT_MODE (op0, BLKmode);
++              set_mem_attributes (op0, exp, 1);
++            }
++
++          return op0;
++        }
++
++      /* If the result is BLKmode, use that to access the object
++         now as well.  */
++      if (mode == BLKmode)
++        mode1 = BLKmode;
++
++      /* Get a reference to just this component.  */
++      if (modifier == EXPAND_CONST_ADDRESS
++          || modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
++        op0 = adjust_address_nv (op0, mode1, bitpos / BITS_PER_UNIT);
++      else
++        op0 = adjust_address (op0, mode1, bitpos / BITS_PER_UNIT);
++
++      if (op0 == orig_op0)
++        op0 = copy_rtx (op0);
++
++      set_mem_attributes (op0, exp, 0);
++      if (GET_CODE (XEXP (op0, 0)) == REG)
++        mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
++
++      MEM_VOLATILE_P (op0) |= volatilep;
++      if (mode == mode1 || mode1 == BLKmode || mode1 == tmode
++          || modifier == EXPAND_CONST_ADDRESS
++          || modifier == EXPAND_INITIALIZER)
++        return op0;
++      else if (target == 0)
++        target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
++
++      convert_move (target, op0, unsignedp);
++      return target;
++      }
++
++    case VTABLE_REF:
++      {
++      rtx insn, before = get_last_insn (), vtbl_ref;
++
++      /* Evaluate the interior expression.  */
++      subtarget = expand_expr (TREE_OPERAND (exp, 0), target,
++                               tmode, modifier);
++
++      /* Get or create an instruction off which to hang a note.  */
++      if (REG_P (subtarget))
++        {
++          target = subtarget;
++          insn = get_last_insn ();
++          if (insn == before)
++            abort ();
++          if (! INSN_P (insn))
++            insn = prev_nonnote_insn (insn);
++        }
++      else
++        {
++          target = gen_reg_rtx (GET_MODE (subtarget));
++          insn = emit_move_insn (target, subtarget);
++        }
++
++      /* Collect the data for the note.  */
++      vtbl_ref = XEXP (DECL_RTL (TREE_OPERAND (exp, 1)), 0);
++      vtbl_ref = plus_constant (vtbl_ref,
++                                tree_low_cst (TREE_OPERAND (exp, 2), 0));
++      /* Discard the initial CONST that was added.  */
++      vtbl_ref = XEXP (vtbl_ref, 0);
++
++      REG_NOTES (insn)
++        = gen_rtx_EXPR_LIST (REG_VTABLE_REF, vtbl_ref, REG_NOTES (insn));
++
++      return target;
++      }
++
++      /* Intended for a reference to a buffer of a file-object in Pascal.
++       But it's not certain that a special tree code will really be
++       necessary for these.  INDIRECT_REF might work for them.  */
++    case BUFFER_REF:
++      abort ();
++
++    case IN_EXPR:
++      {
++      /* Pascal set IN expression.
++
++         Algorithm:
++             rlo       = set_low - (set_low%bits_per_word);
++             the_word  = set [ (index - rlo)/bits_per_word ];
++             bit_index = index % bits_per_word;
++             bitmask   = 1 << bit_index;
++             return !!(the_word & bitmask);  */
++
++      tree set = TREE_OPERAND (exp, 0);
++      tree index = TREE_OPERAND (exp, 1);
++      int iunsignedp = TREE_UNSIGNED (TREE_TYPE (index));
++      tree set_type = TREE_TYPE (set);
++      tree set_low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (set_type));
++      tree set_high_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (set_type));
++      rtx index_val = expand_expr (index, 0, VOIDmode, 0);
++      rtx lo_r = expand_expr (set_low_bound, 0, VOIDmode, 0);
++      rtx hi_r = expand_expr (set_high_bound, 0, VOIDmode, 0);
++      rtx setval = expand_expr (set, 0, VOIDmode, 0);
++      rtx setaddr = XEXP (setval, 0);
++      enum machine_mode index_mode = TYPE_MODE (TREE_TYPE (index));
++      rtx rlow;
++      rtx diff, quo, rem, addr, bit, result;
++
++      /* If domain is empty, answer is no.  Likewise if index is constant
++         and out of bounds.  */
++      if (((TREE_CODE (set_high_bound) == INTEGER_CST
++           && TREE_CODE (set_low_bound) == INTEGER_CST
++           && tree_int_cst_lt (set_high_bound, set_low_bound))
++           || (TREE_CODE (index) == INTEGER_CST
++               && TREE_CODE (set_low_bound) == INTEGER_CST
++               && tree_int_cst_lt (index, set_low_bound))
++           || (TREE_CODE (set_high_bound) == INTEGER_CST
++               && TREE_CODE (index) == INTEGER_CST
++               && tree_int_cst_lt (set_high_bound, index))))
++        return const0_rtx;
++
++      if (target == 0)
++        target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
++
++      /* If we get here, we have to generate the code for both cases
++         (in range and out of range).  */
++
++      op0 = gen_label_rtx ();
++      op1 = gen_label_rtx ();
++
++      if (! (GET_CODE (index_val) == CONST_INT
++             && GET_CODE (lo_r) == CONST_INT))
++        emit_cmp_and_jump_insns (index_val, lo_r, LT, NULL_RTX,
++                                 GET_MODE (index_val), iunsignedp, op1);
++
++      if (! (GET_CODE (index_val) == CONST_INT
++             && GET_CODE (hi_r) == CONST_INT))
++        emit_cmp_and_jump_insns (index_val, hi_r, GT, NULL_RTX,
++                                 GET_MODE (index_val), iunsignedp, op1);
++
++      /* Calculate the element number of bit zero in the first word
++         of the set.  */
++      if (GET_CODE (lo_r) == CONST_INT)
++        rlow = GEN_INT (INTVAL (lo_r)
++                        & ~((HOST_WIDE_INT) 1 << BITS_PER_UNIT));
++      else
++        rlow = expand_binop (index_mode, and_optab, lo_r,
++                             GEN_INT (~((HOST_WIDE_INT) 1 << BITS_PER_UNIT)),
++                             NULL_RTX, iunsignedp, OPTAB_LIB_WIDEN);
++
++      diff = expand_binop (index_mode, sub_optab, index_val, rlow,
++                           NULL_RTX, iunsignedp, OPTAB_LIB_WIDEN);
++
++      quo = expand_divmod (0, TRUNC_DIV_EXPR, index_mode, diff,
++                           GEN_INT (BITS_PER_UNIT), NULL_RTX, iunsignedp);
++      rem = expand_divmod (1, TRUNC_MOD_EXPR, index_mode, index_val,
++                           GEN_INT (BITS_PER_UNIT), NULL_RTX, iunsignedp);
++
++      addr = memory_address (byte_mode,
++                             expand_binop (index_mode, add_optab, diff,
++                                           setaddr, NULL_RTX, iunsignedp,
++                                           OPTAB_LIB_WIDEN));
++
++      /* Extract the bit we want to examine.  */
++      bit = expand_shift (RSHIFT_EXPR, byte_mode,
++                          gen_rtx_MEM (byte_mode, addr),
++                          make_tree (TREE_TYPE (index), rem),
++                          NULL_RTX, 1);
++      result = expand_binop (byte_mode, and_optab, bit, const1_rtx,
++                             GET_MODE (target) == byte_mode ? target : 0,
++                             1, OPTAB_LIB_WIDEN);
++
++      if (result != target)
++        convert_move (target, result, 1);
++
++      /* Output the code to handle the out-of-range case.  */
++      emit_jump (op0);
++      emit_label (op1);
++      emit_move_insn (target, const0_rtx);
++      emit_label (op0);
++      return target;
++      }
++
++    case WITH_CLEANUP_EXPR:
++      if (WITH_CLEANUP_EXPR_RTL (exp) == 0)
++      {
++        WITH_CLEANUP_EXPR_RTL (exp)
++          = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
++        expand_decl_cleanup_eh (NULL_TREE, TREE_OPERAND (exp, 1),
++                                CLEANUP_EH_ONLY (exp));
++
++        /* That's it for this cleanup.  */
++        TREE_OPERAND (exp, 1) = 0;
++      }
++      return WITH_CLEANUP_EXPR_RTL (exp);
++
++    case CLEANUP_POINT_EXPR:
++      {
++      /* Start a new binding layer that will keep track of all cleanup
++         actions to be performed.  */
++      expand_start_bindings (2);
++
++      target_temp_slot_level = temp_slot_level;
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
++      /* If we're going to use this value, load it up now.  */
++      if (! ignore)
++        op0 = force_not_mem (op0);
++      preserve_temp_slots (op0);
++      expand_end_bindings (NULL_TREE, 0, 0);
++      }
++      return op0;
++
++    case CALL_EXPR:
++      /* Check for a built-in function.  */
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
++        && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
++            == FUNCTION_DECL)
++        && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
++      {
++        if (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
++            == BUILT_IN_FRONTEND)
++          return (*lang_hooks.expand_expr) (exp, original_target,
++                                            tmode, modifier);
++        else
++          return expand_builtin (exp, target, subtarget, tmode, ignore);
++      }
++
++      return expand_call (exp, target, ignore);
++
++    case NON_LVALUE_EXPR:
++    case NOP_EXPR:
++    case CONVERT_EXPR:
++    case REFERENCE_EXPR:
++      if (TREE_OPERAND (exp, 0) == error_mark_node)
++      return const0_rtx;
++
++      if (TREE_CODE (type) == UNION_TYPE)
++      {
++        tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0));
++
++        /* If both input and output are BLKmode, this conversion isn't doing
++           anything except possibly changing memory attribute.  */
++        if (mode == BLKmode && TYPE_MODE (valtype) == BLKmode)
++          {
++            rtx result = expand_expr (TREE_OPERAND (exp, 0), target, tmode,
++                                      modifier);
++
++            result = copy_rtx (result);
++            set_mem_attributes (result, exp, 0);
++            return result;
++          }
++
++        if (target == 0)
++          target = assign_temp (type, 0, 1, 1);
++
++        if (GET_CODE (target) == MEM)
++          /* Store data into beginning of memory target.  */
++          store_expr (TREE_OPERAND (exp, 0),
++                      adjust_address (target, TYPE_MODE (valtype), 0),
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++
++        else if (GET_CODE (target) == REG)
++          /* Store this field into a union of the proper type.  */
++          store_field (target,
++                       MIN ((int_size_in_bytes (TREE_TYPE
++                                                (TREE_OPERAND (exp, 0)))
++                             * BITS_PER_UNIT),
++                            (HOST_WIDE_INT) GET_MODE_BITSIZE (mode)),
++                       0, TYPE_MODE (valtype), TREE_OPERAND (exp, 0),
++                       VOIDmode, 0, type, 0);
++        else
++          abort ();
++
++        /* Return the entire union.  */
++        return target;
++      }
++
++      if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
++      {
++        op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode,
++                           modifier);
++
++        /* If the signedness of the conversion differs and OP0 is
++           a promoted SUBREG, clear that indication since we now
++           have to do the proper extension.  */
++        if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) != unsignedp
++            && GET_CODE (op0) == SUBREG)
++          SUBREG_PROMOTED_VAR_P (op0) = 0;
++
++        return op0;
++      }
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
++      if (GET_MODE (op0) == mode)
++      return op0;
++
++      /* If OP0 is a constant, just convert it into the proper mode.  */
++      if (CONSTANT_P (op0))
++      {
++        tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++        enum machine_mode inner_mode = TYPE_MODE (inner_type);
++
++        if (modifier == EXPAND_INITIALIZER)
++          return simplify_gen_subreg (mode, op0, inner_mode,
++                                      subreg_lowpart_offset (mode,
++                                                             inner_mode));
++        else
++          return convert_modes (mode, inner_mode, op0,
++                                TREE_UNSIGNED (inner_type));
++      }
++
++      if (modifier == EXPAND_INITIALIZER)
++      return gen_rtx_fmt_e (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
++
++      if (target == 0)
++      return
++        convert_to_mode (mode, op0,
++                         TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
++      else
++      convert_move (target, op0,
++                    TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
++      return target;
++
++    case VIEW_CONVERT_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
++
++      /* If the input and output modes are both the same, we are done.
++       Otherwise, if neither mode is BLKmode and both are within a word, we
++       can use gen_lowpart.  If neither is true, make sure the operand is
++       in memory and convert the MEM to the new mode.  */
++      if (TYPE_MODE (type) == GET_MODE (op0))
++      ;
++      else if (TYPE_MODE (type) != BLKmode && GET_MODE (op0) != BLKmode
++             && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_WORD
++             && GET_MODE_SIZE (GET_MODE (op0)) <= UNITS_PER_WORD)
++      op0 = gen_lowpart (TYPE_MODE (type), op0);
++      else if (GET_CODE (op0) != MEM)
++      {
++        /* If the operand is not a MEM, force it into memory.  Since we
++           are going to be be changing the mode of the MEM, don't call
++           force_const_mem for constants because we don't allow pool
++           constants to change mode.  */
++        tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++
++        if (TREE_ADDRESSABLE (exp))
++          abort ();
++
++        if (target == 0 || GET_MODE (target) != TYPE_MODE (inner_type))
++          target
++            = assign_stack_temp_for_type
++              (TYPE_MODE (inner_type),
++               GET_MODE_SIZE (TYPE_MODE (inner_type)), 0, inner_type);
++
++        emit_move_insn (target, op0);
++        op0 = target;
++      }
++
++      /* At this point, OP0 is in the correct mode.  If the output type is such
++       that the operand is known to be aligned, indicate that it is.
++       Otherwise, we need only be concerned about alignment for non-BLKmode
++       results.  */
++      if (GET_CODE (op0) == MEM)
++      {
++        op0 = copy_rtx (op0);
++
++        if (TYPE_ALIGN_OK (type))
++          set_mem_align (op0, MAX (MEM_ALIGN (op0), TYPE_ALIGN (type)));
++        else if (TYPE_MODE (type) != BLKmode && STRICT_ALIGNMENT
++                 && MEM_ALIGN (op0) < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
++          {
++            tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++            HOST_WIDE_INT temp_size
++              = MAX (int_size_in_bytes (inner_type),
++                     (HOST_WIDE_INT) GET_MODE_SIZE (TYPE_MODE (type)));
++            rtx new = assign_stack_temp_for_type (TYPE_MODE (type),
++                                                  temp_size, 0, type);
++            rtx new_with_op0_mode = adjust_address (new, GET_MODE (op0), 0);
++
++            if (TREE_ADDRESSABLE (exp))
++              abort ();
++
++            if (GET_MODE (op0) == BLKmode)
++              emit_block_move (new_with_op0_mode, op0,
++                               GEN_INT (GET_MODE_SIZE (TYPE_MODE (type))),
++                               (modifier == EXPAND_STACK_PARM
++                                ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++            else
++              emit_move_insn (new_with_op0_mode, op0);
++
++            op0 = new;
++          }
++
++        op0 = adjust_address (op0, TYPE_MODE (type), 0);
++      }
++
++      return op0;
++
++    case PLUS_EXPR:
++      this_optab = ! unsignedp && flag_trapv
++                   && (GET_MODE_CLASS (mode) == MODE_INT)
++                   ? addv_optab : add_optab;
++
++      /* If we are adding a constant, an RTL_EXPR that is sp, fp, or ap, and
++       something else, make sure we add the register to the constant and
++       then to the other thing.  This case can occur during strength
++       reduction and doing it this way will produce better code if the
++       frame pointer or argument pointer is eliminated.
++
++       fold-const.c will ensure that the constant is always in the inner
++       PLUS_EXPR, so the only case we need to do anything about is if
++       sp, ap, or fp is our second argument, in which case we must swap
++       the innermost first argument and our second argument.  */
++
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR
++        && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST
++        && TREE_CODE (TREE_OPERAND (exp, 1)) == RTL_EXPR
++        && (RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == frame_pointer_rtx
++            || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == stack_pointer_rtx
++            || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == arg_pointer_rtx))
++      {
++        tree t = TREE_OPERAND (exp, 1);
++
++        TREE_OPERAND (exp, 1) = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
++        TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t;
++      }
++
++      /* If the result is to be ptr_mode and we are adding an integer to
++       something, we might be forming a constant.  So try to use
++       plus_constant.  If it produces a sum and we can't accept it,
++       use force_operand.  This allows P = &ARR[const] to generate
++       efficient code on machines where a SYMBOL_REF is not a valid
++       address.
++
++       If this is an EXPAND_SUM call, always return the sum.  */
++      if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER
++        || (mode == ptr_mode && (unsignedp || ! flag_trapv)))
++      {
++        if (modifier == EXPAND_STACK_PARM)
++          target = 0;
++        if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST
++            && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
++            && TREE_CONSTANT (TREE_OPERAND (exp, 1)))
++          {
++            rtx constant_part;
++
++            op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
++                               EXPAND_SUM);
++            /* Use immed_double_const to ensure that the constant is
++               truncated according to the mode of OP1, then sign extended
++               to a HOST_WIDE_INT.  Using the constant directly can result
++               in non-canonical RTL in a 64x32 cross compile.  */
++            constant_part
++              = immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)),
++                                    (HOST_WIDE_INT) 0,
++                                    TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))));
++            op1 = plus_constant (op1, INTVAL (constant_part));
++            if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
++              op1 = force_operand (op1, target);
++            return op1;
++          }
++
++        else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
++                 && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT
++                 && TREE_CONSTANT (TREE_OPERAND (exp, 0)))
++          {
++            rtx constant_part;
++
++            op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
++                               (modifier == EXPAND_INITIALIZER
++                               ? EXPAND_INITIALIZER : EXPAND_SUM));
++            if (! CONSTANT_P (op0))
++              {
++                op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
++                                   VOIDmode, modifier);
++                /* Don't go to both_summands if modifier
++                   says it's not right to return a PLUS.  */
++                if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
++                  goto binop2;
++                goto both_summands;
++              }
++            /* Use immed_double_const to ensure that the constant is
++               truncated according to the mode of OP1, then sign extended
++               to a HOST_WIDE_INT.  Using the constant directly can result
++               in non-canonical RTL in a 64x32 cross compile.  */
++            constant_part
++              = immed_double_const (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)),
++                                    (HOST_WIDE_INT) 0,
++                                    TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))));
++            op0 = plus_constant (op0, INTVAL (constant_part));
++            if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
++              op0 = force_operand (op0, target);
++            return op0;
++          }
++      }
++
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++
++      /* No sense saving up arithmetic to be done
++       if it's all in the wrong mode to form part of an address.
++       And force_operand won't know whether to sign-extend or
++       zero-extend.  */
++      if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
++        || mode != ptr_mode)
++      {
++        op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++        op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++        if (op0 == const0_rtx)
++          return op1;
++        if (op1 == const0_rtx)
++          return op0;
++        goto binop2;
++      }
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, modifier);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, modifier);
++
++      /* We come here from MINUS_EXPR when the second operand is a
++         constant.  */
++    both_summands:
++      /* Make sure any term that's a sum with a constant comes last.  */
++      if (GET_CODE (op0) == PLUS
++        && CONSTANT_P (XEXP (op0, 1)))
++      {
++        temp = op0;
++        op0 = op1;
++        op1 = temp;
++      }
++      /* If adding to a sum including a constant,
++       associate it to put the constant outside.  */
++      if (GET_CODE (op1) == PLUS
++        && CONSTANT_P (XEXP (op1, 1)))
++      {
++        rtx constant_term = const0_rtx;
++
++        temp = simplify_binary_operation (PLUS, mode, XEXP (op1, 0), op0);
++        if (temp != 0)
++          op0 = temp;
++        /* Ensure that MULT comes first if there is one.  */
++        else if (GET_CODE (op0) == MULT)
++          op0 = gen_rtx_PLUS (mode, op0, XEXP (op1, 0));
++        else
++          op0 = gen_rtx_PLUS (mode, XEXP (op1, 0), op0);
++
++        /* Let's also eliminate constants from op0 if possible.  */
++        op0 = eliminate_constant_term (op0, &constant_term);
++
++        /* CONSTANT_TERM and XEXP (op1, 1) are known to be constant, so
++           their sum should be a constant.  Form it into OP1, since the
++           result we want will then be OP0 + OP1.  */
++
++        temp = simplify_binary_operation (PLUS, mode, constant_term,
++                                          XEXP (op1, 1));
++        if (temp != 0)
++          op1 = temp;
++        else
++          op1 = gen_rtx_PLUS (mode, constant_term, XEXP (op1, 1));
++      }
++
++      /* Put a constant term last and put a multiplication first.  */
++      if (CONSTANT_P (op0) || GET_CODE (op1) == MULT)
++      temp = op1, op1 = op0, op0 = temp;
++
++      temp = simplify_binary_operation (PLUS, mode, op0, op1);
++      return temp ? temp : gen_rtx_PLUS (mode, op0, op1);
++
++    case MINUS_EXPR:
++      /* For initializers, we are allowed to return a MINUS of two
++       symbolic constants.  Here we handle all cases when both operands
++       are constant.  */
++      /* Handle difference of two symbolic constants,
++       for the sake of an initializer.  */
++      if ((modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
++        && really_constant_p (TREE_OPERAND (exp, 0))
++        && really_constant_p (TREE_OPERAND (exp, 1)))
++      {
++        rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode,
++                               modifier);
++        rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode,
++                               modifier);
++
++        /* If the last operand is a CONST_INT, use plus_constant of
++           the negated constant.  Else make the MINUS.  */
++        if (GET_CODE (op1) == CONST_INT)
++          return plus_constant (op0, - INTVAL (op1));
++        else
++          return gen_rtx_MINUS (mode, op0, op1);
++      }
++
++      this_optab = ! unsignedp && flag_trapv
++                   && (GET_MODE_CLASS(mode) == MODE_INT)
++                   ? subv_optab : sub_optab;
++
++      /* No sense saving up arithmetic to be done
++       if it's all in the wrong mode to form part of an address.
++       And force_operand won't know whether to sign-extend or
++       zero-extend.  */
++      if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
++        || mode != ptr_mode)
++      goto binop;
++
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, modifier);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, modifier);
++
++      /* Convert A - const to A + (-const).  */
++      if (GET_CODE (op1) == CONST_INT)
++      {
++        op1 = negate_rtx (mode, op1);
++        goto both_summands;
++      }
++
++      goto binop2;
++
++    case MULT_EXPR:
++      /* If first operand is constant, swap them.
++       Thus the following special case checks need only
++       check the second operand.  */
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST)
++      {
++        tree t1 = TREE_OPERAND (exp, 0);
++        TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1);
++        TREE_OPERAND (exp, 1) = t1;
++      }
++
++      /* Attempt to return something suitable for generating an
++       indexed address, for machines that support that.  */
++
++      if (modifier == EXPAND_SUM && mode == ptr_mode
++        && host_integerp (TREE_OPERAND (exp, 1), 0))
++      {
++        tree exp1 = TREE_OPERAND (exp, 1);
++
++        op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
++                           EXPAND_SUM);
++
++        /* If we knew for certain that this is arithmetic for an array
++           reference, and we knew the bounds of the array, then we could
++           apply the distributive law across (PLUS X C) for constant C.
++           Without such knowledge, we risk overflowing the computation
++           when both X and C are large, but X+C isn't.  */
++        /* ??? Could perhaps special-case EXP being unsigned and C being
++           positive.  In that case we are certain that X+C is no smaller
++           than X and so the transformed expression will overflow iff the
++           original would have.  */
++
++        if (GET_CODE (op0) != REG)
++          op0 = force_operand (op0, NULL_RTX);
++        if (GET_CODE (op0) != REG)
++          op0 = copy_to_mode_reg (mode, op0);
++
++        return gen_rtx_MULT (mode, op0,
++                             gen_int_mode (tree_low_cst (exp1, 0),
++                                           TYPE_MODE (TREE_TYPE (exp1))));
++      }
++
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++
++      /* Check for multiplying things that have been extended
++       from a narrower type.  If this machine supports multiplying
++       in that narrower type with a result in the desired type,
++       do it that way, and avoid the explicit type-conversion.  */
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR
++        && TREE_CODE (type) == INTEGER_TYPE
++        && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
++            < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
++        && ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
++             && int_fits_type_p (TREE_OPERAND (exp, 1),
++                                 TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
++             /* Don't use a widening multiply if a shift will do.  */
++             && ((GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))))
++                  > HOST_BITS_PER_WIDE_INT)
++                 || exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0))
++            ||
++            (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR
++             && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
++                 ==
++                 TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))))
++             /* If both operands are extended, they must either both
++                be zero-extended or both be sign-extended.  */
++             && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
++                 ==
++                 TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))))))
++      {
++        enum machine_mode innermode
++          = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)));
++        optab other_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
++                      ? smul_widen_optab : umul_widen_optab);
++        this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
++                      ? umul_widen_optab : smul_widen_optab);
++        if (mode == GET_MODE_WIDER_MODE (innermode))
++          {
++            if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++              {
++                op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                                   NULL_RTX, VOIDmode, 0);
++                if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
++                  op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
++                                     VOIDmode, 0);
++                else
++                  op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
++                                     NULL_RTX, VOIDmode, 0);
++                goto binop2;
++              }
++            else if (other_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
++                     && innermode == word_mode)
++              {
++                rtx htem;
++                op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                                   NULL_RTX, VOIDmode, 0);
++                if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
++                  op1 = convert_modes (innermode, mode,
++                                       expand_expr (TREE_OPERAND (exp, 1),
++                                                    NULL_RTX, VOIDmode, 0),
++                                       unsignedp);
++                else
++                  op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
++                                     NULL_RTX, VOIDmode, 0);
++                temp = expand_binop (mode, other_optab, op0, op1, target,
++                                     unsignedp, OPTAB_LIB_WIDEN);
++                htem = expand_mult_highpart_adjust (innermode,
++                                                    gen_highpart (innermode, temp),
++                                                    op0, op1,
++                                                    gen_highpart (innermode, temp),
++                                                    unsignedp);
++                emit_move_insn (gen_highpart (innermode, temp), htem);
++                return temp;
++              }
++          }
++      }
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++      return expand_mult (mode, op0, op1, target, unsignedp);
++
++    case TRUNC_DIV_EXPR:
++    case FLOOR_DIV_EXPR:
++    case CEIL_DIV_EXPR:
++    case ROUND_DIV_EXPR:
++    case EXACT_DIV_EXPR:
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      /* Possible optimization: compute the dividend with EXPAND_SUM
++       then if the divisor is constant can optimize the case
++       where some terms of the dividend have coeffs divisible by it.  */
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++      return expand_divmod (0, code, mode, op0, op1, target, unsignedp);
++
++    case RDIV_EXPR:
++      /* Emit a/b as a*(1/b).  Later we may manage CSE the reciprocal saving
++         expensive divide.  If not, combine will rebuild the original
++         computation.  */
++      if (flag_unsafe_math_optimizations && optimize && !optimize_size
++        && TREE_CODE (type) == REAL_TYPE
++        && !real_onep (TREE_OPERAND (exp, 0)))
++        return expand_expr (build (MULT_EXPR, type, TREE_OPERAND (exp, 0),
++                                 build (RDIV_EXPR, type,
++                                        build_real (type, dconst1),
++                                        TREE_OPERAND (exp, 1))),
++                          target, tmode, modifier);
++      this_optab = sdiv_optab;
++      goto binop;
++
++    case TRUNC_MOD_EXPR:
++    case FLOOR_MOD_EXPR:
++    case CEIL_MOD_EXPR:
++    case ROUND_MOD_EXPR:
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++      return expand_divmod (1, code, mode, op0, op1, target, unsignedp);
++
++    case FIX_ROUND_EXPR:
++    case FIX_FLOOR_EXPR:
++    case FIX_CEIL_EXPR:
++      abort ();                       /* Not used for C.  */
++
++    case FIX_TRUNC_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
++      if (target == 0 || modifier == EXPAND_STACK_PARM)
++      target = gen_reg_rtx (mode);
++      expand_fix (target, op0, unsignedp);
++      return target;
++
++    case FLOAT_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
++      if (target == 0 || modifier == EXPAND_STACK_PARM)
++      target = gen_reg_rtx (mode);
++      /* expand_float can't figure out what to do if FROM has VOIDmode.
++       So give it the correct mode.  With -O, cse will optimize this.  */
++      if (GET_MODE (op0) == VOIDmode)
++      op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
++                              op0);
++      expand_float (target, op0,
++                  TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
++      return target;
++
++    case NEGATE_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      temp = expand_unop (mode,
++                        ! unsignedp && flag_trapv
++                        && (GET_MODE_CLASS(mode) == MODE_INT)
++                        ? negv_optab : neg_optab, op0, target, 0);
++      if (temp == 0)
++      abort ();
++      return temp;
++
++    case ABS_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++
++      /* Handle complex values specially.  */
++      if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
++        || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
++      return expand_complex_abs (mode, op0, target, unsignedp);
++
++      /* Unsigned abs is simply the operand.  Testing here means we don't
++       risk generating incorrect code below.  */
++      if (TREE_UNSIGNED (type))
++      return op0;
++
++      return expand_abs (mode, op0, target, unsignedp,
++                       safe_from_p (target, TREE_OPERAND (exp, 0), 1));
++
++    case MAX_EXPR:
++    case MIN_EXPR:
++      target = original_target;
++      if (target == 0
++        || modifier == EXPAND_STACK_PARM
++        || ! safe_from_p (target, TREE_OPERAND (exp, 1), 1)
++        || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
++        || GET_MODE (target) != mode
++        || (GET_CODE (target) == REG
++            && REGNO (target) < FIRST_PSEUDO_REGISTER))
++      target = gen_reg_rtx (mode);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++      op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0);
++
++      /* First try to do it with a special MIN or MAX instruction.
++       If that does not win, use a conditional jump to select the proper
++       value.  */
++      this_optab = (TREE_UNSIGNED (type)
++                  ? (code == MIN_EXPR ? umin_optab : umax_optab)
++                  : (code == MIN_EXPR ? smin_optab : smax_optab));
++
++      temp = expand_binop (mode, this_optab, op0, op1, target, unsignedp,
++                         OPTAB_WIDEN);
++      if (temp != 0)
++      return temp;
++
++      /* At this point, a MEM target is no longer useful; we will get better
++       code without it.  */
++
++      if (GET_CODE (target) == MEM)
++      target = gen_reg_rtx (mode);
++
++      if (target != op0)
++      emit_move_insn (target, op0);
++
++      op0 = gen_label_rtx ();
++
++      /* If this mode is an integer too wide to compare properly,
++       compare word by word.  Rely on cse to optimize constant cases.  */
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && ! can_compare_p (GE, mode, ccp_jump))
++      {
++        if (code == MAX_EXPR)
++          do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type),
++                                        target, op1, NULL_RTX, op0);
++        else
++          do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type),
++                                        op1, target, NULL_RTX, op0);
++      }
++      else
++      {
++        int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1)));
++        do_compare_rtx_and_jump (target, op1, code == MAX_EXPR ? GE : LE,
++                                 unsignedp, mode, NULL_RTX, NULL_RTX,
++                                 op0);
++      }
++      emit_move_insn (target, op1);
++      emit_label (op0);
++      return target;
++
++    case BIT_NOT_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      temp = expand_unop (mode, one_cmpl_optab, op0, target, 1);
++      if (temp == 0)
++      abort ();
++      return temp;
++
++    case FFS_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      temp = expand_unop (mode, ffs_optab, op0, target, 1);
++      if (temp == 0)
++      abort ();
++      return temp;
++
++      /* ??? Can optimize bitwise operations with one arg constant.
++       Can optimize (a bitwise1 n) bitwise2 (a bitwise3 b)
++       and (a bitwise1 b) bitwise2 b (etc)
++       but that is probably not worth while.  */
++
++      /* BIT_AND_EXPR is for bitwise anding.  TRUTH_AND_EXPR is for anding two
++       boolean values when we want in all cases to compute both of them.  In
++       general it is fastest to do TRUTH_AND_EXPR by computing both operands
++       as actual zero-or-1 values and then bitwise anding.  In cases where
++       there cannot be any side effects, better code would be made by
++       treating TRUTH_AND_EXPR like TRUTH_ANDIF_EXPR; but the question is
++       how to recognize those cases.  */
++
++    case TRUTH_AND_EXPR:
++    case BIT_AND_EXPR:
++      this_optab = and_optab;
++      goto binop;
++
++    case TRUTH_OR_EXPR:
++    case BIT_IOR_EXPR:
++      this_optab = ior_optab;
++      goto binop;
++
++    case TRUTH_XOR_EXPR:
++    case BIT_XOR_EXPR:
++      this_optab = xor_optab;
++      goto binop;
++
++    case LSHIFT_EXPR:
++    case RSHIFT_EXPR:
++    case LROTATE_EXPR:
++    case RROTATE_EXPR:
++      if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++      subtarget = 0;
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++      return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target,
++                         unsignedp);
++
++      /* Could determine the answer when only additive constants differ.  Also,
++       the addition of one can be handled by changing the condition.  */
++    case LT_EXPR:
++    case LE_EXPR:
++    case GT_EXPR:
++    case GE_EXPR:
++    case EQ_EXPR:
++    case NE_EXPR:
++    case UNORDERED_EXPR:
++    case ORDERED_EXPR:
++    case UNLT_EXPR:
++    case UNLE_EXPR:
++    case UNGT_EXPR:
++    case UNGE_EXPR:
++    case UNEQ_EXPR:
++      temp = do_store_flag (exp,
++                          modifier != EXPAND_STACK_PARM ? target : NULL_RTX,
++                          tmode != VOIDmode ? tmode : mode, 0);
++      if (temp != 0)
++      return temp;
++
++      /* For foo != 0, load foo, and if it is nonzero load 1 instead.  */
++      if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1))
++        && original_target
++        && GET_CODE (original_target) == REG
++        && (GET_MODE (original_target)
++            == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
++      {
++        temp = expand_expr (TREE_OPERAND (exp, 0), original_target,
++                            VOIDmode, 0);
++
++        /* If temp is constant, we can just compute the result.  */
++        if (GET_CODE (temp) == CONST_INT)
++          {
++            if (INTVAL (temp) != 0)
++              emit_move_insn (target, const1_rtx);
++            else
++              emit_move_insn (target, const0_rtx);
++
++            return target;
++          }
++
++        if (temp != original_target)
++          {
++            enum machine_mode mode1 = GET_MODE (temp);
++            if (mode1 == VOIDmode)
++              mode1 = tmode != VOIDmode ? tmode : mode;
++
++            temp = copy_to_mode_reg (mode1, temp);
++          }
++
++        op1 = gen_label_rtx ();
++        emit_cmp_and_jump_insns (temp, const0_rtx, EQ, NULL_RTX,
++                                 GET_MODE (temp), unsignedp, op1);
++        emit_move_insn (temp, const1_rtx);
++        emit_label (op1);
++        return temp;
++      }
++
++      /* If no set-flag instruction, must generate a conditional
++       store into a temporary variable.  Drop through
++       and handle this like && and ||.  */
++
++    case TRUTH_ANDIF_EXPR:
++    case TRUTH_ORIF_EXPR:
++      if (! ignore
++        && (target == 0
++            || modifier == EXPAND_STACK_PARM
++            || ! safe_from_p (target, exp, 1)
++            /* Make sure we don't have a hard reg (such as function's return
++               value) live across basic blocks, if not optimizing.  */
++            || (!optimize && GET_CODE (target) == REG
++                && REGNO (target) < FIRST_PSEUDO_REGISTER)))
++      target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
++
++      if (target)
++      emit_clr_insn (target);
++
++      op1 = gen_label_rtx ();
++      jumpifnot (exp, op1);
++
++      if (target)
++      emit_0_to_1_insn (target);
++
++      emit_label (op1);
++      return ignore ? const0_rtx : target;
++
++    case TRUTH_NOT_EXPR:
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0);
++      /* The parser is careful to generate TRUTH_NOT_EXPR
++       only with operands that are always zero or one.  */
++      temp = expand_binop (mode, xor_optab, op0, const1_rtx,
++                         target, 1, OPTAB_LIB_WIDEN);
++      if (temp == 0)
++      abort ();
++      return temp;
++
++    case COMPOUND_EXPR:
++      expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
++      emit_queue ();
++      return expand_expr (TREE_OPERAND (exp, 1),
++                        (ignore ? const0_rtx : target),
++                        VOIDmode, modifier);
++
++    case COND_EXPR:
++      /* If we would have a "singleton" (see below) were it not for a
++       conversion in each arm, bring that conversion back out.  */
++      if (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR
++        && TREE_CODE (TREE_OPERAND (exp, 2)) == NOP_EXPR
++        && (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0))
++            == TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 2), 0))))
++      {
++        tree iftrue = TREE_OPERAND (TREE_OPERAND (exp, 1), 0);
++        tree iffalse = TREE_OPERAND (TREE_OPERAND (exp, 2), 0);
++
++        if ((TREE_CODE_CLASS (TREE_CODE (iftrue)) == '2'
++             && operand_equal_p (iffalse, TREE_OPERAND (iftrue, 0), 0))
++            || (TREE_CODE_CLASS (TREE_CODE (iffalse)) == '2'
++                && operand_equal_p (iftrue, TREE_OPERAND (iffalse, 0), 0))
++            || (TREE_CODE_CLASS (TREE_CODE (iftrue)) == '1'
++                && operand_equal_p (iffalse, TREE_OPERAND (iftrue, 0), 0))
++            || (TREE_CODE_CLASS (TREE_CODE (iffalse)) == '1'
++                && operand_equal_p (iftrue, TREE_OPERAND (iffalse, 0), 0)))
++          return expand_expr (build1 (NOP_EXPR, type,
++                                      build (COND_EXPR, TREE_TYPE (iftrue),
++                                             TREE_OPERAND (exp, 0),
++                                             iftrue, iffalse)),
++                              target, tmode, modifier);
++      }
++
++      {
++      /* Note that COND_EXPRs whose type is a structure or union
++         are required to be constructed to contain assignments of
++         a temporary variable, so that we can evaluate them here
++         for side effect only.  If type is void, we must do likewise.  */
++
++      /* If an arm of the branch requires a cleanup,
++         only that cleanup is performed.  */
++
++      tree singleton = 0;
++      tree binary_op = 0, unary_op = 0;
++
++      /* If this is (A ? 1 : 0) and A is a condition, just evaluate it and
++         convert it to our mode, if necessary.  */
++      if (integer_onep (TREE_OPERAND (exp, 1))
++          && integer_zerop (TREE_OPERAND (exp, 2))
++          && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<')
++        {
++          if (ignore)
++            {
++              expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
++                           modifier);
++              return const0_rtx;
++            }
++
++          if (modifier == EXPAND_STACK_PARM)
++            target = 0;
++          op0 = expand_expr (TREE_OPERAND (exp, 0), target, mode, modifier);
++          if (GET_MODE (op0) == mode)
++            return op0;
++
++          if (target == 0)
++            target = gen_reg_rtx (mode);
++          convert_move (target, op0, unsignedp);
++          return target;
++        }
++
++      /* Check for X ? A + B : A.  If we have this, we can copy A to the
++         output and conditionally add B.  Similarly for unary operations.
++         Don't do this if X has side-effects because those side effects
++         might affect A or B and the "?" operation is a sequence point in
++         ANSI.  (operand_equal_p tests for side effects.)  */
++
++      if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '2'
++          && operand_equal_p (TREE_OPERAND (exp, 2),
++                              TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0))
++        singleton = TREE_OPERAND (exp, 2), binary_op = TREE_OPERAND (exp, 1);
++      else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '2'
++               && operand_equal_p (TREE_OPERAND (exp, 1),
++                                   TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0))
++        singleton = TREE_OPERAND (exp, 1), binary_op = TREE_OPERAND (exp, 2);
++      else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '1'
++               && operand_equal_p (TREE_OPERAND (exp, 2),
++                                   TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0))
++        singleton = TREE_OPERAND (exp, 2), unary_op = TREE_OPERAND (exp, 1);
++      else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '1'
++               && operand_equal_p (TREE_OPERAND (exp, 1),
++                                   TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0))
++        singleton = TREE_OPERAND (exp, 1), unary_op = TREE_OPERAND (exp, 2);
++
++      /* If we are not to produce a result, we have no target.  Otherwise,
++         if a target was specified use it; it will not be used as an
++         intermediate target unless it is safe.  If no target, use a
++         temporary.  */
++
++      if (ignore)
++        temp = 0;
++      else if (modifier == EXPAND_STACK_PARM)
++        temp = assign_temp (type, 0, 0, 1);
++      else if (original_target
++               && (safe_from_p (original_target, TREE_OPERAND (exp, 0), 1)
++                   || (singleton && GET_CODE (original_target) == REG
++                       && REGNO (original_target) >= FIRST_PSEUDO_REGISTER
++                       && original_target == var_rtx (singleton)))
++               && GET_MODE (original_target) == mode
++#ifdef HAVE_conditional_move
++               && (! can_conditionally_move_p (mode)
++                   || GET_CODE (original_target) == REG
++                   || TREE_ADDRESSABLE (type))
++#endif
++               && (GET_CODE (original_target) != MEM
++                   || TREE_ADDRESSABLE (type)))
++        temp = original_target;
++      else if (TREE_ADDRESSABLE (type))
++        abort ();
++      else
++        temp = assign_temp (type, 0, 0, 1);
++
++      /* If we had X ? A + C : A, with C a constant power of 2, and we can
++         do the test of X as a store-flag operation, do this as
++         A + ((X != 0) << log C).  Similarly for other simple binary
++         operators.  Only do for C == 1 if BRANCH_COST is low.  */
++      if (temp && singleton && binary_op
++          && (TREE_CODE (binary_op) == PLUS_EXPR
++              || TREE_CODE (binary_op) == MINUS_EXPR
++              || TREE_CODE (binary_op) == BIT_IOR_EXPR
++              || TREE_CODE (binary_op) == BIT_XOR_EXPR)
++          && (BRANCH_COST >= 3 ? integer_pow2p (TREE_OPERAND (binary_op, 1))
++              : integer_onep (TREE_OPERAND (binary_op, 1)))
++          && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<')
++        {
++          rtx result;
++          tree cond;
++          optab boptab = (TREE_CODE (binary_op) == PLUS_EXPR
++                          ? (TYPE_TRAP_SIGNED (TREE_TYPE (binary_op))
++                             ? addv_optab : add_optab)
++                          : TREE_CODE (binary_op) == MINUS_EXPR
++                          ? (TYPE_TRAP_SIGNED (TREE_TYPE (binary_op))
++                             ? subv_optab : sub_optab)
++                          : TREE_CODE (binary_op) == BIT_IOR_EXPR ? ior_optab
++                          : xor_optab);
++
++          /* If we had X ? A : A + 1, do this as A + (X == 0).  */
++          if (singleton == TREE_OPERAND (exp, 1))
++            cond = invert_truthvalue (TREE_OPERAND (exp, 0));
++          else
++            cond = TREE_OPERAND (exp, 0);
++
++          result = do_store_flag (cond, (safe_from_p (temp, singleton, 1)
++                                         ? temp : NULL_RTX),
++                                  mode, BRANCH_COST <= 1);
++
++          if (result != 0 && ! integer_onep (TREE_OPERAND (binary_op, 1)))
++            result = expand_shift (LSHIFT_EXPR, mode, result,
++                                   build_int_2 (tree_log2
++                                                (TREE_OPERAND
++                                                 (binary_op, 1)),
++                                                0),
++                                   (safe_from_p (temp, singleton, 1)
++                                    ? temp : NULL_RTX), 0);
++
++          if (result)
++            {
++              op1 = expand_expr (singleton, NULL_RTX, VOIDmode, 0);
++              return expand_binop (mode, boptab, op1, result, temp,
++                                   unsignedp, OPTAB_LIB_WIDEN);
++            }
++        }
++
++      do_pending_stack_adjust ();
++      NO_DEFER_POP;
++      op0 = gen_label_rtx ();
++
++      if (singleton && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)))
++        {
++          if (temp != 0)
++            {
++              /* If the target conflicts with the other operand of the
++                 binary op, we can't use it.  Also, we can't use the target
++                 if it is a hard register, because evaluating the condition
++                 might clobber it.  */
++              if ((binary_op
++                   && ! safe_from_p (temp, TREE_OPERAND (binary_op, 1), 1))
++                  || (GET_CODE (temp) == REG
++                      && REGNO (temp) < FIRST_PSEUDO_REGISTER))
++                temp = gen_reg_rtx (mode);
++              store_expr (singleton, temp,
++                          modifier == EXPAND_STACK_PARM ? 2 : 0);
++            }
++          else
++            expand_expr (singleton,
++                         ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
++          if (singleton == TREE_OPERAND (exp, 1))
++            jumpif (TREE_OPERAND (exp, 0), op0);
++          else
++            jumpifnot (TREE_OPERAND (exp, 0), op0);
++
++          start_cleanup_deferral ();
++          if (binary_op && temp == 0)
++            /* Just touch the other operand.  */
++            expand_expr (TREE_OPERAND (binary_op, 1),
++                         ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
++          else if (binary_op)
++            store_expr (build (TREE_CODE (binary_op), type,
++                               make_tree (type, temp),
++                               TREE_OPERAND (binary_op, 1)),
++                        temp, modifier == EXPAND_STACK_PARM ? 2 : 0);
++          else
++            store_expr (build1 (TREE_CODE (unary_op), type,
++                                make_tree (type, temp)),
++                        temp, modifier == EXPAND_STACK_PARM ? 2 : 0);
++          op1 = op0;
++        }
++      /* Check for A op 0 ? A : FOO and A op 0 ? FOO : A where OP is any
++         comparison operator.  If we have one of these cases, set the
++         output to A, branch on A (cse will merge these two references),
++         then set the output to FOO.  */
++      else if (temp
++               && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<'
++               && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1))
++               && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                                   TREE_OPERAND (exp, 1), 0)
++               && (! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))
++                   || TREE_CODE (TREE_OPERAND (exp, 1)) == SAVE_EXPR)
++               && safe_from_p (temp, TREE_OPERAND (exp, 2), 1))
++        {
++          if (GET_CODE (temp) == REG
++              && REGNO (temp) < FIRST_PSEUDO_REGISTER)
++            temp = gen_reg_rtx (mode);
++          store_expr (TREE_OPERAND (exp, 1), temp,
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++          jumpif (TREE_OPERAND (exp, 0), op0);
++
++          start_cleanup_deferral ();
++          store_expr (TREE_OPERAND (exp, 2), temp,
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++          op1 = op0;
++        }
++      else if (temp
++               && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<'
++               && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1))
++               && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
++                                   TREE_OPERAND (exp, 2), 0)
++               && (! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))
++                   || TREE_CODE (TREE_OPERAND (exp, 2)) == SAVE_EXPR)
++               && safe_from_p (temp, TREE_OPERAND (exp, 1), 1))
++        {
++          if (GET_CODE (temp) == REG
++              && REGNO (temp) < FIRST_PSEUDO_REGISTER)
++            temp = gen_reg_rtx (mode);
++          store_expr (TREE_OPERAND (exp, 2), temp,
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++          jumpifnot (TREE_OPERAND (exp, 0), op0);
++
++          start_cleanup_deferral ();
++          store_expr (TREE_OPERAND (exp, 1), temp,
++                      modifier == EXPAND_STACK_PARM ? 2 : 0);
++          op1 = op0;
++        }
++      else
++        {
++          op1 = gen_label_rtx ();
++          jumpifnot (TREE_OPERAND (exp, 0), op0);
++
++          start_cleanup_deferral ();
++
++          /* One branch of the cond can be void, if it never returns. For
++             example A ? throw : E  */
++          if (temp != 0
++              && TREE_TYPE (TREE_OPERAND (exp, 1)) != void_type_node)
++            store_expr (TREE_OPERAND (exp, 1), temp,
++                        modifier == EXPAND_STACK_PARM ? 2 : 0);
++          else
++            expand_expr (TREE_OPERAND (exp, 1),
++                         ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
++          end_cleanup_deferral ();
++          emit_queue ();
++          emit_jump_insn (gen_jump (op1));
++          emit_barrier ();
++          emit_label (op0);
++          start_cleanup_deferral ();
++          if (temp != 0
++              && TREE_TYPE (TREE_OPERAND (exp, 2)) != void_type_node)
++            store_expr (TREE_OPERAND (exp, 2), temp,
++                        modifier == EXPAND_STACK_PARM ? 2 : 0);
++          else
++            expand_expr (TREE_OPERAND (exp, 2),
++                         ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
++        }
++
++      end_cleanup_deferral ();
++
++      emit_queue ();
++      emit_label (op1);
++      OK_DEFER_POP;
++
++      return temp;
++      }
++
++    case TARGET_EXPR:
++      {
++      /* Something needs to be initialized, but we didn't know
++         where that thing was when building the tree.  For example,
++         it could be the return value of a function, or a parameter
++         to a function which lays down in the stack, or a temporary
++         variable which must be passed by reference.
++
++         We guarantee that the expression will either be constructed
++         or copied into our original target.  */
++
++      tree slot = TREE_OPERAND (exp, 0);
++      tree cleanups = NULL_TREE;
++      tree exp1;
++
++      if (TREE_CODE (slot) != VAR_DECL)
++        abort ();
++
++      if (! ignore)
++        target = original_target;
++
++      /* Set this here so that if we get a target that refers to a
++         register variable that's already been used, put_reg_into_stack
++         knows that it should fix up those uses.  */
++      TREE_USED (slot) = 1;
++
++      if (target == 0)
++        {
++          if (DECL_RTL_SET_P (slot))
++            {
++              target = DECL_RTL (slot);
++              /* If we have already expanded the slot, so don't do
++                 it again.  (mrs)  */
++              if (TREE_OPERAND (exp, 1) == NULL_TREE)
++                return target;
++            }
++          else
++            {
++              target = assign_temp (type, 2, 0, 1);
++              /* All temp slots at this level must not conflict.  */
++              preserve_temp_slots (target);
++              SET_DECL_RTL (slot, target);
++              if (TREE_ADDRESSABLE (slot))
++                put_var_into_stack (slot, /*rescan=*/false);
++
++              /* Since SLOT is not known to the called function
++                 to belong to its stack frame, we must build an explicit
++                 cleanup.  This case occurs when we must build up a reference
++                 to pass the reference as an argument.  In this case,
++                 it is very likely that such a reference need not be
++                 built here.  */
++
++              if (TREE_OPERAND (exp, 2) == 0)
++                TREE_OPERAND (exp, 2)
++                  = (*lang_hooks.maybe_build_cleanup) (slot);
++              cleanups = TREE_OPERAND (exp, 2);
++            }
++        }
++      else
++        {
++          /* This case does occur, when expanding a parameter which
++             needs to be constructed on the stack.  The target
++             is the actual stack address that we want to initialize.
++             The function we call will perform the cleanup in this case.  */
++
++          /* If we have already assigned it space, use that space,
++             not target that we were passed in, as our target
++             parameter is only a hint.  */
++          if (DECL_RTL_SET_P (slot))
++            {
++              target = DECL_RTL (slot);
++              /* If we have already expanded the slot, so don't do
++                   it again.  (mrs)  */
++              if (TREE_OPERAND (exp, 1) == NULL_TREE)
++                return target;
++            }
++          else
++            {
++              SET_DECL_RTL (slot, target);
++              /* If we must have an addressable slot, then make sure that
++                 the RTL that we just stored in slot is OK.  */
++              if (TREE_ADDRESSABLE (slot))
++                put_var_into_stack (slot, /*rescan=*/true);
++            }
++        }
++
++      exp1 = TREE_OPERAND (exp, 3) = TREE_OPERAND (exp, 1);
++      /* Mark it as expanded.  */
++      TREE_OPERAND (exp, 1) = NULL_TREE;
++
++      store_expr (exp1, target, modifier == EXPAND_STACK_PARM ? 2 : 0);
++
++      expand_decl_cleanup_eh (NULL_TREE, cleanups, CLEANUP_EH_ONLY (exp));
++
++      return target;
++      }
++
++    case INIT_EXPR:
++      {
++      tree lhs = TREE_OPERAND (exp, 0);
++      tree rhs = TREE_OPERAND (exp, 1);
++
++      temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0);
++      return temp;
++      }
++
++    case MODIFY_EXPR:
++      {
++      /* If lhs is complex, expand calls in rhs before computing it.
++         That's so we don't compute a pointer and save it over a
++         call.  If lhs is simple, compute it first so we can give it
++         as a target if the rhs is just a call.  This avoids an
++         extra temp and copy and that prevents a partial-subsumption
++         which makes bad code.  Actually we could treat
++         component_ref's of vars like vars.  */
++
++      tree lhs = TREE_OPERAND (exp, 0);
++      tree rhs = TREE_OPERAND (exp, 1);
++
++      temp = 0;
++
++      /* Check for |= or &= of a bitfield of size one into another bitfield
++         of size 1.  In this case, (unless we need the result of the
++         assignment) we can do this more efficiently with a
++         test followed by an assignment, if necessary.
++
++         ??? At this point, we can't get a BIT_FIELD_REF here.  But if
++         things change so we do, this code should be enhanced to
++         support it.  */
++      if (ignore
++          && TREE_CODE (lhs) == COMPONENT_REF
++          && (TREE_CODE (rhs) == BIT_IOR_EXPR
++              || TREE_CODE (rhs) == BIT_AND_EXPR)
++          && TREE_OPERAND (rhs, 0) == lhs
++          && TREE_CODE (TREE_OPERAND (rhs, 1)) == COMPONENT_REF
++          && integer_onep (DECL_SIZE (TREE_OPERAND (lhs, 1)))
++          && integer_onep (DECL_SIZE (TREE_OPERAND (TREE_OPERAND (rhs, 1), 1))))
++        {
++          rtx label = gen_label_rtx ();
++
++          do_jump (TREE_OPERAND (rhs, 1),
++                   TREE_CODE (rhs) == BIT_IOR_EXPR ? label : 0,
++                   TREE_CODE (rhs) == BIT_AND_EXPR ? label : 0);
++          expand_assignment (lhs, convert (TREE_TYPE (rhs),
++                                           (TREE_CODE (rhs) == BIT_IOR_EXPR
++                                            ? integer_one_node
++                                            : integer_zero_node)),
++                             0, 0);
++          do_pending_stack_adjust ();
++          emit_label (label);
++          return const0_rtx;
++        }
++
++      temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0);
++
++      return temp;
++      }
++
++    case RETURN_EXPR:
++      if (!TREE_OPERAND (exp, 0))
++      expand_null_return ();
++      else
++      expand_return (TREE_OPERAND (exp, 0));
++      return const0_rtx;
++
++    case PREINCREMENT_EXPR:
++    case PREDECREMENT_EXPR:
++      return expand_increment (exp, 0, ignore);
++
++    case POSTINCREMENT_EXPR:
++    case POSTDECREMENT_EXPR:
++      /* Faster to treat as pre-increment if result is not used.  */
++      return expand_increment (exp, ! ignore, ignore);
++
++    case ADDR_EXPR:
++      if (modifier == EXPAND_STACK_PARM)
++      target = 0;
++      /* Are we taking the address of a nested function?  */
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == FUNCTION_DECL
++        && decl_function_context (TREE_OPERAND (exp, 0)) != 0
++        && ! DECL_NO_STATIC_CHAIN (TREE_OPERAND (exp, 0))
++        && ! TREE_STATIC (exp))
++      {
++        op0 = trampoline_address (TREE_OPERAND (exp, 0));
++        op0 = force_operand (op0, target);
++      }
++      /* If we are taking the address of something erroneous, just
++       return a zero.  */
++      else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ERROR_MARK)
++      return const0_rtx;
++      /* If we are taking the address of a constant and are at the
++       top level, we have to use output_constant_def since we can't
++       call force_const_mem at top level.  */
++      else if (cfun == 0
++             && (TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR
++                 || (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0)))
++                     == 'c')))
++      op0 = XEXP (output_constant_def (TREE_OPERAND (exp, 0), 0), 0);
++      else
++      {
++        /* We make sure to pass const0_rtx down if we came in with
++           ignore set, to avoid doing the cleanups twice for something.  */
++        op0 = expand_expr (TREE_OPERAND (exp, 0),
++                           ignore ? const0_rtx : NULL_RTX, VOIDmode,
++                           (modifier == EXPAND_INITIALIZER
++                            ? modifier : EXPAND_CONST_ADDRESS));
++
++        /* If we are going to ignore the result, OP0 will have been set
++           to const0_rtx, so just return it.  Don't get confused and
++           think we are taking the address of the constant.  */
++        if (ignore)
++          return op0;
++
++        /* Pass 1 for MODIFY, so that protect_from_queue doesn't get
++           clever and returns a REG when given a MEM.  */
++        op0 = protect_from_queue (op0, 1);
++
++        /* We would like the object in memory.  If it is a constant, we can
++           have it be statically allocated into memory.  For a non-constant,
++           we need to allocate some memory and store the value into it.  */
++
++        if (CONSTANT_P (op0))
++          op0 = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
++                                 op0);
++        else if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
++                 || GET_CODE (op0) == CONCAT || GET_CODE (op0) == ADDRESSOF
++                 || GET_CODE (op0) == PARALLEL)
++          {
++            /* If the operand is a SAVE_EXPR, we can deal with this by
++               forcing the SAVE_EXPR into memory.  */
++            if (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR)
++              {
++                put_var_into_stack (TREE_OPERAND (exp, 0),
++                                    /*rescan=*/true);
++                op0 = SAVE_EXPR_RTL (TREE_OPERAND (exp, 0));
++              }
++            else
++              {
++                /* If this object is in a register, it can't be BLKmode.  */
++                tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++                rtx memloc = assign_temp (inner_type, 1, 1, 1);
++
++                if (GET_CODE (op0) == PARALLEL)
++                  /* Handle calls that pass values in multiple
++                     non-contiguous locations.  The Irix 6 ABI has examples
++                     of this.  */
++                  emit_group_store (memloc, op0,
++                                    int_size_in_bytes (inner_type));
++                else
++                  emit_move_insn (memloc, op0);
++
++                op0 = memloc;
++              }
++          }
++
++        if (GET_CODE (op0) != MEM)
++          abort ();
++
++        mark_temp_addr_taken (op0);
++        if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
++          {
++            op0 = XEXP (op0, 0);
++#ifdef POINTERS_EXTEND_UNSIGNED
++            if (GET_MODE (op0) == Pmode && GET_MODE (op0) != mode
++                && mode == ptr_mode)
++              op0 = convert_memory_address (ptr_mode, op0);
++#endif
++            return op0;
++          }
++
++        /* If OP0 is not aligned as least as much as the type requires, we
++           need to make a temporary, copy OP0 to it, and take the address of
++           the temporary.  We want to use the alignment of the type, not of
++           the operand.  Note that this is incorrect for FUNCTION_TYPE, but
++           the test for BLKmode means that can't happen.  The test for
++           BLKmode is because we never make mis-aligned MEMs with
++           non-BLKmode.
++
++           We don't need to do this at all if the machine doesn't have
++           strict alignment.  */
++        if (STRICT_ALIGNMENT && GET_MODE (op0) == BLKmode
++            && (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
++                > MEM_ALIGN (op0))
++            && MEM_ALIGN (op0) < BIGGEST_ALIGNMENT)
++          {
++            tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++            rtx new;
++
++            if (TYPE_ALIGN_OK (inner_type))
++              abort ();
++
++            if (TREE_ADDRESSABLE (inner_type))
++              {
++                /* We can't make a bitwise copy of this object, so fail.  */
++                error ("cannot take the address of an unaligned member");
++                return const0_rtx;
++              }
++
++            new = assign_stack_temp_for_type
++              (TYPE_MODE (inner_type),
++               MEM_SIZE (op0) ? INTVAL (MEM_SIZE (op0))
++               : int_size_in_bytes (inner_type),
++               1, build_qualified_type (inner_type,
++                                        (TYPE_QUALS (inner_type)
++                                         | TYPE_QUAL_CONST)));
++
++            emit_block_move (new, op0, expr_size (TREE_OPERAND (exp, 0)),
++                             (modifier == EXPAND_STACK_PARM
++                              ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
++
++            op0 = new;
++          }
++
++        op0 = force_operand (XEXP (op0, 0), target);
++      }
++
++      if (flag_force_addr
++        && GET_CODE (op0) != REG
++        && modifier != EXPAND_CONST_ADDRESS
++        && modifier != EXPAND_INITIALIZER
++        && modifier != EXPAND_SUM)
++      op0 = force_reg (Pmode, op0);
++
++      if (GET_CODE (op0) == REG
++        && ! REG_USERVAR_P (op0))
++      mark_reg_pointer (op0, TYPE_ALIGN (TREE_TYPE (type)));
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++      if (GET_MODE (op0) == Pmode && GET_MODE (op0) != mode
++        && mode == ptr_mode)
++      op0 = convert_memory_address (ptr_mode, op0);
++#endif
++
++      return op0;
++
++    case ENTRY_VALUE_EXPR:
++      abort ();
++
++    /* COMPLEX type for Extended Pascal & Fortran  */
++    case COMPLEX_EXPR:
++      {
++      enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
++      rtx insns;
++
++      /* Get the rtx code of the operands.  */
++      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
++      op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
++
++      if (! target)
++        target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
++
++      start_sequence ();
++
++      /* Move the real (op0) and imaginary (op1) parts to their location.  */
++      emit_move_insn (gen_realpart (mode, target), op0);
++      emit_move_insn (gen_imagpart (mode, target), op1);
++
++      insns = get_insns ();
++      end_sequence ();
++
++      /* Complex construction should appear as a single unit.  */
++      /* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
++         each with a separate pseudo as destination.
++         It's not correct for flow to treat them as a unit.  */
++      if (GET_CODE (target) != CONCAT)
++        emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
++      else
++        emit_insn (insns);
++
++      return target;
++      }
++
++    case REALPART_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
++      return gen_realpart (mode, op0);
++
++    case IMAGPART_EXPR:
++      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
++      return gen_imagpart (mode, op0);
++
++    case CONJ_EXPR:
++      {
++      enum machine_mode partmode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
++      rtx imag_t;
++      rtx insns;
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
++
++      if (! target)
++        target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      /* Store the realpart and the negated imagpart to target.  */
++      emit_move_insn (gen_realpart (partmode, target),
++                      gen_realpart (partmode, op0));
++
++      imag_t = gen_imagpart (partmode, target);
++      temp = expand_unop (partmode,
++                          ! unsignedp && flag_trapv
++                          && (GET_MODE_CLASS(partmode) == MODE_INT)
++                          ? negv_optab : neg_optab,
++                          gen_imagpart (partmode, op0), imag_t, 0);
++      if (temp != imag_t)
++        emit_move_insn (imag_t, temp);
++
++      insns = get_insns ();
++      end_sequence ();
++
++      /* Conjugate should appear as a single unit
++         If TARGET is a CONCAT, we got insns like RD = RS, ID = - IS,
++         each with a separate pseudo as destination.
++         It's not correct for flow to treat them as a unit.  */
++      if (GET_CODE (target) != CONCAT)
++        emit_no_conflict_block (insns, target, op0, NULL_RTX, NULL_RTX);
++      else
++        emit_insn (insns);
++
++      return target;
++      }
++
++    case TRY_CATCH_EXPR:
++      {
++      tree handler = TREE_OPERAND (exp, 1);
++
++      expand_eh_region_start ();
++
++      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
++
++      expand_eh_region_end_cleanup (handler);
++
++      return op0;
++      }
++
++    case TRY_FINALLY_EXPR:
++      {
++      tree try_block = TREE_OPERAND (exp, 0);
++      tree finally_block = TREE_OPERAND (exp, 1);
++
++        if (!optimize || unsafe_for_reeval (finally_block) > 1)
++        {
++          /* In this case, wrapping FINALLY_BLOCK in an UNSAVE_EXPR
++             is not sufficient, so we cannot expand the block twice.
++             So we play games with GOTO_SUBROUTINE_EXPR to let us
++             expand the thing only once.  */
++          /* When not optimizing, we go ahead with this form since
++             (1) user breakpoints operate more predictably without
++                 code duplication, and
++             (2) we're not running any of the global optimizers
++                 that would explode in time/space with the highly
++                 connected CFG created by the indirect branching.  */
++
++          rtx finally_label = gen_label_rtx ();
++          rtx done_label = gen_label_rtx ();
++          rtx return_link = gen_reg_rtx (Pmode);
++          tree cleanup = build (GOTO_SUBROUTINE_EXPR, void_type_node,
++                                (tree) finally_label, (tree) return_link);
++          TREE_SIDE_EFFECTS (cleanup) = 1;
++
++          /* Start a new binding layer that will keep track of all cleanup
++             actions to be performed.  */
++          expand_start_bindings (2);
++          target_temp_slot_level = temp_slot_level;
++
++          expand_decl_cleanup (NULL_TREE, cleanup);
++          op0 = expand_expr (try_block, target, tmode, modifier);
++
++          preserve_temp_slots (op0);
++          expand_end_bindings (NULL_TREE, 0, 0);
++          emit_jump (done_label);
++          emit_label (finally_label);
++          expand_expr (finally_block, const0_rtx, VOIDmode, 0);
++          emit_indirect_jump (return_link);
++          emit_label (done_label);
++        }
++      else
++        {
++          expand_start_bindings (2);
++          target_temp_slot_level = temp_slot_level;
++
++          expand_decl_cleanup (NULL_TREE, finally_block);
++          op0 = expand_expr (try_block, target, tmode, modifier);
++
++          preserve_temp_slots (op0);
++          expand_end_bindings (NULL_TREE, 0, 0);
++        }
++
++      return op0;
++      }
++
++    case GOTO_SUBROUTINE_EXPR:
++      {
++      rtx subr = (rtx) TREE_OPERAND (exp, 0);
++      rtx return_link = *(rtx *) &TREE_OPERAND (exp, 1);
++      rtx return_address = gen_label_rtx ();
++      emit_move_insn (return_link,
++                      gen_rtx_LABEL_REF (Pmode, return_address));
++      emit_jump (subr);
++      emit_label (return_address);
++      return const0_rtx;
++      }
++
++    case VA_ARG_EXPR:
++      return expand_builtin_va_arg (TREE_OPERAND (exp, 0), type);
++
++    case EXC_PTR_EXPR:
++      return get_exception_pointer (cfun);
++
++    case FDESC_EXPR:
++      /* Function descriptors are not valid except for as
++       initialization constants, and should not be expanded.  */
++      abort ();
++
++    default:
++      return (*lang_hooks.expand_expr) (exp, original_target, tmode, modifier);
++    }
++
++  /* Here to do an ordinary binary operator, generating an instruction
++     from the optab already placed in `this_optab'.  */
++ binop:
++  if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1), 1))
++    subtarget = 0;
++  op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
++  op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++ binop2:
++  if (modifier == EXPAND_STACK_PARM)
++    target = 0;
++  temp = expand_binop (mode, this_optab, op0, op1, target,
++                     unsignedp, OPTAB_LIB_WIDEN);
++  if (temp == 0)
++    abort ();
++  return temp;
++}
++\f
++/* Subroutine of above: returns 1 if OFFSET corresponds to an offset that
++   when applied to the address of EXP produces an address known to be
++   aligned more than BIGGEST_ALIGNMENT.  */
++
++static int
++is_aligning_offset (offset, exp)
++     tree offset;
++     tree exp;
++{
++  /* Strip off any conversions and WITH_RECORD_EXPR nodes.  */
++  while (TREE_CODE (offset) == NON_LVALUE_EXPR
++       || TREE_CODE (offset) == NOP_EXPR
++       || TREE_CODE (offset) == CONVERT_EXPR
++       || TREE_CODE (offset) == WITH_RECORD_EXPR)
++    offset = TREE_OPERAND (offset, 0);
++
++  /* We must now have a BIT_AND_EXPR with a constant that is one less than
++     power of 2 and which is larger than BIGGEST_ALIGNMENT.  */
++  if (TREE_CODE (offset) != BIT_AND_EXPR
++      || !host_integerp (TREE_OPERAND (offset, 1), 1)
++      || compare_tree_int (TREE_OPERAND (offset, 1), BIGGEST_ALIGNMENT) <= 0
++      || !exact_log2 (tree_low_cst (TREE_OPERAND (offset, 1), 1) + 1) < 0)
++    return 0;
++
++  /* Look at the first operand of BIT_AND_EXPR and strip any conversion.
++     It must be NEGATE_EXPR.  Then strip any more conversions.  */
++  offset = TREE_OPERAND (offset, 0);
++  while (TREE_CODE (offset) == NON_LVALUE_EXPR
++       || TREE_CODE (offset) == NOP_EXPR
++       || TREE_CODE (offset) == CONVERT_EXPR)
++    offset = TREE_OPERAND (offset, 0);
++
++  if (TREE_CODE (offset) != NEGATE_EXPR)
++    return 0;
++
++  offset = TREE_OPERAND (offset, 0);
++  while (TREE_CODE (offset) == NON_LVALUE_EXPR
++       || TREE_CODE (offset) == NOP_EXPR
++       || TREE_CODE (offset) == CONVERT_EXPR)
++    offset = TREE_OPERAND (offset, 0);
++
++  /* This must now be the address either of EXP or of a PLACEHOLDER_EXPR
++     whose type is the same as EXP.  */
++  return (TREE_CODE (offset) == ADDR_EXPR
++        && (TREE_OPERAND (offset, 0) == exp
++            || (TREE_CODE (TREE_OPERAND (offset, 0)) == PLACEHOLDER_EXPR
++                && (TREE_TYPE (TREE_OPERAND (offset, 0))
++                    == TREE_TYPE (exp)))));
++}
++\f
++/* Return the tree node if an ARG corresponds to a string constant or zero
++   if it doesn't.  If we return nonzero, set *PTR_OFFSET to the offset
++   in bytes within the string that ARG is accessing.  The type of the
++   offset will be `sizetype'.  */
++
++tree
++string_constant (arg, ptr_offset)
++     tree arg;
++     tree *ptr_offset;
++{
++  STRIP_NOPS (arg);
++
++  if (TREE_CODE (arg) == ADDR_EXPR
++      && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
++    {
++      *ptr_offset = size_zero_node;
++      return TREE_OPERAND (arg, 0);
++    }
++  else if (TREE_CODE (arg) == PLUS_EXPR)
++    {
++      tree arg0 = TREE_OPERAND (arg, 0);
++      tree arg1 = TREE_OPERAND (arg, 1);
++
++      STRIP_NOPS (arg0);
++      STRIP_NOPS (arg1);
++
++      if (TREE_CODE (arg0) == ADDR_EXPR
++        && TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST)
++      {
++        *ptr_offset = convert (sizetype, arg1);
++        return TREE_OPERAND (arg0, 0);
++      }
++      else if (TREE_CODE (arg1) == ADDR_EXPR
++             && TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST)
++      {
++        *ptr_offset = convert (sizetype, arg0);
++        return TREE_OPERAND (arg1, 0);
++      }
++    }
++
++  return 0;
++}
++\f
++/* Expand code for a post- or pre- increment or decrement
++   and return the RTX for the result.
++   POST is 1 for postinc/decrements and 0 for preinc/decrements.  */
++
++static rtx
++expand_increment (exp, post, ignore)
++     tree exp;
++     int post, ignore;
++{
++  rtx op0, op1;
++  rtx temp, value;
++  tree incremented = TREE_OPERAND (exp, 0);
++  optab this_optab = add_optab;
++  int icode;
++  enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
++  int op0_is_copy = 0;
++  int single_insn = 0;
++  /* 1 means we can't store into OP0 directly,
++     because it is a subreg narrower than a word,
++     and we don't dare clobber the rest of the word.  */
++  int bad_subreg = 0;
++
++  /* Stabilize any component ref that might need to be
++     evaluated more than once below.  */
++  if (!post
++      || TREE_CODE (incremented) == BIT_FIELD_REF
++      || (TREE_CODE (incremented) == COMPONENT_REF
++        && (TREE_CODE (TREE_OPERAND (incremented, 0)) != INDIRECT_REF
++            || DECL_BIT_FIELD (TREE_OPERAND (incremented, 1)))))
++    incremented = stabilize_reference (incremented);
++  /* Nested *INCREMENT_EXPRs can happen in C++.  We must force innermost
++     ones into save exprs so that they don't accidentally get evaluated
++     more than once by the code below.  */
++  if (TREE_CODE (incremented) == PREINCREMENT_EXPR
++      || TREE_CODE (incremented) == PREDECREMENT_EXPR)
++    incremented = save_expr (incremented);
++
++  /* Compute the operands as RTX.
++     Note whether OP0 is the actual lvalue or a copy of it:
++     I believe it is a copy iff it is a register or subreg
++     and insns were generated in computing it.  */
++
++  temp = get_last_insn ();
++  op0 = expand_expr (incremented, NULL_RTX, VOIDmode, 0);
++
++  /* If OP0 is a SUBREG made for a promoted variable, we cannot increment
++     in place but instead must do sign- or zero-extension during assignment,
++     so we copy it into a new register and let the code below use it as
++     a copy.
++
++     Note that we can safely modify this SUBREG since it is know not to be
++     shared (it was made by the expand_expr call above).  */
++
++  if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0))
++    {
++      if (post)
++      SUBREG_REG (op0) = copy_to_reg (SUBREG_REG (op0));
++      else
++      bad_subreg = 1;
++    }
++  else if (GET_CODE (op0) == SUBREG
++         && GET_MODE_BITSIZE (GET_MODE (op0)) < BITS_PER_WORD)
++    {
++      /* We cannot increment this SUBREG in place.  If we are
++       post-incrementing, get a copy of the old value.  Otherwise,
++       just mark that we cannot increment in place.  */
++      if (post)
++      op0 = copy_to_reg (op0);
++      else
++      bad_subreg = 1;
++    }
++
++  op0_is_copy = ((GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
++               && temp != get_last_insn ());
++  op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++
++  /* Decide whether incrementing or decrementing.  */
++  if (TREE_CODE (exp) == POSTDECREMENT_EXPR
++      || TREE_CODE (exp) == PREDECREMENT_EXPR)
++    this_optab = sub_optab;
++
++  /* Convert decrement by a constant into a negative increment.  */
++  if (this_optab == sub_optab
++      && GET_CODE (op1) == CONST_INT)
++    {
++      op1 = GEN_INT (-INTVAL (op1));
++      this_optab = add_optab;
++    }
++
++  if (TYPE_TRAP_SIGNED (TREE_TYPE (exp)))
++    this_optab = this_optab == add_optab ? addv_optab : subv_optab;
++
++  /* For a preincrement, see if we can do this with a single instruction.  */
++  if (!post)
++    {
++      icode = (int) this_optab->handlers[(int) mode].insn_code;
++      if (icode != (int) CODE_FOR_nothing
++        /* Make sure that OP0 is valid for operands 0 and 1
++           of the insn we want to queue.  */
++        && (*insn_data[icode].operand[0].predicate) (op0, mode)
++        && (*insn_data[icode].operand[1].predicate) (op0, mode)
++        && (*insn_data[icode].operand[2].predicate) (op1, mode))
++      single_insn = 1;
++    }
++
++  /* If OP0 is not the actual lvalue, but rather a copy in a register,
++     then we cannot just increment OP0.  We must therefore contrive to
++     increment the original value.  Then, for postincrement, we can return
++     OP0 since it is a copy of the old value.  For preincrement, expand here
++     unless we can do it with a single insn.
++
++     Likewise if storing directly into OP0 would clobber high bits
++     we need to preserve (bad_subreg).  */
++  if (op0_is_copy || (!post && !single_insn) || bad_subreg)
++    {
++      /* This is the easiest way to increment the value wherever it is.
++       Problems with multiple evaluation of INCREMENTED are prevented
++       because either (1) it is a component_ref or preincrement,
++       in which case it was stabilized above, or (2) it is an array_ref
++       with constant index in an array in a register, which is
++       safe to reevaluate.  */
++      tree newexp = build (((TREE_CODE (exp) == POSTDECREMENT_EXPR
++                           || TREE_CODE (exp) == PREDECREMENT_EXPR)
++                          ? MINUS_EXPR : PLUS_EXPR),
++                         TREE_TYPE (exp),
++                         incremented,
++                         TREE_OPERAND (exp, 1));
++
++      while (TREE_CODE (incremented) == NOP_EXPR
++           || TREE_CODE (incremented) == CONVERT_EXPR)
++      {
++        newexp = convert (TREE_TYPE (incremented), newexp);
++        incremented = TREE_OPERAND (incremented, 0);
++      }
++
++      temp = expand_assignment (incremented, newexp, ! post && ! ignore , 0);
++      return post ? op0 : temp;
++    }
++
++  if (post)
++    {
++      /* We have a true reference to the value in OP0.
++       If there is an insn to add or subtract in this mode, queue it.
++       Queueing the increment insn avoids the register shuffling
++       that often results if we must increment now and first save
++       the old value for subsequent use.  */
++
++#if 0  /* Turned off to avoid making extra insn for indexed memref.  */
++      op0 = stabilize (op0);
++#endif
++
++      icode = (int) this_optab->handlers[(int) mode].insn_code;
++      if (icode != (int) CODE_FOR_nothing
++        /* Make sure that OP0 is valid for operands 0 and 1
++           of the insn we want to queue.  */
++        && (*insn_data[icode].operand[0].predicate) (op0, mode)
++        && (*insn_data[icode].operand[1].predicate) (op0, mode))
++      {
++        if (! (*insn_data[icode].operand[2].predicate) (op1, mode))
++          op1 = force_reg (mode, op1);
++
++        return enqueue_insn (op0, GEN_FCN (icode) (op0, op0, op1));
++      }
++      if (icode != (int) CODE_FOR_nothing && GET_CODE (op0) == MEM)
++      {
++        rtx addr = (general_operand (XEXP (op0, 0), mode)
++                    ? force_reg (Pmode, XEXP (op0, 0))
++                    : copy_to_reg (XEXP (op0, 0)));
++        rtx temp, result;
++
++        op0 = replace_equiv_address (op0, addr);
++        temp = force_reg (GET_MODE (op0), op0);
++        if (! (*insn_data[icode].operand[2].predicate) (op1, mode))
++          op1 = force_reg (mode, op1);
++
++        /* The increment queue is LIFO, thus we have to `queue'
++           the instructions in reverse order.  */
++        enqueue_insn (op0, gen_move_insn (op0, temp));
++        result = enqueue_insn (temp, GEN_FCN (icode) (temp, temp, op1));
++        return result;
++      }
++    }
++
++  /* Preincrement, or we can't increment with one simple insn.  */
++  if (post)
++    /* Save a copy of the value before inc or dec, to return it later.  */
++    temp = value = copy_to_reg (op0);
++  else
++    /* Arrange to return the incremented value.  */
++    /* Copy the rtx because expand_binop will protect from the queue,
++       and the results of that would be invalid for us to return
++       if our caller does emit_queue before using our result.  */
++    temp = copy_rtx (value = op0);
++
++  /* Increment however we can.  */
++  op1 = expand_binop (mode, this_optab, value, op1, op0,
++                    TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN);
++
++  /* Make sure the value is stored into OP0.  */
++  if (op1 != op0)
++    emit_move_insn (op0, op1);
++
++  return temp;
++}
++\f
++/* At the start of a function, record that we have no previously-pushed
++   arguments waiting to be popped.  */
++
++void
++init_pending_stack_adjust ()
++{
++  pending_stack_adjust = 0;
++}
++
++/* When exiting from function, if safe, clear out any pending stack adjust
++   so the adjustment won't get done.
++
++   Note, if the current function calls alloca, then it must have a
++   frame pointer regardless of the value of flag_omit_frame_pointer.  */
++
++void
++clear_pending_stack_adjust ()
++{
++#ifdef EXIT_IGNORE_STACK
++  if (optimize > 0
++      && (! flag_omit_frame_pointer || current_function_calls_alloca)
++      && EXIT_IGNORE_STACK
++      && ! (DECL_INLINE (current_function_decl) && ! flag_no_inline)
++      && ! flag_inline_functions)
++    {
++      stack_pointer_delta -= pending_stack_adjust,
++      pending_stack_adjust = 0;
++    }
++#endif
++}
++
++/* Pop any previously-pushed arguments that have not been popped yet.  */
++
++void
++do_pending_stack_adjust ()
++{
++  if (inhibit_defer_pop == 0)
++    {
++      if (pending_stack_adjust != 0)
++      adjust_stack (GEN_INT (pending_stack_adjust));
++      pending_stack_adjust = 0;
++    }
++}
++\f
++/* Expand conditional expressions.  */
++
++/* Generate code to evaluate EXP and jump to LABEL if the value is zero.
++   LABEL is an rtx of code CODE_LABEL, in this function and all the
++   functions here.  */
++
++void
++jumpifnot (exp, label)
++     tree exp;
++     rtx label;
++{
++  do_jump (exp, label, NULL_RTX);
++}
++
++/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero.  */
++
++void
++jumpif (exp, label)
++     tree exp;
++     rtx label;
++{
++  do_jump (exp, NULL_RTX, label);
++}
++
++/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if
++   the result is zero, or IF_TRUE_LABEL if the result is one.
++   Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero,
++   meaning fall through in that case.
++
++   do_jump always does any pending stack adjust except when it does not
++   actually perform a jump.  An example where there is no jump
++   is when EXP is `(foo (), 0)' and IF_FALSE_LABEL is null.
++
++   This function is responsible for optimizing cases such as
++   &&, || and comparison operators in EXP.  */
++
++void
++do_jump (exp, if_false_label, if_true_label)
++     tree exp;
++     rtx if_false_label, if_true_label;
++{
++  enum tree_code code = TREE_CODE (exp);
++  /* Some cases need to create a label to jump to
++     in order to properly fall through.
++     These cases set DROP_THROUGH_LABEL nonzero.  */
++  rtx drop_through_label = 0;
++  rtx temp;
++  int i;
++  tree type;
++  enum machine_mode mode;
++
++#ifdef MAX_INTEGER_COMPUTATION_MODE
++  check_max_integer_computation_mode (exp);
++#endif
++
++  emit_queue ();
++
++  switch (code)
++    {
++    case ERROR_MARK:
++      break;
++
++    case INTEGER_CST:
++      temp = integer_zerop (exp) ? if_false_label : if_true_label;
++      if (temp)
++      emit_jump (temp);
++      break;
++
++#if 0
++      /* This is not true with #pragma weak  */
++    case ADDR_EXPR:
++      /* The address of something can never be zero.  */
++      if (if_true_label)
++      emit_jump (if_true_label);
++      break;
++#endif
++
++    case UNSAVE_EXPR:
++      do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
++      TREE_OPERAND (exp, 0)
++      = (*lang_hooks.unsave_expr_now) (TREE_OPERAND (exp, 0));
++      break;
++
++    case NOP_EXPR:
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == COMPONENT_REF
++        || TREE_CODE (TREE_OPERAND (exp, 0)) == BIT_FIELD_REF
++        || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_REF
++        || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_RANGE_REF)
++      goto normal;
++    case CONVERT_EXPR:
++      /* If we are narrowing the operand, we have to do the compare in the
++       narrower mode.  */
++      if ((TYPE_PRECISION (TREE_TYPE (exp))
++         < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0)))))
++      goto normal;
++    case NON_LVALUE_EXPR:
++    case REFERENCE_EXPR:
++    case ABS_EXPR:
++    case NEGATE_EXPR:
++    case LROTATE_EXPR:
++    case RROTATE_EXPR:
++      /* These cannot change zero->nonzero or vice versa.  */
++      do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
++      break;
++
++    case WITH_RECORD_EXPR:
++      /* Put the object on the placeholder list, recurse through our first
++       operand, and pop the list.  */
++      placeholder_list = tree_cons (TREE_OPERAND (exp, 1), NULL_TREE,
++                                  placeholder_list);
++      do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
++      placeholder_list = TREE_CHAIN (placeholder_list);
++      break;
++
++#if 0
++      /* This is never less insns than evaluating the PLUS_EXPR followed by
++       a test and can be longer if the test is eliminated.  */
++    case PLUS_EXPR:
++      /* Reduce to minus.  */
++      exp = build (MINUS_EXPR, TREE_TYPE (exp),
++                 TREE_OPERAND (exp, 0),
++                 fold (build1 (NEGATE_EXPR, TREE_TYPE (TREE_OPERAND (exp, 1)),
++                               TREE_OPERAND (exp, 1))));
++      /* Process as MINUS.  */
++#endif
++
++    case MINUS_EXPR:
++      /* Nonzero iff operands of minus differ.  */
++      do_compare_and_jump (build (NE_EXPR, TREE_TYPE (exp),
++                                TREE_OPERAND (exp, 0),
++                                TREE_OPERAND (exp, 1)),
++                         NE, NE, if_false_label, if_true_label);
++      break;
++
++    case BIT_AND_EXPR:
++      /* If we are AND'ing with a small constant, do this comparison in the
++       smallest type that fits.  If the machine doesn't have comparisons
++       that small, it will be converted back to the wider comparison.
++       This helps if we are testing the sign bit of a narrower object.
++       combine can't do this for us because it can't know whether a
++       ZERO_EXTRACT or a compare in a smaller mode exists, but we do.  */
++
++      if (! SLOW_BYTE_ACCESS
++        && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
++        && TYPE_PRECISION (TREE_TYPE (exp)) <= HOST_BITS_PER_WIDE_INT
++        && (i = tree_floor_log2 (TREE_OPERAND (exp, 1))) >= 0
++        && (mode = mode_for_size (i + 1, MODE_INT, 0)) != BLKmode
++        && (type = (*lang_hooks.types.type_for_mode) (mode, 1)) != 0
++        && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp))
++        && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code
++            != CODE_FOR_nothing))
++      {
++        do_jump (convert (type, exp), if_false_label, if_true_label);
++        break;
++      }
++      goto normal;
++
++    case TRUTH_NOT_EXPR:
++      do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
++      break;
++
++    case TRUTH_ANDIF_EXPR:
++      if (if_false_label == 0)
++      if_false_label = drop_through_label = gen_label_rtx ();
++      do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX);
++      start_cleanup_deferral ();
++      do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
++      end_cleanup_deferral ();
++      break;
++
++    case TRUTH_ORIF_EXPR:
++      if (if_true_label == 0)
++      if_true_label = drop_through_label = gen_label_rtx ();
++      do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label);
++      start_cleanup_deferral ();
++      do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
++      end_cleanup_deferral ();
++      break;
++
++    case COMPOUND_EXPR:
++      push_temp_slots ();
++      expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
++      preserve_temp_slots (NULL_RTX);
++      free_temp_slots ();
++      pop_temp_slots ();
++      emit_queue ();
++      do_pending_stack_adjust ();
++      do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
++      break;
++
++    case COMPONENT_REF:
++    case BIT_FIELD_REF:
++    case ARRAY_REF:
++    case ARRAY_RANGE_REF:
++      {
++      HOST_WIDE_INT bitsize, bitpos;
++      int unsignedp;
++      enum machine_mode mode;
++      tree type;
++      tree offset;
++      int volatilep = 0;
++
++      /* Get description of this reference.  We don't actually care
++         about the underlying object here.  */
++      get_inner_reference (exp, &bitsize, &bitpos, &offset, &mode,
++                           &unsignedp, &volatilep);
++
++      type = (*lang_hooks.types.type_for_size) (bitsize, unsignedp);
++      if (! SLOW_BYTE_ACCESS
++          && type != 0 && bitsize >= 0
++          && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp))
++          && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code
++              != CODE_FOR_nothing))
++        {
++          do_jump (convert (type, exp), if_false_label, if_true_label);
++          break;
++        }
++      goto normal;
++      }
++
++    case COND_EXPR:
++      /* Do (a ? 1 : 0) and (a ? 0 : 1) as special cases.  */
++      if (integer_onep (TREE_OPERAND (exp, 1))
++        && integer_zerop (TREE_OPERAND (exp, 2)))
++      do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
++
++      else if (integer_zerop (TREE_OPERAND (exp, 1))
++             && integer_onep (TREE_OPERAND (exp, 2)))
++      do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
++
++      else
++      {
++        rtx label1 = gen_label_rtx ();
++        drop_through_label = gen_label_rtx ();
++
++        do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX);
++
++        start_cleanup_deferral ();
++        /* Now the THEN-expression.  */
++        do_jump (TREE_OPERAND (exp, 1),
++                 if_false_label ? if_false_label : drop_through_label,
++                 if_true_label ? if_true_label : drop_through_label);
++        /* In case the do_jump just above never jumps.  */
++        do_pending_stack_adjust ();
++        emit_label (label1);
++
++        /* Now the ELSE-expression.  */
++        do_jump (TREE_OPERAND (exp, 2),
++                 if_false_label ? if_false_label : drop_through_label,
++                 if_true_label ? if_true_label : drop_through_label);
++        end_cleanup_deferral ();
++      }
++      break;
++
++    case EQ_EXPR:
++      {
++      tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++
++      if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
++          || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
++        {
++          tree exp0 = save_expr (TREE_OPERAND (exp, 0));
++          tree exp1 = save_expr (TREE_OPERAND (exp, 1));
++          do_jump
++            (fold
++             (build (TRUTH_ANDIF_EXPR, TREE_TYPE (exp),
++                     fold (build (EQ_EXPR, TREE_TYPE (exp),
++                                  fold (build1 (REALPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp0)),
++                                  fold (build1 (REALPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp1)))),
++                     fold (build (EQ_EXPR, TREE_TYPE (exp),
++                                  fold (build1 (IMAGPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp0)),
++                                  fold (build1 (IMAGPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp1)))))),
++             if_false_label, if_true_label);
++        }
++
++      else if (integer_zerop (TREE_OPERAND (exp, 1)))
++        do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
++
++      else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT
++               && !can_compare_p (EQ, TYPE_MODE (inner_type), ccp_jump))
++        do_jump_by_parts_equality (exp, if_false_label, if_true_label);
++      else
++        do_compare_and_jump (exp, EQ, EQ, if_false_label, if_true_label);
++      break;
++      }
++
++    case NE_EXPR:
++      {
++      tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
++
++      if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
++          || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
++        {
++          tree exp0 = save_expr (TREE_OPERAND (exp, 0));
++          tree exp1 = save_expr (TREE_OPERAND (exp, 1));
++          do_jump
++            (fold
++             (build (TRUTH_ORIF_EXPR, TREE_TYPE (exp),
++                     fold (build (NE_EXPR, TREE_TYPE (exp),
++                                  fold (build1 (REALPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp0)),
++                                  fold (build1 (REALPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp1)))),
++                     fold (build (NE_EXPR, TREE_TYPE (exp),
++                                  fold (build1 (IMAGPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp0)),
++                                  fold (build1 (IMAGPART_EXPR,
++                                                TREE_TYPE (inner_type),
++                                                exp1)))))),
++             if_false_label, if_true_label);
++        }
++
++      else if (integer_zerop (TREE_OPERAND (exp, 1)))
++        do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
++
++      else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT
++               && !can_compare_p (NE, TYPE_MODE (inner_type), ccp_jump))
++        do_jump_by_parts_equality (exp, if_true_label, if_false_label);
++      else
++        do_compare_and_jump (exp, NE, NE, if_false_label, if_true_label);
++      break;
++      }
++
++    case LT_EXPR:
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && ! can_compare_p (LT, mode, ccp_jump))
++      do_jump_by_parts_greater (exp, 1, if_false_label, if_true_label);
++      else
++      do_compare_and_jump (exp, LT, LTU, if_false_label, if_true_label);
++      break;
++
++    case LE_EXPR:
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && ! can_compare_p (LE, mode, ccp_jump))
++      do_jump_by_parts_greater (exp, 0, if_true_label, if_false_label);
++      else
++      do_compare_and_jump (exp, LE, LEU, if_false_label, if_true_label);
++      break;
++
++    case GT_EXPR:
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && ! can_compare_p (GT, mode, ccp_jump))
++      do_jump_by_parts_greater (exp, 0, if_false_label, if_true_label);
++      else
++      do_compare_and_jump (exp, GT, GTU, if_false_label, if_true_label);
++      break;
++
++    case GE_EXPR:
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (GET_MODE_CLASS (mode) == MODE_INT
++        && ! can_compare_p (GE, mode, ccp_jump))
++      do_jump_by_parts_greater (exp, 1, if_true_label, if_false_label);
++      else
++      do_compare_and_jump (exp, GE, GEU, if_false_label, if_true_label);
++      break;
++
++    case UNORDERED_EXPR:
++    case ORDERED_EXPR:
++      {
++      enum rtx_code cmp, rcmp;
++      int do_rev;
++
++      if (code == UNORDERED_EXPR)
++        cmp = UNORDERED, rcmp = ORDERED;
++      else
++        cmp = ORDERED, rcmp = UNORDERED;
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++
++      do_rev = 0;
++      if (! can_compare_p (cmp, mode, ccp_jump)
++          && (can_compare_p (rcmp, mode, ccp_jump)
++              /* If the target doesn't provide either UNORDERED or ORDERED
++                 comparisons, canonicalize on UNORDERED for the library.  */
++              || rcmp == UNORDERED))
++        do_rev = 1;
++
++      if (! do_rev)
++        do_compare_and_jump (exp, cmp, cmp, if_false_label, if_true_label);
++      else
++        do_compare_and_jump (exp, rcmp, rcmp, if_true_label, if_false_label);
++      }
++      break;
++
++    {
++      enum rtx_code rcode1;
++      enum tree_code tcode2;
++
++      case UNLT_EXPR:
++      rcode1 = UNLT;
++      tcode2 = LT_EXPR;
++      goto unordered_bcc;
++      case UNLE_EXPR:
++      rcode1 = UNLE;
++      tcode2 = LE_EXPR;
++      goto unordered_bcc;
++      case UNGT_EXPR:
++      rcode1 = UNGT;
++      tcode2 = GT_EXPR;
++      goto unordered_bcc;
++      case UNGE_EXPR:
++      rcode1 = UNGE;
++      tcode2 = GE_EXPR;
++      goto unordered_bcc;
++      case UNEQ_EXPR:
++      rcode1 = UNEQ;
++      tcode2 = EQ_EXPR;
++      goto unordered_bcc;
++
++      unordered_bcc:
++      mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++      if (can_compare_p (rcode1, mode, ccp_jump))
++        do_compare_and_jump (exp, rcode1, rcode1, if_false_label,
++                             if_true_label);
++      else
++        {
++          tree op0 = save_expr (TREE_OPERAND (exp, 0));
++          tree op1 = save_expr (TREE_OPERAND (exp, 1));
++          tree cmp0, cmp1;
++
++          /* If the target doesn't support combined unordered
++             compares, decompose into UNORDERED + comparison.  */
++          cmp0 = fold (build (UNORDERED_EXPR, TREE_TYPE (exp), op0, op1));
++          cmp1 = fold (build (tcode2, TREE_TYPE (exp), op0, op1));
++          exp = build (TRUTH_ORIF_EXPR, TREE_TYPE (exp), cmp0, cmp1);
++          do_jump (exp, if_false_label, if_true_label);
++        }
++      }
++      break;
++
++      /* Special case:
++              __builtin_expect (<test>, 0)    and
++              __builtin_expect (<test>, 1)
++
++       We need to do this here, so that <test> is not converted to a SCC
++       operation on machines that use condition code registers and COMPARE
++       like the PowerPC, and then the jump is done based on whether the SCC
++       operation produced a 1 or 0.  */
++    case CALL_EXPR:
++      /* Check for a built-in function.  */
++      if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR)
++      {
++        tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
++        tree arglist = TREE_OPERAND (exp, 1);
++
++        if (TREE_CODE (fndecl) == FUNCTION_DECL
++            && DECL_BUILT_IN (fndecl)
++            && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT
++            && arglist != NULL_TREE
++            && TREE_CHAIN (arglist) != NULL_TREE)
++          {
++            rtx seq = expand_builtin_expect_jump (exp, if_false_label,
++                                                  if_true_label);
++
++            if (seq != NULL_RTX)
++              {
++                emit_insn (seq);
++                return;
++              }
++          }
++      }
++      /* fall through and generate the normal code.  */
++
++    default:
++    normal:
++      temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
++#if 0
++      /* This is not needed any more and causes poor code since it causes
++       comparisons and tests from non-SI objects to have different code
++       sequences.  */
++      /* Copy to register to avoid generating bad insns by cse
++       from (set (mem ...) (arithop))  (set (cc0) (mem ...)).  */
++      if (!cse_not_expected && GET_CODE (temp) == MEM)
++      temp = copy_to_reg (temp);
++#endif
++      do_pending_stack_adjust ();
++      /* Do any postincrements in the expression that was tested.  */
++      emit_queue ();
++
++      if (GET_CODE (temp) == CONST_INT
++        || (GET_CODE (temp) == CONST_DOUBLE && GET_MODE (temp) == VOIDmode)
++        || GET_CODE (temp) == LABEL_REF)
++      {
++        rtx target = temp == const0_rtx ? if_false_label : if_true_label;
++        if (target)
++          emit_jump (target);
++      }
++      else if (GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT
++             && ! can_compare_p (NE, GET_MODE (temp), ccp_jump))
++      /* Note swapping the labels gives us not-equal.  */
++      do_jump_by_parts_equality_rtx (temp, if_true_label, if_false_label);
++      else if (GET_MODE (temp) != VOIDmode)
++      do_compare_rtx_and_jump (temp, CONST0_RTX (GET_MODE (temp)),
++                               NE, TREE_UNSIGNED (TREE_TYPE (exp)),
++                               GET_MODE (temp), NULL_RTX,
++                               if_false_label, if_true_label);
++      else
++      abort ();
++    }
++
++  if (drop_through_label)
++    {
++      /* If do_jump produces code that might be jumped around,
++       do any stack adjusts from that code, before the place
++       where control merges in.  */
++      do_pending_stack_adjust ();
++      emit_label (drop_through_label);
++    }
++}
++\f
++/* Given a comparison expression EXP for values too wide to be compared
++   with one insn, test the comparison and jump to the appropriate label.
++   The code of EXP is ignored; we always test GT if SWAP is 0,
++   and LT if SWAP is 1.  */
++
++static void
++do_jump_by_parts_greater (exp, swap, if_false_label, if_true_label)
++     tree exp;
++     int swap;
++     rtx if_false_label, if_true_label;
++{
++  rtx op0 = expand_expr (TREE_OPERAND (exp, swap), NULL_RTX, VOIDmode, 0);
++  rtx op1 = expand_expr (TREE_OPERAND (exp, !swap), NULL_RTX, VOIDmode, 0);
++  enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++  int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)));
++
++  do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label);
++}
++
++/* Compare OP0 with OP1, word at a time, in mode MODE.
++   UNSIGNEDP says to do unsigned comparison.
++   Jump to IF_TRUE_LABEL if OP0 is greater, IF_FALSE_LABEL otherwise.  */
++
++void
++do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label)
++     enum machine_mode mode;
++     int unsignedp;
++     rtx op0, op1;
++     rtx if_false_label, if_true_label;
++{
++  int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD);
++  rtx drop_through_label = 0;
++  int i;
++
++  if (! if_true_label || ! if_false_label)
++    drop_through_label = gen_label_rtx ();
++  if (! if_true_label)
++    if_true_label = drop_through_label;
++  if (! if_false_label)
++    if_false_label = drop_through_label;
++
++  /* Compare a word at a time, high order first.  */
++  for (i = 0; i < nwords; i++)
++    {
++      rtx op0_word, op1_word;
++
++      if (WORDS_BIG_ENDIAN)
++      {
++        op0_word = operand_subword_force (op0, i, mode);
++        op1_word = operand_subword_force (op1, i, mode);
++      }
++      else
++      {
++        op0_word = operand_subword_force (op0, nwords - 1 - i, mode);
++        op1_word = operand_subword_force (op1, nwords - 1 - i, mode);
++      }
++
++      /* All but high-order word must be compared as unsigned.  */
++      do_compare_rtx_and_jump (op0_word, op1_word, GT,
++                             (unsignedp || i > 0), word_mode, NULL_RTX,
++                             NULL_RTX, if_true_label);
++
++      /* Consider lower words only if these are equal.  */
++      do_compare_rtx_and_jump (op0_word, op1_word, NE, unsignedp, word_mode,
++                             NULL_RTX, NULL_RTX, if_false_label);
++    }
++
++  if (if_false_label)
++    emit_jump (if_false_label);
++  if (drop_through_label)
++    emit_label (drop_through_label);
++}
++
++/* Given an EQ_EXPR expression EXP for values too wide to be compared
++   with one insn, test the comparison and jump to the appropriate label.  */
++
++static void
++do_jump_by_parts_equality (exp, if_false_label, if_true_label)
++     tree exp;
++     rtx if_false_label, if_true_label;
++{
++  rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
++  rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++  enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
++  int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD);
++  int i;
++  rtx drop_through_label = 0;
++
++  if (! if_false_label)
++    drop_through_label = if_false_label = gen_label_rtx ();
++
++  for (i = 0; i < nwords; i++)
++    do_compare_rtx_and_jump (operand_subword_force (op0, i, mode),
++                           operand_subword_force (op1, i, mode),
++                           EQ, TREE_UNSIGNED (TREE_TYPE (exp)),
++                           word_mode, NULL_RTX, if_false_label, NULL_RTX);
++
++  if (if_true_label)
++    emit_jump (if_true_label);
++  if (drop_through_label)
++    emit_label (drop_through_label);
++}
++\f
++/* Jump according to whether OP0 is 0.
++   We assume that OP0 has an integer mode that is too wide
++   for the available compare insns.  */
++
++void
++do_jump_by_parts_equality_rtx (op0, if_false_label, if_true_label)
++     rtx op0;
++     rtx if_false_label, if_true_label;
++{
++  int nwords = GET_MODE_SIZE (GET_MODE (op0)) / UNITS_PER_WORD;
++  rtx part;
++  int i;
++  rtx drop_through_label = 0;
++
++  /* The fastest way of doing this comparison on almost any machine is to
++     "or" all the words and compare the result.  If all have to be loaded
++     from memory and this is a very wide item, it's possible this may
++     be slower, but that's highly unlikely.  */
++
++  part = gen_reg_rtx (word_mode);
++  emit_move_insn (part, operand_subword_force (op0, 0, GET_MODE (op0)));
++  for (i = 1; i < nwords && part != 0; i++)
++    part = expand_binop (word_mode, ior_optab, part,
++                       operand_subword_force (op0, i, GET_MODE (op0)),
++                       part, 1, OPTAB_WIDEN);
++
++  if (part != 0)
++    {
++      do_compare_rtx_and_jump (part, const0_rtx, EQ, 1, word_mode,
++                             NULL_RTX, if_false_label, if_true_label);
++
++      return;
++    }
++
++  /* If we couldn't do the "or" simply, do this with a series of compares.  */
++  if (! if_false_label)
++    drop_through_label = if_false_label = gen_label_rtx ();
++
++  for (i = 0; i < nwords; i++)
++    do_compare_rtx_and_jump (operand_subword_force (op0, i, GET_MODE (op0)),
++                           const0_rtx, EQ, 1, word_mode, NULL_RTX,
++                           if_false_label, NULL_RTX);
++
++  if (if_true_label)
++    emit_jump (if_true_label);
++
++  if (drop_through_label)
++    emit_label (drop_through_label);
++}
++\f
++/* Generate code for a comparison of OP0 and OP1 with rtx code CODE.
++   (including code to compute the values to be compared)
++   and set (CC0) according to the result.
++   The decision as to signed or unsigned comparison must be made by the caller.
++
++   We force a stack adjustment unless there are currently
++   things pushed on the stack that aren't yet used.
++
++   If MODE is BLKmode, SIZE is an RTX giving the size of the objects being
++   compared.  */
++
++rtx
++compare_from_rtx (op0, op1, code, unsignedp, mode, size)
++     rtx op0, op1;
++     enum rtx_code code;
++     int unsignedp;
++     enum machine_mode mode;
++     rtx size;
++{
++  enum rtx_code ucode;
++  rtx tem;
++
++  /* If one operand is constant, make it the second one.  Only do this
++     if the other operand is not constant as well.  */
++
++  if (swap_commutative_operands_p (op0, op1))
++    {
++      tem = op0;
++      op0 = op1;
++      op1 = tem;
++      code = swap_condition (code);
++    }
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++      op1 = force_not_mem (op1);
++    }
++
++  do_pending_stack_adjust ();
++
++  ucode = unsignedp ? unsigned_condition (code) : code;
++  if ((tem = simplify_relational_operation (ucode, mode, op0, op1)) != 0)
++    return tem;
++
++#if 0
++  /* There's no need to do this now that combine.c can eliminate lots of
++     sign extensions.  This can be less efficient in certain cases on other
++     machines.  */
++
++  /* If this is a signed equality comparison, we can do it as an
++     unsigned comparison since zero-extension is cheaper than sign
++     extension and comparisons with zero are done as unsigned.  This is
++     the case even on machines that can do fast sign extension, since
++     zero-extension is easier to combine with other operations than
++     sign-extension is.  If we are comparing against a constant, we must
++     convert it to what it would look like unsigned.  */
++  if ((code == EQ || code == NE) && ! unsignedp
++      && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
++    {
++      if (GET_CODE (op1) == CONST_INT
++        && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1))
++      op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0)));
++      unsignedp = 1;
++    }
++#endif
++
++  emit_cmp_insn (op0, op1, code, size, mode, unsignedp);
++
++#if HAVE_cc0
++  return gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
++#else
++  return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
++#endif
++}
++
++/* Like do_compare_and_jump but expects the values to compare as two rtx's.
++   The decision as to signed or unsigned comparison must be made by the caller.
++
++   If MODE is BLKmode, SIZE is an RTX giving the size of the objects being
++   compared.  */
++
++void
++do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, size,
++                       if_false_label, if_true_label)
++     rtx op0, op1;
++     enum rtx_code code;
++     int unsignedp;
++     enum machine_mode mode;
++     rtx size;
++     rtx if_false_label, if_true_label;
++{
++  enum rtx_code ucode;
++  rtx tem;
++  int dummy_true_label = 0;
++
++  /* Reverse the comparison if that is safe and we want to jump if it is
++     false.  */
++  if (! if_true_label && ! FLOAT_MODE_P (mode))
++    {
++      if_true_label = if_false_label;
++      if_false_label = 0;
++      code = reverse_condition (code);
++    }
++
++  /* If one operand is constant, make it the second one.  Only do this
++     if the other operand is not constant as well.  */
++
++  if (swap_commutative_operands_p (op0, op1))
++    {
++      tem = op0;
++      op0 = op1;
++      op1 = tem;
++      code = swap_condition (code);
++    }
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++      op1 = force_not_mem (op1);
++    }
++
++  do_pending_stack_adjust ();
++
++  ucode = unsignedp ? unsigned_condition (code) : code;
++  if ((tem = simplify_relational_operation (ucode, mode, op0, op1)) != 0)
++    {
++      if (tem == const_true_rtx)
++      {
++        if (if_true_label)
++          emit_jump (if_true_label);
++      }
++      else
++      {
++        if (if_false_label)
++          emit_jump (if_false_label);
++      }
++      return;
++    }
++
++#if 0
++  /* There's no need to do this now that combine.c can eliminate lots of
++     sign extensions.  This can be less efficient in certain cases on other
++     machines.  */
++
++  /* If this is a signed equality comparison, we can do it as an
++     unsigned comparison since zero-extension is cheaper than sign
++     extension and comparisons with zero are done as unsigned.  This is
++     the case even on machines that can do fast sign extension, since
++     zero-extension is easier to combine with other operations than
++     sign-extension is.  If we are comparing against a constant, we must
++     convert it to what it would look like unsigned.  */
++  if ((code == EQ || code == NE) && ! unsignedp
++      && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
++    {
++      if (GET_CODE (op1) == CONST_INT
++        && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1))
++      op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0)));
++      unsignedp = 1;
++    }
++#endif
++
++  if (! if_true_label)
++    {
++      dummy_true_label = 1;
++      if_true_label = gen_label_rtx ();
++    }
++
++  emit_cmp_and_jump_insns (op0, op1, code, size, mode, unsignedp,
++                         if_true_label);
++
++  if (if_false_label)
++    emit_jump (if_false_label);
++  if (dummy_true_label)
++    emit_label (if_true_label);
++}
++
++/* Generate code for a comparison expression EXP (including code to compute
++   the values to be compared) and a conditional jump to IF_FALSE_LABEL and/or
++   IF_TRUE_LABEL.  One of the labels can be NULL_RTX, in which case the
++   generated code will drop through.
++   SIGNED_CODE should be the rtx operation for this comparison for
++   signed data; UNSIGNED_CODE, likewise for use if data is unsigned.
++
++   We force a stack adjustment unless there are currently
++   things pushed on the stack that aren't yet used.  */
++
++static void
++do_compare_and_jump (exp, signed_code, unsigned_code, if_false_label,
++                   if_true_label)
++     tree exp;
++     enum rtx_code signed_code, unsigned_code;
++     rtx if_false_label, if_true_label;
++{
++  rtx op0, op1;
++  tree type;
++  enum machine_mode mode;
++  int unsignedp;
++  enum rtx_code code;
++
++  /* Don't crash if the comparison was erroneous.  */
++  op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
++  if (TREE_CODE (TREE_OPERAND (exp, 0)) == ERROR_MARK)
++    return;
++
++  op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
++  if (TREE_CODE (TREE_OPERAND (exp, 1)) == ERROR_MARK)
++    return;
++
++  type = TREE_TYPE (TREE_OPERAND (exp, 0));
++  mode = TYPE_MODE (type);
++  if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST
++      && (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST
++        || (GET_MODE_BITSIZE (mode)
++            > GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp,
++                                                                    1)))))))
++    {
++      /* op0 might have been replaced by promoted constant, in which
++       case the type of second argument should be used.  */
++      type = TREE_TYPE (TREE_OPERAND (exp, 1));
++      mode = TYPE_MODE (type);
++    }
++  unsignedp = TREE_UNSIGNED (type);
++  code = unsignedp ? unsigned_code : signed_code;
++
++#ifdef HAVE_canonicalize_funcptr_for_compare
++  /* If function pointers need to be "canonicalized" before they can
++     be reliably compared, then canonicalize them.  */
++  if (HAVE_canonicalize_funcptr_for_compare
++      && TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == POINTER_TYPE
++      && (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
++        == FUNCTION_TYPE))
++    {
++      rtx new_op0 = gen_reg_rtx (mode);
++
++      emit_insn (gen_canonicalize_funcptr_for_compare (new_op0, op0));
++      op0 = new_op0;
++    }
++
++  if (HAVE_canonicalize_funcptr_for_compare
++      && TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 1))) == POINTER_TYPE
++      && (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 1))))
++        == FUNCTION_TYPE))
++    {
++      rtx new_op1 = gen_reg_rtx (mode);
++
++      emit_insn (gen_canonicalize_funcptr_for_compare (new_op1, op1));
++      op1 = new_op1;
++    }
++#endif
++
++  /* Do any postincrements in the expression that was tested.  */
++  emit_queue ();
++
++  do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode,
++                         ((mode == BLKmode)
++                          ? expr_size (TREE_OPERAND (exp, 0)) : NULL_RTX),
++                         if_false_label, if_true_label);
++}
++\f
++/* Generate code to calculate EXP using a store-flag instruction
++   and return an rtx for the result.  EXP is either a comparison
++   or a TRUTH_NOT_EXPR whose operand is a comparison.
++
++   If TARGET is nonzero, store the result there if convenient.
++
++   If ONLY_CHEAP is nonzero, only do this if it is likely to be very
++   cheap.
++
++   Return zero if there is no suitable set-flag instruction
++   available on this machine.
++
++   Once expand_expr has been called on the arguments of the comparison,
++   we are committed to doing the store flag, since it is not safe to
++   re-evaluate the expression.  We emit the store-flag insn by calling
++   emit_store_flag, but only expand the arguments if we have a reason
++   to believe that emit_store_flag will be successful.  If we think that
++   it will, but it isn't, we have to simulate the store-flag with a
++   set/jump/set sequence.  */
++
++static rtx
++do_store_flag (exp, target, mode, only_cheap)
++     tree exp;
++     rtx target;
++     enum machine_mode mode;
++     int only_cheap;
++{
++  enum rtx_code code;
++  tree arg0, arg1, type;
++  tree tem;
++  enum machine_mode operand_mode;
++  int invert = 0;
++  int unsignedp;
++  rtx op0, op1;
++  enum insn_code icode;
++  rtx subtarget = target;
++  rtx result, label;
++
++  /* If this is a TRUTH_NOT_EXPR, set a flag indicating we must invert the
++     result at the end.  We can't simply invert the test since it would
++     have already been inverted if it were valid.  This case occurs for
++     some floating-point comparisons.  */
++
++  if (TREE_CODE (exp) == TRUTH_NOT_EXPR)
++    invert = 1, exp = TREE_OPERAND (exp, 0);
++
++  arg0 = TREE_OPERAND (exp, 0);
++  arg1 = TREE_OPERAND (exp, 1);
++
++  /* Don't crash if the comparison was erroneous.  */
++  if (arg0 == error_mark_node || arg1 == error_mark_node)
++    return const0_rtx;
++
++  type = TREE_TYPE (arg0);
++  operand_mode = TYPE_MODE (type);
++  unsignedp = TREE_UNSIGNED (type);
++
++  /* We won't bother with BLKmode store-flag operations because it would mean
++     passing a lot of information to emit_store_flag.  */
++  if (operand_mode == BLKmode)
++    return 0;
++
++  /* We won't bother with store-flag operations involving function pointers
++     when function pointers must be canonicalized before comparisons.  */
++#ifdef HAVE_canonicalize_funcptr_for_compare
++  if (HAVE_canonicalize_funcptr_for_compare
++      && ((TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == POINTER_TYPE
++         && (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
++             == FUNCTION_TYPE))
++        || (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 1))) == POINTER_TYPE
++            && (TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 1))))
++                == FUNCTION_TYPE))))
++    return 0;
++#endif
++
++  STRIP_NOPS (arg0);
++  STRIP_NOPS (arg1);
++
++  /* Get the rtx comparison code to use.  We know that EXP is a comparison
++     operation of some type.  Some comparisons against 1 and -1 can be
++     converted to comparisons with zero.  Do so here so that the tests
++     below will be aware that we have a comparison with zero.   These
++     tests will not catch constants in the first operand, but constants
++     are rarely passed as the first operand.  */
++
++  switch (TREE_CODE (exp))
++    {
++    case EQ_EXPR:
++      code = EQ;
++      break;
++    case NE_EXPR:
++      code = NE;
++      break;
++    case LT_EXPR:
++      if (integer_onep (arg1))
++      arg1 = integer_zero_node, code = unsignedp ? LEU : LE;
++      else
++      code = unsignedp ? LTU : LT;
++      break;
++    case LE_EXPR:
++      if (! unsignedp && integer_all_onesp (arg1))
++      arg1 = integer_zero_node, code = LT;
++      else
++      code = unsignedp ? LEU : LE;
++      break;
++    case GT_EXPR:
++      if (! unsignedp && integer_all_onesp (arg1))
++      arg1 = integer_zero_node, code = GE;
++      else
++      code = unsignedp ? GTU : GT;
++      break;
++    case GE_EXPR:
++      if (integer_onep (arg1))
++      arg1 = integer_zero_node, code = unsignedp ? GTU : GT;
++      else
++      code = unsignedp ? GEU : GE;
++      break;
++
++    case UNORDERED_EXPR:
++      code = UNORDERED;
++      break;
++    case ORDERED_EXPR:
++      code = ORDERED;
++      break;
++    case UNLT_EXPR:
++      code = UNLT;
++      break;
++    case UNLE_EXPR:
++      code = UNLE;
++      break;
++    case UNGT_EXPR:
++      code = UNGT;
++      break;
++    case UNGE_EXPR:
++      code = UNGE;
++      break;
++    case UNEQ_EXPR:
++      code = UNEQ;
++      break;
++
++    default:
++      abort ();
++    }
++
++  /* Put a constant second.  */
++  if (TREE_CODE (arg0) == REAL_CST || TREE_CODE (arg0) == INTEGER_CST)
++    {
++      tem = arg0; arg0 = arg1; arg1 = tem;
++      code = swap_condition (code);
++    }
++
++  /* If this is an equality or inequality test of a single bit, we can
++     do this by shifting the bit being tested to the low-order bit and
++     masking the result with the constant 1.  If the condition was EQ,
++     we xor it with 1.  This does not require an scc insn and is faster
++     than an scc insn even if we have it.  */
++
++  if ((code == NE || code == EQ)
++      && TREE_CODE (arg0) == BIT_AND_EXPR && integer_zerop (arg1)
++      && integer_pow2p (TREE_OPERAND (arg0, 1)))
++    {
++      tree inner = TREE_OPERAND (arg0, 0);
++      int bitnum = tree_log2 (TREE_OPERAND (arg0, 1));
++      int ops_unsignedp;
++
++      /* If INNER is a right shift of a constant and it plus BITNUM does
++       not overflow, adjust BITNUM and INNER.  */
++
++      if (TREE_CODE (inner) == RSHIFT_EXPR
++        && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST
++        && TREE_INT_CST_HIGH (TREE_OPERAND (inner, 1)) == 0
++        && bitnum < TYPE_PRECISION (type)
++        && 0 > compare_tree_int (TREE_OPERAND (inner, 1),
++                                 bitnum - TYPE_PRECISION (type)))
++      {
++        bitnum += TREE_INT_CST_LOW (TREE_OPERAND (inner, 1));
++        inner = TREE_OPERAND (inner, 0);
++      }
++
++      /* If we are going to be able to omit the AND below, we must do our
++       operations as unsigned.  If we must use the AND, we have a choice.
++       Normally unsigned is faster, but for some machines signed is.  */
++      ops_unsignedp = (bitnum == TYPE_PRECISION (type) - 1 ? 1
++#ifdef LOAD_EXTEND_OP
++                     : (LOAD_EXTEND_OP (operand_mode) == SIGN_EXTEND ? 0 : 1)
++#else
++                     : 1
++#endif
++                     );
++
++      if (! get_subtarget (subtarget)
++        || GET_MODE (subtarget) != operand_mode
++        || ! safe_from_p (subtarget, inner, 1))
++      subtarget = 0;
++
++      op0 = expand_expr (inner, subtarget, VOIDmode, 0);
++
++      if (bitnum != 0)
++      op0 = expand_shift (RSHIFT_EXPR, operand_mode, op0,
++                          size_int (bitnum), subtarget, ops_unsignedp);
++
++      if (GET_MODE (op0) != mode)
++      op0 = convert_to_mode (mode, op0, ops_unsignedp);
++
++      if ((code == EQ && ! invert) || (code == NE && invert))
++      op0 = expand_binop (mode, xor_optab, op0, const1_rtx, subtarget,
++                          ops_unsignedp, OPTAB_LIB_WIDEN);
++
++      /* Put the AND last so it can combine with more things.  */
++      if (bitnum != TYPE_PRECISION (type) - 1)
++      op0 = expand_and (mode, op0, const1_rtx, subtarget);
++
++      return op0;
++    }
++
++  /* Now see if we are likely to be able to do this.  Return if not.  */
++  if (! can_compare_p (code, operand_mode, ccp_store_flag))
++    return 0;
++
++  icode = setcc_gen_code[(int) code];
++  if (icode == CODE_FOR_nothing
++      || (only_cheap && insn_data[(int) icode].operand[0].mode != mode))
++    {
++      /* We can only do this if it is one of the special cases that
++       can be handled without an scc insn.  */
++      if ((code == LT && integer_zerop (arg1))
++        || (! only_cheap && code == GE && integer_zerop (arg1)))
++      ;
++      else if (BRANCH_COST >= 0
++             && ! only_cheap && (code == NE || code == EQ)
++             && TREE_CODE (type) != REAL_TYPE
++             && ((abs_optab->handlers[(int) operand_mode].insn_code
++                  != CODE_FOR_nothing)
++                 || (ffs_optab->handlers[(int) operand_mode].insn_code
++                     != CODE_FOR_nothing)))
++      ;
++      else
++      return 0;
++    }
++
++  if (! get_subtarget (target)
++      || GET_MODE (subtarget) != operand_mode
++      || ! safe_from_p (subtarget, arg1, 1))
++    subtarget = 0;
++
++  op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
++  op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
++
++  if (target == 0)
++    target = gen_reg_rtx (mode);
++
++  /* Pass copies of OP0 and OP1 in case they contain a QUEUED.  This is safe
++     because, if the emit_store_flag does anything it will succeed and
++     OP0 and OP1 will not be used subsequently.  */
++
++  result = emit_store_flag (target, code,
++                          queued_subexp_p (op0) ? copy_rtx (op0) : op0,
++                          queued_subexp_p (op1) ? copy_rtx (op1) : op1,
++                          operand_mode, unsignedp, 1);
++
++  if (result)
++    {
++      if (invert)
++      result = expand_binop (mode, xor_optab, result, const1_rtx,
++                             result, 0, OPTAB_LIB_WIDEN);
++      return result;
++    }
++
++  /* If this failed, we have to do this with set/compare/jump/set code.  */
++  if (GET_CODE (target) != REG
++      || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1))
++    target = gen_reg_rtx (GET_MODE (target));
++
++  emit_move_insn (target, invert ? const0_rtx : const1_rtx);
++  result = compare_from_rtx (op0, op1, code, unsignedp,
++                           operand_mode, NULL_RTX);
++  if (GET_CODE (result) == CONST_INT)
++    return (((result == const0_rtx && ! invert)
++           || (result != const0_rtx && invert))
++          ? const0_rtx : const1_rtx);
++
++  /* The code of RESULT may not match CODE if compare_from_rtx
++     decided to swap its operands and reverse the original code.
++
++     We know that compare_from_rtx returns either a CONST_INT or
++     a new comparison code, so it is safe to just extract the
++     code from RESULT.  */
++  code = GET_CODE (result);
++
++  label = gen_label_rtx ();
++  if (bcc_gen_fctn[(int) code] == 0)
++    abort ();
++
++  emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label));
++  emit_move_insn (target, invert ? const1_rtx : const0_rtx);
++  emit_label (label);
++
++  return target;
++}
++\f
++
++/* Stubs in case we haven't got a casesi insn.  */
++#ifndef HAVE_casesi
++# define HAVE_casesi 0
++# define gen_casesi(a, b, c, d, e) (0)
++# define CODE_FOR_casesi CODE_FOR_nothing
++#endif
++
++/* If the machine does not have a case insn that compares the bounds,
++   this means extra overhead for dispatch tables, which raises the
++   threshold for using them.  */
++#ifndef CASE_VALUES_THRESHOLD
++#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5)
++#endif /* CASE_VALUES_THRESHOLD */
++
++unsigned int
++case_values_threshold ()
++{
++  return CASE_VALUES_THRESHOLD;
++}
++
++/* Attempt to generate a casesi instruction.  Returns 1 if successful,
++   0 otherwise (i.e. if there is no casesi instruction).  */
++int
++try_casesi (index_type, index_expr, minval, range,
++          table_label, default_label)
++     tree index_type, index_expr, minval, range;
++     rtx table_label ATTRIBUTE_UNUSED;
++     rtx default_label;
++{
++  enum machine_mode index_mode = SImode;
++  int index_bits = GET_MODE_BITSIZE (index_mode);
++  rtx op1, op2, index;
++  enum machine_mode op_mode;
++
++  if (! HAVE_casesi)
++    return 0;
++
++  /* Convert the index to SImode.  */
++  if (GET_MODE_BITSIZE (TYPE_MODE (index_type)) > GET_MODE_BITSIZE (index_mode))
++    {
++      enum machine_mode omode = TYPE_MODE (index_type);
++      rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0);
++
++      /* We must handle the endpoints in the original mode.  */
++      index_expr = build (MINUS_EXPR, index_type,
++                        index_expr, minval);
++      minval = integer_zero_node;
++      index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
++      emit_cmp_and_jump_insns (rangertx, index, LTU, NULL_RTX,
++                             omode, 1, default_label);
++      /* Now we can safely truncate.  */
++      index = convert_to_mode (index_mode, index, 0);
++    }
++  else
++    {
++      if (TYPE_MODE (index_type) != index_mode)
++      {
++        index_expr = convert ((*lang_hooks.types.type_for_size)
++                              (index_bits, 0), index_expr);
++        index_type = TREE_TYPE (index_expr);
++      }
++
++      index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
++    }
++  emit_queue ();
++  index = protect_from_queue (index, 0);
++  do_pending_stack_adjust ();
++
++  op_mode = insn_data[(int) CODE_FOR_casesi].operand[0].mode;
++  if (! (*insn_data[(int) CODE_FOR_casesi].operand[0].predicate)
++      (index, op_mode))
++    index = copy_to_mode_reg (op_mode, index);
++
++  op1 = expand_expr (minval, NULL_RTX, VOIDmode, 0);
++
++  op_mode = insn_data[(int) CODE_FOR_casesi].operand[1].mode;
++  op1 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (minval)),
++                     op1, TREE_UNSIGNED (TREE_TYPE (minval)));
++  if (! (*insn_data[(int) CODE_FOR_casesi].operand[1].predicate)
++      (op1, op_mode))
++    op1 = copy_to_mode_reg (op_mode, op1);
++
++  op2 = expand_expr (range, NULL_RTX, VOIDmode, 0);
++
++  op_mode = insn_data[(int) CODE_FOR_casesi].operand[2].mode;
++  op2 = convert_modes (op_mode, TYPE_MODE (TREE_TYPE (range)),
++                     op2, TREE_UNSIGNED (TREE_TYPE (range)));
++  if (! (*insn_data[(int) CODE_FOR_casesi].operand[2].predicate)
++      (op2, op_mode))
++    op2 = copy_to_mode_reg (op_mode, op2);
++
++  emit_jump_insn (gen_casesi (index, op1, op2,
++                            table_label, default_label));
++  return 1;
++}
++
++/* Attempt to generate a tablejump instruction; same concept.  */
++#ifndef HAVE_tablejump
++#define HAVE_tablejump 0
++#define gen_tablejump(x, y) (0)
++#endif
++
++/* Subroutine of the next function.
++
++   INDEX is the value being switched on, with the lowest value
++   in the table already subtracted.
++   MODE is its expected mode (needed if INDEX is constant).
++   RANGE is the length of the jump table.
++   TABLE_LABEL is a CODE_LABEL rtx for the table itself.
++
++   DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the
++   index value is out of range.  */
++
++static void
++do_tablejump (index, mode, range, table_label, default_label)
++     rtx index, range, table_label, default_label;
++     enum machine_mode mode;
++{
++  rtx temp, vector;
++
++  if (INTVAL (range) > cfun->max_jumptable_ents)
++    cfun->max_jumptable_ents = INTVAL (range);
++
++  /* Do an unsigned comparison (in the proper mode) between the index
++     expression and the value which represents the length of the range.
++     Since we just finished subtracting the lower bound of the range
++     from the index expression, this comparison allows us to simultaneously
++     check that the original index expression value is both greater than
++     or equal to the minimum value of the range and less than or equal to
++     the maximum value of the range.  */
++
++  emit_cmp_and_jump_insns (index, range, GTU, NULL_RTX, mode, 1,
++                         default_label);
++
++  /* If index is in range, it must fit in Pmode.
++     Convert to Pmode so we can index with it.  */
++  if (mode != Pmode)
++    index = convert_to_mode (Pmode, index, 1);
++
++  /* Don't let a MEM slip thru, because then INDEX that comes
++     out of PIC_CASE_VECTOR_ADDRESS won't be a valid address,
++     and break_out_memory_refs will go to work on it and mess it up.  */
++#ifdef PIC_CASE_VECTOR_ADDRESS
++  if (flag_pic && GET_CODE (index) != REG)
++    index = copy_to_mode_reg (Pmode, index);
++#endif
++
++  /* If flag_force_addr were to affect this address
++     it could interfere with the tricky assumptions made
++     about addresses that contain label-refs,
++     which may be valid only very near the tablejump itself.  */
++  /* ??? The only correct use of CASE_VECTOR_MODE is the one inside the
++     GET_MODE_SIZE, because this indicates how large insns are.  The other
++     uses should all be Pmode, because they are addresses.  This code
++     could fail if addresses and insns are not the same size.  */
++  index = gen_rtx_PLUS (Pmode,
++                      gen_rtx_MULT (Pmode, index,
++                                    GEN_INT (GET_MODE_SIZE (CASE_VECTOR_MODE))),
++                      gen_rtx_LABEL_REF (Pmode, table_label));
++#ifdef PIC_CASE_VECTOR_ADDRESS
++  if (flag_pic)
++    index = PIC_CASE_VECTOR_ADDRESS (index);
++  else
++#endif
++    index = memory_address_noforce (CASE_VECTOR_MODE, index);
++  temp = gen_reg_rtx (CASE_VECTOR_MODE);
++  vector = gen_rtx_MEM (CASE_VECTOR_MODE, index);
++  RTX_UNCHANGING_P (vector) = 1;
++  MEM_NOTRAP_P (vector) = 1;
++  convert_move (temp, vector, 0);
++
++  emit_jump_insn (gen_tablejump (temp, table_label));
++
++  /* If we are generating PIC code or if the table is PC-relative, the
++     table and JUMP_INSN must be adjacent, so don't output a BARRIER.  */
++  if (! CASE_VECTOR_PC_RELATIVE && ! flag_pic)
++    emit_barrier ();
++}
++
++int
++try_tablejump (index_type, index_expr, minval, range,
++             table_label, default_label)
++     tree index_type, index_expr, minval, range;
++     rtx table_label, default_label;
++{
++  rtx index;
++
++  if (! HAVE_tablejump)
++    return 0;
++
++  index_expr = fold (build (MINUS_EXPR, index_type,
++                          convert (index_type, index_expr),
++                          convert (index_type, minval)));
++  index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
++  emit_queue ();
++  index = protect_from_queue (index, 0);
++  do_pending_stack_adjust ();
++
++  do_tablejump (index, TYPE_MODE (index_type),
++              convert_modes (TYPE_MODE (index_type),
++                             TYPE_MODE (TREE_TYPE (range)),
++                             expand_expr (range, NULL_RTX,
++                                          VOIDmode, 0),
++                             TREE_UNSIGNED (TREE_TYPE (range))),
++              table_label, default_label);
++  return 1;
++}
++
++/* Nonzero if the mode is a valid vector mode for this architecture.
++   This returns nonzero even if there is no hardware support for the
++   vector mode, but we can emulate with narrower modes.  */
++
++int
++vector_mode_valid_p (mode)
++     enum machine_mode mode;
++{
++  enum mode_class class = GET_MODE_CLASS (mode);
++  enum machine_mode innermode;
++
++  /* Doh!  What's going on?  */
++  if (class != MODE_VECTOR_INT
++      && class != MODE_VECTOR_FLOAT)
++    return 0;
++
++  /* Hardware support.  Woo hoo!  */
++  if (VECTOR_MODE_SUPPORTED_P (mode))
++    return 1;
++
++  innermode = GET_MODE_INNER (mode);
++
++  /* We should probably return 1 if requesting V4DI and we have no DI,
++     but we have V2DI, but this is probably very unlikely.  */
++
++  /* If we have support for the inner mode, we can safely emulate it.
++     We may not have V2DI, but me can emulate with a pair of DIs.  */
++  return mov_optab->handlers[innermode].insn_code != CODE_FOR_nothing;
++}
++
++/* Return a CONST_VECTOR rtx for a VECTOR_CST tree.  */
++static rtx
++const_vector_from_tree (exp)
++     tree exp;
++{
++  rtvec v;
++  int units, i;
++  tree link, elt;
++  enum machine_mode inner, mode;
++
++  mode = TYPE_MODE (TREE_TYPE (exp));
++
++  if (is_zeros_p (exp))
++    return CONST0_RTX (mode);
++
++  units = GET_MODE_NUNITS (mode);
++  inner = GET_MODE_INNER (mode);
++
++  v = rtvec_alloc (units);
++
++  link = TREE_VECTOR_CST_ELTS (exp);
++  for (i = 0; link; link = TREE_CHAIN (link), ++i)
++    {
++      elt = TREE_VALUE (link);
++
++      if (TREE_CODE (elt) == REAL_CST)
++      RTVEC_ELT (v, i) = CONST_DOUBLE_FROM_REAL_VALUE (TREE_REAL_CST (elt),
++                                                       inner);
++      else
++      RTVEC_ELT (v, i) = immed_double_const (TREE_INT_CST_LOW (elt),
++                                             TREE_INT_CST_HIGH (elt),
++                                             inner);
++    }
++
++  return gen_rtx_raw_CONST_VECTOR (mode, v);
++}
++
++#include "gt-expr.h"
+diff -ruN gcc-3.3.1/gcc/flags.h gcc-3.3.1.pp/gcc/flags.h
+--- gcc-3.3.1/gcc/flags.h      2003-06-20 21:18:41.000000000 +0000
++++ gcc-3.3.1.pp/gcc/flags.h   2003-09-05 11:58:59.000000000 +0000
+@@ -690,4 +690,12 @@
+ #define HONOR_SIGN_DEPENDENT_ROUNDING(MODE) \
+   (MODE_HAS_SIGN_DEPENDENT_ROUNDING (MODE) && !flag_unsafe_math_optimizations)
++/* Nonzero means use propolice as a stack protection method */
++
++extern int flag_propolice_protection;
++
++/* Warn when not issuing stack smashing protection for some reason */
++
++extern int warn_stack_protector;
++
+ #endif /* ! GCC_FLAGS_H */
+diff -ruN gcc-3.3.1/gcc/flags.h.orig gcc-3.3.1.pp/gcc/flags.h.orig
+--- gcc-3.3.1/gcc/flags.h.orig 1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/flags.h.orig      2003-06-20 21:18:41.000000000 +0000
+@@ -0,0 +1,693 @@
++/* Compilation switch flag definitions for GCC.
++   Copyright (C) 1987, 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002
++   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.  */
++
++#ifndef GCC_FLAGS_H
++#define GCC_FLAGS_H
++
++/* Name of the input .c file being compiled.  */
++extern const char *main_input_filename;
++
++enum debug_info_type
++{
++  NO_DEBUG,       /* Write no debug info.  */
++  DBX_DEBUG,      /* Write BSD .stabs for DBX (using dbxout.c).  */
++  SDB_DEBUG,      /* Write COFF for (old) SDB (using sdbout.c).  */
++  DWARF_DEBUG,            /* Write Dwarf debug info (using dwarfout.c).  */
++  DWARF2_DEBUG,           /* Write Dwarf v2 debug info (using dwarf2out.c).  */
++  XCOFF_DEBUG,            /* Write IBM/Xcoff debug info (using dbxout.c).  */
++  VMS_DEBUG,        /* Write VMS debug info (using vmsdbgout.c).  */
++  VMS_AND_DWARF2_DEBUG /* Write VMS debug info (using vmsdbgout.c).
++                          and DWARF v2 debug info (using dwarf2out.c).  */
++};
++
++/* Specify which kind of debugging info to generate.  */
++extern enum debug_info_type write_symbols;
++
++enum debug_info_level
++{
++  DINFO_LEVEL_NONE,   /* Write no debugging info.  */
++  DINFO_LEVEL_TERSE,  /* Write minimal info to support tracebacks only.  */
++  DINFO_LEVEL_NORMAL, /* Write info for all declarations (and line table).  */
++  DINFO_LEVEL_VERBOSE /* Write normal info plus #define/#undef info.  */
++};
++
++/* Specify how much debugging info to generate.  */
++extern enum debug_info_level debug_info_level;
++
++/* Nonzero means use GNU-only extensions in the generated symbolic
++   debugging information.  */
++extern int use_gnu_debug_info_extensions;
++
++/* Nonzero means do optimizations.  -opt.  */
++
++extern int optimize;
++
++/* Nonzero means optimize for size.  -Os.  */
++
++extern int optimize_size;
++
++/* Don't print functions as they are compiled and don't print
++   times taken by the various passes.  -quiet.  */
++
++extern int quiet_flag;
++
++/* Print times taken by the various passes.  -ftime-report.  */
++
++extern int time_report;
++
++/* Print memory still in use at end of compilation (which may have little
++   to do with peak memory consumption).  -fmem-report.  */
++
++extern int mem_report;
++
++/* Don't print warning messages.  -w.  */
++
++extern int inhibit_warnings;
++
++/* Don't suppress warnings from system headers.  -Wsystem-headers.  */
++
++extern int warn_system_headers;
++
++/* Do print extra warnings (such as for uninitialized variables).  -W.  */
++
++extern int extra_warnings;
++
++/* Nonzero to warn about unused variables, functions et.al.  Use
++   set_Wunused() to update the -Wunused-* flags that correspond to the
++   -Wunused option.  */
++
++extern void set_Wunused PARAMS ((int setting));
++
++extern int warn_unused_function;
++extern int warn_unused_label;
++extern int warn_unused_parameter;
++extern int warn_unused_variable;
++extern int warn_unused_value;
++
++/* Nonzero to warn about code which is never reached.  */
++
++extern int warn_notreached;
++
++/* Nonzero means warn if inline function is too large.  */
++
++extern int warn_inline;
++
++/* Nonzero to warn about variables used before they are initialized.  */
++
++extern int warn_uninitialized;
++
++/* Zero if unknown pragmas are ignored
++   One if the compiler should warn about an unknown pragma not in
++   a system include file.
++   Greater than one if the compiler should warn for all unknown
++   pragmas.  */
++
++extern int warn_unknown_pragmas;
++
++/* Nonzero means warn about all declarations which shadow others.  */
++
++extern int warn_shadow;
++
++/* Warn if a switch on an enum, that does not have a default case,
++   fails to have a case for every enum value.  */
++
++extern int warn_switch;
++
++/* Warn if a switch does not have a default case.  */
++
++extern int warn_switch_default;
++
++/* Warn if a switch on an enum fails to have a case for every enum
++   value (regardless of the presence or otherwise of a default case).  */
++
++extern int warn_switch_enum;
++
++/* Nonzero means warn about function definitions that default the return type
++   or that use a null return and have a return-type other than void.  */
++
++extern int warn_return_type;
++
++/* Warn about functions which might be candidates for attribute noreturn.  */
++
++extern int warn_missing_noreturn;
++
++/* Nonzero means warn about pointer casts that increase the required
++   alignment of the target type (and might therefore lead to a crash
++   due to a misaligned access).  */
++
++extern int warn_cast_align;
++
++/* Nonzero means warn about any objects definitions whose size is larger
++   than N bytes.  Also want about function definitions whose returned
++   values are larger than N bytes. The value N is in `larger_than_size'.  */
++
++extern int warn_larger_than;
++extern HOST_WIDE_INT larger_than_size;
++
++/* Warn if a function returns an aggregate,
++   since there are often incompatible calling conventions for doing this.  */
++
++extern int warn_aggregate_return;
++
++/* Warn if packed attribute on struct is unnecessary and inefficient.  */
++
++extern int warn_packed;
++
++/* Warn when gcc pads a structure to an alignment boundary.  */
++
++extern int warn_padded;
++
++/* Warn when an optimization pass is disabled.  */
++
++extern int warn_disabled_optimization;
++
++/* Nonzero means warn about uses of __attribute__((deprecated)) 
++   declarations.  */
++
++extern int warn_deprecated_decl;
++
++/* Nonzero means warn about constructs which might not be strict
++   aliasing safe.  */
++
++extern int warn_strict_aliasing;
++
++/* Nonzero if generating code to do profiling.  */
++
++extern int profile_flag;
++
++/* Nonzero if generating code to profile program flow graph arcs.  */
++
++extern int profile_arc_flag;
++
++/* Nonzero if generating info for gcov to calculate line test coverage.  */
++
++extern int flag_test_coverage;
++
++/* Nonzero indicates that branch taken probabilities should be calculated.  */
++
++extern int flag_branch_probabilities;
++
++/* Nonzero if basic blocks should be reordered.  */
++
++extern int flag_reorder_blocks;
++
++/* Nonzero if functions should be reordered.  */
++
++extern int flag_reorder_functions;
++
++/* Nonzero if registers should be renamed.  */
++
++extern int flag_rename_registers;
++
++/* Nonzero for -pedantic switch: warn about anything
++   that standard C forbids.  */
++
++extern int pedantic;
++
++/* Temporarily suppress certain warnings.
++   This is set while reading code from a system header file.  */
++
++extern int in_system_header;
++
++/* Nonzero for -dp: annotate the assembly with a comment describing the
++   pattern and alternative used.  */
++
++extern int flag_print_asm_name;
++\f
++/* Now the symbols that are set with `-f' switches.  */
++
++/* Nonzero means `char' should be signed.  */
++
++extern int flag_signed_char;
++
++/* Nonzero means give an enum type only as many bytes as it needs.  */
++
++extern int flag_short_enums;
++
++/* Nonzero for -fcaller-saves: allocate values in regs that need to
++   be saved across function calls, if that produces overall better code.
++   Optional now, so people can test it.  */
++
++extern int flag_caller_saves;
++
++/* Nonzero for -fpcc-struct-return: return values the same way PCC does.  */
++
++extern int flag_pcc_struct_return;
++
++/* Nonzero for -fforce-mem: load memory value into a register
++   before arithmetic on it.  This makes better cse but slower compilation.  */
++
++extern int flag_force_mem;
++
++/* Nonzero for -fforce-addr: load memory address into a register before
++   reference to memory.  This makes better cse but slower compilation.  */
++
++extern int flag_force_addr;
++
++/* Nonzero for -fdefer-pop: don't pop args after each function call;
++   instead save them up to pop many calls' args with one insns.  */
++
++extern int flag_defer_pop;
++
++/* Nonzero for -ffloat-store: don't allocate floats and doubles
++   in extended-precision registers.  */
++
++extern int flag_float_store;
++
++/* Nonzero enables strength-reduction in loop.c.  */
++
++extern int flag_strength_reduce;
++
++/* Nonzero enables loop unrolling in unroll.c.  Only loops for which the
++   number of iterations can be calculated at compile-time (UNROLL_COMPLETELY,
++   UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are
++   unrolled.  */
++
++extern int flag_unroll_loops;
++
++/* Nonzero enables loop unrolling in unroll.c.  All loops are unrolled.
++   This is generally not a win.  */
++
++extern int flag_unroll_all_loops;
++
++/* Nonzero forces all invariant computations in loops to be moved
++   outside the loop.  */
++
++extern int flag_move_all_movables;
++
++/* Nonzero enables prefetch optimizations for arrays in loops.  */
++
++extern int flag_prefetch_loop_arrays;
++
++/* Nonzero forces all general induction variables in loops to be
++   strength reduced.  */
++
++extern int flag_reduce_all_givs;
++
++/* Nonzero for -fcse-follow-jumps:
++   have cse follow jumps to do a more extensive job.  */
++
++extern int flag_cse_follow_jumps;
++
++/* Nonzero for -fcse-skip-blocks:
++   have cse follow a branch around a block.  */
++
++extern int flag_cse_skip_blocks;
++
++/* Nonzero for -fexpensive-optimizations:
++   perform miscellaneous relatively-expensive optimizations.  */
++extern int flag_expensive_optimizations;
++
++/* Nonzero for -fwritable-strings:
++   store string constants in data segment and don't uniquize them.  */
++
++extern int flag_writable_strings;
++
++/* Nonzero means don't put addresses of constant functions in registers.
++   Used for compiling the Unix kernel, where strange substitutions are
++   done on the assembly output.  */
++
++extern int flag_no_function_cse;
++
++/* Nonzero for -fomit-frame-pointer:
++   don't make a frame pointer in simple functions that don't require one.  */
++
++extern int flag_omit_frame_pointer;
++
++/* Nonzero to inhibit use of define_optimization peephole opts.  */
++
++extern int flag_no_peephole;
++
++/* Nonzero means all references through pointers are volatile.  */
++
++extern int flag_volatile;
++
++/* Nonzero means treat all global and extern variables as volatile.  */
++
++extern int flag_volatile_global;
++
++/* Nonzero means treat all static variables as volatile.  */
++
++extern int flag_volatile_static;
++
++/* Nonzero allows GCC to optimize sibling and tail recursive calls.  */
++
++extern int flag_optimize_sibling_calls;
++
++/* Nonzero means the front end generally wants `errno' maintained by math
++   operations, like built-in SQRT.  */
++
++extern int flag_errno_math;
++
++/* Nonzero means that unsafe floating-point math optimizations are allowed
++   for the sake of speed.  IEEE compliance is not guaranteed, and operations
++   are allowed to assume that their arguments and results are "normal"
++   (e.g., nonnegative for SQRT).  */
++
++extern int flag_unsafe_math_optimizations;
++
++/* Nonzero means that no NaNs or +-Infs are expected.  */
++
++extern int flag_finite_math_only;
++
++/* Zero means that floating-point math operations cannot generate a
++   (user-visible) trap.  This is the case, for example, in nonstop
++   IEEE 754 arithmetic.  */
++
++extern int flag_trapping_math;
++
++/* 0 means straightforward implementation of complex divide acceptable.
++   1 means wide ranges of inputs must work for complex divide.
++   2 means C99-like requirements for complex divide (not yet implemented).  */
++
++extern int flag_complex_divide_method;
++
++/* Nonzero means to run loop optimizations twice.  */
++
++extern int flag_rerun_loop_opt;
++
++/* Nonzero means make functions that look like good inline candidates
++   go inline.  */
++
++extern int flag_inline_functions;
++
++/* Nonzero for -fkeep-inline-functions: even if we make a function
++   go inline everywhere, keep its definition around for debugging
++   purposes.  */
++
++extern int flag_keep_inline_functions;
++
++/* Nonzero means that functions declared `inline' will be treated
++   as `static'.  Prevents generation of zillions of copies of unused
++   static inline functions; instead, `inlines' are written out
++   only when actually used.  Used in conjunction with -g.  Also
++   does the right thing with #pragma interface.  */
++
++extern int flag_no_inline;
++
++/* Nonzero means that we don't want inlining by virtue of -fno-inline,
++   not just because the tree inliner turned us off.  */
++
++extern int flag_really_no_inline;
++
++/* Nonzero if we are only using compiler to check syntax errors.  */
++
++extern int flag_syntax_only;
++
++/* Nonzero means we should save auxiliary info into a .X file.  */
++
++extern int flag_gen_aux_info;
++
++/* Nonzero means make the text shared if supported.  */
++
++extern int flag_shared_data;
++
++/* flag_schedule_insns means schedule insns within basic blocks (before
++   local_alloc).
++   flag_schedule_insns_after_reload means schedule insns after
++   global_alloc.  */
++
++extern int flag_schedule_insns;
++extern int flag_schedule_insns_after_reload;
++
++/* The following flags have effect only for scheduling before register
++   allocation:
++
++   flag_schedule_interblock means schedule insns across basic blocks.
++   flag_schedule_speculative means allow speculative motion of non-load insns.
++   flag_schedule_speculative_load means allow speculative motion of some
++   load insns.
++   flag_schedule_speculative_load_dangerous allows speculative motion of more
++   load insns.  */
++
++extern int flag_schedule_interblock;
++extern int flag_schedule_speculative;
++extern int flag_schedule_speculative_load;
++extern int flag_schedule_speculative_load_dangerous;
++
++/* flag_branch_on_count_reg means try to replace add-1,compare,branch tupple
++   by a cheaper branch, on a count register.  */
++extern int flag_branch_on_count_reg;
++
++/* This option is set to 1 on -fsingle-precision-constant option which is
++   used to convert the floating point constants to single precision 
++   constants.  */
++
++extern int flag_single_precision_constant;
++
++/* Nonzero means put things in delayed-branch slots if supported.  */
++
++extern int flag_delayed_branch;
++
++/* Nonzero means suppress output of instruction numbers and line number
++   notes in debugging dumps.  */
++
++extern int flag_dump_unnumbered;
++
++/* Nonzero means change certain warnings into errors.
++   Usually these are warnings about failure to conform to some standard.  */
++
++extern int flag_pedantic_errors;
++
++/* Nonzero means generate position-independent code.  1 vs 2 for a 
++   target-dependent "small" or "large" mode.  */
++
++extern int flag_pic;
++
++/* Nonzero means generate extra code for exception handling and enable
++   exception handling.  */
++
++extern int flag_exceptions;
++
++/* Nonzero means generate frame unwind info table when supported */
++
++extern int flag_unwind_tables;
++
++/* Nonzero means generate frame unwind info table exact at each insn boundary */
++
++extern int flag_asynchronous_unwind_tables;
++
++/* Nonzero means don't place uninitialized global data in common storage
++   by default.  */
++
++extern int flag_no_common;
++
++/* -finhibit-size-directive inhibits output of .size for ELF.
++   This is used only for compiling crtstuff.c,
++   and it may be extended to other effects
++   needed for crtstuff.c on other systems.  */
++extern int flag_inhibit_size_directive;
++
++/* Nonzero means place each function into its own section on those platforms
++   which support arbitrary section names and unlimited numbers of sections.  */
++
++extern int flag_function_sections;
++
++/* ... and similar for data.  */
++ 
++extern int flag_data_sections;
++
++/* -fverbose-asm causes extra commentary information to be produced in
++   the generated assembly code (to make it more readable).  This option
++   is generally only of use to those who actually need to read the
++   generated assembly code (perhaps while debugging the compiler itself).
++   -fno-verbose-asm, the default, causes the extra information
++   to not be added and is useful when comparing two assembler files.  */
++
++extern int flag_verbose_asm;
++
++/* -dA causes debug information to be produced in
++   the generated assembly code (to make it more readable).  This option
++   is generally only of use to those who actually need to read the
++   generated assembly code (perhaps while debugging the compiler itself).
++   Currently, this switch is only used by dwarfout.c; however, it is intended
++   to be a catchall for printing debug information in the assembler file.  */
++
++extern int flag_debug_asm;
++
++extern int flag_dump_rtl_in_asm;
++
++/* -fgnu-linker specifies use of the GNU linker for initializations.
++   -fno-gnu-linker says that collect will be used.  */
++extern int flag_gnu_linker;
++
++/* Tag all structures with __attribute__(packed) */
++extern int flag_pack_struct;
++
++/* This flag is only tested if alias checking is enabled.
++   0 if pointer arguments may alias each other.  True in C.
++   1 if pointer arguments may not alias each other but may alias
++   global variables.
++   2 if pointer arguments may not alias each other and may not
++   alias global variables.  True in Fortran.
++   The value is ignored if flag_alias_check is 0.  */
++extern int flag_argument_noalias;
++
++/* Nonzero if we should do (language-dependent) alias analysis.
++   Typically, this analysis will assume that expressions of certain
++   types do not alias expressions of certain other types.  Only used
++   if alias analysis (in general) is enabled.  */
++extern int flag_strict_aliasing;
++
++/* Emit code to probe the stack, to help detect stack overflow; also
++   may cause large objects to be allocated dynamically.  */
++extern int flag_stack_check;
++
++/* Do the full regmove optimization pass.  */
++extern int flag_regmove;
++
++/* Instrument functions with calls at entry and exit, for profiling.  */
++extern int flag_instrument_function_entry_exit;
++
++/* Perform a peephole pass before sched2.  */
++extern int flag_peephole2;
++
++/* Try to guess branch probablities.  */
++extern int flag_guess_branch_prob;
++
++/* -fcheck-bounds causes gcc to generate array bounds checks.
++   For C, C++ and ObjC: defaults off.
++   For Java: defaults to on.
++   For Fortran: defaults to off.  */
++extern int flag_bounds_check;
++
++/* This will attempt to merge constant section constants, if 1 only
++   string constants and constants from constant pool, if 2 also constant
++   variables.  */
++extern int flag_merge_constants;
++
++/* If one, renumber instruction UIDs to reduce the number of
++   unused UIDs if there are a lot of instructions.  If greater than
++   one, unconditionally renumber instruction UIDs.  */
++extern int flag_renumber_insns;
++\f
++/* Other basic status info about current function.  */
++
++/* Nonzero means current function must be given a frame pointer.
++   Set in stmt.c if anything is allocated on the stack there.
++   Set in reload1.c if anything is allocated on the stack there.  */
++
++extern int frame_pointer_needed;
++
++/* Nonzero if the generated code should trap on signed overflow
++   for PLUS / SUB / MULT.  */
++extern int flag_trapv;
++
++/* Value of the -G xx switch, and whether it was passed or not.  */
++extern int g_switch_value;
++extern int g_switch_set;
++
++/* Values of the -falign-* flags: how much to align labels in code. 
++   0 means `use default', 1 means `don't align'.  
++   For each variable, there is an _log variant which is the power
++   of two not less than the variable, for .align output.  */
++
++extern int align_loops;
++extern int align_loops_log;
++extern int align_loops_max_skip;
++extern int align_jumps;
++extern int align_jumps_log;
++extern int align_jumps_max_skip;
++extern int align_labels;
++extern int align_labels_log;
++extern int align_labels_max_skip;
++extern int align_functions;
++extern int align_functions_log;
++
++/* Like align_functions_log above, but used by front-ends to force the
++   minimum function alignment.  Zero means no alignment is forced.  */
++extern int force_align_functions_log;
++
++/* Nonzero if we dump in VCG format, not plain text.  */
++extern int dump_for_graph;
++
++/* Selection of the graph form.  */
++enum graph_dump_types
++{
++  no_graph = 0,
++  vcg
++};
++extern enum graph_dump_types graph_dump_format;
++
++/* Nonzero means ignore `#ident' directives.  0 means handle them.
++   On SVR4 targets, it also controls whether or not to emit a
++   string identifying the compiler.  */
++
++extern int flag_no_ident;
++
++/* Nonzero if we want to perform enhanced load motion during gcse.  */
++
++extern int flag_gcse_lm;
++
++/* Nonzero if we want to perform store motion after gcse.  */
++
++extern int flag_gcse_sm;
++
++
++/* Nonzero means we should do dwarf2 duplicate elimination.  */
++
++extern int flag_eliminate_dwarf2_dups;
++
++/* Nonzero means to collect statistics which might be expensive
++   and to print them when we are done.  */
++extern int flag_detailed_statistics;
++
++/* Nonzero means enable synchronous exceptions for non-call instructions.  */
++extern int flag_non_call_exceptions;
++
++/* Nonzero means put zero initialized data in the bss section.  */
++extern int flag_zero_initialized_in_bss;
++
++/* Nonzero means disable transformations observable by signaling NaNs.  */
++extern int flag_signaling_nans;
++
++/* A string that's used when a random name is required.  NULL means
++   to make it really random.  */
++
++extern const char *flag_random_seed;
++
++/* True if the given mode has a NaN representation and the treatment of
++   NaN operands is important.  Certain optimizations, such as folding
++   x * 0 into x, are not correct for NaN operands, and are normally
++   disabled for modes with NaNs.  The user can ask for them to be
++   done anyway using the -funsafe-math-optimizations switch.  */
++#define HONOR_NANS(MODE) \
++  (MODE_HAS_NANS (MODE) && !flag_finite_math_only)
++
++/* Like HONOR_NANs, but true if we honor signaling NaNs (or sNaNs).  */
++#define HONOR_SNANS(MODE) (flag_signaling_nans && HONOR_NANS (MODE))
++
++/* As for HONOR_NANS, but true if the mode can represent infinity and
++   the treatment of infinite values is important.  */
++#define HONOR_INFINITIES(MODE) \
++  (MODE_HAS_INFINITIES (MODE) && !flag_finite_math_only)
++
++/* Like HONOR_NANS, but true if the given mode distinguishes between
++   postive and negative zero, and the sign of zero is important.  */
++#define HONOR_SIGNED_ZEROS(MODE) \
++  (MODE_HAS_SIGNED_ZEROS (MODE) && !flag_unsafe_math_optimizations)
++
++/* Like HONOR_NANS, but true if given mode supports sign-dependent rounding,
++   and the rounding mode is important.  */
++#define HONOR_SIGN_DEPENDENT_ROUNDING(MODE) \
++  (MODE_HAS_SIGN_DEPENDENT_ROUNDING (MODE) && !flag_unsafe_math_optimizations)
++
++#endif /* ! GCC_FLAGS_H */
+diff -ruN gcc-3.3.1/gcc/function.c gcc-3.3.1.pp/gcc/function.c
+--- gcc-3.3.1/gcc/function.c   2003-04-10 22:26:04.000000000 +0000
++++ gcc-3.3.1.pp/gcc/function.c        2003-09-05 11:58:59.000000000 +0000
+@@ -59,6 +59,7 @@
+ #include "tm_p.h"
+ #include "integrate.h"
+ #include "langhooks.h"
++#include "protector.h"
+ #ifndef TRAMPOLINE_ALIGNMENT
+ #define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
+@@ -142,6 +143,10 @@
+ /* Array of INSN_UIDs to hold the INSN_UIDs for each sibcall epilogue
+    in this function.  */
+ static GTY(()) varray_type sibcall_epilogue;
++
++/* Current boundary mark for character arrays.  */
++int temp_boundary_mark = 0;
++
\f
+ /* In order to evaluate some expressions, such as function calls returning
+    structures in memory, we need to temporarily allocate stack locations.
+@@ -195,6 +200,8 @@
+   /* The size of the slot, including extra space for alignment.  This
+      info is for combine_temp_slots.  */
+   HOST_WIDE_INT full_size;
++  /* Boundary mark of a character array and the others. This info is for propolice */
++  int boundary_mark;
+ };
\f
+ /* This structure is used to record MEMs or pseudos used to replace VAR, any
+@@ -629,6 +636,7 @@
+    whose lifetime is controlled by CLEANUP_POINT_EXPRs.  KEEP is 3
+    if we are to allocate something at an inner level to be treated as
+    a variable in the block (e.g., a SAVE_EXPR).
++   KEEP is 5 if we allocate a place to return structure.
+    TYPE is the type that will be used for the stack slot.  */
+@@ -642,6 +650,8 @@
+   unsigned int align;
+   struct temp_slot *p, *best_p = 0;
+   rtx slot;
++  int char_array = (flag_propolice_protection
++                  && keep == 1 && search_string_def (type));
+   /* If SIZE is -1 it means that somebody tried to allocate a temporary
+      of a variable size.  */
+@@ -667,7 +677,8 @@
+       && ! p->in_use
+       && objects_must_conflict_p (p->type, type)
+       && (best_p == 0 || best_p->size > p->size
+-          || (best_p->size == p->size && best_p->align > p->align)))
++          || (best_p->size == p->size && best_p->align > p->align))
++      && (! char_array || p->boundary_mark != 0))
+       {
+       if (p->align == align && p->size == size)
+         {
+@@ -702,6 +713,7 @@
+             p->address = 0;
+             p->rtl_expr = 0;
+             p->type = best_p->type;
++            p->boundary_mark = best_p->boundary_mark;
+             p->next = temp_slots;
+             temp_slots = p;
+@@ -762,6 +774,7 @@
+       p->full_size = frame_offset - frame_offset_old;
+ #endif
+       p->address = 0;
++      p->boundary_mark = char_array?++temp_boundary_mark:0;
+       p->next = temp_slots;
+       temp_slots = p;
+     }
+@@ -932,14 +945,16 @@
+           int delete_q = 0;
+           if (! q->in_use && GET_MODE (q->slot) == BLKmode)
+             {
+-              if (p->base_offset + p->full_size == q->base_offset)
++              if (p->base_offset + p->full_size == q->base_offset &&
++                  p->boundary_mark == q->boundary_mark)
+                 {
+                   /* Q comes after P; combine Q into P.  */
+                   p->size += q->size;
+                   p->full_size += q->full_size;
+                   delete_q = 1;
+                 }
+-              else if (q->base_offset + q->full_size == p->base_offset)
++              else if (q->base_offset + q->full_size == p->base_offset &&
++                       p->boundary_mark == q->boundary_mark)
+                 {
+                   /* P comes after Q; combine P into Q.  */
+                   q->size += p->size;
+@@ -1497,7 +1512,9 @@
+     new = func->x_parm_reg_stack_loc[regno];
+   if (new == 0)
+-    new = assign_stack_local_1 (decl_mode, GET_MODE_SIZE (decl_mode), 0, func);
++    new = function ?
++      assign_stack_local_1 (decl_mode, GET_MODE_SIZE (decl_mode), 0, func):
++      assign_stack_local_for_pseudo_reg (decl_mode, GET_MODE_SIZE (decl_mode), 0);
+   PUT_CODE (reg, MEM);
+   PUT_MODE (reg, decl_mode);
+@@ -3961,7 +3978,8 @@
+                constant with that register.  */
+             temp = gen_reg_rtx (Pmode);
+             XEXP (x, 0) = new;
+-            if (validate_change (object, &XEXP (x, 1), temp, 0))
++            if (validate_change (object, &XEXP (x, 1), temp, 0)
++                && ! flag_propolice_protection)
+               emit_insn_before (gen_move_insn (temp, new_offset), object);
+             else
+               {
+diff -ruN gcc-3.3.1/gcc/gcse.c gcc-3.3.1.pp/gcc/gcse.c
+--- gcc-3.3.1/gcc/gcse.c       2003-07-14 09:21:22.000000000 +0000
++++ gcc-3.3.1.pp/gcc/gcse.c    2003-09-05 11:58:59.000000000 +0000
+@@ -4211,7 +4211,7 @@
+       /* Find an assignment that sets reg_used and is available
+        at the start of the block.  */
+       set = find_avail_set (regno, insn);
+-      if (! set)
++      if (! set || set->expr->volatil)
+       continue;
+       pat = set->expr;
+diff -ruN gcc-3.3.1/gcc/gcse.c.orig gcc-3.3.1.pp/gcc/gcse.c.orig
+--- gcc-3.3.1/gcc/gcse.c.orig  1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/gcse.c.orig       2003-07-14 09:21:22.000000000 +0000
+@@ -0,0 +1,7477 @@
++/* Global common subexpression elimination/Partial redundancy elimination
++   and global constant/copy propagation for GNU compiler.
++   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
++   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.  */
++
++/* TODO
++   - reordering of memory allocation and freeing to be more space efficient
++   - do rough calc of how many regs are needed in each block, and a rough
++     calc of how many regs are available in each class and use that to
++     throttle back the code in cases where RTX_COST is minimal.
++   - a store to the same address as a load does not kill the load if the
++     source of the store is also the destination of the load.  Handling this
++     allows more load motion, particularly out of loops.
++   - ability to realloc sbitmap vectors would allow one initial computation
++     of reg_set_in_block with only subsequent additions, rather than
++     recomputing it for each pass
++
++*/
++
++/* References searched while implementing this.
++
++   Compilers Principles, Techniques and Tools
++   Aho, Sethi, Ullman
++   Addison-Wesley, 1988
++
++   Global Optimization by Suppression of Partial Redundancies
++   E. Morel, C. Renvoise
++   communications of the acm, Vol. 22, Num. 2, Feb. 1979
++
++   A Portable Machine-Independent Global Optimizer - Design and Measurements
++   Frederick Chow
++   Stanford Ph.D. thesis, Dec. 1983
++
++   A Fast Algorithm for Code Movement Optimization
++   D.M. Dhamdhere
++   SIGPLAN Notices, Vol. 23, Num. 10, Oct. 1988
++
++   A Solution to a Problem with Morel and Renvoise's
++   Global Optimization by Suppression of Partial Redundancies
++   K-H Drechsler, M.P. Stadel
++   ACM TOPLAS, Vol. 10, Num. 4, Oct. 1988
++
++   Practical Adaptation of the Global Optimization
++   Algorithm of Morel and Renvoise
++   D.M. Dhamdhere
++   ACM TOPLAS, Vol. 13, Num. 2. Apr. 1991
++
++   Efficiently Computing Static Single Assignment Form and the Control
++   Dependence Graph
++   R. Cytron, J. Ferrante, B.K. Rosen, M.N. Wegman, and F.K. Zadeck
++   ACM TOPLAS, Vol. 13, Num. 4, Oct. 1991
++
++   Lazy Code Motion
++   J. Knoop, O. Ruthing, B. Steffen
++   ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
++
++   What's In a Region?  Or Computing Control Dependence Regions in Near-Linear
++   Time for Reducible Flow Control
++   Thomas Ball
++   ACM Letters on Programming Languages and Systems,
++   Vol. 2, Num. 1-4, Mar-Dec 1993
++
++   An Efficient Representation for Sparse Sets
++   Preston Briggs, Linda Torczon
++   ACM Letters on Programming Languages and Systems,
++   Vol. 2, Num. 1-4, Mar-Dec 1993
++
++   A Variation of Knoop, Ruthing, and Steffen's Lazy Code Motion
++   K-H Drechsler, M.P. Stadel
++   ACM SIGPLAN Notices, Vol. 28, Num. 5, May 1993
++
++   Partial Dead Code Elimination
++   J. Knoop, O. Ruthing, B. Steffen
++   ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
++
++   Effective Partial Redundancy Elimination
++   P. Briggs, K.D. Cooper
++   ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
++
++   The Program Structure Tree: Computing Control Regions in Linear Time
++   R. Johnson, D. Pearson, K. Pingali
++   ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
++
++   Optimal Code Motion: Theory and Practice
++   J. Knoop, O. Ruthing, B. Steffen
++   ACM TOPLAS, Vol. 16, Num. 4, Jul. 1994
++
++   The power of assignment motion
++   J. Knoop, O. Ruthing, B. Steffen
++   ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
++
++   Global code motion / global value numbering
++   C. Click
++   ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
++
++   Value Driven Redundancy Elimination
++   L.T. Simpson
++   Rice University Ph.D. thesis, Apr. 1996
++
++   Value Numbering
++   L.T. Simpson
++   Massively Scalar Compiler Project, Rice University, Sep. 1996
++
++   High Performance Compilers for Parallel Computing
++   Michael Wolfe
++   Addison-Wesley, 1996
++
++   Advanced Compiler Design and Implementation
++   Steven Muchnick
++   Morgan Kaufmann, 1997
++
++   Building an Optimizing Compiler
++   Robert Morgan
++   Digital Press, 1998
++
++   People wishing to speed up the code here should read:
++     Elimination Algorithms for Data Flow Analysis
++     B.G. Ryder, M.C. Paull
++     ACM Computing Surveys, Vol. 18, Num. 3, Sep. 1986
++
++     How to Analyze Large Programs Efficiently and Informatively
++     D.M. Dhamdhere, B.K. Rosen, F.K. Zadeck
++     ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
++
++   People wishing to do something different can find various possibilities
++   in the above papers and elsewhere.
++*/
++
++#include "config.h"
++#include "system.h"
++#include "toplev.h"
++
++#include "rtl.h"
++#include "tm_p.h"
++#include "regs.h"
++#include "hard-reg-set.h"
++#include "flags.h"
++#include "real.h"
++#include "insn-config.h"
++#include "recog.h"
++#include "basic-block.h"
++#include "output.h"
++#include "function.h"
++#include "expr.h"
++#include "except.h"
++#include "ggc.h"
++#include "params.h"
++#include "cselib.h"
++
++#include "obstack.h"
++
++/* Propagate flow information through back edges and thus enable PRE's
++   moving loop invariant calculations out of loops.
++
++   Originally this tended to create worse overall code, but several
++   improvements during the development of PRE seem to have made following
++   back edges generally a win.
++
++   Note much of the loop invariant code motion done here would normally
++   be done by loop.c, which has more heuristics for when to move invariants
++   out of loops.  At some point we might need to move some of those
++   heuristics into gcse.c.  */
++
++/* We support GCSE via Partial Redundancy Elimination.  PRE optimizations
++   are a superset of those done by GCSE.
++
++   We perform the following steps:
++
++   1) Compute basic block information.
++
++   2) Compute table of places where registers are set.
++
++   3) Perform copy/constant propagation.
++
++   4) Perform global cse.
++
++   5) Perform another pass of copy/constant propagation.
++
++   Two passes of copy/constant propagation are done because the first one
++   enables more GCSE and the second one helps to clean up the copies that
++   GCSE creates.  This is needed more for PRE than for Classic because Classic
++   GCSE will try to use an existing register containing the common
++   subexpression rather than create a new one.  This is harder to do for PRE
++   because of the code motion (which Classic GCSE doesn't do).
++
++   Expressions we are interested in GCSE-ing are of the form
++   (set (pseudo-reg) (expression)).
++   Function want_to_gcse_p says what these are.
++
++   PRE handles moving invariant expressions out of loops (by treating them as
++   partially redundant).
++
++   Eventually it would be nice to replace cse.c/gcse.c with SSA (static single
++   assignment) based GVN (global value numbering).  L. T. Simpson's paper
++   (Rice University) on value numbering is a useful reference for this.
++
++   **********************
++
++   We used to support multiple passes but there are diminishing returns in
++   doing so.  The first pass usually makes 90% of the changes that are doable.
++   A second pass can make a few more changes made possible by the first pass.
++   Experiments show any further passes don't make enough changes to justify
++   the expense.
++
++   A study of spec92 using an unlimited number of passes:
++   [1 pass] = 1208 substitutions, [2] = 577, [3] = 202, [4] = 192, [5] = 83,
++   [6] = 34, [7] = 17, [8] = 9, [9] = 4, [10] = 4, [11] = 2,
++   [12] = 2, [13] = 1, [15] = 1, [16] = 2, [41] = 1
++
++   It was found doing copy propagation between each pass enables further
++   substitutions.
++
++   PRE is quite expensive in complicated functions because the DFA can take
++   awhile to converge.  Hence we only perform one pass.  The parameter max-gcse-passes can
++   be modified if one wants to experiment.
++
++   **********************
++
++   The steps for PRE are:
++
++   1) Build the hash table of expressions we wish to GCSE (expr_hash_table).
++
++   2) Perform the data flow analysis for PRE.
++
++   3) Delete the redundant instructions
++
++   4) Insert the required copies [if any] that make the partially
++      redundant instructions fully redundant.
++
++   5) For other reaching expressions, insert an instruction to copy the value
++      to a newly created pseudo that will reach the redundant instruction.
++
++   The deletion is done first so that when we do insertions we
++   know which pseudo reg to use.
++
++   Various papers have argued that PRE DFA is expensive (O(n^2)) and others
++   argue it is not.  The number of iterations for the algorithm to converge
++   is typically 2-4 so I don't view it as that expensive (relatively speaking).
++
++   PRE GCSE depends heavily on the second CSE pass to clean up the copies
++   we create.  To make an expression reach the place where it's redundant,
++   the result of the expression is copied to a new register, and the redundant
++   expression is deleted by replacing it with this new register.  Classic GCSE
++   doesn't have this problem as much as it computes the reaching defs of
++   each register in each block and thus can try to use an existing register.
++
++   **********************
++
++   A fair bit of simplicity is created by creating small functions for simple
++   tasks, even when the function is only called in one place.  This may
++   measurably slow things down [or may not] by creating more function call
++   overhead than is necessary.  The source is laid out so that it's trivial
++   to make the affected functions inline so that one can measure what speed
++   up, if any, can be achieved, and maybe later when things settle things can
++   be rearranged.
++
++   Help stamp out big monolithic functions!  */
++\f
++/* GCSE global vars.  */
++
++/* -dG dump file.  */
++static FILE *gcse_file;
++
++/* Note whether or not we should run jump optimization after gcse.  We
++   want to do this for two cases.
++
++    * If we changed any jumps via cprop.
++
++    * If we added any labels via edge splitting.  */
++
++static int run_jump_opt_after_gcse;
++
++/* Bitmaps are normally not included in debugging dumps.
++   However it's useful to be able to print them from GDB.
++   We could create special functions for this, but it's simpler to
++   just allow passing stderr to the dump_foo fns.  Since stderr can
++   be a macro, we store a copy here.  */
++static FILE *debug_stderr;
++
++/* An obstack for our working variables.  */
++static struct obstack gcse_obstack;
++
++/* Nonzero for each mode that supports (set (reg) (reg)).
++   This is trivially true for integer and floating point values.
++   It may or may not be true for condition codes.  */
++static char can_copy_p[(int) NUM_MACHINE_MODES];
++
++/* Nonzero if can_copy_p has been initialized.  */
++static int can_copy_init_p;
++
++struct reg_use {rtx reg_rtx; };
++
++/* Hash table of expressions.  */
++
++struct expr
++{
++  /* The expression (SET_SRC for expressions, PATTERN for assignments).  */
++  rtx expr;
++  /* Index in the available expression bitmaps.  */
++  int bitmap_index;
++  /* Next entry with the same hash.  */
++  struct expr *next_same_hash;
++  /* List of anticipatable occurrences in basic blocks in the function.
++     An "anticipatable occurrence" is one that is the first occurrence in the
++     basic block, the operands are not modified in the basic block prior
++     to the occurrence and the output is not used between the start of
++     the block and the occurrence.  */
++  struct occr *antic_occr;
++  /* List of available occurrence in basic blocks in the function.
++     An "available occurrence" is one that is the last occurrence in the
++     basic block and the operands are not modified by following statements in
++     the basic block [including this insn].  */
++  struct occr *avail_occr;
++  /* Non-null if the computation is PRE redundant.
++     The value is the newly created pseudo-reg to record a copy of the
++     expression in all the places that reach the redundant copy.  */
++  rtx reaching_reg;
++};
++
++/* Occurrence of an expression.
++   There is one per basic block.  If a pattern appears more than once the
++   last appearance is used [or first for anticipatable expressions].  */
++
++struct occr
++{
++  /* Next occurrence of this expression.  */
++  struct occr *next;
++  /* The insn that computes the expression.  */
++  rtx insn;
++  /* Nonzero if this [anticipatable] occurrence has been deleted.  */
++  char deleted_p;
++  /* Nonzero if this [available] occurrence has been copied to
++     reaching_reg.  */
++  /* ??? This is mutually exclusive with deleted_p, so they could share
++     the same byte.  */
++  char copied_p;
++};
++
++/* Expression and copy propagation hash tables.
++   Each hash table is an array of buckets.
++   ??? It is known that if it were an array of entries, structure elements
++   `next_same_hash' and `bitmap_index' wouldn't be necessary.  However, it is
++   not clear whether in the final analysis a sufficient amount of memory would
++   be saved as the size of the available expression bitmaps would be larger
++   [one could build a mapping table without holes afterwards though].
++   Someday I'll perform the computation and figure it out.  */
++
++struct hash_table
++{
++  /* The table itself.
++     This is an array of `expr_hash_table_size' elements.  */
++  struct expr **table;
++
++  /* Size of the hash table, in elements.  */
++  unsigned int size;
++
++  /* Number of hash table elements.  */
++  unsigned int n_elems;
++
++  /* Whether the table is expression of copy propagation one.  */
++  int set_p;
++};
++
++/* Expression hash table.  */
++static struct hash_table expr_hash_table;
++
++/* Copy propagation hash table.  */
++static struct hash_table set_hash_table;
++
++/* Mapping of uids to cuids.
++   Only real insns get cuids.  */
++static int *uid_cuid;
++
++/* Highest UID in UID_CUID.  */
++static int max_uid;
++
++/* Get the cuid of an insn.  */
++#ifdef ENABLE_CHECKING
++#define INSN_CUID(INSN) (INSN_UID (INSN) > max_uid ? (abort (), 0) : uid_cuid[INSN_UID (INSN)])
++#else
++#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
++#endif
++
++/* Number of cuids.  */
++static int max_cuid;
++
++/* Mapping of cuids to insns.  */
++static rtx *cuid_insn;
++
++/* Get insn from cuid.  */
++#define CUID_INSN(CUID) (cuid_insn[CUID])
++
++/* Maximum register number in function prior to doing gcse + 1.
++   Registers created during this pass have regno >= max_gcse_regno.
++   This is named with "gcse" to not collide with global of same name.  */
++static unsigned int max_gcse_regno;
++
++/* Table of registers that are modified.
++
++   For each register, each element is a list of places where the pseudo-reg
++   is set.
++
++   For simplicity, GCSE is done on sets of pseudo-regs only.  PRE GCSE only
++   requires knowledge of which blocks kill which regs [and thus could use
++   a bitmap instead of the lists `reg_set_table' uses].
++
++   `reg_set_table' and could be turned into an array of bitmaps (num-bbs x
++   num-regs) [however perhaps it may be useful to keep the data as is].  One
++   advantage of recording things this way is that `reg_set_table' is fairly
++   sparse with respect to pseudo regs but for hard regs could be fairly dense
++   [relatively speaking].  And recording sets of pseudo-regs in lists speeds
++   up functions like compute_transp since in the case of pseudo-regs we only
++   need to iterate over the number of times a pseudo-reg is set, not over the
++   number of basic blocks [clearly there is a bit of a slow down in the cases
++   where a pseudo is set more than once in a block, however it is believed
++   that the net effect is to speed things up].  This isn't done for hard-regs
++   because recording call-clobbered hard-regs in `reg_set_table' at each
++   function call can consume a fair bit of memory, and iterating over
++   hard-regs stored this way in compute_transp will be more expensive.  */
++
++typedef struct reg_set
++{
++  /* The next setting of this register.  */
++  struct reg_set *next;
++  /* The insn where it was set.  */
++  rtx insn;
++} reg_set;
++
++static reg_set **reg_set_table;
++
++/* Size of `reg_set_table'.
++   The table starts out at max_gcse_regno + slop, and is enlarged as
++   necessary.  */
++static int reg_set_table_size;
++
++/* Amount to grow `reg_set_table' by when it's full.  */
++#define REG_SET_TABLE_SLOP 100
++
++/* This is a list of expressions which are MEMs and will be used by load
++   or store motion.
++   Load motion tracks MEMs which aren't killed by
++   anything except itself. (ie, loads and stores to a single location).
++   We can then allow movement of these MEM refs with a little special
++   allowance. (all stores copy the same value to the reaching reg used
++   for the loads).  This means all values used to store into memory must have
++   no side effects so we can re-issue the setter value.
++   Store Motion uses this structure as an expression table to track stores
++   which look interesting, and might be moveable towards the exit block.  */
++
++struct ls_expr
++{
++  struct expr * expr;         /* Gcse expression reference for LM.  */
++  rtx pattern;                        /* Pattern of this mem.  */
++  rtx loads;                  /* INSN list of loads seen.  */
++  rtx stores;                 /* INSN list of stores seen.  */
++  struct ls_expr * next;      /* Next in the list.  */
++  int invalid;                        /* Invalid for some reason.  */
++  int index;                  /* If it maps to a bitmap index.  */
++  int hash_index;             /* Index when in a hash table.  */
++  rtx reaching_reg;           /* Register to use when re-writing.  */
++};
++
++/* Head of the list of load/store memory refs.  */
++static struct ls_expr * pre_ldst_mems = NULL;
++
++/* Bitmap containing one bit for each register in the program.
++   Used when performing GCSE to track which registers have been set since
++   the start of the basic block.  */
++static regset reg_set_bitmap;
++
++/* For each block, a bitmap of registers set in the block.
++   This is used by expr_killed_p and compute_transp.
++   It is computed during hash table computation and not by compute_sets
++   as it includes registers added since the last pass (or between cprop and
++   gcse) and it's currently not easy to realloc sbitmap vectors.  */
++static sbitmap *reg_set_in_block;
++
++/* Array, indexed by basic block number for a list of insns which modify
++   memory within that block.  */
++static rtx * modify_mem_list;
++bitmap modify_mem_list_set;
++
++/* This array parallels modify_mem_list, but is kept canonicalized.  */
++static rtx * canon_modify_mem_list;
++bitmap canon_modify_mem_list_set;
++/* Various variables for statistics gathering.  */
++
++/* Memory used in a pass.
++   This isn't intended to be absolutely precise.  Its intent is only
++   to keep an eye on memory usage.  */
++static int bytes_used;
++
++/* GCSE substitutions made.  */
++static int gcse_subst_count;
++/* Number of copy instructions created.  */
++static int gcse_create_count;
++/* Number of constants propagated.  */
++static int const_prop_count;
++/* Number of copys propagated.  */
++static int copy_prop_count;
++\f
++/* These variables are used by classic GCSE.
++   Normally they'd be defined a bit later, but `rd_gen' needs to
++   be declared sooner.  */
++
++/* Each block has a bitmap of each type.
++   The length of each blocks bitmap is:
++
++       max_cuid  - for reaching definitions
++       n_exprs - for available expressions
++
++   Thus we view the bitmaps as 2 dimensional arrays.  i.e.
++   rd_kill[block_num][cuid_num]
++   ae_kill[block_num][expr_num]                        */
++
++/* For reaching defs */
++static sbitmap *rd_kill, *rd_gen, *reaching_defs, *rd_out;
++
++/* for available exprs */
++static sbitmap *ae_kill, *ae_gen, *ae_in, *ae_out;
++
++/* Objects of this type are passed around by the null-pointer check
++   removal routines.  */
++struct null_pointer_info
++{
++  /* The basic block being processed.  */
++  basic_block current_block;
++  /* The first register to be handled in this pass.  */
++  unsigned int min_reg;
++  /* One greater than the last register to be handled in this pass.  */
++  unsigned int max_reg;
++  sbitmap *nonnull_local;
++  sbitmap *nonnull_killed;
++};
++\f
++static void compute_can_copy  PARAMS ((void));
++static char *gmalloc          PARAMS ((unsigned int));
++static char *grealloc         PARAMS ((char *, unsigned int));
++static char *gcse_alloc               PARAMS ((unsigned long));
++static void alloc_gcse_mem    PARAMS ((rtx));
++static void free_gcse_mem     PARAMS ((void));
++static void alloc_reg_set_mem PARAMS ((int));
++static void free_reg_set_mem  PARAMS ((void));
++static int get_bitmap_width     PARAMS ((int, int, int));
++static void record_one_set    PARAMS ((int, rtx));
++static void record_set_info   PARAMS ((rtx, rtx, void *));
++static void compute_sets      PARAMS ((rtx));
++static void hash_scan_insn    PARAMS ((rtx, struct hash_table *, int));
++static void hash_scan_set     PARAMS ((rtx, rtx, struct hash_table *));
++static void hash_scan_clobber PARAMS ((rtx, rtx, struct hash_table *));
++static void hash_scan_call    PARAMS ((rtx, rtx, struct hash_table *));
++static int want_to_gcse_p     PARAMS ((rtx));
++static int oprs_unchanged_p   PARAMS ((rtx, rtx, int));
++static int oprs_anticipatable_p PARAMS ((rtx, rtx));
++static int oprs_available_p   PARAMS ((rtx, rtx));
++static void insert_expr_in_table PARAMS ((rtx, enum machine_mode, rtx,
++                                        int, int, struct hash_table *));
++static void insert_set_in_table PARAMS ((rtx, rtx, struct hash_table *));
++static unsigned int hash_expr PARAMS ((rtx, enum machine_mode, int *, int));
++static unsigned int hash_expr_1 PARAMS ((rtx, enum machine_mode, int *));
++static unsigned int hash_string_1 PARAMS ((const char *));
++static unsigned int hash_set  PARAMS ((int, int));
++static int expr_equiv_p               PARAMS ((rtx, rtx));
++static void record_last_reg_set_info PARAMS ((rtx, int));
++static void record_last_mem_set_info PARAMS ((rtx));
++static void record_last_set_info PARAMS ((rtx, rtx, void *));
++static void compute_hash_table        PARAMS ((struct hash_table *));
++static void alloc_hash_table PARAMS ((int, struct hash_table *, int));
++static void free_hash_table PARAMS ((struct hash_table *));
++static void compute_hash_table_work PARAMS ((struct hash_table *));
++static void dump_hash_table   PARAMS ((FILE *, const char *,
++                                      struct hash_table *));
++static struct expr *lookup_expr       PARAMS ((rtx, struct hash_table *));
++static struct expr *lookup_set        PARAMS ((unsigned int, rtx, struct hash_table *));
++static struct expr *next_set  PARAMS ((unsigned int, struct expr *));
++static void reset_opr_set_tables PARAMS ((void));
++static int oprs_not_set_p     PARAMS ((rtx, rtx));
++static void mark_call         PARAMS ((rtx));
++static void mark_set          PARAMS ((rtx, rtx));
++static void mark_clobber      PARAMS ((rtx, rtx));
++static void mark_oprs_set     PARAMS ((rtx));
++static void alloc_cprop_mem   PARAMS ((int, int));
++static void free_cprop_mem    PARAMS ((void));
++static void compute_transp    PARAMS ((rtx, int, sbitmap *, int));
++static void compute_transpout PARAMS ((void));
++static void compute_local_properties PARAMS ((sbitmap *, sbitmap *, sbitmap *,
++                                            struct hash_table *));
++static void compute_cprop_data        PARAMS ((void));
++static void find_used_regs    PARAMS ((rtx *, void *));
++static int try_replace_reg    PARAMS ((rtx, rtx, rtx));
++static struct expr *find_avail_set PARAMS ((int, rtx));
++static int cprop_jump         PARAMS ((basic_block, rtx, rtx, rtx, rtx));
++static void mems_conflict_for_gcse_p PARAMS ((rtx, rtx, void *));
++static int load_killed_in_block_p    PARAMS ((basic_block, int, rtx, int));
++static void canon_list_insert        PARAMS ((rtx, rtx, void *));
++static int cprop_insn         PARAMS ((rtx, int));
++static int cprop              PARAMS ((int));
++static int one_cprop_pass     PARAMS ((int, int));
++static bool constprop_register        PARAMS ((rtx, rtx, rtx, int));
++static struct expr *find_bypass_set PARAMS ((int, int));
++static bool reg_killed_on_edge            PARAMS ((rtx, edge));
++static int bypass_block                   PARAMS ((basic_block, rtx, rtx));
++static int bypass_conditional_jumps PARAMS ((void));
++static void alloc_pre_mem     PARAMS ((int, int));
++static void free_pre_mem      PARAMS ((void));
++static void compute_pre_data  PARAMS ((void));
++static int pre_expr_reaches_here_p PARAMS ((basic_block, struct expr *,
++                                          basic_block));
++static void insert_insn_end_bb        PARAMS ((struct expr *, basic_block, int));
++static void pre_insert_copy_insn PARAMS ((struct expr *, rtx));
++static void pre_insert_copies PARAMS ((void));
++static int pre_delete         PARAMS ((void));
++static int pre_gcse           PARAMS ((void));
++static int one_pre_gcse_pass  PARAMS ((int));
++static void add_label_notes   PARAMS ((rtx, rtx));
++static void alloc_code_hoist_mem PARAMS ((int, int));
++static void free_code_hoist_mem       PARAMS ((void));
++static void compute_code_hoist_vbeinout       PARAMS ((void));
++static void compute_code_hoist_data PARAMS ((void));
++static int hoist_expr_reaches_here_p PARAMS ((basic_block, int, basic_block,
++                                            char *));
++static void hoist_code                PARAMS ((void));
++static int one_code_hoisting_pass PARAMS ((void));
++static void alloc_rd_mem      PARAMS ((int, int));
++static void free_rd_mem               PARAMS ((void));
++static void handle_rd_kill_set        PARAMS ((rtx, int, basic_block));
++static void compute_kill_rd   PARAMS ((void));
++static void compute_rd                PARAMS ((void));
++static void alloc_avail_expr_mem PARAMS ((int, int));
++static void free_avail_expr_mem PARAMS ((void));
++static void compute_ae_gen    PARAMS ((struct hash_table *));
++static int expr_killed_p      PARAMS ((rtx, basic_block));
++static void compute_ae_kill   PARAMS ((sbitmap *, sbitmap *, struct hash_table *));
++static int expr_reaches_here_p        PARAMS ((struct occr *, struct expr *,
++                                       basic_block, int));
++static rtx computing_insn     PARAMS ((struct expr *, rtx));
++static int def_reaches_here_p PARAMS ((rtx, rtx));
++static int can_disregard_other_sets PARAMS ((struct reg_set **, rtx, int));
++static int handle_avail_expr  PARAMS ((rtx, struct expr *));
++static int classic_gcse               PARAMS ((void));
++static int one_classic_gcse_pass PARAMS ((int));
++static void invalidate_nonnull_info PARAMS ((rtx, rtx, void *));
++static int delete_null_pointer_checks_1 PARAMS ((unsigned int *,
++                                                sbitmap *, sbitmap *,
++                                                struct null_pointer_info *));
++static rtx process_insert_insn        PARAMS ((struct expr *));
++static int pre_edge_insert    PARAMS ((struct edge_list *, struct expr **));
++static int expr_reaches_here_p_work PARAMS ((struct occr *, struct expr *,
++                                           basic_block, int, char *));
++static int pre_expr_reaches_here_p_work       PARAMS ((basic_block, struct expr *,
++                                               basic_block, char *));
++static struct ls_expr * ldst_entry    PARAMS ((rtx));
++static void free_ldst_entry           PARAMS ((struct ls_expr *));
++static void free_ldst_mems            PARAMS ((void));
++static void print_ldst_list           PARAMS ((FILE *));
++static struct ls_expr * find_rtx_in_ldst PARAMS ((rtx));
++static int enumerate_ldsts            PARAMS ((void));
++static inline struct ls_expr * first_ls_expr PARAMS ((void));
++static inline struct ls_expr * next_ls_expr  PARAMS ((struct ls_expr *));
++static int simple_mem                 PARAMS ((rtx));
++static void invalidate_any_buried_refs        PARAMS ((rtx));
++static void compute_ld_motion_mems    PARAMS ((void));
++static void trim_ld_motion_mems               PARAMS ((void));
++static void update_ld_motion_stores   PARAMS ((struct expr *));
++static void reg_set_info              PARAMS ((rtx, rtx, void *));
++static int store_ops_ok                       PARAMS ((rtx, basic_block));
++static void find_moveable_store               PARAMS ((rtx));
++static int compute_store_table                PARAMS ((void));
++static int load_kills_store           PARAMS ((rtx, rtx));
++static int find_loads                 PARAMS ((rtx, rtx));
++static int store_killed_in_insn               PARAMS ((rtx, rtx));
++static int store_killed_after         PARAMS ((rtx, rtx, basic_block));
++static int store_killed_before                PARAMS ((rtx, rtx, basic_block));
++static void build_store_vectors               PARAMS ((void));
++static void insert_insn_start_bb      PARAMS ((rtx, basic_block));
++static int insert_store                       PARAMS ((struct ls_expr *, edge));
++static void replace_store_insn                PARAMS ((rtx, rtx, basic_block));
++static void delete_store              PARAMS ((struct ls_expr *,
++                                               basic_block));
++static void free_store_memory         PARAMS ((void));
++static void store_motion              PARAMS ((void));
++static void free_insn_expr_list_list  PARAMS ((rtx *));
++static void clear_modify_mem_tables   PARAMS ((void));
++static void free_modify_mem_tables    PARAMS ((void));
++static rtx gcse_emit_move_after               PARAMS ((rtx, rtx, rtx));
++static void local_cprop_find_used_regs        PARAMS ((rtx *, void *));
++static bool do_local_cprop            PARAMS ((rtx, rtx, int, rtx*));
++static bool adjust_libcall_notes      PARAMS ((rtx, rtx, rtx, rtx*));
++static void local_cprop_pass          PARAMS ((int));
++\f
++/* Entry point for global common subexpression elimination.
++   F is the first instruction in the function.  */
++
++int
++gcse_main (f, file)
++     rtx f;
++     FILE *file;
++{
++  int changed, pass;
++  /* Bytes used at start of pass.  */
++  int initial_bytes_used;
++  /* Maximum number of bytes used by a pass.  */
++  int max_pass_bytes;
++  /* Point to release obstack data from for each pass.  */
++  char *gcse_obstack_bottom;
++
++  /* Insertion of instructions on edges can create new basic blocks; we
++     need the original basic block count so that we can properly deallocate
++     arrays sized on the number of basic blocks originally in the cfg.  */
++  int orig_bb_count;
++  /* We do not construct an accurate cfg in functions which call
++     setjmp, so just punt to be safe.  */
++  if (current_function_calls_setjmp)
++    return 0;
++
++  /* Assume that we do not need to run jump optimizations after gcse.  */
++  run_jump_opt_after_gcse = 0;
++
++  /* For calling dump_foo fns from gdb.  */
++  debug_stderr = stderr;
++  gcse_file = file;
++
++  /* Identify the basic block information for this function, including
++     successors and predecessors.  */
++  max_gcse_regno = max_reg_num ();
++
++  if (file)
++    dump_flow_info (file);
++
++  orig_bb_count = n_basic_blocks;
++  /* Return if there's nothing to do.  */
++  if (n_basic_blocks <= 1)
++    return 0;
++
++  /* Trying to perform global optimizations on flow graphs which have
++     a high connectivity will take a long time and is unlikely to be
++     particularly useful.
++
++     In normal circumstances a cfg should have about twice as many edges
++     as blocks.  But we do not want to punish small functions which have
++     a couple switch statements.  So we require a relatively large number
++     of basic blocks and the ratio of edges to blocks to be high.  */
++  if (n_basic_blocks > 1000 && n_edges / n_basic_blocks >= 20)
++    {
++      if (warn_disabled_optimization)
++      warning ("GCSE disabled: %d > 1000 basic blocks and %d >= 20 edges/basic block",
++               n_basic_blocks, n_edges / n_basic_blocks);
++      return 0;
++    }
++
++  /* If allocating memory for the cprop bitmap would take up too much
++     storage it's better just to disable the optimization.  */
++  if ((n_basic_blocks
++       * SBITMAP_SET_SIZE (max_gcse_regno)
++       * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
++    {
++      if (warn_disabled_optimization)
++      warning ("GCSE disabled: %d basic blocks and %d registers",
++               n_basic_blocks, max_gcse_regno);
++
++      return 0;
++    }
++
++  /* See what modes support reg/reg copy operations.  */
++  if (! can_copy_init_p)
++    {
++      compute_can_copy ();
++      can_copy_init_p = 1;
++    }
++
++  gcc_obstack_init (&gcse_obstack);
++  bytes_used = 0;
++
++  /* We need alias.  */
++  init_alias_analysis ();
++  /* Record where pseudo-registers are set.  This data is kept accurate
++     during each pass.  ??? We could also record hard-reg information here
++     [since it's unchanging], however it is currently done during hash table
++     computation.
++
++     It may be tempting to compute MEM set information here too, but MEM sets
++     will be subject to code motion one day and thus we need to compute
++     information about memory sets when we build the hash tables.  */
++
++  alloc_reg_set_mem (max_gcse_regno);
++  compute_sets (f);
++
++  pass = 0;
++  initial_bytes_used = bytes_used;
++  max_pass_bytes = 0;
++  gcse_obstack_bottom = gcse_alloc (1);
++  changed = 1;
++  while (changed && pass < MAX_GCSE_PASSES)
++    {
++      changed = 0;
++      if (file)
++      fprintf (file, "GCSE pass %d\n\n", pass + 1);
++
++      /* Initialize bytes_used to the space for the pred/succ lists,
++       and the reg_set_table data.  */
++      bytes_used = initial_bytes_used;
++
++      /* Each pass may create new registers, so recalculate each time.  */
++      max_gcse_regno = max_reg_num ();
++
++      alloc_gcse_mem (f);
++
++      /* Don't allow constant propagation to modify jumps
++       during this pass.  */
++      changed = one_cprop_pass (pass + 1, 0);
++
++      if (optimize_size)
++      changed |= one_classic_gcse_pass (pass + 1);
++      else
++      {
++        changed |= one_pre_gcse_pass (pass + 1);
++        /* We may have just created new basic blocks.  Release and
++           recompute various things which are sized on the number of
++           basic blocks.  */
++        if (changed)
++          {
++            free_modify_mem_tables ();
++            modify_mem_list
++              = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
++            canon_modify_mem_list
++              = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
++            memset ((char *) modify_mem_list, 0, last_basic_block * sizeof (rtx));
++            memset ((char *) canon_modify_mem_list, 0, last_basic_block * sizeof (rtx));
++            orig_bb_count = n_basic_blocks;
++          }
++        free_reg_set_mem ();
++        alloc_reg_set_mem (max_reg_num ());
++        compute_sets (f);
++        run_jump_opt_after_gcse = 1;
++      }
++
++      if (max_pass_bytes < bytes_used)
++      max_pass_bytes = bytes_used;
++
++      /* Free up memory, then reallocate for code hoisting.  We can
++       not re-use the existing allocated memory because the tables
++       will not have info for the insns or registers created by
++       partial redundancy elimination.  */
++      free_gcse_mem ();
++
++      /* It does not make sense to run code hoisting unless we optimizing
++       for code size -- it rarely makes programs faster, and can make
++       them bigger if we did partial redundancy elimination (when optimizing
++       for space, we use a classic gcse algorithm instead of partial
++       redundancy algorithms).  */
++      if (optimize_size)
++      {
++        max_gcse_regno = max_reg_num ();
++        alloc_gcse_mem (f);
++        changed |= one_code_hoisting_pass ();
++        free_gcse_mem ();
++
++        if (max_pass_bytes < bytes_used)
++          max_pass_bytes = bytes_used;
++      }
++
++      if (file)
++      {
++        fprintf (file, "\n");
++        fflush (file);
++      }
++
++      obstack_free (&gcse_obstack, gcse_obstack_bottom);
++      pass++;
++    }
++
++  /* Do one last pass of copy propagation, including cprop into
++     conditional jumps.  */
++
++  max_gcse_regno = max_reg_num ();
++  alloc_gcse_mem (f);
++  /* This time, go ahead and allow cprop to alter jumps.  */
++  one_cprop_pass (pass + 1, 1);
++  free_gcse_mem ();
++
++  if (file)
++    {
++      fprintf (file, "GCSE of %s: %d basic blocks, ",
++             current_function_name, n_basic_blocks);
++      fprintf (file, "%d pass%s, %d bytes\n\n",
++             pass, pass > 1 ? "es" : "", max_pass_bytes);
++    }
++
++  obstack_free (&gcse_obstack, NULL);
++  free_reg_set_mem ();
++  /* We are finished with alias.  */
++  end_alias_analysis ();
++  allocate_reg_info (max_reg_num (), FALSE, FALSE);
++
++  /* Store motion disabled until it is fixed.  */
++  if (0 && !optimize_size && flag_gcse_sm)
++    store_motion ();
++  /* Record where pseudo-registers are set.  */
++  return run_jump_opt_after_gcse;
++}
++\f
++/* Misc. utilities.  */
++
++/* Compute which modes support reg/reg copy operations.  */
++
++static void
++compute_can_copy ()
++{
++  int i;
++#ifndef AVOID_CCMODE_COPIES
++  rtx reg, insn;
++#endif
++  memset (can_copy_p, 0, NUM_MACHINE_MODES);
++
++  start_sequence ();
++  for (i = 0; i < NUM_MACHINE_MODES; i++)
++    if (GET_MODE_CLASS (i) == MODE_CC)
++      {
++#ifdef AVOID_CCMODE_COPIES
++      can_copy_p[i] = 0;
++#else
++      reg = gen_rtx_REG ((enum machine_mode) i, LAST_VIRTUAL_REGISTER + 1);
++      insn = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
++      if (recog (PATTERN (insn), insn, NULL) >= 0)
++        can_copy_p[i] = 1;
++#endif
++      }
++    else
++      can_copy_p[i] = 1;
++
++  end_sequence ();
++}
++\f
++/* Cover function to xmalloc to record bytes allocated.  */
++
++static char *
++gmalloc (size)
++     unsigned int size;
++{
++  bytes_used += size;
++  return xmalloc (size);
++}
++
++/* Cover function to xrealloc.
++   We don't record the additional size since we don't know it.
++   It won't affect memory usage stats much anyway.  */
++
++static char *
++grealloc (ptr, size)
++     char *ptr;
++     unsigned int size;
++{
++  return xrealloc (ptr, size);
++}
++
++/* Cover function to obstack_alloc.  */
++
++static char *
++gcse_alloc (size)
++     unsigned long size;
++{
++  bytes_used += size;
++  return (char *) obstack_alloc (&gcse_obstack, size);
++}
++
++/* Allocate memory for the cuid mapping array,
++   and reg/memory set tracking tables.
++
++   This is called at the start of each pass.  */
++
++static void
++alloc_gcse_mem (f)
++     rtx f;
++{
++  int i, n;
++  rtx insn;
++
++  /* Find the largest UID and create a mapping from UIDs to CUIDs.
++     CUIDs are like UIDs except they increase monotonically, have no gaps,
++     and only apply to real insns.  */
++
++  max_uid = get_max_uid ();
++  n = (max_uid + 1) * sizeof (int);
++  uid_cuid = (int *) gmalloc (n);
++  memset ((char *) uid_cuid, 0, n);
++  for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
++    {
++      if (INSN_P (insn))
++      uid_cuid[INSN_UID (insn)] = i++;
++      else
++      uid_cuid[INSN_UID (insn)] = i;
++    }
++
++  /* Create a table mapping cuids to insns.  */
++
++  max_cuid = i;
++  n = (max_cuid + 1) * sizeof (rtx);
++  cuid_insn = (rtx *) gmalloc (n);
++  memset ((char *) cuid_insn, 0, n);
++  for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
++    if (INSN_P (insn))
++      CUID_INSN (i++) = insn;
++
++  /* Allocate vars to track sets of regs.  */
++  reg_set_bitmap = BITMAP_XMALLOC ();
++
++  /* Allocate vars to track sets of regs, memory per block.  */
++  reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (last_basic_block,
++                                                     max_gcse_regno);
++  /* Allocate array to keep a list of insns which modify memory in each
++     basic block.  */
++  modify_mem_list = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
++  canon_modify_mem_list = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
++  memset ((char *) modify_mem_list, 0, last_basic_block * sizeof (rtx));
++  memset ((char *) canon_modify_mem_list, 0, last_basic_block * sizeof (rtx));
++  modify_mem_list_set = BITMAP_XMALLOC ();
++  canon_modify_mem_list_set = BITMAP_XMALLOC ();
++}
++
++/* Free memory allocated by alloc_gcse_mem.  */
++
++static void
++free_gcse_mem ()
++{
++  free (uid_cuid);
++  free (cuid_insn);
++
++  BITMAP_XFREE (reg_set_bitmap);
++
++  sbitmap_vector_free (reg_set_in_block);
++  free_modify_mem_tables ();
++  BITMAP_XFREE (modify_mem_list_set);
++  BITMAP_XFREE (canon_modify_mem_list_set);
++}
++
++/* Many of the global optimization algorithms work by solving dataflow
++   equations for various expressions.  Initially, some local value is
++   computed for each expression in each block.  Then, the values across the
++   various blocks are combined (by following flow graph edges) to arrive at
++   global values.  Conceptually, each set of equations is independent.  We
++   may therefore solve all the equations in parallel, solve them one at a
++   time, or pick any intermediate approach.
++
++   When you're going to need N two-dimensional bitmaps, each X (say, the
++   number of blocks) by Y (say, the number of expressions), call this
++   function.  It's not important what X and Y represent; only that Y
++   correspond to the things that can be done in parallel.  This function will
++   return an appropriate chunking factor C; you should solve C sets of
++   equations in parallel.  By going through this function, we can easily
++   trade space against time; by solving fewer equations in parallel we use
++   less space.  */
++
++static int
++get_bitmap_width (n, x, y)
++     int n;
++     int x;
++     int y;
++{
++  /* It's not really worth figuring out *exactly* how much memory will
++     be used by a particular choice.  The important thing is to get
++     something approximately right.  */
++  size_t max_bitmap_memory = 10 * 1024 * 1024;
++
++  /* The number of bytes we'd use for a single column of minimum
++     width.  */
++  size_t column_size = n * x * sizeof (SBITMAP_ELT_TYPE);
++
++  /* Often, it's reasonable just to solve all the equations in
++     parallel.  */
++  if (column_size * SBITMAP_SET_SIZE (y) <= max_bitmap_memory)
++    return y;
++
++  /* Otherwise, pick the largest width we can, without going over the
++     limit.  */
++  return SBITMAP_ELT_BITS * ((max_bitmap_memory + column_size - 1)
++                           / column_size);
++}
++\f
++/* Compute the local properties of each recorded expression.
++
++   Local properties are those that are defined by the block, irrespective of
++   other blocks.
++
++   An expression is transparent in a block if its operands are not modified
++   in the block.
++
++   An expression is computed (locally available) in a block if it is computed
++   at least once and expression would contain the same value if the
++   computation was moved to the end of the block.
++
++   An expression is locally anticipatable in a block if it is computed at
++   least once and expression would contain the same value if the computation
++   was moved to the beginning of the block.
++
++   We call this routine for cprop, pre and code hoisting.  They all compute
++   basically the same information and thus can easily share this code.
++
++   TRANSP, COMP, and ANTLOC are destination sbitmaps for recording local
++   properties.  If NULL, then it is not necessary to compute or record that
++   particular property.
++
++   TABLE controls which hash table to look at.  If it is  set hash table,
++   additionally, TRANSP is computed as ~TRANSP, since this is really cprop's
++   ABSALTERED.  */
++
++static void
++compute_local_properties (transp, comp, antloc, table)
++     sbitmap *transp;
++     sbitmap *comp;
++     sbitmap *antloc;
++     struct hash_table *table;
++{
++  unsigned int i;
++
++  /* Initialize any bitmaps that were passed in.  */
++  if (transp)
++    {
++      if (table->set_p)
++      sbitmap_vector_zero (transp, last_basic_block);
++      else
++      sbitmap_vector_ones (transp, last_basic_block);
++    }
++
++  if (comp)
++    sbitmap_vector_zero (comp, last_basic_block);
++  if (antloc)
++    sbitmap_vector_zero (antloc, last_basic_block);
++
++  for (i = 0; i < table->size; i++)
++    {
++      struct expr *expr;
++
++      for (expr = table->table[i]; expr != NULL; expr = expr->next_same_hash)
++      {
++        int indx = expr->bitmap_index;
++        struct occr *occr;
++
++        /* The expression is transparent in this block if it is not killed.
++           We start by assuming all are transparent [none are killed], and
++           then reset the bits for those that are.  */
++        if (transp)
++          compute_transp (expr->expr, indx, transp, table->set_p);
++
++        /* The occurrences recorded in antic_occr are exactly those that
++           we want to set to nonzero in ANTLOC.  */
++        if (antloc)
++          for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
++            {
++              SET_BIT (antloc[BLOCK_NUM (occr->insn)], indx);
++
++              /* While we're scanning the table, this is a good place to
++                 initialize this.  */
++              occr->deleted_p = 0;
++            }
++
++        /* The occurrences recorded in avail_occr are exactly those that
++           we want to set to nonzero in COMP.  */
++        if (comp)
++          for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
++            {
++              SET_BIT (comp[BLOCK_NUM (occr->insn)], indx);
++
++              /* While we're scanning the table, this is a good place to
++                 initialize this.  */
++              occr->copied_p = 0;
++            }
++
++        /* While we're scanning the table, this is a good place to
++           initialize this.  */
++        expr->reaching_reg = 0;
++      }
++    }
++}
++\f
++/* Register set information.
++
++   `reg_set_table' records where each register is set or otherwise
++   modified.  */
++
++static struct obstack reg_set_obstack;
++
++static void
++alloc_reg_set_mem (n_regs)
++     int n_regs;
++{
++  unsigned int n;
++
++  reg_set_table_size = n_regs + REG_SET_TABLE_SLOP;
++  n = reg_set_table_size * sizeof (struct reg_set *);
++  reg_set_table = (struct reg_set **) gmalloc (n);
++  memset ((char *) reg_set_table, 0, n);
++
++  gcc_obstack_init (&reg_set_obstack);
++}
++
++static void
++free_reg_set_mem ()
++{
++  free (reg_set_table);
++  obstack_free (&reg_set_obstack, NULL);
++}
++
++/* Record REGNO in the reg_set table.  */
++
++static void
++record_one_set (regno, insn)
++     int regno;
++     rtx insn;
++{
++  /* Allocate a new reg_set element and link it onto the list.  */
++  struct reg_set *new_reg_info;
++
++  /* If the table isn't big enough, enlarge it.  */
++  if (regno >= reg_set_table_size)
++    {
++      int new_size = regno + REG_SET_TABLE_SLOP;
++
++      reg_set_table
++      = (struct reg_set **) grealloc ((char *) reg_set_table,
++                                      new_size * sizeof (struct reg_set *));
++      memset ((char *) (reg_set_table + reg_set_table_size), 0,
++            (new_size - reg_set_table_size) * sizeof (struct reg_set *));
++      reg_set_table_size = new_size;
++    }
++
++  new_reg_info = (struct reg_set *) obstack_alloc (&reg_set_obstack,
++                                                 sizeof (struct reg_set));
++  bytes_used += sizeof (struct reg_set);
++  new_reg_info->insn = insn;
++  new_reg_info->next = reg_set_table[regno];
++  reg_set_table[regno] = new_reg_info;
++}
++
++/* Called from compute_sets via note_stores to handle one SET or CLOBBER in
++   an insn.  The DATA is really the instruction in which the SET is
++   occurring.  */
++
++static void
++record_set_info (dest, setter, data)
++     rtx dest, setter ATTRIBUTE_UNUSED;
++     void *data;
++{
++  rtx record_set_insn = (rtx) data;
++
++  if (GET_CODE (dest) == REG && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
++    record_one_set (REGNO (dest), record_set_insn);
++}
++
++/* Scan the function and record each set of each pseudo-register.
++
++   This is called once, at the start of the gcse pass.  See the comments for
++   `reg_set_table' for further documenation.  */
++
++static void
++compute_sets (f)
++     rtx f;
++{
++  rtx insn;
++
++  for (insn = f; insn != 0; insn = NEXT_INSN (insn))
++    if (INSN_P (insn))
++      note_stores (PATTERN (insn), record_set_info, insn);
++}
++\f
++/* Hash table support.  */
++
++struct reg_avail_info
++{
++  basic_block last_bb;
++  int first_set;
++  int last_set;
++};
++
++static struct reg_avail_info *reg_avail_info;
++static basic_block current_bb;
++
++
++/* See whether X, the source of a set, is something we want to consider for
++   GCSE.  */
++
++static GTY(()) rtx test_insn;
++static int
++want_to_gcse_p (x)
++     rtx x;
++{
++  int num_clobbers = 0;
++  int icode;
++
++  switch (GET_CODE (x))
++    {
++    case REG:
++    case SUBREG:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case CALL:
++      return 0;
++
++    default:
++      break;
++    }
++
++  /* If this is a valid operand, we are OK.  If it's VOIDmode, we aren't.  */
++  if (general_operand (x, GET_MODE (x)))
++    return 1;
++  else if (GET_MODE (x) == VOIDmode)
++    return 0;
++
++  /* Otherwise, check if we can make a valid insn from it.  First initialize
++     our test insn if we haven't already.  */
++  if (test_insn == 0)
++    {
++      test_insn
++      = make_insn_raw (gen_rtx_SET (VOIDmode,
++                                    gen_rtx_REG (word_mode,
++                                                 FIRST_PSEUDO_REGISTER * 2),
++                                    const0_rtx));
++      NEXT_INSN (test_insn) = PREV_INSN (test_insn) = 0;
++    }
++
++  /* Now make an insn like the one we would make when GCSE'ing and see if
++     valid.  */
++  PUT_MODE (SET_DEST (PATTERN (test_insn)), GET_MODE (x));
++  SET_SRC (PATTERN (test_insn)) = x;
++  return ((icode = recog (PATTERN (test_insn), test_insn, &num_clobbers)) >= 0
++        && (num_clobbers == 0 || ! added_clobbers_hard_reg_p (icode)));
++}
++
++/* Return nonzero if the operands of expression X are unchanged from the
++   start of INSN's basic block up to but not including INSN (if AVAIL_P == 0),
++   or from INSN to the end of INSN's basic block (if AVAIL_P != 0).  */
++
++static int
++oprs_unchanged_p (x, insn, avail_p)
++     rtx x, insn;
++     int avail_p;
++{
++  int i, j;
++  enum rtx_code code;
++  const char *fmt;
++
++  if (x == 0)
++    return 1;
++
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case REG:
++      {
++      struct reg_avail_info *info = &reg_avail_info[REGNO (x)];
++
++      if (info->last_bb != current_bb)
++        return 1;
++      if (avail_p)
++        return info->last_set < INSN_CUID (insn);
++      else
++        return info->first_set >= INSN_CUID (insn);
++      }
++
++    case MEM:
++      if (load_killed_in_block_p (current_bb, INSN_CUID (insn),
++                                x, avail_p))
++      return 0;
++      else
++      return oprs_unchanged_p (XEXP (x, 0), insn, avail_p);
++
++    case PRE_DEC:
++    case PRE_INC:
++    case POST_DEC:
++    case POST_INC:
++    case PRE_MODIFY:
++    case POST_MODIFY:
++      return 0;
++
++    case PC:
++    case CC0: /*FIXME*/
++    case CONST:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case LABEL_REF:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++      return 1;
++
++    default:
++      break;
++    }
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call needed at this
++           level, change it into iteration.  This function is called enough
++           to be worth it.  */
++        if (i == 0)
++          return oprs_unchanged_p (XEXP (x, i), insn, avail_p);
++
++        else if (! oprs_unchanged_p (XEXP (x, i), insn, avail_p))
++          return 0;
++      }
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        if (! oprs_unchanged_p (XVECEXP (x, i, j), insn, avail_p))
++          return 0;
++    }
++
++  return 1;
++}
++
++/* Used for communication between mems_conflict_for_gcse_p and
++   load_killed_in_block_p.  Nonzero if mems_conflict_for_gcse_p finds a
++   conflict between two memory references.  */
++static int gcse_mems_conflict_p;
++
++/* Used for communication between mems_conflict_for_gcse_p and
++   load_killed_in_block_p.  A memory reference for a load instruction,
++   mems_conflict_for_gcse_p will see if a memory store conflicts with
++   this memory load.  */
++static rtx gcse_mem_operand;
++
++/* DEST is the output of an instruction.  If it is a memory reference, and
++   possibly conflicts with the load found in gcse_mem_operand, then set
++   gcse_mems_conflict_p to a nonzero value.  */
++
++static void
++mems_conflict_for_gcse_p (dest, setter, data)
++     rtx dest, setter ATTRIBUTE_UNUSED;
++     void *data ATTRIBUTE_UNUSED;
++{
++  while (GET_CODE (dest) == SUBREG
++       || GET_CODE (dest) == ZERO_EXTRACT
++       || GET_CODE (dest) == SIGN_EXTRACT
++       || GET_CODE (dest) == STRICT_LOW_PART)
++    dest = XEXP (dest, 0);
++
++  /* If DEST is not a MEM, then it will not conflict with the load.  Note
++     that function calls are assumed to clobber memory, but are handled
++     elsewhere.  */
++  if (GET_CODE (dest) != MEM)
++    return;
++
++  /* If we are setting a MEM in our list of specially recognized MEMs,
++     don't mark as killed this time.  */
++
++  if (dest == gcse_mem_operand && pre_ldst_mems != NULL)
++    {
++      if (!find_rtx_in_ldst (dest))
++      gcse_mems_conflict_p = 1;
++      return;
++    }
++
++  if (true_dependence (dest, GET_MODE (dest), gcse_mem_operand,
++                     rtx_addr_varies_p))
++    gcse_mems_conflict_p = 1;
++}
++
++/* Return nonzero if the expression in X (a memory reference) is killed
++   in block BB before or after the insn with the CUID in UID_LIMIT.
++   AVAIL_P is nonzero for kills after UID_LIMIT, and zero for kills
++   before UID_LIMIT.
++
++   To check the entire block, set UID_LIMIT to max_uid + 1 and
++   AVAIL_P to 0.  */
++
++static int
++load_killed_in_block_p (bb, uid_limit, x, avail_p)
++     basic_block bb;
++     int uid_limit;
++     rtx x;
++     int avail_p;
++{
++  rtx list_entry = modify_mem_list[bb->index];
++  while (list_entry)
++    {
++      rtx setter;
++      /* Ignore entries in the list that do not apply.  */
++      if ((avail_p
++         && INSN_CUID (XEXP (list_entry, 0)) < uid_limit)
++        || (! avail_p
++            && INSN_CUID (XEXP (list_entry, 0)) > uid_limit))
++      {
++        list_entry = XEXP (list_entry, 1);
++        continue;
++      }
++
++      setter = XEXP (list_entry, 0);
++
++      /* If SETTER is a call everything is clobbered.  Note that calls
++       to pure functions are never put on the list, so we need not
++       worry about them.  */
++      if (GET_CODE (setter) == CALL_INSN)
++      return 1;
++
++      /* SETTER must be an INSN of some kind that sets memory.  Call
++       note_stores to examine each hunk of memory that is modified.
++
++       The note_stores interface is pretty limited, so we have to
++       communicate via global variables.  Yuk.  */
++      gcse_mem_operand = x;
++      gcse_mems_conflict_p = 0;
++      note_stores (PATTERN (setter), mems_conflict_for_gcse_p, NULL);
++      if (gcse_mems_conflict_p)
++      return 1;
++      list_entry = XEXP (list_entry, 1);
++    }
++  return 0;
++}
++
++/* Return nonzero if the operands of expression X are unchanged from
++   the start of INSN's basic block up to but not including INSN.  */
++
++static int
++oprs_anticipatable_p (x, insn)
++     rtx x, insn;
++{
++  return oprs_unchanged_p (x, insn, 0);
++}
++
++/* Return nonzero if the operands of expression X are unchanged from
++   INSN to the end of INSN's basic block.  */
++
++static int
++oprs_available_p (x, insn)
++     rtx x, insn;
++{
++  return oprs_unchanged_p (x, insn, 1);
++}
++
++/* Hash expression X.
++
++   MODE is only used if X is a CONST_INT.  DO_NOT_RECORD_P is a boolean
++   indicating if a volatile operand is found or if the expression contains
++   something we don't want to insert in the table.
++
++   ??? One might want to merge this with canon_hash.  Later.  */
++
++static unsigned int
++hash_expr (x, mode, do_not_record_p, hash_table_size)
++     rtx x;
++     enum machine_mode mode;
++     int *do_not_record_p;
++     int hash_table_size;
++{
++  unsigned int hash;
++
++  *do_not_record_p = 0;
++
++  hash = hash_expr_1 (x, mode, do_not_record_p);
++  return hash % hash_table_size;
++}
++
++/* Hash a string.  Just add its bytes up.  */
++
++static inline unsigned
++hash_string_1 (ps)
++     const char *ps;
++{
++  unsigned hash = 0;
++  const unsigned char *p = (const unsigned char *) ps;
++
++  if (p)
++    while (*p)
++      hash += *p++;
++
++  return hash;
++}
++
++/* Subroutine of hash_expr to do the actual work.  */
++
++static unsigned int
++hash_expr_1 (x, mode, do_not_record_p)
++     rtx x;
++     enum machine_mode mode;
++     int *do_not_record_p;
++{
++  int i, j;
++  unsigned hash = 0;
++  enum rtx_code code;
++  const char *fmt;
++
++  /* Used to turn recursion into iteration.  We can't rely on GCC's
++     tail-recursion eliminatio since we need to keep accumulating values
++     in HASH.  */
++
++  if (x == 0)
++    return hash;
++
++ repeat:
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case REG:
++      hash += ((unsigned int) REG << 7) + REGNO (x);
++      return hash;
++
++    case CONST_INT:
++      hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode
++             + (unsigned int) INTVAL (x));
++      return hash;
++
++    case CONST_DOUBLE:
++      /* This is like the general case, except that it only counts
++       the integers representing the constant.  */
++      hash += (unsigned int) code + (unsigned int) GET_MODE (x);
++      if (GET_MODE (x) != VOIDmode)
++      for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
++        hash += (unsigned int) XWINT (x, i);
++      else
++      hash += ((unsigned int) CONST_DOUBLE_LOW (x)
++               + (unsigned int) CONST_DOUBLE_HIGH (x));
++      return hash;
++
++    case CONST_VECTOR:
++      {
++      int units;
++      rtx elt;
++
++      units = CONST_VECTOR_NUNITS (x);
++
++      for (i = 0; i < units; ++i)
++        {
++          elt = CONST_VECTOR_ELT (x, i);
++          hash += hash_expr_1 (elt, GET_MODE (elt), do_not_record_p);
++        }
++
++      return hash;
++      }
++
++      /* Assume there is only one rtx object for any given label.  */
++    case LABEL_REF:
++      /* We don't hash on the address of the CODE_LABEL to avoid bootstrap
++       differences and differences between each stage's debugging dumps.  */
++      hash += (((unsigned int) LABEL_REF << 7)
++             + CODE_LABEL_NUMBER (XEXP (x, 0)));
++      return hash;
++
++    case SYMBOL_REF:
++      {
++      /* Don't hash on the symbol's address to avoid bootstrap differences.
++         Different hash values may cause expressions to be recorded in
++         different orders and thus different registers to be used in the
++         final assembler.  This also avoids differences in the dump files
++         between various stages.  */
++      unsigned int h = 0;
++      const unsigned char *p = (const unsigned char *) XSTR (x, 0);
++
++      while (*p)
++        h += (h << 7) + *p++; /* ??? revisit */
++
++      hash += ((unsigned int) SYMBOL_REF << 7) + h;
++      return hash;
++      }
++
++    case MEM:
++      if (MEM_VOLATILE_P (x))
++      {
++        *do_not_record_p = 1;
++        return 0;
++      }
++
++      hash += (unsigned int) MEM;
++      /* We used alias set for hashing, but this is not good, since the alias
++       set may differ in -fprofile-arcs and -fbranch-probabilities compilation
++       causing the profiles to fail to match.  */
++      x = XEXP (x, 0);
++      goto repeat;
++
++    case PRE_DEC:
++    case PRE_INC:
++    case POST_DEC:
++    case POST_INC:
++    case PC:
++    case CC0:
++    case CALL:
++    case UNSPEC_VOLATILE:
++      *do_not_record_p = 1;
++      return 0;
++
++    case ASM_OPERANDS:
++      if (MEM_VOLATILE_P (x))
++      {
++        *do_not_record_p = 1;
++        return 0;
++      }
++      else
++      {
++        /* We don't want to take the filename and line into account.  */
++        hash += (unsigned) code + (unsigned) GET_MODE (x)
++          + hash_string_1 (ASM_OPERANDS_TEMPLATE (x))
++          + hash_string_1 (ASM_OPERANDS_OUTPUT_CONSTRAINT (x))
++          + (unsigned) ASM_OPERANDS_OUTPUT_IDX (x);
++
++        if (ASM_OPERANDS_INPUT_LENGTH (x))
++          {
++            for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
++              {
++                hash += (hash_expr_1 (ASM_OPERANDS_INPUT (x, i),
++                                      GET_MODE (ASM_OPERANDS_INPUT (x, i)),
++                                      do_not_record_p)
++                         + hash_string_1 (ASM_OPERANDS_INPUT_CONSTRAINT
++                                          (x, i)));
++              }
++
++            hash += hash_string_1 (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
++            x = ASM_OPERANDS_INPUT (x, 0);
++            mode = GET_MODE (x);
++            goto repeat;
++          }
++        return hash;
++      }
++
++    default:
++      break;
++    }
++
++  hash += (unsigned) code + (unsigned) GET_MODE (x);
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          {
++            x = XEXP (x, i);
++            goto repeat;
++          }
++
++        hash += hash_expr_1 (XEXP (x, i), 0, do_not_record_p);
++        if (*do_not_record_p)
++          return 0;
++      }
++
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        {
++          hash += hash_expr_1 (XVECEXP (x, i, j), 0, do_not_record_p);
++          if (*do_not_record_p)
++            return 0;
++        }
++
++      else if (fmt[i] == 's')
++      hash += hash_string_1 (XSTR (x, i));
++      else if (fmt[i] == 'i')
++      hash += (unsigned int) XINT (x, i);
++      else
++      abort ();
++    }
++
++  return hash;
++}
++
++/* Hash a set of register REGNO.
++
++   Sets are hashed on the register that is set.  This simplifies the PRE copy
++   propagation code.
++
++   ??? May need to make things more elaborate.  Later, as necessary.  */
++
++static unsigned int
++hash_set (regno, hash_table_size)
++     int regno;
++     int hash_table_size;
++{
++  unsigned int hash;
++
++  hash = regno;
++  return hash % hash_table_size;
++}
++
++/* Return nonzero if exp1 is equivalent to exp2.
++   ??? Borrowed from cse.c.  Might want to remerge with cse.c.  Later.  */
++
++static int
++expr_equiv_p (x, y)
++     rtx x, y;
++{
++  int i, j;
++  enum rtx_code code;
++  const char *fmt;
++
++  if (x == y)
++    return 1;
++
++  if (x == 0 || y == 0)
++    return x == y;
++
++  code = GET_CODE (x);
++  if (code != GET_CODE (y))
++    return 0;
++
++  /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.  */
++  if (GET_MODE (x) != GET_MODE (y))
++    return 0;
++
++  switch (code)
++    {
++    case PC:
++    case CC0:
++      return x == y;
++
++    case CONST_INT:
++      return INTVAL (x) == INTVAL (y);
++
++    case LABEL_REF:
++      return XEXP (x, 0) == XEXP (y, 0);
++
++    case SYMBOL_REF:
++      return XSTR (x, 0) == XSTR (y, 0);
++
++    case REG:
++      return REGNO (x) == REGNO (y);
++
++    case MEM:
++      /* Can't merge two expressions in different alias sets, since we can
++       decide that the expression is transparent in a block when it isn't,
++       due to it being set with the different alias set.  */
++      if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
++      return 0;
++      break;
++
++    /*  For commutative operations, check both orders.  */
++    case PLUS:
++    case MULT:
++    case AND:
++    case IOR:
++    case XOR:
++    case NE:
++    case EQ:
++      return ((expr_equiv_p (XEXP (x, 0), XEXP (y, 0))
++             && expr_equiv_p (XEXP (x, 1), XEXP (y, 1)))
++            || (expr_equiv_p (XEXP (x, 0), XEXP (y, 1))
++                && expr_equiv_p (XEXP (x, 1), XEXP (y, 0))));
++
++    case ASM_OPERANDS:
++      /* We don't use the generic code below because we want to
++       disregard filename and line numbers.  */
++
++      /* A volatile asm isn't equivalent to any other.  */
++      if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
++      return 0;
++
++      if (GET_MODE (x) != GET_MODE (y)
++        || strcmp (ASM_OPERANDS_TEMPLATE (x), ASM_OPERANDS_TEMPLATE (y))
++        || strcmp (ASM_OPERANDS_OUTPUT_CONSTRAINT (x),
++                   ASM_OPERANDS_OUTPUT_CONSTRAINT (y))
++        || ASM_OPERANDS_OUTPUT_IDX (x) != ASM_OPERANDS_OUTPUT_IDX (y)
++        || ASM_OPERANDS_INPUT_LENGTH (x) != ASM_OPERANDS_INPUT_LENGTH (y))
++      return 0;
++
++      if (ASM_OPERANDS_INPUT_LENGTH (x))
++      {
++        for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
++          if (! expr_equiv_p (ASM_OPERANDS_INPUT (x, i),
++                              ASM_OPERANDS_INPUT (y, i))
++              || strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i),
++                         ASM_OPERANDS_INPUT_CONSTRAINT (y, i)))
++            return 0;
++      }
++
++      return 1;
++
++    default:
++      break;
++    }
++
++  /* Compare the elements.  If any pair of corresponding elements
++     fail to match, return 0 for the whole thing.  */
++
++  fmt = GET_RTX_FORMAT (code);
++  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
++    {
++      switch (fmt[i])
++      {
++      case 'e':
++        if (! expr_equiv_p (XEXP (x, i), XEXP (y, i)))
++          return 0;
++        break;
++
++      case 'E':
++        if (XVECLEN (x, i) != XVECLEN (y, i))
++          return 0;
++        for (j = 0; j < XVECLEN (x, i); j++)
++          if (! expr_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j)))
++            return 0;
++        break;
++
++      case 's':
++        if (strcmp (XSTR (x, i), XSTR (y, i)))
++          return 0;
++        break;
++
++      case 'i':
++        if (XINT (x, i) != XINT (y, i))
++          return 0;
++        break;
++
++      case 'w':
++        if (XWINT (x, i) != XWINT (y, i))
++          return 0;
++      break;
++
++      case '0':
++        break;
++
++      default:
++        abort ();
++      }
++    }
++
++  return 1;
++}
++
++/* Insert expression X in INSN in the hash TABLE.
++   If it is already present, record it as the last occurrence in INSN's
++   basic block.
++
++   MODE is the mode of the value X is being stored into.
++   It is only used if X is a CONST_INT.
++
++   ANTIC_P is nonzero if X is an anticipatable expression.
++   AVAIL_P is nonzero if X is an available expression.  */
++
++static void
++insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
++     rtx x;
++     enum machine_mode mode;
++     rtx insn;
++     int antic_p, avail_p;
++     struct hash_table *table;
++{
++  int found, do_not_record_p;
++  unsigned int hash;
++  struct expr *cur_expr, *last_expr = NULL;
++  struct occr *antic_occr, *avail_occr;
++  struct occr *last_occr = NULL;
++
++  hash = hash_expr (x, mode, &do_not_record_p, table->size);
++
++  /* Do not insert expression in table if it contains volatile operands,
++     or if hash_expr determines the expression is something we don't want
++     to or can't handle.  */
++  if (do_not_record_p)
++    return;
++
++  cur_expr = table->table[hash];
++  found = 0;
++
++  while (cur_expr && 0 == (found = expr_equiv_p (cur_expr->expr, x)))
++    {
++      /* If the expression isn't found, save a pointer to the end of
++       the list.  */
++      last_expr = cur_expr;
++      cur_expr = cur_expr->next_same_hash;
++    }
++
++  if (! found)
++    {
++      cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
++      bytes_used += sizeof (struct expr);
++      if (table->table[hash] == NULL)
++      /* This is the first pattern that hashed to this index.  */
++      table->table[hash] = cur_expr;
++      else
++      /* Add EXPR to end of this hash chain.  */
++      last_expr->next_same_hash = cur_expr;
++
++      /* Set the fields of the expr element.  */
++      cur_expr->expr = x;
++      cur_expr->bitmap_index = table->n_elems++;
++      cur_expr->next_same_hash = NULL;
++      cur_expr->antic_occr = NULL;
++      cur_expr->avail_occr = NULL;
++    }
++
++  /* Now record the occurrence(s).  */
++  if (antic_p)
++    {
++      antic_occr = cur_expr->antic_occr;
++
++      /* Search for another occurrence in the same basic block.  */
++      while (antic_occr && BLOCK_NUM (antic_occr->insn) != BLOCK_NUM (insn))
++      {
++        /* If an occurrence isn't found, save a pointer to the end of
++           the list.  */
++        last_occr = antic_occr;
++        antic_occr = antic_occr->next;
++      }
++
++      if (antic_occr)
++      /* Found another instance of the expression in the same basic block.
++         Prefer the currently recorded one.  We want the first one in the
++         block and the block is scanned from start to end.  */
++      ; /* nothing to do */
++      else
++      {
++        /* First occurrence of this expression in this basic block.  */
++        antic_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
++        bytes_used += sizeof (struct occr);
++        /* First occurrence of this expression in any block?  */
++        if (cur_expr->antic_occr == NULL)
++          cur_expr->antic_occr = antic_occr;
++        else
++          last_occr->next = antic_occr;
++
++        antic_occr->insn = insn;
++        antic_occr->next = NULL;
++      }
++    }
++
++  if (avail_p)
++    {
++      avail_occr = cur_expr->avail_occr;
++
++      /* Search for another occurrence in the same basic block.  */
++      while (avail_occr && BLOCK_NUM (avail_occr->insn) != BLOCK_NUM (insn))
++      {
++        /* If an occurrence isn't found, save a pointer to the end of
++           the list.  */
++        last_occr = avail_occr;
++        avail_occr = avail_occr->next;
++      }
++
++      if (avail_occr)
++      /* Found another instance of the expression in the same basic block.
++         Prefer this occurrence to the currently recorded one.  We want
++         the last one in the block and the block is scanned from start
++         to end.  */
++      avail_occr->insn = insn;
++      else
++      {
++        /* First occurrence of this expression in this basic block.  */
++        avail_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
++        bytes_used += sizeof (struct occr);
++
++        /* First occurrence of this expression in any block?  */
++        if (cur_expr->avail_occr == NULL)
++          cur_expr->avail_occr = avail_occr;
++        else
++          last_occr->next = avail_occr;
++
++        avail_occr->insn = insn;
++        avail_occr->next = NULL;
++      }
++    }
++}
++
++/* Insert pattern X in INSN in the hash table.
++   X is a SET of a reg to either another reg or a constant.
++   If it is already present, record it as the last occurrence in INSN's
++   basic block.  */
++
++static void
++insert_set_in_table (x, insn, table)
++     rtx x;
++     rtx insn;
++     struct hash_table *table;
++{
++  int found;
++  unsigned int hash;
++  struct expr *cur_expr, *last_expr = NULL;
++  struct occr *cur_occr, *last_occr = NULL;
++
++  if (GET_CODE (x) != SET
++      || GET_CODE (SET_DEST (x)) != REG)
++    abort ();
++
++  hash = hash_set (REGNO (SET_DEST (x)), table->size);
++
++  cur_expr = table->table[hash];
++  found = 0;
++
++  while (cur_expr && 0 == (found = expr_equiv_p (cur_expr->expr, x)))
++    {
++      /* If the expression isn't found, save a pointer to the end of
++       the list.  */
++      last_expr = cur_expr;
++      cur_expr = cur_expr->next_same_hash;
++    }
++
++  if (! found)
++    {
++      cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
++      bytes_used += sizeof (struct expr);
++      if (table->table[hash] == NULL)
++      /* This is the first pattern that hashed to this index.  */
++      table->table[hash] = cur_expr;
++      else
++      /* Add EXPR to end of this hash chain.  */
++      last_expr->next_same_hash = cur_expr;
++
++      /* Set the fields of the expr element.
++       We must copy X because it can be modified when copy propagation is
++       performed on its operands.  */
++      cur_expr->expr = copy_rtx (x);
++      cur_expr->bitmap_index = table->n_elems++;
++      cur_expr->next_same_hash = NULL;
++      cur_expr->antic_occr = NULL;
++      cur_expr->avail_occr = NULL;
++    }
++
++  /* Now record the occurrence.  */
++  cur_occr = cur_expr->avail_occr;
++
++  /* Search for another occurrence in the same basic block.  */
++  while (cur_occr && BLOCK_NUM (cur_occr->insn) != BLOCK_NUM (insn))
++    {
++      /* If an occurrence isn't found, save a pointer to the end of
++       the list.  */
++      last_occr = cur_occr;
++      cur_occr = cur_occr->next;
++    }
++
++  if (cur_occr)
++    /* Found another instance of the expression in the same basic block.
++       Prefer this occurrence to the currently recorded one.  We want the
++       last one in the block and the block is scanned from start to end.  */
++    cur_occr->insn = insn;
++  else
++    {
++      /* First occurrence of this expression in this basic block.  */
++      cur_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
++      bytes_used += sizeof (struct occr);
++
++      /* First occurrence of this expression in any block?  */
++      if (cur_expr->avail_occr == NULL)
++      cur_expr->avail_occr = cur_occr;
++      else
++      last_occr->next = cur_occr;
++
++      cur_occr->insn = insn;
++      cur_occr->next = NULL;
++    }
++}
++
++/* Scan pattern PAT of INSN and add an entry to the hash TABLE (set or
++   expression one).  */
++
++static void
++hash_scan_set (pat, insn, table)
++     rtx pat, insn;
++     struct hash_table *table;
++{
++  rtx src = SET_SRC (pat);
++  rtx dest = SET_DEST (pat);
++  rtx note;
++
++  if (GET_CODE (src) == CALL)
++    hash_scan_call (src, insn, table);
++
++  else if (GET_CODE (dest) == REG)
++    {
++      unsigned int regno = REGNO (dest);
++      rtx tmp;
++
++      /* If this is a single set and we are doing constant propagation,
++       see if a REG_NOTE shows this equivalent to a constant.  */
++      if (table->set_p && (note = find_reg_equal_equiv_note (insn)) != 0
++        && CONSTANT_P (XEXP (note, 0)))
++      src = XEXP (note, 0), pat = gen_rtx_SET (VOIDmode, dest, src);
++
++      /* Only record sets of pseudo-regs in the hash table.  */
++      if (! table->set_p
++        && regno >= FIRST_PSEUDO_REGISTER
++        /* Don't GCSE something if we can't do a reg/reg copy.  */
++        && can_copy_p [GET_MODE (dest)]
++        /* GCSE commonly inserts instruction after the insn.  We can't
++           do that easily for EH_REGION notes so disable GCSE on these
++           for now.  */
++        && !find_reg_note (insn, REG_EH_REGION, NULL_RTX)
++        /* Is SET_SRC something we want to gcse?  */
++        && want_to_gcse_p (src)
++        /* Don't CSE a nop.  */
++        && ! set_noop_p (pat)
++        /* Don't GCSE if it has attached REG_EQUIV note.
++           At this point this only function parameters should have
++           REG_EQUIV notes and if the argument slot is used somewhere
++           explicitly, it means address of parameter has been taken,
++           so we should not extend the lifetime of the pseudo.  */
++        && ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) == 0
++            || GET_CODE (XEXP (note, 0)) != MEM))
++      {
++        /* An expression is not anticipatable if its operands are
++           modified before this insn or if this is not the only SET in
++           this insn.  */
++        int antic_p = oprs_anticipatable_p (src, insn) && single_set (insn);
++        /* An expression is not available if its operands are
++           subsequently modified, including this insn.  It's also not
++           available if this is a branch, because we can't insert
++           a set after the branch.  */
++        int avail_p = (oprs_available_p (src, insn)
++                       && ! JUMP_P (insn));
++
++        insert_expr_in_table (src, GET_MODE (dest), insn, antic_p, avail_p, table);
++      }
++
++      /* Record sets for constant/copy propagation.  */
++      else if (table->set_p
++             && regno >= FIRST_PSEUDO_REGISTER
++             && ((GET_CODE (src) == REG
++                  && REGNO (src) >= FIRST_PSEUDO_REGISTER
++                  && can_copy_p [GET_MODE (dest)]
++                  && REGNO (src) != regno)
++                 || CONSTANT_P (src))
++             /* A copy is not available if its src or dest is subsequently
++                modified.  Here we want to search from INSN+1 on, but
++                oprs_available_p searches from INSN on.  */
++             && (insn == BLOCK_END (BLOCK_NUM (insn))
++                 || ((tmp = next_nonnote_insn (insn)) != NULL_RTX
++                     && oprs_available_p (pat, tmp))))
++      insert_set_in_table (pat, insn, table);
++    }
++}
++
++static void
++hash_scan_clobber (x, insn, table)
++     rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
++     struct hash_table *table ATTRIBUTE_UNUSED;
++{
++  /* Currently nothing to do.  */
++}
++
++static void
++hash_scan_call (x, insn, table)
++     rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
++     struct hash_table *table ATTRIBUTE_UNUSED;
++{
++  /* Currently nothing to do.  */
++}
++
++/* Process INSN and add hash table entries as appropriate.
++
++   Only available expressions that set a single pseudo-reg are recorded.
++
++   Single sets in a PARALLEL could be handled, but it's an extra complication
++   that isn't dealt with right now.  The trick is handling the CLOBBERs that
++   are also in the PARALLEL.  Later.
++
++   If SET_P is nonzero, this is for the assignment hash table,
++   otherwise it is for the expression hash table.
++   If IN_LIBCALL_BLOCK nonzero, we are in a libcall block, and should
++   not record any expressions.  */
++
++static void
++hash_scan_insn (insn, table, in_libcall_block)
++     rtx insn;
++     struct hash_table *table;
++     int in_libcall_block;
++{
++  rtx pat = PATTERN (insn);
++  int i;
++
++  if (in_libcall_block)
++    return;
++
++  /* Pick out the sets of INSN and for other forms of instructions record
++     what's been modified.  */
++
++  if (GET_CODE (pat) == SET)
++    hash_scan_set (pat, insn, table);
++  else if (GET_CODE (pat) == PARALLEL)
++    for (i = 0; i < XVECLEN (pat, 0); i++)
++      {
++      rtx x = XVECEXP (pat, 0, i);
++
++      if (GET_CODE (x) == SET)
++        hash_scan_set (x, insn, table);
++      else if (GET_CODE (x) == CLOBBER)
++        hash_scan_clobber (x, insn, table);
++      else if (GET_CODE (x) == CALL)
++        hash_scan_call (x, insn, table);
++      }
++
++  else if (GET_CODE (pat) == CLOBBER)
++    hash_scan_clobber (pat, insn, table);
++  else if (GET_CODE (pat) == CALL)
++    hash_scan_call (pat, insn, table);
++}
++
++static void
++dump_hash_table (file, name, table)
++     FILE *file;
++     const char *name;
++     struct hash_table *table;
++{
++  int i;
++  /* Flattened out table, so it's printed in proper order.  */
++  struct expr **flat_table;
++  unsigned int *hash_val;
++  struct expr *expr;
++
++  flat_table
++    = (struct expr **) xcalloc (table->n_elems, sizeof (struct expr *));
++  hash_val = (unsigned int *) xmalloc (table->n_elems * sizeof (unsigned int));
++
++  for (i = 0; i < (int) table->size; i++)
++    for (expr = table->table[i]; expr != NULL; expr = expr->next_same_hash)
++      {
++      flat_table[expr->bitmap_index] = expr;
++      hash_val[expr->bitmap_index] = i;
++      }
++
++  fprintf (file, "%s hash table (%d buckets, %d entries)\n",
++         name, table->size, table->n_elems);
++
++  for (i = 0; i < (int) table->n_elems; i++)
++    if (flat_table[i] != 0)
++      {
++      expr = flat_table[i];
++      fprintf (file, "Index %d (hash value %d)\n  ",
++               expr->bitmap_index, hash_val[i]);
++      print_rtl (file, expr->expr);
++      fprintf (file, "\n");
++      }
++
++  fprintf (file, "\n");
++
++  free (flat_table);
++  free (hash_val);
++}
++
++/* Record register first/last/block set information for REGNO in INSN.
++
++   first_set records the first place in the block where the register
++   is set and is used to compute "anticipatability".
++
++   last_set records the last place in the block where the register
++   is set and is used to compute "availability".
++
++   last_bb records the block for which first_set and last_set are
++   valid, as a quick test to invalidate them.
++
++   reg_set_in_block records whether the register is set in the block
++   and is used to compute "transparency".  */
++
++static void
++record_last_reg_set_info (insn, regno)
++     rtx insn;
++     int regno;
++{
++  struct reg_avail_info *info = &reg_avail_info[regno];
++  int cuid = INSN_CUID (insn);
++
++  info->last_set = cuid;
++  if (info->last_bb != current_bb)
++    {
++      info->last_bb = current_bb;
++      info->first_set = cuid;
++      SET_BIT (reg_set_in_block[current_bb->index], regno);
++    }
++}
++
++
++/* Record all of the canonicalized MEMs of record_last_mem_set_info's insn.
++   Note we store a pair of elements in the list, so they have to be
++   taken off pairwise.  */
++
++static void
++canon_list_insert (dest, unused1, v_insn)
++     rtx    dest ATTRIBUTE_UNUSED;
++     rtx    unused1 ATTRIBUTE_UNUSED;
++     void * v_insn;
++{
++  rtx dest_addr, insn;
++  int bb;
++
++  while (GET_CODE (dest) == SUBREG
++      || GET_CODE (dest) == ZERO_EXTRACT
++      || GET_CODE (dest) == SIGN_EXTRACT
++      || GET_CODE (dest) == STRICT_LOW_PART)
++    dest = XEXP (dest, 0);
++
++  /* If DEST is not a MEM, then it will not conflict with a load.  Note
++     that function calls are assumed to clobber memory, but are handled
++     elsewhere.  */
++
++  if (GET_CODE (dest) != MEM)
++    return;
++
++  dest_addr = get_addr (XEXP (dest, 0));
++  dest_addr = canon_rtx (dest_addr);
++  insn = (rtx) v_insn;
++  bb = BLOCK_NUM (insn);
++
++  canon_modify_mem_list[bb] =
++    alloc_EXPR_LIST (VOIDmode, dest_addr, canon_modify_mem_list[bb]);
++  canon_modify_mem_list[bb] =
++    alloc_EXPR_LIST (VOIDmode, dest, canon_modify_mem_list[bb]);
++  bitmap_set_bit (canon_modify_mem_list_set, bb);
++}
++
++/* Record memory modification information for INSN.  We do not actually care
++   about the memory location(s) that are set, or even how they are set (consider
++   a CALL_INSN).  We merely need to record which insns modify memory.  */
++
++static void
++record_last_mem_set_info (insn)
++     rtx insn;
++{
++  int bb = BLOCK_NUM (insn);
++
++  /* load_killed_in_block_p will handle the case of calls clobbering
++     everything.  */
++  modify_mem_list[bb] = alloc_INSN_LIST (insn, modify_mem_list[bb]);
++  bitmap_set_bit (modify_mem_list_set, bb);
++
++  if (GET_CODE (insn) == CALL_INSN)
++    {
++      /* Note that traversals of this loop (other than for free-ing)
++       will break after encountering a CALL_INSN.  So, there's no
++       need to insert a pair of items, as canon_list_insert does.  */
++      canon_modify_mem_list[bb] =
++      alloc_INSN_LIST (insn, canon_modify_mem_list[bb]);
++      bitmap_set_bit (canon_modify_mem_list_set, bb);
++    }
++  else
++    note_stores (PATTERN (insn), canon_list_insert, (void*) insn);
++}
++
++/* Called from compute_hash_table via note_stores to handle one
++   SET or CLOBBER in an insn.  DATA is really the instruction in which
++   the SET is taking place.  */
++
++static void
++record_last_set_info (dest, setter, data)
++     rtx dest, setter ATTRIBUTE_UNUSED;
++     void *data;
++{
++  rtx last_set_insn = (rtx) data;
++
++  if (GET_CODE (dest) == SUBREG)
++    dest = SUBREG_REG (dest);
++
++  if (GET_CODE (dest) == REG)
++    record_last_reg_set_info (last_set_insn, REGNO (dest));
++  else if (GET_CODE (dest) == MEM
++         /* Ignore pushes, they clobber nothing.  */
++         && ! push_operand (dest, GET_MODE (dest)))
++    record_last_mem_set_info (last_set_insn);
++}
++
++/* Top level function to create an expression or assignment hash table.
++
++   Expression entries are placed in the hash table if
++   - they are of the form (set (pseudo-reg) src),
++   - src is something we want to perform GCSE on,
++   - none of the operands are subsequently modified in the block
++
++   Assignment entries are placed in the hash table if
++   - they are of the form (set (pseudo-reg) src),
++   - src is something we want to perform const/copy propagation on,
++   - none of the operands or target are subsequently modified in the block
++
++   Currently src must be a pseudo-reg or a const_int.
++
++   F is the first insn.
++   TABLE is the table computed.  */
++
++static void
++compute_hash_table_work (table)
++     struct hash_table *table;
++{
++  unsigned int i;
++
++  /* While we compute the hash table we also compute a bit array of which
++     registers are set in which blocks.
++     ??? This isn't needed during const/copy propagation, but it's cheap to
++     compute.  Later.  */
++  sbitmap_vector_zero (reg_set_in_block, last_basic_block);
++
++  /* re-Cache any INSN_LIST nodes we have allocated.  */
++  clear_modify_mem_tables ();
++  /* Some working arrays used to track first and last set in each block.  */
++  reg_avail_info = (struct reg_avail_info*)
++    gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
++
++  for (i = 0; i < max_gcse_regno; ++i)
++    reg_avail_info[i].last_bb = NULL;
++
++  FOR_EACH_BB (current_bb)
++    {
++      rtx insn;
++      unsigned int regno;
++      int in_libcall_block;
++
++      /* First pass over the instructions records information used to
++       determine when registers and memory are first and last set.
++       ??? hard-reg reg_set_in_block computation
++       could be moved to compute_sets since they currently don't change.  */
++
++      for (insn = current_bb->head;
++         insn && insn != NEXT_INSN (current_bb->end);
++         insn = NEXT_INSN (insn))
++      {
++        if (! INSN_P (insn))
++          continue;
++
++        if (GET_CODE (insn) == CALL_INSN)
++          {
++            bool clobbers_all = false;
++#ifdef NON_SAVING_SETJMP
++            if (NON_SAVING_SETJMP
++                && find_reg_note (insn, REG_SETJMP, NULL_RTX))
++              clobbers_all = true;
++#endif
++
++            for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
++              if (clobbers_all
++                  || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
++                record_last_reg_set_info (insn, regno);
++
++            mark_call (insn);
++          }
++
++        note_stores (PATTERN (insn), record_last_set_info, insn);
++      }
++
++      /* The next pass builds the hash table.  */
++
++      for (insn = current_bb->head, in_libcall_block = 0;
++         insn && insn != NEXT_INSN (current_bb->end);
++         insn = NEXT_INSN (insn))
++      if (INSN_P (insn))
++        {
++          if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
++            in_libcall_block = 1;
++          else if (table->set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
++            in_libcall_block = 0;
++          hash_scan_insn (insn, table, in_libcall_block);
++          if (!table->set_p && find_reg_note (insn, REG_RETVAL, NULL_RTX))
++            in_libcall_block = 0;
++        }
++    }
++
++  free (reg_avail_info);
++  reg_avail_info = NULL;
++}
++
++/* Allocate space for the set/expr hash TABLE.
++   N_INSNS is the number of instructions in the function.
++   It is used to determine the number of buckets to use.
++   SET_P determines whether set or expression table will
++   be created.  */
++
++static void
++alloc_hash_table (n_insns, table, set_p)
++     int n_insns;
++     struct hash_table *table;
++     int set_p;
++{
++  int n;
++
++  table->size = n_insns / 4;
++  if (table->size < 11)
++    table->size = 11;
++
++  /* Attempt to maintain efficient use of hash table.
++     Making it an odd number is simplest for now.
++     ??? Later take some measurements.  */
++  table->size |= 1;
++  n = table->size * sizeof (struct expr *);
++  table->table = (struct expr **) gmalloc (n);
++  table->set_p = set_p;
++}
++
++/* Free things allocated by alloc_hash_table.  */
++
++static void
++free_hash_table (table)
++     struct hash_table *table;
++{
++  free (table->table);
++}
++
++/* Compute the hash TABLE for doing copy/const propagation or
++   expression hash table.  */
++
++static void
++compute_hash_table (table)
++    struct hash_table *table;
++{
++  /* Initialize count of number of entries in hash table.  */
++  table->n_elems = 0;
++  memset ((char *) table->table, 0,
++        table->size * sizeof (struct expr *));
++
++  compute_hash_table_work (table);
++}
++\f
++/* Expression tracking support.  */
++
++/* Lookup pattern PAT in the expression TABLE.
++   The result is a pointer to the table entry, or NULL if not found.  */
++
++static struct expr *
++lookup_expr (pat, table)
++     rtx pat;
++     struct hash_table *table;
++{
++  int do_not_record_p;
++  unsigned int hash = hash_expr (pat, GET_MODE (pat), &do_not_record_p,
++                               table->size);
++  struct expr *expr;
++
++  if (do_not_record_p)
++    return NULL;
++
++  expr = table->table[hash];
++
++  while (expr && ! expr_equiv_p (expr->expr, pat))
++    expr = expr->next_same_hash;
++
++  return expr;
++}
++
++/* Lookup REGNO in the set TABLE.  If PAT is non-NULL look for the entry that
++   matches it, otherwise return the first entry for REGNO.  The result is a
++   pointer to the table entry, or NULL if not found.  */
++
++static struct expr *
++lookup_set (regno, pat, table)
++     unsigned int regno;
++     rtx pat;
++     struct hash_table *table;
++{
++  unsigned int hash = hash_set (regno, table->size);
++  struct expr *expr;
++
++  expr = table->table[hash];
++
++  if (pat)
++    {
++      while (expr && ! expr_equiv_p (expr->expr, pat))
++      expr = expr->next_same_hash;
++    }
++  else
++    {
++      while (expr && REGNO (SET_DEST (expr->expr)) != regno)
++      expr = expr->next_same_hash;
++    }
++
++  return expr;
++}
++
++/* Return the next entry for REGNO in list EXPR.  */
++
++static struct expr *
++next_set (regno, expr)
++     unsigned int regno;
++     struct expr *expr;
++{
++  do
++    expr = expr->next_same_hash;
++  while (expr && REGNO (SET_DEST (expr->expr)) != regno);
++
++  return expr;
++}
++
++/* Like free_INSN_LIST_list or free_EXPR_LIST_list, except that the node
++   types may be mixed.  */
++
++static void
++free_insn_expr_list_list (listp)
++     rtx *listp;
++{
++  rtx list, next;
++
++  for (list = *listp; list ; list = next)
++    {
++      next = XEXP (list, 1);
++      if (GET_CODE (list) == EXPR_LIST)
++      free_EXPR_LIST_node (list);
++      else
++      free_INSN_LIST_node (list);
++    }
++
++  *listp = NULL;
++}
++
++/* Clear canon_modify_mem_list and modify_mem_list tables.  */
++static void
++clear_modify_mem_tables ()
++{
++  int i;
++
++  EXECUTE_IF_SET_IN_BITMAP
++    (modify_mem_list_set, 0, i, free_INSN_LIST_list (modify_mem_list + i));
++  bitmap_clear (modify_mem_list_set);
++
++  EXECUTE_IF_SET_IN_BITMAP
++    (canon_modify_mem_list_set, 0, i,
++     free_insn_expr_list_list (canon_modify_mem_list + i));
++  bitmap_clear (canon_modify_mem_list_set);
++}
++
++/* Release memory used by modify_mem_list_set and canon_modify_mem_list_set.  */
++
++static void
++free_modify_mem_tables ()
++{
++  clear_modify_mem_tables ();
++  free (modify_mem_list);
++  free (canon_modify_mem_list);
++  modify_mem_list = 0;
++  canon_modify_mem_list = 0;
++}
++
++/* Reset tables used to keep track of what's still available [since the
++   start of the block].  */
++
++static void
++reset_opr_set_tables ()
++{
++  /* Maintain a bitmap of which regs have been set since beginning of
++     the block.  */
++  CLEAR_REG_SET (reg_set_bitmap);
++
++  /* Also keep a record of the last instruction to modify memory.
++     For now this is very trivial, we only record whether any memory
++     location has been modified.  */
++  clear_modify_mem_tables ();
++}
++
++/* Return nonzero if the operands of X are not set before INSN in
++   INSN's basic block.  */
++
++static int
++oprs_not_set_p (x, insn)
++     rtx x, insn;
++{
++  int i, j;
++  enum rtx_code code;
++  const char *fmt;
++
++  if (x == 0)
++    return 1;
++
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case PC:
++    case CC0:
++    case CONST:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case LABEL_REF:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++      return 1;
++
++    case MEM:
++      if (load_killed_in_block_p (BLOCK_FOR_INSN (insn),
++                                INSN_CUID (insn), x, 0))
++      return 0;
++      else
++      return oprs_not_set_p (XEXP (x, 0), insn);
++
++    case REG:
++      return ! REGNO_REG_SET_P (reg_set_bitmap, REGNO (x));
++
++    default:
++      break;
++    }
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          return oprs_not_set_p (XEXP (x, i), insn);
++
++        if (! oprs_not_set_p (XEXP (x, i), insn))
++          return 0;
++      }
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        if (! oprs_not_set_p (XVECEXP (x, i, j), insn))
++          return 0;
++    }
++
++  return 1;
++}
++
++/* Mark things set by a CALL.  */
++
++static void
++mark_call (insn)
++     rtx insn;
++{
++  if (! CONST_OR_PURE_CALL_P (insn))
++    record_last_mem_set_info (insn);
++}
++
++/* Mark things set by a SET.  */
++
++static void
++mark_set (pat, insn)
++     rtx pat, insn;
++{
++  rtx dest = SET_DEST (pat);
++
++  while (GET_CODE (dest) == SUBREG
++       || GET_CODE (dest) == ZERO_EXTRACT
++       || GET_CODE (dest) == SIGN_EXTRACT
++       || GET_CODE (dest) == STRICT_LOW_PART)
++    dest = XEXP (dest, 0);
++
++  if (GET_CODE (dest) == REG)
++    SET_REGNO_REG_SET (reg_set_bitmap, REGNO (dest));
++  else if (GET_CODE (dest) == MEM)
++    record_last_mem_set_info (insn);
++
++  if (GET_CODE (SET_SRC (pat)) == CALL)
++    mark_call (insn);
++}
++
++/* Record things set by a CLOBBER.  */
++
++static void
++mark_clobber (pat, insn)
++     rtx pat, insn;
++{
++  rtx clob = XEXP (pat, 0);
++
++  while (GET_CODE (clob) == SUBREG || GET_CODE (clob) == STRICT_LOW_PART)
++    clob = XEXP (clob, 0);
++
++  if (GET_CODE (clob) == REG)
++    SET_REGNO_REG_SET (reg_set_bitmap, REGNO (clob));
++  else
++    record_last_mem_set_info (insn);
++}
++
++/* Record things set by INSN.
++   This data is used by oprs_not_set_p.  */
++
++static void
++mark_oprs_set (insn)
++     rtx insn;
++{
++  rtx pat = PATTERN (insn);
++  int i;
++
++  if (GET_CODE (pat) == SET)
++    mark_set (pat, insn);
++  else if (GET_CODE (pat) == PARALLEL)
++    for (i = 0; i < XVECLEN (pat, 0); i++)
++      {
++      rtx x = XVECEXP (pat, 0, i);
++
++      if (GET_CODE (x) == SET)
++        mark_set (x, insn);
++      else if (GET_CODE (x) == CLOBBER)
++        mark_clobber (x, insn);
++      else if (GET_CODE (x) == CALL)
++        mark_call (insn);
++      }
++
++  else if (GET_CODE (pat) == CLOBBER)
++    mark_clobber (pat, insn);
++  else if (GET_CODE (pat) == CALL)
++    mark_call (insn);
++}
++
++\f
++/* Classic GCSE reaching definition support.  */
++
++/* Allocate reaching def variables.  */
++
++static void
++alloc_rd_mem (n_blocks, n_insns)
++     int n_blocks, n_insns;
++{
++  rd_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
++  sbitmap_vector_zero (rd_kill, n_blocks);
++
++  rd_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
++  sbitmap_vector_zero (rd_gen, n_blocks);
++
++  reaching_defs = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
++  sbitmap_vector_zero (reaching_defs, n_blocks);
++
++  rd_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
++  sbitmap_vector_zero (rd_out, n_blocks);
++}
++
++/* Free reaching def variables.  */
++
++static void
++free_rd_mem ()
++{
++  sbitmap_vector_free (rd_kill);
++  sbitmap_vector_free (rd_gen);
++  sbitmap_vector_free (reaching_defs);
++  sbitmap_vector_free (rd_out);
++}
++
++/* Add INSN to the kills of BB.  REGNO, set in BB, is killed by INSN.  */
++
++static void
++handle_rd_kill_set (insn, regno, bb)
++     rtx insn;
++     int regno;
++     basic_block bb;
++{
++  struct reg_set *this_reg;
++
++  for (this_reg = reg_set_table[regno]; this_reg; this_reg = this_reg ->next)
++    if (BLOCK_NUM (this_reg->insn) != BLOCK_NUM (insn))
++      SET_BIT (rd_kill[bb->index], INSN_CUID (this_reg->insn));
++}
++
++/* Compute the set of kill's for reaching definitions.  */
++
++static void
++compute_kill_rd ()
++{
++  int cuid;
++  unsigned int regno;
++  int i;
++  basic_block bb;
++
++  /* For each block
++       For each set bit in `gen' of the block (i.e each insn which
++         generates a definition in the block)
++       Call the reg set by the insn corresponding to that bit regx
++       Look at the linked list starting at reg_set_table[regx]
++       For each setting of regx in the linked list, which is not in
++           this block
++         Set the bit in `kill' corresponding to that insn.  */
++  FOR_EACH_BB (bb)
++    for (cuid = 0; cuid < max_cuid; cuid++)
++      if (TEST_BIT (rd_gen[bb->index], cuid))
++      {
++        rtx insn = CUID_INSN (cuid);
++        rtx pat = PATTERN (insn);
++
++        if (GET_CODE (insn) == CALL_INSN)
++          {
++            for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
++              if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
++                handle_rd_kill_set (insn, regno, bb);
++          }
++
++        if (GET_CODE (pat) == PARALLEL)
++          {
++            for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
++              {
++                enum rtx_code code = GET_CODE (XVECEXP (pat, 0, i));
++
++                if ((code == SET || code == CLOBBER)
++                    && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == REG)
++                  handle_rd_kill_set (insn,
++                                      REGNO (XEXP (XVECEXP (pat, 0, i), 0)),
++                                      bb);
++              }
++          }
++        else if (GET_CODE (pat) == SET && GET_CODE (SET_DEST (pat)) == REG)
++          /* Each setting of this register outside of this block
++             must be marked in the set of kills in this block.  */
++          handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), bb);
++      }
++}
++
++/* Compute the reaching definitions as in
++   Compilers Principles, Techniques, and Tools. Aho, Sethi, Ullman,
++   Chapter 10.  It is the same algorithm as used for computing available
++   expressions but applied to the gens and kills of reaching definitions.  */
++
++static void
++compute_rd ()
++{
++  int changed, passes;
++  basic_block bb;
++
++  FOR_EACH_BB (bb)
++    sbitmap_copy (rd_out[bb->index] /*dst*/, rd_gen[bb->index] /*src*/);
++
++  passes = 0;
++  changed = 1;
++  while (changed)
++    {
++      changed = 0;
++      FOR_EACH_BB (bb)
++      {
++        sbitmap_union_of_preds (reaching_defs[bb->index], rd_out, bb->index);
++        changed |= sbitmap_union_of_diff_cg (rd_out[bb->index], rd_gen[bb->index],
++                                             reaching_defs[bb->index], rd_kill[bb->index]);
++      }
++      passes++;
++    }
++
++  if (gcse_file)
++    fprintf (gcse_file, "reaching def computation: %d passes\n", passes);
++}
++\f
++/* Classic GCSE available expression support.  */
++
++/* Allocate memory for available expression computation.  */
++
++static void
++alloc_avail_expr_mem (n_blocks, n_exprs)
++     int n_blocks, n_exprs;
++{
++  ae_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
++  sbitmap_vector_zero (ae_kill, n_blocks);
++
++  ae_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
++  sbitmap_vector_zero (ae_gen, n_blocks);
++
++  ae_in = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
++  sbitmap_vector_zero (ae_in, n_blocks);
++
++  ae_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
++  sbitmap_vector_zero (ae_out, n_blocks);
++}
++
++static void
++free_avail_expr_mem ()
++{
++  sbitmap_vector_free (ae_kill);
++  sbitmap_vector_free (ae_gen);
++  sbitmap_vector_free (ae_in);
++  sbitmap_vector_free (ae_out);
++}
++
++/* Compute the set of available expressions generated in each basic block.  */
++
++static void
++compute_ae_gen (expr_hash_table)
++     struct hash_table *expr_hash_table;
++{
++  unsigned int i;
++  struct expr *expr;
++  struct occr *occr;
++
++  /* For each recorded occurrence of each expression, set ae_gen[bb][expr].
++     This is all we have to do because an expression is not recorded if it
++     is not available, and the only expressions we want to work with are the
++     ones that are recorded.  */
++  for (i = 0; i < expr_hash_table->size; i++)
++    for (expr = expr_hash_table->table[i]; expr != 0; expr = expr->next_same_hash)
++      for (occr = expr->avail_occr; occr != 0; occr = occr->next)
++      SET_BIT (ae_gen[BLOCK_NUM (occr->insn)], expr->bitmap_index);
++}
++
++/* Return nonzero if expression X is killed in BB.  */
++
++static int
++expr_killed_p (x, bb)
++     rtx x;
++     basic_block bb;
++{
++  int i, j;
++  enum rtx_code code;
++  const char *fmt;
++
++  if (x == 0)
++    return 1;
++
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case REG:
++      return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
++
++    case MEM:
++      if (load_killed_in_block_p (bb, get_max_uid () + 1, x, 0))
++      return 1;
++      else
++      return expr_killed_p (XEXP (x, 0), bb);
++
++    case PC:
++    case CC0: /*FIXME*/
++    case CONST:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case LABEL_REF:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++      return 0;
++
++    default:
++      break;
++    }
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          return expr_killed_p (XEXP (x, i), bb);
++        else if (expr_killed_p (XEXP (x, i), bb))
++          return 1;
++      }
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        if (expr_killed_p (XVECEXP (x, i, j), bb))
++          return 1;
++    }
++
++  return 0;
++}
++
++/* Compute the set of available expressions killed in each basic block.  */
++
++static void
++compute_ae_kill (ae_gen, ae_kill, expr_hash_table)
++     sbitmap *ae_gen, *ae_kill;
++     struct hash_table *expr_hash_table;
++{
++  basic_block bb;
++  unsigned int i;
++  struct expr *expr;
++
++  FOR_EACH_BB (bb)
++    for (i = 0; i < expr_hash_table->size; i++)
++      for (expr = expr_hash_table->table[i]; expr; expr = expr->next_same_hash)
++      {
++        /* Skip EXPR if generated in this block.  */
++        if (TEST_BIT (ae_gen[bb->index], expr->bitmap_index))
++          continue;
++
++        if (expr_killed_p (expr->expr, bb))
++          SET_BIT (ae_kill[bb->index], expr->bitmap_index);
++      }
++}
++\f
++/* Actually perform the Classic GCSE optimizations.  */
++
++/* Return nonzero if occurrence OCCR of expression EXPR reaches block BB.
++
++   CHECK_SELF_LOOP is nonzero if we should consider a block reaching itself
++   as a positive reach.  We want to do this when there are two computations
++   of the expression in the block.
++
++   VISITED is a pointer to a working buffer for tracking which BB's have
++   been visited.  It is NULL for the top-level call.
++
++   We treat reaching expressions that go through blocks containing the same
++   reaching expression as "not reaching".  E.g. if EXPR is generated in blocks
++   2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
++   2 as not reaching.  The intent is to improve the probability of finding
++   only one reaching expression and to reduce register lifetimes by picking
++   the closest such expression.  */
++
++static int
++expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited)
++     struct occr *occr;
++     struct expr *expr;
++     basic_block bb;
++     int check_self_loop;
++     char *visited;
++{
++  edge pred;
++
++  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
++    {
++      basic_block pred_bb = pred->src;
++
++      if (visited[pred_bb->index])
++      /* This predecessor has already been visited. Nothing to do.  */
++        ;
++      else if (pred_bb == bb)
++      {
++        /* BB loops on itself.  */
++        if (check_self_loop
++            && TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index)
++            && BLOCK_NUM (occr->insn) == pred_bb->index)
++          return 1;
++
++        visited[pred_bb->index] = 1;
++      }
++
++      /* Ignore this predecessor if it kills the expression.  */
++      else if (TEST_BIT (ae_kill[pred_bb->index], expr->bitmap_index))
++      visited[pred_bb->index] = 1;
++
++      /* Does this predecessor generate this expression?  */
++      else if (TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index))
++      {
++        /* Is this the occurrence we're looking for?
++           Note that there's only one generating occurrence per block
++           so we just need to check the block number.  */
++        if (BLOCK_NUM (occr->insn) == pred_bb->index)
++          return 1;
++
++        visited[pred_bb->index] = 1;
++      }
++
++      /* Neither gen nor kill.  */
++      else
++      {
++        visited[pred_bb->index] = 1;
++        if (expr_reaches_here_p_work (occr, expr, pred_bb, check_self_loop,
++            visited))
++
++          return 1;
++      }
++    }
++
++  /* All paths have been checked.  */
++  return 0;
++}
++
++/* This wrapper for expr_reaches_here_p_work() is to ensure that any
++   memory allocated for that function is returned.  */
++
++static int
++expr_reaches_here_p (occr, expr, bb, check_self_loop)
++     struct occr *occr;
++     struct expr *expr;
++     basic_block bb;
++     int check_self_loop;
++{
++  int rval;
++  char *visited = (char *) xcalloc (last_basic_block, 1);
++
++  rval = expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited);
++
++  free (visited);
++  return rval;
++}
++
++/* Return the instruction that computes EXPR that reaches INSN's basic block.
++   If there is more than one such instruction, return NULL.
++
++   Called only by handle_avail_expr.  */
++
++static rtx
++computing_insn (expr, insn)
++     struct expr *expr;
++     rtx insn;
++{
++  basic_block bb = BLOCK_FOR_INSN (insn);
++
++  if (expr->avail_occr->next == NULL)
++    {
++      if (BLOCK_FOR_INSN (expr->avail_occr->insn) == bb)
++      /* The available expression is actually itself
++         (i.e. a loop in the flow graph) so do nothing.  */
++      return NULL;
++
++      /* (FIXME) Case that we found a pattern that was created by
++       a substitution that took place.  */
++      return expr->avail_occr->insn;
++    }
++  else
++    {
++      /* Pattern is computed more than once.
++       Search backwards from this insn to see how many of these
++       computations actually reach this insn.  */
++      struct occr *occr;
++      rtx insn_computes_expr = NULL;
++      int can_reach = 0;
++
++      for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
++      {
++        if (BLOCK_FOR_INSN (occr->insn) == bb)
++          {
++            /* The expression is generated in this block.
++               The only time we care about this is when the expression
++               is generated later in the block [and thus there's a loop].
++               We let the normal cse pass handle the other cases.  */
++            if (INSN_CUID (insn) < INSN_CUID (occr->insn)
++                && expr_reaches_here_p (occr, expr, bb, 1))
++              {
++                can_reach++;
++                if (can_reach > 1)
++                  return NULL;
++
++                insn_computes_expr = occr->insn;
++              }
++          }
++        else if (expr_reaches_here_p (occr, expr, bb, 0))
++          {
++            can_reach++;
++            if (can_reach > 1)
++              return NULL;
++
++            insn_computes_expr = occr->insn;
++          }
++      }
++
++      if (insn_computes_expr == NULL)
++      abort ();
++
++      return insn_computes_expr;
++    }
++}
++
++/* Return nonzero if the definition in DEF_INSN can reach INSN.
++   Only called by can_disregard_other_sets.  */
++
++static int
++def_reaches_here_p (insn, def_insn)
++     rtx insn, def_insn;
++{
++  rtx reg;
++
++  if (TEST_BIT (reaching_defs[BLOCK_NUM (insn)], INSN_CUID (def_insn)))
++    return 1;
++
++  if (BLOCK_NUM (insn) == BLOCK_NUM (def_insn))
++    {
++      if (INSN_CUID (def_insn) < INSN_CUID (insn))
++      {
++        if (GET_CODE (PATTERN (def_insn)) == PARALLEL)
++          return 1;
++        else if (GET_CODE (PATTERN (def_insn)) == CLOBBER)
++          reg = XEXP (PATTERN (def_insn), 0);
++        else if (GET_CODE (PATTERN (def_insn)) == SET)
++          reg = SET_DEST (PATTERN (def_insn));
++        else
++          abort ();
++
++        return ! reg_set_between_p (reg, NEXT_INSN (def_insn), insn);
++      }
++      else
++      return 0;
++    }
++
++  return 0;
++}
++
++/* Return nonzero if *ADDR_THIS_REG can only have one value at INSN.  The
++   value returned is the number of definitions that reach INSN.  Returning a
++   value of zero means that [maybe] more than one definition reaches INSN and
++   the caller can't perform whatever optimization it is trying.  i.e. it is
++   always safe to return zero.  */
++
++static int
++can_disregard_other_sets (addr_this_reg, insn, for_combine)
++     struct reg_set **addr_this_reg;
++     rtx insn;
++     int for_combine;
++{
++  int number_of_reaching_defs = 0;
++  struct reg_set *this_reg;
++
++  for (this_reg = *addr_this_reg; this_reg != 0; this_reg = this_reg->next)
++    if (def_reaches_here_p (insn, this_reg->insn))
++      {
++      number_of_reaching_defs++;
++      /* Ignore parallels for now.  */
++      if (GET_CODE (PATTERN (this_reg->insn)) == PARALLEL)
++        return 0;
++
++      if (!for_combine
++          && (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER
++              || ! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
++                                SET_SRC (PATTERN (insn)))))
++        /* A setting of the reg to a different value reaches INSN.  */
++        return 0;
++
++      if (number_of_reaching_defs > 1)
++        {
++          /* If in this setting the value the register is being set to is
++             equal to the previous value the register was set to and this
++             setting reaches the insn we are trying to do the substitution
++             on then we are ok.  */
++          if (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER)
++            return 0;
++          else if (! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
++                                  SET_SRC (PATTERN (insn))))
++            return 0;
++        }
++
++      *addr_this_reg = this_reg;
++      }
++
++  return number_of_reaching_defs;
++}
++
++/* Expression computed by insn is available and the substitution is legal,
++   so try to perform the substitution.
++
++   The result is nonzero if any changes were made.  */
++
++static int
++handle_avail_expr (insn, expr)
++     rtx insn;
++     struct expr *expr;
++{
++  rtx pat, insn_computes_expr, expr_set;
++  rtx to;
++  struct reg_set *this_reg;
++  int found_setting, use_src;
++  int changed = 0;
++
++  /* We only handle the case where one computation of the expression
++     reaches this instruction.  */
++  insn_computes_expr = computing_insn (expr, insn);
++  if (insn_computes_expr == NULL)
++    return 0;
++  expr_set = single_set (insn_computes_expr);
++  if (!expr_set)
++    abort ();
++
++  found_setting = 0;
++  use_src = 0;
++
++  /* At this point we know only one computation of EXPR outside of this
++     block reaches this insn.  Now try to find a register that the
++     expression is computed into.  */
++  if (GET_CODE (SET_SRC (expr_set)) == REG)
++    {
++      /* This is the case when the available expression that reaches
++       here has already been handled as an available expression.  */
++      unsigned int regnum_for_replacing
++      = REGNO (SET_SRC (expr_set));
++
++      /* If the register was created by GCSE we can't use `reg_set_table',
++       however we know it's set only once.  */
++      if (regnum_for_replacing >= max_gcse_regno
++        /* If the register the expression is computed into is set only once,
++           or only one set reaches this insn, we can use it.  */
++        || (((this_reg = reg_set_table[regnum_for_replacing]),
++             this_reg->next == NULL)
++            || can_disregard_other_sets (&this_reg, insn, 0)))
++      {
++        use_src = 1;
++        found_setting = 1;
++      }
++    }
++
++  if (!found_setting)
++    {
++      unsigned int regnum_for_replacing
++      = REGNO (SET_DEST (expr_set));
++
++      /* This shouldn't happen.  */
++      if (regnum_for_replacing >= max_gcse_regno)
++      abort ();
++
++      this_reg = reg_set_table[regnum_for_replacing];
++
++      /* If the register the expression is computed into is set only once,
++       or only one set reaches this insn, use it.  */
++      if (this_reg->next == NULL
++        || can_disregard_other_sets (&this_reg, insn, 0))
++      found_setting = 1;
++    }
++
++  if (found_setting)
++    {
++      pat = PATTERN (insn);
++      if (use_src)
++      to = SET_SRC (expr_set);
++      else
++      to = SET_DEST (expr_set);
++      changed = validate_change (insn, &SET_SRC (pat), to, 0);
++
++      /* We should be able to ignore the return code from validate_change but
++       to play it safe we check.  */
++      if (changed)
++      {
++        gcse_subst_count++;
++        if (gcse_file != NULL)
++          {
++            fprintf (gcse_file, "GCSE: Replacing the source in insn %d with",
++                     INSN_UID (insn));
++            fprintf (gcse_file, " reg %d %s insn %d\n",
++                     REGNO (to), use_src ? "from" : "set in",
++                     INSN_UID (insn_computes_expr));
++          }
++      }
++    }
++
++  /* The register that the expr is computed into is set more than once.  */
++  else if (1 /*expensive_op(this_pattrn->op) && do_expensive_gcse)*/)
++    {
++      /* Insert an insn after insnx that copies the reg set in insnx
++       into a new pseudo register call this new register REGN.
++       From insnb until end of basic block or until REGB is set
++       replace all uses of REGB with REGN.  */
++      rtx new_insn;
++
++      to = gen_reg_rtx (GET_MODE (SET_DEST (expr_set)));
++
++      /* Generate the new insn.  */
++      /* ??? If the change fails, we return 0, even though we created
++       an insn.  I think this is ok.  */
++      new_insn
++      = emit_insn_after (gen_rtx_SET (VOIDmode, to,
++                                      SET_DEST (expr_set)),
++                         insn_computes_expr);
++
++      /* Keep register set table up to date.  */
++      record_one_set (REGNO (to), new_insn);
++
++      gcse_create_count++;
++      if (gcse_file != NULL)
++      {
++        fprintf (gcse_file, "GCSE: Creating insn %d to copy value of reg %d",
++                 INSN_UID (NEXT_INSN (insn_computes_expr)),
++                 REGNO (SET_SRC (PATTERN (NEXT_INSN (insn_computes_expr)))));
++        fprintf (gcse_file, ", computed in insn %d,\n",
++                 INSN_UID (insn_computes_expr));
++        fprintf (gcse_file, "      into newly allocated reg %d\n",
++                 REGNO (to));
++      }
++
++      pat = PATTERN (insn);
++
++      /* Do register replacement for INSN.  */
++      changed = validate_change (insn, &SET_SRC (pat),
++                               SET_DEST (PATTERN
++                                         (NEXT_INSN (insn_computes_expr))),
++                               0);
++
++      /* We should be able to ignore the return code from validate_change but
++       to play it safe we check.  */
++      if (changed)
++      {
++        gcse_subst_count++;
++        if (gcse_file != NULL)
++          {
++            fprintf (gcse_file,
++                     "GCSE: Replacing the source in insn %d with reg %d ",
++                     INSN_UID (insn),
++                     REGNO (SET_DEST (PATTERN (NEXT_INSN
++                                               (insn_computes_expr)))));
++            fprintf (gcse_file, "set in insn %d\n",
++                     INSN_UID (insn_computes_expr));
++          }
++      }
++    }
++
++  return changed;
++}
++
++/* Perform classic GCSE.  This is called by one_classic_gcse_pass after all
++   the dataflow analysis has been done.
++
++   The result is nonzero if a change was made.  */
++
++static int
++classic_gcse ()
++{
++  int changed;
++  rtx insn;
++  basic_block bb;
++
++  /* Note we start at block 1.  */
++
++  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
++    return 0;
++
++  changed = 0;
++  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
++    {
++      /* Reset tables used to keep track of what's still valid [since the
++       start of the block].  */
++      reset_opr_set_tables ();
++
++      for (insn = bb->head;
++         insn != NULL && insn != NEXT_INSN (bb->end);
++         insn = NEXT_INSN (insn))
++      {
++        /* Is insn of form (set (pseudo-reg) ...)?  */
++        if (GET_CODE (insn) == INSN
++            && GET_CODE (PATTERN (insn)) == SET
++            && GET_CODE (SET_DEST (PATTERN (insn))) == REG
++            && REGNO (SET_DEST (PATTERN (insn))) >= FIRST_PSEUDO_REGISTER)
++          {
++            rtx pat = PATTERN (insn);
++            rtx src = SET_SRC (pat);
++            struct expr *expr;
++
++            if (want_to_gcse_p (src)
++                /* Is the expression recorded?  */
++                && ((expr = lookup_expr (src, &expr_hash_table)) != NULL)
++                /* Is the expression available [at the start of the
++                   block]?  */
++                && TEST_BIT (ae_in[bb->index], expr->bitmap_index)
++                /* Are the operands unchanged since the start of the
++                   block?  */
++                && oprs_not_set_p (src, insn))
++              changed |= handle_avail_expr (insn, expr);
++          }
++
++        /* Keep track of everything modified by this insn.  */
++        /* ??? Need to be careful w.r.t. mods done to INSN.  */
++        if (INSN_P (insn))
++          mark_oprs_set (insn);
++      }
++    }
++
++  return changed;
++}
++
++/* Top level routine to perform one classic GCSE pass.
++
++   Return nonzero if a change was made.  */
++
++static int
++one_classic_gcse_pass (pass)
++     int pass;
++{
++  int changed = 0;
++
++  gcse_subst_count = 0;
++  gcse_create_count = 0;
++
++  alloc_hash_table (max_cuid, &expr_hash_table, 0);
++  alloc_rd_mem (last_basic_block, max_cuid);
++  compute_hash_table (&expr_hash_table);
++  if (gcse_file)
++    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
++
++  if (expr_hash_table.n_elems > 0)
++    {
++      compute_kill_rd ();
++      compute_rd ();
++      alloc_avail_expr_mem (last_basic_block, expr_hash_table.n_elems);
++      compute_ae_gen (&expr_hash_table);
++      compute_ae_kill (ae_gen, ae_kill, &expr_hash_table);
++      compute_available (ae_gen, ae_kill, ae_out, ae_in);
++      changed = classic_gcse ();
++      free_avail_expr_mem ();
++    }
++
++  free_rd_mem ();
++  free_hash_table (&expr_hash_table);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "\n");
++      fprintf (gcse_file, "GCSE of %s, pass %d: %d bytes needed, %d substs,",
++             current_function_name, pass, bytes_used, gcse_subst_count);
++      fprintf (gcse_file, "%d insns created\n", gcse_create_count);
++    }
++
++  return changed;
++}
++\f
++/* Compute copy/constant propagation working variables.  */
++
++/* Local properties of assignments.  */
++static sbitmap *cprop_pavloc;
++static sbitmap *cprop_absaltered;
++
++/* Global properties of assignments (computed from the local properties).  */
++static sbitmap *cprop_avin;
++static sbitmap *cprop_avout;
++
++/* Allocate vars used for copy/const propagation.  N_BLOCKS is the number of
++   basic blocks.  N_SETS is the number of sets.  */
++
++static void
++alloc_cprop_mem (n_blocks, n_sets)
++     int n_blocks, n_sets;
++{
++  cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets);
++  cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets);
++
++  cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets);
++  cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets);
++}
++
++/* Free vars used by copy/const propagation.  */
++
++static void
++free_cprop_mem ()
++{
++  sbitmap_vector_free (cprop_pavloc);
++  sbitmap_vector_free (cprop_absaltered);
++  sbitmap_vector_free (cprop_avin);
++  sbitmap_vector_free (cprop_avout);
++}
++
++/* For each block, compute whether X is transparent.  X is either an
++   expression or an assignment [though we don't care which, for this context
++   an assignment is treated as an expression].  For each block where an
++   element of X is modified, set (SET_P == 1) or reset (SET_P == 0) the INDX
++   bit in BMAP.  */
++
++static void
++compute_transp (x, indx, bmap, set_p)
++     rtx x;
++     int indx;
++     sbitmap *bmap;
++     int set_p;
++{
++  int i, j;
++  basic_block bb;
++  enum rtx_code code;
++  reg_set *r;
++  const char *fmt;
++
++  /* repeat is used to turn tail-recursion into iteration since GCC
++     can't do it when there's no return value.  */
++ repeat:
++
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case REG:
++      if (set_p)
++      {
++        if (REGNO (x) < FIRST_PSEUDO_REGISTER)
++          {
++            FOR_EACH_BB (bb)
++              if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
++                SET_BIT (bmap[bb->index], indx);
++          }
++        else
++          {
++            for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
++              SET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
++          }
++      }
++      else
++      {
++        if (REGNO (x) < FIRST_PSEUDO_REGISTER)
++          {
++            FOR_EACH_BB (bb)
++              if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
++                RESET_BIT (bmap[bb->index], indx);
++          }
++        else
++          {
++            for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
++              RESET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
++          }
++      }
++
++      return;
++
++    case MEM:
++      FOR_EACH_BB (bb)
++      {
++        rtx list_entry = canon_modify_mem_list[bb->index];
++
++        while (list_entry)
++          {
++            rtx dest, dest_addr;
++
++            if (GET_CODE (XEXP (list_entry, 0)) == CALL_INSN)
++              {
++                if (set_p)
++                  SET_BIT (bmap[bb->index], indx);
++                else
++                  RESET_BIT (bmap[bb->index], indx);
++                break;
++              }
++            /* LIST_ENTRY must be an INSN of some kind that sets memory.
++               Examine each hunk of memory that is modified.  */
++
++            dest = XEXP (list_entry, 0);
++            list_entry = XEXP (list_entry, 1);
++            dest_addr = XEXP (list_entry, 0);
++
++            if (canon_true_dependence (dest, GET_MODE (dest), dest_addr,
++                                       x, rtx_addr_varies_p))
++              {
++                if (set_p)
++                  SET_BIT (bmap[bb->index], indx);
++                else
++                  RESET_BIT (bmap[bb->index], indx);
++                break;
++              }
++            list_entry = XEXP (list_entry, 1);
++          }
++      }
++
++      x = XEXP (x, 0);
++      goto repeat;
++
++    case PC:
++    case CC0: /*FIXME*/
++    case CONST:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case LABEL_REF:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++      return;
++
++    default:
++      break;
++    }
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          {
++            x = XEXP (x, i);
++            goto repeat;
++          }
++
++        compute_transp (XEXP (x, i), indx, bmap, set_p);
++      }
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        compute_transp (XVECEXP (x, i, j), indx, bmap, set_p);
++    }
++}
++
++/* Top level routine to do the dataflow analysis needed by copy/const
++   propagation.  */
++
++static void
++compute_cprop_data ()
++{
++  compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, &set_hash_table);
++  compute_available (cprop_pavloc, cprop_absaltered,
++                   cprop_avout, cprop_avin);
++}
++\f
++/* Copy/constant propagation.  */
++
++/* Maximum number of register uses in an insn that we handle.  */
++#define MAX_USES 8
++
++/* Table of uses found in an insn.
++   Allocated statically to avoid alloc/free complexity and overhead.  */
++static struct reg_use reg_use_table[MAX_USES];
++
++/* Index into `reg_use_table' while building it.  */
++static int reg_use_count;
++
++/* Set up a list of register numbers used in INSN.  The found uses are stored
++   in `reg_use_table'.  `reg_use_count' is initialized to zero before entry,
++   and contains the number of uses in the table upon exit.
++
++   ??? If a register appears multiple times we will record it multiple times.
++   This doesn't hurt anything but it will slow things down.  */
++
++static void
++find_used_regs (xptr, data)
++     rtx *xptr;
++     void *data ATTRIBUTE_UNUSED;
++{
++  int i, j;
++  enum rtx_code code;
++  const char *fmt;
++  rtx x = *xptr;
++
++  /* repeat is used to turn tail-recursion into iteration since GCC
++     can't do it when there's no return value.  */
++ repeat:
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++  if (REG_P (x))
++    {
++      if (reg_use_count == MAX_USES)
++      return;
++
++      reg_use_table[reg_use_count].reg_rtx = x;
++      reg_use_count++;
++    }
++
++  /* Recursively scan the operands of this expression.  */
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          {
++            x = XEXP (x, 0);
++            goto repeat;
++          }
++
++        find_used_regs (&XEXP (x, i), data);
++      }
++      else if (fmt[i] == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++        find_used_regs (&XVECEXP (x, i, j), data);
++    }
++}
++
++/* Try to replace all non-SET_DEST occurrences of FROM in INSN with TO.
++   Returns nonzero is successful.  */
++
++static int
++try_replace_reg (from, to, insn)
++     rtx from, to, insn;
++{
++  rtx note = find_reg_equal_equiv_note (insn);
++  rtx src = 0;
++  int success = 0;
++  rtx set = single_set (insn);
++
++  validate_replace_src_group (from, to, insn);
++  if (num_changes_pending () && apply_change_group ())
++    success = 1;
++
++  /* Try to simplify SET_SRC if we have substituted a constant.  */
++  if (success && set && CONSTANT_P (to))
++    {
++      src = simplify_rtx (SET_SRC (set));
++
++      if (src)
++      validate_change (insn, &SET_SRC (set), src, 0);
++    }
++
++  if (!success && set && reg_mentioned_p (from, SET_SRC (set)))
++    {
++      /* If above failed and this is a single set, try to simplify the source of
++       the set given our substitution.  We could perhaps try this for multiple
++       SETs, but it probably won't buy us anything.  */
++      src = simplify_replace_rtx (SET_SRC (set), from, to);
++
++      if (!rtx_equal_p (src, SET_SRC (set))
++        && validate_change (insn, &SET_SRC (set), src, 0))
++      success = 1;
++
++      /* If we've failed to do replacement, have a single SET, don't already
++       have a note, and have no special SET, add a REG_EQUAL note to not
++       lose information.  */
++      if (!success && note == 0 && set != 0
++        && GET_CODE (XEXP (set, 0)) != ZERO_EXTRACT
++        && GET_CODE (XEXP (set, 0)) != SIGN_EXTRACT)
++      note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (src));
++    }
++
++  /* If there is already a NOTE, update the expression in it with our
++     replacement.  */
++  else if (note != 0)
++    XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), from, to);
++
++  /* REG_EQUAL may get simplified into register.
++     We don't allow that. Remove that note. This code ought
++     not to hapen, because previous code ought to syntetize
++     reg-reg move, but be on the safe side.  */
++  if (note && REG_P (XEXP (note, 0)))
++    remove_note (insn, note);
++
++  return success;
++}
++
++/* Find a set of REGNOs that are available on entry to INSN's block.  Returns
++   NULL no such set is found.  */
++
++static struct expr *
++find_avail_set (regno, insn)
++     int regno;
++     rtx insn;
++{
++  /* SET1 contains the last set found that can be returned to the caller for
++     use in a substitution.  */
++  struct expr *set1 = 0;
++
++  /* Loops are not possible here.  To get a loop we would need two sets
++     available at the start of the block containing INSN.  ie we would
++     need two sets like this available at the start of the block:
++
++       (set (reg X) (reg Y))
++       (set (reg Y) (reg X))
++
++     This can not happen since the set of (reg Y) would have killed the
++     set of (reg X) making it unavailable at the start of this block.  */
++  while (1)
++    {
++      rtx src;
++      struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
++
++      /* Find a set that is available at the start of the block
++       which contains INSN.  */
++      while (set)
++      {
++        if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
++          break;
++        set = next_set (regno, set);
++      }
++
++      /* If no available set was found we've reached the end of the
++       (possibly empty) copy chain.  */
++      if (set == 0)
++      break;
++
++      if (GET_CODE (set->expr) != SET)
++      abort ();
++
++      src = SET_SRC (set->expr);
++
++      /* We know the set is available.
++       Now check that SRC is ANTLOC (i.e. none of the source operands
++       have changed since the start of the block).
++
++         If the source operand changed, we may still use it for the next
++         iteration of this loop, but we may not use it for substitutions.  */
++
++      if (CONSTANT_P (src) || oprs_not_set_p (src, insn))
++      set1 = set;
++
++      /* If the source of the set is anything except a register, then
++       we have reached the end of the copy chain.  */
++      if (GET_CODE (src) != REG)
++      break;
++
++      /* Follow the copy chain, ie start another iteration of the loop
++       and see if we have an available copy into SRC.  */
++      regno = REGNO (src);
++    }
++
++  /* SET1 holds the last set that was available and anticipatable at
++     INSN.  */
++  return set1;
++}
++
++/* Subroutine of cprop_insn that tries to propagate constants into
++   JUMP_INSNS.  JUMP must be a conditional jump.  If SETCC is non-NULL
++   it is the instruction that immediately preceeds JUMP, and must be a
++   single SET of a register.  FROM is what we will try to replace,
++   SRC is the constant we will try to substitute for it.  Returns nonzero
++   if a change was made.  */
++
++static int
++cprop_jump (bb, setcc, jump, from, src)
++     basic_block bb;
++     rtx setcc;
++     rtx jump;
++     rtx from;
++     rtx src;
++{
++  rtx new, new_set;
++  rtx set = pc_set (jump);
++
++  /* First substitute in the INSN condition as the SET_SRC of the JUMP,
++     then substitute that given values in this expanded JUMP.  */
++  if (setcc != NULL
++      && !modified_between_p (from, setcc, jump)
++      && !modified_between_p (src, setcc, jump))
++    {
++      rtx setcc_set = single_set (setcc);
++      new_set = simplify_replace_rtx (SET_SRC (set),
++                                    SET_DEST (setcc_set),
++                                    SET_SRC (setcc_set));
++    }
++  else
++    new_set = set;
++
++  new = simplify_replace_rtx (new_set, from, src);
++
++  /* If no simplification can be made, then try the next
++     register.  */
++  if (rtx_equal_p (new, new_set) || rtx_equal_p (new, SET_SRC (set)))
++    return 0;
++
++  /* If this is now a no-op delete it, otherwise this must be a valid insn.  */
++  if (new == pc_rtx)
++    delete_insn (jump);
++  else
++    {
++      /* Ensure the value computed inside the jump insn to be equivalent
++         to one computed by setcc.  */
++      if (setcc 
++        && modified_in_p (new, setcc))
++      return 0;
++      if (! validate_change (jump, &SET_SRC (set), new, 0))
++      return 0;
++
++      /* If this has turned into an unconditional jump,
++       then put a barrier after it so that the unreachable
++       code will be deleted.  */
++      if (GET_CODE (SET_SRC (set)) == LABEL_REF)
++      emit_barrier_after (jump);
++     }
++
++#ifdef HAVE_cc0
++  /* Delete the cc0 setter.  */
++  if (setcc != NULL && CC0_P (SET_DEST (single_set (setcc))))
++    delete_insn (setcc);
++#endif
++
++  run_jump_opt_after_gcse = 1;
++
++  const_prop_count++;
++  if (gcse_file != NULL)
++    {
++      fprintf (gcse_file,
++             "CONST-PROP: Replacing reg %d in jump_insn %d with constant ",
++             REGNO (from), INSN_UID (jump));
++      print_rtl (gcse_file, src);
++      fprintf (gcse_file, "\n");
++    }
++  purge_dead_edges (bb);
++
++  return 1;
++}
++
++static bool
++constprop_register (insn, from, to, alter_jumps)
++     rtx insn;
++     rtx from;
++     rtx to;
++     int alter_jumps;
++{
++  rtx sset;
++
++  /* Check for reg or cc0 setting instructions followed by
++     conditional branch instructions first.  */
++  if (alter_jumps
++      && (sset = single_set (insn)) != NULL
++      && NEXT_INSN (insn)
++      && any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
++    {
++      rtx dest = SET_DEST (sset);
++      if ((REG_P (dest) || CC0_P (dest))
++        && cprop_jump (BLOCK_FOR_INSN (insn), insn, NEXT_INSN (insn), from, to))
++      return 1;
++    }
++
++  /* Handle normal insns next.  */
++  if (GET_CODE (insn) == INSN
++      && try_replace_reg (from, to, insn))
++    return 1;
++
++  /* Try to propagate a CONST_INT into a conditional jump.
++     We're pretty specific about what we will handle in this
++     code, we can extend this as necessary over time.
++
++     Right now the insn in question must look like
++     (set (pc) (if_then_else ...))  */
++  else if (alter_jumps && any_condjump_p (insn) && onlyjump_p (insn))
++    return cprop_jump (BLOCK_FOR_INSN (insn), NULL, insn, from, to);
++  return 0;
++}
++
++/* Perform constant and copy propagation on INSN.
++   The result is nonzero if a change was made.  */
++
++static int
++cprop_insn (insn, alter_jumps)
++     rtx insn;
++     int alter_jumps;
++{
++  struct reg_use *reg_used;
++  int changed = 0;
++  rtx note;
++
++  if (!INSN_P (insn))
++    return 0;
++
++  reg_use_count = 0;
++  note_uses (&PATTERN (insn), find_used_regs, NULL);
++
++  note = find_reg_equal_equiv_note (insn);
++
++  /* We may win even when propagating constants into notes.  */
++  if (note)
++    find_used_regs (&XEXP (note, 0), NULL);
++
++  for (reg_used = &reg_use_table[0]; reg_use_count > 0;
++       reg_used++, reg_use_count--)
++    {
++      unsigned int regno = REGNO (reg_used->reg_rtx);
++      rtx pat, src;
++      struct expr *set;
++
++      /* Ignore registers created by GCSE.
++       We do this because ...  */
++      if (regno >= max_gcse_regno)
++      continue;
++
++      /* If the register has already been set in this block, there's
++       nothing we can do.  */
++      if (! oprs_not_set_p (reg_used->reg_rtx, insn))
++      continue;
++
++      /* Find an assignment that sets reg_used and is available
++       at the start of the block.  */
++      set = find_avail_set (regno, insn);
++      if (! set)
++      continue;
++
++      pat = set->expr;
++      /* ??? We might be able to handle PARALLELs.  Later.  */
++      if (GET_CODE (pat) != SET)
++      abort ();
++
++      src = SET_SRC (pat);
++
++      /* Constant propagation.  */
++      if (CONSTANT_P (src))
++      {
++          if (constprop_register (insn, reg_used->reg_rtx, src, alter_jumps))
++          {
++            changed = 1;
++            const_prop_count++;
++            if (gcse_file != NULL)
++              {
++                fprintf (gcse_file, "GLOBAL CONST-PROP: Replacing reg %d in ", regno);
++                fprintf (gcse_file, "insn %d with constant ", INSN_UID (insn));
++                print_rtl (gcse_file, src);
++                fprintf (gcse_file, "\n");
++              }
++          }
++      }
++      else if (GET_CODE (src) == REG
++             && REGNO (src) >= FIRST_PSEUDO_REGISTER
++             && REGNO (src) != regno)
++      {
++        if (try_replace_reg (reg_used->reg_rtx, src, insn))
++          {
++            changed = 1;
++            copy_prop_count++;
++            if (gcse_file != NULL)
++              {
++                fprintf (gcse_file, "GLOBAL COPY-PROP: Replacing reg %d in insn %d",
++                         regno, INSN_UID (insn));
++                fprintf (gcse_file, " with reg %d\n", REGNO (src));
++              }
++
++            /* The original insn setting reg_used may or may not now be
++               deletable.  We leave the deletion to flow.  */
++            /* FIXME: If it turns out that the insn isn't deletable,
++               then we may have unnecessarily extended register lifetimes
++               and made things worse.  */
++          }
++      }
++    }
++
++  return changed;
++}
++
++/* Like find_used_regs, but avoid recording uses that appear in
++   input-output contexts such as zero_extract or pre_dec.  This
++   restricts the cases we consider to those for which local cprop
++   can legitimately make replacements.  */
++
++static void
++local_cprop_find_used_regs (xptr, data)
++     rtx *xptr;
++     void *data;
++{
++  rtx x = *xptr;
++
++  if (x == 0)
++    return;
++
++  switch (GET_CODE (x))
++    {
++    case ZERO_EXTRACT:
++    case SIGN_EXTRACT:
++    case STRICT_LOW_PART:
++      return;
++
++    case PRE_DEC:
++    case PRE_INC:
++    case POST_DEC:
++    case POST_INC:
++    case PRE_MODIFY:
++    case POST_MODIFY:
++      /* Can only legitimately appear this early in the context of
++       stack pushes for function arguments, but handle all of the
++       codes nonetheless.  */
++      return;
++
++    case SUBREG:
++      /* Setting a subreg of a register larger than word_mode leaves
++       the non-written words unchanged.  */
++      if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD)
++      return;
++      break;
++
++    default:
++      break;
++    }
++
++  find_used_regs (xptr, data);
++}
++  
++/* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
++   their REG_EQUAL notes need updating.  */
++
++static bool
++do_local_cprop (x, insn, alter_jumps, libcall_sp)
++     rtx x;
++     rtx insn;
++     int alter_jumps;
++     rtx *libcall_sp;
++{
++  rtx newreg = NULL, newcnst = NULL;
++
++  /* Rule out USE instructions and ASM statements as we don't want to
++     change the hard registers mentioned.  */
++  if (GET_CODE (x) == REG
++      && (REGNO (x) >= FIRST_PSEUDO_REGISTER
++          || (GET_CODE (PATTERN (insn)) != USE
++            && asm_noperands (PATTERN (insn)) < 0)))
++    {
++      cselib_val *val = cselib_lookup (x, GET_MODE (x), 0);
++      struct elt_loc_list *l;
++
++      if (!val)
++      return false;
++      for (l = val->locs; l; l = l->next)
++      {
++        rtx this_rtx = l->loc;
++        rtx note;
++
++        if (l->in_libcall)
++          continue;
++
++        if (CONSTANT_P (this_rtx))
++          newcnst = this_rtx;
++        if (REG_P (this_rtx) && REGNO (this_rtx) >= FIRST_PSEUDO_REGISTER
++            /* Don't copy propagate if it has attached REG_EQUIV note.
++               At this point this only function parameters should have
++               REG_EQUIV notes and if the argument slot is used somewhere
++               explicitly, it means address of parameter has been taken,
++               so we should not extend the lifetime of the pseudo.  */
++            && (!(note = find_reg_note (l->setting_insn, REG_EQUIV, NULL_RTX))
++                || GET_CODE (XEXP (note, 0)) != MEM))
++          newreg = this_rtx;
++      }
++      if (newcnst && constprop_register (insn, x, newcnst, alter_jumps))
++      {
++        /* If we find a case where we can't fix the retval REG_EQUAL notes
++           match the new register, we either have to abandom this replacement
++           or fix delete_trivially_dead_insns to preserve the setting insn,
++           or make it delete the REG_EUAQL note, and fix up all passes that
++           require the REG_EQUAL note there.  */
++        if (!adjust_libcall_notes (x, newcnst, insn, libcall_sp))
++          abort ();
++        if (gcse_file != NULL)
++          {
++            fprintf (gcse_file, "LOCAL CONST-PROP: Replacing reg %d in ",
++                     REGNO (x));
++            fprintf (gcse_file, "insn %d with constant ",
++                     INSN_UID (insn));
++            print_rtl (gcse_file, newcnst);
++            fprintf (gcse_file, "\n");
++          }
++        const_prop_count++;
++        return true;
++      }
++      else if (newreg && newreg != x && try_replace_reg (x, newreg, insn))
++      {
++        adjust_libcall_notes (x, newreg, insn, libcall_sp);
++        if (gcse_file != NULL)
++          {
++            fprintf (gcse_file,
++                     "LOCAL COPY-PROP: Replacing reg %d in insn %d",
++                     REGNO (x), INSN_UID (insn));
++            fprintf (gcse_file, " with reg %d\n", REGNO (newreg));
++          }
++        copy_prop_count++;
++        return true;
++      }
++    }
++  return false;
++}
++
++/* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
++   their REG_EQUAL notes need updating to reflect that OLDREG has been
++   replaced with NEWVAL in INSN.  Return true if all substitutions could
++   be made.  */
++static bool
++adjust_libcall_notes (oldreg, newval, insn, libcall_sp)
++     rtx oldreg, newval, insn, *libcall_sp;
++{
++  rtx end;
++
++  while ((end = *libcall_sp++))
++    {
++      rtx note = find_reg_equal_equiv_note (end);
++
++      if (! note)
++      continue;
++
++      if (REG_P (newval))
++      {
++        if (reg_set_between_p (newval, PREV_INSN (insn), end))
++          {
++            do
++              {
++                note = find_reg_equal_equiv_note (end);
++                if (! note)
++                  continue;
++                if (reg_mentioned_p (newval, XEXP (note, 0)))
++                  return false;
++              }
++            while ((end = *libcall_sp++));
++            return true;
++          }
++      }
++      XEXP (note, 0) = replace_rtx (XEXP (note, 0), oldreg, newval);
++      insn = end;
++    }
++  return true;
++}
++
++#define MAX_NESTED_LIBCALLS 9
++
++static void
++local_cprop_pass (alter_jumps)
++     int alter_jumps;
++{
++  rtx insn;
++  struct reg_use *reg_used;
++  rtx libcall_stack[MAX_NESTED_LIBCALLS + 1], *libcall_sp;
++  bool changed = false;
++
++  cselib_init ();
++  libcall_sp = &libcall_stack[MAX_NESTED_LIBCALLS];
++  *libcall_sp = 0;
++  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
++    {
++      if (INSN_P (insn))
++      {
++        rtx note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
++
++        if (note)
++          {
++            if (libcall_sp == libcall_stack)
++              abort ();
++            *--libcall_sp = XEXP (note, 0);
++          }
++        note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
++        if (note)
++          libcall_sp++;
++        note = find_reg_equal_equiv_note (insn);
++        do
++          {
++            reg_use_count = 0;
++            note_uses (&PATTERN (insn), local_cprop_find_used_regs, NULL);
++            if (note)
++              local_cprop_find_used_regs (&XEXP (note, 0), NULL);
++
++            for (reg_used = &reg_use_table[0]; reg_use_count > 0;
++                 reg_used++, reg_use_count--)
++              if (do_local_cprop (reg_used->reg_rtx, insn, alter_jumps,
++                  libcall_sp))
++                {
++                  changed = true;
++                  break;
++                }
++          }
++        while (reg_use_count);
++      }
++      cselib_process_insn (insn);
++    }
++  cselib_finish ();
++  /* Global analysis may get into infinite loops for unreachable blocks.  */
++  if (changed && alter_jumps)
++    {
++      delete_unreachable_blocks ();
++      free_reg_set_mem ();
++      alloc_reg_set_mem (max_reg_num ());
++      compute_sets (get_insns ());
++    }
++}
++
++/* Forward propagate copies.  This includes copies and constants.  Return
++   nonzero if a change was made.  */
++
++static int
++cprop (alter_jumps)
++     int alter_jumps;
++{
++  int changed;
++  basic_block bb;
++  rtx insn;
++
++  /* Note we start at block 1.  */
++  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
++    {
++      if (gcse_file != NULL)
++      fprintf (gcse_file, "\n");
++      return 0;
++    }
++
++  changed = 0;
++  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
++    {
++      /* Reset tables used to keep track of what's still valid [since the
++       start of the block].  */
++      reset_opr_set_tables ();
++
++      for (insn = bb->head;
++         insn != NULL && insn != NEXT_INSN (bb->end);
++         insn = NEXT_INSN (insn))
++      if (INSN_P (insn))
++        {
++          changed |= cprop_insn (insn, alter_jumps);
++
++          /* Keep track of everything modified by this insn.  */
++          /* ??? Need to be careful w.r.t. mods done to INSN.  Don't
++             call mark_oprs_set if we turned the insn into a NOTE.  */
++          if (GET_CODE (insn) != NOTE)
++            mark_oprs_set (insn);
++        }
++    }
++
++  if (gcse_file != NULL)
++    fprintf (gcse_file, "\n");
++
++  return changed;
++}
++
++/* Perform one copy/constant propagation pass.
++   F is the first insn in the function.
++   PASS is the pass count.  */
++
++static int
++one_cprop_pass (pass, alter_jumps)
++     int pass;
++     int alter_jumps;
++{
++  int changed = 0;
++
++  const_prop_count = 0;
++  copy_prop_count = 0;
++
++  local_cprop_pass (alter_jumps);
++
++  alloc_hash_table (max_cuid, &set_hash_table, 1);
++  compute_hash_table (&set_hash_table);
++  if (gcse_file)
++    dump_hash_table (gcse_file, "SET", &set_hash_table);
++  if (set_hash_table.n_elems > 0)
++    {
++      alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
++      compute_cprop_data ();
++      changed = cprop (alter_jumps);
++      if (alter_jumps)
++      changed |= bypass_conditional_jumps ();
++      free_cprop_mem ();
++    }
++
++  free_hash_table (&set_hash_table);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "CPROP of %s, pass %d: %d bytes needed, ",
++             current_function_name, pass, bytes_used);
++      fprintf (gcse_file, "%d const props, %d copy props\n\n",
++             const_prop_count, copy_prop_count);
++    }
++  /* Global analysis may get into infinite loops for unreachable blocks.  */
++  if (changed && alter_jumps)
++    delete_unreachable_blocks ();
++
++  return changed;
++}
++\f
++/* Bypass conditional jumps.  */
++
++/* Find a set of REGNO to a constant that is available at the end of basic
++   block BB.  Returns NULL if no such set is found.  Based heavily upon
++   find_avail_set.  */
++
++static struct expr *
++find_bypass_set (regno, bb)
++     int regno;
++     int bb;
++{
++  struct expr *result = 0;
++
++  for (;;)
++    {
++      rtx src;
++      struct expr *set = lookup_set (regno, NULL_RTX, &set_hash_table);
++
++      while (set)
++      {
++        if (TEST_BIT (cprop_avout[bb], set->bitmap_index))
++          break;
++        set = next_set (regno, set);
++      }
++
++      if (set == 0)
++      break;
++
++      if (GET_CODE (set->expr) != SET)
++      abort ();
++
++      src = SET_SRC (set->expr);
++      if (CONSTANT_P (src))
++      result = set;
++
++      if (GET_CODE (src) != REG)
++      break;
++
++      regno = REGNO (src);
++    }
++  return result;
++}
++
++
++/* Subroutine of bypass_block that checks whether a pseudo is killed by
++   any of the instructions inserted on an edge.  Jump bypassing places
++   condition code setters on CFG edges using insert_insn_on_edge.  This
++   function is required to check that our data flow analysis is still
++   valid prior to commit_edge_insertions.  */
++
++static bool
++reg_killed_on_edge (reg, e)
++     rtx reg;
++     edge e;
++{
++  rtx insn;
++
++  for (insn = e->insns; insn; insn = NEXT_INSN (insn))
++    if (INSN_P (insn) && reg_set_p (reg, insn))
++      return true;
++
++  return false;
++}
++
++/* Subroutine of bypass_conditional_jumps that attempts to bypass the given
++   basic block BB which has more than one predecessor.  If not NULL, SETCC
++   is the first instruction of BB, which is immediately followed by JUMP_INSN
++   JUMP.  Otherwise, SETCC is NULL, and JUMP is the first insn of BB.
++   Returns nonzero if a change was made.
++
++   During the jump bypassing pass, we may place copies of SETCC instuctions
++   on CFG edges.  The following routine must be careful to pay attention to
++   these inserted insns when performing its transformations.  */
++
++static int
++bypass_block (bb, setcc, jump)
++     basic_block bb;
++     rtx setcc, jump;
++{
++  rtx insn, note;
++  edge e, enext, edest;
++  int i, change;
++
++  insn = (setcc != NULL) ? setcc : jump;
++
++  /* Determine set of register uses in INSN.  */
++  reg_use_count = 0;
++  note_uses (&PATTERN (insn), find_used_regs, NULL);
++  note = find_reg_equal_equiv_note (insn);
++  if (note)
++    find_used_regs (&XEXP (note, 0), NULL);
++
++  change = 0;
++  for (e = bb->pred; e; e = enext)
++    {
++      enext = e->pred_next;
++      for (i = 0; i < reg_use_count; i++)
++      {
++        struct reg_use *reg_used = &reg_use_table[i];
++        unsigned int regno = REGNO (reg_used->reg_rtx);
++        basic_block dest, old_dest;
++        struct expr *set;
++        rtx src, new;
++
++        if (regno >= max_gcse_regno)
++          continue;
++
++        set = find_bypass_set (regno, e->src->index);
++
++        if (! set)
++          continue;
++
++        /* Check the data flow is valid after edge insertions.  */
++        if (e->insns && reg_killed_on_edge (reg_used->reg_rtx, e))
++          continue;
++
++        src = SET_SRC (pc_set (jump));
++
++        if (setcc != NULL)
++            src = simplify_replace_rtx (src,
++                                        SET_DEST (PATTERN (setcc)),
++                                        SET_SRC (PATTERN (setcc)));
++
++        new = simplify_replace_rtx (src, reg_used->reg_rtx,
++                                    SET_SRC (set->expr));
++
++        /* Jump bypassing may have already placed instructions on 
++           edges of the CFG.  We can't bypass an outgoing edge that
++           has instructions associated with it, as these insns won't
++           get executed if the incoming edge is redirected.  */
++
++        if (new == pc_rtx)
++          {
++            edest = FALLTHRU_EDGE (bb);
++            dest = edest->insns ? NULL : edest->dest;
++          }
++        else if (GET_CODE (new) == LABEL_REF)
++          {
++            dest = BLOCK_FOR_INSN (XEXP (new, 0));
++            /* Don't bypass edges containing instructions.  */
++            for (edest = bb->succ; edest; edest = edest->succ_next)
++              if (edest->dest == dest && edest->insns)
++                {
++                  dest = NULL;
++                  break;
++                }
++          }
++        else
++          dest = NULL;
++
++        /* Once basic block indices are stable, we should be able
++           to use redirect_edge_and_branch_force instead.  */
++        old_dest = e->dest;
++        if (dest != NULL && dest != old_dest
++            && redirect_edge_and_branch (e, dest))
++          {
++            /* Copy the register setter to the redirected edge.
++               Don't copy CC0 setters, as CC0 is dead after jump.  */
++            if (setcc)
++              {
++                rtx pat = PATTERN (setcc);
++                if (!CC0_P (SET_DEST (pat)))
++                  insert_insn_on_edge (copy_insn (pat), e);
++              }
++
++            if (gcse_file != NULL)
++              {
++                fprintf (gcse_file, "JUMP-BYPASS: Proved reg %d in jump_insn %d equals constant ",
++                         regno, INSN_UID (jump));
++                print_rtl (gcse_file, SET_SRC (set->expr));
++                fprintf (gcse_file, "\nBypass edge from %d->%d to %d\n",
++                         e->src->index, old_dest->index, dest->index);
++              }
++            change = 1;
++            break;
++          }
++      }
++    }
++  return change;
++}
++
++/* Find basic blocks with more than one predecessor that only contain a
++   single conditional jump.  If the result of the comparison is known at
++   compile-time from any incoming edge, redirect that edge to the
++   appropriate target.  Returns nonzero if a change was made.  */
++
++static int
++bypass_conditional_jumps ()
++{
++  basic_block bb;
++  int changed;
++  rtx setcc;
++  rtx insn;
++  rtx dest;
++
++  /* Note we start at block 1.  */
++  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
++    return 0;
++
++  changed = 0;
++  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb,
++                EXIT_BLOCK_PTR, next_bb)
++    {
++      /* Check for more than one predecessor.  */
++      if (bb->pred && bb->pred->pred_next)
++      {
++        setcc = NULL_RTX;
++        for (insn = bb->head;
++             insn != NULL && insn != NEXT_INSN (bb->end);
++             insn = NEXT_INSN (insn))
++          if (GET_CODE (insn) == INSN)
++            {
++              if (setcc)
++                break;
++              if (GET_CODE (PATTERN (insn)) != SET)
++                break;
++
++              dest = SET_DEST (PATTERN (insn));
++              if (REG_P (dest) || CC0_P (dest))
++                setcc = insn;
++              else
++                break;
++            }
++          else if (GET_CODE (insn) == JUMP_INSN)
++            {
++              if (any_condjump_p (insn) && onlyjump_p (insn))
++                changed |= bypass_block (bb, setcc, insn);
++              break;
++            }
++          else if (INSN_P (insn))
++            break;
++      }
++    }
++
++  /* If we bypassed any register setting insns, we inserted a
++     copy on the redirected edge.  These need to be commited.  */
++  if (changed)
++    commit_edge_insertions();
++
++  return changed;
++}
++\f
++/* Compute PRE+LCM working variables.  */
++
++/* Local properties of expressions.  */
++/* Nonzero for expressions that are transparent in the block.  */
++static sbitmap *transp;
++
++/* Nonzero for expressions that are transparent at the end of the block.
++   This is only zero for expressions killed by abnormal critical edge
++   created by a calls.  */
++static sbitmap *transpout;
++
++/* Nonzero for expressions that are computed (available) in the block.  */
++static sbitmap *comp;
++
++/* Nonzero for expressions that are locally anticipatable in the block.  */
++static sbitmap *antloc;
++
++/* Nonzero for expressions where this block is an optimal computation
++   point.  */
++static sbitmap *pre_optimal;
++
++/* Nonzero for expressions which are redundant in a particular block.  */
++static sbitmap *pre_redundant;
++
++/* Nonzero for expressions which should be inserted on a specific edge.  */
++static sbitmap *pre_insert_map;
++
++/* Nonzero for expressions which should be deleted in a specific block.  */
++static sbitmap *pre_delete_map;
++
++/* Contains the edge_list returned by pre_edge_lcm.  */
++static struct edge_list *edge_list;
++
++/* Redundant insns.  */
++static sbitmap pre_redundant_insns;
++
++/* Allocate vars used for PRE analysis.  */
++
++static void
++alloc_pre_mem (n_blocks, n_exprs)
++     int n_blocks, n_exprs;
++{
++  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
++  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
++  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
++
++  pre_optimal = NULL;
++  pre_redundant = NULL;
++  pre_insert_map = NULL;
++  pre_delete_map = NULL;
++  ae_in = NULL;
++  ae_out = NULL;
++  ae_kill = sbitmap_vector_alloc (n_blocks, n_exprs);
++
++  /* pre_insert and pre_delete are allocated later.  */
++}
++
++/* Free vars used for PRE analysis.  */
++
++static void
++free_pre_mem ()
++{
++  sbitmap_vector_free (transp);
++  sbitmap_vector_free (comp);
++
++  /* ANTLOC and AE_KILL are freed just after pre_lcm finishes.  */
++
++  if (pre_optimal)
++    sbitmap_vector_free (pre_optimal);
++  if (pre_redundant)
++    sbitmap_vector_free (pre_redundant);
++  if (pre_insert_map)
++    sbitmap_vector_free (pre_insert_map);
++  if (pre_delete_map)
++    sbitmap_vector_free (pre_delete_map);
++  if (ae_in)
++    sbitmap_vector_free (ae_in);
++  if (ae_out)
++    sbitmap_vector_free (ae_out);
++
++  transp = comp = NULL;
++  pre_optimal = pre_redundant = pre_insert_map = pre_delete_map = NULL;
++  ae_in = ae_out = NULL;
++}
++
++/* Top level routine to do the dataflow analysis needed by PRE.  */
++
++static void
++compute_pre_data ()
++{
++  sbitmap trapping_expr;
++  basic_block bb;
++  unsigned int ui;
++
++  compute_local_properties (transp, comp, antloc, &expr_hash_table);
++  sbitmap_vector_zero (ae_kill, last_basic_block);
++
++  /* Collect expressions which might trap.  */
++  trapping_expr = sbitmap_alloc (expr_hash_table.n_elems);
++  sbitmap_zero (trapping_expr);
++  for (ui = 0; ui < expr_hash_table.size; ui++)
++    {
++      struct expr *e;
++      for (e = expr_hash_table.table[ui]; e != NULL; e = e->next_same_hash)
++      if (may_trap_p (e->expr))
++        SET_BIT (trapping_expr, e->bitmap_index);
++    }
++
++  /* Compute ae_kill for each basic block using:
++
++     ~(TRANSP | COMP)
++
++     This is significantly faster than compute_ae_kill.  */
++
++  FOR_EACH_BB (bb)
++    {
++      edge e;
++
++      /* If the current block is the destination of an abnormal edge, we
++       kill all trapping expressions because we won't be able to properly
++       place the instruction on the edge.  So make them neither
++       anticipatable nor transparent.  This is fairly conservative.  */
++      for (e = bb->pred; e ; e = e->pred_next)
++      if (e->flags & EDGE_ABNORMAL)
++        {
++          sbitmap_difference (antloc[bb->index], antloc[bb->index], trapping_expr);
++          sbitmap_difference (transp[bb->index], transp[bb->index], trapping_expr);
++          break;
++        }
++
++      sbitmap_a_or_b (ae_kill[bb->index], transp[bb->index], comp[bb->index]);
++      sbitmap_not (ae_kill[bb->index], ae_kill[bb->index]);
++    }
++
++  edge_list = pre_edge_lcm (gcse_file, expr_hash_table.n_elems, transp, comp, antloc,
++                          ae_kill, &pre_insert_map, &pre_delete_map);
++  sbitmap_vector_free (antloc);
++  antloc = NULL;
++  sbitmap_vector_free (ae_kill);
++  ae_kill = NULL;
++  sbitmap_free (trapping_expr);
++}
++\f
++/* PRE utilities */
++
++/* Return nonzero if an occurrence of expression EXPR in OCCR_BB would reach
++   block BB.
++
++   VISITED is a pointer to a working buffer for tracking which BB's have
++   been visited.  It is NULL for the top-level call.
++
++   We treat reaching expressions that go through blocks containing the same
++   reaching expression as "not reaching".  E.g. if EXPR is generated in blocks
++   2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
++   2 as not reaching.  The intent is to improve the probability of finding
++   only one reaching expression and to reduce register lifetimes by picking
++   the closest such expression.  */
++
++static int
++pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited)
++     basic_block occr_bb;
++     struct expr *expr;
++     basic_block bb;
++     char *visited;
++{
++  edge pred;
++
++  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
++    {
++      basic_block pred_bb = pred->src;
++
++      if (pred->src == ENTRY_BLOCK_PTR
++        /* Has predecessor has already been visited?  */
++        || visited[pred_bb->index])
++      ;/* Nothing to do.  */
++
++      /* Does this predecessor generate this expression?  */
++      else if (TEST_BIT (comp[pred_bb->index], expr->bitmap_index))
++      {
++        /* Is this the occurrence we're looking for?
++           Note that there's only one generating occurrence per block
++           so we just need to check the block number.  */
++        if (occr_bb == pred_bb)
++          return 1;
++
++        visited[pred_bb->index] = 1;
++      }
++      /* Ignore this predecessor if it kills the expression.  */
++      else if (! TEST_BIT (transp[pred_bb->index], expr->bitmap_index))
++      visited[pred_bb->index] = 1;
++
++      /* Neither gen nor kill.  */
++      else
++      {
++        visited[pred_bb->index] = 1;
++        if (pre_expr_reaches_here_p_work (occr_bb, expr, pred_bb, visited))
++          return 1;
++      }
++    }
++
++  /* All paths have been checked.  */
++  return 0;
++}
++
++/* The wrapper for pre_expr_reaches_here_work that ensures that any
++   memory allocated for that function is returned.  */
++
++static int
++pre_expr_reaches_here_p (occr_bb, expr, bb)
++     basic_block occr_bb;
++     struct expr *expr;
++     basic_block bb;
++{
++  int rval;
++  char *visited = (char *) xcalloc (last_basic_block, 1);
++
++  rval = pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited);
++
++  free (visited);
++  return rval;
++}
++\f
++
++/* Given an expr, generate RTL which we can insert at the end of a BB,
++   or on an edge.  Set the block number of any insns generated to
++   the value of BB.  */
++
++static rtx
++process_insert_insn (expr)
++     struct expr *expr;
++{
++  rtx reg = expr->reaching_reg;
++  rtx exp = copy_rtx (expr->expr);
++  rtx pat;
++
++  start_sequence ();
++
++  /* If the expression is something that's an operand, like a constant,
++     just copy it to a register.  */
++  if (general_operand (exp, GET_MODE (reg)))
++    emit_move_insn (reg, exp);
++
++  /* Otherwise, make a new insn to compute this expression and make sure the
++     insn will be recognized (this also adds any needed CLOBBERs).  Copy the
++     expression to make sure we don't have any sharing issues.  */
++  else if (insn_invalid_p (emit_insn (gen_rtx_SET (VOIDmode, reg, exp))))
++    abort ();
++
++  pat = get_insns ();
++  end_sequence ();
++
++  return pat;
++}
++
++/* Add EXPR to the end of basic block BB.
++
++   This is used by both the PRE and code hoisting.
++
++   For PRE, we want to verify that the expr is either transparent
++   or locally anticipatable in the target block.  This check makes
++   no sense for code hoisting.  */
++
++static void
++insert_insn_end_bb (expr, bb, pre)
++     struct expr *expr;
++     basic_block bb;
++     int pre;
++{
++  rtx insn = bb->end;
++  rtx new_insn;
++  rtx reg = expr->reaching_reg;
++  int regno = REGNO (reg);
++  rtx pat, pat_end;
++
++  pat = process_insert_insn (expr);
++  if (pat == NULL_RTX || ! INSN_P (pat))
++    abort ();
++
++  pat_end = pat;
++  while (NEXT_INSN (pat_end) != NULL_RTX)
++    pat_end = NEXT_INSN (pat_end);
++
++  /* If the last insn is a jump, insert EXPR in front [taking care to
++     handle cc0, etc. properly].  Similary we need to care trapping
++     instructions in presence of non-call exceptions.  */
++
++  if (GET_CODE (insn) == JUMP_INSN
++      || (GET_CODE (insn) == INSN
++        && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL))))
++    {
++#ifdef HAVE_cc0
++      rtx note;
++#endif
++      /* It should always be the case that we can put these instructions
++       anywhere in the basic block with performing PRE optimizations.
++       Check this.  */
++      if (GET_CODE (insn) == INSN && pre
++        && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
++        && !TEST_BIT (transp[bb->index], expr->bitmap_index))
++      abort ();
++
++      /* If this is a jump table, then we can't insert stuff here.  Since
++       we know the previous real insn must be the tablejump, we insert
++       the new instruction just before the tablejump.  */
++      if (GET_CODE (PATTERN (insn)) == ADDR_VEC
++        || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
++      insn = prev_real_insn (insn);
++
++#ifdef HAVE_cc0
++      /* FIXME: 'twould be nice to call prev_cc0_setter here but it aborts
++       if cc0 isn't set.  */
++      note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
++      if (note)
++      insn = XEXP (note, 0);
++      else
++      {
++        rtx maybe_cc0_setter = prev_nonnote_insn (insn);
++        if (maybe_cc0_setter
++            && INSN_P (maybe_cc0_setter)
++            && sets_cc0_p (PATTERN (maybe_cc0_setter)))
++          insn = maybe_cc0_setter;
++      }
++#endif
++      /* FIXME: What if something in cc0/jump uses value set in new insn?  */
++      new_insn = emit_insn_before (pat, insn);
++    }
++
++  /* Likewise if the last insn is a call, as will happen in the presence
++     of exception handling.  */
++  else if (GET_CODE (insn) == CALL_INSN
++         && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL)))
++    {
++      /* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
++       we search backward and place the instructions before the first
++       parameter is loaded.  Do this for everyone for consistency and a
++       presumtion that we'll get better code elsewhere as well.
++
++       It should always be the case that we can put these instructions
++       anywhere in the basic block with performing PRE optimizations.
++       Check this.  */
++
++      if (pre
++        && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
++        && !TEST_BIT (transp[bb->index], expr->bitmap_index))
++      abort ();
++
++      /* Since different machines initialize their parameter registers
++       in different orders, assume nothing.  Collect the set of all
++       parameter registers.  */
++      insn = find_first_parameter_load (insn, bb->head);
++
++      /* If we found all the parameter loads, then we want to insert
++       before the first parameter load.
++
++       If we did not find all the parameter loads, then we might have
++       stopped on the head of the block, which could be a CODE_LABEL.
++       If we inserted before the CODE_LABEL, then we would be putting
++       the insn in the wrong basic block.  In that case, put the insn
++       after the CODE_LABEL.  Also, respect NOTE_INSN_BASIC_BLOCK.  */
++      while (GET_CODE (insn) == CODE_LABEL
++           || NOTE_INSN_BASIC_BLOCK_P (insn))
++      insn = NEXT_INSN (insn);
++
++      new_insn = emit_insn_before (pat, insn);
++    }
++  else
++    new_insn = emit_insn_after (pat, insn);
++
++  while (1)
++    {
++      if (INSN_P (pat))
++      {
++        add_label_notes (PATTERN (pat), new_insn);
++        note_stores (PATTERN (pat), record_set_info, pat);
++      }
++      if (pat == pat_end)
++      break;
++      pat = NEXT_INSN (pat);
++    }
++
++  gcse_create_count++;
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "PRE/HOIST: end of bb %d, insn %d, ",
++             bb->index, INSN_UID (new_insn));
++      fprintf (gcse_file, "copying expression %d to reg %d\n",
++             expr->bitmap_index, regno);
++    }
++}
++
++/* Insert partially redundant expressions on edges in the CFG to make
++   the expressions fully redundant.  */
++
++static int
++pre_edge_insert (edge_list, index_map)
++     struct edge_list *edge_list;
++     struct expr **index_map;
++{
++  int e, i, j, num_edges, set_size, did_insert = 0;
++  sbitmap *inserted;
++
++  /* Where PRE_INSERT_MAP is nonzero, we add the expression on that edge
++     if it reaches any of the deleted expressions.  */
++
++  set_size = pre_insert_map[0]->size;
++  num_edges = NUM_EDGES (edge_list);
++  inserted = sbitmap_vector_alloc (num_edges, expr_hash_table.n_elems);
++  sbitmap_vector_zero (inserted, num_edges);
++
++  for (e = 0; e < num_edges; e++)
++    {
++      int indx;
++      basic_block bb = INDEX_EDGE_PRED_BB (edge_list, e);
++
++      for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
++      {
++        SBITMAP_ELT_TYPE insert = pre_insert_map[e]->elms[i];
++
++        for (j = indx; insert && j < (int) expr_hash_table.n_elems; j++, insert >>= 1)
++          if ((insert & 1) != 0 && index_map[j]->reaching_reg != NULL_RTX)
++            {
++              struct expr *expr = index_map[j];
++              struct occr *occr;
++
++              /* Now look at each deleted occurrence of this expression.  */
++              for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
++                {
++                  if (! occr->deleted_p)
++                    continue;
++
++                  /* Insert this expression on this edge if if it would
++                     reach the deleted occurrence in BB.  */
++                  if (!TEST_BIT (inserted[e], j))
++                    {
++                      rtx insn;
++                      edge eg = INDEX_EDGE (edge_list, e);
++
++                      /* We can't insert anything on an abnormal and
++                         critical edge, so we insert the insn at the end of
++                         the previous block. There are several alternatives
++                         detailed in Morgans book P277 (sec 10.5) for
++                         handling this situation.  This one is easiest for
++                         now.  */
++
++                      if ((eg->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
++                        insert_insn_end_bb (index_map[j], bb, 0);
++                      else
++                        {
++                          insn = process_insert_insn (index_map[j]);
++                          insert_insn_on_edge (insn, eg);
++                        }
++
++                      if (gcse_file)
++                        {
++                          fprintf (gcse_file, "PRE/HOIST: edge (%d,%d), ",
++                                   bb->index,
++                                   INDEX_EDGE_SUCC_BB (edge_list, e)->index);
++                          fprintf (gcse_file, "copy expression %d\n",
++                                   expr->bitmap_index);
++                        }
++
++                      update_ld_motion_stores (expr);
++                      SET_BIT (inserted[e], j);
++                      did_insert = 1;
++                      gcse_create_count++;
++                    }
++                }
++            }
++      }
++    }
++
++  sbitmap_vector_free (inserted);
++  return did_insert;
++}
++
++/* Copy the result of INSN to REG.  INDX is the expression number.  */
++
++static void
++pre_insert_copy_insn (expr, insn)
++     struct expr *expr;
++     rtx insn;
++{
++  rtx reg = expr->reaching_reg;
++  int regno = REGNO (reg);
++  int indx = expr->bitmap_index;
++  rtx set = single_set (insn);
++  rtx new_insn;
++
++  if (!set)
++    abort ();
++
++  new_insn = emit_insn_after (gen_move_insn (reg, SET_DEST (set)), insn);
++
++  /* Keep register set table up to date.  */
++  record_one_set (regno, new_insn);
++
++  gcse_create_count++;
++
++  if (gcse_file)
++    fprintf (gcse_file,
++           "PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
++            BLOCK_NUM (insn), INSN_UID (new_insn), indx,
++            INSN_UID (insn), regno);
++  update_ld_motion_stores (expr);
++}
++
++/* Copy available expressions that reach the redundant expression
++   to `reaching_reg'.  */
++
++static void
++pre_insert_copies ()
++{
++  unsigned int i;
++  struct expr *expr;
++  struct occr *occr;
++  struct occr *avail;
++
++  /* For each available expression in the table, copy the result to
++     `reaching_reg' if the expression reaches a deleted one.
++
++     ??? The current algorithm is rather brute force.
++     Need to do some profiling.  */
++
++  for (i = 0; i < expr_hash_table.size; i++)
++    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
++      {
++      /* If the basic block isn't reachable, PPOUT will be TRUE.  However,
++         we don't want to insert a copy here because the expression may not
++         really be redundant.  So only insert an insn if the expression was
++         deleted.  This test also avoids further processing if the
++         expression wasn't deleted anywhere.  */
++      if (expr->reaching_reg == NULL)
++        continue;
++
++      for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
++        {
++          if (! occr->deleted_p)
++            continue;
++
++          for (avail = expr->avail_occr; avail != NULL; avail = avail->next)
++            {
++              rtx insn = avail->insn;
++
++              /* No need to handle this one if handled already.  */
++              if (avail->copied_p)
++                continue;
++
++              /* Don't handle this one if it's a redundant one.  */
++              if (TEST_BIT (pre_redundant_insns, INSN_CUID (insn)))
++                continue;
++
++              /* Or if the expression doesn't reach the deleted one.  */
++              if (! pre_expr_reaches_here_p (BLOCK_FOR_INSN (avail->insn),
++                                             expr,
++                                             BLOCK_FOR_INSN (occr->insn)))
++                continue;
++
++              /* Copy the result of avail to reaching_reg.  */
++              pre_insert_copy_insn (expr, insn);
++              avail->copied_p = 1;
++            }
++        }
++      }
++}
++
++/* Emit move from SRC to DEST noting the equivalence with expression computed
++   in INSN.  */
++static rtx
++gcse_emit_move_after (src, dest, insn)
++     rtx src, dest, insn;
++{
++  rtx new;
++  rtx set = single_set (insn), set2;
++  rtx note;
++  rtx eqv;
++
++  /* This should never fail since we're creating a reg->reg copy
++     we've verified to be valid.  */
++
++  new = emit_insn_after (gen_move_insn (dest, src), insn);
++
++  /* Note the equivalence for local CSE pass.  */
++  set2 = single_set (new);
++  if (!set2 || !rtx_equal_p (SET_DEST (set2), dest))
++    return new;
++  if ((note = find_reg_equal_equiv_note (insn)))
++    eqv = XEXP (note, 0);
++  else
++    eqv = SET_SRC (set);
++
++  set_unique_reg_note (new, REG_EQUAL, copy_insn_1 (eqv));
++
++  return new;
++}
++
++/* Delete redundant computations.
++   Deletion is done by changing the insn to copy the `reaching_reg' of
++   the expression into the result of the SET.  It is left to later passes
++   (cprop, cse2, flow, combine, regmove) to propagate the copy or eliminate it.
++
++   Returns nonzero if a change is made.  */
++
++static int
++pre_delete ()
++{
++  unsigned int i;
++  int changed;
++  struct expr *expr;
++  struct occr *occr;
++
++  changed = 0;
++  for (i = 0; i < expr_hash_table.size; i++)
++    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
++      {
++      int indx = expr->bitmap_index;
++
++      /* We only need to search antic_occr since we require
++         ANTLOC != 0.  */
++
++      for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
++        {
++          rtx insn = occr->insn;
++          rtx set;
++          basic_block bb = BLOCK_FOR_INSN (insn);
++
++          if (TEST_BIT (pre_delete_map[bb->index], indx))
++            {
++              set = single_set (insn);
++              if (! set)
++                abort ();
++
++              /* Create a pseudo-reg to store the result of reaching
++                 expressions into.  Get the mode for the new pseudo from
++                 the mode of the original destination pseudo.  */
++              if (expr->reaching_reg == NULL)
++                expr->reaching_reg
++                  = gen_reg_rtx (GET_MODE (SET_DEST (set)));
++
++              gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
++              delete_insn (insn);
++              occr->deleted_p = 1;
++              SET_BIT (pre_redundant_insns, INSN_CUID (insn));
++              changed = 1;
++              gcse_subst_count++;
++
++              if (gcse_file)
++                {
++                  fprintf (gcse_file,
++                           "PRE: redundant insn %d (expression %d) in ",
++                             INSN_UID (insn), indx);
++                  fprintf (gcse_file, "bb %d, reaching reg is %d\n",
++                           bb->index, REGNO (expr->reaching_reg));
++                }
++            }
++        }
++      }
++
++  return changed;
++}
++
++/* Perform GCSE optimizations using PRE.
++   This is called by one_pre_gcse_pass after all the dataflow analysis
++   has been done.
++
++   This is based on the original Morel-Renvoise paper Fred Chow's thesis, and
++   lazy code motion from Knoop, Ruthing and Steffen as described in Advanced
++   Compiler Design and Implementation.
++
++   ??? A new pseudo reg is created to hold the reaching expression.  The nice
++   thing about the classical approach is that it would try to use an existing
++   reg.  If the register can't be adequately optimized [i.e. we introduce
++   reload problems], one could add a pass here to propagate the new register
++   through the block.
++
++   ??? We don't handle single sets in PARALLELs because we're [currently] not
++   able to copy the rest of the parallel when we insert copies to create full
++   redundancies from partial redundancies.  However, there's no reason why we
++   can't handle PARALLELs in the cases where there are no partial
++   redundancies.  */
++
++static int
++pre_gcse ()
++{
++  unsigned int i;
++  int did_insert, changed;
++  struct expr **index_map;
++  struct expr *expr;
++
++  /* Compute a mapping from expression number (`bitmap_index') to
++     hash table entry.  */
++
++  index_map = (struct expr **) xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
++  for (i = 0; i < expr_hash_table.size; i++)
++    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
++      index_map[expr->bitmap_index] = expr;
++
++  /* Reset bitmap used to track which insns are redundant.  */
++  pre_redundant_insns = sbitmap_alloc (max_cuid);
++  sbitmap_zero (pre_redundant_insns);
++
++  /* Delete the redundant insns first so that
++     - we know what register to use for the new insns and for the other
++       ones with reaching expressions
++     - we know which insns are redundant when we go to create copies  */
++
++  changed = pre_delete ();
++
++  did_insert = pre_edge_insert (edge_list, index_map);
++
++  /* In other places with reaching expressions, copy the expression to the
++     specially allocated pseudo-reg that reaches the redundant expr.  */
++  pre_insert_copies ();
++  if (did_insert)
++    {
++      commit_edge_insertions ();
++      changed = 1;
++    }
++
++  free (index_map);
++  sbitmap_free (pre_redundant_insns);
++  return changed;
++}
++
++/* Top level routine to perform one PRE GCSE pass.
++
++   Return nonzero if a change was made.  */
++
++static int
++one_pre_gcse_pass (pass)
++     int pass;
++{
++  int changed = 0;
++
++  gcse_subst_count = 0;
++  gcse_create_count = 0;
++
++  alloc_hash_table (max_cuid, &expr_hash_table, 0);
++  add_noreturn_fake_exit_edges ();
++  if (flag_gcse_lm)
++    compute_ld_motion_mems ();
++
++  compute_hash_table (&expr_hash_table);
++  trim_ld_motion_mems ();
++  if (gcse_file)
++    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
++
++  if (expr_hash_table.n_elems > 0)
++    {
++      alloc_pre_mem (last_basic_block, expr_hash_table.n_elems);
++      compute_pre_data ();
++      changed |= pre_gcse ();
++      free_edge_list (edge_list);
++      free_pre_mem ();
++    }
++
++  free_ldst_mems ();
++  remove_fake_edges ();
++  free_hash_table (&expr_hash_table);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "\nPRE GCSE of %s, pass %d: %d bytes needed, ",
++             current_function_name, pass, bytes_used);
++      fprintf (gcse_file, "%d substs, %d insns created\n",
++             gcse_subst_count, gcse_create_count);
++    }
++
++  return changed;
++}
++\f
++/* If X contains any LABEL_REF's, add REG_LABEL notes for them to INSN.
++   If notes are added to an insn which references a CODE_LABEL, the
++   LABEL_NUSES count is incremented.  We have to add REG_LABEL notes,
++   because the following loop optimization pass requires them.  */
++
++/* ??? This is very similar to the loop.c add_label_notes function.  We
++   could probably share code here.  */
++
++/* ??? If there was a jump optimization pass after gcse and before loop,
++   then we would not need to do this here, because jump would add the
++   necessary REG_LABEL notes.  */
++
++static void
++add_label_notes (x, insn)
++     rtx x;
++     rtx insn;
++{
++  enum rtx_code code = GET_CODE (x);
++  int i, j;
++  const char *fmt;
++
++  if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
++    {
++      /* This code used to ignore labels that referred to dispatch tables to
++       avoid flow generating (slighly) worse code.
++
++       We no longer ignore such label references (see LABEL_REF handling in
++       mark_jump_label for additional information).  */
++
++      REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, XEXP (x, 0),
++                                          REG_NOTES (insn));
++      if (LABEL_P (XEXP (x, 0)))
++      LABEL_NUSES (XEXP (x, 0))++;
++      return;
++    }
++
++  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      add_label_notes (XEXP (x, i), insn);
++      else if (fmt[i] == 'E')
++      for (j = XVECLEN (x, i) - 1; j >= 0; j--)
++        add_label_notes (XVECEXP (x, i, j), insn);
++    }
++}
++
++/* Compute transparent outgoing information for each block.
++
++   An expression is transparent to an edge unless it is killed by
++   the edge itself.  This can only happen with abnormal control flow,
++   when the edge is traversed through a call.  This happens with
++   non-local labels and exceptions.
++
++   This would not be necessary if we split the edge.  While this is
++   normally impossible for abnormal critical edges, with some effort
++   it should be possible with exception handling, since we still have
++   control over which handler should be invoked.  But due to increased
++   EH table sizes, this may not be worthwhile.  */
++
++static void
++compute_transpout ()
++{
++  basic_block bb;
++  unsigned int i;
++  struct expr *expr;
++
++  sbitmap_vector_ones (transpout, last_basic_block);
++
++  FOR_EACH_BB (bb)
++    {
++      /* Note that flow inserted a nop a the end of basic blocks that
++       end in call instructions for reasons other than abnormal
++       control flow.  */
++      if (GET_CODE (bb->end) != CALL_INSN)
++      continue;
++
++      for (i = 0; i < expr_hash_table.size; i++)
++      for (expr = expr_hash_table.table[i]; expr ; expr = expr->next_same_hash)
++        if (GET_CODE (expr->expr) == MEM)
++          {
++            if (GET_CODE (XEXP (expr->expr, 0)) == SYMBOL_REF
++                && CONSTANT_POOL_ADDRESS_P (XEXP (expr->expr, 0)))
++              continue;
++
++            /* ??? Optimally, we would use interprocedural alias
++               analysis to determine if this mem is actually killed
++               by this call.  */
++            RESET_BIT (transpout[bb->index], expr->bitmap_index);
++          }
++    }
++}
++
++/* Removal of useless null pointer checks */
++
++/* Called via note_stores.  X is set by SETTER.  If X is a register we must
++   invalidate nonnull_local and set nonnull_killed.  DATA is really a
++   `null_pointer_info *'.
++
++   We ignore hard registers.  */
++
++static void
++invalidate_nonnull_info (x, setter, data)
++     rtx x;
++     rtx setter ATTRIBUTE_UNUSED;
++     void *data;
++{
++  unsigned int regno;
++  struct null_pointer_info *npi = (struct null_pointer_info *) data;
++
++  while (GET_CODE (x) == SUBREG)
++    x = SUBREG_REG (x);
++
++  /* Ignore anything that is not a register or is a hard register.  */
++  if (GET_CODE (x) != REG
++      || REGNO (x) < npi->min_reg
++      || REGNO (x) >= npi->max_reg)
++    return;
++
++  regno = REGNO (x) - npi->min_reg;
++
++  RESET_BIT (npi->nonnull_local[npi->current_block->index], regno);
++  SET_BIT (npi->nonnull_killed[npi->current_block->index], regno);
++}
++
++/* Do null-pointer check elimination for the registers indicated in
++   NPI.  NONNULL_AVIN and NONNULL_AVOUT are pre-allocated sbitmaps;
++   they are not our responsibility to free.  */
++
++static int
++delete_null_pointer_checks_1 (block_reg, nonnull_avin,
++                            nonnull_avout, npi)
++     unsigned int *block_reg;
++     sbitmap *nonnull_avin;
++     sbitmap *nonnull_avout;
++     struct null_pointer_info *npi;
++{
++  basic_block bb, current_block;
++  sbitmap *nonnull_local = npi->nonnull_local;
++  sbitmap *nonnull_killed = npi->nonnull_killed;
++  int something_changed = 0;
++
++  /* Compute local properties, nonnull and killed.  A register will have
++     the nonnull property if at the end of the current block its value is
++     known to be nonnull.  The killed property indicates that somewhere in
++     the block any information we had about the register is killed.
++
++     Note that a register can have both properties in a single block.  That
++     indicates that it's killed, then later in the block a new value is
++     computed.  */
++  sbitmap_vector_zero (nonnull_local, last_basic_block);
++  sbitmap_vector_zero (nonnull_killed, last_basic_block);
++
++  FOR_EACH_BB (current_block)
++    {
++      rtx insn, stop_insn;
++
++      /* Set the current block for invalidate_nonnull_info.  */
++      npi->current_block = current_block;
++
++      /* Scan each insn in the basic block looking for memory references and
++       register sets.  */
++      stop_insn = NEXT_INSN (current_block->end);
++      for (insn = current_block->head;
++         insn != stop_insn;
++         insn = NEXT_INSN (insn))
++      {
++        rtx set;
++        rtx reg;
++
++        /* Ignore anything that is not a normal insn.  */
++        if (! INSN_P (insn))
++          continue;
++
++        /* Basically ignore anything that is not a simple SET.  We do have
++           to make sure to invalidate nonnull_local and set nonnull_killed
++           for such insns though.  */
++        set = single_set (insn);
++        if (!set)
++          {
++            note_stores (PATTERN (insn), invalidate_nonnull_info, npi);
++            continue;
++          }
++
++        /* See if we've got a usable memory load.  We handle it first
++           in case it uses its address register as a dest (which kills
++           the nonnull property).  */
++        if (GET_CODE (SET_SRC (set)) == MEM
++            && GET_CODE ((reg = XEXP (SET_SRC (set), 0))) == REG
++            && REGNO (reg) >= npi->min_reg
++            && REGNO (reg) < npi->max_reg)
++          SET_BIT (nonnull_local[current_block->index],
++                   REGNO (reg) - npi->min_reg);
++
++        /* Now invalidate stuff clobbered by this insn.  */
++        note_stores (PATTERN (insn), invalidate_nonnull_info, npi);
++
++        /* And handle stores, we do these last since any sets in INSN can
++           not kill the nonnull property if it is derived from a MEM
++           appearing in a SET_DEST.  */
++        if (GET_CODE (SET_DEST (set)) == MEM
++            && GET_CODE ((reg = XEXP (SET_DEST (set), 0))) == REG
++            && REGNO (reg) >= npi->min_reg
++            && REGNO (reg) < npi->max_reg)
++          SET_BIT (nonnull_local[current_block->index],
++                   REGNO (reg) - npi->min_reg);
++      }
++    }
++
++  /* Now compute global properties based on the local properties.   This
++     is a classic global availablity algorithm.  */
++  compute_available (nonnull_local, nonnull_killed,
++                   nonnull_avout, nonnull_avin);
++
++  /* Now look at each bb and see if it ends with a compare of a value
++     against zero.  */
++  FOR_EACH_BB (bb)
++    {
++      rtx last_insn = bb->end;
++      rtx condition, earliest;
++      int compare_and_branch;
++
++      /* Since MIN_REG is always at least FIRST_PSEUDO_REGISTER, and
++       since BLOCK_REG[BB] is zero if this block did not end with a
++       comparison against zero, this condition works.  */
++      if (block_reg[bb->index] < npi->min_reg
++        || block_reg[bb->index] >= npi->max_reg)
++      continue;
++
++      /* LAST_INSN is a conditional jump.  Get its condition.  */
++      condition = get_condition (last_insn, &earliest);
++
++      /* If we can't determine the condition then skip.  */
++      if (! condition)
++      continue;
++
++      /* Is the register known to have a nonzero value?  */
++      if (!TEST_BIT (nonnull_avout[bb->index], block_reg[bb->index] - npi->min_reg))
++      continue;
++
++      /* Try to compute whether the compare/branch at the loop end is one or
++       two instructions.  */
++      if (earliest == last_insn)
++      compare_and_branch = 1;
++      else if (earliest == prev_nonnote_insn (last_insn))
++      compare_and_branch = 2;
++      else
++      continue;
++
++      /* We know the register in this comparison is nonnull at exit from
++       this block.  We can optimize this comparison.  */
++      if (GET_CODE (condition) == NE)
++      {
++        rtx new_jump;
++
++        new_jump = emit_jump_insn_after (gen_jump (JUMP_LABEL (last_insn)),
++                                         last_insn);
++        JUMP_LABEL (new_jump) = JUMP_LABEL (last_insn);
++        LABEL_NUSES (JUMP_LABEL (new_jump))++;
++        emit_barrier_after (new_jump);
++      }
++
++      something_changed = 1;
++      delete_insn (last_insn);
++      if (compare_and_branch == 2)
++      delete_insn (earliest);
++      purge_dead_edges (bb);
++
++      /* Don't check this block again.  (Note that BLOCK_END is
++       invalid here; we deleted the last instruction in the
++       block.)  */
++      block_reg[bb->index] = 0;
++    }
++
++  return something_changed;
++}
++
++/* Find EQ/NE comparisons against zero which can be (indirectly) evaluated
++   at compile time.
++
++   This is conceptually similar to global constant/copy propagation and
++   classic global CSE (it even uses the same dataflow equations as cprop).
++
++   If a register is used as memory address with the form (mem (reg)), then we
++   know that REG can not be zero at that point in the program.  Any instruction
++   which sets REG "kills" this property.
++
++   So, if every path leading to a conditional branch has an available memory
++   reference of that form, then we know the register can not have the value
++   zero at the conditional branch.
++
++   So we merely need to compute the local properies and propagate that data
++   around the cfg, then optimize where possible.
++
++   We run this pass two times.  Once before CSE, then again after CSE.  This
++   has proven to be the most profitable approach.  It is rare for new
++   optimization opportunities of this nature to appear after the first CSE
++   pass.
++
++   This could probably be integrated with global cprop with a little work.  */
++
++int
++delete_null_pointer_checks (f)
++     rtx f ATTRIBUTE_UNUSED;
++{
++  sbitmap *nonnull_avin, *nonnull_avout;
++  unsigned int *block_reg;
++  basic_block bb;
++  int reg;
++  int regs_per_pass;
++  int max_reg;
++  struct null_pointer_info npi;
++  int something_changed = 0;
++
++  /* If we have only a single block, then there's nothing to do.  */
++  if (n_basic_blocks <= 1)
++    return 0;
++
++  /* Trying to perform global optimizations on flow graphs which have
++     a high connectivity will take a long time and is unlikely to be
++     particularly useful.
++
++     In normal circumstances a cfg should have about twice as many edges
++     as blocks.  But we do not want to punish small functions which have
++     a couple switch statements.  So we require a relatively large number
++     of basic blocks and the ratio of edges to blocks to be high.  */
++  if (n_basic_blocks > 1000 && n_edges / n_basic_blocks >= 20)
++    return 0;
++
++  /* We need four bitmaps, each with a bit for each register in each
++     basic block.  */
++  max_reg = max_reg_num ();
++  regs_per_pass = get_bitmap_width (4, last_basic_block, max_reg);
++
++  /* Allocate bitmaps to hold local and global properties.  */
++  npi.nonnull_local = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
++  npi.nonnull_killed = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
++  nonnull_avin = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
++  nonnull_avout = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
++
++  /* Go through the basic blocks, seeing whether or not each block
++     ends with a conditional branch whose condition is a comparison
++     against zero.  Record the register compared in BLOCK_REG.  */
++  block_reg = (unsigned int *) xcalloc (last_basic_block, sizeof (int));
++  FOR_EACH_BB (bb)
++    {
++      rtx last_insn = bb->end;
++      rtx condition, earliest, reg;
++
++      /* We only want conditional branches.  */
++      if (GET_CODE (last_insn) != JUMP_INSN
++        || !any_condjump_p (last_insn)
++        || !onlyjump_p (last_insn))
++      continue;
++
++      /* LAST_INSN is a conditional jump.  Get its condition.  */
++      condition = get_condition (last_insn, &earliest);
++
++      /* If we were unable to get the condition, or it is not an equality
++       comparison against zero then there's nothing we can do.  */
++      if (!condition
++        || (GET_CODE (condition) != NE && GET_CODE (condition) != EQ)
++        || GET_CODE (XEXP (condition, 1)) != CONST_INT
++        || (XEXP (condition, 1)
++            != CONST0_RTX (GET_MODE (XEXP (condition, 0)))))
++      continue;
++
++      /* We must be checking a register against zero.  */
++      reg = XEXP (condition, 0);
++      if (GET_CODE (reg) != REG)
++      continue;
++
++      block_reg[bb->index] = REGNO (reg);
++    }
++
++  /* Go through the algorithm for each block of registers.  */
++  for (reg = FIRST_PSEUDO_REGISTER; reg < max_reg; reg += regs_per_pass)
++    {
++      npi.min_reg = reg;
++      npi.max_reg = MIN (reg + regs_per_pass, max_reg);
++      something_changed |= delete_null_pointer_checks_1 (block_reg,
++                                                       nonnull_avin,
++                                                       nonnull_avout,
++                                                       &npi);
++    }
++
++  /* Free the table of registers compared at the end of every block.  */
++  free (block_reg);
++
++  /* Free bitmaps.  */
++  sbitmap_vector_free (npi.nonnull_local);
++  sbitmap_vector_free (npi.nonnull_killed);
++  sbitmap_vector_free (nonnull_avin);
++  sbitmap_vector_free (nonnull_avout);
++
++  return something_changed;
++}
++
++/* Code Hoisting variables and subroutines.  */
++
++/* Very busy expressions.  */
++static sbitmap *hoist_vbein;
++static sbitmap *hoist_vbeout;
++
++/* Hoistable expressions.  */
++static sbitmap *hoist_exprs;
++
++/* Dominator bitmaps.  */
++dominance_info dominators;
++
++/* ??? We could compute post dominators and run this algorithm in
++   reverse to perform tail merging, doing so would probably be
++   more effective than the tail merging code in jump.c.
++
++   It's unclear if tail merging could be run in parallel with
++   code hoisting.  It would be nice.  */
++
++/* Allocate vars used for code hoisting analysis.  */
++
++static void
++alloc_code_hoist_mem (n_blocks, n_exprs)
++     int n_blocks, n_exprs;
++{
++  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
++  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
++  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
++
++  hoist_vbein = sbitmap_vector_alloc (n_blocks, n_exprs);
++  hoist_vbeout = sbitmap_vector_alloc (n_blocks, n_exprs);
++  hoist_exprs = sbitmap_vector_alloc (n_blocks, n_exprs);
++  transpout = sbitmap_vector_alloc (n_blocks, n_exprs);
++}
++
++/* Free vars used for code hoisting analysis.  */
++
++static void
++free_code_hoist_mem ()
++{
++  sbitmap_vector_free (antloc);
++  sbitmap_vector_free (transp);
++  sbitmap_vector_free (comp);
++
++  sbitmap_vector_free (hoist_vbein);
++  sbitmap_vector_free (hoist_vbeout);
++  sbitmap_vector_free (hoist_exprs);
++  sbitmap_vector_free (transpout);
++
++  free_dominance_info (dominators);
++}
++
++/* Compute the very busy expressions at entry/exit from each block.
++
++   An expression is very busy if all paths from a given point
++   compute the expression.  */
++
++static void
++compute_code_hoist_vbeinout ()
++{
++  int changed, passes;
++  basic_block bb;
++
++  sbitmap_vector_zero (hoist_vbeout, last_basic_block);
++  sbitmap_vector_zero (hoist_vbein, last_basic_block);
++
++  passes = 0;
++  changed = 1;
++
++  while (changed)
++    {
++      changed = 0;
++
++      /* We scan the blocks in the reverse order to speed up
++       the convergence.  */
++      FOR_EACH_BB_REVERSE (bb)
++      {
++        changed |= sbitmap_a_or_b_and_c_cg (hoist_vbein[bb->index], antloc[bb->index],
++                                            hoist_vbeout[bb->index], transp[bb->index]);
++        if (bb->next_bb != EXIT_BLOCK_PTR)
++          sbitmap_intersection_of_succs (hoist_vbeout[bb->index], hoist_vbein, bb->index);
++      }
++
++      passes++;
++    }
++
++  if (gcse_file)
++    fprintf (gcse_file, "hoisting vbeinout computation: %d passes\n", passes);
++}
++
++/* Top level routine to do the dataflow analysis needed by code hoisting.  */
++
++static void
++compute_code_hoist_data ()
++{
++  compute_local_properties (transp, comp, antloc, &expr_hash_table);
++  compute_transpout ();
++  compute_code_hoist_vbeinout ();
++  dominators = calculate_dominance_info (CDI_DOMINATORS);
++  if (gcse_file)
++    fprintf (gcse_file, "\n");
++}
++
++/* Determine if the expression identified by EXPR_INDEX would
++   reach BB unimpared if it was placed at the end of EXPR_BB.
++
++   It's unclear exactly what Muchnick meant by "unimpared".  It seems
++   to me that the expression must either be computed or transparent in
++   *every* block in the path(s) from EXPR_BB to BB.  Any other definition
++   would allow the expression to be hoisted out of loops, even if
++   the expression wasn't a loop invariant.
++
++   Contrast this to reachability for PRE where an expression is
++   considered reachable if *any* path reaches instead of *all*
++   paths.  */
++
++static int
++hoist_expr_reaches_here_p (expr_bb, expr_index, bb, visited)
++     basic_block expr_bb;
++     int expr_index;
++     basic_block bb;
++     char *visited;
++{
++  edge pred;
++  int visited_allocated_locally = 0;
++
++
++  if (visited == NULL)
++    {
++      visited_allocated_locally = 1;
++      visited = xcalloc (last_basic_block, 1);
++    }
++
++  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
++    {
++      basic_block pred_bb = pred->src;
++
++      if (pred->src == ENTRY_BLOCK_PTR)
++      break;
++      else if (pred_bb == expr_bb)
++      continue;
++      else if (visited[pred_bb->index])
++      continue;
++
++      /* Does this predecessor generate this expression?  */
++      else if (TEST_BIT (comp[pred_bb->index], expr_index))
++      break;
++      else if (! TEST_BIT (transp[pred_bb->index], expr_index))
++      break;
++
++      /* Not killed.  */
++      else
++      {
++        visited[pred_bb->index] = 1;
++        if (! hoist_expr_reaches_here_p (expr_bb, expr_index,
++                                         pred_bb, visited))
++          break;
++      }
++    }
++  if (visited_allocated_locally)
++    free (visited);
++
++  return (pred == NULL);
++}
++\f
++/* Actually perform code hoisting.  */
++
++static void
++hoist_code ()
++{
++  basic_block bb, dominated;
++  basic_block *domby;
++  unsigned int domby_len;
++  unsigned int i,j;
++  struct expr **index_map;
++  struct expr *expr;
++
++  sbitmap_vector_zero (hoist_exprs, last_basic_block);
++
++  /* Compute a mapping from expression number (`bitmap_index') to
++     hash table entry.  */
++
++  index_map = (struct expr **) xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
++  for (i = 0; i < expr_hash_table.size; i++)
++    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
++      index_map[expr->bitmap_index] = expr;
++
++  /* Walk over each basic block looking for potentially hoistable
++     expressions, nothing gets hoisted from the entry block.  */
++  FOR_EACH_BB (bb)
++    {
++      int found = 0;
++      int insn_inserted_p;
++
++      domby_len = get_dominated_by (dominators, bb, &domby);
++      /* Examine each expression that is very busy at the exit of this
++       block.  These are the potentially hoistable expressions.  */
++      for (i = 0; i < hoist_vbeout[bb->index]->n_bits; i++)
++      {
++        int hoistable = 0;
++
++        if (TEST_BIT (hoist_vbeout[bb->index], i)
++            && TEST_BIT (transpout[bb->index], i))
++          {
++            /* We've found a potentially hoistable expression, now
++               we look at every block BB dominates to see if it
++               computes the expression.  */
++            for (j = 0; j < domby_len; j++)
++              {
++                dominated = domby[j];
++                /* Ignore self dominance.  */
++                if (bb == dominated)
++                  continue;
++                /* We've found a dominated block, now see if it computes
++                   the busy expression and whether or not moving that
++                   expression to the "beginning" of that block is safe.  */
++                if (!TEST_BIT (antloc[dominated->index], i))
++                  continue;
++
++                /* Note if the expression would reach the dominated block
++                   unimpared if it was placed at the end of BB.
++
++                   Keep track of how many times this expression is hoistable
++                   from a dominated block into BB.  */
++                if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
++                  hoistable++;
++              }
++
++            /* If we found more than one hoistable occurrence of this
++               expression, then note it in the bitmap of expressions to
++               hoist.  It makes no sense to hoist things which are computed
++               in only one BB, and doing so tends to pessimize register
++               allocation.  One could increase this value to try harder
++               to avoid any possible code expansion due to register
++               allocation issues; however experiments have shown that
++               the vast majority of hoistable expressions are only movable
++               from two successors, so raising this threshhold is likely
++               to nullify any benefit we get from code hoisting.  */
++            if (hoistable > 1)
++              {
++                SET_BIT (hoist_exprs[bb->index], i);
++                found = 1;
++              }
++          }
++      }
++      /* If we found nothing to hoist, then quit now.  */
++      if (! found)
++        {
++        free (domby);
++      continue;
++      }
++
++      /* Loop over all the hoistable expressions.  */
++      for (i = 0; i < hoist_exprs[bb->index]->n_bits; i++)
++      {
++        /* We want to insert the expression into BB only once, so
++           note when we've inserted it.  */
++        insn_inserted_p = 0;
++
++        /* These tests should be the same as the tests above.  */
++        if (TEST_BIT (hoist_vbeout[bb->index], i))
++          {
++            /* We've found a potentially hoistable expression, now
++               we look at every block BB dominates to see if it
++               computes the expression.  */
++            for (j = 0; j < domby_len; j++)
++              {
++                dominated = domby[j];
++                /* Ignore self dominance.  */
++                if (bb == dominated)
++                  continue;
++
++                /* We've found a dominated block, now see if it computes
++                   the busy expression and whether or not moving that
++                   expression to the "beginning" of that block is safe.  */
++                if (!TEST_BIT (antloc[dominated->index], i))
++                  continue;
++
++                /* The expression is computed in the dominated block and
++                   it would be safe to compute it at the start of the
++                   dominated block.  Now we have to determine if the
++                   expression would reach the dominated block if it was
++                   placed at the end of BB.  */
++                if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
++                  {
++                    struct expr *expr = index_map[i];
++                    struct occr *occr = expr->antic_occr;
++                    rtx insn;
++                    rtx set;
++
++                    /* Find the right occurrence of this expression.  */
++                    while (BLOCK_FOR_INSN (occr->insn) != dominated && occr)
++                      occr = occr->next;
++
++                    /* Should never happen.  */
++                    if (!occr)
++                      abort ();
++
++                    insn = occr->insn;
++
++                    set = single_set (insn);
++                    if (! set)
++                      abort ();
++
++                    /* Create a pseudo-reg to store the result of reaching
++                       expressions into.  Get the mode for the new pseudo
++                       from the mode of the original destination pseudo.  */
++                    if (expr->reaching_reg == NULL)
++                      expr->reaching_reg
++                        = gen_reg_rtx (GET_MODE (SET_DEST (set)));
++
++                    gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
++                    delete_insn (insn);
++                    occr->deleted_p = 1;
++                    if (!insn_inserted_p)
++                      {
++                        insert_insn_end_bb (index_map[i], bb, 0);
++                        insn_inserted_p = 1;
++                      }
++                  }
++              }
++          }
++      }
++      free (domby);
++    }
++
++  free (index_map);
++}
++
++/* Top level routine to perform one code hoisting (aka unification) pass
++
++   Return nonzero if a change was made.  */
++
++static int
++one_code_hoisting_pass ()
++{
++  int changed = 0;
++
++  alloc_hash_table (max_cuid, &expr_hash_table, 0);
++  compute_hash_table (&expr_hash_table);
++  if (gcse_file)
++    dump_hash_table (gcse_file, "Code Hosting Expressions", &expr_hash_table);
++
++  if (expr_hash_table.n_elems > 0)
++    {
++      alloc_code_hoist_mem (last_basic_block, expr_hash_table.n_elems);
++      compute_code_hoist_data ();
++      hoist_code ();
++      free_code_hoist_mem ();
++    }
++
++  free_hash_table (&expr_hash_table);
++
++  return changed;
++}
++\f
++/*  Here we provide the things required to do store motion towards
++    the exit. In order for this to be effective, gcse also needed to
++    be taught how to move a load when it is kill only by a store to itself.
++
++          int i;
++          float a[10];
++
++          void foo(float scale)
++          {
++            for (i=0; i<10; i++)
++              a[i] *= scale;
++          }
++
++    'i' is both loaded and stored to in the loop. Normally, gcse cannot move
++    the load out since its live around the loop, and stored at the bottom
++    of the loop.
++
++      The 'Load Motion' referred to and implemented in this file is
++    an enhancement to gcse which when using edge based lcm, recognizes
++    this situation and allows gcse to move the load out of the loop.
++
++      Once gcse has hoisted the load, store motion can then push this
++    load towards the exit, and we end up with no loads or stores of 'i'
++    in the loop.  */
++
++/* This will search the ldst list for a matching expression. If it
++   doesn't find one, we create one and initialize it.  */
++
++static struct ls_expr *
++ldst_entry (x)
++     rtx x;
++{
++  struct ls_expr * ptr;
++
++  for (ptr = first_ls_expr(); ptr != NULL; ptr = next_ls_expr (ptr))
++    if (expr_equiv_p (ptr->pattern, x))
++      break;
++
++  if (!ptr)
++    {
++      ptr = (struct ls_expr *) xmalloc (sizeof (struct ls_expr));
++
++      ptr->next         = pre_ldst_mems;
++      ptr->expr         = NULL;
++      ptr->pattern      = x;
++      ptr->loads        = NULL_RTX;
++      ptr->stores       = NULL_RTX;
++      ptr->reaching_reg = NULL_RTX;
++      ptr->invalid      = 0;
++      ptr->index        = 0;
++      ptr->hash_index   = 0;
++      pre_ldst_mems     = ptr;
++    }
++
++  return ptr;
++}
++
++/* Free up an individual ldst entry.  */
++
++static void
++free_ldst_entry (ptr)
++     struct ls_expr * ptr;
++{
++  free_INSN_LIST_list (& ptr->loads);
++  free_INSN_LIST_list (& ptr->stores);
++
++  free (ptr);
++}
++
++/* Free up all memory associated with the ldst list.  */
++
++static void
++free_ldst_mems ()
++{
++  while (pre_ldst_mems)
++    {
++      struct ls_expr * tmp = pre_ldst_mems;
++
++      pre_ldst_mems = pre_ldst_mems->next;
++
++      free_ldst_entry (tmp);
++    }
++
++  pre_ldst_mems = NULL;
++}
++
++/* Dump debugging info about the ldst list.  */
++
++static void
++print_ldst_list (file)
++     FILE * file;
++{
++  struct ls_expr * ptr;
++
++  fprintf (file, "LDST list: \n");
++
++  for (ptr = first_ls_expr(); ptr != NULL; ptr = next_ls_expr (ptr))
++    {
++      fprintf (file, "  Pattern (%3d): ", ptr->index);
++
++      print_rtl (file, ptr->pattern);
++
++      fprintf (file, "\n       Loads : ");
++
++      if (ptr->loads)
++      print_rtl (file, ptr->loads);
++      else
++      fprintf (file, "(nil)");
++
++      fprintf (file, "\n      Stores : ");
++
++      if (ptr->stores)
++      print_rtl (file, ptr->stores);
++      else
++      fprintf (file, "(nil)");
++
++      fprintf (file, "\n\n");
++    }
++
++  fprintf (file, "\n");
++}
++
++/* Returns 1 if X is in the list of ldst only expressions.  */
++
++static struct ls_expr *
++find_rtx_in_ldst (x)
++     rtx x;
++{
++  struct ls_expr * ptr;
++
++  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
++    if (expr_equiv_p (ptr->pattern, x) && ! ptr->invalid)
++      return ptr;
++
++  return NULL;
++}
++
++/* Assign each element of the list of mems a monotonically increasing value.  */
++
++static int
++enumerate_ldsts ()
++{
++  struct ls_expr * ptr;
++  int n = 0;
++
++  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
++    ptr->index = n++;
++
++  return n;
++}
++
++/* Return first item in the list.  */
++
++static inline struct ls_expr *
++first_ls_expr ()
++{
++  return pre_ldst_mems;
++}
++
++/* Return the next item in ther list after the specified one.  */
++
++static inline struct ls_expr *
++next_ls_expr (ptr)
++     struct ls_expr * ptr;
++{
++  return ptr->next;
++}
++\f
++/* Load Motion for loads which only kill themselves.  */
++
++/* Return true if x is a simple MEM operation, with no registers or
++   side effects. These are the types of loads we consider for the
++   ld_motion list, otherwise we let the usual aliasing take care of it.  */
++
++static int
++simple_mem (x)
++     rtx x;
++{
++  if (GET_CODE (x) != MEM)
++    return 0;
++
++  if (MEM_VOLATILE_P (x))
++    return 0;
++
++  if (GET_MODE (x) == BLKmode)
++    return 0;
++
++  if (!rtx_varies_p (XEXP (x, 0), 0))
++    return 1;
++
++  return 0;
++}
++
++/* Make sure there isn't a buried reference in this pattern anywhere.
++   If there is, invalidate the entry for it since we're not capable
++   of fixing it up just yet.. We have to be sure we know about ALL
++   loads since the aliasing code will allow all entries in the
++   ld_motion list to not-alias itself.  If we miss a load, we will get
++   the wrong value since gcse might common it and we won't know to
++   fix it up.  */
++
++static void
++invalidate_any_buried_refs (x)
++     rtx x;
++{
++  const char * fmt;
++  int i, j;
++  struct ls_expr * ptr;
++
++  /* Invalidate it in the list.  */
++  if (GET_CODE (x) == MEM && simple_mem (x))
++    {
++      ptr = ldst_entry (x);
++      ptr->invalid = 1;
++    }
++
++  /* Recursively process the insn.  */
++  fmt = GET_RTX_FORMAT (GET_CODE (x));
++
++  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      invalidate_any_buried_refs (XEXP (x, i));
++      else if (fmt[i] == 'E')
++      for (j = XVECLEN (x, i) - 1; j >= 0; j--)
++        invalidate_any_buried_refs (XVECEXP (x, i, j));
++    }
++}
++
++/* Find all the 'simple' MEMs which are used in LOADs and STORES. Simple
++   being defined as MEM loads and stores to symbols, with no
++   side effects and no registers in the expression. If there are any
++   uses/defs which don't match this criteria, it is invalidated and
++   trimmed out later.  */
++
++static void
++compute_ld_motion_mems ()
++{
++  struct ls_expr * ptr;
++  basic_block bb;
++  rtx insn;
++
++  pre_ldst_mems = NULL;
++
++  FOR_EACH_BB (bb)
++    {
++      for (insn = bb->head;
++         insn && insn != NEXT_INSN (bb->end);
++         insn = NEXT_INSN (insn))
++      {
++        if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
++          {
++            if (GET_CODE (PATTERN (insn)) == SET)
++              {
++                rtx src = SET_SRC (PATTERN (insn));
++                rtx dest = SET_DEST (PATTERN (insn));
++
++                /* Check for a simple LOAD...  */
++                if (GET_CODE (src) == MEM && simple_mem (src))
++                  {
++                    ptr = ldst_entry (src);
++                    if (GET_CODE (dest) == REG)
++                      ptr->loads = alloc_INSN_LIST (insn, ptr->loads);
++                    else
++                      ptr->invalid = 1;
++                  }
++                else
++                  {
++                    /* Make sure there isn't a buried load somewhere.  */
++                    invalidate_any_buried_refs (src);
++                  }
++
++                /* Check for stores. Don't worry about aliased ones, they
++                   will block any movement we might do later. We only care
++                   about this exact pattern since those are the only
++                   circumstance that we will ignore the aliasing info.  */
++                if (GET_CODE (dest) == MEM && simple_mem (dest))
++                  {
++                    ptr = ldst_entry (dest);
++
++                    if (GET_CODE (src) != MEM
++                        && GET_CODE (src) != ASM_OPERANDS)
++                      ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
++                    else
++                      ptr->invalid = 1;
++                  }
++              }
++            else
++              invalidate_any_buried_refs (PATTERN (insn));
++          }
++      }
++    }
++}
++
++/* Remove any references that have been either invalidated or are not in the
++   expression list for pre gcse.  */
++
++static void
++trim_ld_motion_mems ()
++{
++  struct ls_expr * last = NULL;
++  struct ls_expr * ptr = first_ls_expr ();
++
++  while (ptr != NULL)
++    {
++      int del = ptr->invalid;
++      struct expr * expr = NULL;
++
++      /* Delete if entry has been made invalid.  */
++      if (!del)
++      {
++        unsigned int i;
++
++        del = 1;
++        /* Delete if we cannot find this mem in the expression list.  */
++        for (i = 0; i < expr_hash_table.size && del; i++)
++          {
++            for (expr = expr_hash_table.table[i];
++                 expr != NULL;
++                 expr = expr->next_same_hash)
++              if (expr_equiv_p (expr->expr, ptr->pattern))
++                {
++                  del = 0;
++                  break;
++                }
++          }
++      }
++
++      if (del)
++      {
++        if (last != NULL)
++          {
++            last->next = ptr->next;
++            free_ldst_entry (ptr);
++            ptr = last->next;
++          }
++        else
++          {
++            pre_ldst_mems = pre_ldst_mems->next;
++            free_ldst_entry (ptr);
++            ptr = pre_ldst_mems;
++          }
++      }
++      else
++      {
++        /* Set the expression field if we are keeping it.  */
++        last = ptr;
++        ptr->expr = expr;
++        ptr = ptr->next;
++      }
++    }
++
++  /* Show the world what we've found.  */
++  if (gcse_file && pre_ldst_mems != NULL)
++    print_ldst_list (gcse_file);
++}
++
++/* This routine will take an expression which we are replacing with
++   a reaching register, and update any stores that are needed if
++   that expression is in the ld_motion list.  Stores are updated by
++   copying their SRC to the reaching register, and then storeing
++   the reaching register into the store location. These keeps the
++   correct value in the reaching register for the loads.  */
++
++static void
++update_ld_motion_stores (expr)
++     struct expr * expr;
++{
++  struct ls_expr * mem_ptr;
++
++  if ((mem_ptr = find_rtx_in_ldst (expr->expr)))
++    {
++      /* We can try to find just the REACHED stores, but is shouldn't
++       matter to set the reaching reg everywhere...  some might be
++       dead and should be eliminated later.  */
++
++      /* We replace  SET mem = expr   with
++         SET reg = expr
++         SET mem = reg , where reg is the
++         reaching reg used in the load.  */
++      rtx list = mem_ptr->stores;
++
++      for ( ; list != NULL_RTX; list = XEXP (list, 1))
++      {
++        rtx insn = XEXP (list, 0);
++        rtx pat = PATTERN (insn);
++        rtx src = SET_SRC (pat);
++        rtx reg = expr->reaching_reg;
++        rtx copy, new;
++
++        /* If we've already copied it, continue.  */
++        if (expr->reaching_reg == src)
++          continue;
++
++        if (gcse_file)
++          {
++            fprintf (gcse_file, "PRE:  store updated with reaching reg ");
++            print_rtl (gcse_file, expr->reaching_reg);
++            fprintf (gcse_file, ":\n  ");
++            print_inline_rtx (gcse_file, insn, 8);
++            fprintf (gcse_file, "\n");
++          }
++
++        copy = gen_move_insn ( reg, SET_SRC (pat));
++        new = emit_insn_before (copy, insn);
++        record_one_set (REGNO (reg), new);
++        SET_SRC (pat) = reg;
++
++        /* un-recognize this pattern since it's probably different now.  */
++        INSN_CODE (insn) = -1;
++        gcse_create_count++;
++      }
++    }
++}
++\f
++/* Store motion code.  */
++
++/* This is used to communicate the target bitvector we want to use in the
++   reg_set_info routine when called via the note_stores mechanism.  */
++static sbitmap * regvec;
++
++/* Used in computing the reverse edge graph bit vectors.  */
++static sbitmap * st_antloc;
++
++/* Global holding the number of store expressions we are dealing with.  */
++static int num_stores;
++
++/* Checks to set if we need to mark a register set. Called from note_stores.  */
++
++static void
++reg_set_info (dest, setter, data)
++     rtx dest, setter ATTRIBUTE_UNUSED;
++     void * data ATTRIBUTE_UNUSED;
++{
++  if (GET_CODE (dest) == SUBREG)
++    dest = SUBREG_REG (dest);
++
++  if (GET_CODE (dest) == REG)
++    SET_BIT (*regvec, REGNO (dest));
++}
++
++/* Return nonzero if the register operands of expression X are killed
++   anywhere in basic block BB.  */
++
++static int
++store_ops_ok (x, bb)
++     rtx x;
++     basic_block bb;
++{
++  int i;
++  enum rtx_code code;
++  const char * fmt;
++
++  /* Repeat is used to turn tail-recursion into iteration.  */
++ repeat:
++
++  if (x == 0)
++    return 1;
++
++  code = GET_CODE (x);
++  switch (code)
++    {
++    case REG:
++      /* If a reg has changed after us in this
++         block, the operand has been killed.  */
++      return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
++
++    case MEM:
++      x = XEXP (x, 0);
++      goto repeat;
++
++    case PRE_DEC:
++    case PRE_INC:
++    case POST_DEC:
++    case POST_INC:
++      return 0;
++
++    case PC:
++    case CC0: /*FIXME*/
++    case CONST:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case LABEL_REF:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++      return 1;
++
++    default:
++      break;
++    }
++
++  i = GET_RTX_LENGTH (code) - 1;
++  fmt = GET_RTX_FORMAT (code);
++
++  for (; i >= 0; i--)
++    {
++      if (fmt[i] == 'e')
++      {
++        rtx tem = XEXP (x, i);
++
++        /* If we are about to do the last recursive call
++           needed at this level, change it into iteration.
++           This function is called enough to be worth it.  */
++        if (i == 0)
++          {
++            x = tem;
++            goto repeat;
++          }
++
++        if (! store_ops_ok (tem, bb))
++          return 0;
++      }
++      else if (fmt[i] == 'E')
++      {
++        int j;
++
++        for (j = 0; j < XVECLEN (x, i); j++)
++          {
++            if (! store_ops_ok (XVECEXP (x, i, j), bb))
++              return 0;
++          }
++      }
++    }
++
++  return 1;
++}
++
++/* Determine whether insn is MEM store pattern that we will consider moving.  */
++
++static void
++find_moveable_store (insn)
++     rtx insn;
++{
++  struct ls_expr * ptr;
++  rtx dest = PATTERN (insn);
++
++  if (GET_CODE (dest) != SET
++      || GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
++    return;
++
++  dest = SET_DEST (dest);
++
++  if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest)
++      || GET_MODE (dest) == BLKmode)
++    return;
++
++  if (GET_CODE (XEXP (dest, 0)) != SYMBOL_REF)
++      return;
++
++  if (rtx_varies_p (XEXP (dest, 0), 0))
++    return;
++
++  ptr = ldst_entry (dest);
++  ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
++}
++
++/* Perform store motion. Much like gcse, except we move expressions the
++   other way by looking at the flowgraph in reverse.  */
++
++static int
++compute_store_table ()
++{
++  int ret;
++  basic_block bb;
++  unsigned regno;
++  rtx insn, pat;
++
++  max_gcse_regno = max_reg_num ();
++
++  reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (last_basic_block,
++                                                     max_gcse_regno);
++  sbitmap_vector_zero (reg_set_in_block, last_basic_block);
++  pre_ldst_mems = 0;
++
++  /* Find all the stores we care about.  */
++  FOR_EACH_BB (bb)
++    {
++      regvec = & (reg_set_in_block[bb->index]);
++      for (insn = bb->end;
++         insn && insn != PREV_INSN (bb->end);
++         insn = PREV_INSN (insn))
++      {
++        /* Ignore anything that is not a normal insn.  */
++        if (! INSN_P (insn))
++          continue;
++
++        if (GET_CODE (insn) == CALL_INSN)
++          {
++            bool clobbers_all = false;
++#ifdef NON_SAVING_SETJMP
++            if (NON_SAVING_SETJMP
++                && find_reg_note (insn, REG_SETJMP, NULL_RTX))
++              clobbers_all = true;
++#endif
++
++            for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
++              if (clobbers_all
++                  || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
++                SET_BIT (reg_set_in_block[bb->index], regno);
++          }
++
++        pat = PATTERN (insn);
++        note_stores (pat, reg_set_info, NULL);
++
++        /* Now that we've marked regs, look for stores.  */
++        if (GET_CODE (pat) == SET)
++          find_moveable_store (insn);
++      }
++    }
++
++  ret = enumerate_ldsts ();
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "Store Motion Expressions.\n");
++      print_ldst_list (gcse_file);
++    }
++
++  return ret;
++}
++
++/* Check to see if the load X is aliased with STORE_PATTERN.  */
++
++static int
++load_kills_store (x, store_pattern)
++     rtx x, store_pattern;
++{
++  if (true_dependence (x, GET_MODE (x), store_pattern, rtx_addr_varies_p))
++    return 1;
++  return 0;
++}
++
++/* Go through the entire insn X, looking for any loads which might alias
++   STORE_PATTERN.  Return 1 if found.  */
++
++static int
++find_loads (x, store_pattern)
++     rtx x, store_pattern;
++{
++  const char * fmt;
++  int i, j;
++  int ret = 0;
++
++  if (!x)
++    return 0;
++
++  if (GET_CODE (x) == SET)
++    x = SET_SRC (x);
++
++  if (GET_CODE (x) == MEM)
++    {
++      if (load_kills_store (x, store_pattern))
++      return 1;
++    }
++
++  /* Recursively process the insn.  */
++  fmt = GET_RTX_FORMAT (GET_CODE (x));
++
++  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
++    {
++      if (fmt[i] == 'e')
++      ret |= find_loads (XEXP (x, i), store_pattern);
++      else if (fmt[i] == 'E')
++      for (j = XVECLEN (x, i) - 1; j >= 0; j--)
++        ret |= find_loads (XVECEXP (x, i, j), store_pattern);
++    }
++  return ret;
++}
++
++/* Check if INSN kills the store pattern X (is aliased with it).
++   Return 1 if it it does.  */
++
++static int
++store_killed_in_insn (x, insn)
++     rtx x, insn;
++{
++  if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
++    return 0;
++
++  if (GET_CODE (insn) == CALL_INSN)
++    {
++      /* A normal or pure call might read from pattern,
++       but a const call will not.  */
++      return ! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn);
++    }
++
++  if (GET_CODE (PATTERN (insn)) == SET)
++    {
++      rtx pat = PATTERN (insn);
++      /* Check for memory stores to aliased objects.  */
++      if (GET_CODE (SET_DEST (pat)) == MEM && !expr_equiv_p (SET_DEST (pat), x))
++      /* pretend its a load and check for aliasing.  */
++      if (find_loads (SET_DEST (pat), x))
++        return 1;
++      return find_loads (SET_SRC (pat), x);
++    }
++  else
++    return find_loads (PATTERN (insn), x);
++}
++
++/* Returns 1 if the expression X is loaded or clobbered on or after INSN
++   within basic block BB.  */
++
++static int
++store_killed_after (x, insn, bb)
++     rtx x, insn;
++     basic_block bb;
++{
++  rtx last = bb->end;
++
++  if (insn == last)
++    return 0;
++
++  /* Check if the register operands of the store are OK in this block.
++     Note that if registers are changed ANYWHERE in the block, we'll
++     decide we can't move it, regardless of whether it changed above
++     or below the store. This could be improved by checking the register
++     operands while lookinng for aliasing in each insn.  */
++  if (!store_ops_ok (XEXP (x, 0), bb))
++    return 1;
++
++  for ( ; insn && insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
++    if (store_killed_in_insn (x, insn))
++      return 1;
++
++  return 0;
++}
++
++/* Returns 1 if the expression X is loaded or clobbered on or before INSN
++   within basic block BB.  */
++static int
++store_killed_before (x, insn, bb)
++     rtx x, insn;
++     basic_block bb;
++{
++  rtx first = bb->head;
++
++  if (insn == first)
++    return store_killed_in_insn (x, insn);
++
++  /* Check if the register operands of the store are OK in this block.
++     Note that if registers are changed ANYWHERE in the block, we'll
++     decide we can't move it, regardless of whether it changed above
++     or below the store. This could be improved by checking the register
++     operands while lookinng for aliasing in each insn.  */
++  if (!store_ops_ok (XEXP (x, 0), bb))
++    return 1;
++
++  for ( ; insn && insn != PREV_INSN (first); insn = PREV_INSN (insn))
++    if (store_killed_in_insn (x, insn))
++      return 1;
++
++  return 0;
++}
++
++#define ANTIC_STORE_LIST(x)   ((x)->loads)
++#define AVAIL_STORE_LIST(x)   ((x)->stores)
++
++/* Given the table of available store insns at the end of blocks,
++   determine which ones are not killed by aliasing, and generate
++   the appropriate vectors for gen and killed.  */
++static void
++build_store_vectors ()
++{
++  basic_block bb, b;
++  rtx insn, st;
++  struct ls_expr * ptr;
++
++  /* Build the gen_vector. This is any store in the table which is not killed
++     by aliasing later in its block.  */
++  ae_gen = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
++  sbitmap_vector_zero (ae_gen, last_basic_block);
++
++  st_antloc = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
++  sbitmap_vector_zero (st_antloc, last_basic_block);
++
++  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
++    {
++      /* Put all the stores into either the antic list, or the avail list,
++       or both.  */
++      rtx store_list = ptr->stores;
++      ptr->stores = NULL_RTX;
++
++      for (st = store_list; st != NULL; st = XEXP (st, 1))
++      {
++        insn = XEXP (st, 0);
++        bb = BLOCK_FOR_INSN (insn);
++
++        if (!store_killed_after (ptr->pattern, insn, bb))
++          {
++            /* If we've already seen an availale expression in this block,
++               we can delete the one we saw already (It occurs earlier in
++               the block), and replace it with this one). We'll copy the
++               old SRC expression to an unused register in case there
++               are any side effects.  */
++            if (TEST_BIT (ae_gen[bb->index], ptr->index))
++              {
++                /* Find previous store.  */
++                rtx st;
++                for (st = AVAIL_STORE_LIST (ptr); st ; st = XEXP (st, 1))
++                  if (BLOCK_FOR_INSN (XEXP (st, 0)) == bb)
++                    break;
++                if (st)
++                  {
++                    rtx r = gen_reg_rtx (GET_MODE (ptr->pattern));
++                    if (gcse_file)
++                      fprintf (gcse_file, "Removing redundant store:\n");
++                    replace_store_insn (r, XEXP (st, 0), bb);
++                    XEXP (st, 0) = insn;
++                    continue;
++                  }
++              }
++            SET_BIT (ae_gen[bb->index], ptr->index);
++            AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
++                                                      AVAIL_STORE_LIST (ptr));
++          }
++
++        if (!store_killed_before (ptr->pattern, insn, bb))
++          {
++            SET_BIT (st_antloc[BLOCK_NUM (insn)], ptr->index);
++            ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
++                                                      ANTIC_STORE_LIST (ptr));
++          }
++      }
++
++      /* Free the original list of store insns.  */
++      free_INSN_LIST_list (&store_list);
++    }
++
++  ae_kill = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
++  sbitmap_vector_zero (ae_kill, last_basic_block);
++
++  transp = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
++  sbitmap_vector_zero (transp, last_basic_block);
++
++  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
++    FOR_EACH_BB (b)
++      {
++      if (store_killed_after (ptr->pattern, b->head, b))
++        {
++          /* The anticipatable expression is not killed if it's gen'd.  */
++          /*
++            We leave this check out for now. If we have a code sequence
++            in a block which looks like:
++                      ST MEMa = x
++                      L     y = MEMa
++                      ST MEMa = z
++            We should flag this as having an ANTIC expression, NOT
++            transparent, NOT killed, and AVAIL.
++            Unfortunately, since we haven't re-written all loads to
++            use the reaching reg, we'll end up doing an incorrect
++            Load in the middle here if we push the store down. It happens in
++                  gcc.c-torture/execute/960311-1.c with -O3
++            If we always kill it in this case, we'll sometimes do
++            uneccessary work, but it shouldn't actually hurt anything.
++          if (!TEST_BIT (ae_gen[b], ptr->index)).  */
++          SET_BIT (ae_kill[b->index], ptr->index);
++        }
++      else
++        SET_BIT (transp[b->index], ptr->index);
++      }
++
++  /* Any block with no exits calls some non-returning function, so
++     we better mark the store killed here, or we might not store to
++     it at all.  If we knew it was abort, we wouldn't have to store,
++     but we don't know that for sure.  */
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
++      print_ldst_list (gcse_file);
++      dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, last_basic_block);
++      dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, last_basic_block);
++      dump_sbitmap_vector (gcse_file, "Transpt", "", transp, last_basic_block);
++      dump_sbitmap_vector (gcse_file, "st_avloc", "", ae_gen, last_basic_block);
++    }
++}
++
++/* Insert an instruction at the begining of a basic block, and update
++   the BLOCK_HEAD if needed.  */
++
++static void
++insert_insn_start_bb (insn, bb)
++     rtx insn;
++     basic_block bb;
++{
++  /* Insert at start of successor block.  */
++  rtx prev = PREV_INSN (bb->head);
++  rtx before = bb->head;
++  while (before != 0)
++    {
++      if (GET_CODE (before) != CODE_LABEL
++        && (GET_CODE (before) != NOTE
++            || NOTE_LINE_NUMBER (before) != NOTE_INSN_BASIC_BLOCK))
++      break;
++      prev = before;
++      if (prev == bb->end)
++      break;
++      before = NEXT_INSN (before);
++    }
++
++  insn = emit_insn_after (insn, prev);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "STORE_MOTION  insert store at start of BB %d:\n",
++             bb->index);
++      print_inline_rtx (gcse_file, insn, 6);
++      fprintf (gcse_file, "\n");
++    }
++}
++
++/* This routine will insert a store on an edge. EXPR is the ldst entry for
++   the memory reference, and E is the edge to insert it on.  Returns nonzero
++   if an edge insertion was performed.  */
++
++static int
++insert_store (expr, e)
++     struct ls_expr * expr;
++     edge e;
++{
++  rtx reg, insn;
++  basic_block bb;
++  edge tmp;
++
++  /* We did all the deleted before this insert, so if we didn't delete a
++     store, then we haven't set the reaching reg yet either.  */
++  if (expr->reaching_reg == NULL_RTX)
++    return 0;
++
++  reg = expr->reaching_reg;
++  insn = gen_move_insn (expr->pattern, reg);
++
++  /* If we are inserting this expression on ALL predecessor edges of a BB,
++     insert it at the start of the BB, and reset the insert bits on the other
++     edges so we don't try to insert it on the other edges.  */
++  bb = e->dest;
++  for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
++    {
++      int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
++      if (index == EDGE_INDEX_NO_EDGE)
++      abort ();
++      if (! TEST_BIT (pre_insert_map[index], expr->index))
++      break;
++    }
++
++  /* If tmp is NULL, we found an insertion on every edge, blank the
++     insertion vector for these edges, and insert at the start of the BB.  */
++  if (!tmp && bb != EXIT_BLOCK_PTR)
++    {
++      for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
++      {
++        int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
++        RESET_BIT (pre_insert_map[index], expr->index);
++      }
++      insert_insn_start_bb (insn, bb);
++      return 0;
++    }
++
++  /* We can't insert on this edge, so we'll insert at the head of the
++     successors block.  See Morgan, sec 10.5.  */
++  if ((e->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
++    {
++      insert_insn_start_bb (insn, bb);
++      return 0;
++    }
++
++  insert_insn_on_edge (insn, e);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "STORE_MOTION  insert insn on edge (%d, %d):\n",
++             e->src->index, e->dest->index);
++      print_inline_rtx (gcse_file, insn, 6);
++      fprintf (gcse_file, "\n");
++    }
++
++  return 1;
++}
++
++/* This routine will replace a store with a SET to a specified register.  */
++
++static void
++replace_store_insn (reg, del, bb)
++     rtx reg, del;
++     basic_block bb;
++{
++  rtx insn;
++
++  insn = gen_move_insn (reg, SET_SRC (PATTERN (del)));
++  insn = emit_insn_after (insn, del);
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file,
++             "STORE_MOTION  delete insn in BB %d:\n      ", bb->index);
++      print_inline_rtx (gcse_file, del, 6);
++      fprintf (gcse_file, "\nSTORE MOTION  replaced with insn:\n      ");
++      print_inline_rtx (gcse_file, insn, 6);
++      fprintf (gcse_file, "\n");
++    }
++
++  delete_insn (del);
++}
++
++
++/* Delete a store, but copy the value that would have been stored into
++   the reaching_reg for later storing.  */
++
++static void
++delete_store (expr, bb)
++     struct ls_expr * expr;
++     basic_block bb;
++{
++  rtx reg, i, del;
++
++  if (expr->reaching_reg == NULL_RTX)
++    expr->reaching_reg = gen_reg_rtx (GET_MODE (expr->pattern));
++
++
++  /* If there is more than 1 store, the earlier ones will be dead,
++     but it doesn't hurt to replace them here.  */
++  reg = expr->reaching_reg;
++
++  for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
++    {
++      del = XEXP (i, 0);
++      if (BLOCK_FOR_INSN (del) == bb)
++      {
++        /* We know there is only one since we deleted redundant
++           ones during the available computation.  */
++        replace_store_insn (reg, del, bb);
++        break;
++      }
++    }
++}
++
++/* Free memory used by store motion.  */
++
++static void
++free_store_memory ()
++{
++  free_ldst_mems ();
++
++  if (ae_gen)
++    sbitmap_vector_free (ae_gen);
++  if (ae_kill)
++    sbitmap_vector_free (ae_kill);
++  if (transp)
++    sbitmap_vector_free (transp);
++  if (st_antloc)
++    sbitmap_vector_free (st_antloc);
++  if (pre_insert_map)
++    sbitmap_vector_free (pre_insert_map);
++  if (pre_delete_map)
++    sbitmap_vector_free (pre_delete_map);
++  if (reg_set_in_block)
++    sbitmap_vector_free (reg_set_in_block);
++
++  ae_gen = ae_kill = transp = st_antloc = NULL;
++  pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
++}
++
++/* Perform store motion. Much like gcse, except we move expressions the
++   other way by looking at the flowgraph in reverse.  */
++
++static void
++store_motion ()
++{
++  basic_block bb;
++  int x;
++  struct ls_expr * ptr;
++  int update_flow = 0;
++
++  if (gcse_file)
++    {
++      fprintf (gcse_file, "before store motion\n");
++      print_rtl (gcse_file, get_insns ());
++    }
++
++
++  init_alias_analysis ();
++
++  /* Find all the stores that are live to the end of their block.  */
++  num_stores = compute_store_table ();
++  if (num_stores == 0)
++    {
++      sbitmap_vector_free (reg_set_in_block);
++      end_alias_analysis ();
++      return;
++    }
++
++  /* Now compute whats actually available to move.  */
++  add_noreturn_fake_exit_edges ();
++  build_store_vectors ();
++
++  edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
++                              st_antloc, ae_kill, &pre_insert_map,
++                              &pre_delete_map);
++
++  /* Now we want to insert the new stores which are going to be needed.  */
++  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
++    {
++      FOR_EACH_BB (bb)
++      if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
++        delete_store (ptr, bb);
++
++      for (x = 0; x < NUM_EDGES (edge_list); x++)
++      if (TEST_BIT (pre_insert_map[x], ptr->index))
++        update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
++    }
++
++  if (update_flow)
++    commit_edge_insertions ();
++
++  free_store_memory ();
++  free_edge_list (edge_list);
++  remove_fake_edges ();
++  end_alias_analysis ();
++}
++
++#include "gt-gcse.h"
+diff -ruN gcc-3.3.1/gcc/integrate.c gcc-3.3.1.pp/gcc/integrate.c
+--- gcc-3.3.1/gcc/integrate.c  2003-07-15 01:05:43.000000000 +0000
++++ gcc-3.3.1.pp/gcc/integrate.c       2003-09-05 11:58:59.000000000 +0000
+@@ -401,6 +401,10 @@
+   /* These args would always appear unused, if not for this.  */
+   TREE_USED (copy) = 1;
++  /* The inlined variable is marked as INLINE not to sweep by propolice */
++  if (flag_propolice_protection && TREE_CODE (copy) == VAR_DECL)
++    DECL_INLINE (copy) = 1;
++
+   /* Set the context for the new declaration.  */
+   if (!DECL_CONTEXT (decl))
+     /* Globals stay global.  */
+@@ -1965,6 +1969,10 @@
+             seq = get_insns ();
+             end_sequence ();
++#ifdef FRAME_GROWS_DOWNWARD
++            if (flag_propolice_protection && GET_CODE (seq) == SET)
++              RTX_INTEGRATED_P (SET_SRC (seq)) = 1;
++#endif
+             emit_insn_after (seq, map->insns_at_start);
+             return temp;
+           }
+diff -ruN gcc-3.3.1/gcc/integrate.c.orig gcc-3.3.1.pp/gcc/integrate.c.orig
+--- gcc-3.3.1/gcc/integrate.c.orig     1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/integrate.c.orig  2003-07-15 01:05:43.000000000 +0000
+@@ -0,0 +1,3213 @@
++/* Procedure integration for GCC.
++   Copyright (C) 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
++   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
++   Contributed by Michael Tiemann (tiemann@cygnus.com)
++
++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 "tree.h"
++#include "tm_p.h"
++#include "regs.h"
++#include "flags.h"
++#include "debug.h"
++#include "insn-config.h"
++#include "expr.h"
++#include "output.h"
++#include "recog.h"
++#include "integrate.h"
++#include "real.h"
++#include "except.h"
++#include "function.h"
++#include "toplev.h"
++#include "intl.h"
++#include "loop.h"
++#include "params.h"
++#include "ggc.h"
++#include "target.h"
++#include "langhooks.h"
++
++/* Similar, but round to the next highest integer that meets the
++   alignment.  */
++#define CEIL_ROUND(VALUE,ALIGN)       (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
++
++/* Default max number of insns a function can have and still be inline.
++   This is overridden on RISC machines.  */
++#ifndef INTEGRATE_THRESHOLD
++/* Inlining small functions might save more space then not inlining at
++   all.  Assume 1 instruction for the call and 1.5 insns per argument.  */
++#define INTEGRATE_THRESHOLD(DECL) \
++  (optimize_size \
++   ? (1 + (3 * list_length (DECL_ARGUMENTS (DECL))) / 2) \
++   : (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))))
++#endif
++\f
++
++/* Private type used by {get/has}_func_hard_reg_initial_val.  */
++typedef struct initial_value_pair GTY(()) {
++  rtx hard_reg;
++  rtx pseudo;
++} initial_value_pair;
++typedef struct initial_value_struct GTY(()) {
++  int num_entries;
++  int max_entries;
++  initial_value_pair * GTY ((length ("%h.num_entries"))) entries;
++} initial_value_struct;
++
++static void setup_initial_hard_reg_value_integration PARAMS ((struct function *, struct inline_remap *));
++
++static rtvec initialize_for_inline    PARAMS ((tree));
++static void note_modified_parmregs    PARAMS ((rtx, rtx, void *));
++static void integrate_parm_decls      PARAMS ((tree, struct inline_remap *,
++                                               rtvec));
++static tree integrate_decl_tree               PARAMS ((tree,
++                                               struct inline_remap *));
++static void subst_constants           PARAMS ((rtx *, rtx,
++                                               struct inline_remap *, int));
++static void set_block_origin_self     PARAMS ((tree));
++static void set_block_abstract_flags  PARAMS ((tree, int));
++static void process_reg_param         PARAMS ((struct inline_remap *, rtx,
++                                               rtx));
++void set_decl_abstract_flags          PARAMS ((tree, int));
++static void mark_stores                 PARAMS ((rtx, rtx, void *));
++static void save_parm_insns           PARAMS ((rtx, rtx));
++static void copy_insn_list              PARAMS ((rtx, struct inline_remap *,
++                                               rtx));
++static void copy_insn_notes           PARAMS ((rtx, struct inline_remap *,
++                                               int));
++static int compare_blocks               PARAMS ((const PTR, const PTR));
++static int find_block                   PARAMS ((const PTR, const PTR));
++
++/* Used by copy_rtx_and_substitute; this indicates whether the function is
++   called for the purpose of inlining or some other purpose (i.e. loop
++   unrolling).  This affects how constant pool references are handled.
++   This variable contains the FUNCTION_DECL for the inlined function.  */
++static struct function *inlining = 0;
++\f
++/* Returns the Ith entry in the label_map contained in MAP.  If the
++   Ith entry has not yet been set, return a fresh label.  This function
++   performs a lazy initialization of label_map, thereby avoiding huge memory
++   explosions when the label_map gets very large.  */
++
++rtx
++get_label_from_map (map, i)
++     struct inline_remap *map;
++     int i;
++{
++  rtx x = map->label_map[i];
++
++  if (x == NULL_RTX)
++    x = map->label_map[i] = gen_label_rtx ();
++
++  return x;
++}
++
++/* Return false if the function FNDECL cannot be inlined on account of its
++   attributes, true otherwise.  */
++bool
++function_attribute_inlinable_p (fndecl)
++     tree fndecl;
++{
++  if (targetm.attribute_table)
++    {
++      tree a;
++
++      for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
++      {
++        tree name = TREE_PURPOSE (a);
++        int i;
++
++        for (i = 0; targetm.attribute_table[i].name != NULL; i++)
++          if (is_attribute_p (targetm.attribute_table[i].name, name))
++            return (*targetm.function_attribute_inlinable_p) (fndecl);
++      }
++    }
++
++  return true;
++}
++
++/* Zero if the current function (whose FUNCTION_DECL is FNDECL)
++   is safe and reasonable to integrate into other functions.
++   Nonzero means value is a warning msgid with a single %s
++   for the function's name.  */
++
++const char *
++function_cannot_inline_p (fndecl)
++     tree fndecl;
++{
++  rtx insn;
++  tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
++
++  /* For functions marked as inline increase the maximum size to
++     MAX_INLINE_INSNS_RTL (--param max-inline-insn-rtl=<n>). For
++     regular functions use the limit given by INTEGRATE_THRESHOLD.
++     Note that the RTL inliner is not used by the languages that use
++     the tree inliner (C, C++).  */
++
++  int max_insns = (DECL_INLINE (fndecl))
++                 ? (MAX_INLINE_INSNS_RTL
++                    + 8 * list_length (DECL_ARGUMENTS (fndecl)))
++                 : INTEGRATE_THRESHOLD (fndecl);
++
++  int ninsns = 0;
++  tree parms;
++
++  if (DECL_UNINLINABLE (fndecl))
++    return N_("function cannot be inline");
++
++  /* No inlines with varargs.  */
++  if (last && TREE_VALUE (last) != void_type_node)
++    return N_("varargs function cannot be inline");
++
++  if (current_function_calls_alloca)
++    return N_("function using alloca cannot be inline");
++
++  if (current_function_calls_setjmp)
++    return N_("function using setjmp cannot be inline");
++
++  if (current_function_calls_eh_return)
++    return N_("function uses __builtin_eh_return");
++
++  if (current_function_contains_functions)
++    return N_("function with nested functions cannot be inline");
++
++  if (forced_labels)
++    return
++      N_("function with label addresses used in initializers cannot inline");
++
++  if (current_function_cannot_inline)
++    return current_function_cannot_inline;
++
++  /* If its not even close, don't even look.  */
++  if (get_max_uid () > 3 * max_insns)
++    return N_("function too large to be inline");
++
++#if 0
++  /* Don't inline functions which do not specify a function prototype and
++     have BLKmode argument or take the address of a parameter.  */
++  for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
++    {
++      if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode)
++      TREE_ADDRESSABLE (parms) = 1;
++      if (last == NULL_TREE && TREE_ADDRESSABLE (parms))
++      return N_("no prototype, and parameter address used; cannot be inline");
++    }
++#endif
++
++  /* We can't inline functions that return structures
++     the old-fashioned PCC way, copying into a static block.  */
++  if (current_function_returns_pcc_struct)
++    return N_("inline functions not supported for this return value type");
++
++  /* We can't inline functions that return structures of varying size.  */
++  if (TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE
++      && int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0)
++    return N_("function with varying-size return value cannot be inline");
++
++  /* Cannot inline a function with a varying size argument or one that
++     receives a transparent union.  */
++  for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
++    {
++      if (int_size_in_bytes (TREE_TYPE (parms)) < 0)
++      return N_("function with varying-size parameter cannot be inline");
++      else if (TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE
++             && TYPE_TRANSPARENT_UNION (TREE_TYPE (parms)))
++      return N_("function with transparent unit parameter cannot be inline");
++    }
++
++  if (get_max_uid () > max_insns)
++    {
++      for (ninsns = 0, insn = get_first_nonparm_insn ();
++         insn && ninsns < max_insns;
++         insn = NEXT_INSN (insn))
++      if (INSN_P (insn))
++        ninsns++;
++
++      if (ninsns >= max_insns)
++      return N_("function too large to be inline");
++    }
++
++  /* We will not inline a function which uses computed goto.  The addresses of
++     its local labels, which may be tucked into global storage, are of course
++     not constant across instantiations, which causes unexpected behavior.  */
++  if (current_function_has_computed_jump)
++    return N_("function with computed jump cannot inline");
++
++  /* We cannot inline a nested function that jumps to a nonlocal label.  */
++  if (current_function_has_nonlocal_goto)
++    return N_("function with nonlocal goto cannot be inline");
++
++  /* We can't inline functions that return a PARALLEL rtx.  */
++  if (DECL_RTL_SET_P (DECL_RESULT (fndecl)))
++    {
++      rtx result = DECL_RTL (DECL_RESULT (fndecl));
++      if (GET_CODE (result) == PARALLEL)
++      return N_("inline functions not supported for this return value type");
++    }
++
++  /* If the function has a target specific attribute attached to it,
++     then we assume that we should not inline it.  This can be overriden
++     by the target if it defines TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P.  */
++  if (!function_attribute_inlinable_p (fndecl))
++    return N_("function with target specific attribute(s) cannot be inlined");
++
++  return NULL;
++}
++\f
++/* Map pseudo reg number into the PARM_DECL for the parm living in the reg.
++   Zero for a reg that isn't a parm's home.
++   Only reg numbers less than max_parm_reg are mapped here.  */
++static tree *parmdecl_map;
++
++/* In save_for_inline, nonzero if past the parm-initialization insns.  */
++static int in_nonparm_insns;
++\f
++/* Subroutine for `save_for_inline'.  Performs initialization
++   needed to save FNDECL's insns and info for future inline expansion.  */
++
++static rtvec
++initialize_for_inline (fndecl)
++     tree fndecl;
++{
++  int i;
++  rtvec arg_vector;
++  tree parms;
++
++  /* Clear out PARMDECL_MAP.  It was allocated in the caller's frame.  */
++  memset ((char *) parmdecl_map, 0, max_parm_reg * sizeof (tree));
++  arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl)));
++
++  for (parms = DECL_ARGUMENTS (fndecl), i = 0;
++       parms;
++       parms = TREE_CHAIN (parms), i++)
++    {
++      rtx p = DECL_RTL (parms);
++
++      /* If we have (mem (addressof (mem ...))), use the inner MEM since
++       otherwise the copy_rtx call below will not unshare the MEM since
++       it shares ADDRESSOF.  */
++      if (GET_CODE (p) == MEM && GET_CODE (XEXP (p, 0)) == ADDRESSOF
++        && GET_CODE (XEXP (XEXP (p, 0), 0)) == MEM)
++      p = XEXP (XEXP (p, 0), 0);
++
++      RTVEC_ELT (arg_vector, i) = p;
++
++      if (GET_CODE (p) == REG)
++      parmdecl_map[REGNO (p)] = parms;
++      else if (GET_CODE (p) == CONCAT)
++      {
++        rtx preal = gen_realpart (GET_MODE (XEXP (p, 0)), p);
++        rtx pimag = gen_imagpart (GET_MODE (preal), p);
++
++        if (GET_CODE (preal) == REG)
++          parmdecl_map[REGNO (preal)] = parms;
++        if (GET_CODE (pimag) == REG)
++          parmdecl_map[REGNO (pimag)] = parms;
++      }
++
++      /* This flag is cleared later
++       if the function ever modifies the value of the parm.  */
++      TREE_READONLY (parms) = 1;
++    }
++
++  return arg_vector;
++}
++
++/* Copy NODE (which must be a DECL, but not a PARM_DECL).  The DECL
++   originally was in the FROM_FN, but now it will be in the
++   TO_FN.  */
++
++tree
++copy_decl_for_inlining (decl, from_fn, to_fn)
++     tree decl;
++     tree from_fn;
++     tree to_fn;
++{
++  tree copy;
++
++  /* Copy the declaration.  */
++  if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL)
++    {
++      tree type;
++      int invisiref = 0;
++
++      /* See if the frontend wants to pass this by invisible reference.  */
++      if (TREE_CODE (decl) == PARM_DECL
++        && DECL_ARG_TYPE (decl) != TREE_TYPE (decl)
++        && POINTER_TYPE_P (DECL_ARG_TYPE (decl))
++        && TREE_TYPE (DECL_ARG_TYPE (decl)) == TREE_TYPE (decl))
++      {
++        invisiref = 1;
++        type = DECL_ARG_TYPE (decl);
++      }
++      else
++      type = TREE_TYPE (decl);
++
++      /* For a parameter, we must make an equivalent VAR_DECL, not a
++       new PARM_DECL.  */
++      copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
++      if (!invisiref)
++      {
++        TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
++        TREE_READONLY (copy) = TREE_READONLY (decl);
++        TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
++      }
++      else
++      {
++        TREE_ADDRESSABLE (copy) = 0;
++        TREE_READONLY (copy) = 1;
++        TREE_THIS_VOLATILE (copy) = 0;
++      }
++    }
++  else
++    {
++      copy = copy_node (decl);
++      /* The COPY is not abstract; it will be generated in TO_FN.  */
++      DECL_ABSTRACT (copy) = 0;
++      (*lang_hooks.dup_lang_specific_decl) (copy);
++
++      /* TREE_ADDRESSABLE isn't used to indicate that a label's
++       address has been taken; it's for internal bookkeeping in
++       expand_goto_internal.  */
++      if (TREE_CODE (copy) == LABEL_DECL)
++      TREE_ADDRESSABLE (copy) = 0;
++    }
++
++  /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what
++     declaration inspired this copy.  */
++  DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
++
++  /* The new variable/label has no RTL, yet.  */
++  SET_DECL_RTL (copy, NULL_RTX);
++
++  /* These args would always appear unused, if not for this.  */
++  TREE_USED (copy) = 1;
++
++  /* Set the context for the new declaration.  */
++  if (!DECL_CONTEXT (decl))
++    /* Globals stay global.  */
++    ;
++  else if (DECL_CONTEXT (decl) != from_fn)
++    /* Things that weren't in the scope of the function we're inlining
++       from aren't in the scope we're inlining too, either.  */
++    ;
++  else if (TREE_STATIC (decl))
++    /* Function-scoped static variables should say in the original
++       function.  */
++    ;
++  else
++    /* Ordinary automatic local variables are now in the scope of the
++       new function.  */
++    DECL_CONTEXT (copy) = to_fn;
++
++  return copy;
++}
++
++/* Make the insns and PARM_DECLs of the current function permanent
++   and record other information in DECL_SAVED_INSNS to allow inlining
++   of this function in subsequent calls.
++
++   This routine need not copy any insns because we are not going
++   to immediately compile the insns in the insn chain.  There
++   are two cases when we would compile the insns for FNDECL:
++   (1) when FNDECL is expanded inline, and (2) when FNDECL needs to
++   be output at the end of other compilation, because somebody took
++   its address.  In the first case, the insns of FNDECL are copied
++   as it is expanded inline, so FNDECL's saved insns are not
++   modified.  In the second case, FNDECL is used for the last time,
++   so modifying the rtl is not a problem.
++
++   We don't have to worry about FNDECL being inline expanded by
++   other functions which are written at the end of compilation
++   because flag_no_inline is turned on when we begin writing
++   functions at the end of compilation.  */
++
++void
++save_for_inline (fndecl)
++     tree fndecl;
++{
++  rtx insn;
++  rtvec argvec;
++  rtx first_nonparm_insn;
++
++  /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
++     Later we set TREE_READONLY to 0 if the parm is modified inside the fn.
++     Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
++     for the parms, prior to elimination of virtual registers.
++     These values are needed for substituting parms properly.  */
++  if (! flag_no_inline)
++    parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
++
++  /* Make and emit a return-label if we have not already done so.  */
++
++  if (return_label == 0)
++    {
++      return_label = gen_label_rtx ();
++      emit_label (return_label);
++    }
++
++  if (! flag_no_inline)
++    argvec = initialize_for_inline (fndecl);
++  else
++    argvec = NULL;
++
++  /* Delete basic block notes created by early run of find_basic_block.
++     The notes would be later used by find_basic_blocks to reuse the memory
++     for basic_block structures on already freed obstack.  */
++  for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
++      delete_related_insns (insn);
++
++  /* If there are insns that copy parms from the stack into pseudo registers,
++     those insns are not copied.  `expand_inline_function' must
++     emit the correct code to handle such things.  */
++
++  insn = get_insns ();
++  if (GET_CODE (insn) != NOTE)
++    abort ();
++
++  if (! flag_no_inline)
++    {
++      /* Get the insn which signals the end of parameter setup code.  */
++      first_nonparm_insn = get_first_nonparm_insn ();
++
++      /* Now just scan the chain of insns to see what happens to our
++       PARM_DECLs.  If a PARM_DECL is used but never modified, we
++       can substitute its rtl directly when expanding inline (and
++       perform constant folding when its incoming value is
++       constant).  Otherwise, we have to copy its value into a new
++       register and track the new register's life.  */
++      in_nonparm_insns = 0;
++      save_parm_insns (insn, first_nonparm_insn);
++
++      cfun->inl_max_label_num = max_label_num ();
++      cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
++      cfun->original_arg_vector = argvec;
++    }
++  cfun->original_decl_initial = DECL_INITIAL (fndecl);
++  cfun->no_debugging_symbols = (write_symbols == NO_DEBUG);
++  DECL_SAVED_INSNS (fndecl) = cfun;
++
++  /* Clean up.  */
++  if (! flag_no_inline)
++    free (parmdecl_map);
++}
++
++/* Scan the chain of insns to see what happens to our PARM_DECLs.  If a
++   PARM_DECL is used but never modified, we can substitute its rtl directly
++   when expanding inline (and perform constant folding when its incoming
++   value is constant). Otherwise, we have to copy its value into a new
++   register and track the new register's life.  */
++
++static void
++save_parm_insns (insn, first_nonparm_insn)
++     rtx insn;
++     rtx first_nonparm_insn;
++{
++  if (insn == NULL_RTX)
++    return;
++
++  for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
++    {
++      if (insn == first_nonparm_insn)
++      in_nonparm_insns = 1;
++
++      if (INSN_P (insn))
++      {
++        /* Record what interesting things happen to our parameters.  */
++        note_stores (PATTERN (insn), note_modified_parmregs, NULL);
++
++        /* If this is a CALL_PLACEHOLDER insn then we need to look into the
++           three attached sequences: normal call, sibling call and tail
++           recursion.  */
++        if (GET_CODE (insn) == CALL_INSN
++            && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++          {
++            int i;
++
++            for (i = 0; i < 3; i++)
++              save_parm_insns (XEXP (PATTERN (insn), i),
++                               first_nonparm_insn);
++          }
++      }
++    }
++}
++\f
++/* Note whether a parameter is modified or not.  */
++
++static void
++note_modified_parmregs (reg, x, data)
++     rtx reg;
++     rtx x ATTRIBUTE_UNUSED;
++     void *data ATTRIBUTE_UNUSED;
++{
++  if (GET_CODE (reg) == REG && in_nonparm_insns
++      && REGNO (reg) < max_parm_reg
++      && REGNO (reg) >= FIRST_PSEUDO_REGISTER
++      && parmdecl_map[REGNO (reg)] != 0)
++    TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0;
++}
++
++/* Unfortunately, we need a global copy of const_equiv map for communication
++   with a function called from note_stores.  Be *very* careful that this
++   is used properly in the presence of recursion.  */
++
++varray_type global_const_equiv_varray;
++\f
++#define FIXED_BASE_PLUS_P(X) \
++  (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT        \
++   && GET_CODE (XEXP (X, 0)) == REG                           \
++   && REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER           \
++   && REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER)
++
++/* Called to set up a mapping for the case where a parameter is in a
++   register.  If it is read-only and our argument is a constant, set up the
++   constant equivalence.
++
++   If LOC is REG_USERVAR_P, the usual case, COPY must also have that flag set
++   if it is a register.
++
++   Also, don't allow hard registers here; they might not be valid when
++   substituted into insns.  */
++static void
++process_reg_param (map, loc, copy)
++     struct inline_remap *map;
++     rtx loc, copy;
++{
++  if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG)
++      || (GET_CODE (copy) == REG && REG_USERVAR_P (loc)
++        && ! REG_USERVAR_P (copy))
++      || (GET_CODE (copy) == REG
++        && REGNO (copy) < FIRST_PSEUDO_REGISTER))
++    {
++      rtx temp = copy_to_mode_reg (GET_MODE (loc), copy);
++      REG_USERVAR_P (temp) = REG_USERVAR_P (loc);
++      if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
++      SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM);
++      copy = temp;
++    }
++  map->reg_map[REGNO (loc)] = copy;
++}
++
++/* Compare two BLOCKs for qsort.  The key we sort on is the
++   BLOCK_ABSTRACT_ORIGIN of the blocks.  We cannot just subtract the
++   two pointers, because it may overflow sizeof(int).  */
++
++static int
++compare_blocks (v1, v2)
++     const PTR v1;
++     const PTR v2;
++{
++  tree b1 = *((const tree *) v1);
++  tree b2 = *((const tree *) v2);
++  char *p1 = (char *) BLOCK_ABSTRACT_ORIGIN (b1);
++  char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2);
++
++  if (p1 == p2)
++    return 0;
++  return p1 < p2 ? -1 : 1;
++}
++
++/* Compare two BLOCKs for bsearch.  The first pointer corresponds to
++   an original block; the second to a remapped equivalent.  */
++
++static int
++find_block (v1, v2)
++     const PTR v1;
++     const PTR v2;
++{
++  const union tree_node *b1 = (const union tree_node *) v1;
++  tree b2 = *((const tree *) v2);
++  char *p1 = (char *) b1;
++  char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2);
++
++  if (p1 == p2)
++    return 0;
++  return p1 < p2 ? -1 : 1;
++}
++
++/* Integrate the procedure defined by FNDECL.  Note that this function
++   may wind up calling itself.  Since the static variables are not
++   reentrant, we do not assign them until after the possibility
++   of recursion is eliminated.
++
++   If IGNORE is nonzero, do not produce a value.
++   Otherwise store the value in TARGET if it is nonzero and that is convenient.
++
++   Value is:
++   (rtx)-1 if we could not substitute the function
++   0 if we substituted it and it does not produce a value
++   else an rtx for where the value is stored.  */
++
++rtx
++expand_inline_function (fndecl, parms, target, ignore, type,
++                      structure_value_addr)
++     tree fndecl, parms;
++     rtx target;
++     int ignore;
++     tree type;
++     rtx structure_value_addr;
++{
++  struct function *inlining_previous;
++  struct function *inl_f = DECL_SAVED_INSNS (fndecl);
++  tree formal, actual, block;
++  rtx parm_insns = inl_f->emit->x_first_insn;
++  rtx insns = (inl_f->inl_last_parm_insn
++             ? NEXT_INSN (inl_f->inl_last_parm_insn)
++             : parm_insns);
++  tree *arg_trees;
++  rtx *arg_vals;
++  int max_regno;
++  int i;
++  int min_labelno = inl_f->emit->x_first_label_num;
++  int max_labelno = inl_f->inl_max_label_num;
++  int nargs;
++  rtx loc;
++  rtx stack_save = 0;
++  rtx temp;
++  struct inline_remap *map = 0;
++  rtvec arg_vector = inl_f->original_arg_vector;
++  rtx static_chain_value = 0;
++  int inl_max_uid;
++  int eh_region_offset;
++
++  /* The pointer used to track the true location of the memory used
++     for MAP->LABEL_MAP.  */
++  rtx *real_label_map = 0;
++
++  /* Allow for equivalences of the pseudos we make for virtual fp and ap.  */
++  max_regno = inl_f->emit->x_reg_rtx_no + 3;
++  if (max_regno < FIRST_PSEUDO_REGISTER)
++    abort ();
++
++  /* Pull out the decl for the function definition; fndecl may be a
++     local declaration, which would break DECL_ABSTRACT_ORIGIN.  */
++  fndecl = inl_f->decl;
++
++  nargs = list_length (DECL_ARGUMENTS (fndecl));
++
++  if (cfun->preferred_stack_boundary < inl_f->preferred_stack_boundary)
++    cfun->preferred_stack_boundary = inl_f->preferred_stack_boundary;
++
++  /* Check that the parms type match and that sufficient arguments were
++     passed.  Since the appropriate conversions or default promotions have
++     already been applied, the machine modes should match exactly.  */
++
++  for (formal = DECL_ARGUMENTS (fndecl), actual = parms;
++       formal;
++       formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual))
++    {
++      tree arg;
++      enum machine_mode mode;
++
++      if (actual == 0)
++      return (rtx) (size_t) -1;
++
++      arg = TREE_VALUE (actual);
++      mode = TYPE_MODE (DECL_ARG_TYPE (formal));
++
++      if (arg == error_mark_node
++        || mode != TYPE_MODE (TREE_TYPE (arg))
++        /* If they are block mode, the types should match exactly.
++           They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE,
++           which could happen if the parameter has incomplete type.  */
++        || (mode == BLKmode
++            && (TYPE_MAIN_VARIANT (TREE_TYPE (arg))
++                != TYPE_MAIN_VARIANT (TREE_TYPE (formal)))))
++      return (rtx) (size_t) -1;
++    }
++
++  /* Extra arguments are valid, but will be ignored below, so we must
++     evaluate them here for side-effects.  */
++  for (; actual; actual = TREE_CHAIN (actual))
++    expand_expr (TREE_VALUE (actual), const0_rtx,
++               TYPE_MODE (TREE_TYPE (TREE_VALUE (actual))), 0);
++
++  /* Expand the function arguments.  Do this first so that any
++     new registers get created before we allocate the maps.  */
++
++  arg_vals = (rtx *) xmalloc (nargs * sizeof (rtx));
++  arg_trees = (tree *) xmalloc (nargs * sizeof (tree));
++
++  for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0;
++       formal;
++       formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++)
++    {
++      /* Actual parameter, converted to the type of the argument within the
++       function.  */
++      tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual));
++      /* Mode of the variable used within the function.  */
++      enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal));
++      int invisiref = 0;
++
++      arg_trees[i] = arg;
++      loc = RTVEC_ELT (arg_vector, i);
++
++      /* If this is an object passed by invisible reference, we copy the
++       object into a stack slot and save its address.  If this will go
++       into memory, we do nothing now.  Otherwise, we just expand the
++       argument.  */
++      if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
++        && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
++      {
++        rtx stack_slot = assign_temp (TREE_TYPE (arg), 1, 1, 1);
++
++        store_expr (arg, stack_slot, 0);
++        arg_vals[i] = XEXP (stack_slot, 0);
++        invisiref = 1;
++      }
++      else if (GET_CODE (loc) != MEM)
++      {
++        if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg)))
++          {
++            int unsignedp = TREE_UNSIGNED (TREE_TYPE (formal));
++            enum machine_mode pmode = TYPE_MODE (TREE_TYPE (formal));
++
++            pmode = promote_mode (TREE_TYPE (formal), pmode,
++                                  &unsignedp, 0);
++
++            if (GET_MODE (loc) != pmode)
++              abort ();
++
++            /* The mode if LOC and ARG can differ if LOC was a variable
++               that had its mode promoted via PROMOTED_MODE.  */
++            arg_vals[i] = convert_modes (pmode,
++                                         TYPE_MODE (TREE_TYPE (arg)),
++                                         expand_expr (arg, NULL_RTX, mode,
++                                                      EXPAND_SUM),
++                                         unsignedp);
++          }
++        else
++          arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
++      }
++      else
++      arg_vals[i] = 0;
++
++      if (arg_vals[i] != 0
++        && (! TREE_READONLY (formal)
++            /* If the parameter is not read-only, copy our argument through
++               a register.  Also, we cannot use ARG_VALS[I] if it overlaps
++               TARGET in any way.  In the inline function, they will likely
++               be two different pseudos, and `safe_from_p' will make all
++               sorts of smart assumptions about their not conflicting.
++               But if ARG_VALS[I] overlaps TARGET, these assumptions are
++               wrong, so put ARG_VALS[I] into a fresh register.
++               Don't worry about invisible references, since their stack
++               temps will never overlap the target.  */
++            || (target != 0
++                && ! invisiref
++                && (GET_CODE (arg_vals[i]) == REG
++                    || GET_CODE (arg_vals[i]) == SUBREG
++                    || GET_CODE (arg_vals[i]) == MEM)
++                && reg_overlap_mentioned_p (arg_vals[i], target))
++            /* ??? We must always copy a SUBREG into a REG, because it might
++               get substituted into an address, and not all ports correctly
++               handle SUBREGs in addresses.  */
++            || (GET_CODE (arg_vals[i]) == SUBREG)))
++      arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]);
++
++      if (arg_vals[i] != 0 && GET_CODE (arg_vals[i]) == REG
++        && POINTER_TYPE_P (TREE_TYPE (formal)))
++      mark_reg_pointer (arg_vals[i],
++                        TYPE_ALIGN (TREE_TYPE (TREE_TYPE (formal))));
++    }
++
++  /* Allocate the structures we use to remap things.  */
++
++  map = (struct inline_remap *) xcalloc (1, sizeof (struct inline_remap));
++  map->fndecl = fndecl;
++
++  VARRAY_TREE_INIT (map->block_map, 10, "block_map");
++  map->reg_map = (rtx *) xcalloc (max_regno, sizeof (rtx));
++
++  /* We used to use alloca here, but the size of what it would try to
++     allocate would occasionally cause it to exceed the stack limit and
++     cause unpredictable core dumps.  */
++  real_label_map
++    = (rtx *) xmalloc ((max_labelno) * sizeof (rtx));
++  map->label_map = real_label_map;
++  map->local_return_label = NULL_RTX;
++
++  inl_max_uid = (inl_f->emit->x_cur_insn_uid + 1);
++  map->insn_map = (rtx *) xcalloc (inl_max_uid, sizeof (rtx));
++  map->min_insnno = 0;
++  map->max_insnno = inl_max_uid;
++
++  map->integrating = 1;
++  map->compare_src = NULL_RTX;
++  map->compare_mode = VOIDmode;
++
++  /* const_equiv_varray maps pseudos in our routine to constants, so
++     it needs to be large enough for all our pseudos.  This is the
++     number we are currently using plus the number in the called
++     routine, plus 15 for each arg, five to compute the virtual frame
++     pointer, and five for the return value.  This should be enough
++     for most cases.  We do not reference entries outside the range of
++     the map.
++
++     ??? These numbers are quite arbitrary and were obtained by
++     experimentation.  At some point, we should try to allocate the
++     table after all the parameters are set up so we can more accurately
++     estimate the number of pseudos we will need.  */
++
++  VARRAY_CONST_EQUIV_INIT (map->const_equiv_varray,
++                         (max_reg_num ()
++                          + (max_regno - FIRST_PSEUDO_REGISTER)
++                          + 15 * nargs
++                          + 10),
++                         "expand_inline_function");
++  map->const_age = 0;
++
++  /* Record the current insn in case we have to set up pointers to frame
++     and argument memory blocks.  If there are no insns yet, add a dummy
++     insn that can be used as an insertion point.  */
++  map->insns_at_start = get_last_insn ();
++  if (map->insns_at_start == 0)
++    map->insns_at_start = emit_note (NULL, NOTE_INSN_DELETED);
++
++  map->regno_pointer_align = inl_f->emit->regno_pointer_align;
++  map->x_regno_reg_rtx = inl_f->emit->x_regno_reg_rtx;
++
++  /* Update the outgoing argument size to allow for those in the inlined
++     function.  */
++  if (inl_f->outgoing_args_size > current_function_outgoing_args_size)
++    current_function_outgoing_args_size = inl_f->outgoing_args_size;
++
++  /* If the inline function needs to make PIC references, that means
++     that this function's PIC offset table must be used.  */
++  if (inl_f->uses_pic_offset_table)
++    current_function_uses_pic_offset_table = 1;
++
++  /* If this function needs a context, set it up.  */
++  if (inl_f->needs_context)
++    static_chain_value = lookup_static_chain (fndecl);
++
++  if (GET_CODE (parm_insns) == NOTE
++      && NOTE_LINE_NUMBER (parm_insns) > 0)
++    {
++      rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns),
++                          NOTE_LINE_NUMBER (parm_insns));
++      if (note)
++      RTX_INTEGRATED_P (note) = 1;
++    }
++
++  /* Process each argument.  For each, set up things so that the function's
++     reference to the argument will refer to the argument being passed.
++     We only replace REG with REG here.  Any simplifications are done
++     via const_equiv_map.
++
++     We make two passes:  In the first, we deal with parameters that will
++     be placed into registers, since we need to ensure that the allocated
++     register number fits in const_equiv_map.  Then we store all non-register
++     parameters into their memory location.  */
++
++  /* Don't try to free temp stack slots here, because we may put one of the
++     parameters into a temp stack slot.  */
++
++  for (i = 0; i < nargs; i++)
++    {
++      rtx copy = arg_vals[i];
++
++      loc = RTVEC_ELT (arg_vector, i);
++
++      /* There are three cases, each handled separately.  */
++      if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
++        && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
++      {
++        /* This must be an object passed by invisible reference (it could
++           also be a variable-sized object, but we forbid inlining functions
++           with variable-sized arguments).  COPY is the address of the
++           actual value (this computation will cause it to be copied).  We
++           map that address for the register, noting the actual address as
++           an equivalent in case it can be substituted into the insns.  */
++
++        if (GET_CODE (copy) != REG)
++          {
++            temp = copy_addr_to_reg (copy);
++            if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
++              SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM);
++            copy = temp;
++          }
++        map->reg_map[REGNO (XEXP (loc, 0))] = copy;
++      }
++      else if (GET_CODE (loc) == MEM)
++      {
++        /* This is the case of a parameter that lives in memory.  It
++           will live in the block we allocate in the called routine's
++           frame that simulates the incoming argument area.  Do nothing
++           with the parameter now; we will call store_expr later.  In
++           this case, however, we must ensure that the virtual stack and
++           incoming arg rtx values are expanded now so that we can be
++           sure we have enough slots in the const equiv map since the
++           store_expr call can easily blow the size estimate.  */
++        if (DECL_SAVED_INSNS (fndecl)->args_size != 0)
++          copy_rtx_and_substitute (virtual_incoming_args_rtx, map, 0);
++      }
++      else if (GET_CODE (loc) == REG)
++      process_reg_param (map, loc, copy);
++      else if (GET_CODE (loc) == CONCAT)
++      {
++        rtx locreal = gen_realpart (GET_MODE (XEXP (loc, 0)), loc);
++        rtx locimag = gen_imagpart (GET_MODE (XEXP (loc, 0)), loc);
++        rtx copyreal = gen_realpart (GET_MODE (locreal), copy);
++        rtx copyimag = gen_imagpart (GET_MODE (locimag), copy);
++
++        process_reg_param (map, locreal, copyreal);
++        process_reg_param (map, locimag, copyimag);
++      }
++      else
++      abort ();
++    }
++
++  /* Tell copy_rtx_and_substitute to handle constant pool SYMBOL_REFs
++     specially.  This function can be called recursively, so we need to
++     save the previous value.  */
++  inlining_previous = inlining;
++  inlining = inl_f;
++
++  /* Now do the parameters that will be placed in memory.  */
++
++  for (formal = DECL_ARGUMENTS (fndecl), i = 0;
++       formal; formal = TREE_CHAIN (formal), i++)
++    {
++      loc = RTVEC_ELT (arg_vector, i);
++
++      if (GET_CODE (loc) == MEM
++        /* Exclude case handled above.  */
++        && ! (GET_CODE (XEXP (loc, 0)) == REG
++              && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER))
++      {
++        rtx note = emit_note (DECL_SOURCE_FILE (formal),
++                              DECL_SOURCE_LINE (formal));
++        if (note)
++          RTX_INTEGRATED_P (note) = 1;
++
++        /* Compute the address in the area we reserved and store the
++           value there.  */
++        temp = copy_rtx_and_substitute (loc, map, 1);
++        subst_constants (&temp, NULL_RTX, map, 1);
++        apply_change_group ();
++        if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
++          temp = change_address (temp, VOIDmode, XEXP (temp, 0));
++        store_expr (arg_trees[i], temp, 0);
++      }
++    }
++
++  /* Deal with the places that the function puts its result.
++     We are driven by what is placed into DECL_RESULT.
++
++     Initially, we assume that we don't have anything special handling for
++     REG_FUNCTION_RETURN_VALUE_P.  */
++
++  map->inline_target = 0;
++  loc = (DECL_RTL_SET_P (DECL_RESULT (fndecl)) 
++       ? DECL_RTL (DECL_RESULT (fndecl)) : NULL_RTX);
++
++  if (TYPE_MODE (type) == VOIDmode)
++    /* There is no return value to worry about.  */
++    ;
++  else if (GET_CODE (loc) == MEM)
++    {
++      if (GET_CODE (XEXP (loc, 0)) == ADDRESSOF)
++      {
++        temp = copy_rtx_and_substitute (loc, map, 1);
++        subst_constants (&temp, NULL_RTX, map, 1);
++        apply_change_group ();
++        target = temp;
++      }
++      else
++      {
++        if (! structure_value_addr
++            || ! aggregate_value_p (DECL_RESULT (fndecl)))
++          abort ();
++
++        /* Pass the function the address in which to return a structure
++           value.  Note that a constructor can cause someone to call us
++           with STRUCTURE_VALUE_ADDR, but the initialization takes place
++           via the first parameter, rather than the struct return address.
++
++           We have two cases: If the address is a simple register
++           indirect, use the mapping mechanism to point that register to
++           our structure return address.  Otherwise, store the structure
++           return value into the place that it will be referenced from.  */
++
++        if (GET_CODE (XEXP (loc, 0)) == REG)
++          {
++            temp = force_operand (structure_value_addr, NULL_RTX);
++            temp = force_reg (Pmode, temp);
++            /* A virtual register might be invalid in an insn, because
++               it can cause trouble in reload.  Since we don't have access
++               to the expanders at map translation time, make sure we have
++               a proper register now.
++               If a virtual register is actually valid, cse or combine
++               can put it into the mapped insns.  */
++            if (REGNO (temp) >= FIRST_VIRTUAL_REGISTER
++                && REGNO (temp) <= LAST_VIRTUAL_REGISTER)
++            temp = copy_to_mode_reg (Pmode, temp);
++            map->reg_map[REGNO (XEXP (loc, 0))] = temp;
++
++            if (CONSTANT_P (structure_value_addr)
++                || GET_CODE (structure_value_addr) == ADDRESSOF
++                || (GET_CODE (structure_value_addr) == PLUS
++                    && (XEXP (structure_value_addr, 0)
++                        == virtual_stack_vars_rtx)
++                    && (GET_CODE (XEXP (structure_value_addr, 1))
++                        == CONST_INT)))
++              {
++                SET_CONST_EQUIV_DATA (map, temp, structure_value_addr,
++                                      CONST_AGE_PARM);
++              }
++          }
++        else
++          {
++            temp = copy_rtx_and_substitute (loc, map, 1);
++            subst_constants (&temp, NULL_RTX, map, 0);
++            apply_change_group ();
++            emit_move_insn (temp, structure_value_addr);
++          }
++      }
++    }
++  else if (ignore)
++    /* We will ignore the result value, so don't look at its structure.
++       Note that preparations for an aggregate return value
++       do need to be made (above) even if it will be ignored.  */
++    ;
++  else if (GET_CODE (loc) == REG)
++    {
++      /* The function returns an object in a register and we use the return
++       value.  Set up our target for remapping.  */
++
++      /* Machine mode function was declared to return.  */
++      enum machine_mode departing_mode = TYPE_MODE (type);
++      /* (Possibly wider) machine mode it actually computes
++       (for the sake of callers that fail to declare it right).
++       We have to use the mode of the result's RTL, rather than
++       its type, since expand_function_start may have promoted it.  */
++      enum machine_mode arriving_mode
++      = GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
++      rtx reg_to_map;
++
++      /* Don't use MEMs as direct targets because on some machines
++       substituting a MEM for a REG makes invalid insns.
++       Let the combiner substitute the MEM if that is valid.  */
++      if (target == 0 || GET_CODE (target) != REG
++        || GET_MODE (target) != departing_mode)
++      {
++        /* Don't make BLKmode registers.  If this looks like
++           a BLKmode object being returned in a register, get
++           the mode from that, otherwise abort.  */
++        if (departing_mode == BLKmode)
++          {
++            if (REG == GET_CODE (DECL_RTL (DECL_RESULT (fndecl))))
++              {
++                departing_mode = GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
++                arriving_mode = departing_mode;
++              }
++            else
++              abort ();
++          }
++
++        target = gen_reg_rtx (departing_mode);
++      }
++
++      /* If function's value was promoted before return,
++       avoid machine mode mismatch when we substitute INLINE_TARGET.
++       But TARGET is what we will return to the caller.  */
++      if (arriving_mode != departing_mode)
++      {
++        /* Avoid creating a paradoxical subreg wider than
++           BITS_PER_WORD, since that is illegal.  */
++        if (GET_MODE_BITSIZE (arriving_mode) > BITS_PER_WORD)
++          {
++            if (!TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (departing_mode),
++                                        GET_MODE_BITSIZE (arriving_mode)))
++              /* Maybe could be handled by using convert_move () ?  */
++              abort ();
++            reg_to_map = gen_reg_rtx (arriving_mode);
++            target = gen_lowpart (departing_mode, reg_to_map);
++          }
++        else
++          reg_to_map = gen_rtx_SUBREG (arriving_mode, target, 0);
++      }
++      else
++      reg_to_map = target;
++
++      /* Usually, the result value is the machine's return register.
++       Sometimes it may be a pseudo. Handle both cases.  */
++      if (REG_FUNCTION_VALUE_P (loc))
++      map->inline_target = reg_to_map;
++      else
++      map->reg_map[REGNO (loc)] = reg_to_map;
++    }
++  else if (GET_CODE (loc) == CONCAT)
++    {
++      enum machine_mode departing_mode = TYPE_MODE (type);
++      enum machine_mode arriving_mode
++      = GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
++
++      if (departing_mode != arriving_mode)
++      abort ();
++      if (GET_CODE (XEXP (loc, 0)) != REG
++        || GET_CODE (XEXP (loc, 1)) != REG)
++      abort ();
++
++      /* Don't use MEMs as direct targets because on some machines
++       substituting a MEM for a REG makes invalid insns.
++       Let the combiner substitute the MEM if that is valid.  */
++      if (target == 0 || GET_CODE (target) != REG
++        || GET_MODE (target) != departing_mode)
++      target = gen_reg_rtx (departing_mode);
++
++      if (GET_CODE (target) != CONCAT)
++      abort ();
++
++      map->reg_map[REGNO (XEXP (loc, 0))] = XEXP (target, 0);
++      map->reg_map[REGNO (XEXP (loc, 1))] = XEXP (target, 1);
++    }
++  else
++    abort ();
++
++  /* Remap the exception handler data pointer from one to the other.  */
++  temp = get_exception_pointer (inl_f);
++  if (temp)
++    map->reg_map[REGNO (temp)] = get_exception_pointer (cfun);
++
++  /* Initialize label_map.  get_label_from_map will actually make
++     the labels.  */
++  memset ((char *) &map->label_map[min_labelno], 0,
++       (max_labelno - min_labelno) * sizeof (rtx));
++
++  /* Make copies of the decls of the symbols in the inline function, so that
++     the copies of the variables get declared in the current function.  Set
++     up things so that lookup_static_chain knows that to interpret registers
++     in SAVE_EXPRs for TYPE_SIZEs as local.  */
++  inline_function_decl = fndecl;
++  integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector);
++  block = integrate_decl_tree (inl_f->original_decl_initial, map);
++  BLOCK_ABSTRACT_ORIGIN (block) = DECL_ORIGIN (fndecl);
++  inline_function_decl = 0;
++
++  /* Make a fresh binding contour that we can easily remove.  Do this after
++     expanding our arguments so cleanups are properly scoped.  */
++  expand_start_bindings_and_block (0, block);
++
++  /* Sort the block-map so that it will be easy to find remapped
++     blocks later.  */
++  qsort (&VARRAY_TREE (map->block_map, 0),
++       map->block_map->elements_used,
++       sizeof (tree),
++       compare_blocks);
++
++  /* Perform postincrements before actually calling the function.  */
++  emit_queue ();
++
++  /* Clean up stack so that variables might have smaller offsets.  */
++  do_pending_stack_adjust ();
++
++  /* Save a copy of the location of const_equiv_varray for
++     mark_stores, called via note_stores.  */
++  global_const_equiv_varray = map->const_equiv_varray;
++
++  /* If the called function does an alloca, save and restore the
++     stack pointer around the call.  This saves stack space, but
++     also is required if this inline is being done between two
++     pushes.  */
++  if (inl_f->calls_alloca)
++    emit_stack_save (SAVE_BLOCK, &stack_save, NULL_RTX);
++
++  /* Map pseudos used for initial hard reg values.  */
++  setup_initial_hard_reg_value_integration (inl_f, map);
++
++  /* Now copy the insns one by one.  */
++  copy_insn_list (insns, map, static_chain_value);
++
++  /* Duplicate the EH regions.  This will create an offset from the
++     region numbers in the function we're inlining to the region
++     numbers in the calling function.  This must wait until after
++     copy_insn_list, as we need the insn map to be complete.  */
++  eh_region_offset = duplicate_eh_regions (inl_f, map);
++
++  /* Now copy the REG_NOTES for those insns.  */
++  copy_insn_notes (insns, map, eh_region_offset);
++
++  /* If the insn sequence required one, emit the return label.  */
++  if (map->local_return_label)
++    emit_label (map->local_return_label);
++
++  /* Restore the stack pointer if we saved it above.  */
++  if (inl_f->calls_alloca)
++    emit_stack_restore (SAVE_BLOCK, stack_save, NULL_RTX);
++
++  if (! cfun->x_whole_function_mode_p)
++    /* In statement-at-a-time mode, we just tell the front-end to add
++       this block to the list of blocks at this binding level.  We
++       can't do it the way it's done for function-at-a-time mode the
++       superblocks have not been created yet.  */
++    (*lang_hooks.decls.insert_block) (block);
++  else
++    {
++      BLOCK_CHAIN (block)
++      = BLOCK_CHAIN (DECL_INITIAL (current_function_decl));
++      BLOCK_CHAIN (DECL_INITIAL (current_function_decl)) = block;
++    }
++
++  /* End the scope containing the copied formal parameter variables
++     and copied LABEL_DECLs.  We pass NULL_TREE for the variables list
++     here so that expand_end_bindings will not check for unused
++     variables.  That's already been checked for when the inlined
++     function was defined.  */
++  expand_end_bindings (NULL_TREE, 1, 1);
++
++  /* Must mark the line number note after inlined functions as a repeat, so
++     that the test coverage code can avoid counting the call twice.  This
++     just tells the code to ignore the immediately following line note, since
++     there already exists a copy of this note before the expanded inline call.
++     This line number note is still needed for debugging though, so we can't
++     delete it.  */
++  if (flag_test_coverage)
++    emit_note (0, NOTE_INSN_REPEATED_LINE_NUMBER);
++
++  emit_line_note (input_filename, lineno);
++
++  /* If the function returns a BLKmode object in a register, copy it
++     out of the temp register into a BLKmode memory object.  */
++  if (target
++      && TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode
++      && ! aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl))))
++    target = copy_blkmode_from_reg (0, target, TREE_TYPE (TREE_TYPE (fndecl)));
++
++  if (structure_value_addr)
++    {
++      target = gen_rtx_MEM (TYPE_MODE (type),
++                          memory_address (TYPE_MODE (type),
++                                          structure_value_addr));
++      set_mem_attributes (target, type, 1);
++    }
++
++  /* Make sure we free the things we explicitly allocated with xmalloc.  */
++  if (real_label_map)
++    free (real_label_map);
++  VARRAY_FREE (map->const_equiv_varray);
++  free (map->reg_map);
++  free (map->insn_map);
++  free (map);
++  free (arg_vals);
++  free (arg_trees);
++
++  inlining = inlining_previous;
++
++  return target;
++}
++
++/* Make copies of each insn in the given list using the mapping
++   computed in expand_inline_function. This function may call itself for
++   insns containing sequences.
++
++   Copying is done in two passes, first the insns and then their REG_NOTES.
++
++   If static_chain_value is nonzero, it represents the context-pointer
++   register for the function.  */
++
++static void
++copy_insn_list (insns, map, static_chain_value)
++     rtx insns;
++     struct inline_remap *map;
++     rtx static_chain_value;
++{
++  int i;
++  rtx insn;
++  rtx temp;
++#ifdef HAVE_cc0
++  rtx cc0_insn = 0;
++#endif
++  rtx static_chain_mem = 0;
++
++  /* Copy the insns one by one.  Do this in two passes, first the insns and
++     then their REG_NOTES.  */
++
++  /* This loop is very similar to the loop in copy_loop_body in unroll.c.  */
++
++  for (insn = insns; insn; insn = NEXT_INSN (insn))
++    {
++      rtx copy, pattern, set;
++
++      map->orig_asm_operands_vector = 0;
++
++      switch (GET_CODE (insn))
++      {
++      case INSN:
++        pattern = PATTERN (insn);
++        set = single_set (insn);
++        copy = 0;
++        if (GET_CODE (pattern) == USE
++            && GET_CODE (XEXP (pattern, 0)) == REG
++            && REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
++          /* The (USE (REG n)) at return from the function should
++             be ignored since we are changing (REG n) into
++             inline_target.  */
++          break;
++
++        /* Ignore setting a function value that we don't want to use.  */
++        if (map->inline_target == 0
++            && set != 0
++            && GET_CODE (SET_DEST (set)) == REG
++            && REG_FUNCTION_VALUE_P (SET_DEST (set)))
++          {
++            if (volatile_refs_p (SET_SRC (set)))
++              {
++                rtx new_set;
++
++                /* If we must not delete the source,
++                   load it into a new temporary.  */
++                copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
++
++                new_set = single_set (copy);
++                if (new_set == 0)
++                  abort ();
++
++                SET_DEST (new_set)
++                  = gen_reg_rtx (GET_MODE (SET_DEST (new_set)));
++              }
++            /* If the source and destination are the same and it
++               has a note on it, keep the insn.  */
++            else if (rtx_equal_p (SET_DEST (set), SET_SRC (set))
++                     && REG_NOTES (insn) != 0)
++              copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
++            else
++              break;
++          }
++
++        /* Similarly if an ignored return value is clobbered.  */
++        else if (map->inline_target == 0
++                 && GET_CODE (pattern) == CLOBBER
++                 && GET_CODE (XEXP (pattern, 0)) == REG
++                 && REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
++          break;
++
++        /* Look for the address of the static chain slot. The
++             rtx_equal_p comparisons against the
++             static_chain_incoming_rtx below may fail if the static
++             chain is in memory and the address specified is not
++             "legitimate".  This happens on Xtensa where the static
++             chain is at a negative offset from argp and where only
++             positive offsets are legitimate.  When the RTL is
++             generated, the address is "legitimized" by copying it
++             into a register, causing the rtx_equal_p comparisons to
++             fail.  This workaround looks for code that sets a
++             register to the address of the static chain.  Subsequent
++             memory references via that register can then be
++             identified as static chain references.  We assume that
++             the register is only assigned once, and that the static
++             chain address is only live in one register at a time.  */
++
++        else if (static_chain_value != 0
++                 && set != 0
++                 && GET_CODE (static_chain_incoming_rtx) == MEM
++                 && GET_CODE (SET_DEST (set)) == REG
++                 && rtx_equal_p (SET_SRC (set),
++                                 XEXP (static_chain_incoming_rtx, 0)))
++          {
++            static_chain_mem =
++                gen_rtx_MEM (GET_MODE (static_chain_incoming_rtx),
++                             SET_DEST (set));
++
++            /* emit the instruction in case it is used for something
++               other than setting the static chain; if it's not used,
++               it can always be removed as dead code */
++            copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
++          }
++
++        /* If this is setting the static chain rtx, omit it.  */
++        else if (static_chain_value != 0
++                 && set != 0
++                 && (rtx_equal_p (SET_DEST (set),
++                                  static_chain_incoming_rtx)
++                     || (static_chain_mem
++                         && rtx_equal_p (SET_DEST (set), static_chain_mem))))
++          break;
++
++        /* If this is setting the static chain pseudo, set it from
++           the value we want to give it instead.  */
++        else if (static_chain_value != 0
++                 && set != 0
++                 && (rtx_equal_p (SET_SRC (set),
++                                  static_chain_incoming_rtx)
++                     || (static_chain_mem
++                         && rtx_equal_p (SET_SRC (set), static_chain_mem))))
++          {
++            rtx newdest = copy_rtx_and_substitute (SET_DEST (set), map, 1);
++
++            copy = emit_move_insn (newdest, static_chain_value);
++            if (GET_CODE (static_chain_incoming_rtx) != MEM)
++              static_chain_value = 0;
++          }
++
++        /* If this is setting the virtual stack vars register, this must
++           be the code at the handler for a builtin longjmp.  The value
++           saved in the setjmp buffer will be the address of the frame
++           we've made for this inlined instance within our frame.  But we
++           know the offset of that value so we can use it to reconstruct
++           our virtual stack vars register from that value.  If we are
++           copying it from the stack pointer, leave it unchanged.  */
++        else if (set != 0
++                 && rtx_equal_p (SET_DEST (set), virtual_stack_vars_rtx))
++          {
++            HOST_WIDE_INT offset;
++            temp = map->reg_map[REGNO (SET_DEST (set))];
++            temp = VARRAY_CONST_EQUIV (map->const_equiv_varray,
++                                       REGNO (temp)).rtx;
++
++            if (rtx_equal_p (temp, virtual_stack_vars_rtx))
++              offset = 0;
++            else if (GET_CODE (temp) == PLUS
++                     && rtx_equal_p (XEXP (temp, 0), virtual_stack_vars_rtx)
++                     && GET_CODE (XEXP (temp, 1)) == CONST_INT)
++              offset = INTVAL (XEXP (temp, 1));
++            else
++              abort ();
++
++            if (rtx_equal_p (SET_SRC (set), stack_pointer_rtx))
++              temp = SET_SRC (set);
++            else
++              temp = force_operand (plus_constant (SET_SRC (set),
++                                                   - offset),
++                                    NULL_RTX);
++
++            copy = emit_move_insn (virtual_stack_vars_rtx, temp);
++          }
++
++        else
++          copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
++        /* REG_NOTES will be copied later.  */
++
++#ifdef HAVE_cc0
++        /* If this insn is setting CC0, it may need to look at
++           the insn that uses CC0 to see what type of insn it is.
++           In that case, the call to recog via validate_change will
++           fail.  So don't substitute constants here.  Instead,
++           do it when we emit the following insn.
++
++           For example, see the pyr.md file.  That machine has signed and
++           unsigned compares.  The compare patterns must check the
++           following branch insn to see which what kind of compare to
++           emit.
++
++           If the previous insn set CC0, substitute constants on it as
++           well.  */
++        if (sets_cc0_p (PATTERN (copy)) != 0)
++          cc0_insn = copy;
++        else
++          {
++            if (cc0_insn)
++              try_constants (cc0_insn, map);
++            cc0_insn = 0;
++            try_constants (copy, map);
++          }
++#else
++        try_constants (copy, map);
++#endif
++        INSN_SCOPE (copy) = INSN_SCOPE (insn);
++        break;
++
++      case JUMP_INSN:
++        if (map->integrating && returnjump_p (insn))
++          {
++            if (map->local_return_label == 0)
++              map->local_return_label = gen_label_rtx ();
++            pattern = gen_jump (map->local_return_label);
++          }
++        else
++          pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0);
++
++        copy = emit_jump_insn (pattern);
++
++#ifdef HAVE_cc0
++        if (cc0_insn)
++          try_constants (cc0_insn, map);
++        cc0_insn = 0;
++#endif
++        try_constants (copy, map);
++        INSN_SCOPE (copy) = INSN_SCOPE (insn);
++
++        /* If this used to be a conditional jump insn but whose branch
++           direction is now know, we must do something special.  */
++        if (any_condjump_p (insn) && onlyjump_p (insn) && map->last_pc_value)
++          {
++#ifdef HAVE_cc0
++            /* If the previous insn set cc0 for us, delete it.  */
++            if (only_sets_cc0_p (PREV_INSN (copy)))
++              delete_related_insns (PREV_INSN (copy));
++#endif
++
++            /* If this is now a no-op, delete it.  */
++            if (map->last_pc_value == pc_rtx)
++              {
++                delete_related_insns (copy);
++                copy = 0;
++              }
++            else
++              /* Otherwise, this is unconditional jump so we must put a
++                 BARRIER after it.  We could do some dead code elimination
++                 here, but jump.c will do it just as well.  */
++              emit_barrier ();
++          }
++        break;
++
++      case CALL_INSN:
++        /* If this is a CALL_PLACEHOLDER insn then we need to copy the
++           three attached sequences: normal call, sibling call and tail
++           recursion.  */
++        if (GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++          {
++            rtx sequence[3];
++            rtx tail_label;
++
++            for (i = 0; i < 3; i++)
++              {
++                rtx seq;
++
++                sequence[i] = NULL_RTX;
++                seq = XEXP (PATTERN (insn), i);
++                if (seq)
++                  {
++                    start_sequence ();
++                    copy_insn_list (seq, map, static_chain_value);
++                    sequence[i] = get_insns ();
++                    end_sequence ();
++                  }
++              }
++
++            /* Find the new tail recursion label.
++               It will already be substituted into sequence[2].  */
++            tail_label = copy_rtx_and_substitute (XEXP (PATTERN (insn), 3),
++                                                  map, 0);
++
++            copy = emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode,
++                                                             sequence[0],
++                                                             sequence[1],
++                                                             sequence[2],
++                                                             tail_label));
++            break;
++          }
++
++        pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0);
++        copy = emit_call_insn (pattern);
++
++        SIBLING_CALL_P (copy) = SIBLING_CALL_P (insn);
++        CONST_OR_PURE_CALL_P (copy) = CONST_OR_PURE_CALL_P (insn);
++        INSN_SCOPE (copy) = INSN_SCOPE (insn);
++
++        /* Because the USAGE information potentially contains objects other
++           than hard registers, we need to copy it.  */
++
++        CALL_INSN_FUNCTION_USAGE (copy)
++          = copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn),
++                                     map, 0);
++
++#ifdef HAVE_cc0
++        if (cc0_insn)
++          try_constants (cc0_insn, map);
++        cc0_insn = 0;
++#endif
++        try_constants (copy, map);
++
++        /* Be lazy and assume CALL_INSNs clobber all hard registers.  */
++        for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
++          VARRAY_CONST_EQUIV (map->const_equiv_varray, i).rtx = 0;
++        break;
++
++      case CODE_LABEL:
++        copy = emit_label (get_label_from_map (map,
++                                               CODE_LABEL_NUMBER (insn)));
++        LABEL_NAME (copy) = LABEL_NAME (insn);
++        map->const_age++;
++        break;
++
++      case BARRIER:
++        copy = emit_barrier ();
++        break;
++
++      case NOTE:
++        if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)
++          {
++            copy = emit_label (get_label_from_map (map,
++                                                  CODE_LABEL_NUMBER (insn)));
++            LABEL_NAME (copy) = NOTE_SOURCE_FILE (insn);
++            map->const_age++;
++            break;
++          }
++
++        /* NOTE_INSN_FUNCTION_END and NOTE_INSN_FUNCTION_BEG are
++           discarded because it is important to have only one of
++           each in the current function.
++
++           NOTE_INSN_DELETED notes aren't useful.  */
++
++        if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
++            && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
++            && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
++          {
++            copy = emit_note (NOTE_SOURCE_FILE (insn),
++                              NOTE_LINE_NUMBER (insn));
++            if (copy
++                && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG
++                    || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END)
++                && NOTE_BLOCK (insn))
++              {
++                tree *mapped_block_p;
++
++                mapped_block_p
++                  = (tree *) bsearch (NOTE_BLOCK (insn),
++                                      &VARRAY_TREE (map->block_map, 0),
++                                      map->block_map->elements_used,
++                                      sizeof (tree),
++                                      find_block);
++
++                if (!mapped_block_p)
++                  abort ();
++                else
++                  NOTE_BLOCK (copy) = *mapped_block_p;
++              }
++            else if (copy
++                     && NOTE_LINE_NUMBER (copy) == NOTE_INSN_EXPECTED_VALUE)
++              NOTE_EXPECTED_VALUE (copy)
++                = copy_rtx_and_substitute (NOTE_EXPECTED_VALUE (insn),
++                                           map, 0);
++          }
++        else
++          copy = 0;
++        break;
++
++      default:
++        abort ();
++      }
++
++      if (copy)
++      RTX_INTEGRATED_P (copy) = 1;
++
++      map->insn_map[INSN_UID (insn)] = copy;
++    }
++}
++
++/* Copy the REG_NOTES.  Increment const_age, so that only constants
++   from parameters can be substituted in.  These are the only ones
++   that are valid across the entire function.  */
++
++static void
++copy_insn_notes (insns, map, eh_region_offset)
++     rtx insns;
++     struct inline_remap *map;
++     int eh_region_offset;
++{
++  rtx insn, new_insn;
++
++  map->const_age++;
++  for (insn = insns; insn; insn = NEXT_INSN (insn))
++    {
++      if (! INSN_P (insn))
++      continue;
++
++      new_insn = map->insn_map[INSN_UID (insn)];
++      if (! new_insn)
++      continue;
++
++      if (REG_NOTES (insn))
++        {
++        rtx next, note = copy_rtx_and_substitute (REG_NOTES (insn), map, 0);
++
++        /* We must also do subst_constants, in case one of our parameters
++           has const type and constant value.  */
++        subst_constants (&note, NULL_RTX, map, 0);
++        apply_change_group ();
++        REG_NOTES (new_insn) = note;
++
++        /* Delete any REG_LABEL notes from the chain.  Remap any
++             REG_EH_REGION notes.  */
++        for (; note; note = next)
++          {
++            next = XEXP (note, 1);
++            if (REG_NOTE_KIND (note) == REG_LABEL)
++              remove_note (new_insn, note);
++            else if (REG_NOTE_KIND (note) == REG_EH_REGION
++                     && INTVAL (XEXP (note, 0)) > 0)
++              XEXP (note, 0) = GEN_INT (INTVAL (XEXP (note, 0))
++                                        + eh_region_offset);
++          }
++        }
++
++      if (GET_CODE (insn) == CALL_INSN
++        && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
++      {
++        int i;
++        for (i = 0; i < 3; i++)
++          copy_insn_notes (XEXP (PATTERN (insn), i), map, eh_region_offset);
++      }
++
++      if (GET_CODE (insn) == JUMP_INSN
++        && GET_CODE (PATTERN (insn)) == RESX)
++      XINT (PATTERN (new_insn), 0) += eh_region_offset;
++    }
++}
++\f
++/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL,
++   push all of those decls and give each one the corresponding home.  */
++
++static void
++integrate_parm_decls (args, map, arg_vector)
++     tree args;
++     struct inline_remap *map;
++     rtvec arg_vector;
++{
++  tree tail;
++  int i;
++
++  for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
++    {
++      tree decl = copy_decl_for_inlining (tail, map->fndecl,
++                                        current_function_decl);
++      rtx new_decl_rtl
++      = copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map, 1);
++
++      /* We really should be setting DECL_INCOMING_RTL to something reasonable
++       here, but that's going to require some more work.  */
++      /* DECL_INCOMING_RTL (decl) = ?; */
++      /* Fully instantiate the address with the equivalent form so that the
++       debugging information contains the actual register, instead of the
++       virtual register.   Do this by not passing an insn to
++       subst_constants.  */
++      subst_constants (&new_decl_rtl, NULL_RTX, map, 1);
++      apply_change_group ();
++      SET_DECL_RTL (decl, new_decl_rtl);
++    }
++}
++
++/* Given a BLOCK node LET, push decls and levels so as to construct in the
++   current function a tree of contexts isomorphic to the one that is given.
++
++   MAP, if nonzero, is a pointer to an inline_remap map which indicates how
++   registers used in the DECL_RTL field should be remapped.  If it is zero,
++   no mapping is necessary.  */
++
++static tree
++integrate_decl_tree (let, map)
++     tree let;
++     struct inline_remap *map;
++{
++  tree t;
++  tree new_block;
++  tree *next;
++
++  new_block = make_node (BLOCK);
++  VARRAY_PUSH_TREE (map->block_map, new_block);
++  next = &BLOCK_VARS (new_block);
++
++  for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
++    {
++      tree d;
++
++      d = copy_decl_for_inlining (t, map->fndecl, current_function_decl);
++
++      if (DECL_RTL_SET_P (t))
++      {
++        rtx r;
++
++        SET_DECL_RTL (d, copy_rtx_and_substitute (DECL_RTL (t), map, 1));
++
++        /* Fully instantiate the address with the equivalent form so that the
++           debugging information contains the actual register, instead of the
++           virtual register.   Do this by not passing an insn to
++           subst_constants.  */
++        r = DECL_RTL (d);
++        subst_constants (&r, NULL_RTX, map, 1);
++        SET_DECL_RTL (d, r);
++
++        if (GET_CODE (r) == REG)
++          REGNO_DECL (REGNO (r)) = d;
++        else if (GET_CODE (r) == CONCAT)
++          {
++            REGNO_DECL (REGNO (XEXP (r, 0))) = d;
++            REGNO_DECL (REGNO (XEXP (r, 1))) = d;
++          }
++
++        apply_change_group ();
++      }
++
++      /* Add this declaration to the list of variables in the new
++       block.  */
++      *next = d;
++      next = &TREE_CHAIN (d);
++    }
++
++  next = &BLOCK_SUBBLOCKS (new_block);
++  for (t = BLOCK_SUBBLOCKS (let); t; t = BLOCK_CHAIN (t))
++    {
++      *next = integrate_decl_tree (t, map);
++      BLOCK_SUPERCONTEXT (*next) = new_block;
++      next = &BLOCK_CHAIN (*next);
++    }
++
++  TREE_USED (new_block) = TREE_USED (let);
++  BLOCK_ABSTRACT_ORIGIN (new_block) = let;
++
++  return new_block;
++}
++\f
++/* Create a new copy of an rtx. Recursively copies the operands of the rtx,
++   except for those few rtx codes that are sharable.
++
++   We always return an rtx that is similar to that incoming rtx, with the
++   exception of possibly changing a REG to a SUBREG or vice versa.  No
++   rtl is ever emitted.
++
++   If FOR_LHS is nonzero, if means we are processing something that will
++   be the LHS of a SET.  In that case, we copy RTX_UNCHANGING_P even if
++   inlining since we need to be conservative in how it is set for
++   such cases.
++
++   Handle constants that need to be placed in the constant pool by
++   calling `force_const_mem'.  */
++
++rtx
++copy_rtx_and_substitute (orig, map, for_lhs)
++     rtx orig;
++     struct inline_remap *map;
++     int for_lhs;
++{
++  rtx copy, temp;
++  int i, j;
++  RTX_CODE code;
++  enum machine_mode mode;
++  const char *format_ptr;
++  int regno;
++
++  if (orig == 0)
++    return 0;
++
++  code = GET_CODE (orig);
++  mode = GET_MODE (orig);
++
++  switch (code)
++    {
++    case REG:
++      /* If the stack pointer register shows up, it must be part of
++       stack-adjustments (*not* because we eliminated the frame pointer!).
++       Small hard registers are returned as-is.  Pseudo-registers
++       go through their `reg_map'.  */
++      regno = REGNO (orig);
++      if (regno <= LAST_VIRTUAL_REGISTER
++        || (map->integrating
++            && DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer == orig))
++      {
++        /* Some hard registers are also mapped,
++           but others are not translated.  */
++        if (map->reg_map[regno] != 0)
++          return map->reg_map[regno];
++
++        /* If this is the virtual frame pointer, make space in current
++           function's stack frame for the stack frame of the inline function.
++
++           Copy the address of this area into a pseudo.  Map
++           virtual_stack_vars_rtx to this pseudo and set up a constant
++           equivalence for it to be the address.  This will substitute the
++           address into insns where it can be substituted and use the new
++           pseudo where it can't.  */
++        else if (regno == VIRTUAL_STACK_VARS_REGNUM)
++          {
++            rtx loc, seq;
++            int size = get_func_frame_size (DECL_SAVED_INSNS (map->fndecl));
++#ifdef FRAME_GROWS_DOWNWARD
++            int alignment
++              = (DECL_SAVED_INSNS (map->fndecl)->stack_alignment_needed
++                 / BITS_PER_UNIT);
++
++            /* In this case, virtual_stack_vars_rtx points to one byte
++               higher than the top of the frame area.  So make sure we
++               allocate a big enough chunk to keep the frame pointer
++               aligned like a real one.  */
++            if (alignment)
++              size = CEIL_ROUND (size, alignment);
++#endif
++            start_sequence ();
++            loc = assign_stack_temp (BLKmode, size, 1);
++            loc = XEXP (loc, 0);
++#ifdef FRAME_GROWS_DOWNWARD
++            /* In this case, virtual_stack_vars_rtx points to one byte
++               higher than the top of the frame area.  So compute the offset
++               to one byte higher than our substitute frame.  */
++            loc = plus_constant (loc, size);
++#endif
++            map->reg_map[regno] = temp
++              = force_reg (Pmode, force_operand (loc, NULL_RTX));
++
++#ifdef STACK_BOUNDARY
++            mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY);
++#endif
++
++            SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
++
++            seq = get_insns ();
++            end_sequence ();
++            emit_insn_after (seq, map->insns_at_start);
++            return temp;
++          }
++        else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM
++                 || (map->integrating
++                     && (DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer
++                         == orig)))
++          {
++            /* Do the same for a block to contain any arguments referenced
++               in memory.  */
++            rtx loc, seq;
++            int size = DECL_SAVED_INSNS (map->fndecl)->args_size;
++
++            start_sequence ();
++            loc = assign_stack_temp (BLKmode, size, 1);
++            loc = XEXP (loc, 0);
++            /* When arguments grow downward, the virtual incoming
++               args pointer points to the top of the argument block,
++               so the remapped location better do the same.  */
++#ifdef ARGS_GROW_DOWNWARD
++            loc = plus_constant (loc, size);
++#endif
++            map->reg_map[regno] = temp
++              = force_reg (Pmode, force_operand (loc, NULL_RTX));
++
++#ifdef STACK_BOUNDARY
++            mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY);
++#endif
++
++            SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
++
++            seq = get_insns ();
++            end_sequence ();
++            emit_insn_after (seq, map->insns_at_start);
++            return temp;
++          }
++        else if (REG_FUNCTION_VALUE_P (orig))
++          {
++            /* This is a reference to the function return value.  If
++               the function doesn't have a return value, error.  If the
++               mode doesn't agree, and it ain't BLKmode, make a SUBREG.  */
++            if (map->inline_target == 0)
++              {
++                if (rtx_equal_function_value_matters)
++                  /* This is an ignored return value.  We must not
++                     leave it in with REG_FUNCTION_VALUE_P set, since
++                     that would confuse subsequent inlining of the
++                     current function into a later function.  */
++                  return gen_rtx_REG (GET_MODE (orig), regno);
++                else
++                  /* Must be unrolling loops or replicating code if we
++                     reach here, so return the register unchanged.  */
++                  return orig;
++              }
++            else if (GET_MODE (map->inline_target) != BLKmode
++                     && mode != GET_MODE (map->inline_target))
++              return gen_lowpart (mode, map->inline_target);
++            else
++              return map->inline_target;
++          }
++#if defined (LEAF_REGISTERS) && defined (LEAF_REG_REMAP)
++        /* If leaf_renumber_regs_insn() might remap this register to
++           some other number, make sure we don't share it with the
++           inlined function, otherwise delayed optimization of the
++           inlined function may change it in place, breaking our
++           reference to it.  We may still shared it within the
++           function, so create an entry for this register in the
++           reg_map.  */
++        if (map->integrating && regno < FIRST_PSEUDO_REGISTER
++            && LEAF_REGISTERS[regno] && LEAF_REG_REMAP (regno) != regno)
++          {
++            if (!map->leaf_reg_map[regno][mode])
++              map->leaf_reg_map[regno][mode] = gen_rtx_REG (mode, regno);
++            return map->leaf_reg_map[regno][mode]; 
++          }
++#endif
++        else
++          return orig;
++
++        abort ();
++      }
++      if (map->reg_map[regno] == NULL)
++      {
++        map->reg_map[regno] = gen_reg_rtx (mode);
++        REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig);
++        REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig);
++        RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig);
++        /* A reg with REG_FUNCTION_VALUE_P true will never reach here.  */
++
++        if (REG_POINTER (map->x_regno_reg_rtx[regno]))
++          mark_reg_pointer (map->reg_map[regno],
++                            map->regno_pointer_align[regno]);
++      }
++      return map->reg_map[regno];
++
++    case SUBREG:
++      copy = copy_rtx_and_substitute (SUBREG_REG (orig), map, for_lhs);
++      return simplify_gen_subreg (GET_MODE (orig), copy,
++                                GET_MODE (SUBREG_REG (orig)),
++                                SUBREG_BYTE (orig));
++
++    case ADDRESSOF:
++      copy = gen_rtx_ADDRESSOF (mode,
++                              copy_rtx_and_substitute (XEXP (orig, 0),
++                                                       map, for_lhs),
++                              0, ADDRESSOF_DECL (orig));
++      regno = ADDRESSOF_REGNO (orig);
++      if (map->reg_map[regno])
++      regno = REGNO (map->reg_map[regno]);
++      else if (regno > LAST_VIRTUAL_REGISTER)
++      {
++        temp = XEXP (orig, 0);
++        map->reg_map[regno] = gen_reg_rtx (GET_MODE (temp));
++        REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (temp);
++        REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (temp);
++        RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (temp);
++        /* A reg with REG_FUNCTION_VALUE_P true will never reach here.  */
++
++        /* Objects may initially be represented as registers, but
++           but turned into a MEM if their address is taken by
++           put_var_into_stack.  Therefore, the register table may have
++           entries which are MEMs.
++
++           We briefly tried to clear such entries, but that ended up
++           cascading into many changes due to the optimizers not being
++           prepared for empty entries in the register table.  So we've
++           decided to allow the MEMs in the register table for now.  */
++        if (REG_P (map->x_regno_reg_rtx[regno])
++            && REG_POINTER (map->x_regno_reg_rtx[regno]))
++          mark_reg_pointer (map->reg_map[regno],
++                            map->regno_pointer_align[regno]);
++        regno = REGNO (map->reg_map[regno]);
++      }
++      ADDRESSOF_REGNO (copy) = regno;
++      return copy;
++
++    case USE:
++    case CLOBBER:
++      /* USE and CLOBBER are ordinary, but we convert (use (subreg foo))
++       to (use foo) if the original insn didn't have a subreg.
++       Removing the subreg distorts the VAX movstrhi pattern
++       by changing the mode of an operand.  */
++      copy = copy_rtx_and_substitute (XEXP (orig, 0), map, code == CLOBBER);
++      if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG)
++      copy = SUBREG_REG (copy);
++      return gen_rtx_fmt_e (code, VOIDmode, copy);
++
++    /* We need to handle "deleted" labels that appear in the DECL_RTL
++       of a LABEL_DECL.  */
++    case NOTE:
++      if (NOTE_LINE_NUMBER (orig) != NOTE_INSN_DELETED_LABEL)
++      break;
++
++      /* ... FALLTHRU ...  */
++    case CODE_LABEL:
++      LABEL_PRESERVE_P (get_label_from_map (map, CODE_LABEL_NUMBER (orig)))
++      = LABEL_PRESERVE_P (orig);
++      return get_label_from_map (map, CODE_LABEL_NUMBER (orig));
++
++    case LABEL_REF:
++      copy
++      = gen_rtx_LABEL_REF
++        (mode,
++         LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0)
++         : get_label_from_map (map, CODE_LABEL_NUMBER (XEXP (orig, 0))));
++
++      LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig);
++
++      /* The fact that this label was previously nonlocal does not mean
++       it still is, so we must check if it is within the range of
++       this function's labels.  */
++      LABEL_REF_NONLOCAL_P (copy)
++      = (LABEL_REF_NONLOCAL_P (orig)
++         && ! (CODE_LABEL_NUMBER (XEXP (copy, 0)) >= get_first_label_num ()
++               && CODE_LABEL_NUMBER (XEXP (copy, 0)) < max_label_num ()));
++
++      /* If we have made a nonlocal label local, it means that this
++       inlined call will be referring to our nonlocal goto handler.
++       So make sure we create one for this block; we normally would
++       not since this is not otherwise considered a "call".  */
++      if (LABEL_REF_NONLOCAL_P (orig) && ! LABEL_REF_NONLOCAL_P (copy))
++      function_call_count++;
++
++      return copy;
++
++    case PC:
++    case CC0:
++    case CONST_INT:
++    case CONST_VECTOR:
++      return orig;
++
++    case SYMBOL_REF:
++      /* Symbols which represent the address of a label stored in the constant
++       pool must be modified to point to a constant pool entry for the
++       remapped label.  Otherwise, symbols are returned unchanged.  */
++      if (CONSTANT_POOL_ADDRESS_P (orig))
++      {
++        struct function *f = inlining ? inlining : cfun;
++        rtx constant = get_pool_constant_for_function (f, orig);
++        enum machine_mode const_mode = get_pool_mode_for_function (f, orig);
++        if (inlining)
++          {
++            rtx temp = force_const_mem (const_mode,
++                                        copy_rtx_and_substitute (constant,
++                                                                 map, 0));
++
++#if 0
++            /* Legitimizing the address here is incorrect.
++
++               Since we had a SYMBOL_REF before, we can assume it is valid
++               to have one in this position in the insn.
++
++               Also, change_address may create new registers.  These
++               registers will not have valid reg_map entries.  This can
++               cause try_constants() to fail because assumes that all
++               registers in the rtx have valid reg_map entries, and it may
++               end up replacing one of these new registers with junk.  */
++
++            if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
++              temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0));
++#endif
++
++            temp = XEXP (temp, 0);
++
++#ifdef POINTERS_EXTEND_UNSIGNED
++            if (GET_MODE (temp) != GET_MODE (orig))
++              temp = convert_memory_address (GET_MODE (orig), temp);
++#endif
++            return temp;
++          }
++        else if (GET_CODE (constant) == LABEL_REF)
++          return XEXP (force_const_mem
++                       (GET_MODE (orig),
++                        copy_rtx_and_substitute (constant, map, for_lhs)),
++                       0);
++      }
++
++      return orig;
++
++    case CONST_DOUBLE:
++      /* We have to make a new copy of this CONST_DOUBLE because don't want
++       to use the old value of CONST_DOUBLE_MEM.  Also, this may be a
++       duplicate of a CONST_DOUBLE we have already seen.  */
++      if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT)
++      {
++        REAL_VALUE_TYPE d;
++
++        REAL_VALUE_FROM_CONST_DOUBLE (d, orig);
++        return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (orig));
++      }
++      else
++      return immed_double_const (CONST_DOUBLE_LOW (orig),
++                                 CONST_DOUBLE_HIGH (orig), VOIDmode);
++
++    case CONST:
++      /* Make new constant pool entry for a constant
++       that was in the pool of the inline function.  */
++      if (RTX_INTEGRATED_P (orig))
++      abort ();
++      break;
++
++    case ASM_OPERANDS:
++      /* If a single asm insn contains multiple output operands then
++       it contains multiple ASM_OPERANDS rtx's that share the input
++       and constraint vecs.  We must make sure that the copied insn
++       continues to share it.  */
++      if (map->orig_asm_operands_vector == ASM_OPERANDS_INPUT_VEC (orig))
++      {
++        copy = rtx_alloc (ASM_OPERANDS);
++        RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil);
++        PUT_MODE (copy, GET_MODE (orig));
++        ASM_OPERANDS_TEMPLATE (copy) = ASM_OPERANDS_TEMPLATE (orig);
++        ASM_OPERANDS_OUTPUT_CONSTRAINT (copy)
++          = ASM_OPERANDS_OUTPUT_CONSTRAINT (orig);
++        ASM_OPERANDS_OUTPUT_IDX (copy) = ASM_OPERANDS_OUTPUT_IDX (orig);
++        ASM_OPERANDS_INPUT_VEC (copy) = map->copy_asm_operands_vector;
++        ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy)
++          = map->copy_asm_constraints_vector;
++        ASM_OPERANDS_SOURCE_FILE (copy) = ASM_OPERANDS_SOURCE_FILE (orig);
++        ASM_OPERANDS_SOURCE_LINE (copy) = ASM_OPERANDS_SOURCE_LINE (orig);
++        return copy;
++      }
++      break;
++
++    case CALL:
++      /* This is given special treatment because the first
++       operand of a CALL is a (MEM ...) which may get
++       forced into a register for cse.  This is undesirable
++       if function-address cse isn't wanted or if we won't do cse.  */
++#ifndef NO_FUNCTION_CSE
++      if (! (optimize && ! flag_no_function_cse))
++#endif
++      {
++        rtx copy
++          = gen_rtx_MEM (GET_MODE (XEXP (orig, 0)),
++                         copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0),
++                                                  map, 0));
++
++        MEM_COPY_ATTRIBUTES (copy, XEXP (orig, 0));
++
++        return
++          gen_rtx_CALL (GET_MODE (orig), copy, 
++                        copy_rtx_and_substitute (XEXP (orig, 1), map, 0));
++      }
++      break;
++
++#if 0
++      /* Must be ifdefed out for loop unrolling to work.  */
++    case RETURN:
++      abort ();
++#endif
++
++    case SET:
++      /* If this is setting fp or ap, it means that we have a nonlocal goto.
++       Adjust the setting by the offset of the area we made.
++       If the nonlocal goto is into the current function,
++       this will result in unnecessarily bad code, but should work.  */
++      if (SET_DEST (orig) == virtual_stack_vars_rtx
++        || SET_DEST (orig) == virtual_incoming_args_rtx)
++      {
++        /* In case a translation hasn't occurred already, make one now.  */
++        rtx equiv_reg;
++        rtx equiv_loc;
++        HOST_WIDE_INT loc_offset;
++
++        copy_rtx_and_substitute (SET_DEST (orig), map, for_lhs);
++        equiv_reg = map->reg_map[REGNO (SET_DEST (orig))];
++        equiv_loc = VARRAY_CONST_EQUIV (map->const_equiv_varray,
++                                        REGNO (equiv_reg)).rtx;
++        loc_offset
++          = GET_CODE (equiv_loc) == REG ? 0 : INTVAL (XEXP (equiv_loc, 1));
++
++        return gen_rtx_SET (VOIDmode, SET_DEST (orig),
++                            force_operand
++                            (plus_constant
++                             (copy_rtx_and_substitute (SET_SRC (orig),
++                                                       map, 0),
++                              - loc_offset),
++                             NULL_RTX));
++      }
++      else
++      return gen_rtx_SET (VOIDmode,
++                          copy_rtx_and_substitute (SET_DEST (orig), map, 1),
++                          copy_rtx_and_substitute (SET_SRC (orig), map, 0));
++      break;
++
++    case MEM:
++      if (inlining
++        && GET_CODE (XEXP (orig, 0)) == SYMBOL_REF
++        && CONSTANT_POOL_ADDRESS_P (XEXP (orig, 0)))
++      {
++        enum machine_mode const_mode
++          = get_pool_mode_for_function (inlining, XEXP (orig, 0));
++        rtx constant
++          = get_pool_constant_for_function (inlining, XEXP (orig, 0));
++
++        constant = copy_rtx_and_substitute (constant, map, 0);
++
++        /* If this was an address of a constant pool entry that itself
++           had to be placed in the constant pool, it might not be a
++           valid address.  So the recursive call might have turned it
++           into a register.  In that case, it isn't a constant any
++           more, so return it.  This has the potential of changing a
++           MEM into a REG, but we'll assume that it safe.  */
++        if (! CONSTANT_P (constant))
++          return constant;
++
++        return validize_mem (force_const_mem (const_mode, constant));
++      }
++
++      copy = gen_rtx_MEM (mode, copy_rtx_and_substitute (XEXP (orig, 0),
++                                                       map, 0));
++      MEM_COPY_ATTRIBUTES (copy, orig);
++
++      /* If inlining and this is not for the LHS, turn off RTX_UNCHANGING_P
++       since this may be an indirect reference to a parameter and the
++       actual may not be readonly.  */
++      if (inlining && !for_lhs)
++      RTX_UNCHANGING_P (copy) = 0;
++
++      /* If inlining, squish aliasing data that references the subroutine's
++       parameter list, since that's no longer applicable.  */
++      if (inlining && MEM_EXPR (copy)
++        && TREE_CODE (MEM_EXPR (copy)) == INDIRECT_REF
++        && TREE_CODE (TREE_OPERAND (MEM_EXPR (copy), 0)) == PARM_DECL)
++      set_mem_expr (copy, NULL_TREE);
++
++      return copy;
++
++    default:
++      break;
++    }
++
++  copy = rtx_alloc (code);
++  PUT_MODE (copy, mode);
++  RTX_FLAG (copy, in_struct) = RTX_FLAG (orig, in_struct);
++  RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil);
++  RTX_FLAG (copy, unchanging) = RTX_FLAG (orig, unchanging);
++
++  format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
++
++  for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
++    {
++      switch (*format_ptr++)
++      {
++      case '0':
++        /* Copy this through the wide int field; that's safest.  */
++        X0WINT (copy, i) = X0WINT (orig, i);
++        break;
++
++      case 'e':
++        XEXP (copy, i)
++          = copy_rtx_and_substitute (XEXP (orig, i), map, for_lhs);
++        break;
++
++      case 'u':
++        /* Change any references to old-insns to point to the
++           corresponding copied insns.  */
++        XEXP (copy, i) = map->insn_map[INSN_UID (XEXP (orig, i))];
++        break;
++
++      case 'E':
++        XVEC (copy, i) = XVEC (orig, i);
++        if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
++          {
++            XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
++            for (j = 0; j < XVECLEN (copy, i); j++)
++              XVECEXP (copy, i, j)
++                = copy_rtx_and_substitute (XVECEXP (orig, i, j),
++                                           map, for_lhs);
++          }
++        break;
++
++      case 'w':
++        XWINT (copy, i) = XWINT (orig, i);
++        break;
++
++      case 'i':
++        XINT (copy, i) = XINT (orig, i);
++        break;
++
++      case 's':
++        XSTR (copy, i) = XSTR (orig, i);
++        break;
++
++      case 't':
++        XTREE (copy, i) = XTREE (orig, i);
++        break;
++
++      default:
++        abort ();
++      }
++    }
++
++  if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0)
++    {
++      map->orig_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (orig);
++      map->copy_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (copy);
++      map->copy_asm_constraints_vector
++      = ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy);
++    }
++
++  return copy;
++}
++\f
++/* Substitute known constant values into INSN, if that is valid.  */
++
++void
++try_constants (insn, map)
++     rtx insn;
++     struct inline_remap *map;
++{
++  int i;
++
++  map->num_sets = 0;
++
++  /* First try just updating addresses, then other things.  This is
++     important when we have something like the store of a constant
++     into memory and we can update the memory address but the machine
++     does not support a constant source.  */
++  subst_constants (&PATTERN (insn), insn, map, 1);
++  apply_change_group ();
++  subst_constants (&PATTERN (insn), insn, map, 0);
++  apply_change_group ();
++
++  /* Show we don't know the value of anything stored or clobbered.  */
++  note_stores (PATTERN (insn), mark_stores, NULL);
++  map->last_pc_value = 0;
++#ifdef HAVE_cc0
++  map->last_cc0_value = 0;
++#endif
++
++  /* Set up any constant equivalences made in this insn.  */
++  for (i = 0; i < map->num_sets; i++)
++    {
++      if (GET_CODE (map->equiv_sets[i].dest) == REG)
++      {
++        int regno = REGNO (map->equiv_sets[i].dest);
++
++        MAYBE_EXTEND_CONST_EQUIV_VARRAY (map, regno);
++        if (VARRAY_CONST_EQUIV (map->const_equiv_varray, regno).rtx == 0
++            /* Following clause is a hack to make case work where GNU C++
++               reassigns a variable to make cse work right.  */
++            || ! rtx_equal_p (VARRAY_CONST_EQUIV (map->const_equiv_varray,
++                                                  regno).rtx,
++                              map->equiv_sets[i].equiv))
++          SET_CONST_EQUIV_DATA (map, map->equiv_sets[i].dest,
++                                map->equiv_sets[i].equiv, map->const_age);
++      }
++      else if (map->equiv_sets[i].dest == pc_rtx)
++      map->last_pc_value = map->equiv_sets[i].equiv;
++#ifdef HAVE_cc0
++      else if (map->equiv_sets[i].dest == cc0_rtx)
++      map->last_cc0_value = map->equiv_sets[i].equiv;
++#endif
++    }
++}
++\f
++/* Substitute known constants for pseudo regs in the contents of LOC,
++   which are part of INSN.
++   If INSN is zero, the substitution should always be done (this is used to
++   update DECL_RTL).
++   These changes are taken out by try_constants if the result is not valid.
++
++   Note that we are more concerned with determining when the result of a SET
++   is a constant, for further propagation, than actually inserting constants
++   into insns; cse will do the latter task better.
++
++   This function is also used to adjust address of items previously addressed
++   via the virtual stack variable or virtual incoming arguments registers.
++
++   If MEMONLY is nonzero, only make changes inside a MEM.  */
++
++static void
++subst_constants (loc, insn, map, memonly)
++     rtx *loc;
++     rtx insn;
++     struct inline_remap *map;
++     int memonly;
++{
++  rtx x = *loc;
++  int i, j;
++  enum rtx_code code;
++  const char *format_ptr;
++  int num_changes = num_validated_changes ();
++  rtx new = 0;
++  enum machine_mode op0_mode = MAX_MACHINE_MODE;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case PC:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST_VECTOR:
++    case SYMBOL_REF:
++    case CONST:
++    case LABEL_REF:
++    case ADDRESS:
++      return;
++
++#ifdef HAVE_cc0
++    case CC0:
++      if (! memonly)
++      validate_change (insn, loc, map->last_cc0_value, 1);
++      return;
++#endif
++
++    case USE:
++    case CLOBBER:
++      /* The only thing we can do with a USE or CLOBBER is possibly do
++       some substitutions in a MEM within it.  */
++      if (GET_CODE (XEXP (x, 0)) == MEM)
++      subst_constants (&XEXP (XEXP (x, 0), 0), insn, map, 0);
++      return;
++
++    case REG:
++      /* Substitute for parms and known constants.  Don't replace
++       hard regs used as user variables with constants.  */
++      if (! memonly)
++      {
++        int regno = REGNO (x);
++        struct const_equiv_data *p;
++
++        if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x))
++            && (size_t) regno < VARRAY_SIZE (map->const_equiv_varray)
++            && (p = &VARRAY_CONST_EQUIV (map->const_equiv_varray, regno),
++                p->rtx != 0)
++            && p->age >= map->const_age)
++          validate_change (insn, loc, p->rtx, 1);
++      }
++      return;
++
++    case SUBREG:
++      /* SUBREG applied to something other than a reg
++       should be treated as ordinary, since that must
++       be a special hack and we don't know how to treat it specially.
++       Consider for example mulsidi3 in m68k.md.
++       Ordinary SUBREG of a REG needs this special treatment.  */
++      if (! memonly && GET_CODE (SUBREG_REG (x)) == REG)
++      {
++        rtx inner = SUBREG_REG (x);
++        rtx new = 0;
++
++        /* We can't call subst_constants on &SUBREG_REG (x) because any
++           constant or SUBREG wouldn't be valid inside our SUBEG.  Instead,
++           see what is inside, try to form the new SUBREG and see if that is
++           valid.  We handle two cases: extracting a full word in an
++           integral mode and extracting the low part.  */
++        subst_constants (&inner, NULL_RTX, map, 0);
++        new = simplify_gen_subreg (GET_MODE (x), inner,
++                                   GET_MODE (SUBREG_REG (x)),
++                                   SUBREG_BYTE (x));
++
++        if (new)
++          validate_change (insn, loc, new, 1);
++        else
++          cancel_changes (num_changes);
++
++        return;
++      }
++      break;
++
++    case MEM:
++      subst_constants (&XEXP (x, 0), insn, map, 0);
++
++      /* If a memory address got spoiled, change it back.  */
++      if (! memonly && insn != 0 && num_validated_changes () != num_changes
++        && ! memory_address_p (GET_MODE (x), XEXP (x, 0)))
++      cancel_changes (num_changes);
++      return;
++
++    case SET:
++      {
++      /* Substitute constants in our source, and in any arguments to a
++         complex (e..g, ZERO_EXTRACT) destination, but not in the destination
++         itself.  */
++      rtx *dest_loc = &SET_DEST (x);
++      rtx dest = *dest_loc;
++      rtx src, tem;
++      enum machine_mode compare_mode = VOIDmode;
++
++      /* If SET_SRC is a COMPARE which subst_constants would turn into
++         COMPARE of 2 VOIDmode constants, note the mode in which comparison
++         is to be done.  */
++      if (GET_CODE (SET_SRC (x)) == COMPARE)
++        {
++          src = SET_SRC (x);
++          if (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC
++#ifdef HAVE_cc0
++              || dest == cc0_rtx
++#endif
++              )
++            {
++              compare_mode = GET_MODE (XEXP (src, 0));
++              if (compare_mode == VOIDmode)
++                compare_mode = GET_MODE (XEXP (src, 1));
++            }
++        }
++
++      subst_constants (&SET_SRC (x), insn, map, memonly);
++      src = SET_SRC (x);
++
++      while (GET_CODE (*dest_loc) == ZERO_EXTRACT
++             || GET_CODE (*dest_loc) == SUBREG
++             || GET_CODE (*dest_loc) == STRICT_LOW_PART)
++        {
++          if (GET_CODE (*dest_loc) == ZERO_EXTRACT)
++            {
++              subst_constants (&XEXP (*dest_loc, 1), insn, map, memonly);
++              subst_constants (&XEXP (*dest_loc, 2), insn, map, memonly);
++            }
++          dest_loc = &XEXP (*dest_loc, 0);
++        }
++
++      /* Do substitute in the address of a destination in memory.  */
++      if (GET_CODE (*dest_loc) == MEM)
++        subst_constants (&XEXP (*dest_loc, 0), insn, map, 0);
++
++      /* Check for the case of DEST a SUBREG, both it and the underlying
++         register are less than one word, and the SUBREG has the wider mode.
++         In the case, we are really setting the underlying register to the
++         source converted to the mode of DEST.  So indicate that.  */
++      if (GET_CODE (dest) == SUBREG
++          && GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD
++          && GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD
++          && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
++                    <= GET_MODE_SIZE (GET_MODE (dest)))
++          && (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)),
++                                             src)))
++        src = tem, dest = SUBREG_REG (dest);
++
++      /* If storing a recognizable value save it for later recording.  */
++      if ((map->num_sets < MAX_RECOG_OPERANDS)
++          && (CONSTANT_P (src)
++              || (GET_CODE (src) == REG
++                  && (REGNO (src) == VIRTUAL_INCOMING_ARGS_REGNUM
++                      || REGNO (src) == VIRTUAL_STACK_VARS_REGNUM))
++              || (GET_CODE (src) == PLUS
++                  && GET_CODE (XEXP (src, 0)) == REG
++                  && (REGNO (XEXP (src, 0)) == VIRTUAL_INCOMING_ARGS_REGNUM
++                      || REGNO (XEXP (src, 0)) == VIRTUAL_STACK_VARS_REGNUM)
++                  && CONSTANT_P (XEXP (src, 1)))
++              || GET_CODE (src) == COMPARE
++#ifdef HAVE_cc0
++              || dest == cc0_rtx
++#endif
++              || (dest == pc_rtx
++                  && (src == pc_rtx || GET_CODE (src) == RETURN
++                      || GET_CODE (src) == LABEL_REF))))
++        {
++          /* Normally, this copy won't do anything.  But, if SRC is a COMPARE
++             it will cause us to save the COMPARE with any constants
++             substituted, which is what we want for later.  */
++          rtx src_copy = copy_rtx (src);
++          map->equiv_sets[map->num_sets].equiv = src_copy;
++          map->equiv_sets[map->num_sets++].dest = dest;
++          if (compare_mode != VOIDmode
++              && GET_CODE (src) == COMPARE
++              && (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC
++#ifdef HAVE_cc0
++                  || dest == cc0_rtx
++#endif
++                  )
++              && GET_MODE (XEXP (src, 0)) == VOIDmode
++              && GET_MODE (XEXP (src, 1)) == VOIDmode)
++            {
++              map->compare_src = src_copy;
++              map->compare_mode = compare_mode;
++            }
++        }
++      }
++      return;
++
++    default:
++      break;
++    }
++
++  format_ptr = GET_RTX_FORMAT (code);
++
++  /* If the first operand is an expression, save its mode for later.  */
++  if (*format_ptr == 'e')
++    op0_mode = GET_MODE (XEXP (x, 0));
++
++  for (i = 0; i < GET_RTX_LENGTH (code); i++)
++    {
++      switch (*format_ptr++)
++      {
++      case '0':
++        break;
++
++      case 'e':
++        if (XEXP (x, i))
++          subst_constants (&XEXP (x, i), insn, map, memonly);
++        break;
++
++      case 'u':
++      case 'i':
++      case 's':
++      case 'w':
++      case 'n':
++      case 't':
++      case 'B':
++        break;
++
++      case 'E':
++        if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
++          for (j = 0; j < XVECLEN (x, i); j++)
++            subst_constants (&XVECEXP (x, i, j), insn, map, memonly);
++
++        break;
++
++      default:
++        abort ();
++      }
++    }
++
++  /* If this is a commutative operation, move a constant to the second
++     operand unless the second operand is already a CONST_INT.  */
++  if (! memonly
++      && (GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ)
++      && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT)
++    {
++      rtx tem = XEXP (x, 0);
++      validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
++      validate_change (insn, &XEXP (x, 1), tem, 1);
++    }
++
++  /* Simplify the expression in case we put in some constants.  */
++  if (! memonly)
++    switch (GET_RTX_CLASS (code))
++      {
++      case '1':
++      if (op0_mode == MAX_MACHINE_MODE)
++        abort ();
++      new = simplify_unary_operation (code, GET_MODE (x),
++                                      XEXP (x, 0), op0_mode);
++      break;
++
++      case '<':
++      {
++        enum machine_mode op_mode = GET_MODE (XEXP (x, 0));
++
++        if (op_mode == VOIDmode)
++          op_mode = GET_MODE (XEXP (x, 1));
++        new = simplify_relational_operation (code, op_mode,
++                                             XEXP (x, 0), XEXP (x, 1));
++#ifdef FLOAT_STORE_FLAG_VALUE
++        if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
++          {
++            enum machine_mode mode = GET_MODE (x);
++            if (new == const0_rtx)
++              new = CONST0_RTX (mode);
++            else
++              {
++                REAL_VALUE_TYPE val;
++
++                /* Avoid automatic aggregate initialization.  */
++                val = FLOAT_STORE_FLAG_VALUE (mode);
++                new = CONST_DOUBLE_FROM_REAL_VALUE (val, mode);
++              }
++          }
++#endif
++        break;
++      }
++
++      case '2':
++      case 'c':
++      new = simplify_binary_operation (code, GET_MODE (x),
++                                       XEXP (x, 0), XEXP (x, 1));
++      break;
++
++      case 'b':
++      case '3':
++      if (op0_mode == MAX_MACHINE_MODE)
++        abort ();
++
++      if (code == IF_THEN_ELSE)
++        {
++          rtx op0 = XEXP (x, 0);
++
++          if (GET_RTX_CLASS (GET_CODE (op0)) == '<'
++              && GET_MODE (op0) == VOIDmode
++              && ! side_effects_p (op0)
++              && XEXP (op0, 0) == map->compare_src
++              && GET_MODE (XEXP (op0, 1)) == VOIDmode)
++            {
++              /* We have compare of two VOIDmode constants for which
++                 we recorded the comparison mode.  */
++              rtx temp =
++                simplify_relational_operation (GET_CODE (op0),
++                                               map->compare_mode,
++                                               XEXP (op0, 0),
++                                               XEXP (op0, 1));
++
++              if (temp == const0_rtx)
++                new = XEXP (x, 2);
++              else if (temp == const1_rtx)
++                new = XEXP (x, 1);
++            }
++        }
++      if (!new)
++        new = simplify_ternary_operation (code, GET_MODE (x), op0_mode,
++                                          XEXP (x, 0), XEXP (x, 1),
++                                          XEXP (x, 2));
++      break;
++      }
++
++  if (new)
++    validate_change (insn, loc, new, 1);
++}
++
++/* Show that register modified no longer contain known constants.  We are
++   called from note_stores with parts of the new insn.  */
++
++static void
++mark_stores (dest, x, data)
++     rtx dest;
++     rtx x ATTRIBUTE_UNUSED;
++     void *data ATTRIBUTE_UNUSED;
++{
++  int regno = -1;
++  enum machine_mode mode = VOIDmode;
++
++  /* DEST is always the innermost thing set, except in the case of
++     SUBREGs of hard registers.  */
++
++  if (GET_CODE (dest) == REG)
++    regno = REGNO (dest), mode = GET_MODE (dest);
++  else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG)
++    {
++      regno = REGNO (SUBREG_REG (dest));
++      if (regno < FIRST_PSEUDO_REGISTER)
++      regno += subreg_regno_offset (REGNO (SUBREG_REG (dest)),
++                                    GET_MODE (SUBREG_REG (dest)),
++                                    SUBREG_BYTE (dest),
++                                    GET_MODE (dest));
++      mode = GET_MODE (SUBREG_REG (dest));
++    }
++
++  if (regno >= 0)
++    {
++      unsigned int uregno = regno;
++      unsigned int last_reg = (uregno >= FIRST_PSEUDO_REGISTER ? uregno
++                             : uregno + HARD_REGNO_NREGS (uregno, mode) - 1);
++      unsigned int i;
++
++      /* Ignore virtual stack var or virtual arg register since those
++       are handled separately.  */
++      if (uregno != VIRTUAL_INCOMING_ARGS_REGNUM
++        && uregno != VIRTUAL_STACK_VARS_REGNUM)
++      for (i = uregno; i <= last_reg; i++)
++        if ((size_t) i < VARRAY_SIZE (global_const_equiv_varray))
++          VARRAY_CONST_EQUIV (global_const_equiv_varray, i).rtx = 0;
++    }
++}
++\f
++/* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the
++   given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so
++   that it points to the node itself, thus indicating that the node is its
++   own (abstract) origin.  Additionally, if the BLOCK_ABSTRACT_ORIGIN for
++   the given node is NULL, recursively descend the decl/block tree which
++   it is the root of, and for each other ..._DECL or BLOCK node contained
++   therein whose DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also
++   still NULL, set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN
++   values to point to themselves.  */
++
++static void
++set_block_origin_self (stmt)
++     tree stmt;
++{
++  if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE)
++    {
++      BLOCK_ABSTRACT_ORIGIN (stmt) = stmt;
++
++      {
++      tree local_decl;
++
++      for (local_decl = BLOCK_VARS (stmt);
++           local_decl != NULL_TREE;
++           local_decl = TREE_CHAIN (local_decl))
++        set_decl_origin_self (local_decl);    /* Potential recursion.  */
++      }
++
++      {
++      tree subblock;
++
++      for (subblock = BLOCK_SUBBLOCKS (stmt);
++           subblock != NULL_TREE;
++           subblock = BLOCK_CHAIN (subblock))
++        set_block_origin_self (subblock);     /* Recurse.  */
++      }
++    }
++}
++
++/* Given a pointer to some ..._DECL node, if the DECL_ABSTRACT_ORIGIN for
++   the given ..._DECL node is NULL, set the DECL_ABSTRACT_ORIGIN for the
++   node to so that it points to the node itself, thus indicating that the
++   node represents its own (abstract) origin.  Additionally, if the
++   DECL_ABSTRACT_ORIGIN for the given node is NULL, recursively descend
++   the decl/block tree of which the given node is the root of, and for
++   each other ..._DECL or BLOCK node contained therein whose
++   DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also still NULL,
++   set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN values to
++   point to themselves.  */
++
++void
++set_decl_origin_self (decl)
++     tree decl;
++{
++  if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE)
++    {
++      DECL_ABSTRACT_ORIGIN (decl) = decl;
++      if (TREE_CODE (decl) == FUNCTION_DECL)
++      {
++        tree arg;
++
++        for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
++          DECL_ABSTRACT_ORIGIN (arg) = arg;
++        if (DECL_INITIAL (decl) != NULL_TREE
++            && DECL_INITIAL (decl) != error_mark_node)
++          set_block_origin_self (DECL_INITIAL (decl));
++      }
++    }
++}
++\f
++/* Given a pointer to some BLOCK node, and a boolean value to set the
++   "abstract" flags to, set that value into the BLOCK_ABSTRACT flag for
++   the given block, and for all local decls and all local sub-blocks
++   (recursively) which are contained therein.  */
++
++static void
++set_block_abstract_flags (stmt, setting)
++     tree stmt;
++     int setting;
++{
++  tree local_decl;
++  tree subblock;
++
++  BLOCK_ABSTRACT (stmt) = setting;
++
++  for (local_decl = BLOCK_VARS (stmt);
++       local_decl != NULL_TREE;
++       local_decl = TREE_CHAIN (local_decl))
++    set_decl_abstract_flags (local_decl, setting);
++
++  for (subblock = BLOCK_SUBBLOCKS (stmt);
++       subblock != NULL_TREE;
++       subblock = BLOCK_CHAIN (subblock))
++    set_block_abstract_flags (subblock, setting);
++}
++
++/* Given a pointer to some ..._DECL node, and a boolean value to set the
++   "abstract" flags to, set that value into the DECL_ABSTRACT flag for the
++   given decl, and (in the case where the decl is a FUNCTION_DECL) also
++   set the abstract flags for all of the parameters, local vars, local
++   blocks and sub-blocks (recursively) to the same setting.  */
++
++void
++set_decl_abstract_flags (decl, setting)
++     tree decl;
++     int setting;
++{
++  DECL_ABSTRACT (decl) = setting;
++  if (TREE_CODE (decl) == FUNCTION_DECL)
++    {
++      tree arg;
++
++      for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
++      DECL_ABSTRACT (arg) = setting;
++      if (DECL_INITIAL (decl) != NULL_TREE
++        && DECL_INITIAL (decl) != error_mark_node)
++      set_block_abstract_flags (DECL_INITIAL (decl), setting);
++    }
++}
++\f
++/* Output the assembly language code for the function FNDECL
++   from its DECL_SAVED_INSNS.  Used for inline functions that are output
++   at end of compilation instead of where they came in the source.  */
++
++static GTY(()) struct function *old_cfun;
++
++void
++output_inline_function (fndecl)
++     tree fndecl;
++{
++  enum debug_info_type old_write_symbols = write_symbols;
++  const struct gcc_debug_hooks *const old_debug_hooks = debug_hooks;
++  struct function *f = DECL_SAVED_INSNS (fndecl);
++
++  old_cfun = cfun;
++  cfun = f;
++  current_function_decl = fndecl;
++
++  set_new_last_label_num (f->inl_max_label_num);
++
++  /* We're not deferring this any longer.  */
++  DECL_DEFER_OUTPUT (fndecl) = 0;
++
++  /* If requested, suppress debugging information.  */
++  if (f->no_debugging_symbols)
++    {
++      write_symbols = NO_DEBUG;
++      debug_hooks = &do_nothing_debug_hooks;
++    }
++
++  /* Make sure warnings emitted by the optimizers (e.g. control reaches
++     end of non-void function) is not wildly incorrect.  */
++  input_filename = DECL_SOURCE_FILE (fndecl);
++  lineno = DECL_SOURCE_LINE (fndecl);
++
++  /* Compile this function all the way down to assembly code.  As a
++     side effect this destroys the saved RTL representation, but
++     that's okay, because we don't need to inline this anymore.  */
++  rest_of_compilation (fndecl);
++  DECL_INLINE (fndecl) = 0;
++
++  cfun = old_cfun;
++  current_function_decl = old_cfun ? old_cfun->decl : 0;
++  write_symbols = old_write_symbols;
++  debug_hooks = old_debug_hooks;
++}
++
++\f
++/* Functions to keep track of the values hard regs had at the start of
++   the function.  */
++
++rtx
++get_hard_reg_initial_reg (fun, reg)
++     struct function *fun;
++     rtx reg;
++{
++  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
++  int i;
++
++  if (ivs == 0)
++    return NULL_RTX;
++
++  for (i = 0; i < ivs->num_entries; i++)
++    if (rtx_equal_p (ivs->entries[i].pseudo, reg))
++      return ivs->entries[i].hard_reg;
++
++  return NULL_RTX;
++}
++
++rtx
++has_func_hard_reg_initial_val (fun, reg)
++     struct function *fun;
++     rtx reg;
++{
++  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
++  int i;
++
++  if (ivs == 0)
++    return NULL_RTX;
++
++  for (i = 0; i < ivs->num_entries; i++)
++    if (rtx_equal_p (ivs->entries[i].hard_reg, reg))
++      return ivs->entries[i].pseudo;
++
++  return NULL_RTX;
++}
++
++rtx
++get_func_hard_reg_initial_val (fun, reg)
++     struct function *fun;
++     rtx reg;
++{
++  struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
++  rtx rv = has_func_hard_reg_initial_val (fun, reg);
++
++  if (rv)
++    return rv;
++
++  if (ivs == 0)
++    {
++      fun->hard_reg_initial_vals = (void *) ggc_alloc (sizeof (initial_value_struct));
++      ivs = fun->hard_reg_initial_vals;
++      ivs->num_entries = 0;
++      ivs->max_entries = 5;
++      ivs->entries = (initial_value_pair *) ggc_alloc (5 * sizeof (initial_value_pair));
++    }
++
++  if (ivs->num_entries >= ivs->max_entries)
++    {
++      ivs->max_entries += 5;
++      ivs->entries = 
++      (initial_value_pair *) ggc_realloc (ivs->entries,
++                                          ivs->max_entries
++                                          * sizeof (initial_value_pair));
++    }
++
++  ivs->entries[ivs->num_entries].hard_reg = reg;
++  ivs->entries[ivs->num_entries].pseudo = gen_reg_rtx (GET_MODE (reg));
++
++  return ivs->entries[ivs->num_entries++].pseudo;
++}
++
++rtx
++get_hard_reg_initial_val (mode, regno)
++     enum machine_mode mode;
++     int regno;
++{
++  return get_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
++}
++
++rtx
++has_hard_reg_initial_val (mode, regno)
++     enum machine_mode mode;
++     int regno;
++{
++  return has_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
++}
++
++static void
++setup_initial_hard_reg_value_integration (inl_f, remap)
++     struct function *inl_f;
++     struct inline_remap *remap;
++{
++  struct initial_value_struct *ivs = inl_f->hard_reg_initial_vals;
++  int i;
++
++  if (ivs == 0)
++    return;
++
++  for (i = 0; i < ivs->num_entries; i ++)
++    remap->reg_map[REGNO (ivs->entries[i].pseudo)]
++      = get_func_hard_reg_initial_val (cfun, ivs->entries[i].hard_reg);
++}
++
++
++void
++emit_initial_value_sets ()
++{
++  struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
++  int i;
++  rtx seq;
++
++  if (ivs == 0)
++    return;
++
++  start_sequence ();
++  for (i = 0; i < ivs->num_entries; i++)
++    emit_move_insn (ivs->entries[i].pseudo, ivs->entries[i].hard_reg);
++  seq = get_insns ();
++  end_sequence ();
++
++  emit_insn_after (seq, get_insns ());
++}
++
++/* If the backend knows where to allocate pseudos for hard
++   register initial values, register these allocations now.  */
++void
++allocate_initial_values (reg_equiv_memory_loc)
++     rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED;
++{
++#ifdef ALLOCATE_INITIAL_VALUE
++  struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
++  int i;
++
++  if (ivs == 0)
++    return;
++
++  for (i = 0; i < ivs->num_entries; i++)
++    {
++      int regno = REGNO (ivs->entries[i].pseudo);
++      rtx x = ALLOCATE_INITIAL_VALUE (ivs->entries[i].hard_reg);
++
++      if (x == NULL_RTX || REG_N_SETS (REGNO (ivs->entries[i].pseudo)) > 1)
++      ; /* Do nothing.  */
++      else if (GET_CODE (x) == MEM)
++      reg_equiv_memory_loc[regno] = x;
++      else if (GET_CODE (x) == REG)
++      {
++        reg_renumber[regno] = REGNO (x);
++        /* Poke the regno right into regno_reg_rtx
++           so that even fixed regs are accepted.  */
++        REGNO (ivs->entries[i].pseudo) = REGNO (x);
++      }
++      else abort ();
++    }
++#endif
++}
++
++#include "gt-integrate.h"
+diff -ruN gcc-3.3.1/gcc/libgcc-std.ver gcc-3.3.1.pp/gcc/libgcc-std.ver
+--- gcc-3.3.1/gcc/libgcc-std.ver       2003-07-13 21:25:09.000000000 +0000
++++ gcc-3.3.1.pp/gcc/libgcc-std.ver    2003-09-05 11:58:59.000000000 +0000
+@@ -174,6 +174,10 @@
+   _Unwind_SjLj_RaiseException
+   _Unwind_SjLj_ForcedUnwind
+   _Unwind_SjLj_Resume
++
++  # stack smash handler symbols
++  __guard
++  __stack_smash_handler
+ }
+ %inherit GCC_3.3 GCC_3.0
+diff -ruN gcc-3.3.1/gcc/libgcc2.c gcc-3.3.1.pp/gcc/libgcc2.c
+--- gcc-3.3.1/gcc/libgcc2.c    2002-10-23 10:47:24.000000000 +0000
++++ gcc-3.3.1.pp/gcc/libgcc2.c 2003-09-05 11:58:59.000000000 +0000
+@@ -1993,3 +1993,102 @@
+ #endif /* NEED_ATEXIT */
+ #endif /* L_exit */
++\f
++#ifdef L_stack_smash_handler
++#include <stdio.h>
++#include <string.h>
++#include <fcntl.h>
++#include <unistd.h>
++
++#ifdef _POSIX_SOURCE
++#include <signal.h>
++#endif
++
++#if defined(HAVE_SYSLOG)
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++
++#include <sys/syslog.h>
++#ifndef _PATH_LOG
++#define _PATH_LOG "/dev/log"
++#endif
++#endif
++
++long __guard[8] = {0,0,0,0,0,0,0,0};
++static void __guard_setup (void) __attribute__ ((constructor)) ;
++static void __guard_setup (void)
++{
++  int fd;
++  if (__guard[0]!=0) return;
++  fd = open ("/dev/urandom", 0);
++  if (fd != -1) {
++    ssize_t size = read (fd, (char*)&__guard, sizeof(__guard));
++    close (fd) ;
++    if (size == sizeof(__guard)) return;
++  }
++  /* If a random generator can't be used, the protector switches the guard
++     to the "terminator canary" */
++  ((char*)__guard)[0] = 0; ((char*)__guard)[1] = 0;
++  ((char*)__guard)[2] = '\n'; ((char*)__guard)[3] = 255;
++}
++void __stack_smash_handler (char func[], int damaged ATTRIBUTE_UNUSED)
++{
++#if defined (__GNU_LIBRARY__)
++  extern char * __progname;
++#endif
++  const char message[] = ": stack smashing attack in function ";
++  int bufsz = 256, len;
++  char buf[bufsz];
++#if defined(HAVE_SYSLOG)
++  int LogFile;
++  struct sockaddr_un SyslogAddr;  /* AF_UNIX address of local logger */
++#endif
++#ifdef _POSIX_SOURCE
++  {
++    sigset_t mask;
++    sigfillset(&mask);
++    sigdelset(&mask, SIGABRT);        /* Block all signal handlers */
++    sigprocmask(SIG_BLOCK, &mask, NULL); /* except SIGABRT */
++  }
++#endif
++
++  strcpy(buf, "<2>"); len=3;  /* send LOG_CRIT */
++#if defined (__GNU_LIBRARY__)
++  strncat(buf, __progname, bufsz-len-1); len = strlen(buf);
++#endif
++  if (bufsz>len) {strncat(buf, message, bufsz-len-1); len = strlen(buf);}
++  if (bufsz>len) {strncat(buf, func, bufsz-len-1); len = strlen(buf);}
++
++  /* print error message */
++  write (STDERR_FILENO, buf+3, len-3);
++#if defined(HAVE_SYSLOG)
++  if ((LogFile = socket(AF_UNIX, SOCK_DGRAM, 0)) != -1) {
++
++    /*
++     * Send "found" message to the "/dev/log" path
++     */
++    SyslogAddr.sun_family = AF_UNIX;
++    (void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
++                sizeof(SyslogAddr.sun_path) - 1);
++    SyslogAddr.sun_path[sizeof(SyslogAddr.sun_path) - 1] = '\0';
++    sendto(LogFile, buf, len, 0, (struct sockaddr *)&SyslogAddr,
++         sizeof(SyslogAddr));
++  }
++#endif
++
++#ifdef _POSIX_SOURCE
++  { /* Make sure the default handler is associated with SIGABRT */
++    struct sigaction sa;
++    
++    memset(&sa, 0, sizeof(struct sigaction));
++    sigfillset(&sa.sa_mask);  /* Block all signals */
++    sa.sa_flags = 0;
++    sa.sa_handler = SIG_DFL;
++    sigaction(SIGABRT, &sa, NULL);
++    (void)kill(getpid(), SIGABRT);
++  }
++#endif
++  _exit(127);
++}
++#endif
+diff -ruN gcc-3.3.1/gcc/loop.c gcc-3.3.1.pp/gcc/loop.c
+--- gcc-3.3.1/gcc/loop.c       2003-07-11 06:47:05.000000000 +0000
++++ gcc-3.3.1.pp/gcc/loop.c    2003-09-05 11:58:59.000000000 +0000
+@@ -6516,6 +6516,14 @@
+   if (GET_CODE (*mult_val) == USE)
+     *mult_val = XEXP (*mult_val, 0);
++#ifndef FRAME_GROWS_DOWNWARD
++  if (flag_propolice_protection
++      && GET_CODE (*add_val) == PLUS
++      && (XEXP (*add_val, 0) == frame_pointer_rtx
++        || XEXP (*add_val, 1) == frame_pointer_rtx))
++    return 0;
++#endif
++
+   if (is_addr)
+     *pbenefit += address_cost (orig_x, addr_mode) - reg_address_cost;
+   else
+diff -ruN gcc-3.3.1/gcc/optabs.c gcc-3.3.1.pp/gcc/optabs.c
+--- gcc-3.3.1/gcc/optabs.c     2003-07-19 00:25:25.000000000 +0000
++++ gcc-3.3.1.pp/gcc/optabs.c  2003-09-05 11:58:59.000000000 +0000
+@@ -703,6 +703,26 @@
+   if (target)
+     target = protect_from_queue (target, 1);
++  if (flag_propolice_protection
++      && binoptab->code == PLUS
++      && op0 == virtual_stack_vars_rtx
++      && GET_CODE(op1) == CONST_INT)
++    {
++      int icode = (int) binoptab->handlers[(int) mode].insn_code;
++      if (target)
++      temp = target;
++      else
++      temp = gen_reg_rtx (mode);
++
++      if (! (*insn_data[icode].operand[0].predicate) (temp, mode)
++        || GET_CODE (temp) != REG)
++      temp = gen_reg_rtx (mode);
++
++      emit_insn (gen_rtx_SET (VOIDmode, temp,
++                            gen_rtx_PLUS (GET_MODE (op0), op0, op1)));
++      return temp;
++    }
++
+   if (flag_force_mem)
+     {
+       op0 = force_not_mem (op0);
+diff -ruN gcc-3.3.1/gcc/optabs.c.orig gcc-3.3.1.pp/gcc/optabs.c.orig
+--- gcc-3.3.1/gcc/optabs.c.orig        1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/optabs.c.orig     2003-07-19 00:25:25.000000000 +0000
+@@ -0,0 +1,5511 @@
++/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
++   Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
++   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify it under
++the terms of the GNU General Public License as published by the Free
++Software Foundation; either version 2, or (at your option) any later
++version.
++
++GCC is distributed in the hope that it will be useful, but WITHOUT ANY
++WARRANTY; without even the implied warranty of MERCHANTABILITY or
++FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING.  If not, write to the Free
++Software Foundation, 59 Temple Place - Suite 330, Boston, MA
++02111-1307, USA.  */
++
++
++#include "config.h"
++#include "system.h"
++#include "toplev.h"
++
++/* Include insn-config.h before expr.h so that HAVE_conditional_move
++   is properly defined.  */
++#include "insn-config.h"
++#include "rtl.h"
++#include "tree.h"
++#include "tm_p.h"
++#include "flags.h"
++#include "function.h"
++#include "except.h"
++#include "expr.h"
++#include "optabs.h"
++#include "libfuncs.h"
++#include "recog.h"
++#include "reload.h"
++#include "ggc.h"
++#include "real.h"
++#include "basic-block.h"
++
++/* Each optab contains info on how this target machine
++   can perform a particular operation
++   for all sizes and kinds of operands.
++
++   The operation to be performed is often specified
++   by passing one of these optabs as an argument.
++
++   See expr.h for documentation of these optabs.  */
++
++optab optab_table[OTI_MAX];
++
++rtx libfunc_table[LTI_MAX];
++
++/* Tables of patterns for extending one integer mode to another.  */
++enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
++
++/* Tables of patterns for converting between fixed and floating point.  */
++enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
++enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
++enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
++
++/* Contains the optab used for each rtx code.  */
++optab code_to_optab[NUM_RTX_CODE + 1];
++
++/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
++   gives the gen_function to make a branch to test that condition.  */
++
++rtxfun bcc_gen_fctn[NUM_RTX_CODE];
++
++/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
++   gives the insn code to make a store-condition insn
++   to test that condition.  */
++
++enum insn_code setcc_gen_code[NUM_RTX_CODE];
++
++#ifdef HAVE_conditional_move
++/* Indexed by the machine mode, gives the insn code to make a conditional
++   move insn.  This is not indexed by the rtx-code like bcc_gen_fctn and
++   setcc_gen_code to cut down on the number of named patterns.  Consider a day
++   when a lot more rtx codes are conditional (eg: for the ARM).  */
++
++enum insn_code movcc_gen_code[NUM_MACHINE_MODES];
++#endif
++
++/* The insn generating function can not take an rtx_code argument.
++   TRAP_RTX is used as an rtx argument.  Its code is replaced with
++   the code to be used in the trap insn and all other fields are ignored.  */
++static GTY(()) rtx trap_rtx;
++
++static int add_equal_note     PARAMS ((rtx, rtx, enum rtx_code, rtx, rtx));
++static rtx widen_operand      PARAMS ((rtx, enum machine_mode,
++                                     enum machine_mode, int, int));
++static int expand_cmplxdiv_straight PARAMS ((rtx, rtx, rtx, rtx,
++                                         rtx, rtx, enum machine_mode,
++                                         int, enum optab_methods,
++                                         enum mode_class, optab));
++static int expand_cmplxdiv_wide PARAMS ((rtx, rtx, rtx, rtx,
++                                     rtx, rtx, enum machine_mode,
++                                     int, enum optab_methods,
++                                     enum mode_class, optab));
++static void prepare_cmp_insn PARAMS ((rtx *, rtx *, enum rtx_code *, rtx,
++                                    enum machine_mode *, int *,
++                                    enum can_compare_purpose));
++static enum insn_code can_fix_p       PARAMS ((enum machine_mode, enum machine_mode,
++                                     int, int *));
++static enum insn_code can_float_p PARAMS ((enum machine_mode,
++                                         enum machine_mode,
++                                         int));
++static rtx ftruncify  PARAMS ((rtx));
++static optab new_optab        PARAMS ((void));
++static inline optab init_optab        PARAMS ((enum rtx_code));
++static inline optab init_optabv       PARAMS ((enum rtx_code));
++static void init_libfuncs PARAMS ((optab, int, int, const char *, int));
++static void init_integral_libfuncs PARAMS ((optab, const char *, int));
++static void init_floating_libfuncs PARAMS ((optab, const char *, int));
++static void emit_cmp_and_jump_insn_1 PARAMS ((rtx, rtx, enum machine_mode,
++                                          enum rtx_code, int, rtx));
++static void prepare_float_lib_cmp PARAMS ((rtx *, rtx *, enum rtx_code *,
++                                       enum machine_mode *, int *));
++static rtx expand_vector_binop PARAMS ((enum machine_mode, optab,
++                                      rtx, rtx, rtx, int,
++                                      enum optab_methods));
++static rtx expand_vector_unop PARAMS ((enum machine_mode, optab, rtx, rtx,
++                                     int));
++
++#ifndef HAVE_conditional_trap
++#define HAVE_conditional_trap 0
++#define gen_conditional_trap(a,b) (abort (), NULL_RTX)
++#endif
++\f
++/* Add a REG_EQUAL note to the last insn in INSNS.  TARGET is being set to
++   the result of operation CODE applied to OP0 (and OP1 if it is a binary
++   operation).
++
++   If the last insn does not set TARGET, don't do anything, but return 1.
++
++   If a previous insn sets TARGET and TARGET is one of OP0 or OP1,
++   don't add the REG_EQUAL note but return 0.  Our caller can then try
++   again, ensuring that TARGET is not one of the operands.  */
++
++static int
++add_equal_note (insns, target, code, op0, op1)
++     rtx insns;
++     rtx target;
++     enum rtx_code code;
++     rtx op0, op1;
++{
++  rtx last_insn, insn, set;
++  rtx note;
++
++  if (! insns
++      || ! INSN_P (insns)
++      || NEXT_INSN (insns) == NULL_RTX)
++    abort ();
++
++  if (GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
++      && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
++    return 1;
++
++  if (GET_CODE (target) == ZERO_EXTRACT)
++    return 1;
++
++  for (last_insn = insns;
++       NEXT_INSN (last_insn) != NULL_RTX;
++       last_insn = NEXT_INSN (last_insn))
++    ;
++
++  set = single_set (last_insn);
++  if (set == NULL_RTX)
++    return 1;
++
++  if (! rtx_equal_p (SET_DEST (set), target)
++      /* For a STRICT_LOW_PART, the REG_NOTE applies to what is inside the
++       SUBREG.  */
++      && (GET_CODE (SET_DEST (set)) != STRICT_LOW_PART
++        || ! rtx_equal_p (SUBREG_REG (XEXP (SET_DEST (set), 0)),
++                          target)))
++    return 1;
++
++  /* If TARGET is in OP0 or OP1, check if anything in SEQ sets TARGET
++     besides the last insn.  */
++  if (reg_overlap_mentioned_p (target, op0)
++      || (op1 && reg_overlap_mentioned_p (target, op1)))
++    {
++      insn = PREV_INSN (last_insn);
++      while (insn != NULL_RTX)
++      {
++        if (reg_set_p (target, insn))
++          return 0;
++
++        insn = PREV_INSN (insn);
++      }
++    }
++
++  if (GET_RTX_CLASS (code) == '1')
++    note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
++  else
++    note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
++
++  set_unique_reg_note (last_insn, REG_EQUAL, note);
++
++  return 1;
++}
++\f
++/* Widen OP to MODE and return the rtx for the widened operand.  UNSIGNEDP
++   says whether OP is signed or unsigned.  NO_EXTEND is nonzero if we need
++   not actually do a sign-extend or zero-extend, but can leave the 
++   higher-order bits of the result rtx undefined, for example, in the case
++   of logical operations, but not right shifts.  */
++
++static rtx
++widen_operand (op, mode, oldmode, unsignedp, no_extend)
++     rtx op;
++     enum machine_mode mode, oldmode;
++     int unsignedp;
++     int no_extend;
++{
++  rtx result;
++
++  /* If we don't have to extend and this is a constant, return it.  */
++  if (no_extend && GET_MODE (op) == VOIDmode)
++    return op;
++
++  /* If we must extend do so.  If OP is a SUBREG for a promoted object, also
++     extend since it will be more efficient to do so unless the signedness of
++     a promoted object differs from our extension.  */
++  if (! no_extend
++      || (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op)
++        && SUBREG_PROMOTED_UNSIGNED_P (op) == unsignedp))
++    return convert_modes (mode, oldmode, op, unsignedp);
++
++  /* If MODE is no wider than a single word, we return a paradoxical
++     SUBREG.  */
++  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
++    return gen_rtx_SUBREG (mode, force_reg (GET_MODE (op), op), 0);
++
++  /* Otherwise, get an object of MODE, clobber it, and set the low-order
++     part to OP.  */
++
++  result = gen_reg_rtx (mode);
++  emit_insn (gen_rtx_CLOBBER (VOIDmode, result));
++  emit_move_insn (gen_lowpart (GET_MODE (op), result), op);
++  return result;
++}
++\f
++/* Generate code to perform a straightforward complex divide.  */
++
++static int
++expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
++                        unsignedp, methods, class, binoptab)
++     rtx real0, real1, imag0, imag1, realr, imagr;
++     enum machine_mode submode;
++     int unsignedp;
++     enum optab_methods methods;
++     enum mode_class class;
++     optab binoptab;
++{
++  rtx divisor;
++  rtx real_t, imag_t;
++  rtx temp1, temp2;
++  rtx res;
++  optab this_add_optab = add_optab;
++  optab this_sub_optab = sub_optab;
++  optab this_neg_optab = neg_optab;
++  optab this_mul_optab = smul_optab;
++            
++  if (binoptab == sdivv_optab)
++    {
++      this_add_optab = addv_optab;
++      this_sub_optab = subv_optab;
++      this_neg_optab = negv_optab;
++      this_mul_optab = smulv_optab;
++    }
++
++  /* Don't fetch these from memory more than once.  */
++  real0 = force_reg (submode, real0);
++  real1 = force_reg (submode, real1);
++
++  if (imag0 != 0)
++    imag0 = force_reg (submode, imag0);
++
++  imag1 = force_reg (submode, imag1);
++
++  /* Divisor: c*c + d*d.  */
++  temp1 = expand_binop (submode, this_mul_optab, real1, real1,
++                      NULL_RTX, unsignedp, methods);
++
++  temp2 = expand_binop (submode, this_mul_optab, imag1, imag1,
++                      NULL_RTX, unsignedp, methods);
++
++  if (temp1 == 0 || temp2 == 0)
++    return 0;
++
++  divisor = expand_binop (submode, this_add_optab, temp1, temp2,
++                        NULL_RTX, unsignedp, methods);
++  if (divisor == 0)
++    return 0;
++
++  if (imag0 == 0)
++    {
++      /* Mathematically, ((a)(c-id))/divisor.  */
++      /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)).  */
++
++      /* Calculate the dividend.  */
++      real_t = expand_binop (submode, this_mul_optab, real0, real1,
++                           NULL_RTX, unsignedp, methods);
++                
++      imag_t = expand_binop (submode, this_mul_optab, real0, imag1,
++                           NULL_RTX, unsignedp, methods);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++
++      imag_t = expand_unop (submode, this_neg_optab, imag_t,
++                          NULL_RTX, unsignedp);
++    }
++  else
++    {
++      /* Mathematically, ((a+ib)(c-id))/divider.  */
++      /* Calculate the dividend.  */
++      temp1 = expand_binop (submode, this_mul_optab, real0, real1,
++                          NULL_RTX, unsignedp, methods);
++
++      temp2 = expand_binop (submode, this_mul_optab, imag0, imag1,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0 || temp2 == 0)
++      return 0;
++
++      real_t = expand_binop (submode, this_add_optab, temp1, temp2,
++                           NULL_RTX, unsignedp, methods);
++                
++      temp1 = expand_binop (submode, this_mul_optab, imag0, real1,
++                          NULL_RTX, unsignedp, methods);
++
++      temp2 = expand_binop (submode, this_mul_optab, real0, imag1,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0 || temp2 == 0)
++      return 0;
++
++      imag_t = expand_binop (submode, this_sub_optab, temp1, temp2,
++                           NULL_RTX, unsignedp, methods);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++    }
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, real_t, divisor,
++                      realr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       real_t, divisor, realr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != realr)
++    emit_move_insn (realr, res);
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, imag_t, divisor,
++                      imagr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       imag_t, divisor, imagr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != imagr)
++    emit_move_insn (imagr, res);
++
++  return 1;
++}
++\f
++/* Generate code to perform a wide-input-range-acceptable complex divide.  */
++
++static int
++expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
++                    unsignedp, methods, class, binoptab)
++     rtx real0, real1, imag0, imag1, realr, imagr;
++     enum machine_mode submode;
++     int unsignedp;
++     enum optab_methods methods;
++     enum mode_class class;
++     optab binoptab;
++{
++  rtx ratio, divisor;
++  rtx real_t, imag_t;
++  rtx temp1, temp2, lab1, lab2;
++  enum machine_mode mode;
++  rtx res;
++  optab this_add_optab = add_optab;
++  optab this_sub_optab = sub_optab;
++  optab this_neg_optab = neg_optab;
++  optab this_mul_optab = smul_optab;
++
++  if (binoptab == sdivv_optab)
++    {
++      this_add_optab = addv_optab;
++      this_sub_optab = subv_optab;
++      this_neg_optab = negv_optab;
++      this_mul_optab = smulv_optab;
++    }
++            
++  /* Don't fetch these from memory more than once.  */
++  real0 = force_reg (submode, real0);
++  real1 = force_reg (submode, real1);
++
++  if (imag0 != 0)
++    imag0 = force_reg (submode, imag0);
++
++  imag1 = force_reg (submode, imag1);
++
++  /* XXX What's an "unsigned" complex number?  */
++  if (unsignedp)
++    {
++      temp1 = real1;
++      temp2 = imag1;
++    }
++  else
++    {
++      temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1);
++      temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1);
++    }
++
++  if (temp1 == 0 || temp2 == 0)
++    return 0;
++
++  mode = GET_MODE (temp1);
++  lab1 = gen_label_rtx ();
++  emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX,
++                         mode, unsignedp, lab1);
++
++  /* |c| >= |d|; use ratio d/c to scale dividend and divisor.  */
++
++  if (class == MODE_COMPLEX_FLOAT)
++    ratio = expand_binop (submode, binoptab, imag1, real1,
++                        NULL_RTX, unsignedp, methods);
++  else
++    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                         imag1, real1, NULL_RTX, unsignedp);
++
++  if (ratio == 0)
++    return 0;
++
++  /* Calculate divisor.  */
++
++  temp1 = expand_binop (submode, this_mul_optab, imag1, ratio,
++                      NULL_RTX, unsignedp, methods);
++
++  if (temp1 == 0)
++    return 0;
++
++  divisor = expand_binop (submode, this_add_optab, temp1, real1,
++                        NULL_RTX, unsignedp, methods);
++
++  if (divisor == 0)
++    return 0;
++
++  /* Calculate dividend.  */
++
++  if (imag0 == 0)
++    {
++      real_t = real0;
++
++      /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)).  */
++
++      imag_t = expand_binop (submode, this_mul_optab, real0, ratio,
++                           NULL_RTX, unsignedp, methods);
++
++      if (imag_t == 0)
++      return 0;
++
++      imag_t = expand_unop (submode, this_neg_optab, imag_t,
++                          NULL_RTX, unsignedp);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++    }
++  else
++    {
++      /* Compute (a+ib)/(c+id) as
++       (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)).  */
++
++      temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0)
++      return 0;
++
++      real_t = expand_binop (submode, this_add_optab, temp1, real0,
++                           NULL_RTX, unsignedp, methods);
++
++      temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0)
++      return 0;
++
++      imag_t = expand_binop (submode, this_sub_optab, imag0, temp1,
++                           NULL_RTX, unsignedp, methods);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++    }
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, real_t, divisor,
++                      realr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       real_t, divisor, realr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != realr)
++    emit_move_insn (realr, res);
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, imag_t, divisor,
++                      imagr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       imag_t, divisor, imagr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != imagr)
++    emit_move_insn (imagr, res);
++
++  lab2 = gen_label_rtx ();
++  emit_jump_insn (gen_jump (lab2));
++  emit_barrier ();
++
++  emit_label (lab1);
++
++  /* |d| > |c|; use ratio c/d to scale dividend and divisor.  */
++
++  if (class == MODE_COMPLEX_FLOAT)
++    ratio = expand_binop (submode, binoptab, real1, imag1,
++                        NULL_RTX, unsignedp, methods);
++  else
++    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                         real1, imag1, NULL_RTX, unsignedp);
++
++  if (ratio == 0)
++    return 0;
++
++  /* Calculate divisor.  */
++
++  temp1 = expand_binop (submode, this_mul_optab, real1, ratio,
++                      NULL_RTX, unsignedp, methods);
++
++  if (temp1 == 0)
++    return 0;
++
++  divisor = expand_binop (submode, this_add_optab, temp1, imag1,
++                        NULL_RTX, unsignedp, methods);
++
++  if (divisor == 0)
++    return 0;
++
++  /* Calculate dividend.  */
++
++  if (imag0 == 0)
++    {
++      /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d).  */
++
++      real_t = expand_binop (submode, this_mul_optab, real0, ratio,
++                           NULL_RTX, unsignedp, methods);
++
++      imag_t = expand_unop (submode, this_neg_optab, real0,
++                          NULL_RTX, unsignedp);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++    }
++  else
++    {
++      /* Compute (a+ib)/(c+id) as
++       (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d).  */
++
++      temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0)
++      return 0;
++
++      real_t = expand_binop (submode, this_add_optab, temp1, imag0,
++                           NULL_RTX, unsignedp, methods);
++
++      temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
++                          NULL_RTX, unsignedp, methods);
++
++      if (temp1 == 0)
++      return 0;
++
++      imag_t = expand_binop (submode, this_sub_optab, temp1, real0,
++                           NULL_RTX, unsignedp, methods);
++
++      if (real_t == 0 || imag_t == 0)
++      return 0;
++    }
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, real_t, divisor,
++                      realr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       real_t, divisor, realr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != realr)
++    emit_move_insn (realr, res);
++
++  if (class == MODE_COMPLEX_FLOAT)
++    res = expand_binop (submode, binoptab, imag_t, divisor,
++                      imagr, unsignedp, methods);
++  else
++    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                       imag_t, divisor, imagr, unsignedp);
++
++  if (res == 0)
++    return 0;
++
++  if (res != imagr)
++    emit_move_insn (imagr, res);
++
++  emit_label (lab2);
++
++  return 1;
++}
++\f
++/* Wrapper around expand_binop which takes an rtx code to specify
++   the operation to perform, not an optab pointer.  All other
++   arguments are the same.  */
++rtx
++expand_simple_binop (mode, code, op0, op1, target, unsignedp, methods)
++     enum machine_mode mode;
++     enum rtx_code code;
++     rtx op0, op1;
++     rtx target;
++     int unsignedp;
++     enum optab_methods methods;
++{
++  optab binop = code_to_optab[(int) code];
++  if (binop == 0)
++    abort ();
++
++  return expand_binop (mode, binop, op0, op1, target, unsignedp, methods);
++}
++
++/* Generate code to perform an operation specified by BINOPTAB
++   on operands OP0 and OP1, with result having machine-mode MODE.
++
++   UNSIGNEDP is for the case where we have to widen the operands
++   to perform the operation.  It says to use zero-extension.
++
++   If TARGET is nonzero, the value
++   is generated there, if it is convenient to do so.
++   In all cases an rtx is returned for the locus of the value;
++   this may or may not be TARGET.  */
++
++rtx
++expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
++     enum machine_mode mode;
++     optab binoptab;
++     rtx op0, op1;
++     rtx target;
++     int unsignedp;
++     enum optab_methods methods;
++{
++  enum optab_methods next_methods
++    = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
++       ? OPTAB_WIDEN : methods);
++  enum mode_class class;
++  enum machine_mode wider_mode;
++  rtx temp;
++  int commutative_op = 0;
++  int shift_op = (binoptab->code == ASHIFT
++                || binoptab->code == ASHIFTRT
++                || binoptab->code == LSHIFTRT
++                || binoptab->code == ROTATE
++                || binoptab->code == ROTATERT);
++  rtx entry_last = get_last_insn ();
++  rtx last;
++
++  class = GET_MODE_CLASS (mode);
++
++  op0 = protect_from_queue (op0, 0);
++  op1 = protect_from_queue (op1, 0);
++  if (target)
++    target = protect_from_queue (target, 1);
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++      op1 = force_not_mem (op1);
++    }
++
++  /* If subtracting an integer constant, convert this into an addition of
++     the negated constant.  */
++
++  if (binoptab == sub_optab && GET_CODE (op1) == CONST_INT)
++    {
++      op1 = negate_rtx (mode, op1);
++      binoptab = add_optab;
++    }
++
++  /* If we are inside an appropriately-short loop and one operand is an
++     expensive constant, force it into a register.  */
++  if (CONSTANT_P (op0) && preserve_subexpressions_p ()
++      && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1))
++    op0 = force_reg (mode, op0);
++
++  if (CONSTANT_P (op1) && preserve_subexpressions_p ()
++      && ! shift_op && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
++    op1 = force_reg (mode, op1);
++
++  /* Record where to delete back to if we backtrack.  */
++  last = get_last_insn ();
++
++  /* If operation is commutative,
++     try to make the first operand a register.
++     Even better, try to make it the same as the target.
++     Also try to make the last operand a constant.  */
++  if (GET_RTX_CLASS (binoptab->code) == 'c'
++      || binoptab == smul_widen_optab
++      || binoptab == umul_widen_optab
++      || binoptab == smul_highpart_optab
++      || binoptab == umul_highpart_optab)
++    {
++      commutative_op = 1;
++
++      if (((target == 0 || GET_CODE (target) == REG)
++         ? ((GET_CODE (op1) == REG
++             && GET_CODE (op0) != REG)
++            || target == op1)
++         : rtx_equal_p (op1, target))
++        || GET_CODE (op0) == CONST_INT)
++      {
++        temp = op1;
++        op1 = op0;
++        op0 = temp;
++      }
++    }
++
++  /* If we can do it with a three-operand insn, do so.  */
++
++  if (methods != OPTAB_MUST_WIDEN
++      && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    {
++      int icode = (int) binoptab->handlers[(int) mode].insn_code;
++      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
++      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
++      rtx pat;
++      rtx xop0 = op0, xop1 = op1;
++
++      if (target)
++      temp = target;
++      else
++      temp = gen_reg_rtx (mode);
++
++      /* If it is a commutative operator and the modes would match
++       if we would swap the operands, we can save the conversions.  */
++      if (commutative_op)
++      {
++        if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
++            && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0)
++          {
++            rtx tmp;
++
++            tmp = op0; op0 = op1; op1 = tmp;
++            tmp = xop0; xop0 = xop1; xop1 = tmp;
++          }
++      }
++
++      /* In case the insn wants input operands in modes different from
++       those of the actual operands, convert the operands.  It would
++       seem that we don't need to convert CONST_INTs, but we do, so
++       that they're properly zero-extended, sign-extended or truncated
++       for their mode.  */
++
++      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
++      xop0 = convert_modes (mode0,
++                            GET_MODE (op0) != VOIDmode
++                            ? GET_MODE (op0)
++                            : mode,
++                            xop0, unsignedp);
++
++      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
++      xop1 = convert_modes (mode1,
++                            GET_MODE (op1) != VOIDmode
++                            ? GET_MODE (op1)
++                            : mode,
++                            xop1, unsignedp);
++
++      /* Now, if insn's predicates don't allow our operands, put them into
++       pseudo regs.  */
++
++      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)
++        && mode0 != VOIDmode)
++      xop0 = copy_to_mode_reg (mode0, xop0);
++
++      if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
++        && mode1 != VOIDmode)
++      xop1 = copy_to_mode_reg (mode1, xop1);
++
++      if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
++      temp = gen_reg_rtx (mode);
++
++      pat = GEN_FCN (icode) (temp, xop0, xop1);
++      if (pat)
++      {
++        /* If PAT is composed of more than one insn, try to add an appropriate
++           REG_EQUAL note to it.  If we can't because TEMP conflicts with an
++           operand, call ourselves again, this time without a target.  */
++        if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
++            && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
++          {
++            delete_insns_since (last);
++            return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
++                                 unsignedp, methods);
++          }
++
++        emit_insn (pat);
++        return temp;
++      }
++      else
++      delete_insns_since (last);
++    }
++
++  /* If this is a multiply, see if we can do a widening operation that
++     takes operands of this mode and makes a wider mode.  */
++
++  if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode
++      && (((unsignedp ? umul_widen_optab : smul_widen_optab)
++         ->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code)
++        != CODE_FOR_nothing))
++    {
++      temp = expand_binop (GET_MODE_WIDER_MODE (mode),
++                         unsignedp ? umul_widen_optab : smul_widen_optab,
++                         op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
++
++      if (temp != 0)
++      {
++        if (GET_MODE_CLASS (mode) == MODE_INT)
++          return gen_lowpart (mode, temp);
++        else
++          return convert_to_mode (mode, temp, unsignedp);
++      }
++    }
++
++  /* Look for a wider mode of the same class for which we think we
++     can open-code the operation.  Check for a widening multiply at the
++     wider mode as well.  */
++
++  if ((class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
++      && methods != OPTAB_DIRECT && methods != OPTAB_LIB)
++    for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++      if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
++          || (binoptab == smul_optab
++              && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode
++              && (((unsignedp ? umul_widen_optab : smul_widen_optab)
++                   ->handlers[(int) GET_MODE_WIDER_MODE (wider_mode)].insn_code)
++                  != CODE_FOR_nothing)))
++        {
++          rtx xop0 = op0, xop1 = op1;
++          int no_extend = 0;
++
++          /* For certain integer operations, we need not actually extend
++             the narrow operands, as long as we will truncate
++             the results to the same narrowness.  */
++
++          if ((binoptab == ior_optab || binoptab == and_optab
++               || binoptab == xor_optab
++               || binoptab == add_optab || binoptab == sub_optab
++               || binoptab == smul_optab || binoptab == ashl_optab)
++              && class == MODE_INT)
++            no_extend = 1;
++
++          xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend);
++
++          /* The second operand of a shift must always be extended.  */
++          xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
++                                no_extend && binoptab != ashl_optab);
++
++          temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
++                               unsignedp, OPTAB_DIRECT);
++          if (temp)
++            {
++              if (class != MODE_INT)
++                {
++                  if (target == 0)
++                    target = gen_reg_rtx (mode);
++                  convert_move (target, temp, 0);
++                  return target;
++                }
++              else
++                return gen_lowpart (mode, temp);
++            }
++          else
++            delete_insns_since (last);
++        }
++      }
++
++  /* These can be done a word at a time.  */
++  if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
++      && class == MODE_INT
++      && GET_MODE_SIZE (mode) > UNITS_PER_WORD
++      && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
++    {
++      int i;
++      rtx insns;
++      rtx equiv_value;
++
++      /* If TARGET is the same as one of the operands, the REG_EQUAL note
++       won't be accurate, so use a new target.  */
++      if (target == 0 || target == op0 || target == op1)
++      target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      /* Do the actual arithmetic.  */
++      for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
++      {
++        rtx target_piece = operand_subword (target, i, 1, mode);
++        rtx x = expand_binop (word_mode, binoptab,
++                              operand_subword_force (op0, i, mode),
++                              operand_subword_force (op1, i, mode),
++                              target_piece, unsignedp, next_methods);
++
++        if (x == 0)
++          break;
++
++        if (target_piece != x)
++          emit_move_insn (target_piece, x);
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
++      {
++        if (binoptab->code != UNKNOWN)
++          equiv_value
++            = gen_rtx_fmt_ee (binoptab->code, mode,
++                              copy_rtx (op0), copy_rtx (op1));
++        else
++          equiv_value = 0;
++
++        emit_no_conflict_block (insns, target, op0, op1, equiv_value);
++        return target;
++      }
++    }
++
++  /* Synthesize double word shifts from single word shifts.  */
++  if ((binoptab == lshr_optab || binoptab == ashl_optab
++       || binoptab == ashr_optab)
++      && class == MODE_INT
++      && GET_CODE (op1) == CONST_INT
++      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
++      && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
++      && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
++      && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
++    {
++      rtx insns, inter, equiv_value;
++      rtx into_target, outof_target;
++      rtx into_input, outof_input;
++      int shift_count, left_shift, outof_word;
++
++      /* If TARGET is the same as one of the operands, the REG_EQUAL note
++       won't be accurate, so use a new target.  */
++      if (target == 0 || target == op0 || target == op1)
++      target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      shift_count = INTVAL (op1);
++
++      /* OUTOF_* is the word we are shifting bits away from, and
++       INTO_* is the word that we are shifting bits towards, thus
++       they differ depending on the direction of the shift and
++       WORDS_BIG_ENDIAN.  */
++
++      left_shift = binoptab == ashl_optab;
++      outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
++
++      outof_target = operand_subword (target, outof_word, 1, mode);
++      into_target = operand_subword (target, 1 - outof_word, 1, mode);
++
++      outof_input = operand_subword_force (op0, outof_word, mode);
++      into_input = operand_subword_force (op0, 1 - outof_word, mode);
++
++      if (shift_count >= BITS_PER_WORD)
++      {
++        inter = expand_binop (word_mode, binoptab,
++                             outof_input,
++                             GEN_INT (shift_count - BITS_PER_WORD),
++                             into_target, unsignedp, next_methods);
++
++        if (inter != 0 && inter != into_target)
++          emit_move_insn (into_target, inter);
++
++        /* For a signed right shift, we must fill the word we are shifting
++           out of with copies of the sign bit.  Otherwise it is zeroed.  */
++        if (inter != 0 && binoptab != ashr_optab)
++          inter = CONST0_RTX (word_mode);
++        else if (inter != 0)
++          inter = expand_binop (word_mode, binoptab,
++                                outof_input,
++                                GEN_INT (BITS_PER_WORD - 1),
++                                outof_target, unsignedp, next_methods);
++
++        if (inter != 0 && inter != outof_target)
++          emit_move_insn (outof_target, inter);
++      }
++      else
++      {
++        rtx carries;
++        optab reverse_unsigned_shift, unsigned_shift;
++
++        /* For a shift of less then BITS_PER_WORD, to compute the carry,
++           we must do a logical shift in the opposite direction of the
++           desired shift.  */
++
++        reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
++
++        /* For a shift of less than BITS_PER_WORD, to compute the word
++           shifted towards, we need to unsigned shift the orig value of
++           that word.  */
++
++        unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
++
++        carries = expand_binop (word_mode, reverse_unsigned_shift,
++                                outof_input,
++                                GEN_INT (BITS_PER_WORD - shift_count),
++                                0, unsignedp, next_methods);
++
++        if (carries == 0)
++          inter = 0;
++        else
++          inter = expand_binop (word_mode, unsigned_shift, into_input,
++                                op1, 0, unsignedp, next_methods);
++
++        if (inter != 0)
++          inter = expand_binop (word_mode, ior_optab, carries, inter,
++                                into_target, unsignedp, next_methods);
++
++        if (inter != 0 && inter != into_target)
++          emit_move_insn (into_target, inter);
++
++        if (inter != 0)
++          inter = expand_binop (word_mode, binoptab, outof_input,
++                                op1, outof_target, unsignedp, next_methods);
++        
++        if (inter != 0 && inter != outof_target)
++          emit_move_insn (outof_target, inter);
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      if (inter != 0)
++      {
++        if (binoptab->code != UNKNOWN)
++          equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
++        else
++          equiv_value = 0;
++
++        emit_no_conflict_block (insns, target, op0, op1, equiv_value);
++        return target;
++      }
++    }
++
++  /* Synthesize double word rotates from single word shifts.  */
++  if ((binoptab == rotl_optab || binoptab == rotr_optab)
++      && class == MODE_INT
++      && GET_CODE (op1) == CONST_INT
++      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
++      && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
++      && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
++    {
++      rtx insns, equiv_value;
++      rtx into_target, outof_target;
++      rtx into_input, outof_input;
++      rtx inter;
++      int shift_count, left_shift, outof_word;
++
++      /* If TARGET is the same as one of the operands, the REG_EQUAL note
++       won't be accurate, so use a new target.  */
++      if (target == 0 || target == op0 || target == op1)
++      target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      shift_count = INTVAL (op1);
++
++      /* OUTOF_* is the word we are shifting bits away from, and
++       INTO_* is the word that we are shifting bits towards, thus
++       they differ depending on the direction of the shift and
++       WORDS_BIG_ENDIAN.  */
++
++      left_shift = (binoptab == rotl_optab);
++      outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
++
++      outof_target = operand_subword (target, outof_word, 1, mode);
++      into_target = operand_subword (target, 1 - outof_word, 1, mode);
++
++      outof_input = operand_subword_force (op0, outof_word, mode);
++      into_input = operand_subword_force (op0, 1 - outof_word, mode);
++
++      if (shift_count == BITS_PER_WORD)
++      {
++        /* This is just a word swap.  */
++        emit_move_insn (outof_target, into_input);
++        emit_move_insn (into_target, outof_input);
++        inter = const0_rtx;
++      }
++      else
++      {
++        rtx into_temp1, into_temp2, outof_temp1, outof_temp2;
++        rtx first_shift_count, second_shift_count;
++        optab reverse_unsigned_shift, unsigned_shift;
++
++        reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
++                                  ? lshr_optab : ashl_optab);
++
++        unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
++                          ? ashl_optab : lshr_optab);
++
++        if (shift_count > BITS_PER_WORD)
++          {
++            first_shift_count = GEN_INT (shift_count - BITS_PER_WORD);
++            second_shift_count = GEN_INT (2 * BITS_PER_WORD - shift_count);
++          }
++        else
++          {
++            first_shift_count = GEN_INT (BITS_PER_WORD - shift_count);
++            second_shift_count = GEN_INT (shift_count);
++          }
++
++        into_temp1 = expand_binop (word_mode, unsigned_shift,
++                                   outof_input, first_shift_count,
++                                   NULL_RTX, unsignedp, next_methods);
++        into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
++                                   into_input, second_shift_count,
++                                   NULL_RTX, unsignedp, next_methods);
++
++        if (into_temp1 != 0 && into_temp2 != 0)
++          inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2,
++                                into_target, unsignedp, next_methods);
++        else
++          inter = 0;
++
++        if (inter != 0 && inter != into_target)
++          emit_move_insn (into_target, inter);
++
++        outof_temp1 = expand_binop (word_mode, unsigned_shift,
++                                    into_input, first_shift_count,
++                                    NULL_RTX, unsignedp, next_methods);
++        outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
++                                    outof_input, second_shift_count,
++                                    NULL_RTX, unsignedp, next_methods);
++
++        if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
++          inter = expand_binop (word_mode, ior_optab,
++                                outof_temp1, outof_temp2,
++                                outof_target, unsignedp, next_methods);
++
++        if (inter != 0 && inter != outof_target)
++          emit_move_insn (outof_target, inter);
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      if (inter != 0)
++      {
++        if (binoptab->code != UNKNOWN)
++          equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
++        else
++          equiv_value = 0;
++
++        /* We can't make this a no conflict block if this is a word swap,
++           because the word swap case fails if the input and output values
++           are in the same register.  */
++        if (shift_count != BITS_PER_WORD)
++          emit_no_conflict_block (insns, target, op0, op1, equiv_value);
++        else
++          emit_insn (insns);
++
++
++        return target;
++      }
++    }
++
++  /* These can be done a word at a time by propagating carries.  */
++  if ((binoptab == add_optab || binoptab == sub_optab)
++      && class == MODE_INT
++      && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD
++      && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
++    {
++      unsigned int i;
++      optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
++      const unsigned int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
++      rtx carry_in = NULL_RTX, carry_out = NULL_RTX;
++      rtx xop0, xop1, xtarget;
++
++      /* We can handle either a 1 or -1 value for the carry.  If STORE_FLAG
++       value is one of those, use it.  Otherwise, use 1 since it is the
++       one easiest to get.  */
++#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
++      int normalizep = STORE_FLAG_VALUE;
++#else
++      int normalizep = 1;
++#endif
++
++      /* Prepare the operands.  */
++      xop0 = force_reg (mode, op0);
++      xop1 = force_reg (mode, op1);
++
++      xtarget = gen_reg_rtx (mode);
++
++      if (target == 0 || GET_CODE (target) != REG)
++      target = xtarget;
++
++      /* Indicate for flow that the entire target reg is being set.  */
++      if (GET_CODE (target) == REG)
++      emit_insn (gen_rtx_CLOBBER (VOIDmode, xtarget));
++
++      /* Do the actual arithmetic.  */
++      for (i = 0; i < nwords; i++)
++      {
++        int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
++        rtx target_piece = operand_subword (xtarget, index, 1, mode);
++        rtx op0_piece = operand_subword_force (xop0, index, mode);
++        rtx op1_piece = operand_subword_force (xop1, index, mode);
++        rtx x;
++
++        /* Main add/subtract of the input operands.  */
++        x = expand_binop (word_mode, binoptab,
++                          op0_piece, op1_piece,
++                          target_piece, unsignedp, next_methods);
++        if (x == 0)
++          break;
++
++        if (i + 1 < nwords)
++          {
++            /* Store carry from main add/subtract.  */
++            carry_out = gen_reg_rtx (word_mode);
++            carry_out = emit_store_flag_force (carry_out,
++                                               (binoptab == add_optab
++                                                ? LT : GT),
++                                               x, op0_piece,
++                                               word_mode, 1, normalizep);
++          }
++
++        if (i > 0)
++          {
++            rtx newx;
++            
++            /* Add/subtract previous carry to main result.  */
++            newx = expand_binop (word_mode,
++                                 normalizep == 1 ? binoptab : otheroptab,
++                                 x, carry_in,
++                                 NULL_RTX, 1, next_methods);
++
++            if (i + 1 < nwords)
++              {
++                /* Get out carry from adding/subtracting carry in.  */
++                rtx carry_tmp = gen_reg_rtx (word_mode);
++                carry_tmp = emit_store_flag_force (carry_tmp,
++                                                   (binoptab == add_optab
++                                                    ? LT : GT),
++                                                   newx, x,
++                                                   word_mode, 1, normalizep);
++
++                /* Logical-ior the two poss. carry together.  */
++                carry_out = expand_binop (word_mode, ior_optab,
++                                          carry_out, carry_tmp,
++                                          carry_out, 0, next_methods);
++                if (carry_out == 0)
++                  break;
++              }
++            emit_move_insn (target_piece, newx);
++          }
++
++        carry_in = carry_out;
++      }       
++
++      if (i == GET_MODE_BITSIZE (mode) / (unsigned) BITS_PER_WORD)
++      {
++        if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++          {
++            rtx temp = emit_move_insn (target, xtarget);
++
++            set_unique_reg_note (temp,
++                                 REG_EQUAL,
++                                 gen_rtx_fmt_ee (binoptab->code, mode,
++                                                 copy_rtx (xop0),
++                                                 copy_rtx (xop1)));
++          }
++        else
++          target = xtarget;
++
++        return target;
++      }
++
++      else
++      delete_insns_since (last);
++    }
++
++  /* If we want to multiply two two-word values and have normal and widening
++     multiplies of single-word values, we can do this with three smaller
++     multiplications.  Note that we do not make a REG_NO_CONFLICT block here
++     because we are not operating on one word at a time. 
++
++     The multiplication proceeds as follows:
++                               _______________________
++                              [__op0_high_|__op0_low__]
++                               _______________________
++        *                     [__op1_high_|__op1_low__]
++        _______________________________________________
++                               _______________________
++    (1)                               [__op0_low__*__op1_low__]
++                   _______________________
++    (2a)          [__op0_low__*__op1_high_]
++                   _______________________
++    (2b)          [__op0_high_*__op1_low__]
++         _______________________
++    (3) [__op0_high_*__op1_high_]
++
++
++    This gives a 4-word result.  Since we are only interested in the
++    lower 2 words, partial result (3) and the upper words of (2a) and
++    (2b) don't need to be calculated.  Hence (2a) and (2b) can be
++    calculated using non-widening multiplication.
++
++    (1), however, needs to be calculated with an unsigned widening
++    multiplication.  If this operation is not directly supported we
++    try using a signed widening multiplication and adjust the result.
++    This adjustment works as follows:
++
++      If both operands are positive then no adjustment is needed.
++
++      If the operands have different signs, for example op0_low < 0 and
++      op1_low >= 0, the instruction treats the most significant bit of
++      op0_low as a sign bit instead of a bit with significance
++      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
++      with 2**BITS_PER_WORD - op0_low, and two's complements the
++      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
++      the result.
++
++      Similarly, if both operands are negative, we need to add
++      (op0_low + op1_low) * 2**BITS_PER_WORD.
++
++      We use a trick to adjust quickly.  We logically shift op0_low right
++      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
++      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
++      logical shift exists, we do an arithmetic right shift and subtract
++      the 0 or -1.  */
++
++  if (binoptab == smul_optab
++      && class == MODE_INT
++      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
++      && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
++      && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
++      && ((umul_widen_optab->handlers[(int) mode].insn_code
++         != CODE_FOR_nothing)
++        || (smul_widen_optab->handlers[(int) mode].insn_code
++            != CODE_FOR_nothing)))
++    {
++      int low = (WORDS_BIG_ENDIAN ? 1 : 0);
++      int high = (WORDS_BIG_ENDIAN ? 0 : 1);
++      rtx op0_high = operand_subword_force (op0, high, mode);
++      rtx op0_low = operand_subword_force (op0, low, mode);
++      rtx op1_high = operand_subword_force (op1, high, mode);
++      rtx op1_low = operand_subword_force (op1, low, mode);
++      rtx product = 0;
++      rtx op0_xhigh = NULL_RTX;
++      rtx op1_xhigh = NULL_RTX;
++
++      /* If the target is the same as one of the inputs, don't use it.  This
++       prevents problems with the REG_EQUAL note.  */
++      if (target == op0 || target == op1
++        || (target != 0 && GET_CODE (target) != REG))
++      target = 0;
++
++      /* Multiply the two lower words to get a double-word product.
++       If unsigned widening multiplication is available, use that;
++       otherwise use the signed form and compensate.  */
++
++      if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++      {
++        product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
++                                target, 1, OPTAB_DIRECT);
++
++        /* If we didn't succeed, delete everything we did so far.  */
++        if (product == 0)
++          delete_insns_since (last);
++        else
++          op0_xhigh = op0_high, op1_xhigh = op1_high;
++      }
++
++      if (product == 0
++        && smul_widen_optab->handlers[(int) mode].insn_code
++             != CODE_FOR_nothing)
++      {
++        rtx wordm1 = GEN_INT (BITS_PER_WORD - 1);
++        product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
++                                target, 1, OPTAB_DIRECT);
++        op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
++                                  NULL_RTX, 1, next_methods);
++        if (op0_xhigh)
++          op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
++                                    op0_xhigh, op0_xhigh, 0, next_methods);
++        else
++          {
++            op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
++                                      NULL_RTX, 0, next_methods);
++            if (op0_xhigh)
++              op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
++                                        op0_xhigh, op0_xhigh, 0,
++                                        next_methods);
++          }
++
++        op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
++                                  NULL_RTX, 1, next_methods);
++        if (op1_xhigh)
++          op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
++                                    op1_xhigh, op1_xhigh, 0, next_methods);
++        else
++          {
++            op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
++                                      NULL_RTX, 0, next_methods);
++            if (op1_xhigh)
++              op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
++                                        op1_xhigh, op1_xhigh, 0,
++                                        next_methods);
++          }
++      }
++
++      /* If we have been able to directly compute the product of the
++       low-order words of the operands and perform any required adjustments
++       of the operands, we proceed by trying two more multiplications
++       and then computing the appropriate sum.
++
++       We have checked above that the required addition is provided.
++       Full-word addition will normally always succeed, especially if
++       it is provided at all, so we don't worry about its failure.  The
++       multiplication may well fail, however, so we do handle that.  */
++
++      if (product && op0_xhigh && op1_xhigh)
++      {
++        rtx product_high = operand_subword (product, high, 1, mode);
++        rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
++                                 NULL_RTX, 0, OPTAB_DIRECT);
++
++        if (!REG_P (product_high))
++          product_high = force_reg (word_mode, product_high);
++
++        if (temp != 0)
++          temp = expand_binop (word_mode, add_optab, temp, product_high,
++                               product_high, 0, next_methods);
++
++        if (temp != 0 && temp != product_high)
++          emit_move_insn (product_high, temp);
++
++        if (temp != 0)
++          temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, 
++                               NULL_RTX, 0, OPTAB_DIRECT);
++
++        if (temp != 0)
++          temp = expand_binop (word_mode, add_optab, temp,
++                               product_high, product_high,
++                               0, next_methods);
++
++        if (temp != 0 && temp != product_high)
++          emit_move_insn (product_high, temp);
++
++        emit_move_insn (operand_subword (product, high, 1, mode), product_high);
++
++        if (temp != 0)
++          {
++            if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++              {
++                temp = emit_move_insn (product, product);
++                set_unique_reg_note (temp,
++                                     REG_EQUAL,
++                                     gen_rtx_fmt_ee (MULT, mode,
++                                                     copy_rtx (op0),
++                                                     copy_rtx (op1)));
++              }
++
++            return product;
++          }
++      }
++
++      /* If we get here, we couldn't do it for some reason even though we
++       originally thought we could.  Delete anything we've emitted in
++       trying to do it.  */
++
++      delete_insns_since (last);
++    }
++
++  /* Open-code the vector operations if we have no hardware support
++     for them.  */
++  if (class == MODE_VECTOR_INT || class == MODE_VECTOR_FLOAT)
++    return expand_vector_binop (mode, binoptab, op0, op1, target,
++                              unsignedp, methods);
++
++  /* We need to open-code the complex type operations: '+, -, * and /' */
++
++  /* At this point we allow operations between two similar complex
++     numbers, and also if one of the operands is not a complex number
++     but rather of MODE_FLOAT or MODE_INT. However, the caller
++     must make sure that the MODE of the non-complex operand matches
++     the SUBMODE of the complex operand.  */
++
++  if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
++    {
++      rtx real0 = 0, imag0 = 0;
++      rtx real1 = 0, imag1 = 0;
++      rtx realr, imagr, res;
++      rtx seq;
++      rtx equiv_value;
++      int ok = 0;
++
++      /* Find the correct mode for the real and imaginary parts */
++      enum machine_mode submode = GET_MODE_INNER(mode);
++
++      if (submode == BLKmode)
++      abort ();
++
++      if (! target)
++      target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      realr = gen_realpart (submode, target);
++      imagr = gen_imagpart (submode, target);
++
++      if (GET_MODE (op0) == mode)
++      {
++        real0 = gen_realpart (submode, op0);
++        imag0 = gen_imagpart (submode, op0);
++      }
++      else
++      real0 = op0;
++
++      if (GET_MODE (op1) == mode)
++      {
++        real1 = gen_realpart (submode, op1);
++        imag1 = gen_imagpart (submode, op1);
++      }
++      else
++      real1 = op1;
++
++      if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0))
++      abort ();
++
++      switch (binoptab->code)
++      {
++      case PLUS:
++        /* (a+ib) + (c+id) = (a+c) + i(b+d) */
++      case MINUS:
++        /* (a+ib) - (c+id) = (a-c) + i(b-d) */
++        res = expand_binop (submode, binoptab, real0, real1,
++                            realr, unsignedp, methods);
++
++        if (res == 0)
++          break;
++        else if (res != realr)
++          emit_move_insn (realr, res);
++
++        if (imag0 != 0 && imag1 != 0)
++          res = expand_binop (submode, binoptab, imag0, imag1,
++                              imagr, unsignedp, methods);
++        else if (imag0 != 0)
++          res = imag0;
++        else if (binoptab->code == MINUS)
++            res = expand_unop (submode,
++                                binoptab == subv_optab ? negv_optab : neg_optab,
++                                imag1, imagr, unsignedp);
++        else
++          res = imag1;
++
++        if (res == 0)
++          break;
++        else if (res != imagr)
++          emit_move_insn (imagr, res);
++
++        ok = 1;
++        break;
++
++      case MULT:
++        /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */
++
++        if (imag0 != 0 && imag1 != 0)
++          {
++            rtx temp1, temp2;
++
++            /* Don't fetch these from memory more than once.  */
++            real0 = force_reg (submode, real0);
++            real1 = force_reg (submode, real1);
++            imag0 = force_reg (submode, imag0);
++            imag1 = force_reg (submode, imag1);
++
++            temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX,
++                                  unsignedp, methods);
++
++            temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX,
++                                  unsignedp, methods);
++
++            if (temp1 == 0 || temp2 == 0)
++              break;
++
++            res = (expand_binop
++                     (submode,
++                      binoptab == smulv_optab ? subv_optab : sub_optab,
++                      temp1, temp2, realr, unsignedp, methods));
++
++            if (res == 0)
++              break;
++            else if (res != realr)
++              emit_move_insn (realr, res);
++
++            temp1 = expand_binop (submode, binoptab, real0, imag1,
++                                  NULL_RTX, unsignedp, methods);
++
++            temp2 = expand_binop (submode, binoptab, real1, imag0,
++                                  NULL_RTX, unsignedp, methods);
++
++            if (temp1 == 0 || temp2 == 0)
++              break;
++
++            res = (expand_binop
++                     (submode,
++                      binoptab == smulv_optab ? addv_optab : add_optab,
++                      temp1, temp2, imagr, unsignedp, methods));
++
++            if (res == 0)
++              break;
++            else if (res != imagr)
++              emit_move_insn (imagr, res);
++
++            ok = 1;
++          }
++        else
++          {
++            /* Don't fetch these from memory more than once.  */
++            real0 = force_reg (submode, real0);
++            real1 = force_reg (submode, real1);
++
++            res = expand_binop (submode, binoptab, real0, real1,
++                                realr, unsignedp, methods);
++            if (res == 0)
++              break;
++            else if (res != realr)
++              emit_move_insn (realr, res);
++
++            if (imag0 != 0)
++              res = expand_binop (submode, binoptab,
++                                  real1, imag0, imagr, unsignedp, methods);
++            else
++              res = expand_binop (submode, binoptab,
++                                  real0, imag1, imagr, unsignedp, methods);
++
++            if (res == 0)
++              break;
++            else if (res != imagr)
++              emit_move_insn (imagr, res);
++
++            ok = 1;
++          }
++        break;
++
++      case DIV:
++        /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */
++        
++        if (imag1 == 0)
++          {
++            /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
++
++            /* Don't fetch these from memory more than once.  */
++            real1 = force_reg (submode, real1);
++
++            /* Simply divide the real and imaginary parts by `c' */
++            if (class == MODE_COMPLEX_FLOAT)
++              res = expand_binop (submode, binoptab, real0, real1,
++                                  realr, unsignedp, methods);
++            else
++              res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                                   real0, real1, realr, unsignedp);
++
++            if (res == 0)
++              break;
++            else if (res != realr)
++              emit_move_insn (realr, res);
++
++            if (class == MODE_COMPLEX_FLOAT)
++              res = expand_binop (submode, binoptab, imag0, real1,
++                                  imagr, unsignedp, methods);
++            else
++              res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                                   imag0, real1, imagr, unsignedp);
++
++            if (res == 0)
++              break;
++            else if (res != imagr)
++              emit_move_insn (imagr, res);
++
++            ok = 1;
++          }
++        else
++          {
++            switch (flag_complex_divide_method)
++              {
++              case 0:
++                ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1,
++                                               realr, imagr, submode,
++                                               unsignedp, methods,
++                                               class, binoptab);
++                break;
++
++              case 1:
++                ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1,
++                                           realr, imagr, submode,
++                                           unsignedp, methods,
++                                           class, binoptab);
++                break;
++
++              default:
++                abort ();
++              }
++          }
++        break;
++        
++      default:
++        abort ();
++      }
++
++      seq = get_insns ();
++      end_sequence ();
++
++      if (ok)
++      {
++        if (binoptab->code != UNKNOWN)
++          equiv_value
++            = gen_rtx_fmt_ee (binoptab->code, mode,
++                              copy_rtx (op0), copy_rtx (op1));
++        else
++          equiv_value = 0;
++        
++        emit_no_conflict_block (seq, target, op0, op1, equiv_value);
++      
++        return target;
++      }
++    }
++
++  /* It can't be open-coded in this mode.
++     Use a library call if one is available and caller says that's ok.  */
++
++  if (binoptab->handlers[(int) mode].libfunc
++      && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN))
++    {
++      rtx insns;
++      rtx op1x = op1;
++      enum machine_mode op1_mode = mode;
++      rtx value;
++
++      start_sequence ();
++
++      if (shift_op)
++      {
++        op1_mode = word_mode;
++        /* Specify unsigned here,
++           since negative shift counts are meaningless.  */
++        op1x = convert_to_mode (word_mode, op1, 1);
++      }
++
++      if (GET_MODE (op0) != VOIDmode
++        && GET_MODE (op0) != mode)
++      op0 = convert_to_mode (mode, op0, unsignedp);
++
++      /* Pass 1 for NO_QUEUE so we don't lose any increments
++       if the libcall is cse'd or moved.  */
++      value = emit_library_call_value (binoptab->handlers[(int) mode].libfunc,
++                                     NULL_RTX, LCT_CONST, mode, 2,
++                                     op0, mode, op1x, op1_mode);
++
++      insns = get_insns ();
++      end_sequence ();
++
++      target = gen_reg_rtx (mode);
++      emit_libcall_block (insns, target, value,
++                        gen_rtx_fmt_ee (binoptab->code, mode, op0, op1));
++
++      return target;
++    }
++
++  delete_insns_since (last);
++
++  /* It can't be done in this mode.  Can we do it in a wider mode?  */
++
++  if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN
++       || methods == OPTAB_MUST_WIDEN))
++    {
++      /* Caller says, don't even try.  */
++      delete_insns_since (entry_last);
++      return 0;
++    }
++
++  /* Compute the value of METHODS to pass to recursive calls.
++     Don't allow widening to be tried recursively.  */
++
++  methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT);
++
++  /* Look for a wider mode of the same class for which it appears we can do
++     the operation.  */
++
++  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
++    {
++      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++        if ((binoptab->handlers[(int) wider_mode].insn_code
++             != CODE_FOR_nothing)
++            || (methods == OPTAB_LIB
++                && binoptab->handlers[(int) wider_mode].libfunc))
++          {
++            rtx xop0 = op0, xop1 = op1;
++            int no_extend = 0;
++
++            /* For certain integer operations, we need not actually extend
++               the narrow operands, as long as we will truncate
++               the results to the same narrowness.  */
++
++            if ((binoptab == ior_optab || binoptab == and_optab
++                 || binoptab == xor_optab
++                 || binoptab == add_optab || binoptab == sub_optab
++                 || binoptab == smul_optab || binoptab == ashl_optab)
++                && class == MODE_INT)
++              no_extend = 1;
++
++            xop0 = widen_operand (xop0, wider_mode, mode,
++                                  unsignedp, no_extend);
++
++            /* The second operand of a shift must always be extended.  */
++            xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
++                                  no_extend && binoptab != ashl_optab);
++
++            temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
++                                 unsignedp, methods);
++            if (temp)
++              {
++                if (class != MODE_INT)
++                  {
++                    if (target == 0)
++                      target = gen_reg_rtx (mode);
++                    convert_move (target, temp, 0);
++                    return target;
++                  }
++                else
++                  return gen_lowpart (mode, temp);
++              }
++            else
++              delete_insns_since (last);
++          }
++      }
++    }
++
++  delete_insns_since (entry_last);
++  return 0;
++}
++
++/* Like expand_binop, but for open-coding vectors binops.  */
++
++static rtx
++expand_vector_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
++     enum machine_mode mode;
++     optab binoptab;
++     rtx op0, op1;
++     rtx target;
++     int unsignedp;
++     enum optab_methods methods;
++{
++  enum machine_mode submode, tmode;
++  int size, elts, subsize, subbitsize, i;
++  rtx t, a, b, res, seq;
++  enum mode_class class;
++
++  class = GET_MODE_CLASS (mode);
++
++  size = GET_MODE_SIZE (mode);
++  submode = GET_MODE_INNER (mode);
++
++  /* Search for the widest vector mode with the same inner mode that is
++     still narrower than MODE and that allows to open-code this operator.
++     Note, if we find such a mode and the handler later decides it can't
++     do the expansion, we'll be called recursively with the narrower mode.  */
++  for (tmode = GET_CLASS_NARROWEST_MODE (class);
++       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
++       tmode = GET_MODE_WIDER_MODE (tmode))
++    {
++      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
++        && binoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
++      submode = tmode;
++    }
++
++  switch (binoptab->code)
++    {
++    case AND:
++    case IOR:
++    case XOR:
++      tmode = int_mode_for_mode (mode);
++      if (tmode != BLKmode)
++      submode = tmode;
++    case PLUS:
++    case MINUS:
++    case MULT:
++    case DIV:
++      subsize = GET_MODE_SIZE (submode);
++      subbitsize = GET_MODE_BITSIZE (submode);
++      elts = size / subsize;
++
++      /* If METHODS is OPTAB_DIRECT, we don't insist on the exact mode,
++       but that we operate on more than one element at a time.  */
++      if (subsize == GET_MODE_UNIT_SIZE (mode) && methods == OPTAB_DIRECT)
++      return 0;
++
++      start_sequence ();
++
++      /* Errors can leave us with a const0_rtx as operand.  */
++      if (GET_MODE (op0) != mode)
++      op0 = copy_to_mode_reg (mode, op0);
++      if (GET_MODE (op1) != mode)
++      op1 = copy_to_mode_reg (mode, op1);
++
++      if (!target)
++      target = gen_reg_rtx (mode);
++
++      for (i = 0; i < elts; ++i)
++      {
++        /* If this is part of a register, and not the first item in the
++           word, we can't store using a SUBREG - that would clobber
++           previous results.
++           And storing with a SUBREG is only possible for the least
++           significant part, hence we can't do it for big endian
++           (unless we want to permute the evaluation order.  */
++        if (GET_CODE (target) == REG
++            && (BYTES_BIG_ENDIAN
++                ? subsize < UNITS_PER_WORD
++                : ((i * subsize) % UNITS_PER_WORD) != 0))
++          t = NULL_RTX;
++        else
++          t = simplify_gen_subreg (submode, target, mode, i * subsize);
++        if (CONSTANT_P (op0))
++          a = simplify_gen_subreg (submode, op0, mode, i * subsize);
++        else
++          a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
++                                 NULL_RTX, submode, submode, size);
++        if (CONSTANT_P (op1))
++          b = simplify_gen_subreg (submode, op1, mode, i * subsize);
++        else
++          b = extract_bit_field (op1, subbitsize, i * subbitsize, unsignedp,
++                                 NULL_RTX, submode, submode, size);
++
++        if (binoptab->code == DIV)
++          {
++            if (class == MODE_VECTOR_FLOAT)
++              res = expand_binop (submode, binoptab, a, b, t,
++                                  unsignedp, methods);
++            else
++              res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
++                                   a, b, t, unsignedp);
++          }
++        else
++          res = expand_binop (submode, binoptab, a, b, t,
++                              unsignedp, methods);
++
++        if (res == 0)
++          break;
++
++        if (t)
++          emit_move_insn (t, res);
++        else
++          store_bit_field (target, subbitsize, i * subbitsize, submode, res,
++                           size);
++      }
++      break;
++
++    default:
++      abort ();
++    }
++
++  seq = get_insns ();
++  end_sequence ();
++  emit_insn (seq);
++
++  return target;
++}
++
++/* Like expand_unop but for open-coding vector unops.  */
++
++static rtx
++expand_vector_unop (mode, unoptab, op0, target, unsignedp)
++     enum machine_mode mode;
++     optab unoptab;
++     rtx op0;
++     rtx target;
++     int unsignedp;
++{
++  enum machine_mode submode, tmode;
++  int size, elts, subsize, subbitsize, i;
++  rtx t, a, res, seq;
++
++  size = GET_MODE_SIZE (mode);
++  submode = GET_MODE_INNER (mode);
++
++  /* Search for the widest vector mode with the same inner mode that is
++     still narrower than MODE and that allows to open-code this operator.
++     Note, if we find such a mode and the handler later decides it can't
++     do the expansion, we'll be called recursively with the narrower mode.  */
++  for (tmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (mode));
++       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
++       tmode = GET_MODE_WIDER_MODE (tmode))
++    {
++      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
++        && unoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
++      submode = tmode;
++    }
++  /* If there is no negate operation, try doing a subtract from zero.  */
++  if (unoptab == neg_optab && GET_MODE_CLASS (submode) == MODE_INT
++      /* Avoid infinite recursion when an
++       error has left us with the wrong mode.  */
++      && GET_MODE (op0) == mode)
++    {    
++      rtx temp;
++      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
++                           target, unsignedp, OPTAB_DIRECT);
++      if (temp)
++      return temp;
++    }
++
++  if (unoptab == one_cmpl_optab)
++    {
++      tmode = int_mode_for_mode (mode);
++      if (tmode != BLKmode)
++      submode = tmode;
++    }
++
++  subsize = GET_MODE_SIZE (submode);
++  subbitsize = GET_MODE_BITSIZE (submode);
++  elts = size / subsize;
++
++  /* Errors can leave us with a const0_rtx as operand.  */
++  if (GET_MODE (op0) != mode)
++    op0 = copy_to_mode_reg (mode, op0);
++
++  if (!target)
++    target = gen_reg_rtx (mode);
++
++  start_sequence ();
++
++  for (i = 0; i < elts; ++i)
++    {
++      /* If this is part of a register, and not the first item in the
++       word, we can't store using a SUBREG - that would clobber
++       previous results.
++       And storing with a SUBREG is only possible for the least
++       significant part, hence we can't do it for big endian
++       (unless we want to permute the evaluation order.  */
++      if (GET_CODE (target) == REG
++        && (BYTES_BIG_ENDIAN
++            ?  subsize < UNITS_PER_WORD
++            : ((i * subsize) % UNITS_PER_WORD) != 0))
++      t = NULL_RTX;
++      else
++      t = simplify_gen_subreg (submode, target, mode, i * subsize);
++      if (CONSTANT_P (op0))
++      a = simplify_gen_subreg (submode, op0, mode, i * subsize);
++      else
++      a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
++                             t, submode, submode, size);
++
++      res = expand_unop (submode, unoptab, a, t, unsignedp);
++
++      if (t)
++      emit_move_insn (t, res);
++      else
++      store_bit_field (target, subbitsize, i * subbitsize, submode, res,
++                       size);
++    }
++
++  seq = get_insns ();
++  end_sequence ();
++  emit_insn (seq);
++
++  return target;
++}
++\f
++/* Expand a binary operator which has both signed and unsigned forms.
++   UOPTAB is the optab for unsigned operations, and SOPTAB is for
++   signed operations.
++
++   If we widen unsigned operands, we may use a signed wider operation instead
++   of an unsigned wider operation, since the result would be the same.  */
++
++rtx
++sign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods)
++     enum machine_mode mode;
++     optab uoptab, soptab;
++     rtx op0, op1, target;
++     int unsignedp;
++     enum optab_methods methods;
++{
++  rtx temp;
++  optab direct_optab = unsignedp ? uoptab : soptab;
++  struct optab wide_soptab;
++
++  /* Do it without widening, if possible.  */
++  temp = expand_binop (mode, direct_optab, op0, op1, target,
++                     unsignedp, OPTAB_DIRECT);
++  if (temp || methods == OPTAB_DIRECT)
++    return temp;
++
++  /* Try widening to a signed int.  Make a fake signed optab that
++     hides any signed insn for direct use.  */
++  wide_soptab = *soptab;
++  wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing;
++  wide_soptab.handlers[(int) mode].libfunc = 0;
++
++  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
++                     unsignedp, OPTAB_WIDEN);
++
++  /* For unsigned operands, try widening to an unsigned int.  */
++  if (temp == 0 && unsignedp)
++    temp = expand_binop (mode, uoptab, op0, op1, target,
++                       unsignedp, OPTAB_WIDEN);
++  if (temp || methods == OPTAB_WIDEN)
++    return temp;
++
++  /* Use the right width lib call if that exists.  */
++  temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
++  if (temp || methods == OPTAB_LIB)
++    return temp;
++
++  /* Must widen and use a lib call, use either signed or unsigned.  */
++  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
++                     unsignedp, methods);
++  if (temp != 0)
++    return temp;
++  if (unsignedp)
++    return expand_binop (mode, uoptab, op0, op1, target,
++                       unsignedp, methods);
++  return 0;
++}
++\f
++/* Generate code to perform an operation specified by BINOPTAB
++   on operands OP0 and OP1, with two results to TARG1 and TARG2.
++   We assume that the order of the operands for the instruction
++   is TARG0, OP0, OP1, TARG1, which would fit a pattern like
++   [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))].
++
++   Either TARG0 or TARG1 may be zero, but what that means is that
++   the result is not actually wanted.  We will generate it into
++   a dummy pseudo-reg and discard it.  They may not both be zero.
++
++   Returns 1 if this operation can be performed; 0 if not.  */
++
++int
++expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
++     optab binoptab;
++     rtx op0, op1;
++     rtx targ0, targ1;
++     int unsignedp;
++{
++  enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
++  enum mode_class class;
++  enum machine_mode wider_mode;
++  rtx entry_last = get_last_insn ();
++  rtx last;
++
++  class = GET_MODE_CLASS (mode);
++
++  op0 = protect_from_queue (op0, 0);
++  op1 = protect_from_queue (op1, 0);
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++      op1 = force_not_mem (op1);
++    }
++
++  /* If we are inside an appropriately-short loop and one operand is an
++     expensive constant, force it into a register.  */
++  if (CONSTANT_P (op0) && preserve_subexpressions_p ()
++      && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1))
++    op0 = force_reg (mode, op0);
++
++  if (CONSTANT_P (op1) && preserve_subexpressions_p ()
++      && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
++    op1 = force_reg (mode, op1);
++
++  if (targ0)
++    targ0 = protect_from_queue (targ0, 1);
++  else
++    targ0 = gen_reg_rtx (mode);
++  if (targ1)
++    targ1 = protect_from_queue (targ1, 1);
++  else
++    targ1 = gen_reg_rtx (mode);
++
++  /* Record where to go back to if we fail.  */
++  last = get_last_insn ();
++
++  if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    {
++      int icode = (int) binoptab->handlers[(int) mode].insn_code;
++      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
++      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
++      rtx pat;
++      rtx xop0 = op0, xop1 = op1;
++
++      /* In case the insn wants input operands in modes different from
++       those of the actual operands, convert the operands.  It would
++       seem that we don't need to convert CONST_INTs, but we do, so
++       that they're properly zero-extended, sign-extended or truncated
++       for their mode.  */
++
++      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
++      xop0 = convert_modes (mode0,
++                            GET_MODE (op0) != VOIDmode
++                            ? GET_MODE (op0)
++                            : mode,
++                            xop0, unsignedp);
++
++      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
++      xop1 = convert_modes (mode1,
++                            GET_MODE (op1) != VOIDmode
++                            ? GET_MODE (op1)
++                            : mode,
++                            xop1, unsignedp);
++
++      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
++      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
++      xop0 = copy_to_mode_reg (mode0, xop0);
++
++      if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1))
++      xop1 = copy_to_mode_reg (mode1, xop1);
++
++      /* We could handle this, but we should always be called with a pseudo
++       for our targets and all insns should take them as outputs.  */
++      if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
++        || ! (*insn_data[icode].operand[3].predicate) (targ1, mode))
++      abort ();
++      
++      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
++      if (pat)
++      {
++        emit_insn (pat);
++        return 1;
++      }
++      else
++      delete_insns_since (last);
++    }
++
++  /* It can't be done in this mode.  Can we do it in a wider mode?  */
++
++  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
++    {
++      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++        if (binoptab->handlers[(int) wider_mode].insn_code
++            != CODE_FOR_nothing)
++          {
++            rtx t0 = gen_reg_rtx (wider_mode);
++            rtx t1 = gen_reg_rtx (wider_mode);
++            rtx cop0 = convert_modes (wider_mode, mode, op0, unsignedp);
++            rtx cop1 = convert_modes (wider_mode, mode, op1, unsignedp);
++
++            if (expand_twoval_binop (binoptab, cop0, cop1,
++                                     t0, t1, unsignedp))
++              {
++                convert_move (targ0, t0, unsignedp);
++                convert_move (targ1, t1, unsignedp);
++                return 1;
++              }
++            else
++              delete_insns_since (last);
++          }
++      }
++    }
++
++  delete_insns_since (entry_last);
++  return 0;
++}
++\f
++/* Wrapper around expand_unop which takes an rtx code to specify
++   the operation to perform, not an optab pointer.  All other
++   arguments are the same.  */
++rtx
++expand_simple_unop (mode, code, op0, target, unsignedp)
++     enum machine_mode mode;
++     enum rtx_code code;
++     rtx op0;
++     rtx target;
++     int unsignedp;
++{
++  optab unop = code_to_optab[(int) code];
++  if (unop == 0)
++    abort ();
++
++  return expand_unop (mode, unop, op0, target, unsignedp);
++}
++
++/* Generate code to perform an operation specified by UNOPTAB
++   on operand OP0, with result having machine-mode MODE.
++
++   UNSIGNEDP is for the case where we have to widen the operands
++   to perform the operation.  It says to use zero-extension.
++
++   If TARGET is nonzero, the value
++   is generated there, if it is convenient to do so.
++   In all cases an rtx is returned for the locus of the value;
++   this may or may not be TARGET.  */
++
++rtx
++expand_unop (mode, unoptab, op0, target, unsignedp)
++     enum machine_mode mode;
++     optab unoptab;
++     rtx op0;
++     rtx target;
++     int unsignedp;
++{
++  enum mode_class class;
++  enum machine_mode wider_mode;
++  rtx temp;
++  rtx last = get_last_insn ();
++  rtx pat;
++
++  class = GET_MODE_CLASS (mode);
++
++  op0 = protect_from_queue (op0, 0);
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++    }
++
++  if (target)
++    target = protect_from_queue (target, 1);
++
++  if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    {
++      int icode = (int) unoptab->handlers[(int) mode].insn_code;
++      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
++      rtx xop0 = op0;
++
++      if (target)
++      temp = target;
++      else
++      temp = gen_reg_rtx (mode);
++
++      if (GET_MODE (xop0) != VOIDmode
++        && GET_MODE (xop0) != mode0)
++      xop0 = convert_to_mode (mode0, xop0, unsignedp);
++
++      /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
++
++      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
++      xop0 = copy_to_mode_reg (mode0, xop0);
++
++      if (! (*insn_data[icode].operand[0].predicate) (temp, mode))
++      temp = gen_reg_rtx (mode);
++
++      pat = GEN_FCN (icode) (temp, xop0);
++      if (pat)
++      {
++        if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
++            && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
++          {
++            delete_insns_since (last);
++            return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp);
++          }
++
++        emit_insn (pat);
++        
++        return temp;
++      }
++      else
++      delete_insns_since (last);
++    }
++
++  /* It can't be done in this mode.  Can we open-code it in a wider mode?  */
++
++  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
++    for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++      if (unoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
++        {
++          rtx xop0 = op0;
++
++          /* For certain operations, we need not actually extend
++             the narrow operand, as long as we will truncate the
++             results to the same narrowness.  */
++
++          xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
++                                (unoptab == neg_optab
++                                 || unoptab == one_cmpl_optab)
++                                && class == MODE_INT);
++            
++          temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
++                              unsignedp);
++
++          if (temp)
++            {
++              if (class != MODE_INT)
++                {
++                  if (target == 0)
++                    target = gen_reg_rtx (mode);
++                  convert_move (target, temp, 0);
++                  return target;
++                }
++              else
++                return gen_lowpart (mode, temp);
++            }
++          else
++            delete_insns_since (last);
++        }
++      }
++
++  /* These can be done a word at a time.  */
++  if (unoptab == one_cmpl_optab
++      && class == MODE_INT
++      && GET_MODE_SIZE (mode) > UNITS_PER_WORD
++      && unoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
++    {
++      int i;
++      rtx insns;
++
++      if (target == 0 || target == op0)
++      target = gen_reg_rtx (mode);
++
++      start_sequence ();
++
++      /* Do the actual arithmetic.  */
++      for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
++      {
++        rtx target_piece = operand_subword (target, i, 1, mode);
++        rtx x = expand_unop (word_mode, unoptab,
++                             operand_subword_force (op0, i, mode),
++                             target_piece, unsignedp);
++
++        if (target_piece != x)
++          emit_move_insn (target_piece, x);
++      }
++
++      insns = get_insns ();
++      end_sequence ();
++
++      emit_no_conflict_block (insns, target, op0, NULL_RTX,
++                            gen_rtx_fmt_e (unoptab->code, mode,
++                                           copy_rtx (op0)));
++      return target;
++    }
++
++  /* Open-code the complex negation operation.  */
++  else if (unoptab->code == NEG
++         && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT))
++    {
++      rtx target_piece;
++      rtx x;
++      rtx seq;
++
++      /* Find the correct mode for the real and imaginary parts */
++      enum machine_mode submode = GET_MODE_INNER (mode);
++
++      if (submode == BLKmode)
++      abort ();
++
++      if (target == 0)
++      target = gen_reg_rtx (mode);
++      
++      start_sequence ();
++
++      target_piece = gen_imagpart (submode, target);
++      x = expand_unop (submode, unoptab,
++                     gen_imagpart (submode, op0),
++                     target_piece, unsignedp);
++      if (target_piece != x)
++      emit_move_insn (target_piece, x);
++
++      target_piece = gen_realpart (submode, target);
++      x = expand_unop (submode, unoptab,
++                     gen_realpart (submode, op0),
++                     target_piece, unsignedp);
++      if (target_piece != x)
++      emit_move_insn (target_piece, x);
++
++      seq = get_insns ();
++      end_sequence ();
++
++      emit_no_conflict_block (seq, target, op0, 0,
++                            gen_rtx_fmt_e (unoptab->code, mode,
++                                           copy_rtx (op0)));
++      return target;
++    }
++
++  /* Now try a library call in this mode.  */
++  if (unoptab->handlers[(int) mode].libfunc)
++    {
++      rtx insns;
++      rtx value;
++
++      start_sequence ();
++
++      /* Pass 1 for NO_QUEUE so we don't lose any increments
++       if the libcall is cse'd or moved.  */
++      value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc,
++                                     NULL_RTX, LCT_CONST, mode, 1, op0, mode);
++      insns = get_insns ();
++      end_sequence ();
++
++      target = gen_reg_rtx (mode);
++      emit_libcall_block (insns, target, value,
++                        gen_rtx_fmt_e (unoptab->code, mode, op0));
++
++      return target;
++    }
++
++  if (class == MODE_VECTOR_FLOAT || class == MODE_VECTOR_INT)
++    return expand_vector_unop (mode, unoptab, op0, target, unsignedp);
++
++  /* It can't be done in this mode.  Can we do it in a wider mode?  */
++
++  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
++    {
++      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++        if ((unoptab->handlers[(int) wider_mode].insn_code
++             != CODE_FOR_nothing)
++            || unoptab->handlers[(int) wider_mode].libfunc)
++          {
++            rtx xop0 = op0;
++
++            /* For certain operations, we need not actually extend
++               the narrow operand, as long as we will truncate the
++               results to the same narrowness.  */
++
++            xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
++                                  (unoptab == neg_optab
++                                   || unoptab == one_cmpl_optab)
++                                  && class == MODE_INT);
++            
++            temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
++                                unsignedp);
++
++            if (temp)
++              {
++                if (class != MODE_INT)
++                  {
++                    if (target == 0)
++                      target = gen_reg_rtx (mode);
++                    convert_move (target, temp, 0);
++                    return target;
++                  }
++                else
++                  return gen_lowpart (mode, temp);
++              }
++            else
++              delete_insns_since (last);
++          }
++      }
++    }
++
++  /* If there is no negate operation, try doing a subtract from zero.
++     The US Software GOFAST library needs this.  */
++  if (unoptab->code == NEG)
++    {    
++      rtx temp;
++      temp = expand_binop (mode,
++                           unoptab == negv_optab ? subv_optab : sub_optab,
++                           CONST0_RTX (mode), op0,
++                           target, unsignedp, OPTAB_LIB_WIDEN);
++      if (temp)
++      return temp;
++    }
++      
++  return 0;
++}
++\f
++/* Emit code to compute the absolute value of OP0, with result to
++   TARGET if convenient.  (TARGET may be 0.)  The return value says
++   where the result actually is to be found.
++
++   MODE is the mode of the operand; the mode of the result is
++   different but can be deduced from MODE.
++
++ */
++
++rtx
++expand_abs (mode, op0, target, result_unsignedp, safe)
++     enum machine_mode mode;
++     rtx op0;
++     rtx target;
++     int result_unsignedp;
++     int safe;
++{
++  rtx temp, op1;
++
++  if (! flag_trapv)
++    result_unsignedp = 1;
++
++  /* First try to do it with a special abs instruction.  */
++  temp = expand_unop (mode, result_unsignedp ? abs_optab : absv_optab,
++                      op0, target, 0);
++  if (temp != 0)
++    return temp;
++
++  /* If we have a MAX insn, we can do this as MAX (x, -x).  */
++  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    {
++      rtx last = get_last_insn ();
++
++      temp = expand_unop (mode, neg_optab, op0, NULL_RTX, 0);
++      if (temp != 0)
++      temp = expand_binop (mode, smax_optab, op0, temp, target, 0,
++                           OPTAB_WIDEN);
++
++      if (temp != 0)
++      return temp;
++
++      delete_insns_since (last);
++    }
++
++  /* If this machine has expensive jumps, we can do integer absolute
++     value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
++     where W is the width of MODE.  */
++
++  if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
++    {
++      rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
++                                 size_int (GET_MODE_BITSIZE (mode) - 1),
++                                 NULL_RTX, 0);
++
++      temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
++                         OPTAB_LIB_WIDEN);
++      if (temp != 0)
++      temp = expand_binop (mode, result_unsignedp ? sub_optab : subv_optab,
++                             temp, extended, target, 0, OPTAB_LIB_WIDEN);
++
++      if (temp != 0)
++      return temp;
++    }
++
++  /* If that does not win, use conditional jump and negate.  */
++
++  /* It is safe to use the target if it is the same
++     as the source if this is also a pseudo register */
++  if (op0 == target && GET_CODE (op0) == REG
++      && REGNO (op0) >= FIRST_PSEUDO_REGISTER)
++    safe = 1;
++
++  op1 = gen_label_rtx ();
++  if (target == 0 || ! safe
++      || GET_MODE (target) != mode
++      || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
++      || (GET_CODE (target) == REG
++        && REGNO (target) < FIRST_PSEUDO_REGISTER))
++    target = gen_reg_rtx (mode);
++
++  emit_move_insn (target, op0);
++  NO_DEFER_POP;
++
++  /* If this mode is an integer too wide to compare properly,
++     compare word by word.  Rely on CSE to optimize constant cases.  */
++  if (GET_MODE_CLASS (mode) == MODE_INT
++      && ! can_compare_p (GE, mode, ccp_jump))
++    do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, 
++                                NULL_RTX, op1);
++  else
++    do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
++                           NULL_RTX, NULL_RTX, op1);
++
++  op0 = expand_unop (mode, result_unsignedp ? neg_optab : negv_optab,
++                     target, target, 0);
++  if (op0 != target)
++    emit_move_insn (target, op0);
++  emit_label (op1);
++  OK_DEFER_POP;
++  return target;
++}
++\f
++/* Emit code to compute the absolute value of OP0, with result to
++   TARGET if convenient.  (TARGET may be 0.)  The return value says
++   where the result actually is to be found.
++
++   MODE is the mode of the operand; the mode of the result is
++   different but can be deduced from MODE.
++
++   UNSIGNEDP is relevant for complex integer modes.  */
++
++rtx
++expand_complex_abs (mode, op0, target, unsignedp)
++     enum machine_mode mode;
++     rtx op0;
++     rtx target;
++     int unsignedp;
++{
++  enum mode_class class = GET_MODE_CLASS (mode);
++  enum machine_mode wider_mode;
++  rtx temp;
++  rtx entry_last = get_last_insn ();
++  rtx last;
++  rtx pat;
++  optab this_abs_optab;
++
++  /* Find the correct mode for the real and imaginary parts.  */
++  enum machine_mode submode = GET_MODE_INNER (mode);
++
++  if (submode == BLKmode)
++    abort ();
++
++  op0 = protect_from_queue (op0, 0);
++
++  if (flag_force_mem)
++    {
++      op0 = force_not_mem (op0);
++    }
++
++  last = get_last_insn ();
++
++  if (target)
++    target = protect_from_queue (target, 1);
++
++  this_abs_optab = ! unsignedp && flag_trapv
++                   && (GET_MODE_CLASS(mode) == MODE_INT)
++                   ? absv_optab : abs_optab;
++
++  if (this_abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++    {
++      int icode = (int) this_abs_optab->handlers[(int) mode].insn_code;
++      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
++      rtx xop0 = op0;
++
++      if (target)
++      temp = target;
++      else
++      temp = gen_reg_rtx (submode);
++
++      if (GET_MODE (xop0) != VOIDmode
++        && GET_MODE (xop0) != mode0)
++      xop0 = convert_to_mode (mode0, xop0, unsignedp);
++
++      /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
++
++      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
++      xop0 = copy_to_mode_reg (mode0, xop0);
++
++      if (! (*insn_data[icode].operand[0].predicate) (temp, submode))
++      temp = gen_reg_rtx (submode);
++
++      pat = GEN_FCN (icode) (temp, xop0);
++      if (pat)
++      {
++        if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
++            && ! add_equal_note (pat, temp, this_abs_optab->code, xop0, 
++                                 NULL_RTX))
++          {
++            delete_insns_since (last);
++            return expand_unop (mode, this_abs_optab, op0, NULL_RTX, 
++                                unsignedp);
++          }
++
++        emit_insn (pat);
++        
++        return temp;
++      }
++      else
++      delete_insns_since (last);
++    }
++
++  /* It can't be done in this mode.  Can we open-code it in a wider mode?  */
++
++  for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++    {
++      if (this_abs_optab->handlers[(int) wider_mode].insn_code 
++        != CODE_FOR_nothing)
++      {
++        rtx xop0 = op0;
++
++        xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
++        temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
++
++        if (temp)
++          {
++            if (class != MODE_COMPLEX_INT)
++              {
++                if (target == 0)
++                  target = gen_reg_rtx (submode);
++                convert_move (target, temp, 0);
++                return target;
++              }
++            else
++              return gen_lowpart (submode, temp);
++          }
++        else
++          delete_insns_since (last);
++      }
++    }
++
++  /* Open-code the complex absolute-value operation
++     if we can open-code sqrt.  Otherwise it's not worth while.  */
++  if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing
++      && ! flag_trapv)
++    {
++      rtx real, imag, total;
++
++      real = gen_realpart (submode, op0);
++      imag = gen_imagpart (submode, op0);
++
++      /* Square both parts.  */
++      real = expand_mult (submode, real, real, NULL_RTX, 0);
++      imag = expand_mult (submode, imag, imag, NULL_RTX, 0);
++
++      /* Sum the parts.  */
++      total = expand_binop (submode, add_optab, real, imag, NULL_RTX,
++                          0, OPTAB_LIB_WIDEN);
++
++      /* Get sqrt in TARGET.  Set TARGET to where the result is.  */
++      target = expand_unop (submode, sqrt_optab, total, target, 0);
++      if (target == 0)
++      delete_insns_since (last);
++      else
++      return target;
++    }
++
++  /* Now try a library call in this mode.  */
++  if (this_abs_optab->handlers[(int) mode].libfunc)
++    {
++      rtx insns;
++      rtx value;
++
++      start_sequence ();
++
++      /* Pass 1 for NO_QUEUE so we don't lose any increments
++       if the libcall is cse'd or moved.  */
++      value = emit_library_call_value (abs_optab->handlers[(int) mode].libfunc,
++                                     NULL_RTX, LCT_CONST, submode, 1, op0, mode);
++      insns = get_insns ();
++      end_sequence ();
++
++      target = gen_reg_rtx (submode);
++      emit_libcall_block (insns, target, value,
++                        gen_rtx_fmt_e (this_abs_optab->code, mode, op0));
++
++      return target;
++    }
++
++  /* It can't be done in this mode.  Can we do it in a wider mode?  */
++
++  for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++    {
++      if ((this_abs_optab->handlers[(int) wider_mode].insn_code
++         != CODE_FOR_nothing)
++        || this_abs_optab->handlers[(int) wider_mode].libfunc)
++      {
++        rtx xop0 = op0;
++
++        xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
++
++        temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
++
++        if (temp)
++          {
++            if (class != MODE_COMPLEX_INT)
++              {
++                if (target == 0)
++                  target = gen_reg_rtx (submode);
++                convert_move (target, temp, 0);
++                return target;
++              }
++            else
++              return gen_lowpart (submode, temp);
++          }
++        else
++          delete_insns_since (last);
++      }
++    }
++
++  delete_insns_since (entry_last);
++  return 0;
++}
++\f
++/* Generate an instruction whose insn-code is INSN_CODE,
++   with two operands: an output TARGET and an input OP0.
++   TARGET *must* be nonzero, and the output is always stored there.
++   CODE is an rtx code such that (CODE OP0) is an rtx that describes
++   the value that is stored into TARGET.  */
++
++void
++emit_unop_insn (icode, target, op0, code)
++     int icode;
++     rtx target;
++     rtx op0;
++     enum rtx_code code;
++{
++  rtx temp;
++  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
++  rtx pat;
++
++  temp = target = protect_from_queue (target, 1);
++
++  op0 = protect_from_queue (op0, 0);
++
++  /* Sign and zero extension from memory is often done specially on
++     RISC machines, so forcing into a register here can pessimize
++     code.  */
++  if (flag_force_mem && code != SIGN_EXTEND && code != ZERO_EXTEND)
++    op0 = force_not_mem (op0);
++
++  /* Now, if insn does not accept our operands, put them into pseudos.  */
++
++  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
++    op0 = copy_to_mode_reg (mode0, op0);
++
++  if (! (*insn_data[icode].operand[0].predicate) (temp, GET_MODE (temp))
++      || (flag_force_mem && GET_CODE (temp) == MEM))
++    temp = gen_reg_rtx (GET_MODE (temp));
++
++  pat = GEN_FCN (icode) (temp, op0);
++
++  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
++    add_equal_note (pat, temp, code, op0, NULL_RTX);
++  
++  emit_insn (pat);
++
++  if (temp != target)
++    emit_move_insn (target, temp);
++}
++\f
++/* Emit code to perform a series of operations on a multi-word quantity, one
++   word at a time.
++
++   Such a block is preceded by a CLOBBER of the output, consists of multiple
++   insns, each setting one word of the output, and followed by a SET copying
++   the output to itself.
++
++   Each of the insns setting words of the output receives a REG_NO_CONFLICT
++   note indicating that it doesn't conflict with the (also multi-word)
++   inputs.  The entire block is surrounded by REG_LIBCALL and REG_RETVAL
++   notes.
++
++   INSNS is a block of code generated to perform the operation, not including
++   the CLOBBER and final copy.  All insns that compute intermediate values
++   are first emitted, followed by the block as described above.  
++
++   TARGET, OP0, and OP1 are the output and inputs of the operations,
++   respectively.  OP1 may be zero for a unary operation.
++
++   EQUIV, if nonzero, is an expression to be placed into a REG_EQUAL note
++   on the last insn.
++
++   If TARGET is not a register, INSNS is simply emitted with no special
++   processing.  Likewise if anything in INSNS is not an INSN or if
++   there is a libcall block inside INSNS.
++
++   The final insn emitted is returned.  */
++
++rtx
++emit_no_conflict_block (insns, target, op0, op1, equiv)
++     rtx insns;
++     rtx target;
++     rtx op0, op1;
++     rtx equiv;
++{
++  rtx prev, next, first, last, insn;
++
++  if (GET_CODE (target) != REG || reload_in_progress)
++    return emit_insn (insns);
++  else
++    for (insn = insns; insn; insn = NEXT_INSN (insn))
++      if (GET_CODE (insn) != INSN
++        || find_reg_note (insn, REG_LIBCALL, NULL_RTX))
++      return emit_insn (insns);
++
++  /* First emit all insns that do not store into words of the output and remove
++     these from the list.  */
++  for (insn = insns; insn; insn = next)
++    {
++      rtx set = 0, note;
++      int i;
++
++      next = NEXT_INSN (insn);
++
++      /* Some ports (cris) create an libcall regions at their own.  We must
++       avoid any potential nesting of LIBCALLs.  */
++      if ((note = find_reg_note (insn, REG_LIBCALL, NULL)) != NULL)
++      remove_note (insn, note);
++      if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL)
++      remove_note (insn, note);
++
++      if (GET_CODE (PATTERN (insn)) == SET || GET_CODE (PATTERN (insn)) == USE
++        || GET_CODE (PATTERN (insn)) == CLOBBER)
++      set = PATTERN (insn);
++      else if (GET_CODE (PATTERN (insn)) == PARALLEL)
++      {
++        for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
++          if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
++            {
++              set = XVECEXP (PATTERN (insn), 0, i);
++              break;
++            }
++      }
++
++      if (set == 0)
++      abort ();
++
++      if (! reg_overlap_mentioned_p (target, SET_DEST (set)))
++      {
++        if (PREV_INSN (insn))
++          NEXT_INSN (PREV_INSN (insn)) = next;
++        else
++          insns = next;
++
++        if (next)
++          PREV_INSN (next) = PREV_INSN (insn);
++
++        add_insn (insn);
++      }
++    }
++
++  prev = get_last_insn ();
++
++  /* Now write the CLOBBER of the output, followed by the setting of each
++     of the words, followed by the final copy.  */
++  if (target != op0 && target != op1)
++    emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
++
++  for (insn = insns; insn; insn = next)
++    {
++      next = NEXT_INSN (insn);
++      add_insn (insn);
++
++      if (op1 && GET_CODE (op1) == REG)
++      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op1,
++                                            REG_NOTES (insn));
++
++      if (op0 && GET_CODE (op0) == REG)
++      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op0,
++                                            REG_NOTES (insn));
++    }
++
++  if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
++      != CODE_FOR_nothing)
++    {
++      last = emit_move_insn (target, target);
++      if (equiv)
++      set_unique_reg_note (last, REG_EQUAL, equiv);
++    }
++  else
++    {
++      last = get_last_insn ();
++
++      /* Remove any existing REG_EQUAL note from "last", or else it will
++       be mistaken for a note referring to the full contents of the
++       alleged libcall value when found together with the REG_RETVAL
++       note added below.  An existing note can come from an insn
++       expansion at "last".  */
++      remove_note (last, find_reg_note (last, REG_EQUAL, NULL_RTX));
++    }
++
++  if (prev == 0)
++    first = get_insns ();
++  else
++    first = NEXT_INSN (prev);
++
++  /* Encapsulate the block so it gets manipulated as a unit.  */
++  REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
++                                       REG_NOTES (first));
++  REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last));
++
++  return last;
++}
++\f
++/* Emit code to make a call to a constant function or a library call.
++
++   INSNS is a list containing all insns emitted in the call.
++   These insns leave the result in RESULT.  Our block is to copy RESULT
++   to TARGET, which is logically equivalent to EQUIV.
++
++   We first emit any insns that set a pseudo on the assumption that these are
++   loading constants into registers; doing so allows them to be safely cse'ed
++   between blocks.  Then we emit all the other insns in the block, followed by
++   an insn to move RESULT to TARGET.  This last insn will have a REQ_EQUAL
++   note with an operand of EQUIV.
++
++   Moving assignments to pseudos outside of the block is done to improve
++   the generated code, but is not required to generate correct code,
++   hence being unable to move an assignment is not grounds for not making
++   a libcall block.  There are two reasons why it is safe to leave these
++   insns inside the block: First, we know that these pseudos cannot be
++   used in generated RTL outside the block since they are created for
++   temporary purposes within the block.  Second, CSE will not record the
++   values of anything set inside a libcall block, so we know they must
++   be dead at the end of the block.
++
++   Except for the first group of insns (the ones setting pseudos), the
++   block is delimited by REG_RETVAL and REG_LIBCALL notes.  */
++
++void
++emit_libcall_block (insns, target, result, equiv)
++     rtx insns;
++     rtx target;
++     rtx result;
++     rtx equiv;
++{
++  rtx final_dest = target;
++  rtx prev, next, first, last, insn;
++
++  /* If this is a reg with REG_USERVAR_P set, then it could possibly turn
++     into a MEM later.  Protect the libcall block from this change.  */
++  if (! REG_P (target) || REG_USERVAR_P (target))
++    target = gen_reg_rtx (GET_MODE (target));
++  
++  /* If we're using non-call exceptions, a libcall corresponding to an
++     operation that may trap may also trap.  */
++  if (flag_non_call_exceptions && may_trap_p (equiv))
++    {
++      for (insn = insns; insn; insn = NEXT_INSN (insn))
++      if (GET_CODE (insn) == CALL_INSN)
++        {
++          rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
++          
++          if (note != 0 && INTVAL (XEXP (note, 0)) <= 0)
++            remove_note (insn, note);
++        }
++    }
++  else
++  /* look for any CALL_INSNs in this sequence, and attach a REG_EH_REGION
++     reg note to indicate that this call cannot throw or execute a nonlocal
++     goto (unless there is already a REG_EH_REGION note, in which case
++     we update it).  */
++    for (insn = insns; insn; insn = NEXT_INSN (insn))
++      if (GET_CODE (insn) == CALL_INSN)
++      {
++        rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
++      
++        if (note != 0)
++          XEXP (note, 0) = GEN_INT (-1);
++        else
++          REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
++                                                REG_NOTES (insn));
++      }
++
++  /* First emit all insns that set pseudos.  Remove them from the list as
++     we go.  Avoid insns that set pseudos which were referenced in previous
++     insns.  These can be generated by move_by_pieces, for example,
++     to update an address.  Similarly, avoid insns that reference things
++     set in previous insns.  */
++
++  for (insn = insns; insn; insn = next)
++    {
++      rtx set = single_set (insn);
++      rtx note;
++
++      /* Some ports (cris) create an libcall regions at their own.  We must
++       avoid any potential nesting of LIBCALLs.  */
++      if ((note = find_reg_note (insn, REG_LIBCALL, NULL)) != NULL)
++      remove_note (insn, note);
++      if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL)
++      remove_note (insn, note);
++
++      next = NEXT_INSN (insn);
++
++      if (set != 0 && GET_CODE (SET_DEST (set)) == REG
++        && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
++        && (insn == insns
++            || ((! INSN_P(insns)
++                 || ! reg_mentioned_p (SET_DEST (set), PATTERN (insns)))
++                && ! reg_used_between_p (SET_DEST (set), insns, insn)
++                && ! modified_in_p (SET_SRC (set), insns)
++                && ! modified_between_p (SET_SRC (set), insns, insn))))
++      {
++        if (PREV_INSN (insn))
++          NEXT_INSN (PREV_INSN (insn)) = next;
++        else
++          insns = next;
++
++        if (next)
++          PREV_INSN (next) = PREV_INSN (insn);
++
++        add_insn (insn);
++      }
++    }
++
++  prev = get_last_insn ();
++
++  /* Write the remaining insns followed by the final copy.  */
++
++  for (insn = insns; insn; insn = next)
++    {
++      next = NEXT_INSN (insn);
++
++      add_insn (insn);
++    }
++
++  last = emit_move_insn (target, result);
++  if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
++      != CODE_FOR_nothing)
++    set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv));
++  else
++    {
++      /* Remove any existing REG_EQUAL note from "last", or else it will
++       be mistaken for a note referring to the full contents of the
++       libcall value when found together with the REG_RETVAL note added
++       below.  An existing note can come from an insn expansion at
++       "last".  */
++      remove_note (last, find_reg_note (last, REG_EQUAL, NULL_RTX));
++    }
++
++  if (final_dest != target)
++    emit_move_insn (final_dest, target);
++
++  if (prev == 0)
++    first = get_insns ();
++  else
++    first = NEXT_INSN (prev);
++
++  /* Encapsulate the block so it gets manipulated as a unit.  */
++  if (!flag_non_call_exceptions || !may_trap_p (equiv))
++    {
++      /* We can't attach the REG_LIBCALL and REG_RETVAL notes
++       when the encapsulated region would not be in one basic block,
++       i.e. when there is a control_flow_insn_p insn between FIRST and LAST.
++       */
++      bool attach_libcall_retval_notes = true;
++      next = NEXT_INSN (last);
++      for (insn = first; insn != next; insn = NEXT_INSN (insn))
++      if (control_flow_insn_p (insn))
++        {
++          attach_libcall_retval_notes = false;
++          break;
++        }
++
++      if (attach_libcall_retval_notes)
++      {
++        REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
++                                               REG_NOTES (first));
++        REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first,
++                                              REG_NOTES (last));
++      }
++    }
++}
++\f
++/* Generate code to store zero in X.  */
++
++void
++emit_clr_insn (x)
++     rtx x;
++{
++  emit_move_insn (x, const0_rtx);
++}
++
++/* Generate code to store 1 in X
++   assuming it contains zero beforehand.  */
++
++void
++emit_0_to_1_insn (x)
++     rtx x;
++{
++  emit_move_insn (x, const1_rtx);
++}
++
++/* Nonzero if we can perform a comparison of mode MODE straightforwardly.
++   PURPOSE describes how this comparison will be used.  CODE is the rtx
++   comparison code we will be using.
++
++   ??? Actually, CODE is slightly weaker than that.  A target is still
++   required to implement all of the normal bcc operations, but not 
++   required to implement all (or any) of the unordered bcc operations.  */
++  
++int
++can_compare_p (code, mode, purpose)
++     enum rtx_code code;
++     enum machine_mode mode;
++     enum can_compare_purpose purpose;
++{
++  do
++    {
++      if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++      {
++        if (purpose == ccp_jump)
++          return bcc_gen_fctn[(int) code] != NULL;
++        else if (purpose == ccp_store_flag)
++          return setcc_gen_code[(int) code] != CODE_FOR_nothing;
++        else
++          /* There's only one cmov entry point, and it's allowed to fail.  */
++          return 1;
++      }
++      if (purpose == ccp_jump
++        && cbranch_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++      return 1;
++      if (purpose == ccp_cmov
++        && cmov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++      return 1;
++      if (purpose == ccp_store_flag
++        && cstore_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
++      return 1;
++
++      mode = GET_MODE_WIDER_MODE (mode);
++    }
++  while (mode != VOIDmode);
++
++  return 0;
++}
++
++/* This function is called when we are going to emit a compare instruction that
++   compares the values found in *PX and *PY, using the rtl operator COMPARISON.
++
++   *PMODE is the mode of the inputs (in case they are const_int).
++   *PUNSIGNEDP nonzero says that the operands are unsigned;
++   this matters if they need to be widened.
++
++   If they have mode BLKmode, then SIZE specifies the size of both operands.
++
++   This function performs all the setup necessary so that the caller only has
++   to emit a single comparison insn.  This setup can involve doing a BLKmode
++   comparison or emitting a library call to perform the comparison if no insn
++   is available to handle it.
++   The values which are passed in through pointers can be modified; the caller
++   should perform the comparison on the modified values.  */
++
++static void
++prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, purpose)
++     rtx *px, *py;
++     enum rtx_code *pcomparison;
++     rtx size;
++     enum machine_mode *pmode;
++     int *punsignedp;
++     enum can_compare_purpose purpose;
++{
++  enum machine_mode mode = *pmode;
++  rtx x = *px, y = *py;
++  int unsignedp = *punsignedp;
++  enum mode_class class;
++
++  class = GET_MODE_CLASS (mode);
++
++  /* They could both be VOIDmode if both args are immediate constants,
++     but we should fold that at an earlier stage.
++     With no special code here, this will call abort,
++     reminding the programmer to implement such folding.  */
++
++  if (mode != BLKmode && flag_force_mem)
++    {
++      x = force_not_mem (x);
++      y = force_not_mem (y);
++    }
++
++  /* If we are inside an appropriately-short loop and one operand is an
++     expensive constant, force it into a register.  */
++  if (CONSTANT_P (x) && preserve_subexpressions_p ()
++      && rtx_cost (x, COMPARE) > COSTS_N_INSNS (1))
++    x = force_reg (mode, x);
++
++  if (CONSTANT_P (y) && preserve_subexpressions_p ()
++      && rtx_cost (y, COMPARE) > COSTS_N_INSNS (1))
++    y = force_reg (mode, y);
++
++#ifdef HAVE_cc0
++  /* Abort if we have a non-canonical comparison.  The RTL documentation
++     states that canonical comparisons are required only for targets which
++     have cc0.  */
++  if (CONSTANT_P (x) && ! CONSTANT_P (y))
++    abort ();
++#endif
++
++  /* Don't let both operands fail to indicate the mode.  */
++  if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode)
++    x = force_reg (mode, x);
++
++  /* Handle all BLKmode compares.  */
++
++  if (mode == BLKmode)
++    {
++      rtx result;
++      enum machine_mode result_mode;
++      rtx opalign ATTRIBUTE_UNUSED
++      = GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT);
++
++      emit_queue ();
++      x = protect_from_queue (x, 0);
++      y = protect_from_queue (y, 0);
++
++      if (size == 0)
++      abort ();
++#ifdef HAVE_cmpstrqi
++      if (HAVE_cmpstrqi
++        && GET_CODE (size) == CONST_INT
++        && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
++      {
++        result_mode = insn_data[(int) CODE_FOR_cmpstrqi].operand[0].mode;
++        result = gen_reg_rtx (result_mode);
++        emit_insn (gen_cmpstrqi (result, x, y, size, opalign));
++      }
++      else
++#endif
++#ifdef HAVE_cmpstrhi
++      if (HAVE_cmpstrhi
++        && GET_CODE (size) == CONST_INT
++        && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
++      {
++        result_mode = insn_data[(int) CODE_FOR_cmpstrhi].operand[0].mode;
++        result = gen_reg_rtx (result_mode);
++        emit_insn (gen_cmpstrhi (result, x, y, size, opalign));
++      }
++      else
++#endif
++#ifdef HAVE_cmpstrsi
++      if (HAVE_cmpstrsi)
++      {
++        result_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
++        result = gen_reg_rtx (result_mode);
++        size = protect_from_queue (size, 0);
++        emit_insn (gen_cmpstrsi (result, x, y,
++                                 convert_to_mode (SImode, size, 1),
++                                 opalign));
++      }
++      else
++#endif
++      {
++#ifdef TARGET_MEM_FUNCTIONS
++        result = emit_library_call_value (memcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
++                                          TYPE_MODE (integer_type_node), 3,
++                                          XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
++                                          convert_to_mode (TYPE_MODE (sizetype), size,
++                                                           TREE_UNSIGNED (sizetype)),
++                                          TYPE_MODE (sizetype));
++#else
++        result = emit_library_call_value (bcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
++                                          TYPE_MODE (integer_type_node), 3,
++                                          XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
++                                          convert_to_mode (TYPE_MODE (integer_type_node),
++                                                           size,
++                                                           TREE_UNSIGNED (integer_type_node)),
++                                          TYPE_MODE (integer_type_node));
++#endif
++
++        result_mode = TYPE_MODE (integer_type_node);
++      }
++      *px = result;
++      *py = const0_rtx;
++      *pmode = result_mode;
++      return;
++    }
++
++  *px = x;
++  *py = y;
++  if (can_compare_p (*pcomparison, mode, purpose))
++    return;
++
++  /* Handle a lib call just for the mode we are using.  */
++
++  if (cmp_optab->handlers[(int) mode].libfunc && class != MODE_FLOAT)
++    {
++      rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
++      rtx result;
++
++      /* If we want unsigned, and this mode has a distinct unsigned
++       comparison routine, use that.  */
++      if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc)
++      libfunc = ucmp_optab->handlers[(int) mode].libfunc;
++
++      result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
++                                      word_mode, 2, x, mode, y, mode);
++
++      /* Integer comparison returns a result that must be compared against 1,
++       so that even if we do an unsigned compare afterward,
++       there is still a value that can represent the result "less than".  */
++      *px = result;
++      *py = const1_rtx;
++      *pmode = word_mode;
++      return;
++    }
++
++  if (class == MODE_FLOAT)
++    prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
++
++  else
++    abort ();
++}
++
++/* Before emitting an insn with code ICODE, make sure that X, which is going
++   to be used for operand OPNUM of the insn, is converted from mode MODE to
++   WIDER_MODE (UNSIGNEDP determines whether it is an unsigned conversion), and
++   that it is accepted by the operand predicate.  Return the new value.  */
++
++rtx
++prepare_operand (icode, x, opnum, mode, wider_mode, unsignedp)
++     int icode;
++     rtx x;
++     int opnum;
++     enum machine_mode mode, wider_mode;
++     int unsignedp;
++{
++  x = protect_from_queue (x, 0);
++
++  if (mode != wider_mode)
++    x = convert_modes (wider_mode, mode, x, unsignedp);
++
++  if (! (*insn_data[icode].operand[opnum].predicate)
++      (x, insn_data[icode].operand[opnum].mode))
++    {
++      if (no_new_pseudos)
++      return NULL_RTX;
++      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
++    }
++
++  return x;
++}
++
++/* Subroutine of emit_cmp_and_jump_insns; this function is called when we know
++   we can do the comparison.
++   The arguments are the same as for emit_cmp_and_jump_insns; but LABEL may
++   be NULL_RTX which indicates that only a comparison is to be generated.  */
++
++static void
++emit_cmp_and_jump_insn_1 (x, y, mode, comparison, unsignedp, label)
++     rtx x, y;
++     enum machine_mode mode;
++     enum rtx_code comparison;
++     int unsignedp;
++     rtx label;
++{
++  rtx test = gen_rtx_fmt_ee (comparison, mode, x, y);
++  enum mode_class class = GET_MODE_CLASS (mode);
++  enum machine_mode wider_mode = mode;
++
++  /* Try combined insns first.  */
++  do
++    {
++      enum insn_code icode;
++      PUT_MODE (test, wider_mode);
++
++      if (label)
++      {         
++        icode = cbranch_optab->handlers[(int) wider_mode].insn_code;
++        
++        if (icode != CODE_FOR_nothing
++            && (*insn_data[icode].operand[0].predicate) (test, wider_mode))
++          {
++            x = prepare_operand (icode, x, 1, mode, wider_mode, unsignedp);
++            y = prepare_operand (icode, y, 2, mode, wider_mode, unsignedp);
++            emit_jump_insn (GEN_FCN (icode) (test, x, y, label));
++            return;
++          }
++      }
++
++      /* Handle some compares against zero.  */
++      icode = (int) tst_optab->handlers[(int) wider_mode].insn_code;
++      if (y == CONST0_RTX (mode) && icode != CODE_FOR_nothing)
++      {
++        x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
++        emit_insn (GEN_FCN (icode) (x));
++        if (label)
++          emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
++        return;
++      }
++
++      /* Handle compares for which there is a directly suitable insn.  */
++
++      icode = (int) cmp_optab->handlers[(int) wider_mode].insn_code;
++      if (icode != CODE_FOR_nothing)
++      {
++        x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
++        y = prepare_operand (icode, y, 1, mode, wider_mode, unsignedp);
++        emit_insn (GEN_FCN (icode) (x, y));
++        if (label)
++          emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
++        return;
++      }
++
++      if (class != MODE_INT && class != MODE_FLOAT
++        && class != MODE_COMPLEX_FLOAT)
++      break;
++
++      wider_mode = GET_MODE_WIDER_MODE (wider_mode);
++    }
++  while (wider_mode != VOIDmode);
++
++  abort ();
++}
++
++/* Generate code to compare X with Y so that the condition codes are
++   set and to jump to LABEL if the condition is true.  If X is a
++   constant and Y is not a constant, then the comparison is swapped to
++   ensure that the comparison RTL has the canonical form.
++
++   UNSIGNEDP nonzero says that X and Y are unsigned; this matters if they
++   need to be widened by emit_cmp_insn.  UNSIGNEDP is also used to select
++   the proper branch condition code.
++
++   If X and Y have mode BLKmode, then SIZE specifies the size of both X and Y.
++
++   MODE is the mode of the inputs (in case they are const_int).
++
++   COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).  It will
++   be passed unchanged to emit_cmp_insn, then potentially converted into an
++   unsigned variant based on UNSIGNEDP to select a proper jump instruction.  */
++
++void
++emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, label)
++     rtx x, y;
++     enum rtx_code comparison;
++     rtx size;
++     enum machine_mode mode;
++     int unsignedp;
++     rtx label;
++{
++  rtx op0 = x, op1 = y;
++
++  /* Swap operands and condition to ensure canonical RTL.  */
++  if (swap_commutative_operands_p (x, y))
++    {
++      /* If we're not emitting a branch, this means some caller
++         is out of sync.  */
++      if (! label)
++      abort ();
++
++      op0 = y, op1 = x;
++      comparison = swap_condition (comparison);
++    }
++
++#ifdef HAVE_cc0
++  /* If OP0 is still a constant, then both X and Y must be constants.  Force
++     X into a register to avoid aborting in emit_cmp_insn due to non-canonical
++     RTL.  */
++  if (CONSTANT_P (op0))
++    op0 = force_reg (mode, op0);
++#endif
++
++  emit_queue ();
++  if (unsignedp)
++    comparison = unsigned_condition (comparison);
++
++  prepare_cmp_insn (&op0, &op1, &comparison, size, &mode, &unsignedp,
++                  ccp_jump);
++  emit_cmp_and_jump_insn_1 (op0, op1, mode, comparison, unsignedp, label);
++}
++
++/* Like emit_cmp_and_jump_insns, but generate only the comparison.  */
++
++void
++emit_cmp_insn (x, y, comparison, size, mode, unsignedp)
++     rtx x, y;
++     enum rtx_code comparison;
++     rtx size;
++     enum machine_mode mode;
++     int unsignedp;
++{
++  emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, 0);
++}
++\f
++/* Emit a library call comparison between floating point X and Y.
++   COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).  */
++
++static void
++prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp)
++     rtx *px, *py;
++     enum rtx_code *pcomparison;
++     enum machine_mode *pmode;
++     int *punsignedp;
++{
++  enum rtx_code comparison = *pcomparison;
++  rtx tmp;
++  rtx x = *px = protect_from_queue (*px, 0);
++  rtx y = *py = protect_from_queue (*py, 0);
++  enum machine_mode mode = GET_MODE (x);
++  rtx libfunc = 0;
++  rtx result;
++
++  if (mode == HFmode)
++    switch (comparison)
++      {
++      case EQ:
++      libfunc = eqhf2_libfunc;
++      break;
++
++      case NE:
++      libfunc = nehf2_libfunc;
++      break;
++
++      case GT:
++      libfunc = gthf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LT;
++          libfunc = lthf2_libfunc;
++        }
++      break;
++
++      case GE:
++      libfunc = gehf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LE;
++          libfunc = lehf2_libfunc;
++        }
++      break;
++
++      case LT:
++      libfunc = lthf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GT;
++          libfunc = gthf2_libfunc;
++        }
++      break;
++
++      case LE:
++      libfunc = lehf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GE;
++          libfunc = gehf2_libfunc;
++        }
++      break;
++
++      case UNORDERED:
++      libfunc = unordhf2_libfunc;
++      break;
++
++      default:
++      break;
++      }
++  else if (mode == SFmode)
++    switch (comparison)
++      {
++      case EQ:
++      libfunc = eqsf2_libfunc;
++      break;
++
++      case NE:
++      libfunc = nesf2_libfunc;
++      break;
++
++      case GT:
++      libfunc = gtsf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LT;
++          libfunc = ltsf2_libfunc;
++        }
++      break;
++
++      case GE:
++      libfunc = gesf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LE;
++          libfunc = lesf2_libfunc;
++        }
++      break;
++
++      case LT:
++      libfunc = ltsf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GT;
++          libfunc = gtsf2_libfunc;
++        }
++      break;
++
++      case LE:
++      libfunc = lesf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GE;
++          libfunc = gesf2_libfunc;
++        }
++      break;
++
++      case UNORDERED:
++      libfunc = unordsf2_libfunc;
++      break;
++
++      default:
++      break;
++      }
++  else if (mode == DFmode)
++    switch (comparison)
++      {
++      case EQ:
++      libfunc = eqdf2_libfunc;
++      break;
++
++      case NE:
++      libfunc = nedf2_libfunc;
++      break;
++
++      case GT:
++      libfunc = gtdf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LT;
++          libfunc = ltdf2_libfunc;
++        }
++      break;
++
++      case GE:
++      libfunc = gedf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LE;
++          libfunc = ledf2_libfunc;
++        }
++      break;
++
++      case LT:
++      libfunc = ltdf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GT;
++          libfunc = gtdf2_libfunc;
++        }
++      break;
++
++      case LE:
++      libfunc = ledf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GE;
++          libfunc = gedf2_libfunc;
++        }
++      break;
++
++      case UNORDERED:
++      libfunc = unorddf2_libfunc;
++      break;
++
++      default:
++      break;
++      }
++  else if (mode == XFmode)
++    switch (comparison)
++      {
++      case EQ:
++      libfunc = eqxf2_libfunc;
++      break;
++
++      case NE:
++      libfunc = nexf2_libfunc;
++      break;
++
++      case GT:
++      libfunc = gtxf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LT;
++          libfunc = ltxf2_libfunc;
++        }
++      break;
++
++      case GE:
++      libfunc = gexf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LE;
++          libfunc = lexf2_libfunc;
++        }
++      break;
++
++      case LT:
++      libfunc = ltxf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GT;
++          libfunc = gtxf2_libfunc;
++        }
++      break;
++
++      case LE:
++      libfunc = lexf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GE;
++          libfunc = gexf2_libfunc;
++        }
++      break;
++
++      case UNORDERED:
++      libfunc = unordxf2_libfunc;
++      break;
++
++      default:
++      break;
++      }
++  else if (mode == TFmode)
++    switch (comparison)
++      {
++      case EQ:
++      libfunc = eqtf2_libfunc;
++      break;
++
++      case NE:
++      libfunc = netf2_libfunc;
++      break;
++
++      case GT:
++      libfunc = gttf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LT;
++          libfunc = lttf2_libfunc;
++        }
++      break;
++
++      case GE:
++      libfunc = getf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = LE;
++          libfunc = letf2_libfunc;
++        }
++      break;
++
++      case LT:
++      libfunc = lttf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GT;
++          libfunc = gttf2_libfunc;
++        }
++      break;
++
++      case LE:
++      libfunc = letf2_libfunc;
++      if (libfunc == NULL_RTX)
++        {
++          tmp = x; x = y; y = tmp;
++          *pcomparison = GE;
++          libfunc = getf2_libfunc;
++        }
++      break;
++
++      case UNORDERED:
++      libfunc = unordtf2_libfunc;
++      break;
++
++      default:
++      break;
++      }
++  else
++    {
++      enum machine_mode wider_mode;
++
++      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
++         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
++      {
++        if ((cmp_optab->handlers[(int) wider_mode].insn_code
++             != CODE_FOR_nothing)
++            || (cmp_optab->handlers[(int) wider_mode].libfunc != 0))
++          {
++            x = protect_from_queue (x, 0);
++            y = protect_from_queue (y, 0);
++            *px = convert_to_mode (wider_mode, x, 0);
++            *py = convert_to_mode (wider_mode, y, 0);
++            prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
++            return;
++          }
++      }
++      abort ();
++    }
++
++  if (libfunc == 0)
++    abort ();
++
++  result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
++                                  word_mode, 2, x, mode, y, mode);
++  *px = result;
++  *py = const0_rtx;
++  *pmode = word_mode;
++  if (comparison == UNORDERED)
++    *pcomparison = NE;
++#ifdef FLOAT_LIB_COMPARE_RETURNS_BOOL
++  else if (FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
++    *pcomparison = NE;
++#endif
++  *punsignedp = 0;
++}
++\f
++/* Generate code to indirectly jump to a location given in the rtx LOC.  */
++
++void
++emit_indirect_jump (loc)
++     rtx loc;
++{
++  if (! ((*insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate)
++       (loc, Pmode)))
++    loc = copy_to_mode_reg (Pmode, loc);
++
++  emit_jump_insn (gen_indirect_jump (loc));
++  emit_barrier ();
++}
++\f
++#ifdef HAVE_conditional_move
++
++/* Emit a conditional move instruction if the machine supports one for that
++   condition and machine mode.
++
++   OP0 and OP1 are the operands that should be compared using CODE.  CMODE is
++   the mode to use should they be constants.  If it is VOIDmode, they cannot
++   both be constants.
++
++   OP2 should be stored in TARGET if the comparison is true, otherwise OP3
++   should be stored there.  MODE is the mode to use should they be constants.
++   If it is VOIDmode, they cannot both be constants.
++
++   The result is either TARGET (perhaps modified) or NULL_RTX if the operation
++   is not supported.  */
++
++rtx
++emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
++                     unsignedp)
++     rtx target;
++     enum rtx_code code;
++     rtx op0, op1;
++     enum machine_mode cmode;
++     rtx op2, op3;
++     enum machine_mode mode;
++     int unsignedp;
++{
++  rtx tem, subtarget, comparison, insn;
++  enum insn_code icode;
++  enum rtx_code reversed;
++
++  /* If one operand is constant, make it the second one.  Only do this
++     if the other operand is not constant as well.  */
++
++  if (swap_commutative_operands_p (op0, op1))
++    {
++      tem = op0;
++      op0 = op1;
++      op1 = tem;
++      code = swap_condition (code);
++    }
++
++  /* get_condition will prefer to generate LT and GT even if the old
++     comparison was against zero, so undo that canonicalization here since
++     comparisons against zero are cheaper.  */
++  if (code == LT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == 1)
++    code = LE, op1 = const0_rtx;
++  else if (code == GT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == -1)
++    code = GE, op1 = const0_rtx;
++
++  if (cmode == VOIDmode)
++    cmode = GET_MODE (op0);
++
++  if (swap_commutative_operands_p (op2, op3)
++      && ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL))
++          != UNKNOWN))
++    {
++      tem = op2;
++      op2 = op3;
++      op3 = tem;
++      code = reversed;
++    }
++
++  if (mode == VOIDmode)
++    mode = GET_MODE (op2);
++
++  icode = movcc_gen_code[mode];
++
++  if (icode == CODE_FOR_nothing)
++    return 0;
++
++  if (flag_force_mem)
++    {
++      op2 = force_not_mem (op2);
++      op3 = force_not_mem (op3);
++    }
++
++  if (target)
++    target = protect_from_queue (target, 1);
++  else
++    target = gen_reg_rtx (mode);
++
++  subtarget = target;
++
++  emit_queue ();
++
++  op2 = protect_from_queue (op2, 0);
++  op3 = protect_from_queue (op3, 0);
++
++  /* If the insn doesn't accept these operands, put them in pseudos.  */
++
++  if (! (*insn_data[icode].operand[0].predicate)
++      (subtarget, insn_data[icode].operand[0].mode))
++    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
++
++  if (! (*insn_data[icode].operand[2].predicate)
++      (op2, insn_data[icode].operand[2].mode))
++    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
++
++  if (! (*insn_data[icode].operand[3].predicate)
++      (op3, insn_data[icode].operand[3].mode))
++    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
++
++  /* Everything should now be in the suitable form, so emit the compare insn
++     and then the conditional move.  */
++
++  comparison 
++    = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX);
++
++  /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)?  */
++  /* We can get const0_rtx or const_true_rtx in some circumstances.  Just
++     return NULL and let the caller figure out how best to deal with this
++     situation.  */
++  if (GET_CODE (comparison) != code)
++    return NULL_RTX;
++  
++  insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
++
++  /* If that failed, then give up.  */
++  if (insn == 0)
++    return 0;
++
++  emit_insn (insn);
++
++  if (subtarget != target)
++    convert_move (target, subtarget, 0);
++
++  return target;
++}
++
++/* Return nonzero if a conditional move of mode MODE is supported.
++
++   This function is for combine so it can tell whether an insn that looks
++   like a conditional move is actually supported by the hardware.  If we
++   guess wrong we lose a bit on optimization, but that's it.  */
++/* ??? sparc64 supports conditionally moving integers values based on fp
++   comparisons, and vice versa.  How do we handle them?  */
++
++int
++can_conditionally_move_p (mode)
++     enum machine_mode mode;
++{
++  if (movcc_gen_code[mode] != CODE_FOR_nothing)
++    return 1;
++
++  return 0;
++}
++
++#endif /* HAVE_conditional_move */
++\f
++/* These functions generate an insn body and return it
++   rather than emitting the insn.
++
++   They do not protect from queued increments,
++   because they may be used 1) in protect_from_queue itself
++   and 2) in other passes where there is no queue.  */
++
++/* Generate and return an insn body to add Y to X.  */
++
++rtx
++gen_add2_insn (x, y)
++     rtx x, y;
++{
++  int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; 
++
++  if (! ((*insn_data[icode].operand[0].predicate)
++       (x, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (x, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (y, insn_data[icode].operand[2].mode)))
++    abort ();
++
++  return (GEN_FCN (icode) (x, x, y));
++}
++
++/* Generate and return an insn body to add r1 and c,
++   storing the result in r0.  */
++rtx
++gen_add3_insn (r0, r1, c)
++     rtx r0, r1, c;
++{
++  int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
++
++  if (icode == CODE_FOR_nothing
++      || ! ((*insn_data[icode].operand[0].predicate)
++          (r0, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (r1, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (c, insn_data[icode].operand[2].mode)))
++    return NULL_RTX;
++
++  return (GEN_FCN (icode) (r0, r1, c));
++}
++
++int
++have_add2_insn (x, y)
++     rtx x, y;
++{
++  int icode;
++
++  if (GET_MODE (x) == VOIDmode)
++    abort ();
++
++  icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; 
++
++  if (icode == CODE_FOR_nothing)
++    return 0;
++
++  if (! ((*insn_data[icode].operand[0].predicate)
++       (x, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (x, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (y, insn_data[icode].operand[2].mode)))
++    return 0;
++
++  return 1;
++}
++
++/* Generate and return an insn body to subtract Y from X.  */
++
++rtx
++gen_sub2_insn (x, y)
++     rtx x, y;
++{
++  int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; 
++
++  if (! ((*insn_data[icode].operand[0].predicate)
++       (x, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (x, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (y, insn_data[icode].operand[2].mode)))
++    abort ();
++
++  return (GEN_FCN (icode) (x, x, y));
++}
++
++/* Generate and return an insn body to subtract r1 and c,
++   storing the result in r0.  */
++rtx
++gen_sub3_insn (r0, r1, c)
++     rtx r0, r1, c;
++{
++  int icode = (int) sub_optab->handlers[(int) GET_MODE (r0)].insn_code;
++
++  if (icode == CODE_FOR_nothing
++      || ! ((*insn_data[icode].operand[0].predicate)
++          (r0, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (r1, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (c, insn_data[icode].operand[2].mode)))
++    return NULL_RTX;
++
++  return (GEN_FCN (icode) (r0, r1, c));
++}
++
++int
++have_sub2_insn (x, y)
++     rtx x, y;
++{
++  int icode;
++
++  if (GET_MODE (x) == VOIDmode)
++    abort ();
++
++  icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; 
++
++  if (icode == CODE_FOR_nothing)
++    return 0;
++
++  if (! ((*insn_data[icode].operand[0].predicate)
++       (x, insn_data[icode].operand[0].mode))
++      || ! ((*insn_data[icode].operand[1].predicate)
++          (x, insn_data[icode].operand[1].mode))
++      || ! ((*insn_data[icode].operand[2].predicate)
++          (y, insn_data[icode].operand[2].mode)))
++    return 0;
++
++  return 1;
++}
++
++/* Generate the body of an instruction to copy Y into X.
++   It may be a list of insns, if one insn isn't enough.  */
++
++rtx
++gen_move_insn (x, y)
++     rtx x, y;
++{
++  enum machine_mode mode = GET_MODE (x);
++  enum insn_code insn_code;
++  rtx seq;
++
++  if (mode == VOIDmode)
++    mode = GET_MODE (y); 
++
++  insn_code = mov_optab->handlers[(int) mode].insn_code;
++
++  /* Handle MODE_CC modes:  If we don't have a special move insn for this mode,
++     find a mode to do it in.  If we have a movcc, use it.  Otherwise,
++     find the MODE_INT mode of the same width.  */
++
++  if (GET_MODE_CLASS (mode) == MODE_CC && insn_code == CODE_FOR_nothing)
++    {
++      enum machine_mode tmode = VOIDmode;
++      rtx x1 = x, y1 = y;
++
++      if (mode != CCmode
++        && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
++      tmode = CCmode;
++      else
++      for (tmode = QImode; tmode != VOIDmode;
++           tmode = GET_MODE_WIDER_MODE (tmode))
++        if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
++          break;
++
++      if (tmode == VOIDmode)
++      abort ();
++
++      /* Get X and Y in TMODE.  We can't use gen_lowpart here because it
++       may call change_address which is not appropriate if we were
++       called when a reload was in progress.  We don't have to worry
++       about changing the address since the size in bytes is supposed to
++       be the same.  Copy the MEM to change the mode and move any
++       substitutions from the old MEM to the new one.  */
++
++      if (reload_in_progress)
++      {
++        x = gen_lowpart_common (tmode, x1);
++        if (x == 0 && GET_CODE (x1) == MEM)
++          {
++            x = adjust_address_nv (x1, tmode, 0);
++            copy_replacements (x1, x);
++          }
++
++        y = gen_lowpart_common (tmode, y1);
++        if (y == 0 && GET_CODE (y1) == MEM)
++          {
++            y = adjust_address_nv (y1, tmode, 0);
++            copy_replacements (y1, y);
++          }
++      }
++      else
++      {
++        x = gen_lowpart (tmode, x);
++        y = gen_lowpart (tmode, y);
++      }
++        
++      insn_code = mov_optab->handlers[(int) tmode].insn_code;
++      return (GEN_FCN (insn_code) (x, y));
++    }
++
++  start_sequence ();
++  emit_move_insn_1 (x, y);
++  seq = get_insns ();
++  end_sequence ();
++  return seq;
++}
++\f
++/* Return the insn code used to extend FROM_MODE to TO_MODE.
++   UNSIGNEDP specifies zero-extension instead of sign-extension.  If
++   no such operation exists, CODE_FOR_nothing will be returned.  */
++
++enum insn_code
++can_extend_p (to_mode, from_mode, unsignedp)
++     enum machine_mode to_mode, from_mode;
++     int unsignedp;
++{
++#ifdef HAVE_ptr_extend
++  if (unsignedp < 0)
++    return CODE_FOR_ptr_extend;
++  else
++#endif
++    return extendtab[(int) to_mode][(int) from_mode][unsignedp != 0];
++}
++
++/* Generate the body of an insn to extend Y (with mode MFROM)
++   into X (with mode MTO).  Do zero-extension if UNSIGNEDP is nonzero.  */
++
++rtx
++gen_extend_insn (x, y, mto, mfrom, unsignedp)
++     rtx x, y;
++     enum machine_mode mto, mfrom;
++     int unsignedp;
++{
++  return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp != 0]) (x, y));
++}
++\f
++/* can_fix_p and can_float_p say whether the target machine
++   can directly convert a given fixed point type to
++   a given floating point type, or vice versa.
++   The returned value is the CODE_FOR_... value to use,
++   or CODE_FOR_nothing if these modes cannot be directly converted.
++
++   *TRUNCP_PTR is set to 1 if it is necessary to output
++   an explicit FTRUNC insn before the fix insn; otherwise 0.  */
++
++static enum insn_code
++can_fix_p (fixmode, fltmode, unsignedp, truncp_ptr)
++     enum machine_mode fltmode, fixmode;
++     int unsignedp;
++     int *truncp_ptr;
++{
++  *truncp_ptr = 0;
++  if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0]
++      != CODE_FOR_nothing)
++    return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0];
++
++  if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing)
++    {
++      *truncp_ptr = 1;
++      return fixtab[(int) fltmode][(int) fixmode][unsignedp != 0];
++    }
++  return CODE_FOR_nothing;
++}
++
++static enum insn_code
++can_float_p (fltmode, fixmode, unsignedp)
++     enum machine_mode fixmode, fltmode;
++     int unsignedp;
++{
++  return floattab[(int) fltmode][(int) fixmode][unsignedp != 0];
++}
++\f
++/* Generate code to convert FROM to floating point
++   and store in TO.  FROM must be fixed point and not VOIDmode.
++   UNSIGNEDP nonzero means regard FROM as unsigned.
++   Normally this is done by correcting the final value
++   if it is negative.  */
++
++void
++expand_float (to, from, unsignedp)
++     rtx to, from;
++     int unsignedp;
++{
++  enum insn_code icode;
++  rtx target = to;
++  enum machine_mode fmode, imode;
++
++  /* Crash now, because we won't be able to decide which mode to use.  */
++  if (GET_MODE (from) == VOIDmode)
++    abort ();
++
++  /* Look for an insn to do the conversion.  Do it in the specified
++     modes if possible; otherwise convert either input, output or both to
++     wider mode.  If the integer mode is wider than the mode of FROM,
++     we can do the conversion signed even if the input is unsigned.  */
++
++  for (fmode = GET_MODE (to); fmode != VOIDmode;
++       fmode = GET_MODE_WIDER_MODE (fmode))
++    for (imode = GET_MODE (from); imode != VOIDmode;
++       imode = GET_MODE_WIDER_MODE (imode))
++      {
++      int doing_unsigned = unsignedp;
++
++      if (fmode != GET_MODE (to)
++          && significand_size (fmode) < GET_MODE_BITSIZE (GET_MODE (from)))
++        continue;
++
++      icode = can_float_p (fmode, imode, unsignedp);
++      if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp)
++        icode = can_float_p (fmode, imode, 0), doing_unsigned = 0;
++
++      if (icode != CODE_FOR_nothing)
++        {
++          to = protect_from_queue (to, 1);
++          from = protect_from_queue (from, 0);
++
++          if (imode != GET_MODE (from))
++            from = convert_to_mode (imode, from, unsignedp);
++
++          if (fmode != GET_MODE (to))
++            target = gen_reg_rtx (fmode);
++
++          emit_unop_insn (icode, target, from,
++                          doing_unsigned ? UNSIGNED_FLOAT : FLOAT);
++
++          if (target != to)
++            convert_move (to, target, 0);
++          return;
++        }
++      }
++
++  /* Unsigned integer, and no way to convert directly.
++     Convert as signed, then conditionally adjust the result.  */
++  if (unsignedp)
++    {
++      rtx label = gen_label_rtx ();
++      rtx temp;
++      REAL_VALUE_TYPE offset;
++
++      emit_queue ();
++
++      to = protect_from_queue (to, 1);
++      from = protect_from_queue (from, 0);
++
++      if (flag_force_mem)
++      from = force_not_mem (from);
++
++      /* Look for a usable floating mode FMODE wider than the source and at
++       least as wide as the target.  Using FMODE will avoid rounding woes
++       with unsigned values greater than the signed maximum value.  */
++
++      for (fmode = GET_MODE (to);  fmode != VOIDmode;
++         fmode = GET_MODE_WIDER_MODE (fmode))
++      if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
++          && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
++        break;
++
++      if (fmode == VOIDmode)
++      {
++        /* There is no such mode.  Pretend the target is wide enough.  */
++        fmode = GET_MODE (to);
++
++        /* Avoid double-rounding when TO is narrower than FROM.  */
++        if ((significand_size (fmode) + 1)
++            < GET_MODE_BITSIZE (GET_MODE (from)))
++          {
++            rtx temp1;
++            rtx neglabel = gen_label_rtx ();
++
++            /* Don't use TARGET if it isn't a register, is a hard register, 
++               or is the wrong mode.  */
++            if (GET_CODE (target) != REG
++                || REGNO (target) < FIRST_PSEUDO_REGISTER
++                || GET_MODE (target) != fmode)
++              target = gen_reg_rtx (fmode);
++
++            imode = GET_MODE (from);
++            do_pending_stack_adjust ();
++
++            /* Test whether the sign bit is set.  */
++            emit_cmp_and_jump_insns (from, const0_rtx, LT, NULL_RTX, imode,
++                                     0, neglabel);
++
++            /* The sign bit is not set.  Convert as signed.  */
++            expand_float (target, from, 0);
++            emit_jump_insn (gen_jump (label));
++            emit_barrier ();
++
++            /* The sign bit is set.
++               Convert to a usable (positive signed) value by shifting right
++               one bit, while remembering if a nonzero bit was shifted
++               out; i.e., compute  (from & 1) | (from >> 1).  */
++
++            emit_label (neglabel);
++            temp = expand_binop (imode, and_optab, from, const1_rtx,
++                                 NULL_RTX, 1, OPTAB_LIB_WIDEN);
++            temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node,
++                                  NULL_RTX, 1);
++            temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1, 
++                                 OPTAB_LIB_WIDEN);
++            expand_float (target, temp, 0);
++
++            /* Multiply by 2 to undo the shift above.  */
++            temp = expand_binop (fmode, add_optab, target, target,
++                                 target, 0, OPTAB_LIB_WIDEN);
++            if (temp != target)
++              emit_move_insn (target, temp);
++
++            do_pending_stack_adjust ();
++            emit_label (label);
++            goto done;
++          }
++      }
++
++      /* If we are about to do some arithmetic to correct for an
++       unsigned operand, do it in a pseudo-register.  */
++
++      if (GET_MODE (to) != fmode
++        || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER)
++      target = gen_reg_rtx (fmode);
++
++      /* Convert as signed integer to floating.  */
++      expand_float (target, from, 0);
++
++      /* If FROM is negative (and therefore TO is negative),
++       correct its value by 2**bitwidth.  */
++
++      do_pending_stack_adjust ();
++      emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from),
++                             0, label);
++
++      
++      real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)));
++      temp = expand_binop (fmode, add_optab, target,
++                         CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
++                         target, 0, OPTAB_LIB_WIDEN);
++      if (temp != target)
++      emit_move_insn (target, temp);
++
++      do_pending_stack_adjust ();
++      emit_label (label);
++      goto done;
++    }
++
++  /* No hardware instruction available; call a library routine to convert from
++     SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode.  */
++    {
++      rtx libfcn;
++      rtx insns;
++      rtx value;
++
++      to = protect_from_queue (to, 1);
++      from = protect_from_queue (from, 0);
++
++      if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
++      from = convert_to_mode (SImode, from, unsignedp);
++
++      if (flag_force_mem)
++      from = force_not_mem (from);
++
++      if (GET_MODE (to) == SFmode)
++      {
++        if (GET_MODE (from) == SImode)
++          libfcn = floatsisf_libfunc;
++        else if (GET_MODE (from) == DImode)
++          libfcn = floatdisf_libfunc;
++        else if (GET_MODE (from) == TImode)
++          libfcn = floattisf_libfunc;
++        else
++          abort ();
++      }
++      else if (GET_MODE (to) == DFmode)
++      {
++        if (GET_MODE (from) == SImode)
++          libfcn = floatsidf_libfunc;
++        else if (GET_MODE (from) == DImode)
++          libfcn = floatdidf_libfunc;
++        else if (GET_MODE (from) == TImode)
++          libfcn = floattidf_libfunc;
++        else
++          abort ();
++      }
++      else if (GET_MODE (to) == XFmode)
++      {
++        if (GET_MODE (from) == SImode)
++          libfcn = floatsixf_libfunc;
++        else if (GET_MODE (from) == DImode)
++          libfcn = floatdixf_libfunc;
++        else if (GET_MODE (from) == TImode)
++          libfcn = floattixf_libfunc;
++        else
++          abort ();
++      }
++      else if (GET_MODE (to) == TFmode)
++      {
++        if (GET_MODE (from) == SImode)
++          libfcn = floatsitf_libfunc;
++        else if (GET_MODE (from) == DImode)
++          libfcn = floatditf_libfunc;
++        else if (GET_MODE (from) == TImode)
++          libfcn = floattitf_libfunc;
++        else
++          abort ();
++      }
++      else
++      abort ();
++
++      start_sequence ();
++
++      value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
++                                     GET_MODE (to), 1, from,
++                                     GET_MODE (from));
++      insns = get_insns ();
++      end_sequence ();
++
++      emit_libcall_block (insns, target, value,
++                        gen_rtx_FLOAT (GET_MODE (to), from));
++    }
++
++ done:
++
++  /* Copy result to requested destination
++     if we have been computing in a temp location.  */
++
++  if (target != to)
++    {
++      if (GET_MODE (target) == GET_MODE (to))
++      emit_move_insn (to, target);
++      else
++      convert_move (to, target, 0);
++    }
++}
++\f
++/* expand_fix: generate code to convert FROM to fixed point
++   and store in TO.  FROM must be floating point.  */
++
++static rtx
++ftruncify (x)
++     rtx x;
++{
++  rtx temp = gen_reg_rtx (GET_MODE (x));
++  return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
++}
++
++void
++expand_fix (to, from, unsignedp)
++     rtx to, from;
++     int unsignedp;
++{
++  enum insn_code icode;
++  rtx target = to;
++  enum machine_mode fmode, imode;
++  int must_trunc = 0;
++  rtx libfcn = 0;
++
++  /* We first try to find a pair of modes, one real and one integer, at
++     least as wide as FROM and TO, respectively, in which we can open-code
++     this conversion.  If the integer mode is wider than the mode of TO,
++     we can do the conversion either signed or unsigned.  */
++
++  for (fmode = GET_MODE (from); fmode != VOIDmode;
++       fmode = GET_MODE_WIDER_MODE (fmode))
++    for (imode = GET_MODE (to); imode != VOIDmode;
++       imode = GET_MODE_WIDER_MODE (imode))
++      {
++      int doing_unsigned = unsignedp;
++
++      icode = can_fix_p (imode, fmode, unsignedp, &must_trunc);
++      if (icode == CODE_FOR_nothing && imode != GET_MODE (to) && unsignedp)
++        icode = can_fix_p (imode, fmode, 0, &must_trunc), doing_unsigned = 0;
++
++      if (icode != CODE_FOR_nothing)
++        {
++          to = protect_from_queue (to, 1);
++          from = protect_from_queue (from, 0);
++
++          if (fmode != GET_MODE (from))
++            from = convert_to_mode (fmode, from, 0);
++
++          if (must_trunc)
++            from = ftruncify (from);
++
++          if (imode != GET_MODE (to))
++            target = gen_reg_rtx (imode);
++
++          emit_unop_insn (icode, target, from,
++                          doing_unsigned ? UNSIGNED_FIX : FIX);
++          if (target != to)
++            convert_move (to, target, unsignedp);
++          return;
++        }
++      }
++
++  /* For an unsigned conversion, there is one more way to do it.
++     If we have a signed conversion, we generate code that compares
++     the real value to the largest representable positive number.  If if
++     is smaller, the conversion is done normally.  Otherwise, subtract
++     one plus the highest signed number, convert, and add it back.
++
++     We only need to check all real modes, since we know we didn't find
++     anything with a wider integer mode.  */
++
++  if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
++    for (fmode = GET_MODE (from); fmode != VOIDmode;
++       fmode = GET_MODE_WIDER_MODE (fmode))
++      /* Make sure we won't lose significant bits doing this.  */
++      if (GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))
++        && CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0,
++                                          &must_trunc))
++      {
++        int bitsize;
++        REAL_VALUE_TYPE offset;
++        rtx limit, lab1, lab2, insn;
++
++        bitsize = GET_MODE_BITSIZE (GET_MODE (to));
++        real_2expN (&offset, bitsize - 1);
++        limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
++        lab1 = gen_label_rtx ();
++        lab2 = gen_label_rtx ();
++
++        emit_queue ();
++        to = protect_from_queue (to, 1);
++        from = protect_from_queue (from, 0);
++
++        if (flag_force_mem)
++          from = force_not_mem (from);
++
++        if (fmode != GET_MODE (from))
++          from = convert_to_mode (fmode, from, 0);
++
++        /* See if we need to do the subtraction.  */
++        do_pending_stack_adjust ();
++        emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from),
++                                 0, lab1);
++
++        /* If not, do the signed "fix" and branch around fixup code.  */
++        expand_fix (to, from, 0);
++        emit_jump_insn (gen_jump (lab2));
++        emit_barrier ();
++
++        /* Otherwise, subtract 2**(N-1), convert to signed number,
++           then add 2**(N-1).  Do the addition using XOR since this
++           will often generate better code.  */
++        emit_label (lab1);
++        target = expand_binop (GET_MODE (from), sub_optab, from, limit,
++                               NULL_RTX, 0, OPTAB_LIB_WIDEN);
++        expand_fix (to, target, 0);
++        target = expand_binop (GET_MODE (to), xor_optab, to,
++                               gen_int_mode
++                               ((HOST_WIDE_INT) 1 << (bitsize - 1),
++                                GET_MODE (to)),
++                               to, 1, OPTAB_LIB_WIDEN);
++
++        if (target != to)
++          emit_move_insn (to, target);
++
++        emit_label (lab2);
++
++        if (mov_optab->handlers[(int) GET_MODE (to)].insn_code
++            != CODE_FOR_nothing)
++          {
++            /* Make a place for a REG_NOTE and add it.  */
++            insn = emit_move_insn (to, to);
++            set_unique_reg_note (insn,
++                                 REG_EQUAL,
++                                 gen_rtx_fmt_e (UNSIGNED_FIX,
++                                                GET_MODE (to),
++                                                copy_rtx (from)));
++          }
++
++        return;
++      }
++
++  /* We can't do it with an insn, so use a library call.  But first ensure
++     that the mode of TO is at least as wide as SImode, since those are the
++     only library calls we know about.  */
++
++  if (GET_MODE_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode))
++    {
++      target = gen_reg_rtx (SImode);
++
++      expand_fix (target, from, unsignedp);
++    }
++  else if (GET_MODE (from) == SFmode)
++    {
++      if (GET_MODE (to) == SImode)
++      libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc;
++      else if (GET_MODE (to) == DImode)
++      libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc;
++      else if (GET_MODE (to) == TImode)
++      libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc;
++      else
++      abort ();
++    }
++  else if (GET_MODE (from) == DFmode)
++    {
++      if (GET_MODE (to) == SImode)
++      libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc;
++      else if (GET_MODE (to) == DImode)
++      libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc;
++      else if (GET_MODE (to) == TImode)
++      libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc;
++      else
++      abort ();
++    }
++  else if (GET_MODE (from) == XFmode)
++    {
++      if (GET_MODE (to) == SImode)
++      libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc;
++      else if (GET_MODE (to) == DImode)
++      libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc;
++      else if (GET_MODE (to) == TImode)
++      libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc;
++      else
++      abort ();
++    }
++  else if (GET_MODE (from) == TFmode)
++    {
++      if (GET_MODE (to) == SImode)
++      libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc;
++      else if (GET_MODE (to) == DImode)
++      libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc;
++      else if (GET_MODE (to) == TImode)
++      libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc;
++      else
++      abort ();
++    }
++  else
++    abort ();
++
++  if (libfcn)
++    {
++      rtx insns;
++      rtx value;
++
++      to = protect_from_queue (to, 1);
++      from = protect_from_queue (from, 0);
++
++      if (flag_force_mem)
++      from = force_not_mem (from);
++
++      start_sequence ();
++
++      value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
++                                     GET_MODE (to), 1, from,
++                                     GET_MODE (from));
++      insns = get_insns ();
++      end_sequence ();
++
++      emit_libcall_block (insns, target, value,
++                        gen_rtx_fmt_e (unsignedp ? UNSIGNED_FIX : FIX,
++                                       GET_MODE (to), from));
++    }
++      
++  if (target != to)
++    {
++      if (GET_MODE (to) == GET_MODE (target))
++        emit_move_insn (to, target);
++      else
++        convert_move (to, target, 0);
++    }
++}
++\f
++/* Report whether we have an instruction to perform the operation
++   specified by CODE on operands of mode MODE.  */
++int
++have_insn_for (code, mode)
++     enum rtx_code code;
++     enum machine_mode mode;
++{
++  return (code_to_optab[(int) code] != 0
++        && (code_to_optab[(int) code]->handlers[(int) mode].insn_code
++            != CODE_FOR_nothing));
++}
++
++/* Create a blank optab.  */
++static optab
++new_optab ()
++{
++  int i;
++  optab op = (optab) ggc_alloc (sizeof (struct optab));
++  for (i = 0; i < NUM_MACHINE_MODES; i++)
++    {
++      op->handlers[i].insn_code = CODE_FOR_nothing;
++      op->handlers[i].libfunc = 0;
++    }
++
++  return op;
++}
++
++/* Same, but fill in its code as CODE, and write it into the
++   code_to_optab table.  */
++static inline optab
++init_optab (code)
++     enum rtx_code code;
++{
++  optab op = new_optab ();
++  op->code = code;
++  code_to_optab[(int) code] = op;
++  return op;
++}
++
++/* Same, but fill in its code as CODE, and do _not_ write it into
++   the code_to_optab table.  */
++static inline optab
++init_optabv (code)
++     enum rtx_code code;
++{
++  optab op = new_optab ();
++  op->code = code;
++  return op;
++}
++
++/* Initialize the libfunc fields of an entire group of entries in some
++   optab.  Each entry is set equal to a string consisting of a leading
++   pair of underscores followed by a generic operation name followed by
++   a mode name (downshifted to lower case) followed by a single character
++   representing the number of operands for the given operation (which is
++   usually one of the characters '2', '3', or '4').
++
++   OPTABLE is the table in which libfunc fields are to be initialized.
++   FIRST_MODE is the first machine mode index in the given optab to
++     initialize.
++   LAST_MODE is the last machine mode index in the given optab to
++     initialize.
++   OPNAME is the generic (string) name of the operation.
++   SUFFIX is the character which specifies the number of operands for
++     the given generic operation.
++*/
++
++static void
++init_libfuncs (optable, first_mode, last_mode, opname, suffix)
++     optab optable;
++     int first_mode;
++     int last_mode;
++     const char *opname;
++     int suffix;
++{
++  int mode;
++  unsigned opname_len = strlen (opname);
++
++  for (mode = first_mode; (int) mode <= (int) last_mode;
++       mode = (enum machine_mode) ((int) mode + 1))
++    {
++      const char *mname = GET_MODE_NAME (mode);
++      unsigned mname_len = strlen (mname);
++      char *libfunc_name = alloca (2 + opname_len + mname_len + 1 + 1);
++      char *p;
++      const char *q;
++
++      p = libfunc_name;
++      *p++ = '_';
++      *p++ = '_';
++      for (q = opname; *q; )
++      *p++ = *q++;
++      for (q = mname; *q; q++)
++      *p++ = TOLOWER (*q);
++      *p++ = suffix;
++      *p = '\0';
++
++      optable->handlers[(int) mode].libfunc
++      = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (libfunc_name,
++                                                     p - libfunc_name));
++    }
++}
++
++/* Initialize the libfunc fields of an entire group of entries in some
++   optab which correspond to all integer mode operations.  The parameters
++   have the same meaning as similarly named ones for the `init_libfuncs'
++   routine.  (See above).  */
++
++static void
++init_integral_libfuncs (optable, opname, suffix)
++     optab optable;
++     const char *opname;
++     int suffix;
++{
++  init_libfuncs (optable, SImode, TImode, opname, suffix);
++}
++
++/* Initialize the libfunc fields of an entire group of entries in some
++   optab which correspond to all real mode operations.  The parameters
++   have the same meaning as similarly named ones for the `init_libfuncs'
++   routine.  (See above).  */
++
++static void
++init_floating_libfuncs (optable, opname, suffix)
++     optab optable;
++     const char *opname;
++     int suffix;
++{
++  init_libfuncs (optable, SFmode, TFmode, opname, suffix);
++}
++
++rtx
++init_one_libfunc (name)
++     const char *name;
++{
++  /* Create a FUNCTION_DECL that can be passed to
++     targetm.encode_section_info.  */
++  /* ??? We don't have any type information except for this is
++     a function.  Pretend this is "int foo()".  */
++  tree decl = build_decl (FUNCTION_DECL, get_identifier (name),
++                        build_function_type (integer_type_node, NULL_TREE));
++  DECL_ARTIFICIAL (decl) = 1;
++  DECL_EXTERNAL (decl) = 1;
++  TREE_PUBLIC (decl) = 1;
++
++  /* Return the symbol_ref from the mem rtx.  */
++  return XEXP (DECL_RTL (decl), 0);
++}
++
++/* Call this once to initialize the contents of the optabs
++   appropriately for the current target machine.  */
++
++void
++init_optabs ()
++{
++  unsigned int i, j, k;
++
++  /* Start by initializing all tables to contain CODE_FOR_nothing.  */
++
++  for (i = 0; i < ARRAY_SIZE (fixtab); i++)
++    for (j = 0; j < ARRAY_SIZE (fixtab[0]); j++)
++      for (k = 0; k < ARRAY_SIZE (fixtab[0][0]); k++)
++      fixtab[i][j][k] = CODE_FOR_nothing;
++
++  for (i = 0; i < ARRAY_SIZE (fixtrunctab); i++)
++    for (j = 0; j < ARRAY_SIZE (fixtrunctab[0]); j++)
++      for (k = 0; k < ARRAY_SIZE (fixtrunctab[0][0]); k++)
++      fixtrunctab[i][j][k] = CODE_FOR_nothing;
++
++  for (i = 0; i < ARRAY_SIZE (floattab); i++)
++    for (j = 0; j < ARRAY_SIZE (floattab[0]); j++)
++      for (k = 0; k < ARRAY_SIZE (floattab[0][0]); k++)
++      floattab[i][j][k] = CODE_FOR_nothing;
++
++  for (i = 0; i < ARRAY_SIZE (extendtab); i++)
++    for (j = 0; j < ARRAY_SIZE (extendtab[0]); j++)
++      for (k = 0; k < ARRAY_SIZE (extendtab[0][0]); k++)
++      extendtab[i][j][k] = CODE_FOR_nothing;
++
++  for (i = 0; i < NUM_RTX_CODE; i++)
++    setcc_gen_code[i] = CODE_FOR_nothing;
++
++#ifdef HAVE_conditional_move
++  for (i = 0; i < NUM_MACHINE_MODES; i++)
++    movcc_gen_code[i] = CODE_FOR_nothing;
++#endif
++
++  add_optab = init_optab (PLUS);
++  addv_optab = init_optabv (PLUS);
++  sub_optab = init_optab (MINUS);
++  subv_optab = init_optabv (MINUS);
++  smul_optab = init_optab (MULT);
++  smulv_optab = init_optabv (MULT);
++  smul_highpart_optab = init_optab (UNKNOWN);
++  umul_highpart_optab = init_optab (UNKNOWN);
++  smul_widen_optab = init_optab (UNKNOWN);
++  umul_widen_optab = init_optab (UNKNOWN);
++  sdiv_optab = init_optab (DIV);
++  sdivv_optab = init_optabv (DIV);
++  sdivmod_optab = init_optab (UNKNOWN);
++  udiv_optab = init_optab (UDIV);
++  udivmod_optab = init_optab (UNKNOWN);
++  smod_optab = init_optab (MOD);
++  umod_optab = init_optab (UMOD);
++  ftrunc_optab = init_optab (UNKNOWN);
++  and_optab = init_optab (AND);
++  ior_optab = init_optab (IOR);
++  xor_optab = init_optab (XOR);
++  ashl_optab = init_optab (ASHIFT);
++  ashr_optab = init_optab (ASHIFTRT);
++  lshr_optab = init_optab (LSHIFTRT);
++  rotl_optab = init_optab (ROTATE);
++  rotr_optab = init_optab (ROTATERT);
++  smin_optab = init_optab (SMIN);
++  smax_optab = init_optab (SMAX);
++  umin_optab = init_optab (UMIN);
++  umax_optab = init_optab (UMAX);
++
++  /* These three have codes assigned exclusively for the sake of
++     have_insn_for.  */
++  mov_optab = init_optab (SET);
++  movstrict_optab = init_optab (STRICT_LOW_PART);
++  cmp_optab = init_optab (COMPARE);
++
++  ucmp_optab = init_optab (UNKNOWN);
++  tst_optab = init_optab (UNKNOWN);
++  neg_optab = init_optab (NEG);
++  negv_optab = init_optabv (NEG);
++  abs_optab = init_optab (ABS);
++  absv_optab = init_optabv (ABS);
++  one_cmpl_optab = init_optab (NOT);
++  ffs_optab = init_optab (FFS);
++  sqrt_optab = init_optab (SQRT);
++  sin_optab = init_optab (UNKNOWN);
++  cos_optab = init_optab (UNKNOWN);
++  exp_optab = init_optab (UNKNOWN);
++  log_optab = init_optab (UNKNOWN);
++  strlen_optab = init_optab (UNKNOWN);
++  cbranch_optab = init_optab (UNKNOWN);
++  cmov_optab = init_optab (UNKNOWN);
++  cstore_optab = init_optab (UNKNOWN);
++  push_optab = init_optab (UNKNOWN);
++
++  for (i = 0; i < NUM_MACHINE_MODES; i++)
++    {
++      movstr_optab[i] = CODE_FOR_nothing;
++      clrstr_optab[i] = CODE_FOR_nothing;
++
++#ifdef HAVE_SECONDARY_RELOADS
++      reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
++#endif
++    }
++
++  /* Fill in the optabs with the insns we support.  */
++  init_all_optabs ();
++
++#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
++  /* This flag says the same insns that convert to a signed fixnum
++     also convert validly to an unsigned one.  */
++  for (i = 0; i < NUM_MACHINE_MODES; i++)
++    for (j = 0; j < NUM_MACHINE_MODES; j++)
++      fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
++#endif
++
++  /* Initialize the optabs with the names of the library functions.  */
++  init_integral_libfuncs (add_optab, "add", '3');
++  init_floating_libfuncs (add_optab, "add", '3');
++  init_integral_libfuncs (addv_optab, "addv", '3');
++  init_floating_libfuncs (addv_optab, "add", '3');
++  init_integral_libfuncs (sub_optab, "sub", '3');
++  init_floating_libfuncs (sub_optab, "sub", '3');
++  init_integral_libfuncs (subv_optab, "subv", '3');
++  init_floating_libfuncs (subv_optab, "sub", '3');
++  init_integral_libfuncs (smul_optab, "mul", '3');
++  init_floating_libfuncs (smul_optab, "mul", '3');
++  init_integral_libfuncs (smulv_optab, "mulv", '3');
++  init_floating_libfuncs (smulv_optab, "mul", '3');
++  init_integral_libfuncs (sdiv_optab, "div", '3');
++  init_floating_libfuncs (sdiv_optab, "div", '3');
++  init_integral_libfuncs (sdivv_optab, "divv", '3');
++  init_integral_libfuncs (udiv_optab, "udiv", '3');
++  init_integral_libfuncs (sdivmod_optab, "divmod", '4');
++  init_integral_libfuncs (udivmod_optab, "udivmod", '4');
++  init_integral_libfuncs (smod_optab, "mod", '3');
++  init_integral_libfuncs (umod_optab, "umod", '3');
++  init_floating_libfuncs (ftrunc_optab, "ftrunc", '2');
++  init_integral_libfuncs (and_optab, "and", '3');
++  init_integral_libfuncs (ior_optab, "ior", '3');
++  init_integral_libfuncs (xor_optab, "xor", '3');
++  init_integral_libfuncs (ashl_optab, "ashl", '3');
++  init_integral_libfuncs (ashr_optab, "ashr", '3');
++  init_integral_libfuncs (lshr_optab, "lshr", '3');
++  init_integral_libfuncs (smin_optab, "min", '3');
++  init_floating_libfuncs (smin_optab, "min", '3');
++  init_integral_libfuncs (smax_optab, "max", '3');
++  init_floating_libfuncs (smax_optab, "max", '3');
++  init_integral_libfuncs (umin_optab, "umin", '3');
++  init_integral_libfuncs (umax_optab, "umax", '3');
++  init_integral_libfuncs (neg_optab, "neg", '2');
++  init_floating_libfuncs (neg_optab, "neg", '2');
++  init_integral_libfuncs (negv_optab, "negv", '2');
++  init_floating_libfuncs (negv_optab, "neg", '2');
++  init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
++  init_integral_libfuncs (ffs_optab, "ffs", '2');
++
++  /* Comparison libcalls for integers MUST come in pairs, signed/unsigned.  */
++  init_integral_libfuncs (cmp_optab, "cmp", '2');
++  init_integral_libfuncs (ucmp_optab, "ucmp", '2');
++  init_floating_libfuncs (cmp_optab, "cmp", '2');
++
++#ifdef MULSI3_LIBCALL
++  smul_optab->handlers[(int) SImode].libfunc
++    = init_one_libfunc (MULSI3_LIBCALL);
++#endif
++#ifdef MULDI3_LIBCALL
++  smul_optab->handlers[(int) DImode].libfunc
++    = init_one_libfunc (MULDI3_LIBCALL);
++#endif
++
++#ifdef DIVSI3_LIBCALL
++  sdiv_optab->handlers[(int) SImode].libfunc
++    = init_one_libfunc (DIVSI3_LIBCALL);
++#endif
++#ifdef DIVDI3_LIBCALL
++  sdiv_optab->handlers[(int) DImode].libfunc
++    = init_one_libfunc (DIVDI3_LIBCALL);
++#endif
++
++#ifdef UDIVSI3_LIBCALL
++  udiv_optab->handlers[(int) SImode].libfunc
++    = init_one_libfunc (UDIVSI3_LIBCALL);
++#endif
++#ifdef UDIVDI3_LIBCALL
++  udiv_optab->handlers[(int) DImode].libfunc
++    = init_one_libfunc (UDIVDI3_LIBCALL);
++#endif
++
++#ifdef MODSI3_LIBCALL
++  smod_optab->handlers[(int) SImode].libfunc
++    = init_one_libfunc (MODSI3_LIBCALL);
++#endif
++#ifdef MODDI3_LIBCALL
++  smod_optab->handlers[(int) DImode].libfunc
++    = init_one_libfunc (MODDI3_LIBCALL);
++#endif
++
++#ifdef UMODSI3_LIBCALL
++  umod_optab->handlers[(int) SImode].libfunc
++    = init_one_libfunc (UMODSI3_LIBCALL);
++#endif
++#ifdef UMODDI3_LIBCALL
++  umod_optab->handlers[(int) DImode].libfunc
++    = init_one_libfunc (UMODDI3_LIBCALL);
++#endif
++
++  /* Use cabs for DC complex abs, since systems generally have cabs.
++     Don't define any libcall for SCmode, so that cabs will be used.  */
++  abs_optab->handlers[(int) DCmode].libfunc
++    = init_one_libfunc ("cabs");
++
++  /* The ffs function operates on `int'.  */
++  ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)].libfunc
++    = init_one_libfunc ("ffs");
++
++  extendsfdf2_libfunc = init_one_libfunc ("__extendsfdf2");
++  extendsfxf2_libfunc = init_one_libfunc ("__extendsfxf2");
++  extendsftf2_libfunc = init_one_libfunc ("__extendsftf2");
++  extenddfxf2_libfunc = init_one_libfunc ("__extenddfxf2");
++  extenddftf2_libfunc = init_one_libfunc ("__extenddftf2");
++
++  truncdfsf2_libfunc = init_one_libfunc ("__truncdfsf2");
++  truncxfsf2_libfunc = init_one_libfunc ("__truncxfsf2");
++  trunctfsf2_libfunc = init_one_libfunc ("__trunctfsf2");
++  truncxfdf2_libfunc = init_one_libfunc ("__truncxfdf2");
++  trunctfdf2_libfunc = init_one_libfunc ("__trunctfdf2");
++
++  abort_libfunc = init_one_libfunc ("abort");
++  memcpy_libfunc = init_one_libfunc ("memcpy");
++  memmove_libfunc = init_one_libfunc ("memmove");
++  bcopy_libfunc = init_one_libfunc ("bcopy");
++  memcmp_libfunc = init_one_libfunc ("memcmp");
++  bcmp_libfunc = init_one_libfunc ("__gcc_bcmp");
++  memset_libfunc = init_one_libfunc ("memset");
++  bzero_libfunc = init_one_libfunc ("bzero");
++
++  unwind_resume_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
++                                          ? "_Unwind_SjLj_Resume"
++                                          : "_Unwind_Resume");
++#ifndef DONT_USE_BUILTIN_SETJMP
++  setjmp_libfunc = init_one_libfunc ("__builtin_setjmp");
++  longjmp_libfunc = init_one_libfunc ("__builtin_longjmp");
++#else
++  setjmp_libfunc = init_one_libfunc ("setjmp");
++  longjmp_libfunc = init_one_libfunc ("longjmp");
++#endif
++  unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register");
++  unwind_sjlj_unregister_libfunc
++    = init_one_libfunc ("_Unwind_SjLj_Unregister");
++
++  eqhf2_libfunc = init_one_libfunc ("__eqhf2");
++  nehf2_libfunc = init_one_libfunc ("__nehf2");
++  gthf2_libfunc = init_one_libfunc ("__gthf2");
++  gehf2_libfunc = init_one_libfunc ("__gehf2");
++  lthf2_libfunc = init_one_libfunc ("__lthf2");
++  lehf2_libfunc = init_one_libfunc ("__lehf2");
++  unordhf2_libfunc = init_one_libfunc ("__unordhf2");
++
++  eqsf2_libfunc = init_one_libfunc ("__eqsf2");
++  nesf2_libfunc = init_one_libfunc ("__nesf2");
++  gtsf2_libfunc = init_one_libfunc ("__gtsf2");
++  gesf2_libfunc = init_one_libfunc ("__gesf2");
++  ltsf2_libfunc = init_one_libfunc ("__ltsf2");
++  lesf2_libfunc = init_one_libfunc ("__lesf2");
++  unordsf2_libfunc = init_one_libfunc ("__unordsf2");
++
++  eqdf2_libfunc = init_one_libfunc ("__eqdf2");
++  nedf2_libfunc = init_one_libfunc ("__nedf2");
++  gtdf2_libfunc = init_one_libfunc ("__gtdf2");
++  gedf2_libfunc = init_one_libfunc ("__gedf2");
++  ltdf2_libfunc = init_one_libfunc ("__ltdf2");
++  ledf2_libfunc = init_one_libfunc ("__ledf2");
++  unorddf2_libfunc = init_one_libfunc ("__unorddf2");
++
++  eqxf2_libfunc = init_one_libfunc ("__eqxf2");
++  nexf2_libfunc = init_one_libfunc ("__nexf2");
++  gtxf2_libfunc = init_one_libfunc ("__gtxf2");
++  gexf2_libfunc = init_one_libfunc ("__gexf2");
++  ltxf2_libfunc = init_one_libfunc ("__ltxf2");
++  lexf2_libfunc = init_one_libfunc ("__lexf2");
++  unordxf2_libfunc = init_one_libfunc ("__unordxf2");
++
++  eqtf2_libfunc = init_one_libfunc ("__eqtf2");
++  netf2_libfunc = init_one_libfunc ("__netf2");
++  gttf2_libfunc = init_one_libfunc ("__gttf2");
++  getf2_libfunc = init_one_libfunc ("__getf2");
++  lttf2_libfunc = init_one_libfunc ("__lttf2");
++  letf2_libfunc = init_one_libfunc ("__letf2");
++  unordtf2_libfunc = init_one_libfunc ("__unordtf2");
++
++  floatsisf_libfunc = init_one_libfunc ("__floatsisf");
++  floatdisf_libfunc = init_one_libfunc ("__floatdisf");
++  floattisf_libfunc = init_one_libfunc ("__floattisf");
++
++  floatsidf_libfunc = init_one_libfunc ("__floatsidf");
++  floatdidf_libfunc = init_one_libfunc ("__floatdidf");
++  floattidf_libfunc = init_one_libfunc ("__floattidf");
++
++  floatsixf_libfunc = init_one_libfunc ("__floatsixf");
++  floatdixf_libfunc = init_one_libfunc ("__floatdixf");
++  floattixf_libfunc = init_one_libfunc ("__floattixf");
++
++  floatsitf_libfunc = init_one_libfunc ("__floatsitf");
++  floatditf_libfunc = init_one_libfunc ("__floatditf");
++  floattitf_libfunc = init_one_libfunc ("__floattitf");
++
++  fixsfsi_libfunc = init_one_libfunc ("__fixsfsi");
++  fixsfdi_libfunc = init_one_libfunc ("__fixsfdi");
++  fixsfti_libfunc = init_one_libfunc ("__fixsfti");
++
++  fixdfsi_libfunc = init_one_libfunc ("__fixdfsi");
++  fixdfdi_libfunc = init_one_libfunc ("__fixdfdi");
++  fixdfti_libfunc = init_one_libfunc ("__fixdfti");
++
++  fixxfsi_libfunc = init_one_libfunc ("__fixxfsi");
++  fixxfdi_libfunc = init_one_libfunc ("__fixxfdi");
++  fixxfti_libfunc = init_one_libfunc ("__fixxfti");
++
++  fixtfsi_libfunc = init_one_libfunc ("__fixtfsi");
++  fixtfdi_libfunc = init_one_libfunc ("__fixtfdi");
++  fixtfti_libfunc = init_one_libfunc ("__fixtfti");
++
++  fixunssfsi_libfunc = init_one_libfunc ("__fixunssfsi");
++  fixunssfdi_libfunc = init_one_libfunc ("__fixunssfdi");
++  fixunssfti_libfunc = init_one_libfunc ("__fixunssfti");
++
++  fixunsdfsi_libfunc = init_one_libfunc ("__fixunsdfsi");
++  fixunsdfdi_libfunc = init_one_libfunc ("__fixunsdfdi");
++  fixunsdfti_libfunc = init_one_libfunc ("__fixunsdfti");
++
++  fixunsxfsi_libfunc = init_one_libfunc ("__fixunsxfsi");
++  fixunsxfdi_libfunc = init_one_libfunc ("__fixunsxfdi");
++  fixunsxfti_libfunc = init_one_libfunc ("__fixunsxfti");
++
++  fixunstfsi_libfunc = init_one_libfunc ("__fixunstfsi");
++  fixunstfdi_libfunc = init_one_libfunc ("__fixunstfdi");
++  fixunstfti_libfunc = init_one_libfunc ("__fixunstfti");
++
++  /* For function entry/exit instrumentation.  */
++  profile_function_entry_libfunc
++    = init_one_libfunc ("__cyg_profile_func_enter");
++  profile_function_exit_libfunc
++    = init_one_libfunc ("__cyg_profile_func_exit");
++
++  if (HAVE_conditional_trap)
++    trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX);
++
++#ifdef INIT_TARGET_OPTABS
++  /* Allow the target to add more libcalls or rename some, etc.  */
++  INIT_TARGET_OPTABS;
++#endif
++}
++\f
++/* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition
++   CODE.  Return 0 on failure.  */
++
++rtx
++gen_cond_trap (code, op1, op2, tcode)
++     enum rtx_code code ATTRIBUTE_UNUSED;
++     rtx op1, op2 ATTRIBUTE_UNUSED, tcode ATTRIBUTE_UNUSED;
++{
++  enum machine_mode mode = GET_MODE (op1);
++  enum insn_code icode;
++  rtx insn;
++
++  if (!HAVE_conditional_trap)
++    return 0;
++
++  if (mode == VOIDmode)
++    return 0;
++
++  icode = cmp_optab->handlers[(int) mode].insn_code;
++  if (icode == CODE_FOR_nothing)
++    return 0;
++
++  start_sequence ();
++  op1 = prepare_operand (icode, op1, 0, mode, mode, 0);
++  op2 = prepare_operand (icode, op2, 1, mode, mode, 0);
++  if (!op1 || !op2)
++    {
++      end_sequence ();
++      return 0;
++    }
++  emit_insn (GEN_FCN (icode) (op1, op2));
++
++  PUT_CODE (trap_rtx, code);
++  insn = gen_conditional_trap (trap_rtx, tcode);
++  if (insn)
++    {
++      emit_insn (insn);
++      insn = get_insns ();
++    }
++  end_sequence ();
++
++  return insn;
++}
++
++#include "gt-optabs.h"
+diff -ruN gcc-3.3.1/gcc/protector.c gcc-3.3.1.pp/gcc/protector.c
+--- gcc-3.3.1/gcc/protector.c  1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/protector.c       2003-09-05 11:59:47.000000000 +0000
+@@ -0,0 +1,2489 @@
++/* RTL buffer overflow protection function for GNU C compiler
++   Copyright (C) 1987, 88, 89, 92-7, 1998 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify it under
++the terms of the GNU General Public License as published by the Free
++Software Foundation; either version 2, or (at your option) any later
++version.
++
++GCC is distributed in the hope that it will be useful, but WITHOUT ANY
++WARRANTY; without even the implied warranty of MERCHANTABILITY or
++FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING.  If not, write to the Free
++Software Foundation, 59 Temple Place - Suite 330, Boston, MA
++02111-1307, USA.  */
++
++#include "config.h"
++#include "system.h"
++#include "machmode.h"
++
++#include "rtl.h"
++#include "tree.h"
++#include "regs.h"
++#include "flags.h"
++#include "insn-config.h"
++#include "insn-flags.h"
++#include "expr.h"
++#include "output.h"
++#include "recog.h"
++#include "hard-reg-set.h"
++#include "real.h"
++#include "except.h"
++#include "function.h"
++#include "toplev.h"
++#include "conditions.h"
++#include "insn-attr.h"
++#include "c-tree.h"
++#include "optabs.h"
++#include "protector.h"
++
++
++void prepare_stack_protection  PARAMS ((int inlinable));
++int search_string_def PARAMS ((tree names));
++rtx assign_stack_local_for_pseudo_reg PARAMS ((enum machine_mode, HOST_WIDE_INT, int));
++
++
++/* Warn when not issuing stack smashing protection for some reason */
++int warn_stack_protector;
++
++/* Round a value to the lowest integer less than it that is a multiple of
++   the required alignment.  Avoid using division in case the value is
++   negative.  Assume the alignment is a power of two.  */
++#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1))
++
++/* Similar, but round to the next highest integer that meets the
++   alignment.  */
++#define CEIL_ROUND(VALUE,ALIGN)       (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
++
++
++/* Nonzero means use propolice as a stack protection method */
++extern int flag_propolice_protection;
++
++/* This file contains several memory arrangement functions to protect
++   the return address and the frame pointer of the stack
++   from a stack-smashing attack. It also
++   provides the function that protects pointer variables. */
++
++/* Nonzero if function being compiled can define string buffers that may be
++   damaged by the stack-smash attack */
++static int current_function_defines_vulnerable_string;
++static int current_function_defines_short_string;
++static int current_function_has_variable_string;
++static int current_function_defines_vsized_array;
++static int current_function_is_inlinable;
++
++static rtx guard_area, _guard;
++static rtx function_first_insn, prologue_insert_point;
++
++/*  */
++static HOST_WIDE_INT sweep_frame_offset;
++static HOST_WIDE_INT push_allocated_offset = 0;
++static HOST_WIDE_INT push_frame_offset = 0;
++static int saved_cse_not_expected = 0;
++
++static int search_string_from_argsandvars PARAMS ((int caller));
++static int search_string_from_local_vars PARAMS ((tree block));
++static int search_pointer_def PARAMS ((tree names));
++static int search_func_pointer PARAMS ((tree type, int mark));
++static void reset_used_flags_for_insns PARAMS ((rtx insn));
++static void reset_used_flags_for_decls PARAMS ((tree block));
++static void reset_used_flags_of_plus PARAMS ((rtx x));
++static void rtl_prologue PARAMS ((rtx insn));
++static void rtl_epilogue PARAMS ((rtx fnlastinsn));
++static void arrange_var_order PARAMS ((tree blocks));
++static void copy_args_for_protection PARAMS ((void));
++static void sweep_string_variable PARAMS ((rtx sweep_var, HOST_WIDE_INT var_size));
++static void sweep_string_in_decls PARAMS ((tree block, HOST_WIDE_INT sweep_offset, HOST_WIDE_INT size));
++static void sweep_string_in_args PARAMS ((tree parms, HOST_WIDE_INT sweep_offset, HOST_WIDE_INT size));
++static void sweep_string_use_of_insns PARAMS ((rtx insn, HOST_WIDE_INT sweep_offset, HOST_WIDE_INT size));
++static void sweep_string_in_operand PARAMS ((rtx insn, rtx *loc, HOST_WIDE_INT sweep_offset, HOST_WIDE_INT size));
++static void move_arg_location PARAMS ((rtx insn, rtx orig, rtx new, HOST_WIDE_INT var_size));
++static void change_arg_use_of_insns PARAMS ((rtx insn, rtx orig, rtx *new, HOST_WIDE_INT size));
++static void change_arg_use_of_insns_2 PARAMS ((rtx insn, rtx orig, rtx *new, HOST_WIDE_INT size));
++static void change_arg_use_in_operand PARAMS ((rtx x, rtx orig, rtx *new, HOST_WIDE_INT size));
++static void validate_insns_of_varrefs PARAMS ((rtx insn));
++static void validate_operand_of_varrefs PARAMS ((rtx insn, rtx *loc));
++
++#define SUSPICIOUS_BUF_SIZE 8
++
++#define AUTO_BASEPTR(X) \
++  (GET_CODE (X) == PLUS ? XEXP (X, 0) : X)
++#define AUTO_OFFSET(X) \
++  (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0)
++#undef PARM_PASSED_IN_MEMORY
++#define PARM_PASSED_IN_MEMORY(PARM) \
++ (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM)
++#define VIRTUAL_STACK_VARS_P(X) \
++ ((X) == virtual_stack_vars_rtx || (GET_CODE (X) == REG && (X)->used))
++
++
++
++void
++prepare_stack_protection (inlinable)
++     int inlinable;
++{
++  tree blocks = DECL_INITIAL (current_function_decl);
++  current_function_is_inlinable = inlinable && !flag_no_inline;
++  push_frame_offset = push_allocated_offset = 0;
++  saved_cse_not_expected = 0;
++
++  /*
++    skip the protection if the function has no block or it is an inline function
++  */
++  if (current_function_is_inlinable) validate_insns_of_varrefs (get_insns ());
++  if (! blocks || current_function_is_inlinable) return;
++
++  current_function_defines_vulnerable_string = search_string_from_argsandvars (0);
++
++  if (current_function_defines_vulnerable_string)
++    {
++      HOST_WIDE_INT offset;
++      function_first_insn = get_insns ();
++
++      if (current_function_contains_functions) {
++        if (warn_stack_protector)
++             warning ("not protecting function: it contains functions");
++        return;
++      }
++
++      /* Initialize recognition, indicating that volatile is OK.  */
++      init_recog ();
++
++      sweep_frame_offset = 0;
++      
++#ifdef STACK_GROWS_DOWNWARD
++      /*
++      frame_offset: offset to end of allocated area of stack frame.
++       It is defined in the function.c
++      */
++
++      /* the location must be before buffers */
++      guard_area = assign_stack_local (BLKmode, UNITS_PER_GUARD, -1);
++      PUT_MODE (guard_area, GUARD_m);
++      MEM_VOLATILE_P (guard_area) = 1;
++
++#ifndef FRAME_GROWS_DOWNWARD
++      sweep_frame_offset = frame_offset;
++#endif
++
++      /* For making room for guard value, scan all insns and fix the offset address
++       of the variable that is based on frame pointer.
++       Scan all declarations of variables and fix the offset address of the variable that
++       is based on the frame pointer */
++      sweep_string_variable (guard_area, UNITS_PER_GUARD);
++
++      
++      /* the location of guard area moves to the beginning of stack frame */
++      if ((offset = AUTO_OFFSET(XEXP (guard_area, 0))))
++      XEXP (XEXP (guard_area, 0), 1) = gen_rtx_CONST_INT (VOIDmode, sweep_frame_offset);
++
++
++      /* Insert prologue rtl instructions */
++      rtl_prologue (function_first_insn);
++
++      if (! current_function_has_variable_string)
++      {
++        /* Generate argument saving instruction */
++        copy_args_for_protection ();
++
++#ifndef FRAME_GROWS_DOWNWARD
++        /* If frame grows upward, character string copied from an arg stays top of
++           the guard variable. So sweep the guard variable again */
++        sweep_frame_offset = CEIL_ROUND (frame_offset, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
++        sweep_string_variable (guard_area, UNITS_PER_GUARD);
++#endif
++      }
++      else if (warn_stack_protector)
++      warning ("not protecting variables: it has a variable length buffer");
++#endif
++#ifndef FRAME_GROWS_DOWNWARD
++      if (STARTING_FRAME_OFFSET == 0)
++      {
++        /* this may be only for alpha */
++        push_allocated_offset = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
++        assign_stack_local (BLKmode, push_allocated_offset, -1);
++        sweep_frame_offset = frame_offset;
++        sweep_string_variable (const0_rtx, -push_allocated_offset);
++        sweep_frame_offset = AUTO_OFFSET (XEXP (guard_area, 0));
++      }
++#endif
++
++      /* Arrange the order of local variables */
++      arrange_var_order (blocks);
++
++#ifdef STACK_GROWS_DOWNWARD
++      /* Insert epilogue rtl instructions */
++      rtl_epilogue (get_last_insn ());
++#endif
++      init_recog_no_volatile ();
++    }
++  else if (current_function_defines_short_string
++         && warn_stack_protector)
++    warning ("not protecting function: buffer is less than %d bytes long",
++           SUSPICIOUS_BUF_SIZE);
++}
++
++/*
++  search string from arguments and local variables
++  caller: 0 means call from protector_stack_protection
++          1 means call from push_frame
++*/
++static int
++search_string_from_argsandvars (caller)
++     int caller;
++{
++  tree blocks, parms;
++  int string_p;
++
++  /* saves a latest search result as a cached infomation */
++  static tree __latest_search_decl = 0;
++  static int  __latest_search_result = FALSE;
++
++  if (__latest_search_decl == current_function_decl)
++    return __latest_search_result;
++  else if (caller) return FALSE;
++  __latest_search_decl = current_function_decl;
++  __latest_search_result = TRUE;
++  
++  current_function_defines_short_string = FALSE;
++  current_function_has_variable_string = FALSE;
++  current_function_defines_vsized_array = FALSE;
++
++  /*
++    search a string variable from local variables
++  */
++  blocks = DECL_INITIAL (current_function_decl);
++  string_p = search_string_from_local_vars (blocks);
++
++  if (!current_function_defines_vsized_array && current_function_calls_alloca)
++    {
++      current_function_has_variable_string = TRUE;
++      return TRUE;
++    }
++
++  if (string_p) return TRUE;
++
++#ifdef STACK_GROWS_DOWNWARD
++  /*
++    search a string variable from arguments
++  */
++  parms = DECL_ARGUMENTS (current_function_decl);
++
++  for (; parms; parms = TREE_CHAIN (parms))
++    if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
++      {
++      if (PARM_PASSED_IN_MEMORY (parms) && DECL_NAME (parms))
++        {
++          string_p = search_string_def (TREE_TYPE(parms));
++          if (string_p) return TRUE;
++        }
++      }
++#endif
++
++  __latest_search_result = FALSE;
++  return FALSE;
++}
++
++
++static int
++search_string_from_local_vars (block)
++     tree block;
++{
++  tree types;
++  int found = FALSE;
++
++  while (block && TREE_CODE(block)==BLOCK)
++    {
++      types = BLOCK_VARS(block);
++
++      while (types)
++      {
++        /* skip the declaration that refers an external variable */
++        /* name: types.decl.name.identifier.id                   */
++        if (! DECL_EXTERNAL (types) && ! TREE_STATIC (types)
++            && TREE_CODE (types) == VAR_DECL
++            && ! DECL_ARTIFICIAL (types)
++            && DECL_RTL_SET_P (types)
++            && GET_CODE (DECL_RTL (types)) == MEM)
++          {
++            if (search_string_def (TREE_TYPE (types)))
++              {
++                rtx home = DECL_RTL (types);
++
++                if (GET_CODE (home) == MEM
++                    && (GET_CODE (XEXP (home, 0)) == MEM
++                        || (GET_CODE (XEXP (home, 0)) == REG
++                            && XEXP (home, 0) != virtual_stack_vars_rtx
++                            && REGNO (XEXP (home, 0)) != HARD_FRAME_POINTER_REGNUM
++                            && REGNO (XEXP (home, 0)) != STACK_POINTER_REGNUM
++#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
++                            && REGNO (XEXP (home, 0)) != ARG_POINTER_REGNUM
++#endif
++                            )))
++                  /* If the value is indirect by memory or by a register
++                     that isn't the frame pointer
++                     then it means the object is variable-sized and address through
++                     that register or stack slot.  The protection has no way to hide pointer variables
++                     behind the array, so all we can do is staying the order of variables and arguments. */
++                  {
++                    current_function_has_variable_string = TRUE;
++                  }
++          
++                /* found character array */
++                found = TRUE;
++              }
++          }
++
++        types = TREE_CHAIN(types);
++      }
++
++      if (search_string_from_local_vars (BLOCK_SUBBLOCKS (block)))
++      {
++        found = TRUE;
++      }
++
++      block = BLOCK_CHAIN (block);
++    }
++    
++  return found;
++}
++
++
++/*
++ * search a character array from the specified type tree
++ */
++int
++search_string_def (type)
++     tree type;
++{
++  tree tem;
++    
++  if (! type)
++    return FALSE;
++
++  switch (TREE_CODE (type))
++    {
++    case ARRAY_TYPE:
++      /* Check if the array is a variable-sized array */
++      if (TYPE_DOMAIN (type) == 0
++        || (TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != 0
++            && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) == NOP_EXPR))
++      current_function_defines_vsized_array = TRUE;
++
++      /* TREE_CODE( TREE_TYPE(type) ) == INTEGER_TYPE */
++      if (TYPE_MAIN_VARIANT (TREE_TYPE(type)) == char_type_node
++        || TYPE_MAIN_VARIANT (TREE_TYPE(type)) == signed_char_type_node
++        || TYPE_MAIN_VARIANT (TREE_TYPE(type)) == unsigned_char_type_node)
++      {
++        /* Check if the string is a variable string */
++        if (TYPE_DOMAIN (type) == 0
++            || (TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != 0
++                && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) == NOP_EXPR))
++          return TRUE;
++
++        /* Check if the string size is greater than SUSPICIOUS_BUF_SIZE */
++        if (TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != 0
++            && TREE_INT_CST_LOW(TYPE_MAX_VALUE(TYPE_DOMAIN(type)))+1 >= SUSPICIOUS_BUF_SIZE)
++          return TRUE;
++
++        current_function_defines_short_string = TRUE;
++      }
++      return search_string_def(TREE_TYPE(type));
++      
++    case UNION_TYPE:
++    case QUAL_UNION_TYPE:
++    case RECORD_TYPE:
++      /* Output the name, type, position (in bits), size (in bits) of each
++       field.  */
++      for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
++      {
++        /* Omit here local type decls until we know how to support them. */
++        if ((TREE_CODE (tem) == TYPE_DECL)
++            || (TREE_CODE (tem) == VAR_DECL && TREE_STATIC (tem)))
++          continue;
++
++        if (search_string_def(TREE_TYPE(tem))) return TRUE;
++      }
++      break;
++      
++    case POINTER_TYPE:
++    case REFERENCE_TYPE:
++      /* I'm not sure whether OFFSET_TYPE needs this treatment,
++       so I'll play safe and return 1.  */
++    case OFFSET_TYPE:
++    default:
++      break;
++    }
++
++  return FALSE;
++}
++
++/*
++ * examine whether the input contains frame pointer addressing
++ */
++int
++contains_fp (op)
++     rtx op;
++{
++  register enum rtx_code code;
++  rtx x;
++  int i, j;
++  const char *fmt;
++
++  x = op;
++  if (x == 0)
++    return FALSE;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case REG:
++    case ADDRESSOF:
++      return FALSE;
++
++    case PLUS:
++      if (XEXP (x, 0) == virtual_stack_vars_rtx
++        && CONSTANT_P (XEXP (x, 1)))
++      return TRUE;
++
++    default:
++      break;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      {
++      if (contains_fp (XEXP (x, i))) return TRUE;
++      }
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      if (contains_fp (XVECEXP (x, i, j))) return TRUE;
++
++  return FALSE;
++}
++
++
++static int
++search_pointer_def (type)
++     tree type;
++{
++  tree tem;
++    
++  if (! type)
++    return FALSE;
++
++  switch (TREE_CODE (type))
++    {
++    case UNION_TYPE:
++    case QUAL_UNION_TYPE:
++    case RECORD_TYPE:
++      /* Output the name, type, position (in bits), size (in bits) of each
++       field.  */
++      for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
++      {
++        /* Omit here local type decls until we know how to support them. */
++        if ((TREE_CODE (tem) == TYPE_DECL)
++            || (TREE_CODE (tem) == VAR_DECL && TREE_STATIC (tem)))
++          continue;
++
++        if (search_pointer_def (TREE_TYPE(tem))) return TRUE;
++      }
++      break;
++
++    case ARRAY_TYPE:
++      return search_pointer_def (TREE_TYPE(type));
++      
++    case POINTER_TYPE:
++    case REFERENCE_TYPE:
++      /* I'm not sure whether OFFSET_TYPE needs this treatment,
++       so I'll play safe and return 1.  */
++    case OFFSET_TYPE:
++      if (TYPE_READONLY (TREE_TYPE (type)))
++      {
++        int funcp = search_func_pointer (TREE_TYPE (type), 1);
++        /* Un-mark the type as having been visited already */
++        search_func_pointer (TREE_TYPE (type), 0);
++        return funcp;
++      }
++      return TRUE;
++      
++    default:
++      break;
++    }
++
++  return FALSE;
++}
++
++
++static int
++search_func_pointer (type, mark)
++     tree type;
++     int mark;
++{
++  tree tem;
++    
++  if (! type)
++    return FALSE;
++
++  switch (TREE_CODE (type))
++    {
++    case UNION_TYPE:
++    case QUAL_UNION_TYPE:
++    case RECORD_TYPE:
++      if (TREE_ASM_WRITTEN (type) != mark)
++        {
++          /* mark the type as having been visited already */
++          TREE_ASM_WRITTEN (type) = mark;
++
++          /* Output the name, type, position (in bits), size (in bits) of
++             each field.  */
++          for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
++            {
++              /* Omit here local type decls until we know how to support them. */
++              if (TREE_CODE (tem) == FIELD_DECL
++                  && search_func_pointer (TREE_TYPE(tem), mark)) return TRUE;
++            }
++        }
++      break;
++
++    case ARRAY_TYPE:
++      return search_func_pointer (TREE_TYPE(type), mark);
++      
++    case POINTER_TYPE:
++    case REFERENCE_TYPE:
++      /* I'm not sure whether OFFSET_TYPE needs this treatment,
++       so I'll play safe and return 1.  */
++    case OFFSET_TYPE:
++      return TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE;
++      
++    default:
++      break;
++    }
++
++  return FALSE;
++}
++
++
++static void
++reset_used_flags_for_insns (insn)
++     rtx insn;
++{
++  register int i, j;
++  register enum rtx_code code;
++  register const char *format_ptr;
++
++  for (; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++      || GET_CODE (insn) == CALL_INSN)
++      {
++      code = GET_CODE (insn);
++      insn->used = 0;
++      format_ptr = GET_RTX_FORMAT (code);
++
++      for (i = 0; i < GET_RTX_LENGTH (code); i++)
++        {
++          switch (*format_ptr++) {
++          case 'e':
++            reset_used_flags_of_plus (XEXP (insn, i));
++            break;
++                      
++          case 'E':
++            for (j = 0; j < XVECLEN (insn, i); j++)
++              reset_used_flags_of_plus (XVECEXP (insn, i, j));
++            break;
++          }
++        }
++      }
++}
++
++static void
++reset_used_flags_for_decls (block)
++     tree block;
++{
++  tree types;
++  rtx home;
++
++  while (block && TREE_CODE(block)==BLOCK)
++    {
++      types = BLOCK_VARS(block);
++      
++      while (types)
++      {
++        /* skip the declaration that refers an external variable and
++           also skip an global variable */
++        if (! DECL_EXTERNAL (types))
++          {
++            if (!DECL_RTL_SET_P (types)) goto next;
++            home = DECL_RTL (types);
++
++            if (GET_CODE (home) == MEM
++                && GET_CODE (XEXP (home, 0)) == PLUS
++                && GET_CODE (XEXP (XEXP (home, 0), 1)) == CONST_INT)
++              {
++                XEXP (home, 0)->used = 0;
++              }
++          }
++      next:
++        types = TREE_CHAIN(types);
++      }
++
++      reset_used_flags_for_decls (BLOCK_SUBBLOCKS (block));
++
++      block = BLOCK_CHAIN (block);
++    }
++}
++
++/* Clear the USED bits only of type PLUS in X */
++
++static void
++reset_used_flags_of_plus (x)
++     rtx x;
++{
++  register int i, j;
++  register enum rtx_code code;
++  register const char *format_ptr;
++
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++
++  /* These types may be freely shared so we needn't do any resetting
++     for them.  */
++
++  switch (code)
++    {
++    case REG:
++    case QUEUED:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++      return;
++
++    case INSN:
++    case JUMP_INSN:
++    case CALL_INSN:
++    case NOTE:
++    case LABEL_REF:
++    case BARRIER:
++      /* The chain of insns is not being copied.  */
++      return;
++      
++    case PLUS:
++      x->used = 0;
++      break;
++
++    case CALL_PLACEHOLDER:
++      reset_used_flags_for_insns (XEXP (x, 0));
++      reset_used_flags_for_insns (XEXP (x, 1));
++      reset_used_flags_for_insns (XEXP (x, 2));
++      break;
++
++    default:
++      break;
++    }
++
++  format_ptr = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++)
++    {
++      switch (*format_ptr++)
++      {
++      case 'e':
++        reset_used_flags_of_plus (XEXP (x, i));
++        break;
++
++      case 'E':
++        for (j = 0; j < XVECLEN (x, i); j++)
++          reset_used_flags_of_plus (XVECEXP (x, i, j));
++        break;
++      }
++    }
++}
++
++
++static void
++rtl_prologue (insn)
++     rtx insn;
++{
++#if defined(INIT_SECTION_ASM_OP) && !defined(INVOKE__main)
++#undef HAS_INIT_SECTION
++#define HAS_INIT_SECTION
++#endif
++
++  rtx _val;
++
++  for (; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
++      break;
++  
++#if !defined (HAS_INIT_SECTION)
++  /* If this function is `main', skip a call to `__main'
++     to run guard instruments after global initializers, etc.  */
++  if (DECL_NAME (current_function_decl)
++      && MAIN_NAME_P (DECL_NAME (current_function_decl))
++      && DECL_CONTEXT (current_function_decl) == NULL_TREE)
++    {
++      rtx fbinsn = insn;
++      for (; insn; insn = NEXT_INSN (insn))
++      if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
++        break;
++      if (insn == 0) insn = fbinsn;
++    }
++#endif
++
++  prologue_insert_point = NEXT_INSN (insn);   /* mark the next insn of FUNCTION_BEG insn */
++              
++  start_sequence ();
++
++  _guard = gen_rtx_MEM (GUARD_m, gen_rtx_SYMBOL_REF (Pmode, "__guard"));
++  emit_move_insn ( guard_area, _guard);
++
++  _val = get_insns ();
++  end_sequence ();
++
++  emit_insn_before (_val, prologue_insert_point);
++}
++
++static void
++rtl_epilogue (insn)
++     rtx insn;
++{
++  rtx if_false_label;
++  rtx _val;
++  rtx funcname;
++  tree funcstr;
++  int  flag_have_return = FALSE;
++              
++  start_sequence ();
++
++#ifdef HAVE_return
++  if (HAVE_return)
++    {
++      rtx insn;
++      return_label = gen_label_rtx ();
++      
++      for (insn = prologue_insert_point; insn; insn = NEXT_INSN (insn))
++      if (GET_CODE (insn) == JUMP_INSN
++          && GET_CODE (PATTERN (insn)) == RETURN
++          && GET_MODE (PATTERN (insn)) == VOIDmode)
++        {
++          rtx pat = gen_rtx_SET (VOIDmode,
++                                 pc_rtx,
++                                 gen_rtx_LABEL_REF (VOIDmode,
++                                                    return_label));
++          PATTERN (insn) = pat;
++          flag_have_return = TRUE;
++        }
++
++
++      emit_label (return_label);
++    }
++#endif
++
++  compare_from_rtx (guard_area, _guard, NE, 0, GUARD_m, NULL_RTX);    /* if (guard_area != _guard) */
++
++  if_false_label = gen_label_rtx ();                          /* { */
++  emit_jump_insn ( gen_beq(if_false_label));
++
++  /* generate string for the current function name */
++  funcstr = build_string (strlen(current_function_name)+1, current_function_name);
++  TREE_TYPE (funcstr) = build_array_type (char_type_node, 0);/* = char_array_type_node;*/
++  funcname = output_constant_def (funcstr, 1);
++
++  emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__stack_smash_handler"),
++                   0, VOIDmode, 2,
++                     XEXP (funcname, 0), Pmode, guard_area, GUARD_m);
++
++  /* generate RTL to return from the current function */
++              
++  emit_barrier ();                                            /* } */
++  emit_label (if_false_label);
++
++  /* generate RTL to return from the current function */
++  if (DECL_RTL_SET_P (DECL_RESULT (current_function_decl)))
++    use_return_register ();
++
++#ifdef HAVE_return
++  if (HAVE_return && flag_have_return)
++    {
++      emit_jump_insn (gen_return ());
++      emit_barrier ();
++    }
++#endif
++  
++  _val = get_insns ();
++  end_sequence ();
++
++  emit_insn_after (_val, insn);
++}
++
++
++static void
++arrange_var_order (block)
++     tree block;
++{
++  tree types;
++  HOST_WIDE_INT offset;
++    
++  while (block && TREE_CODE(block)==BLOCK)
++    {
++      types = BLOCK_VARS (block);
++
++      while (types)
++      {
++        /* skip the declaration that refers an external variable */
++        /* name: types.decl.assembler_name.id                    */
++        if (! DECL_EXTERNAL (types) && ! TREE_STATIC (types)
++            && TREE_CODE (types) == VAR_DECL
++            && ! DECL_ARTIFICIAL (types)
++            && ! DECL_INLINE (types)  /* don't sweep inlined string */
++            && DECL_RTL_SET_P (types)
++            && GET_CODE (DECL_RTL (types)) == MEM)
++          {
++            if (search_string_def (TREE_TYPE (types)))
++              {
++                rtx home = DECL_RTL (types);
++
++                if (! (GET_CODE (home) == MEM
++                       && (GET_CODE (XEXP (home, 0)) == MEM
++                           || (GET_CODE (XEXP (home, 0)) == REG
++                               && XEXP (home, 0) != virtual_stack_vars_rtx
++                               && REGNO (XEXP (home, 0)) != HARD_FRAME_POINTER_REGNUM
++                               && REGNO (XEXP (home, 0)) != STACK_POINTER_REGNUM
++#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
++                               && REGNO (XEXP (home, 0)) != ARG_POINTER_REGNUM
++#endif
++                               ))))
++                  {
++                    /* found a string variable */
++                    HOST_WIDE_INT var_size =
++                      ((TREE_INT_CST_LOW (DECL_SIZE (types)) + BITS_PER_UNIT - 1)
++                       / BITS_PER_UNIT);
++
++                    if (GET_MODE (DECL_RTL (types)) == BLKmode)
++                      {
++                        int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
++                        var_size = CEIL_ROUND (var_size, alignment);
++                      }
++
++                    /* skip the variable if it is top of the region
++                       specified by sweep_frame_offset */
++                    offset = AUTO_OFFSET (XEXP (DECL_RTL (types), 0));
++                    if (offset == sweep_frame_offset - var_size)
++                      sweep_frame_offset -= var_size;
++                    
++                    else if (offset < sweep_frame_offset - var_size)
++                      sweep_string_variable (DECL_RTL (types), var_size);
++                  }
++              }
++          }
++
++        types = TREE_CHAIN(types);
++      }
++
++      arrange_var_order (BLOCK_SUBBLOCKS (block));
++
++      block = BLOCK_CHAIN (block);
++    }
++}
++
++
++static void
++copy_args_for_protection ()
++{
++  tree parms = DECL_ARGUMENTS (current_function_decl);
++  rtx temp_rtx;
++
++  parms = DECL_ARGUMENTS (current_function_decl);
++  for (; parms; parms = TREE_CHAIN (parms))
++    if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
++      {
++      if (PARM_PASSED_IN_MEMORY (parms) && DECL_NAME (parms))
++        {
++          int string_p;
++
++          /*
++            skip arguemnt protection if the last argument is used
++            for the variable argument
++          */
++          /*
++            tree fntype;
++            if (TREE_CHAIN (parms) == 0)
++            {
++              fntype = TREE_TYPE (current_function_decl);
++
++              if ((TYPE_ARG_TYPES (fntype) != 0 &&
++                   TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)
++                   || current_function_varargs)
++                continue;
++            }
++          */
++
++          string_p = search_string_def (TREE_TYPE(parms));
++
++          /* check if it is a candidate to move */
++          if (string_p || search_pointer_def (TREE_TYPE (parms)))
++            {
++              int arg_size
++                = ((TREE_INT_CST_LOW (DECL_SIZE (parms)) + BITS_PER_UNIT - 1)
++                   / BITS_PER_UNIT);
++              
++              start_sequence ();
++
++              if (GET_CODE (DECL_RTL (parms)) == REG)
++                {
++                  rtx safe = 0;
++                  
++                  change_arg_use_of_insns (prologue_insert_point, DECL_RTL (parms), &safe, 0);
++                  if (safe)
++                    {
++                      /* generate codes for copying the content */
++                      rtx movinsn = emit_move_insn (safe, DECL_RTL (parms));
++                  
++                      /* avoid register elimination in gcse.c (COPY-PROP)*/
++                      PATTERN (movinsn)->volatil = 1;
++                      
++                      /* save debugger info */
++                      DECL_INCOMING_RTL (parms) = safe;
++                    }
++                }
++
++              else if (GET_CODE (DECL_RTL (parms)) == MEM
++                       && GET_CODE (XEXP (DECL_RTL (parms), 0)) == ADDRESSOF)
++                {
++                  rtx movinsn;
++                  rtx safe = gen_reg_rtx (GET_MODE (DECL_RTL (parms)));
++
++                  /* generate codes for copying the content */
++                  movinsn = emit_move_insn (safe, DECL_INCOMING_RTL (parms));
++                  PATTERN (movinsn)->volatil = 1;     /* avoid register elimination in gcse.c (COPY-PROP)*/
++
++                  /* change the addressof information to the newly allocated pseudo register */
++                  emit_move_insn (DECL_RTL (parms), safe);
++
++                  /* save debugger info */
++                  DECL_INCOMING_RTL (parms) = safe;
++                }
++                      
++              else
++                {
++                  /* declare temporary local variable DECL_NAME (parms) for it */
++                  temp_rtx
++                    = assign_stack_local (DECL_MODE (parms), arg_size,
++                                          DECL_MODE (parms) == BLKmode ? -1 : 0);
++                  
++                  MEM_IN_STRUCT_P (temp_rtx) = AGGREGATE_TYPE_P (TREE_TYPE (parms));
++                  set_mem_alias_set (temp_rtx, get_alias_set (parms));
++
++                  /* generate codes for copying the content */
++                  store_expr (parms, temp_rtx, 0);
++
++                  /* change the reference for each instructions */
++                  move_arg_location (prologue_insert_point, DECL_RTL (parms),
++                                     temp_rtx, arg_size);
++
++                  /* change the location of parms variable */
++                  SET_DECL_RTL (parms, temp_rtx);
++
++                  /* change debugger info */
++                  DECL_INCOMING_RTL (parms) = temp_rtx;
++                }
++
++              emit_insn_before (get_insns (), prologue_insert_point);
++              end_sequence ();
++
++#ifdef FRAME_GROWS_DOWNWARD
++              /* process the string argument */
++              if (string_p && DECL_MODE (parms) == BLKmode)
++                {
++                  int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
++                  arg_size = CEIL_ROUND (arg_size, alignment);
++                      
++                  /* change the reference for each instructions */
++                  sweep_string_variable (DECL_RTL (parms), arg_size);
++                }
++#endif
++            }
++        }
++      }
++}
++
++
++/*
++  sweep a string variable to the local variable addressed by sweep_frame_offset, that is
++  a last position of string variables.
++*/
++static void
++sweep_string_variable (sweep_var, var_size)
++     rtx sweep_var;
++     HOST_WIDE_INT var_size;
++{
++  HOST_WIDE_INT sweep_offset;
++
++  switch (GET_CODE (sweep_var))
++    {
++    case MEM:
++      if (GET_CODE (XEXP (sweep_var, 0)) == ADDRESSOF
++        && GET_CODE (XEXP (XEXP (sweep_var, 0), 0)) == REG)
++      return;
++      sweep_offset = AUTO_OFFSET(XEXP (sweep_var, 0));
++      break;
++    case CONST_INT:
++      sweep_offset = INTVAL (sweep_var);
++      break;
++    default:
++      abort ();
++    }
++
++  /* scan all declarations of variables and fix the offset address of
++     the variable based on the frame pointer */
++  sweep_string_in_decls (DECL_INITIAL (current_function_decl), sweep_offset, var_size);
++
++  /* scan all argument variable and fix the offset address based on the frame pointer */
++  sweep_string_in_args (DECL_ARGUMENTS (current_function_decl), sweep_offset, var_size);
++
++  /* For making room for sweep variable, scan all insns and fix the offset address
++     of the variable that is based on frame pointer*/
++  sweep_string_use_of_insns (function_first_insn, sweep_offset, var_size);
++
++
++  /* Clear all the USED bits in operands of all insns and declarations of local vars */
++  reset_used_flags_for_decls (DECL_INITIAL (current_function_decl));
++  reset_used_flags_for_insns (function_first_insn);
++
++  sweep_frame_offset -= var_size;
++}
++
++
++
++/*
++  move an argument to the local variable addressed by frame_offset
++*/
++static void
++move_arg_location (insn, orig, new, var_size)
++     rtx  insn, orig, new;
++     HOST_WIDE_INT var_size;
++{
++  /* For making room for sweep variable, scan all insns and fix the offset address
++     of the variable that is based on frame pointer*/
++  change_arg_use_of_insns (insn, orig, &new, var_size);
++
++
++  /* Clear all the USED bits in operands of all insns and declarations of local vars */
++  reset_used_flags_for_insns (insn);
++}
++
++
++static void
++sweep_string_in_decls (block, sweep_offset, sweep_size)
++     tree block;
++     HOST_WIDE_INT sweep_offset, sweep_size;
++{
++  tree types;
++  HOST_WIDE_INT offset;
++  rtx home;
++
++  while (block && TREE_CODE(block)==BLOCK)
++    {
++      types = BLOCK_VARS(block);
++      
++      while (types)
++      {
++        /* skip the declaration that refers an external variable and
++           also skip an global variable */
++        if (! DECL_EXTERNAL (types) && ! TREE_STATIC (types)) {
++          
++          if (!DECL_RTL_SET_P (types)) goto next;
++          home = DECL_RTL (types);
++
++          /* process for static local variable */
++          if (GET_CODE (home) == MEM
++              && GET_CODE (XEXP (home, 0)) == SYMBOL_REF)
++            goto next;
++
++          if (GET_CODE (home) == MEM
++              && XEXP (home, 0) == virtual_stack_vars_rtx)
++            {
++              offset = 0;
++              
++              /* the operand related to the sweep variable */
++              if (sweep_offset <= offset
++                  && offset < sweep_offset + sweep_size)
++                {
++                  offset = sweep_frame_offset - sweep_size - sweep_offset;
++
++                  XEXP (home, 0) = plus_constant (virtual_stack_vars_rtx, offset);
++                  XEXP (home, 0)->used = 1;
++                }
++              else if (sweep_offset <= offset
++                       && offset < sweep_frame_offset)
++                {     /* the rest of variables under sweep_frame_offset, so shift the location */
++                  XEXP (home, 0) = plus_constant (virtual_stack_vars_rtx, -sweep_size);
++                  XEXP (home, 0)->used = 1;
++                }
++            }
++              
++          if (GET_CODE (home) == MEM
++              && GET_CODE (XEXP (home, 0)) == MEM)
++            {
++              /* process for dynamically allocated aray */
++              home = XEXP (home, 0);
++            }
++              
++          if (GET_CODE (home) == MEM
++              && GET_CODE (XEXP (home, 0)) == PLUS
++              && XEXP (XEXP (home, 0), 0) == virtual_stack_vars_rtx
++              && GET_CODE (XEXP (XEXP (home, 0), 1)) == CONST_INT)
++            {
++              if (! XEXP (home, 0)->used)
++                {
++                  offset = AUTO_OFFSET(XEXP (home, 0));
++
++                  /* the operand related to the sweep variable */
++                  if (sweep_offset <= offset
++                      && offset < sweep_offset + sweep_size)
++                    {
++
++                      offset += sweep_frame_offset - sweep_size - sweep_offset;
++                      XEXP (XEXP (home, 0), 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++
++                      /* mark */
++                      XEXP (home, 0)->used = 1;
++                    }
++                  else if (sweep_offset <= offset
++                           && offset < sweep_frame_offset)
++                    { /* the rest of variables under sweep_frame_offset,
++                         so shift the location */
++
++                      XEXP (XEXP (home, 0), 1)
++                        = gen_rtx_CONST_INT (VOIDmode, offset - sweep_size);
++
++                      /* mark */
++                      XEXP (home, 0)->used = 1;
++                    }
++                }
++            }
++
++        }
++      next:
++        types = TREE_CHAIN(types);
++      }
++
++      sweep_string_in_decls (BLOCK_SUBBLOCKS (block), sweep_offset, sweep_size);
++      block = BLOCK_CHAIN (block);
++    }
++}
++
++
++static void
++sweep_string_in_args (parms, sweep_offset, sweep_size)
++     tree parms;
++     HOST_WIDE_INT sweep_offset, sweep_size;
++{
++  rtx home;
++  HOST_WIDE_INT offset;
++    
++  for (; parms; parms = TREE_CHAIN (parms))
++    if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
++      {
++      if (PARM_PASSED_IN_MEMORY (parms) && DECL_NAME (parms))
++        {
++          home = DECL_INCOMING_RTL (parms);
++
++          if (XEXP (home, 0)->used) continue;
++
++          offset = AUTO_OFFSET(XEXP (home, 0));
++
++          /* the operand related to the sweep variable */
++          if (AUTO_BASEPTR (XEXP (home, 0)) == virtual_stack_vars_rtx)
++            {
++              if (sweep_offset <= offset
++                  && offset < sweep_offset + sweep_size)
++                {
++                  offset += sweep_frame_offset - sweep_size - sweep_offset;
++                  XEXP (XEXP (home, 0), 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++
++                  /* mark */
++                  XEXP (home, 0)->used = 1;
++                }
++              else if (sweep_offset <= offset
++                       && offset < sweep_frame_offset)
++                {     /* the rest of variables under sweep_frame_offset, so shift the location */
++                  XEXP (XEXP (home, 0), 1) = gen_rtx_CONST_INT (VOIDmode, offset - sweep_size);
++
++                  /* mark */
++                  XEXP (home, 0)->used = 1;
++                }
++            }
++        }
++      }
++}
++
++
++static int has_virtual_reg;
++
++static void
++sweep_string_use_of_insns (insn, sweep_offset, sweep_size)
++     rtx insn;
++     HOST_WIDE_INT sweep_offset, sweep_size;
++{
++  for (; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++      || GET_CODE (insn) == CALL_INSN)
++      {
++      has_virtual_reg = FALSE;
++      sweep_string_in_operand (insn, &PATTERN (insn), sweep_offset, sweep_size);
++      sweep_string_in_operand (insn, &REG_NOTES (insn), sweep_offset, sweep_size);
++      }
++}
++
++
++static void
++sweep_string_in_operand (insn, loc, sweep_offset, sweep_size)
++     rtx insn, *loc;
++     HOST_WIDE_INT sweep_offset, sweep_size;
++{
++  register rtx x = *loc;
++  register enum rtx_code code;
++  int i, j, k = 0;
++  HOST_WIDE_INT offset;
++  const char *fmt;
++
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++    case ASM_INPUT:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++    case RETURN:
++    case ADDRESSOF:
++      return;
++          
++    case REG:
++      if (x == virtual_incoming_args_rtx
++        || x == virtual_stack_vars_rtx
++        || x == virtual_stack_dynamic_rtx
++        || x == virtual_outgoing_args_rtx
++        || x == virtual_cfa_rtx)
++      has_virtual_reg = TRUE;
++      return;
++      
++    case SET:
++      /*
++      skip setjmp setup insn and setjmp restore insn
++      Example:
++      (set (MEM (reg:SI xx)) (virtual_stack_vars_rtx)))
++      (set (virtual_stack_vars_rtx) (REG))
++      */
++      if (GET_CODE (XEXP (x, 0)) == MEM
++        && XEXP (x, 1) == virtual_stack_vars_rtx)
++      return;
++      if (XEXP (x, 0) == virtual_stack_vars_rtx
++        && GET_CODE (XEXP (x, 1)) == REG)
++      return;
++      break;
++          
++    case PLUS:
++      /* Handle typical case of frame register plus constant.  */
++      if (XEXP (x, 0) == virtual_stack_vars_rtx
++        && CONSTANT_P (XEXP (x, 1)))
++      {
++        if (x->used) goto single_use_of_virtual_reg;
++        
++        offset = AUTO_OFFSET(x);
++        if (RTX_INTEGRATED_P (x)) k = -1; /* for inline base ptr */
++
++        /* the operand related to the sweep variable */
++        if (sweep_offset <= offset + k
++            && offset + k < sweep_offset + sweep_size)
++          {
++            offset += sweep_frame_offset - sweep_size - sweep_offset;
++
++            XEXP (x, 0) = virtual_stack_vars_rtx;
++            XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++            x->used = 1;
++          }
++        else if (sweep_offset <= offset + k
++                 && offset + k < sweep_frame_offset)
++          {   /* the rest of variables under sweep_frame_offset, so shift the location */
++            XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset - sweep_size);
++            x->used = 1;
++          }
++        
++      single_use_of_virtual_reg:
++        if (has_virtual_reg) {
++          /* excerpt from insn_invalid_p in recog.c */
++          int icode = recog_memoized (insn);
++
++          if (icode < 0 && asm_noperands (PATTERN (insn)) < 0)
++            {
++              rtx temp, seq;
++              
++              start_sequence ();
++              temp = force_operand (x, NULL_RTX);
++              seq = get_insns ();
++              end_sequence ();
++              
++              emit_insn_before (seq, insn);
++              if (! validate_change (insn, loc, temp, 0)
++                  && ! validate_replace_rtx (x, temp, insn))
++                fatal_insn ("sweep_string_in_operand", insn);
++            }
++        }
++
++        has_virtual_reg = TRUE;
++        return;
++      }
++
++#ifdef FRAME_GROWS_DOWNWARD
++      /*
++      alert the case of frame register plus constant given by reg.
++      */
++      else if (XEXP (x, 0) == virtual_stack_vars_rtx
++             && GET_CODE (XEXP (x, 1)) == REG)
++      fatal_insn ("sweep_string_in_operand: unknown addressing", insn);
++#endif
++
++      /*
++      process further subtree:
++      Example:  (plus:SI (mem/s:SI (plus:SI (reg:SI 17) (const_int 8)))
++      (const_int 5))
++      */
++      break;
++
++    case CALL_PLACEHOLDER:
++      sweep_string_use_of_insns (XEXP (x, 0), sweep_offset, sweep_size);
++      sweep_string_use_of_insns (XEXP (x, 1), sweep_offset, sweep_size);
++      sweep_string_use_of_insns (XEXP (x, 2), sweep_offset, sweep_size);
++      break;
++
++    default:
++      break;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      {
++      /*
++        virtual_stack_vars_rtx without offset
++        Example:
++          (set (reg:SI xx) (reg:SI 78))
++          (set (reg:SI xx) (MEM (reg:SI 78)))
++      */
++      if (XEXP (x, i) == virtual_stack_vars_rtx)
++        fatal_insn ("sweep_string_in_operand: unknown fp usage", insn);
++      sweep_string_in_operand (insn, &XEXP (x, i), sweep_offset, sweep_size);
++      }
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      sweep_string_in_operand (insn, &XVECEXP (x, i, j), sweep_offset, sweep_size);
++}   
++
++
++/*
++  change a argument variable to the local variable addressed by the "new" variable.
++*/
++static int flag_caui_exit;
++
++static void
++change_arg_use_of_insns (insn, orig, new, size)
++     rtx insn, orig, *new;
++     HOST_WIDE_INT size;
++{
++  flag_caui_exit = FALSE;
++  change_arg_use_of_insns_2 (insn, orig, new, size);
++}
++
++static void
++change_arg_use_of_insns_2 (insn, orig, new, size)
++     rtx insn, orig, *new;
++     HOST_WIDE_INT size;
++{
++  for (; insn && !flag_caui_exit; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++      || GET_CODE (insn) == CALL_INSN)
++      {
++      change_arg_use_in_operand (PATTERN (insn), orig, new, size);
++      }
++}
++
++
++
++static void
++change_arg_use_in_operand (x, orig, new, size)
++     rtx x, orig, *new;
++     HOST_WIDE_INT size;
++{
++  register enum rtx_code code;
++  int i, j;
++  HOST_WIDE_INT offset;
++  const char *fmt;
++
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++    case ASM_INPUT:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++    case RETURN:
++    case REG:
++    case ADDRESSOF:
++      return;
++
++    case MEM:
++      /* Handle special case of MEM (incoming_args)  */
++      if (GET_CODE (orig) == MEM
++        && XEXP (x, 0) == virtual_incoming_args_rtx)
++      {
++        offset = 0;
++
++        /* the operand related to the sweep variable */
++        if (AUTO_OFFSET(XEXP (orig, 0)) <= offset &&
++            offset < AUTO_OFFSET(XEXP (orig, 0)) + size) {
++
++          offset = AUTO_OFFSET(XEXP (*new, 0))
++            + (offset - AUTO_OFFSET(XEXP (orig, 0)));
++
++          XEXP (x, 0) = plus_constant (virtual_stack_vars_rtx, offset);
++          XEXP (x, 0)->used = 1;
++
++          return;
++        }
++      }
++      break;
++      
++    case PLUS:
++      /* Handle special case of frame register plus constant.  */
++      if (GET_CODE (orig) == MEM /* skip if orig is register variable in the optimization */
++        && XEXP (x, 0) == virtual_incoming_args_rtx && CONSTANT_P (XEXP (x, 1))
++        && ! x->used)
++      {
++        offset = AUTO_OFFSET(x);
++
++        /* the operand related to the sweep variable */
++        if (AUTO_OFFSET(XEXP (orig, 0)) <= offset &&
++            offset < AUTO_OFFSET(XEXP (orig, 0)) + size) {
++
++          offset = AUTO_OFFSET(XEXP (*new, 0))
++            + (offset - AUTO_OFFSET(XEXP (orig, 0)));
++
++          XEXP (x, 0) = virtual_stack_vars_rtx;
++          XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++          x->used = 1;
++
++          return;
++        }
++
++        /*
++          process further subtree:
++          Example:  (plus:SI (mem/s:SI (plus:SI (reg:SI 17) (const_int 8)))
++          (const_int 5))
++        */
++      }
++      break;
++
++    case SET:
++      /* Handle special case of "set (REG or MEM) (incoming_args)".
++       It means that the the address of the 1st argument is stored. */
++      if (GET_CODE (orig) == MEM
++        && XEXP (x, 1) == virtual_incoming_args_rtx)
++      {
++        offset = 0;
++
++        /* the operand related to the sweep variable */
++        if (AUTO_OFFSET(XEXP (orig, 0)) <= offset &&
++            offset < AUTO_OFFSET(XEXP (orig, 0)) + size) {
++
++          offset = AUTO_OFFSET(XEXP (*new, 0))
++            + (offset - AUTO_OFFSET(XEXP (orig, 0)));
++
++          XEXP (x, 1) = plus_constant (virtual_stack_vars_rtx, offset);
++          XEXP (x, 1)->used = 1;
++
++        return;
++        }
++      }
++      break;
++
++    case CALL_PLACEHOLDER:
++      change_arg_use_of_insns_2 (XEXP (x, 0), orig, new, size); if (flag_caui_exit) return;
++      change_arg_use_of_insns_2 (XEXP (x, 1), orig, new, size); if (flag_caui_exit) return;
++      change_arg_use_of_insns_2 (XEXP (x, 2), orig, new, size); if (flag_caui_exit) return;
++      break;
++
++    default:
++      break;
++    }
++
++  if (*new == 0
++      && code == SET
++      && SET_SRC (x) == orig
++      && GET_CODE (SET_DEST (x)) == REG)
++    {
++      /* exit to the change_arg_use_of_insns */
++      flag_caui_exit = TRUE;
++      x->volatil = 1; /* avoid register elimination in gcse.c (COPY-PROP)*/
++      return;
++    }
++  
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      {
++      if (XEXP (x, i) == orig)
++        {
++          if (*new == 0) *new = gen_reg_rtx (GET_MODE (orig));
++          XEXP (x, i) = *new;
++          continue;
++        }
++      change_arg_use_in_operand (XEXP (x, i), orig, new, size);
++      }
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      {
++
++        if (XVECEXP (x, i, j) == orig)
++          {
++            if (*new == 0) *new = gen_reg_rtx (GET_MODE (orig));
++            XVECEXP (x, i, j) = *new;
++            continue;
++          }
++        change_arg_use_in_operand (XVECEXP (x, i, j), orig, new, size);
++      }
++}   
++
++
++static void
++validate_insns_of_varrefs (insn)
++     rtx insn;
++{
++  rtx next;
++
++  /* Initialize recognition, indicating that volatile is OK.  */
++  init_recog ();
++
++  for (; insn; insn = next)
++    {
++      next = NEXT_INSN (insn);
++      if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++        || GET_CODE (insn) == CALL_INSN)
++      {
++        /* excerpt from insn_invalid_p in recog.c */
++        int icode = recog_memoized (insn);
++
++        if (icode < 0 && asm_noperands (PATTERN (insn)) < 0)
++          validate_operand_of_varrefs (insn, &PATTERN (insn));
++      }
++    }
++
++  init_recog_no_volatile ();
++}
++
++
++static void
++validate_operand_of_varrefs (insn, loc)
++     rtx insn, *loc;
++{
++  register enum rtx_code code;
++  rtx x, temp, seq;
++  int i, j;
++  const char *fmt;
++
++  x = *loc;
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case USE:
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++    case ASM_INPUT:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++    case RETURN:
++    case REG:
++    case ADDRESSOF:
++      return;
++
++    case PLUS:
++      /* validate insn of frame register plus constant.  */
++      if (GET_CODE (x) == PLUS
++        && XEXP (x, 0) == virtual_stack_vars_rtx
++        && CONSTANT_P (XEXP (x, 1)))
++      {
++        start_sequence ();
++        /* temp = force_operand (x, NULL_RTX); */
++        { /* excerpt from expand_binop in optabs.c */
++          optab binoptab = add_optab;
++          enum machine_mode mode = GET_MODE (x);
++          int icode = (int) binoptab->handlers[(int) mode].insn_code;
++          enum machine_mode mode1 = insn_data[icode].operand[2].mode;
++          rtx pat;
++          rtx xop0 = XEXP (x, 0), xop1 = XEXP (x, 1);
++          temp = gen_reg_rtx (mode);
++
++          /* Now, if insn's predicates don't allow offset operands, put them into
++             pseudo regs.  */
++
++          if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
++              && mode1 != VOIDmode)
++            xop1 = copy_to_mode_reg (mode1, xop1);
++
++          pat = GEN_FCN (icode) (temp, xop0, xop1);
++          if (pat)
++            emit_insn (pat);
++        }           
++        seq = get_insns ();
++        end_sequence ();
++        
++        emit_insn_before (seq, insn);
++        if (! validate_change (insn, loc, temp, 0))
++          abort ();
++        return;
++      }
++      break;
++      
++
++    case CALL_PLACEHOLDER:
++      validate_insns_of_varrefs (XEXP (x, 0));
++      validate_insns_of_varrefs (XEXP (x, 1));
++      validate_insns_of_varrefs (XEXP (x, 2));
++      break;
++
++    default:
++      break;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      validate_operand_of_varrefs (insn, &XEXP (x, i));
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      validate_operand_of_varrefs (insn, &XVECEXP (x, i, j));
++}
++
++
++
++
++/*
++  The following codes are invoked after the instantiation of pseuso registers.
++
++  Reorder local variables to place a peudo register after buffers to avoid
++  the corruption of local variables that could be used to further corrupt
++  arbitrary memory locations.
++*/
++#if !defined(FRAME_GROWS_DOWNWARD) && defined(STACK_GROWS_DOWNWARD)
++static void push_frame PARAMS ((HOST_WIDE_INT var_size, HOST_WIDE_INT boundary));
++static void push_frame_in_decls PARAMS ((tree block, HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void push_frame_in_args PARAMS ((tree parms, HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void push_frame_of_insns PARAMS ((rtx insn, HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void push_frame_in_operand PARAMS ((rtx insn, rtx orig, HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void push_frame_of_reg_equiv_memory_loc PARAMS ((HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void push_frame_of_reg_equiv_constant PARAMS ((HOST_WIDE_INT push_size, HOST_WIDE_INT boundary));
++static void reset_used_flags_for_push_frame PARAMS ((void));
++static int check_out_of_frame_access PARAMS ((rtx insn, HOST_WIDE_INT boundary));
++static int check_out_of_frame_access_in_operand PARAMS ((rtx, HOST_WIDE_INT boundary));
++#endif
++
++rtx
++assign_stack_local_for_pseudo_reg (mode, size, align)
++     enum machine_mode mode;
++     HOST_WIDE_INT size;
++     int align;
++{
++#if defined(FRAME_GROWS_DOWNWARD) || !defined(STACK_GROWS_DOWNWARD)
++  return assign_stack_local (mode, size, align);
++#else
++  tree blocks = DECL_INITIAL (current_function_decl);
++  rtx new;
++  HOST_WIDE_INT saved_frame_offset, units_per_push, starting_frame;
++  int first_call_from_purge_addressof, first_call_from_global_alloc;
++
++  if (! flag_propolice_protection
++      || size == 0
++      || ! blocks
++      || current_function_is_inlinable
++      || ! search_string_from_argsandvars (1)
++      || current_function_contains_functions)
++    return assign_stack_local (mode, size, align);
++
++  first_call_from_purge_addressof = !push_frame_offset && !cse_not_expected;
++  first_call_from_global_alloc = !saved_cse_not_expected && cse_not_expected;
++  saved_cse_not_expected = cse_not_expected;
++
++  starting_frame = (STARTING_FRAME_OFFSET)?STARTING_FRAME_OFFSET:BIGGEST_ALIGNMENT / BITS_PER_UNIT;
++  units_per_push = MAX(BIGGEST_ALIGNMENT / BITS_PER_UNIT,
++                     GET_MODE_SIZE (mode));
++    
++  if (first_call_from_purge_addressof)
++    {
++      push_frame_offset = push_allocated_offset;
++      if (check_out_of_frame_access (get_insns (), starting_frame))
++      {
++        /* if there is an access beyond frame, push dummy region to seperate
++           the address of instantiated variables */
++        push_frame (GET_MODE_SIZE (DImode), 0);
++        assign_stack_local (BLKmode, GET_MODE_SIZE (DImode), -1);
++      }
++    }
++
++  if (first_call_from_global_alloc)
++    {
++      push_frame_offset = push_allocated_offset = 0;
++      if (check_out_of_frame_access (get_insns (), starting_frame))
++      {
++        if (STARTING_FRAME_OFFSET)
++          {
++            /* if there is an access beyond frame, push dummy region 
++               to seperate the address of instantiated variables */
++            push_frame (GET_MODE_SIZE (DImode), 0);
++            assign_stack_local (BLKmode, GET_MODE_SIZE (DImode), -1);
++          }
++        else
++          push_allocated_offset = starting_frame;
++      }
++    }
++
++  saved_frame_offset = frame_offset;
++  frame_offset = push_frame_offset;
++
++  new = assign_stack_local (mode, size, align);
++
++  push_frame_offset = frame_offset;
++  frame_offset = saved_frame_offset;
++  
++  if (push_frame_offset > push_allocated_offset)
++    {
++      push_frame (units_per_push, push_allocated_offset + STARTING_FRAME_OFFSET);
++
++      assign_stack_local (BLKmode, units_per_push, -1);
++      push_allocated_offset += units_per_push;
++    }
++
++  /* At the second call from global alloc, alpha push frame and assign
++     a local variable to the top of the stack */
++  if (first_call_from_global_alloc && STARTING_FRAME_OFFSET == 0)
++    push_frame_offset = push_allocated_offset = 0;
++
++  return new;
++#endif
++}
++
++
++#if !defined(FRAME_GROWS_DOWNWARD) && defined(STACK_GROWS_DOWNWARD)
++/*
++  push frame infomation for instantiating pseudo register at the top of stack.
++  This is only for the "frame grows upward", it means FRAME_GROWS_DOWNWARD is 
++  not defined.
++
++  It is called by purge_addressof function and global_alloc (or reload)
++  function.
++*/
++static void
++push_frame (var_size, boundary)
++     HOST_WIDE_INT var_size, boundary;
++{
++  reset_used_flags_for_push_frame();
++
++  /* scan all declarations of variables and fix the offset address of the variable based on the frame pointer */
++  push_frame_in_decls (DECL_INITIAL (current_function_decl), var_size, boundary);
++
++  /* scan all argument variable and fix the offset address based on the frame pointer */
++  push_frame_in_args (DECL_ARGUMENTS (current_function_decl), var_size, boundary);
++
++  /* scan all operands of all insns and fix the offset address based on the frame pointer */
++  push_frame_of_insns (get_insns (), var_size, boundary);
++
++  /* scan all reg_equiv_memory_loc and reg_equiv_constant*/
++  push_frame_of_reg_equiv_memory_loc (var_size, boundary);
++  push_frame_of_reg_equiv_constant (var_size, boundary);
++
++  reset_used_flags_for_push_frame();
++}
++
++static void
++reset_used_flags_for_push_frame()
++{
++  int i;
++  extern rtx *reg_equiv_memory_loc;
++  extern rtx *reg_equiv_constant;
++
++  /* Clear all the USED bits in operands of all insns and declarations of local vars */
++  reset_used_flags_for_decls (DECL_INITIAL (current_function_decl));
++  reset_used_flags_for_insns (get_insns ());
++
++
++  /* The following codes are processed if the push_frame is called from 
++     global_alloc (or reload) function */
++  if (reg_equiv_memory_loc == 0) return;
++
++  for (i=LAST_VIRTUAL_REGISTER+1; i < max_regno; i++)
++    if (reg_equiv_memory_loc[i])
++      {
++      rtx x = reg_equiv_memory_loc[i];
++
++      if (GET_CODE (x) == MEM
++          && GET_CODE (XEXP (x, 0)) == PLUS
++          && AUTO_BASEPTR (XEXP (x, 0)) == frame_pointer_rtx)
++        {
++          /* reset */
++          XEXP (x, 0)->used = 0;
++        }
++      }
++
++  
++  if (reg_equiv_constant == 0) return;
++
++  for (i=LAST_VIRTUAL_REGISTER+1; i < max_regno; i++)
++    if (reg_equiv_constant[i])
++      {
++      rtx x = reg_equiv_constant[i];
++
++      if (GET_CODE (x) == PLUS
++          && AUTO_BASEPTR (x) == frame_pointer_rtx)
++        {
++          /* reset */
++          x->used = 0;
++        }
++      }
++}
++
++static void
++push_frame_in_decls (block, push_size, boundary)
++     tree block;
++     HOST_WIDE_INT push_size, boundary;
++{
++  tree types;
++  HOST_WIDE_INT offset;
++  rtx home;
++
++  while (block && TREE_CODE(block)==BLOCK)
++    {
++      types = BLOCK_VARS(block);
++      
++      while (types)
++      {
++        /* skip the declaration that refers an external variable and
++           also skip an global variable */
++        if (! DECL_EXTERNAL (types) && ! TREE_STATIC (types))
++          {
++          
++            if (!DECL_RTL_SET_P (types)) goto next;
++            home = DECL_RTL (types);
++
++            /* process for static local variable */
++            if (GET_CODE (home) == MEM
++                && GET_CODE (XEXP (home, 0)) == SYMBOL_REF)
++              goto next;
++
++            if (GET_CODE (home) == MEM
++                && GET_CODE (XEXP (home, 0)) == REG)
++              {
++                if (XEXP (home, 0) != frame_pointer_rtx
++                    || boundary != 0)
++                  goto next;
++
++                XEXP (home, 0) = plus_constant (frame_pointer_rtx,
++                                                push_size);
++
++                /* mark */
++                XEXP (home, 0)->used = 1;
++              }
++              
++            if (GET_CODE (home) == MEM
++                && GET_CODE (XEXP (home, 0)) == MEM)
++              {
++
++                /* process for dynamically allocated aray */
++                home = XEXP (home, 0);
++              }
++              
++            if (GET_CODE (home) == MEM
++                && GET_CODE (XEXP (home, 0)) == PLUS
++                && GET_CODE (XEXP (XEXP (home, 0), 1)) == CONST_INT)
++              {
++                offset = AUTO_OFFSET(XEXP (home, 0));
++
++                if (! XEXP (home, 0)->used
++                    && offset >= boundary)
++                  {
++                    offset += push_size;
++                    XEXP (XEXP (home, 0), 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++                    
++                    /* mark */
++                    XEXP (home, 0)->used = 1;
++                  }
++              }
++
++          }
++      next:
++        types = TREE_CHAIN(types);
++      }
++
++      push_frame_in_decls (BLOCK_SUBBLOCKS (block), push_size, boundary);
++      block = BLOCK_CHAIN (block);
++    }
++}
++
++
++static void
++push_frame_in_args (parms, push_size, boundary)
++     tree parms;
++     HOST_WIDE_INT push_size, boundary;
++{
++  rtx home;
++  HOST_WIDE_INT offset;
++    
++  for (; parms; parms = TREE_CHAIN (parms))
++    if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
++      {
++      if (PARM_PASSED_IN_MEMORY (parms) && DECL_NAME (parms))
++        {
++          home = DECL_INCOMING_RTL (parms);
++          offset = AUTO_OFFSET(XEXP (home, 0));
++
++          if (XEXP (home, 0)->used || offset < boundary) continue;
++
++          /* the operand related to the sweep variable */
++          if (AUTO_BASEPTR (XEXP (home, 0)) == frame_pointer_rtx)
++            {
++              if (XEXP (home, 0) == frame_pointer_rtx)
++                XEXP (home, 0) = plus_constant (frame_pointer_rtx,
++                                                push_size);
++              else {
++                offset += push_size;
++                XEXP (XEXP (home, 0), 1) = gen_rtx_CONST_INT (VOIDmode,
++                                                              offset);
++              }
++
++              /* mark */
++              XEXP (home, 0)->used = 1;
++            }
++        }
++      }
++}
++
++
++static int insn_pushed;
++static int *fp_equiv = 0;
++
++static void
++push_frame_of_insns (insn, push_size, boundary)
++     rtx insn;
++     HOST_WIDE_INT push_size, boundary;
++{
++  /* init fp_equiv */
++  fp_equiv = (int *) xcalloc (max_reg_num (), sizeof (int));
++              
++  for (; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++      || GET_CODE (insn) == CALL_INSN)
++      {
++      insn_pushed = FALSE;
++      push_frame_in_operand (insn, PATTERN (insn), push_size, boundary);
++
++      if (insn_pushed)
++        {
++          rtx trial = insn;
++          rtx before = PREV_INSN (trial);
++          rtx after = NEXT_INSN (trial);
++          int has_barrier = 0;
++          rtx tem;
++          rtx seq = split_insns (PATTERN (insn), insn);
++
++          /* If we are splitting a JUMP_INSN, it might be followed by a 
++             BARRIER. We may need to handle this specially.  */
++          if (after && GET_CODE (after) == BARRIER)
++            {
++              has_barrier = 1;
++              after = NEXT_INSN (after);
++            }
++
++          if (seq && GET_CODE (seq) == SEQUENCE)
++            {
++              if (XVECLEN (seq, 0) == 2)
++                {
++                  rtx pattern = PATTERN (XVECEXP (seq, 0, 1));
++
++                  if (GET_CODE (pattern) == SET
++                      && GET_CODE (XEXP (pattern, 0)) == REG
++                      && GET_CODE (XEXP (pattern, 1)) == PLUS
++                      && XEXP (pattern, 0) == XEXP (XEXP (pattern, 1), 0)
++                      && CONSTANT_P (XEXP (XEXP (pattern, 1), 1)))
++                    {
++                      rtx offset = XEXP (XEXP (pattern, 1), 1);
++                      fp_equiv[REGNO (XEXP (pattern, 0))] = INTVAL (offset);
++
++                      /* replace the pattern of the insn */
++                      add_insn_after (XVECEXP (seq, 0, 0), before);
++                      delete_insn (trial);
++                      goto next;
++                    }
++                }
++
++              /* excerpt from emit-rtl.c: L3320 */
++              tem = emit_insn_after (seq, trial);
++
++              delete_related_insns (trial);
++              if (has_barrier)
++                emit_barrier_after (tem);
++
++              /* Recursively call try_split for each new insn created */
++              for (tem = NEXT_INSN (before); tem != after;
++                   tem = NEXT_INSN (tem))
++                if (! INSN_DELETED_P (tem) && INSN_P (tem))
++                  tem = try_split (PATTERN (tem), tem, 1);
++            }
++        }
++
++      next:
++      /* push frame in NOTE */
++      push_frame_in_operand (insn, REG_NOTES (insn), push_size, boundary);
++
++      /* push frame in CALL EXPR_LIST */
++      if (GET_CODE (insn) == CALL_INSN)
++        push_frame_in_operand (insn, CALL_INSN_FUNCTION_USAGE (insn), push_size, boundary);
++      }
++
++  /* Clean up.  */
++  free (fp_equiv);
++}
++
++
++static void
++push_frame_in_operand (insn, orig, push_size, boundary)
++     rtx insn, orig;
++     HOST_WIDE_INT push_size, boundary;
++{
++  register rtx x = orig;
++  register enum rtx_code code;
++  int i, j;
++  HOST_WIDE_INT offset;
++  const char *fmt;
++
++  if (x == 0)
++    return;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++    case ASM_INPUT:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++    case RETURN:
++    case REG:
++    case ADDRESSOF:
++    case USE:
++      return;
++          
++    case SET:
++      /*
++      skip setjmp setup insn and setjmp restore insn
++      alpha case:
++      (set (MEM (reg:SI xx)) (frame_pointer_rtx)))
++      (set (frame_pointer_rtx) (REG))
++      */
++      if (GET_CODE (XEXP (x, 0)) == MEM
++        && XEXP (x, 1) == frame_pointer_rtx)
++      return;
++      if (XEXP (x, 0) == frame_pointer_rtx
++        && GET_CODE (XEXP (x, 1)) == REG)
++      return;
++
++      /*
++      powerpc case: restores setjmp address
++      (set (frame_pointer_rtx) (plus frame_pointer_rtx const_int -n))
++      or
++      (set (reg) (plus frame_pointer_rtx const_int -n))
++      (set (frame_pointer_rtx) (reg))
++      */
++      if (GET_CODE (XEXP (x, 0)) == REG
++        && GET_CODE (XEXP (x, 1)) == PLUS
++        && XEXP (XEXP (x, 1), 0) == frame_pointer_rtx
++        && CONSTANT_P (XEXP (XEXP (x, 1), 1))
++        && INTVAL (XEXP (XEXP (x, 1), 1)) < 0)
++      {
++        x = XEXP (x, 1);
++        offset = AUTO_OFFSET(x);
++        if (x->used || abs (offset) < boundary)
++          return;
++
++        XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset - push_size);
++        x->used = 1; insn_pushed = TRUE;
++        return;
++      }
++
++      /* reset fp_equiv register */
++      else if (GET_CODE (XEXP (x, 0)) == REG
++        && fp_equiv[REGNO (XEXP (x, 0))])
++      fp_equiv[REGNO (XEXP (x, 0))] = 0;
++
++      /* propagete fp_equiv register */
++      else if (GET_CODE (XEXP (x, 0)) == REG
++             && GET_CODE (XEXP (x, 1)) == REG
++             && fp_equiv[REGNO (XEXP (x, 1))])
++      if (REGNO (XEXP (x, 0)) <= LAST_VIRTUAL_REGISTER
++          || reg_renumber[REGNO (XEXP (x, 0))] > 0)
++        fp_equiv[REGNO (XEXP (x, 0))] = fp_equiv[REGNO (XEXP (x, 1))];
++      break;
++
++    case MEM:
++      if (XEXP (x, 0) == frame_pointer_rtx
++        && boundary == 0)
++      {
++        XEXP (x, 0) = plus_constant (frame_pointer_rtx, push_size);
++        XEXP (x, 0)->used = 1; insn_pushed = TRUE;
++        return;
++      }
++      break;
++      
++    case PLUS:
++      offset = AUTO_OFFSET(x);
++
++      /* Handle special case of frame register plus constant.  */
++      if (CONSTANT_P (XEXP (x, 1))
++        && XEXP (x, 0) == frame_pointer_rtx)
++      {
++        if (x->used || offset < boundary)
++          return;
++
++        XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset + push_size);
++        x->used = 1; insn_pushed = TRUE;
++
++        return;
++      }
++      /*
++      Handle alpha case:
++       (plus:SI (subreg:SI (reg:DI 63 FP) 0) (const_int 64 [0x40]))
++      */
++      if (CONSTANT_P (XEXP (x, 1))
++        && GET_CODE (XEXP (x, 0)) == SUBREG
++        && SUBREG_REG (XEXP (x, 0)) == frame_pointer_rtx)
++      {
++        if (x->used || offset < boundary)
++          return;
++
++        XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset + push_size);
++        x->used = 1; insn_pushed = TRUE;
++
++        return;
++      }
++      /*
++      Handle powerpc case:
++       (set (reg x) (plus fp const))
++       (set (.....) (... (plus (reg x) (const B))))
++      */
++      else if (CONSTANT_P (XEXP (x, 1))
++             && GET_CODE (XEXP (x, 0)) == REG
++             && fp_equiv[REGNO (XEXP (x, 0))])
++      {
++        if (x->used) return;
++
++        offset += fp_equiv[REGNO (XEXP (x, 0))];
++
++        XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++        x->used = 1; insn_pushed = TRUE;
++
++        return;
++      }
++      /*
++      Handle special case of frame register plus reg (constant).
++       (set (reg x) (const B))
++       (set (....) (...(plus fp (reg x))))
++      */
++      else if (XEXP (x, 0) == frame_pointer_rtx
++             && GET_CODE (XEXP (x, 1)) == REG
++             && PREV_INSN (insn)
++             && PATTERN (PREV_INSN (insn))
++             && SET_DEST (PATTERN (PREV_INSN (insn))) == XEXP (x, 1)
++             && CONSTANT_P (SET_SRC (PATTERN (PREV_INSN (insn)))))
++      {
++        HOST_WIDE_INT offset = INTVAL (SET_SRC (PATTERN (PREV_INSN (insn))));
++
++        if (x->used || offset < boundary)
++          return;
++        
++        SET_SRC (PATTERN (PREV_INSN (insn)))
++          = gen_rtx_CONST_INT (VOIDmode, offset + push_size);
++        x->used = 1;
++        XEXP (x, 1)->used = 1;
++
++        return;
++      }
++      /* Handle special case of frame register plus reg (used).  */
++      else if (XEXP (x, 0) == frame_pointer_rtx
++             && XEXP (x, 1)->used)
++      {
++        x->used = 1;
++        return;
++      }
++      /*
++      process further subtree:
++      Example:  (plus:SI (mem/s:SI (plus:SI (reg:SI 17) (const_int 8)))
++      (const_int 5))
++      */
++      break;
++
++    case CALL_PLACEHOLDER:
++      push_frame_of_insns (XEXP (x, 0), push_size, boundary);
++      push_frame_of_insns (XEXP (x, 1), push_size, boundary);
++      push_frame_of_insns (XEXP (x, 2), push_size, boundary);
++      break;
++
++    default:
++      break;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      {
++      if (XEXP (x, i) == frame_pointer_rtx && boundary == 0)
++        fatal_insn ("push_frame_in_operand", insn);
++      push_frame_in_operand (insn, XEXP (x, i), push_size, boundary);
++      }
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      push_frame_in_operand (insn, XVECEXP (x, i, j), push_size, boundary);
++}   
++
++static void
++push_frame_of_reg_equiv_memory_loc (push_size, boundary)
++     HOST_WIDE_INT push_size, boundary;
++{
++  int i;
++  extern rtx *reg_equiv_memory_loc;
++
++  /* This function is processed if the push_frame is called from 
++     global_alloc (or reload) function */
++  if (reg_equiv_memory_loc == 0) return;
++
++  for (i=LAST_VIRTUAL_REGISTER+1; i < max_regno; i++)
++    if (reg_equiv_memory_loc[i])
++      {
++      rtx x = reg_equiv_memory_loc[i];
++      int offset;
++
++      if (GET_CODE (x) == MEM
++          && GET_CODE (XEXP (x, 0)) == PLUS
++          && XEXP (XEXP (x, 0), 0) == frame_pointer_rtx)
++        {
++          offset = AUTO_OFFSET(XEXP (x, 0));
++          
++          if (! XEXP (x, 0)->used
++              && offset >= boundary)
++            {
++              offset += push_size;
++              XEXP (XEXP (x, 0), 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++
++              /* mark */
++              XEXP (x, 0)->used = 1;
++            }
++        }
++      else if (GET_CODE (x) == MEM
++               && XEXP (x, 0) == frame_pointer_rtx
++               && boundary == 0)
++        {
++          XEXP (x, 0) = plus_constant (frame_pointer_rtx, push_size);
++          XEXP (x, 0)->used = 1; insn_pushed = TRUE;
++        }
++      }
++}
++
++static void
++push_frame_of_reg_equiv_constant (push_size, boundary)
++     HOST_WIDE_INT push_size, boundary;
++{
++  int i;
++  extern rtx *reg_equiv_constant;
++
++  /* This function is processed if the push_frame is called from 
++     global_alloc (or reload) function */
++  if (reg_equiv_constant == 0) return;
++
++  for (i=LAST_VIRTUAL_REGISTER+1; i < max_regno; i++)
++    if (reg_equiv_constant[i])
++      {
++      rtx x = reg_equiv_constant[i];
++      int offset;
++
++      if (GET_CODE (x) == PLUS
++          && XEXP (x, 0) == frame_pointer_rtx)
++        {
++          offset = AUTO_OFFSET(x);
++          
++          if (! x->used
++              && offset >= boundary)
++            {
++              offset += push_size;
++              XEXP (x, 1) = gen_rtx_CONST_INT (VOIDmode, offset);
++
++              /* mark */
++              x->used = 1;
++            }
++        }
++      else if (x == frame_pointer_rtx
++               && boundary == 0)
++        {
++          reg_equiv_constant[i]
++            = plus_constant (frame_pointer_rtx, push_size);
++          reg_equiv_constant[i]->used = 1; insn_pushed = TRUE;
++        }
++      }
++}
++
++static int
++check_out_of_frame_access (insn, boundary)
++     rtx insn;
++     HOST_WIDE_INT boundary;
++{
++  for (; insn; insn = NEXT_INSN (insn))
++    if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
++      || GET_CODE (insn) == CALL_INSN)
++      {
++      if (check_out_of_frame_access_in_operand (PATTERN (insn), boundary))
++        return TRUE;
++      }
++  return FALSE;
++}
++
++
++static int
++check_out_of_frame_access_in_operand (orig, boundary)
++     rtx orig;
++     HOST_WIDE_INT boundary;
++{
++  register rtx x = orig;
++  register enum rtx_code code;
++  int i, j;
++  const char *fmt;
++
++  if (x == 0)
++    return FALSE;
++
++  code = GET_CODE (x);
++
++  switch (code)
++    {
++    case CONST_INT:
++    case CONST_DOUBLE:
++    case CONST:
++    case SYMBOL_REF:
++    case CODE_LABEL:
++    case PC:
++    case CC0:
++    case ASM_INPUT:
++    case ADDR_VEC:
++    case ADDR_DIFF_VEC:
++    case RETURN:
++    case REG:
++    case ADDRESSOF:
++      return FALSE;
++          
++    case MEM:
++      if (XEXP (x, 0) == frame_pointer_rtx)
++      if (0 < boundary) return TRUE;
++      break;
++      
++    case PLUS:
++      /* Handle special case of frame register plus constant.  */
++      if (CONSTANT_P (XEXP (x, 1))
++        && XEXP (x, 0) == frame_pointer_rtx)
++      {
++        if (0 <= AUTO_OFFSET(x)
++            && AUTO_OFFSET(x) < boundary) return TRUE;
++        return FALSE;
++      }
++      /*
++      process further subtree:
++      Example:  (plus:SI (mem/s:SI (plus:SI (reg:SI 17) (const_int 8)))
++      (const_int 5))
++      */
++      break;
++
++    case CALL_PLACEHOLDER:
++      if (check_out_of_frame_access (XEXP (x, 0), boundary)) return TRUE;
++      if (check_out_of_frame_access (XEXP (x, 1), boundary)) return TRUE;
++      if (check_out_of_frame_access (XEXP (x, 2), boundary)) return TRUE;
++      break;
++
++    default:
++      break;
++    }
++
++  /* Scan all subexpressions.  */
++  fmt = GET_RTX_FORMAT (code);
++  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
++    if (*fmt == 'e')
++      {
++      if (check_out_of_frame_access_in_operand (XEXP (x, i), boundary))
++        return TRUE;
++      }
++    else if (*fmt == 'E')
++      for (j = 0; j < XVECLEN (x, i); j++)
++      if (check_out_of_frame_access_in_operand (XVECEXP (x, i, j), boundary))
++        return TRUE;
++
++  return FALSE;
++}
++#endif
+diff -ruN gcc-3.3.1/gcc/protector.h gcc-3.3.1.pp/gcc/protector.h
+--- gcc-3.3.1/gcc/protector.h  1970-01-01 00:00:00.000000000 +0000
++++ gcc-3.3.1.pp/gcc/protector.h       2003-09-05 11:59:47.000000000 +0000
+@@ -0,0 +1,48 @@
++/* RTL buffer overflow protection function for GNU C compiler
++   Copyright (C) 1987, 88, 89, 92-7, 1998 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.  */
++
++
++/* declaration of GUARD variable */
++#define GUARD_m               Pmode
++#define UNITS_PER_GUARD MAX(BIGGEST_ALIGNMENT / BITS_PER_UNIT, GET_MODE_SIZE (GUARD_m))
++
++#ifndef L_stack_smash_handler
++
++/* insert a guard variable before a character buffer and change the order
++ of pointer variables, character buffers and pointer arguments */
++
++extern void prepare_stack_protection  PARAMS ((int inlinable));
++
++#ifdef TREE_CODE
++/* search a character array from the specified type tree */
++
++extern int search_string_def PARAMS ((tree names));
++#endif
++
++/* examine whether the input contains frame pointer addressing */
++
++extern int contains_fp PARAMS ((rtx op));
++
++/* allocate a local variable in the stack area before character buffers
++   to avoid the corruption of it */
++
++extern rtx assign_stack_local_for_pseudo_reg PARAMS ((enum machine_mode, HOST_WIDE_INT, int));
++
++#endif
+diff -ruN gcc-3.3.1/gcc/reload1.c gcc-3.3.1.pp/gcc/reload1.c
+--- gcc-3.3.1/gcc/reload1.c    2003-06-07 05:30:09.000000000 +0000
++++ gcc-3.3.1.pp/gcc/reload1.c 2003-09-05 11:58:59.000000000 +0000
+@@ -42,6 +42,7 @@
+ #include "toplev.h"
+ #include "except.h"
+ #include "tree.h"
++#include "protector.h"
+ /* This file contains the reload pass of the compiler, which is
+    run after register allocation has been done.  It checks that
+@@ -1992,7 +1993,7 @@
+       if (from_reg == -1)
+       {
+         /* No known place to spill from => no slot to reuse.  */
+-        x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size,
++        x = assign_stack_local_for_pseudo_reg (GET_MODE (regno_reg_rtx[i]), total_size,
+                                 inherent_size == total_size ? 0 : -1);
+         if (BYTES_BIG_ENDIAN)
+           /* Cancel the  big-endian correction done in assign_stack_local.
+diff -ruN gcc-3.3.1/gcc/simplify-rtx.c gcc-3.3.1.pp/gcc/simplify-rtx.c
+--- gcc-3.3.1/gcc/simplify-rtx.c       2003-07-03 07:38:22.000000000 +0000
++++ gcc-3.3.1.pp/gcc/simplify-rtx.c    2003-09-05 11:58:59.000000000 +0000
+@@ -1670,7 +1670,8 @@
+   int n_ops = 2, input_ops = 2, input_consts = 0, n_consts;
+   int first, negate, changed;
+   int i, j;
+-
++  HOST_WIDE_INT fp_offset = 0;
++  
+   memset ((char *) ops, 0, sizeof ops);
+   /* Set up the two operands and then expand them until nothing has been
+@@ -1695,6 +1696,10 @@
+         switch (this_code)
+           {
+           case PLUS:
++          if (flag_propolice_protection
++              && XEXP (this_op, 0) == virtual_stack_vars_rtx
++              && GET_CODE (XEXP (this_op, 1)) == CONST_INT)
++            fp_offset = INTVAL (XEXP (this_op, 1));
+           case MINUS:
+             if (n_ops == 7)
+               return NULL_RTX;
+@@ -1849,10 +1854,10 @@
+       && GET_CODE (ops[n_ops - 1].op) == CONST_INT
+       && CONSTANT_P (ops[n_ops - 2].op))
+     {
+-      rtx value = ops[n_ops - 1].op;
++      int value = INTVAL (ops[n_ops - 1].op);
+       if (ops[n_ops - 1].neg ^ ops[n_ops - 2].neg)
+-      value = neg_const_int (mode, value);
+-      ops[n_ops - 2].op = plus_constant (ops[n_ops - 2].op, INTVAL (value));
++      value = -value;
++      ops[n_ops - 2].op = plus_constant (ops[n_ops - 2].op, value);
+       n_ops--;
+     }
+@@ -1871,6 +1876,54 @@
+         || (n_ops + n_consts == input_ops && n_consts <= input_consts)))
+     return NULL_RTX;
++  if (flag_propolice_protection)
++    {
++      /* keep the addressing style of local variables
++       as (plus (virtual_stack_vars_rtx) (CONST_int x))
++       (1) inline function is expanded, (+ (+VFP c1) -c2)=>(+ VFP c1-c2)
++       (2) the case ary[r-1], (+ (+VFP c1) (+r -1))=>(+ R (+r -1))
++      */
++      for (i = 0; i < n_ops; i++)
++#ifdef FRAME_GROWS_DOWNWARD
++      if (ops[i].op == virtual_stack_vars_rtx)
++#else
++      if (ops[i].op == virtual_stack_vars_rtx
++          || ops[i].op == frame_pointer_rtx)
++#endif
++        {
++          if (GET_CODE (ops[n_ops - 1].op) == CONST_INT)
++            {
++              HOST_WIDE_INT value = INTVAL (ops[n_ops - 1].op);
++              if (n_ops < 3 || value >= fp_offset)
++                {
++                  ops[i].op = plus_constant (ops[i].op, value);
++                  n_ops--;
++                }
++              else
++                {
++                  if (!force
++                      && (n_ops+1 + n_consts > input_ops
++                          || (n_ops+1 + n_consts == input_ops && n_consts <= input_consts)))
++                    return NULL_RTX;
++                  ops[n_ops - 1].op = GEN_INT (value-fp_offset);
++                  ops[i].op = plus_constant (ops[i].op, fp_offset);
++                }
++            }
++          /* buf[BUFSIZE]: buf is the first local variable (+ (+ fp -S) S) 
++             or (+ (fp 0) r) ==> ((+ (+fp 1) r) -1) */
++          else if (fp_offset != 0)
++            return NULL_RTX;
++#ifndef FRAME_GROWS_DOWNWARD
++          /*
++           * For the case of buf[i], i: REG, buf: (plus fp 0),
++           */
++          else if (fp_offset == 0)
++            return NULL_RTX;
++#endif
++          break;
++        }
++    }
++
+   /* Put a non-negated operand first.  If there aren't any, make all
+      operands positive and negate the whole thing later.  */
+diff -ruN gcc-3.3.1/gcc/toplev.c gcc-3.3.1.pp/gcc/toplev.c
+--- gcc-3.3.1/gcc/toplev.c     2003-07-18 06:59:16.000000000 +0000
++++ gcc-3.3.1.pp/gcc/toplev.c  2003-09-05 11:58:59.000000000 +0000
+@@ -904,6 +904,13 @@
+    minimum function alignment.  Zero means no alignment is forced.  */
+ int force_align_functions_log;
++#if defined(STACK_PROTECTOR) && defined(STACK_GROWS_DOWNWARD)
++/* Nonzero means use propolice as a stack protection method */
++int flag_propolice_protection = 1;
++#else
++int flag_propolice_protection = 0;
++#endif
++
+ /* Table of supported debugging formats.  */
+ static const struct
+ {
+@@ -1188,6 +1195,10 @@
+    N_("Trap for signed overflow in addition / subtraction / multiplication") },
+   { "new-ra", &flag_new_regalloc, 1,
+    N_("Use graph coloring register allocation.") },
++  {"stack-protector", &flag_propolice_protection, 1,
++   N_("Enables stack protection") },
++  {"no-stack-protector", &flag_propolice_protection, 0,
++   N_("Disables stack protection") },
+ };
+ /* Table of language-specific options.  */
+@@ -1547,7 +1558,9 @@
+   {"missing-noreturn", &warn_missing_noreturn, 1,
+    N_("Warn about functions which might be candidates for attribute noreturn") },
+   {"strict-aliasing", &warn_strict_aliasing, 1,
+-   N_ ("Warn about code which might break the strict aliasing rules") }
++   N_ ("Warn about code which might break the strict aliasing rules") },
++  {"stack-protector", &warn_stack_protector, 1,
++   N_("Warn when disabling stack protector for some reason")}
+ };
+ void
+@@ -2449,6 +2462,8 @@
+       insns = get_insns ();
++      if (flag_propolice_protection) prepare_stack_protection (inlinable);
++  
+       /* Dump the rtl code if we are dumping rtl.  */
+       if (open_dump_file (DFI_rtl, decl))
This page took 2.390601 seconds and 4 git commands to generate.