@samitouri / QOS-React-2 / commits / 7f8ad49165

[hir] Split value tracking into a separate pass

Sathya Gunasekaran committed Dec 13, 2022 at 09:17 UTC 7f8ad491651fa2136288bf731dda79f5d1e8f3e3
2 files changed +150 -118
compiler/forget/src/HIR/InferAlias.ts
+6 -118
@@ -3,97 +3,14 @@ import DisjointSet from "./DisjointSet";
3 import { HIRFunction, Identifier, Instruction, LValue, Place } from "./HIR";
4 import { printInstructionValue } from "./PrintHIR";
5
6 -type AbstractValue = AbstractObject | AbstractPrimitive;
7 -type AbstractObject = {
8 - kind: "Object";
9 - values: Map<string, AbstractValue>;
10 -};
11 -type AbstractPrimitive = {
12 - kind: "Primitive";
13 - value: number | boolean | string | null | undefined;
14 -};
15 -
6 export type AliasSet = Set<Identifier>;
7
18 -class AbstractState {
8 +class AliasAnalyser {
9 aliases = new DisjointSet<Identifier>();
10 // NOTE(gsn): Should this be a part of AbstractObject? No, because this has
11 // nothing to do with values in the object.
12 objectAliases = new Map<Identifier, Map<string, AliasSet>>();
13
24 - #values = new Map<Identifier, AbstractValue>();
25 -
26 - read(alias: Place): AbstractValue {
27 - // Simple alias:
28 - // read(alias);
29 - if (alias.memberPath === null) {
30 - let value = this.#values.get(alias.identifier);
31 -
32 - // Don't know what this is, let's default to an Object conservatively.
33 - if (value === undefined) {
34 - value = { kind: "Object", values: new Map() };
35 - }
36 -
37 - this.#values.set(alias.identifier, value);
38 - return value;
39 - }
40 -
41 - if (alias.memberPath.length > 1) {
42 - // TODO(gsn): Correctly handle nested member paths when reading values
43 - return { kind: "Object", values: new Map() };
44 - }
45 -
46 - // Complex alias:
47 - // read(alias.memberPath);
48 - let object = this.#values.get(alias.identifier);
49 -
50 - // Don't know what this is, let's default to an Object conservatively.
51 - if (object === undefined) {
52 - object = { kind: "Object", values: new Map() };
53 - this.#values.set(alias.identifier, object);
54 - }
55 -
56 - // We're doing a member lookup on a non object.
57 - //
58 - // alias = 1;
59 - // read(alias.memberPath);
60 - if (object.kind !== "Object") {
61 - // Update alias to be an object
62 - object = { kind: "Object", values: new Map() };
63 - this.#values.set(alias.identifier, object);
64 -
65 - // Conservatively type the value as object.
66 - //
67 - // NOTE(gsn): Should this be an AbstractUnknown rather than an
68 - // AbstractObject?
69 - let value: AbstractObject = { kind: "Object", values: new Map() };
70 - object.values.set(alias.memberPath[0], value);
71 - return value;
72 - }
73 -
74 - let value = object.values.get(alias.memberPath[0]);
75 -
76 - // We don't have a value for this member path.
77 - //
78 - // alias = {};
79 - // read(alias.memberPath);
80 - if (value === undefined) {
81 - // Conservatively type the value as object.
82 - value = {
83 - kind: "Object",
84 - values: new Map(),
85 - };
86 - object.values.set(alias.memberPath[0], value);
87 - return value;
88 - }
89 -
90 - // We have a value for this memberPath!
91 - //
92 - // alias.memberPath = value;
93 - // read(alias.memberPath);
94 - return value;
95 - }
96 -
14 alias(lvalue: LValue, alias: Place) {
15 // Complex lvalue:
16 // lvalue.memberPath = alias;
@@ -141,66 +58,37 @@ class AbstractState {
58 // lvalue = alias.memberPath;
59 this.aliases.union([lvalue.place.identifier, alias.identifier]);
60 }
144 -
145 - store(lvalue: LValue, value: AbstractValue) {
146 - // TODO(gsn): Handle stores for complex lvalue
147 - if (lvalue.place.memberPath !== null) {
148 - return;
149 - }
150 -
151 - // Simple lvalue:
152 - // lvalue = alias;
153 - // lvalue = alias.memberPath;
154 - this.#values.set(lvalue.place.identifier, value);
155 - }
61 }
62
63 export function inferAliases(func: HIRFunction): DisjointSet<Identifier> {
159 - const state = new AbstractState();
64 + const analyser = new AliasAnalyser();
65 for (const [_, block] of func.body.blocks) {
66 for (const instr of block.instructions) {
162 - inferInstr(instr, state);
67 + inferInstr(instr, analyser);
68 }
69 }
70
166 - return state.aliases;
71 + return analyser.aliases;
72 }
73
169 -function inferInstr(instr: Instruction, state: AbstractState) {
74 +function inferInstr(instr: Instruction, state: AliasAnalyser) {
75 const { lvalue, value: instrValue } = instr;
76 let alias: Place | null = null;
172 - let value: AbstractValue | null = null;
77 switch (instrValue.kind) {
174 - case "Primitive": {
175 - value = {
176 - kind: "Primitive",
177 - value: instrValue.value,
178 - };
179 - break;
180 - }
78 case "Identifier": {
79 alias = instrValue;
183 - value = state.read(alias);
80 break;
81 }
82 default:
83 return;
84 }
85
190 - invariant(
191 - value !== null,
192 - `expected ${printInstructionValue(instrValue)} to be an alias or value`
193 - );
194 -
86 // TODO(gsn): handle this.
87 if (lvalue === null) {
88 return;
89 }
90
200 - // No need to alias Primitives.
201 - if (alias !== null && value.kind !== "Primitive") {
91 + if (alias !== null) {
92 state.alias(lvalue, alias);
93 }
204 -
205 - state.store(lvalue, value);
94 }
compiler/forget/src/HIR/InferTypes.ts new
+144
@@ -0,0 +1,144 @@
1 +import invariant from "invariant";
2 +import DisjointSet from "./DisjointSet";
3 +import { HIRFunction, Identifier, Instruction, LValue, Place } from "./HIR";
4 +import { printInstructionValue } from "./PrintHIR";
5 +
6 +type AbstractValue = AbstractObject | AbstractPrimitive;
7 +type AbstractObject = {
8 + kind: "Object";
9 + values: Map<string, AbstractValue>;
10 +};
11 +type AbstractPrimitive = {
12 + kind: "Primitive";
13 + value: number | boolean | string | null | undefined;
14 +};
15 +
16 +class AbstractState {
17 + #values = new Map<Identifier, AbstractValue>();
18 +
19 + read(alias: Place): AbstractValue {
20 + // Simple alias:
21 + // read(alias);
22 + if (alias.memberPath === null) {
23 + let value = this.#values.get(alias.identifier);
24 +
25 + // Don't know what this is, let's default to an Object conservatively.
26 + if (value === undefined) {
27 + value = { kind: "Object", values: new Map() };
28 + }
29 +
30 + this.#values.set(alias.identifier, value);
31 + return value;
32 + }
33 +
34 + if (alias.memberPath.length > 1) {
35 + // TODO(gsn): Correctly handle nested member paths when reading values
36 + return { kind: "Object", values: new Map() };
37 + }
38 +
39 + // Complex alias:
40 + // read(alias.memberPath);
41 + let object = this.#values.get(alias.identifier);
42 +
43 + // Don't know what this is, let's default to an Object conservatively.
44 + if (object === undefined) {
45 + object = { kind: "Object", values: new Map() };
46 + this.#values.set(alias.identifier, object);
47 + }
48 +
49 + // We're doing a member lookup on a non object.
50 + //
51 + // alias = 1;
52 + // read(alias.memberPath);
53 + if (object.kind !== "Object") {
54 + // Update alias to be an object
55 + object = { kind: "Object", values: new Map() };
56 + this.#values.set(alias.identifier, object);
57 +
58 + // Conservatively type the value as object.
59 + //
60 + // NOTE(gsn): Should this be an AbstractUnknown rather than an
61 + // AbstractObject?
62 + let value: AbstractObject = { kind: "Object", values: new Map() };
63 + object.values.set(alias.memberPath[0], value);
64 + return value;
65 + }
66 +
67 + let value = object.values.get(alias.memberPath[0]);
68 +
69 + // We don't have a value for this member path.
70 + //
71 + // alias = {};
72 + // read(alias.memberPath);
73 + if (value === undefined) {
74 + // Conservatively type the value as object.
75 + value = {
76 + kind: "Object",
77 + values: new Map(),
78 + };
79 + object.values.set(alias.memberPath[0], value);
80 + return value;
81 + }
82 +
83 + // We have a value for this memberPath!
84 + //
85 + // alias.memberPath = value;
86 + // read(alias.memberPath);
87 + return value;
88 + }
89 +
90 + store(lvalue: LValue, value: AbstractValue) {
91 + // TODO(gsn): Handle stores for complex lvalue
92 + if (lvalue.place.memberPath !== null) {
93 + return;
94 + }
95 +
96 + // Simple lvalue:
97 + // lvalue = alias;
98 + // lvalue = alias.memberPath;
99 + this.#values.set(lvalue.place.identifier, value);
100 + }
101 +}
102 +
103 +export function inferTypes(func: HIRFunction) {
104 + const state = new AbstractState();
105 + for (const [_, block] of func.body.blocks) {
106 + for (const instr of block.instructions) {
107 + inferInstr(instr, state);
108 + }
109 + }
110 +}
111 +
112 +function inferInstr(instr: Instruction, state: AbstractState) {
113 + const { lvalue, value: instrValue } = instr;
114 + let alias: Place | null = null;
115 + let value: AbstractValue | null = null;
116 + switch (instrValue.kind) {
117 + case "Primitive": {
118 + value = {
119 + kind: "Primitive",
120 + value: instrValue.value,
121 + };
122 + break;
123 + }
124 + case "Identifier": {
125 + alias = instrValue;
126 + value = state.read(alias);
127 + break;
128 + }
129 + default:
130 + return;
131 + }
132 +
133 + invariant(
134 + value !== null,
135 + `expected ${printInstructionValue(instrValue)} to be an alias or value`
136 + );
137 +
138 + // TODO(gsn): handle this.
139 + if (lvalue === null) {
140 + return;
141 + }
142 +
143 + state.store(lvalue, value);
144 +}