[hir] Generalize alias analysis for Effect.Store
Rather than special casing for field stores, look for Effect.Store to alias fields. In the future, this will be extended to other constructs like Array#push.
Sathya Gunasekaran committed
Dec 21, 2022 at 12:04 UTC
538883935787ec70273995adf962696d1d742ab2
4 files changed
+33
-28
compiler/forget/src/HIR/InferAliasForStores.ts
renamed
+9
-16
@@ -4,33 +4,26 @@
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
*/
7
+import invariant from "invariant";
8
import DisjointSet from "../Utils/DisjointSet";
8
-import { HIRFunction, Identifier } from "./HIR";
9
+import { Effect, HIRFunction, Identifier } from "./HIR";
10
10
-export function inferAliasForFields(
11
+export function inferAliasForStores(
12
func: HIRFunction,
13
aliases: DisjointSet<Identifier>
14
) {
15
for (const [_, block] of func.body.blocks) {
16
for (const instr of block.instructions) {
16
- let { lvalue, value } = instr;
17
-
18
- // Not an aliasing instruction.
19
- if (value.kind !== "Identifier") {
20
- continue;
21
- }
22
-
23
- // No lvalue, no aliasing.
24
- if (lvalue === null) {
17
+ const { value, lvalue } = instr;
18
+ if (lvalue === null || lvalue.place.effect !== Effect.Store) {
19
continue;
20
}
21
28
- // No field aliasing.
29
- if (lvalue.place.memberPath === null) {
30
- continue;
31
- }
22
+ invariant(
23
+ value.kind === "Identifier",
24
+ "only identifiers can be aliased by stores"
25
+ );
26
33
- // lvalue or value get mutated later, so this aliasing is observable.
27
if (
28
lvalue.place.identifier.mutableRange.end > instr.id ||
29
value.identifier.mutableRange.end > instr.id
compiler/forget/src/HIR/InferMutableLifetimes.ts
+18
-6
@@ -60,7 +60,15 @@ import { eachInstructionOperand } from "./visitors";
60
* ```
61
*/
62
63
-function inferPlace(place: Place, instr: Instruction) {
63
+function infer(place: Place, instr: Instruction) {
64
+ place.identifier.mutableRange.end = makeInstructionId(instr.id + 1);
65
+}
66
+
67
+function inferPlace(
68
+ place: Place,
69
+ instr: Instruction,
70
+ inferMutableRangeForStores: boolean
71
+) {
72
switch (place.effect) {
73
case Effect.Unknown: {
74
throw new Error(
@@ -73,8 +81,12 @@ function inferPlace(place: Place, instr: Instruction) {
81
case Effect.Freeze:
82
return;
83
case Effect.Store:
84
+ if (inferMutableRangeForStores) {
85
+ infer(place, instr);
86
+ }
87
+ return;
88
case Effect.Mutate: {
77
- place.identifier.mutableRange.end = makeInstructionId(instr.id + 1);
89
+ infer(place, instr);
90
return;
91
}
92
default:
@@ -84,7 +96,7 @@ function inferPlace(place: Place, instr: Instruction) {
96
97
export function inferMutableLifetimes(
98
func: HIRFunction,
87
- inferMutableRangeForFields: boolean
99
+ inferMutableRangeForStores: boolean
100
) {
101
for (const [_, block] of func.body.blocks) {
102
for (const phi of block.phis) {
@@ -106,7 +118,7 @@ export function inferMutableLifetimes(
118
119
for (const instr of block.instructions) {
120
for (const input of eachInstructionOperand(instr)) {
109
- inferPlace(input, instr);
121
+ inferPlace(input, instr, inferMutableRangeForStores);
122
}
123
124
if (instr.lvalue !== null) {
@@ -120,8 +132,8 @@ export function inferMutableLifetimes(
132
// Let's be optimistic and assume this lvalue is not mutable by
133
// default.
134
lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
123
- } else if (inferMutableRangeForFields) {
124
- inferPlace(instr.lvalue.place, instr);
135
+ } else {
136
+ inferPlace(instr.lvalue.place, instr, inferMutableRangeForStores);
137
}
138
}
139
}
compiler/forget/src/HIR/InferMutableRanges.ts
+2
-2
@@ -1,6 +1,6 @@
1
import { HIRFunction } from "./HIR";
2
import { inferAliases } from "./InferAlias";
3
-import { inferAliasForFields } from "./InferAliasForFields";
3
+import { inferAliasForStores } from "./InferAliasForStores";
4
import { inferMutableLifetimes } from "./InferMutableLifetimes";
5
import { inferMutableRangesForAlias } from "./InferMutableRangesForAlias";
6
@@ -17,7 +17,7 @@ export function inferMutableRanges(ir: HIRFunction) {
17
inferMutableRangesForAlias(aliases);
18
19
// Update aliasing information of fields
20
- inferAliasForFields(ir, aliases);
20
+ inferAliasForStores(ir, aliases);
21
} while (aliases.size > size);
22
23
// Re-infer mutable ranges for all values
compiler/forget/src/__tests__/fixtures/hir/assignment-variations.expect.md
+4
-4
@@ -63,8 +63,8 @@ function f$0() {
63
```
64
bb0:
65
[1] Const mutate $6:TPrimitive = 1
66
- [2] Const mutate $7:TPrimitive = Binary read a$5_@0.b.c + read $6:TPrimitive
67
- [3] Reassign store a$5_@0.b.c[0:6] = read $7:TPrimitive
66
+ [2] Const mutate $7_@0:TPrimitive[0:6] = Binary read a$5_@0.b.c + read $6:TPrimitive
67
+ [3] Reassign store a$5_@0.b.c[0:6] = read $7_@0:TPrimitive
68
[4] Const mutate $8:TPrimitive = 2
69
[5] Reassign mutate a$5_@0.b.c[0:6] = Binary read a$5_@0.b.c * read $8:TPrimitive
70
[6] Return
@@ -77,8 +77,8 @@ function g(
77
a,
78
) {
79
[1] Const mutate $6:TPrimitive = 1
80
- [2] Const mutate $7:TPrimitive = Binary read a$5_@0.b.c + read $6:TPrimitive
81
- [3] Reassign store a$5_@0.b.c[0:6] = read $7:TPrimitive
80
+ [2] Const mutate $7_@0:TPrimitive[0:6] = Binary read a$5_@0.b.c + read $6:TPrimitive
81
+ [3] Reassign store a$5_@0.b.c[0:6] = read $7_@0:TPrimitive
82
[4] Const mutate $8:TPrimitive = 2
83
[5] Reassign mutate a$5_@0.b.c[0:6] = Binary read a$5_@0.b.c * read $8:TPrimitive
84
return