Skip to content

Commit c70e547

Browse files
committed
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 golang#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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 18d57bc commit c70e547

File tree

2 files changed

+161
-12
lines changed

2 files changed

+161
-12
lines changed

src/cmd/compile/internal/ssa/poset.go

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -629,27 +629,56 @@ func (po *poset) mergeroot(r1, r2 uint32) uint32 {
629629
return r
630630
}
631631

632-
// collapsepath marks i1 and i2 as equal and collapses as equal all
633-
// nodes across all paths between i1 and i2. If a strict edge is
632+
// collapsepath marks n1 and n2 as equal and collapses as equal all
633+
// nodes across all paths between n1 and n2. If a strict edge is
634634
// found, the function does not modify the DAG and returns false.
635+
// Complexity is O(n).
635636
func (po *poset) collapsepath(n1, n2 *Value) bool {
636637
i1, i2 := po.values[n1.ID], po.values[n2.ID]
637638
if po.reaches(i1, i2, true) {
638639
return false
639640
}
640641

641-
// TODO: for now, only handle the simple case of i2 being child of i1
642-
l, r := po.children(i1)
643-
if l.Target() == i2 || r.Target() == i2 {
644-
i2s := newBitset(int(po.lastidx) + 1)
645-
i2s.Set(i2)
646-
po.aliasnodes(n1, i2s)
647-
po.addchild(i1, i2, false)
648-
return true
649-
}
642+
// Find all the paths from i1 to i2
643+
paths := po.findpaths(i1, i2)
644+
// Mark all nodes in all the paths as aliases of n1
645+
// (excluding n1 itself)
646+
paths.Clear(i1)
647+
po.aliasnodes(n1, paths)
650648
return true
651649
}
652650

651+
// findpaths is a recursive function that calculates all paths from cur to dst
652+
// and return them as a bitset (the index of a node is set in the bitset if
653+
// that node is on at least one path from cur to dst).
654+
// We do a DFS from cur (stopping going deep any time we reach dst, if ever),
655+
// and mark as part of the paths any node that has a children which is already
656+
// part of the path (or is dst itself).
657+
func (po *poset) findpaths(cur, dst uint32) bitset {
658+
seen := newBitset(int(po.lastidx + 1))
659+
path := newBitset(int(po.lastidx + 1))
660+
path.Set(dst)
661+
po.findpaths1(cur, dst, seen, path)
662+
return path
663+
}
664+
665+
func (po *poset) findpaths1(cur, dst uint32, seen bitset, path bitset) {
666+
if cur == dst {
667+
return
668+
}
669+
seen.Set(cur)
670+
l, r := po.chl(cur), po.chr(cur)
671+
if !seen.Test(l) {
672+
po.findpaths1(l, dst, seen, path)
673+
}
674+
if !seen.Test(r) {
675+
po.findpaths1(r, dst, seen, path)
676+
}
677+
if path.Test(l) || path.Test(r) {
678+
path.Set(cur)
679+
}
680+
}
681+
653682
// Check whether it is recorded that i1!=i2
654683
func (po *poset) isnoneq(i1, i2 uint32) bool {
655684
if i1 == i2 {

src/cmd/compile/internal/ssa/poset_test.go

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,127 @@ func TestPosetStrict(t *testing.T) {
438438
})
439439
}
440440

