! Copyright (C) 2004, 2008 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.stacks compiler.cfg.iterator compiler.cfg.utilities compiler.cfg.registers compiler.cfg.intrinsics compiler.cfg.instructions compiler.alien ; IN: compiler.cfg.builder ! Convert tree SSA IR to CFG SSA IR. : stop-iterating ( -- next ) end-basic-block f ; SYMBOL: procedures SYMBOL: current-word SYMBOL: current-label SYMBOL: loops SYMBOL: first-basic-block ! Basic block after prologue, makes recursion faster SYMBOL: current-label-start : add-procedure ( -- ) basic-block get current-word get current-label get procedures get push ; : begin-procedure ( word label -- ) end-basic-block begin-basic-block H{ } clone loops set current-label set current-word set add-procedure ; : with-cfg-builder ( nodes word label quot -- ) '[ begin-procedure @ ] with-scope ; inline GENERIC: emit-node ( node -- next ) : check-basic-block ( node -- node' ) basic-block get [ drop f ] unless ; inline : emit-nodes ( nodes -- ) [ current-node emit-node check-basic-block ] iterate-nodes ; : begin-word ( -- ) #! We store the basic block after the prologue as a loop #! labelled by the current word, so that self-recursive #! calls can skip an epilogue/prologue. ##prologue ##branch begin-basic-block basic-block get first-basic-block set ; : (build-cfg) ( nodes word label -- ) [ begin-word V{ } clone node-stack set emit-nodes ] with-cfg-builder ; : build-cfg ( nodes word -- procedures ) V{ } clone [ procedures [ dup (build-cfg) ] with-variable ] keep ; : local-recursive-call ( basic-block -- next ) ##branch basic-block get successors>> push stop-iterating ; : emit-call ( word -- next ) { { [ dup loops get key? ] [ loops get at local-recursive-call ] } { [ tail-call? not ] [ ##call ##branch begin-basic-block iterate-next ] } { [ dup current-label get eq? ] [ drop first-basic-block get local-recursive-call ] } [ ##epilogue ##jump stop-iterating ] } cond ; ! #recursive : compile-recursive ( node -- next ) [ label>> id>> emit-call ] [ [ child>> ] [ label>> word>> ] [ label>> id>> ] tri (build-cfg) ] bi ; : remember-loop ( label -- ) basic-block get swap loops get set-at ; : compile-loop ( node -- next ) ##loop-entry begin-basic-block [ label>> id>> remember-loop ] [ child>> emit-nodes ] bi iterate-next ; M: #recursive emit-node dup label>> loop?>> [ compile-loop ] [ compile-recursive ] if ; ! #if : emit-branch ( obj -- final-bb ) [ begin-basic-block emit-nodes basic-block get dup [ ##branch ] when ] with-scope ; : emit-if ( node -- ) children>> [ emit-branch ] map end-basic-block begin-basic-block basic-block get '[ [ _ swap successors>> push ] when* ] each ; : ##branch-t ( vreg -- ) \ f tag-number cc/= ##compare-imm-branch ; M: #if emit-node ds-pop ##branch-t emit-if iterate-next ; ! #dispatch : dispatch-branch ( nodes word -- label ) gensym [ [ V{ } clone node-stack set ##prologue emit-nodes basic-block get [ ##epilogue ##return end-basic-block ] when ] with-cfg-builder ] keep ; : dispatch-branches ( node -- ) children>> [ current-word get dispatch-branch ##dispatch-label ] each ; : emit-dispatch ( node -- ) ##epilogue ds-pop ^^offset>slot i ##dispatch dispatch-branches ; : ( -- word ) gensym dup t "inlined-block" set-word-prop ; M: #dispatch emit-node tail-call? [ emit-dispatch stop-iterating ] [ current-word get [ [ begin-word emit-dispatch ] with-cfg-builder ] keep emit-call ] if ; ! #call M: #call emit-node dup word>> dup "intrinsic" word-prop [ emit-intrinsic iterate-next ] [ nip emit-call ] if ; ! #call-recursive M: #call-recursive emit-node label>> id>> emit-call ; ! #push M: #push emit-node literal>> ^^load-literal ds-push iterate-next ; ! #shuffle : emit-shuffle ( effect -- ) [ out>> ] [ in>> dup length ds-load zip ] bi '[ _ at ] map ds-store ; M: #shuffle emit-node shuffle-effect emit-shuffle iterate-next ; M: #>r emit-node [ in-d>> length ] [ out-r>> empty? ] bi [ neg ##inc-d ] [ ds-load rs-store ] if iterate-next ; M: #r> emit-node [ in-r>> length ] [ out-d>> empty? ] bi [ neg ##inc-r ] [ rs-load ds-store ] if iterate-next ; ! #return M: #return emit-node drop ##epilogue ##return stop-iterating ; M: #return-recursive emit-node label>> id>> loops get key? [ iterate-next ] [ ##epilogue ##return stop-iterating ] if ; ! #terminate M: #terminate emit-node drop stop-iterating ; ! 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 ; : ( params -- stack-frame ) stack-frame new swap [ return>> return-size >>return ] [ alien-parameters parameter-sizes drop >>params ] bi ; : alien-stack-frame ( params -- ) ##stack-frame ; : emit-alien-node ( node quot -- next ) [ params>> ] dip [ drop alien-stack-frame ] [ call ] 2bi begin-basic-block iterate-next ; 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 drop ##epilogue params>> ##callback-return ] with-cfg-builder iterate-next ; ! No-op nodes M: #introduce emit-node drop iterate-next ; M: #copy emit-node drop iterate-next ; M: #enter-recursive emit-node drop iterate-next ; M: #phi emit-node drop iterate-next ;