new dataflow IR

cvs
Slava Pestov 2005-05-17 20:13:08 +00:00
parent 7418990bdc
commit ac34c06c0c
13 changed files with 478 additions and 555 deletions

View File

@ -6,8 +6,9 @@
<magnus--> http://www.caddr.com/macho/archives/sbcl-devel/2005-3/4764.html
<magnus--> http://clozure.com/cgi-bin/viewcvs.cgi/ccl/lisp-kernel/lisp-exceptions.c?rev=1.9&content-type=text/vnd.viewcvs-markup
- single-stepper and variable access: wrong namespace?
- [ over ] generics no-method
- investigate if COPYING_GEN needs a fix
- alien-global type wrong
- simplifier:
- dead loads not optimized out
- kill tag-fixnum/untag-fixnum

View File

@ -1,9 +1,9 @@
! Copyright (C) 2004, 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: alien
USING: assembler compiler compiler-backend errors generic
inference kernel lists math namespaces sequences stdio strings
unparser words ;
USING: assembler compiler compiler-frontend compiler-backend
errors generic inference kernel lists math namespaces sequences
stdio strings unparser words ;
! ! ! WARNING ! ! !
! Reloading this file into a running Factor instance on Win32
@ -42,48 +42,42 @@ M: alien-error error. ( error -- )
" symbol." %
] make-string print ;
: alien-invoke ( ... returns library function parameters -- ... )
: alien-invoke ( ... return library function parameters -- ... )
#! Call a C library function.
#! 'returns' is a type spec, and 'parameters' is a list of
#! 'return' is a type spec, and 'parameters' is a list of
#! type specs. 'library' is an entry in the "libraries"
#! namespace.
drop <alien-error> throw ;
! These are set in the alien-invoke dataflow IR node.
SYMBOL: alien-returns
SYMBOL: alien-parameters
TUPLE: alien-node return parameters ;
C: alien-node make-node ;
: set-alien-returns ( returns node -- )
[ dup alien-returns set ] bind
"void" = [
[ object ] produce-d 1 0 node-outputs
] unless ;
: set-alien-return ( return node -- )
2dup set-alien-node-return
swap "void" = [
drop
] [
[ object ] produce-d 1 0 rot node-outputs
] ifte ;
: set-alien-parameters ( parameters node -- )
[ dup alien-parameters set ] bind
[ drop object ] map dup dup ensure-d
length 0 node-inputs consume-d ;
2dup set-alien-node-parameters
>r [ drop object ] map dup dup ensure-d
length 0 r> node-inputs consume-d ;
: ensure-dlsym ( symbol library -- ) load-library dlsym drop ;
: alien-invoke-node ( returns params function library -- )
: alien-node ( return params function library -- )
#! We should fail if the library does not exist, so that
#! compilation does not keep trying to compile FFI words
#! over and over again if the library is not loaded.
2dup ensure-dlsym
cons \ alien-invoke dataflow,
cons param-node <alien-node>
[ set-alien-parameters ] keep
set-alien-returns ;
[ set-alien-return ] keep
node, ;
: infer-alien-invoke ( -- )
\ alien-invoke "infer-effect" word-prop car ensure-d
pop-literal nip
pop-literal nip >r
pop-literal nip
pop-literal nip -rot
r> swap alien-invoke-node ;
: parameters [ alien-parameters get reverse ] bind ;
: parameters alien-node-parameters reverse ;
: stack-space ( parameters -- n )
0 swap [ c-size cell align + ] each ;
@ -101,57 +95,35 @@ SYMBOL: alien-parameters
parameters
dup stack-space
dup %parameters , >r
dup dup length swap [ >r 1 - dup r> unbox-parameter ] each drop
dup dup length swap [
>r 1 - dup r> unbox-parameter
] each drop
length [ %parameter ] project % r> ;
: linearize-returns ( returns -- )
[ alien-returns get ] bind dup "void" = [
: linearize-return ( return -- )
alien-node-return dup "void" = [
drop
] [
c-type [ "boxer" get "box-op" get ] bind execute ,
] ifte ;
: linearize-alien-invoke ( node -- )
M: alien-node linearize-node* ( node -- )
dup linearize-parameters >r
dup [ node-param get ] bind %alien-invoke ,
dup [ node-param get cdr library-abi "stdcall" = ] bind
dup node-param %alien-invoke ,
dup node-param cdr library-abi "stdcall" =
r> swap [ drop ] [ %cleanup , ] ifte
linearize-returns ;
linearize-return ;
\ alien-invoke [ linearize-alien-invoke ] "linearizer" set-word-prop
\ alien-invoke [ [ string string string general-list ] [ ] ]
\ alien-invoke [ [ string object string general-list ] [ ] ]
"infer-effect" set-word-prop
\ alien-invoke [ infer-alien-invoke ] "infer" set-word-prop
: alien-global ( type library name -- value )
#! Fetch the value of C global variable.
#! 'type' is a type spec. 'library' is an entry in the
#! "libraries" namespace.
<alien-error> throw ;
: alien-global-node ( type name library -- )
2dup ensure-dlsym
cons \ alien-global dataflow,
set-alien-returns ;
: infer-alien-global ( -- )
\ alien-global "infer-effect" word-prop car ensure-d
\ alien-invoke [
pop-literal nip
pop-literal nip >r
pop-literal nip
pop-literal nip -rot
alien-global-node ;
: linearize-alien-global ( node -- )
dup [ node-param get ] bind %alien-global ,
linearize-returns ;
\ alien-global [ linearize-alien-global ] "linearizer" set-word-prop
\ alien-global [ [ string string string ] [ object ] ] "infer-effect" set-word-prop
\ alien-global [ infer-alien-global ] "infer" set-word-prop
r> swap alien-node
] "infer" set-word-prop
global [
"libraries" get [ <namespace> "libraries" set ] unless

View File

@ -5,6 +5,9 @@ IN: kernel
: slip ( quot x -- x | quot: -- )
>r call r> ; inline
: 2slip ( quot x y -- x y | quot: -- )
>r >r call r> r> ; inline
: keep ( x quot -- x | quot: x -- )
over >r call r> ; inline

View File

@ -5,55 +5,33 @@ USING: assembler compiler-backend generic hashtables inference
kernel kernel-internals lists math math-internals namespaces
sequences words ;
: immediate? ( obj -- ? )
#! fixnums and f have a pointerless representation, and
#! are compiled immediately. Everything else can be moved
#! by GC, and is indexed through a table.
dup fixnum? swap f eq? or ;
: push-1 ( obj -- )
0 swap literal-value dup
immediate? [ %immediate ] [ %indirect ] ifte , ;
#push [
[ node-produce-d get ] bind
dup length dup %inc-d ,
1 - swap [
push-1 0 over %replace-d ,
] each drop
] "linearizer" set-word-prop
#drop [
[ node-consume-d get length ] bind %dec-d ,
] "linearizer" set-word-prop
\ dup [
drop
in-1
1 %inc-d ,
out-1
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ swap [
drop
in-2
0 0 %replace-d ,
1 1 %replace-d ,
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ over [
drop
0 1 %peek-d ,
1 %inc-d ,
out-1
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ pick [
drop
0 2 %peek-d ,
1 %inc-d ,
out-1
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ >r [
drop
@ -61,7 +39,7 @@ sequences words ;
1 %inc-r ,
1 %dec-d ,
0 0 %replace-r ,
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ r> [
drop
@ -69,20 +47,18 @@ sequences words ;
1 %inc-d ,
1 %dec-r ,
out-1
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
: node-peek ( node -- obj ) node-consume-d swap hash peek ;
: node-peek ( node -- obj ) node-in-d peek ;
: peek-2 dup length 2 - swap nth ;
: node-peek-2 ( node -- obj ) node-consume-d swap hash peek-2 ;
: node-peek-2 ( node -- obj ) node-in-d peek-2 ;
: typed? ( value -- ? ) value-types length 1 = ;
\ slot t "intrinsic" set-word-prop
: slot@ ( node -- n )
#! Compute slot offset.
node-consume-d swap hash
node-in-d
dup peek literal-value cell *
swap peek-2 value-types car type-tag - ;
@ -103,9 +79,7 @@ sequences words ;
0 %untag ,
1 0 %slot ,
] ifte out-1
] "linearizer" set-word-prop
\ set-slot t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ set-slot [
dup typed-literal? [
@ -120,9 +94,7 @@ sequences words ;
1 %untag ,
0 1 2 %set-slot ,
] ifte
] "linearizer" set-word-prop
\ type t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ type [
drop
@ -130,9 +102,7 @@ sequences words ;
0 %type ,
0 %tag-fixnum ,
out-1
] "linearizer" set-word-prop
\ arithmetic-type t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ arithmetic-type [
drop
@ -141,25 +111,21 @@ sequences words ;
0 %tag-fixnum ,
1 %inc-d ,
out-1
] "linearizer" set-word-prop
\ getenv t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ getenv [
1 %dec-d ,
node-peek literal-value 0 <vreg> swap %getenv ,
1 %inc-d ,
out-1
] "linearizer" set-word-prop
\ setenv t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ setenv [
1 %dec-d ,
in-1
node-peek literal-value 0 <vreg> swap %setenv ,
1 %dec-d ,
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
: binary-op-reg ( op out -- )
>r in-2
@ -194,13 +160,10 @@ sequences words ;
[[ fixnum> %fixnum> ]]
[[ eq? %eq? ]]
] [
uncons over t "intrinsic" set-word-prop
[ literal, 0 , \ binary-op , ] make-list
"linearizer" set-word-prop
uncons [ literal, 0 , \ binary-op , ] make-list
"intrinsic" set-word-prop
] each
\ fixnum* t "intrinsic" set-word-prop
: slow-fixnum* \ %fixnum* 0 binary-op-reg ;
\ fixnum* [
@ -217,24 +180,20 @@ sequences words ;
] [
drop slow-fixnum*
] ifte
] "linearizer" set-word-prop
\ fixnum-mod t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ fixnum-mod [
! This is not clever. Because of x86, %fixnum-mod is
! hard-coded to put its output in vreg 2, which happends to
! be EDX there.
drop \ %fixnum-mod 2 binary-op-reg
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
\ fixnum/i t "intrinsic" set-word-prop
\ fixnum/i [
drop \ %fixnum/i 0 binary-op-reg
] "linearizer" set-word-prop
\ fixnum/mod t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ fixnum/mod [
! See the remark on fixnum-mod for vreg usage
@ -243,16 +202,14 @@ sequences words ;
0 <vreg> 1 <vreg> %fixnum/mod ,
2 0 %replace-d ,
0 1 %replace-d ,
] "linearizer" set-word-prop
\ fixnum-bitnot t "intrinsic" set-word-prop
] "intrinsic" set-word-prop
\ fixnum-bitnot [
drop
in-1
0 %fixnum-bitnot ,
out-1
] "linearizer" set-word-prop
] "intrinsic" set-word-prop
: slow-shift ( -- ) \ fixnum-shift %call , ;
@ -289,12 +246,10 @@ sequences words ;
] ifte
] ifte ;
\ fixnum-shift t "intrinsic" set-word-prop
\ fixnum-shift [
node-peek dup literal? [
literal-value fast-shift
] [
drop slow-shift
] ifte
] "linearizer" set-word-prop
] "intrinsic" set-word-prop

