* Vcells -*- outline -*- /Vcells/, short for /value cells/ or perhaps /variable cells/, are cells that contain the values of top-level variables, as well as their names and data necessary for interactive redefinition. Variables are used in two different ways: as operators in procedure calls, and either for value in another context or assigned. (The latter two cases are not considered separately.) Along with a value and an identifier, every vcell has two more fields, which are two weak alists mapping units to indices in the unit where the variable is used; the /locations alist/ is a list of all operator position uses, and each corresponding unit slot holds the procedure value itself (not a vcell indirection), so that procedure calls to top-level variables require one fewer heap indirection; and the /vcell locations alist/ is a list of locations of assignments or uses in operand positions. These alists are used for distributing replacement vcells when variables are redefined during interactive development or when existing DEFINE-bound variables are assigned (which is semantically an error, but a useful debugging operation). ** Fields Vcells contain six fields, and they occupy five cells of memory (which include the template): defined? (alt bit) Set if the variable represented by the vcell was made with DEFINE; clear if it was made with LSET. (If the vcell was anomalously created with SET in the interpreter on an undefined variable or SET-CONTENTS or on an undefined vcell, this is also clear; it is as if the variable had been created with LSET.) header byte (bits 8-15 of header, MREF-8-U -2 of object, byte offset 0 from extend descriptor as pointer) Zero if there are no uses of this variable for its value in an operator position; -1 (255) if there is at least one such use, so assignments must walk through the locations alist to update all of the unit slots that referred to the variable, since those slots do not use a vcell indirection but store the value directly. The header byte is maintained by the comex loader, where it is set if any VARIABLE-VALUE comex entry is found (see below), and by locales, which update the header byte whenever a vcell is created. It is checked by any of the variable assignment primops (in code generated by GENERATE-SET in (BACK_END machGEN), where mach is some machine) and by *SET in (OSYS machKERNEL). contents (slot 0, byte offset 2 from extend descriptor) Simply the value of the variable represented. If the variable is not bound, in which case the vcell may still exist (and expect to be linked at a later time), this contains a special /nonvalue/ token, which prints as #{Nonvalue} and which the interpreter checks and considers an error to use as a value. Orbit does not generate nonvalue checks, though. locations alist (slot 1, byte offset 6 from extend descriptor) Weak alist of (unit . index) pairs for all unit slots where this vcell is referred to in the operator position of procedure calls. Those corresponding unit slots contain the vcell's value directly, rather than the vcell. id (slot 2, byte offset 10 from extend descriptor) Symbolic identifier of the vcell. This is the name that a locale maps to the vcell. vcell locations alist (slot 3, byte offset 14 from extend descriptor) Weak alist of (unit . index) pairs for all unit slots where this vcell is used as a regular top-level variable, not only in the operator position of calls. Those corresponding unit slots contain references to the vcell. ** Memory layout extend descriptor pointer (with tag) | defined? bit slot# | value used? / header -------------------------------- / ----------- \ | / type \ tag -1 | unused byte | unused byte |x x x x x x x x|a|1 1 0 1 0|0 1| |------------ ^-- template -------- v-- contents ---------------| 0 | contents descriptor | |---------------------------------------------------------------| 1 | descriptor for pointer to locations alist | |---------------------------------------------------------------| 2 | descriptor for pointer to identifier | |---------------------------------------------------------------| 3 | descriptor for pointer to vcell locations alist | ----------------------------------------------------------------- msb lsb ** Vcells in units; comex ops Vcells are found most commonly in units, from which code within the units accesses the vcells & their values. There are four comex opcodes for storing vcells in units: comex opcode comex object VCELL-STORED-DEFINITION ( . ) The variable named is defined in the unit to be a value that is stored immediately within the unit, usually a procedure, in the slot of the unit. The vcell's contents field is set to be a pointer in the sense of MAKE-POINTER to the unit with an offset of . (The definition was made with DEFINE, not with LSET.) VCELL-DEFINED The variable named is defined in the unit with DEFINE (so the defined? bit is set), but its value is not stored immediately in the unit; rather, it is computed by the unit's code. It is not initialized when loading the comex from the disk; it is left up to the unit's code to initialize it. VCELL-LSET Like VCELL-DEFINED, but the variable was defined with LSET, and so it should be mutable (i.e. the defined? bit is clear). VCELL The variable named is referred to in the unit, but not defined by it. It should be resolved in the locale against which the comex is being linked or any of the locale's inferiors (i.e. resolution should not be local). The variable may be assigned in the unit or used for its value not in the operator position of a call. In all of the above cases, the location of the vcell in the comex/unit is recorded in the vcell's vcell location alist. VARIABLE-VALUE There is code in the unit to call the value of the variable named as a procedure, but not to use it for its non-procedural value or to assign it; the location in the unit is added to the locations alist (not the vcell locations alist). There may be two different slots in the unit for the same variable, one VARIABLE-VALUE and one VCELL, if it was defined in the unit and referred to only for its procedural value in operator positions in the unit; the definition serves as the one vcell reference. In no other context will there ever be simultaneously a VCELL and VARIABLE-VALUE slot for the same variable. ** Vcells as locatives Vcells behave as locatives, in that they answer true to the LOCATIVE? predicate, and they have CONTENTS & SET-CONTENTS methods. There are also two other operations on vcells, which have the same signature as SET-CONTENTS but different semantics: while SET-CONTENTS acts like LSET (in that it behaves as though it were a definition), DEFINE-CONTENTS acts as DEFINE, and *SET acts as SET. Mutation of vcells as locatives should be carried out with these operations and not with VCELL-CONTENTS or (SETTER VCELL-CONTENTS), because the methods for the locative operations perform nonvalue and rebinding checks and distribute link snappers (see below) if the locations alist is non-empty. (SETTER VCELL-CONTENTS) only sets the vcell object's contents slot, but does not distribute the value throughout the non-vcell locations alist, so operator position references will not be updated; and VCELL-CONTENTS does not perform nonvalue checks. (DEFINE-CONTENTS is actually a general operation, while *SET is a procedure specific to vcells. Note that *SET does not check that its first argument is a vcell; it blindly operates on its memory as if it were one. *SET is a LAP procedure defined in (OSYS machLAP).) ** Link snappers When a variable with a non-empty locations alist is assigned, the system walks the locations alist, for each unit location in the alist, to replace the contents of that slot in the unit with a /link snapper/ for the new value. Link snappers are procedure shells that remember the location of the unit slot they were stored in as well as the actual procedure; when called, they verify that that procedure is a procedure and then replace the contents of the unit slot (formerly the snapper) with the actual procedure. When vcells are made for VARIABLE-VALUE comex ops (see below), the corresponding unit slot for that op/object pair is initialized to be a link snapper with the resolved vcell's initial contents, or a nonvalue if the vcell was not yet bound. (The code that does this walk is the %SET label in (OSYS machKERNEL), where mach is some machine, which is stored in the SET slink slot. It is jumped to by any of the variable assignment primops and *SET.) Link snappers exist to provide simple run-time checking of linkage without requiring link-time checking & inhibiting forward references. If some compiled code calls a procedure, and its comex is instantiated before the procedure is actually defined, there should be a meaningful error signalled when that compiled code is run, but there should not be run-time overhead for checking procedurs on every call. By initially filling the unit location that that compiled code refers to with a link snapper, the check can be delayed until call-time (since at link-time the procedure might correctly not yet be vailable), but then, after the check is performed once, it need no longer be performed, so the link snapper removes its own overhead when it is finished. (Theoretically, the same concept as link snappers could probably be used to implement a sort of autoloading mechanism. I don't want to pursue this now, however, since it would undoubtedly introduce great complications with the introduction of a new module system.) Link snappers are objects with headers of *LINK-SNAPPER-TEMPLATE*; they are pooled in the SNAPPER-FREELIST slink slot. MAKE-LINK-SNAPPER constructs a link snapper (or fetches an existing one from the snapper freelist). Link snappers have null handlers. ** Redefinition When the user redefines a variable, a new substitute vcell is created, and the system grovels through the vcell locations alist to substitute a new vcell. (See DISTRIBUTE-VCELLS in (OSYS ENVIRONMENT).) Note that the system does not, though, grovel through the non-vcell locations alist, because that will occur anyway when the new vcell is assigned, as is explained in the next paragraph. When a variable is _assigned_, the vcell's contents slot is set, but if the locations alist is non-empty, it must be walked to substitute the new value in all units where the variable is used, because there is no vcell indirection. As an optimization, a byte in the vcell's header word tells whether the locations alist has any elements, so assignments to vcells walk the locations alist only if that byte is set. If a vcell is created by some definition (DEFINE or LSET) in a locale whose superior already binds that vcell's identifier, the vcell is /shadowed/, and the system walks down the tree of inferior locales, moving entries from the locations alist & vcell locations alist of the superior locale's vcell to the shadowing vcell in every unit whose environment is one of those inferior locales (or the one in which the shadowing vcell was created). This is done by FIX-LOCATION-LISTS in (OSYS ENVIRONMENT). /Virtual vcells/ represent vcells that should be bound in a superior locale, but are not. When a vcell is created by a call to ENV-LOOKUP, non-local and creating, and the vcell is not already found in the locale, a virtual vcell is created in the locale. This vcell is not actually bound in the locale, but the ENV-LOOKUP call returns it; instead, it is recorded in an internal table of virtual vcells. If a new vcell is created locally to a locale, and that locale or one of its inferiors had a virtual vcell for the new vcell's name, the new vcell is substituted for that virtual vcell, moving all entries in the locations and vcell locations alists from the virtual vcell to the new actual vcell, and the entry in that inferior locale's virtual vcell table is removed. Care must be taken to distribute vcells after potentially creating them if they were obtained by using ENV-LOOKUP and specifying that a vcell should be created if one does not exist already. If this does not happen, stale vcells might pervade throughout the image, never being updated. DISTRIBUTE-VCELLS does not distribute the vcell's value through the locations alist, however; this will happen when the vcell is initialized after its creation & redistribution throughout units in the image, and the value to distribute into the locations alist is not yet available anyway for DISTRIBUTE-VCELLS.