8. Values, variables and assignments.
TAGs in TDF fulfil the role played by identifiers in most programming languages. One can apply obtain_tag to find the value bound to the TAG. This value is always a constant over the scope of a particular definition of the TAG. This may sound rather strange to those used to the concepts of left-hand and right-hand values in C, for example, but is quite easily explained as follows.
If a TAG, id, is introduced by an identify, then the value bound is fixed by its definition
argument. If, on the other hand, v was a TAG introduced by a variable definition, then the value bound to v is a pointer to fixed space in the procedure frame (i.e. the left-hand value in C).
8.1. contents
In order to get the contents of this space (the right-hand value in C), one must apply the contents operator to the pointer:
contents(shape(v), obtain_tag(v))
In general, the contents constructor takes a SHAPE and an expression delivering pointer:
s: SHAPE arg1: EXP POINTER(x) -> EXP s
It delivers the value of SHAPE s
, pointed at by the evaluation of arg1
. The alignment of s
need not be identical to x. It only needs to be included in it; this would allow one, for example, to pick out the first field of a structure from a pointer to it.
8.2. assign
A simple assignment in TDF is done using assign:
arg1: EXP POINTER(x) arg2: EXP y -> EXP TOP
The EXPs arg1
and arg2
are evaluated (no ordering implied) and the value of SHAPE y given by arg2
is put into the space pointed at by arg1
. Once again, the alignment of y need only be included in x, allowing the assignment to the first field of a structure using a pointer to the structure. An assignment has no obvious result so its SHAPE is TOP.
Some languages give results to assignments. For example, C defines the result of an assignment to be its right-hand expression, so that if the result of (v = exp) was required, it would probably be expressed as:
identify(empty, newtag, exp, sequence((assign(obtain_tag(v), obtain_tag(newtag))), obtain_tag(newtag)))
From the definition of assign, the destination argument, arg1
, must have a POINTER shape. This means that given the TAG id above, assign(obtain_tag(id), lhs) is only legal if the definition
of its identify had a POINTER SHAPE. A trivial example would be if id was defined:
identify(empty, id, obtain_tag(v), assign(obtain_tag(id), lhs))
This identifies id with the variable v which has a POINTER SHAPE, and assigns lhs to this pointer. Given that id does not occur in lhs, this is identical to:
assign(obtain_tag(v), lhs).
Equivalences like this are widely used for transforming TDF in translators and other tools (see section 11).
8.3. TRANSFER_MODE operations
The TRANSFER_MODE operations allow one to do assignment and contents operations with various qualifiers to control how the access is done in a more detailed manner than the standard contents and assign operations.
For example, the value assigned in assign has some fixed SHAPE; its size is known at translate-time. Variable sized objects can be moved by move_some:
md: TRANSFER_MODE arg1: EXP POINTER x arg2: EXP POINTER y arg3: EXP OFFSET(z, t) -> EXP TOP
The EXP arg1
is the destination pointer, and arg2
is a source pointer. The amount moved is given by the OFFSET arg3
.
The TRANSFER_MODE md
parameter controls the way that the move will be performed. If overlap is present, then the translator will ensure that the move is equivalent to moving the source into new space and then copying it to the destination; it would probably do this by choosing a good direction in which to step through the value. The alternative, standard_transfer_mode, indicates that it does not matter.
If the TRANSFER_MODE trap_on_nil is present and arg1
is a nil pointer, a TDF exception with ERROR_CODE nil_access is raised.
There are variants of both the contents and assign constructors. The signature of contents_with_mode is:
md: TRANSFER_MODE s: SHAPE arg1: EXP POINTER(x) -> EXP s
Here, the only significant TRANSFER_MODE constructors md
are trap_on_nil and volatile. The latter is principally intended to implement the C volatile construction; it certainly means that the contents_with_mode operation will never be “optimised” away.
Similar considerations apply to assign_with_mode; here the overlap TRANSFER_MODE is also possible with the same meaning as in move_some.
8.4. Assigning and extracting bitfields
Since pointers to bits are forbidden, two special operations are provided to extract and assign bitfields. These require the use of a pointer value and a bitfield offset from the pointer. The signature of bitfield_contents which extracts a bitfield in this manner is:
v: BITFIELD_VARIETY arg1: EXP POINTER(x) arg2: EXP OFFSET(y,z) -> EXP bitfield(v)
Here arg1
is a pointer to an alignment x
which includes v
, the required bitfield alignment. In practice, x
must include an INTEGER VARIETY whose representation can contain the entire bitfield. Thus on a standard architecture, if v is a 15 bit bitfield, x must include at least a 16 bit integer variety; a 27 bitfield would require a 32 bit integer variety and so on. Indeed the constraint is stronger than this since there must be an integer variety, accessible from arg1, which entirely contains the bitfield.
This constraint means that producers cannot expect that arbitrary bitfield lengths can be accomodated without extra padding; clearly it also means that the maximum bitfield length possible is the maximum size of integer variety that can be implemented on the translator concerned (this is defined to be at least 32). On standard architectures, the producer can expect that an array of bitfields of lenth 2n will be packed without padding; this, of course, includes arrays of booleans. For structures of several different bitfields, he can be sure of no extra padding bits if the total number of bits involved is less than or equal to 32; similarly if he can subdivide the bitfields so that each of the subdivisions (except the last) is exactly equal to 32 and the last is less than or equal to 32. This could be relaxed to 64 bits if the translator deals with 64 bit integer varieties, but would require that conditional TDF is produced to maintain portability to 32 bit platforms - and on these platforms the assurance of close packing would be lost.
Since a producer is ignorant of the exact representational varieties used by a translator, the onus is on the translator writer to provide standard tokens which can be used by a producer to achieve both optimum packing of bitfields and minimum alignments for COMPOUNDs containing them(see Bitfield offsets). These tokens would allow one to construct an offset of the form OFFSET(x, b) (where b is some bitfield alignment and x is the `minimum' alignment which could contain it) in a manner analogous to the normal padding operations for offsets. This offset could then used both in the construction of a compound shape and in the extraction and assignment constructors.
The assignment of bitfields follows the same pattern with the same constraints using bitfield_assign:
arg1: EXP POINTER(x) arg2: EXP OFFSET(y,z) arg3: EXP BITFIELD_VARIETY(v) -> EXP TOP