[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: LOAD-TIME-EVAL



    Date: Thu, 8 Sep 88  15:24:18 CDT
    From: David N Gray <Gray@DSG.csc.ti.com>

    ... if you want data that can be modified, then that's what variables are for.

People keep saying this, but it's just not true. Consider an expression such
as X(Y+3)+2 in a function FOO which has been translated into Lisp. I might want to
implement that as:

 ... (+ (AREF *FORTRAN-VIRTUAL-MEMORY* 
	      (+ (LOAD-TIME-CONSTANT (+ (FORTRAN-VARIABLE-OFFSET 'FOO 'X) 3))
		 (AREF *FORTRAN-VIRTUAL-MEMORY*
		       (LOAD-TIME-CONSTANT (FORTRAN-VARIABLE-OFFSET 'FOO 'Y)))))
	2) ...

so that if the offset of X in fortran memory is 40 and the offset of Y is 50,
something which cannot be known until runtime, the code will behave as if I'd
written:

 ... (+ (AREF *FORTRAN-VIRTUAL-MEMORY* (+ 43 (AREF *FORTRAN-VIRTUAL-MEMORY* 50)))
        2) ...

Fortunately, since these subscripts are not modified, you aren't asking me
to write:

 (DEFVAR I-HOPE-THIS-IS-UNIQUE-0001 (+ (FORTRAN-VARIABLE-OFFSET 'FOO 'X) 3))
 (DEFVAR I-HOPE-THIS-IS-UNIQUE-0002 (FORTRAN-VARIABLE-OFFSET 'FOO 'Y))
 ... (+ (AREF *FORTRAN-VIRTUAL-MEMORY*
	      (+ I-HOPE-THIS-IS-UNIQUE-0001
		 (AREF *FORTRAN-VIRTUAL-MEMORY* I-HOPE-THIS-IS-UNIQUE-0002)))
	2) ...

Anyone who asks that #, be flushed is, in effect, asking me to write this kind
of stuff. Among other things, it forces the creation of numerous extra symbols
and it may require an indirect access to the value (through the symbol's value
cell) in some implementations where an immediate move (or even an instruction
that utilizes a constant operand) might have been possible if quote were used.
(Among other things, no compiler is going to be able to do any data flow
analysis tricks that will tell it that the value of these unique vars won't change.)

Anyway, suppose now the situation is more complicated. Suppose I'm doing something
CLOS-like. Now I don't want to translate variables to one-level indirect accesses
but rather I want two-level indirect access. That is, suppose I want to translate

(DEFUN FROB (X) 
  (WITH-SLOTS (A B C) X
    (DECLARE (TYPE FOO X))
    (+ A B)))

and let's say the WITH-SLOTS expression expands to something roughly like:

 (LET ((INSTANCE-TABLE (SLOT-TABLE-FOR-INSTANCE FOO X)))
   (SYMBOL-MACROLET ((A (INSTANCE-REF X (SVREF INSTANCE-TABLE (INSTANCE-SLOT-INDEX FOO A))))
		     (B (INSTANCE-REF X (SVREF INSTANCE-TABLE (INSTANCE-SLOT-INDEX FOO B)))))
     (+ A B)))

where

 (SLOT-TABLE-FOR-INSTANCE FOO X)
 <==> (AREF (LOAD-TIME-CONSTANT (SLOT-TABLE-FOR-CLASS 'FOO))
	    (INSTANCE-CLASS-INDEX X))

and

 (INSTANCE-SLOT-INDEX FOO A)
   <==> (LOAD-TIME-CONSTANT (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'A))

so that at runtime my FROB function behaved like:

(DEFUN FROB (X)
  (LET ((INSTANCE-TABLE (AREF (LOAD-TIME-CONSTANT (SLOT-TABLE-FOR-CLASS 'FOO))
			      (INSTANCE-CLASS-INDEX X))))
    (+ (INSTANCE-REF X (SVREF INSTANCE-TABLE
			      (LOAD-TIME-CONSTANT (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'A))))
       (INSTANCE-REF X (SVREF INSTANCE-TABLE
			      (LOAD-TIME-CONSTANT (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'B)))))))

Note that the INSTANCE-TABLE is a known value at load time but that it is easy to
imagine implementations in which it wants to be adjustable and to change over time.
Surely you won't ask me to write:

(DEFVAR I-HOPE-THIS-IS-UNIQUE-TOO-0001 (SLOT-TABLE-FOR-CLASS 'FOO))
(DEFVAR I-HOPE-THIS-IS-UNIQUE-TOO-0002 (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'A))
(DEFVAR I-HOPE-THIS-IS-UNIQUE-TOO-0003 (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'B))

(DEFUN FROB (X)
  (LET ((INSTANCE-TABLE (AREF I-HOPE-THIS-IS-UNIQUE-TOO-0001
			      (INSTANCE-CLASS-INDEX X))))
    (+ (INSTANCE-REF X (SVREF INSTANCE-TABLE I-HOPE-THIS-IS-UNIQUE-TOO-0002))
       (INSTANCE-REF X (SVREF INSTANCE-TABLE I-HOPE-THIS-IS-UNIQUE-TOO-0003)))))

If you did want me to write this, then I have the following concerns:

 - I have to be careful about assigning names to these variables.
   They must be interned or the compiler will not correctly link up the
   DEFUN with the DEFVAR, but since I'm going to generate a zillion of them,
   I have to have a really clever naming scheme.

 - These variables take up unwanted space. Not just symbol space but also
   a pname (string) and value cell.

 - I cannot expand the code for WITH-SLOTS except in a situation where I have
   some mechanism (it was called a Pratt stack in Maclisp) for getting the
   auxiliary DEFVAR definitions snuck out to toplevel so they'll get compiled
   correctly.

I cannot, for example, write:

(LET ((THIS-NEEDNT-BE-VERY-UNIQUE-0001
	(SLOT-TABLE-FOR-CLASS 'FOO))
      (THIS-NEEDNT-BE-VERY-UNIQUE-0002
	(INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'A))
      (THIS-NEEDNT-BE-VERY-UNIQUE-0003
	(INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'B)))
  (DEFUN FROB (X)
    (LET ((INSTANCE-TABLE (AREF THIS-NEEDNT-BE-VERY-UNIQUE-0001
				(INSTANCE-CLASS-INDEX X))))
      (+ (INSTANCE-REF X (SVREF INSTANCE-TABLE THIS-NEEDNT-BE-VERY-UNIQUE-0002))
	 (INSTANCE-REF X (SVREF INSTANCE-TABLE THIS-NEEDNT-BE-VERY-UNIQUE-0003))))))

because DEFUN is not currently allowed inside a LET.

Even if DEFUN were allowed inside a LET, though, I still couldn't win because it's
the WITH-SLOTS that would be wanting to do this, not the DEFUN. The best it could
do would be:

(DEFUN FROB (X)
  (LET ((THIS-NEEDNT-BE-VERY-UNIQUE-0001
	  (SLOT-TABLE-FOR-CLASS 'FOO))
	(THIS-NEEDNT-BE-VERY-UNIQUE-0002
	  (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'A))
	(THIS-NEEDNT-BE-VERY-UNIQUE-0003
	  (INSTANCE-SLOT-INDEX-LOOKUP 'FOO 'B)))
    (LET ((INSTANCE-TABLE (AREF THIS-NEEDNT-BE-VERY-UNIQUE-0001
				(INSTANCE-CLASS-INDEX X))))
      (+ (INSTANCE-REF X (SVREF INSTANCE-TABLE THIS-NEEDNT-BE-VERY-UNIQUE-0002))
	 (INSTANCE-REF X (SVREF INSTANCE-TABLE THIS-NEEDNT-BE-VERY-UNIQUE-0003))))))

which would still be useless unless the compiler had some way to know it could factor
the LET out of the DEFUN so it didn't have to execute it in time. And there are certainly
cases where it would not be able to do that without further clue.

Anyway, I hope this makes a reasonable case for the fact that it is possible to derive
a user application where variables are not suitable and where a LOAD-TIME-CONSTANT
facility is suitable, and where it is important to be able to distinguish between
read-only and non-read-only data.

##### VVVVV ###### NOTE WELL ##### READ THIS ##### DISCLAIMER #####

I don't want to get into ANY discussion of whether this is the right way to
implement WITH-SLOTS. I just picked WITH-SLOTS because it is the domain likely
to be most familiar to readers of this mail. The idea of doing double-indirection
of arrays is not unique to CLOS so any discussion of what you can or can't get
away with in CLOS in actual practice would be utterly irrelevant here.

########## END OF IMPORTANT TEXT ##########

    I also have a problem with this:

    >    Note, however, that in the case of quoted code (processed by explicit
    >    use of EVAL), each call to EVAL is treated like a load. Caching may not
    >    be implemented by having LOAD-TIME-CONSTANT displace its source level
    >    call. 

    These seems like an unnecessary and undesirable restriction; the point is
    to make this feature possible; I don't see why the the implementation
    should be overly constrained.

Well, I don't see why the user should be overly constrained.

Better to burden the implementors than the users on something like this.
Implementations are done only once and can easily be done carefully to
avoid bugs. Users write code repeatedly and the bugs which could result
could happen over and over again.  Displacing macros were once the rage,
but they have become less of a big deal over time as people have shifted
to less user-intrusive paradigms (single pass semantic analysis by
compilers, once-only interpreter pre-pass, and/or hashed memoization --
none of which involve displacing user code).