Redundant null check of boxed values detecting and cleaing out
This commit is contained in:
committed by
Alexander Udalov
parent
69436b64fc
commit
9aa5e85bde
+5
-1
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.jet.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
|
||||
import org.jetbrains.jet.codegen.optimization.boxing.RedundantNullCheckMethodTransformer;
|
||||
import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode;
|
||||
@@ -29,7 +30,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OptimizationMethodVisitor extends MethodVisitor {
|
||||
private final MethodTransformer methodTransformer = new RedundantBoxingMethodTransformer(null);
|
||||
private final MethodTransformer methodTransformer = new RedundantNullCheckMethodTransformer(
|
||||
new RedundantBoxingMethodTransformer(null)
|
||||
);
|
||||
|
||||
private final MethodNode methodNode;
|
||||
private final MethodVisitor delegate;
|
||||
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.codegen.optimization.common.OptimizationBasicInterpreter;
|
||||
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.MethodInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
|
||||
private final InsnList insnList;
|
||||
|
||||
BoxingInterpreter(InsnList insnList) {
|
||||
this.insnList = insnList;
|
||||
}
|
||||
|
||||
private static boolean isClassBox(@NotNull String owner) {
|
||||
|
||||
if (!owner.startsWith("java/lang/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String className = owner.substring("java/lang/".length());
|
||||
|
||||
return (className.equals("Integer") ||
|
||||
className.equals("Double") ||
|
||||
className.equals("Long") ||
|
||||
className.equals("Char") ||
|
||||
className.equals("Byte") ||
|
||||
className.equals("Boolean")) ||
|
||||
className.endsWith("Number");
|
||||
}
|
||||
|
||||
private static boolean isUnboxingMethod(@NotNull String name) {
|
||||
return name.endsWith("Value");
|
||||
}
|
||||
|
||||
private static boolean isUnboxing(@NotNull AbstractInsnNode insn) {
|
||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodInsnNode methodInsn = (MethodInsnNode) insn;
|
||||
|
||||
return isClassBox(methodInsn.owner) && isUnboxingMethod(methodInsn.name);
|
||||
}
|
||||
|
||||
private static boolean isBoxing(@NotNull AbstractInsnNode insn) {
|
||||
if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
|
||||
|
||||
return isClassBox(methodInsnNode.owner) && methodInsnNode.name.equals("valueOf");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException {
|
||||
BasicValue value = super.naryOperation(insn, values);
|
||||
|
||||
if (isBoxing(insn)) {
|
||||
int index = insnList.indexOf(insn);
|
||||
if (!boxingPlaces.containsKey(index)) {
|
||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(value.getType(), values.get(0).getType(), insn);
|
||||
onNewBoxedValue(boxedBasicValue);
|
||||
boxingPlaces.put(index, boxedBasicValue);
|
||||
}
|
||||
|
||||
return boxingPlaces.get(index);
|
||||
}
|
||||
|
||||
if (isUnboxing(insn) &&
|
||||
values.get(0) instanceof BoxedBasicValue &&
|
||||
value.getType().equals(((BoxedBasicValue) values.get(0)).getBoxedType())) {
|
||||
BoxedBasicValue boxedBasicValue = (BoxedBasicValue) values.get(0);
|
||||
boxedBasicValue.addInsn(insn);
|
||||
boxedBasicValue.setWasUnboxed(true);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void onNewBoxedValue(BoxedBasicValue value) {
|
||||
|
||||
}
|
||||
|
||||
protected boolean isAllowedUnaryOperationWithBoxed(int opcode) {
|
||||
return opcode == Opcodes.CHECKCAST || opcode == Opcodes.IFNULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
||||
if (isAllowedUnaryOperationWithBoxed(insn.getOpcode()) && value instanceof BoxedBasicValue) {
|
||||
((BoxedBasicValue) value).addInsn(insn);
|
||||
return value;
|
||||
}
|
||||
|
||||
return super.unaryOperation(insn, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) {
|
||||
if (v instanceof BoxedBasicValue && w instanceof BoxedBasicValue && v.equals(w)) {
|
||||
return v;
|
||||
}
|
||||
|
||||
return super.merge(v, w);
|
||||
}
|
||||
}
|
||||
+11
-81
@@ -17,108 +17,38 @@
|
||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.codegen.optimization.common.OptimizationBasicInterpreter;
|
||||
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.MethodInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class RedundantBoxingInterpreter extends OptimizationBasicInterpreter {
|
||||
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
|
||||
class RedundantBoxingInterpreter extends BoxingInterpreter {
|
||||
private final Set<BoxedBasicValue> candidatesBoxedValues = new HashSet<BoxedBasicValue>();
|
||||
private final InsnList insnList;
|
||||
|
||||
RedundantBoxingInterpreter(InsnList insnList) {
|
||||
this.insnList = insnList;
|
||||
}
|
||||
|
||||
private static boolean isClassBox(@NotNull String owner) {
|
||||
|
||||
if (!owner.startsWith("java/lang/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String className = owner.substring("java/lang/".length());
|
||||
|
||||
return (className.equals("Integer") ||
|
||||
className.equals("Double") ||
|
||||
className.equals("Long") ||
|
||||
className.equals("Char") ||
|
||||
className.equals("Byte") ||
|
||||
className.equals("Boolean")) ||
|
||||
className.endsWith("Number");
|
||||
}
|
||||
|
||||
private static boolean isUnboxingMethod(@NotNull String name) {
|
||||
return name.endsWith("Value");
|
||||
}
|
||||
|
||||
private static boolean isUnboxing(@NotNull AbstractInsnNode insn) {
|
||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodInsnNode methodInsn = (MethodInsnNode) insn;
|
||||
|
||||
return isClassBox(methodInsn.owner) && isUnboxingMethod(methodInsn.name);
|
||||
}
|
||||
|
||||
private static boolean isBoxing(@NotNull AbstractInsnNode insn) {
|
||||
if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
|
||||
|
||||
return isClassBox(methodInsnNode.owner) && methodInsnNode.name.equals("valueOf");
|
||||
super(insnList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException {
|
||||
BasicValue value = super.naryOperation(insn, values);
|
||||
|
||||
if (isBoxing(insn)) {
|
||||
int index = insnList.indexOf(insn);
|
||||
if (!boxingPlaces.containsKey(index)) {
|
||||
BoxedBasicValue boxedBasicValue = new BoxedBasicValue(value.getType(), values.get(0).getType(), insn);
|
||||
candidatesBoxedValues.add(boxedBasicValue);
|
||||
boxingPlaces.put(index, boxedBasicValue);
|
||||
}
|
||||
|
||||
return boxingPlaces.get(index);
|
||||
}
|
||||
|
||||
if (isUnboxing(insn) &&
|
||||
values.get(0) instanceof BoxedBasicValue &&
|
||||
value.getType().equals(((BoxedBasicValue) values.get(0)).getBoxedType())) {
|
||||
BoxedBasicValue boxedBasicValue = (BoxedBasicValue) values.get(0);
|
||||
boxedBasicValue.addInsn(insn);
|
||||
boxedBasicValue.setWasUnboxed(true);
|
||||
}
|
||||
|
||||
return value;
|
||||
protected void onNewBoxedValue(BoxedBasicValue value) {
|
||||
candidatesBoxedValues.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException {
|
||||
if (insn.getOpcode() == Opcodes.CHECKCAST && value instanceof BoxedBasicValue) {
|
||||
((BoxedBasicValue) value).addInsn(insn);
|
||||
return value;
|
||||
}
|
||||
|
||||
return super.unaryOperation(insn, value);
|
||||
protected boolean isAllowedUnaryOperationWithBoxed(int opcode) {
|
||||
return opcode == Opcodes.CHECKCAST;
|
||||
}
|
||||
|
||||
@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) {
|
||||
candidatesBoxedValues.remove(value);
|
||||
return new BasicValue(value.getType());
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.codegen.optimization.boxing;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.codegen.optimization.transformer.MethodTransformer;
|
||||
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.InsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RedundantNullCheckMethodTransformer extends MethodTransformer {
|
||||
public RedundantNullCheckMethodTransformer(MethodTransformer methodTransformer) {
|
||||
super(methodTransformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(@NotNull String owner, @NotNull MethodNode methodNode) {
|
||||
|
||||
while (removeRedundantNullCheck(owner, methodNode)) {
|
||||
|
||||
}
|
||||
|
||||
super.transform(owner, methodNode);
|
||||
}
|
||||
|
||||
private static boolean removeRedundantNullCheck(@NotNull String owner, @NotNull MethodNode methodNode) {
|
||||
InsnList insnList = methodNode.instructions;
|
||||
Frame<BasicValue>[] frames = runAnalyzer(
|
||||
new Analyzer<BasicValue>(
|
||||
new BoxingInterpreter(insnList)
|
||||
),
|
||||
owner, methodNode
|
||||
);
|
||||
|
||||
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
||||
|
||||
for (int i = 0; i < insnList.size(); i++) {
|
||||
Frame<BasicValue> frame = frames[i];
|
||||
AbstractInsnNode insn = insnList.get(i);
|
||||
|
||||
if (insn.getOpcode() == Opcodes.IFNULL &&
|
||||
frame.getStack(frame.getStackSize() - 1) instanceof BoxedBasicValue) {
|
||||
|
||||
insnsToOptimize.add(insn);
|
||||
}
|
||||
}
|
||||
|
||||
for (AbstractInsnNode insn : insnsToOptimize) {
|
||||
if (insn.getPrevious() != null && insn.getPrevious().getOpcode() == Opcodes.DUP) {
|
||||
insnList.remove(insn.getPrevious());
|
||||
}
|
||||
else {
|
||||
insnList.insertBefore(insn, new InsnNode(Opcodes.POP));
|
||||
}
|
||||
|
||||
insnList.remove(insn);
|
||||
}
|
||||
|
||||
return insnsToOptimize.size() > 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user