@samitouri / QOS-React-2 / commits / f4c4dcb1b3

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