View File

@ -4,66 +4,69 @@ IN: compiler-frontend
USING: compiler-backend inference kernel kernel-internals lists
math namespaces words strings errors prettyprint sequences ;
: >linear ( node -- )
#! Dataflow OPs have a linearizer word property. This
#! quotation is executed to convert the node into linear
#! form.
"linearizer" [ "No linearizer" throw ] apply-dataflow ;
GENERIC: linearize-node* ( node -- )
M: f linearize-node* ( f -- ) drop ;
: (linearize) ( dataflow -- )
[ >linear ] each ;
: linearize-node ( node -- )
[
dup linearize-node* node-successor linearize-node
] when* ;
: linearize ( dataflow -- linear )
#! Transform dataflow IR into linear IR. This strips out
#! stack flow information, flattens conditionals into
#! jumps and labels, and turns dataflow IR nodes into
#! lists where the first element is an operation, and the
#! rest is arguments.
[ %prologue , (linearize) ] make-list ;
#! stack flow information, and flattens conditionals into
#! jumps and labels.
[ %prologue , linearize-node ] make-list ;
: linearize-label ( node -- )
#! Labels are tricky, because they might contain non-tail
#! calls. So we push the address of the location right after
#! the #label , then linearize the #label , then add a #return
#! node to the linear IR. The simplifier will take care of
#! this in the common case where the labelled block does
#! not contain non-tail recursive calls to itself.
M: #label linearize-node* ( node -- )
<label> dup %return-to , >r
dup [ node-label get ] bind %label ,
[ node-param get ] bind (linearize)
dup node-param %label ,
node-children car linearize-node
f %return ,
r> %label , ;
#label [
linearize-label
] "linearizer" set-word-prop
M: #call linearize-node* ( node -- )
dup node-param
dup "intrinsic" word-prop [
call
] [
%call , drop
] ?ifte ;
#call [
[ node-param get ] bind %call ,
] "linearizer" set-word-prop
M: #call-label linearize-node* ( node -- )
node-param %call-label , ;
#call-label [
[ node-param get ] bind %call-label ,
] "linearizer" set-word-prop
: immediate? ( obj -- ? )
#! fixnums and f have a pointerless representation, and
#! are compiled immediately. Everything else can be moved
#! by GC, and is indexed through a table.
dup fixnum? swap f eq? or ;
: push-1 ( obj -- )
0 swap literal-value dup
immediate? [ %immediate ] [ %indirect ] ifte , ;
M: #push linearize-node* ( node -- )
node-out-d dup length dup %inc-d ,
1 - swap [ push-1 0 over %replace-d , ] each drop ;
M: #drop linearize-node* ( node -- )
node-in-d length %dec-d , ;
: ifte-head ( label -- )
in-1 1 %dec-d , 0 %jump-t , ;
: linearize-ifte ( param -- )
M: #ifte linearize-node* ( node -- )
#! The parameter is a list of two lists, each one a dataflow
#! IR.
2unlist <label> [
node-children 2unlist <label> [
ifte-head
(linearize) ( false branch )
linearize-node ( false branch )
<label> dup %jump-label ,
] keep %label , ( branch target of BRANCH-T )
swap (linearize) ( true branch )
swap linearize-node ( true branch )
%label , ( branch target of false branch end ) ;
\ ifte [
[ node-param get ] bind linearize-ifte
] "linearizer" set-word-prop
: dispatch-head ( vtable -- end label/code )
#! Output the jump table insn and return a list of
#! label/branch pairs.
@ -77,17 +80,15 @@ math namespaces words strings errors prettyprint sequences ;
: dispatch-body ( end label/param -- )
#! Output each branch, with a jump to the end label.
[ uncons %label , (linearize) %jump-label , ] each-with ;
[ uncons %label , linearize-node %jump-label , ] each-with ;
: linearize-dispatch ( vtable -- )
M: #dispatch linearize-node* ( vtable -- )
#! The parameter is a list of lists, each one is a branch to
#! take in case the top of stack has that type.
dispatch-head dupd dispatch-body %label , ;
node-children dispatch-head dupd dispatch-body %label , ;
\ dispatch [
[ node-param get ] bind linearize-dispatch
] "linearizer" set-word-prop
M: #values linearize-node* ( node -- )
drop ;
#values [ drop ] "linearizer" set-word-prop
#return [ drop f %return , ] "linearizer" set-word-prop
M: #return linearize-node* ( node -- )
drop f %return , ;

View File

@ -11,207 +11,209 @@ sequences vectors words words ;
! 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-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 , ;
!
! : kill-literal ( literals values -- values )
! [
! swap [ swap value= ] some-with? not
! ] subset-with ;
!
! #push [
! [ node-produce-d get ] bind [ literal-value ] map %
! ] "scan-literal" set-word-prop
!
! #push [ 2drop t ] "can-kill" set-word-prop
!
! #push [
! [ node-produce-d [ kill-literal ] change ] extend ,
! ] "kill-node" set-word-prop
!
! #drop [ 2drop t ] "can-kill" set-word-prop
!
! #drop [
! [ node-consume-d [ kill-literal ] change ] extend ,
! ] "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
!
! : 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
!
! #label [ ( literals node -- )
! [ 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
: (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 , ;
: kill-literal ( literals values -- values )
[
swap [ swap value= ] some-with? not
] subset-with ;
#push [
[ node-produce-d get ] bind [ literal-value ] map %
] "scan-literal" set-word-prop
#push [ 2drop t ] "can-kill" set-word-prop
#push [
[ node-produce-d [ kill-literal ] change ] extend ,
] "kill-node" set-word-prop
#drop [ 2drop t ] "can-kill" set-word-prop
#drop [
[ node-consume-d [ kill-literal ] change ] extend ,
] "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
: 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
#label [ ( literals node -- )
[ 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
: optimize ;

View File

@ -73,6 +73,9 @@ sequences strings vectors words hashtables prettyprint ;
terminate
] ifte* ;
: unify-dataflow ( effects -- nodes )
[ [ dataflow-graph get ] bind ] map ;
: deep-clone ( seq -- seq ) [ clone ] map ;
: copy-inference ( -- )
@ -81,7 +84,8 @@ sequences strings vectors words hashtables prettyprint ;
meta-r [ deep-clone ] change
meta-d [ deep-clone ] change
d-in [ deep-clone ] change
dataflow-graph off ;
dataflow-graph off
current-node off ;
: infer-branch ( value -- namespace )
#! Return a namespace with inferencer variables:
@ -92,7 +96,7 @@ sequences strings vectors words hashtables prettyprint ;
dup value-recursion recursive-state set
literal-value dup infer-quot
active? [
#values values-node
#values node,
handle-terminator
] [
drop
@ -110,23 +114,18 @@ sequences strings vectors words hashtables prettyprint ;
] each
] make-list ;
: unify-dataflow ( input instruction effectlist -- )
[ [ get-dataflow ] bind ] map
swap dataflow, [ unit node-consume-d set ] bind ;
: infer-branches ( input instruction branchlist -- )
: infer-branches ( branches node -- )
#! Recursive stack effect inference is done here. If one of
#! the branches has an undecidable stack effect, we set the
#! base case to this stack effect and try again.
(infer-branches) dup unify-effects unify-dataflow ;
: infer-ifte ( true false -- )
#! If branch taken is computed, infer along both paths and
#! unify.
2list >r pop-d \ ifte r> infer-branches ;
[
>r (infer-branches) dup unify-effects unify-dataflow
r> set-node-children
] keep node, ;
\ ifte [
2 dataflow-drop, pop-d pop-d swap infer-ifte
2 #drop node, pop-d pop-d swap 2list
#ifte pop-d drop infer-branches
] "infer" set-word-prop
: vtable>list ( rstate vtable -- list )
@ -134,9 +133,9 @@ sequences strings vectors words hashtables prettyprint ;
USE: kernel-internals
: infer-dispatch ( rstate vtable -- )
>r >r pop-d \ dispatch r> r> vtable>list infer-branches ;
\ dispatch [ pop-literal infer-dispatch ] "infer" set-word-prop
\ dispatch [
pop-literal vtable>list
#dispatch pop-d drop infer-branches
] "infer" set-word-prop
\ dispatch [ [ fixnum vector ] [ ] ] "infer-effect" set-word-prop

