456 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Factor
		
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Factor
		
	
	
| ! Copyright (C) 2005, 2007 Slava Pestov.
 | |
| ! See http://factorcode.org/license.txt for BSD license.
 | |
| IN: optimizer.math
 | |
| USING: alien arrays generic hashtables kernel assocs math
 | |
| math.private kernel.private sequences words parser
 | |
| inference.class inference.dataflow vectors strings sbufs io
 | |
| namespaces assocs quotations math.intervals sequences.private
 | |
| math.libm combinators splitting layouts math.parser classes
 | |
| generic.math optimizer.pattern-match optimizer.backend
 | |
| optimizer.def-use generic.standard ;
 | |
| 
 | |
| { + bignum+ float+ fixnum+fast } {
 | |
|     { { number 0 } [ drop ] }
 | |
|     { { 0 number } [ nip ] }
 | |
| } define-identities
 | |
| 
 | |
| { fixnum+ } {
 | |
|     { { number 0 } [ drop ] }
 | |
|     { { 0 number } [ nip ] }
 | |
| } define-identities
 | |
| 
 | |
| { - fixnum- bignum- float- fixnum-fast } {
 | |
|     { { number 0 } [ drop ] }
 | |
|     { { @ @ } [ 2drop 0 ] }
 | |
| } define-identities
 | |
| 
 | |
| { < fixnum< bignum< float< } {
 | |
|     { { @ @ } [ 2drop f ] }
 | |
| } define-identities
 | |
| 
 | |
| { <= fixnum<= bignum<= float<= } {
 | |
|     { { @ @ } [ 2drop t ] }
 | |
| } define-identities
 | |
| 
 | |
| { > fixnum> bignum> float>= } {
 | |
|     { { @ @ } [ 2drop f ] }
 | |
| } define-identities
 | |
| 
 | |
| { >= fixnum>= bignum>= float>= } {
 | |
|     { { @ @ } [ 2drop t ] }
 | |
| } define-identities
 | |
| 
 | |
| { * fixnum* bignum* float* } {
 | |
|     { { number 1 } [ drop ] }
 | |
|     { { 1 number } [ nip ] }
 | |
|     { { number 0 } [ nip ] }
 | |
|     { { 0 number } [ drop ] }
 | |
|     { { number -1 } [ drop 0 swap - ] }
 | |
|     { { -1 number } [ nip 0 swap - ] }
 | |
| } define-identities
 | |
| 
 | |
| { / fixnum/i bignum/i float/f } {
 | |
|     { { number 1 } [ drop ] }
 | |
|     { { number -1 } [ drop 0 swap - ] }
 | |
| } define-identities
 | |
| 
 | |
| { fixnum-mod bignum-mod } {
 | |
|     { { number 1 } [ 2drop 0 ] }
 | |
| } define-identities
 | |
| 
 | |
| { bitand fixnum-bitand bignum-bitand } {
 | |
|     { { number -1 } [ drop ] }
 | |
|     { { -1 number } [ nip ] }
 | |
|     { { @ @ } [ drop ] }
 | |
|     { { number 0 } [ nip ] }
 | |
|     { { 0 number } [ drop ] }
 | |
| } define-identities
 | |
| 
 | |
| { bitor fixnum-bitor bignum-bitor } {
 | |
|     { { number 0 } [ drop ] }
 | |
|     { { 0 number } [ nip ] }
 | |
|     { { @ @ } [ drop ] }
 | |
|     { { number -1 } [ nip ] }
 | |
|     { { -1 number } [ drop ] }
 | |
| } define-identities
 | |
| 
 | |
| { bitxor fixnum-bitxor bignum-bitxor } {
 | |
|     { { number 0 } [ drop ] }
 | |
|     { { 0 number } [ nip ] }
 | |
|     { { number -1 } [ drop bitnot ] }
 | |
|     { { -1 number } [ nip bitnot ] }
 | |
|     { { @ @ } [ 2drop 0 ] }
 | |
| } define-identities
 | |
| 
 | |
| { shift fixnum-shift bignum-shift } {
 | |
|     { { 0 number } [ drop ] }
 | |
|     { { number 0 } [ drop ] }
 | |
| } define-identities
 | |
| 
 | |
| : math-closure ( class -- newclass )
 | |
|     { fixnum integer rational real }
 | |
|     [ class< ] curry* find nip number or ;
 | |
| 
 | |
| : fits? ( interval class -- ? )
 | |
|     "interval" word-prop dup
 | |
|     [ interval-subset? ] [ 2drop t ] if ;
 | |
| 
 | |
| : math-output-class ( node min -- newclass )
 | |
|     #! if min is f, it means we just want to use the declared
 | |
