Redundant null check of boxed values detecting and cleaing out

This commit is contained in:
Denis Zharkov
2014-06-27 14:39:59 +04:00
committed by Alexander Udalov
parent 69436b64fc
commit 9aa5e85bde
4 changed files with 236 additions and 82 deletions
@@ -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;
@@ -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);
}
}
@@ -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());
@@ -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;
}
}