LoadLocal instruction

Changes InstructionValue::Place to InstructionValue::LoadLocal for clarity, this 
is intended as the only instruction where a variable can appear as an operand. 
All other instructions operands will be temporaries.
This commit is contained in:
Joe Savona
2023-03-01 16:27:34 -08:00
parent 08c1eda7ff
commit 71db40c6ff
26 changed files with 152 additions and 129 deletions
+48 -30
View File
@@ -110,7 +110,7 @@ export function lower(
param.node.loc ?? GeneratedSource,
InstructionKind.Let,
param,
place
{ kind: "LoadLocal", place, loc: place.loc }
);
} else {
builder.errors.push({
@@ -628,7 +628,7 @@ function lowerStatement(
const stmt = stmtPath as NodePath<t.ExpressionStatement>;
const expression = stmt.get("expression");
const value = lowerExpression(builder, expression);
if (expression.isAssignmentExpression() && value.kind === "Identifier") {
if (expression.isAssignmentExpression() && value.kind === "LoadLocal") {
// already lowered to a place
return;
}
@@ -721,7 +721,12 @@ function lowerExpression(
switch (exprNode.type) {
case "Identifier": {
const expr = exprPath as NodePath<t.Identifier>;
return lowerIdentifier(builder, expr);
const place = lowerIdentifier(builder, expr);
return {
kind: "LoadLocal",
place,
loc: exprLoc,
};
}
case "NullLiteral": {
return {
@@ -958,7 +963,11 @@ function lowerExpression(
});
return { kind: "UnsupportedNode", node: expr.node, loc: exprLoc };
}
return last;
return {
kind: "LoadLocal", // TODO: LoadTemp
place: last,
loc: last.loc,
};
}
case "ConditionalExpression": {
const expr = exprPath as NodePath<t.ConditionalExpression>;
@@ -974,7 +983,7 @@ function lowerExpression(
builder.push({
id: makeInstructionId(0),
lvalue: { kind: InstructionKind.Reassign, place: { ...place } },
value: lowerExpressionToTemporary(builder, expr.get("consequent")),
value: lowerExpression(builder, expr.get("consequent")),
loc: exprLoc,
});
return {
@@ -989,7 +998,7 @@ function lowerExpression(
builder.push({
id: makeInstructionId(0),
lvalue: { kind: InstructionKind.Reassign, place: { ...place } },
value: lowerExpressionToTemporary(builder, expr.get("alternate")),
value: lowerExpression(builder, expr.get("alternate")),
loc: exprLoc,
});
return {
@@ -1021,7 +1030,7 @@ function lowerExpression(
},
continuationBlock
);
return place;
return { kind: "LoadLocal", place, loc: place.loc };
}
case "LogicalExpression": {
const expr = exprPath as NodePath<t.LogicalExpression>;
@@ -1037,7 +1046,11 @@ function lowerExpression(
builder.push({
id: makeInstructionId(0),
lvalue: { kind: InstructionKind.Reassign, place: { ...place } },
value: { ...leftPlace },
value: {
kind: "LoadLocal",
place: { ...leftPlace },
loc: leftPlace.loc,
},
loc: exprLoc,
});
return {
@@ -1051,7 +1064,7 @@ function lowerExpression(
builder.push({
id: makeInstructionId(0),
lvalue: { kind: InstructionKind.Reassign, place: { ...place } },
value: lowerExpressionToTemporary(builder, expr.get("right")),
value: lowerExpression(builder, expr.get("right")),
loc: exprLoc,
});
return {
@@ -1075,7 +1088,7 @@ function lowerExpression(
builder.push({
id: makeInstructionId(0),
lvalue: { kind: InstructionKind.Reassign, place: { ...leftPlace } },
value: lowerExpressionToTemporary(builder, expr.get("left")),
value: lowerExpression(builder, expr.get("left")),
loc: exprLoc,
});
builder.terminateWithContinuation(
@@ -1088,7 +1101,7 @@ function lowerExpression(
},
continuationBlock
);
return place;
return { kind: "LoadLocal", place, loc: place.loc };
}
case "AssignmentExpression": {
const expr = exprPath as NodePath<t.AssignmentExpression>;
@@ -1096,7 +1109,7 @@ function lowerExpression(
if (builder.currentBlockKind() === "value") {
// try lowering the RHS in case it also contains errors
lowerExpressionToTemporary(builder, expr.get("right"));
lowerExpression(builder, expr.get("right"));
builder.errors.push({
reason: `(BuildHIR::lowerExpression) Handle AssignmentExpression within a LogicalExpression or ConditionalExpression`,
severity: ErrorSeverity.Todo,
@@ -1112,8 +1125,7 @@ function lowerExpression(
left.node.loc ?? GeneratedSource,
InstructionKind.Reassign,
left,
// NOTE: it's okay not to lower to a temporary here because this is the entire RHS value, not a single operand
lowerExpressionToPlace(builder, expr.get("right"))
lowerExpression(builder, expr.get("right"))
);
}
@@ -1159,7 +1171,7 @@ function lowerExpression(
},
loc: exprLoc,
});
return place;
return { kind: "LoadLocal", place, loc: exprLoc };
}
case "MemberExpression": {
// a.b.c += <right>
@@ -1243,7 +1255,7 @@ function lowerExpression(
value,
loc: exprLoc,
});
return place;
return { kind: "LoadLocal", place, loc: place.loc };
}
case "JSXElement": {
const expr = exprPath as NodePath<t.JSXElement>;
@@ -1496,21 +1508,27 @@ function lowerExpression(
},
loc: expr.node.loc ?? GeneratedSource,
});
const identifier = argument as NodePath<t.Identifier>;
const place = lowerIdentifier(builder, identifier);
const identifier = lowerIdentifier(
builder,
argument as NodePath<t.Identifier>
);
builder.push({
id: makeInstructionId(0),
lvalue: { place: { ...place }, kind: InstructionKind.Reassign },
lvalue: { place: { ...identifier }, kind: InstructionKind.Reassign },
value: {
kind: "BinaryExpression",
operator: expr.node.operator === "++" ? "+" : "-",
left: { ...place },
left: { ...identifier },
right: { ...temp },
loc: exprLoc,
},
loc: exprLoc,
});
return place;
return {
kind: "LoadLocal",
place: { ...identifier },
loc: exprLoc,
};
}
default: {
builder.errors.push({
@@ -1703,8 +1721,8 @@ function lowerExpressionToTemporary(
exprPath: NodePath<t.Expression>
): Place {
const value = lowerExpression(builder, exprPath);
if (value.kind === "Identifier" && value.identifier.name === null) {
return value;
if (value.kind === "LoadLocal" && value.place.identifier.name === null) {
return value.place;
}
const exprLoc = exprPath.node.loc ?? GeneratedSource;
const place: Place = buildTemporaryPlace(builder, exprLoc);
@@ -1722,8 +1740,8 @@ function lowerExpressionToPlace(
exprPath: NodePath<t.Expression>
): Place {
const value = lowerExpression(builder, exprPath);
if (value.kind === "Identifier") {
return value;
if (value.kind === "LoadLocal") {
return value.place;
}
const exprLoc = exprPath.node.loc ?? GeneratedSource;
const place: Place = buildTemporaryPlace(builder, exprLoc);
@@ -1855,15 +1873,15 @@ function lowerAssignment(
value,
loc,
});
return place;
return { kind: "LoadLocal", place, loc: place.loc };
}
case "MemberExpression": {
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
const property = lvalue.get("property");
const object = lowerExpressionToTemporary(builder, lvalue.get("object"));
let valuePlace: Place;
if (value.kind === "Identifier") {
valuePlace = value;
if (value.kind === "LoadLocal") {
valuePlace = value.place;
} else {
valuePlace = buildTemporaryPlace(builder, loc);
builder.push({
@@ -1958,7 +1976,7 @@ function lowerAssignment(
}
return hasError
? { kind: "UnsupportedNode", node: lvalueNode, loc }
: arrayPlace;
: { kind: "LoadLocal", place: arrayPlace, loc: arrayPlace.loc };
}
case "ObjectPattern": {
const lvalue = lvaluePath as NodePath<t.ObjectPattern>;
@@ -2013,7 +2031,7 @@ function lowerAssignment(
}
return hasError
? { kind: "UnsupportedNode", node: lvalueNode, loc }
: objectPlace;
: { kind: "LoadLocal", place: objectPlace, loc: objectPlace.loc };
}
default: {
builder.errors.push({
+5 -1
View File
@@ -396,7 +396,11 @@ export type Phi = {
* Operands are therefore always a Place.
*/
export type InstructionValue =
| Place
| {
kind: "LoadLocal";
place: Place;
loc: SourceLocation;
}
| {
kind: "Primitive";
value: number | boolean | string | null | undefined;
@@ -69,9 +69,13 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
kind: InstructionKind.Const,
},
value: {
kind: "Identifier",
identifier: operand,
effect: Effect.Read,
kind: "LoadLocal",
place: {
kind: "Identifier",
identifier: operand,
effect: Effect.Read,
loc: GeneratedSource,
},
loc: GeneratedSource,
},
loc: GeneratedSource,
+2 -2
View File
@@ -304,8 +304,8 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
value = `UnsupportedNode(${generate(instrValue.node).code})`;
break;
}
case "Identifier": {
value = printPlace(instrValue);
case "LoadLocal": {
value = `LoadLocal ${printPlace(instrValue.place)}`;
break;
}
case "PropertyLoad": {
+4 -4
View File
@@ -48,8 +48,8 @@ export function* eachInstructionValueOperand(
yield* instrValue.args;
break;
}
case "Identifier": {
yield instrValue;
case "LoadLocal": {
yield instrValue.place;
break;
}
case "PropertyLoad": {
@@ -177,8 +177,8 @@ export function mapInstructionOperands(
instrValue.value = fn(instrValue.value);
break;
}
case "Identifier": {
instr.value = fn(instrValue);
case "LoadLocal": {
instrValue.place = fn(instrValue.place);
break;
}
case "NewExpression":
@@ -62,9 +62,9 @@ export default function analyseFunctions(func: HIRFunction) {
);
break;
}
case "Identifier": {
case "LoadLocal": {
if (instr.lvalue.place.identifier.name === null) {
state.declareTemporary(instr.lvalue.place, instr.value);
state.declareTemporary(instr.lvalue.place, instr.value.place);
}
break;
}
@@ -40,9 +40,13 @@ export default function (func: HIRFunction) {
// after:
// foo = $19
instr.value = {
kind: "Identifier",
identifier: fn.identifier,
effect: Effect.Unknown,
kind: "LoadLocal",
place: {
kind: "Identifier",
identifier: fn.identifier,
effect: Effect.Unknown,
loc: instr.value.loc,
},
loc: instr.value.loc,
};
}
+3 -3
View File
@@ -33,11 +33,11 @@ function inferInstr(instr: Instruction, state: AliasAnalyser) {
const { lvalue, value: instrValue } = instr;
let alias: Place | null = null;
switch (instrValue.kind) {
case "Identifier": {
if (isPrimitiveType(instrValue.identifier)) {
case "LoadLocal": {
if (isPrimitiveType(instrValue.place.identifier)) {
return;
}
alias = instrValue;
alias = instrValue.place;
break;
}
case "ComputedLoad":
@@ -188,7 +188,7 @@ class InferenceState {
*/
initialize(value: InstructionValue, kind: ValueKind) {
invariant(
value.kind !== "Identifier",
value.kind !== "LoadLocal",
"Expected all top-level identifiers to be defined as variables, not values"
);
this.#values.set(value, kind);
@@ -782,12 +782,12 @@ function inferBlock(
state.alias(lvalue.place, instrValue.value);
continue;
}
case "Identifier": {
state.reference(instrValue, Effect.Capture);
case "LoadLocal": {
state.reference(instrValue.place, Effect.Capture);
const lvalue = instr.lvalue;
lvalue.place.effect = Effect.Mutate;
// direct aliasing: `a = b`;
state.alias(lvalue.place, instrValue);
state.alias(lvalue.place, instrValue.place);
continue;
}
default: {
@@ -308,8 +308,8 @@ function evaluateInstruction(
}
return null;
}
case "Identifier": {
return read(constants, value);
case "LoadLocal": {
return read(constants, value.place);
}
default: {
// TODO: handle more cases
@@ -100,7 +100,7 @@ function pruneableValue(value: InstructionValue): boolean {
case "ComputedLoad":
case "ComputedStore":
case "FunctionExpression":
case "Identifier":
case "LoadLocal":
case "JsxExpression":
case "JsxFragment":
case "JSXText":
@@ -484,7 +484,11 @@ class Driver {
return {
block: defaultBlock.id,
place: defaultBlock.terminal.test,
value: defaultBlock.terminal.test,
value: {
kind: "LoadLocal",
place: defaultBlock.terminal.test,
loc: defaultBlock.terminal.test.loc,
},
id: defaultBlock.terminal.id,
};
} else if (defaultBlock.instructions.length === 1) {
@@ -696,8 +696,8 @@ function codegenInstructionValue(
);
break;
}
case "Identifier": {
value = codegenPlace(cx, instrValue);
case "LoadLocal": {
value = codegenPlace(cx, instrValue.place);
break;
}
case "FunctionExpression": {
@@ -714,7 +714,7 @@ function codegenInstructionValue(
}
case "TypeCastExpression": {
value = t.typeCastExpression(
codegenInstructionValue(cx, instrValue.value),
codegenPlace(cx, instrValue.value),
instrValue.type
);
break;
@@ -171,7 +171,7 @@ function mayAllocate(value: InstructionValue): boolean {
case "LoadGlobal":
case "TypeCastExpression":
case "BinaryExpression":
case "Identifier":
case "LoadLocal":
case "PropertyLoad":
case "ComputedLoad":
case "JSXText":
@@ -57,7 +57,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
if (
instruction.lvalue !== null &&
instruction.lvalue.place.identifier.name === null &&
instruction.value.kind !== "Identifier"
instruction.value.kind !== "LoadLocal"
) {
state.temporaries.set(instruction.lvalue.place.identifier, 0);
}
@@ -676,14 +676,14 @@ function visitInstructionValue(
value: ReactiveValue,
lvalue: LValue | null
): void {
if (value.kind === "Identifier" && lvalue !== null) {
if (value.kind === "LoadLocal" && lvalue !== null) {
if (
value.identifier.name !== null &&
value.place.identifier.name !== null &&
lvalue.place.identifier.name === null
) {
context.declareTemporary(lvalue.place, value);
context.declareTemporary(lvalue.place, value.place);
} else {
context.visitOperand(value);
context.visitOperand(value.place);
}
} else if (value.kind === "PropertyLoad") {
if (lvalue !== null) {
@@ -91,12 +91,12 @@ export class ReactiveFunctionVisitor<TState = void> {
}
case "return": {
if (terminal.value !== null) {
this.visitValue(terminal.id, terminal.value, state);
this.visitPlace(terminal.id, terminal.value, state);
}
break;
}
case "throw": {
this.visitValue(terminal.id, terminal.value, state);
this.visitPlace(terminal.id, terminal.value, state);
break;
}
case "for": {
@@ -112,7 +112,7 @@ export class ReactiveFunctionVisitor<TState = void> {
break;
}
case "if": {
this.visitValue(terminal.id, terminal.test, state);
this.visitPlace(terminal.id, terminal.test, state);
this.visitBlock(terminal.consequent, state);
if (terminal.alternate !== null) {
this.visitBlock(terminal.alternate, state);
@@ -120,7 +120,7 @@ export class ReactiveFunctionVisitor<TState = void> {
break;
}
case "switch": {
this.visitValue(terminal.id, terminal.test, state);
this.visitPlace(terminal.id, terminal.test, state);
for (const case_ of terminal.cases) {
if (case_.test !== null) {
this.visitPlace(terminal.id, case_.test, state);
+7 -3
View File
@@ -240,9 +240,13 @@ export function leaveSSA(fn: HIRFunction): void {
value:
initOperand !== null
? {
kind: "Identifier",
identifier: initOperand,
effect: Effect.Read,
kind: "LoadLocal",
place: {
kind: "Identifier",
identifier: initOperand,
effect: Effect.Read,
loc: GeneratedSource,
},
loc: GeneratedSource,
}
: {
@@ -107,8 +107,8 @@ function* generateInstructionTypes(
break;
}
case "Identifier": {
yield equation(left, value.identifier.type);
case "LoadLocal": {
yield equation(left, value.place.identifier.type);
break;
}
@@ -24,14 +24,12 @@ function useBar(props) {
let z = undefined;
if (props.a) {
if (props.b) {
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = baz();
$[0] = t0;
z = baz();
$[0] = z;
} else {
t0 = $[0];
z = $[0];
}
z = t0;
}
}
return z;
@@ -30,17 +30,16 @@ function component(a) {
z = $[1];
}
const c_2 = $[2] !== z;
let t0;
let x;
if (c_2) {
t0 = function () {
x = function () {
z;
};
$[2] = z;
$[3] = t0;
$[3] = x;
} else {
t0 = $[3];
x = $[3];
}
const x = t0;
return x;
}
@@ -33,14 +33,12 @@ function Component(props) {
if (cond) {
a = x;
} else {
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [];
$[0] = t0;
a = [];
$[0] = a;
} else {
t0 = $[0];
a = $[0];
}
a = t0;
}
useFreeze(a);
@@ -23,26 +23,22 @@ function foo(a, b, c, d) {
let x = undefined;
if (someVal) {
const c_0 = $[0] !== b;
let t0;
if (c_0) {
t0 = { b: b };
x = { b: b };
$[0] = b;
$[1] = t0;
$[1] = x;
} else {
t0 = $[1];
x = $[1];
}
x = t0;
} else {
const c_2 = $[2] !== c;
let t1;
if (c_2) {
t1 = { c: c };
x = { c: c };
$[2] = c;
$[3] = t1;
$[3] = x;
} else {
t1 = $[3];
x = $[3];
}
x = t1;
}
return x;
}
@@ -36,29 +36,27 @@ function Component(props) {
}
const y = x;
if (props.p1) {
let t0;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [];
$[2] = t0;
x = [];
$[2] = x;
} else {
t0 = $[2];
x = $[2];
}
x = t0;
}
y.push(props.p2);
const c_3 = $[3] !== x;
const c_4 = $[4] !== y;
let t1;
let t0;
if (c_3 || c_4) {
t1 = <Component x={x} y={y}></Component>;
t0 = <Component x={x} y={y}></Component>;
$[3] = x;
$[4] = y;
$[5] = t1;
$[5] = t0;
} else {
t1 = $[5];
t0 = $[5];
}
return t1;
return t0;
}
```
@@ -30,14 +30,12 @@ function Component(props) {
x = [];
x.push(props.p0);
y = x;
let t0;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [];
$[4] = t0;
x = [];
$[4] = x;
} else {
t0 = $[4];
x = $[4];
}
x = t0;
y.push(props.p1);
$[0] = props.p0;
@@ -50,16 +48,16 @@ function Component(props) {
}
const c_5 = $[5] !== x;
const c_6 = $[6] !== y;
let t1;
let t0;
if (c_5 || c_6) {
t1 = <Component x={x} y={y}></Component>;
t0 = <Component x={x} y={y}></Component>;
$[5] = x;
$[6] = y;
$[7] = t1;
$[7] = t0;
} else {
t1 = $[7];
t0 = $[7];
}
return t1;
return t0;
}
```
@@ -45,14 +45,12 @@ function Component(props) {
}
case true: {
x.push(props.p2);
let t0;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t0 = [];
$[3] = t0;
y = [];
$[3] = y;
} else {
t0 = $[3];
y = $[3];
}
y = t0;
break bb1;
}
default: {
@@ -81,16 +79,16 @@ function Component(props) {
y.push(props.p4);
const c_6 = $[6] !== y;
const c_7 = $[7] !== child;
let t1;
let t0;
if (c_6 || c_7) {
t1 = <Component data={y}>{child}</Component>;
t0 = <Component data={y}>{child}</Component>;
$[6] = y;
$[7] = child;
$[8] = t1;
$[8] = t0;
} else {
t1 = $[8];
t0 = $[8];
}
return t1;
return t0;
}
```