A first attempt at implementing Vivek Sarkar's algorithm for scheduling for register pressure
							parent
							
								
									ce11431fdb
								
							
						
					
					
						commit
						04946dcddd
					
				| 
						 | 
				
			
			@ -0,0 +1,180 @@
 | 
			
		|||
! Copyright (C) 2009, 2010 Daniel Ehrenberg.
 | 
			
		||||
! See http://factorcode.org/license.txt for BSD license.
 | 
			
		||||
USING: accessors assocs combinators compiler.cfg.def-use
 | 
			
		||||
compiler.cfg.instructions compiler.cfg.registers fry kernel
 | 
			
		||||
locals namespaces sequences sets sorting math.vectors
 | 
			
		||||
make math combinators.short-circuit ;
 | 
			
		||||
IN: compiler.cfg.dependence
 | 
			
		||||
 | 
			
		||||
! Dependence graph construction
 | 
			
		||||
 | 
			
		||||
SYMBOL: roots
 | 
			
		||||
SYMBOL: node-number
 | 
			
		||||
SYMBOL: nodes
 | 
			
		||||
 | 
			
		||||
! Nodes in the dependency graph
 | 
			
		||||
! These need to be numbered so that the same instruction
 | 
			
		||||
! will get distinct nodes if it occurs multiple times
 | 
			
		||||
TUPLE: node
 | 
			
		||||
    number insn precedes follows
 | 
			
		||||
    children parent
 | 
			
		||||
    registers parent-index ;
 | 
			
		||||
 | 
			
		||||
M: node equal?  [ number>> ] bi@ = ;
 | 
			
		||||
 | 
			
		||||
M: node hashcode* nip number>> ;
 | 
			
		||||
 | 
			
		||||
: <node> ( insn -- node )
 | 
			
		||||
    node new
 | 
			
		||||
        node-number counter >>number
 | 
			
		||||
        swap >>insn
 | 
			
		||||
        H{ } clone >>precedes
 | 
			
		||||
        H{ } clone >>follows ;
 | 
			
		||||
 | 
			
		||||
: ready? ( node -- ? ) precedes>> assoc-empty? ;
 | 
			
		||||
 | 
			
		||||
: precedes ( first second -- )
 | 
			
		||||
    swap precedes>> conjoin ;
 | 
			
		||||
 | 
			
		||||
:: add-data-edges ( nodes -- )
 | 
			
		||||
    ! This builds up def-use information on the fly, since
 | 
			
		||||
    ! we only care about local def-use
 | 
			
		||||
    H{ } clone :> definers
 | 
			
		||||
    nodes [| node |
 | 
			
		||||
        node insn>> defs-vreg [ node swap definers set-at ] when*
 | 
			
		||||
        node insn>> uses-vregs [ definers at [ node precedes ] when* ] each
 | 
			
		||||
    ] each ;
 | 
			
		||||
 | 
			
		||||
: make-chain ( nodes -- )
 | 
			
		||||
    [ dup rest-slice [ precedes ] 2each ] unless-empty ;
 | 
			
		||||
 | 
			
		||||
: instruction-chain ( nodes quot -- )
 | 
			
		||||
    '[ insn>> @ ] filter make-chain ; inline
 | 
			
		||||
 | 
			
		||||
UNION: stack-read-write ##peek ##replace ;
 | 
			
		||||
UNION: stack-change-height ##inc-d ##inc-r ;
 | 
			
		||||
UNION: stack-insn stack-read-write stack-change-height ;
 | 
			
		||||
 | 
			
		||||
GENERIC: data-stack-insn? ( insn -- ? )
 | 
			
		||||
M: object data-stack-insn? drop f ;
 | 
			
		||||
M: ##inc-d data-stack-insn? drop t ;
 | 
			
		||||
M: stack-read-write data-stack-insn? loc>> ds-loc? ;
 | 
			
		||||
 | 
			
		||||
: retain-stack-insn? ( insn -- ? )
 | 
			
		||||
    dup stack-insn? [ data-stack-insn? not ] [ drop f ] if ;
 | 
			
		||||
 | 
			
		||||
