Skip to content

Commit bf1bf9c

Browse files
committed
[compiler] Effects for Return/MaybeThrow terminals
ghstack-source-id: a0411c7 Pull Request resolved: #33429
1 parent 8073478 commit bf1bf9c

File tree

10 files changed

+61
-47
lines changed

10 files changed

+61
-47
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export function lower(
179179
loc: GeneratedSource,
180180
value: lowerExpressionToTemporary(builder, body),
181181
id: makeInstructionId(0),
182+
effects: null,
182183
};
183184
builder.terminateWithContinuation(terminal, fallthrough);
184185
} else if (body.isBlockStatement()) {
@@ -208,6 +209,7 @@ export function lower(
208209
loc: GeneratedSource,
209210
}),
210211
id: makeInstructionId(0),
212+
effects: null,
211213
},
212214
null,
213215
);
@@ -287,6 +289,7 @@ function lowerStatement(
287289
loc: stmt.node.loc ?? GeneratedSource,
288290
value,
289291
id: makeInstructionId(0),
292+
effects: null,
290293
};
291294
builder.terminate(terminal, 'block');
292295
return;

compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ export type ReturnTerminal = {
457457
value: Place;
458458
id: InstructionId;
459459
fallthrough?: never;
460+
effects: Array<AliasingEffect> | null;
460461
};
461462

462463
export type GotoTerminal = {
@@ -617,6 +618,7 @@ export type MaybeThrowTerminal = {
617618
id: InstructionId;
618619
loc: SourceLocation;
619620
fallthrough?: never;
621+
effects: Array<AliasingEffect> | null;
620622
};
621623

622624
export type ReactiveScopeTerminal = {

compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export default class HIRBuilder {
165165
handler: exceptionHandler,
166166
id: makeInstructionId(0),
167167
loc: instruction.loc,
168+
effects: null,
168169
},
169170
continuationBlock,
170171
);

compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
222222
value = `[${terminal.id}] Return${
223223
terminal.value != null ? ' ' + printPlace(terminal.value) : ''
224224
}`;
225+
if (terminal.effects != null) {
226+
value += `\n ${terminal.effects.map(printAliasingEffect).join('\n ')}`;
227+
}
225228
break;
226229
}
227230
case 'goto': {
@@ -290,6 +293,9 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
290293
}
291294
case 'maybe-throw': {
292295
value = `[${terminal.id}] MaybeThrow continuation=bb${terminal.continuation} handler=bb${terminal.handler}`;
296+
if (terminal.effects != null) {
297+
value += `\n ${terminal.effects.map(printAliasingEffect).join('\n ')}`;
298+
}
293299
break;
294300
}
295301
case 'scope': {

compiler/packages/babel-plugin-react-compiler/src/HIR/visitors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ export function mapTerminalSuccessors(
735735
loc: terminal.loc,
736736
value: terminal.value,
737737
id: makeInstructionId(0),
738+
effects: terminal.effects,
738739
};
739740
}
740741
case 'throw': {
@@ -842,6 +843,7 @@ export function mapTerminalSuccessors(
842843
handler,
843844
id: makeInstructionId(0),
844845
loc: terminal.loc,
846+
effects: terminal.effects,
845847
};
846848
}
847849
case 'try': {

compiler/packages/babel-plugin-react-compiler/src/Inference/AnalyseFunctions.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
Place,
1616
isRefOrRefValue,
1717
makeInstructionId,
18-
printFunction,
1918
} from '../HIR';
2019
import {deadCodeElimination} from '../Optimization';
2120
import {inferReactiveScopeVariables} from '../ReactiveScopes';

compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export function inferMutationAliasingEffects(
155155
}
156156
queue(fn.body.entry, initialState);
157157

158-
const context = new Context();
158+
const context = new Context(isFunctionExpression);
159159

