mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[playground] new HIR tab
Instead of hacking into the babel plugin passes, this now takes a new approach for the HIR tab: - The different passes are exported from the babel plugin (for simplicity) - The tab actually runs the compiler steps based on local config in the tab. - Re-purposed the CompilerFlags component to configure what passes to run. This is currently mostly causing different errors, but could be useful going forward as a direction.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import clsx from "clsx";
|
||||
|
||||
import { HIR } from "babel-plugin-react-forget";
|
||||
import { useState } from "react";
|
||||
|
||||
const {
|
||||
parseFunctions,
|
||||
Environment,
|
||||
enterSSA,
|
||||
eliminateRedundantPhi,
|
||||
inferReferenceEffects,
|
||||
inferMutableRanges,
|
||||
leaveSSA,
|
||||
lower,
|
||||
printHIR,
|
||||
} = HIR;
|
||||
|
||||
export default function HIRTabContent({ source }: { source: string }) {
|
||||
const astFunctions = parseFunctions(source);
|
||||
|
||||
const [flags, setFlags] = useState({
|
||||
eliminateRedundantPhi: true,
|
||||
inferReferenceEffects: true,
|
||||
inferMutableRanges: true,
|
||||
leaveSSA: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex-1">
|
||||
{astFunctions.map((func, index): React.ReactNode => {
|
||||
let body;
|
||||
try {
|
||||
const env = new Environment();
|
||||
const ir = lower(func, env);
|
||||
enterSSA(ir, env);
|
||||
if (flags.eliminateRedundantPhi) {
|
||||
eliminateRedundantPhi(ir);
|
||||
}
|
||||
if (flags.inferReferenceEffects) {
|
||||
inferReferenceEffects(ir);
|
||||
}
|
||||
if (flags.inferMutableRanges) {
|
||||
inferMutableRanges(ir);
|
||||
}
|
||||
if (flags.leaveSSA) {
|
||||
leaveSSA(ir);
|
||||
}
|
||||
const textHIR = printHIR(ir.body);
|
||||
body = <pre>{textHIR}</pre>;
|
||||
} catch (e: any) {
|
||||
body = <div>error: ${e.toString()}</div>;
|
||||
}
|
||||
const name = func.node.id?.name ?? "anonymous";
|
||||
return (
|
||||
<div key={index} className="border-b py-4 px-2">
|
||||
<h3 className="font-medium mb-2">Function: {name}</h3>
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<CompilerFlagsEditor flags={flags} setFlags={setFlags} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type Flags = {
|
||||
eliminateRedundantPhi: boolean;
|
||||
inferReferenceEffects: boolean;
|
||||
inferMutableRanges: boolean;
|
||||
leaveSSA: boolean;
|
||||
};
|
||||
|
||||
function CompilerFlagsEditor({
|
||||
flags,
|
||||
setFlags,
|
||||
}: {
|
||||
flags: Flags;
|
||||
setFlags: (flags: Flags) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
|
||||
<h3 className="font-medium">Compiler Options</h3>
|
||||
<div className="flex flex-col">
|
||||
<LabeledCheckbox
|
||||
label="Eliminate Redundant Phi"
|
||||
value={flags.eliminateRedundantPhi}
|
||||
onChange={(eliminateRedundantPhi) => {
|
||||
setFlags({ ...flags, eliminateRedundantPhi });
|
||||
}}
|
||||
/>
|
||||
<LabeledCheckbox
|
||||
label="Infer Reference Effects"
|
||||
value={flags.inferReferenceEffects}
|
||||
onChange={(inferReferenceEffects) => {
|
||||
setFlags({ ...flags, inferReferenceEffects });
|
||||
}}
|
||||
/>
|
||||
<LabeledCheckbox
|
||||
label="Infer Mutable Ranges"
|
||||
value={flags.inferMutableRanges}
|
||||
onChange={(inferMutableRanges) => {
|
||||
setFlags({ ...flags, inferMutableRanges });
|
||||
}}
|
||||
/>
|
||||
<LabeledCheckbox
|
||||
label="Leave SSA"
|
||||
value={flags.leaveSSA}
|
||||
onChange={(leaveSSA) => {
|
||||
setFlags({ ...flags, leaveSSA });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LabeledCheckbox({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
label: string;
|
||||
value: boolean;
|
||||
onChange: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<label className="flex gap-1.5 text-base">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
<span className={clsx({ "text-gray-500": !value, "text-black": value })}>
|
||||
{label}
|
||||
</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { getSelectedFile, type Store } from "../../lib/stores";
|
||||
import GraphView from "../GraphView";
|
||||
import Preview from "../Preview";
|
||||
import { monacoOptions } from "./monacoOptions";
|
||||
import HIRTabContent from "./HIRTabContent";
|
||||
|
||||
const MemoizedOutput = memo(Output);
|
||||
|
||||
@@ -43,7 +44,7 @@ function Output({ store, updateDiagnostics }: Props) {
|
||||
tabs={{
|
||||
Preview: <Preview store={store} />,
|
||||
IR: <TextTabContent outputs={outputs} kind={OutputKind.IR} />,
|
||||
HIR: <TextTabContent outputs={outputs} kind={OutputKind.HIR} />,
|
||||
HIR: <HIRTabContent source={getSelectedFile(store).content} />,
|
||||
CFG: <TextTabContent outputs={outputs} kind={OutputKind.CFG} />,
|
||||
ValGraph: (
|
||||
<GraphTabContent outputs={outputs} kind={OutputKind.ValGraph} />
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { useDrag } from "@use-gesture/react";
|
||||
@@ -89,7 +92,6 @@ export default function Editor() {
|
||||
})}
|
||||
>
|
||||
<Output store={deferredStore} updateDiagnostics={updateDiagnostics} />
|
||||
<CompilerFlagsEditor />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@@ -10,7 +10,6 @@ import * as t from "@babel/types";
|
||||
import * as BE from "./BackEnd";
|
||||
import { CompilerContext } from "./CompilerContext";
|
||||
import { CompilerOptions } from "./CompilerOptions";
|
||||
import DumpHIRPass from "./HIR/DumpHIRPass";
|
||||
import * as ME from "./MiddleEnd";
|
||||
import { PassManager } from "./PassManager";
|
||||
import * as Validation from "./Validation";
|
||||
@@ -38,10 +37,6 @@ export function createCompilerDriver(
|
||||
compile() {
|
||||
const passManager = new PassManager(program, context);
|
||||
|
||||
// New architecture pass to print HIR, unrelated to the
|
||||
// other passes here and just here for the playground.
|
||||
passManager.addPass(DumpHIRPass);
|
||||
|
||||
// Syntax Analysis and IR Generation.
|
||||
passManager.addPass(ME.ReactFuncsInfer);
|
||||
passManager.addPass(ME.ParamAnalysis);
|
||||
|
||||
@@ -16,7 +16,6 @@ import { SCCGraphSnapshot, ValGraphSnapshot } from "./DepGraph";
|
||||
*/
|
||||
export enum OutputKind {
|
||||
IR = "DumpIR",
|
||||
HIR = "DumpHIR",
|
||||
CFG = "DumpCFG",
|
||||
ValGraph = "DumpValGraph",
|
||||
SCCGraph = "DumpSCCGraph",
|
||||
@@ -34,7 +33,6 @@ export function isSyntacticallyValidJS(kind: OutputKind) {
|
||||
|
||||
export interface CompilerOutputs {
|
||||
[OutputKind.IR]: string;
|
||||
[OutputKind.HIR]: string;
|
||||
[OutputKind.CFG]: string;
|
||||
[OutputKind.ValGraph]: ValGraphSnapshot[];
|
||||
[OutputKind.SCCGraph]: SCCGraphSnapshot[];
|
||||
@@ -46,7 +44,6 @@ export interface CompilerOutputs {
|
||||
export function createCompilerOutputs(): CompilerOutputs {
|
||||
return {
|
||||
[OutputKind.IR]: "",
|
||||
[OutputKind.HIR]: "",
|
||||
[OutputKind.CFG]: "",
|
||||
[OutputKind.ValGraph]: [],
|
||||
[OutputKind.SCCGraph]: [],
|
||||
@@ -67,7 +64,6 @@ export function stringifyCompilerOutputs(
|
||||
): PrettyCompilerOutputs {
|
||||
const outputs: PrettyCompilerOutputs = {
|
||||
[OutputKind.IR]: undefined,
|
||||
[OutputKind.HIR]: undefined,
|
||||
[OutputKind.CFG]: undefined,
|
||||
[OutputKind.ValGraph]: undefined,
|
||||
[OutputKind.SCCGraph]: undefined,
|
||||
@@ -95,10 +91,7 @@ export function stringifyCompilerOutputs(
|
||||
console.warn("Error formatting output:", e);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
outputKind === OutputKind.CFG ||
|
||||
outputKind === OutputKind.HIR
|
||||
) {
|
||||
} else if (outputKind === OutputKind.CFG) {
|
||||
outputs[outputKind] = output;
|
||||
} else {
|
||||
invariant(Array.isArray(output), "Graph outputs are arrays.");
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import type { CompilerContext } from "../CompilerContext";
|
||||
import { OutputKind } from "../CompilerOutputs";
|
||||
import { PassKind, PassName } from "../Pass";
|
||||
|
||||
import { lower } from "../HIR/BuildHIR";
|
||||
import { eliminateRedundantPhi } from "../HIR/EliminateRedundantPhi";
|
||||
import enterSSA from "../HIR/EnterSSA";
|
||||
import { inferMutableRanges } from "../HIR/InferMutableLifetimes";
|
||||
import inferReferenceEffects from "../HIR/InferReferenceEffects";
|
||||
import printHIR from "../HIR/PrintHIR";
|
||||
import { Environment } from "./HIRBuilder";
|
||||
import leaveSSA from "./LeaveSSA";
|
||||
|
||||
export default {
|
||||
name: PassName.DumpHIR,
|
||||
kind: PassKind.Prog as const,
|
||||
run,
|
||||
};
|
||||
|
||||
/**
|
||||
* A step in the first approach compiler pipeline that outputs
|
||||
* the new architecture's HIR for the playground.
|
||||
*/
|
||||
export function run(program: NodePath<t.Program>, context: CompilerContext) {
|
||||
if (!context.opts.outputKinds.includes(OutputKind.HIR)) {
|
||||
return;
|
||||
}
|
||||
const results: string[] = [];
|
||||
program.traverse({
|
||||
Function(func) {
|
||||
try {
|
||||
const env = new Environment();
|
||||
const ir = lower(func, env);
|
||||
enterSSA(ir, env);
|
||||
eliminateRedundantPhi(ir);
|
||||
inferReferenceEffects(ir);
|
||||
inferMutableRanges(ir);
|
||||
leaveSSA(ir);
|
||||
const textHIR = printHIR(ir.body);
|
||||
results.push(textHIR);
|
||||
} catch (e) {
|
||||
results.push(`Error: ${e}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
context.outputs[OutputKind.HIR] = results.join("\n\n");
|
||||
}
|
||||
@@ -21,4 +21,49 @@ export * from "./Diagnostic";
|
||||
export * from "./Logger";
|
||||
export { NoUseBeforeDefineRule } from "./Validation";
|
||||
|
||||
import { lower } from "./HIR/BuildHIR";
|
||||
import { eliminateRedundantPhi } from "./HIR/EliminateRedundantPhi";
|
||||
import enterSSA from "./HIR/EnterSSA";
|
||||
import { inferMutableRanges } from "./HIR/InferMutableLifetimes";
|
||||
import inferReferenceEffects from "./HIR/InferReferenceEffects";
|
||||
import printHIR from "./HIR/PrintHIR";
|
||||
import { Environment } from "./HIR/HIRBuilder";
|
||||
import leaveSSA from "./HIR/LeaveSSA";
|
||||
import traverse, { NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import { parse } from "@babel/parser";
|
||||
|
||||
function parseFunctions(
|
||||
source: string
|
||||
): Array<NodePath<t.FunctionDeclaration>> {
|
||||
try {
|
||||
const ast = parse(source, {
|
||||
plugins: ["typescript", "jsx"],
|
||||
});
|
||||
const items: Array<NodePath<t.FunctionDeclaration>> = [];
|
||||
traverse(ast, {
|
||||
FunctionDeclaration: {
|
||||
enter(nodePath) {
|
||||
items.push(nodePath);
|
||||
},
|
||||
},
|
||||
});
|
||||
return items;
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export const HIR = {
|
||||
parseFunctions,
|
||||
lower,
|
||||
eliminateRedundantPhi,
|
||||
enterSSA,
|
||||
inferMutableRanges,
|
||||
inferReferenceEffects,
|
||||
printHIR,
|
||||
Environment,
|
||||
leaveSSA,
|
||||
};
|
||||
|
||||
export default BabelPlugin;
|
||||
|
||||
Reference in New Issue
Block a user