Gerd Moellmann, $Date: 2003/01/02 21:26:26 $

This is an experimental PCL, based on CMUCL's PCL, including, among
other things, several new optimizations that should improve CLOS
application performance.

See the file COPYRIGHT for the copyright to my changes (it's the 
FreeBSD license).

Bugs in this PCL are not CMUCL bugs; please report them to me,
<gerd.moellmann@t-online.de>.

If you have PCL patches, please let me know, and I'll see how to
integrate them, if the issues they fix haven't been fixed already.

Building
========

Please note that this PCL release can only be used with a recent
CMUCL from CVS, 2002/12/13 19:25:50 or later.

You must build CMUCL from sources to use this PCL.  I'm using Pierre
Mai's build scripts for building CMUCL (ISTR, http://cmucl.cons.org has
pointers to these scripts).

Rename the pcl/ directory in your CMUCL source directory to something
else.  Make a new directory pcl/, and untar the new PCL tar into it.
Apply the patch(es) below.  Building should then be possible as usual
(don't forget to delete the fasl files of the old PCL before building).

After a successful build, your CMUCL should start with a herald
message telling that it's using the new PCL, and it should have a 
feature :GERDS-PCL that can be tested should it be necessary.

Please note that you must re-compile your CLOS applications for the 
new PCL.

CMUCL Patches
=============

Apply this change to src/tools/pclcom.lisp:

*** pclcom.lisp	2002/11/29 23:17:02	1.22.1.1
--- pclcom.lisp	2002/11/29 23:39:13
***************
*** 36,41 ****
--- 36,46 ----
  		     (typep (fdefinition ssym) class))
  	    (fmakunbound ssym))))))
  
