Commit Graph
19463 Commits
Author SHA1 Message Date
Joe Savona bc0787fefb More precise handling of const/reassign in ssa form
Addressed a TODO from the previous PR. When we enter SSA form, when we rewrite 
variable reassignments we currently change the identifier but leave the kind of 
the lvalue alone; technically we should convert from Reassign to Const. After 
doing that, it's easier to correctly update when we leave SSA form, we can 
convert just a subset back into let/reassign (but leave most things alone as 
const).
2022-12-20 16:09:59 -08:00
Joe Savona 178bde3495 Run LeaveSSA prior to analyzing reactive scopes
Reorders LeaveSSA so that it runs before we begin evaluating reactive scopes. 
Note that reactive scopes must span the full construction of each variable — for 
variables with a phi, this must span the declaration and all assignments of the 
phi operands. And that's exactly what the new LeaveSSA does! LeaveSSA removes 
phi nodes and ensure that all versions of a variable which flow into a phi have 
been assigned a single canonical identifier (with an appropriate mutable range). 

This PR includes this and some related changes: 

* Reorders the pass 

* Changes hir-test to print the final HIR, eg just prior to codegen 

* Teaches LeaveSSA to update the mutable range of the canonical identifiers it 
assigns, based on the min/max of the variables assigned.
2022-12-20 15:58:03 -08:00
Sathya Gunasekaran 5388839357 [hir] Generalize alias analysis for Effect.Store
Rather than special casing for field stores, look for Effect.Store to alias 
fields. 

In the future, this will be extended to other constructs like Array#push.
2022-12-21 12:04:02 +00:00
Sathya Gunasekaran afdd2cb910 [hir] Add Effect.Store
Effect.Store is exactly like Effect.Mutate, the only difference is that Store 
aliases one into a another value. 

