factor/basis/compiler/cfg/linear-scan/allocation/allocation.factor

150 lines
4.8 KiB
Factor
Raw Normal View History

2008-09-10 23:11:03 -04:00
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: namespaces sequences math math.order kernel assocs
2008-10-19 02:10:21 -04:00
accessors vectors fry heaps cpu.architecture combinators
compiler.cfg.registers
2008-10-07 21:00:38 -04:00
compiler.cfg.linear-scan.live-intervals ;
2008-09-11 03:05:22 -04:00
IN: compiler.cfg.linear-scan.allocation
2008-09-10 23:11:03 -04:00
2008-09-15 02:54:48 -04:00
! Mapping from register classes to sequences of machine registers
SYMBOL: free-registers
: free-registers-for ( vreg -- seq )
reg-class>> free-registers get at ;
: deallocate-register ( live-interval -- )
[ reg>> ] [ vreg>> ] bi free-registers-for push ;
! Vector of active live intervals
2008-09-10 23:11:03 -04:00
SYMBOL: active-intervals
: add-active ( live-interval -- )
active-intervals get push ;
: delete-active ( live-interval -- )
active-intervals get delete ;
2008-09-15 02:54:48 -04:00
: expire-old-intervals ( n -- )
active-intervals get
2008-10-19 02:10:21 -04:00
[ end>> > ] with partition
[ [ deallocate-register ] each ] [ active-intervals set ] bi* ;
2008-09-10 23:11:03 -04:00
2008-09-15 02:54:48 -04:00
! Minheap of live intervals which still need a register allocation
SYMBOL: unhandled-intervals
! Start index of current live interval. We ensure that all
! live intervals added to the unhandled set have a start index
! strictly greater than ths one. This ensures that we can catch
! infinite loop situations.
SYMBOL: progress
: check-progress ( live-interval -- )
start>> progress get <= [ "No progress" throw ] when ; inline
: add-unhandled ( live-interval -- )
[ check-progress ]
[ dup start>> unhandled-intervals get heap-push ]
bi ;
: init-unhandled ( live-intervals -- )
[ [ start>> ] keep ] { } map>assoc
unhandled-intervals get heap-push-all ;
2008-10-19 02:10:21 -04:00
! Splitting
: find-use ( live-interval n quot -- i elt )
[ uses>> ] 2dip curry find ; inline
: split-before ( live-interval i -- before )
[ clone dup uses>> ] dip
[ head >>uses ] [ 1- swap nth >>end ] 2bi ;
: split-after ( live-interval i -- after )
[ clone dup uses>> ] dip
[ tail >>uses ] [ swap nth >>start ] 2bi
f >>reg ;
: split-interval ( live-interval n -- before after )
[ drop ] [ [ > ] find-use drop ] 2bi
[ split-before ] [ split-after ] 2bi ;
: record-split ( live-interval before after -- )
[ >>split-before ] [ >>split-after ] bi* drop ;
2008-09-15 02:54:48 -04:00
! Spilling
SYMBOL: spill-counts
2008-09-10 23:11:03 -04:00
: next-spill-location ( reg-class -- n )
spill-counts get [ dup 1+ ] change-at ;
2008-09-10 23:11:03 -04:00
2008-10-19 02:10:21 -04:00
: interval-to-spill ( active-intervals current -- live-interval )
2008-09-15 02:54:48 -04:00
#! We spill the interval with the most distant use location.
2008-10-19 02:10:21 -04:00
start>> '[ dup _ [ >= ] find-use nip ] { } map>assoc
unclip-slice [ [ [ second ] bi@ > ] most ] reduce first ;
2008-09-15 02:54:48 -04:00
: assign-spill ( before after -- before after )
#! If it has been spilled already, reuse spill location.
over reload-from>>
2008-10-19 02:10:21 -04:00
[ over vreg>> reg-class>> next-spill-location ] unless*
2008-09-15 02:54:48 -04:00
tuck [ >>spill-to ] [ >>reload-from ] 2bi* ;
2008-10-19 02:10:21 -04:00
: split-and-spill ( new existing -- before after )
dup rot start>> split-interval
[ record-split ] [ assign-spill ] 2bi ;
2008-09-15 02:54:48 -04:00
: reuse-register ( new existing -- )
reg>> >>reg add-active ;
2008-09-15 02:54:48 -04:00
: spill-existing ( new existing -- )
#! Our new interval will be used before the active interval
#! with the most distant use location. Spill the existing
#! interval, then process the new interval and the tail end
#! of the existing interval again.
[ reuse-register ]
2008-10-19 02:10:21 -04:00
[ nip delete-active ]
[ split-and-spill [ drop ] [ add-unhandled ] bi* ] 2tri ;
2008-09-15 02:54:48 -04:00
: spill-new ( new existing -- )
#! Our new interval will be used after the active interval
#! with the most distant use location. Split the new
#! interval, then process both parts of the new interval
#! again.
2008-10-19 02:10:21 -04:00
[ dup split-and-spill add-unhandled ] dip spill-existing ;
2008-09-15 02:54:48 -04:00
: spill-existing? ( new existing -- ? )
2008-10-19 02:10:21 -04:00
#! Test if 'new' will be used before 'existing'.
over start>> '[ _ [ > ] find-use nip -1 or ] bi@ < ;
: assign-blocked-register ( new -- )
[ active-intervals get ] keep interval-to-spill
2dup spill-existing? [ spill-existing ] [ spill-new ] if ;
2008-09-15 02:54:48 -04:00
2008-10-19 02:10:21 -04:00
: assign-free-register ( new registers -- )
pop >>reg add-active ;
2008-09-10 23:11:03 -04:00
2008-10-19 02:10:21 -04:00
: assign-register ( new -- )
dup vreg>> free-registers-for
[ assign-blocked-register ] [ assign-free-register ] if-empty ;
2008-09-10 23:11:03 -04:00
2008-09-15 02:54:48 -04:00
! Main loop
: init-allocator ( registers -- )
V{ } clone active-intervals set
<min-heap> unhandled-intervals set
2008-09-15 05:22:12 -04:00
[ reverse >vector ] assoc-map free-registers set
H{ { int-regs 0 } { double-float-regs 0 } } clone spill-counts set
2008-09-15 02:54:48 -04:00
-1 progress set ;
: handle-interval ( live-interval -- )
2008-10-19 02:10:21 -04:00
[ start>> progress set ]
[ start>> expire-old-intervals ]
[ assign-register ]
tri ;
2008-09-15 02:54:48 -04:00
: (allocate-registers) ( -- )
unhandled-intervals get [ handle-interval ] slurp-heap ;
2008-09-15 05:22:12 -04:00
: allocate-registers ( live-intervals machine-registers -- live-intervals )
2008-09-15 03:59:24 -04:00
#! This modifies the input live-intervals.
init-allocator
dup init-unhandled
(allocate-registers) ;