@samitouri / QOS-React-1 / commits / ebbada309d

MemoizeFbtOperandsInSameScope operates on HIR

Moves this pass to operate against HIRFunction instead of ReactiveFunction, no logic changes.

Joe Savona committed Mar 6, 2024 at 14:02 UTC ebbada309db9094cae1c0a2a4e5faffae0422318
2 files changed +80 -86
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+7 -7
@@ -211,6 +211,13 @@ function* runWithEnvironment(
211 value: hir,
212 });
213
214 + memoizeFbtOperandsInSameScope(hir);
215 + yield log({
216 + kind: "hir",
217 + name: "MemoizeFbtOperandsInSameScope",
218 + value: hir,
219 + });
220 +
221 const reactiveFunction = buildReactiveFunction(hir);
222 yield log({
223 kind: "reactive",
@@ -225,13 +232,6 @@ function* runWithEnvironment(
232 value: reactiveFunction,
233 });
234
228 - memoizeFbtOperandsInSameScope(reactiveFunction);
229 - yield log({
230 - kind: "reactive",
231 - name: "MemoizeFbtOperandsInSameScope",
232 - value: reactiveFunction,
233 - });
234 -
235 alignReactiveScopesToBlockScopes(reactiveFunction);
236 yield log({
237 kind: "reactive",
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/MemoizeFbtOperandsInSameScope.ts
+73 -79
@@ -6,18 +6,13 @@
6 */
7
8 import {
9 + HIRFunction,
10 IdentifierId,
11 makeInstructionId,
12 Place,
12 - ReactiveFunction,
13 - ReactiveInstruction,
13 ReactiveValue,
14 } from "../HIR";
16 -import {
17 - eachReactiveValueOperand,
18 - ReactiveFunctionVisitor,
19 - visitReactiveFunction,
20 -} from "./visitors";
15 +import { eachReactiveValueOperand } from "./visitors";
16
17 /*
18 * This pass supports the `fbt` translation system (https://facebook.github.io/fbt/).
@@ -33,12 +28,12 @@ import {
28 * Note that this still allows the props/arguments of `<fbt:param>`/`fbt.param()`
29 * to be independently memoized
30 */
36 -export function memoizeFbtOperandsInSameScope(fn: ReactiveFunction): void {
37 - const transform = new Transform();
31 +export function memoizeFbtOperandsInSameScope(fn: HIRFunction): void {
32 + const fbtValues: Set<IdentifierId> = new Set();
33 while (true) {
39 - let size = transform.fbtValues.size;
40 - visitReactiveFunction(fn, transform, undefined);
41 - if (size === transform.fbtValues.size) {
34 + let size = fbtValues.size;
35 + visit(fn, fbtValues);
36 + if (size === fbtValues.size) {
37 break;
38 }
39 }
@@ -47,80 +42,79 @@ export function memoizeFbtOperandsInSameScope(fn: ReactiveFunction): void {
42 export const FBT_TAGS: Set<string> = new Set(["fbt", "fbt:param"]);
43 export const SINGLE_CHILD_FBT_TAGS: Set<string> = new Set(["fbt:param"]);
44
50 -class Transform extends ReactiveFunctionVisitor<void> {
51 - /*
52 - * Values that represent *potential* references of `fbt` as a JSX tag name
53 - * or as a callee.
54 - */
55 - fbtValues: Set<IdentifierId> = new Set();
56 -
57 - override visitInstruction(
58 - instruction: ReactiveInstruction,
59 - _state: void
60 - ): void {
61 - const { lvalue, value } = instruction;
62 - if (lvalue === null) {
63 - return;
64 - }
65 - if (
66 - value.kind === "Primitive" &&
67 - typeof value.value === "string" &&
68 - FBT_TAGS.has(value.value)
69 - ) {
70 - /*
71 - * We don't distinguish between tag names and strings, so record
72 - * all `fbt` string literals in case they are used as a jsx tag.
73 - */
74 - this.fbtValues.add(lvalue.identifier.id);
75 - } else if (value.kind === "LoadGlobal" && FBT_TAGS.has(value.name)) {
76 - // Record references to `fbt` as a global
77 - this.fbtValues.add(lvalue.identifier.id);
78 - } else if (isFbtCallExpression(this.fbtValues, value)) {
79 - const fbtScope = lvalue.identifier.scope;
80 - if (fbtScope === null) {
81 - return;
82 - }
83 -
84 - /*
85 - * if the JSX element's tag was `fbt`, mark all its operands
86 - * to ensure that they end up in the same scope as the jsx element
87 - * itself.
88 - */
89 - for (const operand of eachReactiveValueOperand(value)) {
90 - operand.identifier.scope = fbtScope;
91 -
92 - // Expand the jsx element's range to account for its operands
93 - fbtScope.range.start = makeInstructionId(
94 - Math.min(fbtScope.range.start, operand.identifier.mutableRange.start)
95 - );
96 - }
97 - } else if (
98 - isFbtJsxExpression(this.fbtValues, value) ||
99 - isFbtJsxChild(this.fbtValues, lvalue, value)
100 - ) {
101 - const fbtScope = lvalue.identifier.scope;
102 - if (fbtScope === null) {
45 +function visit(fn: HIRFunction, fbtValues: Set<IdentifierId>): void {
46 + for (const [, block] of fn.body.blocks) {
47 + for (const instruction of block.instructions) {
48 + const { lvalue, value } = instruction;
49 + if (lvalue === null) {
50 return;
51 }
52 + if (
53 + value.kind === "Primitive" &&
54 + typeof value.value === "string" &&
55 + FBT_TAGS.has(value.value)
56 + ) {
57 + /*
58 + * We don't distinguish between tag names and strings, so record
59 + * all `fbt` string literals in case they are used as a jsx tag.
60 + */
61 + fbtValues.add(lvalue.identifier.id);
62 + } else if (value.kind === "LoadGlobal" && FBT_TAGS.has(value.name)) {
63 + // Record references to `fbt` as a global
64 + fbtValues.add(lvalue.identifier.id);
65 + } else if (isFbtCallExpression(fbtValues, value)) {
66 + const fbtScope = lvalue.identifier.scope;
67 + if (fbtScope === null) {
68 + return;
69 + }
70
106 - /*
107 - * if the JSX element's tag was `fbt`, mark all its operands
108 - * to ensure that they end up in the same scope as the jsx element
109 - * itself.
110 - */
111 - for (const operand of eachReactiveValueOperand(value)) {
112 - operand.identifier.scope = fbtScope;
71 + /*
72 + * if the JSX element's tag was `fbt`, mark all its operands
73 + * to ensure that they end up in the same scope as the jsx element
74 + * itself.
75 + */
76 + for (const operand of eachReactiveValueOperand(value)) {
77 + operand.identifier.scope = fbtScope;
78
114 - // Expand the jsx element's range to account for its operands
115 - fbtScope.range.start = makeInstructionId(
116 - Math.min(fbtScope.range.start, operand.identifier.mutableRange.start)
117 - );
79 + // Expand the jsx element's range to account for its operands
80 + fbtScope.range.start = makeInstructionId(
81 + Math.min(
82 + fbtScope.range.start,
83 + operand.identifier.mutableRange.start
84 + )
85 + );
86 + }
87 + } else if (
88 + isFbtJsxExpression(fbtValues, value) ||
89 + isFbtJsxChild(fbtValues, lvalue, value)
90 + ) {
91 + const fbtScope = lvalue.identifier.scope;
92 + if (fbtScope === null) {
93 + return;
94 + }
95
96 /*
120 - * NOTE: we add the operands as fbt values so that they are also
121 - * grouped with this expression
97 + * if the JSX element's tag was `fbt`, mark all its operands
98 + * to ensure that they end up in the same scope as the jsx element
99 + * itself.
100 */
123 - this.fbtValues.add(operand.identifier.id);
101 + for (const operand of eachReactiveValueOperand(value)) {
102 + operand.identifier.scope = fbtScope;
103 +
104 + // Expand the jsx element's range to account for its operands
105 + fbtScope.range.start = makeInstructionId(
106 + Math.min(
107 + fbtScope.range.start,
108 + operand.identifier.mutableRange.start
109 + )
110 + );
111 +
112 + /*
113 + * NOTE: we add the operands as fbt values so that they are also
114 + * grouped with this expression
115 + */
116 + fbtValues.add(operand.identifier.id);
117 + }
118 }
119 }
120 }