compiler: preliminary implementation of tracking derived pointers in GC maps
parent
53aed0805a
commit
6b5fffc026
|
@ -830,13 +830,16 @@ UNION: conditional-branch-insn
|
||||||
UNION: ##read ##slot ##slot-imm ##vm-field ##alien-global ;
|
UNION: ##read ##slot ##slot-imm ##vm-field ##alien-global ;
|
||||||
UNION: ##write ##set-slot ##set-slot-imm ##set-vm-field ;
|
UNION: ##write ##set-slot ##set-slot-imm ##set-vm-field ;
|
||||||
|
|
||||||
! Instructions that contain subroutine calls to functions which
|
UNION: alien-call-insn
|
||||||
! can callback arbitrary Factor code
|
|
||||||
UNION: factor-call-insn
|
|
||||||
##alien-invoke
|
##alien-invoke
|
||||||
##alien-indirect
|
##alien-indirect
|
||||||
##alien-assembly ;
|
##alien-assembly ;
|
||||||
|
|
||||||
|
! Instructions that contain subroutine calls to functions which
|
||||||
|
! can callback arbitrary Factor code
|
||||||
|
UNION: factor-call-insn
|
||||||
|
alien-call-insn ;
|
||||||
|
|
||||||
! Instructions that contain subroutine calls to functions which
|
! Instructions that contain subroutine calls to functions which
|
||||||
! allocate memory
|
! allocate memory
|
||||||
UNION: gc-map-insn
|
UNION: gc-map-insn
|
||||||
|
@ -848,15 +851,10 @@ factor-call-insn ;
|
||||||
M: gc-map-insn clone call-next-method [ clone ] change-gc-map ;
|
M: gc-map-insn clone call-next-method [ clone ] change-gc-map ;
|
||||||
|
|
||||||
! Each one has a gc-map slot
|
! Each one has a gc-map slot
|
||||||
TUPLE: gc-map scrub-d scrub-r gc-roots ;
|
TUPLE: gc-map scrub-d scrub-r gc-roots derived-roots ;
|
||||||
|
|
||||||
: <gc-map> ( -- gc-map ) gc-map new ;
|
: <gc-map> ( -- gc-map ) gc-map new ;
|
||||||
|
|
||||||
UNION: alien-call-insn
|
|
||||||
##alien-invoke
|
|
||||||
##alien-indirect
|
|
||||||
##alien-assembly ;
|
|
||||||
|
|
||||||
! Instructions that clobber registers. They receive inputs and
|
! Instructions that clobber registers. They receive inputs and
|
||||||
! produce outputs in spill slots.
|
! produce outputs in spill slots.
|
||||||
UNION: hairy-clobber-insn
|
UNION: hairy-clobber-insn
|
||||||
|
|
|
@ -146,9 +146,15 @@ RENAMING: assign [ vreg>reg ] [ vreg>reg ] [ vreg>reg ]
|
||||||
M: vreg-insn assign-registers-in-insn
|
M: vreg-insn assign-registers-in-insn
|
||||||
[ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ;
|
[ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ;
|
||||||
|
|
||||||
|
: assign-gc-roots ( gc-map -- )
|
||||||
|
[ [ vreg>spill-slot ] map ] change-gc-roots drop ;
|
||||||
|
|
||||||
|
: assign-derived-roots ( gc-map -- )
|
||||||
|
[ [ [ vreg>spill-slot ] bi@ ] assoc-map ] change-derived-roots drop ;
|
||||||
|
|
||||||
M: gc-map-insn assign-registers-in-insn
|
M: gc-map-insn assign-registers-in-insn
|
||||||
[ [ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ]
|
[ [ assign-insn-defs ] [ assign-insn-uses ] [ assign-insn-temps ] tri ]
|
||||||
[ gc-map>> [ [ vreg>spill-slot ] map ] change-gc-roots drop ]
|
[ gc-map>> [ assign-gc-roots ] [ assign-derived-roots ] bi ]
|
||||||
bi ;
|
bi ;
|
||||||
|
|
||||||
M: insn assign-registers-in-insn drop ;
|
M: insn assign-registers-in-insn drop ;
|
||||||
|
|
|
@ -206,3 +206,42 @@ V{
|
||||||
[ H{ { 0 0 } } ] [ 2 get 4 get edge-live-in ] unit-test
|
[ H{ { 0 0 } } ] [ 2 get 4 get edge-live-in ] unit-test
|
||||||
|
|
||||||
[ H{ { 1 1 } } ] [ 3 get 4 get edge-live-in ] unit-test
|
[ H{ { 1 1 } } ] [ 3 get 4 get edge-live-in ] unit-test
|
||||||
|
|
||||||
|
|
||||||
|
V{
|
||||||
|
T{ ##prologue }
|
||||||
|
T{ ##branch }
|
||||||
|
} 0 test-bb
|
||||||
|
|
||||||
|
V{
|
||||||
|
T{ ##peek f 0 D 0 }
|
||||||
|
T{ ##tagged>integer f 1 0 }
|
||||||
|
T{ ##call-gc f T{ gc-map } }
|
||||||
|
T{ ##replace f 0 D 0 }
|
||||||
|
T{ ##call-gc f T{ gc-map } }
|
||||||
|
T{ ##replace f 1 D 0 }
|
||||||
|
T{ ##branch }
|
||||||
|
} 1 test-bb
|
||||||
|
|
||||||
|
V{
|
||||||
|
T{ ##epilogue }
|
||||||
|
T{ ##return }
|
||||||
|
} 2 test-bb
|
||||||
|
|
||||||
|
0 1 edge
|
||||||
|
1 2 edge
|
||||||
|
|
||||||
|
H{
|
||||||
|
{ 0 tagged-rep }
|
||||||
|
{ 1 int-rep }
|
||||||
|
} representations set
|
||||||
|
|
||||||
|
[ ] [ cfg new 0 get >>entry dup cfg set compute-live-sets ] unit-test
|
||||||
|
|
||||||
|
[ V{ { 1 0 } } ] [ 1 get instructions>> 2 swap nth gc-map>> derived-roots>> ] unit-test
|
||||||
|
|
||||||
|
[ { 0 } ] [ 1 get instructions>> 2 swap nth gc-map>> gc-roots>> ] unit-test
|
||||||
|
|
||||||
|
[ V{ { 1 0 } } ] [ 1 get instructions>> 4 swap nth gc-map>> derived-roots>> ] unit-test
|
||||||
|
|
||||||
|
[ { 0 } ] [ 1 get instructions>> 4 swap nth gc-map>> gc-roots>> ] unit-test
|
|
@ -1,15 +1,28 @@
|
||||||
! Copyright (C) 2009, 2010 Slava Pestov.
|
! Copyright (C) 2009, 2010 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: kernel accessors assocs fry deques dlists namespaces
|
USING: arrays kernel accessors assocs fry locals combinators
|
||||||
sequences sets compiler.cfg compiler.cfg.def-use
|
deques dlists namespaces sequences sets compiler.cfg
|
||||||
compiler.cfg.instructions compiler.cfg.registers
|
compiler.cfg.def-use compiler.cfg.instructions
|
||||||
compiler.cfg.utilities compiler.cfg.predecessors
|
compiler.cfg.registers compiler.cfg.utilities
|
||||||
compiler.cfg.rpo cpu.architecture ;
|
compiler.cfg.predecessors compiler.cfg.rpo cpu.architecture ;
|
||||||
FROM: namespaces => set ;
|
FROM: namespaces => set ;
|
||||||
IN: compiler.cfg.liveness
|
IN: compiler.cfg.liveness
|
||||||
|
|
||||||
! See http://en.wikipedia.org/wiki/Liveness_analysis
|
! Similar to http://en.wikipedia.org/wiki/Liveness_analysis,
|
||||||
|
! with three additions:
|
||||||
|
|
||||||
|
! 1) With SSA, it is not sufficient to have a single live-in set
|
||||||
|
! per block. There is also there is an edge-live-in set per
|
||||||
|
! edge, consisting of phi inputs from each predecessor.
|
||||||
|
! 2) Liveness analysis annotates call sites with GC maps
|
||||||
|
! indicating the spill slots in the stack frame that contain
|
||||||
|
! tagged pointers, and thus have to be visited if a GC occurs
|
||||||
|
! inside the call.
|
||||||
|
! 3) GC maps can contain derived pointers. A derived pointer is
|
||||||
|
! a pointer into the middle of a data heap object. Each derived
|
||||||
|
! pointer has a base pointer, to keep it up to date when objects
|
||||||
|
! are moved by the garbage collector. This extends live
|
||||||
|
! intervals and inserts new ##phi instructions.
|
||||||
SYMBOL: live-ins
|
SYMBOL: live-ins
|
||||||
|
|
||||||
: live-in ( bb -- set )
|
: live-in ( bb -- set )
|
||||||
|
@ -27,6 +40,8 @@ SYMBOL: edge-live-ins
|
||||||
: edge-live-in ( predecessor basic-block -- set )
|
: edge-live-in ( predecessor basic-block -- set )
|
||||||
edge-live-ins get at at ;
|
edge-live-ins get at at ;
|
||||||
|
|
||||||
|
SYMBOL: base-pointers
|
||||||
|
|
||||||
GENERIC: visit-insn ( live-set insn -- live-set )
|
GENERIC: visit-insn ( live-set insn -- live-set )
|
||||||
|
|
||||||
: kill-defs ( live-set insn -- live-set )
|
: kill-defs ( live-set insn -- live-set )
|
||||||
|
@ -35,20 +50,64 @@ GENERIC: visit-insn ( live-set insn -- live-set )
|
||||||
: gen-uses ( live-set insn -- live-set )
|
: gen-uses ( live-set insn -- live-set )
|
||||||
uses-vregs [ over conjoin ] each ; inline
|
uses-vregs [ over conjoin ] each ; inline
|
||||||
|
|
||||||
M: vreg-insn visit-insn [ kill-defs ] [ gen-uses ] bi ;
|
M: vreg-insn visit-insn
|
||||||
|
[ kill-defs ] [ gen-uses ] bi ;
|
||||||
|
|
||||||
! Our liveness analysis annotates call sites with GC maps
|
DEFER: lookup-base-pointer
|
||||||
! indicating the spill slots in the stack frame that contain
|
|
||||||
! tagged pointers, and thus have to be visited if a GC occurs
|
GENERIC: lookup-base-pointer* ( insn -- vreg/f )
|
||||||
! inside the call.
|
|
||||||
|
M: ##tagged>integer lookup-base-pointer* src>> ;
|
||||||
|
|
||||||
|
M: ##unbox-any-c-ptr lookup-base-pointer*
|
||||||
|
! If the input to unbox-any-c-ptr was an alien and not a
|
||||||
|
! byte array, then the derived pointer will be outside of
|
||||||
|
! the data heap. The GC has to handle this case and ignore
|
||||||
|
! it.
|
||||||
|
src>> ;
|
||||||
|
|
||||||
|
M: ##copy lookup-base-pointer* src>> lookup-base-pointer ;
|
||||||
|
|
||||||
|
M: ##add-imm lookup-base-pointer* src1>> lookup-base-pointer ;
|
||||||
|
|
||||||
|
M: ##sub-imm lookup-base-pointer* src1>> lookup-base-pointer ;
|
||||||
|
|
||||||
|
M: ##add lookup-base-pointer*
|
||||||
|
! If both operands have a base pointer, then the user better
|
||||||
|
! not be doing memory reads and writes on the object, since
|
||||||
|
! we don't give it a base pointer in that case at all.
|
||||||
|
[ src1>> ] [ src2>> ] bi [ lookup-base-pointer ] bi@ xor ;
|
||||||
|
|
||||||
|
M: ##sub lookup-base-pointer*
|
||||||
|
src1>> lookup-base-pointer ;
|
||||||
|
|
||||||
|
M: vreg-insn lookup-base-pointer* drop f ;
|
||||||
|
|
||||||
|
: lookup-base-pointer ( vreg -- vreg/f )
|
||||||
|
base-pointers get [ insn-of lookup-base-pointer* ] cache ;
|
||||||
|
|
||||||
|
:: visit-derived-root ( vreg derived-roots gc-roots -- )
|
||||||
|
vreg lookup-base-pointer :> base
|
||||||
|
base [
|
||||||
|
{ vreg base } derived-roots push
|
||||||
|
base gc-roots adjoin
|
||||||
|
] when ;
|
||||||
|
|
||||||
|
: visit-gc-root ( vreg derived-roots gc-roots -- )
|
||||||
|
pick rep-of {
|
||||||
|
{ tagged-rep [ nip adjoin ] }
|
||||||
|
{ int-rep [ visit-derived-root ] }
|
||||||
|
[ 2drop 2drop ]
|
||||||
|
} case ;
|
||||||
|
|
||||||
|
: gc-roots ( live-set -- derived-roots gc-roots )
|
||||||
|
V{ } clone HS{ } clone
|
||||||
|
[ '[ drop _ _ visit-gc-root ] assoc-each ] 2keep
|
||||||
|
members ;
|
||||||
|
|
||||||
: fill-gc-map ( live-set insn -- live-set )
|
: fill-gc-map ( live-set insn -- live-set )
|
||||||
representations get [
|
[ representations get [ dup gc-roots ] [ f f ] if ] dip
|
||||||
gc-map>> over keys
|
gc-map>> [ gc-roots<< ] [ derived-roots<< ] bi ;
|
||||||
[ rep-of tagged-rep? ] filter
|
|
||||||
>>gc-roots
|
|
||||||
] when
|
|
||||||
drop ;
|
|
||||||
|
|
||||||
M: gc-map-insn visit-insn
|
M: gc-map-insn visit-insn
|
||||||
[ kill-defs ] [ fill-gc-map ] [ gen-uses ] tri ;
|
[ kill-defs ] [ fill-gc-map ] [ gen-uses ] tri ;
|
||||||
|
@ -60,9 +119,6 @@ M: insn visit-insn drop ;
|
||||||
: transfer-liveness ( live-set instructions -- live-set' )
|
: transfer-liveness ( live-set instructions -- live-set' )
|
||||||
[ clone ] [ <reversed> ] bi* [ visit-insn ] each ;
|
[ clone ] [ <reversed> ] bi* [ visit-insn ] each ;
|
||||||
|
|
||||||
: local-live-in ( instructions -- live-set )
|
|
||||||
[ H{ } ] dip transfer-liveness keys ;
|
|
||||||
|
|
||||||
SYMBOL: work-list
|
SYMBOL: work-list
|
||||||
|
|
||||||
: add-to-work-list ( basic-blocks -- )
|
: add-to-work-list ( basic-blocks -- )
|
||||||
|
@ -98,11 +154,13 @@ SYMBOL: work-list
|
||||||
|
|
||||||
: compute-live-sets ( cfg -- )
|
: compute-live-sets ( cfg -- )
|
||||||
needs-predecessors
|
needs-predecessors
|
||||||
|
dup compute-insns
|
||||||
|
|
||||||
<hashed-dlist> work-list set
|
<hashed-dlist> work-list set
|
||||||
H{ } clone live-ins set
|
H{ } clone live-ins set
|
||||||
H{ } clone edge-live-ins set
|
H{ } clone edge-live-ins set
|
||||||
H{ } clone live-outs set
|
H{ } clone live-outs set
|
||||||
|
H{ } clone base-pointers set
|
||||||
post-order add-to-work-list
|
post-order add-to-work-list
|
||||||
work-list get [ liveness-step ] slurp-deque ;
|
work-list get [ liveness-step ] slurp-deque ;
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,14 @@ STRUCT: gc-info
|
||||||
{ scrub-d-count uint }
|
{ scrub-d-count uint }
|
||||||
{ scrub-r-count uint }
|
{ scrub-r-count uint }
|
||||||
{ gc-root-count uint }
|
{ gc-root-count uint }
|
||||||
|
{ derived-root-count uint }
|
||||||
{ return-address-count uint } ;
|
{ return-address-count uint } ;
|
||||||
|
|
||||||
SINGLETON: fake-cpu
|
SINGLETON: fake-cpu
|
||||||
|
|
||||||
fake-cpu \ cpu set
|
fake-cpu \ cpu set
|
||||||
|
|
||||||
M: fake-cpu gc-root-offsets ;
|
M: fake-cpu gc-root-offset ;
|
||||||
|
|
||||||
[ ] [
|
[ ] [
|
||||||
[
|
[
|
||||||
|
@ -27,7 +28,7 @@ M: fake-cpu gc-root-offsets ;
|
||||||
|
|
||||||
50 <byte-array> %
|
50 <byte-array> %
|
||||||
|
|
||||||
T{ gc-map f B{ 0 1 1 1 0 } B{ 1 0 } V{ 1 3 } } gc-map-here
|
T{ gc-map f B{ 0 1 1 1 0 } B{ 1 0 } V{ 1 3 } V{ { 2 4 } } } gc-map-here
|
||||||
|
|
||||||
emit-gc-info
|
emit-gc-info
|
||||||
] B{ } make
|
] B{ } make
|
||||||
|
@ -54,7 +55,10 @@ M: fake-cpu gc-root-offsets ;
|
||||||
f t f t
|
f t f t
|
||||||
} underlying>> %
|
} underlying>> %
|
||||||
|
|
||||||
! Return addresses - 4 bytes
|
! Derived pointers
|
||||||
|
uint-array{ -1 -1 4 } underlying>> %
|
||||||
|
|
||||||
|
! Return addresses
|
||||||
uint-array{ 100 } underlying>> %
|
uint-array{ 100 } underlying>> %
|
||||||
|
|
||||||
! GC info footer - 16 bytes
|
! GC info footer - 16 bytes
|
||||||
|
@ -62,6 +66,7 @@ M: fake-cpu gc-root-offsets ;
|
||||||
{ scrub-d-count 5 }
|
{ scrub-d-count 5 }
|
||||||
{ scrub-r-count 2 }
|
{ scrub-r-count 2 }
|
||||||
{ gc-root-count 4 }
|
{ gc-root-count 4 }
|
||||||
|
{ derived-root-count 3 }
|
||||||
{ return-address-count 1 }
|
{ return-address-count 1 }
|
||||||
} (underlying)>> %
|
} (underlying)>> %
|
||||||
] B{ } make
|
] B{ } make
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: arrays bit-arrays byte-arrays byte-vectors generic assocs
|
USING: arrays bit-arrays byte-arrays byte-vectors generic assocs
|
||||||
hashtables io.binary kernel kernel.private math namespaces make
|
hashtables io.binary kernel kernel.private math namespaces make
|
||||||
sequences words quotations strings alien.accessors alien.strings
|
sequences words quotations strings sorting alien.accessors
|
||||||
layouts system combinators math.bitwise math.order
|
alien.strings layouts system combinators math.bitwise math.order
|
||||||
combinators.short-circuit combinators.smart accessors growable
|
combinators.short-circuit combinators.smart accessors growable
|
||||||
fry memoize compiler.constants compiler.cfg.instructions
|
fry memoize compiler.constants compiler.cfg.instructions
|
||||||
cpu.architecture ;
|
cpu.architecture ;
|
||||||
|
@ -144,12 +144,14 @@ MEMO: cached-string>symbol ( symbol -- obj ) string>symbol ;
|
||||||
! - <scrubbed data stack locations>
|
! - <scrubbed data stack locations>
|
||||||
! - <scrubbed retain stack locations>
|
! - <scrubbed retain stack locations>
|
||||||
! - <GC root spill slots>
|
! - <GC root spill slots>
|
||||||
|
! uint[] <base pointers>
|
||||||
! uint[] <return addresses>
|
! uint[] <return addresses>
|
||||||
! uint <largest scrubbed data stack location>
|
! uint <largest scrubbed data stack location>
|
||||||
! uint <largest scrubbed retain stack location>
|
! uint <largest scrubbed retain stack location>
|
||||||
! uint <largest GC root spill slot>
|
! uint <largest GC root spill slot>
|
||||||
! uint <number of return addresses>
|
! uint <largest derived root spill slot>
|
||||||
|
! int <number of return addresses>
|
||||||
|
!
|
||||||
SYMBOLS: return-addresses gc-maps ;
|
SYMBOLS: return-addresses gc-maps ;
|
||||||
|
|
||||||
: gc-map-needed? ( gc-map -- ? )
|
: gc-map-needed? ( gc-map -- ? )
|
||||||
|
@ -160,6 +162,7 @@ SYMBOLS: return-addresses gc-maps ;
|
||||||
[ scrub-d>> empty? ]
|
[ scrub-d>> empty? ]
|
||||||
[ scrub-r>> empty? ]
|
[ scrub-r>> empty? ]
|
||||||
[ gc-roots>> empty? ]
|
[ gc-roots>> empty? ]
|
||||||
|
[ derived-roots>> empty? ]
|
||||||
} 1&& not
|
} 1&& not
|
||||||
] when ;
|
] when ;
|
||||||
|
|
||||||
|
@ -169,33 +172,64 @@ SYMBOLS: return-addresses gc-maps ;
|
||||||
compiled-offset return-addresses get push
|
compiled-offset return-addresses get push
|
||||||
] [ drop ] if ;
|
] [ drop ] if ;
|
||||||
|
|
||||||
|
: longest ( seqs -- n )
|
||||||
|
[ length ] [ max ] map-reduce ;
|
||||||
|
|
||||||
: emit-scrub ( seqs -- n )
|
: emit-scrub ( seqs -- n )
|
||||||
! seqs is a sequence of sequences of 0/1
|
! seqs is a sequence of sequences of 0/1
|
||||||
dup [ length ] [ max ] map-reduce
|
dup longest
|
||||||
[ '[ [ 0 = ] ?{ } map-as _ f pad-tail % ] each ] keep ;
|
[ '[ [ 0 = ] ?{ } map-as _ f pad-tail % ] each ] keep ;
|
||||||
|
|
||||||
: integers>bits ( seq n -- bit-array )
|
: integers>bits ( seq n -- bit-array )
|
||||||
<bit-array> [ '[ [ t ] dip _ set-nth ] each ] keep ;
|
<bit-array> [ '[ [ t ] dip _ set-nth ] each ] keep ;
|
||||||
|
|
||||||
|
: largest-spill-slot ( seqs -- n )
|
||||||
|
[ [ 0 ] [ supremum 1 + ] if-empty ] [ max ] map-reduce ;
|
||||||
|
|
||||||
: emit-gc-roots ( seqs -- n )
|
: emit-gc-roots ( seqs -- n )
|
||||||
! seqs is a sequence of sequences of integers 0..n-1
|
! seqs is a sequence of sequences of integers 0..n-1
|
||||||
dup [ [ 0 ] [ supremum 1 + ] if-empty ] [ max ] map-reduce
|
dup largest-spill-slot
|
||||||
[ '[ _ integers>bits % ] each ] keep ;
|
[ '[ _ integers>bits % ] each ] keep ;
|
||||||
|
|
||||||
: emit-uint ( n -- )
|
: emit-uint ( n -- )
|
||||||
building get push-uint ;
|
building get push-uint ;
|
||||||
|
|
||||||
|
: emit-uints ( n -- )
|
||||||
|
[ emit-uint ] each ;
|
||||||
|
|
||||||
|
: gc-root-offsets ( gc-map -- offsets )
|
||||||
|
gc-roots>> [ gc-root-offset ] map ;
|
||||||
|
|
||||||
|
: emit-gc-info-bitmaps ( -- scrub-d-count scrub-r-count gc-root-count )
|
||||||
|
[
|
||||||
|
gc-maps get {
|
||||||
|
[ [ scrub-d>> ] map emit-scrub ]
|
||||||
|
[ [ scrub-r>> ] map emit-scrub ]
|
||||||
|
[ [ gc-root-offsets ] map emit-gc-roots ]
|
||||||
|
} cleave
|
||||||
|
] ?{ } make underlying>> % ;
|
||||||
|
|
||||||
|
: emit-base-table ( alist longest -- )
|
||||||
|
-1 <array> <enum> swap assoc-union! seq>> emit-uints ;
|
||||||
|
|
||||||
|
: derived-root-offsets ( gc-map -- offsets )
|
||||||
|
derived-roots>> [ [ gc-root-offset ] bi@ ] assoc-map ;
|
||||||
|
|
||||||
|
: emit-base-tables ( -- count )
|
||||||
|
gc-maps get [ derived-root-offsets ] map
|
||||||
|
dup [ keys ] map largest-spill-slot
|
||||||
|
[ '[ _ emit-base-table ] each ] keep ;
|
||||||
|
|
||||||
|
: emit-return-addresses ( -- )
|
||||||
|
return-addresses get emit-uints ;
|
||||||
|
|
||||||
: gc-info ( -- byte-array )
|
: gc-info ( -- byte-array )
|
||||||
[
|
[
|
||||||
return-addresses get empty? [ 0 emit-uint ] [
|
return-addresses get empty? [ 0 emit-uint ] [
|
||||||
gc-maps get
|
emit-gc-info-bitmaps
|
||||||
[
|
emit-base-tables
|
||||||
[ [ scrub-d>> ] map emit-scrub ]
|
emit-return-addresses
|
||||||
[ [ scrub-r>> ] map emit-scrub ]
|
4array emit-uints
|
||||||
[ [ gc-roots>> gc-root-offsets ] map emit-gc-roots ] tri
|
|
||||||
] ?{ } make underlying>> %
|
|
||||||
return-addresses get [ emit-uint ] each
|
|
||||||
[ emit-uint ] tri@
|
|
||||||
return-addresses get length emit-uint
|
return-addresses get length emit-uint
|
||||||
] if
|
] if
|
||||||
] B{ } make ;
|
] B{ } make ;
|
||||||
|
|
|
@ -823,25 +823,3 @@ TUPLE: some-tuple x ;
|
||||||
aa-indirect-1 >>x
|
aa-indirect-1 >>x
|
||||||
] compile-call
|
] compile-call
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
! Write barrier elimination was being done before scheduling and
|
|
||||||
! GC check insertion, and didn't take subroutine calls into
|
|
||||||
! account. Oops...
|
|
||||||
: write-barrier-elim-in-wrong-place ( -- obj )
|
|
||||||
! A callback used below
|
|
||||||
void { } cdecl [ compact-gc ] alien-callback
|
|
||||||
! Allocate an object A in the nursery
|
|
||||||
1 f <array>
|
|
||||||
! Subroutine call promotes the object to tenured
|
|
||||||
swap void { } cdecl alien-indirect
|
|
||||||
! Allocate another object B in the nursery, store it into
|
|
||||||
! the first
|
|
||||||
1 f <array> over set-first
|
|
||||||
! Now object A's card should be marked and minor GC should
|
|
||||||
! promote B to aging
|
|
||||||
minor-gc
|
|
||||||
! Do stuff
|
|
||||||
[ 100 [ ] times ] infer.
|
|
||||||
;
|
|
||||||
|
|
||||||
[ { { f } } ] [ write-barrier-elim-in-wrong-place ] unit-test
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ sequences tools.test namespaces.private slots.private
|
||||||
sequences.private byte-arrays alien alien.accessors layouts
|
sequences.private byte-arrays alien alien.accessors layouts
|
||||||
words definitions compiler.units io combinators vectors grouping
|
words definitions compiler.units io combinators vectors grouping
|
||||||
make alien.c-types combinators.short-circuit math.order
|
make alien.c-types combinators.short-circuit math.order
|
||||||
math.libm math.parser math.functions alien.syntax ;
|
math.libm math.parser math.functions alien.syntax memory
|
||||||
|
stack-checker ;
|
||||||
FROM: math => float ;
|
FROM: math => float ;
|
||||||
QUALIFIED: namespaces.private
|
QUALIFIED: namespaces.private
|
||||||
IN: compiler.tests.codegen
|
IN: compiler.tests.codegen
|
||||||
|
@ -463,6 +464,13 @@ TUPLE: myseq { underlying1 byte-array read-only } { underlying2 byte-array read-
|
||||||
[ [ HEX: f bitand ] bi@ [ shift ] [ drop -3 shift ] 2bi ] compile-call
|
[ [ HEX: f bitand ] bi@ [ shift ] [ drop -3 shift ] 2bi ] compile-call
|
||||||
] unit-test
|
] unit-test
|
||||||
|
|
||||||
|
! Alias analysis bug
|
||||||
|
[ t ] [
|
||||||
|
[
|
||||||
|
10 10 <byte-array> [ <displaced-alien> underlying>> ] keep eq?
|
||||||
|
] compile-call
|
||||||
|
] unit-test
|
||||||
|
|
||||||
! GC root offsets were computed wrong on x86
|
! GC root offsets were computed wrong on x86
|
||||||
: gc-root-messup ( a -- b )
|
: gc-root-messup ( a -- b )
|
||||||
dup [
|
dup [
|
||||||
|
@ -473,9 +481,45 @@ TUPLE: myseq { underlying1 byte-array read-only } { underlying2 byte-array read-
|
||||||
|
|
||||||
[ ] [ 2000 [ "hello" clone dup gc-root-messup first eq? t assert= ] times ] unit-test
|
[ ] [ 2000 [ "hello" clone dup gc-root-messup first eq? t assert= ] times ] unit-test
|
||||||
|
|
||||||
! Alias analysis bug
|
! Write barrier elimination was being done before scheduling and
|
||||||
[ t ] [
|
! GC check insertion, and didn't take subroutine calls into
|
||||||
[
|
! account. Oops...
|
||||||
10 10 <byte-array> [ <displaced-alien> underlying>> ] keep eq?
|
: write-barrier-elim-in-wrong-place ( -- obj )
|
||||||
] compile-call
|
! A callback used below
|
||||||
] unit-test
|
void { } cdecl [ compact-gc ] alien-callback
|
||||||
|
! Allocate an object A in the nursery
|
||||||
|
1 f <array>
|
||||||
|
! Subroutine call promotes the object to tenured
|
||||||
|
swap void { } cdecl alien-indirect
|
||||||
|
! Allocate another object B in the nursery, store it into
|
||||||
|
! the first
|
||||||
|
1 f <array> over set-first
|
||||||
|
! Now object A's card should be marked and minor GC should
|
||||||
|
! promote B to aging
|
||||||
|
minor-gc
|
||||||
|
! Do stuff
|
||||||
|
[ 100 [ ] times ] infer.
|
||||||
|
;
|
||||||
|
|
||||||
|
[ { { f } } ] [ write-barrier-elim-in-wrong-place ] unit-test
|
||||||
|
|
||||||
|
! GC maps must support derived pointers
|
||||||
|
: (derived-pointer-test-1) ( -- byte-array )
|
||||||
|
2 <byte-array> ;
|
||||||
|
|
||||||
|
: derived-pointer-test-1 ( -- byte-array )
|
||||||
|
! A callback used below
|
||||||
|
void { } cdecl [ compact-gc ] alien-callback
|
||||||
|
! Put the construction in a word since instruction selection
|
||||||
|
! eliminates the untagged pointer entirely if the value is a
|
||||||
|
! byte array
|
||||||
|
(derived-pointer-test-1) { c-ptr } declare
|
||||||
|
! Store into an array, an untagged pointer to the payload
|
||||||
|
! is now an available expression
|
||||||
|
123 over 0 set-alien-unsigned-1
|
||||||
|
! GC, moving the array and derived pointer
|
||||||
|
swap void { } cdecl alien-indirect
|
||||||
|
! Store into the array again
|
||||||
|
231 over 1 set-alien-unsigned-1 ;
|
||||||
|
|
||||||
|
[ B{ 123 231 } ] [ derived-pointer-test-1 ] unit-test
|
||||||
|
|
|
@ -225,7 +225,7 @@ M: object vm-stack-space 0 ;
|
||||||
! %store-memory work
|
! %store-memory work
|
||||||
HOOK: complex-addressing? cpu ( -- ? )
|
HOOK: complex-addressing? cpu ( -- ? )
|
||||||
|
|
||||||
HOOK: gc-root-offsets cpu ( seq -- seq' )
|
HOOK: gc-root-offset cpu ( spill-slot -- n )
|
||||||
|
|
||||||
HOOK: %load-immediate cpu ( reg val -- )
|
HOOK: %load-immediate cpu ( reg val -- )
|
||||||
HOOK: %load-reference cpu ( reg obj -- )
|
HOOK: %load-reference cpu ( reg obj -- )
|
||||||
|
|
|
@ -503,8 +503,8 @@ M:: x86 %check-nursery-branch ( label size cc temp1 temp2 -- )
|
||||||
{ cc/<= [ label JG ] }
|
{ cc/<= [ label JG ] }
|
||||||
} case ;
|
} case ;
|
||||||
|
|
||||||
M: x86 gc-root-offsets
|
M: x86 gc-root-offset
|
||||||
[ n>> spill-offset special-offset cell + cell /i ] map f like ;
|
n>> spill-offset special-offset cell + cell /i ;
|
||||||
|
|
||||||
M: x86 %call-gc ( gc-map -- )
|
M: x86 %call-gc ( gc-map -- )
|
||||||
\ minor-gc %call
|
\ minor-gc %call
|
||||||
|
|
|
@ -60,9 +60,9 @@ void context::scrub_stacks(gc_info *info, cell index)
|
||||||
u8 *bitmap = info->gc_info_bitmap();
|
u8 *bitmap = info->gc_info_bitmap();
|
||||||
|
|
||||||
{
|
{
|
||||||
cell base = info->scrub_d_base(index);
|
cell base = info->callsite_scrub_d(index);
|
||||||
|
|
||||||
for(int loc = 0; loc < info->scrub_d_count; loc++)
|
for(cell loc = 0; loc < info->scrub_d_count; loc++)
|
||||||
{
|
{
|
||||||
if(bitmap_p(bitmap,base + loc))
|
if(bitmap_p(bitmap,base + loc))
|
||||||
{
|
{
|
||||||
|
@ -75,7 +75,7 @@ void context::scrub_stacks(gc_info *info, cell index)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
cell base = info->scrub_r_base(index);
|
cell base = info->callsite_scrub_r(index);
|
||||||
|
|
||||||
for(int loc = 0; loc < info->scrub_r_count; loc++)
|
for(int loc = 0; loc < info->scrub_r_count; loc++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
namespace factor
|
namespace factor
|
||||||
{
|
{
|
||||||
|
|
||||||
int gc_info::return_address_index(cell return_address)
|
cell gc_info::return_address_index(cell return_address)
|
||||||
{
|
{
|
||||||
u32 *return_address_array = return_addresses();
|
u32 *return_address_array = return_addresses();
|
||||||
|
|
||||||
for(int i = 0; i < return_address_count; i++)
|
for(cell i = 0; i < return_address_count; i++)
|
||||||
{
|
{
|
||||||
if(return_address == return_address_array[i])
|
if(return_address == return_address_array[i])
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return gc_info_missing_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
namespace factor
|
namespace factor
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const u32 gc_info_missing_value = (u32)-1;
|
||||||
|
|
||||||
struct gc_info {
|
struct gc_info {
|
||||||
int scrub_d_count;
|
u32 scrub_d_count;
|
||||||
int scrub_r_count;
|
u32 scrub_r_count;
|
||||||
int gc_root_count;
|
u32 gc_root_count;
|
||||||
int return_address_count;
|
u32 derived_root_count;
|
||||||
|
u32 return_address_count;
|
||||||
|
|
||||||
|
cell callsite_bitmap_size()
|
||||||
|
{
|
||||||
|
return scrub_d_count + scrub_r_count + gc_root_count;
|
||||||
|
}
|
||||||
|
|
||||||
cell total_bitmap_size()
|
cell total_bitmap_size()
|
||||||
{
|
{
|
||||||
return return_address_count * (scrub_d_count + scrub_r_count + gc_root_count);
|
return return_address_count * callsite_bitmap_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
cell total_bitmap_bytes()
|
cell total_bitmap_bytes()
|
||||||
|
@ -19,33 +27,43 @@ struct gc_info {
|
||||||
|
|
||||||
u32 *return_addresses()
|
u32 *return_addresses()
|
||||||
{
|
{
|
||||||
return (u32 *)((u8 *)this - return_address_count * sizeof(u32));
|
return (u32 *)this - return_address_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *base_pointer_map()
|
||||||
|
{
|
||||||
|
return return_addresses() - return_address_count * derived_root_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *gc_info_bitmap()
|
u8 *gc_info_bitmap()
|
||||||
{
|
{
|
||||||
return (u8 *)return_addresses() - total_bitmap_bytes();
|
return (u8 *)base_pointer_map() - total_bitmap_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
cell scrub_d_base(cell index)
|
cell callsite_scrub_d(cell index)
|
||||||
{
|
{
|
||||||
return index * scrub_d_count;
|
return index * scrub_d_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell scrub_r_base(cell index)
|
cell callsite_scrub_r(cell index)
|
||||||
{
|
{
|
||||||
return return_address_count * scrub_d_count +
|
return return_address_count * scrub_d_count +
|
||||||
index * scrub_r_count;
|
index * scrub_r_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell spill_slot_base(cell index)
|
cell callsite_gc_roots(cell index)
|
||||||
{
|
{
|
||||||
return return_address_count * scrub_d_count
|
return return_address_count * scrub_d_count
|
||||||
+ return_address_count * scrub_r_count
|
+ return_address_count * scrub_r_count
|
||||||
+ index * gc_root_count;
|
+ index * gc_root_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int return_address_index(cell return_address);
|
cell lookup_base_pointer(cell index, cell derived_root)
|
||||||
|
{
|
||||||
|
return base_pointer_map()[index * derived_root_count + derived_root];
|
||||||
|
}
|
||||||
|
|
||||||
|
cell return_address_index(cell return_address);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,27 +292,52 @@ struct call_frame_slot_visitor {
|
||||||
gc_info *info = compiled->block_gc_info();
|
gc_info *info = compiled->block_gc_info();
|
||||||
|
|
||||||
assert(return_address < compiled->size());
|
assert(return_address < compiled->size());
|
||||||
int index = info->return_address_index(return_address);
|
u32 callsite = info->return_address_index(return_address);
|
||||||
if(index == -1)
|
if(callsite == gc_info_missing_value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef DEBUG_GC_MAPS
|
#ifdef DEBUG_GC_MAPS
|
||||||
std::cout << "call frame code block " << compiled << " with offset " << return_address << std::endl;
|
std::cout << "call frame code block " << compiled << " with offset " << return_address << std::endl;
|
||||||
#endif
|
#endif
|
||||||
u8 *bitmap = info->gc_info_bitmap();
|
|
||||||
cell base = info->spill_slot_base(index);
|
|
||||||
cell *stack_pointer = (cell *)(parent->frame_successor(frame) + 1);
|
cell *stack_pointer = (cell *)(parent->frame_successor(frame) + 1);
|
||||||
|
u8 *bitmap = info->gc_info_bitmap();
|
||||||
|
|
||||||
for(int spill_slot = 0; spill_slot < info->gc_root_count; spill_slot++)
|
/* Subtract old value of base pointer from every derived pointer. */
|
||||||
|
for(cell spill_slot = 0; spill_slot < info->derived_root_count; spill_slot++)
|
||||||
{
|
{
|
||||||
if(bitmap_p(bitmap,base + spill_slot))
|
cell base_pointer = info->lookup_base_pointer(callsite, spill_slot);
|
||||||
|
if(base_pointer != gc_info_missing_value)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_GC_MAPS
|
#ifdef DEBUG_GC_MAPS
|
||||||
std::cout << "visiting spill slot " << spill_slot << std::endl;
|
std::cout << "visiting derived root " << spill_slot
|
||||||
|
<< " with base pointer " << base_pointer
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
stack_pointer[spill_slot] -= stack_pointer[base_pointer];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update all GC roots, including base pointers. */
|
||||||
|
cell callsite_gc_roots = info->callsite_gc_roots(callsite);
|
||||||
|
|
||||||
|
for(cell spill_slot = 0; spill_slot < info->gc_root_count; spill_slot++)
|
||||||
|
{
|
||||||
|
if(bitmap_p(bitmap,callsite_gc_roots + spill_slot))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_GC_MAPS
|
||||||
|
std::cout << "visiting GC root " << spill_slot << std::endl;
|
||||||
#endif
|
#endif
|
||||||
visitor->visit_handle(stack_pointer + spill_slot);
|
visitor->visit_handle(stack_pointer + spill_slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add the base pointers to obtain new derived pointer values. */
|
||||||
|
for(cell spill_slot = 0; spill_slot < info->derived_root_count; spill_slot++)
|
||||||
|
{
|
||||||
|
cell base_pointer = info->lookup_base_pointer(callsite, spill_slot);
|
||||||
|
if(base_pointer != gc_info_missing_value)
|
||||||
|
stack_pointer[spill_slot] += stack_pointer[base_pointer];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
10
vm/vm.hpp
10
vm/vm.hpp
|
@ -329,14 +329,16 @@ struct factor_vm
|
||||||
return (Type *)allot_object(Type::type_number,size);
|
return (Type *)allot_object(Type::type_number,size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool in_data_heap_p(cell pointer)
|
||||||
|
{
|
||||||
|
return (pointer >= data->seg->start && pointer < data->seg->end);
|
||||||
|
}
|
||||||
|
|
||||||
inline void check_data_pointer(object *pointer)
|
inline void check_data_pointer(object *pointer)
|
||||||
{
|
{
|
||||||
#ifdef FACTOR_DEBUG
|
#ifdef FACTOR_DEBUG
|
||||||
if(!(current_gc && current_gc->op == collect_growing_heap_op))
|
if(!(current_gc && current_gc->op == collect_growing_heap_op))
|
||||||
{
|
assert(in_data_heap_p((cell)pointer));
|
||||||
assert((cell)pointer >= data->seg->start
|
|
||||||
&& (cell)pointer < data->seg->end);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue