Dealing with case when boxing value is being stored in a variable
This commit is contained in:
committed by
Alexander Udalov
parent
b2eaac4468
commit
ee87b11723
+54
-13
@@ -28,27 +28,33 @@ import java.util.Set;
|
||||
|
||||
public class BoxedBasicValue extends BasicValue {
|
||||
private final Set<AbstractInsnNode> associatedInsns = new HashSet<AbstractInsnNode>();
|
||||
private final AbstractInsnNode boxingInsn;
|
||||
private final Set<Integer> associatedVariables = new HashSet<Integer>();
|
||||
private final Set<BoxedBasicValue> mergedWith = new HashSet<BoxedBasicValue>();
|
||||
private final Type primitiveType;
|
||||
private boolean wasUnboxed = false;
|
||||
private boolean isSafeToRemove = true;
|
||||
|
||||
public BoxedBasicValue(Type primitiveType, AbstractInsnNode insnNode) {
|
||||
super(AsmUtil.boxType(primitiveType));
|
||||
this.primitiveType = primitiveType;
|
||||
public BoxedBasicValue(Type boxedType, AbstractInsnNode insnNode) {
|
||||
super(boxedType);
|
||||
this.primitiveType = AsmUtil.unboxType(boxedType);
|
||||
associatedInsns.add(insnNode);
|
||||
boxingInsn = insnNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
return this == o;
|
||||
}
|
||||
|
||||
public boolean typeEquals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
BoxedBasicValue that = (BoxedBasicValue) o;
|
||||
|
||||
if (!getType().equals(that.getType())) return false;
|
||||
return getType().equals(that.getType());
|
||||
}
|
||||
|
||||
return (boxingInsn == ((BoxedBasicValue) o).boxingInsn);
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(this);
|
||||
}
|
||||
|
||||
public List<AbstractInsnNode> getAssociatedInsns() {
|
||||
@@ -59,15 +65,50 @@ public class BoxedBasicValue extends BasicValue {
|
||||
associatedInsns.add(insnNode);
|
||||
}
|
||||
|
||||
public void addVariableIndex(int index) {
|
||||
associatedVariables.add(index);
|
||||
}
|
||||
|
||||
public List<Integer> getVariablesIndexes() {
|
||||
return new ArrayList<Integer>(associatedVariables);
|
||||
}
|
||||
|
||||
public Type getPrimitiveType() {
|
||||
return primitiveType;
|
||||
}
|
||||
|
||||
public boolean wasUnboxed() {
|
||||
return wasUnboxed;
|
||||
public void mergeWith(BoxedBasicValue value) {
|
||||
if (this == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value.isSafeToRemove) {
|
||||
propagateRemovingAsUnsafe();
|
||||
}
|
||||
|
||||
if (!isSafeToRemove) {
|
||||
value.propagateRemovingAsUnsafe();
|
||||
}
|
||||
|
||||
mergedWith.add(value);
|
||||
value.mergedWith.add(this);
|
||||
}
|
||||
|
||||
public void setWasUnboxed(boolean wasUnboxed) {
|
||||
this.wasUnboxed = wasUnboxed;
|
||||
public void propagateRemovingAsUnsafe() {
|
||||
isSafeToRemove = false;
|
||||
|
||||
for (BoxedBasicValue value : mergedWith) {
|
||||
if (value.isSafeToRemove()) {
|
||||
value.propagateRemovingAsUnsafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSafeToRemove() {
|
||||
return isSafeToRemove;
|
||||
}
|
||||
|
||||
public boolean isDoubleSize() {
|
||||
return getPrimitiveType().getSize() == 2;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -94,7 +94,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
if (isBoxing(insn)) {
|
||||
int index = insnList.indexOf(insn);
|
||||
if (!boxingPlaces.containsKey(index)) {
|
||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(values.get(0).getType(), insn);
|
||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(value.getType(), insn);
|
||||
onNewBoxedValue(boxedBasicValue);
|
||||
boxingPlaces.put(index, boxedBasicValue);
|
||||
}
|
||||
@@ -105,9 +105,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
values.get(0) instanceof BoxedBasicValue &&
|
||||
value.getType().equals(((BoxedBasicValue) values.get(0)).getPrimitiveType())) {
|
||||
|
||||
BoxedBasicValue boxedBasicValue = (BoxedBasicValue) values.get(0);
|
||||
boxedBasicValue.addInsn(insn);
|
||||
boxedBasicValue.setWasUnboxed(true);
|
||||
onUnboxing((BoxedBasicValue) values.get(0), insn);
|
||||
}
|
||||
else {
|
||||
for (BasicValue arg : values) {
|
||||
@@ -120,12 +118,10 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
||||
if (isAllowedUnaryOperationWithBoxed(insn.getOpcode()) && value instanceof BoxedBasicValue) {
|
||||
((BoxedBasicValue) value).addInsn(insn);
|
||||
if (insn.getOpcode() == Opcodes.CHECKCAST && value instanceof BoxedBasicValue) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -135,7 +131,8 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
@Override
|
||||
@NotNull
|
||||
public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) {
|
||||
if (v instanceof BoxedBasicValue && v.equals(w)) {
|
||||
if (v instanceof BoxedBasicValue && ((BoxedBasicValue) v).typeEquals(w)) {
|
||||
((BoxedBasicValue) v).mergeWith((BoxedBasicValue) w);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -153,19 +150,19 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
}
|
||||
|
||||
|
||||
protected void onNewBoxedValue(BoxedBasicValue value) {
|
||||
protected void onNewBoxedValue(@NotNull BoxedBasicValue value) {
|
||||
|
||||
}
|
||||
|
||||
protected void onMethodCallWithBoxedValue(BoxedBasicValue value) {
|
||||
protected void onUnboxing(@NotNull BoxedBasicValue value, @NotNull AbstractInsnNode insn) {
|
||||
|
||||
}
|
||||
|
||||
protected void onMergeFail(BoxedBasicValue value) {
|
||||
protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) {
|
||||
|
||||
}
|
||||
|
||||
protected boolean isAllowedUnaryOperationWithBoxed(int opcode) {
|
||||
return opcode == Opcodes.CHECKCAST || opcode == Opcodes.IFNULL;
|
||||
protected void onMergeFail(@NotNull BoxedBasicValue value) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+48
-31
@@ -22,12 +22,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnList;
|
||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
@@ -36,7 +35,8 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
static {
|
||||
ImmutableSet.Builder<Integer> unsafeOperationsOpcodesBuilder = ImmutableSet.builder();
|
||||
unsafeOperationsOpcodesBuilder.add(
|
||||
Opcodes.AASTORE, Opcodes.PUTFIELD, Opcodes.PUTSTATIC, Opcodes.ARETURN
|
||||
Opcodes.AASTORE, Opcodes.PUTFIELD, Opcodes.PUTSTATIC, Opcodes.ARETURN,
|
||||
Opcodes.IFNONNULL, Opcodes.IFNULL, Opcodes.IF_ACMPEQ, Opcodes.IF_ACMPNE
|
||||
);
|
||||
|
||||
unsafeOperationsOpcodes = unsafeOperationsOpcodesBuilder.build();
|
||||
@@ -48,9 +48,24 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
super(insnList);
|
||||
}
|
||||
|
||||
private void checkIfUnsafeOperationWithBoxed(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
||||
if (value instanceof BoxedBasicValue && unsafeOperationsOpcodes.contains(insnNode.getOpcode())) {
|
||||
markAsDirty((BoxedBasicValue) value);
|
||||
private static void markValueAsDirty(@NotNull BoxedBasicValue value) {
|
||||
value.propagateRemovingAsUnsafe();
|
||||
}
|
||||
|
||||
private static void addAssociatedInsn(@NotNull BoxedBasicValue value, @NotNull AbstractInsnNode insn) {
|
||||
if (value.isSafeToRemove()) {
|
||||
value.addInsn(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processOperationWithBoxedValue(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
||||
if (value instanceof BoxedBasicValue) {
|
||||
if (unsafeOperationsOpcodes.contains(insnNode.getOpcode())) {
|
||||
markValueAsDirty((BoxedBasicValue) value);
|
||||
}
|
||||
else {
|
||||
addAssociatedInsn((BoxedBasicValue) value, insnNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +76,7 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
@NotNull BasicValue value2
|
||||
) throws AnalyzerException {
|
||||
|
||||
checkIfUnsafeOperationWithBoxed(value2, insn);
|
||||
processOperationWithBoxedValue(value2, insn);
|
||||
|
||||
return super.binaryOperation(insn, value1, value2);
|
||||
}
|
||||
@@ -72,57 +87,59 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
||||
) throws AnalyzerException {
|
||||
|
||||
checkIfUnsafeOperationWithBoxed(value3, insn);
|
||||
processOperationWithBoxedValue(value3, insn);
|
||||
|
||||
return super.ternaryOperation(insn, value1, value2, value3);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected boolean isAllowedUnaryOperationWithBoxed(int opcode) {
|
||||
return opcode == Opcodes.CHECKCAST;
|
||||
public BasicValue unaryOperation(
|
||||
@NotNull AbstractInsnNode insn, @NotNull BasicValue value
|
||||
) throws AnalyzerException {
|
||||
|
||||
processOperationWithBoxedValue(value, insn);
|
||||
|
||||
return super.unaryOperation(insn, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public BasicValue copyOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
||||
// currently we don't allow any copy operations with boxed values
|
||||
if (value instanceof BoxedBasicValue) {
|
||||
markAsDirty((BoxedBasicValue) value);
|
||||
return new BasicValue(value.getType());
|
||||
if (value instanceof BoxedBasicValue && insn.getOpcode() == Opcodes.ASTORE) {
|
||||
((BoxedBasicValue) value).addVariableIndex(((VarInsnNode) insn).var);
|
||||
}
|
||||
|
||||
processOperationWithBoxedValue(value, insn);
|
||||
|
||||
return super.copyOperation(insn, value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onNewBoxedValue(BoxedBasicValue value) {
|
||||
protected void onNewBoxedValue(@NotNull BoxedBasicValue value) {
|
||||
candidatesBoxedValues.add(value);
|
||||
}
|
||||
|
||||
private void markAsDirty(BoxedBasicValue value) {
|
||||
candidatesBoxedValues.remove(value);
|
||||
@Override
|
||||
protected void onUnboxing(
|
||||
@NotNull BoxedBasicValue value, @NotNull AbstractInsnNode insn
|
||||
) {
|
||||
addAssociatedInsn(value, insn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMethodCallWithBoxedValue(BoxedBasicValue value) {
|
||||
markAsDirty(value);
|
||||
protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) {
|
||||
markValueAsDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMergeFail(BoxedBasicValue v) {
|
||||
markAsDirty(v);
|
||||
protected void onMergeFail(@NotNull BoxedBasicValue v) {
|
||||
markValueAsDirty(v);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<BoxedBasicValue> getCandidatesBoxedValues() {
|
||||
List<BoxedBasicValue> result = new ArrayList<BoxedBasicValue>();
|
||||
|
||||
for (BoxedBasicValue value : candidatesBoxedValues) {
|
||||
if (value.wasUnboxed()) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
public Set<BoxedBasicValue> getCandidatesBoxedValues() {
|
||||
return candidatesBoxedValues;
|
||||
}
|
||||
}
|
||||
|
||||
+276
-12
@@ -16,14 +16,19 @@
|
||||
|
||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
public RedundantBoxingMethodTransformer(MethodTransformer methodTransformer) {
|
||||
@@ -32,21 +37,280 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
|
||||
@Override
|
||||
public void transform(@NotNull String owner, @NotNull MethodNode node) {
|
||||
for (BoxedBasicValue value : analyze(owner, node)) {
|
||||
for (AbstractInsnNode insnNode : value.getAssociatedInsns()) {
|
||||
node.instructions.remove(insnNode);
|
||||
}
|
||||
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
||||
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
||||
|
||||
Frame<BasicValue>[] frames = runAnalyzer(analyzer, owner, node);
|
||||
Set<BoxedBasicValue> valuesToOptimize = filterSafeToRemoveValues(interpreter.getCandidatesBoxedValues());
|
||||
|
||||
if (valuesToOptimize.size() > 0) {
|
||||
findValuesClashingWithVariables(node, frames);
|
||||
|
||||
valuesToOptimize = filterSafeToRemoveValues(valuesToOptimize);
|
||||
|
||||
adaptLocalVariableTableForBoxedValues(node, frames);
|
||||
|
||||
int[] remapping = variablesRemapping(valuesToOptimize, node);
|
||||
applyVariablesRemapping(node, remapping);
|
||||
|
||||
adaptInstructionsForBoxedValues(node, frames, valuesToOptimize);
|
||||
}
|
||||
|
||||
super.transform(owner, node);
|
||||
}
|
||||
|
||||
private static List<BoxedBasicValue> analyze(@NotNull String owner, @NotNull MethodNode node) {
|
||||
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
||||
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
||||
@NotNull
|
||||
private static Set<BoxedBasicValue> filterSafeToRemoveValues(@NotNull Set<BoxedBasicValue> values) {
|
||||
return new HashSet<BoxedBasicValue>(Collections2.filter(values, new Predicate<BoxedBasicValue>() {
|
||||
@Override
|
||||
public boolean apply(BoxedBasicValue input) {
|
||||
return input.isSafeToRemove();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
runAnalyzer(analyzer, owner, node);
|
||||
private static void adaptLocalVariableTableForBoxedValues(@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames) {
|
||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||
if (Type.getType(localVariableNode.desc).getSort() != Type.OBJECT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return interpreter.getCandidatesBoxedValues();
|
||||
adaptLocalVariableTableEntryForBoxedValues(node, frames, localVariableNode);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptLocalVariableTableEntryForBoxedValues(
|
||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames, @NotNull LocalVariableNode localVariableNode
|
||||
) {
|
||||
InsnList insnList = node.instructions;
|
||||
int from = insnList.indexOf(localVariableNode.start) + 1;
|
||||
int to = insnList.indexOf(localVariableNode.end) - 1;
|
||||
|
||||
for (int i = from; i <= to; i++) {
|
||||
AbstractInsnNode insn = insnList.get(i);
|
||||
if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == localVariableNode.index) {
|
||||
if (frames[i] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BasicValue top = frames[i].getStack(frames[i].getStackSize() - 1);
|
||||
if (!(top instanceof BoxedBasicValue) || !((BoxedBasicValue) top).isSafeToRemove()) {
|
||||
return;
|
||||
}
|
||||
|
||||
localVariableNode.desc = ((BoxedBasicValue) top).getPrimitiveType().getDescriptor();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void findValuesClashingWithVariables(
|
||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||
) {
|
||||
|
||||
while (findValuesClashingWithVariablesIteration(node, frames)) {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean findValuesClashingWithVariablesIteration(
|
||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||
) {
|
||||
InsnList insnList = node.instructions;
|
||||
boolean needToRepeat = false;
|
||||
|
||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||
if (Type.getType(localVariableNode.desc).getSort() != Type.OBJECT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = localVariableNode.index;
|
||||
int from = insnList.indexOf(localVariableNode.start) + 1;
|
||||
int to = insnList.indexOf(localVariableNode.end) - 1;
|
||||
|
||||
if (isThereUnsafeStoreInstruction(insnList, frames, from, to, index)) {
|
||||
needToRepeat |= markAllBoxedValuesStoredAsUnsafeToRemove(insnList, frames, from, to, index);
|
||||
}
|
||||
}
|
||||
|
||||
return needToRepeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are unsafe ASTORE instructions, that put into var something but boxed values of the same type
|
||||
*
|
||||
* @param insnList
|
||||
* @param frames
|
||||
* @param from
|
||||
* @param to
|
||||
* @param varIndex
|
||||
* @return
|
||||
*/
|
||||
private static boolean isThereUnsafeStoreInstruction(
|
||||
@NotNull InsnList insnList,
|
||||
@NotNull Frame<BasicValue>[] frames,
|
||||
int from, int to, int varIndex
|
||||
) {
|
||||
Type usedAsType = null;
|
||||
|
||||
for (int i = from; i <= to; i++) {
|
||||
AbstractInsnNode insn = insnList.get(i);
|
||||
if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == varIndex) {
|
||||
if (frames[i] == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicValue top = frames[i].getStack(frames[i].getStackSize() - 1);
|
||||
if (!(top instanceof BoxedBasicValue) || !((BoxedBasicValue) top).isSafeToRemove()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (usedAsType == null) {
|
||||
usedAsType = ((BoxedBasicValue) top).getPrimitiveType();
|
||||
}
|
||||
|
||||
if (!usedAsType.equals(((BoxedBasicValue) top).getPrimitiveType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean markAllBoxedValuesStoredAsUnsafeToRemove(
|
||||
@NotNull InsnList insnList,
|
||||
@NotNull Frame<BasicValue>[] frames,
|
||||
int from, int to, int varIndex
|
||||
) {
|
||||
boolean wasChanges = false;
|
||||
|
||||
for (int i = from; i <= to; i++) {
|
||||
AbstractInsnNode insn = insnList.get(i);
|
||||
if (insn.getOpcode() == Opcodes.ASTORE && ((VarInsnNode) insn).var == varIndex) {
|
||||
if (frames[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicValue top = frames[i].getStack(frames[i].getStackSize() - 1);
|
||||
if (!(top instanceof BoxedBasicValue) || !((BoxedBasicValue) top).isSafeToRemove()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wasChanges |= true;
|
||||
((BoxedBasicValue) top).propagateRemovingAsUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
return wasChanges;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static int[] variablesRemapping(@NotNull Iterable<BoxedBasicValue> values, @NotNull MethodNode node) {
|
||||
Set<Integer> longTypesVars = new HashSet<Integer>();
|
||||
for (BoxedBasicValue value : values) {
|
||||
if (value.getPrimitiveType().getSize() == 2) {
|
||||
longTypesVars.addAll(value.getVariablesIndexes());
|
||||
}
|
||||
}
|
||||
|
||||
node.maxLocals += longTypesVars.size();
|
||||
int[] remapping = new int[node.maxLocals];
|
||||
for (int i = 0; i < remapping.length; i++) {
|
||||
remapping[i] = i;
|
||||
}
|
||||
|
||||
for (int longVarIndex : longTypesVars) {
|
||||
for (int i = longVarIndex + 1; i < remapping.length; i++) {
|
||||
remapping[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
return remapping;
|
||||
}
|
||||
|
||||
private static void applyVariablesRemapping(@NotNull MethodNode node, @NotNull int[] remapping) {
|
||||
for (AbstractInsnNode insn : node.instructions.toArray()) {
|
||||
if (insn instanceof VarInsnNode) {
|
||||
((VarInsnNode) insn).var = remapping[((VarInsnNode) insn).var];
|
||||
}
|
||||
if (insn instanceof IincInsnNode) {
|
||||
((IincInsnNode) insn).var = remapping[((IincInsnNode) insn).var];
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||
localVariableNode.index = remapping[localVariableNode.index];
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptInstructionsForBoxedValues(
|
||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames, @NotNull Set<BoxedBasicValue> values
|
||||
) {
|
||||
collectPopInstructionsForBoxedValues(node, frames);
|
||||
|
||||
for (BoxedBasicValue value : values) {
|
||||
adaptInstructionsForBoxedValue(node, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectPopInstructionsForBoxedValues(
|
||||
@NotNull MethodNode node, @NotNull Frame<BasicValue>[] frames
|
||||
) {
|
||||
for (int i = 0; i < node.instructions.size(); i++) {
|
||||
AbstractInsnNode insn = node.instructions.get(i);
|
||||
if (insn.getOpcode() != Opcodes.POP || frames[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicValue top = frames[i].getStack(frames[i].getStackSize() - 1);
|
||||
if (top instanceof BoxedBasicValue && ((BoxedBasicValue) top).isSafeToRemove()) {
|
||||
((BoxedBasicValue) top).addInsn(node.instructions.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedBasicValue value) {
|
||||
for (AbstractInsnNode insn : value.getAssociatedInsns()) {
|
||||
adaptInstruction(node, insn, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptInstruction(
|
||||
@NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value
|
||||
) {
|
||||
boolean isDoubleSize = value.isDoubleSize();
|
||||
|
||||
switch (insn.getOpcode()) {
|
||||
case Opcodes.POP:
|
||||
if (isDoubleSize) {
|
||||
node.instructions.set(
|
||||
insn,
|
||||
new InsnNode(Opcodes.POP2)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Opcodes.DUP:
|
||||
if (isDoubleSize) {
|
||||
node.instructions.set(
|
||||
insn,
|
||||
new InsnNode(Opcodes.DUP2)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case Opcodes.ASTORE:
|
||||
case Opcodes.ALOAD:
|
||||
int intVarOpcode = insn.getOpcode() == Opcodes.ASTORE ? Opcodes.ISTORE : Opcodes.ILOAD;
|
||||
node.instructions.set(
|
||||
insn,
|
||||
new VarInsnNode(
|
||||
value.getPrimitiveType().getOpcode(intVarOpcode),
|
||||
((VarInsnNode) insn).var
|
||||
)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-4
@@ -28,6 +28,10 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
|
||||
public class OptimizationBasicInterpreter extends BasicInterpreter {
|
||||
private static final BasicValue MIXED_VALUE = new BasicValue(Type.getObjectType("#"));
|
||||
private static final BasicValue BOOLEAN_VALUE = new BasicValue(Type.BOOLEAN_TYPE);
|
||||
private static final BasicValue CHAR_VALUE = new BasicValue(Type.CHAR_TYPE);
|
||||
private static final BasicValue BYTE_VALUE = new BasicValue(Type.BYTE_TYPE);
|
||||
private static final BasicValue SHORT_VALUE = new BasicValue(Type.SHORT_TYPE);
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -35,11 +39,23 @@ public class OptimizationBasicInterpreter extends BasicInterpreter {
|
||||
if (type == null) {
|
||||
return super.newValue(null);
|
||||
}
|
||||
if (type.getSort() == Type.OBJECT) {
|
||||
return new BasicValue(type);
|
||||
}
|
||||
|
||||
return super.newValue(type);
|
||||
switch (type.getSort()) {
|
||||
case Type.VOID:
|
||||
return null;
|
||||
case Type.BOOLEAN:
|
||||
return BOOLEAN_VALUE;
|
||||
case Type.CHAR:
|
||||
return CHAR_VALUE;
|
||||
case Type.BYTE:
|
||||
return BYTE_VALUE;
|
||||
case Type.SHORT:
|
||||
return SHORT_VALUE;
|
||||
case Type.OBJECT:
|
||||
return new BasicValue(type);
|
||||
default:
|
||||
return super.newValue(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user