Skip to content

Commit

Permalink
Only selectively deoptimize call parameters (#4892)
Browse files Browse the repository at this point in the history
* Rename deoptimizeThis to refer to arguments and make thisArg optional

* Extract ParameterVariable from ThisVariable

* Use ParameterVariables for parameters

* Move argument deoptimization into deoptimizeArguments

* First attempt at selective parameter deoptimization

While this seems to work, it has abysmal performance as evidenced by the
core-js test. A better approach might be only deoptimize top-level parts.

* New approach with better performance

* Improve handling of functions returned by functions

* Clear some TODOs

* Sometimes, we need to do nothing

* Do not deoptimize arguments to global calls by default

* Fix linting

* Improve coverage

* Improve coverage and simplify code

* Update dependencies
  • Loading branch information
lukastaegert committed Mar 9, 2023
1 parent d985838 commit cff3bbc
Show file tree
Hide file tree
Showing 78 changed files with 7,066 additions and 5,930 deletions.
3,101 changes: 1,584 additions & 1,517 deletions package-lock.json

Large diffs are not rendered by default.

58 changes: 29 additions & 29 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,32 +63,32 @@
"fsevents": "~2.3.2"
},
"devDependencies": {
"@codemirror/commands": "^6.2.0",
"@codemirror/lang-javascript": "^6.1.2",
"@codemirror/language": "^6.4.0",
"@codemirror/commands": "^6.2.1",
"@codemirror/lang-javascript": "^6.1.4",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.2.3",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.7.3",
"@codemirror/view": "^6.9.2",
"@jridgewell/sourcemap-codec": "^1.4.14",
"@mermaid-js/mermaid-cli": "^9.3.0",
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-buble": "^1.0.1",
"@rollup/plugin-commonjs": "^24.0.0",
"@mermaid-js/mermaid-cli": "^10.0.2",
"@rollup/plugin-alias": "^4.0.3",
"@rollup/plugin-buble": "^1.0.2",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.3.0",
"@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/pluginutils": "^5.0.2",
"@types/estree": "1.0.0",
"@types/node": "^14.18.36",
"@types/signal-exit": "^3.0.1",
"@types/yargs-parser": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@typescript-eslint/eslint-plugin": "^5.54.1",
"@typescript-eslint/parser": "^5.54.1",
"@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.2",
"acorn": "^8.8.1",
"acorn": "^8.8.2",
"acorn-import-assertions": "^1.8.0",
"acorn-jsx": "^5.3.2",
"acorn-walk": "^8.2.0",
Expand All @@ -97,15 +97,15 @@
"chokidar": "^3.5.3",
"colorette": "^2.0.19",
"concurrently": "^7.6.0",
"core-js": "^3.27.1",
"core-js": "^3.29.0",
"date-time": "^4.0.0",
"es5-shim": "^4.6.7",
"es6-shim": "^0.35.7",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-unicorn": "^45.0.2",
"eslint-plugin-unicorn": "^46.0.0",
"eslint-plugin-vue": "^9.9.0",
"fixturify": "^3.0.0",
"flru": "^1.0.2",
Expand All @@ -115,17 +115,17 @@
"husky": "^8.0.3",
"inquirer": "^9.1.4",
"is-reference": "^3.0.1",
"lint-staged": "^13.1.0",
"lint-staged": "^13.1.2",
"locate-character": "^2.0.5",
"magic-string": "^0.29.0",
"magic-string": "^0.30.0",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"pinia": "^2.0.29",
"prettier": "^2.8.3",
"pretty-bytes": "^6.0.0",
"pinia": "^2.0.33",
"prettier": "^2.8.4",
"pretty-bytes": "^6.1.0",
"pretty-ms": "^8.0.0",
"requirejs": "^2.3.6",
"rollup": "^3.16.0",
"rollup": "^3.18.0",
"rollup-plugin-license": "^3.0.1",
"rollup-plugin-string": "^3.0.0",
"rollup-plugin-thatworks": "^1.0.4",
Expand All @@ -134,12 +134,12 @@
"signal-exit": "^3.0.7",
"source-map": "^0.7.4",
"source-map-support": "^0.5.21",
"systemjs": "^6.13.0",
"terser": "^5.16.1",
"tslib": "^2.4.1",
"typescript": "^4.9.4",
"vitepress": "^1.0.0-alpha.44",
"vue": "^3.2.45",
"systemjs": "^6.14.0",
"terser": "^5.16.5",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vitepress": "^1.0.0-alpha.50",
"vue": "^3.2.47",
"weak-napi": "^2.0.2",
"yargs-parser": "^21.1.1"
},
Expand Down
6 changes: 4 additions & 2 deletions src/ast/NodeInteractions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ export const INTERACTION_ASSIGNED = 1;
export const INTERACTION_CALLED = 2;

