Skip to content

Commit cf6f16a

Browse files
committed
Expand comments for TreeNodeIndex
1 parent ee40fee commit cf6f16a

1 file changed

Lines changed: 54 additions & 26 deletions

File tree

compiler/rustc_data_structures/src/tree_node_index.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,80 @@
1111
/// 0bXXXXXXX100000000...0
1212
/// ```
1313
///
14-
/// Node reach after traversal of `LRLRRLLR` branches should be represented as `0b0101100110000...0`.
15-
/// Root is obviously encoded as `0b10000...0`.
14+
/// The reached node after traversal of `LRLRRLLR` branches (`L` for left, `R` for right) should be
15+
/// represented as `0b0101100110000...0`.
16+
/// Root is encoded as `0b10000...0` from an empty bitstring we don't need to traverse any branch
17+
/// to reach a binary tree's root.
18+
///
19+
/// Here are some examples:
20+
///
21+
/// ```text
22+
/// (root) -> 0b10000000...0
23+
/// L (left) -> 0b01000000...0
24+
/// R (right) -> 0b11000000...0
25+
/// LL -> 0b00100000...0
26+
/// RLR -> 0b10110000...0
27+
/// LRL -> 0b01010000...0
28+
/// LRRLR -> 0b01101100...0
29+
/// ```
30+
///
31+
/// ## Multi-way tree
32+
///
33+
/// But we don't necessary need to encode a binary tree directly.
34+
/// We can imagine some node to have `N` number of branches instead of two: right and left.
35+
/// We encode `0 <= i < N` numbered branches by interpreting `i`'s binary representation as
36+
/// bitstring for a binary tree traversal.
37+
///
38+
/// For example `N = 3`. Notice how right-most leaf node is unused:
39+
///
40+
/// ```text
41+
/// root
42+
/// root / \
43+
/// / | \ => . .
44+
/// 0 1 2 / \ / \
45+
/// 0 1 2 -
46+
/// ```
1647
///
1748
/// ## Order
1849
///
19-
/// Encoding allows to sort nodes in left < parent < right linear order.
20-
/// If you only consider leaves of a tree then those are sorted in order left < right.
50+
/// Encoding allows to sort nodes in `left < parent < right` linear order.
51+
/// If you only consider leaves of a tree then those are sorted in order `left < right`.
2152
///
2253
/// ## Used in
2354
///
24-
/// Primary purpose of `TreeNodeIndex` is to track order of parallel tasks of functions like `join`
25-
/// or `scope` (see `rustc_middle::sync`).
26-
/// This is done in query cycle handling code to determine **intended** first task for a single-
27-
/// threaded compiler front-end to execute even while multi-threaded.
55+
/// Primary purpose of `TreeNodeIndex` is to track order of parallel tasks of functions like
56+
/// `par_join`, `par_slice`, and others (see `rustc_middle::sync`).
57+
/// This is done in query cycle handling code to determine **intended** first task for a
58+
/// single-threaded compiler front-end to execute even while multi-threaded.
2859
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
2960
pub struct TreeNodeIndex(u64);
3061

3162
impl TreeNodeIndex {
63+
/// Root node of a tree
3264
pub const fn root() -> Self {
3365
Self(0x80000000_00000000)
3466
}
3567

36-
/// Append tree branch no. `branch_idx` reserving `bits` bits.
37-
fn try_bits_branch(self, branch_idx: u64, bits: u32) -> Option<Self> {
68+
/// Append branch `i` out of `n` branches total to `TreeNodeIndex`'s traversal representation.
69+
///
70+
/// This method reserves `ceil(log2(n))` bits within `TreeNodeIndex`'s integer encoded
71+
/// bitstring.
72+
pub fn branch(self, i: u64, n: u64) -> TreeNodeIndex {
73+
debug_assert!(i < n, "i = {i} should be less than n = {n}");
74+
// `branch_num != 0` per debug assertion above
75+
let bits = ceil_ilog2(n);
76+
3877
let trailing_zeros = self.0.trailing_zeros();
39-
let allocated_shift = trailing_zeros.checked_sub(bits)?;
78+
let allocated_shift = trailing_zeros.checked_sub(bits).expect(
79+
"TreeNodeIndex's free bits have been exhausted, make sure recursion is used carefully",
80+
);
4081
// Using wrapping operations for optimization, as edge cases are unreachable:
4182
// - `trailing_zeros < 64` as we are guaranteed at least one bit is set
4283
// - `allocated_shift == trailing_zeros - bits <= trailing_zeros < 64`
43-
Some(TreeNodeIndex(
84+
TreeNodeIndex(
4485
self.0 & !u64::wrapping_shl(1, trailing_zeros)
4586
| u64::wrapping_shl(1, allocated_shift)
46-
| branch_idx.unbounded_shl(allocated_shift.wrapping_add(1)),
47-
))
48-
}
49-
50-
/// Append tree branch no. `branch_idx` reserving `ceil(log2(branch_num))` bits.
51-
pub fn branch(self, branch_idx: u64, branch_num: u64) -> TreeNodeIndex {
52-
debug_assert!(
53-
branch_idx < branch_num,
54-
"branch_idx = {branch_idx} should be less than branch_num = {branch_num}"
55-
);
56-
// `branch_num != 0` per debug assertion above
57-
let bits = ceil_ilog2(branch_num);
58-
self.try_bits_branch(branch_idx, bits).expect(
59-
"TreeNodeIndex's free bits have been exhausted, make sure recursion is used carefully",
87+
| i.unbounded_shl(allocated_shift.wrapping_add(1)),
6088
)
6189
}
6290
}

0 commit comments

Comments
 (0)