|     #! output class from the "infer-effect".
 | |
|     dup [
 | |
|         swap node-in-d
 | |
|         [ value-class* math-closure math-class-max ] each
 | |
|     ] [
 | |
|         2drop f
 | |
|     ] if ;
 | |
| 
 | |
| : won't-overflow? ( interval node -- ? )
 | |
|     node-in-d [ value-class* fixnum class< ] all?
 | |
|     swap fixnum fits? and ;
 | |
| 
 | |
| : post-process ( class interval node -- classes intervals )
 | |
|     dupd won't-overflow?
 | |
|     [ >r dup { f integer } memq? [ drop fixnum ] when r> ] when
 | |
|     [ dup [ 1array ] when ] 2apply ;
 | |
| 
 | |
| : math-output-interval-1 ( node word -- interval )
 | |
|     dup [
 | |
|         >r node-in-d first value-interval* dup
 | |
|         [ r> execute ] [ r> 2drop f ] if
 | |
|     ] [
 | |
|         2drop f
 | |
|     ] if ; inline
 | |
| 
 | |
| : math-output-class/interval-1 ( node min word -- classes intervals )
 | |
|     pick >r
 | |
|     >r over r>
 | |
|     math-output-interval-1
 | |
|     >r math-output-class r>
 | |
|     r> post-process ; inline
 | |
| 
 | |
