Module: dfmc-back-end
Synopsis: Static heap modeling.
Author: Keith Playford and Jonathan Bachrach
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 constant *merge-literals?* = #t;
define variable *literal-merging-stats* = #f; // make(
);
// Notes:
//
// Claiming is the act of taking an object as being the responsibility
// of the current heap. When an object is claimed, it is recorded
// appropriately if special in some way (bindings, code, etc.) and
// placed on the pending queue. Objects on the pending queue are
// internal elements of the heap that have yet to have their references
// walked.
//
// The bindings belonging to a particular compilation record are
// static and evident - no descent or tracing is required to
// work out the set, they can just be accessed directly from
// the record's definition objects.
//
// Tracing is done on the root objects to determine their local
// contents vs. references to objects created in other compilation
// records - to tell a vector used to represent a sequence of
// superclasses from the superclasses the vector contains for
// example.
// Any bindings are queried in order to claim their value, so a form
// doesn't have to explicitly return any object that is reached
// directly via that route.
// If the init method has ended up doing no computation, we don't emit it.
define function maybe-init-method
(init-method :: false-or(<&lambda>))
=> (maybe-method :: false-or(<&lambda>))
if (init-method)
case
code(^iep(init-method))
=> init-method; // Previously emitted code?
~body(init-method)
// The no body & no code case means the method is being re-emitted,
// except that it wasn't emitted the first time because it was
// empty.
=> #f;
~empty-method?(init-method)
=> init-method;
otherwise
=> #f;
end
end
end function;
define class ()
// The known starting points of the heap.
constant slot heap-root-init-code ::
= make();
constant slot heap-root-system-init-code ::
= make();
// The information derived about the heap, including its contents
// and references to elements belonging to other compilation
// records.
slot heap-back-pointers :: = make();
slot heap-defined-bindings ::
= make();
slot heap-referenced-bindings ::
= make();
slot heap-defined-objects ::
= make();
slot heap-referenced-objects ::
= make();
slot heap-defined-repeated-object-sizes ::
= make();
slot heap-referenced-repeated-object-sizes ::
= make();
slot heap-load-bound-references ::
= make();
slot heap-symbols :: = make();
slot %heap-next-id :: = 0;
slot heap-size :: = 0;
constant slot heap-deferred-model-references ::
= make();
constant slot heap-record-repeated-object-sizes? ::
= back-end-record-repeated-object-sizes?(current-back-end());
end class;
define function heap-defined-object-sequence (heap :: )
key-sequence(heap-defined-objects(heap))
end;
define class ()
constant slot heap-compilation-record :: ,
required-init-keyword: compilation-record:;
// The incoming references from other heaps within this library.
constant slot heap-incoming-references ::
= make();
// slot heap-original-defined-objects :: false-or() = #f;
end;
define class ()
constant slot heap-library :: ,
required-init-keyword: library:;
end;
define method heap-approximate-size
(heap :: ) => (res :: )
size(heap-defined-bindings(heap))
+ size(heap-referenced-bindings(heap))
+ size(heap-defined-objects(heap))
+ size(heap-referenced-objects(heap))
end method;
define method print-object (o :: , stream :: ) => ()
format(stream, "{model-heap of %s}", o.heap-library);
end method;
define method heap-library
(heap :: ) => (ld :: )
compilation-record-library(heap-compilation-record(heap))
end;
define method heap-compilation-record
(heap :: ) => (cr :: )
heap.heap-library.library-description-combined-record
end;
define method heap-next-id (heap :: ) => (number :: )
let next-id = heap.%heap-next-id;
heap.%heap-next-id := next-id + 1;
next-id
end method;
define variable *heap-record-back-pointers?* = #f;
define sideways method retract-compilation-record-heap
(cr :: ) => ()
unless (*heap-record-back-pointers?*)
let heap = compilation-record-model-heap(cr);
if (heap)
// Get rid of all pointers to the heap.
// Nowadays, there aren't any...
compilation-record-model-heap(cr) := #f;
end;
end unless;
cr.compilation-record-heap-referenced-objects := #f;
cr.compilation-record-needs-linking? := #t;
end method;
/*
define variable *dbg?* = #f;
define method dbg? (name :: )
*dbg?* == #t | name == *dbg?*
end;
define method dbg? (model)
*dbg?* &
select (model by instance?)
=> dbg?(as(, model));
=> dbg?(model.head) | dbg?(model.tail);
=> any?(dbg?, model);
=> dbg?(model.fragment-identifier);
=> dbg?(model.name);
=> dbg?(model.form-variable-name);
<&iep> => dbg?(model.function);
<&mm-wrapper> => dbg?(model.^mm-wrapper-implementation-class.^iclass-class);
otherwise => model & ~direct-object?(model)
& dbg?(model.model-definition | model.^debug-name);
end;
end method;
*/
define thread variable *precomputing-heap?* :: = #f;
// Used by loosely-link-library-heaps, and by tightly-link-library-heaps when
// combining, and also for interactive layer, and maybe-recompute-library-heaps
// (which is used by ensure-library-stripped)
define method compute-and-install-compilation-record-heap
(cr :: , #rest flags) => ()
install-compilation-record-heap(cr);
apply(compute-compilation-record-heap, cr, flags);
end method;
// Used by tightly-link-library-heaps, when not combining, to compute
// heap-incoming-references.
define method precompute-library-heaps (ld :: )
dynamic-bind (*precomputing-heap?* = #t)
let cr* = ld.compilation-context-records;
do(install-compilation-record-heap, cr*);
for (cr in cr*)
compute-compilation-record-heap(cr, skip-emit?: #t);
end;
end;
end;
// Used by tightly-link-library-heaps when not combining.
define method compute-library-reachable-heap (ld :: )
let cr* = ld.compilation-context-records;
do(compute-compilation-record-reachable-heap, cr*);
do(process-heap-deferred-models, cr*);
end;
// in loose link, or preheaping, or combining.
define method compute-compilation-record-heap
(cr :: , #rest flags, #key skip-emit?, #all-keys)
=> ()
with-dependent ($compilation of cr)
trace-heap-from-roots(compilation-record-model-heap(cr));
end;
process-heap-deferred-models(cr);
unless (skip-emit?)
apply(emit-compilation-record-heap, cr, flags);
end;
let heap = compilation-record-model-heap(cr);
compilation-record-approximate-model-heap-size(cr)
:= heap-approximate-size(heap);
compilation-record-data-size(cr)
:= heap-size(heap) * word-size();
when (compilation-record-interactive?(cr)
| library-forms-dynamic?(compilation-record-original-library(cr)))
compilation-record-heap-referenced-objects(cr)
:= as(, heap-referenced-objects(heap));
end when;
// CHECK-HEAP(cr);
when (*precomputing-heap?*)
// Reset the walking machinery, just wanted to the incoming references.
// heap.heap-original-defined-objects := heap.heap-defined-objects;
heap.heap-back-pointers := make();
heap.heap-defined-bindings := make();
heap.heap-referenced-bindings := make();
heap.heap-defined-objects := make();
heap.heap-referenced-objects := make();
heap.heap-defined-repeated-object-sizes := make();
heap.heap-referenced-repeated-object-sizes := make();
heap.heap-load-bound-references := make();
heap.heap-symbols := make();
heap.%heap-next-id := 0;
heap.heap-size := 0;
end;
end method;
// tight link non-combining real heaping pass
define method compute-compilation-record-reachable-heap
(cr :: ) => ()
with-dependent ($compilation of cr)
trace-heap-from-roots(compilation-record-model-heap(cr),
incoming?: #t);
end with-dependent;
// CHECK-HEAP(cr);
end method;
define method emit-compilation-record-heap
(cr :: , #rest flags)
with-dependent ($compilation of cr)
apply(emit-all, current-back-end(), cr, flags);
end with-dependent;
end;
/*
define function CHECK-HEAP (cr :: )
let heap = compilation-record-model-heap(cr);
let objects = heap.heap-referenced-objects;
let object-names = make();
for (object in objects)
if (instance?(object, <&class>))
let binding = model-variable-binding(object);
if (binding)
let info = element(object-names, binding, default: #());
object-names[binding] := pair(object, info);
unless (info == #())
error("Duplicate objects for %s:%s", binding, object-names[binding]);
end;
end;
end;
end;
end function;
*/
define method install-compilation-record-heap
(cr :: ) => ()
// Make sure any old one is properly retracted and that old object file
// is marked obsolete (i.e. cr-needs-linking? is set)
with-dependent ($compilation of cr)
retract-compilation-record-heap(cr);
cr.compilation-record-preceeding-line-count := 0;
compilation-record-model-heap(cr)
:= make(, compilation-record: cr)
end with-dependent;
end;
define method install-compilation-record-heap
(combined-cr :: ) => ()
let ld = combined-cr.compilation-record-library;
local method cr-lines (cr :: )
let lines = cr.compilation-record-source-line-count | 0;
// For some reason, this count seems to be consistently off by 2.
let fudged-lines = if (lines >= 2) lines - 2 else lines end;
fudged-lines
+ cr.compilation-record-source-record.source-record-start-line
end;
for (cr in ld.library-description-compilation-records,
lines = 0 then lines + cr-lines(cr))
cr.compilation-record-preceeding-line-count := lines
end;
compilation-record-model-heap(combined-cr)
:= make(, library: ld);
end method;
define method form-created-bindings (form :: )
form-defined-bindings(form)
end method;
define method form-created-bindings (form :: )
#()
end method;
define function claim-init-method (heap :: ,
code :: false-or(<&lambda>),
system? :: )
when (code)
mark-emitted-name(heap, code);
maybe-claim-computations-references(heap, code, #f);
end;
end;
define function claim-init-form (heap :: , form :: )
=> (claimed? :: singleton(#t))
let (init, sys-init) = form-init-code(form);
claim-init-method(heap, init, #f);
claim-init-method(heap, sys-init, #t);
#t
end claim-init-form;
define function claim-compilation-record-roots
(heap :: , cr :: )
for (form :: in compilation-record-top-level-forms(cr))
claim-form-roots(heap, form)
end;
end claim-compilation-record-roots;
define inline function claim-form-roots
(heap :: , form :: )
=> (object);
let bindings = form-created-bindings(form);
unless (empty?(bindings) & form-ignored?(form))
for (binding :: in bindings)
unless (binding-previously-defined?(binding))
if (*precomputing-heap?* | model-externally-visible?(binding))
maybe-claim-heap-element(heap, #f, binding, #t);
end;
end;
end;
if (*precomputing-heap?*)
claim-init-form(heap, form);
let model = instance?(form, ) & form-model(form);
// When pre-heaping, need to note external references from the
// method in this heap in case end up claiming the method via
// generic-function-methods from a generic in another heap
when (model) maybe-claim-heap-element(heap, #f, model, #t) end;
else
let (init, system-init) = form-init-code(form);
when (init | system-init)
unless (process-pending-init-form(heap, form))
make-init-form-pending(heap, form);
end;
end;
end;
end;
end claim-form-roots;
define method process-pending-init-form (heap :: ,
form :: )
=> (processed? :: )
claim-init-form(heap, form);
end process-pending-init-form;
define method process-pending-init-form (heap :: ,
form :: )
=> (processed? :: )
// For a modifying form (method or domain) any init code just installs
// the object with varying degrees of runtime checking. If we can decide
// that the object itself will not be created, we can ignore the
// init form.
let model = form-model(form);
when (~model |
model-externally-visible?(model) |
model-externally-accessible?(heap, model))
// TODO: Why do we do this? Should we only export if the generic
// is exported? How else could somebody get access to it?
when (model) maybe-export(heap, model, #t) end;
claim-init-form(heap, form);
end;
end process-pending-init-form;
// If true, assume constant/variable init forms are side-effect-free, and
// hence can be ignored when the variable they initialize is ignored.
define variable *assume-side-effect-free-init-forms?* = #f;
define method process-pending-init-form (heap :: ,
form :: )
=> (processed? :: )
// The init code in define constant/variable can be ignored if
// (1) none of the bindings defined by the form are actually created
// and (2) the init code is side-effect-free except for setting the
// bindings it defines...
// Note that for thread variables, the init code actually allocates
// the storage, not just sets it... However, if the binding is
// never actually referenced, we should be able to remove it.
// And if the binding is ever referenced, it should get claimed.
// so how come dylan-string-buffer seems unclaimed (init gets zappd)
// but the functions referencing it seem to be around???
// (display-class-breakpoints).
when (~*assume-side-effect-free-init-forms?* |
any?(method (binding :: )
debug-assert(internal-binding?(heap, binding),
"Local form with non-local binding?");
heap-element-claimed?(heap, binding)
end,
form-defined-bindings(form)))
claim-init-form(heap, form)
end;
end process-pending-init-form;
define method process-pending-init-form (heap :: ,
form :: )
=> (processed? :: )
// A generic definition is just like a define constant...
let binding = form-variable-binding(form);
debug-assert(internal-binding?(heap, binding), "Local form with non-local binding?");
when (heap-element-claimed?(heap, binding))
claim-init-form(heap, form)
end;
end process-pending-init-form;
define method process-pending-init-form (heap :: ,
form :: )
=> (processed? :: )
// The init code in a define class can be ignored if the class
// itself is not created, since it just installs the class (e.g. in
// subclass lists of external superclasses).
let binding = form-variable-binding(form);
debug-assert(internal-binding?(heap, binding), "Local form with non-local binding?");
when (heap-element-claimed?(heap, binding) |
begin
let class = binding-model-object(binding);
debug-assert(~class | internal-object?(heap, class),
"Local class with non-local model?");
~class | heap-element-claimed?(heap, class)
end)
claim-init-form(heap, form)
end;
end process-pending-init-form;
define inline method claim-heap-roots (heap :: )
let cr = heap-compilation-record(heap);
when (first-compilation-record?(cr))
let ld = compilation-record-library(cr);
claim-init-method(heap, library-description-system-class-init-code(ld), #f);
claim-init-method(heap, library-description-system-gf-init-code(ld), #f);
end;
claim-compilation-record-roots(heap, cr);
end claim-heap-roots;
define inline method claim-heap-roots (heap :: )
let ld = heap.heap-library;
claim-init-method(heap, library-description-system-class-init-code(ld), #f);
claim-init-method(heap, library-description-system-gf-init-code(ld), #f);
for (cr in ld.library-description-compilation-records)
claim-compilation-record-roots(heap, cr);
end for;
end claim-heap-roots;
define function make-init-form-pending (heap :: , form)
*heap-pending*.heap-pending-init-forms
:= add(*heap-pending*.heap-pending-init-forms, form);
end;
define inline-only function first-compilation-record? (cr :: )
cr == first(library-description-compilation-records(compilation-record-library(cr)))
end function;
define method add-heap-init-code
(heap :: ,
code :: ,
system-code :: ,
exceptions :: )
let cr = heap.heap-compilation-record;
when (first-compilation-record?(cr))
add-whole-library-init-code(compilation-record-library(cr),
code, system-code);
end;
add-compilation-record-init-code(cr, code, system-code, exceptions);
end add-heap-init-code;
define method add-heap-init-code
(heap :: ,
code :: ,
system-code :: ,
exceptions :: )
let ld = heap.heap-library;
add-whole-library-init-code(ld, code, system-code);
for (cr in library-description-compilation-records(ld))
add-compilation-record-init-code(cr, code, system-code, exceptions);
end;
end add-heap-init-code;
define inline function add-whole-library-init-code
(ld :: , code, system-code)
let class-init = library-description-system-class-init-code(ld);
when (class-init) add!(code, class-init) end;
let gf-init = library-description-system-gf-init-code(ld);
when (gf-init) add!(code, gf-init) end;
end add-whole-library-init-code;
define inline function add-compilation-record-init-code
(cr :: , code, system-code, exceptions)
for (form in compilation-record-top-level-forms(cr))
unless (form-ignored?(form) | member?(form, exceptions))
let (init, system-init) = form-init-code(form);
when (init) add!(code, init) end;
when (system-init) add!(system-code, system-init) end;
end;
end;
end add-compilation-record-init-code;
define function form-init-code
(form :: ) => (init, system-init)
let init = maybe-init-method(form.form-init-method);
let sys-init = maybe-init-method(form.form-system-init-method);
debug-assert(~(init | sys-init) | ~form-compile-stage-only?(form),
"Compile-stage form %s with non-empty inits!", form);
values(init, sys-init)
end;
define class ()
constant slot heap-pending-heap :: ,
required-init-keyword: heap:;
constant slot heap-pending-elements :: = make();
constant slot heap-compile-time-references :: = make();
constant slot heap-compile-time-elements :: = make();
slot heap-pending-init-forms :: = #();
slot heap-pending-generics :: = #f;
constant slot heap-merged-literals :: false-or()
= *merge-literals?* & ~*precomputing-heap?* & make();
end;
define thread variable *heap-pending* :: false-or() = #f;
// when combining: incoming? = #f, *precomputing-heap?* = #f
// when not combining (tight mode):
// pre-heaping: incoming? = #f, *precomputing-heap?* = #t
// heaping: incoming? = #t, *precomputing-heap?* = #f
// When loose-mode/interactive
// heaping: incoming? = #f, *precomputing-heap?* = #f
define method trace-heap-from-roots (heap :: , #key incoming? = #f)
=> ()
debug-out(#"heap", "Trace %= from roots preheaping?=%s incoming?=%s\n",
heap.heap-compilation-record, *precomputing-heap?*, incoming?);
dynamic-bind (*heap-pending* = make(, heap: heap))
when (~incoming? & *literal-merging-stats*)
*literal-merging-stats* := make();
end;
// Seed the tracing process with the known roots.
claim-heap-roots(heap);
when (incoming?)
for (ct-ref? keyed-by element in heap-incoming-references(heap))
// Even though we can't go back and change the original pointers in
// other CR's, these objects must participate in literal merging
// to avoid the case where non-eq incoming and internal versions
// of an object get defined, causing link-time conflicts. Since at
// link-time, incoming references are by name only, it's not important
// to change the original pointers in other CR's.
let element = maybe-merge-literal(element);
maybe-claim-heap-element-derived(heap, #f, element, ct-ref?);
end;
end;
// All compilation units need these because the back-end inserts
// references to them that may not be evident in the code.
maybe-claim-heap-element(heap, #f, dylan-value(#""), #f);
maybe-claim-heap-element(heap, #f, dylan-value(#""), #f);
maybe-claim-heap-element(heap, #f, &unbound, #f);
maybe-claim-heap-element(heap, #f, #t, #f);
maybe-claim-heap-element(heap, #f, #f, #f);
drain-pending-elements(heap);
// store init code for the linker.
unless (*precomputing-heap?*)
let code = heap.heap-root-init-code;
let system-code = heap.heap-root-system-init-code;
let exceptions = make();
for (form in *heap-pending*.heap-pending-init-forms)
add!(exceptions, form);
end;
add-heap-init-code(heap, code, system-code, exceptions);
end;
/*
unless (empty?(*heap-pending*.heap-pending-init-forms))
for (form in *heap-pending*.heap-pending-init-forms)
format-out(">>>>>> Zapped init form: %=\n", form);
end;
end;
*/
end dynamic-bind;
// Record the repeated sizes of heap members.
// Note: This is now done as a post-pass since it requires the class
// of the objects to be available and initialized, an assumption that
// causes false circularities on classes in the Dylan library now that
// heap roots are determined as models are computed. Because this
// is only a problem in the Dylan library, which has no external
// references, external reference repeated sizes are still recorded
// as we go along.
for (object in heap-defined-object-sequence(heap))
// HACK: THIS SHOULD BE A SUBCLASS OF A CATEGORY THAT IS NOT A MODEL
unless (instance?(object, <&iep>))
record-repeated-size(heap, object);
end unless;
end;
end method;
define method element-compile-stage-only? (e :: )
let form = untracked-binding-definition(e, default: #f);
form & form-compile-stage-only?(form)
end;
define method element-compile-stage-only? (e)
~direct-object?(e) & model-compile-stage-only?(e)
end;
define method element-compile-stage-only? (e :: <&iep>)
element-compile-stage-only?(e.function)
end;
define method element-compile-stage-only? (e :: <&mm-wrapper>)
element-compile-stage-only?(e.^mm-wrapper-implementation-class.^iclass-class)
end;
// TODO: this is a workaround for form-compile-stage-only? being false for
// domains on compile-time-only methods/generics. Should fix form-compile-
// stage-only() to be on instead.
define method element-compile-stage-only? (e :: <&domain>)
let binding = model-variable-binding(e);
element-compile-stage-only?(binding)
end;
// TODO: This is a workaround for raw slot accessors - form-compile-stage-only?
// is true for them, but they are referenced from the class (via the slot
// descriptor) so they must exist at run-time if the class exists at run-time
define method element-compile-stage-only? (e :: <&generic-function>)
let form = model-definition(e);
if (form & form-compile-stage-only?(form))
let parent = form-parent-form(form);
~instance?(parent, )
| form-compile-stage-only?(form-parent-form(parent))
end;
end;
define inline method remove-if! (list :: , remove? :: )
iterate loop (list :: = list, last :: false-or() = #f, this :: = list)
if (this == #())
list
else
let object = this.head;
let next = this.tail;
if (~remove?(object))
loop(list, this, next)
elseif (last == #f)
loop(next, last, next)
else
last.tail := next;
loop(list, last, next)
end;
end if;
end iterate;
end remove-if!;
define function drain-pending-elements (heap :: )
while (~empty?(*heap-pending*.heap-pending-elements))
let e = pop(*heap-pending*.heap-pending-elements);
let ct? = member?(e, *heap-pending*.heap-compile-time-elements);
maybe-claim-heap-element-references(heap, e, ct?);
end;
process-pending-init-forms(heap);
process-pending-generic-models(heap);
if (~empty?(*heap-pending*.heap-pending-elements))
drain-pending-elements(heap)
else
unless (*precomputing-heap?*)
// this should only add 's to the pending elements,
// everything else should be already claimed.
remove-unclaimed-pending-models(heap);
if (~empty?(*heap-pending*.heap-pending-elements))
drain-pending-elements(heap)
end;
end;
end;
end function drain-pending-elements;
define method maybe-claim-generic-function-modifying-models
(heap :: , gf :: <&generic-function>)
let methods = ^generic-function-methods(gf);
let domains = if (instance?(gf, <&incremental-generic-function>))
^generic-function-domains(gf)
else // else domains not referenced at runtime...
#()
end;
if (*precomputing-heap?*)
do-claim-generic-function-modifying-models(heap, gf);
else
let methods = choose(method (m)
~process-generic-function-model(heap, gf, m)
end,
methods);
let domains = choose(method (d)
~process-generic-function-model(heap, gf, d)
end,
domains);
if (methods == #() & domains == #())
do-claim-generic-function-modifying-models(heap, gf);
else
*heap-pending*.heap-pending-generics
:= make(,
function: gf,
methods: methods,
domains: domains,
next: *heap-pending*.heap-pending-generics)
end;
end;
end maybe-claim-generic-function-modifying-models;
define method do-claim-generic-function-modifying-models
(heap :: , gf :: <&generic-function>)
maybe-claim-heap-element(heap, gf, gf.^generic-function-methods, #f);
end do-claim-generic-function-modifying-models;
define method do-claim-generic-function-modifying-models
(heap :: , gf :: <&incremental-generic-function>)
maybe-claim-heap-element(heap, gf, gf.^generic-function-methods, #f);
// TODO: The way generic function domains are stored, only the last one is
// actually referenced directly from the gf.
for (domain in gf.^generic-function-domains)
maybe-claim-heap-element(heap, gf, domain, #f);
end;
end do-claim-generic-function-modifying-models;
define method claim-generic-model
(heap :: , gf :: <&generic-function>, model)
when (model-externally-visible?(gf))
maybe-export(heap, model, #f);
end;
maybe-claim-heap-element(heap, gf, model, #f);
debug-assert(begin
let form = model.model-definition;
~form | begin
let (init, sys-init) = form-init-code(form);
~init & ~sys-init
end
end,
"Init form on non-ct generic model %s",
format-to-string("%s", model.model-definition));
end;
define constant = false-or();
define class ()
constant slot pending-generic-function, required-init-keyword: function:;
slot pending-generic-unclaimed-methods :: , required-init-keyword: methods:;
slot pending-generic-unclaimed-domains :: , required-init-keyword: domains:;
slot pending-generic-next :: , required-init-keyword: next:;
end;
define sealed-constructor ;
define inline method remove-if! (pgfs :: , remove? :: )
iterate loop (all :: = pgfs,
last :: = #f,
this :: = pgfs)
if (this == #f)
all
else
let next = this.pending-generic-next;
if (~remove?(this))
loop(all, this, next)
elseif (last == #f)
loop(next, last, next)
else
last.pending-generic-next := next;
loop(all, last, next)
end;
end if;
end iterate;
end method remove-if!;
define method process-generic-function-model
(heap :: , gf :: <&generic-function>, model)
=> (processed? :: )
when (model-externally-visible?(model) |
model-externally-accessible?(heap, model))
// Claim it and remove from list.
claim-generic-model(heap, gf, model);
#t
end when;
end process-generic-function-model;
define function process-pending-generic-models (heap :: )
let pgfs = *heap-pending*.heap-pending-generics;
*heap-pending*.heap-pending-generics := #f;
let rest
= remove-if!(pgfs,
method (pgf :: )
let gf = pgf.pending-generic-function;
pgf.pending-generic-unclaimed-methods
:= remove-if!(pgf.pending-generic-unclaimed-methods,
curry(process-generic-function-model,
heap, gf));
pgf.pending-generic-unclaimed-domains
:= remove-if!(pgf.pending-generic-unclaimed-domains,
curry(process-generic-function-model,
heap, gf));
when (pgf.pending-generic-unclaimed-methods == #() &
pgf.pending-generic-unclaimed-domains == #())
// Claim them and remove from list
do-claim-generic-function-modifying-models(heap, gf);
#t
end;
end method);
// Concatenate
iterate loop (last = #f, this = *heap-pending*.heap-pending-generics)
if (this)
loop(this, this.pending-generic-next)
elseif (last)
last.pending-generic-next := rest
else
*heap-pending*.heap-pending-generics := rest
end
end;
end process-pending-generic-models;
define function remove-unclaimed-pending-models (heap :: )
while (*heap-pending*.heap-pending-generics)
let pgf = *heap-pending*.heap-pending-generics;
*heap-pending*.heap-pending-generics := pgf.pending-generic-next;
remove-unclaimed-pending-models-1
(heap, pgf.pending-generic-function,
pgf.pending-generic-unclaimed-methods,
pgf.pending-generic-unclaimed-domains);
end;
end remove-unclaimed-pending-models;
define method remove-unclaimed-pending-models-1
(heap :: , gf :: <&generic-function>, methods, domains)
debug-assert(gf.%generic-function-methods-initialized?, "Uninitialized gf?");
gf.%generic-function-methods
:= choose(method (m) ~member?(m, methods) end,
gf.%generic-function-methods);
do-claim-generic-function-modifying-models(heap, gf);
end remove-unclaimed-pending-models-1;
define method remove-unclaimed-pending-models-1
(heap :: , gf :: <&incremental-generic-function>, methods, domains)
debug-assert(gf.%generic-function-domains-initialized?, "Uninitialized gf?");
gf.%generic-function-domains
:= choose(method (d) ~member?(d, domains) end,
gf.%generic-function-domains);
for (d :: <&domain> in gf.%generic-function-domains,
a = #f then begin ^domain-next(d) := a; d end)
finally ^incremental-gf-domain-info(gf) := a
end for;
next-method();
end remove-unclaimed-pending-models-1;
define function process-pending-init-forms (heap :: )
// I don't think currently init forms can get added during claiming, but
// for the sake of generality, allow for that.
let forms :: = *heap-pending*.heap-pending-init-forms;
*heap-pending*.heap-pending-init-forms := #();
let rest = remove-if!(forms, curry(process-pending-init-form, heap));
*heap-pending*.heap-pending-init-forms
:= concatenate!(*heap-pending*.heap-pending-init-forms, rest);
end process-pending-init-forms;
define method model-externally-accessible? (heap :: , model :: <&domain>)
// TODO: maybe can skip domain auto-generated for sealed methods if the
// method is skipped.
let types = ^domain-types(model);
~types | block (return)
for (type in types,
count from 0 below model.^domain-number-required)
unless (type-can-have-instances?(heap, type))
return(#f)
end;
end;
#t
end;
end;
define method model-externally-accessible? (heap :: , model :: <&method>)
// A method is externally accessible only if all of its specializers
// can have instances. If any of the specializers can't have instances,
// then the method can never be applicable, and so it might as well
// not exist.
let sig = ^function-signature(model);
~sig | block (return)
for (type in sig.^signature-required,
count from 0 below sig.^signature-number-required)
unless (type-can-have-instances?(heap, type))
// If a specializer can't have instances, the method can't possibly
// be applicable, so it might as well not exist.
return(#f);
end;
end;
#t
end;
end;
define method type-can-have-instances? (heap :: , type)
#t
end;
define method type-can-have-instances? (heap :: , class :: <&class>)
~internal-object?(heap, class) | heap-element-claimed?(heap, class)
end;
define method type-can-have-instances? (heap :: , type :: <&singleton>)
let object = ^singleton-object(type);
~instance?(object, <&class>)
| ~internal-object?(heap, object) | heap-element-claimed?(heap, object)
end;
define method type-can-have-instances? (heap :: , type :: <&subclass>)
let class = ^subclass-class(type);
~internal-object?(heap, class) | heap-element-claimed?(heap, class)
end;
define method type-can-have-instances? (heap :: , type :: <&union>)
type-can-have-instances?(heap, type.^union-type1)
| type-can-have-instances?(heap, type.^union-type2)
end;
//// Deferred models
// *** DEBUGGING
define method maybe-claim-heap-element (heap ::,
parent,
e :: ,
ct-ref?)
error("Who is claiming %= from %=?", e, parent)
end;
define method record-deferred-model-reference
(heap :: , ref, model :: )
let table = heap.heap-deferred-model-references;
table[model] := add-new!(element(table, model, default: #()), ref, test: \=);
end;
define method install-deferred-model-reference
(heap :: , ref :: , value)
element(ref.head, ref.tail) := value
end;
define method install-deferred-model-reference
(heap :: , ref :: , value)
ref.reference-value := value
end;
define method process-heap-deferred-models (cr :: )
with-dependent ($compilation of cr)
let heap = cr.compilation-record-model-heap;
for (refs keyed-by model in heap.heap-deferred-model-references)
let value = compute-heap-deferred-model(heap, model);
for (ref in refs)
install-deferred-model-reference(heap, ref, value)
end;
end;
end;
end;
define method compute-heap-deferred-model
(heap :: , model :: )
=> (all-classes :: )
let ld = heap-library(heap);
let all-classes = make();
for (obj in ld.library-externally-visible-models)
when (instance?(obj, <&class>) & ~instance?(obj, )
& ~heap-imported-object?(heap, obj))
all-classes := add!(all-classes, obj);
end;
end;
let all-classes = as(, all-classes);
// Nothing really cares about the order, but force an order so can
// have reproducible heaping.
sort!(all-classes, test: method (c1 :: <&class>, c2 :: <&class>)
defined-after?(c1.model-definition, c2.model-definition)
end);
// Assign class dispatch keys in the dylan library. Dispatch keys are integers, so
// they don't need to get claimed, so it's ok to assign them this late.
when (compiling-dylan-library?())
for (obj in all-classes, index :: from 0 by 1)
let key :: = ^iclass-number-to-key(index);
^iclass-dispatch-key(^class-implementation-class(obj)) := key;
end;
end;
// Update direct-subclasses slots
local method mark-carefully (heap, parent, obj)
if (heap-imported-object?(heap, obj))
record-external-heap-element-reference(heap, parent, obj, #f);
elseif (~internal-object?(heap, obj))
debug-assert(heap-element-referenced?(heap, obj, #f),
"Introducing new reference in all-class computation?");
else
mark-heap-element(heap, parent, obj);
let &class = &object-class(obj);
debug-assert(heap-imported-object?(heap, &class)
| model-externally-visible?(&class),
"Introducing new object in all-class computation?");
maybe-claim-heap-element(heap, obj, &class, #f);
end;
end method;
for (obj in all-classes)
let ic :: <&implementation-class> = obj.^class-implementation-class;
// debug-assert(%direct-subclasses-initialized?(ic));
let subclasses :: = ic.^direct-subclasses;
let claimed-subclasses
= if (every?(model-externally-visible?, subclasses))
subclasses // don't copy...
else
mapped-model(choose(model-externally-visible?, subclasses));
end;
ic.^direct-subclasses := claimed-subclasses;
let ic-heap = if (internal-object?(heap, ic)) // includes combined heap case
heap
else
record-external-heap-element-reference(heap, #f, obj, #f);
ic.model-compilation-record.compilation-record-model-heap;
end;
for (pair = claimed-subclasses then pair.tail, until: pair == #())
mark-carefully(ic-heap, ic, pair);
let subc = pair.head;
if (internal-object?(ic-heap, subc))
debug-assert(heap-element-claimed?(ic-heap, subc));
else
record-external-heap-element-reference(ic-heap, ic, subc, #f);
end;
finally
mark-carefully(ic-heap, ic, #());
end;
end for;
let classvec = immutable-model(all-classes);
record-repeated-size(heap, classvec);
mark-carefully(heap, #f, classvec);
classvec
end method;
define method record-repeated-size-explicitly
(heap :: , class, size) => ()
when (heap-record-repeated-object-sizes?(heap))
let sizes ::
= if (internal-object?(heap, class))
heap-defined-repeated-object-sizes
else
heap-referenced-repeated-object-sizes
end if;
heap.sizes[class]
:= add-new!(element(heap.sizes, class, default: #()), size);
end when;
end method;
define method record-repeated-size (heap :: , object) => ()
when (heap-record-repeated-object-sizes?(heap))
let class = object.&object-class;
let rslotd = class.^repeated-slot-descriptor;
if (rslotd)
let value = ^slot-value(object, rslotd.^size-slot-descriptor);
record-repeated-size-explicitly(heap, class, value);
end if;
end when;
end method;
define method record-heap-load-bound-reference
(heap :: , object, ref) => ()
let ref-table = heap-load-bound-references(heap);
let refs = element(ref-table, object, default: #());
ref-table[object] := pair(ref, refs);
end method;
/// INTERNAL ALREADY TRACED OBJECT DETERMINATION
define inline method internal-object?
(heap :: , object)
// TODO: SHOULD BE DEBUG-ASSERT
// if (instance?(object, <&implementation-class>)
// & object.model-compilation-record ~== heap.heap-compilation-record)
// break("About to assert implementation-class %= external.", ^iclass-class(object))
// end if;
object.model-compilation-record == heap.heap-compilation-record
end;
define inline method internal-binding?
(heap :: , object :: )
object.binding-compilation-record == heap.heap-compilation-record
end;
/// IMPORTED FROM ANOTHER LIBRARY DETERMINATION
define inline function library-imported-object? (ld :: , object)
model-library(object) ~== ld
end function;
define inline function library-imported-binding?
(ld :: , object :: )
debug-assert(valid-binding-home-library-in?(ld, object));
binding-imported-into-library?(object);
end function;
define inline function heap-imported-object? (heap :: , object)
library-imported-object?(heap-library(heap), object)
end function;
define inline function heap-imported-binding?
(heap :: , object :: )
library-imported-binding?(heap-library(heap), object)
end function;
define inline method internal-object?
(heap :: , object)
// TODO: SHOULD BE DEBUG-ASSERT
// if (instance?(object, <&implementation-class>)
// & heap-imported-object?(heap, object))
// break("About to assert implementation-class %= external.", ^iclass-class(object))
// end if;
~heap-imported-object?(heap, object)
end;
define inline method internal-binding?
(heap :: , object :: )
~heap-imported-binding?(heap, object)
end;
define macro with-merged-literal
{ with-merged-literal (?:variable = ?lvalue:expression)
?:body
end }
=> { let (?variable, changed?) = maybe-merge-literal(?lvalue);
if (changed?) ?lvalue := ?variable end;
?body }
end macro;
// external visibility
define method do-export (heap :: , e)
let e = standard-model-object(e);
unless (model-externally-visible?(e))
model-externally-visible?(e) := #t;
// Eagerly force a run-time claim if necessary.
maybe-claim-heap-element(heap, #f, e, element-compile-stage-only?(e));
make-binding-externally-visible(heap, e);
when (heap-element-seen?(heap, e))
// might have already been processed but without the exports
// have to walk again to get all the exports marked
let ct? = member?(e, *heap-pending*.heap-compile-time-elements);
export-references(heap, e, ct?);
end;
end;
end;
define method do-export (heap :: , e :: )
when (e == #()) next-method() end; // TODO: Emulator-specific hack.
end;
define method do-export (heap :: , e :: )
end;
define method do-export (heap :: , e :: )
when (empty?(e)) next-method() end; // other strings never exported
end;
define method do-export (heap :: , e :: <&primitive>)
end;
define inline method maybe-export-derived (heap :: , e)
// Doesn't require a definition
unless (direct-object?(e) | heap-imported-object?(heap, e))
do-export(heap, e)
end;
end;
define method maybe-export (heap :: , e, ct-ref?)
unless (direct-object?(e) | heap-imported-object?(heap, e)
| ~model-has-definition?(e))
do-export(heap, e);
end;
end method;
define method maybe-export (heap :: , e :: <&iep>, ct-ref?)
unless (direct-object?(e) | heap-imported-object?(heap, e)
| ~model-has-definition?(e.function))
do-export(heap, e);
end;
end method;
define method maybe-export (heap :: , e :: <&mm-wrapper>, ct-ref?)
let class = e.^mm-wrapper-implementation-class.^iclass-class;
unless (direct-object?(e) | heap-imported-object?(heap, e)
| ~model-has-definition?(class))
do-export(heap, e);
end;
end method;
define method maybe-export (heap :: , e :: , ct-ref?)
unless (heap-imported-binding?(heap, e))
do-export(heap, e);
// The binding might have been already processed and skipped due to not
// being exported, so now have to force it.
maybe-claim-heap-element(heap, #f, e, ct-ref?);
end;
end method;
define function maybe-export-sequence (heap :: , s :: , ct-ref?)
for (e in s) maybe-export(heap, e, ct-ref?) end;
end;
define method export-references (heap :: ,
e :: ,
ct?)
let form = untracked-binding-definition(e, default: #f);
if (instance?(form, ))
for (symbol in form-shared-symbols(form))
unless (model-externally-visible?(symbol))
model-externally-visible?(symbol) := #t;
maybe-claim-heap-element(heap, e, symbol, ct?);
end;
end;
else
let value = merged-binding-value(e);
when (value) maybe-export(heap, value, ct?) end;
let type = merged-binding-type(e);
when (type) maybe-export(heap, type, ct?) end;
end;
end;
define function merged-binding-value (binding :: )
let value = binding-model-or-hollow-object(binding);
let (value, changed?) = maybe-merge-literal(value);
// assume models that could be hollow (classes, gf's) never actually change
if (changed?) binding-cached-model-object(binding) := value end;
value
end;
define function merged-binding-type (binding :: )
let value = binding-type-model-object(binding);
let (value, changed?) = maybe-merge-literal(value);
if (changed?) binding-cached-type-model-object(binding) := value end;
value
end;
define method export-references
(heap :: , e :: <&library>, ct?) => ()
with-merged-literal (value = ^used-libraries(e))
maybe-export-sequence(heap, value, ct?)
end;
end method;
define method export-references
(heap :: , e :: <&used-library>, ct?) => ()
maybe-export(heap, ^used-library(e), ct?)
end method;
define method export-references
(heap :: , e :: <&module>, ct?) => ()
maybe-export(heap, ^home-library(e), ct?)
end method;
define method export-references
(heap :: , e :: <&domain>, ct?) => ()
maybe-export(heap, ^domain-library(e), ct?);
maybe-export(heap, ^domain-next(e), ct?);
// // maybe-export(heap, the generic function ??? ) ;
// for (i :: from 0 below ^domain-number-required(e))
// with-merged-literal (type = ^domain-type(e, i))
// maybe-export(walk, type)
// end;
// end for;
end method;
define method export-references
(heap :: , e :: <&class>, ct?) => ()
if (~(^class-sealed?(e)))
// Used directly by subclasses.
maybe-export-sequence(heap, ^direct-superclasses(e), ct?);
maybe-export-sequence(heap, ^slot-descriptors(e), ct?);
for (d in ^direct-inherited-slot-descriptors(e))
maybe-export-init-data(heap, d, ct?);
end;
for (d in ^direct-initialization-argument-descriptors(e))
maybe-export-init-data(heap, d, ct?);
end;
end;
// Used by type checks.
let wrapper = ^class-mm-wrapper(e);
// format-out(">>> Walking wrapper for %=: %=\n", e, wrapper);
wrapper & maybe-export-derived(heap, wrapper);
// Direct call to the constructor may be generated implicitly.
maybe-export(heap, ^class-constructor(e), ct?);
end method;
define method export-references
(heap ::