@samitouri / QOS-React / commits / 3af905d954

[compiler] Fix issue with macro arguments being outlined

Summary: Fixes issue documented by #30435. We change the pipeline order so that outlining comes after tracking macro operands, and any function that is referenced in a macro will now not be outlined. ghstack-source-id: f731ad65c8b84db3fc5f3a2ff3a6986112765963 Pull Request resolved: https://github.com/facebook/react/pull/30587

Mike Vitousek committed Aug 2, 2024 at 14:55 UTC 3af905d95448d582cbd62fe6d41bd976ce9787ea
7 files changed +89 -43
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+9 -9
@@ -56,7 +56,7 @@ import {
56 flattenReactiveLoops,
57 flattenScopesWithHooksOrUse,
58 inferReactiveScopeVariables,
59 - memoizeFbtOperandsInSameScope,
59 + memoizeFbtAndMacroOperandsInSameScope,
60 mergeOverlappingReactiveScopes,
61 mergeReactiveScopesThatInvalidateTogether,
62 promoteUsedTemporaries,
@@ -243,8 +243,15 @@ function* runWithEnvironment(
243 inferReactiveScopeVariables(hir);
244 yield log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
245
246 + const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
247 + yield log({
248 + kind: 'hir',
249 + name: 'MemoizeFbtAndMacroOperandsInSameScope',
250 + value: hir,
251 + });
252 +
253 if (env.config.enableFunctionOutlining) {
247 - outlineFunctions(hir);
254 + outlineFunctions(hir, fbtOperands);
255 yield log({kind: 'hir', name: 'OutlineFunctions', value: hir});
256 }
257
@@ -262,13 +269,6 @@ function* runWithEnvironment(
269 value: hir,
270 });
271
265 - const fbtOperands = memoizeFbtOperandsInSameScope(hir);
266 - yield log({
267 - kind: 'hir',
268 - name: 'MemoizeFbtAndMacroOperandsInSameScope',
269 - value: hir,
270 - });
271 -
272 if (env.config.enableReactiveScopesInHIR) {
273 pruneUnusedLabelsHIR(hir);
274 yield log({
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+5
@@ -485,6 +485,11 @@ export function parseConfigPragma(pragma: string): EnvironmentConfig {
485 continue;
486 }
487
488 + if (key === 'customMacros' && val) {
489 + maybeConfig[key] = [val];
490 + continue;
491 + }
492 +
493 if (typeof defaultConfig[key as keyof EnvironmentConfig] !== 'boolean') {
494 // skip parsing non-boolean properties
495 continue;
compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineFunctions.ts
+9 -6
@@ -5,27 +5,30 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {HIRFunction} from '../HIR';
8 +import {HIRFunction, IdentifierId} from '../HIR';
9
10 -export function outlineFunctions(fn: HIRFunction): void {
10 +export function outlineFunctions(
11 + fn: HIRFunction,
12 + fbtOperands: Set<IdentifierId>,
13 +): void {
14 for (const [, block] of fn.body.blocks) {
15 for (const instr of block.instructions) {
13 - const {value} = instr;
16 + const {value, lvalue} = instr;
17
18 if (
19 value.kind === 'FunctionExpression' ||
20 value.kind === 'ObjectMethod'
21 ) {
22 // Recurse in case there are inner functions which can be outlined
20 - outlineFunctions(value.loweredFunc.func);
23 + outlineFunctions(value.loweredFunc.func, fbtOperands);
24 }
22 -
25 if (
26 value.kind === 'FunctionExpression' &&
27 value.loweredFunc.dependencies.length === 0 &&
28 value.loweredFunc.func.context.length === 0 &&
29 // TODO: handle outlining named functions
28 - value.loweredFunc.func.id === null
30 + value.loweredFunc.func.id === null &&
31 + !fbtOperands.has(lvalue.identifier.id)
32 ) {
33 const loweredFunc = value.loweredFunc.func;
34
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/index.ts
+1 -1
@@ -16,7 +16,7 @@ export {extractScopeDeclarationsFromDestructuring} from './ExtractScopeDeclarati
16 export {flattenReactiveLoops} from './FlattenReactiveLoops';
17 export {flattenScopesWithHooksOrUse} from './FlattenScopesWithHooksOrUse';
18 export {inferReactiveScopeVariables} from './InferReactiveScopeVariables';
19 -export {memoizeFbtAndMacroOperandsInSameScope as memoizeFbtOperandsInSameScope} from './MemoizeFbtAndMacroOperandsInSameScope';
19 +export {memoizeFbtAndMacroOperandsInSameScope} from './MemoizeFbtAndMacroOperandsInSameScope';
20 export {mergeOverlappingReactiveScopes} from './MergeOverlappingReactiveScopes';
21 export {mergeReactiveScopesThatInvalidateTogether} from './MergeReactiveScopesThatInvalidateTogether';
22 export {printReactiveFunction} from './PrintReactiveFunction';
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.idx-outlining.expect.md deleted
-27
@@ -1,27 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -import idx from 'idx';
6 -
7 -function Component(props) {
8 - // the lambda should not be outlined
9 - const groupName = idx(props, _ => _.group.label);
10 - return <div>{groupName}</div>;
11 -}
12 -
13 -export const FIXTURE_ENTRYPOINT = {
14 - fn: Component,
15 - params: [{}],
16 -};
17 -
18 -```
19 -
20 -
21 -## Error
22 -
23 -```
24 -The second argument supplied to `idx` must be an arrow function. (This is an error on an internal node. Probably an internal error.)
25 -```
26 -
27 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/idx-no-outlining.expect.md new
+64
@@ -0,0 +1,64 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @customMacros(idx)
6 +import idx from 'idx';
7 +
8 +function Component(props) {
9 + // the lambda should not be outlined
10 + const groupName = idx(props, _ => _.group.label);
11 + return <div>{groupName}</div>;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{}],
17 +};
18 +
19 +```
20 +
21 +## Code
22 +
23 +```javascript
24 +import { c as _c } from "react/compiler-runtime"; // @customMacros(idx)
25 +
26 +function Component(props) {
27 + var _ref2;
28 + const $ = _c(4);
29 + let t0;
30 + if ($[0] !== props) {
31 + var _ref;
32 +
33 + t0 =
34 + (_ref = props) != null
35 + ? (_ref = _ref.group) != null
36 + ? _ref.label
37 + : _ref
38 + : _ref;
39 + $[0] = props;
40 + $[1] = t0;
41 + } else {
42 + t0 = $[1];
43 + }
44 + const groupName = t0;
45 + let t1;
46 + if ($[2] !== groupName) {
47 + t1 = <div>{groupName}</div>;
48 + $[2] = groupName;
49 + $[3] = t1;
50 + } else {
51 + t1 = $[3];
52 + }
53 + return t1;
54 +}
55 +
56 +export const FIXTURE_ENTRYPOINT = {
57 + fn: Component,
58 + params: [{}],
59 +};
60 +
61 +```
62 +
63 +### Eval output
64 +(kind: ok) <div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/idx-no-outlining.js renamed
+1
@@ -1,3 +1,4 @@
1 +// @customMacros(idx)
2 import idx from 'idx';
3
4 function Component(props) {