Module: dfmc-flow-graph 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 //////////////////////////////////////////////////////////////////////// /// /// Classes to represent thing the typist must do and when to do them /// and also functions to manipulte them. /// /////////////////////////////////////////////////////////////////////// /// Records a computation which needs to be typed and a context /// i.e. a call site summary in which to do it. These are used /// to schedule work. /// They are queueable and indicate whether they are in a queue (they can /// only be in one queue at a time. define primary abstract class () slot node :: , required-init-keyword: comp:; slot call-site-summary :: , required-init-keyword: summary:; slot next-item :: false-or() = #f, init-keyword: next:; end; define method initialize(twi :: , #key) next-method(); if (twi.call-site-summary.compressed?) // break("recon") end end; define function work-item-= (i1 :: , i2 :: ) => (result :: ) (i1.node == i2.node) & (i1.call-site-summary == i2.call-site-summary) end; define class () slot queue-front :: false-or() = #f, init-keyword: front:; slot queue-back :: false-or() = #f, init-keyword: back:; end; define function queue-empty? (q :: ) ~q.queue-front; end; define class () slot initial-work :: = make(), init-keyword: initial-work:; slot current-re-type-work :: = make(); slot re-type-work :: = make(); slot %resourced-work-items :: false-or() = #f; slot used-call-site-summaries :: = make(
); end; define function resourced-work-item (agenda :: ) => (work-item :: false-or()); let item = %resourced-work-items(agenda); if (item) %resourced-work-items(agenda) := item.next-item; end; item; end; define function resourced-work-item-setter (work-item :: , agenda :: ) => (); work-item.next-item := %resourced-work-items(agenda); %resourced-work-items(agenda) := work-item; end; /// These only ever appear in an agenda (i.e aren`t used to record dependencies). They can be /// resourced by the agenda. define class () end; define sealed method make (c == , #key comp :: , summary :: , agenda :: false-or(), next :: false-or() = #f) => (work-item :: ); /* if (comp.environment.lambda ~= summary.css-lambda) error("Invalid work item: %s & %s", comp, summary); else */ if (agenda) let item :: false-or() = resourced-work-item(agenda); if (item) item.node := comp; item.call-site-summary := summary; item.next-item := next; item; else next-method(); end; else next-method(); end; end; define method schedule-for-initial-typing (agenda :: , comp :: , css :: ) => (); let where = agenda.initial-work; let first = where.queue-front; if (first) block (exit) iterate repeat (item :: = first) if (comp == item.node & css == item.call-site-summary) exit(); elseif (item.next-item) repeat(item.next-item); else let new-item = make(, comp: comp, summary: css); item.next-item := (where.queue-back := new-item); end; end; end; else let new-item = make(, comp: comp, summary: css); where.queue-front := (where.queue-back := new-item); end; end; /// In this case we don`t check to see whether an equivalent work item is in the agenda because /// This almost certainly costs more than the redundant processing of the node. define method schedule-for-retyping (agenda :: , comp :: , css :: ) => (); let item = make(, comp: comp, summary: css, agenda: agenda); if (agenda.current-re-type-work.queue-back) agenda.current-re-type-work.queue-back.next-item := item; agenda.current-re-type-work.queue-back := item; else agenda.current-re-type-work.queue-front := (agenda.current-re-type-work.queue-back := item); end; end; /// These implicitly record NON-LOCAL dependencies /// between computations (e.g. actual call sites depend on the return /// computation of the function called) or computations and 'bindings' /// with indefinite extent (e.g. module variables and 'boxes' (better /// known as cells - but we wont go into that again). define class () slot home-queue :: , required-init-keyword: home:; end; define class () slot node :: , required-init-keyword: comp:; slot call-site-summary :: , required-init-keyword: summary:; end; define constant = ; define class (
) end class; define sealed method make (c == , #key comp :: , summary :: , home :: false-or(), next :: false-or() = #f) => (work-item :: ); // if (comp.environment.lambda ~= summary.css-lambda) // format-out("Invalid dependency record?: %s & %s", // comp.environment.lambda, summary); // break("inv"); // end; next-method(); end; define sideways method make-dependency-records() => (dependency-records) make(); end; /// I don't actually believe we need to check to the presence of this dependency already /// because the dependencies are set up during initialize-node-type which only visits each node /// once. But for safety`s sake we`ll do a version which checks. We can try a non-checking /// version later. define function record-dependency-new (dependee, comp, summary :: ) => (); let (dependee-compilation-record-library, record?) = if (instance?(dependee, )) values( dependee.css-lambda.model-creator.form-compilation-record.compilation-record-library, ~dependee.compressed?) else values( dependee.model-creator.form-compilation-record.compilation-record-library, #t) end; let summary-creator = summary.model-creator; let summary-cr = if (instance?(summary-creator, )) summary-creator else summary-creator.form-compilation-record end; let summary-compilation-record-library = summary-cr.compilation-record-library; if (dependee-compilation-record-library == summary-compilation-record-library & record?) summary.introduced-dependencies := add-new!(summary.introduced-dependencies, dependee); let where :: = dependee.type-dependencies; let first :: false-or() = where.queue-front; if (first) block (exit) iterate repeat (item :: = first) if (comp == item.node & summary == item.call-site-summary) exit(); elseif (item.next-item) repeat(item.next-item); else let new-dependency = make(, comp: comp, summary: summary, home: where); item.next-item := (where.queue-back := new-dependency); end; end; end; else let new-dependency = make(, comp: comp, summary: summary, home: where); where.queue-front := (where.queue-back := new-dependency); end; end; end; define method record-dependency (dependee, comp, summary :: ) => (); let (dependee-compilation-record-library, record?) = if (instance?(dependee, )) values( dependee.css-lambda.model-creator.form-compilation-record.compilation-record-library, ~dependee.compressed?) else values( dependee.model-creator.form-compilation-record.compilation-record-library, #t) end; let summary-creator = summary.model-creator; let summary-cr = if (instance?(summary-creator, )) summary-creator else summary-creator.form-compilation-record end; let summary-compilation-record-library = summary-cr.compilation-record-library; if (dependee-compilation-record-library == summary-compilation-record-library & record?) summary.introduced-dependencies := add-new!(summary.introduced-dependencies, dependee); let where :: = dependee.type-dependencies; let new-dependency = make(, comp: comp, summary: summary, home: where); if (where.queue-front) where.queue-back := (where.queue-back.next-item := new-dependency); else where.queue-front := (where.queue-back := new-dependency); end; end; end; define method record-dependency (dependee :: , comp, summary :: ) => (); let dependee-compilation-record-library = dependee.model-creator.form-compilation-record.compilation-record-library; let summary-compilation-record-library = summary.model-creator.form-compilation-record.compilation-record-library; if (dependee-compilation-record-library == summary-compilation-record-library) summary.introduced-dependencies := add-new!(summary.introduced-dependencies, dependee); let table = dependee.type-dependencies; let l :: = element(table, summary, default: #()); if (empty?(l)) table[summary] := list(comp) else unless (member?(comp, l)) l.tail := pair(comp, l.tail); end end end end; define function remove-introduced-dependencies (l :: <&lambda>) for-all-summaries (css in l.call-site-summaries) for (dependee in css.introduced-dependencies) unless (instance?(dependee, ) & dependee.compressed?) remove-dependency(dependee, css); end; end; end; end; define method remove-dependency (dependee, dependent :: ) let from :: = dependee.type-dependencies; let first :: false-or() = from.queue-front; when (first) let new-first = #f; iterate repeat (prev :: = first) unless (new-first | prev.call-site-summary == dependent) new-first := prev; end; let current = prev.next-item; when (current) when (current.call-site-summary == dependent) prev.next-item := current.next-item; end; repeat(current); end; end; from.queue-front := new-first; unless (new-first) from.queue-back := #f; end; end; end; define method remove-dependency (dependee :: , dependent :: ) let table = dependee.type-dependencies; remove-key!(table, dependent); end; /* define function schedule-dependents (agenda :: , deps :: ) => (); let first-dep :: false-or() = deps.queue-front; when (first-dep) if (agenda.current-re-type-work.queue-back) agenda.current-re-type-work.queue-back.next-item := first-dep; else agenda.current-re-type-work.queue-front := first-dep; end; agenda.current-re-type-work.queue-back := deps.queue-back; deps.queue-front := (deps.queue-back := #f); end; end; */ define method schedule-dependents (agenda :: , dependents :: ) => (); iterate loop (item :: false-or() = dependents.queue-front) when (item) schedule-for-retyping(agenda, item.node, item.call-site-summary); loop(item.next-item); end; end; end; define method schedule-filtered-dependents (agenda :: , dependents :: , css :: ) => (); let lambda = css.css-lambda; iterate loop (item :: false-or() = dependents.queue-front) when (item) let item-css = item.call-site-summary; if (item-css == css | item-css.css-lambda ~== lambda) schedule-for-retyping(agenda, item.node, item-css); end; loop(item.next-item); end; end; end; define method schedule-dependents (agenda :: , dependents ::
) => (); for (l keyed-by summary in dependents) for (node in l) schedule-for-retyping(agenda, node, summary); end end; end; define method schedule-filtered-dependents (agenda :: , dependents ::
, css :: ) => (); let lambda = css.css-lambda; for (l keyed-by summary in dependents) if (summary = css | summary.css-lambda ~== lambda) for (node in l) schedule-for-retyping(agenda, node, summary); end end end; end; // gts,98may27 added except: keyword define function schedule-users (agenda :: , nodes :: , summary, #key except :: = #f) for (node in nodes) if (except & ~ member?(node, except)) if (node.environment.lambda == summary.css-lambda) schedule-for-retyping(agenda, node, summary); else for-all-summaries (summary in node.environment.lambda.call-site-summaries) schedule-for-retyping(agenda, node, summary); end; end if; end if; end for; end; define method push-current-work (w-a :: ) let crtw = w-a.current-re-type-work; let b = crtw.queue-back; when (b) let rtw = w-a.re-type-work; b.next-item := rtw.queue-front; unless (rtw.queue-back) rtw.queue-back := b; end; rtw.queue-front := crtw.queue-front; crtw.queue-front := (crtw.queue-back := #f) ; end; end; define function pop-work-item (agenda :: , queue :: ) => (item :: false-or()); let item = queue.queue-front; if (item) let new-front = item.next-item; queue.queue-front := new-front; unless (new-front) queue.queue-back := new-front; end; maybe-save-item(agenda, item); end; item; end; define method maybe-save-item (agenda :: , item :: ) => () resourced-work-item(agenda) := item; end; define method maybe-save-item (agenda :: , rec :: ) => () rec.next-item := #f; let where = rec.home-queue; let first = where.queue-front; if (first) block (exit) iterate repeat (item :: = first) if (rec.node == item.node & rec.call-site-summary == item.call-site-summary) exit(); elseif (item.next-item) repeat(item.next-item); else item.next-item := (where.queue-back := rec); end; end; end; else where.queue-front := (where.queue-back := rec); end; end;