There is no practical difference between Effect.Mutate and Effect.Store 
currently.
2022-12-21 12:04:01 +00:00
Jan Kassens 2b1fb91a55 ESLint upgrade to use hermes-eslint (#25915)
Hermes parser is the preferred parser for Flow code going forward. We
need to upgrade to this parser to support new Flow syntax like function
`this` context type annotations or `ObjectType['prop']` syntax.

Unfortunately, there's quite a few upgrades here to make it work somehow
(dependencies between the changes)

- ~Upgrade `eslint` to `8.*`~ reverted this as the React eslint plugin
tests depend on the older version and there's a [yarn
bug](https://github.com/yarnpkg/yarn/issues/6285) that prevents
`devDependencies` and `peerDependencies` to different versions.
- Remove `eslint-config-fbjs` preset dependency and inline the rules,
imho this makes it a lot clearer what the rules are.
- Remove the turned off `jsx-a11y/*` rules and it's dependency instead
of inlining those from the `fbjs` config.
- Update parser and dependency from `babel-eslint` to `hermes-eslint`.
- `ft-flow/no-unused-expressions` rule replaces `no-unused-expressions`
which now allows standalone type asserts, e.g. `(foo: number);`
- Bunch of globals added to the eslint config
- Disabled `no-redeclare`, seems like the eslint upgrade started making
this more precise and warn against re-defined globals like
`__EXPERIMENTAL__` (in rollup scripts) or `fetch` (when importing fetch
from node-fetch).
- Minor lint fixes like duplicate keys in objects.
2022-12-20 14:27:01 -05:00
Sathya Gunasekaran 69fbdfc7cd [typer] Add inferTypes pass to playground 2022-12-20 14:55:56 +00:00
Sathya Gunasekaran c130bcc9c2 [typer] Use opaque TypeId to number types 2022-12-20 14:55:52 +00:00
Sathya Gunasekaran 549f5a4af6 [typer] Return a narrow-er type from makeType 2022-12-20 14:55:48 +00:00
Joe Savona 828fd81be3 Add new pass to playground 2022-12-20 12:16:34 -08:00
Joe Savona af91a7ab86 Ensure member path assignments memoize independently
Assignment expressions to a member path are a special case because they're the 
only place where a value isn't assigned to a (possibly temporary) variable, 
which is our unit of memoization. #901 demonstrated how this can lead to values 
that can't be independently memoized: 

```javascript 

const x = {a: a} 

x.y = [b, c]; // array recomputed w `x`, even if only `a` changed 

``` 

This PR ensures that assignment expressions where the LHS is a member path lower 
the RHS to a Place. That means the above example is handled as if you wrote: 

```javascript 

const x = {a: a}; 

const tmp1 = [b, c]; 

x.y = tmp1; 

``` 

And we independently memoize the temporary.
2022-12-20 11:06:08 -08:00
Joe Savona cedaee5b03 Codegen for scopes without dependencies
Completes a todo for the `if` condition of scopes without any inputs. In this 
case since there are no inputs we can check for changes, we check if the first 
_output_ cache slot is set to the sentinel. 

For this to work we need to ensure that all scopes have at least one output, 
which isn't currently the case. Dead code can produce output-less sentinels. So 
this PR also adds a pass to find scopes w/o any outputs and convert them to 
regular blocks. We could in theory also just delete them, but for now let's be 
more conservative. This is something we'd want to highlight as a diagnostic in 
an IDE, though existing dead code linters would almost certainly find this case 
too.
2022-12-20 11:06:07 -08:00
Lauren Tan ae8f9066dd Fix invalid import path in test262-preprocessor 2022-12-20 11:42:51 -05:00
Lauren Tan 2721d2a0d9 Remove compiler flags
Remove our existing compiler flags since they were only being used for 
enabling/disabling passes to aid debugging and to simplify in preparation for 
the upcoming work on diagnostics and bailouts. Additionally with the new 
playground tabs disabling passes has become less necessary. In the future when 
we have actual compiler flags (eg tweaking optimization levels) we can add this 
back. 

I opted to keep the existing `CompilerResult` return value instead of just 
returning the optimized AST as we're still using `scopes` in our test fixtures.
2022-12-20 11:42:51 -05:00
Lauren Tan 7cf8e9ac42 [ez] Fixup missing passes in test262-preprocessor 2022-12-20 11:13:05 -05:00
Lauren Tan 9390e969e2 mv src/HIR/Pipeline src/CompilerPipeline
Small reorganization to move Pipeline out of HIR since it's a compiler module. 
It used to make sense before to be in HIR since the old architecture was the 
still the primary, but no longer!
2022-12-20 11:13:05 -05:00
Sathya Gunasekaran f1af022be1 [typer] Fix objectTypeEquals 2022-12-20 13:38:09 +00:00
Sathya Gunasekaran 6f06b0e08a [typer] Reuse PolyType objects 2022-12-20 13:38:08 +00:00
Sathya Gunasekaran 2c3e572566 [typer] Be more conservative with function type inference
It's not safe to infer types of arguments and return values because Javascript 
is so polymorphic. Instead just infer the type of the callee for non methods. 

Interestingly, even this is not conservative enough for JavaScript because Proxy 
can also be callable. But I think for our use cases we will treat Proxy and 
Functions similarly (they're all just objects) so it's ok.
2022-12-20 13:38:07 +00:00
Joe Savona 9207607d37 Delete old architecture
Thanks old architecture! We learned a lot about what does and doesn't work via 
this POC, but it's time to move forward w the ~~new~~ current architecture.
2022-12-19 10:04:54 -08:00
Joe Savona d9b9554948 Remove old architecture from playground
Removes the old architecture from the playground. Next step deletion.
2022-12-19 10:00:16 -08:00
Joe Savona 5db523a98a Fix bug with scope output analysis for nested scopes
When collecting the output of each scope I was checking to see whether each 
operand was being used after the end of the _current_ scope. What we really want 
to be checking is whether the operand is used after _the scope in which it's 
defined_. Those are the same thing when there is no nesting, involved, so the 
previous logic worked for most examples. 

It isn't super easy to tell when if the operand's scope has ended, because we 
don't always know what the "current" InstructionId is inside a ReactiveFunction. 
And that in turn isn't quite so easy to change, because of some edge cases like 
break statements that we synthesize. The solution here is to track the set of 
active scopes, and if an operand is used and its scope is not active then voila, 
it's scope must have completed and its an output.
2022-12-18 12:14:41 -08:00
Joe Savona d649940c27 Test case showing lack of independent memoization of object properties 2022-12-18 12:14:38 -08:00
Joe Savona 05d1fe94f9 [be] Split files into directories
Moves _some_ files from HIR into new top-level directories. To not make 
@gsathya's life a pain I left the files he's touching alone, but I moved some 
others. My intent is to have something like this: 

* Babel/ - code for the Babel plugin, though ideally this actually gets split 
into a separate package and the compiler itself is AST in, AST out w no Babel 
dep. 

* HIR/ - the core HIRFunction, HIR and related data types, plus the HIR 
construction and printing, HIR visitors. 

* Inference/ - the core inference passes that operate on the HIR, including type 
inference, reference effects, alias analysis, mutable range analysis, etc. I'll 
let @gsathya  move these files when at a good stopping point. 

* ReactiveScopes/ - inference relating to reactive scopes, 
constructing/printing/codegenning ReactiveFunction 

* SSA/ - enter/leave SSA and eliminate redundant phis 

* Utils/ - every project needs a place to put stuff that doesn't fit into the 
other categories, this is ours. 

This leaves just index.ts at the top level, and overall feels pretty tidy. Not 
too tedious to figure out where anything goes, hopefully.
2022-12-16 17:25:11 -08:00
Joe Savona 6053703dbb add missing pass to playground
It's pretty tedious to keep the playground in sync w `Pipeline` — we need some 
abstraction so we can write the sequence of passes once and reuse it (while 
inspecting intermediate states).
2022-12-16 09:41:47 -08:00
Joe Savona 075cdcec9e [be] Move old compiler to separate package 2022-12-16 09:33:07 -08:00
Joe Savona 7a29405d68 [be] Rename some types for ReactiveFunction 2022-12-16 08:45:46 -08:00
Tianyu Yao fabef7a6b7 Resubmit Add HydrationSyncLane (#25878)
Depends on #25876

Resubmit #25711 again(previously reverted in #25812), and added the fix
for unwinding in selective hydration during a hydration on the sync
lane.
2022-12-15 12:23:53 -08:00
Tianyu YaoandAndrew Clark 7efa9e5970 Fix unwinding context during selective hydration (#25876)
This PR includes the previously reverted #25695 and #25754, and the fix
for the regression test added in #25867.


Tested internally with a previous failed test,  and it's passing now.

Co-authored-by: Andrew Clark <git@andrewclark.io>
2022-12-15 11:47:07 -08:00
Sathya Gunasekaran 7ab0303822 [playground] Increase min width of output tabs 2022-12-15 18:38:06 +00:00
Sathya Gunasekaran 25fb36c966 [playground] Make gutter smaller in the output tabs 2022-12-15 18:38:05 +00:00
Sathya Gunasekaran 9e9dba90eb [playground] Make input tab smaller 2022-12-15 18:38:05 +00:00
Sathya Gunasekaran 78d976302f [playground] Remove mobile support
This is mostly unused and there's just not enough benefit for now. We can re-add 
this if folks start using this site on mobile. Removing now to simplify the 
codebase.
2022-12-15 18:38:04 +00:00
Sathya Gunasekaran c4f16fce5c [playground] Remove handlebar to resize input
The only use of this is to resize the input tab immediately to be smaller. 
Instead, a follow on PR will just make the input tab smaller by default.
2022-12-15 18:38:03 +00:00
Joe Savona 6efac28677 Prune unnecessary labels in output 2022-12-14 16:42:54 -08:00
Joe Savona de3952352b Use new codegen in playground
Switches to the new codegen with memoization applied, and puts the JS tab first 
to make it easier to look at the input/output 

side by side.
2022-12-14 16:23:41 -08:00
Joe Savona 575db2b4ea [wip] Codegen with memoization applied
Updates the ReactiveFunction-based codegen from the previous PR to emit 
memoization code for each scope. This is currently naive and has some bugs, but 
it gets the idea across. The core logic is straightforward at this point, all 
the hard work is in earlier passes: 

* Compute one change variable per scope dependency, eg `const c_0 = $[0] === 
maxItems` 

* Generate one `let` binding for each scope output 

* Generate an if block where the test is if any of the change variables are true 
(`||` them together) 

* Generate the consequent block with the original code block, plus statements to 
save dependencies and outputs to their cache slots 

* Generate the alternate block to populate the scope outputs from their cached 
values 

## Todos 

A few things don't quite work yet: 

* Codegen is designed to avoid emitting variables for temporary values, but 
that's causing a few values to sort of disappear n the examples, or get emitted 
twice. There are a variety of ways to achieve this but we'll need to ensure that 
this category of values gets assigned to a variable and then reference the 
variable. This is more involved. 

* Scopes can end up with zero dependencies, in which case we should check that 
the first output cache is initialized. This one is more straightforward. 

* If there are early returns, we don't record that they occurred and replay them 
in the `else` branch for each scope. We know the algorithm though so i'm okay 
delaying that for now.
2022-12-14 16:12:36 -08:00
Joe Savona 70a7d5f41e Codegen from ReactiveFunction (no memoization yet)
Currently codegen operates from HIR using a tree visitor, but for scope 
construction we're converting the HIR (CFG) into a ReactiveFunction (AST-like). 
Our original idea for codegen was that we would convert the ReactiveFunction 
back to HIR, and then codegen from there. However, the ReactiveFunction is 
already in tree form...which makes it very straightforward to generate code 
from. 

So this PR implements codegen from ReactiveFunction. The output is _identical_ 
thanks in large part to reusing as much logic from Codegen.ts as possible. The 
next PR will add memoization logic.
2022-12-14 14:54:37 -08:00
Joe Savona 7199229cb4 Record scope outputs
While we're collecting scope dependencies, we have the exact right information 
to record scope outputs. These are variables that need to be defined outside of 
the scope and populated by recomputing (on change) or via the cached value (if 
no change).
2022-12-14 13:27:20 -08:00
Sebastian Markbåge 84a0a171ea Rename experimental useEvent to useEffectEvent (#25881)
We originally had grand plans for using this Event concept for more but
now it's only meant to be used in combination with effects.

It's an Event in the FRP terms, that is triggered from an Effect.
Technically it can also be from another function that itself is
triggered from an existing side-effect but that's kind of an advanced
case.

The canonical case is an effect that triggers an event:

```js
const onHappened = useEffectEvent(() => ...);
useEffect(() => {
  onHappened();
}, []);
```
2022-12-14 15:08:29 -05:00
Joe Savona 459263b763 Refactor reactive scope representation 2022-12-14 12:07:50 -08:00
Joe Savona 552fe1878b Cleanup previous dep collection 2022-12-14 08:42:39 -08:00
Joe Savona dd1ff4dc30 Combine dependency collection and propagation, more precise collection
I realized that properly propagating scope dependencies requires reusing the 
same logic as dependency collection itself: a dependency of an inner scope 
should only be propagated upward if the dependency was declared before the outer 
scope, for example. So this PR reimplements dependency collection in the 
propagation pass. 

At the same time I made a few other improvements: 

* Don't report dependencies that are "constant". This is a bit simplistic for 
now, we can use a more advanced analysis later. 

* Try to avoid creating duplicate dependencies. 

This addresses the todo from the previous PR (flattening scopes in loops) since 
now we don't need to compute deps until after that runs. As a follow-up i'll 
remove the existing dependency collection.
2022-12-14 08:42:39 -08:00
Joe Savona d909ae0434 Fix scope dependency collection ordering bug
Dependency collection has to visit the instruction id first before evaluating 
the instruction, in order to completely any scopes that would end at that 
instruction. Note the removed dependencies that don't appear within the scopes.
2022-12-14 08:42:38 -08:00
Joe Savona 972caf5bf1 Propagate scope dependencies upwards
Initial, partial implementation of dependency propagation. This lays the 
groundwork, #876 completes it after some other fixes this uncovered.
2022-12-14 08:42:37 -08:00
Joe Savona 5f3dc0b6c7 Flatten scopes within loops
We can't independently memoize values created within a loop, so this pass 
flattens scopes within loops. Right now this just flattens the scope away 
without propagating any dependency (or output) information, follow-ups will 
extend it to do that.
2022-12-14 08:42:37 -08:00
Joe Savona be9b6356df cleanup unnecessary code from BuildReactiveFunction 2022-12-14 08:42:36 -08:00
Joe Savona 189f48ee0a Dont create scopes for consts w primitive values
We don't need to create scopes for primitive values that are never reassigned. 
The actual rules are more complex — we could choose to skip creating scopes for 
values that don't allocate — but this simple heuristic is good for now.
2022-12-14 08:42:36 -08:00
Joe Savona 73ad571c6b Add invariant for problematic LeaveSSA case
The new LeaveSSA looks ahead to the phis of fallback blocks. However, HIR can 
sometimes have multiple blocks with the same fallthrough (totally fine), so this 
diff clears the phis of fallbacks as they are reached to avoid reprocessing 
them. This caused a previously incorrect case to now fail, yay.
2022-12-14 08:42:35 -08:00
Sathya Gunasekaran 4b66531237 [test] Remove mermaid diagrams from test output 2022-12-14 15:47:13 +00:00
Sathya Gunasekaran 9bfad00410 [type-infer] Implement Hindley Milner type inference
Based on the lambda calculus of ForgetScript, this infers the types of 
primitives, objects and functions.
2022-12-14 15:36:32 +00:00