+   (let ((sym (find-symbol "%CHECK-GF-REDEFINITION" "PCL")))
+     (when sym
+       (setq lisp::*setf-fdefinition-hook*
+ 	    (delete (symbol-function sym) lisp::*setf-fdefinition-hook*))))
+ 
    ;; Undefine all PCL classes, and clear CLASS-PCL-CLASS slots.
    (let ((wot (find-symbol "*FIND-CLASS*" "PCL")))
      (when (and wot (boundp wot))

Please also look for further patches for later PCL versions below.

Testing
=======

The subdirectory rt/ in the tar contains a small regression test suite,
using the RT regression test utility.  After building CMUCL with the
new PCL, the regression tests should all succeed.

You can run the test suite after loading RT and DEFSYSTEM

  * (require :rt)
  * (require :defsystem) 

(or however you do it).  Then

  * (load "rt/system.lisp")
  * (mk:clean-system :pcl-test)
  * (mk:compile-system :pcl-test)
  * (rt:do-tests)

PCL Changes
===========

* Numerous ANSI and AMOP compliance fixes, and bug fixes.

* Code cleanup.  I've taken the freedom to make the code readable to
  me, and to remove or rewrite parts.

* Files that are not used, or that I think aren't useful, have been
  removed.

* New implementation of MAKE-INSTANCE that is around 2 to 2.5 times
  faster than the old one, and fixes several bugs related to instance
  initialization.

* New optimization of SLOT-VALUE, (SETF SLOT-VALUE), and SLOT-BOUNDP
  when used outside of methods.  This speeds up SLOT-VALUE and (SETF
  SLOT-VALUE) by a factor of ca. 6, SLOT-BOUNDP by a factor of ca. 15.

* New slot accessor generic function call optimization in methods.  In
  the code

  (defclass foo () 
    ((a :accessor foo-a)))

  (defmethod bar1 ((x foo))
    (slot-value x 'a))

  (defmethod bar2 ((x foo))
    (foo-a x))

  BAR1 and BAR2 are equally fast with this optimization.  Formerly,
  a full generic function call was generated for (FOO-A X) in BAR2.

  For both slot-value and slot accessor call optimization to happen,
  classes must be known at compile time.  To ensure this, you can push
  :COMPILE-TOPLEVEL onto PCL::*DEFCLASS-TIMES*, for instance.

  The slot accessor call optimization can be turned off by setting
  PCL::*OPTIMIZE-ACCESSOR-CALLS-P* to NIL.

* New optimization of some generic function calls inside of methods.

  Generic function calls where required arguments are required method
  parameters can be optimized so that they call effective methods
  directly instead of calling the generic function's discriminating
  function.  Only one cache lookup of actual arguments is used for all
  optimized slot-value, slot-accessor, generic function calls in a
  method.

  The optimization requires compile-time knowledge that a function
  name names a generic function.  (One way of ensuring this, is
  writing separate DEFGENERICs for your methods and pushing
  :COMPILE-TOPLEVEL onto PCL::*DEFGENERIC-TIMES*).

  The performance effect of this optimization depends on circumstances.
  You might see a twofold speed increase per generic function call.

  Example:

  (defclass foo () ())

  (defgeneric bar (x))

  (defmethod baz ((x foo) y) ...)

  (defmethod bar ((x foo))
    (loop repeat 10 do 
      (baz x 1))

  In this case, the call to BAZ in the BAR method is optimizable,
  if it is known at compile time that BAZ is a generic function.

  Generic function call optimization can be turned off by setting
  PCL::*OPTIMIZE-GF-CALLS-P* to NIL.

* Faster dispatch functions are generated for some forms of generic
  functions.  There is no need anymore to add templates to
  PCL::CHECKING-OR-CACHING-LIST to get fast functions.

  This optimization can improve the speed of affected generic function
  calls by an order of magnitude.

  The optimization can be turned off by setting
  PCL::*OPTIMIZE-CACHE-FUNCTIONS-P* to NIL.

* Method tracing has been implemented.  See TRACE-METHOD,
  UNTRACE-METHOD, TRACE-GENERIC-FUNCTION-METHODS, and
  UNTRACE-GENERIC-FUNCTION-METHODS.

  Example:
  
  (defclass foo () ())
  (defmethod bar ((x foo)) x)
  (defmethod bar :before ((x foo)) x)

  * (pcl:trace-method (bar :before (foo)))

  or

  * (pcl:trace-generic-function-methods #'bar)

  etc.

Changes since 2002-11-09
========================

* A bug has been fixed that caused inheritance of class slots to fail.
  Thanks to Fred Gilham for reporting this.

Changes since 2002-11-14
========================

* DEFCLASS is checked more strictly: reader and initarg names must be
  symbols, :DEFAULT-INITARGS and :METACLASS options may only appear
  once.

* In methods, the declared slot type is checked when reading slot
  values with SLOT-VALUE, or setting slots with (SETF SLOT-VALUE).
  Example:

  (defclass foo ()
    ((a :type fixnum)))

  (defmethod bar ((object foo) value)
    (with-slots (a) object
      (setf a value)))

  (defmethod baz ((object foo))
    (< (slot-value object 'a) 10))

  In method BAR, a type error will occur if VALUE is not a fixnum.
  In method BAZ, a fixnum comparison can be used by the compiler.
  
  This checking can be turned off by setting PCL::*USE-SLOT-TYPES* to
  NIL.

Changes since 2002-11-15
========================

* A bug has been fixed causing compilation failures when
  PCL::*USE-SLOT-TYPES* is T.  The bug led to errors of the form
  "No applicable method for SLOT-DEFINITION-TYPE when called with
  argument NIL."

Note that you probably want to set PCL::*USE-SLOT-TYPES* to NIL when
trying to compile current McCLIM.  The McCLIM code contains some
places where, for instance, slots are declared to be of type
COORDINATE, which is BOUBLE-FLOAT, but actually integers are stored
into the slots.

This might also be true for other applications.  To my knowledge there
are few Lisps, if any, actually checking slot types.

Changes since 2002-11-21
========================

Please note that building CMUCL with the new PCL requires a patch to
one of the CMUCL build scripts; see the CMUCL Patches section above.

* Slot access and other optimizations no longer require adding
  :COMPILE-TOPLEVEL or COMPILE to PCL::*DEFCLASS-TIMES*,
  PCL::*DEFMETHOD-TIMES*, and/or PCL::*DEFGENERIC-TIMES* (or loading
  class and other definitions prior to compiling files.)

  The above variables have been retained for backward compatibility,
  but are no longer used; they may be deleted in future PCL versions.

* A bug has been fixed causing an error when defining classes with
  metaclass STRUCTURE-CLASS.

* Some *PRINT-CASE* dependencies in STRUCTURE-CLASS code have been
  fixed.

* A bug has been fixed causing errors when redefining a slot
  accessor method to an ordinary function.

* Slots of instances of classes with metaclass STRUCTURE-CLASS are now
  initialized to NIL instead of a special slot-unbound marker, for two
  reasons: (1) Slots of structures are always bound, and (2) the logic
  of checking for the unbound marker in PCL was anyway incomplete, so
  that user code would sometimes get an SLOT-UNBOUND error, and
  sometimes see the unbound-marker (this was also the case in original
  PCL).

* A hacked version of the original bench.lisp, clos-bench.lisp, is
  now included in the tar.  Clos-bench.lisp runs with 18d, the
  new PCL and ACL.  Happy benchmarking.

Changes since 2002-12-01
========================

* A bug has been fixed causing an error when loading a compiled file
  containing a method calling a generic function that was defined at
  compile time, but is not, or not yet, defined at load time.
  
* A bug has been fixed causing an error for (defclass foo () (a)).

* An unbound-marker object is now used instead of a PCL-internal
  symbol for the value of unbound slots.

Changes since 2002-12-01
========================

Please note that this PCL release can only be used with a recent
CMUCL from CVS, 2002/12/13 19:25:50 or later.

* Optimized MAKE-INSTANCE calls check initialization values against
  slot types if PCL::*USE-SLOT-TYPES* is true.  Example:

  (defclass foo () 
    ((a :type integer :initarg :a)))

  (defun bar (x)
    (make-instance 'foo :a x))

  (compile 'bar)

  (bar 1.0)
    => type-error: 1.0 is not of type integer

* A bug has been fixed causing 

  (symbol-macrolet ((x 1))
    (defmethod foo (z)
      (macrolet ((ml (form) `(progn ,form ,x)))
        (ml (print x)))))

  to fail with ``unknown variable x''.  This fix required a change in
  CMUCL's compiler, so that this PCL can only be used with a recent
  CMUCL from CVS.

* MAKE-INSTANCE, when called with a non-constant class or non-constant
  initargs, now tries to use an existing optimized constructor
  function, which can speed up MAKE-INSTANCE 2x or more if such a
  constructor function exists.
  
Changes since 2002-12-16
========================

* ARGLIST, INDENTATION, and VALUES declarations have been removed from
  the code.

* A bug has been fixed causing structure class instances to be
  initialized incompletely.

* MAKE-INSTANCE optimizations are now performed also for classes with
  metaclass STRUCTURE-CLASS.

* PCL no longer uses an unbound-marker for unbound slots, because
  using an unbound-marker caused problems in interpreted code.

* PCL::*USE-SLOT-TYPES* has been renamed to PCL::*USE-SLOT-TYPES-P*.

* COMPUTE-SLOTS has been changed to be more in line with AMOP.

* A new experimental declaration PCL:OPTIMIZE-SLOT-ACCESS is
  recognized in method bodies for optimizing SLOT-VALUE, (SETF
  SLOT-VALUE), and SLOT-BOUNDP.

  declare (pcl:optimize-slot-access specifier*)

  specifier   ::= (quality class-entry*)
  quality     ::= SLOT-BOUNDP | INLINE
  class-entry ::= class | (class slot-name*)
  class       ::= the name of a class
  slot-name   ::= the name of a slot

  The SLOT-BOUNDP quality specifies that all or some slots of a class
  are always bound.

  The INLINE quality specifies that access to all or some slots of a
  class should be inlined, using compile-time knowledge of class
  layouts.

  Example:

  (defclass foo ()
    (a b))

  (defmethod bar ((x foo))
    (declare (pcl:optimize-slot-access 
               (slot-boundp foo) (inline (foo a))))
    (list (slot-value x 'a) (slot-value x 'b)))

  The SLOT-BOUNDP declaration in method BAR specifies that the slots A
  and B accessed through parameter X in the scope of the declaration
  are always bound, because parameter X is specialized on class FOO to
  which the SLOT-BOUNDP declaration applies.  The PCL-generated code
  for the SLOT-VALUE forms will thus not contain tests for the slots
  being bound or not.  The consequences are undefined should one of
  the accessed slots not be bound.

  The INLINE declaration in method BAR specifies that PCL should use
  compile-time knowledge of slot locations for accessing slot A of
  class FOO in the scope of the declaration.

  Class FOO must be known at compile time for this optimization.  If
  the class is not defined at compile time, PCL will print a warning
  and use normal slot access.

  Normal slot access will also be used if PCL finds, at method
  compilation time, that

  - FOO has a subclass in which slot A is at a different location.

  - There exists a SLOT-VALUE-USING-CLASS method for FOO or a subclass
    of FOO.

  The consequences are undefined should the compile-time environment
  not be the same as the run-time environment in these respects, or if
  the definition of FOO or any subclass of FOO is changed in an
  incompatible way, that is, if slot locations change.

  The effect of the INLINE optimization combined with the SLOT-BOUNDP
  optimization is that CLOS slot access becomes as fast as structure
  slot access, which is an order of magnitude faster than normal CLOS
  slot access (something like 25x in my benchmarks).

  The declaration is also used to optimize calls to slot accessor
  generic functions.

  The same restrictions as for SLOT-VALUE, (SETF SLOT-VALUE) apply;
  in addition, the optimization is not used if

  - There exist, at compile time, applicable methods on the
    reader/writer generic function that are not standard accessor
    methods (for instance, around methods).

  - Applicable reader/writer methods access different slots in a class
    accessed INLINE and one of its subclasses.

  The consequences are undefined should the compile-time environment
  not be the same as the run-time environment in these respects.

Changes since 2002-12-24
========================

* Some more unused code has been removed from PCL.

* Invalid initargs passed to REINITIALIZE-INSTANCE, which were
  previously silently ignored, now signal an error.

* A bug in the optimized MAKE-INSTANCE implementation has been fixed,
  exemplified by this code

  (defclass s1 () ())
  (defclass s2 (s1) ())
  (defun f () (make-instance 's2))
  (compile 'f)
  (f)
  (defmethod initialize-instance :before ((x s1) &rest args)
    (declare (ignore args))
    (print "before"))
  (f)
    ==> before-method not being called
  
* The variable PCL::*OPTIMIZE-INLINE-SLOT-ACCESS-P* controls if
  inline slot access optimizations are performed.  It is true by
  default.

* The PCL:OPTIMIZE-SLOT-ACCESS declaration has been renamed to the
  shorter PCL:SLOTS.

* The PCL:SLOTS declaration can be PROCLAIM'd.  Please use the
  following patch to add a hook to src/compiler/proclaim.lisp that PCL
  can use.

*** proclaim.lisp	2002/12/05 19:39:14	1.37
--- proclaim.lisp	2002/12/24 20:58:36
***************
*** 327,332 ****
--- 327,334 ----
  (defun %declaim (x)
    (proclaim x))
  
+ (defvar *proclamation-hook*)
+ 
  ;;; PROCLAIM  --  Public
  ;;;
  ;;;    This function is the guts of proclaim, since it does the global
***************
*** 335,340 ****
--- 337,345 ----
  (defun proclaim (form)
    (unless (consp form)
      (error "Malformed PROCLAIM spec: ~S." form))
+ 
+   (when (boundp '*proclamation-hook*)
+     (funcall *proclamation-hook* form))
    
    (let ((kind (first form))
  	(args (rest form)))

* If a class has been proclaimed to use inline slot access before it
  is defined, the class will defined at compile time.

  In the example:

  (declaim (pcl:slots (inline (foo slot-a))))
  (defclass foo () ...)
  (defclass bar (foo) ...)

  FOO will be defined at compile time because it's declared to use
  inline slot access.  Methods accessing slot SLOT-A of FOO will use
  inline slot access if otherwise possible.

  BAR will be defined at compile time because its superclass FOO is
  declared to use inline slot access, and PCL uses compile-time
  information from subclasses to warn about situations where using
  inline slot access isn't possible.
  
* Methods using inline slot access can be automatically recompiled
  after class changes.  Two new declarations control which methods are
  automatically recompiled.

  declaim (pcl:auto-compile specifier*)
  declaim (pcl:not-auto-compile specifier*)

  specifier   ::= gf-name | (gf-name qualifier* (specializer*))
  gf-name     ::= the name of a generic function
  qualifier   ::= a method qualifier
  specializer ::= a method specializer

  If no specifier is given, auto compilation is by default done/not
  done for all methods of all generic functions using inline slot
  access; current default is that it is not done.  This global policy
  can be overidden on a generic function and method basis.

  If SPECIFIER is a generic function name, it applies to all methods
  of that generic function.

  Examples:

    (declaim (pcl:auto-compile foo))
    (defmethod foo :around ((x bar)) ...)

    The around-method FOO will be automatically recompiled because the
    declamation applies to all methods with name FOO.

    (declaim (pcl:auto-compile (foo (bar))))
    (defmethod foo :around ((x bar)) ...)
    (defmethod foo ((x bar)) ...)

    The around-method will not be automatically recompiled, but the
    primary method will.

    (declaim (pcl:auto-compile foo))
    (declaim (pcl:not-auto-compile (foo :around (bar)))  
    (defmethod foo :around ((x bar)) ...)
    (defmethod foo ((x bar)) ...)

    The around-method will not be automatically recompiled, because it
    is explicitly declaimed not to be.  The primary method will be
    automatically recompiled because the first declamation applies to
    it.

  Auto-recompilation works by recording the bodies of methods using
  inline slot access.  When PCL thinks a recompilation is necessary, a
  DEFMETHOD form is constructed and EVAL'd.

  This can only be done for methods defined in a null lexical
  enviroment.  PCL prints a warning and doesn't record the method body
  if the method is defined in a non-null enviroment.  Instead of doing
  a recompilation, PCL will then print a warning that the method must
  be recompiled manually when classes are changed, as in other cases.