441-
func TestSetEqual(t *testing.T) {
441+
func TestPosetCollapse(t *testing.T) {
442+
testPosetOps(t, false, []posetTestOp{
443+
{Checkpoint, 0, 0},
444+
// Create a complex graph of <= relations among nodes between 10 and 25.
445+
{SetOrderOrEqual, 10, 15},
446+
{SetOrderOrEqual, 15, 20},
447+
{SetOrderOrEqual, 20, vconst(20)},
448+
{SetOrderOrEqual, vconst(20), 25},
449+
{SetOrderOrEqual, 10, 12},
450+
{SetOrderOrEqual, 12, 16},
451+
{SetOrderOrEqual, 16, vconst(20)},
452+
{SetOrderOrEqual, 10, 17},
453+
{SetOrderOrEqual, 17, 25},
454+
{SetOrderOrEqual, 15, 18},
455+
{SetOrderOrEqual, 18, vconst(20)},
456+
{SetOrderOrEqual, 15, 19},
457+
{SetOrderOrEqual, 19, 25},
458+
459+
// These are other paths not part of the main collapsing path
460+
{SetOrderOrEqual, 10, 11},
461+
{SetOrderOrEqual, 11, 26},
462+
{SetOrderOrEqual, 13, 25},
463+
{SetOrderOrEqual, 100, 25},
464+
{SetOrderOrEqual, 101, 15},
465+
{SetOrderOrEqual, 102, 10},
466+
{SetOrderOrEqual, 25, 103},
467+
{SetOrderOrEqual, 20, 104},
468+
469+
{Checkpoint, 0, 0},
470+
// Collapse everything by setting 10 >= 25: this should make everything equal
471+
{SetOrderOrEqual, 25, 10},
472+
473+
// Check that all nodes are pairwise equal now
474+
{Equal, 10, 12},
475+
{Equal, 10, 15},
476+
{Equal, 10, 16},
477+
{Equal, 10, 17},
478+
{Equal, 10, 18},
479+
{Equal, 10, 19},
480+
{Equal, 10, vconst(20)},
481+
{Equal, 10, vconst2(20)},
482+
{Equal, 10, 25},
483+
484+
{Equal, 12, 15},
485+
{Equal, 12, 16},
486+
{Equal, 12, 17},
487+
{Equal, 12, 18},
488+
{Equal, 12, 19},
489+
{Equal, 12, vconst(20)},
490+
{Equal, 12, vconst2(20)},
491+
{Equal, 12, 25},
492+
493+
{Equal, 15, 16},
494+
{Equal, 15, 17},
495+
{Equal, 15, 18},
496+
{Equal, 15, 19},
497+
{Equal, 15, vconst(20)},
498+
{Equal, 15, vconst2(20)},
499+
{Equal, 15, 25},
500+
501+
{Equal, 16, 17},
502+
{Equal, 16, 18},
503+
{Equal, 16, 19},
504+
{Equal, 16, vconst(20)},
505+
{Equal, 16, vconst2(20)},
506+
{Equal, 16, 25},
507+
508+
{Equal, 17, 18},
509+
{Equal, 17, 19},
510+
{Equal, 17, vconst(20)},
511+
{Equal, 17, vconst2(20)},
512+
{Equal, 17, 25},
513+
514+
{Equal, 18, 19},
515+
{Equal, 18, vconst(20)},
516+
{Equal, 18, vconst2(20)},
517+
{Equal, 18, 25},
518+
519+
{Equal, 19, vconst(20)},
520+
{Equal, 19, vconst2(20)},
521+
{Equal, 19, 25},
522+
523+
{Equal, vconst(20), vconst2(20)},
524+
{Equal, vconst(20), 25},
525+
526+
{Equal, vconst2(20), 25},
527+
528+
// ... but not 11/26/100/101/102, which were on a different path
529+
{Equal_Fail, 10, 11},
530+
{Equal_Fail, 10, 26},
531+
{Equal_Fail, 10, 100},
532+
{Equal_Fail, 10, 101},
533+
{Equal_Fail, 10, 102},
534+
{OrderedOrEqual, 10, 26},
535+
{OrderedOrEqual, 25, 26},
536+
{OrderedOrEqual, 13, 25},
537+
{OrderedOrEqual, 13, 10},
538+
539+
{Undo, 0, 0},
540+
{OrderedOrEqual, 10, 25},
541+
{Equal_Fail, 10, 12},
542+
{Equal_Fail, 10, 15},
543+
{Equal_Fail, 10, 25},
544+
545+
{Undo, 0, 0},
546+
})
547+
548+
testPosetOps(t, false, []posetTestOp{
549+
{Checkpoint, 0, 0},
550+
{SetOrderOrEqual, 10, 15},
551+
{SetOrderOrEqual, 15, 20},
552+
{SetOrderOrEqual, 20, 25},
553+
{SetOrder, 10, 16},
554+
{SetOrderOrEqual, 16, 20},
555+
// Check that we cannot collapse here because of the strict relation 10<16
556+
{SetOrderOrEqual_Fail, 20, 10},
557+
{Undo, 0, 0},
558+
})
559+
}
560+
561+
func TestPosetSetEqual(t *testing.T) {
442562
testPosetOps(t, false, []posetTestOp{
443563
// 10<=20<=30<40, 20<=100<110
444564
{Checkpoint, 0, 0},

0 commit comments

Comments
 (0)