Commit Graph
1708 Commits
Author SHA1 Message Date
Joe Savona ab150bd9d1 Object and array destructuring support in declaration/assignment
Implements support for array and object de-structuring in variable declarations 
and assignment expressions. Note that the code currently makes the overly 
optimistic assumption that the RHS is an array or object that can be safely 
indexed into. The correct representation would instead treat the RHS as possibly 
iterable, but we need to consider the appropriate representation. I think it's 
worth landing a first optimistic pass and we can iterate forward, this helps 
make it more clear what the ideal representation would have to be and should 
make a bunch of examples work. It also allows us to experiment with 
representations of, and handling for, scope dependencies that involve computed 
property access.
2023-01-03 16:59:38 -08:00
Joe Savona 45b3bd4899 Foundation for IndexStore/IndexLoad (no lowering)
Adds new types for IndexLoad/IndexStore (renamed later in the stack to 
ComputedLoad/ComputedStore) which will be used to represent computed property 
access/update. The actual lowering to use these is later in the stack.
2023-01-03 16:59:35 -08:00
Joe Savona c2b2df0d17 Remove unused Place.memberPath
After the previous PR to change the scope dependency representation, 
`Place.memberPath` is now completely unused, this PR deletes that field and all 
references. Rejoice!
2023-01-03 15:34:42 -08:00
Joe Savona c69cba357f Use distinct type for scope dependencies
This is a pre-req to deleting the `Place.memberPath` field. We no longer need 
memberPath in the HIR now that we have PropertyStore and PropertyLoad. However, 
scope dependencies use memberPath to track the precise fields that a computation 
depends upon. This PR changes scopes dependencies to use a new 
`ReactiveScopeDependency` type (Place + optional path), which allows the next PR 
to remove `Place.memberPath`.
2023-01-03 15:34:39 -08:00
Joe Savona 26cd435c7d Support chained assignment expressions
Fixes codegen for chained assignment expressions. Previously each intermediate 
assignment would be generated independently _in addition_ to the final chained 
expression being emitted. We now emit a single chained expression, almost 
exactly matching the input except for expanding from `x += 1` into `x = x + 1`. 

There are two key changes: 

* Ensuring that assignment expressions always generate an lvalue, which is 
necessary for alias analysis to kick in, since it relies on the effect of the 
lvalue to know where to look for aliasing. 

* The above makes codegen think the entire assignment expression value is a 
temporary that can be emitted later, but that isn't true. The new 
PruneTemporaryLValue pass nulls out lvalues that are never read later, ensuring 
that codegen can eagerly emit the value instead of saving it as a temporary.
2023-01-03 14:11:04 -08:00
Joe Savona 0a6af823b3 Lowering for PropertyStore
Converts assignment expressions where the lvalue is a MemberExpression to use 
PropertyStore, rather than creating an lvalue with a member path. The net effect 
is that lvalue will always have a null member path. 

There are two main cases: 

