Constant propagation for globals
This PR updates ConstantPropagation to support propagating global references: ```javascript // Before const x = Math; foo(x); // After foo(Math); ``` This is a generally useful optimization but also helps with a subset of cases around JSX element tags, which are frequently globals.
Joe Savona committed
Apr 20, 2023 at 16:48 UTC
db378d39f3091e4b9302e74b1c6651112d45888b
4 files changed
+33
-11
compiler/forget/src/HIR/HIR.ts
+7
-1
@@ -678,7 +678,7 @@ export type InstructionValue =
678
property: Place;
679
loc: SourceLocation;
680
}
681
- | { kind: "LoadGlobal"; name: string; loc: SourceLocation }
681
+ | LoadGlobal
682
| FunctionExpression
683
| {
684
kind: "TaggedTemplateExpression";
@@ -746,6 +746,12 @@ export type Primitive = {
746
loc: SourceLocation;
747
};
748
749
+export type LoadGlobal = {
750
+ kind: "LoadGlobal";
751
+ name: string;
752
+ loc: SourceLocation;
753
+};
754
+
755
/*
756
* Range in which an identifier is mutable. Start and End refer to Instruction.id.
757
*
compiler/forget/src/Optimization/ConstantPropagation.ts
+22
-5
@@ -13,6 +13,7 @@ import {
13
IdentifierId,
14
Instruction,
15
InstructionValue,
16
+ LoadGlobal,
17
markInstructionIds,
18
markPredecessors,
19
mergeConsecutiveBlocks,
@@ -91,7 +92,7 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
92
// Note that this analysis uses a single-pass only, so it will never fill in
93
// phi values for blocks that have a back-edge.
94
for (const phi of block.phis) {
94
- let value: Primitive | null = null;
95
+ let value: Primitive | LoadGlobal | null = null;
96
for (const [, operand] of phi.operands) {
97
const operandValue = constants.get(operand.id) ?? null;
98
if (operandValue === null) {
@@ -100,7 +101,12 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
101
}
102
if (value === null) {
103
value = operandValue;
103
- } else if (operandValue.value !== value.value) {
104
+ } else if (
105
+ operandValue.kind !== value.kind ||
106
+ (operandValue.kind === "Primitive" &&
107
+ value.kind === "Primitive" &&
108
+ operandValue.value !== value.value)
109
+ ) {
110
value = null;
111
break;
112
}
@@ -125,7 +131,7 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
131
switch (terminal.kind) {
132
case "if": {
133
const testValue = read(constants, terminal.test);
128
- if (testValue !== null) {
134
+ if (testValue !== null && testValue.kind === "Primitive") {
135
hasChanges = true;
136
const targetBlockId = testValue.value
137
? terminal.consequent
@@ -158,10 +164,14 @@ function evaluateInstruction(
164
case "Primitive": {
165
return value;
166
}
167
+ case "LoadGlobal": {
168
+ return value;
169
+ }
170
case "ComputedLoad": {
171
const property = read(constants, value.property);
172
if (
173
property !== null &&
174
+ property.kind === "Primitive" &&
175
typeof property.value === "string" &&
176
isValidIdentifier(property.value)
177
) {
@@ -188,6 +198,7 @@ function evaluateInstruction(
198
const property = read(constants, value.property);
199
if (
200
property !== null &&
201
+ property.kind === "Primitive" &&
202
typeof property.value === "string" &&
203
isValidIdentifier(property.value)
204
) {
@@ -205,7 +216,12 @@ function evaluateInstruction(
216
case "BinaryExpression": {
217
const lhsValue = read(constants, value.left);
218
const rhsValue = read(constants, value.right);
208
- if (lhsValue !== null && rhsValue !== null) {
219
+ if (
220
+ lhsValue !== null &&
221
+ rhsValue !== null &&
222
+ lhsValue.kind === "Primitive" &&
223
+ rhsValue.kind === "Primitive"
224
+ ) {
225
const lhs = lhsValue.value;
226
const rhs = rhsValue.value;
227
let result: Primitive | null = null;
@@ -289,6 +305,7 @@ function evaluateInstruction(
305
const objectValue = read(constants, value.object);
306
if (objectValue !== null) {
307
if (
308
+ objectValue.kind === "Primitive" &&
309
typeof objectValue.value === "string" &&
310
value.property === "length"
311
) {
@@ -332,5 +349,5 @@ function read(constants: Constants, place: Place): Constant | null {
349
return constants.get(place.identifier.id) ?? null;
350
}
351
335
-type Constant = Primitive;
352
+type Constant = Primitive | LoadGlobal;
353
type Constants = Map<IdentifierId, Constant>;
compiler/forget/src/__tests__/fixtures/compiler/infer-global-object.expect.md
+3
-3
@@ -47,14 +47,14 @@ function Component(props) {
47
t1 = $[4];
48
}
49
const primitiveVal1 = t1;
50
- const primitiveVal2 = Infinity;
50
+
51
const primitiveVal3 = globaThis.globalThis.NaN;
52
53
- foo(primitiveVal1, primitiveVal2, primitiveVal3);
53
+ foo(primitiveVal1, Infinity, primitiveVal3);
54
const c_5 = $[5] !== primitiveVal1;
55
let t2;
56
if (c_5) {
57
- t2 = { primitiveVal1, primitiveVal2, primitiveVal3 };
57
+ t2 = { primitiveVal1, primitiveVal2: Infinity, primitiveVal3 };
58
$[5] = primitiveVal1;
59
$[6] = t2;
60
} else {
compiler/forget/src/__tests__/fixtures/compiler/useRef-rename-mutable.expect.md
+1
-2
@@ -14,8 +14,7 @@ function Component(props) {
14
15
```javascript
16
function Component(props) {
17
- const x = useRef;
18
- const ref = x(null);
17
+ const ref = useRef(null);
18
return ref.current;
19
}
20