diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts index 92c95323cd566..bf3ba01ddecc2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/BuildReactiveGraph.ts @@ -31,7 +31,6 @@ import { ControlNode, EntryNode, InstructionNode, - JoinNode, LoadArgumentNode, LoadNode, makeReactiveId, @@ -45,6 +44,8 @@ import { reversePostorderReactiveGraph, StoreNode, eachNodeDependency, + FallthroughNode, + printReactiveGraph, } from './ReactiveIR'; export function buildReactiveGraph(fn: HIRFunction): ReactiveGraph { @@ -73,7 +74,10 @@ export function buildReactiveGraph(fn: HIRFunction): ReactiveGraph { const exitNode = buildBlockScope(fn, context, fn.body.entry, entryNode.id); - const graph = builder.build(fn, exitNode); + const graph = builder.build(fn, entryNode.id, exitNode); + + console.log(); + console.log(printReactiveGraph(graph)); populateReactiveGraphNodeOutputs(graph); reversePostorderReactiveGraph(graph); @@ -85,11 +89,12 @@ class Builder { #environment: Map = new Map(); #nodes: Map = new Map(); - build(fn: HIRFunction, exit: ReactiveId): ReactiveGraph { + build(fn: HIRFunction, entry: ReactiveId, exit: ReactiveId): ReactiveGraph { const graph: ReactiveGraph = { async: fn.async, directives: fn.directives, env: fn.env, + entry, exit, fnType: fn.fnType, generator: fn.generator, @@ -494,24 +499,16 @@ function buildBlockScope( from: {...terminal.test}, as: {...terminal.test}, }; - const branch: BranchNode = { - kind: 'Branch', - control, - dependencies: [], - id: context.nextReactiveId, - loc: terminal.loc, - outputs: [], - }; - context.putNode(branch); - const joinNodeId = context.nextReactiveId; + const branchNodeId = context.nextReactiveId; + const fallthroughNodeId = context.nextReactiveId; const joinFallthrough = { kind: 'If', block: terminal.fallthrough, - fallthrough: joinNodeId, + fallthrough: fallthroughNodeId, } as const; const consequentContext = context.fork(joinFallthrough); const consequentControl = consequentContext.controlNode( - branch.id, + branchNodeId, terminal.loc, ); const consequent = buildBlockScope( @@ -522,7 +519,7 @@ function buildBlockScope( ); const alternateContext = context.fork(joinFallthrough); const alternateControl = alternateContext.controlNode( - branch.id, + branchNodeId, terminal.loc, ); const alternate = @@ -534,19 +531,37 @@ function buildBlockScope( alternateControl, ) : alternateControl; - const ifNode: JoinNode = { - kind: 'Join', - control: branch.id, - id: joinNodeId, + + const branch: BranchNode = { + kind: 'Branch', + control, + dependencies: [], + id: branchNodeId, loc: terminal.loc, outputs: [], + fallthrough: fallthroughNodeId, terminal: { kind: 'If', test, - consequent, - alternate, + consequent: { + entry: consequentControl, + exit: consequent, + }, + alternate: { + entry: alternateControl, + exit: alternate, + }, }, }; + context.putNode(branch); + const ifNode: FallthroughNode = { + kind: 'Fallthrough', + control: branch.id, + id: fallthroughNodeId, + loc: terminal.loc, + outputs: [], + branches: [consequent, alternate], + }; const predecessors: Array<{ enter: ReactiveId; diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts index 1bc41ee52c39e..4bf766c068d24 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveIR/ReactiveIR.ts @@ -22,6 +22,7 @@ import {assertExhaustive} from '../Utils/utils'; export type ReactiveGraph = { nodes: Map; nextNodeId: number; + entry: ReactiveId; exit: ReactiveId; loc: SourceLocation; id: string | null; @@ -57,7 +58,7 @@ export type ReactiveNode = | LoadArgumentNode | InstructionNode | BranchNode - | JoinNode + | FallthroughNode | ControlNode | ReturnNode | GotoNode; @@ -146,24 +147,26 @@ export type BranchNode = { outputs: Array; dependencies: Array; // values/scopes depended on by more than one branch, or by the terminal control: ReactiveId; + fallthrough: ReactiveId; + terminal: BranchTerminal; }; -export type JoinNode = { - kind: 'Join'; - id: ReactiveId; - loc: SourceLocation; - outputs: Array; - terminal: NodeTerminal; - control: ReactiveId; // join node always has a control, which is the corresponding Branch node -}; - -export type NodeTerminal = IfBranch; +export type BranchTerminal = IfBranch; export type IfBranch = { kind: 'If'; test: NodeReference; - consequent: ReactiveId; - alternate: ReactiveId; + consequent: {entry: ReactiveId; exit: ReactiveId}; + alternate: {entry: ReactiveId; exit: ReactiveId}; +}; + +export type FallthroughNode = { + kind: 'Fallthrough'; + id: ReactiveId; + loc: SourceLocation; + outputs: Array; + control: ReactiveId; // always the corresponding branch node + branches: Array; // the other control-flow paths that reach the fallthrough }; export type ControlNode = { @@ -238,6 +241,16 @@ export function reversePostorderReactiveGraph(graph: ReactiveGraph): void { graph.nodes = nodes; } +export function* eachBranchTerminalDependency( + terminal: BranchTerminal, +): Iterable { + switch (terminal.kind) { + case 'If': { + yield terminal.test.node; + } + } +} + export function* eachNodeDependency(node: ReactiveNode): Iterable { switch (node.kind) { case 'Entry': @@ -245,15 +258,17 @@ export function* eachNodeDependency(node: ReactiveNode): Iterable { break; } case 'Goto': - case 'Control': + case 'Control': { + yield* node.dependencies; + break; + } case 'Branch': { yield* node.dependencies; + yield* eachBranchTerminalDependency(node.terminal); break; } - case 'Join': { - yield node.terminal.test.node; - yield node.terminal.consequent; - yield node.terminal.alternate; + case 'Fallthrough': { + yield* node.branches; break; } case 'Load': { @@ -282,6 +297,17 @@ export function* eachNodeDependency(node: ReactiveNode): Iterable { } } +export function* eachBranchTerminalReference( + terminal: BranchTerminal, +): Iterable { + switch (terminal.kind) { + case 'If': { + yield terminal.test; + break; + } + } +} + export function* eachNodeReference( node: ReactiveNode, ): Iterable { @@ -305,10 +331,10 @@ export function* eachNodeReference( break; } case 'Branch': { + yield* eachBranchTerminalReference(node.terminal); break; } - case 'Join': { - yield node.terminal.test; + case 'Fallthrough': { break; } case 'Value': { @@ -415,14 +441,12 @@ function writeReactiveNodes( buffer.push( `£${id} Branch deps=[${node.dependencies.map(id => `£${id}`).join(', ')}]${control}`, ); - break; - } - case 'Join': { - buffer.push(`£${id} Join${control}`); switch (node.terminal.kind) { case 'If': { buffer.push( - ` If test=${printNodeReference(node.terminal.test)} consequent=£${node.terminal.consequent} alternate=£${node.terminal.alternate}${control}`, + ` If test=${printNodeReference(node.terminal.test)} ` + + `consequent=£${node.terminal.consequent.entry}:${node.terminal.consequent.exit} ` + + `alternate=£${node.terminal.alternate.entry}:${node.terminal.alternate.exit}`, ); break; } @@ -432,6 +456,12 @@ function writeReactiveNodes( } break; } + case 'Fallthrough': { + buffer.push( + `£${id} Fallthrough${control} branches=[${node.branches.map(id => `£${id}`).join(', ')}]`, + ); + break; + } case 'Value': { const deps = [...eachNodeReference(node)] .map(id => printNodeReference(id))