! Copyright (C) 2010 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors combinators combinators.short-circuit compiler.cfg.instructions compiler.cfg.registers compiler.cfg.utilities compiler.cfg.value-numbering.folding compiler.cfg.value-numbering.graph compiler.cfg.value-numbering.rewrite cpu.architecture fry kernel layouts locals make math ; IN: compiler.cfg.value-numbering.math : f-insn? ( insn -- ? ) { [ load-reference##? ] [ obj>> not ] } 1&& ; inline : zero-insn? ( insn -- ? ) { [ load-integer##? ] [ val>> 0 = ] } 1&& ; inline M: tagged>integer## rewrite [ dst>> ] [ src>> vreg>insn ] bi { { [ dup load-integer##? ] [ val>> tag-fixnum load-integer## new-insn ] } { [ dup f-insn? ] [ drop \ f type-number load-integer## new-insn ] } [ 2drop f ] } cond ; : self-inverse ( insn -- insn' ) [ dst>> ] [ src>> vreg>insn src>> ] bi ; : identity ( insn -- insn' ) [ dst>> ] [ src1>> ] bi ; M: neg## rewrite { { [ dup src>> vreg>insn neg##? ] [ self-inverse ] } { [ dup unary-constant-fold? ] [ unary-constant-fold ] } [ drop f ] } cond ; M: not## rewrite { { [ dup src>> vreg>insn not##? ] [ self-inverse ] } { [ dup unary-constant-fold? ] [ unary-constant-fold ] } [ drop f ] } cond ; ! Reassociation converts ! ## *-imm 2 1 X ! ## *-imm 3 2 Y ! into ! ## *-imm 3 1 (X $ Y) ! If * is associative, then $ is the same operation as *. ! In the case of shifts, $ is addition. : (reassociate) ( insn -- dst src1 src2' src2'' ) { [ dst>> ] [ src1>> vreg>insn [ src1>> ] [ src2>> ] bi ] [ src2>> ] } cleave ; inline : reassociate ( insn -- dst src1 src2 ) [ (reassociate) ] keep binary-constant-fold* ; : ?new-insn ( dst src1 src2 ? class -- insn/f ) '[ _ new-insn ] [ 3drop f ] if ; inline : reassociate-arithmetic ( insn new-insn -- insn/f ) [ reassociate dup immediate-arithmetic? ] dip ?new-insn ; inline : reassociate-bitwise ( insn new-insn -- insn/f ) [ reassociate dup immediate-bitwise? ] dip ?new-insn ; inline : reassociate-shift ( insn new-insn -- insn/f ) [ (reassociate) + dup immediate-shift-count? ] dip ?new-insn ; inline M: add-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn add-imm##? ] [ add-imm## reassociate-arithmetic ] } [ drop f ] } cond ; : sub-imm>add-imm ( insn -- insn' ) [ dst>> ] [ src1>> ] [ src2>> neg ] tri dup immediate-arithmetic? add-imm## ?new-insn ; M: sub-imm## rewrite sub-imm>add-imm ; ! Convert mul-imm## -1 => neg## : mul-to-neg? ( insn -- ? ) src2>> -1 = ; : mul-to-neg ( insn -- insn' ) [ dst>> ] [ src1>> ] bi neg## new-insn ; ! Convert mul-imm## 2^X => shl-imm## X : mul-to-shl? ( insn -- ? ) src2>> power-of-2? ; : mul-to-shl ( insn -- insn' ) [ [ dst>> ] [ src1>> ] bi ] [ src2>> log2 ] bi shl-imm## new-insn ; ! Distribution converts ! ##+-imm 2 1 X ! ##*-imm 3 2 Y ! Into ! ##*-imm 4 1 Y ! ##+-imm 3 4 X*Y ! Where * is mul or shl, + is add or sub ! Have to make sure that X*Y fits in an immediate :: (distribute) ( outer inner imm temp add-op mul-op -- new-outers/f ) imm immediate-arithmetic? [ [ temp inner src1>> outer src2>> mul-op execute outer dst>> temp imm add-op execute ] { } make ] [ f ] if ; inline : distribute-over-add? ( insn -- ? ) src1>> vreg>insn add-imm##? ; : distribute-over-sub? ( insn -- ? ) src1>> vreg>insn sub-imm##? ; : distribute ( insn add-op mul-op -- new-insns/f ) [ dup src1>> vreg>insn 2dup src2>> swap [ src2>> ] keep binary-constant-fold* next-vreg ] 2dip (distribute) ; inline M: mul-imm## rewrite { { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup mul-to-neg? ] [ mul-to-neg ] } { [ dup mul-to-shl? ] [ mul-to-shl ] } { [ dup src1>> vreg>insn mul-imm##? ] [ mul-imm## reassociate-arithmetic ] } { [ dup distribute-over-add? ] [ \ add-imm##, \ mul-imm##, distribute ] } { [ dup distribute-over-sub? ] [ \ sub-imm##, \ mul-imm##, distribute ] } [ drop f ] } cond ; M: and-imm## rewrite { { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn and-imm##? ] [ and-imm## reassociate-bitwise ] } { [ dup src2>> 0 = ] [ dst>> 0 load-integer## new-insn ] } { [ dup src2>> -1 = ] [ identity ] } [ drop f ] } cond ; M: or-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup src2>> -1 = ] [ dst>> -1 load-integer## new-insn ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn or-imm##? ] [ or-imm## reassociate-bitwise ] } [ drop f ] } cond ; M: xor-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup src2>> -1 = ] [ [ dst>> ] [ src1>> ] bi not## new-insn ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn xor-imm##? ] [ xor-imm## reassociate-bitwise ] } [ drop f ] } cond ; M: shl-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn shl-imm##? ] [ shl-imm## reassociate-shift ] } { [ dup distribute-over-add? ] [ \ add-imm##, \ shl-imm##, distribute ] } { [ dup distribute-over-sub? ] [ \ sub-imm##, \ shl-imm##, distribute ] } [ drop f ] } cond ; M: shr-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn shr-imm##? ] [ shr-imm## reassociate-shift ] } [ drop f ] } cond ; M: sar-imm## rewrite { { [ dup src2>> 0 = ] [ identity ] } { [ dup binary-constant-fold? ] [ binary-constant-fold ] } { [ dup src1>> vreg>insn sar-imm##? ] [ sar-imm## reassociate-shift ] } [ drop f ] } cond ; ! Convert ! load-integer## 2 X ! ##* 3 1 2 ! Where * is an operation with an -imm equivalent into ! ##*-imm 3 1 X : insn>imm-insn ( insn op swap? -- new-insn ) swap [ [ [ dst>> ] [ src1>> ] [ src2>> ] tri ] dip [ swap ] when vreg>integer ] dip new-insn ; inline M: add## rewrite { { [ dup src2>> vreg-immediate-arithmetic? ] [ add-imm## f insn>imm-insn ] } { [ dup src1>> vreg-immediate-arithmetic? ] [ add-imm## t insn>imm-insn ] } [ drop f ] } cond ; : diagonal? ( insn -- ? ) [ src1>> vreg>vn ] [ src2>> vreg>vn ] bi = ; inline ! sub## 2 1 1 => load-integer## 2 0 : rewrite-subtraction-identity ( insn -- insn' ) dst>> 0 load-integer## new-insn ; ! load-integer## 1 0 ! sub## 3 1 2 ! => ! neg## 3 2 : sub-to-neg? ( sub## -- ? ) src1>> vreg>insn zero-insn? ; : sub-to-neg ( sub## -- insn ) [ dst>> ] [ src2>> ] bi neg## new-insn ; M: sub## rewrite { { [ dup sub-to-neg? ] [ sub-to-neg ] } { [ dup diagonal? ] [ rewrite-subtraction-identity ] } { [ dup src2>> vreg-immediate-arithmetic? ] [ sub-imm## f insn>imm-insn ] } [ drop f ] } cond ; M: mul## rewrite { { [ dup src2>> vreg-immediate-arithmetic? ] [ mul-imm## f insn>imm-insn ] } { [ dup src1>> vreg-immediate-arithmetic? ] [ mul-imm## t insn>imm-insn ] } [ drop f ] } cond ; M: and## rewrite { { [ dup diagonal? ] [ identity ] } { [ dup src2>> vreg-immediate-bitwise? ] [ and-imm## f insn>imm-insn ] } { [ dup src1>> vreg-immediate-bitwise? ] [ and-imm## t insn>imm-insn ] } [ drop f ] } cond ; M: or## rewrite { { [ dup diagonal? ] [ identity ] } { [ dup src2>> vreg-immediate-bitwise? ] [ or-imm## f insn>imm-insn ] } { [ dup src1>> vreg-immediate-bitwise? ] [ or-imm## t insn>imm-insn ] } [ drop f ] } cond ; M: xor## rewrite { { [ dup diagonal? ] [ dst>> 0 load-integer## new-insn ] } { [ dup src2>> vreg-immediate-bitwise? ] [ xor-imm## f insn>imm-insn ] } { [ dup src1>> vreg-immediate-bitwise? ] [ xor-imm## t insn>imm-insn ] } [ drop f ] } cond ; M: shl## rewrite { { [ dup src2>> vreg-immediate-bitwise? ] [ shl-imm## f insn>imm-insn ] } [ drop f ] } cond ; M: shr## rewrite { { [ dup src2>> vreg-immediate-bitwise? ] [ shr-imm## f insn>imm-insn ] } [ drop f ] } cond ; M: sar## rewrite { { [ dup src2>> vreg-immediate-bitwise? ] [ sar-imm## f insn>imm-insn ] } [ drop f ] } cond ;