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-07 21:00:38 -04:00
|
|
|
accessors vectors fry heaps cpu.architecture
|
2008-09-17 20:31:35 -04:00
|
|
|
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-10-09 00:42:53 -04:00
|
|
|
: expired-interval? ( n interval -- ? )
|
|
|
|
[ end>> ] [ start>> ] bi or > ;
|
|
|
|
|
2008-09-15 02:54:48 -04:00
|
|
|
: expire-old-intervals ( n -- )
|
|
|
|
active-intervals get
|
2008-10-09 00:42:53 -04:00
|
|
|
[ expired-interval? ] 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
|
|
|
: expire-old-uses ( n -- )
|
|
|
|
active-intervals get
|
2008-10-09 00:42:53 -04:00
|
|
|
swap '[
|
|
|
|
uses>> [
|
|
|
|
dup peek _ < [ pop* ] [ drop ] if
|
|
|
|
] unless-empty
|
|
|
|
] each ;
|
2008-09-15 02:54:48 -04:00
|
|
|
|
|
|
|
: update-state ( live-interval -- )
|
|
|
|
start>> [ expire-old-intervals ] [ expire-old-uses ] bi ;
|
|
|
|
|
|
|
|
! 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 ;
|
|
|
|
|
|
|
|
: assign-free-register ( live-interval registers -- )
|
2008-10-09 00:42:53 -04:00
|
|
|
pop >>reg add-active ;
|
2008-09-15 02:54:48 -04:00
|
|
|
|
|
|
|
! Spilling
|
2008-09-17 20:31:35 -04:00
|
|
|
SYMBOL: spill-counts
|
2008-09-10 23:11:03 -04:00
|
|
|
|
2008-09-17 20:31:35 -04:00
|
|
|
: next-spill-location ( reg-class -- n )
|
|
|
|
spill-counts get [ dup 1+ ] change-at ;
|
2008-09-10 23:11:03 -04:00
|
|
|
|
|
|
|
: interval-to-spill ( -- live-interval )
|
2008-09-15 02:54:48 -04:00
|
|
|
#! We spill the interval with the most distant use location.
|
2008-10-09 00:42:53 -04:00
|
|
|
active-intervals get
|
|
|
|
[ uses>> empty? not ] filter
|
|
|
|
unclip-slice [
|
2008-09-15 02:54:48 -04:00
|
|
|
[ [ uses>> peek ] bi@ > ] most
|
2008-09-10 23:11:03 -04:00
|
|
|
] reduce ;
|
|
|
|
|
2008-09-15 02:54:48 -04:00
|
|
|
: check-split ( live-interval -- )
|
|
|
|
[ start>> ] [ end>> ] bi = [ "Cannot split any further" throw ] when ;
|
|
|
|
|
|
|
|
: split-interval ( live-interval -- before after )
|
|
|
|
#! Split the live interval at the location of its first use.
|
|
|
|
#! 'Before' now starts and ends on the same instruction.
|
|
|
|
[ check-split ]
|
|
|
|
[ clone [ uses>> delete-all ] [ dup start>> >>end ] bi ]
|
|
|
|
[ clone f >>reg dup uses>> peek >>start ]
|
|
|
|
tri ;
|
|
|
|
|
|
|
|
: record-split ( live-interval before after -- )
|
|
|
|
[ >>split-before ] [ >>split-after ] bi* drop ;
|
|
|
|
|
|
|
|
: assign-spill ( before after -- before after )
|
|
|
|
#! If it has been spilled already, reuse spill location.
|
2008-10-09 00:42:53 -04:00
|
|
|
USE: cpu.architecture ! XXX
|
|
|
|
over reload-from>>
|
|
|
|
[ int-regs next-spill-location ] unless*
|
2008-09-15 02:54:48 -04:00
|
|
|
tuck [ >>spill-to ] [ >>reload-from ] 2bi* ;
|
|
|
|
|
|
|
|
: split-and-spill ( live-interval -- before after )
|
|
|
|
dup split-interval [ record-split ] [ assign-spill ] 2bi ;
|
|
|
|
|
|
|
|
: reuse-register ( new existing -- )
|
2008-10-09 00:42:53 -04:00
|
|
|
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 ]
|
|
|
|
[ delete-active ]
|
2008-09-15 03:59:24 -04:00
|
|
|
[ split-and-spill [ drop ] [ add-unhandled ] bi* ] tri ;
|
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.
|
|
|
|
[ split-and-spill add-unhandled ] dip spill-existing ;
|
|
|
|
|
|
|
|
: spill-existing? ( new existing -- ? )
|
|
|
|
over uses>> empty? [ 2drop t ] [ [ uses>> peek ] bi@ < ] if ;
|
|
|
|
|
|
|
|
: assign-blocked-register ( live-interval -- )
|
2008-09-10 23:11:03 -04:00
|
|
|
interval-to-spill
|
2008-09-15 02:54:48 -04:00
|
|
|
2dup spill-existing?
|
|
|
|
[ spill-existing ] [ spill-new ] if ;
|
2008-09-10 23:11:03 -04:00
|
|
|
|
2008-09-15 02:54:48 -04:00
|
|
|
: assign-register ( live-interval -- )
|
2008-09-10 23:11:03 -04:00
|
|
|
dup vreg>> free-registers-for [
|
2008-09-15 02:54:48 -04:00
|
|
|
assign-blocked-register
|
2008-09-10 23:11:03 -04:00
|
|
|
] [
|
2008-09-15 02:54:48 -04:00
|
|
|
assign-free-register
|
2008-09-10 23:11:03 -04:00
|
|
|
] if-empty ;
|
|
|
|
|
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
|
2008-09-17 20:31:35 -04:00
|
|
|
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 -- )
|
|
|
|
[ start>> progress set ] [ update-state ] [ assign-register ] tri ;
|
|
|
|
|
|
|
|
: (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.
|
2008-09-17 20:31:35 -04:00
|
|
|
init-allocator
|
|
|
|
dup init-unhandled
|
|
|
|
(allocate-registers) ;
|