Blame | Last modification | View Log | RSS feed
<!-- Crown Copyright (c) 1998 -->
<HTML>
<HEAD>
<TITLE>Procedures and Locals</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000FF" VLINK="#400080" ALINK="#FF0000">
<A NAME=S37>
<H1>TDF Guide, Issue 4.0 </H1>
<H3>January 1998</H3>
<A HREF="guide8.html"><IMG SRC="../images/next.gif" ALT="next section">
</A> <A HREF="guide6.html">
<IMG SRC="../images/prev.gif" ALT="previous section"></A>
<A HREF="guide1.html"><IMG SRC="../images/top.gif" ALT="current document"></A>
<A HREF="../index.html"><IMG SRC="../images/home.gif" ALT="TenDRA home page">
</A>
<IMG SRC="../images/no_index.gif" ALT="document index"><P>
<HR>
<DL>
<DT><A HREF="#S38"><B>5.1 </B> - make_proc and apply_proc</A>
<DD>
<DT><A HREF="#S39"><B>5.1.1 </B> - vartag, varparam
</A><DD>
<DT><A HREF="#S40"><B>5.2 </B> - make_general_proc and apply_general_proc</A>
<DD>
<DT><A HREF="#S41"><B>5.2.1 </B> - tail_call</A><DD>
<DT><A HREF="#S42"><B>5.2.2 </B> - PROCPROPS</A><DD>
<DT><A HREF="#S43"><B>5.3 </B> - Defining and using locals</A><DD>
<DT><A HREF="#S44"><B>5.3.1 </B> - identify, variable</A><DD>
<DT><A HREF="#S45"><B>5.3.2 </B> - ACCESS</A><DD>
<DT><A HREF="#S46"><B>5.3.2.1 </B> - Locals model</A><DD>
<DT><A HREF="#S47"><B>5.3.2.2 </B> - Access "hints"
</A><DD>
<DT><A HREF="#S48"><B>5.3.3 </B> - current_env, env_offset</A>
<DD>
<DT><A HREF="#S49"><B>5.3.4 </B> - local_alloc, local_free_all, last_local</A>
<DD>
<DT><A HREF="#S50"><B>5.4 </B> - Heap storage</A><DD>
</DL>
<HR>
<H1>5 Procedures and Locals</H1>
All procedures in TDF are essentially global; the only values which
are accessible from the body of a procedure are those which are derived
from global TAGs (introduced by TAGDEFs or TAGDECs), local TAGs defined
within the procedure and parameter TAGs of the procedure<P>
All executable code in TDF will arise from an EXP PROC made by either
make_proc or make_general_proc. They differ in their treatment of
how space for the actual parameters of a call is managed; in particular,
is it the caller or the callee which deallocates the parameter space?<P>
With make_proc, this management is conceptually done by the caller
at an apply_proc; i.e. the normal C situation. This suffers from the
limitation that tail-calls of procedures are then only possible in
restricted circumstances (e.g. the space for the parameters of the
tail-call must be capable of being included in caller's parameters)
and could only be implemented as an optimisation within a translator.
A producer could not predict these circumstances in a machine independent
manner, whether or not it knew that a tail-call was valid.<P>
An alternative would be to make the management of parameter space
the responsibility of the called procedure. Rather than do this, make_general_proc
(and apply_general_proc) splits the parameters into two sets, one
whose allocation is the responsibility of the caller and the other
whose allocation is dealt with by the callee. This allows an explicit
tail_call to be made to a procedure with new callee parameters; the
caller parameters for the tail_call will be the same as (or some initial
subset of) the caller parameters of the procedure containing the tail_call
.<P>
A further refinement of make_general_proc is to allow access to the
caller parameter space in a postlude at the call of the procedure
using an apply_general_proc. This allows simple implementations of
Ada out_parameters, or more generally, multiple results of procedures.<P>
<A NAME=S38>
<HR><H2>5.1. make_proc and apply_proc</H2>
The make_proc constructor has signature:<P>
<PRE>
<I> result_shape</I>: SHAPE
<I> params_intro</I>: LIST(TAGSHACC)
<I> var_intro</I>: OPTION(TAGACC)
<I> body</I>: EXP BOTTOM
-> EXP PROC
</PRE>
The <I>params_intro</I> and <I>var_intro</I> parameters introduce
the formal parameters of the procedure which may be used in <I>body</I>.
The procedure result will have SHAPE <I>result_shape</I> and will
be usually given by some return construction within <I>body</I>. The
basic model is that space will be provided to copy actual parameters
(into space supplied by some apply_proc) by value into these formals
and the body will treat this space effectively as local variables.
<P>
Each straightforward formal parameter is introduced by an auxiliary
SORT TAGSHACC using make_tagshacc:<P>
<PRE>
<I>sha</I>: SHAPE
<I>opt_access</I>: OPTION(LIST(ACCESS))
<I>tg_intro</I>: TAG POINTER(alignment(<I>sha</I>))
-> TAGSHACC
</PRE>
<P>
Within <I>body</I>, the formal will be accessed using <I>tg_intro</I>;
it is always considered to be a pointer to the space of SHAPE <I>sha</I>
allocated by apply_proc, hence the pointer SHAPE. <P>
For example, if we had a simple procedure with one integer parameter,
<I>var_intro</I> would be empty and <I>params_intro</I> might be:<P>
<PRE>
<I>params_intro</I> = make_tagshacc<I>(</I> integer(v), empty, make_tag(13))
</PRE>
Then, TAG 13 from the enclosing UNIT's name-space is identified with
the formal parameter with SHAPE POINTER(INTEGER(v)). Any use of obtain_tag(make_tag(13))
in <I>body</I> will deliver a pointer to the integer parameter. I
shall return to the meaning of <I>opt_access</I> and the ramifications
of the scope and extent of TAGs involved in conjunction with local
declarations in <A HREF="#35">section 5.3.1 on page 30</A>.<P>
Procedures, whether defined by make_proc or make_general_proc, will
usually terminate and deliver its result with a return:<P>
<PRE>
<I> arg1</I>: EXP <I>x</I>
-> EXP BOTTOM
</PRE>
Here <I>x</I> must be identical to the <I>result_shape</I> of the
call of the procedure There may be several returns in body; and the
SHAPE <I>x</I> in each will be the same. Some languages allow different
types to be returned depending on the particular call. The producer
must resolve this issue. For example, C allows one to deliver void
if the resulting value is not used. In TDF a dummy value must be provided
at the return; for example make_value(<I>result_shape</I>)<P>
Note that the <I>body</I> has SHAPE bottom since all possible terminations
to a procedure have SHAPE BOTTOM..<P>
Procedures defined by make_proc are called using apply_proc:
<P>
<PRE>
<I> result_shape</I>: SHAPE
<I> arg1</I>: EXP PROC
<I> arg2</I>: LIST(EXP)
<I> varparam</I>: OPTION(EXP)
--> EXP <I>result_shape</I>
</PRE>
Here <I>arg1</I> is the procedure to be called and <I>arg2</I> gives
the actual parameters. There must be at least as many actual parameters
as given (with the same SHAPE) in the <I>params_intro</I> of the corresponding
make_proc for arg1
<A NAME=footnote74 HREF="footnote.html#74">*</A>. The values of <I>arg2</I>
will be copied into space managed by caller.
<P>
The SHAPE of the result of the call is given by <I>result_shape</I>
which must be identical to the <I>result_shape</I> of the make_proc.<P>
<A NAME=S39>
<H3>5.1.1. <I><A NAME=12>vartag</I>, <I>varparam
</I> </H3>
Use of the <I>var_intro</I> OPTION in make_proc and the corresponding
<I>varparam</I> in apply_proc allows one to have a parameter of any
SHAPE, possibly differing from call to call where the actual SHAPE
can be deduced in some way by the <I>body</I> of the make_proc . One
supplies an extra actual parameter, <I>varparam</I>, which usually
would be a structure grouping some set of values. The body of the
procedure can then access these values using the pointer given by
the TAG <I>var_intro</I>, using add_to_ptr with some computed offsets
to pick out the individual fields. <P>
This is a slightly different method of giving a variable number of
parameters to a procedure, rather than simply giving more actuals
than formals. The principle difference is in the alignment of the
components of <I>varparam</I>; these will be laid out according to
the default padding defined by the component shapes. In most ABIs,
this padding is usually different to the way parameters are laid out;
for example, character parameters are generally padded out to a full
word. Thus a sequence of parameters of given shape has a different
layout in store to the same sequence of shapes in a structure. If
one wished to pass an arbitrary structure to a procedure, one would
use the <I>varparam</I> option rather passing the fields individually
as extra actual parameters.<P>
<A NAME=S40>
<HR><H2>5.2. make_general_proc and apply_general_proc</H2>
A make_general_proc has signature:<P>
<PRE>
<I>result_shape</I>: SHAPE
<I>prcprops</I>: OPTION(PROCPROPS)
<I>caller_intro</I>: LIST(TAGSHACC)
<I>callee_intro</I>: LIST(TAGSHACC)
<I>body</I>: EXP BOTTOM
-> EXP PROC
</PRE>
Here the formal parameters are split into two sets, <I>caller_intro</I>
and <I>callee_intro</I>, each given by a list of TAGSHACCs just as
in make_proc. The distinction between the two sets is that the make_general_proc
is responsible for de_allocating any space required for the callee
parameter set; this really only becomes obvious at uses of tail_call
within <I>body.</I> <P>
The <I>result_shape</I> and <I>body</I> have the same general properties
as in make_proc. In addition <I>prcprops</I> gives other information
both about <I>body</I> and the way that that the procedure is called.
PROCPROPS are a set drawn from check_stack, inline, no_long_jump_dest,
untidy, var_callees and var_callers. The set is composed using add_procprops.
The PROCPROPS no_long_jump_dest is a property of <I>body</I> only;
it indicates that none of the labels within <I>body</I> will be the
target of a long_jump construct. The other properties should also
be given consistently at all calls of the procedure; theu are discussed
in <A HREF="#24">section 5.2.2 on page 29</A>.<P>
A procedure, <I>p</I>, constructed by make_general_proc is called
using apply_general_proc: <P>
<PRE>
<I>result_shape</I>: SHAPE
<I>prcprops</I>: OPTION(PROCPROPS)
<I>p</I>: EXP PROC
<I>caller_params</I>: LIST(OTAGEXP)
<I>callee_params</I>: CALLEES
<I>postlude</I>: EXP TOP
-> EXP <I>result_shape</I>
</PRE>
The actual caller parameters are given by <I>caller_params</I> as
a list of OTAGEXPs constructed using make_otagexp:<P>
<PRE>
<I>tgopt</I>: OPTION(TAG <I>x</I>)
<I>e</I>: EXP <I>x</I>
-> OTAGEXP
</PRE>
Here, <I>e</I> is the value of the parameter and <I>tgopt</I>, if
present, is a TAG which will bound to the final value of the parameter
(after <I>body</I> is evaluated) in the <I>postlude</I> expression
of the apply_general_proc
<A NAME=footnote75 HREF="footnote.html#75">*</A>. Clearly, this allows
one to use a caller parameter as an extra result of the procedure;
for example, as in Ada out-parameters.<P>
The actual <I>callee_params</I> may be constructed in three different
ways. The usual method is to use make_callee_list, giving a list of
actual EXP parameters, corresponding to the <I>caller_intro</I>
list in the obvious way.The constructor, same_callees allows one to
use the callees of the current procedure as the callees of the call;
this, of course, assumes that the formals of the current procedure
are compatible with the formals required for the call The final method
allows one to construct a dynamically sized set of CALLEES; make_dynamic_callees
takes a pointer and a size (expressed as an OFFSET) to make the CALLEES;
this will be used in conjunction with a var_callees PROCPROPS (see
<A HREF="#24">section 5.2.2 on page 29</A>).<P>
Some procedures can be expressed using either make_proc or make_general_proc.
For example:<P>
make_proc(S, L, empty, B) = make_general_proc(S, var_callers, L, empty,
B)<P>
<A NAME=S41>
<H3>5.2.1. tail_call</H3>
Often the result of a procedure, <I>f</I>, is simply given by the
call of another (or the same) procedure, <I>g</I>. In appropriate
circumstances, the same stack space can be used for the call of <I>g</I>
as the call of <I>f</I>. This can be particularly important where
heavily recursive routines are involved; some languages even use tail
recursion as the preferred method of looping.<P>
One condition for such a tail call to be applicable is knowing that
<I>g</I> does not require any pointers to locals of <I>f</I>; this
is often implicit in the language involved. Equally important is that
the action on the return from <I>f</I> is indistiguishable from the
return from <I>g</I>. For example, if it were the callers responsibility
to pop the the space for the parameters on return from a call, then
the tail call of <I>g</I> would only work if <I>g</I> had the same
parameter space as <I>f</I>.<P>
This is the justification for splitting the parameter set of a general
proc; it is (at least conceptually) the caller's responsibility for
popping the caller-parameters only - the callee-parameters are dealt
with by the procedure itself. Hence we can define tail_call which
uses the same caller-parameters, but a different set of callee-parameters:
<P>
<PRE>
<I>prcprops</I>: OPTION(PROCPROPS)
<I>p</I>: EXP PROC
<I>callee_params</I>: CALLEES
-> EXP BOTTOM
</PRE>
The procedure p will be called with the same caller parameters as
the current procedure and the new <I>callee_params</I> and return
to the call site of the current procedure. Semantically, if S is the
return SHAPE of the current procedure, and L is its caller-parameters:<P>
tail_call(P, p, C) = return(apply_general_proc(S, P, p, L, C, make_top()))<P>
However an implementation is expected to conserve stack by using the
same space for the call of p as the current procedure.<P>
<P>
<A NAME=S42>
<H3>5.2.2. <A NAME=24>PROCPROPS</H3>
The presence of var_callees (or var_callers) means that the procedure
can be called with more actual callee (or caller) parameters than
are indicated in <I>callee_intro</I> (or <I>caller_intro
</I>). These extra parameters would be accessed within body using
offset calculations with respect to the named parameters. The offsets
should be calculated using parameter_alignment to give the packing
of the parameter packs.<P>
The presence of untidy means that <I>body</I> may be terminated by
an untidy_return. This returns the result of the procedure as in return,
but the lifetime of the local space of the procedure is extended (in
practice this is performed by not returning the stack to its original
value at the call). A procedure containing an untidy_return is a generalisation
of a local_alloc(see
<A HREF="#60">section 5.3.4 on page 32</A>). For example the procedure
could do some complicated local allocation (a triangular array, say)
and untidily return a pointer to it so that the space is still valid
in the calling procedure. The space will remain valid for the lifetime
of the calling procedure unless some local_free is called within it,
just as if the space had been generated by a local_alloc in the calling
procedure.<P>
The presence of inline is just a hint to the translator that the procedure
body is a good candidate for inlining at the call.<P>
The presence of check_stack means that the static stack requirements
of the procedure will be checked on entry to see that they do not
exceed the limits imposed by set_stack_limit; if they are exceeded
a TDF exception with ERROR_CODE stack_overflow (see <A HREF="guide8.html#31">section
6.3 on page 35</A>) will be raised.<P>
<A NAME=S43>
<HR><H2>5.3. <A NAME=34>Defining and using locals</H2>
<A NAME=S44>
<H3>5.3.1. <A NAME=35>identify, variable</H3>
Local definitions within the <I>body</I> of a procedure are given
by two EXP constructors which permit one to give names to values over
a scope given by the definition. Note that this is somewhat different
to declarations in standard languages where the declaration is usually
embedded in a larger construct which defines the scope of the name;
here the scope is explicit in the definition. The reason for this
will become more obvious in the discussion of TDF transformations.
The simpler constructor is identify:<P>
<PRE>
<I> opt_access</I>: OPTION(ACCESS)
<I> name_intro</I>: TAG<I> x</I>
<I> definition</I>: EXP <I>x</I>
<I> body</I>: EXP <I>y</I>
-> EXP<I> y</I>
</PRE>
The <I>definition</I> is evaluated and its result is identified with
the TAG given by <I>name_intro</I> within its scope <I>body</I>. Hence
the use of any obtain_tag(<I>name_intro</I>) within
<I>body</I> is equivalent to using this result. Anywhere else, obtain_tag(<I>name_intro
</I>) is meaningless, including in other procedures.<P>
The other kind of local definition is variable:<P>
<PRE>
<I> opt_access</I>: OPTION(ACCESS)
<I> name_intro</I>: TAG<I> x</I>
<I> init</I>: EXP <I>x</I>
<I> body</I>: EXP <I>y</I>
-> EXP<I> y</I>
</PRE>
Here the <I>init</I> EXP is evaluated and its result serves as an
initialisation of space of SHAPE <I>x</I> local to the procedure.
The TAG name_intro is then identified with a pointer to that SPACE
within body. A use of obtain_tag(<I>name_intro</I>) within <I>body</I>
is equivalent to using this pointer and is meaningless outside <I>body</I>
or in other procedures. Many variable declarations in programs are
uninitialised; in this case, the <I>init</I> argument could be provided
by make_value which will produce some value with SHAPE given by its
parameter.<P>
<A NAME=S45>
<H3>5.3.2. <A NAME=40>ACCESS</H3>
The ACCESS SORT given in tag declarations is a way of describing a
list of properties to be associated with the tag. They are basically
divided into two classes, one which describes global properties of
the tag with respect to the model for locals and the other which gives
"hints" on how the value will be used. Any of these can
be combined using add_access.<P>
<A NAME=S46>
<H4>5.3.2.1 . Locals model</H4>
At the moment there are just three possibilities in the first class
of ACCESS constructors. They are standard_access (the default) , visible,
out_par and long_jump_access.
<P>
The basic model used for the locals and parameters of a procedure
is a frame within a stack of nested procedure calls. One could implement
a procedure by allocating space according to SHAPEs of all of the
parameter and local TAGs so that the corresponding values are at fixed
offsets either from the start of the frame or some pointer within
it.<P>
Indeed, if the ACCESS <I>opt_access </I>parameter in a TAG definition
is produced by visible, then a translator is almost bound to do just
that for that TAG. This is because it allows for the possibility of
the value to be accessed in some way other than by using obtain_tag
which is the standard way of recovering the value bound to the TAG.
The principal way that this could happen within TDF is by the combined
use of env_offset to give the offset and current_env to give a pointer
to the current frame (see <A HREF="#57">section 5.3.3 on page 31</A>).
<P>
The out_par ACCESS is only applicable to caller parameters of procedures;
it indicates that the value of the TAG concerned will accessed by
the postlude part of an apply_general_proc. Hence, the value of the
parameter must be accessible after the call; usually this will be
on the stack in the callers frame.<P>
The long_jump_access flag is used to indicate that the tag must be
available after a long_jump. In practice, if either visible or long_jump_access
is set, most translators would allocate the space for the declaration
on the main-store stack rather than in an available register. If it
is not set, then a translator is free to use its own criteria for
whether space which can fit into a register is allocated on the stack
or in a register, provided there is no observable difference (other
than time or program size) between the two possibilities.<P>
Some of these criteria are rather obvious; for example, if a pointer
to local variable is passed outside the procedure in an opaque manner,
then it is highly unlikely that one can allocate the variable in a
register. Some might be less obvious. If the only uses of a TAG t
was in obtain_tag(t)s which are operands of contents or the left-hand
operands of assigns , most ABIs would allow the tag to be placed in
a register. We do not necessarily have to generate a pointer value
if it can be subsumed by the operations available.<P>
<A NAME=S47>
<H4>5.3.2.2 . Access "hints"</H4>
A variable tag with ACCESS constant is a write-once value; once it
is initialised the variable will always contain the initialisation.
In other words the tag is a pointer to a constant value; translators
can use this information to apply various optimisations.<P>
A POINTER tag with ACCESS no_other_read or no_other_write is asserting
that there are no "aliassed" accesses to the contents of
the pointer. For example, when applied to a parameter of a procedure,
it is saying that the original pointer of the tag is distinct from
any other tags used (reading/writing) in the lifetime of the tag.
These other tags could either be further parameters of the procedure
or globals. Clearly, this is useful for describing the limitations
imposed by Fortran parameters, for example.<P>
<A NAME=S48>
<H3>5.3.3. <A NAME=57>current_env, env_offset</H3>
The constructor current_env gives a pointer to the current procedure
frame of SHAPE POINTER(<I>fa</I>) where <I>fa</I>
is depends on how the procedure was defined and will be some set of
the special frame ALIGNMENTs. This set will always include locals_alignment
- the alignment of any locals defined within the procedure. If the
procedure has any caller- parameters, the set will also include callers_alignment(b)
where b indicates whether there can be a variable number of them;
similarly for callee-parameters.<P>
Offsets from the current_env of a procedure to a tag declared in the
procedure are constructed by env_offset:<P>
<PRE>
<I>fa</I>: ALIGNMENT
<I>y</I>: ALIGNMENT
<I>t</I>: TAG <I>x</I>
-> EXP OFFSET(<I>fa</I>,<I>y</I>)
</PRE>
The frame ALIGNMENT <I>fa</I> will be the appropriate one for the
TAG <I>t</I>; i.e. if <I>t</I> is a local then the <I>fa</I> will
be locals_alignment; if <I>t</I> is a caller parameter, <I>fa</I>
will be callers_alignment(b); if <I>t</I> is a callee_parameter, <I>fa</I>
will be callees_alignment(b). The alignment <I>y</I> will be the alignment
of the initialisation of <I>t</I>.<P>
The offset arithmetic operations allow one to access the values of
tags non-locally using values derived from current_env and env_offset.
They are effectively defined by the following identities:<P>
<PRE>
If TAG t is derived from a variable definition
add_to_ptr(current_env(), env_offset(locals_alignment, A, t)) = obtain_tag(t)
if TAG t is derived from an identify definition:
contents(S, add_to_ptr(current_env(), env_offset(locals_alignment, A, t))) = obtain_tag(t)
if TAG t is derived from a caller parameter:
add_to_ptr(current_env(), env_offset(callers_alignment(b), A, t)) = obtain_tag(t)
if TAG t is derived from a callee parameter:
add_to_ptr(current_env(), env_offset(callees_alignment(b), A, t)) = obtain_tag(t)
</PRE>
These identities are valid throughout the extent of t, including in
inner procedure calls. In other words, one can dynamically create
a pointer to the value by composing current_env and env_offset. <P>
The importance of this is that env_offset(t) is a constant OFFSET
and can be used anywhere within the enclosing UNIT, in other procedures
or as part of constant TAGDEF; remember that the TDFINT underlying
t is unique within the UNIT. The result of a current_env could be
passed to another procedure (as a parameter, say) and this new procedure
could then access a local of the original by using its env_offset.
This would be the method one would use to access non-local, non-global
identifiers in a language which allowed one to define procedures within
procedures such as Pascal or Algol. Of course, given the stack-based
model, the value given by current_env becomes meaningless once the
procedure in which it is invoked is exited.<P>
<P>
<A NAME=S49>
<H3>5.3.4. <A NAME=60>local_alloc, local_free_all, last_local</H3>
The size of stack frame produced by variable and identify definitions
is a translate-time constant since the frame is composed of values
whose SHAPEs are known. TDF also allows one to produce dynamically
sized local objects which are conceptually part of the frame. These
are produced by local_alloc:<P>
<PRE>
<I> arg1</I>: EXP OFFSET(<I>x, y</I>)
-> EXP POINTER(alloca_alignment)
</PRE>
The operand <I>arg1</I> gives the size of the new object required
and the result is a pointer to the space for this object "on
top of the stack" as part of the frame. The quotation marks indicate
that a translator writer might prefer to maintain a dynamic stack
as well as static one. There are some disadvantages in putting everything
into one stack which may well out-weigh the trouble of maintaining
another stack which is relatively infrequently used. If a frame has
a known size, then all addressing of locals can be done using a stack-front
register; if it is dynamically sized, then another frame-pointer register
must be used - some ABIs make this easy but not all. The majority
of procedures contain no local_allocs, so their addressing of locals
can always be done relative to a stack-front; only the others have
to use another register for a frame pointer.<P>
The alignment of pointer result is alloca_alignment which must include
all SHAPE alignments.<P>
There are two constructors for releasing space generated by local_alloc.
To release all such space generated in the current procedure one does
local_free_all(); this reduces the size of the current frame to its
static size. <P>
The other constructor is local_free whch is effectively a "pop"
to local_alloc's "push":<P>
<PRE>
a: EXP OFFSET(<I>x</I>, <I>y</I>)
<I> p</I>: EXP POINTER(alloca_alignment)
-> EXP TOP
</PRE>
Here <I>p</I> must evaluate to a pointer generated either by local_alloc
or last_local . The effect is to free all of the space locally allocated
after p. The usual implementation (with a downward growing stack)
of this is that p becomes the "top of stack" pointer<P>
The use of a procedure with an untidy_return is just a generalisation
of the idea of local_alloc and the space made available by its use
can be freed in the same way as normal local allocations. Of course,
given that it could be the result of the procedure it can be structured
in an arbitrarily complicated way.<P>
<A NAME=S50>
<HR><H2>5.4. Heap storage</H2>
At the moment, there are no explicit constructors of creating dynamic
off-stack storage in TDF. Any off-stack storage requirements must
be met by the API in which the system is embedded, using the standard
procedural interface. For example, the ANSI C API allows the creation
of heap space using standard library procedures like malloc.<P>
<HR>
<P><I>Part of the <A HREF="../index.html">TenDRA Web</A>.<BR>Crown
Copyright © 1998.</I></P>
</BODY>
</HTML>