1 /* 2 * Copyright (c) 2009, 2011, 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; 24 25 import static org.graalvm.compiler.nodeinfo.InputType.Association; 26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; 27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; 28 29 import java.util.List; 30 31 import org.graalvm.compiler.debug.Debug; 32 import org.graalvm.compiler.graph.IterableNodeType; 33 import org.graalvm.compiler.graph.Node; 34 import org.graalvm.compiler.graph.NodeClass; 35 import org.graalvm.compiler.graph.NodeInputList; 36 import org.graalvm.compiler.graph.iterators.NodeIterable; 37 import org.graalvm.compiler.graph.spi.Simplifiable; 38 import org.graalvm.compiler.graph.spi.SimplifierTool; 39 import org.graalvm.compiler.nodeinfo.NodeInfo; 40 import org.graalvm.compiler.nodes.spi.LIRLowerable; 41 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 42 import org.graalvm.compiler.nodes.util.GraphUtil; 43 44 /** 45 * Denotes the merging of multiple control-flow paths. 46 */ 47 @NodeInfo(allowedUsageTypes = Association, cycles = CYCLES_0, size = SIZE_0) 48 public abstract class AbstractMergeNode extends BeginStateSplitNode implements IterableNodeType, Simplifiable, LIRLowerable { 49 public static final NodeClass<AbstractMergeNode> TYPE = NodeClass.create(AbstractMergeNode.class); 50 51 protected AbstractMergeNode(NodeClass<? extends AbstractMergeNode> c) { 52 super(c); 53 } 54 55 @Input(Association) protected NodeInputList<EndNode> ends = new NodeInputList<>(this); 56 57 @Override 58 public void generate(NodeLIRBuilderTool gen) { 59 gen.visitMerge(this); 60 } 61 62 public int forwardEndIndex(EndNode end) { 63 return ends.indexOf(end); 64 } 65 66 public void addForwardEnd(EndNode end) { 67 ends.add(end); 68 } 69 70 public final int forwardEndCount() { 71 return ends.size(); 72 } 73 74 public final EndNode forwardEndAt(int index) { 75 return ends.get(index); 76 } 77 78 @Override 79 public NodeIterable<EndNode> cfgPredecessors() { 80 return ends; 81 } 82 83 /** 84 * Determines if a given node is a phi whose {@linkplain PhiNode#merge() merge} is this node. 85 * 86 * @param value the instruction to test 87 * @return {@code true} if {@code value} is a phi and its merge is {@code this} 88 */ 89 public boolean isPhiAtMerge(Node value) { 90 return value instanceof PhiNode && ((PhiNode) value).merge() == this; 91 } 92 93 /** 94 * Removes the given end from the merge, along with the entries corresponding to this end in the 95 * phis connected to the merge. 96 * 97 * @param pred the end to remove 98 */ 99 public void removeEnd(AbstractEndNode pred) { 100 int predIndex = phiPredecessorIndex(pred); 101 assert predIndex != -1; 102 deleteEnd(pred); 103 for (PhiNode phi : phis().snapshot()) { 104 if (phi.isDeleted()) { 105 continue; 106 } 107 ValueNode removedValue = phi.valueAt(predIndex); 108 phi.removeInput(predIndex); 109 if (removedValue != null && removedValue.isAlive() && removedValue.hasNoUsages() && GraphUtil.isFloatingNode(removedValue)) { 110 GraphUtil.killWithUnusedFloatingInputs(removedValue); 111 } 112 } 113 } 114 115 protected void deleteEnd(AbstractEndNode end) { 116 ends.remove(end); 117 } 118 119 public void clearEnds() { 120 ends.clear(); 121 } 122 123 public NodeInputList<EndNode> forwardEnds() { 124 return ends; 125 } 126 127 public int phiPredecessorCount() { 128 return forwardEndCount(); 129 } 130 131 public int phiPredecessorIndex(AbstractEndNode pred) { 132 return forwardEndIndex((EndNode) pred); 133 } 134 135 public AbstractEndNode phiPredecessorAt(int index) { 136 return forwardEndAt(index); 137 } 138 139 public NodeIterable<PhiNode> phis() { 140 return this.usages().filter(PhiNode.class).filter(this::isPhiAtMerge); 141 } 142 143 public NodeIterable<ValuePhiNode> valuePhis() { 144 return this.usages().filter(ValuePhiNode.class).filter(this::isPhiAtMerge); 145 } 146 147 @Override 148 public NodeIterable<Node> anchored() { 149 return super.anchored().filter(n -> !isPhiAtMerge(n)); 150 } 151 152 /** 153 * This simplify method can deal with a null value for tool, so that it can be used outside of 154 * canonicalization. 155 */ 156 @Override 157 public void simplify(SimplifierTool tool) { 158 FixedNode currentNext = next(); 159 if (currentNext instanceof AbstractEndNode) { 160 AbstractEndNode origLoopEnd = (AbstractEndNode) currentNext; 161 AbstractMergeNode merge = origLoopEnd.merge(); 162 if (merge instanceof LoopBeginNode && !(origLoopEnd instanceof LoopEndNode)) { 163 return; 164 } 165 // in order to move anchored values to the other merge we would need to check if the 166 // anchors are used by phis of the other merge 167 if (this.anchored().isNotEmpty()) { 168 return; 169 } 170 if (merge.stateAfter() == null && this.stateAfter() != null) { 171 // We hold a state, but the succeeding merge does not => do not combine. 172 return; 173 } 174 for (PhiNode phi : phis()) { 175 for (Node usage : phi.usages()) { 176 if (!(usage instanceof VirtualState) && !merge.isPhiAtMerge(usage)) { 177 return; 178 } 179 } 180 } 181 Debug.log("Split %s into ends for %s.", this, merge); 182 int numEnds = this.forwardEndCount(); 183 for (int i = 0; i < numEnds - 1; i++) { 184 AbstractEndNode end = forwardEndAt(numEnds - 1 - i); 185 if (tool != null) { 186 tool.addToWorkList(end); 187 } 188 AbstractEndNode newEnd; 189 if (merge instanceof LoopBeginNode) { 190 newEnd = graph().add(new LoopEndNode((LoopBeginNode) merge)); 191 } else { 192 EndNode tmpEnd = graph().add(new EndNode()); 193 merge.addForwardEnd(tmpEnd); 194 newEnd = tmpEnd; 195 } 196 for (PhiNode phi : merge.phis()) { 197 ValueNode v = phi.valueAt(origLoopEnd); 198 ValueNode newInput; 199 if (isPhiAtMerge(v)) { 200 PhiNode endPhi = (PhiNode) v; 201 newInput = endPhi.valueAt(end); 202 } else { 203 newInput = v; 204 } 205 phi.addInput(newInput); 206 } 207 this.removeEnd(end); 208 end.replaceAtPredecessor(newEnd); 209 end.safeDelete(); 210 if (tool != null) { 211 tool.addToWorkList(newEnd.predecessor()); 212 } 213 } 214 graph().reduceTrivialMerge(this); 215 } else if (currentNext instanceof ReturnNode) { 216 ReturnNode returnNode = (ReturnNode) currentNext; 217 if (anchored().isNotEmpty() || returnNode.getMemoryMap() != null) { 218 return; 219 } 220 List<PhiNode> phis = phis().snapshot(); 221 for (PhiNode phi : phis) { 222 for (Node usage : phi.usages()) { 223 if (usage != returnNode && !(usage instanceof FrameState)) { 224 return; 225 } 226 } 227 } 228 229 ValuePhiNode returnValuePhi = returnNode.result() == null || !isPhiAtMerge(returnNode.result()) ? null : (ValuePhiNode) returnNode.result(); 230 List<EndNode> endNodes = forwardEnds().snapshot(); 231 for (EndNode end : endNodes) { 232 ReturnNode newReturn = graph().add(new ReturnNode(returnValuePhi == null ? returnNode.result() : returnValuePhi.valueAt(end))); 233 if (tool != null) { 234 tool.addToWorkList(end.predecessor()); 235 } 236 end.replaceAtPredecessor(newReturn); 237 } 238 GraphUtil.killCFG(this); 239 for (EndNode end : endNodes) { 240 end.safeDelete(); 241 } 242 for (PhiNode phi : phis) { 243 if (tool.allUsagesAvailable() && phi.isAlive() && phi.hasNoUsages()) { 244 GraphUtil.killWithUnusedFloatingInputs(phi); 245 } 246 } 247 } 248 } 249 }