/******************************************************************************* * Copyright (c) 2002,2006 IBM Corporation. * 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 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.shrikeBT.tools; import java.util.Arrays; import java.util.BitSet; import com.ibm.wala.shrikeBT.DupInstruction; import com.ibm.wala.shrikeBT.ExceptionHandler; import com.ibm.wala.shrikeBT.IInstruction; import com.ibm.wala.shrikeBT.LoadInstruction; import com.ibm.wala.shrikeBT.MethodData; import com.ibm.wala.shrikeBT.MethodEditor; import com.ibm.wala.shrikeBT.MethodEditor.Output; import com.ibm.wala.shrikeBT.PopInstruction; import com.ibm.wala.shrikeBT.StoreInstruction; import com.ibm.wala.shrikeBT.Util; import com.ibm.wala.shrikeBT.info.LocalAllocator; @Deprecated public final class MethodOptimizer { final private MethodData data; private IInstruction[] instructions; private ExceptionHandler[][] handlers; private final MethodEditor editor; // The value at index [i][N] is the index of the only instruction which pushes // a value onto // the stack which is #N popped by instruction i, or -2 if there is no such // instruction // or -1 if there is more than one such instruction. private int[][] uniqueStackDefLocations; // The value at index i[N] is the index of the only instruction which pops a // value off // the stack which is pushed by instruction i, or -2 if there is no such // instruction // or -1 if there is more than one such instruction. private int[] uniqueStackUseLocations; private int[] stackSizes; private int[][] backEdges; // The value at index i is the index of the only instruction which stores a // value onto // the stack which is popped by instruction i, or -2 if there is no such // instruction // or -1 if there is more than one such instruction. final static int[] noEdges = new int[0]; public MethodOptimizer(MethodData d, MethodEditor e) { if (d == null) { throw new IllegalArgumentException("null d"); } this.data = d; this.editor = e; } public MethodOptimizer(MethodData d) { this(d, new MethodEditor(d)); } public static class UnoptimizableCodeException extends Exception { private static final long serialVersionUID = 2543170335674010642L; public UnoptimizableCodeException(String s) { super(s); } } public int findUniqueStackDef(final int instr, final int stack) throws UnoptimizableCodeException { instructions = editor.getInstructions(); handlers = editor.getHandlers(); checkConsistentStackSizes(); buildBackEdges(); buildStackDefMap(); return uniqueStackDefLocations[instr][stack]; } public void optimize() throws UnoptimizableCodeException { boolean changed; do { instructions = editor.getInstructions(); handlers = editor.getHandlers(); checkConsistentStackSizes(); buildBackEdges(); editor.beginPass(); buildStackDefMap(); pushBackLocalStores(); forwardDups(); changed = editor.applyPatches(); editor.endPass(); } while (changed); } private void buildBackEdges() { int[] backEdgeCount = new int[instructions.length]; for (int i = 0; i < instructions.length; i++) { int[] targets = instructions[i].getBranchTargets(); for (int j = 0; j < targets.length; j++) { backEdgeCount[targets[j]]++; } ExceptionHandler[] hs = handlers[i]; for (int j = 0; j < hs.length; j++) { backEdgeCount[hs[j].getHandler()]++; } } backEdges = new int[instructions.length][]; for (int i = 0; i < backEdges.length; i++) { if (backEdgeCount[i] > 0) { backEdges[i] = new int[backEdgeCount[i]]; } else { backEdges[i] = noEdges; } } Arrays.fill(backEdgeCount, 0); for (int i = 0; i < instructions.length; i++) { int[] targets = instructions[i].getBranchTargets(); for (int j = 0; j < targets.length; j++) { int target = targets[j]; backEdges[target][backEdgeCount[target]] = i; backEdgeCount[target]++; } ExceptionHandler[] hs = handlers[i]; for (int j = 0; j < hs.length; j++) { int target = hs[j].getHandler(); backEdges[target][backEdgeCount[target]] = i; backEdgeCount[target]++; } } } private int checkConsistentStackSizes() throws UnoptimizableCodeException { stackSizes = new int[instructions.length]; Arrays.fill(stackSizes, -1); checkStackSizesAt(0, 0); int result = 0; for (int i = 0; i < stackSizes.length; i++) { result = Math.max(result, stackSizes[i]); } return result; } private void checkStackSizesAt(int instruction, int stackSize) throws UnoptimizableCodeException { while (true) { if (instruction < 0 || instruction >= instructions.length) { throw new UnoptimizableCodeException("Code exits in an illegal way"); } if (stackSizes[instruction] != -1) { if (stackSizes[instruction] != stackSize) { throw new UnoptimizableCodeException("Mismatched stack sizes at " + instruction + ": " + stackSize + " and " + stackSizes[instruction]); } else { return; } } stackSizes[instruction] = stackSize; IInstruction instr = instructions[instruction]; stackSize -= instr.getPoppedCount(); if (stackSize < 0) { throw new UnoptimizableCodeException("Stack underflow at " + instruction); } if (instr instanceof DupInstruction) { DupInstruction d = (DupInstruction) instr; stackSize += d.getSize() + d.getPoppedCount(); } else if (instr.getPushedType(null) != null) { stackSize++; } int[] targets = instr.getBranchTargets(); for (int i = 0; i < targets.length; i++) { checkStackSizesAt(targets[i], stackSize); } ExceptionHandler[] hs = handlers[instruction]; for (int i = 0; i < hs.length; i++) { checkStackSizesAt(hs[i].getHandler(), 1); } if (!instr.isFallThrough()) { return; } instruction++; } } private static boolean instructionKillsVar(IInstruction instr, int v) { if (instr instanceof StoreInstruction) { StoreInstruction st = (StoreInstruction) instr; return st.getVarIndex() == v || (Util.getWordSize(st.getType()) == 2 && st.getVarIndex() + 1 == v); } else { return false; } } private void forwardDups() { for (int i = 0; i < instructions.length; i++) { IInstruction instr = instructions[i]; if (instr instanceof DupInstruction && ((DupInstruction) instr).getDelta() == 0 && uniqueStackDefLocations[i][0] >= 0 && instructions[uniqueStackDefLocations[i][0]] instanceof LoadInstruction) { int source = uniqueStackDefLocations[i][0]; final LoadInstruction li = (LoadInstruction) instructions[source]; for (int j = 0; j < instructions.length; j++) { int[] locs = uniqueStackDefLocations[j]; if (locs[0] == i) { // check to see if the variable is killed along any path from the // dup // to its use BitSet path = getInstructionsOnPath(source, j); boolean killed = false; int v = li.getVarIndex(); for (int k = 0; j < instructions.length && !killed; k++) { if (path.get(k)) { if (instructionKillsVar(instructions[k], v)) { killed = true; } } } if (!killed) { editor.insertBefore(j, new MethodEditor.Patch() { @Override public void emitTo(Output w) { w.emit(PopInstruction.make(1)); w.emit(li); } }); } } } } } } private void pushBackLocalStores() { for (int i = 0; i < instructions.length; i++) { IInstruction instr = instructions[i]; if (instr instanceof StoreInstruction && uniqueStackDefLocations[i][0] >= 0 && uniqueStackDefLocations[i][0] != i - 1 && uniqueStackUseLocations[uniqueStackDefLocations[i][0]] == i) { final StoreInstruction s = (StoreInstruction) instr; int source = uniqueStackDefLocations[i][0]; // Check if the path from source to i contains anything killing the // variable BitSet path = getInstructionsOnPath(source, i); boolean killed = false; int v = s.getVarIndex(); for (int j = 0; j < instructions.length && !killed; j++) { if (path.get(j)) { if (instructionKillsVar(instructions[j], v)) { killed = true; } } } if (killed) { final String type = s.getType(); final int newVar = LocalAllocator.allocate(data, type); // put a store to the newVar right after the source editor.insertAfter(source, new MethodEditor.Patch() { @Override public void emitTo(Output w) { w.emit(StoreInstruction.make(type, newVar)); } }); // load newVar before storing to correct variable editor.insertBefore(i, new MethodEditor.Patch() { @Override public void emitTo(Output w) { w.emit(LoadInstruction.make(type, newVar)); } }); } else { // remove store instruction editor.replaceWith(i, new MethodEditor.Patch() { @Override public void emitTo(Output w) { } }); // replace it right after the source editor.insertAfter(source, new MethodEditor.Patch() { @Override public void emitTo(Output w) { w.emit(s); } }); } } } } private void buildStackDefMap() { int[][] abstractStacks = new int[instructions.length][]; for (int i = 0; i < instructions.length; i++) { abstractStacks[i] = new int[stackSizes[i]]; Arrays.fill(abstractStacks[i], -2); } for (int i = 0; i < instructions.length; i++) { if (instructions[i] instanceof DupInstruction) { DupInstruction d = (DupInstruction) instructions[i]; for (int j = 0; j < 2 * d.getSize() + d.getDelta(); j++) { followStackDef(abstractStacks, i, i + 1, stackSizes[i + 1] - 1 - j); } } else if (instructions[i].getPushedType(null) != null) { followStackDef(abstractStacks, i, i + 1, stackSizes[i + 1] - 1); } } uniqueStackDefLocations = new int[instructions.length][]; for (int i = 0; i < instructions.length; i++) { uniqueStackDefLocations[i] = new int[instructions[i].getPoppedCount()]; int popped = instructions[i].getPoppedCount(); System.arraycopy(abstractStacks[i], stackSizes[i] - popped, uniqueStackDefLocations[i], 0, popped); } uniqueStackUseLocations = new int[instructions.length]; Arrays.fill(uniqueStackUseLocations, -2); for (int i = 0; i < instructions.length; i++) { abstractStacks[i] = new int[stackSizes[i]]; Arrays.fill(abstractStacks[i], -2); } for (int i = 0; i < instructions.length; i++) { int count = instructions[i].getPoppedCount(); if (count == 1) { followStackUse(abstractStacks, i, i, stackSizes[i] - 1); } else if (count > 1) { for (int j = 0; j < count; j++) { followStackUse(abstractStacks, -1, i, stackSizes[i] - 1 - j); } } } for (int i = 0; i < instructions.length; i++) { if (instructions[i].getPushedType(null) != null) { uniqueStackUseLocations[i] = abstractStacks[i + 1][stackSizes[i + 1] - 1]; } } } private void followStackDef(int[][] abstractDefStacks, int def, int instruction, int stackPointer) { while (true) { int[] stack = abstractDefStacks[instruction]; if (stackPointer >= stack.length) { // the value must have been popped off by the last instruction return; } if (stack[stackPointer] == -2) { stack[stackPointer] = def; } else if (stack[stackPointer] == def) { return; } else if (stack[stackPointer] == -1) { return; } else { stack[stackPointer] = -1; def = -1; } int[] targets = instructions[instruction].getBranchTargets(); for (int i = 0; i < targets.length; i++) { followStackDef(abstractDefStacks, def, targets[i], stackPointer); } ExceptionHandler[] hs = handlers[instruction]; for (int i = 0; i < hs.length; i++) { followStackDef(abstractDefStacks, -1, hs[i].getHandler(), 0); } if (!instructions[instruction].isFallThrough()) { return; } instruction++; } } private void followStackUse(int[][] abstractUseStacks, int use, int instruction, int stackPointer) { while (true) { int[] stack = abstractUseStacks[instruction]; if (stackPointer >= stack.length) { // the value must have been pushed by this instruction return; } if (stack[stackPointer] == -2) { stack[stackPointer] = use; } else if (stack[stackPointer] == use || stack[stackPointer] == -1) { return; } else { stack[stackPointer] = -1; use = -1; } int[] back = backEdges[instruction]; for (int i = 0; i < back.length; i++) { followStackUse(abstractUseStacks, use, back[i], stackPointer); } if (instruction == 0 || !instructions[instruction - 1].isFallThrough()) { return; } instruction--; } } private BitSet getInstructionsOnPath(int from, int to) { BitSet reachable = new BitSet(); getReachableInstructions(reachable, from, to); BitSet reaching = new BitSet(); getReachingInstructions(reaching, from, to); reachable.and(reaching); return reachable; } private void getReachableInstructions(BitSet bits, int from, int to) { while (true) { if (from == to) { return; } bits.set(from); int[] targets = instructions[from].getBranchTargets(); for (int i = 0; i < targets.length; i++) { getReachableInstructions(bits, targets[i], to); } if (!instructions[from].isFallThrough()) { return; } from++; } } private void getReachingInstructions(BitSet bits, int from, int to) { while (true) { if (to == from) { return; } bits.set(to); int[] targets = backEdges[to]; for (int i = 0; i < targets.length; i++) { getReachingInstructions(bits, from, targets[i]); } if (to == 0 || !instructions[to - 1].isFallThrough()) { return; } to--; } } }