export interface NodeInteractionAccessed {
args: null;
thisArg: ExpressionEntity | null;
type: typeof INTERACTION_ACCESSED;
}

export const NODE_INTERACTION_UNKNOWN_ACCESS: NodeInteractionAccessed = {
args: null,
thisArg: null,
type: INTERACTION_ACCESSED
};
Expand Down Expand Up @@ -49,9 +51,9 @@ export const NODE_INTERACTION_UNKNOWN_CALL: NodeInteractionCalled = {
withNew: false
};

// For tracking, called and assigned are uniquely determined by their .args
// while accessed is determined by .thisArg
export type NodeInteraction =
| NodeInteractionAccessed
| NodeInteractionAssigned
| NodeInteractionCalled;

export type NodeInteractionWithThisArgument = NodeInteraction & { thisArg: ExpressionEntity };
24 changes: 12 additions & 12 deletions src/ast/nodes/ArrayExpression.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext } from '../ExecutionContext';
import type {
NodeInteraction,
NodeInteractionCalled,
NodeInteractionWithThisArgument
} from '../NodeInteractions';
import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions';
import {
type ObjectPath,
type PathTracker,
Expand All @@ -24,16 +20,20 @@ export default class ArrayExpression extends NodeBase {
declare type: NodeType.tArrayExpression;
private objectEntity: ObjectEntity | null = null;

deoptimizePath(path: ObjectPath): void {
this.getObjectEntity().deoptimizePath(path);
}

deoptimizeThisOnInteractionAtPath(
interaction: NodeInteractionWithThisArgument,
deoptimizeArgumentsOnInteractionAtPath(
interaction: NodeInteraction,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.getObjectEntity().deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
this.getObjectEntity().deoptimizeArgumentsOnInteractionAtPath(
interaction,
path,
recursionTracker
);
}

deoptimizePath(path: ObjectPath): void {
this.getObjectEntity().deoptimizePath(path);
}

getLiteralValueAtPath(
Expand Down
26 changes: 7 additions & 19 deletions src/ast/nodes/CallExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,17 @@ import { renderCallArguments } from '../../utils/renderCallArguments';
import { type NodeRenderOptions, type RenderOptions } from '../../utils/renderHelpers';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type { NodeInteractionWithThisArgument } from '../NodeInteractions';
import { INTERACTION_CALLED } from '../NodeInteractions';
import {
EMPTY_PATH,
type PathTracker,
SHARED_RECURSION_TRACKER,
UNKNOWN_PATH
} from '../utils/PathTracker';
import { EMPTY_PATH, type PathTracker, SHARED_RECURSION_TRACKER } from '../utils/PathTracker';
import Identifier from './Identifier';
import MemberExpression from './MemberExpression';
import type * as NodeType from './NodeType';
import type SpreadElement from './SpreadElement';
import type Super from './Super';
import CallExpressionBase from './shared/CallExpressionBase';
import { type ExpressionEntity, UNKNOWN_RETURN_EXPRESSION } from './shared/Expression';
import { INCLUDE_PARAMETERS } from './shared/Node';
import type { ChainElement, ExpressionNode, IncludeChildren } from './shared/Node';
import { INCLUDE_PARAMETERS } from './shared/Node';

export default class CallExpression
extends CallExpressionBase
Expand Down Expand Up @@ -116,17 +110,11 @@ export default class CallExpression

protected applyDeoptimizations(): void {
this.deoptimized = true;
if (this.interaction.thisArg) {
this.callee.deoptimizeThisOnInteractionAtPath(
this.interaction as NodeInteractionWithThisArgument,
EMPTY_PATH,
SHARED_RECURSION_TRACKER
);
}
for (const argument of this.arguments) {
// This will make sure all properties of parameters behave as "unknown"
argument.deoptimizePath(UNKNOWN_PATH);
}
this.callee.deoptimizeArgumentsOnInteractionAtPath(
this.interaction,
EMPTY_PATH,
SHARED_RECURSION_TRACKER
);
this.context.requestTreeshakingPass();
}

Expand Down
24 changes: 10 additions & 14 deletions src/ast/nodes/ConditionalExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ import {
import { removeAnnotations } from '../../utils/treeshakeNode';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type {
NodeInteraction,
NodeInteractionCalled,
NodeInteractionWithThisArgument
} from '../NodeInteractions';
import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions';
import type { ObjectPath, PathTracker } from '../utils/PathTracker';
import { EMPTY_PATH, SHARED_RECURSION_TRACKER, UNKNOWN_PATH } from '../utils/PathTracker';
import type * as NodeType from './NodeType';
Expand All @@ -34,6 +30,15 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz
private isBranchResolutionAnalysed = false;
private usedBranch: ExpressionNode | null = null;

deoptimizeArgumentsOnInteractionAtPath(
interaction: NodeInteraction,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.consequent.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker);
this.alternate.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker);
}

deoptimizeCache(): void {
if (this.usedBranch !== null) {
const unusedBranch = this.usedBranch === this.consequent ? this.alternate : this.consequent;
Expand All @@ -55,15 +60,6 @@ export default class ConditionalExpression extends NodeBase implements Deoptimiz
}
}

deoptimizeThisOnInteractionAtPath(
interaction: NodeInteractionWithThisArgument,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.consequent.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
this.alternate.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
}

getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
Expand Down
22 changes: 9 additions & 13 deletions src/ast/nodes/Identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import { PureFunctionKey } from '../../utils/pureFunctions';
import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type {
NodeInteraction,
NodeInteractionCalled,
NodeInteractionWithThisArgument
} from '../NodeInteractions';
import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions';
import {
INTERACTION_ACCESSED,
INTERACTION_ASSIGNED,
Expand Down Expand Up @@ -102,6 +98,14 @@ export default class Identifier extends NodeBase implements PatternNode {
return [(this.variable = variable)];
}

deoptimizeArgumentsOnInteractionAtPath(
interaction: NodeInteraction,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.variable!.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker);
}

deoptimizePath(path: ObjectPath): void {
if (path.length === 0 && !this.scope.contains(this.name)) {
this.disallowImportReassignment();
Expand All @@ -111,14 +115,6 @@ export default class Identifier extends NodeBase implements PatternNode {
this.variable?.deoptimizePath(path);
}

deoptimizeThisOnInteractionAtPath(
interaction: NodeInteractionWithThisArgument,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.variable!.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
}

getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes/Literal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class Literal<T extends LiteralValue = LiteralValue> extends Node

private declare members: { [key: string]: MemberDescription };

deoptimizeThisOnInteractionAtPath(): void {}
deoptimizeArgumentsOnInteractionAtPath(): void {}

getLiteralValueAtPath(path: ObjectPath): LiteralValueOrUnknown {
if (
Expand Down
24 changes: 10 additions & 14 deletions src/ast/nodes/LogicalExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import {
import { removeAnnotations } from '../../utils/treeshakeNode';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext, InclusionContext } from '../ExecutionContext';
import type {
NodeInteraction,
NodeInteractionCalled,
NodeInteractionWithThisArgument
} from '../NodeInteractions';
import type { NodeInteraction, NodeInteractionCalled } from '../NodeInteractions';
import {
EMPTY_PATH,
type ObjectPath,
Expand Down Expand Up @@ -44,6 +40,15 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable
private isBranchResolutionAnalysed = false;
private usedBranch: ExpressionNode | null = null;

deoptimizeArgumentsOnInteractionAtPath(
interaction: NodeInteraction,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.left.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker);
this.right.deoptimizeArgumentsOnInteractionAtPath(interaction, path, recursionTracker);
}

deoptimizeCache(): void {
if (this.usedBranch) {
const unusedBranch = this.usedBranch === this.left ? this.right : this.left;
Expand All @@ -68,15 +73,6 @@ export default class LogicalExpression extends NodeBase implements Deoptimizable
}
}

deoptimizeThisOnInteractionAtPath(
interaction: NodeInteractionWithThisArgument,
path: ObjectPath,
recursionTracker: PathTracker
): void {
this.left.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
this.right.deoptimizeThisOnInteractionAtPath(interaction, path, recursionTracker);
}

getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
Expand Down

0 comments on commit cff3bbc

Please sign in to comment.