The base type_node
class defines some common features of all
types. This section describes the methods from this base class along
with some comments about conventions for using types. Besides the
things covered here, each type_node
contains an ID number that
uniquely identifies it within a particular context. See section Numbering Types and Symbols.
New types can be created as necessary, but once a type has been entered
in a symbol table it should generally not be changed. This helps to
avoid duplicate types and to prevent unintended side effects when a type
is used in multiple places. Instead, the copy
method can be used
to get a new type that is the same as the existing type, except that it
does not copy annotations. The copy can then be freely modified and
installed in a symbol table. If necessary, annotations on the type can
be copied separately using the copy_annotes
method. See section SUIF Objects.
When a type is entered in a symbol table, it automatically records a
pointer to that parent table. Similarly, when the type is removed from
the symbol table, its parent pointer is cleared. The parent
method retrieves this parent pointer.
Each type has a size
method that returns the number of bits used
to store values of that type. (That is not very meaningful for function
types, so they always have a fixed size of zero.) The size for a type
may or may not be set directly, depending on the type operator. The
sizes for type nodes from each of the derived classes are described in
the appropriate sections below.
Structures, unions, and enumerated types are all given names. The
is_named
method checks to see if a type_node
is one of
these types. The name of a type should be unique within the symbol
table where it is defined, but the type name space is separate from the
names for symbols. Because the type names are automatically entered in
the lexicon (see section Lexicon) when the types are created, they can be
compared as pointers without doing string comparisons.
More than one type_node
can represent the same type. That is,
types may be equivalent even if they are represented by different type
nodes. Given that, the only reason to reuse existing types is to keep
the symbol tables from getting too big. The is_same
method is
provided to check if two types are equivalent, so that the symbol tables
can be kept to a reasonable size. However, is_same
is only
intended to help get rid of duplicates. Because it assumes that
duplicate types are still perfectly legal, it may potentially return
false negatives to avoid the expense of comparing annotations on type
nodes (5). For named types, the is_same
method assumes that all
type nodes are unique; it does not check for structural equivalence. In
most cases, is_same
need not be called directly. Instead, the
symbol table install_type
method (see section Adding and Removing Entries), which uses is_same
to detect and avoid duplicate
types, is the recommended way to add new types to a symbol table.
SUIF has its own definition of type compatibility. Two types do not need to be strictly equivalent to be compatible. Besides the rules for type equivalence, the following conditions define which types are compatible:
The compatible
method is included in the type_node
class
to determine if two types are compatible according to these rules.
There are three different methods for printing SUIF types. The
print_abbrev
method is used when printing the result types of
instructions. It prints the type ID number along with a single
character to identify the type operator followed by a period and the
size (e.g. `i.32' for a 32-bit integer). The print
method
shows the ID number prefixed with `t:' to identify it as a type.
The print_full
method prints all of the type information and is
used when listing symbol tables. It's optional depth
parameter
can be used to specify the indentation level.
Many kinds of type nodes contain fields that refer to other types. Moreover, each type may contain annotations that include references to other types. With one exception, however, recursive type references are not allowed. In other words, if the type nodes are viewed as a directed graph with the references between them forming the edges, there can be no cycles. To support recursive data structures, we allow an exception to this rule: types within a field of a structure or union may refer back to the structure or union type.