@samitouri / QOS-React-2 / commits / db378d39f3

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