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