Hi all,
Sorry for the bumpy ride while I reorganized the auto loader system.
Thanks for taking the time to respond to my earlier questions on auto
loaders. This is the time to explain what I did with your answers (and what
I did in general).
To reiterate what this is all about: the autoloader is an important part of
the start up efficiency and internal file dependency resolution of ABCL: it
establishes function and macro bindings on symbols without loading the
actual code to bind to. Instead, loading the FASL is being delayed until
some part of the system (or a user) needs it. There are two variants of
autoloaders: Java class autoloaders and lisp (fasl) autoloaders. Their
purpose is the same and they operate roughly the same way, but one operates
by forcing a class to be loaded while the other causes a FASL to be loaded.
Most parts of the system don't know about autoloaders and simply evaluate
code. The autoloaders take special action when evaluated: their purpose is
not to execute the intended action, but instead, they load the fasl in
which the actual code is located. Then, when the actual code is loaded, the
function (or macro) call is forwarded to the loaded code. The FASL loader
process replaces the content of the function symbol slot with the true
function so the autoloader gets garbage collected after replacement.
This entire system works pretty well, that is: *if* you know which symbols
you have to install an autoloader on. Before my changes, we didn't know
which symbols we had to install autoloaders on. Instead, we maintained a
list of symbols by hand through trial and error. When a change caused total
havoc, you'd look for the cause and - hopefully - debugged the problem to a
missing autoloader. That's when you would add an autoloader to
autoloads.lisp. That works as long as our only purpose is to build ABCL.
However, we actually intend for ABCL to be used by our users.
Users may not be aware of what autoloaders do for them and choose to start
calling some random function in the AMOP definition. Since we hand-maintain
a list of functions to be decorated with autoloaders, that probably doesn't
work. So, in order to provide more stability for our users we need to
decorate all functions with autoloaders.
What's more: having all functions decorated with autoloaders -if done well-
should also help ABCL development quite a bit: the chances of a change
causing havoc because a non-decorated function is being used have been
reduced to next-to-none.
What then did I do?
The change involves a few steps:
1. Extraction of symbols to be decorated
=============================
The file compiler now generates (when compiling our own system, that is)
two extra output files: basename.funcs and basename.macs. These files
contain a single s-exp with all the symbols which have been DEFUNed or
DEFMACROed respectively. With this information, we can generate decoration
information.
Analysis of the extracted symbols leads to the conclusion we can't assign
an autoloader to each and every extracted symbol for 2 reasons:
a. There is a built-in function associated with the symbol to help
bootstrapping
b. There are multiple DEFUNs or DEFGENERIC/DEFMETHODs
2. Determination of what to decorate symbols with
====================================
Autoloaders - at this time - can only be used to load a single FASL. So,
when a symbol is in multiple files, we need to choose which file it load
the FASL for (and hence not the others).
Most symbols which occur in multiple files are DEFMETHODs which is
perfectly fine. Most symbols with double locations stem from the
EXTENSIBLE-SEQUENCES implementation. We want to exclude those FASLs, which
reduces the number of multi-homed symbols to only a few.
All single-homed symbols excluding those which have bootstrap functions
attached can be decorated with autoloaders.
3. Generate code to install the autoloaders
===============================
We just used to have autoloads.lisp (and Autoload.java). But that file will
contain only the manual bits which can't be auto-generated. So there's now
a second file autoload-gen.lisp. This file is being generated during the
build and populated with all symbols which need an autoloader. Today's
autoload.lisp has been stripped to the bare minimum of AUTOLOAD and
AUTOLOAD-MACRO forms.
With the stripped autoload.lisp and the fact that the autoloaders are
bootstrapping helpers, I had to take additional measures to help us
bootstrap again. The result is autoloads-gen.lisp in the source directory.
It is loaded at build time to help bootstrapping. It's an unmodified
version I took from the build directory:
build/classes/org/armedbear/lisp/autoloads-gen.lisp - the one which is
generated on each build.
4. Exporting the right symbols
======================
Not a problem related to autoloading per se, but definitely related to the
fact that we don't load all fasls at boot time: autoload.lisp - as the
canonical location being loaded at startup - had grown to be a listing of
EXPORT forms. Having EXPORT forms and actual code widely apart is
counter-intuitive: to the occasional developer it's inexplicable how some
symbols become exported and some don't. (We had files with additional
EXPORT forms.)
So, when the above was "solved", I created another facility which collects
all the symbols in the second argument of EXPORT throughout our source
tree. Those symbols are added to the autoload-gen file and the EXPORT forms
executed at start up as well. Even though the developer still doesn't know
what's going on, the system should be working transparently.
All of the above has happened over the last week(s) - last week, mostly.
Then there is the question:
As it turns out, the macroexpansion for DEFSTRUCT happens in a way such
that the file compiler can't detect the slot accessors as being defined as
functions. The advantage is that we're *not* seeing a whole slew of symbols
to be attached to autoloaders and hence limiting the number of autoloaders
(the contructor function *is* detected).
Now: what do others think: should we depend on the constructor being called
before any of the accessors to make sure the autoloaders have been run? Or
should we attach an autoloader to each accessor function just to be sure?
(This requires small changes to the file compiler and defstruct macro
expansion.)
Bye,
Erik.