]> gitweb.factorcode.org Git - factor.git/commitdiff
compiler.cfg.*: new step in finalization: clear-uninitialized
authorBjörn Lindqvist <bjourne@gmail.com>
Thu, 1 Jan 2015 10:12:58 +0000 (11:12 +0100)
committerDoug Coleman <doug.coleman@gmail.com>
Thu, 22 Jan 2015 20:17:20 +0000 (12:17 -0800)
this step is supposed to analyze the cfg, find where there are peeks
that can cause stack underflow, and insert replace-imm instructions so
that there are no uninitialized stack locations if they do. could fix #1187

basis/compiler/cfg/finalization/finalization.factor
basis/compiler/cfg/stacks/clearing/clearing-tests.factor [new file with mode: 0644]
basis/compiler/cfg/stacks/clearing/clearing.factor [new file with mode: 0644]
basis/compiler/cfg/stacks/vacant/vacant-docs.factor
basis/compiler/cfg/stacks/vacant/vacant-tests.factor
basis/compiler/cfg/stacks/vacant/vacant.factor

index 3c1fab30af88100b68fb78d3e1693c1639d90205..785cb681083c14f5ef3e3806f767f99a2a6fb1be 100644 (file)
@@ -3,7 +3,8 @@
 USING: compiler.cfg.build-stack-frame compiler.cfg.gc-checks
 compiler.cfg.linear-scan compiler.cfg.representations
 compiler.cfg.save-contexts compiler.cfg.ssa.destruction
-compiler.cfg.stacks.vacant compiler.cfg.utilities compiler.cfg.write-barrier ;
+compiler.cfg.stacks.clearing compiler.cfg.stacks.vacant compiler.cfg.utilities
+compiler.cfg.write-barrier ;
 IN: compiler.cfg.finalization
 
 : finalize-cfg ( cfg -- )
@@ -11,7 +12,8 @@ IN: compiler.cfg.finalization
         select-representations
         insert-gc-checks
         eliminate-write-barriers
-        compute-vacant-sets
+        clear-uninitialized
+        fill-gc-maps
         insert-save-contexts
         destruct-ssa
         linear-scan
