[Oberon] Should one automatically initalize local pointer or procedure variables (safety precaution)?
Andreas Pirklbauer
andreas_pirklbauer at yahoo.com
Mon Mar 4 17:29:32 CET 2019
> Another example: Experimental Oberon also adds some safety features
> (e.g. with respect to module unloading), yet it does not prevent access
> to uninitialized local (pointer) variables. There is a simple to implement
> solution for that (but that comes with a runtime performance penalty):
>
> <https://github.com/schierlm/OberonEmulator/blob/master/ProposedPatches/initialize-local-variables.patch>.
>
> Solutions without runtime performance penalty would make the compiler more
> complex as it has to track that variables are initialized before they are used (with
> the ambiguity whether a variable that is passed via VAR to another procedure has
> to be initialized before or not; similar to <ref> vs <out> in C#).
The performance penalty of the above solution is indeed too high, as for *every*
single procedure call, *all* local variables are always initialized to 0, even when
they are not pointers and even when there are no local pointer variables at all.
One possible optimization would be to initialize only local *pointer* and *procedure*
variables, which would effectively generate “hidden" instructions to turn a procedure P
PROCEDURE P;
VAR ptr1, ptr2, ptr3: Ptr; proc1, proc2: Proc;
BEGIN somecode
END P;
into:
PROCEDURE P;
VAR ptr1, ptr2, ptr3: Ptr;
BEGIN ptr1 := NIL; ptr2 := NIL; ptr3 := NIL; proc1 := NIL; proc2 := NIL (* <--- generated by the compiler*)
somecode
END P;
Such a scheme could easily be accomplished via the code below. However, if the programmer -
adopting good programming style - himself initializes a local pointer or procedure variable before
it is used, it will be initialized twice. Which is an argument against adapting the compiler in this way.
PS: If you are familiar with the (internals of the) Swift compiler, you’ll know what I am getting at..
-ap
ORG:
----
PROCEDURE ClearVars(ftyp: SET; typ: ORB.Type; off, r: LONGINT; VAR init: BOOLEAN);
VAR fld: ORB.Object; i, s: LONGINT;
BEGIN
IF typ.form IN ftyp THEN
IF init THEN Put1(Mov, r, 0, 0); init := FALSE END ;
Put2(Str, r, SP, off + frame)
ELSIF typ.form = ORB.Record THEN fld := typ.dsc;
WHILE fld # NIL DO ClearVars(ftyp, fld.type, fld.val + off, r, init); fld := fld.next END
ELSIF typ.form = ORB.Array THEN s := typ.base.size;
FOR i := 0 TO typ.len-1 DO ClearVars(ftyp, typ.base, i*s + off, r, init) END
END
END ClearVars;
PROCEDURE InitVars(proc: ORB.Object; r: LONGINT);
VAR obj: ORB.Object; i: INTEGER; init: BOOLEAN;
BEGIN obj := proc.type.dsc; i := proc.type.nofpar; init := TRUE;
WHILE (obj # NIL) & (i > 0) DO obj := obj.next; DEC(i) END ;
WHILE obj # NIL DO
IF obj.class = ORB.Var THEN ClearVars(ORB.Ptrs + ORB.Procs, obj.type, obj.val, r, init) END ;
obj := obj.next
END
END InitVars;
PROCEDURE Enter*(proc: ORB.Object; parblksize, locblksize: LONGINT; int: BOOLEAN);
VAR a, r: LONGINT;
BEGIN frame := 0;
IF ~int THEN (*procedure prolog*)
IF locblksize >= 10000H THEN ORS.Mark("too many locals") END ;
a := 4; r := 0;
Put1(Sub, SP, SP, locblksize); Put2(Str, LNK, SP, 0);
WHILE a < parblksize DO Put2(Str, r, SP, a); INC(r); INC(a, 4) END ;
IF r < MT THEN InitVars(proc, r) ELSE ORS.Mark("reg stack overflow") END
ELSE (*interrupt procedure*)
Put1(Sub, SP, SP, locblksize); Put2(Str, 0, SP, 0); Put2(Str, 1, SP, 4); Put2(Str, 2, SP, 8)
(*R0, R1, R2 saved on stack*)
END
END Enter;
ORP:
----
PROCEDURE ProcedureDecl;
…
ORG.Enter(proc, parblksize, locblksize, int); (*add parameter ‘proc'*)
…
ORB:
----
TYPE Ptrs* = {Pointer, NilTyp}; Procs* = {Proc, NoTyp}; (*with hidden variants*)
More information about the Oberon
mailing list