diff --git a/collects/typed-scheme/optimizer/fixnum.rkt b/collects/typed-scheme/optimizer/fixnum.rkt new file mode 100644 index 00000000..378015e2 --- /dev/null +++ b/collects/typed-scheme/optimizer/fixnum.rkt @@ -0,0 +1,58 @@ +#lang scheme + +(require syntax/parse + "../utils/utils.rkt" + (for-template scheme/base scheme/fixnum scheme/unsafe/ops) + (types abbrev type-table utils subtype) + (optimizer utils)) + +(provide (all-defined-out)) + +(define-syntax-class fixnum-opt-expr + (pattern e:expr + #:when (subtypeof? #'e -Fixnum) + #:with opt ((optimize) #'e))) +(define-syntax-class nonzero-fixnum-opt-expr + (pattern e:expr + #:when (or (isoftype? #'e -PositiveFixnum) (isoftype? #'e -NegativeFixnum)) + #:with opt ((optimize) #'e))) + +(define (mk-fixnum-tbl generic) + (mk-unsafe-tbl generic "fx~a" "unsafe-fx~a")) + +;; due to undefined behavior when results are out of the fixnum range, only some +;; fixnum operations can be optimized +;; the following must be closed on fixnums +(define binary-fixnum-ops + (dict-set + (dict-set + (dict-set + (dict-set + (dict-set + (dict-set + (mk-fixnum-tbl (list #'= #'<= #'< #'> #'>= #'min #'max)) + #'bitwise-and #'unsafe-fxand) + #'fxand #'unsafe-fxand) + #'bitwise-ior #'unsafe-fxior) + #'fxior #'unsafe-fxior) + #'bitwise-xor #'unsafe-fxxor) + #'fxxor #'unsafe-fxxor)) +(define-syntax-class fixnum-unary-op + (pattern (~or (~literal bitwise-not) (~literal fxnot)) #:with unsafe #'unsafe-fxnot) + (pattern (~or (~literal abs) (~literal fxabs)) #:with unsafe #'unsafe-fxabs)) +;; closed on fixnums, but 2nd argument must not be 0 +(define-syntax-class nonzero-fixnum-binary-op + (pattern (~or (~literal quotient) (~literal fxquotient)) #:with unsafe #'unsafe-fxquotient) + (pattern (~or (~literal modulo) (~literal fxmodulo)) #:with unsafe #'unsafe-fxmodulo) + (pattern (~or (~literal remainder) (~literal fxremainder)) #:with unsafe #'unsafe-fxremainder)) + +(define-syntax-class (fixnum-op tbl) + (pattern i:id + #:when (dict-ref tbl #'i #f) + #:with unsafe (dict-ref tbl #'i))) + + +(define (optimize-finum-expr stx) + (syntax-parse stx #:literal-sets (kernel-literals) + [e:fixnum-opt-expr + (syntax/loc stx e.opt)])) diff --git a/collects/typed-scheme/optimizer/float.rkt b/collects/typed-scheme/optimizer/float.rkt new file mode 100644 index 00000000..7b2ef5cb --- /dev/null +++ b/collects/typed-scheme/optimizer/float.rkt @@ -0,0 +1,59 @@ +#lang scheme/base + +(require syntax/parse + syntax/id-table racket/dict + (for-template scheme/base scheme/flonum scheme/fixnum scheme/unsafe/ops) + "../utils/utils.rkt" + (types abbrev type-table utils subtype) + (optimizer utils fixnum)) + +(provide (all-defined-out)) + +(define-syntax-class float-opt-expr + (pattern e:expr + #:when (subtypeof? #'e -Flonum) + #:with opt ((optimize) #'e))) +(define-syntax-class int-opt-expr + (pattern e:expr + #:when (subtypeof? #'e -Integer) + #:with opt ((optimize) #'e))) + +;; if the result of an operation is of type float, its non float arguments +;; can be promoted, and we can use unsafe float operations +;; note: none of the unary operations have types where non-float arguments +;; can result in float (as opposed to real) results +(define-syntax-class float-arg-expr + (pattern e:fixnum-opt-expr + #:with opt #'(unsafe-fx->fl e.opt)) + (pattern e:int-opt-expr + #:with opt #'(->fl e.opt)) + (pattern e:float-opt-expr + #:with opt #'e.opt)) + +(define (mk-float-tbl generic) + (mk-unsafe-tbl generic "fl~a" "unsafe-fl~a")) + +(define binary-float-ops + (mk-float-tbl (list #'+ #'- #'* #'/ #'min #'max))) +(define binary-float-comps + (dict-set + (dict-set + (mk-float-tbl (list #'= #'<= #'< #'> #'>=)) + ;; not a comparison, but takes 2 floats and does not return a float, + ;; unlike binary-float-ops + #'make-rectangular #'unsafe-make-flrectangular) + #'make-flrectangular #'unsafe-make-flrectangular)) +(define unary-float-ops + (mk-float-tbl (list #'abs #'sin #'cos #'tan #'asin #'acos #'atan #'log #'exp + #'sqrt #'round #'floor #'ceiling #'truncate))) + +(define-syntax-class (float-op tbl) + (pattern i:id + #:when (dict-ref tbl #'i #f) + #:with unsafe (dict-ref tbl #'i))) + + +(define (optimize-float-expr stx) + (syntax-parse stx #:literal-sets (kernel-literals) + [e:float-opt-expr + (syntax/loc stx e.opt)])) diff --git a/collects/typed-scheme/optimizer/inexact-complex.rkt b/collects/typed-scheme/optimizer/inexact-complex.rkt new file mode 100644 index 00000000..629d7756 --- /dev/null +++ b/collects/typed-scheme/optimizer/inexact-complex.rkt @@ -0,0 +1,134 @@ +#lang scheme/base + +(require syntax/parse + "../utils/utils.rkt" + (for-template scheme/base scheme/flonum scheme/unsafe/ops) + (types abbrev type-table utils subtype) + (optimizer utils float)) + +(provide (all-defined-out)) + + +(define-syntax-class inexact-complex-opt-expr + (pattern e:expr + #:when (isoftype? #'e -InexactComplex) + #:with opt ((optimize) #'e))) + +;; it's faster to take apart a complex number and use unsafe operations on +;; its parts than it is to use generic operations +;; we keep the real and imaginary parts unboxed as long as we stay within +;; complex operations +(define-syntax-class unboxed-inexact-complex-opt-expr + (pattern (#%plain-app (~and (~var op (float-op binary-inexact-complex-ops)) (~or (~literal +) (~literal -))) + c1:unboxed-inexact-complex-opt-expr + c2:unboxed-inexact-complex-opt-expr + cs:unboxed-inexact-complex-opt-expr ...) + #:with real-part (unboxed-gensym) + #:with imag-part (unboxed-gensym) + #:with (bindings ...) + (begin (log-optimization "unboxed binary inexact complex" #'op) + #`(#,@(append (syntax->list #'(c1.bindings ... c2.bindings ... cs.bindings ... ...)) + (list #`(real-part #,(for/fold ((o #'c1.real-part)) + ((e (syntax->list #'(c2.real-part cs.real-part ...)))) + #`(op.unsafe #,o #,e))) + #`(imag-part #,(for/fold ((o #'c1.imag-part)) + ((e (syntax->list #'(c2.imag-part cs.imag-part ...)))) + #`(op.unsafe #,o #,e)))))))) + (pattern (#%plain-app (~and op (~literal *)) + c1:unboxed-inexact-complex-opt-expr + c2:unboxed-inexact-complex-opt-expr + cs:unboxed-inexact-complex-opt-expr ...) + #:with real-part (unboxed-gensym) + #:with imag-part (unboxed-gensym) + #:with (bindings ...) + (begin (log-optimization "unboxed binary inexact complex" #'op) + #`(c1.bindings ... c2.bindings ... cs.bindings ... ... + ;; we want to bind the intermediate results to reuse them + ;; the final results are bound to real-part and imag-part + #,@(let loop ([o1 #'c1.real-part] + [o2 #'c1.imag-part] + [e1 (syntax->list #'(c2.real-part cs.real-part ...))] + [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))] + [rs (append (map (lambda (x) (unboxed-gensym)) + (syntax->list #'(cs.real-part ...))) + (list #'real-part))] + [is (append (map (lambda (x) (unboxed-gensym)) + (syntax->list #'(cs.imag-part ...))) + (list #'imag-part))] + [res '()]) + (if (null? e1) + (reverse res) + (loop (car rs) (car is) (cdr e1) (cdr e2) (cdr rs) (cdr is) + ;; complex multiplication, imag part, then real part (reverse) + (list* #`(#,(car is) + (unsafe-fl+ (unsafe-fl* #,o2 #,(car e1)) + (unsafe-fl* #,o1 #,(car e2)))) + #`(#,(car rs) + (unsafe-fl- (unsafe-fl* #,o1 #,(car e1)) + (unsafe-fl* #,o2 #,(car e2)))) + res))))))) + (pattern (#%plain-app (~and op (~literal /)) + c1:unboxed-inexact-complex-opt-expr + c2:unboxed-inexact-complex-opt-expr + cs:unboxed-inexact-complex-opt-expr ...) + #:with real-part (unboxed-gensym) + #:with imag-part (unboxed-gensym) + #:with (denominators ...) + (for/list + ([e1 (syntax->list #'(c2.real-part cs.real-part ...))] + [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))]) + #`(#,(unboxed-gensym) (unsafe-fl+ (unsafe-fl* #,e1 #,e1) (unsafe-fl* #,e2 #,e2)))) + #:with (bindings ...) + (begin (log-optimization "unboxed binary inexact complex" #'op) + #`(c1.bindings ... c2.bindings ... cs.bindings ... ... denominators ... + ;; we want to bind the intermediate results to reuse them + ;; the final results are bound to real-part and imag-part + #,@(let loop ([o1 #'c1.real-part] + [o2 #'c1.imag-part] + [e1 (syntax->list #'(c2.real-part cs.real-part ...))] + [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))] + [d (map (lambda (x) (car (syntax-e x))) + (syntax->list #'(denominators ...)))] + [rs (append (map (lambda (x) (unboxed-gensym)) + (syntax->list #'(cs.real-part ...))) + (list #'real-part))] + [is (append (map (lambda (x) (unboxed-gensym)) + (syntax->list #'(cs.imag-part ...))) + (list #'imag-part))] + [res '()]) + (if (null? e1) + (reverse res) + (loop (car rs) (car is) (cdr e1) (cdr e2) (cdr d) (cdr rs) (cdr is) + ;; complex division, imag part, then real part (reverse) + (list* #`(#,(car is) + (unsafe-fl/ (unsafe-fl- (unsafe-fl* #,o2 #,(car e1)) + (unsafe-fl* #,o1 #,(car e2))) + #,(car d))) + #`(#,(car rs) + (unsafe-fl/ (unsafe-fl+ (unsafe-fl* #,o1 #,(car e1)) + (unsafe-fl* #,o2 #,(car e2))) + #,(car d))) + res))))))) + (pattern e:expr + ;; can't work on inexact reals, which are a subtype of inexact + ;; complexes, so this has to be equality + #:when (isoftype? #'e -InexactComplex) + #:with e* (unboxed-gensym) + #:with real-part (unboxed-gensym) + #:with imag-part (unboxed-gensym) + #:with (bindings ...) + #`((e* #,((optimize) #'e)) + (real-part (unsafe-flreal-part e*)) + (imag-part (unsafe-flimag-part e*))))) + +(define-syntax-class inexact-complex-unary-op + (pattern (~or (~literal real-part) (~literal flreal-part)) #:with unsafe #'unsafe-flreal-part) + (pattern (~or (~literal imag-part) (~literal flimag-part)) #:with unsafe #'unsafe-flimag-part)) +(define binary-inexact-complex-ops + (mk-float-tbl (list #'+ #'- #'* #'/))) + + +(define (optimize-inexact-complex-expr e) + (syntax-parse e #:literal-sets (kernel-literals) + [e:inexact-complex-opt-expr + (syntax/loc stx e.opt)])) diff --git a/collects/typed-scheme/optimizer/optimizer.rkt b/collects/typed-scheme/optimizer/optimizer.rkt index e21a8018..05518323 100644 --- a/collects/typed-scheme/optimizer/optimizer.rkt +++ b/collects/typed-scheme/optimizer/optimizer.rkt @@ -1,239 +1,15 @@ #lang scheme/base -(require syntax/parse (for-template scheme/base scheme/flonum scheme/fixnum scheme/unsafe/ops racket/private/for) - "../utils/utils.rkt" "../utils/tc-utils.rkt" unstable/match scheme/match unstable/syntax unstable/values - (rep type-rep) syntax/id-table racket/dict - (types abbrev type-table utils subtype)) -(provide optimize) +(require syntax/parse + syntax/id-table racket/dict + unstable/match scheme/match + (for-template scheme/base scheme/flonum scheme/fixnum scheme/unsafe/ops racket/private/for) + "../utils/utils.rkt" "../utils/tc-utils.rkt" + (rep type-rep) + (types abbrev type-table utils subtype) + (optimizer utils fixnum float inexact-complex)) -;; is the syntax object s's type a subtype of t? -(define (subtypeof s t) - (match (type-of s) - [(tc-result1: (== t (lambda (x y) (subtype y x)))) #t] [_ #f])) - - -(define-syntax-class float-opt-expr - (pattern e:opt-expr - #:when (subtypeof #'e -Flonum) - #:with opt #'e.opt)) -(define-syntax-class int-opt-expr - (pattern e:opt-expr - #:when (subtypeof #'e -Integer) - #:with opt #'e.opt)) - -;; if the result of an operation is of type float, its non float arguments -;; can be promoted, and we can use unsafe float operations -;; note: none of the unary operations have types where non-float arguments -;; can result in float (as opposed to real) results -(define-syntax-class float-arg-expr - (pattern e:fixnum-opt-expr - #:with opt #'(unsafe-fx->fl e.opt)) - (pattern e:int-opt-expr - #:with opt #'(->fl e.opt)) - (pattern e:float-opt-expr - #:with opt #'e.opt)) - -(define (mk-unsafe-tbl generic safe-pattern unsafe-pattern) - (for/fold ([h (make-immutable-free-id-table)]) ([g generic]) - (let ([f (format-id g safe-pattern g)] [u (format-id g unsafe-pattern g)]) - (dict-set (dict-set h g u) f u)))) - -(define (mk-float-tbl generic) - (mk-unsafe-tbl generic "fl~a" "unsafe-fl~a")) - -(define binary-float-ops - (mk-float-tbl (list #'+ #'- #'* #'/ #'min #'max))) -(define binary-float-comps - (dict-set - (dict-set - (mk-float-tbl (list #'= #'<= #'< #'> #'>=)) - ;; not a comparison, but takes 2 floats and does not return a float, - ;; unlike binary-float-ops - #'make-rectangular #'unsafe-make-flrectangular) - #'make-flrectangular #'unsafe-make-flrectangular)) -(define unary-float-ops - (mk-float-tbl (list #'abs #'sin #'cos #'tan #'asin #'acos #'atan #'log #'exp - #'sqrt #'round #'floor #'ceiling #'truncate))) - -(define-syntax-class (float-op tbl) - (pattern i:id - #:when (dict-ref tbl #'i #f) - #:with unsafe (dict-ref tbl #'i))) - -;; to generate temporary symbols in a predictable manner -;; these identifiers are unique within a sequence of unboxed operations -;; necessary to have predictable symbols to add in the hand-optimized versions -;; of the optimizer tests (which check for equality of expanded code) -(define *unboxed-gensym-counter* 0) -(define (unboxed-gensym) - (set! *unboxed-gensym-counter* (add1 *unboxed-gensym-counter*)) - (format-unique-id #'here "unboxed-gensym-~a" *unboxed-gensym-counter*)) - -(define-syntax-class inexact-complex-opt-expr - (pattern e:opt-expr - #:when (match (type-of #'e) - [(tc-result1: (== -InexactComplex type-equal?)) #t] [_ #f]) - - #:with opt #'e.opt)) -;; it's faster to take apart a complex number and use unsafe operations on -;; its parts than it is to use generic operations -;; we keep the real and imaginary parts unboxed as long as we stay within -;; complex operations -(define-syntax-class unboxed-inexact-complex-opt-expr - (pattern (#%plain-app (~and (~var op (float-op binary-inexact-complex-ops)) (~or (~literal +) (~literal -))) - c1:unboxed-inexact-complex-opt-expr - c2:unboxed-inexact-complex-opt-expr - cs:unboxed-inexact-complex-opt-expr ...) - #:with real-part (unboxed-gensym) - #:with imag-part (unboxed-gensym) - #:with (bindings ...) - (begin (log-optimization "unboxed binary inexact complex" #'op) - #`(#,@(append (syntax->list #'(c1.bindings ... c2.bindings ... cs.bindings ... ...)) - (list #`(real-part #,(for/fold ((o #'c1.real-part)) - ((e (syntax->list #'(c2.real-part cs.real-part ...)))) - #`(op.unsafe #,o #,e))) - #`(imag-part #,(for/fold ((o #'c1.imag-part)) - ((e (syntax->list #'(c2.imag-part cs.imag-part ...)))) - #`(op.unsafe #,o #,e)))))))) - (pattern (#%plain-app (~and op (~literal *)) - c1:unboxed-inexact-complex-opt-expr - c2:unboxed-inexact-complex-opt-expr - cs:unboxed-inexact-complex-opt-expr ...) - #:with real-part (unboxed-gensym) - #:with imag-part (unboxed-gensym) - #:with (bindings ...) - (begin (log-optimization "unboxed binary inexact complex" #'op) - #`(c1.bindings ... c2.bindings ... cs.bindings ... ... - ;; we want to bind the intermediate results to reuse them - ;; the final results are bound to real-part and imag-part - #,@(let loop ([o1 #'c1.real-part] - [o2 #'c1.imag-part] - [e1 (syntax->list #'(c2.real-part cs.real-part ...))] - [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))] - [rs (append (map (lambda (x) (unboxed-gensym)) - (syntax->list #'(cs.real-part ...))) - (list #'real-part))] - [is (append (map (lambda (x) (unboxed-gensym)) - (syntax->list #'(cs.imag-part ...))) - (list #'imag-part))] - [res '()]) - (if (null? e1) - (reverse res) - (loop (car rs) (car is) (cdr e1) (cdr e2) (cdr rs) (cdr is) - ;; complex multiplication, imag part, then real part (reverse) - (list* #`(#,(car is) - (unsafe-fl+ (unsafe-fl* #,o2 #,(car e1)) - (unsafe-fl* #,o1 #,(car e2)))) - #`(#,(car rs) - (unsafe-fl- (unsafe-fl* #,o1 #,(car e1)) - (unsafe-fl* #,o2 #,(car e2)))) - res))))))) - (pattern (#%plain-app (~and op (~literal /)) - c1:unboxed-inexact-complex-opt-expr - c2:unboxed-inexact-complex-opt-expr - cs:unboxed-inexact-complex-opt-expr ...) - #:with real-part (unboxed-gensym) - #:with imag-part (unboxed-gensym) - #:with (denominators ...) - (for/list - ([e1 (syntax->list #'(c2.real-part cs.real-part ...))] - [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))]) - #`(#,(unboxed-gensym) (unsafe-fl+ (unsafe-fl* #,e1 #,e1) (unsafe-fl* #,e2 #,e2)))) - #:with (bindings ...) - (begin (log-optimization "unboxed binary inexact complex" #'op) - #`(c1.bindings ... c2.bindings ... cs.bindings ... ... denominators ... - ;; we want to bind the intermediate results to reuse them - ;; the final results are bound to real-part and imag-part - #,@(let loop ([o1 #'c1.real-part] - [o2 #'c1.imag-part] - [e1 (syntax->list #'(c2.real-part cs.real-part ...))] - [e2 (syntax->list #'(c2.imag-part cs.imag-part ...))] - [d (map (lambda (x) (car (syntax-e x))) - (syntax->list #'(denominators ...)))] - [rs (append (map (lambda (x) (unboxed-gensym)) - (syntax->list #'(cs.real-part ...))) - (list #'real-part))] - [is (append (map (lambda (x) (unboxed-gensym)) - (syntax->list #'(cs.imag-part ...))) - (list #'imag-part))] - [res '()]) - (if (null? e1) - (reverse res) - (loop (car rs) (car is) (cdr e1) (cdr e2) (cdr d) (cdr rs) (cdr is) - ;; complex division, imag part, then real part (reverse) - (list* #`(#,(car is) - (unsafe-fl/ (unsafe-fl- (unsafe-fl* #,o2 #,(car e1)) - (unsafe-fl* #,o1 #,(car e2))) - #,(car d))) - #`(#,(car rs) - (unsafe-fl/ (unsafe-fl+ (unsafe-fl* #,o1 #,(car e1)) - (unsafe-fl* #,o2 #,(car e2))) - #,(car d))) - res))))))) - (pattern e:opt-expr - ;; can't work on inexact reals, which are a subtype of inexact - ;; complexes, so this has to be equality - #:when (match (type-of #'e) - [(tc-result1: (== -InexactComplex type-equal?)) #t] [_ #f]) - #:with e* (unboxed-gensym) - #:with real-part (unboxed-gensym) - #:with imag-part (unboxed-gensym) - #:with (bindings ...) - #'((e* e.opt) - (real-part (unsafe-flreal-part e*)) - (imag-part (unsafe-flimag-part e*))))) - -(define-syntax-class inexact-complex-unary-op - (pattern (~or (~literal real-part) (~literal flreal-part)) #:with unsafe #'unsafe-flreal-part) - (pattern (~or (~literal imag-part) (~literal flimag-part)) #:with unsafe #'unsafe-flimag-part)) -(define binary-inexact-complex-ops - (mk-float-tbl (list #'+ #'- #'* #'/))) - -(define-syntax-class fixnum-opt-expr - (pattern e:opt-expr - #:when (subtypeof #'e -Fixnum) - #:with opt #'e.opt)) -(define-syntax-class nonzero-fixnum-opt-expr - (pattern e:opt-expr - #:when (match (type-of #'e) - [(tc-result1: (== -PositiveFixnum type-equal?)) #t] - [(tc-result1: (== -NegativeFixnum type-equal?)) #t] - [_ #f]) - #:with opt #'e.opt)) - -(define (mk-fixnum-tbl generic) - (mk-unsafe-tbl generic "fx~a" "unsafe-fx~a")) - -;; due to undefined behavior when results are out of the fixnum range, only some -;; fixnum operations can be optimized -;; the following must be closed on fixnums -(define binary-fixnum-ops - (dict-set - (dict-set - (dict-set - (dict-set - (dict-set - (dict-set - (mk-fixnum-tbl (list #'= #'<= #'< #'> #'>= #'min #'max)) - #'bitwise-and #'unsafe-fxand) - #'fxand #'unsafe-fxand) - #'bitwise-ior #'unsafe-fxior) - #'fxior #'unsafe-fxior) - #'bitwise-xor #'unsafe-fxxor) - #'fxxor #'unsafe-fxxor)) -(define-syntax-class fixnum-unary-op - (pattern (~or (~literal bitwise-not) (~literal fxnot)) #:with unsafe #'unsafe-fxnot) - (pattern (~or (~literal abs) (~literal fxabs)) #:with unsafe #'unsafe-fxabs)) -;; closed on fixnums, but 2nd argument must not be 0 -(define-syntax-class nonzero-fixnum-binary-op - (pattern (~or (~literal quotient) (~literal fxquotient)) #:with unsafe #'unsafe-fxquotient) - (pattern (~or (~literal modulo) (~literal fxmodulo)) #:with unsafe #'unsafe-fxmodulo) - (pattern (~or (~literal remainder) (~literal fxremainder)) #:with unsafe #'unsafe-fxremainder)) - -(define-syntax-class (fixnum-op tbl) - (pattern i:id - #:when (dict-ref tbl #'i #f) - #:with unsafe (dict-ref tbl #'i))) +(provide optimize-top) (define-syntax-class pair-opt-expr @@ -272,39 +48,29 @@ (pattern e:opt-expr* #:with opt (syntax-recertify #'e.opt this-syntax (current-code-inspector) #f))) -(define *log-optimizations?* #f) -(define *log-optimizatons-to-log-file?* #f) -(define *optimization-log-file* "opt-log") -(define (log-optimization kind stx) - (if *log-optimizations?* - (printf "~a line ~a col ~a - ~a - ~a\n" - (syntax-source stx) (syntax-line stx) (syntax-column stx) - (syntax->datum stx) - kind) - #t)) - -;; unlike their safe counterparts, unsafe binary operators can only take 2 arguments -(define (n-ary->binary op arg1 arg2 rest) - (for/fold ([o arg1]) - ([e (syntax->list #`(#,arg2 #,@rest))]) - #`(#,op #,o #,e))) (define-syntax-class opt-expr* #:literal-sets (kernel-literals) ;; interesting cases, where something is optimized (pattern (~and res (#%plain-app (~var op (float-op unary-float-ops)) f:float-opt-expr)) - #:when (subtypeof #'res -Flonum) + #:when (subtypeof? #'res -Flonum) #:with opt (begin (log-optimization "unary float" #'op) #'(op.unsafe f.opt))) - (pattern (~and res (#%plain-app (~var op (float-op binary-float-ops)) f1:float-arg-expr f2:float-arg-expr fs:float-arg-expr ...)) + (pattern (~and res (#%plain-app (~var op (float-op binary-float-ops)) + f1:float-arg-expr + f2:float-arg-expr + fs:float-arg-expr ...)) ;; if the result is a float, we can coerce integers to floats and optimize - #:when (subtypeof #'res -Flonum) + #:when (subtypeof? #'res -Flonum) #:with opt (begin (log-optimization "binary float" #'op) (n-ary->binary #'op.unsafe #'f1.opt #'f2.opt #'(fs.opt ...)))) - (pattern (~and res (#%plain-app (~var op (float-op binary-float-comps)) f1:float-opt-expr f2:float-opt-expr fs:float-opt-expr ...)) + (pattern (~and res (#%plain-app (~var op (float-op binary-float-comps)) + f1:float-opt-expr + f2:float-opt-expr + fs:float-opt-expr ...)) #:with opt (begin (log-optimization "binary float comp" #'op) (n-ary->binary #'op.unsafe #'f1.opt #'f2.opt #'(fs.opt ...)))) @@ -313,11 +79,16 @@ #:with opt (begin (log-optimization "unary fixnum" #'op) #'(op.unsafe n.opt))) - (pattern (#%plain-app (~var op (fixnum-op binary-fixnum-ops)) n1:fixnum-opt-expr n2:fixnum-opt-expr ns:fixnum-opt-expr ...) + (pattern (#%plain-app (~var op (fixnum-op binary-fixnum-ops)) + n1:fixnum-opt-expr + n2:fixnum-opt-expr + ns:fixnum-opt-expr ...) #:with opt (begin (log-optimization "binary fixnum" #'op) (n-ary->binary #'op.unsafe #'n1.opt #'n2.opt #'(ns.opt ...)))) - (pattern (#%plain-app op:nonzero-fixnum-binary-op n1:fixnum-opt-expr n2:nonzero-fixnum-opt-expr) + (pattern (#%plain-app op:nonzero-fixnum-binary-op + n1:fixnum-opt-expr + n2:nonzero-fixnum-opt-expr) #:with opt (begin (log-optimization "binary nonzero fixnum" #'op) #'(op.unsafe n1.opt n2.opt))) @@ -326,11 +97,12 @@ #:with opt (begin (log-optimization "unary inexact complex" #'op) #'(op.unsafe n.opt))) - (pattern (~and exp (#%plain-app (~var op (float-op binary-inexact-complex-ops)) e:inexact-complex-opt-expr ...)) + (pattern (~and exp (#%plain-app (~var op (float-op binary-inexact-complex-ops)) + e:inexact-complex-opt-expr ...)) #:with exp*:unboxed-inexact-complex-opt-expr #'exp #:with opt (begin (log-optimization "unboxed inexact complex" #'exp) - (begin (set! *unboxed-gensym-counter* 0) + (begin (reset-unboxed-gensym) #'(let* (exp*.bindings ...) (unsafe-make-flrectangular exp*.real-part exp*.imag-part))))) @@ -438,17 +210,19 @@ (pattern other:expr #:with opt #'other)) -(define (optimize stx) +(define (optimize-top stx) (let ((port (if (and *log-optimizations?* *log-optimizatons-to-log-file?*) (open-output-file *optimization-log-file* #:exists 'append) (current-output-port)))) (begin0 - (parameterize ([current-output-port port]) - (syntax-parse stx #:literal-sets (kernel-literals) - [e:opt-expr - (syntax/loc stx e.opt)])) + (parameterize ([current-output-port port] + [optimize (lambda (stx) + (syntax-parse stx #:literal-sets (kernel-literals) + [e:opt-expr + (syntax/loc stx e.opt)]))]) + ((optimize) stx)) (if (and *log-optimizations?* *log-optimizatons-to-log-file?*) (close-output-port port) diff --git a/collects/typed-scheme/optimizer/utils.rkt b/collects/typed-scheme/optimizer/utils.rkt new file mode 100644 index 00000000..912b0184 --- /dev/null +++ b/collects/typed-scheme/optimizer/utils.rkt @@ -0,0 +1,64 @@ +#lang scheme/base + +(require unstable/match scheme/match + racket/dict syntax/id-table unstable/syntax + (for-template scheme/base scheme/flonum scheme/fixnum scheme/unsafe/ops) + "../utils/utils.rkt" + (types abbrev type-table utils subtype) + (rep type-rep)) + +(provide log-optimization *log-optimizations?* *log-optimizatons-to-log-file?* *optimization-log-file* + subtypeof? isoftype? + mk-unsafe-tbl + n-ary->binary + unboxed-gensym reset-unboxed-gensym + optimize) + + +(define *log-optimizations?* #f) +(define *log-optimizatons-to-log-file?* #f) +(define *optimization-log-file* "opt-log") +(define (log-optimization kind stx) + (if *log-optimizations?* + (printf "~a line ~a col ~a - ~a - ~a\n" + (syntax-source stx) (syntax-line stx) (syntax-column stx) + (syntax->datum stx) + kind) + #t)) + +;; is the syntax object s's type a subtype of t? +(define (subtypeof? s t) + (match (type-of s) + [(tc-result1: (== t (lambda (x y) (subtype y x)))) #t] [_ #f])) +;; similar, but with type equality +(define (isoftype? s t) + (match (type-of s) + [(tc-result1: (== t type-equal?)) #t] [_ #f])) + +;; generates a table matching safe to unsafe promitives +(define (mk-unsafe-tbl generic safe-pattern unsafe-pattern) + (for/fold ([h (make-immutable-free-id-table)]) ([g generic]) + (let ([f (format-id g safe-pattern g)] [u (format-id g unsafe-pattern g)]) + (dict-set (dict-set h g u) f u)))) + +;; unlike their safe counterparts, unsafe binary operators can only take 2 arguments +(define (n-ary->binary op arg1 arg2 rest) + (for/fold ([o arg1]) + ([e (syntax->list #`(#,arg2 #,@rest))]) + #`(#,op #,o #,e))) + +;; to generate temporary symbols in a predictable manner +;; these identifiers are unique within a sequence of unboxed operations +;; necessary to have predictable symbols to add in the hand-optimized versions +;; of the optimizer tests (which check for equality of expanded code) +(define *unboxed-gensym-counter* 0) +(define (unboxed-gensym) + (set! *unboxed-gensym-counter* (add1 *unboxed-gensym-counter*)) + (format-unique-id #'here "unboxed-gensym-~a" *unboxed-gensym-counter*)) +(define (reset-unboxed-gensym) + (set! *unboxed-gensym-counter* 0)) + +;; to avoid mutually recursive syntax classes +;; will be set to the actual optimization function at the entry point +;; of the optimizer +(define optimize (make-parameter #f)) diff --git a/collects/typed-scheme/typed-scheme.rkt b/collects/typed-scheme/typed-scheme.rkt index 9c246122..6063fdfa 100644 --- a/collects/typed-scheme/typed-scheme.rkt +++ b/collects/typed-scheme/typed-scheme.rkt @@ -44,7 +44,7 @@ [(optimized-body ...) ;; do we optimize? (if (optimize?) - (begin0 (map optimize (syntax->list #'transformed-body)) + (begin0 (map optimize-top (syntax->list #'transformed-body)) (do-time "Optimized")) #'transformed-body)]) ;; reconstruct the module with the extra code