View File

@ -1,87 +1,99 @@
! Copyright (C) 2004, 2005 Slava Pestov.
! See http://factor.sf.net/license.txt for BSD license.
IN: inference
USING: interpreter kernel lists namespaces sequences vectors
words ;
USING: generic interpreter kernel lists namespaces parser
sequences vectors words ;
! The dataflow IR is the first of the two intermediate
! representations used by Factor. It annotates concatenative
! code with stack flow information and types.
TUPLE: node effect param in-d out-d in-r out-r
successor children ;
: make-node ( effect param in-d out-d in-r out-r node -- node )
[ >r f <node> r> set-delegate ] keep ;
: NODE:
#! Followed by a node name.
scan dup [ ] define-tuple
create-in [ make-node ] define-constructor ; parsing
: empty-node f f f f f f f f f ;
: param-node ( label) f swap f f f f f ;
: in-d-node ( inputs) >r f f r> f f f f ;
: out-d-node ( outputs) >r f f f r> f f f ;
: d-tail ( n -- list ) meta-d get vector-tail* ;
: r-tail ( n -- list ) meta-r get vector-tail* ;
NODE: #label
: #label ( label -- node ) param-node <#label> ;
NODE: #call
: #call ( word -- node ) param-node <#call> ;
NODE: #call-label
: #call-label ( label -- node ) param-node <#call> ;
NODE: #push
: #push ( outputs -- node ) d-tail out-d-node <#push> ;
NODE: #drop
: #drop ( inputs -- node ) d-tail in-d-node <#drop> ;
NODE: #values
: #values ( -- node ) meta-d get >list in-d-node <#values> ;
NODE: #return
: #return ( -- node ) meta-d get >list in-d-node <#return> ;
NODE: #ifte
: #ifte ( in -- node ) 1 d-tail in-d-node <#ifte> ;
NODE: #dispatch
: #dispatch ( in -- node ) 1 d-tail in-d-node <#dispatch> ;
: node-inputs ( d-count r-count node -- )
tuck
>r r-tail r> set-node-in-r
>r d-tail r> set-node-in-d ;
: node-outputs ( d-count r-count node -- )
tuck
>r r-tail r> set-node-out-r
>r d-tail r> set-node-out-d ;
! Variable holding dataflow graph being built.
SYMBOL: dataflow-graph
! The most recently added node.
SYMBOL: current-node
: node, ( node -- )
dataflow-graph get [
dup current-node [ set-node-successor ] change
] [
! first node
dup dataflow-graph set current-node set
] ifte ;
: nest-node ( -- dataflow current )
dataflow-graph get dataflow-graph off
current-node get current-node off ;
: unnest-node ( new-node dataflow current -- new-node )
>r >r dataflow-graph get unit over set-node-children
r> dataflow-graph set
r> current-node set ;
: with-nesting ( quot -- new-node | quot: -- new-node )
nest-node 2slip unnest-node ; inline
: copy-effect ( from to -- )
over node-in-d over set-node-in-d
over node-in-r over set-node-in-r
over node-out-d over set-node-out-d
swap node-out-r swap set-node-out-r ;
! Recursive state. An alist, mapping words to labels.
SYMBOL: recursive-state
! We build a dataflow graph for the compiler.
SYMBOL: dataflow-graph
! Label nodes have the node-label variable set.
SYMBOL: #label
SYMBOL: #call ( non-tail call )
SYMBOL: #call-label
SYMBOL: #push ( literal )
SYMBOL: #drop
! This is purely a marker for values we retain after a
! conditional. It does not generate code, but merely alerts the
! dataflow optimizer to the fact these values must be retained.
SYMBOL: #values
SYMBOL: #return
SYMBOL: node-consume-d
SYMBOL: node-produce-d
SYMBOL: node-consume-r
SYMBOL: node-produce-r
SYMBOL: node-op
SYMBOL: node-label
! #push nodes have this field set to the value being pushed.
! #call nodes have this as the word being called
SYMBOL: node-param
: <dataflow-node> ( param op -- node )
<namespace> [
node-op set
node-param set
[ ] node-consume-d set
[ ] node-produce-d set
[ ] node-consume-r set
[ ] node-produce-r set
] extend ;
: node-inputs ( d-count r-count -- )
#! Execute in the node's namespace.
meta-r get vector-tail* node-consume-r set
meta-d get vector-tail* node-consume-d set ;
: dataflow-inputs ( in node -- )
[ length 0 node-inputs ] bind ;
: node-outputs ( d-count r-count -- )
#! Execute in the node's namespace.
meta-r get vector-tail* node-produce-r set
meta-d get vector-tail* node-produce-d set ;
: dataflow-outputs ( out node -- )
[ length 0 node-outputs ] bind ;
: get-dataflow ( -- IR )
dataflow-graph get reverse ;
: dataflow, ( param op -- node )
#! Add a node to the dataflow IR.
<dataflow-node> dup dataflow-graph [ cons ] change ;
: dataflow-drop, ( n -- )
f #drop dataflow, [ 0 node-inputs ] bind ;
: dataflow-push, ( n -- )
f #push dataflow, [ 0 node-outputs ] bind ;
: apply-dataflow ( dataflow name default -- )
#! For the dataflow node, look up named word property,
#! if its not defined, apply default quotation to
#! ( node ) otherwise apply property quotation to
#! ( node ).
>r >r dup [ node-op get ] bind r> word-prop dup [
call r> drop
] [
drop r> call
] ifte ;

