--- /dev/null
+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, ®_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 (®_set_obstack);
++}
++
++static void
++free_reg_set_mem ()
++{
++ free (reg_set_table);
++ obstack_free (®_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 (®_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 = ®_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 = ®_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 = ®_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 = ®_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 = ®_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 (¬e, 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, ®_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))