1321 lines
35 KiB
C
1321 lines
35 KiB
C
/* Language-depednent node constructors for parse phase of GNU compiler.
|
||
Copyright (C) 1987, 1988 Free Software Foundation, Inc.
|
||
Hacked by Michael Tiemann (tiemann@mcc.com)
|
||
|
||
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. */
|
||
|
||
#include "config.h"
|
||
#include <stdio.h>
|
||
#include "obstack.h"
|
||
#include "tree.h"
|
||
#include "cplus-tree.h"
|
||
#include "flags.h"
|
||
#include "assert.h"
|
||
|
||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||
#define CEIL(x,y) (((x) + (y) - 1) / (y))
|
||
|
||
/* Return nonzero if REF is an lvalue valid for this language.
|
||
Lvalues can be assigned, unless they have TREE_READONLY.
|
||
Lvalues can have their address taken, unless they have TREE_REGDECL. */
|
||
|
||
int
|
||
lvalue_p (ref)
|
||
tree ref;
|
||
{
|
||
register enum tree_code code = TREE_CODE (ref);
|
||
|
||
if (language_lvalue_valid (ref))
|
||
switch (code)
|
||
{
|
||
case COMPONENT_REF:
|
||
return lvalue_p (TREE_OPERAND (ref, 0));
|
||
|
||
case STRING_CST:
|
||
return 1;
|
||
|
||
case VAR_DECL:
|
||
if (TREE_READONLY (ref) && ! TREE_STATIC (ref)
|
||
&& DECL_LANG_SPECIFIC (ref)
|
||
&& DECL_IN_AGGR_P (ref))
|
||
return 0;
|
||
case INDIRECT_REF:
|
||
case ARRAY_REF:
|
||
case PARM_DECL:
|
||
case RESULT_DECL:
|
||
case ERROR_MARK:
|
||
if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
|
||
&& TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)
|
||
return 1;
|
||
break;
|
||
|
||
case NEW_EXPR:
|
||
return 1;
|
||
|
||
case CALL_EXPR:
|
||
if (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE
|
||
/* unary_complex_lvalue knows how to deal with this case. */
|
||
|| TREE_ADDRESSABLE (TREE_TYPE (ref)))
|
||
return 1;
|
||
break;
|
||
|
||
/* A currently unresolved scope ref. */
|
||
case SCOPE_REF:
|
||
abort ();
|
||
case OFFSET_REF:
|
||
if (TREE_CODE (TREE_OPERAND (ref, 1)) == FUNCTION_DECL)
|
||
return 1;
|
||
if (TREE_CODE (TREE_OPERAND (ref, 1)) == VAR_DECL)
|
||
if (TREE_READONLY (ref) && ! TREE_STATIC (ref)
|
||
&& DECL_LANG_SPECIFIC (ref)
|
||
&& DECL_IN_AGGR_P (ref))
|
||
return 0;
|
||
else
|
||
return 1;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Return nonzero if REF is an lvalue valid for this language;
|
||
otherwise, print an error message and return zero. */
|
||
|
||
int
|
||
lvalue_or_else (ref, string)
|
||
tree ref;
|
||
char *string;
|
||
{
|
||
int win = lvalue_p (ref);
|
||
if (! win)
|
||
error ("invalid lvalue in %s", string);
|
||
return win;
|
||
}
|
||
|
||
/* INIT is a CALL_EXPR which needs info about its target.
|
||
TYPE is the type that this initialization should appear to have.
|
||
|
||
Build an encapsultation of the initialization to perfom
|
||
and return it so that it can be processed by language-independent
|
||
and language-specific expression expanders. */
|
||
tree
|
||
build_cplus_new (type, init)
|
||
tree type;
|
||
tree init;
|
||
{
|
||
tree slot = build (VAR_DECL, type);
|
||
tree rval = build (CPLUS_NEW_EXPR, type,
|
||
TREE_OPERAND (init, 0), TREE_OPERAND (init, 1), slot);
|
||
TREE_ADDRESSABLE (rval) = 1;
|
||
rval = build (NEW_EXPR, type, slot, rval, 0);
|
||
TREE_ADDRESSABLE (rval) = 1;
|
||
return rval;
|
||
}
|
||
|
||
extern struct obstack *current_obstack;
|
||
extern struct obstack permanent_obstack, class_obstack;
|
||
extern struct obstack *saveable_obstack;
|
||
|
||
/* Return a type like TYPE except that its CLASSTYPE_OFFSET
|
||
is OFFSET.
|
||
|
||
Such variant types already made are recorded so that duplicates
|
||
are not made.
|
||
|
||
A variant types should never be used as the type of an expression.
|
||
Use TYPE_MAIN_VARIANT to find the main variant. */
|
||
|
||
tree
|
||
build_classtype_variant (type, offset, virtualp)
|
||
tree type;
|
||
tree offset;
|
||
int virtualp;
|
||
{
|
||
register tree t, m = CLASSTYPE_MAIN_VARIANT (type);
|
||
register struct obstack *ambient_obstack = current_obstack;
|
||
register struct obstack *ambient_saveable_obstack = saveable_obstack;
|
||
register int lo = TREE_INT_CST_LOW (offset);
|
||
register int hi = TREE_INT_CST_HIGH (offset);
|
||
/* First search the chain variants for one that is what we want. */
|
||
|
||
if (hi == 0 && lo == 0)
|
||
offset = integer_zero_node;
|
||
|
||
for (t = m; t; t = CLASSTYPE_NEXT_VARIANT (t))
|
||
if (virtualp == TREE_VIA_VIRTUAL (t)
|
||
&& lo == TREE_INT_CST_LOW (CLASSTYPE_OFFSET (t))
|
||
&& hi == TREE_INT_CST_HIGH (CLASSTYPE_OFFSET (t)))
|
||
return t;
|
||
|
||
/* We need a new one. */
|
||
if (TREE_PERMANENT (type))
|
||
saveable_obstack = &permanent_obstack;
|
||
current_obstack = saveable_obstack;
|
||
|
||
t = copy_node (type);
|
||
copy_type_lang_specific (t);
|
||
CLASSTYPE_AS_LIST (t) = build_tree_list (NULL_TREE, t);
|
||
|
||
TYPE_POINTER_TO (t) = 0;
|
||
TYPE_REFERENCE_TO (t) = 0;
|
||
CLASSTYPE_OFFSET (t) = offset;
|
||
TREE_VIA_VIRTUAL (t) = virtualp;
|
||
|
||
/* Always promise to have TYPE_POINTER_TO filled in. */
|
||
build_pointer_type (t);
|
||
|
||
/* Add this type to the chain of variants of TYPE. */
|
||
CLASSTYPE_NEXT_VARIANT (t) = CLASSTYPE_NEXT_VARIANT (m);
|
||
CLASSTYPE_NEXT_VARIANT (m) = t;
|
||
|
||
current_obstack = ambient_obstack;
|
||
saveable_obstack = ambient_saveable_obstack;
|
||
return t;
|
||
}
|
||
|
||
/* Here is how primitive or already-canonicalized types' hash
|
||
codes are made. MUST BE CONSISTENT WITH tree.c !!! */
|
||
#define TYPE_HASH(TYPE) TREE_UID (TYPE)
|
||
|
||
/* Construct, lay out and return the type of methods belonging to class
|
||
BASETYPE and whose arguments and values are described by TYPE.
|
||
If that type exists already, reuse it.
|
||
TYPE must be a FUNCTION_TYPE node. */
|
||
|
||
tree
|
||
build_cplus_method_type (basetype, rettype, argtypes)
|
||
tree basetype, rettype, argtypes;
|
||
{
|
||
register tree t;
|
||
tree ptype = build_pointer_type (basetype);
|
||
int hashcode;
|
||
|
||
/* Make a node of the sort we want. */
|
||
t = make_node (METHOD_TYPE);
|
||
|
||
TYPE_METHOD_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
|
||
TREE_TYPE (t) = rettype;
|
||
ptype = build_type_variant (ptype, !flag_this_is_variable, 0);
|
||
|
||
/* The actual arglist for this function includes a "hidden" argument
|
||
which is "this". Put it into the list of argument types. */
|
||
|
||
TYPE_ARG_TYPES (t) = tree_cons (NULL, ptype, argtypes);
|
||
|
||
/* If we already have such a type, use the old one and free this one.
|
||
Note that it also frees up the above cons cell if found. */
|
||
hashcode = TYPE_HASH (basetype) + TYPE_HASH (rettype) + type_hash_list (argtypes);
|
||
t = type_hash_canon (hashcode, t);
|
||
|
||
if (TYPE_SIZE (t) == 0)
|
||
layout_type (t);
|
||
|
||
return t;
|
||
}
|
||
|
||
tree
|
||
build_cplus_array_type (elt_type, index_type)
|
||
tree elt_type;
|
||
tree index_type;
|
||
{
|
||
register struct obstack *ambient_obstack = current_obstack;
|
||
register struct obstack *ambient_saveable_obstack = saveable_obstack;
|
||
tree t;
|
||
|
||
/* We need a new one. If ELT_TYPE is permanent, make this permanent too. */
|
||
if (TREE_PERMANENT (elt_type))
|
||
{
|
||
current_obstack = &permanent_obstack;
|
||
saveable_obstack = &permanent_obstack;
|
||
}
|
||
|
||
t = build_array_type (elt_type, index_type);
|
||
|
||
/* Push these needs up so that initialization takes place
|
||
more easily. */
|
||
TYPE_NEEDS_CONSTRUCTING (t) = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (elt_type));
|
||
TYPE_NEEDS_DESTRUCTOR (t) = TYPE_NEEDS_DESTRUCTOR (TYPE_MAIN_VARIANT (elt_type));
|
||
current_obstack = ambient_obstack;
|
||
saveable_obstack = ambient_saveable_obstack;
|
||
return t;
|
||
}
|
||
|
||
/* This is temporary until later. */
|
||
/* Construct, lay out and return the type of objects which are of type TYPE
|
||
as members of type BASETYPE. If that type exists already, reuse it. */
|
||
tree
|
||
build_member_type (basetype, type)
|
||
tree basetype, type;
|
||
{
|
||
register tree t;
|
||
int hashcode;
|
||
|
||
assert (TREE_CODE (type) != FUNCTION_TYPE);
|
||
|
||
/* Make a node of the sort we want. */
|
||
t = make_node (OFFSET_TYPE);
|
||
TYPE_OFFSET_BASETYPE (t) = basetype;
|
||
TREE_TYPE (t) = type;
|
||
hashcode = TREE_UID (basetype) + TREE_UID (type);
|
||
t = type_hash_canon (hashcode, t);
|
||
|
||
return t;
|
||
}
|
||
|
||
/* Compute the actual offsets that our virtual base classes
|
||
will have *for this type*. This must be performed after
|
||
the fields are laid out, since virtual baseclasses must
|
||
lay down at the end of the record.
|
||
|
||
Returns the maximum number of virtual functions any of the virtual
|
||
baseclasses provide. */
|
||
int
|
||
layout_vbasetypes (rec, max)
|
||
tree rec;
|
||
int max;
|
||
{
|
||
/* Get all the virtual base types that this type uses.
|
||
The TREE_VALUE slot holds the virtual baseclass type. */
|
||
tree vbase_types = get_vbase_types (rec);
|
||
|
||
#ifdef STRUCTURE_SIZE_BOUNDARY
|
||
int record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
|
||
#else
|
||
int record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
|
||
#endif
|
||
|
||
/* Record size so far is CONST_SIZE + VAR_SIZE * SIZE_UNIT bits,
|
||
where CONST_SIZE is an integer
|
||
and VAR_SIZE is a tree expression.
|
||
If VAR_SIZE is null, the size is just CONST_SIZE.
|
||
Naturally we try to avoid using VAR_SIZE. */
|
||
register int const_size = 0;
|
||
register tree var_size = 0;
|
||
register int size_unit = BITS_PER_UNIT;
|
||
int nonvirtual_const_size;
|
||
tree nonvirtual_var_size;
|
||
|
||
CLASSTYPE_VBASECLASSES (rec) = vbase_types;
|
||
|
||
if (TREE_CODE (TYPE_SIZE (rec)) == INTEGER_CST)
|
||
const_size = TREE_INT_CST_LOW (TYPE_SIZE (rec)) * TYPE_SIZE_UNIT (rec);
|
||
else
|
||
{
|
||
var_size = TYPE_SIZE (rec);
|
||
size_unit = record_align;
|
||
}
|
||
|
||
nonvirtual_const_size = const_size;
|
||
nonvirtual_var_size = var_size;
|
||
|
||
while (vbase_types)
|
||
{
|
||
int inc;
|
||
tree basetype = ASSOC_TYPE (vbase_types);
|
||
tree offset;
|
||
|
||
if (const_size == 0)
|
||
offset = integer_zero_node;
|
||
else
|
||
offset = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
|
||
|
||
if (CLASSTYPE_VSIZE (basetype) > max)
|
||
max = CLASSTYPE_VSIZE (basetype);
|
||
ASSOC_OFFSET (vbase_types) = offset;
|
||
|
||
inc = MAX (record_align,
|
||
(TREE_INT_CST_LOW (TYPE_SIZE (basetype))
|
||
- TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype)))
|
||
* TYPE_SIZE_UNIT (basetype));
|
||
|
||
const_size += inc;
|
||
vbase_types = TREE_CHAIN (vbase_types);
|
||
}
|
||
|
||
if (const_size - nonvirtual_const_size)
|
||
{
|
||
CLASSTYPE_VBASE_SIZE (rec) = convert_units (build_int (const_size - nonvirtual_const_size),
|
||
1, BITS_PER_UNIT);
|
||
TYPE_SIZE (rec) = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
|
||
}
|
||
else
|
||
CLASSTYPE_VBASE_SIZE (rec) = integer_zero_node;
|
||
return max;
|
||
}
|
||
|
||
#if 0
|
||
/* This function should never be needed. */
|
||
void
|
||
fixup_vbase_offsets (type)
|
||
tree type;
|
||
{
|
||
tree virtuals = TREE_CHAIN (CLASS_ASSOC_VIRTUALS (type));
|
||
|
||
while (virtuals)
|
||
{
|
||
tree pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals));
|
||
tree decl = TREE_OPERAND (pfn, 0);
|
||
tree vcontext = get_base_type (DECL_VCONTEXT (decl), DECL_CONTEXT (decl), 0);
|
||
if (vcontext != NULL_TREE && TREE_VIA_VIRTUAL (vcontext))
|
||
{
|
||
tree offset;
|
||
tree vbase_offset_info;
|
||
tree parent_type;
|
||
|
||
if (DECL_CONTEXT (decl) == TYPE_MAIN_VARIANT (type))
|
||
parent_type = type;
|
||
else
|
||
{
|
||
parent_type = get_base_type (DECL_CONTEXT (decl), type, 0);
|
||
if (parent_type == 0)
|
||
parent_type = type;
|
||
}
|
||
vbase_offset_info = value_member (vcontext,
|
||
CLASSTYPE_VBASECLASSES (parent_type));
|
||
offset = genop (MINUS_EXPR, CLASSTYPE_OFFSET (parent_type),
|
||
TREE_PURPOSE (vbase_offset_info));
|
||
TREE_VALUE (virtuals) = build_vtable_entry (offset, pfn);
|
||
}
|
||
virtuals = TREE_CHAIN (virtuals);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* Lay out the base types of a record type, REC.
|
||
Tentatively set the size and alignment of REC
|
||
according to the base types alone.
|
||
|
||
Returns list of virtual base classes in a FIELD_DECL chain. */
|
||
tree
|
||
layout_basetypes (rec)
|
||
tree rec;
|
||
{
|
||
/* Chain to hold all the new FIELD_DECLs which point at virtual
|
||
base classes. */
|
||
tree vbase_decls = NULL_TREE;
|
||
|
||
#ifdef STRUCTURE_SIZE_BOUNDARY
|
||
int record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
|
||
#else
|
||
int record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
|
||
#endif
|
||
|
||
/* Record size so far is CONST_SIZE + VAR_SIZE * SIZE_UNIT bits,
|
||
where CONST_SIZE is an integer
|
||
and VAR_SIZE is a tree expression.
|
||
If VAR_SIZE is null, the size is just CONST_SIZE.
|
||
Naturally we try to avoid using VAR_SIZE. */
|
||
register int const_size = 0;
|
||
register tree var_size = 0;
|
||
register int size_unit = BITS_PER_UNIT;
|
||
int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (rec);
|
||
|
||
/* Handle basetypes almost like fields, but record their
|
||
offsets differently. */
|
||
|
||
for (i = 1; i <= n_baseclasses; i++)
|
||
{
|
||
int inc, desired_align;
|
||
register tree basetype = CLASSTYPE_BASECLASS (rec, i);
|
||
tree decl;
|
||
|
||
if (TYPE_SIZE (basetype) == 0)
|
||
{
|
||
error_with_aggr_type (basetype, "base class `%s' has incomplete type");
|
||
SET_CLASSTYPE_VIAS (rec, i, 1, 0);
|
||
continue;
|
||
}
|
||
|
||
/* All basetypes are recorded in the association list of the
|
||
derived type. */
|
||
|
||
if (CLASSTYPE_VIA_VIRTUAL (rec, i))
|
||
{
|
||
int j;
|
||
char *name = (char *)alloca (TYPE_NAME_LENGTH (basetype)
|
||
+ sizeof (VBASE_NAME) + 1);
|
||
sprintf (name, VBASE_NAME_FORMAT, TYPE_NAME_STRING (basetype));
|
||
|
||
/* The offset for a virtual base class is only
|
||
used in computing virtual function tables and
|
||
for initializing virtual base pointers. The assoc
|
||
for this base type is built once `get_vbase_types'
|
||
is called. */
|
||
CLASSTYPE_BASECLASS (rec, i) = basetype
|
||
= build_classtype_variant (basetype, integer_zero_node, 1);
|
||
|
||
/* If this basetype can come from another vbase pointer
|
||
without an additional indirection, we will share
|
||
that pointer. If an indirection is involved, we
|
||
make our own pointer. */
|
||
for (j = 1; j <= n_baseclasses; j++)
|
||
if (! CLASSTYPE_VIA_VIRTUAL (rec, j)
|
||
&& TYPE_USES_VIRTUAL_BASECLASSES (CLASSTYPE_BASECLASS (rec, j))
|
||
&& value_member (TYPE_MAIN_VARIANT (basetype),
|
||
CLASSTYPE_VBASECLASSES (CLASSTYPE_BASECLASS (rec, j))))
|
||
{
|
||
goto got_it;
|
||
}
|
||
|
||
decl = build_lang_decl (FIELD_DECL, get_identifier (name),
|
||
build_pointer_type (basetype));
|
||
DECL_FIELD_CONTEXT (decl) = rec;
|
||
SET_DECL_FCONTEXT (decl, TYPE_MAIN_VARIANT (basetype));
|
||
DECL_VBASE_P (decl) = 1;
|
||
TREE_CHAIN (decl) = vbase_decls;
|
||
vbase_decls = decl;
|
||
|
||
got_it:
|
||
/* The space this decl occupies has already been accounted for. */
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
tree class_offset;
|
||
tree assoc;
|
||
|
||
if (const_size == 0)
|
||
class_offset = integer_zero_node;
|
||
else
|
||
{
|
||
/* Give each base type the alignment it wants. */
|
||
const_size = CEIL (const_size, TYPE_ALIGN (basetype))
|
||
* TYPE_ALIGN (basetype);
|
||
class_offset = convert_units (build_int (const_size), 1, BITS_PER_UNIT);
|
||
CLASSTYPE_BASECLASS (rec, i) = basetype
|
||
= build_classtype_variant (basetype, class_offset, 0);
|
||
}
|
||
|
||
if (CLASSTYPE_VSIZE (basetype))
|
||
assoc = make_assoc (class_offset, basetype,
|
||
CLASS_ASSOC_VTABLE (basetype),
|
||
CLASS_ASSOC_VIRTUALS (basetype),
|
||
CLASSTYPE_ASSOC (rec));
|
||
else
|
||
assoc = make_assoc (class_offset, basetype, 0, 0,
|
||
CLASSTYPE_ASSOC (rec));
|
||
CLASSTYPE_ASSOC (rec) = assoc;
|
||
if (const_size != 0)
|
||
{
|
||
TYPE_NAME (basetype) = copy_node (TYPE_NAME (basetype));
|
||
TREE_TYPE (TYPE_NAME (basetype)) = basetype;
|
||
DECL_OFFSET (TYPE_NAME (basetype)) = const_size;
|
||
}
|
||
}
|
||
|
||
/* Add only the amount of storage not present in
|
||
the virtual baseclasses. */
|
||
inc = MAX (record_align,
|
||
(TREE_INT_CST_LOW (TYPE_SIZE (basetype))
|
||
- TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype)))
|
||
* TYPE_SIZE_UNIT (basetype));
|
||
|
||
/* Record must have at least as much alignment as any field. */
|
||
desired_align = TYPE_ALIGN (basetype);
|
||
record_align = MAX (record_align, desired_align);
|
||
|
||
const_size += inc;
|
||
}
|
||
|
||
if (const_size)
|
||
CLASSTYPE_SIZE (rec) = build_int_2 (const_size, 0);
|
||
else
|
||
CLASSTYPE_SIZE (rec) = integer_zero_node;
|
||
CLASSTYPE_ALIGN (rec) = record_align;
|
||
|
||
return vbase_decls;
|
||
}
|
||
|
||
/* Hashing of lists so that we don't make duplicates.
|
||
The entry point is `list_hash_canon'. */
|
||
|
||
/* Each hash table slot is a bucket containing a chain
|
||
of these structures. */
|
||
|
||
struct list_hash
|
||
{
|
||
struct list_hash *next; /* Next structure in the bucket. */
|
||
int hashcode; /* Hash code of this list. */
|
||
tree list; /* The list recorded here. */
|
||
};
|
||
|
||
/* Now here is the hash table. When recording a list, it is added
|
||
to the slot whose index is the hash code mod the table size.
|
||
Note that the hash table is used for several kinds of lists.
|
||
While all these live in the same table, they are completely independent,
|
||
and the hash code is computed differently for each of these. */
|
||
|
||
#define TYPE_HASH_SIZE 59
|
||
struct list_hash *list_hash_table[TYPE_HASH_SIZE];
|
||
|
||
/* Here is how primitive or already-canonicalized lists' hash
|
||
codes are made. */
|
||
#define TYPE_HASH(TYPE) TREE_UID (TYPE)
|
||
|
||
/* Compute a hash code for a list (chain of TREE_LIST nodes
|
||
with goodies in the TREE_PURPOSE, TREE_VALUE, and bits of the
|
||
TREE_COMMON slots), by adding the hash codes of the individual entries. */
|
||
|
||
int
|
||
list_hash (list)
|
||
tree list;
|
||
{
|
||
register int hashcode = 0;
|
||
|
||
if (TREE_CHAIN (list))
|
||
hashcode = TYPE_HASH (TREE_CHAIN (list));
|
||
else
|
||
hashcode = 0;
|
||
if (TREE_VALUE (list))
|
||
hashcode += TYPE_HASH (TREE_VALUE (list));
|
||
else
|
||
hashcode += 1007;
|
||
if (TREE_PURPOSE (list))
|
||
hashcode += TYPE_HASH (TREE_PURPOSE (list));
|
||
else
|
||
hashcode += 1009;
|
||
return hashcode;
|
||
}
|
||
|
||
/* Look in the type hash table for a type isomorphic to TYPE.
|
||
If one is found, return it. Otherwise return 0. */
|
||
|
||
tree
|
||
list_hash_lookup (hashcode, list)
|
||
int hashcode;
|
||
tree list;
|
||
{
|
||
register struct list_hash *h;
|
||
for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
|
||
if (h->hashcode == hashcode
|
||
&& TREE_VIA_VIRTUAL (h->list) == TREE_VIA_VIRTUAL (list)
|
||
&& TREE_VIA_PUBLIC (h->list) == TREE_VIA_PUBLIC (list)
|
||
&& TREE_PURPOSE (h->list) == TREE_PURPOSE (list)
|
||
&& TREE_VALUE (h->list) == TREE_VALUE (list))
|
||
{
|
||
assert (TREE_TYPE (h->list) == TREE_TYPE (list));
|
||
assert (TREE_CHAIN (h->list) == TREE_CHAIN (list));
|
||
return h->list;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Add an entry to the list-hash-table
|
||
for a list TYPE whose hash code is HASHCODE. */
|
||
|
||
void
|
||
list_hash_add (hashcode, list)
|
||
int hashcode;
|
||
tree list;
|
||
{
|
||
register struct list_hash *h;
|
||
|
||
h = (struct list_hash *) obstack_alloc (&class_obstack, sizeof (struct list_hash));
|
||
h->hashcode = hashcode;
|
||
h->list = list;
|
||
h->next = list_hash_table[hashcode % TYPE_HASH_SIZE];
|
||
list_hash_table[hashcode % TYPE_HASH_SIZE] = h;
|
||
}
|
||
|
||
/* Given TYPE, and HASHCODE its hash code, return the canonical
|
||
object for an identical list if one already exists.
|
||
Otherwise, return TYPE, and record it as the canonical object
|
||
if it is a permanent object.
|
||
|
||
To use this function, first create a list of the sort you want.
|
||
Then compute its hash code from the fields of the list that
|
||
make it different from other similar lists.
|
||
Then call this function and use the value.
|
||
This function frees the list you pass in if it is a duplicate. */
|
||
|
||
/* Set to 1 to debug without canonicalization. Never set by program. */
|
||
int debug_no_list_hash = 0;
|
||
|
||
tree
|
||
list_hash_canon (hashcode, list)
|
||
int hashcode;
|
||
tree list;
|
||
{
|
||
tree t1;
|
||
|
||
if (debug_no_list_hash)
|
||
return list;
|
||
|
||
t1 = list_hash_lookup (hashcode, list);
|
||
if (t1 != 0)
|
||
{
|
||
obstack_free (&class_obstack, list);
|
||
return t1;
|
||
}
|
||
|
||
/* If this is a new list, record it for later reuse. */
|
||
list_hash_add (hashcode, list);
|
||
|
||
return list;
|
||
}
|
||
|
||
tree
|
||
hash_tree_cons (via_public, via_virtual, purpose, value, chain)
|
||
int via_public, via_virtual;
|
||
tree purpose, value, chain;
|
||
{
|
||
struct obstack *ambient_obstack = current_obstack;
|
||
tree t;
|
||
int hashcode;
|
||
|
||
current_obstack = &class_obstack;
|
||
t = tree_cons (purpose, value, chain);
|
||
TREE_VIA_PUBLIC (t) = via_public;
|
||
TREE_VIA_VIRTUAL (t) = via_virtual;
|
||
hashcode = list_hash (t);
|
||
t = list_hash_canon (hashcode, t);
|
||
current_obstack = ambient_obstack;
|
||
return t;
|
||
}
|
||
|
||
/* Constructor for hashed lists. */
|
||
tree
|
||
hash_tree_chain (value, chain)
|
||
tree value, chain;
|
||
{
|
||
struct obstack *ambient_obstack = current_obstack;
|
||
tree t;
|
||
int hashcode;
|
||
|
||
current_obstack = &class_obstack;
|
||
t = tree_cons (NULL_TREE, value, chain);
|
||
hashcode = list_hash (t);
|
||
t = list_hash_canon (hashcode, t);
|
||
current_obstack = ambient_obstack;
|
||
return t;
|
||
}
|
||
|
||
/* Similar, but used for concatenating two lists. */
|
||
tree
|
||
hash_chainon (list1, list2)
|
||
tree list1, list2;
|
||
{
|
||
if (list2 == 0)
|
||
return list1;
|
||
if (list1 == 0)
|
||
return list2;
|
||
if (TREE_CHAIN (list1) == NULL_TREE)
|
||
return hash_tree_chain (TREE_VALUE (list1), list2);
|
||
return hash_tree_chain (TREE_VALUE (list1),
|
||
hash_chainon (TREE_CHAIN (list1), list2));
|
||
}
|
||
|
||
tree
|
||
build_decl_list_1 (value)
|
||
tree value;
|
||
{
|
||
tree list = NULL_TREE;
|
||
|
||
if (TREE_CODE (value) == IDENTIFIER_NODE)
|
||
{
|
||
list = IDENTIFIER_AS_LIST (value);
|
||
if (list != NULL_TREE
|
||
&& (TREE_CODE (list) != TREE_LIST
|
||
|| TREE_VALUE (list) != value))
|
||
list = NULL_TREE;
|
||
else if (TREE_TYPE (value) != NULL_TREE
|
||
&& TREE_CODE (TREE_TYPE (TREE_TYPE (value))) == RECORD_TYPE)
|
||
{
|
||
tree type = TREE_TYPE (TREE_TYPE (value));
|
||
if (CLASSTYPE_AS_ID_LIST (type) == NULL_TREE)
|
||
CLASSTYPE_AS_ID_LIST (type) = perm_tree_cons (NULL_TREE, value, NULL_TREE);
|
||
list = CLASSTYPE_AS_ID_LIST (type);
|
||
}
|
||
}
|
||
else if (TREE_CODE (value) == RECORD_TYPE
|
||
&& TYPE_LANG_SPECIFIC (value))
|
||
list = CLASSTYPE_AS_LIST (value);
|
||
|
||
if (list != NULL_TREE)
|
||
{
|
||
assert (TREE_CHAIN (list) == NULL_TREE);
|
||
return list;
|
||
}
|
||
|
||
return build_decl_list (NULL_TREE, value);
|
||
}
|
||
|
||
/* Look in the type hash table for a type isomorphic to
|
||
`build_tree_list (NULL_TREE, VALUE)'.
|
||
If one is found, return it. Otherwise return 0. */
|
||
|
||
tree
|
||
list_hash_lookup_or_cons (value)
|
||
tree value;
|
||
{
|
||
register int hashcode = TYPE_HASH (value);
|
||
register struct list_hash *h;
|
||
struct obstack *ambient_obstack;
|
||
tree list = NULL_TREE;
|
||
|
||
if (TREE_CODE (value) == IDENTIFIER_NODE)
|
||
{
|
||
list = IDENTIFIER_AS_LIST (value);
|
||
if (list != NULL_TREE
|
||
&& (TREE_CODE (list) != TREE_LIST
|
||
|| TREE_VALUE (list) != value))
|
||
list = NULL_TREE;
|
||
else if (TREE_TYPE (value) != NULL_TREE
|
||
&& TREE_CODE (TREE_TYPE (TREE_TYPE (value))) == RECORD_TYPE)
|
||
{
|
||
tree type = TREE_TYPE (TREE_TYPE (value));
|
||
if (CLASSTYPE_AS_ID_LIST (type) == NULL_TREE)
|
||
CLASSTYPE_AS_ID_LIST (type) = perm_tree_cons (NULL_TREE, value, NULL_TREE);
|
||
list = CLASSTYPE_AS_ID_LIST (type);
|
||
}
|
||
}
|
||
else if (TREE_CODE (value) == TYPE_DECL
|
||
&& TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE
|
||
&& TYPE_LANG_SPECIFIC (TREE_TYPE (value)))
|
||
list = CLASSTYPE_AS_ID_LIST (TREE_TYPE (value));
|
||
else if (TREE_CODE (value) == RECORD_TYPE
|
||
&& TYPE_LANG_SPECIFIC (value))
|
||
list = CLASSTYPE_AS_LIST (value);
|
||
|
||
if (list != NULL_TREE)
|
||
{
|
||
assert (TREE_CHAIN (list) == NULL_TREE);
|
||
return list;
|
||
}
|
||
|
||
if (debug_no_list_hash)
|
||
return hash_tree_chain (value, NULL_TREE);
|
||
|
||
for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
|
||
if (h->hashcode == hashcode
|
||
&& TREE_VIA_VIRTUAL (h->list) == 0
|
||
&& TREE_VIA_PUBLIC (h->list) == 0
|
||
&& TREE_PURPOSE (h->list) == 0
|
||
&& TREE_VALUE (h->list) == value)
|
||
{
|
||
assert (TREE_TYPE (h->list) == 0);
|
||
assert (TREE_CHAIN (h->list) == 0);
|
||
return h->list;
|
||
}
|
||
|
||
ambient_obstack = current_obstack;
|
||
current_obstack = &class_obstack;
|
||
list = build_tree_list (NULL_TREE, value);
|
||
list_hash_add (hashcode, list);
|
||
current_obstack = ambient_obstack;
|
||
return list;
|
||
}
|
||
|
||
/* Build an association between TYPE and some parameters:
|
||
|
||
OFFSET is the offset added to `this' to convert it to a pointer
|
||
of type `TYPE *'
|
||
|
||
VTABLE is the virtual function table with which to initialize
|
||
sub-objects of type TYPE.
|
||
|
||
VIRTUALS are the virtual functions sitting in VTABLE.
|
||
|
||
CHAIN are more associations we must retain. */
|
||
|
||
tree
|
||
make_assoc (offset, type, vtable, virtuals, chain)
|
||
tree offset, type;
|
||
tree vtable, virtuals;
|
||
tree chain;
|
||
{
|
||
tree assoc = make_tree_vec (4);
|
||
|
||
TREE_TYPE (assoc) = type;
|
||
TREE_CHAIN (assoc) = chain;
|
||
if (chain)
|
||
TREE_USED (assoc) = TREE_USED (chain);
|
||
|
||
/* n.b.: TREE_VEC_ELT (assoc, 0) <=> TREE_VALUE (assoc). */
|
||
TREE_VEC_ELT (assoc, 0) = TYPE_MAIN_VARIANT (type);
|
||
TREE_VEC_ELT (assoc, 1) = offset;
|
||
TREE_VEC_ELT (assoc, 2) = vtable;
|
||
TREE_VEC_ELT (assoc, 3) = virtuals;
|
||
return assoc;
|
||
}
|
||
|
||
tree
|
||
copy_assoc (list)
|
||
tree list;
|
||
{
|
||
tree assoc = copy_list (list);
|
||
tree rval = assoc;
|
||
while (assoc)
|
||
{
|
||
TREE_USED (assoc) = 0;
|
||
assoc = TREE_CHAIN (assoc);
|
||
}
|
||
return rval;
|
||
}
|
||
|
||
tree
|
||
assoc_value (elem, type)
|
||
tree elem;
|
||
tree type;
|
||
{
|
||
tree assoc = CLASSTYPE_ASSOC (type);
|
||
tree rval = NULL_TREE;
|
||
|
||
/* Dispose quickly of degenerate case. */
|
||
if (elem == type)
|
||
return assoc;
|
||
|
||
while (assoc)
|
||
{
|
||
if (elem == ASSOC_VALUE (assoc))
|
||
/* If we find it on the main spine, then
|
||
there can be no ambiguity. */
|
||
return assoc;
|
||
|
||
if (ASSOC_VALUE (assoc) != type)
|
||
{
|
||
tree nval = assoc_value (elem, ASSOC_TYPE (assoc));
|
||
|
||
if (nval)
|
||
if (rval && ASSOC_TYPE (rval) != ASSOC_TYPE (nval))
|
||
/* If we find it underneath, we must make sure that
|
||
there are no two ways to do it. */
|
||
compiler_error ("base class `%s' ambiguous in assoc_value",
|
||
TYPE_NAME_STRING (elem));
|
||
else
|
||
rval = nval;
|
||
}
|
||
assoc = TREE_CHAIN (assoc);
|
||
}
|
||
return rval;
|
||
}
|
||
|
||
tree
|
||
virtual_member (elem, list)
|
||
tree elem;
|
||
tree list;
|
||
{
|
||
tree t;
|
||
tree rval, nval;
|
||
|
||
for (t = list; t; t = TREE_CHAIN (t))
|
||
if (elem == TREE_VALUE (t))
|
||
return t;
|
||
rval = 0;
|
||
for (t = list; t; t = TREE_CHAIN (t))
|
||
{
|
||
int i;
|
||
for (i = CLASSTYPE_N_BASECLASSES (TREE_TYPE (t)); i > 0; i--)
|
||
{
|
||
nval = assoc_value (elem, CLASSTYPE_BASECLASS (TREE_TYPE (t), i));
|
||
if (nval)
|
||
{
|
||
if (rval && TREE_TYPE (nval) != TREE_TYPE (rval))
|
||
abort ();
|
||
rval = nval;
|
||
}
|
||
}
|
||
}
|
||
return rval;
|
||
}
|
||
|
||
void
|
||
debug_dump_assoc (elem)
|
||
tree elem;
|
||
{
|
||
int i;
|
||
tree virtuals;
|
||
|
||
fprintf (stderr, "type \"%s\"; offset = %d\n",
|
||
IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (ASSOC_VALUE (elem)))),
|
||
TREE_INT_CST_LOW (ASSOC_OFFSET (elem)));
|
||
fprintf (stderr, "vtable type:\n");
|
||
dump_tree (stderr, ASSOC_TYPE (elem));
|
||
if (ASSOC_VTABLE (elem))
|
||
fprintf (stderr, "vtable decl \"%s\"\n", IDENTIFIER_POINTER (DECL_NAME (ASSOC_VTABLE (elem))));
|
||
else
|
||
fprintf (stderr, "no vtable decl yet\n");
|
||
fprintf (stderr, "virtuals:\n");
|
||
virtuals = ASSOC_VIRTUALS (elem);
|
||
if (virtuals != 0)
|
||
virtuals = TREE_CHAIN (virtuals);
|
||
i = 1;
|
||
while (virtuals)
|
||
{
|
||
tree fndecl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals)), 0);
|
||
fprintf (stderr, "%s [%d =? %d]\n",
|
||
IDENTIFIER_POINTER (DECL_NAME (fndecl)),
|
||
i, TREE_INT_CST_LOW (DECL_VINDEX (fndecl)));
|
||
virtuals = TREE_CHAIN (virtuals);
|
||
i += 1;
|
||
}
|
||
}
|
||
|
||
char *
|
||
lang_printable_name (decl)
|
||
tree decl;
|
||
{
|
||
if (TREE_CODE (decl) != FUNCTION_DECL
|
||
|| DECL_LANG_SPECIFIC (decl) == 0)
|
||
{
|
||
if (DECL_NAME (decl))
|
||
{
|
||
if (THIS_NAME_P (DECL_NAME (decl)))
|
||
return "this";
|
||
return IDENTIFIER_POINTER (DECL_NAME (decl));
|
||
}
|
||
return "((anonymous))";
|
||
}
|
||
if (DECL_PRINT_NAME (decl) == 0)
|
||
{
|
||
int print_ret_type_p
|
||
= (!DECL_CONSTRUCTOR_P (decl)
|
||
&& !DESTRUCTOR_NAME_P (DECL_NAME (decl)));
|
||
int temp = allocation_temporary_p ();
|
||
char *buf = (char *)alloca (8192);
|
||
char *name = (char *)fndecl_as_string (buf, 0, decl, print_ret_type_p);
|
||
end_temporary_allocation ();
|
||
DECL_PRINT_NAME (decl) = oballoc (strlen (name) + 1);
|
||
strcpy (DECL_PRINT_NAME (decl), name);
|
||
if (temp)
|
||
resume_temporary_allocation ();
|
||
}
|
||
else if (DECL_NAME (decl) == 0)
|
||
DECL_PRINT_NAME (decl) = "((anonymous))";
|
||
|
||
return DECL_PRINT_NAME (decl);
|
||
}
|
||
|
||
/* Return truthvalue about whether debugger should
|
||
output full info about this type or not.
|
||
|
||
Current strategy is to permit types which define
|
||
no member functions to be output normally. For
|
||
those which do define member functions, if no
|
||
member functions have yet been output, then don't
|
||
output the definition of the type. If member functions
|
||
for the type are later seen, a full definition of the
|
||
type will eventually be output. */
|
||
int
|
||
lang_output_debug_info (type)
|
||
tree type;
|
||
{
|
||
extern tree pending_vtables;
|
||
|
||
if (! IS_AGGR_TYPE (type))
|
||
return 1;
|
||
if (TYPE_LANG_SPECIFIC (type) == 0)
|
||
return 1;
|
||
if (CLASSTYPE_METHOD_VEC (type) == 0)
|
||
return 1;
|
||
|
||
if (flag_minimal_debug)
|
||
{
|
||
/* Don't output full info about any type
|
||
which does not have its implementation defined here. */
|
||
if (TYPE_VIRTUAL_P (type) && write_virtuals == 2)
|
||
return value_member (DECL_NAME (TYPE_NAME (type)), pending_vtables) != 0;
|
||
if (CLASSTYPE_INTERFACE_ONLY (type))
|
||
return 0;
|
||
|
||
return CLASSTYPE_ASM_WRITTEN (type);
|
||
}
|
||
else
|
||
/* Can't work until GDB is modified. */
|
||
return 1;
|
||
}
|
||
|
||
/* Comparison function for sorting identifiers in RAISES lists.
|
||
Note that because IDENTIFIER_NODEs are unique, we can sort
|
||
them by address, saving an indirection. */
|
||
static int
|
||
id_cmp (p1, p2)
|
||
int *p1, *p2;
|
||
{
|
||
return *p1 - *p2;
|
||
}
|
||
|
||
/* Build the FUNCTION_TYPE or METHOD_TYPE which may raise exceptions
|
||
listed in RAISES. */
|
||
tree
|
||
build_exception_variant (ctype, type, raises)
|
||
tree ctype, type;
|
||
tree raises;
|
||
{
|
||
int i;
|
||
tree v = TYPE_MAIN_VARIANT (type);
|
||
tree t, t2, cname;
|
||
tree *a = (tree *)alloca ((list_length (raises)+1) * sizeof (tree));
|
||
int constp = TREE_READONLY (type);
|
||
int volatilep = TREE_VOLATILE (type);
|
||
|
||
if (raises && TREE_CHAIN (raises))
|
||
{
|
||
for (i = 0, t = raises; t; t = TREE_CHAIN (t), i++)
|
||
a[i] = t;
|
||
/* NULL terminator for list. */
|
||
a[i] = NULL_TREE;
|
||
qsort (a, i, sizeof (tree), id_cmp);
|
||
while (i--)
|
||
TREE_CHAIN (a[i]) = a[i+1];
|
||
raises = a[0];
|
||
}
|
||
else if (raises)
|
||
/* do nothing. */;
|
||
else
|
||
return build_type_variant (v, constp, volatilep);
|
||
|
||
if (ctype)
|
||
{
|
||
cname = TYPE_NAME (ctype);
|
||
if (TREE_CODE (cname) == TYPE_DECL)
|
||
cname = DECL_NAME (cname);
|
||
}
|
||
else
|
||
cname = NULL_TREE;
|
||
|
||
for (t = raises; t; t = TREE_CHAIN (t))
|
||
{
|
||
/* See that all the exceptions we are thinking about
|
||
raising have been declared. */
|
||
tree this_cname = lookup_exception_cname (ctype, cname, t);
|
||
tree decl = lookup_exception_object (this_cname, TREE_VALUE (t), 1);
|
||
|
||
if (decl == NULL_TREE)
|
||
decl = lookup_exception_object (this_cname, TREE_VALUE (t), 0);
|
||
/* Place canonical exception decl into TREE_TYPE of RAISES list. */
|
||
TREE_TYPE (t) = decl;
|
||
}
|
||
|
||
for (v = TYPE_NEXT_VARIANT (v); v; v = TYPE_NEXT_VARIANT (v))
|
||
{
|
||
if (TREE_READONLY (v) != constp
|
||
|| TREE_VOLATILE (v) != volatilep)
|
||
continue;
|
||
|
||
t = raises;
|
||
t2 = TYPE_RAISES_EXCEPTIONS (v);
|
||
while (t && t2)
|
||
{
|
||
if (TREE_TYPE (t) == TREE_TYPE (t2))
|
||
{
|
||
t = TREE_CHAIN (t);
|
||
t2 = TREE_CHAIN (t2);
|
||
}
|
||
else break;
|
||
}
|
||
if (t || t2)
|
||
continue;
|
||
/* List of exceptions raised matches previously found list.
|
||
|
||
@@ Nice to free up storage used in consing up the
|
||
@@ list of exceptions raised. */
|
||
return v;
|
||
}
|
||
|
||
/* Need to build a new variant. */
|
||
v = copy_node (type);
|
||
TYPE_NEXT_VARIANT (v) = TYPE_NEXT_VARIANT (type);
|
||
TYPE_NEXT_VARIANT (type) = v;
|
||
if (raises && ! TREE_PERMANENT (raises))
|
||
{
|
||
int temporary = allocation_temporary_p ();
|
||
if (temporary)
|
||
end_temporary_allocation ();
|
||
raises = copy_list (raises);
|
||
if (temporary)
|
||
resume_temporary_allocation ();
|
||
}
|
||
TYPE_RAISES_EXCEPTIONS (v) = raises;
|
||
return v;
|
||
}
|
||
|
||
/* Subroutine of make_permanent_node.
|
||
|
||
Assuming T is a node build bottom-up, make it all exist on
|
||
permanent obstack, if it is not permanent already. */
|
||
static tree
|
||
make_deep_copy (t)
|
||
tree t;
|
||
{
|
||
enum tree_code code;
|
||
|
||
if (t == NULL_TREE || TREE_PERMANENT (t))
|
||
return t;
|
||
|
||
switch (code = TREE_CODE (t))
|
||
{
|
||
case ERROR_MARK:
|
||
return error_mark_node;
|
||
|
||
case VAR_DECL:
|
||
case PARM_DECL:
|
||
case FUNCTION_DECL:
|
||
case CONST_DECL:
|
||
break;
|
||
|
||
case TREE_LIST:
|
||
{
|
||
tree chain = TREE_CHAIN (t);
|
||
t = copy_node (t);
|
||
TREE_PURPOSE (t) = make_deep_copy (TREE_PURPOSE (t));
|
||
TREE_VALUE (t) = make_deep_copy (TREE_VALUE (t));
|
||
TREE_CHAIN (t) = make_deep_copy (chain);
|
||
return t;
|
||
}
|
||
|
||
case TREE_VEC:
|
||
{
|
||
int len = TREE_VEC_LENGTH (t);
|
||
|
||
t = copy_node (t);
|
||
while (len--)
|
||
TREE_VEC_ELT (t, len) = make_deep_copy (TREE_VEC_ELT (t, len));
|
||
return t;
|
||
}
|
||
|
||
case INTEGER_CST:
|
||
case REAL_CST:
|
||
case STRING_CST:
|
||
return copy_node (t);
|
||
|
||
case COND_EXPR:
|
||
case NEW_EXPR:
|
||
t = copy_node (t);
|
||
TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
|
||
TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
|
||
TREE_OPERAND (t, 2) = make_deep_copy (TREE_OPERAND (t, 2));
|
||
return t;
|
||
|
||
case SAVE_EXPR:
|
||
t = copy_node (t);
|
||
TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
|
||
return t;
|
||
|
||
case MODIFY_EXPR:
|
||
case PLUS_EXPR:
|
||
case MINUS_EXPR:
|
||
case MULT_EXPR:
|
||
case TRUNC_DIV_EXPR:
|
||
case TRUNC_MOD_EXPR:
|
||
case MIN_EXPR:
|
||
case MAX_EXPR:
|
||
case LSHIFT_EXPR:
|
||
case RSHIFT_EXPR:
|
||
case BIT_IOR_EXPR:
|
||
case BIT_XOR_EXPR:
|
||
case BIT_AND_EXPR:
|
||
case BIT_ANDTC_EXPR:
|
||
case TRUTH_ANDIF_EXPR:
|
||
case TRUTH_ORIF_EXPR:
|
||
case LT_EXPR:
|
||
case LE_EXPR:
|
||
case GT_EXPR:
|
||
case GE_EXPR:
|
||
case EQ_EXPR:
|
||
case NE_EXPR:
|
||
case CEIL_DIV_EXPR:
|
||
case FLOOR_DIV_EXPR:
|
||
case ROUND_DIV_EXPR:
|
||
case CEIL_MOD_EXPR:
|
||
case FLOOR_MOD_EXPR:
|
||
case ROUND_MOD_EXPR:
|
||
case COMPOUND_EXPR:
|
||
case PREDECREMENT_EXPR:
|
||
case PREINCREMENT_EXPR:
|
||
case POSTDECREMENT_EXPR:
|
||
case POSTINCREMENT_EXPR:
|
||
case CALL_EXPR:
|
||
t = copy_node (t);
|
||
TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
|
||
TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
|
||
return t;
|
||
|
||
case CONVERT_EXPR:
|
||
case ADDR_EXPR:
|
||
case INDIRECT_REF:
|
||
case NEGATE_EXPR:
|
||
case BIT_NOT_EXPR:
|
||
case TRUTH_NOT_EXPR:
|
||
case NOP_EXPR:
|
||
case COMPONENT_REF:
|
||
t = copy_node (t);
|
||
TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
|
||
return t;
|
||
|
||
/* This list is incomplete, but should suffice for now.
|
||
It is very important that `sorry' does not call
|
||
`report_error_function'. That could cause an infinite loop. */
|
||
default:
|
||
sorry ("initializer contains unrecognized tree code");
|
||
return error_mark_node;
|
||
|
||
}
|
||
abort ();
|
||
}
|
||
|
||
/* Assuming T is a node built bottom-up, make it all exist on
|
||
permanent obstack, if it is not permanent already. */
|
||
tree
|
||
copy_to_permanent (t)
|
||
tree t;
|
||
{
|
||
register struct obstack *ambient_obstack = current_obstack;
|
||
register struct obstack *ambient_saveable_obstack = saveable_obstack;
|
||
|
||
if (t == NULL_TREE || TREE_PERMANENT (t))
|
||
return t;
|
||
|
||
saveable_obstack = &permanent_obstack;
|
||
current_obstack = saveable_obstack;
|
||
|
||
t = make_deep_copy (t);
|
||
|
||
current_obstack = ambient_obstack;
|
||
saveable_obstack = ambient_saveable_obstack;
|
||
|
||
return t;
|
||
}
|
||
|
||
int
|
||
lang_simple_cst_equal (t1, t2)
|
||
tree t1, t2;
|
||
{
|
||
register enum tree_code code1, code2;
|
||
int cmp;
|
||
|
||
if (t1 == t2)
|
||
return 1;
|
||
if (t1 == 0 || t2 == 0)
|
||
return 0;
|
||
|
||
code1 = TREE_CODE (t1);
|
||
code2 = TREE_CODE (t2);
|
||
|
||
switch (code1)
|
||
{
|
||
case CPLUS_NEW_EXPR:
|
||
cmp = simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
|
||
if (cmp <= 0)
|
||
return cmp;
|
||
return simple_cst_list_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1));
|
||
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|