[fpc-devel] Possible idea... "safe" subroutines/methods

Jonas Maebe jonas at freepascal.org
Sat May 4 10:37:02 CEST 2019


On 2019-05-03 19:37, J. Gareth Moreton wrote:
> By telling the compiler that the procedure (or maybe a whole class) is
> thread-safe, you are telling it that you can guarantee that any
> objects, fields or global variables that you access are guaranteed to
> not suddenly change mid-routine (because another thread has modified
> it).  This would allow the compiler to move commonly-accessed fields
> into local registers or the stack for faster access, especially if the
> fields are only read and not written, since they'll be guaranteed to
> contain a constant value.

Multi-threading is not the main issue. The main problems are aliasing 
and subroutine calls:

1) Aliasing

type
   tc = class
     a: longint;
     procedure test(var l: longint);
   end;

procedure tc.test(var l: longint);
begin
   if a<>5 then
     begin
       l:=1;
       // the above will change c.a to 1, but if c.a is in a register 
that will not be detected
       if a<>1 then
         writeln('error');
     end;
end;

var
   c: tc;
begin
   c:=tc.create;
   c.a:=6;
   c.test(c.a);
   c.free;
end.


2) subroutine calls

type
   tc = class
     a: longint;
     procedure test;
   end;

var
   c: tc;

procedure change;
begin
   c.a:=1;
end;

procedure tc.test;
begin
   if a<>5 then
     begin
       change;
       if a<>1 then
         writeln('error');
     end;
end;

begin
   c:=tc.create;
   c.a:=6;
   c.test;
   c.free;
end.

In both cases, many additional scenarios are possible (there are many 
different way to alias memory and to perform modifications in subroutine 
calls).

For the former, you need inter-procedural alias analysis, or limit 
yourself to routines that only write to local variables. For the latter, 
you need to limit yourself to routines that don't call other routines, 
and/or record various function attributes that indicate what these other 
routines do. See e.g. the function attributes from LLVM 
(http://llvm.org/docs/LangRef.html#function-attributes) like 
inaccessiblememonly, inaccessiblemem_or_argmemonly, readnone, readonly, 
writeonly, and argmemonly. Since LLVM found a use for them in terms of 
optimising code, they're probably a good a start.

I wish to stress that I do _not_ propose or support adding any of those 
attributes to the language; most of those attributes don't exist in 
C/C++ either. They get added by LLVM itself while optmising and 
analysing the functions, or by compiler backends for auto-generted 
functions.

However, you could add compiler analyses that add those, or similar, 
attributes to the implementation procdef flags 
(tprocdef.implprocoptions), and then make use of those attributes even 
in cross-unit calls (in case the body of the function in the other unit 
has already been compiled, similar to inlining). Or in case of 
whole-program optimisation, they could written and loaded for the entire 
program, so you can use them even when function bodies have not yet been 
parsed.

As far as the threading issue is concerned: trunk has support for the 
"volatile" intrinsic. At most, I would add an optimizer option that 
prevents optimisations that may break things in case "volatile" is 
missing. This should happen in very few places though, since it can only 
change the behaviour of a well-defined program if you are busy-waiting 
on a single value that another thread may change (and do nothing else 
with values produced by this other thread, unless you also add a bunch 
of memory barriers and, depending on the architecture, also 
acquire/release helpers).


Jonas



More information about the fpc-devel mailing list