compiler.cfg.*: new system for emitting spill/reloads for gc calls (#1471)

GC calls were treated as sync points so all registers were spilled
around them. But if the gc call isn't triggered, it is unnecessary to
spill. This commit fixes that by handling ##call-gc specially and
putting ##spill and ##reloads in the same block as the gc call itself.
locals-and-roots
Björn Lindqvist 2016-04-22 18:35:10 +02:00
parent d5cb972a71
commit 70e9834f2f
9 changed files with 129 additions and 67 deletions

View File

@ -357,10 +357,13 @@ HELP: gc-map
{ $table { $table
{ {
{ $slot "gc-roots" } { $slot "gc-roots" }
{ "First a " { $link sequence } " of vregs that will be spilled during a gc. It is assigned in the " { $vocab-link "compiler.cfg.liveness" } " compiler pass. Then it is converted to a sequence of " { $link spill-slot } "s in " { $link assign-registers } "." } { { $link sequence } " of vregs or spill-slots" }
} }
{ { $slot "derived-roots" } { "An " { $link assoc } " of pairs of spill slots." } } {
{ $slot "derived-roots" }
{ "An " { $link assoc } " of pairs of vregs or spill slots." } }
} }
"The 'gc-roots' and 'derived-roots' slots are initially vreg integers referencing objects that are live during the gc call and needs to be spilled so that they can be traced. In the " { $link emit-gc-map-insn } " word in " { $vocab-link "compiler.cfg.linear-scan.assignment" } " they are converted to spill slots which the collector is able to trace."
} }
{ $see-also emit-gc-info-bitmaps fill-gc-map } ; { $see-also emit-gc-info-bitmaps fill-gc-map } ;

View File

