clos: rework slightly the dispatch mechanism

- improve set-generic-function-dispatch comments
- add a new file that contains "lisp-defined" dispatchers
- remove unused code
This commit is contained in:
Daniel Kochmański 2021-12-16 13:15:21 +01:00
parent 5cb67471fd
commit 84200fd315
3 changed files with 96 additions and 81 deletions

41
src/clos/dispatch.lsp Normal file
View file

@ -0,0 +1,41 @@
;;;;
;;;; Copyright (c) 2021, Daniel Kochmański
;;;; See file 'LICENSE' for the copyright details.
;;;;
;;;
;;; This file contains implementations of various attempts at the generic
;;; function dispatch.
;;;
(in-package #:clos)
;;; This discriminator function is included for the reference and benchmarks.
;;; It doesn't perform any optimizations and always recomputes everything.
;;;
;;; This function is similar to `generic_compute_applicable_method' in C.
(defun unoptimized-discriminator (gf)
(lambda (&rest args)
(multiple-value-bind (method-list ok)
(compute-applicable-methods-using-classes gf (mapcar #'class-of args))
(unless ok
(setf method-list (compute-applicable-methods gf args))
(unless method-list
(apply #'no-applicable-method gf args)))
(let* ((combin (generic-function-method-combination gf))
(em-fun (compute-effective-method-function gf combin method-list)))
(funcall em-fun args nil)))))
;;; This discriminator function is similar to UNOPTIMIZED-DISCRIMINATOR except
;;; that uses the fact that the function class is STANDARD-GENERIC-FUNCTION so
;;; it may use non-generic version of computation functions and it doesn't call
;;; COMPUTE-APPLICABLE-METHODS-USING-CLASSES[1]. Both behaviors are permissible.
;;;
;;; This function is similar to `restricted_compute_applicable_method' in C.
(defun standard-gf-discriminator (gf)
(lambda (&rest args)
(let ((method-list (std-compute-applicable-methods gf args)))
(unless method-list
(apply #'no-applicable-method gf args))
(let* ((combin (generic-function-method-combination gf))
(em-fun (compute-effective-method-function gf combin method-list)))
(funcall em-fun args nil)))))

View file

@ -118,89 +118,61 @@
(reinitialize-instance gf :name new-name)
(setf (slot-value gf 'name) new-name)))
(defun default-dispatch (generic-function)
(cond ((null *clos-booted*)
'standard-generic-function)
((eq (class-id (class-of generic-function))
'standard-generic-function)
'standard-generic-function)
(t)))
(defun compute-discriminating-function (generic-function)
(values #'(lambda (&rest args)
(multiple-value-bind (method-list ok)
(compute-applicable-methods-using-classes
generic-function
(mapcar #'class-of args))
(unless ok
(setf method-list
(compute-applicable-methods generic-function args))
(unless method-list
(apply #'no-applicable-method generic-function args)))
(funcall (compute-effective-method-function
generic-function
(generic-function-method-combination generic-function)
method-list)
args
nil)))
(defun std-compute-discriminating-function (generic-function)
(values (if (eq (slot-value (class-of generic-function) 'name)
'standard-generic-function)
(standard-gf-discriminator generic-function)
(unoptimized-discriminator generic-function))
t))
(defun set-generic-function-dispatch (gfun)
;;
(defun compute-discriminating-function (gf)
(declare (notinline std-compute-discriminating-function))
(std-compute-discriminating-function gf))
(defun set-generic-function-dispatch (gf)
;; We have to decide which discriminating function to install:
;; 1* One supplied by the user
;; 2* One coded in C that follows the MOP
;; 3* One in C specialized for slot accessors
;; 4* One in C that does not use generic versions of compute-applicable-...
;; Respectively
;; 1* The user supplies a discriminating function, or the number of arguments
;; is so large that they cannot be handled by the C dispatchers with
;; with memoization.
;; 2* The generic function is not a s-g-f but takes less than 64 arguments
;; 3* The generic function is a standard-generic-function and all its slots
;; are standard-{reader,writer}-slots
;; 4* The generic function is a standard-generic-function with less
;; than 64 arguments
;; 1. One supplied by the user (or not possible to optimize in C)
;; 2. One coded in C that follows the MOP
;; 3. One in C specialized for slot accessors
;; 4. One in C specialized for standard-generic-function
;; Respectively:
;; 1. The user supplies the discriminating function[1]
;; 2. The function is not STANDARD-GENERIC-FUNCTION
;; 3. The class of all function methods is STANDARD-OPTIMIZED-*-METHOD
;; 4. The function is a STANDARD-GENERIC-FUNCTION
;;
;; This chain of reasoning uses the fact that the user cannot override methods
;; such as COMPUTE-APPLICABLE-METHODS, or COMPUTE-EFFECTIVE-METHOD, or
;; COMPUTE-DISCRIMINATING-FUNCTION acting on STANDARD-GENERIC-FUNCTION.
;; This chain of reasoning uses the fact that the user is not allowed to
;; override standard methods specialized on standard classes.
;;
;; [1] We recognize that the user didn't overwrite our function by examining
;; the second value returned from C-D-F; It is also possible that C-D-F
;; disables the C optimization because it has more efficient strategy.
;;
(declare (notinline compute-discriminating-function))
(multiple-value-bind (default-function optimizable)
;; If the class is not a standard-generic-function, we must honor
;; whatever function the user provides. However, we still recognize the
;; case without user-computed function, where we can replace the output
;; of COMPUTE-DISCRIMINATING-FUNCTION with a similar implementation in C
(compute-discriminating-function gfun)
(let ((methods (slot-value gfun 'methods)))
(set-funcallable-instance-function
gfun
(cond
;; Case 1*
((or (not optimizable)
(> (length (slot-value gfun 'spec-list))
si::c-arguments-limit))
default-function)
;; Case 2*
((and (not (eq (slot-value (class-of gfun) 'name)
'standard-generic-function))
*clos-booted*)
t)
((null methods)
'standard-generic-function)
;; Cases 3*
((loop with class = (find-class 'standard-optimized-reader-method nil)
for m in methods
always (eq class (class-of m)))
'standard-optimized-reader-method)
((loop with class = (find-class 'standard-optimized-writer-method nil)
for m in methods
always (eq class (class-of m)))
'standard-optimized-writer-method)
;; Case 4*
(t
'standard-generic-function))))))
(multiple-value-bind (computed-discriminator optimizable)
(compute-discriminating-function gf)
(unless (not (> (length (slot-value gf 'spec-list)) 63))
;; Code in C caches effective methods; the cache key length is 64 and
;; can't handle functions with more specializable arguments. The first
;; argument is the generic function itself.
(setf optimizable nil))
(let ((methods (slot-value gf 'methods))
(standard-generic-function-p
(eq (slot-value (class-of gf) 'name) 'standard-generic-function)))
(flet ((only-slot-accessors-p (class-name)
(and methods
(loop with class = (find-class class-name nil)
for m in methods
always (eq class (class-of m)))
class-name)))
(set-funcallable-instance-function
gf
(cond
((not optimizable) computed-discriminator)
((not standard-generic-function-p) 't)
((only-slot-accessors-p 'standard-optimized-reader-method))
((only-slot-accessors-p 'standard-optimized-writer-method))
(t 'standard-generic-function)))))))
;;; ----------------------------------------------------------------------
;;; COMPUTE-APPLICABLE-METHODS
@ -301,9 +273,10 @@
(with-early-accessors (+standard-method-slots+)
(let* ((specializers-list-1 (method-specializers method-1))
(specializers-list-2 (method-specializers method-2)))
(compare-specializers-lists (if f (funcall f specializers-list-1) specializers-list-1)
(if f (funcall f specializers-list-2) specializers-list-2)
args-specializers))))
(compare-specializers-lists
(if f (funcall f specializers-list-1) specializers-list-1)
(if f (funcall f specializers-list-2) specializers-list-2)
args-specializers))))
(defun compare-specializers-lists (spec-list-1 spec-list-2 args-specializers)
(declare (si::c-local))
@ -349,7 +322,7 @@
(cond ((eq spec-1 spec-2) '=)
((fast-subtypep spec-1 spec-2) '1)
((fast-subtypep spec-2 spec-1) '2)
((eql-specializer-flag spec-1) '1) ; is this engough?
((eql-specializer-flag spec-1) '1) ; is this enough?
((eql-specializer-flag spec-2) '2) ; Beppe
((member spec-1 (member spec-2 cpl)) '2)
((member spec-2 (member spec-1 cpl)) '1)

View file

@ -9,6 +9,7 @@
"src:clos;std-slot-value.lsp"
"src:clos;slot.lsp"
"src:clos;boot.lsp"
"src:clos;dispatch.lsp"
"src:clos;kernel.lsp"
"src:clos;method.lsp"
"src:clos;combin.lsp"