From 5ab5688dee96d4f92cc760c528a39220fe6b2713 Mon Sep 17 00:00:00 2001 From: "William F. Jolitz" Date: Wed, 8 May 1991 09:47:11 -0800 Subject: [PATCH] 386BSD 0.1 development Work on file usr/src/usr.bin/gcc/cc1/c-decl.c Work on file usr/src/usr.bin/gcc/cc1/c-typeck.c Work on file usr/src/usr.bin/gcc/cc1/c-parse.y Work on file usr/src/usr.bin/gcc/cc1/toplev.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- usr/src/usr.bin/gcc/cc1/c-decl.c | 4060 ++++++++++++++++++++++++++++ usr/src/usr.bin/gcc/cc1/c-parse.y | 2884 ++++++++++++++++++++ usr/src/usr.bin/gcc/cc1/c-typeck.c | 3812 ++++++++++++++++++++++++++ usr/src/usr.bin/gcc/cc1/toplev.c | 2123 +++++++++++++++ 4 files changed, 12879 insertions(+) create mode 100644 usr/src/usr.bin/gcc/cc1/c-decl.c create mode 100644 usr/src/usr.bin/gcc/cc1/c-parse.y create mode 100644 usr/src/usr.bin/gcc/cc1/c-typeck.c create mode 100644 usr/src/usr.bin/gcc/cc1/toplev.c diff --git a/usr/src/usr.bin/gcc/cc1/c-decl.c b/usr/src/usr.bin/gcc/cc1/c-decl.c new file mode 100644 index 0000000000..b4bec6e473 --- /dev/null +++ b/usr/src/usr.bin/gcc/cc1/c-decl.c @@ -0,0 +1,4060 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)c-decl.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Process declarations and variables for C compiler. + Copyright (C) 1988 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* Process declarations and symbol lookup for C front end. + Also constructs types; the standard scalar types at initialization, + and structure, union, array and enum types when they are declared. */ + +/* ??? not all decl nodes are given the most useful possible + line numbers. For example, the CONST_DECLs for enum values. */ + +#include "config.h" +#include "tree.h" +#include "flags.h" +#include "c-tree.h" +#include "c-parse.h" + +#include + +/* In grokdeclarator, distinguish syntactic contexts of declarators. */ +enum decl_context +{ NORMAL, /* Ordinary declaration */ + FUNCDEF, /* Function definition */ + PARM, /* Declaration of parm before function body */ + FIELD, /* Declaration inside struct or union */ + TYPENAME}; /* Typename (inside cast or sizeof) */ + +#define NULL 0 +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif + +#ifndef SHORT_TYPE_SIZE +#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2)) +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_TYPE_SIZE +#define LONG_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef LONG_LONG_TYPE_SIZE +#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef FLOAT_TYPE_SIZE +#define FLOAT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef DOUBLE_TYPE_SIZE +#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +/* a node which has tree code ERROR_MARK, and whose type is itself. + All erroneous expressions are replaced with this node. All functions + that accept nodes as arguments should avoid generating error messages + if this node is one of the arguments, since it is undesirable to get + multiple error messages from one error in the input. */ + +tree error_mark_node; + +/* INTEGER_TYPE and REAL_TYPE nodes for the standard data types */ + +tree short_integer_type_node; +tree integer_type_node; +tree long_integer_type_node; +tree long_long_integer_type_node; + +tree short_unsigned_type_node; +tree unsigned_type_node; +tree long_unsigned_type_node; +tree long_long_unsigned_type_node; + +tree unsigned_char_type_node; +tree signed_char_type_node; +tree char_type_node; + +tree float_type_node; +tree double_type_node; +tree long_double_type_node; + +/* a VOID_TYPE node. */ + +tree void_type_node; + +/* A node for type `void *'. */ + +tree ptr_type_node; + +/* A node for type `char *'. */ + +tree string_type_node; + +/* Type `char[256]' or something like it. + Used when an array of char is needed and the size is irrelevant. */ + +tree char_array_type_node; + +/* Type `int[256]' or something like it. + Used when an array of int needed and the size is irrelevant. */ + +tree int_array_type_node; + +/* type `int ()' -- used for implicit declaration of functions. */ + +tree default_function_type; + +/* function types `double (double)' and `double (double, double)', etc. */ + +tree double_ftype_double, double_ftype_double_double; +tree int_ftype_int, long_ftype_long; + +/* Function type `void (void *, void *, int)' and similar ones */ + +tree void_ftype_ptr_ptr_int, int_ftype_ptr_ptr_int, void_ftype_ptr_int_int; + +/* Two expressions that are constants with value zero. + The first is of type `int', the second of type `void *'. */ + +tree integer_zero_node; +tree null_pointer_node; + +/* A node for the integer constant 1. */ + +tree integer_one_node; + +/* An identifier whose name is . This is used as the "name" + of the RESULT_DECLs for values of functions. */ + +tree value_identifier; + +/* While defining an enum type, this is 1 plus the last enumerator + constant value. */ + +static tree enum_next_value; + +/* Parsing a function declarator leaves a list of parameter names + or a chain or parameter decls here. */ + +static tree last_function_parms; + +/* Parsing a function declarator leaves here a chain of structure + and enum types declared in the parmlist. */ + +static tree last_function_parm_tags; + +/* After parsing the declarator that starts a function definition, + `start_function' puts here the list of parameter names or chain of decls. + `store_parm_decls' finds it here. */ + +static tree current_function_parms; + +/* Similar, for last_function_parm_tags. */ +static tree current_function_parm_tags; + +/* A list (chain of TREE_LIST nodes) of all LABEL_STMTs in the function + that have names. Here so we can clear out their names' definitions + at the end of the function. */ + +static tree named_labels; + +/* The FUNCTION_DECL for the function currently being compiled, + or 0 if between functions. */ +tree current_function_decl; + +/* Set to 0 at beginning of a function definition, set to 1 if + a return statement that specifies a return value is seen. */ + +int current_function_returns_value; + +/* Set to 0 at beginning of a function definition, set to 1 if + a return statement with no argument is seen. */ + +int current_function_returns_null; + +/* Set to nonzero by `grokdeclarator' for a function + whose return type is defaulted, if warnings for this are desired. */ + +static int warn_about_return_type; + +/* Nonzero when starting a function delcared `extern inline'. */ + +static int current_extern_inline; + +/* For each binding contour we allocate a binding_level structure + * which records the names defined in that contour. + * Contours include: + * 0) the global one + * 1) one for each function definition, + * where internal declarations of the parameters appear. + * 2) one for each compound statement, + * to record its declarations. + * + * The current meaning of a name can be found by searching the levels from + * the current one out to the global one. + */ + +/* Note that the information in the `names' component of the global contour + is duplicated in the IDENTIFIER_GLOBAL_VALUEs of all identifiers. */ + +struct binding_level + { + /* A chain of _DECL nodes for all variables, constants, functions, + and typedef types. These are in the reverse of the order supplied. + */ + tree names; + + /* A list of structure, union and enum definitions, + * for looking up tag names. + * It is a chain of TREE_LIST nodes, each of whose TREE_PURPOSE is a name, + * or NULL_TREE; and whose TREE_VALUE is a RECORD_TYPE, UNION_TYPE, + * or ENUMERAL_TYPE node. + */ + tree tags; + + /* For each level, a list of shadowed outer-level local definitions + to be restored when this level is popped. + Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and + whose TREE_VALUE is its old definition (a kind of ..._DECL node). */ + tree shadowed; + + /* For each level (except not the global one), + a chain of LET_STMT nodes for all the levels + that were entered and exited one level down. */ + tree blocks; + + /* The binding level which this one is contained in (inherits from). */ + struct binding_level *level_chain; + + /* Nonzero for the level that holds the parameters of a function. */ + char parm_flag; + + /* Nonzero if this level "doesn't exist" for tags. */ + char tag_transparent; + + /* Nonzero means make a LET_STMT for this level regardless of all else. */ + char keep; + + /* Nonzero means make a LET_STMT if this level has any subblocks. */ + char keep_if_subblocks; + + /* Number of decls in `names' that have incomplete + structure or union types. */ + int n_incomplete; + }; + +#define NULL_BINDING_LEVEL (struct binding_level *) NULL + +/* The binding level currently in effect. */ + +static struct binding_level *current_binding_level; + +/* A chain of binding_level structures awaiting reuse. */ + +static struct binding_level *free_binding_level; + +/* The outermost binding level, for names of file scope. + This is created when the compiler is started and exists + through the entire run. */ + +static struct binding_level *global_binding_level; + +/* Binding level structures are initialized by copying this one. */ + +static struct binding_level clear_binding_level + = {NULL, NULL, NULL, NULL, NULL, 0, 0, 0}; + +/* Nonzero means unconditionally make a LET_STMT for the next level pushed. */ + +static int keep_next_level_flag; + +/* Nonzero means make a LET_STMT for the next level pushed + if it has subblocks. */ + +static int keep_next_if_subblocks; + +/* Forward declarations. */ + +static tree grokparms (), grokdeclarator (); +tree pushdecl (); +static void builtin_function (); + +static tree lookup_tag (); +static tree lookup_tag_reverse (); +static tree lookup_name_current_level (); +static char *redeclaration_error_message (); +static void layout_array_type (); + +/* C-specific option variables. */ + +/* Nonzero means allow type mismatches in conditional expressions; + just make their values `void'. */ + +int flag_cond_mismatch; + +/* Nonzero means don't recognize the keyword `asm'. */ + +int flag_no_asm; + +/* Nonzero means do some things the same way PCC does. */ + +int flag_traditional; + +/* Nonzero means warn about implicit declarations. */ + +int warn_implicit; + +/* Nonzero means warn about function definitions that default the return type + or that use a null return and have a return-type other than void. */ + +int warn_return_type; + +/* Nonzero means give string constants the type `const char *' + to get extra warnings from them. These warnings will be too numerous + to be useful, except in thoroughly ANSIfied programs. */ + +int warn_write_strings; + +/* Nonzero means warn about pointer casts that can drop a type qualifier + from the pointer target type. */ + +int warn_cast_qual; + +/* Nonzero means warn about sizeof(function) or addition/subtraction + of function pointers. */ + +int warn_pointer_arith; + +/* Nonzero means warn for all old-style non-prototype function decls. */ + +int warn_strict_prototypes; + +/* Nonzero means `$' can be in an identifier. + See cccp.c for reasons why this breaks some obscure ANSI C programs. */ + +#ifndef DOLLARS_IN_IDENTIFIERS +#define DOLLARS_IN_IDENTIFIERS 0 +#endif +int dollars_in_ident = DOLLARS_IN_IDENTIFIERS; + +char *language_string = "GNU C"; + +/* Decode the string P as a language-specific option. + Return 1 if it is recognized (and handle it); + return 0 if not recognized. */ + +int +lang_decode_option (p) + char *p; +{ + if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional")) + flag_traditional = 1, dollars_in_ident = 1, flag_writable_strings = 1; + else if (!strcmp (p, "-fsigned-char")) + flag_signed_char = 1; + else if (!strcmp (p, "-funsigned-char")) + flag_signed_char = 0; + else if (!strcmp (p, "-fno-signed-char")) + flag_signed_char = 0; + else if (!strcmp (p, "-fno-unsigned-char")) + flag_signed_char = 1; + else if (!strcmp (p, "-fshort-enums")) + flag_short_enums = 1; + else if (!strcmp (p, "-fno-short-enums")) + flag_short_enums = 0; + else if (!strcmp (p, "-fcond-mismatch")) + flag_cond_mismatch = 1; + else if (!strcmp (p, "-fno-cond-mismatch")) + flag_cond_mismatch = 0; + else if (!strcmp (p, "-fasm")) + flag_no_asm = 0; + else if (!strcmp (p, "-fno-asm")) + flag_no_asm = 1; + else if (!strcmp (p, "-ansi")) + flag_no_asm = 1, dollars_in_ident = 0; + else if (!strcmp (p, "-Wimplicit")) + warn_implicit = 1; + else if (!strcmp (p, "-Wreturn-type")) + warn_return_type = 1; + else if (!strcmp (p, "-Wwrite-strings")) + warn_write_strings = 1; + else if (!strcmp (p, "-Wcast-qual")) + warn_cast_qual = 1; + else if (!strcmp (p, "-Wpointer-arith")) + warn_pointer_arith = 1; + else if (!strcmp (p, "-Wstrict-prototypes")) + warn_strict_prototypes = 1; + else if (!strcmp (p, "-Wcomment")) + ; /* cpp handles this one. */ + else if (!strcmp (p, "-Wcomments")) + ; /* cpp handles this one. */ + else if (!strcmp (p, "-Wtrigraphs")) + ; /* cpp handles this one. */ + else if (!strcmp (p, "-Wall")) + { + extra_warnings = 1; + warn_implicit = 1; + warn_return_type = 1; + warn_unused = 1; + warn_switch = 1; + } + else + return 0; + + return 1; +} + +void +print_lang_identifier (file, node, indent) + FILE *file; + tree node; + int indent; +{ + print_node (file, "global", IDENTIFIER_GLOBAL_VALUE (node), indent + 4); + print_node (file, "local", IDENTIFIER_LOCAL_VALUE (node), indent + 4); + print_node (file, "label", IDENTIFIER_LABEL_VALUE (node), indent + 4); + print_node (file, "implicit", IDENTIFIER_IMPLICIT_DECL (node), indent + 4); + print_node (file, "error locus", IDENTIFIER_ERROR_LOCUS (node), indent + 4); +} + +/* Create a new `struct binding_level'. */ + +static +struct binding_level * +make_binding_level () +{ + /* NOSTRICT */ + return (struct binding_level *) xmalloc (sizeof (struct binding_level)); +} + +/* Nonzero if we are currently in the global binding level. */ + +int +global_bindings_p () +{ + return current_binding_level == global_binding_level; +} + +void +keep_next_level () +{ + keep_next_level_flag = 1; +} + +/* Nonzero if the current level needs to have a LET_STMT made. */ + +int +kept_level_p () +{ + return ((current_binding_level->keep_if_subblocks + && current_binding_level->blocks != 0) + || current_binding_level->keep + || current_binding_level->names != 0); +} + +/* Identify this binding level as a level of parameters. */ + +void +declare_parm_level () +{ + current_binding_level->parm_flag = 1; +} + +/* Nonzero if currently making parm declarations. */ + +in_parm_level_p () +{ + return current_binding_level->parm_flag; +} + +/* Enter a new binding level. + If TAG_TRANSPARENT is nonzero, do so only for the name space of variables, + not for that of tags. */ + +void +pushlevel (tag_transparent) + int tag_transparent; +{ + register struct binding_level *newlevel = NULL_BINDING_LEVEL; + + /* If this is the top level of a function, + just make sure that NAMED_LABELS is 0. + They should have been set to 0 at the end of the previous function. */ + + if (current_binding_level == global_binding_level) + { + if (named_labels) + abort (); + } + + /* Reuse or create a struct for this binding level. */ + + if (free_binding_level) + { + newlevel = free_binding_level; + free_binding_level = free_binding_level->level_chain; + } + else + { + newlevel = make_binding_level (); + } + + /* Add this level to the front of the chain (stack) of levels that + are active. */ + + *newlevel = clear_binding_level; + newlevel->level_chain = current_binding_level; + current_binding_level = newlevel; + newlevel->tag_transparent = tag_transparent; + newlevel->keep = keep_next_level_flag; + keep_next_level_flag = 0; + newlevel->keep_if_subblocks = keep_next_if_subblocks; + keep_next_if_subblocks = 0; +} + +/* Exit a binding level. + Pop the level off, and restore the state of the identifier-decl mappings + that were in effect when this level was entered. + + If KEEP is nonzero, this level had explicit declarations, so + and create a "block" (a LET_STMT node) for the level + to record its declarations and subblocks for symbol table output. + + If FUNCTIONBODY is nonzero, this level is the body of a function, + so create a block as if KEEP were set and also clear out all + label names. + + If REVERSE is nonzero, reverse the order of decls before putting + them into the LET_STMT. */ + +tree +poplevel (keep, reverse, functionbody) + int keep; + int reverse; + int functionbody; +{ + register tree link; + /* The chain of decls was accumulated in reverse order. + Put it into forward order, just for cleanliness. */ + tree decls; + tree tags = current_binding_level->tags; + tree subblocks = current_binding_level->blocks; + tree block = 0; + + keep |= current_binding_level->keep; + + /* This warning is turned off because it causes warnings for + declarations like `extern struct foo *x'. */ +#if 0 + /* Warn about incomplete structure types in this level. */ + for (link = tags; link; link = TREE_CHAIN (link)) + if (TYPE_SIZE (TREE_VALUE (link)) == 0) + { + tree type = TREE_VALUE (link); + char *errmsg; + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + errmsg = "`struct %s' incomplete in scope ending here"; + break; + case UNION_TYPE: + errmsg = "`union %s' incomplete in scope ending here"; + break; + case ENUMERAL_TYPE: + errmsg = "`enum %s' incomplete in scope ending here"; + break; + } + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type))); + else + /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */ + error (errmsg, IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); + } +#endif /* 0 */ + + /* Get the decls in the order they were written. + Usually current_binding_level->names is in reverse order. + But parameter decls were previously put in forward order. */ + + if (reverse) + current_binding_level->names + = decls = nreverse (current_binding_level->names); + else + decls = current_binding_level->names; + + /* If there were any declarations or structure tags in that level, + or if this level is a function body, + create a LET_STMT to record them for the life of this function. */ + + if (keep || functionbody + || (current_binding_level->keep_if_subblocks && subblocks != 0)) + block = build_let (0, 0, keep ? decls : 0, + subblocks, 0, keep ? tags : 0); + + /* In each subblock, record that this is its superior. */ + + for (link = subblocks; link; link = TREE_CHAIN (link)) + STMT_SUPERCONTEXT (link) = block; + + /* Clear out the meanings of the local variables of this level; + also record in each decl which block it belongs to. */ + + for (link = decls; link; link = TREE_CHAIN (link)) + { + if (DECL_NAME (link) != 0) + { + /* If the ident. was used via a local extern decl, + don't forget that fact. */ + if (TREE_USED (link) && TREE_EXTERNAL (link)) + TREE_USED (DECL_NAME (link)) = 1; + IDENTIFIER_LOCAL_VALUE (DECL_NAME (link)) = 0; + } + DECL_CONTEXT (link) = block; + } + + /* Restore all name-meanings of the outer levels + that were shadowed by this level. */ + + for (link = current_binding_level->shadowed; link; link = TREE_CHAIN (link)) + IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link); + + /* If the level being exited is the top level of a function, + check over all the labels. */ + + if (functionbody) + { + /* Clear out the definitions of all label names, + since their scopes end here. */ + + for (link = named_labels; link; link = TREE_CHAIN (link)) + { + if (DECL_SOURCE_LINE (TREE_VALUE (link)) == 0) + { + error ("label `%s' used somewhere above but not defined", + IDENTIFIER_POINTER (DECL_NAME (TREE_VALUE (link)))); + /* Avoid crashing later. */ + define_label (input_filename, 1, DECL_NAME (TREE_VALUE (link))); + } + else if (warn_unused && !TREE_USED (TREE_VALUE (link))) + warning_with_decl (TREE_VALUE (link), + "label `%s' defined but not used"); + IDENTIFIER_LABEL_VALUE (DECL_NAME (TREE_VALUE (link))) = 0; + } + + named_labels = 0; + } + + /* Pop the current level, and free the structure for reuse. */ + + { + register struct binding_level *level = current_binding_level; + current_binding_level = current_binding_level->level_chain; + + level->level_chain = free_binding_level; + free_binding_level = level; + } + + if (functionbody) + { + DECL_INITIAL (current_function_decl) = block; + /* If this is the top level block of a function, + the vars are the function's parameters. + Don't leave them in the LET_STMT because they are + found in the FUNCTION_DECL instead. */ + STMT_VARS (block) = 0; + } + else if (block) + current_binding_level->blocks + = chainon (current_binding_level->blocks, block); + /* If we did not make a block for the level just exited, + any blocks made for inner levels + (since they cannot be recorded as subblocks in that level) + must be carried forward so they will later become subblocks + of something else. */ + else if (subblocks) + current_binding_level->blocks + = chainon (current_binding_level->blocks, subblocks); + + if (block) + TREE_USED (block) = 1; + return block; +} + +/* Push a definition of struct, union or enum tag "name". + "type" should be the type node. + We assume that the tag "name" is not already defined. + + Note that the definition may really be just a forward reference. + In that case, the TYPE_SIZE will be zero. */ + +void +pushtag (name, type) + tree name, type; +{ + register struct binding_level *b = current_binding_level; + while (b->tag_transparent) b = b->level_chain; + + if (name) + { + /* Record the identifier as the type's name if it has none. */ + + if (TYPE_NAME (type) == 0) + TYPE_NAME (type) = name; + + if (b == global_binding_level) + b->tags = perm_tree_cons (name, type, b->tags); + else + b->tags = saveable_tree_cons (name, type, b->tags); + } +} + +/* Handle when a new declaration NEWDECL + has the same name as an old one OLDDECL + in the same binding contour. + Prints an error message if appropriate. + + If safely possible, alter OLDDECL to look like NEWDECL, and return 1. + Otherwise, return 0. */ + +static int +duplicate_decls (newdecl, olddecl) + register tree newdecl, olddecl; +{ + int types_match = comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl)); + + if (TREE_CODE (TREE_TYPE (newdecl)) == ERROR_MARK + || TREE_CODE (TREE_TYPE (olddecl)) == ERROR_MARK) + types_match = 0; + + /* If this decl has linkage, and the old one does too, maybe no error. */ + if (TREE_CODE (olddecl) != TREE_CODE (newdecl)) + { + error_with_decl (newdecl, "`%s' redeclared as different kind of symbol"); + error_with_decl (olddecl, "previous declaration of `%s'"); + } + else + { + if (flag_traditional && TREE_CODE (newdecl) == FUNCTION_DECL + && IDENTIFIER_IMPLICIT_DECL (DECL_NAME (newdecl)) == olddecl + && DECL_INITIAL (olddecl) == 0) + /* If -traditional, avoid error for redeclaring fcn + after implicit decl. */ + ; + else if (TREE_CODE (olddecl) == FUNCTION_DECL + && DECL_FUNCTION_CODE (olddecl) != NOT_BUILT_IN) + { + if (!types_match) + error_with_decl (newdecl, "conflicting types for built-in function `%s'"); + else if (extra_warnings) + warning_with_decl (newdecl, "built-in function `%s' redeclared"); + } + else if (!types_match) + { + error_with_decl (newdecl, "conflicting types for `%s'"); + /* Check for function type mismatch + involving an empty arglist vs a nonempty one. */ + if (TREE_CODE (olddecl) == FUNCTION_DECL + && comptypes (TREE_TYPE (TREE_TYPE (olddecl)), + TREE_TYPE (TREE_TYPE (newdecl))) + && ((TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == 0 + && DECL_INITIAL (olddecl) == 0) + || + (TYPE_ARG_TYPES (TREE_TYPE (newdecl)) == 0 + && DECL_INITIAL (newdecl) == 0))) + { + /* Classify the problem further. */ + register tree t = TYPE_ARG_TYPES (TREE_TYPE (olddecl)); + if (t == 0) + t = TYPE_ARG_TYPES (TREE_TYPE (newdecl)); + for (; t; t = TREE_CHAIN (t)) + { + register tree type = TREE_VALUE (t); + + if (TREE_CHAIN (t) == 0 && type != void_type_node) + { + error ("A parameter list with an ellipsis can't match"); + error ("an empty parameter name list declaration."); + break; + } + + if (type == float_type_node + || (TREE_CODE (type) == INTEGER_TYPE + && (TYPE_PRECISION (type) + < TYPE_PRECISION (integer_type_node)))) + { + error ("An argument type that has a default promotion"); + error ("can't match an empty parameter name list declaration."); + break; + } + } + } + error_with_decl (olddecl, "previous declaration of `%s'"); + } + else + { + char *errmsg = redeclaration_error_message (newdecl, olddecl); + if (errmsg) + { + error_with_decl (newdecl, errmsg); + error_with_decl (olddecl, + "here is the previous declaration of `%s'"); + } + else if (TREE_CODE (olddecl) == FUNCTION_DECL + && DECL_INITIAL (olddecl) != 0 + && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == 0 + && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0) + { + /* Prototype decl follows defn w/o prototype. */ + warning_with_decl (newdecl, "prototype for `%s'"); + warning_with_decl (olddecl, + "follows non-prototype definition here"); + } + + /* These bits are logically part of the type. */ + if (pedantic + && (TREE_READONLY (newdecl) != TREE_READONLY (olddecl) + || TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl))) + warning_with_decl (newdecl, "type qualifiers for `%s' conflict with previous decl"); + } + } + + if (TREE_CODE (olddecl) == TREE_CODE (newdecl)) + { + int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL + && DECL_INITIAL (newdecl) != 0); + + /* Copy all the DECL_... slots specified in the new decl + except for any that we copy here from the old type. */ + + if (types_match) + { + tree oldtype = TREE_TYPE (olddecl); + /* Merge the data types specified in the two decls. */ + TREE_TYPE (newdecl) + = TREE_TYPE (olddecl) + = commontype (TREE_TYPE (newdecl), TREE_TYPE (olddecl)); + + /* Lay the type out, unless already done. */ + if (oldtype != TREE_TYPE (newdecl)) + { + if (TREE_TYPE (newdecl) != error_mark_node) + layout_type (TREE_TYPE (newdecl)); + if (TREE_CODE (newdecl) != FUNCTION_DECL + && TREE_CODE (newdecl) != TYPE_DECL + && TREE_CODE (newdecl) != CONST_DECL) + layout_decl (newdecl, 0); + } + else + { + /* Since the type is OLDDECL's, make OLDDECL's size go with. */ + DECL_SIZE (newdecl) = DECL_SIZE (olddecl); + DECL_SIZE_UNIT (newdecl) = DECL_SIZE_UNIT (olddecl); + if (DECL_ALIGN (olddecl) > DECL_ALIGN (newdecl)) + DECL_ALIGN (newdecl) = DECL_ALIGN (olddecl); + } + + /* Merge the type qualifiers. */ + if (TREE_READONLY (newdecl)) + TREE_READONLY (olddecl) = 1; + if (TREE_THIS_VOLATILE (newdecl)) + TREE_THIS_VOLATILE (olddecl) = 1; + + /* Merge the initialization information. */ + if (DECL_INITIAL (newdecl) == 0) + DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); + /* Keep the old rtl since we can safely use it. */ + DECL_RTL (newdecl) = DECL_RTL (olddecl); + } + /* If cannot merge, then use the new type and qualifiers, + and don't preserve the old rtl. */ + else + { + TREE_TYPE (olddecl) = TREE_TYPE (newdecl); + TREE_READONLY (olddecl) = TREE_READONLY (newdecl); + TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl); + TREE_VOLATILE (olddecl) = TREE_VOLATILE (newdecl); + } + + /* Merge the storage class information. */ + if (TREE_EXTERNAL (newdecl)) + { + TREE_STATIC (newdecl) = TREE_STATIC (olddecl); + TREE_EXTERNAL (newdecl) = TREE_EXTERNAL (olddecl); + + /* For functions, static overrides non-static. */ + if (TREE_CODE (newdecl) == FUNCTION_DECL) + { + TREE_PUBLIC (newdecl) &= TREE_PUBLIC (olddecl); + /* This is since we don't automatically + copy the attributes of NEWDECL into OLDDECL. */ + TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); + /* If this clears `static', clear it in the identifier too. */ + if (! TREE_PUBLIC (olddecl)) + TREE_PUBLIC (DECL_NAME (olddecl)) = 0; + } + else + TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl); + } + else + { + TREE_STATIC (olddecl) = TREE_STATIC (newdecl); + TREE_EXTERNAL (olddecl) = 0; + TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl); + } + + /* If either decl says `inline', this fn is inline, + unless its definition was passed already. */ + if (TREE_INLINE (newdecl) && DECL_INITIAL (olddecl) == 0) + TREE_INLINE (olddecl) = 1; + + /* If redeclaring a builtin function, and not a definition, + it stays built in. + Also preserve various other info from the definition. */ + if (TREE_CODE (newdecl) == FUNCTION_DECL && !new_is_definition) + { + DECL_SET_FUNCTION_CODE (newdecl, DECL_FUNCTION_CODE (olddecl)); + DECL_RESULT (newdecl) = DECL_RESULT (olddecl); + DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl); + DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl); + DECL_RESULT_TYPE (newdecl) = DECL_RESULT_TYPE (olddecl); + DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl); + DECL_FRAME_SIZE (newdecl) = DECL_FRAME_SIZE (olddecl); + } + + /* Don't lose track of having output OLDDECL as GDB symbol. */ + DECL_BLOCK_SYMTAB_ADDRESS (newdecl) + = DECL_BLOCK_SYMTAB_ADDRESS (olddecl); + + bcopy ((char *) newdecl + sizeof (struct tree_common), + (char *) olddecl + sizeof (struct tree_common), + sizeof (struct tree_decl) - sizeof (struct tree_common)); + + return 1; + } + + /* New decl is completely inconsistent with the old one => + tell caller to replace the old one. */ + return 0; +} + +/* Record a decl-node X as belonging to the current lexical scope. + Check for errors (such as an incompatible declaration for the same + name already seen in the same scope). + + Returns either X or an old decl for the same name. + If an old decl is returned, it may have been smashed + to agree with what X says. */ + +tree +pushdecl (x) + tree x; +{ + register tree t; + register tree name = DECL_NAME (x); + register struct binding_level *b = current_binding_level; + + if (name) + { + char *file; + int line; + + t = lookup_name_current_level (name); + if (t != 0 && t == error_mark_node) + /* error_mark_node is 0 for a while during initialization! */ + { + t = 0; + error_with_decl (x, "`%s' used prior to declaration"); + } + + if (t != 0) + { + file = DECL_SOURCE_FILE (t); + line = DECL_SOURCE_LINE (t); + } + + if (t != 0 && duplicate_decls (x, t)) + { + /* If this decl is `static' and an implicit decl was seen previously, + warn. But don't complain if -traditional, + since traditional compilers don't complain. */ + if (!flag_traditional && TREE_PUBLIC (name) + && ! TREE_PUBLIC (x) && ! TREE_EXTERNAL (x) + /* We used to warn also for explicit extern followed by static, + but sometimes you need to do it that way. */ + && IDENTIFIER_IMPLICIT_DECL (name) != 0) + { + warning ("`%s' was declared implicitly `extern' and later `static'", + IDENTIFIER_POINTER (name)); + warning_with_file_and_line (file, line, + "previous declaration of `%s'", + IDENTIFIER_POINTER (name)); + } + return t; + } + + /* If declaring a type as a typedef, and the type has no known + typedef name, install this TYPE_DECL as its typedef name. */ + if (TREE_CODE (x) == TYPE_DECL) + if (TYPE_NAME (TREE_TYPE (x)) == 0) + TYPE_NAME (TREE_TYPE (x)) = x; + + /* Multiple external decls of the same identifier ought to match. */ + + if (TREE_EXTERNAL (x) && IDENTIFIER_GLOBAL_VALUE (name) != 0 + && (TREE_EXTERNAL (IDENTIFIER_GLOBAL_VALUE (name)) + || TREE_PUBLIC (IDENTIFIER_GLOBAL_VALUE (name)))) + { + if (! comptypes (TREE_TYPE (x), + TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)))) + { + warning_with_decl (x, + "type mismatch with previous external decl"); + warning_with_decl (IDENTIFIER_GLOBAL_VALUE (name), + "previous external decl of `%s'"); + } + } + + /* In PCC-compatibility mode, extern decls of vars with no current decl + take effect at top level no matter where they are. */ + if (flag_traditional && TREE_EXTERNAL (x) + && lookup_name (name) == 0) + b = global_binding_level; + + /* This name is new in its binding level. + Install the new declaration and return it. */ + if (b == global_binding_level) + { + /* Install a global value. */ + + /* If the first global decl has external linkage, + warn if we later see static one. */ + if (IDENTIFIER_GLOBAL_VALUE (name) == 0 && TREE_PUBLIC (x)) + TREE_PUBLIC (name) = 1; + + IDENTIFIER_GLOBAL_VALUE (name) = x; + + /* Don't forget if the function was used via an implicit decl. */ + if (IDENTIFIER_IMPLICIT_DECL (name) + && TREE_USED (IDENTIFIER_IMPLICIT_DECL (name))) + TREE_USED (x) = 1, TREE_USED (name) = 1; + + /* Don't forget if its address was taken in that way. */ + if (IDENTIFIER_IMPLICIT_DECL (name) + && TREE_ADDRESSABLE (IDENTIFIER_IMPLICIT_DECL (name))) + TREE_ADDRESSABLE (x) = 1; + + /* Warn about mismatches against previous implicit decl. */ + if (IDENTIFIER_IMPLICIT_DECL (name) != 0 + /* If this real decl matches the implicit, don't complain. */ + && ! (TREE_CODE (x) == FUNCTION_DECL + && TREE_TYPE (TREE_TYPE (x)) == integer_type_node)) + warning ("`%s' was previously implicitly declared to return `int'", + IDENTIFIER_POINTER (name)); + + /* If this decl is `static' and an `extern' was seen previously, + that is erroneous. */ + if (TREE_PUBLIC (name) + && ! TREE_PUBLIC (x) && ! TREE_EXTERNAL (x)) + { + if (IDENTIFIER_IMPLICIT_DECL (name)) + warning ("`%s' was declared implicitly `extern' and later `static'", + IDENTIFIER_POINTER (name)); + else + warning ("`%s' was declared `extern' and later `static'", + IDENTIFIER_POINTER (name)); + } + } + else + { + /* Here to install a non-global value. */ + tree oldlocal = IDENTIFIER_LOCAL_VALUE (name); + tree oldglobal = IDENTIFIER_GLOBAL_VALUE (name); + IDENTIFIER_LOCAL_VALUE (name) = x; + + /* If this is an extern function declaration, see if we + have a global definition for the function. */ + if (oldlocal == 0 + && oldglobal != 0 + && TREE_CODE (x) == FUNCTION_DECL + && TREE_CODE (oldglobal) == FUNCTION_DECL) + { + /* We have one. Their types must agree. */ + if (! comptypes (TREE_TYPE (x), + TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)))) + warning_with_decl (x, "local declaration of `%s' doesn't match global one"); + /* If the global one is inline, make the local one inline. */ + else if (TREE_INLINE (oldglobal) + || DECL_FUNCTION_CODE (oldglobal) != NOT_BUILT_IN + || (TYPE_ARG_TYPES (TREE_TYPE (oldglobal)) != 0 + && TYPE_ARG_TYPES (TREE_TYPE (x)) == 0)) + IDENTIFIER_LOCAL_VALUE (name) = oldglobal; + } + /* If we have a local external declaration, + and no file-scope declaration has yet been seen, + then if we later have a file-scope decl it must not be static. */ + if (oldlocal == 0 + && oldglobal == 0 + && TREE_EXTERNAL (x) + && TREE_PUBLIC (x)) + { + TREE_PUBLIC (name) = 1; + } + + /* Warn if shadowing an argument at the top level of the body. */ + if (oldlocal != 0 && !TREE_EXTERNAL (x) + && TREE_CODE (oldlocal) == PARM_DECL + && TREE_CODE (x) != PARM_DECL + && current_binding_level->level_chain->parm_flag) + warning ("declaration of `%s' shadows a parameter", + IDENTIFIER_POINTER (name)); + + /* Maybe warn if shadowing something else. */ + else if (warn_shadow && !TREE_EXTERNAL (x) + /* No shadow warnings for vars made for inlining. */ + && !TREE_INLINE (x)) + { + char *warnstring = 0; + + if (oldlocal != 0 && TREE_CODE (oldlocal) == PARM_DECL) + warnstring = "declaration of `%s' shadows a parameter"; + else if (oldlocal != 0) + warnstring = "declaration of `%s' shadows previous local"; + else if (IDENTIFIER_GLOBAL_VALUE (name) != 0) + warnstring = "declaration of `%s' shadows global declaration"; + + if (warnstring) + warning (warnstring, IDENTIFIER_POINTER (name)); + } + + /* If storing a local value, there may already be one (inherited). + If so, record it for restoration when this binding level ends. */ + if (oldlocal != 0) + b->shadowed = tree_cons (name, oldlocal, b->shadowed); + } + + /* Keep count of variables in this level with incomplete type. */ + if (TYPE_SIZE (TREE_TYPE (x)) == 0) + ++b->n_incomplete; + } + + /* Put decls on list in reverse order. + We will reverse them later if necessary. */ + TREE_CHAIN (x) = b->names; + b->names = x; + + return x; +} + +/* Generate an implicit declaration for identifier FUNCTIONID + as a function of type int (). Print a warning if appropriate. */ + +tree +implicitly_declare (functionid) + tree functionid; +{ + register tree decl; + + /* Save the decl permanently so we can warn if definition follows. */ +#if 0 /* A temporary implicit decl causes a crash in pushdecl. + In 1.38, fix pushdecl. */ + if (flag_traditional || !warn_implicit + || current_binding_level == global_binding_level) +#endif + end_temporary_allocation (); + + /* We used to reuse an old implicit decl here, + but this loses with inline functions because it can clobber + the saved decl chains. */ +/* if (IDENTIFIER_IMPLICIT_DECL (functionid) != 0) + decl = IDENTIFIER_IMPLICIT_DECL (functionid); + else */ + decl = build_decl (FUNCTION_DECL, functionid, default_function_type); + + TREE_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + + /* ANSI standard says implicit declarations are in the innermost block. + So we record the decl in the standard fashion. + If flag_traditional is set, pushdecl does it top-level. */ + pushdecl (decl); + rest_of_decl_compilation (decl, 0, 0, 0); + + if (warn_implicit + /* Only one warning per identifier. */ + && IDENTIFIER_IMPLICIT_DECL (functionid) == 0) + warning ("implicit declaration of function `%s'", + IDENTIFIER_POINTER (functionid)); + + IDENTIFIER_IMPLICIT_DECL (functionid) = decl; + +#if 0 + if (flag_traditional || ! warn_implicit + || current_binding_level == global_binding_level) +#endif + resume_temporary_allocation (); + + return decl; +} + +/* Return zero if the declaration NEWDECL is valid + when the declaration OLDDECL (assumed to be for the same name) + has already been seen. + Otherwise return an error message format string with a %s + where the identifier should go. */ + +static char * +redeclaration_error_message (newdecl, olddecl) + tree newdecl, olddecl; +{ + if (TREE_CODE (newdecl) == TYPE_DECL) + { + if (flag_traditional && TREE_TYPE (newdecl) == TREE_TYPE (olddecl)) + return 0; + return "redefinition of `%s'"; + } + else if (TREE_CODE (newdecl) == FUNCTION_DECL) + { + /* Declarations of functions can insist on internal linkage + but they can't be inconsistent with internal linkage, + so there can be no error on that account. + However defining the same name twice is no good. */ + if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0 + /* However, defining once as extern inline and a second + time in another way is ok. */ + && !(TREE_INLINE (olddecl) && TREE_EXTERNAL (olddecl) + && !(TREE_INLINE (newdecl) && TREE_EXTERNAL (newdecl)))) + return "redefinition of `%s'"; + return 0; + } + else if (current_binding_level == global_binding_level) + { + /* Objects declared at top level: */ + /* If at least one is a reference, it's ok. */ + if (TREE_EXTERNAL (newdecl) || TREE_EXTERNAL (olddecl)) + return 0; + /* Reject two definitions. */ + if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0) + return "redefinition of `%s'"; + /* Now we have two tentative defs, or one tentative and one real def. */ + /* Insist that the linkage match. */ + if (TREE_PUBLIC (olddecl) != TREE_PUBLIC (newdecl)) + return "conflicting declarations of `%s'"; + return 0; + } + else + { + /* Objects declared with block scope: */ + /* Reject two definitions, and reject a definition + together with an external reference. */ + if (!(TREE_EXTERNAL (newdecl) && TREE_EXTERNAL (olddecl))) + return "redeclaration of `%s'"; + return 0; + } +} + +/* Get the LABEL_DECL corresponding to identifier ID as a label. + Create one if none exists so far for the current function. + This function is called for both label definitions and label references. */ + +tree +lookup_label (id) + tree id; +{ + register tree decl = IDENTIFIER_LABEL_VALUE (id); + + if (decl != 0) + return decl; + + decl = build_decl (LABEL_DECL, id, NULL_TREE); + DECL_MODE (decl) = VOIDmode; + /* Mark that the label's definition has not been seen. */ + DECL_SOURCE_LINE (decl) = 0; + + IDENTIFIER_LABEL_VALUE (id) = decl; + + named_labels + = tree_cons (NULL_TREE, decl, named_labels); + + return decl; +} + +/* Define a label, specifying the location in the source file. + Return the LABEL_DECL node for the label, if the definition is valid. + Otherwise return 0. */ + +tree +define_label (filename, line, name) + char *filename; + int line; + tree name; +{ + tree decl = lookup_label (name); + if (DECL_SOURCE_LINE (decl) != 0) + { + error_with_decl (decl, "duplicate label `%s'"); + return 0; + } + else + { + /* Mark label as having been defined. */ + DECL_SOURCE_FILE (decl) = filename; + DECL_SOURCE_LINE (decl) = line; + return decl; + } +} + +/* Return the list of declarations of the current level. + Note that this list is in reverse order unless/until + you nreverse it; and when you do nreverse it, you must + store the result back using `storedecls' or you will lose. */ + +tree +getdecls () +{ + return current_binding_level->names; +} + +/* Return the list of type-tags (for structs, etc) of the current level. */ + +tree +gettags () +{ + return current_binding_level->tags; +} + +/* Store the list of declarations of the current level. + This is done for the parameter declarations of a function being defined, + after they are modified in the light of any missing parameters. */ + +static void +storedecls (decls) + tree decls; +{ + current_binding_level->names = decls; +} + +/* Similarly, store the list of tags of the current level. */ + +static void +storetags (tags) + tree tags; +{ + current_binding_level->tags = tags; +} + +/* Given NAME, an IDENTIFIER_NODE, + return the structure (or union or enum) definition for that name. + Searches binding levels from BINDING_LEVEL up to the global level. + If THISLEVEL_ONLY is nonzero, searches only the specified context + (but skips any tag-transparent contexts to find one that is + meaningful for tags). + FORM says which kind of type the caller wants; + it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE. + If the wrong kind of type is found, an error is reported. */ + +static tree +lookup_tag (form, name, binding_level, thislevel_only) + enum tree_code form; + struct binding_level *binding_level; + tree name; + int thislevel_only; +{ + register struct binding_level *level; + + for (level = binding_level; level; level = level->level_chain) + { + register tree tail; + for (tail = level->tags; tail; tail = TREE_CHAIN (tail)) + { + if (TREE_PURPOSE (tail) == name) + { + if (TREE_CODE (TREE_VALUE (tail)) != form) + { + /* Definition isn't the kind we were looking for. */ + error ("`%s' defined as wrong kind of tag", + IDENTIFIER_POINTER (name)); + } + return TREE_VALUE (tail); + } + } + if (thislevel_only && ! level->tag_transparent) + return NULL_TREE; + } + return NULL_TREE; +} + +/* Given a type, find the tag that was defined for it and return the tag name. + Otherwise return 0. However, the value can never be 0 + in the cases in which this is used. */ + +static tree +lookup_tag_reverse (type) + tree type; +{ + register struct binding_level *level; + + for (level = current_binding_level; level; level = level->level_chain) + { + register tree tail; + for (tail = level->tags; tail; tail = TREE_CHAIN (tail)) + { + if (TREE_VALUE (tail) == type) + return TREE_PURPOSE (tail); + } + } + return NULL_TREE; +} + +/* Look up NAME in the current binding level and its superiors + in the namespace of variables, functions and typedefs. + Return a ..._DECL node of some kind representing its definition, + or return 0 if it is undefined. */ + +tree +lookup_name (name) + tree name; +{ + register tree val; + if (current_binding_level != global_binding_level + && IDENTIFIER_LOCAL_VALUE (name)) + val = IDENTIFIER_LOCAL_VALUE (name); + else + val = IDENTIFIER_GLOBAL_VALUE (name); + if (val && TREE_TYPE (val) == error_mark_node) + return error_mark_node; + return val; +} + +/* Similar to `lookup_name' but look only at current binding level. */ + +static tree +lookup_name_current_level (name) + tree name; +{ + register tree t; + + if (current_binding_level == global_binding_level) + return IDENTIFIER_GLOBAL_VALUE (name); + + if (IDENTIFIER_LOCAL_VALUE (name) == 0) + return 0; + + for (t = current_binding_level->names; t; t = TREE_CHAIN (t)) + if (DECL_NAME (t) == name) + break; + + return t; +} + +/* Create the predefined scalar types of C, + and some nodes representing standard constants (0, 1, (void *)0). + Initialize the global binding level. + Make definitions for built-in primitive functions. */ + +void +init_decl_processing () +{ + register tree endlink; + + /* Make identifier nodes long enough for the language-specific slots. */ + set_identifier_size (sizeof (struct lang_identifier)); + + current_function_decl = NULL; + named_labels = NULL; + current_binding_level = NULL_BINDING_LEVEL; + free_binding_level = NULL_BINDING_LEVEL; + pushlevel (0); /* make the binding_level structure for global names */ + global_binding_level = current_binding_level; + + value_identifier = get_identifier (""); + + /* Define `int' and `char' first so that dbx will output them first. */ + + integer_type_node = make_signed_type (INT_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_INT], + integer_type_node)); + + /* Define `char', which is like either `signed char' or `unsigned char' + but not the same as either. */ + + char_type_node = + (flag_signed_char + ? make_signed_type (CHAR_TYPE_SIZE) + : make_unsigned_type (CHAR_TYPE_SIZE)); + pushdecl (build_decl (TYPE_DECL, get_identifier ("char"), + char_type_node)); + + long_integer_type_node = make_signed_type (LONG_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("long int"), + long_integer_type_node)); + + unsigned_type_node = make_unsigned_type (INT_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned int"), + unsigned_type_node)); + + long_unsigned_type_node = make_unsigned_type (LONG_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("long unsigned int"), + long_unsigned_type_node)); + + /* `unsigned long' or `unsigned int' is the standard type for sizeof. + Traditionally, use a signed type. */ + if (INT_TYPE_SIZE != LONG_TYPE_SIZE) + sizetype = flag_traditional ? long_integer_type_node : long_unsigned_type_node; + else + sizetype = flag_traditional ? integer_type_node : unsigned_type_node; + + TREE_TYPE (TYPE_SIZE (integer_type_node)) = sizetype; + TREE_TYPE (TYPE_SIZE (char_type_node)) = sizetype; + TREE_TYPE (TYPE_SIZE (unsigned_type_node)) = sizetype; + TREE_TYPE (TYPE_SIZE (long_unsigned_type_node)) = sizetype; + TREE_TYPE (TYPE_SIZE (long_integer_type_node)) = sizetype; + + error_mark_node = make_node (ERROR_MARK); + TREE_TYPE (error_mark_node) = error_mark_node; + + short_integer_type_node = make_signed_type (SHORT_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("short int"), + short_integer_type_node)); + + long_long_integer_type_node = make_signed_type (LONG_LONG_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("long long int"), + long_long_integer_type_node)); + + short_unsigned_type_node = make_unsigned_type (SHORT_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("short unsigned int"), + short_unsigned_type_node)); + + long_long_unsigned_type_node = make_unsigned_type (LONG_LONG_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("long long unsigned int"), + long_long_unsigned_type_node)); + + /* Define both `signed char' and `unsigned char'. */ + signed_char_type_node = make_signed_type (CHAR_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("signed char"), + signed_char_type_node)); + + unsigned_char_type_node = make_unsigned_type (CHAR_TYPE_SIZE); + pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned char"), + unsigned_char_type_node)); + + float_type_node = make_node (REAL_TYPE); + TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE; + pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_FLOAT], + float_type_node)); + layout_type (float_type_node); + + double_type_node = make_node (REAL_TYPE); + TYPE_PRECISION (double_type_node) = DOUBLE_TYPE_SIZE; + pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_DOUBLE], + double_type_node)); + layout_type (double_type_node); + + long_double_type_node = make_node (REAL_TYPE); + TYPE_PRECISION (long_double_type_node) = LONG_DOUBLE_TYPE_SIZE; + pushdecl (build_decl (TYPE_DECL, get_identifier ("long double"), + long_double_type_node)); + layout_type (long_double_type_node); + + integer_zero_node = build_int_2 (0, 0); + TREE_TYPE (integer_zero_node) = integer_type_node; + integer_one_node = build_int_2 (1, 0); + TREE_TYPE (integer_one_node) = integer_type_node; + + size_zero_node = build_int_2 (0, 0); + TREE_TYPE (size_zero_node) = sizetype; + size_one_node = build_int_2 (1, 0); + TREE_TYPE (size_one_node) = sizetype; + + void_type_node = make_node (VOID_TYPE); + pushdecl (build_decl (TYPE_DECL, + ridpointers[(int) RID_VOID], void_type_node)); + layout_type (void_type_node); /* Uses integer_zero_node */ + + null_pointer_node = build_int_2 (0, 0); + TREE_TYPE (null_pointer_node) = build_pointer_type (void_type_node); + layout_type (TREE_TYPE (null_pointer_node)); + + string_type_node = build_pointer_type (char_type_node); + + /* make a type for arrays of 256 characters. + 256 is picked randomly because we have a type for integers from 0 to 255. + With luck nothing will ever really depend on the length of this + array type. */ + char_array_type_node + = build_array_type (char_type_node, unsigned_char_type_node); + /* Likewise for arrays of ints. */ + int_array_type_node + = build_array_type (integer_type_node, unsigned_char_type_node); + + default_function_type + = build_function_type (integer_type_node, NULL_TREE); + + ptr_type_node = build_pointer_type (void_type_node); + endlink = tree_cons (NULL_TREE, void_type_node, NULL_TREE); + + double_ftype_double + = build_function_type (double_type_node, + tree_cons (NULL_TREE, double_type_node, endlink)); + + double_ftype_double_double + = build_function_type (double_type_node, + tree_cons (NULL_TREE, double_type_node, + tree_cons (NULL_TREE, + double_type_node, endlink))); + + int_ftype_int + = build_function_type (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, endlink)); + + long_ftype_long + = build_function_type (long_integer_type_node, + tree_cons (NULL_TREE, + long_integer_type_node, endlink)); + + void_ftype_ptr_ptr_int + = build_function_type (void_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)))); + + int_ftype_ptr_ptr_int + = build_function_type (integer_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)))); + + void_ftype_ptr_int_int + = build_function_type (void_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)))); + + builtin_function ("__builtin_alloca", + build_function_type (ptr_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)), + BUILT_IN_ALLOCA); + + builtin_function ("__builtin_abs", int_ftype_int, BUILT_IN_ABS); + builtin_function ("__builtin_fabs", double_ftype_double, BUILT_IN_FABS); + builtin_function ("__builtin_labs", long_ftype_long, BUILT_IN_LABS); + builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS); + builtin_function ("__builtin_saveregs", default_function_type, + BUILT_IN_SAVEREGS); + builtin_function ("__builtin_classify_type", default_function_type, + BUILT_IN_CLASSIFY_TYPE); + builtin_function ("__builtin_next_arg", + build_function_type (ptr_type_node, endlink), + BUILT_IN_NEXT_ARG); +#if 0 + /* Support for these has not been written in either expand_builtin + or build_function_call. */ + builtin_function ("__builtin_div", default_ftype, BUILT_IN_DIV); + builtin_function ("__builtin_ldiv", default_ftype, BUILT_IN_LDIV); + builtin_function ("__builtin_ffloor", double_ftype_double, BUILT_IN_FFLOOR); + builtin_function ("__builtin_fceil", double_ftype_double, BUILT_IN_FCEIL); + builtin_function ("__builtin_fmod", double_ftype_double_double, BUILT_IN_FMOD); + builtin_function ("__builtin_frem", double_ftype_double_double, BUILT_IN_FREM); + builtin_function ("__builtin_memcpy", void_ftype_ptr_ptr_int, BUILT_IN_MEMCPY); + builtin_function ("__builtin_memcmp", int_ftype_ptr_ptr_int, BUILT_IN_MEMCMP); + builtin_function ("__builtin_memset", void_ftype_ptr_int_int, BUILT_IN_MEMSET); + builtin_function ("__builtin_fsqrt", double_ftype_double, BUILT_IN_FSQRT); + builtin_function ("__builtin_getexp", double_ftype_double, BUILT_IN_GETEXP); + builtin_function ("__builtin_getman", double_ftype_double, BUILT_IN_GETMAN); +#endif + + start_identifier_warnings (); +} + +/* Make a definition for a builtin function named NAME and whose data type + is TYPE. TYPE should be a function type with argument types. + FUNCTION_CODE tells later passes how to compile calls to this function. + See tree.h for its possible values. */ + +static void +builtin_function (name, type, function_code) + char *name; + tree type; + enum built_in_function function_code; +{ + tree decl = build_decl (FUNCTION_DECL, get_identifier (name), type); + TREE_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + make_decl_rtl (decl, 0, 1); + pushdecl (decl); + DECL_SET_FUNCTION_CODE (decl, function_code); +} + +/* Called when a declaration is seen that contains no names to declare. + If its type is a reference to a structure, union or enum inherited + from a containing scope, shadow that tag name for the current scope + with a forward reference. + If its type defines a new named structure or union + or defines an enum, it is valid but we need not do anything here. + Otherwise, it is an error. */ + +void +shadow_tag (declspecs) + tree declspecs; +{ + int found_tag = 0; + int warned = 0; + register tree link; + + for (link = declspecs; link; link = TREE_CHAIN (link)) + { + register tree value = TREE_VALUE (link); + register enum tree_code code = TREE_CODE (value); + int ok = 0; + + if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) + /* Used to test also that TYPE_SIZE (value) != 0. + That caused warning for `struct foo;' at top level in the file. */ + { + register tree name = lookup_tag_reverse (value); + register tree t = lookup_tag (code, name, current_binding_level, 1); + + if (t == 0) + { + t = make_node (code); + pushtag (name, t); + ok = 1; + } + else if (name != 0 || code == ENUMERAL_TYPE) + ok = 1; + } + + if (ok) + found_tag++; + else + { + if (!warned) + warning ("useless keyword or type name in declaration"); + warned = 1; + } + } + + if (!warned) + { + if (found_tag > 1) + warning ("multiple types in one declaration"); + if (found_tag == 0) + warning ("empty declaration"); + } +} + +/* Decode a "typename", such as "int **", returning a ..._TYPE node. */ + +tree +groktypename (typename) + tree typename; +{ + if (TREE_CODE (typename) != TREE_LIST) + return typename; + return grokdeclarator (TREE_VALUE (typename), + TREE_PURPOSE (typename), + TYPENAME, 0); +} + +/* Decode a declarator in an ordinary declaration or data definition. + This is called as soon as the type information and variable name + have been parsed, before parsing the initializer if any. + Here we create the ..._DECL node, fill in its type, + and put it on the list of decls for the current context. + The ..._DECL node is returned as the value. + + Exception: for arrays where the length is not specified, + the type is left null, to be filled in by `finish_decl'. + + Function definitions do not come here; they go to start_function + instead. However, external and forward declarations of functions + do go through here. Structure field declarations are done by + grokfield and not through here. */ + +/* Set this to zero to debug not using the temporary obstack + to parse initializers. */ +int debug_temp_inits = 1; + +tree +start_decl (declarator, declspecs, initialized) + tree declspecs, declarator; + int initialized; +{ + register tree decl = grokdeclarator (declarator, declspecs, + NORMAL, initialized); + register tree tem; + int init_written = initialized; + + if (initialized) + /* Is it valid for this decl to have an initializer at all? + If not, set INITIALIZED to zero, which will indirectly + tell `finish_decl' to ignore the initializer once it is parsed. */ + switch (TREE_CODE (decl)) + { + case TYPE_DECL: + /* typedef foo = bar means give foo the same type as bar. + We haven't parsed bar yet, so `finish_decl' will fix that up. + Any other case of an initialization in a TYPE_DECL is an error. */ + if (pedantic || list_length (declspecs) > 1) + { + error ("typedef `%s' is initialized", + IDENTIFIER_POINTER (DECL_NAME (decl))); + initialized = 0; + } + break; + + case FUNCTION_DECL: + error ("function `%s' is initialized like a variable", + IDENTIFIER_POINTER (DECL_NAME (decl))); + initialized = 0; + break; + + default: + /* Don't allow initializations for incomplete types + except for arrays which might be completed by the initialization. */ + if (TYPE_SIZE (TREE_TYPE (decl)) != 0) + ; /* A complete type is ok. */ + else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE) + { + error ("variable `%s' has initializer but incomplete type", + IDENTIFIER_POINTER (DECL_NAME (decl))); + initialized = 0; + } + else if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl))) == 0) + { + error ("elements of array `%s' have incomplete type", + IDENTIFIER_POINTER (DECL_NAME (decl))); + initialized = 0; + } + } + + if (initialized) + { +#if 0 /* Seems redundant. */ + if (current_binding_level != global_binding_level + && TREE_EXTERNAL (decl) + && TREE_CODE (decl) != FUNCTION_DECL) + warning ("declaration of `%s' has `extern' and is initialized", + IDENTIFIER_POINTER (DECL_NAME (decl))); +#endif + TREE_EXTERNAL (decl) = 0; + if (current_binding_level == global_binding_level) + TREE_STATIC (decl) = 1; + + /* Tell `pushdecl' this is an initialized decl + even though we don't yet have the initializer expression. + Also tell `finish_decl' it may store the real initializer. */ + DECL_INITIAL (decl) = error_mark_node; + } + + /* Add this decl to the current binding level. + TEM may equal DECL or it may be a previous decl of the same name. */ + tem = pushdecl (decl); + + /* For a local variable, define the RTL now. */ + if (current_binding_level != global_binding_level + /* But not if this is a duplicate decl + and we preserved the rtl from the previous one + (which may or may not happen). */ + && DECL_RTL (tem) == 0) + { + if (TYPE_SIZE (TREE_TYPE (tem)) != 0) + expand_decl (tem, NULL_TREE); + else if (TREE_CODE (TREE_TYPE (tem)) == ARRAY_TYPE + && DECL_INITIAL (tem) != 0) + expand_decl (tem, NULL_TREE); + } + + if (init_written) + { + /* When parsing and digesting the initializer, + use temporary storage. Do this even if we will ignore the value. */ + if (current_binding_level == global_binding_level && debug_temp_inits) + temporary_allocation (); + } + + return tem; +} + +/* Finish processing of a declaration; + install its line number and initial value. + If the length of an array type is not known before, + it must be determined now, from the initial value, or it is an error. */ + +void +finish_decl (decl, init, asmspec_tree) + tree decl, init; + tree asmspec_tree; +{ + register tree type = TREE_TYPE (decl); + int was_incomplete = (DECL_SIZE (decl) == 0); + int temporary = allocation_temporary_p (); + char *asmspec = 0; + + if (asmspec_tree) + asmspec = TREE_STRING_POINTER (asmspec_tree); + + /* If `start_decl' didn't like having an initialization, ignore it now. */ + + if (init != 0 && DECL_INITIAL (decl) == 0) + init = 0; + + if (init) + { + if (TREE_CODE (decl) != TYPE_DECL) + store_init_value (decl, init); + else + { + /* typedef foo = bar; store the type of bar as the type of foo. */ + TREE_TYPE (decl) = TREE_TYPE (init); + DECL_INITIAL (decl) = init = 0; + } + } + + /* For top-level declaration, the initial value was read in + the temporary obstack. MAXINDEX, rtl, etc. to be made below + must go in the permanent obstack; but don't discard the + temporary data yet. */ + + if (current_binding_level == global_binding_level && temporary) + end_temporary_allocation (); + + /* Deduce size of array from initialization, if not already known */ + + if (TREE_CODE (type) == ARRAY_TYPE + && TYPE_DOMAIN (type) == 0 + && TREE_CODE (decl) != TYPE_DECL) + { +#if 0 + int do_default = ! ((!pedantic && TREE_STATIC (decl)) + || TREE_EXTERNAL (decl)); +#endif + int do_default + = (TREE_STATIC (decl) + /* Even if pedantic, an external linkage array + may have incomplete type at first. */ + ? pedantic && !TREE_PUBLIC (decl) + : !TREE_EXTERNAL (decl)); + int failure + = complete_array_type (type, DECL_INITIAL (decl), do_default); + + if (failure == 1) + error_with_decl (decl, "initializer fails to determine size of `%s'"); + + if (failure == 2) + { + if (do_default) + { + if (! TREE_PUBLIC (decl)) + error_with_decl (decl, "array size missing in `%s'"); + } + else if (!pedantic && TREE_STATIC (decl)) + TREE_EXTERNAL (decl) = 1; + } + + if (pedantic && TYPE_DOMAIN (type) != 0 + && tree_int_cst_lt (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), + integer_zero_node)) + error_with_decl (decl, "zero-size array `%s'"); + + layout_decl (decl, 0); + } + + if (TREE_CODE (decl) == VAR_DECL) + { + if (TREE_STATIC (decl) && DECL_SIZE (decl) == 0) + { + /* A static variable with an incomplete type: + that is an error if it is initialized or `static'. + Otherwise, let it through, but if it is not `extern' + then it may cause an error message later. */ + if (! (TREE_PUBLIC (decl) && DECL_INITIAL (decl) == 0)) + error_with_decl (decl, "storage size of `%s' isn't known"); + } + else if (!TREE_EXTERNAL (decl) && DECL_SIZE (decl) == 0) + { + /* An automatic variable with an incomplete type: + that is an error. */ + error_with_decl (decl, "storage size of `%s' isn't known"); + TREE_TYPE (decl) = error_mark_node; + } + + if ((TREE_EXTERNAL (decl) || TREE_STATIC (decl)) + && DECL_SIZE (decl) != 0 && ! TREE_LITERAL (DECL_SIZE (decl))) + error_with_decl (decl, "storage size of `%s' isn't constant"); + } + + /* Output the assembler code and/or RTL code for variables and functions, + unless the type is an undefined structure or union. + If not, it will get done when the type is completed. */ + + if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL) + { + if (flag_traditional && allocation_temporary_p ()) + { + end_temporary_allocation (); + rest_of_decl_compilation (decl, asmspec, + current_binding_level == global_binding_level, + 0); + resume_temporary_allocation (); + } + else + rest_of_decl_compilation (decl, asmspec, + current_binding_level == global_binding_level, + 0); + if (current_binding_level != global_binding_level) + { + /* Recompute the RTL of a local array now + if it used to be an incomplete type. */ + if (was_incomplete + && ! TREE_STATIC (decl) && ! TREE_EXTERNAL (decl)) + { + /* If we used it already as memory, it must stay in memory. */ + TREE_ADDRESSABLE (decl) = TREE_USED (decl); + /* If it's still incomplete now, no init will save it. */ + if (DECL_SIZE (decl) == 0) + DECL_INITIAL (decl) = 0; + expand_decl (decl, NULL_TREE); + } + /* Compute and store the initial value. */ + expand_decl_init (decl); + } + } + + if (TREE_CODE (decl) == TYPE_DECL) + rest_of_decl_compilation (decl, 0, + current_binding_level == global_binding_level, + 0); + + /* Resume permanent allocation, if not within a function. */ + if (temporary && current_binding_level == global_binding_level) + { + permanent_allocation (); + /* We need to remember that this array HAD an initialization, + but discard the actual nodes, since they are temporary anyway. */ + if (DECL_INITIAL (decl) != 0) + DECL_INITIAL (decl) = error_mark_node; + } + + /* At the end of a declaration, throw away any variable type sizes + of types defined inside that declaration. There is no use + computing them in the following function definition. */ + if (current_binding_level == global_binding_level) + get_pending_sizes (); +} + +/* Given a parsed parameter declaration, + decode it into a PARM_DECL and push that on the current binding level. */ + +void +push_parm_decl (parm) + tree parm; +{ + register tree decl = grokdeclarator (TREE_VALUE (parm), TREE_PURPOSE (parm), + PARM, 0); + + /* Add this decl to the current binding level. */ + finish_decl (pushdecl (decl), NULL_TREE, NULL_TREE); +} + +/* Make TYPE a complete type based on INITIAL_VALUE. + Return 0 if successful, 1 if INITIAL_VALUE can't be decyphered, + 2 if there was no information (in which case assume 1 if DO_DEFAULT). */ + +int +complete_array_type (type, initial_value, do_default) + tree type; + tree initial_value; + int do_default; +{ + register tree maxindex = NULL_TREE; + int value = 0; + int temporary = (TREE_PERMANENT (type) && allocation_temporary_p ()); + + /* Don't put temporary nodes in permanent type. */ + if (temporary) + end_temporary_allocation (); + + if (initial_value) + { + /* Note MAXINDEX is really the maximum index, + one less than the size. */ + if (TREE_CODE (initial_value) == STRING_CST) + { + int eltsize = TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (TREE_TYPE (initial_value)))); + maxindex = build_int_2 (TREE_STRING_LENGTH (initial_value) / eltsize - 1, 0); + } + else if (TREE_CODE (initial_value) == CONSTRUCTOR) + { + register int nelts + = list_length (CONSTRUCTOR_ELTS (initial_value)); + maxindex = build_int_2 (nelts - 1, 0); + } + else + { + /* Make an error message unless that happened already. */ + if (initial_value != error_mark_node) + value = 1; + + /* Prevent further error messages. */ + maxindex = build_int_2 (1, 0); + } + } + + if (!maxindex) + { + if (do_default) + maxindex = build_int_2 (1, 0); + value = 2; + } + + if (maxindex) + { + TYPE_DOMAIN (type) = build_index_type (maxindex); + if (!TREE_TYPE (maxindex)) + TREE_TYPE (maxindex) = TYPE_DOMAIN (type); + } + + /* Lay out the type now that we can get the real answer. */ + + layout_type (type); + + if (temporary) + resume_temporary_allocation (); + + return value; +} + +/* Given declspecs and a declarator, + determine the name and type of the object declared + and construct a ..._DECL node for it. + (In one case we can return a ..._TYPE node instead. + For invalid input we sometimes return 0.) + + DECLSPECS is a chain of tree_list nodes whose value fields + are the storage classes and type specifiers. + + DECL_CONTEXT says which syntactic context this declaration is in: + NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL. + FUNCDEF for a function definition. Like NORMAL but a few different + error messages in each case. Return value may be zero meaning + this definition is too screwy to try to parse. + PARM for a parameter declaration (either within a function prototype + or before a function body). Make a PARM_DECL, or return void_type_node. + TYPENAME if for a typename (in a cast or sizeof). + Don't make a DECL node; just return the ..._TYPE node. + FIELD for a struct or union field; make a FIELD_DECL. + INITIALIZED is 1 if the decl has an initializer. + + In the TYPENAME case, DECLARATOR is really an absolute declarator. + It may also be so in the PARM case, for a prototype where the + argument type is specified but not the name. + + This function is where the complicated C meanings of `static' + and `extern' are intrepreted. */ + +static tree +grokdeclarator (declarator, declspecs, decl_context, initialized) + tree declspecs; + tree declarator; + enum decl_context decl_context; + int initialized; +{ + int specbits = 0; + tree spec; + tree type = NULL_TREE; + int longlong = 0; + int constp; + int volatilep; + int inlinep; + int explicit_int = 0; + int explicit_char = 0; + char *name; + tree typedef_type = 0; + int funcdef_flag = 0; + int resume_temporary = 0; + enum tree_code innermost_code = ERROR_MARK; + + if (decl_context == FUNCDEF) + funcdef_flag = 1, decl_context = NORMAL; + + if (flag_traditional && allocation_temporary_p ()) + { + resume_temporary = 1; + end_temporary_allocation (); + } + + /* Look inside a declarator for the name being declared + and get it as a string, for an error message. */ + { + register tree decl = declarator; + name = 0; + + while (decl) + switch (TREE_CODE (decl)) + { + case ARRAY_REF: + case INDIRECT_REF: + case CALL_EXPR: + innermost_code = TREE_CODE (decl); + decl = TREE_OPERAND (decl, 0); + break; + + case IDENTIFIER_NODE: + name = IDENTIFIER_POINTER (decl); + decl = 0; + break; + + default: + abort (); + } + if (name == 0) + name = "type name"; + } + + /* A function definition's declarator must have the form of + a function declarator. */ + + if (funcdef_flag && innermost_code != CALL_EXPR) + return 0; + + /* Anything declared one level down from the top level + must be one of the parameters of a function + (because the body is at least two levels down). */ + + if (decl_context == NORMAL + && current_binding_level->level_chain == global_binding_level) + decl_context = PARM; + + /* Look through the decl specs and record which ones appear. + Some typespecs are defined as built-in typenames. + Others, the ones that are modifiers of other types, + are represented by bits in SPECBITS: set the bits for + the modifiers that appear. Storage class keywords are also in SPECBITS. + + If there is a typedef name or a type, store the type in TYPE. + This includes builtin typedefs such as `int'. + + Set EXPLICIT_INT if the type is `int' or `char' and did not + come from a user typedef. + + Set LONGLONG if `long' is mentioned twice. */ + + for (spec = declspecs; spec; spec = TREE_CHAIN (spec)) + { + register int i; + register tree id = TREE_VALUE (spec); + + if (id == ridpointers[(int) RID_INT]) + explicit_int = 1; + if (id == ridpointers[(int) RID_CHAR]) + explicit_char = 1; + + if (TREE_CODE (id) == IDENTIFIER_NODE) + for (i = (int) RID_FIRST_MODIFIER; i < (int) RID_MAX; i++) + { + if (ridpointers[i] == id) + { + if (i == (int) RID_LONG && specbits & (1< 1) + warning ("duplicate `const'"); + if (volatilep > 1) + warning ("duplicate `volatile'"); + type = TYPE_MAIN_VARIANT (type); + + /* Warn if two storage classes are given. Default to `auto'. */ + + { + int nclasses = 0; + + if (specbits & 1 << (int) RID_AUTO) nclasses++; + if (specbits & 1 << (int) RID_STATIC) nclasses++; + if (specbits & 1 << (int) RID_EXTERN) nclasses++; + if (specbits & 1 << (int) RID_REGISTER) nclasses++; + if (specbits & 1 << (int) RID_TYPEDEF) nclasses++; + + /* Warn about storage classes that are invalid for certain + kinds of declarations (parameters, typenames, etc.). */ + + if (nclasses > 1) + error ("multiple storage classes in declaration of `%s'", name); + else if (funcdef_flag + && (specbits + & ((1 << (int) RID_REGISTER) + | (1 << (int) RID_AUTO) + | (1 << (int) RID_TYPEDEF)))) + { + if (specbits & 1 << (int) RID_AUTO) + error ("function definition declared `auto'"); + if (specbits & 1 << (int) RID_REGISTER) + error ("function definition declared `auto'"); + if (specbits & 1 << (int) RID_TYPEDEF) + error ("function definition declared `typedef'"); + specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) + | (1 << (int) RID_AUTO)); + } + else if (decl_context != NORMAL && nclasses > 0) + { + if (decl_context == PARM && specbits & 1 << (int) RID_REGISTER) + ; + else + { + error ((decl_context == FIELD + ? "storage class specified for structure field `%s'" + : (decl_context == PARM + ? "storage class specified for parameter `%s'" + : "storage class specified for typename")), + name); + specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER) + | (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC) + | (1 << (int) RID_EXTERN)); + } + } + else if (specbits & 1 << (int) RID_EXTERN && initialized + && ! funcdef_flag) + warning ("`%s' initialized and declared `extern'", name); + else if (current_binding_level == global_binding_level + && specbits & (1 << (int) RID_AUTO)) + error ("top-level declaration of `%s' specifies `auto'", name); + } + + /* Now figure out the structure of the declarator proper. + Descend through it, creating more complex types, until we reach + the declared identifier (or NULL_TREE, in an absolute declarator). */ + + while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE) + { + if (type == error_mark_node) + { + declarator = TREE_OPERAND (declarator, 0); + continue; + } + + /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]), + an INDIRECT_REF (for *...), + a CALL_EXPR (for ...(...)), + an identifier (for the name being declared) + or a null pointer (for the place in an absolute declarator + where the name was omitted). + For the last two cases, we have just exited the loop. + + At this point, TYPE is the type of elements of an array, + or for a function to return, or for a pointer to point to. + After this sequence of ifs, TYPE is the type of the + array or function or pointer, and DECLARATOR has had its + outermost layer removed. */ + + if (TREE_CODE (declarator) == ARRAY_REF) + { + register tree itype = NULL_TREE; + register tree size = TREE_OPERAND (declarator, 1); + + declarator = TREE_OPERAND (declarator, 0); + + /* Check for some types that there cannot be arrays of. */ + + if (type == void_type_node) + { + error ("declaration of `%s' as array of voids", name); + type = error_mark_node; + } + + if (TREE_CODE (type) == FUNCTION_TYPE) + { + error ("declaration of `%s' as array of functions", name); + type = error_mark_node; + } + + if (size == error_mark_node) + type = error_mark_node; + + if (type == error_mark_node) + continue; + + /* If size was specified, set ITYPE to a range-type for that size. + Otherwise, ITYPE remains null. finish_decl may figure it out + from an initial value. */ + + if (size) + { + /* might be a cast */ + if (TREE_CODE (size) == NOP_EXPR + && TREE_TYPE (size) == TREE_TYPE (TREE_OPERAND (size, 0))) + size = TREE_OPERAND (size, 0); + + if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE + && TREE_CODE (TREE_TYPE (size)) != ENUMERAL_TYPE) + { + error ("size of array `%s' has non-integer type", name); + size = integer_one_node; + } + if (pedantic && integer_zerop (size)) + warning ("ANSI C forbids zero-size array `%s'", name); + if (TREE_CODE (size) == INTEGER_CST) + { + if (INT_CST_LT (size, integer_zero_node)) + { + error ("size of array `%s' is negative", name); + size = integer_one_node; + } + itype = build_index_type (build_int_2 (TREE_INT_CST_LOW (size) - 1, 0)); + } + else + { + if (pedantic) + warning ("ANSI C forbids variable-size array `%s'", name); + itype = build_binary_op (MINUS_EXPR, size, integer_one_node); + itype = build_index_type (itype); + } + } + + /* Complain about arrays of incomplete types, except in typedefs. */ + + if (TYPE_SIZE (type) == 0 + && !(specbits & (1 << (int) RID_TYPEDEF))) + warning ("array type has incomplete element type"); + + /* Build the array type itself. + Merge any constancy or volatility into the target type. */ + + if (constp || volatilep) + type = c_build_type_variant (type, constp, volatilep); + +#if 0 /* don't clear these; leave them set so that the array type + or the variable is itself const or volatile. */ + constp = 0; + volatilep = 0; +#endif + + type = build_array_type (type, itype); + } + else if (TREE_CODE (declarator) == CALL_EXPR) + { + tree arg_types; + + /* Declaring a function type. + Make sure we have a valid type for the function to return. */ + if (type == error_mark_node) + continue; + + if (pedantic && (constp || volatilep)) + warning ("function declared to return const or volatile result"); + + /* Warn about some types functions can't return. */ + + if (TREE_CODE (type) == FUNCTION_TYPE) + { + error ("`%s' declared as function returning a function", name); + type = integer_type_node; + } + if (TREE_CODE (type) == ARRAY_TYPE) + { + error ("`%s' declared as function returning an array", name); + type = integer_type_node; + } + + /* Traditionally, declaring return type float means double. */ + + if (flag_traditional && type == float_type_node) + type = double_type_node; + + /* Construct the function type and go to the next + inner layer of declarator. */ + + arg_types = grokparms (TREE_OPERAND (declarator, 1), + funcdef_flag + /* Say it's a definition + only for the CALL_EXPR + closest to the identifier. */ + && TREE_CODE (TREE_OPERAND (declarator, 0)) == IDENTIFIER_NODE); +#if 0 /* This seems to be false. We turn off temporary allocation + above in this function if -traditional. + And this code caused inconsistent results with prototypes: + callers would ignore them, and pass arguments wrong. */ + + /* Omit the arg types if -traditional, since the arg types + and the list links might not be permanent. */ + type = build_function_type (type, flag_traditional ? 0 : arg_types); +#endif + type = build_function_type (type, arg_types); + declarator = TREE_OPERAND (declarator, 0); + } + else if (TREE_CODE (declarator) == INDIRECT_REF) + { + /* Merge any constancy or volatility into the target type + for the pointer. */ + + if (constp || volatilep) + type = c_build_type_variant (type, constp, volatilep); + constp = 0; + volatilep = 0; + + type = build_pointer_type (type); + + /* Process a list of type modifier keywords + (such as const or volatile) that were given inside the `*'. */ + + if (TREE_TYPE (declarator)) + { + register tree typemodlist; + int erred = 0; + for (typemodlist = TREE_TYPE (declarator); typemodlist; + typemodlist = TREE_CHAIN (typemodlist)) + { + if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_CONST]) + constp++; + else if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_VOLATILE]) + volatilep++; + else if (!erred) + { + erred = 1; + error ("invalid type modifier within pointer declarator"); + } + } + if (constp > 1) + warning ("duplicate `const'"); + if (volatilep > 1) + warning ("duplicate `volatile'"); + } + + declarator = TREE_OPERAND (declarator, 0); + } + else + abort (); + +/* layout_type (type); */ + + /* @@ Should perhaps replace the following code by changes in + * @@ stor_layout.c. */ + if (TREE_CODE (type) == FUNCTION_DECL) + { + /* A function variable in C should be Pmode rather than EPmode + because it has just the address of a function, no static chain.*/ + TYPE_MODE (type) = Pmode; + } + } + + /* Now TYPE has the actual type. */ + + /* If this is declaring a typedef name, return a TYPE_DECL. */ + + if (specbits & (1 << (int) RID_TYPEDEF)) + { + /* Note that the grammar rejects storage classes + in typenames, fields or parameters */ + if (constp || volatilep) + type = c_build_type_variant (type, constp, volatilep); + if (resume_temporary) + resume_temporary_allocation (); + return build_decl (TYPE_DECL, declarator, type); + } + + /* Detect the case of an array type of unspecified size + which came, as such, direct from a typedef name. + We must copy the type, so that each identifier gets + a distinct type, so that each identifier's size can be + controlled separately by its own initializer. */ + + if (type != 0 && typedef_type != 0 + && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (typedef_type) + && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == 0) + type = build_array_type (TREE_TYPE (type), 0); + + /* If this is a type name (such as, in a cast or sizeof), + compute the type and return it now. */ + + if (decl_context == TYPENAME) + { + /* Note that the grammar rejects storage classes + in typenames, fields or parameters */ + if (constp || volatilep) + type = c_build_type_variant (type, constp, volatilep); + if (resume_temporary) + resume_temporary_allocation (); + return type; + } + + /* `void' at top level (not within pointer) + is allowed only in typedefs or type names. + We don't complain about parms either, but that is because + a better error message can be made later. */ + + if (type == void_type_node && decl_context != PARM) + { + error ("variable or field `%s' declared void", + IDENTIFIER_POINTER (declarator)); + type = integer_type_node; + } + + /* Now create the decl, which may be a VAR_DECL, a PARM_DECL + or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */ + + { + register tree decl; + + if (decl_context == PARM) + { + /* A parameter declared as an array of T is really a pointer to T. + One declared as a function is really a pointer to a function. */ + + if (TREE_CODE (type) == ARRAY_TYPE) + { + /* Transfer const-ness of array into that of type pointed to. */ + type = build_pointer_type + (c_build_type_variant (TREE_TYPE (type), constp, volatilep)); + volatilep = constp = 0; + } + else if (TREE_CODE (type) == FUNCTION_TYPE) + { + if (pedantic && (constp || volatilep)) + warning ("ANSI C forbids const or volatile function types"); + type = build_pointer_type (c_build_type_variant (type, constp, volatilep)); + volatilep = constp = 0; + } + + if (initialized) + error ("parameter `%s' is initialized", name); + + decl = build_decl (PARM_DECL, declarator, type); + + /* Compute the type actually passed in the parmlist, + for the case where there is no prototype. + (For example, shorts and chars are passed as ints.) + When there is a prototype, this is overridden later. */ + + DECL_ARG_TYPE (decl) = type; + if (type == float_type_node) + DECL_ARG_TYPE (decl) = double_type_node; + else if (TREE_CODE (type) == INTEGER_TYPE + /* ANSI C says short and char are promoted to int + or unsigned int, even if that is not wider. */ + && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) + || type == short_integer_type_node + || type == short_unsigned_type_node)) + { + if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node) + && TREE_UNSIGNED (type)) + DECL_ARG_TYPE (decl) = unsigned_type_node; + else + DECL_ARG_TYPE (decl) = integer_type_node; + } + } + else if (decl_context == FIELD) + { + /* Structure field. It may not be a function. */ + + if (TREE_CODE (type) == FUNCTION_TYPE) + { + error ("field `%s' declared as a function", + IDENTIFIER_POINTER (declarator)); + type = build_pointer_type (type); + } + else if (TREE_CODE (type) != ERROR_MARK && TYPE_SIZE (type) == 0) + { + error ("field `%s' has incomplete type", + IDENTIFIER_POINTER (declarator)); + type = error_mark_node; + } + /* Move type qualifiers down to element of an array. */ + if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep)) + { + type = c_build_type_variant (type, constp, volatilep); + constp = volatilep = 0; + } + /* Note that the grammar rejects storage classes + in typenames, fields or parameters */ + decl = build_decl (FIELD_DECL, declarator, type); + } + else if (TREE_CODE (type) == FUNCTION_TYPE) + { + if (specbits & ((1 << (int) RID_AUTO) | (1 << (int) RID_REGISTER))) + error ("invalid storage class for function `%s'", + IDENTIFIER_POINTER (declarator)); + /* Function declaration not at top level. + Storage classes other than `extern' are not allowed + and `extern' makes no difference. */ + if (current_binding_level != global_binding_level + && (specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_INLINE))) + && pedantic) + warning ("invalid storage class for function `%s'", + IDENTIFIER_POINTER (declarator)); + decl = build_decl (FUNCTION_DECL, declarator, type); + + TREE_EXTERNAL (decl) = 1; + /* Record presence of `static'. */ + TREE_PUBLIC (decl) = !(specbits & (1 << (int) RID_STATIC)); + /* Record presence of `inline', if it is reasonable. */ + if (inlinep) + { + tree last = tree_last (TYPE_ARG_TYPES (type)); + + if (! strcmp (IDENTIFIER_POINTER (declarator), "main")) + warning ("cannot inline function `main'"); + else if (last && TREE_VALUE (last) != void_type_node) + warning ("inline declaration ignored for function with `...'"); + else + /* Assume that otherwise the function can be inlined. */ + TREE_INLINE (decl) = 1; + + if (specbits & (1 << (int) RID_EXTERN)) + current_extern_inline = 1; + } + } + else + { + /* It's a variable. */ + + /* Move type qualifiers down to element of an array. */ + if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep)) + { + type = c_build_type_variant (type, constp, volatilep); +#if 0 /* but a variable whose type is const should still have TREE_READONLY. */ + constp = volatilep = 0; +#endif + } + + decl = build_decl (VAR_DECL, declarator, type); + + if (inlinep) + warning_with_decl (decl, "variable `%s' declared `inline'"); + + /* An uninitialized decl with `extern' is a reference. */ + TREE_EXTERNAL (decl) + = !initialized && (specbits & (1 << (int) RID_EXTERN)); + /* At top level, either `static' or no s.c. makes a definition + (perhaps tentative), and absence of `static' makes it public. */ + if (current_binding_level == global_binding_level) + { + TREE_PUBLIC (decl) = !(specbits & (1 << (int) RID_STATIC)); + TREE_STATIC (decl) = ! TREE_EXTERNAL (decl); + } + /* Not at top level, only `static' makes a static definition. */ + else + { + TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0; + TREE_PUBLIC (decl) = TREE_EXTERNAL (decl); + /* `extern' with initialization is invalid if not at top level. */ + if ((specbits & (1 << (int) RID_EXTERN)) && initialized) + error ("`%s' has both `extern' and initializer", name); + } + } + + /* Record `register' declaration for warnings on & + and in case doing stupid register allocation. */ + + if (specbits & (1 << (int) RID_REGISTER)) + TREE_REGDECL (decl) = 1; + + /* Record constancy and volatility. */ + + if (constp) + TREE_READONLY (decl) = 1; + if (volatilep) + { + TREE_VOLATILE (decl) = 1; + TREE_THIS_VOLATILE (decl) = 1; + } + + if (resume_temporary) + resume_temporary_allocation (); + + return decl; + } +} + +/* Make a variant type in the proper way for C, propagating qualifiers + down to the element type of an array. */ + +tree +c_build_type_variant (type, constp, volatilep) + tree type; + int constp, volatilep; +{ + if (TREE_CODE (type) != ARRAY_TYPE) + return build_type_variant (type, constp, volatilep); + + return build_array_type (c_build_type_variant (TREE_TYPE (type), + constp, volatilep), + TYPE_DOMAIN (type)); +} + +/* Decode the parameter-list info for a function type or function definition. + The argument is the value returned by `get_parm_info' (or made in parse.y + if there is an identifier list instead of a parameter decl list). + These two functions are separate because when a function returns + or receives functions then each is called multiple times but the order + of calls is different. The last call to `grokparms' is always the one + that contains the formal parameter names of a function definition. + + Store in `last_function_parms' a chain of the decls of parms. + Also store in `last_function_parm_tags' a chain of the struct and union + tags declared among the parms. + + Return a list of arg types to use in the FUNCTION_TYPE for this function. + + FUNCDEF_FLAG is nonzero for a function definition, 0 for + a mere declaration. A nonempty identifier-list gets an error message + when FUNCDEF_FLAG is zero. */ + +static tree +grokparms (parms_info, funcdef_flag) + tree parms_info; + int funcdef_flag; +{ + tree first_parm = TREE_CHAIN (parms_info); + + last_function_parms = TREE_PURPOSE (parms_info); + last_function_parm_tags = TREE_VALUE (parms_info); + + if (warn_strict_prototypes && first_parm == 0 && !funcdef_flag) + warning ("function declaration isn't a prototype"); + + if (first_parm != 0 + && TREE_CODE (TREE_VALUE (first_parm)) == IDENTIFIER_NODE) + { + if (! funcdef_flag) + warning ("parameter names (without types) in function declaration"); + + last_function_parms = first_parm; + return 0; + } + else + { + tree parm; + tree typelt; + /* We no longer test FUNCDEF_FLAG. + If the arg types are incomplete in a declaration, + they must include undefined tags. + These tags can never be defined in the scope of the declaration, + so the types can never be completed, + and no call can be compiled successfully. */ +#if 0 + /* In a fcn definition, arg types must be complete. */ + if (funcdef_flag) +#endif + for (parm = last_function_parms, typelt = first_parm; + parm; + parm = TREE_CHAIN (parm)) + /* Skip over any enumeration constants declared here. */ + if (TREE_CODE (parm) == PARM_DECL) + { + /* Barf if the parameter itself has an incomplete type. */ + tree type = TREE_VALUE (typelt); + if (TYPE_SIZE (type) == 0) + { + if (funcdef_flag && DECL_NAME (parm) != 0) + error ("parameter `%s' has incomplete type", + IDENTIFIER_POINTER (DECL_NAME (parm))); + else + warning ("parameter has incomplete type"); + if (funcdef_flag) + { + TREE_VALUE (typelt) = error_mark_node; + TREE_TYPE (parm) = error_mark_node; + } + } +#if 0 /* This has been replaced by parm_tags_warning + which uses a more accurate criterion for what to warn about. */ + else + { + /* Now warn if is a pointer to an incomplete type. */ + while (TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + type = TYPE_MAIN_VARIANT (type); + if (TYPE_SIZE (type) == 0) + { + if (DECL_NAME (parm) != 0) + warning ("parameter `%s' points to incomplete type", + IDENTIFIER_POINTER (DECL_NAME (parm))); + else + warning ("parameter points to incomplete type"); + } + } +#endif + typelt = TREE_CHAIN (typelt); + } + + return first_parm; + } +} + + +/* Return a tree_list node with info on a parameter list just parsed. + The TREE_PURPOSE is a chain of decls of those parms. + The TREE_VALUE is a list of structure, union and enum tags defined. + The TREE_CHAIN is a list of argument types to go in the FUNCTION_TYPE. + This tree_list node is later fed to `grokparms'. + + VOID_AT_END nonzero means append `void' to the end of the type-list. + Zero means the parmlist ended with an ellipsis so don't append `void'. */ + +tree +get_parm_info (void_at_end) + int void_at_end; +{ + register tree decl; + register tree types = 0; + int erred = 0; + tree tags = gettags (); + tree parms = nreverse (getdecls ()); + + /* Just `void' (and no ellipsis) is special. There are really no parms. */ + if (void_at_end && parms != 0 + && TREE_CHAIN (parms) == 0 + && TREE_TYPE (parms) == void_type_node + && DECL_NAME (parms) == 0) + { + parms = NULL_TREE; + storedecls (NULL_TREE); + return saveable_tree_cons (NULL_TREE, NULL_TREE, + saveable_tree_cons (NULL_TREE, void_type_node, NULL_TREE)); + } + + storedecls (parms); + + for (decl = parms; decl; decl = TREE_CHAIN (decl)) + /* There may also be declarations for enumerators if an enumeration + type is declared among the parms. Ignore them here. */ + if (TREE_CODE (decl) == PARM_DECL) + { + /* Since there is a prototype, + args are passed in their declared types. */ + tree type = TREE_TYPE (decl); + DECL_ARG_TYPE (decl) = type; +#ifdef PROMOTE_PROTOTYPES + if (TREE_CODE (type) == INTEGER_TYPE + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) + DECL_ARG_TYPE (decl) = integer_type_node; +#endif + + types = saveable_tree_cons (NULL_TREE, TREE_TYPE (decl), types); + if (TREE_VALUE (types) == void_type_node && ! erred + && DECL_NAME (decl) == 0) + { + error ("`void' in parameter list must be the entire list"); + erred = 1; + } + } + + if (void_at_end) + return saveable_tree_cons (parms, tags, + nreverse (saveable_tree_cons (NULL_TREE, void_type_node, types))); + + return saveable_tree_cons (parms, tags, nreverse (types)); +} + +/* At end of parameter list, warn about any struct, union or enum tags + defined within. Do so because these types cannot ever become complete. */ + +void +parmlist_tags_warning () +{ + tree elt; + static int already; + + for (elt = current_binding_level->tags; elt; elt = TREE_CHAIN (elt)) + { + enum tree_code code = TREE_CODE (TREE_VALUE (elt)); + warning ("`%s %s' declared inside parameter list", + (code == RECORD_TYPE ? "struct" + : code == UNION_TYPE ? "union" + : "enum"), + IDENTIFIER_POINTER (TREE_PURPOSE (elt))); + if (! already) + { + warning ("its scope is only this definition or declaration,"); + warning ("which is probably not what you want."); + already = 1; + } + } +} + +/* Get the struct, enum or union (CODE says which) with tag NAME. + Define the tag as a forward-reference if it is not defined. */ + +tree +xref_tag (code, name) + enum tree_code code; + tree name; +{ + int temporary = allocation_temporary_p (); + + /* If a cross reference is requested, look up the type + already defined for this tag and return it. */ + + register tree ref = lookup_tag (code, name, current_binding_level, 0); + if (ref) return ref; + + if (current_binding_level == global_binding_level && temporary) + end_temporary_allocation (); + + /* If no such tag is yet defined, create a forward-reference node + and record it as the "definition". + When a real declaration of this type is found, + the forward-reference will be altered into a real type. */ + + ref = make_node (code); + if (code == ENUMERAL_TYPE) + { + /* (In ANSI, Enums can be referred to only if already defined.) */ + if (pedantic) + warning ("ANSI C forbids forward references to `enum' types"); + /* Give the type a default layout like unsigned int + to avoid crashing if it does not get defined. */ + TYPE_MODE (ref) = SImode; + TYPE_ALIGN (ref) = TYPE_ALIGN (unsigned_type_node); + TREE_UNSIGNED (ref) = 1; + TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node); + TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node); + TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node); + } + + pushtag (name, ref); + + if (current_binding_level == global_binding_level && temporary) + resume_temporary_allocation (); + + return ref; +} + +/* Make sure that the tag NAME is defined *in the current binding level* + at least as a forward reference. + CODE says which kind of tag NAME ought to be. */ + +tree +start_struct (code, name) + enum tree_code code; + tree name; +{ + /* If there is already a tag defined at this binding level + (as a forward reference), just return it. */ + + register tree ref = 0; + + if (name != 0) + ref = lookup_tag (code, name, current_binding_level, 1); + if (ref && TREE_CODE (ref) == code) + { + if (TYPE_FIELDS (ref)) + error ((code == UNION_TYPE ? "redefinition of `union %s'" + : "redefinition of `struct %s'"), + IDENTIFIER_POINTER (name)); + + return ref; + } + + /* Otherwise create a forward-reference just so the tag is in scope. */ + + ref = make_node (code); + pushtag (name, ref); + return ref; +} + +/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted) + of a structure component, returning a FIELD_DECL node. + WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node. + + This is done during the parsing of the struct declaration. + The FIELD_DECL nodes are chained together and the lot of them + are ultimately passed to `build_struct' to make the RECORD_TYPE node. */ + +tree +grokfield (filename, line, declarator, declspecs, width) + char *filename; + int line; + tree declarator, declspecs, width; +{ + register tree value = grokdeclarator (declarator, declspecs, FIELD, 0); + + finish_decl (value, NULL, NULL); + DECL_INITIAL (value) = width; + + return value; +} + +/* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T. + FIELDLIST is a chain of FIELD_DECL nodes for the fields. */ + +tree +finish_struct (t, fieldlist) + register tree t, fieldlist; +{ + register tree x; + int old_momentary; + int round_up_size = 1; + + /* If this type was previously laid out as a forward reference, + make sure we lay it out again. */ + + TYPE_SIZE (t) = 0; + + if (extra_warnings && in_parm_level_p ()) + warning ((TREE_CODE (t) == UNION_TYPE ? "union defined inside parms" + : "structure defined inside parms")); + + old_momentary = suspend_momentary (); + + if (fieldlist == 0 && pedantic) + warning ((TREE_CODE (t) == UNION_TYPE ? "union has no members" + : "structure has no members")); + + /* Install struct as DECL_CONTEXT of each field decl. + Also process specified field sizes. + Set DECL_SIZE_UNIT to the specified size, or 0 if none specified. + The specified size is found in the DECL_INITIAL. + Store 0 there, except for ": 0" fields (so we can find them + and delete them, below). */ + + for (x = fieldlist; x; x = TREE_CHAIN (x)) + { + DECL_CONTEXT (x) = t; + DECL_SIZE_UNIT (x) = 0; + + /* If any field is const, the structure type is pseudo-const. */ + if (TREE_READONLY (x)) + C_TYPE_FIELDS_READONLY (t) = 1; + else + { + /* A field that is pseudo-const makes the structure likewise. */ + tree t1 = TREE_TYPE (x); + while (TREE_CODE (t1) == ARRAY_TYPE) + t1 = TREE_TYPE (t1); + if ((TREE_CODE (t1) == RECORD_TYPE || TREE_CODE (t1) == UNION_TYPE) + && C_TYPE_FIELDS_READONLY (t1)) + C_TYPE_FIELDS_READONLY (t) = 1; + } + + /* Detect invalid bit-field size. */ + if (DECL_INITIAL (x) && TREE_CODE (DECL_INITIAL (x)) != INTEGER_CST) + { + error_with_decl (x, "bit-field `%s' width not an integer constant"); + DECL_INITIAL (x) = NULL; + } + + /* Detect invalid bit-field type. */ + if (DECL_INITIAL (x) + && TREE_CODE (TREE_TYPE (x)) != INTEGER_TYPE + && TREE_CODE (TREE_TYPE (x)) != ENUMERAL_TYPE) + { + error_with_decl (x, "bit-field `%s' has invalid type"); + DECL_INITIAL (x) = NULL; + } + if (DECL_INITIAL (x) && pedantic + && TREE_TYPE (x) != integer_type_node + && TREE_TYPE (x) != unsigned_type_node) + warning_with_decl (x, "bit-field `%s' type invalid in ANSI C"); + + /* Detect and ignore out of range field width. */ + if (DECL_INITIAL (x)) + { + register int width = TREE_INT_CST_LOW (DECL_INITIAL (x)); + + if (width < 0) + { + DECL_INITIAL (x) = NULL; + warning_with_decl (x, "negative width in bit-field `%s'"); + } + else if (width == 0 && DECL_NAME (x) != 0) + { + error_with_decl (x, "zero width for bit-field `%s'"); + DECL_INITIAL (x) = NULL; + } + else if (width > TYPE_PRECISION (TREE_TYPE (x))) + { + DECL_INITIAL (x) = NULL; + warning_with_decl (x, "width of `%s' exceeds its type"); + } + } + + /* Process valid field width. */ + if (DECL_INITIAL (x)) + { + register int width = TREE_INT_CST_LOW (DECL_INITIAL (x)); + + if (width == 0) + { + /* field size 0 => mark following field as "aligned" */ + if (TREE_CHAIN (x)) + DECL_ALIGN (TREE_CHAIN (x)) + = MAX (DECL_ALIGN (TREE_CHAIN (x)), EMPTY_FIELD_BOUNDARY); + /* field of size 0 at the end => round up the size. */ + else + round_up_size = EMPTY_FIELD_BOUNDARY; + } + else + { + DECL_INITIAL (x) = NULL; + DECL_SIZE_UNIT (x) = width; + TREE_PACKED (x) = 1; + /* Traditionally a bit field is unsigned + even if declared signed. */ + if (flag_traditional + && TREE_CODE (TREE_TYPE (x)) == INTEGER_TYPE) + TREE_TYPE (x) = unsigned_type_node; + } + } + else + /* Non-bit-fields are aligned for their type. */ + DECL_ALIGN (x) = MAX (DECL_ALIGN (x), TYPE_ALIGN (TREE_TYPE (x))); + } + + /* Now DECL_INITIAL is null on all members except for zero-width bit-fields. + And they have already done their work. */ + + /* Delete all zero-width bit-fields from the front of the fieldlist */ + while (fieldlist + && DECL_INITIAL (fieldlist)) + fieldlist = TREE_CHAIN (fieldlist); + /* Delete all such members from the rest of the fieldlist */ + for (x = fieldlist; x;) + { + if (TREE_CHAIN (x) && DECL_INITIAL (TREE_CHAIN (x))) + TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x)); + else x = TREE_CHAIN (x); + } + + /* Delete all duplicate fields from the fieldlist */ + for (x = fieldlist; x && TREE_CHAIN (x);) + /* Anonymous fields aren't duplicates. */ + if (DECL_NAME (TREE_CHAIN (x)) == 0) + x = TREE_CHAIN (x); + else + { + register tree y = fieldlist; + + while (1) + { + if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x))) + break; + if (y == x) + break; + y = TREE_CHAIN (y); + } + if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x))) + { + error_with_decl (TREE_CHAIN (x), "duplicate member `%s'"); + TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x)); + } + else x = TREE_CHAIN (x); + } + + /* Now we have the final fieldlist. Record it, + then lay out the structure or union (including the fields). */ + + TYPE_FIELDS (t) = fieldlist; + + /* If there's a :0 field at the end, round the size to the + EMPTY_FIELD_BOUNDARY. */ + TYPE_ALIGN (t) = round_up_size; + + for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x)) + { + TYPE_FIELDS (x) = TYPE_FIELDS (t); + TYPE_ALIGN (x) = TYPE_ALIGN (t); + } + + layout_type (t); + + /* Promote each bit-field's type to int if it is narrower than that. */ + for (x = fieldlist; x; x = TREE_CHAIN (x)) + if (TREE_PACKED (x) + && TREE_CODE (TREE_TYPE (x)) == INTEGER_TYPE + && (TREE_INT_CST_LOW (DECL_SIZE (x)) * DECL_SIZE_UNIT (x) + < TYPE_PRECISION (integer_type_node))) + TREE_TYPE (x) = integer_type_node; + + /* If this structure or union completes the type of any previous + variable declaration, lay it out and output its rtl. */ + + if (current_binding_level->n_incomplete != 0) + { + tree decl; + for (decl = current_binding_level->names; decl; decl = TREE_CHAIN (decl)) + { + if (TREE_TYPE (decl) == t + && TREE_CODE (decl) != TYPE_DECL) + { + int toplevel = global_binding_level == current_binding_level; + layout_decl (decl, 0); + rest_of_decl_compilation (decl, 0, toplevel, 0); + if (! toplevel) + expand_decl (decl, NULL_TREE); + --current_binding_level->n_incomplete; + } + else if (TYPE_SIZE (TREE_TYPE (decl)) == 0 + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + { + tree element = TREE_TYPE (decl); + while (TREE_CODE (element) == ARRAY_TYPE) + element = TREE_TYPE (element); + if (element == t) + layout_array_type (TREE_TYPE (decl)); + } + } + } + + resume_momentary (old_momentary); + + return t; +} + +/* Lay out the type T, and its element type, and so on. */ + +static void +layout_array_type (t) + tree t; +{ + if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE) + layout_array_type (TREE_TYPE (t)); + layout_type (t); +} + +/* Begin compiling the definition of an enumeration type. + NAME is its name (or null if anonymous). + Returns the type object, as yet incomplete. + Also records info about it so that build_enumerator + may be used to declare the individual values as they are read. */ + +tree +start_enum (name) + tree name; +{ + register tree enumtype = 0; + + /* If this is the real definition for a previous forward reference, + fill in the contents in the same object that used to be the + forward reference. */ + + if (name != 0) + enumtype = lookup_tag (ENUMERAL_TYPE, name, current_binding_level, 1); + + if (enumtype == 0 || TREE_CODE (enumtype) != ENUMERAL_TYPE) + { + enumtype = make_node (ENUMERAL_TYPE); + pushtag (name, enumtype); + } + + if (TYPE_VALUES (enumtype) != 0) + { + /* This enum is a named one that has been declared already. */ + error ("redeclaration of `enum %s'", IDENTIFIER_POINTER (name)); + + /* Completely replace its old definition. + The old enumerators remain defined, however. */ + TYPE_VALUES (enumtype) = 0; + } + + /* Initially, set up this enum as like `int' + so that we can create the enumerators' declarations and values. + Later on, the precision of the type may be changed and + it may be laid out again. */ + + TYPE_PRECISION (enumtype) = TYPE_PRECISION (integer_type_node); + TYPE_SIZE (enumtype) = 0; + fixup_unsigned_type (enumtype); + + enum_next_value = integer_zero_node; + + return enumtype; +} + +/* After processing and defining all the values of an enumeration type, + install their decls in the enumeration type and finish it off. + ENUMTYPE is the type object and VALUES a list of name-value pairs. + Returns ENUMTYPE. */ + +tree +finish_enum (enumtype, values) + register tree enumtype, values; +{ + register tree pair; + register long maxvalue = 0; + register long minvalue = 0; + register int i; + + if (in_parm_level_p ()) + warning ("enum defined inside parms"); + + TYPE_VALUES (enumtype) = values; + + /* Calculate the maximum value of any enumerator in this type. */ + + for (pair = values; pair; pair = TREE_CHAIN (pair)) + { + int value = TREE_INT_CST_LOW (TREE_VALUE (pair)); + if (pair == values) + minvalue = maxvalue = value; + else + { + if (value > maxvalue) + maxvalue = value; + if (value < minvalue) + minvalue = value; + } + } + + if (flag_short_enums) + { + /* Determine the precision this type needs, lay it out, and define it. */ + + for (i = maxvalue; i; i >>= 1) + TYPE_PRECISION (enumtype)++; + + if (!TYPE_PRECISION (enumtype)) + TYPE_PRECISION (enumtype) = 1; + + /* Cancel the laying out previously done for the enum type, + so that fixup_unsigned_type will do it over. */ + TYPE_SIZE (enumtype) = 0; + + fixup_unsigned_type (enumtype); + } + + TREE_INT_CST_LOW (TYPE_MAX_VALUE (enumtype)) = maxvalue; + + /* An enum can have some negative values; then it is signed. */ + if (minvalue < 0) + { + TREE_INT_CST_LOW (TYPE_MIN_VALUE (enumtype)) = minvalue; + TREE_INT_CST_HIGH (TYPE_MIN_VALUE (enumtype)) = -1; + TREE_UNSIGNED (enumtype) = 0; + } + return enumtype; +} + +/* Build and install a CONST_DECL for one value of the + current enumeration type (one that was begun with start_enum). + Return a tree-list containing the name and its value. + Assignment of sequential values by default is handled here. */ + +tree +build_enumerator (name, value) + tree name, value; +{ + register tree decl; + + /* Validate and default VALUE. */ + + /* Remove no-op casts from the value. */ + while (value != 0 && TREE_CODE (value) == NOP_EXPR) + value = TREE_OPERAND (value, 0); + + if (value != 0 && TREE_CODE (value) != INTEGER_CST) + { + error ("enumerator value for `%s' not integer constant", + IDENTIFIER_POINTER (name)); + value = 0; + } + + /* Default based on previous value. */ + if (value == 0) + value = enum_next_value; + + /* Might as well enforce the ANSI restriction, since + values outside this range don't work in version 1. */ + if (! int_fits_type_p (value, integer_type_node)) + { + error ("enumerator value outside range of `int'"); + value = integer_zero_node; + } + + /* Set basis for default for next value. */ + enum_next_value = build_binary_op_nodefault (PLUS_EXPR, value, + integer_one_node, PLUS_EXPR); + + /* Now create a declaration for the enum value name. */ + + decl = build_decl (CONST_DECL, name, integer_type_node); + DECL_INITIAL (decl) = value; + TREE_TYPE (value) = integer_type_node; + pushdecl (decl); + + return saveable_tree_cons (name, value, NULL); +} + +/* Create the FUNCTION_DECL for a function definition. + DECLSPECS and DECLARATOR are the parts of the declaration; + they describe the function's name and the type it returns, + but twisted together in a fashion that parallels the syntax of C. + + This function creates a binding context for the function body + as well as setting up the FUNCTION_DECL in current_function_decl. + + Returns 1 on success. If the DECLARATOR is not suitable for a function + (it defines a datum instead), we return 0, which tells + yyparse to report a parse error. */ + +int +start_function (declspecs, declarator) + tree declarator, declspecs; +{ + tree decl1, old_decl; + tree restype; + + current_function_returns_value = 0; /* Assume, until we see it does. */ + current_function_returns_null = 0; + warn_about_return_type = 0; + current_extern_inline = 0; + + decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1); + + /* If the declarator is not suitable for a function definition, + cause a syntax error. */ + if (decl1 == 0) + return 0; + + current_function_decl = decl1; + + announce_function (current_function_decl); + + if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl1))) == 0) + { + error ("return-type is an incomplete type"); + /* Make it return void instead. */ + TREE_TYPE (decl1) + = build_function_type (void_type_node, + TYPE_ARG_TYPES (TREE_TYPE (decl1))); + } + + if (warn_about_return_type) + warning ("return-type defaults to `int'"); + + /* Save the parm names or decls from this function's declarator + where store_parm_decls will find them. */ + current_function_parms = last_function_parms; + current_function_parm_tags = last_function_parm_tags; + + /* Make the init_value nonzero so pushdecl knows this is not tentative. + error_mark_node is replaced below (in poplevel) with the LET_STMT. */ + DECL_INITIAL (current_function_decl) = error_mark_node; + + /* If this definition isn't a prototype and we had a prototype declaration + before, copy the arg type info from that prototype. */ + old_decl = lookup_name_current_level (DECL_NAME (current_function_decl)); + if (old_decl != 0 + && TREE_TYPE (TREE_TYPE (current_function_decl)) == TREE_TYPE (TREE_TYPE (old_decl)) + && TYPE_ARG_TYPES (TREE_TYPE (current_function_decl)) == 0) + TREE_TYPE (current_function_decl) = TREE_TYPE (old_decl); + + /* This is a definition, not a reference. + So normally clear TREE_EXTERNAL. + However, `extern inline' acts like a declaration + except for defining how to inline. So set TREE_EXTERNAL in that case. */ + TREE_EXTERNAL (current_function_decl) = current_extern_inline; + + /* This function exists in static storage. + (This does not mean `static' in the C sense!) */ + TREE_STATIC (current_function_decl) = 1; + + /* Record the decl so that the function name is defined. + If we already have a decl for this name, and it is a FUNCTION_DECL, + use the old decl. */ + + current_function_decl = pushdecl (current_function_decl); + + pushlevel (0); + declare_parm_level (); + + make_function_rtl (current_function_decl); + + restype = TREE_TYPE (TREE_TYPE (current_function_decl)); + /* Promote the value to int before returning it. */ + if (TREE_CODE (restype) == INTEGER_TYPE + && TYPE_PRECISION (restype) < TYPE_PRECISION (integer_type_node)) + restype = integer_type_node; + DECL_RESULT_TYPE (current_function_decl) = restype; + DECL_RESULT (current_function_decl) + = build_decl (RESULT_DECL, value_identifier, restype); + + /* Allocate further tree nodes temporarily during compilation + of this function only. */ + temporary_allocation (); + + /* If this fcn was already referenced via a block-scope `extern' decl + (or an implicit decl), propagate certain information about the usage. */ + if (TREE_ADDRESSABLE (DECL_NAME (current_function_decl))) + TREE_ADDRESSABLE (current_function_decl) = 1; + + return 1; +} + +/* Store the parameter declarations into the current function declaration. + This is called after parsing the parameter declarations, before + digesting the body of the function. */ + +void +store_parm_decls () +{ + register tree fndecl = current_function_decl; + register tree parm; + + /* This is either a chain of PARM_DECLs (if a prototype was used) + or a list of IDENTIFIER_NODEs (for an old-fashioned C definition). */ + tree specparms = current_function_parms; + + /* This is a list of types declared among parms in a prototype. */ + tree parmtags = current_function_parm_tags; + + /* This is a chain of PARM_DECLs from old-style parm declarations. */ + register tree parmdecls = getdecls (); + + /* This is a chain of any other decls that came in among the parm + declarations. If a parm is declared with enum {foo, bar} x; + then CONST_DECLs for foo and bar are put here. */ + tree nonparms = 0; + + if (specparms != 0 && TREE_CODE (specparms) != TREE_LIST) + { + /* This case is when the function was defined with an ANSI prototype. + The parms already have decls, so we need not do anything here + except record them as in effect + and complain if any redundant old-style parm decls were written. */ + + register tree next; + tree others = 0; + + if (parmdecls != 0) + error_with_decl (fndecl, + "parm types given both in parmlist and separately"); + + specparms = nreverse (specparms); + for (parm = specparms; parm; parm = next) + { + next = TREE_CHAIN (parm); + if (DECL_NAME (parm) == 0) + error_with_decl (parm, "parameter name omitted"); + else if (TREE_TYPE (parm) == void_type_node) + error_with_decl (parm, "parameter `%s' declared void"); + else if (TREE_CODE (parm) == PARM_DECL) + pushdecl (parm); + else + { + /* If we find an enum constant, put it aside for the moment. */ + TREE_CHAIN (parm) = 0; + others = chainon (others, parm); + } + } + + /* Get the decls in their original chain order + and record in the function. */ + DECL_ARGUMENTS (fndecl) = getdecls (); + + /* Now pushdecl the enum constants. */ + for (parm = others; parm; parm = next) + { + next = TREE_CHAIN (parm); + if (DECL_NAME (parm) == 0) + ; + else if (TREE_TYPE (parm) == void_type_node) + ; + else if (TREE_CODE (parm) != PARM_DECL) + pushdecl (parm); + } + + storetags (chainon (parmtags, gettags ())); + } + else + { + /* SPECPARMS is an identifier list--a chain of TREE_LIST nodes + each with a parm name as the TREE_VALUE. + + PARMDECLS is a list of declarations for parameters. + Warning! It can also contain CONST_DECLs which are not parameters + but are names of enumerators of any enum types + declared among the parameters. + + First match each formal parameter name with its declaration. + Associate decls with the names and store the decls + into the TREE_PURPOSE slots. */ + + for (parm = specparms; parm; parm = TREE_CHAIN (parm)) + { + register tree tail, found = NULL; + + if (TREE_VALUE (parm) == 0) + { + error_with_decl (fndecl, "parameter name missing from parameter list"); + TREE_PURPOSE (parm) = 0; + continue; + } + + /* See if any of the parmdecls specifies this parm by name. + Ignore any enumerator decls. */ + for (tail = parmdecls; tail; tail = TREE_CHAIN (tail)) + if (DECL_NAME (tail) == TREE_VALUE (parm) + && TREE_CODE (tail) == PARM_DECL) + { + found = tail; + break; + } + + /* If declaration already marked, we have a duplicate name. + Complain, and don't use this decl twice. */ + if (found && DECL_CONTEXT (found) != 0) + { + error_with_decl (found, "multiple parameters named `%s'"); + found = 0; + } + + /* If the declaration says "void", complain and ignore it. */ + if (found && TREE_TYPE (found) == void_type_node) + { + error_with_decl (found, "parameter `%s' declared void"); + TREE_TYPE (found) = integer_type_node; + DECL_ARG_TYPE (found) = integer_type_node; + layout_decl (found, 0); + } + + /* If no declaration found, default to int. */ + if (!found) + { + found = build_decl (PARM_DECL, TREE_VALUE (parm), + integer_type_node); + DECL_ARG_TYPE (found) = TREE_TYPE (found); + DECL_SOURCE_LINE (found) = DECL_SOURCE_LINE (fndecl); + DECL_SOURCE_FILE (found) = DECL_SOURCE_FILE (fndecl); + if (extra_warnings) + warning_with_decl (found, "type of `%s' defaults to `int'"); + pushdecl (found); + } + + TREE_PURPOSE (parm) = found; + + /* Mark this decl as "already found" -- see test, above. + It is safe to clobber DECL_CONTEXT temporarily + because the final values are not stored until + the `poplevel' in `finish_function'. */ + DECL_CONTEXT (found) = error_mark_node; + } + + /* Complain about declarations not matched with any names. + Put any enumerator constants onto the list NONPARMS. */ + + nonparms = 0; + for (parm = parmdecls; parm; ) + { + tree next = TREE_CHAIN (parm); + TREE_CHAIN (parm) = 0; + + /* Complain about args with incomplete types. */ + if (TYPE_SIZE (TREE_TYPE (parm)) == 0) + { + error_with_decl (parm, "parameter `%s' has incomplete type"); + TREE_TYPE (parm) = error_mark_node; + } + + if (TREE_CODE (parm) != PARM_DECL) + nonparms = chainon (nonparms, parm); + + else if (DECL_CONTEXT (parm) == 0) + { + error_with_decl (parm, + "declaration for parameter `%s' but no such parameter"); + /* Pretend the parameter was not missing. + This gets us to a standard state and minimizes + further error messages. */ + specparms + = chainon (specparms, + tree_cons (parm, NULL_TREE, NULL_TREE)); + } + + parm = next; + } + + /* Chain the declarations together in the order of the list of names. */ + /* Store that chain in the function decl, replacing the list of names. */ + parm = specparms; + DECL_ARGUMENTS (fndecl) = 0; + { + register tree last; + for (last = 0; parm; parm = TREE_CHAIN (parm)) + if (TREE_PURPOSE (parm)) + { + if (last == 0) + DECL_ARGUMENTS (fndecl) = TREE_PURPOSE (parm); + else + TREE_CHAIN (last) = TREE_PURPOSE (parm); + last = TREE_PURPOSE (parm); + TREE_CHAIN (last) = 0; + DECL_CONTEXT (last) = 0; + } + } + + /* If there was a previous prototype, + set the DECL_ARG_TYPE of each argument according to + the type previously specified, and report any mismatches. */ + + if (TYPE_ARG_TYPES (TREE_TYPE (fndecl))) + { + register tree type; + for (parm = DECL_ARGUMENTS (fndecl), + type = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + parm || (type && TREE_VALUE (type) != void_type_node); + parm = TREE_CHAIN (parm), type = TREE_CHAIN (type)) + { + if (parm == 0 || type == 0 + || TREE_VALUE (type) == void_type_node) + { + error ("number of arguments doesn't match prototype"); + break; + } + /* Type for passing arg must be consistent + with that declared for the arg. */ + if (! comptypes (DECL_ARG_TYPE (parm), TREE_VALUE (type))) + { + error ("argument `%s' doesn't match function prototype", + IDENTIFIER_POINTER (DECL_NAME (parm))); + if (DECL_ARG_TYPE (parm) == integer_type_node + && TREE_VALUE (type) == TREE_TYPE (parm)) + { + error ("a formal parameter type that promotes to `int'"); + error ("can match only `int' in the prototype"); + } + if (DECL_ARG_TYPE (parm) == double_type_node + && TREE_VALUE (type) == TREE_TYPE (parm)) + { + error ("a formal parameter type that promotes to `double'"); + error ("can match only `double' in the prototype"); + } + } + } + } + + /* Now store the final chain of decls for the arguments + as the decl-chain of the current lexical scope. + Put the enumerators in as well, at the front so that + DECL_ARGUMENTS is not modified. */ + + storedecls (chainon (nonparms, DECL_ARGUMENTS (fndecl))); + } + + /* Make sure the binding level for the top of the function body + gets a LET_STMT if there are any in the function. + Otherwise, the dbx output is wrong. */ + + keep_next_if_subblocks = 1; + + /* Initialize the RTL code for the function. */ + + init_function_start (fndecl, input_filename, lineno); + + /* Set up parameters and prepare for return, for the function. */ + + expand_function_start (fndecl, 0); +} + +/* Finish up a function declaration and compile that function + all the way to assembler language output. The free the storage + for the function definition. + + This is called after parsing the body of the function definition. + LINENO is the current line number. */ + +void +finish_function (lineno) + int lineno; +{ + register tree fndecl = current_function_decl; + +/* TREE_READONLY (fndecl) = 1; + This caused &foo to be of type ptr-to-const-function + which then got a warning when stored in a ptr-to-function variable. */ + + poplevel (1, 0, 1); + + /* Must mark the RESULT_DECL as being in this function. */ + + DECL_CONTEXT (DECL_RESULT (fndecl)) = DECL_INITIAL (fndecl); + + /* Obey `register' declarations if `setjmp' is called in this fn. */ + if (flag_traditional && current_function_calls_setjmp) + setjmp_protect (DECL_INITIAL (fndecl)); + + /* Generate rtl for function exit. */ + expand_function_end (input_filename, lineno); + + /* So we can tell if jump_optimize sets it to 1. */ + current_function_returns_null = 0; + + /* Run the optimizers and output the assembler code for this function. */ + rest_of_compilation (fndecl); + + if (TREE_THIS_VOLATILE (fndecl) && current_function_returns_null) + warning ("`volatile' function does return"); + else if (warn_return_type && current_function_returns_null + && TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node) + /* If this function returns non-void and control can drop through, + complain. */ + warning ("control reaches end of non-void function"); + /* With just -W, complain only if function returns both with + and without a value. */ + else if (extra_warnings + && current_function_returns_value && current_function_returns_null) + warning ("this function may return with or without a value"); + + /* Free all the tree nodes making up this function. */ + /* Switch back to allocating nodes permanently + until we start another function. */ + permanent_allocation (); + + if (DECL_SAVED_INSNS (fndecl) == 0) + { + /* Stop pointing to the local nodes about to be freed. */ + /* But DECL_INITIAL must remain nonzero so we know this + was an actual function definition. */ + DECL_INITIAL (fndecl) = error_mark_node; + DECL_ARGUMENTS (fndecl) = 0; + } + + /* Let the error reporting routines know that we're outside a function. */ + current_function_decl = NULL; +} diff --git a/usr/src/usr.bin/gcc/cc1/c-parse.y b/usr/src/usr.bin/gcc/cc1/c-parse.y new file mode 100644 index 0000000000..c0bc6db598 --- /dev/null +++ b/usr/src/usr.bin/gcc/cc1/c-parse.y @@ -0,0 +1,2884 @@ +/*- + * + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + * Modified 1991 by The CSRG at the University of California, Berkeley. + */ + +/* YACC parser for C syntax. + Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* To whomever it may concern: I have heard that such a thing was once +written by AT&T, but I have never seen it. */ + +/* %expect 8 */ + +/* These are the 8 conflicts you should get in parse.output; + the state numbers may vary if minor changes in the grammar are made. + +State 41 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 92 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 99 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 103 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 119 contains 1 shift/reduce conflict. (See comment at component_decl.) +State 183 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 193 contains 1 shift/reduce conflict. (Two ways to recover from error.) +State 199 contains 1 shift/reduce conflict. (Two ways to recover from error.) +*/ + +%{ +#ifndef lint +static char sccsid[] = "@(#)c-parse.y 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +#include "config.h" +#include "tree.h" +#include "input.h" +#include "c-parse.h" +#include "c-tree.h" + +#include +#include + +#ifndef errno +extern int errno; +#endif + +void yyerror (); + +/* Cause the `yydebug' variable to be defined. */ +#define YYDEBUG 1 +%} + +%start program + +%union {long itype; tree ttype; enum tree_code code; } + +/* All identifiers that are not reserved words + and are not declared typedefs in the current block */ +%token IDENTIFIER + +/* All identifiers that are declared typedefs in the current block. + In some contexts, they are treated just like IDENTIFIER, + but they can also serve as typespecs in declarations. */ +%token TYPENAME + +/* Reserved words that specify storage class. + yylval contains an IDENTIFIER_NODE which indicates which one. */ +%token SCSPEC + +/* Reserved words that specify type. + yylval contains an IDENTIFIER_NODE which indicates which one. */ +%token TYPESPEC + +/* Reserved words that qualify type: "const" or "volatile". + yylval contains an IDENTIFIER_NODE which indicates which one. */ +%token TYPE_QUAL + +/* Character or numeric constants. + yylval is the node for the constant. */ +%token CONSTANT + +/* String constants in raw form. + yylval is a STRING_CST node. */ +%token STRING + +/* "...", used for functions with variable arglists. */ +%token ELLIPSIS + +/* the reserved words */ +%token SIZEOF ENUM STRUCT UNION IF ELSE WHILE DO FOR SWITCH CASE DEFAULT +%token BREAK CONTINUE RETURN GOTO ASM TYPEOF ALIGNOF +%token ATTRIBUTE + +/* Add precedence rules to solve dangling else s/r conflict */ +%nonassoc IF +%nonassoc ELSE + +/* Define the operator tokens and their precedences. + The value is an integer because, if used, it is the tree code + to use in the expression made from the operator. */ + +%right ASSIGN '=' +%right '?' ':' +%left OROR +%left ANDAND +%left '|' +%left '^' +%left '&' +%left EQCOMPARE +%left ARITHCOMPARE +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%right UNARY PLUSPLUS MINUSMINUS +%left HYPERUNARY +%left POINTSAT '.' '(' '[' + +%type unop + +%type identifier IDENTIFIER TYPENAME CONSTANT expr nonnull_exprlist exprlist +%type expr_no_commas cast_expr unary_expr primary string STRING +%type typed_declspecs reserved_declspecs +%type typed_typespecs reserved_typespecquals +%type declmods typespec typespecqual_reserved +%type SCSPEC TYPESPEC TYPE_QUAL nonempty_type_quals maybe_type_qual +%type initdecls notype_initdecls initdcl notype_initdcl +%type init initlist maybeasm +%type asm_operands nonnull_asm_operands asm_operand asm_clobbers +%type maybe_attribute attribute_list attrib + +%type compstmt + +%type declarator +%type notype_declarator after_type_declarator +%type parm_declarator + +%type structsp component_decl_list component_decl_list2 +%type component_decl components component_declarator +%type enumlist enumerator +%type typename absdcl absdcl1 type_quals +%type xexpr parms parm identifiers + +%type parmlist parmlist_1 parmlist_2 +%type parmlist_or_identifiers parmlist_or_identifiers_1 + +%type setspecs + +%{ +/* the declaration found for the last IDENTIFIER token read in. + yylex must look this up to detect typedefs, which get token type TYPENAME, + so it is left around in case the identifier is not a typedef but is + used in a context which makes it a reference to a variable. */ +static tree lastiddecl; + +static tree make_pointer_declarator (); +static tree combine_strings (); +static void reinit_parse_for_function (); + +/* List of types and structure classes of the current declaration. */ +tree current_declspecs; + +/* Stack of saved values of current_declspecs. */ +tree declspec_stack; + +int undeclared_variable_notice; /* 1 if we explained undeclared var errors. */ + +static int yylex (); +%} + +%% +program: /* empty */ + | extdefs + ; + +/* the reason for the strange actions in this rule + is so that notype_initdecls when reached via datadef + can find a valid list of type and sc specs in $0. */ + +extdefs: + {$$ = NULL_TREE; } extdef + | extdefs {$$ = NULL_TREE; } extdef + ; + +extdef: + fndef + | datadef + | ASM '(' string ')' ';' + { if (pedantic) + warning ("ANSI C forbids use of `asm' keyword"); + if (TREE_CHAIN ($3)) $3 = combine_strings ($3); + assemble_asm ($3); } + ; + +datadef: + setspecs notype_initdecls ';' + { if (pedantic) + error ("ANSI C forbids data definition lacking type or storage class"); + else if (!flag_traditional) + warning ("data definition lacks type or storage class"); } + | declmods setspecs notype_initdecls ';' + {} + | typed_declspecs setspecs initdecls ';' + {} + | declmods ';' + { error ("empty declaration"); } + | typed_declspecs ';' + { shadow_tag ($1); } + | error ';' + | error '}' + | ';' + { if (pedantic) + warning ("ANSI C does not allow extra `;' outside of a function"); } + ; + +fndef: + typed_declspecs setspecs declarator + { if (! start_function ($1, $3)) + YYERROR; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (lineno); } + | typed_declspecs setspecs declarator error + { } + | declmods setspecs notype_declarator + { if (! start_function ($1, $3)) + YYERROR; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (lineno); } + | declmods setspecs notype_declarator error + { } + | setspecs notype_declarator + { if (! start_function (0, $2)) + YYERROR; + reinit_parse_for_function (); } + xdecls + { store_parm_decls (); } + compstmt_or_error + { finish_function (lineno); } + | setspecs notype_declarator error + { } + ; + +identifier: + IDENTIFIER + | TYPENAME + ; + +unop: '&' + { $$ = ADDR_EXPR; } + | '-' + { $$ = NEGATE_EXPR; } + | '+' + { $$ = CONVERT_EXPR; } + | PLUSPLUS + { $$ = PREINCREMENT_EXPR; } + | MINUSMINUS + { $$ = PREDECREMENT_EXPR; } + | '~' + { $$ = BIT_NOT_EXPR; } + | '!' + { $$ = TRUTH_NOT_EXPR; } + ; + +expr: nonnull_exprlist + { $$ = build_compound_expr ($1); } + ; + +exprlist: + /* empty */ + { $$ = NULL_TREE; } + | nonnull_exprlist + ; + +nonnull_exprlist: + expr_no_commas + { $$ = build_tree_list (NULL_TREE, $1); } + | nonnull_exprlist ',' expr_no_commas + { chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; + +unary_expr: + primary + | '*' cast_expr %prec UNARY + { $$ = build_indirect_ref ($2, "unary *"); } + | unop cast_expr %prec UNARY + { $$ = build_unary_op ($1, $2, 0); } + | SIZEOF unary_expr %prec UNARY + { if (TREE_CODE ($2) == COMPONENT_REF + && TREE_PACKED (TREE_OPERAND ($2, 1))) + error ("`sizeof' applied to a bit-field"); + /* ANSI says arrays and functions are converted inside comma. + But we can't really convert them in build_compound_expr + because that would break commas in lvalues. + So do the conversion here if operand was a comma. */ + if (TREE_CODE ($2) == COMPOUND_EXPR + && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE)) + $2 = default_conversion ($2); + $$ = c_sizeof (TREE_TYPE ($2)); } + | SIZEOF '(' typename ')' %prec HYPERUNARY + { $$ = c_sizeof (groktypename ($3)); } + | ALIGNOF unary_expr %prec UNARY + { if (TREE_CODE ($2) == COMPONENT_REF + && TREE_PACKED (TREE_OPERAND ($2, 1))) + error ("`__alignof' applied to a bit-field"); + if (TREE_CODE ($2) == INDIRECT_REF) + { + tree t = TREE_OPERAND ($2, 0); + tree best = t; + int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); + while (TREE_CODE (t) == NOP_EXPR + && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE) + { + int thisalign; + t = TREE_OPERAND (t, 0); + thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t))); + if (thisalign > bestalign) + best = t, bestalign = thisalign; + } + $$ = c_alignof (TREE_TYPE (TREE_TYPE (best))); + } + else + { + /* ANSI says arrays and fns are converted inside comma. + But we can't convert them in build_compound_expr + because that would break commas in lvalues. + So do the conversion here if operand was a comma. */ + if (TREE_CODE ($2) == COMPOUND_EXPR + && (TREE_CODE (TREE_TYPE ($2)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE ($2)) == FUNCTION_TYPE)) + $2 = default_conversion ($2); + $$ = c_alignof (TREE_TYPE ($2)); + } + } + | ALIGNOF '(' typename ')' %prec HYPERUNARY + { $$ = c_alignof (groktypename ($3)); } + ; + +cast_expr: + unary_expr + | '(' typename ')' cast_expr %prec UNARY + { tree type = groktypename ($2); + $$ = build_c_cast (type, $4); } + | '(' typename ')' '{' initlist maybecomma '}' %prec UNARY + { tree type = groktypename ($2); + if (pedantic) + warning ("ANSI C forbids constructor expressions"); + $$ = digest_init (type, build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($5)), 0); + if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0) + { + int failure = complete_array_type (type, $$, 1); + if (failure) + abort (); + } + } + ; + +expr_no_commas: + cast_expr + | expr_no_commas '+' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '-' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '*' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '/' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '%' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas LSHIFT expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas RSHIFT expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas ARITHCOMPARE expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas EQCOMPARE expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '&' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '|' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas '^' expr_no_commas + { $$ = build_binary_op ($2, $1, $3); } + | expr_no_commas ANDAND expr_no_commas + { $$ = build_binary_op (TRUTH_ANDIF_EXPR, $1, $3); } + | expr_no_commas OROR expr_no_commas + { $$ = build_binary_op (TRUTH_ORIF_EXPR, $1, $3); } + | expr_no_commas '?' xexpr ':' expr_no_commas + { $$ = build_conditional_expr ($1, $3, $5); } + | expr_no_commas '=' expr_no_commas + { $$ = build_modify_expr ($1, NOP_EXPR, $3); } + | expr_no_commas ASSIGN expr_no_commas + { $$ = build_modify_expr ($1, $2, $3); } + ; + +primary: + IDENTIFIER + { $$ = lastiddecl; + if (!$$ || $$ == error_mark_node) + { + if (yychar == -1) + yychar = yylex(); + if (yychar == '(') + { + $$ = implicitly_declare ($1); + assemble_external ($$); + TREE_USED ($$) = 1; + } + else if (current_function_decl == 0) + { + error ("`%s' undeclared, outside of functions", + IDENTIFIER_POINTER ($1)); + $$ = error_mark_node; + } + else + { + if (IDENTIFIER_GLOBAL_VALUE ($1) != error_mark_node + || IDENTIFIER_ERROR_LOCUS ($1) != current_function_decl) + { + error ("`%s' undeclared (first use this function)", + IDENTIFIER_POINTER ($1)); + + if (! undeclared_variable_notice) + { + error ("(Each undeclared identifier is reported only once"); + error ("for each function it appears in.)"); + undeclared_variable_notice = 1; + } + } + $$ = error_mark_node; + /* Prevent repeated error messages. */ + IDENTIFIER_GLOBAL_VALUE ($1) = error_mark_node; + IDENTIFIER_ERROR_LOCUS ($1) = current_function_decl; + } + } + else if (! TREE_USED ($$)) + { + if (TREE_EXTERNAL ($$)) + assemble_external ($$); + TREE_USED ($$) = 1; + } + if (TREE_CODE ($$) == CONST_DECL) + $$ = DECL_INITIAL ($$); + } + | CONSTANT + | string + { $$ = combine_strings ($1); } + | '(' expr ')' + { $$ = $2; } + | '(' error ')' + { $$ = error_mark_node; } + | '(' + { if (current_function_decl == 0) + { + error ("braced-group within expression allowed only inside a function"); + YYERROR; + } + keep_next_level (); + $$ = expand_start_stmt_expr (); } + compstmt ')' + { tree rtl_exp; + if (pedantic) + warning ("ANSI C forbids braced-groups within expressions"); + rtl_exp = expand_end_stmt_expr ($2); + $$ = $3; + TREE_USED ($$) = 0; + /* Since the statements have side effects, + consider this volatile. */ + TREE_VOLATILE ($$) = 1; + TREE_TYPE ($$) = TREE_TYPE (rtl_exp); + STMT_BODY ($$) = rtl_exp; } + | primary '(' exprlist ')' %prec '.' + { $$ = build_function_call ($1, $3); } + | primary '[' expr ']' %prec '.' + { $$ = build_array_ref ($1, $3); } + | primary '.' identifier + { $$ = build_component_ref ($1, $3); } + | primary POINTSAT identifier + { $$ = build_component_ref (build_indirect_ref ($1, "->"), $3); } + | primary PLUSPLUS + { $$ = build_unary_op (POSTINCREMENT_EXPR, $1, 0); } + | primary MINUSMINUS + { $$ = build_unary_op (POSTDECREMENT_EXPR, $1, 0); } + ; + +/* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ +string: + STRING + | string STRING + { $$ = chainon ($1, $2); } + ; + +xdecls: + /* empty */ + | decls + ; + +decls: + decl + | errstmt + | decls decl + | decl errstmt + ; + +/* records the type and storage class specs to use for processing + the declarators that follow. + Maintains a stack of outer-level values of current_declspecs, + for the sake of parm declarations nested in function declarators. */ +setspecs: /* empty */ + { $$ = suspend_momentary (); + declspec_stack = tree_cons (0, current_declspecs, + declspec_stack); + current_declspecs = $0; } + ; + +decl: + typed_declspecs setspecs initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | declmods setspecs notype_initdecls ';' + { current_declspecs = TREE_VALUE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | typed_declspecs ';' + { shadow_tag ($1); } + | declmods ';' + { warning ("empty declaration"); } + ; + +/* Declspecs which contain at least one type specifier or typedef name. + (Just `const' or `volatile' is not enough.) + A typedef'd name following these is taken as a name to be declared. */ + +typed_declspecs: + typespec reserved_declspecs + { $$ = tree_cons (NULL_TREE, $1, $2); } + | declmods typespec reserved_declspecs + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_declspecs: /* empty */ + { $$ = NULL_TREE; } + | reserved_declspecs typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + | reserved_declspecs SCSPEC + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* List of just storage classes and type modifiers. + A declaration can start with just this, but then it cannot be used + to redeclare a typedef-name. */ + +declmods: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | SCSPEC + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | declmods TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + | declmods SCSPEC + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + + +/* Used instead of declspecs where storage classes are not allowed + (that is, for typenames and structure components). + Don't accept a typedef-name if anything but a modifier precedes it. */ + +typed_typespecs: + typespec reserved_typespecquals + { $$ = tree_cons (NULL_TREE, $1, $2); } + | nonempty_type_quals typespec reserved_typespecquals + { $$ = chainon ($3, tree_cons (NULL_TREE, $2, $1)); } + ; + +reserved_typespecquals: /* empty */ + { $$ = NULL_TREE; } + | reserved_typespecquals typespecqual_reserved + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +/* A typespec (but not a type qualifier). + Once we have seen one of these in a declaration, + if a typedef name appears then it is being redeclared. */ + +typespec: TYPESPEC + | structsp + | TYPENAME + | TYPEOF '(' expr ')' + { $$ = TREE_TYPE ($3); + if (pedantic) + warning ("ANSI C forbids `typeof'"); } + | TYPEOF '(' typename ')' + { $$ = groktypename ($3); + if (pedantic) + warning ("ANSI C forbids `typeof'"); } + ; + +/* A typespec that is a reserved word, or a type qualifier. */ + +typespecqual_reserved: TYPESPEC + | TYPE_QUAL + | structsp + ; + +initdecls: + initdcl + | initdecls ',' initdcl + ; + +notype_initdecls: + notype_initdcl + | notype_initdecls ',' initdcl + ; + +maybeasm: + /* empty */ + { $$ = NULL_TREE; } + | ASM '(' string ')' + { if (TREE_CHAIN ($3)) $3 = combine_strings ($3); + $$ = $3; + if (pedantic) + warning ("ANSI C forbids use of `asm' keyword"); + } + ; + +initdcl: + declarator maybeasm maybe_attribute '=' + { $$ = start_decl ($1, current_declspecs, 1); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_decl ($5, $6, $2); } + | declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0); + finish_decl (d, NULL_TREE, $2); } + ; + +notype_initdcl: + notype_declarator maybeasm maybe_attribute '=' + { $$ = start_decl ($1, current_declspecs, 1); } + init +/* Note how the declaration of the variable is in effect while its init is parsed! */ + { finish_decl ($5, $6, $2); } + | notype_declarator maybeasm maybe_attribute + { tree d = start_decl ($1, current_declspecs, 0); + finish_decl (d, NULL_TREE, $2); } + ; +/* the * rules are dummies to accept the Apollo extended syntax + so that the header files compile. */ +maybe_attribute: + /* empty */ + { $$ = NULL_TREE; } + | ATTRIBUTE '(' '(' attribute_list ')' ')' + { $$ = $4; } + ; + +attribute_list + : attrib + | attribute_list ',' attrib + ; + +attrib + : IDENTIFIER + { warning ("`%s' attribute directive ignored", + IDENTIFIER_POINTER ($1)); + $$ = $1; } + | IDENTIFIER '(' CONSTANT ')' + { /* if not "aligned(1)", then issue warning */ + if (strcmp (IDENTIFIER_POINTER ($1), "aligned") != 0 + || TREE_CODE ($3) != INTEGER_CST + || TREE_INT_CST_LOW ($3) != 1) + warning ("`%s' attribute directive ignored", + IDENTIFIER_POINTER ($1)); + $$ = $1; } + | IDENTIFIER '(' identifiers ')' + { warning ("`%s' attribute directive ignored", + IDENTIFIER_POINTER ($1)); + $$ = $1; } + ; + +init: + expr_no_commas + | '{' '}' + { $$ = build_nt (CONSTRUCTOR, NULL_TREE, NULL_TREE); + if (pedantic) + warning ("ANSI C forbids empty initializer braces"); } + | '{' initlist '}' + { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2)); } + | '{' initlist ',' '}' + { $$ = build_nt (CONSTRUCTOR, NULL_TREE, nreverse ($2)); } + | error + { $$ = NULL_TREE; } + ; + +/* This chain is built in reverse order, + and put in forward order where initlist is used. */ +initlist: + init + { $$ = build_tree_list (NULL_TREE, $1); } + | initlist ',' init + { $$ = tree_cons (NULL_TREE, $3, $1); } + ; + +/* Any kind of declarator (thus, all declarators allowed + after an explicit typespec). */ + +declarator: + after_type_declarator + | notype_declarator + ; + +/* A declarator that is allowed only after an explicit typespec. */ + +after_type_declarator: + '(' after_type_declarator ')' + { $$ = $2; } + | after_type_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | after_type_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | after_type_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | after_type_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals after_type_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | TYPENAME + ; + +/* Kinds of declarator that can appear in a parameter list + in addition to notype_declarator. This is like after_type_declarator + but does not allow a typedef name in parentheses as an identifier + (because it would conflict with a function with that typedef as arg). */ + +parm_declarator: + parm_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | parm_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | parm_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | parm_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '*' type_quals parm_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | TYPENAME + ; + +/* A declarator allowed whether or not there has been + an explicit typespec. These cannot redeclare a typedef-name. */ + +notype_declarator: + notype_declarator '(' parmlist_or_identifiers %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } +/* | notype_declarator '(' error ')' %prec '.' + { $$ = build_nt (CALL_EXPR, $1, NULL_TREE, NULL_TREE); + poplevel (0, 0, 0); } */ + | '(' notype_declarator ')' + { $$ = $2; } + | '*' type_quals notype_declarator %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | notype_declarator '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | notype_declarator '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | IDENTIFIER + ; + +structsp: + STRUCT identifier '{' + { $$ = start_struct (RECORD_TYPE, $2); + /* Start scope of tag before parsing components. */ + } + component_decl_list '}' + { $$ = finish_struct ($4, $5); + /* Really define the structure. */ + } + | STRUCT '{' component_decl_list '}' + { $$ = finish_struct (start_struct (RECORD_TYPE, NULL_TREE), + $3); } + | STRUCT identifier + { $$ = xref_tag (RECORD_TYPE, $2); } + | UNION identifier '{' + { $$ = start_struct (UNION_TYPE, $2); } + component_decl_list '}' + { $$ = finish_struct ($4, $5); } + | UNION '{' component_decl_list '}' + { $$ = finish_struct (start_struct (UNION_TYPE, NULL_TREE), + $3); } + | UNION identifier + { $$ = xref_tag (UNION_TYPE, $2); } + | ENUM identifier '{' + { $3 = suspend_momentary (); + $$ = start_enum ($2); } + enumlist maybecomma_warn '}' + { $$ = finish_enum ($4, nreverse ($5)); + resume_momentary ($3); } + | ENUM '{' + { $2 = suspend_momentary (); + $$ = start_enum (NULL_TREE); } + enumlist maybecomma_warn '}' + { $$ = finish_enum ($3, nreverse ($4)); + resume_momentary ($2); } + | ENUM identifier + { $$ = xref_tag (ENUMERAL_TYPE, $2); } + ; + +maybecomma: + /* empty */ + | ',' + ; + +maybecomma_warn: + /* empty */ + | ',' + { if (pedantic) warning ("comma at end of enumerator list"); } + ; + +component_decl_list: + component_decl_list2 + { $$ = $1; } + | component_decl_list2 component_decl + { $$ = chainon ($1, $2); + warning ("no semicolon at end of struct or union"); } + ; + +component_decl_list2: /* empty */ + { $$ = NULL_TREE; } + | component_decl_list2 component_decl ';' + { $$ = chainon ($1, $2); } + | component_decl_list2 ';' + { if (pedantic) + warning ("extra semicolon in struct or union specified"); } + ; + +/* There is a shift-reduce conflict here, because `components' may + start with a `typename'. It happens that shifting (the default resolution) + does the right thing, because it treats the `typename' as part of + a `typed_typespecs'. + + It is possible that this same technique would allow the distinction + between `notype_initdecls' and `initdecls' to be eliminated. + But I am being cautious and not trying it. */ + +component_decl: + typed_typespecs setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | nonempty_type_quals setspecs components + { $$ = $3; + current_declspecs = TREE_VALUE (declspec_stack); + declspec_stack = TREE_CHAIN (declspec_stack); + resume_momentary ($2); } + | error + { $$ = NULL_TREE; } + ; + +components: + /* empty */ + { if (pedantic) + warning ("ANSI C forbids member declarations with no members"); + $$ = NULL_TREE; } + | component_declarator + | components ',' component_declarator + { $$ = chainon ($1, $3); } + ; + +component_declarator: + declarator maybe_attribute + { $$ = grokfield (input_filename, lineno, $1, current_declspecs, NULL_TREE); } + | declarator ':' expr_no_commas maybe_attribute + { $$ = grokfield (input_filename, lineno, $1, current_declspecs, $3); } + | ':' expr_no_commas + { $$ = grokfield (input_filename, lineno, NULL_TREE, current_declspecs, $2); } + ; + +/* We chain the enumerators in reverse order. + They are put in forward order where enumlist is used. + (The order used to be significant, but no longer is so. + However, we still maintain the order, just to be clean.) */ + +enumlist: + enumerator + | enumlist ',' enumerator + { $$ = chainon ($3, $1); } + ; + + +enumerator: + identifier + { $$ = build_enumerator ($1, NULL_TREE); } + | identifier '=' expr_no_commas + { $$ = build_enumerator ($1, $3); } + ; + +typename: + typed_typespecs absdcl + { $$ = build_tree_list ($1, $2); } + | nonempty_type_quals absdcl + { $$ = build_tree_list ($1, $2); } + ; + +absdcl: /* an absolute declarator */ + /* empty */ + { $$ = NULL_TREE; } + | absdcl1 + ; + +nonempty_type_quals: + TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $1, NULL_TREE); } + | nonempty_type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +type_quals: + /* empty */ + { $$ = NULL_TREE; } + | type_quals TYPE_QUAL + { $$ = tree_cons (NULL_TREE, $2, $1); } + ; + +absdcl1: /* a nonempty absolute declarator */ + '(' absdcl1 ')' + { $$ = $2; } + /* `(typedef)1' is `int'. */ + | '*' type_quals absdcl1 %prec UNARY + { $$ = make_pointer_declarator ($2, $3); } + | '*' type_quals %prec UNARY + { $$ = make_pointer_declarator ($2, NULL_TREE); } + | absdcl1 '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, $1, $3, NULL_TREE); } + | absdcl1 '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, $3); } + | absdcl1 '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, $1, NULL_TREE); } + | '(' parmlist %prec '.' + { $$ = build_nt (CALL_EXPR, NULL_TREE, $2, NULL_TREE); } + | '[' expr ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, $2); } + | '[' ']' %prec '.' + { $$ = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); } + ; + +/* at least one statement, the first of which parses without error. */ +/* stmts is used only after decls, so an invalid first statement + is actually regarded as an invalid decl and part of the decls. */ + +stmts: + stmt + | stmts stmt + | stmts errstmt + ; + +xstmts: + /* empty */ + | stmts + ; + +errstmt: error ';' + ; + +pushlevel: /* empty */ + { pushlevel (0); + clear_last_expr (); + push_momentary (); + expand_start_bindings (0); } + ; + +/* This is the body of a function definition. + It causes syntax errors to ignore to the next openbrace. */ +compstmt_or_error: + compstmt + {} + | error compstmt + ; + +compstmt: '{' '}' + { $$ = 0; } + | '{' pushlevel decls xstmts '}' + { expand_end_bindings (getdecls (), 1, 0); + $$ = poplevel (1, 1, 0); + pop_momentary (); } + | '{' pushlevel error '}' + { expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + pop_momentary (); } + | '{' pushlevel stmts '}' + { expand_end_bindings (getdecls (), kept_level_p (), 0); + $$ = poplevel (kept_level_p (), 0, 0); + pop_momentary (); } + ; + +simple_if: + IF '(' expr ')' + { emit_line_note (input_filename, lineno); + expand_start_cond (truthvalue_conversion ($3), 0); } + stmt + ; + +stmt: + compstmt {} + | expr ';' + { emit_line_note (input_filename, lineno); + /* Do default conversion if safe and possibly important, + in case within ({...}). */ + if ((TREE_CODE (TREE_TYPE ($1)) == ARRAY_TYPE + && lvalue_p ($1)) + || TREE_CODE (TREE_TYPE ($1)) == FUNCTION_TYPE) + $1 = default_conversion ($1); + expand_expr_stmt ($1); + clear_momentary (); } + | simple_if ELSE + { expand_start_else (); } + stmt + { expand_end_else (); } + | simple_if %prec IF + { expand_end_cond (); } + | WHILE + { emit_nop (); + emit_line_note (input_filename, lineno); + expand_start_loop (1); } + '(' expr ')' + { emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (truthvalue_conversion ($4)); } + stmt + { expand_end_loop (); } + | DO + { emit_nop (); + emit_line_note (input_filename, lineno); + expand_start_loop_continue_elsewhere (1); } + stmt WHILE + { expand_loop_continue_here (); } + '(' expr ')' ';' + { emit_line_note (input_filename, lineno); + expand_exit_loop_if_false (truthvalue_conversion ($7)); + expand_end_loop (); + clear_momentary (); } + | FOR + '(' xexpr ';' + { emit_nop (); + emit_line_note (input_filename, lineno); + if ($3) expand_expr_stmt ($3); + expand_start_loop_continue_elsewhere (1); } + xexpr ';' + { emit_line_note (input_filename, lineno); + if ($6) + expand_exit_loop_if_false (truthvalue_conversion ($6)); } + xexpr ')' + /* Don't let the tree nodes for $9 be discarded + by clear_momentary during the parsing of the next stmt. */ + { push_momentary (); + $10 = lineno; } + stmt + { emit_line_note (input_filename, $10); + expand_loop_continue_here (); + if ($9) + expand_expr_stmt ($9); + pop_momentary (); + expand_end_loop (); } + | SWITCH '(' expr ')' + { emit_line_note (input_filename, lineno); + c_expand_start_case ($3); + /* Don't let the tree nodes for $3 be discarded by + clear_momentary during the parsing of the next stmt. */ + push_momentary (); } + stmt + { expand_end_case ($3); + pop_momentary (); } + | CASE expr ':' + { register tree value = fold ($2); + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + /* build_c_cast puts on a NOP_EXPR to make a non-lvalue. + Strip such NOP_EXPRs. */ + if (TREE_CODE (value) == NOP_EXPR + && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0))) + value = TREE_OPERAND (value, 0); + + if (TREE_CODE (value) != INTEGER_CST + && value != error_mark_node) + { + error ("case label does not reduce to an integer constant"); + value = error_mark_node; + } + else + /* Promote char or short to int. */ + value = default_conversion (value); + if (value != error_mark_node) + { + int success = pushcase (value, label); + if (success == 1) + error ("case label not within a switch statement"); + else if (success == 2) + error ("duplicate case value"); + else if (success == 3) + warning ("case value out of range"); + } + } + stmt + | DEFAULT ':' + { + register tree label + = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + int success = pushcase (NULL_TREE, label); + if (success == 1) + error ("default label not within a switch statement"); + else if (success == 2) + error ("multiple default labels in one switch"); + } + stmt + | BREAK ';' + { emit_line_note (input_filename, lineno); + if ( ! expand_exit_something ()) + error ("break statement not within loop or switch"); } + | CONTINUE ';' + { emit_line_note (input_filename, lineno); + if (! expand_continue_loop ()) + error ("continue statement not within a loop"); } + | RETURN ';' + { emit_line_note (input_filename, lineno); + c_expand_return (NULL_TREE); } + | RETURN expr ';' + { emit_line_note (input_filename, lineno); + c_expand_return ($2); } + | ASM maybe_type_qual '(' string ')' ';' + { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); + emit_line_note (input_filename, lineno); + expand_asm ($4); } + /* This is the case with just output operands. */ + | ASM maybe_type_qual '(' string ':' asm_operands ')' ';' + { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); + emit_line_note (input_filename, lineno); + c_expand_asm_operands ($4, $6, NULL_TREE, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with input operands as well. */ + | ASM maybe_type_qual '(' string ':' asm_operands ':' asm_operands ')' ';' + { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); + emit_line_note (input_filename, lineno); + c_expand_asm_operands ($4, $6, $8, NULL_TREE, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + /* This is the case with clobbered registers as well. */ + | ASM maybe_type_qual '(' string ':' asm_operands ':' + asm_operands ':' asm_clobbers ')' ';' + { if (TREE_CHAIN ($4)) $4 = combine_strings ($4); + emit_line_note (input_filename, lineno); + c_expand_asm_operands ($4, $6, $8, $10, + $2 == ridpointers[(int)RID_VOLATILE], + input_filename, lineno); } + | GOTO identifier ';' + { tree decl; + emit_line_note (input_filename, lineno); + decl = lookup_label ($2); + TREE_USED (decl) = 1; + expand_goto (decl); } + | identifier ':' + { tree label = define_label (input_filename, lineno, $1); + emit_nop (); + if (label) + expand_label (label); } + stmt + | ';' + ; + +/* Either a type-qualifier or nothing. First thing in an `asm' statement. */ + +maybe_type_qual: + /* empty */ + { if (pedantic) + warning ("ANSI C forbids use of `asm' keyword"); + emit_line_note (input_filename, lineno); } + | TYPE_QUAL + { if (pedantic) + warning ("ANSI C forbids use of `asm' keyword"); + emit_line_note (input_filename, lineno); } + ; + +xexpr: + /* empty */ + { $$ = NULL_TREE; } + | expr + ; + +/* These are the operands other than the first string and colon + in asm ("addextend %2,%1": "=dm" (x), "0" (y), "g" (*x)) */ +asm_operands: /* empty */ + { $$ = NULL_TREE; } + | nonnull_asm_operands + ; + +nonnull_asm_operands: + asm_operand + | nonnull_asm_operands ',' asm_operand + { $$ = chainon ($1, $3); } + ; + +asm_operand: + STRING '(' expr ')' + { $$ = build_tree_list ($1, $3); } + ; + +asm_clobbers: + string + { $$ = tree_cons (NULL_TREE, combine_strings ($1), NULL_TREE); } + | asm_clobbers ',' string + { $$ = tree_cons (NULL_TREE, combine_strings ($3), $1); } + ; + +/* This is what appears inside the parens in a function declarator. + Its value is a list of ..._TYPE nodes. */ +parmlist: + { pushlevel (0); + declare_parm_level (); } + parmlist_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +/* This is referred to where either a parmlist or an identifier list is ok. + Its value is a list of ..._TYPE nodes or a list of identifiers. */ +parmlist_or_identifiers: + { pushlevel (0); + declare_parm_level (); } + parmlist_or_identifiers_1 + { $$ = $2; + parmlist_tags_warning (); + poplevel (0, 0, 0); } + ; + +parmlist_or_identifiers_1: + parmlist_2 ')' + | identifiers ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, $1); } + | error ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } + ; + +parmlist_1: + parmlist_2 ')' + | error ')' + { $$ = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); } + ; + +/* This is what appears inside the parens in a function declarator. + Is value is represented in the format that grokdeclarator expects. */ +parmlist_2: /* empty */ + { $$ = get_parm_info (0); } + | parms + { $$ = get_parm_info (1); } + | parms ',' ELLIPSIS + { $$ = get_parm_info (0); } + ; + +parms: + parm + { push_parm_decl ($1); } + | parms ',' parm + { push_parm_decl ($3); } + ; + +/* A single parameter declaration or parameter type name, + as found in a parmlist. */ +parm: + typed_declspecs parm_declarator + { $$ = build_tree_list ($1, $2) ; } + | typed_declspecs notype_declarator + { $$ = build_tree_list ($1, $2) ; } + | typed_declspecs absdcl + { $$ = build_tree_list ($1, $2); } + | declmods notype_declarator + { $$ = build_tree_list ($1, $2) ; } + | declmods absdcl + { $$ = build_tree_list ($1, $2); } + ; + +/* A nonempty list of identifiers. */ +identifiers: + IDENTIFIER + { $$ = build_tree_list (NULL_TREE, $1); } + | identifiers ',' IDENTIFIER + { $$ = chainon ($1, build_tree_list (NULL_TREE, $3)); } + ; +%% + +/* Return something to represent absolute declarators containing a *. + TARGET is the absolute declarator that the * contains. + TYPE_QUALS is a list of modifiers such as const or volatile + to apply to the pointer type, represented as identifiers. + + We return an INDIRECT_REF whose "contents" are TARGET + and whose type is the modifier list. */ + +static tree +make_pointer_declarator (type_quals, target) + tree type_quals, target; +{ + return build (INDIRECT_REF, type_quals, target); +} + +/* Given a chain of STRING_CST nodes, + concatenate them into one STRING_CST + and give it a suitable array-of-chars data type. */ + +static tree +combine_strings (strings) + tree strings; +{ + register tree value, t; + register int length = 1; + int wide_length = 0; + int wide_flag = 0; + int nchars; + + if (TREE_CHAIN (strings)) + { + /* More than one in the chain, so concatenate. */ + register char *p, *q; + + /* Don't include the \0 at the end of each substring, + except for the last one. + Count wide strings and ordinary strings separately. */ + for (t = strings; t; t = TREE_CHAIN (t)) + { + if (TREE_TYPE (t) == int_array_type_node) + { + wide_length += (TREE_STRING_LENGTH (t) - UNITS_PER_WORD); + wide_flag = 1; + } + else + length += (TREE_STRING_LENGTH (t) - 1); + } + + /* If anything is wide, the non-wides will be converted, + which makes them take more space. */ + if (wide_flag) + length = length * UNITS_PER_WORD + wide_length; + + p = (char *) savealloc (length); + + /* Copy the individual strings into the new combined string. + If the combined string is wide, convert the chars to ints + for any individual strings that are not wide. */ + + q = p; + for (t = strings; t; t = TREE_CHAIN (t)) + { + int len = TREE_STRING_LENGTH (t) - 1; + if ((TREE_TYPE (t) == int_array_type_node) == wide_flag) + { + bcopy (TREE_STRING_POINTER (t), q, len); + q += len; + } + else + { + int i; + for (i = 0; i < len; i++) + ((int *) q)[i] = TREE_STRING_POINTER (t)[i]; + q += len * UNITS_PER_WORD; + } + } + *q = 0; + + value = make_node (STRING_CST); + TREE_STRING_POINTER (value) = p; + TREE_STRING_LENGTH (value) = length; + TREE_LITERAL (value) = 1; + } + else + { + value = strings; + length = TREE_STRING_LENGTH (value); + if (TREE_TYPE (value) == int_array_type_node) + wide_flag = 1; + } + + /* Compute the number of elements, for the array type. */ + nchars = wide_flag ? length / UNITS_PER_WORD : length; + + /* Create the array type for the string constant. + -Wwrite-strings says make the string constant an array of const char + so that copying it to a non-const pointer will get a warning. */ + if (warn_write_strings) + { + tree elements + = build_type_variant (wide_flag ? integer_type_node : char_type_node, + 1, 0); + TREE_TYPE (value) + = build_array_type (elements, + build_index_type (build_int_2 (nchars - 1, 0))); + } + else + TREE_TYPE (value) + = build_array_type (wide_flag ? integer_type_node : char_type_node, + build_index_type (build_int_2 (nchars - 1, 0))); + TREE_LITERAL (value) = 1; + TREE_STATIC (value) = 1; + return value; +} + +FILE *finput; /* input file. + Normally a pipe from the preprocessor. */ + +/* lexical analyzer */ + +static int maxtoken; /* Current nominal length of token buffer. */ +static char *token_buffer; /* Pointer to token buffer. + Actual allocated length is maxtoken + 2. */ +static int max_wide; /* Current nominal length of wide_buffer. */ +static int *wide_buffer; /* Pointer to wide-string buffer. + Actual allocated length is max_wide + 1. */ + +/* Nonzero if end-of-file has been seen on input. */ +static int end_of_file; + +/* Data type that represents the GNU C reserved words. */ +struct resword { char *name; short token; enum rid rid; }; + +#define MIN_WORD_LENGTH 2 /* minimum size for C keyword */ +#define MAX_WORD_LENGTH 13 /* maximum size for C keyword */ +#define MIN_HASH_VALUE 7 /* range of the hash keys values */ +#define MAX_HASH_VALUE 91 /* for the perfect hash generator */ +#define NORID RID_UNUSED + +/* This function performs the minimum-perfect hash mapping from input + string to reswords table index. It only looks at the first and + last characters in the string, thus assuring the O(1) lookup time + (this keeps our constant down to an insignificant amount!). Compiling + the following 2 functions as inline removes all overhead of the + function calls. */ + +#ifdef __GNUC__ +__inline +#endif +static int +hash (str, len) + register char *str; + register int len; +{ +/* This table is used to build the hash table index that recognizes + reserved words in 0(1) steps. It is larger than strictly necessary, + but I'm trading off the space for the time-saving luxury of avoiding + subtraction of an offset. All those ``91's'' (actually just a + short-hand for MAX_HASH_VALUE #defined above) are used to speed up + the search when the string found on the input stream doesn't have a + first or last character that is part of the set of alphabetic + characters that comprise the first or last characters in C + reserved words. */ + + static int hash_table[] = + { + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 1, 91, 2, 1, 32, + 7, 5, 18, 20, 1, 17, 91, 1, 18, 1, + 28, 1, 23, 91, 12, 20, 1, 41, 7, 15, + 91, 91, 10, 91, 91, 91, 91, 91, + }; + register int hval = len ; + + switch (hval) + { + default: + case 3: + hval += hash_table[str[2]]; + case 2: + case 1: + return hval + hash_table[str[0]] + hash_table[str[len - 1]]; + } +} + +/* This routine attempts to match the string found in the reswords table + with the one from the input stream. If all the relevant details + match then an actual strcmp comparison is performed and the address of + correct struct resword entry is returned. Otherwise, a NULL + pointer is returned. */ + +#ifdef __GNUC__ +__inline +#endif +struct resword * +is_reserved_word (str, len) + register char *str; + register int len; +{ + /* This is the hash table of keywords. + The order of keywords has been chosen for perfect hashing. + Therefore, this table cannot be updated by hand. + Use the program ``gperf,'' available with the latest libg++ + distribution, to generate an updated table. A file called + c-parse.gperf, distributed with GNU C, contains the keyword file. */ + + static struct resword reswords[] = + { + { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, + {"asm", ASM, NORID }, + {"auto", SCSPEC, RID_AUTO }, + {"__asm", ASM, NORID }, + {"do", DO, NORID }, + {"__asm__", ASM, NORID }, + {"break", BREAK, NORID }, + {"__typeof__", TYPEOF, NORID }, + { "", }, + {"__alignof__", ALIGNOF, NORID }, + { "", }, + {"__attribute__", ATTRIBUTE, NORID }, + { "", }, + {"__attribute", ATTRIBUTE, NORID }, + { "", }, + {"__volatile__", TYPE_QUAL, RID_VOLATILE }, + {"int", TYPESPEC, RID_INT }, + {"__volatile", TYPE_QUAL, RID_VOLATILE }, + { "", }, + {"float", TYPESPEC, RID_FLOAT }, + {"goto", GOTO, NORID }, + {"short", TYPESPEC, RID_SHORT }, + {"__typeof", TYPEOF, NORID }, + {"__inline__", SCSPEC, RID_INLINE }, + {"__alignof", ALIGNOF, NORID }, + {"__inline", SCSPEC, RID_INLINE }, + {"__signed__", TYPESPEC, RID_SIGNED }, + {"default", DEFAULT, NORID }, + {"else", ELSE, NORID }, + {"void", TYPESPEC, RID_VOID }, + {"__signed", TYPESPEC, RID_SIGNED }, + {"if", IF, NORID }, + {"volatile", TYPE_QUAL, RID_VOLATILE }, + {"struct", STRUCT, NORID }, + {"extern", SCSPEC, RID_EXTERN }, + {"__const", TYPE_QUAL, RID_CONST }, + {"while", WHILE, NORID }, + {"__const__", TYPE_QUAL, RID_CONST }, + {"switch", SWITCH, NORID }, + {"for", FOR, NORID }, + {"inline", SCSPEC, RID_INLINE }, + {"return", RETURN, NORID }, + {"typeof", TYPEOF, NORID }, + {"typedef", SCSPEC, RID_TYPEDEF }, + {"char", TYPESPEC, RID_CHAR }, + {"enum", ENUM, NORID }, + {"register", SCSPEC, RID_REGISTER }, + {"signed", TYPESPEC, RID_SIGNED }, + {"sizeof", SIZEOF, NORID }, + { "", }, { "", }, { "", }, { "", }, + {"double", TYPESPEC, RID_DOUBLE }, + {"static", SCSPEC, RID_STATIC }, + {"case", CASE, NORID }, + { "", }, { "", }, { "", }, { "", }, + {"const", TYPE_QUAL, RID_CONST }, + { "", }, { "", }, { "", }, + {"long", TYPESPEC, RID_LONG }, + { "", }, { "", }, + {"continue", CONTINUE, NORID }, + { "", }, { "", }, + {"unsigned", TYPESPEC, RID_UNSIGNED }, + { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, { "", }, + { "", }, { "", }, { "", }, { "", }, { "", }, + {"union", UNION, NORID }, + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register char *s = reswords[key].name; + + if (*s == *str && !strcmp (str + 1, s + 1)) + return &reswords[key]; + } + } + return 0; +} + +/* The elements of `ridpointers' are identifier nodes + for the reserved type names and storage classes. + It is indexed by a RID_... value. */ + +tree ridpointers[(int) RID_MAX]; + +int check_newline (); + +void +init_lex () +{ + /* Start it at 0, because check_newline is called at the very beginning + and will increment it to 1. */ + lineno = 0; + + maxtoken = 40; + token_buffer = (char *) xmalloc (maxtoken + 2); + max_wide = 40; + wide_buffer = (int *) xmalloc (max_wide + 1); + + ridpointers[(int) RID_INT] = get_identifier ("int"); + ridpointers[(int) RID_CHAR] = get_identifier ("char"); + ridpointers[(int) RID_VOID] = get_identifier ("void"); + ridpointers[(int) RID_FLOAT] = get_identifier ("float"); + ridpointers[(int) RID_DOUBLE] = get_identifier ("double"); + ridpointers[(int) RID_SHORT] = get_identifier ("short"); + ridpointers[(int) RID_LONG] = get_identifier ("long"); + ridpointers[(int) RID_UNSIGNED] = get_identifier ("unsigned"); + ridpointers[(int) RID_SIGNED] = get_identifier ("signed"); + ridpointers[(int) RID_INLINE] = get_identifier ("inline"); + ridpointers[(int) RID_CONST] = get_identifier ("const"); + ridpointers[(int) RID_VOLATILE] = get_identifier ("volatile"); + ridpointers[(int) RID_AUTO] = get_identifier ("auto"); + ridpointers[(int) RID_STATIC] = get_identifier ("static"); + ridpointers[(int) RID_EXTERN] = get_identifier ("extern"); + ridpointers[(int) RID_TYPEDEF] = get_identifier ("typedef"); + ridpointers[(int) RID_REGISTER] = get_identifier ("register"); +} + +static void +reinit_parse_for_function () +{ +} + +/* If C is not whitespace, return C. + Otherwise skip whitespace and return first nonwhite char read. */ + +static int +skip_white_space (c) + register int c; +{ +#if 0 + register int inside; +#endif + + for (;;) + { + switch (c) + { + /* Don't recognize comments in cc1: all comments are removed by cpp, + and cpp output can include / and * consecutively as operators. */ +#if 0 + case '/': + c = getc (finput); + if (c != '*') + { + ungetc (c, finput); + return '/'; + } + + c = getc (finput); + + inside = 1; + while (inside) + { + if (c == '*') + { + while (c == '*') + c = getc (finput); + + if (c == '/') + { + inside = 0; + c = getc (finput); + } + } + else if (c == '\n') + { + lineno++; + c = getc (finput); + } + else if (c == EOF) + { + error ("unterminated comment"); + break; + } + else + c = getc (finput); + } + + break; +#endif + + case '\n': + c = check_newline (); + break; + + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case '\b': + c = getc (finput); + break; + + case '\\': + c = getc (finput); + if (c == '\n') + lineno++; + else + error ("stray '\\' in program"); + c = getc (finput); + break; + + default: + return (c); + } + } +} + + + +/* Make the token buffer longer, preserving the data in it. + P should point to just beyond the last valid character in the old buffer. + The value we return is a pointer to the new buffer + at a place corresponding to P. */ + +static char * +extend_token_buffer (p) + char *p; +{ + int offset = p - token_buffer; + + maxtoken = maxtoken * 2 + 10; + token_buffer = (char *) xrealloc (token_buffer, maxtoken + 2); + + return token_buffer + offset; +} + +/* At the beginning of a line, increment the line number + and process any #-directive on this line. + If the line is a #-directive, read the entire line and return a newline. + Otherwise, return the line's first non-whitespace character. */ + +int +check_newline () +{ + register int c; + register int token; + + lineno++; + + /* Read first nonwhite char on the line. */ + + c = getc (finput); + while (c == ' ' || c == '\t') + c = getc (finput); + + if (c != '#') + { + /* If not #, return it so caller will use it. */ + return c; + } + + /* Read first nonwhite char after the `#'. */ + + c = getc (finput); + while (c == ' ' || c == '\t') + c = getc (finput); + + /* If a letter follows, then if the word here is `line', skip + it and ignore it; otherwise, ignore the line, with an error + if the word isn't `pragma'. */ + + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + { + if (c == 'p') + { + if (getc (finput) == 'r' + && getc (finput) == 'a' + && getc (finput) == 'g' + && getc (finput) == 'm' + && getc (finput) == 'a' + && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n')) + goto skipline; + } + + else if (c == 'l') + { + if (getc (finput) == 'i' + && getc (finput) == 'n' + && getc (finput) == 'e' + && ((c = getc (finput)) == ' ' || c == '\t')) + goto linenum; + } + else if (c == 'i') + { + if (getc (finput) == 'd' + && getc (finput) == 'e' + && getc (finput) == 'n' + && getc (finput) == 't' + && ((c = getc (finput)) == ' ' || c == '\t')) + { + extern FILE *asm_out_file; + + if (pedantic) + error ("ANSI C does not allow #ident"); + + /* Here we have just seen `#ident '. + A string constant should follow. */ + + while (c == ' ' || c == '\t') + c = getc (finput); + + /* If no argument, ignore the line. */ + if (c == '\n') + return c; + + ungetc (c, finput); + token = yylex (); + if (token != STRING + || TREE_CODE (yylval.ttype) != STRING_CST) + { + error ("invalid #ident"); + goto skipline; + } + +#ifdef ASM_OUTPUT_IDENT + ASM_OUTPUT_IDENT (asm_out_file, TREE_STRING_POINTER (yylval.ttype)); +#endif + + /* Skip the rest of this line. */ + goto skipline; + } + } + + error ("undefined or invalid # directive"); + goto skipline; + } + +linenum: + /* Here we have either `#line' or `# '. + In either case, it should be a line number; a digit should follow. */ + + while (c == ' ' || c == '\t') + c = getc (finput); + + /* If the # is the only nonwhite char on the line, + just ignore it. Check the new newline. */ + if (c == '\n') + return c; + + /* Something follows the #; read a token. */ + + ungetc (c, finput); + token = yylex (); + + if (token == CONSTANT + && TREE_CODE (yylval.ttype) == INTEGER_CST) + { + int old_lineno = lineno; + /* subtract one, because it is the following line that + gets the specified number */ + + int l = TREE_INT_CST_LOW (yylval.ttype) - 1; + + /* Is this the last nonwhite stuff on the line? */ + c = getc (finput); + while (c == ' ' || c == '\t') + c = getc (finput); + if (c == '\n') + { + /* No more: store the line number and check following line. */ + lineno = l; + return c; + } + ungetc (c, finput); + + /* More follows: it must be a string constant (filename). */ + + token = yylex (); + if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST) + { + error ("invalid #line"); + goto skipline; + } + + input_filename + = (char *) permalloc (TREE_STRING_LENGTH (yylval.ttype) + 1); + strcpy (input_filename, TREE_STRING_POINTER (yylval.ttype)); + lineno = l; + + if (main_input_filename == 0) + main_input_filename = input_filename; + + /* Is this the last nonwhite stuff on the line? */ + c = getc (finput); + while (c == ' ' || c == '\t') + c = getc (finput); + if (c == '\n') + return c; + ungetc (c, finput); + + token = yylex (); + + /* `1' after file name means entering new file. + `2' after file name means just left a file. */ + + if (token == CONSTANT + && TREE_CODE (yylval.ttype) == INTEGER_CST) + { + if (TREE_INT_CST_LOW (yylval.ttype) == 1) + { + struct file_stack *p + = (struct file_stack *) xmalloc (sizeof (struct file_stack)); + input_file_stack->line = old_lineno; + p->next = input_file_stack; + p->name = input_filename; + input_file_stack = p; + input_file_stack_tick++; + } + else if (input_file_stack->next) + { + struct file_stack *p = input_file_stack; + input_file_stack = p->next; + free (p); + input_file_stack_tick++; + } + else + error ("#-lines for entering and leaving files don't match"); + } + } + else + error ("invalid #-line"); + + /* skip the rest of this line. */ + skipline: + if (c == '\n') + return c; + while ((c = getc (finput)) != EOF && c != '\n'); + return c; +} + +#define isalnum(char) ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) +#define isdigit(char) (char >= '0' && char <= '9') +#define ENDFILE -1 /* token that represents end-of-file */ + + +static int +readescape () +{ + register int c = getc (finput); + register int count, code; + int firstdig; + + switch (c) + { + case 'x': + code = 0; + count = 0; + while (1) + { + c = getc (finput); + if (!(c >= 'a' && c <= 'f') + && !(c >= 'A' && c <= 'F') + && !(c >= '0' && c <= '9')) + { + ungetc (c, finput); + break; + } + code *= 16; + if (c >= 'a' && c <= 'f') + code += c - 'a' + 10; + if (c >= 'A' && c <= 'F') + code += c - 'A' + 10; + if (c >= '0' && c <= '9') + code += c - '0'; + if (count == 0) + firstdig = code; + count++; + } + if (count == 0) + error ("\\x used with no following hex digits"); + else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node) + || (count > 1 + && ((1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4)) + <= firstdig))) + warning ("hex escape out of range"); + return code; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + code = 0; + count = 0; + while ((c <= '7') && (c >= '0') && (count++ < 3)) + { + code = (code * 8) + (c - '0'); + c = getc (finput); + } + ungetc (c, finput); + return code; + + case '\\': case '\'': case '"': + return c; + + case '\n': + lineno++; + return -1; + + case 'n': + return TARGET_NEWLINE; + + case 't': + return TARGET_TAB; + + case 'r': + return TARGET_CR; + + case 'f': + return TARGET_FF; + + case 'b': + return TARGET_BS; + + case 'a': + return TARGET_BELL; + + case 'v': + return TARGET_VT; + + case 'E': + return 033; + + case '?': + /* `\(', etc, are used at beginning of line to avoid confusing Emacs. */ + case '(': + case '{': + case '[': + return c; + } + if (c >= 040 && c <= 0177) + warning ("unknown escape sequence `\\%c'", c); + else + warning ("unknown escape sequence: `\\' followed by char code 0x%x", c); + return c; +} + +void +yyerror (string) + char *string; +{ + char buf[200]; + + strcpy (buf, string); + + /* We can't print string and character constants well + because the token_buffer contains the result of processing escapes. */ + if (end_of_file) + strcat (buf, " at end of input"); + else if (token_buffer[0] == 0) + strcat (buf, " at null character"); + else if (token_buffer[0] == '"') + strcat (buf, " before string constant"); + else if (token_buffer[0] == '\'') + strcat (buf, " before character constant"); + else if (token_buffer[0] < 040 || token_buffer[0] >= 0177) + sprintf (buf + strlen (buf), " before character 0%o", token_buffer[0]); + else + strcat (buf, " before `%s'"); + + error (buf, token_buffer); +} + +static int nextchar = -1; + +static int +yylex () +{ + register int c; + register char *p; + register int value; + int wide_flag = 0; + + if (nextchar >= 0) + c = nextchar, nextchar = -1; + else + c = getc (finput); + + /* Effectively do c = skip_white_space (c) + but do it faster in the usual cases. */ + while (1) + switch (c) + { + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case '\b': + c = getc (finput); + break; + + case '\n': + case '/': + case '\\': + c = skip_white_space (c); + default: + goto found_nonwhite; + } + found_nonwhite: + + token_buffer[0] = c; + token_buffer[1] = 0; + +/* yylloc.first_line = lineno; */ + + switch (c) + { + case EOF: + end_of_file = 1; + token_buffer[0] = 0; + value = ENDFILE; + break; + + case '$': + if (dollars_in_ident) + goto letter; + return '$'; + + case 'L': + /* Capital L may start a wide-string or wide-character constant. */ + { + register int c = getc (finput); + if (c == '\'') + { + wide_flag = 1; + goto char_constant; + } + if (c == '"') + { + wide_flag = 1; + goto string_constant; + } + ungetc (c, finput); + } + + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + case '_': + letter: + p = token_buffer; + while (isalnum (c) || c == '_' || c == '$') + { + if (p >= token_buffer + maxtoken) + p = extend_token_buffer (p); + if (c == '$' && ! dollars_in_ident) + break; + + *p++ = c; + c = getc (finput); + } + + *p = 0; + nextchar = c; + + value = IDENTIFIER; + yylval.itype = 0; + + /* Try to recognize a keyword. Uses minimum-perfect hash function */ + + { + register struct resword *ptr; + + if (ptr = is_reserved_word (token_buffer, p - token_buffer)) + { + if (ptr->rid) + yylval.ttype = ridpointers[(int) ptr->rid]; + if ((! flag_no_asm + /* -fno-asm means don't recognize the non-ANSI keywords. */ + || ((int) ptr->token != ASM + && (int) ptr->token != TYPEOF + && ptr->rid != RID_INLINE) + /* Recognize __asm and __inline despite -fno-asm. */ + || token_buffer[0] == '_') + /* -ftraditional means don't recognize nontraditional keywords + typeof, const, volatile, signed or inline. */ + && (! flag_traditional + || ((int) ptr->token != TYPE_QUAL + && (int) ptr->token != TYPEOF + && ptr->rid != RID_SIGNED + && ptr->rid != RID_INLINE) + /* Recognize __inline, etc. despite -ftraditional. */ + || token_buffer[0] == '_')) + value = (int) ptr->token; + } + } + + /* If we did not find a keyword, look for an identifier + (or a typename). */ + + if (value == IDENTIFIER) + { + yylval.ttype = get_identifier (token_buffer); + lastiddecl = lookup_name (yylval.ttype); + + if (lastiddecl != 0 && TREE_CODE (lastiddecl) == TYPE_DECL) + value = TYPENAME; + } + + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '.': + { + int base = 10; + int count = 0; + int largest_digit = 0; + int numdigits = 0; + /* for multi-precision arithmetic, + we store only 8 live bits in each short, + giving us 64 bits of reliable precision */ + short shorts[8]; + int overflow = 0; + + enum anon1 { NOT_FLOAT, AFTER_POINT, TOO_MANY_POINTS} floatflag + = NOT_FLOAT; + + for (count = 0; count < 8; count++) + shorts[count] = 0; + + p = token_buffer; + *p++ = c; + + if (c == '0') + { + *p++ = (c = getc (finput)); + if ((c == 'x') || (c == 'X')) + { + base = 16; + *p++ = (c = getc (finput)); + } + else + { + base = 8; + numdigits++; + } + } + + /* Read all the digits-and-decimal-points. */ + + while (c == '.' + || (isalnum (c) && (c != 'l') && (c != 'L') + && (c != 'u') && (c != 'U') + && (floatflag == NOT_FLOAT || ((c != 'f') && (c != 'F'))))) + { + if (c == '.') + { + if (base == 16) + error ("floating constant may not be in radix 16"); + if (floatflag == AFTER_POINT) + { + error ("malformed floating constant"); + floatflag = TOO_MANY_POINTS; + } + else + floatflag = AFTER_POINT; + + base = 10; + *p++ = c = getc (finput); + /* Accept '.' as the start of a floating-point number + only when it is followed by a digit. + Otherwise, unread the following non-digit + and use the '.' as a structural token. */ + if (p == token_buffer + 2 && !isdigit (c)) + { + if (c == '.') + { + c = getc (finput); + if (c == '.') + { + *p++ = c; + *p = 0; + return ELLIPSIS; + } + error ("parse error at `..'"); + } + ungetc (c, finput); + token_buffer[1] = 0; + value = '.'; + goto done; + } + } + else + { + /* It is not a decimal point. + It should be a digit (perhaps a hex digit). */ + + if (isdigit (c)) + { + c = c - '0'; + } + else if (base <= 10) + { + if ((c&~040) == 'E') + { + base = 10; + floatflag = AFTER_POINT; + break; /* start of exponent */ + } + error ("nondigits in number and not hexadecimal"); + c = 0; + } + else if (c >= 'a') + { + c = c - 'a' + 10; + } + else + { + c = c - 'A' + 10; + } + if (c >= largest_digit) + largest_digit = c; + numdigits++; + + for (count = 0; count < 8; count++) + { + shorts[count] *= base; + if (count) + { + shorts[count] += (shorts[count-1] >> 8); + shorts[count-1] &= (1<<8)-1; + } + else shorts[0] += c; + } + + if (shorts[7] >= 1<<8 + || shorts[7] < - (1 << 8)) + overflow = TRUE; + + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = (c = getc (finput)); + } + } + + if (numdigits == 0) + error ("numeric constant with no digits"); + + if (largest_digit >= base) + error ("numeric constant contains digits beyond the radix"); + + /* Remove terminating char from the token buffer and delimit the string */ + *--p = 0; + + if (floatflag != NOT_FLOAT) + { + tree type = double_type_node; + char f_seen = 0; + char l_seen = 0; + REAL_VALUE_TYPE value; + + /* Read explicit exponent if any, and put it in tokenbuf. */ + + if ((c == 'e') || (c == 'E')) + { + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + if ((c == '+') || (c == '-')) + { + *p++ = c; + c = getc (finput); + } + if (! isdigit (c)) + error ("floating constant exponent has no digits"); + while (isdigit (c)) + { + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + } + } + + *p = 0; + errno = 0; + value = REAL_VALUE_ATOF (token_buffer); +#ifdef ERANGE + if (errno == ERANGE && !flag_traditional) + { + char *p1 = token_buffer; + /* Check for "0.0" and variants; + Sunos 4 spuriously returns ERANGE for them. */ + while (*p1 == '0') p1++; + if (*p1 == '.') + { + p1++; + while (*p1 == '0') p1++; + } + if (*p1 == 'e' || *p1 == 'E') + { + /* with significand==0, ignore the exponent */ + p1++; + while (*p1 != 0) p1++; + } + /* ERANGE is also reported for underflow, + so test the value to distinguish overflow from that. */ + if (*p1 != 0 && (value > 1.0 || value < -1.0)) + warning ("floating point number exceeds range of `double'"); + } +#endif + + /* Read the suffixes to choose a data type. */ + while (1) + { + if (c == 'f' || c == 'F') + { + float floater; + if (f_seen) + error ("two `f's in floating constant"); + f_seen = 1; + type = float_type_node; + floater = value; + value = floater; + } + else if (c == 'l' || c == 'L') + { + if (l_seen) + error ("two `l's in floating constant"); + l_seen = 1; + type = long_double_type_node; + } + else + { + if (isalnum (c)) + { + error ("garbage at end of number"); + while (isalnum (c)) + { + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + } + } + break; + } + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + } + + /* Create a node with determined type and value. */ + yylval.ttype = build_real (type, value); + + ungetc (c, finput); + *p = 0; + } + else + { + tree type; + int spec_unsigned = 0; + int spec_long = 0; + int spec_long_long = 0; + + while (1) + { + if (c == 'u' || c == 'U') + { + if (spec_unsigned) + error ("two `u's in integer constant"); + spec_unsigned = 1; + } + else if (c == 'l' || c == 'L') + { + if (spec_long) + { + if (spec_long_long) + error ("three `l's in integer constant"); + else if (pedantic) + warning ("ANSI C forbids long long integer constants"); + spec_long_long = 1; + } + spec_long = 1; + } + else + { + if (isalnum (c)) + { + error ("garbage at end of number"); + while (isalnum (c)) + { + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + } + } + break; + } + if (p >= token_buffer + maxtoken - 3) + p = extend_token_buffer (p); + *p++ = c; + c = getc (finput); + } + + ungetc (c, finput); + + if ((overflow || shorts[7] || shorts[6] || shorts[5] || shorts[4]) + && !spec_long_long) + warning ("integer constant out of range"); + + /* If it won't fit in a signed long long, make it unsigned. + We can't distinguish based on the tree node because + any integer constant fits any long long type. */ + if (shorts[7] >= (1<<8)) + spec_unsigned = 1; + + /* This is simplified by the fact that our constant + is always positive. */ + yylval.ttype + = (build_int_2 + ((shorts[3]<<24) + (shorts[2]<<16) + (shorts[1]<<8) + shorts[0], + (spec_long_long + ? (shorts[7]<<24) + (shorts[6]<<16) + (shorts[5]<<8) + shorts[4] + : 0))); + + if (!spec_long && !spec_unsigned + && int_fits_type_p (yylval.ttype, integer_type_node)) + type = integer_type_node; + + else if (!spec_long && (base != 10 || spec_unsigned) + && int_fits_type_p (yylval.ttype, unsigned_type_node)) + type = unsigned_type_node; + + else if (!spec_unsigned && !spec_long_long + && int_fits_type_p (yylval.ttype, long_integer_type_node)) + type = long_integer_type_node; + + else if (! spec_long_long + && int_fits_type_p (yylval.ttype, + long_unsigned_type_node)) + type = long_unsigned_type_node; + + else if (! spec_unsigned + && int_fits_type_p (yylval.ttype, + long_long_integer_type_node)) + type = long_long_integer_type_node; + + else if (int_fits_type_p (yylval.ttype, + long_long_unsigned_type_node)) + type = long_long_unsigned_type_node; + + else + { + type = long_long_integer_type_node; + warning ("integer constant out of range"); + } + + TREE_TYPE (yylval.ttype) = type; + *p = 0; + } + + value = CONSTANT; break; + } + + case '\'': + char_constant: + { + register int result = 0; + register num_chars = 0; + int width = TYPE_PRECISION (char_type_node); + int max_chars; + + if (wide_flag) width = TYPE_PRECISION (integer_type_node); + + max_chars = TYPE_PRECISION (integer_type_node) / width; + + while (1) + { + tryagain: + + c = getc (finput); + + if (c == '\'' || c == EOF) + break; + + if (c == '\\') + { + c = readescape (); + if (c < 0) + goto tryagain; + if (width < HOST_BITS_PER_INT + && (unsigned) c >= (1 << width)) + warning ("escape sequence out of range for character"); + } + else if (c == '\n') + { + if (pedantic) + warning ("ANSI C forbids newline in character constant"); + lineno++; + } + + num_chars++; + if (num_chars > maxtoken - 4) + extend_token_buffer (token_buffer); + + token_buffer[num_chars] = c; + + /* Merge character into result; ignore excess chars. */ + if (num_chars < max_chars + 1) + { + if (width < HOST_BITS_PER_INT) + result = (result << width) | (c & ((1 << width) - 1)); + else + result = c; + } + } + + token_buffer[num_chars + 1] = '\''; + token_buffer[num_chars + 2] = 0; + + if (c != '\'') + error ("malformatted character constant"); + else if (num_chars == 0) + error ("empty character constant"); + else if (num_chars > max_chars) + { + num_chars = max_chars; + error ("character constant too long"); + } + else if (num_chars != 1 && ! flag_traditional) + warning ("multi-character character constant"); + + /* If char type is signed, sign-extend the constant. */ + if (! wide_flag) + { + int num_bits = num_chars * width; + if (TREE_UNSIGNED (char_type_node) + || ((result >> (num_bits - 1)) & 1) == 0) + yylval.ttype + = build_int_2 (result & ((unsigned) ~0 + >> (HOST_BITS_PER_INT - num_bits)), + 0); + else + yylval.ttype + = build_int_2 (result | ~((unsigned) ~0 + >> (HOST_BITS_PER_INT - num_bits)), + -1); + } + else + yylval.ttype = build_int_2 (result, 0); + + TREE_TYPE (yylval.ttype) = integer_type_node; + value = CONSTANT; break; + } + + case '"': + string_constant: + { + int *widep; + + c = getc (finput); + p = token_buffer + 1; + + if (wide_flag) + widep = wide_buffer; + + while (c != '"' && c >= 0) + { + if (c == '\\') + { + c = readescape (); + if (c < 0) + goto skipnewline; + if (!wide_flag && c >= (1 << TYPE_PRECISION (char_type_node))) + warning ("escape sequence out of range for character"); + } + else if (c == '\n') + { + if (pedantic) + warning ("ANSI C forbids newline in string constant"); + lineno++; + } + + /* Store the char in C into the appropriate buffer. */ + + if (wide_flag) + { + if (widep == wide_buffer + max_wide) + { + int n = widep - wide_buffer; + max_wide *= 2; + wide_buffer = (int *) xrealloc (wide_buffer, max_wide + 1); + widep = wide_buffer + n; + } + *widep++ = c; + } + else + { + if (p == token_buffer + maxtoken) + p = extend_token_buffer (p); + *p++ = c; + } + + skipnewline: + c = getc (finput); + } + + /* We have read the entire constant. + Construct a STRING_CST for the result. */ + + if (wide_flag) + { + /* If this is a L"..." wide-string, make a vector + of the ints in wide_buffer. */ + *widep = 0; + /* We have not implemented the case where `int' + on the target and on the execution machine differ in size. */ + if (TYPE_PRECISION (integer_type_node) + != sizeof (int) * BITS_PER_UNIT) + abort (); + yylval.ttype + = build_string ((widep - wide_buffer + 1) * sizeof (int), + wide_buffer); + TREE_TYPE (yylval.ttype) = int_array_type_node; + } + else + { + *p = 0; + yylval.ttype = build_string (p - token_buffer, token_buffer + 1); + TREE_TYPE (yylval.ttype) = char_array_type_node; + } + + *p++ = '"'; + *p = 0; + + value = STRING; break; + } + + case '+': + case '-': + case '&': + case '|': + case '<': + case '>': + case '*': + case '/': + case '%': + case '^': + case '!': + case '=': + { + register int c1; + + combine: + + switch (c) + { + case '+': + yylval.code = PLUS_EXPR; break; + case '-': + yylval.code = MINUS_EXPR; break; + case '&': + yylval.code = BIT_AND_EXPR; break; + case '|': + yylval.code = BIT_IOR_EXPR; break; + case '*': + yylval.code = MULT_EXPR; break; + case '/': + yylval.code = TRUNC_DIV_EXPR; break; + case '%': + yylval.code = TRUNC_MOD_EXPR; break; + case '^': + yylval.code = BIT_XOR_EXPR; break; + case LSHIFT: + yylval.code = LSHIFT_EXPR; break; + case RSHIFT: + yylval.code = RSHIFT_EXPR; break; + case '<': + yylval.code = LT_EXPR; break; + case '>': + yylval.code = GT_EXPR; break; + } + + token_buffer[1] = c1 = getc (finput); + token_buffer[2] = 0; + + if (c1 == '=') + { + switch (c) + { + case '<': + value = ARITHCOMPARE; yylval.code = LE_EXPR; goto done; + case '>': + value = ARITHCOMPARE; yylval.code = GE_EXPR; goto done; + case '!': + value = EQCOMPARE; yylval.code = NE_EXPR; goto done; + case '=': + value = EQCOMPARE; yylval.code = EQ_EXPR; goto done; + } + value = ASSIGN; goto done; + } + else if (c == c1) + switch (c) + { + case '+': + value = PLUSPLUS; goto done; + case '-': + value = MINUSMINUS; goto done; + case '&': + value = ANDAND; goto done; + case '|': + value = OROR; goto done; + case '<': + c = LSHIFT; + goto combine; + case '>': + c = RSHIFT; + goto combine; + } + else if ((c == '-') && (c1 == '>')) + { value = POINTSAT; goto done; } + ungetc (c1, finput); + token_buffer[1] = 0; + + if ((c == '<') || (c == '>')) + value = ARITHCOMPARE; + else value = c; + goto done; + } + + case 0: + /* Don't make yyparse think this is eof. */ + value = 1; + break; + + default: + value = c; + } + +done: +/* yylloc.last_line = lineno; */ + + return value; +} diff --git a/usr/src/usr.bin/gcc/cc1/c-typeck.c b/usr/src/usr.bin/gcc/cc1/c-typeck.c new file mode 100644 index 0000000000..74628dfdf5 --- /dev/null +++ b/usr/src/usr.bin/gcc/cc1/c-typeck.c @@ -0,0 +1,3812 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)c-typeck.c 6.3 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Build expressions with type checking for C compiler. + Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This file is part of the C front end. + It contains routines to build C expressions given their operands, + including computing the types of the result, C-specific error checks, + and some optimization. + + There are also routines to build RETURN_STMT nodes and CASE_STMT nodes, + and to process initializations in declarations (since they work + like a strange sort of assignment). */ + +#include "config.h" +#include +#include "tree.h" +#include "c-tree.h" +#include "flags.h" + + + +int mark_addressable (); +static tree convert_for_assignment (); +static int compparms (); +int comp_target_types (); +static tree shorten_compare (); +static void binary_op_error (); +static tree pointer_int_sum (); +static tree pointer_diff (); +static tree convert_sequence (); +static tree unary_complex_lvalue (); +static tree process_init_constructor (); +tree digest_init (); +tree truthvalue_conversion (); +static tree invert_truthvalue (); +void incomplete_type_error (); +void readonly_warning (); + +/* Return the _TYPE node describing the data type + of the data which NODE represents as a C expression. + Arrays and functions are converted to pointers + just as they are when they appear as C expressions. */ + +tree +datatype (node) + tree node; +{ + register tree type = TREE_TYPE (node); + if (TREE_CODE (type) == ARRAY_TYPE) + return TYPE_POINTER_TO (TREE_TYPE (type)); + if (TREE_CODE (type) == FUNCTION_TYPE) + return build_pointer_type (type); + return type; +} + +/* Do `exp = require_complete_type (exp);' to make sure exp + does not have an incomplete type. (That includes void types.) */ + +tree +require_complete_type (value) + tree value; +{ + tree type = TREE_TYPE (value); + + /* First, detect a valid value with a complete type. */ + if (TYPE_SIZE (type) != 0 + && type != void_type_node) + return value; + + incomplete_type_error (value, type); + return error_mark_node; +} + +/* Print an error message for invalid use of an incomplete type. + VALUE is the expression that was used (or 0 if that isn't known) + and TYPE is the type that was invalid. */ + +void +incomplete_type_error (value, type) + tree value; + tree type; +{ + char *errmsg; + + /* Avoid duplicate error message. */ + if (TREE_CODE (type) == ERROR_MARK) + return; + + if (value != 0 && (TREE_CODE (value) == VAR_DECL + || TREE_CODE (value) == PARM_DECL)) + error ("`%s' has an incomplete type", + IDENTIFIER_POINTER (DECL_NAME (value))); + else + { + retry: + /* We must print an error message. Be clever about what it says. */ + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + errmsg = "invalid use of undefined type `struct %s'"; + break; + + case UNION_TYPE: + errmsg = "invalid use of undefined type `union %s'"; + break; + + case ENUMERAL_TYPE: + errmsg = "invalid use of undefined type `enum %s'"; + break; + + case VOID_TYPE: + error ("invalid use of void expression"); + return; + + case ARRAY_TYPE: + if (TYPE_DOMAIN (type)) + { + type = TREE_TYPE (type); + goto retry; + } + error ("invalid use of array with unspecified bounds"); + return; + + default: + abort (); + } + + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type))); + else + /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */ + error ("invalid use of incomplete typedef `%s'", + IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); + } +} + +/* Return a variant of TYPE which has all the type qualifiers of LIKE + as well as those of TYPE. */ + +static tree +qualify_type (type, like) + tree type, like; +{ + int constflag = TREE_READONLY (type) || TREE_READONLY (like); + int volflag = TREE_VOLATILE (type) || TREE_VOLATILE (like); + return c_build_type_variant (type, constflag, volflag); +} + +/* Return the common type of two types. + We assume that comptypes has already been done and returned 1; + if that isn't so, this may crash. + + This is the type for the result of most arithmetic operations + if the operands have the given two types. + + We do not deal with enumeral types here because they have already been + converted to integer types. */ + +tree +commontype (t1, t2) + tree t1, t2; +{ + register enum tree_code form1; + register enum tree_code form2; + + /* Save time if the two types are the same. */ + + if (t1 == t2) return t1; + + /* If one type is nonsense, use the other. */ + if (t1 == error_mark_node) + return t2; + if (t2 == error_mark_node) + return t1; + + /* Treat an enum type as the unsigned integer type of the same width. */ + + if (TREE_CODE (t1) == ENUMERAL_TYPE) + t1 = type_for_size (TYPE_PRECISION (t1), 1); + if (TREE_CODE (t2) == ENUMERAL_TYPE) + t2 = type_for_size (TYPE_PRECISION (t2), 1); + + form1 = TREE_CODE (t1); + form2 = TREE_CODE (t2); + + switch (form1) + { + case INTEGER_TYPE: + case REAL_TYPE: + /* If only one is real, use it as the result. */ + + if (form1 == REAL_TYPE && form2 != REAL_TYPE) + return t1; + + if (form2 == REAL_TYPE && form1 != REAL_TYPE) + return t2; + + /* Both real or both integers; use the one with greater precision. */ + + if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2)) + return t1; + else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1)) + return t2; + + /* Same precision. Prefer longs to ints even when same size. */ + + if (t1 == long_unsigned_type_node + || t2 == long_unsigned_type_node) + return long_unsigned_type_node; + + if (t1 == long_integer_type_node + || t2 == long_integer_type_node) + { + /* But preserve unsignedness from the other type, + since long cannot hold all the values of an unsigned int. */ + if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2)) + return long_unsigned_type_node; + return long_integer_type_node; + } + + /* Otherwise prefer the unsigned one. */ + + if (TREE_UNSIGNED (t1)) + return t1; + else return t2; + + case POINTER_TYPE: +#if 0 + /* For two pointers, do this recursively on the target type, + and combine the qualifiers of the two types' targets. */ + { + tree target = commontype (TYPE_MAIN_VARIANT (TREE_TYPE (t1)), + TYPE_MAIN_VARIANT (TREE_TYPE (t2))); + int constp + = TREE_READ_ONLY (TREE_TYPE (t1)) || TREE_READ_ONLY (TREE_TYPE (t2)); + int volatilep + = TREE_VOLATILE (TREE_TYPE (t1)) || TREE_VOLATILE (TREE_TYPE (t2)); + return build_pointer_type (c_build_type_variant (target, constp, volatilep)); + } +#endif + return build_pointer_type (commontype (TREE_TYPE (t1), TREE_TYPE (t2))); + + case ARRAY_TYPE: + { + tree elt = commontype (TREE_TYPE (t1), TREE_TYPE (t2)); + /* Save space: see if the result is identical to one of the args. */ + if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1)) + return t1; + if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2)) + return t2; + /* Merge the element types, and have a size if either arg has one. */ + return build_array_type (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2)); + } + + case FUNCTION_TYPE: + /* Function types: prefer the one that specified arg types. + If both do, merge the arg types. Also merge the return types. */ + { + tree valtype = commontype (TREE_TYPE (t1), TREE_TYPE (t2)); + tree p1 = TYPE_ARG_TYPES (t1); + tree p2 = TYPE_ARG_TYPES (t2); + int len; + tree newargs, n; + int i; + + /* Save space: see if the result is identical to one of the args. */ + if (valtype == TREE_TYPE (t1) && ! TYPE_ARG_TYPES (t2)) + return t1; + if (valtype == TREE_TYPE (t2) && ! TYPE_ARG_TYPES (t1)) + return t2; + + /* Simple way if one arg fails to specify argument types. */ + if (TYPE_ARG_TYPES (t1) == 0) + return build_function_type (valtype, TYPE_ARG_TYPES (t2)); + if (TYPE_ARG_TYPES (t2) == 0) + return build_function_type (valtype, TYPE_ARG_TYPES (t1)); + + /* If both args specify argument types, we must merge the two + lists, argument by argument. */ + + len = list_length (p1); + newargs = 0; + + for (i = 0; i < len; i++) + newargs = tree_cons (0, 0, newargs); + + n = newargs; + + for (; p1; + p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n)) + TREE_VALUE (n) = commontype (TREE_VALUE (p1), TREE_VALUE (p2)); + + return build_function_type (valtype, newargs); + } + + default: + return t1; + } + +} + +/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment + or various other operations. This is what ANSI C speaks of as + "being the same". */ + +int +comptypes (type1, type2) + tree type1, type2; +{ + register tree t1 = type1; + register tree t2 = type2; + + /* Suppress errors caused by previously reported errors */ + + if (t1 == t2 || TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK) + return 1; + + /* Treat an enum type as the unsigned integer type of the same width. */ + + if (TREE_CODE (t1) == ENUMERAL_TYPE) + t1 = type_for_size (TYPE_PRECISION (t1), 1); + if (TREE_CODE (t2) == ENUMERAL_TYPE) + t2 = type_for_size (TYPE_PRECISION (t2), 1); + + if (t1 == t2) + return 1; + + /* Different classes of types can't be compatible. */ + + if (TREE_CODE (t1) != TREE_CODE (t2)) return 0; + + /* Qualifiers must match. */ + + if (TREE_READONLY (t1) != TREE_READONLY (t2)) + return 0; + if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2)) + return 0; + + switch (TREE_CODE (t1)) + { + case POINTER_TYPE: + return (TREE_TYPE (t1) == TREE_TYPE (t2) + || comptypes (TREE_TYPE (t1), TREE_TYPE (t2))); + + case FUNCTION_TYPE: + return ((TREE_TYPE (t1) == TREE_TYPE (t2) + || comptypes (TREE_TYPE (t1), TREE_TYPE (t2))) + && compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2))); + + case ARRAY_TYPE: + /* Target types must match incl. qualifiers. */ + if (!(TREE_TYPE (t1) == TREE_TYPE (t2) + || comptypes (TREE_TYPE (t1), TREE_TYPE (t2)))) + return 0; + { + tree d1 = TYPE_DOMAIN (t1); + tree d2 = TYPE_DOMAIN (t2); + + /* Sizes must match unless one is missing or variable. */ + if (d1 == 0 || d2 == 0 || d1 == d2 + || TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST + || TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST + || TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST + || TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST) + return 1; + + return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1)) + == TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2))) + && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1)) + == TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2))) + && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1)) + == TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2))) + && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1)) + == TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2)))); + } + } + return 0; +} + +/* Return 1 if TTL and TTR are pointers to types that are equivalent, + ignoring their qualifiers. */ + +int +comp_target_types (ttl, ttr) + tree ttl, ttr; +{ + return comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)), + TYPE_MAIN_VARIANT (TREE_TYPE (ttr))); +} + +/* Subroutines of `comptypes'. */ + +/* Return 1 if two parameter type lists PARMS1 and PARMS2 + are equivalent in the sense that functions with those parameter types + can have equivalent types. + If either list is empty, we win. + Otherwise, the two lists must be equivalent, element by element. */ + +static int +compparms (parms1, parms2) + tree parms1, parms2; +{ + register tree t1 = parms1, t2 = parms2; + + /* An unspecified parmlist matches any specified parmlist + whose argument types don't need default promotions. */ + + if (t1 == 0) + return compparms1 (t2); + if (t2 == 0) + return compparms1 (t1); + + while (1) + { + if (t1 == 0 && t2 == 0) + return 1; + /* If one parmlist is shorter than the other, + they fail to match. */ + if (t1 == 0 || t2 == 0) + return 0; + if (! comptypes (TREE_VALUE (t1), TREE_VALUE (t2))) + return 0; + t1 = TREE_CHAIN (t1); + t2 = TREE_CHAIN (t2); + } +} + +/* Return 1 if PARMS specifies a fixed number of parameters + and none of their types is affected by default promotions. */ + +int +compparms1 (parms) + tree parms; +{ + register tree t; + for (t = parms; t; t = TREE_CHAIN (t)) + { + register tree type = TREE_VALUE (t); + + if (TREE_CHAIN (t) == 0 && type != void_type_node) + return 0; + + if (type == float_type_node) + return 0; + + if (TREE_CODE (type) == INTEGER_TYPE + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) + return 0; + } + return 1; +} + +/* Return an unsigned type the same as TYPE in other respects. */ + +tree +unsigned_type (type) + tree type; +{ + if (type == signed_char_type_node || type == char_type_node) + return unsigned_char_type_node; + if (type == integer_type_node) + return unsigned_type_node; + if (type == short_integer_type_node) + return short_unsigned_type_node; + if (type == long_integer_type_node) + return long_unsigned_type_node; + if (type == long_long_integer_type_node) + return long_long_unsigned_type_node; + return type; +} + +/* Return a signed type the same as TYPE in other respects. */ + +tree +signed_type (type) + tree type; +{ + if (type == unsigned_char_type_node || type == char_type_node) + return signed_char_type_node; + if (type == unsigned_type_node) + return integer_type_node; + if (type == short_unsigned_type_node) + return short_integer_type_node; + if (type == long_unsigned_type_node) + return long_integer_type_node; + if (type == long_long_unsigned_type_node) + return long_long_integer_type_node; + return type; +} + +/* Return a type the same as TYPE except unsigned or + signed according to UNSIGNEDP. */ + +tree +signed_or_unsigned_type (unsignedp, type) + int unsignedp; + tree type; +{ + if (TREE_CODE (type) != INTEGER_TYPE) + return type; + if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node)) + return unsignedp ? unsigned_char_type_node : signed_char_type_node; + if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)) + return unsignedp ? unsigned_type_node : integer_type_node; + if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node)) + return unsignedp ? short_unsigned_type_node : short_integer_type_node; + if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node)) + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node)) + return (unsignedp ? long_long_unsigned_type_node + : long_long_integer_type_node); + return type; +} + +/* Return an integer type with BITS bits of precision, + that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */ + +tree +type_for_size (bits, unsignedp) + int bits; + int unsignedp; +{ + if (bits <= TYPE_PRECISION (signed_char_type_node)) + return unsignedp ? unsigned_char_type_node : signed_char_type_node; + + if (bits <= TYPE_PRECISION (short_integer_type_node)) + return unsignedp ? short_unsigned_type_node : short_integer_type_node; + + if (bits <= TYPE_PRECISION (integer_type_node)) + return unsignedp ? unsigned_type_node : integer_type_node; + + if (bits <= TYPE_PRECISION (long_integer_type_node)) + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + + if (bits <= TYPE_PRECISION (long_long_integer_type_node)) + return (unsignedp ? long_long_unsigned_type_node + : long_long_integer_type_node); + + return 0; +} + +tree +get_floating_type (mode) + enum machine_mode mode; +{ + if (mode == TYPE_MODE (float_type_node)) + return float_type_node; + if (mode == TYPE_MODE (double_type_node)) + return double_type_node; + if (mode == TYPE_MODE (long_double_type_node)) + return long_double_type_node; + abort (); +} + +tree +c_sizeof (type) + tree type; +{ + enum tree_code code = TREE_CODE (type); + + if (code == FUNCTION_TYPE) + { + if (pedantic || warn_pointer_arith) + warning ("sizeof applied to a function type"); + return build_int (1); + } + if (code == VOID_TYPE) + { + if (pedantic || warn_pointer_arith) + warning ("sizeof applied to a void type"); + return build_int (1); + } + + /* Convert in case a char is more than one unit. */ + return convert_units (size_in_bytes (type), BITS_PER_UNIT, + TYPE_PRECISION (char_type_node)); +} + +tree +c_sizeof_nowarn (type) + tree type; +{ + enum tree_code code = TREE_CODE (type); + + if (code == FUNCTION_TYPE + || code == VOID_TYPE) + return build_int (1); + + /* Convert in case a char is more than one unit. */ + return convert_units (size_in_bytes (type), BITS_PER_UNIT, + TYPE_PRECISION (char_type_node)); +} + +/* Implement the __alignof keyword: Return the minimum required + alignment of TYPE, measured in bytes. */ + +tree +c_alignof (type) + tree type; +{ + enum tree_code code = TREE_CODE (type); + + if (code == FUNCTION_TYPE) + return build_int (FUNCTION_BOUNDARY / BITS_PER_UNIT); + + if (code == VOID_TYPE) + return build_int (1); + + return build_int (TYPE_ALIGN (type) / BITS_PER_UNIT); +} + +/* Return either DECL or its known constant value (if it has one). */ + +static tree +decl_constant_value (decl) + tree decl; +{ + if (! TREE_PUBLIC (decl) + /* Don't change a variable array bound or initial value to a constant + in a place where a variable is invalid. */ + && current_function_decl != 0 + && ! pedantic + && ! TREE_THIS_VOLATILE (decl) + && DECL_INITIAL (decl) != 0 + && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK + /* This is invalid if initial value is not constant. + If it has either a function call, a memory reference, + or a variable, then re-evaluating it could give different results. */ + && TREE_LITERAL (DECL_INITIAL (decl)) + /* Check for cases where this is sub-optimal, even though valid. */ + && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR + && DECL_MODE (decl) != BLKmode) + return DECL_INITIAL (decl); + return decl; +} + +/* Perform default promotions for C data used in expressions. + Arrays and functions are converted to pointers; + enumeral types or short or char, to int. + In addition, manifest constants symbols are replaced by their values. */ + +tree +default_conversion (exp) + tree exp; +{ + register tree dt = TREE_TYPE (exp); + register enum tree_code form = TREE_CODE (dt); + + if (TREE_CODE (exp) == CONST_DECL) + exp = DECL_INITIAL (exp); + /* Replace a nonvolatile const static variable with its value. */ + else if (optimize + && TREE_CODE (exp) == VAR_DECL + && TREE_READONLY (exp)) + exp = decl_constant_value (exp); + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since EXP is being used in non-lvalue context. */ + if (TREE_CODE (exp) == NOP_EXPR + && TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))) + exp = TREE_OPERAND (exp, 0); + + if (form == ENUMERAL_TYPE + || (form == INTEGER_TYPE + && (TYPE_PRECISION (dt) + < TYPE_PRECISION (integer_type_node)))) + { + /* Traditionally, unsignedness is preserved in default promotions. */ + if (flag_traditional && TREE_UNSIGNED (dt)) + return convert (unsigned_type_node, exp); + return convert (integer_type_node, exp); + } + if (flag_traditional && dt == float_type_node) + return convert (double_type_node, exp); + if (form == VOID_TYPE) + { + error ("void value not ignored as it ought to be"); + return error_mark_node; + } + if (form == FUNCTION_TYPE) + { + return build_unary_op (ADDR_EXPR, exp, 0); + } + if (form == ARRAY_TYPE) + { + register tree adr; + tree restype = TREE_TYPE (dt); + tree ptrtype; + + if (TREE_CODE (exp) == INDIRECT_REF) + return convert (TYPE_POINTER_TO (restype), + TREE_OPERAND (exp, 0)); + + if (TREE_CODE (exp) == COMPOUND_EXPR) + { + tree op1 = default_conversion (TREE_OPERAND (exp, 1)); + return build (COMPOUND_EXPR, TREE_TYPE (op1), + TREE_OPERAND (exp, 0), op1); + } + + if (!lvalue_p (exp) + && ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp))) + { + error ("invalid use of non-lvalue array"); + return error_mark_node; + } + + if (TREE_READONLY (exp) || TREE_THIS_VOLATILE (exp)) + restype = c_build_type_variant (restype, TREE_READONLY (exp), + TREE_THIS_VOLATILE (exp)); + + ptrtype = build_pointer_type (restype); + + if (TREE_CODE (exp) == VAR_DECL) + { + /* ??? This is not really quite correct + in that the type of the operand of ADDR_EXPR + is not the target type of the type of the ADDR_EXPR itself. + Question is, can this lossage be avoided? */ + adr = build (ADDR_EXPR, ptrtype, exp); + if (mark_addressable (exp) == 0) + return error_mark_node; + TREE_LITERAL (adr) = staticp (exp); + TREE_VOLATILE (adr) = 0; /* Default would be, same as EXP. */ + return adr; + } + /* This way is better for a COMPONENT_REF since it can + simplify the offset for a component. */ + adr = build_unary_op (ADDR_EXPR, exp, 1); + return convert (ptrtype, adr); + } + return exp; +} + +/* Make an expression to refer to the COMPONENT field of + structure or union value DATUM. COMPONENT is an IDENTIFIER_NODE. */ + +tree +build_component_ref (datum, component) + tree datum, component; +{ + register tree basename = datum; + register tree basetype = TREE_TYPE (basename); + register enum tree_code form = TREE_CODE (basetype); + register tree field = NULL; + register tree ref; + + /* First, see if there is a field or component with name COMPONENT. */ + + if (form == RECORD_TYPE || form == UNION_TYPE) + { + if (TYPE_SIZE (basetype) == 0) + { + incomplete_type_error (0, basetype); + return error_mark_node; + } + + /* Look up component name in the structure type definition. */ + + for (field = TYPE_FIELDS (basetype); field; field = TREE_CHAIN (field)) + { + if (DECL_NAME (field) == component) + break; + } + + if (!field) + { + error (form == RECORD_TYPE + ? "structure has no member named `%s'" + : "union has no member named `%s'", + IDENTIFIER_POINTER (component)); + return error_mark_node; + } + if (TREE_TYPE (field) == error_mark_node) + return error_mark_node; + + ref = build (COMPONENT_REF, TREE_TYPE (field), basename, field); + + if (TREE_READONLY (basename) || TREE_READONLY (field)) + TREE_READONLY (ref) = 1; + if (TREE_THIS_VOLATILE (basename) || TREE_VOLATILE (field)) + TREE_THIS_VOLATILE (ref) = 1; + + return ref; + } + else if (form != ERROR_MARK) + error ("request for member `%s' in something not a structure or union", + IDENTIFIER_POINTER (component)); + + return error_mark_node; +} + +/* Given an expression PTR for a pointer, return an expression + for the value pointed to. + ERRORSTRING is the name of the operator to appear in error messages. */ + +tree +build_indirect_ref (ptr, errorstring) + tree ptr; + char *errorstring; +{ + register tree pointer = default_conversion (ptr); + register tree dt = TREE_TYPE (pointer); + + if (TREE_CODE (dt) == POINTER_TYPE) + if (TREE_CODE (pointer) == ADDR_EXPR + && (TREE_TYPE (TREE_OPERAND (pointer, 0)) + == TREE_TYPE (dt))) + return TREE_OPERAND (pointer, 0); + else + { + tree t = TREE_TYPE (dt); + register tree ref = build (INDIRECT_REF, + TYPE_MAIN_VARIANT (t), pointer); + + if (TREE_CODE (t) == VOID_TYPE + || (TYPE_SIZE (t) == 0 && TREE_CODE (t) != ARRAY_TYPE)) + { + error ("dereferencing pointer to incomplete type"); + return error_mark_node; + } + + TREE_READONLY (ref) = TREE_READONLY (t); + TREE_VOLATILE (ref) = TREE_VOLATILE (t) || TREE_VOLATILE (pointer); + TREE_THIS_VOLATILE (ref) = TREE_VOLATILE (t); + return ref; + } + else if (TREE_CODE (pointer) != ERROR_MARK) + error ("invalid type argument of `%s'", errorstring); + return error_mark_node; +} + +/* This handles expressions of the form "a[i]", which denotes + an array reference. + + This is logically equivalent in C to *(a+i), but we may do it differently. + If A is a variable or a member, we generate a primitive ARRAY_REF. + This avoids forcing the array out of registers, and can work on + arrays that are not lvalues (for example, members of structures returned + by functions). */ + +tree +build_array_ref (array, index) + tree array, index; +{ + if (index == 0) + { + error ("subscript missing in array reference"); + return error_mark_node; + } + + if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE + && TREE_CODE (array) != INDIRECT_REF) + { + tree rval, type; + + index = default_conversion (index); + if (index != error_mark_node + && TREE_CODE (TREE_TYPE (index)) != INTEGER_TYPE) + { + error ("array subscript is not an integer"); + return error_mark_node; + } + + /* An array that is indexed by a non-constant + cannot be stored in a register; we must be able to do + address arithmetic on its address. + Likewise an array of elements of variable size. */ + if (TREE_CODE (index) != INTEGER_CST + || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0 + && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST)) + { + if (mark_addressable (array) == 0) + return error_mark_node; + } + + if (pedantic && !lvalue_p (array)) + warning ("ANSI C forbids subscripting non-lvalue array"); + + if (pedantic) + { + tree foo = array; + while (TREE_CODE (foo) == COMPONENT_REF) + foo = TREE_OPERAND (foo, 0); + if (TREE_CODE (foo) == VAR_DECL && TREE_REGDECL (foo)) + warning ("ANSI C forbids subscripting non-lvalue array"); + } + + type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array))); + rval = build (ARRAY_REF, type, array, index); + /* Array ref is const/volatile if the array elements are. */ + TREE_READONLY (rval) |= TREE_READONLY (TREE_TYPE (TREE_TYPE (array))); + TREE_VOLATILE (rval) |= TREE_VOLATILE (TREE_TYPE (TREE_TYPE (array))); + TREE_THIS_VOLATILE (rval) |= TREE_VOLATILE (TREE_TYPE (TREE_TYPE (array))); + return require_complete_type (fold (rval)); + } + + { + tree ar = default_conversion (array); + tree ind = default_conversion (index); + + if ((TREE_CODE (TREE_TYPE (ar)) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE) + || (TREE_CODE (TREE_TYPE (ind)) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (ar)) != INTEGER_TYPE)) + { + error ("array subscript is not an integer"); + return error_mark_node; + } + + return build_indirect_ref (build_binary_op_nodefault (PLUS_EXPR, ar, ind, PLUS_EXPR), + "array indexing"); + } +} + +/* Build a function call to function FUNCTION with parameters PARAMS. + PARAMS is a list--a chain of TREE_LIST nodes--in which the + TREE_VALUE of each node is a parameter-expression. + FUNCTION's data type may be a function type or a pointer-to-function. */ + +tree +build_function_call (function, params) + tree function, params; +{ + register tree fntype; + register tree value_type; + register tree coerced_params; + tree name = NULL_TREE; + tree actualparameterlist (); + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context. */ + if (TREE_CODE (function) == NOP_EXPR + && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0))) + function = TREE_OPERAND (function, 0); + + /* Convert anything with function type to a pointer-to-function. */ + if (TREE_CODE (function) == FUNCTION_DECL) + { + name = DECL_NAME (function); + /* Differs from default_conversion by not setting TREE_ADDRESSABLE + (because calling an inline function does not mean the function + needs to be separately compiled). */ + function = build (ADDR_EXPR, build_pointer_type (TREE_TYPE (function)), + function); + } + else + function = default_conversion (function); + + fntype = TREE_TYPE (function); + + if (TREE_CODE (fntype) == ERROR_MARK) + return error_mark_node; + + if (!(TREE_CODE (fntype) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE)) + { + error ("called object is not a function"); + return error_mark_node; + } + + /* fntype now gets the type of function pointed to. */ + fntype = TREE_TYPE (fntype); + + /* Convert the parameters to the types declared in the + function prototype, or apply default promotions. */ + + coerced_params = actualparameterlist (TYPE_ARG_TYPES (fntype), params, name); + + /* Recognize certain built-in functions so we can make tree-codes + other than CALL_EXPR. We do this when it enables fold-const.c + to do something useful. */ + + if (TREE_CODE (function) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL) + switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0))) + { + case BUILT_IN_ABS: + case BUILT_IN_LABS: + case BUILT_IN_FABS: + if (coerced_params == 0) + return integer_zero_node; + return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0); + } + + value_type = TREE_TYPE (fntype) ? TREE_TYPE (fntype) : void_type_node; + + { + register tree result = + build (CALL_EXPR, value_type, function, coerced_params, NULL_TREE); + + TREE_VOLATILE (result) = 1; + if (value_type == void_type_node) + return result; + return require_complete_type (result); + } +} + +/* Convert the actual parameter expressions in the list VALUES + to the types in the list TYPELIST. + If parmdecls is exhausted, or when an element has NULL as its type, + perform the default conversions. + + NAME is an IDENTIFIER_NODE or 0. It is used only for error messages. + + This is also where warnings about wrong number of args are generated. + + Return a list of expressions for the parameters as converted. + + Both VALUES and the returned value are chains of TREE_LIST nodes + with the elements of the list in the TREE_VALUE slots of those nodes. */ + +tree +actualparameterlist (typelist, values, name) + tree typelist, values, name; +{ + register tree typetail, valtail; + register tree result = NULL; + register ordinal = 1; + + for (valtail = values, typetail = typelist; + valtail; + ++ordinal, valtail = TREE_CHAIN (valtail)) + { + register tree type = typetail ? TREE_VALUE (typetail) : 0; + register tree val = TREE_VALUE (valtail); + register tree parm; + + if (type == void_type_node) + { + if (name) + error ("too many arguments to function `%s'", + IDENTIFIER_POINTER (name)); + else + error ("too many arguments to function"); + break; + } + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since VAL is used in non-lvalue context. */ + if (TREE_CODE (val) == NOP_EXPR + && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))) + val = TREE_OPERAND (val, 0); + + if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE) + val = default_conversion (val); + + val = require_complete_type (val); + + if (type != 0) + { + /* Formal parm type is specified by a function prototype. */ + tree parmval; + + if (TYPE_SIZE (type) == 0) + { + error ("parameter type of called function is incomplete"); + parmval = val; + } + else + { +#ifdef PROMOTE_PROTOTYPES + /* Rather than truncating and then reextending, + convert directly to int, if that's the type we will want. */ + if (! flag_traditional + && TREE_CODE (type) == INTEGER_TYPE + && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))) + type = integer_type_node; +#endif + parmval = convert_for_assignment (type, val, "argument passing", + ordinal); +#ifdef PROMOTE_PROTOTYPES + if (TREE_CODE (type) == INTEGER_TYPE + && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))) + parmval = default_conversion (parmval); +#endif + } + parm = build_tree_list (0, parmval); + } + else if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE + && (TYPE_PRECISION (TREE_TYPE (val)) + < TYPE_PRECISION (double_type_node))) + /* Convert `float' to `double'. */ + parm = build_tree_list (NULL_TREE, convert (double_type_node, val)); + else + /* Convert `short' and `char' to full-size `int'. */ + parm = build_tree_list (NULL_TREE, default_conversion (val)); + + result = chainon (result, parm); + if (typetail) + typetail = TREE_CHAIN (typetail); + } + + if (typetail != 0 && TREE_VALUE (typetail) != void_type_node) + { + if (name) + error ("too few arguments to function `%s'", + IDENTIFIER_POINTER (name)); + else + error ("too few arguments to function"); + } + + return result; +} + +/* Build a binary-operation expression, after performing default + conversions on the operands. CODE is the kind of expression to build. */ + +tree +build_binary_op (code, arg1, arg2) + enum tree_code code; + tree arg1, arg2; +{ + return build_binary_op_nodefault (code, default_conversion (arg1), + default_conversion (arg2), code); +} + +/* Build a binary-operation expression without default conversions. + CODE is the kind of expression to build. + This function differs from `build' in several ways: + the data type of the result is computed and recorded in it, + warnings are generated if arg data types are invalid, + special handling for addition and subtraction of pointers is known, + and some optimization is done (operations on narrow ints + are done in the narrower type when that gives the same result). + Constant folding is also done before the result is returned. + + ERROR_CODE is the code that determines what to say in error messages. + It is usually, but not always, the same as CODE. + + Note that the operands will never have enumeral types + because either they have just had the default conversions performed + or they have both just been converted to some other type in which + the arithmetic is to be done. */ + +tree +build_binary_op_nodefault (code, op0, op1, error_code) + enum tree_code code; + tree op0, op1; + enum tree_code error_code; +{ + tree dt0 = datatype (op0), dt1 = datatype (op1); + + /* The expression codes of the data types of the arguments tell us + whether the arguments are integers, floating, pointers, etc. */ + register enum tree_code code0 = TREE_CODE (dt0); + register enum tree_code code1 = TREE_CODE (dt1); + + /* Expression code to give to the expression when it is built. + Normally this is CODE, which is what the caller asked for, + but in some special cases we change it. */ + register enum tree_code resultcode = code; + + /* Data type in which the computation is to be performed. + In the simplest cases this is the common type of the arguments. */ + register tree result_type = NULL; + + /* Nonzero means operands have already been type-converted + in whatever way is necessary. + Zero means they need to be converted to RESULT_TYPE. */ + int converted = 0; + + /* Nonzero means after finally constructing the expression + give it this type. Otherwise, give it type RESULT_TYPE. */ + tree final_type = 0; + + /* Nonzero if this is an operation like MIN or MAX which can + safely be computed in short if both args are promoted shorts. + Also implies COMMON. + -1 indicates a bitwise operation; this makes a difference + in the exact conditions for when it is safe to do the operation + in a narrower mode. */ + int shorten = 0; + + /* Nonzero if this is a comparison operation; + if both args are promoted shorts, compare the original shorts. + Also implies COMMON. */ + int short_compare = 0; + + /* Nonzero if this is a right-shift operation, which can be computed on the + original short and then promoted if the operand is a promoted short. */ + int short_shift = 0; + + /* Nonzero means set RESULT_TYPE to the common type of the args. */ + int common = 0; + + /* If an error was already reported for one of the arguments, + avoid reporting another error. */ + + if (code0 == ERROR_MARK || code1 == ERROR_MARK) + return error_mark_node; + + switch (code) + { + case PLUS_EXPR: + /* Handle the pointer + int case. */ + if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) + return pointer_int_sum (PLUS_EXPR, op0, op1); + else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE) + return pointer_int_sum (PLUS_EXPR, op1, op0); + else + common = 1; + break; + + case MINUS_EXPR: + /* Subtraction of two similar pointers. + We must subtract them as integers, then divide by object size. */ + if (code0 == POINTER_TYPE && code1 == POINTER_TYPE + && comp_target_types (dt0, dt1)) + return pointer_diff (op0, op1); + /* Handle pointer minus int. Just like pointer plus int. */ + else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) + return pointer_int_sum (MINUS_EXPR, op0, op1); + else + common = 1; + break; + + case MULT_EXPR: + common = 1; + break; + + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case EXACT_DIV_EXPR: + if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) + { + if (!(code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)) + resultcode = RDIV_EXPR; + else + shorten = 1; + common = 1; + } + break; + + case BIT_AND_EXPR: + case BIT_ANDTC_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) + shorten = -1; + /* If one operand is a constant, and the other is a short type + that has been converted to an int, + really do the work in the short type and then convert the + result to int. If we are lucky, the constant will be 0 or 1 + in the short type, making the entire operation go away. */ + if (TREE_CODE (op0) == INTEGER_CST + && TREE_CODE (op1) == NOP_EXPR + && TYPE_PRECISION (dt1) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op1, 0))) + && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op1, 0)))) + { + final_type = result_type; + op1 = TREE_OPERAND (op1, 0); + result_type = TREE_TYPE (op1); + } + if (TREE_CODE (op1) == INTEGER_CST + && TREE_CODE (op0) == NOP_EXPR + && TYPE_PRECISION (dt0) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0))) + && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)))) + { + final_type = result_type; + op0 = TREE_OPERAND (op0, 0); + result_type = TREE_TYPE (op0); + } + break; + + case TRUNC_MOD_EXPR: + if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) + shorten = 1; + break; + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == POINTER_TYPE || code1 == REAL_TYPE)) + { + /* Result of these operations is always an int, + but that does not mean the operands should be + converted to ints! */ + result_type = integer_type_node; + op0 = truthvalue_conversion (op0); + op1 = truthvalue_conversion (op1); + converted = 1; + } + break; + + /* Shift operations: result has same type as first operand; + always convert second operand to int. + Also set SHORT_SHIFT if shifting rightward. */ + + case RSHIFT_EXPR: + if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) + { + result_type = dt0; + if (TREE_CODE (op1) == INTEGER_CST + && TREE_INT_CST_LOW (op1) > 0) + short_shift = 1; + /* Convert the shift-count to an integer, regardless of + size of value being shifted. */ + if (TREE_TYPE (op1) != integer_type_node) + op1 = convert (integer_type_node, op1); + } + break; + + case LSHIFT_EXPR: + if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) + { + result_type = dt0; + if (TREE_CODE (op1) == INTEGER_CST + && TREE_INT_CST_LOW (op1) < 0) + short_shift = 1; + /* Convert the shift-count to an integer, regardless of + size of value being shifted. */ + if (TREE_TYPE (op1) != integer_type_node) + op1 = convert (integer_type_node, op1); + } + break; + + case RROTATE_EXPR: + case LROTATE_EXPR: + if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE) + { + result_type = dt0; + /* Convert the shift-count to an integer, regardless of + size of value being shifted. */ + if (TREE_TYPE (op1) != integer_type_node) + op1 = convert (integer_type_node, op1); + } + break; + + case EQ_EXPR: + case NE_EXPR: + /* Result of comparison is always int, + but don't convert the args to int! */ + result_type = integer_type_node; + converted = 1; + if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) + short_compare = 1; + else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) + { + register tree tt0 = TREE_TYPE (dt0); + register tree tt1 = TREE_TYPE (dt1); + /* Anything compares with void *. void * compares with anything. + Otherwise, the targets must be the same. */ + if (comp_target_types (dt0, dt1)) + ; + else if (TYPE_MAIN_VARIANT (tt0) == void_type_node) + { + if (pedantic && TREE_CODE (tt1) == FUNCTION_TYPE) + warning ("ANSI C forbids comparison of `void *' with function pointer"); + } + else if (TYPE_MAIN_VARIANT (tt1) == void_type_node) + { + if (pedantic && TREE_CODE (tt0) == FUNCTION_TYPE) + warning ("ANSI C forbids comparison of `void *' with function pointer"); + } + else + warning ("comparison of distinct pointer types lacks a cast"); + } + else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST + && integer_zerop (op1)) + op1 = null_pointer_node; + else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST + && integer_zerop (op0)) + op0 = null_pointer_node; + else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) + { + if (! flag_traditional) + warning ("comparison between pointer and integer"); + op1 = convert (TREE_TYPE (op0), op1); + } + else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE) + { + if (! flag_traditional) + warning ("comparison between pointer and integer"); + op0 = convert (TREE_TYPE (op1), op0); + } + else + /* If args are not valid, clear out RESULT_TYPE + to cause an error message later. */ + result_type = 0; + break; + + case MAX_EXPR: + case MIN_EXPR: + if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) + shorten = 1; + else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) + { + if (! comp_target_types (dt0, dt1)) + warning ("comparison of distinct pointer types lacks a cast"); + else if (pedantic + && TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) + warning ("ANSI C forbids ordered comparisons of pointers to functions"); + result_type = commontype (dt0, dt1); + } + break; + + case LE_EXPR: + case GE_EXPR: + case LT_EXPR: + case GT_EXPR: + if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) + short_compare = 1; + else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) + { + if (! comp_target_types (dt0, dt1)) + warning ("comparison of distinct pointer types lacks a cast"); + else if (pedantic + && TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) + warning ("ANSI C forbids ordered comparisons of pointers to functions"); + result_type = integer_type_node; + } + else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST + && integer_zerop (op1)) + { + result_type = integer_type_node; + op1 = null_pointer_node; + if (! flag_traditional) + warning ("ordered comparison of pointer with integer zero"); + } + else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST + && integer_zerop (op0)) + { + result_type = integer_type_node; + op0 = null_pointer_node; + if (pedantic) + warning ("ordered comparison of pointer with integer zero"); + } + else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) + { + result_type = integer_type_node; + if (! flag_traditional) + warning ("comparison between pointer and integer"); + op1 = convert (TREE_TYPE (op0), op1); + } + else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE) + { + result_type = integer_type_node; + if (! flag_traditional) + warning ("comparison between pointer and integer"); + op0 = convert (TREE_TYPE (op1), op0); + } + converted = 1; + break; + } + + if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE) + && (code1 == INTEGER_TYPE || code1 == REAL_TYPE)) + { + if (shorten || common || short_compare) + result_type = commontype (dt0, dt1); + + /* For certain operations (which identify themselves by shorten != 0) + if both args were extended from the same smaller type, + do the arithmetic in that type and then extend. + + shorten !=0 and !=1 indicates a bitwise operation. + For them, this optimization is safe only if + both args are zero-extended or both are sign-extended. + Otherwise, we might change the result. + Eg, (short)-1 | (unsigned short)-1 is (int)-1 + but calculated in (unsigned short) it would be (unsigned short)-1. */ + + if (shorten) + { + int unsigned0, unsigned1; + tree arg0 = get_narrower (op0, &unsigned0); + tree arg1 = get_narrower (op1, &unsigned1); + /* UNS is 1 if the operation to be done is an unsigned one. */ + int uns = TREE_UNSIGNED (result_type); + tree type; + + final_type = result_type; + + /* Handle the case that OP0 does not *contain* a conversion + but it *requires* conversion to FINAL_TYPE. */ + + if (op0 == arg0 && TREE_TYPE (op0) != final_type) + unsigned0 = TREE_UNSIGNED (TREE_TYPE (op0)); + if (op1 == arg1 && TREE_TYPE (op1) != final_type) + unsigned1 = TREE_UNSIGNED (TREE_TYPE (op1)); + + /* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */ + + /* For bitwise operations, signedness of nominal type + does not matter. Consider only how operands were extended. */ + if (shorten == -1) + uns = unsigned0; + + /* Note that in all three cases below we refrain from optimizing + an unsigned operation on sign-extended args. + That would not be valid. */ + + /* Both args variable: if both extended in same way + from same width, do it in that width. + Do it unsigned if args were zero-extended. */ + if ((TYPE_PRECISION (TREE_TYPE (arg0)) + < TYPE_PRECISION (result_type)) + && (TYPE_PRECISION (TREE_TYPE (arg1)) + == TYPE_PRECISION (TREE_TYPE (arg0))) + && unsigned0 == unsigned1 + && (unsigned0 || !uns)) + result_type + = signed_or_unsigned_type (unsigned0, + commontype (TREE_TYPE (arg0), TREE_TYPE (arg1))); + else if (TREE_CODE (arg0) == INTEGER_CST + && (unsigned1 || !uns) + && (TYPE_PRECISION (TREE_TYPE (arg1)) + < TYPE_PRECISION (result_type)) + && (type = signed_or_unsigned_type (unsigned1, + TREE_TYPE (arg1)), + int_fits_type_p (arg0, type))) + result_type = type; + else if (TREE_CODE (arg1) == INTEGER_CST + && (unsigned0 || !uns) + && (TYPE_PRECISION (TREE_TYPE (arg0)) + < TYPE_PRECISION (result_type)) + && (type = signed_or_unsigned_type (unsigned0, + TREE_TYPE (arg0)), + int_fits_type_p (arg1, type))) + result_type = type; + } + + /* Shifts can be shortened if shifting right. */ + + if (short_shift) + { + int unsigned_arg; + tree arg0 = get_narrower (op0, &unsigned_arg); + + final_type = result_type; + + if (arg0 == op0 && final_type == TREE_TYPE (op0)) + unsigned_arg = TREE_UNSIGNED (TREE_TYPE (op0)); + + if (TYPE_PRECISION (TREE_TYPE (arg0)) < TYPE_PRECISION (result_type) + /* If arg is sign-extended and then unsigned-shifted, + we can simulate this with a signed shift in arg's type + only if the extended result is at least twice as wide + as the arg. Otherwise, the shift could use up all the + ones made by sign-extension and bring in zeros. + We can't optimize that case at all, but in most machines + it never happens because available widths are 2**N. */ + && (!TREE_UNSIGNED (final_type) + || unsigned_arg + || 2 * TYPE_PRECISION (TREE_TYPE (arg0)) <= TYPE_PRECISION (result_type))) + { + /* Do an unsigned shift if the operand was zero-extended. */ + result_type + = signed_or_unsigned_type (unsigned_arg, + TREE_TYPE (arg0)); + /* Convert value-to-be-shifted to that type. */ + if (TREE_TYPE (op0) != result_type) + op0 = convert (result_type, op0); + converted = 1; + } + } + + /* Comparison operations are shortened too but differently. + They identify themselves by setting short_compare = 1. */ + + if (short_compare) + { + /* Don't write &op0, etc., because that would prevent op0 + from being kept in a register. + Instead, make copies of the our local variables and + pass the copies by reference, then copy them back afterward. */ + tree xop0 = op0, xop1 = op1, xresult_type = result_type; + enum tree_code xresultcode = resultcode; + tree val + = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode); + if (val != 0) + return val; + op0 = xop0, op1 = xop1, result_type = xresult_type; + resultcode = xresultcode; + } + } + + /* At this point, RESULT_TYPE must be nonzero to avoid an error message. + If CONVERTED is zero, both args will be converted to type RESULT_TYPE. + Then the expression will be built. + It will be given type FINAL_TYPE if that is nonzero; + otherwise, it will be given type RESULT_TYPE. */ + + if (!result_type) + { + binary_op_error (error_code); + return error_mark_node; + } + + if (! converted) + { + if (TREE_TYPE (op0) != result_type) + op0 = convert (result_type, op0); + if (TREE_TYPE (op1) != result_type) + op1 = convert (result_type, op1); + } + + { + register tree result = build (resultcode, result_type, op0, op1); + register tree folded; + + folded = fold (result); + if (folded == result) + TREE_LITERAL (folded) = TREE_LITERAL (op0) & TREE_LITERAL (op1); + if (final_type != 0) + return convert (final_type, folded); + return folded; + } +} + +/* Return a tree for the sum or difference (RESULTCODE says which) + of pointer PTROP and integer INTOP. */ + +static tree +pointer_int_sum (resultcode, ptrop, intop) + enum tree_code resultcode; + register tree ptrop, intop; +{ + tree size_exp; + + register tree result; + register tree folded; + + /* The result is a pointer of the same type that is being added. */ + + register tree result_type = datatype (ptrop); + + if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE) + { + if (pedantic || warn_pointer_arith) + warning ("pointer of type `void *' used in arithmetic"); + size_exp = integer_one_node; + } + else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE) + { + if (pedantic || warn_pointer_arith) + warning ("pointer to a function used in arithmetic"); + size_exp = integer_one_node; + } + else + size_exp = c_sizeof (TREE_TYPE (result_type)); + + /* If what we are about to multiply by the size of the elements + contains a constant term, apply distributive law + and multiply that constant term separately. + This helps produce common subexpressions. */ + + if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR) + && ! TREE_LITERAL (intop) + && TREE_LITERAL (TREE_OPERAND (intop, 1)) + && TREE_LITERAL (size_exp)) + { + enum tree_code subcode = resultcode; + if (TREE_CODE (intop) == MINUS_EXPR) + subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR); + ptrop = build_binary_op (subcode, ptrop, TREE_OPERAND (intop, 1)); + intop = TREE_OPERAND (intop, 0); + } + + /* Convert the integer argument to a type the same size as a pointer + so the multiply won't overflow spuriously. */ + + if (TYPE_PRECISION (TREE_TYPE (intop)) != POINTER_SIZE) + intop = convert (type_for_size (POINTER_SIZE, 0), intop); + + /* Replace the integer argument + with a suitable product by the object size. */ + + intop = build_binary_op (MULT_EXPR, intop, size_exp); + + /* Create the sum or difference. */ + + result = build (resultcode, result_type, ptrop, intop); + + folded = fold (result); + if (folded == result) + TREE_LITERAL (folded) = TREE_LITERAL (ptrop) & TREE_LITERAL (intop); + return folded; +} + +/* Return a tree for the difference of pointers OP0 and OP1. + The resulting tree has type int. */ + +static tree +pointer_diff (op0, op1) + register tree op0, op1; +{ + tree dt0 = datatype (op0); + enum tree_code resultcode; + register tree result, folded; + tree restype = type_for_size (POINTER_SIZE, 0); + + if (pedantic) + { + if (TREE_CODE (TREE_TYPE (dt0)) == VOID_TYPE) + warning ("pointer of type `void *' used in subtraction"); + if (TREE_CODE (TREE_TYPE (dt0)) == FUNCTION_TYPE) + warning ("pointer to a function used in subtraction"); + } + + /* First do the subtraction as integers; + then drop through to build the divide operator. */ + + op0 = build_binary_op (MINUS_EXPR, + convert (restype, op0), convert (restype, op1)); + op1 = c_sizeof_nowarn (TREE_TYPE (dt0)); + + /* Create the sum or difference. */ + + result = build (EXACT_DIV_EXPR, restype, op0, op1); + + folded = fold (result); + if (folded == result) + TREE_LITERAL (folded) = TREE_LITERAL (op0) & TREE_LITERAL (op1); + return folded; +} + +/* Print an error message for invalid operands to arith operation CODE. + NOP_EXPR is used as a special case (see truthvalue_conversion). */ + +static void +binary_op_error (code) + enum tree_code code; +{ + register char *opname; + switch (code) + { + case NOP_EXPR: + error ("invalid truth-value expression"); + return; + + case PLUS_EXPR: + opname = "+"; break; + case MINUS_EXPR: + opname = "-"; break; + case MULT_EXPR: + opname = "*"; break; + case MAX_EXPR: + opname = "max"; break; + case MIN_EXPR: + opname = "min"; break; + case EQ_EXPR: + opname = "=="; break; + case NE_EXPR: + opname = "!="; break; + case LE_EXPR: + opname = "<="; break; + case GE_EXPR: + opname = ">="; break; + case LT_EXPR: + opname = "<"; break; + case GT_EXPR: + opname = ">"; break; + case LSHIFT_EXPR: + opname = "<<"; break; + case RSHIFT_EXPR: + opname = ">>"; break; + case TRUNC_MOD_EXPR: + opname = "%"; break; + case TRUNC_DIV_EXPR: + opname = "/"; break; + case BIT_AND_EXPR: + opname = "&"; break; + case BIT_IOR_EXPR: + opname = "|"; break; + case TRUTH_ANDIF_EXPR: + opname = "&&"; break; + case TRUTH_ORIF_EXPR: + opname = "||"; break; + case BIT_XOR_EXPR: + opname = "^"; break; + } + error ("invalid operands to binary %s", opname); +} + +/* Subroutine of build_binary_op_nodefault, used for comparison operations. + See if the operands have both been converted from subword integer types + and, if so, perhaps change them both back to their original type. + + The arguments of this function are all pointers to local variables + of build_binary_op_nodefault: OP0_PTR is &OP0, OP1_PTR is &OP1, + RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE. + + If this function returns nonzero, it means that the comparison has + a constant value. What this function returns is an expression for + that value. */ + +static tree +shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr) + tree *op0_ptr, *op1_ptr; + tree *restype_ptr; + enum tree_code *rescode_ptr; +{ + register tree type; + tree op0 = *op0_ptr; + tree op1 = *op1_ptr; + int unsignedp0, unsignedp1; + int real1, real2; + tree primop0, primop1; + enum tree_code code = *rescode_ptr; + + /* Throw away any conversions to wider types + already present in the operands. */ + + primop0 = get_narrower (op0, &unsignedp0); + primop1 = get_narrower (op1, &unsignedp1); + + /* Handle the case that OP0 does not *contain* a conversion + but it *requires* conversion to FINAL_TYPE. */ + + if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr) + unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0)); + if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr) + unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1)); + + /* If one of the operands must be floated, we cannot optimize. */ + real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE; + real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE; + + /* If first arg is constant, swap the args (changing operation + so value is preserved), for canonicalization. */ + + if (TREE_LITERAL (primop0)) + { + register tree tem = primop0; + register int temi = unsignedp0; + primop0 = primop1; + primop1 = tem; + tem = op0; + op0 = op1; + op1 = tem; + *op0_ptr = op0; + *op1_ptr = op1; + unsignedp0 = unsignedp1; + unsignedp1 = temi; + temi = real1; + real1 = real2; + real2 = temi; + + switch (code) + { + case LT_EXPR: + code = GT_EXPR; + break; + case GT_EXPR: + code = LT_EXPR; + break; + case LE_EXPR: + code = GE_EXPR; + break; + case GE_EXPR: + code = LE_EXPR; + break; + } + *rescode_ptr = code; + } + + /* If comparing an integer against a constant more bits wide, + maybe we can deduce a value of 1 or 0 independent of the data. + Or else truncate the constant now + rather than extend the variable at run time. + + This is only interesting if the constant is the wider arg. + Also, it is not safe if the constant is unsigned and the + variable arg is signed, since in this case the variable + would be sign-extended and then regarded as unsigned. + Our technique fails in this case because the lowest/highest + possible unsigned results don't follow naturally from the + lowest/highest possible values of the variable operand. + For just EQ_EXPR and NE_EXPR there is another technique that + could be used: see if the constant can be faithfully represented + in the other operand's type, by truncating it and reextending it + and see if that preserves the constant's value. */ + + if (!real1 && !real2 + && TREE_CODE (primop1) == INTEGER_CST + && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)) + { + int min_gt, max_gt, min_lt, max_lt; + tree maxval, minval; + /* 1 if comparison is nominally unsigned. */ + int unsignedp = TREE_UNSIGNED (*restype_ptr); + tree val; + + type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)); + + maxval = TYPE_MAX_VALUE (type); + minval = TYPE_MIN_VALUE (type); + + if (unsignedp && !unsignedp0) + *restype_ptr = signed_type (*restype_ptr); + + if (TREE_TYPE (primop1) != *restype_ptr) + primop1 = convert (*restype_ptr, primop1); + if (type != *restype_ptr) + { + minval = convert (*restype_ptr, minval); + maxval = convert (*restype_ptr, maxval); + } + + if (unsignedp && unsignedp0) + { + min_gt = INT_CST_LT_UNSIGNED (primop1, minval); + max_gt = INT_CST_LT_UNSIGNED (primop1, maxval); + min_lt = INT_CST_LT_UNSIGNED (minval, primop1); + max_lt = INT_CST_LT_UNSIGNED (maxval, primop1); + } + else + { + min_gt = INT_CST_LT (primop1, minval); + max_gt = INT_CST_LT (primop1, maxval); + min_lt = INT_CST_LT (minval, primop1); + max_lt = INT_CST_LT (maxval, primop1); + } + + val = 0; + /* This used to be a switch, but Genix compiler can't handle that. */ + if (code == NE_EXPR) + { + if (max_lt || min_gt) + val = integer_one_node; + } + else if (code == EQ_EXPR) + { + if (max_lt || min_gt) + val = integer_zero_node; + } + else if (code == LT_EXPR) + { + if (max_lt) + val = integer_one_node; + if (!min_lt) + val = integer_zero_node; + } + else if (code == GT_EXPR) + { + if (min_gt) + val = integer_one_node; + if (!max_gt) + val = integer_zero_node; + } + else if (code == LE_EXPR) + { + if (!max_gt) + val = integer_one_node; + if (min_gt) + val = integer_zero_node; + } + else if (code == GE_EXPR) + { + if (!min_lt) + val = integer_one_node; + if (max_lt) + val = integer_zero_node; + } + + /* If primop0 was sign-extended and unsigned comparison specd, + we did a signed comparison above using the signed type bounds. + But the comparison we output must be unsigned. + + Also, for inequalities, VAL is no good; but if the signed + comparison had *any* fixed result, it follows that the + unsigned comparison just tests the sign in reverse + (positive values are LE, negative ones GE). + So we can generate an unsigned comparison + against an extreme value of the signed type. */ + + if (unsignedp && !unsignedp0) + { + if (val != 0) + switch (code) + { + case LT_EXPR: + case GE_EXPR: + primop1 = TYPE_MIN_VALUE (type); + val = 0; + break; + + case LE_EXPR: + case GT_EXPR: + primop1 = TYPE_MAX_VALUE (type); + val = 0; + break; + } + type = unsigned_type (type); + } + + if (max_lt && !unsignedp0) + { + /* This is the case of (char)x >?< 0x80, which people used to use + expecting old C compilers to change the 0x80 into -0x80. */ + if (val == integer_zero_node) + warning ("comparison is always 0 due to limited range of data type"); + if (val == integer_one_node) + warning ("comparison is always 1 due to limited range of data type"); + } + + if (val != 0) + { + /* Don't forget to evaluate PRIMOP0 if it has side effects. */ + if (TREE_VOLATILE (primop0)) + return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val); + return val; + } + + /* Value is not predetermined, but do the comparison + in the type of the operand that is not constant. + TYPE is already properly set. */ + } + else if (real1 && real2 + && TYPE_PRECISION (TREE_TYPE (primop0)) == TYPE_PRECISION (TREE_TYPE (primop1))) + type = TREE_TYPE (primop0); + + /* If args' natural types are both narrower than nominal type + and both extend in the same manner, compare them + in the type of the wider arg. + Otherwise must actually extend both to the nominal + common type lest different ways of extending + alter the result. + (eg, (short)-1 == (unsigned short)-1 should be 0.) */ + + else if (unsignedp0 == unsignedp1 && real1 == real2 + && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr) + && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr)) + { + type = commontype (TREE_TYPE (primop0), TREE_TYPE (primop1)); + type = signed_or_unsigned_type (unsignedp0 + || TREE_UNSIGNED (*restype_ptr), + type); + /* Make sure shorter operand is extended the right way + to match the longer operand. */ + primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)), + primop0); + primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)), + primop1); + } + else + { + /* Here we must do the comparison on the nominal type + using the args exactly as we received them. */ + type = *restype_ptr; + primop0 = op0; + primop1 = op1; + } + + *op0_ptr = convert (type, primop0); + *op1_ptr = convert (type, primop1); + + *restype_ptr = integer_type_node; + + return 0; +} + +/* Construct and perhaps optimize a tree representation + for a unary operation. CODE, a tree_code, specifies the operation + and XARG is the operand. NOCONVERT nonzero suppresses + the default promotions (such as from short to int). */ + +tree +build_unary_op (code, xarg, noconvert) + enum tree_code code; + tree xarg; + int noconvert; +{ + /* No default_conversion here. It causes trouble for ADDR_EXPR. */ + register tree arg = xarg; + register tree argtype = 0; + register enum tree_code typecode = TREE_CODE (TREE_TYPE (arg)); + char *errstring = NULL; + tree val; + + if (typecode == ERROR_MARK) + return error_mark_node; + if (typecode == ENUMERAL_TYPE) + typecode = INTEGER_TYPE; + + switch (code) + { + case CONVERT_EXPR: + /* This is used for unary plus, because a CONVERT_EXPR + is enough to prevent anybody from looking inside for + associativity, but won't generate any code. */ + if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) + errstring = "wrong type argument to unary plus"; + else if (!noconvert) + arg = default_conversion (arg); + break; + + case NEGATE_EXPR: + if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) + errstring = "wrong type argument to unary minus"; + else if (!noconvert) + arg = default_conversion (arg); + break; + + case BIT_NOT_EXPR: + if (typecode != INTEGER_TYPE) + errstring = "wrong type argument to bit-complement"; + else if (!noconvert) + arg = default_conversion (arg); + break; + + case ABS_EXPR: + if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE)) + errstring = "wrong type argument to abs"; + else if (!noconvert) + arg = default_conversion (arg); + break; + + case TRUTH_NOT_EXPR: + if (typecode != INTEGER_TYPE + && typecode != REAL_TYPE && typecode != POINTER_TYPE + /* This will convert to a pointer. */ + && typecode != ARRAY_TYPE && typecode != FUNCTION_TYPE) + { + errstring = "wrong type argument to unary exclamation mark"; + break; + } + arg = truthvalue_conversion (arg); + val = invert_truthvalue (arg); + if (val) return val; + break; + + case NOP_EXPR: + break; + + case PREINCREMENT_EXPR: + case POSTINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTDECREMENT_EXPR: + /* Handle complex lvalues (when permitted) + by reduction to simpler cases. */ + + val = unary_complex_lvalue (code, arg); + if (val != 0) + return val; + + /* Report invalid types. */ + + if (typecode != POINTER_TYPE + && typecode != INTEGER_TYPE && typecode != REAL_TYPE) + { + if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR) + errstring ="wrong type argument to increment"; + else + errstring ="wrong type argument to decrement"; + break; + } + + /* Report something read-only. */ + + if (TREE_READONLY (arg)) + readonly_warning (arg, + ((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? "increment" : "decrement")); + + { + register tree inc; + tree result_type = TREE_TYPE (arg); + + arg = get_unwidened (arg, 0); + argtype = TREE_TYPE (arg); + + /* Compute the increment. */ + + if (typecode == POINTER_TYPE) + { + if (pedantic + && (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)) + warning ("wrong type argument to %s", + ((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? "increment" : "decrement")); + inc = c_sizeof_nowarn (TREE_TYPE (result_type)); + } + else + inc = integer_one_node; + + inc = convert (argtype, inc); + + /* Handle incrementing a cast-expression. */ + + if (!pedantic) + switch (TREE_CODE (arg)) + { + case NOP_EXPR: + case CONVERT_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + case FIX_CEIL_EXPR: + { + tree incremented, modify, value; + arg = stabilize_reference (arg); + if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR) + value = arg; + else + value = save_expr (arg); + incremented = build (((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? PLUS_EXPR : MINUS_EXPR), + argtype, value, inc); + TREE_VOLATILE (incremented) = 1; + modify = build_modify_expr (arg, NOP_EXPR, incremented); + return build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value); + } + } + + /* Complain about anything else that is not a true lvalue. */ + if (!lvalue_or_else (arg, ((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? "increment" : "decrement"))) + return error_mark_node; + + val = build (code, TREE_TYPE (arg), arg, inc); + TREE_VOLATILE (val) = 1; + return convert (result_type, val); + } + + case ADDR_EXPR: + /* Note that this operation never does default_conversion + regardless of NOCONVERT. */ + + /* Let &* cancel out to simplify resulting code. */ + if (TREE_CODE (arg) == INDIRECT_REF) + { + /* Don't let this be an lvalue. */ + if (lvalue_p (TREE_OPERAND (arg, 0))) + return build (NOP_EXPR, TREE_TYPE (TREE_OPERAND (arg, 0)), + TREE_OPERAND (arg, 0)); + return TREE_OPERAND (arg, 0); + } + + /* For &x[y], return x+y */ + if (TREE_CODE (arg) == ARRAY_REF) + { + if (mark_addressable (TREE_OPERAND (arg, 0)) == 0) + return error_mark_node; + return build_binary_op (PLUS_EXPR, TREE_OPERAND (arg, 0), + TREE_OPERAND (arg, 1)); + } + + /* Handle complex lvalues (when permitted) + by reduction to simpler cases. */ + val = unary_complex_lvalue (code, arg); + if (val != 0) + return val; + + /* Address of a cast is just a cast of the address + of the operand of the cast. */ + switch (TREE_CODE (arg)) + { + case NOP_EXPR: + case CONVERT_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + case FIX_CEIL_EXPR: + if (pedantic) + warning ("ANSI C forbids the address of a cast expression"); + return convert (build_pointer_type (TREE_TYPE (arg)), + build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), + 0)); + } + + /* Allow the address of a constructor if all the elements + are constant. */ + if (TREE_CODE (arg) == CONSTRUCTOR && TREE_LITERAL (arg)) + ; + /* Anything not already handled and not a true memory reference + is an error. */ + else if (typecode != FUNCTION_TYPE && !lvalue_or_else (arg, "unary `&'")) + return error_mark_node; + + /* Ordinary case; arg is a COMPONENT_REF or a decl. */ + argtype = TREE_TYPE (arg); + if (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg)) + argtype = c_build_type_variant (argtype, + TREE_READONLY (arg), + TREE_THIS_VOLATILE (arg)); + + argtype = build_pointer_type (argtype); + + if (mark_addressable (arg) == 0) + return error_mark_node; + + { + tree addr; + + if (TREE_CODE (arg) == COMPONENT_REF) + { + tree field = TREE_OPERAND (arg, 1); + + addr = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), 0); + + if (TREE_PACKED (field)) + { + error ("attempt to take address of bit-field structure member `%s'", + IDENTIFIER_POINTER (DECL_NAME (field))); + return error_mark_node; + } + + addr = convert (argtype, addr); + + if (DECL_OFFSET (field) != 0) + { + tree offset = build_int_2 ((DECL_OFFSET (field) + / BITS_PER_UNIT), + 0); + TREE_TYPE (offset) = argtype; + addr = fold (build (PLUS_EXPR, argtype, addr, offset)); + } + } + else + addr = build (code, argtype, arg); + + /* Address of a static or external variable or + function counts as a constant */ + TREE_LITERAL (addr) = staticp (arg); + return addr; + } + } + + if (!errstring) + { + if (argtype == 0) + argtype = TREE_TYPE (arg); + return fold (build (code, argtype, arg)); + } + + error (errstring); + return error_mark_node; +} + +/* If CONVERSIONS is a conversion expression or a nested sequence of such, + convert ARG with the same conversions in the same order + and return the result. */ + +static tree +convert_sequence (conversions, arg) + tree conversions; + tree arg; +{ + switch (TREE_CODE (conversions)) + { + case NOP_EXPR: + case CONVERT_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + case FIX_CEIL_EXPR: + return convert (TREE_TYPE (conversions), + convert_sequence (TREE_OPERAND (conversions, 0), + arg)); + + default: + return arg; + } +} + +/* Apply unary lvalue-demanding operator CODE to the expression ARG + for certain kinds of expressions which are not really lvalues + but which we can accept as lvalues. + + If ARG is not a kind of expression we can handle, return zero. */ + +static tree +unary_complex_lvalue (code, arg) + enum tree_code code; + tree arg; +{ + if (pedantic) + return 0; + + /* Handle (a, b) used as an "lvalue". */ + if (TREE_CODE (arg) == COMPOUND_EXPR) + { + tree real_result = build_unary_op (code, TREE_OPERAND (arg, 1), 0); + return build (COMPOUND_EXPR, TREE_TYPE (real_result), + TREE_OPERAND (arg, 0), real_result); + } + + /* Handle (a ? b : c) used as an "lvalue". */ + if (TREE_CODE (arg) == COND_EXPR) + return (build_conditional_expr + (TREE_OPERAND (arg, 0), + build_unary_op (code, TREE_OPERAND (arg, 1), 0), + build_unary_op (code, TREE_OPERAND (arg, 2), 0))); + + return 0; +} + +/* Warn about storing in something that is `const'. */ + +void +readonly_warning (arg, string) + tree arg; + char *string; +{ + char buf[80]; + strcpy (buf, string); + + if (TREE_CODE (arg) == COMPONENT_REF) + { + if (TREE_READONLY (TREE_OPERAND (arg, 0))) + readonly_warning (TREE_OPERAND (arg, 0), string); + else + { + strcat (buf, " of read-only member `%s'"); + warning (buf, IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (arg, 1)))); + } + } + else if (TREE_CODE (arg) == VAR_DECL) + { + strcat (buf, " of read-only variable `%s'"); + warning (buf, IDENTIFIER_POINTER (DECL_NAME (arg))); + } + else + { + warning ("%s of read-only location", buf); + } +} + +/* Prepare expr to be an argument of a TRUTH_NOT_EXPR, + or validate its data type for an `if' or `while' statement or ?..: exp. + + This preparation consists of taking the ordinary + representation of an expression expr and producing a valid tree + boolean expression describing whether expr is nonzero. We could + simply always do build_binary_op (NE_EXPR, expr, integer_zero_node), + but we optimize comparisons, &&, ||, and ! */ + +tree +truthvalue_conversion (expr) + tree expr; +{ + register enum tree_code form; + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since EXPR is being used in non-lvalue context. */ + if (TREE_CODE (expr) == NOP_EXPR + && TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0))) + expr = TREE_OPERAND (expr, 0); + + form = TREE_CODE (expr); + + if (form == EQ_EXPR && integer_zerop (TREE_OPERAND (expr, 1))) + return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0); + + /* A one-bit unsigned bit-field is already acceptable. */ + if (form == COMPONENT_REF + && 1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1))) + && 1 == DECL_SIZE_UNIT (TREE_OPERAND (expr, 1)) + && TREE_UNSIGNED (TREE_OPERAND (expr, 1))) + return expr; + + if (form == TRUTH_ANDIF_EXPR || form == TRUTH_ORIF_EXPR + || form == TRUTH_AND_EXPR || form == TRUTH_OR_EXPR + || form == TRUTH_NOT_EXPR + || form == EQ_EXPR || form == NE_EXPR + || form == LE_EXPR || form == GE_EXPR + || form == LT_EXPR || form == GT_EXPR + || form == ERROR_MARK) + return expr; + + /* Unary minus has no effect on whether its argument is nonzero. */ + if (form == NEGATE_EXPR) + return truthvalue_conversion (TREE_OPERAND (expr, 0)); + + /* Distribute the conversion into the arms of a COND_EXPR. */ + if (form == COND_EXPR) + return build (COND_EXPR, integer_type_node, + TREE_OPERAND (expr, 0), + truthvalue_conversion (TREE_OPERAND (expr, 1)), + truthvalue_conversion (TREE_OPERAND (expr, 2))); + + /* Sign-extension and zero-extension has no effect. */ + if (form == NOP_EXPR + && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE + && (TYPE_PRECISION (TREE_TYPE (expr)) + > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))) + return truthvalue_conversion (TREE_OPERAND (expr, 0)); + + return build_binary_op_nodefault (NE_EXPR, default_conversion (expr), + integer_zero_node, NOP_EXPR); +} + +/* Return a simplified tree node for the truth-negation of ARG + (perhaps by altering ARG). + If it can't be simplified, return 0. */ + +static tree +invert_truthvalue (arg) + tree arg; +{ + switch (TREE_CODE (arg)) + { + case NE_EXPR: + TREE_SET_CODE (arg, EQ_EXPR); + return arg; + + case EQ_EXPR: + TREE_SET_CODE (arg, NE_EXPR); + return arg; + + case GE_EXPR: + TREE_SET_CODE (arg, LT_EXPR); + return arg; + + case GT_EXPR: + TREE_SET_CODE (arg, LE_EXPR); + return arg; + + case LE_EXPR: + TREE_SET_CODE (arg, GT_EXPR); + return arg; + + case LT_EXPR: + TREE_SET_CODE (arg, GE_EXPR); + return arg; + + case TRUTH_AND_EXPR: + return build (TRUTH_OR_EXPR, TREE_TYPE (arg), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 0), 0), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 1), 0)); + + case TRUTH_OR_EXPR: + return build (TRUTH_AND_EXPR, TREE_TYPE (arg), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 0), 0), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 1), 0)); + + case TRUTH_ANDIF_EXPR: + return build (TRUTH_ORIF_EXPR, TREE_TYPE (arg), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 0), 0), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 1), 0)); + + case TRUTH_ORIF_EXPR: + return build (TRUTH_ANDIF_EXPR, TREE_TYPE (arg), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 0), 0), + build_unary_op (TRUTH_NOT_EXPR, + TREE_OPERAND (arg, 1), 0)); + + case TRUTH_NOT_EXPR: + return TREE_OPERAND (arg, 0); + + case COND_EXPR: + return build (COND_EXPR, TREE_TYPE (arg), TREE_OPERAND (arg, 0), + build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (arg, 1), 0), + build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (arg, 2), 0)); + } + return 0; +} + +/* Mark EXP saying that we need to be able to take the + address of it; it should not be allocated in a register. + Value is 1 if successful. */ + +int +mark_addressable (exp) + tree exp; +{ + register tree x = exp; + while (1) + switch (TREE_CODE (x)) + { + case ADDR_EXPR: + case COMPONENT_REF: + case ARRAY_REF: + x = TREE_OPERAND (x, 0); + break; + + case VAR_DECL: + case CONST_DECL: + case PARM_DECL: + case RESULT_DECL: + if (TREE_REGDECL (x) && !TREE_ADDRESSABLE (x)) + { + if (TREE_PUBLIC (x)) + { + error ("address of global register variable `%s' requested", + IDENTIFIER_POINTER (DECL_NAME (x))); + return 0; + } + warning ("address of register variable `%s' requested", + IDENTIFIER_POINTER (DECL_NAME (x))); + } + put_var_into_stack (x); + + /* drops in */ + case FUNCTION_DECL: + TREE_ADDRESSABLE (x) = 1; + TREE_ADDRESSABLE (DECL_NAME (x)) = 1; + + default: + return 1; + } +} + +/* Build and return a conditional expression IFEXP ? OP1 : OP2. */ + +tree +build_conditional_expr (ifexp, op1, op2) + tree ifexp, op1, op2; +{ + register tree type1; + register tree type2; + register enum tree_code code1; + register enum tree_code code2; + register tree result_type = NULL; + + /* If second operand is omitted, it is the same as the first one; + make sure it is calculated only once. */ + if (op1 == 0) + { + if (pedantic) + warning ("ANSI C forbids omitting the middle term of a ?: expression"); + ifexp = op1 = save_expr (ifexp); + } + + ifexp = truthvalue_conversion (default_conversion (ifexp)); + + if (TREE_CODE (ifexp) == ERROR_MARK + || TREE_CODE (TREE_TYPE (op1)) == ERROR_MARK + || TREE_CODE (TREE_TYPE (op2)) == ERROR_MARK) + return error_mark_node; + +#if 0 /* Produces wrong result if within sizeof. */ + /* Don't promote the operands separately if they promote + the same way. Return the unpromoted type and let the combined + value get promoted if necessary. */ + + if (TREE_TYPE (op1) == TREE_TYPE (op2) + && TREE_CODE (TREE_TYPE (op1)) != ARRAY_TYPE + && TREE_CODE (TREE_TYPE (op1)) != ENUMERAL_TYPE + && TREE_CODE (TREE_TYPE (op1)) != FUNCTION_TYPE) + { + if (TREE_LITERAL (ifexp) + && (TREE_CODE (ifexp) == INTEGER_CST + || TREE_CODE (ifexp) == ADDR_EXPR)) + return (integer_zerop (ifexp) ? op2 : op1); + + return build (COND_EXPR, TREE_TYPE (op1), ifexp, op1, op2); + } +#endif + + /* They don't match; promote them both and then try to reconcile them. */ + + if (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE) + op1 = default_conversion (op1); + if (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE) + op2 = default_conversion (op2); + + type1 = TREE_TYPE (op1); + code1 = TREE_CODE (type1); + type2 = TREE_TYPE (op2); + code2 = TREE_CODE (type2); + + /* Quickly detect the usual case where op1 and op2 have the same type + after promotion. */ + if (type1 == type2) + result_type = type1; + else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE) + && (code2 == INTEGER_TYPE || code2 == REAL_TYPE)) + { + result_type = commontype (type1, type2); + } + else if (code1 == VOID_TYPE || code2 == VOID_TYPE) + { + if (pedantic && (code1 != VOID_TYPE || code2 != VOID_TYPE)) + warning ("ANSI C forbids conditional expr with only one void side"); + result_type = void_type_node; + } + else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE) + { + if (comp_target_types (type1, type2)) + result_type = commontype (type1, type2); + else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node) + result_type = qualify_type (type2, type1); + else if (integer_zerop (op2) && TREE_TYPE (type2) == void_type_node) + result_type = qualify_type (type1, type2); + else if (TYPE_MAIN_VARIANT (TREE_TYPE (type1)) == void_type_node) + { + if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE) + warning ("ANSI C forbids conditional expr between `void *' and function pointer"); + result_type = qualify_type (type1, type2); + } + else if (TYPE_MAIN_VARIANT (TREE_TYPE (type2)) == void_type_node) + { + if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE) + warning ("ANSI C forbids conditional expr between `void *' and function pointer"); + result_type = qualify_type (type2, type1); + } + else + { + warning ("pointer type mismatch in conditional expression"); + result_type = build_pointer_type (void_type_node); + } + } + else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE) + { + if (!integer_zerop (op2)) + warning ("pointer/integer type mismatch in conditional expression"); + else + { + op2 = null_pointer_node; +#if 0 /* The spec seems to say this is permitted. */ + if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE) + warning ("ANSI C forbids conditional expr between 0 and function pointer"); +#endif + } + result_type = type1; + } + else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE) + { + if (!integer_zerop (op1)) + warning ("pointer/integer type mismatch in conditional expression"); + else + { + op1 = null_pointer_node; +#if 0 /* The spec seems to say this is permitted. */ + if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE) + warning ("ANSI C forbids conditional expr between 0 and function pointer"); +#endif + } + result_type = type2; + } + + if (!result_type) + { + if (flag_cond_mismatch) + result_type = void_type_node; + else + { + error ("type mismatch in conditional expression"); + return error_mark_node; + } + } + + if (result_type != TREE_TYPE (op1)) + op1 = convert (result_type, op1); + if (result_type != TREE_TYPE (op2)) + op2 = convert (result_type, op2); + +#if 0 + if (code1 == RECORD_TYPE || code1 == UNION_TYPE) + { + result_type = TREE_TYPE (op1); + if (TREE_LITERAL (ifexp)) + return (integer_zerop (ifexp) ? op2 : op1); + + if (TYPE_MODE (result_type) == BLKmode) + { + register tree tempvar + = build_decl (VAR_DECL, NULL_TREE, result_type); + register tree xop1 = build_modify_expr (tempvar, op1); + register tree xop2 = build_modify_expr (tempvar, op2); + register tree result = build (COND_EXPR, result_type, + ifexp, xop1, xop2); + + layout_decl (tempvar); + /* No way to handle variable-sized objects here. + I fear that the entire handling of BLKmode conditional exprs + needs to be redone. */ + if (! TREE_LITERAL (DECL_SIZE (tempvar))) + abort (); + DECL_RTL (tempvar) + = assign_stack_local (DECL_MODE (tempvar), + (TREE_INT_CST_LOW (DECL_SIZE (tempvar)) + * DECL_SIZE_UNIT (tempvar) + + BITS_PER_UNIT - 1) + / BITS_PER_UNIT); + + TREE_VOLATILE (result) + = TREE_VOLATILE (ifexp) | TREE_VOLATILE (op1) + | TREE_VOLATILE (op2); + return build (COMPOUND_EXPR, result_type, result, tempvar); + } + } +#endif /* 0 */ + + if (TREE_CODE (ifexp) == INTEGER_CST) + return (integer_zerop (ifexp) ? op2 : op1); + + return build (COND_EXPR, result_type, ifexp, op1, op2); +} + +/* Given a list of expressions, return a compound expression + that performs them all and returns the value of the last of them. */ + +tree +build_compound_expr (list) + tree list; +{ + register tree rest; + + if (TREE_CHAIN (list) == 0) + { + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since LIST is used in non-lvalue context. */ + if (TREE_CODE (list) == NOP_EXPR + && TREE_TYPE (list) == TREE_TYPE (TREE_OPERAND (list, 0))) + list = TREE_OPERAND (list, 0); + + return TREE_VALUE (list); + } + + rest = build_compound_expr (TREE_CHAIN (list)); + + /* This is patched out so that sizeof (0, array) is distinguishable from + sizeof array. */ +#if 0 + if (! TREE_VOLATILE (TREE_VALUE (list))) + return rest; +#endif + + return build (COMPOUND_EXPR, TREE_TYPE (rest), TREE_VALUE (list), rest); +} + +/* Build an expression representing a cast to type TYPE of expression EXPR. */ + +tree +build_c_cast (type, expr) + register tree type; + tree expr; +{ + register tree value = expr; + + if (type == error_mark_node || expr == error_mark_node) + return error_mark_node; + type = TYPE_MAIN_VARIANT (type); + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since VALUE is being used in non-lvalue context. */ + if (TREE_CODE (value) == NOP_EXPR + && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0))) + value = TREE_OPERAND (value, 0); + + if (TREE_CODE (type) == ARRAY_TYPE) + { + error ("cast specifies array type"); + return error_mark_node; + } + + if (type == TREE_TYPE (value)) + { + if (pedantic) + { + if (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE) + warning ("ANSI C forbids casting nonscalar to the same type"); + } + } + else + { + tree otype; + /* Convert functions and arrays to pointers, + but don't convert any other types. */ + if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE) + value = default_conversion (value); + otype = TREE_TYPE (value); + + /* Optionally warn about potentially worrysome casts. */ + + if (warn_cast_qual + && TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (otype) == POINTER_TYPE) + { + if (TREE_VOLATILE (TREE_TYPE (otype)) + && ! TREE_VOLATILE (TREE_TYPE (type))) + warning ("cast discards `volatile' from pointer target type"); + if (TREE_READONLY (TREE_TYPE (otype)) + && ! TREE_READONLY (TREE_TYPE (type))) + warning ("cast discards `const' from pointer target type"); + } + + value = convert (type, value); + } + + if (value == expr) + { + /* Always produce some operator for an explicit cast, + so we can tell (for -pedantic) that the cast is no lvalue. */ + tree nvalue = build (NOP_EXPR, type, value); + TREE_LITERAL (nvalue) = TREE_LITERAL (value); + return nvalue; + } + return value; +} + +/* Build an assignment expression of lvalue LHS from value RHS. + MODIFYCODE is the code for a binary operator that we use + to combine the old value of LHS with RHS to get the new value. + Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment. */ + +tree +build_modify_expr (lhs, modifycode, rhs) + tree lhs, rhs; + enum tree_code modifycode; +{ + register tree result; + tree newrhs; + tree lhstype = TREE_TYPE (lhs); + tree olhstype = lhstype; + + /* Types that aren't fully specified cannot be used in assignments. */ + lhs = require_complete_type (lhs); + + /* Avoid duplicate error messages from operands that had errors. */ + if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK) + return error_mark_node; + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since RHS is being used in non-lvalue context. */ + if (TREE_CODE (rhs) == NOP_EXPR + && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))) + rhs = TREE_OPERAND (rhs, 0); + + newrhs = rhs; + + /* Handle control structure constructs used as "lvalues". */ + + if (!pedantic) + switch (TREE_CODE (lhs)) + { + /* Handle (a, b) used as an "lvalue". */ + case COMPOUND_EXPR: + return build (COMPOUND_EXPR, lhstype, + TREE_OPERAND (lhs, 0), + build_modify_expr (TREE_OPERAND (lhs, 1), + modifycode, rhs)); + + /* Handle (a ? b : c) used as an "lvalue". */ + case COND_EXPR: + rhs = save_expr (rhs); + { + /* Produce (a ? (b = rhs) : (c = rhs)) + except that the RHS goes through a save-expr + so the code to compute it is only emitted once. */ + tree cond + = build_conditional_expr + (TREE_OPERAND (lhs, 0), + build_modify_expr (TREE_OPERAND (lhs, 1), + modifycode, rhs), + build_modify_expr (TREE_OPERAND (lhs, 2), + modifycode, rhs)); + /* Make sure the code to compute the rhs comes out + before the split. */ + return build (COMPOUND_EXPR, TREE_TYPE (lhs), + /* Cast to void to suppress warning + from warn_if_unused_value. */ + convert (void_type_node, rhs), + cond); + } + } + + /* If a binary op has been requested, combine the old LHS value with the RHS + producing the value we should actually store into the LHS. */ + + if (modifycode != NOP_EXPR) + { + lhs = stabilize_reference (lhs); + newrhs = build_binary_op (modifycode, lhs, rhs); + } + + /* Handle a cast used as an "lvalue". + We have already performed any binary operator using the value as cast. + Now convert the result to the true type of the lhs and store there; + then cast the result back to the specified type to be the value + of the assignment. */ + + if (!pedantic) + switch (TREE_CODE (lhs)) + { + case NOP_EXPR: + case CONVERT_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + case FIX_CEIL_EXPR: + if (TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (newrhs)) == FUNCTION_TYPE) + newrhs = default_conversion (newrhs); + { + tree inner_lhs = TREE_OPERAND (lhs, 0); + tree result = build_modify_expr (inner_lhs, NOP_EXPR, + convert (TREE_TYPE (inner_lhs), + newrhs)); + return convert (TREE_TYPE (lhs), result); + } + } + + /* Now we have handled acceptable kinds of LHS that are not truly lvalues. + Reject anything strange now. */ + + if (!lvalue_or_else (lhs, "assignment")) + return error_mark_node; + + /* Warn about storing in something that is `const'. */ + + if (TREE_READONLY (lhs) + || ((TREE_CODE (lhstype) == RECORD_TYPE + || TREE_CODE (lhstype) == UNION_TYPE) + && C_TYPE_FIELDS_READONLY (lhstype))) + readonly_warning (lhs, "assignment"); + + /* If storing into a structure or union member, + it has probably been given type `int'. + Compute the type that would go with + the actual amount of storage the member occupies. */ + + if (TREE_CODE (lhs) == COMPONENT_REF + && (TREE_CODE (lhstype) == INTEGER_TYPE + || TREE_CODE (lhstype) == REAL_TYPE + || TREE_CODE (lhstype) == ENUMERAL_TYPE)) + lhstype = TREE_TYPE (get_unwidened (lhs, 0)); + + /* If storing in a field that is in actuality a short or narrower than one, + we must store in the field in its actual type. */ + + if (lhstype != TREE_TYPE (lhs)) + { + lhs = copy_node (lhs); + TREE_TYPE (lhs) = lhstype; + } + + /* Convert new value to destination type. */ + + newrhs = convert_for_assignment (lhstype, newrhs, "assignment", 0); + if (TREE_CODE (newrhs) == ERROR_MARK) + return error_mark_node; + + result = build (MODIFY_EXPR, lhstype, lhs, newrhs); + TREE_VOLATILE (result) = 1; + + /* If we got the LHS in a different type for storing in, + convert the result back to the nominal type of LHS + so that the value we return always has the same type + as the LHS argument. */ + + if (olhstype == TREE_TYPE (result)) + return result; + return convert_for_assignment (olhstype, result, "assignment", 0); +} + +/* Return 0 if EXP is not a valid lvalue in this language + even though `lvalue_or_else' would accept it. */ + +int +language_lvalue_valid (exp) + tree exp; +{ + return 1; +} + +/* Convert value RHS to type TYPE as preparation for an assignment + to an lvalue of type TYPE. + The real work of conversion is done by `convert'. + The purpose of this function is to generate error messages + for assignments that are not allowed in C. + ERRTYPE is a string to use in error messages: + "assignment", "return", etc. + If NUM is positive, ERRTYPE is a printf format + that contains a %d to print NUM. */ + +static tree +convert_for_assignment (type, rhs, errtype, num) + tree type, rhs; + char *errtype; + int num; +{ + register enum tree_code codel = TREE_CODE (type); + register tree rhstype; + register enum tree_code coder; + + /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue. + Strip such NOP_EXPRs, since RHS is used in non-lvalue context. */ + if (TREE_CODE (rhs) == NOP_EXPR + && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))) + rhs = TREE_OPERAND (rhs, 0); + + if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE + || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE) + rhs = default_conversion (rhs); + + rhstype = TREE_TYPE (rhs); + coder = TREE_CODE (rhstype); + + if (coder == ERROR_MARK) + return error_mark_node; + + if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype)) + return rhs; + + if (coder == VOID_TYPE) + { + error ("void value not ignored as it ought to be"); + return error_mark_node; + } + /* Arithmetic types all interconvert, and enum is treated like int. */ + if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == ENUMERAL_TYPE) + && + (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == ENUMERAL_TYPE)) + { + return convert (type, rhs); + } + /* Conversions among pointers */ + else if (codel == POINTER_TYPE && coder == POINTER_TYPE) + { + register tree ttl = TREE_TYPE (type); + register tree ttr = TREE_TYPE (rhstype); + /* Any non-function converts to a [const][volatile] void * + and vice versa; otherwise, targets must be the same. + Meanwhile, the lhs target must have all the qualifiers of the rhs. */ + if (TYPE_MAIN_VARIANT (ttl) == void_type_node + || TYPE_MAIN_VARIANT (ttr) == void_type_node + || comp_target_types (type, rhstype)) + { + if (pedantic + && ((TYPE_MAIN_VARIANT (ttl) == void_type_node + && TREE_CODE (ttr) == FUNCTION_TYPE) + || + (TYPE_MAIN_VARIANT (ttr) == void_type_node + && TREE_CODE (ttl) == FUNCTION_TYPE))) + warning_with_arg ("%s between incompatible pointer types", errtype, num); + else + { + if (! TREE_READONLY (ttl) && TREE_READONLY (ttr)) + warning_with_arg ("%s of non-const * pointer from const *", errtype, num); + if (! TREE_VOLATILE (ttl) && TREE_VOLATILE (ttr)) + warning_with_arg ("%s of non-volatile * pointer from volatile *", errtype, num); + } + } + else + warning_with_arg ("%s between incompatible pointer types", errtype, num); + return convert (type, rhs); + } + else if (codel == POINTER_TYPE && coder == INTEGER_TYPE) + { + if (! integer_zerop (rhs)) + { + warning_with_arg ("%s of pointer from integer lacks a cast", errtype, num); + return convert (type, rhs); + } + return null_pointer_node; + } + else if (codel == INTEGER_TYPE && coder == POINTER_TYPE) + { + warning_with_arg ("%s of integer from pointer lacks a cast", errtype, num); + return convert (type, rhs); + } + + error_with_arg ("incompatible types in %s", errtype, num); + return error_mark_node; +} + +/* Return nonzero if VALUE is a valid constant-valued expression + for use in initializing a static variable; one that can be an + element of a "constant" initializer. + + Return 1 if the value is absolute; return 2 if it is relocatable. + We assume that VALUE has been folded as much as possible; + therefore, we do not need to check for such things as + arithmetic-combinations of integers. */ + +static int +initializer_constant_valid_p (value) + tree value; +{ + switch (TREE_CODE (value)) + { + case CONSTRUCTOR: + return TREE_STATIC (value); + + case INTEGER_CST: + case REAL_CST: + case STRING_CST: + return 1; + + case ADDR_EXPR: + return 2; + + case CONVERT_EXPR: + case NOP_EXPR: + /* Allow conversions between types of the same kind. */ + if (TREE_CODE (TREE_TYPE (value)) + == TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0)))) + return initializer_constant_valid_p (TREE_OPERAND (value, 0)); + /* Allow (int) &foo. */ + if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE + && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE) + return initializer_constant_valid_p (TREE_OPERAND (value, 0)); + return 0; + + case PLUS_EXPR: + { + int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0)); + int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1)); + if (valid0 == 1 && valid1 == 2) + return 2; + if (valid0 == 2 && valid1 == 1) + return 2; + return 0; + } + + case MINUS_EXPR: + { + int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0)); + int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1)); + if (valid0 == 2 && valid1 == 1) + return 2; + return 0; + } + } + + return 0; +} + +/* Perform appropriate conversions on the initial value of a variable, + store it in the declaration DECL, + and print any error messages that are appropriate. + If the init is invalid, store an ERROR_MARK. */ + +void +store_init_value (decl, init) + tree decl, init; +{ + register tree value, type; + + /* If variable's type was invalidly declared, just ignore it. */ + + type = TREE_TYPE (decl); + if (TREE_CODE (type) == ERROR_MARK) + return; + + /* Digest the specified initializer into an expression. */ + + value = digest_init (type, init, 0); + + /* Store the expression if valid; else report error. */ + + if (value == error_mark_node) + ; + else if (TREE_STATIC (decl) && ! TREE_LITERAL (value)) + { + error ("initializer for static variable is not constant"); + value = error_mark_node; + } + else if (TREE_STATIC (decl) + && ! initializer_constant_valid_p (value)) + { + error ("initializer for static variable uses complicated arithmetic"); + value = error_mark_node; + } + else + { + if (pedantic && TREE_CODE (value) == CONSTRUCTOR) + { + if (! TREE_LITERAL (value)) + warning ("aggregate initializer is not constant"); + else if (! TREE_STATIC (value)) + warning ("aggregate initializer uses complicated arithmetic"); + } + } + DECL_INITIAL (decl) = value; +} + +/* Digest the parser output INIT as an initializer for type TYPE. + Return a C expression of type TYPE to represent the initial value. + + If TAIL is nonzero, it points to a variable holding a list of elements + of which INIT is the first. We update the list stored there by + removing from the head all the elements that we use. + Normally this is only one; we use more than one element only if + TYPE is an aggregate and INIT is not a constructor. */ + +tree +digest_init (type, init, tail) + tree type, init, *tail; +{ + enum tree_code code = TREE_CODE (type); + tree element = 0; + tree old_tail_contents; + /* Nonzero if INIT is a braced grouping, which comes in as a CONSTRUCTOR + tree node which has no TREE_TYPE. */ + int raw_constructor + = TREE_CODE (init) == CONSTRUCTOR && TREE_TYPE (init) == 0; + + /* By default, assume we use one element from a list. + We correct this later in the sole case where it is not true. */ + + if (tail) + { + old_tail_contents = *tail; + *tail = TREE_CHAIN (*tail); + } + + if (init == error_mark_node) + return init; + + if (init && raw_constructor + && CONSTRUCTOR_ELTS (init) != 0 + && TREE_CHAIN (CONSTRUCTOR_ELTS (init)) == 0) + element = TREE_VALUE (CONSTRUCTOR_ELTS (init)); + + /* Any type can be initialized from an expression of the same type, + optionally with braces. */ + + if (init && (TREE_TYPE (init) == type + || (code == ARRAY_TYPE && TREE_TYPE (init) + && comptypes (TREE_TYPE (init), type)))) + { + if (pedantic && code == ARRAY_TYPE + && TREE_CODE (init) != STRING_CST) + warning ("ANSI C forbids initializing array from array expression"); + if (optimize && TREE_READONLY (init) && TREE_CODE (init) == VAR_DECL) + return decl_constant_value (init); + return init; + } + + if (element && (TREE_TYPE (element) == type + || (code == ARRAY_TYPE && TREE_TYPE (element) + && comptypes (TREE_TYPE (element), type)))) + { + if (pedantic && code == ARRAY_TYPE) + warning ("ANSI C forbids initializing array from array expression"); + if (pedantic && (code == RECORD_TYPE || code == UNION_TYPE)) + warning ("single-expression nonscalar initializer has braces"); + if (optimize && TREE_READONLY (element) && TREE_CODE (element) == VAR_DECL) + return decl_constant_value (element); + return element; + } + + /* Check for initializing a union by its first field. + Such an initializer must use braces. */ + + if (code == UNION_TYPE) + { + tree result; + + if (TYPE_FIELDS (type) == 0) + { + error ("union with no members cannot be initialized"); + return error_mark_node; + } + + if (raw_constructor) + return process_init_constructor (type, init, 0); + else if (tail != 0) + { + *tail = old_tail_contents; + return process_init_constructor (type, 0, tail); + } + } + + /* Initialization of an array of chars from a string constant + optionally enclosed in braces. */ + + if (code == ARRAY_TYPE) + { + tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type)); + if ((typ1 == char_type_node + || typ1 == signed_char_type_node + || typ1 == unsigned_char_type_node + || typ1 == unsigned_type_node + || typ1 == integer_type_node) + && ((init && TREE_CODE (init) == STRING_CST) + || (element && TREE_CODE (element) == STRING_CST))) + { + tree string = element ? element : init; + + if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string))) + != char_type_node) + && TYPE_PRECISION (typ1) == TYPE_PRECISION (char_type_node)) + { + error ("char-array initialized from wide string"); + return error_mark_node; + } + if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string))) + == char_type_node) + && TYPE_PRECISION (typ1) == TYPE_PRECISION (integer_type_node)) + { + error ("int-array initialized from non-wide string"); + return error_mark_node; + } + + TREE_TYPE (string) = type; + if (TYPE_DOMAIN (type) != 0 + && TREE_LITERAL (TYPE_SIZE (type))) + { + register int size + = TREE_INT_CST_LOW (TYPE_SIZE (type)) * TYPE_SIZE_UNIT (type); + size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT; + /* Subtract 1 because it's ok to ignore the terminating null char + that is counted in the length of the constant. */ + if (size < TREE_STRING_LENGTH (string) - 1) + warning ("initializer-string for array of chars is too long"); + } + return string; + } + } + + /* Handle scalar types, including conversions. */ + + if (code == INTEGER_TYPE || code == REAL_TYPE || code == POINTER_TYPE + || code == ENUMERAL_TYPE) + { + if (raw_constructor) + { + if (element == 0) + { + error ("initializer for scalar variable requires one element"); + return error_mark_node; + } + init = element; + } + + if (TREE_CODE (init) == CONSTRUCTOR) + { + error ("initializer for scalar has extra braces"); + return error_mark_node; + } + + return convert_for_assignment (type, default_conversion (init), + "initialization", 0); + } + + /* Come here only for records and arrays. */ + + if (TYPE_SIZE (type) && ! TREE_LITERAL (TYPE_SIZE (type))) + { + error ("variable-sized object may not be initialized"); + return error_mark_node; + } + + if (code == ARRAY_TYPE || code == RECORD_TYPE) + { + if (raw_constructor) + return process_init_constructor (type, init, 0); + else if (tail != 0) + { + *tail = old_tail_contents; + return process_init_constructor (type, 0, tail); + } + else if (flag_traditional) + /* Traditionally one can say `char x[100] = 0;'. */ + return process_init_constructor (type, + build_nt (CONSTRUCTOR, 0, + tree_cons (0, init, 0)), + 0); + } + + error ("invalid initializer"); + return error_mark_node; +} + +/* Process a constructor for a variable of type TYPE. + The constructor elements may be specified either with INIT or with ELTS, + only one of which should be non-null. + + If INIT is specified, it is a CONSTRUCTOR node which is specifically + and solely for initializing this datum. + + If ELTS is specified, it is the address of a variable containing + a list of expressions. We take as many elements as we need + from the head of the list and update the list. + + In the resulting constructor, TREE_LITERAL is set if all elts are + constant, and TREE_STATIC is set if, in addition, all elts are simple enough + constants that the assembler and linker can compute them. */ + +static tree +process_init_constructor (type, init, elts) + tree type, init, *elts; +{ + register tree tail; + /* List of the elements of the result constructor, + in reverse order. */ + register tree members = NULL; + tree result; + int allconstant = 1; + int allsimple = 1; + int error_flag = 0; + + /* Make TAIL be the list of elements to use for the initialization, + no matter how the data was given to us. */ + + if (elts) + tail = *elts; + else + tail = CONSTRUCTOR_ELTS (init); + + /* Gobble as many elements as needed, and make a constructor or initial value + for each element of this aggregate. Chain them together in result. + If there are too few, use 0 for each scalar ultimate component. */ + + if (TREE_CODE (type) == ARRAY_TYPE) + { + tree domain = TYPE_DOMAIN (type); + register long len; + register int i; + + if (domain) + len = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain)) + - TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain)) + + 1; + else + len = -1; /* Take as many as there are */ + + for (i = 0; (len < 0 || i < len) && tail != 0; i++) + { + register tree next1; + + if (TREE_VALUE (tail) != 0) + { + tree tail1 = tail; + next1 = digest_init (TYPE_MAIN_VARIANT (TREE_TYPE (type)), + TREE_VALUE (tail), &tail1); + if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) + abort (); + if (tail == tail1 && len < 0) + { + error ("non-empty initializer for array of empty elements"); + /* Just ignore what we were supposed to use. */ + tail1 = 0; + } + tail = tail1; + } + else + { + next1 = error_mark_node; + tail = TREE_CHAIN (tail); + } + + if (next1 == error_mark_node) + error_flag = 1; + else if (!TREE_LITERAL (next1)) + allconstant = 0; + else if (! initializer_constant_valid_p (next1)) + allsimple = 0; + members = tree_cons (NULL_TREE, next1, members); + } + } + if (TREE_CODE (type) == RECORD_TYPE) + { + register tree field; + + for (field = TYPE_FIELDS (type); field && tail; + field = TREE_CHAIN (field)) + { + register tree next1; + + if (! DECL_NAME (field)) + { + members = tree_cons (field, integer_zero_node, members); + continue; + } + + if (TREE_VALUE (tail) != 0) + { + tree tail1 = tail; + next1 = digest_init (TREE_TYPE (field), + TREE_VALUE (tail), &tail1); + if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) + abort (); + tail = tail1; + } + else + { + next1 = error_mark_node; + tail = TREE_CHAIN (tail); + } + + if (next1 == error_mark_node) + error_flag = 1; + else if (!TREE_LITERAL (next1)) + allconstant = 0; + else if (! initializer_constant_valid_p (next1)) + allsimple = 0; + members = tree_cons (field, next1, members); + } + } + + if (TREE_CODE (type) == UNION_TYPE) + { + register tree field = TYPE_FIELDS (type); + register tree next1; + + /* For a union, get the initializer for 1 fld. */ + + if (TREE_VALUE (tail) != 0) + { + tree tail1 = tail; + next1 = digest_init (TREE_TYPE (field), + TREE_VALUE (tail), &tail1); + if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST) + abort (); + tail = tail1; + } + else + { + next1 = error_mark_node; + tail = TREE_CHAIN (tail); + } + + if (next1 == error_mark_node) + error_flag = 1; + else if (!TREE_LITERAL (next1)) + allconstant = 0; + else if (! initializer_constant_valid_p (next1)) + allsimple = 0; + members = tree_cons (field, next1, members); + } + + /* If arguments were specified as a list, just remove the ones we used. */ + if (elts) + *elts = tail; + /* If arguments were specified as a constructor, + complain unless we used all the elements of the constructor. */ + else if (tail) + warning ("excess elements in aggregate initializer"); + + if (error_flag) + return error_mark_node; + + result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (members)); + if (allconstant) TREE_LITERAL (result) = 1; + if (allconstant && allsimple) TREE_STATIC (result) = 1; + return result; +} + +/* Expand an ASM statement with operands, handling output operands + that are not variables or INDIRECT_REFS by transforming such + cases into cases that expand_asm_operands can handle. + + Arguments are same as for expand_asm_operands. */ + +void +c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line) + tree string, outputs, inputs, clobbers; + int vol; + char *filename; + int line; +{ + int noutputs = list_length (outputs); + register int i; + /* o[I] is the place that output number I should be written. */ + register tree *o = (tree *) alloca (noutputs * sizeof (tree)); + register tree tail; + + /* Record the contents of OUTPUTS before it is modifed. */ + for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) + o[i] = TREE_VALUE (tail); + +#if 0 /* Don't do this--it screws up operands expected to be in memory. */ + /* Perform default conversions on all inputs. */ + for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), i++) + TREE_VALUE (tail) = default_conversion (TREE_VALUE (tail)); +#endif + + /* Generate the ASM_OPERANDS insn; + store into the TREE_VALUEs of OUTPUTS some trees for + where the values were actually stored. */ + expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line); + + /* Copy all the intermediate outputs into the specified outputs. */ + for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++) + { + if (o[i] != TREE_VALUE (tail)) + expand_expr (build_modify_expr (o[i], NOP_EXPR, TREE_VALUE (tail)), + 0, VOIDmode, 0); + /* Detect modification of read-only values. + (Otherwise done by build_modify_expr.) */ + else + { + tree type = TREE_TYPE (o[i]); + if (TREE_READONLY (o[i]) + || ((TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE) + && C_TYPE_FIELDS_READONLY (type))) + readonly_warning (o[i], "modification by `asm'"); + } + } + + /* Those MODIFY_EXPRs could do autoincrements. */ + emit_queue (); +} + +/* Expand a C `return' statement. + RETVAL is the expression for what to return, + or a null pointer for `return;' with no value. */ + +void +c_expand_return (retval) + tree retval; +{ + tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)); + + if (TREE_THIS_VOLATILE (current_function_decl)) + warning ("function declared `volatile' has a `return' statement"); + + if (!retval) + { + current_function_returns_null = 1; + if (warn_return_type && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE) + warning ("`return' with no value, in function returning non-void"); + expand_null_return (); + } + else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE) + { + current_function_returns_null = 1; + if (pedantic || TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE) + warning ("`return' with a value, in function returning void"); + expand_return (retval); + } + else + { + tree t = convert_for_assignment (valtype, retval, "return", 0); + tree res = DECL_RESULT (current_function_decl); + t = build (MODIFY_EXPR, TREE_TYPE (res), + res, convert (TREE_TYPE (res), t)); + expand_return (t); + current_function_returns_value = 1; + } +} + +/* Start a C switch statement, testing expression EXP. + Return EXP if it is valid, an error node otherwise. */ + +tree +c_expand_start_case (exp) + tree exp; +{ + register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); + tree type = TREE_TYPE (exp); + + if (code != INTEGER_TYPE && code != ENUMERAL_TYPE && code != ERROR_MARK) + { + error ("switch quantity not an integer"); + exp = error_mark_node; + } + else + { + tree index; + + exp = default_conversion (exp); + type = TREE_TYPE (exp); + index = get_unwidened (exp, 0); + /* We can't strip a conversion from a signed type to an unsigned, + because if we did, int_fits_type_p would do the wrong thing + when checking case values for being in range, + and it's too hard to do the right thing. */ + if (TREE_UNSIGNED (TREE_TYPE (exp)) + == TREE_UNSIGNED (TREE_TYPE (index))) + exp = index; + } + + expand_start_case (1, exp, type); + + return exp; +} diff --git a/usr/src/usr.bin/gcc/cc1/toplev.c b/usr/src/usr.bin/gcc/cc1/toplev.c new file mode 100644 index 0000000000..c75dc214a9 --- /dev/null +++ b/usr/src/usr.bin/gcc/cc1/toplev.c @@ -0,0 +1,2123 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)toplev.c 6.4 (Berkeley) 5/8/91"; +#endif /* not lint */ + +/* Top level of GNU C compiler + Copyright (C) 1987, 1988, 1989 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* This is the top level of cc1. + It parses command args, opens files, invokes the various passes + in the proper order, and counts the time used by each. + Error messages and low-level interface to malloc also handled here. */ + +#include "config.h" +#include +#include +#include +#include + +#include + +#ifdef USG +#undef FLOAT +#include +/* This is for hpux. It is a real screw. They should change hpux. */ +#undef FLOAT +#include +#include /* Correct for hpux at least. Is it good on other USG? */ +#undef FFS /* Some systems define this in param.h. */ +#else +#ifndef VMS +#include +#include +#endif +#endif + +#include "input.h" +#include "tree.h" +#include "c-tree.h" +#include "rtl.h" +#include "flags.h" + +extern int yydebug; + +extern FILE *finput; + +extern int reload_completed; +extern int rtx_equal_function_value_matters; + +extern void init_lex (); +extern void init_decl_processing (); +extern void init_tree (); +extern void init_rtl (); +extern void init_optabs (); +extern void init_reg_sets (); +extern void dump_flow_info (); +extern void dump_local_alloc (); + +void rest_of_decl_compilation (); +void error (); +void error_with_file_and_line (); +void fancy_abort (); +void set_target_switch (); +void print_target_switch_defaults (); + +/* Bit flags that specify the machine subtype we are compiling for. + Bits are tested using macros TARGET_... defined in the tm-...h file + and set by `-m...' switches. */ + +int target_flags; + +/* Name of current original source file (what was input to cpp). + This comes from each #-command in the actual input. */ + +char *input_filename; + +/* Name of top-level original source file (what was input to cpp). + This comes from the #-command at the beginning of the actual input. + If there isn't any there, then this is the cc1 input file name. */ + +char *main_input_filename; + +/* Current line number in real source file. */ + +int lineno; + +/* Stack of currently pending input files. */ + +struct file_stack *input_file_stack; + +/* Incremented on each change to input_file_stack. */ +int input_file_stack_tick; + +/* FUNCTION_DECL for function now being parsed or compiled. */ + +extern tree current_function_decl; + +/* Name to use as base of names for dump output files. */ + +char *dump_base_name; + +/* Flags saying which kinds of debugging dump have been requested. */ + +int rtl_dump = 0; +int rtl_dump_and_exit = 0; +int jump_opt_dump = 0; +int cse_dump = 0; +int loop_dump = 0; +int flow_dump = 0; +int combine_dump = 0; +int local_reg_dump = 0; +int global_reg_dump = 0; +int jump2_opt_dump = 0; +int dbr_sched_dump = 0; + +/* 1 => write gdb debugging output (using symout.c). -g + 2 => write dbx debugging output (using dbxout.c). -G + 3 => write sdb debugging output (using sdbout.c). -g. */ + +enum debugger write_symbols = NO_DEBUG; + +/* Nonzero means can use our own extensions to DBX format. + Relevant only with write_symbols == DBX_DEBUG. */ + +int use_gdb_dbx_extensions; + +/* Nonzero means do optimizations. -opt. */ + +int optimize = 0; + +/* Nonzero means `char' should be signed. */ + +int flag_signed_char; + +/* Nonzero means give an enum type only as many bytes as it needs. */ + +int flag_short_enums; + +/* Nonzero for -fcaller-saves: allocate values in regs that need to + be saved across function calls, if that produces overall better code. + Optional now, so people can test it. */ + +#ifdef DEFAULT_CALLER_SAVES +int flag_caller_saves = 1; +#else +int flag_caller_saves = 0; +#endif + +/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */ + +int flag_pcc_struct_return = 0; + +/* Nonzero for -fforce-mem: load memory value into a register + before arithmetic on it. This makes better cse but slower compilation. */ + +int flag_force_mem = 0; + +/* Nonzero for -fforce-addr: load memory address into a register before + reference to memory. This makes better cse but slower compilation. */ + +int flag_force_addr = 0; + +/* Nonzero for -fdefer-pop: don't pop args after each function call; + instead save them up to pop many calls' args with one insns. */ + +int flag_defer_pop = 1; + +/* Nonzero for -ffloat-store: don't allocate floats and doubles + in extended-precision registers. */ + +int flag_float_store = 0; + +/* Nonzero for -fcombine-regs: + allow instruction combiner to combine an insn + that just copies one reg to another. */ + +int flag_combine_regs = 0; + +/* Nonzero enables strength-reduction in loop.c. */ + +int flag_strength_reduce = 0; + +/* Nonzero for -fwritable-strings: + store string constants in data segment and don't uniquize them. */ + +int flag_writable_strings = 0; + +/* Nonzero means don't put addresses of constant functions in registers. + Used for compiling the Unix kernel, where strange substitutions are + done on the assembly output. */ + +int flag_no_function_cse = 0; + +/* Nonzero for -fomit-frame-pointer: + don't make a frame pointer in simple functions that don't require one. */ + +int flag_omit_frame_pointer = 0; + +/* Nonzero to inhibit use of define_optimization peephole opts. */ + +int flag_no_peephole = 0; + +/* Nonzero means all references through pointers are volatile. */ + +int flag_volatile; + +/* Nonzero means just do syntax checking; don't output anything. */ + +int flag_syntax_only = 0; + +/* Nonzero means do stupid register allocation. -noreg. + This and `optimize' are controlled by different switches in cc1, + but normally cc controls them both with the -O switch. */ + +int obey_regdecls = 0; + +/* Don't print functions as they are compiled and don't print + times taken by the various passes. -quiet. */ + +int quiet_flag = 0; + +/* Don't print warning messages. -w. */ + +int inhibit_warnings = 0; + +/* Do print extra warnings (such as for uninitialized variables). -W. */ + +int extra_warnings = 0; + +/* Nonzero to warn about unused local variables. */ + +int warn_unused; + +/* Nonzero means warn about all declarations which shadow others. */ + +int warn_shadow; + +/* Warn if a switch on an enum fails to have a case for every enum value. */ + +int warn_switch; + +/* Nonzero means warn about any identifiers that match in the first N + characters. The value N is in `id_clash_len'. */ + +int warn_id_clash; +int id_clash_len; + +/* Number of error messages and warning messages so far. */ + +int errorcount = 0; +int warningcount = 0; +int sorrycount = 0; + +/* Name of program invoked, sans directories. */ + +char *progname; + +/* Nonzero if generating code to do profiling. */ + +int profile_flag = 0; + +/* Nonzero if generating code to do profiling on a line-by-line basis. */ + +int profile_block_flag; + +/* Nonzero for -pedantic switch: warn about anything + that standard spec forbids. */ + +int pedantic = 0; + +/* Nonzero for -finline-functions: ok to inline functions that look like + good inline candidates. */ + +int flag_inline_functions; + +/* Nonzero for -fkeep-inline-functions: even if we make a function + go inline everywhere, keep its defintion around for debugging + purposes. */ + +int flag_keep_inline_functions; + +/* Nonzero means make the text shared if supported. */ + +int flag_shared_data; + +/* Nonzero means schedule into delayed branch slots if supported. */ + +int flag_delayed_branch; + +/* Copy of arguments to main. */ +int save_argc; +char **save_argv; + +/* Name for output file of assembly code, specified with -o. */ + +char *asm_file_name; + +/* Name for output file of GDB symbol segment, specified with -symout. */ + +char *sym_file_name; + +/* Table of language-independent -f options. + STRING is the option name. VARIABLE is the address of the variable. + ON_VALUE is the value to store in VARIABLE + if `-fSTRING' is seen as an option. + (If `-fno-STRING' is seen as an option, the opposite value is stored.) */ + +struct { char *string; int *variable; int on_value;} f_options[] = +{ + {"float-store", &flag_float_store, 1}, + {"volatile", &flag_volatile, 1}, + {"defer-pop", &flag_defer_pop, 1}, + {"omit-frame-pointer", &flag_omit_frame_pointer, 1}, + {"strength-reduce", &flag_strength_reduce, 1}, + {"writable-strings", &flag_writable_strings, 1}, + {"peephole", &flag_no_peephole, 0}, + {"force-mem", &flag_force_mem, 1}, + {"force-addr", &flag_force_addr, 1}, + {"combine-regs", &flag_combine_regs, 1}, + {"function-cse", &flag_no_function_cse, 0}, + {"inline-functions", &flag_inline_functions, 1}, + {"keep-inline-functions", &flag_keep_inline_functions, 1}, + {"syntax-only", &flag_syntax_only, 1}, + {"shared-data", &flag_shared_data, 1}, + {"caller-saves", &flag_caller_saves, 1}, + {"pcc-struct-return", &flag_pcc_struct_return, 1}, + {"delayed-branch", &flag_delayed_branch, 1} +}; + +/* Output files for assembler code (real compiler output) + and debugging dumps. */ + +FILE *asm_out_file; +FILE *rtl_dump_file; +FILE *jump_opt_dump_file; +FILE *cse_dump_file; +FILE *loop_dump_file; +FILE *flow_dump_file; +FILE *combine_dump_file; +FILE *local_reg_dump_file; +FILE *global_reg_dump_file; +FILE *jump2_opt_dump_file; +FILE *dbr_sched_dump_file; + +/* Time accumulators, to count the total time spent in various passes. */ + +int parse_time; +int varconst_time; +int integration_time; +int jump_time; +int cse_time; +int loop_time; +int flow_time; +int combine_time; +int local_alloc_time; +int global_alloc_time; +int dbr_sched_time; +int final_time; +int symout_time; +int dump_time; + +/* Return time used so far, in microseconds. */ + +int +gettime () +{ +#ifdef USG + struct tms tms; +#else +#ifndef VMS + struct rusage rusage; +#else /* VMS */ + struct + { + int proc_user_time; + int proc_system_time; + int child_user_time; + int child_system_time; + } vms_times; +#endif +#endif + + if (quiet_flag) + return 0; + +#ifdef USG + times (&tms); + return (tms.tms_utime + tms.tms_stime) * (1000000 / HZ); +#else +#ifndef VMS + getrusage (0, &rusage); + return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec + + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec); +#else /* VMS */ + times (&vms_times); + return (vms_times.proc_user_time + vms_times.proc_system_time) * 10000; +#endif +#endif +} + +#define TIMEVAR(VAR, BODY) \ +do { int otime = gettime (); BODY; VAR += gettime () - otime; } while (0) + +void +print_time (str, total) + char *str; + int total; +{ + fprintf (stderr, + "time in %s: %d.%06d\n", + str, total / 1000000, total % 1000000); +} + +/* Count an error or warning. Return 1 if the message should be printed. */ + +int +count_error (warningp) + int warningp; +{ + if (warningp && inhibit_warnings) + return 0; + + if (warningp) + warningcount++; + else + errorcount++; + + return 1; +} + +/* Print a fatal error message. NAME is the text. + Also include a system error message based on `errno'. */ + +void +pfatal_with_name (name) + char *name; +{ + fprintf (stderr, "%s: ", progname); + perror (name); + exit (35); +} + +void +fatal_io_error (name) + char *name; +{ + fprintf (stderr, "%s: %s: I/O error\n", progname, name); + exit (35); +} + +void +fatal (s, v) + char *s; + int v; +{ + error (s, v); + exit (34); +} + +/* Called from insn-extract to give a better error message when we + don't have an insn to match what we are looking for, rather + than just calling abort(). */ + +void +fatal_insn_not_found (insn) + rtx insn; +{ + error ("The following insn was not recognizable:", 0); + debug_rtx (insn); + abort (); +} + +static int need_error_newline; + +/* Function of last error message; + more generally, function such that if next error message is in it + then we don't have to mention the function name. */ +static tree last_error_function = NULL; + +/* Used to detect when input_file_stack has changed since last described. */ +static int last_error_tick; + +/* Called when the start of a function definition is parsed, + this function prints on stderr the name of the function. */ + +void +announce_function (decl) + tree decl; +{ + if (! quiet_flag) + { + fprintf (stderr, " %s", DECL_PRINT_NAME (decl)); + fflush (stderr); + need_error_newline = 1; + last_error_function = current_function_decl; + } +} + +/* Prints out, if necessary, the name of the current function + which caused an error. Called from all error and warning functions. */ + +void +report_error_function (file) + char *file; +{ + struct file_stack *p; + + if (need_error_newline) + { + fprintf (stderr, "\n"); + need_error_newline = 0; + } + + if (last_error_function != current_function_decl) + { + if (file) + fprintf (stderr, "%s: ", file); + + if (current_function_decl == NULL) + fprintf (stderr, "At top level:\n"); + else if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE) + fprintf (stderr, "In method %s:\n", + DECL_PRINT_NAME (current_function_decl)); + else + fprintf (stderr, "In function %s:\n", + DECL_PRINT_NAME (current_function_decl)); + + last_error_function = current_function_decl; + } + if (input_file_stack && input_file_stack->next != 0 + && input_file_stack_tick != last_error_tick) + { + fprintf (stderr, "In file included"); + for (p = input_file_stack->next; p; p = p->next) + { + fprintf (stderr, " from %s:%d", p->name, p->line); + if (p->next) + fprintf (stderr, ","); + } + fprintf (stderr, ":\n"); + last_error_tick = input_file_stack_tick; + } +} + +/* Report an error at the current line number. + S and V are a string and an arg for `printf'. */ + +void +error (s, v, v2) + char *s; + int v; /* @@also used as pointer */ + int v2; /* @@also used as pointer */ +{ + error_with_file_and_line (input_filename, lineno, s, v, v2); +} + +/* Report an error at line LINE of file FILE. + S and V are a string and an arg for `printf'. */ + +void +error_with_file_and_line (file, line, s, v, v2) + char *file; + int line; + char *s; + int v; + int v2; +{ + count_error (0); + + report_error_function (file); + + if (file) + fprintf (stderr, "%s:%d: ", file, line); + else + fprintf (stderr, "%s: ", progname); + fprintf (stderr, s, v, v2); + fprintf (stderr, "\n"); +} + +/* Report an error at the declaration DECL. + S and V are a string and an arg which uses %s to substitute the declaration name. */ + +void +error_with_decl (decl, s, v) + tree decl; + char *s; + int v; +{ + count_error (0); + + report_error_function (DECL_SOURCE_FILE (decl)); + + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + + if (DECL_PRINT_NAME (decl)) + fprintf (stderr, s, DECL_PRINT_NAME (decl), v); + else if (DECL_NAME (decl)) + fprintf (stderr, s, IDENTIFIER_POINTER (DECL_NAME (decl)), v); + else + fprintf (stderr, s, "((anonymous))", v); + fprintf (stderr, "\n"); +} + +/* Report an error at argument #NUM. + S is a string that uses %s to substitute the error type. + E is the string for the error type. */ +void +error_with_arg (s, e, num) + char *s, *e; + int num; +{ + char *w; + static char argument[] = "argument #%d: "; + + if (num <= 0) + { + error (s, e); + return; + } + + w = alloca (strlen (s) + sizeof (argument)); + (void) strcat (strcpy (w, argument), s); + error (w, num, e); +} + +/* Report an error at the line number of the insn INSN. + S and V are a string and an arg for `printf'. + This is used only when INSN is an `asm' with operands, + and each ASM_OPERANDS records its own source file and line. */ + +void +error_for_asm (insn, s, v, v2) + rtx insn; + char *s; + int v; /* @@also used as pointer */ + int v2; /* @@also used as pointer */ +{ + rtx temp; + char *filename; + int line; + rtx body = PATTERN (insn); + rtx asmop; + + /* Find the (or one of the) ASM_OPERANDS in the insn. */ + if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS) + asmop = SET_SRC (body); + else if (GET_CODE (body) == ASM_OPERANDS) + asmop = body; + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == SET) + asmop = SET_SRC (XVECEXP (body, 0, 0)); + else if (GET_CODE (body) == PARALLEL + && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS) + asmop = XVECEXP (body, 0, 0); + + filename = ASM_OPERANDS_SOURCE_FILE (asmop); + line = ASM_OPERANDS_SOURCE_LINE (asmop); + + error_with_file_and_line (filename, line, s, v, v2); +} + +/* Report a warning at line LINE. + S and V are a string and an arg for `printf'. */ + +void +warning_with_file_and_line (file, line, s, v, v2) + char *file; + int line; + char *s; + int v; + int v2; +{ + if (count_error (1) == 0) + return; + + report_error_function (file); + + if (file) + fprintf (stderr, "%s:%d: ", file, line); + else + fprintf (stderr, "%s: ", progname); + + fprintf (stderr, "warning: "); + fprintf (stderr, s, v, v2); + fprintf (stderr, "\n"); +} + +/* Report a warning at the current line number. + S and V are a string and an arg for `printf'. */ + +void +warning (s, v, v2) + char *s; + int v; /* @@also used as pointer */ + int v2; +{ + warning_with_file_and_line (input_filename, lineno, s, v, v2); +} + +/* Report a warning at the declaration DECL. + S is string which uses %s to substitute the declaration name. + V is a second parameter that S can refer to. */ + +void +warning_with_decl (decl, s, v) + tree decl; + char *s; + int v; +{ + if (count_error (1) == 0) + return; + + report_error_function (DECL_SOURCE_FILE (decl)); + + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + + fprintf (stderr, "warning: "); + if (DECL_PRINT_NAME (decl)) + fprintf (stderr, s, DECL_PRINT_NAME (decl), v); + else if (DECL_NAME (decl)) + fprintf (stderr, s, IDENTIFIER_POINTER (DECL_NAME (decl)), v); + else + fprintf (stderr, s, "((anonymous))", v); + fprintf (stderr, "\n"); +} + +/* Report a warning at argument #NUM. + S is a string that uses %s to substitute the error type. + E is the string for the error type. */ +void +warning_with_arg (s, e, num) + char *s, *e; + int num; +{ + char *w; + static char argument[] = "argument #%d: "; + + if (num <= 0) + { + warning (s, e); + return; + } + + w = alloca (strlen (s) + sizeof (argument)); + (void) strcat (strcpy (w, argument), s); + warning (w, num, e); +} + +/* Apologize for not implementing some feature. + S, V, and V2 are a string and args for `printf'. */ + +void +sorry (s, v, v2) + char *s; + int v, v2; +{ + sorrycount++; + if (input_filename) + fprintf (stderr, "%s:%d: ", input_filename, lineno); + else + fprintf (stderr, "%s: ", progname); + + fprintf (stderr, "sorry, not implemented: "); + fprintf (stderr, s, v, v2); + fprintf (stderr, "\n"); +} + +/* Apologize for not implementing some feature, then quit. + S, V, and V2 are a string and args for `printf'. */ + +void +really_sorry (s, v, v2) + char *s; + int v, v2; +{ + if (input_filename) + fprintf (stderr, "%s:%d: ", input_filename, lineno); + else + fprintf (stderr, "c++: "); + + fprintf (stderr, "sorry, not implemented: "); + fprintf (stderr, s, v, v2); + fatal (" (fatal)\n"); +} + +/* More 'friendly' abort that prints the line and file. + config.h can #define abort fancy_abort if you like that sort of thing. */ + +void +fancy_abort () +{ + fatal ("Internal gcc abort."); +} + +/* When `malloc.c' is compiled with `rcheck' defined, + it calls this function to report clobberage. */ + +void +botch (s) +{ + abort (); +} + +/* Same as `malloc' but report error if no memory available. */ + +int +xmalloc (size) + unsigned size; +{ + register int value = (int) malloc (size); + if (value == 0) + fatal ("Virtual memory exhausted."); + return value; +} + +/* Same as `realloc' but report error if no memory available. */ + +int +xrealloc (ptr, size) + char *ptr; + int size; +{ + int result = realloc (ptr, size); + if (!result) + fatal ("Virtual memory exhausted."); + return result; +} + +/* Return the logarithm of X, base 2, considering X unsigned, + if X is a power of 2. Otherwise, returns -1. */ + +int +exact_log2 (x) + register unsigned int x; +{ + register int log = 0; + for (log = 0; log < HOST_BITS_PER_INT; log++) + if (x == (1 << log)) + return log; + return -1; +} + +/* Given X, an unsigned number, return the largest int Y such that 2**Y <= X. + If X is 0, return -1. */ + +int +floor_log2 (x) + register unsigned int x; +{ + register int log = 0; + for (log = 0; log < HOST_BITS_PER_INT; log++) + if ((x & ((-1) << log)) == 0) + return log - 1; + return HOST_BITS_PER_INT - 1; +} + +int float_handled; +jmp_buf float_handler; + +/* Specify where to longjmp to when a floating arithmetic error happens. + If HANDLER is 0, it means don't handle the errors any more. */ + +void +set_float_handler (handler) + jmp_buf handler; +{ + float_handled = (handler != 0); + if (handler) + bcopy (handler, float_handler, sizeof (float_handler)); +} + +/* Signals actually come here. */ + +static void +float_signal () +{ + if (float_handled == 0) + abort (); + float_handled = 0; + longjmp (float_handler, 1); +} + +/* Handler for SIGPIPE. */ + +static void +pipe_closed () +{ + fatal ("output pipe has been closed"); +} + +/* Compile an entire file of output from cpp, named NAME. + Write a file of assembly output and various debugging dumps. */ + +static void +compile_file (name) + char *name; +{ + tree globals; + int start_time; + int dump_base_name_length; + + int name_specified = name != 0; + + if (dump_base_name == 0) + dump_base_name = name ? name : "gccdump"; + dump_base_name_length = strlen (dump_base_name); + + parse_time = 0; + varconst_time = 0; + integration_time = 0; + jump_time = 0; + cse_time = 0; + loop_time = 0; + flow_time = 0; + combine_time = 0; + local_alloc_time = 0; + global_alloc_time = 0; + dbr_sched_time = 0; + final_time = 0; + symout_time = 0; + dump_time = 0; + + /* Open input file. */ + + if (name == 0 || !strcmp (name, "-")) + { + finput = stdin; + name = "stdin"; + } + else + finput = fopen (name, "r"); + if (finput == 0) + pfatal_with_name (name); + + /* Initialize data in various passes. */ + + init_tree (); + init_lex (); + init_rtl (); + init_emit_once (); + init_decl_processing (); + init_optabs (); + + /* If rtl dump desired, open the output file. */ + if (rtl_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".rtl"); + rtl_dump_file = fopen (dumpname, "w"); + if (rtl_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If jump_opt dump desired, open the output file. */ + if (jump_opt_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".jump"); + jump_opt_dump_file = fopen (dumpname, "w"); + if (jump_opt_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If cse dump desired, open the output file. */ + if (cse_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".cse"); + cse_dump_file = fopen (dumpname, "w"); + if (cse_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If loop dump desired, open the output file. */ + if (loop_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".loop"); + loop_dump_file = fopen (dumpname, "w"); + if (loop_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If flow dump desired, open the output file. */ + if (flow_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".flow"); + flow_dump_file = fopen (dumpname, "w"); + if (flow_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If combine dump desired, open the output file. */ + if (combine_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 10); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".combine"); + combine_dump_file = fopen (dumpname, "w"); + if (combine_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If local_reg dump desired, open the output file. */ + if (local_reg_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".lreg"); + local_reg_dump_file = fopen (dumpname, "w"); + if (local_reg_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If global_reg dump desired, open the output file. */ + if (global_reg_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".greg"); + global_reg_dump_file = fopen (dumpname, "w"); + if (global_reg_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If jump2_opt dump desired, open the output file. */ + if (jump2_opt_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".jump2"); + jump2_opt_dump_file = fopen (dumpname, "w"); + if (jump2_opt_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* If dbr_sched dump desired, open the output file. */ + if (dbr_sched_dump) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".dbr"); + dbr_sched_dump_file = fopen (dumpname, "w"); + if (dbr_sched_dump_file == 0) + pfatal_with_name (dumpname); + } + + /* Open assembler code output file. */ + + if (! name_specified && asm_file_name == 0) + asm_out_file = stdout; + else + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + int len = strlen (dump_base_name); + strcpy (dumpname, dump_base_name); + if (len > 2 && ! strcmp (".c", dumpname + len - 2)) + dumpname[len - 2] = 0; + else if (len > 2 && ! strcmp (".i", dumpname + len - 2)) + dumpname[len - 2] = 0; + else if (len > 3 && ! strcmp (".co", dumpname + len - 3)) + dumpname[len - 3] = 0; + strcat (dumpname, ".s"); + if (asm_file_name == 0) + { + asm_file_name = (char *) malloc (strlen (dumpname) + 1); + strcpy (asm_file_name, dumpname); + } + if (!strcmp (asm_file_name, "-")) + asm_out_file = stdout; + else + asm_out_file = fopen (asm_file_name, "w"); + if (asm_out_file == 0) + pfatal_with_name (asm_file_name); + } + + input_filename = name; + + /* the beginning of the file is a new line; check for # */ + /* With luck, we discover the real source file's name from that + and put it in input_filename. */ + ungetc (check_newline (), finput); + + /* If the input doesn't start with a #line, use the input name + as the official input file name. */ + if (main_input_filename == 0) + main_input_filename = name; + + /* Put an entry on the input file stack for the main input file. */ + input_file_stack + = (struct file_stack *) xmalloc (sizeof (struct file_stack)); + input_file_stack->next = 0; + input_file_stack->name = input_filename; + + ASM_FILE_START (asm_out_file); + + /* Output something to inform GDB that this compilation was by GCC. */ +#ifndef ASM_IDENTIFY_GCC + fprintf (asm_out_file, "gcc_compiled.:\n"); +#else + ASM_IDENTIFY_GCC (asm_out_file); +#endif + + /* If GDB symbol table desired, open the GDB symbol output file. */ + if (write_symbols == GDB_DEBUG) + { + register char *dumpname = (char *) xmalloc (dump_base_name_length + 6); + int len = strlen (dump_base_name); + strcpy (dumpname, dump_base_name); + if (len > 2 && ! strcmp (".c", dumpname + len - 2)) + dumpname[len - 2] = 0; + else if (len > 2 && ! strcmp (".i", dumpname + len - 2)) + dumpname[len - 2] = 0; + else if (len > 3 && ! strcmp (".co", dumpname + len - 3)) + dumpname[len - 3] = 0; + strcat (dumpname, ".sym"); + if (sym_file_name == 0) + sym_file_name = dumpname; + symout_init (sym_file_name, asm_out_file, main_input_filename); + } + + /* If dbx symbol table desired, initialize writing it + and output the predefined types. */ +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG) + dbxout_init (asm_out_file, main_input_filename); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + sdbout_init (asm_out_file, main_input_filename); +#endif + + /* Initialize yet another pass. */ + + init_final (main_input_filename); + + start_time = gettime (); + + /* Call the parser, which parses the entire file + (calling rest_of_compilation for each function). */ + + yyparse (); + + /* Compilation is now finished except for writing + what's left of the symbol table output. */ + + parse_time += gettime () - start_time; + + parse_time -= integration_time; + parse_time -= varconst_time; + + globals = getdecls (); + + /* Really define vars that have had only a tentative definition. + Really output inline functions that must actually be callable + and have not been output so far. */ + + { + tree decl; + for (decl = globals; decl; decl = TREE_CHAIN (decl)) + { + if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl) + && ! TREE_ASM_WRITTEN (decl)) + { + /* Don't write out static consts, unless we needed + to take their address for some reason. */ + if (! TREE_READONLY (decl) + || TREE_PUBLIC (decl) + || TREE_ADDRESSABLE (decl)) + rest_of_decl_compilation (decl, 0, 1, 1); + /* Otherwise maybe mention them just for the debugger. */ +#ifdef DBX_DEBUGGING_INFO + else if (DECL_INITIAL (decl) && write_symbols == DBX_DEBUG) + TIMEVAR (varconst_time, dbxout_symbol (decl, 0)); +#endif +#ifdef SDB_DEBUGGING_INFO + else if (DECL_INITIAL (decl) && write_symbols == SDB_DEBUG) + TIMEVAR (varconst_time, sdbout_symbol (decl, 0)); +#endif + } + if (TREE_CODE (decl) == FUNCTION_DECL + && ! TREE_ASM_WRITTEN (decl) + && DECL_INITIAL (decl) != 0 + && TREE_ADDRESSABLE (decl) + && ! TREE_EXTERNAL (decl)) + output_inline_function (decl); + + /* Warn about any function declared static but not defined. */ + if (warn_unused + && TREE_CODE (decl) == FUNCTION_DECL + && DECL_INITIAL (decl) == 0 + && TREE_EXTERNAL (decl) + && ! TREE_PUBLIC (decl)) + warning_with_decl (decl, "`%s' declared but never defined"); + /* Warn about statics fns or vars defined but not used, + but not about inline functions + since unused inline statics is normal practice. */ + if (warn_unused + && (TREE_CODE (decl) == FUNCTION_DECL + || TREE_CODE (decl) == VAR_DECL) + && ! TREE_EXTERNAL (decl) + && ! TREE_PUBLIC (decl) + && ! TREE_USED (decl) + && ! TREE_INLINE (decl) + /* The TREE_USED bit for file-scope decls + is kept in the identifier, to handle multiple + external decls in different scopes. */ + && ! TREE_USED (DECL_NAME (decl))) + warning_with_decl (decl, "`%s' defined but not used"); + } + } + + /* Do dbx symbols */ +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG) + TIMEVAR (symout_time, + { + dbxout_tags (gettags ()); + dbxout_types (get_permanent_types ()); + }); +#endif + +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG) + TIMEVAR (symout_time, + { + sdbout_tags (gettags ()); + sdbout_types (get_permanent_types ()); + }); +#endif + + /* Do gdb symbols */ + if (write_symbols == GDB_DEBUG) + TIMEVAR (symout_time, + { + struct stat statbuf; + fstat (fileno (finput), &statbuf); + symout_types (get_permanent_types ()); + symout_top_blocks (globals, gettags ()); + symout_finish (name, statbuf.st_ctime); + }); + + /* Output some stuff at end of file if nec. */ + + end_final (main_input_filename); + +#ifdef ASM_FILE_END + ASM_FILE_END (asm_out_file); +#endif + + /* Close the dump files. */ + + if (rtl_dump) + fclose (rtl_dump_file); + + if (jump_opt_dump) + fclose (jump_opt_dump_file); + + if (cse_dump) + fclose (cse_dump_file); + + if (loop_dump) + fclose (loop_dump_file); + + if (flow_dump) + fclose (flow_dump_file); + + if (combine_dump) + { + dump_combine_total_stats (combine_dump_file); + fclose (combine_dump_file); + } + + if (local_reg_dump) + fclose (local_reg_dump_file); + + if (global_reg_dump) + fclose (global_reg_dump_file); + + if (jump2_opt_dump) + fclose (jump2_opt_dump_file); + + if (dbr_sched_dump) + fclose (dbr_sched_dump_file); + + /* Close non-debugging input and output files. Take special care to note + whether fclose returns an error, since the pages might still be on the + buffer chain while the file is open. */ + + fclose (finput); + if (ferror (asm_out_file) != 0 || fclose (asm_out_file) != 0) + fatal_io_error (asm_file_name); + + /* Print the times. */ + + if (! quiet_flag) + { + fprintf (stderr,"\n"); + print_time ("parse", parse_time); + print_time ("integration", integration_time); + print_time ("jump", jump_time); + print_time ("cse", cse_time); + print_time ("loop", loop_time); + print_time ("flow", flow_time); + print_time ("combine", combine_time); + print_time ("local-alloc", local_alloc_time); + print_time ("global-alloc", global_alloc_time); + print_time ("dbranch", dbr_sched_time); + print_time ("final", final_time); + print_time ("varconst", varconst_time); + print_time ("symout", symout_time); + print_time ("dump", dump_time); + } +} + +/* This is called from finish_decl (within yyparse) + for each declaration of a function or variable. + This does nothing for automatic variables. + Otherwise, it sets up the RTL and outputs any assembler code + (label definition, storage allocation and initialization). + + DECL is the declaration. If ASMSPEC is nonzero, it specifies + the assembler symbol name to be used. TOP_LEVEL is nonzero + if this declaration is not within a function. */ + +void +rest_of_decl_compilation (decl, asmspec, top_level, at_end) + tree decl; + char *asmspec; + int top_level; + int at_end; +{ + /* Declarations of variables, and of functions defined elsewhere. */ + + if (TREE_STATIC (decl) || TREE_EXTERNAL (decl)) + TIMEVAR (varconst_time, + { + make_decl_rtl (decl, asmspec, top_level); + /* Don't output anything + when a tentative file-scope definition is seen. + But at end of compilation, do output code for them. */ + if (! (! at_end && top_level + && (DECL_INITIAL (decl) == 0 + || DECL_INITIAL (decl) == error_mark_node))) + assemble_variable (decl, top_level, write_symbols, at_end); + }); + else if (TREE_REGDECL (decl) && asmspec != 0) + { + if (decode_reg_name (asmspec) >= 0) + { + DECL_RTL (decl) = 0; + make_decl_rtl (decl, asmspec, top_level); + } + else + error ("invalid register name `%s' for register variable", asmspec); + } +#ifdef DBX_DEBUGGING_INFO + else if (write_symbols == DBX_DEBUG && TREE_CODE (decl) == TYPE_DECL) + TIMEVAR (varconst_time, dbxout_symbol (decl, 0)); +#endif +#ifdef SDB_DEBUGGING_INFO + else if (write_symbols == SDB_DEBUG && top_level + && TREE_CODE (decl) == TYPE_DECL) + TIMEVAR (varconst_time, sdbout_symbol (decl, 0)); +#endif + + if (top_level) + { + if (write_symbols == GDB_DEBUG) + { + TIMEVAR (symout_time, + { + /* The initizations make types when they contain + string constants. The types are on the temporary + obstack, so output them now before they go away. */ + symout_types (get_temporary_types ()); + }); + } + else + /* Clean out the temporary type list, since the types will go away. */ + get_temporary_types (); + } +} + +/* This is called from finish_function (within yyparse) + after each top-level definition is parsed. + It is supposed to compile that function or variable + and output the assembler code for it. + After we return, the tree storage is freed. */ + +void +rest_of_compilation (decl) + tree decl; +{ + register rtx insns; + int start_time = gettime (); + int tem; + + /* If we are reconsidering an inline function + at the end of compilation, skip the stuff for making it inline. */ + + if (DECL_SAVED_INSNS (decl) == 0) + { + + /* If requested, consider whether to make this function inline. */ + if (flag_inline_functions || TREE_INLINE (decl)) + { + TIMEVAR (integration_time, + { + int specd = TREE_INLINE (decl); + char *lose = function_cannot_inline_p (decl); + if (lose != 0 && specd) + warning_with_decl (decl, lose); + if (lose == 0) + save_for_inline (decl); + else + TREE_INLINE (decl) = 0; + }); + } + + insns = get_insns (); + + /* Dump the rtl code if we are dumping rtl. */ + + if (rtl_dump) + TIMEVAR (dump_time, + { + fprintf (rtl_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + if (DECL_SAVED_INSNS (decl)) + fprintf (rtl_dump_file, ";; (integrable)\n\n"); + print_rtl (rtl_dump_file, insns); + fflush (rtl_dump_file); + }); + + /* If function is inline, and we don't yet know whether to + compile it by itself, defer decision till end of compilation. + finish_compilation will call rest_of_compilation again + for those functions that need to be output. */ + + if (((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl) + && ! flag_keep_inline_functions) + || TREE_EXTERNAL (decl)) + && TREE_INLINE (decl)) + goto exit_rest_of_compilation; + } + + if (rtl_dump_and_exit || flag_syntax_only) + { + get_temporary_types (); + goto exit_rest_of_compilation; + } + + TREE_ASM_WRITTEN (decl) = 1; + + insns = get_insns (); + + /* Copy any shared structure that should not be shared. */ + + unshare_all_rtl (insns); + + /* See if we have allocated stack slots that are not directly addressable. + If so, scan all the insns and create explicit address computation + for all references to such slots. */ +/* fixup_stack_slots (); */ + + /* Do jump optimization the first time, if -opt. + Also do it if -W, but in that case it doesn't change the rtl code, + it only computes whether control can drop off the end of the function. */ + + if (optimize || extra_warnings || warn_return_type + /* If function is `volatile', we should warn if it tries to return. */ + || TREE_THIS_VOLATILE (decl)) + TIMEVAR (jump_time, jump_optimize (insns, 0, 0)); + + /* Dump rtl code after jump, if we are doing that. */ + + if (jump_opt_dump) + TIMEVAR (dump_time, + { + fprintf (jump_opt_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (jump_opt_dump_file, insns); + fflush (jump_opt_dump_file); + }); + + /* Perform common subexpression elimination. + Nonzero value from `cse_main' means that jumps were simplified + and some code may now be unreachable, so do + jump optimization again. */ + + if (optimize) + { + TIMEVAR (cse_time, reg_scan (insns, max_reg_num (), 0)); + + TIMEVAR (cse_time, tem = cse_main (insns, max_reg_num ())); + + if (tem) + TIMEVAR (jump_time, jump_optimize (insns, 0, 0)); + } + + /* Dump rtl code after cse, if we are doing that. */ + + if (cse_dump) + TIMEVAR (dump_time, + { + fprintf (cse_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (cse_dump_file, insns); + fflush (cse_dump_file); + }); + + if (loop_dump) + TIMEVAR (dump_time, + { + fprintf (loop_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + /* Move constant computations out of loops. */ + + if (optimize) + { + TIMEVAR (loop_time, + { + reg_scan (insns, max_reg_num (), 1); + loop_optimize (insns, loop_dump ? loop_dump_file : 0); + }); + } + + /* Dump rtl code after loop opt, if we are doing that. */ + + if (loop_dump) + TIMEVAR (dump_time, + { + print_rtl (loop_dump_file, insns); + fflush (loop_dump_file); + }); + + /* Now we choose between stupid (pcc-like) register allocation + (if we got the -noreg switch and not -opt) + and smart register allocation. */ + + if (optimize) /* Stupid allocation probably won't work */ + obey_regdecls = 0; /* if optimizations being done. */ + + regclass_init (); + + /* Print function header into flow dump now + because doing the flow analysis makes some of the dump. */ + + if (flow_dump) + TIMEVAR (dump_time, + { + fprintf (flow_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + }); + + if (obey_regdecls) + { + TIMEVAR (flow_time, + { + regclass (insns, max_reg_num ()); + stupid_life_analysis (insns, max_reg_num (), + flow_dump_file); + }); + } + else + { + /* Do control and data flow analysis, + and write some of the results to dump file. */ + + TIMEVAR (flow_time, flow_analysis (insns, max_reg_num (), + flow_dump_file)); + if (extra_warnings) + uninitialized_vars_warning (DECL_INITIAL (decl)); + } + + /* Dump rtl after flow analysis. */ + + if (flow_dump) + TIMEVAR (dump_time, + { + print_rtl (flow_dump_file, insns); + fflush (flow_dump_file); + }); + + /* If -opt, try combining insns through substitution. */ + + if (optimize) + TIMEVAR (combine_time, combine_instructions (insns, max_reg_num ())); + + /* Dump rtl code after insn combination. */ + + if (combine_dump) + TIMEVAR (dump_time, + { + fprintf (combine_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + dump_combine_stats (combine_dump_file); + print_rtl (combine_dump_file, insns); + fflush (combine_dump_file); + }); + + /* Unless we did stupid register allocation, + allocate pseudo-regs that are used only within 1 basic block. */ + + if (!obey_regdecls) + TIMEVAR (local_alloc_time, + { + regclass (insns, max_reg_num ()); + local_alloc (); + }); + + /* Dump rtl code after allocating regs within basic blocks. */ + + if (local_reg_dump) + TIMEVAR (dump_time, + { + fprintf (local_reg_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + dump_flow_info (local_reg_dump_file); + dump_local_alloc (local_reg_dump_file); + print_rtl (local_reg_dump_file, insns); + fflush (local_reg_dump_file); + }); + + if (global_reg_dump) + TIMEVAR (dump_time, + fprintf (global_reg_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl)))); + + /* Unless we did stupid register allocation, + allocate remaining pseudo-regs, then do the reload pass + fixing up any insns that are invalid. */ + + TIMEVAR (global_alloc_time, + { + if (!obey_regdecls) + global_alloc (global_reg_dump ? global_reg_dump_file : 0); + else + reload (insns, 0, + global_reg_dump ? global_reg_dump_file : 0); + }); + + if (global_reg_dump) + TIMEVAR (dump_time, + { + dump_global_regs (global_reg_dump_file); + print_rtl (global_reg_dump_file, insns); + fflush (global_reg_dump_file); + }); + + rtx_equal_function_value_matters = 1; + reload_completed = 1; + + /* One more attempt to remove jumps to .+1 + left by dead-store-elimination. + Also do cross-jumping this time + and delete no-op move insns. */ + + if (optimize) + { + TIMEVAR (jump_time, jump_optimize (insns, 1, 1)); + } + + /* Dump rtl code after jump, if we are doing that. */ + + if (jump2_opt_dump) + TIMEVAR (dump_time, + { + fprintf (jump2_opt_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (jump2_opt_dump_file, insns); + fflush (jump2_opt_dump_file); + }); + + /* If a scheduling pass for delayed branches is to be done, + call the scheduling code. */ + +#ifdef HAVE_DELAYED_BRANCH + if (optimize && flag_delayed_branch) + { + TIMEVAR (dbr_sched_time, dbr_schedule (insns, dbr_sched_dump_file)); + if (dbr_sched_dump) + { + TIMEVAR (dump_time, + { + fprintf (dbr_sched_dump_file, "\n;; Function %s\n\n", + IDENTIFIER_POINTER (DECL_NAME (decl))); + print_rtl (dbr_sched_dump_file, insns); + fflush (dbr_sched_dump_file); + }); + } + } +#endif + + /* Now turn the rtl into assembler code. */ + + TIMEVAR (final_time, + { + assemble_function (decl); + final_start_function (insns, asm_out_file, + write_symbols, optimize); + final (insns, asm_out_file, + write_symbols, optimize, 0); + final_end_function (insns, asm_out_file, + write_symbols, optimize); + fflush (asm_out_file); + }); + + /* Write GDB symbols if requested */ + + if (write_symbols == GDB_DEBUG) + { + TIMEVAR (symout_time, + { + symout_types (get_permanent_types ()); + symout_types (get_temporary_types ()); + + DECL_BLOCK_SYMTAB_ADDRESS (decl) + = symout_function (DECL_INITIAL (decl), + DECL_ARGUMENTS (decl), 0); + symout_function_end (); + }); + } + else + get_temporary_types (); + + /* Write DBX symbols if requested */ + +#ifdef DBX_DEBUGGING_INFO + if (write_symbols == DBX_DEBUG) + TIMEVAR (symout_time, dbxout_function (decl)); +#endif + + exit_rest_of_compilation: + + rtx_equal_function_value_matters = 0; + reload_completed = 0; + + /* Clear out the real_constant_chain before some of the rtx's + it runs through become garbage. */ + + clear_const_double_mem (); + + /* The parsing time is all the time spent in yyparse + *except* what is spent in this function. */ + + parse_time -= gettime () - start_time; +} + +/* Entry point of cc1. Decode command args, then call compile_file. + Exit code is 35 if can't open files, 34 if fatal error, + 33 if had nonfatal errors, else success. */ + +int +main (argc, argv, envp) + int argc; + char **argv; + char **envp; +{ + register int i; + char *filename = 0; + int print_mem_flag = 0; + char *p; + + /* save in case md file wants to emit args as a comment. */ + save_argc = argc; + save_argv = argv; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && p[-1] != '/') --p; + progname = p; + +#ifdef RLIMIT_STACK + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + signal (SIGFPE, float_signal); + + signal (SIGPIPE, pipe_closed); + + /* Initialize whether `char' is signed. */ + flag_signed_char = DEFAULT_SIGNED_CHAR; +#ifdef DEFAULT_SHORT_ENUMS + /* Initialize how much space enums occupy, by default. */ + flag_short_enums = DEFAULT_SHORT_ENUMS; +#endif + + /* This is zeroed by -O. */ + obey_regdecls = 1; + + /* Initialize register usage now so switches may override. */ + init_reg_sets (); + + target_flags = 0; + set_target_switch (""); + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] != 0) + { + register char *str = argv[i] + 1; + if (str[0] == 'Y') + str++; + + if (str[0] == 'm') + set_target_switch (&str[1]); + else if (!strcmp (str, "dumpbase")) + { + dump_base_name = argv[++i]; + } + else if (str[0] == 'd') + { + register char *p = &str[1]; + while (*p) + switch (*p++) + { + case 'c': + combine_dump = 1; + break; + case 'd': + dbr_sched_dump = 1; + break; + case 'f': + flow_dump = 1; + break; + case 'g': + global_reg_dump = 1; + break; + case 'j': + jump_opt_dump = 1; + break; + case 'J': + jump2_opt_dump = 1; + break; + case 'l': + local_reg_dump = 1; + break; + case 'L': + loop_dump = 1; + break; + case 'm': + print_mem_flag = 1; + break; + case 'r': + rtl_dump = 1; + break; + case 's': + cse_dump = 1; + break; + case 'y': + yydebug = 1; + break; + } + } + else if (str[0] == 'f') + { + int j; + register char *p = &str[1]; + int found = 0; + + /* Some kind of -f option. + P's value is the option sans `-f'. + Search for it in the table of options. */ + + for (j = 0; + !found && j < sizeof (f_options) / sizeof (f_options[0]); + j++) + { + if (!strcmp (p, f_options[j].string)) + { + *f_options[j].variable = f_options[j].on_value; + /* A goto here would be cleaner, + but breaks the vax pcc. */ + found = 1; + } + if (p[0] == 'n' && p[1] == 'o' && p[2] == '-' + && ! strcmp (p+3, f_options[j].string)) + { + *f_options[j].variable = ! f_options[j].on_value; + found = 1; + } + } + + if (found) + ; + else if (!strncmp (p, "fixed-", 6)) + fix_register (&p[6], 1, 1); + else if (!strncmp (p, "call-used-", 10)) + fix_register (&p[10], 0, 1); + else if (!strncmp (p, "call-saved-", 11)) + fix_register (&p[11], 0, 0); + else if (! lang_decode_option (argv[i])) + error ("Invalid option `%s'", argv[i]); + } + else if (!strcmp (str, "noreg")) + ; + else if (!strcmp (str, "opt")) + optimize = 1, obey_regdecls = 0; + else if (!strcmp (str, "O")) + optimize = 1, obey_regdecls = 0; + else if (!strcmp (str, "pedantic")) + pedantic = 1; + else if (lang_decode_option (argv[i])) + ; + else if (!strcmp (str, "quiet")) + quiet_flag = 1; + else if (!strcmp (str, "version")) + { + extern char *version_string, *language_string; + fprintf (stderr, "%s version %s", language_string, version_string); +#ifdef TARGET_VERSION + TARGET_VERSION; +#endif +#ifdef __GNUC__ +#ifndef __VERSION__ +#define __VERSION__ "[unknown]" +#endif + fprintf (stderr, " compiled by GNU C version %s.\n", __VERSION__); +#else + fprintf (stderr, " compiled by CC.\n"); +#endif + print_target_switch_defaults (); + } + else if (!strcmp (str, "w")) + inhibit_warnings = 1; + else if (!strcmp (str, "W")) + extra_warnings = 1; + else if (!strcmp (str, "Wunused")) + warn_unused = 1; + else if (!strcmp (str, "Wshadow")) + warn_shadow = 1; + else if (!strcmp (str, "Wswitch")) + warn_switch = 1; + else if (!strncmp (str, "Wid-clash-", 10)) + { + char *endp = str + 10; + + while (*endp) + { + if (*endp >= '0' && *endp <= '9') + endp++; + else + error ("Invalid option `%s'", argv[i]); + } + warn_id_clash = 1; + id_clash_len = atoi (str + 10); + } + else if (!strcmp (str, "p")) + profile_flag = 1; + else if (!strcmp (str, "a")) + { +#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER) + warning ("`-a' option (basic block profile) not supported"); +#else + profile_block_flag = 1; +#endif + } + else if (!strcmp (str, "gg")) + write_symbols = GDB_DEBUG; +#ifdef DBX_DEBUGGING_INFO + else if (!strcmp (str, "g0")) + write_symbols = DBX_DEBUG; + else if (!strcmp (str, "G0")) + write_symbols = DBX_DEBUG; + else if (!strcmp (str, "g")) + { + write_symbols = DBX_DEBUG; + use_gdb_dbx_extensions = 1; + } + else if (!strcmp (str, "G")) + { + write_symbols = DBX_DEBUG; + use_gdb_dbx_extensions = 1; + } +#endif +#ifdef SDB_DEBUGGING_INFO + else if (!strcmp (str, "g")) + write_symbols = SDB_DEBUG; + else if (!strcmp (str, "G")) + write_symbols = SDB_DEBUG; + else if (!strcmp (str, "g0")) + write_symbols = SDB_DEBUG; + else if (!strcmp (str, "G0")) + write_symbols = SDB_DEBUG; +#endif + else if (!strcmp (str, "symout")) + { + if (write_symbols == NO_DEBUG) + write_symbols = GDB_DEBUG; + sym_file_name = argv[++i]; + } + else if (!strcmp (str, "o")) + { + asm_file_name = argv[++i]; + } + else + error ("Invalid option `%s'", argv[i]); + } + else + filename = argv[i]; + +#ifdef OVERRIDE_OPTIONS + /* Some machines may reject certain combinations of options. */ + OVERRIDE_OPTIONS; +#endif + + /* Now that register usage is specified, convert it to HARD_REG_SETs. */ + init_reg_sets_1 (); + + compile_file (filename); + +#ifndef USG +#ifndef VMS + if (print_mem_flag) + { + extern char **environ; + char *lim = (char *) sbrk (0); + + fprintf (stderr, "Data size %d.\n", + (int) lim - (int) &environ); + fflush (stderr); + + system ("ps v"); + } +#endif /* not VMS */ +#endif /* not USG */ + + if (errorcount) + exit (FATAL_EXIT_CODE); + if (sorrycount) + exit (FATAL_EXIT_CODE); + exit (SUCCESS_EXIT_CODE); + return 34; +} + +/* Decode -m switches. */ + +/* Here is a table, controlled by the tm-...h file, listing each -m switch + and which bits in `target_switches' it should set or clear. + If VALUE is positive, it is bits to set. + If VALUE is negative, -VALUE is bits to clear. + (The sign bit is not used so there is no confusion.) */ + +struct {char *name; int value;} target_switches [] + = TARGET_SWITCHES; + +/* Decode the switch -mNAME. */ + +void +set_target_switch (name) + char *name; +{ + register int j; + for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) + if (!strcmp (target_switches[j].name, name)) + { + if (target_switches[j].value < 0) + target_flags &= ~-target_switches[j].value; + else + target_flags |= target_switches[j].value; + return; + } + error ("Invalid option `%s'", name); +} + +/* Print default target switches for -version. */ + +void +print_target_switch_defaults () +{ + register int j; + register int mask = TARGET_DEFAULT; + fprintf (stderr, "default target switches:"); + for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) + if (target_switches[j].name[0] != '\0' + && target_switches[j].value > 0 + && (target_switches[j].value & mask) == target_switches[j].value) + + fprintf (stderr, " -m%s", target_switches[j].name); + + fprintf (stderr, "\n"); +} -- 2.20.1