Make LValue non-nullable in HIR

This commit is contained in:
Joe Savona
2023-01-04 09:51:17 -08:00
parent 29bb3f55a3
commit e4642326d3
20 changed files with 74 additions and 98 deletions
+11 -2
View File
@@ -697,7 +697,13 @@ function lowerStatement(
case "WithStatement": {
builder.push({
id: makeInstructionId(0),
lvalue: null,
lvalue: {
place: buildTemporaryPlace(
builder,
stmtPath.node.loc ?? GeneratedSource
),
kind: InstructionKind.Const,
},
loc: stmtPath.node.loc ?? GeneratedSource,
value: {
kind: "OtherStatement",
@@ -1330,7 +1336,10 @@ function lowerExpressionToVoid(
id: makeInstructionId(0),
value: instr,
loc: exprLoc,
lvalue: null,
lvalue: {
place: buildTemporaryPlace(builder, exprLoc),
kind: InstructionKind.Const,
},
});
}
+2 -1
View File
@@ -21,6 +21,7 @@ import {
InstructionValue,
LValue,
Place,
ReactiveInstruction,
SourceLocation,
} from "./HIR";
import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor";
@@ -293,7 +294,7 @@ export function codegenLabel(id: BlockId): string {
export function codegenInstruction(
temp: Temporaries,
instr: Instruction,
instr: ReactiveInstruction,
value: t.Expression
): t.Statement {
if (t.isStatement(value)) {
+9 -2
View File
@@ -65,10 +65,17 @@ export type ReactiveValueBlock = {
};
export type ReactiveStatement =
| { kind: "instruction"; instruction: Instruction }
| { kind: "instruction"; instruction: ReactiveInstruction }
| { kind: "terminal"; terminal: ReactiveTerminal; label: BlockId | null }
| ReactiveScopeBlock;
export type ReactiveInstruction = {
id: InstructionId;
lvalue: LValue | null;
value: InstructionValue;
loc: SourceLocation;
};
export type ReactiveTerminal =
| { kind: "break"; label: BlockId | null }
| { kind: "continue"; label: BlockId | null }
@@ -225,7 +232,7 @@ export type ForTerminal = {
*/
export type Instruction = {
id: InstructionId;
lvalue: LValue | null;
lvalue: LValue;
value: InstructionValue;
loc: SourceLocation;
};
@@ -497,14 +497,6 @@ class Driver<
let lastValue: { value: InstructionValue; id: InstructionId } | null = null;
if (terminalValue != null) {
lastValue = terminalValue;
} else {
if (
instructions.length &&
instructions[instructions.length - 1].lvalue === null
) {
const last = instructions.pop()!;
lastValue = { value: last.value, id: last.id };
}
}
for (const instr of instructions) {
const value = this.visitor.visitValue(instr.value, instr.id);
-4
View File
@@ -38,9 +38,5 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
return;
}
if (lvalue === null) {
return;
}
state.alias(lvalue, alias);
}
@@ -14,7 +14,7 @@ export function inferAliasForStores(
for (const [_, block] of func.body.blocks) {
for (const instr of block.instructions) {
const { value, lvalue } = instr;
if (lvalue === null || lvalue.place.effect !== Effect.Store) {
if (lvalue.place.effect !== Effect.Store) {
continue;
}
switch (value.kind) {
@@ -121,17 +121,15 @@ export function inferMutableLifetimes(
inferPlace(input, instr, inferMutableRangeForStores);
}
if (instr.lvalue !== null) {
const lvalueId = instr.lvalue.place.identifier;
const lvalueId = instr.lvalue.place.identifier;
// lvalue start being mutable when they're initially assigned a
// value.
lvalueId.mutableRange.start = instr.id;
// lvalue start being mutable when they're initially assigned a
// value.
lvalueId.mutableRange.start = instr.id;
// Let's be optimistic and assume this lvalue is not mutable by
// default.
lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
}
// Let's be optimistic and assume this lvalue is not mutable by
// default.
lvalueId.mutableRange.end = makeInstructionId(instr.id + 1);
}
}
}
@@ -581,10 +581,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
env.reference(instrValue.object, effect);
const lvalue = instr.lvalue;
if (lvalue !== null) {
env.alias(lvalue.place, instrValue.value);
lvalue.place.effect = Effect.Store;
}
env.alias(lvalue.place, instrValue.value);
lvalue.place.effect = Effect.Store;
continue;
}
case "PropertyLoad": {
@@ -601,10 +599,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
env.reference(instrValue.object, Effect.Read);
const lvalue = instr.lvalue;
if (lvalue !== null) {
env.initialize(instrValue, env.kind(instrValue.object));
env.define(lvalue.place, instrValue);
}
env.initialize(instrValue, env.kind(instrValue.object));
env.define(lvalue.place, instrValue);
continue;
}
case "ComputedStore": {
@@ -616,10 +612,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
env.reference(instrValue.object, effect);
const lvalue = instr.lvalue;
if (lvalue !== null) {
env.alias(lvalue.place, instrValue.value);
lvalue.place.effect = Effect.Store;
}
env.alias(lvalue.place, instrValue.value);
lvalue.place.effect = Effect.Store;
continue;
}
case "ComputedLoad": {
@@ -637,20 +631,16 @@ function inferBlock(env: Environment, block: BasicBlock) {
env.reference(instrValue.object, Effect.Read);
env.reference(instrValue.property, Effect.Read);
const lvalue = instr.lvalue;
if (lvalue !== null) {
env.initialize(instrValue, env.kind(instrValue.object));
env.define(lvalue.place, instrValue);
}
env.initialize(instrValue, env.kind(instrValue.object));
env.define(lvalue.place, instrValue);
continue;
}
case "Identifier": {
env.reference(instrValue, Effect.Read);
const lvalue = instr.lvalue;
if (lvalue !== null) {
lvalue.place.effect = Effect.Mutate;
// direct aliasing: `a = b`;
env.alias(lvalue.place, instrValue);
}
lvalue.place.effect = Effect.Mutate;
// direct aliasing: `a = b`;
env.alias(lvalue.place, instrValue);
continue;
}
default: {
@@ -668,10 +658,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
}
env.initialize(instrValue, valueKind);
if (instr.lvalue !== null) {
env.define(instr.lvalue.place, instrValue);
instr.lvalue.place.effect = lvalueEffect;
}
env.define(instr.lvalue.place, instrValue);
instr.lvalue.place.effect = lvalueEffect;
}
const effect =
+1 -5
View File
@@ -55,11 +55,7 @@ function apply(func: HIRFunction, unifier: Unifier) {
place.identifier.type = unifier.get(place.identifier.type);
}
const { lvalue } = instr;
if (lvalue !== null) {
lvalue.place.identifier.type = unifier.get(
lvalue.place.identifier.type
);
}
lvalue.place.identifier.type = unifier.get(lvalue.place.identifier.type);
}
}
}
+2 -1
View File
@@ -20,6 +20,7 @@ import {
MutableRange,
Phi,
Place,
ReactiveInstruction,
ReactiveScope,
SourceLocation,
Terminal,
@@ -96,7 +97,7 @@ export function printMixedHIR(
}
}
export function printInstruction(instr: Instruction): string {
export function printInstruction(instr: ReactiveInstruction): string {
const id = `[${instr.id}]`;
const value = printInstructionValue(instr.value);
@@ -301,9 +301,7 @@ class ReactiveFunctionBuilder
}
function getInstructionScope(instr: Instruction): ReactiveScope | null {
let scope: ReactiveScope | null = null;
if (
instr.lvalue !== null &&
instr.lvalue.place.identifier.scope !== null &&
isScopeActive(instr.lvalue.place.identifier.scope, instr.id)
) {
@@ -18,10 +18,10 @@ import {
} from "../HIR/Codegen";
import {
Identifier,
Instruction,
InstructionKind,
ReactiveBlock,
ReactiveFunction,
ReactiveInstruction,
ReactiveScope,
ReactiveScopeDependency,
ReactiveTerminal,
@@ -301,7 +301,7 @@ function codegenTerminal(cx: Context, terminal: ReactiveTerminal): t.Statement {
export function codegenInstructionNullable(
cx: Context,
instr: Instruction,
instr: ReactiveInstruction,
value: t.Expression
): t.Statement | null {
let statement;
@@ -78,11 +78,9 @@ export function inferReactiveScopeVariables(fn: HIRFunction) {
for (const instr of block.instructions) {
const operands: Array<Identifier> = [];
if (instr.lvalue !== null) {
const range = instr.lvalue.place.identifier.mutableRange;
if (range.end > range.start + 1 || mayAllocate(instr.value)) {
operands.push(instr.lvalue!.place.identifier);
}
const range = instr.lvalue.place.identifier.mutableRange;
if (range.end > range.start + 1 || mayAllocate(instr.value)) {
operands.push(instr.lvalue!.place.identifier);
}
for (const operand of eachInstructionOperand(instr)) {
if (
@@ -286,7 +286,6 @@ class MergeOverlappingReactiveScopesVisitor
visitInstruction(instruction: Instruction, value: void): void {
this.visitId(instruction.id);
if (
instruction.lvalue !== null &&
instruction.lvalue.place.identifier.scope !== null &&
instruction.id >= instruction.lvalue.place.identifier.scope.range.start &&
instruction.id < instruction.lvalue.place.identifier.scope.range.end
@@ -443,7 +442,6 @@ class AlignReactiveScopesToBlockScopeRangeVisitor
function getInstructionScope(instr: Instruction): ReactiveScope | null {
if (
instr.lvalue !== null &&
instr.lvalue.place.identifier.scope !== null &&
isActive(instr, instr.lvalue.place.identifier.scope.range)
) {
@@ -7,7 +7,6 @@
import {
Identifier,
Instruction,
InstructionId,
InstructionKind,
InstructionValue,
@@ -16,6 +15,7 @@ import {
Place,
ReactiveBlock,
ReactiveFunction,
ReactiveInstruction,
ReactiveScope,
ReactiveScopeDependency,
ReactiveValueBlock,
@@ -280,7 +280,7 @@ function visitInstructionValue(
}
}
function visitInstruction(context: Context, instr: Instruction): void {
function visitInstruction(context: Context, instr: ReactiveInstruction): void {
const { lvalue } = instr;
visitInstructionValue(context, instr.value, lvalue);
if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
@@ -7,9 +7,9 @@
import {
Identifier,
Instruction,
InstructionKind,
ReactiveFunction,
ReactiveInstruction,
} from "../HIR/HIR";
import { visitFunction } from "./visitors";
@@ -18,7 +18,7 @@ import { visitFunction } from "./visitors";
* nulls out the lvalue itself, it does not remove the corresponding instructions.
*/
export function pruneTemporaryLValues(fn: ReactiveFunction): void {
const lvalues = new Map<Identifier, Instruction>();
const lvalues = new Map<Identifier, ReactiveInstruction>();
visitFunction(fn, {
visitInstruction: (instr) => {
if (
@@ -13,10 +13,7 @@ import {
ReactiveTerminal,
ReactiveValueBlock,
} from "../HIR/HIR";
import {
eachInstructionOperand,
eachInstructionValueOperand,
} from "../HIR/visitors";
import { eachInstructionValueOperand } from "../HIR/visitors";
import { invariant } from "../Utils/CompilerError";
import { assertExhaustive } from "../Utils/utils";
@@ -49,7 +46,9 @@ function visitBlockInner(scopes: Scopes, block: ReactiveBlock): void {
for (const stmt of block) {
switch (stmt.kind) {
case "instruction": {
for (const operand of eachInstructionOperand(stmt.instruction)) {
for (const operand of eachInstructionValueOperand(
stmt.instruction.value
)) {
scopes.visit(operand.identifier);
}
if (stmt.instruction.lvalue !== null) {
@@ -6,11 +6,11 @@
*/
import {
Instruction,
InstructionValue,
Place,
ReactiveBlock,
ReactiveFunction,
ReactiveInstruction,
ReactiveScope,
ReactiveTerminal,
ReactiveValueBlock,
@@ -22,7 +22,7 @@ export function visitFunction(
fn: ReactiveFunction,
visitors: {
visitValue?: (value: InstructionValue) => void;
visitInstruction?: (instr: Instruction) => void;
visitInstruction?: (instr: ReactiveInstruction) => void;
visitTerminal?: (terminal: ReactiveTerminal) => void;
visitScope?: (scope: ReactiveScope) => void;
}
@@ -82,10 +82,7 @@ export function eliminateRedundantPhi(fn: HIRFunction) {
for (const place of eachInstructionOperand(instr)) {
rewritePlace(place, rewrites);
}
const { lvalue } = instr;
if (lvalue !== null) {
rewritePlace(lvalue.place, rewrites);
}
rewritePlace(instr.lvalue.place, rewrites);
}
// Rewrite all terminal operands
+12 -14
View File
@@ -281,21 +281,19 @@ export function leaveSSA(fn: HIRFunction) {
// SSA variables to `const` where possible
for (const instr of block.instructions) {
const { lvalue } = instr;
if (lvalue !== null) {
if (
lvalue.kind === InstructionKind.Const &&
rewrites.has(lvalue.place.identifier)
) {
// For rewrites, the declaration of the canonical identifier has to be `let`,
// all other assignments are reassignments (which we annotate for codegen
// purposes).
lvalue.kind =
rewrites.get(lvalue.place.identifier) === lvalue.place.identifier
? InstructionKind.Let
: InstructionKind.Reassign;
}
rewritePlace(lvalue.place, rewrites);
if (
lvalue.kind === InstructionKind.Const &&
rewrites.has(lvalue.place.identifier)
) {
// For rewrites, the declaration of the canonical identifier has to be `let`,
// all other assignments are reassignments (which we annotate for codegen
// purposes).
lvalue.kind =
rewrites.get(lvalue.place.identifier) === lvalue.place.identifier
? InstructionKind.Let
: InstructionKind.Reassign;
}
rewritePlace(lvalue.place, rewrites);
for (const operand of eachInstructionValueOperand(instr.value)) {
rewritePlace(operand, rewrites);
}