[fpc-devel] Local procedures as procedural parameter
Adriaan van Os
fpc at microbizz.nl
Mon Mar 14 01:46:52 CET 2005
1. On the todo list for fpc's MacPas mode is a feature that allows
local procedures (functions) to be passed a procedural parameters. I
tentatively put it on my own todo list, since this is the feature that
I like most in Pascal compilers, at the same time making a small
contribution to fpc and learning about the compiler. Before doing so, I
need agreement on the method followed.
2. The problem with local procedures is that there must be a mechanism
to address parameters and local variables of parent procedures. In
Section 12.2 of "Grundlagen und Techniken des Compilerbaus" Niklaus
Wirth points out we can do that with a static link chain pointer to the
stack frame of the parent procedure(s). It is important to note that
the static link chain can differ from the dynamic link chain because
the latter corresponds to the calling chain and that calling chain can
differ from the static parent-sub-procedure relation, i.e. if there is
more than one local procedure at the same level.
3. FPC passes the static link chain frame pointer as an extra invisible
parameter to local procedures (Section 6.4 of the fpc Programmer’s
Manual).
4. So, when passing a local procedure (function) as procedural
parameter, the static link chain frame pointer must be passed along
with the entry point of the local procedure. However, this raises
concerns about calling conventions and compatibility with existing code
and the current compiler.
5. To solve this dilemma, gnu C, Pascal and Ada (and also MetroWerks
CodeWarrior Pascal) use a technique called trampolines. The idea is
outlined in <http://people.debian.org/~aaronl/Usenix88-lexic.pdf> and
the gnu implementation is discussed at
<http://gcc.gnu.org/onlinedocs/gccint/Trampolines.html>. In short, a
transition vector is set up temporarily for the local procedure that
contains code from which the static link frame pointer can be obtained.
6. Although conventional wisdom dictates that we always follow the
latest fashion in programming without further thinking, I believe that
with some analysis we can do better than trampolines. There are two
strong arguments that speak against the use of trampolines:
(a) trampolines must be setup at runtime and this involves flushing the
instruction/data cache of the processor, a grave (and stupid) slowdown
(b) trampolines are very tricky to implement and quite
platform-dependent (see e.g.
<http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10900>).
7. Consider the following program:
program func;
type tfun = function( x: real): real;
procedure iso_fun( function f( x: real): real);
begin
end;
procedure typ_fun( pf: tfun);
begin
end;
procedure somefun;
function f( x: real): real;
begin
f:= x
end;
begin
iso_fun( f);
typ_fun( f); {procedural variable can't get nested routiine}
end;
begin
end.
MetroWerks CodeWarrior Pascal allows passing local procedures
(functions) as procedural parameter, but there is an interesting point.
It allows passing the local function f to iso_fun, but it doesn't allow
passing it to typ_fun. The reason is clear - the value of the static
link chain frame pointer is valid only within a specific activation of
the parent procedure. So, it should not be allowed that the procedural
parameter f somehow propagates out of the surrounding parent procedure
and this can be guaranteed only if assignment to a typed procedural
variable or (typed procedural value parameter) is forbidden.
So, we need to allow local procedures as actual parameters only for the
"iso" notation. So, I suggest to change "iso" style procedural
parameters only, i.e.
(a) change them from a pointer to a record: the record has two pointer
fields, one for the routine's entry point and one for the optional
static link chain frame pointer, or
(b) pass the static link chain frame pointer as an extra invisible
parameter for each "iso" style procedural parameter.
Something similar has been implemented in fpc already, namely for
methods that are passed as parameter. Here, an extra "self" pointer is
needed.
The effect on current software should be minimal, because (currently)
only MacPas mode allows "iso" style procedural parameters. This
eliminates any concerns about calling conventions and compatibility
with existing code and the current compiler.
Note that, with this solution, typed procedural variables can be passed
as actual procedural parameters to "iso" style formal parameters, but
not the other way round.
9. Conclusion.
With the above method, it should be relatively simple to implement
local procedures (functions) as procedural parameters, without
affecting the code generation of the current compiler.
Regards,
Adriaan van Os
More information about the fpc-devel
mailing list