[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