/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o 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 for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package EDU.purdue.cs.bloat.trans; import java.io.*; import java.util.*; import EDU.purdue.cs.bloat.cfg.*; import EDU.purdue.cs.bloat.ssa.*; import EDU.purdue.cs.bloat.tree.*; import EDU.purdue.cs.bloat.util.*; /** * Performs value numbering analysis on the nodes in a control flow graph. Nodes * with identical value numbers are folded into one another so that common * (redundent) expressions are eliminated. Note that ValueNumbering works on the * SSAGraph for the CFG and not the CFG itself. * * @see SSAGraph */ // L.T. Simpson 1996. Value-driven redundancy elimination. PhD // Thesis. Rice University. Look it up. Chapter 4 contains the // stuff on SCC-based value numbering. public class ValueNumbering { public static boolean DEBUG = false; SSAGraph ssaGraph; HashMap tuples; // Maps a Node to its Tuple ValueFolder folder; int next; // The next value number to assign public String debugDirName = "debug"; public File debugDir; public boolean DUMP = false; private PrintWriter dump = new PrintWriter(System.out); /** * Performs value numbering on a control flow graph. * * @see ComponentVisitor * @see SSAGraph * @see ValueFolder */ public void transform(final FlowGraph cfg) { // Specify directory into which all debugging files should be // placed if (DUMP || ValueFolding.DUMP) { final String className = cfg.method().declaringClass().type() .className(); final String methodName = cfg.method().name(); final String dirName = debugDirName + File.separator + className + File.separator + methodName; debugDir = new File(dirName); for (int nextDir = 1; debugDir.exists(); nextDir++) { // Multiple directories debugDir = new File(dirName + "_" + nextDir); } if (!debugDir.exists()) { debugDir.mkdirs(); } // General dumping file try { final File dumpFile = new File(debugDir, "vn_dump"); dump = new PrintWriter(new FileWriter(dumpFile), true); } catch (final IOException ex) { System.err.println(ex.toString()); } } ssaGraph = new SSAGraph(cfg); tuples = new HashMap(); folder = new ValueFolder(false, cfg.method().declaringClass().context()); next = 0; final HashMap valid = new HashMap(); final HashMap optimistic = new HashMap(); ssaGraph.visitComponents(new ComponentVisitor() { public void visitComponent(final List scc) { if (ValueNumbering.DEBUG || DUMP) { dump.println("\nNumbering SCC = " + scc); } final Iterator e = scc.iterator(); while (e.hasNext()) { final Node node = (Node) e.next(); node.setValueNumber(-1); } if (scc.size() > 1) { if (ValueNumbering.DEBUG || DUMP) { dump.println("Optimistic-----------------------"); } boolean changed = true; while (changed) { changed = false; final Iterator iter = scc.iterator(); while (iter.hasNext()) { final Node node = (Node) iter.next(); if (valnum(node, optimistic)) { changed = true; } } } } if (ValueNumbering.DEBUG || DUMP) { dump.println("Valid--------------------------------"); } // The valid table contains the correct value numbers. Run // through the each node in the SCC and call valnum. // Presumably, the nodes are in reverse postorder. final Iterator iter = scc.iterator(); while (iter.hasNext()) { final Node node = (Node) iter.next(); valnum(node, valid); } } }); if (ValueNumbering.DEBUG || DUMP) { dump.println("Final value numbers--------------------------"); printValueNumbers(cfg, new PrintWriter(dump)); } if (DUMP) { System.out.println(" Dumping to: " + debugDir); try { final File valueNumbers = new File(debugDir, "scc.txt"); ssaGraph .printSCCs(new PrintWriter(new FileWriter(valueNumbers))); } catch (final IOException ex) { System.err.println("IOException: " + ex); } } ssaGraph = null; tuples = null; folder.cleanup(); folder = null; // Make sure each node has a value number cfg.visit(new TreeVisitor() { public void visitTree(final Tree tree) { tree.visitChildren(this); } public void visitNode(final Node node) { node.visitChildren(this); Assert.isTrue(node.valueNumber() != -1, "No value number for " + node); } }); } private void printValueNumbers(final FlowGraph cfg, final PrintWriter pw) { cfg.visit(new TreeVisitor() { public void visitTree(final Tree tree) { tree.visitChildren(this); } public void visitNode(final Node node) { node.visitChildren(this); pw.println("VN[" + node + " " + System.identityHashCode(node) + // " == " + ssaGraph.equivalent(node) + " " + // System.identityHashCode(ssaGraph.equivalent(node)) + "] = " + node.valueNumber()); } }); } /** * Simplifies a node by examining its type. A ValueFolder may be used to * perform simplification. * * @return The folded (simplified) value of the node (which may be the same * as the node itself) */ private Node simplify(final Node node) { if (ValueNumbering.DEBUG || DUMP) { dump.println("folding " + node + " in " + node.parent()); } final int v = node.valueNumber(); // A value number of -1 (i.e. value number has not yet been // assigned) cannot be simplified. if (v == -1) { return node; } // A constant expression can't be simplified, set the value of // value number v to be node if (node instanceof ConstantExpr) { folder.values.ensureSize(v + 1); folder.values.set(v, node); return node; } // Check for the value number in the folder. if (v < folder.values.size()) { final ConstantExpr value = (ConstantExpr) folder.values.get(v); if (value != null) { return value; } } // Else, use a ValueFolder to fold the node folder.node = null; node.visit(folder); if (ValueNumbering.DEBUG || DUMP) { dump.println("folded " + node + " to " + folder.node); } if (folder.node == null) { // Nothing changed return node; } // If we folded the node into a constant expression, add it to // the values list if (folder.node instanceof ConstantExpr) { folder.values.ensureSize(v + 1); folder.values.set(v, folder.node); } return folder.node; } /** * Processes a Node in an SCC. */ private boolean valnum(final Node node, final HashMap table) { boolean changed = false; // Did the table change? Tuple tuple = (Tuple) tuples.get(node); if (tuple == null) { // Make a new Tuple for the node being processed final Node s = simplify(node); tuple = new Tuple(s); tuples.put(node, tuple); if (ValueNumbering.DEBUG || DUMP) { dump.println(" New tuple " + tuple); } } else if (DUMP) { dump.println(" " + node + " mapped to tuple " + tuple); } final Node w = (Node) table.get(tuple); if (ValueNumbering.DEBUG || DUMP) { dump.println(" Looking up " + tuple); dump.println(" " + tuple + " mapped to node " + w + (w != null ? " (VN = " + w.valueNumber() + ")" : "")); } int value = -1; if ((w != null) && (w.valueNumber() != -1)) { value = w.valueNumber(); } else { if (ValueNumbering.DEBUG || DUMP) { dump.println(" New value number " + next); } value = next++; } Assert.isTrue(value != -1); // Now make sure all equivalent nodes have the same value number. final Iterator iter = ssaGraph.equivalent(node).iterator(); while (iter.hasNext()) { final Node v = (Node) iter.next(); final Tuple t = (Tuple) tuples.get(v); if (t == null) { // Will get done later. continue; } if (v.valueNumber() != value) { v.setValueNumber(value); table.put(t, v); if (ValueNumbering.DEBUG || DUMP) { dump.println(" Assigning value number " + v.valueNumber() + " to " + v); dump.println(" Mapping tuple " + t + " to node " + v); } changed = true; } } return changed; } /** * Tuple contains a Node and an associated hash value. The Node is the * simplified version of another node. The main purpose of the Tuple class * is to compare two Nodes to determine if they are the same with respect to * their value numbers. */ class Tuple { Node node; int hash; public Tuple(final Node node) { this.node = node; final List children = ssaGraph.children(node); this.hash = NodeComparator.hashCode(node) + children.size(); } public String toString() { final List children = ssaGraph.children(node); String s = "<" + node + ", hash=" + hash; final Iterator iter = children.iterator(); while (iter.hasNext()) { final Node child = (Node) iter.next(); s += ", " + child + "{" + child.valueNumber() + "}"; } s += ">"; return s; } public int hashCode() { return hash; } public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof Tuple) { final Tuple t = (Tuple) obj; if (node == t.node) { return true; } // All mem refs are unequal. if ((node instanceof MemRefExpr) || (t.node instanceof MemRefExpr)) { return false; } if (!NodeComparator.equals(node, t.node)) { return false; } final List children1 = ssaGraph.children(node); final List children2 = ssaGraph.children(t.node); if (children1.size() != children2.size()) { return false; } if (node instanceof PhiStmt) { // The order of the children does not matter final int[] used = new int[next]; int free = 0; // The number of un-numbered children Iterator iter = children1.iterator(); while (iter.hasNext()) { final Node child = (Node) iter.next(); final int v = child.valueNumber(); if (v != -1) { used[v]++; } else { free++; } } iter = children2.iterator(); while (iter.hasNext()) { final Node child = (Node) iter.next(); final int v = child.valueNumber(); if (v != -1) { if (used[v] != 0) { used[v]--; } else { free--; } } else { free--; } } if (free < 0) { return false; } return true; } else { // The children of the nodes in the SSAGraph must have the // same value numbers and be in the same order. final Iterator iter1 = children1.iterator(); final Iterator iter2 = children2.iterator(); while (iter1.hasNext() && iter2.hasNext()) { final Node child1 = (Node) iter1.next(); final Node child2 = (Node) iter2.next(); final int v1 = child1.valueNumber(); final int v2 = child2.valueNumber(); if ((v1 != -1) && (v2 != -1) && (v1 != v2)) { return false; } } if (iter1.hasNext() || iter2.hasNext()) { // Size mismatch. return false; } return true; } } return false; } } }