Next: Heap Object Layout, Up: Objects In Memory [Contents]
The in-memory representation of Lisp data includes type information about each object. This type information takes the form of a lowtag in the low bits of each pointer to heap space, a widetag for each boxed immediate value and a header (also with a widetag) at the start of the allocated space for each object. These tags are used to inform both the GC and Lisp code about the type and allocated size of Lisp objects.
Objects allocated on the Lisp heap are aligned to a double-word boundary, leaving the low-order bits (which would normally identify a particular octet within the first two words) available for use to hold type information. This turns out to be three bits on 32-bit systems and four bits on 64-bit systems.
Of these 8 or 16 tags, we have some constraints for allocation:
other-immediate
lowtag.
fixnum
s are required to have their lowtags be comprised
entirely of zeros.
Complicating this issue is that while the lowtag space is three
or four bits wide, some of the lowtags are effectively narrower. The
other-immediate
tags effectively have a two-bit lowtag, and
fixnum
s have historically been one bit narrower than the other
lowtags (thus even-fixnum-lowtag
and odd-fixnum-lowtag
)
though with the recent work on wider fixnums on 64-bit systems this is
no longer necessarily so.
The lowtags are specified in src/compiler/generic/early-objdef.lisp.
Fixnum
s are signed integers represented as immediate values.
In SBCL, these integers are (- n-word-bits n-fixnum-tag-bits)
bits wide, stored in the most-significant section of a machine word.
The reason that fixnum
tags are required to have the low
n-fixnum-tag-bits
as zeros is that it allows for addition and
subtraction to be performed using native machine instructions
directly, and multiplication and division can be performed likewise
using a simple shift instruction to compensate for the effect of the
tag.
Other-immediate
s are the lowtag part of widetag values. Due to
the constraints of widetag allocation, one out of every four lowtags
must be a widetag (alternately, the width of the
other-immediate
lowtag is two bits).
There are four different pointer lowtags, largely for optimization purposes.
cons
cell. This effectively halves the
memory cost of cons
cells.
funcall
or apply
of a function object.
Widetags are used for three purposes. First, to provide type information for immediate (non-pointer) data such as characters. Second, to provide “marker” values for things such as unbound slots. Third, to provide type information for objects stored on the heap.
Because widetags are used for immediate data they must have a lowtag
component. This ends up being the other-immediate
lowtags.
For various reasons it was deemed convenient for widetags to be no
more than eight bits wide, and with 27 or more distinct array types
(depending on build-time configuration), seven numeric types, markers,
and non-numeric heap object headers there ends up being more than 32
widetags required (though less than 64). This combination of factors
leads to the requirement that one out of every four lowtags be an
other-immediate
lowtag.
As widetags are involved in type tests for non-CLOS objects, their allocation is carefully arranged to allow for certain type tests to be cheaper than they might otherwise be.
rational
, float
,
real
, complex
and number
type tests become range
tests on the widetag.
stringp
type
test to become a masking operation followed by a range test or a
masking operation followed by a simple comparison.
The widetags are specified in src/compiler/generic/early-objdef.lisp.
Next: Heap Object Layout, Up: Objects In Memory [Contents]