From e4642326d3d8c27ff0639412ffd953b7606cde18 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Wed, 4 Jan 2023 09:51:17 -0800 Subject: [PATCH] Make LValue non-nullable in HIR --- compiler/forget/src/HIR/BuildHIR.ts | 13 ++++++- compiler/forget/src/HIR/Codegen.ts | 3 +- compiler/forget/src/HIR/HIR.ts | 11 +++++- compiler/forget/src/HIR/HIRTreeVisitor.ts | 8 ---- compiler/forget/src/HIR/InferAlias.ts | 4 -- .../forget/src/HIR/InferAliasForStores.ts | 2 +- .../forget/src/HIR/InferMutableLifetimes.ts | 16 ++++---- .../forget/src/HIR/InferReferenceEffects.ts | 38 +++++++------------ compiler/forget/src/HIR/InferTypes.ts | 6 +-- compiler/forget/src/HIR/PrintHIR.ts | 3 +- .../ReactiveScopes/BuildReactiveFunction.ts | 2 - .../ReactiveScopes/CodegenReactiveFunction.ts | 4 +- .../InferReactiveScopeVariables.ts | 8 ++-- .../src/ReactiveScopes/InferReactiveScopes.ts | 2 - .../PropagateScopeDependencies.ts | 4 +- .../ReactiveScopes/PruneTemporaryLValues.ts | 4 +- .../src/ReactiveScopes/RenameVariables.ts | 9 ++--- .../forget/src/ReactiveScopes/visitors.ts | 4 +- .../forget/src/SSA/EliminateRedundantPhi.ts | 5 +-- compiler/forget/src/SSA/LeaveSSA.ts | 26 ++++++------- 20 files changed, 74 insertions(+), 98 deletions(-) diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 7a76dc2213..98c295c291 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -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, + }, }); } diff --git a/compiler/forget/src/HIR/Codegen.ts b/compiler/forget/src/HIR/Codegen.ts index c792461792..7a1efcbba6 100644 --- a/compiler/forget/src/HIR/Codegen.ts +++ b/compiler/forget/src/HIR/Codegen.ts @@ -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)) { diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 6d26f5a61a..a0c2a6a182 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -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; }; diff --git a/compiler/forget/src/HIR/HIRTreeVisitor.ts b/compiler/forget/src/HIR/HIRTreeVisitor.ts index 5ed4a04fb0..3bc6c34d9f 100644 --- a/compiler/forget/src/HIR/HIRTreeVisitor.ts +++ b/compiler/forget/src/HIR/HIRTreeVisitor.ts @@ -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); diff --git a/compiler/forget/src/HIR/InferAlias.ts b/compiler/forget/src/HIR/InferAlias.ts index 7bed382a12..337c0cad06 100644 --- a/compiler/forget/src/HIR/InferAlias.ts +++ b/compiler/forget/src/HIR/InferAlias.ts @@ -38,9 +38,5 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) { return; } - if (lvalue === null) { - return; - } - state.alias(lvalue, alias); } diff --git a/compiler/forget/src/HIR/InferAliasForStores.ts b/compiler/forget/src/HIR/InferAliasForStores.ts index 256d9caf22..92881ad3de 100644 --- a/compiler/forget/src/HIR/InferAliasForStores.ts +++ b/compiler/forget/src/HIR/InferAliasForStores.ts @@ -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) { diff --git a/compiler/forget/src/HIR/InferMutableLifetimes.ts b/compiler/forget/src/HIR/InferMutableLifetimes.ts index 2c84933eec..e836f256dc 100644 --- a/compiler/forget/src/HIR/InferMutableLifetimes.ts +++ b/compiler/forget/src/HIR/InferMutableLifetimes.ts @@ -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); } } } diff --git a/compiler/forget/src/HIR/InferReferenceEffects.ts b/compiler/forget/src/HIR/InferReferenceEffects.ts index 685265fc3a..0a6c727594 100644 --- a/compiler/forget/src/HIR/InferReferenceEffects.ts +++ b/compiler/forget/src/HIR/InferReferenceEffects.ts @@ -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 = diff --git a/compiler/forget/src/HIR/InferTypes.ts b/compiler/forget/src/HIR/InferTypes.ts index 2e7da0da31..4226ec6340 100644 --- a/compiler/forget/src/HIR/InferTypes.ts +++ b/compiler/forget/src/HIR/InferTypes.ts @@ -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); } } } diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index 040482543a..e25870e3a5 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -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); diff --git a/compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts index 322a550c8a..3b5d24d8e9 100644 --- a/compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts @@ -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) ) { diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index 0c9b939b66..e3274393b9 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -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; diff --git a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts index 3f674ae152..1b18d224a5 100644 --- a/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts +++ b/compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts @@ -78,11 +78,9 @@ export function inferReactiveScopeVariables(fn: HIRFunction) { for (const instr of block.instructions) { const operands: Array = []; - 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 ( diff --git a/compiler/forget/src/ReactiveScopes/InferReactiveScopes.ts b/compiler/forget/src/ReactiveScopes/InferReactiveScopes.ts index 11c9f7ee5c..4e7fa7d19f 100644 --- a/compiler/forget/src/ReactiveScopes/InferReactiveScopes.ts +++ b/compiler/forget/src/ReactiveScopes/InferReactiveScopes.ts @@ -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) ) { diff --git a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts index 5f3a4440c3..f7a1b2914d 100644 --- a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts +++ b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts @@ -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) { diff --git a/compiler/forget/src/ReactiveScopes/PruneTemporaryLValues.ts b/compiler/forget/src/ReactiveScopes/PruneTemporaryLValues.ts index 9ce26cf09f..ccf98d5308 100644 --- a/compiler/forget/src/ReactiveScopes/PruneTemporaryLValues.ts +++ b/compiler/forget/src/ReactiveScopes/PruneTemporaryLValues.ts @@ -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(); + const lvalues = new Map(); visitFunction(fn, { visitInstruction: (instr) => { if ( diff --git a/compiler/forget/src/ReactiveScopes/RenameVariables.ts b/compiler/forget/src/ReactiveScopes/RenameVariables.ts index f62ade7926..3cdc50d78d 100644 --- a/compiler/forget/src/ReactiveScopes/RenameVariables.ts +++ b/compiler/forget/src/ReactiveScopes/RenameVariables.ts @@ -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) { diff --git a/compiler/forget/src/ReactiveScopes/visitors.ts b/compiler/forget/src/ReactiveScopes/visitors.ts index 75c34f1b9e..e23dd5844e 100644 --- a/compiler/forget/src/ReactiveScopes/visitors.ts +++ b/compiler/forget/src/ReactiveScopes/visitors.ts @@ -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; } diff --git a/compiler/forget/src/SSA/EliminateRedundantPhi.ts b/compiler/forget/src/SSA/EliminateRedundantPhi.ts index 21c4fc81f9..ee6657c36a 100644 --- a/compiler/forget/src/SSA/EliminateRedundantPhi.ts +++ b/compiler/forget/src/SSA/EliminateRedundantPhi.ts @@ -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 diff --git a/compiler/forget/src/SSA/LeaveSSA.ts b/compiler/forget/src/SSA/LeaveSSA.ts index e5c30cb3f4..abc52f6a20 100644 --- a/compiler/forget/src/SSA/LeaveSSA.ts +++ b/compiler/forget/src/SSA/LeaveSSA.ts @@ -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); }