Iterative logic to handle embiggening of goto instructions, when subsequent code shifts are inserted. Layout logic creates a HashMap of predicted instruction replacements and nop insertions to be used later in writing method, which is more cromulent solution than simply replicating the logic, given its iterativeness.

This commit is contained in:
Izzat Bahadirov 2013-03-08 19:22:34 -05:00
parent 15ae0affc4
commit 5df16db108

View File

@ -50,6 +50,7 @@ import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class InstructionWriteUtil {
@ -58,6 +59,7 @@ public class InstructionWriteUtil {
private List<Instruction> instructions;
private ArrayList<Integer> codeOffsetShifts;
private HashMap<Integer,Format> offsetToNewInstructionMap;
private int codeUnitCount;
private int outParamCount;
@ -148,7 +150,11 @@ public class InstructionWriteUtil {
if (codeOffsetShifts == null) {
codeOffsetShifts = new ArrayList<Integer>();
}
if (offsetToNewInstructionMap == null) {
offsetToNewInstructionMap = new HashMap<Integer,Format>();
}
codeOffsetShifts.add(currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, instruction.getOpcode().format);
}
}
currentCodeOffset += instruction.getCodeUnits();
@ -162,9 +168,12 @@ public class InstructionWriteUtil {
// since code offset delta is equivalent to the position of instruction's code offset in the shift list,
// we use it as a position here
// we also check if we will have to insert nops to ensure 4-byte alignment for switch statements and packed arrays
boolean shiftsInserted;
do {
currentCodeOffset = 0;
shiftsInserted = false;
for (Instruction instruction: methodImplementation.getInstructions()) {
if (instruction.getOpcode().format.equals(Format.Format10t)) {
if (instruction.getOpcode().format.equals(Format.Format10t) && !offsetToNewInstructionMap.containsKey(currentCodeOffset)) {
int targetOffset = ((Instruction10t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
@ -174,26 +183,40 @@ public class InstructionWriteUtil {
// we insert extra 1 code unit shift referring to the same position
// this will cause subsequent code offsets to be shifted by 2 code units
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
} else {
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format20t);
}
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
shiftsInserted = true;
}
} else if (instruction.getOpcode().format.equals(Format.Format20t)) {
} else if (instruction.getOpcode().format.equals(Format.Format20t) && !offsetToNewInstructionMap.containsKey(currentCodeOffset)) {
int targetOffset = ((Instruction20t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffsetShift(currentCodeOffset, targetOffset);
if ((short)newTargetOffset != newTargetOffset) {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
shiftsInserted = true;
}
} else if (instruction.getOpcode().format.equals(Format.ArrayPayload)
|| instruction.getOpcode().format.equals(Format.SparseSwitchPayload)
|| instruction.getOpcode().format.equals(Format.PackedSwitchPayload)) {
int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
if ((currentCodeOffset+codeOffsetDelta)%2 != 0) {
if (codeOffsetShifts.contains(currentCodeOffset)) {
codeOffsetShifts.remove(codeOffsetDelta);
offsetToNewInstructionMap.remove(currentCodeOffset);
} else {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset);
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format10x);
shiftsInserted = true;
}
}
}
currentCodeOffset += instruction.getCodeUnits();
}
} while (shiftsInserted);
codeUnitCount += codeOffsetShifts.size();
}
@ -212,30 +235,28 @@ public class InstructionWriteUtil {
Instruction10t instr = (Instruction10t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
if ((byte)newTargetOffset != newTargetOffset) {
if ((short)newTargetOffset != newTargetOffset) {
Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if (newInstructionFormat != null) {
if (newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else {
} else if (newInstructionFormat.equals(Format.Format20t)) {
modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset);
}
} else {
} else if (newTargetOffset != targetOffset) {
modifiedInstruction = new ImmutableInstruction10t(instr.getOpcode(), newTargetOffset);
}
}
break;
}
case Format20t: {
Instruction20t instr = (Instruction20t)instruction;
int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) {
if ((short)newTargetOffset != newTargetOffset) {
Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else {
} else if (newTargetOffset != targetOffset) {
modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset);
}
}
break;
}
case Format21c: {
@ -321,7 +342,8 @@ public class InstructionWriteUtil {
}
private void alignPayload(int codeOffset) {
if (codeOffsetShifts.contains(codeOffset)) {
Format newInstructionFormat = offsetToNewInstructionMap.get(codeOffset);
if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format10x)) {
instructions.add(new ImmutableInstruction10x(Opcode.NOP));
}
}