diff --git a/basis/bitstreams/bitstreams.factor b/basis/bitstreams/bitstreams.factor index cb6a753735..2aa0059542 100644 --- a/basis/bitstreams/bitstreams.factor +++ b/basis/bitstreams/bitstreams.factor @@ -1,10 +1,10 @@ ! Copyright (C) 2009 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: accessors alien.accessors assocs byte-arrays combinators -constructors destructors fry io io.binary io.encodings.binary -io.streams.byte-array kernel locals macros math math.ranges -multiline sequences sequences.private vectors byte-vectors -combinators.short-circuit math.bitwise ; +destructors fry io io.binary io.encodings.binary io.streams.byte-array +kernel locals macros math math.ranges multiline sequences +sequences.private vectors byte-vectors combinators.short-circuit +math.bitwise ; IN: bitstreams TUPLE: widthed { bits integer read-only } { #bits integer read-only } ; @@ -36,8 +36,12 @@ TUPLE: bit-writer TUPLE: msb0-bit-reader < bit-reader ; TUPLE: lsb0-bit-reader < bit-reader ; -CONSTRUCTOR: msb0-bit-reader ( bytes -- bs ) ; -CONSTRUCTOR: lsb0-bit-reader ( bytes -- bs ) ; + +: ( bytes -- bs ) + msb0-bit-reader new swap >>bytes ; inline + +: ( bytes -- bs ) + lsb0-bit-reader new swap >>bytes ; inline TUPLE: msb0-bit-writer < bit-writer ; TUPLE: lsb0-bit-writer < bit-writer ; @@ -56,13 +60,20 @@ TUPLE: lsb0-bit-writer < bit-writer ; GENERIC: peek ( n bitstream -- value ) GENERIC: poke ( value n bitstream -- ) +: get-abp ( bitstream -- abp ) + [ byte-pos>> 8 * ] [ bit-pos>> + ] bi ; inline + +: set-abp ( abp bitstream -- ) + [ 8 /mod ] dip [ (>>bit-pos) ] [ (>>byte-pos) ] bi ; inline + : seek ( n bitstream -- ) - { - [ byte-pos>> 8 * ] - [ bit-pos>> + + 8 /mod ] - [ (>>bit-pos) ] - [ (>>byte-pos) ] - } cleave ; inline + [ get-abp + ] [ set-abp ] bi ; inline + +: (align) ( n m -- n' ) + [ /mod 0 > [ 1+ ] when ] [ * ] bi ; inline + +: align ( n bitstream -- ) + [ get-abp swap (align) ] [ set-abp ] bi ; inline : read ( n bitstream -- value ) [ peek ] [ seek ] 2bi ; inline @@ -158,3 +169,9 @@ M: msb0-bit-reader peek ( n bs -- bits ) \ be> \ subseq>bits-be (peek) ; writer bytes>> swap push ] unless writer bytes>> ; + +:: byte-array-n>seq ( byte-array n -- seq ) + byte-array length 8 * n / iota + byte-array '[ + drop n _ read + ] { } map-as ; diff --git a/basis/circular/circular-tests.factor b/basis/circular/circular-tests.factor index 3a94e14640..b4a9d547f2 100644 --- a/basis/circular/circular-tests.factor +++ b/basis/circular/circular-tests.factor @@ -13,6 +13,7 @@ circular strings ; [ [ 1 2 3 ] ] [ { 1 2 3 } [ ] like ] unit-test [ [ 2 3 1 ] ] [ { 1 2 3 } [ rotate-circular ] keep [ ] like ] unit-test +[ [ 3 1 2 ] ] [ { 1 2 3 } [ rotate-circular ] keep [ rotate-circular ] keep [ ] like ] unit-test [ [ 2 3 1 ] ] [ { 1 2 3 } 1 over change-circular-start [ ] like ] unit-test [ [ 3 1 2 ] ] [ { 1 2 3 } 1 over change-circular-start 1 over change-circular-start [ ] like ] unit-test [ [ 3 1 2 ] ] [ { 1 2 3 } -100 over change-circular-start [ ] like ] unit-test diff --git a/basis/circular/circular.factor b/basis/circular/circular.factor index ae79e70d73..d47b954ecf 100644 --- a/basis/circular/circular.factor +++ b/basis/circular/circular.factor @@ -28,10 +28,10 @@ M: circular virtual-seq seq>> ; circular-wrap (>>start) ; : rotate-circular ( circular -- ) - [ start>> 1 + ] keep circular-wrap (>>start) ; + [ 1 ] dip change-circular-start ; : push-circular ( elt circular -- ) - [ set-first ] [ 1 swap change-circular-start ] bi ; + [ set-first ] [ rotate-circular ] bi ; : ( n -- circular ) 0 ; diff --git a/basis/compiler/cfg/alias-analysis/alias-analysis-tests.factor b/basis/compiler/cfg/alias-analysis/alias-analysis-tests.factor index 81359690db..79165f2c96 100644 --- a/basis/compiler/cfg/alias-analysis/alias-analysis-tests.factor +++ b/basis/compiler/cfg/alias-analysis/alias-analysis-tests.factor @@ -1,56 +1 @@ -USING: compiler.cfg.instructions compiler.cfg.registers -compiler.cfg.alias-analysis compiler.cfg.debugger -cpu.architecture tools.test kernel ; IN: compiler.cfg.alias-analysis.tests - -[ ] [ - { - T{ ##peek f V int-regs 2 D 1 f } - T{ ##box-alien f V int-regs 1 V int-regs 2 } - T{ ##slot-imm f V int-regs 3 V int-regs 1 0 3 } - } alias-analysis drop -] unit-test - -[ ] [ - { - T{ ##load-reference f V int-regs 1 "hello" } - T{ ##slot-imm f V int-regs 0 V int-regs 1 0 3 } - } alias-analysis drop -] unit-test - -[ - { - T{ ##peek f V int-regs 1 D 1 f } - T{ ##peek f V int-regs 2 D 2 f } - T{ ##replace f V int-regs 1 D 0 f } - } -] [ - { - T{ ##peek f V int-regs 1 D 1 f } - T{ ##peek f V int-regs 2 D 2 f } - T{ ##replace f V int-regs 2 D 0 f } - T{ ##replace f V int-regs 1 D 0 f } - } alias-analysis -] unit-test - -[ - { - T{ ##peek f V int-regs 1 D 1 f } - T{ ##peek f V int-regs 2 D 0 f } - T{ ##copy f V int-regs 3 V int-regs 2 f } - T{ ##copy f V int-regs 4 V int-regs 1 f } - T{ ##replace f V int-regs 3 D 0 f } - T{ ##replace f V int-regs 4 D 1 f } - } -] [ - { - T{ ##peek f V int-regs 1 D 1 f } - T{ ##peek f V int-regs 2 D 0 f } - T{ ##replace f V int-regs 1 D 0 f } - T{ ##replace f V int-regs 2 D 1 f } - T{ ##peek f V int-regs 3 D 1 f } - T{ ##peek f V int-regs 4 D 0 f } - T{ ##replace f V int-regs 3 D 0 f } - T{ ##replace f V int-regs 4 D 1 f } - } alias-analysis -] unit-test diff --git a/basis/compiler/cfg/alias-analysis/alias-analysis.factor b/basis/compiler/cfg/alias-analysis/alias-analysis.factor index 2a9d2579e3..d0bb792f72 100644 --- a/basis/compiler/cfg/alias-analysis/alias-analysis.factor +++ b/basis/compiler/cfg/alias-analysis/alias-analysis.factor @@ -1,15 +1,13 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel math namespaces assocs hashtables sequences arrays accessors vectors combinators sets classes compiler.cfg compiler.cfg.registers compiler.cfg.instructions -compiler.cfg.copy-prop ; +compiler.cfg.copy-prop compiler.cfg.rpo +compiler.cfg.liveness compiler.cfg.local ; IN: compiler.cfg.alias-analysis -! Alias analysis -- assumes compiler.cfg.height has already run. -! -! We try to eliminate redundant slot and stack -! traffic using some simple heuristics. +! We try to eliminate redundant slot operations using some simple heuristics. ! ! All heap-allocated objects which are loaded from the stack, or ! other object slots are pessimistically assumed to belong to @@ -17,9 +15,6 @@ IN: compiler.cfg.alias-analysis ! ! Freshly-allocated objects get their own alias class. ! -! The data and retain stack pointer registers are treated -! uniformly, and each one gets its own alias class. -! ! Simple pseudo-C example showing load elimination: ! ! int *x, *y, z: inputs @@ -68,15 +63,14 @@ IN: compiler.cfg.alias-analysis ! Map vregs -> alias classes SYMBOL: vregs>acs -: check ( obj -- obj ) - [ "BUG: static type error detected" throw ] unless* ; inline - +ERROR: vreg-ac-not-set vreg ; + : vreg>ac ( vreg -- ac ) #! Only vregs produced by ##allot, ##peek and ##slot can #! ever be used as valid inputs to ##slot and ##set-slot, #! so we assert this fact by not giving alias classes to #! other vregs. - vregs>acs get at check ; + vregs>acs get ?at [ vreg-ac-not-set ] unless ; ! Map alias classes -> sequence of vregs SYMBOL: acs>vregs @@ -122,8 +116,10 @@ SYMBOL: histories #! value. over [ live-slots get at at ] [ 2drop f ] if ; +ERROR: vreg-has-no-slots vreg ; + : load-constant-slot ( value slot# vreg -- ) - live-slots get at check set-at ; + live-slots get ?at [ vreg-has-no-slots ] unless set-at ; : load-slot ( value slot#/f vreg -- ) over [ load-constant-slot ] [ 3drop ] if ; @@ -189,67 +185,49 @@ SYMBOL: constants GENERIC: insn-slot# ( insn -- slot#/f ) GENERIC: insn-object ( insn -- vreg ) -M: ##peek insn-slot# loc>> n>> ; -M: ##replace insn-slot# loc>> n>> ; M: ##slot insn-slot# slot>> constant ; M: ##slot-imm insn-slot# slot>> ; M: ##set-slot insn-slot# slot>> constant ; M: ##set-slot-imm insn-slot# slot>> ; M: ##alien-global insn-slot# [ library>> ] [ symbol>> ] bi 2array ; -M: ##peek insn-object loc>> class ; -M: ##replace insn-object loc>> class ; M: ##slot insn-object obj>> resolve ; M: ##slot-imm insn-object obj>> resolve ; M: ##set-slot insn-object obj>> resolve ; M: ##set-slot-imm insn-object obj>> resolve ; M: ##alien-global insn-object drop \ ##alien-global ; -: init-alias-analysis ( -- ) +: init-alias-analysis ( live-in -- ) H{ } clone histories set H{ } clone vregs>acs set H{ } clone acs>vregs set H{ } clone live-slots set H{ } clone constants set H{ } clone copies set - + 0 ac-counter set next-ac heap-ac set - ds-loc next-ac set-ac - rs-loc next-ac set-ac ; + [ set-heap-ac ] each ; GENERIC: analyze-aliases* ( insn -- insn' ) M: ##load-immediate analyze-aliases* dup [ val>> ] [ dst>> ] bi constants get set-at ; -M: ##load-reference analyze-aliases* +M: ##flushable analyze-aliases* dup dst>> set-heap-ac ; -M: ##alien-global analyze-aliases* - dup dst>> set-heap-ac ; - -M: ##allot analyze-aliases* - #! A freshly allocated object is distinct from any other - #! object. - dup dst>> set-new-ac ; - -M: ##box-float analyze-aliases* - #! A freshly allocated object is distinct from any other - #! object. - dup dst>> set-new-ac ; - -M: ##box-alien analyze-aliases* +M: ##allocation analyze-aliases* #! A freshly allocated object is distinct from any other #! object. dup dst>> set-new-ac ; M: ##read analyze-aliases* - dup dst>> set-heap-ac + call-next-method dup [ dst>> ] [ insn-slot# ] [ insn-object ] tri 2dup live-slot dup [ - 2nip f \ ##copy boa analyze-aliases* nip + 2nip \ ##copy new-insn analyze-aliases* nip ] [ drop remember-slot ] if ; @@ -292,15 +270,6 @@ GENERIC: eliminate-dead-stores* ( insn -- insn' ) ] unless ] when ; -M: ##replace eliminate-dead-stores* - #! Writes to above the top of the stack can be pruned also. - #! This is sound since any such writes are not observable - #! after the basic block, and any reads of those locations - #! will have been converted to copies by analyze-slot, - #! and the final stack height of the basic block is set at - #! the beginning by compiler.cfg.stack. - dup loc>> n>> 0 < [ drop f ] [ (eliminate-dead-stores) ] if ; - M: ##set-slot eliminate-dead-stores* (eliminate-dead-stores) ; M: ##set-slot-imm eliminate-dead-stores* (eliminate-dead-stores) ; @@ -310,8 +279,10 @@ M: insn eliminate-dead-stores* ; : eliminate-dead-stores ( insns -- insns' ) [ insn# set eliminate-dead-stores* ] map-index sift ; -: alias-analysis ( insns -- insns' ) - init-alias-analysis +: alias-analysis-step ( insns -- insns' ) analyze-aliases compute-live-stores eliminate-dead-stores ; + +: alias-analysis ( cfg -- cfg' ) + [ init-alias-analysis ] [ alias-analysis-step ] local-optimization ; \ No newline at end of file diff --git a/basis/compiler/cfg/build-stack-frame/build-stack-frame.factor b/basis/compiler/cfg/build-stack-frame/build-stack-frame.factor new file mode 100644 index 0000000000..e5be2d9eb9 --- /dev/null +++ b/basis/compiler/cfg/build-stack-frame/build-stack-frame.factor @@ -0,0 +1,74 @@ +! Copyright (C) 2008 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: namespaces accessors math.order assocs kernel sequences +combinators make classes words cpu.architecture +compiler.cfg.instructions compiler.cfg.registers +compiler.cfg.stack-frame ; +IN: compiler.cfg.build-stack-frame + +SYMBOL: frame-required? + +SYMBOL: spill-counts + +GENERIC: compute-stack-frame* ( insn -- ) + +: request-stack-frame ( stack-frame -- ) + stack-frame [ max-stack-frame ] change ; + +M: ##stack-frame compute-stack-frame* + frame-required? on + stack-frame>> request-stack-frame ; + +M: ##call compute-stack-frame* + word>> sub-primitive>> [ frame-required? on ] unless ; + +M: _gc compute-stack-frame* + frame-required? on + stack-frame new swap gc-root-size>> >>gc-root-size + request-stack-frame ; + +M: _spill-counts compute-stack-frame* + counts>> stack-frame get (>>spill-counts) ; + +M: insn compute-stack-frame* + class frame-required? word-prop [ + frame-required? on + ] when ; + +\ _spill t frame-required? set-word-prop +\ ##fixnum-add t frame-required? set-word-prop +\ ##fixnum-sub t frame-required? set-word-prop +\ ##fixnum-mul t frame-required? set-word-prop +\ ##fixnum-add-tail f frame-required? set-word-prop +\ ##fixnum-sub-tail f frame-required? set-word-prop +\ ##fixnum-mul-tail f frame-required? set-word-prop + +: compute-stack-frame ( insns -- ) + frame-required? off + T{ stack-frame } clone stack-frame set + [ compute-stack-frame* ] each + stack-frame get dup stack-frame-size >>total-size drop ; + +GENERIC: insert-pro/epilogues* ( insn -- ) + +M: ##stack-frame insert-pro/epilogues* drop ; + +M: ##prologue insert-pro/epilogues* + drop frame-required? get [ stack-frame get _prologue ] when ; + +M: ##epilogue insert-pro/epilogues* + drop frame-required? get [ stack-frame get _epilogue ] when ; + +M: insn insert-pro/epilogues* , ; + +: insert-pro/epilogues ( insns -- insns ) + [ [ insert-pro/epilogues* ] each ] { } make ; + +: build-stack-frame ( mr -- mr ) + [ + [ + [ compute-stack-frame ] + [ insert-pro/epilogues ] + bi + ] change-instructions + ] with-scope ; diff --git a/basis/compiler/cfg/stack-frame/summary.txt b/basis/compiler/cfg/build-stack-frame/summary.txt similarity index 100% rename from basis/compiler/cfg/stack-frame/summary.txt rename to basis/compiler/cfg/build-stack-frame/summary.txt diff --git a/basis/compiler/cfg/builder/builder.factor b/basis/compiler/cfg/builder/builder.factor index 4b521725fe..d323263fc7 100755 --- a/basis/compiler/cfg/builder/builder.factor +++ b/basis/compiler/cfg/builder/builder.factor @@ -15,6 +15,7 @@ compiler.cfg.iterator compiler.cfg.utilities compiler.cfg.registers compiler.cfg.intrinsics +compiler.cfg.stack-frame compiler.cfg.instructions compiler.alien ; IN: compiler.cfg.builder @@ -81,30 +82,35 @@ GENERIC: emit-node ( node -- next ) basic-block get successors>> push stop-iterating ; -: emit-call ( word -- next ) +: emit-call ( word height -- next ) { - { [ dup loops get key? ] [ loops get at local-recursive-call ] } + { [ over loops get key? ] [ drop loops get at local-recursive-call ] } + { [ terminate-call? ] [ ##call stop-iterating ] } { [ tail-call? not ] [ ##call ##branch begin-basic-block iterate-next ] } - { [ dup current-label get eq? ] [ drop first-basic-block get local-recursive-call ] } - [ ##epilogue ##jump stop-iterating ] + { [ dup current-label get eq? ] [ 2drop first-basic-block get local-recursive-call ] } + [ drop ##epilogue ##jump stop-iterating ] } cond ; ! #recursive -: compile-recursive ( node -- next ) - [ label>> id>> emit-call ] +: recursive-height ( #recursive -- n ) + [ label>> return>> in-d>> length ] [ in-d>> length ] bi - ; + +: emit-recursive ( #recursive -- next ) + [ [ label>> id>> ] [ recursive-height ] bi emit-call ] [ [ child>> ] [ label>> word>> ] [ label>> id>> ] tri (build-cfg) ] bi ; : remember-loop ( label -- ) basic-block get swap loops get set-at ; -: compile-loop ( node -- next ) +: emit-loop ( node -- next ) ##loop-entry + ##branch begin-basic-block [ label>> id>> remember-loop ] [ child>> emit-nodes ] bi iterate-next ; M: #recursive emit-node - dup label>> loop?>> [ compile-loop ] [ compile-recursive ] if ; + dup label>> loop?>> [ emit-loop ] [ emit-recursive ] if ; ! #if : emit-branch ( obj -- final-bb ) @@ -154,65 +160,16 @@ M: #if emit-node } cond iterate-next ; ! #dispatch -: trivial-dispatch-branch? ( nodes -- ? ) - dup length 1 = [ - first dup #call? [ - word>> "intrinsic" word-prop not - ] [ drop f ] if - ] [ drop f ] if ; - -: dispatch-branch ( nodes word -- label ) - over trivial-dispatch-branch? [ - drop first word>> - ] [ - gensym [ - [ - V{ } clone node-stack set - ##prologue - begin-basic-block - emit-nodes - basic-block get [ - ##epilogue - ##return - end-basic-block - ] when - ] with-cfg-builder - ] keep - ] if ; - -: dispatch-branches ( node -- ) - children>> [ - current-word get dispatch-branch - ##dispatch-label - ] each ; - -: emit-dispatch ( node -- ) - ##epilogue - ds-pop ^^offset>slot i 0 ##dispatch - dispatch-branches ; - -: ( -- word ) - gensym dup t "inlined-block" set-word-prop ; - M: #dispatch emit-node - tail-call? [ - emit-dispatch stop-iterating - ] [ - current-word get [ - [ - begin-word - emit-dispatch - ] with-cfg-builder - ] keep emit-call - ] if ; + ds-pop ^^offset>slot i ##dispatch emit-if iterate-next ; ! #call M: #call emit-node dup word>> dup "intrinsic" word-prop - [ emit-intrinsic ] [ nip emit-call ] if ; + [ emit-intrinsic ] [ swap call-height emit-call ] if ; ! #call-recursive -M: #call-recursive emit-node label>> id>> emit-call ; +M: #call-recursive emit-node [ label>> id>> ] [ call-height ] bi emit-call ; ! #push M: #push emit-node diff --git a/basis/compiler/cfg/cfg.factor b/basis/compiler/cfg/cfg.factor index 054b4f7ed0..dabc7338d2 100644 --- a/basis/compiler/cfg/cfg.factor +++ b/basis/compiler/cfg/cfg.factor @@ -1,27 +1,37 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: kernel arrays vectors accessors namespaces ; +USING: kernel arrays vectors accessors +namespaces math make fry sequences ; IN: compiler.cfg TUPLE: basic-block < identity-tuple -id +{ id integer } number { instructions vector } { successors vector } { predecessors vector } ; -: ( -- basic-block ) +M: basic-block hashcode* nip id>> ; + +: ( -- bb ) basic-block new V{ } clone >>instructions V{ } clone >>successors V{ } clone >>predecessors \ basic-block counter >>id ; -TUPLE: cfg { entry basic-block } word label ; +: add-instructions ( bb quot -- ) + [ instructions>> building ] dip '[ + building get pop + _ dip + building get push + ] with-variable ; inline -C: cfg +TUPLE: cfg { entry basic-block } word label spill-counts post-order ; -TUPLE: mr { instructions array } word label spill-counts ; +: ( entry word label -- cfg ) f f cfg boa ; + +TUPLE: mr { instructions array } word label ; : ( instructions word label -- mr ) mr new diff --git a/basis/constructors/authors.txt b/basis/compiler/cfg/checker/authors.txt similarity index 100% rename from basis/constructors/authors.txt rename to basis/compiler/cfg/checker/authors.txt diff --git a/basis/compiler/cfg/checker/checker.factor b/basis/compiler/cfg/checker/checker.factor new file mode 100644 index 0000000000..4f215f1dc8 --- /dev/null +++ b/basis/compiler/cfg/checker/checker.factor @@ -0,0 +1,61 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: kernel compiler.cfg.instructions compiler.cfg.rpo +compiler.cfg.def-use compiler.cfg.linearization compiler.cfg.liveness +combinators.short-circuit accessors math sequences sets assocs ; +IN: compiler.cfg.checker + +ERROR: last-insn-not-a-jump insn ; + +: check-last-instruction ( bb -- ) + last dup { + [ ##branch? ] + [ ##dispatch? ] + [ ##conditional-branch? ] + [ ##compare-imm-branch? ] + [ ##return? ] + [ ##callback-return? ] + [ ##jump? ] + [ ##fixnum-add-tail? ] + [ ##fixnum-sub-tail? ] + [ ##fixnum-mul-tail? ] + [ ##call? ] + } 1|| [ drop ] [ last-insn-not-a-jump ] if ; + +ERROR: bad-loop-entry ; + +: check-loop-entry ( bb -- ) + dup length 2 >= [ + 2 head* [ ##loop-entry? ] any? + [ bad-loop-entry ] when + ] [ drop ] if ; + +ERROR: bad-successors ; + +: check-successors ( bb -- ) + dup successors>> [ predecessors>> memq? ] with all? + [ bad-successors ] unless ; + +: check-basic-block ( bb -- ) + [ instructions>> check-last-instruction ] + [ instructions>> check-loop-entry ] + [ check-successors ] + tri ; + +ERROR: bad-live-in ; + +ERROR: undefined-values uses defs ; + +: check-mr ( mr -- ) + ! Check that every used register has a definition + instructions>> + [ [ uses-vregs ] map concat ] + [ [ [ defs-vregs ] [ temp-vregs ] bi append ] map concat ] bi + 2dup subset? [ 2drop ] [ undefined-values ] if ; + +: check-cfg ( cfg -- ) + compute-liveness + [ entry>> live-in assoc-empty? [ bad-live-in ] unless ] + [ [ check-basic-block ] each-basic-block ] + [ flatten-cfg check-mr ] + tri ; diff --git a/basis/compiler/cfg/copy-prop/copy-prop.factor b/basis/compiler/cfg/copy-prop/copy-prop.factor index 52cc75f047..d526ea9c1d 100644 --- a/basis/compiler/cfg/copy-prop/copy-prop.factor +++ b/basis/compiler/cfg/copy-prop/copy-prop.factor @@ -6,7 +6,7 @@ IN: compiler.cfg.copy-prop SYMBOL: copies : resolve ( vreg -- vreg ) - dup copies get at swap or ; + [ copies get at ] keep or ; : record-copy ( insn -- ) [ src>> resolve ] [ dst>> ] bi copies get set-at ; inline diff --git a/basis/compiler/cfg/dce/authors.txt b/basis/compiler/cfg/dce/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/dce/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/dce/dce.factor b/basis/compiler/cfg/dce/dce.factor new file mode 100644 index 0000000000..68c89be455 --- /dev/null +++ b/basis/compiler/cfg/dce/dce.factor @@ -0,0 +1,45 @@ +! Copyright (C) 2008, 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors assocs sets kernel namespaces sequences +compiler.cfg.instructions compiler.cfg.def-use +compiler.cfg.rpo ; +IN: compiler.cfg.dce + +! Maps vregs to sequences of vregs +SYMBOL: liveness-graph + +! vregs which participate in side effects and thus are always live +SYMBOL: live-vregs + +: init-dead-code ( -- ) + H{ } clone liveness-graph set + H{ } clone live-vregs set ; + +GENERIC: update-liveness-graph ( insn -- ) + +M: ##flushable update-liveness-graph + [ uses-vregs ] [ dst>> ] bi liveness-graph get set-at ; + +: record-live ( vregs -- ) + [ + dup live-vregs get key? [ drop ] [ + [ live-vregs get conjoin ] + [ liveness-graph get at record-live ] + bi + ] if + ] each ; + +M: insn update-liveness-graph uses-vregs record-live ; + +GENERIC: live-insn? ( insn -- ? ) + +M: ##flushable live-insn? dst>> live-vregs get key? ; + +M: insn live-insn? drop t ; + +: eliminate-dead-code ( cfg -- cfg' ) + init-dead-code + [ [ instructions>> [ update-liveness-graph ] each ] each-basic-block ] + [ [ [ [ live-insn? ] filter ] change-instructions drop ] each-basic-block ] + [ ] + tri ; \ No newline at end of file diff --git a/basis/compiler/cfg/dead-code/dead-code-tests.factor b/basis/compiler/cfg/dead-code/dead-code-tests.factor deleted file mode 100644 index ee7d8d2a43..0000000000 --- a/basis/compiler/cfg/dead-code/dead-code-tests.factor +++ /dev/null @@ -1,9 +0,0 @@ -USING: compiler.cfg.dead-code compiler.cfg.instructions -compiler.cfg.registers compiler.cfg.debugger -cpu.architecture tools.test ; -IN: compiler.cfg.dead-code.tests - -[ { } ] [ - { T{ ##load-immediate f V int-regs 134 16 } } - eliminate-dead-code -] unit-test diff --git a/basis/compiler/cfg/dead-code/dead-code.factor b/basis/compiler/cfg/dead-code/dead-code.factor deleted file mode 100644 index 73aa7b4a5a..0000000000 --- a/basis/compiler/cfg/dead-code/dead-code.factor +++ /dev/null @@ -1,61 +0,0 @@ -! Copyright (C) 2008 Slava Pestov. -! See http://factorcode.org/license.txt for BSD license. -USING: accessors assocs sets kernel namespaces sequences -compiler.cfg.instructions compiler.cfg.def-use ; -IN: compiler.cfg.dead-code - -! Dead code elimination -- assumes compiler.cfg.alias-analysis -! has already run. - -! Maps vregs to sequences of vregs -SYMBOL: liveness-graph - -! vregs which participate in side effects and thus are always live -SYMBOL: live-vregs - -! mapping vregs to stack locations -SYMBOL: vregs>locs - -: init-dead-code ( -- ) - H{ } clone liveness-graph set - H{ } clone live-vregs set - H{ } clone vregs>locs set ; - -GENERIC: compute-liveness ( insn -- ) - -M: ##flushable compute-liveness - [ uses-vregs ] [ dst>> ] bi liveness-graph get set-at ; - -M: ##peek compute-liveness - [ [ loc>> ] [ dst>> ] bi vregs>locs get set-at ] - [ call-next-method ] - bi ; - -: live-replace? ( ##replace -- ? ) - [ src>> vregs>locs get at ] [ loc>> ] bi = not ; - -M: ##replace compute-liveness - dup live-replace? [ call-next-method ] [ drop ] if ; - -: record-live ( vregs -- ) - [ - dup live-vregs get key? [ drop ] [ - [ live-vregs get conjoin ] - [ liveness-graph get at record-live ] - bi - ] if - ] each ; - -M: insn compute-liveness uses-vregs record-live ; - -GENERIC: live-insn? ( insn -- ? ) - -M: ##flushable live-insn? dst>> live-vregs get key? ; - -M: ##replace live-insn? live-replace? ; - -M: insn live-insn? drop t ; - -: eliminate-dead-code ( insns -- insns' ) - init-dead-code - [ [ compute-liveness ] each ] [ [ live-insn? ] filter ] bi ; diff --git a/basis/compiler/cfg/dead-code/summary.txt b/basis/compiler/cfg/dead-code/summary.txt deleted file mode 100644 index c66cd99606..0000000000 --- a/basis/compiler/cfg/dead-code/summary.txt +++ /dev/null @@ -1 +0,0 @@ -Dead-code elimination diff --git a/basis/compiler/cfg/debugger/debugger.factor b/basis/compiler/cfg/debugger/debugger.factor index 6b0aba6813..cb56937758 100644 --- a/basis/compiler/cfg/debugger/debugger.factor +++ b/basis/compiler/cfg/debugger/debugger.factor @@ -7,7 +7,8 @@ parser compiler.tree.builder compiler.tree.optimizer compiler.cfg.builder compiler.cfg.linearization compiler.cfg.registers compiler.cfg.stack-frame compiler.cfg.linear-scan compiler.cfg.two-operand -compiler.cfg.optimizer ; +compiler.cfg.liveness compiler.cfg.optimizer +compiler.cfg.mr ; IN: compiler.cfg.debugger GENERIC: test-cfg ( quot -- cfgs ) @@ -18,20 +19,14 @@ M: callable test-cfg M: word test-cfg [ build-tree optimize-tree ] keep build-cfg ; -SYMBOL: allocate-registers? - : test-mr ( quot -- mrs ) test-cfg [ optimize-cfg build-mr - convert-two-operand - allocate-registers? get - [ linear-scan build-stack-frame ] when ] map ; : insn. ( insn -- ) - tuple>array allocate-registers? get [ but-last ] unless - [ pprint bl ] each nl ; + tuple>array [ pprint bl ] each nl ; : mr. ( mrs -- ) [ diff --git a/basis/compiler/cfg/def-use/def-use.factor b/basis/compiler/cfg/def-use/def-use.factor index 068a6a6377..4ff9814e6d 100644 --- a/basis/compiler/cfg/def-use/def-use.factor +++ b/basis/compiler/cfg/def-use/def-use.factor @@ -1,28 +1,32 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors arrays kernel compiler.cfg.instructions ; IN: compiler.cfg.def-use GENERIC: defs-vregs ( insn -- seq ) +GENERIC: temp-vregs ( insn -- seq ) GENERIC: uses-vregs ( insn -- seq ) -: dst/tmp-vregs ( insn -- seq ) [ dst>> ] [ temp>> ] bi 2array ; M: ##flushable defs-vregs dst>> 1array ; -M: ##write-barrier defs-vregs [ card#>> ] [ table>> ] bi 2array ; -M: ##unary/temp defs-vregs dst/tmp-vregs ; -M: ##allot defs-vregs dst/tmp-vregs ; -M: ##dispatch defs-vregs temp>> 1array ; -M: ##slot defs-vregs dst/tmp-vregs ; -M: ##set-slot defs-vregs temp>> 1array ; -M: ##string-nth defs-vregs dst/tmp-vregs ; -M: ##set-string-nth-fast defs-vregs temp>> 1array ; -M: ##compare defs-vregs dst/tmp-vregs ; -M: ##compare-imm defs-vregs dst/tmp-vregs ; -M: ##compare-float defs-vregs dst/tmp-vregs ; -M: ##fixnum-mul defs-vregs [ temp1>> ] [ temp2>> ] bi 2array ; -M: ##fixnum-mul-tail defs-vregs [ temp1>> ] [ temp2>> ] bi 2array ; M: insn defs-vregs drop f ; +M: ##write-barrier temp-vregs [ card#>> ] [ table>> ] bi 2array ; +M: ##unary/temp temp-vregs temp>> 1array ; +M: ##allot temp-vregs temp>> 1array ; +M: ##dispatch temp-vregs temp>> 1array ; +M: ##slot temp-vregs temp>> 1array ; +M: ##set-slot temp-vregs temp>> 1array ; +M: ##string-nth temp-vregs temp>> 1array ; +M: ##set-string-nth-fast temp-vregs temp>> 1array ; +M: ##compare temp-vregs temp>> 1array ; +M: ##compare-imm temp-vregs temp>> 1array ; +M: ##compare-float temp-vregs temp>> 1array ; +M: ##fixnum-mul temp-vregs [ temp1>> ] [ temp2>> ] bi 2array ; +M: ##fixnum-mul-tail temp-vregs [ temp1>> ] [ temp2>> ] bi 2array ; +M: ##gc temp-vregs [ temp1>> ] [ temp2>> ] bi 2array ; +M: _dispatch temp-vregs temp>> 1array ; +M: insn temp-vregs drop f ; + M: ##unary uses-vregs src>> 1array ; M: ##binary uses-vregs [ src1>> ] [ src2>> ] bi 2array ; M: ##binary-imm uses-vregs src1>> 1array ; @@ -39,10 +43,13 @@ M: ##dispatch uses-vregs src>> 1array ; M: ##alien-getter uses-vregs src>> 1array ; M: ##alien-setter uses-vregs [ src>> ] [ value>> ] bi 2array ; M: ##fixnum-overflow uses-vregs [ src1>> ] [ src2>> ] bi 2array ; +M: ##phi uses-vregs inputs>> ; M: _conditional-branch uses-vregs [ src1>> ] [ src2>> ] bi 2array ; M: _compare-imm-branch uses-vregs src1>> 1array ; +M: _dispatch uses-vregs src>> 1array ; M: insn uses-vregs drop f ; +! Instructions that use vregs UNION: vreg-insn ##flushable ##write-barrier @@ -51,5 +58,8 @@ UNION: vreg-insn ##fixnum-overflow ##conditional-branch ##compare-imm-branch +##phi +##gc _conditional-branch -_compare-imm-branch ; +_compare-imm-branch +_dispatch ; diff --git a/basis/compiler/cfg/dominance/authors.txt b/basis/compiler/cfg/dominance/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/dominance/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/dominance/dominance.factor b/basis/compiler/cfg/dominance/dominance.factor new file mode 100644 index 0000000000..750a46ee6c --- /dev/null +++ b/basis/compiler/cfg/dominance/dominance.factor @@ -0,0 +1,41 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors assocs combinators compiler.cfg.rpo +compiler.cfg.stack-analysis fry kernel math.order namespaces +sequences ; +IN: compiler.cfg.dominance + +! Reference: + +! A Simple, Fast Dominance Algorithm +! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy +! http://www.cs.rice.edu/~keith/EMBED/dom.pdf + +SYMBOL: idoms + +: idom ( bb -- bb' ) idoms get at ; + +> ] compare { + { +lt+ [ [ idom ] dip intersect ] } + { +gt+ [ idom intersect ] } + [ 2drop ] + } case ; + +: compute-idom ( bb -- idom ) + predecessors>> [ idom ] map sift + [ ] [ intersect ] map-reduce ; + +: iterate ( rpo -- changed? ) + [ [ compute-idom ] keep set-idom ] map [ ] any? ; + +PRIVATE> + +: compute-dominance ( cfg -- cfg ) + H{ } clone idoms set + dup reverse-post-order + unclip dup set-idom drop '[ _ iterate ] loop ; \ No newline at end of file diff --git a/basis/compiler/cfg/gc-checks/authors.txt b/basis/compiler/cfg/gc-checks/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/gc-checks/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/gc-checks/gc-checks.factor b/basis/compiler/cfg/gc-checks/gc-checks.factor new file mode 100644 index 0000000000..4176914126 --- /dev/null +++ b/basis/compiler/cfg/gc-checks/gc-checks.factor @@ -0,0 +1,21 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors kernel sequences assocs +cpu.architecture compiler.cfg.rpo +compiler.cfg.liveness compiler.cfg.instructions +compiler.cfg.hats ; +IN: compiler.cfg.gc-checks + +: gc? ( bb -- ? ) + instructions>> [ ##allocation? ] any? ; + +: object-pointer-regs ( basic-block -- vregs ) + live-in keys [ reg-class>> int-regs eq? ] filter ; + +: insert-gc-check ( basic-block -- ) + dup gc? [ + [ i i f f \ ##gc new-insn prefix ] change-instructions drop + ] [ drop ] if ; + +: insert-gc-checks ( cfg -- cfg' ) + dup [ insert-gc-check ] each-basic-block ; \ No newline at end of file diff --git a/basis/compiler/cfg/hats/hats.factor b/basis/compiler/cfg/hats/hats.factor index 817c0f4680..b61f091fad 100644 --- a/basis/compiler/cfg/hats/hats.factor +++ b/basis/compiler/cfg/hats/hats.factor @@ -73,3 +73,5 @@ IN: compiler.cfg.hats : ^^offset>slot ( vreg -- vreg' ) cell 4 = [ 1 ^^shr-imm ] when ; inline : ^^tag-fixnum ( src -- dst ) ^^i1 ##tag-fixnum ; inline : ^^untag-fixnum ( src -- dst ) ^^i1 ##untag-fixnum ; inline + +: ^^phi ( inputs -- dst ) ^^i1 ##phi ; inline \ No newline at end of file diff --git a/basis/compiler/cfg/height/height.factor b/basis/compiler/cfg/height/height.factor index 9312f6f133..14a0a54715 100644 --- a/basis/compiler/cfg/height/height.factor +++ b/basis/compiler/cfg/height/height.factor @@ -1,7 +1,8 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors math namespaces sequences kernel fry -compiler.cfg compiler.cfg.registers compiler.cfg.instructions ; +compiler.cfg compiler.cfg.registers compiler.cfg.instructions +compiler.cfg.liveness compiler.cfg.local ; IN: compiler.cfg.height ! Combine multiple stack height changes into one at the @@ -42,10 +43,13 @@ M: ##replace normalize-height* normalize-peek/replace ; M: insn normalize-height* ; -: normalize-height ( insns -- insns' ) +: height-step ( insns -- insns' ) 0 ds-height set 0 rs-height set [ [ compute-heights ] each ] [ [ [ normalize-height* ] map sift ] with-scope ] bi - ds-height get dup zero? [ drop ] [ f \ ##inc-d boa prefix ] if - rs-height get dup zero? [ drop ] [ f \ ##inc-r boa prefix ] if ; + ds-height get dup 0 = [ drop ] [ \ ##inc-d new-insn prefix ] if + rs-height get dup 0 = [ drop ] [ \ ##inc-r new-insn prefix ] if ; + +: normalize-height ( cfg -- cfg' ) + [ drop ] [ height-step ] local-optimization ; diff --git a/basis/compiler/cfg/instructions/instructions.factor b/basis/compiler/cfg/instructions/instructions.factor index d152a8cc33..1bf94985a6 100644 --- a/basis/compiler/cfg/instructions/instructions.factor +++ b/basis/compiler/cfg/instructions/instructions.factor @@ -1,4 +1,4 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: assocs accessors arrays kernel sequences namespaces words math math.order layouts classes.algebra alien byte-arrays @@ -6,6 +6,8 @@ compiler.constants combinators compiler.cfg.registers compiler.cfg.instructions.syntax ; IN: compiler.cfg.instructions +: new-insn ( ... class -- insn ) [ f f ] dip boa ; inline + ! Virtual CPU instructions, used by CFG and machine IRs TUPLE: insn ; @@ -44,26 +46,19 @@ M: fixnum ##load-literal tag-fixnum ##load-immediate ; M: f ##load-literal drop \ f tag-number ##load-immediate ; M: object ##load-literal ##load-reference ; -INSN: ##peek < ##read { loc loc } ; -INSN: ##replace < ##write { loc loc } ; +INSN: ##peek < ##flushable { loc loc } ; +INSN: ##replace < ##effect { loc loc } ; INSN: ##inc-d { n integer } ; INSN: ##inc-r { n integer } ; ! Subroutine calls -TUPLE: stack-frame -{ params integer } -{ return integer } -{ total-size integer } -spill-counts ; - INSN: ##stack-frame stack-frame ; -INSN: ##call word ; +INSN: ##call word { height integer } ; INSN: ##jump word ; INSN: ##return ; ! Jump tables -INSN: ##dispatch src temp offset ; -INSN: ##dispatch-label label ; +INSN: ##dispatch src temp ; ! Slot access INSN: ##slot < ##read { obj vreg } { slot vreg } { tag integer } { temp vreg } ; @@ -160,9 +155,12 @@ INSN: ##set-alien-double < ##alien-setter ; ! Memory allocation INSN: ##allot < ##flushable size class { temp vreg } ; + +UNION: ##allocation ##allot ##box-float ##box-alien ##integer>bignum ; + INSN: ##write-barrier < ##effect card# table ; -INSN: ##alien-global < ##read symbol library ; +INSN: ##alien-global < ##flushable symbol library ; ! FFI INSN: ##alien-invoke params ; @@ -178,6 +176,8 @@ INSN: ##branch ; INSN: ##loop-entry ; +INSN: ##phi < ##pure inputs ; + ! Condition codes SYMBOL: cc< SYMBOL: cc<= @@ -217,16 +217,19 @@ INSN: ##compare-imm < ##binary-imm cc temp ; INSN: ##compare-float-branch < ##conditional-branch ; INSN: ##compare-float < ##binary cc temp ; +INSN: ##gc { temp1 vreg } { temp2 vreg } live-registers live-spill-slots ; + ! Instructions used by machine IR only. INSN: _prologue stack-frame ; INSN: _epilogue stack-frame ; INSN: _label id ; -INSN: _gc ; - INSN: _branch label ; +INSN: _dispatch src temp ; +INSN: _dispatch-label label ; + TUPLE: _conditional-branch < insn label { src1 vreg } { src2 vreg } cc ; INSN: _compare-branch < _conditional-branch ; @@ -234,8 +237,13 @@ INSN: _compare-imm-branch label { src1 vreg } { src2 integer } cc ; INSN: _compare-float-branch < _conditional-branch ; +TUPLE: spill-slot n ; C: spill-slot + +INSN: _gc { temp1 vreg } { temp2 vreg } gc-roots gc-root-count gc-root-size ; + ! These instructions operate on machine registers and not ! virtual registers INSN: _spill src class n ; INSN: _reload dst class n ; +INSN: _copy dst src class ; INSN: _spill-counts counts ; diff --git a/basis/compiler/cfg/instructions/syntax/syntax.factor b/basis/compiler/cfg/instructions/syntax/syntax.factor index 876ac5596c..e8f8641e7d 100644 --- a/basis/compiler/cfg/instructions/syntax/syntax.factor +++ b/basis/compiler/cfg/instructions/syntax/syntax.factor @@ -11,12 +11,12 @@ IN: compiler.cfg.instructions.syntax "insn" "compiler.cfg.instructions" lookup ; : insn-effect ( word -- effect ) - boa-effect in>> but-last f ; + boa-effect in>> 2 head* f ; SYNTAX: INSN: - parse-tuple-definition "regs" suffix + parse-tuple-definition { "regs" "insn#" } append [ dup tuple eq? [ drop insn-word ] when ] dip [ define-tuple-class ] [ 2drop save-location ] - [ 2drop [ ] [ '[ f _ boa , ] ] [ insn-effect ] tri define-inline ] + [ 2drop [ ] [ '[ f f _ boa , ] ] [ insn-effect ] tri define-inline ] 3tri ; diff --git a/basis/compiler/cfg/iterator/iterator.factor b/basis/compiler/cfg/iterator/iterator.factor index a8958733a7..eb7f71ad60 100644 --- a/basis/compiler/cfg/iterator/iterator.factor +++ b/basis/compiler/cfg/iterator/iterator.factor @@ -37,9 +37,9 @@ DEFER: (tail-call?) : tail-call? ( -- ? ) node-stack get [ rest-slice - [ t ] [ - [ (tail-call?) ] - [ first #terminate? not ] - bi and - ] if-empty + [ t ] [ (tail-call?) ] if-empty ] all? ; + +: terminate-call? ( -- ? ) + node-stack get last + rest-slice [ f ] [ first #terminate? ] if-empty ; diff --git a/basis/compiler/cfg/linear-scan/allocation/allocation.factor b/basis/compiler/cfg/linear-scan/allocation/allocation.factor index 908bf2475b..a99fea1d24 100644 --- a/basis/compiler/cfg/linear-scan/allocation/allocation.factor +++ b/basis/compiler/cfg/linear-scan/allocation/allocation.factor @@ -1,177 +1,41 @@ -! Copyright (C) 2008 Slava Pestov. +! 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 combinators -compiler.cfg.registers -compiler.cfg.linear-scan.live-intervals ; +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 ; - -: expire-old-intervals ( n -- ) - active-intervals swap '[ - [ - [ end>> _ < ] partition - [ [ deallocate-register ] each ] dip - ] assoc-map - ] change ; - -! 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 ] bi* ] - [ reg>> >>reg drop ] - 2bi ; - -! Splitting -: find-use ( live-interval n quot -- i elt ) - [ uses>> ] 2dip curry find ; inline - -: split-before ( live-interval i -- before ) - [ clone dup uses>> ] dip - [ head >>uses ] [ 1- swap nth >>end ] 2bi ; - -: split-after ( live-interval i -- after ) - [ clone dup uses>> ] dip - [ tail >>uses ] [ swap nth >>start ] 2bi - f >>reg f >>copy-from ; - -: split-interval ( live-interval n -- before after ) - [ drop ] [ [ > ] find-use drop ] 2bi - [ split-before ] [ split-after ] 2bi ; - -: record-split ( live-interval before after -- ) - [ >>split-before ] [ >>split-after ] bi* drop ; - -! Spilling -SYMBOL: spill-counts - -: next-spill-location ( reg-class -- n ) - spill-counts get [ dup 1+ ] change-at ; - -: 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 ) - dup rot start>> split-interval - [ record-split ] [ assign-spill ] 2bi ; - -: 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 [ drop ] [ 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 ; - : assign-register ( new -- ) - dup coalesce? [ - coalesce - ] [ - dup vreg>> free-registers-for - [ assign-blocked-register ] - [ assign-free-register ] + dup coalesce? [ coalesce ] [ + dup vreg>> free-registers-for [ + dup intersecting-inactive + [ assign-blocked-register ] + [ assign-inactive-register ] + if-empty + ] [ assign-free-register ] if-empty ] if ; -! Main loop -: reg-classes ( -- seq ) { int-regs double-float-regs } ; inline - -: init-allocator ( registers -- ) - unhandled-intervals set - [ reverse >vector ] assoc-map free-registers set - reg-classes [ 0 ] { } map>assoc spill-counts set - reg-classes [ V{ } clone ] { } map>assoc active-intervals set - -1 progress set ; - : handle-interval ( live-interval -- ) - [ start>> progress set ] - [ start>> expire-old-intervals ] - [ assign-register ] - tri ; + [ + start>> + [ progress set ] + [ deactivate-intervals ] + [ activate-intervals ] tri + ] [ assign-register ] bi ; : (allocate-registers) ( -- ) unhandled-intervals get [ handle-interval ] slurp-heap ; +: finish-allocation ( -- ) + 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 - dup init-unhandled - (allocate-registers) ; + init-unhandled + (allocate-registers) + finish-allocation + handled-intervals get ; diff --git a/basis/compiler/cfg/linear-scan/allocation/coalescing/coalescing.factor b/basis/compiler/cfg/linear-scan/allocation/coalescing/coalescing.factor new file mode 100644 index 0000000000..99ed75dcbc --- /dev/null +++ b/basis/compiler/cfg/linear-scan/allocation/coalescing/coalescing.factor @@ -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 ; diff --git a/basis/compiler/cfg/linear-scan/allocation/spilling/spilling.factor b/basis/compiler/cfg/linear-scan/allocation/spilling/spilling.factor new file mode 100644 index 0000000000..4981a223a4 --- /dev/null +++ b/basis/compiler/cfg/linear-scan/allocation/spilling/spilling.factor @@ -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 ; + diff --git a/basis/compiler/cfg/linear-scan/allocation/splitting/splitting.factor b/basis/compiler/cfg/linear-scan/allocation/splitting/splitting.factor new file mode 100644 index 0000000000..40ee4083e4 --- /dev/null +++ b/basis/compiler/cfg/linear-scan/allocation/splitting/splitting.factor @@ -0,0 +1,120 @@ +! 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 ] + [ 1 + swap to>> ] + 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 ] + [ + [ over last ] dip 2dup split-last-range? + [ split-last-range ] [ 2drop ] 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-too-early ; + +ERROR: splitting-atomic-interval ; + +: check-split ( live-interval n -- ) + [ [ start>> ] dip > [ splitting-too-early ] when ] + [ drop [ end>> ] [ start>> ] bi - 0 = [ splitting-atomic-interval ] when ] + 2bi ; 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 n 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/f ) + ! If the interval's register is currently in use, we cannot + ! re-use it. + 2dup [ reg>> ] dip key? + [ 3drop f ] [ 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 + [ nip ] assoc-filter ; + +: 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 ; \ No newline at end of file diff --git a/basis/compiler/cfg/linear-scan/allocation/state/state.factor b/basis/compiler/cfg/linear-scan/allocation/state/state.factor new file mode 100644 index 0000000000..2a1e87dcdd --- /dev/null +++ b/basis/compiler/cfg/linear-scan/allocation/state/state.factor @@ -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 + 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 ; \ No newline at end of file diff --git a/basis/compiler/cfg/linear-scan/assignment/assignment-tests.factor b/basis/compiler/cfg/linear-scan/assignment/assignment-tests.factor deleted file mode 100644 index 13c1783711..0000000000 --- a/basis/compiler/cfg/linear-scan/assignment/assignment-tests.factor +++ /dev/null @@ -1,4 +0,0 @@ -USING: compiler.cfg.linear-scan.assignment tools.test ; -IN: compiler.cfg.linear-scan.assignment.tests - - diff --git a/basis/compiler/cfg/linear-scan/assignment/assignment.factor b/basis/compiler/cfg/linear-scan/assignment/assignment.factor index da45b45aaa..ea918a7424 100644 --- a/basis/compiler/cfg/linear-scan/assignment/assignment.factor +++ b/basis/compiler/cfg/linear-scan/assignment/assignment.factor @@ -1,87 +1,144 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors kernel math assocs namespaces sequences heaps -fry make combinators +fry make combinators sets cpu.architecture 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. -SYMBOL: active-intervals +! 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 push ; - -: lookup-register ( vreg -- reg ) - active-intervals get [ vreg>> = ] with find nip reg>> ; + pending-intervals get push ; ! Minheap of live intervals which still need a register allocation SYMBOL: unhandled-intervals : add-unhandled ( live-interval -- ) - dup split-before>> [ - [ split-before>> ] [ split-after>> ] bi - [ add-unhandled ] bi@ - ] [ - dup start>> unhandled-intervals get heap-push - ] if ; + dup start>> unhandled-intervals get heap-push ; : init-unhandled ( live-intervals -- ) [ add-unhandled ] each ; +! Mapping spill slots to vregs +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? [ already-spilled ] [ set-at ] if ; + : insert-spill ( live-interval -- ) - [ reg>> ] [ vreg>> reg-class>> ] [ spill-to>> ] tri - dup [ _spill ] [ 3drop ] if ; + [ reg>> ] [ vreg>> reg-class>> ] [ spill-to>> ] tri _spill ; + +: 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 - active-intervals set - [ insert-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 ] [ already-reloaded ] if ; : insert-reload ( live-interval -- ) - [ reg>> ] [ vreg>> reg-class>> ] [ reload-from>> ] tri - dup [ _reload ] [ 3drop ] if ; + [ reg>> ] [ vreg>> reg-class>> ] [ reload-from>> ] tri _reload ; + +: handle-reload ( live-interval -- ) + dup reload-from>> [ [ record-reload ] [ insert-reload ] bi ] [ drop ] if ; : activate-new-intervals ( n -- ) #! Any live intervals which start on the current instruction #! are added to the active set. unhandled-intervals get dup heap-empty? [ 2drop ] [ 2dup heap-peek drop start>> = [ - heap-pop drop [ add-active ] [ insert-reload ] bi + heap-pop drop + [ add-active ] [ handle-reload ] bi activate-new-intervals ] [ 2drop ] if ] if ; -GENERIC: (assign-registers) ( insn -- ) +GENERIC: assign-registers-in-insn ( insn -- ) -M: vreg-insn (assign-registers) - dup - [ defs-vregs ] [ uses-vregs ] bi append - active-intervals get swap '[ vreg>> _ member? ] filter - [ [ vreg>> ] [ reg>> ] bi ] { } map>assoc +: register-mapping ( live-intervals -- alist ) + [ [ vreg>> ] [ reg>> ] bi ] { } map>assoc ; + +: all-vregs ( insn -- vregs ) + [ defs-vregs ] [ temp-vregs ] [ uses-vregs ] tri 3append ; + +: 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-registers) drop ; +: 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-registers-in-insn + dup call-next-method + dup compute-live-registers >>live-registers + compute-live-spill-slots >>live-spill-slots + drop ; + +M: insn assign-registers-in-insn drop ; : init-assignment ( live-intervals -- ) - V{ } clone active-intervals set + V{ } clone pending-intervals set unhandled-intervals set + [ H{ } clone ] reg-class-assoc spill-slots set init-unhandled ; -: assign-registers ( insns live-intervals -- insns' ) +: assign-registers-in-block ( bb -- ) [ - init-assignment [ - [ activate-new-intervals ] - [ drop [ (assign-registers) ] [ , ] bi ] - [ expire-old-intervals ] - tri - ] each-index - ] { } make ; + [ + [ + insn#>> + [ expire-old-intervals ] + [ activate-new-intervals ] + bi + ] + [ assign-registers-in-insn ] + [ , ] + tri + ] each + ] V{ } make + ] change-instructions drop ; + +: assign-registers ( rpo live-intervals -- ) + init-assignment + [ assign-registers-in-block ] each ; diff --git a/basis/compiler/cfg/linear-scan/linear-scan-tests.factor b/basis/compiler/cfg/linear-scan/linear-scan-tests.factor index 65b932c4a2..243e83445d 100644 --- a/basis/compiler/cfg/linear-scan/linear-scan-tests.factor +++ b/basis/compiler/cfg/linear-scan/linear-scan-tests.factor @@ -1,15 +1,77 @@ 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 } } +] [ + { + T{ live-range f 1 10 } + T{ live-range f 15 20 } + } 15 split-ranges +] unit-test + +[ + { T{ live-range f 1 10 } T{ live-range f 15 16 } } + { T{ live-range f 17 20 } } +] [ + { + T{ live-range f 1 10 } + T{ live-range f 15 20 } + } 16 split-ranges +] unit-test + +[ + { T{ live-range f 1 10 } } + { T{ live-range f 15 20 } } +] [ + { + T{ live-range f 1 10 } + T{ live-range f 15 20 } + } 12 split-ranges +] unit-test + +[ + { T{ live-range f 1 10 } T{ live-range f 15 17 } } + { T{ live-range f 18 20 } } +] [ + { + T{ live-range f 1 10 } + T{ live-range f 15 20 } + } 17 split-ranges +] unit-test + +[ + { T{ live-range f 1 10 } } 0 split-ranges +] must-fail + +[ + { T{ live-range f 0 0 } } + { T{ live-range f 1 5 } } +] [ + { T{ live-range f 0 5 } } 0 split-ranges +] unit-test + [ 7 ] [ T{ live-interval { vreg T{ vreg { reg-class int-regs } { n 2 } } } @@ -42,46 +104,77 @@ compiler.cfg.linear-scan.debugger ; [ T{ live-interval - { vreg T{ vreg { reg-class int-regs } { n 1 } } } - { start 0 } - { end 1 } - { uses V{ 0 1 } } + { vreg T{ vreg { reg-class int-regs } { n 1 } } } + { start 0 } + { end 1 } + { uses V{ 0 1 } } + { ranges V{ T{ live-range f 0 1 } } } } T{ live-interval - { vreg T{ vreg { reg-class int-regs } { n 1 } } } - { start 5 } - { end 5 } - { uses V{ 5 } } + { vreg T{ vreg { reg-class int-regs } { n 1 } } } + { start 5 } + { end 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 } } - } 2 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 } } } + } 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 } } + { 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 } } + { 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 } } - } 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 [ @@ -171,7 +264,13 @@ compiler.cfg.linear-scan.debugger ; [ ] [ { - T{ live-interval { vreg T{ vreg { n 1 } { reg-class int-regs } } } { start 0 } { end 100 } { uses V{ 0 100 } } } + T{ live-interval + { vreg T{ vreg { n 1 } { reg-class int-regs } } } + { start 0 } + { end 100 } + { uses V{ 0 100 } } + { ranges V{ T{ live-range f 0 100 } } } + } } H{ { int-regs { "A" } } } check-linear-scan @@ -179,8 +278,20 @@ compiler.cfg.linear-scan.debugger ; [ ] [ { - T{ live-interval { vreg T{ vreg { n 1 } { reg-class int-regs } } } { start 0 } { end 10 } { uses V{ 0 10 } } } - T{ live-interval { vreg T{ vreg { n 2 } { reg-class int-regs } } } { start 11 } { end 20 } { uses V{ 11 20 } } } + T{ live-interval + { vreg T{ vreg { n 1 } { reg-class int-regs } } } + { start 0 } + { end 10 } + { uses V{ 0 10 } } + { ranges V{ T{ live-range f 0 10 } } } + } + T{ live-interval + { vreg T{ vreg { n 2 } { reg-class int-regs } } } + { start 11 } + { end 20 } + { uses V{ 11 20 } } + { ranges V{ T{ live-range f 11 20 } } } + } } H{ { int-regs { "A" } } } check-linear-scan @@ -188,8 +299,20 @@ compiler.cfg.linear-scan.debugger ; [ ] [ { - T{ live-interval { vreg T{ vreg { n 1 } { reg-class int-regs } } } { start 0 } { end 100 } { uses V{ 0 100 } } } - T{ live-interval { vreg T{ vreg { n 2 } { reg-class int-regs } } } { start 30 } { end 60 } { uses V{ 30 60 } } } + T{ live-interval + { vreg T{ vreg { n 1 } { reg-class int-regs } } } + { start 0 } + { end 100 } + { uses V{ 0 100 } } + { ranges V{ T{ live-range f 0 100 } } } + } + T{ live-interval + { vreg T{ vreg { n 2 } { reg-class int-regs } } } + { start 30 } + { end 60 } + { uses V{ 30 60 } } + { ranges V{ T{ live-range f 30 60 } } } + } } H{ { int-regs { "A" } } } check-linear-scan @@ -197,8 +320,20 @@ compiler.cfg.linear-scan.debugger ; [ ] [ { - T{ live-interval { vreg T{ vreg { n 1 } { reg-class int-regs } } } { start 0 } { end 100 } { uses V{ 0 100 } } } - T{ live-interval { vreg T{ vreg { n 2 } { reg-class int-regs } } } { start 30 } { end 200 } { uses V{ 30 200 } } } + T{ live-interval + { vreg T{ vreg { n 1 } { reg-class int-regs } } } + { start 0 } + { end 100 } + { uses V{ 0 100 } } + { ranges V{ T{ live-range f 0 100 } } } + } + T{ live-interval + { vreg T{ vreg { n 2 } { reg-class int-regs } } } + { start 30 } + { end 200 } + { uses V{ 30 200 } } + { ranges V{ T{ live-range f 30 200 } } } + } } H{ { int-regs { "A" } } } check-linear-scan @@ -206,8 +341,20 @@ compiler.cfg.linear-scan.debugger ; [ { - T{ live-interval { vreg T{ vreg { n 1 } { reg-class int-regs } } } { start 0 } { end 100 } { uses V{ 0 100 } } } - T{ live-interval { vreg T{ vreg { n 2 } { reg-class int-regs } } } { start 30 } { end 100 } { uses V{ 30 100 } } } + T{ live-interval + { vreg T{ vreg { n 1 } { reg-class int-regs } } } + { start 0 } + { end 100 } + { uses V{ 0 100 } } + { ranges V{ T{ live-range f 0 100 } } } + } + T{ live-interval + { vreg T{ vreg { n 2 } { reg-class int-regs } } } + { start 30 } + { end 100 } + { uses V{ 30 100 } } + { ranges V{ T{ live-range f 30 100 } } } + } } H{ { int-regs { "A" } } } check-linear-scan @@ -240,11 +387,12 @@ SYMBOL: max-uses max-insns get [ 0 ] replicate taken set max-insns get [ dup ] H{ } map>assoc available set [ - live-interval new + \ live-interval new swap int-regs swap vreg boa >>vreg max-uses get random 2 max [ not-taken ] replicate natural-sort [ >>uses ] [ first >>start ] bi dup uses>> last >>end + dup [ start>> ] [ end>> ] bi 1vector >>ranges ] map ] with-scope ; @@ -264,45 +412,15 @@ SYMBOL: max-uses USING: math.private compiler.cfg.debugger ; -[ ] [ [ float+ float>fixnum 3 fixnum*fast ] test-mr first linear-scan drop ] unit-test - -[ f ] [ - T{ ##allot - f - T{ vreg f int-regs 1 } - 40 - array - T{ vreg f int-regs 2 } - f - } clone - 1array (linear-scan) first regs>> values all-equal? +[ ] [ + [ float+ float>fixnum 3 fixnum*fast ] + test-cfg first optimize-cfg linear-scan drop ] unit-test -[ 0 1 ] [ - { - T{ live-interval - { vreg T{ vreg { reg-class int-regs } { n 1 } } } - { start 0 } - { end 5 } - { uses V{ 0 1 5 } } - } - T{ live-interval - { vreg T{ vreg { reg-class int-regs } { n 2 } } } - { start 3 } - { end 4 } - { uses V{ 3 4 } } - } - T{ live-interval - { vreg T{ vreg { reg-class int-regs } { n 3 } } } - { start 2 } - { end 6 } - { uses V{ 2 4 6 } } - } - } [ clone ] map - H{ { int-regs { "A" "B" } } } - allocate-registers - first split-before>> [ start>> ] [ end>> ] bi -] unit-test +: fake-live-ranges ( seq -- seq' ) + [ + clone dup [ start>> ] [ end>> ] bi 1vector >>ranges + ] map ; ! Coalescing interacted badly with splitting [ ] [ @@ -351,7 +469,7 @@ USING: math.private compiler.cfg.debugger ; { end 10 } { uses V{ 9 10 } } } - } + } fake-live-ranges { { int-regs { 0 1 2 3 } } } allocate-registers drop ] unit-test @@ -1106,7 +1224,7 @@ USING: math.private compiler.cfg.debugger ; { end 109 } { uses V{ 103 109 } } } - } + } fake-live-ranges { { int-regs { 0 1 2 3 4 } } } allocate-registers drop ] unit-test @@ -1199,7 +1317,487 @@ USING: math.private compiler.cfg.debugger ; { end 92 } { uses V{ 42 45 78 80 92 } } } - } + } fake-live-ranges { { int-regs { 0 1 2 3 } } } allocate-registers drop ] unit-test + +! Spill slot liveness was computed incorrectly, leading to a FEP +! early in bootstrap on x86-32 +[ t ] [ + [ + 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 ] [ + T{ live-range f 0 10 } + T{ live-range f 20 30 } + intersect-live-range +] unit-test + +[ 10 ] [ + T{ live-range f 0 10 } + T{ live-range f 10 30 } + intersect-live-range +] unit-test + +[ 5 ] [ + T{ live-range f 0 10 } + T{ live-range f 5 30 } + intersect-live-range +] unit-test + +[ 5 ] [ + T{ live-range f 5 30 } + T{ live-range f 0 10 } + intersect-live-range +] unit-test + +[ 5 ] [ + T{ live-range f 5 10 } + T{ live-range f 0 15 } + intersect-live-range +] unit-test + +[ 50 ] [ + { + T{ live-range f 0 10 } + T{ live-range f 20 30 } + T{ live-range f 40 50 } + } + { + T{ live-range f 11 15 } + T{ live-range f 31 35 } + T{ live-range f 50 55 } + } + intersect-live-ranges +] unit-test + +[ 5 ] [ + T{ live-interval + { start 0 } + { end 10 } + { uses { 0 10 } } + { ranges V{ T{ live-range f 0 10 } } } + } + T{ live-interval + { start 5 } + { end 10 } + { uses { 5 10 } } + { ranges V{ T{ live-range f 5 10 } } } + } + H{ } + intersect-inactive +] 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 } + { number 0 } + { instructions V{ T{ ##prologue } T{ ##branch } } } +} 0 set + +T{ basic-block + { id 1 } + { 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/= } + } + } + } +} 1 set + +T{ basic-block + { id 2 } + { 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 } + } + } +} 2 set + +T{ basic-block + { id 3 } + { 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 } + } + } +} 3 set + +T{ basic-block + { id 4 } + { instructions + V{ + T{ ##replace + { src V int-regs 2 } + { loc D 0 } + } + T{ ##return } + } + } +} 4 set + +test-diamond + +{ 1 2 3 4 } test-linear-scan-on-cfg \ No newline at end of file diff --git a/basis/compiler/cfg/linear-scan/linear-scan.factor b/basis/compiler/cfg/linear-scan/linear-scan.factor index 855f2a6648..3a0a7f8770 100644 --- a/basis/compiler/cfg/linear-scan/linear-scan.factor +++ b/basis/compiler/cfg/linear-scan/linear-scan.factor @@ -1,11 +1,14 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel accessors namespaces make cpu.architecture compiler.cfg +compiler.cfg.rpo 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 @@ -23,16 +26,15 @@ IN: compiler.cfg.linear-scan ! by Omri Traub, Glenn Holloway, Michael D. Smith ! http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.8435 -: (linear-scan) ( insns -- insns' ) - dup compute-live-intervals - machine-registers allocate-registers assign-registers ; - -: linear-scan ( mr -- mr' ) +: (linear-scan) ( rpo machine-registers -- ) [ - [ - [ - (linear-scan) % - spill-counts get _spill-counts - ] { } make - ] change-instructions + dup number-instructions + dup compute-live-intervals + ] dip + allocate-registers assign-registers ; + +: linear-scan ( cfg -- cfg' ) + [ + dup reverse-post-order machine-registers (linear-scan) + spill-counts get >>spill-counts ] with-scope ; diff --git a/basis/compiler/cfg/linear-scan/live-intervals/live-intervals.factor b/basis/compiler/cfg/linear-scan/live-intervals/live-intervals.factor index 1055a3524a..c88f7fd21b 100644 --- a/basis/compiler/cfg/linear-scan/live-intervals/live-intervals.factor +++ b/basis/compiler/cfg/linear-scan/live-intervals/live-intervals.factor @@ -1,26 +1,65 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: namespaces kernel assocs accessors sequences math fry -compiler.cfg.instructions compiler.cfg.registers -compiler.cfg.def-use ; +USING: namespaces kernel assocs accessors sequences math math.order fry +binary-search combinators compiler.cfg.instructions compiler.cfg.registers +compiler.cfg.def-use compiler.cfg.liveness compiler.cfg ; IN: compiler.cfg.linear-scan.live-intervals +TUPLE: live-range from to ; + +C: live-range + TUPLE: live-interval vreg -reg spill-to reload-from split-before split-after -start end uses +reg spill-to reload-from +split-before split-after split-next +start end ranges uses copy-from ; -: add-use ( n live-interval -- ) - dup live-interval? [ "No def" throw ] unless - [ (>>end) ] [ uses>> push ] 2bi ; +: covers? ( insn# live-interval -- ? ) + ranges>> [ [ from>> ] [ to>> ] bi between? ] with any? ; -: ( start vreg -- live-interval ) - live-interval new +: 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 -- ) + dup ranges>> empty? + [ vreg>> dead-value-error ] [ ranges>> last (>>from) ] if ; + +: extend-range ( from to live-range -- ) + ranges>> last + [ max ] change-to + [ min ] change-from + drop ; + +: add-new-range ( from to live-interval -- ) + [ ] dip ranges>> push ; + +: extend-range? ( to live-interval -- ? ) + ranges>> [ drop f ] [ last from>> >= ] if-empty ; + +: add-range ( from to live-interval -- ) + 2dup extend-range? + [ extend-range ] [ add-new-range ] if ; + +: add-use ( n live-interval -- ) + uses>> push ; + +: ( vreg -- live-interval ) + \ live-interval new V{ } clone >>uses - swap >>vreg - over >>start - [ add-use ] keep ; + V{ } clone >>ranges + swap >>vreg ; + +: block-from ( bb -- n ) instructions>> first insn#>> ; + +: block-to ( bb -- n ) instructions>> last insn#>> ; M: live-interval hashcode* nip [ start>> ] [ end>> 1000 * ] bi + ; @@ -31,34 +70,77 @@ M: live-interval clone ! Mapping from vreg to live-interval SYMBOL: live-intervals -: new-live-interval ( n vreg live-intervals -- ) - 2dup key? [ - at add-use - ] [ - [ [ ] keep ] dip set-at - ] if ; +: live-interval ( vreg live-intervals -- live-interval ) + [ ] cache ; -GENERIC# compute-live-intervals* 1 ( insn n -- ) +GENERIC: compute-live-intervals* ( insn -- ) -M: insn compute-live-intervals* 2drop ; +M: insn compute-live-intervals* drop ; + +: handle-output ( n vreg live-intervals -- ) + live-interval + [ add-use ] [ shorten-range ] 2bi ; + +: handle-input ( n vreg live-intervals -- ) + live-interval + [ [ basic-block get block-from ] 2dip add-range ] [ add-use ] 2bi ; + +: handle-temp ( n vreg live-intervals -- ) + live-interval + [ dupd add-range ] [ add-use ] 2bi ; M: vreg-insn compute-live-intervals* + dup insn#>> live-intervals get - [ [ uses-vregs ] 2dip '[ _ swap _ at add-use ] each ] - [ [ defs-vregs ] 2dip '[ _ swap _ new-live-interval ] each ] - 3bi ; + [ [ defs-vregs ] 2dip '[ [ _ ] dip _ handle-output ] each ] + [ [ uses-vregs ] 2dip '[ [ _ ] dip _ handle-input ] each ] + [ [ temp-vregs ] 2dip '[ [ _ ] dip _ handle-temp ] each ] + 3tri ; : record-copy ( insn -- ) [ dst>> live-intervals get at ] [ src>> ] bi >>copy-from drop ; M: ##copy compute-live-intervals* - [ call-next-method ] [ drop record-copy ] 2bi ; + [ call-next-method ] [ record-copy ] bi ; M: ##copy-float compute-live-intervals* - [ call-next-method ] [ drop record-copy ] 2bi ; + [ call-next-method ] [ record-copy ] bi ; -: compute-live-intervals ( instructions -- live-intervals ) +: handle-live-out ( bb -- ) + live-out keys + basic-block get [ block-from ] [ block-to ] bi + live-intervals get '[ + [ _ _ ] dip _ live-interval add-range + ] each ; + +: compute-live-intervals-step ( bb -- ) + [ basic-block set ] + [ handle-live-out ] + [ instructions>> [ compute-live-intervals* ] each ] tri ; + +: compute-start/end ( live-interval -- ) + dup ranges>> [ first from>> ] [ last to>> ] bi + [ >>start ] [ >>end ] bi* drop ; + +: check-start/end ( live-interval -- ) + [ [ start>> ] [ uses>> first ] bi assert= ] + [ [ end>> ] [ uses>> last ] bi assert= ] + bi ; + +: finish-live-intervals ( live-intervals -- ) + ! Since live intervals are computed in a backward order, we have + ! to reverse some sequences, and compute the start and end. + [ + { + [ ranges>> reverse-here ] + [ uses>> reverse-here ] + [ compute-start/end ] + [ check-start/end ] + } cleave + ] each ; + +: compute-live-intervals ( rpo -- live-intervals ) H{ } clone [ live-intervals set - [ compute-live-intervals* ] each-index - ] keep values ; + [ compute-live-intervals-step ] each + ] keep values dup finish-live-intervals ; diff --git a/basis/compiler/cfg/linear-scan/numbering/authors.txt b/basis/compiler/cfg/linear-scan/numbering/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/linear-scan/numbering/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/linear-scan/numbering/numbering.factor b/basis/compiler/cfg/linear-scan/numbering/numbering.factor new file mode 100644 index 0000000000..6734f6a359 --- /dev/null +++ b/basis/compiler/cfg/linear-scan/numbering/numbering.factor @@ -0,0 +1,11 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: kernel accessors math sequences ; +IN: compiler.cfg.linear-scan.numbering + +: number-instructions ( rpo -- ) + [ 0 ] dip [ + instructions>> [ + [ (>>insn#) ] [ drop 2 + ] 2bi + ] each + ] each drop ; \ No newline at end of file diff --git a/basis/compiler/cfg/linear-scan/resolve/resolve.factor b/basis/compiler/cfg/linear-scan/resolve/resolve.factor new file mode 100644 index 0000000000..8996327beb --- /dev/null +++ b/basis/compiler/cfg/linear-scan/resolve/resolve.factor @@ -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 ; \ No newline at end of file diff --git a/basis/compiler/cfg/linearization/linearization.factor b/basis/compiler/cfg/linearization/linearization.factor index 8ef3abda39..9e222f1832 100755 --- a/basis/compiler/cfg/linearization/linearization.factor +++ b/basis/compiler/cfg/linearization/linearization.factor @@ -1,24 +1,28 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel math accessors sequences namespaces make -combinators classes +combinators assocs arrays locals cpu.architecture compiler.cfg compiler.cfg.rpo +compiler.cfg.liveness +compiler.cfg.stack-frame compiler.cfg.instructions ; IN: compiler.cfg.linearization ! Convert CFG IR to machine IR. GENERIC: linearize-insn ( basic-block insn -- ) -: linearize-insns ( basic-block -- ) - dup instructions>> [ linearize-insn ] with each ; inline +: linearize-basic-block ( bb -- ) + [ number>> _label ] + [ dup instructions>> [ linearize-insn ] with each ] + bi ; M: insn linearize-insn , drop ; : useless-branch? ( basic-block successor -- ? ) #! If our successor immediately follows us in RPO, then we #! don't need to branch. - [ number>> ] bi@ 1- = ; inline + [ number>> ] bi@ 1 - = ; inline : branch-to-branch? ( successor -- ? ) #! A branch to a block containing just a jump return is cloned. @@ -30,7 +34,7 @@ M: insn linearize-insn , drop ; : emit-branch ( basic-block successor -- ) { { [ 2dup useless-branch? ] [ 2drop ] } - { [ dup branch-to-branch? ] [ nip linearize-insns ] } + { [ dup branch-to-branch? ] [ nip linearize-basic-block ] } [ nip number>> _branch ] } cond ; @@ -46,35 +50,82 @@ M: ##branch linearize-insn [ drop dup successors>> second useless-branch? ] 2bi [ [ swap number>> ] 3dip ] [ [ number>> ] 3dip negate-cc ] if ; +: with-regs ( insn quot -- ) + over regs>> [ call ] dip building get last (>>regs) ; inline + M: ##compare-branch linearize-insn - binary-conditional _compare-branch emit-branch ; + [ binary-conditional _compare-branch ] with-regs emit-branch ; M: ##compare-imm-branch linearize-insn - binary-conditional _compare-imm-branch emit-branch ; + [ binary-conditional _compare-imm-branch ] with-regs emit-branch ; M: ##compare-float-branch linearize-insn - binary-conditional _compare-float-branch emit-branch ; + [ binary-conditional _compare-float-branch ] with-regs emit-branch ; -: gc? ( bb -- ? ) - instructions>> [ - class { - ##allot - ##integer>bignum - ##box-float - ##box-alien - } memq? - ] any? ; +M: ##dispatch linearize-insn + swap + [ [ [ src>> ] [ temp>> ] bi _dispatch ] with-regs ] + [ successors>> [ number>> _dispatch-label ] each ] + bi* ; -: linearize-basic-block ( bb -- ) - [ number>> _label ] - [ gc? [ _gc ] when ] - [ linearize-insns ] - tri ; +: gc-root-registers ( n live-registers -- n ) + [ + [ second 2array , ] + [ first reg-class>> reg-size + ] + 2bi + ] each ; -: linearize-basic-blocks ( rpo -- insns ) - [ [ linearize-basic-block ] each ] { } make ; +: gc-root-spill-slots ( n live-spill-slots -- n ) + [ + dup first reg-class>> int-regs eq? [ + [ second 2array , ] + [ first reg-class>> reg-size + ] + 2bi + ] [ drop ] if + ] each ; -: build-mr ( cfg -- mr ) - [ entry>> reverse-post-order linearize-basic-blocks ] - [ word>> ] [ label>> ] - tri ; +: oop-registers ( regs -- regs' ) + [ first reg-class>> int-regs eq? ] filter ; + +: data-registers ( regs -- regs' ) + [ first reg-class>> double-float-regs eq? ] filter ; + +:: compute-gc-roots ( live-registers live-spill-slots -- alist ) + [ + 0 + ! we put float registers last; the GC doesn't actually scan them + live-registers oop-registers gc-root-registers + live-spill-slots gc-root-spill-slots + live-registers data-registers gc-root-registers + drop + ] { } make ; + +: count-gc-roots ( live-registers live-spill-slots -- n ) + ! Size of GC root area, minus the float registers + [ oop-registers length ] bi@ + ; + +M: ##gc linearize-insn + nip + [ + [ temp1>> ] + [ temp2>> ] + [ + [ live-registers>> ] [ live-spill-slots>> ] bi + [ compute-gc-roots ] + [ count-gc-roots ] + [ gc-roots-size ] + 2tri + ] tri + _gc + ] with-regs ; + +: linearize-basic-blocks ( cfg -- insns ) + [ + [ [ linearize-basic-block ] each-basic-block ] + [ spill-counts>> _spill-counts ] + bi + ] { } make ; + +: flatten-cfg ( cfg -- mr ) + [ linearize-basic-blocks ] [ word>> ] [ label>> ] tri + ; diff --git a/basis/compiler/cfg/liveness/authors.txt b/basis/compiler/cfg/liveness/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/liveness/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/liveness/liveness.factor b/basis/compiler/cfg/liveness/liveness.factor new file mode 100644 index 0000000000..6c40bb3782 --- /dev/null +++ b/basis/compiler/cfg/liveness/liveness.factor @@ -0,0 +1,78 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: kernel namespaces deques accessors sets sequences assocs fry +dlists compiler.cfg.def-use compiler.cfg.instructions +compiler.cfg.rpo ; +IN: compiler.cfg.liveness + +! This is a backward dataflow analysis. See http://en.wikipedia.org/wiki/Liveness_analysis + +! Assoc mapping basic blocks to sets of vregs +SYMBOL: live-ins + +: live-in ( basic-block -- set ) live-ins get at ; + +! Assoc mapping basic blocks to sequences of sets of vregs; each sequence +! is in conrrespondence with a predecessor +SYMBOL: phi-live-ins + +: phi-live-in ( predecessor basic-block -- set ) + [ predecessors>> index ] keep phi-live-ins get at + dup [ nth ] [ 2drop f ] if ; + +! Assoc mapping basic blocks to sets of vregs +SYMBOL: live-outs + +: live-out ( basic-block -- set ) live-outs get at ; + +SYMBOL: work-list + +: add-to-work-list ( basic-blocks -- ) + work-list get '[ _ push-front ] each ; + +: map-unique ( seq quot -- assoc ) + map concat unique ; inline + +: gen-set ( instructions -- seq ) + [ ##phi? not ] filter [ uses-vregs ] map-unique ; + +: kill-set ( instructions -- seq ) + [ [ defs-vregs ] [ temp-vregs ] bi append ] map-unique ; + +: compute-live-in ( basic-block -- live-in ) + dup instructions>> + [ [ live-out ] [ gen-set ] bi* assoc-union ] + [ nip kill-set ] + 2bi assoc-diff ; + +: compute-phi-live-in ( basic-block -- phi-live-in ) + instructions>> [ ##phi? ] filter + [ f ] [ [ inputs>> ] map flip [ unique ] map ] if-empty ; + +: update-live-in ( basic-block -- changed? ) + [ [ compute-live-in ] keep live-ins get maybe-set-at ] + [ [ compute-phi-live-in ] keep phi-live-ins get maybe-set-at ] + bi and ; + +: compute-live-out ( basic-block -- live-out ) + [ successors>> [ live-in ] map ] + [ dup successors>> [ phi-live-in ] with map ] bi + append assoc-combine ; + +: update-live-out ( basic-block -- changed? ) + [ compute-live-out ] keep + live-outs get maybe-set-at ; + +: liveness-step ( basic-block -- ) + dup update-live-out [ + dup update-live-in + [ predecessors>> add-to-work-list ] [ drop ] if + ] [ drop ] if ; + +: compute-liveness ( cfg -- cfg' ) + work-list set + H{ } clone live-ins set + H{ } clone phi-live-ins set + H{ } clone live-outs set + dup post-order add-to-work-list + work-list get [ liveness-step ] slurp-deque ; diff --git a/basis/compiler/cfg/local/authors.txt b/basis/compiler/cfg/local/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/local/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/local/local.factor b/basis/compiler/cfg/local/local.factor new file mode 100644 index 0000000000..5d78397998 --- /dev/null +++ b/basis/compiler/cfg/local/local.factor @@ -0,0 +1,10 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: fry accessors kernel assocs compiler.cfg.liveness compiler.cfg.rpo ; +IN: compiler.cfg.local + +: optimize-basic-block ( bb init-quot insn-quot -- ) + [ '[ live-in keys @ ] ] [ '[ _ change-instructions drop ] ] bi* bi ; inline + +: local-optimization ( cfg init-quot: ( live-in -- ) insn-quot: ( insns -- insns' ) -- cfg' ) + [ dup ] 2dip '[ _ _ optimize-basic-block ] each-basic-block ; inline \ No newline at end of file diff --git a/basis/compiler/cfg/mr/authors.txt b/basis/compiler/cfg/mr/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/mr/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/mr/mr.factor b/basis/compiler/cfg/mr/mr.factor new file mode 100644 index 0000000000..9f6a62090c --- /dev/null +++ b/basis/compiler/cfg/mr/mr.factor @@ -0,0 +1,14 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: compiler.cfg.linearization compiler.cfg.two-operand +compiler.cfg.liveness compiler.cfg.gc-checks compiler.cfg.linear-scan +compiler.cfg.build-stack-frame compiler.cfg.rpo ; +IN: compiler.cfg.mr + +: build-mr ( cfg -- mr ) + convert-two-operand + compute-liveness + insert-gc-checks + linear-scan + flatten-cfg + build-stack-frame ; \ No newline at end of file diff --git a/basis/compiler/cfg/optimizer/optimizer-tests.factor b/basis/compiler/cfg/optimizer/optimizer-tests.factor new file mode 100644 index 0000000000..b95a8c79ea --- /dev/null +++ b/basis/compiler/cfg/optimizer/optimizer-tests.factor @@ -0,0 +1,34 @@ +USING: arrays sequences tools.test compiler.cfg.checker compiler.cfg.debugger +compiler.cfg.def-use sets kernel kernel.private fry slots.private vectors +sequences.private math sbufs math.private slots.private strings ; +IN: compiler.cfg.optimizer.tests + +! Miscellaneous tests + +: more? ( x -- ? ) ; + +: test-case-1 ( -- ? ) f ; + +: test-case-2 ( -- ) + test-case-1 [ test-case-2 ] [ ] if ; inline recursive + +{ + [ 1array ] + [ 1 2 ? ] + [ { array } declare [ ] map ] + [ { array } declare dup 1 slot [ 1 slot ] when ] + [ [ dup more? ] [ dup ] produce ] + [ vector new over test-case-1 [ test-case-2 ] [ ] if ] + [ [ [ nth-unsafe ".." = 0 ] dip set-nth-unsafe ] 2curry (each-integer) ] + [ + { fixnum sbuf } declare 2dup 3 slot fixnum> [ + over 3 fixnum* over dup [ 2 slot resize-string ] dip 2 set-slot + ] [ ] if + ] + [ [ 2 fixnum* ] when 3 ] + [ [ 2 fixnum+ ] when 3 ] + [ [ 2 fixnum- ] when 3 ] + [ 10000 [ ] times ] +} [ + [ [ ] ] dip '[ _ test-mr first check-mr ] unit-test +] each diff --git a/basis/compiler/cfg/optimizer/optimizer.factor b/basis/compiler/cfg/optimizer/optimizer.factor index 7887faeb61..9d481ef1d2 100644 --- a/basis/compiler/cfg/optimizer/optimizer.factor +++ b/basis/compiler/cfg/optimizer/optimizer.factor @@ -1,29 +1,39 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: kernel sequences compiler.cfg.rpo -compiler.cfg.instructions +USING: kernel sequences accessors combinators namespaces compiler.cfg.predecessors compiler.cfg.useless-blocks compiler.cfg.height +compiler.cfg.stack-analysis compiler.cfg.alias-analysis compiler.cfg.value-numbering -compiler.cfg.dead-code -compiler.cfg.write-barrier ; +compiler.cfg.dce +compiler.cfg.write-barrier +compiler.cfg.liveness +compiler.cfg.rpo +compiler.cfg.phi-elimination +compiler.cfg.checker ; IN: compiler.cfg.optimizer -: trivial? ( insns -- ? ) - dup length 2 = [ first ##call? ] [ drop f ] if ; +SYMBOL: check-optimizer? + +: ?check ( cfg -- cfg' ) + check-optimizer? get [ + dup check-cfg + ] when ; : optimize-cfg ( cfg -- cfg' ) - compute-predecessors - delete-useless-blocks - delete-useless-conditionals [ - dup trivial? [ - normalize-height - alias-analysis - value-numbering - eliminate-dead-code - eliminate-write-barriers - ] unless - ] change-basic-blocks ; + compute-predecessors + delete-useless-blocks + delete-useless-conditionals + normalize-height + stack-analysis + compute-liveness + alias-analysis + value-numbering + eliminate-dead-code + eliminate-write-barriers + eliminate-phis + ?check + ] with-scope ; diff --git a/basis/compiler/cfg/phi-elimination/authors.txt b/basis/compiler/cfg/phi-elimination/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/phi-elimination/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/phi-elimination/phi-elimination.factor b/basis/compiler/cfg/phi-elimination/phi-elimination.factor new file mode 100644 index 0000000000..3ebf553a45 --- /dev/null +++ b/basis/compiler/cfg/phi-elimination/phi-elimination.factor @@ -0,0 +1,21 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors compiler.cfg compiler.cfg.instructions +compiler.cfg.rpo fry kernel sequences ; +IN: compiler.cfg.phi-elimination + +: insert-copy ( predecessor input output -- ) + '[ _ _ swap ##copy ] add-instructions ; + +: eliminate-phi ( bb ##phi -- ) + [ predecessors>> ] [ [ inputs>> ] [ dst>> ] bi ] bi* + '[ _ insert-copy ] 2each ; + +: eliminate-phi-step ( bb -- ) + dup [ + [ ##phi? ] partition + [ [ eliminate-phi ] with each ] dip + ] change-instructions drop ; + +: eliminate-phis ( cfg -- cfg' ) + dup [ eliminate-phi-step ] each-basic-block ; \ No newline at end of file diff --git a/basis/compiler/cfg/predecessors/predecessors.factor b/basis/compiler/cfg/predecessors/predecessors.factor index 01a2a771bc..54efc53bc4 100644 --- a/basis/compiler/cfg/predecessors/predecessors.factor +++ b/basis/compiler/cfg/predecessors/predecessors.factor @@ -1,10 +1,13 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel accessors sequences compiler.cfg.rpo ; IN: compiler.cfg.predecessors -: (compute-predecessors) ( bb -- ) +: predecessors-step ( bb -- ) dup successors>> [ predecessors>> push ] with each ; : compute-predecessors ( cfg -- cfg' ) - dup [ (compute-predecessors) ] each-basic-block ; + [ [ V{ } clone >>predecessors drop ] each-basic-block ] + [ [ predecessors-step ] each-basic-block ] + [ ] + tri ; diff --git a/basis/compiler/cfg/rpo/rpo.factor b/basis/compiler/cfg/rpo/rpo.factor index bb4153da78..f6a40e17d0 100644 --- a/basis/compiler/cfg/rpo/rpo.factor +++ b/basis/compiler/cfg/rpo/rpo.factor @@ -1,4 +1,4 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel accessors namespaces make math sequences sets assocs fry compiler.cfg compiler.cfg.instructions ; @@ -7,29 +7,29 @@ IN: compiler.cfg.rpo SYMBOL: visited : post-order-traversal ( bb -- ) - dup id>> visited get key? [ drop ] [ - dup id>> visited get conjoin + dup visited get key? [ drop ] [ + dup visited get conjoin [ successors>> [ post-order-traversal ] each ] [ , ] bi ] if ; -: post-order ( bb -- blocks ) - [ post-order-traversal ] { } make ; - : number-blocks ( blocks -- ) - [ >>number drop ] each-index ; + dup length iota + [ >>number drop ] 2each ; -: reverse-post-order ( bb -- blocks ) - H{ } clone visited [ - post-order dup number-blocks - ] with-variable ; inline +: post-order ( cfg -- blocks ) + dup post-order>> [ ] [ + [ + H{ } clone visited set + dup entry>> post-order-traversal + ] { } make dup number-blocks + >>post-order post-order>> + ] ?if ; + +: reverse-post-order ( cfg -- blocks ) + post-order ; inline : each-basic-block ( cfg quot -- ) - [ entry>> reverse-post-order ] dip each ; inline - -: change-basic-blocks ( cfg quot -- cfg' ) - [ '[ _ change-instructions drop ] each-basic-block ] - [ drop ] - 2bi ; inline + [ reverse-post-order ] dip each ; inline diff --git a/basis/compiler/cfg/stack-analysis/authors.txt b/basis/compiler/cfg/stack-analysis/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/stack-analysis/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/stack-analysis/stack-analysis-tests.factor b/basis/compiler/cfg/stack-analysis/stack-analysis-tests.factor new file mode 100644 index 0000000000..3501825704 --- /dev/null +++ b/basis/compiler/cfg/stack-analysis/stack-analysis-tests.factor @@ -0,0 +1,115 @@ +USING: prettyprint compiler.cfg.debugger compiler.cfg.linearization +compiler.cfg.predecessors compiler.cfg.stack-analysis +compiler.cfg.instructions sequences kernel tools.test accessors +sequences.private alien math combinators.private compiler.cfg +compiler.cfg.checker compiler.cfg.height compiler.cfg.rpo +compiler.cfg.dce compiler.cfg.registers compiler.cfg.useless-blocks +sets namespaces ; +IN: compiler.cfg.stack-analysis.tests + +! Fundamental invariant: a basic block should not load or store a value more than once +: check-for-redundant-ops ( cfg -- ) + [ + instructions>> + [ + [ ##peek? ] filter [ loc>> ] map duplicates empty? + [ "Redundant peeks" throw ] unless + ] [ + [ ##replace? ] filter [ loc>> ] map duplicates empty? + [ "Redundant replaces" throw ] unless + ] bi + ] each-basic-block ; + +: test-stack-analysis ( quot -- cfg ) + dup cfg? [ test-cfg first ] unless + compute-predecessors + delete-useless-blocks + delete-useless-conditionals + normalize-height + stack-analysis + dup check-cfg + dup check-for-redundant-ops ; + +: linearize ( cfg -- mr ) + flatten-cfg instructions>> ; + +local-only? off + +[ ] [ [ ] test-stack-analysis drop ] unit-test + +! Only peek once +[ 1 ] [ [ dup drop dup ] test-stack-analysis linearize [ ##peek? ] count ] unit-test + +! Redundant replace is redundant +[ f ] [ [ dup drop ] test-stack-analysis linearize [ ##replace? ] any? ] unit-test +[ f ] [ [ swap swap ] test-stack-analysis linearize [ ##replace? ] any? ] unit-test + +! Replace required here +[ t ] [ [ dup ] test-stack-analysis linearize [ ##replace? ] any? ] unit-test +[ t ] [ [ [ drop 1 ] when ] test-stack-analysis linearize [ ##replace? ] any? ] unit-test + +! Only one replace, at the end +[ 1 ] [ [ [ 1 ] [ 2 ] if ] test-stack-analysis linearize [ ##replace? ] count ] unit-test + +! Do we support the full language? +[ ] [ [ { [ ] [ ] } dispatch ] test-stack-analysis drop ] unit-test +[ ] [ [ { [ ] [ ] } dispatch dup ] test-stack-analysis drop ] unit-test +[ ] [ + [ "int" { "int" "int" } "cdecl" [ + ] alien-callback ] + test-cfg second test-stack-analysis drop +] unit-test + +! Test loops +[ ] [ [ [ t ] loop ] test-stack-analysis drop ] unit-test +[ ] [ [ [ dup ] loop ] test-stack-analysis drop ] unit-test + +! Make sure that peeks are inserted in the right place +[ ] [ [ [ drop 1 ] when ] test-stack-analysis drop ] unit-test + +! This should be a total no-op +[ f ] [ [ [ ] dip ] test-stack-analysis linearize [ ##replace? ] any? ] unit-test + +! Don't insert inc-d/inc-r; that's wrong! +[ 1 ] [ [ dup ] test-stack-analysis linearize [ ##inc-d? ] count ] unit-test + +! Bug in height tracking +[ ] [ [ dup [ ] [ reverse ] if ] test-stack-analysis drop ] unit-test +[ ] [ [ dup [ ] [ dup reverse drop ] if ] test-stack-analysis drop ] unit-test +[ ] [ [ [ drop dup 4.0 > ] find-last-integer ] test-stack-analysis drop ] unit-test + +! Bugs with code that throws +[ ] [ [ [ "Oops" throw ] unless ] test-stack-analysis drop ] unit-test +[ ] [ [ [ ] (( -- * )) call-effect-unsafe ] test-stack-analysis drop ] unit-test +[ ] [ [ dup [ "Oops" throw ] when dup ] test-stack-analysis drop ] unit-test +[ ] [ [ B{ 1 2 3 4 } over [ "Oops" throw ] when swap ] test-stack-analysis drop ] unit-test + +! Make sure the replace stores a value with the right height +[ ] [ + [ [ . ] [ 2drop 1 ] if ] test-stack-analysis eliminate-dead-code linearize + [ ##replace? ] filter [ length 1 assert= ] [ first loc>> D 0 assert= ] bi +] unit-test + +! translate-loc was the wrong way round +[ ] [ + [ 1 2 rot ] test-stack-analysis eliminate-dead-code linearize + [ [ ##load-immediate? ] count 2 assert= ] + [ [ ##peek? ] count 1 assert= ] + [ [ ##replace? ] count 3 assert= ] + tri +] unit-test + +[ ] [ + [ 1 2 ? ] test-stack-analysis eliminate-dead-code linearize + [ [ ##load-immediate? ] count 2 assert= ] + [ [ ##peek? ] count 1 assert= ] + [ [ ##replace? ] count 1 assert= ] + tri +] unit-test + +! Sync before a back-edge, not after +! ##peeks should be inserted before a ##loop-entry +! Don't optimize out the constants +[ 1 t ] [ + [ 1000 [ ] times ] test-stack-analysis eliminate-dead-code linearize + [ [ ##add-imm? ] count ] [ [ ##load-immediate? ] any? ] bi +] unit-test diff --git a/basis/compiler/cfg/stack-analysis/stack-analysis.factor b/basis/compiler/cfg/stack-analysis/stack-analysis.factor new file mode 100644 index 0000000000..4ebdf7012f --- /dev/null +++ b/basis/compiler/cfg/stack-analysis/stack-analysis.factor @@ -0,0 +1,295 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors assocs kernel namespaces math sequences fry grouping +sets make combinators compiler.cfg.copy-prop compiler.cfg.def-use +compiler.cfg.instructions compiler.cfg.registers compiler.cfg.rpo +compiler.cfg.hats compiler.cfg ; +IN: compiler.cfg.stack-analysis + +! Convert stack operations to register operations + +! If 'poisoned' is set, disregard height information. This is set if we don't have +! height change information for an instruction. +TUPLE: state locs>vregs actual-locs>vregs changed-locs ds-height rs-height poisoned? ; + +: ( -- state ) + state new + H{ } clone >>locs>vregs + H{ } clone >>actual-locs>vregs + H{ } clone >>changed-locs + 0 >>ds-height + 0 >>rs-height ; + +M: state clone + call-next-method + [ clone ] change-locs>vregs + [ clone ] change-actual-locs>vregs + [ clone ] change-changed-locs ; + +: loc>vreg ( loc -- vreg ) state get locs>vregs>> at ; + +: record-peek ( dst loc -- ) + state get [ locs>vregs>> set-at ] [ actual-locs>vregs>> set-at ] 3bi ; + +: changed-loc ( loc -- ) + state get changed-locs>> conjoin ; + +: record-replace ( src loc -- ) + dup changed-loc state get locs>vregs>> set-at ; + +GENERIC: height-for ( loc -- n ) + +M: ds-loc height-for drop state get ds-height>> ; +M: rs-loc height-for drop state get rs-height>> ; + +: (translate-loc) ( loc -- n height ) [ n>> ] [ height-for ] bi ; inline + +GENERIC: translate-loc ( loc -- loc' ) + +M: ds-loc translate-loc (translate-loc) - ; +M: rs-loc translate-loc (translate-loc) - ; + +GENERIC: untranslate-loc ( loc -- loc' ) + +M: ds-loc untranslate-loc (translate-loc) + ; +M: rs-loc untranslate-loc (translate-loc) + ; + +: redundant-replace? ( vreg loc -- ? ) + dup untranslate-loc n>> 0 < + [ 2drop t ] [ state get actual-locs>vregs>> at = ] if ; + +: save-changed-locs ( state -- ) + [ changed-locs>> ] [ locs>vregs>> ] bi '[ + _ at swap 2dup redundant-replace? + [ 2drop ] [ untranslate-loc ##replace ] if + ] assoc-each ; + +: clear-state ( state -- ) + [ locs>vregs>> clear-assoc ] + [ actual-locs>vregs>> clear-assoc ] + [ changed-locs>> clear-assoc ] + tri ; + +ERROR: poisoned-state state ; + +: sync-state ( -- ) + state get { + [ dup poisoned?>> [ poisoned-state ] [ drop ] if ] + [ save-changed-locs ] + [ clear-state ] + } cleave ; + +: poison-state ( -- ) state get t >>poisoned? drop ; + +! Abstract interpretation +GENERIC: visit ( insn -- ) + +! Instructions which don't have any effect on the stack +UNION: neutral-insn + ##flushable + ##effect ; + +M: neutral-insn visit , ; + +UNION: sync-if-back-edge + ##branch + ##conditional-branch + ##compare-imm-branch + ##dispatch + ##loop-entry ; + +SYMBOL: local-only? + +t local-only? set-global + +: back-edge? ( from to -- ? ) + [ number>> ] bi@ > ; + +: sync-state? ( -- ? ) + basic-block get successors>> + [ [ predecessors>> ] keep '[ _ back-edge? ] any? ] any? + local-only? get or ; + +M: sync-if-back-edge visit + sync-state? [ sync-state ] when , ; + +: adjust-d ( n -- ) state get [ + ] change-ds-height drop ; + +M: ##inc-d visit [ , ] [ n>> adjust-d ] bi ; + +: adjust-r ( n -- ) state get [ + ] change-rs-height drop ; + +M: ##inc-r visit [ , ] [ n>> adjust-r ] bi ; + +: eliminate-peek ( dst src -- ) + ! the requested stack location is already in 'src' + [ ##copy ] [ swap copies get set-at ] 2bi ; + +M: ##peek visit + dup + [ dst>> ] [ loc>> translate-loc ] bi + dup loc>vreg dup [ nip eliminate-peek drop ] [ drop record-peek , ] if ; + +M: ##replace visit + [ src>> resolve ] [ loc>> translate-loc ] bi + record-replace ; + +M: ##copy visit + [ call-next-method ] [ record-copy ] bi ; + +M: ##call visit + [ call-next-method ] [ height>> adjust-d ] bi ; + +! Instructions that poison the stack state +UNION: poison-insn + ##jump + ##return + ##callback-return + ##fixnum-mul-tail + ##fixnum-add-tail + ##fixnum-sub-tail ; + +M: poison-insn visit call-next-method poison-state ; + +! Instructions that kill all live vregs +UNION: kill-vreg-insn + poison-insn + ##stack-frame + ##call + ##prologue + ##epilogue + ##fixnum-mul + ##fixnum-add + ##fixnum-sub + ##alien-invoke + ##alien-indirect ; + +M: kill-vreg-insn visit sync-state , ; + +: visit-alien-node ( node -- ) + params>> [ out-d>> length ] [ in-d>> length ] bi - adjust-d ; + +M: ##alien-invoke visit + [ call-next-method ] [ visit-alien-node ] bi ; + +M: ##alien-indirect visit + [ call-next-method ] [ visit-alien-node ] bi ; + +M: ##alien-callback visit , ; + +! Maps basic-blocks to states +SYMBOLS: state-in state-out ; + +: initial-state ( bb states -- state ) 2drop ; + +: single-predecessor ( bb states -- state ) nip first clone ; + +ERROR: must-equal-failed seq ; + +: must-equal ( seq -- elt ) + dup all-equal? [ first ] [ must-equal-failed ] if ; + +: merge-heights ( state predecessors states -- state ) + nip + [ [ ds-height>> ] map must-equal >>ds-height ] + [ [ rs-height>> ] map must-equal >>rs-height ] bi ; + +: insert-peek ( predecessor loc -- vreg ) + ! XXX critical edges + '[ _ ^^peek ] add-instructions ; + +: merge-loc ( predecessors locs>vregs loc -- vreg ) + ! Insert a ##phi in the current block where the input + ! is the vreg storing loc from each predecessor block + [ '[ [ _ ] dip at ] map ] keep + '[ [ ] [ _ insert-peek ] ?if ] 2map + dup all-equal? [ first ] [ ^^phi ] if ; + +: (merge-locs) ( predecessors assocs -- assoc ) + dup [ keys ] map concat prune + [ [ 2nip ] [ merge-loc ] 3bi ] with with + H{ } map>assoc ; + +: merge-locs ( state predecessors states -- state ) + [ locs>vregs>> ] map (merge-locs) >>locs>vregs ; + +: merge-loc' ( locs>vregs loc -- vreg ) + ! Insert a ##phi in the current block where the input + ! is the vreg storing loc from each predecessor block + '[ [ _ ] dip at ] map + dup all-equal? [ first ] [ drop f ] if ; + +: merge-actual-locs ( state predecessors states -- state ) + nip + [ actual-locs>vregs>> ] map + dup [ keys ] map concat prune + [ [ nip ] [ merge-loc' ] 2bi ] with + H{ } map>assoc + [ nip ] assoc-filter + >>actual-locs>vregs ; + +: merge-changed-locs ( state predecessors states -- state ) + nip [ changed-locs>> ] map assoc-combine >>changed-locs ; + +ERROR: cannot-merge-poisoned states ; + +: multiple-predecessors ( bb states -- state ) + dup [ not ] any? [ + [ ] 2dip + sift merge-heights + ] [ + dup [ poisoned?>> ] any? [ + cannot-merge-poisoned + ] [ + [ state new ] 2dip + [ predecessors>> ] dip + { + [ merge-locs ] + [ merge-actual-locs ] + [ merge-heights ] + [ merge-changed-locs ] + } 2cleave + ] if + ] if ; + +: merge-states ( bb states -- state ) + ! If any states are poisoned, save all registers + ! to the stack in each branch + dup length { + { 0 [ initial-state ] } + { 1 [ single-predecessor ] } + [ drop multiple-predecessors ] + } case ; + +: block-in-state ( bb -- states ) + dup predecessors>> state-out get '[ _ at ] map merge-states ; + +: set-block-in-state ( state bb -- ) + [ clone ] dip state-in get set-at ; + +: set-block-out-state ( state bb -- ) + [ clone ] dip state-out get set-at ; + +: visit-block ( bb -- ) + ! block-in-state may add phi nodes at the start of the basic block + ! so we wrap the whole thing with a 'make' + [ + dup basic-block set + dup block-in-state + [ swap set-block-in-state ] [ + state [ + [ instructions>> [ visit ] each ] + [ [ state get ] dip set-block-out-state ] + [ ] + tri + ] with-variable + ] 2bi + ] V{ } make >>instructions drop ; + +: stack-analysis ( cfg -- cfg' ) + [ + H{ } clone copies set + H{ } clone state-in set + H{ } clone state-out set + dup [ visit-block ] each-basic-block + ] with-scope ; diff --git a/basis/compiler/cfg/stack-frame/authors.txt b/basis/compiler/cfg/stack-frame/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/basis/compiler/cfg/stack-frame/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/basis/compiler/cfg/stack-frame/stack-frame.factor b/basis/compiler/cfg/stack-frame/stack-frame.factor index d545b6d15c..5cb5762b78 100644 --- a/basis/compiler/cfg/stack-frame/stack-frame.factor +++ b/basis/compiler/cfg/stack-frame/stack-frame.factor @@ -1,72 +1,55 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: namespaces accessors math.order assocs kernel sequences -combinators make classes words cpu.architecture -compiler.cfg.instructions compiler.cfg.registers ; +USING: math math.order namespaces accessors kernel layouts combinators +combinators.smart assocs sequences cpu.architecture ; IN: compiler.cfg.stack-frame -SYMBOL: frame-required? +TUPLE: stack-frame +{ params integer } +{ return integer } +{ total-size integer } +{ gc-root-size integer } +spill-counts ; -SYMBOL: spill-counts +! Stack frame utilities +: param-base ( -- n ) + stack-frame get [ params>> ] [ return>> ] bi + ; -GENERIC: compute-stack-frame* ( insn -- ) +: spill-float-offset ( n -- offset ) + double-float-regs reg-size * ; + +: spill-integer-base ( -- n ) + stack-frame get spill-counts>> double-float-regs [ swap at ] keep reg-size * + param-base + ; + +: spill-integer-offset ( n -- offset ) + cells spill-integer-base + ; + +: spill-area-size ( stack-frame -- n ) + spill-counts>> [ swap reg-size * ] { } assoc>map sum ; + +: gc-root-base ( -- n ) + stack-frame get spill-area-size + param-base + ; + +: gc-root-offset ( n -- n' ) gc-root-base + ; + +: gc-roots-size ( live-registers live-spill-slots -- n ) + [ keys [ reg-class>> reg-size ] sigma ] bi@ + ; + +: (stack-frame-size) ( stack-frame -- n ) + [ + { + [ spill-area-size ] + [ gc-root-size>> ] + [ params>> ] + [ return>> ] + } cleave + ] sum-outputs ; : max-stack-frame ( frame1 frame2 -- frame3 ) [ stack-frame new ] 2dip [ [ params>> ] bi@ max >>params ] [ [ return>> ] bi@ max >>return ] - 2bi ; - -M: ##stack-frame compute-stack-frame* - frame-required? on - stack-frame>> stack-frame [ max-stack-frame ] change ; - -M: ##call compute-stack-frame* - word>> sub-primitive>> [ frame-required? on ] unless ; - -M: _spill-counts compute-stack-frame* - counts>> stack-frame get (>>spill-counts) ; - -M: insn compute-stack-frame* - class frame-required? word-prop [ - frame-required? on - ] when ; - -\ _gc t frame-required? set-word-prop -\ _spill t frame-required? set-word-prop -\ ##fixnum-add t frame-required? set-word-prop -\ ##fixnum-sub t frame-required? set-word-prop -\ ##fixnum-mul t frame-required? set-word-prop -\ ##fixnum-add-tail f frame-required? set-word-prop -\ ##fixnum-sub-tail f frame-required? set-word-prop -\ ##fixnum-mul-tail f frame-required? set-word-prop - -: compute-stack-frame ( insns -- ) - frame-required? off - T{ stack-frame } clone stack-frame set - [ compute-stack-frame* ] each - stack-frame get dup stack-frame-size >>total-size drop ; - -GENERIC: insert-pro/epilogues* ( insn -- ) - -M: ##stack-frame insert-pro/epilogues* drop ; - -M: ##prologue insert-pro/epilogues* - drop frame-required? get [ stack-frame get _prologue ] when ; - -M: ##epilogue insert-pro/epilogues* - drop frame-required? get [ stack-frame get _epilogue ] when ; - -M: insn insert-pro/epilogues* , ; - -: insert-pro/epilogues ( insns -- insns ) - [ [ insert-pro/epilogues* ] each ] { } make ; - -: build-stack-frame ( mr -- mr ) - [ - [ - [ compute-stack-frame ] - [ insert-pro/epilogues ] - bi - ] change-instructions - ] with-scope ; + [ [ gc-root-size>> ] bi@ max >>gc-root-size ] + 2tri ; \ No newline at end of file diff --git a/basis/compiler/cfg/two-operand/two-operand.factor b/basis/compiler/cfg/two-operand/two-operand.factor index dabecaeec4..d30a02b0d3 100644 --- a/basis/compiler/cfg/two-operand/two-operand.factor +++ b/basis/compiler/cfg/two-operand/two-operand.factor @@ -1,40 +1,39 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: accessors arrays kernel sequences compiler.utilities -compiler.cfg.instructions cpu.architecture ; +USING: accessors kernel sequences make compiler.cfg.instructions +compiler.cfg.local cpu.architecture ; IN: compiler.cfg.two-operand ! On x86, instructions take the form x = x op y ! Our SSA IR is x = y op z -! We don't bother with ##add, ##add-imm or ##sub-imm since x86 -! has a LEA instruction which is effectively a three-operand -! addition +! We don't bother with ##add, ##add-imm, ##sub-imm or ##mul-imm +! since x86 has LEA and IMUL instructions which are effectively +! three-operand addition and multiplication, respectively. -: make-copy ( dst src -- insn ) f \ ##copy boa ; inline +: make-copy ( dst src -- insn ) \ ##copy new-insn ; inline -: make-copy/float ( dst src -- insn ) f \ ##copy-float boa ; inline +: make-copy/float ( dst src -- insn ) \ ##copy-float new-insn ; inline -: convert-two-operand/integer ( insn -- insns ) - [ [ dst>> ] [ src1>> ] bi make-copy ] - [ dup dst>> >>src1 ] - bi 2array ; inline +: convert-two-operand/integer ( insn -- ) + [ [ dst>> ] [ src1>> ] bi ##copy ] + [ dup dst>> >>src1 , ] + bi ; inline -: convert-two-operand/float ( insn -- insns ) - [ [ dst>> ] [ src1>> ] bi make-copy/float ] - [ dup dst>> >>src1 ] - bi 2array ; inline +: convert-two-operand/float ( insn -- ) + [ [ dst>> ] [ src1>> ] bi ##copy-float ] + [ dup dst>> >>src1 , ] + bi ; inline -GENERIC: convert-two-operand* ( insn -- insns ) +GENERIC: convert-two-operand* ( insn -- ) M: ##not convert-two-operand* - [ [ dst>> ] [ src>> ] bi make-copy ] - [ dup dst>> >>src ] - bi 2array ; + [ [ dst>> ] [ src>> ] bi ##copy ] + [ dup dst>> >>src , ] + bi ; M: ##sub convert-two-operand* convert-two-operand/integer ; M: ##mul convert-two-operand* convert-two-operand/integer ; -M: ##mul-imm convert-two-operand* convert-two-operand/integer ; M: ##and convert-two-operand* convert-two-operand/integer ; M: ##and-imm convert-two-operand* convert-two-operand/integer ; M: ##or convert-two-operand* convert-two-operand/integer ; @@ -50,11 +49,11 @@ M: ##sub-float convert-two-operand* convert-two-operand/float ; M: ##mul-float convert-two-operand* convert-two-operand/float ; M: ##div-float convert-two-operand* convert-two-operand/float ; -M: insn convert-two-operand* ; +M: insn convert-two-operand* , ; -: convert-two-operand ( mr -- mr' ) - [ - two-operand? [ - [ convert-two-operand* ] map-flat - ] when - ] change-instructions ; +: convert-two-operand ( cfg -- cfg' ) + two-operand? [ + [ drop ] + [ [ [ convert-two-operand* ] each ] V{ } make ] + local-optimization + ] when ; diff --git a/basis/compiler/cfg/useless-blocks/useless-blocks-tests.factor b/basis/compiler/cfg/useless-blocks/useless-blocks-tests.factor new file mode 100644 index 0000000000..1d14cef193 --- /dev/null +++ b/basis/compiler/cfg/useless-blocks/useless-blocks-tests.factor @@ -0,0 +1,11 @@ +IN: compiler.cfg.useless-blocks.tests +USING: fry kernel sequences compiler.cfg.useless-blocks compiler.cfg.checker +compiler.cfg.debugger compiler.cfg.predecessors tools.test ; + +{ + [ [ drop 1 ] when ] + [ [ drop 1 ] unless ] +} [ + [ [ ] ] dip + '[ _ test-cfg first compute-predecessors delete-useless-blocks check-cfg ] unit-test +] each \ No newline at end of file diff --git a/basis/compiler/cfg/useless-blocks/useless-blocks.factor b/basis/compiler/cfg/useless-blocks/useless-blocks.factor index 05cb13748b..cbe006b4d7 100644 --- a/basis/compiler/cfg/useless-blocks/useless-blocks.factor +++ b/basis/compiler/cfg/useless-blocks/useless-blocks.factor @@ -1,10 +1,12 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: kernel accessors sequences combinators classes vectors -compiler.cfg compiler.cfg.rpo compiler.cfg.instructions ; +USING: kernel accessors sequences combinators combinators.short-circuit +classes vectors compiler.cfg compiler.cfg.instructions compiler.cfg.rpo ; IN: compiler.cfg.useless-blocks : update-predecessor-for-delete ( bb -- ) + ! We have to replace occurrences of bb with bb's successor + ! in bb's predecessor's list of successors. dup predecessors>> first [ [ 2dup eq? [ drop successors>> first ] [ nip ] if @@ -12,9 +14,13 @@ IN: compiler.cfg.useless-blocks ] change-successors drop ; : update-successor-for-delete ( bb -- ) - [ predecessors>> first ] - [ successors>> first predecessors>> ] - bi set-first ; + ! We have to replace occurrences of bb with bb's predecessor + ! in bb's sucessor's list of predecessors. + dup successors>> first [ + [ + 2dup eq? [ drop predecessors>> first ] [ nip ] if + ] with map + ] change-predecessors drop ; : delete-basic-block ( bb -- ) [ update-predecessor-for-delete ] @@ -23,17 +29,17 @@ IN: compiler.cfg.useless-blocks : delete-basic-block? ( bb -- ? ) { - { [ dup instructions>> length 1 = not ] [ f ] } - { [ dup predecessors>> length 1 = not ] [ f ] } - { [ dup successors>> length 1 = not ] [ f ] } - { [ dup instructions>> first ##branch? not ] [ f ] } - [ t ] - } cond nip ; + [ instructions>> length 1 = ] + [ predecessors>> length 1 = ] + [ successors>> length 1 = ] + [ instructions>> first ##branch? ] + } 1&& ; : delete-useless-blocks ( cfg -- cfg' ) dup [ dup delete-basic-block? [ delete-basic-block ] [ drop ] if - ] each-basic-block ; + ] each-basic-block + f >>post-order ; : delete-conditional? ( bb -- ? ) dup instructions>> [ drop f ] [ @@ -46,10 +52,11 @@ IN: compiler.cfg.useless-blocks : delete-conditional ( bb -- ) dup successors>> first 1vector >>successors - [ but-last f \ ##branch boa suffix ] change-instructions + [ but-last \ ##branch new-insn suffix ] change-instructions drop ; : delete-useless-conditionals ( cfg -- cfg' ) dup [ dup delete-conditional? [ delete-conditional ] [ drop ] if - ] each-basic-block ; + ] each-basic-block + f >>post-order ; diff --git a/basis/compiler/cfg/utilities/utilities.factor b/basis/compiler/cfg/utilities/utilities.factor index 99a138a763..e415008808 100644 --- a/basis/compiler/cfg/utilities/utilities.factor +++ b/basis/compiler/cfg/utilities/utilities.factor @@ -35,5 +35,8 @@ IN: compiler.cfg.utilities : stop-iterating ( -- next ) end-basic-block f ; +: call-height ( ##call -- n ) + [ out-d>> length ] [ in-d>> length ] bi - ; + : emit-primitive ( node -- ) - word>> ##call ##branch begin-basic-block ; + [ word>> ] [ call-height ] bi ##call ##branch begin-basic-block ; diff --git a/basis/compiler/cfg/value-numbering/expressions/expressions.factor b/basis/compiler/cfg/value-numbering/expressions/expressions.factor index cc790c6c0a..bf750231c7 100644 --- a/basis/compiler/cfg/value-numbering/expressions/expressions.factor +++ b/basis/compiler/cfg/value-numbering/expressions/expressions.factor @@ -22,17 +22,17 @@ M: constant-expr equal? and ] [ 2drop f ] if ; -SYMBOL: input-expr-counter - -: next-input-expr ( -- n ) - input-expr-counter [ dup 1 + ] change ; - ! Expressions whose values are inputs to the basic block. We ! can eliminate a second computation having the same 'n' as ! the first one; we can also eliminate input-exprs whose ! result is not used. TUPLE: input-expr < expr n ; +SYMBOL: input-expr-counter + +: next-input-expr ( class -- expr ) + input-expr-counter [ dup 1 + ] change input-expr boa ; + : constant>vn ( constant -- vn ) expr>vn ; inline GENERIC: >expr ( insn -- expr ) @@ -80,7 +80,7 @@ M: ##compare-imm >expr compare-imm>expr ; M: ##compare-float >expr compare>expr ; -M: ##flushable >expr class next-input-expr input-expr boa ; +M: ##flushable >expr class next-input-expr ; : init-expressions ( -- ) 0 input-expr-counter set ; diff --git a/basis/compiler/cfg/value-numbering/rewrite/rewrite.factor b/basis/compiler/cfg/value-numbering/rewrite/rewrite.factor index 990543ed7a..7630d0a658 100644 --- a/basis/compiler/cfg/value-numbering/rewrite/rewrite.factor +++ b/basis/compiler/cfg/value-numbering/rewrite/rewrite.factor @@ -13,7 +13,7 @@ GENERIC: rewrite ( insn -- insn' ) M: ##mul-imm rewrite dup src2>> dup power-of-2? [ - [ [ dst>> ] [ src1>> ] bi ] [ log2 ] bi* f \ ##shl-imm boa + [ [ dst>> ] [ src1>> ] bi ] [ log2 ] bi* \ ##shl-imm new-insn dup number-values ] [ drop ] if ; @@ -36,9 +36,9 @@ M: ##mul-imm rewrite : rewrite-boolean-comparison ( expr -- insn ) src1>> vreg>expr dup op>> { - { \ ##compare [ >compare-expr< f \ ##compare-branch boa ] } - { \ ##compare-imm [ >compare-imm-expr< f \ ##compare-imm-branch boa ] } - { \ ##compare-float [ >compare-expr< f \ ##compare-float-branch boa ] } + { \ ##compare [ >compare-expr< \ ##compare-branch new-insn ] } + { \ ##compare-imm [ >compare-imm-expr< \ ##compare-imm-branch new-insn ] } + { \ ##compare-float [ >compare-expr< \ ##compare-float-branch new-insn ] } } case ; : tag-fixnum-expr? ( expr -- ? ) @@ -60,11 +60,11 @@ M: ##mul-imm rewrite GENERIC: rewrite-tagged-comparison ( insn -- insn' ) M: ##compare-imm-branch rewrite-tagged-comparison - (rewrite-tagged-comparison) f \ ##compare-imm-branch boa ; + (rewrite-tagged-comparison) \ ##compare-imm-branch new-insn ; M: ##compare-imm rewrite-tagged-comparison [ dst>> ] [ (rewrite-tagged-comparison) ] bi - i f \ ##compare-imm boa ; + i \ ##compare-imm new-insn ; M: ##compare-imm-branch rewrite dup rewrite-boolean-comparison? [ rewrite-boolean-comparison ] when @@ -79,7 +79,7 @@ M: ##compare-imm-branch rewrite [ dst>> ] [ src2>> ] [ src1>> vreg>vn vn>constant ] tri - cc= f i \ ##compare-imm boa ; + cc= i \ ##compare-imm new-insn ; M: ##compare rewrite dup flip-comparison? [ @@ -96,9 +96,9 @@ M: ##compare rewrite : rewrite-redundant-comparison ( insn -- insn' ) [ cc>> ] [ dst>> ] [ src1>> vreg>expr dup op>> ] tri { - { \ ##compare [ >compare-expr< i f \ ##compare boa ] } - { \ ##compare-imm [ >compare-imm-expr< i f \ ##compare-imm boa ] } - { \ ##compare-float [ >compare-expr< i f \ ##compare-float boa ] } + { \ ##compare [ >compare-expr< i \ ##compare new-insn ] } + { \ ##compare-imm [ >compare-imm-expr< i \ ##compare-imm new-insn ] } + { \ ##compare-float [ >compare-expr< i \ ##compare-float new-insn ] } } case swap cc= eq? [ [ negate-cc ] change-cc ] when ; @@ -114,18 +114,4 @@ M: ##compare-imm rewrite ] when ] when ; -: dispatch-offset ( expr -- n ) - [ in2>> vn>constant tag-bits get neg shift ] [ op>> ] bi - \ ##sub-imm eq? [ neg ] when ; - -: add-dispatch-offset? ( insn -- expr ? ) - src>> vreg>expr dup op>> { ##add-imm ##sub-imm } memq? ; inline - -M: ##dispatch rewrite - dup add-dispatch-offset? [ - [ clone ] dip - [ in1>> vn>vreg >>src ] - [ dispatch-offset '[ _ + ] change-offset ] bi - ] [ drop ] if ; - M: insn rewrite ; diff --git a/basis/compiler/cfg/value-numbering/value-numbering-tests.factor b/basis/compiler/cfg/value-numbering/value-numbering-tests.factor index abd2720817..5063273bf4 100644 --- a/basis/compiler/cfg/value-numbering/value-numbering-tests.factor +++ b/basis/compiler/cfg/value-numbering/value-numbering-tests.factor @@ -2,7 +2,7 @@ IN: compiler.cfg.value-numbering.tests USING: compiler.cfg.value-numbering compiler.cfg.instructions compiler.cfg.registers compiler.cfg.debugger cpu.architecture tools.test kernel math combinators.short-circuit accessors -sequences ; +sequences compiler.cfg vectors arrays ; : trim-temps ( insns -- insns ) [ @@ -13,6 +13,10 @@ sequences ; } 1|| [ f >>temp ] when ] map ; +: test-value-numbering ( insns -- insns ) + { } init-value-numbering + value-numbering-step ; + [ { T{ ##peek f V int-regs 45 D 1 } @@ -24,7 +28,7 @@ sequences ; T{ ##peek f V int-regs 45 D 1 } T{ ##copy f V int-regs 48 V int-regs 45 } T{ ##compare-imm-branch f V int-regs 48 7 cc/= } - } value-numbering + } test-value-numbering ] unit-test [ @@ -40,14 +44,14 @@ sequences ; T{ ##peek f V int-regs 3 D 0 } T{ ##slot-imm f V int-regs 4 V int-regs 3 1 3 } T{ ##replace f V int-regs 4 D 0 } - } value-numbering + } test-value-numbering ] unit-test [ t ] [ { T{ ##peek f V int-regs 1 D 0 } - T{ ##dispatch f V int-regs 1 V int-regs 2 0 } - } dup value-numbering = + T{ ##dispatch f V int-regs 1 V int-regs 2 } + } dup test-value-numbering = ] unit-test [ t ] [ @@ -60,7 +64,7 @@ sequences ; T{ ##alien-unsigned-1 f V int-regs 22 V int-regs 21 } T{ ##shl-imm f V int-regs 23 V int-regs 22 3 } T{ ##replace f V int-regs 23 D 0 } - } dup value-numbering = + } dup test-value-numbering = ] unit-test [ @@ -76,7 +80,7 @@ sequences ; T{ ##mul-imm f V int-regs 2 V int-regs 1 8 } T{ ##shr-imm f V int-regs 3 V int-regs 2 3 } T{ ##replace f V int-regs 3 D 0 } - } value-numbering + } test-value-numbering ] unit-test [ @@ -94,7 +98,7 @@ sequences ; T{ ##compare f V int-regs 4 V int-regs 2 V int-regs 1 cc> } T{ ##compare-imm f V int-regs 6 V int-regs 4 5 cc/= } T{ ##replace f V int-regs 6 D 0 } - } value-numbering trim-temps + } test-value-numbering trim-temps ] unit-test [ @@ -112,7 +116,7 @@ sequences ; T{ ##compare f V int-regs 4 V int-regs 2 V int-regs 1 cc<= } T{ ##compare-imm f V int-regs 6 V int-regs 4 5 cc= } T{ ##replace f V int-regs 6 D 0 } - } value-numbering trim-temps + } test-value-numbering trim-temps ] unit-test [ @@ -134,7 +138,7 @@ sequences ; T{ ##compare-float f V int-regs 12 V double-float-regs 10 V double-float-regs 11 cc< } T{ ##compare-imm f V int-regs 14 V int-regs 12 5 cc= } T{ ##replace f V int-regs 14 D 0 } - } value-numbering trim-temps + } test-value-numbering trim-temps ] unit-test [ @@ -150,5 +154,18 @@ sequences ; T{ ##peek f V int-regs 30 D -2 } T{ ##compare f V int-regs 33 V int-regs 29 V int-regs 30 cc<= } T{ ##compare-imm-branch f V int-regs 33 5 cc/= } - } value-numbering trim-temps + } test-value-numbering trim-temps +] unit-test + +[ + { + T{ ##copy f V int-regs 48 V int-regs 45 } + T{ ##compare-imm-branch f V int-regs 45 7 cc/= } + } +] [ + { V int-regs 45 } init-value-numbering + { + T{ ##copy f V int-regs 48 V int-regs 45 } + T{ ##compare-imm-branch f V int-regs 48 7 cc/= } + } value-numbering-step ] unit-test diff --git a/basis/compiler/cfg/value-numbering/value-numbering.factor b/basis/compiler/cfg/value-numbering/value-numbering.factor index d17b2a7e1f..9f5473c62f 100644 --- a/basis/compiler/cfg/value-numbering/value-numbering.factor +++ b/basis/compiler/cfg/value-numbering/value-numbering.factor @@ -2,6 +2,8 @@ ! See http://factorcode.org/license.txt for BSD license. USING: namespaces assocs biassocs classes kernel math accessors sorting sets sequences +compiler.cfg.local +compiler.cfg.liveness compiler.cfg.value-numbering.graph compiler.cfg.value-numbering.expressions compiler.cfg.value-numbering.propagate @@ -9,7 +11,16 @@ compiler.cfg.value-numbering.simplify compiler.cfg.value-numbering.rewrite ; IN: compiler.cfg.value-numbering -: value-numbering ( insns -- insns' ) +: number-input-values ( live-in -- ) + [ [ f next-input-expr simplify ] dip set-vn ] each ; + +: init-value-numbering ( live-in -- ) init-value-graph init-expressions + number-input-values ; + +: value-numbering-step ( insns -- insns' ) [ [ number-values ] [ rewrite propagate ] bi ] map ; + +: value-numbering ( cfg -- cfg' ) + [ init-value-numbering ] [ value-numbering-step ] local-optimization ; diff --git a/basis/compiler/cfg/write-barrier/write-barrier-tests.factor b/basis/compiler/cfg/write-barrier/write-barrier-tests.factor index 73748dbc37..c1a667c004 100644 --- a/basis/compiler/cfg/write-barrier/write-barrier-tests.factor +++ b/basis/compiler/cfg/write-barrier/write-barrier-tests.factor @@ -1,8 +1,11 @@ USING: compiler.cfg.write-barrier compiler.cfg.instructions compiler.cfg.registers compiler.cfg.debugger cpu.architecture -arrays tools.test ; +arrays tools.test vectors compiler.cfg kernel accessors ; IN: compiler.cfg.write-barrier.tests +: test-write-barrier ( insns -- insns ) + write-barriers-step ; + [ { T{ ##peek f V int-regs 4 D 0 f } @@ -24,7 +27,7 @@ IN: compiler.cfg.write-barrier.tests T{ ##set-slot-imm f V int-regs 6 V int-regs 7 2 3 } T{ ##write-barrier f V int-regs 7 V int-regs 12 V int-regs 13 } T{ ##replace f V int-regs 7 D 0 } - } eliminate-write-barriers + } test-write-barrier ] unit-test [ @@ -42,7 +45,7 @@ IN: compiler.cfg.write-barrier.tests T{ ##peek f V int-regs 6 D -2 } T{ ##set-slot-imm f V int-regs 5 V int-regs 6 3 2 } T{ ##write-barrier f V int-regs 6 V int-regs 7 V int-regs 8 } - } eliminate-write-barriers + } test-write-barrier ] unit-test [ @@ -69,5 +72,5 @@ IN: compiler.cfg.write-barrier.tests T{ ##copy f V int-regs 29 V int-regs 19 } T{ ##set-slot-imm f V int-regs 28 V int-regs 29 4 2 } T{ ##write-barrier f V int-regs 29 V int-regs 30 V int-regs 3 } - } eliminate-write-barriers + } test-write-barrier ] unit-test diff --git a/basis/compiler/cfg/write-barrier/write-barrier.factor b/basis/compiler/cfg/write-barrier/write-barrier.factor index 4a55cb3266..b260b0464e 100644 --- a/basis/compiler/cfg/write-barrier/write-barrier.factor +++ b/basis/compiler/cfg/write-barrier/write-barrier.factor @@ -1,7 +1,8 @@ -! Copyright (C) 2008 Slava Pestov. +! Copyright (C) 2008, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: kernel accessors namespaces assocs sets sequences locals -compiler.cfg compiler.cfg.instructions compiler.cfg.copy-prop ; +compiler.cfg compiler.cfg.instructions compiler.cfg.copy-prop +compiler.cfg.liveness compiler.cfg.local ; IN: compiler.cfg.write-barrier ! Eliminate redundant write barrier hits. @@ -35,8 +36,11 @@ M: ##set-slot-imm eliminate-write-barrier M: insn eliminate-write-barrier ; -: eliminate-write-barriers ( insns -- insns' ) +: write-barriers-step ( insns -- insns' ) H{ } clone safe set H{ } clone mutated set H{ } clone copies set [ eliminate-write-barrier ] map sift ; + +: eliminate-write-barriers ( cfg -- cfg' ) + [ drop ] [ write-barriers-step ] local-optimization ; diff --git a/basis/compiler/codegen/codegen-tests.factor b/basis/compiler/codegen/codegen-tests.factor new file mode 100644 index 0000000000..9c3817bad6 --- /dev/null +++ b/basis/compiler/codegen/codegen-tests.factor @@ -0,0 +1,14 @@ +IN: compiler.codegen.tests +USING: compiler.codegen.fixup tools.test cpu.architecture math kernel make +compiler.constants ; + +[ ] [ [ ] with-fixup drop ] unit-test +[ ] [ [ \ + %call ] with-fixup drop ] unit-test + +[ ] [ [