diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:16:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:16:40 +0000 |
commit | 47ab3d4a42e9ab51c465c4322d2ec233f6324e6b (patch) | |
tree | a61a0ffd83f4a3def4b36e5c8e99630c559aa723 /src/cmd/compile/internal/ssa/lca.go | |
parent | Initial commit. (diff) | |
download | golang-1.18-upstream.tar.xz golang-1.18-upstream.zip |
Adding upstream version 1.18.10.upstream/1.18.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/compile/internal/ssa/lca.go')
-rw-r--r-- | src/cmd/compile/internal/ssa/lca.go | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/lca.go b/src/cmd/compile/internal/ssa/lca.go new file mode 100644 index 0000000..90daebe --- /dev/null +++ b/src/cmd/compile/internal/ssa/lca.go @@ -0,0 +1,127 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa + +import ( + "math/bits" +) + +// Code to compute lowest common ancestors in the dominator tree. +// https://en.wikipedia.org/wiki/Lowest_common_ancestor +// https://en.wikipedia.org/wiki/Range_minimum_query#Solution_using_constant_time_and_linearithmic_space + +// lcaRange is a data structure that can compute lowest common ancestor queries +// in O(n lg n) precomputed space and O(1) time per query. +type lcaRange struct { + // Additional information about each block (indexed by block ID). + blocks []lcaRangeBlock + + // Data structure for range minimum queries. + // rangeMin[k][i] contains the ID of the minimum depth block + // in the Euler tour from positions i to i+1<<k-1, inclusive. + rangeMin [][]ID +} + +type lcaRangeBlock struct { + b *Block + parent ID // parent in dominator tree. 0 = no parent (entry or unreachable) + firstChild ID // first child in dominator tree + sibling ID // next child of parent + pos int32 // an index in the Euler tour where this block appears (any one of its occurrences) + depth int32 // depth in dominator tree (root=0, its children=1, etc.) +} + +func makeLCArange(f *Func) *lcaRange { + dom := f.Idom() + + // Build tree + blocks := make([]lcaRangeBlock, f.NumBlocks()) + for _, b := range f.Blocks { + blocks[b.ID].b = b + if dom[b.ID] == nil { + continue // entry or unreachable + } + parent := dom[b.ID].ID + blocks[b.ID].parent = parent + blocks[b.ID].sibling = blocks[parent].firstChild + blocks[parent].firstChild = b.ID + } + + // Compute euler tour ordering. + // Each reachable block will appear #children+1 times in the tour. + tour := make([]ID, 0, f.NumBlocks()*2-1) + type queueEntry struct { + bid ID // block to work on + cid ID // child we're already working on (0 = haven't started yet) + } + q := []queueEntry{{f.Entry.ID, 0}} + for len(q) > 0 { + n := len(q) - 1 + bid := q[n].bid + cid := q[n].cid + q = q[:n] + + // Add block to tour. + blocks[bid].pos = int32(len(tour)) + tour = append(tour, bid) + + // Proceed down next child edge (if any). + if cid == 0 { + // This is our first visit to b. Set its depth. + blocks[bid].depth = blocks[blocks[bid].parent].depth + 1 + // Then explore its first child. + cid = blocks[bid].firstChild + } else { + // We've seen b before. Explore the next child. + cid = blocks[cid].sibling + } + if cid != 0 { + q = append(q, queueEntry{bid, cid}, queueEntry{cid, 0}) + } + } + + // Compute fast range-minimum query data structure + rangeMin := make([][]ID, 0, bits.Len64(uint64(len(tour)))) + rangeMin = append(rangeMin, tour) // 1-size windows are just the tour itself. + for logS, s := 1, 2; s < len(tour); logS, s = logS+1, s*2 { + r := make([]ID, len(tour)-s+1) + for i := 0; i < len(tour)-s+1; i++ { + bid := rangeMin[logS-1][i] + bid2 := rangeMin[logS-1][i+s/2] + if blocks[bid2].depth < blocks[bid].depth { + bid = bid2 + } + r[i] = bid + } + rangeMin = append(rangeMin, r) + } + + return &lcaRange{blocks: blocks, rangeMin: rangeMin} +} + +// find returns the lowest common ancestor of a and b. +func (lca *lcaRange) find(a, b *Block) *Block { + if a == b { + return a + } + // Find the positions of a and bin the Euler tour. + p1 := lca.blocks[a.ID].pos + p2 := lca.blocks[b.ID].pos + if p1 > p2 { + p1, p2 = p2, p1 + } + + // The lowest common ancestor is the minimum depth block + // on the tour from p1 to p2. We've precomputed minimum + // depth blocks for powers-of-two subsequences of the tour. + // Combine the right two precomputed values to get the answer. + logS := uint(log64(int64(p2 - p1))) + bid1 := lca.rangeMin[logS][p1] + bid2 := lca.rangeMin[logS][p2-1<<logS+1] + if lca.blocks[bid1].depth < lca.blocks[bid2].depth { + return lca.blocks[bid1].b + } + return lca.blocks[bid2].b +} |