001/* 002 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.nodes; 024 025import static com.oracle.graal.graph.iterators.NodePredicates.*; 026 027import java.util.*; 028 029import com.oracle.graal.debug.*; 030 031import com.oracle.graal.graph.*; 032import com.oracle.graal.graph.iterators.*; 033import com.oracle.graal.graph.spi.*; 034import com.oracle.graal.nodeinfo.*; 035import com.oracle.graal.nodes.spi.*; 036import com.oracle.graal.nodes.util.*; 037 038/** 039 * Denotes the merging of multiple control-flow paths. 040 */ 041@NodeInfo(allowedUsageTypes = {InputType.Association}) 042public abstract class AbstractMergeNode extends BeginStateSplitNode implements IterableNodeType, LIRLowerable { 043 public static final NodeClass<AbstractMergeNode> TYPE = NodeClass.create(AbstractMergeNode.class); 044 045 protected AbstractMergeNode(NodeClass<? extends AbstractMergeNode> c) { 046 super(c); 047 } 048 049 @Input(InputType.Association) protected NodeInputList<EndNode> ends = new NodeInputList<>(this); 050 051 @Override 052 public void generate(NodeLIRBuilderTool gen) { 053 gen.visitMerge(this); 054 } 055 056 public int forwardEndIndex(EndNode end) { 057 return ends.indexOf(end); 058 } 059 060 public void addForwardEnd(EndNode end) { 061 ends.add(end); 062 } 063 064 public int forwardEndCount() { 065 return ends.size(); 066 } 067 068 public EndNode forwardEndAt(int index) { 069 return ends.get(index); 070 } 071 072 @Override 073 public NodeIterable<EndNode> cfgPredecessors() { 074 return ends; 075 } 076 077 /** 078 * Determines if a given node is a phi whose {@linkplain PhiNode#merge() merge} is this node. 079 * 080 * @param value the instruction to test 081 * @return {@code true} if {@code value} is a phi and its merge is {@code this} 082 */ 083 public boolean isPhiAtMerge(Node value) { 084 return value instanceof PhiNode && ((PhiNode) value).merge() == this; 085 } 086 087 /** 088 * Removes the given end from the merge, along with the entries corresponding to this end in the 089 * phis connected to the merge. 090 * 091 * @param pred the end to remove 092 */ 093 public void removeEnd(AbstractEndNode pred) { 094 int predIndex = phiPredecessorIndex(pred); 095 assert predIndex != -1; 096 deleteEnd(pred); 097 for (PhiNode phi : phis().snapshot()) { 098 if (phi.isDeleted()) { 099 continue; 100 } 101 ValueNode removedValue = phi.valueAt(predIndex); 102 phi.removeInput(predIndex); 103 if (removedValue != null && removedValue.isAlive() && removedValue.hasNoUsages() && GraphUtil.isFloatingNode().apply(removedValue)) { 104 GraphUtil.killWithUnusedFloatingInputs(removedValue); 105 } 106 } 107 } 108 109 protected void deleteEnd(AbstractEndNode end) { 110 ends.remove(end); 111 } 112 113 public void clearEnds() { 114 ends.clear(); 115 } 116 117 public NodeInputList<EndNode> forwardEnds() { 118 return ends; 119 } 120 121 public int phiPredecessorCount() { 122 return forwardEndCount(); 123 } 124 125 public int phiPredecessorIndex(AbstractEndNode pred) { 126 return forwardEndIndex((EndNode) pred); 127 } 128 129 public AbstractEndNode phiPredecessorAt(int index) { 130 return forwardEndAt(index); 131 } 132 133 public NodeIterable<PhiNode> phis() { 134 return this.usages().filter(PhiNode.class).filter(this::isPhiAtMerge); 135 } 136 137 public NodeIterable<ValuePhiNode> valuePhis() { 138 return this.usages().filter(ValuePhiNode.class).filter(this::isPhiAtMerge); 139 } 140 141 @Override 142 public NodeIterable<Node> anchored() { 143 return super.anchored().filter(n -> !isPhiAtMerge(n)); 144 } 145 146 /** 147 * This simplify method can deal with a null value for tool, so that it can be used outside of 148 * canonicalization. 149 */ 150 @Override 151 public void simplify(SimplifierTool tool) { 152 FixedNode currentNext = next(); 153 if (currentNext instanceof AbstractEndNode) { 154 AbstractEndNode origLoopEnd = (AbstractEndNode) currentNext; 155 AbstractMergeNode merge = origLoopEnd.merge(); 156 if (merge instanceof LoopBeginNode && !(origLoopEnd instanceof LoopEndNode)) { 157 return; 158 } 159 // in order to move anchored values to the other merge we would need to check if the 160 // anchors are used by phis of the other merge 161 if (this.anchored().isNotEmpty()) { 162 return; 163 } 164 if (merge.stateAfter() == null && this.stateAfter() != null) { 165 // We hold a state, but the succeeding merge does not => do not combine. 166 return; 167 } 168 for (PhiNode phi : phis()) { 169 if (phi.usages().filter(isNotA(VirtualState.class)).and(node -> !merge.isPhiAtMerge(node)).isNotEmpty()) { 170 return; 171 } 172 } 173 Debug.log("Split %s into ends for %s.", this, merge); 174 int numEnds = this.forwardEndCount(); 175 for (int i = 0; i < numEnds - 1; i++) { 176 AbstractEndNode end = forwardEndAt(numEnds - 1 - i); 177 if (tool != null) { 178 tool.addToWorkList(end); 179 } 180 AbstractEndNode newEnd; 181 if (merge instanceof LoopBeginNode) { 182 newEnd = graph().add(new LoopEndNode((LoopBeginNode) merge)); 183 } else { 184 EndNode tmpEnd = graph().add(new EndNode()); 185 merge.addForwardEnd(tmpEnd); 186 newEnd = tmpEnd; 187 } 188 for (PhiNode phi : merge.phis()) { 189 ValueNode v = phi.valueAt(origLoopEnd); 190 ValueNode newInput; 191 if (isPhiAtMerge(v)) { 192 PhiNode endPhi = (PhiNode) v; 193 newInput = endPhi.valueAt(end); 194 } else { 195 newInput = v; 196 } 197 phi.addInput(newInput); 198 } 199 this.removeEnd(end); 200 end.replaceAtPredecessor(newEnd); 201 end.safeDelete(); 202 if (tool != null) { 203 tool.addToWorkList(newEnd.predecessor()); 204 } 205 } 206 graph().reduceTrivialMerge(this); 207 } else if (currentNext instanceof ReturnNode) { 208 ReturnNode returnNode = (ReturnNode) currentNext; 209 if (anchored().isNotEmpty() || returnNode.getMemoryMap() != null) { 210 return; 211 } 212 List<PhiNode> phis = phis().snapshot(); 213 for (PhiNode phi : phis) { 214 for (Node usage : phi.usages().filter(isNotA(FrameState.class))) { 215 if (usage != returnNode) { 216 return; 217 } 218 } 219 } 220 221 ValuePhiNode returnValuePhi = returnNode.result() == null || !isPhiAtMerge(returnNode.result()) ? null : (ValuePhiNode) returnNode.result(); 222 List<EndNode> endNodes = forwardEnds().snapshot(); 223 for (EndNode end : endNodes) { 224 ReturnNode newReturn = graph().add(new ReturnNode(returnValuePhi == null ? returnNode.result() : returnValuePhi.valueAt(end))); 225 if (tool != null) { 226 tool.addToWorkList(end.predecessor()); 227 } 228 end.replaceAtPredecessor(newReturn); 229 } 230 GraphUtil.killCFG(this); 231 for (EndNode end : endNodes) { 232 end.safeDelete(); 233 } 234 for (PhiNode phi : phis) { 235 if (tool.allUsagesAvailable() && phi.isAlive() && phi.hasNoUsages()) { 236 GraphUtil.killWithUnusedFloatingInputs(phi); 237 } 238 } 239 } 240 } 241}