ikarus/scheme/pass-specify-rep.ss

595 lines
21 KiB
Scheme

;;; Ikarus Scheme -- A compiler for R6RS Scheme.
;;; Copyright (C) 2006,2007,2008 Abdulaziz Ghuloum
;;;
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License version 3 as
;;; published by the Free Software Foundation.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;(module primops (primop? cogen-primop)
; (define (primop? x) #f)
; (define cogen-primop (lambda args (error 'cogen-primop "not yet"))))
;
;#!eof
;(define-syntax export-all-module
; (syntax-rules (define)
; [(_ M (define name* v*) ...)
; (module M (name* ...)
; (define name* v*) ...)]))
;
;(export-all-module object-representation
; (define fixnum-scale 4)
; (define fixnum-shift 2)
; (define fixnum-tag 0)
; (define fixnum-mask 3))
(module primops (primop? get-primop set-primop!)
(define cookie (gensym))
(define (primop? x)
(and (getprop x cookie) #t))
(define (get-primop x)
(or (getprop x cookie)
(error 'getprimop "not a primitive" x)))
(define (set-primop! x v)
(putprop x cookie v))
)
(module (specify-representation)
(import primops)
(define-struct PH
(interruptable? p-handler p-handled? v-handler v-handled? e-handler e-handled?))
(define interrupt-handler
(make-parameter (lambda () (error 'interrupt-handler "uninitialized"))))
(define (interrupt)
((interrupt-handler))
(prm 'interrupt))
(define (with-interrupt-handler p x ctxt args
make-interrupt-call make-no-interrupt-call
k)
(cond
[(not (PH-interruptable? p))
(parameterize ([interrupt-handler
(lambda ()
(error 'cogen "uninterruptable"
x args ctxt))])
(k))]
[else
(let ([interrupted? #f])
(let ([body
(parameterize ([interrupt-handler
(lambda () (set! interrupted? #t))])
(k))])
(cond
[(not interrupted?) body]
[(eq? ctxt 'V)
(let ([h (make-interrupt-call x args)])
(if (struct-case body
[(primcall op) (eq? op 'interrupt)]
[else #f])
(make-no-interrupt-call x args)
(make-shortcut body h)))]
[(eq? ctxt 'E)
(let ([h (make-interrupt-call x args)])
(if (struct-case body
[(primcall op) (eq? op 'interrupt)]
[else #f])
(make-no-interrupt-call x args)
(make-shortcut body h)))]
[(eq? ctxt 'P)
(let ([h (prm '!= (make-interrupt-call x args) (K bool-f))])
(if (struct-case body
[(primcall op) (eq? op 'interrupt)]
[else #f])
(prm '!= (make-no-interrupt-call x args) (K bool-f))
(make-shortcut body h)))]
[else (error 'with-interrupt-handler "invalid context" ctxt)])))]))
(define (copy-tag orig new)
(struct-case orig
[(known _ t) (make-known new t)]
[else new]))
(define (remove-tag x)
(struct-case x
[(known expr t) expr]
[else x]))
(define-syntax with-tmp
(lambda (x)
(syntax-case x ()
[(_ ([lhs* rhs*] ...) b b* ...)
(with-syntax ([(n* ...) (generate-temporaries #'(lhs* ...))])
#'(let ([lhs* rhs*] ...)
(let ([n* (unique-var 'lhs*)] ...)
(make-bind (list n* ...) (list lhs* ...)
(let ([lhs* (copy-tag lhs* n*)] ...)
(seq* b b* ...))))))])))
;;; if ctxt is V:
;;; if cogen-value, then V
;;; if cogen-pred, then (if P #f #t)
;;; if cogen-effect, then (seq E (void))
;;;
;;; if ctxt is P:
;;; if cogen-pred, then P
;;; if cogen-value, then (!= V #f)
;;; if cogen-effect, then (seq E #t)
;;;
;;; if ctxt is E:
;;; if cogen-effect, then E
;;; if cogen-value, then (let ([tmp V]) (nop))
;;; if cogen-pred, then (if P (nop) (nop))
(define (simplify* args k)
(define (S* ls)
(cond
[(null? ls) (values '() '() '())]
[else
(let-values ([(lhs* rhs* arg*) (S* (cdr ls))])
(let ([a (car ls)])
(struct-case a
[(known expr type)
(struct-case expr
[(constant i)
;;; erase known tag
(values lhs* rhs* (cons expr arg*))]
[else
;(printf "known ~s ~s\n" type expr)
(let ([tmp (unique-var 'tmp)])
(values (cons tmp lhs*)
(cons (V expr) rhs*)
(cons (make-known tmp type) arg*)))])]
[(constant i)
(values lhs* rhs* (cons a arg*))]
[else
(let ([t (unique-var 'tmp)])
(values (cons t lhs*) (cons (V a) rhs*) (cons t arg*)))])))]))
(let-values ([(lhs* rhs* args) (S* args)])
(cond
[(null? lhs*) (k args)]
[else
(make-bind lhs* rhs* (k args))])))
;;;
(define (make-cogen-handler make-interrupt-call make-no-interrupt-call)
(define (cogen-primop x ctxt args)
(define (interrupt? x)
(struct-case x
[(primcall x) (eq? x 'interrupt)]
[else #f]))
(let ([p (get-primop x)])
(simplify* args
(lambda (args)
(with-interrupt-handler p x ctxt (map T args)
make-interrupt-call make-no-interrupt-call
(lambda ()
(case ctxt
[(P)
(cond
[(PH-p-handled? p)
(apply (PH-p-handler p) args)]
[(PH-v-handled? p)
(let ([e (apply (PH-v-handler p) args)])
(if (interrupt? e) e (prm '!= e (K bool-f))))]
[(PH-e-handled? p)
(let ([e (apply (PH-e-handler p) args)])
(if (interrupt? e) e (make-seq e (K #t))))]
[else (error 'cogen-primop "not handled" x)])]
[(V)
(cond
[(PH-v-handled? p)
(apply (PH-v-handler p) args)]
[(PH-p-handled? p)
(let ([e (apply (PH-p-handler p) args)])
(if (interrupt? e)
e
(make-conditional e (K bool-t) (K bool-f))))]
[(PH-e-handled? p)
(let ([e (apply (PH-e-handler p) args)])
(if (interrupt? e) e (make-seq e (K void-object))))]
[else (error 'cogen-primop "not handled" x)])]
[(E)
(cond
[(PH-e-handled? p)
(apply (PH-e-handler p) args)]
[(PH-p-handled? p)
(let ([e (apply (PH-p-handler p) args)])
(if (interrupt? e)
e
(make-conditional e (prm 'nop) (prm 'nop))))]
[(PH-v-handled? p)
(let ([e (apply (PH-v-handler p) args)])
(if (interrupt? e)
e
(with-tmp ([t e]) (prm 'nop))))]
[else (error 'cogen-primop "not handled" x)])]
[else
(error 'cogen-primop "invalid context" ctxt)])))))))
cogen-primop)
(module (cogen-primop cogen-debug-primop)
(define (primop-interrupt-handler x)
(case x
[(fx+) 'error@fx+]
[(fx-) 'error@fx-]
[(fx*) 'error@fx*]
[(add1) 'error@add1]
[(sub1) 'error@sub1]
[(fxadd1) 'error@fxadd1]
[(fxsub1) 'error@fxsub1]
[(fxarithmetic-shift-left) 'error@fxarithmetic-shift-left]
[(fxarithmetic-shift-right) 'error@fxarithmetic-shift-right]
[else x]))
(define (make-interrupt-call op args)
(make-funcall
(V (make-primref (primop-interrupt-handler op)))
args))
(define (make-no-interrupt-call op args)
(make-funcall (V (make-primref op)) args))
(define cogen-primop
(make-cogen-handler make-interrupt-call make-no-interrupt-call))
(define (cogen-debug-primop op src/loc ctxt args)
(define (make-call op args)
(make-funcall
(V (make-primref 'debug-call))
(cons* (V src/loc) (V (make-primref op)) args)))
((make-cogen-handler make-call make-call)
op ctxt args)))
(define-syntax define-primop
(lambda (x)
(define (cogen-name stx name suffix)
(datum->syntax stx
(string->symbol
(format "cogen-~a-~a"
suffix
(syntax->datum name)))))
(define (generate-handler name ctxt case*)
(define (filter-cases case*)
(syntax-case case* ()
[() '()]
[([(c . arg*) b b* ...] . rest)
(free-identifier=? #'c ctxt)
(cons #'[arg* b b* ...] (filter-cases #'rest))]
[(c . rest) (filter-cases #'rest)]))
(let ([case* (filter-cases case*)])
(with-syntax ([ctxt ctxt] [name name]
[(case* ...) case*]
[handled? (not (null? case*))])
#'[(case-lambda
case* ...
[args (interrupt)])
handled?])))
(syntax-case x ()
[(stx name int? case* ...)
(with-syntax ([cogen-p (cogen-name #'stx #'name "pred")]
[cogen-e (cogen-name #'stx #'name "effect")]
[cogen-v (cogen-name #'stx #'name "value")]
[interruptable?
(syntax-case #'int? (safe unsafe)
[safe #t] [unsafe #f])]
[(p-handler phandled?)
(generate-handler #'name #'P #'(case* ...))]
[(v-handler vhandled?)
(generate-handler #'name #'V #'(case* ...))]
[(e-handler ehandled?)
(generate-handler #'name #'E #'(case* ...))])
#'(begin
(define cogen-p p-handler)
(define cogen-v v-handler)
(define cogen-e e-handler)
(module ()
(set-primop! 'name
(make-PH interruptable?
cogen-p phandled?
cogen-v vhandled?
cogen-e ehandled?)))))])))
(define (handle-fix lhs* rhs* body)
(define (closure-size x)
(struct-case x
[(closure code free*)
(if (null? free*)
0
(align (+ disp-closure-data
(* (length free*) wordsize))))]))
(define (partition p? lhs* rhs*)
(cond
[(null? lhs*) (values '() '() '() '())]
[else
(let-values ([(a* b* c* d*)
(partition p? (cdr lhs*) (cdr rhs*))]
[(x y) (values (car lhs*) (car rhs*))])
(cond
[(p? x y)
(values (cons x a*) (cons y b*) c* d*)]
[else
(values a* b* (cons x c*) (cons y d*))]))]))
(define (combinator? lhs rhs)
(struct-case rhs
[(closure code free*) (null? free*)]))
(define (sum n* n)
(cond
[(null? n*) n]
[else (sum (cdr n*) (+ n (car n*)))]))
(define (adders lhs n n*)
(cond
[(null? n*) '()]
[else
(cons (prm 'int+ lhs (K n))
(adders lhs (+ n (car n*)) (cdr n*)))]))
(define (build-closures lhs* rhs* body)
(let ([lhs (car lhs*)] [rhs (car rhs*)]
[lhs* (cdr lhs*)] [rhs* (cdr rhs*)])
(let ([n (closure-size rhs)]
[n* (map closure-size rhs*)])
(make-bind (list lhs)
(list (prm 'alloc
(K (sum n* n))
(K closure-tag)))
(make-bind lhs* (adders lhs n n*)
body)))))
(define (build-setters lhs* rhs* body)
(define (build-setter lhs rhs body)
(struct-case rhs
[(closure code free*)
(make-seq
(prm 'mset lhs
(K (- disp-closure-code closure-tag))
(V code))
(let f ([ls free*]
[i (- disp-closure-data closure-tag)])
(cond
[(null? ls) body]
[else
(make-seq
(prm 'mset lhs (K i) (V (car ls)))
(f (cdr ls) (+ i wordsize)))])))]))
(cond
[(null? lhs*) body]
[else
(build-setter (car lhs*) (car rhs*)
(build-setters (cdr lhs*) (cdr rhs*) body))]))
(let-values ([(flhs* frhs* clhs* crhs*)
(partition combinator? lhs* rhs*)])
(cond
[(null? clhs*) (make-bind flhs* (map V frhs*) body)]
[(null? flhs*)
(build-closures clhs* crhs*
(build-setters clhs* crhs* body))]
[else
(make-bind flhs* (map V frhs*)
(build-closures clhs* crhs*
(build-setters clhs* crhs* body)))])))
(define (constant-rep x)
(let ([c (constant-value x)])
(cond
[(fx? c) (make-constant (* c fx-scale))]
[(boolean? c) (make-constant (if c bool-t bool-f))]
[(eq? c (void)) (make-constant void-object)]
[(bwp-object? c) (make-constant bwp-object)]
[(char? c) (make-constant
(fxlogor char-tag
(fxsll (char->integer c) char-shift)))]
[(null? c) (make-constant nil)]
[(eof-object? c) (make-constant eof)]
[(object? c) (error 'constant-rep "double-wrap")]
[else (make-constant (make-object c))])))
(define (V x) ;;; erase known values
(struct-case x
[(known x t)
(unknown-V x)]
[else (unknown-V x)]))
(define (unknown-V x)
(struct-case x
[(constant) (constant-rep x)]
[(var) x]
[(primref name)
(prm 'mref
(K (make-object (primref->symbol name)))
(K (- disp-symbol-record-value symbol-ptag)))]
[(code-loc) (make-constant x)]
[(closure) (make-constant x)]
[(bind lhs* rhs* body)
(make-bind lhs* (map V rhs*) (V body))]
[(fix lhs* rhs* body)
(handle-fix lhs* rhs* (V body))]
[(conditional e0 e1 e2)
(make-conditional (P e0) (V e1) (V e2))]
[(seq e0 e1)
(make-seq (E e0) (V e1))]
[(primcall op arg*)
(case op
[(debug-call)
(cogen-debug-call op 'V arg* V)]
[else (cogen-primop op 'V arg*)])]
[(forcall op arg*)
(make-forcall op (map V arg*))]
[(funcall rator arg*)
(make-funcall (Function rator) (map V arg*))]
[(jmpcall label rator arg*)
(make-jmpcall label (V rator) (map V arg*))]
[else (error 'cogen-V "invalid value expr" x)]))
(define (cogen-debug-call op ctxt arg* k)
(define (fail)
(k (make-funcall (make-primref 'debug-call) arg*)))
(assert (>= (length arg*) 2))
(let ([src/expr (car arg*)]
[op (cadr arg*)]
[args (cddr arg*)])
(struct-case (remove-tag op)
[(primref name)
(if (primop? name)
(cogen-debug-primop name src/expr ctxt args)
(fail))]
[else (fail)])))
(define (P x)
(struct-case x
[(constant c) (if c (K #t) (K #f))]
[(primref) (K #t)]
[(code-loc) (K #t)]
[(closure) (K #t)]
[(bind lhs* rhs* body)
(make-bind lhs* (map V rhs*) (P body))]
[(conditional e0 e1 e2)
(make-conditional (P e0) (P e1) (P e2))]
[(seq e0 e1)
(make-seq (E e0) (P e1))]
[(fix lhs* rhs* body)
(handle-fix lhs* rhs* (P body))]
[(primcall op arg*)
(case op
[(debug-call)
(cogen-debug-call op 'P arg* P)]
[else (cogen-primop op 'P arg*)])]
[(var) (prm '!= (V x) (V (K #f)))]
[(funcall) (prm '!= (V x) (V (K #f)))]
[(jmpcall) (prm '!= (V x) (V (K #f)))]
[(forcall) (prm '!= (V x) (V (K #f)))]
[(known expr type)
;;; FIXME: suboptimal
(P expr)]
[else (error 'cogen-P "invalid pred expr" x)]))
(define (E x)
(struct-case x
[(constant) (nop)]
[(var) (nop)]
[(primref) (nop)]
[(code-loc) (nop)]
[(closure) (nop)]
[(bind lhs* rhs* body)
(make-bind lhs* (map V rhs*) (E body))]
[(conditional e0 e1 e2)
(make-conditional (P e0) (E e1) (E e2))]
[(seq e0 e1)
(make-seq (E e0) (E e1))]
[(fix lhs* rhs* body)
(handle-fix lhs* rhs* (E body))]
[(primcall op arg*)
(case op
[(debug-call)
(cogen-debug-call op 'E arg* E)]
[else (cogen-primop op 'E arg*)])]
[(forcall op arg*)
(make-forcall op (map V arg*))]
[(funcall rator arg*)
(make-funcall (Function rator) (map V arg*))]
[(jmpcall label rator arg*)
(make-jmpcall label (V rator) (map V arg*))]
[(known expr type)
;;; FIXME: suboptimal
(E expr)]
[else (error 'cogen-E "invalid effect expr" x)]))
(define (Function x)
(define (Function x check?)
(define (nonproc x check?)
(cond
[check?
(with-tmp ([x (V x)])
(make-shortcut
(make-seq
(make-conditional
(tag-test x closure-mask closure-tag)
(prm 'nop)
(prm 'interrupt))
x)
(V (make-funcall (make-primref 'error)
(list (K 'apply) (K "not a procedure") x)))))]
[else
(V x)]))
(struct-case x
[(primcall op args)
(cond
[(and (eq? op 'top-level-value)
(= (length args) 1)
(let f ([x (car args)])
(struct-case x
[(constant x)
(and (symbol? x) x)]
[(known x t) (f x)]
[else #f]))) =>
(lambda (sym)
(reset-symbol-proc! sym)
(prm 'mref (T (K sym))
(K (- disp-symbol-record-proc symbol-ptag))))]
[else (nonproc x check?)])]
[(primref op) (V x)]
[(known x t)
(cond
[(eq? (T:procedure? t) 'yes)
;(record-optimization 'procedure x)
(Function x #f)]
[else (Function x check?)])]
[else (nonproc x check?)]))
(Function x #t))
(define record-optimization^
(let ([h (make-eq-hashtable)])
(lambda (what expr)
(let ([n (hashtable-ref h what 0)])
(hashtable-set! h what (+ n 1))
(printf "optimize ~a[~s]: ~s\n" what n (unparse expr))))))
(define-syntax record-optimization
(syntax-rules ()
[(_ what expr) (void)]))
;;;========================================================================
;;;
(define (interrupt-unless x)
(make-conditional x (prm 'nop) (interrupt)))
(define (interrupt-when x)
(make-conditional x (interrupt) (prm 'nop)))
(define (interrupt-unless-fixnum x)
(interrupt-unless (tag-test x fx-mask fx-tag)))
(define (T x)
(struct-case x
[(var) x]
[(constant i) (constant-rep x)]
[(known expr type)
(make-known (T expr) type)]
[else (error 'cogen-T "invalid" (unparse x))]))
(define (ClambdaCase x)
(struct-case x
[(clambda-case info body)
(make-clambda-case info (V body))]
[else (error 'specify-rep "invalid clambda-case" x)]))
;;;
(define (Clambda x)
(struct-case x
[(clambda label case* cp free* name)
(make-clambda label
(map ClambdaCase case*)
cp free* name)]
[else (error 'specify-rep "invalid clambda" x)]))
;;;
(define (Program x)
(struct-case x
[(codes code* body)
(let ([code* (map Clambda code*)]
[body (V body)])
(make-codes code* body))]
[else (error 'specify-rep "invalid program" x)]))
(define (specify-representation x)
(let ([x (Program x)])
x))
(include "pass-specify-rep-primops.ss"))