Skip to content

Commit 13a5a77

Browse files
committed
Update on "[compiler] Optimize emission in normal (non-value) blocks"
In #29863 I tried to find a clean way to share code for emitting instructions between value blocks and regular blocks. The catch is that value blocks have special meaning for their final instruction — that's the value of the block — so reordering can't change the last instruction. However, in finding a clean way to share code for these two categories of code, i also inadvertently reduced the effectiveness of the optimization. This PR updates to use different strategies for these two kinds of blocks: value blocks use the code from #29863 where we first emit all non-reorderable instructions in their original order, _then_ try to emit reorderable values. The reason this is suboptimal, though, is that we want to move instructions closer to their dependencies so that they can invalidate (merge) together. Emitting the reorderable values last prevents this. So for normal blocks, we now emit terminal operands first. This will invariably cause _some_ of the non-reorderable instructions to be emitted, but it will intersperse reoderable instructions in between, right after their dependencies. This maximizes our ability to merge scopes. I think the complexity cost of two strategies is worth the benefit, though i still need to double-check all the output changes. [ghstack-poisoned]
2 parents 11a4916 + 461435e commit 13a5a77

13 files changed

+99
-102
lines changed

compiler/packages/babel-plugin-react-compiler/src/Optimization/InstructionReordering.ts

+9-14
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
eachInstructionValueOperand,
2727
eachTerminalOperand,
2828
} from "../HIR/visitors";
29-
import { mayAllocate } from "../ReactiveScopes/InferReactiveScopeVariables";
3029
import { getOrInsertWith } from "../Utils/utils";
3130

