1 /* 2 * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.nodes.extended; 26 27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; 28 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64; 29 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; 30 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; 31 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; 32 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; 33 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; 34 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; 35 36 import java.util.Arrays; 37 38 import org.graalvm.compiler.core.common.type.AbstractPointerStamp; 39 import org.graalvm.compiler.core.common.type.Stamp; 40 import org.graalvm.compiler.core.common.type.StampFactory; 41 import org.graalvm.compiler.debug.GraalError; 42 import org.graalvm.compiler.graph.Node; 43 import org.graalvm.compiler.graph.NodeClass; 44 import org.graalvm.compiler.graph.NodeSuccessorList; 45 import org.graalvm.compiler.graph.spi.SimplifierTool; 46 import org.graalvm.compiler.nodeinfo.NodeCycles; 47 import org.graalvm.compiler.nodeinfo.NodeInfo; 48 import org.graalvm.compiler.nodeinfo.NodeSize; 49 import org.graalvm.compiler.nodes.AbstractBeginNode; 50 import org.graalvm.compiler.nodes.ControlSplitNode; 51 import org.graalvm.compiler.nodes.NodeView; 52 import org.graalvm.compiler.nodes.ValueNode; 53 54 import jdk.vm.ci.meta.Constant; 55 56 /** 57 * The {@code SwitchNode} class is the base of both lookup and table switches. 58 */ 59 // @formatter:off 60 @NodeInfo(cycles = CYCLES_UNKNOWN, 61 cyclesRationale = "We cannot estimate the runtime cost of a switch statement without knowing the number" + 62 "of case statements and the involved keys.", 63 size = SIZE_UNKNOWN, 64 sizeRationale = "We cannot estimate the code size of a switch statement without knowing the number" + 65 "of case statements.") 66 // @formatter:on 67 public abstract class SwitchNode extends ControlSplitNode { 68 69 public static final NodeClass<SwitchNode> TYPE = NodeClass.create(SwitchNode.class); 70 @Successor protected NodeSuccessorList<AbstractBeginNode> successors; 71 @Input protected ValueNode value; 72 73 // do not change the contents of these arrays: 74 protected final double[] keyProbabilities; 75 protected final int[] keySuccessors; 76 77 /** 78 * Constructs a new Switch. 79 * 80 * @param value the instruction that provides the value to be switched over 81 * @param successors the list of successors of this switch 82 */ 83 protected SwitchNode(NodeClass<? extends SwitchNode> c, ValueNode value, AbstractBeginNode[] successors, int[] keySuccessors, double[] keyProbabilities) { 84 super(c, StampFactory.forVoid()); 85 assert value.stamp(NodeView.DEFAULT).getStackKind().isNumericInteger() || value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp : value.stamp(NodeView.DEFAULT) + 86 " key not supported by SwitchNode"; 87 assert keySuccessors.length == keyProbabilities.length; 88 this.successors = new NodeSuccessorList<>(this, successors); 89 this.value = value; 90 this.keySuccessors = keySuccessors; 91 this.keyProbabilities = keyProbabilities; 92 assert assertProbabilities(); 93 } 94 95 private boolean assertProbabilities() { 96 double total = 0; 97 for (double d : keyProbabilities) { 98 total += d; 99 assert d >= 0.0 : "Cannot have negative probabilities in switch node: " + d; 100 } 101 assert total > 0.999 && total < 1.001 : "Total " + total; 102 return true; 103 } 104 105 @Override 106 public int getSuccessorCount() { 107 return successors.count(); 108 } 109 110 @Override 111 public double probability(AbstractBeginNode successor) { 112 double sum = 0; 113 for (int i = 0; i < keySuccessors.length; i++) { 114 if (successors.get(keySuccessors[i]) == successor) { 115 sum += keyProbabilities[i]; 116 } 117 } 118 return sum; 119 } 120 121 @Override 122 public boolean setProbability(AbstractBeginNode successor, double value) { 123 assert value <= 1.0 && value >= 0.0 : value; 124 assert assertProbabilities(); 125 126 double sum = 0; 127 double otherSum = 0; 128 for (int i = 0; i < keySuccessors.length; i++) { 129 if (successors.get(keySuccessors[i]) == successor) { 130 sum += keyProbabilities[i]; 131 } else { 132 otherSum += keyProbabilities[i]; 133 } 134 } 135 136 if (otherSum == 0 || sum == 0) { 137 // Cannot correctly adjust probabilities. 138 return false; 139 } 140 141 double delta = value - sum; 142 143 for (int i = 0; i < keySuccessors.length; i++) { 144 if (successors.get(keySuccessors[i]) == successor) { 145 keyProbabilities[i] = Math.max(0.0, keyProbabilities[i] + (delta * keyProbabilities[i]) / sum); 146 } else { 147 keyProbabilities[i] = Math.max(0.0, keyProbabilities[i] - (delta * keyProbabilities[i]) / otherSum); 148 } 149 } 150 assert assertProbabilities(); 151 return true; 152 } 153 154 public ValueNode value() { 155 return value; 156 } 157 158 public abstract boolean isSorted(); 159 160 /** 161 * The number of distinct keys in this switch. 162 */ 163 public abstract int keyCount(); 164 165 /** 166 * The key at the specified position, encoded in a Constant. 167 */ 168 public abstract Constant keyAt(int i); 169 170 public boolean structureEquals(SwitchNode switchNode) { 171 return Arrays.equals(keySuccessors, switchNode.keySuccessors) && equalKeys(switchNode); 172 } 173 174 /** 175 * Returns true if the switch has the same keys in the same order as this switch. 176 */ 177 public abstract boolean equalKeys(SwitchNode switchNode); 178 179 /** 180 * Returns the index of the successor belonging to the key at the specified index. 181 */ 182 public int keySuccessorIndex(int i) { 183 return keySuccessors[i]; 184 } 185 186 /** 187 * Returns the successor for the key at the given index. 188 */ 189 public AbstractBeginNode keySuccessor(int i) { 190 return successors.get(keySuccessors[i]); 191 } 192 193 /** 194 * Returns the probability of the key at the given index. 195 */ 196 public double keyProbability(int i) { 197 return keyProbabilities[i]; 198 } 199 200 /** 201 * Returns the probability of taking the default branch. 202 */ 203 public double defaultProbability() { 204 return keyProbabilities[keyProbabilities.length - 1]; 205 } 206 207 /** 208 * Returns the index of the default (fall through) successor of this switch. 209 */ 210 public int defaultSuccessorIndex() { 211 return keySuccessors[keySuccessors.length - 1]; 212 } 213 214 public AbstractBeginNode blockSuccessor(int i) { 215 return successors.get(i); 216 } 217 218 public void setBlockSuccessor(int i, AbstractBeginNode s) { 219 successors.set(i, s); 220 } 221 222 public int blockSuccessorCount() { 223 return successors.count(); 224 } 225 226 /** 227 * Gets the successor corresponding to the default (fall through) case. 228 * 229 * @return the default successor 230 */ 231 public AbstractBeginNode defaultSuccessor() { 232 if (defaultSuccessorIndex() == -1) { 233 throw new GraalError("unexpected"); 234 } 235 return successors.get(defaultSuccessorIndex()); 236 } 237 238 @Override 239 public AbstractBeginNode getPrimarySuccessor() { 240 return null; 241 } 242 243 /** 244 * Delete all other successors except for the one reached by {@code survivingEdge}. 245 * 246 * @param tool 247 * @param survivingEdge index of the edge in the {@link SwitchNode#successors} list 248 */ 249 protected void killOtherSuccessors(SimplifierTool tool, int survivingEdge) { 250 for (Node successor : successors()) { 251 /* 252 * Deleting a branch change change the successors so reload the surviving successor each 253 * time. 254 */ 255 if (successor != blockSuccessor(survivingEdge)) { 256 tool.deleteBranch(successor); 257 } 258 } 259 tool.addToWorkList(blockSuccessor(survivingEdge)); 260 graph().removeSplit(this, blockSuccessor(survivingEdge)); 261 } 262 263 public abstract Stamp getValueStampForSuccessor(AbstractBeginNode beginNode); 264 265 @Override 266 public NodeCycles estimatedNodeCycles() { 267 if (keyCount() == 1) { 268 // if 269 return CYCLES_2; 270 } else if (isSorted()) { 271 // good heuristic 272 return CYCLES_8; 273 } else { 274 // not so good 275 return CYCLES_64; 276 } 277 } 278 279 @Override 280 public NodeSize estimatedNodeSize() { 281 if (keyCount() == 1) { 282 // if 283 return SIZE_2; 284 } else if (isSorted()) { 285 // good heuristic 286 return SIZE_8; 287 } else { 288 // not so good 289 return SIZE_64; 290 } 291 } 292 293 }