Add invariant for problematic LeaveSSA case

The new LeaveSSA looks ahead to the phis of fallback blocks. However, HIR can 
sometimes have multiple blocks with the same fallthrough (totally fine), so this 
diff clears the phis of fallbacks as they are reached to avoid reprocessing 
them. This caused a previously incorrect case to now fail, yay.
This commit is contained in:
Joe Savona
2022-12-14 08:42:35 -08:00
parent 4b66531237
commit 73ad571c6b
7 changed files with 72 additions and 97 deletions
+11 -7
View File
@@ -23,6 +23,7 @@ import {
makeType,
Terminal,
} from "./HIR";
import { logHIR } from "./logger";
import { printInstruction } from "./PrintHIR";
import { eachTerminalSuccessor, mapTerminalSuccessors } from "./visitors";
@@ -156,16 +157,19 @@ export default class HIRBuilder {
preds: new Set(),
phis: new Set(),
});
// First reduce indirections and prune unreachable blocks
let reduced = shrink({
let ir: HIR = {
blocks: this.#completed,
entry: this.#entry,
});
};
logHIR("Build (pre-shrink)", ir);
// First reduce indirections and prune unreachable blocks
let shrunk = shrink(ir);
logHIR("Build (shrunk)", shrunk);
// then convert to reverse postorder
const blocks = reversePostorderBlocks(reduced);
markInstructionIds(blocks);
markPredecessors(blocks);
return blocks;
const rpo = reversePostorderBlocks(shrunk);
markInstructionIds(rpo);
markPredecessors(rpo);
return rpo;
}
/**
+13 -2
View File
@@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import invariant from "invariant";
import {
Effect,
GeneratedSource,
@@ -34,6 +35,11 @@ export function leaveSSA(fn: HIRFunction) {
const hasDeclaration: Set<Identifier> = new Set();
for (const [, block] of fn.body.blocks) {
invariant(
block.phis.size === 0,
"Expected all phis to be cleared by predecessors"
);
// Identifiers (from phis) that *may* need a new `let` declaration created. If the original
// variable declaration flows into the phi, then we can reuse its declaration - this is
// discovered during iteration of instructions.
@@ -53,18 +59,25 @@ export function leaveSSA(fn: HIRFunction) {
) {
const fallthrough = fn.body.blocks.get(terminal.fallthrough)!;
phis.push(...fallthrough.phis);
fallthrough.phis.clear();
}
if (terminal.kind === "while" || terminal.kind === "for") {
const test = fn.body.blocks.get(terminal.test)!;
phis.push(...test.phis);
test.phis.clear();
const loop = fn.body.blocks.get(terminal.loop)!;
phis.push(...loop.phis);
loop.phis.clear();
}
if (terminal.kind === "for") {
const init = fn.body.blocks.get(terminal.init)!;
phis.push(...init.phis);
init.phis.clear();
const update = fn.body.blocks.get(terminal.update)!;
phis.push(...update.phis);
update.phis.clear();
// find declarations in the for init
for (const instr of init.instructions) {
@@ -174,8 +187,6 @@ export function leaveSSA(fn: HIRFunction) {
};
block.instructions.push(instr);
}
block.phis.clear();
}
}
+11 -16
View File
@@ -18,9 +18,8 @@ import { inferMutableRanges } from "./InferMutableRanges";
import { inferReactiveScopeDependencies } from "./InferReactiveScopeDependencies";
import { inferReactiveScopes } from "./InferReactiveScopes";
import { inferReactiveScopeVariables } from "./InferReactiveScopeVariables";
import { log } from "./logger";
import { printFunction } from "./PrintHIR";
import { inferTypes } from "./InferTypes";
import { logHIRFunction } from "./logger";
export type CompilerFlags = {
eliminateRedundantPhi: boolean;
@@ -46,47 +45,47 @@ export default function (
const env = new Environment();
const ir = lower(func, env);
logStep("HIR", ir);
logHIRFunction("HIR", ir);
enterSSA(ir, env);
logStep("SSA", ir);
logHIRFunction("SSA", ir);
if (flags.eliminateRedundantPhi) {
eliminateRedundantPhi(ir);
logStep("eliminateRedundantPhi", ir);
logHIRFunction("eliminateRedundantPhi", ir);
}
if (flags.inferTypes) {
inferTypes(ir);
logStep("inferTypes", ir);
logHIRFunction("inferTypes", ir);
}
if (flags.inferReferenceEffects) {
inferReferenceEffects(ir);
logStep("inferReferenceEffects", ir);
logHIRFunction("inferReferenceEffects", ir);
}
if (flags.inferMutableRanges) {
inferMutableRanges(ir);
logStep("inferMutableRanges", ir);
logHIRFunction("inferMutableRanges", ir);
}
if (flags.leaveSSA) {
leaveSSA(ir);
logStep("leaveSSA", ir);
logHIRFunction("leaveSSA", ir);
}
if (flags.inferReactiveScopeVariables) {
inferReactiveScopeVariables(ir);
logStep("inferReactiveScopeVariables", ir);
logHIRFunction("inferReactiveScopeVariables", ir);
}
if (flags.inferReactiveScopes) {
inferReactiveScopes(ir);
logStep("inferReactiveScopes", ir);
logHIRFunction("inferReactiveScopes", ir);
}
if (flags.inferReactiveScopeDependencies) {
inferReactiveScopeDependencies(ir);
logStep("inferReactiveScopeDependencies", ir);
logHIRFunction("inferReactiveScopeDependencies", ir);
}
if (flags.codegen) {
@@ -98,7 +97,3 @@ export default function (
return { ast: null, ir: ir };
}
function logStep(step: string, ir: HIRFunction) {
log(() => `${step}:\n${printFunction(ir)}`);
}
+11
View File
@@ -5,12 +5,23 @@
* LICENSE file in the root directory of this source tree.
*/
import { HIR, HIRFunction } from "./HIR";
import printHIR, { printFunction } from "./PrintHIR";
let ENABLED: boolean = false;
export function toggleLogging(enabled: boolean) {
ENABLED = enabled;
}
export function logHIR(step: string, ir: HIR): void {
log(() => `${step}:\n${printHIR(ir)}`);
}
export function logHIRFunction(step: string, fn: HIRFunction): void {
log(() => `${step}:\n${printFunction(fn)}`);
}
export function log(fn: () => string) {
if (ENABLED) {
const message = fn();
@@ -1,72 +0,0 @@
## Input
```javascript
function foo(a, b, c) {
let x = null;
label: {
if (a) {
x = b;
break label;
}
x = c;
}
return x;
}
```
## HIR
```
bb0:
[1] Const mutate x$8_@0:TPrimitive = null
[2] If (read a$5) then:bb3 else:bb2 fallthrough=bb2
bb3:
predecessor blocks: bb0
[3] Const mutate x$9_@1 = read b$6
[4] Goto bb1
bb2:
predecessor blocks: bb0
[5] Const mutate x$10_@2 = read c$7
[6] Goto bb1
bb1:
predecessor blocks: bb3 bb2
[7] Return read x$11
scope1 [3:4]:
- dependency: read b$6
scope2 [5:6]:
- dependency: read c$7
```
## Reactive Scopes
```
function foo(
a,
b,
c,
) {
[1] Const mutate x$8_@0:TPrimitive = null
if (read a$5) {
[3] Const mutate x$9_@1 = read b$6
}
[5] Const mutate x$10_@2 = read c$7
}
```
## Code
```javascript
function foo$0(a$5, b$6, c$7) {
const x$8 = null;
bb2: if (a$5) {
const x$9 = b$6;
}
const x$10 = c$7;
}
```
@@ -0,0 +1,26 @@
## Input
```javascript
function foo(a, b, c) {
let x = null;
label: {
if (a) {
x = b;
break label;
}
x = c;
}
return x;
}
```
## Error
```
Expected all phis to be cleared by predecessors
```