Optimize var flattening #27
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
public final int capturedOffset() {
|
||||
if (parent != null) return parent.capturedOffset() + captured.size();
|
||||
else return localsCount() + captured.size();
|
||||
|
||||
return res;
|
||||
|
||||
// if (parent != null) return parent.variableOffset() + variables.size();
|
||||
// else return variables.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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user