Skip to content

Commit

Permalink
Update on "[compiler] ReactiveIR: add Let/Assign/Branch/Join nodes"
Browse files Browse the repository at this point in the history
Further exploration of the new ReactiveIR sea-of-nodes approach, adding Load/Store/Branch/Join nodes. This has evolved a bit relative to the doc, but the doc still gives a decent idea (Let/Const/Assign are all collapsed to Store now), and we have an explicit Load to ensure loads are sequenced correctly.

Notable things in the implementation are the ordering of variable loads/stores and scope mutations.

## Ordering Loads/Stores

The rules are:
* Reads after writes: reads have a control dependency that ensures they come after the write they should be reading
* Writes after reads: subsequent writes come after any reads of previous writes, or if no reads, after the previous write

In both cases, the ordering only applies within consecutive control flow. The first read or write of a variable within a given branch of control flow will only take a dependency on the block's entry node (the default control).

This ordering extends to conditional control flow. For Join nodes, we look at all the reads/writes which occurred in any of the branches, and then consider the join itself to be either a write or read of those variables. 

So in the following:

```js
let x = 0;
if (cond) {
  read(x);
}
x = 1;
```

We establish two ordering conditions:
* The Branch node for the if has a control dependency on `let x = 0`, since one of the branches needs to read that version of `x`
* The Join node for the if acts as a read of `x`, such that the `x = 1` has a control dependency on the if. This ensures the write happens after the read.

## Ordering Scope Mutations

The only rule here is that all the mutations affecting a particular scope must remain in their original order. Within linear control flow we accomplish this by tracking the last node which mutated each scope, and setting the previous such node as the control of the next one.

For conditional control flow, we take the union of all the scopes mutated within any of the branches. The Branch node takes a control on the previous mutating node for each scope (ensuring the branch comes after those earlier mutations), and then the Join node is set as the last mutation of each scope (ensuring any subsequent mutations of those scopes stay after).

## Todos (for follow-ups)

The main things not completed yet wrt to the branch/join infra are:
* handling other terminal types
* phis (though i'm actually wondering about converting directly to ReactiveFunction and not needing to preserve them!)




[ghstack-poisoned]
  • Loading branch information
josephsavona committed Jan 8, 2025
2 parents 312362f + b677e81 commit f8806af
Showing 0 changed files with 0 additions and 0 deletions.

0 comments on commit f8806af

Please sign in to comment.