@ -1,22 +1,15 @@
USING: assocs compiler.cfg compiler.cfg.instructions USING: assocs compiler.cfg compiler.cfg.instructions
compiler.cfg.linear-scan.live-intervals compiler.cfg.linear-scan.allocation compiler.cfg.linear-scan.allocation
compiler.cfg.linear-scan.allocation.state compiler.cfg.liveness compiler.cfg.linear-scan.allocation.state
compiler.cfg.registers heaps help.markup help.syntax math sequences ; compiler.cfg.linear-scan.live-intervals compiler.cfg.liveness
compiler.cfg.registers heaps help.markup help.syntax math quotations
sequences ;
IN: compiler.cfg.linear-scan.assignment IN: compiler.cfg.linear-scan.assignment
HELP: add-pending HELP: add-pending
{ $values { "live-interval" live-interval-state } } { $values { "live-interval" live-interval-state } }
{ $description "Adds a live interval to the pending interval set." } ; { $description "Adds a live interval to the pending interval set." } ;
HELP: assign-derived-roots
{ $values { "gc-map" gc-map } }
{ $description "Assigns pairs of spill slots for all derived roots in a gc map." } ;
{ assign-gc-roots assign-derived-roots } related-words
HELP: assign-gc-roots
{ $values { "gc-map" gc-map } }
{ $description "Assigns spill slots for all gc roots in a gc map." } ;
HELP: assign-registers-in-block HELP: assign-registers-in-block
{ $values { "bb" basic-block } } { $values { "bb" basic-block } }
{ $description "Assigns registers to vregs and also inserts " { $link ##reload } " and " { $link ##spill } " instructions." } ; { $description "Assigns registers to vregs and also inserts " { $link ##reload } " and " { $link ##spill } " instructions." } ;
@ -29,14 +22,22 @@ HELP: assign-all-registers
{ $values { "insn" insn } } { $values { "insn" insn } }
{ $description "Assigns physical registers for the virtual registers used and defined by the instruction." } ; { $description "Assigns physical registers for the virtual registers used and defined by the instruction." } ;
HELP: change-insn-gc-roots
{ $values { "gc-map-insn" gc-map-insn } { "quot" quotation } }
{ $description "Applies the quotation to all vregs in the instructions " { $link gc-map } "." } ;
HELP: compute-live-in HELP: compute-live-in
{ $values { "bb" basic-block } } { $values { "bb" basic-block } }
{ $description "Computes the live in registers for a basic block." } { $description "Computes the live in registers for a basic block." }
{ $see-also machine-live-ins } ; { $see-also machine-live-ins } ;
HELP: emit-##call-gc
{ $values { "insn" ##call-gc } }
{ $description "Emits a " { $link ##call-gc } " instruction and the " { $link ##reload } " and " { $link ##spill } " instructions it requires. ##call-gc aren't counted as sync points, so the instruction requires special handling." } ;
HELP: expire-old-intervals HELP: expire-old-intervals
{ $values { "n" integer } { "pending-heap" min-heap } } { $values { "n" integer } { "pending-heap" min-heap } }
{ $description "Expires all intervals older than the cutoff point." } ; { $description "Expires all intervals older than the cutoff point. First they are removed from the 'pending-heap' and " { $link pending-interval-assoc } ". Then " { $link ##spill } " instructions are inserted for each interval that was removed." } ;
HELP: insert-reload HELP: insert-reload
{ $values { "live-interval" live-interval-state } } { $values { "live-interval" live-interval-state } }
@ -74,15 +75,28 @@ HELP: vreg>spill-slot
{ $description "Converts a vreg number to a spill slot." } ; { $description "Converts a vreg number to a spill slot." } ;
ARTICLE: "compiler.cfg.linear-scan.assignment" "Assigning registers to live intervals" ARTICLE: "compiler.cfg.linear-scan.assignment" "Assigning registers to live intervals"
"The " { $vocab-link "compiler.cfg.linear-scan.assignment" } " assigns registers to live intervals." $nl "The " { $vocab-link "compiler.cfg.linear-scan.assignment" } " assigns registers to live intervals. Before this compiler pass, all values in the " { $link cfg } " were represented as simple integers called \"virtual registers\" or vregs. In this pass, using the live interval data computed in the register allocation pass (" { $vocab-link "compiler.cfg.linear-scan.allocation" } "), those vregs are translated into physical registers."
$nl
"Since there is an infinite number of vregs but the number of physical registers is limited, some values must be spilled. So this pass also handles spilling decisions and inserts " { $link ##spill } " and " { $link ##reload } " instructions where needed."
$nl
"GC maps:"
{ $subsections
change-insn-gc-roots
emit-##call-gc
}
"Pending intervals:" "Pending intervals:"
{ $subsections { $subsections
activate-interval activate-interval
add-pending add-pending
pending-interval-assoc pending-interval-assoc
expire-old-intervals
remove-pending remove-pending
} }
"Vreg transformations:" "Vreg transformations:"
{ $subsections vreg>reg vreg>spill-slot vregs>regs } ; { $subsections
vreg>reg
vreg>spill-slot
vregs>regs
} ;
ABOUT: "compiler.cfg.linear-scan.assignment" ABOUT: "compiler.cfg.linear-scan.assignment"

View File

@ -1,9 +1,10 @@
USING: accessors arrays compiler.cfg compiler.cfg.instructions USING: accessors arrays compiler.cfg.instructions
compiler.cfg.linear-scan.allocation.state compiler.cfg.linear-scan.assignment compiler.cfg.linear-scan.allocation.state
compiler.cfg.linear-scan.assignment
compiler.cfg.linear-scan.live-intervals compiler.cfg.registers compiler.cfg.linear-scan.live-intervals compiler.cfg.registers
compiler.cfg.ssa.destruction.leaders compiler.cfg.utilities cpu.architecture compiler.cfg.ssa.destruction.leaders compiler.cfg.utilities
cpu.x86.assembler.operands grouping heaps kernel make namespaces random cpu.architecture cpu.x86.assembler.operands heaps kernel make
sequences sorting tools.test ; namespaces sequences sorting tools.test ;
IN: compiler.cfg.linear-scan.assignment.tests IN: compiler.cfg.linear-scan.assignment.tests
: cherry-pick ( seq indices -- seq' ) : cherry-pick ( seq indices -- seq' )
@ -49,14 +50,6 @@ IN: compiler.cfg.linear-scan.assignment.tests
} live-intervals>min-heap [ activate-new-intervals ] { } make } live-intervals>min-heap [ activate-new-intervals ] { } make
] unit-test ] unit-test
! assign-gc-roots
{
T{ gc-map { gc-roots { T{ spill-slot { n 7 } } } } }
} [
{ { 23 int-rep 23 T{ spill-slot { n 7 } } } } setup-vreg-spills
<gc-map> 23 1array >>gc-roots [ assign-gc-roots ] keep
] unit-test
! assign-insn-defs ! assign-insn-defs
{ {
T{ ##peek { dst RAX } { loc T{ ds-loc } } { insn# 0 } } T{ ##peek { dst RAX } { loc T{ ds-loc } } { insn# 0 } }
@ -97,9 +90,11 @@ IN: compiler.cfg.linear-scan.assignment.tests
] unit-test ] unit-test
! expire-old-intervals ! expire-old-intervals
{ 3 } [ { 3 H{ } } [
H{ { 25 RBX } } clone pending-interval-assoc set
90 { 50 90 95 120 } [ 25 <live-interval> 2array ] map >min-heap 90 { 50 90 95 120 } [ 25 <live-interval> 2array ] map >min-heap
[ expire-old-intervals ] keep heap-size [ expire-old-intervals ] keep heap-size
pending-interval-assoc get
] unit-test ] unit-test
! insert-reload ! insert-reload

View File

@ -1,22 +1,26 @@
! Copyright (C) 2008, 2010 Slava Pestov. ! Copyright (C) 2008, 2010 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors assocs combinators compiler.cfg USING: accessors arrays assocs combinators compiler.cfg
compiler.cfg.instructions compiler.cfg.linear-scan.allocation.state compiler.cfg.instructions compiler.cfg.linear-scan.allocation.state
compiler.cfg.linear-scan.live-intervals compiler.cfg.linearization compiler.cfg.linear-scan.live-intervals compiler.cfg.linearization
compiler.cfg.liveness compiler.cfg.registers compiler.cfg.liveness compiler.cfg.registers
compiler.cfg.renaming.functor compiler.cfg.ssa.destruction.leaders fry compiler.cfg.renaming.functor compiler.cfg.ssa.destruction.leaders
heaps kernel make math namespaces sequences ; compiler.cfg.utilities fry heaps kernel make math namespaces sequences
;
IN: compiler.cfg.linear-scan.assignment IN: compiler.cfg.linear-scan.assignment
QUALIFIED: sets
: heap-pop-while ( heap quot: ( key -- ? ) -- values )
'[ dup heap-empty? [ f f ] [ dup heap-peek @ ] if ]
[ over heap-pop* ] produce 2nip ; inline
! This contains both active and inactive intervals; any interval ! This contains both active and inactive intervals; any interval
! such that start <= insn# <= end is in this set. ! such that start <= insn# <= end is in this set.
SYMBOL: pending-interval-heap SYMBOL: pending-interval-heap
SYMBOL: pending-interval-assoc SYMBOL: pending-interval-assoc
: insert-spill ( live-interval -- )
[ reg>> ] [ spill-rep>> ] [ spill-to>> ] tri ##spill, ;
: handle-spill ( live-interval -- )
dup spill-to>> [ insert-spill ] [ drop ] if ;
: add-pending ( live-interval -- ) : add-pending ( live-interval -- )
[ dup live-interval-end pending-interval-heap get heap-push ] [ dup live-interval-end pending-interval-heap get heap-push ]
[ [ reg>> ] [ vreg>> ] bi pending-interval-assoc get set-at ] [ [ reg>> ] [ vreg>> ] bi pending-interval-assoc get set-at ]
@ -25,16 +29,13 @@ SYMBOL: pending-interval-assoc
: remove-pending ( live-interval -- ) : remove-pending ( live-interval -- )
vreg>> pending-interval-assoc get delete-at ; vreg>> pending-interval-assoc get delete-at ;
: vreg>spill-slot ( vreg -- slot )
dup rep-of lookup-spill-slot ;
: vreg>reg ( vreg -- reg/spill-slot ) : vreg>reg ( vreg -- reg/spill-slot )
dup leader dup pending-interval-assoc get at dup leader dup pending-interval-assoc get at
[ 2nip ] [ swap rep-of lookup-spill-slot ] if* ; [ 2nip ] [ swap rep-of lookup-spill-slot ] if* ;
ERROR: not-spilled-error vreg ;
: vreg>spill-slot ( vreg -- spill-slot )
dup vreg>reg dup spill-slot?
[ nip ] [ drop leader not-spilled-error ] if ;
: vregs>regs ( assoc -- assoc' ) : vregs>regs ( assoc -- assoc' )
[ vreg>reg ] assoc-map ; [ vreg>reg ] assoc-map ;
@ -65,12 +66,6 @@ SYMBOL: machine-live-outs
: compute-live-out ( bb -- ) : compute-live-out ( bb -- )
[ live-out vregs>regs ] keep machine-live-outs get set-at ; [ live-out vregs>regs ] keep machine-live-outs get set-at ;
: insert-spill ( live-interval -- )
[ reg>> ] [ spill-rep>> ] [ spill-to>> ] tri ##spill, ;
: handle-spill ( live-interval -- )
dup spill-to>> [ insert-spill ] [ drop ] if ;
: expire-interval ( live-interval -- ) : expire-interval ( live-interval -- )
[ remove-pending ] [ handle-spill ] bi ; [ remove-pending ] [ handle-spill ] bi ;
@ -98,12 +93,6 @@ RENAMING: assign [ vreg>reg ] [ vreg>reg ] [ vreg>reg ]
: assign-all-registers ( insn -- ) : assign-all-registers ( insn -- )
[ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ; [ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ;
: assign-gc-roots ( gc-map -- )
gc-roots>> [ vreg>spill-slot ] map! drop ;
: assign-derived-roots ( gc-map -- )
[ [ [ vreg>spill-slot ] bi@ ] assoc-map ] change-derived-roots drop ;
: begin-block ( bb -- ) : begin-block ( bb -- )
{ {
[ basic-block namespaces:set ] [ basic-block namespaces:set ]
@ -112,8 +101,39 @@ RENAMING: assign [ vreg>reg ] [ vreg>reg ] [ vreg>reg ]
[ compute-live-in ] [ compute-live-in ]
} cleave ; } cleave ;
: handle-gc-map-insn ( insn -- ) : change-insn-gc-roots ( gc-map-insn quot: ( x -- x ) -- )
dup , gc-map>> [ assign-gc-roots ] [ assign-derived-roots ] bi ; [ gc-map>> ] dip [ swap gc-roots>> swap map! drop ]
[ '[ [ [ @ ] bi@ ] assoc-map ] change-derived-roots drop ] 2bi ; inline
: spill-required? ( live-interval root-leaders n -- ? )
[ [ vreg>> ] dip sets:in? ] [ swap covers? ] bi-curry* bi or ;
: spill-intervals ( root-leaders n -- live-intervals )
[ pending-interval-heap get heap-members ] 2dip
'[ _ _ spill-required? ] filter ;
: spill/reload ( interval -- {reg,rep,slot} )
[ reg>> ] [ vreg>> dup rep-of dup swapd assign-spill-slot ] bi 3array ;
: spill/reloads ( intervals -- spill/reloads )
[ spill/reload ] map ;
: spill/reloads-for-call-gc ( ##call-gc -- spill-seq )
[ gc-map>> gc-roots>> ] [ insn#>> ] bi spill-intervals spill/reloads ;
: emit-##call-gc ( insn -- )
dup spill/reloads-for-call-gc
dup [ first3 ##spill, ] each
swap ,
[ first3 ##reload, ] each ;
: emit-gc-map-insn ( gc-map-insn -- )
[ [ leader ] change-insn-gc-roots ]
[ dup ##call-gc? [ emit-##call-gc ] [ , ] if ]
[ [ vreg>spill-slot ] change-insn-gc-roots ] tri ;
: emit-insn ( insn -- )
dup gc-map-insn? [ emit-gc-map-insn ] [ , ] if ;
: assign-registers-in-block ( bb -- ) : assign-registers-in-block ( bb -- )
dup begin-block dup begin-block
@ -122,7 +142,7 @@ RENAMING: assign [ vreg>reg ] [ vreg>reg ] [ vreg>reg ]
[ [
[ insn#>> prepare-insn ] [ insn#>> prepare-insn ]
[ assign-all-registers ] [ assign-all-registers ]
[ dup gc-map-insn? [ handle-gc-map-insn ] [ , ] if ] tri [ emit-insn ] tri
] each ] each
] V{ } make ] V{ } make
] change-instructions compute-live-out ; ] change-instructions compute-live-out ;

View File

@ -25,7 +25,7 @@ HELP: cfg>sync-points
{ $see-also sync-point } ; { $see-also sync-point } ;
HELP: clobber-insn HELP: clobber-insn
{ $class-description "Instructions that clobber registers but are allowed to produce outputs in registers. Inputs are in spill slots, except for inputs coalesced with the output, in which case that input will be in a register." } ; { $class-description "Instructions that clobber registers but are allowed to produce outputs in registers. Inputs are in spill slots, except for inputs coalesced with the output, in which case that input will be in a register. Each instruction that is a member of the clobber-insn class requires a " { $link sync-point } "." } ;
HELP: compute-live-intervals HELP: compute-live-intervals
{ $values { "cfg" cfg } { "intervals/sync-points" sequence } } { $values { "cfg" cfg } { "intervals/sync-points" sequence } }
@ -48,7 +48,8 @@ HELP: from
{ $var-description "An integer representing a sequence number one lower than all numbers in the currently processed block." } ; { $var-description "An integer representing a sequence number one lower than all numbers in the currently processed block." } ;
HELP: hairy-clobber-insn HELP: hairy-clobber-insn
{ $class-description "Instructions that clobber registers. They receive inputs and produce outputs in spill slots." } ; { $class-description "Instructions that clobber registers. They receive inputs and produce outputs in spill slots." }
{ $notes "The " { $link ##call-gc } " instruction is not included in the class even though it clobbers registers because it is handled specially." } ;
HELP: insn>sync-point HELP: insn>sync-point
{ $values { "insn" insn } { "sync-point/f" { $maybe sync-point } } } { $values { "insn" insn } { "sync-point/f" { $maybe sync-point } } }
@ -135,6 +136,8 @@ $nl
"Sync point handling:" "Sync point handling:"
{ $subsections { $subsections
cfg>sync-points cfg>sync-points
clobber-insn
hairy-clobber-insn
insn>sync-point insn>sync-point
} ; } ;

View File

@ -11,9 +11,14 @@ IN: compiler.cfg.linear-scan.live-intervals.tests
! cfg>sync-points ! cfg>sync-points
{ {
V{ T{ sync-point { n 0 } } } V{
T{ sync-point { n 2 } }
}
} [ } [
V{ T{ ##call-gc } } insns>cfg V{
T{ ##call-gc }
T{ ##callback-inputs }
} insns>cfg
[ number-instructions ] [ cfg>sync-points ] bi [ number-instructions ] [ cfg>sync-points ] bi
] unit-test ] unit-test
@ -35,7 +40,7 @@ IN: compiler.cfg.linear-scan.live-intervals.tests
! insn>sync-point ! insn>sync-point
{ f f t } [ { f f t } [
T{ ##call-gc } insn>sync-point keep-dst?>> T{ ##call-gc } insn>sync-point
T{ ##callback-outputs } insn>sync-point keep-dst?>> T{ ##callback-outputs } insn>sync-point keep-dst?>>
T{ ##unbox } insn>sync-point keep-dst?>> T{ ##unbox } insn>sync-point keep-dst?>>
] unit-test ] unit-test

View File

@ -87,7 +87,6 @@ M: insn compute-live-intervals* drop ;
] [ uses-vregs ] if ; ] [ uses-vregs ] if ;
UNION: hairy-clobber-insn UNION: hairy-clobber-insn
##call-gc
alien-call-insn alien-call-insn
##callback-inputs ##callback-inputs
##callback-outputs ##callback-outputs

View File

@ -1,4 +1,5 @@
USING: compiler.cfg help.markup help.syntax sequences ; USING: assocs compiler.cfg hashtables help.markup help.syntax
sequences ;
IN: compiler.cfg.utilities IN: compiler.cfg.utilities
HELP: connect-Nto1-bbs HELP: connect-Nto1-bbs
@ -8,3 +9,18 @@ HELP: connect-Nto1-bbs
HELP: insert-basic-block HELP: insert-basic-block
{ $values { "from" basic-block } { "to" basic-block } { "insns" sequence } } { $values { "from" basic-block } { "to" basic-block } { "insns" sequence } }
{ $description "Insert basic block on the edge between 'from' and 'to'." } ; { $description "Insert basic block on the edge between 'from' and 'to'." } ;
ARTICLE: "compiler.cfg.utilities" "Utility words used by CFG optimization"
"Various utilities."
$nl
"For " { $vocab-link "heaps" } ":"
{ $subsections
heap-members
heap-pop-while
}
"For " { $vocab-link "deques" } ":"
{ $subsections
slurp/replenish-deque
} ;
ABOUT: "compiler.cfg.utilities"

View File

@ -2,7 +2,7 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors assocs combinators.short-circuit compiler.cfg USING: accessors assocs combinators.short-circuit compiler.cfg
compiler.cfg.instructions compiler.cfg.rpo cpu.architecture deques fry compiler.cfg.instructions compiler.cfg.rpo cpu.architecture deques fry
kernel locals make math namespaces sequences sets ; heaps kernel locals math sequences sets ;
IN: compiler.cfg.utilities IN: compiler.cfg.utilities
: block>cfg ( bb -- cfg ) : block>cfg ( bb -- cfg )
@ -92,3 +92,10 @@ IN: compiler.cfg.utilities
: slurp/replenish-deque ( ... deque quot: ( ... obj -- ... seq ) -- ... ) : slurp/replenish-deque ( ... deque quot: ( ... obj -- ... seq ) -- ... )
over '[ @ _ push-all-front ] slurp-deque ; inline over '[ @ _ push-all-front ] slurp-deque ; inline
: heap-members ( heap -- seq )
data>> [ value>> ] map ;
: heap-pop-while ( heap quot: ( key -- ? ) -- values )
'[ dup heap-empty? [ f f ] [ dup heap-peek @ ] if ]
[ over heap-pop* ] produce 2nip ; inline