UNION: ##alien-read
 | 
			
		||||
    ##alien-double ##alien-float ##alien-cell ##alien-vector
 | 
			
		||||
    ##alien-signed-1 ##alien-signed-2 ##alien-signed-4
 | 
			
		||||
    ##alien-unsigned-1 ##alien-unsigned-2 ##alien-unsigned-4 ;
 | 
			
		||||
 | 
			
		||||
UNION: ##alien-write
 | 
			
		||||
    ##set-alien-double ##set-alien-float ##set-alien-cell ##set-alien-vector
 | 
			
		||||
    ##set-alien-integer-1 ##set-alien-integer-2 ##set-alien-integer-4 ;
 | 
			
		||||
 | 
			
		||||
UNION: slot-memory-insn
 | 
			
		||||
    ##read ##write ;
 | 
			
		||||
 | 
			
		||||
UNION: alien-memory-insn
 | 
			
		||||
    ##alien-read ##alien-write ;
 | 
			
		||||
 | 
			
		||||
UNION: string-memory-insn
 | 
			
		||||
    ##string-nth ##set-string-nth-fast ;
 | 
			
		||||
 | 
			
		||||
UNION: alien-call-insn
 | 
			
		||||
    ##save-context ##alien-invoke ##alien-indirect ##alien-callback ;
 | 
			
		||||
 | 
			
		||||
: add-control-edges ( nodes -- )
 | 
			
		||||
    {
 | 
			
		||||
        [ [ data-stack-insn? ] instruction-chain ]
 | 
			
		||||
        [ [ retain-stack-insn? ] instruction-chain ]
 | 
			
		||||
        [ [ alien-memory-insn? ] instruction-chain ]
 | 
			
		||||
        [ [ slot-memory-insn? ] instruction-chain ]
 | 
			
		||||
        [ [ string-memory-insn? ] instruction-chain ]
 | 
			
		||||
        [ [ alien-call-insn? ] instruction-chain ]
 | 
			
		||||
    } cleave ;
 | 
			
		||||
 | 
			
		||||
: set-follows ( nodes -- )
 | 
			
		||||
    [
 | 
			
		||||
        dup precedes>> values [
 | 
			
		||||
            follows>> conjoin
 | 
			
		||||
        ] with each
 | 
			
		||||
    ] each ;
 | 
			
		||||
 | 
			
		||||
: set-roots ( nodes -- )
 | 
			
		||||
    [ ready? ] filter V{ } like roots set ;
 | 
			
		||||
 | 
			
		||||
: build-dependence-graph ( instructions -- )
 | 
			
		||||
    [ <node> ] map {
 | 
			
		||||
        [ add-data-edges ]
 | 
			
		||||
        [ add-control-edges ]
 | 
			
		||||
        [ set-follows ]
 | 
			
		||||
        [ nodes set ] ! for assertions later
 | 
			
		||||
        [ set-roots ]
 | 
			
		||||
    } cleave ;
 | 
			
		||||
 | 
			
		||||
:: calculate-registers ( node -- registers )
 | 
			
		||||
    node children>> [ 0 ] [
 | 
			
		||||
        [ [ calculate-registers ] map natural-sort ]
 | 
			
		||||
        [ length iota ]
 | 
			
		||||
        bi v+ supremum
 | 
			
		||||
    ] if-empty
 | 
			
		||||
    node insn>> temp-vregs length +
 | 
			
		||||
    dup node (>>registers) ;
 | 
			
		||||
 | 
			
		||||
: data-dependence? ( to from -- ? )
 | 
			
		||||
    ! If this takes lots of time, then refactor code
 | 
			
		||||
    ! so that nodes store their data dependences
 | 
			
		||||
    [ insn>> ] bi@
 | 
			
		||||
    [ uses-vregs ] [ defs-vreg ] bi*
 | 
			
		||||
    swap member? ;
 | 
			
		||||
 | 
			
		||||
DEFER: follow-tree
 | 
			
		||||
 | 
			
		||||
