]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: in poset, implement path collapsing
authorGiovanni Bajo <rasky@develer.com>
Fri, 27 Sep 2019 22:05:54 +0000 (00:05 +0200)
committerGiovanni Bajo <rasky@develer.com>
Sat, 26 Oct 2019 07:52:35 +0000 (07:52 +0000)
Sometimes, poset needs to collapse a path making all nodes in
the path aliases. For instance, we know that A<=N1<=B and we
learn that B<=A, we can deduce A==N1==B, and thus we can
collapse all paths from A to B into a single aliased node.

Currently, this is a TODO. This CL implements the path-collapsing
primitive by doing a DFS walk to build a bitset of all nodes
across all paths, and then calling the new aliasnodes that allow
to mark multiple nodes as aliases of a single master node.

This helps only 4 times in std+cmd, but it will be fundamental
when we will rely on poset to calculate numerical limits, to
calculate the correct values.

This also fixes #35157, a bug uncovered by a previous CL in this
serie. A testcase will be added soon.

Change-Id: I5fc54259711769d7bd7c2d166a5abc1cddc26350
Reviewed-on: https://go-review.googlesource.com/c/go/+/200861
Run-TryBot: Giovanni Bajo <rasky@develer.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/ssa/poset.go
src/cmd/compile/internal/ssa/poset_test.go

index 329471ac386ca71715d133c5b971bd69d6151f8d..f5a2b3a8c2104ff785f4daf0bb3decb2a64a8d01 100644 (file)
@@ -629,27 +629,56 @@ func (po *poset) mergeroot(r1, r2 uint32) uint32 {
        return r
 }
 
-// collapsepath marks i1 and i2 as equal and collapses as equal all
-// nodes across all paths between i1 and i2. If a strict edge is
+// collapsepath marks n1 and n2 as equal and collapses as equal all
+// nodes across all paths between n1 and n2. If a strict edge is
 // found, the function does not modify the DAG and returns false.
+// Complexity is O(n).
 func (po *poset) collapsepath(n1, n2 *Value) bool {
        i1, i2 := po.values[n1.ID], po.values[n2.ID]
        if po.reaches(i1, i2, true) {
                return false
        }
 
-       // TODO: for now, only handle the simple case of i2 being child of i1
-       l, r := po.children(i1)
-       if l.Target() == i2 || r.Target() == i2 {
-               i2s := newBitset(int(po.lastidx) + 1)
-               i2s.Set(i2)
-               po.aliasnodes(n1, i2s)
-               po.addchild(i1, i2, false)
-               return true
-       }
+       // Find all the paths from i1 to i2
+       paths := po.findpaths(i1, i2)
+       // Mark all nodes in all the paths as aliases of n1
+       // (excluding n1 itself)
+       paths.Clear(i1)
+       po.aliasnodes(n1, paths)
        return true
 }
 
+// findpaths is a recursive function that calculates all paths from cur to dst
+// and return them as a bitset (the index of a node is set in the bitset if
+// that node is on at least one path from cur to dst).
+// We do a DFS from cur (stopping going deep any time we reach dst, if ever),
+// and mark as part of the paths any node that has a children which is already
+// part of the path (or is dst itself).
+func (po *poset) findpaths(cur, dst uint32) bitset {
+       seen := newBitset(int(po.lastidx + 1))
+       path := newBitset(int(po.lastidx + 1))
+       path.Set(dst)
+       po.findpaths1(cur, dst, seen, path)
+       return path
+}
+
+func (po *poset) findpaths1(cur, dst uint32, seen bitset, path bitset) {
+       if cur == dst {
+               return
+       }
+       seen.Set(cur)
+       l, r := po.chl(cur), po.chr(cur)
+       if !seen.Test(l) {
+               po.findpaths1(l, dst, seen, path)
+       }
+       if !seen.Test(r) {
+               po.findpaths1(r, dst, seen, path)
+       }
+       if path.Test(l) || path.Test(r) {
+               path.Set(cur)
+       }
+}
+
 // Check whether it is recorded that i1!=i2
 func (po *poset) isnoneq(i1, i2 uint32) bool {
        if i1 == i2 {
index 6f048a30a844ce0de179c04cd64fb1f84b12dcf5..a6db1d1c24a8cfc69e98aec56e75b132469ed7a7 100644 (file)
@@ -438,7 +438,127 @@ func TestPosetStrict(t *testing.T) {
        })
 }
 
