Skip to content

Commit 674e84b

Browse files
committed
Merge pull request #8471 from Microsoft/controlFlowCaching
Improved control flow caching
2 parents 7521891 + 6589176 commit 674e84b

11 files changed

+1218
-89
lines changed

src/compiler/binder.ts

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ namespace ts {
129129
let Symbol: { new (flags: SymbolFlags, name: string): Symbol };
130130
let classifiableNames: Map<string>;
131131

132-
const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable };
133-
const reportedUnreachableFlow: FlowNode = { kind: FlowKind.Unreachable };
132+
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
133+
const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
134134

135135
function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
136136
file = f;
@@ -471,7 +471,7 @@ namespace ts {
471471
savedActiveLabels = activeLabels;
472472

473473
hasExplicitReturn = false;
474-
currentFlow = { kind: FlowKind.Start };
474+
currentFlow = { flags: FlowFlags.Start };
475475
currentBreakTarget = undefined;
476476
currentContinueTarget = undefined;
477477
activeLabels = undefined;
@@ -483,7 +483,7 @@ namespace ts {
483483

484484
bindReachableStatement(node);
485485

486-
if (currentFlow.kind !== FlowKind.Unreachable && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
486+
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
487487
flags |= NodeFlags.HasImplicitReturn;
488488
if (hasExplicitReturn) {
489489
flags |= NodeFlags.HasExplicitReturn;
@@ -639,55 +639,80 @@ namespace ts {
639639
return false;
640640
}
641641

642-
function createFlowLabel(): FlowLabel {
642+
function createBranchLabel(): FlowLabel {
643643
return {
644-
kind: FlowKind.Label,
644+
flags: FlowFlags.BranchLabel,
645645
antecedents: undefined
646646
};
647647
}
648648

649-
function createFlowLoopLabel(): FlowLabel {
649+
function createLoopLabel(): FlowLabel {
650650
return {
651-
kind: FlowKind.LoopLabel,
651+
flags: FlowFlags.LoopLabel,
652652
antecedents: undefined
653653
};
654654
}
655655

656+
function setFlowNodeReferenced(flow: FlowNode) {
657+
// On first reference we set the Referenced flag, thereafter we set the Shared flag
658+
flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced;
659+
}
660+
656661
function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
657-
if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) {
662+
if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
658663
(label.antecedents || (label.antecedents = [])).push(antecedent);
664+
setFlowNodeReferenced(antecedent);
659665
}
660666
}
661667

662-
function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode {
663-
if (antecedent.kind === FlowKind.Unreachable) {
668+
function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression): FlowNode {
669+
if (antecedent.flags & FlowFlags.Unreachable) {
664670
return antecedent;
665671
}
666672
if (!expression) {
667-
return assumeTrue ? antecedent : unreachableFlow;
673+
return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow;
668674
}
669-
if (expression.kind === SyntaxKind.TrueKeyword && !assumeTrue || expression.kind === SyntaxKind.FalseKeyword && assumeTrue) {
675+
if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
676+
expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
670677
return unreachableFlow;
671678
}
672679
if (!isNarrowingExpression(expression)) {
673680
return antecedent;
674681
}
682+
setFlowNodeReferenced(antecedent);
675683
return <FlowCondition>{
676-
kind: FlowKind.Condition,
684+
flags,
677685
antecedent,
678686
expression,
679-
assumeTrue
680687
};
681688
}
682689

683690
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
691+
setFlowNodeReferenced(antecedent);
684692
return <FlowAssignment>{
685-
kind: FlowKind.Assignment,
693+
flags: FlowFlags.Assignment,
686694
antecedent,
687695
node
688696
};
689697
}
690698

699+
function skipSimpleConditionalFlow(flow: FlowNode) {
700+
// We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain
701+
// no constructs that affect control flow type analysis. Such simple flows have no effect on the
702+
// code paths that follow and ignoring them means we'll do less work.
703+
if (flow.flags & FlowFlags.BranchLabel && (<FlowLabel>flow).antecedents.length === 2) {
704+
const a = (<FlowLabel>flow).antecedents[0];
705+
const b = (<FlowLabel>flow).antecedents[1];
706+
if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition ||
707+
a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) &&
708+
(<FlowCondition>a).antecedent === (<FlowCondition>b).antecedent &&
709+
(<FlowCondition>a).expression === (<FlowCondition>b).expression) {
710+
return (<FlowCondition>a).antecedent;
711+
}
712+
}
713+
return flow;
714+
}
715+
691716
function finishFlowLabel(flow: FlowLabel): FlowNode {
692717
const antecedents = flow.antecedents;
693718
if (!antecedents) {
@@ -696,7 +721,7 @@ namespace ts {
696721
if (antecedents.length === 1) {
697722
return antecedents[0];
698723
}
699-
return flow;
724+
return skipSimpleConditionalFlow(flow);
700725
}
701726

702727
function isStatementCondition(node: Node) {
@@ -747,8 +772,8 @@ namespace ts {
747772
currentTrueTarget = saveTrueTarget;
748773
currentFalseTarget = saveFalseTarget;
749774
if (!node || !isLogicalExpression(node)) {
750-
addAntecedent(trueTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ true));
751-
addAntecedent(falseTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ false));
775+
addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
776+
addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
752777
}
753778
}
754779

@@ -763,9 +788,9 @@ namespace ts {
763788
}
764789

765790
function bindWhileStatement(node: WhileStatement): void {
766-
const preWhileLabel = createFlowLoopLabel();
767-
const preBodyLabel = createFlowLabel();
768-
const postWhileLabel = createFlowLabel();
791+
const preWhileLabel = createLoopLabel();
792+
const preBodyLabel = createBranchLabel();
793+
const postWhileLabel = createBranchLabel();
769794
addAntecedent(preWhileLabel, currentFlow);
770795
currentFlow = preWhileLabel;
771796
bindCondition(node.expression, preBodyLabel, postWhileLabel);
@@ -776,9 +801,9 @@ namespace ts {
776801
}
777802

778803
function bindDoStatement(node: DoStatement): void {
779-
const preDoLabel = createFlowLoopLabel();
780-
const preConditionLabel = createFlowLabel();
781-
const postDoLabel = createFlowLabel();
804+
const preDoLabel = createLoopLabel();
805+
const preConditionLabel = createBranchLabel();
806+
const postDoLabel = createBranchLabel();
782807
addAntecedent(preDoLabel, currentFlow);
783808
currentFlow = preDoLabel;
784809
bindIterativeStatement(node.statement, postDoLabel, preConditionLabel);
@@ -789,9 +814,9 @@ namespace ts {
789814
}
790815

791816
function bindForStatement(node: ForStatement): void {
792-
const preLoopLabel = createFlowLoopLabel();
793-
const preBodyLabel = createFlowLabel();
794-
const postLoopLabel = createFlowLabel();
817+
const preLoopLabel = createLoopLabel();
818+
const preBodyLabel = createBranchLabel();
819+
const postLoopLabel = createBranchLabel();
795820
bind(node.initializer);
796821
addAntecedent(preLoopLabel, currentFlow);
797822
currentFlow = preLoopLabel;
@@ -804,8 +829,8 @@ namespace ts {
804829
}
805830

806831
function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void {
807-
const preLoopLabel = createFlowLoopLabel();
808-
const postLoopLabel = createFlowLabel();
832+
const preLoopLabel = createLoopLabel();
833+
const postLoopLabel = createBranchLabel();
809834
addAntecedent(preLoopLabel, currentFlow);
810835
currentFlow = preLoopLabel;
811836
bind(node.expression);
@@ -820,9 +845,9 @@ namespace ts {
820845
}
821846

822847
function bindIfStatement(node: IfStatement): void {
823-
const thenLabel = createFlowLabel();
824-
const elseLabel = createFlowLabel();
825-
const postIfLabel = createFlowLabel();
848+
const thenLabel = createBranchLabel();
849+
const elseLabel = createBranchLabel();
850+
const postIfLabel = createBranchLabel();
826851
bindCondition(node.expression, thenLabel, elseLabel);
827852
currentFlow = finishFlowLabel(thenLabel);
828853
bind(node.thenStatement);
@@ -875,7 +900,7 @@ namespace ts {
875900
}
876901

877902
function bindTryStatement(node: TryStatement): void {
878-
const postFinallyLabel = createFlowLabel();
903+
const postFinallyLabel = createBranchLabel();
879904
const preTryFlow = currentFlow;
880905
// TODO: Every statement in try block is potentially an exit point!
881906
bind(node.tryBlock);
@@ -893,7 +918,7 @@ namespace ts {
893918
}
894919

895920
function bindSwitchStatement(node: SwitchStatement): void {
896-
const postSwitchLabel = createFlowLabel();
921+
const postSwitchLabel = createBranchLabel();
897922
bind(node.expression);
898923
const saveBreakTarget = currentBreakTarget;
899924
const savePreSwitchCaseFlow = preSwitchCaseFlow;
@@ -915,17 +940,17 @@ namespace ts {
915940
for (let i = 0; i < clauses.length; i++) {
916941
const clause = clauses[i];
917942
if (clause.statements.length) {
918-
if (currentFlow.kind === FlowKind.Unreachable) {
943+
if (currentFlow.flags & FlowFlags.Unreachable) {
919944
currentFlow = preSwitchCaseFlow;
920945
}
921946
else {
922-
const preCaseLabel = createFlowLabel();
947+
const preCaseLabel = createBranchLabel();
923948
addAntecedent(preCaseLabel, preSwitchCaseFlow);
924949
addAntecedent(preCaseLabel, currentFlow);
925950
currentFlow = finishFlowLabel(preCaseLabel);
926951
}
927952
bind(clause);
928-
if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
953+
if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
929954
errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch);
930955
}
931956
}
@@ -951,8 +976,8 @@ namespace ts {
951976
}
952977

953978
function bindLabeledStatement(node: LabeledStatement): void {
954-
const preStatementLabel = createFlowLoopLabel();
955-
const postStatementLabel = createFlowLabel();
979+
const preStatementLabel = createLoopLabel();
980+
const postStatementLabel = createBranchLabel();
956981
bind(node.label);
957982
addAntecedent(preStatementLabel, currentFlow);
958983
const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel);
@@ -1001,7 +1026,7 @@ namespace ts {
10011026
}
10021027

10031028
function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
1004-
const preRightLabel = createFlowLabel();
1029+
const preRightLabel = createBranchLabel();
10051030
if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
10061031
bindCondition(node.left, preRightLabel, falseTarget);
10071032
}
@@ -1031,7 +1056,7 @@ namespace ts {
10311056
const operator = node.operatorToken.kind;
10321057
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
10331058
if (isTopLevelLogicalExpression(node)) {
1034-
const postExpressionLabel = createFlowLabel();
1059+
const postExpressionLabel = createBranchLabel();
10351060
bindLogicalExpression(node, postExpressionLabel, postExpressionLabel);
10361061
currentFlow = finishFlowLabel(postExpressionLabel);
10371062
}
@@ -1048,9 +1073,9 @@ namespace ts {
10481073
}
10491074

10501075
function bindConditionalExpressionFlow(node: ConditionalExpression) {
1051-
const trueLabel = createFlowLabel();
1052-
const falseLabel = createFlowLabel();
1053-
const postExpressionLabel = createFlowLabel();
1076+
const trueLabel = createBranchLabel();
1077+
const falseLabel = createBranchLabel();
1078+
const postExpressionLabel = createBranchLabel();
10541079
bindCondition(node.condition, trueLabel, falseLabel);
10551080
currentFlow = finishFlowLabel(trueLabel);
10561081
bind(node.whenTrue);
@@ -2065,7 +2090,7 @@ namespace ts {
20652090
}
20662091

20672092
function checkUnreachable(node: Node): boolean {
2068-
if (currentFlow.kind !== FlowKind.Unreachable) {
2093+
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
20692094
return false;
20702095
}
20712096
if (currentFlow === unreachableFlow) {

0 commit comments

Comments
 (0)