Module: dfmc-conversion Synopsis: The method definition processor. Author: Keith Playford 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 //// Method class framework. define constant $compiler-method-class-map = make(); define method define-compiler-method-class (tag :: , method-class) => () element($compiler-method-class-map, tag) := method-class end method; define method lookup-compiler-method-class (tag :: ) => (method-class) element($compiler-method-class-map, tag, default: #f) | error("Failed to resolve the compiler method-class %=.", tag); end method; define-compiler-method-class(#"simple", <&method>); define-compiler-method-class(#"initializer", <&initializer-method>); define-compiler-method-class(#"getter", <&getter-method>); define-compiler-method-class(#"setter", <&setter-method>); define-compiler-method-class(#"repeated-getter", <&repeated-getter-method>); define-compiler-method-class(#"repeated-setter", <&repeated-setter-method>); define-compiler-method-class(#"copy-down-method", <©-down-method>) ; //// Method modeling. define compiler-sideways method compute-and-install-form-model-objects (form :: ) => () form-evaluation-tried-and-failed?(form) := #f; if (form-dynamic?(form)) compute-and-install-form-dynamic-init-method(form); else compute-and-install-form-model-objects-statically(form); end; end method; define function get-form-generic-definition (form) => (gf-def, gf-static?) let binding = form-variable-binding(form); let gf-def = binding-definition(binding, default: #f); let gf-static? = gf-def & if (current-library-description?(form-library(gf-def))) // Have to be slightly careful about evaluation order, hence // the implictly defined check shortcut which is a precondition // for model computation of the generic looking back at its // methods. form-implicitly-defined?(gf-def) | binding-model-object(binding) else binding-model-object(binding) end; values(gf-def, gf-static?) end function; define method compute-and-install-form-model-objects-statically (form :: ) => () let name = form-variable-name(form); let (gf-def, gf-static?) = get-form-generic-definition(form); if (~gf-static?) // Oops, we have to be dynamic after all. debug-out(#"dynamic", ">>> GF forced retreat to the dynamic case for %=\n", form); form-evaluation-tried-and-failed?(form) := #t; compute-and-install-form-dynamic-init-method(form); else // We're at least partly static; the g.f. is known. But if the method isn't, we have // to add it at load time, and check congruency too. let model = compute-form-model-object(form, name); let model = if (model & (form-equivalent-method-definition(gf-def) == form | check-model-at-definition(model))) // The equivalent method definition check gets around potential // circularity problems. model else #f end; let lib = library-description-model(form-library(form)); if (model) lambda-top-level?(model) := #t; form-model(form) := model; else form-evaluation-tried-and-failed?(form) := #t; end; let method-locally-defined? = form-library(gf-def) == form-library(form); unless (form-compile-stage-only?(gf-def) | (method-locally-defined? & model) | (~model & form-handled-by-make-when-dynamic?(form)) // | (model & ~method-locally-defined? // & single-method-generic-function?(lookup-model-object(name, reference?: #f))) ) let code = (with-expansion-source-form(form) let mcode = if (~model) let signature-and-body = form-signature-and-body-fragment(form); #{ generic-method (?form) ?signature-and-body end } else #{ ?model } end if; let gf-runtime-sealed? = (form-sealable?(gf-def) | form-compiler-open?(gf-def)); // Do we, at runtime, need to add domain for this method? We // do if it's a sealed method, and one is not going to be added statically in // the gf model. let mdomain? = ~gf-runtime-sealed? & form-sealed?(form); // We only need to check congruency for dynamically computed methods. let check-congruency? = if (model) #f else #t end; // Do we check sealing? // - We skip it (thus skipping the other hairier computations) if the gf is runtime-sealed. // - We can also skip it if we are in the defining library of the generic, as there's no // way anyone could have put on a domain to exclude us. (Being not in incremental // definition mode we don't have to worry about out-of-order definitions. // - Finally, we can also skip it if some specialized type is defined in our own // library, which has the same consequence. This last check could be smarter for // dynamically computed methods by looking at those specializers it can rather than // just punting entirely in that case. let check-sealing? = (~gf-runtime-sealed? & ~method-locally-defined? & (~model | all-types-known-imported? (model-library(model), ^function-signature(model)))); if (~check-congruency? & check-sealing? & ~mdomain?) let definer = dylan-value(#"%add-method"); #{ ?definer(?name, ?mcode, ?lib) } elseif (gf-runtime-sealed? & check-congruency? & ~check-sealing? & ~mdomain?) let definer = dylan-value(#"%add-dynamic-method"); #{ ?definer(?name, ?mcode) } else let definer = dylan-value(#"%add-a-method"); #{ ?definer(?name, ?mcode, ?lib, ?check-congruency?, ?check-sealing?, ?mdomain?) } end if end with-expansion-source-form); let init-model = convert-top-level-initializer(code); form-init-method(form) := init-model; end unless; end if; end method; /* define function ^method-generic-function-definition (object :: <&method>) let binding = model-variable-binding(object); binding-definition(binding); end function; */ // Despite its name, this function actually gets used to create lambdas // sometimes (by the C-callable constructor). define method compute-method-explicitly (method-class :: , form, name, signature-spec, body-spec, #rest options, #key, #all-keys) => (model :: <&method>) apply(^make, method-class, definition: form, body-spec: body-spec, // debug-name: // ~form & name & mapped-model(as-lowercase(as(, name))), compiler-debug-name: name, signature-spec: signature-spec, options) end method; define method install-method-signature (m :: <&lambda>, form :: , sig :: <&signature>) => () ^function-signature(m) := sig end method; define method install-method-signature (m :: <&accessor-method>, form :: , sig :: <&signature>) => () ^function-signature(m) := sig; // ^method-slot-descriptor(m) := compute-signature(form, signature-spec); end method; /* define method install-method-signature (m :: <&getter-method>, form :: , signature-spec :: ) let req-spec = spec-argument-required-variable-specs(spec); let class-spec = req-spec[0]; ^top-level-eval(class-spec if (instance?(method-object, <&lambda>)) ^function-signature(method-object) := compute-signature(form, signature-spec); end if; end method; */ define compiler-sideways method compute-form-model-object (form :: , name :: ) => (model :: false-or(<&method>)) let signature-spec = form-signature(form); let (sig-object, static?) = compute-signature(form, signature-spec); if (static?) let method-class = lookup-compiler-method-class(form-class(form)); let method-object = compute-method-explicitly (method-class, form, name, signature-spec, form-body(form)); install-method-signature(method-object, form, sig-object); // One final check here. If we get this far but it's an accessor // method and the class as a whole is dynamic, we still can't // have the method. if (instance?(method-object, <&accessor-method>)) let class = ^signature-required(sig-object) [accessor-method-dispatch-arg(method-object)]; if (^ensure-slots-initialized(class)) // Install the slot descriptor before we return. let gf = lookup-model-object(name, default: #f, reference?: #f); if (gf) let slotd = ^slot-descriptor(class, gf); ^method-slot-descriptor(method-object) := slotd; method-object end; end; else method-object end; end; end method; define compiler-sideways method form-top-level-methods (form :: ) => (methods :: ) let inits = next-method(); let model = form-model(form); if (model) pair(model, inits) else inits end; end method; define compiler-sideways method retract-form-model-objects (form :: ) => () library-description-system-gf-init-code(form-library(form)) := #f; next-method() end method; // Note that maybe-compute-and-install-method-dfm is the client entry // point, while compute-and-install-method-dfm is for implementors of // the protocol. This allows us to relieve specific implementing methods // of setting up the appropriate context, since we can guarantee that // this has been done before compute-and-install-method-dfm has been // called. define method maybe-compute-and-install-method-dfm (m :: <&method>) => () end method; define method maybe-compute-and-install-method-dfm (m :: <&lambda>) => () unless (m.body | m.lambda-optimized?) // OR RETRACTED BUT 1ST GROKKED with-simple-abort-retry-restart ("Skip generating DFM for this method", "Retry generating DFM for this method") with-dependent-context($compilation of model-creator(m)) compute-and-install-method-dfm(m); end; end; end unless; end method; define method retract-method-dfm (m) => () end method; define method slot-initial-value-method? (f :: <&method>) => (res :: ) let creator = model-creator(f); instance?(creator, ) | (~lambda-top-level?(f) & instance?(creator, ) & form-class(creator) == #"initializer") end method; define method dodgy-method? (f :: <&lambda>) => (well? :: ) ~model-has-definition?(f) & lambda-top-level?(f) end method; define method method-indirectly-inlineable? (f :: <&lambda>) => (well? :: ) unless (lambda-top-level?(f)) local method outer-lambda (f :: <&lambda>) => (res :: false-or(<&lambda>)) let env = environment(f); let outer-env = env & lambda-environment(outer(env)); outer-env & lambda(outer-env) end method; iterate loop (outer :: false-or(<&lambda>) = outer-lambda(f)) if (~outer) #f elseif (lambda-top-level?(outer)) method-inlineable?(outer) else loop(outer-lambda(outer)) end if; end iterate; end unless; end method; define method method-fragments-strippable? (x :: <&lambda>) => (well? :: ) #t end method; define variable *strip-enabled?* = #t; define method method-dfm-strippable? (x :: <&lambda>) => (well? :: ) when (*strip-enabled?*) lambda-initializer?(x) | ~(method-inlineable?(x) | method-indirectly-inlineable?(x) | slot-initial-value-method?(x) | dodgy-method?(x) | lambda-copied-down?(x)) end when; end method; define compiler-sideways method retract-body-fragments (m :: <&lambda>) => () when (method-fragments-strippable?(m)) when (lambda-body(m)) body-spec(m) := #f; end; let form = model-definition(m); if (instance?(form, )) strip-incremental-slots(form); end if; end when; end method; define compiler-sideways method strip-incremental-slots (x :: <&lambda>) lambda-heap(x) := #f; retract-body-fragments(x); strip-incremental-slots(^iep(x)); end method; define compiler-sideways method strip-incremental-slots (x :: <&iep>) code(x) := #f; end method; define method retract-method-dfm (m :: <&lambda>) => () if (method-dfm-strippable?(m) & lambda-body(m)) m.parameters := #f; m.body := #f; m.environment := #f; m.users := #(); m.optimization-queue := #f; m.lambda-body := #f; // format-out("RETRACTING %=\n", m); else lambda-body(m) & (m.optimization-queue := #f); when (lambda-top-level?(m)) m.users := #(); end when; end if; end method; define method compute-and-install-method-dfm (method-object :: <&method>) => () end method; define method compute-and-install-method-dfm (method-object :: <&lambda>) => () let body = compute-method-body(method-object); if (body) with-parent-source-location (model-source-location(method-object)) convert-lambda-into*($top-level-environment, method-object, body); // format-out("COMPUTING DFM FOR %=\n", method-object); end; retract-body-fragments(method-object); // one of this and the one below is redundant lambda-optimized?(method-object) := #f; end if; end method; define method compute-method-body (m :: <&lambda>) body-spec(m) end method; /// SHOULD HAPPEN AFTER OPTIMIZATION BUT CONTROL WOULD HAVE TO /// CHANGE TO NOT RETRACT UNTIL AFTER OPTIMIZATION DERIVED INLINEABILITY define constant $maximum-inlining-cost = 0; define generic computation-inlining-cost (c :: ) => (res :: ); define method computation-inlining-cost (c :: ) => (res :: ) 1 end method; define method computation-inlining-cost (c :: ) => (res :: ) 0 end method; define method computation-inlining-cost (c :: ) => (res :: ) 0 end method; define method computation-inlining-cost (c :: ) => (res :: ) 0 end method; define method computation-inlining-cost (c :: ) => (res :: ) 0 end method; define method update-lambda-inlineable? (f :: <&lambda>) when (lambda-inlineable?(f) == #"unknown") let definition = model-definition(f); let inlineable? = if (lambda-top-level?(f) & definition & form-inline-policy(definition) == #"default-inline" & empty?(f.environment.inners) & ~instance?(f, <©-down-method>)) let cost :: = 0; let inlineable? = block (return) walk-lambda-computations // ESTIMATE INLINING COST (method (c) walk-computation-references (method (c, ref, object) ignore(c); ignore(ref); unless (inlineable?(object)) return(#f) end unless end method, c); cost := cost + computation-inlining-cost(c); when (cost > $maximum-inlining-cost) return(#f) end when; end method, f.body); #t end block; // when (inlineable?) // format-out("LAMBDA %= INLINEABLE %=\n", f, cost); // end when; inlineable? else #f end if; lambda-inlineable?(f) := inlineable?; end when; end method; define method method-inlineable? (f :: <&method>) => (res :: ) let definition = model-definition(f); if (definition) update-lambda-inlineable?(f); assert(lambda-inlineable?(f) ~== #"unknown", "uninitialized lambda-inlineable? slot %=", f); let policy = form-inline-policy(definition); ~(policy == #"not-inline" | policy == #"default-inline") | model-compile-stage-only?(f) | (policy ~== #"not-inline" & lambda-inlineable?(f) == #t) end if; end method; define method ensure-method-optimized (f :: <&method>) maybe-compute-and-install-method-dfm(f) ; run-compilation-passes(f); end method; define method ensure-method-model (f :: <&method>) end method; define method ensure-method-model (f :: <&lambda>) if (f.lambda-optimized?) // quick check ensure-lambda-body(f); else block () ensure-method-optimized(f) cleanup let ld = current-top-level-library-description(); when (ld ~== model-library(f) & method-fragments-strippable?(f)) retract-method-dfm(f); end when; end block; end if; end method; define method ensure-optimized-method-model (f :: <&method>) ensure-method-model (f) end method; define method ensure-method-dfm (f :: <&method>) end method; define method force-method-model (f :: <&lambda>) // retract-method-dfm(f); // MAKE SURE CLEAN START lambda-optimized?(f) := #f; // FORCE DFM TO BE BUILT end method; define method ensure-method-dfm (f :: <&lambda>) unless (body(f)) force-method-model(f); end unless; ensure-method-model(f); end method; define method ensure-method-dfm-or-heap (f :: <&method>) end method; define method ensure-method-dfm-or-heap (f :: <&lambda>) unless (lambda-heap(f) | body(f)) force-method-model(f); maybe-compute-and-install-method-dfm(f); end unless; end method; // markt - copy-down support define compiler-open generic copy-down-body (m :: <©-down-method>) => () ; define method compute-and-install-method-dfm (m :: <©-down-method>) => () next-method () ; let bind = m.body ; if (bind) copy-down-body (m) // this is in optimization/inlining, but exported from conversion else format-out ("seem not to have dummy-body for a copy-down %s\n", m) end; end; //// Dynamic incremental version define method compute-and-install-form-dynamic-init-method (form :: ) => () // Filter out accessor methods, which are created and added dynamically // within make. if (~form-handled-by-make-when-dynamic?(form)) next-method(); end; end method; define method compute-form-dynamic-init-code (form :: ) => (computed-method) let name = form-variable-name(form); let ld = form-library(form); let lib = library-description-model(ld); let signature-and-body = form-signature-and-body-fragment(form); let definer = if (form-sealed?(form)) dylan-value(#"%define-sealed-method"); else dylan-value(#"%define-method"); end if; (with-expansion-source-form (form) #{ ?definer(?name, generic-method (?form) ?signature-and-body end, ?lib) } end with-expansion-source-form) end method; // eof