From 9aa5e85bde116f6597c49d7f767179912c842caa Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 27 Jun 2014 14:39:59 +0400 Subject: [PATCH] Redundant null check of boxed values detecting and cleaing out --- .../OptimizationMethodVisitor.java | 6 +- .../boxing/BoxingInterpreter.java | 137 ++++++++++++++++++ .../boxing/RedundantBoxingInterpreter.java | 92 ++---------- .../RedundantNullCheckMethodTransformer.java | 83 +++++++++++ 4 files changed, 236 insertions(+), 82 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/OptimizationMethodVisitor.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/OptimizationMethodVisitor.java index 1495746f910..a751e1c801e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/OptimizationMethodVisitor.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/OptimizationMethodVisitor.java @@ -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; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java new file mode 100644 index 00000000000..a131b98b61d --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java @@ -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 boxingPlaces = new HashMap(); + 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 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); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java index cf60cb5f9bb..b6ab61905ff 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java @@ -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 boxingPlaces = new HashMap(); +class RedundantBoxingInterpreter extends BoxingInterpreter { private final Set candidatesBoxedValues = new HashSet(); - 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 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()); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java new file mode 100644 index 00000000000..f1ea8492eab --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantNullCheckMethodTransformer.java @@ -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[] frames = runAnalyzer( + new Analyzer( + new BoxingInterpreter(insnList) + ), + owner, methodNode + ); + + List insnsToOptimize = new ArrayList(); + + for (int i = 0; i < insnList.size(); i++) { + Frame 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; + } +}