-*- coding: utf-8 -*-
Project Name: Metaobject Sealing for CLOS
========
Synopsis
Sealing is a technique whereby a programmer may assert that certain
metaobjects — such as classes or generic functions — should not and
will not be used or extended in certain ways. It is then the
responsibility of the object system to verify that this promise is
not broken.
In addition to clarifying the programmer's intention with regard
to a given protocol, sealing enables a variety of optimizations.
Because of its highly dynamic nature (allowing redefinitions of
classes and methods at runtime), the Common Lisp Object System
(CLOS) is able to benefit greatly from a sealing promise.
The goal of this project is to define appropriate semantics for
sealing in CLOS and investigate how much can be achieved via the
CLOS Metaobject Protocol¹ (MOP) alone and what hooks need to be
implementation-provided for the sake of efficiency. Furthermore, we
would like to implement said hooks for the SBCL² compiler.
¹
²
=========
Rationale
The metaobject approach to programming language design seeks to
provide elegance and extensibility while simultaneously providing
opportunities for implementation efficiency through careful design.
A sealing protocol fits this exact pattern. It should be possible
to implement a significant part of said protocol through the CLOS
MOP. More specifically, the MOP provides plenty of hooks for
verifying that a sealing promise is being kept. However, because
the MOP is a runtime protocol, this approach is likely to be limited
to runtime optimizations which may or may not be straightforward.
Hence, we anticipate the need for implementation-specific hooks,
particularly in the area of compile-time optimizations. It is one
of the goals of this project to identify this boundary between what
can/should be implemented portably and what should be provided by
specific implementations.
The obvious metaobject candidates for sealing are classes and
generic functions and it might be worthwhile to study the effects of
sealing specializers, method combinations, and methods. Possible
optimization avenues opened by sealing include:
1. compile-time method selection and combination; applicable
methods can be computed and combined according to the
generic function's method combination into an effective
method function at compile-time.
2. slot-value cloning and inlining; slot accesses can be
turned into direct memory accesses with an offset from the
start of the object, possibly inlined, very much like
structure slot access is optimized in some CL compilers.
3. method cloning and inlining; just as class slot access can
approach that of structure slot access, so can generic
function calls be identical to regular function calls.
Effective methods can be cloned for specific types, opening
way for optimization (4).
4. more precise type inferencing; optimizations (2) and (3)
allow improvements in flow-based type inference, by putting
barriers on where uncertain types can flow from/to. Not
only the generic functions' bodies benefit from this but
also the call sites. The same applies to slot-value being
able to propagate declared slot types.
In short, these optimizations have the potential to bring classes
and generic functions up to pair with structures and regular
functions, performance-wise, substituting the approach of rewriting
the former in terms of the latter (for purposes of optimization)
with sealing.
One of the most important steps in this project will be figuring
out the exact semantics of sealing for the various metaobjects. For
instance, possible sealing alternatives for classes could be:
* Shallow sealing: the class may not be further subclassed
directly, but might be subclassed indirectly.
* Deep sealing: the class may not be further subclassed in any
way, directly or indirectly.
* Redefinition sealing: the class (and perhaps its
superclasses) may not be redefined.
Each presents different optimization opportunities. It is one of
the main purposes of this project to clearly define these semantics
and associated optimization opportunities. Similarly, sealing a
generic function would forbid adding new methods but it might be
desirable to seal only particular intersections of methods and
argument types. This would allow for optimization without eschewing
extensibility altogether.
Following Lisp's and CLOS' best traditions in dynamism, this
protocol should be complete with unsealing operations. Naturally,
this has serious implications regarding the way optimizations should
be performed and this potentially turns sealing into a hint that
optimizations that are potentially expensive to back out can and
should be used. Moreover, more extreme, ‘irreversible’,
optimizations such as full-fledged method selection and inlining
(that would require recompilation of the call sites in order to be
backed out or redone) could be applied when compiling with specific
optimization levels, for instance. This might be particularly
sensible in compilerless delivery scenarios.