Skip to content

Commit 7db60ef

Browse files
committed
Untangle the funcCount / rootCount confusion and simplify lower wing call node info.
1 parent 278a370 commit 7db60ef

3 files changed

Lines changed: 131 additions & 140 deletions

File tree

src/profile-logic/call-node-info.ts

Lines changed: 81 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,23 @@ export interface CallNodeInfoInverted extends CallNodeInfo {
608608
nodeHandle: IndexIntoCallNodeTable
609609
): [SuffixOrderIndex, SuffixOrderIndex];
610610

611-
// The number of functions. There is one root in the inverted tree per func,
612-
// and the inverted root's call node handle equals the func index.
611+
// The number of functions in the func table this CNI was built from.
612+
// (Sizes per-func scratch buffers in callers; not directly tied to the
613+
// number of inverted-tree roots — see getRootCount.)
613614
getFuncCount(): number;
614615

616+
// The number of roots in the inverted tree. Root call node handles are
617+
// 0..getRootCount()-1. For the full inverted tree this equals funcCount
618+
// (one root per func); for a lower-wing CNI it is 1.
619+
getRootCount(): number;
620+
621+
// Returns the inverted root call node handle for `funcIndex`, or -1 if no
622+
// root in this CNI corresponds to that function (e.g. a lower-wing CNI only
623+
// has a root for its selected func).
624+
getRootNodeForFunc(
625+
funcIndex: IndexIntoFuncTable
626+
): IndexIntoCallNodeTable | -1;
627+
615628
// True if the given node is a root of the inverted tree.
616629
isRoot(nodeHandle: IndexIntoCallNodeTable): boolean;
617630

@@ -1059,13 +1072,23 @@ export class LazyInvertedCallNodeInfo implements CallNodeInfoInverted {
10591072
return this._suffixOrderIndexes;
10601073
}
10611074

1062-
// Get the number of functions. There is one root per function.
1063-
// So this is also the number of roots at the same time.
1064-
// The inverted call node index for a root is the same as the function index.
1075+
// Get the number of functions. There is one root per function in this
1076+
// (full inverted) CNI, so it also equals getRootCount().
1077+
// For roots, the inverted call node handle equals the function index.
10651078
getFuncCount(): number {
10661079
return this._rootCount;
10671080
}
10681081

