~ chicken-core (chicken-5) 41a1decf5762c1178a9da2250df39a28badbb736
commit 41a1decf5762c1178a9da2250df39a28badbb736 Author: Evan Hanson <evhan@foldling.org> AuthorDate: Sat May 9 20:15:46 2020 +1200 Commit: Evan Hanson <evhan@foldling.org> CommitDate: Tue Nov 3 18:11:03 2020 +1300 Rework library loading to support conditional unit entry This makes a handful of changes to the way library dependencies are processed in order to support conditional unit loading, i.e. not calling a unit's entry procedure until its code path is actually visited. This resolves a difference in behaviour between dynamically compiled libraries whose entrypoints are only called when their code path is visited, and statically-compiled libraries whose entrypoints are currently hoisted to the start of the program and always called, regardless of whether or where they're actually used. Drop the `file-requirements' hash table in favour of two global lists, `required-libraries' and `linked-libraries', the first of which is a superset of the second. The `linked-libraries' list includes everything that should be linked with the program statically (i.e. as a unit), and everything else is a runtime dependency (i.e. loaded as a shared object or source file). Split the batch driver's `initforms' list into two separate values, one for import forms (which must be included within the program's wrapper module's body, if one is used) and one for compiler-introduced initialisation forms (which must precede the profiling-related expressions that are inserted into the program when profiling is enabled, since they're responsible for loading the "profiler" unit). This is necessary because all libraries now go through the compiler's logic for `##core#require' nodes, whereas units were previously exempt. Move all "forms" bindings together in the `let' that introduces them. Introduce a new `used-libraries' global to keep track of units that are specified with "-uses" or `(declare (uses))'. These are hoisted to the top level and called at the start of the program, just like before this change. The list of `used-units', which is used to generate prototypes for external unit toplevels in the C backend, is constructed by simply remembering all `##core#callunit' nodes as they're encounted during canonicalisation. Because each import form now has the potential to introduce a call to a unit entrypoint, `##core#callunit' nodes are deduplicated during CPS. This ensures that only one call to a given unit's entrypoint will be generated along a given code path. Simplify `##sys#process-require' so that it expects just a library, module, and compilation flag as arguments, and returns just a single value. Get rid of the global `provided' and `linked-static-extensions' lists, which are no longer necessary. For modules that export syntax in static libraries (where module information is compiled into the libraries themselves), generate code that will load the module's library dependencies *before* the code for runtime evaluation of the module's import forms, that is, "(scheme#eval '(import-syntax ...))". This ensures that static programs do not attempt to dynamically load the named import libraries dynamically, since dlopen(3) et al. are disabled by static compilation. We communicate this situation to `##sys#compiled-module-registration' with a compile mode flag, for consistency with `##sys#process-require'. Change the meaning of the "-link" option so that it indicates libraries that should be linked iff they're required, rather than always generating a call to their entrypoints (thus requiring them to be linked unconditionally, as is the case with "-uses"). This option now also needs to be plumbed through to the "chicken" program, which handles the differentiation between static and (potentially) dynamic requirements. Simplify some of the internal procedures in eval.scm. The `load-library/internal' and `##sys#load-library' procedures can be combined, as can `load-extension/internal' and `load-extension'. Rename the internal version of the `load-library' procedure to `load-unit', since that's really what it does, and use it in the expansion of `##core#require'. Drop the `stripu' alias from `process-declaration' and simply call `strip-syntax' directly instead. `stripu' was only used in two places. Signed-off-by: felix <felix@call-with-current-continuation.org> diff --git a/batch-driver.scm b/batch-driver.scm index 206d4089..9e91d916 100644 --- a/batch-driver.scm +++ b/batch-driver.scm @@ -194,8 +194,7 @@ (register-feature! 'chicken-compile-static)) (let* ((dynamic (memq 'dynamic options)) (unit (memq 'unit options)) - (initforms `((import-for-syntax ,@default-syntax-imports) - (##core#declare + (init-forms `((##core#declare ,@(append default-declarations (if emit-debug-info @@ -211,11 +210,12 @@ (or (not compile-module-registration) (eq? compile-module-registration 'yes))) '((uses eval-modules)) - '()))) - ,@(if explicit-use-flag - '() - `((import ,@default-imports))))) - (verbose (memq 'verbose options)) + '()))))) + (import-forms `((import-for-syntax ,@default-syntax-imports) + ,@(if explicit-use-flag + '() + `((import-syntax ,@default-imports))))) + (cleanup-forms '(((chicken.base#implicit-exit-handler)))) (outfile (cond ((memq 'output-file options) => (lambda (node) (let ((oname (option-arg node))) @@ -227,14 +227,12 @@ (ipath (map chop-separator (##sys#split-path (or (get-environment-variable "CHICKEN_INCLUDE_PATH") "")))) - (opasses (default-optimization-passes)) (time0 #f) (time-breakdown #f) (forms '()) (inline-output-file #f) (type-output-file #f) - (cleanup-forms '(((chicken.base#implicit-exit-handler)))) (profile (or (memq 'profile options) (memq 'accumulate-profile options) (memq 'profile-name options))) @@ -352,8 +350,9 @@ (when (memq 'b debugging-chicken) (set! time-breakdown #t)) (when (memq 'raw options) (set! explicit-use-flag #t) - (set! cleanup-forms '()) - (set! initforms '()) ) + (set! init-forms '()) + (set! import-forms '()) + (set! cleanup-forms '())) (when (memq 'no-lambda-info options) (set! emit-closure-info #f) ) (when (memq 'no-compiler-syntax options) @@ -363,7 +362,8 @@ (when (memq 'inline-global options) (set! enable-inline-files #t) (set! inline-locally #t)) - (when verbose + (when (memq 'verbose options) + (set! verbose-mode #t) (set! ##sys#notices-enabled #t)) (when (memq 'strict-types options) (set! strict-variable-types #t) @@ -428,7 +428,6 @@ (keyword-style #:none) (parentheses-synonyms #f) (symbol-escape #f) ) - (set! verbose-mode verbose) (set! ##sys#read-error-with-line-number #t) (set! ##sys#include-pathnames (append (map chop-separator (collect-options 'include-path)) @@ -486,13 +485,19 @@ (lambda (u) (map string->symbol (string-split u ", "))) (collect-options 'uses)))) (unless (null? uses) - (set! forms - (cons `(##core#declare (uses . ,uses)) forms)))) + (set! init-forms + (append init-forms `((##core#declare (uses . ,uses))))))) - ;; Append required extensions to initforms: - (set! initforms + ;; Mark linked libraries so they will be compiled as unit dependencies. + (let ((link (append-map + (lambda (l) (map string->symbol (string-split l ", "))) + (collect-options 'link)))) + (set! linked-libraries (lset-union/eq? linked-libraries link))) + + ;; Append required extensions to imports: + (set! import-forms (append - initforms + import-forms (map (lambda (r) `(import ,(string->symbol r))) (collect-options 'require-extension)))) @@ -524,9 +529,9 @@ "you need to specify -profile-name if using accumulated profiling runs")) (set! emit-profile #t) (set! profiled-procedures 'all) - (set! initforms + (set! init-forms (append - initforms + init-forms default-profiling-declarations (if acc '((set! ##sys#profile-append-mode #t)) @@ -599,18 +604,23 @@ (print-expr "source" '|1| forms) (begin-time) ;; Canonicalize s-expressions - (let* ((exps0 (map (lambda (x) + (let* ((init0 (map canonicalize-expression init-forms)) + (exps0 (map (lambda (x) (fluid-let ((##sys#current-source-filename filename)) (canonicalize-expression x))) - (let ((forms (append initforms forms))) + (let ((forms (append import-forms forms))) (if (not module-name) forms `((##core#module ,(string->symbol module-name) () ,@forms)))))) + (uses0 (map (lambda (u) + (canonicalize-expression `(##core#require ,u))) + (##sys#fast-reverse used-libraries))) (exps (append (map (lambda (ic) `(set! ,(cdr ic) ',(car ic))) immutable-constants) - (map (lambda (uu) `(##core#callunit ,uu)) used-units) + init0 + uses0 (if unit-name `((##core#provide ,unit-name)) '()) (if emit-profile (profiling-prelude-exps (and (not unit-name) @@ -629,18 +639,6 @@ (map (lambda (il) (->string (car il))) import-libraries) ", "))) - (and-let* ((reqs (hash-table-ref file-requirements 'dynamic)) - (missing (remove (cut chicken.load#find-dynamic-extension <> #f) reqs))) - (when (null? (lset-intersection/eq? '(eval repl) used-units)) - (notice ; XXX only issued when "-verbose" is used - (sprintf "~A has dynamic requirements but doesn't load (chicken eval): ~A" - (cond (unit-name "unit") (dynamic "library") (else "program")) - (string-intersperse (map ->string reqs) ", ")))) - (when (pair? missing) - (warning - (sprintf "the following extensions are not currently installed: ~A" - (string-intersperse (map ->string missing) ", "))))) - (when (pair? compiler-syntax-statistics) (with-debugging-output 'S @@ -679,10 +677,7 @@ (initialize-analysis-database) ;; collect requirements and load inline files - (let* ((req (concatenate (vector->list file-requirements))) - (mreq (concatenate (map cdr req)))) - (when (debugging 'M "; requirements:") - (pp req)) + (let ((extensions (remove chicken.load#core-unit? required-libraries))) (when enable-inline-files (for-each (lambda (id) @@ -690,7 +685,7 @@ (symbol->string id) '(".inline") #t #f))) (dribble "Loading inline file ~a ..." ifile) (load-inline-file ifile))) - mreq)) + extensions)) (let ((ifs (collect-options 'consult-inline-file))) (unless (null? ifs) (set! inline-locally #t) @@ -717,7 +712,7 @@ (load-type-database (make-pathname #f (symbol->string id) "types") enable-specialization)) - mreq) + extensions) (begin-time) (set! first-analysis #f) (set! db (analyze 'scrutiny node0)) @@ -853,12 +848,11 @@ (begin-time) ;; generate link file - (when emit-link-file - (dribble "generating link file `~a' ..." emit-link-file) - (with-output-to-file - emit-link-file - (cut pp linked-static-extensions))) - + (when emit-link-file + (let ((exts (remove chicken.load#core-unit? required-libraries))) + (dribble "generating link file `~a' ..." emit-link-file) + (with-output-to-file emit-link-file (cut pp exts)))) + ;; Code generation (let ((out (if outfile (open-output-file outfile) (current-output-port))) ) (dribble "generating `~A' ..." outfile) diff --git a/c-platform.scm b/c-platform.scm index 19a4c97f..ca209b39 100644 --- a/c-platform.scm +++ b/c-platform.scm @@ -109,7 +109,7 @@ module-registration no-module-registration)) (define valid-compiler-options-with-argument - '(debug emit-link-file + '(debug link emit-link-file output-file include-path heap-size stack-size unit uses module keyword-style require-extension inline-limit profile-name prelude postlude prologue epilogue nursery extend feature no-feature diff --git a/chicken-syntax.scm b/chicken-syntax.scm index c1ade0ab..ef9b7aa6 100644 --- a/chicken-syntax.scm +++ b/chicken-syntax.scm @@ -541,7 +541,7 @@ (let-values (((name lib _ _ _ _) (##sys#decompose-import x r c 'import))) (if (not lib) '(##core#undefined) - `(##core#require ,lib ,(module-requirement name))))) + `(##core#require ,lib ,name)))) (cdr x)))))) (##sys#extend-macro-environment diff --git a/core.scm b/core.scm index 584c3453..1e0c7ab7 100644 --- a/core.scm +++ b/core.scm @@ -139,8 +139,8 @@ ; (##core#foreign-callback-wrapper '<name> <qualifiers> '<type> '({<type>}) <exp>) ; (##core#define-external-variable <name> <type> <bool> [<symbol>]) ; (##core#check <exp>) -; (##core#require-for-syntax <id> ...) -; (##core#require <id> <id> ...) +; (##core#require-for-syntax <id>) +; (##core#require <id> [<id>]) ; (##core#app <exp> {<exp>}) ; (##core#define-syntax <symbol> <expr>) ; (##core#define-compiler-syntax <symbol> <expr>) @@ -291,10 +291,6 @@ initialize-compiler perform-closure-conversion perform-cps-conversion prepare-for-code-generation build-toplevel-procedure - ;; These are both exported for use in eval.scm (which is a bit of - ;; a hack). file-requirements is also used by batch-driver - process-declaration file-requirements - ;; Various ugly global boolean flags that get set by the (batch) driver all-import-libraries preserve-unchanged-import-libraries bootstrap-mode compiler-syntax-enabled @@ -309,15 +305,17 @@ disable-stack-overflow-checking emit-trace-info external-protos-first external-variables insert-timer-checks no-argc-checks no-global-procedure-checks no-procedure-checks emit-debug-info - linked-static-extensions ;; Other, non-boolean, flags set by (batch) driver profiled-procedures import-libraries inline-max-size unroll-limit extended-bindings standard-bindings + ;; Non-booleans set and read by the (batch) driver + required-libraries linked-libraries used-libraries + ;; non-booleans set by the (batch) driver, and read by the (c) backend - target-heap-size target-stack-size unit-name used-units provided + target-heap-size target-stack-size unit-name used-units ;; bindings, set by the (c) platform default-extended-bindings default-standard-bindings internal-bindings @@ -377,7 +375,6 @@ (define-constant default-line-number-database-size 997) (define-constant inline-table-size 301) (define-constant constant-table-size 301) -(define-constant file-requirements-size 301) (define-constant default-inline-max-size 20) (define-constant default-unroll-limit 1) @@ -449,9 +446,9 @@ (define callback-names '()) (define toplevel-scope #t) (define toplevel-lambda-id #f) -(define file-requirements #f) -(define provided '()) -(define linked-static-extensions '()) +(define required-libraries '()) +(define linked-libraries '()) +(define used-libraries '()) (define unlikely-variables '(unquote unquote-splicing)) @@ -474,9 +471,6 @@ (set! constant-table (make-vector constant-table-size '())) ) (reset-profile-info-vector-name!) (clear-real-name-table!) - (if file-requirements - (vector-fill! file-requirements '()) - (set! file-requirements (make-vector file-requirements-size '())) ) (clear-foreign-type-table!) ) @@ -612,11 +606,11 @@ (get-real-name x))))) (else x)))) - (define (emit-import-lib name il) + (define (emit-import-lib name mod il) (let* ((fname (if all-import-libraries (string-append (symbol->string name) ".import.scm") (cdr il))) - (imps (##sys#compiled-module-registration (##sys#current-module))) + (imps (##sys#compiled-module-registration mod #f)) (oldimps (and (file-exists? fname) (call-with-input-file fname read-expressions)))) @@ -706,12 +700,12 @@ (hide-variable var) var) ] ) ) ) - ((##core#callunit ##core#primitive ##core#undefined) x) + ((##core#provide ##core#primitive ##core#undefined) x) - ((##core#provide) - (let ((id (cadr x))) - (set! provided (lset-adjoin/eq? provided id)) - `(##core#provide ,id))) + ((##core#callunit) + (let ((unit (cadr x))) + (set! used-units (lset-adjoin/eq? used-units unit)) + `(##core#callunit ,unit))) ((##core#inline_ref) `(##core#inline_ref @@ -723,24 +717,19 @@ ,(walk (caddr x) e dest ldest h ln #f))) ((##core#require-for-syntax) - (chicken.load#load-extension (cadr x) '() 'require) + (chicken.load#load-extension (cadr x) #f #f) '(##core#undefined)) ((##core#require) - (let ((id (cadr x)) - (alternates (cddr x))) - (let-values (((exp type) - (##sys#process-require - id #t - alternates provided - static-extensions - register-static-extension))) - (unless (not type) - (hash-table-update! - file-requirements type - (cut lset-adjoin/eq? <> id) - (cut list id))) - (walk exp e dest ldest h ln #f)))) + (let ((lib (cadr x)) + (mod (and (pair? (cddr x)) (caddr x)))) + (set! required-libraries (lset-adjoin/eq? required-libraries lib)) + (walk (##sys#process-require + lib mod + (if (or (memq lib linked-libraries) static-extensions) + 'static + 'dynamic)) + e dest ldest h ln #f))) ((##core#let) (let* ((bindings (cadr x)) @@ -1010,49 +999,47 @@ ((##core#module) (let* ((name (strip-syntax (cadr x))) - (lib (or unit-name name)) - (req (module-requirement name)) - (exports - (or (eq? #t (caddr x)) - (map (lambda (exp) - (cond ((symbol? exp) exp) - ((and (pair? exp) - (let loop ((exp exp)) - (or (null? exp) - (and (symbol? (car exp)) - (loop (cdr exp)))))) - exp) - (else - (##sys#syntax-error-hook - 'module - "invalid export syntax" exp name)))) - (strip-syntax (caddr x))))) + (il (or (assq name import-libraries) all-import-libraries)) + (lib (and (not standalone-executable) il (or unit-name name))) + (mod (##sys#register-module + name lib + (or (eq? #t (caddr x)) + (map (lambda (exp) + (cond ((symbol? exp) exp) + ((and (pair? exp) + (let loop ((exp exp)) + (or (null? exp) + (and (symbol? (car exp)) + (loop (cdr exp)))))) + exp) + (else + (##sys#syntax-error-hook + 'module + "invalid export syntax" exp name)))) + (strip-syntax (caddr x)))))) (csyntax compiler-syntax)) (when (##sys#current-module) (##sys#syntax-error-hook 'module "modules may not be nested" name)) - (let-values (((body module-registration) - (parameterize ((##sys#current-module - (##sys#register-module name lib exports)) - (##sys#current-environment '()) - (##sys#macro-environment - ##sys#initial-macro-environment) - (##sys#module-alias-environment - (##sys#module-alias-environment))) - (##sys#with-property-restore - (lambda () - (let loop ((body (cdddr x)) (xs '())) - (cond - ((null? body) + (let ((body (parameterize ((##sys#current-module mod) + (##sys#current-environment '()) + (##sys#macro-environment + ##sys#initial-macro-environment) + (##sys#module-alias-environment + (##sys#module-alias-environment))) + (##sys#with-property-restore + (lambda () + (let loop ((body (cdddr x)) (xs '())) + (if (null? body) (handle-exceptions ex (begin ;; avoid backtrace (print-error-message ex (current-error-port)) (exit 1)) (##sys#finalize-module - (##sys#current-module) - (lambda (id) - (cond + mod + (lambda (id) + (cond ((assq id foreign-variables) "a foreign variable") ((hash-table-ref inline-table id) @@ -1061,50 +1048,38 @@ "a constant") ((##sys#get id '##compiler#type-abbreviation) "a type abbreviation") - (else #f))))) - (let ((il (or (assq name import-libraries) all-import-libraries))) - (when il - (emit-import-lib name il) - ;; Remove from list to avoid error - (when (pair? il) - (set! import-libraries - (delete il import-libraries equal?)))) - (values - (reverse xs) - (if (or (eq? compile-module-registration 'yes) - (and (not il) ; default behaviour - (not compile-module-registration))) - (##sys#compiled-module-registration (##sys#current-module)) - '())))) - (else + (else #f)))) + (reverse xs)) (loop (cdr body) - (cons (walk - (car body) - e ;? - #f #f h ln #t) ; reset to toplevel! - xs)))))))))) - (let ((body - (canonicalize-begin-body - (append - (parameterize ((##sys#current-module #f) - (##sys#macro-environment - (##sys#meta-macro-environment)) - (##sys#current-environment ; ??? - (##sys#current-meta-environment))) - (map - (lambda (x) - (walk - x - e ;? - #f #f h ln tl?) ) - (cons `(##core#provide ,req) module-registration))) - body)))) - (do ((cs compiler-syntax (cdr cs))) - ((eq? cs csyntax)) - (##sys#put! (caar cs) '##compiler#compiler-syntax (cdar cs))) - (set! compiler-syntax csyntax) - body)))) + (cons (walk (car body) + e #f #f + h ln #t) ; reset to toplevel! + xs))))))))) + (do ((cs compiler-syntax (cdr cs))) + ((eq? cs csyntax) (set! compiler-syntax csyntax)) + (##sys#put! (caar cs) '##compiler#compiler-syntax (cdar cs))) + (when il + (emit-import-lib name mod il) + (when (pair? il) + (set! import-libraries + (delete il import-libraries equal?)))) + (canonicalize-begin-body + (append + (list (list '##core#provide (module-requirement name))) + (if (or (eq? compile-module-registration 'yes) + (and (not il) ; default behaviour + (not compile-module-registration))) + (parameterize ((##sys#macro-environment + (##sys#meta-macro-environment)) + (##sys#current-environment ; ??? + (##sys#current-meta-environment))) + (map (lambda (x) (walk x e #f #f h ln tl?)) + (##sys#compiled-module-registration + mod + (if static-extensions 'static 'dynamic)))) + '()) + body))))) ((##core#loop-lambda) ;XXX is this really needed? (let* ((vars (cadr x)) @@ -1560,7 +1535,6 @@ (syntax-error "invalid declaration" spec) ) ) ) (define (stripa x) ; global aliasing (##sys#globalize x se)) - (define stripu strip-syntax) (define (globalize-all syms) (filter-map (lambda (var) @@ -1578,17 +1552,12 @@ (syntax-error "invalid declaration specification" spec) ) (case (strip-syntax (car spec)) ; no global aliasing ((uses) - (let ((us (lset-difference/eq? (stripu (cdr spec)) used-units))) - (when (pair? us) - (set! provided (append provided us)) - (set! used-units (append used-units us)) - (hash-table-update! - file-requirements 'static - (cut lset-union/eq? us <>) - (lambda () us))))) + (let ((units (strip-syntax (cdr spec)))) + (set! used-libraries (lset-union/eq? used-libraries units)) + (set! linked-libraries (lset-union/eq? linked-libraries units)))) ((unit) (check-decl spec 1 1) - (let ((u (stripu (cadr spec)))) + (let ((u (strip-syntax (cadr spec)))) (when (and unit-name (not (eq? unit-name u))) (warning "unit was already given a name (new name is ignored)")) (set! unit-name u) @@ -1830,12 +1799,6 @@ '(##core#undefined) ) ) ) -;;; Register statically linked extension - -(define (register-static-extension id) - (set! linked-static-extensions (cons id linked-static-extensions))) - - ;;; Create entry procedure: (define (build-toplevel-procedure node) @@ -1954,6 +1917,7 @@ ;;; Convert canonicalized node-graph into continuation-passing-style: (define (perform-cps-conversion node) + (let ((called-units '())) (define (cps-lambda id llist subs k) (let ([t1 (gensym 'k)]) @@ -2017,7 +1981,12 @@ ##core#inline_loc_update ##core#debug-event) (walk-inline-call class params subs k) ) ((##core#call) (walk-call (car subs) (cdr subs) params k)) - ((##core#callunit) (walk-call-unit (first params) k)) + ((##core#callunit) + (let ((unit (first params))) + (if (memq unit called-units) + (walk (make-node '##core#undefined '() '()) k) + (fluid-let ((called-units (cons unit called-units))) + (walk-call-unit unit k))))) ((##core#the ##core#the/result) ;; remove "the" nodes, as they are not used after scrutiny (walk (car subs) k)) @@ -2080,7 +2049,7 @@ ##core#inline_loc_ref ##core#inline_loc_update)) (every atomic? (node-subexpressions n)) ) ) ) ) - (walk node values) ) + (walk node values))) ;;; Perform source-code analysis: diff --git a/csc.scm b/csc.scm index 01cfd04f..35129870 100644 --- a/csc.scm +++ b/csc.scm @@ -673,7 +673,7 @@ EOF (set! compile-options (cons "-DC_EMBEDDED" compile-options)) ] [(-link) (check s rest) - (t-options "-uses" (car rest)) + (t-options "-link" (car rest)) (set! linked-extensions (append linked-extensions (string-split (car rest) ", "))) (set! rest (cdr rest))] diff --git a/eval.scm b/eval.scm index 344660dd..504985d1 100644 --- a/eval.scm +++ b/eval.scm @@ -591,14 +591,13 @@ (compile `(##sys#provide (##core#quote ,(cadr x))) e #f tf cntr #f)] [(##core#require-for-syntax) - (chicken.load#load-extension (cadr x) '() 'require) + (chicken.load#load-extension (cadr x) #f #f) (compile '(##core#undefined) e #f tf cntr #f)] [(##core#require) - (let ((id (cadr x)) - (alternates (cddr x))) - (let-values (((exp _) (##sys#process-require id #f alternates))) - (compile exp e #f tf cntr #f)))] + (let ((lib (cadr x)) + (mod (and (pair? (cddr x)) (caddr x)))) + (compile (##sys#process-require lib mod #f) e #f tf cntr #f))] [(##core#elaborationtimeonly ##core#elaborationtimetoo) ; <- Note this! (##sys#eval/meta (cadr x)) @@ -934,6 +933,10 @@ (define ##sys#load-dynamic-extension default-load-library-extension) +(define (chicken.load#core-unit? id) ; used by batch-driver.scm + (or (memq id core-units) + (assq id core-unit-requirements))) + ; these are actually in unit extras, but that is used by default (define-constant builtin-features @@ -1122,36 +1125,31 @@ (##sys#check-list x) x) ) ) ) -(define load-library/internal - (let ((display display)) - (lambda (uname lib loc) - (let ((libs - (if lib - (##sys#list lib) - (cons (##sys#string-append (##sys#slot uname 1) load-library-extension) - (dynamic-load-libraries)))) - (top - (c-toplevel uname loc))) - (when (load-verbose) - (display "; loading library ") - (display uname) - (display " ...\n") ) - (let loop ((libs libs)) - (cond ((null? libs) - (##sys#error loc "unable to load library" uname _dlerror)) - ((##sys#dload (##sys#make-c-string (##sys#slot libs 0) 'load-library) top)) - (else - (loop (##sys#slot libs 1))))))))) - -(define (##sys#load-library uname #!optional lib loc) - (unless (##sys#provided? uname) - (load-library/internal uname lib loc) - (##core#undefined))) - -(define (load-library uname #!optional lib) - (##sys#check-symbol uname 'load-library) +(define (load-unit unit-name lib loc) + (unless (##sys#provided? unit-name) + (let ((libs + (if lib + (##sys#list lib) + (cons (##sys#string-append (##sys#slot unit-name 1) load-library-extension) + (dynamic-load-libraries)))) + (top + (c-toplevel unit-name loc))) + (when (load-verbose) + (display "; loading library ") + (display unit-name) + (display " ...\n")) + (let loop ((libs libs)) + (cond ((null? libs) + (##sys#error loc "unable to load library" unit-name (or _dlerror "library not found"))) + ((##sys#dload (##sys#make-c-string (##sys#slot libs 0) 'load-library) top) + (##core#undefined)) + (else + (loop (##sys#slot libs 1)))))))) + +(define (load-library unit-name #!optional lib) + (##sys#check-symbol unit-name 'load-library) (unless (not lib) (##sys#check-string lib 'load-library)) - (##sys#load-library uname lib 'load-library)) + (load-unit unit-name lib 'load-library)) (define ##sys#include-forms-from-file (let ((with-input-from-file with-input-from-file) @@ -1208,25 +1206,29 @@ (or (check pa) (loop (##sys#slot paths 1)) ) ) ) ) ) ) )) -(define (load-extension/internal id alternates loc) - (cond ((##sys#provided? id)) - ((any ##sys#provided? alternates)) - ((memq id core-units) - (load-library/internal id #f loc)) - ((find-dynamic-extension id #f) => - (lambda (ext) - (load/internal ext #f #f #f #f id) - (##sys#provide id))) - (else - (##sys#error loc "cannot load extension" id)))) - -(define (chicken.load#load-extension id alternates loc) - (load-extension/internal id alternates loc) - (##core#undefined)) +(define-inline (extension-loaded? lib mod) + (cond ((##sys#provided? lib)) + ((eq? mod #t) + (##sys#provided? (module-requirement lib))) + ((symbol? mod) + (##sys#provided? (module-requirement mod))) + (else #f))) + +(define (load-extension lib mod loc) + (unless (extension-loaded? lib mod) + (cond ((memq lib core-units) + (load-unit lib #f loc)) + ((find-dynamic-extension lib #f) => + (lambda (ext) + (load/internal ext #f #f #f #f lib) + (##sys#provide lib) + (##core#undefined))) + (else + (##sys#error loc "cannot load extension" lib))))) (define (require . ids) (for-each (cut ##sys#check-symbol <> 'require) ids) - (for-each (cut chicken.load#load-extension <> '() 'require) ids)) + (for-each (cut load-extension <> #f 'require) ids)) (define (provide . ids) (for-each (cut ##sys#check-symbol <> 'provide) ids) @@ -1236,40 +1238,32 @@ (for-each (cut ##sys#check-symbol <> 'provided?) ids) (every ##sys#provided? ids)) +;; Export for internal use in the expansion of `##core#require': +(define chicken.load#load-unit load-unit) +(define chicken.load#load-extension load-extension) + ;; Export for internal use in csc, modules and batch-driver: (define chicken.load#find-file find-file) (define chicken.load#find-dynamic-extension find-dynamic-extension) -;; -;; Given a library specification, returns three values: -;; -;; - an expression for loading the library, if required -;; - a requirement type (e.g. 'dynamic) or #f if provided in core -;; -(define (##sys#process-require lib #!optional compiling? (alternates '()) (provided '()) static? mark-static) - (let ((id (library-id lib))) +;; Do the right thing with a `##core#require' form. +(define (##sys#process-require lib mod compile-mode) + (let ((mod (or (eq? lib mod) mod))) (cond - ((assq id core-unit-requirements) => - (lambda (x) (values (cdr x) #f))) - ((memq id builtin-features) - (values '(##core#undefined) #f)) - ((memq id provided) - (values '(##core#undefined) #f)) - ((any (cut memq <> provided) alternates) - (values '(##core#undefined) #f)) - ((memq id core-units) - (if compiling? - (values `(##core#declare (uses ,id)) #f) - (values `(##sys#load-library (##core#quote ,id)) #f))) - ((and compiling? static?) - (mark-static id) - (values `(##core#declare (uses ,id)) 'static)) + ((assq lib core-unit-requirements) => cdr) + ((memq lib builtin-features) '(##core#undefined)) + ((memq lib core-units) + (if compile-mode + `(##core#callunit ,lib) + `(chicken.load#load-unit (##core#quote ,lib) + (##core#quote #f) + (##core#quote #f)))) + ((eq? compile-mode 'static) + `(##core#callunit ,lib)) (else - (values `(chicken.load#load-extension - (##core#quote ,id) - (##core#quote ,alternates) - (##core#quote require)) - 'dynamic))))) + `(chicken.load#load-extension (##core#quote ,lib) + (##core#quote ,mod) + (##core#quote #f)))))) ;;; Find included file: diff --git a/expand.scm b/expand.scm index b228be8c..e96882c5 100644 --- a/expand.scm +++ b/expand.scm @@ -986,7 +986,7 @@ ##sys#current-environment ##sys#macro-environment #f #f 'import)) (if (not lib) '(##core#undefined) - `(##core#require ,lib ,(module-requirement name))))) + `(##core#require ,lib ,name)))) (cdr x))))))) (##sys#extend-macro-environment diff --git a/modules.scm b/modules.scm index beb52eaf..e70c56df 100644 --- a/modules.scm +++ b/modules.scm @@ -33,9 +33,9 @@ (disable-interrupts) (fixnum) (not inline ##sys#alias-global-hook) - (hide check-for-redef find-export find-module/import-library - match-functor-argument merge-se module-indirect-exports - module-rename register-undefined)) + (hide check-for-redef compiled-module-dependencies find-export + find-module/import-library match-functor-argument merge-se + module-indirect-exports module-rename register-undefined)) (import scheme chicken.base @@ -311,7 +311,14 @@ (else (hash-table-set! seen (caar se) #t) (lp (cdr se) (cons (car se) se2)))))))))) -(define (##sys#compiled-module-registration mod) +(define (compiled-module-dependencies mod) + (let ((libs (filter-map ; extract library names + (lambda (x) (nth-value 1 (##sys#decompose-import x o eq? 'module))) + (module-import-forms mod)))) + (map (lambda (lib) `(##core#require ,lib)) + (delete-duplicates libs eq?)))) + +(define (##sys#compiled-module-registration mod compile-mode) (let ((dlist (module-defined-list mod)) (mname (module-name mod)) (ifs (module-import-forms mod)) @@ -319,6 +326,9 @@ (mifs (module-meta-import-forms mod))) `((##sys#with-environment (lambda () + ,@(if (and (eq? compile-mode 'static) (pair? ifs) (pair? sexports)) + (compiled-module-dependencies mod) + '()) ,@(if (and (pair? ifs) (pair? sexports)) `((scheme#eval '(import-syntax ,@(strip-syntax ifs)))) '()) diff --git a/support.scm b/support.scm index f610349d..a7637bb2 100644 --- a/support.scm +++ b/support.scm @@ -1915,7 +1915,6 @@ Available debugging options: x display information about experimental features D when printing nodes, use node-tree output I show inferred type information for unexported globals - M show syntax-/runtime-requirements N show the real-name mapping table P show expressions after specialization S show applications of compiler syntax diff --git a/tests/compiler-tests.scm b/tests/compiler-tests.scm index 9cda75ef..7bdfda99 100644 --- a/tests/compiler-tests.scm +++ b/tests/compiler-tests.scm @@ -469,3 +469,10 @@ (define fold- (lambda xs (reduce xs (car xs)))) (print (fold- 1 2 3)) + +; libraries are only loaded when entry point is called +(let () + (if #f (require-library (chicken repl))) + (assert (not (##sys#symbol-has-toplevel-binding? 'chicken.repl#repl))) + (if #t (require-library (chicken repl))) + (assert (##sys#symbol-has-toplevel-binding? 'chicken.repl#repl))) diff --git a/tests/module-static-eval-compiled.scm b/tests/module-static-eval-compiled.scm index dc6ba9ba..9a7f9a72 100644 --- a/tests/module-static-eval-compiled.scm +++ b/tests/module-static-eval-compiled.scm @@ -1,5 +1,6 @@ ;;;; test eval in statically compiled code +(declare (uses lolevel)) (eval '(import (chicken memory representation))) (assert (eval '(= 1 (block-ref #(1) 0))))Trap