~ chicken-core (chicken-5) /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" blob)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" blob)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" blob)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" blob)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 ;; TODO: Remove once we have a bootstrapping libchicken with bwp-object?271 ((##core#inline "C_bwpp" lit) #;(bwp-object? lit) 'bwp)272 ((vector? lit) 'vector)273 ((and (not (##sys#immediate? lit)) (##sys#generic-structure? lit))274 `(struct ,(##sys#slot lit 0)))275 ((char? lit) 'char)276 (else '*)))277278 (define (merge t1 t2)279 (cond ((eq? t1 t2) t1)280 ((and (pair? t1) (pair? t2)281 (eq? (car t1) 'struct)282 (eq? (car t2) 'struct)283 (eq? (cadr t1) (cadr t2)))284 t1)285 (else '*)))286287 (define (report elim)288 (cond ((assoc elim stats) =>289 (lambda (a) (set-cdr! a (add1 (cdr a)))))290 (else (set! stats (alist-cons elim 1 stats)))))291292 (define (assigned? var)293 (db-get db var 'assigned))294295 (define (droppable? n)296 (or (memq (node-class n)297 '(quote ##core#undefined ##core#primitive ##core#lambda))298 (and (eq? '##core#variable (node-class n))299 (let ((var (first (node-parameters n))))300 (or (not (db-get db var 'global))301 (variable-mark var '##compiler#always-bound))))))302303 (define (drop! n)304 (sub-boxed n)305 (node-class-set! n '##core#undefined)306 (node-parameters-set! n '())307 (node-subexpressions-set! n '()))308309 (define (extinguish! node rpl) ; replace ##core#inline call310 (report (first (node-parameters node)))311 (let ((subs (node-subexpressions node))312 (alldropped #t))313 (for-each314 (lambda (sn)315 (if (droppable? sn)316 (drop! sn)317 (set! alldropped #f)))318 subs)319 (if alldropped320 (drop! node)321 (node-parameters-set!322 node323 (list324 (string-append325 rpl326 (case (length (node-subexpressions node))327 ((1) "1")328 ((2) "2")329 ((3) "3")330 (else (bomb "bad number of arguments to extinguished ##core#inline")))))))))331332 (define (vartype v te ae)333 (cond ((assq v te) => cdr)334 (else335 (let loop ((ae ae))336 (cond ((null? ae) '*)337 ((and (eq? v (cdar ae))338 (assq (caar ae) te) )339 => cdr)340 (else (loop (cdr ae))))))))341342 (define (varnode? n)343 (eq? '##core#variable (node-class n)))344345 (define (floatvar? var)346 (assq var floatvars))347348 (define (eliminate-floatvar var)349 (set! floatvars350 (remove (lambda (a) (eq? var (car a))) floatvars)))351352 (define (count-floatvar node acc #!optional (n 1))353 (cond ((and (varnode? node)354 (assq (first (node-parameters node)) floatvars))355 =>356 (lambda (a)357 (set-car! (acc a) (+ n (car (acc a))))))))358359 (define (add-boxed node) (count-floatvar node cdr))360 (define (add-unboxed node) (count-floatvar node cddr))361 (define (sub-boxed node) (count-floatvar node cdr -1))362363 (define (walk n te ae)364 (let ((class (node-class n))365 (params (node-parameters n))366 (subs (node-subexpressions n)))367 (case class368 ((##core#variable)369 (when (and (floatvar? (first params))370 (not (assq (first params) te)))371 (eliminate-floatvar (first params)))372 (add-boxed n)373 (vartype (first params) te ae))374 ((if ##core#cond)375 (let ((tr (walk (first subs) te ae)))376 (if (and (pair? tr) (eq? 'boolean (car tr)))377 (merge (walk (second subs)378 (append (second tr) te)379 ae)380 (walk (third subs)381 (append (third tr) te)382 ae)))383 (merge (walk (second subs) te ae)384 (walk (third subs) te ae))))385 ((quote) (constant-result (first params)))386 ((let)387 (let* ((val (first subs))388 (var (first params))389 (r (walk val te ae))390 (avar (assigned? var)))391 (cond ((and (not avar)392 (eq? 'float r)393 (not (floatvar? var)))394 (set! floatvars (cons (list var 0 0) floatvars))395 (add-unboxed val))396 (else (add-boxed val)))397 (walk (second subs)398 (if avar399 te400 (alist-cons var r te))401 (if (and (varnode? val)402 (not avar)403 (not (assigned? (first (node-parameters val)))))404 (let ((var2 (first (node-parameters val))))405 (alist-cons var var2 (alist-cons var2 var ae)))406 ae))))407 ((##core#lambda ##core#direct_lambda)408 ;; fresh env and we don't bother to create entries in the environment409 ;; for the llist-bound variables (missing implies type '*)410 ;;XXX (but we could treat the first arg in non-CPS lambdas as procedure...)411 (walk (first subs) '() '())412 'procedure)413 ((set! ##core#set!) ;XXX is ##core#set! still used?414 (let ((val (first subs)))415 (when (and (varnode? val)416 (floatvar? (first (node-parameters val))))417 (eliminate-floatvar (first (node-parameters val))))418 (walk val te ae)419 'undefined))420 ((##core#undefined) 'undefined)421 ((##core#primitive) 'procedure)422 ((##core#inline ##core#inline_allocate)423 (let ((ubop (assoc (first params) +unboxed-map+)))424 (for-each425 (lambda (arg)426 (walk arg te ae)427 (when ubop (add-unboxed arg)))428 subs))429 (cond ((assoc (first params) +type-check-map+) =>430 (lambda (a)431 (let ((r1 (walk (first subs) te ae)))432 (cond (unsafe433 (extinguish! n "C_i_noop"))434 ((eq? '*struct* (cadr a))435 ;; handle known structure type436 (when (and (pair? r1)437 (eq? 'struct (first r1))438 (eq? 'quote (node-class (second subs))))439 (let ((st (first (node-parameters (second subs)))))440 (when (and (symbol? st)441 (eq? st (second r1)))442 (extinguish! n "C_i_noop")))))443 ((and (pair? r1) (eq? 'boolean (car r1)))444 (when (memq 'boolean (cdr a))445 (extinguish! n "C_i_noop")))446 ;; handle other types447 ((member r1 (cdr a))448 (extinguish! n "C_i_noop")))449 '*)))450 ((assoc (first params) +ffi-type-check-map+) =>451 (lambda (a)452 (let* ((arg (first subs))453 (r1 (walk arg te ae)))454 (when (member r1 (cdr a))455 (node-class-set! n (node-class arg))456 (node-parameters-set! n (node-parameters arg))457 (node-subexpressions-set! n (node-subexpressions arg)))458 ;; the ffi checks are enforcing so we always end up with459 ;; the correct type460 r1)))461 ((assoc (first params) +predicate-map+) =>462 (lambda (a)463 (let ((arg (first subs)))464 (cond ((varnode? arg)465 `(boolean466 ((,(first (node-parameters arg))467 .468 ,(if (eq? '*struct* (cadr a))469 (if (eq? 'quote (node-class (second subs)))470 (let ((st (first471 (node-parameters472 (second subs)))))473 (if (symbol? st)474 `(struct ,st)475 'struct))476 'struct)477 (cadr a))))478 ()))479 (else480 (let ((r1 (walk arg te ae)))481 (cond ((eq? '*struct* (cadr a))482 ;; known structure type483 (when (and (pair? r1)484 (eq? 'struct (first r1))485 (eq? 'quote (node-class (second subs))))486 (let ((st (first487 (node-parameters (second subs)))))488 (when (and (symbol? st)489 (eq? st (second r1)))490 (extinguish! n "C_i_true")))))491 ((and (pair? r1) (eq? 'boolean (car r1)))492 (when (memq 'boolean (cdr a))493 (extinguish! n "C_i_true")))494 ;; other types495 ((member r1 (cdr a))496 (extinguish! n "C_i_true")))497 'boolean))))))498 ((assoc (first params) +constructor-map+) =>499 (lambda (a)500 (let ((arg1 (and (pair? subs) (first subs))))501 (if (and arg1502 (eq? '*struct* (cadr a))503 (eq? 'quote (node-class arg1)))504 (let ((tag (first (node-parameters arg1))))505 (if (symbol? tag)506 `(struct ,tag)507 'struct))508 (cadr a)))))))509 (else510 (for-each (cut walk <> te ae) subs)511 '*))))512513 (walk node '() '())514 (when (pair? stats)515 (with-debugging-output516 '(x o)517 (lambda ()518 (print "eliminated type checks:")519 (for-each520 (lambda (ss) (printf " ~a:\t~a~%" (car ss) (cdr ss)))521 stats))))522 floatvars))523524525(define (perform-unboxing node floatvar-counts)526 (let ((floatvars (filter-map527 (lambda (a)528 (and (= (cadr a) (caddr a))529 (car a)))530 floatvar-counts))531 (count 0))532533 (define (walk/unbox n)534 (let ((class (node-class n))535 (params (node-parameters n))536 (subs (node-subexpressions n)))537 (case class538 ((quote)539 (let ((c (first params)))540 (if (##core#inline "C_i_flonump" c)541 (make-node '##core#float (list c) '())542 n)))543 ((##core#variable)544 (let ((i (posq (first params) floatvars)))545 (if i546 (make-node '##core#float-variable (cons i params) '())547 (make-node '##core#unbox_float '() (list n)))))548 ((##core#inline ##core#inline_allocate)549 (cond ((assoc (first params) +unboxed-map+) =>550 (lambda (a)551 (let ((ub (second a))552 (type (third a)))553 (set! count (add1 count))554 (make-node '##core#inline555 (list ub)556 (map (if (eq? type 'op)557 walk/unbox558 walk)559 subs)))))560 (else561 (make-node '##core#unbox_float '()562 (list (make-node class params563 (map walk subs)))))))564 (else (make-node '##core#unbox_float '() (list (walk n)))))))565566 (define (walk n)567 (let ((class (node-class n))568 (params (node-parameters n))569 (subs (node-subexpressions n)))570 (case class571 ((##core#variable)572 (let ((i (posq (first params) floatvars)))573 (if i574 (make-node '##core#box_float '()575 (list (make-node '##core#float-variable576 (cons i params) '())))577 n)))578 ((let)579 (let* ((val (first subs))580 (var (first params))581 (i (posq var floatvars)))582 (if i583 (make-node '##core#let_float (list i var)584 (list (walk/unbox val)585 (walk (second subs))))586 (make-node 'let params (map walk subs)))))587 ((##core#inline ##core#inline_allocate)588 (cond ((assoc (first params) +unboxed-map+) =>589 (lambda (a)590 (let ((ub (second a))591 (type (third a)))592 (set! count (add1 count))593 (let ((n (make-node '##core#inline594 (list ub)595 (map (if (eq? type 'acc)596 walk597 walk/unbox)598 subs))))599 (case type600 ((pred) n)601 (else (make-node '##core#box_float '()602 (list n))))))))603 (else (make-node class params (map walk subs)))))604 (else (make-node class params (map walk subs))))))605606 (let ((node (walk node)))607 (with-debugging-output608 '(x o)609 (lambda ()610 (printf "number of unboxed float variables: ~a\n"611 (length floatvars))612 (printf "number of inline operations replaced with unboxed ones: ~a\n"613 count)))614 node)))615616)