1 /*
   2  * Copyright (c) 2009, 2015, 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 package org.graalvm.compiler.nodes.extended;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
  27 
  28 import java.util.Arrays;
  29 
  30 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
  31 import org.graalvm.compiler.core.common.type.StampFactory;
  32 import org.graalvm.compiler.debug.GraalError;
  33 import org.graalvm.compiler.graph.Node;
  34 import org.graalvm.compiler.graph.NodeClass;
  35 import org.graalvm.compiler.graph.NodeSuccessorList;
  36 import org.graalvm.compiler.graph.spi.SimplifierTool;
  37 import org.graalvm.compiler.nodeinfo.NodeInfo;
  38 import org.graalvm.compiler.nodes.AbstractBeginNode;
  39 import org.graalvm.compiler.nodes.ControlSplitNode;
  40 import org.graalvm.compiler.nodes.ValueNode;
  41 
  42 import jdk.vm.ci.meta.Constant;
  43 
  44 /**
  45  * The {@code SwitchNode} class is the base of both lookup and table switches.
  46  */
  47 // @formatter:off
  48 @NodeInfo(cycles = CYCLES_UNKNOWN,
  49           cyclesRationale = "We cannot estimate the runtime cost of a switch statement without knowing the number" +
  50                             "of case statements and the involved keys.",
  51           size = SIZE_UNKNOWN,
  52           sizeRationale = "We cannot estimate the code size of a switch statement without knowing the number" +
  53                           "of case statements.")
  54 // @formatter:on
  55 public abstract class SwitchNode extends ControlSplitNode {
  56 
  57     public static final NodeClass<SwitchNode> TYPE = NodeClass.create(SwitchNode.class);
  58     @Successor protected NodeSuccessorList<AbstractBeginNode> successors;
  59     @Input protected ValueNode value;
  60 
  61     // do not change the contents of these arrays:
  62     protected final double[] keyProbabilities;
  63     protected final int[] keySuccessors;
  64 
  65     /**
  66      * Constructs a new Switch.
  67      *
  68      * @param value the instruction that provides the value to be switched over
  69      * @param successors the list of successors of this switch
  70      */
  71     protected SwitchNode(NodeClass<? extends SwitchNode> c, ValueNode value, AbstractBeginNode[] successors, int[] keySuccessors, double[] keyProbabilities) {
  72         super(c, StampFactory.forVoid());
  73         assert value.stamp().getStackKind().isNumericInteger() || value.stamp() instanceof AbstractPointerStamp : value.stamp() + " key not supported by SwitchNode";
  74         assert keySuccessors.length == keyProbabilities.length;
  75         this.successors = new NodeSuccessorList<>(this, successors);
  76         this.value = value;
  77         this.keySuccessors = keySuccessors;
  78         this.keyProbabilities = keyProbabilities;
  79         assert assertProbabilities();
  80     }
  81 
  82     private boolean assertProbabilities() {
  83         double total = 0;
  84         for (double d : keyProbabilities) {
  85             total += d;
  86             assert d >= 0.0 : "Cannot have negative probabilities in switch node: " + d;
  87         }
  88         assert total > 0.999 && total < 1.001 : "Total " + total;
  89         return true;
  90     }
  91 
  92     @Override
  93     public double probability(AbstractBeginNode successor) {
  94         double sum = 0;
  95         for (int i = 0; i < keySuccessors.length; i++) {
  96             if (successors.get(keySuccessors[i]) == successor) {
  97                 sum += keyProbabilities[i];
  98             }
  99         }
 100         return sum;
 101     }
 102 
 103     public ValueNode value() {
 104         return value;
 105     }
 106 
 107     public abstract boolean isSorted();
 108 
 109     /**
 110      * The number of distinct keys in this switch.
 111      */
 112     public abstract int keyCount();
 113 
 114     /**
 115      * The key at the specified position, encoded in a Constant.
 116      */
 117     public abstract Constant keyAt(int i);
 118 
 119     public boolean structureEquals(SwitchNode switchNode) {
 120         return Arrays.equals(keySuccessors, switchNode.keySuccessors) && equalKeys(switchNode);
 121     }
 122 
 123     /**
 124      * Returns true if the switch has the same keys in the same order as this switch.
 125      */
 126     public abstract boolean equalKeys(SwitchNode switchNode);
 127 
 128     /**
 129      * Returns the index of the successor belonging to the key at the specified index.
 130      */
 131     public int keySuccessorIndex(int i) {
 132         return keySuccessors[i];
 133     }
 134 
 135     /**
 136      * Returns the successor for the key at the given index.
 137      */
 138     public AbstractBeginNode keySuccessor(int i) {
 139         return successors.get(keySuccessors[i]);
 140     }
 141 
 142     /**
 143      * Returns the probability of the key at the given index.
 144      */
 145     public double keyProbability(int i) {
 146         return keyProbabilities[i];
 147     }
 148 
 149     /**
 150      * Returns the index of the default (fall through) successor of this switch.
 151      */
 152     public int defaultSuccessorIndex() {
 153         return keySuccessors[keySuccessors.length - 1];
 154     }
 155 
 156     public AbstractBeginNode blockSuccessor(int i) {
 157         return successors.get(i);
 158     }
 159 
 160     public void setBlockSuccessor(int i, AbstractBeginNode s) {
 161         successors.set(i, s);
 162     }
 163 
 164     public int blockSuccessorCount() {
 165         return successors.count();
 166     }
 167 
 168     /**
 169      * Gets the successor corresponding to the default (fall through) case.
 170      *
 171      * @return the default successor
 172      */
 173     public AbstractBeginNode defaultSuccessor() {
 174         if (defaultSuccessorIndex() == -1) {
 175             throw new GraalError("unexpected");
 176         }
 177         return successors.get(defaultSuccessorIndex());
 178     }
 179 
 180     @Override
 181     public AbstractBeginNode getPrimarySuccessor() {
 182         return this.defaultSuccessor();
 183     }
 184 
 185     /**
 186      * Delete all other successors except for the one reached by {@code survivingEdge}.
 187      *
 188      * @param tool
 189      * @param survivingEdge index of the edge in the {@link SwitchNode#successors} list
 190      */
 191     protected void killOtherSuccessors(SimplifierTool tool, int survivingEdge) {
 192         for (Node successor : successors()) {
 193             /*
 194              * Deleting a branch change change the successors so reload the surviving successor each
 195              * time.
 196              */
 197             if (successor != blockSuccessor(survivingEdge)) {
 198                 tool.deleteBranch(successor);
 199             }
 200         }
 201         tool.addToWorkList(blockSuccessor(survivingEdge));
 202         graph().removeSplit(this, blockSuccessor(survivingEdge));
 203     }
 204 }