/*
* @(#)Instruction.java 1.38 95/10/24 Arthur van Hoff
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package sun.tools.asm;
import sun.tools.java.*;
import java.util.Enumeration;
import java.io.IOException;
import java.io.DataOutputStream;
/**
* An Java instruction
*/
public
class Instruction implements Constants {
int where;
int pc;
int opc;
Object value;
Instruction next;
/**
* Constructor
*/
public Instruction(int where, int opc, Object value) {
this.where = where;
this.opc = opc;
this.value = value;
}
/**
* Accessor
*/
public int getOpcode() {
return pc;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
/**
* Optimize
*/
void optimize(Environment env) {
switch (opc) {
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
// Don't keep the LocalVariable info around, unless we
// are actually going to generate a local variable table.
if ((value instanceof LocalVariable) && !env.debug()) {
value = new Integer(((LocalVariable)value).slot);
}
break;
case opc_goto: {
Label lbl = (Label)value;
value = lbl = lbl.getDestination();
if (lbl == next) {
// goto to the next instruction, obsolete
opc = opc_dead;
break;
}
if (lbl.next != null) {
switch (lbl.next.opc) {
case opc_return:
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
// goto to return
opc = lbl.next.opc;
value = lbl.next.value;
break;
}
}
break;
}
case opc_ifeq:
case opc_ifne:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_ifnull:
case opc_ifnonnull:
value = ((Label)value).getDestination();
if (value == next) {
// branch to next instruction, obsolete
opc = opc_pop;
break;
}
if ((next.opc == opc_goto) && (value == next.next)) {
// Conditional branch over goto, invert
// Note that you can't invert all conditions, condition
// results for float/double compares are not invertable.
switch (opc) {
case opc_ifeq: opc = opc_ifne; break;
case opc_ifne: opc = opc_ifeq; break;
case opc_iflt: opc = opc_ifge; break;
case opc_ifle: opc = opc_ifgt; break;
case opc_ifgt: opc = opc_ifle; break;
case opc_ifge: opc = opc_iflt; break;
case opc_ifnull: opc = opc_ifnonnull; break;
case opc_ifnonnull: opc = opc_ifnull; break;
}
value = next.value;
next.opc = opc_dead;
}
break;
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
value = ((Label)value).getDestination();
if (value == next) {
// branch to next instruction, obsolete
opc = opc_pop2;
break;
}
if ((next.opc == opc_goto) && (value == next.next)) {
// Conditional branch over goto, invert
switch (opc) {
case opc_if_acmpeq: opc = opc_if_acmpne; break;
case opc_if_acmpne: opc = opc_if_acmpeq; break;
case opc_if_icmpeq: opc = opc_if_icmpne; break;
case opc_if_icmpne: opc = opc_if_icmpeq; break;
case opc_if_icmpgt: opc = opc_if_icmple; break;
case opc_if_icmpge: opc = opc_if_icmplt; break;
case opc_if_icmplt: opc = opc_if_icmpge; break;
case opc_if_icmple: opc = opc_if_icmpgt; break;
}
value = next.value;
next.opc = opc_dead;
}
break;
case opc_tableswitch:
case opc_lookupswitch: {
SwitchData sw = (SwitchData)value;
sw.defaultLabel = sw.defaultLabel.getDestination();
for (Enumeration e = sw.tab.keys() ; e.hasMoreElements() ; ) {
Integer k = (Integer)e.nextElement();
Label lbl = (Label)sw.tab.get(k);
sw.tab.put(k, lbl.getDestination());
}
long ratio = env.optimize() ? 2 : 4;
long range = (long)sw.maxValue - (long)sw.minValue;
long size = sw.tab.size();
opc = ((range - size) <= (size / ratio)) ? opc_tableswitch : opc_lookupswitch;
break;
}
}
}
/**
* Collect constants into the constant table
*/
void collect(ConstantPool tab) {
switch (opc) {
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
if (value instanceof LocalVariable) {
FieldDefinition field = ((LocalVariable)value).field;
tab.put(field.getName().toString());
tab.put(field.getType().getTypeSignature());
}
return;
case opc_new:
case opc_putfield:
case opc_putstatic:
case opc_getfield:
case opc_getstatic:
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokestatic:
case opc_invokeinterface:
case opc_instanceof:
case opc_checkcast:
tab.put(value);
return;
case opc_anewarray:
tab.put(value);
return;
case opc_multianewarray:
tab.put(((ArrayData)value).type);
return;
case opc_ldc:
case opc_ldc_w:
if (value instanceof Integer) {
int v = ((Integer)value).intValue();
if ((v >= -1) && (v <= 5)) {
opc = opc_iconst_0 + v;
return;
} else if ((v > -(1 << 7)) && (v < (1 << 7))) {
opc = opc_bipush;
return;
} else if ((v > -(1 << 15)) && (v < (1 << 15))) {
opc = opc_sipush;
return;
}
} else if (value instanceof Float) {
float v = ((Float)value).floatValue();
if (v == 0) {
if (Float.floatToIntBits(v) == 0) {
opc = opc_fconst_0;
return;
}
} else if (v == 1) {
opc = opc_fconst_1;
return;
} else if (v == 2) {
opc = opc_fconst_2;
return;
}
}
tab.put(value);
return;
case opc_ldc2_w:
if (value instanceof Long) {
long v = ((Long)value).longValue();
if (v == 0) {
opc = opc_lconst_0;
return;
} else if (v == 1) {
opc = opc_lconst_1;
return;
}
} else if (value instanceof Double) {
double v = ((Double)value).doubleValue();
if (v == 0) {
if (Double.doubleToLongBits(v) == 0) {
opc = opc_dconst_0;
return;
}
} else if (v == 1) {
opc = opc_dconst_1;
return;
}
}
tab.put(value);
return;
case opc_try:
for (Enumeration e = ((TryData)value).catches.elements() ; e.hasMoreElements() ;) {
CatchData cd = (CatchData)e.nextElement();
if (cd.getType() != null) {
tab.put(cd.getType());
}
}
return;
case opc_nop:
if ((value != null) && (value instanceof ClassDeclaration))
tab.put(value);
return;
}
}
/**
* Balance the stack
*/
int balance() {
switch (opc) {
case opc_dead:
case opc_label:
case opc_iinc:
case opc_arraylength:
case opc_laload:
case opc_daload:
case opc_nop:
case opc_ineg:
case opc_fneg:
case opc_lneg:
case opc_dneg:
case opc_i2f:
case opc_f2i:
case opc_l2d:
case opc_d2l:
case opc_int2byte:
case opc_int2char:
case opc_int2short:
case opc_jsr:
case opc_goto:
case opc_jsr_w:
case opc_goto_w:
case opc_return:
case opc_ret:
case opc_instanceof:
case opc_checkcast:
case opc_newarray:
case opc_anewarray:
case opc_try:
case opc_swap:
return 0;
case opc_ldc:
case opc_ldc_w:
case opc_bipush:
case opc_sipush:
case opc_aconst_null:
case opc_iconst_m1:
case opc_iconst_0:
case opc_iconst_1:
case opc_iconst_2:
case opc_iconst_3:
case opc_iconst_4:
case opc_iconst_5:
case opc_fconst_0:
case opc_fconst_1:
case opc_fconst_2:
case opc_iload:
case opc_fload:
case opc_aload:
case opc_dup:
case opc_dup_x1:
case opc_dup_x2:
case opc_i2l:
case opc_i2d:
case opc_f2l:
case opc_f2d:
case opc_new:
return 1;
case opc_lload:
case opc_dload:
case opc_dup2:
case opc_dup2_x1:
case opc_dup2_x2:
case opc_ldc2_w:
case opc_lconst_0:
case opc_lconst_1:
case opc_dconst_0:
case opc_dconst_1:
return 2;
case opc_istore:
case opc_fstore:
case opc_astore:
case opc_iaload:
case opc_faload:
case opc_aaload:
case opc_baload:
case opc_caload:
case opc_saload:
case opc_pop:
case opc_iadd:
case opc_fadd:
case opc_isub:
case opc_fsub:
case opc_imul:
case opc_fmul:
case opc_idiv:
case opc_fdiv:
case opc_irem:
case opc_frem:
case opc_ishl:
case opc_ishr:
case opc_iushr:
case opc_lshl:
case opc_lshr:
case opc_lushr:
case opc_iand:
case opc_ior:
case opc_ixor:
case opc_l2i:
case opc_l2f:
case opc_d2i:
case opc_d2f:
case opc_ifeq:
case opc_ifne:
case opc_iflt:
case opc_ifle:
case opc_ifgt:
case opc_ifge:
case opc_ifnull:
case opc_ifnonnull:
case opc_fcmpl:
case opc_fcmpg:
case opc_ireturn:
case opc_freturn:
case opc_areturn:
case opc_tableswitch:
case opc_lookupswitch:
case opc_athrow:
case opc_monitorenter:
case opc_monitorexit:
return -1;
case opc_lstore:
case opc_dstore:
case opc_pop2:
case opc_ladd:
case opc_dadd:
case opc_lsub:
case opc_dsub:
case opc_lmul:
case opc_dmul:
case opc_ldiv:
case opc_ddiv:
case opc_lrem:
case opc_drem:
case opc_land:
case opc_lor:
case opc_lxor:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmplt:
case opc_if_icmple:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_lreturn:
case opc_dreturn:
return -2;
case opc_iastore:
case opc_fastore:
case opc_aastore:
case opc_bastore:
case opc_castore:
case opc_sastore:
case opc_lcmp:
case opc_dcmpl:
case opc_dcmpg:
return -3;
case opc_lastore:
case opc_dastore:
return -4;
case opc_multianewarray:
return 1 - ((ArrayData)value).nargs;
case opc_getfield:
return ((FieldDefinition)value).getType().stackSize() - 1;
case opc_putfield:
return -1 - ((FieldDefinition)value).getType().stackSize();
case opc_getstatic:
return ((FieldDefinition)value).getType().stackSize();
case opc_putstatic:
return -((FieldDefinition)value).getType().stackSize();
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokeinterface:
return ((FieldDefinition)value).getType().getReturnType().stackSize() -
(((FieldDefinition)value).getType().stackSize() + 1);
case opc_invokestatic:
return ((FieldDefinition)value).getType().getReturnType().stackSize() -
(((FieldDefinition)value).getType().stackSize());
}
throw new CompilerError("invalid opcode: " + toString());
}
/**
* Return the size of the instruction
*/
int size(ConstantPool tab) {
switch (opc) {
case opc_try:
case opc_label:
case opc_dead:
return 0;
case opc_bipush:
case opc_newarray:
return 2;
case opc_sipush:
case opc_goto:
case opc_jsr:
case opc_ifeq:
case opc_ifne:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_ifnull:
case opc_ifnonnull:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
return 3;
case opc_ldc:
case opc_ldc_w:
if (tab.index(value) < 256) {
opc = opc_ldc;
return 2;
} else {
opc = opc_ldc_w;
return 3;
}
case opc_iload:
case opc_lload:
case opc_fload:
case opc_dload:
case opc_aload: {
int v = ((Number)value).intValue();
if (v < 4) {
opc = opc_iload_0 + (opc - opc_iload) * 4 + v;
return 1;
} else if (v <= 255) {
return 2;
} else {
opc += 256; // indicate wide variant
return 4;
}
}
case opc_iinc: {
int register = ((int[])value)[0];
int increment = ((int[])value)[1];
if (register <= 255 && (((byte)increment) == increment)) {
return 3;
} else {
opc += 256; // indicate wide variant
return 6;
}
}
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore: {
int v = (value instanceof Number) ?
((Number)value).intValue() : ((LocalVariable)value).slot;
if (v < 4) {
opc = opc_istore_0 + (opc - opc_istore) * 4 + v;
return 1;
} else if (v <= 255) {
return 2;
} else {
opc += 256; // indicate wide variant
return 4;
}
}
case opc_ret: {
int v = ((Number)value).intValue();
if (v <= 255) {
return 2;
} else {
opc += 256; // indicate wide variant
return 4;
}
}
case opc_ldc2_w:
case opc_new:
case opc_putstatic:
case opc_getstatic:
case opc_putfield:
case opc_getfield:
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokestatic:
case opc_instanceof:
case opc_checkcast:
case opc_anewarray:
return 3;
case opc_multianewarray:
return 4;
case opc_invokeinterface:
case opc_goto_w:
case opc_jsr_w:
return 5;
case opc_tableswitch: {
SwitchData sw = (SwitchData)value;
int n = 1;
for(; ((pc + n) % 4) != 0 ; n++);
return n + 16 + (sw.maxValue - sw.minValue) * 4;
}
case opc_lookupswitch: {
SwitchData sw = (SwitchData)value;
int n = 1;
for(; ((pc + n) % 4) != 0 ; n++);
return n + 8 + sw.tab.size() * 8;
}
case opc_nop:
if ((value != null) && !(value instanceof Integer))
return 2;
else
return 1;
}
// most opcodes are only 1 byte long
return 1;
}
/**
* Generate code
*/
void write(DataOutputStream out, ConstantPool tab) throws IOException {
switch (opc) {
case opc_try:
case opc_label:
case opc_dead:
break;
case opc_bipush:
case opc_newarray:
out.writeByte(opc);
out.writeByte(((Number)value).intValue());
break;
case opc_iload:
case opc_lload:
case opc_fload:
case opc_dload:
case opc_aload:
case opc_ret:
out.writeByte(opc);
out.writeByte(((Number)value).intValue());
break;
case opc_iload + 256:
case opc_lload + 256:
case opc_fload + 256:
case opc_dload + 256:
case opc_aload + 256:
case opc_ret + 256:
out.writeByte(opc_wide);
out.writeByte(opc - 256);
out.writeShort(((Number)value).intValue());
break;
case opc_istore:
case opc_lstore:
case opc_fstore:
case opc_dstore:
case opc_astore:
out.writeByte(opc);
out.writeByte((value instanceof Number) ?
((Number)value).intValue() : ((LocalVariable)value).slot);
break;
case opc_istore + 256:
case opc_lstore + 256:
case opc_fstore + 256:
case opc_dstore + 256:
case opc_astore + 256:
out.writeByte(opc_wide);
out.writeByte(opc - 256);
out.writeShort((value instanceof Number) ?
((Number)value).intValue() : ((LocalVariable)value).slot);
break;
case opc_sipush:
out.writeByte(opc);
out.writeShort(((Number)value).intValue());
break;
case opc_ldc:
out.writeByte(opc);
out.writeByte(tab.index(value));
break;
case opc_ldc_w:
case opc_ldc2_w:
case opc_new:
case opc_putstatic:
case opc_getstatic:
case opc_putfield:
case opc_getfield:
case opc_invokevirtual:
case opc_invokenonvirtual:
case opc_invokestatic:
case opc_instanceof:
case opc_checkcast:
out.writeByte(opc);
out.writeShort(tab.index(value));
break;
case opc_iinc:
out.writeByte(opc);
out.writeByte(((int[])value)[0]); // register
out.writeByte(((int[])value)[1]); // increment
break;
case opc_iinc + 256:
out.writeByte(opc_wide);
out.writeByte(opc - 256);
out.writeShort(((int[])value)[0]); // register
out.writeShort(((int[])value)[1]); // increment
break;
case opc_anewarray:
out.writeByte(opc);
out.writeShort(tab.index(value));
break;
case opc_multianewarray:
out.writeByte(opc);
out.writeShort(tab.index(((ArrayData)value).type));
out.writeByte(((ArrayData)value).nargs);
break;
case opc_invokeinterface:
out.writeByte(opc);
out.writeShort(tab.index(value));
out.writeByte(((FieldDefinition)value).getType().stackSize() + 1);
out.writeByte(0);
break;
case opc_goto:
case opc_jsr:
case opc_ifeq:
case opc_ifne:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_ifnull:
case opc_ifnonnull:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
out.writeByte(opc);
out.writeShort(((Instruction)value).pc - pc);
break;
case opc_goto_w:
case opc_jsr_w:
out.writeByte(opc);
out.writeLong(((Instruction)value).pc - pc);
break;
case opc_tableswitch: {
SwitchData sw = (SwitchData)value;
out.writeByte(opc);
for(int n = 1 ; ((pc + n) % 4) != 0 ; n++) {
out.writeByte(0);
}
out.writeInt(sw.defaultLabel.pc - pc);
out.writeInt(sw.minValue);
out.writeInt(sw.maxValue);
for (int n = sw.minValue ; n <= sw.maxValue ; n++) {
Label lbl = (Label)sw.tab.get(new Integer(n));
out.writeInt(((lbl != null) ? lbl.pc : sw.defaultLabel.pc) - pc);
}
break;
}
case opc_lookupswitch: {
SwitchData sw = (SwitchData)value;
out.writeByte(opc);
int n = pc + 1;
for(; (n % 4) != 0 ; n++) {
out.writeByte(0);
}
out.writeInt(sw.defaultLabel.pc - pc);
out.writeInt(sw.tab.size());
for (Enumeration e = sw.tab.keys() ; e.hasMoreElements() ; ) {
Integer v = (Integer)e.nextElement();
out.writeInt(v.intValue());
out.writeInt(((Label)sw.tab.get(v)).pc - pc);
}
break;
}
case opc_nop:
if (value != null) {
if (value instanceof Integer)
out.writeByte(((Integer)value).intValue());
else
out.writeShort(tab.index(value));
return;
}
// fall through
default:
out.writeByte(opc);
break;
}
}
/**
* toString
*/
public String toString() {
String prefix = (where >> OFFSETBITS) + ":\t";
switch (opc) {
case opc_try:
return prefix + "try " + ((TryData)value).getEndLabel().hashCode();
case opc_dead:
return prefix + "dead";
case opc_iinc: {
int register = ((int[])value)[0];
int increment = ((int[])value)[1];
return prefix + opcNames[opc] + " " + register + ", " + increment;
}
default:
if (value != null) {
if (value instanceof Label) {
return prefix + opcNames[opc] + " " + value.toString();
} else if (value instanceof Instruction) {
return prefix + opcNames[opc] + " " + value.hashCode();
} else if (value instanceof String) {
return prefix + opcNames[opc] + " \"" + value + "\"";
} else {
return prefix + opcNames[opc] + " " + value;
}
} else {
return prefix + opcNames[opc];
}
}
}
}