Refactor bytecode analysis

This commit is contained in:
James Roseborough
2010-08-06 05:35:18 +00:00
parent a1e0bc520d
commit 0348ac9f90
8 changed files with 723 additions and 555 deletions

View 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;
}
}

View File

@@ -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));

View File

@@ -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) {

View File

@@ -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] );
}

View 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;
}
}

View File

@@ -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();
}
}

View 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;
}
}

View 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);
}
}