[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