language: infix-dylan module: dispatch-engine-internal Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define inline constant fixnum-dammit = method (x :: ) => (x :: ) x end; define constant = ; define constant callback-iep = mep; define function parent-gf (parent :: ) => (g :: ) select (parent by instance?) => let g :: = parent; g; => let c :: = parent; parent-gf(cache-header-engine-node-parent(c)); end select end function; define function dispatch-start (dispatch-starter :: ) => (engine) select (dispatch-starter by instance?) => let gf :: = dispatch-starter; discriminator(gf); => let e :: = dispatch-starter; cache-header-engine-node-next(e) | $absent-engine-node; end select end function; define function dispatch-start-setter (v, dispatch-starter :: ) => (engine) select (dispatch-starter by instance?) => let gf :: = dispatch-starter; discriminator(gf) := v; => let e :: = dispatch-starter; cache-header-engine-node-next(e) := v; end select end function; define function bootstrap-allocate-engine-node (entry-type :: , root-bits :: ) => (e :: ) let classes :: = *engine-node-classes*; let c :: = vector-element(classes, entry-type); bootstrap-typed-allocate-engine-node(c, entry-type, root-bits) end function; define function bootstrap-typed-allocate-engine-node (c :: , entry-type :: , root-bits :: ) let callbacks :: = *engine-node-callbacks*; let e :: = system-allocate-simple-instance(c); let extra-bits :: = logand(root-bits, lognot(properties$m-entry-type)); properties(e) := logior(extra-bits, entry-type); let callback? = vector-element(callbacks, entry-type); if (callback?) let callback :: = callback?; engine-node-callback(e) := callback-iep(callback); end if; e end function; define function bootstrap-allocate-and-initialize-engine-node (entry-type :: , root-bits :: ) => (e :: ); let e :: = bootstrap-allocate-engine-node(entry-type, root-bits); primitive-initialize-engine-node(e); e end function; define function standard-discriminator-bits (gf :: ) => (bits :: ); let sig :: = function-signature(gf); let props :: = ash(signature-number-required(sig), discriminator$v-nrequired); if (signature-optionals?(sig)) logior(props, discriminator$m-restp) else props end end function; define constant $standard-discriminator-bit-mask = logior(discriminator$m-nrequired, discriminator$m-restp); define inline function bootstrap-shared-allocate-discriminator (entry-type :: , argnum :: , root-bits :: , d :: ) => (d :: ) let callbacks :: = *engine-node-callbacks*; let props :: = logand(root-bits, lognot(logior(discriminator$m-argnum, properties$m-entry-type))); props := logior(props, entry-type); props := logior(props, fixnum-dammit(ash(argnum, discriminator$v-argnum))); let nreq :: = ash(logand(root-bits, discriminator$m-nrequired), - discriminator$v-nrequired); if (~(argnum < nreq)) // @@@@ (argnum >= nreq) error("Discriminator being created with conflicting nrequired %= and argnum %=.", nreq, argnum) end if; properties(d) := props; let callback? = vector-element(callbacks, entry-type); if (callback?) let callback :: = callback?; engine-node-callback(d) := callback-iep(callback); end if; d end function; define function bootstrap-allocate-discriminator (entry-type :: , argnum :: , root-bits :: ) => (d :: ) let classes :: = *engine-node-classes*; let c :: = vector-element(classes, entry-type); let d :: = system-allocate-simple-instance(c); bootstrap-shared-allocate-discriminator(entry-type, argnum, root-bits, d); end function; define function bootstrap-allocate-repeated-discriminator (entry-type :: , argnum :: , root-bits :: , size :: , default) => (d :: ) let classes :: = *engine-node-classes*; let c :: = vector-element(classes, entry-type); let d :: = system-allocate-repeated-object-instance(c, #f, size, default); bootstrap-shared-allocate-discriminator(entry-type, argnum, root-bits, d); end function; define open generic bletch (x :: ) => (); define open method bletch (x :: ) => () signal(x) end method; define open method bletch (x :: ) => () error(x) end method; define constant bletch-stack = method (l :: ) if (l == #()) #f else bletch-stack(tail(l)); bletch(head(l)); end if end method; /* ******************************************************* Object locking ******************************************************* */ // This is the general mechanism we use for locking individual objects // against writing. It doesn't need extra space in the object, nor a high // number of locks (two total), and hopefully it will be fast enough. I // see it being used for generic functions, maybe dispatch tables (I'm // undecided as to how generic function vs. dispatch table locking will be // done during discrimination fulfillment). I suspect class redefinition // may require a bigger and more exclusive club (not per-object). Maybe // this lock will revert to being a generic function only lock, and maybe // as a consequence it will implicitly lock out class redefinition, or // something like that. Anyway, it seems to be a reasonable paradigm for // being able to write-lock individual objects. define constant $object-lock-notification-lock :: = make-simple-lock(); define constant $object-lock-notification :: = make-notification($object-lock-notification-lock); define variable *object-lock-data* :: = #(); define inline-only function token-for-current-thread () => (a-token) if (*dylan-library-initialized?*) current-thread() else #t; // This token could be anything end if; end function; // define constant object-locked-p = method (obj) => (ans :: ); // iterate loop (l :: = *object-lock-data*) // if (l == #()) // #() // else // let couple :: = head(l); // if (pointer-id?(head(couple), obj)) // couple // else // let nxt :: = tail(l); // loop(nxt) // end if // end if // end iterate // end method; define constant multiple-objects-locked-p = method (cells :: , tokin) => (ans); if (*object-lock-data* == #()) #f else local method peruse (cells :: , recursive-losers :: ) if (cells == #()) if (recursive-losers == #()) #f else recursive-losers end else let cell :: = head(cells); let obj = head(cell); let nxt :: = tail(cells); local method checklock (l :: ) if (l == #()) peruse(nxt, recursive-losers) else let this :: = head(l); if (pointer-id?(obj, this)) if (pointer-id?(tokin, tail(this))) peruse(nxt, pair(obj, recursive-losers)) else #t end if else checklock(tail(l)) end if end if end method; checklock(*object-lock-data*) end if end method; peruse(cells, #()) end if end method; //define constant begin-locking-object = method (cell :: ) // let cell2 :: = head(cell); // let obj = head(cell2); // let lock :: = $object-lock-notification-lock; // let notif :: = $object-lock-notification; // with-lock (lock) // iterate try-again () // let lockedp :: = object-locked-p(obj); // if (lockedp == #()) // tail(cell) := *object-lock-data*; // *object-lock-data* := cell; // tail(cell2) := token-for-current-thread(); // release(notif); // cell // elseif (tail(lockedp) == token-for-current-thread()) // error("Attempt to recursively lock object %=", obj) // else // wait-for(notif); // try-again() // end if // end iterate // end with-lock //end method; define constant begin-locking-multiple-objects = method (hd :: , tl :: ) let lock :: = $object-lock-notification-lock; let notif :: = $object-lock-notification; let tokin = token-for-current-thread(); with-lock (lock) iterate try-again () let stuff = multiple-objects-locked-p(hd, tokin); if (stuff == #f) for (x :: in hd) tail(x) := tokin end; tail(tl) := *object-lock-data*; *object-lock-data* := hd; release(notif); elseif (stuff == #t) wait-for(notif); try-again() else error("Attempt to recursively lock objects %=", stuff) end if end iterate end with-lock end method; define constant end-locking-object-cell = method (cell :: ) let data :: = *object-lock-data*; let first-l :: = tail(data); if (cell == data) *object-lock-data* := tail(data) else iterate sigh (prev :: = data, l :: = first-l) if (l == #()) #f // This means we aborted before getting the lock. else let nxt :: = tail(l); if (l == cell) tail(prev) := nxt; else sigh(l, nxt) end if end if end iterate end if; end method; //define constant end-locking-object = method (cell :: ) // let lock :: = $object-lock-notification-lock; // let notif :: = $object-lock-notification; // with-lock (lock) // end-locking-object-cell(cell); // release(notif); // end with-lock; // values() //end method; define constant end-locking-multiple-objects = method (hd :: , tl :: ) => () let lock :: = $object-lock-notification-lock; let notif :: = $object-lock-notification; with-lock (lock) block(done) for (x :: = hd then tail(x)) end-locking-object-cell(x); if (x == tl) done() end; end for; end; release(notif); end with-lock; values() end method; define macro with-object-lock { with-object-lock (?object:expression) ?body:body end } => // { begin // let $cell$ = pair(pair(?object, #f), #()); // block () // begin-locking-object($cell$); // ?body; // cleanup // end-locking-object($cell$); // end block // end // } { begin let _objlist = pair(pair(?object, #f), #()); block () begin-locking-multiple-objects(_objlist, _objlist); ?body; cleanup end-locking-multiple-objects(_objlist, _objlist); end block end } { with-object-lock (?object:expression, ?punter:name) ?body:body end } => { begin let _objlist = pair(pair(?object, #f), #()); block (?punter) begin-locking-multiple-objects(_objlist, _objlist); ?body; cleanup end-locking-multiple-objects(_objlist, _objlist); end block end } end macro; define inline-only function make-multiple-object-lock-cells (seq :: ) => (h :: , t :: ) let t = #(); for (x in seq, ans :: = #() then pair(pair(x, #f), ans)) if (t == #()) t := ans end; finally values(ans, if (t == #()) ans else t end) end for; end function; define macro with-multiple-object-lock { with-multiple-object-lock (?objectsequence:expression) ?body:body end } => { begin let _objseq = ?objectsequence; if (~empty?(_objseq)) let (_head :: , _tail :: ) = make-multiple-object-lock-cells(_objseq); block () begin-locking-multiple-objects(_head, _tail); ?body; cleanup end-locking-multiple-objects(_head, _tail); end block end if end } end macro; /* ******************************* Simple, terminal discriminators ******************************* */ define constant %gf-dispatch-absent = method (mepargs :: , e :: , parent :: ) handle-missed-dispatch(e, parent, mepargs) end method; define constant %gf-dispatch-inapplicable = method (spreadargs :: , e :: , parent :: ) e; no-applicable-method-error(parent-gf(parent), copy-sequence(spreadargs)) end method; define constant %gf-dispatch-ambiguous-methods = method (spreadargs :: , e :: , parent :: ) ambiguous-method-error(parent-gf(parent), copy-sequence(spreadargs), ambiguous-methods-engine-node-ordered(e), ambiguous-methods-engine-node-ambig(e)) end method; define constant make-ambiguous-methods-engine-node = method (ordered :: , ambig :: ) => (e :: ); let e :: = bootstrap-allocate-engine-node(engine-node$k-ambiguous-methods, 0); ambiguous-methods-engine-node-ordered(e) := ordered; ambiguous-methods-engine-node-ambig(e) := ambig; primitive-initialize-engine-node(e); e end method; define inline constant make-ambiguous-methods-next-method = method (ordered :: , ambig :: , gf :: ) => (p :: ); pair(make-ambiguous-methods-engine-node(ordered, ambig), gf) end method; define sealed inline method make (c == , #key meth :: , data, keys) => (e :: ); c; make-single-method-engine-node(meth, data: data, keys: keys) end method; define constant make-single-method-engine-node = method (meth :: , #key data, keys) // @@@@@ The method here is known to not be an , so can // use a more specialized version of function-signature when one is available. let sig :: = function-signature(meth); let bits :: = ash(signature-number-required(sig), smen$v-nrequired); let bits :: = if (signature-optionals?(sig)) logior(smen$m-restp, bits) else bits end; let sme :: = if (keys == #f) bootstrap-allocate-engine-node(engine-node$k-unkeyed-single-method, bits) elseif (keys == #t) bootstrap-allocate-engine-node(engine-node$k-unrestricted-keyed-single-method, bits) else let keys :: = keys; let mkeys :: = keyword-specifiers(meth); let nkeys :: = size(keys); let nmkeys :: = size(mkeys); if (nkeys = ash(nmkeys, -1) & begin local method outer (i :: ) if (i = nkeys) #t else let k :: = vector-element(keys, i); local method inner (j :: ) if (j = nmkeys) #f elseif (pointer-id?(k, vector-element(mkeys, j))) outer(i + 1) else inner(j + 2) end if end method; inner(0) end if end method; outer(0) end) bootstrap-allocate-engine-node(engine-node$k-implicit-keyed-single-method, bits) else let e :: = bootstrap-allocate-engine-node(engine-node$k-explicit-keyed-single-method, bits); single-method-engine-node-keys(e) := keys; e end if end if; single-method-engine-node-method(sme) := meth; single-method-engine-node-data(sme) := data; primitive-initialize-engine-node(sme); sme end method; /* ***************** Dispatch by Class ***************** */ define constant ckd$v-log2size = discriminator$v-data-start; define constant ckd$s-log2size = 5; define macro with-lckd-dispatch { with-lckd-dispatch (?d:name) ?:body end } => { if (instance?(?d, )) let ?d :: = ?d; ?body else let ?d :: = ?d; ?body end if } end macro; define macro with-hckd-dispatch { with-hckd-dispatch (?d:name) ?:body end } => { if (instance?(?d, )) let ?d :: = ?d; ?body else let ?d :: = ?d; ?body end if } end macro; define macro with-ckd-dispatch { with-ckd-dispatch (?d:name) ?:body end } => { if (instance?(?d, )) let ?d :: = ?d; with-lckd-dispatch (?d) ?body end else let ?d :: = ?d; with-hckd-dispatch (?d) ?body end end if } end macro; define inline function %ckd-ref (ckd :: , idx :: ) class-keyed-discriminator-table-element(ckd, idx) end function; define inline function %ckd-ref-setter (value, ckd :: , idx :: ) class-keyed-discriminator-table-element(ckd, idx) := value end function; define inline function %ckd-size (ckd :: ) => (key-and-value-vector-size :: ) class-keyed-discriminator-table-size(ckd) end function; define inline function %ckd-mask (ckd :: ) => (mask :: ) class-keyed-discriminator-table-size(ckd) - 2 end function; // @@@@ Variable because Apple Dylan compiler copies value and loses eqness! // @@@@ Keep it that way - better safe than sorry. define variable $ckd-empty = #(#"*empty*"); // How big we let a linear discrimination table get. Units are total number // of data entries, twice the number of key/value pairs. define constant $linear-discriminator-table-limit :: = 2 * 5; define constant $linear-discriminator-table-growth-increment :: = 2 * 2; define function hashed-class-keyed-discriminator-log2size (storage-size :: ) => (table-size :: ) local method f (i :: ) let nxt :: = i + 1; let siz :: = %twopower(i); // @@@@ if (siz > storage-size) nxt else f(nxt) end if (storage-size < siz) nxt else f(nxt) end end method; f(4) end function; define inline function grow-linear-class-keyed-discriminator (d :: ) => (nd :: ) let stgsiz :: = %ckd-size(d); let nstgsiz :: = stgsiz + $linear-discriminator-table-growth-increment; let initbits :: = logand(properties(d), $standard-discriminator-bit-mask); dbg("grow-linear-class-keyed-discriminator %= - stgsiz=%=, nstgsiz=%=", d, stgsiz, nstgsiz); let nd :: = if (~(nstgsiz < $linear-discriminator-table-limit)) // @@@@ (nstgsiz >= $linear-discriminator-table-limit) make-hashed-class-keyed-discriminator(logior(engine-node-function-code(d), 1), discriminator-argnum(d), hashed-class-keyed-discriminator-log2size(nstgsiz), initbits) else make-linear-class-keyed-discriminator(logand(engine-node-function-code(d), -2), discriminator-argnum(d), nstgsiz, initbits) end if; copy-class-keyed-discriminator-attributes(d, nd); local method loop (i :: , nd :: ) if (i = stgsiz) nd else loop(i + 2, ckd-add!(nd, %ckd-ref(d, i), %ckd-ref(d, i + 1))) end if end method; loop(0, nd); end function; define inline function grow-hashed-class-keyed-discriminator (d :: ) => (nd :: ) let stgsiz :: = %ckd-size(d); let log2stgsize = %load-byte(ckd$v-log2size, ckd$s-log2size, properties(d)); let nstgsiz :: = ash(stgsiz, 1); let initbits :: = logand(properties(d), $standard-discriminator-bit-mask); dbg("grow-hashedclass-keyed-discriminator %= - stgsiz=%=, log2=%=, nstgsiz=%=", d, stgsiz, log2stgsize, nstgsiz); let nd :: = make-hashed-class-keyed-discriminator(engine-node-function-code(d), discriminator-argnum(d), log2stgsize + 1, initbits); copy-class-keyed-discriminator-attributes(d, nd); local method loop (i :: , nd :: ) if (i = stgsiz) nd else let k = %ckd-ref(d, i); if (pointer-id?(k, $ckd-empty)) loop (i + 2, nd) else loop(i + 2, ckd-add!(nd, k, %ckd-ref(d, i + 1))) end if end if end method; loop (0, nd) end function; define constant copy-class-keyed-discriminator-attributes = method (d :: , nd :: ) => (); if (instance?(d, )) grounded-class-keyed-discriminator-default(nd) := grounded-class-keyed-discriminator-default(d); end if; end method; define constant grounded-class-keyed-discriminator-default = method (d :: ) => (nd :: ) select (d by instance?) , , => $absent-engine-node; => let d :: = d; class-keyed-discriminator-default(d); => let d :: = d; class-keyed-discriminator-default(d); end select end method; define constant grounded-class-keyed-discriminator-default-setter = method (value :: , d :: ) => (nd :: ) select (d by instance?) => let d :: = d; class-keyed-discriminator-default(d) := value; => let d :: = d; class-keyed-discriminator-default(d) := value; end select end method; define function make-linear-class-keyed-discriminator (code :: , argnum :: , table-size :: , extra-bits :: ) => (discriminator :: ) dbg("make-linear-class-keyed-discriminator %= %= %=", code, argnum, table-size); let d :: = bootstrap-allocate-repeated-discriminator(code, argnum, extra-bits, table-size, $ckd-empty); lckd-index(d) := 0; primitive-initialize-discriminator(d); d end function; define function make-hashed-class-keyed-discriminator (code :: , argnum :: , log2size :: , extra-bits :: ) => (discriminator :: ); dbg("make-hashed-class-keyed-discriminator %= %= %=", code, argnum, log2size); let bitz :: = logior(extra-bits, ash(log2size, ckd$v-log2size)); let table-size :: = %twopower(log2size); let d :: = bootstrap-allocate-repeated-discriminator(code, argnum, bitz, table-size, $ckd-empty); primitive-initialize-discriminator(d); d end function; define function make-initial-class-keyed-discriminator (code :: , argnum :: , gf :: , number-of-keys :: ) dbg("make-initial-class-keyed-discriminator %= %= %=", code, argnum, number-of-keys); let stgsiz :: = ash(number-of-keys, 1); let bitz :: = standard-discriminator-bits(gf); if (~(stgsiz < $linear-discriminator-table-limit)) // @@@@ (stgsiz >= $linear-discriminator-table-limit) make-hashed-class-keyed-discriminator(logior(code, 1), argnum, hashed-class-keyed-discriminator-log2size(stgsiz), bitz) else make-linear-class-keyed-discriminator(logand(code, -2), argnum, logand(stgsiz + 3, -8), bitz) end if end function; define function make-by-class-discriminator (argnum :: , gf :: , number-of-keys :: ) => (d :: ); if (number-of-keys == 1) make-monomorphic-by-class-discriminator(argnum, gf) else make-initial-class-keyed-discriminator(engine-node$k-linear-by-class, argnum, gf, number-of-keys) end if end function; define function make-by-singleton-class-discriminator (argnum :: , gf :: , number-of-keys :: , default) => (d :: ); let d :: = make-initial-class-keyed-discriminator(engine-node$k-linear-by-singleton-class, argnum, gf, number-of-keys); grounded-class-keyed-discriminator-default(d) := default; d end function; define inline function linear-class-key-lookup (key :: , d :: , default) let n :: = %ckd-size(d); local method loop (i :: ) if (i = n) default else let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) %ckd-ref(d, i + 1) // elseif (pointer-id?(otherkey, $ckd-empty)) // default else loop(i + 2) end if end if end method; loop(0) end function; /* define inline function linear-class-key-lookup (key :: , d :: , default) => (ans) let firsti :: = lckd-index(d); let otherkey = %ckd-ref(d, firsti); if (pointer-id?(otherkey, key)) %ckd-ref(d, firsti + 1) else let n :: = %ckd-size(d); local method loop (i :: ) let i :: = i + 2; let i :: = if (i == n) 0 else i end; if (i == firsti) default else let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) lckd-index(d) := i; %ckd-ref(d, i + 1) else loop(i) end if end if end method; loop(firsti) end if end function; */ define function grounded-linear-class-key-lookup (key :: , d :: , default) with-lckd-dispatch (d) linear-class-key-lookup(key, d, default); end with-lckd-dispatch; end function; define function %gf-dispatch-linear-by-class (arg, parent :: , d :: ) parent; linear-class-key-lookup(object-class-unique-key(arg), d, $absent-engine-node) end function; define function %gf-dispatch-linear-by-singleton-class (arg :: , parent :: , d :: ) parent; linear-class-key-lookup(class-unique-key(arg), d, class-keyed-discriminator-default(d)) end function; /* ****** Hashing. ****** * We use a power of 2 hash table size - avoid division in computing the hash index. * The hash index is computed by masking something of the wrapper. It may be an incrementally assigned index, or it may come from the address, but we assume it is unchanging over time (we don't want to have to rehash, at least not commonly). It does need to be a per-wrapper (maybe per-implementation-class), not per-class, key, if we want to allow any kind of lazy invalidation. The number used to compute the first hash probe index is simply masked by the bitmask for the size of the table. Were it coming from an address, a small right-shift might be appropriate. * We compute a step for successive probes in the event of a hash conflict. To do this, we start with the same number which was used to compute the initial probe, but select the bits just to the left of the ones used to compute the first index. This maximizes the chance that they are different when the lower set of bits are the same. Some set of these bits are used to index into a table of prime numbers. The result only has to be odd in order step through all positions in the table, so this masking will decrease the optimality of the second hash in smaller tables, but not remove it entirely. * The table is referenced as alternating keys and values at successive indices, so all numbers indicating positions/probes/steps are returned scaled appropriately by the routines which generate them. */ define inline function %hckd-first-index (key :: , d :: ) => (index :: ) logand(key, %ckd-mask(d)) end function; // See %hckd-hash-step. define inline constant %second-hash-values = method () => (v :: ) #[2, // index = 0, step = 1 6, // index = 1, Step = 3 10, // index = 2, Step = 5 14, // index = 3, Step = 7 22, // index = 4, step = 11 26, // index = 5, step = 13 34, // index = 6, step = 17 38, // index = 7, step = 19 46, // index = 8, step = 23 58, // index = 9, step = 29 62, // index = 10, step = 31 74, // index = 11, step = 37 82, // index = 12, step = 41 86, // index = 13, step = 43 94, // index = 14, step = 47 106 // index = 15, step = 53 ] end method; // Mask to compute index mod the size of $second-hash-values. define constant $second-hash-mask :: = 15; define inline function %hckd-hash-step (key :: , d :: ) => (step :: , mask :: ) let log2size :: = %load-byte(ckd$v-log2size, ckd$s-log2size, properties(d)); values(vector-element(%second-hash-values(), logand(%scale-down(key, log2size), $second-hash-mask)), %twopower(log2size) - 2) end function; define inline function %hckd-next-index (i :: , step :: , mask :: ) => (idx :: ) let next :: = i + step; logand(next, mask) end function; define inline function hashed-class-key-lookup (key :: , d :: , default) => (discriminator :: ); let i :: = %hckd-first-index(key, d); let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) %ckd-ref(d, i + 1) elseif (pointer-id?(otherkey, $ckd-empty)) default else let (step :: , mask :: ) = %hckd-hash-step(key, d); local method loop (i :: ) let i :: = %hckd-next-index(i, step, mask); let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) %ckd-ref(d, i + 1) elseif (pointer-id?(otherkey, $ckd-empty)) default else loop(i) end if end method; loop(i) end if end function; define function grounded-hashed-class-key-lookup (key :: , d :: , default) with-hckd-dispatch (d) hashed-class-key-lookup(key, d, default); end with-hckd-dispatch; end function; define function %gf-dispatch-hashed-by-class (arg, parent :: , d :: ) parent; hashed-class-key-lookup(object-class-unique-key(arg), d, $absent-engine-node) end function; define function %gf-dispatch-hashed-by-singleton-class (arg :: , parent :: , d :: ) parent; hashed-class-key-lookup(class-unique-key(arg), d, class-keyed-discriminator-default(d)) end function; define function ckd-lookup (key, d :: ) => (ans :: ) let default = grounded-class-keyed-discriminator-default(d); if (instance?(d, )) let d :: = d; if (monomorphic-by-class-discriminator-key(d) == key) monomorphic-by-class-discriminator-next(d) else default end if else if (instance?(d, )) let d :: = d; grounded-linear-class-key-lookup(key, d, default) else let d :: = d; grounded-hashed-class-key-lookup(key, d, default) end if end if end function; define function ckd-ref (d :: , index :: ) => (value); with-ckd-dispatch (d) %ckd-ref(d, index) end with-ckd-dispatch; end function; define function ckd-ref-setter (value, d :: , index :: ) with-ckd-dispatch (d) %ckd-ref(d, index) := value; end with-ckd-dispatch; end function; define function ckd-size (d :: ) => (value); with-ckd-dispatch (d) %ckd-size(d) end with-ckd-dispatch; end function; define function ckd-add! (d :: , key, value) => (discriminator :: ); dbg("ckd-add(%=, %=, %=)", d, key, value); let ans :: = if (instance?(d, )) let d :: = d; grounded-lckd-add!(d, key, value) elseif (instance?(d, )) let d :: = d; grounded-mckd-add!(d, key, value); else let d :: = d; grounded-hckd-add!(d, key, value) end if; dbg(if(ans == d) "ckd-add! same discriminator" else "ckd-add! new discriminator" end); ans end function; // define constant $initial-linear-size = 4; define function mckd-add! (d :: , key, value) => (discriminator :: ); let old-key = monomorphic-by-class-discriminator-key(d); if (pointer-id?(old-key, $ckd-empty)) monomorphic-by-class-discriminator-key(d) := key; monomorphic-by-class-discriminator-next(d) := value; d elseif (pointer-id?(old-key, key)) monomorphic-by-class-discriminator-next(d) := value; d else let new-d :: = make-linear-class-keyed-discriminator (engine-node$k-linear-by-class, discriminator-argnum(d), 2, logand(properties(d), $standard-discriminator-bit-mask)); ckd-add!(lckd-add!(new-d, monomorphic-by-class-discriminator-key(d), monomorphic-by-class-discriminator-next(d)), key, value) end if; end function; define constant grounded-mckd-add! = mckd-add!; define inline function hckd-add! (d :: , key, value) => (discriminator :: ); let (step :: , mask :: ) = %hckd-hash-step(key, d); let enlarge? = method (d :: , nprobes :: ) let siz :: = %ckd-size(d); // Note siz is number of entries * 2. local method count (i :: , cnt :: ) if (i == siz) let quarterfull :: = ash(siz, -3); if (~(quarterfull < cnt)) // @@@@ (cnt <= quarterfull) #f // The table is less than 1/4 full. else let halffull :: = ash(siz, -2); if (cnt <= halffull) // Table less than 1/2 full, tolerate 7 collisions. nprobes > 7 else // Table is more than 1/2 full. Grow if the hash chain will be // more than 4 long, or if the table gets 3/4 full. let threequarters :: = quarterfull + halffull; (cnt >= threequarters) | (nprobes > 4) end if end if else count(i + 2, if (%ckd-ref(d, i) == $ckd-empty) cnt else cnt + 1 end if) end if end method; count(0, 0) end method; local method loop (i :: , nprobes :: ) let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) %ckd-ref(d, i + 1) := value; d elseif (~pointer-id?(otherkey, $ckd-empty)) loop(%hckd-next-index(i, step, mask), nprobes + 1) elseif (enlarge?(d, nprobes)) let nd :: = grow-hashed-class-keyed-discriminator(d); grounded-hckd-add!(nd, key, value) else %ckd-ref(d, i + 1) := value; sequence-point(); %ckd-ref(d, i) := key; d end if end method; loop(%hckd-first-index(key, d), 0) end function; define function grounded-hckd-add! (d :: , key, value) => (discriminator :: ); with-hckd-dispatch (d) hckd-add!(d, key, value); end with-hckd-dispatch; end function; define inline function lckd-add! (d :: , key, value) => (possibly-new-discriminator :: ); // *** The g.f. is assumed to be locked; no one else is permitted // *** to be mucking around with the table. let n :: = %ckd-size(d); local method loop (i :: ) if (i = n) let nd :: = grow-linear-class-keyed-discriminator(d); ckd-add!(nd, key, value) elseif (pointer-id?(%ckd-ref(d, i), $ckd-empty)) %ckd-ref(d, i + 1) := value; sequence-point(); %ckd-ref(d, i) := key; d elseif (pointer-id?(%ckd-ref(d, i), key)) %ckd-ref(d, i + 1) := value; d else loop (i + 2) end if end method; loop(0) end function; define function grounded-lckd-add! (d :: , key, value) => (discriminator :: ); with-lckd-dispatch (d) lckd-add!(d, key, value); end with-lckd-dispatch; end function; // define engine-node-slot typecheck-discriminator-type engine-node-data-1; // define engine-node-slot typecheck-discriminator-next engine-node-data-2; define constant %gf-dispatch-typecheck = method (arg, parent :: , d :: ) parent; if (primitive-instance?(arg, typecheck-discriminator-type(d))) typecheck-discriminator-next(d) else $inapplicable-engine-node end if end method; define constant make-typecheck-discriminator = method (argnum :: , gf :: , t :: , next :: ) => (d :: ) // let d :: // = bootstrap-allocate-discriminator(engine-node$k-typecheck, argnum, // standard-discriminator-bits(gf)); // typecheck-discriminator-type(d) := type; // typecheck-discriminator-next(d) := next; // primitive-initialize-discriminator(d); // d make-if-type-discriminator(argnum, gf, t, next, $inapplicable-engine-node) end method; define constant make-monomorphic-by-class-discriminator = method (argnum :: , gf :: /* , ic :: , next :: */) => (d :: ) let d :: = bootstrap-allocate-discriminator(engine-node$k-monomorphic-by-class, argnum, standard-discriminator-bits(gf)); monomorphic-by-class-discriminator-key(d) := $ckd-empty; // let key = iclass-unique-key(ic); // monomorphic-by-class-discriminator-key(d) := key; // monomorphic-by-class-discriminator-next(d) := next; primitive-initialize-discriminator(d); d end method; // define engine-node-slot if-type-discriminator-type engine-node-data-1; // define engine-node-slot if-type-discriminator-then engine-node-data-2; // define engine-node-slot if-type-discriminator-else engine-node-data-3; define constant %gf-dispatch-if-type = method (arg, parent :: , disp :: ) parent; if (primitive-instance?(arg, if-type-discriminator-type(disp))) if-type-discriminator-then(disp) else if-type-discriminator-else(disp) end if end method; define constant make-if-type-discriminator = method (argnum :: , gf :: , type :: , thend :: , elsed :: ) let d :: = bootstrap-allocate-discriminator(engine-node$k-if-type, argnum, standard-discriminator-bits(gf)); if-type-discriminator-type(d) := type; if-type-discriminator-then(d) := thend; if-type-discriminator-else(d) := elsed; primitive-initialize-discriminator(d); d end method; /* Singleton Dispatch */ //define engine-node-slot singleton-discriminator-table // // engine-node-data-1; //define engine-node-slot singleton-discriminator-default // // engine-node-data-2; define constant make-linear-singleton-discriminator = method (entry-type :: , argnum :: , gf :: , keys :: , nkeys :: ) => (d :: ) let len :: = ash(nkeys, 1); let v :: = make(, size: len, fill: $absent-engine-node); let d :: = bootstrap-allocate-discriminator(entry-type, argnum, standard-discriminator-bits(gf)); singleton-discriminator-table(d) := v; singleton-discriminator-default(d) := $absent-engine-node; lsd-index(d) := 0; local method loop(i :: , l :: ) unless (l == #()) if (~(i < len)) // @@@@ (i >= len) error("fmh") else vector-element(v, i) := head(l); let nxt :: = tail(l); loop(i + 2, nxt) end if end unless end method; loop(0, keys); primitive-initialize-discriminator(d); d end method; /* define inline function immediate-linear-singleton-discriminator-element (d :: , key, default) => (val :: ) let table :: = singleton-discriminator-table(d); let n :: = size(table); local method loop (i :: ) if (i = n) default else let k = vector-element(table, i); if (pointer-id?(k, key)) vector-element(table, i + 1); else loop(i + 2) end if end if end method; loop(0) end function; */ define inline function immediate-linear-singleton-discriminator-element (d :: , key, default) => (val :: ) let table :: = singleton-discriminator-table(d); let n :: = size(table); let firsti :: = lsd-index(d); let k = vector-element(table, firsti); if (pointer-id?(k, key)) vector-element(table, firsti + 1) else local method loop (i :: ) let i :: = i + 2; let i :: = if (i == n) 0 else i end; if (i == firsti) default else let k = vector-element(table, i); if (pointer-id?(k, key)) lsd-index(d) := i; vector-element(table, i + 1) else loop(i) end if end if end method; loop(firsti) end if end function; /* define inline function value-object-linear-singleton-discriminator-element (d :: , key, default) => (val :: ) let table :: = singleton-discriminator-table(d); let n :: = size(table); local method loop (i :: ) if (i = n) default else let k = vector-element(table, i); if (k ~== $absent-engine-node & k = key) vector-element(table, i + 1); else loop(i + 2) end if end if end method; loop(0) end function; */ define inline function value-object-linear-singleton-discriminator-element (d :: , key, default) => (val :: ) let table :: = singleton-discriminator-table(d); let n :: = size(table); let firsti :: = lsd-index(d); let k = vector-element(table, firsti); if (k = key) vector-element(table, firsti + 1) else local method loop (i :: ) let i :: = i + 2; let i :: = if (i == n) 0 else i end; if (i == firsti) default else let k = vector-element(table, i); if (k ~== $absent-engine-node & k = key) lsd-index(d) := i; vector-element(table, i + 1) else loop(i) end if end if end method; loop(firsti) end if end function; define constant linear-singleton-discriminator-element-setter = method (value, d :: , key) let table :: = singleton-discriminator-table(d); let n :: = size(table); local method loop (i :: ) if (i == n) error("key not found") else let k = vector-element(table, i); if (k == key) lsd-index(d) := i; vector-element(table, i + 1) := value else loop(i + 2) end if end if end method; loop(0) end method; define constant %gf-dispatch-immediate-linear-singleton = method (arg, parent :: , d :: ) parent; immediate-linear-singleton-discriminator-element(d, arg, singleton-discriminator-default(d)) end method; define constant %gf-dispatch-value-object-linear-singleton = method (arg, parent :: , d :: ) parent; value-object-linear-singleton-discriminator-element(d, arg, singleton-discriminator-default(d)) end method; define constant singleton-discriminator-element = method (d :: , key, default) => (val :: ) select (d by instance?) => immediate-linear-singleton-discriminator-element(d, key, default); => value-object-linear-singleton-discriminator-element(d, key, default); end select end method; define constant singleton-discriminator-element-setter = method (value, d :: , key) select (d by instance?) => linear-singleton-discriminator-element-setter(value, d, key); end select end method; define constant make-single-class-singleton-discriminator = method (keys :: , argnum :: , gf :: ) => (d :: ); let n :: = size(keys); make-linear-singleton-discriminator (if (value-object?(head(keys))) engine-node$k-value-object-linear-singleton else engine-node$k-immediate-linear-singleton end if, argnum, gf, keys, n) end method; //// Handle-missed-dispatch define primary class () slot %ds-gf :: ; slot %ds-parent :: ; slot %ds-args :: ; slot %ds-argnum-set :: ; slot %ds-args-to-check-first :: = #(); slot %ds-headed-methods :: ; slot %ds-cache = #f; slot %ds-result = #f; slot %ds-conditions = #(); slot %ds-argtypes :: = #[]; end class; define inline function %ds-add-argnum (argnum :: , ds :: ) add-argnum(argnum, %ds-argnum-set(ds)) end function; define inline-only function %ds-argtype (ds :: , i :: ) element(%ds-argtypes(ds), i, default: ) end function; define function dispinapplicable (ds :: ) dispresult($inapplicable-engine-node, ds); $absent-engine-node end function; define function dispwarn (c :: , ds :: ) => () %ds-conditions(ds) := pair(c, %ds-conditions(ds)) end function; define function dispresult (r, ds :: ) => () if (%ds-result(ds)) error("Bug! Multiple dispatch results?") else %ds-result(ds) := r end if; end function; define constant handle-missed-dispatch = method (d :: , parent :: , args :: ) /* iterate redefinition-check (i :: = size(args) - 1, updated-p :: = #f) if (i < 0) if (updated-p) apply(gf, args) else handle-missed-dispatch-1(d, gf, args) end if else redefinition-check(i - 1, if (obsolete-instance?(element(args, i))) element(args, i) := update-obsolete-instance(element(args, i)); #t else updated-p end if) end if end iterate */ handle-missed-dispatch-1(d, parent, args) end method; define variable *dispatch-miss-count* = 0; define variable *dispatch-computation-count* = 0; define constant handle-missed-dispatch-1 = method (d :: , parent :: , args :: ) let ds :: = system-allocate-simple-instance(); let parent :: = if (d == $absent-engine-node) parent else d end; let gf :: = parent-gf(parent); %ds-parent(ds) := parent; %ds-gf(ds) := gf; %ds-args(ds) := args; %ds-argnum-set(ds) := make-argnum-set(); %ds-args-to-check-first(ds) := #(); %ds-conditions(ds) := #(); %ds-result(ds) := #f; %ds-argtypes(ds) := #[]; // *dispatch-miss-count* := wrapped-incr(*dispatch-miss-count*); let new-start-engine = (with-object-lock (gf) let cache = %gf-cache(gf); %ds-cache(ds) := cache | #f; (~type-complete?(gf) & call-to-type-incomplete-generic(gf, args)) | compute-dispatch-engine(ds) end with-object-lock); bletch-stack(%ds-conditions(ds)); let what = %ds-result(ds) | new-start-engine; if (what) if (instance?(what, )) bletch(what) else %invoke-engine-node(what, parent, args) end if else dbg("Handle-missed-dispatch: reinvoking %=", gf); // show(gf); %invoke-generic-function-mepized(gf, args) end if end method; define open generic todays-dispatch-report (x); define method todays-dispatch-report (x) end method;