diff --git a/compiler/forget/src/HIR/InferAlias.ts b/compiler/forget/src/HIR/InferAlias.ts index 274217c2ff..5511f7a532 100644 --- a/compiler/forget/src/HIR/InferAlias.ts +++ b/compiler/forget/src/HIR/InferAlias.ts @@ -3,97 +3,14 @@ import DisjointSet from "./DisjointSet"; import { HIRFunction, Identifier, Instruction, LValue, Place } from "./HIR"; import { printInstructionValue } from "./PrintHIR"; -type AbstractValue = AbstractObject | AbstractPrimitive; -type AbstractObject = { - kind: "Object"; - values: Map; -}; -type AbstractPrimitive = { - kind: "Primitive"; - value: number | boolean | string | null | undefined; -}; - export type AliasSet = Set; -class AbstractState { +class AliasAnalyser { aliases = new DisjointSet(); // NOTE(gsn): Should this be a part of AbstractObject? No, because this has // nothing to do with values in the object. objectAliases = new Map>(); - #values = new Map(); - - read(alias: Place): AbstractValue { - // Simple alias: - // read(alias); - if (alias.memberPath === null) { - let value = this.#values.get(alias.identifier); - - // Don't know what this is, let's default to an Object conservatively. - if (value === undefined) { - value = { kind: "Object", values: new Map() }; - } - - this.#values.set(alias.identifier, value); - return value; - } - - if (alias.memberPath.length > 1) { - // TODO(gsn): Correctly handle nested member paths when reading values - return { kind: "Object", values: new Map() }; - } - - // Complex alias: - // read(alias.memberPath); - let object = this.#values.get(alias.identifier); - - // Don't know what this is, let's default to an Object conservatively. - if (object === undefined) { - object = { kind: "Object", values: new Map() }; - this.#values.set(alias.identifier, object); - } - - // We're doing a member lookup on a non object. - // - // alias = 1; - // read(alias.memberPath); - if (object.kind !== "Object") { - // Update alias to be an object - object = { kind: "Object", values: new Map() }; - this.#values.set(alias.identifier, object); - - // Conservatively type the value as object. - // - // NOTE(gsn): Should this be an AbstractUnknown rather than an - // AbstractObject? - let value: AbstractObject = { kind: "Object", values: new Map() }; - object.values.set(alias.memberPath[0], value); - return value; - } - - let value = object.values.get(alias.memberPath[0]); - - // We don't have a value for this member path. - // - // alias = {}; - // read(alias.memberPath); - if (value === undefined) { - // Conservatively type the value as object. - value = { - kind: "Object", - values: new Map(), - }; - object.values.set(alias.memberPath[0], value); - return value; - } - - // We have a value for this memberPath! - // - // alias.memberPath = value; - // read(alias.memberPath); - return value; - } - alias(lvalue: LValue, alias: Place) { // Complex lvalue: // lvalue.memberPath = alias; @@ -141,66 +58,37 @@ class AbstractState { // lvalue = alias.memberPath; this.aliases.union([lvalue.place.identifier, alias.identifier]); } - - store(lvalue: LValue, value: AbstractValue) { - // TODO(gsn): Handle stores for complex lvalue - if (lvalue.place.memberPath !== null) { - return; - } - - // Simple lvalue: - // lvalue = alias; - // lvalue = alias.memberPath; - this.#values.set(lvalue.place.identifier, value); - } } export function inferAliases(func: HIRFunction): DisjointSet { - const state = new AbstractState(); + const analyser = new AliasAnalyser(); for (const [_, block] of func.body.blocks) { for (const instr of block.instructions) { - inferInstr(instr, state); + inferInstr(instr, analyser); } } - return state.aliases; + return analyser.aliases; } -function inferInstr(instr: Instruction, state: AbstractState) { +function inferInstr(instr: Instruction, state: AliasAnalyser) { const { lvalue, value: instrValue } = instr; let alias: Place | null = null; - let value: AbstractValue | null = null; switch (instrValue.kind) { - case "Primitive": { - value = { - kind: "Primitive", - value: instrValue.value, - }; - break; - } case "Identifier": { alias = instrValue; - value = state.read(alias); break; } default: return; } - invariant( - value !== null, - `expected ${printInstructionValue(instrValue)} to be an alias or value` - ); - // TODO(gsn): handle this. if (lvalue === null) { return; } - // No need to alias Primitives. - if (alias !== null && value.kind !== "Primitive") { + if (alias !== null) { state.alias(lvalue, alias); } - - state.store(lvalue, value); } diff --git a/compiler/forget/src/HIR/InferTypes.ts b/compiler/forget/src/HIR/InferTypes.ts new file mode 100644 index 0000000000..212a356f35 --- /dev/null +++ b/compiler/forget/src/HIR/InferTypes.ts @@ -0,0 +1,144 @@ +import invariant from "invariant"; +import DisjointSet from "./DisjointSet"; +import { HIRFunction, Identifier, Instruction, LValue, Place } from "./HIR"; +import { printInstructionValue } from "./PrintHIR"; + +type AbstractValue = AbstractObject | AbstractPrimitive; +type AbstractObject = { + kind: "Object"; + values: Map; +}; +type AbstractPrimitive = { + kind: "Primitive"; + value: number | boolean | string | null | undefined; +}; + +class AbstractState { + #values = new Map(); + + read(alias: Place): AbstractValue { + // Simple alias: + // read(alias); + if (alias.memberPath === null) { + let value = this.#values.get(alias.identifier); + + // Don't know what this is, let's default to an Object conservatively. + if (value === undefined) { + value = { kind: "Object", values: new Map() }; + } + + this.#values.set(alias.identifier, value); + return value; + } + + if (alias.memberPath.length > 1) { + // TODO(gsn): Correctly handle nested member paths when reading values + return { kind: "Object", values: new Map() }; + } + + // Complex alias: + // read(alias.memberPath); + let object = this.#values.get(alias.identifier); + + // Don't know what this is, let's default to an Object conservatively. + if (object === undefined) { + object = { kind: "Object", values: new Map() }; + this.#values.set(alias.identifier, object); + } + + // We're doing a member lookup on a non object. + // + // alias = 1; + // read(alias.memberPath); + if (object.kind !== "Object") { + // Update alias to be an object + object = { kind: "Object", values: new Map() }; + this.#values.set(alias.identifier, object); + + // Conservatively type the value as object. + // + // NOTE(gsn): Should this be an AbstractUnknown rather than an + // AbstractObject? + let value: AbstractObject = { kind: "Object", values: new Map() }; + object.values.set(alias.memberPath[0], value); + return value; + } + + let value = object.values.get(alias.memberPath[0]); + + // We don't have a value for this member path. + // + // alias = {}; + // read(alias.memberPath); + if (value === undefined) { + // Conservatively type the value as object. + value = { + kind: "Object", + values: new Map(), + }; + object.values.set(alias.memberPath[0], value); + return value; + } + + // We have a value for this memberPath! + // + // alias.memberPath = value; + // read(alias.memberPath); + return value; + } + + store(lvalue: LValue, value: AbstractValue) { + // TODO(gsn): Handle stores for complex lvalue + if (lvalue.place.memberPath !== null) { + return; + } + + // Simple lvalue: + // lvalue = alias; + // lvalue = alias.memberPath; + this.#values.set(lvalue.place.identifier, value); + } +} + +export function inferTypes(func: HIRFunction) { + const state = new AbstractState(); + for (const [_, block] of func.body.blocks) { + for (const instr of block.instructions) { + inferInstr(instr, state); + } + } +} + +function inferInstr(instr: Instruction, state: AbstractState) { + const { lvalue, value: instrValue } = instr; + let alias: Place | null = null; + let value: AbstractValue | null = null; + switch (instrValue.kind) { + case "Primitive": { + value = { + kind: "Primitive", + value: instrValue.value, + }; + break; + } + case "Identifier": { + alias = instrValue; + value = state.read(alias); + break; + } + default: + return; + } + + invariant( + value !== null, + `expected ${printInstructionValue(instrValue)} to be an alias or value` + ); + + // TODO(gsn): handle this. + if (lvalue === null) { + return; + } + + state.store(lvalue, value); +}