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 javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
public class InstructionWriteUtil { public class InstructionWriteUtil {
@ -58,6 +59,7 @@ public class InstructionWriteUtil {
private List<Instruction> instructions; private List<Instruction> instructions;
private ArrayList<Integer> codeOffsetShifts; private ArrayList<Integer> codeOffsetShifts;
private HashMap<Integer,Format> offsetToNewInstructionMap;
private int codeUnitCount; private int codeUnitCount;
private int outParamCount; private int outParamCount;
@ -148,7 +150,11 @@ public class InstructionWriteUtil {
if (codeOffsetShifts == null) { if (codeOffsetShifts == null) {
codeOffsetShifts = new ArrayList<Integer>(); codeOffsetShifts = new ArrayList<Integer>();
} }
if (offsetToNewInstructionMap == null) {
offsetToNewInstructionMap = new HashMap<Integer,Format>();
}
codeOffsetShifts.add(currentCodeOffset+instruction.getCodeUnits()); codeOffsetShifts.add(currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, instruction.getOpcode().format);
} }
} }
currentCodeOffset += instruction.getCodeUnits(); 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, // 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 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 // 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; currentCodeOffset = 0;
shiftsInserted = false;
for (Instruction instruction: methodImplementation.getInstructions()) { 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 targetOffset = ((Instruction10t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset); int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); 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 // 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 // this will cause subsequent code offsets to be shifted by 2 code units
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits()); codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
} else {
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format20t);
} }
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits()); 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 targetOffset = ((Instruction20t)instruction).getCodeOffset();
int codeOffsetDelta = codeOffsetShift(currentCodeOffset); int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
int newTargetOffset = targetOffsetShift(currentCodeOffset, targetOffset); int newTargetOffset = targetOffsetShift(currentCodeOffset, targetOffset);
if ((short)newTargetOffset != newTargetOffset) { if ((short)newTargetOffset != newTargetOffset) {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits()); codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits());
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t);
shiftsInserted = true;
} }
} else if (instruction.getOpcode().format.equals(Format.ArrayPayload) } else if (instruction.getOpcode().format.equals(Format.ArrayPayload)
|| instruction.getOpcode().format.equals(Format.SparseSwitchPayload) || instruction.getOpcode().format.equals(Format.SparseSwitchPayload)
|| instruction.getOpcode().format.equals(Format.PackedSwitchPayload)) { || instruction.getOpcode().format.equals(Format.PackedSwitchPayload)) {
int codeOffsetDelta = codeOffsetShift(currentCodeOffset); int codeOffsetDelta = codeOffsetShift(currentCodeOffset);
if ((currentCodeOffset+codeOffsetDelta)%2 != 0) { if ((currentCodeOffset+codeOffsetDelta)%2 != 0) {
if (codeOffsetShifts.contains(currentCodeOffset)) {
codeOffsetShifts.remove(codeOffsetDelta);
offsetToNewInstructionMap.remove(currentCodeOffset);
} else {
codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset); codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset);
offsetToNewInstructionMap.put(currentCodeOffset, Format.Format10x);
shiftsInserted = true;
}
} }
} }
currentCodeOffset += instruction.getCodeUnits(); currentCodeOffset += instruction.getCodeUnits();
} }
} while (shiftsInserted);
codeUnitCount += codeOffsetShifts.size(); codeUnitCount += codeOffsetShifts.size();
} }
@ -212,30 +235,28 @@ public class InstructionWriteUtil {
Instruction10t instr = (Instruction10t)instruction; Instruction10t instr = (Instruction10t)instruction;
int targetOffset = instr.getCodeOffset(); int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) { Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if ((byte)newTargetOffset != newTargetOffset) { if (newInstructionFormat != null) {
if ((short)newTargetOffset != newTargetOffset) { if (newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset); modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else { } else if (newInstructionFormat.equals(Format.Format20t)) {
modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset); modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset);
} }
} else { } else if (newTargetOffset != targetOffset) {
modifiedInstruction = new ImmutableInstruction10t(instr.getOpcode(), newTargetOffset); modifiedInstruction = new ImmutableInstruction10t(instr.getOpcode(), newTargetOffset);
} }
}
break; break;
} }
case Format20t: { case Format20t: {
Instruction20t instr = (Instruction20t)instruction; Instruction20t instr = (Instruction20t)instruction;
int targetOffset = instr.getCodeOffset(); int targetOffset = instr.getCodeOffset();
int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset);
if (newTargetOffset != targetOffset) { Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset);
if ((short)newTargetOffset != newTargetOffset) { if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format30t)) {
modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset); modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset);
} else { } else if (newTargetOffset != targetOffset) {
modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset); modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset);
} }
}
break; break;
} }
case Format21c: { case Format21c: {
@ -321,7 +342,8 @@ public class InstructionWriteUtil {
} }
private void alignPayload(int codeOffset) { 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)); instructions.add(new ImmutableInstruction10x(Opcode.NOP));
} }
} }