Skip to content

Commit d289535

Browse files
committed
[compiler] Allow reordering of LoadLocal after their last assignment
[ghstack-poisoned]
1 parent 1501466 commit d289535

File tree

1 file changed

+99
-5
lines changed

1 file changed

+99
-5
lines changed

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

+99-5
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ import {
1313
HIRFunction,
1414
IdentifierId,
1515
Instruction,
16+
InstructionId,
17+
MutableRange,
18+
Place,
1619
isExpressionBlockKind,
20+
makeInstructionId,
1721
markInstructionIds,
1822
} from "../HIR";
1923
import { printInstruction } from "../HIR/PrintHIR";
2024
import {
25+
eachInstructionLValue,
2126
eachInstructionValueLValue,
2227
eachInstructionValueOperand,
2328
eachTerminalOperand,
29+
terminalFallthrough,
2430
} from "../HIR/visitors";
2531
import { mayAllocate } from "../ReactiveScopes/InferReactiveScopeVariables";
2632
import { getOrInsertWith } from "../Utils/utils";
@@ -69,8 +75,9 @@ import { getOrInsertWith } from "../Utils/utils";
6975
export function instructionReordering(fn: HIRFunction): void {
7076
// Shared nodes are emitted when they are first used
7177
const shared: Nodes = new Map();
78+
const references = findReferencedRangeOfTemporaries(fn);
7279
for (const [, block] of fn.body.blocks) {
73-
reorderBlock(fn.env, block, shared);
80+
reorderBlock(fn.env, block, shared, references);
7481
}
7582
CompilerError.invariant(shared.size === 0, {
7683
reason: `InstructionReordering: expected all reorderable nodes to have been emitted`,
@@ -91,10 +98,76 @@ type Node = {
9198
depth: number | null;
9299
};
93100

101+
// Inclusive start and end
102+
type References = {
103+
accessedRanges: AccessedRanges;
104+
lastAssignments: LastAssignments;
105+
};
106+
type LastAssignments = Map<string, InstructionId>;
107+
type AccessedRanges = Map<IdentifierId, Range>;
108+
type Range = { start: InstructionId; end: InstructionId };
109+
enum ReferenceKind {
110+
Read,
111+
Write,
112+
}
113+
function findReferencedRangeOfTemporaries(fn: HIRFunction): References {
114+
const accessedRanges: AccessedRanges = new Map();
115+
const lastAssignments: LastAssignments = new Map();
116+
function reference(
117+
instr: InstructionId,
118+
place: Place,
119+
kind: ReferenceKind
120+
): void {
121+
if (
122+
place.identifier.name !== null &&
123+
place.identifier.name.kind === "named"
124+
) {
125+
if (kind === ReferenceKind.Write) {
126+
const name = place.identifier.name.value;
127+
const previous = lastAssignments.get(name);
128+
if (previous === undefined) {
129+
lastAssignments.set(name, instr);
130+
} else {
131+
lastAssignments.set(
132+
name,
133+
makeInstructionId(Math.max(previous, instr))
134+
);
135+
}
136+
}
137+
return;
138+
} else if (kind === ReferenceKind.Read) {
139+
const range = getOrInsertWith(
140+
accessedRanges,
141+
place.identifier.id,
142+
() => ({
143+
start: instr,
144+
end: instr,
145+
})
146+
);
147+
range.end = instr;
148+
}
149+
}
150+
for (const [, block] of fn.body.blocks) {
151+
for (const instr of block.instructions) {
152+
for (const operand of eachInstructionValueLValue(instr.value)) {
153+
reference(instr.id, operand, ReferenceKind.Read);
154+
}
155+
for (const lvalue of eachInstructionLValue(instr)) {
156+
reference(instr.id, lvalue, ReferenceKind.Write);
157+
}
158+
}
159+
for (const operand of eachTerminalOperand(block.terminal)) {
160+
reference(block.terminal.id, operand, ReferenceKind.Read);
161+
}
162+
}
163+
return { accessedRanges, lastAssignments };
164+
}
165+
94166
function reorderBlock(
95167
env: Environment,
96168
block: BasicBlock,
97-
shared: Nodes
169+
shared: Nodes,
170+
references: References
98171
): void {
99172
const locals: Nodes = new Map();
100173
const named: Map<string, IdentifierId> = new Map();
@@ -116,7 +189,7 @@ function reorderBlock(
116189
* Ensure non-reoderable instructions have their order retained by
117190
* adding explicit dependencies to the previous such instruction.
118191
*/
119-
if (getReoderability(instr) === Reorderability.Nonreorderable) {
192+
if (getReoderability(instr, references) === Reorderability.Nonreorderable) {
120193
if (previous !== null) {
121194
node.dependencies.add(previous);
122195
}
@@ -220,7 +293,8 @@ function reorderBlock(
220293
}
221294
CompilerError.invariant(
222295
node.instruction != null &&
223-
getReoderability(node.instruction) === Reorderability.Reorderable,
296+
getReoderability(node.instruction, references) ===
297+
Reorderability.Reorderable,
224298
{
225299
reason: `Expected all remaining instructions to be reorderable`,
226300
loc: node.instruction?.loc ?? block.terminal.loc,
@@ -334,7 +408,10 @@ enum Reorderability {
334408
Reorderable,
335409
Nonreorderable,
336410
}
337-
function getReoderability(instr: Instruction): Reorderability {
411+
function getReoderability(
412+
instr: Instruction,
413+
references: References
414+
): Reorderability {
338415
switch (instr.value.kind) {
339416
case "JsxExpression":
340417
case "JsxFragment":
@@ -346,6 +423,23 @@ function getReoderability(instr: Instruction): Reorderability {
346423
case "UnaryExpression": {
347424
return Reorderability.Reorderable;
348425
}
426+
case "LoadLocal": {
427+
const name = instr.value.place.identifier.name;
428+
if (name !== null && name.kind === "named") {
429+
const lastAssignment = references.lastAssignments.get(name.value);
430+
const range = references.accessedRanges.get(instr.lvalue.identifier.id);
431+
if (
432+
lastAssignment !== undefined &&
433+
lastAssignment < instr.id &&
434+
range !== undefined &&
435+
range.end === range.start // this LoadLocal is used exactly once
436+
) {
437+
console.log(`reorderable: ${name.value}`);
438+
return Reorderability.Reorderable;
439+
}
440+
}
441+
return Reorderability.Nonreorderable;
442+
}
349443
default: {
350444
return Reorderability.Nonreorderable;
351445
}

0 commit comments

Comments
 (0)