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 {
|
public class BoxedBasicValue extends BasicValue {
|
||||||
private final Set<AbstractInsnNode> associatedInsns = new HashSet<AbstractInsnNode>();
|
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 final Type primitiveType;
|
||||||
private boolean wasUnboxed = false;
|
private boolean isSafeToRemove = true;
|
||||||
|
|
||||||
public BoxedBasicValue(Type primitiveType, AbstractInsnNode insnNode) {
|
public BoxedBasicValue(Type boxedType, AbstractInsnNode insnNode) {
|
||||||
super(AsmUtil.boxType(primitiveType));
|
super(boxedType);
|
||||||
this.primitiveType = primitiveType;
|
this.primitiveType = AsmUtil.unboxType(boxedType);
|
||||||
associatedInsns.add(insnNode);
|
associatedInsns.add(insnNode);
|
||||||
boxingInsn = insnNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
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;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
BoxedBasicValue that = (BoxedBasicValue) o;
|
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() {
|
public List<AbstractInsnNode> getAssociatedInsns() {
|
||||||
@@ -59,15 +65,50 @@ public class BoxedBasicValue extends BasicValue {
|
|||||||
associatedInsns.add(insnNode);
|
associatedInsns.add(insnNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addVariableIndex(int index) {
|
||||||
|
associatedVariables.add(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getVariablesIndexes() {
|
||||||
|
return new ArrayList<Integer>(associatedVariables);
|
||||||
|
}
|
||||||
|
|
||||||
public Type getPrimitiveType() {
|
public Type getPrimitiveType() {
|
||||||
return primitiveType;
|
return primitiveType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean wasUnboxed() {
|
public void mergeWith(BoxedBasicValue value) {
|
||||||
return wasUnboxed;
|
if (this == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.isSafeToRemove) {
|
||||||
|
propagateRemovingAsUnsafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSafeToRemove) {
|
||||||
|
value.propagateRemovingAsUnsafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedWith.add(value);
|
||||||
|
value.mergedWith.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWasUnboxed(boolean wasUnboxed) {
|
public void propagateRemovingAsUnsafe() {
|
||||||
this.wasUnboxed = wasUnboxed;
|
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)) {
|
if (isBoxing(insn)) {
|
||||||
int index = insnList.indexOf(insn);
|
int index = insnList.indexOf(insn);
|
||||||
if (!boxingPlaces.containsKey(index)) {
|
if (!boxingPlaces.containsKey(index)) {
|
||||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(values.get(0).getType(), insn);
|
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(value.getType(), insn);
|
||||||
onNewBoxedValue(boxedBasicValue);
|
onNewBoxedValue(boxedBasicValue);
|
||||||
boxingPlaces.put(index, boxedBasicValue);
|
boxingPlaces.put(index, boxedBasicValue);
|
||||||
}
|
}
|
||||||
@@ -105,9 +105,7 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
values.get(0) instanceof BoxedBasicValue &&
|
values.get(0) instanceof BoxedBasicValue &&
|
||||||
value.getType().equals(((BoxedBasicValue) values.get(0)).getPrimitiveType())) {
|
value.getType().equals(((BoxedBasicValue) values.get(0)).getPrimitiveType())) {
|
||||||
|
|
||||||
BoxedBasicValue boxedBasicValue = (BoxedBasicValue) values.get(0);
|
onUnboxing((BoxedBasicValue) values.get(0), insn);
|
||||||
boxedBasicValue.addInsn(insn);
|
|
||||||
boxedBasicValue.setWasUnboxed(true);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (BasicValue arg : values) {
|
for (BasicValue arg : values) {
|
||||||
@@ -120,12 +118,10 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
||||||
if (isAllowedUnaryOperationWithBoxed(insn.getOpcode()) && value instanceof BoxedBasicValue) {
|
if (insn.getOpcode() == Opcodes.CHECKCAST && value instanceof BoxedBasicValue) {
|
||||||
((BoxedBasicValue) value).addInsn(insn);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +131,8 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) {
|
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;
|
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) {
|
protected void onMergeFail(@NotNull BoxedBasicValue value) {
|
||||||
return opcode == Opcodes.CHECKCAST || opcode == Opcodes.IFNULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-31
@@ -22,12 +22,11 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.InsnList;
|
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.AnalyzerException;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||||
@@ -36,7 +35,8 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
static {
|
static {
|
||||||
ImmutableSet.Builder<Integer> unsafeOperationsOpcodesBuilder = ImmutableSet.builder();
|
ImmutableSet.Builder<Integer> unsafeOperationsOpcodesBuilder = ImmutableSet.builder();
|
||||||
unsafeOperationsOpcodesBuilder.add(
|
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();
|
unsafeOperationsOpcodes = unsafeOperationsOpcodesBuilder.build();
|
||||||
@@ -48,9 +48,24 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
super(insnList);
|
super(insnList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIfUnsafeOperationWithBoxed(@Nullable BasicValue value, @NotNull AbstractInsnNode insnNode) {
|
private static void markValueAsDirty(@NotNull BoxedBasicValue value) {
|
||||||
if (value instanceof BoxedBasicValue && unsafeOperationsOpcodes.contains(insnNode.getOpcode())) {
|
value.propagateRemovingAsUnsafe();
|
||||||
markAsDirty((BoxedBasicValue) value);
|
}
|
||||||
|
|
||||||
|
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
|
@NotNull BasicValue value2
|
||||||
) throws AnalyzerException {
|
) throws AnalyzerException {
|
||||||
|
|
||||||
checkIfUnsafeOperationWithBoxed(value2, insn);
|
processOperationWithBoxedValue(value2, insn);
|
||||||
|
|
||||||
return super.binaryOperation(insn, value1, value2);
|
return super.binaryOperation(insn, value1, value2);
|
||||||
}
|
}
|
||||||
@@ -72,57 +87,59 @@ class RedundantBoxingInterpreter extends BoxingInterpreter {
|
|||||||
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
@NotNull BasicValue value1, @NotNull BasicValue value2, @NotNull BasicValue value3
|
||||||
) throws AnalyzerException {
|
) throws AnalyzerException {
|
||||||
|
|
||||||
checkIfUnsafeOperationWithBoxed(value3, insn);
|
processOperationWithBoxedValue(value3, insn);
|
||||||
|
|
||||||
return super.ternaryOperation(insn, value1, value2, value3);
|
return super.ternaryOperation(insn, value1, value2, value3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected boolean isAllowedUnaryOperationWithBoxed(int opcode) {
|
public BasicValue unaryOperation(
|
||||||
return opcode == Opcodes.CHECKCAST;
|
@NotNull AbstractInsnNode insn, @NotNull BasicValue value
|
||||||
|
) throws AnalyzerException {
|
||||||
|
|
||||||
|
processOperationWithBoxedValue(value, insn);
|
||||||
|
|
||||||
|
return super.unaryOperation(insn, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public BasicValue copyOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
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 && insn.getOpcode() == Opcodes.ASTORE) {
|
||||||
if (value instanceof BoxedBasicValue) {
|
((BoxedBasicValue) value).addVariableIndex(((VarInsnNode) insn).var);
|
||||||
markAsDirty((BoxedBasicValue) value);
|
|
||||||
return new BasicValue(value.getType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOperationWithBoxedValue(value, insn);
|
||||||
|
|
||||||
return super.copyOperation(insn, value);
|
return super.copyOperation(insn, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNewBoxedValue(BoxedBasicValue value) {
|
protected void onNewBoxedValue(@NotNull BoxedBasicValue value) {
|
||||||
candidatesBoxedValues.add(value);
|
candidatesBoxedValues.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markAsDirty(BoxedBasicValue value) {
|
@Override
|
||||||
candidatesBoxedValues.remove(value);
|
protected void onUnboxing(
|
||||||
|
@NotNull BoxedBasicValue value, @NotNull AbstractInsnNode insn
|
||||||
|
) {
|
||||||
|
addAssociatedInsn(value, insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMethodCallWithBoxedValue(BoxedBasicValue value) {
|
protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) {
|
||||||
markAsDirty(value);
|
markValueAsDirty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMergeFail(BoxedBasicValue v) {
|
protected void onMergeFail(@NotNull BoxedBasicValue v) {
|
||||||
markAsDirty(v);
|
markValueAsDirty(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<BoxedBasicValue> getCandidatesBoxedValues() {
|
public Set<BoxedBasicValue> getCandidatesBoxedValues() {
|
||||||
List<BoxedBasicValue> result = new ArrayList<BoxedBasicValue>();
|
return candidatesBoxedValues;
|
||||||
|
|
||||||
for (BoxedBasicValue value : candidatesBoxedValues) {
|
|
||||||
if (value.wasUnboxed()) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+276
-12
@@ -16,14 +16,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
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.annotations.NotNull;
|
||||||
import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
|
import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
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.Analyzer;
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
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 class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||||
public RedundantBoxingMethodTransformer(MethodTransformer methodTransformer) {
|
public RedundantBoxingMethodTransformer(MethodTransformer methodTransformer) {
|
||||||
@@ -32,21 +37,280 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(@NotNull String owner, @NotNull MethodNode node) {
|
public void transform(@NotNull String owner, @NotNull MethodNode node) {
|
||||||
for (BoxedBasicValue value : analyze(owner, node)) {
|
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
||||||
for (AbstractInsnNode insnNode : value.getAssociatedInsns()) {
|
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
||||||
node.instructions.remove(insnNode);
|
|
||||||
}
|
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);
|
super.transform(owner, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<BoxedBasicValue> analyze(@NotNull String owner, @NotNull MethodNode node) {
|
@NotNull
|
||||||
RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions);
|
private static Set<BoxedBasicValue> filterSafeToRemoveValues(@NotNull Set<BoxedBasicValue> values) {
|
||||||
Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(interpreter);
|
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 {
|
public class OptimizationBasicInterpreter extends BasicInterpreter {
|
||||||
private static final BasicValue MIXED_VALUE = new BasicValue(Type.getObjectType("#"));
|
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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -35,11 +39,23 @@ public class OptimizationBasicInterpreter extends BasicInterpreter {
|
|||||||
if (type == null) {
|
if (type == null) {
|
||||||
return super.newValue(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
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user