Skip to content

Commit 8241eec

Browse files
committed
Update on "compiler: Known hooks/nonescaping scopes dont count as pruned"
There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope: * Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them. * Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case. I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement. [ghstack-poisoned]
1 parent 21724a5 commit 8241eec

File tree

3 files changed

+83
-44
lines changed

3 files changed

+83
-44
lines changed

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -331,29 +331,8 @@ class CountMemoBlockVisitor extends ReactiveFunctionVisitor<void> {
331331
scopeBlock: PrunedReactiveScopeBlock,
332332
state: void
333333
): void {
334-
let isHookOnlyMemoBlock = false;
335-
if (
336-
scopeBlock.instructions.length === 1 &&
337-
scopeBlock.instructions[0].kind === "instruction"
338-
) {
339-
const instr = scopeBlock.instructions[0]!.instruction;
340-
if (
341-
instr.value.kind === "MethodCall" ||
342-
instr.value.kind === "CallExpression"
343-
) {
344-
const callee =
345-
instr.value.kind === "MethodCall"
346-
? instr.value.property
347-
: instr.value.callee;
348-
if (getHookKind(this.env, callee.identifier) != null) {
349-
isHookOnlyMemoBlock = true;
350-
}
351-
}
352-
}
353-
if (!isHookOnlyMemoBlock) {
354-
this.prunedMemoBlocks += 1;
355-
this.prunedMemoValues += scopeBlock.scope.declarations.size;
356-
}
334+
this.prunedMemoBlocks += 1;
335+
this.prunedMemoValues += scopeBlock.scope.declarations.size;
357336
this.traversePrunedScope(scopeBlock, state);
358337
}
359338
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/log-pruned-memoization.expect.md

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33

44
```javascript
55
// @logger
6-
import { useState } from "react";
7-
import { identity, makeObject_Primitives, useHook } from "shared-runtime";
6+
import { createContext, use, useState } from "react";
7+
import {
8+
Stringify,
9+
identity,
10+
makeObject_Primitives,
11+
useHook,
12+
} from "shared-runtime";
813

914
function Component() {
15+
const w = use(Context);
16+
1017
// The scopes for x and x2 are interleaved, so this is one scope with two values
1118
const x = makeObject_Primitives();
1219
const x2 = makeObject_Primitives();
@@ -26,11 +33,21 @@ function Component() {
2633
}
2734

2835
// Overall we expect two pruned scopes (for x+x2, and obj), with 3 pruned scope values.
29-
return [x, x2, y, z];
36+
return <Stringify items={[w, x, x2, y, z]} />;
37+
}
38+
39+
const Context = createContext();
40+
41+
function Wrapper() {
42+
return (
43+
<Context value={42}>
44+
<Component />
45+
</Context>
46+
);
3047
}
3148

3249
export const FIXTURE_ENTRYPOINT = {
33-
fn: Component,
50+
fn: Wrapper,
3451
params: [{}],
3552
};
3653

@@ -40,11 +57,17 @@ export const FIXTURE_ENTRYPOINT = {
4057

4158
```javascript
4259
import { c as _c } from "react/compiler-runtime"; // @logger
43-
import { useState } from "react";
44-
import { identity, makeObject_Primitives, useHook } from "shared-runtime";
60+
import { createContext, use, useState } from "react";
61+
import {
62+
Stringify,
63+
identity,
64+
makeObject_Primitives,
65+
useHook,
66+
} from "shared-runtime";
4567

4668
function Component() {
47-
const $ = _c(5);
69+
const $ = _c(6);
70+
const w = use(Context);
4871

4972
const x = makeObject_Primitives();
5073
const x2 = makeObject_Primitives();
@@ -65,20 +88,39 @@ function Component() {
6588
z = $[0];
6689
}
6790
let t0;
68-
if ($[1] !== x || $[2] !== x2 || $[3] !== y) {
69-
t0 = [x, x2, y, z];
70-
$[1] = x;
71-
$[2] = x2;
72-
$[3] = y;
73-
$[4] = t0;
91+
if ($[1] !== w || $[2] !== x || $[3] !== x2 || $[4] !== y) {
92+
t0 = <Stringify items={[w, x, x2, y, z]} />;
93+
$[1] = w;
94+
$[2] = x;
95+
$[3] = x2;
96+
$[4] = y;
97+
$[5] = t0;
98+
} else {
99+
t0 = $[5];
100+
}
101+
return t0;
102+
}
103+
104+
const Context = createContext();
105+
106+
function Wrapper() {
107+
const $ = _c(1);
108+
let t0;
109+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
110+
t0 = (
111+
<Context value={42}>
112+
<Component />
113+
</Context>
114+
);
115+
$[0] = t0;
74116
} else {
75-
t0 = $[4];
117+
t0 = $[0];
76118
}
77119
return t0;
78120
}
79121

80122
export const FIXTURE_ENTRYPOINT = {
81-
fn: Component,
123+
fn: Wrapper,
82124
params: [{}],
83125
};
84126

@@ -87,8 +129,9 @@ export const FIXTURE_ENTRYPOINT = {
87129
## Logs
88130

89131
```
90-
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":121},"end":{"line":26,"column":1,"index":813},"filename":"log-pruned-memoization.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":2,"prunedMemoValues":3}
132+
{"kind":"CompileSuccess","fnLoc":{"start":{"line":10,"column":0,"index":161},"end":{"line":33,"column":1,"index":905},"filename":"log-pruned-memoization.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":2,"prunedMemoValues":3}
133+
{"kind":"CompileSuccess","fnLoc":{"start":{"line":37,"column":0,"index":941},"end":{"line":43,"column":1,"index":1039},"filename":"log-pruned-memoization.ts"},"fnName":"Wrapper","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
91134
```
92135
93136
### Eval output
94-
(kind: ok) [{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},[{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true}]]
137+
(kind: ok) <div>{"items":[42,{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},[{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true},{"a":0,"b":"value1","c":true}]]}</div>

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/log-pruned-memoization.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
// @logger
2-
import { useState } from "react";
3-
import { identity, makeObject_Primitives, useHook } from "shared-runtime";
2+
import { createContext, use, useState } from "react";
3+
import {
4+
Stringify,
5+
identity,
6+
makeObject_Primitives,
7+
useHook,
8+
} from "shared-runtime";
49

510
function Component() {
11+
const w = use(Context);
12+
613
// The scopes for x and x2 are interleaved, so this is one scope with two values
714
const x = makeObject_Primitives();
815
const x2 = makeObject_Primitives();
@@ -22,10 +29,20 @@ function Component() {
2229
}
2330

2431
// Overall we expect two pruned scopes (for x+x2, and obj), with 3 pruned scope values.
25-
return [x, x2, y, z];
32+
return <Stringify items={[w, x, x2, y, z]} />;
33+
}
34+
35+
const Context = createContext();
36+
37+
function Wrapper() {
38+
return (
39+
<Context value={42}>
40+
<Component />
41+
</Context>
42+
);
2643
}
2744

2845
export const FIXTURE_ENTRYPOINT = {
29-
fn: Component,
46+
fn: Wrapper,
3047
params: [{}],
3148
};

0 commit comments

Comments
 (0)