~ chicken-core (chicken-5) e73515a5b4e03a5e8d843cf31d8fa3d05f2290de
commit e73515a5b4e03a5e8d843cf31d8fa3d05f2290de Author: felix <felix@call-with-current-continuation.org> AuthorDate: Mon Apr 11 23:05:26 2011 +0200 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Mon Apr 11 23:05:26 2011 +0200 trying to understand unboxing diff --git a/scrutinizer.scm b/scrutinizer.scm index bbd71d42..12d38aa7 100755 --- a/scrutinizer.scm +++ b/scrutinizer.scm @@ -97,6 +97,7 @@ (define (scrutinize node db complain specialize) (let ((blist '()) (safe-calls 0)) + (define (constant-result lit) (cond ((string? lit) 'string) ((symbol? lit) 'symbol) @@ -117,6 +118,7 @@ `(struct ,(##sys#slot lit 0))) ((char? lit) 'char) (else '*))) + (define (global-result id loc) (cond ((variable-mark id '##compiler#type) => (lambda (a) @@ -141,6 +143,7 @@ '(*)) (else (list a))))) (else '(*)))) + (define (blist-type id flow) (cond ((find (lambda (b) (and (eq? id (caar b)) @@ -148,6 +151,7 @@ blist) => cdr) (else #f))) + (define (variable-result id e loc flow) (cond ((blist-type id flow) => list) ((and (get db id 'assigned) @@ -163,11 +167,13 @@ '(*)) (else (list (cdr a)))))) (else (global-result id loc)))) + (define (always-true1 t) (cond ((and (pair? t) (eq? 'or (car t))) (every always-true1 (cdr t))) ((memq t '(* boolean undefined noreturn)) #f) (else #t))) + (define (always-true t loc x) (let ((f (always-true1 t))) (when f @@ -178,6 +184,7 @@ t (pp-fragment x)))) f)) + (define (typename t) (case t ((*) "anything") @@ -200,6 +207,7 @@ (sprintf "a structure of type ~a" (cadr t))) (else (bomb "invalid type: ~a" t)))) (else (bomb "invalid type: ~a" t)))))) + (define (argument-string args) (let* ((len (length args)) (m (multiples len))) @@ -209,6 +217,7 @@ "~a argument~a of type~a ~a" len m m (map typename args))))) + (define (result-string results) (if (eq? '* results) "an unknown number of values" @@ -220,10 +229,12 @@ "~a value~a of type~a ~a" len m m (map typename results)))))) + (define (simplify t) (let ((t2 (simplify1 t))) (dd "simplify: ~a -> ~a" t t2) t2)) + (define (simplify1 t) (call/cc (lambda (return) @@ -284,6 +295,7 @@ (map simplify rtypes))))) (else t)) t)))) + (define (merge-argument-types ts1 ts2) (cond ((null? ts1) (cond ((null? ts2) '()) @@ -304,16 +316,19 @@ (else '(#!rest)))) ;XXX (else (cons (simplify `(or ,(car ts1) ,(car ts2))) (merge-argument-types (cdr ts1) (cdr ts2)))))) + (define (merge-result-types ts1 ts2) ;XXX possibly overly conservative (cond ((null? ts1) ts2) ((null? ts2) ts1) ((or (atom? ts1) (atom? ts2)) '*) (else (cons (simplify `(or ,(car ts1) ,(car ts2))) (merge-result-types (cdr ts1) (cdr ts2)))))) + (define (match t1 t2) (let ((m (match1 t1 t2))) (dd " match ~a <-> ~a -> ~a" t1 t2 m) m)) + (define (match1 t1 t2) (cond ((eq? t1 t2)) ((eq? t1 '*)) @@ -340,6 +355,7 @@ ((struct) (equal? t1 t2)) (else #f) ) ) (else #f))) + (define (match-args args1 args2) (d "match-args: ~s <-> ~s" args1 args2) (define (match-rest rtype args opt) ;XXX currently ignores `opt' @@ -367,6 +383,7 @@ (match-rest (rest-type (cdr args2)) args1 opt1)) ((match (car args1) (car args2)) (loop (cdr args1) (cdr args2) opt1 opt2)) (else #f)))) + (define (match-results results1 results2) (cond ((null? results1) (atom? results2)) ((eq? '* results1)) @@ -375,8 +392,10 @@ ((match (car results1) (car results2)) (match-results (cdr results1) (cdr results2))) (else #f))) + (define (multiples n) (if (= n 1) "" "s")) + (define (single what tv loc) (if (eq? '* tv) '* @@ -393,10 +412,12 @@ (sprintf "expected ~a a single result, but were given ~a result~a" what n (multiples n))) (first tv)))))) + (define (report loc desc #!optional (show complain)) (when show (warning (conc (location-name loc) desc)))) + (define (location-name loc) (define (lname loc1) (if loc1 @@ -410,7 +431,9 @@ (if (null? (cdr loc)) (location-name loc) (sprintf "in local ~a,\n ~a" (lname (car loc)) (rec (cdr loc)))))))) + (define add-loc cons) + (define (fragment x) (let ((x (build-expression-tree x))) (let walk ((x x) (d 0)) @@ -419,11 +442,13 @@ ((list? x) (map (cute walk <> (add1 d)) (take x (min +fragment-max-length+ (length x))))) (else x))))) + (define (pp-fragment x) (string-chomp (with-output-to-string (lambda () (pp (fragment x)))))) + (define (call-result node args e loc params) (define (pname) (sprintf "~ain procedure call to `~s', " @@ -521,6 +546,7 @@ (set! safe-calls (add1 safe-calls)))) (d " result-types: ~a" r) r)))) + (define (self-call? node loc) (case (node-class node) ((##core#call) @@ -531,11 +557,13 @@ ((let) (self-call? (last (node-subexpressions node)) loc)) (else #f))) + (define tag (let ((n 0)) (lambda () (set! n (add1 n)) n))) + (define (invalidate-blist) (for-each (lambda (b) @@ -547,6 +575,7 @@ (dd "invalidating: ~a" b) (set-cdr! b '*)))) blist)) + (define (walk n e loc dest tail flow ctags) ; returns result specifier (let ((subs (node-subexpressions n)) (params (node-parameters n)) @@ -743,6 +772,7 @@ (set! d-depth (sub1 d-depth)) (dd " -> ~a" results) results))) + (let ((rn (walk (first (node-subexpressions node)) '() '() #f #f (list (tag)) #f))) (when (and (pair? specialization-statistics) (debugging 'x "specializations:")) diff --git a/unboxing.scm b/unboxing.scm index 961dc9cc..e668e9de 100644 --- a/unboxing.scm +++ b/unboxing.scm @@ -24,16 +24,20 @@ ; POSSIBILITY OF SUCH DAMAGE. -(declare (unit unboxing)) +(declare + (unit unboxing) + (hide d-depth)) (include "compiler-namespace") (include "tweaks") +(define d-depth 0) + (define (d fstr . args) (when (##sys#fudge 13) - (printf "[debug] ~?~%" fstr args)) ) + (printf "[debug] ~a~?~%" (make-string d-depth #\space) fstr args)) ) ;(define-syntax d (syntax-rules () ((_ . _) (void)))) @@ -46,7 +50,7 @@ (define (walk-lambda id e body) (let ((ae '())) - (define (boxed! v) ; 'boxed is sticky + (define (boxed! v) ; boxed is sticky (d "boxing ~a" v ) (cond ((assq v e) => (lambda (a) @@ -74,13 +78,14 @@ (define (unboxed-value? x) (and x (cdr x))) - (define (invalidate r) ; if result is variable, mark it 'boxed + (define (invalidate r) ; if result is variable, mark it boxed (when (and (pair? r) (car r)) (boxed! (car r)))) (define (alias v) (alist-ref v ae eq? v) ) + ;; merge results at control-flow join (i.e. conditional) (define (merge r1 r2) (cond ((or (not r1) (not (cdr r1))) (invalidate r2) @@ -105,7 +110,6 @@ (let ((n2 (make-node '##core#inline_unboxed (list alt) (reverse iargs)))) - ;(pp (build-expression-tree n2)) (if (and dest (cdr dest)) n2 (let ((tmp (gensym "tu"))) @@ -136,7 +140,8 @@ ((bool) (make-node '##core#inline '("C_mk_bool") - (list (make-node '##core#unboxed_ref (list tmp rtype) '())))) + (list + (make-node '##core#unboxed_ref (list tmp rtype) '())))) ((*) (bomb "unboxed type `*' not allowed as result")) (else (bomb "invalid unboxed type" rtype))))))))) ((or (eq? (car atypes) '*) @@ -146,7 +151,14 @@ (cdr atypes) (cons (car anodes) iargs))) (else - ;; introduce unboxed temporary + ;; introduce unboxed temporary for argument + ;; + ;;XXX this is suboptimal: we could reuse unboxed temporaries + ;; that are in scope. Currently the same are will be unboxed + ;; repeatedly. + ;; (But we must make sure there are not intermediate side + ;; effects - possibly only reuse unboxed value if unassigned + ;; local or lexical variable ref, or literal) (let ((tmp (gensym "tu"))) (make-node '##core#let_unboxed (list tmp (car atypes)) @@ -160,7 +172,8 @@ ((pointer) "C_pointer_address") ((bool) "C_truep") ((*) "C_id") - (else (bomb "invalid unboxed argument type" (car atypes))))) + (else + (bomb "invalid unboxed argument type" (car atypes))))) (list (car anodes))) (loop (cdr args) (cdr anodes) @@ -255,134 +268,145 @@ n)) n)) + ;; walk node and return either "(<var> . <type>)" or #f + ;; - at second pass: rewrite "##core#inline[_allocate]" nodes (define (walk n dest udest pass2?) (let ((subs (node-subexpressions n)) (params (node-parameters n)) (class (node-class n)) ) - (d "walk: (~a) ~a ~a" pass2? class params) - (case class - - ((##core#undefined - ##core#proc - ##core#global-ref - ##core#inline_ref - ##core#inline_loc_ref) #f) - - ((##core#lambda ##core#direct_lambda) - (decompose-lambda-list - (third params) - (lambda (vars argc rest) - (unless pass2? - (walk-lambda - (first params) - (map (cut cons <> #f) vars) - (first subs)) ) - #f))) - - ((##core#variable) - (let* ((v (first params)) - (a (assq v e))) - (cond (pass2? - (when (and a (cdr a)) - (copy-node! - (make-node '##core#unboxed_ref (list (alias v) (cdr a)) '()) - n))) - ((not a) #f) ; global - ((not udest) (boxed! v))) - a)) - - ((##core#inline ##core#inline_allocate) - (let* ((rw1 (##sys#get (symbolify (first params)) '##compiler#unboxed-op)) - (rw (and rw1 - (or unsafe - (and (fourth rw1) - unchecked-specialized-arithmetic)) - rw1)) - (args (map (cut walk <> #f rw pass2?) subs))) - (cond ((not rw) #f) - ((or (not pass2?) - (and dest (unboxed? dest)) - (any unboxed-value? args)) - (let ((alt (first rw)) - (atypes (second rw)) - (rtype (third rw))) - ;; result or arguments are unboxed - rewrite node to alternative + (d "walk: (~a) ~a ~a" (if pass2? 2 1) class params) + (set! d-depth (add1 d-depth)) + (let ((result + (case class + + ((##core#undefined + ##core#proc + ##core#global-ref + ##core#inline_ref + ##core#inline_loc_ref) #f) + + ((##core#lambda ##core#direct_lambda) + (decompose-lambda-list + (third params) + (lambda (vars argc rest) + (unless pass2? + (walk-lambda + (first params) + (map (cut cons <> #f) vars) + (first subs)) ) + #f))) + + ((##core#variable) + (let* ((v (first params)) + (a (assq v e))) + (cond (pass2? + (when (and a (cdr a)) + (copy-node! + (make-node + '##core#unboxed_ref + (list (alias v) (cdr a)) + '()) + n))) + ((not a) #f) ; global + ((not udest) (boxed! v))) + a)) + + ((##core#inline ##core#inline_allocate) + (let* ((rw1 (##sys#get + (symbolify (first params)) + '##compiler#unboxed-op)) + (rw (and rw1 + (or unsafe + (and (fourth rw1) + unchecked-specialized-arithmetic)) + rw1)) + (args (map (cut walk <> #f rw pass2?) subs))) + (cond ((not rw) #f) + ((or (not pass2?) + (and dest (unboxed? dest)) + (any unboxed-value? args)) + (let ((alt (first rw)) + (atypes (second rw)) + (rtype (third rw))) + ;; result or arguments are unboxed - rewrite node to alternative + (when pass2? + (rewrite! + n alt subs args atypes rtype + (and dest (assq dest e)))) + (cons #f rtype)) ) + (else + (let ((rtype (third rw))) + ;; mark argument-vars and dest as unboxed if alternative exists + (unless pass2? + (for-each + (lambda (a) + (when (and a (car a) (cdr a)) + (unboxed! (car a) (cdr a)))) + args) + (when dest + (unboxed! dest rtype))) + (cons #f rtype)))))) + + ((let) + (let* ((v (first params)) + (r1 (walk (first subs) v #t pass2?))) + (when (and (not pass2?) r1 (cdr r1)) + (unboxed! (first params) (cdr r1))) + (let ((r (walk (second subs) dest udest pass2?))) (when pass2? - (rewrite! - n alt subs args atypes rtype - (and dest (assq dest e)))) - (cons #f rtype)) ) - (else - (let ((rtype (third rw))) - ;; mark argument-vars and dest as unboxed if alternative exists - (unless pass2? - (for-each - (lambda (a) - (when (and a (car a) (cdr a)) - (unboxed! (car a) (cdr a)))) - args) - (when dest - (unboxed! dest rtype))) - (cons #f rtype)))))) - - ((let) - (let* ((v (first params)) - (r1 (walk (first subs) v #t pass2?))) - (when (and (not pass2?) r1 (cdr r1)) - (unboxed! (first params) (cdr r1))) - (let ((r (walk (second subs) dest udest pass2?))) - (when pass2? - (let ((a (assq v e))) - (if (and a (cdr a)) - (rebind-unboxed! n (cdr a)) - (straighten-binding! n))) ) - r))) - - ((set!) - (let* ((var (first params)) - (a (assq var e)) - (val (walk (first subs) var (and a (cdr a)) pass2?))) - (cond (pass2? - (when (and a (cdr a)) ; may have mutated - (copy-node! - (make-node - '##core#unboxed_set! (list (alias var) (cdr a)) subs) - n))) - ((and val (cdr val)) - (unboxed! var (cdr val))) - (else - (boxed! var) - (invalidate val) ) ) - #f)) - - ((quote) #f) - - ((if ##core#cond) - (invalidate (walk (first subs) #f #f pass2?)) - (straighten-conditional! n) - (let ((r1 (walk (second subs) dest udest pass2?)) - (r2 (walk (third subs) dest udest pass2?))) - (merge r1 r2))) - - ((##core#switch) - (invalidate (walk (first subs) #f #f pass2?)) - (do ((clauses (cdr subs) (cddr clauses)) - (r 'none - (if (eq? r 'none) - (walk (second clauses) dest udest pass2?) - (merge r (walk (second clauses) dest udest pass2?))))) - ((null? (cdr clauses)) - (merge r (walk (car clauses) dest udest pass2?))) ) ) - - ((##core#call ##core#direct_call) - (for-each (o invalidate (cut walk <> #f #f pass2?)) subs) - (when pass2? - (straighten-call! n)) - #f) - - (else - (for-each (o invalidate (cut walk <> #f #f pass2?)) subs) - #f)))) + (let ((a (assq v e))) + (if (and a (cdr a)) + (rebind-unboxed! n (cdr a)) + (straighten-binding! n))) ) + r))) + + ((set!) + (let* ((var (first params)) + (a (assq var e)) + (val (walk (first subs) var (and a (cdr a)) pass2?))) + (cond (pass2? + (when (and a (cdr a)) ; may have mutated + (copy-node! + (make-node + '##core#unboxed_set! (list (alias var) (cdr a)) subs) + n))) + ((and val (cdr val)) + (unboxed! var (cdr val))) + (else + (boxed! var) + (invalidate val) ) ) + #f)) + + ((quote) #f) + + ((if ##core#cond) + (invalidate (walk (first subs) #f #f pass2?)) + (straighten-conditional! n) + (let ((r1 (walk (second subs) dest udest pass2?)) + (r2 (walk (third subs) dest udest pass2?))) + (merge r1 r2))) + + ((##core#switch) + (invalidate (walk (first subs) #f #f pass2?)) + (do ((clauses (cdr subs) (cddr clauses)) + (r 'none + (if (eq? r 'none) + (walk (second clauses) dest udest pass2?) + (merge r (walk (second clauses) dest udest pass2?))))) + ((null? (cdr clauses)) + (merge r (walk (car clauses) dest udest pass2?))) ) ) + + ((##core#call ##core#direct_call) + (for-each (o invalidate (cut walk <> #f #f pass2?)) subs) + (when pass2? + (straighten-call! n)) + #f) + + (else + (for-each (o invalidate (cut walk <> #f #f pass2?)) subs) + #f)))) + (set! d-depth (sub1 d-depth)) + result))) (d "walk lambda: ~a (pass 1)" id) ;; walk once and mark boxed/unboxed variables in environmentTrap