-func TestSetEqual(t *testing.T) {
+func TestPosetCollapse(t *testing.T) {
+       testPosetOps(t, false, []posetTestOp{
+               {Checkpoint, 0, 0},
+               // Create a complex graph of <= relations among nodes between 10 and 25.
+               {SetOrderOrEqual, 10, 15},
+               {SetOrderOrEqual, 15, 20},
+               {SetOrderOrEqual, 20, vconst(20)},
+               {SetOrderOrEqual, vconst(20), 25},
+               {SetOrderOrEqual, 10, 12},
+               {SetOrderOrEqual, 12, 16},
+               {SetOrderOrEqual, 16, vconst(20)},
+               {SetOrderOrEqual, 10, 17},
+               {SetOrderOrEqual, 17, 25},
+               {SetOrderOrEqual, 15, 18},
+               {SetOrderOrEqual, 18, vconst(20)},
+               {SetOrderOrEqual, 15, 19},
+               {SetOrderOrEqual, 19, 25},
+
+               // These are other paths not part of the main collapsing path
+               {SetOrderOrEqual, 10, 11},
+               {SetOrderOrEqual, 11, 26},
+               {SetOrderOrEqual, 13, 25},
+               {SetOrderOrEqual, 100, 25},
+               {SetOrderOrEqual, 101, 15},
+               {SetOrderOrEqual, 102, 10},
+               {SetOrderOrEqual, 25, 103},
+               {SetOrderOrEqual, 20, 104},
+
+               {Checkpoint, 0, 0},
+               // Collapse everything by setting 10 >= 25: this should make everything equal
+               {SetOrderOrEqual, 25, 10},
+
+               // Check that all nodes are pairwise equal now
+               {Equal, 10, 12},
+               {Equal, 10, 15},
+               {Equal, 10, 16},
+               {Equal, 10, 17},
+               {Equal, 10, 18},
+               {Equal, 10, 19},
+               {Equal, 10, vconst(20)},
+               {Equal, 10, vconst2(20)},
+               {Equal, 10, 25},
+
+               {Equal, 12, 15},
+               {Equal, 12, 16},
+               {Equal, 12, 17},
+               {Equal, 12, 18},
+               {Equal, 12, 19},
+               {Equal, 12, vconst(20)},
+               {Equal, 12, vconst2(20)},
+               {Equal, 12, 25},
+
+               {Equal, 15, 16},
+               {Equal, 15, 17},
+               {Equal, 15, 18},
+               {Equal, 15, 19},
+               {Equal, 15, vconst(20)},
+               {Equal, 15, vconst2(20)},
+               {Equal, 15, 25},
+
+               {Equal, 16, 17},
+               {Equal, 16, 18},
+               {Equal, 16, 19},
+               {Equal, 16, vconst(20)},
+               {Equal, 16, vconst2(20)},
+               {Equal, 16, 25},
+
+               {Equal, 17, 18},
+               {Equal, 17, 19},
+               {Equal, 17, vconst(20)},
+               {Equal, 17, vconst2(20)},
+               {Equal, 17, 25},
+
+               {Equal, 18, 19},
+               {Equal, 18, vconst(20)},
+               {Equal, 18, vconst2(20)},
+               {Equal, 18, 25},
+
+               {Equal, 19, vconst(20)},
+               {Equal, 19, vconst2(20)},
+               {Equal, 19, 25},
+
+               {Equal, vconst(20), vconst2(20)},
+               {Equal, vconst(20), 25},
+
+               {Equal, vconst2(20), 25},
+
+               // ... but not 11/26/100/101/102, which were on a different path
+               {Equal_Fail, 10, 11},
+               {Equal_Fail, 10, 26},
+               {Equal_Fail, 10, 100},
+               {Equal_Fail, 10, 101},
+               {Equal_Fail, 10, 102},
+               {OrderedOrEqual, 10, 26},
+               {OrderedOrEqual, 25, 26},
+               {OrderedOrEqual, 13, 25},
+               {OrderedOrEqual, 13, 10},
+
+               {Undo, 0, 0},
+               {OrderedOrEqual, 10, 25},
+               {Equal_Fail, 10, 12},
+               {Equal_Fail, 10, 15},
+               {Equal_Fail, 10, 25},
+
+               {Undo, 0, 0},
+       })
+
+       testPosetOps(t, false, []posetTestOp{
+               {Checkpoint, 0, 0},
+               {SetOrderOrEqual, 10, 15},
+               {SetOrderOrEqual, 15, 20},
+               {SetOrderOrEqual, 20, 25},
+               {SetOrder, 10, 16},
+               {SetOrderOrEqual, 16, 20},
+               // Check that we cannot collapse here because of the strict relation 10<16
+               {SetOrderOrEqual_Fail, 20, 10},
+               {Undo, 0, 0},
+       })
+}
+
+func TestPosetSetEqual(t *testing.T) {
        testPosetOps(t, false, []posetTestOp{
                // 10<=20<=30<40,  20<=100<110
                {Checkpoint, 0, 0},