View File

@ -18,7 +18,7 @@ SYMBOL: inferring-base-case
SYMBOL: d-in
: pop-literal ( -- rstate obj )
1 dataflow-drop, pop-d >literal< ;
1 #drop node, pop-d >literal< ;
: (ensure-types) ( typelist n stack -- )
pick [
@ -48,6 +48,12 @@ SYMBOL: d-in
meta-d [ append ] change
d-in [ append ] change ;
: hairy-node ( node effect quot -- )
over car ensure-d
-rot 2dup car length 0 rot node-inputs
2slip
cdr car length 0 rot node-outputs ; inline
: (present-effect) ( vector -- list )
>list [ value-class ] map ;
@ -64,6 +70,7 @@ SYMBOL: d-in
0 <vector> d-in set
recursive-state set
dataflow-graph off
current-node off
inferring-base-case off ;
GENERIC: apply-object
@ -71,7 +78,7 @@ GENERIC: apply-object
: apply-literal ( obj -- )
#! Literals are annotated with the current recursive
#! state.
recursive-state get <literal> push-d 1 dataflow-push, ;
recursive-state get <literal> push-d 1 #push node, ;
M: object apply-object apply-literal ;
@ -119,12 +126,6 @@ M: object apply-object apply-literal ;
"Word leaves elements on return stack" inference-error
] unless ;
: values-node ( op -- )
#! Add a #values or #return node to the graph.
f swap dataflow, [
meta-d get >list node-consume-d set
] bind ;
: with-infer ( quot -- )
[
f init-inference
@ -133,10 +134,10 @@ M: object apply-object apply-literal ;
check-return
] with-scope ;
: infer ( quot -- [[ in out ]] )
: infer ( quot -- effect )
#! Stack effect of a quotation.
[ infer-quot effect present-effect ] with-infer ;
: dataflow ( quot -- dataflow )
#! Data flow of a quotation.
[ infer-quot #return values-node get-dataflow ] with-infer ;
[ infer-quot #return node, dataflow-graph get ] with-infer ;

View File

@ -6,7 +6,7 @@ sequences words ;
: literal-inputs? ( in stack -- )
tail-slice dup >list [ safe-literal? ] all? [
length dataflow-drop, t
length #drop node, t
] [
drop f
] ifte ;
@ -16,7 +16,7 @@ sequences words ;
: literal-outputs ( out stack -- )
tail-slice dup [ recursive-state get <literal> ] nmap
length dataflow-push, ;
length #push node, ;
: partial-eval? ( word -- ? )
"infer-effect" word-prop car length

View File

@ -4,24 +4,27 @@ IN: inference
USING: interpreter kernel namespaces words ;
\ >r [
f \ >r dataflow, [ 1 0 node-inputs ] extend
\ >r #call
1 0 pick node-inputs
pop-d push-r
[ 0 1 node-outputs ] bind
0 1 pick node-outputs
node,
] "infer" set-word-prop
\ r> [
f \ r> dataflow, [ 0 1 node-inputs ] extend
\ r> #call
0 1 pick node-inputs
pop-r push-d
[ 1 0 node-outputs ] bind
1 0 pick node-outputs
node,
] "infer" set-word-prop
: partial-eval ( word quot -- | quot: word -- )
>r f over dup "infer-effect" word-prop r> with-dataflow ;
: infer-shuffle ( word -- )
[ host-word ] partial-eval ;
dup #call [
over "infer-effect" word-prop [ host-word ] hairy-node
] keep node, ;
\ drop [ 1 dataflow-drop, pop-d drop ] "infer" set-word-prop
\ drop [ 1 #drop node, pop-d drop ] "infer" set-word-prop
\ dup [ \ dup infer-shuffle ] "infer" set-word-prop
\ swap [ \ swap infer-shuffle ] "infer" set-word-prop
\ over [ \ over infer-shuffle ] "infer" set-word-prop

View File

@ -5,37 +5,24 @@ USING: errors generic interpreter kernel lists math
math-internals namespaces sequences strings vectors words
hashtables parser prettyprint ;
: with-dataflow ( param op [[ in# out# ]] quot -- )
#! Take input parameters, execute quotation, take output
#! parameters, add node. The quotation is called with the
#! stack effect.
>r dup car ensure-d
>r dataflow, r> r> rot
[ pick car swap [ length 0 node-inputs ] bind ] keep
pick >r >r nip call r> r> cdr car swap
[ length 0 node-outputs ] bind ; inline
: consume-d ( typelist -- )
[ pop-d 2drop ] each ;
: produce-d ( typelist -- )
[ <computed> push-d ] each ;
: (consume/produce) ( param op effect )
dup >r -rot r>
[ unswons consume-d car produce-d ] with-dataflow ;
: consume/produce ( word [ in-types out-types ] -- )
: consume/produce ( word effect -- )
#! Add a node to the dataflow graph that consumes and
#! produces a number of values.
over "intrinsic" word-prop [
f -rot
] [
#call swap
] ifte (consume/produce) ;
swap #call [
over [
2unlist swap consume-d produce-d
] hairy-node
] keep node, ;
: no-effect ( word -- )
"Unknown stack effect: " swap word-name cat2 inference-error ;
"Unknown stack effect: " swap word-name append
inference-error ;
: inhibit-parital ( -- )
meta-d get [ f swap set-value-safe? ] each ;
@ -43,31 +30,16 @@ hashtables parser prettyprint ;
: recursive? ( word -- ? )
f swap dup word-def [ = or ] tree-each-with ;
: (with-block) ( [[ label quot ]] quot -- node )
#! Call a quotation in a new namespace, and transfer
#! inference state from the outer scope.
swap car >r [
dataflow-graph off
call
d-in get meta-d get meta-r get get-dataflow
] with-scope
r> swap #label dataflow, [ node-label set ] extend >r
meta-r set meta-d set d-in set r> ;
: with-block ( word [[ label quot ]] quot -- node )
: with-block ( word [[ label quot ]] quot -- block-node )
#! Execute a quotation with the word on the stack, and add
#! its dataflow contribution to a new block node in the IR.
over [
>r
dupd cons
recursive-state [ cons ] change
r> call
] (with-block) ;
#! its dataflow contribution to a new #label node in the IR.
>r 2dup cons recursive-state [ cons ] change r>
[ swap car #label slip ] with-nesting
recursive-state [ cdr ] change ; inline
: inline-block ( word -- effect node )
: inline-block ( word -- node-block )
gensym over word-def cons [
inhibit-parital
word-def infer-quot effect
inhibit-parital word-def infer-quot
] with-block ;
: inline-compound ( word -- )
@ -75,7 +47,7 @@ hashtables parser prettyprint ;
#! inferencer instance. If the word in question is recursive
#! we infer its stack effect inside a new block.
dup recursive? [
inline-block 2drop
inline-block node,
] [
word-def infer-quot
] ifte ;
@ -86,7 +58,7 @@ hashtables parser prettyprint ;
[
[
recursive-state get init-inference
dup dup inline-block drop present-effect
dup dup inline-block drop effect present-effect
[ "infer-effect" set-word-prop ] keep
] with-scope consume/produce
] [
@ -153,16 +125,16 @@ M: compound apply-word ( word -- )
: base-case ( word [ label quot ] -- )
[
car over inline-block [
drop
[ #call-label ] [ #call ] ?ifte
node-op set
node-param set
] bind
>r [ inline-block ] keep r> car [
#call-label
] [
#call
] ?ifte [ copy-effect ] keep node,
] with-recursion ;
: no-base-case ( word -- )
word-name " does not have a base case." cat2 inference-error ;
word-name " does not have a base case." append
inference-error ;
: recursive-word ( word [ label quot ] -- )
#! Handle a recursive call, by either applying a previously

View File

@ -70,3 +70,5 @@ TUPLE: circle radius ;
M: circle area circle-radius sq pi * ;
[ 200 ] [ << rect f 0 0 10 20 >> area ] unit-test
[ ] [ "IN: temporary SYMBOL: #x TUPLE: #x ;" eval ] unit-test