Cleaner loop detection pass
parent
215f6ef65b
commit
bbd05723a5
|
@ -1,22 +1,13 @@
|
||||||
! Copyright (C) 2008 Slava Pestov.
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: kernel sequences namespaces assocs accessors fry
|
USING: kernel sequences namespaces assocs accessors fry
|
||||||
compiler.tree ;
|
compiler.tree dequeues search-dequeues ;
|
||||||
IN: compiler.tree.loop.detection
|
IN: compiler.tree.loop.detection
|
||||||
|
|
||||||
! A loop is a #recursive which only tail calls itself, and those
|
! A loop is a #recursive which only tail calls itself, and those
|
||||||
! calls are nested inside other loops only.
|
! calls are nested inside other loops only. We optimistically
|
||||||
|
! assume all #recursive nodes are loops, disqualifying them as
|
||||||
TUPLE: recursive-call tail? nesting ;
|
! we see evidence to the contrary.
|
||||||
|
|
||||||
! calls is a sequence of recursive-call instances
|
|
||||||
TUPLE: loop-info calls height ;
|
|
||||||
|
|
||||||
! Mapping inline-recursive instances to loop-info instances
|
|
||||||
SYMBOL: loop-infos
|
|
||||||
|
|
||||||
! A sequence of inline-recursive instances
|
|
||||||
SYMBOL: label-stack
|
|
||||||
|
|
||||||
: (tail-calls) ( tail? seq -- seq' )
|
: (tail-calls) ( tail? seq -- seq' )
|
||||||
reverse [ swap [ and ] keep ] map nip reverse ;
|
reverse [ swap [ and ] keep ] map nip reverse ;
|
||||||
|
@ -29,6 +20,11 @@ SYMBOL: label-stack
|
||||||
tri or or
|
tri or or
|
||||||
] map (tail-calls) ;
|
] map (tail-calls) ;
|
||||||
|
|
||||||
|
SYMBOL: loop-heights
|
||||||
|
SYMBOL: loop-calls
|
||||||
|
SYMBOL: label-stack
|
||||||
|
SYMBOL: work-list
|
||||||
|
|
||||||
GENERIC: collect-loop-info* ( tail? node -- )
|
GENERIC: collect-loop-info* ( tail? node -- )
|
||||||
|
|
||||||
: non-tail-label-info ( nodes -- )
|
: non-tail-label-info ( nodes -- )
|
||||||
|
@ -37,24 +33,32 @@ GENERIC: collect-loop-info* ( tail? node -- )
|
||||||
: (collect-loop-info) ( tail? nodes -- )
|
: (collect-loop-info) ( tail? nodes -- )
|
||||||
[ tail-calls ] keep [ collect-loop-info* ] 2each ;
|
[ tail-calls ] keep [ collect-loop-info* ] 2each ;
|
||||||
|
|
||||||
: remember-loop-info ( #recursive -- )
|
: remember-loop-info ( label -- )
|
||||||
V{ } clone label-stack get length loop-info boa
|
label-stack get length swap loop-heights get set-at ;
|
||||||
swap label>> loop-infos get set-at ;
|
|
||||||
|
|
||||||
M: #recursive collect-loop-info*
|
M: #recursive collect-loop-info*
|
||||||
nip
|
nip
|
||||||
[
|
[
|
||||||
[ label-stack [ swap label>> suffix ] change ]
|
[
|
||||||
[ remember-loop-info ]
|
label>>
|
||||||
[ t swap child>> (collect-loop-info) ]
|
[ label-stack [ swap suffix ] change ]
|
||||||
tri
|
[ remember-loop-info ]
|
||||||
|
[ t >>loop? drop ]
|
||||||
|
tri
|
||||||
|
]
|
||||||
|
[ t swap child>> (collect-loop-info) ] bi
|
||||||
] with-scope ;
|
] with-scope ;
|
||||||
|
|
||||||
|
: current-loop-nesting ( label -- labels )
|
||||||
|
label-stack get swap loop-heights get at tail ;
|
||||||
|
|
||||||
|
: disqualify-loop ( label -- )
|
||||||
|
work-list get push-front ;
|
||||||
|
|
||||||
M: #call-recursive collect-loop-info*
|
M: #call-recursive collect-loop-info*
|
||||||
label>> loop-infos get at
|
label>>
|
||||||
[ label-stack get swap height>> tail recursive-call boa ]
|
swap [ dup disqualify-loop ] unless
|
||||||
[ calls>> ]
|
dup current-loop-nesting [ loop-calls get push-at ] with each ;
|
||||||
bi push ;
|
|
||||||
|
|
||||||
M: #if collect-loop-info*
|
M: #if collect-loop-info*
|
||||||
children>> [ (collect-loop-info) ] with each ;
|
children>> [ (collect-loop-info) ] with each ;
|
||||||
|
@ -66,38 +70,19 @@ M: node collect-loop-info* 2drop ;
|
||||||
|
|
||||||
: collect-loop-info ( node -- )
|
: collect-loop-info ( node -- )
|
||||||
{ } label-stack set
|
{ } label-stack set
|
||||||
H{ } clone loop-infos set
|
H{ } clone loop-calls set
|
||||||
|
H{ } clone loop-heights set
|
||||||
|
<hashed-dlist> work-list set
|
||||||
t swap (collect-loop-info) ;
|
t swap (collect-loop-info) ;
|
||||||
|
|
||||||
! Sub-assoc of loop-infos
|
: disqualify-loops ( -- )
|
||||||
SYMBOL: potential-loops
|
work-list get [
|
||||||
|
dup loop?>> [
|
||||||
: remove-non-tail-calls ( -- )
|
[ f >>loop? drop ]
|
||||||
loop-infos get
|
[ loop-calls get at [ disqualify-loop ] each ]
|
||||||
[ nip calls>> [ tail?>> ] all? ] assoc-filter
|
bi
|
||||||
potential-loops set ;
|
] [ drop ] if
|
||||||
|
] slurp-dequeue ;
|
||||||
: (remove-non-loop-calls) ( loop-infos -- )
|
|
||||||
f over [
|
|
||||||
! If label X is called from within a label Y that is
|
|
||||||
! no longer a potential loop, then X is no longer a
|
|
||||||
! potential loop either.
|
|
||||||
over potential-loops get key? [
|
|
||||||
potential-loops get '[ , key? ] all?
|
|
||||||
[ drop ] [ potential-loops get delete-at t or ] if
|
|
||||||
] [ 2drop ] if
|
|
||||||
] assoc-each
|
|
||||||
[ (remove-non-loop-calls) ] [ drop ] if ;
|
|
||||||
|
|
||||||
: remove-non-loop-calls ( -- )
|
|
||||||
! Boolean is set to t if something changed.
|
|
||||||
! We recurse until a fixed point is reached.
|
|
||||||
loop-infos get [ calls>> [ nesting>> ] map concat ] assoc-map
|
|
||||||
(remove-non-loop-calls) ;
|
|
||||||
|
|
||||||
: detect-loops ( nodes -- nodes )
|
: detect-loops ( nodes -- nodes )
|
||||||
dup
|
dup collect-loop-info disqualify-loops ;
|
||||||
collect-loop-info
|
|
||||||
remove-non-tail-calls
|
|
||||||
remove-non-loop-calls
|
|
||||||
potential-loops get [ drop t >>loop? drop ] assoc-each ;
|
|
||||||
|
|
Loading…
Reference in New Issue