~ chicken-core (master) /lfa2.scm
Trap1;;;; lfa2.scm - a lightweight "secondary" flow analysis2;3; Copyright (c) 2012-2022, The CHICKEN Team4; All rights reserved.5;6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following7; conditions are met:8;9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following10; disclaimer.11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following12; disclaimer in the documentation and/or other materials provided with the distribution.13; Neither the name of the author nor the names of its contributors may be used to endorse or promote14; products derived from this software without specific prior written permission.15;16; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE24; POSSIBILITY OF SUCH DAMAGE.252627;; This pass does a lightweight flow-analysis on value types, mostly28;; to handle the case where user code performs a type-check followed29;; by inlined accessors (for example when using record structures).30;; Specialization takes place before inlining, so even though we have31;; the type-information, later inlining will still keep the code for32;; checking argument types. Additionally, this pass detects unboxing33;; opportunities for floating point values and replaces uses of certain34;; fp operations with unboxed ones.353637(declare38 (unit lfa2)39 (uses extras support))4041(module chicken.compiler.lfa242 (perform-secondary-flow-analysis perform-unboxing)4344(import scheme45 chicken.base46 chicken.compiler.support47 chicken.fixnum48 chicken.format49 chicken.keyword)5051(include "tweaks")52(include "mini-srfi-1.scm")535455;;; Maps checks to types5657(define +type-check-map+58 '(("C_i_check_closure" procedure)59 ("C_i_check_exact" fixnum bignum integer ratnum) ;; DEPRECATED60 ("C_i_check_inexact" float) ; Or an inexact cplxnum...61 ("C_i_check_number" fixnum integer bignum ratnum float cplxnum number)62 ("C_i_check_string" string)63 ("C_i_check_bytevector" bytevector)64 ("C_i_check_symbol" symbol)65 ("C_i_check_keyword" keyword)66 ("C_i_check_list" null pair list)67 ("C_i_check_pair" pair)68 ("C_i_check_locative" locative)69 ("C_i_check_boolean" boolean)70 ("C_i_check_vector" vector)71 ("C_i_check_structure" *struct*) ; special case72 ("C_i_check_char" char)73 ("C_i_check_closure_2" procedure)74 ("C_i_check_exact_2" fixnum bignum integer ratnum) ;; DEPRECATED75 ("C_i_check_inexact_2" float) ; Or an inexact cplxnum...76 ("C_i_check_number_2" fixnum integer bignum ratnum float cplxnum number)77 ("C_i_check_string_2" string)78 ("C_i_check_bytevector_2" bytevector)79 ("C_i_check_symbol_2" symbol)80 ("C_i_check_keyword_2" keyword)81 ("C_i_check_list_2" null pair list)82 ("C_i_check_pair_2" pair)83 ("C_i_check_locative_2" locative)84 ("C_i_check_boolean_2" boolean)85 ("C_i_check_vector_2" vector)86 ("C_i_check_structure_2" *struct*) ; special case87 ("C_i_check_char_2" char)))888990;; Maps predicates to types9192(define +predicate-map+93 '(("C_i_closurep" procedure)94 ("C_fixnump" fixnum)95 ("C_bignump" bignum)96 ("C_i_exact_integerp" integer fixnum bignum)97 ("C_i_flonump" float)98 ("C_i_numberp" number fixnum integer bignum ratnum float cplxnum)99 ("C_i_ratnump" ratnum)100 ("C_i_cplxnump" cplxnum)101 ("C_stringp" string)102 ("C_bytevectorp" bytevector)103 ("C_i_keywordp" keyword)104 ("C_i_symbolp" symbol)105 ("C_i_listp" list)106 ("C_i_pairp" pair)107 ("C_locativep" locative)108 ("C_booleanp" boolean)109 ("C_i_vectorp" vector)110 ("C_structurep" struct)111 ("C_i_structurep" *struct*) ; special case112 ("C_charp" char)113 ("C_i_portp" port)114 ("C_i_nullp" null)))115116;; Maps foreign type checks to types117118(define +ffi-type-check-map+119 '(("C_i_foreign_fixnum_argumentp" fixnum)120 ("C_i_foreign_integer_argumentp" integer fixnum bignum)121 ("C_i_foreign_char_argumentp" char)122 ("C_i_foreign_flonum_argumentp" float)123 ("C_i_foreign_string_argumentp" string)124 ("C_i_foreign_symbol_argumentp" symbol)))125126;; Maps constructors to types127128(define +constructor-map+129 '(("C_a_i_record1" *struct*) ; special case130 ("C_a_i_record2" *struct*)131 ("C_a_i_record3" *struct*)132 ("C_a_i_record4" *struct*)133 ("C_a_i_record5" *struct*)134 ("C_a_i_record6" *struct*)135 ("C_a_i_record7" *struct*)136 ("C_a_i_record8" *struct*)137 ("C_a_i_record" *struct*)138 ("C_a_i_string" string)139 ("C_a_i_port" port)140 ("C_a_i_vector1" vector)141 ("C_a_i_vector2" vector)142 ("C_a_i_vector3" vector)143 ("C_a_i_vector4" vector)144 ("C_a_i_vector5" vector)145 ("C_a_i_vector6" vector)146 ("C_a_i_vector7" vector)147 ("C_a_i_vector8" vector)148 ("C_a_pair" pair)149 ("C_a_i_bytevector" bytevector)150 ("C_a_i_make_locative" locative)151 ("C_a_i_vector" vector)152 ("C_a_i_list1" pair)153 ("C_a_i_list2" pair)154 ("C_a_i_list3" pair)155 ("C_a_i_list4" pair)156 ("C_a_i_list5" pair)157 ("C_a_i_list6" pair)158 ("C_a_i_list7" pair)159 ("C_a_i_list8" pair)160 ("C_a_i_cons" pair)161 ("C_a_i_flonum" float)162 ("C_a_i_fix_to_flo" float)163 ("C_a_i_big_to_flo" float)164 ("C_a_i_fix_to_big" bignum)165 ("C_a_i_bignum0" bignum)166 ("C_a_i_bignum1" bignum)167 ("C_a_i_bignum2" bignum)168 ("C_a_i_flonum_abs" float)169 ("C_a_i_flonum_acos" float)170 ("C_a_i_flonum_acosh" float)171 ("C_a_i_flonum_actual_quotient_checked" float)172 ("C_a_i_flonum_asin" float)173 ("C_a_i_flonum_asinh" float)174 ("C_a_i_flonum_atan2" float)175 ("C_a_i_flonum_atan" float)176 ("C_a_i_flonum_atanh" float)177 ("C_a_i_flonum_ceiling" float)178 ("C_a_i_flonum_cos" float)179 ("C_a_i_flonum_cosh" float)180 ("C_a_i_flonum_difference" float)181 ("C_a_i_flonum_exp" float)182 ("C_a_i_flonum_expt" float)183 ("C_a_i_flonum_floor" float)184 ("C_a_i_flonum_gcd" float)185 ("C_a_i_flonum_log" float)186 ("C_a_i_flonum_modulo_checked" float)187 ("C_a_i_flonum_negate" float)188 ("C_a_i_flonum_plus" float)189 ("C_a_i_flonum_quotient_checked" float)190 ("C_a_i_flonum_quotient" float)191 ("C_a_i_flonum_remainder_checked" float)192 ("C_a_i_flonum_round" float)193 ("C_a_i_flonum_round_proper" float)194 ("C_a_i_flonum_sin" float)195 ("C_a_i_flonum_sinh" float)196 ("C_a_i_flonum_sqrt" float)197 ("C_a_i_flonum_tan" float)198 ("C_a_i_flonum_tanh" float)199 ("C_a_i_flonum_times" float)200 ("C_a_i_flonum_multiply_add" float)201 ("C_a_i_flonum_truncate" float)202 ("C_a_u_i_f64vector_ref" float)203 ("C_a_u_i_f32vector_ref" float)204 ;;XXX are there more?205 ))206207(define +unboxed-map+208 '(("C_a_i_flonum_plus" "C_ub_i_flonum_plus" op)209 ("C_a_i_flonum_difference" "C_ub_i_flonum_difference" op)210 ("C_a_i_flonum_times" "C_ub_i_flonum_times" op)211 ("C_a_i_flonum_multiply_add" "C_ub_i_flonum_multiply_add" op)212 ("C_a_i_flonum_quotient" "C_ub_i_flonum_quotient" op)213 ("C_flonum_equalp" "C_ub_i_flonum_equalp" pred)214 ("C_flonum_greaterp" "C_ub_i_flonum_greaterp" pred)215 ("C_flonum_lessp" "C_ub_i_flonum_lessp" pred)216 ("C_flonum_greater_or_equal_p" "C_ub_i_flonum_greater_or_equal_p" pred)217 ("C_flonum_less_or_equal_p" "C_ub_i_flonum_less_or_equal_p" pred)218 ("C_u_i_flonum_nanp" "C_ub_i_flonum_nanp" pred)219 ("C_u_i_flonum_infinitep" "C_ub_i_flonum_infinitep" pred)220 ("C_u_i_flonum_finitepp" "C_ub_i_flonum_finitep" pred)221 ("C_a_i_flonum_sin" "C_sin" op)222 ("C_a_i_flonum_cos" "C_cos" op)223 ("C_a_i_flonum_tan" "C_tan" op)224 ("C_a_i_flonum_asin" "C_asin" op)225 ("C_a_i_flonum_acos" "C_acos" op)226 ("C_a_i_flonum_atan" "C_atan" op)227 ("C_a_i_flonum_atan2" "C_atan2" op)228 ("C_a_i_flonum_sinh" "C_sinh" op)229 ("C_a_i_flonum_cosh" "C_cosh" op)230 ("C_a_i_flonum_tanh" "C_tanh" op)231 ("C_a_i_flonum_asinh" "C_asinh" op)232 ("C_a_i_flonum_acosh" "C_acosh" op)233 ("C_a_i_flonum_atanh" "C_atanh" op)234 ("C_a_i_flonum_exp" "C_exp" op)235 ("C_a_i_flonum_expr" "C_pow" op)236 ("C_a_i_flonum_log" "C_log" op)237 ("C_a_i_flonum_sqrt" "C_sqrt" op)238 ("C_a_i_flonum_truncate" "C_trunc" op)239 ("C_a_i_flonum_ceiling" "C_ceil" op)240 ("C_a_i_flonum_floor" "C_floor" op)241 ("C_a_i_flonum_round" "C_round" op)242 ("C_a_i_flonum_abs" "C_fabs" op)243 ("C_a_u_i_f32vector_ref" "C_ub_i_f32vector_ref" acc)244 ("C_a_u_i_f64vector_ref" "C_ub_i_f64vector_ref" acc)))245246247;;; Walk nodes and perform simplified type-analysis248249(define (perform-secondary-flow-analysis node db)250 (let ((stats '())251 (floatvars '()))252253 (define (constant-result lit)254 ;; a simplified variant of the one in scrutinizer.scm255 (cond ((string? lit) 'string)256 ((keyword? lit) 'keyword)257 ((symbol? lit) 'symbol)258 ;; Do not assume fixnum width matches target platforms!259 ((or (big-fixnum? lit) (small-bignum? lit)) 'integer)260 ((fixnum? lit) 'fixnum)261 ((bignum? lit) 'bignum)262 ((flonum? lit) 'float)263 ((ratnum? lit) 'ratnum)264 ((cplxnum? lit) 'cplxnum)265 ((boolean? lit) 'boolean)266 ((null? lit) 'null)267 ((list? lit) 'list)268 ((pair? lit) 'pair)269 ((eof-object? lit) 'eof)270 ((bwp-object? lit) 'bwp)271 ((vector? lit) 'vector)272 ((and (not (##sys#immediate? lit)) (##sys#generic-structure? lit))273 `(struct ,(##sys#slot lit 0)))274 ((char? lit) 'char)275 (else '*)))276277 (define (merge t1 t2)278 (cond ((eq? t1 t2) t1)279 ((and (pair? t1) (pair? t2)280 (eq? (car t1) 'struct)281 (eq? (car t2) 'struct)282 (eq? (cadr t1) (cadr t2)))283 t1)284 (else '*)))285286 (define (report elim)287 (cond ((assoc elim stats) =>288 (lambda (a) (set-cdr! a (add1 (cdr a)))))289 (else (set! stats (alist-cons elim 1 stats)))))290291 (define (assigned? var)292 (db-get db var 'assigned))293294 (define (droppable? n)295 (or (memq (node-class n)296 '(quote ##core#undefined ##core#primitive ##core#lambda))297 (and (eq? '##core#variable (node-class n))298 (let ((var (first (node-parameters n))))299 (or (not (db-get db var 'global))300 (variable-mark var '##compiler#always-bound))))))301302 (define (drop! n)303 (sub-boxed n)304 (node-class-set! n '##core#undefined)305 (node-parameters-set! n '())306 (node-subexpressions-set! n '()))307308 (define (extinguish! node rpl) ; replace ##core#inline call309 (report (first (node-parameters node)))310 (let ((subs (node-subexpressions node))311 (alldropped #t))312 (for-each313 (lambda (sn)314 (if (droppable? sn)315 (drop! sn)316 (set! alldropped #f)))317 subs)318 (if alldropped319 (drop! node)320 (node-parameters-set!321 node322 (list323 (string-append324 rpl325 (case (length (node-subexpressions node))326 ((1) "1")327 ((2) "2")328 ((3) "3")329 (else (bomb "bad number of arguments to extinguished ##core#inline")))))))))330331 (define (vartype v te ae)332 (cond ((assq v te) => cdr)333 (else334 (let loop ((ae ae))335 (cond ((null? ae) '*)336 ((and (eq? v (cdar ae))337 (assq (caar ae) te) )338 => cdr)339 (else (loop (cdr ae))))))))340341 (define (varnode? n)342 (eq? '##core#variable (node-class n)))343344 (define (floatvar? var)345 (assq var floatvars))346347 (define (eliminate-floatvar var)348 (set! floatvars349 (remove (lambda (a) (eq? var (car a))) floatvars)))350351 (define (count-floatvar node acc #!optional (n 1))352 (cond ((and (varnode? node)353 (assq (first (node-parameters node)) floatvars))354 =>355 (lambda (a)356 (set-car! (acc a) (+ n (car (acc a))))))))357358 (define (add-boxed node) (count-floatvar node cdr))359 (define (add-unboxed node) (count-floatvar node cddr))360 (define (sub-boxed node) (count-floatvar node cdr -1))361362 (define (walk n te ae)363 (let ((class (node-class n))364 (params (node-parameters n))365 (subs (node-subexpressions n)))366 (case class367 ((##core#variable)368 (when (and (floatvar? (first params))369 (not (assq (first params) te)))370 (eliminate-floatvar (first params)))371 (add-boxed n)372 (vartype (first params) te ae))373 ((if ##core#cond)374 (let ((tr (walk (first subs) te ae)))375 (if (and (pair? tr) (eq? 'boolean (car tr)))376 (merge (walk (second subs)377 (append (second tr) te)378 ae)379 (walk (third subs)380 (append (third tr) te)381 ae)))382 (merge (walk (second subs) te ae)383 (walk (third subs) te ae))))384 ((quote) (constant-result (first params)))385 ((let)386 (let* ((val (first subs))387 (var (first params))388 (r (walk val te ae))389 (avar (assigned? var)))390 (cond ((and (not avar)391 (eq? 'float r)392 (not (floatvar? var)))393 (set! floatvars (cons (list var 0 0) floatvars))394 (add-unboxed val))395 (else (add-boxed val)))396 (walk (second subs)397 (if avar398 te399 (alist-cons var r te))400 (if (and (varnode? val)401 (not avar)402 (not (assigned? (first (node-parameters val)))))403 (let ((var2 (first (node-parameters val))))404 (alist-cons var var2 (alist-cons var2 var ae)))405 ae))))406 ((##core#lambda ##core#direct_lambda)407 ;; fresh env and we don't bother to create entries in the environment408 ;; for the llist-bound variables (missing implies type '*)409 ;;XXX (but we could treat the first arg in non-CPS lambdas as procedure...)410 (walk (first subs) '() '())411 'procedure)412 ((set! ##core#set!) ;XXX is ##core#set! still used?413 (let ((val (first subs)))414 (when (and (varnode? val)415 (floatvar? (first (node-parameters val))))416 (eliminate-floatvar (first (node-parameters val))))417 (walk val te ae)418 'undefined))419 ((##core#undefined) 'undefined)420 ((##core#primitive) 'procedure)421 ((##core#inline ##core#inline_allocate)422 (let ((ubop (assoc (first params) +unboxed-map+)))423 (for-each424 (lambda (arg)425 (walk arg te ae)426 (when ubop (add-unboxed arg)))427 subs))428 (cond ((assoc (first params) +type-check-map+) =>429 (lambda (a)430 (let ((r1 (walk (first subs) te ae)))431 (cond (unsafe432 (extinguish! n "C_i_noop"))433 ((eq? '*struct* (cadr a))434 ;; handle known structure type435 (when (and (pair? r1)436 (eq? 'struct (first r1))437 (eq? 'quote (node-class (second subs))))438 (let ((st (first (node-parameters (second subs)))))439 (when (and (symbol? st)440 (eq? st (second r1)))441 (extinguish! n "C_i_noop")))))442 ((and (pair? r1) (eq? 'boolean (car r1)))443 (when (memq 'boolean (cdr a))444 (extinguish! n "C_i_noop")))445 ;; handle other types446 ((member r1 (cdr a))447 (extinguish! n "C_i_noop")))448 '*)))449 ((assoc (first params) +ffi-type-check-map+) =>450 (lambda (a)451 (let* ((arg (first subs))452 (r1 (walk arg te ae)))453 (when (member r1 (cdr a))454 (node-class-set! n (node-class arg))455 (node-parameters-set! n (node-parameters arg))456 (node-subexpressions-set! n (node-subexpressions arg)))457 ;; the ffi checks are enforcing so we always end up with458 ;; the correct type459 r1)))460 ((assoc (first params) +predicate-map+) =>461 (lambda (a)462 (let ((arg (first subs)))463 (cond ((varnode? arg)464 `(boolean465 ((,(first (node-parameters arg))466 .467 ,(if (eq? '*struct* (cadr a))468 (if (eq? 'quote (node-class (second subs)))469 (let ((st (first470 (node-parameters471 (second subs)))))472 (if (symbol? st)473 `(struct ,st)474 'struct))475 'struct)476 (cadr a))))477 ()))478 (else479 (let ((r1 (walk arg te ae)))480 (cond ((eq? '*struct* (cadr a))481 ;; known structure type482 (when (and (pair? r1)483 (eq? 'struct (first r1))484 (eq? 'quote (node-class (second subs))))485 (let ((st (first486 (node-parameters (second subs)))))487 (when (and (symbol? st)488 (eq? st (second r1)))489 (extinguish! n "C_i_true")))))490 ((and (pair? r1) (eq? 'boolean (car r1)))491 (when (memq 'boolean (cdr a))492 (extinguish! n "C_i_true")))493 ;; other types494 ((member r1 (cdr a))495 (extinguish! n "C_i_true")))496 'boolean))))))497 ((assoc (first params) +constructor-map+) =>498 (lambda (a)499 (let ((arg1 (and (pair? subs) (first subs))))500 (if (and arg1501 (eq? '*struct* (cadr a))502 (eq? 'quote (node-class arg1)))503 (let ((tag (first (node-parameters arg1))))504 (if (symbol? tag)505 `(struct ,tag)506 'struct))507 (cadr a)))))))508 (else509 (for-each (cut walk <> te ae) subs)510 '*))))511512 (walk node '() '())513 (when (pair? stats)514 (with-debugging-output515 '(x o)516 (lambda ()517 (print "eliminated type checks:")518 (for-each519 (lambda (ss) (printf " ~a:\t~a~%" (car ss) (cdr ss)))520 stats))))521 floatvars))522523524(define (perform-unboxing node floatvar-counts)525 (let ((floatvars (filter-map526 (lambda (a)527 (and (= (cadr a) (caddr a))528 (car a)))529 floatvar-counts))530 (count 0))531532 (define (walk/unbox n)533 (let ((class (node-class n))534 (params (node-parameters n))535 (subs (node-subexpressions n)))536 (case class537 ((quote)538 (let ((c (first params)))539 (if (##core#inline "C_i_flonump" c)540 (make-node '##core#float (list c) '())541 n)))542 ((##core#variable)543 (let ((i (posq (first params) floatvars)))544 (if i545 (make-node '##core#float-variable (cons i params) '())546 (make-node '##core#unbox_float '() (list n)))))547 ((##core#inline ##core#inline_allocate)548 (cond ((assoc (first params) +unboxed-map+) =>549 (lambda (a)550 (let ((ub (second a))551 (type (third a)))552 (set! count (add1 count))553 (make-node '##core#inline554 (list ub)555 (map (if (eq? type 'op)556 walk/unbox557 walk)558 subs)))))559 (else560 (make-node '##core#unbox_float '()561 (list (make-node class params562 (map walk subs)))))))563 (else (make-node '##core#unbox_float '() (list (walk n)))))))564565 (define (walk n)566 (let ((class (node-class n))567 (params (node-parameters n))568 (subs (node-subexpressions n)))569 (case class570 ((##core#variable)571 (let ((i (posq (first params) floatvars)))572 (if i573 (make-node '##core#box_float '()574 (list (make-node '##core#float-variable575 (cons i params) '())))576 n)))577 ((let)578 (let* ((val (first subs))579 (var (first params))580 (i (posq var floatvars)))581 (if i582 (make-node '##core#let_float (list i var)583 (list (walk/unbox val)584 (walk (second subs))))585 (make-node 'let params (map walk subs)))))586 ((##core#inline ##core#inline_allocate)587 (cond ((assoc (first params) +unboxed-map+) =>588 (lambda (a)589 (let ((ub (second a))590 (type (third a)))591 (set! count (add1 count))592 (let ((n (make-node '##core#inline593 (list ub)594 (map (if (eq? type 'acc)595 walk596 walk/unbox)597 subs))))598 (case type599 ((pred) n)600 (else (make-node '##core#box_float '()601 (list n))))))))602 (else (make-node class params (map walk subs)))))603 (else (make-node class params (map walk subs))))))604605 (let ((node (walk node)))606 (with-debugging-output607 '(x o)608 (lambda ()609 (printf "number of unboxed float variables: ~a\n"610 (length floatvars))611 (printf "number of inline operations replaced with unboxed ones: ~a\n"612 count)))613 node)))614615)