160160
let count = 0;
161161
while (queuedStates.size !== 0) {
@@ -192,6 +192,11 @@ class Context {
192192
effectInstructionValueCache: Map<AliasingEffect, InstructionValue> =
193193
new Map();
194194
catchHandlers: Map<BlockId, Place> = new Map();
195+
isFuctionExpression: boolean;
196+
197+
constructor(isFunctionExpression: boolean) {
198+
this.isFuctionExpression = isFunctionExpression;
199+
}
195200
}
196201

197202
function inferParam(
@@ -233,6 +238,7 @@ function inferBlock(
233238
} else if (terminal.kind === 'maybe-throw') {
234239
const handlerParam = context.catchHandlers.get(terminal.handler);
235240
if (handlerParam != null) {
241+
const effects: Array<AliasingEffect> = [];
236242
for (const instr of block.instructions) {
237243
if (
238244
instr.value.kind === 'CallExpression' ||
@@ -243,10 +249,33 @@ function inferBlock(
243249
* itself. For example, `c = a.b` can throw if `a` is nullish, but the thrown value
244250
* is an error object synthesized by the JS runtime. Whereas `throwsInput(x)` can
245251
* throw (effectively) the result of the call.
252+
*
253+
* TODO: call applyEffect() instead. This meant that the catch param wasn't inferred
254+
* as a mutable value, though. See `try-catch-try-value-modified-in-catch-escaping.js`
255+
* fixture as an example
246256
*/
247257
state.appendAlias(handlerParam, instr.lvalue);
258+
const kind = state.kind(instr.lvalue).kind;
259+
if (kind === ValueKind.Mutable || kind == ValueKind.Context) {
260+
effects.push({
261+
kind: 'Alias',
262+
from: instr.lvalue,
263+
into: handlerParam,
264+
});
265+
}
248266
}
249267
}
268+
terminal.effects = effects.length !== 0 ? effects : null;
269+
}
270+
} else if (terminal.kind === 'return') {
271+
if (!context.isFuctionExpression) {
272+
terminal.effects = [
273+
{
274+
kind: 'Freeze',
275+
value: terminal.value,
276+
reason: ValueReason.JsxCaptured,
277+
},
278+
];
250279
}
251280
}
252281
}
@@ -326,7 +355,7 @@ function applySignature(
326355
}
327356

328357
for (const effect of signature.effects) {
329-
applyEffect(context, state, effect, instruction, aliased, effects);
358+
applyEffect(context, state, effect, aliased, effects);
330359
}
331360
if (DEBUG) {
332361
console.log(
@@ -351,7 +380,6 @@ function applyEffect(
351380
context: Context,
352381
state: InferenceState,
353382
effect: AliasingEffect,
354-
instruction: Instruction,
355383
aliased: Set<IdentifierId>,
356384
effects: Array<AliasingEffect>,
357385
): void {
@@ -477,7 +505,6 @@ function applyEffect(
477505
from: capture,
478506
into: effect.into,
479507
},
480-
instruction,
481508
aliased,
482509
effects,
483510
);
@@ -618,14 +645,7 @@ function applyEffect(
618645
console.log('apply function expression effects');
619646
}
620647
for (const signatureEffect of signatureEffects) {
621-
applyEffect(
622-
context,
623-
state,
624-
signatureEffect,
625-
instruction,
626-
aliased,
627-
effects,
628-
);
648+
applyEffect(context, state, signatureEffect, aliased, effects);
629649
}
630650
break;
631651
}
@@ -645,14 +665,7 @@ function applyEffect(
645665
console.log('apply aliasing signature effects');
646666
}
647667
for (const signatureEffect of signatureEffects) {
648-
applyEffect(
649-
context,
650-
state,
651-
signatureEffect,
652-
instruction,
653-
aliased,
654-
effects,
655-
);
668+
applyEffect(context, state, signatureEffect, aliased, effects);
656669
}
657670
} else if (effect.signature != null) {
658671
if (DEBUG) {
@@ -666,14 +679,7 @@ function applyEffect(
666679
effect.args,
667680
);
668681
for (const legacyEffect of legacyEffects) {
669-
applyEffect(
670-
context,
671-
state,
672-
legacyEffect,
673-
instruction,
674-
aliased,
675-
effects,
676-
);
682+
applyEffect(context, state, legacyEffect, aliased, effects);
677683
}
678684
} else {
679685
if (DEBUG) {
@@ -687,7 +693,6 @@ function applyEffect(
687693
into: effect.into,
688694
value: ValueKind.Mutable,
689695
},
690-
instruction,
691696
aliased,
692697
effects,
693698
);
@@ -711,7 +716,6 @@ function applyEffect(
711716
kind: 'MutateTransitiveConditionally',
712717
value: operand,
713718
},
714-
instruction,
715719
aliased,
716720
effects,
717721
);
@@ -721,7 +725,6 @@ function applyEffect(
721725
state,
722726
// OK: recording information flow
723727
{kind: 'Alias', from: operand, into: effect.into},
724-
instruction,
725728
aliased,
726729
effects,
727730
);
@@ -750,7 +753,6 @@ function applyEffect(
750753
from: operand,
751754
into: other,
752755
},
753-
instruction,
754756
aliased,
755757
effects,
756758
);
@@ -772,7 +774,6 @@ function applyEffect(
772774
) {
773775
const value = state.kind(effect.value);
774776
if (DEBUG) {
775-
console.log(printInstruction(instruction));
776777
console.log(printAliasingEffect(effect));
777778
console.log(prettyFormat(state.debugAbstractValue(value)));
778779
}

compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
7171
kind: MutationKind;
7272
place: Place;
7373
}> = [];
74-
const catchHandlers = new Map<BlockId, Place>();
7574

7675
let index = 0;
7776

@@ -157,20 +156,19 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
157156
state.assign(index++, block.terminal.value, fn.returns);
158157
}
159158

160-
/**
161-
* TODO: add effects to terminals so that these can be emitted by the equivalent
162-
* logic in InferMutationAliasingEffects
163-
*/
164159
if (
165-
block.terminal.kind === 'try' &&
166-
block.terminal.handlerBinding != null
160+
(block.terminal.kind === 'maybe-throw' ||
161+
block.terminal.kind === 'return') &&
162+
block.terminal.effects != null
167163
) {
168-
catchHandlers.set(block.terminal.handler, block.terminal.handlerBinding);
169-
} else if (block.terminal.kind === 'maybe-throw') {
170-
const handlerParam = catchHandlers.get(block.terminal.handler);
171-
if (handlerParam != null) {
172-
for (const instr of block.instructions) {
173-
state.assign(index++, instr.lvalue, handlerParam);
164+
for (const effect of block.terminal.effects) {
165+
if (effect.kind === 'Alias') {
166+
state.assign(index++, effect.from, effect.into);
167+
} else {
168+
CompilerError.invariant(effect.kind === 'Freeze', {
169+
reason: `Unexpected '${effect.kind}' effect for MaybeThrow terminal`,
170+
loc: block.terminal.loc,
171+
});
174172
}
175173
}
176174
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ function emitSelectorFn(env: Environment, keys: Array<string>): Instruction {
241241
kind: 'return',
242242
loc: GeneratedSource,
243243
value: arrayInstr.lvalue,
244+
effects: null,
244245
},
245246
preds: new Set(),
246247
phis: new Set(),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ function emitOutlinedFn(
356356
kind: 'return',
357357
loc: GeneratedSource,
358358
value: instructions.at(-1)!.lvalue,
359+
effects: null,
359360
},
360361
preds: new Set(),
361362
phis: new Set(),

0 commit comments

Comments
 (0)