Optimize var flattening #27

Merged
TopchetoEU merged 2 commits from TopchetoEU/optimize-var-flatten into master 2024-09-05 19:32:48 +00:00
10 changed files with 113 additions and 65 deletions
Showing only changes of commit 6f548ce5ff - Show all commits

View File

@ -3,12 +3,13 @@ package me.topchetoeu.jscript.common;
public class FunctionBody {
public final FunctionBody[] children;
public final Instruction[] instructions;
public final int localsN, capturesN, length;
public final int localsN, capturablesN, capturesN, length;
public FunctionBody(int localsN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
public FunctionBody(int localsN, int capturablesN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
this.children = children;
this.length = length;
this.localsN = localsN;
this.capturablesN = capturablesN;
this.capturesN = capturesN;
this.instructions = instructions;
}

View File

@ -35,20 +35,21 @@ public class Instruction {
LOAD_FUNC(0x30),
LOAD_ARR(0x31),
LOAD_OBJ(0x32),
LOAD_GLOB(0x33),
LOAD_INTRINSICS(0x34),
LOAD_REGEX(0x35),
LOAD_REGEX(0x33),
LOAD_GLOB(0x38),
LOAD_INTRINSICS(0x39),
LOAD_ARGS(0x3A),
LOAD_REST_ARGS(0x3B),
LOAD_CALLEE(0x3C),
LOAD_THIS(0x3D),
LOAD_ERROR(0x3E),
LOAD_VAR(0x40),
LOAD_MEMBER(0x41),
LOAD_MEMBER_INT(0x42),
LOAD_MEMBER_STR(0x43),
LOAD_ARGS(0x44),
LOAD_REST_ARGS(0x45),
LOAD_CALLEE(0x46),
LOAD_THIS(0x47),
STORE_VAR(0x48),
STORE_MEMBER(0x49),
STORE_MEMBER_INT(0x4A),
@ -368,6 +369,9 @@ public class Instruction {
public static Instruction loadIntrinsics(String key) {
return new Instruction(Type.LOAD_INTRINSICS, key);
}
public static Instruction loadError() {
return new Instruction(Type.LOAD_ERROR);
}
public static Instruction loadMember() {
return new Instruction(Type.LOAD_MEMBER);
}
@ -457,12 +461,15 @@ public class Instruction {
return new Instruction(Type.OPERATION, op);
}
public static Instruction stackAlloc(int n) {
return new Instruction(Type.STACK_ALLOC, n);
public static Instruction stackAlloc(int start, int n) {
return new Instruction(Type.STACK_ALLOC, start, start + n);
}
public static Instruction stackRealloc(int n) {
return new Instruction(Type.STACK_REALLOC, n);
public static Instruction stackRealloc(int start, int n) {
return new Instruction(Type.STACK_REALLOC, start, start + n);
}
/**
* @deprecated
*/
public static Instruction stackFree(int n) {
return new Instruction(Type.STACK_FREE, n);
}

View File

@ -111,7 +111,7 @@ public final class CompileResult {
}
return new FunctionBody(
scope.localsCount() + scope.allocCount(), scope.capturesCount(),
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren
);
}

View File

@ -25,7 +25,7 @@ public class CompoundNode extends Node {
var subtarget = hasScope ? target.subtarget() : target;
if (hasScope) {
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
subtarget.scope.singleEntry = singleEntry;
}
@ -45,10 +45,7 @@ public class CompoundNode extends Node {
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
}
if (hasScope) {
subtarget.scope.end();
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
}
if (hasScope) subtarget.scope.end();
if (!polluted && pollute) {
target.add(Instruction.pushUndefined());

View File

@ -26,7 +26,7 @@ public class ForNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) {
var subtarget = target.subtarget();
subtarget.scope.singleEntry = false;
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
@ -40,7 +40,7 @@ public class ForNode extends Node {
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
LabelContext.popLoop(subtarget.env, label);
subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.allocCount()));
subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
int endI = subtarget.size();
@ -52,7 +52,6 @@ public class ForNode extends Node {
if (pollute) subtarget.add(Instruction.pushUndefined());
subtarget.scope.end();
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
}
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {

View File

@ -44,7 +44,7 @@ public class SwitchNode extends Node {
value.compile(target, true, BreakpointType.STEP_OVER);
var subtarget = target.subtarget();
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
// TODO: create a jump map
for (var ccase : cases) {
@ -65,7 +65,6 @@ public class SwitchNode extends Node {
LabelContext.getBreak(target.env).pop(label);
subtarget.scope.end();
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
int endI = subtarget.size();
end.set(endI);

View File

@ -23,8 +23,8 @@ public class TryNode extends Node {
@Override public void resolve(CompileResult target) {
tryBody.resolve(target);
catchBody.resolve(target);
finallyBody.resolve(target);
if (catchBody != null) catchBody.resolve(target);
if (finallyBody != null) finallyBody.resolve(target);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
@ -43,8 +43,14 @@ public class TryNode extends Node {
if (captureName != null) {
var subtarget = target.subtarget();
subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
subtarget.scope.singleEntry = true;
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
subtarget.add(Instruction.loadError());
subtarget.add(_i -> catchVar.index().toSet(false));
catchBody.compile(subtarget, false);
subtarget.scope.end();
}
else catchBody.compile(target, false);

View File

@ -9,8 +9,8 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class Scope {
protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::parentVarOffset);
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::parentCapOffset);
protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
private boolean ended = false;
private boolean finished = false;
@ -41,14 +41,14 @@ public class Scope {
return var;
}
private final int parentVarOffset() {
if (parent != null) return parent.variableOffset();
else return 0;
}
private final int parentCapOffset() {
if (parent != null) return parent.capturedOffset();
else return localsCount();
}
// private final int parentVarOffset() {
// if (parent != null) return parent.variableOffset();
// else return 0;
// }
// private final int parentCapOffset() {
// if (parent != null) return parent.capturedOffset();
// else return localsCount();
// }
protected final SyntaxException alreadyDefinedErr(Location loc, String name) {
return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name));
@ -118,12 +118,28 @@ public class Scope {
* Gets the index offset from this scope to its children
*/
public final int variableOffset() {
if (parent != null) return parent.variableOffset() + variables.size();
else return variables.size();
var res = 0;
for (var curr = parent; curr != null; curr = curr.parent) {
res += parent.variables.size();
}
return res;
// if (parent != null) return parent.variableOffset() + variables.size();
// else return variables.size();
}
public final int capturedOffset() {
if (parent != null) return parent.capturedOffset() + captured.size();
else return localsCount() + captured.size();
public final int capturablesOffset() {
var res = 0;
for (var curr = this; curr != null; curr = curr.parent) {
if (curr != this) res += parent.captured.size();
if (curr.parent == null) res += curr.localsCount();
}
return res;
// if (parent != null) return parent.capturedOffset() + captured.size();
// else return localsCount() + captured.size();
}
public int localsCount() {
@ -142,7 +158,7 @@ public class Scope {
}
public int capturablesCount() {
var res = captured.size();
for (var child : children) res += child.allocCount();
for (var child : children) res += child.capturablesCount();
return res;
}

View File

@ -1,8 +1,7 @@
package me.topchetoeu.jscript.runtime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
@ -99,7 +98,8 @@ public final class Frame {
* A list of one-element arrays of values. This is so that we can pass captures to other functions
*/
public final Value[][] captures;
public final List<Value[]> locals = new ArrayList<>();
public final Value[] locals;
public final Value[][] capturables;
public final Value argsVal;
public Value self;
public Value fakeArgs;
@ -110,9 +110,20 @@ public final class Frame {
public final Environment env;
private final DebugContext dbg;
public Value[] getVar(int i) {
public Value getVar(int i) {
if (i < 0) return captures[~i][0];
else if (i < locals.length) return locals[i];
else return capturables[i - locals.length][0];
}
public Value setVar(int i, Value val) {
if (i < 0) return captures[~i][0] = val;
else if (i < locals.length) return locals[i] = val;
else return capturables[i - locals.length][0] = val;
}
public Value[] captureVar(int i) {
if (i < 0) return captures[~i];
else return locals.get(i);
if (i >= locals.length) return capturables[i - locals.length];
else throw new RuntimeException("Illegal capture");
}
public Value[] stack = new Value[32];
@ -215,12 +226,13 @@ public final class Frame {
if (newCtx != tryCtx) {
switch (newCtx.state) {
case CATCH:
if (tryCtx.state != TryState.CATCH) locals.add(new Value[] { error.value });
// TODO: may cause problems
// if (tryCtx.state != TryState.CATCH) locals.add(new Value[] { error.value });
codePtr = tryCtx.catchStart;
stackPtr = tryCtx.restoreStackPtr;
break;
case FINALLY:
if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
// if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
codePtr = tryCtx.finallyStart;
stackPtr = tryCtx.restoreStackPtr;
default:
@ -236,7 +248,7 @@ public final class Frame {
}
else {
popTryFlag = false;
if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
// if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
codePtr = tryCtx.finallyStart;
@ -422,8 +434,13 @@ public final class Frame {
var i = 0;
for (i = 0; i < func.body.localsN; i++) {
this.locals.add(new Value[] { Value.UNDEFINED });
this.locals = new Value[func.body.localsN];
Arrays.fill(locals, Value.UNDEFINED);
this.capturables = new Value[func.body.capturablesN][1];
for (i = 0; i < func.body.capturablesN; i++) {
this.capturables[i][0] = Value.UNDEFINED;
}
}
}

View File

@ -140,7 +140,7 @@ public class InstructionRunner {
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
int i = instr.get(0);
frame.push(frame.getVar(i)[0]);
frame.push(frame.getVar(i));
frame.codePtr++;
return null;
@ -179,7 +179,7 @@ public class InstructionRunner {
var captures = new Value[instr.params.length - 5][];
for (var i = 5; i < instr.params.length; i++) {
captures[i - 5] = frame.getVar(instr.get(i));
captures[i - 5] = frame.captureVar(instr.get(i));
}
var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
@ -278,7 +278,7 @@ public class InstructionRunner {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
int i = instr.get(0);
frame.getVar(i)[0] = val;
frame.setVar(i, val);
frame.codePtr++;
return null;
@ -498,29 +498,34 @@ public class InstructionRunner {
frame.codePtr++;
return null;
}
private static Value execLoadError(Environment env, Instruction instr, Frame frame) {
frame.push(frame.tryStack.peek().error.value);
frame.codePtr++;
return null;
}
private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) {
int n = instr.get(0);
int offset = instr.get(0);
int n = instr.get(1);
for (var i = 0; i < n; i++) frame.locals.add(new Value[] { Value.UNDEFINED });
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { Value.UNDEFINED };
frame.codePtr++;
return null;
}
private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) {
int n = instr.get(0);
int offset = instr.get(0);
int n = instr.get(1);
for (var i = frame.locals.size() - n; i < frame.locals.size(); i++) frame.locals.set(i, new Value[] { frame.locals.get(i)[0] });
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { frame.capturables[i][0] };
frame.codePtr++;
return null;
}
private static Value execStackFree(Environment env, Instruction instr, Frame frame) {
int n = instr.get(0);
// int n = instr.get(0);
for (var i = 0; i < n; i++) {
frame.locals.remove(frame.locals.size() - 1);
}
// TODO: Remove if safe to do so
frame.codePtr++;
return null;
@ -559,6 +564,7 @@ public class InstructionRunner {
case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame);
case LOAD_CALLEE: return execLoadCallee(env, instr, frame);
case LOAD_THIS: return execLoadThis(env, instr, frame);
case LOAD_ERROR: return execLoadError(env, instr, frame);
case DISCARD: return execDiscard(env, instr, frame);
case STORE_MEMBER: return execStoreMember(env, instr, frame);