@samitouri / QOS-React-2 / commits / 779947520d

[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.

Jan Kassens committed Nov 10, 2022 at 15:45 UTC 779947520d0aa8fd75deb0f1440f61004299f5cd
7 files changed +202 -73
compiler/forget/packages/playground/components/Editor/HIRTabContent.tsx new
+149
@@ -0,0 +1,149 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import clsx from "clsx";
9 +
10 +import { HIR } from "babel-plugin-react-forget";
11 +import { useState } from "react";
12 +
13 +const {
14 + parseFunctions,
15 + Environment,
16 + enterSSA,
17 + eliminateRedundantPhi,
18 + inferReferenceEffects,
19 + inferMutableRanges,
20 + leaveSSA,
21 + lower,
22 + printHIR,
23 +} = HIR;
24 +
25 +export default function HIRTabContent({ source }: { source: string }) {
26 + const astFunctions = parseFunctions(source);
27 +
28 + const [flags, setFlags] = useState({
29 + eliminateRedundantPhi: true,
30 + inferReferenceEffects: true,
31 + inferMutableRanges: true,
32 + leaveSSA: true,
33 + });
34 +
35 + return (
36 + <div className="h-full flex flex-col">
37 + <div className="flex-1">
38 + {astFunctions.map((func, index): React.ReactNode => {
39 + let body;
40 + try {
41 + const env = new Environment();
42 + const ir = lower(func, env);
43 + enterSSA(ir, env);
44 + if (flags.eliminateRedundantPhi) {
45 + eliminateRedundantPhi(ir);
46 + }
47 + if (flags.inferReferenceEffects) {
48 + inferReferenceEffects(ir);
49 + }
50 + if (flags.inferMutableRanges) {
51 + inferMutableRanges(ir);
52 + }
53 + if (flags.leaveSSA) {
54 + leaveSSA(ir);
55 + }
56 + const textHIR = printHIR(ir.body);
57 + body = <pre>{textHIR}</pre>;
58 + } catch (e: any) {
59 + body = <div>error: ${e.toString()}</div>;
60 + }
61 + const name = func.node.id?.name ?? "anonymous";
62 + return (
63 + <div key={index} className="border-b py-4 px-2">
64 + <h3 className="font-medium mb-2">Function: {name}</h3>
65 + {body}
66 + </div>
67 + );
68 + })}
69 + </div>
70 + <CompilerFlagsEditor flags={flags} setFlags={setFlags} />
71 + </div>
72 + );
73 +}
74 +
75 +type Flags = {
76 + eliminateRedundantPhi: boolean;
77 + inferReferenceEffects: boolean;
78 + inferMutableRanges: boolean;
79 + leaveSSA: boolean;
80 +};
81 +
82 +function CompilerFlagsEditor({
83 + flags,
84 + setFlags,
85 +}: {
86 + flags: Flags;
87 + setFlags: (flags: Flags) => void;
88 +}) {
89 + return (
90 + <div className="flex flex-col gap-2 px-5 py-4 border-t border-gray-200">
91 + <h3 className="font-medium">Compiler Options</h3>
92 + <div className="flex flex-col">
93 + <LabeledCheckbox
94 + label="Eliminate Redundant Phi"
95 + value={flags.eliminateRedundantPhi}
96 + onChange={(eliminateRedundantPhi) => {
97 + setFlags({ ...flags, eliminateRedundantPhi });
98 + }}
99 + />
100 + <LabeledCheckbox
101 + label="Infer Reference Effects"
102 + value={flags.inferReferenceEffects}
103 + onChange={(inferReferenceEffects) => {
104 + setFlags({ ...flags, inferReferenceEffects });
105 + }}
106 + />
107 + <LabeledCheckbox
108 + label="Infer Mutable Ranges"
109 + value={flags.inferMutableRanges}
110 + onChange={(inferMutableRanges) => {
111 + setFlags({ ...flags, inferMutableRanges });
112 + }}
113 + />
114 + <LabeledCheckbox
115 + label="Leave SSA"
116 + value={flags.leaveSSA}
117 + onChange={(leaveSSA) => {
118 + setFlags({ ...flags, leaveSSA });
119 + }}
120 + />
121 + </div>
122 + </div>
123 + );
124 +}
125 +
126 +function LabeledCheckbox({
127 + label,
128 + value,
129 + onChange,
130 +}: {
131 + label: string;
132 + value: boolean;
133 + onChange: (value: boolean) => void;
134 +}) {
135 + return (
136 + <label className="flex gap-1.5 text-base">
137 + <input
138 + type="checkbox"
139 + checked={value}
140 + onChange={(e) => {
141 + onChange(e.target.checked);
142 + }}
143 + />
144 + <span className={clsx({ "text-gray-500": !value, "text-black": value })}>
145 + {label}
146 + </span>
147 + </label>
148 + );
149 +}
compiler/forget/packages/playground/components/Editor/Output.tsx
+2 -1
@@ -19,6 +19,7 @@ import { getSelectedFile, type Store } from "../../lib/stores";
19 import GraphView from "../GraphView";
20 import Preview from "../Preview";
21 import { monacoOptions } from "./monacoOptions";
22 +import HIRTabContent from "./HIRTabContent";
23
24 const MemoizedOutput = memo(Output);
25
@@ -43,7 +44,7 @@ function Output({ store, updateDiagnostics }: Props) {
44 tabs={{
45 Preview: <Preview store={store} />,
46 IR: <TextTabContent outputs={outputs} kind={OutputKind.IR} />,
46 - HIR: <TextTabContent outputs={outputs} kind={OutputKind.HIR} />,
47 + HIR: <HIRTabContent source={getSelectedFile(store).content} />,
48 CFG: <TextTabContent outputs={outputs} kind={OutputKind.CFG} />,
49 ValGraph: (
50 <GraphTabContent outputs={outputs} kind={OutputKind.ValGraph} />
compiler/forget/packages/playground/components/Editor/index.tsx
+5 -3
@@ -1,5 +1,8 @@
1 -/*
2 - * Copyright (c) Facebook, Inc. and its affiliates.
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 */
7
8 import { useDrag } from "@use-gesture/react";
@@ -89,7 +92,6 @@ export default function Editor() {
92 })}
93 >
94 <Output store={deferredStore} updateDiagnostics={updateDiagnostics} />
92 - <CompilerFlagsEditor />
95 </div>
96 </div>
97 <button
compiler/forget/src/CompilerDriver.ts
-5
@@ -10,7 +10,6 @@ import * as t from "@babel/types";
10 import * as BE from "./BackEnd";
11 import { CompilerContext } from "./CompilerContext";
12 import { CompilerOptions } from "./CompilerOptions";
13 -import DumpHIRPass from "./HIR/DumpHIRPass";
13 import * as ME from "./MiddleEnd";
14 import { PassManager } from "./PassManager";
15 import * as Validation from "./Validation";
@@ -38,10 +37,6 @@ export function createCompilerDriver(
37 compile() {
38 const passManager = new PassManager(program, context);
39
41 - // New architecture pass to print HIR, unrelated to the
42 - // other passes here and just here for the playground.
43 - passManager.addPass(DumpHIRPass);
44 -
40 // Syntax Analysis and IR Generation.
41 passManager.addPass(ME.ReactFuncsInfer);
42 passManager.addPass(ME.ParamAnalysis);
compiler/forget/src/CompilerOutputs.ts
+1 -8
@@ -16,7 +16,6 @@ import { SCCGraphSnapshot, ValGraphSnapshot } from "./DepGraph";
16 */
17 export enum OutputKind {
18 IR = "DumpIR",
19 - HIR = "DumpHIR",
19 CFG = "DumpCFG",
20 ValGraph = "DumpValGraph",
21 SCCGraph = "DumpSCCGraph",
@@ -34,7 +33,6 @@ export function isSyntacticallyValidJS(kind: OutputKind) {
33
34 export interface CompilerOutputs {
35 [OutputKind.IR]: string;
37 - [OutputKind.HIR]: string;
36 [OutputKind.CFG]: string;
37 [OutputKind.ValGraph]: ValGraphSnapshot[];
38 [OutputKind.SCCGraph]: SCCGraphSnapshot[];
@@ -46,7 +44,6 @@ export interface CompilerOutputs {
44 export function createCompilerOutputs(): CompilerOutputs {
45 return {
46 [OutputKind.IR]: "",
49 - [OutputKind.HIR]: "",
47 [OutputKind.CFG]: "",
48 [OutputKind.ValGraph]: [],
49 [OutputKind.SCCGraph]: [],
@@ -67,7 +64,6 @@ export function stringifyCompilerOutputs(
64 ): PrettyCompilerOutputs {
65 const outputs: PrettyCompilerOutputs = {
66 [OutputKind.IR]: undefined,
70 - [OutputKind.HIR]: undefined,
67 [OutputKind.CFG]: undefined,
68 [OutputKind.ValGraph]: undefined,
69 [OutputKind.SCCGraph]: undefined,
@@ -95,10 +91,7 @@ export function stringifyCompilerOutputs(
91 console.warn("Error formatting output:", e);
92 }
93 }
98 - } else if (
99 - outputKind === OutputKind.CFG ||
100 - outputKind === OutputKind.HIR
101 - ) {
94 + } else if (outputKind === OutputKind.CFG) {
95 outputs[outputKind] = output;
96 } else {
97 invariant(Array.isArray(output), "Graph outputs are arrays.");
compiler/forget/src/HIR/DumpHIRPass.ts deleted
-56
@@ -1,56 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -import type { NodePath } from "@babel/traverse";
9 -import * as t from "@babel/types";
10 -import type { CompilerContext } from "../CompilerContext";
11 -import { OutputKind } from "../CompilerOutputs";
12 -import { PassKind, PassName } from "../Pass";
13 -
14 -import { lower } from "../HIR/BuildHIR";
15 -import { eliminateRedundantPhi } from "../HIR/EliminateRedundantPhi";
16 -import enterSSA from "../HIR/EnterSSA";
17 -import { inferMutableRanges } from "../HIR/InferMutableLifetimes";
18 -import inferReferenceEffects from "../HIR/InferReferenceEffects";
19 -import printHIR from "../HIR/PrintHIR";
20 -import { Environment } from "./HIRBuilder";
21 -import leaveSSA from "./LeaveSSA";
22 -
23 -export default {
24 - name: PassName.DumpHIR,
25 - kind: PassKind.Prog as const,
26 - run,
27 -};
28 -
29 -/**
30 - * A step in the first approach compiler pipeline that outputs
31 - * the new architecture's HIR for the playground.
32 - */
33 -export function run(program: NodePath<t.Program>, context: CompilerContext) {
34 - if (!context.opts.outputKinds.includes(OutputKind.HIR)) {
35 - return;
36 - }
37 - const results: string[] = [];
38 - program.traverse({
39 - Function(func) {
40 - try {
41 - const env = new Environment();
42 - const ir = lower(func, env);
43 - enterSSA(ir, env);
44 - eliminateRedundantPhi(ir);
45 - inferReferenceEffects(ir);
46 - inferMutableRanges(ir);
47 - leaveSSA(ir);
48 - const textHIR = printHIR(ir.body);
49 - results.push(textHIR);
50 - } catch (e) {
51 - results.push(`Error: ${e}`);
52 - }
53 - },
54 - });
55 - context.outputs[OutputKind.HIR] = results.join("\n\n");
56 -}
compiler/forget/src/index.ts
+45
@@ -21,4 +21,49 @@ export * from "./Diagnostic";
21 export * from "./Logger";
22 export { NoUseBeforeDefineRule } from "./Validation";
23
24 +import { lower } from "./HIR/BuildHIR";
25 +import { eliminateRedundantPhi } from "./HIR/EliminateRedundantPhi";
26 +import enterSSA from "./HIR/EnterSSA";
27 +import { inferMutableRanges } from "./HIR/InferMutableLifetimes";
28 +import inferReferenceEffects from "./HIR/InferReferenceEffects";
29 +import printHIR from "./HIR/PrintHIR";
30 +import { Environment } from "./HIR/HIRBuilder";
31 +import leaveSSA from "./HIR/LeaveSSA";
32 +import traverse, { NodePath } from "@babel/traverse";
33 +import * as t from "@babel/types";
34 +import { parse } from "@babel/parser";
35 +
36 +function parseFunctions(
37 + source: string
38 +): Array<NodePath<t.FunctionDeclaration>> {
39 + try {
40 + const ast = parse(source, {
41 + plugins: ["typescript", "jsx"],
42 + });
43 + const items: Array<NodePath<t.FunctionDeclaration>> = [];
44 + traverse(ast, {
45 + FunctionDeclaration: {
46 + enter(nodePath) {
47 + items.push(nodePath);
48 + },
49 + },
50 + });
51 + return items;
52 + } catch (e) {
53 + return [];
54 + }
55 +}
56 +
57 +export const HIR = {
58 + parseFunctions,
59 + lower,
60 + eliminateRedundantPhi,
61 + enterSSA,
62 + inferMutableRanges,
63 + inferReferenceEffects,
64 + printHIR,
65 + Environment,
66 + leaveSSA,
67 +};
68 +
69 export default BabelPlugin;