mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
695 lines
33 KiB
Plaintext
695 lines
33 KiB
Plaintext
|
||
|
||
;;;; Terminology.
|
||
|
||
OBJECT
|
||
An object is one of the following:
|
||
a single word containing immediate data (characters, fixnums, etc)
|
||
a single word pointing to an object (structures, conses, etc.)
|
||
These are tagged with three low-tag bits as described in the section
|
||
"Tagging". This is synonymous with DESCRIPTOR.
|
||
|
||
LISP OBJECT
|
||
A Lisp object is a high-level object discussed as a data type in Common
|
||
Lisp: The Language.
|
||
|
||
DATA-BLOCK
|
||
A data-block is a dual-word aligned block of memory that either manifests a
|
||
Lisp object (vectors, code, symbols, etc.) or helps manage a Lisp object on
|
||
the heap (array header, function header, etc.).
|
||
|
||
DESCRIPTOR
|
||
A descriptor is a tagged, single-word object. It either contains immediate
|
||
data or a pointer to data. This is synonymous with OBJECT.
|
||
|
||
WORD
|
||
A word is a 32-bit quantity.
|
||
|
||
|
||
|
||
;;;; Tagging.
|
||
|
||
The following is a key of the three bit low-tagging scheme:
|
||
000 even fixnum
|
||
001 function pointer
|
||
010 other-immediate (header-words, characters, symbol-value trap value, etc.)
|
||
011 list pointer
|
||
100 odd fixnum
|
||
101 structure pointer
|
||
110 unused
|
||
111 other-pointer to data-blocks (other than conses, structures,
|
||
and functions)
|
||
|
||
This taging scheme forces a dual-word alignment of data-blocks on the heap, but
|
||
this can be pretty negligible:
|
||
RATIOS and COMPLEX must have a header-word anyway since they are not a
|
||
major type. This wastes one word for these infrequent data-blocks since
|
||
they require two words for the data.
|
||
BIGNUMS must have a header-word and probably contain only one other word
|
||
anyway, so we probably don't waste any words here. Most bignums just
|
||
barely overflow fixnums, that is by a bit or two.
|
||
Single and double FLOATS?
|
||
no waste
|
||
one word wasted
|
||
SYMBOLS are dual-word aligned with the header-word.
|
||
Everything else is vector-like including code, so these probably take up
|
||
so many words that one extra one doesn't matter.
|
||
|
||
|
||
|
||
;;;; GC Comments.
|
||
|
||
Data-Blocks comprise only descriptors, or they contain immediate data and raw
|
||
bits interpreted by the system. GC must skip the latter when scanning the
|
||
heap, so it does not look at a word of raw bits and interpret it as a pointer
|
||
descriptor. These data-blocks require headers for GC as well as for operations
|
||
that need to know how to interpret the raw bits. When GC is scanning, and it
|
||
sees a header-word, then it can determine how to skip that data-block if
|
||
necessary. Header-Words are tagged as other-immediates. See the sections
|
||
"Other-Immediates" and "Data-Blocks and Header-Words" for comments on
|
||
distinguishing header-words from other-immediate data. This distinction is
|
||
necessary since we scan through data-blocks containing only descriptors just as
|
||
we scan through the heap looking for header-words introducing data-blocks.
|
||
|
||
Data-Blocks containing only descriptors do not require header-words for GC
|
||
since the entire data-block can be scanned by GC a word at a time, taking
|
||
whatever action is necessary or appropriate for the data in that slot. For
|
||
example, a cons is referenced by a descriptor with a specific tag, and the
|
||
system always knows the size of this data-block. When GC encounters a pointer
|
||
to a cons, it can transport it into the new space, and when scanning, it can
|
||
simply scan the two words manifesting the cons interpreting each word as a
|
||
descriptor. Actually there is no cons tag, but a list tag, so we make sure the
|
||
cons is not nil when appropriate. A header may still be desired if the pointer
|
||
to the data-block does not contain enough information to adequately maintain
|
||
the data-block. An example of this is a simple-vector containing only
|
||
descriptor slots, and we attach a header-word because the descriptor pointing
|
||
to the vector lacks necessary information -- the type of the vector's elements,
|
||
its length, etc.
|
||
|
||
There is no need for a major tag for GC forwarding pointers. Since the tag
|
||
bits are in the low end of the word, a range check on the start and end of old
|
||
space tells you if you need to move the thing. This is all GC overhead.
|
||
|
||
|
||
|
||
;;;; Structures.
|
||
|
||
Structures comprise a word for each slot in the definition in addition to one
|
||
word, a type slot which is a pointer descriptor. This points to a structure
|
||
describing the data-block as a structure, a defstruct-descriptor object. When
|
||
operating on a structure, doing a structure test can be done by simply checking
|
||
the tag bits on the pointer descriptor referencing it. As described in section
|
||
"GC Comments", data-blocks such as those representing structures may avoid
|
||
having a header-word since they are GC-scanable without any problem. This
|
||
saves two words for every structure instance.
|
||
|
||
|
||
|
||
;;;; Fixnums.
|
||
|
||
A fixnum has one of the following formats in 32 bits:
|
||
-------------------------------------------------------
|
||
| 30 bit 2's complement even integer | 0 0 0 |
|
||
-------------------------------------------------------
|
||
or
|
||
-------------------------------------------------------
|
||
| 30 bit 2's complement odd integer | 1 0 0 |
|
||
-------------------------------------------------------
|
||
|
||
Effectively, there is one tag for immediate integers, two zeros. This buys one
|
||
more bit for fixnums, and now when these numbers index into simple-vectors or
|
||
offset into memory, they point to word boundaries on 32-bit, byte-addressable
|
||
machines. That is, no shifting need occur to use the number directly as an
|
||
offset.
|
||
|
||
This format has another advantage on byte-addressable machines when fixnums are
|
||
offsets into vector-like data-blocks, including structures. Even though we
|
||
previously mentioned data-blocks are dual-word aligned, most indexing and slot
|
||
accessing is word aligned, and so are fixnums with effectively two tag bits.
|
||
|
||
Two tags also allow better usage of special instructions on some machines that
|
||
can deal with two low-tag bits but not three.
|
||
|
||
Since the two bits are zeros, we avoid having to mask them off before using the
|
||
words for arithmetic, but division and multiplication require special shifting.
|
||
|
||
|
||
|
||
;;;; Other-immediates.
|
||
|
||
An other-immediate has the following format:
|
||
----------------------------------------------------------------
|
||
| Data (24 bits) | Type (8 bits with low-tag) | 0 1 0 |
|
||
----------------------------------------------------------------
|
||
|
||
The system uses eight bits of type when checking types and defining system
|
||
constants. This allows allows for 32 distinct other-immediate objects given
|
||
the three low-tag bits tied down.
|
||
|
||
The system uses this format for characters, SYMBOL-VALUE unbound trap value,
|
||
and header-words for data-blocks on the heap. The type codes are laid out to
|
||
facilitate range checks for common subtypes; for example, all numbers will have
|
||
contiguous type codes which are distinct from the contiguous array type codes.
|
||
See section "Data-Blocks and Other-immediates Typing" for details.
|
||
|
||
|
||
|
||
;;;; Data-Blocks and Header-Word Format.
|
||
|
||
Pointers to data-blocks have the following format:
|
||
----------------------------------------------------------------
|
||
| Dual-word address of data-block (29 bits) | 1 1 1 |
|
||
----------------------------------------------------------------
|
||
|
||
The word pointed to by the above descriptor is a header-word, and it has the
|
||
same format as an other-immediate:
|
||
----------------------------------------------------------------
|
||
| Data (24 bits) | Type (8 bits with low-tag) | 0 1 0 |
|
||
----------------------------------------------------------------
|
||
|
||
This is convenient for scanning the heap when GC'ing, but it does mean that
|
||
whenever GC encounters an other-immediate word, it has to do a range check on
|
||
the low byte to see if it is a header-word or just a character (for example).
|
||
This is easily acceptable performance hit for scanning.
|
||
|
||
The system interprets the data portion of the header-word for non-vector
|
||
data-blocks as the word length excluding the header-word. For example, the
|
||
data field of the header for ratio and complex numbers is two, one word each
|
||
for the numerator and denominator or for the real and imaginary parts.
|
||
|
||
For vectors and data-blocks representing Lisp objects stored like vectors, the
|
||
system ignores the data portion of the header-word:
|
||
----------------------------------------------------------------
|
||
| Unused Data (24 bits) | Type (8 bits with low-tag) | 0 1 0 |
|
||
----------------------------------------------------------------
|
||
| Element Length of Vector (30 bits) | 0 0 |
|
||
----------------------------------------------------------------
|
||
|
||
Using a separate word allows for much larger vectors, and it allows LENGTH to
|
||
simply access a single word without masking or shifting. Similarly, the header
|
||
for complex arrays and vectors has a second word, following the header-word,
|
||
the system uses for the fill pointer, so computing the length of any array is
|
||
the same code sequence.
|
||
|
||
|
||
|
||
;;;; Data-Blocks and Other-immediates Typing.
|
||
|
||
These are the other-immediate types. We specify them including all low eight
|
||
bits, including the other-immediate tag, so we can think of the type bits as
|
||
one type -- not an other-immediate major type and a subtype. Also, fetching a
|
||
byte and comparing it against a constant is more efficient than wasting even a
|
||
small amount of time shifting out the other-immediate tag to compare against a
|
||
five bit constant.
|
||
|
||
Number (< 30)
|
||
00000 010 bignum 10
|
||
00000 010 ratio 14
|
||
00000 010 single-float 18
|
||
00000 010 double-float 22
|
||
00000 010 complex 26
|
||
|
||
Array (>= 30 code 86)
|
||
Simple-Array (>= 20 code 70)
|
||
00000 010 simple-array 30
|
||
Vector (>= 34 code 82)
|
||
00000 010 simple-string 34
|
||
00000 010 simple-bit-vector 38
|
||
00000 010 simple-vector 42
|
||
00000 010 (simple-array (unsigned-byte 2) (*)) 46
|
||
00000 010 (simple-array (unsigned-byte 4) (*)) 50
|
||
00000 010 (simple-array (unsigned-byte 8) (*)) 54
|
||
00000 010 (simple-array (unsigned-byte 16) (*)) 58
|
||
00000 010 (simple-array (unsigned-byte 32) (*)) 62
|
||
00000 010 (simple-array single-float (*)) 66
|
||
00000 010 (simple-array double-float (*)) 70
|
||
00000 010 complex-string 74
|
||
00000 010 complex-bit-vector 78
|
||
00000 010 (array * (*)) -- general complex vector. 82
|
||
00000 010 complex-array 86
|
||
|
||
00000 010 code-header-type 90
|
||
00000 010 function-header-type 94
|
||
00000 010 closure-header-type 98
|
||
00000 010 funcallable-instance-header-type 102
|
||
00000 010 unused-function-header-1-type 106
|
||
00000 010 unused-function-header-2-type 110
|
||
00000 010 unused-function-header-3-type 114
|
||
00000 010 closure-function-header-type 118
|
||
00000 010 return-pc-header-type 122
|
||
00000 010 value-cell-header-type 126
|
||
00000 010 symbol-header-type 130
|
||
00000 010 base-character-type 134
|
||
00000 010 system-area-pointer-type (header type) 138
|
||
00000 010 unbound-marker 142
|
||
00000 010 weak-pointer-type 146
|
||
|
||
|
||
|
||
;;;; Strings.
|
||
|
||
All strings in the system are C-null terminated. This saves copying the bytes
|
||
when calling out to C. The only time this wastes memory is when the string
|
||
contains a multiple of eight characters, and then the system allocates two more
|
||
words (since Lisp objects are dual-word aligned) to hold the C-null byte.
|
||
Since the system will make heavy use of C routines for systems calls and
|
||
libraries that save reimplementation of higher level operating system
|
||
functionality (such as pathname resolution or current directory computation),
|
||
saving on copying strings for C should make C call out more efficient.
|
||
|
||
The length word in a string header, see section "Data-Blocks and Header-Word
|
||
Format", counts only the characters truly in the Common Lisp string.
|
||
Allocation and GC will have to know to handle the extra C-null byte, and GC
|
||
already has to deal with rounding up various objects to dual-word alignment.
|
||
|
||
|
||
|
||
;;;; Symbols and NIL.
|
||
|
||
Symbol data-block has the following format:
|
||
-------------------------------------------------------
|
||
| 5 (data-block words) | Symbol Type (8 bits) |
|
||
-------------------------------------------------------
|
||
| Value Descriptor |
|
||
-------------------------------------------------------
|
||
| Function Pointer |
|
||
-------------------------------------------------------
|
||
| Raw Function Address |
|
||
-------------------------------------------------------
|
||
| Setf Function |
|
||
-------------------------------------------------------
|
||
| Property List |
|
||
-------------------------------------------------------
|
||
| Print Name |
|
||
-------------------------------------------------------
|
||
| Package |
|
||
-------------------------------------------------------
|
||
|
||
Most of these slots are self-explanatory given what symbols must do in Common
|
||
Lisp, but a couple require comments. We added the Raw Function Address slot to
|
||
speed up named call which is the most common calling convention. This is a
|
||
non-descriptor slot, but since objects are dual word aligned, the value
|
||
inherently has fixnum low-tag bits. The GC method for symbols must know to
|
||
update this slot. The Setf Function slot is currently unused, but we had an
|
||
extra slot due to adding Raw Function Address since objects must be dual-word
|
||
aligned.
|
||
|
||
The issues with nil are that we want it to act like a symbol, and we need list
|
||
operations such as CAR and CDR to be fast on it. CMU Common Lisp solves this
|
||
by putting nil as the first object in static space, where other global values
|
||
reside, so it has a known address in the system:
|
||
------------------------------------------------------- <-- start static
|
||
| 0 | space
|
||
-------------------------------------------------------
|
||
| 5 (data-block words) | Symbol Type (8 bits) |
|
||
------------------------------------------------------- <-- nil
|
||
| Value/CAR |
|
||
-------------------------------------------------------
|
||
| Definition/CDR |
|
||
-------------------------------------------------------
|
||
| Raw Function Address |
|
||
-------------------------------------------------------
|
||
| Setf Function |
|
||
-------------------------------------------------------
|
||
| Property List |
|
||
-------------------------------------------------------
|
||
| Print Name |
|
||
-------------------------------------------------------
|
||
| Package |
|
||
-------------------------------------------------------
|
||
| ... |
|
||
-------------------------------------------------------
|
||
In addition, we make the list typed pointer to nil actually point past the
|
||
header word of the nil symbol data-block. This has usefulness explained below.
|
||
The value and definition of nil are nil. Therefore, any reference to nil used
|
||
as a list has quick list type checking, and CAR and CDR can go right through
|
||
the first and second words as if nil were a cons object.
|
||
|
||
When there is a reference to nil used as a symbol, the system adds offsets to
|
||
the address the same as it does for any symbol. This works due to a
|
||
combination of nil pointing past the symbol header-word and the chosen list and
|
||
other-pointer type tags. The list type tag is four less than the other-pointer
|
||
type tag, but nil points four additional bytes into its symbol data-block.
|
||
|
||
|
||
|
||
;;;; Array Headers.
|
||
|
||
The array-header data-block has the following format:
|
||
----------------------------------------------------------------
|
||
| Header Len (24 bits) = Array Rank +5 | Array Type (8 bits) |
|
||
----------------------------------------------------------------
|
||
| Fill Pointer (30 bits) | 0 0 |
|
||
----------------------------------------------------------------
|
||
| Available Elements (30 bits) | 0 0 |
|
||
----------------------------------------------------------------
|
||
| Data Vector (29 bits) | 1 1 1 |
|
||
----------------------------------------------------------------
|
||
| Displacement (30 bits) | 0 0 |
|
||
----------------------------------------------------------------
|
||
| Displacedp (29 bits) -- t or nil | 1 1 1 |
|
||
----------------------------------------------------------------
|
||
| Range of First Index (30 bits) | 0 0 |
|
||
----------------------------------------------------------------
|
||
.
|
||
.
|
||
.
|
||
|
||
The array type in the header-word is one of the eight-bit patterns from section
|
||
"Data-Blocks and Other-immediates Typing", indicating that this is a complex
|
||
string, complex vector, complex bit-vector, or a multi-dimensional array. The
|
||
data portion of the other-immediate word is the length of the array header
|
||
data-block. Due to its format, its length is always five greater than the
|
||
array's number of dimensions. The following words have the following
|
||
interpretations and types:
|
||
Fill Pointer
|
||
This is a fixnum indicating the number of elements in the data vector
|
||
actually in use. This is the logical length of the array, and it is
|
||
typically the same value as the next slot. This is the second word, so
|
||
LENGTH of any array, with or without an array header, is just four bytes
|
||
off the pointer to it.
|
||
Available Elements
|
||
This is a fixnum indicating the number of elements for which there is
|
||
space in the data vector. This is greater than or equal to the logical
|
||
length of the array when it is a vector having a fill pointer.
|
||
Data Vector
|
||
This is a pointer descriptor referencing the actual data of the array.
|
||
This a data-block whose first word is a header-word with an array type as
|
||
described in sections "Data-Blocks and Header-Word Format" and
|
||
"Data-Blocks and Other-immediates Typing"
|
||
Displacement
|
||
This is a fixnum added to the computed row-major index for any array.
|
||
This is typically zero.
|
||
Displacedp
|
||
This is either t or nil. This is separate from the displacement slot, so
|
||
most array accesses can simply add in the displacement slot. The rare
|
||
need to know if an array is displaced costs one extra word in array
|
||
headers which probably aren't very frequent anyway.
|
||
Range of First Index
|
||
This is a fixnum indicating the number of elements in the first dimension
|
||
of the array. Legal index values are zero to one less than this number
|
||
inclusively. IF the array is zero-dimensional, this slot is
|
||
non-existent.
|
||
... (remaining slots)
|
||
There is an additional slot in the header for each dimension of the
|
||
array. These are the same as the Range of First Index slot.
|
||
|
||
|
||
|
||
;;;; Bignums.
|
||
|
||
Bignum data-blocks have the following format:
|
||
-------------------------------------------------------
|
||
| Length (24 bits) | Bignum Type (8 bits) |
|
||
-------------------------------------------------------
|
||
| least significant bits |
|
||
-------------------------------------------------------
|
||
.
|
||
.
|
||
.
|
||
|
||
The elements contain the two's complement representation of the integer with
|
||
the least significant bits in the first element or closer to the header. The
|
||
sign information is in the high end of the last element.
|
||
|
||
|
||
|
||
|
||
;;;; Code Data-Blocks.
|
||
|
||
A code data-block is the run-time representation of a "component". A component
|
||
is a connected portion of a program's flow graph that is compiled as a single
|
||
unit, and it contains code for many functions. Some of these functions are
|
||
callable from outside of the component, and these are termed "entry points".
|
||
|
||
Each entry point has an associated user-visible function data-block (of type
|
||
FUNCTION). The full call convention provides for calling an entry point
|
||
specified by a function object.
|
||
|
||
Although all of the function data-blocks for a component's entry points appear
|
||
to the user as distinct objects, the system keeps all of the code in a single
|
||
code data-block. The user-visible function object is actually a pointer into
|
||
the middle of a code data-block. This allows any control transfer within a
|
||
component to be done using a relative branch.
|
||
|
||
Besides a function object, there are other kinds of references into the middle
|
||
of a code data-block. Control transfer into a function also occurs at the
|
||
return-PC for a call. The system represents a return-PC somewhat similarly to
|
||
a function, so GC can also recognize a return-PC as a reference to a code
|
||
data-block.
|
||
|
||
It is incorrect to think of a code data-block as a concatenation of "function
|
||
data-blocks". Code for a function is not emitted in any particular order with
|
||
respect to that function's function-header (if any). The code following a
|
||
function-header may only be a branch to some other location where the
|
||
function's "real" definition is.
|
||
|
||
|
||
The following are the three kinds of pointers to code data-blocks:
|
||
Code pointer (labeled A below):
|
||
A code pointer is a descriptor, with other-pointer low-tag bits, pointing
|
||
to the beginning of the code data-block. The code pointer for the
|
||
currently running function is always kept in a register (CODE). In
|
||
addition to allowing loading of non-immediate constants, this also serves
|
||
to represent the currently running function to the debugger.
|
||
Return-PC (labeled B below):
|
||
The return-PC is a descriptor, with other-pointer low-tag bits, pointing
|
||
to a location for a function call. Note that this location contains no
|
||
descriptors other than the one word of immediate data, so GC can treat
|
||
return-PC locations the same as instructions.
|
||
Function (labeled C below):
|
||
A function is a descriptor, with function low-tag bits, that is user
|
||
callable. When a function header is referenced from a closure or from
|
||
the function header's self-pointer, the pointer has other-pointer low-tag
|
||
bits, instead of function low-tag bits. This ensures that the internal
|
||
function data-block associated with a closure appears to be uncallable
|
||
(although users should never see such an object anyway).
|
||
|
||
Information about functions that is only useful for entry points is kept
|
||
in some descriptors following the function's self-pointer descriptor.
|
||
All of these together with the function's header-word are known as the
|
||
"function header". GC must be able to locate the function header. We
|
||
provide for this by chaining together the function headers in a NIL
|
||
terminated list kept in a known slot in the code data-block.
|
||
|
||
|
||
A code data-block has the following format:
|
||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ <-- A
|
||
| Header-Word count (24 bits) | %Code-Type (8 bits) |
|
||
----------------------------------------------------------------
|
||
| Number of code words (fixnum tag) |
|
||
----------------------------------------------------------------
|
||
| Pointer to first function header (other-pointer tag) |
|
||
----------------------------------------------------------------
|
||
| Debug information (structure tag) |
|
||
----------------------------------------------------------------
|
||
| First constant (a descriptor) |
|
||
----------------------------------------------------------------
|
||
| ... |
|
||
----------------------------------------------------------------
|
||
| Last constant (and last word of code header) |
|
||
----------------------------------------------------------------
|
||
| Some instructions (non-descriptor) |
|
||
----------------------------------------------------------------
|
||
| (pad to dual-word boundary if necessary) |
|
||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ <-- B
|
||
| Word offset from code header (24) | %Return-PC-Type (8) |
|
||
----------------------------------------------------------------
|
||
| First instruction after return |
|
||
----------------------------------------------------------------
|
||
| ... more code and return-PC header-words |
|
||
----------------------------------------------------------------
|
||
| (pad to dual-word boundary if necessary) |
|
||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ <-- C
|
||
| Offset from code header (24) | %Function-Header-Type (8) |
|
||
----------------------------------------------------------------
|
||
| Self-pointer back to previous word (with other-pointer tag) |
|
||
----------------------------------------------------------------
|
||
| Pointer to next function (other-pointer low-tag) or NIL |
|
||
----------------------------------------------------------------
|
||
| Function name (a string or a symbol) |
|
||
----------------------------------------------------------------
|
||
| Function debug arglist (a string) |
|
||
----------------------------------------------------------------
|
||
| Function type (a list-style function type specifier) |
|
||
----------------------------------------------------------------
|
||
| Start of instructions for function (non-descriptor) |
|
||
----------------------------------------------------------------
|
||
| More function headers and instructions and return PCs, |
|
||
| until we reach the total size of header-words + code |
|
||
| words. |
|
||
----------------------------------------------------------------
|
||
|
||
|
||
The following are detailed slot descriptions:
|
||
Code data-block header-word:
|
||
The immediate data in the code data-block's header-word is the number of
|
||
leading descriptors in the code data-block, the fixed overhead words plus
|
||
the number of constants. The first non-descriptor word, some code,
|
||
appears at this word offset from the header.
|
||
Number of code words:
|
||
The total number of non-header-words in the code data-block. The total
|
||
word size of the code data-block is the sum of this slot and the
|
||
immediate header-word data of the previous slot. The system accesses
|
||
this slot with the system constant, %Code-Code-Size-Slot, offset from the
|
||
header-word.
|
||
Pointer to first function header:
|
||
A NIL-terminated list of the function headers for all entry points to
|
||
this component. The system accesses this slot with the system constant,
|
||
%Code-Entry-Points-Slot, offset from the header-word.
|
||
Debug information:
|
||
The DEBUG-INFO structure describing this component. All information that
|
||
the debugger wants to get from a running function is kept in this
|
||
structure. Since there are many functions, the current PC is used to
|
||
locate the appropriate debug information. The system keeps the debug
|
||
information separate from the function data-block, since the currently
|
||
running function may not be an entry point. There is no way to recover
|
||
the function object for the currently running function, since this
|
||
data-block may not exist. The system accesses this slot with the system
|
||
constant, %Code-Debug-Info-Slot, offset from the header-word.
|
||
First constant ... last constant:
|
||
These are the constants referenced by the component, if there are any.
|
||
The system accesses the first constant slot with the system constant,
|
||
%Code-Constants-Offset, offset from the header-word.
|
||
|
||
Return-PC header word:
|
||
The immediate header-word data is the word offset from the enclosing code
|
||
data-block's header-word to this word. This allows GC and the debugger
|
||
to easily recover the code data-block from a return-PC. The code at the
|
||
return point restores the current code pointer using a subtract immediate
|
||
of the offset, which is known at compile time.
|
||
|
||
Function entry point header-word:
|
||
The immediate header-word data is the word offset from the enclosing code
|
||
data-block's header-word to this word. This is the same as for the
|
||
retrun-PC header-word.
|
||
Self-pointer back to header-word:
|
||
In a non-closure function, this self-pointer to the previous header-word
|
||
allows the call sequence to always indirect through the second word in a
|
||
user callable function. See section "Closure Format". With a closure,
|
||
indirecting through the second word gets you a function header-word. The
|
||
system ignores this slot in the function header for a closure, since it
|
||
has already indirected once, and this slot could be some random thing
|
||
that causes an error if you jump to it. This pointer has an
|
||
other-pointer tag instead of a function pointer tag, indicating it is not
|
||
a user callable Lisp object. The system accesses this slot with the
|
||
system constant, %Function-Code-Slot, offset from the function
|
||
header-word.
|
||
Pointer to next function:
|
||
This is the next link in the thread of entry point functions found in
|
||
this component. This value is NIL when the current header is the last
|
||
entry point in the component. The system accesses this slot with the
|
||
system constant, %Function-Header-Next-Slot, offset from the function
|
||
header-word.
|
||
Function name:
|
||
This function's name (for printing). If the user defined this function
|
||
with DEFUN, then this is the defined symbol, otherwise it is a
|
||
descriptive string. The system accesses this slot with the system
|
||
constant, %Function-Header-Name-Slot, offset from the function
|
||
header-word.
|
||
Function debug arglist:
|
||
A printed string representing the function's argument list, for human
|
||
readability. If it is a macroexpansion function, then this is the
|
||
original DEFMACRO arglist, not the actual expander function arglist. The
|
||
system accesses this slot with the system constant,
|
||
%Function-Header-Debug-Arglist-Slot, offset from the function
|
||
header-word.
|
||
Function type:
|
||
A list-style function type specifier representing the argument signature
|
||
and return types for this function. For example,
|
||
(FUNCTION (FIXNUM FIXNUM FIXNUM) FIXNUM)
|
||
or
|
||
(FUNCTION (STRING &KEY (:START UNSIGNED-BYTE)) STRING)
|
||
This information is intended for machine readablilty, such as by the
|
||
compiler. The system accesses this slot with the system constant,
|
||
%Function-Header-Type-Slot, offset from the function header-word.
|
||
|
||
|
||
|
||
;;;; Closure Format.
|
||
|
||
A closure data-block has the following format:
|
||
----------------------------------------------------------------
|
||
| Word size (24 bits) | %Closure-Type (8 bits) |
|
||
----------------------------------------------------------------
|
||
| Pointer to function header (other-pointer low-tag) |
|
||
----------------------------------------------------------------
|
||
| . |
|
||
| Environment information |
|
||
| . |
|
||
----------------------------------------------------------------
|
||
|
||
|
||
A closure descriptor has function low-tag bits. This means that a descriptor
|
||
with function low-tag bits may point to either a function header or to a
|
||
closure. The idea is that any callable Lisp object has function low-tag bits.
|
||
Insofar as call is concerned, we make the format of closures and non-closure
|
||
functions compatible. This is the reason for the self-pointer in a function
|
||
header. Whenever you have a callable object, you just jump through the second
|
||
word, offset some bytes, and go.
|
||
|
||
|
||
|
||
;;;; Function call.
|
||
|
||
Due to alignment requirements and low-tag codes, it is not possible to use a
|
||
hardware call instruction to compute the return-PC. Instead the return-PC
|
||
for a call is computed by doing an add-immediate to the start of the code
|
||
data-block.
|
||
|
||
An advantage of using a single data-block to represent both the descriptor and
|
||
non-descriptor parts of a function is that both can be represented by a
|
||
single pointer. This reduces the number of memory accesses that have to be
|
||
done in a full call. For example, since the constant pool is implicit in a
|
||
return-PC, a call need only save the return-PC, rather than saving both the
|
||
return PC and the constant pool.
|
||
|
||
|
||
|
||
;;;; Memory Layout.
|
||
|
||
CMU Common Lisp has four spaces, read-only, static, dynamic-0, and dynamic-1.
|
||
Read-only contains objects that the system never modifies, moves, or reclaims.
|
||
Static space contains some global objects necessary for the system's runtime or
|
||
performance (since they are located at a known offset at a know address), and
|
||
the system never moves or reclaims these. However, GC does need to scan static
|
||
space for references to moved objects. Dynamic-0 and dynamic-1 are the two
|
||
heap areas for stop-and-copy GC algorithms.
|
||
|
||
What global objects are at the head of static space???
|
||
NIL
|
||
eval::*top-of-stack*
|
||
lisp::*current-catch-block*
|
||
lisp::*current-unwind-protect*
|
||
FLAGS (RT only)
|
||
BSP (RT only)
|
||
HEAP (RT only)
|
||
|
||
In addition to the above spaces, the system has a control stack, binding stack,
|
||
and a number stack. The binding stack contains pairs of descriptors, a symbol
|
||
and its previous value. The number stack is the same as the C stack, and the
|
||
system uses it for non-Lisp objects such as raw system pointers, saving
|
||
non-Lisp registers, parts of bignum computations, etc.
|
||
|
||
|
||
|
||
;;;; System Pointers.
|
||
|
||
The system pointers reference raw allocated memory, data returned by foreign
|
||
function calls, etc. The system uses these when you need a pointer to a
|
||
non-Lisp block of memory, using an other-pointer. This provides the greatest
|
||
flexibility by relieving contraints placed by having more direct references
|
||
that require descriptor type tags.
|
||
|
||
A system area pointer data-block has the following format:
|
||
-------------------------------------------------------
|
||
| 1 (data-block words) | SAP Type (8 bits) |
|
||
-------------------------------------------------------
|
||
| system area pointer |
|
||
-------------------------------------------------------
|
||
|
||
"SAP" means "system area pointer", and much of our code contains this naming
|
||
scheme. We don't currently restrict system pointers to one area of memory, but
|
||
if they do point onto the heap, it is up to the user to prevent being screwed
|
||
by GC or whatever.
|