1 ! Copyright (C) 2009, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs combinators fry hints kernel locals
4 math sequences sets sorting splitting namespaces linked-assocs
5 combinators.short-circuit compiler.utilities
6 compiler.cfg.linear-scan.allocation.state
7 compiler.cfg.linear-scan.allocation.splitting
8 compiler.cfg.linear-scan.live-intervals ;
9 IN: compiler.cfg.linear-scan.allocation.spilling
11 ERROR: bad-live-ranges interval ;
13 : check-ranges ( live-interval -- )
14 check-allocation? get [
15 dup ranges>> [ [ from>> ] [ to>> ] bi <= ] all?
16 [ drop ] [ bad-live-ranges ] if
19 : trim-before-ranges ( live-interval -- )
20 [ ranges>> ] [ last-use n>> 1 + ] bi
21 [ '[ from>> _ <= ] filter! drop ]
25 : trim-after-ranges ( live-interval -- )
26 [ ranges>> ] [ first-use n>> ] bi
27 [ '[ to>> _ >= ] filter! drop ]
31 : assign-spill ( live-interval -- )
32 dup [ vreg>> ] [ last-use rep>> ] bi
33 assign-spill-slot >>spill-to drop ;
35 : spill-before ( before -- before/f )
36 ! If the interval does not have any usages before the spill location,
37 ! then it is the second child of an interval that was split. We reload
38 ! the value and let the resolve pass insert a split later.
39 dup uses>> empty? [ drop f ] [
43 [ trim-before-ranges ]
49 : assign-reload ( live-interval -- )
50 dup [ vreg>> ] [ first-use rep>> ] bi
51 assign-spill-slot >>reload-from drop ;
53 : spill-after ( after -- after/f )
54 ! If the interval has no more usages after the spill location,
55 ! then it is the first child of an interval that was split. We
56 ! spill the value and let the resolve pass insert a reload later.
57 dup uses>> empty? [ drop f ] [
67 : split-for-spill ( live-interval n -- before after )
68 split-interval [ spill-before ] [ spill-after ] bi* ;
70 : find-use-position ( live-interval new -- n )
71 [ uses>> ] [ start>> '[ n>> _ >= ] ] bi* find nip
72 [ n>> ] [ 1/0. ] if* ;
74 : find-use-positions ( live-intervals new assoc -- )
75 '[ [ _ find-use-position ] [ reg>> ] bi _ add-use-position ] each ;
77 : active-positions ( new assoc -- )
78 [ [ active-intervals-for ] keep ] dip
81 : inactive-positions ( new assoc -- )
83 [ inactive-intervals-for ] keep
84 [ '[ _ intervals-intersect? ] filter ] keep
88 : spill-status ( new -- use-pos )
90 [ inactive-positions ] [ active-positions ] [ nip ] 2tri
93 : spill-new? ( new pair -- ? )
94 [ first-use n>> ] [ second ] bi* > ;
96 : spill-new ( new pair -- )
97 drop spill-after add-unhandled ;
99 : spill ( live-interval n -- )
101 [ [ add-handled ] when* ]
102 [ [ add-unhandled ] when* ] bi* ;
104 :: spill-intersecting-active ( new reg -- )
105 ! If there is an active interval using 'reg' (there should be at
106 ! most one) are split and spilled and removed from the inactive
108 new active-intervals-for [ [ reg>> reg = ] find swap dup ] keep
109 '[ _ remove-nth! drop new start>> spill ] [ 2drop ] if ;
111 :: spill-intersecting-inactive ( new reg -- )
112 ! Any inactive intervals using 'reg' are split and spilled
113 ! and removed from the inactive set.
114 new inactive-intervals-for [
116 dup new intervals-intersect? [
122 : spill-intersecting ( new reg -- )
123 ! Split and spill all active and inactive intervals
124 ! which intersect 'new' and use 'reg'.
125 [ spill-intersecting-active ]
126 [ spill-intersecting-inactive ]
129 : spill-available ( new pair -- )
130 ! A register would become fully available if all
131 ! active and inactive intervals using it were split
133 [ first spill-intersecting ] [ register-available ] 2bi ;
135 : spill-partially-available ( new pair -- )
136 ! A register would be available for part of the new
137 ! interval's lifetime if all active and inactive intervals
138 ! using that register were split and spilled.
139 [ second 1 - split-for-spill [ add-unhandled ] when* ] keep
140 '[ _ spill-available ] when* ;
142 : assign-blocked-register ( new -- )
144 { [ 2dup spill-new? ] [ spill-new ] }
145 { [ 2dup register-available? ] [ spill-available ] }
146 [ spill-partially-available ]