Split up compiler.cfg.linear-scan.allocation into a number of sub-vocabularies; start work on compiler.cfg.linear-scan.resolve; start work on inactive interval splitting
parent
bcfc0c5759
commit
d0f6a7d048
|
@ -245,4 +245,5 @@ INSN: _gc { temp1 vreg } { temp2 vreg } gc-roots gc-root-count gc-root-size ;
|
|||
! virtual registers
|
||||
INSN: _spill src class n ;
|
||||
INSN: _reload dst class n ;
|
||||
INSN: _copy dst src class ;
|
||||
INSN: _spill-counts counts ;
|
||||
|
|
|
@ -1,280 +1,12 @@
|
|||
! Copyright (C) 2008, 2009 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: namespaces sequences math math.order kernel assocs
|
||||
accessors vectors fry heaps cpu.architecture sorting locals
|
||||
combinators compiler.cfg.registers
|
||||
compiler.cfg.linear-scan.live-intervals hints ;
|
||||
USING: accessors assocs heaps kernel namespaces sequences
|
||||
compiler.cfg.linear-scan.allocation.coalescing
|
||||
compiler.cfg.linear-scan.allocation.spilling
|
||||
compiler.cfg.linear-scan.allocation.splitting
|
||||
compiler.cfg.linear-scan.allocation.state ;
|
||||
IN: compiler.cfg.linear-scan.allocation
|
||||
|
||||
! 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
|
||||
SYMBOL: active-intervals
|
||||
|
||||
: active-intervals-for ( vreg -- seq )
|
||||
reg-class>> active-intervals get at ;
|
||||
|
||||
: add-active ( live-interval -- )
|
||||
dup vreg>> active-intervals-for push ;
|
||||
|
||||
: delete-active ( live-interval -- )
|
||||
dup vreg>> active-intervals-for delq ;
|
||||
|
||||
! Vector of inactive live intervals
|
||||
SYMBOL: inactive-intervals
|
||||
|
||||
: inactive-intervals-for ( vreg -- seq )
|
||||
reg-class>> inactive-intervals get at ;
|
||||
|
||||
: add-inactive ( live-interval -- )
|
||||
dup vreg>> inactive-intervals-for push ;
|
||||
|
||||
! Vector of handled live intervals
|
||||
SYMBOL: handled-intervals
|
||||
|
||||
: add-handled ( live-interval -- )
|
||||
handled-intervals get push ;
|
||||
|
||||
: finished? ( n live-interval -- ? ) end>> swap < ;
|
||||
|
||||
: finish ( n live-interval -- keep? )
|
||||
nip [ deallocate-register ] [ add-handled ] bi f ;
|
||||
|
||||
: activate ( n live-interval -- keep? )
|
||||
nip add-active f ;
|
||||
|
||||
: deactivate ( n live-interval -- keep? )
|
||||
nip add-inactive f ;
|
||||
|
||||
: don't-change ( n live-interval -- keep? ) 2drop t ;
|
||||
|
||||
! Moving intervals between active and inactive sets
|
||||
: process-intervals ( n symbol quots -- )
|
||||
! symbol stores an alist mapping register classes to vectors
|
||||
[ get values ] dip '[ [ _ cond ] with filter-here ] with each ; inline
|
||||
|
||||
: covers? ( insn# live-interval -- ? )
|
||||
ranges>> [ [ from>> ] [ to>> ] bi between? ] with any? ;
|
||||
|
||||
: deactivate-intervals ( n -- )
|
||||
! Any active intervals which have ended are moved to handled
|
||||
! Any active intervals which cover the current position
|
||||
! are moved to inactive
|
||||
active-intervals {
|
||||
{ [ 2dup finished? ] [ finish ] }
|
||||
{ [ 2dup covers? not ] [ deactivate ] }
|
||||
[ don't-change ]
|
||||
} process-intervals ;
|
||||
|
||||
: activate-intervals ( n -- )
|
||||
! Any inactive intervals which have ended are moved to handled
|
||||
! Any inactive intervals which do not cover the current position
|
||||
! are moved to active
|
||||
inactive-intervals {
|
||||
{ [ 2dup finished? ] [ finish ] }
|
||||
{ [ 2dup covers? ] [ activate ] }
|
||||
[ don't-change ]
|
||||
} process-intervals ;
|
||||
|
||||
! 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 ;
|
||||
|
||||
! Coalescing
|
||||
: active-interval ( vreg -- live-interval )
|
||||
dup [ dup active-intervals-for [ vreg>> = ] with find nip ] when ;
|
||||
|
||||
: coalesce? ( live-interval -- ? )
|
||||
[ start>> ] [ copy-from>> active-interval ] bi
|
||||
dup [ end>> = ] [ 2drop f ] if ;
|
||||
|
||||
: coalesce ( live-interval -- )
|
||||
dup copy-from>> active-interval
|
||||
[ [ add-active ] [ [ delete-active ] [ add-handled ] bi ] bi* ]
|
||||
[ reg>> >>reg drop ]
|
||||
2bi ;
|
||||
|
||||
! Splitting
|
||||
: split-range ( live-range n -- before after )
|
||||
[ [ from>> ] dip <live-range> ]
|
||||
[ 1 + swap to>> <live-range> ]
|
||||
2bi ;
|
||||
|
||||
: split-last-range? ( last n -- ? )
|
||||
swap to>> <= ;
|
||||
|
||||
: split-last-range ( before after last n -- before' after' )
|
||||
split-range [ [ but-last ] dip suffix ] [ prefix ] bi-curry* bi* ;
|
||||
|
||||
: split-ranges ( live-ranges n -- before after )
|
||||
[ '[ from>> _ <= ] partition ]
|
||||
[
|
||||
pick empty? [ drop ] [
|
||||
[ over last ] dip 2dup split-last-range?
|
||||
[ split-last-range ] [ 2drop ] if
|
||||
] if
|
||||
] bi ;
|
||||
|
||||
: split-uses ( uses n -- before after )
|
||||
'[ _ <= ] partition ;
|
||||
|
||||
: record-split ( live-interval before after -- )
|
||||
[ >>split-before ] [ >>split-after ] bi* drop ; inline
|
||||
|
||||
: check-split ( live-interval -- )
|
||||
[ end>> ] [ start>> ] bi - 0 =
|
||||
[ "BUG: splitting atomic interval" throw ] when ; inline
|
||||
|
||||
: split-before ( before -- before' )
|
||||
[ [ ranges>> last ] [ uses>> last ] bi >>to drop ]
|
||||
[ compute-start/end ]
|
||||
[ ]
|
||||
tri ; inline
|
||||
|
||||
: split-after ( after -- after' )
|
||||
[ [ ranges>> first ] [ uses>> first ] bi >>from drop ]
|
||||
[ compute-start/end ]
|
||||
[ ]
|
||||
tri ; inline
|
||||
|
||||
:: split-interval ( live-interval n -- before after )
|
||||
live-interval check-split
|
||||
live-interval clone :> before
|
||||
live-interval clone f >>copy-from f >>reg :> after
|
||||
live-interval uses>> n split-uses before after [ (>>uses) ] bi-curry@ bi*
|
||||
live-interval ranges>> n split-ranges before after [ (>>ranges) ] bi-curry@ bi*
|
||||
live-interval before after record-split
|
||||
before split-before
|
||||
after split-after ;
|
||||
|
||||
HINTS: split-interval live-interval object ;
|
||||
|
||||
! Spilling
|
||||
SYMBOL: spill-counts
|
||||
|
||||
: next-spill-location ( reg-class -- n )
|
||||
spill-counts get [ dup 1+ ] change-at ;
|
||||
|
||||
: find-use ( live-interval n quot -- i elt )
|
||||
[ uses>> ] 2dip curry find ; inline
|
||||
|
||||
: interval-to-spill ( active-intervals current -- live-interval )
|
||||
#! We spill the interval with the most distant use location.
|
||||
start>> '[ dup _ [ >= ] find-use nip ] { } map>assoc
|
||||
[ ] [ [ [ second ] bi@ > ] most ] map-reduce first ;
|
||||
|
||||
: assign-spill ( before after -- before after )
|
||||
#! If it has been spilled already, reuse spill location.
|
||||
over reload-from>>
|
||||
[ over vreg>> reg-class>> next-spill-location ] unless*
|
||||
[ >>spill-to ] [ >>reload-from ] bi-curry bi* ;
|
||||
|
||||
: split-and-spill ( new existing -- before after )
|
||||
swap start>> split-interval assign-spill ;
|
||||
|
||||
: reuse-register ( new existing -- )
|
||||
reg>> >>reg add-active ;
|
||||
|
||||
: 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 ]
|
||||
[ nip delete-active ]
|
||||
[ split-and-spill [ add-handled ] [ add-unhandled ] bi* ] 2tri ;
|
||||
|
||||
: 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.
|
||||
[ dup split-and-spill add-unhandled ] dip spill-existing ;
|
||||
|
||||
: spill-existing? ( new existing -- ? )
|
||||
#! Test if 'new' will be used before 'existing'.
|
||||
over start>> '[ _ [ > ] find-use nip -1 or ] bi@ < ;
|
||||
|
||||
: assign-blocked-register ( new -- )
|
||||
[ dup vreg>> active-intervals-for ] keep interval-to-spill
|
||||
2dup spill-existing? [ spill-existing ] [ spill-new ] if ;
|
||||
|
||||
: assign-free-register ( new registers -- )
|
||||
pop >>reg add-active ;
|
||||
|
||||
: relevant-ranges ( new inactive -- new' inactive' )
|
||||
! Slice off all ranges of 'inactive' that precede the start of 'new'
|
||||
[ [ ranges>> ] bi@ ] [ nip start>> ] 2bi '[ to>> _ >= ] filter ;
|
||||
|
||||
: intersect-live-range ( range1 range2 -- n/f )
|
||||
2dup [ from>> ] bi@ > [ swap ] when
|
||||
2dup [ to>> ] [ from>> ] bi* >= [ nip from>> ] [ 2drop f ] if ;
|
||||
|
||||
: intersect-live-ranges ( ranges1 ranges2 -- n )
|
||||
{
|
||||
{ [ over empty? ] [ 2drop 1/0. ] }
|
||||
{ [ dup empty? ] [ 2drop 1/0. ] }
|
||||
[
|
||||
2dup [ first ] bi@ intersect-live-range dup [ 2nip ] [
|
||||
drop
|
||||
2dup [ first from>> ] bi@ <
|
||||
[ [ rest-slice ] dip ] [ rest-slice ] if
|
||||
intersect-live-ranges
|
||||
] if
|
||||
]
|
||||
} cond ;
|
||||
|
||||
: intersect-inactive ( new inactive -- n )
|
||||
relevant-ranges intersect-live-ranges ;
|
||||
|
||||
: intersecting-inactive ( new -- live-intervals )
|
||||
dup vreg>> inactive-intervals-for
|
||||
[ tuck intersect-inactive ] with { } map>assoc ;
|
||||
|
||||
: fits-in-hole ( new pair -- )
|
||||
first reuse-register ;
|
||||
|
||||
: split-before-use ( new pair -- before after )
|
||||
! Find optimal split position
|
||||
! Insert move instruction
|
||||
second split-interval ;
|
||||
|
||||
: assign-inactive-register ( new live-intervals -- )
|
||||
! If there is an interval which is inactive for the entire lifetime
|
||||
! if the new interval, reuse its vreg. Otherwise, split new so that
|
||||
! the first half fits.
|
||||
sort-values last
|
||||
2dup [ end>> ] [ second ] bi* < [
|
||||
fits-in-hole
|
||||
] [
|
||||
[ split-before-use ] keep
|
||||
'[ _ fits-in-hole ] [ add-unhandled ] bi*
|
||||
] if ;
|
||||
|
||||
: assign-register ( new -- )
|
||||
dup coalesce? [ coalesce ] [
|
||||
dup vreg>> free-registers-for [
|
||||
|
@ -286,21 +18,6 @@ SYMBOL: spill-counts
|
|||
if-empty
|
||||
] if ;
|
||||
|
||||
! Main loop
|
||||
CONSTANT: reg-classes { int-regs double-float-regs }
|
||||
|
||||
: reg-class-assoc ( quot -- assoc )
|
||||
[ reg-classes ] dip { } map>assoc ; inline
|
||||
|
||||
: init-allocator ( registers -- )
|
||||
[ reverse >vector ] assoc-map free-registers set
|
||||
[ 0 ] reg-class-assoc spill-counts set
|
||||
<min-heap> unhandled-intervals set
|
||||
[ V{ } clone ] reg-class-assoc active-intervals set
|
||||
[ V{ } clone ] reg-class-assoc inactive-intervals set
|
||||
V{ } clone handled-intervals set
|
||||
-1 progress set ;
|
||||
|
||||
: handle-interval ( live-interval -- )
|
||||
[
|
||||
start>>
|
||||
|
@ -313,12 +30,10 @@ CONSTANT: reg-classes { int-regs double-float-regs }
|
|||
unhandled-intervals get [ handle-interval ] slurp-heap ;
|
||||
|
||||
: finish-allocation ( -- )
|
||||
! Sanity check: all live intervals should've been processed
|
||||
active-intervals inactive-intervals
|
||||
[ get values [ handled-intervals get push-all ] each ] bi@ ;
|
||||
|
||||
: allocate-registers ( live-intervals machine-registers -- live-intervals )
|
||||
#! This modifies the input live-intervals.
|
||||
init-allocator
|
||||
init-unhandled
|
||||
(allocate-registers)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
! Copyright (C) 2009 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors kernel sequences
|
||||
compiler.cfg.linear-scan.allocation.state ;
|
||||
IN: compiler.cfg.linear-scan.allocation.coalescing
|
||||
|
||||
: active-interval ( vreg -- live-interval )
|
||||
dup [ dup active-intervals-for [ vreg>> = ] with find nip ] when ;
|
||||
|
||||
: coalesce? ( live-interval -- ? )
|
||||
[ start>> ] [ copy-from>> active-interval ] bi
|
||||
dup [ end>> = ] [ 2drop f ] if ;
|
||||
|
||||
: coalesce ( live-interval -- )
|
||||
dup copy-from>> active-interval
|
||||
[ [ add-active ] [ [ delete-active ] [ add-handled ] bi ] bi* ]
|
||||
[ reg>> >>reg drop ]
|
||||
2bi ;
|
|
@ -0,0 +1,60 @@
|
|||
! Copyright (C) 2009 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors arrays assocs combinators fry hints kernel locals
|
||||
math sequences sets sorting splitting
|
||||
compiler.cfg.linear-scan.allocation.state
|
||||
compiler.cfg.linear-scan.allocation.splitting
|
||||
compiler.cfg.linear-scan.live-intervals ;
|
||||
IN: compiler.cfg.linear-scan.allocation.spilling
|
||||
|
||||
: split-for-spill ( live-interval n -- before after )
|
||||
split-interval
|
||||
[
|
||||
[ [ ranges>> last ] [ uses>> last ] bi >>to drop ]
|
||||
[ [ ranges>> first ] [ uses>> first ] bi >>from drop ] bi*
|
||||
]
|
||||
[ [ compute-start/end ] bi@ ]
|
||||
[ ]
|
||||
2tri ;
|
||||
|
||||
: find-use ( live-interval n quot -- i elt )
|
||||
[ uses>> ] 2dip curry find ; inline
|
||||
|
||||
: interval-to-spill ( active-intervals current -- live-interval )
|
||||
#! We spill the interval with the most distant use location.
|
||||
start>> '[ dup _ [ >= ] find-use nip ] { } map>assoc
|
||||
[ ] [ [ [ second ] bi@ > ] most ] map-reduce first ;
|
||||
|
||||
: assign-spill ( before after -- before after )
|
||||
#! If it has been spilled already, reuse spill location.
|
||||
over reload-from>>
|
||||
[ over vreg>> reg-class>> next-spill-location ] unless*
|
||||
[ >>spill-to ] [ >>reload-from ] bi-curry bi* ;
|
||||
|
||||
: split-and-spill ( new existing -- before after )
|
||||
swap start>> split-for-spill assign-spill ;
|
||||
|
||||
: 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 ]
|
||||
[ nip delete-active ]
|
||||
[ split-and-spill [ add-handled ] [ add-unhandled ] bi* ] 2tri ;
|
||||
|
||||
: 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.
|
||||
[ dup split-and-spill add-unhandled ] dip spill-existing ;
|
||||
|
||||
: spill-existing? ( new existing -- ? )
|
||||
#! Test if 'new' will be used before 'existing'.
|
||||
over start>> '[ _ [ > ] find-use nip -1 or ] bi@ < ;
|
||||
|
||||
: assign-blocked-register ( new -- )
|
||||
[ dup vreg>> active-intervals-for ] keep interval-to-spill
|
||||
2dup spill-existing? [ spill-existing ] [ spill-new ] if ;
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
! Copyright (C) 2009 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors arrays assocs combinators fry hints kernel locals
|
||||
math sequences sets sorting splitting
|
||||
compiler.cfg.linear-scan.allocation.state
|
||||
compiler.cfg.linear-scan.live-intervals ;
|
||||
IN: compiler.cfg.linear-scan.allocation.splitting
|
||||
|
||||
: split-range ( live-range n -- before after )
|
||||
[ [ from>> ] dip <live-range> ]
|
||||
[ 1 + swap to>> <live-range> ]
|
||||
2bi ;
|
||||
|
||||
: split-last-range? ( last n -- ? )
|
||||
swap to>> <= ;
|
||||
|
||||
: split-last-range ( before after last n -- before' after' )
|
||||
split-range [ [ but-last ] dip suffix ] [ prefix ] bi-curry* bi* ;
|
||||
|
||||
: split-ranges ( live-ranges n -- before after )
|
||||
[ '[ from>> _ <= ] partition ]
|
||||
[
|
||||
pick empty? [ drop ] [
|
||||
[ over last ] dip 2dup split-last-range?
|
||||
[ split-last-range ] [ 2drop ] if
|
||||
] if
|
||||
] bi ;
|
||||
|
||||
: split-uses ( uses n -- before after )
|
||||
'[ _ <= ] partition ;
|
||||
|
||||
: record-split ( live-interval before after -- )
|
||||
[ >>split-next drop ]
|
||||
[ [ >>split-before ] [ >>split-after ] bi* drop ]
|
||||
2bi ; inline
|
||||
|
||||
ERROR: splitting-atomic-interval ;
|
||||
|
||||
: check-split ( live-interval -- )
|
||||
[ end>> ] [ start>> ] bi - 0 =
|
||||
[ splitting-atomic-interval ] when ; inline
|
||||
|
||||
: split-before ( before -- before' )
|
||||
f >>spill-to ; inline
|
||||
|
||||
: split-after ( after -- after' )
|
||||
f >>copy-from f >>reg f >>reload-from ; inline
|
||||
|
||||
:: split-interval ( live-interval n -- before after )
|
||||
live-interval check-split
|
||||
live-interval clone :> before
|
||||
live-interval clone :> after
|
||||
live-interval uses>> n split-uses before after [ (>>uses) ] bi-curry@ bi*
|
||||
live-interval ranges>> n split-ranges before after [ (>>ranges) ] bi-curry@ bi*
|
||||
live-interval before after record-split
|
||||
before split-before
|
||||
after split-after ;
|
||||
|
||||
HINTS: split-interval live-interval object ;
|
||||
|
||||
: reuse-register ( new existing -- )
|
||||
reg>> >>reg add-active ;
|
||||
|
||||
: relevant-ranges ( new inactive -- new' inactive' )
|
||||
! Slice off all ranges of 'inactive' that precede the start of 'new'
|
||||
[ [ ranges>> ] bi@ ] [ nip start>> ] 2bi '[ to>> _ >= ] filter ;
|
||||
|
||||
: intersect-live-range ( range1 range2 -- n/f )
|
||||
2dup [ from>> ] bi@ > [ swap ] when
|
||||
2dup [ to>> ] [ from>> ] bi* >= [ nip from>> ] [ 2drop f ] if ;
|
||||
|
||||
: intersect-live-ranges ( ranges1 ranges2 -- n )
|
||||
{
|
||||
{ [ over empty? ] [ 2drop 1/0. ] }
|
||||
{ [ dup empty? ] [ 2drop 1/0. ] }
|
||||
[
|
||||
2dup [ first ] bi@ intersect-live-range dup [ 2nip ] [
|
||||
drop
|
||||
2dup [ first from>> ] bi@ <
|
||||
[ [ rest-slice ] dip ] [ rest-slice ] if
|
||||
intersect-live-ranges
|
||||
] if
|
||||
]
|
||||
} cond ;
|
||||
|
||||
: intersect-inactive ( new inactive active-regs -- n )
|
||||
2dup [ reg>> ] dip key? [
|
||||
2drop start>>
|
||||
] [
|
||||
drop relevant-ranges intersect-live-ranges
|
||||
] if ;
|
||||
|
||||
: intersecting-inactive ( new -- live-intervals )
|
||||
dup vreg>>
|
||||
[ inactive-intervals-for ]
|
||||
[ active-intervals-for [ reg>> ] map unique ] bi
|
||||
'[ tuck _ intersect-inactive ] with { } map>assoc ;
|
||||
|
||||
: insert-use-for-copy ( seq n -- seq' )
|
||||
[ 1array split1 ] keep [ 1 - ] keep 2array glue ;
|
||||
|
||||
: split-before-use ( new n -- before after )
|
||||
! Find optimal split position
|
||||
! Insert move instruction
|
||||
[ '[ _ insert-use-for-copy ] change-uses ] keep
|
||||
1 - split-interval
|
||||
2dup [ compute-start/end ] bi@ ;
|
||||
|
||||
: assign-inactive-register ( new live-intervals -- )
|
||||
! If there is an interval which is inactive for the entire lifetime
|
||||
! if the new interval, reuse its vreg. Otherwise, split new so that
|
||||
! the first half fits.
|
||||
sort-values last
|
||||
2dup [ end>> ] [ second ] bi* < [
|
||||
first reuse-register
|
||||
] [
|
||||
[ second split-before-use ] keep
|
||||
'[ _ first reuse-register ] [ add-unhandled ] bi*
|
||||
] if ;
|
|
@ -0,0 +1,134 @@
|
|||
! Copyright (C) 2009 Slava Pestov.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors assocs combinators cpu.architecture fry heaps
|
||||
kernel math namespaces sequences vectors
|
||||
compiler.cfg.linear-scan.live-intervals ;
|
||||
IN: compiler.cfg.linear-scan.allocation.state
|
||||
|
||||
! 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
|
||||
SYMBOL: active-intervals
|
||||
|
||||
: active-intervals-for ( vreg -- seq )
|
||||
reg-class>> active-intervals get at ;
|
||||
|
||||
: add-active ( live-interval -- )
|
||||
dup vreg>> active-intervals-for push ;
|
||||
|
||||
: delete-active ( live-interval -- )
|
||||
dup vreg>> active-intervals-for delq ;
|
||||
|
||||
: assign-free-register ( new registers -- )
|
||||
pop >>reg add-active ;
|
||||
|
||||
! Vector of inactive live intervals
|
||||
SYMBOL: inactive-intervals
|
||||
|
||||
: inactive-intervals-for ( vreg -- seq )
|
||||
reg-class>> inactive-intervals get at ;
|
||||
|
||||
: add-inactive ( live-interval -- )
|
||||
dup vreg>> inactive-intervals-for push ;
|
||||
|
||||
! Vector of handled live intervals
|
||||
SYMBOL: handled-intervals
|
||||
|
||||
: add-handled ( live-interval -- )
|
||||
handled-intervals get push ;
|
||||
|
||||
: finished? ( n live-interval -- ? ) end>> swap < ;
|
||||
|
||||
: finish ( n live-interval -- keep? )
|
||||
nip [ deallocate-register ] [ add-handled ] bi f ;
|
||||
|
||||
SYMBOL: check-allocation?
|
||||
|
||||
ERROR: register-already-used live-interval ;
|
||||
|
||||
: check-activate ( live-interval -- )
|
||||
check-allocation? get [
|
||||
dup [ reg>> ] [ vreg>> active-intervals-for [ reg>> ] map ] bi member?
|
||||
[ register-already-used ] [ drop ] if
|
||||
] [ drop ] if ;
|
||||
|
||||
: activate ( n live-interval -- keep? )
|
||||
dup check-activate
|
||||
nip add-active f ;
|
||||
|
||||
: deactivate ( n live-interval -- keep? )
|
||||
nip add-inactive f ;
|
||||
|
||||
: don't-change ( n live-interval -- keep? ) 2drop t ;
|
||||
|
||||
! Moving intervals between active and inactive sets
|
||||
: process-intervals ( n symbol quots -- )
|
||||
! symbol stores an alist mapping register classes to vectors
|
||||
[ get values ] dip '[ [ _ cond ] with filter-here ] with each ; inline
|
||||
|
||||
: deactivate-intervals ( n -- )
|
||||
! Any active intervals which have ended are moved to handled
|
||||
! Any active intervals which cover the current position
|
||||
! are moved to inactive
|
||||
active-intervals {
|
||||
{ [ 2dup finished? ] [ finish ] }
|
||||
{ [ 2dup covers? not ] [ deactivate ] }
|
||||
[ don't-change ]
|
||||
} process-intervals ;
|
||||
|
||||
: activate-intervals ( n -- )
|
||||
! Any inactive intervals which have ended are moved to handled
|
||||
! Any inactive intervals which do not cover the current position
|
||||
! are moved to active
|
||||
inactive-intervals {
|
||||
{ [ 2dup finished? ] [ finish ] }
|
||||
{ [ 2dup covers? ] [ activate ] }
|
||||
[ don't-change ]
|
||||
} process-intervals ;
|
||||
|
||||
! 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 ;
|
||||
|
||||
CONSTANT: reg-classes { int-regs double-float-regs }
|
||||
|
||||
: reg-class-assoc ( quot -- assoc )
|
||||
[ reg-classes ] dip { } map>assoc ; inline
|
||||
|
||||
SYMBOL: spill-counts
|
||||
|
||||
: next-spill-location ( reg-class -- n )
|
||||
spill-counts get [ dup 1 + ] change-at ;
|
||||
|
||||
: init-allocator ( registers -- )
|
||||
[ reverse >vector ] assoc-map free-registers set
|
||||
[ 0 ] reg-class-assoc spill-counts set
|
||||
<min-heap> unhandled-intervals set
|
||||
[ V{ } clone ] reg-class-assoc active-intervals set
|
||||
[ V{ } clone ] reg-class-assoc inactive-intervals set
|
||||
V{ } clone handled-intervals set
|
||||
-1 progress set ;
|
||||
|
||||
: init-unhandled ( live-intervals -- )
|
||||
[ [ start>> ] keep ] { } map>assoc
|
||||
unhandled-intervals get heap-push-all ;
|
|
@ -1,4 +0,0 @@
|
|||
USING: compiler.cfg.linear-scan.assignment tools.test ;
|
||||
IN: compiler.cfg.linear-scan.assignment.tests
|
||||
|
||||
|
|
@ -7,20 +7,16 @@ compiler.cfg.def-use
|
|||
compiler.cfg.registers
|
||||
compiler.cfg.instructions
|
||||
compiler.cfg.linear-scan.allocation
|
||||
compiler.cfg.linear-scan.allocation.state
|
||||
compiler.cfg.linear-scan.live-intervals ;
|
||||
IN: compiler.cfg.linear-scan.assignment
|
||||
|
||||
! A vector of live intervals. There is linear searching involved
|
||||
! but since we never have too many machine registers (around 30
|
||||
! at most) and we probably won't have that many live at any one
|
||||
! time anyway, it is not a problem to check each element.
|
||||
TUPLE: active-intervals seq ;
|
||||
! This contains both active and inactive intervals; any interval
|
||||
! such that start <= insn# <= end is in this set.
|
||||
SYMBOL: pending-intervals
|
||||
|
||||
: add-active ( live-interval -- )
|
||||
active-intervals get seq>> push ;
|
||||
|
||||
: lookup-register ( vreg -- reg )
|
||||
active-intervals get seq>> [ vreg>> = ] with find nip reg>> ;
|
||||
pending-intervals get push ;
|
||||
|
||||
! Minheap of live intervals which still need a register allocation
|
||||
SYMBOL: unhandled-intervals
|
||||
|
@ -37,9 +33,11 @@ SYMBOL: spill-slots
|
|||
: spill-slots-for ( vreg -- assoc )
|
||||
reg-class>> spill-slots get at ;
|
||||
|
||||
ERROR: already-spilled ;
|
||||
|
||||
: record-spill ( live-interval -- )
|
||||
[ dup spill-to>> ] [ vreg>> spill-slots-for ] bi
|
||||
2dup key? [ "BUG: Already spilled" throw ] [ set-at ] if ;
|
||||
2dup key? [ already-spilled ] [ set-at ] if ;
|
||||
|
||||
: insert-spill ( live-interval -- )
|
||||
[ reg>> ] [ vreg>> reg-class>> ] [ spill-to>> ] tri _spill ;
|
||||
|
@ -47,14 +45,27 @@ SYMBOL: spill-slots
|
|||
: handle-spill ( live-interval -- )
|
||||
dup spill-to>> [ [ record-spill ] [ insert-spill ] bi ] [ drop ] if ;
|
||||
|
||||
: insert-copy ( live-interval -- )
|
||||
[ split-next>> reg>> ]
|
||||
[ reg>> ]
|
||||
[ vreg>> reg-class>> ]
|
||||
tri _copy ;
|
||||
|
||||
: handle-copy ( live-interval -- )
|
||||
dup [ spill-to>> not ] [ split-next>> ] bi and
|
||||
[ insert-copy ] [ drop ] if ;
|
||||
|
||||
: expire-old-intervals ( n -- )
|
||||
active-intervals get
|
||||
[ swap '[ end>> _ = ] partition ] change-seq drop
|
||||
[ handle-spill ] each ;
|
||||
[ pending-intervals get ] dip '[
|
||||
dup end>> _ <
|
||||
[ [ handle-spill ] [ handle-copy ] bi f ] [ drop t ] if
|
||||
] filter-here ;
|
||||
|
||||
ERROR: already-reloaded ;
|
||||
|
||||
: record-reload ( live-interval -- )
|
||||
[ reload-from>> ] [ vreg>> spill-slots-for ] bi
|
||||
2dup key? [ delete-at ] [ "BUG: Already reloaded" throw ] if ;
|
||||
2dup key? [ delete-at ] [ already-reloaded ] if ;
|
||||
|
||||
: insert-reload ( live-interval -- )
|
||||
[ reg>> ] [ vreg>> reg-class>> ] [ reload-from>> ] tri _reload ;
|
||||
|
@ -73,39 +84,40 @@ SYMBOL: spill-slots
|
|||
] [ 2drop ] if
|
||||
] if ;
|
||||
|
||||
GENERIC: assign-before ( insn -- )
|
||||
GENERIC: assign-registers-in-insn ( insn -- )
|
||||
|
||||
GENERIC: assign-after ( insn -- )
|
||||
: register-mapping ( live-intervals -- alist )
|
||||
[ [ vreg>> ] [ reg>> ] bi ] { } map>assoc ;
|
||||
|
||||
: all-vregs ( insn -- vregs )
|
||||
[ defs-vregs ] [ temp-vregs ] [ uses-vregs ] tri 3append ;
|
||||
|
||||
M: vreg-insn assign-before
|
||||
active-intervals get seq>> over all-vregs '[ vreg>> _ member? ] filter
|
||||
[ [ vreg>> ] [ reg>> ] bi ] { } map>assoc
|
||||
: active-intervals ( insn -- intervals )
|
||||
insn#>> pending-intervals get [ covers? ] with filter ;
|
||||
|
||||
M: vreg-insn assign-registers-in-insn
|
||||
dup [ active-intervals ] [ all-vregs ] bi
|
||||
'[ vreg>> _ member? ] filter
|
||||
register-mapping
|
||||
>>regs drop ;
|
||||
|
||||
M: insn assign-before drop ;
|
||||
|
||||
: compute-live-registers ( -- regs )
|
||||
active-intervals get seq>> [ [ vreg>> ] [ reg>> ] bi ] { } map>assoc ;
|
||||
: compute-live-registers ( insn -- regs )
|
||||
active-intervals register-mapping ;
|
||||
|
||||
: compute-live-spill-slots ( -- spill-slots )
|
||||
spill-slots get values [ values ] map concat
|
||||
[ [ vreg>> ] [ reload-from>> ] bi ] { } map>assoc ;
|
||||
|
||||
M: ##gc assign-after
|
||||
compute-live-registers >>live-registers
|
||||
M: ##gc assign-registers-in-insn
|
||||
dup call-next-method
|
||||
dup compute-live-registers >>live-registers
|
||||
compute-live-spill-slots >>live-spill-slots
|
||||
drop ;
|
||||
|
||||
M: insn assign-after drop ;
|
||||
|
||||
: <active-intervals> ( -- obj )
|
||||
V{ } clone active-intervals boa ;
|
||||
M: insn assign-registers-in-insn drop ;
|
||||
|
||||
: init-assignment ( live-intervals -- )
|
||||
<active-intervals> active-intervals set
|
||||
V{ } clone pending-intervals set
|
||||
<min-heap> unhandled-intervals set
|
||||
[ H{ } clone ] reg-class-assoc spill-slots set
|
||||
init-unhandled ;
|
||||
|
@ -114,13 +126,15 @@ M: insn assign-after drop ;
|
|||
[
|
||||
[
|
||||
[
|
||||
{
|
||||
[ insn#>> activate-new-intervals ]
|
||||
[ assign-before ]
|
||||
[ , ]
|
||||
[ insn#>> expire-old-intervals ]
|
||||
[ assign-after ]
|
||||
} cleave
|
||||
[
|
||||
insn#>>
|
||||
[ activate-new-intervals ]
|
||||
[ expire-old-intervals ]
|
||||
bi
|
||||
]
|
||||
[ assign-registers-in-insn ]
|
||||
[ , ]
|
||||
tri
|
||||
] each
|
||||
] V{ } make
|
||||
] change-instructions drop ;
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
IN: compiler.cfg.linear-scan.tests
|
||||
USING: tools.test random sorting sequences sets hashtables assocs
|
||||
kernel fry arrays splitting namespaces math accessors vectors
|
||||
kernel fry arrays splitting namespaces math accessors vectors locals
|
||||
math.order grouping
|
||||
cpu.architecture
|
||||
compiler.cfg
|
||||
compiler.cfg.optimizer
|
||||
compiler.cfg.instructions
|
||||
compiler.cfg.registers
|
||||
compiler.cfg.liveness
|
||||
compiler.cfg.predecessors
|
||||
compiler.cfg.rpo
|
||||
compiler.cfg.linear-scan
|
||||
compiler.cfg.linear-scan.live-intervals
|
||||
compiler.cfg.linear-scan.allocation
|
||||
compiler.cfg.linear-scan.allocation.state
|
||||
compiler.cfg.linear-scan.allocation.splitting
|
||||
compiler.cfg.linear-scan.allocation.spilling
|
||||
compiler.cfg.linear-scan.assignment
|
||||
compiler.cfg.linear-scan.debugger ;
|
||||
|
||||
check-allocation? on
|
||||
|
||||
[
|
||||
{ T{ live-range f 1 10 } T{ live-range f 15 15 } }
|
||||
{ T{ live-range f 16 20 } }
|
||||
|
@ -118,32 +127,57 @@ compiler.cfg.linear-scan.debugger ;
|
|||
{ end 5 }
|
||||
{ uses V{ 0 1 5 } }
|
||||
{ ranges V{ T{ live-range f 0 5 } } }
|
||||
} 2 split-interval
|
||||
} 2 split-for-spill [ f >>split-next ] bi@
|
||||
] unit-test
|
||||
|
||||
[
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 0 }
|
||||
{ end 0 }
|
||||
{ uses V{ 0 } }
|
||||
{ ranges V{ T{ live-range f 0 0 } } }
|
||||
}
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 1 }
|
||||
{ end 5 }
|
||||
{ uses V{ 1 5 } }
|
||||
{ ranges V{ T{ live-range f 1 5 } } }
|
||||
}
|
||||
] [
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 0 }
|
||||
{ end 5 }
|
||||
{ uses V{ 0 1 5 } }
|
||||
{ ranges V{ T{ live-range f 0 5 } } }
|
||||
} 0 split-for-spill [ f >>split-next ] bi@
|
||||
] unit-test
|
||||
|
||||
[
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 0 }
|
||||
{ end 0 }
|
||||
{ uses V{ 0 } }
|
||||
{ ranges V{ T{ live-range f 0 0 } } }
|
||||
{ end 4 }
|
||||
{ uses V{ 0 1 4 } }
|
||||
{ ranges V{ T{ live-range f 0 4 } } }
|
||||
}
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 1 }
|
||||
{ start 5 }
|
||||
{ end 5 }
|
||||
{ uses V{ 1 5 } }
|
||||
{ ranges V{ T{ live-range f 1 5 } } }
|
||||
{ uses V{ 5 } }
|
||||
{ ranges V{ T{ live-range f 5 5 } } }
|
||||
}
|
||||
] [
|
||||
T{ live-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 0 }
|
||||
{ end 5 }
|
||||
{ uses V{ 0 1 5 } }
|
||||
{ ranges V{ T{ live-range f 0 5 } } }
|
||||
} 0 split-interval
|
||||
{ vreg T{ vreg { reg-class int-regs } { n 1 } } }
|
||||
{ start 0 }
|
||||
{ end 5 }
|
||||
{ uses V{ 0 1 5 } }
|
||||
{ ranges V{ T{ live-range f 0 5 } } }
|
||||
} 5 split-before-use [ f >>split-next ] bi@
|
||||
] unit-test
|
||||
|
||||
[
|
||||
|
@ -1294,26 +1328,32 @@ USING: math.private compiler.cfg.debugger ;
|
|||
! Spill slot liveness was computed incorrectly, leading to a FEP
|
||||
! early in bootstrap on x86-32
|
||||
[ t ] [
|
||||
T{ basic-block
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##gc f V int-regs 6 V int-regs 7 }
|
||||
T{ ##peek f V int-regs 0 D 0 }
|
||||
T{ ##peek f V int-regs 1 D 1 }
|
||||
T{ ##peek f V int-regs 2 D 2 }
|
||||
T{ ##peek f V int-regs 3 D 3 }
|
||||
T{ ##peek f V int-regs 4 D 4 }
|
||||
T{ ##peek f V int-regs 5 D 5 }
|
||||
T{ ##replace f V int-regs 0 D 1 }
|
||||
T{ ##replace f V int-regs 1 D 2 }
|
||||
T{ ##replace f V int-regs 2 D 3 }
|
||||
T{ ##replace f V int-regs 3 D 4 }
|
||||
T{ ##replace f V int-regs 4 D 5 }
|
||||
T{ ##replace f V int-regs 5 D 0 }
|
||||
}
|
||||
}
|
||||
} dup 1array { { int-regs V{ 0 1 2 3 } } } (linear-scan)
|
||||
instructions>> first live-spill-slots>> empty?
|
||||
[
|
||||
H{ } clone live-ins set
|
||||
H{ } clone live-outs set
|
||||
H{ } clone phi-live-ins set
|
||||
T{ basic-block
|
||||
{ id 12345 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##gc f V int-regs 6 V int-regs 7 }
|
||||
T{ ##peek f V int-regs 0 D 0 }
|
||||
T{ ##peek f V int-regs 1 D 1 }
|
||||
T{ ##peek f V int-regs 2 D 2 }
|
||||
T{ ##peek f V int-regs 3 D 3 }
|
||||
T{ ##peek f V int-regs 4 D 4 }
|
||||
T{ ##peek f V int-regs 5 D 5 }
|
||||
T{ ##replace f V int-regs 0 D 1 }
|
||||
T{ ##replace f V int-regs 1 D 2 }
|
||||
T{ ##replace f V int-regs 2 D 3 }
|
||||
T{ ##replace f V int-regs 3 D 4 }
|
||||
T{ ##replace f V int-regs 4 D 5 }
|
||||
T{ ##replace f V int-regs 5 D 0 }
|
||||
}
|
||||
}
|
||||
} dup 1array { { int-regs V{ 0 1 2 3 } } } (linear-scan)
|
||||
instructions>> first live-spill-slots>> empty?
|
||||
] with-scope
|
||||
] unit-test
|
||||
|
||||
[ f ] [
|
||||
|
@ -1373,5 +1413,388 @@ USING: math.private compiler.cfg.debugger ;
|
|||
{ uses { 5 10 } }
|
||||
{ ranges V{ T{ live-range f 5 10 } } }
|
||||
}
|
||||
H{ }
|
||||
intersect-inactive
|
||||
] unit-test
|
||||
] unit-test
|
||||
|
||||
! Bug in live spill slots calculation
|
||||
|
||||
T{ basic-block
|
||||
{ id 205651 }
|
||||
{ number 0 }
|
||||
{ instructions V{ T{ ##prologue } T{ ##branch } } }
|
||||
} 0 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 205652 }
|
||||
{ number 1 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 703128 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##peek
|
||||
{ dst V int-regs 703129 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 703134 }
|
||||
{ src V int-regs 703128 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 703135 }
|
||||
{ src V int-regs 703129 }
|
||||
}
|
||||
T{ ##compare-imm-branch
|
||||
{ src1 V int-regs 703128 }
|
||||
{ src2 5 }
|
||||
{ cc cc/= }
|
||||
}
|
||||
}
|
||||
}
|
||||
} 1 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 205653 }
|
||||
{ number 2 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##copy
|
||||
{ dst V int-regs 703134 }
|
||||
{ src V int-regs 703129 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 703135 }
|
||||
{ src V int-regs 703128 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 2 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 205655 }
|
||||
{ number 3 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##replace
|
||||
{ src V int-regs 703134 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##replace
|
||||
{ src V int-regs 703135 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##epilogue }
|
||||
T{ ##return }
|
||||
}
|
||||
}
|
||||
} 3 set
|
||||
|
||||
1 get 1vector 0 get (>>successors)
|
||||
2 get 3 get V{ } 2sequence 1 get (>>successors)
|
||||
3 get 1vector 2 get (>>successors)
|
||||
|
||||
:: test-linear-scan-on-cfg ( regs -- )
|
||||
[ ] [
|
||||
cfg new 0 get >>entry
|
||||
compute-predecessors
|
||||
compute-liveness
|
||||
reverse-post-order
|
||||
{ { int-regs regs } } (linear-scan)
|
||||
] unit-test ;
|
||||
|
||||
{ 1 2 } test-linear-scan-on-cfg
|
||||
|
||||
! Bug in inactive interval handling
|
||||
! [ rot dup [ -rot ] when ]
|
||||
T{ basic-block
|
||||
{ id 201486 }
|
||||
{ number 0 }
|
||||
{ instructions V{ T{ ##prologue } T{ ##branch } } }
|
||||
} 0 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201487 }
|
||||
{ number 1 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689473 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689474 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689475 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##compare-imm-branch
|
||||
{ src1 V int-regs 689473 }
|
||||
{ src2 5 }
|
||||
{ cc cc/= }
|
||||
}
|
||||
}
|
||||
}
|
||||
} 1 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201488 }
|
||||
{ number 2 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689481 }
|
||||
{ src V int-regs 689475 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689482 }
|
||||
{ src V int-regs 689474 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689483 }
|
||||
{ src V int-regs 689473 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 2 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201489 }
|
||||
{ number 3 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689481 }
|
||||
{ src V int-regs 689473 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689482 }
|
||||
{ src V int-regs 689475 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689483 }
|
||||
{ src V int-regs 689474 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 3 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201490 }
|
||||
{ number 4 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##replace
|
||||
{ src V int-regs 689481 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##replace
|
||||
{ src V int-regs 689482 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##replace
|
||||
{ src V int-regs 689483 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##epilogue }
|
||||
T{ ##return }
|
||||
}
|
||||
}
|
||||
} 4 set
|
||||
|
||||
: test-diamond ( -- )
|
||||
1 get 1vector 0 get (>>successors)
|
||||
2 get 3 get V{ } 2sequence 1 get (>>successors)
|
||||
4 get 1vector 2 get (>>successors)
|
||||
4 get 1vector 3 get (>>successors) ;
|
||||
|
||||
test-diamond
|
||||
|
||||
{ 1 2 3 4 } test-linear-scan-on-cfg
|
||||
|
||||
! Similar to the above
|
||||
! [ swap dup [ rot ] when ]
|
||||
|
||||
T{ basic-block
|
||||
{ id 201537 }
|
||||
{ number 0 }
|
||||
{ instructions V{ T{ ##prologue } T{ ##branch } } }
|
||||
} 0 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201538 }
|
||||
{ number 1 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689600 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689601 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##compare-imm-branch
|
||||
{ src1 V int-regs 689600 }
|
||||
{ src2 5 }
|
||||
{ cc cc/= }
|
||||
}
|
||||
}
|
||||
}
|
||||
} 1 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201539 }
|
||||
{ number 2 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689604 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689607 }
|
||||
{ src V int-regs 689604 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689608 }
|
||||
{ src V int-regs 689600 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689610 }
|
||||
{ src V int-regs 689601 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 2 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201540 }
|
||||
{ number 3 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 689609 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689607 }
|
||||
{ src V int-regs 689600 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689608 }
|
||||
{ src V int-regs 689601 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 689610 }
|
||||
{ src V int-regs 689609 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 3 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 201541 }
|
||||
{ number 4 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##replace
|
||||
{ src V int-regs 689607 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##replace
|
||||
{ src V int-regs 689608 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##replace
|
||||
{ src V int-regs 689610 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##epilogue }
|
||||
T{ ##return }
|
||||
}
|
||||
}
|
||||
} 4 set
|
||||
|
||||
test-diamond
|
||||
|
||||
{ 1 2 3 4 } test-linear-scan-on-cfg
|
||||
|
||||
! compute-live-registers was inaccurate since it didn't take
|
||||
! lifetime holes into account
|
||||
|
||||
T{ basic-block
|
||||
{ id 0 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 0 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##compare-imm-branch
|
||||
{ src1 V int-regs 0 }
|
||||
{ src2 5 }
|
||||
{ cc cc/= }
|
||||
}
|
||||
}
|
||||
}
|
||||
} 0 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 1 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 1 }
|
||||
{ loc D 1 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 2 }
|
||||
{ src V int-regs 1 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 1 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 2 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##peek
|
||||
{ dst V int-regs 3 }
|
||||
{ loc D 2 }
|
||||
}
|
||||
T{ ##copy
|
||||
{ dst V int-regs 2 }
|
||||
{ src V int-regs 3 }
|
||||
}
|
||||
T{ ##branch }
|
||||
}
|
||||
}
|
||||
} 2 set
|
||||
|
||||
T{ basic-block
|
||||
{ id 3 }
|
||||
{ instructions
|
||||
V{
|
||||
T{ ##replace
|
||||
{ src V int-regs 2 }
|
||||
{ loc D 0 }
|
||||
}
|
||||
T{ ##return }
|
||||
}
|
||||
}
|
||||
} 3 set
|
||||
|
||||
test-diamond
|
||||
|
||||
{ 1 2 3 4 } test-linear-scan-on-cfg
|
|
@ -8,6 +8,7 @@ compiler.cfg.instructions
|
|||
compiler.cfg.linear-scan.numbering
|
||||
compiler.cfg.linear-scan.live-intervals
|
||||
compiler.cfg.linear-scan.allocation
|
||||
compiler.cfg.linear-scan.allocation.state
|
||||
compiler.cfg.linear-scan.assignment ;
|
||||
IN: compiler.cfg.linear-scan
|
||||
|
||||
|
|
|
@ -11,10 +11,21 @@ C: <live-range> live-range
|
|||
|
||||
TUPLE: live-interval
|
||||
vreg
|
||||
reg spill-to reload-from split-before split-after
|
||||
reg spill-to reload-from
|
||||
split-before split-after split-next
|
||||
start end ranges uses
|
||||
copy-from ;
|
||||
|
||||
: covers? ( insn# live-interval -- ? )
|
||||
ranges>> [ [ from>> ] [ to>> ] bi between? ] with any? ;
|
||||
|
||||
: child-interval-at ( insn# interval -- interval' )
|
||||
dup split-after>> [
|
||||
2dup split-after>> start>> <
|
||||
[ split-before>> ] [ split-after>> ] if
|
||||
child-interval-at
|
||||
] [ nip ] if ;
|
||||
|
||||
ERROR: dead-value-error vreg ;
|
||||
|
||||
: shorten-range ( n live-interval -- )
|
||||
|
@ -46,11 +57,9 @@ ERROR: dead-value-error vreg ;
|
|||
V{ } clone >>ranges
|
||||
swap >>vreg ;
|
||||
|
||||
: block-from ( -- n )
|
||||
basic-block get instructions>> first insn#>> ;
|
||||
: block-from ( bb -- n ) instructions>> first insn#>> ;
|
||||
|
||||
: block-to ( -- n )
|
||||
basic-block get instructions>> last insn#>> ;
|
||||
: block-to ( bb -- n ) instructions>> last insn#>> ;
|
||||
|
||||
M: live-interval hashcode*
|
||||
nip [ start>> ] [ end>> 1000 * ] bi + ;
|
||||
|
@ -74,7 +83,7 @@ M: insn compute-live-intervals* drop ;
|
|||
|
||||
: handle-input ( n vreg live-intervals -- )
|
||||
live-interval
|
||||
[ [ block-from ] 2dip add-range ] [ add-use ] 2bi ;
|
||||
[ [ basic-block get block-from ] 2dip add-range ] [ add-use ] 2bi ;
|
||||
|
||||
: handle-temp ( n vreg live-intervals -- )
|
||||
live-interval
|
||||
|
@ -98,7 +107,9 @@ M: ##copy-float compute-live-intervals*
|
|||
[ call-next-method ] [ record-copy ] bi ;
|
||||
|
||||
: handle-live-out ( bb -- )
|
||||
live-out keys block-from block-to live-intervals get '[
|
||||
live-out keys
|
||||
basic-block get [ block-from ] [ block-to ] bi
|
||||
live-intervals get '[
|
||||
[ _ _ ] dip _ live-interval add-range
|
||||
] each ;
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
! Copyright (C) 2009 Slava Pestov
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: accessors assocs kernel math namespaces sequences
|
||||
compiler.cfg.linear-scan.live-intervals compiler.cfg.liveness ;
|
||||
IN: compiler.cfg.linear-scan.resolve
|
||||
|
||||
: add-mapping ( from to -- )
|
||||
2drop
|
||||
;
|
||||
|
||||
: resolve-value-data-flow ( bb to vreg -- )
|
||||
live-intervals get at
|
||||
[ [ block-to ] dip child-interval-at ]
|
||||
[ [ block-from ] dip child-interval-at ]
|
||||
bi-curry bi* 2dup = [ 2drop ] [
|
||||
add-mapping
|
||||
] if ;
|
||||
|
||||
: resolve-mappings ( bb to -- )
|
||||
2drop
|
||||
;
|
||||
|
||||
: resolve-edge-data-flow ( bb to -- )
|
||||
[ 2dup live-in [ resolve-value-data-flow ] with with each ]
|
||||
[ resolve-mappings ]
|
||||
2bi ;
|
||||
|
||||
: resolve-block-data-flow ( bb -- )
|
||||
dup successors>> [
|
||||
resolve-edge-data-flow
|
||||
] with each ;
|
||||
|
||||
: resolve-data-flow ( rpo -- )
|
||||
[ resolve-block-data-flow ] each ;
|
|
@ -7,4 +7,7 @@ IN: compiler.cfg.predecessors
|
|||
dup successors>> [ predecessors>> push ] with each ;
|
||||
|
||||
: compute-predecessors ( cfg -- cfg' )
|
||||
dup [ predecessors-step ] each-basic-block ;
|
||||
[ [ V{ } clone >>predecessors drop ] each-basic-block ]
|
||||
[ [ predecessors-step ] each-basic-block ]
|
||||
[ ]
|
||||
tri ;
|
||||
|
|
|
@ -531,4 +531,10 @@ M: _reload generate-insn
|
|||
{ double-float-regs [ %reload-float ] }
|
||||
} case ;
|
||||
|
||||
M: _copy generate-insn
|
||||
[ dst>> ] [ src>> ] [ class>> ] tri {
|
||||
{ int-regs [ %copy ] }
|
||||
{ double-float-regs [ %copy-float ] }
|
||||
} case ;
|
||||
|
||||
M: _spill-counts generate-insn drop ;
|
||||
|
|
Loading…
Reference in New Issue