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. 253
      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;
import java.util.*;
import jode.bytecode.*;
import jode.AssertError;
public class LocalOptimizer {
public class LocalOptimizer implements Opcodes {
class LocalInfo {
LocalInfo shadow = null;
@ -16,6 +36,7 @@ public class LocalOptimizer {
Vector usingInstrs = new Vector();
Vector conflictingLocals = new Vector();
int size;
int newSlot = -1;
LocalInfo(InstrInfo instr) {
@ -51,17 +72,18 @@ public class LocalOptimizer {
}
}
}
class InstrInfo {
/**
* Tell if the localsToRead variable has changed.
*/
boolean changed;
/**
* The LocalInfo of the next Instruction, that may read a local,
* without prior writing.
*/
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
*/
@ -74,9 +96,14 @@ public class LocalOptimizer {
* The next info in the chain.
*/
InstrInfo nextInfo;
/**
* If instruction is a jsr, this array contains the ret instructions.
*/
Instruction[] retInstrs;
}
InstrInfo firstInfo;
Stack changedInfos;
Hashtable instrInfos;
BytecodeInfo bc;
@ -84,12 +111,63 @@ public class LocalOptimizer {
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);
int omitLocal = -1;
if (preInstr.localSlot != -1
&& preInstr.opcode >= BytecodeInfo.opc_istore
&& preInstr.opcode < BytecodeInfo.opc_iastore) {
&& preInstr.opcode >= opc_istore
&& preInstr.opcode <= opc_astore) {
/* This is a store */
omitLocal = preInstr.localSlot;
if (info.nextReads[preInstr.localSlot] != null)
@ -98,10 +176,30 @@ public class LocalOptimizer {
}
int maxlocals = bc.getMaxLocals();
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) {
preInfo.belongsToJsrs[i]
= merge(preInfo.belongsToJsrs[i], newBelongs);
if (preInfo.nextReads[i] == null) {
preInfo.nextReads[i] = info.nextReads[i];
preInfo.changed = true;
changedInfos.push(preInfo);
} else {
preInfo.nextReads[i]
.combineInto(info.nextReads[i]);
@ -115,6 +213,7 @@ public class LocalOptimizer {
Handler[] handlers = bc.getExceptionHandlers();
/* Initialize the InstrInfos and LocalInfos
*/
changedInfos = new Stack();
instrInfos = new Hashtable();
{
InstrInfo info = firstInfo = new InstrInfo();
@ -123,13 +222,25 @@ public class LocalOptimizer {
instrInfos.put(instr, info);
info.instr = instr;
info.nextReads = new LocalInfo[maxlocals];
info.belongsToJsrs = new Vector[maxlocals];
if (instr.localSlot != -1) {
info.local = new LocalInfo(info);
if (instr.opcode < BytecodeInfo.opc_istore
|| instr.opcode > BytecodeInfo.opc_iastore) {
info.local.size = 1;
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 */
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)
@ -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.
*/
boolean changed = true;
while (changed) {
changed = false;
for (InstrInfo info = firstInfo;
info != null; info = info.nextInfo) {
if (info.changed) {
info.changed = false;
Enumeration enum = info.instr.preds.elements();
while (enum.hasMoreElements()) {
changed = true;
Instruction preInstr
= (Instruction) enum.nextElement();
promoteReads(info, preInstr);
}
for (int i=0; i<handlers.length; i++) {
if (handlers[i].catcher == info.instr) {
for (Instruction preInstr = handlers[i].start;
preInstr != handlers[i].end;
preInstr = preInstr.nextByAddr) {
promoteReads(info, preInstr);
}
}
while (!changedInfos.isEmpty()) {
InstrInfo info = (InstrInfo) changedInfos.pop();
Enumeration enum = info.instr.preds.elements();
while (enum.hasMoreElements()) {
Instruction preInstr
= (Instruction) enum.nextElement();
if (preInstr.opcode == opc_jsr
&& info.instr != preInstr.succs[0]) {
/* Prev expr is a jsr, continue with the
* corresponding ret, instead with the jsr.
*/
InstrInfo jsrInfo =
(InstrInfo) instrInfos.get(preInstr);
for (int i= jsrInfo.retInstrs.length; i-- > 0; )
promoteReads(info, jsrInfo.retInstrs[i], preInstr);
} else
promoteReads(info, preInstr, null);
}
for (int i=0; i<handlers.length; i++) {
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) {
if (info.instr.localSlot != -1
&& info.instr.opcode >= BytecodeInfo.opc_istore
&& info.instr.opcode < BytecodeInfo.opc_iastore) {
/* 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]);
if (info.local != null && info.local.usingInstrs.size() == 1) {
/* If this is a store, whose value is never read; it can
* be removed, i.e replaced by a pop. */
switch (info.instr.opcode) {
case opc_istore:
case opc_fstore:
case opc_astore:
info.local = null;
info.instr.opcode = opc_pop;
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)
return;
@ -217,8 +345,18 @@ public class LocalOptimizer {
for (int slot = 0; ; slot++) {
Enumeration conflenum = bestLocal.conflictingLocals.elements();
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;
}
}
bestLocal.newSlot = slot;
break;
@ -241,6 +379,23 @@ public class LocalOptimizer {
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.
*/
Vector locals = new Vector();

Loading…
Cancel
Save