the builtin.module op behaves exactly like any other op, and is designed
to be a container for other ops of any type. it can be used as the
top-level container for other ops (other top-level containers can be used
instead).
this also changes the file format. now, an ir file can only contain a
single top-level op. any other ops in the ir must be contained
directly or indirectly by this top-level op.
the symbol system will allow ops to reference each other via symbolic
names, rather than just via their SSA output registers. this will
allow for links between ops that are less strict than SSA.
the two parts of the system are:
- the symbol-table trait. ops with this trait can contain symbol ops.
- the symbol interface. ops with this interface have the necessary attributes
to be treated as symbols.
this system allows dialects to register named interfaces, which are collections of function
pointers. an op can then implement this interface, providing callbacks for the interface's
virtual function pointers.
C code can request a pointer to an op's implementation of a given interface and call
virtual functions with no knowledge required about the op itself. this functonality
will also be extended to types, attributes, and dialects themselves.
under this new system, dialects can define their own custom attributes,
complete with their own print() and parse() callbacks, which can then be
used as values in an op's attribute dictionary.
alongside custom dialect attributes, the former int, float, and string
constant values have been converted to attributes provided by the
arith and builtin dialects respectively. the caches for these attributes
have also been moved from mie_ctx to their respective dialect data
structures.
this system will allow new types of attributes to be implemented,
including dictionaries, arrays, and references to types themselves
(rather than just particular values of a given type).
any struct that contains a mie_name cannot be stored in movable memory
(i.e. any memory that may be re-allocated using realloc(), or whose
contents may be moved to a different buffer).
mie_names form part of a bst when they are added to a mie_name_map,
and moving them after this happens will result in the bst pointers
being invalidated. this causes some obscure and hard-to-debug
memory errors.
all structs that contain a mie_name (including named IR objects like
mie_register and mie_block) are no longer stored directly in vectors.
rather, vectors of pointers are used instead.
unlike values, type-instances represent specialisations of parametised types.
they behave like concrete implementations of C++ template types.
for example: arith.int represents an integer type of unspecified bit-width.
a type-instance of this type would be arith.int<32> (or i32 for short) which
has a defined width of 32 bits.