/*
* Copyright (c) 2011, 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 com.oracle.max.graal.compiler.phases;
import com.oracle.max.criutils.*;
import com.oracle.max.graal.compiler.*;
import com.oracle.max.graal.graph.*;
import com.oracle.max.graal.nodes.*;
import com.oracle.max.graal.nodes.calc.*;
import com.oracle.max.graal.nodes.extended.*;
import com.oracle.max.graal.nodes.spi.*;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
public class CanonicalizerPhase extends Phase {
private static final int MAX_ITERATION_PER_NODE = 10;
private boolean newNodes;
private final CiTarget target;
private final CiAssumptions assumptions;
private final RiRuntime runtime;
public CanonicalizerPhase(CiTarget target, RiRuntime runtime, CiAssumptions assumptions) {
this(target, runtime, false, assumptions);
}
public CanonicalizerPhase(CiTarget target, RiRuntime runtime, boolean newNodes, CiAssumptions assumptions) {
this.newNodes = newNodes;
this.target = target;
this.assumptions = assumptions;
this.runtime = runtime;
}
@Override
protected void run(StructuredGraph graph) {
NodeWorkList nodeWorkList = graph.createNodeWorkList(!newNodes, MAX_ITERATION_PER_NODE);
if (newNodes) {
nodeWorkList.addAll(graph.getNewNodes());
}
canonicalize(graph, nodeWorkList, runtime, target, assumptions);
}
public static void canonicalize(StructuredGraph graph, NodeWorkList nodeWorkList, RiRuntime runtime, CiTarget target, CiAssumptions assumptions) {
Tool tool = new Tool(nodeWorkList, runtime, target, assumptions);
for (Node node : nodeWorkList) {
if (node instanceof Canonicalizable) {
if (GraalOptions.TraceCanonicalizer) {
TTY.println("Canonicalizer: work on " + node);
}
graph.mark();
tool.setNode(node);
Node canonical = ((Canonicalizable) node).canonical(tool);
if (canonical == null) {
node.safeDelete();
} else if (canonical != node) {
// cases: original node:
// |Floating|Fixed-unconnected|Fixed-connected|
// --------------------------------------------
// null| 1 | X | 3 |
// --------------------------------------------
// Floating| 1 | X | 3 |
// canonical node: --------------------------------------------
// Fixed-unconnected| X | X | 2 |
// --------------------------------------------
// Fixed-connected| 1 | X | 3 |
// --------------------------------------------
// X: must not happen (checked with assertions)
if (node instanceof FloatingNode) {
// case 1
assert canonical == null || canonical instanceof FloatingNode || (canonical instanceof FixedNode && canonical.predecessor() != null) || canonical instanceof ReadNode : canonical;
node.replaceAndDelete(canonical);
} else {
assert node instanceof FixedNode && node.predecessor() != null : node + " -> " + canonical + " : node should be fixed & connected (" + node.predecessor() + ")";
if (canonical instanceof FixedNode && canonical.predecessor() == null) {
// case 2
node.replaceAndDelete(canonical);
} else {
// case 3
FixedNode nextNode = null;
if (node instanceof FixedWithNextNode) {
nextNode = ((FixedWithNextNode) node).next();
} else if (node instanceof ControlSplitNode) {
for (FixedNode sux : ((ControlSplitNode) node).blockSuccessors()) {
if (nextNode == null) {
nextNode = sux;
} else {
assert sux == null;
}
}
}
node.clearSuccessors();
node.replaceAtPredecessors(nextNode);
node.replaceAndDelete(canonical);
}
}
for (Node newNode : graph.getNewNodes()) {
nodeWorkList.add(newNode);
}
if (canonical != null) {
nodeWorkList.replaced(canonical, node, false);
}
}
}
}
while (graph.getUsagesDroppedNodesCount() > 0) {
for (Node n : graph.getAndCleanUsagesDroppedNodes()) {
if (!n.isDeleted() && n.usages().size() == 0 && n instanceof FloatingNode) {
killFloating((FloatingNode) n);
}
}
}
}
private static void killFloating(FloatingNode node) {
if (node.usages().size() == 0) {
node.clearInputs();
node.safeDelete();
}
}
private static final class Tool implements CanonicalizerTool {
private Node node;
private final NodeWorkList nodeWorkList;
private final RiRuntime runtime;
private final CiTarget target;
private final CiAssumptions assumptions;
public Tool(NodeWorkList nodeWorkList, RiRuntime runtime, CiTarget target, CiAssumptions assumptions) {
this.nodeWorkList = nodeWorkList;
this.runtime = runtime;
this.target = target;
this.assumptions = assumptions;
}
@Override
public void deleteBranch(FixedNode branch) {
node.replaceFirstSuccessor(branch, null);
killCFG(branch);
}
public void killCFG(FixedNode node) {
for (Node successor : node.successors()) {
if (successor != null) {
node.replaceFirstSuccessor(successor, null);
assert !node.isDeleted();
killCFG((FixedNode) successor);
}
}
if (node instanceof LoopEndNode) {
LoopEndNode loopEnd = (LoopEndNode) node;
LoopBeginNode loopBegin = loopEnd.loopBegin();
if (loopBegin != null) {
assert loopBegin.isAlive();
assert loopBegin.endCount() == 1;
EndNode predEnd = loopBegin.endAt(0);
for (PhiNode phi : loopBegin.phis().snapshot()) {
ValueNode value = phi.valueAt(0);
phi.replaceAndDelete(value);
nodeWorkList.replaced(value, phi, false);
}
FixedNode next = loopBegin.next();
loopEnd.setLoopBegin(null);
loopBegin.safeDelete();
predEnd.replaceAndDelete(next);
}
} else if (node instanceof EndNode) {
EndNode end = (EndNode) node;
MergeNode merge = end.merge();
if (merge instanceof LoopBeginNode) {
for (PhiNode phi : merge.phis().snapshot()) {
ValueNode value = phi.valueAt(0);
phi.replaceAndDelete(value);
nodeWorkList.replaced(value, phi, false);
}
if (((LoopBeginNode) merge).loopEnd() != null) {
((LoopBeginNode) merge).loopEnd().setLoopBegin(null);
}
killCFG(merge);
} else {
merge.removeEnd(end);
if (merge.phiPredecessorCount() == 1) {
for (PhiNode phi : merge.phis().snapshot()) {
ValueNode value = phi.valueAt(0);
phi.replaceAndDelete(value);
nodeWorkList.replaced(value, phi, false);
}
Node replacedSux = merge.phiPredecessorAt(0);
Node pred = replacedSux.predecessor();
assert replacedSux instanceof EndNode;
FixedNode next = merge.next();
merge.setNext(null);
pred.replaceFirstSuccessor(replacedSux, next);
if (merge.stateAfter().usages().size() == 1) {
merge.stateAfter().delete();
}
merge.safeDelete();
replacedSux.safeDelete();
}
}
}
killNonCFG(node, null);
}
public void killNonCFG(Node node, Node input) {
if (node instanceof PhiNode) {
node.replaceFirstInput(input, null);
} else {
for (Node usage : node.usages().snapshot()) {
if ((usage instanceof FloatingNode || usage instanceof CallTargetNode) && !usage.isDeleted()) {
killNonCFG(usage, node);
}
}
// null out remaining usages
node.replaceAtUsages(null);
node.safeDelete();
}
}
public void setNode(Node node) {
this.node = node;
}
/**
* @return the current target or {@code null} if no target is available in the current context.
*/
@Override
public CiTarget target() {
return target;
}
/**
* @return an object that can be used for recording assumptions or {@code null} if assumptions are not allowed in the current context.
*/
@Override
public CiAssumptions assumptions() {
return assumptions;
}
@Override
public RiRuntime runtime() {
return runtime;
}
}
}