* `x.y = <value>`. We lower <value> to a Place, lower the object of the member 
expression to a place (`object)` create a temporary Place for the result of the 
assignment, and then create a `PropertyStore <object>, "<property>", <value>`. 

* `x.y += <value>` (and similar update-in-place operands). We extract the object 
of the member expression, read the current value via a PropertyLoad, compute the 
updated value, and store it back with a PropertyStore (each of these goes into 
its own temporary).
2022-12-22 15:04:57 -08:00
Joe Savona c7a84fc033 Foundation for PropertyStore (no lowering yet)
Adds a `InstructionValue::PropertyStore` variant and the minimal necessary 
handling across the passes to get things to compile. Lowering is in the next PR.
2022-12-22 14:51:27 -08:00
lauren 7ee40764e6 Revert "[nocommit] Run test262 on this PR to verify it works for real…
…-realsies" 

This reverts commit 3c8700cbc7502e56cca8d4bb77a08e1ee747c166. 

I'm not sure how this happened but I accidentally hit up + enter on a previous 
`ghstack land` command for a different stack and it landed this one. 
Infuriating.
2022-12-22 17:53:59 -05:00
Lauren Tan 2d4c2094d8 [@nocommit] Run test262 on this PR to verify it works for real-realsies
final v2
2022-12-22 17:40:20 -05:00
Lauren Tan 5e0dba9012 [test262] Always return a pseudo Error object
For unknown reasons I didn't have the energy to dig into, some Babel Error 
objects can't be written to so despite formatting the error message, the 
original one would still be used. To get around this I'm just constructing a 
fake object with a `name` and `message` so the correctly formatted messages are 
used.
2022-12-22 17:40:18 -05:00
Joe Savona 74a0d54db2 Account for stores that can occur in object/array literals 2022-12-22 14:00:49 -08:00
Joe Savona 1fd4bad525 RHS member expression converts to PropertyLoad
This is an incremental step to removing `Place.memberPath`. This PR changes how 
we handle MemberExpressions in rvalue position, converting to a new 
`PropertyLoad` InstructionValue variant. Example: 

``` 

let x = a.b; 

x.y = b.c; 

=> 

Const tmp1 = PropertyLoad a, 'b'; 

Const x = Place tmp1; 

Const tmp2 = PropertyLoad b, 'c'; 

Reassign x.y = tmp2 

``` 

That we already recently made a chance to ensure that _if_ the lvalue is a 
member expression, that we convert the RHS to a Place. So although `x.y = b.c` 
could technically be lowered to a single instruction (with the`b.c` as a 
PropertyLoad), we force this to a temporary to ensure that we can independently 
memoize the RHS value. 

The net result is that the following combinations are possible: 

* `x = y`, lvalue identifier, rvalue identifier 

* `x.y = y` lvalue member path, rvalue identifier 

* `x = y.z` lvalue identifier, rvalue property load 

As noted above, `x.y = a.b` no longer occurs (and there's an invariant for this 
in one of the passes). 

A follow-up PR will add a PropertyStore instruction so that we can remove member 
paths in lvalue position too.
2022-12-22 14:00:48 -08:00
Lauren Tan 8246956331 Force DisjointSet.union to always pick a root
@josephsavona had the intuition that we were picking the wrong root in the 
previous infinite loop test case, so the fix for this is to force a root to 
always be picked. This works because `find` implements path compression (if a <- 
b and b <- c, then we can just point a <- c to "flatten" the tree which makes 
subsequent `find` operations more efficient since we don't have to follow the 
ancestor chain each time), so we're forcing all those unions to pick one parent. 

From some googling it looks like the traditional way to implement union is to 
call `find` so this should be the "right" way to fix it (?). 

I'm also adding a basic unit test for DisjointSet, I think we could revisit 
later and see if property testing is worth it but for now I mainly wanted to 
capture the regression test as a unit test.
2022-12-22 16:16:16 -05:00
Lauren Tan 311e5fdf69 Add infinite loop test case
Discovered this by accident modifying Joe's playground example. This seemed to 
be triggered in inferReactiveScopeVariables when iterating over the DisjointSet 
of scopeIdentifiers. In particular this test case contains a cycle and 
DisjointSet.find would never terminate.
2022-12-22 16:16:16 -05:00
Lauren Tan 75fc20981a Fix test262 for realsies
The github action was exceeding maximum allowed memory size because we were no 
longer grouping messages correctly prior to formatting them in the script. I 
think these were introduced when we integrated the Babel plugin into the 
preprocessor. This PR strips out filenames from the message so they can be 
grouped together again. Also added some light comments 

Test plan: manually ran `scripts/test262.sh` and verified that the JSON was 
grouped together correctly
2022-12-22 13:12:08 -05:00
Joe Savona 47e20c3f66 Expect files are input/ouput code only (no HIR/scopes) 2022-12-22 10:23:14 -08:00
Joe Savona 4385a283e4 Ensure non-conflicting names in output, wo suffixes by default 2022-12-22 10:23:11 -08:00
Lauren Tan b5623abf74 [ez] Update test262 submodule
`git submodule update --remote`
2022-12-21 16:07:11 -05:00
Lauren Tan 003a1772ab Fix broken import path in test262-preprocessor
Previously we weren't ever clearing the `dist` directory so there were likely 
some vestigial files left over from previous builds that might have confused the 
import path. This commit fixes the import path and also deletes the `dist` 
directory on every build to ensure a clean slate.
2022-12-21 16:04:29 -05:00
Sathya Gunasekaran f72397b5c1 [typer] Infer JSXText as a Primitive 2022-12-21 21:13:58 +00:00
Sathya Gunasekaran 6ba2117227 [hir] Use type inference for Effect.Store refinement
Only identifiers of type object can be safely refined to Effect.Store, default 
unknown identifiers to Effect.Mutate.
2022-12-21 21:13:58 +00:00
Sathya Gunasekaran ff29264050 [typer] Remove type inference of fields
Given that our type inference needs to be very conservative, there's not a lot 
of benefit to having such fine grained type inference. 

In the future, we can use type information from flow/ts for inference.
2022-12-21 21:13:57 +00:00
Sathya Gunasekaran b59f9dd561 [hir] Remove unused code from InferAlias
For now, we've decided to punt on super fine grained aliasing of fields (ie, 
mutating x.y shouldn't mutate x.z or it's aliases). 

This PR removes the code that tracks the aliases of each field. We can re-add 
this when we revisit this functionality. 

For the rest of the field aliasing, most of it has been replaced by 
InferAliasForStores.
2022-12-21 21:13:57 +00:00
Joe Savona 8c920cfa90 Factor out assignment expression cases
Splits handling of simple assignment updates (`=`) from update-assignments (`+=` 
etc). This unblocks starting to support destructuring for the simple case in the 
subsequent PR.
2022-12-21 10:02:06 -08:00
Joe Savona 69e34270a2 Factor out assignment lowering to allow generating multiple instructions 2022-12-21 10:02:06 -08:00
Joe Savona 836211a549 Prep for destructuring, factor out lowerIdentifier() 2022-12-20 16:57:13 -08:00
Joe Savona 079cfe68bf Fix test262 preprocessor
The test262 preprocessor was correctly running forget against all the functions 
in each test file, but it was incorrectly transforming the file contents 
overall. Previously we swapped the contents of the file for the transformed 
output of the last function, now we use the babel plugin to rewrite functions in 
place and keep the rest of the file intact. 

Of course, the tests that failed before still fail. But when we fix them the 
tests will work now.
2022-12-20 16:27:11 -08:00
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
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