Instruction-specific propagation to account for assignment-as-value
Updates ConstantPropagation so that each instruction is responsible for whether to replace its `.value` with the resolved constant value (if found). Specifically, for `StoreLocal` we don't want to replace the value — we want to keep the assignment — but we do want to propagate the _result_ of the assignment downstream. This more accurately models the semantics of assignment expressions, and helps with subsequent PRs.
Joe Savona committed
Mar 21, 2023 at 10:01 UTC
f4c4dcb1b381e2772b21474d0f791197cc7f92e8
1 file changed
+39
-27
compiler/forget/src/Optimization/ConstantPropagation.ts
+39
-27
@@ -104,7 +104,6 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
104
for (const instr of block.instructions) {
105
const value = evaluateInstruction(constants, instr);
106
if (value !== null) {
107
- instr.value = value;
107
constants.set(instr.lvalue.identifier.id, value);
108
}
109
}
@@ -225,72 +224,80 @@ function evaluateInstruction(
224
if (lhsValue !== null && rhsValue !== null) {
225
const lhs = lhsValue.value;
226
const rhs = rhsValue.value;
227
+ let result: Primitive | null = null;
228
switch (value.operator) {
229
case "+": {
230
if (typeof lhs === "number" && typeof rhs === "number") {
231
- return { kind: "Primitive", value: lhs + rhs, loc: value.loc };
231
+ result = { kind: "Primitive", value: lhs + rhs, loc: value.loc };
232
}
233
- return null;
233
+ break;
234
}
235
case "-": {
236
if (typeof lhs === "number" && typeof rhs === "number") {
237
- return { kind: "Primitive", value: lhs - rhs, loc: value.loc };
237
+ result = { kind: "Primitive", value: lhs - rhs, loc: value.loc };
238
}
239
- return null;
239
+ break;
240
}
241
case "*": {
242
if (typeof lhs === "number" && typeof rhs === "number") {
243
- return { kind: "Primitive", value: lhs * rhs, loc: value.loc };
243
+ result = { kind: "Primitive", value: lhs * rhs, loc: value.loc };
244
}
245
- return null;
245
+ break;
246
}
247
case "/": {
248
if (typeof lhs === "number" && typeof rhs === "number") {
249
- return { kind: "Primitive", value: lhs / rhs, loc: value.loc };
249
+ result = { kind: "Primitive", value: lhs / rhs, loc: value.loc };
250
}
251
- return null;
251
+ break;
252
}
253
case "<": {
254
if (typeof lhs === "number" && typeof rhs === "number") {
255
- return { kind: "Primitive", value: lhs < rhs, loc: value.loc };
255
+ result = { kind: "Primitive", value: lhs < rhs, loc: value.loc };
256
}
257
- return null;
257
+ break;
258
}
259
case "<=": {
260
if (typeof lhs === "number" && typeof rhs === "number") {
261
- return { kind: "Primitive", value: lhs <= rhs, loc: value.loc };
261
+ result = { kind: "Primitive", value: lhs <= rhs, loc: value.loc };
262
}
263
- return null;
263
+ break;
264
}
265
case ">": {
266
if (typeof lhs === "number" && typeof rhs === "number") {
267
- return { kind: "Primitive", value: lhs > rhs, loc: value.loc };
267
+ result = { kind: "Primitive", value: lhs > rhs, loc: value.loc };
268
}
269
- return null;
269
+ break;
270
}
271
case ">=": {
272
if (typeof lhs === "number" && typeof rhs === "number") {
273
- return { kind: "Primitive", value: lhs >= rhs, loc: value.loc };
273
+ result = { kind: "Primitive", value: lhs >= rhs, loc: value.loc };
274
}
275
- return null;
275
+ break;
276
}
277
case "==": {
278
- return { kind: "Primitive", value: lhs == rhs, loc: value.loc };
278
+ result = { kind: "Primitive", value: lhs == rhs, loc: value.loc };
279
+ break;
280
}
281
case "===": {
281
- return { kind: "Primitive", value: lhs === rhs, loc: value.loc };
282
+ result = { kind: "Primitive", value: lhs === rhs, loc: value.loc };
283
+ break;
284
}
285
case "!=": {
284
- return { kind: "Primitive", value: lhs != rhs, loc: value.loc };
286
+ result = { kind: "Primitive", value: lhs != rhs, loc: value.loc };
287
+ break;
288
}
289
case "!==": {
287
- return { kind: "Primitive", value: lhs !== rhs, loc: value.loc };
290
+ result = { kind: "Primitive", value: lhs !== rhs, loc: value.loc };
291
+ break;
292
}
293
default: {
290
- // TODO: handle more cases
291
- return null;
294
+ break;
295
}
296
}
297
+ if (result !== null) {
298
+ instr.value = result;
299
+ return result;
300
+ }
301
}
302
return null;
303
}
@@ -301,25 +308,30 @@ function evaluateInstruction(
308
typeof objectValue.value === "string" &&
309
value.property === "length"
310
) {
304
- return {
311
+ const result: InstructionValue = {
312
kind: "Primitive",
313
value: objectValue.value.length,
314
loc: value.loc,
315
};
316
+ instr.value = result;
317
+ return result;
318
}
319
}
320
return null;
321
}
322
case "LoadLocal": {
314
- return read(constants, value.place);
323
+ const placeValue = read(constants, value.place);
324
+ if (placeValue !== null) {
325
+ instr.value = placeValue;
326
+ }
327
+ return placeValue;
328
}
329
case "StoreLocal": {
330
const placeValue = read(constants, value.value);
331
if (placeValue !== null) {
332
constants.set(value.lvalue.place.identifier.id, placeValue);
333
}
321
- // NOTE: always return null to avoid replacing the StoreLocal with its value
322
- return null;
334
+ return placeValue;
335
}
336
default: {
337
// TODO: handle more cases