Skip to content

Commit f9ca0c4

Browse files
committedMay 1, 2025
perf: try to improve cfg construction and union
1 parent 4ef2d21 commit f9ca0c4

File tree

13 files changed

+59
-47
lines changed

13 files changed

+59
-47
lines changed
 

‎src/control-flow/extract-cfg.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ function dataflowCfgFolds(dataflowGraph: DataflowGraph): FoldFunctions<ParentInf
7878
* @param ast - the normalized AST
7979
* @param graph - additional dataflow facts to consider by the control flow extraction
8080
* @param simplifications - a list of simplification passes to apply to the control flow graph
81+
*
82+
* @see {@link extractSimpleCfg} - for a simplified version of this function
8183
*/
8284
export function extractCFG<Info=ParentInformation>(
8385
ast: NormalizedAst<Info>,
@@ -87,6 +89,13 @@ export function extractCFG<Info=ParentInformation>(
8789
return simplifyControlFlowInformation(foldAst(ast.ast, graph ? dataflowCfgFolds(graph) : cfgFolds), simplifications);
8890
}
8991

92+
/**
93+
* Simplified version of {@link extractCFG} that is much quicker, but much simpler!
94+
*/
95+
export function extractSimpleCfg<Info=ParentInformation>(ast: NormalizedAst<Info>) {
96+
return foldAst(ast.ast, cfgFolds);
97+
}
98+
9099
function cfgLeaf(type: CfgVertexType.Expression | CfgVertexType.Statement): (leaf: RNodeWithParent) => ControlFlowInformation {
91100
return (leaf: RNodeWithParent) => {
92101
const graph = new ControlFlowGraph();
@@ -318,9 +327,10 @@ function cfgFunctionCall(call: RFunctionCall<ParentInformation>, name: ControlFl
318327
continue;
319328
}
320329
graph.merge(arg.graph);
321-
info.breaks.push(...arg.breaks);
322-
info.nexts.push(...arg.nexts);
323-
info.returns.push(...arg.returns);
330+
info.breaks = info.breaks.concat(arg.breaks);
331+
info.nexts = info.nexts.concat(arg.nexts);
332+
info.returns = info.returns.concat(arg.returns);
333+
324334
for(const entry of arg.entryPoints) {
325335
for(const exit of lastArgExits) {
326336
graph.addEdge(entry, exit, { label: CfgEdgeType.Fd });
@@ -389,9 +399,10 @@ function cfgArgumentOrParameter(node: RNodeWithParent, name: ControlFlowInformat
389399

390400
if(name) {
391401
graph.merge(name.graph);
392-
info.breaks.push(...name.breaks);
393-
info.nexts.push(...name.nexts);
394-
info.returns.push(...name.returns);
402+
info.breaks = info.breaks.concat(name.breaks);
403+
info.nexts = info.nexts.concat(name.nexts);
404+
info.returns = info.returns.concat(name.returns);
405+
395406
for(const entry of name.entryPoints) {
396407
graph.addEdge(entry, node.info.id, { label: CfgEdgeType.Fd });
397408
}
@@ -406,9 +417,10 @@ function cfgArgumentOrParameter(node: RNodeWithParent, name: ControlFlowInformat
406417

407418
if(value) {
408419
graph.merge(value.graph);
409-
info.breaks.push(...value.breaks);
410-
info.nexts.push(...value.nexts);
411-
info.returns.push(...value.returns);
420+
info.breaks = info.breaks.concat(value.breaks);
421+
info.nexts = info.nexts.concat(value.nexts);
422+
info.returns = info.returns.concat(value.returns);
423+
412424
for(const exitPoint of currentExitPoint) {
413425
for(const entry of value.entryPoints) {
414426
graph.addEdge(entry, exitPoint, { label: CfgEdgeType.Fd });
@@ -476,9 +488,9 @@ function cfgAccess(access: RAccess<ParentInformation>, name: ControlFlowInformat
476488
}
477489
}
478490
result.exitPoints = accessor.exitPoints;
479-
result.breaks.push(...accessor.breaks);
480-
result.nexts.push(...accessor.nexts);
481-
result.returns.push(...accessor.returns);
491+
result.breaks = result.breaks.concat(accessor.breaks);
492+
result.nexts = result.nexts.concat(accessor.nexts);
493+
result.returns = result.returns.concat(accessor.returns);
482494
}
483495
for(const exitPoint of result.exitPoints) {
484496
graph.addEdge(access.info.id + '-exit', exitPoint, { label: CfgEdgeType.Fd });
@@ -518,9 +530,9 @@ function cfgExprList(node: RExpressionList<ParentInformation>, _grouping: unknow
518530
}
519531
}
520532
result.graph.merge(expression.graph);
521-
result.breaks.push(...expression.breaks);
522-
result.nexts.push(...expression.nexts);
523-
result.returns.push(...expression.returns);
533+
result.breaks = result.breaks.concat(expression.breaks);
534+
result.nexts = result.nexts.concat(expression.nexts);
535+
result.returns = result.returns.concat(expression.returns);
524536
result.exitPoints = expression.exitPoints;
525537
}
526538

‎src/control-flow/simple-visitor.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function visitCfgInReverseOrder(
2222
visitor: (node: NodeId) => boolean | void
2323
): void {
2424
const visited = new Set<NodeId>();
25-
const queue = [...startNodes];
25+
let queue = [...startNodes];
2626
while(queue.length > 0) {
2727
const current = queue.pop() as NodeId;
2828
if(visited.has(current)) {
@@ -34,7 +34,7 @@ export function visitCfgInReverseOrder(
3434
}
3535
const get = graph.getVertex(current);
3636
if(get?.type === CfgVertexType.Block) {
37-
queue.push(...get.elems.toReversed().map(e => e.id));
37+
queue = queue.concat(get.elems.toReversed().map(e => e.id));
3838
}
3939
const incoming = graph.outgoing(current) ?? [];
4040
for(const [from] of incoming) {
@@ -60,7 +60,7 @@ export function visitCfgInOrder(
6060
visitor: (node: NodeId) => boolean | void
6161
): void {
6262
const visited = new Set<NodeId>();
63-
const queue = [...startNodes];
63+
let queue = [...startNodes];
6464
while(queue.length > 0) {
6565
const current = queue.shift() as NodeId;
6666
if(visited.has(current)) {
@@ -72,7 +72,7 @@ export function visitCfgInOrder(
7272
}
7373
const get = graph.getVertex(current);
7474
if(get?.type === CfgVertexType.Block) {
75-
queue.push(...get.elems.map(e => e.id));
75+
queue = queue.concat(get.elems.map(e => e.id));
7676
}
7777
const outgoing = graph.ingoing(current) ?? [];
7878
for(const [to] of outgoing) {

‎src/dataflow/environments/resolve-by-name.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const AliasHandler = {
120120
} as const satisfies Record<VertexType, AliasHandler>;
121121

122122
function getUseAlias(sourceId: NodeId, dataflow: DataflowGraph, environment: REnvironmentInformation): NodeId[] | undefined {
123-
const definitions: NodeId[] = [];
123+
let definitions: NodeId[] = [];
124124

125125
// Source is Symbol -> resolve definitions of symbol
126126
const identifier = recoverName(sourceId, dataflow.idMap);
@@ -140,7 +140,7 @@ function getUseAlias(sourceId: NodeId, dataflow: DataflowGraph, environment: REn
140140
if(def.value === undefined) {
141141
return undefined;
142142
}
143-
definitions.push(...def.value);
143+
definitions = definitions.concat(def.value);
144144
} else if(def.type === ReferenceType.Constant || def.type === ReferenceType.BuiltInConstant) {
145145
definitions.push(def.nodeId);
146146
} else {

‎src/dataflow/extractor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { RParseRequest, RParseRequests } from '../r-bridge/retriever';
1717
import { initializeCleanEnvironments } from './environments/environment';
1818
import { standaloneSourceFile } from './internal/process/functions/call/built-in/built-in-source';
1919
import type { DataflowGraph } from './graph/graph';
20-
import { extractCFG } from '../control-flow/extract-cfg';
20+
import { extractSimpleCfg } from '../control-flow/extract-cfg';
2121
import { EdgeType } from './graph/edge';
2222
import {
2323
identifyLinkToLastCallRelation
@@ -73,7 +73,7 @@ function resolveLinkToSideEffects(ast: NormalizedAst, graph: DataflowGraph) {
7373
if(typeof s !== 'object') {
7474
continue;
7575
}
76-
cfg ??= extractCFG(ast).graph;
76+
cfg ??= extractSimpleCfg(ast).graph;
7777
/* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
7878
const potentials = identifyLinkToLastCallRelation(s.id, cfg, graph, s.linkTo);
7979
for(const pot of potentials) {

‎src/dataflow/graph/graph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ export class DataflowGraph<
381381
}
382382
}
383383

384-
this.sourced.push(...otherGraph.sourced);
384+
this._sourced = this._sourced.concat(otherGraph.sourced);
385385

386386
for(const unknown of otherGraph.unknownSideEffects) {
387387
this._unknownSideEffects.add(unknown);

‎src/dataflow/internal/linker.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { VertexType } from '../graph/vertex';
2222
import { resolveByName } from '../environments/resolve-by-name';
2323
import type { BuiltIn } from '../environments/built-in';
2424
import { isBuiltIn } from '../environments/built-in';
25-
import { slicerLogger } from '../../slicing/static/static-slicer';
2625
import type { REnvironmentInformation } from '../environments/environment';
2726
import { findByPrefixIfUnique } from '../../util/prefix';
2827

@@ -298,7 +297,6 @@ export function getAllLinkedFunctionDefinitions(
298297

299298
const currentInfo = dataflowGraph.get(currentId, true);
300299
if(currentInfo === undefined) {
301-
slicerLogger.trace('skipping unknown link');
302300
continue;
303301
}
304302
visited.add(currentId);
@@ -308,7 +306,7 @@ export function getAllLinkedFunctionDefinitions(
308306
const returnEdges = outgoingEdges.filter(([_, e]) => edgeIncludesType(e.types, EdgeType.Returns));
309307
if(returnEdges.length > 0) {
310308
// only traverse return edges and do not follow `calls` etc. as this indicates that we have a function call which returns a result, and not the function calls itself
311-
potential.push(...returnEdges.map(([target]) => target).filter(id => !visited.has(id)));
309+
potential = potential.concat(...returnEdges.map(([target]) => target).filter(id => !visited.has(id)));
312310
continue;
313311
}
314312

‎src/dataflow/internal/process/functions/call/built-in/built-in-expression-list.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export function processExpressionList<OtherInfo>(
127127
const remainingRead = new Map<string, IdentifierReference[]>();
128128

129129
const nextGraph = new DataflowGraph(data.completeAst.idMap);
130-
const out = [];
130+
let out: IdentifierReference[] = [];
131131
const exitPoints: ExitPoint[] = [];
132132

133133
let expressionCounter = 0;
@@ -157,7 +157,7 @@ export function processExpressionList<OtherInfo>(
157157

158158
addNonDefaultExitPoints(exitPoints, processed.exitPoints);
159159

160-
out.push(...processed.out);
160+
out = out.concat(processed.out);
161161

162162
expensiveTrace(dataflowLogger, () => `expression ${expressionCounter} of ${expressions.length} has ${processed.unknownReferences.length} unknown nodes`);
163163

‎src/dataflow/internal/process/functions/call/built-in/built-in-quote.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ export function processQuote<OtherInfo>(
2020
const startEnv = data.environment;
2121
const { information, processedArguments, fnRef } = processKnownFunctionCall({ name, args, rootId, data, forceArgs: config.forceArgs, origin: 'builtin:quote' });
2222

23-
const inRefs: IdentifierReference[] = [fnRef];
24-
const outRefs: IdentifierReference[] = [];
25-
const unknownRefs: IdentifierReference[] = [];
23+
let inRefs: IdentifierReference[] = [fnRef];
24+
let outRefs: IdentifierReference[] = [];
25+
let unknownRefs: IdentifierReference[] = [];
2626

2727
for(let i = 0; i < args.length; i++) {
2828
const processedArg = processedArguments[i];
2929
if(processedArg && i !== config?.quoteArgumentsWithIndex) {
30-
inRefs.push(...processedArg.in);
31-
outRefs.push(...processedArg.out);
32-
unknownRefs.push(...processedArg.unknownReferences);
30+
inRefs = inRefs.concat(processedArg.in);
31+
outRefs = outRefs.concat(processedArg.out);
32+
unknownRefs = unknownRefs.concat(processedArg.unknownReferences);
3333
} else if(processedArg) {
3434
information.graph.addEdge(rootId, processedArg.entryPoint, EdgeType.NonStandardEvaluation);
3535
/* nse actually affects _everything_ within that argument! */

‎src/dataflow/internal/process/functions/call/built-in/built-in-vector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function processVector<OtherInfo>(
3131
return fnCall.information;
3232
}
3333

34-
const vectorArgs: ContainerIndex[] = [];
34+
let vectorArgs: ContainerIndex[] = [];
3535
let argIndex = 1;
3636
for(const arg of args) {
3737
// Skip invalid argument types
@@ -61,7 +61,7 @@ export function processVector<OtherInfo>(
6161
nodeId: index.nodeId,
6262
};
6363
}) ?? [];
64-
vectorArgs.push(...flattenedIndices);
64+
vectorArgs = vectorArgs.concat(flattenedIndices);
6565
}
6666
}
6767

‎src/dataflow/internal/process/functions/call/unnamed-call-handling.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ export function processUnnamedFunctionCall<OtherInfo>(functionCall: RUnnamedFunc
5252
origin: ['unnamed']
5353
});
5454

55-
const inIds = remainingReadInArgs;
55+
let inIds = remainingReadInArgs;
5656
inIds.push({ nodeId: functionRootId, name: functionCallName, controlDependencies: data.controlDependencies, type: ReferenceType.Function });
5757

5858
if(functionCall.calledFunction.type === RType.FunctionDefinition) {
5959
linkArgumentsOnCall(callArgs, functionCall.calledFunction.parameters, finalGraph);
6060
}
6161
// push the called function to the ids:
62-
inIds.push(...calledFunction.in, ...calledFunction.unknownReferences);
62+
63+
inIds = inIds.concat(calledFunction.in, calledFunction.unknownReferences);
6364

6465
return {
6566
unknownReferences: [],

‎src/queries/catalog/call-context-query/call-context-query-executor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function retrieveAllCallAliases(nodeId: NodeId, graph: DataflowGraph): Map<strin
104104

105105
const visited = new Set<NodeId>();
106106
/* we store the current call name */
107-
const queue: (readonly [string, NodeId])[] = [[recoverContent(nodeId, graph) ?? '', nodeId]];
107+
let queue: (readonly [string, NodeId])[] = [[recoverContent(nodeId, graph) ?? '', nodeId]];
108108

109109
while(queue.length > 0) {
110110
const [str, id] = queue.shift() as [string, NodeId];
@@ -132,7 +132,7 @@ function retrieveAllCallAliases(nodeId: NodeId, graph: DataflowGraph): Map<strin
132132
.filter(([,{ types }]) => edgeIncludesType(types, EdgeType.Reads | EdgeType.DefinedBy | EdgeType.DefinedByOnCall))
133133
.map(([t]) => [recoverContent(t, graph) ?? '', t] as const);
134134
/** only follow defined-by and reads */
135-
queue.push(...x);
135+
queue = queue.concat(x);
136136
continue;
137137
}
138138

‎src/queries/catalog/call-context-query/identify-link-to-last-call-relation.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,17 @@ export function identifyLinkToLastCallRelation(
127127
if(node === from) {
128128
return;
129129
}
130-
const vertex = graph.get(node);
130+
const vertex = graph.get(node, true);
131131
if(vertex === undefined || vertex[0].tag !== VertexType.FunctionCall) {
132132
return;
133133
}
134-
if(callName.test(vertex[0].name)) {
135-
const act = cascadeIf ? cascadeIf(vertex[0], from, graph) : CascadeAction.Stop;
134+
const [fst] = vertex;
135+
if(callName.test(fst.name)) {
136+
const act = cascadeIf ? cascadeIf(fst, from, graph) : CascadeAction.Stop;
136137
if(act === CascadeAction.Skip) {
137138
return;
138139
}
139-
const tar = satisfiesCallTargets(vertex[0].id, graph, CallTargets.MustIncludeGlobal);
140+
const tar = satisfiesCallTargets(fst.id, graph, CallTargets.MustIncludeGlobal);
140141
if(tar === 'no') {
141142
return act === CascadeAction.Stop;
142143
}

‎src/queries/catalog/dependencies-query/dependencies-query-executor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,9 @@ function getArgumentValue(
334334
}
335335

336336
function getFunctionsToCheck(customFunctions: readonly FunctionInfo[] | undefined, ignoreDefaultFunctions: boolean, defaultFunctions: readonly FunctionInfo[]): FunctionInfo[] {
337-
const functions: FunctionInfo[] = ignoreDefaultFunctions ? [] : [...defaultFunctions];
337+
let functions: FunctionInfo[] = ignoreDefaultFunctions ? [] : [...defaultFunctions];
338338
if(customFunctions) {
339-
functions.push(...customFunctions);
339+
functions = functions.concat(customFunctions);
340340
}
341341
return functions;
342342
}

0 commit comments

Comments
 (0)
Failed to load comments.