: maybe-cut-node ( node -- ? )
 | 
			
		||||
    ! If this node has multiple successors
 | 
			
		||||
    ! then it needs to be made into the head of a new tree
 | 
			
		||||
    [ precedes>> assoc-size 1 = dup ] keep
 | 
			
		||||
    '[ _ dup , follow-tree ] when ;
 | 
			
		||||
 | 
			
		||||
: follow-tree ( node -- )
 | 
			
		||||
    ! This is bogus: it misses nodes that aren't reachable
 | 
			
		||||
    ! from the roots because of a control dependence
 | 
			
		||||
    dup dup follows>> values
 | 
			
		||||
    [ data-dependence? ] with filter
 | 
			
		||||
    [ parent>> not ] filter
 | 
			
		||||
    [ maybe-cut-node ] filter
 | 
			
		||||
    
 | 
			
		||||
    [ [ >>parent drop ] with each ]
 | 
			
		||||
    [ >>children drop ] 2bi ;
 | 
			
		||||
 | 
			
		||||
ERROR: node-missing-parent trees nodes ;
 | 
			
		||||
ERROR: node-missing-children trees nodes ;
 | 
			
		||||
 | 
			
		||||
: flatten-tree ( node -- nodes )
 | 
			
		||||
    [ children>> [ flatten-tree ] map concat ] keep
 | 
			
		||||
    suffix ;
 | 
			
		||||
 | 
			
		||||
: verify-parents ( trees -- trees )
 | 
			
		||||
    nodes get over '[ [ parent>> ] [ _ member? ] bi or ] all?
 | 
			
		||||
    [ nodes get node-missing-parent ] unless ;
 | 
			
		||||
 | 
			
		||||
: verify-children ( trees -- trees )
 | 
			
		||||
    dup [ flatten-tree ] map concat
 | 
			
		||||
    nodes get
 | 
			
		||||
    { [ [ length ] bi@ = ] [ set= ] } 2&&
 | 
			
		||||
    [ nodes get node-missing-children ] unless ;
 | 
			
		||||
 | 
			
		||||
: verify-trees ( trees -- trees )
 | 
			
		||||
    verify-parents verify-children ;
 | 
			
		||||
 | 
			
		||||
: make-trees ( -- trees )
 | 
			
		||||
    [
 | 
			
		||||
        roots get [ dup , follow-tree ] each
 | 
			
		||||
    ] { } make verify-trees ;
 | 
			
		||||
 | 
			
		||||
: build-fan-in-trees ( -- )
 | 
			
		||||
    make-trees [
 | 
			
		||||
        -1/0. >>parent-index 
 | 
			
		||||
        calculate-registers drop
 | 
			
		||||
    ] each ;
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ compiler.cfg.value-numbering
 | 
			
		|||
compiler.cfg.copy-prop
 | 
			
		||||
compiler.cfg.dce
 | 
			
		||||
compiler.cfg.write-barrier
 | 
			
		||||
compiler.cfg.scheduling
 | 
			
		||||
compiler.cfg.representations
 | 
			
		||||
compiler.cfg.ssa.destruction
 | 
			
		||||
compiler.cfg.empty-blocks
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,7 @@ SYMBOL: check-optimizer?
 | 
			
		|||
    copy-propagation
 | 
			
		||||
    eliminate-dead-code
 | 
			
		||||
    eliminate-write-barriers
 | 
			
		||||
    schedule-instructions
 | 
			
		||||
    select-representations
 | 
			
		||||
    destruct-ssa
 | 
			
		||||
    delete-empty-blocks
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
! Copyright (C) 2009, 2010 Daniel Ehrenberg.
 | 
			
		||||
! See http://factorcode.org/license.txt for BSD license.
 | 
			
		||||
USING: accessors assocs combinators compiler.cfg.def-use
 | 
			
		||||
compiler.cfg.dependence compiler.cfg.instructions
 | 
			
		||||
compiler.cfg.registers compiler.cfg.rpo continuations
 | 
			
		||||
cpu.architecture fry hashtables kernel locals math math.order
 | 
			
		||||
namespaces random sequences sets vectors literals make
 | 
			
		||||
arrays
 | 
			
		||||
compiler.cfg.liveness
 | 
			
		||||
compiler.cfg.liveness.ssa ;
 | 
			
		||||
IN: compiler.cfg.scheduling
 | 
			
		||||
 | 
			
		||||
! Instruction scheduling to reduce register pressure, from:
 | 
			
		||||
! "Register-sensitive selection, duplication, and
 | 
			
		||||
!  sequencing of instructions"
 | 
			
		||||
! by Vivek Sarkar, et al.
 | 
			
		||||
! http://portal.acm.org/citation.cfm?id=377849
 | 
			
		||||
 | 
			
		||||
ERROR: bad-delete-at key assoc ;
 | 
			
		||||
 | 
			
		||||
: check-delete-at ( key assoc -- )
 | 
			
		||||
    2dup key? [ delete-at ] [ bad-delete-at ] if ;
 | 
			
		||||
 | 
			
		||||
: set-parent-indices ( node -- )
 | 
			
		||||
    children>> building get length
 | 
			
		||||
    '[ _ >>parent-index drop ] each ;
 | 
			
		||||
 | 
			
		||||
: remove-node ( node -- )
 | 
			
		||||
    [ follows>> values ] keep
 | 
			
		||||
    '[ [ precedes>> _ swap check-delete-at ] each ]
 | 
			
		||||
    [ [ ready? ] filter roots get push-all ] bi ;
 | 
			
		||||
 | 
			
		||||
: score ( insn -- n )
 | 
			
		||||
    [ parent-index>> ] [ registers>> neg ] bi 2array ;
 | 
			
		||||
 | 
			
		||||
: pull-out-nth ( n seq -- elt )
 | 
			
		||||
    [ nth ] [ remove-nth! drop ] 2bi ;
 | 
			
		||||
 | 
			
		||||
: select ( vector quot -- elt )
 | 
			
		||||
    ! This could be sped up by a constant factor
 | 
			
		||||
    [ dup <enum> ] dip '[ _ call( insn -- score ) ] assoc-map
 | 
			
		||||
    dup values supremum '[ nip _ = ] assoc-find
 | 
			
		||||
    2drop swap pull-out-nth ; inline
 | 
			
		||||
 | 
			
		||||
: select-instruction ( -- insn/f )
 | 
			
		||||
    roots get [ f ] [
 | 
			
		||||
        [ score ] select 
 | 
			
		||||
        [ insn>> ]
 | 
			
		||||
        [ set-parent-indices ]
 | 
			
		||||
        [ remove-node ] tri
 | 
			
		||||
    ] if-empty ;
 | 
			
		||||
 | 
			
		||||
: (reorder) ( -- )
 | 
			
		||||
    select-instruction [
 | 
			
		||||
        , (reorder)
 | 
			
		||||
    ] when* ;
 | 
			
		||||
 | 
			
		||||
: cut-by ( seq quot -- before after )
 | 
			
		||||
    dupd find drop [ cut ] [ f ] if* ; inline
 | 
			
		||||
 | 
			
		||||
