fix double/long locals, fix jsr

remove stores without locads


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@514 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent f184ef9b97
commit 8007f5401f
  1. 251
      jode/jode/obfuscator/LocalOptimizer.java

@ -1,8 +1,28 @@
/* LocalOptimizer Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.obfuscator; package jode.obfuscator;
import java.util.*; import java.util.*;
import jode.bytecode.*; import jode.bytecode.*;
import jode.AssertError;
public class LocalOptimizer { public class LocalOptimizer implements Opcodes {
class LocalInfo { class LocalInfo {
LocalInfo shadow = null; LocalInfo shadow = null;
@ -16,6 +36,7 @@ public class LocalOptimizer {
Vector usingInstrs = new Vector(); Vector usingInstrs = new Vector();
Vector conflictingLocals = new Vector(); Vector conflictingLocals = new Vector();
int size;
int newSlot = -1; int newSlot = -1;
LocalInfo(InstrInfo instr) { LocalInfo(InstrInfo instr) {
@ -53,15 +74,16 @@ public class LocalOptimizer {
} }
class InstrInfo { class InstrInfo {
/**
* Tell if the localsToRead variable has changed.
*/
boolean changed;
/** /**
* The LocalInfo of the next Instruction, that may read a local, * The LocalInfo of the next Instruction, that may read a local,
* without prior writing. * without prior writing.
*/ */
LocalInfo[] nextReads; LocalInfo[] nextReads;
/**
* The jsr to which the nextReads at this slot belongs to.
* I think I don't like jsrs any more.
*/
Vector[] belongsToJsrs;
/** /**
* The LocalInfo for this local * The LocalInfo for this local
*/ */
@ -74,9 +96,14 @@ public class LocalOptimizer {
* The next info in the chain. * The next info in the chain.
*/ */
InstrInfo nextInfo; InstrInfo nextInfo;
/**
* If instruction is a jsr, this array contains the ret instructions.
*/
Instruction[] retInstrs;
} }
InstrInfo firstInfo; InstrInfo firstInfo;
Stack changedInfos;
Hashtable instrInfos; Hashtable instrInfos;
BytecodeInfo bc; BytecodeInfo bc;
@ -84,12 +111,63 @@ public class LocalOptimizer {
this.bc = bc; this.bc = bc;
} }
public void promoteReads(InstrInfo info, Instruction preInstr) { /**
* This method determines which rets belong to a given jsr. This
* is needed, since the predecessors must be exact.
*/
void findRets(InstrInfo jsrInfo) {
Vector rets = new Vector();
Stack instrStack = new Stack();
Instruction subInstr = jsrInfo.instr.succs[0];
if (subInstr.opcode != opc_astore)
throw new AssertError("Non standard jsr");
int slot = subInstr.localSlot;
instrStack.push(subInstr.nextByAddr);
while (!instrStack.isEmpty()) {
Instruction instr = (Instruction) instrStack.pop();
if (instr.localSlot == slot) {
if (instr.opcode != opc_ret)
throw new AssertError("Non standard jsr");
rets.addElement(instr);
}
if (!instr.alwaysJumps)
instrStack.push(instr.nextByAddr);
if (instr.succs != null)
for (int i=0; i< instr.succs.length; i++)
instrStack.push(instr.succs[i]);
}
jsrInfo.retInstrs = new Instruction[rets.size()];
rets.copyInto(jsrInfo.retInstrs);
}
/**
* Merges the given vector to a new vector. Both vectors may
* be null in which case they are interpreted as empty vectors.
* The vectors will never changed, but the result may be one
* of the given vectors.
*/
Vector merge(Vector v1, Vector v2) {
if (v1 == null || v1.isEmpty())
return v2;
if (v2 == null || v2.isEmpty())
return v1;
Vector result = (Vector) v1.clone();
Enumeration enum = v2.elements();
while (enum.hasMoreElements()) {
Object elem = enum.nextElement();
if (!result.contains(elem))
result.addElement(elem);
}
return result;
}
void promoteReads(InstrInfo info, Instruction preInstr,
Instruction belongingJsr) {
InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr); InstrInfo preInfo = (InstrInfo) instrInfos.get(preInstr);
int omitLocal = -1; int omitLocal = -1;
if (preInstr.localSlot != -1 if (preInstr.localSlot != -1
&& preInstr.opcode >= BytecodeInfo.opc_istore && preInstr.opcode >= opc_istore
&& preInstr.opcode < BytecodeInfo.opc_iastore) { && preInstr.opcode <= opc_astore) {
/* This is a store */ /* This is a store */
omitLocal = preInstr.localSlot; omitLocal = preInstr.localSlot;
if (info.nextReads[preInstr.localSlot] != null) if (info.nextReads[preInstr.localSlot] != null)
@ -98,10 +176,30 @@ public class LocalOptimizer {
} }
int maxlocals = bc.getMaxLocals(); int maxlocals = bc.getMaxLocals();
for (int i=0; i< maxlocals; i++) { for (int i=0; i< maxlocals; i++) {
Vector newBelongs = info.belongsToJsrs[i];
if (preInstr.opcode == opc_jsr
&& newBelongs != null) {
if (!info.belongsToJsrs[i].contains(preInstr))
/* This was the wrong jsr */
continue;
if (info.belongsToJsrs[i].size() == 1)
newBelongs = null;
else {
newBelongs = (Vector) info.belongsToJsrs[i].clone();
newBelongs.removeElement(preInstr);
}
}
if (belongingJsr != null) {
if (newBelongs == null)
newBelongs = new Vector();
newBelongs.addElement(belongingJsr);
}
if (info.nextReads[i] != null && i != omitLocal) { if (info.nextReads[i] != null && i != omitLocal) {
preInfo.belongsToJsrs[i]
= merge(preInfo.belongsToJsrs[i], newBelongs);
if (preInfo.nextReads[i] == null) { if (preInfo.nextReads[i] == null) {
preInfo.nextReads[i] = info.nextReads[i]; preInfo.nextReads[i] = info.nextReads[i];
preInfo.changed = true; changedInfos.push(preInfo);
} else { } else {
preInfo.nextReads[i] preInfo.nextReads[i]
.combineInto(info.nextReads[i]); .combineInto(info.nextReads[i]);
@ -115,6 +213,7 @@ public class LocalOptimizer {
Handler[] handlers = bc.getExceptionHandlers(); Handler[] handlers = bc.getExceptionHandlers();
/* Initialize the InstrInfos and LocalInfos /* Initialize the InstrInfos and LocalInfos
*/ */
changedInfos = new Stack();
instrInfos = new Hashtable(); instrInfos = new Hashtable();
{ {
InstrInfo info = firstInfo = new InstrInfo(); InstrInfo info = firstInfo = new InstrInfo();
@ -123,13 +222,25 @@ public class LocalOptimizer {
instrInfos.put(instr, info); instrInfos.put(instr, info);
info.instr = instr; info.instr = instr;
info.nextReads = new LocalInfo[maxlocals]; info.nextReads = new LocalInfo[maxlocals];
info.belongsToJsrs = new Vector[maxlocals];
if (instr.localSlot != -1) { if (instr.localSlot != -1) {
info.local = new LocalInfo(info); info.local = new LocalInfo(info);
if (instr.opcode < BytecodeInfo.opc_istore info.local.size = 1;
|| instr.opcode > BytecodeInfo.opc_iastore) { switch (instr.opcode) {
case opc_lload: case opc_dload:
info.local.size = 2;
/* fall through */
case opc_iload: case opc_fload: case opc_aload:
case opc_ret:
case opc_iinc:
/* this is a load instruction */ /* this is a load instruction */
info.nextReads[instr.localSlot] = info.local; info.nextReads[instr.localSlot] = info.local;
info.changed = true; changedInfos.push(info);
break;
case opc_lstore: case opc_dstore:
info.local.size = 2;
case opc_istore: case opc_fstore: case opc_astore:
} }
} }
if ((instr = instr.nextByAddr) == null) if ((instr = instr.nextByAddr) == null)
@ -138,54 +249,71 @@ public class LocalOptimizer {
} }
} }
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo)
if (info.instr.opcode == opc_jsr)
findRets(info);
/* find out which locals are the same. /* find out which locals are the same.
*/ */
boolean changed = true; while (!changedInfos.isEmpty()) {
while (changed) { InstrInfo info = (InstrInfo) changedInfos.pop();
changed = false; Enumeration enum = info.instr.preds.elements();
for (InstrInfo info = firstInfo; while (enum.hasMoreElements()) {
info != null; info = info.nextInfo) { Instruction preInstr
if (info.changed) { = (Instruction) enum.nextElement();
info.changed = false; if (preInstr.opcode == opc_jsr
Enumeration enum = info.instr.preds.elements(); && info.instr != preInstr.succs[0]) {
while (enum.hasMoreElements()) { /* Prev expr is a jsr, continue with the
changed = true; * corresponding ret, instead with the jsr.
Instruction preInstr */
= (Instruction) enum.nextElement(); InstrInfo jsrInfo =
promoteReads(info, preInstr); (InstrInfo) instrInfos.get(preInstr);
} for (int i= jsrInfo.retInstrs.length; i-- > 0; )
for (int i=0; i<handlers.length; i++) { promoteReads(info, jsrInfo.retInstrs[i], preInstr);
if (handlers[i].catcher == info.instr) { } else
for (Instruction preInstr = handlers[i].start; promoteReads(info, preInstr, null);
preInstr != handlers[i].end; }
preInstr = preInstr.nextByAddr) { for (int i=0; i<handlers.length; i++) {
promoteReads(info, preInstr); if (handlers[i].catcher == info.instr) {
} for (Instruction preInstr = handlers[i].start;
} preInstr != handlers[i].end.nextByAddr;
preInstr = preInstr.nextByAddr) {
promoteReads(info, preInstr, null);
} }
} }
} }
} }
changedInfos = null;
}
/* Now calculate the conflict settings. public void stripLocals() {
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) { for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.instr.localSlot != -1 if (info.local != null && info.local.usingInstrs.size() == 1) {
&& info.instr.opcode >= BytecodeInfo.opc_istore /* If this is a store, whose value is never read; it can
&& info.instr.opcode < BytecodeInfo.opc_iastore) { * be removed, i.e replaced by a pop. */
/* This is a store. It conflicts with every local, whose switch (info.instr.opcode) {
* value will be read without write. case opc_istore:
*/ case opc_fstore:
for (int i=0; i < maxlocals; i++) { case opc_astore:
if (i != info.instr.localSlot info.local = null;
&& info.nextReads[i] != null) info.instr.opcode = opc_pop;
info.local.conflictsWith(info.nextReads[i]); info.instr.length = 1;
info.instr.localSlot = -1;
break;
case opc_lstore:
case opc_dstore:
info.local = null;
info.instr.opcode = opc_pop2;
info.instr.length = 1;
info.instr.localSlot = -1;
break;
default:
} }
} }
} }
} }
public void distributeLocals(Vector locals) { void distributeLocals(Vector locals) {
if (locals.size() == 0) if (locals.size() == 0)
return; return;
@ -217,8 +345,18 @@ public class LocalOptimizer {
for (int slot = 0; ; slot++) { for (int slot = 0; ; slot++) {
Enumeration conflenum = bestLocal.conflictingLocals.elements(); Enumeration conflenum = bestLocal.conflictingLocals.elements();
while (conflenum.hasMoreElements()) { while (conflenum.hasMoreElements()) {
if (((LocalInfo)conflenum.nextElement()).newSlot == slot) LocalInfo conflLocal = (LocalInfo)conflenum.nextElement();
if (bestLocal.size == 2 && conflLocal.newSlot == slot+1) {
slot++;
continue next_slot;
}
if (conflLocal.size == 2 && conflLocal.newSlot+1 == slot)
continue next_slot;
if (conflLocal.newSlot == slot) {
if (conflLocal.size == 2)
slot++;
continue next_slot; continue next_slot;
}
} }
bestLocal.newSlot = slot; bestLocal.newSlot = slot;
break; break;
@ -241,6 +379,23 @@ public class LocalOptimizer {
firstInfo.nextReads[i].getReal().newSlot = i; firstInfo.nextReads[i].getReal().newSlot = i;
} }
/* Now calculate the conflict settings.
*/
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
if (info.instr.localSlot != -1
&& info.instr.opcode >= BytecodeInfo.opc_istore
&& info.instr.opcode <= BytecodeInfo.opc_astore) {
/* This is a store. It conflicts with every local, whose
* value will be read without write.
*/
for (int i=0; i < maxlocals; i++) {
if (i != info.instr.localSlot
&& info.nextReads[i] != null)
info.local.conflictsWith(info.nextReads[i]);
}
}
}
/* Now put the locals that need a color into a vector. /* Now put the locals that need a color into a vector.
*/ */
Vector locals = new Vector(); Vector locals = new Vector();

Loading…
Cancel
Save