From: Giovanni Bajo Date: Fri, 27 Sep 2019 22:05:54 +0000 (+0200) Subject: cmd/compile: in poset, implement path collapsing X-Git-Tag: go1.14beta1~554 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c70e5475e6ad21f4b5685ef18567a3e4e9388bbc;p=gostls13.git cmd/compile: in poset, implement path collapsing 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 TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go index 329471ac38..f5a2b3a8c2 100644 --- a/src/cmd/compile/internal/ssa/poset.go +++ b/src/cmd/compile/internal/ssa/poset.go @@ -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 { diff --git a/src/cmd/compile/internal/ssa/poset_test.go b/src/cmd/compile/internal/ssa/poset_test.go index 6f048a30a8..a6db1d1c24 100644 --- a/src/cmd/compile/internal/ssa/poset_test.go +++ b/src/cmd/compile/internal/ssa/poset_test.go @@ -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},