! Copyright (C) 2004, 2005 Slava Pestov. ! See http://factor.sf.net/license.txt for BSD license. IN: compiler USING: inference kernel kernel-internals lists namespaces sequences vectors words words ; ! The optimizer transforms dataflow IR to dataflow IR. Currently ! it removes literals that are eventually dropped, and never ! arise as inputs to any other type of function. Such 'dead' ! literals arise when combinators are inlined and quotations are ! lifted to their call sites. Also, #label nodes are inlined if ! their children do not make a recursive call to the label. : scan-literal ( node -- ) #! If the node represents a literal push, add the literal to #! the list being constructed. "scan-literal" [ drop ] apply-dataflow ; : (scan-literals) ( dataflow -- ) [ scan-literal ] each ; : scan-literals ( dataflow -- list ) [ (scan-literals) ] make-list ; : scan-branches ( branches -- ) #! Collect all literals from all branches. [ node-param get ] bind [ [ scan-literal ] each ] each ; : mentions-literal? ( literal list -- ? ) #! Does the given list of result objects refer to this #! literal? [ value= ] some-with? ; : consumes-literal? ( literal node -- ? ) #! Does the dataflow node consume the literal? [ dup node-consume-d get mentions-literal? swap dup node-consume-r get mentions-literal? nip or ] bind ; : produces-literal? ( literal node -- ? ) #! Does the dataflow node produce the literal? [ dup node-produce-d get mentions-literal? swap dup node-produce-r get mentions-literal? nip or ] bind ; : (can-kill?) ( literal node -- ? ) #! Return false if the literal appears as input to this #! node, and this node is not a stack operation. 2dup consumes-literal? >r produces-literal? r> or not ; : can-kill? ( literal dataflow -- ? ) #! Return false if the literal appears in any node in the #! list. [ dupd "can-kill" [ (can-kill?) ] apply-dataflow ] all? nip ; : kill-set ( dataflow -- list ) #! Push a list of literals that may be killed in the IR. dup scan-literals [ over can-kill? ] subset nip ; SYMBOL: branch-returns : can-kill-branches? ( literal node -- ? ) #! Check if the literal appears in either branch. This #! assumes that the last element of each branch is a #values #! node. 2dup consumes-literal? [ 2drop f ] [ [ node-param get ] bind [ dup [ peek [ node-consume-d get >vector ] bind ] map unify-stacks >list branch-returns set [ dupd can-kill? ] all? nip ] with-scope ] ifte ; : kill-node ( literals node -- ) swap [ over (can-kill?) ] all? [ , ] [ drop ] ifte ; : (kill-nodes) ( literals dataflow -- ) #! Append live nodes to currently constructing list. [ "kill-node" [ nip , ] apply-dataflow ] each-with ; : kill-nodes ( literals dataflow -- dataflow ) #! Remove literals and construct a list. [ (kill-nodes) ] make-list ; : optimize ( dataflow -- dataflow ) #! Remove redundant literals from the IR. The original IR #! is destructively modified. dup kill-set swap kill-nodes ; : kill-branches ( literals node -- ) [ node-param [ [ dupd kill-nodes ] map nip ] change ] extend , ; #push [ [ node-param get ] bind , ] "scan-literal" set-word-prop #push [ consumes-literal? not ] "can-kill" set-word-prop #push [ kill-node ] "kill-node" set-word-prop #label [ [ node-param get ] bind (scan-literals) ] "scan-literal" set-word-prop #label [ [ node-param get ] bind can-kill? ] "can-kill" set-word-prop #call-label [ [ node-param get ] bind = ] "calls-label" set-word-prop : calls-label? ( label list -- ? ) [ "calls-label" [ 2drop f ] apply-dataflow ] some-with? ; #label [ [ node-param get ] bind calls-label? ] "calls-label" set-word-prop #simple-label [ [ node-param get ] bind calls-label? ] "calls-label" set-word-prop : branches-call-label? ( label list -- ? ) [ calls-label? ] some-with? ; \ ifte [ [ node-param get ] bind branches-call-label? ] "calls-label" set-word-prop \ dispatch [ [ node-param get ] bind branches-call-label? ] "calls-label" set-word-prop : optimize-label ( -- op ) #! Does the label node contain calls to itself? node-label get node-param get calls-label? #label #simple-label ? ; #label [ ( literals node -- ) [ optimize-label node-op set node-param [ kill-nodes ] change ] extend , ] "kill-node" set-word-prop #values [ dupd consumes-literal? [ branch-returns get mentions-literal? ] [ drop t ] ifte ] "can-kill" set-word-prop \ ifte [ scan-branches ] "scan-literal" set-word-prop \ ifte [ can-kill-branches? ] "can-kill" set-word-prop \ ifte [ kill-branches ] "kill-node" set-word-prop \ dispatch [ scan-branches ] "scan-literal" set-word-prop \ dispatch [ can-kill-branches? ] "can-kill" set-word-prop \ dispatch [ kill-branches ] "kill-node" set-word-prop ! Don't care about inputs to recursive combinator calls #call-label [ 2drop t ] "can-kill" set-word-prop \ drop [ 2drop t ] "can-kill" set-word-prop \ drop [ kill-node ] "kill-node" set-word-prop \ dup [ 2drop t ] "can-kill" set-word-prop \ dup [ kill-node ] "kill-node" set-word-prop \ swap [ 2drop t ] "can-kill" set-word-prop \ swap [ kill-node ] "kill-node" set-word-prop : kill-mask ( killing inputs -- mask ) [ over [ over value= ] some? >boolean nip ] map nip ; : reduce-stack-op ( literals node map -- ) #! If certain values passing through a stack op are being #! killed, the stack op can be reduced, in extreme cases #! to a no-op. -rot [ [ node-consume-d get ] bind kill-mask swap assoc ] keep over [ [ node-op set ] extend , ] [ 2drop ] ifte ; \ over [ 2drop t ] "can-kill" set-word-prop \ over [ [ [[ [ f f ] over ]] [[ [ f t ] dup ]] ] reduce-stack-op ] "kill-node" set-word-prop \ pick [ 2drop t ] "can-kill" set-word-prop \ pick [ [ [[ [ f f f ] pick ]] [[ [ f f t ] over ]] [[ [ f t f ] over ]] [[ [ f t t ] dup ]] ] reduce-stack-op ] "kill-node" set-word-prop \ >r [ 2drop t ] "can-kill" set-word-prop \ >r [ kill-node ] "kill-node" set-word-prop \ r> [ 2drop t ] "can-kill" set-word-prop \ r> [ kill-node ] "kill-node" set-word-prop