Skip to content

Commit 109f884

Browse files
committed
[compiler] Effect inference across signatures and user-provided callbacks
This ties all the ideas together, showing the promise of the new inference. When applying effects, when we encounter an Apply effect we now check to see if the function we're calling is a locally defined FunctionExpression. If so, we construct a signature for it on the fly (we already have created the effects in AnalyzeFunctions), substitute in the args to get a set of effects we can apply, and then recursively apply those effects. This required adding an ability for signatures to declare additional temporary places that they can reference. For example, Array.prototype.map needs a temporary to represent the items it extracts from the receiver array, and another temporary for the result of calling the user-provided function. This also meant adding a `CreateFunction` effect which a) allows us to preserve the FunctionExpression value in the inference state (the previous Create effect meant we just created a dummy ObjectExpression) and b) allows dynamically constructing the ValueKind of the function based on whether it actually captures any mutable values. Lots of other little fixes as well, such as changing function related effects (and PropertyLoad) to use Alias instead of Capture so that subsequent mutations of the output count as mutations of the input. ghstack-source-id: 93c3bc3 Pull Request resolved: facebook/react#33384
1 parent 467b4f6 commit 109f884

File tree

10 files changed

+455
-44
lines changed

10 files changed

+455
-44
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export const EnvironmentConfigSchema = z.object({
246246
/**
247247
* Enable a new model for mutability and aliasing inference
248248
*/
249-
enableNewMutationAliasingModel: z.boolean().default(false),
249+
enableNewMutationAliasingModel: z.boolean().default(true),
250250

251251
/**
252252
* Enables inference of optional dependency chains. Without this flag

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ export type FunctionSignature = {
192192
canonicalName?: string;
193193

194194
aliasing?: AliasingSignature | null;
195+
todo_aliasing?: AliasingSignature | null;
195196
};
196197

197198
/*
@@ -320,6 +321,7 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
320321
params: [],
321322
rest: makeIdentifierId(1),
322323
returns: makeIdentifierId(2),
324+
temporaries: [],
323325
effects: [
324326
// Push directly mutates the array itself
325327
{kind: 'Mutate', value: signatureArgument(0)},
@@ -367,7 +369,59 @@ addObject(BUILTIN_SHAPES, BuiltInArrayId, [
367369
returnValueKind: ValueKind.Mutable,
368370
noAlias: true,
369371
mutableOnlyIfOperandsAreMutable: true,
370-
aliasing: null,
372+
aliasing: {
373+
receiver: makeIdentifierId(0),
374+
params: [makeIdentifierId(1)],
375+
rest: null,
376+
returns: makeIdentifierId(2),
377+
temporaries: [
378+
// Temporary representing captured items of the receiver
379+
signatureArgument(3),
380+
// Temporary representing the result of the callback
381+
signatureArgument(4),
382+
/*
383+
* Undefined `this` arg to the callback. Note the signature does not
384+
* support passing an explicit thisArg second param
385+
*/
386+
signatureArgument(5),
387+
],
388+
effects: [
389+
// Map creates a new mutable array
390+
{
391+
kind: 'Create',
392+
into: signatureArgument(2),
393+
value: ValueKind.Mutable,
394+
},
395+
// The first arg to the callback is an item extracted from the receiver array
396+
{
397+
kind: 'CreateFrom',
398+
from: signatureArgument(0),
399+
into: signatureArgument(3),
400+
},
401+
// The undefined this for the callback
402+
{
403+
kind: 'Create',
404+
into: signatureArgument(5),
405+
value: ValueKind.Primitive,
406+
},
407+
// calls the callback, returning the result into a temporary
408+
{
409+
kind: 'Apply',
410+
receiver: signatureArgument(5),
411+
args: [signatureArgument(3), {kind: 'Hole'}, signatureArgument(0)],
412+
function: signatureArgument(1),
413+
into: signatureArgument(4),
414+
signature: null,
415+
mutatesFunction: false,
416+
},
417+
// captures the result of the callback into the return array
418+
{
419+
kind: 'Capture',
420+
from: signatureArgument(4),
421+
into: signatureArgument(2),
422+
},
423+
],
424+
},
371425
}),
372426
],
373427
[

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,8 @@ export function printAliasingEffect(effect: AliasingEffect): string {
972972
.map(arg => {
973973
if (arg.kind === 'Identifier') {
974974
return printPlaceForAliasEffect(arg);
975+
} else if (arg.kind === 'Hole') {
976+
return ' ';
975977
}
976978
return `...${printPlaceForAliasEffect(arg.place)}`;
977979
})
@@ -986,6 +988,9 @@ export function printAliasingEffect(effect: AliasingEffect): string {
986988
}
987989
return `Apply ${printPlaceForAliasEffect(effect.into)} = ${receiverCallee}(${args})${signature != '' ? '\n ' : ''}${signature}`;
988990
}
991+
case 'CreateFunction': {
992+
return `Function ${printPlaceForAliasEffect(effect.into)} = Function captures=[${effect.captures.map(printPlaceForAliasEffect).join(', ')}]`;
993+
}
989994
case 'Freeze': {
990995
return `Freeze ${printPlaceForAliasEffect(effect.value)} ${effect.reason}`;
991996
}
@@ -1007,6 +1012,13 @@ function printPlaceForAliasEffect(place: Place): string {
10071012

10081013
export function printAliasingSignature(signature: AliasingSignature): string {
10091014
const tokens: Array<string> = ['function '];
1015+
if (signature.temporaries.length !== 0) {
1016+
tokens.push('<');
1017+
tokens.push(
1018+
signature.temporaries.map(temp => `$${temp.identifier.id}`).join(', '),
1019+
);
1020+
tokens.push('>');
1021+
}
10101022
tokens.push('(');
10111023
tokens.push('this=$' + String(signature.receiver));
10121024
for (const param of signature.params) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ function lowerWithMutationAliasing(fn: HIRFunction): void {
9696
capturedOrMutated.add(effect.value.identifier.id);
9797
break;
9898
}
99+
case 'CreateFunction':
99100
case 'Create':
100101
case 'Freeze':
101102
case 'ImmutableCapture': {

0 commit comments

Comments
 (0)