Category : UNIX Files
Archive   : GPLUS3.ZIP
Filename : G3

 
Output of file : G3 contained in archive : GPLUS3.ZIP

#!/bin/sh
# This is a shell archive.
# remove everything above the "#!/bin/sh" line
# and feed to /bin/sh
#
# Contents:
# print-tree.c
# README
# cplus-method.c
# g++filt.c
# stab.def
# case.c
# cplus-parse.h
# gcc.c
# stab.h
# ccrt0.c
# cplus-parse.y
# getpagesize.h
# stack.h
# collect.c
# cplus-ptree.c
# gnulib3.c
# stmt.c
# collect2.c
# cplus-search.c
# packed: Wed Jan 01 23:08:18 EST 1992
# by: [email protected]
#
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo "Extracting print-tree.c..."
sed 's/^X//' >print-tree.c << '!EOF'
X/* Prints out tree in human readable form - GNU C-compiler
X Copyright (C) 1987 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X#include "config.h"
X#include "tree.h"
X#include
X
Xextern char **tree_code_name;
X
Xextern char *mode_name[];
X
Xextern char spaces[];
X
X#define MIN(x,y) ((x < y) ? x : y)
X
Xstatic FILE *outfile;
X
Xextern int tree_node_counter;
X
X/* markvec[i] is 1 if node number i has been seen already. */
X
Xstatic char *markvec;
X
Xstatic void dump ();
Xvoid dump_tree ();
X
Xvoid
Xdebug_dump_tree (root)
X tree root;
X{
X dump_tree (stderr, root);
X}
X
X/* Don't know, why they have different names in C and c++ (hgs) */
Xvoid
Xdebug_tree (root)
X tree root;
X{
X dump_tree (stderr, root);
X}
X
Xvoid
Xdump_tree (outf, root)
X FILE *outf;
X tree root;
X{
X markvec = (char *) alloca (tree_node_counter + 1);
X bzero (markvec, tree_node_counter + 1);
X outfile = outf;
X dump (root, 0);
X fflush (outf);
X}
X
Xstatic
Xvoid
Xwruid (node)
X tree node;
X{
X
X if (node == NULL)
X fputs ("<>", outfile);
X else {
X fprintf (outfile, "%1d", TREE_UID (node));
X }
X}
X
Xstatic
Xvoid
Xpart (title, node)
X char title[];
X tree node;
X{
X fprintf (outfile, " %s = ", title);
X wruid (node);
X putc (';', outfile);
X}
X
X/* Similar to `part' but prefix with @ if value is not constant
X and print the constant value if it is constant. */
Xstatic
Xvoid
Xcpart (title, ct, punct)
X char *title;
X tree ct;
X char punct;
X{
X fprintf (outfile, " %s = ", title);
X if (ct == NULL)
X fputs ("<>", outfile);
X else
X {
X if (!TREE_LITERAL (ct))
X {
X putc ('@', outfile);
X wruid (ct);
X }
X else
X fprintf (outfile, "%ld", TREE_INT_CST_LOW (ct));
X }
X putc (punct, outfile);
X}
X
Xstatic void
Xwalk (node, leaf, indent)
X tree node;
X tree leaf;
X int indent;
X{
X if (node != NULL
X /* Don't walk any global nodes reached from local nodes!
X The global nodes will be dumped at the end, all together.
X Also don't mention a FUNCTION_DECL node that is marked local
X since it was fully described when it was dumped locally. */
X && (TREE_CODE (node) != FUNCTION_DECL
X || TREE_PERMANENT (node))
X && (TREE_PERMANENT (leaf) == TREE_PERMANENT (node)))
X dump (node, indent+1);
X}
X
Xstatic void
Xcwalk (s, leaf, indent)
X tree s;
X tree leaf;
X int indent;
X{
X if (s != NULL)
X if (!TREE_LITERAL (s))
X walk (s, leaf, indent);
X}
X
Xstatic void
Xprtypeinfo (node)
X register tree node;
X{
X int first;
X
X part ("type", TREE_TYPE (node));
X first = 1;
X fputs (" [", outfile);
X if (TREE_EXTERNAL (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("external", outfile);
X first = 0;
X }
X if (TREE_PUBLIC (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("public", outfile);
X first = 0;
X }
X if (TREE_STATIC (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("static", outfile);
X first = 0;
X }
X if (TREE_VOLATILE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("volatile", outfile);
X first = 0;
X }
X if (TREE_PACKED (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("packed", outfile);
X first = 0;
X }
X if (TREE_READONLY (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("readonly", outfile);
X first = 0;
X }
X if (TREE_LITERAL (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("literal", outfile);
X first = 0;
X }
X if (TREE_NONLOCAL (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("nonlocal", outfile);
X first = 0;
X }
X if (TREE_ADDRESSABLE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("addressable", outfile);
X first = 0;
X }
X if (TREE_REGDECL (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("regdecl", outfile);
X first = 0;
X }
X if (TREE_THIS_VOLATILE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("this_vol", outfile);
X first = 0;
X }
X if (TREE_UNSIGNED (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("unsigned", outfile);
X first = 0;
X }
X if (TREE_ASM_WRITTEN (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("asm_written", outfile);
X first = 0;
X }
X if (TREE_INLINE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("inline", outfile);
X first = 0;
X }
X if (TREE_USED (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("used", outfile);
X first = 0;
X }
X if (TREE_PERMANENT (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("permanent", outfile);
X first = 0;
X }
X if (TREE_LANG_FLAG_1 (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("lang_flag_1", outfile);
X first = 0;
X }
X if (TREE_LANG_FLAG_2 (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("lang_flag_2", outfile);
X first = 0;
X }
X if (TREE_LANG_FLAG_3 (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("lang_flag_3", outfile);
X first = 0;
X }
X if (TREE_LANG_FLAG_4 (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("lang_flag_4", outfile);
X first = 0;
X }
X fputs ("] ", outfile);
X}
X
Xstatic void
Xprdeclmodeinfo (node)
X tree node;
X{
X register enum machine_mode mode = DECL_MODE (node);
X fprintf (outfile, " %s;", mode_name[(int) mode]);
X
X cpart ("size", DECL_SIZE (node), '*');
X fprintf (outfile, "%d;", DECL_SIZE_UNIT (node));
X
X fprintf (outfile, " alignment = %1d;", DECL_ALIGN (node));
X}
X
Xstatic void
Xprtypemodeinfo (node)
X tree node;
X{
X register enum machine_mode mode = TYPE_MODE (node);
X fprintf (outfile, " %s;", mode_name[(int) mode]);
X
X cpart ("size", TYPE_SIZE (node), '*');
X fprintf (outfile, "%d;", TYPE_SIZE_UNIT (node));
X
X fprintf (outfile, " alignment = %1d;", TYPE_ALIGN (node));
X}
X
Xstatic void
Xskip (indent)
X int indent;
X{
X putc ('\n',outfile);
X fputs (spaces + (strlen (spaces) - (12 + MIN (40,(indent+1)*2))), outfile);
X}
X
X/* Output a description of the tree node NODE
X if its description has not been output already. */
X
Xstatic
Xvoid
Xdump (node, indent)
X tree node;
X int indent;
X{
X register enum tree_code code = TREE_CODE (node);
X register int i;
X register int len, first_rtl;
X int nochain = 0;
X
X if (markvec[TREE_UID (node)])
X return;
X markvec[TREE_UID (node)] = 1;
X
X fputs (" ", outfile);
X fprintf (outfile, "%5d", TREE_UID (node));
X fputs (spaces + (strlen (spaces) - MIN (40, (indent+1)*2)), outfile);
X fputs (tree_code_name[(int) code], outfile);
X
X switch (*tree_code_type[(int) code])
X {
X case 'd':
X fputs (" name = ", outfile);
X if (DECL_NAME (node) == NULL)
X fputs ("<>;", outfile);
X else
X fprintf (outfile, "%s;",
X IDENTIFIER_POINTER (DECL_NAME (node)));
X if (code != PARM_DECL)
X fprintf (outfile, " at %s line %d;",
X DECL_SOURCE_FILE (node), DECL_SOURCE_LINE (node));
X skip (indent);
X prdeclmodeinfo (node);
X prtypeinfo (node);
X#ifdef PRINT_LANG_DECL
X print_lang_decl (node);
X#endif
X skip (indent);
X fprintf (outfile, " offset = %1d;", DECL_OFFSET (node));
X if (DECL_VOFFSET (node) != NULL)
X {
X fputs ("voffset = ", outfile);
X wruid (DECL_VOFFSET (node));
X fprintf (outfile, "*%1d;", DECL_VOFFSET_UNIT (node));
X }
X part ("context", DECL_CONTEXT (node));
X if (code == FUNCTION_DECL)
X {
X if (DECL_ARGUMENTS (node) || DECL_RESULT (node)
X || DECL_INITIAL (node))
X {
X skip (indent);
X part ("arguments", DECL_ARGUMENTS (node));
X part ("result", DECL_RESULT (node));
X if ((int) (DECL_INITIAL (node)) == 1)
X fprintf (outfile, " initial = const 1;");
X else
X part ("initial", DECL_INITIAL (node));
X }
X }
X else if (DECL_INITIAL (node))
X {
X if ((int) (DECL_INITIAL (node)) == 1)
X fprintf (outfile, " initial = const 1;");
X else
X part ("initial", DECL_INITIAL (node));
X }
X#ifdef PRINT_LANG_DECL
X walk_lang_decl (node);
X#endif
X part ("chain", TREE_CHAIN (node));
X /* A Decl's chain contents is not part of the decl. */
X nochain = 1;
X fputc ('\n', outfile);
X cwalk (DECL_SIZE (node), node, indent);
X walk (TREE_TYPE (node), node, indent);
X walk (DECL_VOFFSET (node), node, indent);
X walk (DECL_CONTEXT (node), node, indent);
X if (code == FUNCTION_DECL)
X {
X walk (DECL_ARGUMENTS (node), node, indent);
X walk (DECL_RESULT (node), node, indent);
X }
X if ((int) (DECL_INITIAL (node)) != 1)
X walk (DECL_INITIAL (node), node, indent);
X break;
X
X case 't':
X prtypemodeinfo (node);
X prtypeinfo (node);
X#ifdef PRINT_LANG_TYPE
X print_lang_type (node);
X#endif
X skip (indent);
X part ("pointers_to_this", TYPE_POINTER_TO (node));
X if (code == ARRAY_TYPE || code == SET_TYPE)
X {
X part ("domain", TYPE_DOMAIN (node));
X cpart ("separation", TYPE_SEP (node), '*');
X fprintf (outfile, "%d;", TYPE_SEP_UNIT (node));
X }
X else if (code == INTEGER_TYPE)
X {
X cpart ("min", TYPE_MIN_VALUE (node), ';');
X cpart ("max", TYPE_MAX_VALUE (node), ';');
X fprintf (outfile, "precision = %d;", TYPE_PRECISION (node));
X }
X else if (code == ENUMERAL_TYPE)
X {
X cpart ("min", TYPE_MIN_VALUE (node), ';');
X cpart ("max", TYPE_MAX_VALUE (node), ';');
X part ("values", TYPE_VALUES (node));
X fprintf (outfile, "precision = %d;", TYPE_PRECISION (node));
X }
X else if (code == REAL_TYPE)
X {
X fprintf (outfile, "precision = %d;", TYPE_PRECISION (node));
X }
X else if (code == RECORD_TYPE
X || code == UNION_TYPE)
X {
X part ("fields", TYPE_FIELDS (node));
X }
X else if (code == FUNCTION_TYPE)
X {
X part ("arg_types", TYPE_ARG_TYPES (node));
X }
X else if (code == METHOD_TYPE)
X {
X part ("arg_types", TYPE_ARG_TYPES (node));
X }
X#ifdef PRINT_LANG_TYPE
X walk_lang_type (node);
X#endif
X part ("chain", TREE_CHAIN (node));
X /* A type's chain's contents are not printed because the chain of types
X is not part of the meaning of any particular type. */
X nochain = 1;
X fputc ('\n', outfile);
X cwalk (TYPE_SIZE (node), node, indent);
X walk (TREE_TYPE (node), node, indent);
X walk (TYPE_VALUES (node), node, indent);
X walk (TYPE_SEP (node), node, indent);
X walk (TYPE_POINTER_TO (node), node, indent);
X walk (TYPE_REFERENCE_TO (node), node, indent);
X break;
X
X case 'e':
X case 'r':
X prtypeinfo (node);
X fputs (" ops =", outfile);
X first_rtl = len = tree_code_length[(int) code];
X /* These kinds of nodes contain rtx's, not trees,
X after a certain point. Print the rtx's as rtx's. */
X switch (code)
X {
X case SAVE_EXPR:
X first_rtl = 1;
X break;
X case CALL_EXPR:
X first_rtl = 2;
X break;
X case METHOD_CALL_EXPR:
X first_rtl = 3;
X break;
X case WITH_CLEANUP_EXPR:
X /* Should be defined to be 2. */
X first_rtl = 1;
X break;
X case RTL_EXPR:
X first_rtl = 0;
X }
X for (i = 0; i < len; i++)
X {
X if (i >= first_rtl)
X {
X skip (indent);
X if (TREE_OPERAND (node, i))
X print_rtl (outfile, TREE_OPERAND (node, i));
X else
X fprintf (outfile, "(nil)");
X fprintf (outfile, "\n");
X }
X else
X {
X fputs (" ", outfile);
X wruid (TREE_OPERAND (node, i));
X fputs (";", outfile);
X }
X }
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X for (i = 0; i < len && i < first_rtl; i++)
X walk (TREE_OPERAND (node, i), node, indent);
X break;
X
X case 's':
X prtypeinfo (node);
X fprintf (outfile, " at %s line %d;",
X STMT_SOURCE_FILE (node), STMT_SOURCE_LINE (node));
X switch (TREE_CODE (node))
X {
X case IF_STMT:
X part ("cond", STMT_COND (node));
X part ("then", STMT_THEN (node));
X part ("else", STMT_ELSE (node));
X break;
X
X case LET_STMT:
X case WITH_STMT:
X part ("vars", STMT_VARS (node));
X part ("tags", STMT_TYPE_TAGS (node));
X part ("supercontext", STMT_SUPERCONTEXT (node));
X part ("bind_size", STMT_BIND_SIZE (node));
X part ("body", STMT_BODY (node));
X part ("subblocks", STMT_SUBBLOCKS (node));
X break;
X
X case CASE_STMT:
X part ("case_index", STMT_CASE_INDEX (node));
X part ("case_list", STMT_CASE_LIST (node));
X break;
X
X default:
X part ("body", STMT_BODY (node));
X break;
X }
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X switch (TREE_CODE (node))
X {
X case IF_STMT:
X walk (STMT_COND (node), node, indent);
X walk (STMT_THEN (node), node, indent);
X walk (STMT_ELSE (node), node, indent);
X break;
X
X case LET_STMT:
X case WITH_STMT:
X walk (STMT_VARS (node), node, indent);
X walk (STMT_TYPE_TAGS (node), node, indent);
X walk (STMT_SUPERCONTEXT (node), node, indent);
X walk (STMT_BIND_SIZE (node), node, indent);
X walk (STMT_BODY (node), node, indent);
X walk (STMT_SUBBLOCKS (node), node, indent);
X break;
X
X case CASE_STMT:
X walk (STMT_CASE_INDEX (node), node, indent);
X walk (STMT_CASE_LIST (node), node, indent);
X break;
X
X default:
X walk (STMT_BODY (node), node, indent);
X break;
X }
X break;
X
X case 'c':
X switch (code)
X {
X case INTEGER_CST:
X if (TREE_INT_CST_HIGH (node) == 0)
X fprintf (outfile, " = %1u;", TREE_INT_CST_LOW (node));
X else if (TREE_INT_CST_HIGH (node) == -1
X && TREE_INT_CST_LOW (node) != 0)
X fprintf (outfile, " = -%1u;", -TREE_INT_CST_LOW (node));
X else
X fprintf (outfile, " = 0x%x%08x;",
X TREE_INT_CST_HIGH (node),
X TREE_INT_CST_LOW (node));
X break;
X
X case REAL_CST:
X#ifndef REAL_IS_NOT_DOUBLE
X fprintf (outfile, " = %e;", TREE_REAL_CST (node));
X#else
X {
X int i;
X char *p = (char *) &TREE_REAL_CST (node);
X fprintf (outfile, " = 0x");
X for (i = 0; i < sizeof TREE_REAL_CST (node); i++)
X fprintf (outfile, "%02x", *p++);
X fprintf (outfile, ";");
X }
X#endif /* REAL_IS_NOT_DOUBLE */
X break;
X
X case COMPLEX_CST:
X part ("realpart", TREE_REALPART (node));
X part ("imagpart", TREE_IMAGPART (node));
X walk (TREE_REALPART (node), node, indent);
X walk (TREE_IMAGPART (node), node, indent);
X break;
X
X case STRING_CST:
X fprintf (outfile, " = \"%s\";", TREE_STRING_POINTER (node));
X }
X prtypeinfo (node);
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X break;
X
X case 'x':
X if (code == IDENTIFIER_NODE)
X {
X fprintf (outfile, " = %s;\n", IDENTIFIER_POINTER (node));
X nochain = 1;
X }
X else if (code == TREE_LIST)
X {
X prtypeinfo (node);
X part ("purpose", TREE_PURPOSE (node));
X part ("value", TREE_VALUE (node));
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X walk (TREE_PURPOSE (node), node, indent);
X walk (TREE_VALUE (node), node, indent);
X }
X else if (code == TREE_VEC)
X {
X prtypeinfo (node);
X len = TREE_VEC_LENGTH (node);
X fprintf (outfile, "length = %d\n", len);
X for (i = 0; i < len; i++)
X {
X fputs (" ", outfile);
X wruid (TREE_VEC_ELT (node, i));
X fputs (";", outfile);
X }
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X for (i = 0; i < len; i++)
X walk (TREE_VEC_ELT (node, i), node, indent);
X }
X else if (code == OP_IDENTIFIER)
X {
X prtypeinfo (node);
X part ("op1", TREE_PURPOSE (node));
X part ("op2", TREE_VALUE (node));
X part ("chain", TREE_CHAIN (node));
X fputc ('\n', outfile);
X walk (TREE_TYPE (node), node, indent);
X walk (TREE_PURPOSE (node), node, indent);
X walk (TREE_VALUE (node), node, indent);
X }
X else if (code == ERROR_MARK)
X fputc ('\n', outfile);
X else abort ();
X
X break;
X
X default:
X abort ();
X } /* switch */
X
X if (TREE_CHAIN (node) != NULL && ! nochain)
X dump (TREE_CHAIN (node), indent);
X}
!EOF
echo "Extracting README..."
sed 's/^X//' >README << '!EOF'
XThis is the file README. It documents release 1.40.3 of the GNU C++
Xcompiler system, still in test release status. Many bugs reported in
Xthe previous test releases have been fixed. Some bugs surely remain.
XTo see the list of current issues/bugs in GNU C++, look at the file
Xdist-g++/ideas, or see the BugList section of the GNU C++ Users Guide.
X
XSee the file HINTS for special clues relating to GNU C++ configuration
Xand troubleshooting.
X
XIntroduction.
X
XGNU C++ is GNU CC, with a C++ front end. GNU CC is the Free Software
XFoundation's optimizing, retargetable, ANSI C compiler. GNU CC has
Xsource level debugging support from the GDB debugger (also available
Xfrom the Free Software Foundation). For more information about GNU
XCC, write to the Free Software Foundation at the address given below.
XGNU C++ is an extension of GNU CC. It keeps the powerful optimizer,
Xand maintains a high degree of source-level debugging support, while
Xproviding the object-oriented features of C++. Numerous files have
Xbeen added, changed, and hacked without mercy from their original GNU
XCC incarnation to make this compiler accept a reasonable super/subset
Xof C++.
X
X*Disclaimer*: GNU C++ is not perfectly compatible with AT&T
XC++. It is, however, a reasonably faithful implementation of C++ as
Xdescribed in the C++ reference manual. To make GNU C++ more mobile,
Xonly the files that are not shared with GNU CC are currently being
Xdistributed. If you do not have GNU CC yet, or your version is older
Xthan 1.40, you should take care of getting that first. GNU CC is
Xavailable to sites which have anonymous ftp capability to
Xprep.ai.mit.edu. Contact the Free Software Foundation for more
Xinformation.
X
XGNU C++ is still under development, but has stabilized (hopefully) to
Xthe point where bug fixes will not require major implementation or
Xreimplementation. The purpose of this distribution is to give
Xinterested parties a chance to start working with a free C++ compiler.
XIt is expected (and hoped) that this compiler will continue to evolve
Xat a fairly rapid pace. I am hoping to receive not just bug reports,
Xbut also code contributions, new features, and anything else that
Xmakes GNU C++ a better compiler.
X
XDocumentation for GNU C++ does not really exist. If you would like to
Xwrite some, more power to you. What does exist is in the file
Xdist-g++/g++.texinfo .
X
XGNU C++ is no longer distributed with a special debugger. The current
Xversion of GDB needed to debug GNU C++ program is version 3.5, which
Xcan be gotten from prep.ai.mit.edu in the usual way.
X
XCurrently, the debugger supports all of the features of the compiler,
Xexcept for: new and delete, operator forms of new and delete, user
Xdefined type conversion, and multiple inheritance when multiple
Xinheritance must actually be used. The reason for these exceptions is
Xthe degree of difficulty of their implementation. I am waiting until
XI see a clean, general way of doing it. Until then, I would
Xappreciate any input (thoughts, caveats, source code) you would like
Xto provide.
X
XA name demangler has been provided by James Clark. Here is the text
Xfrom his README file:
X
X This package contains a demangler for GNU C++ version 1.40
X (October 19 version); it will probably require work for other
X versions.
X
X It has *not* been extensively tested, so use with caution.
X
X Demangling converts an encoded g++ symbol name approximately into
X the form of a C++ declaration.
X
X cplus-dem.c provides a self-contained implementation of the
X demangling function cplus_demangle.
X
X g++filt.c is a simple filter that illustrates the use of
X cplus_demangle. It has a similar function to the c++filt program
X provided for cfront 2.0. It filters its input replacing encoded
X g++ symbol names by their demangled equivalents. Anything not part
X of a g++ symbol name is passed through unchanged. It can be used
X to filter the output of, for example, gprof or nm.
X
X ld.c.patch contains a rather ugly patch to ld.c (the version which
X comes with g++) to make it use cplus_demangle for printing the
X names of undefined symbols when a -lg++ option is given.
X
X James Clark
X [email protected]
X
XGNU C++ was originally developed on a Sun-3 workstation, running Sun's
XOS 3.5, and was migrated to a Sun-4 workstation, running Sun's OS 4.0
X(Berkeley 4.2 compatible with some System V enhancements). The GNU
XC++ library was developed on a VAX 11/750 running BSD 4.3, and is now
Xbeing developed on a Sun-4 workstation as well. If you have machines
Xother than these, or any kind of machine running System V, you may
Xexperience installation difficulties due to conditions which I cannot
Xanticipate. I will try to help you with problems on these machines,
Xbut my primary goal is supporting GNU C++, and not System V (or VMS).
X
XInstallation of GNU C++. (From distribution tape)
X
XSelect the directory in your file system where GNU code usually goes.
XIf this is your first GNU code, then you have probably not installed
XGNU CC. Please install it, and then continue with these instructions.
XYou also need to have bison installed. In this directory, you should
Xhave the subdirectories
X
X gcc-1.40/
X gcc/ linked to -> gcc-1.40
X gcc-test/
X
Xand possibly others. Unload the tape by using the tar command.
X
X install% tar xvf TARFILE
X
Xwhere TARFILE is either the name of a tar file if you got the
Xdistribution via ftp, or is the name of the tape device on which the
Xrelease tape is held.
X
XYour directory tree should now contain the additional file
X
X g++-1.40.3/
X
XIf you have specifically requested that the GNU C++ library be
Xincluded on the tape that we make for you, you will also have the
Xdirectory
X
X libg++-1.40.3/
X
XIf you got this tar file electronically instead of by tape, these
Xdirectories will come from separate tar files.
X
XThe GNU C++ library contains header files such as `stdio.h',
X`stream.h', etc., which are useful when trying examples from the book.
XIt also contains a number of useful classes which serve both as
Xfunction program units, as well as example C++ code.
X
XThe code for GNU C++ is in the directory g++-1.40.3/. Here is how to
Xinstall GNU C++:
X
X(0) Installation of GNU C++ assumes that you will share binaries
X with GNU CC. If this is not the case, then run "make cleanconfig"
X after running "make maketest".
X
X(1) Make a directory, e.g. g++/ and `cd' into it
X
X install% mkdir g++
X install% cd g++
X
X(2) Make symbolic links from all files in the g++-1.40.3 directory
X to the current g++ directory, i.e., ln -s ../g++-1.40.3/* .
X
X(3) Read carefully the comments at the top of the Makefile, to see
X what flags, you will need to modify, if any. For example, if you
X have a System V machine, you may have to uncomment the line which
X defines USG_STDIO.
X
X(4) Do a `make' of the "maketest" target in the Makefile. If your
X directory structure is as described here, you need not give any
X additional arguments. Otherwise, you must set DIR to the
X directory which contains GNU CC sources, and TDIR to the directory
X which contains GNU CC object files. The variable CDIR is the
X directory for GNU CC's machine-specific files. You do not need to
X explicitly give this a value unless you have moved GNU CC's
X "config" directory relative to the DIR director.
X
XThe error messages about links that could not be made should be
Xignored.
X
X(5) Configure the compiler for the machine target you want. This is
X accomplished by running the program "config.g++". For example, on
X a Sun4 running SunOS 4.0, you would type:
X
X install% config.g++ sun4-os4
X
XNow, You should still be in the directory g++/:
X
X install% pwd
X ./g++
X install%
X
XIf GAS and GNU LD work for your machine, *use them*. That will permit
Xyou to use the system's crt0.o, including mcrt0.o and gcrt0.o which
Xsupport profiling and other features. Said another way, if you use
XGNU ld and GNU as, don't use special crt0.o!
X
XTo install GAS, you should have release 1.34 or greater. Since GAS
Xand GNU C/C++ do not share source code, there is no need to try to
Xmatch GAS and GNU C/C++ version numbers. To install GAS in such a way
Xthat GNU C/C++ can find it, install it as `gcc-as' wherever `gcc-cc1'
Xand `gcc-cc1plus' are installed. Usually this is
X`/usr/local/lib/gcc-as'. Similarly install GNU LD as `gcc-ld' where
X`gcc-cc1' and friends are installed, usually `/usr/local/lib/gcc-ld'.
X
XGNU LD will not work if your system uses COFF object files. In
Xthis case, use the `collect3' program.
X
X[If neither GNU LD or `collect' works, use collect2.c. (In
Xparticular, use it on Convex machines.) To use collect2.c, your
Xsystem must support the -r flag of ld. Edit collect2.c if
Xnecessary to tell it about your assembler syntax, then compile it
Xwith gcc and install it as /usr/local/lib/gcc-ld.]
X
X*Very Important* [For non-GAS users]: If you do not use GAS, GNU C++
Xmay need to use its own crt0.c, borrowed and modified from GNU Emacs.
XYou should verify that the crt0.c provided is fed suitable definitions
Xfor correct compilation. If you have GNU Emacs, and you are not
Xcompiling to a SUN, consult your local GNU Emacs guru, to see what
Xsort of #defines are required for proper operation.
X
XIn any event, at this point, just type `make':
X
X install% make
X
XIf you are not using a SUN, you will need to use the appropriate
Xmachine dependent files, as per GNU CC. If you do not provide a
Xproper crt0.c, any executable produced by GNU C++ may fail to run at
Xall. Conversely, if you have a program which does not make it as far
Xas the first line in main (), you have probably failed to provide the
Xcorrect flags to the compiler when building crt0.c.
X
XYou have now just made GNU C++. Having done that, you should now
Xproceed to use GNU C++ to build the GNU C++ run-time libraries, which
Xare in the directory dist-libg++/ . This code was contributed
Xby Doug Lea, and implements streams, obstacks, structured files, and
Xother C++ public service objects. The README in that directory
Xexplains the installation procedure for that code. Also, by making
Xthe library and running the test programs (the Makefile in
Xdist-libg++/ will tell you what to do), you can verify that GNU
XC++ has been properly installed.
X
XGNU ld is normally distributed with the GNU binary utilities. It is
Xpreferable to use that for linking GNU C++ programs. However, it is
Xpossible to link GNU C++ programs using the version of GNU ld
Xdistributed with GNU C++ (this has been tested under SunOS 4.0 and
Xunder SunOS 4.1.1). To install GNU ld from these sources, execute the
Xfollowing command:
X
X install% make install-ld
X
XInstallation of GDB.
X
XGNU C++ and GDB 3.4 are intended to be compatible. GDB+ no longer
Xexists. Read installation instructions provided in dist-gdb.
X
XSuggestions.
X
XI suggest making all of the files in ../gcc/ read-only, so that when
Xyou are making modifications to files of GNU C++, you will notice when
Xnew ground is being broken. It also helps to know what files can be
Xupdated from the standard GNU software without impacting GNU C++. For
Xexample, if there is a bug fix for the file `cse.c', that same fix
Xapplies to GNU CC and GNU C++. With both compilers pointing to the
Xsame directory via symbolic links, one need not concern oneself
Xwith those changes.
X
XHave fun!
X
XMichael Tiemann
X
X1/11/91
X
XFor more information.
X
XFor questions concerning GNU CC and GDB, the Free Software Foundation
Xmaintains the following address:
X
X The Free Software Foundation
X 675 Massachusetts Avenue
X Cambridge, Mass
X 02139
X
X Phone: (617) 876 - 3296
X
XCygnus Support provides commercial support for GNU C++ on a fixed-fee
Xbasis. If you want support, or have support-related questions, please
Xcontact:
X
X Cygnus Support
X 814 University Avenue
X Palo Alto, CA
X 94301
X
X Phone: (415) 322-3811
X
XSoftware from the Free Software Foundation is provided with absolutely
Xno warranty, to the extent permitted by applicable state law.
XRedistribution of its code (source and/or executable) by MCC does not
Ximply that MCC offers a warranty for such code.
!EOF
echo "Extracting cplus-method.c..."
sed 's/^X//' >cplus-method.c << '!EOF'
X/* Handle the hair of processing (but not expanding) inline functions.
X Also manage function and varaible name overloading.
X Copyright (C) 1987 Free Software Foundation, Inc.
X Contributed by Michael Tiemann ([email protected])
X
X This file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* Handle method declarations. */
X#include
X#include "config.h"
X#include "tree.h"
X#include "cplus-tree.h"
X#include "assert.h"
X
X/* TREE_LIST of the current inline functions that need to be
X processed. */
Xstruct pending_inline *pending_inlines;
X
X# define MAX_INLINE_BUF_SIZE 8188
X# define OB_INIT() (inline_bufp = inline_buffer)
X# define OB_PUTC(C) (*inline_bufp++ = (C))
X# define OB_PUTC2(C1,C2) (OB_PUTC (C1), OB_PUTC (C2))
X# define OB_PUTS(S) (strcpy (inline_bufp, S), inline_bufp += sizeof (S) - 1)
X# define OB_PUTCP(S) (strcpy (inline_bufp, S), inline_bufp += strlen (S))
X# define OB_FINISH() (*inline_bufp++ = '\0')
X
X/* Counter to help build parameter names in case they were omitted. */
Xstatic int dummy_name;
Xstatic int in_parmlist;
X/* Just a pointer into INLINE_BUFFER. */
Xstatic char *inline_bufp;
X/* Also a pointer into INLINE_BUFFER. This points to a safe place to
X cut back to if we assign it 0, in case of error. */
Xstatic char *inline_errp;
Xstatic char *inline_buffer;
Xstatic void dump_type (), dump_decl ();
Xstatic void dump_init (), dump_unary_op (), dump_binary_op ();
X
Xtree wrapper_name, wrapper_pred_name, anti_wrapper_name;
X
X#ifdef NO_AUTO_OVERLOAD
Xint is_overloaded ();
X#endif
X
Xvoid
Xinit_method ()
X{
X char buf[sizeof (ANTI_WRAPPER_NAME_FORMAT) + 8];
X sprintf (buf, WRAPPER_NAME_FORMAT, "");
X wrapper_name = get_identifier (buf);
X sprintf (buf, WRAPPER_PRED_NAME_FORMAT, "");
X wrapper_pred_name = get_identifier (buf);
X sprintf (buf, ANTI_WRAPPER_NAME_FORMAT, "");
X anti_wrapper_name = get_identifier (buf);
X}
X
X/* Return a pointer to the end of the new text in INLINE_BUFFER.
X We cannot use `fatal' or `error' in here because that
X might cause an infinite loop. */
Xstatic char *
Xnew_text_len (s)
X char *s;
X{
X while (*s++) ;
X
X if (s >= inline_buffer + MAX_INLINE_BUF_SIZE)
X {
X fprintf (stderr, "recompile c++ with larger MAX_INLINE_BUF_SIZE (%d)", MAX_INLINE_BUF_SIZE);
X abort ();
X }
X return s - 1;
X}
X
X/* Check that we have not overflowed INLINE_BUFFER.
X We cannot use `fatal' or `error' in here because that
X might cause an infinite loop. */
Xstatic void
Xcheck_text_len (s)
X char *s;
X{
X if (s >= inline_buffer + MAX_INLINE_BUF_SIZE)
X {
X fprintf (stderr, "recompile c++ with larger MAX_INLINE_BUF_SIZE (%d)", MAX_INLINE_BUF_SIZE);
X abort ();
X }
X}
X
Xtree
Xmake_anon_parm_name ()
X{
X char buf[32];
X
X sprintf (buf, ANON_PARMNAME_FORMAT, dummy_name++);
X return get_identifier (buf);
X}
X
Xvoid
Xclear_anon_parm_name ()
X{
X /* recycle these names. */
X dummy_name = 0;
X}
X
Xstatic void
Xdump_readonly_or_volatile (t)
X tree t;
X{
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X}
X
Xstatic void
Xdump_type_prefix (t, p)
X tree t;
X int *p;
X{
X int old_p = 0;
X int print_struct = 1;
X tree name;
X
X if (t == NULL_TREE)
X return;
X
X switch (TREE_CODE (t))
X {
X case ERROR_MARK:
X sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
X break;
X
X case UNKNOWN_TYPE:
X OB_PUTS ("");
X return;
X
X case TREE_LIST:
X dump_type (TREE_VALUE (t), &old_p);
X if (TREE_CHAIN (t))
X {
X if (TREE_CHAIN (t) != void_list_node)
X {
X OB_PUTC (',');
X dump_type (TREE_CHAIN (t), &old_p);
X }
X }
X else OB_PUTS ("...");
X return;
X
X case POINTER_TYPE:
X *p += 1;
X dump_type_prefix (TREE_TYPE (t), p);
X while (*p)
X {

X OB_PUTC ('*');
X *p -= 1;
X }
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X return;
X
X case OFFSET_TYPE:
X {
X tree type = TREE_TYPE (t);
X if (TREE_CODE (type) == FUNCTION_TYPE)
X {
X type = TREE_TYPE (type);
X if (in_parmlist)
X OB_PUTS ("auto ");
X }
X
X dump_type_prefix (type, &old_p);
X
X OB_PUTC ('(');
X dump_type (TYPE_OFFSET_BASETYPE (t), &old_p);
X OB_PUTC2 (':', ':');
X while (*p)
X {
X OB_PUTC ('*');
X *p -= 1;
X }
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X return;
X }
X
X case METHOD_TYPE:
X {
X tree type = TREE_TYPE (t);
X if (in_parmlist)
X OB_PUTS ("auto ");
X
X dump_type_prefix (type, &old_p);
X
X OB_PUTC ('(');
X dump_type (TYPE_METHOD_BASETYPE (t), &old_p);
X OB_PUTC2 (':', ':');
X while (*p)
X {
X OB_PUTC ('*');
X *p -= 1;
X }
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X return;
X }
X
X case REFERENCE_TYPE:
X dump_type_prefix (TREE_TYPE (t), p);
X OB_PUTC ('&');
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X return;
X
X case ARRAY_TYPE:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X dump_type_prefix (TREE_TYPE (t), p);
X return;
X
X case FUNCTION_TYPE:
X if (in_parmlist)
X OB_PUTS ("auto ");
X dump_type_prefix (TREE_TYPE (t), &old_p);
X OB_PUTC ('(');
X while (*p)
X {
X OB_PUTC ('*');
X *p -= 1;
X }
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X return;
X
X case IDENTIFIER_NODE:
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
X break;
X
X case RECORD_TYPE:
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
X print_struct = 0;
X name = TYPE_NAME (t);
X if (TREE_CODE (name) == TYPE_DECL)
X name = DECL_NAME (name);
X if (print_struct)
X sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (name));
X else
X sprintf (inline_bufp, "class %s ", IDENTIFIER_POINTER (name));
X break;
X
X case UNION_TYPE:
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X name = TYPE_NAME (t);
X if (TREE_CODE (name) == TYPE_DECL)
X name = DECL_NAME (name);
X sprintf (inline_bufp, "union %s ", IDENTIFIER_POINTER (name));
X break;
X
X case ENUMERAL_TYPE:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X name = TYPE_NAME (t);
X if (TREE_CODE (name) == TYPE_DECL)
X name = DECL_NAME (name);
X sprintf (inline_bufp, "enum %s ", IDENTIFIER_POINTER (name));
X break;
X
X case TYPE_DECL:
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
X break;
X
X case INTEGER_TYPE:
X /* Normally, `unsigned' is part of the deal. Not so if it comes
X with `const' or `volatile'. */
X if (TREE_UNSIGNED (t)
X && (TREE_READONLY (t) || TREE_VOLATILE (t)))
X OB_PUTS ("unsigned ");
X /* fall through. */
X case REAL_TYPE:
X case VOID_TYPE:
X if (TREE_READONLY (t))
X OB_PUTS ("const ");
X if (TREE_VOLATILE (t))
X OB_PUTS ("volatile ");
X sprintf (inline_bufp, "%s ", TYPE_NAME_STRING (t));
X break;
X
X default:
X abort ();
X }
X inline_bufp = new_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_type_suffix (t, p)
X tree t;
X int *p;
X{
X int old_p = 0;
X
X if (t == NULL_TREE)
X return;
X
X switch (TREE_CODE (t))
X {
X case ERROR_MARK:
X sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
X break;
X
X case UNKNOWN_TYPE:
X return;
X
X case POINTER_TYPE:
X dump_type_suffix (TREE_TYPE (t), p);
X return;
X
X case OFFSET_TYPE:
X {
X tree type = TREE_TYPE (t);
X
X OB_PUTC (')');
X if (TREE_CODE (type) == FUNCTION_TYPE)
X {
X#if 0
X tree next_arg = TREE_CHAIN (TYPE_ARG_TYPES (type));
X OB_PUTC ('(');
X if (next_arg)
X {
X if (next_arg != void_list_node)
X {
X in_parmlist++;
X dump_type (next_arg, &old_p);
X in_parmlist--;
X }
X }
X else OB_PUTS ("...");
X OB_PUTC (')');
X dump_type_suffix (TREE_TYPE (type), p);
X#else
X abort ();
X#endif
X }
X return;
X }
X
X case METHOD_TYPE:
X {
X tree next_arg;
X OB_PUTC (')');
X next_arg = TREE_CHAIN (TYPE_ARG_TYPES (t));
X OB_PUTC ('(');
X if (next_arg)
X {
X if (next_arg != void_list_node)
X {
X in_parmlist++;
X dump_type (next_arg, &old_p);
X in_parmlist--;
X }
X }

X else OB_PUTS ("...");
X OB_PUTC (')');
X dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))));
X dump_type_suffix (TREE_TYPE (t), p);
X return;
X }
X
X case REFERENCE_TYPE:
X dump_type_suffix (TREE_TYPE (t), p);
X return;
X
X case ARRAY_TYPE:
X dump_type_suffix (TREE_TYPE (t), p);
X OB_PUTC2 ('[', ']');
X return;
X
X case FUNCTION_TYPE:
X OB_PUTC2 (')', '(');
X if (TYPE_ARG_TYPES (t) && TYPE_ARG_TYPES (t) != void_list_node)
X {
X in_parmlist++;
X dump_type (TYPE_ARG_TYPES (t), &old_p);
X in_parmlist--;
X }
X OB_PUTC (')');
X dump_type_suffix (TREE_TYPE (t), p);
X return;
X
X case IDENTIFIER_NODE:
X case RECORD_TYPE:
X case UNION_TYPE:
X case ENUMERAL_TYPE:
X case TYPE_DECL:
X case INTEGER_TYPE:
X case REAL_TYPE:
X case VOID_TYPE:
X return;
X
X default:
X abort ();
X }
X inline_bufp = new_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_type (t, p)
X tree t;
X int *p;
X{
X int old_p = 0;
X int print_struct = 1;
X
X if (t == NULL_TREE)
X return;
X
X switch (TREE_CODE (t))
X {
X case ERROR_MARK:
X sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
X break;
X
X case UNKNOWN_TYPE:
X OB_PUTS ("");
X return;
X
X case TREE_LIST:
X dump_type (TREE_VALUE (t), &old_p);
X if (TREE_CHAIN (t))
X {
X if (TREE_CHAIN (t) != void_list_node)
X {
X OB_PUTC (',');
X dump_type (TREE_CHAIN (t), &old_p);
X }
X }
X else OB_PUTS ("...");
X return;
X
X case POINTER_TYPE:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X *p += 1;
X dump_type (TREE_TYPE (t), p);
X while (*p)
X {
X OB_PUTC ('*');
X *p -= 1;
X }
X return;
X
X case REFERENCE_TYPE:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X dump_type (TREE_TYPE (t), p);
X OB_PUTC ('&');
X return;
X
X case ARRAY_TYPE:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X dump_type (TREE_TYPE (t), p);
X OB_PUTC2 ('[', ']');
X return;
X
X case OFFSET_TYPE:
X case METHOD_TYPE:
X case FUNCTION_TYPE:
X dump_type_prefix (t, p);
X dump_type_suffix (t, p);
X return;
X
X case IDENTIFIER_NODE:
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
X break;
X
X case RECORD_TYPE:
X {
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
X print_struct = 0;
X t = TYPE_NAME (t);
X if (TREE_CODE (t) == TYPE_DECL)
X t = DECL_NAME (t);
X if (print_struct)
X sprintf (inline_bufp, "struct %s ", IDENTIFIER_POINTER (t));
X else
X sprintf (inline_bufp, "class %s ", IDENTIFIER_POINTER (t));
X break;
X }
X
X case UNION_TYPE:
X {
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X t = TYPE_NAME (t);
X if (TREE_CODE (t) == TYPE_DECL)
X t = DECL_NAME (t);
X sprintf (inline_bufp, "union %s ", IDENTIFIER_POINTER (t));
X }
X break;
X
X case ENUMERAL_TYPE:
X {
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X t = TYPE_NAME (t);
X if (TREE_CODE (t) == TYPE_DECL)
X t = DECL_NAME (t);
X sprintf (inline_bufp, "enum %s ", IDENTIFIER_POINTER (t));
X }
X break;
X
X case TYPE_DECL:
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
X break;
X
X case INTEGER_TYPE:
X /* Normally, `unsigned' is part of the deal. Not so if it comes
X with `const' or `volatile'. */
X if (TREE_READONLY (t) | TREE_VOLATILE (t))
X dump_readonly_or_volatile (t);
X if (TREE_UNSIGNED (t)
X && (TREE_READONLY (t) | TREE_VOLATILE (t)))
X OB_PUTS ("unsigned ");
X /* fall through. */
X case REAL_TYPE:
X case VOID_TYPE:
X sprintf (inline_bufp, "%s ", TYPE_NAME_STRING (t));
X break;
X
X default:
X abort ();
X }
X inline_bufp = new_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_decl (t)
X tree t;
X{
X int p = 0;
X
X if (t == NULL_TREE)
X return;
X
X switch (TREE_CODE (t))
X {
X case ERROR_MARK:
X strcpy (inline_bufp, " /* decl error */ ");
X break;
X
X case PARM_DECL:
X dump_type_prefix (TREE_TYPE (t), &p);
X if (DECL_NAME (t))
X dump_decl (DECL_NAME (t));
X else
X {
X sprintf (inline_bufp, ANON_PARMNAME_FORMAT, dummy_name++);
X break;
X }
X dump_type_suffix (TREE_TYPE (t), &p);
X return;
X
X case CALL_EXPR:
X dump_decl (TREE_OPERAND (t, 0));
X OB_PUTC ('(');
X in_parmlist++;
X dump_decl (TREE_OPERAND (t, 1));
X in_parmlist--;
X t = tree_last (TYPE_ARG_TYPES (TREE_TYPE (t)));
X if (!t || t != void_list_node)
X OB_PUTS ("...");
X OB_PUTC (')');
X return;
X
X case ARRAY_REF:
X dump_decl (TREE_OPERAND (t, 0));
X OB_PUTC ('[');
X dump_decl (TREE_OPERAND (t, 1));
X OB_PUTC (']');
X return;
X
X case TYPE_DECL:
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (DECL_NAME (t)));
X break;
X
X case TYPE_EXPR:
X abort ();
X break;
X
X case IDENTIFIER_NODE:
X if (OPERATOR_NAME_P (t))
X sprintf (inline_bufp, "operator %s ", operator_name_string (t));
X else if (OPERATOR_TYPENAME_P (t))
X {
X OB_PUTS ("operator ");
X dump_type (TREE_TYPE (t), &p);
X return;
X }
X else
X sprintf (inline_bufp, "%s ", IDENTIFIER_POINTER (t));
X break;
X
X case BIT_NOT_EXPR:
X OB_PUTC2 ('~', ' ');
X dump_decl (TREE_OPERAND (t, 0));
X return;
X
X case SCOPE_REF:
X sprintf (inline_bufp, "%s :: ", IDENTIFIER_POINTER (TREE_OPERAND (t, 0)));
X inline_bufp += sizeof ("%s :: ") + IDENTIFIER_LENGTH (TREE_OPERAND (t, 0));
X dump_decl (TREE_OPERAND (t, 1));
X return;
X
X case INDIRECT_REF:
X OB_PUTC ('*');
X dump_decl (TREE_OPERAND (t, 0));
X return;
X
X case ADDR_EXPR:
X OB_PUTC ('&');
X dump_decl (TREE_OPERAND (t, 0));
X return;
X
X default:
X abort ();
X }
X inline_bufp = new_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_init_list (l)
X tree l;
X{
X while (l)
X {
X dump_init (TREE_VALUE (l));
X if (TREE_CHAIN (l))
X OB_PUTC (',');
X l = TREE_CHAIN (l);
X }
X}
X
Xstatic void
Xdump_init (t)
X tree t;
X{
X int dummy;
X
X switch (TREE_CODE (t))
X {
X case VAR_DECL:
X case PARM_DECL:
X sprintf (inline_bufp, " %s ", IDENTIFIER_POINTER (DECL_NAME (t)));
X break;
X
X case FUNCTION_DECL:
X {
X tree name = DECL_NAME (t);
X
X if (DESTRUCTOR_NAME_P (name))
X sprintf (inline_bufp, " ~%s ",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
X else if (OPERATOR_NAME_P (name))
X sprintf (inline_bufp, "operator %s ", operator_name_string (name));
X else if (OPERATOR_TYPENAME_P (name))
X {
X dummy = 0;
X OB_PUTS ("operator ");
X dump_type (TREE_TYPE (name), &dummy);
X }
X#if 0
X else if (WRAPPER_NAME_P (name))
X sprintf (inline_bufp, " ()%s ",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
X else if (WRAPPER_PRED_NAME_P (name))
X sprintf (inline_bufp, " ()?%s ",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
X else if (ANTI_WRAPPER_NAME_P (name))
X sprintf (inline_bufp, " ~()%s ",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
X#endif
X else sprintf (inline_bufp, " %s ",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (t)));
X }
X break;
X
X case CONST_DECL:
X dummy = 0;
X OB_PUTC2 ('(', '(');
X dump_type (TREE_TYPE (t), &dummy);
X OB_PUTC (')');
X dump_init (DECL_INITIAL (t));
X OB_PUTC (')');
X return;
X
X case INTEGER_CST:
X sprintf (inline_bufp, " %d ", TREE_INT_CST_LOW (t));
X break;
X
X case REAL_CST:
X sprintf (inline_bufp, " %g ", TREE_REAL_CST (t));
X break;
X
X case STRING_CST:
X {
X char *p = TREE_STRING_POINTER (t);
X int len = TREE_STRING_LENGTH (t) - 1;
X int i;
X
X check_text_len (inline_bufp + len + 2);
X OB_PUTC ('\"');
X for (i = 0; i < len; i++)
X {
X register char c = p[i];
X if (c == '\"' || c == '\\')
X OB_PUTC ('\\');
X if (c >= ' ' && c < 0177)
X OB_PUTC (c);
X else
X {
X sprintf (inline_bufp, "\\%03o", c);
X inline_bufp = new_text_len (inline_bufp);
X }
X }
X OB_PUTC ('\"');
X }
X return;
X
X case COMPOUND_EXPR:
X dump_binary_op (",", t, 1);
X break;
X
X case COND_EXPR:
X OB_PUTC ('(');
X dump_init (TREE_OPERAND (t, 0));
X OB_PUTS (" ? ");
X dump_init (TREE_OPERAND (t, 1));
X OB_PUTS (" : ");
X dump_init (TREE_OPERAND (t, 2));
X OB_PUTC (')');
X return;
X
X case SAVE_EXPR:
X if (TREE_HAS_CONSTRUCTOR (t))
X {
X dummy = 0;
X OB_PUTS ("new ");
X dump_type (TREE_TYPE (TREE_TYPE (t)), &dummy);
X PARM_DECL_EXPR (t) = 1;
X }
X else
X {
X sorry ("operand of SAVE_EXPR not understood");
X *inline_errp = '\0';
X inline_bufp = inline_errp + 1;
X }
X return;
X
X case NEW_EXPR:
X strcpy (inline_bufp, TYPE_NAME_STRING (TREE_TYPE (t)));
X inline_bufp = new_text_len (inline_bufp);
X OB_PUTC ('(');
X dump_init_list (TREE_CHAIN (TREE_OPERAND (t, 1)));
X OB_PUTC (')');
X return;
X
X case CALL_EXPR:
X OB_PUTC ('(');
X dump_init (TREE_OPERAND (t, 0));
X dump_init_list (TREE_OPERAND (t, 1));
X OB_PUTC (')');
X return;
X
X case MODIFY_EXPR:
X case PLUS_EXPR:
X case MINUS_EXPR:
X case MULT_EXPR:
X case TRUNC_DIV_EXPR:
X case TRUNC_MOD_EXPR:
X case MIN_EXPR:
X case MAX_EXPR:
X case LSHIFT_EXPR:
X case RSHIFT_EXPR:
X case BIT_IOR_EXPR:
X case BIT_XOR_EXPR:
X case BIT_AND_EXPR:
X case BIT_ANDTC_EXPR:
X case TRUTH_ANDIF_EXPR:
X case TRUTH_ORIF_EXPR:
X case LT_EXPR:
X case LE_EXPR:
X case GT_EXPR:
X case GE_EXPR:
X case EQ_EXPR:
X case NE_EXPR:
X dump_binary_op (opname_tab[(int) TREE_CODE (t)], t,
X strlen (opname_tab[(int) TREE_CODE (t)]));
X return;
X
X case CEIL_DIV_EXPR:
X case FLOOR_DIV_EXPR:
X case ROUND_DIV_EXPR:
X dump_binary_op ("/", t, 1);
X return;
X
X case CEIL_MOD_EXPR:
X case FLOOR_MOD_EXPR:
X case ROUND_MOD_EXPR:
X dump_binary_op ("%", t, 1);
X return;
X
X case COMPONENT_REF:
X dump_binary_op (".", t, 1);
X return;
X
X case CONVERT_EXPR:
X dump_unary_op ("+", t, 1);
X return;
X
X case ADDR_EXPR:
X if (TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
X || TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
X dump_init (TREE_OPERAND (t, 0));
X else
X dump_unary_op ("&", t, 1);
X return;
X
X case INDIRECT_REF:
X dump_unary_op ("*", t, 1);
X return;
X
X case NEGATE_EXPR:
X case BIT_NOT_EXPR:
X case TRUTH_NOT_EXPR:
X case PREDECREMENT_EXPR:
X case PREINCREMENT_EXPR:
X dump_unary_op (opname_tab [(int)TREE_CODE (t)], t,
X strlen (opname_tab[(int) TREE_CODE (t)]));
X return;
X
X case POSTDECREMENT_EXPR:
X case POSTINCREMENT_EXPR:
X OB_PUTC ('(');
X dump_init (TREE_OPERAND (t, 0));
X OB_PUTCP (opname_tab[(int)TREE_CODE (t)]);
X OB_PUTC (')');
X return;
X
X case NOP_EXPR:
X dummy = 0;
X OB_PUTC2 ('(', '(');
X dump_type (TREE_TYPE (t), &dummy);
X OB_PUTC (')');
X dump_init (TREE_OPERAND (t, 0));
X OB_PUTC (')');
X return;
X
X case CONSTRUCTOR:
X OB_PUTC ('{');
X dump_init_list (CONSTRUCTOR_ELTS (t));
X OB_PUTC ('}');
X return;
X
X /* This list is incomplete, but should suffice for now.
X It is very important that `sorry' does not call
X `report_error_function'. That could cause an infinite loop. */
X default:
X sorry ("that operation not supported for default parameters");
X
X /* fall through to ERROR_MARK... */
X case ERROR_MARK:
X *inline_errp = '\0';
X inline_bufp = inline_errp + 1;
X return;
X }
X inline_bufp = new_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_binary_op (opstring, t, len)
X char *opstring;
X tree t;
X int len;
X{
X OB_PUTC ('(');
X dump_init (TREE_OPERAND (t, 0));
X sprintf (inline_bufp, " %s ", opstring);
X inline_bufp += len + 2;
X dump_init (TREE_OPERAND (t, 1));
X OB_PUTC (')');
X check_text_len (inline_bufp);
X}
X
Xstatic void
Xdump_unary_op (opstring, t, len)
X char *opstring;
X tree t;
X int len;
X{
X OB_PUTC ('(');
X sprintf (inline_bufp, " %s ", opstring);
X inline_bufp += len + 2;
X dump_init (TREE_OPERAND (t, 0));
X OB_PUTC (')');
X check_text_len (inline_bufp);
X}
X
X#ifdef DO_METHODS_THE_OLD_WAY
X/* Process the currently pending inline function definitions.
X This entails:
X (1) Creating a temporary file which contains return type,
X delarator name, and argment names and types of the
X function to be inlined.
X (2) Reading that file into a buffer which can then be
X made to look line another piece of inline code to
X process, stuffing that on the top of the inline
X stack, then letting the lexer and parser read from those
X two.
X*/
X
Xstatic struct pending_inline *
Xstash_inline_prefix (cname, field)
X tree cname, field;
X{
X extern int lineno;
X struct pending_inline *t;
X tree name, fndecl, fntype;
X int p = 0;
X inline_buffer = (char *)alloca (MAX_INLINE_BUF_SIZE + 4);
X dummy_name = 0;
X
X name = DECL_ORIGINAL_NAME (field);
X /* We still don't do friends right. */
X fndecl = field;
X fntype = TREE_TYPE (fndecl);
X
X if (TREE_INLINE (fndecl))
X strcpy (inline_buffer, "inline ");
X else
X strcpy (inline_buffer, "static ");
X inline_bufp = inline_buffer + strlen (inline_buffer);
X if (! OPERATOR_TYPENAME_P (name))
X dump_type_prefix (TREE_TYPE (fntype), &p);
X if (TREE_CODE (fntype) == METHOD_TYPE)
X {
X dump_type (cname, &p);
X inline_bufp[-1] = ':';
X *inline_bufp++ = ':';
X if (DESTRUCTOR_NAME_P (DECL_NAME (fndecl)))
X OB_PUTC ('~');
X#if 0
X else if (WRAPPER_NAME_P (DECL_NAME (fndecl)))
X OB_PUTC2 ('(', ')');
X else if (WRAPPER_PRED_NAME_P (DECL_NAME (fndecl)))
X OB_PUTS ("()?");
X else if (ANTI_WRAPPER_NAME_P (DECL_NAME (fndecl)))
X OB_PUTS ("~()");
X#endif
X }
X dump_decl (name);
X OB_PUTC ('(');
X if (! DESTRUCTOR_NAME_P (DECL_NAME (fndecl)))
X {
X tree parmlist = DECL_ARGUMENTS (fndecl);
X tree typelist = TYPE_ARG_TYPES (fntype);
X
X if (TREE_CODE (field) == FIELD_DECL)
X {
X parmlist = TREE_CHAIN (parmlist);
X typelist = TREE_CHAIN (typelist);
X }
X
X in_parmlist++;
X while (parmlist)
X {
X dump_decl (parmlist);
X#if 0
X if (TREE_PURPOSE (typelist))
X {
X inline_errp = inline_bufp;
X OB_PUTS (" = (");
X dump_init (TREE_PURPOSE (typelist));
X OB_PUTC (')');
X if (*inline_errp == '\0')
X inline_bufp = inline_errp;
X }
X#endif
X if (TREE_CHAIN (parmlist))
X OB_PUTC (',');
X parmlist = TREE_CHAIN (parmlist);
X typelist = TREE_CHAIN (typelist);
X }
X in_parmlist--;
X if (!typelist || typelist != void_list_node)
X OB_PUTS ("...");
X }
X OB_PUTC (')');
X
X if (! OPERATOR_TYPENAME_P (name))
X dump_type_suffix (TREE_TYPE (fntype), &p);
X if (TREE_CODE (fntype) == METHOD_TYPE)
X dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))));
X {
X extern tree value_identifier;
X
X if (DECL_RESULT (fndecl) != value_identifier)
X {
X tree result = DECL_RESULT (fndecl);
X
X OB_PUTS ("return ");
X OB_PUTS (IDENTIFIER_POINTER (DECL_NAME (result)));
X if (DECL_INITIAL (result))
X {
X OB_PUTC ('=');
X dump_init (DECL_INITIAL (result));
X OB_PUTC (';');
X }
X }
X }
X OB_FINISH ();
X check_text_len (inline_bufp);
X
X t = (struct pending_inline *)xmalloc (sizeof (struct pending_inline));
X t->len = inline_bufp - inline_buffer;
X t->buf = (char *)xmalloc (t->len);
X bcopy (inline_buffer, t->buf, t->len);
X t->lineno = lineno;
X t->filename = input_filename;
X t->token = 0;
X return t;
X}
X#endif
X
X#define OVERLOAD_MAX_LEN 1024
X
X/* Pretty printing for announce_function. If BUF is nonzero, then
X the text is written there. The buffer is assued to be of size
X OVERLOAD_MAX_LEN. CNAME is the name of the class that FNDECL
X belongs to, if we could not figure that out from FNDECL
X itself. FNDECL is the declaration of the function we
X are interested in seeing. PRINT_RET_TYPE_P is non-zero if
X we should print the type that this function returns. */
Xchar *
Xfndecl_as_string (buf, cname, fndecl, print_ret_type_p)
X char *buf;
X tree cname, fndecl;
X int print_ret_type_p;
X{
X tree name = DECL_NAME (fndecl);
X tree fntype = TREE_TYPE (fndecl);
X tree parmtypes = TYPE_ARG_TYPES (fntype);
X int p = 0;
X int spaces = 0;
X
X inline_buffer = buf;
X OB_INIT ();
X
X if (DECL_STATIC_FUNCTION_P (fndecl))
X cname = TYPE_NAME (DECL_STATIC_CONTEXT (fndecl));
X else if (! cname && TREE_CODE (fntype) == METHOD_TYPE)
X cname = TYPE_NAME (TYPE_METHOD_BASETYPE (fntype));
X
X if (print_ret_type_p && ! OPERATOR_TYPENAME_P (name))
X dump_type_prefix (TREE_TYPE (fntype), &p);
X if (DECL_STATIC_FUNCTION_P (fndecl))
X OB_PUTS ("static ");
X
X if (cname)
X {
X dump_type (cname, &p);
X inline_bufp[-1] = ':';
X *inline_bufp++ = ':';
X if (TREE_CODE (fntype) == METHOD_TYPE && parmtypes)
X parmtypes = TREE_CHAIN (parmtypes);
X if (DECL_CONSTRUCTOR_FOR_VBASE_P (fndecl))
X /* Skip past "in_charge" identifier. */
X parmtypes = TREE_CHAIN (parmtypes);
X }
X
X if (DESTRUCTOR_NAME_P (name))
X {
X OB_PUTC ('~');
X parmtypes = TREE_CHAIN (parmtypes);
X dump_decl (DECL_ORIGINAL_NAME (fndecl));
X }
X else if (OPERATOR_NAME_P (name))
X {
X sprintf (inline_bufp, "operator %s ", operator_name_string (name));
X inline_bufp += strlen (inline_bufp);
X }
X else if (OPERATOR_TYPENAME_P (name))
X {
X /* This cannot use the hack that the operator's return
X type is stashed off of its name because it may be
X used for error reporting. In the case of conflicting
X declarations, both will have the same name, yet
X the types will be different, hence the TREE_TYPE field
X of the first name will be clobbered by the second. */
X OB_PUTS ("operator ");
X dump_type (TREE_TYPE (TREE_TYPE (fndecl)), &p);
X }
X else if (DECL_CONSTRUCTOR_P (fndecl))
X {
X#ifdef SOS
X if (TYPE_DYNAMIC (TREE_TYPE (TREE_TYPE (DECL_ORIGINAL_NAME (fndecl)))))
X {
X OB_PUTS ("dynamic ");
X parmtypes = TREE_CHAIN (parmtypes);
X }
X#endif
X dump_decl (DECL_ORIGINAL_NAME (fndecl));
X }
X else
X {
X#if 0
X if (WRAPPER_NAME_P (name))
X OB_PUTC2 ('(', ')');
X if (WRAPPER_PRED_NAME_P (name))
X OB_PUTS ("()?");
X else if (ANTI_WRAPPER_NAME_P (name))
X OB_PUTS ("~()");
X#endif
X dump_decl (DECL_ORIGINAL_NAME (fndecl));
X }
X
X OB_PUTC ('(');
X if (parmtypes)
X {
X in_parmlist++;
X if (parmtypes != void_list_node)
X spaces = 2;
X while (parmtypes && parmtypes != void_list_node)
X {
X dump_type (TREE_VALUE (parmtypes), &p);
X while (inline_bufp[-1] == ' ')
X inline_bufp--;
X if (TREE_PURPOSE (parmtypes))
X {
X inline_errp = inline_bufp;
X OB_PUTS (" (= ");
X dump_init (TREE_PURPOSE (parmtypes));
X OB_PUTC (')');
X }
X OB_PUTC2 (',', ' ');
X parmtypes = TREE_CHAIN (parmtypes);
X }
X in_parmlist--;
X }
X
X if (parmtypes)
X inline_bufp -= spaces;
X else
X OB_PUTS ("...");
X
X OB_PUTC (')');
X
X if (print_ret_type_p && ! OPERATOR_TYPENAME_P (name))
X dump_type_suffix (TREE_TYPE (fntype), &p);
X
X if (TREE_CODE (fntype) == METHOD_TYPE)
X dump_readonly_or_volatile (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))));
X
X OB_FINISH ();
X check_text_len (inline_bufp);
X
X if (strlen (buf) >= OVERLOAD_MAX_LEN)
X {
X fprintf (stderr, "fndecl_as_string returns something too large");
X abort ();
X }
X return buf;
X}
X
X#ifdef FIELD_XREF
X
Xchar *
Xtype_as_string (buf, typ)
X char *buf;
X tree typ;
X{
X int p = 0;
X int spaces = 0;
X
X inline_buffer = buf;
X OB_INIT ();
X
X dump_type(typ,&p);
X
X OB_FINISH ();
X
X return buf;
X}
X
X#endif
X
X/* Move inline function defintions out of structure so that they
X can be processed normally. CNAME is the name of the class
X we are working from, METHOD_LIST is the list of method lists
X of the structure. We delete friend methods here, after
X saving away their inline function definitions (if any). */
X
X/* Subroutine of `do_inline_function_hair'. */
Xstatic void
Xprepare_inline (cname, fndecl)
X tree cname, fndecl;
X{
X if (DECL_PENDING_INLINE_INFO (fndecl))
X {
X struct pending_inline *t1, *t2;
X tree args;
X
X t2 = DECL_PENDING_INLINE_INFO (fndecl);
X t2->next = pending_inlines;
X t2->fndecl = fndecl;
X args = DECL_ARGUMENTS (fndecl);
X while (args)
X {
X DECL_CONTEXT (args) = fndecl;
X args = TREE_CHAIN (args);
X }
X#ifdef DO_METHODS_THE_OLD_WAY
X t1 = stash_inline_prefix (cname, methods);
X t1->next = t2;
X#else
X t1 = t2;
X#endif
X pending_inlines = t1;
X
X /* Allow this decl to be seen in global scope */
X IDENTIFIER_GLOBAL_VALUE (DECL_NAME (fndecl)) = fndecl;
X }
X}
X
Xvoid
Xdo_inline_function_hair (type, friend_list)
X tree type, friend_list;
X{
X tree cname = DECL_NAME (TYPE_NAME (type));
X tree method_vec = CLASSTYPE_METHOD_VEC (type);
X if (method_vec != 0)
X {
X tree *methods = &TREE_VEC_ELT (method_vec, 0);
X tree *end = TREE_VEC_END (method_vec);
X while (methods != end)
X {
X /* Do inline member functions. */
X tree method = *methods;
X while (method)
X {
X prepare_inline (cname, method);
X method = TREE_CHAIN (method);
X }
X methods++;
X }
X }
X while (friend_list)
X {
X prepare_inline (NULL_TREE, TREE_VALUE (friend_list));
X friend_list = TREE_CHAIN (friend_list);
X }
X}
X
X/* Report a argument type mismatch between the best declared function
X we could find and the current argument list that we have. */
Xvoid
Xreport_type_mismatch (cp, parmtypes, name_kind, err_name)
X struct candidate *cp;
X tree parmtypes;
X char *name_kind, *err_name;
X{
X char buf[OVERLOAD_MAX_LEN];
X int i = cp->u.bad_arg;
X tree ttf, tta;
X
X if (i == -3)
X {
X if (TREE_READONLY (TREE_TYPE (TREE_VALUE (parmtypes))))
X error ("call to const %s `%s' with non-const object", name_kind, err_name);
X else
X error ("call to non-const %s `%s' with const object", name_kind, err_name);
X return;
X }
X if (i == -2)
X {
X error ("too few arguments for %s `%s'", name_kind, err_name);
X return;
X }
X else if (i == -1)
X {
X error ("too many arguments for %s `%s'", name_kind, err_name);
X return;
X }
X if (i == 0)
X {
X if (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE)
X {
X /* Happens when we have an ambiguous base class. */
X assert (get_base_type (DECL_CONTEXT (cp->function), TREE_TYPE (TREE_TYPE (TREE_VALUE (parmtypes))), 1) == error_mark_node);
X return;
X }
X }
X ttf = TYPE_ARG_TYPES (TREE_TYPE (cp->function));
X tta = parmtypes;
X
X while (i-- > 0)
X {
X ttf = TREE_CHAIN (ttf);
X tta = TREE_CHAIN (tta);
X }
X fndecl_as_string (buf, 0, cp->function, 0);
X inline_bufp = inline_buffer + strlen (inline_buffer) + 1;
X inline_buffer = inline_bufp;
X
X /* Reset `i' so that type printing routines do the right thing. */
X if (tta)
X {
X enum tree_code code = TREE_CODE (TREE_TYPE (TREE_VALUE (tta)));
X if (code == ERROR_MARK)
X OB_PUTS ("(failed type instatiation)");
X else
X {
X i = (code == FUNCTION_TYPE || code == METHOD_TYPE);
X dump_type (TREE_TYPE (TREE_VALUE (tta)), &i);
X }
X }
X else OB_PUTS ("void");
X
X OB_FINISH ();
X sprintf (inline_bufp, "bad argument %d for function `%s' (type was %s)",
X cp->u.bad_arg - (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE), buf, inline_buffer);
X strcpy (buf, inline_bufp);
X error (buf);
X}
X
X/* Here is where overload code starts. */
X
X#define OVERLOAD_MAX_LEN 1024
X
X/* Array of types seen so far in top-level call to `build_overload_name'.
X Allocated and deallocated by caller. */
Xstatic tree *typevec;
X
X/* Number of types interned by `build_overload_name' so far. */
Xstatic int maxtype;
X
X/* Number of occurances of last type seen. */
Xstatic int nrepeats;
X
X/* Nonzero if we should not try folding parameter types. */
Xstatic int nofold;
X
X#define ALLOCATE_TYPEVEC(PARMTYPES) \
X do { maxtype = 0, nrepeats = 0; \
X typevec = (tree *)alloca (list_length (PARMTYPES) * sizeof (tree)); } while (0)
X
X#define DEALLOCATE_TYPEVEC(PARMTYPES) \
X do { tree t = (PARMTYPES); \
X while (t) { TREE_USED (TREE_VALUE (t)) = 0; t = TREE_CHAIN (t); } \
X } while (0)
X
X/* Code to concatenate an asciified integer to a string,
X and return the end of the string. */
Xstatic
X#ifdef __GNUC__
X__inline
X#endif
Xchar *
Xicat (s, i)
X char *s;
X int i;
X{
X if (i < 10)
X {
X *s++ = '0' + i;
X return s;
X }
X s = icat (s, i / 10);
X *s++ = '0' + (i % 10);
X return s;
X}
X
Xstatic
X#ifdef __GNUC__
X__inline
X#endif
Xchar *
Xflush_repeats (s, type)
X char *s;
X tree type;
X{
X int tindex = 0;
X char *rval;
X
X while (typevec[tindex] != type)
X tindex++;
X
X if (nrepeats > 1)
X {
X *s++ = 'N';
X s = icat (s, nrepeats);
X if (nrepeats > 9)
X *s++ = '_';
X }
X else
X *s++ = 'T';
X nrepeats = 0;
X rval = icat (s, tindex);
X if (tindex > 9)
X *rval++ = '_';
X return rval;
X}
X
X/* Given a list of parameters in PARMS, and a buffer in TEXT, of
X length LEN bytes, create an unambiguous overload string. Should
X distinguish any type that C (or C++) can distinguish. I.e.,
X pointers to functions are treated correctly.
X
X Caller must deal with whether a final `e' goes on the end or not.
X
X Any default conversions must take place before this function
X is called. */
X
Xstatic char *
Xbuild_overload_name (parmtypes, text, text_end)
X tree parmtypes;
X char *text, *text_end;
X{
X char *textp = text;
X int just_one;
X tree parmtype;
X
X if (just_one = (TREE_CODE (parmtypes) != TREE_LIST))
X {
X parmtype = parmtypes;
X goto only_one;
X }
X
X while (parmtypes)
X {
X if (text_end - text < 4)
X fatal ("Out of string space in build_overload_name!");
X parmtype = TREE_VALUE (parmtypes);
X
X only_one:
X
X if (! nofold)
X {
X if (! just_one)
X /* Every argument gets counted. */
X typevec[maxtype++] = parmtype;
X
X if (TREE_USED (parmtype))
X {
X if (! just_one && parmtype == typevec[maxtype-2])
X nrepeats++;
X else
X {
X if (nrepeats)
X textp = flush_repeats (textp, parmtype);
X if (! just_one && TREE_CHAIN (parmtypes)
X && parmtype == TREE_VALUE (TREE_CHAIN (parmtypes)))
X nrepeats++;
X else
X {
X int tindex = 0;
X
X while (typevec[tindex] != parmtype)
X tindex++;
X *textp++ = 'T';
X textp = icat (textp, tindex);
X if (tindex > 9)
X *textp++ = '_';
X }
X }
X goto next;
X }
X if (nrepeats)
X textp = flush_repeats (textp, typevec[maxtype-2]);
X if (! just_one
X /* Only cache types which take more than one character. */
X && (parmtype != TYPE_MAIN_VARIANT (parmtype)
X || (TREE_CODE (parmtype) != INTEGER_TYPE
X && TREE_CODE (parmtype) != REAL_TYPE)))
X TREE_USED (parmtype) = 1;
X }
X
X if (TREE_READONLY (parmtype))
X *textp++ = 'C';
X if (TREE_CODE (parmtype) == INTEGER_TYPE && TREE_UNSIGNED (parmtype))
X *textp++ = 'U';
X if (TREE_VOLATILE (parmtype))
X *textp++ = 'V';
X
X switch (TREE_CODE (parmtype))
X {
X case OFFSET_TYPE:
X *textp++ = 'O';
X textp = build_overload_name (TYPE_OFFSET_BASETYPE (parmtype), textp, text_end);
X *textp++ = '_';
X textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
X break;
X
X case REFERENCE_TYPE:
X *textp++ = 'R';
X goto more;
X
X case ARRAY_TYPE:
X#ifdef PARM_CAN_BE_ARRAY_TYPE
X {
X tree length;
X
X *textp++ = 'A';
X length = array_type_nelts (parmtype);
X if (TREE_CODE (length) == INTEGER_CST)
X textp = icat (textp, TREE_INT_CST_LOW (length));
X *textp++ = '_';
X goto more;
X }
X#else
X *textp++ = 'P';
X goto more;
X#endif
X
X case POINTER_TYPE:
X *textp++ = 'P';
X more:
X textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
X break;
X
X case FUNCTION_TYPE:
X case METHOD_TYPE:
X {
X tree firstarg = TYPE_ARG_TYPES (parmtype);
X /* Otherwise have to implement reentrant typevecs,
X unmark and remark types, etc. */
X int old_nofold = nofold;
X nofold = 1;
X
X if (nrepeats)
X textp = flush_repeats (textp, typevec[maxtype-1]);
X
X /* @@ It may be possible to pass a function type in
X which is not preceded by a 'P'. */
X if (TREE_CODE (parmtype) == FUNCTION_TYPE)
X {

X *textp++ = 'F';
X if (firstarg == NULL_TREE)
X *textp++ = 'e';
X else if (firstarg == void_list_node)
X *textp++ = 'v';
X else
X textp = build_overload_name (firstarg, textp, text_end);
X }
X else
X {
X int constp = TREE_READONLY (TREE_TYPE (TREE_VALUE (firstarg)));
X int volatilep = TREE_VOLATILE (TREE_TYPE (TREE_VALUE (firstarg)));
X *textp++ = 'M';
X firstarg = TREE_CHAIN (firstarg);
X
X textp = build_overload_name (TYPE_METHOD_BASETYPE (parmtype), textp, text_end);
X if (constp)
X *textp++ = 'C';
X if (volatilep)
X *textp++ = 'V';
X
X /* For cfront 2.0 compatability. */
X *textp++ = 'F';
X
X if (firstarg == NULL_TREE)
X *textp++ = 'e';
X else if (firstarg == void_list_node)
X *textp++ = 'v';
X else
X textp = build_overload_name (firstarg, textp, text_end);
X }
X
X /* Separate args from return type. */
X *textp++ = '_';
X textp = build_overload_name (TREE_TYPE (parmtype), textp, text_end);
X nofold = old_nofold;
X break;
X }
X
X case INTEGER_TYPE:
X parmtype = TYPE_MAIN_VARIANT (parmtype);
X switch (TYPE_MODE (parmtype))
X {
X case TImode:
X if (parmtype == long_integer_type_node
X || parmtype == long_unsigned_type_node)
X *textp++ = 'l';
X else
X *textp++ = 'q';
X break;
X case DImode:
X if (parmtype == long_integer_type_node
X || parmtype == long_unsigned_type_node)
X *textp++ = 'l';
X else if (parmtype == integer_type_node
X || parmtype == unsigned_type_node)
X *textp++ = 'i';
X else if (parmtype == short_integer_type_node
X || parmtype == short_unsigned_type_node)
X *textp++ = 's';
X else
X *textp++ = 'x';
X break;
X case SImode:
X if (parmtype == long_integer_type_node
X || parmtype == long_unsigned_type_node)
X *textp++ = 'l';
X else if (parmtype == short_integer_type_node
X || parmtype == short_unsigned_type_node)
X *textp++ = 's';
X else
X *textp++ = 'i';
X break;
X case HImode:
X if (parmtype == integer_type_node
X || parmtype == unsigned_type_node)
X *textp++ = 'i';
X else
X *textp++ = 's';
X break;
X case QImode:
X *textp++ = 'c';
X break;
X default:
X abort ();
X }
X break;
X
X case REAL_TYPE:
X parmtype = TYPE_MAIN_VARIANT (parmtype);
X if (parmtype == long_double_type_node)
X *textp++ = 'r';
X else if (parmtype == double_type_node)
X *textp++ = 'd';
X else if (parmtype == float_type_node)
X *textp++ = 'f';
X else abort ();
X break;
X
X case VOID_TYPE:
X if (! just_one)
X {
X#if 0
X extern tree void_list_node;
X
X /* See if anybody is wasting memory. */
X assert (parmtypes == void_list_node);
X#endif
X /* This is the end of a parameter list. */
X *textp = '\0';
X return textp;
X }
X *textp++ = 'v';
X break;
X
X case ERROR_MARK: /* not right, but nothing is anyway */
X break;
X
X /* have to do these */
X case UNION_TYPE:
X case RECORD_TYPE:
X if (! just_one)
X /* Make this type signature look incompatible
X with AT&T. */
X *textp++ = 'G';
X goto common;
X case ENUMERAL_TYPE:
X common:
X {
X tree name = TYPE_NAME (parmtype);
X if (TREE_CODE (name) == TYPE_DECL)
X name = DECL_NAME (name);
X assert (TREE_CODE (name) == IDENTIFIER_NODE);
X textp = icat (textp, IDENTIFIER_LENGTH (name));
X strcpy (textp, IDENTIFIER_POINTER (name));
X textp += IDENTIFIER_LENGTH (name);
X break;
X }
X
X case UNKNOWN_TYPE:
X /* This will take some work. */
X *textp++ = '?';
X break;
X
X default:
X abort ();
X }
X
X next:
X if (just_one) break;
X parmtypes = TREE_CHAIN (parmtypes);
X }
X if (! just_one)
X {
X if (nrepeats)
X textp = flush_repeats (textp, typevec[maxtype-1]);
X
X /* To get here, parms must end with `...'. */
X *textp++ = 'e';
X }
X
X *textp = '\0';
X return textp;
X}
X
X/* Change the name of a function definition so that it may be
X overloaded. NAME is the name of the function to overload,
X PARMS is the parameter list (which determines what name the
X final function obtains).
X
X FOR_METHOD is 1 if this overload is being performed
X for a method, rather than a function type. It is 2 if
X this overload is being performed for a constructor. */
Xtree
Xbuild_decl_overload (name, parms, for_method)
X char *name;
X tree parms;
X int for_method;
X{
X int tmp;
X char tname[OVERLOAD_MAX_LEN];
X
X if (for_method == 2)
X /* We can divine that this is a constructor,
X and figure out its name without any extra encoding. */
X tmp = 0;
X else
X {
X strcpy (tname, name);
X tmp = strlen (tname);
X }
X tname[tmp++] = '_';
X tname[tmp++] = '_';
X if (for_method)
X {
X#if 0
X /* We can get away without doing this. */
X tname[tmp++] = 'M';
X#endif
X parms = temp_tree_cons (NULL_TREE, TREE_TYPE (TREE_VALUE (parms)), TREE_CHAIN (parms));
X }
X else
X tname[tmp++] = 'F';
X
X if (parms == NULL_TREE)
X tname[tmp++] = 'e', tname[tmp] = '\0';
X else if (parms == void_list_node)
X tname[tmp++] = 'v', tname[tmp] = '\0';
X else
X {
X ALLOCATE_TYPEVEC (parms);
X nofold = 0;
X if (for_method)
X {
X tmp = build_overload_name (TREE_VALUE (parms), tname+tmp, &tname[OVERLOAD_MAX_LEN]) - tname;
X
X#ifndef LONGERNAMES
X typevec[maxtype++] = TREE_VALUE (parms);
X TREE_USED (TREE_VALUE (parms)) = 1;
X#endif
X
X if (TREE_CHAIN (parms))
X build_overload_name (TREE_CHAIN (parms), tname+tmp, &tname[OVERLOAD_MAX_LEN]);
X else
X {
X tname[tmp++] = 'e';
X tname[tmp] = '\0';
X }
X }
X else
X build_overload_name (parms, tname+tmp, &tname[OVERLOAD_MAX_LEN]);
X DEALLOCATE_TYPEVEC (parms);
X }
X return get_identifier (tname);
X}
X
X/* Build an overload name for the type expression TYPE. */
Xtree
Xbuild_typename_overload (type)
X tree type;
X{
X char tname[OVERLOAD_MAX_LEN];
X int i = sizeof (OPERATOR_TYPENAME_FORMAT) - 1;
X sprintf (tname, OPERATOR_TYPENAME_FORMAT);
X#if 0
X /* We can get away without doing this--it really gets
X overloaded later. */
X tname[i++] = '_';
X tname[i++] = '_';
X tname[i++] = 'M';
X#endif
X nofold = 1;
X build_overload_name (type, tname + i, &tname[OVERLOAD_MAX_LEN]);
X return get_identifier (tname);
X}
X
X/* Top-level interface to explicit overload requests. Allow NAME
X to be overloaded. Error if NAME is already declared for the current
X scope. Warning if function is redundanly overloaded. */
X
Xvoid
Xdeclare_overloaded (name)
X tree name;
X{
X#ifdef NO_AUTO_OVERLOAD
X if (is_overloaded (name))
X warning ("function `%s' already declared overloaded",
X IDENTIFIER_POINTER (name));
X else if (IDENTIFIER_GLOBAL_VALUE (name))
X error ("overloading function `%s' that is already defined",
X IDENTIFIER_POINTER (name));
X else
X {
X TREE_OVERLOADED (name) = 1;
X IDENTIFIER_GLOBAL_VALUE (name) = build_tree_list (name, NULL_TREE);
X TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)) = unknown_type_node;
X }
X#else
X if (current_lang_name == lang_name_cplusplus)
X {
X if (0)
X warning ("functions are implicitly overloaded in C++");
X }
X else if (current_lang_name == lang_name_c)
X error ("overloading function `%s' cannot be done in C language context");
X else
X abort ();
X#endif
X}
X
X#ifdef NO_AUTO_OVERLOAD
X/* Check to see if NAME is overloaded. For first approximation,
X check to see if its TREE_OVERLOADED is set. This is used on
X IDENTIFIER nodes. */
Xint
Xis_overloaded (name)
X tree name;
X{
X /* @@ */
X return (TREE_OVERLOADED (name)
X && (! IDENTIFIER_CLASS_VALUE (name) || current_class_type == 0)
X && ! IDENTIFIER_LOCAL_VALUE (name));
X}
X#endif
X
X/* Given a tree_code CODE, and some arguments (at least one),
X attempt to use an overloaded operator on the arguments.
X
X For unary operators, only the first argument need be checked.
X For binary operators, both arguments may need to be checked.
X
X Member functions can convert class references to class pointers,
X for one-level deep indirection. More than that is not supported.
X Operators [](), ()(), and ->() must be member functions.
X
X We call function call building calls with nonzero complain if
X they are our only hope. This is true when we see a vanilla operator
X applied to something of aggregate type. If this fails, we are free to
X return `error_mark_node', because we will have reported the error.
X
X Operators NEW and DELETE overload in funny ways: operator new takes
X a single `size' parameter, and operator delete takes a pointer to the
X storage being deleted. When overloading these operators, success is
X assumed. If there is a failure, report an error message and return
X `error_mark_node'. */
X
X/* NOSTRICT */
Xtree
Xbuild_opfncall (code, flags, xarg1, xarg2, arg3)
X enum tree_code code;
X tree xarg1, xarg2;
X tree arg3;
X{
X tree rval = 0;
X tree arg1, arg2;
X tree type1, type2, fnname;
X tree fields1 = 0, parms = 0;
X tree global_fn;
X int try_second;
X int binary_is_unary;
X
X if (xarg1 == error_mark_node)
X return error_mark_node;
X
X if (code == COND_EXPR)
X {
X if (TREE_CODE (xarg2) == ERROR_MARK
X || TREE_CODE (arg3) == ERROR_MARK)
X return error_mark_node;
X }
X if (code == COMPONENT_REF)
X if (TREE_CODE (TREE_TYPE (xarg1)) == POINTER_TYPE)
X return rval;
X
X /* First, see if we can work with the first argument */
X type1 = TREE_TYPE (xarg1);
X
X /* Some tree codes have length > 1, but we really only want to
X overload them if their first argument has a user defined type. */
X switch (code)
X {
X case PREINCREMENT_EXPR:
X code = POSTINCREMENT_EXPR;
X binary_is_unary = 1;
X try_second = 0;
X break;
X
X case POSTDECREMENT_EXPR:
X code = PREDECREMENT_EXPR;
X binary_is_unary = 1;
X try_second = 0;
X break;
X
X case PREDECREMENT_EXPR:
X case POSTINCREMENT_EXPR:
X case COMPONENT_REF:
X binary_is_unary = 1;
X try_second = 0;
X break;
X
X /* ARRAY_REFs and CALL_EXPRs must overload successfully.
X If they do not, return error_mark_node instead of NULL_TREE. */
X case ARRAY_REF:
X if (xarg2 == error_mark_node)
X return error_mark_node;
X case CALL_EXPR:
X rval = error_mark_node;
X binary_is_unary = 0;
X try_second = 0;
X break;
X
X case NEW_EXPR:
X {
X /* For operators `new' (`delete'), only check visibility
X if we are in a constructor (destructor), and we are
X allocating for that constructor's (destructor's) type. */
X
X fnname = get_identifier (OPERATOR_NEW_FORMAT);
X if (flags & LOOKUP_GLOBAL)
X return build_overload_call (fnname, tree_cons (NULL_TREE, xarg2, arg3),
X flags & LOOKUP_COMPLAIN, 0);
X
X if (current_function_decl == NULL_TREE
X || !DECL_CONSTRUCTOR_P (current_function_decl)
X || current_class_type != TYPE_MAIN_VARIANT (type1))
X flags = LOOKUP_COMPLAIN;
X rval = build_method_call (build1 (NOP_EXPR, xarg1, error_mark_node),
X fnname, tree_cons (NULL_TREE, xarg2, arg3),
X NULL_TREE, flags);
X if (rval == error_mark_node)
X /* User might declare fancy operator new, but invoke it
X like standard one. */
X return rval;
X
X TREE_TYPE (rval) = xarg1;
X TREE_CALLS_NEW (rval) = 1;
X return rval;
X }
X break;
X
X case DELETE_EXPR:
X {
X /* See comment above. */
X
X fnname = get_identifier (OPERATOR_DELETE_FORMAT);
X if (flags & LOOKUP_GLOBAL)
X return build_overload_call (fnname, build_tree_list (NULL_TREE, xarg1),
X flags & LOOKUP_COMPLAIN, 0);
X
X if (current_function_decl == NULL_TREE
X || !DESTRUCTOR_NAME_P (DECL_NAME (current_function_decl))
X || current_class_type != TYPE_MAIN_VARIANT (type1))
X flags = LOOKUP_COMPLAIN;
X rval = build_method_call (build1 (NOP_EXPR, TREE_TYPE (xarg1), error_mark_node),
X fnname, build_tree_list (NULL_TREE, xarg1),
X NULL_TREE, flags);
X /* This happens when the user mis-declares `operator delete'.
X Should now be impossible. */
X assert (rval != error_mark_node);
X TREE_TYPE (rval) = void_type_node;
X return rval;
X }
X break;
X
X default:
X binary_is_unary = 0;
X try_second = tree_code_length [(int) code] == 2;
X if (xarg2 == error_mark_node)
X return error_mark_node;
X break;
X }
X
X if (try_second && xarg2 == error_mark_node)
X return error_mark_node;
X
X /* What ever it was, we do not know how to deal with it. */
X if (type1 == NULL_TREE)
X return rval;
X
X if (TREE_CODE (type1) == OFFSET_TYPE)
X type1 = TREE_TYPE (type1);
X
X if (TREE_CODE (type1) == REFERENCE_TYPE)
X {
X arg1 = convert_from_reference (xarg1);
X type1 = TREE_TYPE (arg1);
X }
X else
X {
X arg1 = xarg1;
X }
X
X if (!IS_AGGR_TYPE (type1))
X {
X /* Try to fail. First, fail if unary */
X if (! try_second)
X return rval;
X /* Second, see if second argument is non-aggregate. */
X type2 = TREE_TYPE (xarg2);
X if (TREE_CODE (type2) == OFFSET_TYPE)
X type2 = TREE_TYPE (type2);
X if (TREE_CODE (type2) == REFERENCE_TYPE)
X {
X arg2 = convert_from_reference (xarg2);
X type2 = TREE_TYPE (arg2);
X }
X else
X {
X arg2 = xarg2;
X }
X
X if (!IS_AGGR_TYPE (type2))
X return rval;
X try_second = 0;
X }
X
X if (try_second)
X {
X /* First arg may succeed; see whether second should. */
X type2 = TREE_TYPE (xarg2);
X if (TREE_CODE (type2) == OFFSET_TYPE)
X type2 = TREE_TYPE (type2);
X if (TREE_CODE (type2) == REFERENCE_TYPE)
X {
X arg2 = convert_from_reference (xarg2);
X type2 = TREE_TYPE (arg2);
X }
X else
X {
X arg2 = xarg2;
X }
X
X if (! IS_AGGR_TYPE (type2))
X try_second = 0;
X }
X
X if (type1 == unknown_type_node
X || (try_second && TREE_TYPE (xarg2) == unknown_type_node))
X {
X /* This will not be implemented in the forseeable future. */
X return rval;
X }
X
X if (code == MODIFY_EXPR)
X {
X tree op_id = build_opid (MODIFY_EXPR, arg3);
X fnname = build_operator_fnname (&op_id, 0, 2);
X }
X else
X {
X tree op_id = build_opid (0, code);
X if (binary_is_unary)
X fnname = build_operator_fnname (&op_id, 0, 1);
X else
X fnname = build_operator_fnname (&op_id, 0,
X tree_code_length [(int) code]);
X }
X
X global_fn = IDENTIFIER_GLOBAL_VALUE (fnname);
X
X /* This is the last point where we will accept failure. This
X may be too eager if we wish an overloaded operator not to match,
X but would rather a normal operator be called on a type-converted
X argument. */
X
X if (IS_AGGR_TYPE (type1))
X fields1 = lookup_fnfields (CLASSTYPE_AS_LIST (type1), fnname, 0);
X
X if (fields1 == NULL_TREE && global_fn == NULL_TREE)
X return rval;
X
X /* If RVAL winds up being `error_mark_node', we will return
X that... There is no way that normal semantics of these
X operators will succeed. */
X
X /* This argument may be an uncommited OFFSET_REF. This is
X the case for example when dealing with static class members
X which are referenced from their class name rather than
X from a class instance. */
X if (TREE_CODE (xarg1) == OFFSET_REF
X && TREE_CODE (TREE_OPERAND (xarg1, 1)) == VAR_DECL)
X xarg1 = TREE_OPERAND (xarg1, 1);
X if (try_second && xarg2 && TREE_CODE (xarg2) == OFFSET_REF
X && TREE_CODE (TREE_OPERAND (xarg2, 1)) == VAR_DECL)
X xarg2 = TREE_OPERAND (xarg2, 1);
X
X if (global_fn)
X flags |= LOOKUP_GLOBAL;
X
X if (code == CALL_EXPR)
X {
X /* This can only be a member function. */
X return build_method_call (xarg1, fnname, xarg2,
X NULL_TREE, LOOKUP_NORMAL);
X }
X else if (tree_code_length[(int) code] == 1 || binary_is_unary)
X {
X parms = NULL_TREE;
X rval = build_method_call (xarg1, fnname, NULL_TREE, NULL_TREE, flags);
X }
X else if (code == COND_EXPR)
X {
X parms = tree_cons (0, xarg2, build_tree_list (0, arg3));
X rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
X }
X else if (code == METHOD_CALL_EXPR)
X {
X /* must be a member function. */
X parms = tree_cons (NULL_TREE, xarg2, arg3);
X return build_method_call (xarg1, fnname, parms, NULL_TREE, LOOKUP_NORMAL);
X }
X else if (fields1)
X {
X parms = build_tree_list (NULL_TREE, xarg2);
X rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
X }
X else
X {
X parms = tree_cons (NULL_TREE, xarg1,
X build_tree_list (NULL_TREE, xarg2));
X rval = build_overload_call (fnname, parms, flags & LOOKUP_COMPLAIN, 0);
X }
X
X /* If we did not win, do not lose yet, since type conversion may work. */
X if (TREE_CODE (rval) == ERROR_MARK)
X {
X if (flags & LOOKUP_COMPLAIN)
X return rval;
X return 0;
X }
X
X return rval;
X}
X
X/* This function takes an identifier, ID, and attempts to figure out what
X it means. There are a number of possible scenarios, presented in increasing
X order of hair:
X
X 1) not in a class's scope
X 2) in class's scope, member name of the class's method
X 3) in class's scope, but not a member name of the class
X 4) in class's scope, member name of a class's variable
X
X NAME is $1 from the bison rule. It is an IDENTIFIER_NODE.
X VALUE is $$ from the bison rule. It is the value returned by lookup_name ($1)
X yychar is the pending input character (suitably encoded :-).
X
X As a last ditch, try to look up the name as a label and return that
X address.
X
X Values which are declared as being of REFERENCE_TYPE are
X automatically dereferenced here (as a hack to make the
X compiler faster). */
X
Xtree
Xhack_identifier (value, name, yychar)
X tree value, name;
X{
X tree type;
X
X if (TREE_CODE (value) == ERROR_MARK)
X {
X if (current_class_name)
X {
X tree fields = lookup_fnfields (CLASSTYPE_AS_LIST (current_class_type), name, 0);
X if (fields)
X {
X fields = TREE_VALUE (fields);
X assert (TREE_CODE (fields) == FUNCTION_DECL);
X if (TREE_CHAIN (fields) == NULL_TREE)
X {
X warning ("methods cannot be converted to function pointers");
X return fields;
X }
X else
X {
X error ("ambiguous request for method pointer `%s'",
X IDENTIFIER_POINTER (name));
X return error_mark_node;
X }
X }
X }
X if (flag_labels_ok && IDENTIFIER_LABEL_VALUE (name))
X {
X return IDENTIFIER_LABEL_VALUE (name);
X }
X return error_mark_node;
X }
X
X type = TREE_TYPE (value);
X if (TREE_NONLOCAL (value))
X {
X if (TREE_CODE (value) == FIELD_DECL)
X {
X if (current_class_decl == NULL_TREE)
X {
X error ("request for member `%s' in static member function",
X IDENTIFIER_POINTER (DECL_NAME (value)));
X return error_mark_node;
X }
X TREE_USED (current_class_decl) = 1;
X if (yychar == '(')
X if (! ((TYPE_LANG_SPECIFIC (type)
X && TYPE_OVERLOADS_CALL_EXPR (type))
X || (TREE_CODE (type) == REFERENCE_TYPE
X && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
X && TYPE_OVERLOADS_CALL_EXPR (TREE_TYPE (type))))
X && TREE_CODE (type) != FUNCTION_TYPE
X && TREE_CODE (type) != METHOD_TYPE
X && (TREE_CODE (type) != POINTER_TYPE
X || (TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE
X && TREE_CODE (TREE_TYPE (type)) != METHOD_TYPE)))
X {
X error ("component `%s' is not a method",
X IDENTIFIER_POINTER (name));
X return error_mark_node;
X }
X /* Mark so that if we are in a constructor, and then find that
X this field was initialized by a base initializer,
X we can emit an error message. */
X TREE_USED (value) = 1;
X return build_component_ref (C_C_D, name, 0, 1);
X }
X if (DECL_CONTEXT (value) != current_class_type
X && (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == CONST_DECL))
X {
X tree path;
X enum visibility_type visibility;
X
X get_base_distance (DECL_CONTEXT (value), current_class_type, 0, &path);
X visibility = compute_visibility (path, value);
X if (visibility != visibility_public)
X {
X if (TREE_CODE (value) == VAR_DECL)
X error ("static member `%s' is from private base class",
X IDENTIFIER_POINTER (name));
X else
X error ("enum `%s' is from private base class",
X IDENTIFIER_POINTER (name));
X return error_mark_node;
X }
X }
X else if (TREE_CODE (value) == TREE_LIST && type == 0)
X {
X error ("request for member `%s' is ambiguous in multiple inheritance lattice",
X IDENTIFIER_POINTER (name));
X return error_mark_node;
X }
X
X return value;
X }
X
X if (! TREE_USED (value))
X {
X if (TREE_EXTERNAL (value))
X assemble_external (value);
X TREE_USED (value) = 1;
X }
X if (TREE_CODE (type) == REFERENCE_TYPE)
X {
X if (! (TREE_CODE (value) == VAR_DECL
X || TREE_CODE (value) == PARM_DECL
X || TREE_CODE (value) == RESULT_DECL))
X abort ();
X if (DECL_REFERENCE_SLOT (value))
X return DECL_REFERENCE_SLOT (value);
X }
X return value;
X}
X
Xtree
Xhack_operator (op)
X tree op;
X{
X if (op == NULL_TREE)
X return error_mark_node;
X
X if (TREE_CODE (op) != TYPE_EXPR)
X return grokopexpr (&op, NULL_TREE, 0, 0, 0);
X
X return op;
X}
X
X/* NONWRAPPER is nonzero if this call is not to be wrapped.
X TYPE is the type that the wrapper belongs to (in case
X it should be non-virtual).
X DECL is the function will will be (not be) wrapped. */
Xtree
Xhack_wrapper (nonwrapper, type, decl)
X int nonwrapper;
X tree type, decl;
X{
X if (type == NULL_TREE || is_aggr_typedef (type, 1))
X {
X if (type)
X type = TREE_TYPE (type);
X
X switch (nonwrapper)
X {
X case 0:
X return build_nt (WRAPPER_EXPR, type, decl);
X case 1:
X return build_nt (ANTI_WRAPPER_EXPR, type, decl);
X case 2:
X return build_nt (WRAPPER_EXPR, type,
X build_nt (COND_EXPR, decl, NULL_TREE, NULL_TREE));
X default:
X assert (0 <= nonwrapper && nonwrapper <= 2);
X }
X }
X return error_mark_node;
X}
X
X/* Return an IDENTIFIER which can be used as a name for
X anonymous structs and unions. */
Xtree
Xmake_anon_name ()
X{
X static int cnt = 0;
X char buf[32];
X
X sprintf (buf, ANON_AGGRNAME_FORMAT, cnt++);
X return get_identifier (buf);
X}
X
X/* Given an object OF, and a type conversion operator COMPONENT
X build a call to the conversion operator, if a call is requested,
X or return the address (as a pointer to member function) if one is not.
X
X OF can be a TYPE_DECL or any kind of datum that would normally
X be passed to `build_component_ref'. It may also be NULL_TREE,
X in which case `current_class_type' and `current_class_decl'
X provide default values.
X
X BASETYPE_PATH, if non-null, is the path of basetypes
X to go through before we get the the instance of interest.
X
X PROTECT says whether we apply C++ scoping rules or not. */
Xtree
Xbuild_component_type_expr (of, component, basetype_path, protect)
X tree of, component, basetype_path;
X int protect;
X{
X tree cname = NULL_TREE;
X tree tmp, last;
X tree name;
X int flags = protect ? LOOKUP_NORMAL : LOOKUP_COMPLAIN;
X
X assert (IS_AGGR_TYPE (TREE_TYPE (of)));
X assert (TREE_CODE (component) == TYPE_EXPR);
X
X tmp = TREE_OPERAND (component, 0);
X last = NULL_TREE;
X
X while (tmp)
X {
X switch (TREE_CODE (tmp))
X {
X case CALL_EXPR:
X if (last)
X TREE_OPERAND (last, 0) = TREE_OPERAND (tmp, 0);
X else
X TREE_OPERAND (component, 0) = TREE_OPERAND (tmp, 0);
X if (TREE_OPERAND (tmp, 0)
X && TREE_OPERAND (tmp, 0) != void_list_node)
X {
X error ("operator requires empty parameter list");
X TREE_OPERAND (tmp, 0) = NULL_TREE;
X }
X last = groktypename (build_tree_list (TREE_TYPE (component),
X TREE_OPERAND (component, 0)));
X name = build_typename_overload (last);
X TREE_TYPE (name) = last;
X
X if (of && TREE_CODE (of) != TYPE_DECL)
X return build_method_call (of, name, NULL_TREE, NULL_TREE, flags);
X else if (of)
X {
X tree this_this;
X
X if (current_class_decl == NULL_TREE)
X {
X error ("object required for `operator ' call");
X return error_mark_node;
X }
X
X this_this = convert_pointer_to (TREE_TYPE (of), current_class_decl);
X return build_method_call (this_this, name, NULL_TREE,
X NULL_TREE, flags | LOOKUP_NONVIRTUAL);
X }
X else if (current_class_decl)
X return build_method_call (tmp, name, NULL_TREE, NULL_TREE, flags);
X
X error ("object required for `operator ' call");
X return error_mark_node;
X
X case INDIRECT_REF:
X case ADDR_EXPR:
X case ARRAY_REF:
X break;
X
X case SCOPE_REF:
X assert (cname == 0);
X cname = TREE_OPERAND (tmp, 0);
X tmp = TREE_OPERAND (tmp, 1);
X break;
X
X default:
X abort ();
X }
X last = tmp;
X tmp = TREE_OPERAND (tmp, 0);
X }
X
X last = groktypename (build_tree_list (TREE_TYPE (component), TREE_OPERAND (component, 0)));
X name = build_typename_overload (last);
X TREE_TYPE (name) = last;
X if (of && TREE_CODE (of) == TYPE_DECL)
X {
X if (cname == NULL_TREE)
X {
X cname = DECL_NAME (of);
X of = NULL_TREE;
X }
X else assert (cname == DECL_NAME (of));
X }
X
X if (of)
X {
X tree this_this;
X
X if (current_class_decl == NULL_TREE)
X {
X error ("object required for `operator ' call");
X return error_mark_node;
X }
X
X this_this = convert_pointer_to (TREE_TYPE (of), current_class_decl);
X return build_component_ref (this_this, name, 0, protect);
X }
X else if (cname)
X return build_offset_ref (cname, name);
X else if (current_class_name)
X return build_offset_ref (current_class_name, name);
X
X error ("object required for `operator ' member reference");
X return error_mark_node;
X}
!EOF
echo "Extracting g++filt.c..."
sed 's/^X//' >g++filt.c << '!EOF'
X/* Demangler filter for GNU C++
X Copyright (C) 1989 Free Software Foundation, Inc.
X written by James Clark ([email protected])
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include
X#include
X#include
X
Xchar *malloc ();
Xchar *realloc ();
X
X#ifndef __STDC__
X#define const
X#endif
X
X#ifdef __STDC__
Xextern char *cplus_demangle (const char *);
X#else
Xextern char *cplus_demangle ();
X#endif
X
Xvoid fatal ();
X
Xstatic char *prog_name;
X
X#define SYMBOL_MAX 1024
X
X#define SYMBOL_CHAR(c) (isascii(c) && (isalnum(c) || (c) == '_' || (c) == '$'))
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X char buf[SYMBOL_MAX+1];
X int c;
X
X prog_name = argv[0];
X
X c = getchar ();
X while (c != EOF)
X {
X int i = 0;
X while (c != EOF && !SYMBOL_CHAR (c))
X {
X putchar (c);
X c = getchar ();
X }
X while (i < SYMBOL_MAX && c != EOF && SYMBOL_CHAR (c))
X {
X buf[i++] = c;
X c = getchar ();
X }
X buf[i] = '\0';
X if (i == SYMBOL_MAX)
X {
X fputs (buf, stdout);
X while (c != EOF && SYMBOL_CHAR (c))
X {
X putchar (c);
X c = getchar ();
X }
X }
X else
X {
X char *result;
X if (i > 0 && (result = cplus_demangle (buf)) != NULL)
X {
X fputs (result, stdout);
X free (result);
X }
X else
X fputs (buf, stdout);
X }
X }
X return 0;
X}
X
Xchar *
Xxmalloc (n)
X int n;
X{
X char *tem = malloc (n);
X if (tem == NULL)
X fatal ("out of memory");
X return tem;
X}
X
Xchar *
Xxrealloc (p, n)
X char *p;
X int n;
X{
X char *tem = realloc (p, n);
X if (tem == NULL)
X fatal ("out of memory");
X return tem;
X}
X
Xvoid
Xfatal (message)
X const char *message;
X{
X fprintf (stderr, "%s: %s\n", prog_name, message);
X exit (1);
X}
!EOF
echo "Extracting stab.def..."
sed 's/^X//' >stab.def << '!EOF'
X/* Table of DBX symbol codes for the GNU system.
X Copyright (C) 1988 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* Global variable. Only the name is significant.
X To find the address, look in the corresponding external symbol. */
X__define_stab (N_GSYM, 0x20, "GSYM")
X
X/* Function name for BSD Fortran. Only the name is significant.
X To find the address, look in the corresponding external symbol. */
X__define_stab (N_FNAME, 0x22, "FNAME")
X
X/* Function name or text-segment variable for C. Value is its address.
X Desc is supposedly starting line number, but GCC doesn't set it
X and DBX seems not to miss it. */
X__define_stab (N_FUN, 0x24, "FUN")
X
X/* Data-segment variable with internal linkage. Value is its address. */
X__define_stab (N_STSYM, 0x26, "STSYM")
X
X/* BSS-segment variable with internal linkage. Value is its address. */
X__define_stab (N_LCSYM, 0x28, "LCSYM")
X
X/* Name of main routine. Only the name is significant.
X This is not used in C. */
X__define_stab (N_MAIN, 0x2a, "MAIN")
X
X/* Register variable. Value is number of register. */
X__define_stab (N_RSYM, 0x40, "RSYM")
X
X/* Structure or union element. Value is offset in the structure. */
X__define_stab (N_SSYM, 0x60, "SSYM")
X
X/* Parameter variable. Value is offset from argument pointer.
X (On most machines the argument pointer is the same as the frame pointer. */
X__define_stab (N_PSYM, 0xa0, "PSYM")
X
X/* Automatic variable in the stack. Value is offset from frame pointer.
X Also used for type descriptions. */
X__define_stab (N_LSYM, 0x80, "LSYM")
X
X/* Alternate entry point. Value is its address. */
X__define_stab (N_ENTRY, 0xa4, "ENTRY")
X
X/* Name of main source file.
X Value is starting text address of the compilation. */
X__define_stab (N_SO, 0x64, "SO")
X
X/* Name of sub-source file.
X Value is starting text address of the compilation. */
X__define_stab (N_SOL, 0x84, "SOL")
X
X/* Line number in text segment. Desc is the line number;
X value is corresponding address. */
X__define_stab (N_SLINE, 0x44, "SLINE")
X/* Similar, for data segment. */
X__define_stab (N_DSLINE, 0x66, "DSLINE")
X/* Similar, for bss segment. */
X__define_stab (N_BSLINE, 0x68, "BSLINE")
X
X/* Beginning of an include file. Only Sun uses this.
X In an object file, only the name is significant.
X The Sun linker puts data into some of the other fields. */
X__define_stab (N_BINCL, 0x82, "BINCL")
X/* End of an include file. No name.
X These two act as brackets around the file's output.
X In an object file, there is no significant data in this entry.
X The Sun linker puts data into some of the fields. */
X__define_stab (N_EINCL, 0xa2, "EINCL")
X/* Place holder for deleted include file.
X This appears only in output from the Sun linker. */
X__define_stab (N_EXCL, 0xc2, "EXCL")
X
X/* Beginning of lexical block.
X The desc is the nesting level in lexical blocks.
X The value is the address of the start of the text for the block.
X The variables declared inside the block *precede* the N_LBRAC symbol. */
X__define_stab (N_LBRAC, 0xc0, "LBRAC")
X/* End of a lexical block. Desc matches the N_LBRAC's desc.
X The value is the address of the end of the text for the block. */
X__define_stab (N_RBRAC, 0xe0, "RBRAC")
X
X/* Begin named common block. Only the name is significant. */
X__define_stab (N_BCOMM, 0xe2, "BCOMM")
X/* Begin named common block. Only the name is significant
X (and it should match the N_BCOMM). */
X__define_stab (N_ECOMM, 0xe4, "ECOMM")
X/* End common (local name): value is address.
X I'm not sure how this is used. */
X__define_stab (N_ECOML, 0xe8, "ECOML")
X/* Second symbol entry containing a length-value for the preceding entry.
X The value is the length. */
X__define_stab (N_LENG, 0xfe, "LENG")
X
X/* Global symbol in Pascal.
X Supposedly the value is its line number; I'm skeptical. */
X__define_stab (N_PC, 0x30, "PC")
X
X/* Modula-2 compilation unit. Can someone say what info it contains? */
X__define_stab (N_M2C, 0x42, "M2C")
X/* Modula-2 scope information. Can someone say what info it contains? */
X__define_stab (N_SCOPE, 0xc4, "SCOPE")
X
X/* Sun's source-code browser stabs. ?? Don't know what the fields are. */
X__define_stab (N_BROWS, 0x48, "BROWS")
X
X/* GNU C++ exception stabs. */
X
X/* GNU C++ exception variable. Name is variable name. */
X__define_stab (N_EHDECL, 0x50, "EHDECL")
X
X/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if
X this entry is immediately followed by a CAUGHT stab saying what exception
X was caught. Multiple CAUGHT stabs means that multiple exceptions
X can be caught here. If Desc is 0, it means all exceptions are caught
X here. */
X__define_stab (N_CATCH, 0x54, "CATCH")
X
X/* The above information, in matrix format.
X
X STAB MATRIX
X _________________________________________________
X | 00 - 1F are not dbx stab symbols |
X | Entries with bits 01 set are external symbols |
X | N_UNDEF | N_ABS | N_TEXT | N_DATA |
X | N_BSS | N_COMM | | N_FN |
X |_______________________________________________|
X | 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM |
X | 28 LCSYM | 2A MAIN | 2C | 2E |
X | 30 PC | 32 | 34 | 36 |
X | 38 | 3A | 3C | 3E |
X | 40 RSYM | 42 M2C | 44 SLINE | 46 |
X | 48 BROWS | 4A | 4C | 4E |
X | 50 EHDECL | 52 | 54 CATCH | 56 |
X | 58 | 5A | 5C | 5E |
X | 60 SSYM | 62 | 64 SO | 66 DSLINE |
X | 68 BSLINE | 6A | 6C | 6E |
X | 70 | 72 | 74 | 76 |
X | 78 | 7A | 7C | 7E |
X | 80 LSYM | 82 BINCL | 84 SOL | 86 |
X | 88 | 8A | 8C | 8E |
X | 90 | 92 | 94 | 96 |
X | 98 | 9A | 9C | 9E |
X | A0 PSYM | A2 EINCL | A4 ENTRY | A6 |
X | A8 | AA | AC | AE |
X | B0 | B2 | B4 | B6 |
X | B8 | BA | BC | BE |
X | C0 LBRAC | C2 EXCL | C4 SCOPE | C6 |
X | C8 | CA | CC | CE |
X | D0 | D2 | D4 | D6 |
X | D8 | DA | DC | DE |
X | E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 |
X | E8 ECOML | EA | EC | EE |
X | F0 | F2 | F4 | F6 |
X | F8 | FA | FC | FE LENG |
X +-----------------------------------------------+
X
X*/
!EOF
echo "Extracting case.c..."
sed 's/^X//' >case.c << '!EOF'
X#ifndef FIRST_PSEUDO_REGISTER
X#define NULL 0
X#include "config.h"
X#include "rtl.h"
X#include "tree.h"
X#include "insn-flags.h"
X#endif
X
X/* Functions and data structures for expanding case statements. */
X
X/* Case label structure, used to hold info on labels within case
X statements. We handle "range" labels; for a single-value label
X as in C, the high and low limits are the same. */
X
Xstruct case_node
X{
X struct case_node *left;
X struct case_node *right;
X struct case_node *parent;
X tree low;
X tree high;
X tree test_label;
X tree code_label;
X};
X
Xtypedef struct case_node case_node;
Xtypedef struct case_node *case_node_ptr;
X
Xvoid balance_case_nodes ();
Xvoid emit_case_nodes ();
Xvoid group_case_nodes ();
Xvoid emit_jump_if_reachable ();
X
X/* Generate code to jump to LABEL if OP1 and OP2 are equal. */
X
Xstatic void
Xdo_jump_if_equal (op1, op2, label, unsignedp)
X rtx op1, op2, label;
X int unsignedp;
X{
X if (GET_CODE (op1) == CONST_INT
X && GET_CODE (op2) == CONST_INT)
X {
X if (INTVAL (op1) == INTVAL (op2))
X emit_jump (label);
X }
X else
X {
X emit_cmp_insn (op1, op2, 0, unsignedp, 0);
X emit_jump_insn (gen_beq (label));
X }
X}
X
X/* Not all case values are encountered equally. This function
X uses a heuristic to weight case labels, in cases where that
X looks like a reasonable thing to do.
X
X Right now, all we try to guess is text, and we establish the
X following weights:
X
X chars above space: 16
X digits: 16
X default: 12
X space, punct: 8
X tab: 4
X newline: 2
X other "\" chars: 1
X remaining chars: 0
X
X Under this weighting, ranges are automagically taken care of. */
X
X#include
Xstatic char *cost_table;
Xstatic int use_cost_table;
X
Xvoid
Xestimate_case_costs (node, default_label)
X case_node_ptr node;
X rtx default_label;
X{
X tree min_ascii = build_int_2 (-1, -1);
X tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0));
X case_node_ptr n;
X use_cost_table = 0;
X
X /* If the user is running without default, who are we
X to guess where values are likely to land? */
X if (default_label == 0)
X return;
X
X /* See if all the case expressions look like text. It is text if the lowest
X constant is >= -1 and the highest constant is <= 127. If the case
X expression is unsigned, suppress the test for >= -1, since it would always
X be true. */
X for (n = node; n; n = n->right)
X if ((! TREE_UNSIGNED (TREE_TYPE (n->low))
X && tree_int_cst_lt (n->low, min_ascii))
X || tree_int_cst_lt (max_ascii, n->high))
X return;
X
X /* All interesting values with within the range of
X interesting ASCII characters. */
X if (cost_table == NULL)
X {
X int i;
X cost_table = ((char *)malloc (129)) + 1;
X bzero (cost_table-1, 128);
X for (i = 0; i < 128; i++)
X {
X if (isalnum (i))
X cost_table[i] = 16;
X else if (ispunct (i))
X cost_table[i] = 8;
X else if (iscntrl (i))
X cost_table[i] = -1;
X }
X cost_table[' '] = 8;
X cost_table['\t'] = 4;
X cost_table['\0'] = 4;
X cost_table['\n'] = 2;
X cost_table['\f'] = 1;
X cost_table['\v'] = 1;
X cost_table['\b'] = 1;
X }
X use_cost_table = 1;
X}
X
X/* Scan an ordered list of case nodes
X combining those with consecutive values or ranges.
X
X Eg. three separate entries 1: 2: 3: become one entry 1..3: */
X
Xvoid
Xgroup_case_nodes (head)
X case_node_ptr head;
X{
X case_node_ptr node = head;
X
X while (node)
X {
X rtx lb = next_real_insn (label_rtx (node->code_label));
X case_node_ptr np = node;
X
X /* Try to group the successors of NODE with NODE. */
X while (((np = np->right) != 0)
X /* Do they jump to the same place? */
X && next_real_insn (label_rtx (np->code_label)) == lb
X /* Are their ranges consecutive? */
X && tree_int_cst_equal (np->low,
X combine (PLUS_EXPR, node->high,
X integer_one_node)))
X {
X node->high = np->high;
X }
X /* NP is the first node after NODE which can't be grouped with it.
X Delete the nodes in between, and move on to that node. */
X node->right = np;
X node = np;
X }
X}
X
X/* Take an ordered list of case nodes
X and transform them into a near optimal binary tree,
X on the assumtion that any target code selection value is as
X likely as any other.
X
X The transformation is performed by splitting the ordered
X list into two equal sections plus a pivot. The parts are
X then attached to the pivot as left and right branches. Each
X branch is is then transformed recursively. */
X
Xvoid
Xbalance_case_nodes (head, parent)
X case_node_ptr *head;
X case_node_ptr parent;
X{
X register case_node_ptr np;
X
X np = *head;
X if (np)
X {
X int cost = 0;
X int i = 0;
X int ranges = 0;
X register case_node_ptr *npp;
X case_node_ptr left;
X
X /* Count the number of entries on branch.
X Also count the ranges. */
X while (np)
X {
X if (!tree_int_cst_equal (np->low, np->high))
X {
X ranges++;
X if (use_cost_table)
X {
X int hi_cost = cost_table[TREE_INT_CST_LOW (np->high)];
X if (hi_cost < 0)
X use_cost_table = 0;
X else
X cost += hi_cost;
X }
X }
X if (use_cost_table)
X {
X int lo_cost = cost_table[TREE_INT_CST_LOW (np->low)];
X if (lo_cost < 0)
X use_cost_table = 0;
X else
X cost += lo_cost;
X }
X else
X cost += 1;
X i++;
X np = np->right;
X }
X if (i > 2)
X {
X /* Split this list if it is long enough for that to help. */
X npp = head;
X left = *npp;
X if (use_cost_table)
X {
X /* Find the place in the list that bisects the list's total cost,
X Here I gets half the total cost. */
X int n_moved = 0;
X i = (cost + 1) / 2;
X while (1)
X {
X /* Skip nodes while their cost does not reach that amount. */
X if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
X i -= cost_table[TREE_INT_CST_LOW ((*npp)->high)];
X i -= cost_table[TREE_INT_CST_LOW ((*npp)->low)];
X if (i <= 0)
X break;
X npp = &(*npp)->right;
X n_moved += 1;
X }
X if (n_moved == 0)
X {
X /* Leave this branch lopsided, but optimize left-hand
X side and fill in `parent' fields for right-hand side. */
X np = *head;
X np->parent = parent;
X balance_case_nodes (&np->left, np);
X for (; np->right; np = np->right)
X np->right->parent = np;
X return;
X }
X }
X /* If there are just three nodes, split at the middle one. */
X else if (i == 3)
X npp = &(*npp)->right;
X else
X {
X /* Find the place in the list that bisects the list's total cost,
X where ranges count as 2.
X Here I gets half the total cost. */

X i = (i + ranges + 1) / 2;
X while (1)
X {
X /* Skip nodes while their cost does not reach that amount. */
X if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
X i--;
X i--;
X if (i <= 0)
X break;
X npp = &(*npp)->right;
X }
X }
X *head = np = *npp;
X *npp = 0;
X np->parent = parent;
X np->left = left;
X
X /* Optimize each of the two split parts. */
X balance_case_nodes (&np->left, np);
X balance_case_nodes (&np->right, np);
X }
X else
X {
X /* Else leave this branch as one level,
X but fill in `parent' fields. */
X np = *head;
X np->parent = parent;
X for (; np->right; np = np->right)
X np->right->parent = np;
X }
X }
X}
X
X/* Search the parent sections of the case node tree
X to see if a test for the lower bound of NODE would be redundant.
X
X The instructions to synthesis the case decision tree are
X output in the same order as nodes are processed so it is
X known that if a parent node checks the range of the current
X node minus one that the current node is bounded at its lower
X span. Thus the test would be redundant. */
X
Xstatic int
Xnode_has_low_bound (node)
X case_node_ptr node;
X{
X tree low_minus_one;
X case_node_ptr pnode;
X
X if (node->left)
X {
X low_minus_one = combine (MINUS_EXPR, node->low, integer_one_node);
X /* Avoid the screw case of overflow where low_minus_one is > low. */
X if (tree_int_cst_lt (low_minus_one, node->low))
X for (pnode = node->parent; pnode; pnode = pnode->parent)
X {
X if (tree_int_cst_equal (low_minus_one, pnode->high))
X return 1;
X /* If a parent node has a left branch we know that none
X of its parents can have a high bound of our target
X minus one so we abort the search. */
X if (node->left)
X break;
X }
X }
X return 0;
X}
X
X/* Search the parent sections of the case node tree
X to see if a test for the upper bound of NODE would be redundant.
X
X The instructions to synthesis the case decision tree are
X output in the same order as nodes are processed so it is
X known that if a parent node checks the range of the current
X node plus one that the current node is bounded at its upper
X span. Thus the test would be redundant. */
X
Xstatic int
Xnode_has_high_bound (node)
X case_node_ptr node;
X{
X tree high_plus_one;
X case_node_ptr pnode;
X
X if (node->right == 0)
X {
X high_plus_one = combine (PLUS_EXPR, node->high, integer_one_node);
X /* Avoid the screw case of overflow where high_plus_one is > high. */
X if (tree_int_cst_lt (node->high, high_plus_one))
X for (pnode = node->parent; pnode; pnode = pnode->parent)
X {
X if (tree_int_cst_equal (high_plus_one, pnode->low))
X return 1;
X /* If a parent node has a right branch we know that none
X of its parents can have a low bound of our target
X plus one so we abort the search. */
X if (node->right)
X break;
X }
X }
X return 0;
X}
X
X/* Search the parent sections of the
X case node tree to see if both tests for the upper and lower
X bounds of NODE would be redundant. */
X
Xstatic int
Xnode_is_bounded (node)
X case_node_ptr node;
X{
X if (node->left || node->right)
X return 0;
X return node_has_low_bound (node) && node_has_high_bound (node);
X}
X
X/* Emit an unconditional jump to LABEL unless it would be dead code. */
X
Xvoid
Xemit_jump_if_reachable (label)
X rtx label;
X{
X rtx last_insn;
X
X if (GET_CODE (get_last_insn ()) != BARRIER)
X emit_jump (label);
X}
X
X/* Emit step-by-step code to select a case for the value of INDEX.
X The thus generated decision tree follows the form of the
X case-node binary tree NODE, whose nodes represent test conditions.
X UNSIGNEDP is nonzero if we should do unsigned comparisons.
X
X Care is taken to prune redundant tests from the decision tree
X by detecting any boundary conditions already checked by
X emitted rtx. (See node_has_high_bound, node_has_low_bound
X and node_is_bounded, above.)
X
X Where the test conditions can be shown to be redundant we emit
X an unconditional jump to the target code. As a further
X optimization, the subordinates of a tree node are examined to
X check for bounded nodes. In this case conditional and/or
X unconditional jumps as a result of the boundary check for the
X current node are arranged to target the subordinates associated
X code for out of bound conditions on the current node node. */
X
Xvoid
Xemit_case_nodes (index, node, default_label, unsignedp)
X rtx index;
X case_node_ptr node;
X rtx default_label;
X int unsignedp;
X{
X /* If INDEX has an unsigned type, we must make unsigned branches. */
X typedef rtx rtx_function ();
X rtx_function *gen_bgt_pat = unsignedp ? gen_bgtu : gen_bgt;
X rtx_function *gen_bge_pat = unsignedp ? gen_bgeu : gen_bge;
X rtx_function *gen_blt_pat = unsignedp ? gen_bltu : gen_blt;
X rtx_function *gen_ble_pat = unsignedp ? gen_bleu : gen_ble;
X int defaulted_left = 0;
X int defaulted_right = 0;
X
X if (node->test_label)
X {
X /* If this test node requires a label it follows that
X it must be preceeded by an unconditional branch.
X If control can pass to this point we can assume that
X a "br default" is in order. */
X emit_jump_if_reachable (default_label);
X expand_label (node->test_label);
X }
X if (tree_int_cst_equal (node->low, node->high))
X {
X /* Node is single valued. */
X do_jump_if_equal (index, expand_expr (node->low, 0, VOIDmode, 0),
X label_rtx (node->code_label), unsignedp);
X if (node->right)
X {
X if (node->left)
X {
X /* This node has children on either side. */
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X
X if (node_is_bounded (node->right))
X {
X emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label)));
X if (node_is_bounded (node->left))
X emit_jump (label_rtx (node->left->code_label));
X else
X emit_case_nodes (index, node->left,
X default_label, unsignedp);
X }
X else
X {
X if (node_is_bounded (node->left))
X emit_jump_insn ((*gen_blt_pat) (label_rtx (node->left->code_label)));
X else
X {
X node->right->test_label =
X build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->test_label)));
X emit_case_nodes (index, node->left,
X default_label, unsignedp);
X }
X emit_case_nodes (index, node->right,
X default_label, unsignedp);
X }
X }
X else
X {
X /* Here we have a right child but no left
X so we issue conditional branch to default
X and process the right child. */
X
X /* Omit the conditional branch to default
X if we it avoid only one right child;
X it costs too much space to save so little time. */
X if (node->right->right && !node_has_low_bound (node))
X {
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_blt_pat) (default_label));
X }
X if (node_is_bounded (node->right))
X emit_jump (label_rtx (node->right->code_label));
X else
X emit_case_nodes (index, node->right, default_label, unsignedp);
X }
X }
X else if (node->left)
X {
X if (use_cost_table
X && ! defaulted_right
X && cost_table[TREE_INT_CST_LOW (node->high)] < 12)
X {
X /* If our "most probably entry" is less probable
X than the default label, emit a jump to
X the default label using condition codes
X already lying around. With no right branch,
X a branch-greater-than will get us to the default
X label correctly. */
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bgt_pat) (default_label));
X /* No sense doing this too often. */
X defaulted_right = 1;
X }
X if (node_is_bounded (node->left))
X emit_jump (label_rtx (node->left->code_label));
X else
X emit_case_nodes (index, node->left, default_label, unsignedp);
X }
X }
X else
X {
X /* Node is a range. */
X if (node->right)
X {
X if (node->left)
X {
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X if (node_is_bounded (node->right))
X {
X /* Right hand node is fully bounded so we can
X eliminate any testing and branch directly
X to the target code. */
X emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label)));
X }
X else
X {
X /* Right hand node requires testing so create
X a label to put on the cmp code. */
X node->right->test_label =
X build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->test_label)));
X }
X emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label)));
X if (node_is_bounded (node->left))
X {
X /* Left hand node is fully bounded so we can
X eliminate any testing and branch directly
X to the target code. */
X emit_jump (label_rtx (node->left->code_label));
X }
X else
X emit_case_nodes (index, node->left, default_label, unsignedp);
X /* If right node has been given a test label above
X we must process it now. */
X if (node->right->test_label)
X emit_case_nodes (index, node->right, default_label, unsignedp);
X }
X else
X {
X if (!node_has_low_bound (node))
X {
X emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_blt_pat) (default_label));
X }
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_ble_pat) (label_rtx (node->code_label)));
X if (node_is_bounded (node->right))
X {
X /* Right hand node is fully bounded so we can
X eliminate any testing and branch directly
X to the target code. */
X emit_jump (label_rtx (node->right->code_label));
X }
X else
X emit_case_nodes (index, node->right, default_label, unsignedp);
X }
X }
X else if (node->left)
X {
X if (!node_has_high_bound (node))
X {
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bgt_pat) (default_label));
X }
X emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label)));
X if (node_is_bounded (node->left))
X {
X /* Left hand node is fully bounded so we can
X eliminate any testing and branch directly
X to the target code. */
X emit_jump (label_rtx (node->left->code_label));
X }
X else
X emit_case_nodes (index, node->left, default_label, unsignedp);
X }
X else
X {
X /* Node has no children so we check low and
X high bounds to remove redundant tests. In practice
X only one of the limits may be bounded or the parent
X node will have emmited a jump to our target code. */
X if (!node_has_high_bound (node))
X {
X emit_cmp_insn (index, expand_expr (node->high, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bgt_pat) (default_label));
X }
X if (!node_has_low_bound (node))
X {
X emit_cmp_insn (index, expand_expr (node->low, 0, VOIDmode, 0), 0, unsignedp, 0);
X emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label)));
X }
X /* We allow the default case to drop through since
X it will picked up by calls to `jump_if_reachable'
X either on the next test label or at the end of
X the decision tree emission. */
X }
X }
X}
X
!EOF
echo "Extracting cplus-parse.h..."
sed 's/^X//' >cplus-parse.h << '!EOF'
X/* Define constants for communication with parse.y.
X Copyright (C) 1987 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY. No author or distributor
Xaccepts responsibility to anyone for the consequences of using it
Xor for whether it serves any particular purpose or works at all,
Xunless he says so in writing. Refer to the GNU CC General Public
XLicense for full details.
X
XEveryone is granted permission to copy, modify and redistribute
XGNU CC, but only under the conditions described in the
XGNU CC General Public License. A copy of this license is
Xsupposed to have been given to you along with GNU CC so you
Xcan know your rights and responsibilities. It should be in a
Xfile named COPYING. Among other things, the copyright notice
Xand this notice must be preserved on all copies. */
X
X
X
Xenum rid
X{
X RID_UNUSED,
X RID_INT,
X RID_CHAR,
X RID_FLOAT,
X RID_DOUBLE,
X RID_VOID,
X RID_UNUSED1,
X
X /* C++ extension */
X RID_CLASS,
X RID_RECORD,
X RID_UNION,
X RID_ENUM,
X RID_LONGLONG,
X
X RID_UNSIGNED,
X RID_SHORT,
X RID_LONG,
X RID_AUTO,
X RID_STATIC,
X RID_EXTERN,
X RID_REGISTER,
X RID_TYPEDEF,
X RID_SIGNED,
X RID_CONST,
X RID_VOLATILE,
X RID_INLINE,
X RID_NOALIAS,
X
X /* extensions */
X RID_FRIEND,
X RID_VIRTUAL,
X RID_EXCEPTION,
X RID_RAISES,
X
X RID_MAX,
X};
X
X#define RID_FIRST_MODIFIER RID_UNSIGNED
!EOF
echo "Extracting gcc.c..."
sed 's/^X//' >gcc.c << '!EOF'
X/* Compiler driver program that can handle many languages.
X Copyright (C) 1987,1989 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
X
XThis paragraph is here to try to keep Sun CC from dying.
XThe number of chars here seems crucial!!!! */
X
Xvoid record_temp_file ();
X
X/* This program is the user interface to the C compiler and possibly to
Xother compilers. It is used because compilation is a complicated procedure
Xwhich involves running several programs and passing temporary files between
Xthem, forwarding the users switches to those programs selectively,
Xand deleting the temporary files at the end.
X
XCC recognizes how to compile each input file by suffixes in the file names.
XOnce it knows which kind of compilation to perform, the procedure for
Xcompilation is specified by a string called a "spec".
X
XSpecs are strings containing lines, each of which (if not blank)
Xis made up of a program name, and arguments separated by spaces.
XThe program name must be exact and start from root, since no path
Xis searched and it is unreliable to depend on the current working directory.
XRedirection of input or output is not supported; the subprograms must
Xaccept filenames saying what files to read and write.
X
XIn addition, the specs can contain %-sequences to substitute variable text
Xor for conditional text. Here is a table of all defined %-sequences.
XNote that spaces are not generated automatically around the results of
Xexpanding these sequences; therefore, you can concatenate them together
Xor with constant text in a single argument.
X
X %% substitute one % into the program name or argument.
X %i substitute the name of the input file being processed.
X %b substitute the basename of the input file being processed.
X This is the substring up to (and not including) the last period.
X %g substitute the temporary-file-name-base. This is a string chosen
X once per compilation. Different temporary file names are made by
X concatenation of constant strings on the end, as in `%g.s'.
X %g also has the same effect of %d.
X %d marks the argument containing or following the %d as a
X temporary file name, so that that file will be deleted if CC exits
X successfully. Unlike %g, this contributes no text to the argument.
X %w marks the argument containing or following the %w as the
X "output file" of this compilation. This puts the argument
X into the sequence of arguments that %o will substitute later.
X %o substitutes the names of all the output files, with spaces
X automatically placed around them. You should write spaces
X around the %o as well or the results are undefined.
X %o is for use in the specs for running the linker.
X Input files whose names have no recognized suffix are not compiled
X at all, but they are included among the output files, so they will
X be linked.
X %r substitutes the standard start file prefix.
X %p substitutes the standard macro predefinitions for the
X current target machine. Use this when running cpp.
X %P like %p, but puts `__' before and after the name of each macro.
X This is for ANSI C.
X %s current argument is the name of a library or startup file of some sort.
X Search for that file in a standard list of directories
X and substitute the full pathname found.
X %eSTR Print STR as an error message. STR is terminated by a newline.
X Use this when inconsistent options are detected.
X %a process ASM_SPEC as a spec.
X This allows config.h to specify part of the spec for running as.
X %l process LINK_SPEC as a spec.
X %L process LIB_SPEC as a spec.
X %S process STARTFILE_SPEC as a spec. A capital S is actually used here.
X %c process SIGNED_CHAR_SPEC as a spec.
X %C process CPP_SPEC as a spec. A capital C is actually used here.
X %1 process CC1_SPEC as a spec.
X %{S} substitutes the -S switch, if that switch was given to CC.
X If that switch was not specified, this substitutes nothing.
X Here S is a metasyntactic variable.
X %{S*} substitutes all the switches specified to CC whose names start
X with -S. This is used for -o, -D, -I, etc; switches that take
X arguments. CC considers `-o foo' as being one switch whose
X name starts with `o'. %{o*} would substitute this text,
X including the space; thus, two arguments would be generated.
X %{S:X} substitutes X, but only if the -S switch was given to CC.
X %{!S:X} substitutes X, but only if the -S switch was NOT given to CC.
X %{|S:X} like %{S:X}, but if no S switch, substitute `-'.
X %{|!S:X} like %{!S:X}, but if there is an S switch, substitute `-'.
X
XThe conditional text X in a %{S:X} or %{!S:X} construct may contain
Xother nested % constructs or spaces, or even newlines.
XThey are processed as usual, as described above.
X
XThe character | is used to indicate that a command should be piped to
Xthe following command, but only if -pipe is specified.
X
XNote that it is built into CC which switches take arguments and which
Xdo not. You might think it would be useful to generalize this to
Xallow each compiler's spec to say which switches take arguments. But
Xthis cannot be done in a consistent fashion. CC cannot even decide
Xwhich input files have been specified without knowing which switches
Xtake arguments, and it must know which input files to compile in order
Xto tell which compilers to run.
X
XCC also knows implicitly that arguments starting in `-l' are to
Xbe treated as compiler output files, and passed to the linker in their proper
Xposition among the other output files.
X
X*/
X
X#include
X#include
X#include
X#include
X
X#include "config.h"
X#include "obstack.h"
X#include "gvarargs.h"
X
X#ifdef USG
X#define R_OK 4
X#define W_OK 2
X#define X_OK 1
X#define vfork fork
X#endif /* USG */
X
X#define obstack_chunk_alloc xmalloc
X#define obstack_chunk_free free
Xextern int xmalloc ();
Xextern void free ();
X
X/* If a stage of compilation returns an exit status >= 1,
X compilation of that file ceases. */
X
X#define MIN_FATAL_STATUS 1
X
X/* This is the obstack which we use to allocate many strings. */
X
Xstruct obstack obstack;
X
Xchar *handle_braces ();
Xchar *save_string ();
Xchar *concat ();
Xint do_spec ();
Xint do_spec_1 ();
Xchar *find_file ();
Xstatic char *find_exec_file ();
Xvoid validate_switches ();
Xvoid validate_all_switches ();
Xvoid fancy_abort ();
X
X/* config.h can define ASM_SPEC to provide extra args to the assembler
X or extra switch-translations. */
X#ifndef ASM_SPEC
X#define ASM_SPEC ""
X#endif
X
X/* config.h can define CPP_SPEC to provide extra args to the C preprocessor
X or extra switch-translations. */
X#ifndef CPP_SPEC
X#define CPP_SPEC ""
X#endif
X
X/* config.h can define CC1_SPEC to provide extra args to cc1
X or extra switch-translations. */
X#ifndef CC1_SPEC
X#define CC1_SPEC ""
X#endif
X
X/* config.h can define LINK_SPEC to provide extra args to the linker
X or extra switch-translations. */
X#ifndef LINK_SPEC
X#define LINK_SPEC ""
X#endif
X
X/* config.h can define LIB_SPEC to override the default libraries. */
X#ifndef LIB_SPEC
X#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}"
X#endif
X
X/* config.h can define STARTFILE_SPEC to override the default crt0 files. */
X#ifndef STARTFILE_SPEC
X#define STARTFILE_SPEC \
X "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}"
X#endif
X
X/* This spec is used for telling cpp whether char is signed or not. */
X#define SIGNED_CHAR_SPEC \
X (DEFAULT_SIGNED_CHAR ? "%{funsigned-char:-D__CHAR_UNSIGNED__}" \
X : "%{!fsigned-char:-D__CHAR_UNSIGNED__}")
X
X/* This defines which switch letters take arguments. */
X
X#ifndef SWITCH_TAKES_ARG
X#define SWITCH_TAKES_ARG(CHAR) \
X ((CHAR) == 'D' || (CHAR) == 'U' || (CHAR) == 'o' \
X || (CHAR) == 'e' || (CHAR) == 'T' || (CHAR) == 'u' \
X || (CHAR) == 'I' || (CHAR) == 'Y' || (CHAR) == 'm' \
X || (CHAR) == 'L' || (CHAR) == 'i')
X#endif
X
X/* This defines which multi-letter switches take arguments. */
X
X#ifndef WORD_SWITCH_TAKES_ARG
X#define WORD_SWITCH_TAKES_ARG(STR) (!strcmp (STR, "Tdata"))
X#endif
X
X/* This structure says how to run one compiler, and when to do so. */
X
Xstruct compiler
X{
X char *suffix; /* Use this compiler for input files
X whose names end in this suffix. */
X char *spec; /* To use this compiler, pass this spec
X to do_spec. */
X};
X
X#define cplusplus_string \
X "cpp -+ %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{i*} \
X -undef -D__GNUC__ -D__GNUG__ -D__cplusplus %p %P\
X %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic}\
X %{Wcomment*} %{Wtrigraphs} %{Wall} %C\
X %i %{!M*:%{!E:%{!pipe:%g.cpp}}}%{E:%{o*}}%{M*:%{o*}} |\n\
X %{!M*:%{!E:cc1plus %{!pipe:%g.cpp} %1\
X %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{+e*} %{a}\
X %{g} %{g0} %{O} %{W*} %{w} %{pedantic} %{traditional}\
X %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p} %{xref}\
X %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
X %{S:%{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
X %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\
X %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\
X %{!pipe:%g.s}\n }}}"
X
X#define cplusplus_string1 \
X "cc1plus %i %1\
X %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{+e*} %{a}\
X %{g} %{g0} %{O} %{W*} %{w} %{pedantic} %{traditional}\
X %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p} %{xref}\
X %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
X %{S:%{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
X %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\
X %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\
X %{!pipe:%g.s}\n }"
X
X/* Here are the specs for compiling files with various known suffixes.
X A file that does not end in any of these suffixes will be passed
X unchanged to the loader and nothing else will be done to it. */
X
Xstruct compiler compilers[] =
X{
X {".c",
X#if 0
X "cpp %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{M*} %{i*} %{trigraphs} -undef \
X -D__GNUC__ %{ansi:-trigraphs -$ -D__STRICT_ANSI__} %{!ansi:%p} %P\
X %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic}\
X %{Wcomment*} %{Wtrigraphs} %{Wall} %C\
X %i %{!M*:%{!E:%{!pipe:%g.cpp}}}%{E:%{o*}}%{M*:%{o*}} |\n\
X %{!M*:%{!E:cc1 %{!pipe:%g.cpp} %1 \
X %{!Q:-quiet} -dumpbase %i %{Y*} %{d*} %{m*} %{f*} %{a}\
X %{g} %{O} %{W*} %{w} %{pedantic} %{ansi} %{traditional}\
X %{v:-version} %{gg:-symout %g.sym} %{pg:-p} %{p} %{xref}\
X %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
X %{S:%{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
X %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %{gg:-G %g.sym}\
X %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\
X %{!pipe:%g.s}\n }}}"
X#else
X cplusplus_string
X#endif
X },
X
X {".cc",
X cplusplus_string},
X {".C",
X cplusplus_string},
X {".cxx",
X cplusplus_string},
X
X {".i",
X cplusplus_string1},
X {".s",
X "%{!S:as %{R} %{j} %{J} %{h} %{d2} %a \
X %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o} %i\n }"},
X {".S",
X "cpp %{nostdinc} %{C} %{v} %{D*} %{U*} %{I*} %{i*} %{M*} %{trigraphs} \
X -undef -D__GNUC__ -$ %p %P\
X %c %{O:-D__OPTIMIZE__} %{traditional} %{pedantic}\
X %{Wcomment*} %{Wtrigraphs} %{Wall} %C\
X %i %{!M*:%{!E:%{!pipe:%g.s}}}%{E:%{o*}}%{M*:%{o*}} |\n\
X %{!M*:%{!E:%{!S:as %{R} %{j} %{J} %{h} %{d2} %a \
X %{c:%{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%b.o}\
X %{!pipe:%g.s}\n }}}"},
X /* Mark end of table */
X {0, 0}
X};
X
X#ifdef USE_COLLECT
X/* C++: Here is the spec for collecting global ctor and dtor
X requirements. */
Xchar *collect_spec =
X "%{!c:%{!M*:%{!E:%{!S:collect -o %g.s %g.R\n\
Xas %g.s -o %g.O\n\
Xld %{o*} %g.R %g.O\n\
X}}}}";
X
X/* Here is the spec for running the linker, after compiling all files. */
Xchar *link_spec = "%{!c:%{!M*:%{!E:%{!S:ld -r -o %g.R %l\
X %{A} %{d} %{e*} %{N} %{n} %{r} %{s} %{S} %{T*} %{t} %{u*} %{X} %{x} %{z}\
X %{y*} %{!nostdlib:%S} \
X %{L*} %o %{!nostdlib:-lg++ gnulib%s %{g:-lg} %L}\n }}}}";
X#else
X/* Here is the spec for running the linker, after compiling all files. */
Xchar *link_spec = "%{!c:%{!M*:%{!E:%{!S:ld %{o*} %l\
X %{A} %{d} %{e*} %{N} %{n} %{r} %{s} %{S} %{T*} %{t} %{u*} %{X} %{x} %{z}\
X %{y*} %{static:} %{!nostdlib:%S %{pg:gccrt0.o%s}%{!pg:ccrt0.o%s}} \
X %{L*} %o %{!nostdlib:-L%r -lg++ gnulib%s %{g:-lg} %L}\n }}}}";
X#endif
X
X/* Accumulate a command (program name and args), and run it. */
X
X/* Vector of pointers to arguments in the current line of specifications. */
X
Xchar **argbuf;
X
X/* Number of elements allocated in argbuf. */
X
Xint argbuf_length;
X
X/* Number of elements in argbuf currently in use (containing args). */
X
Xint argbuf_index;
X
X/* Number of commands executed so far. */
X
Xint execution_count;
X
X/* Flag indicating whether we should print the command and arguments */
X
Xunsigned char vflag;
X
X/* Name with which this program was invoked. */
X
Xchar *programname;
X
X/* User-specified -B prefix to attach to command names,
X or 0 if none specified. */
X
Xchar *user_exec_prefix = 0;
Xint user_exec_prefix_used = 0;
X
X/* Environment-specified prefix to attach to command names,
X or 0 if none specified. */
X
Xchar *env_exec_prefix = 0;
X
X/* Suffix to attach to directories searched for commands. */
X
Xchar *machine_suffix = 0;
X
X/* Default prefixes to attach to command names. */
X
X#ifndef STANDARD_EXEC_PREFIX
X#define STANDARD_EXEC_PREFIX "/usr/local/lib/gcc-"
X#endif /* !defined STANDARD_EXEC_PREFIX */
X
Xchar *standard_exec_prefix = STANDARD_EXEC_PREFIX;
Xchar *standard_exec_prefix_1 = "/usr/lib/gcc-";
X
X#ifndef STANDARD_STARTFILE_PREFIX
X#define STANDARD_STARTFILE_PREFIX "/usr/local/lib/"
X#endif /* !defined STANDARD_STARTFILE_PREFIX */
X
Xchar *standard_startfile_prefix = STANDARD_STARTFILE_PREFIX;
Xchar *standard_startfile_prefix_1 = "/lib/";
Xchar *standard_startfile_prefix_2 = "/usr/lib/";
X
X/* Clear out the vector of arguments (after a command is executed). */
X
Xvoid
Xclear_args ()
X{
X argbuf_index = 0;
X}
X
X/* Add one argument to the vector at the end.
X This is done when a space is seen or at the end of the line.
X If DELETE_ALWAYS is nonzero, the arg is a filename
X and the file should be deleted eventually.
X If DELETE_FAILURE is nonzero, the arg is a filename
X and the file should be deleted if this compilation fails. */
X
Xvoid
Xstore_arg (arg, delete_always, delete_failure)
X char *arg;
X int delete_always, delete_failure;
X{
X if (argbuf_index + 1 == argbuf_length)
X {
X argbuf = (char **) realloc (argbuf, (argbuf_length *= 2) * sizeof (char *));
X }
X
X argbuf[argbuf_index++] = arg;
X argbuf[argbuf_index] = 0;
X
X if (delete_always || delete_failure)
X record_temp_file (arg, delete_always, delete_failure);
X}
X
X/* Record the names of temporary files we tell compilers to write,
X and delete them at the end of the run. */
X
X/* This is the common prefix we use to make temp file names.
X It is chosen once for each run of this program.
X It is substituted into a spec by %g.
X Thus, all temp file names contain this prefix.
X In practice, all temp file names start with this prefix.
X
X This prefix comes from the envvar TMPDIR if it is defined;
X otherwise, from the P_tmpdir macro if that is defined;
X otherwise, in /usr/tmp or /tmp. */
X
Xchar *temp_filename;
X
X/* Length of the prefix. */
X
Xint temp_filename_length;
X
X/* Define the list of temporary files to delete. */
X
Xstruct temp_file
X{
X char *name;
X struct temp_file *next;
X};
X
X/* Queue of files to delete on success or failure of compilation. */
Xstruct temp_file *always_delete_queue;
X/* Queue of files to delete on failure of compilation. */
Xstruct temp_file *failure_delete_queue;
X
X/* Record FILENAME as a file to be deleted automatically.
X ALWAYS_DELETE nonzero means delete it if all compilation succeeds;
X otherwise delete it in any case.
X FAIL_DELETE nonzero means delete it if a compilation step fails;
X otherwise delete it in any case. */
X
Xvoid
Xrecord_temp_file (filename, always_delete, fail_delete)
X char *filename;
X int always_delete;
X int fail_delete;
X{
X register char *name;
X name = (char *) xmalloc (strlen (filename) + 1);
X strcpy (name, filename);
X
X if (always_delete)
X {
X register struct temp_file *temp;
X for (temp = always_delete_queue; temp; temp = temp->next)
X if (! strcmp (name, temp->name))
X goto already1;
X temp = (struct temp_file *) xmalloc (sizeof (struct temp_file));
X temp->next = always_delete_queue;
X temp->name = name;
X always_delete_queue = temp;
X already1:;
X }
X
X if (fail_delete)
X {
X register struct temp_file *temp;
X for (temp = failure_delete_queue; temp; temp = temp->next)
X if (! strcmp (name, temp->name))
X goto already2;
X temp = (struct temp_file *) xmalloc (sizeof (struct temp_file));
X temp->next = failure_delete_queue;
X temp->name = name;
X failure_delete_queue = temp;
X already2:;
X }
X}
X
X/* Delete all the temporary files whose names we previously recorded. */
X
Xvoid
Xdelete_temp_files ()
X{
X register struct temp_file *temp;
X
X for (temp = always_delete_queue; temp; temp = temp->next)
X {
X#ifdef DEBUG
X int i;
X printf ("Delete %s? (y or n) ", temp->name);
X fflush (stdout);
X i = getchar ();
X if (i != '\n')
X while (getchar () != '\n') ;
X if (i == 'y' || i == 'Y')
X#endif /* DEBUG */
X {
X if (unlink (temp->name) < 0)
X if (vflag)
X perror_with_name (temp->name);
X }
X }
X
X always_delete_queue = 0;
X}
X
X/* Delete all the files to be deleted on error. */
X
Xvoid
Xdelete_failure_queue ()
X{
X register struct temp_file *temp;
X
X for (temp = failure_delete_queue; temp; temp = temp->next)
X {
X#ifdef DEBUG
X int i;
X printf ("Delete %s? (y or n) ", temp->name);
X fflush (stdout);
X i = getchar ();
X if (i != '\n')
X while (getchar () != '\n') ;
X if (i == 'y' || i == 'Y')
X#endif /* DEBUG */
X {
X if (unlink (temp->name) < 0)
X if (vflag)
X perror_with_name (temp->name);
X }
X }
X}
X
Xvoid
Xclear_failure_queue ()
X{
X failure_delete_queue = 0;
X}
X
X/* Compute a string to use as the base of all temporary file names.
X It is substituted for %g. */
X
Xvoid
Xchoose_temp_base ()
X{
X extern char *getenv ();
X char *base = getenv ("TMPDIR");
X int len;
X
X if (base == (char *)0)
X {
X#ifdef P_tmpdir
X if (access (P_tmpdir, R_OK | W_OK) == 0)
X base = P_tmpdir;
X#endif
X if (base == (char *)0)
X {
X if (access ("/usr/tmp", R_OK | W_OK) == 0)
X base = "/usr/tmp/";
X else
X base = "/tmp/";
X }
X }
X
X len = strlen (base);
X temp_filename = (char *) xmalloc (len + sizeof("/ccXXXXXX"));
X strcpy (temp_filename, base);
X if (len > 0 && temp_filename[len-1] != '/')
X temp_filename[len++] = '/';
X strcpy (temp_filename + len, "ccXXXXXX");
X
X mktemp (temp_filename);
X temp_filename_length = strlen (temp_filename);
X}
X
X/* Search for an execute file through our search path.
X Return 0 if not found, otherwise return its name, allocated with malloc. */
X
Xstatic char *
Xfind_exec_file (prog)
X char *prog;
X{
X int win = 0;
X char *temp;
X int size;
X
X size = strlen (standard_exec_prefix);
X if (user_exec_prefix != 0 && strlen (user_exec_prefix) > size)
X size = strlen (user_exec_prefix);
X if (env_exec_prefix != 0 && strlen (env_exec_prefix) > size)
X size = strlen (env_exec_prefix);
X if (strlen (standard_exec_prefix_1) > size)
X size = strlen (standard_exec_prefix_1);
X size += strlen (prog) + 1;
X if (machine_suffix)
X size += strlen (machine_suffix) + 1;
X temp = (char *) xmalloc (size);
X
X /* Determine the filename to execute. */
X
X if (user_exec_prefix)
X {
X if (machine_suffix)
X {
X strcpy (temp, user_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, user_exec_prefix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X user_exec_prefix_used |= win;
X }
X
X if (!win && env_exec_prefix)
X {
X if (machine_suffix)
X {
X strcpy (temp, env_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, env_exec_prefix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_exec_prefix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_exec_prefix_1);
X strcat (temp, machine_suffix);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_exec_prefix_1);
X strcat (temp, prog);
X win = (access (temp, X_OK) == 0);
X }
X }
X
X if (win)
X return temp;
X else
X return 0;
X}
X
X/* stdin file number. */
X#define STDIN_FILE_NO 0
X
X/* stdout file number. */
X#define STDOUT_FILE_NO 1
X
X/* value of `pipe': port index for reading. */
X#define READ_PORT 0
X
X/* value of `pipe': port index for writing. */
X#define WRITE_PORT 1
X
X/* Pipe waiting from last process, to be used as input for the next one.
X Value is STDIN_FILE_NO if no pipe is waiting
X (i.e. the next command is the first of a group). */
X
Xint last_pipe_input;
X
X/* Fork one piped subcommand. FUNC is the system call to use
X (either execv or execvp). ARGV is the arg vector to use.
X NOT_LAST is nonzero if this is not the last subcommand
X (i.e. its output should be piped to the next one.) */
X
Xstatic int
Xpexecute (func, program, argv, not_last)
X char *program;
X int (*func)();
X char *argv[];
X int not_last;
X{
X int pid;
X int pdes[2];
X int input_desc = last_pipe_input;
X int output_desc = STDOUT_FILE_NO;
X
X /* If this isn't the last process, make a pipe for its output,
X and record it as waiting to be the input to the next process. */
X
X if (not_last)
X {
X if (pipe (pdes) < 0)
X pfatal_with_name ("pipe");
X output_desc = pdes[WRITE_PORT];
X last_pipe_input = pdes[READ_PORT];
X }
X else
X last_pipe_input = STDIN_FILE_NO;
X
X pid = vfork ();
X
X switch (pid)
X {
X case -1:
X pfatal_with_name ("vfork");
X break;
X
X case 0: /* child */
X /* Move the input and output pipes into place, if nec. */
X if (input_desc != STDIN_FILE_NO)
X {
X close (STDIN_FILE_NO);
X dup (input_desc);
X close (input_desc);
X }
X if (output_desc != STDOUT_FILE_NO)
X {
X close (STDOUT_FILE_NO);
X dup (output_desc);
X close (output_desc);
X }
X
X /* Close the parent's descs that aren't wanted here. */
X if (last_pipe_input != STDIN_FILE_NO)
X close (last_pipe_input);
X
X /* Exec the program. */
X (*func) (program, argv);
X perror_exec (program);
X exit (-1);
X /* NOTREACHED */
X
X default:
X /* In the parent, after forking.
X Close the descriptors that we made for this child. */
X if (input_desc != STDIN_FILE_NO)
X close (input_desc);
X if (output_desc != STDOUT_FILE_NO)
X close (output_desc);
X
X /* Return child's process number. */
X return pid;
X }
X}
X
X/* Execute the command specified by the arguments on the current line of spec.
X When using pipes, this includes several piped-together commands
X with `|' between them.
X
X Return 0 if successful, -1 if failed. */
X
Xint
Xexecute ()
X{
X int i;
X int n_commands; /* # of command. */
X char *string;
X struct command
X {
X char *prog; /* program name. */
X char **argv; /* vector of args. */
X int pid; /* pid of process for this command. */
X };
X
X struct command *commands; /* each command buffer with above info. */
X
X /* Count # of piped commands. */
X for (n_commands = 1, i = 0; i < argbuf_index; i++)
X if (strcmp (argbuf[i], "|") == 0)
X n_commands++;
X
X /* Get storage for each command. */
X commands
X = (struct command *) alloca (n_commands * sizeof (struct command));
X
X /* Split argbuf into its separate piped processes,
X and record info about each one.
X Also search for the programs that are to be run. */
X
X commands[0].prog = argbuf[0]; /* first command. */
X commands[0].argv = &argbuf[0];
X string = find_exec_file (commands[0].prog);
X if (string)
X commands[0].argv[0] = string;
X
X for (n_commands = 1, i = 0; i < argbuf_index; i++)
X if (strcmp (argbuf[i], "|") == 0)
X { /* each command. */
X argbuf[i] = 0; /* termination of command args. */
X commands[n_commands].prog = argbuf[i + 1];
X commands[n_commands].argv = &argbuf[i + 1];
X string = find_exec_file (commands[n_commands].prog);
X if (string)
X commands[n_commands].argv[0] = string;
X n_commands++;
X }
X
X argbuf[argbuf_index] = 0;
X
X /* If -v, print what we are about to do, and maybe query. */
X
X if (vflag)
X {
X /* Print each piped command as a separate line. */
X for (i = 0; i < n_commands ; i++)
X {
X char **j;
X
X for (j = commands[i].argv; *j; j++)
X fprintf (stderr, " %s", *j);
X
X /* Print a pipe symbol after all but the last command. */
X if (i + 1 != n_commands)
X fprintf (stderr, " |");
X fprintf (stderr, "\n");
X }
X fflush (stderr);
X#ifdef DEBUG
X fprintf (stderr, "\nGo ahead? (y or n) ");
X fflush (stderr);
X j = getchar ();
X if (j != '\n')
X while (getchar () != '\n') ;
X if (j != 'y' && j != 'Y')
X return 0;
X#endif /* DEBUG */
X }
X
X /* Run each piped subprocess. */
X
X last_pipe_input = STDIN_FILE_NO;
X for (i = 0; i < n_commands; i++)
X {
X extern int execv(), execvp();
X char *string = commands[i].argv[0];
X
X commands[i].pid = pexecute ((string != commands[i].prog ? execv : execvp),
X string, commands[i].argv,
X i + 1 < n_commands);
X
X if (string != commands[i].prog)
X free (string);
X }
X
X execution_count++;
X
X /* Wait for all the subprocesses to finish.
X We don't care what order they finish in;
X we know that N_COMMANDS waits will get them all. */
X
X {
X int ret_code = 0;
X
X for (i = 0; i < n_commands; i++)
X {
X int status;
X int pid;
X char *prog;
X
X pid = wait (&status);
X if (pid < 0)
X abort ();
X
X if (status != 0)
X {
X int j;
X for (j = 0; j < n_commands; j++)
X if (commands[j].pid == pid)
X prog = commands[j].prog;
X
X if ((status & 0x7F) != 0)
X fatal ("Program %s got fatal signal %d.", prog, (status & 0x7F));
X if (((status & 0xFF00) >> 8) >= MIN_FATAL_STATUS)
X ret_code = -1;
X }
X }
X return ret_code;
X }
X}
X
X/* Find all the switches given to us
X and make a vector describing them.
X
X The elements of the vector a strings, one per switch given.
X If a switch uses the following argument, then the `part1' field
X is the switch itself and the `part2' field is the following argument.
X The `valid' field is nonzero if any spec has looked at this switch;
X if it remains zero at the end of the run, it must be meaningless.
X
X `prefix' is the prefix of this switch which makes it
X recognizable as a switch. This is almost always "-". When
X it is NULL, it means that the prefix to this switch is part
X `part1' instead. */
X
Xstruct switchstr
X{
X char *prefix;
X char *part1;
X char *part2;
X int valid;
X};
X
Xstruct switchstr *switches;
X
Xint n_switches;
X
X/* Also a vector of input files specified. */
X
Xchar **infiles;
X
Xint n_infiles;
X
X/* And a vector of corresponding output files is made up later. */
X
Xchar **outfiles;
X
X/* Create the vector `switches' and its contents.
X Store its length in `n_switches'. */
X
Xvoid
Xprocess_command (argc, argv)
X int argc;
X char **argv;
X{
X extern char *getenv ();
X register int i;
X n_switches = 0;
X n_infiles = 0;
X
X env_exec_prefix = getenv ("GCC_EXEC_PREFIX");
X
X /* Scan argv twice. Here, the first time, just count how many switches
X there will be in their vector, and how many input files in theirs.
X Here we also parse the switches that cc itself uses (e.g. -v). */
X
X for (i = 1; i < argc; i++)
X {
X if (argv[i][0] == '-' && argv[i][1] != 'l')
X {
X register char *p = &argv[i][1];
X register int c = *p;
X
X switch (c)
X {
X case 'b':
X machine_suffix = p + 1;
X break;
X
X case 'B':
X user_exec_prefix = p + 1;
X break;
X
X case 'v': /* Print our subcommands and print versions. */
X vflag++;
X n_switches++;
X break;
X
X default:
X n_switches++;
X
X if (SWITCH_TAKES_ARG (c) && p[1] == 0)
X i++;
X else if (WORD_SWITCH_TAKES_ARG (p))
X i++;
X }
X }
X else if (argv[i][0] == '+')
X ;
X else
X n_infiles++;
X }
X
X /* Then create the space for the vectors and scan again. */
X
X switches = ((struct switchstr *)
X xmalloc ((n_switches + 1) * sizeof (struct switchstr)));
X infiles = (char **) xmalloc ((n_infiles + 1) * sizeof (char *));
X n_switches = 0;
X n_infiles = 0;
X
X /* This, time, copy the text of each switch and store a pointer
X to the copy in the vector of switches.
X Store all the infiles in their vector. */
X
X for (i = 1; i < argc; i++)
X {
X if (argv[i][0] == '-' && argv[i][1] != 'l')
X {
X register char *p = &argv[i][1];
X register int c = *p;
X
X if (c == 'B' || c == 'b')
X continue;
X switches[n_switches].prefix = "-";
X switches[n_switches].part1 = p;
X if ((SWITCH_TAKES_ARG (c) && p[1] == 0)
X || WORD_SWITCH_TAKES_ARG (p))
X switches[n_switches].part2 = argv[++i];
X else
X switches[n_switches].part2 = 0;
X switches[n_switches].valid = 0;
X n_switches++;
X }
X else if (argv[i][0] == '+')
X {
X switches[n_switches].prefix = "";
X switches[n_switches].part1 = argv[i];
X switches[n_switches].part2 = 0;
X switches[n_switches].valid = 0;
X n_switches++;
X }
X else
X infiles[n_infiles++] = argv[i];
X }
X
X switches[n_switches].part1 = 0;
X infiles[n_infiles] = 0;
X}
X
X/* Process a spec string, accumulating and running commands. */
X
X/* These variables describe the input file name.
X input_file_number is the index on outfiles of this file,
X so that the output file name can be stored for later use by %o.
X input_basename is the start of the part of the input file
X sans all directory names, and basename_length is the number
X of characters starting there excluding the suffix .c or whatever. */
X
Xchar *input_filename;
Xint input_file_number;
Xint input_filename_length;
Xint basename_length;
Xchar *input_basename;
X
X/* These are variables used within do_spec and do_spec_1. */
X
X/* Nonzero if an arg has been started and not yet terminated
X (with space, tab or newline). */
Xint arg_going;
X
X/* Nonzero means %d or %g has been seen; the next arg to be terminated
X is a temporary file name. */
Xint delete_this_arg;
X
X/* Nonzero means %w has been seen; the next arg to be terminated
X is the output file name of this compilation. */
Xint this_is_output_file;
X
X/* Nonzero means %s has been seen; the next arg to be terminated
X is the name of a library file and we should try the standard
X search dirs for it. */
Xint this_is_library_file;
X
X/* Process the spec SPEC and run the commands specified therein.
X Returns 0 if the spec is successfully processed; -1 if failed. */
X
Xint
Xdo_spec (spec)
X char *spec;
X{
X int value;
X
X clear_args ();
X arg_going = 0;
X delete_this_arg = 0;
X this_is_output_file = 0;
X this_is_library_file = 0;
X
X value = do_spec_1 (spec, 0);
X
X /* Force out any unfinished command.
X If -pipe, this forces out the last command if it ended in `|'. */
X if (value == 0)
X {
X if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|"))
X argbuf_index--;
X
X if (argbuf_index > 0)
X value = execute ();
X }
X
X return value;
X}
X
X/* Process the sub-spec SPEC as a portion of a larger spec.
X This is like processing a whole spec except that we do
X not initialize at the beginning and we do not supply a
X newline by default at the end.
X INSWITCH nonzero means don't process %-sequences in SPEC;
X in this case, % is treated as an ordinary character.
X This is used while substituting switches.
X INSWITCH nonzero also causes SPC not to terminate an argument.
X
X Value is zero unless a line was finished
X and the command on that line reported an error. */
X
Xint
Xdo_spec_1 (spec, inswitch)
X char *spec;
X int inswitch;
X{
X register char *p = spec;
X register int c;
X char *string;
X
X while (c = *p++)
X /* If substituting a switch, treat all chars like letters.
X Otherwise, NL, SPC, TAB and % are special. */
X switch (inswitch ? 'a' : c)
X {
X case '\n':
X /* End of line: finish any pending argument,
X then run the pending command if one has been started. */
X if (arg_going)
X {
X obstack_1grow (&obstack, 0);
X string = obstack_finish (&obstack);
X if (this_is_library_file)
X string = find_file (string);
X store_arg (string, delete_this_arg, this_is_output_file);
X if (this_is_output_file)
X outfiles[input_file_number] = string;
X }
X arg_going = 0;
X
X if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|"))
X {
X int i;
X for (i = 0; i < n_switches; i++)
X if (!strcmp (switches[i].part1, "pipe"))
X break;
X
X /* A `|' before the newline means use a pipe here,
X but only if -pipe was specified.
X Otherwise, execute now and don't pass the `|' as an arg. */
X if (i < n_switches)
X {
X switches[i].valid = 1;
X break;
X }
X else
X argbuf_index--;
X }
X
X if (argbuf_index > 0)
X {
X int value = execute ();
X if (value)
X return value;
X }
X /* Reinitialize for a new command, and for a new argument. */
X clear_args ();
X arg_going = 0;
X delete_this_arg = 0;
X this_is_output_file = 0;
X this_is_library_file = 0;
X break;
X
X case '|':
X /* End any pending argument. */
X if (arg_going)
X {
X obstack_1grow (&obstack, 0);
X string = obstack_finish (&obstack);
X if (this_is_library_file)
X string = find_file (string);
X store_arg (string, delete_this_arg, this_is_output_file);
X if (this_is_output_file)
X outfiles[input_file_number] = string;
X }
X
X /* Use pipe */
X obstack_1grow (&obstack, c);
X arg_going = 1;
X break;
X
X case '\t':
X case ' ':
X /* Space or tab ends an argument if one is pending. */
X if (arg_going)
X {
X obstack_1grow (&obstack, 0);
X string = obstack_finish (&obstack);
X if (this_is_library_file)
X string = find_file (string);
X store_arg (string, delete_this_arg, this_is_output_file);
X if (this_is_output_file)
X outfiles[input_file_number] = string;
X }
X /* Reinitialize for a new argument. */
X arg_going = 0;
X delete_this_arg = 0;
X this_is_output_file = 0;
X this_is_library_file = 0;
X break;
X
X case '%':
X switch (c = *p++)
X {
X case 0:
X fatal ("Invalid specification! Bug in cc.");
X
X case 'b':
X obstack_grow (&obstack, input_basename, basename_length);
X arg_going = 1;
X break;
X
X case 'd':
X delete_this_arg = 2;
X break;
X
X case 'e':
X /* {...:%efoo} means report an error with `foo' as error message
X and don't execute any more commands for this file. */
X {
X char *q = p;
X char *buf;
X while (*p != 0 && *p != '\n') p++;
X buf = (char *) alloca (p - q + 1);
X strncpy (buf, q, p - q);
X error ("%s", buf);
X return -1;
X }
X break;
X
X case 'g':
X obstack_grow (&obstack, temp_filename, temp_filename_length);
X delete_this_arg = 1;
X arg_going = 1;
X break;
X
X case 'i':
X obstack_grow (&obstack, input_filename, input_filename_length);
X arg_going = 1;
X break;
X
X case 'o':
X {
X register int f;
X for (f = 0; f < n_infiles; f++)
X store_arg (outfiles[f], 0, 0);
X }
X break;
X
X
X case 'r':
X obstack_grow (&obstack, standard_startfile_prefix,
X strlen(standard_startfile_prefix));
X break;
X
X
X case 's':
X this_is_library_file = 1;
X break;
X
X case 'w':
X this_is_output_file = 1;
X break;
X
X case '{':
X p = handle_braces (p);
X if (p == 0)
X return -1;
X break;
X
X case '%':
X obstack_1grow (&obstack, '%');
X break;
X
X/*** The rest just process a certain constant string as a spec. */
X
X case '1':
X do_spec_1 (CC1_SPEC, 0);
X break;
X
X case 'a':
X do_spec_1 (ASM_SPEC, 0);
X break;
X
X case 'c':
X do_spec_1 (SIGNED_CHAR_SPEC, 0);
X break;
X
X case 'C':
X do_spec_1 (CPP_SPEC, 0);
X break;
X
X case 'l':
X do_spec_1 (LINK_SPEC, 0);
X break;
X
X case 'L':
X do_spec_1 (LIB_SPEC, 0);
X break;
X
X case 'p':
X do_spec_1 (CPP_PREDEFINES, 0);
X break;
X
X case 'P':
X {
X char *x = (char *) alloca (strlen (CPP_PREDEFINES) * 2 + 1);
X char *buf = x;
X char *y = CPP_PREDEFINES;
X
X /* Copy all of CPP_PREDEFINES into BUF,
X but put __ after every -D and at the end of each arg, */
X while (1)
X {
X if (! strncmp (y, "-D", 2))
X {
X *x++ = '-';
X *x++ = 'D';
X *x++ = '_';
X *x++ = '_';
X y += 2;
X }
X else if (*y == ' ' || *y == 0)
X {
X *x++ = '_';
X *x++ = '_';
X if (*y == 0)
X break;
X else
X *x++ = *y++;
X }
X else
X *x++ = *y++;
X }
X *x = 0;
X
X do_spec_1 (buf, 0);
X }
X break;
X
X case 'S':
X do_spec_1 (STARTFILE_SPEC, 0);
X break;
X
X default:
X abort ();
X }
X break;
X
X default:
X /* Ordinary character: put it into the current argument. */
X obstack_1grow (&obstack, c);
X arg_going = 1;
X }
X
X return 0; /* End of string */
X}
X
X/* Return 0 if we call do_spec_1 and that returns -1. */
X
Xchar *
Xhandle_braces (p)
X register char *p;
X{
X register char *q;
X char *filter;
X int pipe = 0;
X int negate = 0;
X
X if (*p == '|')
X /* A `|' after the open-brace means,
X if the test fails, output a single minus sign rather than nothing.
X This is used in %{|!pipe:...}. */
X pipe = 1, ++p;
X
X if (*p == '!')
X /* A `!' after the open-brace negates the condition:
X succeed if the specified switch is not present. */
X negate = 1, ++p;
X
X filter = p;
X while (*p != ':' && *p != '}') p++;
X if (*p != '}')
X {
X register int count = 1;
X q = p + 1;
X while (count > 0)
X {
X if (*q == '{')
X count++;
X else if (*q == '}')
X count--;
X else if (*q == 0)
X abort ();
X q++;
X }
X }
X else
X q = p + 1;
X
X if (p[-1] == '*' && p[0] == '}')
X {
X /* Substitute all matching switches as separate args. */
X register int i;
X --p;
X for (i = 0; i < n_switches; i++)
X if (!strncmp (switches[i].part1, filter, p - filter))
X give_switch (i);
X }
X else
X {
X /* Test for presence of the specified switch. */
X register int i;
X int present = 0;
X
X /* If name specified ends in *, as in {x*:...},
X check for presence of any switch name starting with x. */
X if (p[-1] == '*')
X {
X for (i = 0; i < n_switches; i++)
X {
X if (!strncmp (switches[i].part1, filter, p - filter - 1))
X {
X switches[i].valid = 1;
X present = 1;
X }
X }
X }
X /* Otherwise, check for presence of exact name specified. */
X else
X {
X for (i = 0; i < n_switches; i++)
X {
X if (!strncmp (switches[i].part1, filter, p - filter)
X && switches[i].part1[p - filter] == 0)
X {
X switches[i].valid = 1;
X present = 1;
X break;
X }
X }
X }
X
X /* If it is as desired (present for %{s...}, absent for %{-s...})
X then substitute either the switch or the specified
X conditional text. */
X if (present != negate)
X {
X if (*p == '}')
X {
X give_switch (i);
X }
X else
X {
X if (do_spec_1 (save_string (p + 1, q - p - 2), 0) < 0)
X return 0;
X }
X }
X else if (pipe)
X {
X /* Here if a %{|...} conditional fails: output a minus sign,
X which means "standard output" or "standard input". */
X do_spec_1 ("-", 0);
X }
X }
X
X return q;
X}
X
X/* Pass a switch to the current accumulating command
X in the same form that we received it.
X SWITCHNUM identifies the switch; it is an index into
X the vector of switches gcc received, which is `switches'.
X This cannot fail since it never finishes a command line. */
X
Xgive_switch (switchnum)
X int switchnum;
X{
X do_spec_1 (switches[switchnum].prefix, 1);
X do_spec_1 (switches[switchnum].part1, 1);
X do_spec_1 (" ", 0);
X if (switches[switchnum].part2 != 0)
X {
X do_spec_1 (switches[switchnum].part2, 1);
X do_spec_1 (" ", 0);
X }
X switches[switchnum].valid = 1;
X}
X
X/* Search for a file named NAME trying various prefixes including the
X user's -B prefix and some standard ones.
X Return the absolute pathname found. If nothing is found, return NAME. */
X
Xchar *
Xfind_file (name)
X char *name;
X{
X int size;
X char *temp;
X int win = 0;
X
X /* Compute maximum size of NAME plus any prefix we will try. */
X
X size = strlen (standard_exec_prefix);
X if (user_exec_prefix != 0 && strlen (user_exec_prefix) > size)
X size = strlen (user_exec_prefix);
X if (env_exec_prefix != 0 && strlen (env_exec_prefix) > size)
X size = strlen (env_exec_prefix);
X if (strlen (standard_exec_prefix) > size)
X size = strlen (standard_exec_prefix);
X if (strlen (standard_exec_prefix_1) > size)
X size = strlen (standard_exec_prefix_1);
X if (strlen (standard_startfile_prefix) > size)
X size = strlen (standard_startfile_prefix);
X if (strlen (standard_startfile_prefix_1) > size)
X size = strlen (standard_startfile_prefix_1);
X if (strlen (standard_startfile_prefix_2) > size)
X size = strlen (standard_startfile_prefix_2);
X if (machine_suffix)
X size += strlen (machine_suffix) + 1;
X size += strlen (name) + 1;
X
X temp = (char *) alloca (size);
X
X if (user_exec_prefix)
X {
X if (machine_suffix)
X {
X strcpy (temp, user_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, user_exec_prefix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win && env_exec_prefix)
X {
X if (machine_suffix)
X {
X strcpy (temp, env_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, env_exec_prefix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_exec_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_exec_prefix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_exec_prefix_1);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_exec_prefix_1);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_startfile_prefix);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_startfile_prefix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_startfile_prefix_1);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_startfile_prefix_1);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, standard_startfile_prefix_2);
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, standard_startfile_prefix_2);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (!win)
X {
X if (machine_suffix)
X {
X strcpy (temp, "./");
X strcat (temp, machine_suffix);
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X if (!win)
X {
X strcpy (temp, "./");
X strcat (temp, name);
X win = (access (temp, R_OK) == 0);
X }
X }
X
X if (win)
X return save_string (temp, strlen (temp));
X return name;
X}
X
X/* On fatal signals, delete all the temporary files. */
X
Xvoid
Xfatal_error (signum)
X int signum;
X{
X signal (signum, SIG_DFL);
X delete_failure_queue ();
X delete_temp_files ();
X /* Get the same signal again, this time not handled,
X so its normal effect occurs. */
X kill (getpid (), signum);
X}
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X register int i;
X int value;
X int error_count = 0;
X int linker_was_run = 0;
X char *explicit_link_files;
X
X programname = argv[0];
X
X if (signal (SIGINT, SIG_IGN) != SIG_IGN)
X signal (SIGINT, fatal_error);
X if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
X signal (SIGHUP, fatal_error);
X if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
X signal (SIGTERM, fatal_error);
X
X argbuf_length = 10;
X argbuf = (char **) xmalloc (argbuf_length * sizeof (char *));
X
X obstack_init (&obstack);
X
X choose_temp_base ();
X
X /* Make a table of what switches there are (switches, n_switches).
X Make a table of specified input files (infiles, n_infiles). */
X
X process_command (argc, argv);
X
X if (vflag)
X {
X extern char *version_string;
X fprintf (stderr, "g++ version %s\n", version_string);
X if (n_infiles == 0)
X exit (0);
X }
X
X if (n_infiles == 0)
X fatal ("No input files specified.");
X
X /* Make a place to record the compiler output file names
X that correspond to the input files. */
X
X outfiles = (char **) xmalloc (n_infiles * sizeof (char *));
X bzero (outfiles, n_infiles * sizeof (char *));
X
X /* Record which files were specified explicitly as link input. */
X
X explicit_link_files = (char *) xmalloc (n_infiles);
X bzero (explicit_link_files, n_infiles);
X
X for (i = 0; i < n_infiles; i++)
X {
X register struct compiler *cp;
X int this_file_error = 0;
X
X /* Tell do_spec what to substitute for %i. */
X
X input_filename = infiles[i];
X input_filename_length = strlen (input_filename);
X input_file_number = i;
X
X /* Use the same thing in %o, unless cp->spec says otherwise. */
X
X outfiles[i] = input_filename;
X
X /* Figure out which compiler from the file's suffix. */
X
X for (cp = compilers; cp->spec; cp++)
X {
X if (strlen (cp->suffix) < input_filename_length
X && !strcmp (cp->suffix,
X infiles[i] + input_filename_length
X - strlen (cp->suffix)))
X {
X /* Ok, we found an applicable compiler. Run its spec. */
X /* First say how much of input_filename to substitute for %b */
X register char *p;
X
X input_basename = input_filename;
X for (p = input_filename; *p; p++)
X if (*p == '/')
X input_basename = p + 1;
X basename_length = (input_filename_length - strlen (cp->suffix)
X - (input_basename - input_filename));
X value = do_spec (cp->spec);
X if (value < 0)
X this_file_error = 1;
X else
X if (user_exec_prefix != 0
X && user_exec_prefix_used == 0)
X fprintf (stderr, "`-B%s' not used by compiler\n", user_exec_prefix);
X break;
X }
X }
X
X /* If this file's name does not contain a recognized suffix,
X record it as explicit linker input. */
X
X if (! cp->spec)
X explicit_link_files[i] = 1;
X
X /* Clear the delete-on-failure queue, deleting the files in it
X if this compilation failed. */
X
X if (this_file_error)
X {
X delete_failure_queue ();
X error_count++;
X }
X /* If this compilation succeeded, don't delete those files later. */
X clear_failure_queue ();
X }
X
X /* Run ld to link all the compiler output files. */
X
X if (error_count == 0)
X {
X int tmp = execution_count;
X value = do_spec (link_spec);
X if (value < 0)
X error_count = 1;
X linker_was_run = (tmp != execution_count);
X }
X
X#ifdef USE_COLLECT
X /* C++: now collect all the requirements for
X calling global constructors and destructors,
X and write them to a file. We will link this file
X in when we run. */
X if (linker_was_run && ! error_count)
X {
X int tmp = execution_count;
X value = do_spec (collect_spec);
X if (value < 0)
X error_count = 1;
X linker_was_run &= (tmp != execution_count);
X }
X#endif
X
X /* If options said don't run linker,
X complain about input files to be given to the linker. */
X
X if (! linker_was_run && error_count == 0)
X for (i = 0; i < n_infiles; i++)
X if (explicit_link_files[i])
X error ("%s: linker input file unused since linking not done",
X outfiles[i]);
X
X /* Set the `valid' bits for switches that match anything in any spec. */
X
X validate_all_switches ();
X
X /* Warn about any switches that no pass was interested in. */
X
X for (i = 0; i < n_switches; i++)
X if (! switches[i].valid)
X error ("unrecognized option `%s%s'",
X switches[i].prefix, switches[i].part1);
X
X /* Delete some or all of the temporary files we made. */
X
X if (error_count)
X delete_failure_queue ();
X delete_temp_files ();
X
X exit (error_count);
X}
X
Xxmalloc (size)
X int size;
X{
X register int value = malloc (size);
X if (value == 0)
X fatal ("Virtual memory full.");
X return value;
X}
X
Xxrealloc (ptr, size)
X int ptr, size;
X{
X register int value = realloc (ptr, size);
X if (value == 0)
X fatal ("Virtual memory full.");
X return value;
X}
X
X/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
X
Xchar *
Xconcat (s1, s2, s3)
X char *s1, *s2, *s3;
X{
X int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
X char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
X
X strcpy (result, s1);
X strcpy (result + len1, s2);
X strcpy (result + len1 + len2, s3);
X *(result + len1 + len2 + len3) = 0;
X
X return result;
X}
X
Xchar *
Xsave_string (s, len)
X char *s;
X int len;
X{
X register char *result = (char *) xmalloc (len + 1);
X
X bcopy (s, result, len);
X result[len] = 0;
X return result;
X}
X
Xpfatal_with_name (name)
X char *name;
X{
X extern int errno, sys_nerr;
X extern char *sys_errlist[];
X char *s;
X
X if (errno < sys_nerr)
X s = concat ("%s: ", sys_errlist[errno], "");
X else
X s = "cannot open %s";
X fatal (s, name);
X}
X
Xperror_with_name (name)
X char *name;
X{
X extern int errno, sys_nerr;
X extern char *sys_errlist[];
X char *s;
X
X if (errno < sys_nerr)
X s = concat ("%s: ", sys_errlist[errno], "");
X else
X s = "cannot open %s";
X error (s, name);
X}
X
Xperror_exec (name)
X char *name;
X{
X extern int errno, sys_nerr;
X extern char *sys_errlist[];
X char *s;
X
X if (errno < sys_nerr)
X s = concat ("installation problem, cannot exec %s: ",
X sys_errlist[errno], "");
X else
X s = "installation problem, cannot exec %s";
X error (s, name);
X}
X
X/* More 'friendly' abort that prints the line and file.
X config.h can #define abort fancy_abort if you like that sort of thing. */
X
Xvoid
Xfancy_abort ()
X{
X fatal ("Internal gcc abort.");
X}
X
X#ifdef HAVE_VPRINTF
X
X/* Output an error message and exit */
X
Xint
Xfatal (va_alist)
X va_dcl
X{
X va_list ap;
X char *format;
X
X va_start(ap);
X format = va_arg (ap, char *);
X vfprintf (stderr, format, ap);
X va_end (ap);
X fprintf (stderr, "\n");
X delete_temp_files (0);
X exit (1);
X}
X
Xerror (va_alist)
X va_dcl
X{
X va_list ap;
X char *format;
X
X va_start(ap);
X format = va_arg (ap, char *);
X fprintf (stderr, "%s: ", programname);
X vfprintf (stderr, format, ap);
X va_end (ap);
X
X fprintf (stderr, "\n");
X}
X
X#else /* not HAVE_VPRINTF */
X
Xfatal (msg, arg1, arg2)
X char *msg, *arg1, *arg2;
X{
X error (msg, arg1, arg2);
X delete_temp_files (0);
X exit (1);
X}
X
Xerror (msg, arg1, arg2)
X char *msg, *arg1, *arg2;
X{
X fprintf (stderr, "%s: ", programname);
X fprintf (stderr, msg, arg1, arg2);
X fprintf (stderr, "\n");
X}
X
X#endif /* not HAVE_VPRINTF */
X
X
Xvoid
Xvalidate_all_switches ()
X{
X struct compiler *comp;
X register char *p;
X register char c;
X
X for (comp = compilers; comp->spec; comp++)
X {
X p = comp->spec;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X }
X
X p = link_spec;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X
X /* Now notice switches mentioned in the machine-specific specs. */
X
X#ifdef ASM_SPEC
X p = ASM_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef CPP_SPEC
X p = CPP_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef SIGNED_CHAR_SPEC
X p = SIGNED_CHAR_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef CC1_SPEC
X p = CC1_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef LINK_SPEC
X p = LINK_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef LIB_SPEC
X p = LIB_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X
X#ifdef STARTFILE_SPEC
X p = STARTFILE_SPEC;
X while (c = *p++)
X if (c == '%' && *p == '{')
X /* We have a switch spec. */
X validate_switches (p + 1);
X#endif
X}
X
X/* Look at the switch-name that comes after START
X and mark as valid all supplied switches that match it. */
X
Xvoid
Xvalidate_switches (start)
X char *start;
X{
X register char *p = start;
X char *filter;
X register int i;
X
X if (*p == '|')
X ++p;
X
X if (*p == '!')
X ++p;
X
X filter = p;
X while (*p != ':' && *p != '}') p++;
X
X if (p[-1] == '*')
X {
X /* Mark all matching switches as valid. */
X --p;
X for (i = 0; i < n_switches; i++)
X if (!strncmp (switches[i].part1, filter, p - filter))
X switches[i].valid = 1;
X }
X else
X {
X /* Mark an exact matching switch as valid. */
X for (i = 0; i < n_switches; i++)
X {
X if (!strncmp (switches[i].part1, filter, p - filter)
X && switches[i].part1[p - filter] == 0)
X switches[i].valid = 1;
X }
X }
X}
!EOF
echo "Extracting stab.h..."
sed 's/^X//' >stab.h << '!EOF'
X#ifndef __GNU_STAB__
X
X/* Indicate the GNU stab.h is in use. */
X
X#define __GNU_STAB__
X
X#define __define_stab(NAME, CODE, STRING) NAME=CODE,
X
Xenum __stab_debug_code
X{
X#include "stab.def"
X};
X
X#undef __define_stab
X
X#endif /* __GNU_STAB_ */
!EOF
echo "Extracting ccrt0.c..."
sed 's/^X//' >ccrt0.c << '!EOF'
X/* Startup code needed by GCC C++ output code. */
X/* Compile this file with gcc (-fno-pic). */
X/* Copyright (C) 1991 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* As a special exception, if you link this file with files
X compiled with GCC to produce an executable, this does not cause
X the resulting executable to be covered by the GNU General Public License.
X This exception does not however invalidate any other reasons why
X the executable file might be covered by the GNU General Public License. */
X
X/* ccrt0.c: Runtime initialization/finalization for static data
X */
X
Xstatic char * rcsId = "$Id: ccrt0.c,v 1.1 1991/09/21 02:43:38 hgs Exp $";
X
X#include "init_main.c" /* That's right, .c */
X#include "init.c"
X#include "init_libs.c"
!EOF
echo "Extracting cplus-parse.y..."
sed 's/^X//' >cplus-parse.y << '!EOF'
X/* YACC parser for C++ syntax.
X Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* This grammar is based on the GNU CC grammar. */
X
X/* Also note: this version contains experimental exception
X handling features. They could break, change, disappear,
X or otherwise exhibit volatile behavior. Don't depend on
X me (Michael Tiemann) to protect you from any negative impact
X this may have on your professional, personal, or spiritual life. */
X
X%{
X#include "config.h"
X#include "tree.h"
X#include "input.h"
X#include "cplus-parse.h"
X#include "cplus-tree.h"
X#include "assert.h"
X
X/* C++ extensions */
Xextern tree ridpointers[]; /* need this up here */
Xextern tree void_list_node;
X
X#include
X#include
X
X#ifndef errno
Xextern int errno;
X#endif
X
Xextern int end_of_file;
X
Xvoid yyerror ();
X
X/* Contains error message to give if user tries to declare
X a variable where one does not belong. */
Xstatic char *stmt_decl_msg = 0;
X
X#ifndef YYDEBUG
X/* Cause the `yydebug' variable to be defined. */
Xint yydebug;
X#endif
X
X/* Cons up an empty parameter list. */
X#ifdef __GNU__
X__inline
X#endif
Xstatic tree
Xempty_parms ()
X{
X tree parms;
X
X if (strict_prototype)
X parms = void_list_node;
X else
X parms = NULL_TREE;
X return parms;
X}
X
Xvoid yyhook ();
X%}
X
X%start program
X
X%union {long itype; tree ttype; enum tree_code code; }
X
X/* All identifiers that are not reserved words
X and are not declared typedefs in the current block */
X%token IDENTIFIER
X
X/* All identifiers that are declared typedefs in the current block.
X In some contexts, they are treated just like IDENTIFIER,
X but they can also serve as typespecs in declarations. */
X%token TYPENAME
X
X/* Reserved words that specify storage class.
X yylval contains an IDENTIFIER_NODE which indicates which one. */
X%token SCSPEC
X
X/* Reserved words that specify type.
X yylval contains an IDENTIFIER_NODE which indicates which one. */
X%token TYPESPEC
X
X/* Reserved words that qualify type: "const" or "volatile".
X yylval contains an IDENTIFIER_NODE which indicates which one. */
X%token TYPE_QUAL
X
X/* Character or numeric constants.
X yylval is the node for the constant. */
X%token CONSTANT
X
X/* String constants in raw form.
X yylval is a STRING_CST node. */
X%token STRING
X
X/* "...", used for functions with variable arglists. */
X%token ELLIPSIS
X
X/* the reserved words */
X%token SIZEOF ENUM /* STRUCT UNION */ IF ELSE WHILE DO FOR SWITCH CASE DEFAULT
X%token BREAK CONTINUE RETURN GOTO ASM TYPEOF ALIGNOF
X%token ATTRIBUTE
X
X/* the reserved words... C++ extensions */
X%token AGGR
X%token DELETE NEW OVERLOAD PRIVATE PUBLIC PROTECTED THIS OPERATOR
X%token DYNAMIC POINTSAT_LEFT_RIGHT LEFT_RIGHT
X%token SCOPE
X
X/* Define the operator tokens and their precedences.
X The value is an integer because, if used, it is the tree code
X to use in the expression made from the operator. */
X
X%left EMPTY /* used to resolve s/r with epsilon */
X
X/* Add precedence rules to solve dangling else s/r conflict */
X%nonassoc IF
X%nonassoc ELSE
X
X%left IDENTIFIER TYPENAME TYPENAME_COLON SCSPEC TYPESPEC TYPE_QUAL ENUM AGGR
X
X%left '{' ','
X
X%right ASSIGN '='
X%right '?' ':' RANGE
X%left OROR
X%left ANDAND
X%left '|'
X%left '^'
X%left '&'
X%left MIN_MAX
X%left EQCOMPARE
X%left ARITHCOMPARE
X%left LSHIFT RSHIFT
X%left '+' '-'
X%left '*' '/' '%'
X%right UNARY PLUSPLUS MINUSMINUS
X%left HYPERUNARY
X%left PAREN_STAR_PAREN PAREN_X_SCOPE_STAR_PAREN PAREN_X_SCOPE_REF_PAREN LEFT_RIGHT
X%left POINTSAT '.' '(' '['
X
X%right SCOPE /* C++ extension */
X%nonassoc NEW DELETE RAISE RAISES RERAISE TRY EXCEPT CATCH
X%right DYNAMIC
X
X%type unop
X
X%type identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist /* exprlist */
X%type expr_no_commas cast_expr unary_expr primary string STRING
X%type typed_declspecs reserved_declspecs
X%type typed_typespecs reserved_typespecquals
X%type declmods typespec typespecqual_reserved
X%type SCSPEC TYPESPEC TYPE_QUAL nonempty_type_quals maybe_type_qual
X%type initdecls notype_initdecls initdcl /* C++ modification */
X%type init initlist maybeasm
X%type asm_operands nonnull_asm_operands asm_operand asm_clobbers
X%type maybe_attribute attribute_list attrib
X
X%type compstmt except_stmts
X
X%type declarator notype_declarator after_type_declarator
X
X%type structsp opt.component_decl_list component_decl_list component_decl components component_declarator
X%type enumlist enumerator
X%type typename absdcl absdcl1 type_quals
X%type xexpr see_typename parmlist parms parm bad_parm
X
X/* C++ extensions */
X%token TYPENAME_COLON TYPENAME_SCOPE TYPENAME_ELLIPSIS
X%token PRE_PARSED_FUNCTION_DECL EXTERN_LANG_STRING ALL
X%type fn.def2 dummy_decl x_typespec return_id
X%type class_head opt.init base_class_list base_class_visibility_list
X%type after_type_declarator_no_typename
X%type maybe_raises raise_identifier raise_identifiers
X%type component_declarator0 scoped_identifier
X%type forhead.1 identifier_or_opname operator_name
X%type new delete object primary_no_id aggr nonmomentary_expr
X%type LC forhead.2 initdcl0 notype_initdcl0 wrapper member_init_list
X%type .scope try
X
X%{
X/* the declaration found for the last IDENTIFIER token read in.
X yylex must look this up to detect typedefs, which get token type TYPENAME,
X so it is left around in case the identifier is not a typedef but is
X used in a context which makes it a reference to a variable. */
Xtree lastiddecl;
X
Xtree make_pointer_declarator (), make_reference_declarator ();
X
Xtree combine_strings ();
Xvoid reinit_parse_for_function ();
Xvoid reinit_parse_for_method ();
X
X/* List of types and structure classes of the current declaration. */
Xtree current_declspecs;
X
Xint undeclared_variable_notice; /* 1 if we explained undeclared var errors. */
X
Xint yylex ();
Xextern FILE *finput;
X%}
X
X%%
Xprogram: .program /* empty */
X | .program extdefs
X { finish_file (); }
X ;
X
X.program: /* empty */
X {
X }
X
X/* the reason for the strange actions in this rule
X is so that notype_initdecls when reached via datadef
X can find a valid list of type and sc specs in $0. */
X
Xextdefs:
X {$$ = NULL_TREE; } extdef
X | extdefs {$$ = NULL_TREE; } extdef
X ;
X
Xextdef:
X fndef
X { if (pending_inlines) do_pending_inlines (); }
X | datadef
X { if (pending_inlines) do_pending_inlines (); }
X | overloaddef
X | ASM '(' string ')' ';'
X { if (pedantic)
X warning ("ANSI C forbids use of `asm' keyword");
X if (TREE_CHAIN ($3)) $3 = combine_strings ($3);
X assemble_asm ($3); }
X | extern_lang_string '{' extdefs '}'
X { pop_lang_context (); }
X | extern_lang_string '{' '}'
X { pop_lang_context (); }
X | extern_lang_string fndef
X { if (pending_inlines) do_pending_inlines ();
X pop_lang_context (); }
X | extern_lang_string datadef
X { if (pending_inlines) do_pending_inlines ();
X pop_lang_context (); }
X ;
X
Xextern_lang_string:
X EXTERN_LANG_STRING
X { push_lang_context ($1); }
X ;
X
Xoverloaddef:
X OVERLOAD ov_identifiers ';'
X
Xov_identifiers: IDENTIFIER
X { declare_overloaded ($1); }
X | ov_identifiers ',' IDENTIFIER
X { declare_overloaded ($3); }
X ;
X
Xdummy_decl: /* empty */
X { $$ = NULL_TREE; }
X ;
X
Xdatadef:
X dummy_decl notype_initdecls ';'
X { if (pedantic)
X error ("ANSI C forbids data definition lacking type or storage class");
X else if (! flag_traditional)
X warning ("data definition lacks type or storage class"); }
X | declmods notype_initdecls ';'
X {}
X /* Normal case to make fast: "int i;". */
X | declmods declarator ';'
X { tree d;
X d = start_decl ($2, $1, 0, NULL_TREE);
X finish_decl (d, NULL_TREE, NULL_TREE);
X }
X | typed_declspecs initdecls ';'
X {
X end_exception_decls ();
X note_got_semicolon ($1);
X }
X /* Normal case: make this fast. */
X | typed_declspecs declarator ';'
X { tree d;
X d = start_decl ($2, $1, 0, NULL_TREE);
X finish_decl (d, NULL_TREE, NULL_TREE);
X end_exception_decls ();
X note_got_semicolon ($1);
X }
X | declmods ';'
X { error ("empty declaration"); }
X | typed_declspecs ';'
X {
X shadow_tag ($1);
X note_got_semicolon ($1);
X }
X | error ';'
X | error '}'
X | ';'
X ;
X
Xfndef:
X fn.def1 base_init compstmt_or_error
X {
X finish_function (lineno, 1);
X /* finish_function performs these three statements:
X
X expand_end_bindings (getdecls (), 1, 0);
X poplevel (1, 1, 0);
X
X expand_end_bindings (0, 0, 0);
X poplevel (0, 0, 1);
X */
X }
X | fn.def1 return_init base_init compstmt_or_error
X {
X finish_function (lineno, 1);
X /* finish_function performs these three statements:
X
X expand_end_bindings (getdecls (), 1, 0);
X poplevel (1, 1, 0);
X
X expand_end_bindings (0, 0, 0);
X poplevel (0, 0, 1);
X */
X }
X | fn.def1 nodecls compstmt_or_error
X { finish_function (lineno, 0); }
X | fn.def1 return_init ';' nodecls compstmt_or_error
X { finish_function (lineno, 0); }
X | fn.def1 return_init nodecls compstmt_or_error
X { finish_function (lineno, 0); }
X | typed_declspecs declarator error
X {}
X | declmods notype_declarator error
X {}
X | dummy_decl notype_declarator error
X {}
X ;
X
Xfn.def1:
X typed_declspecs declarator maybe_raises
X { if (! start_function ($1, $2, $3, 0))
X YYERROR;
X reinit_parse_for_function (); }
X | declmods notype_declarator maybe_raises
X { if (! start_function ($1, $2, $3, 0))
X YYERROR;
X reinit_parse_for_function (); }
X | dummy_decl notype_declarator maybe_raises
X { if (! start_function (NULL_TREE, $2, $3, 0))
X YYERROR;
X reinit_parse_for_function (); }
X | dummy_decl TYPENAME '(' parmlist ')' type_quals maybe_raises
X { if (! start_function (NULL_TREE, build_parse_node (CALL_EXPR, $2, $4, $6), $7, 0))
X YYERROR;
X reinit_parse_for_function (); }
X | dummy_decl TYPENAME LEFT_RIGHT type_quals maybe_raises
X { if (! start_function (NULL_TREE, build_parse_node (CALL_EXPR, $2, empty_parms (), $4), $5, 0))
X YYERROR;
X reinit_parse_for_function (); }
X | PRE_PARSED_FUNCTION_DECL
X { start_function (NULL_TREE, $1, NULL_TREE, 1);
X reinit_parse_for_function (); }
X ;
X
X/* more C++ complexity */
Xfn.def2:
X typed_declspecs '(' parmlist ')' type_quals maybe_raises
X {
X tree decl = build_parse_node (CALL_EXPR, TREE_VALUE ($1), $3, $5);
X $$ = start_method (TREE_CHAIN ($1), decl, $6);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | typed_declspecs LEFT_RIGHT type_quals maybe_raises
X {
X tree decl = build_parse_node (CALL_EXPR, TREE_VALUE ($1), empty_parms (), $3);
X $$ = start_method (TREE_CHAIN ($1), decl, $4);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | typed_declspecs declarator maybe_raises
X { $$ = start_method ($1, $2, $3);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | declmods '(' parmlist ')' type_quals maybe_raises
X {
X tree decl = build_parse_node (CALL_EXPR, TREE_VALUE ($1), $3, $5);
X $$ = start_method (TREE_CHAIN ($1), decl, $6);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | declmods LEFT_RIGHT type_quals maybe_raises
X {
X tree decl = build_parse_node (CALL_EXPR, TREE_VALUE ($1), empty_parms (), $3);
X $$ = start_method (TREE_CHAIN ($1), decl, $4);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | declmods declarator maybe_raises
X { $$ = start_method ($1, $2, $3);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X | dummy_decl notype_declarator maybe_raises
X { $$ = start_method (NULL_TREE, $2, $3);
X if (! $$)
X YYERROR;
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X reinit_parse_for_method (yychar, $$); }
X ;
X
Xreturn_id: RETURN IDENTIFIER
X {
X if (! current_function_parms_stored)
X store_parm_decls ();
X $$ = $2;
X }
X ;
X
Xreturn_init: return_id opt.init
X {
X extern tree value_identifier;
X tree result;
X
X result = DECL_RESULT (current_function_decl);
X if (DECL_NAME (result) == value_identifier)
X DECL_NAME (result) = $1;
X else
X error ("return identifier `%s' already in place",
X IDENTIFIER_POINTER (DECL_NAME (result)));
X store_return_init ($2);
X }
X | return_id '(' nonnull_exprlist ')'
X {
X extern tree value_identifier;
X tree result;
X
X result = DECL_RESULT (current_function_decl);
X if (DECL_NAME (result) == value_identifier)
X DECL_NAME (result) = $1;
X else
X error ("return identifier `%s' already in place",
X IDENTIFIER_POINTER (DECL_NAME (result)));
X store_return_init ($3);
X }
X | return_id LEFT_RIGHT
X {
X extern tree value_identifier;
X tree result;
X
X result = DECL_RESULT (current_function_decl);
X if (DECL_NAME (result) == value_identifier)
X DECL_NAME (result) = $1;
X else
X error ("return identifier `%s' already in place",
X IDENTIFIER_POINTER (DECL_NAME (result)));
X store_return_init (NULL_TREE);
X }
X ;
X
Xbase_init:
X ':' .set_base_init member_init_list
X {
X if ($3 == 0)
X error ("no base initializers given following ':'");
X setup_vtbl_ptr ();
X }
X ;
X
X.set_base_init:
X /* empty */
X {
X int preserve = (current_class_type
X && TYPE_USES_VIRTUAL_BASECLASSES (current_class_type));
X preserve = 0; /* "in charge" arg means we no longer
X need this hack. */
X if (! current_function_parms_stored)
X store_parm_decls ();
X else if (preserve)
X preserve_data ();
X
X /* Flag that we are processing base and member initializers. */
X current_vtable_decl = error_mark_node;
X
X if (DECL_CONSTRUCTOR_P (current_function_decl))
X {
X /* Make a contour for the initializer list. */
X pushlevel (0);
X clear_last_expr ();
X expand_start_bindings (0);
X }
X else if (current_class_type == NULL_TREE)
X error ("base initializers not allowed for non-member functions");
X else if (! DECL_CONSTRUCTOR_P (current_function_decl))
X error ("only constructors take base initializers");
X }
X ;
X
Xmember_init_list:
X /* empty */
X { $$ = 0; }
X | member_init
X { $$ = 1; }
X | member_init_list ',' member_init
X | member_init_list error
X ;
X
X
X
Xmember_init: '(' nonnull_exprlist ')'
X {
X if (current_class_name && pedantic)
X warning ("old style base class initialization; use `%s (...)'",
X IDENTIFIER_POINTER (current_class_name));
X expand_member_init (C_C_D, NULL_TREE, $2);
X }
X | LEFT_RIGHT
X {
X if (current_class_name && pedantic)
X warning ("old style base class initialization; use `%s (...)'",
X IDENTIFIER_POINTER (current_class_name));
X expand_member_init (C_C_D, NULL_TREE, void_type_node);
X }
X | identifier '(' nonnull_exprlist ')'
X {
X expand_member_init (C_C_D, $1, $3);
X }
X | identifier LEFT_RIGHT
X { expand_member_init (C_C_D, $1, void_type_node); }
X | scoped_identifier identifier '(' nonnull_exprlist ')'
X {
X tree base, basetype;
X tree scopes = $1;
X
X if (TREE_CODE (scopes) == SCOPE_REF)
X /* just a pain to do this right now. */
X abort ();
X
X if (current_class_type == NULL_TREE
X || ! is_aggr_typedef (scopes, 1))
X break;
X basetype = get_base_type (TREE_TYPE (TREE_TYPE (scopes)),
X current_class_type, 1);
X if (basetype == error_mark_node)
X break;
X if (basetype == 0)
X {
X error_not_base_type (TREE_TYPE (TREE_TYPE (scopes)), current_class_type);
X break;
X }
X
X base = convert_pointer_to (basetype, current_class_decl);
X expand_member_init (build_indirect_ref (base), $2, $4);
X }
X | scoped_identifier identifier LEFT_RIGHT
X {
X tree basetype, base;
X tree scopes = $1;
X if (TREE_CODE (scopes) == SCOPE_REF)
X /* just a pain to do this right now. */
X abort ();
X
X if (current_class_type == NULL_TREE
X || ! is_aggr_typedef (scopes, 1))
X break;
X basetype = get_base_type (TREE_TYPE (TREE_TYPE (scopes)),
X current_class_type, 1);
X if (basetype == error_mark_node)
X break;
X if (basetype == 0)
X {
X error_not_base_type (TREE_TYPE (TREE_TYPE (scopes)), current_class_type);
X break;
X }
X
X base = convert_pointer_to (basetype, current_class_decl);
X expand_member_init (build_indirect_ref (base), $2, void_type_node);
X }
X ;
X
Xidentifier:
X IDENTIFIER
X | TYPENAME
X ;
X
Xidentifier_or_opname:
X IDENTIFIER
X | TYPENAME
X | '~' identifier
X { $$ = build_parse_node (BIT_NOT_EXPR, $2); }
X | operator_name
X { $$ = hack_operator ($1);
X if ($$ == error_mark_node)
X $$ = get_identifier (""); }
X | wrapper IDENTIFIER
X { $$ = hack_wrapper ($1, NULL_TREE, $2); }
X | wrapper TYPENAME
X { $$ = hack_wrapper ($1, NULL_TREE, $2); }
X | wrapper operator_name
X { $$ = hack_wrapper ($1, NULL_TREE, $2); }
X | wrapper scoped_identifier IDENTIFIER
X { $$ = hack_wrapper ($1, $2, $3); }
X | wrapper scoped_identifier operator_name
X { $$ = hack_wrapper ($1, $2, $3); }
X ;
X
Xwrapper: LEFT_RIGHT
X { $$ = 0; }
X | '~' LEFT_RIGHT
X { $$ = 1; }
X | LEFT_RIGHT '?'
X { $$ = 2; }
X ;
X
Xunop: '-'
X { $$ = NEGATE_EXPR; }
X | '+'
X { $$ = CONVERT_EXPR; }
X | PLUSPLUS
X { $$ = PREINCREMENT_EXPR; }
X | MINUSMINUS
X { $$ = PREDECREMENT_EXPR; }
X | '!'
X { $$ = TRUTH_NOT_EXPR; }
X ;
X
Xexpr: nonnull_exprlist
X { $$ = build_x_compound_expr ($1); }
X /* Ugly, but faster. */
X | expr_no_commas
X {
X if (TREE_CODE ($1) == CALL_EXPR
X && TYPE_NEEDS_DESTRUCTOR (TREE_TYPE ($1)))
X $$ = cleanup_after_call ($1);
X }
X ;
X
X/* Now obsolete.
Xexprlist:
X / * empty * /
X { $$ = NULL_TREE; }
X | nonnull_exprlist
X ;
X*/
X
Xnonnull_exprlist:
X expr_no_commas
X { $$ = build_tree_list (NULL_TREE, $1); }
X | nonnull_exprlist ',' expr_no_commas
X { chainon ($1, build_tree_list (NULL_TREE, $3)); }
X | nonnull_exprlist ',' error
X { chainon ($1, build_tree_list (NULL_TREE, error_mark_node)); }
X ;
X
Xunary_expr:
X primary %prec UNARY
X {
X if (TREE_CODE ($1) == TYPE_EXPR)
X $$ = build_component_type_expr (C_C_D, $1, NULL_TREE, 1);
X else
X $$ = $1;
X }
X | '*' cast_expr %prec UNARY
X { $$ = build_x_indirect_ref ($2, "unary *"); }
X | '&' cast_expr %prec UNARY
X { $$ = build_x_unary_op (ADDR_EXPR, $2); }
X | '~' cast_expr %prec UNARY
X { $$ = build_x_unary_op (BIT_NOT_EXPR, $2); }
X | unop cast_expr %prec UNARY
X { $$ = build_x_unary_op ($1, $2);
X if ($1 == NEGATE_EXPR && TREE_CODE ($2) == INTEGER_CST)
X TREE_NEGATED_INT ($$) = 1;
X }
X | SIZEOF unary_expr %prec UNARY
X { if (TREE_CODE ($2) == COMPONENT_REF
X && TREE_PACKED (TREE_OPERAND ($2, 1)))
X error ("sizeof applied to a bit-field");
X /* ANSI says arrays and functions are converted inside comma.
X But we can't really convert them in build_compound_expr
X because that would break commas in lvalues.
X So do the conversion here if operand was a comma. */
X if (TREE_CODE ($2) == COMPOUND_EXPR
X && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE))
X $2 = default_conversion ($2);
X $$ = c_sizeof (TREE_TYPE ($2)); }
X | SIZEOF '(' typename ')' %prec HYPERUNARY
X { $$ = c_sizeof (groktypename ($3)); }
X | ALIGNOF unary_expr %prec UNARY
X { if (TREE_CODE ($2) == COMPONENT_REF
X && TREE_PACKED (TREE_OPERAND ($2, 1)))
X error ("`__alignof' applied to a bit-field");
X if (TREE_CODE ($2) == INDIRECT_REF)
X {
X tree t = TREE_OPERAND ($2, 0);
X tree best = t;
X int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
X while (TREE_CODE (t) == NOP_EXPR
X && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
X {
X int thisalign;
X t = TREE_OPERAND (t, 0);
X thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
X if (thisalign > bestalign)
X best = t, bestalign = thisalign;
X }
X $$ = c_alignof (TREE_TYPE (TREE_TYPE (best)));
X }
X else
X {
X /* ANSI says arrays and fns are converted inside comma.
X But we can't convert them in build_compound_expr
X because that would break commas in lvalues.
X So do the conversion here if operand was a comma. */
X if (TREE_CODE ($2) == COMPOUND_EXPR
X && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE))
X $2 = default_conversion ($2);
X $$ = c_alignof (TREE_TYPE ($2));
X }
X }
X | ALIGNOF '(' typename ')' %prec HYPERUNARY
X { $$ = c_alignof (groktypename ($3)); }
X
X | .scope new typename %prec '='
X { $$ = build_new ($2, $3, NULL_TREE, $1); }
X | .scope new x_typespec '(' nonnull_exprlist ')'
X { $$ = build_new ($2, $3, $5, $1); }
X | .scope new x_typespec LEFT_RIGHT
X { $$ = build_new ($2, $3, NULL_TREE, $1); }
X | .scope new typename '=' init %prec '='
X { $$ = build_new ($2, $3, $5, $1); }
X | .scope new '(' typename ')'
X { $$ = build_new ($2, $4, NULL_TREE, $1); }
X /* Unswallow a ':' which is probably meant for ?: expression. */
X | .scope new TYPENAME_COLON
X { yyungetc (':', 1);
X $$ = build_new ($2, $3, NULL_TREE, $1); }
X
X | delete cast_expr %prec UNARY
X { tree expr = stabilize_reference (convert_from_reference ($2));
X tree type = TREE_TYPE (expr);
X
X if (integer_zerop (expr))
X $$ = build1 (NOP_EXPR, void_type_node, expr);
X else if (TREE_CODE (type) != POINTER_TYPE)
X {
X error ("non-pointer type to `delete'");
X $$ = error_mark_node;
X break;
X }
X if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (type)))
X $$ = build_delete (type, expr, integer_three_node,
X LOOKUP_NORMAL|LOOKUP_HAS_IN_CHARGE, $1);
X else if (! TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (type)))
X $$ = build_x_delete (type, expr, $1);
X else
X $$ = build_delete (type, expr, integer_three_node,
X LOOKUP_NORMAL|LOOKUP_HAS_IN_CHARGE, 0);
X }
X | delete '[' expr ']' cast_expr %prec UNARY
X {
X tree maxindex = build_binary_op (MINUS_EXPR, $3, integer_one_node);
X tree exp = stabilize_reference (convert_from_reference ($5));
X tree elt_size = c_sizeof (TREE_TYPE (exp));
X
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X
X $$ = build_vec_delete (exp, maxindex, elt_size, NULL_TREE,
X integer_one_node, integer_two_node);
X }
X ;
X
Xcast_expr:
X unary_expr
X | '(' typename ')' expr_no_commas %prec UNARY
X { tree type = groktypename ($2);
X $$ = build_c_cast (type, $4); }
X | '(' typename ')' '{' initlist maybecomma '}' %prec UNARY
X { tree type = groktypename ($2);
X tree init = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($5));
X if (pedantic)
X warning ("ANSI C forbids constructor-expressions");
X /* Indicate that this was a GNU C constructor expression. */
X TREE_HAS_CONSTRUCTOR (init) = 1;
X $$ = digest_init (type, init, 0);
X if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0)
X {
X int failure = complete_array_type (type, $$, 1);
X if (failure)
X abort ();
X }
X }
X ;
X
Xexpr_no_commas:
X cast_expr
X | expr_no_commas '+' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '-' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '*' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '/' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '%' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas LSHIFT expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas RSHIFT expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas ARITHCOMPARE expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }

X | expr_no_commas EQCOMPARE expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas MIN_MAX expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '&' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '|' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas '^' expr_no_commas
X { $$ = build_x_binary_op ($2, $1, $3); }
X | expr_no_commas ANDAND expr_no_commas
X { $$ = build_x_binary_op (TRUTH_ANDIF_EXPR, $1, $3); }
X | expr_no_commas OROR expr_no_commas
X { $$ = build_x_binary_op (TRUTH_ORIF_EXPR, $1, $3); }
X | expr_no_commas '?' xexpr ':' expr_no_commas
X { $$ = build_x_conditional_expr ($1, $3, $5); }
X | expr_no_commas '=' expr_no_commas
X { $$ = build_modify_expr ($1, NOP_EXPR, $3); }
X | expr_no_commas ASSIGN expr_no_commas
X { register tree rval;
X if (rval = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL, $1, $3, $2))
X $$ = rval;
X else
X $$ = build_modify_expr ($1, $2, $3); }
X
X /* Handle general members. */
X | object '*' expr_no_commas %prec UNARY
X { $$ = build_m_component_ref ($1, build_x_indirect_ref ($3, "unary *")); }
X | object '&' expr_no_commas %prec UNARY
X { $$ = build_m_component_ref ($1, build_x_unary_op (ADDR_EXPR, $3)); }
X | object unop expr_no_commas %prec UNARY
X { $$ = build_m_component_ref ($1, build_x_unary_op ($2, $3)); }
X | object '(' typename ')' expr_no_commas %prec UNARY
X { tree type = groktypename ($3);
X $$ = build_m_component_ref ($1, build_c_cast (type, $5)); }
X | object primary_no_id %prec UNARY
X { $$ = build_m_component_ref ($1, $2); }
X ;
X
Xprimary:
X IDENTIFIER
X { $$ = do_identifier ($1); }
X | operator_name
X {
X tree op = hack_operator ($1);
X if (TREE_CODE (op) != IDENTIFIER_NODE)
X $$ = op;
X else
X {
X $$ = lookup_name (op);
X if ($$ == NULL_TREE)
X {
X error ("operator %s not defined", operator_name_string (op));
X $$ = error_mark_node;
X }
X }
X }
X | CONSTANT
X | string
X { $$ = combine_strings ($1); }
X | '(' expr ')'
X { $$ = $2; }
X | '(' error ')'
X { $$ = error_mark_node; }
X | '('
X { if (current_function_decl == 0)
X {
X error ("braced-group within expression allowed only inside a function");
X YYERROR;
X }
X keep_next_level ();
X $$ = expand_start_stmt_expr (); }
X compstmt ')'
X { tree rtl_exp;
X if (pedantic)
X warning ("ANSI C forbids braced-groups within expressions");
X rtl_exp = expand_end_stmt_expr ($2);
X $$ = $3;
X TREE_USED ($$) = 0;
X /* Since the statements have side effects,
X consider this volatile. */
X TREE_VOLATILE ($$) = 1;
X TREE_TYPE ($$) = TREE_TYPE (rtl_exp);
X STMT_BODY ($$) = rtl_exp; }
X | primary '(' nonnull_exprlist ')'
X { $$ = build_x_function_call ($1, $3, current_class_decl); }
X | primary LEFT_RIGHT
X { $$ = build_x_function_call ($1, NULL_TREE, current_class_decl); }
X | primary '[' expr ']'
X {
X do_array:
X {
X tree type = TREE_TYPE ($1);
X if (type == error_mark_node || $3 == error_mark_node)
X $$ = error_mark_node;
X else if (type == NULL_TREE)
X {
X /* Something has gone very wrong. Assume we
X are mistakenly reducing an expression
X instead of a declaration. */
X error ("parser may be lost: is there a '{' missing somewhere?");
X $$ = NULL_TREE;
X }
X else
X {
X if (TREE_CODE (type) == OFFSET_TYPE)
X type = TREE_TYPE (type);
X if (TREE_CODE (type) == REFERENCE_TYPE)
X type = TREE_TYPE (type);
X
X if (TYPE_LANG_SPECIFIC (type) &&
X TYPE_OVERLOADS_ARRAY_REF (type))
X $$ = build_opfncall (ARRAY_REF, LOOKUP_NORMAL, $1, $3);
X else if (TREE_CODE (type) == POINTER_TYPE
X || TREE_CODE (type) == ARRAY_TYPE)
X $$ = build_array_ref ($1, $3);
X else
X error("[] applied to non-pointer type");
X }
X }
X }
X | object identifier_or_opname %prec UNARY
X { $$ = build_component_ref ($1, $2, NULL_TREE, 1); }
X | object scoped_identifier identifier_or_opname %prec UNARY
X {
X tree basetype = (TREE_CODE ($2) == SCOPE_REF) ? TREE_OPERAND ($2, 1) : $2;
X if (is_aggr_typedef (basetype, 1))
X {
X basetype = TREE_TYPE (TREE_TYPE (basetype));
X
X if ($1 == error_mark_node)
X $$ = error_mark_node;
X else if (basetype_or_else (basetype, TREE_TYPE ($1)))
X $$ = build_component_ref (build_scoped_ref ($1, $2), $3, NULL_TREE, 1);
X else
X $$ = error_mark_node;
X }
X else $$ = error_mark_node;
X }
X | primary PLUSPLUS
X { $$ = build_x_unary_op (POSTINCREMENT_EXPR, $1); }
X | primary MINUSMINUS
X { $$ = build_x_unary_op (POSTDECREMENT_EXPR, $1); }
X
X /* C++ extensions */
X | THIS
X { if (current_class_decl)
X {
X#ifdef WARNING_ABOUT_CCD
X TREE_USED (current_class_decl) = 1;
X#endif
X $$ = current_class_decl;
X }
X else if (current_function_decl
X && DECL_STATIC_FUNCTION_P (current_function_decl))
X {
X error ("`this' is unavailable for static member functions");
X $$ = error_mark_node;
X }
X else
X {
X if (current_function_decl)
X error ("invalid use of `this' in non-member function");
X else
X error ("invalid use of `this' at top level");
X $$ = error_mark_node;
X }
X }
X | dummy_decl TYPE_QUAL '(' nonnull_exprlist ')'
X {
X tree type;
X tree id = $2;
X
X /* This is a C cast in C++'s `functional' notation. */
X if ($4 == error_mark_node)
X {
X $$ = error_mark_node;
X break;
X }
X#if 0
X if ($4 == NULL_TREE)
X {
X error ("cannot cast null list to type `%s'",
X IDENTIFIER_POINTER (TYPE_NAME ($2)));
X $$ = error_mark_node;
X break;
X }
X#endif
X if (type == error_mark_node)
X $$ = error_mark_node;
X else
X {
X if (id == ridpointers[(int) RID_CONST])
X type = build_type_variant (integer_type_node, 1, 0);
X else if (id == ridpointers[(int) RID_VOLATILE])
X type = build_type_variant (integer_type_node, 0, 1);
X else if (id == ridpointers[(int) RID_FRIEND])
X {
X error ("cannot cast expression to `friend' type");
X $$ = error_mark_node;
X break;
X }
X else abort ();
X $$ = build_c_cast (type, build_compound_expr ($4));
X }
X }
X | x_typespec '(' nonnull_exprlist ')'
X { $$ = build_functional_cast ($1, $3); }
X | x_typespec LEFT_RIGHT
X { $$ = build_functional_cast ($1, NULL_TREE); }
X | SCOPE IDENTIFIER
X {
X do_scoped_identifier:
X $$ = IDENTIFIER_GLOBAL_VALUE ($2);
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X if (! $$)
X {
X if (yychar == '(' || yychar == LEFT_RIGHT)
X {
X $$ = implicitly_declare ($2);
X }
X else
X {
X if (IDENTIFIER_GLOBAL_VALUE ($2) != error_mark_node)
X error ("undeclared variable `%s' (first use here)",
X IDENTIFIER_POINTER ($2));
X $$ = error_mark_node;
X /* Prevent repeated error messages. */
X IDENTIFIER_GLOBAL_VALUE ($2) = error_mark_node;
X }
X }
X else if (TREE_CODE ($$) == CONST_DECL)
X $$ = DECL_INITIAL ($$);
X if (! TREE_USED ($$))
X {
X if (TREE_EXTERNAL ($$))
X assemble_external ($$);
X TREE_USED ($$) = 1;
X }
X }
X | SCOPE operator_name
X {
X $2 = hack_operator ($2);
X if (TREE_CODE ($2) == IDENTIFIER_NODE)
X goto do_scoped_identifier;
X do_scoped_operator:
X $$ = $2;
X }
X | scoped_identifier identifier_or_opname %prec HYPERUNARY
X { $$ = build_offset_ref ($1, $2); }
X | scoped_identifier identifier_or_opname '(' nonnull_exprlist ')'
X { $$ = build_member_call ($1, $2, $4); }
X | scoped_identifier identifier_or_opname LEFT_RIGHT
X { $$ = build_member_call ($1, $2, NULL_TREE); }
X
X | object identifier_or_opname '(' nonnull_exprlist ')'
X { $$ = build_method_call ($1, $2, $4, NULL_TREE,
X (LOOKUP_NORMAL|LOOKUP_AGGR)); }
X | object identifier_or_opname LEFT_RIGHT
X { $$ = build_method_call ($1, $2, NULL_TREE, NULL_TREE,
X (LOOKUP_NORMAL|LOOKUP_AGGR)); }
X | object scoped_identifier identifier_or_opname '(' nonnull_exprlist ')'
X { $$ = build_scoped_method_call ($1, $2, $3, $5); }
X | object scoped_identifier identifier_or_opname LEFT_RIGHT
X { $$ = build_scoped_method_call ($1, $2, $3, NULL_TREE); }
X ;
X
Xprimary_no_id:
X '(' expr ')'
X { $$ = $2; }
X | '(' error ')'
X { $$ = error_mark_node; }
X | '('
X { if (current_function_decl == 0)
X {
X error ("braced-group within expression allowed only inside a function");
X YYERROR;
X }
X $$ = expand_start_stmt_expr (); }
X compstmt ')'
X { if (pedantic)
X warning ("ANSI C forbids braced-groups within expressions");
X $$ = expand_end_stmt_expr ($2); }
X | primary_no_id '(' nonnull_exprlist ')'
X { $$ = build_x_function_call ($1, $3, current_class_decl); }
X | primary_no_id LEFT_RIGHT
X { $$ = build_x_function_call ($1, NULL_TREE, current_class_decl); }
X | primary_no_id '[' expr ']'
X { goto do_array; }
X | primary_no_id PLUSPLUS
X { $$ = build_x_unary_op (POSTINCREMENT_EXPR, $1); }
X | primary_no_id MINUSMINUS
X { $$ = build_x_unary_op (POSTDECREMENT_EXPR, $1); }
X | SCOPE IDENTIFIER
X { goto do_scoped_identifier; }
X | SCOPE operator_name
X { $2 = hack_operator ($2);
X if (TREE_CODE ($2) == IDENTIFIER_NODE)
X goto do_scoped_identifier;
X goto do_scoped_operator;
X }
X ;
X
Xnew: NEW
X { $$ = NULL_TREE; }
X | NEW '{' nonnull_exprlist '}'
X { $$ = $3; }
X | NEW DYNAMIC %prec EMPTY
X { $$ = void_type_node; }
X | NEW DYNAMIC '(' string ')'
X { $$ = combine_strings ($4); }
X ;
X
X.scope:
X /* empty */
X { $$ = 0; }
X | SCOPE
X { $$ = 1; }
X ;
X
Xdelete: DELETE
X { $$ = NULL_TREE; }
X | SCOPE delete
X { if ($2)
X error ("extra `::' before `delete' ignored");
X $$ = error_mark_node;
X }
X ;
X
X/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */
Xstring:
X STRING
X | string STRING
X { $$ = chainon ($1, $2); }
X ;
X
Xnodecls:
X /* empty */
X {
X if (! current_function_parms_stored)
X store_parm_decls ();
X setup_vtbl_ptr ();
X }
X ;
X
Xobject: primary '.'
X {
X if ($1 == error_mark_node)
X $$ = error_mark_node;
X else
X {
X tree type = TREE_TYPE ($1);
X
X if (IS_AGGR_TYPE (type)
X || (TREE_CODE (type) == REFERENCE_TYPE
X && IS_AGGR_TYPE (TREE_TYPE (type))))
X $$ = $1;
X else
X {
X error ("object in '.' expression is not of aggregate type");
X $$ = error_mark_node;
X }
X }
X }
X | primary POINTSAT
X {
X $$ = build_x_arrow ($1);
X }
X ;
X
Xdecl:
X typed_declspecs initdecls ';'
X {
X resume_momentary ($2);
X note_got_semicolon ($1);
X }
X /* Normal case: make this fast. */
X | typed_declspecs declarator ';'
X { tree d;
X int yes = suspend_momentary ();
X d = start_decl ($2, $1, 0, NULL_TREE);
X finish_decl (d, NULL_TREE, NULL_TREE);
X resume_momentary (yes);
X note_got_semicolon ($1);
X }
X | declmods notype_initdecls ';'
X { resume_momentary ($2); }
X /* Normal case: make this fast. */
X | declmods declarator ';'
X { tree d;
X int yes = suspend_momentary ();
X d = start_decl ($2, $1, 0, NULL_TREE);
X finish_decl (d, NULL_TREE, NULL_TREE);
X resume_momentary (yes);
X }
X | typed_declspecs ';'
X {
X shadow_tag ($1);
X note_got_semicolon ($1);
X }
X | declmods ';'
X { warning ("empty declaration"); }
X ;
X
X/* Any kind of declarator (thus, all declarators allowed
X after an explicit typespec). */
X
Xdeclarator:
X after_type_declarator
X | notype_declarator
X ;
X
X/* Declspecs which contain at least one type specifier or typedef name.
X (Just `const' or `volatile' is not enough.)
X A typedef'd name following these is taken as a name to be declared. */
X
Xtyped_declspecs:
X x_typespec
X { $$ = list_hash_lookup_or_cons ($1); }
X | declmods typespec
X { $$ = hash_tree_chain ($2, $1); }
X | x_typespec reserved_declspecs
X { $$ = hash_tree_chain ($1, $2); }
X | declmods typespec reserved_declspecs
X { $$ = hash_tree_chain ($2, hash_chainon ($3, $1)); }
X ;
X
Xreserved_declspecs: /* empty
X { $$ = NULL_TREE; } */
X typespecqual_reserved
X { $$ = build_decl_list (NULL_TREE, $1); }
X | SCSPEC
X { $$ = build_decl_list (NULL_TREE, $1); }
X | reserved_declspecs typespecqual_reserved
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X | reserved_declspecs SCSPEC
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X ;
X
X/* List of just storage classes and type modifiers.
X A declaration can start with just this, but then it cannot be used
X to redeclare a typedef-name. */
X
Xdeclmods:
X dummy_decl TYPE_QUAL
X { $$ = IDENTIFIER_AS_LIST ($2); }
X | dummy_decl SCSPEC
X { $$ = IDENTIFIER_AS_LIST ($2); }
X | declmods TYPE_QUAL
X { $$ = hash_tree_chain ($2, $1); }
X | declmods SCSPEC
X { $$ = hash_tree_chain ($2, $1); }
X ;
X
X
X/* Used instead of declspecs where storage classes are not allowed
X (that is, for typenames and structure components).
X
X C++ can takes storage classes for structure components.
X Don't accept a typedef-name if anything but a modifier precedes it. */
X
Xtyped_typespecs:
X x_typespec %prec EMPTY
X { $$ = build_decl_list_1 ($1); }
X | nonempty_type_quals typespec
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X | x_typespec reserved_typespecquals
X { $$ = decl_tree_cons (NULL_TREE, $1, $2); }
X | nonempty_type_quals typespec reserved_typespecquals
X { $$ = decl_tree_cons (NULL_TREE, $2, hash_chainon ($3, $1)); }
X ;
X
Xreserved_typespecquals:
X typespecqual_reserved
X { $$ = build_decl_list_1 ($1); }
X | reserved_typespecquals typespecqual_reserved
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X ;
X
X/* A typespec (but not a type qualifier).
X Once we have seen one of these in a declaration,
X if a typedef name appears then it is being redeclared. */
X
Xtypespec: TYPESPEC
X | structsp
X | TYPENAME
X | TYPEOF '(' expr ')'
X { $$ = TREE_TYPE ($3);
X if (pedantic)
X warning ("ANSI C forbids `typeof'"); }
X | TYPEOF '(' typename ')'
X { $$ = groktypename ($3);
X if (pedantic)
X warning ("ANSI C forbids `typeof'"); }
X ;
X
X/* A typespec that is a reserved word, or a type qualifier. */
X
Xtypespecqual_reserved: TYPESPEC
X | TYPE_QUAL
X | structsp
X ;
X
Xx_typespec:
X dummy_decl TYPESPEC
X { $$ = $2; }
X | dummy_decl structsp
X { $$ = $2; }
X | dummy_decl TYPENAME
X { $$ = $2; }
X | dummy_decl TYPEOF '(' expr ')'
X { $$ = TREE_TYPE ($4);
X if (pedantic)
X warning ("ANSI C forbids `typeof'") }
X | dummy_decl TYPEOF '(' typename ')'
X { $$ = groktypename ($4);
X if (pedantic)
X warning ("ANSI C forbids `typeof'") }
X ;
X
Xinitdecls:
X initdcl0
X | initdecls ',' initdcl
X ;
X
Xnotype_initdecls:
X notype_initdcl0
X | notype_initdecls ',' initdcl
X ;
X
Xmaybeasm:
X /* empty */
X { $$ = NULL_TREE; }
X | ASM '(' string ')'
X { if (TREE_CHAIN ($3)) $3 = combine_strings ($3);
X $$ = $3;
X if (pedantic)
X warning ("ANSI C forbids use of `asm' keyword");
X }
X ;
X
Xinitdcl0:
X declarator maybe_raises maybeasm maybe_attribute '='
X { current_declspecs = $0;
X $5 = suspend_momentary ();
X $$ = start_decl ($1, current_declspecs, 1, $2); }
X init
X/* Note how the declaration of the variable is in effect while its init is parsed! */
X { finish_decl ($6, $7, $3);
X $$ = $5; }
X | declarator maybe_raises maybeasm maybe_attribute
X { tree d;
X current_declspecs = $0;
X $$ = suspend_momentary ();
X d = start_decl ($1, current_declspecs, 0, $2);
X finish_decl (d, NULL_TREE, $3); }
X ;
X
Xinitdcl:
X declarator maybe_raises maybeasm maybe_attribute '='
X { $$ = start_decl ($1, current_declspecs, 1, $2); }
X init
X/* Note how the declaration of the variable is in effect while its init is parsed! */
X { finish_decl ($6, $7, $3); }
X | declarator maybe_raises maybeasm maybe_attribute
X { tree d = start_decl ($1, current_declspecs, 0, $2);
X finish_decl (d, NULL_TREE, $3); }
X ;
X
Xnotype_initdcl0:
X notype_declarator maybe_raises maybeasm maybe_attribute '='
X { current_declspecs = $0;
X $5 = suspend_momentary ();
X $$ = start_decl ($1, current_declspecs, 1, $2); }
X init
X/* Note how the declaration of the variable is in effect while its init is parsed! */
X { finish_decl ($6, $7, $3);
X $$ = $5; }
X | notype_declarator maybe_raises maybeasm maybe_attribute
X { tree d;
X current_declspecs = $0;
X $$ = suspend_momentary ();
X d = start_decl ($1, current_declspecs, 0, $2);
X finish_decl (d, NULL_TREE, $3); }
X ;
X
X/* the * rules are dummies to accept the Apollo extended syntax
X so that the header files compile. */
Xmaybe_attribute:
X /* empty */
X { $$ = NULL_TREE; }
X | ATTRIBUTE '(' '(' attribute_list ')' ')'
X { $$ = $4; }
X ;
X
Xattribute_list
X : attrib
X | attribute_list ',' attrib
X ;
X
Xattrib
X : IDENTIFIER
X { warning ("`%s' attribute directive ignored",
X IDENTIFIER_POINTER ($1));
X $$ = $1; }
X | IDENTIFIER '(' CONSTANT ')'
X { /* if not "aligned(1)", then issue warning */
X if (strcmp (IDENTIFIER_POINTER ($1), "aligned") != 0
X || TREE_CODE ($3) != INTEGER_CST
X || TREE_INT_CST_LOW ($3) != 1)
X warning ("`%s' attribute directive ignored",
X IDENTIFIER_POINTER ($1));
X $$ = $1; }
X | IDENTIFIER '(' identifiers ')'
X { warning ("`%s' attribute directive ignored",
X IDENTIFIER_POINTER ($1));
X $$ = $1; }
X ;
X
Xidentifiers:
X IDENTIFIER
X { }
X | identifiers ',' IDENTIFIER
X { }
X ;
X
Xinit:
X expr_no_commas %prec '='
X | '{' '}'
X { $$ = build_nt (CONSTRUCTOR, NULL_TREE, NULL_TREE);
X TREE_HAS_CONSTRUCTOR ($$) = 1;
X if (pedantic)
X warning ("ANSI C forbids empty initializer braces"); }
X | '{' initlist '}'
X { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2));
X TREE_HAS_CONSTRUCTOR ($$) = 1; }
X | '{' initlist ',' '}'
X { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2));
X TREE_HAS_CONSTRUCTOR ($$) = 1; }
X | error
X { $$ = NULL_TREE; }
X ;
X
X/* This chain is built in reverse order,
X and put in forward order where initlist is used. */
Xinitlist:
X init
X { $$ = build_tree_list (NULL_TREE, $1); }
X | initlist ',' init
X { $$ = tree_cons (NULL_TREE, $3, $1); }
X ;
X
Xstructsp:
X ENUM identifier '{'
X { $3 = suspend_momentary ();
X $$ = start_enum ($2); }
X enumlist maybecomma_warn '}'
X { $$ = finish_enum ($4, $5);
X resume_momentary ($3);
X check_for_missing_semicolon ($4); }
X | ENUM identifier '{' '}'
X { $$ = finish_enum (start_enum ($2), NULL_TREE);
X check_for_missing_semicolon ($$); }
X | ENUM '{'
X { $2 = suspend_momentary ();
X $$ = start_enum (make_anon_name ()); }
X enumlist maybecomma_warn '}'
X { $$ = finish_enum ($3, $4);
X resume_momentary ($1);
X check_for_missing_semicolon ($3); }
X | ENUM '{' '}'
X { $$ = finish_enum (start_enum (make_anon_name()), NULL_TREE);
X check_for_missing_semicolon ($$); }
X | ENUM identifier
X { $$ = xref_tag (enum_type_node, $2, NULL_TREE); }
X
X /* C++ extensions, merged with C to avoid shift/reduce conflicts */
X | class_head LC opt.component_decl_list '}'
X {
X if (TREE_CODE ($1) == ENUMERAL_TYPE)
X $$ = $1;
X else if (CLASSTYPE_DECLARED_EXCEPTION ($1))
X $$ = finish_exception ($1, $3);
X else
X $$ = finish_struct ($1, $3, 0, 0);
X
X if ($2 & 1)
X resume_temporary_allocation ();
X if ($2 & 2)
X resume_momentary (1);
X check_for_missing_semicolon ($$);
X }
X | class_head LC opt.component_decl_list '}' ';'
X { if (TREE_CODE ($1) == ENUMERAL_TYPE)
X $$ = $1;
X else if (CLASSTYPE_DECLARED_EXCEPTION ($1))
X warning ("empty exception declaration\n");
X else
X $$ = finish_struct ($1, $3, 1, 1);
X if ($2 & 1)
X resume_temporary_allocation ();
X if ($2 & 2)
X resume_momentary (1);
X note_got_semicolon ($$);
X yyungetc (';', 0); }
X | class_head %prec EMPTY
X { $$ = $1; }
X ;
X
Xmaybecomma:
X /* empty */
X | ','
X ;
X
Xmaybecomma_warn:
X /* empty */
X | ','
X { if (pedantic) warning ("comma at end of enumerator list"); }
X ;
X
Xaggr: AGGR
X { $$ = $1; }
X | DYNAMIC AGGR
X { $$ = build_tree_list (NULL_TREE, $2); }
X | DYNAMIC '(' string ')' AGGR
X { $$ = build_tree_list ($3, $5); }
X | aggr SCSPEC
X { error ("storage class specifier `%s' not allow after struct or class", IDENTIFIER_POINTER ($2));
X }
X | aggr TYPESPEC
X { error ("type specifier `%s' not allow after struct or class", IDENTIFIER_POINTER ($2));
X }
X | aggr TYPE_QUAL
X { error ("type qualifier `%s' not allow after struct or class", IDENTIFIER_POINTER ($2));
X }
X | aggr AGGR
X { error ("no body nor ';' separates two class, struct or union declarations");
X }
X
Xclass_head:
X aggr %prec EMPTY
X { $$ = xref_tag ($1, make_anon_name (), NULL_TREE); }
X | aggr identifier %prec EMPTY
X { $$ = xref_tag ($1, $2, NULL_TREE); }
X | aggr IDENTIFIER ':' base_class_list %prec EMPTY
X { $$ = xref_tag ($1, $2, $4); }
X | aggr TYPENAME_COLON %prec EMPTY
X { yyungetc (':', 1);
X $$ = xref_tag ($1, $2, NULL_TREE); }
X | aggr TYPENAME_COLON base_class_list %prec EMPTY
X { $$ = xref_tag ($1, $2, $3); }
X ;
X
Xbase_class_list:
X identifier
X { if (! is_aggr_typedef ($1, 1))
X $$ = NULL_TREE;
X else $$ = build_tree_list ((tree)visibility_default, $1); }
X | base_class_visibility_list identifier
X { if (! is_aggr_typedef ($2, 1))
X $$ = NULL_TREE;
X else $$ = build_tree_list ($1, $2); }
X | base_class_list ',' identifier
X { if (! is_aggr_typedef ($3, 1))
X $$ = NULL_TREE;
X else $$ = chainon ($1, build_tree_list ((tree)visibility_default, $3)); }
X | base_class_list ',' base_class_visibility_list identifier
X { if (! is_aggr_typedef ($4, 1))
X $$ = NULL_TREE;
X else $$ = chainon ($1, build_tree_list ($3, $4)); }
X ;
X
Xbase_class_visibility_list:
X PUBLIC
X { $$ = (tree)visibility_public; }
X | PRIVATE
X { $$ = (tree)visibility_private; }
X | SCSPEC
X { if ($1 != ridpointers[(int)RID_VIRTUAL])
X sorry ("non-virtual visibility");
X $$ = (tree)visibility_default_virtual; }
X | base_class_visibility_list PUBLIC
X { if ($1 == (tree)visibility_private)
X error ("base class cannot be public and private");
X else if ($1 == (tree)visibility_default_virtual)
X $$ = (tree)visibility_public_virtual; }
X | base_class_visibility_list PRIVATE
X { if ($1 == (tree)visibility_public)
X error ("base class cannot be private and public");
X else if ($1 == (tree)visibility_default_virtual)
X $$ = (tree)visibility_private_virtual; }
X | base_class_visibility_list SCSPEC
X { if ($2 != ridpointers[(int)RID_VIRTUAL])
X sorry ("non-virtual visibility");
X if ($1 == (tree)visibility_public)
X $$ = (tree)visibility_public_virtual;
X else if ($1 == (tree)visibility_private)
X $$ = (tree)visibility_private_virtual; }
X ;
X
XLC: '{'
X { int temp = allocation_temporary_p ();
X int momentary = suspend_momentary ();
X if (temp)
X end_temporary_allocation ();
X $$ = (momentary << 1) | temp;
X if (! IS_AGGR_TYPE ($0))
X {
X $0 = make_lang_type (RECORD_TYPE);
X TYPE_NAME ($0) = get_identifier ("erroneous type");
X }
X pushclass ($0, 0); }
X
Xopt.component_decl_list:
X /* empty */
X { $$ = NULL_TREE; }
X | component_decl_list
X { $$ = build_tree_list ((tree)visibility_default, $1); }
X | opt.component_decl_list PUBLIC ':' component_decl_list
X { $$ = chainon ($1, build_tree_list ((tree)visibility_public, $4)); }
X | opt.component_decl_list PRIVATE ':' component_decl_list
X { $$ = chainon ($1, build_tree_list ((tree)visibility_private, $4)); }
X | opt.component_decl_list PROTECTED ':' component_decl_list
X { $$ = chainon ($1, build_tree_list ((tree)visibility_protected, $4)); }
X | opt.component_decl_list PUBLIC ':'
X | opt.component_decl_list PRIVATE ':'
X | opt.component_decl_list PROTECTED ':'
X ;
X
Xcomponent_decl_list:
X component_decl
X { if ($1 == void_type_node) $$ = NULL_TREE; }
X | component_decl_list component_decl
X { if ($2 != NULL_TREE && $2 != void_type_node)
X $$ = chainon ($1, $2); }
X | component_decl_list ';'
X { if (pedantic)
X warning ("extra semicolon in struct or union specified"); }
X ;
X
Xcomponent_decl:
X typed_declspecs components ';'
X {
X do_components:
X if ($2 == void_type_node)
X /* We just got some friends.
X They have been recorded elsewhere. */
X $$ = NULL_TREE;
X else if ($2 == NULL_TREE)
X {
X tree t = groktypename (build_decl_list ($1, NULL_TREE));
X if (t == NULL_TREE)
X {
X error ("error in component specification");
X $$ = NULL_TREE;
X }
X else if (TREE_CODE (t) == UNION_TYPE)
X {
X /* handle anonymous unions */
X if (CLASSTYPE_METHOD_VEC (t))
X sorry ("methods in anonymous unions");
X $$ = build_lang_field_decl (FIELD_DECL, NULL_TREE, t);
X DECL_ANON_UNION_ELEM ($$) = 1;
X }
X else if (TREE_CODE (t) == ENUMERAL_TYPE)
X $$ = grok_enum_decls (t, NULL_TREE);
X else if (TREE_CODE (t) == RECORD_TYPE)
X {
X if (TYPE_LANG_SPECIFIC (t)
X && CLASSTYPE_DECLARED_EXCEPTION (t))
X shadow_tag ($1);
X $$ = NULL_TREE;
X }
X else if (t != void_type_node)
X {
X error ("empty component declaration");
X $$ = NULL_TREE;
X }
X else $$ = NULL_TREE;
X }
X else
X {
X tree t = TREE_TYPE ($2);
X if (TREE_CODE (t) == ENUMERAL_TYPE && TREE_NONLOCAL (t))
X $$ = grok_enum_decls (t, $2);
X else
X $$ = $2;
X }
X end_exception_decls ();
X }
X | typed_declspecs '(' parmlist ')' ';'
X { $$ = groktypefield ($1, $3); }
X | typed_declspecs '(' parmlist ')' '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = groktypefield ($1, $3); }
X | typed_declspecs LEFT_RIGHT ';'
X { $$ = groktypefield ($1, empty_parms ()); }
X | typed_declspecs LEFT_RIGHT '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = groktypefield ($1, empty_parms ()); }
X | declmods components ';'
X { goto do_components; }
X /* Normal case: make this fast. */
X | declmods declarator ';'
X { $$ = grokfield ($2, $1, 0, 0, 0, 0); }
X | declmods components '}'

X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X goto do_components; }
X | declmods '(' parmlist ')' ';'
X { $$ = groktypefield ($1, $3); }
X | declmods '(' parmlist ')' '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = groktypefield ($1, $3); }
X | declmods LEFT_RIGHT ';'
X { $$ = groktypefield ($1, empty_parms ()); }
X | declmods LEFT_RIGHT '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = groktypefield ($1, empty_parms ()); }
X | ':' expr_no_commas ';'
X { $$ = grokbitfield (NULL_TREE, NULL_TREE, $2); }
X | ':' expr_no_commas '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = grokbitfield (NULL_TREE, NULL_TREE, $2); }
X | error
X { $$ = NULL_TREE; }
X
X /* C++: handle constructors, destructors and inline functions */
X /* note that INLINE is like a TYPESPEC */
X | fn.def2 ':' /* base_init compstmt */
X { $$ = finish_method ($1); }
X | fn.def2 '{' /* nodecls compstmt */
X { $$ = finish_method ($1); }
X | dummy_decl notype_declarator maybe_raises ';'
X { $$ = grokfield ($2, NULL_TREE, $3, NULL_TREE, NULL_TREE); }
X | dummy_decl notype_declarator maybe_raises '}'
X { error ("missing ';' before right brace");
X yyungetc ('}', 0);
X $$ = grokfield ($2, NULL_TREE, $3, NULL_TREE, NULL_TREE); }
X ;
X
Xcomponents:
X /* empty: possibly anonymous */
X { $$ = NULL_TREE; }
X | component_declarator0
X | components ',' component_declarator
X {
X /* In this context, void_type_node encodes
X friends. They have been recorded elsewhere. */
X if ($1 == void_type_node)
X $$ = $3;
X else
X $$ = chainon ($1, $3);
X }
X ;
X
Xcomponent_declarator0:
X declarator maybe_raises maybeasm opt.init
X { current_declspecs = $0;
X $$ = grokfield ($1, current_declspecs, $2, $4, $3); }
X | IDENTIFIER ':' expr_no_commas
X { current_declspecs = $0;
X $$ = grokbitfield ($1, current_declspecs, $3); }
X | TYPENAME_COLON expr_no_commas
X { current_declspecs = $0;
X $$ = grokbitfield ($1, current_declspecs, $2); }
X | ':' expr_no_commas
X { current_declspecs = $0;
X $$ = grokbitfield (NULL_TREE, NULL_TREE, $2); }
X ;
X
Xcomponent_declarator:
X declarator maybe_raises maybeasm opt.init
X { $$ = grokfield ($1, current_declspecs, $2, $4, $3); }
X | IDENTIFIER ':' expr_no_commas
X { $$ = grokbitfield ($1, current_declspecs, $3); }
X | TYPENAME_COLON expr_no_commas
X { $$ = grokbitfield ($1, current_declspecs, $2); }
X | ':' expr_no_commas
X { $$ = grokbitfield (NULL_TREE, NULL_TREE, $2); }
X ;
X
X/* We chain the enumerators in reverse order.
X Because of the way enums are built, the order is
X insignificant. Take advantage of this fact. */
X
Xenumlist:
X enumerator
X | enumlist ',' enumerator
X { TREE_CHAIN ($3) = $1; $$ = $3; }
X ;
X
Xenumerator:
X identifier
X { $$ = build_enumerator ($1, NULL_TREE); }
X | identifier '=' expr_no_commas
X { $$ = build_enumerator ($1, $3); }
X ;
X
Xtypename:
X typed_typespecs absdcl
X { $$ = build_decl_list ($1, $2); }
X | nonempty_type_quals absdcl
X { $$ = build_decl_list ($1, $2); }
X ;
X
Xabsdcl: /* an abstract declarator */
X /* empty */ %prec EMPTY
X { $$ = NULL_TREE; }
X | absdcl1 %prec EMPTY
X ;
X
Xnonempty_type_quals:
X dummy_decl TYPE_QUAL
X { $$ = IDENTIFIER_AS_LIST ($2); }
X | nonempty_type_quals TYPE_QUAL
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X ;
X
Xtype_quals:
X /* empty */
X { $$ = NULL_TREE; }
X | type_quals TYPE_QUAL
X { $$ = decl_tree_cons (NULL_TREE, $2, $1); }
X ;
X
X/* These rules must follow the rules for function declarations
X and component declarations. That way, longer rules are prefered. */
X
X/* An expression which will not live on the momentary obstack. */
Xnonmomentary_expr:
X { $$ = suspend_momentary (); } expr
X { resume_momentary ($1); $$ = $2; }
X
X/* A declarator that is allowed only after an explicit typespec. */
X/* may all be followed by prec '.' */
Xafter_type_declarator:
X after_type_declarator '(' nonnull_exprlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | after_type_declarator '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | after_type_declarator LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, empty_parms (), $3); }
X | after_type_declarator '(' error ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, NULL_TREE, NULL_TREE); }
X | after_type_declarator '[' nonmomentary_expr ']'
X { $$ = build_parse_node (ARRAY_REF, $1, $3); }
X | after_type_declarator '[' ']'
X { $$ = build_parse_node (ARRAY_REF, $1, NULL_TREE); }
X | '(' dummy_decl after_type_declarator_no_typename ')'
X { $$ = $3; }
X | '(' '*' type_quals after_type_declarator ')'
X { $$ = make_pointer_declarator ($3, $4); }
X | PAREN_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_REF_PAREN
X { $$ = $1;
X see_typename (); }
X | '(' '&' type_quals after_type_declarator ')'
X { $$ = make_reference_declarator ($3, $4); }
X | '*' type_quals after_type_declarator %prec UNARY
X { $$ = make_pointer_declarator ($2, $3); }
X | '&' type_quals after_type_declarator %prec UNARY
X { $$ = make_reference_declarator ($2, $3); }
X | TYPENAME
X ;
X
Xafter_type_declarator_no_typename:
X after_type_declarator_no_typename '(' nonnull_exprlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | after_type_declarator_no_typename '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | after_type_declarator_no_typename LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, empty_parms (), $3); }
X | after_type_declarator_no_typename '(' error ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, NULL_TREE, NULL_TREE); }
X | after_type_declarator_no_typename '[' nonmomentary_expr ']'
X { $$ = build_parse_node (ARRAY_REF, $1, $3); }
X | after_type_declarator_no_typename '[' ']'
X { $$ = build_parse_node (ARRAY_REF, $1, NULL_TREE); }
X | '(' dummy_decl after_type_declarator_no_typename ')'
X { $$ = $3; }
X | PAREN_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_REF_PAREN
X { $$ = $1;
X see_typename (); }
X | '*' type_quals after_type_declarator %prec UNARY
X { $$ = make_pointer_declarator ($2, $3); }
X | '&' type_quals after_type_declarator %prec UNARY
X { $$ = make_reference_declarator ($2, $3); }
X ;
X
X/* A declarator allowed whether or not there has been
X an explicit typespec. These cannot redeclare a typedef-name. */
X
Xnotype_declarator:
X notype_declarator '(' nonnull_exprlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | notype_declarator '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | notype_declarator LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, empty_parms (), $3); }
X | notype_declarator '(' error ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, NULL_TREE, NULL_TREE); }
X | '(' notype_declarator ')'
X { $$ = $2; }
X | '*' type_quals notype_declarator %prec UNARY
X { $$ = make_pointer_declarator ($2, $3); }
X | '&' type_quals notype_declarator %prec UNARY
X { $$ = make_reference_declarator ($2, $3); }
X | notype_declarator '[' nonmomentary_expr ']'
X { $$ = build_parse_node (ARRAY_REF, $1, $3); }
X | notype_declarator '[' ']'
X { $$ = build_parse_node (ARRAY_REF, $1, NULL_TREE); }
X | IDENTIFIER
X { see_typename (); }
X
X /* C++ extensions. */
X | operator_name
X { see_typename (); }
X
X | '~' TYPENAME
X { see_typename ();
X $$ = build_parse_node (BIT_NOT_EXPR, $2); }
X | '~' IDENTIFIER
X { see_typename ();
X $$ = build_parse_node (BIT_NOT_EXPR, $2); }
X | LEFT_RIGHT identifier
X {
X see_typename ();
X $$ = build_parse_node (WRAPPER_EXPR, $2);
X }
X | LEFT_RIGHT '?' identifier
X {
X see_typename ();
X $$ = build_parse_node (WRAPPER_EXPR,
X build_parse_node (COND_EXPR, $3, NULL_TREE, NULL_TREE));
X }
X | '~' LEFT_RIGHT identifier
X { see_typename ();
X $$ = build_parse_node (ANTI_WRAPPER_EXPR, $3); }
X | TYPENAME_SCOPE type_quals notype_declarator %prec '('
X { see_typename ();
X $$ = build_parse_node (SCOPE_REF, $1, $3); }
X | TYPENAME_SCOPE TYPENAME %prec '('
X { $$ = build_parse_node (SCOPE_REF, $1, $2); }
X | TYPENAME_SCOPE see_typename TYPENAME '(' nonnull_exprlist ')' type_quals %prec '.'
X { $$ = build_parse_node (SCOPE_REF, $1,
X build_parse_node (CALL_EXPR, $3, $5, $7)); }
X | TYPENAME_SCOPE see_typename TYPENAME '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (SCOPE_REF, $1,
X build_parse_node (CALL_EXPR, $3, $5, $7)); }
X | TYPENAME_SCOPE see_typename TYPENAME LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (SCOPE_REF, $1,
X build_parse_node (CALL_EXPR, $3, empty_parms (), $5)); }
X | TYPENAME_SCOPE see_typename TYPENAME '(' error ')' type_quals %prec '.'
X { $$ = build_parse_node (SCOPE_REF, $1, build_parse_node (CALL_EXPR, $3, NULL_TREE, NULL_TREE)); }
X | SCOPE see_typename notype_declarator
X { $$ = build_parse_node (SCOPE_REF, NULL_TREE, $3); }
X ;
X
Xscoped_identifier:
X TYPENAME_SCOPE
X | IDENTIFIER SCOPE
X | scoped_identifier TYPENAME_SCOPE
X { $$ = build_parse_node (SCOPE_REF, $1, $2); }
X ;
X
Xabsdcl1: /* a nonempty abstract declarator */
X '(' absdcl1 ')'
X { see_typename ();
X $$ = $2; }
X /* `(typedef)1' is `int'. */
X | '*' type_quals absdcl1 %prec EMPTY
X { $$ = make_pointer_declarator ($2, $3); }
X | '*' type_quals %prec EMPTY
X { $$ = make_pointer_declarator ($2, NULL_TREE); }
X | PAREN_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_STAR_PAREN
X { $$ = $1;
X see_typename (); }
X | PAREN_X_SCOPE_REF_PAREN
X { $$ = $1;
X see_typename (); }
X | '&' type_quals absdcl1 %prec EMPTY
X { $$ = make_reference_declarator ($2, $3); }
X | '&' type_quals %prec EMPTY
X { $$ = make_reference_declarator ($2, NULL_TREE); }
X | absdcl1 '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, $3, $5); }
X | absdcl1 LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, $1, empty_parms (), $3); }
X | absdcl1 '[' nonmomentary_expr ']' %prec '.'
X { $$ = build_parse_node (ARRAY_REF, $1, $3); }
X | absdcl1 '[' ']' %prec '.'
X { $$ = build_parse_node (ARRAY_REF, $1, NULL_TREE); }
X | '(' parmlist ')' type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, NULL_TREE, $2, $4); }
X | LEFT_RIGHT type_quals %prec '.'
X { $$ = build_parse_node (CALL_EXPR, NULL_TREE, empty_parms (), $2); }
X | '[' nonmomentary_expr ']' %prec '.'
X { $$ = build_parse_node (ARRAY_REF, NULL_TREE, $2); }
X | '[' ']' %prec '.'
X { $$ = build_parse_node (ARRAY_REF, NULL_TREE, NULL_TREE); }
X | TYPENAME_SCOPE type_quals absdcl1 %prec EMPTY
X { $$ = build_parse_node (SCOPE_REF, $1, $3); }
X | IDENTIFIER SCOPE type_quals absdcl1 %prec EMPTY
X { $$ = build_parse_node (SCOPE_REF, $1, $4); }
X | TYPENAME_SCOPE type_quals %prec EMPTY
X { $$ = build_parse_node (SCOPE_REF, $1, 0); }
X | IDENTIFIER SCOPE type_quals %prec EMPTY
X { $$ = build_parse_node (SCOPE_REF, $1, 0); }
X ;
X
X/* For C++, decls and stmts can be intermixed, so we don't need to
X have a special rule that won't start parsing the stmt section
X until we have a stmt that parses without errors. */
X
Xstmts:
X stmt
X | errstmt
X | stmts stmt
X | stmts errstmt
X ;
X
Xerrstmt: error ';'
X ;
X
X/* build the LET_STMT node before parsing its contents,
X so that any LET_STMTs within the context can have their display pointers
X set up to point at this one. */
X
X.pushlevel: /* empty */
X {
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X stmt_decl_msg = 0;
X }
X ;
X
X/* This is the body of a function definition.
X It causes syntax errors to ignore to the next openbrace. */
Xcompstmt_or_error:
X compstmt
X {}
X | error compstmt
X ;
X
Xcompstmt: '{' '}'
X { $$ = 0; }
X | '{' .pushlevel stmts '}'
X { tree decls;
X
X pop_implicit_try_blocks (NULL_TREE);
X decls = getdecls ();
X expand_end_bindings (decls, decls != 0, 1);
X $$ = poplevel (decls != 0, 1, 0);
X pop_momentary (); }
X | '{' .pushlevel error '}'
X { pop_implicit_try_blocks (NULL_TREE);
X expand_end_bindings (getdecls (), 0, 1);
X $$ = poplevel (0, 0, 0);
X pop_momentary (); }
X ;
X
Xsimple_if:
X IF '(' expr ')'
X { emit_line_note (input_filename, lineno);
X expand_start_cond (truthvalue_conversion ($3), 0);
X stmt_decl_msg = "if"; }
X stmt
X { stmt_decl_msg = 0; }
X ;
X
Xstmt:
X compstmt
X { finish_stmt (); }
X | decl
X { if (stmt_decl_msg)
X error ("declaration after %s invalid", stmt_decl_msg);
X stmt_decl_msg = 0;
X finish_stmt (); }
X | expr ';'
X { emit_line_note (input_filename, lineno);
X /* Do default conversion if safe and possibly important,
X in case within ({...}). */
X if ((TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE
X && lvalue_p ($1))
X || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE)
X $1 = default_conversion ($1);
X cplus_expand_expr_stmt ($1);
X clear_momentary ();
X finish_stmt (); }
X | simple_if ELSE
X { expand_start_else ();
X stmt_decl_msg = "else"; }
X stmt
X { expand_end_else ();
X stmt_decl_msg = 0;
X finish_stmt (); }
X | simple_if %prec IF
X { expand_end_cond ();
X stmt_decl_msg = 0;
X finish_stmt (); }
X | WHILE
X { emit_nop ();
X emit_line_note (input_filename, lineno);
X expand_start_loop (1); }
X '(' expr ')'
X { expand_exit_loop_if_false (truthvalue_conversion ($4));
X stmt_decl_msg = "while"; }
X stmt
X {
X expand_end_loop ();
X stmt_decl_msg = 0;
X finish_stmt (); }
X | DO
X { emit_nop ();
X emit_line_note (input_filename, lineno);
X expand_start_loop_continue_elsewhere (1);
X stmt_decl_msg = "do"; }
X stmt WHILE
X { stmt_decl_msg = 0;
X expand_loop_continue_here (); }
X '(' expr ')' ';'
X { emit_line_note (input_filename, lineno);
X expand_exit_loop_if_false (truthvalue_conversion ($7));
X expand_end_loop ();
X clear_momentary ();
X finish_stmt (); }
X | forhead.1
X { emit_nop ();
X emit_line_note (input_filename, lineno);
X if ($1) cplus_expand_expr_stmt ($1);
X expand_start_loop_continue_elsewhere (1); }
X xexpr ';'
X { emit_line_note (input_filename, lineno);
X if ($3) expand_exit_loop_if_false (truthvalue_conversion ($3)); }
X xexpr ')'
X /* Don't let the tree nodes for $6 be discarded
X by clear_momentary during the parsing of the next stmt. */
X { push_momentary ();
X stmt_decl_msg = "for"; }
X stmt
X { emit_line_note (input_filename, lineno);
X expand_loop_continue_here ();
X if ($6) cplus_expand_expr_stmt ($6);
X pop_momentary ();
X expand_end_loop ();
X stmt_decl_msg = 0;
X finish_stmt (); }
X | forhead.2
X { emit_nop ();
X emit_line_note (input_filename, lineno);
X expand_start_loop_continue_elsewhere (1); }
X xexpr ';'
X { emit_line_note (input_filename, lineno);
X if ($3) expand_exit_loop_if_false (truthvalue_conversion ($3)); }
X xexpr ')'
X /* Don't let the tree nodes for $6 be discarded
X by clear_momentary during the parsing of the next stmt. */
X { push_momentary ();
X stmt_decl_msg = "for";
X $7 = lineno; }
X stmt
X { emit_line_note (input_filename, $7);
X expand_loop_continue_here ();
X if ($6) cplus_expand_expr_stmt ($6);
X pop_momentary ();
X expand_end_loop ();
X pop_implicit_try_blocks (NULL_TREE);
X if ($1)
X {
X register keep = $1 > 0;
X if (keep) expand_end_bindings (0, keep, 1);
X poplevel (keep, 1, 0);
X pop_momentary ();
X }
X stmt_decl_msg = 0;
X finish_stmt ();
X }
X | SWITCH '(' expr ')'
X { emit_line_note (input_filename, lineno);
X c_expand_start_case ($3);
X /* Don't let the tree nodes for $3 be discarded by
X clear_momentary during the parsing of the next stmt. */
X push_momentary ();
X stmt_decl_msg = "switch"; }
X stmt
X { expand_end_case ($3);
X pop_momentary ();
X stmt_decl_msg = 0;
X finish_stmt (); }
X | CASE expr ':'
X { register tree value = $2;
X register tree label
X = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value) == NOP_EXPR
X && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0)))
X value = TREE_OPERAND (value, 0);
X
X if (TREE_READONLY_DECL_P (value))
X {
X value = decl_constant_value (value);
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value) == NOP_EXPR
X && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0)))
X value = TREE_OPERAND (value, 0);
X }
X value = fold (value);
X
X if (TREE_CODE (value) != INTEGER_CST
X && value != error_mark_node)
X {
X error ("case label does not reduce to an integer constant");
X value = error_mark_node;
X }
X else
X /* Promote char or short to int. */
X value = default_conversion (value);
X if (value != error_mark_node)
X {
X int success = pushcase (value, label);
X if (success == 1)
X error ("case label not within a switch statement");
X else if (success == 2)
X error ("duplicate case value");
X else if (success == 3)
X warning ("case value out of range");
X }
X define_case_label (label);
X }
X stmt
X | CASE expr RANGE expr ':'
X { register tree value1 = $2;
X register tree value2 = $4;
X register tree label
X = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X
X if (pedantic)
X {
X error ("ANSI C does not allow range expressions in switch statement");
X value1 = error_mark_node;
X value2 = error_mark_node;
X break;
X }
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value1) == NOP_EXPR
X && TREE_TYPE (value1) == TREE_TYPE (TREE_OPERAND (value1, 0)))
X value1 = TREE_OPERAND (value1, 0);
X
X if (TREE_READONLY_DECL_P (value1))
X {
X value1 = decl_constant_value (value1);
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value1) == NOP_EXPR
X && TREE_TYPE (value1) == TREE_TYPE (TREE_OPERAND (value1, 0)))
X value1 = TREE_OPERAND (value1, 0);
X }
X value1 = fold (value1);
X
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value2) == NOP_EXPR
X && TREE_TYPE (value2) == TREE_TYPE (TREE_OPERAND (value2, 0)))
X value2 = TREE_OPERAND (value2, 0);
X
X if (TREE_READONLY_DECL_P (value2))
X {
X value2 = decl_constant_value (value2);
X /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
X Strip such NOP_EXPRs. */
X if (TREE_CODE (value2) == NOP_EXPR
X && TREE_TYPE (value2) == TREE_TYPE (TREE_OPERAND (value2, 0)))
X value2 = TREE_OPERAND (value2, 0);
X }
X value2 = fold (value2);
X
X
X if (TREE_CODE (value1) != INTEGER_CST
X && value1 != error_mark_node)
X {
X error ("case label does not reduce to an integer constant");
X value1 = error_mark_node;
X }
X if (TREE_CODE (value2) != INTEGER_CST
X && value2 != error_mark_node)
X {
X error ("case label does not reduce to an integer constant");
X value2 = error_mark_node;
X }
X if (value1 != error_mark_node
X && value2 != error_mark_node)
X {
X int success = pushcase_range (value1, value2, label);
X if (success == 1)
X error ("case label not within a switch statement");
X else if (success == 2)
X error ("duplicate (or overlapping) case value");
X else if (success == 3)
X warning ("case value out of range");
X else if (success == 4)
X warning ("empty range specified");
X }
X define_case_label (label);
X }
X stmt
X | DEFAULT ':'
X {
X register tree label
X = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X int success = pushcase (NULL_TREE, label);
X if (success == 1)
X error ("default label not within a switch statement");
X else if (success == 2)
X error ("multiple default labels in one switch");
X }
X stmt
X | BREAK ';'
X { emit_line_note (input_filename, lineno);
X if ( ! expand_exit_something ())
X error ("break statement not within loop or switch"); }
X | CONTINUE ';'
X { emit_line_note (input_filename, lineno);
X if (! expand_continue_loop ())
X error ("continue statement not within a loop"); }
X | RETURN ';'
X { emit_line_note (input_filename, lineno);
X c_expand_return (NULL_TREE); }
X | RETURN expr ';'
X { emit_line_note (input_filename, lineno);
X c_expand_return ($2);
X finish_stmt ();
X }
X | ASM maybe_type_qual '(' string ')' ';'
X { if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
X emit_line_note (input_filename, lineno);
X expand_asm ($4);
X finish_stmt ();
X }
X /* This is the case with just output operands. */
X | ASM maybe_type_qual '(' string ':' asm_operands ')' ';'
X { if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
X emit_line_note (input_filename, lineno);
X c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE,
X $2 == ridpointers[(int)RID_VOLATILE],
X input_filename, lineno);
X finish_stmt ();
X }
X /* This is the case with input operands as well. */
X | ASM maybe_type_qual '(' string ':' asm_operands ':' asm_operands ')' ';'
X { if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
X emit_line_note (input_filename, lineno);
X c_expand_asm_operands ($4, $6, $8, NULL_TREE,
X $2 == ridpointers[(int)RID_VOLATILE],
X input_filename, lineno);
X finish_stmt ();
X }
X /* This is the case with clobbered registers as well. */
X | ASM maybe_type_qual '(' string ':' asm_operands ':'
X asm_operands ':' asm_clobbers ')' ';'
X { if (TREE_CHAIN ($4)) $4 = combine_strings ($4);
X emit_line_note (input_filename, lineno);
X c_expand_asm_operands ($4, $6, $8, $10,
X $2 == ridpointers[(int)RID_VOLATILE],
X input_filename, lineno);
X finish_stmt ();
X }
X | GOTO identifier ';'
X { tree decl;
X emit_line_note (input_filename, lineno);
X decl = lookup_label ($2);
X TREE_USED (decl) = 1;
X expand_goto (decl); }
X | IDENTIFIER ':'
X { tree label = define_label (input_filename, lineno, $1);
X emit_nop ();
X if (label)
X expand_label (label); }
X stmt
X { finish_stmt (); }
X | TYPENAME_COLON
X { tree label = define_label (input_filename, lineno, $1);
X if (label)
X expand_label (label); }
X stmt
X { finish_stmt (); }
X | ';'
X { finish_stmt (); }
X
X /* Exception handling extentions. */
X | RAISE raise_identifier '(' nonnull_exprlist ')' ';'
X { cplus_expand_raise ($2, $4, NULL_TREE);
X finish_stmt (); }
X | RAISE raise_identifier LEFT_RIGHT ';'
X { cplus_expand_raise ($2, NULL_TREE, NULL_TREE);
X finish_stmt (); }
X | RAISE identifier ';'
X { cplus_expand_reraise ($2);
X finish_stmt (); }
X | try EXCEPT identifier '{'
X {
X tree decl = cplus_expand_end_try ($1);
X $2 = current_exception_type;
X $4 = current_exception_decl;
X $$ = current_exception_object;
X cplus_expand_start_except ($3, decl);
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X stmt_decl_msg = 0;
X }
X except_stmts '}'
X {
X tree decls = getdecls ();
X tree block;
X /* If there is a default exception to handle,
X handle it here. */
X if ($6)
X {
X tree decl = build_decl (CPLUS_CATCH_DECL, NULL_TREE, 0);
X
X pushlevel (1);
X expand_start_bindings (0);
X expand_expr ($6, 0, 0, 0);
X expand_end_bindings (0, 1, 0);
X block = poplevel (1, 0, 0);
X
X /* This is a catch block. */
X TREE_LANG_FLAG_2 (block) = 1;
X STMT_VARS (block) = decl;
X }
X
X expand_end_bindings (decls, 1, 1);
X block = poplevel (1, 1, 0);
X TREE_LANG_FLAG_3 (block) = 1;
X pop_momentary ();
X current_exception_type = $2;
X current_exception_decl = $4;
X current_exception_object = $5;
X cplus_expand_end_except ($6);
X }
X | try RERAISE raise_identifiers /* ';' checked for at bottom. */
X { tree name = get_identifier ("(compiler error)");
X tree orig_ex_type = current_exception_type;
X tree orig_ex_decl = current_exception_decl;
X tree orig_ex_obj = current_exception_object;
X tree decl = cplus_expand_end_try ($1), decls;
X tree block;
X
X /* Start hidden EXCEPT. */
X cplus_expand_start_except (name, decl);
X pushlevel (0);
X clear_last_expr ();
X push_momentary ();
X expand_start_bindings (0);
X stmt_decl_msg = 0;
X
X /* This sets up the reraise. */
X cplus_expand_reraise ($3);
X
X decls = getdecls ();
X expand_end_bindings (decls, 1, 1);
X block = poplevel (1, 1, 0);
X TREE_LANG_FLAG_3 (block) = 1;
X pop_momentary ();
X current_exception_type = orig_ex_type;
X current_exception_decl = orig_ex_decl;
X current_exception_object = orig_ex_obj;
X /* This will reraise for us. */
X cplus_expand_end_except (error_mark_node);
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X if (yychar != ';')
X error ("missing ';' after reraise statement");
X }
X | try %prec EMPTY
X { yyerror ("`except' missing after `try' statement");
X /* Terminate the binding contour started by special
X code in `.pushlevel'. Automagically pops off
X the conditional we started for `try' stmt. */
X cplus_expand_end_try ($1);
X expand_end_bindings (0, 0, 1);
X poplevel (0, 0, 0);
X pop_momentary ();
X YYERROR; }
X ;
X
Xtry: TRY '{' '}'
X { $$ = 0; }
X | try_head stmts '}'
X {
X $$ = 1;
X pop_implicit_try_blocks (NULL_TREE);
X }
X | try_head error '}'
X {
X $$ = 0;
X pop_implicit_try_blocks (NULL_TREE);
X }
X ;
X
Xtry_head: TRY '{' { cplus_expand_start_try (0); } .pushlevel
X
Xexcept_stmts:
X /* empty */
X { $$ = NULL_TREE; }
X | except_stmts raise_identifier
X {
X tree type = lookup_exception_type (current_class_type, current_class_name, $2);
X if (type == NULL_TREE)
X {
X error ("`%s' is not an exception type",
X IDENTIFIER_POINTER (TREE_VALUE ($2)));
X current_exception_type = NULL_TREE;
X TREE_TYPE (current_exception_object) = error_mark_node;
X }
X else
X {
X current_exception_type = type;
X /* In-place union. */
X TREE_TYPE (current_exception_object) = type;
X }
X $2 = cplus_expand_start_catch ($2);
X pushlevel (1);
X expand_start_bindings (0);
X }
X compstmt
X {
X expand_end_bindings (0, 1, 0);
X $4 = poplevel (1, 0, 0);
X
X cplus_expand_end_catch (0);
X
X /* Mark this as a catch block. */
X TREE_LANG_FLAG_2 ($4) = 1;
X if ($2 != error_mark_node)
X {
X tree decl = build_decl (CPLUS_CATCH_DECL, DECL_NAME ($2), 0);
X DECL_RTL (decl) = DECL_RTL ($2);
X TREE_CHAIN (decl) = STMT_VARS ($4);
X STMT_VARS ($4) = decl;
X }
X }
X | except_stmts DEFAULT
X {
X if ($1)
X error ("duplicate default in exception handler");
X current_exception_type = NULL_TREE;
X /* Takes it right out of scope. */
X TREE_TYPE (current_exception_object) = error_mark_node;
X
X if (! expand_catch_default ())
X compiler_error ("default catch botch");
X
X /* The default exception is handled as the
X last in the chain of exceptions handled. */
X do_pending_stack_adjust ();
X start_sequence ();
X $1 = make_node (RTL_EXPR);
X TREE_TYPE ($1) = void_type_node;
X }
X compstmt
X {
X do_pending_stack_adjust ();
X if (! expand_catch (NULL_TREE))
X compiler_error ("except nesting botch");
X if (! expand_end_catch ())
X compiler_error ("except nesting botch");
X RTL_EXPR_SEQUENCE ($1) = (struct rtx_def *)get_insns ();
X if ($4)
X {
X /* Mark this block as the default catch block. */
X TREE_LANG_FLAG_1 ($4) = 1;
X TREE_LANG_FLAG_2 ($4) = 1;
X }
X end_sequence ();
X }
X ;
X
Xforhead.1:
X FOR '(' ';'
X { $$ = NULL_TREE; }
X | FOR '(' expr ';'
X { $$ = $3; }
X | FOR '(' '{' '}'
X { $$ = NULL_TREE; }
X ;
X
Xforhead.2:
X FOR '(' decl
X { $$ = 0; }
X | FOR '(' error ';'
X { $$ = 0; }
X | FOR '(' '{' .pushlevel stmts '}'
X { $$ = 1; }
X | FOR '(' '{' .pushlevel error '}'
X { $$ = -1; }
X ;
X
X/* Either a type-qualifier or nothing. First thing in an `asm' statement. */
X
Xmaybe_type_qual:
X /* empty */
X { if (pedantic)
X warning ("ANSI C forbids use of `asm' keyword");
X emit_line_note (input_filename, lineno); }
X | TYPE_QUAL
X { if (pedantic)
X warning ("ANSI C forbids use of `asm' keyword");
X emit_line_note (input_filename, lineno); }
X ;
X
Xxexpr:
X /* empty */
X { $$ = NULL_TREE; }
X | expr
X | error
X { $$ = NULL_TREE; }
X ;
X
X/* These are the operands other than the first string and colon
X in asm ("addextend %2,%1": "=dm" (x), "0" (y), "g" (*x)) */
Xasm_operands: /* empty */
X { $$ = NULL_TREE; }
X | nonnull_asm_operands
X ;
X
Xnonnull_asm_operands:
X asm_operand
X | nonnull_asm_operands ',' asm_operand
X { $$ = chainon ($1, $3); }
X ;
X
Xasm_operand:
X STRING '(' expr ')'
X { $$ = build_tree_list ($1, $3); }
X ;
X
Xasm_clobbers:
X STRING
X { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); }
X | asm_clobbers ',' STRING
X { $$ = tree_cons (NULL_TREE, $3, $1); }
X ;
X
X/* This is what appears inside the parens in a function declarator.
X Its value is represented in the format that grokdeclarator expects.
X
X In C++, declaring a function with no parameters
X means that that function takes *no* parameters. */
Xparmlist: /* empty */
X {
X if (strict_prototype)
X $$ = void_list_node;
X else
X $$ = NULL_TREE;
X }
X | parms
X {
X $$ = chainon ($1, void_list_node);
X TREE_PARMLIST ($$) = 1;
X }
X | parms ',' ELLIPSIS
X {
X $$ = $1;
X TREE_PARMLIST ($$) = 1;
X }
X /* C++ allows an ellipsis without a separating ',' */
X | parms ELLIPSIS
X {
X $$ = $1;
X TREE_PARMLIST ($$) = 1;
X }
X | ELLIPSIS
X {
X $$ = NULL_TREE;
X }
X | TYPENAME_ELLIPSIS
X {
X $$ = $1;
X TREE_PARMLIST ($$) = 1;
X }
X | parms TYPENAME_ELLIPSIS
X {
X $$ = $1;
X TREE_PARMLIST ($$) = 1;
X }
X | parms ':'
X {
X /* This helps us recover from really nasty
X parse errors, for example, a missing right
X parenthesis. */
X yyerror ("possibly missing ')'");
X $$ = chainon ($1, void_list_node);
X TREE_PARMLIST ($$) = 1;
X yyungetc (':', 0);
X yychar = ')';
X }
X ;
X
X/* A nonempty list of parameter declarations or type names. */
Xparms:
X parm opt.init
X { $$ = build_tree_list ($2, $1); }
X | parms ',' parm opt.init
X { $$ = chainon ($1, build_tree_list ($4, $3)); }
X | parms ',' bad_parm opt.init
X { $$ = chainon ($1, build_tree_list ($4, $3)); }
X ;
X
X/* A single parameter declaration or parameter type name,
X as found in a parmlist. */
Xparm:
X typed_declspecs dont_see_typename notype_declarator
X { $$ = build_tree_list ($1, $3);
X see_typename (); }
X | typed_declspecs dont_see_typename absdcl
X { $$ = build_tree_list ($1, $3);
X see_typename (); }
X | declmods dont_see_typename notype_declarator
X { $$ = build_tree_list ($1, $3);
X see_typename (); }
X | declmods dont_see_typename absdcl
X { $$ = build_tree_list ($1, $3);
X see_typename (); }
X ;
X
Xsee_typename: type_quals
X { see_typename (); }
X ;
X
Xdont_see_typename: /* empty */
X { dont_see_typename (); }
X ;
X
Xbad_parm:
X dummy_decl notype_declarator
X {
X warning ("type specifier omitted for parameter");
X $$ = build_tree_list (TREE_PURPOSE (TREE_VALUE ($-1)), $2);
X }
X | dummy_decl absdcl
X {
X warning ("type specifier omitted for parameter");
X $$ = build_tree_list (TREE_PURPOSE (TREE_VALUE ($-1)), $2);
X }
X ;
X
X /* C++ extension: allow for initialization */
Xopt.init:
X /* empty */
X { $$ = NULL_TREE; }
X | '=' init
X { $$ = $2; }
X ;
X
Xmaybe_raises:
X /* empty */
X { $$ = NULL_TREE; }
X | RAISES raise_identifiers %prec EMPTY
X { $$ = $2; }
X ;
X
Xraise_identifier:
X ALL
X { $$ = void_list_node; }
X | IDENTIFIER
X { $$ = build_decl_list (NULL_TREE, $1); }
X | TYPENAME
X { $$ = build_decl_list (NULL_TREE, $1); }
X | SCOPE IDENTIFIER
X { $$ = build_decl_list (void_type_node, $2); }
X | SCOPE TYPENAME
X { $$ = build_decl_list (void_type_node, $2); }
X | scoped_identifier IDENTIFIER
X { $$ = build_decl_list ($1, $2); }
X | scoped_identifier TYPENAME
X { $$ = build_decl_list ($1, $2); }
X
Xraise_identifiers:
X raise_identifier
X | raise_identifiers ',' raise_identifier
X {
X TREE_CHAIN ($3) = $1;
X $$ = $3;
X }
X ;
X
Xoperator_name:
X OPERATOR '*'
X { $$ = build_opid (0, MULT_EXPR); }
X | OPERATOR '/'
X { $$ = build_opid (0, TRUNC_DIV_EXPR); }
X | OPERATOR '%'
X { $$ = build_opid (0, TRUNC_MOD_EXPR); }
X | OPERATOR '+'
X { $$ = build_opid (0, PLUS_EXPR); }
X | OPERATOR '-'
X { $$ = build_opid (0, MINUS_EXPR); }
X | OPERATOR '&'
X { $$ = build_opid (0, BIT_AND_EXPR); }
X | OPERATOR '|'
X { $$ = build_opid (0, BIT_IOR_EXPR); }
X | OPERATOR '^'
X { $$ = build_opid (0, BIT_XOR_EXPR); }
X | OPERATOR '~'
X { $$ = build_opid (0, BIT_NOT_EXPR); }
X | OPERATOR ','
X { $$ = build_opid (0, COMPOUND_EXPR); }
X | OPERATOR ARITHCOMPARE
X { $$ = build_opid (0, $2); }
X | OPERATOR EQCOMPARE
X { $$ = build_opid (0, $2); }
X | OPERATOR ASSIGN
X { $$ = build_opid (MODIFY_EXPR, $2); }
X | OPERATOR '='
X {
X $$ = build_opid (MODIFY_EXPR, NOP_EXPR);
X if (current_class_type)
X {
X TYPE_HAS_ASSIGNMENT (current_class_type) = 1;
X TYPE_GETS_ASSIGNMENT (current_class_type) = 1;
X }
X }
X | OPERATOR LSHIFT
X { $$ = build_opid (0, $2); }
X | OPERATOR RSHIFT
X { $$ = build_opid (0, $2); }
X | OPERATOR PLUSPLUS
X { $$ = build_opid (0, POSTINCREMENT_EXPR); }
X | OPERATOR MINUSMINUS
X { $$ = build_opid (0, PREDECREMENT_EXPR); }
X | OPERATOR ANDAND
X { $$ = build_opid (0, TRUTH_ANDIF_EXPR); }
X | OPERATOR OROR
X { $$ = build_opid (0, TRUTH_ORIF_EXPR); }
X | OPERATOR '!'
X { $$ = build_opid (0, TRUTH_NOT_EXPR); }
X | OPERATOR '?' ':'
X { $$ = build_opid (0, COND_EXPR); }
X | OPERATOR MIN_MAX
X { $$ = build_opid (0, $2); }
X | OPERATOR POINTSAT %prec EMPTY
X { $$ = build_opid (0, COMPONENT_REF); }
X | OPERATOR POINTSAT_LEFT_RIGHT type_quals %prec '.'
X {
X if (yychar == YYEMPTY)
X yychar = YYLEX;
X if (yychar == '(' || yychar == LEFT_RIGHT)
X {
X $$ = build_opid (0, METHOD_CALL_EXPR);
X if (current_class_type)
X {
X tree t = current_class_type;
X while (t)
X {
X TYPE_OVERLOADS_METHOD_CALL_EXPR (t) = 1;
X t = TYPE_NEXT_VARIANT (t);
X }
X }
X }
X else
X $$ = build_parse_node (CALL_EXPR, build_opid (0, COMPONENT_REF), void_list_node, $3);
X }
X | OPERATOR LEFT_RIGHT
X { $$ = build_opid (0, CALL_EXPR);
X if (current_class_type)
X {
X tree t = current_class_type;
X while (t)
X {
X TYPE_OVERLOADS_CALL_EXPR (t) = 1;
X t = TYPE_NEXT_VARIANT (t);
X }
X }
X }
X | OPERATOR '[' ']'
X { $$ = build_opid (0, ARRAY_REF);
X if (current_class_type)
X {
X tree t = current_class_type;
X while (t)
X {
X TYPE_OVERLOADS_ARRAY_REF (t) = 1;
X t = TYPE_NEXT_VARIANT (t);
X }
X }
X }
X | OPERATOR NEW
X {
X $$ = build_opid (0, NEW_EXPR);
X if (current_class_type)
X {
X tree t = current_class_type;
X while (t)
X {
X TREE_GETS_NEW (t) = 1;
X t = TYPE_NEXT_VARIANT (t);
X }
X }
X }
X | OPERATOR DELETE
X {
X $$ = build_opid (0, DELETE_EXPR);
X if (current_class_type)
X {
X tree t = current_class_type;
X while (t)
X {
X TREE_GETS_DELETE (t) = 1;
X t = TYPE_NEXT_VARIANT (t);
X }

X }
X }
X
X /* These should do `groktypename' and set up TREE_HAS_X_CONVERSION
X here, rather than doing it in class.c . */
X | OPERATOR typed_typespecs absdcl
X {
X $$ = build1 (TYPE_EXPR, $2, $3);
X }
X | OPERATOR error
X { $$ = build_opid (ERROR_MARK, ERROR_MARK); }
X ;
X
X%%
X
X#if YYDEBUG != 0
Xdb_yyerror (s, yyps, yychar)
X char *s;
X short *yyps;
X int yychar;
X{
X FILE *yyout;
X char buf[1024];
X int st;
X
X yyerror (s);
X printf ("State is %d, input token number is %d.\n", *yyps, yychar);
X
X#ifdef PARSE_OUTPUT
X if (*yyps < 1) fatal ("Cannot start from here");
X else if ((yyout = fopen (PARSE_OUTPUT, "r")) == NULL)
X error ("cannot open file %s", PARSE_OUTPUT);
X else
X {
X printf ("That is to say,\n\n");
X while (fgets(buf, sizeof (buf)-1, yyout))
X {
X if (buf[0] != 's') continue;
X st = atoi (buf+6);
X if (st != *yyps) continue;
X printf ("%s", buf);
X while (fgets (buf, sizeof (buf)-1, yyout))
X {
X if (buf[0] == 's') break;
X printf ("%s", buf);
X }
X break;
X }
X printf ("With the token %s\n", yytname[YYTRANSLATE (yychar)]);
X fclose (yyout);
X }
X#endif
X}
X#endif
X
Xvoid
Xyyerror (string)
X char *string;
X{
X extern FILE *finput;
X extern char *token_buffer;
X char buf[200];
X
X strcpy (buf, string);
X
X /* We can't print string and character constants well
X because the token_buffer contains the result of processing escapes. */
X if (end_of_file || feof (finput))
X strcat (buf, " at end of input");
X else if (token_buffer[0] == 0)
X strcat (buf, " at null character");
X else if (token_buffer[0] == '"')
X strcat (buf, " before string constant");
X else if (token_buffer[0] == '\'')
X strcat (buf, " before character constant");
X else
X strcat (buf, " before `%s'");
X
X error (buf, token_buffer);
X}
X
Xstatic int *reduce_count;
Xstatic int *token_count;
X
X#define REDUCE_LENGTH (sizeof (yyr2) / sizeof (yyr2[0]))
X#define TOKEN_LENGTH (256 + YYNTBASE + 1)
X
Xint *
Xinit_parse ()
X{
X#ifdef GATHER_STATISTICS
X reduce_count = (int *)malloc (sizeof (int) * (REDUCE_LENGTH + 1));
X bzero (reduce_count, sizeof (int) * (REDUCE_LENGTH + 1));
X reduce_count += 1;
X token_count = (int *)malloc (sizeof (int) * (TOKEN_LENGTH + 1));
X bzero (token_count, sizeof (int) * (TOKEN_LENGTH + 1));
X token_count += 1;
X#endif
X return token_count;
X}
X
X#ifdef GATHER_STATISTICS
Xvoid
Xyyhook (yyn)
X int yyn;
X{
X reduce_count[yyn] += 1;
X}
X#endif
X
Xstatic int reduce_cmp (p, q)
X int *p, *q;
X{
X return reduce_count[*q] - reduce_count[*p];
X}
X
Xstatic int token_cmp (p, q)
X int *p, *q;
X{
X return token_count[*q] - token_count[*p];
X}
X
Xvoid
Xprint_parse_statistics ()
X{
X int i;
X int maxlen = REDUCE_LENGTH;
X unsigned *sorted;
X
X if (reduce_count[-1] == 0)
X return;
X
X if (TOKEN_LENGTH > REDUCE_LENGTH)
X maxlen = TOKEN_LENGTH;
X sorted = (unsigned *) alloca (sizeof (int) * maxlen);
X
X for (i = 0; i < TOKEN_LENGTH; i++)
X sorted[i] = i;
X qsort (sorted, TOKEN_LENGTH, sizeof (int), token_cmp);
X for (i = 0; i < TOKEN_LENGTH; i++)
X {
X int index = sorted[i];
X if (token_count[index] == 0)
X break;
X if (token_count[index] < token_count[-1])
X break;
X fprintf (stderr, "token %d", index);
X#if YYDEBUG
X fprintf (stderr, ", `%s'", yytname[YYTRANSLATE (index)]);
X#endif
X fprintf (stderr, ", count = %d\n", token_count[index]);
X }
X fprintf (stderr, "\n");
X for (i = 0; i < REDUCE_LENGTH; i++)
X sorted[i] = i;
X qsort (sorted, REDUCE_LENGTH, sizeof (int), reduce_cmp);
X for (i = 0; i < REDUCE_LENGTH; i++)
X {
X int index = sorted[i];
X if (reduce_count[index] == 0)
X break;
X if (reduce_count[index] < reduce_count[-1])
X break;
X fprintf (stderr, "rule %d", index);
X#if YYDEBUG
X fprintf (stderr, ", line %d", yyrline[index]);
X#endif
X fprintf (stderr, ", count = %d\n", reduce_count[index]);
X }
X fprintf (stderr, "\n");
X}
!EOF
echo "Extracting getpagesize.h..."
sed 's/^X//' >getpagesize.h << '!EOF'
X#ifdef BSD
X#ifndef BSD4_1
X#define HAVE_GETPAGESIZE
X#endif
X#endif
X
X#ifndef HAVE_GETPAGESIZE
X
X#include
X
X#ifdef EXEC_PAGESIZE
X#define getpagesize() EXEC_PAGESIZE
X#else
X#ifdef NBPG
X#define getpagesize() NBPG * CLSIZE
X#ifndef CLSIZE
X#define CLSIZE 1
X#endif /* no CLSIZE */
X#else /* no NBPG */
X#define getpagesize() NBPC
X#endif /* no NBPG */
X#endif /* no EXEC_PAGESIZE */
X
X#endif /* not HAVE_GETPAGESIZE */
!EOF
echo "Extracting stack.h..."
sed 's/^X//' >stack.h << '!EOF'
X/* Stack of data placed on obstacks. */
X
Xstruct stack_level
X{
X /* Pointer back to previous such level. */
X struct stack_level *prev;
X
X /* Point to obstack we should return to. */
X struct obstack *obstack;
X
X /* First place we start putting data. */
X tree *first;
X
X /* Number of entries we can have from `first'.
X Right now we are dumb: if we overflow, abort. */
X int limit;
X};
X
Xstruct stack_level *push_stack_level ();
Xstruct stack_level *pop_stack_level ();
X
!EOF
echo "Extracting collect.c..."
sed 's/^X//' >collect.c << '!EOF'
X/* Output tables which say what global initializers need
X to be called at program startup, and what global destructors
X need to be called at program termination, for GNU C++ compiler.
X Copyright (C) 1987 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X COFF Changes by Dirk Grunwald ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY. No author or distributor
Xaccepts responsibility to anyone for the consequences of using it
Xor for whether it serves any particular purpose or works at all,
Xunless he says so in writing. Refer to the GNU CC General Public
XLicense for full details.
X
XEveryone is granted permission to copy, modify and redistribute
XGNU CC, but only under the conditions described in the
XGNU CC General Public License. A copy of this license is
Xsupposed to have been given to you along with GNU CC so you
Xcan know your rights and responsibilities. It should be in a
Xfile named COPYING. Among other things, the copyright notice
Xand this notice must be preserved on all copies. */
X
X
X/* This file contains all the code needed for the program `collect'.
X
X `collect' is run on all object files that are processed
X by GNU C++, to create a list of all the file-level
X initialization and destruction that need to be performed.
X It generates an assembly file which holds the tables
X which are walked by the global init and delete routines.
X The format of the tables are an integer length,
X followed by the list of function pointers for the
X routines to be called.
X
X Constructors are called in the order they are laid out
X in the table. Destructors are called in the reverse order
X of the way they lie in the table. */
X
X#include "config.h"
Xint target_flags; /* satisfy dependency in config.h */
X
X#ifndef USE_COLLECT
Xmain()
X{
X exit(0);
X}
X
X#else
X
X
X#include
X#include
X
X#include
X#include
X#include
X
X#ifdef UMAX
X#include
X#endif
X
X/*
X * Define various output routines in terms on ASM_INT_OP,
X * which should be defined in config.h -- if it's not, add it
X * as the marker used to allocate an int on your architecture.
X */
X#ifdef i386
X#define ASM_INT_OP ASM_LONG
X#define NO_UNDERSCORES 1
X#endif
X
X#ifdef mips
X#define NO_UNDERSCORES 1
X/*
X * provide stubs for various things in tm-mips.h
X */
Xenum mips_sections cur_section;
Xint inside_function = 0;
Xint num_source_filenames = 0;
Xvoid print_options() { return; };
Xvoid data_section() { return; };
XFILE *asm_out_text_file, *asm_out_data_file;
Xvoid pfatal_with_name(str)
Xchar *str;
X{
X fprintf(stderr,"collect: %s\n", str);
X exit(1);
X}
Xvoid pic_label_is_global () {return;};
X#endif
X
X#ifndef ASM_OUTPUT_INT_CONST
X#define ASM_OUTPUT_INT_CONST(FILE,VALUE) \
X fprintf(FILE,"\t%s %d\n", ASM_INT_OP, VALUE)
X#endif
X
X#ifndef ASM_OUTPUT_LABELREF_AS_INT
X#define ASM_OUTPUT_LABELREF_AS_INT(FILE,NAME) \
X (fprintf(FILE,"\t%s", ASM_INT_OP), \
X ASM_OUTPUT_LABELREF(FILE,NAME), \
X fprintf(FILE,"\n"))
X#endif
X
X#ifndef ASM_OUTPUT_PTR_INT_SUM
X#define ASM_OUTPUT_PTR_INT_SUM(FILE,NAME,VALUE) \
X (fprintf(FILE,"\t%s", ASM_INT_OP), \
X ASM_OUTPUT_LABELREF(FILE,NAME), \
X fprintf(FILE,"+%d\n", VALUE))
X#endif
X
X#ifndef ASM_OUTPUT_SOURCE_FILENAME
X#define ASM_OUTPUT_SOURCE_FILENAME(FILE,NAME)\
X fprintf (FILE, "\t.file\t\"%s\"\n", NAME);
X#endif
X
Xextern int xmalloc ();
Xextern void free ();
X
X#ifndef CTOR_TABLE_NAME
X#define CTOR_TABLE_NAME "__CTOR_LIST__"
X#endif
X
X#ifndef DTOR_TABLE_NAME
X#define DTOR_TABLE_NAME "__DTOR_LIST__"
X#endif
X
X/* Define or undef this in your config.h. Should be defined for
X * MIPS & Sun-386i.
X */
X
X#ifdef NO_UNDERSCORES
X# ifndef CTOR_DTOR_MARKER_NAME
X# ifndef NO_DOLLAR_IN_LABEL
X# define CTOR_DTOR_MARKER_NAME "_GLOBAL_$"
X# else
X# define CTOR_DTOR_MARKER_NAME "_GLOBAL_."
X# endif
X# define CTOR_DTOR_MARKER_LENGTH 9
X# define CTOR_DTOR_MARKER_OFFSET 0
X# endif
X#else
X# ifndef CTOR_DTOR_MARKER_NAME
X# ifndef NO_DOLLAR_IN_LABEL
X# define CTOR_DTOR_MARKER_NAME "__GLOBAL_$"
X# else
X# define CTOR_DTOR_MARKER_NAME "__GLOBAL_."
X# endif
X# define CTOR_DTOR_MARKER_LENGTH 10
X# define CTOR_DTOR_MARKER_OFFSET 1
X# endif
X#endif
X
Xenum error_code { OK, BAD_MAGIC, NO_NAMELIST,
X FOPEN_ERROR, FREAD_ERROR, FWRITE_ERROR,
X RANDOM_ERROR, };
X
Xenum error_code process ();
Xenum error_code process_a (), process_o ();
Xenum error_code output_ctor_dtor_table ();
Xvoid assemble_name ();
X
X/* Files for holding the assembly code for table of constructor
X function pointer addresses and list of destructor
X function pointer addresses. */
Xstatic FILE *outfile;
X
X/* Default outfile name, or take name from argv with -o option. */
Xstatic char *outfile_name = "a.out";
X
X/* For compatibility with toplev.c and tm-sun386.h */
Xchar *dump_base_name;
Xint optimize = 0;
Xchar *language_string = "GNU C++";
Xchar *main_input_filename;
X
X/*
X * The list of constructors & destructors. We process all files & then
X * spit these out in output_ctor_dtor_table(), because we need to know
X * the length of the list.
X */
X
Xstruct ctor_dtor_list_elem
X{
X struct ctor_dtor_list_elem *next;
X char *name;
X} *dtor_chain, *ctor_chain;
X
Xint dtor_chain_length = 0;
Xint ctor_chain_length = 0;
X
Xmain (argc, argv)
X int argc;
X char *argv[];
X{
X int i, nerrs = 0;
X enum error_code code;
X FILE *fp;
X
X if (argc > 2 && !strcmp (argv[1], "-o"))
X {
X outfile_name = argv[2];
X i = 3;
X }
X else
X i = 1;
X
X if ((outfile = fopen (outfile_name, "w")) == NULL)
X {
X perror ("collect");
X exit (-1);
X }
X
X dump_base_name = main_input_filename = outfile_name;
X for (; i < argc; i++)
X {
X char buf[80];
X
X /* This is a library, skip it. */
X if (argv[i][0] == '-' && argv[i][1] == 'l')
X continue;
X
X if ((fp = fopen (argv[i], "r")) == NULL)
X {
X sprintf (buf, "collect `%s'", argv[i]);
X perror (buf);
X exit (-1);
X }
X
X switch (code = process (fp, argv[i]))
X {
X case OK:
X break;
X
X case BAD_MAGIC:
X fprintf (stderr, "file `%s' has a bad magic number for collect\n",
X argv[i]);
X exit (-1);
X
X case NO_NAMELIST:
X fprintf (stderr, "file `%s' has a no namelist for collect\n",
X argv[i]);
X exit (-1);
X
X case RANDOM_ERROR:
X fprintf (stderr, "random error while processing file `%s':\n",
X argv[i]);
X perror ("collect");
X exit (-1);
X
X case FOPEN_ERROR:
X fprintf (stderr, "fopen(3S) error while processing file `%s' in collect\n",
X argv[i]);
X exit (-1);
X
X case FREAD_ERROR:
X fprintf (stderr, "fread(3S) error while processing file `%s' in collect\n",
X argv[i]);
X exit (-1);
X
X case FWRITE_ERROR:
X fprintf (stderr, "fwrite(3S) error while processing file `%s' in collect\n",
X argv[i]);
X exit (-1);
X
X default:
X abort ();
X }
X
X fclose (fp);
X
X }
X
X switch (code = output_ctor_dtor_table ())
X {
X case OK:
X fclose (outfile);
X exit (0);
X case FREAD_ERROR:
X perror ("fread(3S) failed in collect, at end");
X break;
X case FWRITE_ERROR:
X perror ("fwrite(3S) failed in collect, at end");
X break;
X case FOPEN_ERROR:
X perror ("fopen(3S) failed in collect, at end");
X break;
X case RANDOM_ERROR:
X fprintf (stderr, "random error in collect, at end");
X break;
X }
X exit (-1);
X}
X
Xvoid
X add_ctor_dtor_elem(symbol_name)
Xchar *symbol_name;
X{
X /*
X * In EXTENDED_COFF systems, it's possible to
X * have multiple occurances of symbols ( or so it
X * appears) in the symbol table. Sooo, we scan to
X * eliminate duplicate entries. This can never hurt,
X * and helps EXTENDED_COFF.
X */
X int exists;
X int is_ctor = (symbol_name[CTOR_DTOR_MARKER_LENGTH] == 'I');
X
X struct ctor_dtor_list_elem *p
X = ((is_ctor) ? ctor_chain : dtor_chain);
X
X exists = 0;
X while (p) {
X if (strcmp( symbol_name, p -> name ) == 0 ) {
X exists = 1;
X break;
X }
X p = p -> next;
X }
X if ( ! exists ) {
X
X struct ctor_dtor_list_elem *new = (struct ctor_dtor_list_elem *)
X xmalloc(sizeof(struct ctor_dtor_list_elem));
X
X new->name = (char *)xmalloc (strlen (symbol_name)
X + CTOR_DTOR_MARKER_OFFSET + 2);
X
X strcpy(new -> name, symbol_name + CTOR_DTOR_MARKER_OFFSET);
X
X if ( is_ctor ) {
X new -> next = ctor_chain;
X ctor_chain = new;
X ctor_chain_length++;
X }
X else {
X new->next = dtor_chain;
X dtor_chain = new;
X dtor_chain_length++;
X }
X }
X}
X
X
Xenum error_code
Xoutput_ctor_dtor_table ()
X{
X int dtor_offset;
X
X /* Write out the CTOR tabel */
X ASM_FILE_START(outfile);
X
X#ifdef EXTENDED_COFF
X fprintf (outfile, "%s\n", SDATA_SECTION_ASM_OP);
X#else
X fprintf (outfile, "%s\n", DATA_SECTION_ASM_OP);
X#endif
X ASM_GLOBALIZE_LABEL (outfile, CTOR_TABLE_NAME);
X ASM_OUTPUT_LABEL (outfile, CTOR_TABLE_NAME);
X ASM_OUTPUT_INT_CONST(outfile,ctor_chain_length);
X while ( ctor_chain ) {
X ASM_OUTPUT_LABELREF_AS_INT (outfile, ctor_chain->name);
X ctor_chain = ctor_chain -> next;
X }
X /* NULL-terminate the list of constructors. -- not needed, but keep it */
X ASM_OUTPUT_INT_CONST (outfile, 0);
X
X /*
X * Now, lay out the destructors
X */
X
X#ifdef EXTENDED_COFF
X fprintf (outfile, "%s\n", SDATA_SECTION_ASM_OP);
X#else
X fprintf (outfile, "%s\n", DATA_SECTION_ASM_OP);
X#endif
X ASM_GLOBALIZE_LABEL (outfile, DTOR_TABLE_NAME);
X ASM_OUTPUT_LABEL (outfile, DTOR_TABLE_NAME);
X ASM_OUTPUT_INT_CONST(outfile,dtor_chain_length);
X while (dtor_chain)
X {
X ASM_OUTPUT_LABELREF_AS_INT (outfile, dtor_chain->name);
X dtor_chain = dtor_chain->next;
X }
X ASM_OUTPUT_INT_CONST (outfile, 0);
X ASM_OUTPUT_INT_CONST (outfile, 0);
X
X fclose (outfile);
X return OK;
X}
X
X
X/*****************************************************************************
X *
X * COFF & EXTENDED COFF
X *
X ****************************************************************************/
X#if defined(COFF) || defined(EXTENDED_COFF)
X
X#include
X
X#if defined(EXTENDED_COFF)
X# define GCC_SYMBOLS(X) (SYMHEADER(X).isymMax+SYMHEADER(X).iextMax)
X# define GCC_SYMENT SYMR
X# define GCC_OK_SYMBOL(X) ((X).st == stProc && (X).sc == scText)
X# define GCC_SYMINC(X) (1)
X#else
X# define GCC_SYMBOLS(X) (HEADER(ldptr).f_nsyms)
X# define GCC_SYMENT SYMENT
X#ifndef UNUSUAL_COFF_DEFINITION
X# define GCC_OK_SYMBOL(X) (!(((X).n_type & N_TMASK) != (DT_NON << N_BTSHFT)))
X#else
X# define GCC_OK_SYMBOL(X) ((X).n_scnum == 1 && (X).n_sclass == C_EXT)
X#endif
X# define GCC_SYMINC(X) ((X).n_numaux+1)
X#endif
X
Xenum error_code
X process (fp, filename)
XFILE *fp;
Xchar *filename;
X{
X LDFILE *ldptr = NULL;
X do {
X if ((ldptr = ldopen(filename, ldptr)) != NULL ) {
X
X if (!ISCOFF( HEADER(ldptr).f_magic ) ) {
X return BAD_MAGIC;
X }
X else {
X
X int symbols = GCC_SYMBOLS(ldptr);
X int symindex;
X
X for (symindex = 0;
X symindex < symbols;
X ) {
X
X GCC_SYMENT symbol;
X
X char *symbol_name;
X extern char *ldgetname();
X int returnCode;
X
X returnCode = ldtbread(ldptr, symindex, &symbol);
X
X if ( returnCode <= 0 ) {
X break;
X }
X
X symindex += GCC_SYMINC(symbol);
X
X if (! GCC_OK_SYMBOL(symbol)) continue;
X symbol_name = ldgetname(ldptr, &symbol);
X
X /* Check to see if we have a CTOR/DTOR marker */
X
X if (! strncmp (CTOR_DTOR_MARKER_NAME, symbol_name,
X CTOR_DTOR_MARKER_LENGTH))
X add_ctor_dtor_elem(symbol_name);
X }
X }
X }
X else {
X return( RANDOM_ERROR );
X }
X } while ( ldclose(ldptr) == FAILURE ) ;
X return ( OK );
X}
X
X/****** taken from sdbout.c ******/
X
X
X/* Tell the assembler the source file name.
X On systems that use SDB, this is done whether or not -g,
X so it is called by ASM_FILE_START.
X
X ASM_FILE is the assembler code output file,
X INPUT_NAME is the name of the main input file. */
X
X/* void */
Xsdbout_filename (asm_file, input_name)
X FILE *asm_file;
X char *input_name;
X{
X int len = strlen (input_name);
X char *na = input_name + len;
X
X /* NA gets INPUT_NAME sans directory names. */
X while (na > input_name)
X {
X if (na[-1] == '/')
X break;
X na--;
X }
X
X ASM_OUTPUT_SOURCE_FILENAME (asm_file, na);
X}
X
X#else
X
X/*****************************************************************************
X *
X * BSD SYMBOL TABLES
X *
X *****************************************************************************/
X
X/* Figure out the type of file we need to process.
X Currently, only .o and .a formats are acceptable. */
Xenum error_code
Xprocess (fp, filename)
X FILE *fp;
X char *filename;
X{
X struct stat file_stat;
X union
X {
X char ar_form[SARMAG];
X struct exec a_out_form;
X } header;
X int size;
X
X if (fstat (fp->_file, &file_stat))
X return RANDOM_ERROR;
X
X size = file_stat.st_size;
X
X if (fread (header.ar_form, SARMAG, 1, fp) < 1)
X return RANDOM_ERROR;
X
X if (strncmp (ARMAG, header.ar_form, SARMAG))
X {
X fseek (fp, 0, 0);
X if (fread (&header.a_out_form, sizeof (struct exec), 1, fp) < 1)
X return RANDOM_ERROR;
X
X if (N_BADMAG (header.a_out_form))
X return BAD_MAGIC;
X
X return process_o (fp, &header.a_out_form, size);
X }
X return process_a (fp);
X}
X
Xenum error_code
Xprocess_o (fp, header, size)
X FILE *fp;
X struct exec *header;
X int size;
X{
X int symoff, symend;
X#ifndef hp9000s300
X struct nlist *nelem, *nelems, *nend;
X char *strtab;
X#else
X struct nlist_ *nelem, *nelems, *nend;
X#endif /* hp9000s300 */
X
X if (N_BADMAG (*header))
X return BAD_MAGIC;
X
X#ifndef hp9000s300
X symoff = N_SYMOFF (*header);
X symend = N_STROFF (*header);
X#else
X symoff = LESYM_OFFSET (*header);
X symend = DNTT_OFFSET (*header);
X#endif /* hp9000s300 */
X if (symoff == symend)
X return NO_NAMELIST;
X fseek (fp, symoff - sizeof (struct exec), 1);
X#ifndef hp9000s300
X nelems = (struct nlist *)alloca (symend - symoff);
X#else
X nelems = (struct nlist_ *)alloca (symend - symoff);
X#endif /* hp9000s300 */
X if (fread (nelems, sizeof (char), symend - symoff, fp) < symend - symoff)
X return FREAD_ERROR;
X
X#ifndef hp9000s300
X strtab = (char *)alloca ((char *)size - (char *)symend);
X if (fread (strtab, sizeof (char), (char *)size - (char *)symend, fp)
X < ((char *)size - (char *)symend) * sizeof (char))
X return FREAD_ERROR;
X
X nend = (struct nlist *)((char *)nelems + symend - symoff);
X for (nelem = nelems; nelem < nend; nelem++)
X#else
X nend = (struct nlist_ *)((char *)nelems + symend - symoff);
X for (nelem = nelems; nelem < nend; )
X#endif /* hp9000s300 */
X {
X#ifndef hp9000s300
X int strindex = nelem->n_un.n_strx;
X#else
X int symlen = nelem->n_length;
X char p[255];
X memcpy(p, (char *) (++nelem), symlen);
X p[symlen]='\0';
X
X /* printf("'%s'\n",p); */
X#endif /* hp9000s300 */
X
X#ifndef hp9000s300
X if (strindex)
X#else
X nelem = (struct nlist_ *)((char *)nelem + symlen);
X
X if (symlen)
X#endif /* hp9000s300 */
X {
X#ifndef hp9000s300
X char *p = strtab+strindex;
X#endif /* hp9000s300 */
X
X if (! strncmp ("__GLOBAL_$", p, 10))
X add_ctor_dtor_elem(p);
X }
X }
X return OK;
X}
X
Xenum error_code
Xprocess_a (fp)
X FILE *fp;
X{
X struct ar_hdr header;
X struct exec exec_header;
X int size;
X enum error_code code;
X
X while (! feof (fp))
X {
X char c;
X#ifdef hp9000s300
X int curpos;
X#endif /* hp9000s300 */
X
X if (fread (&header, sizeof (struct ar_hdr), 1, fp) < 1)
X return RANDOM_ERROR;
X
X size = atoi (header.ar_size);
X#ifdef hp9000s300
X curpos = ftell(fp);
X#endif /* hp9000s300 */
X
X#ifndef hp9000s300
X if (fread (&exec_header, sizeof (struct exec), 1, fp) < 1)
X return RANDOM_ERROR;
X#else
X /* if the name starts with /, it's an index file */
X if (header.ar_name[0] != '/') {
X
X if (fread (&exec_header, sizeof (struct exec), 1, fp) < 1)
X return RANDOM_ERROR;
X#endif /* hp9000s300 */
X
X code = process_o (fp, &exec_header, size);
X if (code != OK)
X return code;
X#ifdef hp9000s300
X }
X
X if (fseek(fp,(long) curpos + size,0))
X return RANDOM_ERROR;
X#endif /* hp9000s300 */
X if ((c = getc (fp)) == '\n')
X ;
X else
X ungetc (c, fp);
X c = getc (fp);
X if (c != EOF)
X ungetc (c, fp);
X }
X return OK;
X}
X#endif
X
X/* Output to FILE a reference to the assembler name of a C-level name NAME.
X If NAME starts with a *, the rest of NAME is output verbatim.
X Otherwise NAME is transformed in an implementation-defined way
X (usually by the addition of an underscore).
X Many macros in the tm file are defined to call this function.
X
X Swiped from `varasm.c' */
X
Xvoid
Xassemble_name (file, name)
X FILE *file;
X char *name;
X{
X if (name[0] == '*')
X fputs (&name[1], file);
X else
X#ifdef NO_UNDERSCORES
X fprintf (file, "%s", name);
X#else
X fprintf (file, "_%s", name);
X#endif
X}
X
Xint
Xxmalloc (size)
X int size;
X{
X int result = malloc (size);
X if (! result)
X {
X fprintf (stderr, "Virtual memory exhausted\n");
X exit (-1);
X }
X return result;
X}
X#endif
!EOF
echo "Extracting cplus-ptree.c..."
sed 's/^X//' >cplus-ptree.c << '!EOF'
X/* Prints out tree in human readable form - GNU C++ compiler
X Copyright (C) 1987 Free Software Foundation, Inc.
X Hacked by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X#include "config.h"
X#include "tree.h"
X#include "cplus-tree.h"
X#include
X
X
Xextern char *tree_code_err_name[];
Xextern char *tree_code_err_asg_name[];
Xextern char *mode_name[];
X
Xextern char spaces[];
X
X#define MIN(x,y) ((x < y) ? x : y)
X
Xstatic FILE *outfile;
X
X/* This code is copied from print-tree.c. */
Xstatic
Xvoid
Xwruid (node)
X tree node;
X{
X
X if (node == NULL)
X fputs ("<>", outfile);
X else {
X fprintf (outfile, "%1d", TREE_UID (node));
X }
X}
X
Xstatic
Xvoid
Xpart (title, node)
X char title[];
X tree node;
X{
X fprintf (outfile, " %s = ", title);
X wruid (node);
X putc (';', outfile);
X}
X
X/* Similar to `part' but prefix with @ if value is not constant
X and print the constant value if it is constant. */
Xstatic
Xvoid
Xcpart (title, ct, punct)
X char *title;
X tree ct;
X char punct;
X{
X fprintf (outfile, " %s = ", title);
X if (ct == NULL)
X fputs ("<>", outfile);
X else
X {
X if (!TREE_LITERAL (ct))
X {
X putc ('@', outfile);
X wruid (ct);
X }
X else
X fprintf (outfile, "%ld", TREE_INT_CST_LOW (ct));
X }
X putc (punct, outfile);
X}
X
Xvoid
Xprint_lang_decl (node)
X tree node;
X{
X}
X
Xvoid walk_lang_decl (node)
X tree node;
X{
X}
X
Xvoid
Xprint_lang_type (node)
X register tree node;
X{
X int first;
X if (! (TREE_CODE (node) == RECORD_TYPE
X || TREE_CODE (node) == UNION_TYPE))
X return;
X first = 1;
X fputs (" [", outfile);
X if (TYPE_NEEDS_CONSTRUCTOR (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("needs-constructor", outfile);
X first = 0;
X }
X if (TYPE_NEEDS_DESTRUCTOR (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("needs-destructor", outfile);
X first = 0;
X }
X if (TYPE_HAS_CONVERSION (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("has-type-conversion", outfile);
X first = 0;
X }
X if (TYPE_HAS_INT_CONVERSION (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("has-int-conversion", outfile);
X first = 0;
X }
X if (TYPE_HAS_REAL_CONVERSION (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("has-float-conversion", outfile);
X first = 0;
X }
X if (TYPE_HAS_INIT_REF (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("X(X&)", outfile);
X first = 0;
X }
X if (TREE_GETS_NEW (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("gets-new", outfile);
X first = 0;
X }
X if (TREE_GETS_DELETE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("gets-delete", outfile);
X first = 0;
X }
X if (TYPE_HAS_ASSIGNMENT (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("has=", outfile);
X first = 0;
X }
X if (TYPE_GETS_ASSIGNMENT (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("gets=", outfile);
X first = 0;
X }
X if (TYPE_HAS_ASSIGN_REF (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("this=(X&)", outfile);
X first = 0;
X }
X if (TYPE_GETS_ASSIGN_REF (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("gets=(X&)", outfile);
X first = 0;
X }
X if (TYPE_HAS_WRAPPER (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("wrapper", outfile);
X first = 0;
X }
X if (TYPE_OVERLOADS_METHOD_CALL_EXPR (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("op->()", outfile);
X first = 0;
X }
X if (TYPE_GETS_INIT_AGGR (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("gets X(X, ...)", outfile);
X first = 0;
X }
X if (TYPE_OVERLOADS_CALL_EXPR (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("op()", outfile);
X first = 0;
X }
X if (TYPE_OVERLOADS_ARRAY_REF (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("op[]", outfile);
X first = 0;
X }
X if (TYPE_USES_MULTIPLE_INHERITANCE (node))
X {
X if (!first) putc (' ', outfile);
X fputs ("uses-multiple-inheritance", outfile);
X first = 0;
X }
X
X fputs ("] ", outfile);
X}
X
Xvoid
Xwalk_lang_type (node)
X tree node;
X{
X if (! (TREE_CODE (node) == RECORD_TYPE))
X return;
X
X part ("member functions", CLASSTYPE_METHOD_VEC (node));
X part ("baselinks", CLASSTYPE_BASELINK_VEC (node));
X cpart ("offset", CLASSTYPE_OFFSET (node), ';');
X fprintf (outfile, "n_parents = %d; n_ancestors = %d;",
X CLASSTYPE_N_BASECLASSES (node),
X CLASSTYPE_N_SUPERCLASSES (node));
X}
!EOF
echo "Extracting gnulib3.c..."
sed 's/^X//' >gnulib3.c << '!EOF'
Xtypedef struct set_vector
X{
X int length;
X int vector[1];
X /* struct set_vector *next; */
X} set_vector;
X
Xextern set_vector __CTOR_LIST__;
Xextern set_vector __DTOR_LIST__;
Xset_vector *__dlp;
Xint __dli;
X
Xextern void exit ();
Xextern void __do_global_init ();
Xextern void __do_global_cleanup ();
Xextern void on_exit(void*, void*);
X
X#if defined(i386) && !defined(sequent)
X#define COFF
X#endif
X
X#ifdef COFF_ENCAPSULATE
X#undef COFF
X#endif
X
X#if defined(sun)
X#define ON_EXIT(PROCP, ARG) \
X do { extern void PROCP (); on_exit (PROCP, ARG); } while (0)
X#endif
X
Xint
X__main ()
X{
X /* Gross hack for GNU ld. This is defined in `builtin.cc'
X from libg++. */
X#ifndef COFF
X extern int __1xyzzy__;
X#endif
X
X#ifdef ON_EXIT
X
X#ifdef sun
X ON_EXIT (_cleanup, 0);
X#endif
X
X ON_EXIT (__do_global_cleanup, 0);
X
X#endif
X __dli = __DTOR_LIST__.length;
X __dlp = &__DTOR_LIST__;
X#ifndef COFF
X __do_global_init (&__1xyzzy__);
X#else
X __do_global_init ();
X#endif
X}
X
X#ifndef ON_EXIT
Xvoid
Xexit (status)
X int status;
X{
X __do_global_cleanup ();
X _cleanup ();
X _exit (status);
X}
X#endif
X
Xvoid
X__do_global_init ()
X{
X register int i, len;
X register void (**ppf)() = (void (**)())__CTOR_LIST__.vector;
X
X len = __CTOR_LIST__.length;
X for (i = 0; i < len; i++)
X (*ppf[i])();
X}
X
Xvoid
X__do_global_cleanup ()
X{
X while (__dlp)
X {
X while (--__dli >= 0)
X {
X void (*pf)() = (void (*)())__dlp->vector[__dli];
X (*pf)();
X }
X __dlp = (struct set_vector *)__dlp->vector[__dlp->length];
X if (__dlp) __dli = __dlp->length;
X }
X}
!EOF
echo "Extracting stmt.c..."
sed 's/^X//' >stmt.c << '!EOF'
X/* Expands front end tree to back end RTL for GNU C-Compiler
X Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* This file handles the generation of rtl code from tree structure
X above the level of expressions, using subroutines in exp*.c and emit-rtl.c.
X It also creates the rtl expressions for parameters and auto variables
X and has full responsibility for allocating stack slots.
X
X The functions whose names start with `expand_' are called by the
X parser to generate RTL instructions for various kinds of constructs.
X
X Some control and binding constructs require calling several such
X functions at different times. For example, a simple if-then
X is expanded by calling `expand_start_cond' (with the condition-expression
X as argument) before parsing the then-clause and calling `expand_end_cond'
X after parsing the then-clause.
X
X `expand_function_start' is called at the beginning of a function,
X before the function body is parsed, and `expand_function_end' is
X called after parsing the body.
X
X Call `assign_stack_local' to allocate a stack slot for a local variable.
X This is usually done during the RTL generation for the function body,
X but it can also be done in the reload pass when a pseudo-register does
X not get a hard register.
X
X Call `put_var_into_stack' when you learn, belatedly, that a variable
X previously given a pseudo-register must in fact go in the stack.
X This function changes the DECL_RTL to be a stack slot instead of a reg
X then scans all the RTL instructions so far generated to correct them. */
X
X#include "config.h"
X
X#include
X
X#include "rtl.h"
X#include "tree.h"
X#include "flags.h"
X#include "insn-flags.h"
X#include "insn-config.h"
X#include "insn-codes.h"
X#include "expr.h"
X#include "regs.h"
X#include "hard-reg-set.h"
X#include "recog.h"
X#include "obstack.h"
X
X#define obstack_chunk_alloc xmalloc
X#define obstack_chunk_free free
Xstruct obstack stmt_obstack;
X
Xextern int xmalloc ();
Xextern void free ();
X
X#define MAX(x,y) (((x) > (y)) ? (x) : (y))
X#define MIN(x,y) (((x) < (y)) ? (x) : (y))
X
X/* Nonzero if function being compiled pops its args on return.
X May affect compilation of return insn or of function epilogue. */
X
Xint current_function_pops_args;
X
X/* Nonzero if function being compiled needs to be given an address
X where the value should be stored. */
X
Xint current_function_returns_struct;
X
X/* Nonzero if function being compiled needs to
X return the address of where it has put a structure value. */
X
Xint current_function_returns_pcc_struct;
X
X/* Nonzero if function being compiled needs to be passed a static chain. */
X
Xint current_function_needs_context;
X
X/* Nonzero if function being compiled can call setjmp. */
X
Xint current_function_calls_setjmp;
X
X/* Nonzero if function being compiled can call alloca,
X either as a subroutine or builtin. */
X
Xint current_function_calls_alloca;
X
X/* Nonzero if the current function returns a pointer type */
X
Xint current_function_returns_pointer;
X
X/* If function's args have a fixed size, this is that size, in bytes.
X Otherwise, it is -1.
X May affect compilation of return insn or of function epilogue. */
X
Xint current_function_args_size;
X
X/* # bytes the prologue should push and pretend that the caller pushed them.
X The prologue must do this, but only if parms can be passed in registers. */
X
Xint current_function_pretend_args_size;
X
X/* This is the offset from the arg pointer to the place where the first
X anonymous arg can be found, if there is one. */
Xrtx current_function_arg_offset_rtx;
X
X/* Name of function now being compiled. */
X
Xchar *current_function_name;
X
X/* Label that will go on parm cleanup code, if any.
X Jumping to this label runs cleanup code for parameters, if
X such code must be run. Following this code is the logical return label. */
X
Xrtx cleanup_label;
X
X/* Label that will go on function epilogue.
X Jumping to this label serves as a "return" instruction
X on machines which require execution of the epilogue on all returns. */
X
Xrtx return_label;
X
X/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs.
X So we can mark them all live at the end of the function, if nonopt. */
Xrtx save_expr_regs;
X
X/* List (chain of EXPR_LISTs) of all stack slots in this function.
X Made for the sake of unshare_all_rtl. */
Xrtx stack_slot_list;
X
X/* Filename and line number of last line-number note,
X whether we actually emitted it or not. */
Xchar *emit_filename;
Xint emit_lineno;
X
X/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */
Xstatic rtx parm_birth_insn;
X
X/* The FUNCTION_DECL node for the function being compiled. */
X
Xstatic tree this_function;
X
X/* Number of binding contours started so far in this function. */
X
Xstatic int block_start_count;
X
X/* Offset to end of allocated area of stack frame.
X If stack grows down, this is the address of the last stack slot allocated.
X If stack grows up, this is the address for the next slot. */
Xstatic int frame_offset;
X
X/* Nonzero if a stack slot has been generated whose address is not
X actually valid. It means that the generated rtl must all be scanned
X to detect and correct the invalid addresses where they occur. */
Xstatic int invalid_stack_slot;
X
X/* Label to jump back to for tail recursion, or 0 if we have
X not yet needed one for this function. */
Xstatic rtx tail_recursion_label;
X
X/* Place after which to insert the tail_recursion_label if we need one. */
Xstatic rtx tail_recursion_reentry;
X
X/* Each time we expand an expression-statement,
X record the expr's type and its RTL value here. */
X
Xstatic tree last_expr_type;
Xstatic rtx last_expr_value;
X
X/* Chain of all RTL_EXPRs that have insns in them. */
Xstatic tree rtl_expr_chain;
X
X/* Last insn of those whose job was to put parms into their nominal homes. */
Xstatic rtx last_parm_insn;
X
X/* Functions and data structures for expanding case statements. */
X
X/* Case label structure, used to hold info on labels within case
X statements. We handle "range" labels; for a single-value label
X as in C, the high and low limits are the same. */
X
Xstruct case_node
X{
X struct case_node *left;
X struct case_node *right;
X struct case_node *parent;
X tree low;
X tree high;
X tree test_label;
X tree code_label;
X};
X
Xtypedef struct case_node case_node;
Xtypedef struct case_node *case_node_ptr;
X
Xextern void balance_case_nodes ();
Xextern void emit_case_nodes ();
Xextern void group_case_nodes ();
Xextern void emit_jump_if_reachable ();
X
X/* Stack of control and binding constructs we are currently inside.
X
X These constructs begin when you call `expand_start_WHATEVER'
X and end when you call `expand_end_WHATEVER'. This stack records
X info about how the construct began that tells the end-function
X what to do. It also may provide information about the construct
X to alter the behavior of other constructs within the body.
X For example, they may affect the behavior of C `break' and `continue'.
X
X Each construct gets one `struct nesting' object.
X All of these objects are chained through the `all' field.
X `nesting_stack' points to the first object (innermost construct).
X The position of an entry on `nesting_stack' is in its `depth' field.
X
X Each type of construct has its own individual stack.
X For example, loops have `loop_stack'. Each object points to the
X next object of the same type through the `next' field.
X
X Some constructs are visible to `break' exit-statements and others
X are not. Which constructs are visible depends on the language.
X Therefore, the data structure allows each construct to be visible
X or not, according to the args given when the construct is started.
X The construct is visible if the `exit_label' field is non-null.
X In that case, the value should be a CODE_LABEL rtx. */
X
Xstruct nesting
X{
X struct nesting *all;
X struct nesting *next;
X int depth;
X rtx exit_label;
X union
X {
X /* For conds (if-then and if-then-else statements). */
X struct
X {
X /* Label on the else-part, if any, else 0. */
X rtx else_label;
X /* Label at the end of the whole construct. */
X rtx after_label;
X } cond;
X /* For loops. */
X struct
X {
X /* Label at the top of the loop; place to loop back to. */
X rtx start_label;
X /* Label at the end of the whole construct. */
X rtx end_label;
X /* Label for `continue' statement to jump to;
X this is in front of the stepper of the loop. */
X rtx continue_label;
X } loop;
X /* For variable binding contours. */
X struct
X {
X /* Sequence number of this binding contour within the function,
X in order of entry. */
X int block_start_count;
X /* Nonzero => value to restore stack to on exit. */
X rtx stack_level;
X /* The NOTE that starts this contour.
X Used by expand_goto to check whether the destination
X is within each contour or not. */
X rtx first_insn;
X /* Innermost containing binding contour that has a stack level. */
X struct nesting *innermost_stack_block;
X /* List of cleanups to be run on exit from this contour.
X This is a list of expressions to be evaluated.
X The TREE_PURPOSE of each link is the ..._DECL node
X which the cleanup pertains to. */
X tree cleanups;
X /* List of cleanup-lists of blocks containing this block,
X as they were at the locus where this block appears.
X There is an element for each containing block,
X ordered innermost containing block first.
X The element's TREE_VALUE is the cleanup-list of that block,
X which may be null. */
X tree outer_cleanups;
X /* Chain of labels defined inside this binding contour.
X For contours that have stack levels or cleanups. */
X struct label_chain *label_chain;
X } block;
X /* For switch (C) or case (Pascal) statements,
X and also for dummies (see `expand_start_case_dummy'). */
X struct
X {
X /* The insn after which the case dispatch should finally
X be emitted. Zero for a dummy. */
X rtx start;
X /* A list of case labels, kept in ascending order by value
X as the list is built.
X During expand_end_case, this list may be rearranged into a
X nearly balanced binary tree. */
X struct case_node *case_list;
X /* Label to jump to if no case matches. */
X tree default_label;
X /* The expression to be dispatched on. */
X tree index_expr;
X /* Type that INDEX_EXPR should be converted to. */
X tree nominal_type;
X /* Number of range exprs in case statement. */
X short num_ranges;
X } case_stmt;
X /* For exception contours. */
X struct
X {
X /* List of exceptions raised. This is a TREE_LIST
X of whatever you want. */
X tree raised;
X /* List of exceptions caught. This is also a TREE_LIST
X of whatever you want. As a special case, it has the
X value `void_type_node' if it handles default exceptions. */
X tree handled;
X
X /* First insn of TRY block, in case resumptive model is needed. */
X rtx first_insn;
X /* Label for the catch clauses. */
X rtx except_label;
X /* Label for unhandled exceptions. */
X rtx unhandled_label;
X /* Label at the end of whole construct. */
X rtx after_label;
X /* Label which "escapes" the exception construct.
X Like EXIT_LABEL for BREAK construct, but for exceptions. */
X rtx escape_label;
X } except_stmt;
X } data;
X};
X
X/* Chain of all pending binding contours. */
Xstruct nesting *block_stack;
X
X/* Chain of all pending binding contours that restore stack levels
X or have cleanups. */
Xstruct nesting *stack_block_stack;
X
X/* Chain of all pending conditional statements. */
Xstruct nesting *cond_stack;
X
X/* Chain of all pending loops. */
Xstruct nesting *loop_stack;
X
X/* Chain of all pending case or switch statements. */
Xstruct nesting *case_stack;
X
X/* Chain of all pending exception contours. */
Xstruct nesting *except_stack;
X
X/* Separate chain including all of the above,
X chained through the `all' field. */
Xstruct nesting *nesting_stack;
X
X/* Number of entries on nesting_stack now. */
Xint nesting_depth;
X
X/* Pop one of the sub-stacks, such as `loop_stack' or `cond_stack';
X and pop off `nesting_stack' down to the same level. */
X
X#define POPSTACK(STACK) \
Xdo { int initial_depth = nesting_stack->depth; \
X do { struct nesting *this = STACK; \
X STACK = this->next; \
X nesting_stack = this->all; \
X nesting_depth = this->depth; \
X obstack_free (&stmt_obstack, this); } \
X while (nesting_depth > initial_depth); } while (0)
X
Xstatic int warn_if_unused_value ();
Xstatic void expand_goto_internal ();
Xstatic int expand_fixup ();
Xstatic void fixup_gotos ();
Xstatic void expand_cleanups ();
Xstatic void fixup_cleanups ();
Xstatic void expand_null_return_1 ();
Xstatic int tail_recursion_args ();
Xstatic void fixup_stack_slots ();
Xstatic rtx fixup_stack_1 ();
Xstatic rtx fixup_memory_subreg ();
Xstatic rtx walk_fixup_memory_subreg ();
Xstatic void fixup_var_refs ();
Xstatic void fixup_var_refs_insns ();
Xstatic rtx fixup_var_refs_1 ();
Xstatic rtx parm_stack_loc ();
Xstatic void optimize_bit_field ();
Xstatic void do_jump_if_equal ();
X
X/* Emit a no-op instruction. */
X
Xrtx
Xemit_nop ()
X{
X rtx last_insn = get_last_insn ();
X if (!optimize
X && (GET_CODE (last_insn) == CODE_LABEL
X || prev_real_insn (last_insn) == 0))
X emit_insn (gen_nop ());
X}
X
X/* Return the rtx-label that corresponds to a LABEL_DECL,
X creating it if necessary. */
X
Xrtx /* @@ non-static for case.c. */
Xlabel_rtx (label)
X tree label;
X{
X if (TREE_CODE (label) != LABEL_DECL)
X abort ();
X
X if (DECL_RTL (label))
X return DECL_RTL (label);
X
X return DECL_RTL (label) = gen_label_rtx ();
X}
X
X/* Add an unconditional jump to LABEL as the next sequential instruction. */
X
Xvoid
Xemit_jump (label)
X rtx label;
X{
X do_pending_stack_adjust ();
X emit_jump_insn (gen_jump (label));
X emit_barrier ();
X}
X
X/* Handle goto statements and the labels that they can go to. */
X
X/* In some cases it is impossible to generate code for a forward goto
X until the label definition is seen. This happens when it may be necessary
X for the goto to reset the stack pointer: we don't yet know how to do that.
X So expand_goto puts an entry on this fixup list.
X Each time a binding contour that resets the stack is exited,
X we check each fixup.
X If the target label has now been defined, we can insert the proper code. */
X
Xstruct goto_fixup
X{
X /* Points to following fixup. */
X struct goto_fixup *next;
X /* Points to the insn before the jump insn.
X If more code must be inserted, it goes after this insn. */
X rtx before_jump;
X /* The LABEL_DECL that this jump is jumping to, or 0
X for break, continue or return. */
X tree target;
X /* The CODE_LABEL rtx that this is jumping to. */
X rtx target_rtl;
X /* Number of binding contours started in current function
X before the label reference. */
X int block_start_count;
X /* The outermost stack level that should be restored for this jump.
X Each time a binding contour that resets the stack is exited,
X if the target label is *not* yet defined, this slot is updated. */
X rtx stack_level;
X /* List of lists of cleanup expressions to be run by this goto.
X There is one element for each block that this goto is within.
X The TREE_VALUE contains the cleanup list of that block as of the
X time this goto was seen.
X The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */
X tree cleanup_list_list;
X};
X
Xstatic struct goto_fixup *goto_fixup_chain;
X
X/* Within any binding contour that must restore a stack level,
X all labels are recorded with a chain of these structures. */
X
Xstruct label_chain
X{
X /* Points to following fixup. */
X struct label_chain *next;
X tree label;
X};
X
X/* Specify the location in the RTL code of a label LABEL,
X which is a LABEL_DECL tree node.
X
X This is used for the kind of label that the user can jump to with a
X goto statement, and for alternatives of a switch or case statement.
X RTL labels generated for loops and conditionals don't go through here;
X they are generated directly at the RTL level, by other functions below.
X
X Note that this has nothing to do with defining label *names*.
X Languages vary in how they do that and what that even means. */
X
Xvoid
Xexpand_label (label)
X tree label;
X{
X struct label_chain *p;
X
X do_pending_stack_adjust ();
X emit_label (label_rtx (label));
X
X if (stack_block_stack != 0)
X {
X p = (struct label_chain *) oballoc (sizeof (struct label_chain));
X p->next = stack_block_stack->data.block.label_chain;
X stack_block_stack->data.block.label_chain = p;
X p->label = label;
X }
X}
X
X/* Generate RTL code for a `goto' statement with target label LABEL.
X LABEL should be a LABEL_DECL tree node that was or will later be
X defined with `expand_label'. */
X
Xvoid
Xexpand_goto (label)
X tree label;
X{
X expand_goto_internal (label, label_rtx (label), 0);
X}
X
X/* Generate RTL code for a `goto' statement with target label BODY.
X LABEL should be a LABEL_REF.
X LAST_INSN, if non-0, is the rtx we should consider as the last
X insn emitted (for the purposes of cleaning up a return). */
X
Xstatic void
Xexpand_goto_internal (body, label, last_insn)
X tree body;
X rtx label;
X rtx last_insn;
X{
X struct nesting *block;
X rtx stack_level = 0;
X
X if (GET_CODE (label) != CODE_LABEL)
X abort ();
X
X /* If label has already been defined, we can tell now
X whether and how we must alter the stack level. */
X
X if (PREV_INSN (label) != 0)
X {
X /* Find the innermost pending block that contains the label.
X (Check containment by comparing insn-uids.)
X Then restore the outermost stack level within that block,
X and do cleanups of all blocks contained in it. */
X for (block = block_stack; block; block = block->next)
X {
X if (INSN_UID (block->data.block.first_insn) < INSN_UID (label))
X break;
X if (block->data.block.stack_level != 0)
X stack_level = block->data.block.stack_level;
X /* Execute the cleanups for blocks we are exiting. */
X if (block->data.block.cleanups != 0)
X {
X expand_cleanups (block->data.block.cleanups, 0);
X do_pending_stack_adjust ();
X }
X }
X
X if (stack_level)
X {
X /* Ensure stack adjust isn't done by emit_jump, as this would clobber
X the stack pointer. This one should be deleted as dead by flow. */
X clear_pending_stack_adjust ();
X do_pending_stack_adjust ();
X emit_move_insn (stack_pointer_rtx, stack_level);
X }
X
X if (body != 0 && TREE_PACKED (body))
X error ("jump to `%s' invalidly jumps into binding contour",
X IDENTIFIER_POINTER (DECL_NAME (body)));
X }
X /* Label not yet defined: may need to put this goto
X on the fixup list. */
X else if (! expand_fixup (body, label, last_insn))
X {
X /* No fixup needed. Record that the label is the target
X of at least one goto that has no fixup. */
X if (body != 0)
X TREE_ADDRESSABLE (body) = 1;
X }
X
X emit_jump (label);
X}
X
X/* Generate if necessary a fixup for a goto
X whose target label in tree structure (if any) is TREE_LABEL
X and whose target in rtl is RTL_LABEL.
X
X If LAST_INSN is nonzero, we pretend that the jump appears
X after insn LAST_INSN instead of at the current point in the insn stream.
X
X The fixup will be used later to insert insns at this point
X to restore the stack level as appropriate for the target label.
X
X Value is nonzero if a fixup is made. */
X
Xstatic int
Xexpand_fixup (tree_label, rtl_label, last_insn)
X tree tree_label;
X rtx rtl_label;
X rtx last_insn;
X{
X struct nesting *block, *end_block;
X
X /* See if we can recognize which block the label will be output in.
X This is possible in some very common cases.
X If we succeed, set END_BLOCK to that block.
X Otherwise, set it to 0. */
X
X if (cond_stack
X && (rtl_label == cond_stack->data.cond.else_label
X || rtl_label == cond_stack->data.cond.after_label))
X end_block = cond_stack;
X /* If we are in a loop, recognize certain labels which
X are likely targets. This reduces the number of fixups
X we need to create. */
X else if (loop_stack
X && (rtl_label == loop_stack->data.loop.start_label
X || rtl_label == loop_stack->data.loop.end_label
X || rtl_label == loop_stack->data.loop.continue_label))
X end_block = loop_stack;
X else
X end_block = 0;
X
X /* Now set END_BLOCK to the binding level to which we will return. */
X
X if (end_block)
X {
X struct nesting *next_block = end_block->all;
X block = block_stack;
X
X /* First see if the END_BLOCK is inside the innermost binding level.
X If so, then no cleanups or stack levels are relevant. */
X while (next_block && next_block != block)
X next_block = next_block->all;
X
X if (next_block)
X return 0;
X
X /* Otherwise, set END_BLOCK to the innermost binding level
X which is outside the relevant control-structure nesting. */
X next_block = block_stack->next;
X for (block = block_stack; block != end_block; block = block->all)
X if (block == next_block)
X next_block = next_block->next;
X end_block = next_block;
X }
X
X /* Does any containing block have a stack level or cleanups?
X If not, no fixup is needed, and that is the normal case
X (the only case, for standard C). */
X for (block = block_stack; block != end_block; block = block->next)
X if (block->data.block.stack_level != 0
X || block->data.block.cleanups != 0)
X break;
X
X if (block != end_block)
X {
X /* Ok, a fixup is needed. Add a fixup to the list of such. */
X struct goto_fixup *fixup
X = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup));
X /* In case an old stack level is restored, make sure that comes
X after any pending stack adjust. */
X /* ?? If the fixup isn't to come at the present position,
X doing the stack adjust here isn't useful. Doing it with our
X settings at that location isn't useful either. Let's hope
X someone does it! */
X if (last_insn == 0)
X do_pending_stack_adjust ();
X fixup->before_jump = last_insn ? last_insn : get_last_insn ();
X fixup->target = tree_label;
X fixup->target_rtl = rtl_label;
X fixup->block_start_count = block_start_count;
X fixup->stack_level = 0;
X fixup->cleanup_list_list
X = (((block->data.block.outer_cleanups
X#if 0
X && block->data.block.outer_cleanups != empty_cleanup_list
X#endif
X )
X || block->data.block.cleanups)
X ? tree_cons (0, block->data.block.cleanups,
X block->data.block.outer_cleanups)
X : 0);
X fixup->next = goto_fixup_chain;
X goto_fixup_chain = fixup;
X }
X
X return block != 0;
X}
X
X/* When exiting a binding contour, process all pending gotos requiring fixups.
X THISBLOCK is the structure that describes the block being exited.
X STACK_LEVEL is the rtx for the stack level to restore exiting this contour.
X CLEANUP_LIST is a list of expressions to evaluate on exiting this contour.
X FIRST_INSN is the insn that began this contour.
X
X Gotos that jump out of this contour must restore the
X stack level and do the cleanups before actually jumping.
X
X DONT_JUMP_IN nonzero means report error there is a jump into this
X contour from before the beginning of the contour.
X This is also done if STACK_LEVEL is nonzero. */
X
Xstatic void
Xfixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in)
X struct nesting *thisblock;
X rtx stack_level;
X tree cleanup_list;
X rtx first_insn;
X int dont_jump_in;
X{
X register struct goto_fixup *f, *prev;
X
X /* F is the fixup we are considering; PREV is the previous one. */
X /* We run this loop in two passes so that cleanups of exited blocks
X are run first, and blocks that are exited are marked so
X afterwards. */
X
X for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
X {
X /* Test for a fixup that is inactive because it is already handled. */
X if (f->before_jump == 0)
X {
X /* Delete inactive fixup from the chain, if that is easy to do. */
X if (prev != 0)
X prev->next = f->next;
X }
X /* Has this fixup's target label been defined?
X If so, we can finalize it. */
X else if (PREV_INSN (f->target_rtl) != 0)
X {
X /* Get the first non-label after the label
X this goto jumps to. If that's before this scope begins,
X we don't have a jump into the scope. */
X rtx after_label = f->target_rtl;
X while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL)
X after_label = NEXT_INSN (after_label);
X
X /* If this fixup jumped into this contour from before the beginning
X of this contour, report an error. */
X /* ??? Bug: this does not detect jumping in through intermediate
X blocks that have stack levels or cleanups.
X It detects only a problem with the innermost block
X around the label. */
X if (f->target != 0
X && (dont_jump_in || stack_level || cleanup_list)
X /* If AFTER_LABEL is 0, it means the jump goes to the end
X of the rtl, which means it jumps into this scope. */
X && (after_label == 0
X || INSN_UID (first_insn) < INSN_UID (after_label))
X && INSN_UID (first_insn) > INSN_UID (f->before_jump)
X && ! TREE_REGDECL (f->target))
X {
X error_with_decl (f->target,
X "label `%s' used before containing binding contour");
X /* Prevent multiple errors for one label. */
X TREE_REGDECL (f->target) = 1;
X }
X
X /* Execute cleanups for blocks this jump exits. */
X if (f->cleanup_list_list)
X {
X tree lists;
X for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists))
X /* Marked elements correspond to blocks that have been closed.
X Do their cleanups. */
X if (TREE_ADDRESSABLE (lists)
X && TREE_VALUE (lists) != 0)
X fixup_cleanups (TREE_VALUE (lists), &f->before_jump);
X }
X
X /* Restore stack level for the biggest contour that this
X jump jumps out of. */
X if (f->stack_level)
X emit_insn_after (gen_move_insn (stack_pointer_rtx, f->stack_level),
X f->before_jump);
X f->before_jump = 0;
X }
X }
X
X /* Mark the cleanups of exited blocks so that they are executed
X by the code above. */
X for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
X if (f->before_jump != 0
X && PREV_INSN (f->target_rtl) == 0
X /* Label has still not appeared. If we are exiting a block with
X a stack level to restore, that started before the fixup,
X mark this stack level as needing restoration
X when the fixup is later finalized.
X Also mark the cleanup_list_list element for F
X that corresponds to this block, so that ultimately
X this block's cleanups will be executed by the code above. */
X && thisblock != 0
X /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared,
X it means the label is undefined. That's erroneous, but possible. */
X && (thisblock->data.block.block_start_count
X <= f->block_start_count))
X {
X tree lists = f->cleanup_list_list;
X for (; lists; lists = TREE_CHAIN (lists))
X /* If the following elt. corresponds to our containing block
X then the elt. must be for this block. */
X if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups)
X TREE_ADDRESSABLE (lists) = 1;
X
X if (stack_level)
X f->stack_level = stack_level;
X }
X}
X
X/* Generate RTL for an asm statement (explicit assembler code).
X BODY is a STRING_CST node containing the assembler code text. */
X
Xvoid
Xexpand_asm (body)
X tree body;
X{
X emit_insn (gen_rtx (ASM_INPUT, VOIDmode,
X TREE_STRING_POINTER (body)));
X last_expr_type = 0;
X}
X
X/* Generate RTL for an asm statement with arguments.
X STRING is the instruction template.
X OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
X Each output or input has an expression in the TREE_VALUE and
X a constraint-string in the TREE_PURPOSE.
X CLOBBERS is a list of STRING_CST nodes each naming a hard register
X that is clobbered by this insn.
X
X Not all kinds of lvalue that may appear in OUTPUTS can be stored directly.
X Some elements of OUTPUTS may be replaced with trees representing temporary
X values. The caller should copy those temporary values to the originally
X specified lvalues.
X
X VOL nonzero means the insn is volatile; don't optimize it. */
X
Xvoid
Xexpand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
X tree string, outputs, inputs, clobbers;
X int vol;
X char *filename;
X int line;
X{
X rtvec argvec, constraints;
X rtx body;
X int ninputs = list_length (inputs);
X int noutputs = list_length (outputs);
X int nclobbers = list_length (clobbers);
X tree tail;
X register int i;
X /* Vector of RTX's of evaluated output operands. */
X rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
X /* The insn we have emitted. */
X rtx insn;
X
X last_expr_type = 0;
X
X for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
X {
X tree val = TREE_VALUE (tail);
X tree val1;
X int j;
X int found_equal;
X
X /* If there's an erroneous arg, emit no insn. */
X if (TREE_TYPE (val) == error_mark_node)
X return;
X
X /* Make sure constraint has `=' and does not have `+'. */
X
X found_equal = 0;
X for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++)
X {
X if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+')
X {
X error ("output operand constraint contains `+'");
X return;
X }
X if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=')
X found_equal = 1;
X }
X if (! found_equal)
X {
X error ("output operand constraint lacks `='");
X return;
X }
X
X /* If an output operand is not a variable or indirect ref,
X or a part of one,
X create a SAVE_EXPR which is a pseudo-reg
X to act as an intermediate temporary.
X Make the asm insn write into that, then copy it to
X the real output operand. */
X
X while (TREE_CODE (val) == COMPONENT_REF
X || TREE_CODE (val) == ARRAY_REF)
X val = TREE_OPERAND (val, 0);
X
X if (TREE_CODE (val) != VAR_DECL
X && TREE_CODE (val) != PARM_DECL
X && TREE_CODE (val) != INDIRECT_REF)
X TREE_VALUE (tail) = save_expr (TREE_VALUE (tail));
X
X output_rtx[i] = expand_expr (TREE_VALUE (tail), 0, VOIDmode, 0);
X }
X
X if (ninputs + noutputs > MAX_RECOG_OPERANDS)
X {
X error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS);
X return;
X }
X
X /* Make vectors for the expression-rtx and constraint strings. */
X
X argvec = rtvec_alloc (ninputs);
X constraints = rtvec_alloc (ninputs);
X
X body = gen_rtx (ASM_OPERANDS, VOIDmode,
X TREE_STRING_POINTER (string), "", 0, argvec, constraints,
X filename, line);
X MEM_VOLATILE_P (body) = vol;
X
X /* Eval the inputs and put them into ARGVEC.
X Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */
X
X i = 0;
X for (tail = inputs; tail; tail = TREE_CHAIN (tail))
X {
X int j;
X
X /* If there's an erroneous arg, emit no insn,
X because the ASM_INPUT would get VOIDmode
X and that could cause a crash in reload. */
X if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
X return;
X if (TREE_PURPOSE (tail) == NULL_TREE)
X {
X error ("hard register `%s' listed as input operand to `asm'",
X TREE_STRING_POINTER (TREE_VALUE (tail)) );
X return;
X }
X
X /* Make sure constraint has neither `=' nor `+'. */
X
X for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++)
X if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '='
X || TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+')
X {
X error ("input operand constraint contains `%c'",
X TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]);
X return;
X }
X
X XVECEXP (body, 3, i) /* argvec */
X = expand_expr (TREE_VALUE (tail), 0, VOIDmode, 0);
X XVECEXP (body, 4, i) /* constraints */
X = gen_rtx (ASM_INPUT, TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
X TREE_STRING_POINTER (TREE_PURPOSE (tail)));
X i++;
X }
X
X /* Protect all the operands from the queue,
X now that they have all been evaluated. */
X
X for (i = 0; i < ninputs; i++)
X XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0);
X
X for (i = 0; i < noutputs; i++)
X output_rtx[i] = protect_from_queue (output_rtx[i], 1);
X
X /* Now, for each output, construct an rtx
X (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT
X ARGVEC CONSTRAINTS))
X If there is more than one, put them inside a PARALLEL. */
X
X if (noutputs == 1 && nclobbers == 0)
X {
X XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs));
X insn = emit_insn (gen_rtx (SET, VOIDmode, output_rtx[0], body));
X }
X else if (noutputs == 0 && nclobbers == 0)
X {
X /* No output operands: put in a raw ASM_OPERANDS rtx. */
X insn = emit_insn (body);
X }
X else
X {
X rtx obody = body;
X int num = noutputs;
X if (num == 0) num = 1;
X body = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num + nclobbers));
X
X /* For each output operand, store a SET. */
X
X for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
X {
X XVECEXP (body, 0, i)
X = gen_rtx (SET, VOIDmode,
X output_rtx[i],
X gen_rtx (ASM_OPERANDS, VOIDmode,
X TREE_STRING_POINTER (string),
X TREE_STRING_POINTER (TREE_PURPOSE (tail)),
X i, argvec, constraints,
X filename, line));
X MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
X }
X
X /* If there are no outputs (but there are some clobbers)
X store the bare ASM_OPERANDS into the PARALLEL. */
X
X if (i == 0)
X XVECEXP (body, 0, i++) = obody;
X
X /* Store (clobber REG) for each clobbered register specified. */
X
X for (tail = clobbers; tail; tail = TREE_CHAIN (tail), i++)
X {
X int j;
X char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
X extern char *reg_names[];
X
X for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
X if (!strcmp (regname, reg_names[j]))
X break;
X
X if (j == FIRST_PSEUDO_REGISTER)
X {
X error ("unknown register name `%s' in `asm'", regname);
X return;
X }
X
X /* Use QImode since that's guaranteed to clobber just one reg. */
X XVECEXP (body, 0, i)
X = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, QImode, j));
X }
X
X insn = emit_insn (body);
X }
X
X last_expr_type = 0;
X}
X
X/* Nonzero if within a ({...}) grouping, in which case we must
X always compute a value for each expr-stmt in case it is the last one. */
X
Xint expr_stmts_for_value;
X
X/* Generate RTL to evaluate the expression EXP
X and remember it in case this is the VALUE in a ({... VALUE; }) constr. */
X
Xvoid
Xexpand_expr_stmt (exp)
X tree exp;
X{
X /* If -W, warn about statements with no side effects,
X except for statements explicitly casted to void (e.g. for assert()), and
X except inside a ({...}) where they may be useful. */
X if (expr_stmts_for_value == 0 && exp != error_mark_node)
X {
X if (! TREE_VOLATILE (exp)
X && !(TREE_CODE (exp) == CONVERT_EXPR
X && TREE_TYPE (exp) == void_type_node)
X && (extra_warnings || warn_unused))
X warning_with_file_and_line (emit_filename, emit_lineno,
X "statement with no effect");
X else if (warn_unused)
X warn_if_unused_value (exp);
X }
X last_expr_type = TREE_TYPE (exp);
X if (! flag_syntax_only)
X last_expr_value = expand_expr (exp, expr_stmts_for_value ? 0 : const0_rtx,
X VOIDmode, 0);
X emit_queue ();
X}
X
X/* Warn if EXP contains any computations whose results are not used.
X Return 1 if a warning is printed; 0 otherwise. */
X
Xstatic int
Xwarn_if_unused_value (exp)
X tree exp;
X{
X if (TREE_USED (exp))
X return 0;
X
X switch (TREE_CODE (exp))
X {
X case PREINCREMENT_EXPR:
X case POSTINCREMENT_EXPR:
X case PREDECREMENT_EXPR:
X case POSTDECREMENT_EXPR:
X case MODIFY_EXPR:
X case INIT_EXPR:
X case NEW_EXPR:
X case CALL_EXPR:
X case METHOD_CALL_EXPR:
X case RTL_EXPR:
X case WRAPPER_EXPR:
X case ANTI_WRAPPER_EXPR:
X case WITH_CLEANUP_EXPR:
X /* We don't warn about COND_EXPR because it may be a useful
X construct if either arm contains a side effect. */
X case COND_EXPR:
X return 0;
X
X /* These kinds of exprs are really stmts. What to do? */
X case LOOP_STMT:
X case LET_STMT:
X case IF_STMT:
X return 0;
X
X case TRUTH_ORIF_EXPR:
X case TRUTH_ANDIF_EXPR:
X /* In && or ||, warn if 2nd operand has no side effect. */
X return warn_if_unused_value (TREE_OPERAND (exp, 1));
X
X case COMPOUND_EXPR:
X if (warn_if_unused_value (TREE_OPERAND (exp, 0)))
X return 1;
X return warn_if_unused_value (TREE_OPERAND (exp, 1));
X
X case NOP_EXPR:
X case CONVERT_EXPR:
X /* Don't warn about values cast to void. */
X if (TREE_TYPE (exp) == void_type_node)
X return 0;
X /* Assignment to a cast results in a cast of a modify.
X Don't complain about that. */
X if (TREE_CODE (TREE_OPERAND (exp, 0)) == MODIFY_EXPR)
X return 0;
X /* Sometimes it results in a cast of a cast of a modify.
X Don't complain about that. */
X if ((TREE_CODE (TREE_OPERAND (exp, 0)) == CONVERT_EXPR
X || TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR)
X && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == MODIFY_EXPR)
X return 0;
X
X default:
X warning_with_file_and_line (emit_filename, emit_lineno,
X "value computed is not used");
X return 1;
X }
X}
X
X/* Clear out the memory of the last expression evaluated. */
X
Xvoid
Xclear_last_expr ()
X{
X last_expr_type = 0;
X}
X
X/* Begin a statement which will return a value.
X Return the RTL_EXPR for this statement expr.
X The caller must save that value and pass it to expand_end_stmt_expr. */
X
Xtree
Xexpand_start_stmt_expr ()
X{
X rtx save = start_sequence ();
X /* Make the RTL_EXPR node temporary, not momentary,
X so that rtl_expr_chain doesn't become garbage. */
X int momentary = suspend_momentary ();
X tree t = make_node (RTL_EXPR);
X resume_momentary (momentary);
X RTL_EXPR_RTL (t) = save;
X NO_DEFER_POP;
X expr_stmts_for_value++;
X return t;
X}
X
X/* Restore the previous state at the end of a statement that returns a value.
X Returns a tree node representing the statement's value and the
X insns to compute the value.
X
X The nodes of that expression have been freed by now, so we cannot use them.
X But we don't want to do that anyway; the expression has already been
X evaluated and now we just want to use the value. So generate a RTL_EXPR
X with the proper type and RTL value.
X
X If the last substatement was not an expression,
X return something with type `void'. */
X
Xtree
Xexpand_end_stmt_expr (t)
X tree t;
X{
X rtx saved = RTL_EXPR_RTL (t);
X
X OK_DEFER_POP;
X
X if (last_expr_type == 0)
X {
X last_expr_type = void_type_node;
X last_expr_value = const0_rtx;
X }
X TREE_TYPE (t) = last_expr_type;
X RTL_EXPR_RTL (t) = last_expr_value;
X RTL_EXPR_SEQUENCE (t) = get_insns ();
X
X rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain);
X
X end_sequence (saved);
X
X /* Don't consider deleting this expr or containing exprs at tree level. */
X TREE_VOLATILE (t) = 1;
X /* Propagate volatility of the actual RTL expr. */
X TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value);
X
X last_expr_type = 0;
X expr_stmts_for_value--;
X
X return t;
X}
X
X/* The exception handling nesting looks like this:
X
X <-- Level N-1
X { <-- exception handler block
X <-- Level N
X <-- in an exception handler
X { <-- try block
X : <-- in a TRY block
X : <-- in an exception handler
X :
X }
X
X { <-- except block
X : <-- in an except block
X : <-- in an exception handler
X :
X }
X
X }
X
X/* Return nonzero iff in a try block at level LEVEL. */
X
Xint
Xin_try_block (level)
X int level;
X{
X struct nesting *n = except_stack;
X while (1)
X {
X while (n && n->data.except_stmt.after_label != 0)
X n = n->next;
X if (n == 0)
X return 0;
X if (level == 0)
X return n != 0;
X level--;
X n = n->next;
X }
X}
X
X/* Return nonzero iff in an except block at level LEVEL. */
X
Xint
Xin_except_block (level)
X int level;
X{
X struct nesting *n = except_stack;
X while (1)
X {
X while (n && n->data.except_stmt.after_label == 0)
X n = n->next;
X if (n == 0)
X return 0;
X if (level == 0)
X return n != 0;
X level--;
X n = n->next;
X }
X}
X
X/* Return nonzero iff in an exception handler at level LEVEL. */
X
Xint
Xin_exception_handler (level)
X int level;
X{
X struct nesting *n = except_stack;
X while (n && level--)
X n = n->next;
X return n != 0;
X}
X
X/* Record the fact that the current exception nesting raises
X exception EX. If not in an exception handler, return 0. */
Xint
Xexpand_raise (ex)
X tree ex;
X{
X tree *raises_ptr;
X
X if (except_stack == 0)
X return 0;
X raises_ptr = &except_stack->data.except_stmt.raised;
X if (! value_member (ex, *raises_ptr))
X *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr);
X return 1;
X}
X
X/* Generate RTL for the start of a try block.
X
X TRY_CLAUSE is the condition to test to enter the try block. */
X
Xvoid
Xexpand_start_try (try_clause, exitflag, escapeflag)
X tree try_clause;
X int exitflag;
X int escapeflag;
X{
X struct nesting *thishandler
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X /* Make an entry on cond_stack for the cond we are entering. */
X
X thishandler->next = except_stack;
X thishandler->all = nesting_stack;
X thishandler->depth = ++nesting_depth;
X thishandler->data.except_stmt.raised = 0;
X thishandler->data.except_stmt.handled = 0;
X thishandler->data.except_stmt.first_insn = get_insns ();
X thishandler->data.except_stmt.except_label = gen_label_rtx ();
X thishandler->data.except_stmt.unhandled_label = 0;
X thishandler->data.except_stmt.after_label = 0;
X thishandler->data.except_stmt.escape_label
X = escapeflag ? thishandler->data.except_stmt.except_label : 0;
X thishandler->exit_label = exitflag ? gen_label_rtx () : 0;
X except_stack = thishandler;
X nesting_stack = thishandler;
X
X do_jump (try_clause, thishandler->data.except_stmt.except_label, NULL);
X}
X
X/* End of a TRY block. Nothing to do for now. */
X
Xvoid
Xexpand_end_try ()
X{
X except_stack->data.except_stmt.after_label = gen_label_rtx ();
X expand_goto_internal (NULL, except_stack->data.except_stmt.after_label, 0);
X}
X
X/* Start an `except' nesting contour.
X EXITFLAG says whether this contour should be able to `exit' something.
X ESCAPEFLAG says whether this contour should be escapable. */
X
Xvoid
Xexpand_start_except (exitflag, escapeflag)
X int exitflag;
X int escapeflag;
X{
X if (exitflag)
X {
X struct nesting *n;
X /* An `exit' from catch clauses goes out to next exit level,
X if there is one. Otherwise, it just goes to the end
X of the construct. */
X for (n = except_stack->next; n; n = n->next)
X if (n->exit_label != 0)
X {
X except_stack->exit_label = n->exit_label;
X break;
X }
X if (n == 0)
X except_stack->exit_label = except_stack->data.except_stmt.after_label;
X }
X if (escapeflag)
X {
X struct nesting *n;
X /* An `escape' from catch clauses goes out to next escape level,
X if there is one. Otherwise, it just goes to the end
X of the construct. */
X for (n = except_stack->next; n; n = n->next)
X if (n->data.except_stmt.escape_label != 0)
X {
X except_stack->data.except_stmt.escape_label
X = n->data.except_stmt.escape_label;
X break;
X }
X if (n == 0)
X except_stack->data.except_stmt.escape_label
X = except_stack->data.except_stmt.after_label;
X }
X do_pending_stack_adjust ();
X emit_label (except_stack->data.except_stmt.except_label);
X}
X
X/* Generate code to `escape' from an exception contour. This
X is like `exiting', but does not conflict with constructs which
X use `exit_label'.
X
X Return nonzero if this contour is escapable, otherwise
X return zero, and language-specific code will emit the
X appropriate error message. */
Xint
Xexpand_escape_except ()
X{
X struct nesting *n;
X last_expr_type = 0;
X for (n = except_stack; n; n = n->next)
X if (n->data.except_stmt.escape_label != 0)
X {
X expand_goto_internal (0, n->data.except_stmt.escape_label, 0);
X return 1;
X }
X
X return 0;
X}
X
X/* Finish processing and `except' contour.
X Culls out all exceptions which might be raise but not
X handled, and returns the list to the caller.
X Language-specific code is responsible for dealing with these
X exceptions. */
X
Xtree
Xexpand_end_except ()
X{
X struct nesting *n;
X tree raised = NULL_TREE;
X
X do_pending_stack_adjust ();
X emit_label (except_stack->data.except_stmt.after_label);
X
X n = except_stack->next;
X if (n)
X {
X /* Propagate exceptions raised but not handled to next
X highest level. */
X tree handled = except_stack->data.except_stmt.raised;
X if (handled != void_type_node)
X {
X tree prev = NULL_TREE;
X raised = except_stack->data.except_stmt.raised;
X while (handled)
X {
X tree this_raise;
X for (this_raise = raised, prev = 0; this_raise;
X this_raise = TREE_CHAIN (this_raise))
X {
X if (value_member (TREE_VALUE (this_raise), handled))
X {
X if (prev)
X TREE_CHAIN (prev) = TREE_CHAIN (this_raise);
X else
X {
X raised = TREE_CHAIN (raised);
X if (raised == NULL_TREE)
X goto nada;
X }
X }
X else
X prev = this_raise;
X }
X handled = TREE_CHAIN (handled);
X }
X if (prev == NULL_TREE)
X prev = raised;
X if (prev)
X TREE_CHAIN (prev) = n->data.except_stmt.raised;
X nada:
X n->data.except_stmt.raised = raised;
X }
X }
X
X POPSTACK (except_stack);
X last_expr_type = 0;
X return raised;
X}
X
X/* Record that exception EX is caught by this exception handler.
X Return nonzero if in exception handling construct, otherwise return 0. */
X
Xint
Xexpand_catch (ex)
X tree ex;
X{
X tree *raises_ptr;
X
X if (except_stack == 0)
X return 0;
X raises_ptr = &except_stack->data.except_stmt.handled;
X if (*raises_ptr != void_type_node
X && ex != NULL_TREE
X && ! value_member (ex, *raises_ptr))
X *raises_ptr = tree_cons (NULL_TREE, ex, *raises_ptr);
X return 1;
X}
X
X/* Record that this exception handler catches all exceptions.
X Return nonzero if in exception handling construct, otherwise return 0. */
X
Xint
Xexpand_catch_default ()
X{
X if (except_stack == 0)
X return 0;
X except_stack->data.except_stmt.handled = void_type_node;
X return 1;
X}
X
Xint
Xexpand_end_catch ()
X{
X if (except_stack == 0 || except_stack->data.except_stmt.after_label == 0)
X return 0;
X expand_goto_internal (0, except_stack->data.except_stmt.after_label, 0);
X return 1;
X}
X
X/* Generate RTL for the start of an if-then. COND is the expression
X whose truth should be tested.
X
X If EXITFLAG is nonzero, this conditional is visible to
X `exit_something'. */
X
Xvoid
Xexpand_start_cond (cond, exitflag)
X tree cond;
X int exitflag;
X{
X struct nesting *thiscond
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X /* Make an entry on cond_stack for the cond we are entering. */
X
X thiscond->next = cond_stack;
X thiscond->all = nesting_stack;
X thiscond->depth = ++nesting_depth;
X thiscond->data.cond.after_label = 0;
X thiscond->data.cond.else_label = gen_label_rtx ();
X thiscond->exit_label = exitflag ? thiscond->data.cond.else_label : 0;
X cond_stack = thiscond;
X nesting_stack = thiscond;
X
X do_jump (cond, thiscond->data.cond.else_label, NULL);
X}
X
X/* Generate RTL for the end of an if-then with no else-clause.
X Pop the record for it off of cond_stack. */
X
Xvoid
Xexpand_end_cond ()
X{
X struct nesting *thiscond = cond_stack;
X
X do_pending_stack_adjust ();
X emit_label (thiscond->data.cond.else_label);
X
X POPSTACK (cond_stack);
X last_expr_type = 0;
X}
X
X/* Generate RTL between the then-clause and the else-clause
X of an if-then-else. */
X
Xvoid
Xexpand_start_else ()
X{
X cond_stack->data.cond.after_label = gen_label_rtx ();
X if (cond_stack->exit_label != 0)
X cond_stack->exit_label = cond_stack->data.cond.after_label;
X emit_jump (cond_stack->data.cond.after_label);
X if (cond_stack->data.cond.else_label)
X emit_label (cond_stack->data.cond.else_label);
X}
X
X/* Generate RTL for the end of an if-then-else.
X Pop the record for it off of cond_stack. */
X
Xvoid
Xexpand_end_else ()
X{
X struct nesting *thiscond = cond_stack;
X
X do_pending_stack_adjust ();
X /* Note: a syntax error can cause this to be called
X without first calling `expand_start_else'. */
X if (thiscond->data.cond.after_label)
X emit_label (thiscond->data.cond.after_label);
X
X POPSTACK (cond_stack);
X last_expr_type = 0;
X}
X
X/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this
X loop should be exited by `exit_something'. This is a loop for which
X `expand_continue' will jump to the top of the loop.
X
X Make an entry on loop_stack to record the labels associated with
X this loop. */
X
Xvoid
Xexpand_start_loop (exit_flag)
X int exit_flag;
X{
X register struct nesting *thisloop
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X /* Make an entry on loop_stack for the loop we are entering. */
X
X thisloop->next = loop_stack;
X thisloop->all = nesting_stack;
X thisloop->depth = ++nesting_depth;
X thisloop->data.loop.start_label = gen_label_rtx ();
X thisloop->data.loop.end_label = gen_label_rtx ();
X thisloop->data.loop.continue_label = thisloop->data.loop.start_label;
X thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0;
X loop_stack = thisloop;
X nesting_stack = thisloop;
X
X do_pending_stack_adjust ();
X emit_queue ();
X emit_note (0, NOTE_INSN_LOOP_BEG);
X emit_label (thisloop->data.loop.start_label);
X}
X
X/* Like expand_start_loop but for a loop where the continuation point
X (for expand_continue_loop) will be specified explicitly. */
X
Xvoid
Xexpand_start_loop_continue_elsewhere (exit_flag)
X int exit_flag;
X{
X expand_start_loop (exit_flag);
X loop_stack->data.loop.continue_label = gen_label_rtx ();
X}
X
X/* Specify the continuation point for a loop started with
X expand_start_loop_continue_elsewhere.
X Use this at the point in the code to which a continue statement
X should jump. */
X
Xvoid
Xexpand_loop_continue_here ()
X{
X do_pending_stack_adjust ();
X emit_note (0, NOTE_INSN_LOOP_CONT);
X emit_label (loop_stack->data.loop.continue_label);
X}
X
X/* Finish a loop. Generate a jump back to the top and the loop-exit label.
X Pop the block off of loop_stack. */
X
Xvoid
Xexpand_end_loop ()
X{
X register rtx insn = get_last_insn ();
X register rtx start_label = loop_stack->data.loop.start_label;
X
X do_pending_stack_adjust ();
X
X /* If optimizing, perhaps reorder the loop. If the loop
X starts with a conditional exit, roll that to the end
X where it will optimize together with the jump back. */
X if (optimize
X &&
X ! (GET_CODE (insn) == JUMP_INSN
X && GET_CODE (PATTERN (insn)) == SET
X && SET_DEST (PATTERN (insn)) == pc_rtx
X && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE))
X {
X /* Scan insns from the top of the loop looking for a qualified
X conditional exit. */
X for (insn = loop_stack->data.loop.start_label; insn; insn= NEXT_INSN (insn))
X if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == SET
X && SET_DEST (PATTERN (insn)) == pc_rtx
X && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE
X &&
X ((GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == LABEL_REF
X && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0)
X == loop_stack->data.loop.end_label))
X ||
X (GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 2)) == LABEL_REF
X && (XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0)
X == loop_stack->data.loop.end_label))))
X break;
X if (insn != 0)
X {
X /* We found one. Move everything from there up
X to the end of the loop, and add a jump into the loop
X to jump to there. */
X register rtx newstart_label = gen_label_rtx ();
X
X emit_label_after (newstart_label, PREV_INSN (start_label));
X reorder_insns (start_label, insn, get_last_insn ());
X emit_jump_insn_after (gen_jump (start_label), PREV_INSN (newstart_label));
X emit_barrier_after (PREV_INSN (newstart_label));
X start_label = newstart_label;
X }
X }
X
X emit_jump (start_label);
X emit_note (0, NOTE_INSN_LOOP_END);
X emit_label (loop_stack->data.loop.end_label);
X
X POPSTACK (loop_stack);
X
X last_expr_type = 0;
X}
X
X/* Generate a jump to the current loop's continue-point.
X This is usually the top of the loop, but may be specified
X explicitly elsewhere. If not currently inside a loop,
X return 0 and do nothing; caller will print an error message. */
X
Xint
Xexpand_continue_loop ()
X{
X last_expr_type = 0;
X if (loop_stack == 0)
X return 0;
X expand_goto_internal (0, loop_stack->data.loop.continue_label, 0);
X return 1;
X}
X
X/* Generate a jump to exit the current loop. If not currently inside a loop,
X return 0 and do nothing; caller will print an error message. */
X
Xint
Xexpand_exit_loop ()
X{
X last_expr_type = 0;
X if (loop_stack == 0)
X return 0;
X expand_goto_internal (0, loop_stack->data.loop.end_label, 0);
X return 1;
X}
X
X/* Generate a conditional jump to exit the current loop if COND
X evaluates to zero. If not currently inside a loop,
X return 0 and do nothing; caller will print an error message. */
X
Xint
Xexpand_exit_loop_if_false (cond)
X tree cond;
X{
X last_expr_type = 0;
X if (loop_stack == 0)
X return 0;
X do_jump (cond, loop_stack->data.loop.end_label, NULL);
X return 1;
X}
X
X/* Return non-zero if currently inside a loop. */
X
Xint
Xinside_loop ()
X{
X return loop_stack != 0;
X}
X
X/* Generate a jump to exit the current loop, conditional, binding contour
X or case statement. Not all such constructs are visible to this function,
X only those started with EXIT_FLAG nonzero. Individual languages use
X the EXIT_FLAG parameter to control which kinds of constructs you can
X exit this way.
X
X If not currently inside anything that can be exited,
X return 0 and do nothing; caller will print an error message. */
X
Xint
Xexpand_exit_something ()
X{
X struct nesting *n;
X last_expr_type = 0;
X for (n = nesting_stack; n; n = n->all)
X if (n->exit_label != 0)
X {
X expand_goto_internal (0, n->exit_label, 0);
X return 1;
X }
X
X return 0;
X}
X
X/* Generate RTL to return from the current function, with no value.
X (That is, we do not do anything about returning any value.) */
X
Xvoid
Xexpand_null_return ()
X{
X struct nesting *block = block_stack;
X rtx last_insn = 0;
X
X /* Does any pending block have cleanups? */
X
X while (block && block->data.block.cleanups == 0)
X block = block->next;
X
X /* If yes, use a goto to return, since that runs cleanups. */
X
X expand_null_return_1 (last_insn, block != 0);
X}
X
X/* Output a return with no value. If LAST_INSN is nonzero,
X pretend that the return takes place after LAST_INSN.
X If USE_GOTO is nonzero then don't use a return instruction;
X go to the return label instead. This causes any cleanups
X of pending blocks to be executed normally. */
X
Xstatic void
Xexpand_null_return_1 (last_insn, use_goto)
X rtx last_insn;
X int use_goto;
X{
X rtx end_label = cleanup_label ? cleanup_label : return_label;
X
X clear_pending_stack_adjust ();
X do_pending_stack_adjust ();
X last_expr_type = 0;
X
X /* PCC-struct return always uses an epilogue. */
X if (current_function_returns_pcc_struct || use_goto)
X {
X if (end_label == 0)
X end_label = return_label = gen_label_rtx ();
X expand_goto_internal (0, end_label, last_insn);
X return;
X }
X
X /* Otherwise output a simple return-insn if one is available,
X unless it won't do the job. */
X#ifdef HAVE_return
X if (HAVE_return && use_goto == 0 && cleanup_label == 0)
X {
X emit_jump_insn (gen_return ());
X emit_barrier ();
X return;
X }
X#endif
X
X /* Otherwise jump to the epilogue. */
X expand_goto_internal (0, end_label, last_insn);
X}
X
X/* Generate RTL to evaluate the expression RETVAL and return it
X from the current function. */
X
Xvoid
Xexpand_return (retval)
X tree retval;
X{
X /* If there are any cleanups to be performed, then they will
X be inserted following LAST_INSN. It is desirable
X that the last_insn, for such purposes, should be the
X last insn before computing the return value. Otherwise, cleanups
X which call functions can clobber the return value. */
X /* ??? rms: I think that is erroneous, because in C++ it would
X run destructors on variables that might be used in the subsequent
X computation of the return value. */
X rtx last_insn = 0;
X register rtx val = 0;
X register rtx op0;
X tree retval_rhs;
X int cleanups;
X struct nesting *block;
X
X /* Are any cleanups needed? E.g. C++ destructors to be run? */
X cleanups = any_pending_cleanups (1);
X
X if (TREE_CODE (retval) == RESULT_DECL)
X retval_rhs = retval;
X else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR)
X && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
X retval_rhs = TREE_OPERAND (retval, 1);
X else if (TREE_TYPE (retval) == void_type_node)
X /* Recognize tail-recursive call to void function. */
X retval_rhs = retval;
X else
X retval_rhs = NULL_TREE;
X
X /* Only use `last_insn' if there are cleanups which must be run. */
X if (cleanups || cleanup_label != 0)
X last_insn = get_last_insn ();
X
X /* Distribute return down conditional expr if either of the sides
X may involve tail recursion (see test below). This enhances the number
X of tail recursions we see. Don't do this always since it can produce
X sub-optimal code in some cases and we distribute assignments into
X conditional expressions when it would help. */
X
X if (optimize && retval_rhs != 0
X && frame_offset == 0
X && TREE_CODE (retval_rhs) == COND_EXPR
X && (TREE_CODE (TREE_OPERAND (retval_rhs, 1)) == CALL_EXPR
X || TREE_CODE (TREE_OPERAND (retval_rhs, 2)) == CALL_EXPR))
X {
X rtx label = gen_label_rtx ();
X do_jump (TREE_OPERAND (retval_rhs, 0), label, 0);
X expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
X DECL_RESULT (current_function_decl),
X TREE_OPERAND (retval_rhs, 1)));
X emit_label (label);
X expand_return (build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
X DECL_RESULT (current_function_decl),
X TREE_OPERAND (retval_rhs, 2)));
X return;
X }
X
X /* For tail-recursive call to current function,
X just jump back to the beginning.
X It's unsafe if any auto variable in this function
X has its address taken; for simplicity,
X require stack frame to be empty. */
X if (optimize && retval_rhs != 0
X && frame_offset == STARTING_FRAME_OFFSET
X && TREE_CODE (retval_rhs) == CALL_EXPR
X && TREE_CODE (TREE_OPERAND (retval_rhs, 0)) == ADDR_EXPR
X && TREE_OPERAND (TREE_OPERAND (retval_rhs, 0), 0) == current_function_decl
X /* Finish checking validity, and if valid emit code
X to set the argument variables for the new call. */
X && tail_recursion_args (TREE_OPERAND (retval_rhs, 1),
X DECL_ARGUMENTS (current_function_decl)))
X {
X if (tail_recursion_label == 0)
X {
X tail_recursion_label = gen_label_rtx ();
X emit_label_after (tail_recursion_label,
X tail_recursion_reentry);
X }
X expand_goto_internal (0, tail_recursion_label, last_insn);
X emit_barrier ();
X return;
X }
X#ifdef HAVE_return
X /* This optimization is safe if there are local cleanups
X because expand_null_return takes care of them.
X ??? I think it should also be safe when there is a cleanup label,
X because expand_null_return takes care of them, too.
X Any reason why not? */
X if (HAVE_return && cleanup_label == 0
X && ! current_function_returns_pcc_struct)
X {
X /* If this is return x == y; then generate
X if (x == y) return 1; else return 0;
X if we can do it with explicit return insns. */
X if (retval_rhs)
X switch (TREE_CODE (retval_rhs))
X {
X case EQ_EXPR:
X case NE_EXPR:
X case GT_EXPR:
X case GE_EXPR:
X case LT_EXPR:
X case LE_EXPR:
X case TRUTH_ANDIF_EXPR:
X case TRUTH_ORIF_EXPR:
X case TRUTH_AND_EXPR:
X case TRUTH_OR_EXPR:
X case TRUTH_NOT_EXPR:
X op0 = gen_label_rtx ();
X val = DECL_RTL (DECL_RESULT (current_function_decl));
X jumpifnot (retval_rhs, op0);
X emit_move_insn (val, const1_rtx);
X emit_insn (gen_rtx (USE, VOIDmode, val));
X expand_null_return ();
X emit_label (op0);
X emit_move_insn (val, const0_rtx);
X emit_insn (gen_rtx (USE, VOIDmode, val));
X expand_null_return ();
X return;
X }
X }
X#endif /* HAVE_return */
X
X if (cleanups
X && retval_rhs != 0
X && TREE_TYPE (retval_rhs) != void_type_node
X && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG)
X {
X /* Calculate the return value into a pseudo reg. */
X val = expand_expr (retval_rhs, 0, VOIDmode, 0);
X emit_queue ();
X /* Put the cleanups here. */
X last_insn = get_last_insn ();
X /* Copy the value into hard return reg. */
X emit_move_insn (DECL_RTL (DECL_RESULT (current_function_decl)), val);
X val = DECL_RTL (DECL_RESULT (current_function_decl));
X
X if (GET_CODE (val) == REG && REGNO (val) < FIRST_PSEUDO_REGISTER)
X emit_insn (gen_rtx (USE, VOIDmode, val));
X expand_null_return_1 (last_insn, cleanups);
X }
X else
X {
X /* No cleanups or no hard reg used;
X calculate value into hard return reg
X and let cleanups come after. */
X expand_expr (retval, 0, VOIDmode, 0);
X emit_queue ();
X
X val = DECL_RTL (DECL_RESULT (current_function_decl));
X if (val && GET_CODE (val) == REG
X && REGNO (val) < FIRST_PSEUDO_REGISTER)
X emit_insn (gen_rtx (USE, VOIDmode, val));
X expand_null_return ();
X }
X}
X
X/* Return 1 if the end of the generated RTX is not a barrier.
X This means code already compiled can drop through. */
X
Xint
Xdrop_through_at_end_p ()
X{
X rtx insn = get_last_insn ();
X while (insn && GET_CODE (insn) == NOTE)
X insn = PREV_INSN (insn);
X return insn && GET_CODE (insn) != BARRIER;
X}
X
X/* Emit code to alter this function's formal parms for a tail-recursive call.
X ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs).
X FORMALS is the chain of decls of formals.
X Return 1 if this can be done;
X otherwise return 0 and do not emit any code. */
X
Xstatic int
Xtail_recursion_args (actuals, formals)
X tree actuals, formals;
X{
X register tree a = actuals, f = formals;
X register int i;
X register rtx *argvec;
X
X /* Check that number and types of actuals are compatible
X with the formals. This is not always true in valid C code.
X Also check that no formal needs to be addressable
X and that all formals are scalars. */
X
X /* Also count the args. */
X
X for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++)
X {
X if (TREE_TYPE (TREE_VALUE (a)) != TREE_TYPE (f))
X return 0;
X if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode)
X return 0;
X }
X if (a != 0 || f != 0)
X return 0;
X
X /* Compute all the actuals. */
X
X argvec = (rtx *) alloca (i * sizeof (rtx));
X
X for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
X argvec[i] = expand_expr (TREE_VALUE (a), 0, VOIDmode, 0);
X
X /* Find which actual values refer to current values of previous formals.
X Copy each of them now, before any formal is changed. */
X
X for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
X {
X int copy = 0;
X register int j;
X for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++)
X if (reg_mentioned_p (DECL_RTL (f), argvec[i]))
X { copy = 1; break; }
X if (copy)
X argvec[i] = copy_to_reg (argvec[i]);
X }
X
X /* Store the values of the actuals into the formals. */
X
X for (f = formals, a = actuals, i = 0; f;
X f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++)
X {
X if (DECL_MODE (f) == GET_MODE (argvec[i]))
X emit_move_insn (DECL_RTL (f), argvec[i]);
X else
X convert_move (DECL_RTL (f), argvec[i],
X TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a))));
X }
X
X return 1;
X}
X
X/* Generate the RTL code for entering a binding contour.
X The variables are declared one by one, by calls to `expand_decl'.
X
X EXIT_FLAG is nonzero if this construct should be visible to
X `exit_something'. */
X
Xvoid
Xexpand_start_bindings (exit_flag)
X int exit_flag;
X{
X struct nesting *thisblock
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X rtx note = emit_note (0, NOTE_INSN_BLOCK_BEG);
X
X /* Make an entry on block_stack for the block we are entering. */
X
X thisblock->next = block_stack;
X thisblock->all = nesting_stack;
X thisblock->depth = ++nesting_depth;
X thisblock->data.block.stack_level = 0;
X thisblock->data.block.cleanups = 0;
X#if 0
X if (block_stack)
X {
X if (block_stack->data.block.cleanups == NULL_TREE
X && (block_stack->data.block.outer_cleanups == NULL_TREE
X || block_stack->data.block.outer_cleanups == empty_cleanup_list))
X thisblock->data.block.outer_cleanups = empty_cleanup_list;
X else
X thisblock->data.block.outer_cleanups
X = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
X block_stack->data.block.outer_cleanups);
X }
X else
X thisblock->data.block.outer_cleanups = 0;
X#endif
X#if 1
X if (block_stack
X && !(block_stack->data.block.cleanups == NULL_TREE
X && block_stack->data.block.outer_cleanups == NULL_TREE))
X thisblock->data.block.outer_cleanups
X = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
X block_stack->data.block.outer_cleanups);
X else
X thisblock->data.block.outer_cleanups = 0;
X#endif
X thisblock->data.block.label_chain = 0;
X thisblock->data.block.innermost_stack_block = stack_block_stack;
X thisblock->data.block.first_insn = note;
X thisblock->data.block.block_start_count = ++block_start_count;
X thisblock->exit_label = exit_flag ? gen_label_rtx () : 0;
X block_stack = thisblock;
X nesting_stack = thisblock;
X}
X
X/* Output a USE for any register use in RTL.
X This is used with -noreg to mark the extent of lifespan
X of any registers used in a user-visible variable's DECL_RTL. */
X
Xvoid
Xuse_variable (rtl)
X rtx rtl;
X{
X if (GET_CODE (rtl) == REG)
X /* This is a register variable. */
X emit_insn (gen_rtx (USE, VOIDmode, rtl));
X else if (GET_CODE (rtl) == MEM
X && GET_CODE (XEXP (rtl, 0)) == REG
X && XEXP (rtl, 0) != frame_pointer_rtx
X && XEXP (rtl, 0) != arg_pointer_rtx)
X /* This is a variable-sized structure. */
X emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)));
X}
X
X/* Like use_variable except that it outputs the USEs after INSN
X instead of at the end of the insn-chain. */
X
Xstatic void
Xuse_variable_after (rtl, insn)
X rtx rtl, insn;
X{
X if (GET_CODE (rtl) == REG)
X /* This is a register variable. */
X emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn);
X else if (GET_CODE (rtl) == MEM
X && GET_CODE (XEXP (rtl, 0)) == REG
X && XEXP (rtl, 0) != frame_pointer_rtx
X && XEXP (rtl, 0) != arg_pointer_rtx)
X /* This is a variable-sized structure. */
X emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn);
X}
X
X/* Generate RTL code to terminate a binding contour.
X VARS is the chain of VAR_DECL nodes
X for the variables bound in this contour.
X MARK_ENDS is nonzero if we should put a note at the beginning
X and end of this binding contour.
X
X DONT_JUMP_IN is nonzero if it is not valid to jump into this contour.
X (That is true automatically if the contour has a saved stack level.) */
X
Xvoid
Xexpand_end_bindings (vars, mark_ends, dont_jump_in)
X tree vars;
X int mark_ends;
X int dont_jump_in;
X{
X register struct nesting *thisblock = block_stack;
X register tree decl;
X
X if (warn_unused)
X for (decl = vars; decl; decl = TREE_CHAIN (decl))
X if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL)
X warning_with_decl (decl, "unused variable `%s'");
X
X /* Mark the beginning and end of the scope if requested. */
X
X if (mark_ends)
X emit_note (0, NOTE_INSN_BLOCK_END);
X else
X /* Get rid of the beginning-mark if we don't make an end-mark. */
X NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
X
X if (thisblock->exit_label)
X {
X do_pending_stack_adjust ();
X emit_label (thisblock->exit_label);
X }
X
X /* Don't allow jumping into a block that has cleanups or a stack level. */
X if (dont_jump_in
X || thisblock->data.block.stack_level != 0
X || thisblock->data.block.cleanups != 0)
X {
X struct label_chain *chain;
X
X /* Any labels in this block are no longer valid to go to.
X Mark them to cause an error message. */
X for (chain = thisblock->data.block.label_chain; chain; chain = chain->next)
X {
X TREE_PACKED (chain->label) = 1;
X /* If any goto without a fixup came to this label,
X that must be an error, because gotos without fixups
X come from outside all saved stack-levels and all cleanups. */
X if (TREE_ADDRESSABLE (chain->label))
X error_with_decl (chain->label,
X "label `%s' used before containing binding contour");
X }
X }
X
X /* Restore stack level in effect before the block
X (only if variable-size objects allocated). */
X /* Perform any cleanups associated with the block. */
X
X if (thisblock->data.block.stack_level != 0
X || thisblock->data.block.cleanups != 0)
X {
X /* Don't let cleanups affect ({...}) constructs. */
X int old_expr_stmts_for_value = expr_stmts_for_value;
X rtx old_last_expr_value = last_expr_value;
X tree old_last_expr_type = last_expr_type;
X expr_stmts_for_value = 0;
X
X /* Do the cleanups. */
X expand_cleanups (thisblock->data.block.cleanups, 0);
X do_pending_stack_adjust ();
X
X expr_stmts_for_value = old_expr_stmts_for_value;
X last_expr_value = old_last_expr_value;
X last_expr_type = old_last_expr_type;
X
X /* Restore the stack level. */
X
X if (thisblock->data.block.stack_level != 0)
X emit_move_insn (stack_pointer_rtx,
X thisblock->data.block.stack_level);
X
X /* Any gotos out of this block must also do these things.
X Also report any gotos with fixups that came to labels in this level. */
X fixup_gotos (thisblock,
X thisblock->data.block.stack_level,
X thisblock->data.block.cleanups,
X thisblock->data.block.first_insn,
X dont_jump_in);
X }
X
X /* If doing stupid register allocation, make sure lives of all
X register variables declared here extend thru end of scope. */
X
X if (obey_regdecls)
X for (decl = vars; decl; decl = TREE_CHAIN (decl))
X {
X rtx rtl = DECL_RTL (decl);
X if (TREE_CODE (decl) == VAR_DECL && rtl != 0)
X use_variable (rtl);
X }
X
X /* Restore block_stack level for containing block. */
X
X stack_block_stack = thisblock->data.block.innermost_stack_block;
X POPSTACK (block_stack);
X}
X
X/* Generate RTL for the automatic variable declaration DECL.
X (Other kinds of declarations are simply ignored if seen here.)
X There is no special support here for C++ constructors.
X They should be handled by the proper code in DECL_INITIAL. */
X
Xvoid
Xexpand_decl (decl)
X register tree decl;
X{
X struct nesting *thisblock = block_stack;
X tree type = TREE_TYPE (decl);
X
X /* Only automatic variables need any expansion done.
X Static and external variables, and external functions,
X will be handled by `assemble_variable' (called from finish_decl).
X TYPE_DECL and CONST_DECL require nothing.
X PARM_DECLs are handled in `assign_parms'. */
X
X if (TREE_CODE (decl) != VAR_DECL)
X return;
X if (TREE_STATIC (decl) || TREE_EXTERNAL (decl))
X return;
X
X /* Create the RTL representation for the variable. */
X
X if (type == error_mark_node)
X DECL_RTL (decl) = gen_rtx (MEM, BLKmode, const0_rtx);
X else if (DECL_SIZE (decl) == 0)
X /* Variable with incomplete type. */
X {
X if (DECL_INITIAL (decl) == 0)
X /* Error message was already done; now avoid a crash. */
X DECL_RTL (decl) = assign_stack_local (DECL_MODE (decl), 0);
X else
X /* An initializer is going to decide the size of this array.
X Until we know the size, represent its address with a reg. */
X DECL_RTL (decl) = gen_rtx (MEM, BLKmode, gen_reg_rtx (Pmode));
X }
X else if (DECL_MODE (decl) != BLKmode
X /* If -ffloat-store, don't put explicit float vars
X into regs. */
X && !(flag_float_store
X && TREE_CODE (type) == REAL_TYPE)
X && ! TREE_VOLATILE (decl)
X && ! TREE_ADDRESSABLE (decl)
X && (TREE_REGDECL (decl) || ! obey_regdecls))
X {
X /* Automatic variable that can go in a register. */
X DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl));
X if (TREE_CODE (type) == POINTER_TYPE)
X mark_reg_pointer (DECL_RTL (decl));
X REG_USERVAR_P (DECL_RTL (decl)) = 1;
X }
X else if (TREE_LITERAL (DECL_SIZE (decl)))
X {
X rtx oldaddr = 0;
X rtx addr;
X
X /* If we previously made RTL for this decl, it must be an array
X whose size was determined by the initializer.
X The old address was a register; set that register now
X to the proper address. */
X if (DECL_RTL (decl) != 0)
X {
X if (GET_CODE (DECL_RTL (decl)) != MEM
X || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG)
X abort ();
X oldaddr = XEXP (DECL_RTL (decl), 0);
X }
X
X /* Variable of fixed size that goes on the stack. */
X DECL_RTL (decl)
X = assign_stack_local (DECL_MODE (decl),
X (TREE_INT_CST_LOW (DECL_SIZE (decl))
X * DECL_SIZE_UNIT (decl)
X + BITS_PER_UNIT - 1)
X / BITS_PER_UNIT);
X if (oldaddr)
X {
X addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
X emit_move_insn (oldaddr, addr);
X }
X
X /* If this is a memory ref that contains aggregate components,
X mark it as such for cse and loop optimize. */
X MEM_IN_STRUCT_P (DECL_RTL (decl))
X = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE);
X#if 0
X /* If this is in memory because of -ffloat-store,
X set the volatile bit, to prevent optimizations from
X undoing the effects. */
X if (flag_float_store && TREE_CODE (type) == REAL_TYPE)
X MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
X#endif
X }
X else
X /* Dynamic-size object: must push space on the stack. */
X {
X rtx address, size;
X
X frame_pointer_needed = 1;
X
X /* Record the stack pointer on entry to block, if have
X not already done so. */
X if (thisblock->data.block.stack_level == 0)
X {
X do_pending_stack_adjust ();
X thisblock->data.block.stack_level
X = copy_to_reg (stack_pointer_rtx);
X stack_block_stack = thisblock;
X }
X
X /* Compute the variable's size, in bytes. */
X size = expand_expr (convert_units (DECL_SIZE (decl),
X DECL_SIZE_UNIT (decl),
X BITS_PER_UNIT),
X 0, VOIDmode, 0);
X
X /* Round it up to this machine's required stack boundary. */
X#ifdef STACK_BOUNDARY
X /* Avoid extra code if we can prove it's a multiple already. */
X if (DECL_SIZE_UNIT (decl) % STACK_BOUNDARY)
X {
X#ifdef STACK_POINTER_OFFSET
X /* Avoid extra code if we can prove that adding STACK_POINTER_OFFSET
X will not give this address invalid alignment. */
X if (DECL_ALIGN (decl) > ((STACK_POINTER_OFFSET * BITS_PER_UNIT) % STACK_BOUNDARY))
X size = plus_constant (size,
X STACK_POINTER_OFFSET % (STACK_BOUNDARY / BITS_PER_UNIT));
X#endif
X size = round_push (size);
X }
X#endif /* STACK_BOUNDARY */
X
X /* Make space on the stack, and get an rtx for the address of it. */
X#ifdef STACK_GROWS_DOWNWARD
X anti_adjust_stack (size);
X#endif
X address = copy_to_reg (stack_pointer_rtx);
X#ifdef STACK_POINTER_OFFSET
X {
X /* If the contents of the stack pointer reg are offset from the
X actual top-of-stack address, add the offset here. */
X rtx sp_offset = gen_rtx (CONST_INT, VOIDmode, STACK_POINTER_OFFSET);
X#ifdef STACK_BOUNDARY
X#ifdef STACK_GROWS_DOWNWARD
X int direction = 1;
X#else /* not STACK_GROWS_DOWNWARD */
X int direction = 0;
X#endif /* not STACK_GROWS_DOWNWARD */
X if (DECL_ALIGN (decl) > ((STACK_POINTER_OFFSET * BITS_PER_UNIT) % STACK_BOUNDARY))
X sp_offset = plus_constant (sp_offset,
X (STACK_POINTER_OFFSET
X % (STACK_BOUNDARY / BITS_PER_UNIT)
X * direction));
X#endif /* STACK_BOUNDARY */
X emit_insn (gen_add2_insn (address, sp_offset));
X }
X#endif /* STACK_POINTER_OFFSET */
X#ifndef STACK_GROWS_DOWNWARD
X anti_adjust_stack (size);
X#endif
X
X /* Some systems require a particular insn to refer to the stack
X to make the pages exist. */
X#ifdef HAVE_probe
X if (HAVE_probe)
X emit_insn (gen_probe ());
X#endif
X
X /* Reference the variable indirect through that rtx. */
X DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address);
X }
X
X if (TREE_VOLATILE (decl))
X MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
X if (TREE_READONLY (decl))
X RTX_UNCHANGING_P (DECL_RTL (decl)) = 1;
X
X /* If doing stupid register allocation, make sure life of any
X register variable starts here, at the start of its scope. */
X
X if (obey_regdecls)
X use_variable (DECL_RTL (decl));
X}
X
X/* Emit code to perform the initialization of a declaration DECL. */
X
Xvoid
Xexpand_decl_init (decl)
X tree decl;
X{
X if (TREE_STATIC (decl))
X return;
X
X /* Compute and store the initial value now. */
X
X if (DECL_INITIAL (decl) == error_mark_node)
X {
X enum tree_code code = TREE_CODE (TREE_TYPE (decl));
X if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE
X || code == POINTER_TYPE)
X expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node),
X 0, 0);
X emit_queue ();
X }
X else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST)
X {
X emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl));
X expand_assignment (decl, DECL_INITIAL (decl), 0, 0);
X emit_queue ();
X }
X}
X
X/* CLEANUP is an expression to be executed at exit from this binding contour;
X for example, in C++, it might call the destructor for this variable.
X
X If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them
X either before or after calling `expand_decl' but before compiling
X any subsequent expressions. This is because CLEANUP may be expanded
X more than once, on different branches of execution.
X For the same reason, CLEANUP may not contain a CALL_EXPR
X except as its topmost node--else `preexpand_calls' would get confused.
X
X If CLEANUP is nonzero and DECL is zero, we record a cleanup
X that is not associated with any particular variable.
X
X Return 0 if such an expansion is invalid. Otherwise, return 1. */
Xint
Xexpand_decl_cleanup (decl, cleanup)
X tree decl, cleanup;
X{
X struct nesting *thisblock = block_stack;
X
X /* Error if we are not in any block. */
X if (thisblock == 0)
X return 0;
X
X /* Record the cleanup if there is one. */
X
X if (cleanup != 0)
X {
X thisblock->data.block.cleanups
X = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
X /* If this block has a cleanup, it belongs in stack_block_stack. */
X stack_block_stack = thisblock;
X }
X return 1;
X}
X
X/* DECL is an anonymous union. CLEANUP is a cleanup for DECL.
X DECL_ELTS is the list of elements that belong to DECL's type.
X In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */
X
Xvoid
Xexpand_anon_union_decl (decl, cleanup, decl_elts)
X tree decl, cleanup, decl_elts;
X{
X struct nesting *thisblock = block_stack;
X rtx x;
X
X expand_decl (decl, cleanup);
X x = DECL_RTL (decl);
X
X while (decl_elts)
X {
X tree decl_elt = TREE_VALUE (decl_elts);
X tree cleanup_elt = TREE_PURPOSE (decl_elts);
X enum machine_mode tmode = TYPE_MODE (TREE_TYPE (decl_elt));
X
X if (GET_CODE (x) == MEM)
X {
X /* @@ calling `change_address' means that we cannot
X be at top-level, since `memory_address' might try
X to kick this address into a register, which won't
X work. Will this work? */
X rtx new = gen_rtx (MEM, tmode, XEXP (x, 0));
X DECL_RTL (decl_elt) = new;
X MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x);
X RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
X MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x);
X }
X else
X DECL_RTL (decl_elt) = gen_rtx (SUBREG, tmode, x, 0);
X
X /* Record the cleanup if there is one. */
X
X if (cleanup != 0)
X thisblock->data.block.cleanups
X = temp_tree_cons (decl_elt, cleanup_elt,
X thisblock->data.block.cleanups);
X
X decl_elts = TREE_CHAIN (decl_elts);
X }
X}
X
X/* Expand a list of cleanups LIST.
X Elements may be expressions or may be nested lists.
X
X If DONT_DO is nonnull, then any list-element
X whose TREE_PURPOSE matches DONT_DO is omitted.
X This is sometimes used to avoid a cleanup associated with
X a value that is being returned out of the scope. */
X
Xstatic void
Xexpand_cleanups (list, dont_do)
X tree list;
X tree dont_do;
X{
X tree tail;
X for (tail = list; tail; tail = TREE_CHAIN (tail))
X if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do)
X {
X if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
X expand_cleanups (TREE_VALUE (tail), dont_do);
X else
X expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
X }
X}
X
X/* Expand a list of cleanups for a goto fixup.
X The expansion is put into the insn chain after the insn *BEFORE_JUMP
X and *BEFORE_JUMP is set to the insn that now comes before the jump. */
X
Xstatic void
Xfixup_cleanups (list, before_jump)
X tree list;
X rtx *before_jump;
X{
X rtx beyond_jump = get_last_insn ();
X rtx new_before_jump;
X
X expand_cleanups (list, 0);
X /* Pop any pushes done in the cleanups,
X in case function is about to return. */
X do_pending_stack_adjust ();
X
X new_before_jump = get_last_insn ();
X
X if (beyond_jump != new_before_jump)
X {
X /* If cleanups expand to nothing, don't reorder. */
X reorder_insns (NEXT_INSN (beyond_jump), new_before_jump, *before_jump);
X *before_jump = new_before_jump;
X }
X}
X
X/* Move all cleanups from the current block_stack
X to the containing block_stack, where they are assumed to
X have been created. If anything can cause a temporary to
X be created, but not expanded for more than one level of
X block_stacks, then this code will have to change. */
X
Xvoid
Xmove_cleanups_up ()
X{
X struct nesting *block = block_stack;
X struct nesting *outer = block->next;
X
X outer->data.block.cleanups
X = chainon (block->data.block.cleanups,
X outer->data.block.cleanups);
X block->data.block.cleanups = 0;
X}
X
Xint
Xthis_contour_has_cleanups_p ()
X{
X return block_stack && block_stack->data.block.cleanups != 0;
X}
X
X/* Return 1 if there are any pending cleanups at this point.
X If THIS_CONTOUR is nonzero, check the current contour as well.
X Otherwise, look only at the contours that enclose this one. */
X
Xint
Xany_pending_cleanups (this_contour)
X int this_contour;
X{
X struct nesting *block;
X
X if (block_stack == 0)
X return 0;
X
X if (this_contour && block_stack->data.block.cleanups != NULL)
X return 1;
X if (block_stack->data.block.cleanups == 0
X && (block_stack->data.block.outer_cleanups == 0
X#if 0
X || block_stack->data.block.outer_cleanups == empty_cleanup_list
X#endif
X ))
X return 0;
X
X for (block = block_stack->next; block; block = block->next)
X if (block->data.block.cleanups != 0)
X return 1;
X
X return 0;
X}
X
X/* Enter a case (Pascal) or switch (C) statement.
X Push a block onto case_stack and nesting_stack
X to accumulate the case-labels that are seen
X and to record the labels generated for the statement.
X
X EXIT_FLAG is nonzero if `exit_something' should exit this case stmt.
X Otherwise, this construct is transparent for `exit_something'.
X
X EXPR is the index-expression to be dispatched on.
X TYPE is its nominal type. We could simply convert EXPR to this type,
X but instead we take short cuts. */
X
Xvoid
Xexpand_start_case (exit_flag, expr, type)
X int exit_flag;
X tree expr;
X tree type;
X{
X register struct nesting *thiscase
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X /* Make an entry on case_stack for the case we are entering. */
X
X thiscase->next = case_stack;
X thiscase->all = nesting_stack;
X thiscase->depth = ++nesting_depth;
X thiscase->exit_label = exit_flag ? gen_label_rtx () : 0;
X thiscase->data.case_stmt.case_list = 0;
X thiscase->data.case_stmt.index_expr = expr;
X thiscase->data.case_stmt.nominal_type = type;
X thiscase->data.case_stmt.default_label = 0;
X thiscase->data.case_stmt.num_ranges = 0;
X case_stack = thiscase;
X nesting_stack = thiscase;
X
X do_pending_stack_adjust ();
X
X /* Make sure case_stmt.start points to something that won't
X need any transformation before expand_end_case. */
X emit_note (0, NOTE_INSN_DELETED);
X
X thiscase->data.case_stmt.start = get_last_insn ();
X}
X
X/* Start a "dummy case statement" within which case labels are invalid
X and are not connected to any larger real case statement.
X This can be used if you don't want to let a case statement jump
X into the middle of certain kinds of constructs. */
X
Xvoid
Xexpand_start_case_dummy ()
X{
X register struct nesting *thiscase
X = (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting));
X
X /* Make an entry on case_stack for the dummy. */
X
X thiscase->next = case_stack;
X thiscase->all = nesting_stack;
X thiscase->depth = ++nesting_depth;
X thiscase->exit_label = 0;
X thiscase->data.case_stmt.case_list = 0;
X thiscase->data.case_stmt.start = 0;
X thiscase->data.case_stmt.nominal_type = 0;
X thiscase->data.case_stmt.default_label = 0;
X thiscase->data.case_stmt.num_ranges = 0;
X case_stack = thiscase;
X nesting_stack = thiscase;
X}
X
X/* End a dummy case statement. */
X
Xvoid
Xexpand_end_case_dummy ()
X{
X POPSTACK (case_stack);
X}
X
X/* Accumulate one case or default label inside a case or switch statement.
X VALUE is the value of the case (a null pointer, for a default label).
X
X If not currently inside a case or switch statement, return 1 and do
X nothing. The caller will print a language-specific error message.
X If VALUE is a duplicate or overlaps, return 2 and do nothing.
X If VALUE is out of range, return 3 and do nothing.
X Return 0 on success.
X
X Extended to handle range statements, should they ever
X be adopted. */
X
Xint
Xpushcase (value, label)
X register tree value;
X register tree label;
X{
X register struct case_node **l;
X register struct case_node *n;
X tree index_type;
X tree nominal_type;
X
X /* Fail if not inside a real case statement. */
X if (! (case_stack && case_stack->data.case_stmt.start))
X return 1;
X
X index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
X nominal_type = case_stack->data.case_stmt.nominal_type;
X
X /* If the index is erroneous, avoid more problems: pretend to succeed. */
X if (index_type == error_mark_node)
X return 0;
X
X /* Convert VALUE to the type in which the comparisons are nominally done. */
X if (value != 0)
X value = convert (nominal_type, value);
X
X /* Fail if this value is out of range for the actual type of the index
X (which may be narrower than NOMINAL_TYPE). */
X if (value != 0 && ! int_fits_type_p (value, index_type))
X return 3;
X
X /* Fail if this is a duplicate or overlaps another entry. */
X if (value == 0)
X {
X if (case_stack->data.case_stmt.default_label != 0)
X return 2;
X case_stack->data.case_stmt.default_label = label;
X }
X else
X {
X /* Find the elt in the chain before which to insert the new value,
X to keep the chain sorted in increasing order.
X But report an error if this element is a duplicate. */
X for (l = &case_stack->data.case_stmt.case_list;
X /* Keep going past elements distinctly less than VALUE. */
X *l != 0 && tree_int_cst_lt ((*l)->high, value);
X l = &(*l)->right)
X ;
X if (*l)
X {
X /* Element we will insert before must be distinctly greater;
X overlap means error. */
X if (! tree_int_cst_lt (value, (*l)->low))
X return 2;
X }
X
X /* Add this label to the chain, and succeed.
X Copy VALUE so it is on temporary rather than momentary
X obstack and will thus survive till the end of the case statement. */
X n = (struct case_node *) oballoc (sizeof (struct case_node));
X n->left = 0;
X n->right = *l;
X n->high = n->low = copy_node (value);
X n->code_label = label;
X n->test_label = 0;
X *l = n;
X }
X
X expand_label (label);
X return 0;
X}
X
X/* Like pushcase but this case applies to all values
X between VALUE1 and VALUE2 (inclusive).
X The return value is the same as that of pushcase
X but there is one additional error code:
X 4 means the specified range was empty.
X
X Note that this does not currently work, since expand_end_case
X has yet to be extended to handle RANGE_EXPRs. */
X
Xint
Xpushcase_range (value1, value2, label)
X register tree value1, value2;
X register tree label;
X{
X register struct case_node **l;
X register struct case_node *n;
X tree index_type;
X tree nominal_type;
X
X /* Fail if not inside a real case statement. */
X if (! (case_stack && case_stack->data.case_stmt.start))
X return 1;
X
X index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
X nominal_type = case_stack->data.case_stmt.nominal_type;
X
X /* If the index is erroneous, avoid more problems: pretend to succeed. */
X if (index_type == error_mark_node)
X return 0;
X
X /* Convert VALUEs to type in which the comparisons are nominally done. */
X if (value1 != 0)
X value1 = convert (nominal_type, value1);
X if (value2 != 0)
X value2 = convert (nominal_type, value2);
X
X /* Fail if these values are out of range. */
X if (value1 != 0 && ! int_fits_type_p (value1, index_type))
X return 3;
X
X if (value2 != 0 && ! int_fits_type_p (value2, index_type))
X return 3;
X
X /* Fail if the range is empty. */
X if (tree_int_cst_lt (value2, value1))
X return 4;
X
X /* If the bounds are equal, turn this into the one-value case. */
X if (tree_int_cst_equal (value1, value2))
X return pushcase (value1, label);
X
X /* Find the elt in the chain before which to insert the new value,
X to keep the chain sorted in increasing order.
X But report an error if this element is a duplicate. */
X for (l = &case_stack->data.case_stmt.case_list;
X /* Keep going past elements distinctly less than this range. */
X *l != 0 && tree_int_cst_lt ((*l)->high, value1);
X l = &(*l)->right)
X ;
X if (*l)
X {
X /* Element we will insert before must be distinctly greater;
X overlap means error. */
X if (! tree_int_cst_lt (value2, (*l)->low))
X return 2;
X }
X
X /* Add this label to the chain, and succeed.
X Copy VALUE1, VALUE2 so they are on temporary rather than momentary
X obstack and will thus survive till the end of the case statement. */
X
X n = (struct case_node *) oballoc (sizeof (struct case_node));
X n->left = 0;
X n->right = *l;
X n->low = copy_node (value1);
X n->high = copy_node (value2);
X n->code_label = label;
X n->test_label = 0;
X *l = n;
X
X expand_label (label);
X
X case_stack->data.case_stmt.num_ranges++;
X
X return 0;
X}
X
X/* Check that all enumeration literals are covered by the case
X expressions of a switch. Also, warn if there are any extra
X switch cases that are *not* elements of the enumerated type. */
X
Xvoid
Xcheck_for_full_enumeration_handling (type)
X tree type;
X{
X register struct case_node *n;
X register tree chain;
X
X /* The time complexity of this loop is currently O(N * M), with
X N being the number of enumerals in the enumerated type, and
X M being the number of case expressions in the switch. */
X
X for (chain = TYPE_VALUES (type);
X chain;
X chain = TREE_CHAIN (chain))
X {
X /* Find a match between enumeral and case expression, if possible.
X Quit looking when we've gone too far (since case expressions
X are kept sorted in ascending order). Warn about enumerals not
X handled in the switch statement case expression list. */
X
X for (n = case_stack->data.case_stmt.case_list;
X n && tree_int_cst_lt (n->high, TREE_VALUE (chain));
X n = n->right)
X ;
X
X if (!(n && tree_int_cst_equal (n->low, TREE_VALUE (chain))))
X warning ("enumerated value `%s' not handled in switch",
X IDENTIFIER_POINTER (TREE_PURPOSE (chain)));
X }
X
X /* Now we go the other way around; we warn if there are case
X expressions that don't correspond to enumerals. This can
X occur since C and C++ don't enforce type-checking of
X assignments to enumeration variables. */
X
X for (n = case_stack->data.case_stmt.case_list; n; n = n->right)
X {
X for (chain = TYPE_VALUES (type);
X chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain));
X chain = TREE_CHAIN (chain))
X ;
X
X if (!chain)
X warning ("case value `%d' not in enumerated type `%s'",
X TREE_INT_CST_LOW (n->low),
X IDENTIFIER_POINTER (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE
X ? TYPE_NAME (type)
X : DECL_NAME (TYPE_NAME (type))));
X }
X}
X
X/* Terminate a case (Pascal) or switch (C) statement
X in which CASE_INDEX is the expression to be tested.
X Generate the code to test it and jump to the right place. */
X
Xvoid
Xexpand_end_case (orig_index)
X tree orig_index;
X{
X tree minval, maxval, range;
X rtx default_label = 0;
X register struct case_node *n;
X int count;
X rtx index;
X rtx table_label = gen_label_rtx ();
X int ncases;
X rtx *labelvec;
X register int i;
X rtx before_case;
X register struct nesting *thiscase = case_stack;
X tree index_expr = thiscase->data.case_stmt.index_expr;
X int unsignedp = TREE_UNSIGNED (TREE_TYPE (index_expr));
X
X do_pending_stack_adjust ();
X
X /* An ERROR_MARK occurs for various reasons including invalid data type. */
X if (TREE_TYPE (index_expr) != error_mark_node)
X {
X /* If switch expression was an enumerated type, check that all
X enumeration literals are covered by the cases.
X No sense trying this if there's a default case, however. */
X
X if (!thiscase->data.case_stmt.default_label
X && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE
X && TREE_CODE (index_expr) != INTEGER_CST
X && warn_switch)
X check_for_full_enumeration_handling (TREE_TYPE (orig_index));
X
X /* If we don't have a default-label, create one here,
X after the body of the switch. */
X if (thiscase->data.case_stmt.default_label == 0)
X {
X thiscase->data.case_stmt.default_label
X = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
X expand_label (thiscase->data.case_stmt.default_label);
X }
X default_label = label_rtx (thiscase->data.case_stmt.default_label);
X
X before_case = get_last_insn ();
X
X /* Simplify the case-list before we count it. */
X group_case_nodes (thiscase->data.case_stmt.case_list);
X
X /* Get upper and lower bounds of case values.
X Also convert all the case values to the index expr's data type. */
X
X count = 0;
X for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
X {
X /* Check low and high label values are integers. */
X if (TREE_CODE (n->low) != INTEGER_CST)
X abort ();
X if (TREE_CODE (n->high) != INTEGER_CST)
X abort ();
X
X n->low = convert (TREE_TYPE (index_expr), n->low);
X n->high = convert (TREE_TYPE (index_expr), n->high);
X
X /* Count the elements and track the largest and smallest
X of them (treating them as signed even if they are not). */
X if (count++ == 0)
X {
X minval = n->low;
X maxval = n->high;
X }
X else
X {
X if (INT_CST_LT (n->low, minval))
X minval = n->low;
X if (INT_CST_LT (maxval, n->high))
X maxval = n->high;
X }
X /* A range counts double, since it requires two compares. */
X if (! tree_int_cst_equal (n->low, n->high))
X count++;
X }
X
X /* Compute span of values. */
X if (count != 0)
X range = combine (MINUS_EXPR, maxval, minval);
X
X if (count == 0 || TREE_CODE (TREE_TYPE (index_expr)) == ERROR_MARK)
X {
X expand_expr (index_expr, const0_rtx, VOIDmode, 0);
X emit_queue ();
X emit_jump (default_label);
X }
X /* If range of values is much bigger than number of values,
X make a sequence of conditional branches instead of a dispatch.
X If the switch-index is a constant, do it this way
X because we can optimize it. */
X else if (TREE_INT_CST_HIGH (range) != 0
X#ifdef HAVE_casesi
X || count < 4
X#else
X /* If machine does not have a case insn that compares the
X bounds, this means extra overhead for dispatch tables
X which raises the threshold for using them. */
X || count < 5
X#endif
X || (unsigned) (TREE_INT_CST_LOW (range)) > 10 * count
X || TREE_CODE (index_expr) == INTEGER_CST)
X {
X index = expand_expr (index_expr, 0, VOIDmode, 0);
X
X /* If the index is a short or char that we do not have
X an insn to handle comparisons directly, convert it to
X a full integer now, rather than letting each comparison
X generate the conversion. */
X
X if ((GET_MODE (index) == QImode || GET_MODE (index) == HImode)
X && (cmp_optab->handlers[(int) GET_MODE(index)].insn_code
X == CODE_FOR_nothing))
X index = convert_to_mode (SImode, index, unsignedp);
X
X emit_queue ();
X do_pending_stack_adjust ();
X
X index = protect_from_queue (index, 0);
X if (GET_CODE (index) == MEM)
X index = copy_to_reg (index);
X if (GET_CODE (index) == CONST_INT
X || TREE_CODE (index_expr) == INTEGER_CST)
X {
X /* Make a tree node with the proper constant value
X if we don't already have one. */
X if (TREE_CODE (index_expr) != INTEGER_CST)
X {
X index_expr
X = build_int_2 (INTVAL (index),
X !unsignedp && INTVAL (index) >= 0 ? 0 : -1);
X index_expr = convert (TREE_TYPE (index_expr), index_expr);
X }
X
X /* For constant index expressions we need only
X issue a unconditional branch to the appropriate
X target code. The job of removing any unreachable
X code is left to the optimisation phase if the
X "-O" option is specified. */
X for (n = thiscase->data.case_stmt.case_list;
X n;
X n = n->right)
X {
X if (! tree_int_cst_lt (index_expr, n->low)
X && ! tree_int_cst_lt (n->high, index_expr))
X break;
X }
X if (n)
X emit_jump (label_rtx (n->code_label));
X else
X emit_jump (default_label);
X }
X else
X {
X /* If the index expression is not constant we generate
X a binary decision tree to select the appropriate
X target code. This is done as follows:
X
X The list of cases is rearranged into a binary tree,
X nearly optimal assuming equal probability for each case.
X
X The tree is transformed into RTL, eliminating
X redundant test conditions at the same time.
X
X If program flow could reach the end of the
X decision tree an unconditional jump to the
X default code is emitted. */
X if (optimize
X && TREE_CODE (TREE_TYPE (orig_index)) != ENUMERAL_TYPE)
X estimate_case_costs (thiscase->data.case_stmt.case_list,
X default_label);
X balance_case_nodes (&thiscase->data.case_stmt.case_list, 0);
X emit_case_nodes (index, thiscase->data.case_stmt.case_list,
X default_label, unsignedp);
X emit_jump_if_reachable (default_label);
X }
X }
X else
X {
X#ifdef HAVE_casesi
X /* Convert the index to SImode. */
X if (TYPE_MODE (TREE_TYPE (index_expr)) == DImode)
X {
X index_expr = build (MINUS_EXPR, TREE_TYPE (index_expr),
X index_expr, minval);
X minval = integer_zero_node;
X }
X if (TYPE_MODE (TREE_TYPE (index_expr)) != SImode)
X index_expr = convert (type_for_size (GET_MODE_BITSIZE (SImode), 0),
X index_expr);
X index = expand_expr (index_expr, 0, VOIDmode, 0);
X emit_queue ();
X index = protect_from_queue (index, 0);
X do_pending_stack_adjust ();
X
X emit_jump_insn (gen_casesi (index, expand_expr (minval, 0, VOIDmode, 0),
X expand_expr (range, 0, VOIDmode, 0),
X table_label, default_label));
X#else
X#ifdef HAVE_tablejump
X index_expr = convert (type_for_size (GET_MODE_BITSIZE (SImode), 0),
X build (MINUS_EXPR, TREE_TYPE (index_expr),
X index_expr, minval));
X index = expand_expr (index_expr, 0, VOIDmode, 0);
X emit_queue ();
X index = protect_from_queue (index, 0);
X do_pending_stack_adjust ();
X
X do_tablejump (index,
X gen_rtx (CONST_INT, VOIDmode, TREE_INT_CST_LOW (range)),
X table_label, default_label);
X#else
X lossage;
X#endif /* not HAVE_tablejump */
X#endif /* not HAVE_casesi */
X
X /* Get table of labels to jump to, in order of case index. */
X
X ncases = TREE_INT_CST_LOW (range) + 1;
X labelvec = (rtx *) alloca (ncases * sizeof (rtx));
X bzero (labelvec, ncases * sizeof (rtx));
X
X for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
X {
X register int i
X = TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (minval);
X
X while (i + TREE_INT_CST_LOW (minval)
X <= TREE_INT_CST_LOW (n->high))
X labelvec[i++]
X = gen_rtx (LABEL_REF, Pmode, label_rtx (n->code_label));
X }
X
X /* Fill in the gaps with the default. */
X for (i = 0; i < ncases; i++)
X if (labelvec[i] == 0)
X labelvec[i] = gen_rtx (LABEL_REF, Pmode, default_label);
X
X /* Output the table */
X emit_label (table_label);
X
X#ifdef CASE_VECTOR_PC_RELATIVE
X emit_jump_insn (gen_rtx (ADDR_DIFF_VEC, CASE_VECTOR_MODE,
X gen_rtx (LABEL_REF, Pmode, table_label),
X gen_rtvec_v (ncases, labelvec)));
X#else
X emit_jump_insn (gen_rtx (ADDR_VEC, CASE_VECTOR_MODE,
X gen_rtvec_v (ncases, labelvec)));
X#endif
X /* If the case insn drops through the table,
X after the table we must jump to the default-label.
X Otherwise record no drop-through after the table. */
X#ifdef CASE_DROPS_THROUGH
X emit_jump (default_label);
X#else
X emit_barrier ();
X#endif
X }
X
X reorder_insns (NEXT_INSN (before_case), get_last_insn (),
X thiscase->data.case_stmt.start);
X }
X if (thiscase->exit_label)
X emit_label (thiscase->exit_label);
X
X POPSTACK (case_stack);
X}
X
X/* See case.c for CASE-handling code. */
X
X/* Allocate fixed slots in the stack frame of the current function. */
X
X/* Return size needed for stack frame based on slots so far allocated. */
X
Xint
Xget_frame_size ()
X{
X#ifdef FRAME_GROWS_DOWNWARD
X return -frame_offset + STARTING_FRAME_OFFSET;
X#else
X return frame_offset - STARTING_FRAME_OFFSET;
X#endif
X}
X
X/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
X with machine mode MODE. */
X
Xrtx
Xassign_stack_local (mode, size)
X enum machine_mode mode;
X int size;
X{
X register rtx x, addr;
X int bigend_correction = 0;
X
X frame_pointer_needed = 1;
X
X /* Make each stack slot a multiple of the main allocation unit. */
X size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
X / (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
X * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
X
X /* On a big-endian machine, if we are allocating more space than we will use,
X use the least significant bytes of those that are allocated. */
X#ifdef BYTES_BIG_ENDIAN
X if (mode != BLKmode)
X bigend_correction = size - GET_MODE_SIZE (mode);
X#endif
X
X#ifdef FRAME_GROWS_DOWNWARD
X frame_offset -= size;
X#endif
X addr = gen_rtx (PLUS, Pmode, frame_pointer_rtx,
X gen_rtx (CONST_INT, VOIDmode,
X (frame_offset + bigend_correction)));
X#ifndef FRAME_GROWS_DOWNWARD
X frame_offset += size;
X#endif
X
X if (! memory_address_p (mode, addr))
X invalid_stack_slot = 1;
X
X x = gen_rtx (MEM, mode, addr);
X
X stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list);
X
X return x;
X}
X
X/* Retroactively move an auto variable from a register to a stack slot.
X This is done when an address-reference to the variable is seen. */
X
Xvoid
Xput_var_into_stack (decl)
X tree decl;
X{
X register rtx reg = DECL_RTL (decl);
X register rtx new;
X
X /* No need to do anything if decl has no rtx yet
X since in that case caller is setting TREE_ADDRESSABLE
X and a stack slot will be assigned when the rtl is made. */
X if (reg == 0)
X return;
X if (GET_CODE (reg) != REG)
X return;
X
X new = parm_stack_loc (reg);
X if (new == 0)
X new = assign_stack_local (GET_MODE (reg), GET_MODE_SIZE (GET_MODE (reg)));
X
X XEXP (reg, 0) = XEXP (new, 0);
X /* `volatil' bit means one thing for MEMs, another entirely for REGs. */
X REG_USERVAR_P (reg) = 0;
X PUT_CODE (reg, MEM);
X
X /* If this is a memory ref that contains aggregate components,
X mark it as such for cse and loop optimize. */
X MEM_IN_STRUCT_P (reg)
X = (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE);
X
X fixup_var_refs (reg);
X}
X
Xstatic void
Xfixup_var_refs (var)
X rtx var;
X{
X extern rtx sequence_stack;
X rtx stack = sequence_stack;
X tree pending;
X
X stack = sequence_stack;
X
X /* Must scan all insns for stack-refs that exceed the limit. */
X fixup_var_refs_insns (var, get_insns (), stack == 0);
X
X /* Scan all pending sequences too. */
X for (; stack; stack = XEXP (XEXP (stack, 1), 1))
X {
X push_to_sequence (XEXP (stack, 0));
X fixup_var_refs_insns (var, XEXP (stack, 0),
X XEXP (XEXP (stack, 1), 1) == 0);
X /* Update remembered end of sequence
X in case we added an insn at the end. */
X XEXP (XEXP (stack, 1), 0) = get_last_insn ();
X end_sequence ();
X }
X
X /* Scan all waiting RTL_EXPRs too. */
X for (pending = rtl_expr_chain; pending; pending = TREE_CHAIN (pending))
X {
X rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending));
X if (seq != const0_rtx && seq != 0)
X {
X push_to_sequence (seq);
X fixup_var_refs_insns (var, seq, 0);
X end_sequence ();
X }
X }
X}
X
X/* Scan the insn-chain starting with INSN for refs to VAR
X and fix them up. TOPLEVEL is nonzero if this chain is the
X main chain of insns for the current function. */
X
Xstatic void
Xfixup_var_refs_insns (var, insn, toplevel)
X rtx var;
X rtx insn;
X int toplevel;
X{
X while (insn)
X {
X rtx next = NEXT_INSN (insn);
X rtx note;
X if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
X || GET_CODE (insn) == JUMP_INSN)
X {
X /* The insn to load VAR from a home in the arglist
X is now a no-op. When we see it, just delete it. */
X if (toplevel
X && GET_CODE (PATTERN (insn)) == SET
X && SET_DEST (PATTERN (insn)) == var
X && rtx_equal_p (SET_SRC (PATTERN (insn)), var))
X {
X next = delete_insn (insn);
X if (insn == last_parm_insn)
X last_parm_insn = PREV_INSN (next);
X }
X else
X fixup_var_refs_1 (var, PATTERN (insn), insn);
X /* Also fix up any invalid exprs in the REG_NOTES of this insn.
X But don't touch other insns referred to by reg-notes;
X we will get them elsewhere. */
X for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
X if (GET_CODE (note) != INSN_LIST)
X XEXP (note, 0) = walk_fixup_memory_subreg (XEXP (note, 0), insn);
X }
X insn = next;
X }
X}
X
Xstatic rtx
Xfixup_var_refs_1 (var, x, insn)
X register rtx var;
X register rtx x;
X rtx insn;
X{
X register int i;
X RTX_CODE code = GET_CODE (x);
X register char *fmt;
X register rtx tem;
X
X switch (code)
X {
X case MEM:
X if (var == x)
X {
X x = fixup_stack_1 (x, insn);
X tem = gen_reg_rtx (GET_MODE (x));
X /* Put new insn before a CALL, before any USEs before it. */
X if (GET_CODE (insn) == CALL_INSN)
X while (PREV_INSN (insn) != 0 && GET_CODE (PREV_INSN (insn)) == INSN
X && GET_CODE (PATTERN (PREV_INSN (insn))) == USE)
X insn = PREV_INSN (insn);
X emit_insn_before (gen_move_insn (tem, x), insn);
X return tem;
X }
X break;
X
X case REG:
X case CC0:
X case PC:
X case CONST_INT:
X case CONST:
X case SYMBOL_REF:
X case LABEL_REF:
X case CONST_DOUBLE:
X return x;
X
X case SIGN_EXTRACT:
X case ZERO_EXTRACT:
X /* Note that in some cases those types of expressions are altered
X by optimize_bit_field, and do not survive to get here. */
X case SUBREG:
X tem = x;
X while (GET_CODE (tem) == SUBREG || GET_CODE (tem) == SIGN_EXTRACT
X || GET_CODE (tem) == ZERO_EXTRACT)
X tem = XEXP (tem, 0);
X if (tem == var)
X {
X x = fixup_stack_1 (x, insn);
X tem = gen_reg_rtx (GET_MODE (x));
X if (GET_CODE (x) == SUBREG)
X x = fixup_memory_subreg (x, insn);
X emit_insn_before (gen_move_insn (tem, x), insn);
X return tem;
X }
X break;
X
X case SET:
X /* First do special simplification of bit-field references. */
X if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT
X || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
X optimize_bit_field (x, insn, 0);
X if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT
X || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT)
X optimize_bit_field (x, insn, 0);
X
X {
X rtx dest = SET_DEST (x);
X rtx src = SET_SRC (x);
X rtx outerdest = dest;
X rtx outersrc = src;
X
X while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
X || GET_CODE (dest) == SIGN_EXTRACT
X || GET_CODE (dest) == ZERO_EXTRACT)
X dest = XEXP (dest, 0);
X while (GET_CODE (src) == SUBREG
X || GET_CODE (src) == SIGN_EXTRACT
X || GET_CODE (src) == ZERO_EXTRACT)
X src = XEXP (src, 0);
X
X /* If VAR does not appear at the top level of the SET
X just scan the lower levels of the tree. */
X
X if (src != var && dest != var)
X break;
X
X /* Clean up (SUBREG:SI (MEM:mode ...) 0)
X that may appear inside a SIGN_EXTRACT or ZERO_EXTRACT.
X This was legitimate when the MEM was a REG. */
X
X if ((GET_CODE (outerdest) == SIGN_EXTRACT
X || GET_CODE (outerdest) == ZERO_EXTRACT)
X && GET_CODE (XEXP (outerdest, 0)) == SUBREG
X && SUBREG_REG (XEXP (outerdest, 0)) == var)
X XEXP (outerdest, 0) = fixup_memory_subreg (XEXP (outerdest, 0), insn);
X
X if ((GET_CODE (outersrc) == SIGN_EXTRACT
X || GET_CODE (outersrc) == ZERO_EXTRACT)
X && GET_CODE (XEXP (outersrc, 0)) == SUBREG
X && SUBREG_REG (XEXP (outersrc, 0)) == var)
X XEXP (outersrc, 0) = fixup_memory_subreg (XEXP (outersrc, 0), insn);
X
X /* Make sure that the machine's SIGN_EXTRACT and ZERO_EXTRACT insns
X accept a memory operand. */
X#ifdef HAVE_extzv
X if (GET_CODE (outersrc) == ZERO_EXTRACT
X && ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0])
X (XEXP (outersrc, 0), VOIDmode)))
X XEXP (outersrc, 0) = src
X = fixup_var_refs_1 (var, XEXP (outersrc, 0), insn);
X#endif
X#ifdef HAVE_extv
X if (GET_CODE (outersrc) == SIGN_EXTRACT
X && ! ((*insn_operand_predicate[(int) CODE_FOR_extv][0])
X (XEXP (outersrc, 0), VOIDmode)))
X XEXP (outersrc, 0) = src
X = fixup_var_refs_1 (var, XEXP (outersrc, 0), insn);
X#endif
X#ifdef HAVE_insv
X if (GET_CODE (outerdest) == ZERO_EXTRACT
X && ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0])
X (XEXP (outerdest, 0), VOIDmode)))
X {
X rtx tem = gen_reg_rtx (GET_MODE (XEXP (outerdest, 0)));
X
X emit_insn_before (gen_move_insn (tem, XEXP (outerdest, 0)), insn);
X emit_insn_after (gen_move_insn (XEXP (outerdest, 0), tem), insn);
X dest = XEXP (outerdest, 0) = tem;
X }
X#endif
X
X /* Make sure a MEM inside a SIGN_EXTRACT has QImode
X since that's what bit-field insns want. */
X
X if ((GET_CODE (outerdest) == SIGN_EXTRACT
X || GET_CODE (outerdest) == ZERO_EXTRACT)
X && GET_CODE (XEXP (outerdest, 0)) == MEM
X && GET_MODE (XEXP (outerdest, 0)) != QImode)
X {
X XEXP (outerdest, 0) = copy_rtx (XEXP (outerdest, 0));
X PUT_MODE (XEXP (outerdest, 0), QImode);
X /* Adjust the address so the bit field starts within the byte
X addressed. This helps certain optimization patterns. */
X if (GET_CODE (XEXP (outerdest, 2)) == CONST_INT
X && offsettable_memref_p (XEXP (outerdest, 0)))
X {
X int count = INTVAL (XEXP (outerdest, 2));
X XEXP (outerdest, 0)
X = adj_offsettable_operand (XEXP (outerdest, 0),
X count / GET_MODE_BITSIZE (QImode));
X XEXP (outerdest, 2)
X = gen_rtx (CONST_INT, VOIDmode,
X count % GET_MODE_BITSIZE (QImode));
X }

X }
X
X if ((GET_CODE (outersrc) == SIGN_EXTRACT
X || GET_CODE (outersrc) == ZERO_EXTRACT)
X && GET_CODE (XEXP (outersrc, 0)) == MEM
X && GET_MODE (XEXP (outersrc, 0)) != QImode)
X {
X XEXP (outersrc, 0) = copy_rtx (XEXP (outersrc, 0));
X PUT_MODE (XEXP (outersrc, 0), QImode);
X /* Adjust the address so the bit field starts within the byte
X addressed. This helps certain optimization patterns. */
X if (GET_CODE (XEXP (outersrc, 2)) == CONST_INT
X && offsettable_memref_p (XEXP (outersrc, 0)))
X {
X int count = INTVAL (XEXP (outersrc, 2));
X XEXP (outersrc, 0)
X = adj_offsettable_operand (XEXP (outersrc, 0),
X count / GET_MODE_BITSIZE (QImode));
X XEXP (outersrc, 2)
X = gen_rtx (CONST_INT, VOIDmode,
X count % GET_MODE_BITSIZE (QImode));
X }
X }
X
X /* STRICT_LOW_PART is a no-op on memory references
X and it can cause combinations to be unrecognizable,
X so eliminate it. */
X
X if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
X SET_DEST (x) = XEXP (SET_DEST (x), 0);
X
X /* An insn to copy VAR into or out of a register
X must be left alone, to avoid an infinite loop here.
X But do fix up the address of VAR's stack slot if nec,
X and fix up SUBREGs containing VAR
X (since they are now memory subregs). */
X
X if (GET_CODE (SET_SRC (x)) == REG || GET_CODE (SET_DEST (x)) == REG
X || (GET_CODE (SET_SRC (x)) == SUBREG
X && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG)
X || (GET_CODE (SET_DEST (x)) == SUBREG
X && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))
X {
X if (src == var && GET_CODE (SET_SRC (x)) == SUBREG)
X SET_SRC (x) = fixup_memory_subreg (SET_SRC (x), insn);
X if (dest == var && GET_CODE (SET_DEST (x)) == SUBREG)
X SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn);
X return fixup_stack_1 (x, insn);
X }
X
X /* Otherwise, storing into VAR must be handled specially
X by storing into a temporary and copying that into VAR
X with a new insn after this one. */
X
X if (dest == var)
X {
X rtx temp;
X rtx fixeddest;
X tem = SET_DEST (x);
X /* STRICT_LOW_PART can be discarded, around a MEM. */
X if (GET_CODE (tem) == STRICT_LOW_PART)
X tem = XEXP (tem, 0);
X /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */
X if (GET_CODE (tem) == SUBREG)
X tem = fixup_memory_subreg (tem, insn);
X fixeddest = fixup_stack_1 (tem, insn);
X temp = gen_reg_rtx (GET_MODE (tem));
X emit_insn_after (gen_move_insn (fixeddest, temp), insn);
X SET_DEST (x) = temp;
X }
X }
X }
X
X /* Nothing special about this RTX; fix its operands. */
X
X fmt = GET_RTX_FORMAT (code);
X for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
X {
X if (fmt[i] == 'e')
X XEXP (x, i) = fixup_var_refs_1 (var, XEXP (x, i), insn);
X if (fmt[i] == 'E')
X {
X register int j;
X for (j = 0; j < XVECLEN (x, i); j++)
X XVECEXP (x, i, j)
X = fixup_var_refs_1 (var, XVECEXP (x, i, j), insn);
X }
X }
X return x;
X}
X
X/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)),
X return an rtx (MEM:m1 newaddr) which is equivalent.
X If any insns must be emitted to compute NEWADDR, put them before INSN. */
X
Xstatic rtx
Xfixup_memory_subreg (x, insn)
X rtx x;
X rtx insn;
X{
X int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
X rtx addr = XEXP (SUBREG_REG (x), 0);
X enum machine_mode mode = GET_MODE (x);
X rtx saved, result;
X
X#ifdef BYTES_BIG_ENDIAN
X offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
X - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
X#endif
X addr = plus_constant (addr, offset);
X if (memory_address_p (mode, addr))
X return change_address (SUBREG_REG (x), mode, addr);
X saved = start_sequence ();
X result = change_address (SUBREG_REG (x), mode, addr);
X emit_insn_before (gen_sequence (), insn);
X end_sequence (saved);
X return result;
X}
X
X/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X.
X Replace subexpressions of X in place.
X If X itself is a (SUBREG (MEM ...) ...), return the replacement expression.
X Otherwise return X, with its contents possibly altered.
X
X If any insns must be emitted to compute NEWADDR, put them before INSN. */
X
Xstatic rtx
Xwalk_fixup_memory_subreg (x, insn)
X register rtx x;
X rtx insn;
X{
X register enum rtx_code code;
X register char *fmt;
X register int i;
X
X if (x == 0)
X return 0;
X
X code = GET_CODE (x);
X
X if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM)
X return fixup_memory_subreg (x, insn);
X
X /* Nothing special about this RTX; fix its operands. */
X
X fmt = GET_RTX_FORMAT (code);
X for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
X {
X if (fmt[i] == 'e')
X XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn);
X if (fmt[i] == 'E')
X {
X register int j;
X for (j = 0; j < XVECLEN (x, i); j++)
X XVECEXP (x, i, j)
X = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn);
X }
X }
X return x;
X}
X
X#if 0
X/* Fix up any references to stack slots that are invalid memory addresses
X because they exceed the maximum range of a displacement. */
X
Xvoid
Xfixup_stack_slots ()
X{
X register rtx insn;
X
X /* Did we generate a stack slot that is out of range
X or otherwise has an invalid address? */
X if (invalid_stack_slot)
X {
X /* Yes. Must scan all insns for stack-refs that exceed the limit. */
X for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
X if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
X || GET_CODE (insn) == JUMP_INSN)
X fixup_stack_1 (PATTERN (insn), insn);
X }
X}
X#endif
X
X/* For each memory ref within X, if it refers to a stack slot
X with an out of range displacement, put the address in a temp register
X (emitting new insns before INSN to load these registers)
X and alter the memory ref to use that register.
X Replace each such MEM rtx with a copy, to avoid clobberage. */
X
Xstatic rtx
Xfixup_stack_1 (x, insn)
X rtx x;
X rtx insn;
X{
X register int i;
X register RTX_CODE code = GET_CODE (x);
X register char *fmt;
X
X if (code == MEM)
X {
X register rtx ad = XEXP (x, 0);
X /* If we have address of a stack slot but it's not valid
X (displacement is too large), compute the sum in a register. */
X if (GET_CODE (ad) == PLUS
X && XEXP (ad, 0) == frame_pointer_rtx
X && GET_CODE (XEXP (ad, 1)) == CONST_INT)
X {
X rtx temp;
X if (memory_address_p (GET_MODE (x), ad))
X return x;
X temp = gen_reg_rtx (GET_MODE (ad));
X emit_insn_before (gen_move_insn (temp, ad), insn);
X return change_address (x, VOIDmode, temp);
X }
X return x;
X }
X
X fmt = GET_RTX_FORMAT (code);
X for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
X {
X if (fmt[i] == 'e')
X XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn);
X if (fmt[i] == 'E')
X {
X register int j;
X for (j = 0; j < XVECLEN (x, i); j++)
X XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn);
X }
X }
X return x;
X}
X
X/* Optimization: a bit-field instruction whose field
X happens to be a byte or halfword in memory
X can be changed to a move instruction.
X
X We call here when INSN is an insn to examine or store into a bit-field.
X BODY is the SET-rtx to be altered.
X
X EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0.
X (Currently this is called only from stmt.c, and EQUIV_MEM is always 0.) */
X
Xstatic void
Xoptimize_bit_field (body, insn, equiv_mem)
X rtx body;
X rtx insn;
X rtx *equiv_mem;
X{
X register rtx bitfield;
X int destflag;
X
X if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT
X || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT)
X bitfield = SET_DEST (body), destflag = 1;
X else
X bitfield = SET_SRC (body), destflag = 0;
X
X /* First check that the field being stored has constant size and position
X and is in fact a byte or halfword suitably aligned. */
X
X if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT
X && GET_CODE (XEXP (bitfield, 2)) == CONST_INT
X && (INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (QImode)
X || INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (HImode))
X && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0)
X {
X register rtx memref = 0;
X
X /* Now check that the containing word is memory, not a register,
X and that it is safe to change the machine mode and to
X add something to the address. */
X
X if (GET_CODE (XEXP (bitfield, 0)) == MEM)
X memref = XEXP (bitfield, 0);
X else if (GET_CODE (XEXP (bitfield, 0)) == REG
X && equiv_mem != 0)
X memref = equiv_mem[REGNO (XEXP (bitfield, 0))];
X else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
X && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM)
X memref = SUBREG_REG (XEXP (bitfield, 0));
X else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
X && equiv_mem != 0
X && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG)
X memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))];
X
X if (memref
X && ! mode_dependent_address_p (XEXP (memref, 0))
X && offsettable_address_p (0, GET_MODE (bitfield), XEXP (memref, 0)))
X {
X /* Now adjust the address, first for any subreg'ing
X that we are now getting rid of,
X and then for which byte of the word is wanted. */
X
X register int offset
X = INTVAL (XEXP (bitfield, 2)) / GET_MODE_BITSIZE (QImode);
X if (GET_CODE (XEXP (bitfield, 0)) == SUBREG)
X {
X offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD;
X#ifdef BYTES_BIG_ENDIAN
X offset -= (MIN (UNITS_PER_WORD,
X GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0))))
X - MIN (UNITS_PER_WORD,
X GET_MODE_SIZE (GET_MODE (memref))));
X#endif
X }
X
X memref = gen_rtx (MEM,
X (INTVAL (XEXP (bitfield, 1)) == GET_MODE_BITSIZE (QImode)
X ? QImode : HImode),
X XEXP (memref, 0));
X
X /* Store this memory reference where
X we found the bit field reference. */
X
X if (destflag)
X {
X SET_DEST (body)
X = adj_offsettable_operand (memref, offset);
X if (! CONSTANT_ADDRESS_P (SET_SRC (body)))
X {
X rtx src = SET_SRC (body);
X while (GET_CODE (src) == SUBREG
X && SUBREG_WORD (src) == 0)
X src = SUBREG_REG (src);
X if (GET_MODE (src) != GET_MODE (memref))
X src = gen_lowpart (GET_MODE (memref), SET_SRC (body));
X SET_SRC (body) = src;
X }
X else if (GET_MODE (SET_SRC (body)) != VOIDmode
X && GET_MODE (SET_SRC (body)) != GET_MODE (memref))
X /* This shouldn't happen because anything that didn't have
X one of these modes should have got converted explicitly
X and then referenced through a subreg.
X This is so because the original bit-field was
X handled by agg_mode and so its tree structure had
X the same mode that memref now has. */
X abort ();
X }
X else
X {
X rtx dest = SET_DEST (body);
X
X while (GET_CODE (dest) == SUBREG
X && SUBREG_WORD (dest) == 0)
X dest = SUBREG_REG (dest);
X SET_DEST (body) = dest;
X
X memref = adj_offsettable_operand (memref, offset);
X if (GET_MODE (dest) == GET_MODE (memref))
X SET_SRC (body) = memref;
X else
X {
X /* Convert the mem ref to the destination mode. */
X rtx last = get_last_insn ();
X rtx newreg = gen_reg_rtx (GET_MODE (dest));
X convert_move (newreg, memref,
X GET_CODE (SET_SRC (body)) == ZERO_EXTRACT);
X /* Put the conversion before the insn being fixed. */
X reorder_insns (NEXT_INSN (last), get_last_insn (),
X PREV_INSN (insn));
X SET_SRC (body) = newreg;
X }
X }
X
X /* Cause the insn to be re-recognized. */
X
X INSN_CODE (insn) = -1;
X }
X }
X}
X
X/* 1 + last pseudo register number used for loading a copy
X of a parameter of this function. */
X
Xstatic int max_parm_reg;
X
X/* Vector indexed by REGNO, containing location on stack in which
X to put the parm which is nominally in pseudo register REGNO,
X if we discover that that parm must go in the stack. */
Xstatic rtx *parm_reg_stack_loc;
X
Xint
Xmax_parm_reg_num ()
X{
X return max_parm_reg;
X}
X
X/* Return the first insn following those generated by `assign_parms'. */
X
Xrtx
Xget_first_nonparm_insn ()
X{
X if (last_parm_insn)
X return NEXT_INSN (last_parm_insn);
X return get_insns ();
X}
X
X/* Get the stack home of a REG rtx that is one of this function's parameters.
X This is called rather than assign a new stack slot as a local.
X Return 0 if there is no existing stack home suitable for such use. */
X
Xstatic rtx
Xparm_stack_loc (reg)
X rtx reg;
X{
X if (REGNO (reg) < max_parm_reg)
X return parm_reg_stack_loc[REGNO (reg)];
X return 0;
X}
X
X/* Return 1 if EXP returns an aggregate value, for which an address
X must be passed to the function or returned by the function. */
X
Xint
Xaggregate_value_p (exp)
X tree exp;
X{
X if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
X return 1;
X if (RETURN_IN_MEMORY (TREE_TYPE (exp)))
X return 1;
X if (flag_pcc_struct_return
X && (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE))
X return 1;
X return 0;
X}
X
X/* Convert a mem ref into one with a valid memory address.
X Pass through anything else unchanged. */
X
Xrtx
Xvalidize_mem (ref)
X rtx ref;
X{
X if (GET_CODE (ref) != MEM)
X return ref;
X if (memory_address_p (GET_MODE (ref), XEXP (ref, 0)))
X return ref;
X return change_address (ref, VOIDmode,
X memory_address (GET_MODE (ref), XEXP (ref, 0)));
X}
X
X/* Assign RTL expressions to the function's parameters.
X This may involve copying them into registers and using
X those registers as the RTL for them. */
X
Xstatic void
Xassign_parms (fndecl)
X tree fndecl;
X{
X register tree parm;
X register rtx entry_parm;
X register rtx stack_parm;
X register CUMULATIVE_ARGS args_so_far;
X enum machine_mode passed_mode, nominal_mode;
X /* Total space needed so far for args on the stack,
X given as a constant and a tree-expression. */
X struct args_size stack_args_size;
X int first_parm_offset = FIRST_PARM_OFFSET (fndecl);
X tree fntype = TREE_TYPE (fndecl);
X /* This is used for the arg pointer when referring to stack args. */
X rtx internal_arg_pointer;
X
X int nparmregs
X = list_length (DECL_ARGUMENTS (fndecl)) + FIRST_PSEUDO_REGISTER;
X
X /* Nonzero if function takes extra anonymous args.
X This means the last named arg must be on the stack
X right before the anonymous ones.
X Also nonzero if the first arg is named `__builtin_va_alist',
X which is used on some machines for old-fashioned non-ANSI varargs.h;
X this too should be stuck onto the stack as if it had arrived there. */
X int vararg
X = ((DECL_ARGUMENTS (fndecl) != 0
X && DECL_NAME (DECL_ARGUMENTS (fndecl))
X && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (DECL_ARGUMENTS (fndecl))),
X "__builtin_va_alist")))
X ||
X (TYPE_ARG_TYPES (fntype) != 0
X && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
X != void_type_node)));
X int arg_pointer_copied = 0;
X
X#if ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM
X internal_arg_pointer = arg_pointer_rtx;
X#else
X /* If the arg pointer reg is not a fixed reg,
X make a copy of it, and address parms via the copy. */
X if (fixed_regs[ARG_POINTER_REGNUM])
X internal_arg_pointer = arg_pointer_rtx;
X else
X {
X internal_arg_pointer = copy_to_reg (arg_pointer_rtx);
X arg_pointer_copied = 1;
X }
X#endif
X
X stack_args_size.constant = 0;
X stack_args_size.var = 0;
X
X /* If struct value address comes on the stack, count it in size of args. */
X if (aggregate_value_p (DECL_RESULT (fndecl))
X && GET_CODE (struct_value_incoming_rtx) == MEM)
X stack_args_size.constant += GET_MODE_SIZE (Pmode);
X
X parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx));
X bzero (parm_reg_stack_loc, nparmregs * sizeof (rtx));
X
X INIT_CUMULATIVE_ARGS (args_so_far, fntype);
X
X for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm))
X {
X int aggregate
X = (TREE_CODE (TREE_TYPE (parm)) == ARRAY_TYPE
X || TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE
X || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE);
X struct args_size stack_offset;
X rtx stack_offset_rtx;
X enum direction where_pad;
X /* Extra bytes to add in after parameter is assigned, in
X case where argument cannot be assigned an offsetted
X location. For example, BLKmode parameters cannot be
X other than on a word boundary (no matter the size)
X because `access_parm_map' does not know how to handle
X that case. */
X int extra = 0;
X
X DECL_OFFSET (parm) = -1;
X
X if (TREE_TYPE (parm) == error_mark_node
X /* This can happen after weird syntax errors
X or if an enum type is defined among the parms. */
X || TREE_CODE (parm) != PARM_DECL
X || DECL_ARG_TYPE (parm) == NULL)
X {
X DECL_RTL (parm) = gen_rtx (MEM, BLKmode, const0_rtx);
X TREE_USED (parm) = 1;
X continue;
X }
X
X /* Find mode of arg as it is passed, and mode of arg
X as it should be during execution of this function. */
X passed_mode = TYPE_MODE (DECL_ARG_TYPE (parm));
X nominal_mode = TYPE_MODE (TREE_TYPE (parm));
X
X /* Get this parm's offset as an rtx. */
X stack_offset = stack_args_size;
X stack_offset.constant += first_parm_offset;
X
X /* If this argument needs more than the usual parm alignment, do
X extrinsic padding to reach that alignment. */
X
X#ifdef MAX_PARM_BOUNDARY
X /* If MAX_PARM_BOUNDARY is not defined, it means that the usual
X alignment requirements are relaxed for parms, and that no parm
X needs more alignment than PARM_BOUNDARY, regardless of data type. */
X
X if (PARM_BOUNDARY < TYPE_ALIGN (DECL_ARG_TYPE (parm)))
X {
X int boundary = PARM_BOUNDARY;
X
X /* Determine the boundary to pad up to. */
X if (TYPE_ALIGN (DECL_ARG_TYPE (parm)) > boundary)
X boundary = TYPE_ALIGN (DECL_ARG_TYPE (parm));
X if (boundary > MAX_PARM_BOUNDARY)
X boundary = MAX_PARM_BOUNDARY;
X
X /* If the previous args don't reach such a boundary,
X advance to the next one. */
X boundary /= BITS_PER_UNIT;
X stack_offset.constant += boundary - 1;
X stack_offset.constant &= ~(boundary - 1);
X stack_args_size.constant += boundary - 1;
X stack_args_size.constant &= ~(boundary - 1);
X
X if (stack_offset.var != 0)
X abort (); /* This case not implemented yet */
X }
X#endif /* MAX_PARM_BOUNDARY */
X
X /* Find out if the parm needs intrinsic padding (up to PARM_BOUNDARY),
X and whether above or below. */
X
X where_pad
X = FUNCTION_ARG_PADDING (passed_mode,

X expand_expr (size_in_bytes (DECL_ARG_TYPE (parm)),
X 0, VOIDmode, 0));
X
X /* If arg should be padded below, adjust the stack address upward.
X This padding is considered part of the space occupied by the
X argument. It pads only up to PARM_BOUNDARY, and it does not
X depend on the previous arguments, since they are assumed to
X occupy a multiple of PARM_BOUNDARY. */
X
X if (where_pad == downward)
X {
X if (passed_mode != BLKmode)
X {
X if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY)
X stack_offset.constant
X += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1)
X / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT)
X - GET_MODE_SIZE (passed_mode));
X }
X else
X {
X tree sizetree = size_in_bytes (DECL_ARG_TYPE (parm));
X if (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT != PARM_BOUNDARY)
X {
X /* Round the size up to multiple of PARM_BOUNDARY bits. */
X tree s1 = convert_units (sizetree, BITS_PER_UNIT, PARM_BOUNDARY);
X tree s2 = convert_units (s1, PARM_BOUNDARY, BITS_PER_UNIT);
X /* Add it in. */
X ADD_PARM_SIZE (stack_offset, s2);
X SUB_PARM_SIZE (stack_offset, sizetree);
X extra = stack_offset.constant % UNITS_PER_WORD;
X stack_offset.constant -= extra;
X }
X }
X }
X
X stack_offset_rtx = ARGS_SIZE_RTX (stack_offset);
X
X /* Determine parm's home in the stack,
X in case it arrives in the stack or we should pretend it did. */
X stack_parm
X = gen_rtx (MEM, passed_mode,
X memory_address (passed_mode,
X gen_rtx (PLUS, Pmode,
X internal_arg_pointer,
X stack_offset_rtx)));
X
X /* If this is a memory ref that contains aggregate components,
X mark it as such for cse and loop optimize. */
X MEM_IN_STRUCT_P (stack_parm) = aggregate;
X
X /* Let machine desc say which reg (if any) the parm arrives in.
X 0 means it arrives on the stack. */
X entry_parm = 0;
X /* Variable-size args, and args following such, are never in regs. */
X if (TREE_CODE (TYPE_SIZE (TREE_TYPE (parm))) == INTEGER_CST
X || stack_offset.var != 0)
X {
X /* Set LAST_NAMED if this is last named arg before some
X anonymous args. We treat it as if it were anonymous too. */
X int last_named = (TREE_CHAIN (parm) == 0 && vararg);
X#ifdef FUNCTION_INCOMING_ARG
X entry_parm
X = FUNCTION_INCOMING_ARG (args_so_far, passed_mode,
X DECL_ARG_TYPE (parm), ! last_named);
X#else
X entry_parm
X = FUNCTION_ARG (args_so_far, passed_mode, DECL_ARG_TYPE (parm),
X ! last_named);
X#endif
X }
X
X /* If this parm was passed part in regs and part in memory,
X pretend it arrived entirely in memory
X by pushing the register-part onto the stack.
X
X In the special case of a DImode or DFmode that is split,
X we could put it together in a pseudoreg directly,
X but for now that's not worth bothering with. */
X
X if (entry_parm)
X {
X int nregs = 0;
X int i;
X#ifdef FUNCTION_ARG_PARTIAL_NREGS
X nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode,
X DECL_ARG_TYPE (parm), 1);
X#endif
X
X#if 0 /* Replaced by new calling convention
X which actually passes these args on the stack. */
X /* If this is the last named arg and anonymous args follow,
X likewise pretend this arg arrived on the stack
X so varargs can find the anonymous args following it. */
X if (TREE_CHAIN (parm) == 0 && vararg)
X {
X if (GET_MODE (entry_parm) == BLKmode)
X nregs = GET_MODE_SIZE (GET_MODE (entry_parm)) / UNITS_PER_WORD;
X else
X nregs = (int_size_in_bytes (DECL_ARG_TYPE (parm))
X / UNITS_PER_WORD);
X }
X#endif /* 0 */
X
X if (nregs > 0)
X {
X current_function_pretend_args_size
X = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
X / (PARM_BOUNDARY / BITS_PER_UNIT)
X * (PARM_BOUNDARY / BITS_PER_UNIT));
X
X i = nregs;
X while (--i >= 0)
X emit_move_insn (gen_rtx (MEM, SImode,
X plus_constant (XEXP (stack_parm, 0),
X i * GET_MODE_SIZE (SImode))),
X gen_rtx (REG, SImode, REGNO (entry_parm) + i));
X entry_parm = stack_parm;
X }
X }
X
X /* If we didn't decide this parm came in a register,
X by default it came on the stack. */
X if (entry_parm == 0)
X entry_parm = stack_parm;
X
X /* For a stack parm, record in DECL_OFFSET the arglist offset
X of the parm at the time it is passed (before conversion). */
X if (entry_parm == stack_parm)
X DECL_OFFSET (parm) = stack_offset.constant * BITS_PER_UNIT;
X
X /* If there is actually space on the stack for this parm,
X count it in stack_args_size; otherwise set stack_parm to 0
X to indicate there is no preallocated stack slot for the parm. */
X
X if (entry_parm == stack_parm
X#ifdef REG_PARM_STACK_SPACE
X /* On some machines, even if a parm value arrives in a register
X there is still an (uninitialized) stack slot allocated for it. */
X || 1
X#endif
X )
X {
X tree sizetree = size_in_bytes (DECL_ARG_TYPE (parm));
X if (where_pad != none
X && TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT != PARM_BOUNDARY)
X {
X /* Round the size up to multiple of PARM_BOUNDARY bits. */
X tree s1 = convert_units (sizetree, BITS_PER_UNIT, PARM_BOUNDARY);
X sizetree = convert_units (s1, PARM_BOUNDARY, BITS_PER_UNIT);
X }
X /* Add it in. */
X ADD_PARM_SIZE (stack_args_size, sizetree);
X }
X else
X /* No stack slot was pushed for this parm. */
X stack_parm = 0;
X
X /* Now adjust STACK_PARM to the mode and precise location
X where this parameter should live during execution,
X if we discover that it must live in the stack during execution.
X To make debuggers happier on big-endian machines, we store
X the value in the last bytes of the space available. */
X
X if (nominal_mode != BLKmode && nominal_mode != passed_mode
X && stack_parm != 0)
X {
X#ifdef BYTES_BIG_ENDIAN
X if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
X {
X stack_offset.constant
X += GET_MODE_SIZE (passed_mode)
X - GET_MODE_SIZE (nominal_mode);
X stack_offset_rtx = ARGS_SIZE_RTX (stack_offset);
X }
X#endif
X
X stack_parm
X = gen_rtx (MEM, nominal_mode,
X memory_address (nominal_mode,
X gen_rtx (PLUS, Pmode,
X arg_pointer_rtx,
X stack_offset_rtx)));
X
X /* If this is a memory ref that contains aggregate components,
X mark it as such for cse and loop optimize. */
X MEM_IN_STRUCT_P (stack_parm) = aggregate;
X }
X
X /* If there is rounding to do for a BLKmode parameter,
X add it in here, since STACK_OFFSET is not used for the
X rest of this iteration. */
X stack_offset.constant += extra;
X
X /* ENTRY_PARM is an RTX for the parameter as it arrives,
X in the mode in which it arrives.
X STACK_PARM is an RTX for a stack slot where the parameter can live
X during the function (in case we want to put it there).
X STACK_PARM is 0 if no stack slot was pushed for it.
X
X Now output code if necessary to convert ENTRY_PARM to
X the type in which this function declares it,
X and store that result in an appropriate place,
X which may be a pseudo reg, may be STACK_PARM,
X or may be a local stack slot if STACK_PARM is 0.
X
X Set DECL_RTL to that place. */
X
X if (nominal_mode == BLKmode)
X {
X /* If a BLKmode arrives in registers, copy it to a stack slot. */
X if (GET_CODE (entry_parm) == REG)
X {
X if (stack_parm == 0)
X stack_parm
X = assign_stack_local (GET_MODE (entry_parm),
X int_size_in_bytes (TREE_TYPE (parm)));
X
X move_block_from_reg (REGNO (entry_parm), stack_parm,
X ((int_size_in_bytes (TREE_TYPE (parm))
X + UNITS_PER_WORD - 1)
X / UNITS_PER_WORD));
X }
X else if (vararg)
X {
X /* If this function uses varargs, and `__builtin_saveregs'
X can clobber this stack location, then protect it. */
X rtx pseudo_parm;
X#ifdef FUNCTION_INCOMING_ARG
X pseudo_parm
X = FUNCTION_INCOMING_ARG (args_so_far, SImode,
X integer_type_node, 1);
X#else
X pseudo_parm
X = FUNCTION_ARG (args_so_far, SImode,
X integer_type_node, 1);
X#endif
X if (pseudo_parm && GET_CODE (pseudo_parm) == REG)
X {
X push_to_sequence (save_from_saveregs);
X move_block_to_reg (REGNO (pseudo_parm), stack_parm,
X int_size_in_bytes (TREE_TYPE (parm))
X / UNITS_PER_WORD);
X save_from_saveregs = get_insns ();
X end_sequence (0);
X }
X }
X DECL_RTL (parm) = stack_parm;
X }
X else if (! ((obey_regdecls && ! TREE_REGDECL (parm)
X && ! TREE_INLINE (fndecl))
X /* layout_decl may set this. */
X || TREE_ADDRESSABLE (parm)
X || TREE_VOLATILE (parm)
X /* If -ffloat-store specified, don't put explicit
X float variables into registers. */
X || (flag_float_store
X && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)))
X {

X /* Store the parm in a pseudoregister during the function. */
X register rtx parmreg = gen_reg_rtx (nominal_mode);
X
X REG_USERVAR_P (parmreg) = 1;
X DECL_RTL (parm) = parmreg;
X
X /* Copy the value into the register. */
X if (GET_MODE (parmreg) != GET_MODE (entry_parm))
X convert_move (parmreg, validize_mem (entry_parm), 0);
X else
X emit_move_insn (parmreg, validize_mem (entry_parm));
X
X /* In any case, record the parm's desired stack location
X in case we later discover it must live in the stack. */
X if (REGNO (parmreg) >= nparmregs)
X {
X rtx *new;
X nparmregs = REGNO (parmreg) + 5;
X new = (rtx *) oballoc (nparmregs * sizeof (rtx));
X bcopy (parm_reg_stack_loc, new, nparmregs * sizeof (rtx));
X parm_reg_stack_loc = new;
X }
X parm_reg_stack_loc[REGNO (parmreg)] = stack_parm;
X
X /* Mark the register as eliminable if we did no conversion
X and it was copied from memory at a fixed offset,
X and the arg pointer was not copied to a pseudo-reg.
X If the arg pointer is a pseudo reg, such memory-equivalences
X as we make here would screw up life analysis for it. */
X if (nominal_mode == passed_mode
X && GET_CODE (entry_parm) == MEM
X && stack_offset.var == 0
X && ! arg_pointer_copied)
X REG_NOTES (get_last_insn ())
X = gen_rtx (EXPR_LIST, REG_EQUIV,
X entry_parm, REG_NOTES (get_last_insn ()));
X
X /* For pointer data type, suggest pointer register. */
X if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
X mark_reg_pointer (parmreg);
X }
X else
X {
X /* Value must be stored in the stack slot STACK_PARM
X during function execution. */
X
X if (passed_mode != nominal_mode)
X /* Conversion is required. */
X entry_parm = convert_to_mode (nominal_mode, entry_parm, 0);
X
X if (entry_parm != stack_parm)
X {
X if (stack_parm == 0)
X stack_parm = assign_stack_local (GET_MODE (entry_parm),
X GET_MODE_SIZE (GET_MODE (entry_parm)));
X emit_move_insn (validize_mem (stack_parm),
X validize_mem (entry_parm));
X }
X
X DECL_RTL (parm) = stack_parm;
X frame_pointer_needed = 1;
X }
X
X if (TREE_VOLATILE (parm))
X MEM_VOLATILE_P (DECL_RTL (parm)) = 1;
X if (TREE_READONLY (parm))
X RTX_UNCHANGING_P (DECL_RTL (parm)) = 1;
X
X /* Update info on where next arg arrives in registers. */
X
X FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, DECL_ARG_TYPE (parm), 1);
X }
X
X max_parm_reg = max_reg_num ();
X last_parm_insn = get_last_insn ();
X
X current_function_args_size = stack_args_size.constant;
X}
X
X/* Allocation of space for returned structure values.
X During the rtl generation pass, `get_structure_value_addr'
X is called from time to time to request the address of a block in our
X stack frame in which called functions will store the structures
X they are returning. The same space is used for all of these blocks.
X
X We allocate these blocks like stack locals. We keep reusing
X the same block until a bigger one is needed. */
X
X/* Length in bytes of largest structure value returned by
X any function called so far in this function. */
Xstatic int max_structure_value_size;
X
X/* An rtx for the addr we are currently using for structure values.
X This is typically (PLUS (REG:SI stackptr) (CONST_INT...)). */
Xstatic rtx structure_value;
X
Xrtx
Xget_structure_value_addr (sizex)
X rtx sizex;
X{
X register int size;
X if (GET_CODE (sizex) != CONST_INT)
X abort ();
X size = INTVAL (sizex);
X
X /* Round up to a multiple of the main allocation unit. */
X size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
X / (BIGGEST_ALIGNMENT / BITS_PER_UNIT))
X * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
X
X /* If this size is bigger than space we know to use,
X get a bigger piece of space. */
X if (size > max_structure_value_size)
X {
X max_structure_value_size = size;
X structure_value = assign_stack_local (BLKmode, size);
X if (GET_CODE (structure_value) == MEM)
X structure_value = XEXP (structure_value, 0);
X }
X
X return structure_value;
X}
X
X/* Walk the tree of LET_STMTs describing the binding levels within a function
X and warn about uninitialized variables.
X This is done after calling flow_analysis and before global_alloc
X clobbers the pseudo-regs to hard regs. */
X
Xvoid
Xuninitialized_vars_warning (block)
X tree block;
X{
X register tree decl, sub;
X for (decl = STMT_VARS (block); decl; decl = TREE_CHAIN (decl))
X {
X if (TREE_CODE (decl) == VAR_DECL
X /* These warnings are unreliable for and aggregates
X because assigning the fields one by one can fail to convince
X flow.c that the entire aggregate was initialized.
X Unions are troublesome because members may be shorter. */
X && TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE
X && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE
X && TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE
X && DECL_RTL (decl) != 0
X && GET_CODE (DECL_RTL (decl)) == REG
X && regno_uninitialized (REGNO (DECL_RTL (decl))))
X warning_with_decl (decl,
X "`%s' may be used uninitialized in this function");
X if (TREE_CODE (decl) == VAR_DECL
X && DECL_RTL (decl) != 0
X && GET_CODE (DECL_RTL (decl)) == REG
X && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
X warning_with_decl (decl,
X "variable `%s' may be clobbered by `longjmp'");
X }
X for (sub = STMT_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
X uninitialized_vars_warning (sub);
X}
X
X/* If this function call setjmp, put all vars into the stack
X unless they were declared `register'. */
X
Xvoid
Xsetjmp_protect (block)
X tree block;
X{
X register tree decl, sub;
X for (decl = STMT_VARS (block); decl; decl = TREE_CHAIN (decl))
X if ((TREE_CODE (decl) == VAR_DECL
X || TREE_CODE (decl) == PARM_DECL)
X && DECL_RTL (decl) != 0
X && GET_CODE (DECL_RTL (decl)) == REG
X && ! TREE_REGDECL (decl))
X put_var_into_stack (decl);
X for (sub = STMT_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
X setjmp_protect (sub);
X}
X
X/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
X and initialize static variables for generating RTL for the statements
X of the function. */
X
Xvoid
Xinit_function_start (subr)
X tree subr;
X{
X this_function = subr;
X cse_not_expected = ! optimize;
X
X /* We have not yet found a reason why a frame pointer cannot
X be omitted for this function in particular, but maybe we know
X a priori that it is required.
X `flag_omit_frame_pointer' has its main effect here. */
X frame_pointer_needed = FRAME_POINTER_REQUIRED || ! flag_omit_frame_pointer;
X
X /* Caller save not needed yet. */
X caller_save_needed = 0;
X
X /* No gotos have been expanded yet. */
X goto_fixup_chain = 0;
X
X /* No stack slots have been made yet. */
X stack_slot_list = 0;
X
X /* No invalid stack slots have been made yet. */
X invalid_stack_slot = 0;
X
X /* No parm regs have been allocated.
X (This is important for output_inline_function.) */
X max_parm_reg = FIRST_PSEUDO_REGISTER;
X
X /* Initialize the RTL mechanism. */
X init_emit (write_symbols);
X
X /* Initialize the queue of pending postincrement and postdecrements,
X and some other info in expr.c. */
X init_expr ();
X
X init_const_rtx_hash_table ();
X
X /* Decide whether function should try to pop its args on return. */
X
X current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (subr));
X
X current_function_name = (char *)lang_printable_name (subr);
X
X /* Nonzero if this is a nested function that uses a static chain. */
X
X current_function_needs_context
X = (DECL_CONTEXT (current_function_decl) != 0
X && TREE_CODE (DECL_CONTEXT (current_function_decl)) == LET_STMT);
X
X /* Set if a call to setjmp is seen. */
X
X current_function_calls_setjmp = 0;
X current_function_calls_alloca = 0;
X
X current_function_returns_pcc_struct = 0;
X current_function_returns_struct = 0;
X
X /* No space assigned yet for structure values. */
X max_structure_value_size = 0;
X structure_value = 0;
X
X /* We are not currently within any block, conditional, loop or case.
X @@ No longer true. We are within the block for the parms. */
X block_stack = 0;
X loop_stack = 0;
X case_stack = 0;
X cond_stack = 0;
X nesting_stack = 0;
X nesting_depth = 0;
X
X block_start_count = 0;
X
X /* We have not yet needed to make a label to jump to for tail-recursion. */
X tail_recursion_label = 0;
X
X /* No stack slots allocated yet. */
X frame_offset = STARTING_FRAME_OFFSET;
X
X /* No SAVE_EXPRs in this function yet. */
X save_expr_regs = 0;
X
X /* No parameters to protect from `__builtin_saveregs' yet. */
X save_from_saveregs = 0;
X
X /* No RTL_EXPRs in this function yet. */
X rtl_expr_chain = 0;
X
X /* Within function body, compute a type's size as soon it is laid out. */
X immediate_size_expand++;
X
X init_pending_stack_adjust ();
X inhibit_defer_pop = 0;
X current_function_pretend_args_size = 0;
X
X /* Prevent ever trying to delete the first instruction of a function.
X Also tell final how to output a linenum before the function prologue. */
X emit_line_note (DECL_SOURCE_FILE (subr), DECL_SOURCE_LINE (subr));
X /* Make sure first insn is a note even if we don't want linenums.
X This makes sure the first insn will never be deleted.
X Also, final expects a note to appear there. */
X emit_note (0, NOTE_INSN_DELETED);
X /* Indicate the beginning of the function body,
X as opposed to parm setup. */
X emit_note (0, NOTE_INSN_FUNCTION_BEG);
X
X /* Set flags used by final.c. */
X if (aggregate_value_p (DECL_RESULT (subr)))
X {
X#ifdef PCC_STATIC_STRUCT_RETURN
X if (flag_pcc_struct_return)
X current_function_returns_pcc_struct = 1;
X else
X#endif
X current_function_returns_struct = 1;
X }
X}
X
X/* Start the RTL for a new function, and set variables used for
X emitting RTL.
X SUBR is the FUNCTION_DECL node.
X PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
X the function's parameters, which must be run at any return statement. */
X
Xvoid
Xexpand_function_start (subr, parms_have_cleanups)
X tree subr;
X int parms_have_cleanups;
X{
X register int i;
X tree tem;
X
X /* Make sure volatile mem refs aren't considered
X valid operands of arithmetic insns. */
X init_recog ();
X
X /* If the parameters of this function need cleaning up, get a label
X for the beginning of the code which executes those cleanups. This must
X be done before doing anything with return_label. */
X if (parms_have_cleanups)
X cleanup_label = gen_label_rtx ();
X else
X cleanup_label = 0;
X
X /* Make the label for return statements to jump to, if this machine
X does not have a one-instruction return and uses an epilogue,
X or if it returns a structure, or if it has parm cleanups. */
X#ifdef HAVE_return
X if (cleanup_label == 0 && HAVE_return
X && ! current_function_returns_pcc_struct
X && ! (current_function_returns_struct && ! optimize))
X return_label = 0;
X else
X return_label = gen_label_rtx ();
X#else
X return_label = gen_label_rtx ();
X#endif
X
X /* Initialize rtx used to return the value. */
X /* Do this before assign_parms so that we copy the struct value address
X before any library calls that assign parms might generate. */
X
X /* Decide whether to return the value in memory or in a register. */
X if (aggregate_value_p (DECL_RESULT (subr)))
X {
X /* Returning something that won't go in a register. */
X register rtx value_address;
X
X#ifdef PCC_STATIC_STRUCT_RETURN
X if (flag_pcc_struct_return)
X {
X int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr)));
X value_address = assemble_static_space (size);
X current_function_returns_pcc_struct = 1;
X }
X else
X#endif
X {
X /* Expect to be passed the address of a place to store the value. */
X value_address = gen_reg_rtx (Pmode);
X emit_move_insn (value_address, struct_value_incoming_rtx);
X current_function_returns_struct = 1;
X }
X DECL_RTL (DECL_RESULT (subr))
X = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)),
X value_address);
X }
X else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
X /* If return mode is void, this decl rtl should not be used. */
X DECL_RTL (DECL_RESULT (subr)) = 0;
X else if (parms_have_cleanups)
X {
X /* If function will end with cleanup code for parms,
X compute the return values into a pseudo reg,
X which we will copy into the true return register
X after the cleanups are done. */
X DECL_RTL (DECL_RESULT (subr))
X = gen_reg_rtx (DECL_MODE (DECL_RESULT (subr)));
X TREE_REGDECL (DECL_RESULT (subr)) = 1;
X }
X else
X /* Scalar, returned in a register. */
X {
X#ifdef FUNCTION_OUTGOING_VALUE
X DECL_RTL (DECL_RESULT (subr))
X = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
X#else
X DECL_RTL (DECL_RESULT (subr))
X = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
X#endif
X
X current_function_returns_pointer
X = (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == POINTER_TYPE);
X
X /* Mark this reg as the function's return value. */
X if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG)
X {
X REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1;
X TREE_REGDECL (DECL_RESULT (subr)) = 1;
X }
X }
X
X /* Initialize rtx for parameters and local variables.
X In some cases this requires emitting insns. */
X
X assign_parms (subr);
X
X#if 0
X /* Not yet defined in GCC 1.37.1. */
X
X /* Tell final that the parameters are in the final places
X (either on the stack or in registers). */
X emit_note ((char *) 0, NOTE_INSN_PARMS_HOMED);
X#endif
X
X /* If doing stupid allocation, mark parms as born here. */
X
X if (GET_CODE (get_last_insn ()) != NOTE)
X emit_note (0, NOTE_INSN_DELETED);
X parm_birth_insn = get_last_insn ();
X
X if (obey_regdecls)
X {
X for (i = FIRST_PSEUDO_REGISTER; i < max_parm_reg; i++)
X use_variable (regno_reg_rtx[i]);
X }
X
X /* After the parm initializations is where the tail-recursion label
X should go, if we end up needing one. */
X tail_recursion_reentry = get_last_insn ();
X
X /* Evaluate now the sizes of any types declared among the arguments. */
X for (tem = get_pending_sizes (); tem; tem = TREE_CHAIN (tem))
X expand_expr (TREE_VALUE (tem), 0, VOIDmode, 0);
X
X /* Make sure there is a line number after the function entry setup code.
X There normally is one anyway, from the following statement,
X but there could fail to be one if there is no newline here. */
X force_next_line_note ();
X}
X
X/* Generate RTL for the end of the current function.
X FILENAME and LINE are the current position in the source file. */
X
X/* ??? Nobody seems to emit the cleanup_label and the cleanups themselves.
X
X !!! Not true. finish_function does this is cplus-decl.c. */
X
Xvoid
Xexpand_function_end (filename, line)
X char *filename;
X int line;
X{
X register int i;
X tree decl;
X extern rtx sequence_stack;
X
X#if 0 /* I think unused parms are legitimate enough. */
X /* Warn about unused parms. */
X if (warn_unused)
X for (decl = DECL_ARGUMENTS (current_function_decl);
X decl; decl = TREE_CHAIN (decl))
X if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL)
X warning_with_decl (decl, "unused parameter `%s'");
X#endif
X
X /* End any sequences that failed to be closed due to syntax errors. */
X while (sequence_stack)
X end_sequence (0);
X
X /* Outside function body, can't compute type's actual size
X until next function's body starts. */
X immediate_size_expand--;
X
X /* If doing stupid register allocation,
X mark register parms as dying here. */
X
X if (obey_regdecls)
X {
X rtx tem;
X for (i = FIRST_PSEUDO_REGISTER; i < max_parm_reg; i++)
X use_variable (regno_reg_rtx[i]);
X
X /* Likewise for the regs of all the SAVE_EXPRs in the function. */
X
X for (tem = save_expr_regs; tem; tem = XEXP (tem, 1))
X {
X use_variable (XEXP (tem, 0));
X use_variable_after (XEXP (tem, 0), parm_birth_insn);
X }
X }
X
X clear_pending_stack_adjust ();
X do_pending_stack_adjust ();
X
X /* Mark the end of the function body.
X If control reaches this insn, the function can drop through
X without returning a value. */
X emit_note (0, NOTE_INSN_FUNCTION_END);
X
X /* Output a linenumber for the end of the function.
X SDB depends on this. */
X emit_line_note_force (filename, line);
X
X /* Output the label for the actual return from the function,
X if one is expected. This happens either because a function epilogue
X is used instead of a return instruction, or because a return was done
X with a goto in order to run local cleanups, or because of pcc-style
X structure returning. */
X
X if (return_label)
X emit_label (return_label);
X
X /* If we had calls to alloca, and this machine needs
X an accurate stack pointer to exit the function,
X insert some code to save and restore the stack pointer. */
X#ifdef EXIT_IGNORE_STACK
X if (! EXIT_IGNORE_STACK)
X#endif
X if (current_function_calls_alloca)
X {
X rtx tem = gen_reg_rtx (Pmode);
X emit_insn_after (gen_rtx (SET, VOIDmode, tem, stack_pointer_rtx),
X parm_birth_insn);
X emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, tem));
X }
X
X /* If scalar return value was computed in a pseudo-reg,
X copy that to the hard return register. */
X if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0
X && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG
X && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl)))
X >= FIRST_PSEUDO_REGISTER))
X {
X rtx real_decl_result;
X
X#ifdef FUNCTION_OUTGOING_VALUE
X real_decl_result
X = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
X current_function_decl);
X#else
X real_decl_result
X = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
X current_function_decl);
X#endif
X REG_FUNCTION_VALUE_P (real_decl_result) = 1;
X emit_move_insn (real_decl_result,
X DECL_RTL (DECL_RESULT (current_function_decl)));
X emit_insn (gen_rtx (USE, VOIDmode, real_decl_result));
X }
X
X /* If returning a structure, arrange to return the address of the value
X in a place where debuggers expect to find it. */
X /* If returning a structure PCC style,
X the caller also depends on this value.
X And current_function_returns_pcc_struct is not necessarily set. */
X if (current_function_returns_struct
X || current_function_returns_pcc_struct)
X {
X rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
X tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
X#ifdef FUNCTION_OUTGOING_VALUE
X rtx outgoing
X = FUNCTION_OUTGOING_VALUE (build_pointer_type (type),
X current_function_decl);
X#else
X rtx outgoing
X = hard_function_value (build_pointer_type (type),
X current_function_decl);
X#endif
X
X#if 1
X /* If this works, leave it in. */
X /* This is not really the function value,
X and it confuses integrate.c around line 1211. */
X REG_FUNCTION_VALUE_P (outgoing) = 1;
X#endif
X
X emit_move_insn (outgoing, value_address);
X use_variable (outgoing);
X }
X
X /* Output a return insn if we are using one.
X Otherwise, let the rtl chain end here, to drop through
X into the epilogue. */
X
X#ifdef HAVE_return
X if (HAVE_return)
X emit_jump_insn (gen_return ());
X#endif
X
X /* Fix up any gotos that jumped out to the outermost
X binding level of the function.
X Must follow emitting RETURN_LABEL. */
X
X /* If you have any cleanups to do at this point,
X and they need to create temporary variables,
X then you will lose. */
X fixup_gotos (0, 0, 0, get_insns (), 0);
X}
X
Xinit_stmt ()
X{
X obstack_init (&stmt_obstack);
X}
!EOF
echo "Extracting collect2.c..."
sed 's/^X//' >collect2.c << '!EOF'
X/* Build tables of static constructors and destructors and run ld. */
X
X#include
X
X#ifdef convex
X
X#define TEXT_SECTION_ASM_OP ".text"
X#define DATA_SECTION_ASM_OP ".data"
X
X#define ASM_GLOBALIZE_LABEL(FILE, LABEL) \
X fprintf (FILE, ".globl _%s\n", LABEL)
X
X#define ASM_OUTPUT_LABEL(FILE, LABEL) \
X fprintf (FILE, "_%s:", LABEL)
X
X#define ASM_OUTPUT_LABELREF(FILE, LABEL) \
X fprintf (FILE, "\tds.w _%s\n", LABEL)
X
X#define ASM_OUTPUT_INT(FILE, INT) \
X fprintf (FILE, "\tds.w %d\n", INT)
X
X#endif
X
X#ifdef MASSCOMP
X
X#define TEXT_SECTION_ASM_OP ".text"
X#define DATA_SECTION_ASM_OP ".data"
X
X#define ASM_GLOBALIZE_LABEL(FILE, LABEL) \
X fprintf (FILE, ".globl _%s\n", LABEL)
X
X#define ASM_OUTPUT_LABEL(FILE, LABEL) \
X fprintf (FILE, "_%s:\n", LABEL)
X
X#define ASM_OUTPUT_LABELREF(FILE, LABEL) \
X fprintf (FILE, "\t.long _%s\n", LABEL)
X
X#define ASM_OUTPUT_INT(FILE, INT) \
X fprintf (FILE, "\t.long %d\n", INT)
X
X#endif
X
X#if defined (__GNUC__) || defined (sparc)
X#define alloca __builtin_alloca
X#endif
X
Xextern char *mktemp (char *template);
X
X/* Linked lists of constructor and destructor names. */
X
Xstruct id
X{
X char *name;
X struct id *next;
X};
X
X/* Main program. */
X
Xmain (int argc, char *argv[])
X{
X static char codexxx[] = "/tmp/codeXXXXXX";
X static char asxxx[] = "/tmp/asXXXXXX";
X char *codefile, *hookfile, hooksfile[40], hookofile[40];
X char *outfile = "a.out";
X char *arg, ldargs[1024], cmd[1024];
X FILE *inf, *outf;
X
X /* Make temp file names. */
X
X codefile = mktemp (codexxx);
X hookfile = mktemp (asxxx);
X sprintf (hooksfile, "%s.s", hookfile);
X sprintf (hookofile, "%s.o", hookfile);
X
X /* Parse arguments. Remove output file spec, pass the rest to ld. */
X
X ldargs[0] = '\0';
X while (arg = *++argv)
X {
X if (! strcmp (arg, "-o"))
X outfile = *++argv;
X else
X {
X#ifdef masscomp
X if ( *arg == '-' && *(arg+1) == 'L' )
X {
X strcat (ldargs, " -L ");
X strcat (ldargs, arg+2);
X }
X else
X {
X strcat (ldargs, " ");
X strcat (ldargs, arg);
X }
X#else
X strcat (ldargs, " ");
X strcat (ldargs, arg);
X#endif
X }
X }
X
X /* Load the program, searching all libraries.
X Use -r to save the output as a relocatable file.
X Examine the namelist with nm and search it for static constructors
X and destructors to call.
X Write the constructor and destructor tables to a .s file. */
X
X#ifdef masscomp
X/*
X sprintf (cmd, "ld -r -o %s %s", codefile, ldargs);
X if (system(cmd))
X fatal( "##F-ld: Cannot perform link." );
X sprintf (cmd, "nm -p %s", codefile);
X*/
X sprintf (cmd, "ld -r -o %s %s && nm -p %s", codefile, ldargs, codefile);
X#else
X sprintf (cmd, "ld -r -o %s %s && nm -p %s", codefile, ldargs, codefile);
X#endif
X
X if (! (inf = popen (cmd, "r")))
X fatal_perror ("Can't open pipe to ld\n");
X if (! (outf = fopen (hooksfile, "w")))
X fatal_perror ("Can't write %s\n", hooksfile);
X
X write_hooks (inf, outf);
X
X if (pclose (inf) || fclose (outf))
X fatal ("load failed");
X
X /* Assemble the constructor and destructor tables.
X Link the tables in with the rest of the program. */
X
X sprintf (cmd, "as -o %s %s && ld -o %s %s %s && rm %s %s %s",
X hookofile, hooksfile,
X outfile, codefile, hookofile,
X codefile, hooksfile, hookofile);
X exit (system (cmd));
X}
X
X/* Scan the name list of the loaded program for the symbols g++ uses
X for static constructors and destructors. Write their addresses
X into tables which __main and exit will call.
X
X The constructor table __CTOR_LIST__ is an integer count followed by
X that many pointers to constructor functions. The destructor table
X __DTOR_LIST__ is the same thing, followed by a zero word. */
X
Xwrite_hooks (FILE *inf, FILE *outf)
X{
X char *p, buf[1024];
X struct id *newid;
X
X struct id *constructors = 0;
X struct id *destructors = 0;
X
X while (! feof (inf)) {
X
X /* Read a line of nm output and strip the trailing newline. */
X
X fgets (buf, sizeof buf, inf);
X p = buf + strlen (buf) - 1;
X if (*p == '\n')
X *p = '\0';
X
X#ifdef masscomp
X /* If loader spits out an error/warning message then
X ** lets display it.
X */
X if (buf[0] == '#' && buf[1] == '#')
X {
X fprintf( stderr, "%s\n", buf );
X buf[0] = '\0';
X }
X#endif
X
X /* If it contains a constructor or destructor name, add the name
X to the appropriate list. */
X
X for (p = buf; *p; p++)
X {
X while (*p && *p != '_')
X p++;
X if (! strncmp (p, "_GLOBAL_$I$", 11))
X {
X newid = alloca (sizeof *newid);
X newid->name = alloca (strlen (p) + 1);
X strcpy (newid->name, p);
X newid->next = constructors;
X constructors = newid;
X break;
X }
X else if (! strncmp (p, "_GLOBAL_$D$", 11))
X {
X newid = alloca (sizeof *newid);
X newid->name = alloca (strlen (p) + 1);
X strcpy (newid->name, p);
X newid->next = destructors;
X destructors = newid;
X break;
X }
X }
X }
X
X /* Write the tables. */
X
X fprintf (outf, "%s\n", TEXT_SECTION_ASM_OP);
X ASM_GLOBALIZE_LABEL (outf, "__CTOR_LIST__");
X ASM_OUTPUT_LABEL (outf, "__CTOR_LIST__");
X ASM_OUTPUT_INT (outf, count_list (constructors));
X write_list (outf, constructors);
X
X fprintf (outf, "%s\n", DATA_SECTION_ASM_OP);
X ASM_GLOBALIZE_LABEL (outf, "__DTOR_LIST__");
X ASM_OUTPUT_LABEL (outf, "__DTOR_LIST__");
X ASM_OUTPUT_INT (outf, count_list (destructors));
X write_list (outf, destructors);
X ASM_OUTPUT_INT (outf, 0);
X}
X
X/* Length of list LIST. */
X
Xcount_list (struct id *list)
X{
X int count = 0;
X while (list)
X {
X count++;
X list = list->next;
X }
X return count;
X}
X
X/* Write the names on list LIST, in reverse order. */
X
Xwrite_list (FILE *outf, struct id *list)
X{
X if (! list)
X return;
X write_list (outf, list->next);
X ASM_OUTPUT_LABELREF (outf, list->name);
X}
X
X/* Die when sys call fails. */
X
Xfatal_perror (string, arg)
X{
X char buf[80];
X sprintf (buf, string, arg);
X perror (buf);
X exit (1);
X}
X
X/* Just die. */
X
Xfatal (string)
X{
X fprintf (stderr, "\n");
X fprintf (stderr, string);
X fprintf (stderr, "\n");
X exit (1);
X}
!EOF
echo "Extracting cplus-search.c..."
sed 's/^X//' >cplus-search.c << '!EOF'
X/* Breadth-first and depth-first routines for
X searching multiple-inheritance lattice for GNU C++.
X Copyright (C) 1987 Free Software Foundation, Inc.
X Contributed by Michael Tiemann ([email protected])
X
XThis file is part of GNU CC.
X
XGNU CC is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU CC is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU CC; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X/* High-level class interface. */
X
X#include "config.h"
X#include "tree.h"
X#include "cplus-tree.h"
X#include "obstack.h"
X#include "flags.h"
X#include "assert.h"
X#include
X
X/* For expand_asm_operands. */
Xextern char *input_filename;
Xextern int lineno;
X
X#define NULL 0
X
X#define obstack_chunk_alloc xmalloc
X#define obstack_chunk_free free
X
Xextern int xmalloc ();
Xextern void free ();
X
Xvoid init_search ();
Xextern struct obstack *current_obstack;
X
X#include "stack.h"
X
X/* Obstack used for remembering decision points of breadth-first. */
Xstatic struct obstack search_obstack;
X
X/* Obstack used to bridge from one function context to another. */
Xstatic struct obstack bridge_obstack;
X
X/* Methods for pushing and popping objects to and from obstacks. */
X
Xstruct stack_level *
Xpush_stack_level (obstack, tp, size)
X struct obstack *obstack;
X void *tp;
X int size;
X{
X struct stack_level *stack;
X stack = (struct stack_level *) obstack_next_free (obstack);
X obstack_grow (obstack, tp, size);
X obstack_finish (obstack);
X stack->obstack = obstack;
X stack->first = (tree *) obstack_base (obstack);
X stack->limit = obstack_room (obstack) / sizeof (tree *);
X return stack;

X}
X
Xstruct stack_level *
Xpop_stack_level (stack)
X struct stack_level *stack;
X{
X struct stack_level *tem = stack;
X struct obstack *obstack = tem->obstack;
X stack = tem->prev;
X obstack_free (obstack, tem);
X return stack;
X}
X
X#define search_level stack_level
Xstatic struct search_level *search_stack;
X
Xstatic tree lookup_field_1 ();
Xstatic int lookup_fnfields_1 ();
X
X/* Allocate a level of searching. */
Xstatic struct search_level *
Xpush_search_level (stack, obstack)
X struct stack_level *stack;
X struct obstack *obstack;
X{
X struct search_level tem;
X tem.prev = stack;
X
X return push_stack_level (obstack, &tem, sizeof (tem));
X}
X
X/* Discard a level of search allocation. */
X#define pop_search_level pop_stack_level
X
X/* Search memoization. */
Xstruct type_level
X{
X struct stack_level base;
X
X /* First object allocated in obstack of entries. */
X char *entries;
X
X /* Number of types memoized in this context. */
X int len;
X
X /* Type being memoized; save this if we are saving
X memoized contexts. */
X tree type;
X};
X
X/* Obstack used for memoizing member and member function lookup. */
X
Xstatic struct obstack type_obstack, type_obstack_entries;
Xstatic struct type_level *type_stack;
Xstatic tree _vptr_name;
X
X/* Make things that look like tree nodes, but allocate them
X on type_obstack_entries. */
Xstatic int my_tree_node_counter;
Xstatic tree my_tree_cons (), my_build_string ();
X
Xextern int flag_memoize_lookups, flag_save_memoized_contexts;
X
X/* Variables for gathering statistics. */
Xstatic int my_memoized_entry_counter;
Xstatic int memoized_fast_finds[2], memoized_adds[2], memoized_fast_rejects[2];
Xstatic int memoized_fields_searched[2];
Xstatic int n_fields_searched;
Xstatic int n_calls_lookup_field, n_calls_lookup_field_1;
Xstatic int n_calls_lookup_fnfields, n_calls_lookup_fnfields_1;
Xstatic int n_calls_get_base_type;
Xstatic int n_outer_fields_searched;
Xstatic int n_contexts_saved;
X
X/* Local variables to help save memoization contexts. */
Xstatic tree prev_type_memoized;
Xstatic struct type_level *prev_type_stack;
X
X/* Allocate a level of type memoziation context. */
Xstatic struct type_level *
Xpush_type_level (stack, obstack)
X struct stack_level *stack;
X struct obstack *obstack;
X{
X struct type_level tem;
X
X tem.base.prev = stack;
X
X obstack_finish (&type_obstack_entries);
X tem.entries = (char *) obstack_base (&type_obstack_entries);
X tem.len = 0;
X tem.type = NULL_TREE;
X
X return (struct type_level *)push_stack_level (obstack, &tem, sizeof (tem));
X}
X
X/* Discard a level of type memoziation context. */
X
Xstatic struct type_level *
Xpop_type_level (stack)
X struct type_level *stack;
X{
X obstack_free (&type_obstack_entries, stack->entries);
X return (struct type_level *)pop_stack_level (stack);
X}
X
X/* Make something that looks like a TREE_LIST, but
X do it on the type_obstack_entries obstack. */
Xstatic tree
Xmy_tree_cons (purpose, value, chain)
X tree purpose, value, chain;
X{
X tree p = (tree)obstack_alloc (&type_obstack_entries, sizeof (struct tree_list));
X TREE_UID (p) = ++my_tree_node_counter;
X TREE_TYPE (p) = 0;
X ((int *)p)[3] = 0;
X TREE_SET_CODE (p, TREE_LIST);
X TREE_PURPOSE (p) = purpose;
X TREE_VALUE (p) = value;
X TREE_CHAIN (p) = chain;
X return p;
X}
X
Xstatic tree
Xmy_build_string (str)
X char *str;
X{
X tree p = (tree)obstack_alloc (&type_obstack_entries, sizeof (struct tree_string));
X TREE_UID (p) = ++my_tree_node_counter;
X TREE_TYPE (p) = 0;
X ((int *)p)[3] = 0;
X TREE_SET_CODE (p, STRING_CST);
X TREE_STRING_POINTER (p) = str;
X TREE_STRING_LENGTH (p) = strlen (str);
X return p;
X}
X
Xstatic tree
Xmy_copy_node (node)
X tree node;
X{
X struct obstack *ambient_obstack = current_obstack;
X tree t;
X
X current_obstack = &type_obstack_entries;
X
X t = copy_node (node);
X TREE_UID (t) = ++my_tree_node_counter;
X
X current_obstack = ambient_obstack;
X return t;
X}
X
X/* Memoizing machinery to make searches for multiple inheritance
X reasonably efficient. */
X#define MEMOIZE_HASHSIZE 8
Xtypedef struct memoized_entry
X{
X struct memoized_entry *chain;
X int uid;
X tree data_members[MEMOIZE_HASHSIZE];
X tree function_members[MEMOIZE_HASHSIZE];
X} *ME;
X
X#define MEMOIZED_CHAIN(ENTRY) (((ME)ENTRY)->chain)
X#define MEMOIZED_UID(ENTRY) (((ME)ENTRY)->uid)
X#define MEMOIZED_FIELDS(ENTRY,INDEX) (((ME)ENTRY)->data_members[INDEX])
X#define MEMOIZED_FNFIELDS(ENTRY,INDEX) (((ME)ENTRY)->function_members[INDEX])
X#define MEMOIZED_HASH_FN(NODE) (TREE_UID (NODE)&(MEMOIZE_HASHSIZE - 1))
X
Xstatic struct memoized_entry *
Xmy_new_memoized_entry (chain)
X struct memoized_entry *chain;
X{
X struct memoized_entry *p =
X (struct memoized_entry *)obstack_alloc (&type_obstack_entries,
X sizeof (struct memoized_entry));
X bzero (p, sizeof (struct memoized_entry));
X MEMOIZED_CHAIN (p) = chain;
X MEMOIZED_UID (p) = ++my_memoized_entry_counter;
X return p;
X}
X
X/* When a new function or class context is entered, we build
X a table of types which have been searched for members.
X The table is an array (obstack) of types. When a type is
X entered into the obstack, its CLASSTYPE_MTABLE_ENTRY
X field is set to point to a new record, of type struct memoized_entry.
X
X A non-NULL TREE_TYPE of the entry contains a visibility error message.
X
X The slots for the data members are arrays of tree nodes.
X These tree nodes are lists, with the TREE_PURPOSE
X of this list the known member name, and the TREE_VALUE
X as the FIELD_DECL for the member.
X
X For member functions, the TREE_PURPOSE is again the
X name of the member functions for that class,
X and the TREE_VALUE of the list is a pairs
X whose TREE_PURPOSE is a member functions of this name,
X and whose TREE_VALUE is a list of known argument lists this
X member function has been called with. The TREE_TYPE of the pair,
X if non-NULL, is an error message to print. */
X
X/* Tell search machinery that we are entering a new context, and
X to update tables appropriately.
X
X TYPE is the type of the context we are entering, which can
X be NULL_TREE if we are not in a class's scope.
X
X USE_OLD, if nonzero tries to use previous context. */
Xvoid
Xpush_memoized_context (type, use_old)
X tree type;
X int use_old;
X{
X int len;
X tree *tem;
X
X if (prev_type_stack)
X {
X if (use_old && prev_type_memoized == type)
X {
X#ifdef GATHER_STATISTICS
X n_contexts_saved++;
X#endif
X type_stack = prev_type_stack;
X prev_type_stack = 0;
X
X tem = &type_stack->base.first[0];
X len = type_stack->len;
X while (len--)
X CLASSTYPE_MTABLE_ENTRY (tem[len*2]) = tem[len*2+1];
X return;
X }
X /* Otherwise, need to pop old stack here. */
X type_stack = pop_type_level (prev_type_stack);
X prev_type_memoized = 0;
X prev_type_stack = 0;
X }
X
X type_stack = push_type_level (type_stack, &type_obstack);
X type_stack->type = type;
X}
X
X/* Tell search machinery that we have left a context.
X We do not currently save these contexts for later use.
X If we wanted to, we could not use pop_search_level, since
X poping that level allows the data we have collected to
X be clobbered; a stack of obstacks would be needed. */
Xpop_memoized_context (use_old)
X int use_old;
X{
X int len;
X tree *tem = &type_stack->base.first[0];
X
X if (! flag_save_memoized_contexts)
X use_old = 0;
X else if (use_old)
X {
X len = type_stack->len;
X while (len--)
X tem[len*2+1] = (tree)CLASSTYPE_MTABLE_ENTRY (tem[len*2]);
X
X prev_type_stack = type_stack;
X prev_type_memoized = type_stack->type;
X }
X
X if (flag_memoize_lookups)
X {
X len = type_stack->len;
X while (len--)
X CLASSTYPE_MTABLE_ENTRY (tem[len*2])
X = MEMOIZED_CHAIN (CLASSTYPE_MTABLE_ENTRY (tem[len*2]));
X }
X if (! use_old)
X type_stack = pop_type_level (type_stack);
X else
X type_stack = (struct type_level *)type_stack->base.prev;
X}
X
X/* Some simple list processing predicates. */
X
X/* Check whether TYPE is immediately derived from PARENT.
X Return actual base information if so. Otherwise, return 0. */
Xtree
Xget_base_type_1 (parent, type)
X register tree parent, type;
X{
X register int i;
X
X parent = TYPE_MAIN_VARIANT (parent);
X type = TYPE_MAIN_VARIANT (type);
X
X for (i = 1; i <= CLASSTYPE_N_BASECLASSES (type); i++)
X if (TYPE_MAIN_VARIANT (CLASSTYPE_BASECLASS (type, i)) == parent)
X return CLASSTYPE_BASECLASS (type, i);
X
X return 0;
X}
X
X/* Check whether TYPE is derived from PARENT.
X Return the actual base information if so. Otherwise return 0.
X If PROTECT is 1, then emit an error message if access to
X a public field of PARENT would be private.
X If PROTECT is 2, then emit an error message if
X TYPE is derived from PARENT via private visibility rules.
X If PROTECT is 3, then immediately private baseclass is ok,
X but deeper than that, if private, emit error message. */
Xtree
Xget_base_type (parent, type, protect)
X register tree parent, type;
X{
X tree xtype = type;
X tree otype;
X int head = 0, tail = 0;
X int is_private = 0;
X tree rval = NULL_TREE;
X int rval_private = 0;
X tree friends = current_class_type
X ? CLASSTYPE_FRIEND_CLASSES (type) : NULL_TREE;
X
X#ifdef GATHER_STATISTICS
X n_calls_get_base_type++;
X#endif
X
X parent = TYPE_MAIN_VARIANT (parent);
X search_stack = push_search_level (search_stack, &search_obstack);
X
X while (1)
X {
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X if (CLASSTYPE_MARKED5 (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X int via_private = is_private || !CLASSTYPE_VIA_PUBLIC (type, i);
X
X if (via_private == 0)
X ;
X else if (protect == 0)
X via_private = 0;
X else if (protect == 1 && type == current_class_type)
X /* The immediate base class of the class we are in
X does let its public members through. */
X via_private = 0;
X#ifndef NOJJG
X else if (protect
X && friends != NULL_TREE
X && type == xtype
X && value_member (current_class_type, friends))
X /* Friend types of the most derived type have access
X to its baseclass pointers. */
X via_private = 0;
X#endif
X
X CLASSTYPE_MARKED5 (CLASSTYPE_BASECLASS (type, i)) = 1;
X otype = type;
X obstack_ptr_grow (&search_obstack, CLASSTYPE_BASECLASS (type, i));
X obstack_int_grow (&search_obstack, via_private);
X tail += 2;
X if (tail >= search_stack->limit)
X abort ();
X }
X else if (protect && ! CLASSTYPE_VIA_VIRTUAL (type, i))
X {
X error_with_aggr_type (parent, "type `%s' is ambiguous base class for type `%s'",
X TYPE_NAME_STRING (xtype));
X error ("(base class for types `%s' and `%s')",
X TYPE_NAME_STRING (type),
X TYPE_NAME_STRING (otype));
X rval = error_mark_node;
X break;
X }
X
X dont_queue:
X /* Process head of queue, if one exists. */
X if (head >= tail)
X break;
X
X type = search_stack->first[head++];
X is_private = (int)search_stack->first[head++];
X if (TYPE_MAIN_VARIANT (type) == parent)
X {
X if (rval == 0)
X {
X rval = type;
X rval_private = is_private;
X }
X goto dont_queue;
X }
X }
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X
X while (tp < search_tail)
X {
X CLASSTYPE_MARKED5 (*tp) = 0;
X tp += 2;
X }
X }
X search_stack = pop_search_level (search_stack);
X
X if (rval == error_mark_node)
X return error_mark_node;
X
X if (rval && protect && rval_private)
X {
X if (protect == 3)
X {
X int i;
X
X for (i = 1; i <= CLASSTYPE_N_BASECLASSES (xtype); i++)
X if (parent == TYPE_MAIN_VARIANT (CLASSTYPE_BASECLASS (xtype, i)))
X /* It's ok, since it's immedate. */
X return rval;
X }
X error_with_aggr_type (xtype, "type `%s' is derived from private `%s'",
X TYPE_NAME_STRING (parent));
X return error_mark_node;
X }
X
X return rval;
X}
X
X/* Return the number of levels between type PARENT and type TYPE,
X following the leftmost path to PARENT. If PARENT is its own main
X type variant, then if PARENT appears in different places from TYPE's
X point of view, the leftmost PARENT will be the one chosen.
X
X Return -1 if TYPE is not derived from PARENT.
X Return -2 if PARENT is an ambiguous base class of TYPE.
X Return -3 if PARENT is private to TYPE, and protect is non-zero.
X
X If PATH_PTR is non-NULL, then also build the list of types
X from PARENT to TYPE, with TREE_VIA_VIRUAL and TREE_VIA_PUBLIC
X set. */
Xget_base_distance (parent, type, protect, path_ptr)
X register tree parent, type;
X int protect;
X tree *path_ptr;
X{
X int head = 0, tail = 0;
X int is_private = 0;
X int rval;
X int depth = 0;
X int rval_private = 0;
X tree basetypes;
X tree friends = current_class_type
X ? CLASSTYPE_FRIEND_CLASSES (type) : NULL_TREE;
X int use_leftmost;
X
X if (TREE_READONLY (parent) || TREE_VOLATILE (parent))
X parent = TYPE_MAIN_VARIANT (parent);
X use_leftmost = (parent == TYPE_MAIN_VARIANT (parent));
X
X if (path_ptr)
X basetypes = CLASSTYPE_AS_LIST (type);
X
X if (TYPE_MAIN_VARIANT (parent) == type)
X {
X /* If the distance is 0, then we don't really need
X a path pointer, but we shouldn't let garbage go back. */
X if (path_ptr)
X *path_ptr = basetypes;
X return 0;
X }
X
X search_stack = push_search_level (search_stack, &search_obstack);
X
X while (1)
X {
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X if (CLASSTYPE_MARKED5 (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X tree btypes;
X
X int via_private = is_private || !CLASSTYPE_VIA_PUBLIC (type, i);
X
X if (via_private == 0)
X ;
X else if (protect == 0)
X via_private = 0;
X#if 0
X /* 13 Jan, 1990: I guess this is turned off because
X `get_base_type' will emit a more eloquent message
X if a message desired [--Michael]. */
X /* The immediate base class of the class we are in
X does let its public members through. */
X else if (type == current_class_type)
X via_private = 0;
X else if (protect
X && friends != NULL_TREE
X && type == xtype
X && value_member (current_class_type, friends))
X /* Friend types of the most derived type have access
X to its baseclass pointers. */
X via_private = 0;
X#endif
X
X CLASSTYPE_MARKED5 (CLASSTYPE_BASECLASS (type, i)) = 1;
X obstack_ptr_grow (&search_obstack, CLASSTYPE_BASECLASS (type, i));
X
X obstack_ptr_grow (&search_obstack, depth);
X obstack_int_grow (&search_obstack, via_private);
X if (path_ptr)
X {
X btypes = tree_cons (NULL_TREE, CLASSTYPE_BASECLASS (type, i),
X basetypes);
X TREE_VIA_PUBLIC (btypes) = CLASSTYPE_VIA_PUBLIC (type, i);
X TREE_VIA_VIRTUAL (btypes) = CLASSTYPE_VIA_VIRTUAL (type, i);
X obstack_ptr_grow (&search_obstack, btypes);
X tail += 1;
X }
X tail += 3;
X if (tail >= search_stack->limit)
X abort ();
X }
X else if (! CLASSTYPE_VIA_VIRTUAL (type, i))
X {
X rval = -2;
X goto done;
X }
X
X /* Process head of queue, if one exists. */
X if (head >= tail)
X {
X rval = -1;
X break;
X }
X
X type = search_stack->first[head++];
X depth = (int)search_stack->first[head++] + 1;
X is_private = (int)search_stack->first[head++];
X if (path_ptr)
X basetypes = search_stack->first[head++];
X if (type == parent
X || (use_leftmost && TYPE_MAIN_VARIANT (type) == parent))
X {
X rval = depth;
X rval_private = is_private;
X break;
X }
X }
X done:
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X int increment = path_ptr ? 4 : 3;
X
X while (tp < search_tail)
X {
X CLASSTYPE_MARKED5 (*tp) = 0;
X tp += increment;
X }
X
X /* Now, guarantee that we are following the leftmost
X path in the chain. */
X if (use_leftmost
X && rval > 0
X && (DECL_OFFSET (TYPE_NAME (type)) != 0
X || TREE_VIA_VIRTUAL (type)))
X {
X /* Reduce all types yet to be fully processed into
X the base type we are looking for, or NULL_TREE. */
X for (tp = search_stack->first; tp < search_tail; tp += increment)
X {
X tree *sub_tp, sub_path_ptr;
X int sub_rval;
X
X /* Don't chase down more right-most paths. */
X if (TREE_VIA_VIRTUAL (*tp)
X || DECL_OFFSET (TYPE_NAME (*tp)) > DECL_OFFSET (TYPE_NAME (type)))
X {
X *tp = NULL_TREE;

X continue;
X }
X
X /* Don't hassle with duplicates. */
X if (*tp == type)
X goto skip;
X
X for (sub_tp = search_stack->first; sub_tp < tp; sub_tp += increment)
X if (*tp == *sub_tp)
X goto skip;
X
X /* Find this type's TYPE basetype, if it has one. */
X sub_rval = get_base_distance (parent, *tp, 0, &sub_path_ptr);
X if (sub_rval == -1)
X *tp = NULL_TREE;
X else
X {
X if (path_ptr && TREE_CHAIN (tp[3]))
X {
X tree last;
X tree next_to_last = sub_path_ptr;
X while (TREE_CHAIN (next_to_last)
X && TREE_CHAIN (TREE_CHAIN (next_to_last)))
X next_to_last = TREE_CHAIN (next_to_last);
X if (next_to_last == sub_path_ptr)
X {
X sub_path_ptr = copy_node (sub_path_ptr);
X last = sub_path_ptr;
X }
X else
X {
X last = copy_node (TREE_CHAIN (next_to_last));
X TREE_CHAIN (next_to_last) = last;
X }
X TREE_CHAIN (last) = TREE_CHAIN (tp[3]);
X }
X *tp = TREE_VALUE (sub_path_ptr);
X if (path_ptr)
X tp[3] = sub_path_ptr;
X }
X skip: {}
X }
X
X /* For all the types which reduce to TYPE, choose
X the leftmost non-virtual one of them. */
X for (tp = search_stack->first; tp < search_tail; tp += increment)
X {
X if (*tp == NULL_TREE)
X continue;
X
X if (DECL_OFFSET (TYPE_NAME (*tp)) < DECL_OFFSET (TYPE_NAME (type)))
X {
X rval = -2;
X type = *tp;
X if (path_ptr)
X basetypes = tp[3];
X }
X }
X if (rval == -2)
X rval_private = 0;
X }
X }
X search_stack = pop_search_level (search_stack);
X
X if (rval && protect && rval_private)
X return -3;
X
X if (path_ptr)
X *path_ptr = basetypes;
X return rval;
X}
X
X/* Search for a member with name NAME in a multiple inheritance lattice
X specified by TYPE. If it does not exist, return NULL_TREE.
X If the member is ambiguously referenced, return `error_mark_node'.
X Otherwise, return the FIELD_DECL. */
X

X/* Do a 1-level search for NAME as a member of TYPE. The caller
X must figure out whether it has a visible path to this field.
X (Since it is only one level, this is reasonable.) */
Xstatic tree
Xlookup_field_1 (type, name)
X tree type, name;
X{
X register tree field = TYPE_FIELDS (type);
X
X#ifdef GATHER_STATISTICS
X n_calls_lookup_field_1++;
X#endif
X while (field)
X {
X#ifdef GATHER_STATISTICS
X n_fields_searched++;
X#endif
X if (DECL_ANON_UNION_ELEM (field))
X {
X tree temp = lookup_field_1 (TREE_TYPE (field), name);
X if (temp)
X return temp;
X }
X if (DECL_NAME (field) == name)
X return field;
X field = TREE_CHAIN (field);
X }
X /* Not found. */
X if (name == _vptr_name)
X {
X /* Give the user what s/he thinks s/he wants. */
X if (TYPE_VIRTUAL_P (type))
X return CLASSTYPE_VFIELD (type);
X }
X return NULL_TREE;
X}
X
X/* Compute the visibility of FIELD. This is done by computing
X the visibility available to each type in BASETYPES (which comes
X as a list of [via_public/basetype] in reverse order, namely base
X class before derived class). The first one which defines a
X visibility defines the visibility for the field. Otherwise, the
X visibility of the field is that which occurs normally.
X
X Uses global variables CURRENT_CLASS_TYPE and
X CURRENT_FUNCTION_DECL to use friend relationships
X if necessary.
X
X This will be static when lookup_fnfield comes into this file. */
X
X#define PUBLIC_RETURN do { TREE_FIELD_PUBLIC (field) = 1; return visibility_public; } while (0)
X#define PROTECTED_RETURN do { TREE_FIELD_PROTECTED (field) = 1; return visibility_protected; } while (0)
X#define PRIVATE_RETURN do { TREE_FIELD_PRIVATE (field) = 1; return visibility_private; } while (0)
X
Xenum visibility_type
Xcompute_visibility (basetypes, field)
X tree basetypes, field;
X{
X enum visibility_type visibility = visibility_public;
X tree types, type;
X tree context = DECL_FIELD_CONTEXT (field);
X
X /* Virtual function tables are never private.
X But we should know that we are looking for this,
X and not even try to hide it. */
X if (VFIELD_NAME_P (DECL_NAME (field)) == 1)
X return visibility_public;
X
X /* Make these special cases fast. */
X if (TREE_VALUE (basetypes) == current_class_type)
X {
X if (TREE_FIELD_PUBLIC (field))
X return visibility_public;
X if (TREE_FIELD_PROTECTED (field))
X return visibility_protected;
X if (TREE_FIELD_PRIVATE (field))
X return visibility_private;
X }
X
X /* Member function manipulating its own members. */
X if (current_class_type == context)
X PUBLIC_RETURN;
X
X /* Member found immediately within object. */
X if (TREE_CHAIN (basetypes) == NULL_TREE)
X {
X /* At object's top level, public members are public. */
X if (TREE_PROTECTED (field) == 0 && TREE_PRIVATE (field) == 0)
X PUBLIC_RETURN;
X
X /* Friend function manipulating members it gets (for being a friend). */
X if (is_friend (context, current_function_decl))
X PUBLIC_RETURN;
X
X /* Inner than that, without special visibility,
X
X protected members are ok if type of object is current_class_type
X is derived therefrom. This means that if the type of the object
X is a base type for our current class type, we cannot access
X protected members.
X
X private members are not ok. */
X if (current_class_type && DECL_VISIBILITY (field) == NULL_TREE)
X {
X if (TREE_PRIVATE (field))
X PRIVATE_RETURN;
X
X if (TREE_PROTECTED (field))
X {
X if (context == current_class_type
X || (type = get_base_type (current_class_type, context, 0)))
X PUBLIC_RETURN;
X else
X PROTECTED_RETURN;
X }
X else abort ();
X }
X }
X /* Friend function manipulating members it gets (for being a friend). */
X if (is_friend (context, current_function_decl))
X PUBLIC_RETURN;
X
X /* must reverse more than one element */
X basetypes = nreverse (basetypes);
X
X types = basetypes;
X
X while (types)
X {
X tree member;
X type = TYPE_MAIN_VARIANT (TREE_VALUE (types));
X
X member = purpose_member (type, DECL_VISIBILITY (field));
X if (member)
X {
X visibility = (enum visibility_type)TREE_VALUE (member);
X if (visibility == visibility_public
X || is_friend (type, current_function_decl)
X || (visibility == visibility_protected
X && current_class_type
X && get_base_type (context, current_class_type, 0)))
X visibility = visibility_public;
X goto ret;
X }
X
X /* Friends inherit the visibility of the class they inherit from. */
X if (is_friend (type, current_function_decl))
X {
X if (type == context)
X {
X visibility = visibility_public;
X goto ret;
X }
X if (TREE_PROTECTED (field))
X {
X visibility = visibility_public;
X goto ret;
X }
X#if 0
X /* This short-cut is too short. */
X if (visibility == visibility_public)
X goto ret;
X#endif
X /* else, may be a friend of a deeper base class */
X }
X
X if (type == context)
X break;
X
X types = TREE_CHAIN (types);
X /* If the next type was not VIA_PUBLIC, then fields of all
X remaining class past that one are private. */
X if (types && ! TREE_VIA_PUBLIC (types))
X visibility = visibility_private;
X }
X
X /* No special visibilities apply. Use normal rules.
X No assignment needed for BASETYPEs here from the nreverse.
X This is because we use it only for information about the
X path to the base. The code earlier dealt with what
X happens when we are at the base level. */
X
X if (visibility == visibility_public)
X {
X basetypes = nreverse (basetypes);
X if (TREE_PRIVATE (field))
X PRIVATE_RETURN;
X if (TREE_PROTECTED (field))
X {
X /* Used to check if the current class type was derived from
X the type that contains the field. This is wrong for
X multiple inheritance because is gives one class reference
X to protected members via another classes protected path.
X I.e., if A; B1 : A; B2 : A; Then B1 and B2 can access
X their own members which are protected in A, but not
X those same members in one another. */
X if (
X#if 1
X current_class_type
X && get_base_type (context, current_class_type, 0)
X#else
X current_class_type
X && value_member (current_class_type, basetypes)
X#endif
X )
X PUBLIC_RETURN;
X PROTECTED_RETURN;
X }
X PUBLIC_RETURN;
X }
X
X if (visibility == visibility_private
X && current_class_type != NULL_TREE)
X {
X if (TREE_PRIVATE (field))
X {
X nreverse (basetypes);
X PRIVATE_RETURN;
X }
X
X /* See if the field isn't protected. */
X if (TREE_PROTECTED (field))
X {
X tree test;
X#if 0
X test = get_base_type (type, current_class_type, 0);
X#else
X test = value_member (current_class_type, basetypes);
X#endif

X nreverse (basetypes);
X if (test)
X PUBLIC_RETURN;
X PROTECTED_RETURN;
X }
X
X /* See if the field isn't a public member of
X a private base class. */
X
X visibility = visibility_public;
X types = TREE_CHAIN (basetypes);
X while (types)
X {
X if (! TREE_VIA_PUBLIC (types))
X {
X if (visibility == visibility_private)
X {
X visibility = visibility_private;
X goto ret;
X }
X visibility = visibility_private;
X }
X if (TYPE_MAIN_VARIANT (TREE_VALUE (types)) == context)
X {
X visibility = visibility_public;
X goto ret;
X }
X types = TREE_CHAIN (types);
X }
X abort ();
X }
X
X ret:
X nreverse (basetypes);
X
X if (visibility == visibility_public)
X TREE_FIELD_PUBLIC (field) = 1;
X else if (visibility == visibility_protected)
X TREE_FIELD_PROTECTED (field) = 1;
X else if (visibility == visibility_private)
X TREE_FIELD_PRIVATE (field) = 1;
X else abort ();
X return visibility;
X}
X
X/* Make an entry in the memoized table for type TYPE
X that the entry for NAME is FIELD. */
X
Xtree
Xmake_memoized_table_entry (type, name, function_p)
X tree type, name;
X int function_p;
X{
X int index = MEMOIZED_HASH_FN (name);
X tree entry, *prev_entry;
X
X memoized_adds[function_p] += 1;
X if (CLASSTYPE_MTABLE_ENTRY (type) == NULL_TREE)
X {
X obstack_ptr_grow (&type_obstack, type);
X obstack_blank (&type_obstack, sizeof (struct memoized_entry *));
X CLASSTYPE_MTABLE_ENTRY (type) = my_new_memoized_entry (0);
X type_stack->len++;
X if (type_stack->len * 2 >= type_stack->base.limit)
X abort ();
X }
X if (function_p)
X prev_entry = &MEMOIZED_FNFIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
X else
X prev_entry = &MEMOIZED_FIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
X
X entry = my_tree_cons (name, 0, *prev_entry);
X *prev_entry = entry;
X
X /* Don't know the error message to give yet. */
X TREE_TYPE (entry) = error_mark_node;
X
X return entry;
X}
X
Xtree
Xlookup_field (xbasetype, name, protect)
X register tree xbasetype, name;
X int protect;
X{
X int head = 0, tail = 0;
X tree type, rval;
X tree basetype, basetypes;
X enum visibility_type this_v = visibility_default;
X tree entry;
X enum visibility_type own_visibility = visibility_default;
X int vbase_name_p = VBASE_NAME_P (name);
X
X /* Things for memoization. */
X char *errstr = 0;
X
X /* Set this to nonzero if we don't know how to compute
X accurate error messages for visibility. */
X int index = MEMOIZED_HASH_FN (name);
X
X if (TREE_CODE (xbasetype) == TREE_LIST)
X basetypes = xbasetype, basetype = TREE_VALUE (xbasetype);
X else
X basetypes = CLASSTYPE_AS_LIST (xbasetype), basetype = xbasetype;
X
X if (CLASSTYPE_MTABLE_ENTRY (basetype))
X {
X tree tem = MEMOIZED_FIELDS (CLASSTYPE_MTABLE_ENTRY (basetype), index);
X
X while (tem && TREE_PURPOSE (tem) != name)
X {
X memoized_fields_searched[0]++;
X tem = TREE_CHAIN (tem);
X }
X if (tem)
X {
X if (protect && TREE_TYPE (tem))
X {
X error (TREE_STRING_POINTER (TREE_TYPE (tem)),
X IDENTIFIER_POINTER (name),
X TYPE_NAME_STRING (DECL_FIELD_CONTEXT (TREE_VALUE (tem))));
X return error_mark_node;
X }
X if (TREE_VALUE (tem) == NULL_TREE)
X memoized_fast_rejects[0] += 1;
X else
X memoized_fast_finds[0] += 1;
X return TREE_VALUE (tem);
X }
X }
X
X#ifdef GATHER_STATISTICS
X n_calls_lookup_field++;
X#endif
X if (flag_memoize_lookups && ! global_bindings_p ())
X entry = make_memoized_table_entry (basetype, name, 0);
X else
X entry = 0;
X
X rval = lookup_field_1 (basetype, name);
X
X if (rval)
X {
X if (flag_memoize_lookups || protect)
X {
X if (TREE_PRIVATE (rval) | TREE_PROTECTED (rval))
X this_v = compute_visibility (basetypes, rval);
X if (TREE_CODE (rval) == CONST_DECL)
X {
X if (this_v == visibility_private)
X errstr = "enum `%s' is a private value of class `%s'";
X else if (this_v == visibility_protected)
X errstr = "enum `%s' is a protected value of class `%s'";
X }
X else
X {
X if (this_v == visibility_private)
X errstr = "member `%s' is a private member of class `%s'";
X else if (this_v == visibility_protected)
X errstr = "member `%s' is a protected member of class `%s'";
X }
X }
X
X if (entry)
X {
X if (errstr)
X {
X /* This depends on behavior of lookup_field_1! */
X tree error_string = my_build_string (errstr);
X TREE_TYPE (entry) = error_string;
X }
X else
X {
X /* Let entry know there is no problem with this access. */
X TREE_TYPE (entry) = NULL_TREE;
X#if 0
X /* And since everything is ok, bear the
X cost of generating correct code. */
X if (DECL_OFFSET (TYPE_NAME (basetype)) != 0
X || TREE_VIA_VIRTUAL (basetype))
X {
X rval = my_copy_node (rval);
X DECL_FIELD_CONTEXT (rval) = basetype;
X }
X#endif
X }
X TREE_VALUE (entry) = rval;
X }
X#if 0
X else if ((DECL_OFFSET (TYPE_NAME (basetype)) != 0
X || TREE_VIA_VIRTUAL (basetype))
X && ! (errstr && protect))
X {
X rval = my_copy_node (rval);
X DECL_FIELD_CONTEXT (rval) = basetype;
X }
X#endif
X
X if (errstr && protect)
X {
X error (errstr, IDENTIFIER_POINTER (name), TYPE_NAME_STRING (basetype));
X return error_mark_node;
X }
X return rval;
X }
X
X type = TYPE_MAIN_VARIANT (basetype);
X
X search_stack = push_search_level (search_stack, &search_obstack);
X TREE_VIA_PUBLIC (basetypes) = 1;
X
X while (1)
X {
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X {
X if (CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X tree btypes;
X
X CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)) = 1;
X btypes = my_tree_cons (NULL_TREE, CLASSTYPE_BASECLASS (type, i),
X basetypes);
X TREE_VIA_PUBLIC (btypes) = CLASSTYPE_VIA_PUBLIC (type, i);
X TREE_VIA_VIRTUAL (btypes) = CLASSTYPE_VIA_VIRTUAL (type, i);
X obstack_ptr_grow (&search_obstack, btypes);
X tail += 1;
X if (tail >= search_stack->limit)
X abort ();
X }
X }
X
X /* Process head of queue, if one exists. */
X if (head >= tail)
X break;
X
X basetypes = search_stack->first[head++];
X type = TREE_VALUE (basetypes);
X
X /* See if we can find NAME in TYPE. If RVAL is nonzero,
X and we do find NAME in TYPE, verify that such a second
X sighting is in fact legal. */
X
X if (rval)
X {
X /* Just another way of finding the same member. */
X if (DECL_FIELD_CONTEXT (rval) == type)
X {
X enum visibility_type new_v
X = compute_visibility (basetypes, rval);
X if (this_v != new_v)
X errstr = "conflicting visibilities to member `%s'";
X }
X /* Same baseclass, different places in the lattice. */
X else if (DECL_FIELD_CONTEXT (rval) == TYPE_MAIN_VARIANT (type))
X errstr = "member `%s' belongs to distinct base classes `%s'";
X else
X {
X tree nval = lookup_field_1 (type, name);
X
X if (nval && get_base_type (type, DECL_FIELD_CONTEXT (rval), 0) == 0)
X {
X /* We found it in other than a baseclass of RVAL's. */
X errstr = "request for member `%s' is ambiguous";
X }
X }
X if (errstr && entry)
X {
X tree error_string = my_build_string (errstr);
X TREE_TYPE (entry) = error_string;
X }
X if (errstr && protect)
X break;
X }
X else
X {
X rval = lookup_field_1 (type, name);
X if (rval)
X {
X#if 0
X if (DECL_OFFSET (TYPE_NAME (type)) != 0
X || TREE_VIA_VIRTUAL (type))
X {
X rval = my_copy_node (rval);
X DECL_FIELD_CONTEXT (rval) = type;
X }
X#endif
X
X if (entry || protect)
X this_v = compute_visibility (basetypes, rval);
X if (entry)
X TREE_VALUE (entry) = rval;
X
X /* These may look ambiguous, but they really are not. */
X if (vbase_name_p)
X break;
X }
X }
X }
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X
X /* If this FIELD_DECL defines its own visibility, deal with that. */
X if (rval && errstr == 0
X && DECL_VISIBILITY (rval)
X && (protect || entry))
X {
X while (tp < search_tail)
X {
X /* If is possible for one of the derived types on the
X path to have defined special visibility for this
X field. Look for such declarations and report an
X error if a conflict is found. */
X enum visibility_type new_v;
X
X if (this_v != visibility_default)
X new_v = compute_visibility (*tp, rval);
X if (this_v != visibility_default && new_v != this_v)
X {
X errstr = "conflicting visibilities to member `%s'";
X this_v = visibility_default;
X }
X own_visibility = new_v;
X CLASSTYPE_MARKED2 (TREE_VALUE (*tp++)) = 0;
X }
X }
X else
X {
X while (tp < search_tail)
X CLASSTYPE_MARKED2 (TREE_VALUE (*tp++)) = 0;
X }
X }
X search_stack = pop_search_level (search_stack);
X
X if (errstr == 0)
X {
X if (own_visibility == visibility_private)
X errstr = "member `%s' declared private";
X else if (own_visibility == visibility_protected)
X errstr = "member `%s' declared protected";
X else if (this_v == visibility_private)
X errstr = TREE_PRIVATE (rval) ? "member `%s' is private" : "member `%s' is from private base class";
X else if (this_v == visibility_protected)
X errstr = "member `%s' is protected";
X }
X
X if (entry)
X {
X if (errstr)
X {
X tree error_string = my_build_string (errstr);
X /* Save error message with entry. */
X TREE_TYPE (entry) = error_string;
X }
X else
X {
X /* Mark entry as having no error string. */
X TREE_TYPE (entry) = NULL_TREE;
X }
X }

X
X if (errstr && protect)
X {
X error (errstr, IDENTIFIER_POINTER (name), TYPE_NAME_STRING (type));
X rval = error_mark_node;
X }
X return rval;
X}
X
X/* TYPE is a class type. Return the index of the fields within
X the method vector with name NAME, or -1 is no such field exists. */
Xstatic int
Xlookup_fnfields_1 (type, name)
X tree type, name;
X{
X register tree method_vec = CLASSTYPE_METHOD_VEC (type);
X
X if (method_vec != 0)
X {
X register tree *methods = &TREE_VEC_ELT (method_vec, 0);
X register tree *end = TREE_VEC_END (method_vec);
X
X#ifdef GATHER_STATISTICS
X n_calls_lookup_fnfields_1++;
X#endif
X if (*methods == 0)
X methods++;
X while (methods != end)
X {
X#ifdef GATHER_STATISTICS
X n_outer_fields_searched++;
X#endif
X if (DECL_ORIGINAL_NAME (*methods) == name)
X break;
X methods++;
X }
X if (methods != end)
X return methods - &TREE_VEC_ELT (method_vec, 0);
X }
X
X return -1;
X}
X

X/* Given a list of member functions FIELDS (which are implicitly
X named TREE_PURPOSE (FIELDS), and come from base type
X DECL_FIELD_CONTEXT (TREE_VALUE (FIELDS))), attempt to find the
X actual method which can accept (using conversions) PARMS.
X The types of PARMS are already computed in PARMTYPES. */
Xtree
Xlookup_fnfield (fields, parms, parmtypes)
X tree fields, parms, parmtypes;
X{
X abort ();
X}
X
X/* Starting from BASETYPE, return a TREE_BASELINK-like object
X which gives the following information (in a list):
X
X TREE_TYPE: list of basetypes needed to get to...
X TREE_VALUE: list of all functions in of given type
X which have name NAME.
X
X No visibility information is computed by this function,
X other then to adorn the list of basetypes with
X TREE_VIA_PUBLIC.
X
X If FIND_AMBIGUOUS is non-zero, then if we find two ways to get
X to the same member function, both those ways are found,
X and the caller must know what to do about this. */
Xtree
Xlookup_fnfields (basetypes, name, find_ambiguous)
X tree basetypes, name;
X int find_ambiguous;
X{
X int head = 0, tail = 0;
X tree type, rval, rvals = NULL_TREE;
X tree basetype;
X tree entry;
X
X /* For now, don't try this. */
X int protect = find_ambiguous;
X
X /* Things for memoization. */
X char *errstr = 0;
X
X /* Set this to nonzero if we don't know how to compute
X accurate error messages for visibility. */
X int index = MEMOIZED_HASH_FN (name);
X
X basetype = TREE_VALUE (basetypes);
X
X if (CLASSTYPE_MTABLE_ENTRY (basetype))
X {
X tree tem = MEMOIZED_FNFIELDS (CLASSTYPE_MTABLE_ENTRY (basetype), index);
X
X while (tem && TREE_PURPOSE (tem) != name)
X {
X memoized_fields_searched[1]++;
X tem = TREE_CHAIN (tem);
X }
X if (tem)
X {
X if (protect && TREE_TYPE (tem))
X {
X error (TREE_STRING_POINTER (TREE_TYPE (tem)),
X IDENTIFIER_POINTER (name),
X TYPE_NAME_STRING (DECL_FIELD_CONTEXT (TREE_VALUE (TREE_VALUE (tem)))));
X return error_mark_node;
X }
X if (TREE_VALUE (tem) == NULL_TREE)
X {
X memoized_fast_rejects[1] += 1;
X return NULL_TREE;
X }
X else
X {
X /* Want to return this, but we must make sure
X that visibility information is consistent. */
X tree baselink = TREE_VALUE (tem);
X tree memoized_basetypes = TREE_PURPOSE (baselink);
X tree these_basetypes = basetypes;
X while (memoized_basetypes && these_basetypes)
X {
X memoized_fields_searched[1]++;
X if (TREE_VALUE (memoized_basetypes) != TREE_VALUE (these_basetypes))
X break;
X memoized_basetypes = TREE_CHAIN (memoized_basetypes);
X these_basetypes = TREE_CHAIN (these_basetypes);
X }
X if (memoized_basetypes == these_basetypes)
X {
X memoized_fast_finds[1] += 1;
X return TREE_VALUE (tem);
X }
X /* else, we must re-find this field by hand. */
X baselink = tree_cons (basetypes, TREE_VALUE (baselink), TREE_CHAIN (baselink));
X return baselink;
X }
X }
X }
X
X#ifdef GATHER_STATISTICS
X n_calls_lookup_fnfields++;
X#endif
X if (flag_memoize_lookups && ! global_bindings_p ())
X entry = make_memoized_table_entry (basetype, name, 1);
X else
X entry = 0;
X
X index = lookup_fnfields_1 (basetype, name);
X
X if (index >= 0)
X {
X rval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), index);
X rvals = my_tree_cons (basetypes, rval, NULL_TREE);
X if (CLASSTYPE_BASELINK_VEC (basetype))
X TREE_TYPE (rvals) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (basetype), index);
X
X if (entry)
X {
X TREE_VALUE (entry) = rvals;
X TREE_TYPE (entry) = NULL_TREE;
X }
X
X if (errstr && protect)
X {
X error (errstr, IDENTIFIER_POINTER (name), TYPE_NAME_STRING (basetype));
X return error_mark_node;
X }
X return rvals;
X }
X rval = NULL_TREE;
X type = TYPE_MAIN_VARIANT (basetype);
X
X search_stack = push_search_level (search_stack, &search_obstack);
X TREE_VIA_PUBLIC (basetypes) = 1;
X
X while (1)
X {
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X {
X if (CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X tree btypes;
X
X CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)) = 1;
X btypes = my_tree_cons (NULL_TREE, CLASSTYPE_BASECLASS (type, i),
X basetypes);
X TREE_VIA_PUBLIC (btypes) = CLASSTYPE_VIA_PUBLIC (type, i);
X TREE_VIA_VIRTUAL (btypes) = CLASSTYPE_VIA_VIRTUAL (type, i);
X obstack_ptr_grow (&search_obstack, btypes);
X tail += 1;
X if (tail >= search_stack->limit)
X abort ();
X }
X }
X
X /* Process head of queue, if one exists. */
X if (head >= tail)
X break;
X
X basetypes = search_stack->first[head++];
X type = TREE_VALUE (basetypes);
X
X /* See if we can find NAME in TYPE. If RVAL is nonzero,
X and we do find NAME in TYPE, verify that such a second
X sighting is in fact legal. */
X
X if (rval)
X {
X tree context = DECL_FIELD_CONTEXT (rval);
X /* Just another way of finding the same member. */
X if (context == type)
X ;
X /* Same baseclass, maybe different places in the lattice. */
X else if (context == TYPE_MAIN_VARIANT (type))
X {
X if (TREE_VIA_VIRTUAL (TREE_PURPOSE (rvals)))
X if (TREE_VIA_VIRTUAL (basetypes))
X ;
X else
X errstr = "member `%s' belongs to virtual and non-virtual baseclasses `%s'";
X else if (TREE_VIA_VIRTUAL (basetypes))
X errstr = "member `%s' belongs to virtual and non-virtual baseclasses `%s'";
X else
X errstr = "member `%s' belongs to MI-distinct base classes `%s'";
X }
X else
X {
X int index = lookup_fnfields_1 (type, name);
X
X if (index >= 0 && get_base_type (type, context, 0) == 0)
X {
X /* We found it in other than a baseclass of RVAL's. */
X rvals = my_tree_cons (basetypes, TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index), rvals);
X if (CLASSTYPE_BASELINK_VEC (type))
X TREE_TYPE (rvals) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
X }
X }
X if (errstr && entry)
X {
X tree error_string = my_build_string (errstr);
X TREE_TYPE (entry) = error_string;
X }
X if (errstr && find_ambiguous)
X {
X rvals = error_mark_node;
X break;
X }
X }
X else
X {
X int index = lookup_fnfields_1 (type, name);
X if (index >= 0)
X {
X rval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
X rvals = my_tree_cons (basetypes, rval, NULL_TREE);
X if (CLASSTYPE_BASELINK_VEC (type))
X TREE_TYPE (rvals) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
X if (entry)
X TREE_VALUE (entry) = rvals;
X }
X else
X rval = NULL_TREE;
X }
X }
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X
X while (tp < search_tail)
X CLASSTYPE_MARKED2 (TREE_VALUE (*tp++)) = 0;
X }
X search_stack = pop_search_level (search_stack);
X
X if (entry)
X {
X if (errstr)
X {
X tree error_string = my_build_string (errstr);
X /* Save error message with entry. */
X TREE_TYPE (entry) = error_string;
X }
X else
X {
X /* Mark entry as having no error string. */
X TREE_TYPE (entry) = NULL_TREE;
X }
X }
X
X if (errstr && protect)
X {
X error (errstr, IDENTIFIER_POINTER (name), TYPE_NAME_STRING (type));
X rvals = error_mark_node;
X }
X
X return rvals;
X}
X
X/* BREADTH-FIRST SEARCH ROUTINES. */
X
X/* Search a multiple inheritance hierarchy by breadth-first search.
X
X TYPE is an aggregate type, possibly in a multiple-inheritance hierarchy.
X TESTFN is a function, which, if true, means that our condition has been met,
X and its return value should be returned.
X QFN, if non-NULL, is a predicate dictating whether the type should
X even be queued. */
X
Xint
Xbreadth_first_search (type, testfn, qfn)
X tree type;
X int (*testfn)();
X int (*qfn)();
X{
X int head = 0, tail = 0;
X int rval = 0;
X
X search_stack = push_search_level (search_stack, &search_obstack);
X
X while (1)
X {
X int n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X int i;
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X if (CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) == 0
X && (qfn == 0 || (*qfn) (type, i)))
X {
X CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) = 1;
X obstack_ptr_grow (&search_obstack, type);
X obstack_int_grow (&search_obstack, i);
X tail += 2;
X if (tail >= search_stack->limit)
X abort ();
X }
X
X /* Process head of queue, if one exists. */
X if (head >= tail)
X {
X rval = 0;
X break;
X }
X
X type = search_stack->first[head++];
X i = (int)search_stack->first[head++];
X if (rval = (*testfn) (type, i))
X break;
X type = CLASSTYPE_BASECLASS (type, i);
X }
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X while (tp < search_tail)
X {
X tree type = *tp++;
X int i = (int)(*tp++);
X CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) = 0;
X }
X }
X
X search_stack = pop_search_level (search_stack);
X return rval;
X}
X
X/* Functions to use in breadth first searches. */
Xtypedef tree (*pft)();
Xtypedef int (*pfi)();
X
Xint tree_needs_constructor_p (type, i)
X tree type;
X{
X tree basetype = i == 0 ? type : CLASSTYPE_BASECLASS (type, i);
X return TYPE_NEEDS_CONSTRUCTOR (basetype);
X}
X
Xstatic tree declarator;
X
Xstatic tree
Xget_virtuals_named_this (type, i)
X tree type;
X int i;
X{
X tree basetype = i == 0? type : CLASSTYPE_BASECLASS (type, i);
X tree fields = lookup_fnfields (CLASSTYPE_AS_LIST (basetype), declarator, 0);
X
X if (fields == 0 || fields == error_mark_node)
X return 0;
X
X /* Get to the function decls, and return the first virtual function
X with this name, if there is one. */
X while (fields)
X {
X tree fndecl;
X
X for (fndecl = TREE_VALUE (fields); fndecl; fndecl = TREE_CHAIN (fndecl))
X if (DECL_VIRTUAL_P (fndecl))
X return fields;
X fields = next_baselink (fields);
X }
X return NULL_TREE;
X}
X
Xstatic tree get_virtual_destructor (type, i)
X tree type;
X int i;
X{
X type = i == 0 ? type : CLASSTYPE_BASECLASS (type, i);
X if (TYPE_HAS_DESTRUCTOR (type)
X && DECL_VIRTUAL_P (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0)))
X return TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0);
X return 0;
X}
X
Xint tree_has_any_destructor_p (type, i)
X tree type;
X int i;
X{
X if (i == 0)
X return TYPE_NEEDS_DESTRUCTOR (type);
X return TYPE_NEEDS_DESTRUCTOR (CLASSTYPE_BASECLASS (type, i));
X}
X
X/* Given a class type TYPE, and a function decl FNDECL,
X look for the first function the TYPE's heirarchy which
X FNDECL could match as a virtual function.
X
X DTORP is nonzero if we are looking for a destructor. Destructors
X need special treatment because they do not match by name. */
Xtree
Xget_first_matching_virtual (type, fndecl, dtorp)
X tree type, fndecl;
X int dtorp;
X{
X tree tmp = NULL_TREE;
X
X /* Breadth first search routines start searching basetypes
X of TYPE, so we must perform first ply of search here. */
X if (dtorp)
X {
X if (tree_has_any_destructor_p (type, 0))
X tmp = get_virtual_destructor (type, 0);
X
X if (tmp)
X return tmp;
X
X tmp = (tree) breadth_first_search (type,
X (pfi) get_virtual_destructor,
X tree_has_any_destructor_p);
X return tmp;
X }
X else
X {
X tree drettype, dtypes, btypes, instptr_type;
X tree basetype = TYPE_METHOD_BASETYPE (fndecl);
X tree baselink, best = NULL_TREE;
X tree name = DECL_NAME (fndecl);
X
X declarator = DECL_ORIGINAL_NAME (fndecl);
X if (DECL_VIRTUAL_P (declarator) == 0)
X return 0;
X
X drettype = TREE_TYPE (TREE_TYPE (fndecl));
X dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
X if (DECL_STATIC_FUNCTION_P (fndecl))
X instptr_type = NULL_TREE;
X else
X instptr_type = TREE_TYPE (TREE_VALUE (dtypes));
X
X for (baselink = get_virtuals_named_this (type, 0);
X baselink; baselink = next_baselink (baselink))
X {
X for (tmp = TREE_VALUE (baselink); tmp; tmp = TREE_CHAIN (tmp))
X {
X if (! DECL_VIRTUAL_P (tmp))
X continue;
X
X btypes = TYPE_ARG_TYPES (TREE_TYPE (tmp));
X if (instptr_type == NULL_TREE
X && compparms (TREE_CHAIN (btypes), dtypes, 3))
X /* Caller knows to give error in this case. */
X return tmp;
X
X if ((TREE_READONLY (TREE_TYPE (TREE_VALUE (btypes)))
X == TREE_READONLY (instptr_type))
X && compparms (TREE_CHAIN (btypes), TREE_CHAIN (dtypes), 3))
X {
X if (IDENTIFIER_ERROR_LOCUS (name) == NULL_TREE
X && ! comptypes (TREE_TYPE (TREE_TYPE (tmp)), drettype, 1))
X {
X error_with_decl (fndecl, "conflicting return type specified for virtual function `%s'");
X SET_IDENTIFIER_ERROR_LOCUS (name, basetype);
X }
X break;
X }
X }
X if (tmp)
X {
X /* If this is ambiguous, we will warn about it later. */
X if (best)
X {
X if (get_base_distance (TYPE_METHOD_BASETYPE (TREE_TYPE (best)),
X TYPE_METHOD_BASETYPE (TREE_TYPE (tmp)), 0, 0) > 0)
X best = tmp;
X }
X else
X best = tmp;
X }
X }
X if (IDENTIFIER_ERROR_LOCUS (name) == NULL_TREE
X && best == NULL_TREE && warn_overloaded_virtual)
X {
X error_with_decl (fndecl, "conficting specification deriving virtual function `%s'");
X SET_IDENTIFIER_ERROR_LOCUS (name, basetype);
X }
X return best;
X }
X}
X
X/* Return the list of virtual functions which are abstract in type TYPE.
X This information is cached, and so must be built on a
X non-temporary obstack. */
Xtree
Xget_abstract_virtuals (type)
X tree type;
X{
X /* For each layer of base class (i.e., the first base class, and each
X virtual base class from that one), modify the virtual function table
X of the derived class to contain the new virtual function.
X A class has as many vfields as it has virtual base classes (total). */
X tree vfields, vbases, base, tmp;
X tree vfield = CLASSTYPE_VFIELD (type);
X tree fcontext = vfield ? DECL_FCONTEXT (vfield) : NULL_TREE;
X tree abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (type);
X
X for (vfields = CLASSTYPE_VFIELDS (type); vfields; vfields = TREE_CHAIN (vfields))
X {
X int normal;
X
X /* Find the right base class for this derived class, call it BASE. */
X base = TREE_VALUE (vfields);
X if (base == type)
X continue;
X
X /* We call this case NORMAL iff this virtual function table
X pointer field has its storage reserved in this class.
X This is normally the case without virtual baseclasses
X or off-center multiple baseclasses. */
X normal = (base == fcontext
X && (TREE_PURPOSE (vfields) == NULL_TREE
X || ! TREE_VIA_VIRTUAL (TREE_PURPOSE (vfields))));
X
X if (normal)
X tmp = TREE_CHAIN (CLASS_ASSOC_VIRTUALS (type));
X else
X {
X tree assoc = assoc_value (base, type);
X tmp = TREE_CHAIN (ASSOC_VIRTUALS (assoc));
X }
X
X while (tmp)
X {
X tree base_pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (tmp));
X tree base_fndecl = TREE_OPERAND (base_pfn, 0);
X if (DECL_ABSTRACT_VIRTUAL_P (base_fndecl))
X abstract_virtuals = tree_cons (NULL_TREE, base_fndecl, abstract_virtuals);
X tmp = TREE_CHAIN (tmp);
X }
X }
X for (vbases = CLASSTYPE_VBASECLASSES (type); vbases; vbases = TREE_CHAIN (vbases))
X {
X if (! ASSOC_VIRTUALS (vbases));
X continue;
X
X base = TREE_TYPE (vbases);
X tmp = TREE_CHAIN (ASSOC_VIRTUALS (vbases));
X while (tmp)
X {
X tree base_pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (tmp));
X tree base_fndecl = TREE_OPERAND (base_pfn, 0);
X if (DECL_ABSTRACT_VIRTUAL_P (base_fndecl))
X abstract_virtuals = tree_cons (NULL_TREE, base_fndecl, abstract_virtuals);
X tmp = TREE_CHAIN (tmp);
X }
X }
X return nreverse (abstract_virtuals);
X}
X
X/* For the type TYPE, return a list of member functions available from
X base classes with name NAME. The TREE_VALUE of the list is a chain of
X member functions with name NAME. The TREE_PURPOSE of the list is a
X basetype, or a list of base types (in reverse order) which were
X traversed to reach the chain of member functions. If we reach a base
X type which provides a member function of name NAME, and which has at
X most one base type itself, then we can terminate the search. */
X
Xtree
Xget_baselinks (type, name)
X tree type, name;
X{
X tree hash_tree_cons ();
X int head = 0, tail = 0, index;
X tree rval = 0, nval = 0;
X tree basetypes = CLASSTYPE_AS_LIST (type);
X
X search_stack = push_search_level (search_stack, &search_obstack);
X
X while (1)
X {
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X /* Process and/or queue base types. */
X for (i = 1; i <= n_baselinks; i++)
X if (CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X tree btypes;
X
X btypes = hash_tree_cons (CLASSTYPE_VIA_PUBLIC (type, i),
X CLASSTYPE_VIA_VIRTUAL (type, i),
X NULL_TREE, CLASSTYPE_BASECLASS (type, i),
X basetypes);
X CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) = 1;
X obstack_ptr_grow (&search_obstack, btypes);
X
X tail += 1;
X if (tail >= search_stack->limit)
X abort ();
X }
X
X dont_queue:
X /* Process head of queue, if one exists. */
X if (head >= tail)
X break;
X
X basetypes = search_stack->first[head++];
X type = TREE_VALUE (basetypes);
X index = lookup_fnfields_1 (type, name);
X if (index >= 0)
X {
X nval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
X rval = hash_tree_cons (0, 0, basetypes, nval, rval);
X if (CLASSTYPE_N_BASECLASSES (type) <= 1)
X {
X if (CLASSTYPE_BASELINK_VEC (type))
X TREE_TYPE (rval) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
X goto dont_queue;
X }
X }
X nval = NULL_TREE;
X }
X {
X tree *tp = search_stack->first;
X tree *search_tail = tp + tail;
X
X while (tp < search_tail)
X {
X CLASSTYPE_MARKED (TREE_VALUE (*tp++)) = 0;
X }
X }
X search_stack = pop_search_level (search_stack);
X return rval;
X}
X
Xtree
Xnext_baselink (baselink)
X tree baselink;
X{
X tree tmp = TREE_TYPE (baselink);
X baselink = TREE_CHAIN (baselink);
X while (tmp)
X {
X /* @@ does not yet add previous base types. */
X baselink = tree_cons (TREE_PURPOSE (tmp), TREE_VALUE (tmp),
X baselink);
X TREE_TYPE (baselink) = TREE_TYPE (tmp);
X tmp = TREE_CHAIN (tmp);
X }
X return baselink;
X}
X
X/* DEPTH-FIRST SEARCH ROUTINES. */
X
X/* Assign unique numbers to _CLASSTYPE members of the lattice
X specified by TYPE. The root nodes are marked first; the nodes
X are marked depth-fisrt, left-right. */
X
Xstatic int cid;
X
X/* Matrix implementing a relation from CLASSTYPE X CLASSTYPE => INT.
X Relation yields 1 if C1 <= C2, 0 otherwise. */
Xtypedef char mi_boolean;
Xstatic mi_boolean *mi_matrix;
X
X/* Type for which this matrix is defined. */
Xstatic tree mi_type;
X
X/* Size of the matrix for indexing purposes. */
Xstatic int mi_size;
X
X/* Return nonzero if class C2 derives from class C1. */
X#define DERIVES_FROM(C1, C2) \
X ((mi_matrix+mi_size*(CLASSTYPE_CID (C1)-1))[CLASSTYPE_CID (C2)-1])
X#define DERIVES_FROM_STAR(C) \
X (mi_matrix+(CLASSTYPE_CID (C)-1))
X
X/* The main function which implements depth first search. */
Xstatic void
Xdfs_walk (type, fn, qfn)
X tree type;
X void (*fn)();
X int (*qfn)();
X{
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X for (i = 1; i <= n_baselinks; i++)
X if ((*qfn)(CLASSTYPE_BASECLASS (type, i)))
X {
X dfs_walk (CLASSTYPE_BASECLASS (type, i), fn, qfn);
X }
X
X fn (type);
X}
X
X/* Predicate functions which serve for dfs_walk. */
Xstatic int numberedp (type) tree type;
X{ return CLASSTYPE_CID (type); }
Xstatic int unnumberedp (type) tree type;
X{ return CLASSTYPE_CID (type) == 0; }
X
Xstatic int markedp (type) tree type;
X{ return CLASSTYPE_MARKED (type); }
Xstatic int bfs_markedp (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)); }
Xstatic int unmarkedp (type) tree type;
X{ return CLASSTYPE_MARKED (type) == 0; }
Xstatic int bfs_unmarkedp (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED (CLASSTYPE_BASECLASS (type, i)) == 0; }
Xstatic int marked2p (type) tree type;
X{ return CLASSTYPE_MARKED2 (type); }
Xstatic int bfs_marked2p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)); }
Xstatic int unmarked2p (type) tree type;
X{ return CLASSTYPE_MARKED2 (type) == 0; }
Xstatic int bfs_unmarked2p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED2 (CLASSTYPE_BASECLASS (type, i)) == 0; }
Xstatic int marked3p (type) tree type;
X{ return CLASSTYPE_MARKED3 (type); }
Xstatic int bfs_marked3p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED3 (CLASSTYPE_BASECLASS (type, i)); }
Xstatic int unmarked3p (type) tree type;
X{ return CLASSTYPE_MARKED3 (type) == 0; }
Xstatic int bfs_unmarked3p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED3 (CLASSTYPE_BASECLASS (type, i)) == 0; }
Xstatic int marked4p (type) tree type;
X{ return CLASSTYPE_MARKED4 (type); }
Xstatic int bfs_marked4p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED4 (CLASSTYPE_BASECLASS (type, i)); }
Xstatic int unmarked4p (type) tree type;
X{ return CLASSTYPE_MARKED4 (type) == 0; }
Xstatic int bfs_unmarked4p (type, i) tree type; int i;
X{ return CLASSTYPE_MARKED4 (CLASSTYPE_BASECLASS (type, i)) == 0; }
X
Xstatic int dfs_search_slot_nonempty_p (type) tree type;
X{ return CLASSTYPE_SEARCH_SLOT (type) != 0; }
X
X
X/* The worker functions for `dfs_walk'. These do not need to
X test anything (vis a vis marking) if they are paired with
X a predicate function (above). */
X
X/* Assign each type within the lattice a number which is unique
X in the lattice. The first number assigned is 1. */
X
Xstatic void
Xdfs_number (type)
X tree type;
X{
X CLASSTYPE_CID (type) = ++cid;
X}
X
Xstatic void
Xdfs_unnumber (type)
X tree type;
X{
X CLASSTYPE_CID (type) = 0;
X}
X
Xstatic void
Xdfs_mark (type) tree type;
X{ CLASSTYPE_MARKED (type) = 1; }
X
Xstatic void
Xdfs_unmark (type) tree type;
X{ CLASSTYPE_MARKED (type) = 0; }
X
Xstatic void
Xdfs_mark2 (type) tree type;
X{ CLASSTYPE_MARKED2 (type) = 1; }
X
Xstatic void
Xdfs_unmark2 (type) tree type;
X{ CLASSTYPE_MARKED2 (type) = 0; }
X
Xstatic void
Xdfs_mark3 (type) tree type;
X{ CLASSTYPE_MARKED3 (type) = 1; }
X
Xstatic void
Xdfs_unmark3 (type) tree type;
X{ CLASSTYPE_MARKED3 (type) = 0; }
X
Xstatic void
Xdfs_mark4 (type) tree type;
X{ CLASSTYPE_MARKED4 (type) = 1; }
X
Xstatic void
Xdfs_unmark4 (type) tree type;
X{ CLASSTYPE_MARKED4 (type) = 0; }
X
Xstatic void
Xdfs_unmark12 (type) tree type;
X{ CLASSTYPE_MARKED (type) = 0;
X CLASSTYPE_MARKED2 (type) = 0; }
X
Xstatic void
Xdfs_unmark34 (type) tree type;
X{ CLASSTYPE_MARKED3 (type) = 0;
X CLASSTYPE_MARKED4 (type) = 0; }
X
Xstatic tree
Xdfs_clear_search_slot (type) tree type;
X{ CLASSTYPE_SEARCH_SLOT (type) = 0; }
X
Xstatic tree vbase_types;
Xstatic tree vbase_decl, vbase_decl_ptr;
Xstatic tree vbase_init_result;
X
Xstatic void
Xdfs_find_vbases (type)
X tree type;
X{
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X
X for (i = n_baselinks; i > 0; i--)
X if (CLASSTYPE_VIA_VIRTUAL (type, i)
X && CLASSTYPE_SEARCH_SLOT (CLASSTYPE_BASECLASS (type, i)) == 0)
X {
X tree vbase = CLASSTYPE_BASECLASS (type, i);
X /* ??? ASSOC_VALUE and TREE_VALUE must be the same for this to work. */
X tree assoc = value_member (TYPE_MAIN_VARIANT (vbase), vbase_types);
X
X CLASSTYPE_SEARCH_SLOT (vbase)
X = build (PLUS_EXPR, TYPE_POINTER_TO (vbase),
X vbase_decl_ptr, ASSOC_OFFSET (assoc));
X }
X CLASSTYPE_MARKED3 (type) = 1;
X CLASSTYPE_MARKED4 (type) = 1;
X}
X
Xstatic void
Xdfs_init_vbase_pointers (type)
X tree type;
X{
X tree fields = TYPE_FIELDS (type);
X tree path, this_vbase_ptr;
X int distance;
X
X CLASSTYPE_MARKED3 (type) = 0;
X
X if (fields == NULL_TREE
X || DECL_NAME (fields) == NULL_TREE
X || ! VBASE_NAME_P (DECL_NAME (fields)))
X return;
X
X distance = get_base_distance (type, TREE_TYPE (vbase_decl), 0, &path);
X while (path)
X {
X if (TREE_VIA_VIRTUAL (path))
X break;
X distance -= 1;
X path = TREE_CHAIN (path);
X }
X
X if (distance > 0)
X this_vbase_ptr = convert_pointer_to (type, CLASSTYPE_SEARCH_SLOT (TREE_VALUE (path)));
X else
X this_vbase_ptr = convert_pointer_to (type, vbase_decl_ptr);
X
X while (fields && DECL_NAME (fields)
X && VBASE_NAME_P (DECL_NAME (fields)))
X {
X tree ref = build (COMPONENT_REF, TREE_TYPE (fields),
X build_indirect_ref (this_vbase_ptr, 0), fields);
X tree init = CLASSTYPE_SEARCH_SLOT (TREE_TYPE (TREE_TYPE (fields)));
X vbase_init_result = tree_cons (TREE_TYPE (TREE_TYPE (fields)),
X build_modify_expr (ref, NOP_EXPR, init),
X vbase_init_result);
X fields = TREE_CHAIN (fields);
X }
X}
X
X/* Sometimes this needs to clear both 3 and 4. Other times,
X just 4, but optimizer should make both with equal efficiency
X (though it does not currently). */
Xstatic void
Xdfs_clear_vbase_slots (type)
X tree type;
X{
X CLASSTYPE_SEARCH_SLOT (type) = 0;
X CLASSTYPE_MARKED3 (type) = 0;
X CLASSTYPE_MARKED4 (type) = 0;
X}
X
Xtree
Xinit_vbase_pointers (type, decl_ptr)
X tree type;
X tree decl_ptr;
X{
X if (TYPE_USES_VIRTUAL_BASECLASSES (type))
X {
X int old_flag = flag_this_is_variable;
X flag_this_is_variable = 0;
X vbase_types = CLASSTYPE_VBASECLASSES (type);
X vbase_decl_ptr = decl_ptr;
X vbase_decl = build_indirect_ref (decl_ptr, 0);
X vbase_init_result = NULL_TREE;
X dfs_walk (type, dfs_find_vbases, unmarked3p);
X dfs_walk (type, dfs_init_vbase_pointers, marked3p);
X dfs_walk (type, dfs_clear_vbase_slots, marked4p);
X flag_this_is_variable = old_flag;
X return vbase_init_result;
X }
X return 0;
X}
X
X/* Build a COMPOUND_EXPR which when expanded will generate the code
X needed to initialize all the virtual function table slots of all
X the virtual baseclasses. FOR_TYPE is the type which determines the
X virtual baseclasses to use; TYPE is the type of the object to which
X the initialization applies. TRUE_EXP is the true object we are
X initializing, and DECL_PTR is the pointer to the sub-object we
X are initializing.
X
X CTOR_P is non-zero if the caller of this function is a top-level
X constructor. It is zero when called from a destructor. When
X non-zero, we can use computed offsets to store the vtables. When
X zero, we must store new vtables through virtual baseclass pointers. */
X
Xtree
Xbuild_vbase_vtables_init (for_type, type, true_exp, decl_ptr, ctor_p)
X tree for_type, type;
X tree true_exp, decl_ptr;
X int ctor_p;
X{
X if (TYPE_USES_VIRTUAL_BASECLASSES (type))
X {
X int old_flag = flag_this_is_variable;
X tree vtable_init_result = NULL_TREE;
X tree vbases = CLASSTYPE_VBASECLASSES (type);
X
X vbase_types = CLASSTYPE_VBASECLASSES (for_type);
X vbase_decl_ptr = true_exp ? build_unary_op (ADDR_EXPR, true_exp, 0) : decl_ptr;
X vbase_decl = true_exp ? true_exp : build_indirect_ref (decl_ptr, 0);
X flag_this_is_variable = 0;
X
X if (ctor_p)
X /* This is an object of type IN_TYPE, */
X dfs_walk (for_type, dfs_find_vbases, unmarked4p);
X
X /* Initialized with vtables of type TYPE. */
X while (vbases)
X {
X tree basetype = get_base_type (ASSOC_VALUE (vbases), type, 0);
X
X /* This time through, not every class's vtable
X is going to be initialized. That is, we only initialize
X the "last" vtable pointer. */
X
X assert (basetype == ASSOC_TYPE (vbases));
X
X if (basetype
X && CLASSTYPE_VSIZE (basetype)
X && TYPE_MAIN_VARIANT (basetype) == ASSOC_VALUE (vbases))
X {
X tree addr;
X tree vtbl = ASSOC_VTABLE (vbases);
X tree init = build_unary_op (ADDR_EXPR, vtbl, 0);
X TREE_USED (vtbl) = 1;
X
X if (ctor_p == 0)
X addr = convert_pointer_to (basetype, vbase_decl_ptr);
X else
X addr = CLASSTYPE_SEARCH_SLOT (basetype);
X
X if (addr)
X {
X tree ref = build_vfield_ref (build_indirect_ref (addr, 0), basetype);
X init = convert_force (TREE_TYPE (ref), init);
X vtable_init_result = tree_cons (NULL_TREE, build_modify_expr (ref, NOP_EXPR, init),
X vtable_init_result);
X }
X }
X vbases = TREE_CHAIN (vbases);
X }
X
X dfs_walk (type, dfs_clear_vbase_slots, marked4p);
X
X flag_this_is_variable = old_flag;
X if (vtable_init_result)
X return build_compound_expr (vtable_init_result);
X }
X return error_mark_node;
X}
X
Xtree
Xclear_search_slots (type)
X tree type;
X{
X dfs_walk (type, dfs_clear_search_slot, dfs_search_slot_nonempty_p);
X}
X
Xstatic void
Xdfs_get_vbase_types (type)
X tree type;
X{
X int i;
X tree these_vbase_types = CLASSTYPE_VBASECLASSES (type);
X tree basetype;
X
X if (these_vbase_types)
X {
X while (these_vbase_types)
X {
X basetype = ASSOC_TYPE (these_vbase_types);
X if (! CLASSTYPE_MARKED2 (basetype))
X {
X vbase_types = make_assoc (integer_zero_node,
X basetype,
X CLASS_ASSOC_VTABLE (basetype),
X CLASS_ASSOC_VIRTUALS (basetype),
X vbase_types);
X CLASSTYPE_MARKED2 (basetype) = 1;
X }
X these_vbase_types = TREE_CHAIN (these_vbase_types);
X }
X }
X else for (i = CLASSTYPE_N_BASECLASSES (type); i > 0; i--)
X {
X basetype = CLASSTYPE_BASECLASS (type, i);
X if (CLASSTYPE_VIA_VIRTUAL (type, i) && ! CLASSTYPE_MARKED2 (basetype))
X {
X vbase_types = make_assoc (integer_zero_node,
X basetype,
X CLASS_ASSOC_VTABLE (basetype),
X CLASS_ASSOC_VIRTUALS (basetype),
X vbase_types);
X CLASSTYPE_MARKED2 (basetype) = 1;
X }
X }
X CLASSTYPE_MARKED (type) = 1;
X}
X
X/* Some virtual baseclasses might be virtual baseclasses for
X other virtual baseclasses. We sort the virtual baseclasses
X topologically: in the list returned, the first virtual base
X classes have no virtual baseclasses themselves, and any entry
X on the list has no dependency on virtual base classes later in the
X list. */
Xtree
Xget_vbase_types (type)
X tree type;
X{
X tree ordered_vbase_types = NULL_TREE, prev, next;
X tree vbases;
X
X vbase_types = NULL_TREE;

X dfs_walk (type, dfs_get_vbase_types, unmarkedp);
X dfs_walk (type, dfs_unmark, markedp);
X
X while (vbase_types)
X {
X /* Now sort these types. This is essentially a bubble merge. */
X
X /* Farm out virtual baseclasses which have no marked ancestors. */
X for (vbases = vbase_types, prev = NULL_TREE;
X vbases; vbases = next)
X {
X next = TREE_CHAIN (vbases);
X if (! TYPE_USES_VIRTUAL_BASECLASSES (ASSOC_TYPE (vbases))
X || CLASSTYPE_MARKED2 (ASSOC_TYPE (vbases)) == 0)
X {
X if (prev)
X TREE_CHAIN (prev) = TREE_CHAIN (vbases);
X else
X vbase_types = TREE_CHAIN (vbases);
X TREE_CHAIN (vbases) = NULL_TREE;
X ordered_vbase_types = chainon (ordered_vbase_types, vbases);
X CLASSTYPE_MARKED2 (ASSOC_TYPE (vbases)) = 0;
X }
X else
X prev = vbases;
X }
X
X /* Now unmark types all of whose ancestors are now on the
X `ordered_vbase_types' list. */
X for (vbases = vbase_types; vbases; vbases = TREE_CHAIN (vbases))
X {
X /* If all our virtual baseclasses are unmarked, ok. */
X tree t = CLASSTYPE_VBASECLASSES (ASSOC_VALUE (vbases));
X while (t && (CLASSTYPE_MARKED2 (ASSOC_TYPE (t)) == 0
X || !TYPE_USES_VIRTUAL_BASECLASSES (ASSOC_TYPE (t))))
X t = TREE_CHAIN (t);
X if (t == NULL_TREE)
X CLASSTYPE_MARKED2 (ASSOC_TYPE (vbases)) = 0;
X }
X }
X
X return ordered_vbase_types;
X}
X
Xstatic void
Xdfs_record_inheritance (type)
X tree type;
X{
X int i, n_baselinks = CLASSTYPE_N_BASECLASSES (type);
X mi_boolean *derived_row = DERIVES_FROM_STAR (type);
X
X for (i = n_baselinks; i > 0; i--)
X {
X int j;
X tree baseclass = TYPE_MAIN_VARIANT (CLASSTYPE_BASECLASS (type, i));
X mi_boolean *base_row = DERIVES_FROM_STAR (baseclass);
X
X /* Don't search if there's nothing there! MI_SIZE can be
X zero as a result of parse errors. */
X if (CLASSTYPE_N_BASECLASSES (baseclass) && mi_size > 0)
X for (j = mi_size*(CLASSTYPE_CID (baseclass)-1); j >= 0; j -= mi_size)
X derived_row[j] |= base_row[j];
X DERIVES_FROM (baseclass, type) = 1;
X }
X
X CLASSTYPE_MARKED (type) = 1;
X}
X
X/* Given a _CLASSTYPE node in a multiple inheritance lattice,
X convert the lattice into a simple relation such that,
X given to CIDs, C1 and C2, one can determine if C1 <= C2
X or C2 <= C1 or C1 <> C2.
X
X Once constructed, we walk the lattice depth fisrt,
X applying various functions to elements as they are encountered.
X
X We use malloc here, in case we want to randomly free these tables. */
X
X#define SAVE_MI_MATRIX
X
Xvoid
Xbuild_mi_matrix (type)
X tree type;
X{
X cid = 0;
X
X#ifdef SAVE_MI_MATRIX
X if (CLASSTYPE_MI_MATRIX (type))
X {
X mi_size = CLASSTYPE_N_SUPERCLASSES (type) + CLASSTYPE_N_VBASECLASSES (type);
X mi_matrix = CLASSTYPE_MI_MATRIX (type);
X mi_type = type;
X dfs_walk (type, dfs_number, unnumberedp);
X return;
X }
X#endif
X
X mi_size = CLASSTYPE_N_SUPERCLASSES (type) + CLASSTYPE_N_VBASECLASSES (type);
X mi_matrix = (char *)malloc ((mi_size+1) * (mi_size+1));
X mi_type = type;
X bzero (mi_matrix, mi_size * mi_size);
X dfs_walk (type, dfs_number, unnumberedp);
X dfs_walk (type, dfs_record_inheritance, unmarkedp);
X dfs_walk (type, dfs_unmark, markedp);
X}
X
Xvoid
Xfree_mi_matrix ()
X{
X dfs_walk (mi_type, dfs_unnumber, numberedp);
X
X#ifdef SAVE_MI_MATRIX
X CLASSTYPE_MI_MATRIX (mi_type) = mi_matrix;
X#else
X free (mi_matrix);
X mi_size = 0;
X cid = 0;
X#endif
X}
X
X/* Local variables for detecting ambiguities of virtual functions
X when two or more classes are joined at a multiple inheritance
X seam. */
Xtypedef tree mi_ventry[3];
Xstatic mi_ventry *mi_vmatrix;
Xstatic int *mi_vmax;
Xstatic int mi_vrows, mi_vcols;
X#define MI_VMATRIX(ROW,COL) ((mi_vmatrix + (ROW)*mi_vcols)[COL])
X
X/* Build a table of virtual functions for a multiple-inheritance
X structure. Here, there are N base classes, and at most
X M entries per class.
X
X This function does nothing if N is 0 or 1. */
Xvoid
Xbuild_mi_virtuals (rows, cols)
X int rows, cols;
X{
X if (rows < 2)
X return;
X mi_vrows = rows;
X mi_vcols = cols;
X mi_vmatrix = (mi_ventry *)malloc ((rows+1) * cols * sizeof (mi_ventry));
X mi_vmax = (int *)malloc ((rows+1) * sizeof (int));
X
X bzero (mi_vmax, rows * sizeof (int));
X
X /* Row indicies start at 1, so adjust this. */
X mi_vmatrix -= cols;
X mi_vmax -= 1;
X}
X
X/* Comparison function for ordering virtual function table entries. */
Xstatic int
Xrank_mi_virtuals (v1, v2)
X mi_ventry *v1, *v2;
X{
X tree p1, p2;
X int i;
X
X i = (TREE_UID (DECL_ORIGINAL_NAME ((*v1)[0]))
X - TREE_UID (DECL_ORIGINAL_NAME ((*v2)[0])));
X if (i)
X return i;
X p1 = (*v1)[1];
X p2 = (*v2)[1];
X
X if (p1 == p2)
X return 0;
X
X while (p1 && p2)
X {
X i = (TREE_UID (TREE_VALUE (p1))
X - TREE_UID (TREE_VALUE (p2)));
X if (i)
X return i;
X
X if (TREE_CHAIN (p1))
X {
X if (! TREE_CHAIN (p2))
X return 1;
X p1 = TREE_CHAIN (p1);
X p2 = TREE_CHAIN (p2);
X }
X else if (TREE_CHAIN (p2))
X return -1;
X else
X {
X /* When matches of argument lists occur, pick lowest
X TREE_UID to keep searching time to a minimum on
X later passes--like hashing, only different.
X *MUST BE STABLE*. */
X if (TREE_UID ((*v2)[1]) < TREE_UID ((*v1)[1]))
X (*v1)[1] = (*v2)[1];
X else
X (*v2)[1] = (*v1)[1];
X return 0;
X }
X }
X return 0;
X}
X
X/* Install the virtuals functions got from the initializer VIRTUALS to
X the table at index ROW. */
Xvoid
Xadd_mi_virtuals (row, virtuals)
X int row;
X tree virtuals;
X{
X int col = 0;
X
X if (mi_vmatrix == 0)
X return;
X while (virtuals)
X {
X tree decl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals)), 0);
X MI_VMATRIX (row, col)[0] = decl;
X MI_VMATRIX (row, col)[1] = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (decl)));
X MI_VMATRIX (row, col)[2] = TREE_VALUE (virtuals);
X virtuals = TREE_CHAIN (virtuals);
X col += 1;
X }
X mi_vmax[row] = col;
X
X qsort (mi_vmatrix + row * mi_vcols,
X col,
X sizeof (mi_ventry),
X rank_mi_virtuals);
X}
X
X/* If joining two types results in an ambiguity in the virtual
X function table, report such here. */
Xvoid
Xreport_ambiguous_mi_virtuals (rows, type)
X int rows;
X tree type;
X{
X int *mi_vmin;
X int row1, col1, row, col;
X
X if (mi_vmatrix == 0)
X return;
X
X /* Now virtuals are all sorted, so we merge to find ambiguous cases. */
X mi_vmin = (int *)alloca ((rows+1) * sizeof (int));
X bzero (mi_vmin, rows * sizeof (int));
X
X /* adjust. */
X mi_vmin -= 1;
X
X /* For each base class with virtual functions (and this includes views
X of the virtual baseclasses from different base classes), see that
X each virtual function in that base class has a unique meet.
X
X When the column loop is finished, THIS_DECL is in fact the meet.
X If that value does not appear in the virtual function table for
X the row, install it. This happens when that virtual function comes
X from a virtual baseclass, or a non-leftmost baseclass. */
X
X for (row1 = 1; row1 < rows; row1++)
X {
X tree this_decl = 0;
X
X for (col1 = mi_vmax[row1]-1; col1 >= mi_vmin[row1]; col1--)
X {
X tree these_args = MI_VMATRIX (row1, col1)[1];
X tree this_context;
X
X this_decl = MI_VMATRIX (row1, col1)[0];
X if (this_decl == 0)
X continue;
X this_context = DECL_CONTEXT (this_decl);
X
X if (this_context != type)
X this_context = get_base_type (this_context, type, 0);
X
X for (row = row1+1; row <= rows; row++)
X for (col = mi_vmax[row]-1; col >= mi_vmin[row]; col--)
X {
X mi_ventry this_entry;
X
X if (MI_VMATRIX (row, col)[0] == 0)
X continue;
X
X this_entry[0] = this_decl;
X this_entry[1] = these_args;
X this_entry[2] = MI_VMATRIX (row1, col1)[2];
X if (rank_mi_virtuals (&this_entry,
X &MI_VMATRIX (row, col)) == 0)
X {
X /* They are equal. There are four possibilities:
X
X (1) Derived class is defining this virtual function.
X (2) Two paths to the same virtual function in the
X same base class.
X (3) A path to a virtual function declared in one base
X class, and another path to a virtual function in a
X base class of the base class.
X (4) Two paths to the same virtual function in different
X base classes.
X
X The first three cases are ok (non-ambiguous). */
X
X tree that_context, tmp;
X int this_before_that;
X
X if (type == this_context)
X /* case 1. */
X goto ok;
X that_context = get_base_type (DECL_CONTEXT (MI_VMATRIX (row, col)[0]), type, 0);
X if (that_context == this_context)
X /* case 2. */
X goto ok;
X if (that_context != NULL_TREE)
X {
X tmp = get_base_type (that_context, this_context, 0);
X this_before_that = (that_context != tmp);
X if (this_before_that == 0)
X /* case 3a. */
X goto ok;
X tmp = get_base_type (this_context, that_context, 0);
X this_before_that = (this_context == tmp);
X if (this_before_that != 0)
X /* case 3b. */
X goto ok;
X
X /* case 4. */
X error_with_decl (MI_VMATRIX (row, col)[0], "ambiguous virtual function `%s'");
X error_with_decl (this_decl, "ambiguating function `%s' (joined by type `%s')", IDENTIFIER_POINTER (current_class_name));
X }
X ok:
X MI_VMATRIX (row, col)[0] = 0;
X
X /* Let zeros propagate. */
X if (col == mi_vmax[row]-1)
X {
X int i = col;
X while (i >= mi_vmin[row]
X && MI_VMATRIX (row, i)[0] == 0)
X i--;
X mi_vmax[row] = i;
X }
X else if (col == mi_vmin[row])
X {
X int i = col;
X while (i < mi_vmax[row]
X && MI_VMATRIX (row, i)[0] == 0)
X i++;
X mi_vmin[row] = i;
X }
X }
X }
X }
X }
X free (mi_vmatrix + mi_vcols);
X mi_vmatrix = 0;
X free (mi_vmax + 1);
X mi_vmax = 0;
X}
X
X/* Subroutines of push_class_decls (). */
X
X/* Add the instance variables which this class contributed to the
X current class binding contour. When a redefinition occurs,
X if the redefinition is strictly within a single inheritance path,
X we just overwrite (in the case of a data field) or
X cons (in the case of a member function) the old declaration with
X the new. If the fields are not within a single inheritance path,
X we must cons them in either case. */
X
Xstatic void
Xdfs_pushdecls (type)
X tree type;
X{
X tree fields, *methods, *end;
X tree method_vec;
X
X for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
X {
X /* Unmark so that if we are in a constructor, and then find that
X this field was initialized by a base initializer,
X we can emit an error message. */
X if (TREE_CODE (fields) == FIELD_DECL)
X TREE_USED (fields) = 0;
X
X if (DECL_ANON_UNION_ELEM (fields))
X {
X dfs_pushdecls (TREE_TYPE (fields));
X continue;
X }
X if (TREE_CODE (fields) != TYPE_DECL)
X {
X TREE_FIELD_PUBLIC (fields) = 0;
X TREE_FIELD_PROTECTED (fields) = 0;
X TREE_FIELD_PRIVATE (fields) = 0;
X }
X
X if (DECL_NAME (fields))
X {
X tree value = IDENTIFIER_CLASS_VALUE (DECL_NAME (fields));
X if (value)
X {
X /* Possible ambiguity. If its defining type(s)
X is (are all) derived from us, no problem. */
X
X if (TREE_CODE (value) != TREE_LIST)
X {
X if (DECL_FIELD_CONTEXT (value) == type
X || DERIVES_FROM (DECL_FIELD_CONTEXT (value), type))
X value = fields;
X else
X value = tree_cons (NULL_TREE, fields,
X build_tree_list (NULL_TREE, value));
X }
X else
X {
X /* All children may derive from us, in which case
X there is no problem. Otherwise, we have to
X keep lists around of what the ambiguities might be. */
X tree values;
X int problem = 0;
X
X for (values = value; values; values = TREE_CHAIN (values))
X {
X tree sub_values = TREE_VALUE (values);
X if (TREE_CODE (sub_values) == TREE_LIST)
X {
X for (; sub_values; sub_values = TREE_CHAIN (sub_values))
X if (! DERIVES_FROM (DECL_FIELD_CONTEXT (TREE_VALUE (sub_values)), type))
X {
X value = tree_cons (NULL_TREE, TREE_VALUE (values), value);
X problem = 1;
X break;
X }
X }
X else
X {
X if (! DERIVES_FROM (DECL_FIELD_CONTEXT (sub_values), type))
X {
X value = tree_cons (NULL_TREE, values, value);
X problem = 1;
X break;
X }
X }
X }
X if (! problem) value = fields;
X }
X
X /* Mark this as a potentially ambiguous member. */
X if (TREE_CODE (value) == TREE_LIST)
X {
X /* Leaving TREE_TYPE blank is intentional.
X We cannot use `error_mark_node' (lookup_name)
X or `unknown_type_node' (all member functions use this). */
X TREE_NONLOCAL (value) = 1;
X }
X
X IDENTIFIER_CLASS_VALUE (DECL_NAME (fields)) = value;
X }
X else IDENTIFIER_CLASS_VALUE (DECL_NAME (fields)) = fields;
X }
X }
X
X method_vec = CLASSTYPE_METHOD_VEC (type);
X if (method_vec != 0)
X {
X /* Farm out constructors and destructors. */
X methods = &TREE_VEC_ELT (method_vec, 1);
X end = TREE_VEC_END (method_vec);
X
X /* This does not work for multiple inheritance yet. */
X while (methods != end)
X {
X /* This will cause lookup_name to return a pointer
X to the tree_list of possible methods of this name.
X If the order is a problem, we can nreverse them. */
X tree tmp;
X tree old = IDENTIFIER_CLASS_VALUE (DECL_ORIGINAL_NAME (*methods));
X
X if (old && TREE_CODE (old) == TREE_LIST)
X tmp = tree_cons (DECL_ORIGINAL_NAME (*methods), *methods, old);
X else
X {
X /* Only complain if we shadow something we can access. */
X if (old && (DECL_CONTEXT (old) == current_class_type
X || ! TREE_PRIVATE (old)))
X /* Should figure out visibility more accurately. */
X warning ("shadowing member `%s' with member function",
X IDENTIFIER_POINTER (DECL_ORIGINAL_NAME (*methods)));
X tmp = build_tree_list (DECL_ORIGINAL_NAME (*methods), *methods);
X }
X
X TREE_TYPE (tmp) = unknown_type_node;
X#if 0
X TREE_OVERLOADED (tmp) = DECL_OVERLOADED (*methods);
X#endif
X TREE_NONLOCAL (tmp) = 1;
X IDENTIFIER_CLASS_VALUE (DECL_ORIGINAL_NAME (*methods)) = tmp;
X
X tmp = *methods;
X while (tmp != 0)
X {
X TREE_FIELD_PUBLIC (tmp) = 0;
X TREE_FIELD_PROTECTED (tmp) = 0;
X TREE_FIELD_PRIVATE (tmp) = 0;
X tmp = TREE_CHAIN (tmp);
X }
X
X methods++;
X }
X }
X CLASSTYPE_MARKED (type) = 1;
X}
X
X/* Consolidate unique (by name) member functions. */
Xstatic void
Xdfs_compress_decls (type)
X tree type;
X{
X tree method_vec = CLASSTYPE_METHOD_VEC (type);
X
X if (method_vec != 0)
X {
X /* Farm out constructors and destructors. */
X tree *methods = &TREE_VEC_ELT (method_vec, 1);
X tree *end = TREE_VEC_END (method_vec);
X
X for (; methods != end; methods++)
X {
X tree tmp = IDENTIFIER_CLASS_VALUE (DECL_ORIGINAL_NAME (*methods));
X
X /* This was replaced in scope by somebody else. Just leave it
X alone. */
X if (TREE_CODE (tmp) != TREE_LIST)
X continue;
X
X if (TREE_CHAIN (tmp) == NULL_TREE
X && TREE_VALUE (tmp)
X && TREE_CHAIN (TREE_VALUE (tmp)) == NULL_TREE)
X {
X IDENTIFIER_CLASS_VALUE (DECL_ORIGINAL_NAME (*methods))
X = TREE_VALUE (tmp);
X }
X }
X }
X CLASSTYPE_MARKED (type) = 0;
X}
X
X/* When entering the scope of a class, we cache all of the
X fields that that class provides within its inheritance
X lattice. Where ambiguities result, we mark them
X with `error_mark_node' so that if they are encountered
X without explicit qualification, we can emit an error
X message. */
Xvoid
Xpush_class_decls (type)
X tree type;
X{
X struct obstack *ambient_obstack = current_obstack;
X
X#if 0
X tree tags = CLASSTYPE_TAGS (type);
X
X while (tags)
X {
X tree code_type_node;
X tree tag;
X
X switch (TREE_CODE (TREE_VALUE (tags)))
X {
X case ENUMERAL_TYPE:
X code_type_node = enum_type_node;
X break;
X case RECORD_TYPE:
X code_type_node = record_type_node;
X break;
X case CLASS_TYPE:
X code_type_node = class_type_node;
X break;
X case UNION_TYPE:
X code_type_node = union_type_node;
X break;
X default:
X assert (0);
X }
X tag = xref_tag (code_type_node, TREE_PURPOSE (tags),
X CLASSTYPE_BASECLASS (TREE_VALUE (tags), 1));
X pushdecl (build_decl (TYPE_DECL, TREE_PURPOSE (tags), TREE_VALUE (tags)));
X }
X#endif
X
X current_obstack = &bridge_obstack;
X search_stack = push_search_level (search_stack, &bridge_obstack);
X
X /* Push class fields into CLASS_VALUE scope, and mark. */
X dfs_walk (type, dfs_pushdecls, unmarkedp);
X
X /* Compress fields which have only a single entry
X by a given name, and unmark. */
X dfs_walk (type, dfs_compress_decls, markedp);
X current_obstack = ambient_obstack;
X}
X
Xstatic void
Xdfs_popdecls (type)
X tree type;
X{
X tree fields = TYPE_FIELDS (type);
X tree method_vec = CLASSTYPE_METHOD_VEC (type);
X
X while (fields)
X {
X if (DECL_ANON_UNION_ELEM (fields))
X {
X dfs_popdecls (TREE_TYPE (fields));
X }
X else if (DECL_NAME (fields))
X IDENTIFIER_CLASS_VALUE (DECL_NAME (fields)) = NULL_TREE;
X fields = TREE_CHAIN (fields);
X }
X if (method_vec != 0)
X {
X tree *methods = &TREE_VEC_ELT (method_vec, 0);
X tree *end = TREE_VEC_END (method_vec);
X
X if (*methods == 0)
X methods += 1;
X
X for (; methods != end; methods++)
X IDENTIFIER_CLASS_VALUE (DECL_ORIGINAL_NAME (*methods)) = NULL_TREE;
X }
X
X CLASSTYPE_MARKED (type) = 1;
X}
X
Xvoid
Xpop_class_decls (type)
X tree type;
X{
X /* Clear out the IDENTIFIER_CLASS_VALUE which this
X class may have occupied, and mark. */
X dfs_walk (type, dfs_popdecls, unmarkedp);
X
X /* Unmark. */
X dfs_walk (type, dfs_unmark, markedp);
X search_stack = pop_search_level (search_stack);
X}
X
X/* Given a base type PARENT, and a derived type TYPE, build
X a name which distinguishes exactly the PARENT member of TYPE's type.
X
X FORMAT is a string which controls how sprintf formats the name
X we have generated.
X
X For example, given
X
X class A; class B; class C : A, B;
X
X it is possible to distinguish "A" from "C's A". And given
X
X class L;
X class A : L; class B : L; class C : A, B;
X
X it is possible to distinguish "L" from "A's L", and also from
X "C's L from A". */
Xtree
Xbuild_type_pathname (format, parent, type)
X char *format;
X tree parent, type;
X{
X extern struct obstack temporary_obstack;
X char *first, *base, *name;
X int i;
X tree id;
X
X parent = TYPE_MAIN_VARIANT (parent);
X
X /* Remember where to cut the obstack to. */
X first = obstack_base (&temporary_obstack);
X
X /* Put on TYPE+PARENT. */
X obstack_grow (&temporary_obstack,
X TYPE_NAME_STRING (type), TYPE_NAME_LENGTH (type));
X obstack_1grow (&temporary_obstack, JOINER);
X obstack_grow0 (&temporary_obstack,
X TYPE_NAME_STRING (parent), TYPE_NAME_LENGTH (parent));
X i = obstack_object_size (&temporary_obstack);
X base = obstack_base (&temporary_obstack);
X obstack_finish (&temporary_obstack);
X
X /* Put on FORMAT+TYPE+PARENT. */
X obstack_blank (&temporary_obstack, strlen (format) + i + 1);
X name = obstack_base (&temporary_obstack);
X sprintf (name, format, base);
X id = get_identifier (name);
X obstack_free (&temporary_obstack, first);
X
X return id;
X}
X
Xstatic int
Xbfs_unmark_finished_struct (type, i)
X tree type;

X int i;
X{
X type = i == 0 ? type : CLASSTYPE_BASECLASS (type, i);
X if (CLASSTYPE_MARKED4 (type))
X {
X tree assoc, decl, context;
X
X if (type == current_class_type)
X assoc = CLASSTYPE_ASSOC (type);
X else if (TREE_VIA_VIRTUAL (type))
X assoc = value_member (TYPE_MAIN_VARIANT (type), CLASSTYPE_VBASECLASSES (current_class_type));
X else
X assoc = assoc_value (TYPE_MAIN_VARIANT (type), current_class_type);
X decl = ASSOC_VTABLE (assoc);
X context = DECL_CONTEXT (decl);
X DECL_CONTEXT (decl) = 0;
X if (write_virtuals >= 0
X && DECL_INITIAL (decl) != ASSOC_VIRTUALS (assoc))
X DECL_INITIAL (decl) = build_nt (CONSTRUCTOR, NULL_TREE,
X ASSOC_VIRTUALS (assoc));
X finish_decl (decl, DECL_INITIAL (decl), NULL_TREE);
X DECL_CONTEXT (decl) = context;
X }
X CLASSTYPE_MARKED3 (type) = 0;
X CLASSTYPE_MARKED4 (type) = 0;
X return 0;
X}
X
Xvoid
Xunmark_finished_struct (type)
X tree type;
X{
X bfs_unmark_finished_struct (type, 0);
X breadth_first_search (type, bfs_unmark_finished_struct, bfs_marked3p);
X}
X
Xvoid
Xprint_search_statistics ()
X{
X#ifdef GATHER_STATISTICS
X if (flag_memoize_lookups)
X {
X fprintf (stderr, "%d memoized contexts saved\n",
X n_contexts_saved);
X fprintf (stderr, "%d local tree nodes made\n", my_tree_node_counter);
X fprintf (stderr, "%d local hash nodes made\n", my_memoized_entry_counter);
X fprintf (stderr, "fields statistics:\n");
X fprintf (stderr, " memoized finds = %d; rejects = %d; (searches = %d)\n",
X memoized_fast_finds[0], memoized_fast_rejects[0],
X memoized_fields_searched[0]);
X fprintf (stderr, " memoized_adds = %d\n", memoized_adds[0]);
X fprintf (stderr, "fnfields statistics:\n");
X fprintf (stderr, " memoized finds = %d; rejects = %d; (searches = %d)\n",
X memoized_fast_finds[1], memoized_fast_rejects[1],
X memoized_fields_searched[1]);
X fprintf (stderr, " memoized_adds = %d\n", memoized_adds[1]);
X }
X fprintf (stderr, "%d fields searched in %d[%d] calls to lookup_field[_1]\n",
X n_fields_searched, n_calls_lookup_field, n_calls_lookup_field_1);
X fprintf (stderr, "%d fnfields searched in %d calls to lookup_fnfields\n",
X n_outer_fields_searched, n_calls_lookup_fnfields);
X fprintf (stderr, "%d calls to get_base_type\n", n_calls_get_base_type);
X#else
X fprintf (stderr, "no search statistics\n");
X#endif
X}
X
Xvoid
Xinit_search_processing ()
X{
X obstack_init (&search_obstack);
X obstack_init (&type_obstack);
X obstack_init (&type_obstack_entries);
X obstack_init (&bridge_obstack);
X
X /* This gives us room to build our chains of basetypes,
X whether or not we decide to memoize them. */
X type_stack = push_type_level (0, &type_obstack);
X _vptr_name = get_identifier ("_vptr");
X}
X
Xtree
Xget_wrapper (type)
X tree type;
X{
X tree wrap_type;
X char *name;
X assert (IS_AGGR_TYPE (type));
X wrap_type = TYPE_WRAP_TYPE (type);
X name = (char *)alloca (TYPE_NAME_LENGTH (wrap_type)
X + strlen (WRAPPER_NAME_FORMAT));
X sprintf (name, WRAPPER_NAME_FORMAT, TYPE_NAME_STRING (wrap_type));
X return lookup_fnfields (CLASSTYPE_AS_LIST (wrap_type),
X get_identifier (name), 0);
X}
X
Xvoid
Xreinit_search_statistics ()
X{
X my_memoized_entry_counter = 0;
X memoized_fast_finds[0] = 0;
X memoized_fast_finds[1] = 0;
X memoized_adds[0] = 0;
X memoized_adds[1] = 0;
X memoized_fast_rejects[0] = 0;
X memoized_fast_rejects[1] = 0;
X memoized_fields_searched[0] = 0;
X memoized_fields_searched[1] = 0;
X n_fields_searched = 0;
X n_calls_lookup_field = 0, n_calls_lookup_field_1 = 0;
X n_calls_lookup_fnfields = 0, n_calls_lookup_fnfields_1 = 0;
X n_calls_get_base_type = 0;
X n_outer_fields_searched = 0;
X n_contexts_saved = 0;
X}
!EOF
exit 0