Commit Graph

25 Commits

Author SHA1 Message Date
Joseph Savona cdde15efe1 [compiler] InlineJSXTransform transforms jsx inside function expressions (#31282)
InlineJSXTransform wasn't traversing into function expressions or object
methods, so any JSX inside such functions wouldn't have gotten inlined.
This PR updates to traverse nested functions to transform all JSX within
a hook or component.

Note that this still doesn't transform JSX outside of components or
hooks, ie in standalone render helpers.
2024-10-18 11:27:48 -07:00
Sathya Gunasekaran c91b3b090a JSX Outlining (#30956)
Currently, the react compiler can not compile within callbacks which can
potentially cause over rendering. Consider this example:
```jsx
function Component(countries, onDelete) {
  const name = useFoo();
  return countries.map(() => {
    return (
      <Foo>
        <Bar name={name}/>
        <Baz onclick={onDelete} />
      </Foo>
    );
  });
}
```

In this case, there's no memoization of the nested jsx elements. But
instead if we were to manually refactor the nested jsx into separate
component like this:
```jsx
function Component(countries, onDelete) {
  const name = useFoo();
  return countries.map(() => {
    return <Temp name={name} onDelete={onDelete} />;
  });
}

function Temp({ name, onDelete }) {
  return (
    <Foo>
      <Bar name={name} />
      <Baz onclick={onDelete} />
    </Foo>
  );
}

```

The compiler can now optimise both these components:
```jsx
function Component(countries, onDelete) {
  const $ = _c(4);
  const name = useFoo();
  let t0;
  if ($[0] !== name || $[1] !== onDelete || $[2] !== countries) {
    t0 = countries.map(() => <Temp name={name} onDelete={onDelete} />);
    $[0] = name;
    $[1] = onDelete;
    $[2] = countries;
    $[3] = t0;
  } else {
    t0 = $[3];
  }
  return t0;
}

function Temp(t0) {
  const $ = _c(7);
  const { name, onDelete } = t0;
  let t1;
  if ($[0] !== name) {
    t1 = <Bar name={name} />;
    $[0] = name;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  let t2;
  if ($[2] !== onDelete) {
    t2 = <Baz onclick={onDelete} />;
    $[2] = onDelete;
    $[3] = t2;
  } else {
    t2 = $[3];
  }
  let t3;
  if ($[4] !== t1 || $[5] !== t2) {
    t3 = (
      <Foo>
        {t1}
        {t2}
      </Foo>
    );
    $[4] = t1;
    $[5] = t2;
    $[6] = t3;
  } else {
    t3 = $[6];
  }
  return t3;
}
```

Now, when `countries` is updated by adding one single value, only the
newly added value is re-rendered and not the entire list. Rather than
having to do this manually, this PR teaches the react compiler to do
this transformation.

This PR adds a new pass (`OutlineJsx`) to capture nested jsx statements
and outline them in a separate component. This newly outlined component
can then by memoized by the compiler, giving us more fine grained
rendering.
2024-10-17 18:15:32 +01:00
Mike Vitousek 7b7fac073d [compiler] Represent phis with places rather than identifiers
Summary:
The fact that phis are identifiers rather than places is unfortunate in a few cases. In some later analyses, we might wish to know whether a phi is reactive, but we don't have an easy way to do that currently.

Most of the changes here is just replacing phi.id with phi.place.identifier and such. Interesting bits are EnterSSA (several functions now take places rather than identifiers, and InferReactivePlaces now needs to mark places as reactive explicitly.

ghstack-source-id: 5f4fb396cd
Pull Request resolved: https://github.com/facebook/react/pull/31171
2024-10-10 12:41:03 -07:00
Jack Pope 632f88df11 [compiler] Allow ReactElement symbol to be configured when inlining jsx (#30996)
Based on https://github.com/facebook/react/pull/30995 ([rendered
diff](https://github.com/jackpope/react/compare/inline-jsx-2...jackpope:react:inline-jsx-3?expand=1))

____

Some apps still use `react.element` symbols. Not only do we want to test
there but we also want to be able to upgrade those sites to
`react.transitional.element` without blocking on the compiler (we can
change the symbol feature flag and compiler config at the same time).

The compiler runtime uses `react.transitional.element`, so the snap
fixture will fail if we change the default here. However I confirmed
that commenting out the fixture entrypoint and running snap with
`react.element` will update the fixture symbols as expected.
2024-09-19 10:34:24 -04:00
Jack Pope d5e955d3c0 [compiler] Pass through unmodified props spread when inlining jsx (#30995)
If JSX receives a props spread without additional attributes (besides
`ref` and `key`), we can pass the spread object as a property directly
to avoid the extra object copy.

```
<Test {...propsToSpread} />
// {props: propsToSpread}
<Test {...propsToSpread} a="z" />
// {props: {...propsToSpread, a: "z"}}
```
2024-09-19 10:07:29 -04:00
Jack Pope 5dcb009760 [compiler] Add JSX inlining optimization (#30867)
This adds an `InlineJsxTransform` optimization pass, toggled by the
`enableInlineJsxTransform` flag. When enabled, JSX will be transformed
into React Element object literals, preventing runtime overhead during
element creation.

TODO:
- [ ] Add conditionals to make transform PROD-only
- [ ] Make the React element symbol configurable so this works with
runtimes that support `react.element` or `react.transitional.element`
- [ ] Look into additional optimization to pass props spread through
directly if none of the properties are mutated
2024-09-18 11:51:36 -04:00
Joe Savona 7a3fcc9898 [compiler] Flatten returnIdentifier to just returnType
We don't a full Identifier object for the return type, we can just store the type.

ghstack-source-id: 4594d64ce3
Pull Request resolved: https://github.com/facebook/react/pull/30790
2024-08-22 09:33:09 -07:00
Joe Savona 98b5740821 [compiler] Rename HIRFunction.returnType
Rename this field so we can use it for the actual return type.

ghstack-source-id: 118d7dcfbb
Pull Request resolved: https://github.com/facebook/react/pull/30789
2024-08-22 09:33:09 -07:00
Joe Savona 8410c8b959 [compiler] Infer return types of function expressions
Uses the returnIdentifier added in the previous PR to provide a stable identifier for which we can infer a return type for functions, then wires up the equations in InferTypes to infer the type.

ghstack-source-id: 22c0a9ea09
Pull Request resolved: https://github.com/facebook/react/pull/30785
2024-08-22 09:33:09 -07:00
Joe Savona 217a0efcd9 [compiler] Add returnIdentifier to function expressions
This gives us a place to store type information, used in follow-up PRs.

ghstack-source-id: ee0bfa253f
Pull Request resolved: https://github.com/facebook/react/pull/30784
2024-08-22 09:33:09 -07:00
Sathya Gunsasekaran 9d2da5913a [compiler] Add context callee import if required
Previously the compiler would add an import for the specified context
callee even if the context access was not lowered, leading to unused
imports.

This PR tracks if lowering has happened and adds the import only when
necessary.

ghstack-source-id: 6ad794da41
Pull Request resolved: https://github.com/facebook/react/pull/30628
2024-08-08 15:53:13 +01:00
Sathya Gunsasekaran 83cc13f746 [compiler] Rewrite useContext callee
If a value is specified for the LowerContextAccess environment config,
we rewrite the callee from 'useContext' to the specificed value.

This will allow us run an experiment internally.

ghstack-source-id: 00e161b988
Pull Request resolved: https://github.com/facebook/react/pull/30612
2024-08-08 15:53:13 +01:00
Sathya Gunsasekaran e948a5ac68 [compiler] Add lowerContextAccess pass
*This is only for internal profiling, not intended to ship.*

This pass is intended to be used with https://github.com/facebook/react/pull/30407.

This pass synthesizes selector functions by collecting immediately
destructured context acesses. We bailout for other types of context
access.

This pass lowers context access to use a selector function by passing
the synthesized selector function as the second argument.

ghstack-source-id: 92d0f6ff2f
Pull Request resolved: https://github.com/facebook/react/pull/30548
2024-08-07 16:30:11 +01:00
Mike Vitousek 3af905d954 [compiler] Fix issue with macro arguments being outlined
Summary:
Fixes issue documented by #30435. We change the pipeline order so that outlining comes after tracking macro operands, and any function that is referenced in a macro will now not be outlined.

ghstack-source-id: f731ad65c8
Pull Request resolved: https://github.com/facebook/react/pull/30587
2024-08-02 14:55:55 -07:00
Jan Kassens fd2b3e13d3 Compiler: unfork prettier config (#30205)
Updates the prettier config to format all `.ts` and `.tsx` files in the
repo using the existing defaults and removing overrides.

The first commit in this PR contains the config changes, the second is
just the result of running `yarn prettier-all`.
2024-07-18 17:00:24 -04:00
Joe Savona 9d7f02d9ab [compiler] General-purpose function outlining
Implements general-purpose function outlining. Specifically, anonymous function expressions which have no dependencies/context variables are extracted into named top-level functions. The original function expression is replaced with a `LoadGlobal` of the generated name.

Note that the architecture is designed to allow very general purpose forms of outlining, though we currently are very conservative in what we outline. Specifically, the outlining allows annotating functions with an optional ReactiveFunctionType, which if set will cause the outlined function to get compiled as that type. So we could for example outline a helper hook or helper component, set the type, and then have the hook/component get memoized as well. For now though we just outline with no type set, and generate the function as-is without running it through compilation.

ghstack-source-id: 2a7da6c8e8
Pull Request resolved: https://github.com/facebook/react/pull/30331
2024-07-17 10:22:08 +09:00
Joe Savona 2caaa05c08 [compiler] Optimize instruction reordering
Note: due to a bad rebase i included #29883 here. Both were stamped so i'm not gonna bother splitting it back up aain.

This PR includes two changes:
* First, allow `LoadLocal` to be reordered if a) the load occurs after the last write to a variable and b) the LoadLocal lvalue is used exactly once
* Uses a more optimal reordering for statement blocks, while keeping the existing approach for expression 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, as evidenced by the reduced memo slots in the fixtures.

ghstack-source-id: ad3e516fa4
Pull Request resolved: https://github.com/facebook/react/pull/29882
2024-06-21 16:48:14 -07:00
Lauren Tan 3776e98f90 [prettier] Run prettier
ghstack-source-id: 80f1247d5d
Pull Request resolved: https://github.com/facebook/react/pull/30022
2024-06-21 12:05:29 -04:00
Joe Savona 59e73d9016 [compiler] Instruction reordering
Adds a pass just after DCE to reorder safely reorderable instructions (jsx, primitives, globals) closer to where they are used, to allow other optimization passes to be more effective. Notably, the reordering allows scope merging to be more effective, since that pass relies on two scopes not having intervening instructions — in many cases we can now reorder such instructions out of the way and unlock merging, as demonstrated in the changed fixtures.

The algorithm itself is described in the docblock.

note: This is a cleaned up version of #29579 that is ready for review.

ghstack-source-id: c54a806cad
Pull Request resolved: https://github.com/facebook/react/pull/29863
2024-06-14 14:14:42 -07:00
Niklas Mollenhauer f5af92d2c4 feat(compiler): Support MetaProperty (#29752)
## Summary
See #29737

## How did you test this change?
As the feature requires module support and the test runner does
currently not support running tests as modules, I could only test it via
playground.
2024-06-08 16:18:35 -07:00
Niklas Mollenhauer c2b45ef0dd feat(compiler): Implement constant folding for more binary expressions (#29650)
## Summary

There are already most arithmetic operators in constant propagation:
`+`, `-`, `*`, `/`.
We could add more, namely: `|`, `&`, `^`, `<<`, `>>`, `>>>` and `%`:

Input:
```js
function f() {
  return [
    123.45 | 0,
    123.45 & 0,
    123.45 ^ 0,
    123 << 0,
    123 >> 0,
    123 >>> 0,
    123.45 | 1,
    123.45 & 1,
    123.45 ^ 1,
    123 << 1,
    123 >> 1,
    123 >>> 1,
    3 ** 2,
    3 ** 2.5,
    3.5 ** 2,
    2 ** 3 ** 0.5,
    4 % 2,
    4 % 2.5,
    4 % 3,
    4.5 % 2,
  ];
}
```
Output:
```js
function f() {
  return [
    123, 0, 123, 123, 123, 123, 123, 1, 122, 246, 61, 61, 9,
    15.588457268119896, 12.25, 3.3219970854839125, 0, 1.5, 1, 0.5,
  ];
}
```

Resolves #29649

## How did you test this change?
See tests.

Note:
This PR was done without waiting for approval in #29649, so feel free to
just close it without any comment.
2024-05-29 10:35:19 -07:00
Niklas Mollenhauer 320da67570 feat(compiler): Compiler Logical Negation Constant Propagation (#29623)
## Summary

Resolves #29622

## How did you test this change?
I verified the implementation using the test.

Note:
This PR was done without waiting for approval in #29622, so feel free to
just close it.
2024-05-29 09:17:43 -07:00
Niklas Mollenhauer a9a0106808 feat(compiler): Implement constant string concat propagation (#29621)
## Summary

Resolves #29617

## How did you test this change?
I verified the implementation using the test.
2024-05-28 16:15:46 -07:00
Joe Savona 5061f31f4b compiler: distinguish globals/imports/module-locals
We currently use `LoadGlobal` and `StoreGlobal` to represent any read (or write) of a variable defined outside the component or hook that is being compiled. This is mostly fine, but for a lot of things we want to do going forward (resolving types across modules, for example) it helps to understand the actual source of a variable.

This PR is an incremental step in that direction. We continue to use LoadGlobal/StoreGlobal, but LoadGlobal now has a `binding:NonLocalBinding` instead of just the name of the global. The NonLocalBinding type tells us whether it was an import (and which kind, the source module name etc), a module-local binding, or a true global. By keeping the LoadGlobal/StoreGlobal instructions, most code that deals with "anything not declared locally" doesn't have to care about the difference. However, code that _does_ want to know the source of the value can figure it out.

ghstack-source-id: e701d4ebc0
Pull Request resolved: https://github.com/facebook/react/pull/29188
2024-05-24 10:20:26 +01:00
Joe Savona 48e0c70292 Rename babel plugin
ghstack-source-id: bb66913e2d3c814696311371ed655f3da03d1199
Pull Request resolved: https://github.com/facebook/react-forget/pull/2926
2024-05-02 14:12:33 -07:00