--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java 2016-12-09 00:55:52.537205413 -0800 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.nodes.java; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.graalvm.compiler.core.common.type.AbstractPointerStamp; +import org.graalvm.compiler.core.common.type.ObjectStamp; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.spi.Simplifiable; +import org.graalvm.compiler.graph.spi.SimplifierTool; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.extended.LoadHubNode; +import org.graalvm.compiler.nodes.extended.SwitchNode; +import org.graalvm.compiler.nodes.spi.LIRLowerable; +import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; +import org.graalvm.compiler.nodes.util.GraphUtil; + +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * The {@code TypeSwitchNode} performs a lookup based on the type of the input value. The type + * comparison is an exact type comparison, not an instanceof. + */ +@NodeInfo +public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Simplifiable { + + public static final NodeClass TYPE = NodeClass.create(TypeSwitchNode.class); + protected final ResolvedJavaType[] keys; + protected final Constant[] hubs; + + public TypeSwitchNode(ValueNode value, AbstractBeginNode[] successors, ResolvedJavaType[] keys, double[] keyProbabilities, int[] keySuccessors, ConstantReflectionProvider constantReflection) { + super(TYPE, value, successors, keySuccessors, keyProbabilities); + assert successors.length <= keys.length + 1; + assert keySuccessors.length == keyProbabilities.length; + this.keys = keys; + assert value.stamp() instanceof AbstractPointerStamp; + assert assertKeys(); + + hubs = new Constant[keys.length]; + for (int i = 0; i < hubs.length; i++) { + hubs[i] = constantReflection.asObjectHub(keys[i]); + } + } + + /** + * Don't allow duplicate keys. + */ + private boolean assertKeys() { + for (int i = 0; i < keys.length; i++) { + for (int j = 0; j < keys.length; j++) { + if (i == j) { + continue; + } + assert !keys[i].equals(keys[j]); + } + } + return true; + } + + @Override + public boolean isSorted() { + return false; + } + + @Override + public int keyCount() { + return keys.length; + } + + @Override + public Constant keyAt(int index) { + return hubs[index]; + } + + @Override + public boolean equalKeys(SwitchNode switchNode) { + if (!(switchNode instanceof TypeSwitchNode)) { + return false; + } + TypeSwitchNode other = (TypeSwitchNode) switchNode; + return Arrays.equals(keys, other.keys); + } + + public ResolvedJavaType typeAt(int index) { + return keys[index]; + } + + @Override + public void generate(NodeLIRBuilderTool gen) { + gen.emitSwitch(this); + } + + @Override + public void simplify(SimplifierTool tool) { + if (value() instanceof ConstantNode) { + Constant constant = value().asConstant(); + + int survivingEdge = keySuccessorIndex(keyCount()); + for (int i = 0; i < keyCount(); i++) { + Constant typeHub = keyAt(i); + Boolean equal = tool.getConstantReflection().constantEquals(constant, typeHub); + if (equal == null) { + /* We don't know if this key is a match or not, so we cannot simplify. */ + return; + } else if (equal.booleanValue()) { + survivingEdge = keySuccessorIndex(i); + } + } + killOtherSuccessors(tool, survivingEdge); + } + if (value() instanceof LoadHubNode && ((LoadHubNode) value()).getValue().stamp() instanceof ObjectStamp) { + ObjectStamp objectStamp = (ObjectStamp) ((LoadHubNode) value()).getValue().stamp(); + if (objectStamp.type() != null) { + int validKeys = 0; + for (int i = 0; i < keyCount(); i++) { + if (objectStamp.type().isAssignableFrom(keys[i])) { + validKeys++; + } + } + if (validKeys == 0) { + tool.addToWorkList(defaultSuccessor()); + graph().removeSplitPropagate(this, defaultSuccessor()); + } else if (validKeys != keys.length) { + ArrayList newSuccessors = new ArrayList<>(blockSuccessorCount()); + ResolvedJavaType[] newKeys = new ResolvedJavaType[validKeys]; + int[] newKeySuccessors = new int[validKeys + 1]; + double[] newKeyProbabilities = new double[validKeys + 1]; + double totalProbability = 0; + int current = 0; + for (int i = 0; i < keyCount() + 1; i++) { + if (i == keyCount() || objectStamp.type().isAssignableFrom(keys[i])) { + int index = newSuccessors.indexOf(keySuccessor(i)); + if (index == -1) { + index = newSuccessors.size(); + newSuccessors.add(keySuccessor(i)); + } + newKeySuccessors[current] = index; + if (i < keyCount()) { + newKeys[current] = keys[i]; + } + newKeyProbabilities[current] = keyProbability(i); + totalProbability += keyProbability(i); + current++; + } + } + if (totalProbability > 0) { + for (int i = 0; i < current; i++) { + newKeyProbabilities[i] /= totalProbability; + } + } else { + for (int i = 0; i < current; i++) { + newKeyProbabilities[i] = 1.0 / current; + } + } + + for (int i = 0; i < blockSuccessorCount(); i++) { + AbstractBeginNode successor = blockSuccessor(i); + if (!newSuccessors.contains(successor)) { + tool.deleteBranch(successor); + } + setBlockSuccessor(i, null); + } + + AbstractBeginNode[] successorsArray = newSuccessors.toArray(new AbstractBeginNode[newSuccessors.size()]); + TypeSwitchNode newSwitch = graph().add(new TypeSwitchNode(value(), successorsArray, newKeys, newKeyProbabilities, newKeySuccessors, tool.getConstantReflection())); + ((FixedWithNextNode) predecessor()).setNext(newSwitch); + GraphUtil.killWithUnusedFloatingInputs(this); + } + } + } + } +}