/*
BytecodeOptimizer.java
(c) 2008-2013 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package v9t9.engine.compiler;
import java.util.Iterator;
import org.apache.bcel.Constants;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionTargeter;
import org.apache.bcel.generic.LocalVariableInstruction;
import org.apache.bcel.generic.PushInstruction;
import org.apache.bcel.generic.StackProducer;
import org.apache.bcel.generic.TargetLostException;
import org.apache.bcel.util.InstructionFinder;
/**
* Optimizer for bytecode
* @author ejs
*
*/
public class BytecodeOptimizer {
/**
* @param ii
*/
public static void peephole(CompileInfo info, CompiledInstInfo ii) {
boolean changed;
//int origCount = info.ilist.size();
do {
changed = false;
changed |= removeStoreLoads(info);
changed |= removeStoreStackOpLoads(info);
changed |= removeSwaps(info);
if (changed) {
removeNOPs(info);
//System.out.println("Changed " + origCount + " instructions to " + info.ilist.size());
}
} while (changed);
}
/**
* Cleanup this pattern:
*
* <pre>
* ISTORE n
* ILOAD n
* </pre>
*
* @param info
* @return true: changes made
*/
@SuppressWarnings("unchecked")
private static final boolean removeStoreLoads(CompileInfo info) {
InstructionFinder f = new InstructionFinder(info.ilist);
String pat = "ISTORE ILOAD";
int count = 0;
for (Iterator<InstructionHandle[]> e = f.search(pat); e.hasNext();) {
InstructionHandle[] match = (InstructionHandle[]) e.next();
InstructionHandle first = match[0];
InstructionHandle last = match[1];
// see if they share the same local
LocalVariableInstruction store = (LocalVariableInstruction) first
.getInstruction();
LocalVariableInstruction load = (LocalVariableInstruction) last
.getInstruction();
if (store.getIndex() == load.getIndex()
&& sameBlocks(first, last)) {
// ensure this is the last use
InstructionHandle anotherUse = findNextLocalUse(store
.getIndex(), last);
if (anotherUse != null) {
continue;
}
count++;
first.setInstruction(InstructionConstants.NOP);
last.setInstruction(InstructionConstants.NOP);
f.reread();
}
}
if (count > 0) {
// System.out.println("Removed " + count
// + " STORE/LOAD pairs from method");
return true;
}
return false;
}
/**
* Cleanup this pattern:
*
* <pre>
* ISTORE n
* stackload
* ILOAD n
* </pre>
*
* into:
*
* <pre>
* stackload
* SWAP
* </pre>
*
* @param info
* @return true: changes made
*/
@SuppressWarnings("unchecked")
private static final boolean removeStoreStackOpLoads(CompileInfo info) {
InstructionFinder f = new InstructionFinder(info.ilist);
String pat = "ISTORE StackProducer ILOAD";
int count = 0;
for (Iterator<InstructionHandle[]> e = f.search(pat); e.hasNext();) {
InstructionHandle[] match = (InstructionHandle[]) e.next();
if (match.length != 3) {
throw new AssertionError();
}
InstructionHandle first = match[0];
InstructionHandle middle = match[1];
InstructionHandle last = match[2];
// see if they share the same local
LocalVariableInstruction store = (LocalVariableInstruction) first
.getInstruction();
StackProducer stackop = (StackProducer) middle.getInstruction();
LocalVariableInstruction load = (LocalVariableInstruction) last
.getInstruction();
if (store.getIndex() == load.getIndex()
&& sameBlocks(first, last)
&& stackop.produceStack(info.pgen) == 1) {
// ensure this is the last use
InstructionHandle anotherUse = findNextLocalUse(store
.getIndex(), last);
if (anotherUse != null) {
continue;
}
count++;
// change to SWAP
first.setInstruction(InstructionConstants.NOP);
last.setInstruction(InstructionConstants.SWAP);
f.reread();
}
}
if (count > 0) {
// System.out.println("Removed " + count
// + " STORE/stackop/LOAD pairs from method");
return true;
}
return false;
}
@SuppressWarnings("unchecked")
static final boolean removeSwaps(CompileInfo info) {
InstructionFinder f = new InstructionFinder(info.ilist);
String pat = "PushInstruction PushInstruction SWAP";
int count = 0;
for (Iterator<InstructionHandle[]> e = f.search(pat); e.hasNext();) {
InstructionHandle[] match = e.next();
if (match.length != 3) {
throw new AssertionError();
}
InstructionHandle first = match[0];
InstructionHandle middle = match[1];
InstructionHandle last = match[2];
PushInstruction prod1 = (PushInstruction) first.getInstruction();
PushInstruction prod2 = (PushInstruction) middle.getInstruction();
if (sameBlocks(first, last)
&& prod1.produceStack(info.pgen) == 1
&& prod2.produceStack(info.pgen) == 1
&& first.getInstruction().getOpcode() != Constants.SWAP
&& middle.getInstruction().getOpcode() != Constants.SWAP) {
count++;
// remove SWAP
org.apache.bcel.generic.Instruction tmp = first
.getInstruction();
first.setInstruction(middle.getInstruction());
middle.setInstruction(tmp);
last.setInstruction(InstructionConstants.NOP);
f.reread();
}
}
if (count > 0) {
// System.out.println("Removed " + count
// + " stackop/stackop/SWAP pairs from method");
return true;
}
return false;
}
private static boolean sameBlocks(InstructionHandle a, InstructionHandle b) {
while (a != b) {
if (a.getTargeters() != null) {
return false;
}
a = a.getNext();
}
return true;
}
private static InstructionHandle findNextLocalUse(int index,
InstructionHandle after) {
InstructionHandle step = after.getNext();
while (step != null) {
if (step.getInstruction() instanceof LocalVariableInstruction) {
LocalVariableInstruction inst = (LocalVariableInstruction) step
.getInstruction();
if (inst.getIndex() == index) {
return step;
}
}
step = step.getNext();
}
return null;
}
@SuppressWarnings("unchecked")
private static final void removeNOPs(CompileInfo info) {
InstructionFinder f = new InstructionFinder(info.ilist);
String pat = "NOP+"; // Find at least one NOP
InstructionHandle next = null;
int count = 0;
for (Iterator<InstructionHandle[]> e = f.search(pat); e.hasNext();) {
InstructionHandle[] match = (InstructionHandle[]) e.next();
InstructionHandle first = match[0];
InstructionHandle last = match[match.length - 1];
/*
* Some nasty Java compilers may add NOP at end of method.
*/
if ((next = last.getNext()) == null) {
break;
}
count += match.length;
/*
* Delete NOPs and redirect any references to them to the following
* (non-nop) instruction.
*/
try {
info.ilist.delete(first, last);
} catch (TargetLostException ex) {
InstructionHandle[] targets = ex.getTargets();
for (InstructionHandle element : targets) {
InstructionTargeter[] targeters = element.getTargeters();
for (InstructionTargeter element2 : targeters) {
element2.updateTarget(element, next);
}
}
}
}
if (count > 0) {
// System.out.println("Removed " + count
// + " NOP instructions from method");
}
// info.ilist.dispose(); // Reuse instruction handles
}
}