3231
/**
@@ -93,6 +92,7 @@ type Nodes = Map<IdentifierId, Node>;
9392
type Node = {
9493
instruction: Instruction | null;
9594
dependencies: Set<IdentifierId>;
95+
reorderability: Reorderability;
9696
depth: number | null;
9797
};
9898

@@ -173,21 +173,23 @@ function reorderBlock(
173173
for (const instr of block.instructions) {
174174
const { lvalue, value } = instr;
175175
// Get or create a node for this lvalue
176+
const reorderability = getReorderability(instr, references);
176177
const node = getOrInsertWith(
177178
locals,
178179
lvalue.identifier.id,
179180
() =>
180181
({
181182
instruction: instr,
182183
dependencies: new Set(),
184+
reorderability,
183185
depth: null,
184186
}) as Node
185187
);
186188
/**
187189
* Ensure non-reoderable instructions have their order retained by
188190
* adding explicit dependencies to the previous such instruction.
189191
*/
190-
if (getReoderability(instr, references) === Reorderability.Nonreorderable) {
192+
if (reorderability === Reorderability.Nonreorderable) {
191193
if (previous !== null) {
192194
node.dependencies.add(previous);
193195
}
@@ -298,9 +300,7 @@ function reorderBlock(
298300
continue;
299301
}
300302
CompilerError.invariant(
301-
node.instruction != null &&
302-
getReoderability(node.instruction, references) ===
303-
Reorderability.Reorderable,
303+
node.reorderability === Reorderability.Reorderable,
304304
{
305305
reason: `Expected all remaining instructions to be reorderable`,
306306
loc: node.instruction?.loc ?? block.terminal.loc,
@@ -355,11 +355,7 @@ function reorderBlock(
355355
if (node === undefined) {
356356
continue;
357357
}
358-
if (
359-
node.instruction !== null &&
360-
getReoderability(node.instruction, references) ===
361-
Reorderability.Reorderable
362-
) {
358+
if (node.reorderability === Reorderability.Reorderable) {
363359
DEBUG && console.log(`save shared: $${id}`);
364360
shared.set(id, node);
365361
} else {
@@ -383,8 +379,7 @@ function getDepth(env: Environment, nodes: Nodes, id: IdentifierId): number {
383379
return node.depth;
384380
}
385381
node.depth = 0; // in case of cycles
386-
let depth =
387-
node.instruction != null && mayAllocate(env, node.instruction) ? 1 : 0;
382+
let depth = node.reorderability === Reorderability.Reorderable ? 0 : 1;
388383
for (const dep of node.dependencies) {
389384
depth += getDepth(env, nodes, dep);
390385
}
@@ -419,7 +414,7 @@ function print(
419414
print(env, locals, shared, seen, dep, depth + 1);
420415
}
421416
console.log(
422-
`${"| ".repeat(depth)}$${id} ${printNode(node)} deps=[${deps.map((x) => `$${x}`).join(", ")}]`
417+
`${"| ".repeat(depth)}$${id} ${printNode(node)} deps=[${deps.map((x) => `$${x}`).join(", ")}] depth=${node.depth}`
423418
);
424419
}
425420

@@ -470,7 +465,7 @@ enum Reorderability {
470465
Reorderable,
471466
Nonreorderable,
472467
}
473-
function getReoderability(
468+
function getReorderability(
474469
instr: Instruction,
475470
references: References
476471
): Reorderability {

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,7 @@ export function isMutable({ id }: Instruction, place: Place): boolean {
186186
return id >= range.start && id < range.end;
187187
}
188188

189-
export function mayAllocate(
190-
env: Environment,
191-
instruction: Instruction
192-
): boolean {
189+
function mayAllocate(env: Environment, instruction: Instruction): boolean {
193190
const { value } = instruction;
194191
switch (value.kind) {
195192
case "Destructure": {

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-no-whitespace-btw-text-and-param.expect.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ import fbt from "fbt";
2929
const _ = fbt;
3030
function Component(t0) {
3131
const $ = _c(2);
32+
const { value } = t0;
3233
let t1;
33-
if ($[0] !== t0) {
34-
const { value } = t0;
35-
34+
if ($[0] !== value) {
3635
t1 = fbt._(
3736
"Before text{paramName}After text",
3837
[fbt._param("paramName", value)],
3938
{ hk: "aKEGX" },
4039
);
41-
$[0] = t0;
40+
$[0] = value;
4241
$[1] = t1;
4342
} else {
4443
t1 = $[1];

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-preserve-whitespace.expect.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ import fbt from "fbt";
3030
const _ = fbt;
3131
function Component(t0) {
3232
const $ = _c(2);
33+
const { value } = t0;
3334
let t1;
34-
if ($[0] !== t0) {
35-
const { value } = t0;
36-
35+
if ($[0] !== value) {
3736
t1 = fbt._(
3837
"Before text {paramName}",
3938
[
@@ -45,7 +44,7 @@ function Component(t0) {
4544
],
4645
{ hk: "3z5SVE" },
4746
);
48-
$[0] = t0;
47+
$[0] = value;
4948
$[1] = t1;
5049
} else {
5150
t1 = $[1];

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-single-space-btw-param-and-text.expect.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ import fbt from "fbt";
2929
const _ = fbt;
3030
function Component(t0) {
3131
const $ = _c(2);
32+
const { value } = t0;
3233
let t1;
33-
if ($[0] !== t0) {
34-
const { value } = t0;
35-
34+
if ($[0] !== value) {
3635
t1 = fbt._(
3736
"Before text {paramName} after text",
3837
[fbt._param("paramName", value)],
3938
{ hk: "26pxNm" },
4039
);
41-
$[0] = t0;
40+
$[0] = value;
4241
$[1] = t1;
4342
} else {
4443
t1 = $[1];

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-whitespace-around-param-value.expect.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ import fbt from "fbt";
2929
const _ = fbt;
3030
function Component(t0) {
3131
const $ = _c(2);
32+
const { value } = t0;
3233
let t1;
33-
if ($[0] !== t0) {
34-
const { value } = t0;
35-
34+
if ($[0] !== value) {
3635
t1 = fbt._(
3736
"Before text {paramName} after text",
3837
[fbt._param("paramName", value)],
3938
{ hk: "26pxNm" },
4039
);
41-
$[0] = t0;
40+
$[0] = value;
4241
$[1] = t1;
4342
} else {
4443
t1 = $[1];

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbt-whitespace-within-text.expect.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ import fbt from "fbt";
3131
const _ = fbt;
3232
function Component(t0) {
3333
const $ = _c(2);
34+
const { value } = t0;
3435
let t1;
35-
if ($[0] !== t0) {
36-
const { value } = t0;
37-
36+
if ($[0] !== value) {
3837
t1 = fbt._(
3938
"Before text {paramName} after text more text and more and more and more and more and more and more and more and more and blah blah blah blah",
4039
[fbt._param("paramName", value)],
4140
{ hk: "24ZPpO" },
4241
);
43-
$[0] = t0;
42+
$[0] = value;
4443
$[1] = t1;
4544
} else {
4645
t1 = $[1];

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/fbtparam-with-jsx-element-content.expect.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ import { c as _c } from "react/compiler-runtime";
2929
import fbt from "fbt";
3030

3131
function Component(t0) {
32-
const $ = _c(2);
32+
const $ = _c(4);
33+
const { name, data, icon } = t0;
3334
let t1;
34-
if ($[0] !== t0) {
35-
const { name, data, icon } = t0;
36-
35+
if ($[0] !== data || $[1] !== icon || $[2] !== name) {
3736
t1 = (
3837
<Text type="body4">
3938
{fbt._(
@@ -62,10 +61,12 @@ function Component(t0) {
6261
)}
6362
</Text>
6463
);
65-
$[0] = t0;
66-
$[1] = t1;
64+
$[0] = data;
65+
$[1] = icon;
66+
$[2] = name;
67+
$[3] = t1;
6768
} else {
68-
t1 = $[1];
69+
t1 = $[3];
6970
}
7071
return t1;
7172
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inner-memo-value-not-promoted-to-outer-scope-static.expect.md

+13-6
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,31 @@ function Component(props) {
2121
```javascript
2222
import { c as _c } from "react/compiler-runtime";
2323
function Component(props) {
24-
const $ = _c(1);
24+
const $ = _c(2);
2525
let t0;
2626
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
2727
const count = new MaybeMutable();
2828

29-
t0 = (
29+
t0 = <span>{maybeMutate(count)}</span>;
30+
$[0] = t0;
31+
} else {
32+
t0 = $[0];
33+
}
34+
let t1;
35+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
36+
t1 = (
3037
<View>
3138
<View>
3239
<span>Text</span>
33-
<span>{maybeMutate(count)}</span>
40+
{t0}
3441
</View>
3542
</View>
3643
);
37-
$[0] = t0;
44+
$[1] = t1;
3845
} else {
39-
t0 = $[0];
46+
t1 = $[1];
4047
}
41-
return t0;
48+
return t1;
4249
}
4350

4451
```

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-preserve-whitespace.expect.md

+21-11
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,39 @@ import { c as _c } from "react/compiler-runtime";
3636
import { StaticText1 } from "shared-runtime";
3737

3838
function Component() {
39-
const $ = _c(1);
39+
const $ = _c(3);
4040
let t0;
4141
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
42-
t0 = (
42+
t0 = <StaticText1 />;
43+
$[0] = t0;
44+
} else {
45+
t0 = $[0];
46+
}
47+
let t1;
48+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
49+
t1 = <StaticText1 />;
50+
$[1] = t1;
51+
} else {
52+
t1 = $[1];
53+
}
54+
let t2;
55+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
56+
t2 = (
4357
<div>
44-
Before text
45-
<StaticText1 />
46-
Middle text
58+
Before text{t0}Middle text
4759
<StaticText1>
48-
Inner before text
49-
<StaticText1 />
50-
Inner middle text
60+
Inner before text{t1}Inner middle text
5161
<StaticText1 />
5262
Inner after text
5363
</StaticText1>
5464
After text
5565
</div>
5666
);
57-
$[0] = t0;
67+
$[2] = t2;
5868
} else {
59-
t0 = $[0];
69+
t2 = $[2];
6070
}
61-
return t0;
71+
return t2;
6272
}
6373

6474
export const FIXTURE_ENTRYPOINT = {

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -54,31 +54,31 @@ function Post(t0) {
5454
const $ = _c(7);
5555
const { author, text } = t0;
5656
let t1;
57-
if ($[0] !== author) {
58-
t1 = <h1>{author}</h1>;
59-
$[0] = author;
57+
if ($[0] !== text) {
58+
t1 = <span>{text}</span>;
59+
$[0] = text;
6060
$[1] = t1;
6161
} else {
6262
t1 = $[1];
6363
}
6464
let t2;
65-
if ($[2] !== text) {
66-
t2 = <span>{text}</span>;
67-
$[2] = text;
65+
if ($[2] !== author) {
66+
t2 = <h1>{author}</h1>;
67+
$[2] = author;
6868
$[3] = t2;
6969
} else {
7070
t2 = $[3];
7171
}
7272
let t3;
73-
if ($[4] !== t1 || $[5] !== t2) {
73+
if ($[4] !== t2 || $[5] !== t1) {
7474
t3 = (
7575
<div>
76-
{t1}
7776
{t2}
77+
{t1}
7878
</div>
7979
);
80-
$[4] = t1;
81-
$[5] = t2;
80+
$[4] = t2;
81+
$[5] = t1;
8282
$[6] = t3;
8383
} else {
8484
t3 = $[6];

0 commit comments

Comments
 (0)