diff --git a/basis/compiler/cfg/stacks/clearing/clearing-tests.factor b/basis/compiler/cfg/stacks/clearing/clearing-tests.factor
new file mode 100644 (file)
index 0000000..a9ced56
--- /dev/null
@@ -0,0 +1,21 @@
+USING: compiler.cfg.instructions compiler.cfg.registers
+compiler.cfg.stacks.clearing tools.test ;
+IN: compiler.cfg.stacks.clearing.tests
+
+{ { } } [
+    { { 0 { } } { 0 { } } } state>replaces
+] unit-test
+
+{ t f } [
+    { { 0 { } } { 0 { } } } T{ ##peek { loc D 0 } } dangerous-insn?
+    { { 0 { } } { 0 { } } } T{ ##peek { loc D -1 } } dangerous-insn?
+] unit-test
+
+{
+    {
+        T{ ##replace-imm { src 17 } { loc D 0 } }
+        T{ ##replace-imm { src 17 } { loc D 1 } }
+    }
+} [
+    { { 2 { } } { 0 { } } } state>replaces
+] unit-test
diff --git a/basis/compiler/cfg/stacks/clearing/clearing.factor b/basis/compiler/cfg/stacks/clearing/clearing.factor
new file mode 100644 (file)
index 0000000..9385490
--- /dev/null
@@ -0,0 +1,25 @@
+USING: accessors arrays assocs combinators.short-circuit
+compiler.cfg.instructions compiler.cfg.registers compiler.cfg.rpo
+compiler.cfg.stacks.map kernel math sequences ;
+IN: compiler.cfg.stacks.clearing
+
+: state>replaces ( state -- replaces )
+    state>vacancies first2
+    [ [ <ds-loc> ] map ] [ [ <rs-loc> ] map ] bi* append
+    [ 17 swap f ##replace-imm boa ] map ;
+
+: dangerous-insn? ( state insn -- ? )
+    { [ nip ##peek? ] [ dangerous-peek? ] } 2&& ;
+
+: clearing-replaces ( assoc insn -- insns' )
+    [ of ] keep 2dup dangerous-insn? [
+        drop state>replaces
+    ] [ 2drop { } ] if ;
+
+: visit-insns ( assoc insns -- insns' )
+    [ [ clearing-replaces ] keep suffix ] with map V{ } concat-as ;
+
+: clear-uninitialized ( cfg -- )
+    [ trace-stack-state ] keep [
+        [ visit-insns ] change-instructions drop
+    ] with each-basic-block ;
index 1bab1d594a3766faa9e9fd3e8071cc3fb78ebd2a..fefe1d0d8ee07f5ce6305903ab3215b7c14b0d4b 100644 (file)
@@ -12,8 +12,8 @@ ARTICLE: "compiler.cfg.stacks.vacant" "Uninitialized/overinitialized stack locat
 }
 "The GC check runs before stack locations 0 and 1 have been initialized, and so the GC needs to scrub them so that they don't get traced. This is achieved by computing uninitialized locations with a dataflow analysis, and recording the information in GC maps. The call_frame_slot_visitor object in vm/slot_visitor.hpp reads this information from GC maps and performs the scrubbing." ;
 
-HELP: initial-state
-{ $description "Initially the stack bottom is at 0 for both the data and retain stacks and no replaces have been registered." } ;
+HELP: initial-state
+{ $description "Initially the stack bottom is at 0 for both the data and retain stacks and no replaces have been registered." } ;
 
 HELP: vacant>bits
 { $values
index 0e4d0e43163a509707f0b3eca2606675410515c0..60b3bb06f1637a5c6a1b73c33fe98850cfcf8efc 100644 (file)
@@ -5,59 +5,10 @@ compiler.cfg.utilities compiler.cfg.stacks.vacant kernel math sequences sorting
 tools.test vectors ;
 IN: compiler.cfg.stacks.vacant.tests
 
-! Utils
-: output-stack-map ( cfg -- map )
-    vacant-analysis run-dataflow-analysis
-    nip [ [ number>> ] dip ] assoc-map >alist natural-sort last second ;
-
-! Initially both the d and r stacks are empty.
-{
-    { { 0 { } } { 0 { } } }
-} [ V{ } insns>cfg output-stack-map ] unit-test
-
-! Raise d stack.
-{
-    { { 1 { } } { 0 { } } }
-} [ V{ T{ ##inc-d f 1 } } insns>cfg output-stack-map ] unit-test
-
-! Raise r stack.
 {
-    { { 0 { } } { 1 { } } }
-} [ V{ T{ ##inc-r f 1 } } insns>cfg output-stack-map ] unit-test
-
-! Uninitialized peeks
-[
-    V{
-        T{ ##inc-d f 1 }
-        T{ ##peek { dst 0 } { loc D 0 } }
-    } insns>cfg
-    compute-vacant-sets
-] [ vacant-peek? ] must-fail-with
-
-[
-    V{
-        T{ ##inc-r f 1 }
-        T{ ##peek { dst 0 } { loc R 0 } }
-    } insns>cfg
-    compute-vacant-sets
-] [ vacant-peek? ] must-fail-with
-
-
-! Here the peek refers to a parameter of the word.
-[ ] [
-    V{
-        T{ ##peek { dst 0 } { loc D 0 } }
-    } insns>cfg
-    compute-vacant-sets
-] unit-test
-
-! Replace -1 then peek is ok.
-[ ] [
-    V{
-        T{ ##replace { src 10 } { loc D -1 } }
-        T{ ##peek { dst 0 } { loc D -1 } }
-    } insns>cfg
-    compute-vacant-sets
+    { { { } { 0 0 0 } } { { } { 0 } } }
+} [
+    { { 4 { 3 2 1 -3 0 -2 -1 } } { 0 { -1 } } } state>gc-data
 ] unit-test
 
 ! Replace -1, then gc. Peek is ok here because the -1 should be
@@ -68,164 +19,99 @@ IN: compiler.cfg.stacks.vacant.tests
         T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
         T{ ##peek { dst 0 } { loc D -1 } }
     }
-    [ insns>cfg compute-vacant-sets ]
+    [ insns>cfg fill-in-gc-maps ]
     [ second gc-map>> check-d>> ] bi
 ] unit-test
 
-! Should be ok because the value was at 0 when the gc ran.
-{ { -1 { -1 } } } [
-    V{
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
-        T{ ##inc-d f -1 }
-        T{ ##peek { dst 0 } { loc D -1 } }
-    } insns>cfg output-stack-map first
-] unit-test
-
-! Should not be ok because the value wasn't initialized when gc ran.
-[
-    V{
-        T{ ##inc-d f 1 }
-        T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
-        T{ ##peek { dst 0 } { loc D 0 } }
-    } insns>cfg
-    compute-vacant-sets
-] [ vacant-peek? ] must-fail-with
+! ! Replace -1, then gc. Peek is ok here because the -1 should be
+! ! checked.
+! { { 0 } } [
+!     V{
+!         T{ ##replace { src 10 } { loc D -1 } }
+!         T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
+!         T{ ##peek { dst 0 } { loc D -1 } }
+!     }
+!     [ insns>cfg compute-vacant-sets ]
+!     [ second gc-map>> check-d>> ] bi
+! ] unit-test
+
+! ! Should not be ok because the value wasn't initialized when gc ran.
+! [
+!     V{
+!         T{ ##inc-d f 1 }
+!         T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
+!         T{ ##peek { dst 0 } { loc D 0 } }
+!     } insns>cfg
+!     compute-vacant-sets
+! ] [ vacant-peek? ] must-fail-with
 
 ! visit-insn should set the gc info.
 { { 0 0 } { } } [
     { { 2 { } } { 0 { } } }
     T{ ##alien-invoke { gc-map T{ gc-map } } }
-    [ visit-insn drop ] keep gc-map>> [ scrub-d>> ] [ scrub-r>> ] bi
-] unit-test
-
-{
-    { { 0 { } } { 0 { } } }
-} [
-    V{ T{ ##safepoint } T{ ##prologue } T{ ##branch } }
-    insns>cfg output-stack-map
-] unit-test
-
-{
-    { { 0 { 0 1 2 } } { 0 { } } }
-} [
-    V{
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##replace { src 10 } { loc D 1 } }
-        T{ ##replace { src 10 } { loc D 2 } }
-    } insns>cfg output-stack-map
-] unit-test
-
-{
-    { { 1 { 1 0 } } { 0 { } } }
-} [
-    V{
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##inc-d f 1 }
-        T{ ##replace { src 10 } { loc D 0 } }
-    } insns>cfg output-stack-map
-] unit-test
-
-{
-    { 0 { 0 -1 } }
-} [
-    V{
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##inc-d f 1 }
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##inc-d f -1 }
-    } insns>cfg output-stack-map first
-] unit-test
-
-{
-    { 0 { -1 } }
-} [
-    V{
-        T{ ##inc-d f 1 }
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##inc-d f -1 }
-    } insns>cfg output-stack-map first
-] unit-test
-
-{
-    { { { } { 0 0 0 } } { { } { 0 } } }
-} [
-    { { 4 { 3 2 1 -3 0 -2 -1 } } { 0 { -1 } } } state>gc-data
+    [ gc-map>> set-gc-map ] keep gc-map>> [ scrub-d>> ] [ scrub-r>> ] bi
 ] unit-test
 
-! ##call clears the overinitialized slots.
-{
-    { -1 { } }
-} [
-    V{
-        T{ ##replace { src 10 } { loc D 0 } }
-        T{ ##inc-d f -1 }
-        T{ ##call }
-    } insns>cfg output-stack-map first
-] unit-test
 
-: cfg1 ( -- cfg )
-    V{
-        T{ ##inc-d f 1 }
-        T{ ##replace { src 10 } { loc D 0 } }
-    } 0 insns>block
-    V{
-        T{ ##peek { dst 37 } { loc D 0 } }
-        T{ ##inc-d f -1 }
-    } 1 insns>block
-    1vector >>successors block>cfg ;
-
-{ { 0 { -1 } } } [ cfg1 output-stack-map first ] unit-test
-
-! Same cfg structure as the bug1021:run-test word but with
-! non-datastack instructions mostly omitted.
-: bug1021-cfg ( -- cfg )
-    {
-        { 0 V{ T{ ##safepoint } T{ ##prologue } T{ ##branch } } }
-        {
-            1 V{
-                T{ ##inc-d f 2 }
-                T{ ##replace { src 0 } { loc D 1 } }
-                T{ ##replace { src 0 } { loc D 0 } }
-            }
-        }
-        {
-            2 V{
-                T{ ##call { word <array> } }
-            }
-        }
-        {
-            3 V{
-                T{ ##inc-d f 2 }
-                T{ ##peek { dst 0 } { loc D 2 } }
-                T{ ##peek { dst 0 } { loc D 3 } }
-                T{ ##replace { src 0 } { loc D 2 } }
-                T{ ##replace { src 0 } { loc D 3 } }
-                T{ ##replace { src 0 } { loc D 1 } }
-            }
-        }
-        {
-            8 V{
-                T{ ##inc-d f 3 }
-                T{ ##peek { dst 0 } { loc D 5 } }
-                T{ ##replace { src 0 } { loc D 0 } }
-                T{ ##replace { src 0 } { loc D 3 } }
-                T{ ##peek { dst 0 } { loc D 4 } }
-                T{ ##replace { src 0 } { loc D 1 } }
-                T{ ##replace { src 0 } { loc D 2 } }
-            }
-        }
-        {
-            10 V{
-
-                T{ ##inc-d f -3 }
-                T{ ##peek { dst 0 } { loc D -3 } }
-                T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
-            }
-        }
-    } [ over insns>block ] assoc-map dup
-    { { 0 1 } { 1 2 } { 2 3 } { 3 8 } { 8 10 } } make-edges 0 of block>cfg ;
-
-{ { 4 { 3 2 1 -3 0 -2 -1 } } } [
-    bug1021-cfg output-stack-map first
-] unit-test
+! ! read-ok?
+! { t } [
+!     0 { 0 { 0 1 2 } } read-ok?
+! ] unit-test
+
+! { f } [
+!     2 { 3 { } } read-ok?
+! ] unit-test
+
+! { f } [
+!     -1 { 3 { } } read-ok?
+! ] unit-test
+
+! ! { f } [
+! !     4 { 3 { } } read-ok?
+! ! ] unit-test
+
+! { t } [
+!     4 { 0 { } } read-ok?
+! ] unit-test
+
+! { t } [
+!     4 { 1 { 0 } } read-ok?
+! ] unit-test
+
+! ! Uninitialized peeks
+! [
+!     V{
+!         T{ ##inc-d f 1 }
+!         T{ ##peek { dst 0 } { loc D 0 } }
+!     } insns>cfg
+!     compute-vacant-sets
+! ] [ vacant-peek? ] must-fail-with
+
+! [
+!     V{
+!         T{ ##inc-r f 1 }
+!         T{ ##peek { dst 0 } { loc R 0 } }
+!     } insns>cfg
+!     compute-vacant-sets
+! ] [ vacant-peek? ] must-fail-with
+
+! ! Here again the peek refers to a parameter word, but there are
+! ! uninitialized stack locations. That probably isn't ok.
+! [
+!     V{
+!         T{ ##inc-d f 3 }
+!         T{ ##peek { dst 0 } { loc D 3 } }
+!     } insns>cfg
+!     compute-vacant-sets
+! ] [ vacant-peek? ] must-fail-with
+
+
+! ! Should not be ok because the value wasn't initialized when gc ran.
+! ! [
+! !     V{
+! !         T{ ##inc-d f 1 }
+! !         T{ ##alien-invoke { gc-map T{ gc-map { scrub-d { } } } } }
+! !         T{ ##peek { dst 0 } { loc D 0 } }
+! !     } insns>cfg
+! !     compute-map-sets
+! ! ] [ vacant-peek? ] must-fail-with
index 7b6efb157f9d60e53fe0c49b624b255359241814..002bbef51fe1f973fd28b759ebfba86e00b628ea 100644 (file)
@@ -1,25 +1,11 @@
-USING: accessors arrays compiler.cfg.dataflow-analysis
-compiler.cfg.instructions compiler.cfg.registers fry kernel math
-math.order sequences sets ;
+USING: accessors arrays assocs compiler.cfg.instructions
+compiler.cfg.stacks.map fry kernel math sequences ;
 IN: compiler.cfg.stacks.vacant
 
-! Utils
+! Utils
 : write-slots ( tuple values slots -- )
     [ execute( x y -- z ) ] 2each drop ;
 
-! Operations on the stack info
-: register-write ( n stack -- stack' )
-    first2 rot suffix members 2array ;
-
-: adjust-stack ( n stack -- stack' )
-    first2 pick '[ _ + ] map [ + ] dip 2array ;
-
-: read-ok? ( n stack -- ? )
-    [ first >= ] [ second in? ] 2bi or ;
-
-: stack>vacant ( stack -- seq )
-    first2 [ 0 max iota ] dip diff ;
-
 : vacant>bits ( vacant --  bits )
     [ { } ] [
         dup supremum 1 + 1 <array>
@@ -40,57 +26,10 @@ IN: compiler.cfg.stacks.vacant
 : state>gc-data ( state -- gc-data )
     [ stack>scrub-and-check ] map ;
 
-CONSTANT: initial-state { { 0 { } } { 0 { } } }
-
-: insn>location ( insn -- n ds? )
-    loc>> [ n>> ] [ ds-loc? ] bi ;
-
-: visit-replace ( state insn -- state' )
-    [ first2 ] dip insn>location
-    [ rot register-write swap ] [ swap register-write ] if 2array ;
-
-ERROR: vacant-peek insn ;
-
-: peek-loc-ok? ( state insn -- ? )
-    insn>location 0 1 ? rot nth read-ok? ;
-
-GENERIC: visit-insn ( state insn -- state' )
-
-M: ##inc-d visit-insn ( state insn -- state' )
-    n>> swap first2 [ adjust-stack ] dip 2array ;
-
-M: ##inc-r visit-insn ( state insn -- state' )
-    n>> swap first2 swapd adjust-stack 2array ;
-
-M: ##replace-imm visit-insn visit-replace ;
-M: ##replace visit-insn visit-replace ;
-
-M: ##peek visit-insn ( state insn -- state' )
-    2dup peek-loc-ok? [ drop ] [ vacant-peek ] if ;
-
-M: ##call visit-insn ( state insn -- state' )
-    ! After a word call, we can't trust any overinitialized locations
-    ! to contain valid pointers anymore.
-    drop [ first2 [ 0 >= ] filter 2array ] map ;
-
 : set-gc-map ( state gc-map -- )
     swap state>gc-data concat
     { >>scrub-d >>check-d >>scrub-r >>check-r } write-slots ;
 
-M: gc-map-insn visit-insn ( state insn -- state' )
-    dupd gc-map>> set-gc-map ;
-
-M: insn visit-insn ( state insn -- state' )
-    drop ;
-
-FORWARD-ANALYSIS: vacant
-
-M: vacant-analysis transfer-set ( in-set bb dfa -- out-set )
-    drop instructions>> swap [ visit-insn ] reduce ;
-
-M: vacant-analysis ignore-block? ( bb dfa -- ? )
-    2drop f ;
-
-! Picking the first means that a block will only be analyzed once.
-M: vacant-analysis join-sets ( sets bb dfa -- set )
-    2drop [ initial-state ] [ first ] if-empty ;
+: fill-gc-maps ( cfg -- )
+    trace-stack-state [ drop gc-map-insn? ] assoc-filter
+    [ swap gc-map>> set-gc-map ] assoc-each ;