: split-3-ways ( insns -- first middle last )
 | 
			
		||||
    [ ##phi? not ] cut-by unclip-last ;
 | 
			
		||||
 | 
			
		||||
: reorder ( insns -- insns' )
 | 
			
		||||
    split-3-ways [
 | 
			
		||||
        build-dependence-graph
 | 
			
		||||
        build-fan-in-trees
 | 
			
		||||
        [ (reorder) ] V{ } make reverse
 | 
			
		||||
    ] dip suffix append ;
 | 
			
		||||
 | 
			
		||||
ERROR: not-all-instructions-were-scheduled old-bb new-bb ;
 | 
			
		||||
 | 
			
		||||
SYMBOL: check-scheduling?
 | 
			
		||||
t check-scheduling? set-global
 | 
			
		||||
 | 
			
		||||
:: check-instructions ( new-bb old-bb -- )
 | 
			
		||||
    new-bb old-bb [ instructions>> ] bi@
 | 
			
		||||
    [ [ length ] bi@ = ] [ [ unique ] bi@ = ] 2bi and
 | 
			
		||||
    [ old-bb new-bb not-all-instructions-were-scheduled ] unless ;
 | 
			
		||||
 | 
			
		||||
ERROR: definition-after-usage vreg old-bb new-bb ;
 | 
			
		||||
 | 
			
		||||
:: check-usages ( new-bb old-bb -- )
 | 
			
		||||
    H{ } clone :> useds
 | 
			
		||||
    new-bb instructions>> split-3-ways drop nip
 | 
			
		||||
    [| insn |
 | 
			
		||||
        insn uses-vregs [ useds conjoin ] each
 | 
			
		||||
        insn defs-vreg :> def-reg
 | 
			
		||||
        def-reg useds key?
 | 
			
		||||
        [ def-reg old-bb new-bb definition-after-usage ] when
 | 
			
		||||
    ] each ;
 | 
			
		||||
 | 
			
		||||
: check-scheduling ( new-bb old-bb -- )
 | 
			
		||||
    [ check-instructions ] [ check-usages ] 2bi ;
 | 
			
		||||
 | 
			
		||||
: with-scheduling-check ( bb quot: ( bb -- ) -- )
 | 
			
		||||
    check-scheduling? get [
 | 
			
		||||
        over dup clone
 | 
			
		||||
        [ call( bb -- ) ] 2dip
 | 
			
		||||
        check-scheduling
 | 
			
		||||
    ] [
 | 
			
		||||
        call( bb -- )
 | 
			
		||||
    ] if ; inline
 | 
			
		||||
 | 
			
		||||
: number-insns ( insns -- )
 | 
			
		||||
    [ >>insn# drop ] each-index ;
 | 
			
		||||
 | 
			
		||||
: clear-numbers ( insns -- )
 | 
			
		||||
    [ f >>insn# drop ] each ;
 | 
			
		||||
 | 
			
		||||
: schedule-block ( bb -- )
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            [ number-insns ]
 | 
			
		||||
            [ reorder ]
 | 
			
		||||
            [ clear-numbers ] tri
 | 
			
		||||
        ] change-instructions drop
 | 
			
		||||
    ] with-scheduling-check ;
 | 
			
		||||
 | 
			
		||||
! Really, instruction scheduling should be aware that there are
 | 
			
		||||
! multiple types of registers, but this number is just used
 | 
			
		||||
! to decide whether to schedule instructions
 | 
			
		||||
: num-registers ( -- x ) int-regs machine-registers at length ;
 | 
			
		||||
 | 
			
		||||
: update-vregs ( insn vregs -- )
 | 
			
		||||
    [ [ defs-vreg ] dip '[ _ delete-at ] when* ]
 | 
			
		||||
    [ [ uses-vregs ] dip '[ _ conjoin ] each ] 2bi ;
 | 
			
		||||
 | 
			
		||||
:: (might-spill?) ( vregs insns -- ? )
 | 
			
		||||
    insns <reversed> [
 | 
			
		||||
        [ vregs update-vregs ]
 | 
			
		||||
        [ temp-vregs length vregs assoc-size + num-registers > ] bi
 | 
			
		||||
    ] any? ;
 | 
			
		||||
 | 
			
		||||
: might-spill? ( bb -- ? )
 | 
			
		||||
    ! Conservative approximation testing whether a bb might spill
 | 
			
		||||
    ! by calculating register pressure all along, assuming
 | 
			
		||||
    ! everything in live-out are in registers
 | 
			
		||||
    ! This is done bottom-up: a def means the register is no longer live
 | 
			
		||||
    [ live-out H{ } assoc-clone-like ] [ instructions>> ] bi (might-spill?) ;
 | 
			
		||||
 | 
			
		||||
: schedule-instructions ( cfg -- cfg' )
 | 
			
		||||
    dup [
 | 
			
		||||
        dup might-spill?
 | 
			
		||||
        [ schedule-block ]
 | 
			
		||||
        [ drop ] if
 | 
			
		||||
    ] each-basic-block ;
 | 
			
		||||
		Loading…
	
		Reference in New Issue