1082+
getRootCount(): number {
1083+
return this._rootCount;
1084+
}
1085+
1086+
// For the full inverted tree, each func has its own root and the root
1087+
// handle equals the func index.
1088+
getRootNodeForFunc(funcIndex: IndexIntoFuncTable): InvertedCallNodeHandle {
1089+
return funcIndex;
1090+
}
1091+
10691092
// Returns whether the given node is a root node.
10701093
isRoot(nodeHandle: InvertedCallNodeHandle): boolean {
10711094
return nodeHandle < this._rootCount;
@@ -1757,9 +1780,9 @@ export class LazyInvertedCallNodeInfo implements CallNodeInfoInverted {
17571780
}
17581781

17591782
// The lower wing's inverted call node table. Index 0 is the selected-func
1760-
// root; indices 1..length-1 are non-root nodes. The public handle scheme
1761-
// (rootHandle === funcIndex, non-root handles start at funcCount) is preserved
1762-
// by the accessors, which translate handle ↔ table index.
1783+
// root; indices 1..length-1 are non-root nodes. The public InvertedCallNodeHandle
1784+
// IS the table index (no translation): handle 0 is the root, handles
1785+
// 1..length-1 are non-roots.
17631786
//
17641787
// Layout note: rows are appended in BFS-by-inverted-depth order, with each
17651788
// parent's children emitted contiguously, sorted by func index. Index 0 is the
@@ -1801,19 +1824,23 @@ export type LowerWingTable = {
18011824
* only renders the rows currently in the viewport, so building deeper than
18021825
* necessary is wasted work.
18031826
*
1804-
* Conventions kept from LazyInvertedCallNodeInfo so consumers (especially
1805-
* CallTreeInternalInverted and computeCallTreeTimingsInverted) work unchanged:
1806-
* - There are funcCount logical roots, with rootHandle === funcIndex.
1807-
* Only the root at selectedFuncIndex has non-empty data.
1808-
* - Non-root handles start at funcCount.
1827+
* Handle scheme:
1828+
* - There is exactly one root, with handle === 0. Its func is `selectedFuncIndex`
1829+
* (or 0 when no selection — the row is then a placeholder with no children).
1830+
* - Non-root handles are 1..length-1 and equal their table index. Handles
1831+
* other than 0 only exist after the BFS has expanded to materialize them.
1832+
* - `getRootNodeForFunc(f)` returns 0 iff f === selectedFuncIndex, else -1.
18091833
* - `getSuffixOrderedCallNodes()` and `getSuffixOrderIndexes()` are kept in
18101834
* sync as the BFS refines partitions.
18111835
*/
18121836
export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
18131837
_callNodeTable: CallNodeTable;
18141838
_stackIndexToNonInvertedCallNodeIndex: Int32Array;
18151839
_defaultCategory: IndexIntoCategoryList;
1816-
_rootCount: number;
1840+
// Number of functions in the originating func table. Used to size per-func
1841+
// scratch buffers in `_extendToDepth`; unrelated to the number of roots
1842+
// (which is always 1 — see getRootCount).
1843+
_funcCount: number;
18171844
_selectedFuncIndex: IndexIntoFuncTable | null;
18181845

18191846
// Suffix-ordered entry points: maps suffix order index -> non-inverted call
@@ -1884,7 +1911,7 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
18841911
this._stackIndexToNonInvertedCallNodeIndex =
18851912
stackIndexToNonInvertedCallNodeIndex;
18861913
this._defaultCategory = defaultCategory;
1887-
this._rootCount = funcCount;
1914+
this._funcCount = funcCount;
18881915
this._selectedFuncIndex = selectedFuncIndex;
18891916

18901917
// Pass 1: collect root-most non-inverted nodes whose func is the selected
@@ -2007,38 +2034,35 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
20072034
}
20082035

20092036
getFuncCount(): number {
2010-
return this._rootCount;
2037+
return this._funcCount;
2038+
}
2039+
2040+
getRootCount(): number {
2041+
return 1;
2042+
}
2043+
2044+
// Only the selected func has a root in this CNI. Other funcs return -1.
2045+
getRootNodeForFunc(
2046+
funcIndex: IndexIntoFuncTable
2047+
): InvertedCallNodeHandle | -1 {
2048+
return funcIndex === this._selectedFuncIndex ? 0 : -1;
20112049
}
20122050

20132051
isRoot(nodeHandle: InvertedCallNodeHandle): boolean {
2014-
return nodeHandle < this._rootCount;
2052+
return nodeHandle === 0;
20152053
}
20162054

20172055
getSuffixOrderIndexRangeForCallNode(
20182056
nodeHandle: InvertedCallNodeHandle
20192057
): [SuffixOrderIndex, SuffixOrderIndex] {
2020-
if (nodeHandle < this._rootCount) {
2021-
if (nodeHandle === this._selectedFuncIndex) {
2022-
return [this._tSoStart[0], this._tSoEnd[0]];
2023-
}
2024-
return [0, 0];
2025-
}
2026-
const idx = nodeHandle - this._rootCount + 1;
2027-
return [this._tSoStart[idx], this._tSoEnd[idx]];
2058+
return [this._tSoStart[nodeHandle], this._tSoEnd[nodeHandle]];
20282059
}
20292060

20302061
getChildren(nodeIndex: InvertedCallNodeHandle): InvertedCallNodeHandle[] {
2031-
if (nodeIndex < this._rootCount) {
2032-
if (nodeIndex !== this._selectedFuncIndex) {
2033-
return EMPTY_LOWER_WING_CHILDREN;
2034-
}
2035-
// Root: depth 0 — process it (if needed) so children are populated.
2036-
this._extendToDepth(0);
2037-
return this._tChildren[0];
2038-
}
2039-
const idx = nodeIndex - this._rootCount + 1;
2040-
this._extendToDepth(this._tDepth[idx]);
2041-
return this._tChildren[idx];
2062+
// Extend the BFS far enough for this node to have its children
2063+
// materialized (depth 0 for the root, depth d for a node at depth d).
2064+
this._extendToDepth(this._tDepth[nodeIndex]);
2065+
return this._tChildren[nodeIndex];
20422066
}
20432067

20442068
getCallNodePathFromIndex(
@@ -2049,12 +2073,11 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
20492073
}
20502074
const callNodePath: number[] = [];
20512075
let current = callNodeHandle;
2052-
while (current >= this._rootCount) {
2053-
const idx = current - this._rootCount + 1;
2054-
callNodePath.push(this._tFunc[idx]);
2055-
current = this._tPrefix[idx];
2076+
while (current !== 0) {
2077+
callNodePath.push(this._tFunc[current]);
2078+
current = this._tPrefix[current];
20562079
}
2057-
callNodePath.push(current); // root: handle === func
2080+
callNodePath.push(this._tFunc[0]);
20582081
callNodePath.reverse();
20592082
return callNodePath;
20602083
}
@@ -2065,14 +2088,12 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
20652088
if (callNodePath.length === 0) {
20662089
return null;
20672090
}
2068-
// Reject paths that don't start at the selected func — only that root has
2069-
// any descendants.
2091+
// The only root in this CNI is for the selected func. Any path that doesn't
2092+
// start there has no match.
20702093
if (callNodePath[0] !== this._selectedFuncIndex) {
2071-
// The first func is also a valid root handle (handle === func), but only
2072-
// the selected func's path can be reached beyond depth 0.
2073-
return callNodePath.length === 1 ? callNodePath[0] : null;
2094+
return null;
20742095
}
2075-
let handle: InvertedCallNodeHandle = callNodePath[0];
2096+
let handle: InvertedCallNodeHandle = 0;
20762097
for (let i = 1; i < callNodePath.length; i++) {
20772098
const next = this.getCallNodeIndexFromParentAndFunc(
20782099
handle,
@@ -2091,7 +2112,7 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
20912112
func: IndexIntoFuncTable
20922113
): InvertedCallNodeHandle | null {
20932114
if (parent === -1) {
2094-
return func; // For roots, handle === func.
2115+
return this.getRootNodeForFunc(func) === -1 ? null : 0;
20952116
}
20962117
const children = this.getChildren(parent);
20972118
// Children are sorted by func; bisect to find a match.
@@ -2108,68 +2129,39 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
21082129
prefixForNode(
21092130
callNodeHandle: InvertedCallNodeHandle
21102131
): InvertedCallNodeHandle | -1 {
2111-
if (callNodeHandle < this._rootCount) {
2112-
return -1;
2113-
}
2114-
return this._tPrefix[callNodeHandle - this._rootCount + 1];
2132+
return this._tPrefix[callNodeHandle];
21152133
}
21162134

21172135
funcForNode(callNodeHandle: InvertedCallNodeHandle): IndexIntoFuncTable {
2118-
if (callNodeHandle < this._rootCount) {
2119-
return callNodeHandle; // root handle === func index
2120-
}
2121-
return this._tFunc[callNodeHandle - this._rootCount + 1];
2136+
return this._tFunc[callNodeHandle];
21222137
}
21232138

21242139
categoryForNode(
21252140
callNodeHandle: InvertedCallNodeHandle
21262141
): IndexIntoCategoryList {
2127-
if (callNodeHandle < this._rootCount) {
2128-
return callNodeHandle === this._selectedFuncIndex
2129-
? this._tCategory[0]
2130-
: this._defaultCategory;
2131-
}
2132-
return this._tCategory[callNodeHandle - this._rootCount + 1];
2142+
return this._tCategory[callNodeHandle];
21332143
}
21342144

21352145
subcategoryForNode(
21362146
callNodeHandle: InvertedCallNodeHandle
21372147
): IndexIntoSubcategoryListForCategory {
2138-
if (callNodeHandle < this._rootCount) {
2139-
return callNodeHandle === this._selectedFuncIndex
2140-
? this._tSubcategory[0]
2141-
: 0;
2142-
}
2143-
return this._tSubcategory[callNodeHandle - this._rootCount + 1];
2148+
return this._tSubcategory[callNodeHandle];
21442149
}
21452150

21462151
innerWindowIDForNode(
21472152
callNodeHandle: InvertedCallNodeHandle
21482153
): IndexIntoCategoryList {
2149-
if (callNodeHandle < this._rootCount) {
2150-
return callNodeHandle === this._selectedFuncIndex
2151-
? this._tInnerWindowID[0]
2152-
: 0;
2153-
}
2154-
return this._tInnerWindowID[callNodeHandle - this._rootCount + 1];
2154+
return this._tInnerWindowID[callNodeHandle];
21552155
}
21562156

21572157
depthForNode(callNodeHandle: InvertedCallNodeHandle): number {
2158-
if (callNodeHandle < this._rootCount) {
2159-
return 0;
2160-
}
2161-
return this._tDepth[callNodeHandle - this._rootCount + 1];
2158+
return this._tDepth[callNodeHandle];
21622159
}
21632160

21642161
sourceFramesInlinedIntoSymbolForNode(
21652162
callNodeHandle: InvertedCallNodeHandle
21662163
): IndexIntoNativeSymbolTable | -1 | -2 {
2167-
if (callNodeHandle < this._rootCount) {
2168-
return callNodeHandle === this._selectedFuncIndex
2169-
? (this._tInlinedInto[0] as IndexIntoNativeSymbolTable | -1 | -2)
2170-
: -2;
2171-
}
2172-
return this._tInlinedInto[callNodeHandle - this._rootCount + 1] as
2164+
return this._tInlinedInto[callNodeHandle] as
21732165
| IndexIntoNativeSymbolTable
21742166
| -1
21752167
| -2;
@@ -2242,7 +2234,6 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
22422234
const scratchSelf = this._scratchSelf;
22432235
const scratchDeep = this._scratchDeep;
22442236
const defaultCategory = this._defaultCategory;
2245-
const funcCount = this._rootCount;
22462237

22472238
// Scratch list of distinct funcs seen in Pass 1 of the current node.
22482239
// Cleared at the top of each iteration.
@@ -2257,7 +2248,8 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
22572248
const parentSuffixStart = tSoStart[k];
22582249
const parentSuffixEnd = tSoEnd[k];
22592250
const parentDepth = tDepth[k];
2260-
const parentHandle = k === 0 ? tFunc[0] : funcCount + k - 1;
2251+
// Public handle === table index.
2252+
const parentHandle = k;
22612253

22622254
// Pass 1: walk each entry's deepNode one step up, bucket-counting by
22632255
// the new deepNode's func. Entries whose chain bottoms out are tallied
@@ -2359,7 +2351,9 @@ export class LowerWingCallNodeInfo implements CallNodeInfoInverted {
23592351
}
23602352
}
23612353

2362-
const newHandle = funcCount + tPrefix.length - 1;
2354+
// Public handle === table index, which is the slot the child row is
2355+
// about to be pushed into.
2356+
const newHandle = tPrefix.length;
23632357
tPrefix.push(parentHandle);
23642358
tFunc.push(f);
23652359
tCategory.push(category);

0 commit comments

Comments
 (0)