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
}