84
* Represents the set of reactive scopes as disjoint sets of identifiers
85
* that mutate together.
86
*/
87
- const scopeIdentifiers = new DisjointSet<Identifier>();
88
- for (const [_, block] of fn.body.blocks) {
89
- /*
90
- * If a phi is mutated after creation, then we need to alias all of its operands such that they
91
- * are assigned to the same scope.
92
- */
93
- for (const phi of block.phis) {
94
- if (
95
- // The phi was reset because it was not mutated after creation
96
- phi.id.mutableRange.start + 1 !== phi.id.mutableRange.end &&
97
- phi.id.mutableRange.end >
98
- (block.instructions.at(0)?.id ?? block.terminal.id)
99
- ) {
100
- for (const [, phiId] of phi.operands) {
101
- scopeIdentifiers.union([phi.id, phiId]);
102
- }
103
- }
104
- }
105
- block.phis.clear();
106
-
107
- for (const instr of block.instructions) {
108
- const operands: Array<Identifier> = [];
109
- const range = instr.lvalue.identifier.mutableRange;
110
- if (range.end > range.start + 1 || mayAllocate(fn.env, instr)) {
111
- operands.push(instr.lvalue!.identifier);
112
- }
113
- if (
114
- instr.value.kind === "StoreLocal" ||
115
- instr.value.kind === "StoreContext"
116
- ) {
117
- if (
118
- instr.value.lvalue.place.identifier.mutableRange.end >
119
- instr.value.lvalue.place.identifier.mutableRange.start + 1
120
- ) {
121
- operands.push(instr.value.lvalue.place.identifier);
122
- }
123
- if (
124
- isMutable(instr, instr.value.value) &&
125
- instr.value.value.identifier.mutableRange.start > 0
126
- ) {
127
- operands.push(instr.value.value.identifier);
128
- }
129
- } else if (instr.value.kind === "Destructure") {
130
- for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
131
- if (
132
- place.identifier.mutableRange.end >
133
- place.identifier.mutableRange.start + 1
134
- ) {
135
- operands.push(place.identifier);
136
- }
137
- }
138
- if (
139
- isMutable(instr, instr.value.value) &&
140
- instr.value.value.identifier.mutableRange.start > 0
141
- ) {
142
- operands.push(instr.value.value.identifier);
143
- }
144
- } else if (instr.value.kind === "MethodCall") {
145
- for (const operand of eachInstructionOperand(instr)) {
146
- if (
147
- isMutable(instr, operand) &&
148
- /*
149
- * exclude global variables from being added to scopes, we can't recreate them!
150
- * TODO: improve handling of module-scoped variables and globals
151
- */
152
- operand.identifier.mutableRange.start > 0
153
- ) {
154
- operands.push(operand.identifier);
155
- }
156
- }
157
- /*
158
- * Ensure that the ComputedLoad to resolve the method is in the same scope as the
159
- * call itself
160
- */
161
- operands.push(instr.value.property.identifier);
162
- } else {
163
- for (const operand of eachInstructionOperand(instr)) {
164
- if (
165
- isMutable(instr, operand) &&
166
- /*
167
- * exclude global variables from being added to scopes, we can't recreate them!
168
- * TODO: improve handling of module-scoped variables and globals
169
- */
170
- operand.identifier.mutableRange.start > 0
171
- ) {
172
- operands.push(operand.identifier);
173
- }
174
- }
175
- }
176
- if (operands.length !== 0) {
177
- scopeIdentifiers.union(operands);
178
- }
179
- }
180
- }
87
+ const scopeIdentifiers = findDisjointMutableValues(fn);
88
89
// Maps each scope (by its identifying member) to a ScopeId value
90
const scopes: Map<Identifier, ReactiveScope> = new Map();
185
}
186
}
187
}
188
+
189
+export function findDisjointMutableValues(
190
+ fn: HIRFunction
191
+): DisjointSet<Identifier> {
192
+ const scopeIdentifiers = new DisjointSet<Identifier>();
193
+ for (const [_, block] of fn.body.blocks) {
194
+ /*
195
+ * If a phi is mutated after creation, then we need to alias all of its operands such that they
196
+ * are assigned to the same scope.
197
+ */
198
+ for (const phi of block.phis) {
199
+ if (
200
+ // The phi was reset because it was not mutated after creation
201
+ phi.id.mutableRange.start + 1 !== phi.id.mutableRange.end &&
202
+ phi.id.mutableRange.end >
203
+ (block.instructions.at(0)?.id ?? block.terminal.id)
204
+ ) {
205
+ for (const [, phiId] of phi.operands) {
206
+ scopeIdentifiers.union([phi.id, phiId]);
207
+ }
208
+ }
209
+ }
210
+
211
+ for (const instr of block.instructions) {
212
+ const operands: Array<Identifier> = [];
213
+ const range = instr.lvalue.identifier.mutableRange;
214
+ if (range.end > range.start + 1 || mayAllocate(fn.env, instr)) {
215
+ operands.push(instr.lvalue!.identifier);
216
+ }
217
+ if (
218
+ instr.value.kind === "StoreLocal" ||
219
+ instr.value.kind === "StoreContext"
220
+ ) {
221
+ if (
222
+ instr.value.lvalue.place.identifier.mutableRange.end >
223
+ instr.value.lvalue.place.identifier.mutableRange.start + 1
224
+ ) {
225
+ operands.push(instr.value.lvalue.place.identifier);
226
+ }
227
+ if (
228
+ isMutable(instr, instr.value.value) &&
229
+ instr.value.value.identifier.mutableRange.start > 0
230
+ ) {
231
+ operands.push(instr.value.value.identifier);
232
+ }
233
+ } else if (instr.value.kind === "Destructure") {
234
+ for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
235
+ if (
236
+ place.identifier.mutableRange.end >
237
+ place.identifier.mutableRange.start + 1
238
+ ) {
239
+ operands.push(place.identifier);
240
+ }
241
+ }
242
+ if (
243
+ isMutable(instr, instr.value.value) &&
244
+ instr.value.value.identifier.mutableRange.start > 0
245
+ ) {
246
+ operands.push(instr.value.value.identifier);
247
+ }
248
+ } else if (instr.value.kind === "MethodCall") {
249
+ for (const operand of eachInstructionOperand(instr)) {
250
+ if (
251
+ isMutable(instr, operand) &&
252
+ /*
253
+ * exclude global variables from being added to scopes, we can't recreate them!
254
+ * TODO: improve handling of module-scoped variables and globals
255
+ */
256
+ operand.identifier.mutableRange.start > 0
257
+ ) {
258
+ operands.push(operand.identifier);
259
+ }
260
+ }
261
+ /*
262
+ * Ensure that the ComputedLoad to resolve the method is in the same scope as the
263
+ * call itself
264
+ */
265
+ operands.push(instr.value.property.identifier);
266
+ } else {
267
+ for (const operand of eachInstructionOperand(instr)) {
268
+ if (
269
+ isMutable(instr, operand) &&
270
+ /*
271
+ * exclude global variables from being added to scopes, we can't recreate them!
272
+ * TODO: improve handling of module-scoped variables and globals
273
+ */
274
+ operand.identifier.mutableRange.start > 0
275
+ ) {
276
+ operands.push(operand.identifier);
277
+ }
278
+ }
279
+ }
280
+ if (operands.length !== 0) {
281
+ scopeIdentifiers.union(operands);
282
+ }
283
+ }
284
+ }
285
+ return scopeIdentifiers;
286
+}