250 lines
6.5 KiB
Factor
Executable File
250 lines
6.5 KiB
Factor
Executable File
! Copyright (C) 2004, 2009 Slava Pestov.
|
|
! See http://factorcode.org/license.txt for BSD license.
|
|
USING: accessors arrays assocs combinators hashtables kernel
|
|
math fry namespaces make sequences words byte-arrays
|
|
layouts alien.c-types alien.structs
|
|
stack-checker.inlining cpu.architecture
|
|
compiler.tree
|
|
compiler.tree.builder
|
|
compiler.tree.combinators
|
|
compiler.tree.propagation.info
|
|
compiler.cfg
|
|
compiler.cfg.hats
|
|
compiler.cfg.utilities
|
|
compiler.cfg.registers
|
|
compiler.cfg.intrinsics
|
|
compiler.cfg.comparisons
|
|
compiler.cfg.stack-frame
|
|
compiler.cfg.instructions
|
|
compiler.cfg.predecessors
|
|
compiler.cfg.builder.blocks
|
|
compiler.cfg.stacks
|
|
compiler.cfg.stacks.local
|
|
compiler.alien ;
|
|
IN: compiler.cfg.builder
|
|
|
|
! Convert tree SSA IR to CFG IR. The result is not in SSA form; this is
|
|
! constructed later by calling compiler.cfg.ssa.construction:construct-ssa.
|
|
|
|
SYMBOL: procedures
|
|
SYMBOL: loops
|
|
|
|
: begin-cfg ( word label -- cfg )
|
|
initial-basic-block
|
|
H{ } clone loops set
|
|
[ basic-block get ] 2dip <cfg> dup cfg set ;
|
|
|
|
: begin-procedure ( word label -- )
|
|
begin-cfg procedures get push ;
|
|
|
|
: with-cfg-builder ( nodes word label quot -- )
|
|
'[
|
|
begin-stack-analysis
|
|
begin-procedure
|
|
@
|
|
end-stack-analysis
|
|
] with-scope ; inline
|
|
|
|
GENERIC: emit-node ( node -- )
|
|
|
|
: emit-nodes ( nodes -- )
|
|
[ basic-block get [ emit-node ] [ drop ] if ] each ;
|
|
|
|
: begin-word ( -- )
|
|
##prologue
|
|
##branch
|
|
begin-basic-block ;
|
|
|
|
: (build-cfg) ( nodes word label -- )
|
|
[
|
|
begin-word
|
|
emit-nodes
|
|
] with-cfg-builder ;
|
|
|
|
: build-cfg ( nodes word -- procedures )
|
|
V{ } clone [
|
|
procedures [
|
|
dup (build-cfg)
|
|
] with-variable
|
|
] keep ;
|
|
|
|
: emit-loop-call ( basic-block -- )
|
|
##branch
|
|
basic-block get successors>> push
|
|
end-basic-block ;
|
|
|
|
: emit-call ( word height -- )
|
|
over loops get key?
|
|
[ drop loops get at emit-loop-call ]
|
|
[ [ [ ##call ] [ adjust-d ] bi* ] emit-trivial-block ]
|
|
if ;
|
|
|
|
! #recursive
|
|
: recursive-height ( #recursive -- n )
|
|
[ label>> return>> in-d>> length ] [ in-d>> length ] bi - ;
|
|
|
|
: emit-recursive ( #recursive -- )
|
|
[ [ label>> id>> ] [ recursive-height ] bi emit-call ]
|
|
[ [ child>> ] [ label>> word>> ] [ label>> id>> ] tri (build-cfg) ] bi ;
|
|
|
|
: remember-loop ( label -- )
|
|
basic-block get swap loops get set-at ;
|
|
|
|
: emit-loop ( node -- )
|
|
##branch
|
|
begin-basic-block
|
|
[ label>> id>> remember-loop ] [ child>> emit-nodes ] bi ;
|
|
|
|
M: #recursive emit-node
|
|
dup label>> loop?>> [ emit-loop ] [ emit-recursive ] if ;
|
|
|
|
! #if
|
|
: emit-branch ( obj -- final-bb )
|
|
[ emit-nodes ] with-branch ;
|
|
|
|
: emit-if ( node -- )
|
|
children>> [ emit-branch ] map emit-conditional ;
|
|
|
|
: trivial-branch? ( nodes -- value ? )
|
|
dup length 1 = [
|
|
first dup #push? [ literal>> t ] [ drop f f ] if
|
|
] [ drop f f ] if ;
|
|
|
|
: trivial-if? ( #if -- ? )
|
|
children>> first2
|
|
[ trivial-branch? [ t eq? ] when ]
|
|
[ trivial-branch? [ f eq? ] when ] bi*
|
|
and ;
|
|
|
|
: emit-trivial-if ( -- )
|
|
ds-pop \ f tag-number cc/= ^^compare-imm ds-push ;
|
|
|
|
: trivial-not-if? ( #if -- ? )
|
|
children>> first2
|
|
[ trivial-branch? [ f eq? ] when ]
|
|
[ trivial-branch? [ t eq? ] when ] bi*
|
|
and ;
|
|
|
|
: emit-trivial-not-if ( -- )
|
|
ds-pop \ f tag-number cc= ^^compare-imm ds-push ;
|
|
|
|
: emit-actual-if ( #if -- )
|
|
! Inputs to the final instruction need to be copied because of
|
|
! loc>vreg sync
|
|
ds-pop ^^copy \ f tag-number cc/= ##compare-imm-branch emit-if ;
|
|
|
|
M: #if emit-node
|
|
{
|
|
{ [ dup trivial-if? ] [ drop emit-trivial-if ] }
|
|
{ [ dup trivial-not-if? ] [ drop emit-trivial-not-if ] }
|
|
[ emit-actual-if ]
|
|
} cond ;
|
|
|
|
! #dispatch
|
|
M: #dispatch emit-node
|
|
! Inputs to the final instruction need to be copied because of
|
|
! loc>vreg sync. ^^offset>slot always returns a fresh vreg,
|
|
! though.
|
|
ds-pop ^^offset>slot next-vreg ##dispatch emit-if ;
|
|
|
|
! #call
|
|
M: #call emit-node
|
|
dup word>> dup "intrinsic" word-prop
|
|
[ emit-intrinsic ] [ swap call-height emit-call ] if ;
|
|
|
|
! #call-recursive
|
|
M: #call-recursive emit-node [ label>> id>> ] [ call-height ] bi emit-call ;
|
|
|
|
! #push
|
|
M: #push emit-node
|
|
literal>> ^^load-literal ds-push ;
|
|
|
|
! #shuffle
|
|
|
|
! Even though low level IR has its own dead code elimination pass,
|
|
! we try not to introduce useless ##peeks here, since this reduces
|
|
! the accuracy of global stack analysis.
|
|
|
|
: make-input-map ( #shuffle -- assoc )
|
|
! Assoc maps high-level IR values to stack locations.
|
|
[
|
|
[ in-d>> <reversed> [ <ds-loc> swap set ] each-index ]
|
|
[ in-r>> <reversed> [ <rs-loc> swap set ] each-index ] bi
|
|
] H{ } make-assoc ;
|
|
|
|
: make-output-seq ( values mapping input-map -- vregs )
|
|
'[ _ at _ at peek-loc ] map ;
|
|
|
|
: load-shuffle ( #shuffle mapping input-map -- ds-vregs rs-vregs )
|
|
[ [ out-d>> ] 2dip make-output-seq ]
|
|
[ [ out-r>> ] 2dip make-output-seq ] 3bi ;
|
|
|
|
: store-shuffle ( #shuffle ds-vregs rs-vregs -- )
|
|
[ [ in-d>> length neg inc-d ] dip ds-store ]
|
|
[ [ in-r>> length neg inc-r ] dip rs-store ]
|
|
bi-curry* bi ;
|
|
|
|
M: #shuffle emit-node
|
|
dup dup [ mapping>> ] [ make-input-map ] bi load-shuffle store-shuffle ;
|
|
|
|
! #return
|
|
: emit-return ( -- )
|
|
##branch begin-basic-block ##epilogue ##return ;
|
|
|
|
M: #return emit-node drop emit-return ;
|
|
|
|
M: #return-recursive emit-node
|
|
label>> id>> loops get key? [ emit-return ] unless ;
|
|
|
|
! #terminate
|
|
M: #terminate emit-node drop ##no-tco end-basic-block ;
|
|
|
|
! FFI
|
|
: return-size ( ctype -- n )
|
|
#! Amount of space we reserve for a return value.
|
|
{
|
|
{ [ dup c-struct? not ] [ drop 0 ] }
|
|
{ [ dup large-struct? not ] [ drop 2 cells ] }
|
|
[ heap-size ]
|
|
} cond ;
|
|
|
|
: <alien-stack-frame> ( params -- stack-frame )
|
|
stack-frame new
|
|
swap
|
|
[ return>> return-size >>return ]
|
|
[ alien-parameters parameter-sizes drop >>params ] bi ;
|
|
|
|
: alien-node-height ( params -- )
|
|
[ out-d>> length ] [ in-d>> length ] bi - adjust-d ;
|
|
|
|
: emit-alien-node ( node quot -- )
|
|
[
|
|
[ params>> dup dup <alien-stack-frame> ] dip call
|
|
alien-node-height
|
|
] emit-trivial-block ; inline
|
|
|
|
M: #alien-invoke emit-node
|
|
[ ##alien-invoke ] emit-alien-node ;
|
|
|
|
M: #alien-indirect emit-node
|
|
[ ##alien-indirect ] emit-alien-node ;
|
|
|
|
M: #alien-callback emit-node
|
|
dup params>> xt>> dup
|
|
[
|
|
##prologue
|
|
dup [ ##alien-callback ] emit-alien-node
|
|
##epilogue
|
|
params>> ##callback-return
|
|
] with-cfg-builder ;
|
|
|
|
! No-op nodes
|
|
M: #introduce emit-node drop ;
|
|
|
|
M: #copy emit-node drop ;
|
|
|
|
M: #enter-recursive emit-node drop ;
|
|
|
|
M: #phi emit-node drop ;
|
|
|
|
M: #declare emit-node drop ; |