Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
16 KiB
726 lines
16 KiB
/**
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package EDU.purdue.cs.bloat.inline;
|
|
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.editor.*;
|
|
|
|
/**
|
|
* Used to keep track of the height of the stack. As instructions are visited,
|
|
* the height of the stack is adjusted accordingly.
|
|
*/
|
|
public class StackHeightCounter extends InstructionAdapter {
|
|
public static boolean DEBUG = false;
|
|
|
|
private int height; // Current stack height
|
|
|
|
private HashMap labelHeights; // Maps labels to their heights as Integers
|
|
|
|
private MethodEditor method; // Method whose height we're computing
|
|
|
|
Set tryCatches; // TryCatches active at current instruction
|
|
|
|
private static void db(final String s) {
|
|
if (StackHeightCounter.DEBUG) {
|
|
System.out.println(s);
|
|
}
|
|
}
|
|
|
|
public StackHeightCounter(final MethodEditor method) {
|
|
this.method = method;
|
|
this.height = 0;
|
|
this.labelHeights = new HashMap();
|
|
this.tryCatches = new HashSet();
|
|
}
|
|
|
|
/**
|
|
* Returns the current height of the stack.
|
|
*/
|
|
public int height() {
|
|
return (this.height);
|
|
}
|
|
|
|
/**
|
|
* Handles a Label. Special provisions must be made for labels that catch
|
|
* exceptions.
|
|
*/
|
|
public void handle(final Label label) {
|
|
final Integer labelHeight = (Integer) labelHeights.get(label);
|
|
if (labelHeight != null) {
|
|
height = labelHeight.intValue();
|
|
}
|
|
|
|
// If this label begins an exception handler, then start it off
|
|
// with a new stack with one element (the exception object) on it.
|
|
final Iterator tryCatches = method.tryCatches().iterator();
|
|
while (tryCatches.hasNext()) {
|
|
final TryCatch tc = (TryCatch) tryCatches.next();
|
|
if (tc.handler().equals(label)) {
|
|
label.setStartsBlock(true);
|
|
height = 1;
|
|
break;
|
|
}
|
|
|
|
if (tc.start().equals(label)) {
|
|
// If this block starts a protected region make note of the
|
|
// TryCatch block
|
|
this.tryCatches.add(tc);
|
|
}
|
|
|
|
if (tc.end().equals(label)) {
|
|
// If this block ends a protected region, remove it from the
|
|
// tryCatches list
|
|
this.tryCatches.remove(tc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles an instruction. Special provisions must be made to handle jumps,
|
|
* switches, throws, and returns.
|
|
*/
|
|
public void handle(final Instruction inst) {
|
|
inst.visit(this);
|
|
|
|
if (inst.isJump()) {
|
|
final Label target = (Label) inst.operand();
|
|
target.setStartsBlock(true);
|
|
final Integer targetHeight = (Integer) labelHeights.get(target);
|
|
if (targetHeight != null) {
|
|
if (targetHeight.intValue() != height) {
|
|
// Make sure stack heights match
|
|
StackHeightCounter.db("Stack height mismatch ("
|
|
+ targetHeight.intValue() + " != " + height
|
|
+ ") at " + inst);
|
|
}
|
|
|
|
} else {
|
|
labelHeights.put(target, new Integer(height));
|
|
}
|
|
|
|
} else if (inst.isSwitch()) {
|
|
// Propagate height to all targets
|
|
final Switch sw = (Switch) inst.operand();
|
|
final Label defaultTarget = sw.defaultTarget();
|
|
defaultTarget.setStartsBlock(true);
|
|
final Integer dTargetHeight = (Integer) labelHeights
|
|
.get(defaultTarget);
|
|
if (dTargetHeight != null) {
|
|
if (dTargetHeight.intValue() != height) {
|
|
// Make sure stack heights match
|
|
StackHeightCounter.db("Stack height mismatch ("
|
|
+ dTargetHeight.intValue() + " != " + height
|
|
+ ") at " + inst);
|
|
}
|
|
} else {
|
|
labelHeights.put(defaultTarget, new Integer(height));
|
|
}
|
|
|
|
final Label[] targets = sw.targets();
|
|
for (int t = 0; t < targets.length; t++) {
|
|
final Label target = targets[t];
|
|
target.setStartsBlock(true);
|
|
final Integer targetHeight = (Integer) labelHeights.get(target);
|
|
if (targetHeight != null) {
|
|
if (targetHeight.intValue() != height) {
|
|
// Make sure stack heights match
|
|
StackHeightCounter.db("Stack height mismatch ("
|
|
+ targetHeight.intValue() + " != " + height
|
|
+ ") at " + inst);
|
|
}
|
|
} else {
|
|
labelHeights.put(target, new Integer(height));
|
|
}
|
|
}
|
|
|
|
} else if (inst.isJsr()) {
|
|
// We have to account for the return address being pushed on the
|
|
// stack. Let's ignore the fact that someday in the future
|
|
// subroutines may push stuff on the stack. M'kay?
|
|
final Label subroutine = (Label) inst.operand();
|
|
subroutine.setStartsBlock(true);
|
|
final Integer subHeight = (Integer) labelHeights.get(subroutine);
|
|
if (subHeight != null) {
|
|
if (subHeight.intValue() != height + 1) {
|
|
StackHeightCounter
|
|
.db("Stack height mismatch at subroutine ("
|
|
+ subHeight.intValue() + " != "
|
|
+ (height + 1) + ") at " + inst);
|
|
}
|
|
|
|
} else {
|
|
labelHeights.put(subroutine, new Integer(height + 1));
|
|
}
|
|
|
|
} else if (inst.isThrow() || inst.isReturn()) {
|
|
// Clear the stack
|
|
height = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simulates the effect of "backing up" over an instruction.
|
|
*/
|
|
public void unhandle(final Instruction inst) {
|
|
// Temporarily negate the stack height, perform the normal handle,
|
|
// and then negate the stack height again.
|
|
this.height = -this.height;
|
|
this.handle(inst);
|
|
this.height = -this.height;
|
|
}
|
|
|
|
/**
|
|
* Returns the set of <tt>TryCatch</tt> objects for the protected region
|
|
* that the current instruction may be in.
|
|
*/
|
|
public Set tryCatches() {
|
|
return (this.tryCatches);
|
|
}
|
|
|
|
public void visit_ldc(final Instruction inst) {
|
|
final Object operand = inst.operand();
|
|
|
|
if ((operand instanceof Long) || (operand instanceof Double)) {
|
|
height += 2;
|
|
|
|
} else {
|
|
height += 1;
|
|
}
|
|
}
|
|
|
|
public void visit_iload(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_lload(final Instruction inst) {
|
|
height += 2;
|
|
}
|
|
|
|
public void visit_fload(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_dload(final Instruction inst) {
|
|
height += 2;
|
|
}
|
|
|
|
public void visit_aload(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_iaload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_laload(final Instruction inst) {
|
|
height -= 0;
|
|
}
|
|
|
|
public void visit_faload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_daload(final Instruction inst) {
|
|
height -= 0;
|
|
}
|
|
|
|
public void visit_aaload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_baload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_caload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_saload(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_istore(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lstore(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_fstore(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_dstore(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_astore(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_iastore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_lastore(final Instruction inst) {
|
|
height -= 4;
|
|
}
|
|
|
|
public void visit_fastore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_dastore(final Instruction inst) {
|
|
height -= 4;
|
|
}
|
|
|
|
public void visit_aastore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_bastore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_castore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_sastore(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_pop(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_pop2(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_dup(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_dup_x1(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_dup_x2(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_dup2(final Instruction inst) {
|
|
height += 2;
|
|
}
|
|
|
|
public void visit_dup2_x1(final Instruction inst) {
|
|
height += 2;
|
|
}
|
|
|
|
public void visit_dup2_x2(final Instruction inst) {
|
|
height += 2;
|
|
}
|
|
|
|
public void visit_iadd(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ladd(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_fadd(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_dadd(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_isub(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lsub(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_fsub(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_dsub(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_imul(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lmul(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_fmul(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_dmul(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_idiv(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ldiv(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_fdiv(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ddiv(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_irem(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lrem(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_frem(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_drem(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_ishl(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lshl(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ishr(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lshr(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_iushr(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lushr(final Instruction inst) {
|
|
// Yes, it's only -1. The long and the int index are popped off
|
|
// and the shifted value is pushed. Net loss of 1.
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_iand(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_land(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_ior(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lor(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_ixor(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lxor(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_i2l(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_i2d(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_l2i(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_l2f(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_f2l(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_f2d(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_d2i(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_d2f(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_lcmp(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_fcmpl(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_fcmpg(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_dcmpl(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_dcmpg(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
public void visit_ifeq(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ifne(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_iflt(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ifge(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ifgt(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ifle(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_if_icmpeq(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpne(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_icmplt(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpge(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpgt(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_icmple(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_acmpeq(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_if_acmpne(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_jsr(final Instruction inst) {
|
|
// Even though the jsr instruction itself pushes the return
|
|
// address onto the stack, we don't want to account for that
|
|
// here. It is already taken care of in the handle method. This
|
|
// way the label following the jsr (the return site) will have the
|
|
// stack height it had before the call. Once again, we do not
|
|
// account for the possibility of the jsr modifying the height of
|
|
// the stack.
|
|
height += 0;
|
|
}
|
|
|
|
public void visit_switch(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ireturn(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_lreturn(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_freturn(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_dreturn(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_areturn(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_return(final Instruction inst) {
|
|
height = 0;
|
|
}
|
|
|
|
public void visit_getstatic(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height += type.stackHeight();
|
|
}
|
|
|
|
public void visit_putstatic(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height -= type.stackHeight();
|
|
}
|
|
|
|
public void visit_putstatic_nowb(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height -= type.stackHeight();
|
|
}
|
|
|
|
public void visit_getfield(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height += type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_putfield(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height -= type.stackHeight() + 1;
|
|
}
|
|
|
|
public void visit_putfield_nowb(final Instruction inst) {
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
height -= type.stackHeight() + 1;
|
|
}
|
|
|
|
public void visit_invokevirtual(final Instruction inst) {
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
height += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_invokespecial(final Instruction inst) {
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
height += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_invokestatic(final Instruction inst) {
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
height += type.returnType().stackHeight() - type.stackHeight();
|
|
}
|
|
|
|
public void visit_invokeinterface(final Instruction inst) {
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
height += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
|
|
}
|
|
|
|
public void visit_new(final Instruction inst) {
|
|
height += 1;
|
|
}
|
|
|
|
public void visit_monitorenter(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_monitorexit(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_multianewarray(final Instruction inst) {
|
|
final MultiArrayOperand operand = (MultiArrayOperand) inst.operand();
|
|
final int dim = operand.dimensions();
|
|
|
|
height += 1 - dim;
|
|
}
|
|
|
|
public void visit_ifnull(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_ifnonnull(final Instruction inst) {
|
|
height -= 1;
|
|
}
|
|
|
|
public void visit_aswizzle(final Instruction inst) {
|
|
height -= 2;
|
|
}
|
|
|
|
public void visit_aswrange(final Instruction inst) {
|
|
height -= 3;
|
|
}
|
|
|
|
/**
|
|
* Returns a clone of this <tt>StackHeightCounter</tt>
|
|
*/
|
|
public Object clone() {
|
|
final StackHeightCounter clone = new StackHeightCounter(this.method);
|
|
clone.height = this.height;
|
|
clone.labelHeights = (HashMap) this.labelHeights.clone();
|
|
return (clone);
|
|
}
|
|
}
|
|
|