~ chicken-core (master) /support.scm
Trap1;;;; support.scm - Miscellaneous support code for the CHICKEN compiler2;3; Copyright (c) 2008-2022, The CHICKEN Team4; Copyright (c) 2000-2007, Felix L. Winkelmann5; All rights reserved.6;7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following8; conditions are met:9;10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following11; disclaimer.12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following13; disclaimer in the documentation and/or other materials provided with the distribution.14; Neither the name of the author nor the names of its contributors may be used to endorse or promote15; products derived from this software without specific prior written permission.16;17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE25; POSSIBILITY OF SUCH DAMAGE.262728(declare (unit support)29 (not inline ##sys#user-read-hook) ; XXX: Is this needed?30 (uses data-structures extras file internal pathname port))3132(module chicken.compiler.support33 (compiler-cleanup-hook bomb collected-debugging-output debugging34 debugging-chicken with-debugging-output quit-compiling35 emit-syntax-trace-info check-signature build-lambda-list36 valid-c-identifier? read-expressions37 bytes->words words->bytes replace-rest-op-with-list-ops38 check-and-open-input-file close-checked-input-file fold-inner39 constant? collapsable-literal? immediate? basic-literal?40 canonicalize-begin-body string->expr llist-length llist-match?41 expand-profile-lambda reset-profile-info-vector-name!42 profiling-prelude-exps db-get db-get-all db-put! collect! db-get-list43 make-node node? node-class node-class-set! node-parameters node-parameters-set!44 node-subexpressions node-subexpressions-set! varnode qnode45 build-node-graph build-expression-tree fold-boolean inline-lambda-bindings46 tree-copy copy-node! copy-node emit-global-inline-file load-inline-file47 match-node expression-has-side-effects? simple-lambda-node?48 dump-undefined-globals dump-defined-globals dump-global-refs49 make-foreign-callback-stub foreign-callback-stub?50 foreign-callback-stub-id foreign-callback-stub-name51 foreign-callback-stub-qualifiers foreign-callback-stub-return-type52 foreign-callback-stub-argument-types register-foreign-callback-stub!53 foreign-callback-stubs ; should not be exported54 foreign-type-check foreign-type-convert-result55 foreign-type-convert-argument final-foreign-type56 register-foreign-type! lookup-foreign-type clear-foreign-type-table!57 estimate-foreign-result-size estimate-foreign-result-location-size58 finish-foreign-result foreign-type->scrutiny-type scan-used-variables59 scan-free-variables60 make-block-variable-literal block-variable-literal?61 block-variable-literal-name make-random-name62 clear-real-name-table! get-real-name set-real-name!63 real-name real-name2 display-real-name-table64 source-info->string source-info->line source-info->name65 call-info constant-form-eval maybe-constant-fold-call66 dump-nodes read/source-info big-fixnum? small-bignum?67 hide-variable export-variable variable-hidden? variable-visible?68 mark-variable variable-mark intrinsic? predicate? foldable?69 load-identifier-database70 print-version print-usage print-debug-options7172 ;; XXX: These are evil globals that were too hairy to get rid of.73 ;; These values are set! by compiler and batch-driver, and read74 ;; in a lot of other places.75 number-type unsafe)7677(import scheme78 chicken.base79 chicken.bitwise80 chicken.bytevector81 chicken.condition82 chicken.file83 chicken.fixnum84 chicken.foreign85 chicken.format86 chicken.internal87 chicken.io88 chicken.keyword89 chicken.pathname90 chicken.platform91 chicken.plist92 chicken.port93 chicken.pretty-print94 chicken.sort95 chicken.string96 chicken.syntax97 chicken.time)98(import (only (scheme base) open-output-string get-output-string))99100(include "tweaks")101(include "mini-srfi-1.scm")102(include "banner")103104;; Evil globals105(define number-type 'generic)106(define unsafe #f)107108;;; Debugging and error-handling stuff:109110(define (compiler-cleanup-hook) #f)111112(define debugging-chicken '())113114(define (bomb . msg-and-args)115 (if (pair? msg-and-args)116 (apply error (string-append "[internal compiler error] " (car msg-and-args)) (cdr msg-and-args))117 (error "[internal compiler error]") ) )118119(define collected-debugging-output120 (open-output-string))121122(define +logged-debugging-modes+ '(o x S))123124(define (test-debugging-mode mode enabled)125 (if (symbol? mode)126 (memq mode enabled)127 (any (lambda (m) (memq m enabled)) mode)))128129(define (debugging mode msg . args)130 (define (text)131 (with-output-to-string132 (lambda ()133 (display msg)134 (when (pair? args)135 (display ": ")136 (for-each137 (lambda (x) (printf "~s " (force x)))138 args) )139 (newline))))140 (define (dump txt)141 (fprintf collected-debugging-output "~a|~a" mode txt))142 (cond ((test-debugging-mode mode debugging-chicken)143 (let ((txt (text)))144 (display txt)145 (flush-output)146 (when (test-debugging-mode mode +logged-debugging-modes+)147 (dump txt))148 #t))149 (else150 (when (test-debugging-mode mode +logged-debugging-modes+)151 (dump (text)))152 #f)))153154(define (with-debugging-output mode thunk)155 (define (collect text)156 (for-each157 (lambda (ln)158 (fprintf collected-debugging-output "~a|~a~%"159 (if (pair? mode) (car mode) mode)160 ln))161 (string-split text "\n")))162 (cond ((test-debugging-mode mode debugging-chicken)163 (let ((txt (with-output-to-string thunk)))164 (display txt)165 (flush-output)166 (when (test-debugging-mode mode +logged-debugging-modes+)167 (collect txt))))168 ((test-debugging-mode mode +logged-debugging-modes+)169 (collect (with-output-to-string thunk)))))170171(define (quit-compiling msg . args)172 (let ([out (current-error-port)])173 (apply fprintf out (string-append "\nError: " msg) args)174 (newline out)175 (exit 1) ) )176177(set! ##sys#syntax-error-hook178 (lambda (msg . args)179 (let ((out (current-error-port))180 (loc (and (symbol? msg)181 (let ((loc msg))182 (set! msg (car args))183 (set! args (cdr args))184 loc))))185 (if loc186 (fprintf out "\nSyntax error (~a): ~a~%~%" loc msg)187 (fprintf out "\nSyntax error: ~a~%~%" msg) )188 (for-each (cut fprintf out "\t~s~%" <>) args)189 (print-call-chain out 0 ##sys#current-thread "\n\tExpansion history:\n")190 (exit 70) ) ) )191192(define (emit-syntax-trace-info info cntr)193 (define (thread-id t) (##sys#slot t 14))194 (##core#inline "C_emit_syntax_trace_info" info cntr195 (thread-id ##sys#current-thread)))196197(define (map-llist proc llist)198 (let loop ([llist llist])199 (cond [(null? llist) '()]200 [(symbol? llist) (proc llist)]201 [else (cons (proc (car llist)) (loop (cdr llist)))] ) ) )202203(define (check-signature var args llist)204 (let loop ((as args) (ll llist))205 (cond ((null? ll) (null? as))206 ((symbol? ll))207 ((null? as) #f)208 (else (loop (cdr as) (cdr ll))) ) ) )209210211;;; Generic utility routines:212213(define (build-lambda-list vars argc rest)214 (let loop ((vars vars) (n argc))215 (cond ((or (zero? n) (null? vars)) (or rest '()))216 (else (cons (car vars) (loop (cdr vars) (sub1 n)))) ) ) )217218;; XXX: This too, but it's used only in core.scm, WTF?219(define (valid-c-identifier? name)220 (let ([str (string->list (->string name))])221 (and (pair? str)222 (let ([c0 (car str)])223 (and (or (char-alphabetic? c0) (char=? #\_ c0))224 (every (lambda (c) (or (char-alphabetic? c) (char-numeric? c) (char=? #\_ c)))225 (cdr str)))))))226227(define (struct/union-wrapper-type-name x)228 (cond ((list? (cadr x)) (string->symbol (->string (caadr x))))229 (else (string->symbol (string-append (symbol->string (car x)) " "230 (->string (cadr x)))))))231232;; TODO: Move these to (chicken memory)?233(define bytes->words (foreign-lambda int "C_bytestowords" int))234(define words->bytes (foreign-lambda int "C_wordstobytes" int))235236;; Used only in batch-driver; move it there?237(define (check-and-open-input-file fname . line)238 (cond ((string=? fname "-") (current-input-port))239 ((file-exists? fname) (open-input-file fname))240 ((or (null? line) (not (car line)))241 (quit-compiling "Can not open file ~s" fname))242 (else (quit-compiling "(~a) can not open file ~s" (car line) fname)) ) )243244(define (close-checked-input-file port fname)245 (unless (string=? fname "-") (close-input-port port)) )246247(define (fold-inner proc lst)248 (if (null? (cdr lst))249 lst250 (let fold ((xs (reverse lst)))251 (apply252 proc253 (if (null? (cddr xs))254 (list (cadr xs) (car xs))255 (list (fold (cdr xs)) (car xs)) ) ) ) ) )256257(define (follow-without-loop seed proc abort)258 (let loop ([x seed] [done '()])259 (if (member x done)260 (abort)261 (proc x (lambda (x2) (loop x2 (cons x done)))) ) ) )262263(define (sort-symbols lst)264 (sort lst (lambda (s1 s2) (string<? (symbol->string s1) (symbol->string s2)))))265266(define (read-expressions #!optional (port (current-input-port)))267 (do ((x (read port) (read port))268 (i 0 (add1 i))269 (xs '() (cons x xs)))270 ((eof-object? x) (reverse xs))))271272273;;; Predicates on expressions and literals:274275(define (constant? x)276 (or (number? x)277 (char? x)278 (string? x)279 (boolean? x)280 (eof-object? x)281 (bytevector? x)282 (bwp-object? x)283 (vector? x)284 (##sys#srfi-4-vector? x)285 (and (pair? x) (eq? 'quote (car x))) ) )286287(define (collapsable-literal? x)288 (or (boolean? x)289 (char? x)290 (eof-object? x)291 (bwp-object? x)292 (number? x)293 (symbol? x) ) )294295(define (immediate? x)296 (or (and (fixnum? x) (not (big-fixnum? x))) ; 64-bit fixnums would result in platform-dependent .c files297 (eq? (##core#undefined) x)298 (null? x)299 (eof-object? x)300 (bwp-object? x)301 (char? x)302 (boolean? x) ) )303304(define (basic-literal? x)305 (or (null? x)306 (symbol? x)307 (constant? x)308 (and (vector? x) (every basic-literal? (vector->list x)))309 (and (pair? x)310 (basic-literal? (car x))311 (basic-literal? (cdr x)) ) ) )312313314;;; Expression manipulation:315316(define (canonicalize-begin-body body)317 (let loop ((xs body))318 (cond ((null? xs) '(##core#undefined))319 ((null? (cdr xs)) (car xs))320 ((let ([h (car xs)])321 (or (equal? h '(##core#undefined))322 (constant? h)323 (equal? h '(##sys#void)) ) )324 (loop (cdr xs)) )325 (else `(let ((,(gensym 't) ,(car xs)))326 ,(loop (cdr xs))) ) ) ) )327328;; Only used in batch-driver: move it there?329(define string->expr330 (let ([exn? (condition-predicate 'exn)]331 [exn-msg (condition-property-accessor 'exn 'message)] )332 (lambda (str)333 (handle-exceptions ex334 (quit-compiling "cannot parse expression: ~s [~a]~%"335 str336 (if (exn? ex)337 (exn-msg ex)338 (->string ex) ) )339 (let ((xs (with-input-from-string340 str341 (lambda ()342 (let loop ((lst '()))343 (let ((x (read)))344 (if (eof-object? x)345 (reverse lst)346 (loop (cons x lst)))))))))347 (cond [(null? xs) '(##core#undefined)]348 [(null? (cdr xs)) (car xs)]349 [else `(begin ,@xs)] ) ) ) ) ) )350351;; Only used in optimizer; move it there? But it's a C function call, so352;; it may be better in c-platform353(define (llist-length llist)354 (##core#inline "C_u_i_length" llist)) ; stops at non-pair node355356(define (llist-match? llist args) ; assumes #!optional/#!rest/#!key have been expanded357 (let loop ((llist llist) (args args))358 (cond ((null? llist) (null? args))359 ((symbol? llist))360 ((null? args) (atom? llist))361 (else (loop (cdr llist) (cdr args))))))362363364;;; Profiling instrumentation:365(define profile-info-vector-name #f)366(define (reset-profile-info-vector-name!)367 (set! profile-info-vector-name (make-random-name 'profile-info)))368369(define profile-lambda-list '())370(define profile-lambda-index 0)371372(define (expand-profile-lambda name llist body)373 (let ([index profile-lambda-index]374 [args (gensym)] )375 (set! profile-lambda-list (alist-cons index name profile-lambda-list))376 (set! profile-lambda-index (add1 index))377 `(##core#lambda ,args378 (##sys#dynamic-wind379 (##core#lambda () (##sys#profile-entry ',index ,profile-info-vector-name))380 (##core#lambda () (##sys#apply (##core#lambda ,llist ,body) ,args))381 (##core#lambda () (##sys#profile-exit ',index ,profile-info-vector-name)) ) ) ) )382383;; Get expressions which initialize and populate the profiling vector384(define (profiling-prelude-exps profile-name)385 `((set! ,profile-info-vector-name386 (##sys#register-profile-info387 ',(length profile-lambda-list)388 ',profile-name))389 ,@(map (lambda (pl)390 `(##sys#set-profile-info-vector!391 ,profile-info-vector-name392 ',(car pl)393 ',(cdr pl) ) )394 profile-lambda-list)))395396;;; Database operations:397398(define (db-get db key prop)399 (let ((plist (hash-table-ref db key)))400 (and plist401 (let ([a (assq prop plist)])402 (and a (##sys#slot a 1)) ) ) ) )403404(define (db-get-all db key . props)405 (let ((plist (hash-table-ref db key)))406 (if plist407 (filter-map (lambda (prop) (assq prop plist)) props)408 '() ) ) )409410(define (db-put! db key prop val)411 (let ((plist (hash-table-ref db key)))412 (if plist413 (let ([a (assq prop plist)])414 (cond [a (##sys#setslot a 1 val)]415 [val (##sys#setslot plist 1 (alist-cons prop val (##sys#slot plist 1)))] ) )416 (when val (hash-table-set! db key (list (cons prop val)))))))417418(define (collect! db key prop val)419 (let ((plist (hash-table-ref db key)))420 (if plist421 (let ([a (assq prop plist)])422 (cond [a (##sys#setslot a 1 (cons val (##sys#slot a 1)))]423 [else (##sys#setslot plist 1 (alist-cons prop (list val) (##sys#slot plist 1)))] ) )424 (hash-table-set! db key (list (list prop val))))))425426(define (db-get-list db key prop) ; returns '() if not set427 (let ((x (db-get db key prop)))428 (or x '())))429430431;;; Node creation and -manipulation:432433;; Note: much of this stuff will be overridden by the inline-definitions in "tweaks.scm".434435(define-record node436 class ; symbol437 parameters ; (value...)438 subexpressions ) ; (node...)439440(set-record-printer! node441 (lambda (n out)442 (fprintf out "#<node ~a ~a>" (node-class n) (node-parameters n))))443444(define (make-node c p s)445 (##sys#make-structure 'chicken.compiler.support#node c p s))446447(define (varnode var) (make-node '##core#variable (list var) '()))448(define (qnode const) (make-node 'quote (list const) '()))449450(define (build-node-graph exp)451 (let ((count 0))452 (define (walk x)453 (cond ((symbol? x) (varnode x))454 ((node? x) x)455 ((not (pair? x)) (bomb "bad expression" x))456 ((symbol? (car x))457 (case (car x)458 ((if ##core#undefined) (make-node (car x) '() (map walk (cdr x))))459 ((quote)460 (let ((c (cadr x)))461 (qnode (if (and (number? c)462 (eq? 'fixnum number-type)463 (not (integer? c)) )464 (begin465 (warning466 "literal is out of range - will be truncated to integer" c)467 (inexact->exact (truncate c)) )468 c) ) ) )469 ((let)470 (let ([bs (cadr x)]471 [body (caddr x)] )472 (if (null? bs)473 (walk body)474 (make-node475 'let (unzip1 bs)476 (append (map (lambda (b) (walk (cadr b))) (cadr x))477 (list (walk body)) ) ) ) ) )478 ((lambda ##core#lambda)479 (make-node 'lambda (list (cadr x)) (list (walk (caddr x)))))480 ((##core#the)481 (make-node '##core#the482 (list (second x) (third x))483 (list (walk (fourth x)))))484 ((##core#typecase)485 ;; clause-head is already stripped486 (let loop ((cls (cdddr x)) (types '()) (exps (list (walk (caddr x)))))487 (cond ((null? cls) ; no "else" clause given488 (make-node489 '##core#typecase490 (cons (cadr x) (reverse types))491 (reverse492 (cons (make-node '##core#undefined '() '()) exps))))493 ((eq? 'else (caar cls))494 (make-node495 '##core#typecase496 (cons (cadr x) (reverse (cons '* types)))497 (reverse (cons (walk (cadar cls)) exps))))498 (else (loop (cdr cls)499 (cons (caar cls) types)500 (cons (walk (cadar cls)) exps))))))501 ((##core#primitive)502 (let ((arg (cadr x)))503 (make-node504 (car x)505 (list (if (and (pair? arg) (eq? 'quote (car arg))) (cadr arg) arg))506 (map walk (cddr x)) ) ) )507 ((##core#inline ##core#provide ##core#callunit)508 (make-node (car x) (list (cadr x)) (map walk (cddr x))) )509 ((##core#debug-event) ; 2nd argument is provided by canonicalization phase510 (make-node (car x) (cdr x) '()))511 ((##core#proc)512 (make-node '##core#proc (list (cadr x) #t) '()) )513 ((set! ##core#set!)514 (make-node515 'set! (list (cadr x))516 (map walk (cddr x))))517 ((##core#foreign-callback-wrapper)518 (let ([name (cadr (second x))])519 (make-node520 '##core#foreign-callback-wrapper521 (list name (cadr (third x)) (cadr (fourth x)) (cadr (fifth x)))522 (list (walk (list-ref x 5))) ) ) )523 ((##core#inline_allocate ##core#inline_ref ##core#inline_update524 ##core#inline_loc_ref ##core#inline_loc_update)525 (make-node (first x) (second x) (map walk (cddr x))) )526 ((##core#app)527 (make-node '##core#call (list #t) (map walk (cdr x))) )528 (else529 (receive (name ln) (##sys#get-line-2 x)530 (make-node531 '##core#call532 (list (cond [(variable-mark name '##compiler#always-bound-to-procedure)533 (set! count (add1 count))534 #t]535 [else #f] )536 (if ln537 (let ([rn (real-name name)])538 (list ln539 (or rn (##sys#symbol->string name))) )540 (##sys#symbol->string name) ) )541 (map walk x) ) ) ) ) )542 (else (make-node '##core#call (list #f) (map walk x))) ) )543 (let ([exp2 (walk exp)])544 (when (positive? count)545 (debugging 'o "eliminated procedure checks" count)) ;XXX perhaps throw this out546 exp2) ) )547548(define (build-expression-tree node)549 (let walk ((n node))550 (let ((subs (node-subexpressions n))551 (params (node-parameters n))552 (class (node-class n)) )553 (case class554 ((if ##core#box ##core#cond) (cons class (map walk subs)))555 ((##core#closure)556 `(##core#closure ,params ,@(map walk subs)) )557 ((##core#variable) (car params))558 ((quote)559 (let ((c (car params)))560 (if (or (boolean? c) (string? c) (number? c) (char? c))561 c562 `(quote ,(car params)))))563 ((let)564 `(let ,(map list params (map walk (butlast subs)))565 ,(walk (last subs)) ) )566 ((##core#lambda)567 (list (if (second params)568 'lambda569 '##core#lambda)570 (third params)571 (walk (car subs)) ) )572 ((##core#the)573 `(the ,(first params) ,(walk (first subs))))574 ((##core#the/result)575 (walk (first subs)))576 ((##core#typecase)577 `(compiler-typecase578 ,(walk (first subs))579 ,@(let loop ((types (cdr params)) (bodies (cdr subs)))580 (if (null? types)581 (if (null? bodies)582 '()583 `((else ,(walk (car bodies)))))584 (cons (list (car types) (walk (car bodies)))585 (loop (cdr types) (cdr bodies)))))))586 ((##core#call)587 (map walk subs))588 ((##core#callunit) (cons* '##core#callunit (car params) (map walk subs)))589 ((##core#undefined) (list class))590 ((##core#bind)591 (let loop ((n (car params)) (vals subs) (bindings '()))592 (if (zero? n)593 `(##core#bind ,(reverse bindings) ,(walk (car vals)))594 (loop (- n 1) (cdr vals) (cons (walk (car vals)) bindings)) ) ) )595 ((##core#unbox ##core#ref ##core#update ##core#update_i)596 (cons* class (walk (car subs)) params (map walk (cdr subs))) )597 ((##core#inline_allocate)598 (cons* class params (map walk subs)))599 (else (cons class (append params (map walk subs)))) ) ) ) )600601(define (fold-boolean proc lst)602 (let fold ([vars lst])603 (if (null? (cddr vars))604 (apply proc vars)605 (make-node606 '##core#inline '("C_and")607 (list (proc (first vars) (second vars))608 (fold (cdr vars)) ) ) ) ) )609610;; Move to optimizer.scm?611(define (inline-lambda-bindings llist args body copy? db cfk)612 (##sys#decompose-lambda-list613 llist614 (lambda (vars argc rest)615 (receive (largs rargs) (split-at args argc)616 (let* ((rlist (if copy? (map gensym vars) vars))617 (body (if copy?618 (copy-node-tree-and-rename body vars rlist db cfk)619 body) )620 (rarg-aliases (map (lambda (r) (gensym 'rarg)) rargs)) )621 (replace-rest-ops-in-known-call! db body rest (last rlist) rarg-aliases)622623 ;; Make sure rest ops aren't replaced after inlining (#1658)624 ;; argvector does not belong to the same procedure anymore.625 (when rest626 (for-each (lambda (v)627 (db-put! db v 'rest-cdr #f)628 (db-put! db v 'rest-null? #f) )629 (db-get-list db rest 'derived-rest-vars) )630 (db-put! db rest 'rest-cdr #f)631 (db-put! db rest 'derived-rest-vars '()) )632633 (let loop ((vars (take rlist argc))634 (vals largs))635 (if (null? vars)636 (if rest637 ;; NOTE: If contraction happens before rest-op638 ;; detection, we might needlessly build a list.639 (let loop2 ((rarg-values rargs)640 (rarg-aliases rarg-aliases))641 (if (null? rarg-aliases)642 (if (null? (db-get-list db rest 'references))643 body644 (make-node645 'let (list (last rlist))646 (list (if (null? rargs)647 (qnode '())648 (make-node649 '##core#inline_allocate650 (list "C_a_i_list" (* 3 (length rargs)))651 rargs) )652 body) ))653 (make-node 'let (list (car rarg-aliases))654 (list (car rarg-values)655 (loop2 (cdr rarg-values) (cdr rarg-aliases))))))656 body)657 (make-node 'let (list (car vars))658 (list (car vals)659 (loop (cdr vars) (cdr vals)))))))))))660661;; Copy along with the above662(define (copy-node-tree-and-rename node vars aliases db cfk)663 (let ((rlist (map cons vars aliases)))664 (define (rename v rl) (alist-ref v rl eq? v))665 (define (walk n rl)666 (let ((subs (node-subexpressions n))667 (params (node-parameters n))668 (class (node-class n)) )669 (case class670 ((quote)671 (make-node class params '()))672 ((##core#variable)673 (let ((var (first params)))674 (when (db-get db var 'contractable)675 (cfk var))676 (varnode (rename var rl))) )677 ((set!)678 (make-node679 'set! (list (rename (first params) rl))680 (list (walk (first subs) rl)) ) )681 ((let)682 (let* ((v (first params))683 (val1 (walk (first subs) rl))684 (a (gensym v))685 (rl2 (alist-cons v a rl)) )686 (db-put! db a 'inline-transient #t)687 (make-node688 'let (list a)689 (list val1 (walk (second subs) rl2)))) )690 ((##core#lambda)691 (##sys#decompose-lambda-list692 (third params)693 (lambda (vars argc rest)694 (let* ((as (map (lambda (v)695 (let ((a (gensym v)))696 (db-put! db v 'inline-transient #t)697 a))698 vars) )699 (rl2 (append (map cons vars as) rl)) )700 (make-node701 '##core#lambda702 (list (gensym 'f) (second params) ; new function-id703 (build-lambda-list as argc (and rest (rename rest rl2)))704 (fourth params) )705 (map (cut walk <> rl2) subs) ) ) ) ) )706 (else (make-node class (tree-copy params)707 (map (cut walk <> rl) subs))) ) ) )708 (walk node rlist) ) )709710;; Replace rest-{car,cdr,null?} with equivalent code which accesses711;; the rest argument directly.712(define (replace-rest-ops-in-known-call! db node rest-var rest-alias rest-args)713 (define (walk n)714 (let ((subs (node-subexpressions n))715 (params (node-parameters n))716 (class (node-class n)) )717 (case class718 ((##core#rest-null?)719 (if (eq? rest-var (first params))720 (copy-node! (qnode (<= (length rest-args) (second params))) n)721 n))722 ((##core#rest-car)723 (if (eq? rest-var (first params))724 (let ((depth (second params))725 (len (length rest-args)))726 (if (> len depth)727 (copy-node! (varnode (list-ref rest-args depth)) n)728 (copy-node! (make-node '##core#inline729 (list "C_rest_arg_out_of_bounds_error_value")730 (list (qnode len) (qnode depth) (qnode 0)))731 n)))732 n))733 ((##core#rest-cdr)734 (cond ((eq? rest-var (first params))735 (collect! db rest-var 'references n) ; Restore this reference736 (let lp ((i (add1 (second params)))737 (new-node (varnode rest-alias)))738 (if (zero? i)739 (copy-node! new-node n)740 (lp (sub1 i)741 (make-node '##core#inline (list "C_i_cdr") (list new-node))))))742 (else n)))743 (else (for-each walk subs)) ) ) )744745 (walk node) )746747(define (replace-rest-op-with-list-ops class rest-var-node params)748 (case class749 ((##core#rest-car)750 (make-node '##core#inline751 (list "C_i_list_ref")752 (list rest-var-node (qnode (second params)))))753 ((##core#rest-cdr)754 (let lp ((cdr-calls (add1 (second params)))755 (var rest-var-node))756 (if (zero? cdr-calls)757 var758 (lp (sub1 cdr-calls)759 (make-node '##core#inline (list "C_i_cdr") (list var))))))760 ((##core#rest-null?)761 (make-node '##core#inline762 (list "C_i_greater_or_equalp")763 (list (qnode (second params))764 (make-node '##core#inline (list "C_i_length") (list rest-var-node)))))765 ((##core#rest-length)766 (make-node '##core#inline767 (list "C_i_length")768 (list rest-var-node (qnode (second params)))))769 (else (bomb "Unknown rest op node class while undoing rest op for explicitly consed rest arg. This shouldn't happen!" class))))770771;; Maybe move to scrutinizer. It's generic enough to keep it here though772(define (tree-copy t)773 (let rec ([t t])774 (if (pair? t)775 (cons (rec (car t)) (rec (cdr t)))776 t) ) )777778(define (copy-node n)779 (make-node (node-class n)780 (node-parameters n)781 (node-subexpressions n)))782783(define (copy-node! from to)784 (node-class-set! to (node-class from))785 (node-parameters-set! to (node-parameters from))786 (node-subexpressions-set! to (node-subexpressions from))787 to)788789(define (node->sexpr n)790 (let walk ((n n))791 `(,(node-class n)792 ,(node-parameters n)793 ,@(map walk (node-subexpressions n)))))794795(define (sexpr->node x)796 (let walk ((x x))797 (make-node (car x) (cadr x) (map walk (cddr x)))))798799;; Only used in batch-driver.scm800(define (emit-global-inline-file source-file inline-file db801 block-compilation inline-limit802 foreign-stubs)803 (define (uses-foreign-stubs? node)804 (let walk ((n node))805 (case (node-class n)806 ((##core#inline)807 (memq (car (node-parameters n)) foreign-stubs))808 (else809 (any walk (node-subexpressions n))))))810 (let ((lst '())811 (out '()))812 (hash-table-for-each813 (lambda (sym plist)814 (when (variable-visible? sym block-compilation)815 (and-let* ((val (assq 'local-value plist))816 ((not (node? (variable-mark sym '##compiler#inline-global))))817 ((let ((val (assq 'value plist)))818 (or (not val)819 (not (eq? 'unknown (cdr val))))))820 ((assq 'inlinable plist))821 (lparams (node-parameters (cdr val)))822 ((not (db-get db sym 'hidden-refs)))823 ((case (variable-mark sym '##compiler#inline)824 ((yes) #t)825 ((no) #f)826 (else827 (< (fourth lparams) inline-limit))))828 ;; See #1440829 ((not (uses-foreign-stubs? (cdr val)))))830 (set! lst (cons sym lst))831 (set! out (cons (list sym (node->sexpr (cdr val))) out)))))832 db)833 (with-output-to-file inline-file834 (lambda ()835 (print "; GENERATED BY CHICKEN " (chicken-version) " FROM "836 source-file "\n")837 (for-each838 (lambda (x)839 (pp x)840 (newline))841 (reverse out))842 (print "; END OF FILE")))843 (when (and (pair? lst)844 (debugging 'i "the following procedures can be globally inlined:"))845 (for-each (cut print " " <>) (sort-symbols lst)))))846847;; Used only in batch-driver.scm848(define (load-inline-file fname)849 (with-input-from-file fname850 (lambda ()851 (let loop ()852 (let ((x (read)))853 (unless (eof-object? x)854 (mark-variable855 (car x)856 '##compiler#inline-global857 (sexpr->node (cadr x)))858 (loop)))))))859860861;;; Match node-structure with pattern:862863(define (match-node node pat vars) ; Only used in optimizer.scm864 (let ((env '()))865866 (define (resolve v x)867 (cond ((assq v env) => (lambda (a) (equal? x (cdr a))))868 ((memq v vars)869 (set! env (alist-cons v x env))870 #t)871 (else (eq? v x)) ) )872873 (define (match1 x p)874 (cond ((not (pair? p)) (resolve p x))875 ((not (pair? x)) #f)876 ((match1 (car x) (car p)) (match1 (cdr x) (cdr p)))877 (else #f) ) )878879 (define (matchn n p)880 (if (not (pair? p))881 (resolve p n)882 (and (eq? (node-class n) (first p))883 (match1 (node-parameters n) (second p))884 (let loop ((ns (node-subexpressions n))885 (ps (cddr p)) )886 (cond ((null? ps) (null? ns))887 ((not (pair? ps)) (resolve ps ns))888 ((null? ns) #f)889 (else (and (matchn (car ns) (car ps))890 (loop (cdr ns) (cdr ps)) ) ) ) ) ) ) )891892 (let ((r (matchn node pat)))893 (and r894 (begin895 (debugging 'a "matched" (node-class node) (node-parameters node) pat)896 env) ) ) ) )897898899;;; Test nodes for certain properties:900901(define (expression-has-side-effects? node db)902 (let walk ([n node])903 (let ([subs (node-subexpressions n)])904 (case (node-class n)905 [(##core#variable quote ##core#undefined ##core#proc) #f]906 [(##core#lambda)907 (let ([id (first (node-parameters n))])908 (find (lambda (fs)909 (eq? id (foreign-callback-stub-id fs)))910 foreign-callback-stubs) ) ]911 [(if let) (any walk subs)]912 [else #t] ) ) ) )913914(define (simple-lambda-node? node) ; Used only in compiler.scm915 (let* ([params (node-parameters node)]916 [llist (third params)]917 [k (and (pair? llist) (first llist))] ) ; leaf-routine has no continuation argument918 (and k919 (second params)920 (let rec ([n node])921 (case (node-class n)922 [(##core#call)923 (let* ([subs (node-subexpressions n)]924 [f (first subs)] )925 (and (eq? '##core#variable (node-class f))926 (eq? k (first (node-parameters f)))927 (every rec (cdr subs)) ) ) ]928 [(##core#callunit) #f]929 [else (every rec (node-subexpressions n))] ) ) ) ) )930931932;;; Some safety checks and database dumping:933934(define (dump-undefined-globals db) ; Used only in batch-driver.scm935 (hash-table-for-each936 (lambda (sym plist)937 (when (and (not (keyword? sym))938 (assq 'global plist)939 (not (assq 'assigned plist)) )940 (write sym)941 (newline) ) )942 db) )943944(define (dump-defined-globals db) ; Used only in batch-driver.scm945 (hash-table-for-each946 (lambda (sym plist)947 (when (and (not (keyword? sym))948 (assq 'global plist)949 (assq 'assigned plist))950 (write sym)951 (newline) ) )952 db) )953954(define (dump-global-refs db) ; Used only in batch-driver.scm955 (hash-table-for-each956 (lambda (sym plist)957 (when (and (not (keyword? sym)) (assq 'global plist))958 (let ((a (assq 'references plist)))959 (write (list sym (if a (length (cdr a)) 0)))960 (newline) ) ) )961 db) )962963964;;; change hook function to hide non-exported module bindings965966(set! ##sys#toplevel-definition-hook967 (lambda (sym renamed exported?)968 (cond ((namespaced-symbol? sym)969 (unhide-variable sym))970 ((not exported?)971 (debugging 'o "hiding unexported module binding" renamed)972 (hide-variable renamed)))))973974975;;; Foreign callback stub and type tables:976977(define foreign-callback-stubs '())978979(define-record foreign-callback-stub980 id ; symbol981 name ; string982 qualifiers ; string983 return-type ; type-specifier984 argument-types ) ; (type-specifier ...)985986(define (register-foreign-callback-stub! id params)987 (set! foreign-callback-stubs988 (cons (apply make-foreign-callback-stub id params) foreign-callback-stubs) )989 ;; mark to avoid leaf-routine optimization990 (mark-variable id '##compiler#callback-lambda))991992(define-constant foreign-type-table-size 301)993994(define foreign-type-table #f)995996(define (clear-foreign-type-table!)997 (if foreign-type-table998 (vector-fill! foreign-type-table '())999 (set! foreign-type-table (make-vector foreign-type-table-size '())) ))10001001;; Register a foreign type under the given alias. type is the foreign1002;; type's name, arg and ret are the *names* of conversion procedures1003;; when this type is used as argument or return value, respectively.1004;; The latter two must either both be supplied, or neither.1005;; TODO: Maybe create a separate record type for foreign types?1006(define (register-foreign-type! alias type #!optional arg ret)1007 (hash-table-set! foreign-type-table alias1008 (vector type (and ret arg) (and arg ret))))10091010;; Returns either #f (if t does not exist) or a vector with the type,1011;; the *name* of the argument conversion procedure and the *name* of1012;; the return value conversion procedure. If no conversion procedures1013;; have been supplied, the corresponding slots will be #f.1014(define (lookup-foreign-type t)1015 (hash-table-ref foreign-type-table t))10161017;;; Create foreign type checking expression:10181019(define foreign-type-check ; Used only in compiler.scm1020 (let ((tmap '((nonnull-u8vector . u8vector) (nonnull-u16vector . u16vector)1021 (nonnull-s8vector . s8vector) (nonnull-s16vector . s16vector)1022 (nonnull-u32vector . u32vector) (nonnull-s32vector . s32vector)1023 (nonnull-u64vector . u64vector) (nonnull-s64vector . s64vector)1024 (nonnull-f32vector . f32vector) (nonnull-f64vector . f64vector)))1025 (ftmap '((integer . "int") (unsigned-integer . "unsigned int")1026 (integer32 . "C_s32") (unsigned-integer32 . "C_u32")1027 (integer64 . "C_s64") (unsigned-integer64 . "C_u64")1028 (short . "short") (unsigned-short . "unsigned short")1029 (long . "long") (unsigned-long . "unsigned long")1030 (ssize_t . "ssize_t") (size_t . "size_t"))))1031 (lambda (param type)1032 (follow-without-loop1033 type1034 (lambda (t next)1035 (let repeat ((t t))1036 (case t1037 ((char unsigned-char) (if unsafe param `(##sys#foreign-char-argument ,param)))1038 ;; TODO: Should "[unsigned-]byte" be range checked?1039 ((int unsigned-int byte unsigned-byte int32 unsigned-int32)1040 (if unsafe param `(##sys#foreign-fixnum-argument ,param)))1041 ((float double number)1042 (if unsafe param `(##sys#foreign-flonum-argument ,param)))1043 ((u8vector bytevector scheme-pointer1044 blob) ; DEPRECATED1045 (let ((tmp (gensym)))1046 `(##core#let ((,tmp ,param))1047 (##core#if ,tmp1048 ,(if unsafe1049 tmp1050 `(##sys#foreign-block-argument ,tmp) )1051 (##core#quote #f)) ) ) )1052 ((nonnull-scheme-pointer nonnull-bytevector nonnull-u8vector1053 nonnull-blob) ; DEPRECATED1054 (if unsafe1055 param1056 `(##sys#foreign-block-argument ,param) ) )1057 ((pointer-vector)1058 (let ((tmp (gensym)))1059 `(##core#let ((,tmp ,param))1060 (##core#if ,tmp1061 ,(if unsafe1062 tmp1063 `(##sys#foreign-struct-wrapper-argument (##core#quote pointer-vector) ,tmp) )1064 (##core#quote #f)) ) ) )1065 ((nonnull-pointer-vector)1066 (if unsafe1067 param1068 `(##sys#foreign-struct-wrapper-argument (##core#quote pointer-vector) ,param) ) )1069 ((u16vector s8vector s16vector u32vector s32vector1070 u64vector s64vector f32vector f64vector)1071 (let ((tmp (gensym)))1072 `(##core#let ((,tmp ,param))1073 (##core#if ,tmp1074 ,(if unsafe1075 tmp1076 `(##sys#foreign-struct-wrapper-argument (##core#quote ,t) ,tmp) )1077 (##core#quote #f)) ) ) )1078 ((nonnull-u16vector1079 nonnull-s8vector nonnull-s16vector1080 nonnull-u32vector nonnull-s32vector1081 nonnull-u64vector nonnull-s64vector1082 nonnull-f32vector nonnull-f64vector)1083 (if unsafe1084 param1085 `(##sys#foreign-struct-wrapper-argument1086 (##core#quote ,(##sys#slot (assq t tmap) 1))1087 ,param) ) )1088 ((complex cplxnum)1089 ;; always converts to inexact1090 `(##sys#foreign-cplxnum-argument ,param))1091 ((integer32 integer64 integer short long ssize_t)1092 (let* ((foreign-type (##sys#slot (assq t ftmap) 1))1093 (size-expr (sprintf "sizeof(~A) * CHAR_BIT" foreign-type)))1094 (if unsafe1095 param1096 `(##sys#foreign-ranged-integer-argument1097 ,param (foreign-value ,size-expr int)))))1098 ((unsigned-short unsigned-long unsigned-integer size_t1099 unsigned-integer32 unsigned-integer64)1100 (let* ((foreign-type (##sys#slot (assq t ftmap) 1))1101 (size-expr (sprintf "sizeof(~A) * CHAR_BIT" foreign-type)))1102 (if unsafe1103 param1104 `(##sys#foreign-unsigned-ranged-integer-argument1105 ,param (foreign-value ,size-expr int)))))1106 ((c-pointer c-string-list c-string-list*)1107 (let ((tmp (gensym)))1108 `(##core#let ((,tmp ,param))1109 (##core#if ,tmp1110 (##sys#foreign-pointer-argument ,tmp)1111 (##core#quote #f)) ) ) )1112 ((nonnull-c-pointer)1113 `(##sys#foreign-pointer-argument ,param) )1114 ((c-string c-string* unsigned-c-string unsigned-c-string*)1115 (let ((tmp (gensym)))1116 `(##core#let ((,tmp ,param))1117 (##core#if ,tmp1118 ,(if unsafe1119 `(##sys#slot ,tmp 0)1120 `(##sys#make-c-string (##sys#foreign-string-argument ,tmp)) )1121 (##core#quote #f)) ) ) )1122 ((nonnull-c-string nonnull-c-string* nonnull-unsigned-c-string*)1123 (if unsafe1124 `(##sys#slot ,param 0)1125 `(##sys#make-c-string (##sys#foreign-string-argument ,param)) ) )1126 ((symbol)1127 (if unsafe1128 `(##sys#slot ,param 1)1129 `(##sys#slot (##sys#foreign-symbol-argument ,param) 1)) )1130 (else1131 (cond ((and (symbol? t) (lookup-foreign-type t))1132 => (lambda (t) (next (vector-ref t 0)) ) )1133 ((pair? t)1134 (case (car t)1135 ((ref pointer function c-pointer)1136 (let ((tmp (gensym)))1137 `(##core#let ((,tmp ,param))1138 (##core#if ,tmp1139 (##sys#foreign-pointer-argument ,tmp)1140 (##core#quote #f)) ) ) )1141 ((instance instance-ref)1142 (let ((tmp (gensym)))1143 `(##core#let ((,tmp ,param))1144 (##core#if ,tmp1145 (slot-ref ,param (##core#quote this))1146 (##core#quote #f)) ) ) )1147 ((struct union)1148 `(##sys#slot (##sys#foreign-struct-wrapper-argument (##core#quote ,(struct/union-wrapper-type-name t))1149 ,param) 1))1150 ((scheme-pointer)1151 (let ((tmp (gensym)))1152 `(##core#let ((,tmp ,param))1153 (##core#if ,tmp1154 ,(if unsafe1155 tmp1156 `(##sys#foreign-block-argument ,tmp) )1157 (##core#quote #f)) ) ) )1158 ((nonnull-scheme-pointer)1159 (if unsafe1160 param1161 `(##sys#foreign-block-argument ,param) ) )1162 ((nonnull-instance)1163 `(slot-ref ,param (##core#quote this)) )1164 ((const) (repeat (cadr t)))1165 ((enum)1166 (if unsafe1167 param1168 `(##sys#foreign-ranged-integer-argument1169 ;; enums are integer size, according to the C standard.1170 ,param (foreign-value "sizeof(int) * CHAR_BIT" int))))1171 ((nonnull-pointer nonnull-c-pointer)1172 `(##sys#foreign-pointer-argument ,param) )1173 (else param) ) )1174 (else param) ) ) ) ) )1175 (lambda ()1176 (quit-compiling "foreign type `~S' refers to itself" type)) ) ) ) )117711781179;;; Compute foreign-type conversions:11801181(define (foreign-type-result-converter t)1182 (and-let* (((symbol? t))1183 (ft (lookup-foreign-type t))1184 (retconv (vector-ref ft 2)) )1185 retconv))11861187(define (foreign-type-argument-converter t)1188 (and-let* (((symbol? t))1189 (ft (lookup-foreign-type t))1190 (argconv (vector-ref ft 1)) )1191 argconv))11921193(define (foreign-type-convert-result r t) ; Used only in compiler.scm1194 (or (and-let* ((retconv (foreign-type-result-converter t)))1195 (list retconv r) )1196 r) )11971198(define (foreign-type-convert-argument a t) ; Used only in compiler.scm1199 (or (and-let* ((argconv (foreign-type-argument-converter t)) )1200 (list argconv a) )1201 a) )12021203(define (final-foreign-type t0) ; Used here and in compiler.scm1204 (follow-without-loop1205 t01206 (lambda (t next)1207 (cond ((and (symbol? t) (lookup-foreign-type t))1208 => (lambda (t2) (next (vector-ref t2 0)) ) )1209 (else t) ) )1210 (lambda () (quit-compiling "foreign type `~S' refers to itself" t0)) ) )121112121213;;; Compute foreign result size:12141215(define (estimate-foreign-result-size type)1216 (define (err t)1217 (quit-compiling "cannot compute size for unknown foreign type `~S' result" type))1218 (follow-without-loop1219 type1220 (lambda (t next)1221 (case t1222 ((char int short bool void unsigned-short scheme-object unsigned-char unsigned-int byte unsigned-byte1223 int32 unsigned-int32)1224 0)1225 ((c-string nonnull-c-string c-pointer nonnull-c-pointer symbol c-string* nonnull-c-string*1226 unsigned-c-string unsigned-c-string* nonnull-unsigned-c-string*1227 c-string-list c-string-list*)1228 (words->bytes 3) )1229 ((unsigned-integer long integer unsigned-long integer32 unsigned-integer32)1230 (words->bytes 6) ) ; 1 bignum digit on 32-bit (overallocs on 64-bit)1231 ((float double number)1232 (words->bytes 4) ) ; possibly 8-byte aligned 64-bit double1233 ((complex cplxnum)1234 (words->bytes 8)) ; 2 double numbers, possibly 8-byte aligned (overallocs on 64-bit)1235 ((integer64 unsigned-integer64 size_t ssize_t)1236 (words->bytes 7)) ; 2 bignum digits on 32-bit (overallocs on 64-bit)1237 (else1238 (cond ((and (symbol? t) (lookup-foreign-type t))1239 => (lambda (t2) (next (vector-ref t2 0)) ) )1240 ((pair? t)1241 (case (car t)1242 ((ref nonnull-pointer pointer c-pointer nonnull-c-pointer function instance instance-ref nonnull-instance)1243 (words->bytes 3) )1244 ((const) (next (cadr t)))1245 ((struct union) (words->bytes 3)) ;; struct wrapper1246 ((enum) (words->bytes 6)) ; 1 bignum digit on 32-bit (overallocs on 64-bit)1247 (else (err t))))1248 (else (err t))))))1249 (lambda () (quit-compiling "foreign type `~S' refers to itself" type)) ) )12501251(define (estimate-foreign-result-location-size type) ; Used only in compiler.scm1252 (define (err t)1253 (quit-compiling "cannot compute size of location for foreign type `~S'" t) )1254 (follow-without-loop1255 type1256 (lambda (t next)1257 (case t1258 ((char int short bool unsigned-short unsigned-char unsigned-int long unsigned-long byte1259 unsigned-byte c-pointer nonnull-c-pointer unsigned-integer integer float c-string symbol1260 scheme-pointer nonnull-scheme-pointer int32 unsigned-int32 integer32 unsigned-integer321261 unsigned-c-string unsigned-c-string* nonnull-unsigned-c-string*1262 nonnull-c-string c-string* nonnull-c-string* c-string-list c-string-list*)1263 (words->bytes 1) )1264 ((double integer64 unsigned-integer64 size_t ssize_t)1265 (words->bytes 2) )1266 ((complex cplxnum)1267 (words->bytes 4))1268 (else1269 (cond ((and (symbol? t) (lookup-foreign-type t))1270 => (lambda (t2) (next (vector-ref t2 0)) ) )1271 ((pair? t)1272 (case (car t)1273 ((ref nonnull-pointer pointer c-pointer nonnull-c-pointer function1274 scheme-pointer nonnull-scheme-pointer enum)1275 (words->bytes 1))1276 ((struct union) (words->bytes 3)) ;; struct wrapper1277 ((const) (next (cadr t)))1278 (else (err t)) ) )1279 (else (err t)) ) ) ) )1280 (lambda () (quit-compiling "foreign type `~S' refers to itself" type)) ) )128112821283;;; Convert result value, if a string:12841285(define (finish-foreign-result type body) ; Used only in compiler.scm1286 (let ((type (strip-syntax type)))1287 (case type1288 ((c-string unsigned-c-string) `(##sys#peek-c-string ,body (##core#quote 0)))1289 ((nonnull-c-string) `(##sys#peek-nonnull-c-string ,body (##core#quote 0)))1290 ((c-string* unsigned-c-string*) `(##sys#peek-and-free-c-string ,body (##core#quote 0)))1291 ((nonnull-c-string* nonnull-unsigned-c-string*) `(##sys#peek-and-free-nonnull-c-string ,body (##core#quote 0)))1292 ((symbol) `(##sys#string->symbol (##sys#peek-c-string ,body (##core#quote 0))))1293 ((c-string-list) `(##sys#peek-c-string-list ,body (##core#quote #f)))1294 ((c-string-list*) `(##sys#peek-and-free-c-string-list ,body (##core#quote #f)))1295 (else1296 (cond ((not (list? type)) body)1297 ((and (memq (car type) '(struct union))1298 (= 2 (length type)))1299 `(##sys#wrap-struct (##core#quote ,(struct/union-wrapper-type-name type)) ,body))1300 ((and (eq? (car type) 'const)1301 (= 2 (length type))1302 (memq (cadr type) '(c-string c-string* unsigned-c-string1303 unsigned-c-string* nonnull-c-string1304 nonnull-c-string*1305 nonnull-unsigned-string*)))1306 (finish-foreign-result (cadr type) body))1307 ((= 3 (length type))1308 (case (car type)1309 ((instance instance-ref)1310 (let ((tmp (gensym)))1311 `(let ((,tmp ,body))1312 (and ,tmp1313 (not (##sys#null-pointer? ,tmp))1314 (make ,(caddr type)1315 (##core#quote this) ,tmp) ) ) ) )1316 ((nonnull-instance)1317 `(make ,(caddr type) (##core#quote this) ,body) )1318 (else body)))1319 (else body))))))132013211322;;; Translate foreign-type into scrutinizer type:13231324;; Used in chicken-ffi-syntax.scm and scrutinizer.scm1325(define (foreign-type->scrutiny-type t mode) ; MODE = 'arg | 'result1326 ;; If the foreign type has a converter, it can return a different1327 ;; type from the native type matching the foreign type (see #1649)1328 (if (or (and (eq? mode 'arg) (foreign-type-argument-converter t))1329 (and (eq? mode 'result) (foreign-type-result-converter t)))1330 ;; Here we just punt on the type, but it would be better to1331 ;; find out the result type of the converter procedure.1332 '*1333 (let ((ft (final-foreign-type t)))1334 (case ft1335 ((void) 'undefined)1336 ((char unsigned-char) 'char)1337 ((int unsigned-int short unsigned-short byte unsigned-byte int32 unsigned-int32)1338 'fixnum)1339 ((float double)1340 (case mode1341 ((arg) 'number)1342 (else 'float)))1343 ((complex cplxnum) 'complex)1344 ((scheme-pointer nonnull-scheme-pointer) '*)1345 ((bytevector u8vector1346 blob) ; DEPRECATED1347 (case mode1348 ((arg) '(or false bytevector))1349 (else 'bytevector)))1350 ((nonnull-bytevector) 'bytevector)1351 ((nonnull-blob) 'bytevector) ; DEPRECATED1352 ((pointer-vector)1353 (case mode1354 ((arg) '(or false pointer-vector))1355 (else 'pointer-vector)))1356 ((nonnull-pointer-vector) 'pointer-vector)1357 ((u16vector s8vector s16vector u32vector s32vector u64vector s64vector f32vector f64vector)1358 (case mode1359 ((arg) `(or false (struct ,ft)))1360 (else `(struct ,ft))))1361 ((nonnull-u8vector) 'bytevector)1362 ((nonnull-s8vector) '(struct s8vector))1363 ((nonnull-u16vector) '(struct u16vector))1364 ((nonnull-s16vector) '(struct s16vector))1365 ((nonnull-u32vector) '(struct u32vector))1366 ((nonnull-s32vector) '(struct s32vector))1367 ((nonnull-u64vector) '(struct u64vector))1368 ((nonnull-s64vector) '(struct s64vector))1369 ((nonnull-f32vector) '(struct f32vector))1370 ((nonnull-f64vector) '(struct f64vector))1371 ((integer long size_t ssize_t integer32 unsigned-integer32 integer64 unsigned-integer641372 unsigned-long)1373 'integer)1374 ((c-pointer)1375 (if (eq? 'arg mode)1376 '(or false pointer locative)1377 '(or false pointer)))1378 ((nonnull-c-pointer)1379 (if (eq? 'arg mode)1380 '(or pointer locative)1381 'pointer))1382 ((c-string c-string* unsigned-c-string unsigned-c-string*)1383 '(or false string))1384 ((c-string-list c-string-list*)1385 '(list-of string))1386 ((nonnull-c-string nonnull-c-string* nonnull-unsigned-c-string*) 'string)1387 ((symbol) 'symbol)1388 (else1389 (cond ((pair? t)1390 (case (car t)1391 ((ref pointer function c-pointer)1392 (if (eq? 'arg mode)1393 '(or false pointer locative)1394 '(or false pointer)))1395 ((const) (foreign-type->scrutiny-type (cadr t) mode))1396 ((struct union)1397 `(struct ,(struct/union-wrapper-type-name t)))1398 ((enum) 'integer)1399 ((nonnull-pointer nonnull-c-pointer)1400 (if (eq? 'arg mode)1401 '(or pointer locative)1402 'pointer))1403 (else '*)))1404 (else '*)))))))140514061407;;; Scan expression-node for variable usage:14081409(define (scan-used-variables node vars)1410 (let ([used '()])1411 (let walk ([n node])1412 (let ([subs (node-subexpressions n)])1413 (case (node-class n)1414 [(##core#variable set!)1415 (let ([var (first (node-parameters n))])1416 (when (and (memq var vars) (not (memq var used)))1417 (set! used (cons var used)) )1418 (for-each walk subs) ) ]1419 [(quote ##core#undefined ##core#primitive) #f]1420 [else (for-each walk subs)] ) ) )1421 used) )142214231424;;; Scan expression-node for free variables (that are not in env):14251426(define (scan-free-variables node block-compilation)1427 (let ((vars '())1428 (hvars '()))14291430 (define (walk n e)1431 (let ([subs (node-subexpressions n)]1432 [params (node-parameters n)] )1433 (case (node-class n)1434 ((quote ##core#undefined ##core#primitive ##core#proc ##core#inline_ref) #f)1435 ((##core#variable)1436 (let ((var (first params)))1437 (unless (memq var e)1438 (set! vars (lset-adjoin/eq? vars var))1439 (unless (variable-visible? var block-compilation)1440 (set! hvars (lset-adjoin/eq? hvars var))))))1441 ((set!)1442 (let ((var (first params)))1443 (unless (memq var e) (set! vars (lset-adjoin/eq? vars var)))1444 (walk (car subs) e) ) )1445 ((let)1446 (walk (first subs) e)1447 (walk (second subs) (append params e)) )1448 ((##core#lambda)1449 (##sys#decompose-lambda-list1450 (third params)1451 (lambda (vars argc rest)1452 (walk (first subs) (append vars e)) ) ) )1453 (else (walkeach subs e)) ) ) )14541455 (define (walkeach ns e)1456 (for-each (lambda (n) (walk n e)) ns) )14571458 (walk node '())1459 (values vars hvars) ) ) ; => freevars hiddenvars146014611462;;; Special block-variable literal type:14631464(define-record block-variable-literal1465 name) ; symbol146614671468;;; Generation of random names:14691470;; This one looks iffy. It's also used only in compiler.scm1471(define (make-random-name . prefix)1472 (string->symbol1473 (sprintf "~A-~A~A"1474 (optional prefix (gensym))1475 (current-seconds)1476 (##core#inline "C_random_fixnum" 1000))))147714781479;;; Register/lookup real names:1480;1481; - The real-name-table contains the following mappings:1482;1483; <variable-alias> -> <variable>1484; <lambda-id> -> <variable> or <variable-alias>14851486(define-constant real-name-table-size 997)14871488(define real-name-table #f)14891490(define (clear-real-name-table!)1491 (set! real-name-table (make-vector real-name-table-size '())))14921493(define (set-real-name! name rname) ; Used only in compiler.scm1494 (hash-table-set! real-name-table name rname))14951496;; TODO: Find out why there are so many lookup functions for this and1497;; reduce them to the minimum.1498(define (get-real-name name)1499 (hash-table-ref real-name-table name))15001501;; Arbitrary limit to prevent runoff into exponential behavior1502(define real-name-max-depth 20)15031504(define (real-name var . db)1505 (define (resolve n)1506 (let ((n2 (hash-table-ref real-name-table n)))1507 (if n21508 (or (hash-table-ref real-name-table n2)1509 n2)1510 n) ) )1511 (let ((rn (resolve var)))1512 (cond ((not rn) (##sys#symbol->string var))1513 ((pair? db)1514 (let ((db (car db)))1515 (let loop ((nesting (list (##sys#symbol->string rn)))1516 (depth 0)1517 (container (db-get db var 'contained-in)) )1518 (cond1519 ((> depth real-name-max-depth)1520 (string-intersperse (reverse (cons "..." nesting)) " in "))1521 (container1522 (let ((rc (resolve container)))1523 (if (eq? rc container)1524 (string-intersperse (reverse nesting) " in ")1525 (loop (cons (symbol->string rc) nesting)1526 (fx+ depth 1)1527 (db-get db container 'contained-in) ) ) ))1528 (else (string-intersperse (reverse nesting) " in "))) ) ) )1529 (else (##sys#symbol->string rn)) ) ) )15301531(define (real-name2 var db) ; Used only in c-backend.scm1532 (and-let* ((rn (hash-table-ref real-name-table var)))1533 (real-name rn db) ) )15341535(define (display-real-name-table)1536 (hash-table-for-each1537 (lambda (key val)1538 (printf "~S\t~S~%" key val) )1539 real-name-table) )15401541(define (source-info->string info) ; Used only in c-backend.scm1542 (if (list? info)1543 (let ((ln (car info))1544 (name (cadr info)))1545 (conc ln ":" (make-string (max 0 (- 4 (string-length ln))) #\space) " " name) )1546 (->string info)))15471548(define (source-info->name info)1549 (if (list? info) (cadr info) (->string info)))15501551(define (source-info->line info)1552 (and (list? info) (car info)))15531554(define (call-info params var) ; Used only in optimizer.scm1555 (or (and-let* ((info (and (pair? (cdr params)) (second params))))1556 (and (list? info)1557 (let ((ln (car info))1558 (name (cadr info)))1559 (conc "(" ln ") " var))))1560 var))156115621563;;; constant folding support:15641565(define (constant-form-eval op argnodes k) ; Used only in optimizer.scm1566 (let* ((args (map (lambda (n) (first (node-parameters n))) argnodes))1567 (form (cons op (map (lambda (arg) `(quote ,arg)) args))))1568 ;; op must have toplevel binding, result must be single-valued1569 (let ((proc (##sys#slot op 0)))1570 (if (procedure? proc)1571 (let ((results (handle-exceptions ex ex (receive (apply proc args)))))1572 (cond ((condition? results) (k #f #f))1573 ((and (= 1 (length results))1574 (encodeable-literal? (car results)))1575 (debugging 'o "folded constant expression" form)1576 (k #t (car results)))1577 ((= 1 (length results)) ; not encodeable; don't fold1578 (k #f #f))1579 (else1580 (bomb "attempt to constant-fold call to procedure that has multiple results" form))))1581 (bomb "attempt to constant-fold call to non-procedure" form)))))15821583(define (maybe-constant-fold-call n subs k)1584 (define (constant-node? n2) (eq? 'quote (node-class n2)))1585 (if (eq? '##core#variable (node-class (car subs)))1586 (let ((var (first (node-parameters (car subs)))))1587 (if (and (intrinsic? var)1588 (or (foldable? var)1589 (predicate? var))1590 (every constant-node? (cdr subs)) )1591 (constant-form-eval var (cdr subs) (lambda (ok res) (k ok res #t)))1592 (k #f #f #f)))1593 (k #f #f #f)))15941595;; Is the literal small enough to be encoded? Otherwise, it should1596;; not be constant-folded.1597(define (encodeable-literal? lit)1598 (define getsize1599 (foreign-lambda* int ((scheme-object lit))1600 "return(C_header_size(lit));"))1601 (define (fits? n)1602 (fx<= (integer-length n) 24))1603 (cond ((immediate? lit))1604 ((##core#inline "C_i_exact_integerp" lit)1605 ;; Could use integer-length, but that's trickier (minus1606 ;; symbol etc). If the string is too large to allocate,1607 ;; we'll also get an exception!1608 (let ((str (handle-exceptions ex #f (number->string lit 16))))1609 (and str (fits? (string-length str)))))1610 ((flonum? lit))1611 ((symbol? lit)1612 (let ((str (##sys#symbol->string/shared lit)))1613 (fits? (string-length str))))1614 ((string? lit)1615 (fits? (getsize (##sys#slot lit 0))))1616 ((##core#inline "C_byteblockp" lit)1617 (fits? (getsize lit)))1618 (else1619 (let ((len (getsize lit)))1620 (and (fits? len)1621 (every1622 encodeable-literal?1623 (list-tabulate len (lambda (i)1624 (##sys#slot lit i)))))))))162516261627;;; Dump node structure:16281629(define (dump-nodes n) ; Used only in batch-driver.scm1630 (let loop ([i 0] [n n])1631 (let ([class (node-class n)]1632 [params (node-parameters n)]1633 [subs (node-subexpressions n)]1634 [ind (make-string i #\space)]1635 [i2 (+ i 2)] )1636 (printf "~%~A<~A ~S" ind class params)1637 (for-each (cut loop i2 <>) subs)1638 (let ([len (##sys#size n)])1639 (when (fx> len 4)1640 (printf "[~S" (##sys#slot n 4))1641 (do ([i 5 (fx+ i 1)])1642 ((fx>= i len))1643 (printf " ~S" (##sys#slot n i)) )1644 (write-char #\]) ) )1645 (write-char #\>) ) )1646 (newline) )164716481649;; DEPRECATED1650(define (read/source-info in)1651 (chicken.syntax#read-with-source-info in) )16521653;;; "#> ... <#" syntax:16541655(set! ##sys#user-read-hook1656 (let ([old-hook ##sys#user-read-hook])1657 (lambda (char port)1658 (if (char=? #\> char)1659 (let* ((_ (read-char port)) ; swallow #\>1660 (text (scan-sharp-greater-string port)))1661 `(declare (foreign-declare ,text)) )1662 (old-hook char port) ) ) ) )16631664(define (scan-sharp-greater-string port)1665 (let ([out (open-output-string)])1666 (let loop ()1667 (let ((c (read-char port)))1668 (cond ((eof-object? c)1669 (quit-compiling "unexpected end of `#> ... <#' sequence"))1670 ((char=? c #\newline)1671 (newline out)1672 (loop) )1673 ((char=? c #\<)1674 (let ([c (read-char port)])1675 (if (eqv? #\# c)1676 (get-output-string out)1677 (begin1678 (write-char #\< out)1679 (write-char c out)1680 (loop) ) ) ) )1681 (else1682 (write-char c out)1683 (loop) ) ) ) ) ) )168416851686;;; 64-bit fixnum?16871688(define (big-fixnum? x) ;; XXX: This should probably be in c-platform1689 (and (fixnum? x)1690 (feature? #:64bit)1691 (or (fx> x 1073741823)1692 (fx< x -1073741824) ) ) )16931694(define (small-bignum? x) ;; XXX: This should probably be in c-platform1695 (and (bignum? x)1696 (not (feature? #:64bit))1697 (fx<= (integer-length x) 62) ) )169816991700;;; symbol visibility and other global variable properties17011702(define (hide-variable sym) ; Used in compiler.scm and here1703 (mark-variable sym '##compiler#visibility 'hidden))17041705(define (export-variable sym) ; Used only in compiler.scm1706 (mark-variable sym '##compiler#visibility 'exported))17071708(define (variable-hidden? sym)1709 (eq? (##sys#get sym '##compiler#visibility) 'hidden))17101711(define (unhide-variable sym)1712 (when (variable-hidden? sym) (remprop! sym '##compiler#visibility)))17131714(define (variable-visible? sym block-compilation)1715 (let ((p (##sys#get sym '##compiler#visibility)))1716 (case p1717 ((hidden) #f)1718 ((exported) #t)1719 (else (not block-compilation)))))17201721;; These two have somewhat confusing names. Maybe mark-variable could1722;; be renamed to "variable-mark-set!"? Also, in some other situations,1723;; put!/get are used directly.1724(define (mark-variable var mark #!optional (val #t))1725 (##sys#put! var mark val) )17261727(define (variable-mark var mark)1728 (##sys#get var mark) )17291730(define intrinsic? (cut variable-mark <> '##compiler#intrinsic))1731;; Used only in optimizer.scm1732(define foldable? (cut variable-mark <> '##compiler#foldable))1733(define predicate? (cut variable-mark <> '##compiler#predicate))173417351736;;; Load support files17371738(define (load-identifier-database name) ; Used only in batch-driver.scm1739 (and-let* ((dbfile (chicken.load#find-file name (repository-path))))1740 (debugging 'p (sprintf "loading identifier database ~a ...~%" dbfile))1741 (for-each1742 (lambda (e)1743 (let ((id (car e)))1744 (##sys#put!1745 id '##core#db1746 (append (or (##sys#get id '##core#db) '()) (list (cdr e))) )))1747 (call-with-input-file dbfile read-expressions))))174817491750;;; Print version/usage information:17511752(define (print-version #!optional b) ; Used only in batch-driver.scm1753 (when b (print* +banner+))1754 (print (chicken-version #t)) )17551756;; Used only in batch-driver.scm, but it seems to me this should be moved1757;; to chicken.scm, as that's the only place this belongs.1758(define (print-usage)1759 (print-version)1760 (newline)1761 (display #<<EOF1762Usage: chicken FILENAME [OPTION ...]17631764 `chicken' is the CHICKEN compiler.17651766 FILENAME should be a complete source file name with extension, or "-" for1767 standard input. OPTION may be one of the following:17681769 General options:17701771 -help display this text and exit1772 -version display compiler version and exit1773 -release print release number and exit1774 -verbose display information on compilation progress17751776 File and pathname options:17771778 -output-file FILENAME specifies output-filename, default is 'out.c'1779 -include-path PATHNAME specifies alternative path for included files1780 -to-stdout write compiled file to stdout instead of file17811782 Language options:17831784 -feature SYMBOL register feature identifier1785 -no-feature SYMBOL disable built-in feature identifier17861787 Syntax related options:17881789 -case-insensitive don't preserve case of read symbols1790 -keyword-style STYLE allow alternative keyword syntax1791 (prefix, suffix or none)1792 -no-parentheses-synonyms disables list delimiter synonyms1793 -r7rs-syntax disables the CHICKEN extensions to1794 R7RS syntax1795 -compile-syntax macros are made available at run-time1796 -emit-import-library MODULE write compile-time module information into1797 separate file1798 -emit-all-import-libraries emit import-libraries for all defined modules1799 -no-compiler-syntax disable expansion of compiler-macros1800 -module NAME wrap compiled code in a module1801 -module-registration always generate module registration code1802 -no-module-registration never generate module registration code1803 (overrides `-module-registration')18041805 Translation options:18061807 -explicit-use do not use units 'library' and 'eval' by1808 default1809 -check-syntax stop compilation after macro-expansion1810 -analyze-only stop compilation after first analysis pass18111812 Debugging options:18131814 -no-warnings disable warnings1815 -debug-level NUMBER set level of available debugging information1816 -no-trace disable tracing information1817 -debug-info enable debug-information in compiled code for use1818 with an external debugger1819 -profile executable emits profiling information1820 -profile-name FILENAME name of the generated profile information file1821 -accumulate-profile executable emits profiling information in1822 append mode1823 -no-lambda-info omit additional procedure-information1824 -emit-types-file FILENAME write type-declaration information into file1825 -consult-types-file FILENAME load additional type database18261827 Optimization options:18281829 -optimize-level NUMBER enable certain sets of optimization options1830 -optimize-leaf-routines enable leaf routine optimization1831 -no-usual-integrations standard procedures may be redefined1832 -unsafe disable all safety checks1833 -local assume globals are only modified in current1834 file1835 -block enable block-compilation1836 -disable-interrupts disable interrupts in compiled code1837 -fixnum-arithmetic assume all numbers are fixnums1838 -disable-stack-overflow-checks disables detection of stack-overflows1839 -inline enable inlining1840 -inline-limit LIMIT set inlining threshold1841 -inline-global enable cross-module inlining1842 -specialize perform type-based specialization of primitive calls1843 -emit-inline-file FILENAME generate file with globally inlinable1844 procedures (implies -inline -local)1845 -consult-inline-file FILENAME explicitly load inline file1846 -no-argc-checks disable argument count checks1847 -no-bound-checks disable bound variable checks1848 -no-procedure-checks disable procedure call checks1849 -no-procedure-checks-for-usual-bindings1850 disable procedure call checks only for usual1851 bindings1852 -no-procedure-checks-for-toplevel-bindings1853 disable procedure call checks for toplevel1854 bindings1855 -strict-types assume variable do not change their type1856 -lfa2 perform additional lightweight flow-analysis pass1857 -unroll-limit LIMIT specifies inlining limit for self-recursive calls18581859 Configuration options:18601861 -unit NAME compile file as a library unit1862 -uses NAME declare library unit as used.1863 -heap-size NUMBER specifies heap-size of compiled executable1864 -nursery NUMBER -stack-size NUMBER1865 specifies nursery size of compiled executable1866 -extend FILENAME load file before compilation commences1867 -prelude EXPRESSION add expression to front of source file1868 -postlude EXPRESSION add expression to end of source file1869 -prologue FILENAME include file before main source file1870 -epilogue FILENAME include file after main source file1871 -dynamic compile as dynamically loadable code1872 -require-extension NAME require and import extension NAME18731874 Obscure options:18751876 -debug MODES display debugging output for the given modes1877 -raw do not generate implicit init- and exit code1878 -emit-external-prototypes-first1879 emit prototypes for callbacks before foreign1880 declarations1881 -regenerate-import-libraries emit import libraries even when unchanged1882 -ignore-repository do not refer to repository for extensions1883 -setup-mode prefer the current directory when locating extensions18841885EOF1886) )18871888;; Same as above1889(define (print-debug-options)1890 (display #<<EOF18911892Available debugging options:18931894 a show node-matching during simplification1895 b show breakdown of time needed for each compiler pass1896 c print every expression before macro-expansion1897 d lists all assigned global variables1898 e show information about specializations1899 h you already figured that out1900 i show information about inlining1901 m show GC statistics during compilation1902 n print the line-number database1903 o show performed optimizations1904 p display information about what the compiler is currently doing1905 r show invocation parameters1906 s show program-size information and other statistics1907 t show time needed for compilation1908 u lists all unassigned global variable references1909 x display information about experimental features1910 D when printing nodes, use node-tree output1911 I show inferred type information for unexported globals1912 N show the real-name mapping table1913 P show expressions after specialization1914 S show applications of compiler syntax1915 T show expressions after converting to node tree1916 1 show source expressions1917 2 show canonicalized expressions1918 3 show expressions converted into CPS1919 4 show database after each analysis pass1920 5 show expressions after each optimization pass1921 6 show expressions after each inlining pass1922 7 show expressions after complete optimization1923 8 show database after final analysis1924 9 show expressions after closure conversion192519261927EOF1928))1929)1930