Make LValue non-nullable in HIR
Joe Savona committed
Jan 4, 2023 at 09:51 UTC
e4642326d3d8c27ff0639412ffd953b7606cde18
20 files changed
+74
-98
compiler/forget/src/HIR/BuildHIR.ts
+11
-2
@@ -697,7 +697,13 @@ function lowerStatement(
697
case "WithStatement": {
698
builder.push({
699
id: makeInstructionId(0),
700
- lvalue: null,
700
+ lvalue: {
701
+ place: buildTemporaryPlace(
702
+ builder,
703
+ stmtPath.node.loc ?? GeneratedSource
704
+ ),
705
+ kind: InstructionKind.Const,
706
+ },
707
loc: stmtPath.node.loc ?? GeneratedSource,
708
value: {
709
kind: "OtherStatement",
@@ -1330,7 +1336,10 @@ function lowerExpressionToVoid(
1336
id: makeInstructionId(0),
1337
value: instr,
1338
loc: exprLoc,
1333
- lvalue: null,
1339
+ lvalue: {
1340
+ place: buildTemporaryPlace(builder, exprLoc),
1341
+ kind: InstructionKind.Const,
1342
+ },
1343
});
1344
}
1345
compiler/forget/src/HIR/Codegen.ts
+2
-1
@@ -21,6 +21,7 @@ import {
21
InstructionValue,
22
LValue,
23
Place,
24
+ ReactiveInstruction,
25
SourceLocation,
26
} from "./HIR";
27
import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor";
@@ -293,7 +294,7 @@ export function codegenLabel(id: BlockId): string {
294
295
export function codegenInstruction(
296
temp: Temporaries,
296
- instr: Instruction,
297
+ instr: ReactiveInstruction,
298
value: t.Expression
299
): t.Statement {
300
if (t.isStatement(value)) {
compiler/forget/src/HIR/HIR.ts
+9
-2
@@ -65,10 +65,17 @@ export type ReactiveValueBlock = {
65
};
66
67
export type ReactiveStatement =
68
- | { kind: "instruction"; instruction: Instruction }
68
+ | { kind: "instruction"; instruction: ReactiveInstruction }
69
| { kind: "terminal"; terminal: ReactiveTerminal; label: BlockId | null }
70
| ReactiveScopeBlock;
71
72
+export type ReactiveInstruction = {
73
+ id: InstructionId;
74
+ lvalue: LValue | null;
75
+ value: InstructionValue;
76
+ loc: SourceLocation;
77
+};
78
+
79
export type ReactiveTerminal =
80
| { kind: "break"; label: BlockId | null }
81
| { kind: "continue"; label: BlockId | null }
@@ -225,7 +232,7 @@ export type ForTerminal = {
232
*/
233
export type Instruction = {
234
id: InstructionId;
228
- lvalue: LValue | null;
235
+ lvalue: LValue;
236
value: InstructionValue;
237
loc: SourceLocation;
238
};
compiler/forget/src/HIR/HIRTreeVisitor.ts
-8
@@ -497,14 +497,6 @@ class Driver<
497
let lastValue: { value: InstructionValue; id: InstructionId } | null = null;
498
if (terminalValue != null) {
499
lastValue = terminalValue;
500
- } else {
501
- if (
502
- instructions.length &&
503
- instructions[instructions.length - 1].lvalue === null
504
- ) {
505
- const last = instructions.pop()!;
506
- lastValue = { value: last.value, id: last.id };
507
- }
500
}
501
for (const instr of instructions) {
502
const value = this.visitor.visitValue(instr.value, instr.id);
compiler/forget/src/HIR/InferAlias.ts
-4
@@ -38,9 +38,5 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
38
return;
39
}
40
41
- if (lvalue === null) {
42
- return;
43
- }
44
-
41
state.alias(lvalue, alias);
42
}
compiler/forget/src/HIR/InferAliasForStores.ts
+1
-1
@@ -14,7 +14,7 @@ export function inferAliasForStores(
14
for (const [_, block] of func.body.blocks) {
15
for (const instr of block.instructions) {
16
const { value, lvalue } = instr;
17
- if (lvalue === null || lvalue.place.effect !== Effect.Store) {
17
+ if (lvalue.place.effect !== Effect.Store) {
18
continue;
19
}
20
switch (value.kind) {
compiler/forget/src/HIR/InferMutableLifetimes.ts
+7
-9
@@ -121,17 +121,15 @@ export function inferMutableLifetimes(
121
inferPlace(input, instr, inferMutableRangeForStores);
122
}
123
124
- if (instr.lvalue !== null) {
125
- const lvalueId = instr.lvalue.place.identifier;
124
+ const lvalueId = instr.lvalue.place.identifier;
125
127
- // lvalue start being mutable when they're initially assigned a
128
- // value.
129
- lvalueId.mutableRange.start = instr.id;
126
+ // lvalue start being mutable when they're initially assigned a
127
+ // value.
128
+ lvalueId.mutableRange.start = instr.id;
129
131
- // Let's be optimistic and assume this lvalue is not mutable by
132
- // default.
133
- lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
134
- }
130
+ // Let's be optimistic and assume this lvalue is not mutable by
131
+ // default.
132
+ lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
133
}
134
}
135
}
compiler/forget/src/HIR/InferReferenceEffects.ts
+13
-25
@@ -581,10 +581,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
581
env.reference(instrValue.object, effect);
582
583
const lvalue = instr.lvalue;
584
- if (lvalue !== null) {
585
- env.alias(lvalue.place, instrValue.value);
586
- lvalue.place.effect = Effect.Store;
587
- }
584
+ env.alias(lvalue.place, instrValue.value);
585
+ lvalue.place.effect = Effect.Store;
586
continue;
587
}
588
case "PropertyLoad": {
@@ -601,10 +599,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
599
600
env.reference(instrValue.object, Effect.Read);
601
const lvalue = instr.lvalue;
604
- if (lvalue !== null) {
605
- env.initialize(instrValue, env.kind(instrValue.object));
606
- env.define(lvalue.place, instrValue);
607
- }
602
+ env.initialize(instrValue, env.kind(instrValue.object));
603
+ env.define(lvalue.place, instrValue);
604
continue;
605
}
606
case "ComputedStore": {
@@ -616,10 +612,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
612
env.reference(instrValue.object, effect);
613
614
const lvalue = instr.lvalue;
619
- if (lvalue !== null) {
620
- env.alias(lvalue.place, instrValue.value);
621
- lvalue.place.effect = Effect.Store;
622
- }
615
+ env.alias(lvalue.place, instrValue.value);
616
+ lvalue.place.effect = Effect.Store;
617
continue;
618
}
619
case "ComputedLoad": {
@@ -637,20 +631,16 @@ function inferBlock(env: Environment, block: BasicBlock) {
631
env.reference(instrValue.object, Effect.Read);
632
env.reference(instrValue.property, Effect.Read);
633
const lvalue = instr.lvalue;
640
- if (lvalue !== null) {
641
- env.initialize(instrValue, env.kind(instrValue.object));
642
- env.define(lvalue.place, instrValue);
643
- }
634
+ env.initialize(instrValue, env.kind(instrValue.object));
635
+ env.define(lvalue.place, instrValue);
636
continue;
637
}
638
case "Identifier": {
639
env.reference(instrValue, Effect.Read);
640
const lvalue = instr.lvalue;
649
- if (lvalue !== null) {
650
- lvalue.place.effect = Effect.Mutate;
651
- // direct aliasing: `a = b`;
652
- env.alias(lvalue.place, instrValue);
653
- }
641
+ lvalue.place.effect = Effect.Mutate;
642
+ // direct aliasing: `a = b`;
643
+ env.alias(lvalue.place, instrValue);
644
continue;
645
}
646
default: {
@@ -668,10 +658,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
658
}
659
660
env.initialize(instrValue, valueKind);
671
- if (instr.lvalue !== null) {
672
- env.define(instr.lvalue.place, instrValue);
673
- instr.lvalue.place.effect = lvalueEffect;
674
- }
661
+ env.define(instr.lvalue.place, instrValue);
662
+ instr.lvalue.place.effect = lvalueEffect;
663
}
664
665
const effect =
compiler/forget/src/HIR/InferTypes.ts
+1
-5
@@ -55,11 +55,7 @@ function apply(func: HIRFunction, unifier: Unifier) {
55
place.identifier.type = unifier.get(place.identifier.type);
56
}
57
const { lvalue } = instr;
58
- if (lvalue !== null) {
59
- lvalue.place.identifier.type = unifier.get(
60
- lvalue.place.identifier.type
61
- );
62
- }
58
+ lvalue.place.identifier.type = unifier.get(lvalue.place.identifier.type);
59
}
60
}
61
}
compiler/forget/src/HIR/PrintHIR.ts
+2
-1
@@ -20,6 +20,7 @@ import {
20
MutableRange,
21
Phi,
22
Place,
23
+ ReactiveInstruction,
24
ReactiveScope,
25
SourceLocation,
26
Terminal,
@@ -96,7 +97,7 @@ export function printMixedHIR(
97
}
98
}
99
99
-export function printInstruction(instr: Instruction): string {
100
+export function printInstruction(instr: ReactiveInstruction): string {
101
const id = `[${instr.id}]`;
102
const value = printInstructionValue(instr.value);
103
compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts
-2
@@ -301,9 +301,7 @@ class ReactiveFunctionBuilder
301
}
302
303
function getInstructionScope(instr: Instruction): ReactiveScope | null {
304
- let scope: ReactiveScope | null = null;
304
if (
306
- instr.lvalue !== null &&
305
instr.lvalue.place.identifier.scope !== null &&
306
isScopeActive(instr.lvalue.place.identifier.scope, instr.id)
307
) {
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+2
-2
@@ -18,10 +18,10 @@ import {
18
} from "../HIR/Codegen";
19
import {
20
Identifier,
21
- Instruction,
21
InstructionKind,
22
ReactiveBlock,
23
ReactiveFunction,
24
+ ReactiveInstruction,
25
ReactiveScope,
26
ReactiveScopeDependency,
27
ReactiveTerminal,
@@ -301,7 +301,7 @@ function codegenTerminal(cx: Context, terminal: ReactiveTerminal): t.Statement {
301
302
export function codegenInstructionNullable(
303
cx: Context,
304
- instr: Instruction,
304
+ instr: ReactiveInstruction,
305
value: t.Expression
306
): t.Statement | null {
307
let statement;
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+3
-5
@@ -78,11 +78,9 @@ export function inferReactiveScopeVariables(fn: HIRFunction) {
78
79
for (const instr of block.instructions) {
80
const operands: Array<Identifier> = [];
81
- if (instr.lvalue !== null) {
82
- const range = instr.lvalue.place.identifier.mutableRange;
83
- if (range.end > range.start + 1 || mayAllocate(instr.value)) {
84
- operands.push(instr.lvalue!.place.identifier);
85
- }
81
+ const range = instr.lvalue.place.identifier.mutableRange;
82
+ if (range.end > range.start + 1 || mayAllocate(instr.value)) {
83
+ operands.push(instr.lvalue!.place.identifier);
84
}
85
for (const operand of eachInstructionOperand(instr)) {
86
if (
compiler/forget/src/ReactiveScopes/InferReactiveScopes.ts
-2
@@ -286,7 +286,6 @@ class MergeOverlappingReactiveScopesVisitor
286
visitInstruction(instruction: Instruction, value: void): void {
287
this.visitId(instruction.id);
288
if (
289
- instruction.lvalue !== null &&
289
instruction.lvalue.place.identifier.scope !== null &&
290
instruction.id >= instruction.lvalue.place.identifier.scope.range.start &&
291
instruction.id < instruction.lvalue.place.identifier.scope.range.end
@@ -443,7 +442,6 @@ class AlignReactiveScopesToBlockScopeRangeVisitor
442
443
function getInstructionScope(instr: Instruction): ReactiveScope | null {
444
if (
446
- instr.lvalue !== null &&
445
instr.lvalue.place.identifier.scope !== null &&
446
isActive(instr, instr.lvalue.place.identifier.scope.range)
447
) {
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+2
-2
@@ -7,7 +7,6 @@
7
8
import {
9
Identifier,
10
- Instruction,
10
InstructionId,
11
InstructionKind,
12
InstructionValue,
@@ -16,6 +15,7 @@ import {
15
Place,
16
ReactiveBlock,
17
ReactiveFunction,
18
+ ReactiveInstruction,
19
ReactiveScope,
20
ReactiveScopeDependency,
21
ReactiveValueBlock,
@@ -280,7 +280,7 @@ function visitInstructionValue(
280
}
281
}
282
283
-function visitInstruction(context: Context, instr: Instruction): void {
283
+function visitInstruction(context: Context, instr: ReactiveInstruction): void {
284
const { lvalue } = instr;
285
visitInstructionValue(context, instr.value, lvalue);
286
if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
compiler/forget/src/ReactiveScopes/PruneTemporaryLValues.ts
+2
-2
@@ -7,9 +7,9 @@
7
8
import {
9
Identifier,
10
- Instruction,
10
InstructionKind,
11
ReactiveFunction,
12
+ ReactiveInstruction,
13
} from "../HIR/HIR";
14
import { visitFunction } from "./visitors";
15
@@ -18,7 +18,7 @@ import { visitFunction } from "./visitors";
18
* nulls out the lvalue itself, it does not remove the corresponding instructions.
19
*/
20
export function pruneTemporaryLValues(fn: ReactiveFunction): void {
21
- const lvalues = new Map<Identifier, Instruction>();
21
+ const lvalues = new Map<Identifier, ReactiveInstruction>();
22
visitFunction(fn, {
23
visitInstruction: (instr) => {
24
if (
compiler/forget/src/ReactiveScopes/RenameVariables.ts
+4
-5
@@ -13,10 +13,7 @@ import {
13
ReactiveTerminal,
14
ReactiveValueBlock,
15
} from "../HIR/HIR";
16
-import {
17
- eachInstructionOperand,
18
- eachInstructionValueOperand,
19
-} from "../HIR/visitors";
16
+import { eachInstructionValueOperand } from "../HIR/visitors";
17
import { invariant } from "../Utils/CompilerError";
18
import { assertExhaustive } from "../Utils/utils";
19
@@ -49,7 +46,9 @@ function visitBlockInner(scopes: Scopes, block: ReactiveBlock): void {
46
for (const stmt of block) {
47
switch (stmt.kind) {
48
case "instruction": {
52
- for (const operand of eachInstructionOperand(stmt.instruction)) {
49
+ for (const operand of eachInstructionValueOperand(
50
+ stmt.instruction.value
51
+ )) {
52
scopes.visit(operand.identifier);
53
}
54
if (stmt.instruction.lvalue !== null) {
compiler/forget/src/ReactiveScopes/visitors.ts
+2
-2
@@ -6,11 +6,11 @@
6
*/
7
8
import {
9
- Instruction,
9
InstructionValue,
10
Place,
11
ReactiveBlock,
12
ReactiveFunction,
13
+ ReactiveInstruction,
14
ReactiveScope,
15
ReactiveTerminal,
16
ReactiveValueBlock,
@@ -22,7 +22,7 @@ export function visitFunction(
22
fn: ReactiveFunction,
23
visitors: {
24
visitValue?: (value: InstructionValue) => void;
25
- visitInstruction?: (instr: Instruction) => void;
25
+ visitInstruction?: (instr: ReactiveInstruction) => void;
26
visitTerminal?: (terminal: ReactiveTerminal) => void;
27
visitScope?: (scope: ReactiveScope) => void;
28
}
compiler/forget/src/SSA/EliminateRedundantPhi.ts
+1
-4
@@ -82,10 +82,7 @@ export function eliminateRedundantPhi(fn: HIRFunction) {
82
for (const place of eachInstructionOperand(instr)) {
83
rewritePlace(place, rewrites);
84
}
85
- const { lvalue } = instr;
86
- if (lvalue !== null) {
87
- rewritePlace(lvalue.place, rewrites);
88
- }
85
+ rewritePlace(instr.lvalue.place, rewrites);
86
}
87
88
// Rewrite all terminal operands
compiler/forget/src/SSA/LeaveSSA.ts
+12
-14
@@ -281,21 +281,19 @@ export function leaveSSA(fn: HIRFunction) {
281
// SSA variables to `const` where possible
282
for (const instr of block.instructions) {
283
const { lvalue } = instr;
284
- if (lvalue !== null) {
285
- if (
286
- lvalue.kind === InstructionKind.Const &&
287
- rewrites.has(lvalue.place.identifier)
288
- ) {
289
- // For rewrites, the declaration of the canonical identifier has to be `let`,
290
- // all other assignments are reassignments (which we annotate for codegen
291
- // purposes).
292
- lvalue.kind =
293
- rewrites.get(lvalue.place.identifier) === lvalue.place.identifier
294
- ? InstructionKind.Let
295
- : InstructionKind.Reassign;
296
- }
297
- rewritePlace(lvalue.place, rewrites);
284
+ if (
285
+ lvalue.kind === InstructionKind.Const &&
286
+ rewrites.has(lvalue.place.identifier)
287
+ ) {
288
+ // For rewrites, the declaration of the canonical identifier has to be `let`,
289
+ // all other assignments are reassignments (which we annotate for codegen
290
+ // purposes).
291
+ lvalue.kind =
292
+ rewrites.get(lvalue.place.identifier) === lvalue.place.identifier
293
+ ? InstructionKind.Let
294
+ : InstructionKind.Reassign;
295
}
296
+ rewritePlace(lvalue.place, rewrites);
297
for (const operand of eachInstructionValueOperand(instr.value)) {
298
rewritePlace(operand, rewrites);
299
}