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

Rename Environment => State for infer reference effects for clarity

InferReferenceEffects needs to be able to pass around the function's Environment, but there is already a local class with that name. It's confusing to have two "environment" concepts in one file, so this PR renames that local class to the more appropriate `InferenceState` and renames local variables and updates comments accordingly.

Joe Savona committed Feb 16, 2023 at 14:06 UTC cee25928bf1d31a5ba60dccd8b39f3efcf940529
1 file changed +101 -101
compiler/forget/src/Inference/InferReferenceEffects.ts
+101 -101
@@ -76,15 +76,15 @@ import { assertExhaustive } from "../Utils/utils";
76 * types forming a lattice to ensure convergence.
77 */
78 export default function inferReferenceEffects(fn: HIRFunction) {
79 - // Initial environment contains function params
79 + // Initial state contains function params
80 // TODO: include module declarations here as well
81 - const initialEnvironment = Environment.empty();
81 + const initialState = InferenceState.empty();
82 const value: InstructionValue = {
83 kind: "Primitive",
84 loc: fn.loc,
85 value: undefined,
86 };
87 - initialEnvironment.initialize(value, ValueKind.Frozen);
87 + initialState.initialize(value, ValueKind.Frozen);
88 if (fn.id !== null) {
89 const id: Place = {
90 kind: "Identifier",
@@ -92,7 +92,7 @@ export default function inferReferenceEffects(fn: HIRFunction) {
92 loc: fn.loc,
93 effect: Effect.Freeze,
94 };
95 - initialEnvironment.define(id, value);
95 + initialState.define(id, value);
96 }
97
98 for (const ref of fn.context) {
@@ -102,8 +102,8 @@ export default function inferReferenceEffects(fn: HIRFunction) {
102 properties: null,
103 loc: ref.loc,
104 };
105 - initialEnvironment.initialize(value, ValueKind.Context);
106 - initialEnvironment.define(ref, value);
105 + initialState.initialize(value, ValueKind.Context);
106 + initialState.define(ref, value);
107 }
108
109 for (const param of fn.params) {
@@ -112,52 +112,49 @@ export default function inferReferenceEffects(fn: HIRFunction) {
112 loc: param.loc,
113 value: undefined,
114 };
115 - initialEnvironment.initialize(value, ValueKind.Frozen);
116 - initialEnvironment.define(param, value);
115 + initialState.initialize(value, ValueKind.Frozen);
116 + initialState.define(param, value);
117 }
118
119 - // Map of blocks to the last (merged) incoming environment that was processed
120 - const environmentsByBlock: Map<BlockId, Environment> = new Map();
119 + // Map of blocks to the last (merged) incoming state that was processed
120 + const statesByBlock: Map<BlockId, InferenceState> = new Map();
121
122 // Multiple predecessors may be visited prior to reaching a given successor,
123 - // so track the list of incoming environments for each successor block.
123 + // so track the list of incoming state for each successor block.
124 // These are merged when reaching that block again.
125 - const queuedEnvironments: Map<BlockId, Environment> = new Map();
126 - function queue(blockId: BlockId, environment: Environment) {
127 - let queuedEnvironment = queuedEnvironments.get(blockId);
128 - if (queuedEnvironment != null) {
129 - // merge the queued environments for this block
130 - environment = queuedEnvironment.merge(environment) ?? environment;
131 - queuedEnvironments.set(blockId, environment);
125 + const queuedStates: Map<BlockId, InferenceState> = new Map();
126 + function queue(blockId: BlockId, state: InferenceState) {
127 + let queuedState = queuedStates.get(blockId);
128 + if (queuedState != null) {
129 + // merge the queued states for this block
130 + state = queuedState.merge(state) ?? state;
131 + queuedStates.set(blockId, state);
132 } else {
133 - // this is the first queued environment for this block, see whether
133 + // this is the first queued state for this block, see whether
134 // there are changed relative to the last time it was processed.
135 - const prevEnvironment = environmentsByBlock.get(blockId);
136 - const nextEnvironment =
137 - prevEnvironment != null
138 - ? prevEnvironment.merge(environment)
139 - : environment;
140 - if (nextEnvironment != null) {
141 - queuedEnvironments.set(blockId, nextEnvironment);
135 + const prevState = statesByBlock.get(blockId);
136 + const nextState = prevState != null ? prevState.merge(state) : state;
137 + if (nextState != null) {
138 + queuedStates.set(blockId, nextState);
139 }
140 }
141 }
145 - queue(fn.body.entry, initialEnvironment);
142 + queue(fn.body.entry, initialState);
143
147 - while (queuedEnvironments.size !== 0) {
144 + while (queuedStates.size !== 0) {
145 for (const [blockId, block] of fn.body.blocks) {
149 - const incomingEnvironment = queuedEnvironments.get(blockId);
150 - queuedEnvironments.delete(blockId);
151 - if (incomingEnvironment == null) {
146 + const incomingState = queuedStates.get(blockId);
147 + queuedStates.delete(blockId);
148 + if (incomingState == null) {
149 continue;
150 }
151
155 - environmentsByBlock.set(blockId, incomingEnvironment);
156 - const environment = incomingEnvironment.clone();
157 - inferBlock(environment, block);
152 + statesByBlock.set(blockId, incomingState);
153 + const state = incomingState.clone();
154 + inferBlock(state, block);
155
156 for (const nextBlockId of eachTerminalSuccessor(block.terminal)) {
160 - queue(nextBlockId, environment);
157 + queue(nextBlockId, state);
158 }
159 }
160 }
@@ -166,7 +163,7 @@ export default function inferReferenceEffects(fn: HIRFunction) {
163 /**
164 * Maintains a mapping of top-level variables to the kind of value they hold
165 */
169 -class Environment {
166 +class InferenceState {
167 // The kind of reach value, based on its allocation site
168 #values: Map<InstructionValue, ValueKind>;
169 // The set of values pointed to by each identifier. This is a set
@@ -182,8 +179,8 @@ class Environment {
179 this.#variables = variables;
180 }
181
185 - static empty(): Environment {
186 - return new Environment(new Map(), new Map());
182 + static empty(): InferenceState {
183 + return new InferenceState(new Map(), new Map());
184 }
185
186 /**
@@ -352,7 +349,7 @@ class Environment {
349 * Note that values are joined using a lattice operation to ensure
350 * termination.
351 */
355 - merge(other: Environment): Environment | null {
352 + merge(other: InferenceState): InferenceState | null {
353 let nextValues: Map<InstructionValue, ValueKind> | null = null;
354 let nextVariables: Map<IdentifierId, Set<InstructionValue>> | null = null;
355
@@ -402,7 +399,7 @@ class Environment {
399 if (nextVariables === null && nextValues === null) {
400 return null;
401 } else {
405 - return new Environment(
402 + return new InferenceState(
403 nextValues ?? new Map(this.#values),
404 nextVariables ?? new Map(this.#variables)
405 );
@@ -410,16 +407,16 @@ class Environment {
407 }
408
409 /**
413 - * Returns a copy of this environment.
410 + * Returns a copy of this state.
411 * TODO: consider using persistent data structures to make
412 * clone cheaper.
413 */
417 - clone(): Environment {
418 - return new Environment(new Map(this.#values), new Map(this.#variables));
414 + clone(): InferenceState {
415 + return new InferenceState(new Map(this.#values), new Map(this.#variables));
416 }
417
418 /**
422 - * For debugging purposes, dumps the environment to a plain
419 + * For debugging purposes, dumps the state to a plain
420 * object so that it can printed as JSON.
421 */
422 debug(): any {
@@ -447,7 +444,7 @@ class Environment {
444 const values: Set<InstructionValue> = new Set();
445 for (const [_, operand] of phi.operands) {
446 const operandValues = this.#variables.get(operand.id);
450 - // This is a backedge that will be handled later by Environment.merge
447 + // This is a backedge that will be handled later by State.merge
448 if (operandValues === undefined) continue;
449 for (const v of operandValues) {
450 values.add(v);
@@ -552,11 +549,11 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind {
549
550 /**
551 * Iterates over the given @param block, defining variables and
555 - * recording references on the @param env according to JS semantics.
552 + * recording references on the @param state according to JS semantics.
553 */
557 -function inferBlock(env: Environment, block: BasicBlock) {
554 +function inferBlock(state: InferenceState, block: BasicBlock) {
555 for (const phi of block.phis) {
559 - env.inferPhi(phi);
556 + state.inferPhi(phi);
557 }
558
559 for (const instr of block.instructions) {
@@ -571,7 +568,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
568 break;
569 }
570 case "ArrayExpression": {
574 - valueKind = hasContextRefOperand(env, instrValue)
571 + valueKind = hasContextRefOperand(state, instrValue)
572 ? ValueKind.Context
573 : ValueKind.Mutable;
574 effectKind = Effect.Capture;
@@ -594,7 +591,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
591 break;
592 }
593 case "ObjectExpression": {
597 - valueKind = hasContextRefOperand(env, instrValue)
594 + valueKind = hasContextRefOperand(state, instrValue)
595 ? ValueKind.Context
596 : ValueKind.Mutable;
597
@@ -642,123 +639,123 @@ function inferBlock(env: Environment, block: BasicBlock) {
639 }
640 case "FunctionExpression": {
641 for (const operand of eachInstructionOperand(instr)) {
645 - env.reference(
642 + state.reference(
643 operand,
644 operand.effect === Effect.Unknown ? Effect.Read : operand.effect
645 );
646 }
650 - env.initialize(instrValue, ValueKind.Mutable);
651 - env.define(instr.lvalue.place, instrValue);
647 + state.initialize(instrValue, ValueKind.Mutable);
648 + state.define(instr.lvalue.place, instrValue);
649 instr.lvalue.place.effect = Effect.Store;
650 continue;
651 }
652 case "PropertyCall": {
656 - if (!env.isDefined(instrValue.receiver)) {
653 + if (!state.isDefined(instrValue.receiver)) {
654 // TODO @josephsavona: improve handling of globals
655 const value: InstructionValue = {
656 kind: "Primitive",
657 loc: instrValue.loc,
658 value: undefined,
659 };
663 - env.initialize(value, ValueKind.Frozen);
664 - env.define(instrValue.receiver, value);
660 + state.initialize(value, ValueKind.Frozen);
661 + state.define(instrValue.receiver, value);
662 }
663
667 - env.reference(instrValue.receiver, Effect.Mutate);
664 + state.reference(instrValue.receiver, Effect.Mutate);
665 for (const arg of instrValue.args) {
669 - env.reference(arg, Effect.Mutate);
666 + state.reference(arg, Effect.Mutate);
667 }
671 - env.initialize(instrValue, ValueKind.Mutable);
672 - env.define(instr.lvalue.place, instrValue);
668 + state.initialize(instrValue, ValueKind.Mutable);
669 + state.define(instr.lvalue.place, instrValue);
670 instr.lvalue.place.effect = Effect.Mutate;
671 continue;
672 }
673 case "ComputedCall": {
677 - if (!env.isDefined(instrValue.receiver)) {
674 + if (!state.isDefined(instrValue.receiver)) {
675 // TODO @josephsavona: improve handling of globals
676 const value: InstructionValue = {
677 kind: "Primitive",
678 loc: instrValue.loc,
679 value: undefined,
680 };
684 - env.initialize(value, ValueKind.Frozen);
685 - env.define(instrValue.receiver, value);
681 + state.initialize(value, ValueKind.Frozen);
682 + state.define(instrValue.receiver, value);
683 }
684
688 - env.reference(instrValue.receiver, Effect.Mutate);
689 - env.reference(instrValue.property, Effect.Read);
685 + state.reference(instrValue.receiver, Effect.Mutate);
686 + state.reference(instrValue.property, Effect.Read);
687 for (const arg of instrValue.args) {
691 - env.reference(arg, Effect.Mutate);
688 + state.reference(arg, Effect.Mutate);
689 }
693 - env.initialize(instrValue, ValueKind.Mutable);
694 - env.define(instr.lvalue.place, instrValue);
690 + state.initialize(instrValue, ValueKind.Mutable);
691 + state.define(instr.lvalue.place, instrValue);
692 instr.lvalue.place.effect = Effect.Mutate;
693 continue;
694 }
695 case "PropertyStore": {
696 const effect =
700 - env.kind(instrValue.object) === ValueKind.Context
697 + state.kind(instrValue.object) === ValueKind.Context
698 ? Effect.Mutate
699 : Effect.Capture;
703 - env.reference(instrValue.value, effect);
704 - env.reference(instrValue.object, Effect.Store);
700 + state.reference(instrValue.value, effect);
701 + state.reference(instrValue.object, Effect.Store);
702
703 const lvalue = instr.lvalue;
707 - env.alias(lvalue.place, instrValue.value);
704 + state.alias(lvalue.place, instrValue.value);
705 lvalue.place.effect = Effect.Store;
706 continue;
707 }
708 case "PropertyLoad": {
712 - if (!env.isDefined(instrValue.object)) {
709 + if (!state.isDefined(instrValue.object)) {
710 // TODO @josephsavona: improve handling of globals
711 const value: InstructionValue = {
712 kind: "Primitive",
713 loc: instrValue.loc,
714 value: undefined,
715 };
719 - env.initialize(value, ValueKind.Frozen);
720 - env.define(instrValue.object, value);
716 + state.initialize(value, ValueKind.Frozen);
717 + state.define(instrValue.object, value);
718 }
719
723 - env.reference(instrValue.object, Effect.Read);
720 + state.reference(instrValue.object, Effect.Read);
721 const lvalue = instr.lvalue;
722 lvalue.place.effect = Effect.Mutate;
726 - env.initialize(instrValue, env.kind(instrValue.object));
727 - env.define(lvalue.place, instrValue);
723 + state.initialize(instrValue, state.kind(instrValue.object));
724 + state.define(lvalue.place, instrValue);
725 continue;
726 }
727 case "ComputedStore": {
728 const effect =
732 - env.kind(instrValue.object) === ValueKind.Context
729 + state.kind(instrValue.object) === ValueKind.Context
730 ? Effect.Mutate
731 : Effect.Capture;
735 - env.reference(instrValue.value, effect);
736 - env.reference(instrValue.property, Effect.Capture);
737 - env.reference(instrValue.object, Effect.Store);
732 + state.reference(instrValue.value, effect);
733 + state.reference(instrValue.property, Effect.Capture);
734 + state.reference(instrValue.object, Effect.Store);
735
736 const lvalue = instr.lvalue;
740 - env.alias(lvalue.place, instrValue.value);
737 + state.alias(lvalue.place, instrValue.value);
738 lvalue.place.effect = Effect.Store;
739 continue;
740 }
741 case "ComputedLoad": {
745 - if (!env.isDefined(instrValue.object)) {
742 + if (!state.isDefined(instrValue.object)) {
743 // TODO @josephsavona: improve handling of globals
744 const value: InstructionValue = {
745 kind: "Primitive",
746 loc: instrValue.loc,
747 value: undefined,
748 };
752 - env.initialize(value, ValueKind.Frozen);
753 - env.define(instrValue.object, value);
749 + state.initialize(value, ValueKind.Frozen);
750 + state.define(instrValue.object, value);
751 }
752
756 - env.reference(instrValue.object, Effect.Read);
757 - env.reference(instrValue.property, Effect.Read);
753 + state.reference(instrValue.object, Effect.Read);
754 + state.reference(instrValue.property, Effect.Read);
755 const lvalue = instr.lvalue;
756 lvalue.place.effect = Effect.Mutate;
760 - env.initialize(instrValue, env.kind(instrValue.object));
761 - env.define(lvalue.place, instrValue);
757 + state.initialize(instrValue, state.kind(instrValue.object));
758 + state.define(lvalue.place, instrValue);
759 continue;
760 }
761 case "TypeCastExpression": {
@@ -768,19 +765,19 @@ function inferBlock(env: Environment, block: BasicBlock) {
765 // x = (y: type) // is equivalent to...
766 // x = y
767 // ```
771 - env.initialize(instrValue, env.kind(instrValue.value));
772 - env.reference(instrValue.value, Effect.Read);
768 + state.initialize(instrValue, state.kind(instrValue.value));
769 + state.reference(instrValue.value, Effect.Read);
770 const lvalue = instr.lvalue;
771 lvalue.place.effect = Effect.Mutate;
775 - env.alias(lvalue.place, instrValue.value);
772 + state.alias(lvalue.place, instrValue.value);
773 continue;
774 }
775 case "Identifier": {
779 - env.reference(instrValue, Effect.Capture);
776 + state.reference(instrValue, Effect.Capture);
777 const lvalue = instr.lvalue;
778 lvalue.place.effect = Effect.Mutate;
779 // direct aliasing: `a = b`;
783 - env.alias(lvalue.place, instrValue);
780 + state.alias(lvalue.place, instrValue);
781 continue;
782 }
783 default: {
@@ -794,11 +791,11 @@ function inferBlock(env: Environment, block: BasicBlock) {
791 "effectKind must be set for instruction value `%s`",
792 instrValue.kind
793 );
797 - env.reference(operand, effectKind);
794 + state.reference(operand, effectKind);
795 }
796
800 - env.initialize(instrValue, valueKind);
801 - env.define(instr.lvalue.place, instrValue);
797 + state.initialize(instrValue, valueKind);
798 + state.define(instr.lvalue.place, instrValue);
799 instr.lvalue.place.effect = lvalueEffect;
800 }
801
@@ -807,13 +804,16 @@ function inferBlock(env: Environment, block: BasicBlock) {
804 ? Effect.Freeze
805 : Effect.Read;
806 for (const operand of eachTerminalOperand(block.terminal)) {
810 - env.reference(operand, effect);
807 + state.reference(operand, effect);
808 }
809 }
810
814 -function hasContextRefOperand(env: Environment, instrValue: InstructionValue) {
811 +function hasContextRefOperand(
812 + state: InferenceState,
813 + instrValue: InstructionValue
814 +) {
815 for (const place of eachInstructionValueOperand(instrValue)) {
816 - if (env.isDefined(place) && env.kind(place) === ValueKind.Context) {
816 + if (state.isDefined(place) && state.kind(place) === ValueKind.Context) {
817 return true;
818 }
819 }