Skip to content

Commit f8cb40b

Browse files
committed
[compiler] Allow reordering of LoadLocal after their last assignment
ghstack-source-id: 401d1019f6b93c631c273a138e925719d348a06e Pull Request resolved: #29882
1 parent f14d7f0 commit f8cb40b

File tree

1 file changed

+97
-5
lines changed

1 file changed

+97
-5
lines changed

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

+97-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ import {
1313
HIRFunction,
1414
IdentifierId,
1515
Instruction,
16+
InstructionId,
17+
Place,
1618
isExpressionBlockKind,
19+
makeInstructionId,
1720
markInstructionIds,
1821
} from "../HIR";
1922
import { printInstruction } from "../HIR/PrintHIR";
2023
import {
24+
eachInstructionLValue,
2125
eachInstructionValueLValue,
2226
eachInstructionValueOperand,
2327
eachTerminalOperand,
@@ -69,8 +73,9 @@ import { getOrInsertWith } from "../Utils/utils";
6973
export function instructionReordering(fn: HIRFunction): void {
7074
// Shared nodes are emitted when they are first used
7175
const shared: Nodes = new Map();
76+
const references = findReferencedRangeOfTemporaries(fn);
7277
for (const [, block] of fn.body.blocks) {
73-
reorderBlock(fn.env, block, shared);
78+
reorderBlock(fn.env, block, shared, references);
7479
}
7580
CompilerError.invariant(shared.size === 0, {
7681
reason: `InstructionReordering: expected all reorderable nodes to have been emitted`,
@@ -91,10 +96,76 @@ type Node = {
9196
depth: number | null;
9297
};
9398

99+
// Inclusive start and end
100+
type References = {
101+
accessedRanges: AccessedRanges;
102+
lastAssignments: LastAssignments;
103+
};
104+
type LastAssignments = Map<string, InstructionId>;
105+
type AccessedRanges = Map<IdentifierId, Range>;
106+
type Range = { start: InstructionId; end: InstructionId };
107+
enum ReferenceKind {
108+
Read,
109+
Write,
110+
}
111+
function findReferencedRangeOfTemporaries(fn: HIRFunction): References {
112+
const accessedRanges: AccessedRanges = new Map();
113+
const lastAssignments: LastAssignments = new Map();
114+
function reference(
115+
instr: InstructionId,
116+
place: Place,
117+
kind: ReferenceKind
118+
): void {
119+
if (
120+
place.identifier.name !== null &&
121+
place.identifier.name.kind === "named"
122+
) {
123+
if (kind === ReferenceKind.Write) {
124+
const name = place.identifier.name.value;
125+
const previous = lastAssignments.get(name);
126+
if (previous === undefined) {
127+
lastAssignments.set(name, instr);
128+
} else {
129+
lastAssignments.set(
130+
name,
131+
makeInstructionId(Math.max(previous, instr))
132+
);
133+
}
134+
}
135+
return;
136+
} else if (kind === ReferenceKind.Read) {
137+
const range = getOrInsertWith(
138+
accessedRanges,
139+
place.identifier.id,
140+
() => ({
141+
start: instr,
142+
end: instr,
143+
})
144+
);
145+
range.end = instr;
146+
}
147+
}
148+
for (const [, block] of fn.body.blocks) {
149+
for (const instr of block.instructions) {
150+
for (const operand of eachInstructionValueLValue(instr.value)) {
151+
reference(instr.id, operand, ReferenceKind.Read);
152+
}
153+
for (const lvalue of eachInstructionLValue(instr)) {
154+
reference(instr.id, lvalue, ReferenceKind.Write);
155+
}
156+
}
157+
for (const operand of eachTerminalOperand(block.terminal)) {
158+
reference(block.terminal.id, operand, ReferenceKind.Read);
159+
}
160+
}
161+
return { accessedRanges, lastAssignments };
162+
}
163+
94164
function reorderBlock(
95165
env: Environment,
96166
block: BasicBlock,
97-
shared: Nodes
167+
shared: Nodes,
168+
references: References
98169
): void {
99170
const locals: Nodes = new Map();
100171
const named: Map<string, IdentifierId> = new Map();
@@ -116,7 +187,7 @@ function reorderBlock(
116187
* Ensure non-reoderable instructions have their order retained by
117188
* adding explicit dependencies to the previous such instruction.
118189
*/
119-
if (getReoderability(instr) === Reorderability.Nonreorderable) {
190+
if (getReoderability(instr, references) === Reorderability.Nonreorderable) {
120191
if (previous !== null) {
121192
node.dependencies.add(previous);
122193
}
@@ -220,7 +291,8 @@ function reorderBlock(
220291
}
221292
CompilerError.invariant(
222293
node.instruction != null &&
223-
getReoderability(node.instruction) === Reorderability.Reorderable,
294+
getReoderability(node.instruction, references) ===
295+
Reorderability.Reorderable,
224296
{
225297
reason: `Expected all remaining instructions to be reorderable`,
226298
loc: node.instruction?.loc ?? block.terminal.loc,
@@ -334,7 +406,10 @@ enum Reorderability {
334406
Reorderable,
335407
Nonreorderable,
336408
}
337-
function getReoderability(instr: Instruction): Reorderability {
409+
function getReoderability(
410+
instr: Instruction,
411+
references: References
412+
): Reorderability {
338413
switch (instr.value.kind) {
339414
case "JsxExpression":
340415
case "JsxFragment":
@@ -346,6 +421,23 @@ function getReoderability(instr: Instruction): Reorderability {
346421
case "UnaryExpression": {
347422
return Reorderability.Reorderable;
348423
}
424+
case "LoadLocal": {
425+
const name = instr.value.place.identifier.name;
426+
if (name !== null && name.kind === "named") {
427+
const lastAssignment = references.lastAssignments.get(name.value);
428+
const range = references.accessedRanges.get(instr.lvalue.identifier.id);
429+
if (
430+
lastAssignment !== undefined &&
431+
lastAssignment < instr.id &&
432+
range !== undefined &&
433+
range.end === range.start // this LoadLocal is used exactly once
434+
) {
435+
console.log(`reorderable: ${name.value}`);
436+
return Reorderability.Reorderable;
437+
}
438+
}
439+
return Reorderability.Nonreorderable;
440+
}
349441
default: {
350442
return Reorderability.Nonreorderable;
351443
}

0 commit comments

Comments
 (0)