[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