| {
 | |
|     { 1+ integer interval-1+ }
 | |
|     { 1- integer interval-1- }
 | |
|     { neg integer interval-neg }
 | |
|     { shift integer interval-recip }
 | |
|     { bitnot fixnum interval-bitnot }
 | |
|     { fixnum-bitnot f interval-bitnot }
 | |
|     { bignum-bitnot f interval-bitnot }
 | |
|     { 2/ fixnum interval-2/ }
 | |
|     { sq integer f }
 | |
| } [
 | |
|     first3 [
 | |
|         math-output-class/interval-1
 | |
|     ] 2curry "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| : intervals ( node -- i1 i2 )
 | |
|     node-in-d first2 [ value-interval* ] 2apply ;
 | |
| 
 | |
| : math-output-interval-2 ( node word -- interval )
 | |
|     dup [
 | |
|         >r intervals 2dup and [ r> execute ] [ r> 3drop f ] if
 | |
|     ] [
 | |
|         2drop f
 | |
|     ] if ; inline
 | |
| 
 | |
| : math-output-class/interval-2 ( node min word -- classes intervals )
 | |
|     pick >r
 | |
|     >r over r>
 | |
|     math-output-interval-2
 | |
|     >r math-output-class r>
 | |
|     r> post-process ; inline
 | |
| 
 | |
| {
 | |
|     { + integer interval+ }
 | |
|     { - integer interval- }
 | |
|     { * integer interval* }
 | |
|     { / rational interval/ }
 | |
|     { /i integer interval/i }
 | |
| 
 | |
|     { fixnum+ f interval+ }
 | |
|     { fixnum+fast f interval+ }
 | |
|     { fixnum- f interval- }
 | |
|     { fixnum-fast f interval- }
 | |
|     { fixnum* f interval* }
 | |
|     { fixnum*fast f interval* }
 | |
|     { fixnum/i f interval/i }
 | |
| 
 | |
|     { bignum+ f interval+ }
 | |
|     { bignum- f interval- }
 | |
|     { bignum* f interval* }
 | |
|     { bignum/i f interval/i }
 | |
|     { bignum-shift f interval-shift-safe }
 | |
| 
 | |
|     { float+ f interval+ }
 | |
|     { float- f interval- }
 | |
|     { float* f interval* }
 | |
|     { float/f f interval/ }
 | |
| 
 | |
|     { min fixnum interval-min }
 | |
|     { max fixnum interval-max }
 | |
| } [
 | |
|     first3 [
 | |
|         math-output-class/interval-2
 | |
|     ] 2curry "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| { fixnum-shift shift } [
 | |
|     [
 | |
|         dup
 | |
|         node-in-d second value-interval*
 | |
|         -1./0. 0 [a,b] interval-subset? fixnum integer ?
 | |
|         \ interval-shift-safe
 | |
|         math-output-class/interval-2
 | |
|     ] "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| : real-value? ( value -- n ? )
 | |
|     dup value? [ value-literal dup real? ] [ drop f f ] if ;
 | |
| 
 | |
| : mod-range ( n -- interval )
 | |
|     dup neg swap (a,b) ;
 | |
| 
 | |
| : rem-range ( n -- interval )
 | |
|     0 swap [a,b) ;
 | |
| 
 | |
| : bitand-range ( n -- interval )
 | |
|     dup 0 < [ drop f ] [ 0 swap [a,b] ] if ;
 | |
| 
 | |
| : math-output-interval-special ( node word -- interval )
 | |
|     dup [
 | |
|         >r node-in-d second real-value?
 | |
|         [ r> execute ] [ r> 2drop f ] if
 | |
|     ] [
 | |
|         2drop f
 | |
|     ] if ; inline
 | |
| 
 | |
| : math-output-class/interval-special ( node min word -- classes intervals )
 | |
|     pick >r
 | |
|     >r over r>
 | |
|     math-output-interval-special
 | |
|     >r math-output-class r>
 | |
|     r> post-process ; inline
 | |
| 
 | |
| {
 | |
|     { mod fixnum mod-range }
 | |
|     { fixnum-mod f mod-range }
 | |
|     { bignum-mod f mod-range }
 | |
|     { float-mod f mod-range }
 | |
| 
 | |
|     { rem integer rem-range }
 | |
| 
 | |
|     { bitand fixnum bitand-range }
 | |
|     { fixnum-bitand f bitand-range }
 | |
| 
 | |
|     { bitor fixnum f }
 | |
|     { bitxor fixnum f }
 | |
| } [
 | |
|     first3 [
 | |
|         math-output-class/interval-special
 | |
|     ] 2curry "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| : twiddle-interval ( i1 -- i2 )
 | |
|     dup [
 | |
|         node get node-in-d
 | |
|         [ value-class* integer class< ] all?
 | |
|         [ integral-closure ] when
 | |
|     ] when ;
 | |
| 
 | |
| : (comparison-constraints) ( i1 i2 word class -- )
 | |
|     node get [
 | |
|         >r execute twiddle-interval 0 `input interval,
 | |
|         r> 0 `output class,
 | |
|     ] set-constraints ; inline
 | |
| 
 | |
| : comparison-constraints ( node true false -- )
 | |
|     >r >r dup node set intervals dup [
 | |
|         2dup
 | |
|         r> general-t (comparison-constraints)
 | |
|         r> \ f (comparison-constraints)
 | |
|     ] [
 | |
|         r> r> 2drop 2drop
 | |
|     ] if ; inline
 | |
| 
 | |
| {
 | |
|     { < assume< assume>= }
 | |
|     { <= assume<= assume> }
 | |
|     { > assume> assume<= }
 | |
|     { >= assume>= assume< }
 | |
| 
 | |
|     { fixnum< assume< assume>= }
 | |
|     { fixnum<= assume<= assume> }
 | |
|     { fixnum> assume> assume<= }
 | |
|     { fixnum>= assume>= assume< }
 | |
| 
 | |
|     { bignum< assume< assume>= }
 | |
|     { bignum<= assume<= assume> }
 | |
|     { bignum> assume> assume<= }
 | |
|     { bignum>= assume>= assume< }
 | |
| 
 | |
|     { float< assume< assume>= }
 | |
|     { float<= assume<= assume> }
 | |
|     { float> assume> assume<= }
 | |
|     { float>= assume>= assume< }
 | |
| } [
 | |
|     first3
 | |
|     [
 | |
|         [ comparison-constraints ] with-scope
 | |
|     ] 2curry "constraints" set-word-prop
 | |
| ] each
 | |
| 
 | |
| {
 | |
|     alien-signed-1
 | |
|     alien-unsigned-1
 | |
|     alien-signed-2
 | |
|     alien-unsigned-2
 | |
|     alien-signed-4
 | |
|     alien-unsigned-4
 | |
|     alien-signed-8
 | |
|     alien-unsigned-8
 | |
| } [
 | |
|     dup word-name {
 | |
|         {
 | |
|             [ "alien-signed-" ?head ]
 | |
|             [ string>number 8 * 1- 2^ dup neg swap 1- [a,b] ]
 | |
|         }
 | |
|         {
 | |
|             [ "alien-unsigned-" ?head ]
 | |
|             [ string>number 8 * 2^ 1- 0 swap [a,b] ]
 | |
|         }
 | |
|     } cond 1array
 | |
|     [ nip f swap ] curry "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| ! Associate intervals to classes
 | |
| \ fixnum
 | |
| most-negative-fixnum most-positive-fixnum [a,b]
 | |
| "interval" set-word-prop
 | |
| 
 | |
| \ array-capacity
 | |
| 0 max-array-capacity [a,b]
 | |
| "interval" set-word-prop
 | |
| 
 | |
| {
 | |
|     { >fixnum fixnum }
 | |
|     { >bignum bignum }
 | |
|     { >float float }
 | |
| } [
 | |
|     [
 | |
|         over node-in-d first value-interval*
 | |
|         dup pick fits? [ drop f ] unless
 | |
|         rot post-process
 | |
|     ] curry "output-classes" set-word-prop
 | |
| ] assoc-each
 | |
| 
 | |
| ! Removing overflow checks
 | |
| : remove-overflow-check? ( #call -- ? )
 | |
|     dup node-out-d first node-class fixnum class< ;
 | |
| 
 | |
| {
 | |
|     { + [ fixnum+fast ] }
 | |
|     { - [ fixnum-fast ] }
 | |
|     { * [ fixnum*fast ] }
 | |
|     { fixnum+ [ fixnum+fast ] }
 | |
|     { fixnum- [ fixnum-fast ] }
 | |
|     { fixnum* [ fixnum*fast ] }
 | |
|     ! these are here as an optimization. if they weren't given
 | |
|     ! explicitly, the same would be inferred after an extra
 | |
|     ! optimization step (see optimistic-inline?)
 | |
|     { 1+ [ 1 fixnum+fast ] }
 | |
|     { 1- [ 1 fixnum-fast ] }
 | |
|     { 2/ [ -1 fixnum-shift ] }
 | |
|     { neg [ 0 swap fixnum-fast ] }
 | |
| } [
 | |
|     [
 | |
|         [ dup remove-overflow-check? ] ,
 | |
|         [ splice-quot ] curry ,
 | |
|     ] { } make 1array define-optimizers
 | |
| ] assoc-each
 | |
| 
 | |
| ! Remove redundant comparisons
 | |
| : known-comparison? ( #call -- ? )
 | |
|     dup dup node-in-d first node-interval
 | |
|     swap dup node-in-d second node-literal real? and ;
 | |
| 
 | |
| : perform-comparison ( #call word -- result )
 | |
|     >r dup dup node-in-d first node-interval
 | |
|     swap dup node-in-d second node-literal r> execute ; inline
 | |
| 
 | |
| : foldable-comparison? ( #call word -- )
 | |
|     >r dup known-comparison? [
 | |
|         r> perform-comparison incomparable eq? not
 | |
|     ] [
 | |
|         r> 2drop f
 | |
|     ] if ; inline
 | |
| 
 | |
| : fold-comparison ( #call word -- node )
 | |
|     dupd perform-comparison 1array inline-literals ;
 | |
| 
 | |
| {
 | |
|     { < interval< }
 | |
|     { <= interval<= }
 | |
|     { > interval> }
 | |
|     { >= interval>= }
 | |
| 
 | |
|     { fixnum< interval< }
 | |
|     { fixnum<= interval<= }
 | |
|     { fixnum> interval> }
 | |
|     { fixnum>= interval>= }
 | |
| 
 | |
|     { bignum< interval< }
 | |
|     { bignum<= interval<= }
 | |
|     { bignum> interval> }
 | |
|     { bignum>= interval>= }
 | |
| 
 | |
|     { float< interval< }
 | |
|     { float<= interval<= }
 | |
|     { float> interval> }
 | |
|     { float>= interval>= }
 | |
| } [
 | |
|     [
 | |
|         dup [ dupd foldable-comparison? ] curry ,
 | |
|         [ fold-comparison ] curry ,
 | |
|     ] { } make 1array define-optimizers
 | |
| ] assoc-each
 | |
| 
 | |
| ! The following words are handled in a similar way except if
 | |
| ! the only consumer is a >fixnum we remove the overflow check
 | |
| ! too
 | |
| : consumed-by? ( node word -- ? )
 | |
|     swap sole-consumer
 | |
|     dup #call? [ node-param eq? ] [ 2drop f ] if ;
 | |
| 
 | |
| : coereced-to-fixnum? ( #call -- ? )
 | |
|     \ >fixnum consumed-by? ;
 | |
| 
 | |
| {
 | |
|     { fixnum+ [ fixnum+fast ] }
 | |
|     { fixnum- [ fixnum-fast ] }
 | |
|     { fixnum* [ fixnum*fast ] }
 | |
| } [
 | |
|     [
 | |
|         [
 | |
|             dup remove-overflow-check?
 | |
|             over coereced-to-fixnum? or
 | |
|         ] ,
 | |
|         [ splice-quot ] curry ,
 | |
|     ] { } make 1array define-optimizers
 | |
| ] assoc-each
 | |
| 
 | |
| ! This will go away when we have cross-word type inference
 | |
| {
 | |
|     facos fasin fatan
 | |
|     fcos fexp fcosh flog fpow
 | |
|     fsin fsinh fsqrt
 | |
| } [
 | |
|     [ drop { float } f ]
 | |
|     "output-classes" set-word-prop
 | |
| ] each
 | |
| 
 | |
| \ fatan2
 | |
| [ drop { float float } f ]
 | |
| "output-classes" set-word-prop
 |