Refactor bytecode analysis
This commit is contained in:
228
src/jse/org/luaj/vm2/luajc/BasicBlock.java
Normal file
228
src/jse/org/luaj/vm2/luajc/BasicBlock.java
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.luaj.vm2.luajc;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.luaj.vm2.Lua;
|
||||
import org.luaj.vm2.Prototype;
|
||||
|
||||
public class BasicBlock {
|
||||
int pc0,pc1; // range of program counter values for the block
|
||||
BasicBlock[] prev; // previous basic blocks (0-n of these)
|
||||
BasicBlock[] next; // next basic blocks (0, 1, or 2 of these)
|
||||
|
||||
short[] ninputs; // number of input values per slot, >0 means is phi variable
|
||||
VarInfo[][] inputs; // variables for each input, -1=closed/undefined, 1,2...=revisionof that variable
|
||||
|
||||
public BasicBlock(Prototype p, int pc0) {
|
||||
this.pc0 = this.pc1 = pc0;
|
||||
this.ninputs = new short[p.maxstacksize];
|
||||
this.inputs = new VarInfo[p.maxstacksize][];
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append( (pc0+1)+"-"+(pc1+1)
|
||||
+(prev!=null? " prv: "+str(prev,1): "")
|
||||
+(next!=null? " nxt: "+str(next,0): "")
|
||||
+"\n " );
|
||||
for ( int i=0; i<ninputs.length; i++ ) {
|
||||
if ( ninputs[i] < 0 )
|
||||
sb.append( " x " );
|
||||
else if ( ninputs[i] == 0 )
|
||||
sb.append( " - " );
|
||||
else {
|
||||
sb.append( " "+inputs[i][0] );
|
||||
for ( int j=1; j<ninputs[i]; j++ )
|
||||
sb.append(","+inputs[i][j]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String str(BasicBlock[] b, int p) {
|
||||
if ( b == null )
|
||||
return "";
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("(");
|
||||
for ( int i=0, n=b.length; i<n; i++ ) {
|
||||
if ( i > 0 )
|
||||
sb.append( "," );
|
||||
sb.append( String.valueOf( p==1? b[i].pc1+1: b[i].pc0+1 ) );
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static BasicBlock[] findBasicBlocks(Prototype p) {
|
||||
|
||||
// mark beginnings, endings
|
||||
final int n = p.code.length;
|
||||
final boolean[] isbeg = new boolean[n];
|
||||
final boolean[] isend = new boolean[n];
|
||||
isbeg[0] = true;
|
||||
BranchVisitor bv = new BranchVisitor(isbeg) {
|
||||
public void visitBranch(int pc0, int pc1) {
|
||||
isend[pc0] = true;
|
||||
isbeg[pc1] = true;
|
||||
}
|
||||
public void visitReturn(int pc) {
|
||||
isend[pc] = true;
|
||||
}
|
||||
};
|
||||
visitBranches(p, bv); // 1st time to mark branches
|
||||
visitBranches(p, bv); // 2nd time to catch merges
|
||||
|
||||
// create basic blocks
|
||||
final BasicBlock[] blocks = new BasicBlock[n];
|
||||
for ( int i=0; i<n; i++ ) {
|
||||
isbeg[i] = true;
|
||||
BasicBlock b = new BasicBlock(p,i);
|
||||
blocks[i] = b;
|
||||
while ( !isend[i] && i+1<n && !isbeg[i+1] )
|
||||
blocks[b.pc1=++i] = b;
|
||||
}
|
||||
|
||||
// count previous, next
|
||||
final int[] nnext = new int[n];
|
||||
final int[] nprev = new int[n];
|
||||
visitBranches(p, new BranchVisitor(isbeg) {
|
||||
public void visitBranch(int pc0, int pc1) {
|
||||
nnext[pc0]++;
|
||||
nprev[pc1]++;
|
||||
}
|
||||
});
|
||||
|
||||
// allocate and cross-reference
|
||||
visitBranches( p, new BranchVisitor(isbeg) {
|
||||
public void visitBranch(int pc0, int pc1) {
|
||||
if ( blocks[pc0].next == null ) blocks[pc0].next = new BasicBlock[nnext[pc0]];
|
||||
if ( blocks[pc1].prev == null ) blocks[pc1].prev = new BasicBlock[nprev[pc1]];
|
||||
blocks[pc0].next[--nnext[pc0]] = blocks[pc1];
|
||||
blocks[pc1].prev[--nprev[pc1]] = blocks[pc0];
|
||||
}
|
||||
});
|
||||
return blocks;
|
||||
}
|
||||
|
||||
abstract public static class BranchVisitor {
|
||||
final boolean[] isbeg;
|
||||
public BranchVisitor(boolean[] isbeg) {
|
||||
this.isbeg = isbeg;
|
||||
}
|
||||
public void visitBranch( int frompc, int topc ) {}
|
||||
public void visitReturn( int atpc ) {}
|
||||
}
|
||||
|
||||
public static void visitBranches( Prototype p, BranchVisitor visitor ) {
|
||||
int sbx,j;
|
||||
int[] code = p.code;
|
||||
int n = code.length;
|
||||
boolean[] branchdests = new boolean[n];
|
||||
for ( int i=0; i<n; i++ ) {
|
||||
int ins = code[i];
|
||||
switch ( Lua.GET_OPCODE( ins ) ) {
|
||||
case Lua.OP_LOADBOOL:
|
||||
if ( Lua.GETARG_C(ins) == 0 )
|
||||
break;
|
||||
case Lua.OP_EQ:
|
||||
case Lua.OP_LT:
|
||||
case Lua.OP_LE:
|
||||
case Lua.OP_TEST:
|
||||
case Lua.OP_TESTSET:
|
||||
case Lua.OP_TFORLOOP:
|
||||
if ( Lua.GET_OPCODE(code[i+1]) != Lua.OP_JMP )
|
||||
throw new IllegalArgumentException("test not followed by jump at "+i);
|
||||
sbx = Lua.GETARG_sBx(code[i+1]);
|
||||
++i;
|
||||
j = i + sbx + 1;
|
||||
visitor.visitBranch( i, j );
|
||||
visitor.visitBranch( i, i+1 );
|
||||
continue;
|
||||
case Lua.OP_JMP:
|
||||
case Lua.OP_FORPREP:
|
||||
case Lua.OP_FORLOOP:
|
||||
sbx = Lua.GETARG_sBx(ins);
|
||||
j = i + sbx + 1;
|
||||
visitor.visitBranch( i, j );
|
||||
continue;
|
||||
case Lua.OP_TAILCALL:
|
||||
case Lua.OP_RETURN:
|
||||
visitor.visitReturn( i );
|
||||
continue;
|
||||
}
|
||||
if ( i+1<n && visitor.isbeg[i+1] )
|
||||
visitor.visitBranch( i, i+1 );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mergeSlotInput(int slot, VarInfo v) {
|
||||
int n = ninputs[slot];
|
||||
if ( n == -1 )
|
||||
return false;
|
||||
if ( v == VarInfo.INVALID) {
|
||||
ninputs[slot] = -1;
|
||||
inputs[slot] = null;
|
||||
return true;
|
||||
}
|
||||
if ( n == 0 ) {
|
||||
ninputs[slot] = 1;
|
||||
inputs[slot] = new VarInfo[] { v };
|
||||
return true;
|
||||
}
|
||||
for ( int i=0; i<n; i++ )
|
||||
if ( inputs[slot][i] == v )
|
||||
return false;
|
||||
if ( n+1 > inputs[slot].length ) {
|
||||
VarInfo[] s = inputs[slot];
|
||||
inputs[slot] = new VarInfo[(n+1)*2];
|
||||
System.arraycopy(s, 0, inputs[slot], 0, n);
|
||||
}
|
||||
inputs[slot][n] = v;
|
||||
ninputs[slot]++;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean includesVar(int slot, VarInfo v) {
|
||||
int n = ninputs[slot];
|
||||
for ( int i=0; i<n; i++ )
|
||||
if ( inputs[slot][i] == v )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static BasicBlock[] sortDepthFirst(BasicBlock[] blocks) {
|
||||
Vector list = new Vector();
|
||||
Hashtable seen = new Hashtable();
|
||||
Vector next = new Vector();
|
||||
BasicBlock b0,b1,b[];
|
||||
|
||||
next.addElement( blocks[0] );
|
||||
seen.put( blocks[0], Boolean.TRUE );
|
||||
while ( (b = toBasicBlockArray(next)) != null ) {
|
||||
next.clear();
|
||||
for ( int i=0; i<b.length; i++ ) {
|
||||
list.addElement( b0 = b[i] );
|
||||
for ( int k=0, n=b0.next!=null? b0.next.length: 0; k<n; k++ ) {
|
||||
if ( seen.get( b1 = b0.next[k] ) == null ) {
|
||||
seen.put( b1, Boolean.TRUE );
|
||||
next.addElement( b1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return toBasicBlockArray(list);
|
||||
}
|
||||
|
||||
private static BasicBlock[] toBasicBlockArray(Vector list) {
|
||||
if ( list.size() <= 0 )
|
||||
return null;
|
||||
BasicBlock[] array = new BasicBlock[list.size()];
|
||||
list.copyInto(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -139,9 +139,9 @@ public class JavaBuilder {
|
||||
private static final String NAME_VARRESULT = "v";
|
||||
|
||||
// basic info
|
||||
private final ProtoInfo pi;
|
||||
private final Prototype p;
|
||||
private final String classname;
|
||||
private final Slots slots;
|
||||
|
||||
// bcel variables
|
||||
private final ClassGen cg;
|
||||
@@ -166,10 +166,10 @@ public class JavaBuilder {
|
||||
// hold vararg result
|
||||
private LocalVariableGen varresult = null;
|
||||
|
||||
public JavaBuilder(Prototype p, String classname, String filename) {
|
||||
this.p = p;
|
||||
public JavaBuilder(ProtoInfo pi, String classname, String filename) {
|
||||
this.pi = pi;
|
||||
this.p = pi.prototype;
|
||||
this.classname = classname;
|
||||
this.slots = new Slots(p);
|
||||
|
||||
// what class to inherit from
|
||||
superclassType = p.numparams;
|
||||
@@ -224,7 +224,7 @@ public class JavaBuilder {
|
||||
int slot = 0;
|
||||
if ( superclassType == SUPERTYPE_VARARGS ) {
|
||||
for ( slot=0; slot<p.numparams; slot++ ) {
|
||||
if ( slots.isInitialValueUsed(slot) ) {
|
||||
if ( pi.isInitialValueUsed(slot) ) {
|
||||
append(new ALOAD(1));
|
||||
append(new PUSH(cp, slot+1));
|
||||
append(factory.createInvoke(STR_VARARGS, "arg", TYPE_LUAVALUE, ARG_TYPES_INT, Constants.INVOKEVIRTUAL));
|
||||
@@ -248,7 +248,7 @@ public class JavaBuilder {
|
||||
// fixed arg function between 0 and 3 arguments
|
||||
for ( slot=0; slot<p.numparams; slot++ ) {
|
||||
this.plainSlotVars.put( Integer.valueOf(slot), Integer.valueOf(1+slot) );
|
||||
if ( slots.isUpvalueCreate(-1, slot) ) {
|
||||
if ( pi.isUpvalueCreate(-1, slot) ) {
|
||||
append(new ALOAD(1+slot));
|
||||
storeLocal(-1, slot);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ public class JavaBuilder {
|
||||
|
||||
// nil parameters
|
||||
for ( ; slot<p.maxstacksize; slot++ ) {
|
||||
if ( slots.isInitialValueUsed(slot) ) {
|
||||
if ( pi.isInitialValueUsed(slot) ) {
|
||||
loadNil();
|
||||
storeLocal(-1, slot);
|
||||
}
|
||||
@@ -336,7 +336,7 @@ public class JavaBuilder {
|
||||
}
|
||||
|
||||
public void loadLocal(int pc, int slot) {
|
||||
boolean isupval = slots.isUpvalueRefer(pc, slot);
|
||||
boolean isupval = pi.isUpvalueRefer(pc, slot);
|
||||
int index = findSlotIndex( slot, isupval );
|
||||
append(new ALOAD(index));
|
||||
if (isupval) {
|
||||
@@ -346,10 +346,10 @@ public class JavaBuilder {
|
||||
}
|
||||
|
||||
public void storeLocal(int pc, int slot) {
|
||||
boolean isupval = slots.isUpvalueAssign(pc, slot);
|
||||
boolean isupval = pi.isUpvalueAssign(pc, slot);
|
||||
int index = findSlotIndex( slot, isupval );
|
||||
if (isupval) {
|
||||
boolean isupcreate = slots.isUpvalueCreate(pc, slot);
|
||||
boolean isupcreate = pi.isUpvalueCreate(pc, slot);
|
||||
if ( isupcreate ) {
|
||||
append(factory.createInvoke(classname, "newupe", TYPE_LOCALUPVALUE, ARG_TYPES_NONE, Constants.INVOKESTATIC));
|
||||
append(InstructionConstants.DUP);
|
||||
@@ -369,7 +369,7 @@ public class JavaBuilder {
|
||||
public void createUpvalues(int pc, int firstslot, int numslots) {
|
||||
for ( int i=0; i<numslots; i++ ) {
|
||||
int slot = firstslot + i;
|
||||
boolean isupcreate = slots.isUpvalueCreate(pc, slot);
|
||||
boolean isupcreate = pi.isUpvalueCreate(pc, slot);
|
||||
if ( isupcreate ) {
|
||||
int index = findSlotIndex( slot, true );
|
||||
append(factory.createInvoke(classname, "newupn", TYPE_LOCALUPVALUE, ARG_TYPES_NONE, Constants.INVOKESTATIC));
|
||||
@@ -379,7 +379,7 @@ public class JavaBuilder {
|
||||
}
|
||||
|
||||
public void convertToUpvalue(int pc, int slot) {
|
||||
boolean isupassign = slots.isUpvalueAssign(pc, slot);
|
||||
boolean isupassign = pi.isUpvalueAssign(pc, slot);
|
||||
if ( isupassign ) {
|
||||
int index = findSlotIndex( slot, false );
|
||||
append(new ALOAD(index));
|
||||
|
||||
@@ -36,18 +36,26 @@ public class JavaGen {
|
||||
public JavaGen[] inners;
|
||||
|
||||
public JavaGen( Prototype p, String classname, String filename ) {
|
||||
this( new ProtoInfo(p,classname), classname, filename );
|
||||
}
|
||||
|
||||
private JavaGen( ProtoInfo pi, String classname, String filename ) {
|
||||
this.classname = classname;
|
||||
|
||||
// build this class
|
||||
JavaBuilder builder = new JavaBuilder(p, classname, filename);
|
||||
scanInstructions(p, classname, builder);
|
||||
JavaBuilder builder = new JavaBuilder(pi, classname, filename);
|
||||
scanInstructions(pi.prototype, classname, builder);
|
||||
this.bytecode = builder.completeClass();
|
||||
|
||||
// build sub-prototypes
|
||||
int n = p.p.length;
|
||||
inners = new JavaGen[n];
|
||||
for ( int i=0; i<n; i++ )
|
||||
inners[i] = new JavaGen(p.p[i], closureName(classname,i), filename);
|
||||
if ( pi.subprotos != null ) {
|
||||
int n = pi.subprotos.length;
|
||||
inners = new JavaGen[n];
|
||||
for ( int i=0; i<n; i++ )
|
||||
inners[i] = new JavaGen(pi.subprotos[i], closureName(classname,i), filename);
|
||||
} else {
|
||||
inners = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String closureName(String classname, int subprotoindex) {
|
||||
|
||||
@@ -62,7 +62,7 @@ public class JavaLoader extends ClassLoader {
|
||||
|
||||
public void include( JavaGen jg ) {
|
||||
unloaded.put( jg.classname, jg.bytecode );
|
||||
for ( int i=0; i<jg.inners.length; i++ )
|
||||
for ( int i=0, n=jg.inners!=null? jg.inners.length: 0; i<n; i++ )
|
||||
include( jg.inners[i] );
|
||||
}
|
||||
|
||||
|
||||
317
src/jse/org/luaj/vm2/luajc/ProtoInfo.java
Normal file
317
src/jse/org/luaj/vm2/luajc/ProtoInfo.java
Normal file
@@ -0,0 +1,317 @@
|
||||
package org.luaj.vm2.luajc;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.luaj.vm2.Lua;
|
||||
import org.luaj.vm2.Print;
|
||||
import org.luaj.vm2.Prototype;
|
||||
|
||||
/**
|
||||
* Prototype information for static single-assignment analysis
|
||||
*/
|
||||
public class ProtoInfo {
|
||||
|
||||
public final String name;
|
||||
public final Prototype prototype; // the prototype that this info is about
|
||||
public final ProtoInfo[] subprotos; // one per enclosed prototype, or null
|
||||
public final BasicBlock[] blocks; // basic block analysis of code branching
|
||||
public final BasicBlock[] blocklist; // blocks in breadhth-first order
|
||||
public final VarInfo[][] vars; // Each variable
|
||||
public final UpvalInfo[] upvals; // from outer scope
|
||||
public final UpvalInfo[][] openups; // per slot, upvalues allocated by this prototype
|
||||
|
||||
|
||||
public ProtoInfo(Prototype p, String name) {
|
||||
this(p,name,null);
|
||||
}
|
||||
|
||||
private ProtoInfo(Prototype p, String name, UpvalInfo[] u) {
|
||||
this.name = name;
|
||||
this.prototype = p;
|
||||
this.upvals = u;
|
||||
this.subprotos = p.p!=null&&p.p.length>0? new ProtoInfo[p.p.length]: null;
|
||||
|
||||
// find basic blocks
|
||||
this.blocks = BasicBlock.findBasicBlocks(p);
|
||||
this.blocklist = BasicBlock.sortDepthFirst(blocks);
|
||||
|
||||
// find variables and block inputs
|
||||
this.vars = findVariables();
|
||||
findBasicBlockInputs();
|
||||
|
||||
// find upvalues, create sub-prototypes
|
||||
this.openups = new UpvalInfo[p.maxstacksize][];
|
||||
findUpvalues();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
// prototpye name
|
||||
sb.append( "proto '"+name+"'\n" );
|
||||
|
||||
// upvalues from outer scopes
|
||||
for ( int i=0, n=(upvals!=null? upvals.length: 0); i<n; i++ )
|
||||
sb.append( " up["+i+"]: "+upvals[i]+"\n" );
|
||||
|
||||
// basic blocks
|
||||
for ( int i=0; i<blocklist.length; i++ ) {
|
||||
sb.append( " block "+blocklist[i].toString() );
|
||||
sb.append( "\n" );
|
||||
|
||||
// instructions
|
||||
for ( int pc=blocklist[i].pc0; pc<=blocklist[i].pc1; pc++ ) {
|
||||
|
||||
// open upvalue storage
|
||||
for ( int j=0; j<prototype.maxstacksize; j++ ) {
|
||||
if ( vars[j][pc].pc == pc && vars[j][pc].allocupvalue ) {
|
||||
sb.append( " open: "+vars[j][pc].upvalue+"\n" );
|
||||
}
|
||||
}
|
||||
|
||||
// opcode
|
||||
sb.append( " " );
|
||||
for ( int j=0; j<prototype.maxstacksize; j++ ) {
|
||||
VarInfo v = vars[j][pc];
|
||||
String u = (v.upvalue!=null? !v.upvalue.rw? "[C] ": (v.allocupvalue&&v.pc==pc? "[*] ": "[] "): " ");
|
||||
String s = String.valueOf(v);
|
||||
sb.append( s+u);
|
||||
}
|
||||
sb.append( " " );
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintStream ops = Print.ps;
|
||||
Print.ps = new PrintStream(baos);
|
||||
try {
|
||||
Print.printOpCode(prototype, pc);
|
||||
} finally {
|
||||
Print.ps.close();
|
||||
Print.ps = ops;
|
||||
}
|
||||
sb.append( baos.toString() );
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
// nested functions
|
||||
for ( int i=0, n=subprotos!=null? subprotos.length: 0; i<n; i++ ) {
|
||||
sb.append( subprotos[i].toString() );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private VarInfo[][] findVariables() {
|
||||
|
||||
// create storage for variables.
|
||||
int n = prototype.code.length;
|
||||
int m = prototype.maxstacksize;
|
||||
VarInfo[][] v = new VarInfo[m][];
|
||||
for ( int i=0; i<v.length; i++ )
|
||||
v[i] = new VarInfo[n];
|
||||
|
||||
// process instructions
|
||||
for ( int pc=0; pc<n; pc++ ) {
|
||||
|
||||
// propogate previous value except at block boundaries
|
||||
if ( pc>0 && blocks[pc].pc0 != pc )
|
||||
for ( int j=0; j<m; j++ )
|
||||
v[j][pc] = v[j][pc-1];
|
||||
|
||||
// account for assignments and invalidations
|
||||
int a,b,c;
|
||||
int ins = prototype.code[pc];
|
||||
switch ( Lua.GET_OPCODE( ins ) ) {
|
||||
case Lua.OP_MOVE:/* A B R(A) := R(B) */
|
||||
case Lua.OP_LOADK:/* A Bx R(A) := Kst(Bx) */
|
||||
case Lua.OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
case Lua.OP_GETUPVAL: /* A B R(A) := UpValue[B] */
|
||||
case Lua.OP_GETGLOBAL: /* A Bx R(A) := Gbl[Kst(Bx)] */
|
||||
case Lua.OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */
|
||||
case Lua.OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */
|
||||
case Lua.OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
|
||||
case Lua.OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
|
||||
case Lua.OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
|
||||
case Lua.OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
|
||||
case Lua.OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
|
||||
case Lua.OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
|
||||
case Lua.OP_UNM: /* A B R(A) := -R(B) */
|
||||
case Lua.OP_NOT: /* A B R(A) := not R(B) */
|
||||
case Lua.OP_LEN: /* A B R(A) := length of R(B) */
|
||||
case Lua.OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
|
||||
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
a = Lua.GETARG_A( ins );
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
break;
|
||||
case Lua.OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
a = Lua.GETARG_A( ins );
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
v[a+1][pc] = new VarInfo(a+1,pc);
|
||||
break;
|
||||
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
a = Lua.GETARG_A( ins );
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
v[a+3][pc] = new VarInfo(a+1,pc);
|
||||
break;
|
||||
case Lua.OP_LOADNIL: /* A B R(A) := ... := R(B) := nil */
|
||||
a = Lua.GETARG_A( ins );
|
||||
b = Lua.GETARG_B( ins );
|
||||
for ( ; a<=b; a++ )
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
break;
|
||||
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
|
||||
a = Lua.GETARG_A( ins );
|
||||
b = Lua.GETARG_B( ins );
|
||||
for ( int j=0; j<b; j++, a++ )
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
for ( ; a<m; a++ )
|
||||
v[a][pc] = VarInfo.INVALID;
|
||||
break;
|
||||
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
a = Lua.GETARG_A( ins );
|
||||
c = Lua.GETARG_C( ins );
|
||||
for ( int j=0; j<c; j++, a++ )
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
for ( ; a<m; a++ )
|
||||
v[a][pc] = VarInfo.INVALID;
|
||||
break;
|
||||
case Lua.OP_TFORLOOP: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
|
||||
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
|
||||
a = Lua.GETARG_A( ins );
|
||||
c = Lua.GETARG_C( ins );
|
||||
a += 3;
|
||||
for ( int j=0; j<c; j++, a++ )
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
for ( ; a<m; a++ )
|
||||
v[a][pc] = VarInfo.INVALID;
|
||||
break;
|
||||
case Lua.OP_CLOSURE: /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
|
||||
a = Lua.GETARG_A( ins );
|
||||
b = Lua.GETARG_Bx( ins );
|
||||
v[a][pc] = new VarInfo(a,pc);
|
||||
for ( int k=prototype.p[b].nups; --k>=0; ) {
|
||||
++pc;
|
||||
for ( int j=0; j<m; j++ )
|
||||
v[j][pc] = v[j][pc-1];
|
||||
}
|
||||
break;
|
||||
case Lua.OP_CLOSE:
|
||||
a = Lua.GETARG_A( ins );
|
||||
for ( ; a<m; a++ )
|
||||
v[a][pc] = VarInfo.INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private void findBasicBlockInputs() {
|
||||
|
||||
for ( boolean changed=true; changed; ) {
|
||||
changed = false;
|
||||
|
||||
// send inputs to next stage
|
||||
for ( int i=0; i<blocklist.length; i++ ) {
|
||||
BasicBlock b0 = blocklist[i];
|
||||
for ( int k=0, n=b0.next!=null? b0.next.length: 0; k<n; k++ ) {
|
||||
BasicBlock b1 = b0.next[k];
|
||||
for ( int slot=0; slot<prototype.maxstacksize; slot++ ) {
|
||||
VarInfo v = vars[slot][b0.pc1];
|
||||
if ( v != null ) {
|
||||
if ( b1.mergeSlotInput(slot, v) ) {
|
||||
if ( vars[slot][b1.pc0] == null )
|
||||
if ( b1.ninputs[slot] == 2 || v == VarInfo.INVALID )
|
||||
createInputVar( b1, slot );
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// propogate up to one more variable per slot
|
||||
if ( ! changed ) {
|
||||
eachslot: for ( int slot=0; slot<prototype.maxstacksize; slot++ ) {
|
||||
for ( int i=0; i<blocklist.length; i++ ) {
|
||||
BasicBlock b0 = blocklist[i];
|
||||
if ( vars[slot][b0.pc0] == null ) {
|
||||
createInputVar( b0, slot );
|
||||
changed = true;
|
||||
continue eachslot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createInputVar(BasicBlock b, int slot) {
|
||||
int n = b.ninputs[slot];
|
||||
VarInfo v =
|
||||
n==-1? VarInfo.INVALID:
|
||||
n==0? VarInfo.NIL(slot):
|
||||
n==1? b.inputs[slot][0]:
|
||||
VarInfo.PHI(slot,b.pc0);
|
||||
for ( int pc=b.pc0; pc<=b.pc1 && vars[slot][pc] == null; ++pc )
|
||||
vars[slot][pc] = v;
|
||||
}
|
||||
|
||||
private void findUpvalues() {
|
||||
int[] code = prototype.code;
|
||||
int n = code.length;
|
||||
|
||||
// propogate to inner prototypes
|
||||
for ( int pc=0; pc<n; pc++ ) {
|
||||
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) {
|
||||
int bx = Lua.GETARG_Bx(code[pc]);
|
||||
Prototype newp = prototype.p[bx];
|
||||
UpvalInfo[] newu = newp.nups>0? new UpvalInfo[newp.nups]: null;
|
||||
String newname = name + "$" + bx;
|
||||
for ( int j=0; j<newp.nups; ++j ) {
|
||||
int i = code[++pc];
|
||||
int b = Lua.GETARG_B(i);
|
||||
newu[j] = (i&4) != 0? upvals[b]: findOpenUp(pc,b);
|
||||
}
|
||||
subprotos[bx] = new ProtoInfo(newp, newname, newu);
|
||||
}
|
||||
}
|
||||
|
||||
// mark all upvalues that are written locally as read/write
|
||||
for ( int pc=0; pc<n; pc++ ) {
|
||||
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_SETUPVAL )
|
||||
upvals[Lua.GETARG_B(code[pc])].rw = true;
|
||||
}
|
||||
}
|
||||
|
||||
private UpvalInfo findOpenUp(int pc, int slot) {
|
||||
if ( openups[slot] == null )
|
||||
openups[slot] = new UpvalInfo[prototype.code.length];
|
||||
if ( openups[slot][pc] != null )
|
||||
return openups[slot][pc];
|
||||
return new UpvalInfo(this, pc, slot);
|
||||
}
|
||||
|
||||
public boolean isUpvalueAssign(int pc, int slot) {
|
||||
if ( pc < 0 ) pc = 0;
|
||||
VarInfo v = vars[slot][pc];
|
||||
return v.upvalue != null && v.upvalue.rw;
|
||||
}
|
||||
|
||||
public boolean isUpvalueCreate(int pc, int slot) {
|
||||
VarInfo v = vars[slot][pc];
|
||||
return v.upvalue != null && v.upvalue.rw && v.allocupvalue && pc == v.pc;
|
||||
}
|
||||
|
||||
public boolean isUpvalueRefer(int pc, int slot) {
|
||||
// TODO: when it is a CALL
|
||||
VarInfo v = vars[slot][pc];
|
||||
return v.upvalue != null && v.upvalue.rw;
|
||||
}
|
||||
|
||||
public boolean isInitialValueUsed(int slot) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,536 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2010 Luaj.org. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
package org.luaj.vm2.luajc;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.luaj.vm2.Lua;
|
||||
import org.luaj.vm2.Print;
|
||||
import org.luaj.vm2.Prototype;
|
||||
|
||||
/**
|
||||
* Analyze slot usage to find:
|
||||
* - which assignments and references are to upvalue 'u'
|
||||
* - which slots must be initialized with the implied "nil"
|
||||
* - which assignment locations need to create upvalue storage 'U'
|
||||
*
|
||||
* Eventually add:
|
||||
* - subexpression sequences that can remain in primitive types
|
||||
* - assignments of constant values to upvalues that are never modified
|
||||
*/
|
||||
public class Slots {
|
||||
|
||||
private static final byte BIT_ASSIGN = 0x01; // assignment is made to this slot at this pc
|
||||
private static final byte BIT_REFER = 0x02; // reference is made to this slot at this pc
|
||||
private static final byte BIT_UP_ASSIGN = 0x04; // upvalue assignment
|
||||
private static final byte BIT_UP_REFER = 0x08; // upvalue reference
|
||||
private static final byte BIT_UP_CREATE = 0x10; // upvalue storage must be created here
|
||||
private static final byte BIT_INVALID = 0x20; // slot becomes invlaid at this pc
|
||||
private static final byte BIT_NIL = 0x40; // slot initialized to nil at this point
|
||||
|
||||
final Prototype p;
|
||||
final int n,m;
|
||||
public final byte[][] slots;
|
||||
public final boolean[] branchdest;
|
||||
|
||||
public boolean isUpvalueCreate(int pc, int slot) {
|
||||
return (slots[pc+1][slot] & (BIT_UP_CREATE)) != 0;
|
||||
}
|
||||
|
||||
public boolean isUpvalueAssign(int pc, int slot) {
|
||||
return (slots[pc+1][slot] & (BIT_UP_ASSIGN | BIT_UP_CREATE)) != 0;
|
||||
}
|
||||
|
||||
public boolean isUpvalueRefer(int pc, int slot) {
|
||||
return (slots[pc+1][slot] & (BIT_UP_REFER)) != 0;
|
||||
}
|
||||
|
||||
public boolean isInitialValueUsed(int slot) {
|
||||
return (slots[0][slot] & (BIT_INVALID)) == 0;
|
||||
}
|
||||
|
||||
public Slots(Prototype prototype) {
|
||||
p = prototype;
|
||||
n = p.code.length;
|
||||
m = p.maxstacksize;
|
||||
slots = new byte[n+1][m];
|
||||
branchdest = new boolean[n+1];
|
||||
markassignments();
|
||||
while ( propogatebranches() )
|
||||
;
|
||||
markuninitialized();
|
||||
markupvalues();
|
||||
markforloopupvalues();
|
||||
}
|
||||
|
||||
private void markassignments() {
|
||||
// mark initial assignments and references
|
||||
int j=0;
|
||||
for ( ; j<p.numparams; j++ )
|
||||
slots[0][j] = BIT_ASSIGN;
|
||||
for ( ; j<m; j++ )
|
||||
slots[0][j] = BIT_NIL;
|
||||
|
||||
for ( int pc=0; pc<n; pc++ ) {
|
||||
int index = pc+1;
|
||||
byte[] s = slots[index];
|
||||
|
||||
int ins = p.code[pc];
|
||||
int a = Lua.GETARG_A(ins);
|
||||
int b = Lua.GETARG_B(ins);
|
||||
int bx = Lua.GETARG_Bx(ins);
|
||||
int sbx = Lua.GETARG_sBx(ins);
|
||||
int c = Lua.GETARG_C(ins);
|
||||
|
||||
switch ( Lua.GET_OPCODE(ins) ) {
|
||||
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
|
||||
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
|
||||
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
|
||||
case Lua.OP_GETGLOBAL: /* A Bx R(A):= Gbl[Kst(Bx)] */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
break;
|
||||
|
||||
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
|
||||
case Lua.OP_SETGLOBAL: /* A Bx Gbl[Kst(Bx)]:= R(A) */
|
||||
s[a] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_MOVE:/* A B R(A):= R(B) */
|
||||
case Lua.OP_UNM: /* A B R(A):= -R(B) */
|
||||
case Lua.OP_NOT: /* A B R(A):= not R(B) */
|
||||
case Lua.OP_LEN: /* A B R(A):= length of R(B) */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
s[b] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(B):= nil */
|
||||
while ( a<=b )
|
||||
s[a++] |= BIT_ASSIGN | BIT_NIL;
|
||||
break;
|
||||
|
||||
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
s[b] |= BIT_REFER;
|
||||
if (c<=0xff) s[c] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
|
||||
s[a] |= BIT_REFER;
|
||||
if (b<=0xff) s[b] |= BIT_REFER;
|
||||
if (c<=0xff) s[c] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
|
||||
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
|
||||
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
|
||||
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
|
||||
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
|
||||
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
if (b<=0xff) s[b] |= BIT_REFER;
|
||||
if (c<=0xff) s[c] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
s[a+1] |= BIT_ASSIGN;
|
||||
s[b] |= BIT_REFER;
|
||||
if (c<=0xff) s[c] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
while ( b<=c )
|
||||
s[b++] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
|
||||
s[a] |= BIT_ASSIGN;
|
||||
//if ( c!=0 ) branchdest[index+2] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_JMP: /* sBx pc+=sBx */
|
||||
//if ( sbx < 0 )
|
||||
branchdest[index+1+sbx] = true;
|
||||
ins = p.code[index+0+sbx];
|
||||
if ( Lua.GET_OPCODE(ins) == Lua.OP_TFORLOOP ) {
|
||||
a = Lua.GETARG_A(ins);
|
||||
c = Lua.GETARG_C(ins);
|
||||
for ( int i=1; i<=c; i++ )
|
||||
s[a+2+i] |= BIT_INVALID;
|
||||
}
|
||||
break;
|
||||
|
||||
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
if (bx<=0xff) s[bx] |= BIT_REFER;
|
||||
if (c<=0xff) s[c] |= BIT_REFER;
|
||||
//branchdest[index+2] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
|
||||
s[a] |= BIT_REFER;
|
||||
//branchdest[index+2] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
|
||||
s[a] |= BIT_REFER;
|
||||
s[b] |= BIT_REFER;
|
||||
//branchdest[index+2] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
for ( int i=0; i<b; i++ )
|
||||
s[a+i] |= BIT_REFER;
|
||||
for ( int i=0; i<c-1; i++, a++ )
|
||||
s[a] |= BIT_ASSIGN;
|
||||
for ( ; a<m; a++ )
|
||||
s[a] |= BIT_INVALID;
|
||||
break;
|
||||
|
||||
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
for ( int i=1; i<b; i++, a++ )
|
||||
s[a] |= BIT_REFER;
|
||||
for ( ; a<m; a++ )
|
||||
s[a] |= BIT_INVALID;
|
||||
break;
|
||||
|
||||
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
while ( --b>0 )
|
||||
s[a++] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
|
||||
s[a] |= BIT_REFER | BIT_ASSIGN;
|
||||
s[a+2] |= BIT_REFER;
|
||||
branchdest[index+1+sbx] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
|
||||
s[a] |= BIT_REFER | BIT_ASSIGN;
|
||||
s[a+1] |= BIT_REFER;
|
||||
s[a+2] |= BIT_REFER;
|
||||
s[a+3] |= BIT_ASSIGN;
|
||||
branchdest[index+1+sbx] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_TFORLOOP: /*
|
||||
* A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1),
|
||||
* R(A+2)): if R(A+3) ~= nil then R(A+2)=R(A+3)
|
||||
* else pc++
|
||||
*/
|
||||
s[a] |= BIT_REFER;
|
||||
s[a+1] |= BIT_REFER;
|
||||
s[a+2] |= BIT_REFER | BIT_ASSIGN;
|
||||
for ( int aa=a+3; aa<a+3+c; aa++ )
|
||||
s[aa] |= BIT_ASSIGN;
|
||||
for ( int aa=a+3+c; aa<m; aa++ )
|
||||
s[aa] |= BIT_INVALID;
|
||||
branchdest[index+2] = true;
|
||||
break;
|
||||
|
||||
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
|
||||
s[a] |= BIT_REFER;
|
||||
for ( int i=1; i<=b; i++ )
|
||||
s[a+i] |= BIT_REFER;
|
||||
break;
|
||||
|
||||
case Lua.OP_CLOSE: /* A close all variables in the stack up to (>=) R(A)*/
|
||||
while ( a<m )
|
||||
s[a++] |= BIT_INVALID;
|
||||
break;
|
||||
|
||||
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
|
||||
{
|
||||
Prototype newp = p.p[bx];
|
||||
for ( int up=0, nup=newp.nups; up<nup; ++up ) {
|
||||
ins = p.code[++pc];
|
||||
b = Lua.GETARG_B(ins);
|
||||
if ( (ins&4) != 0 ) {
|
||||
// up : ups[b]
|
||||
} else {
|
||||
s[b] |= BIT_REFER | BIT_UP_REFER;
|
||||
}
|
||||
}
|
||||
s[a] |= ((s[a] & BIT_UP_REFER) != 0 )?
|
||||
(BIT_ASSIGN | BIT_UP_ASSIGN):
|
||||
BIT_ASSIGN;
|
||||
break;
|
||||
}
|
||||
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
|
||||
if ( b == 0 ) {
|
||||
while ( a<m )
|
||||
s[a++] |= BIT_INVALID;
|
||||
} else {
|
||||
for ( int i=1; i<b; ++a, ++i )
|
||||
s[a] |= BIT_ASSIGN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean propogatebranches() {
|
||||
boolean hadchanges = false;
|
||||
for ( int pc=0; pc<n; pc++ ) {
|
||||
int index = pc+1;
|
||||
int ins = p.code[pc];
|
||||
switch ( Lua.GET_OPCODE(ins) ) {
|
||||
case Lua.OP_LOADBOOL:
|
||||
if ( Lua.GETARG_C(ins) == 0 )
|
||||
break;
|
||||
case Lua.OP_EQ:
|
||||
case Lua.OP_LT:
|
||||
case Lua.OP_LE:
|
||||
case Lua.OP_TEST:
|
||||
case Lua.OP_TESTSET:
|
||||
case Lua.OP_TFORLOOP:
|
||||
hadchanges |= propogatebranch( index, index+2 );
|
||||
break;
|
||||
case Lua.OP_JMP:
|
||||
case Lua.OP_FORPREP:
|
||||
case Lua.OP_FORLOOP:
|
||||
hadchanges |= propogatebranch( index, index+1+Lua.GETARG_sBx(ins) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hadchanges;
|
||||
}
|
||||
|
||||
private boolean propogatebranch(int src, int dest) {
|
||||
if ( dest >= slots.length )
|
||||
return false;
|
||||
boolean hadchanges = false;
|
||||
byte[] s = slots[src];
|
||||
byte[] d = slots[dest];
|
||||
for ( int j=0; j<m; j++ ) {
|
||||
byte bits = (byte) (s[j] & (BIT_ASSIGN | BIT_INVALID));
|
||||
hadchanges |= ((d[j] & bits) & (BIT_ASSIGN | BIT_INVALID)) != bits;
|
||||
d[j] |= bits;
|
||||
}
|
||||
return hadchanges;
|
||||
}
|
||||
|
||||
private void markuninitialized() {
|
||||
for ( int j=p.numparams; j<m; j++ )
|
||||
if ( ! needsinitialnil(j) )
|
||||
slots[0][j] |= BIT_INVALID;
|
||||
}
|
||||
|
||||
private boolean needsinitialnil(int j) {
|
||||
for ( int i=1; i<=n; i++ ) {
|
||||
if ( isbranchsource(i) )
|
||||
return true;
|
||||
if ( (slots[i][j] & (BIT_REFER | BIT_UP_REFER)) != 0 )
|
||||
return true;
|
||||
if ( (slots[i][j] & (BIT_ASSIGN | BIT_UP_ASSIGN | BIT_UP_CREATE | BIT_INVALID)) != 0 )
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isbranchsource(int index) {
|
||||
if (index >= p.code.length )
|
||||
return false;
|
||||
int ins = p.code[index];
|
||||
switch ( Lua.GET_OPCODE(ins) ) {
|
||||
case Lua.OP_LOADBOOL:
|
||||
if ( Lua.GETARG_C(ins) == 0 )
|
||||
return true;
|
||||
case Lua.OP_EQ:
|
||||
case Lua.OP_LT:
|
||||
case Lua.OP_LE:
|
||||
case Lua.OP_TEST:
|
||||
case Lua.OP_TESTSET:
|
||||
case Lua.OP_TFORLOOP:
|
||||
case Lua.OP_JMP:
|
||||
case Lua.OP_FORPREP:
|
||||
case Lua.OP_FORLOOP:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void markupvalues( ) {
|
||||
for ( int pc=0; pc<n; ++pc ) {
|
||||
if ( Lua.GET_OPCODE(p.code[pc]) == Lua.OP_CLOSURE ) {
|
||||
int index = pc+1;
|
||||
byte[] s = slots[index];
|
||||
for ( int j=0; j<m; j++ )
|
||||
if ( (s[j] & BIT_REFER) != 0 ) {
|
||||
promoteUpvalueBefore( index, j );
|
||||
if ( pc<n-1 )
|
||||
promoteUpvalueAfter( index+1, j );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void markforloopupvalues( ) {
|
||||
for ( int pc1=n; --pc1>=0; ) {
|
||||
int i = p.code[pc1];
|
||||
if ( Lua.GET_OPCODE(i) == Lua.OP_TFORLOOP ) {
|
||||
int a = Lua.GETARG_A(i);
|
||||
int c = Lua.GETARG_C(i);
|
||||
for ( int pc0=pc1; --pc0>=0; ) {
|
||||
i = p.code[pc0];
|
||||
int o = Lua.GET_OPCODE(i);
|
||||
int sbx = Lua.GETARG_sBx(i);
|
||||
if ( o == Lua.OP_JMP && (pc0 + 1 + sbx == pc1) ) {
|
||||
for ( int j=1; j<=c; j++ ) {
|
||||
checkPromoteLoopUpvalue( pc0+1, pc1+1, a+2+j );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPromoteLoopUpvalue(int index0, int index1, int slot) {
|
||||
for ( int index=index0; index<=index1; ++index ) {
|
||||
if ( (slots[index][slot] & BIT_UP_CREATE) != 0 ) {
|
||||
for ( int i=index0+1; i<index1; ++i ) {
|
||||
promoteUpvalue(slots[i], slot);
|
||||
slots[i][slot] &= (~BIT_UP_CREATE);
|
||||
}
|
||||
slots[index1][slot] |= BIT_UP_CREATE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void promoteUpvalueBefore(int index, int j) {
|
||||
int undef = prevUndefined(index,j);
|
||||
// int branch = firstBranchAfter(undef,index,j);
|
||||
// int assign = lastAssignBefore(branch,undef,j);
|
||||
int assign = firstAssignAfter(undef,index,j);
|
||||
slots[assign][j] |= BIT_UP_CREATE;
|
||||
while ( index>assign)
|
||||
promoteUpvalue( slots[index--], j );
|
||||
}
|
||||
|
||||
private void promoteUpvalueAfter(int index, int j) {
|
||||
int end = nextUndefined(index,j);
|
||||
int access = lastAccessBefore(end,index,j);
|
||||
while ( index<=access )
|
||||
promoteUpvalue( slots[index++], j );
|
||||
}
|
||||
|
||||
private void promoteUpvalue(byte[] s, int slot) {
|
||||
if ( (s[slot] & BIT_REFER) != 0 )
|
||||
s[slot] |= BIT_UP_REFER;
|
||||
if ( (s[slot] & BIT_ASSIGN) != 0 )
|
||||
s[slot] |= BIT_UP_ASSIGN;
|
||||
}
|
||||
|
||||
private int prevUndefined(int index, int j) {
|
||||
for ( ; index>=0; --index ) {
|
||||
int s = slots[index][j];
|
||||
if ( ((s & BIT_INVALID) != 0) )
|
||||
return index;
|
||||
else if ( ((s & BIT_NIL) != 0) )
|
||||
return index-1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private int firstBranchAfter(int index, int limit, int j) {
|
||||
for ( ; ++index<limit; )
|
||||
if ( index>0 && this.branchdest[index-1] )
|
||||
return index;
|
||||
return index;
|
||||
}
|
||||
|
||||
private int lastAssignBefore(int index, int limit, int j) {
|
||||
for ( int i=index; i>limit; --i )
|
||||
if ( (slots[i][j] & (BIT_ASSIGN | BIT_NIL)) != 0 )
|
||||
return i;
|
||||
return index;
|
||||
}
|
||||
|
||||
private int firstAssignAfter(int index, int limit, int j) {
|
||||
for ( int i=index; ++i<limit; )
|
||||
if ( (slots[i][j] & (BIT_ASSIGN | BIT_NIL)) != 0 )
|
||||
return i;
|
||||
return limit;
|
||||
}
|
||||
|
||||
private int nextUndefined(int index, int j) {
|
||||
while ( index<slots.length && ((slots[index][j] & BIT_INVALID) == 0) )
|
||||
++index;
|
||||
return index;
|
||||
}
|
||||
|
||||
private int lastAccessBefore(int index, int limit, int j) {
|
||||
for ( --index; index>limit; --index )
|
||||
if ( (slots[index][j] & (BIT_ASSIGN|BIT_REFER)) != 0 )
|
||||
return index;
|
||||
return index;
|
||||
}
|
||||
|
||||
// ------------- pretty-print slot info --------------
|
||||
|
||||
String[] toStrings() {
|
||||
int n = slots.length;
|
||||
int m = slots[0].length;
|
||||
String[] strs = new String[n];
|
||||
byte[] b = new byte[m+1];
|
||||
for ( int i=0; i<n; i++ ) {
|
||||
for ( int j=0; j<=m; j++ )
|
||||
b[j] = ' ';
|
||||
if ( branchdest[i] )
|
||||
b[0] = 'D';
|
||||
byte[] si = slots[i];
|
||||
for ( int j=0; j<m; j++ ) {
|
||||
byte s = si[j];
|
||||
b[1+j] = (byte) (
|
||||
((s & BIT_UP_CREATE) != 0)? 'C':
|
||||
((s & BIT_UP_ASSIGN) != 0)?
|
||||
(((s & BIT_UP_REFER) != 0)? 'B': 'A'):
|
||||
((s & BIT_UP_REFER) != 0)? 'R':
|
||||
((s & BIT_ASSIGN) != 0)?
|
||||
(((s & BIT_REFER) != 0)? 'b': 'a'):
|
||||
((s & BIT_REFER) != 0)? 'r':
|
||||
((s & BIT_INVALID) != 0)? 'x':
|
||||
((s & BIT_NIL) != 0)? 'n': ' ' );
|
||||
}
|
||||
strs[i] = new String(b);
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String[] s = toStrings();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintStream ps = new PrintStream( baos );
|
||||
for ( int i=0; i<s.length; i++ ) {
|
||||
if ( i>0 ) ps.append( '\n' );
|
||||
ps.append( s[i] );
|
||||
if ( p != null && i>0 && i<=p.code.length )
|
||||
Print.printOpCode(ps, p, i-1);
|
||||
}
|
||||
ps.close();
|
||||
return baos.toString();
|
||||
}
|
||||
}
|
||||
113
src/jse/org/luaj/vm2/luajc/UpvalInfo.java
Normal file
113
src/jse/org/luaj/vm2/luajc/UpvalInfo.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.luaj.vm2.luajc;
|
||||
|
||||
public class UpvalInfo {
|
||||
ProtoInfo pi; // where defined
|
||||
int slot; // where defined
|
||||
int nvars; // number of vars involved
|
||||
VarInfo var[]; // list of vars
|
||||
boolean rw; // read-write
|
||||
|
||||
public UpvalInfo(ProtoInfo pi, int pc, int slot) {
|
||||
this.pi = pi;
|
||||
this.slot = slot;
|
||||
this.nvars = 0;
|
||||
this.var = null;
|
||||
includeVars( pi.vars[slot][pc] );
|
||||
for ( int i=0; i<nvars; i++ )
|
||||
var[i].allocupvalue = testIsAllocUpvalue( var[i] );
|
||||
this.rw = nvars > 1;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append( pi.name );
|
||||
for ( int i=0; i<nvars; i++ ) {
|
||||
sb.append( i>0? ",": " " );
|
||||
sb.append( String.valueOf(var[i]));
|
||||
}
|
||||
if ( rw )
|
||||
sb.append( "(rw)" );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void includeVar(VarInfo v) {
|
||||
if ( v == null )
|
||||
return;
|
||||
if ( includes(v) )
|
||||
return;
|
||||
if ( nvars == 0 ) {
|
||||
var = new VarInfo[1];
|
||||
} else if ( nvars+1 >= var.length ) {
|
||||
VarInfo[] s = var;
|
||||
var = new VarInfo[nvars*2+1];
|
||||
System.arraycopy(s, 0, var, 0, nvars);
|
||||
}
|
||||
var[nvars++] = v;
|
||||
}
|
||||
|
||||
private boolean includes(VarInfo v) {
|
||||
for ( int i=0; i<nvars; i++ )
|
||||
if ( var[i] == v )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void includeVars(VarInfo v) {
|
||||
int slot = this.slot;
|
||||
|
||||
// check for previous assignment
|
||||
loop: while ( true ) {
|
||||
BasicBlock b = pi.blocks[v.pc];
|
||||
if ( v.upvalue == this ) {
|
||||
// loop detected, include previous values
|
||||
for ( int i=0, n=b.ninputs[slot]; i<n; i++ ) {
|
||||
v = b.inputs[slot][i];
|
||||
if ( v.upvalue != this )
|
||||
includeVars(v);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// check for assignment to 2 upvalues at once
|
||||
if ( v.upvalue != null )
|
||||
throw new IllegalArgumentException("upvalue collision detected between "+v.upvalue+" and "+this);
|
||||
|
||||
// invalid values terminate search
|
||||
if ( v == VarInfo.INVALID )
|
||||
return;
|
||||
|
||||
// assign the variable
|
||||
v.upvalue = this;
|
||||
this.includeVar(v);
|
||||
|
||||
// find next variable within the basic block
|
||||
for ( int i=v.pc; i<=b.pc1; i++ ) {
|
||||
if ( pi.vars[slot][i] != v ) {
|
||||
v = pi.vars[slot][i];
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
|
||||
// at end of basic block, go to next blocks
|
||||
for ( int i=0, n=b.next!=null? b.next.length: 0; i<n; i++ )
|
||||
includeVars(pi.vars[slot][b.next[i].pc0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean testIsAllocUpvalue(VarInfo v) {
|
||||
BasicBlock b = pi.blocks[v.pc];
|
||||
if ( v.pc > b.pc0 )
|
||||
return pi.vars[slot][v.pc-1].upvalue != this;
|
||||
if ( b.ninputs[slot] <= 0 )
|
||||
return true;
|
||||
for ( int k=0, n=b.ninputs[slot]; k<n; k++ )
|
||||
if ( b.inputs[slot][k].upvalue != this )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
38
src/jse/org/luaj/vm2/luajc/VarInfo.java
Normal file
38
src/jse/org/luaj/vm2/luajc/VarInfo.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.luaj.vm2.luajc;
|
||||
|
||||
public class VarInfo {
|
||||
|
||||
public static VarInfo INVALID = new VarInfo(-1,-1);
|
||||
|
||||
public static VarInfo NIL(int slot) {
|
||||
return new VarInfo(slot,-1);
|
||||
}
|
||||
|
||||
public static VarInfo PHI(int slot, int pc) {
|
||||
return new VarInfo(slot,pc) {
|
||||
public String toString() {
|
||||
return super.toString()+"p";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final int slot; // where assigned
|
||||
public final int pc; // where assigned, or -1 if for block inputs
|
||||
|
||||
public UpvalInfo upvalue; // not null if this var is an upvalue
|
||||
public boolean allocupvalue; // true if this variable allocations r/w upvalue storage
|
||||
|
||||
public VarInfo(int slot, int pc) {
|
||||
this.slot = slot;
|
||||
this.pc = pc;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return slot<0? "x.x":
|
||||
pc<0? "nil":
|
||||
(slot+"."+pc);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user