1 cananian 1.1.2.1 // ToTreeHelpers.java, created Fri Feb 11 15:11:20 2000 by cananian
  2 cananian 1.1.2.1 // Copyright (C) 2000 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1.2.1 package harpoon.IR.Tree;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.1 import harpoon.Analysis.ReachingDefs;
  7 cananian 1.1.2.1 import harpoon.ClassFile.HCode;
  8 cananian 1.1.2.1 import harpoon.ClassFile.HCodeEdge;
  9 cananian 1.1.2.1 import harpoon.ClassFile.HCodeElement;
 10 cananian 1.1.2.1 import harpoon.IR.Properties.CFGrapher;
 11 cananian 1.1.2.1 import harpoon.IR.Properties.UseDefer;
 12 cananian 1.1.2.4 import harpoon.IR.Quads.PHI;
 13 cananian 1.1.2.3 import harpoon.IR.Quads.MONITORENTER;
 14 cananian 1.1.2.3 import harpoon.IR.Quads.MONITOREXIT;
 15 cananian 1.1.2.4 import harpoon.IR.Quads.SIGMA;
 16 cananian 1.1.2.3 import harpoon.IR.Quads.Quad;
 17 cananian 1.1.2.3 import harpoon.IR.LowQuad.PCALL;
 18 cananian 1.1.2.8 import harpoon.IR.LowQuad.PGET;
 19 cananian 1.1.2.3 import harpoon.IR.LowQuad.PSET;
 20 cananian 1.1.2.1 import harpoon.Temp.Temp;
 21 cananian 1.6     import net.cscott.jutil.BinaryHeap;
 22 cananian 1.6     import net.cscott.jutil.BinomialHeap;
 23 cananian 1.6     import net.cscott.jutil.FibonacciHeap;
 24 cananian 1.6     import net.cscott.jutil.Heap;
 25 cananian 1.6     import net.cscott.jutil.Environment;
 26 cananian 1.6     import net.cscott.jutil.HashEnvironment;
 27 cananian 1.1.2.1 import harpoon.Util.Util;
 28 cananian 1.1.2.1 
 29 cananian 1.1.2.4 import java.util.ArrayList;
 30 cananian 1.1.2.4 import java.util.Arrays;
 31 cananian 1.1.2.1 import java.util.Collections;
 32 cananian 1.1.2.1 import java.util.HashMap;
 33 cananian 1.1.2.3 import java.util.HashSet;
 34 cananian 1.1.2.1 import java.util.Iterator;
 35 cananian 1.1.2.1 import java.util.Map;
 36 cananian 1.1.2.1 import java.util.Set;
 37 cananian 1.1.2.1 /**
 38 cananian 1.1.2.1  * <code>ToTreeHelpers</code>
 39 cananian 1.1.2.1  * 
 40 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 41 cananian 1.6      * @version $Id: ToTreeHelpers.java,v 1.6 2004/02/08 01:55:51 cananian Exp $
 42 cananian 1.1.2.1  */
 43 cananian 1.1.2.1 abstract class ToTreeHelpers {
 44 cananian 1.1.2.1     //------------ EdgeOracle IMPLEMENTATIONS ------------------
 45 cananian 1.1.2.1 
 46 cananian 1.1.2.1     /** The <code>DefaultEdgeOracle</code> always follows the zeroth
 47 cananian 1.1.2.1      *  outgoing edge.  Very simple. */
 48 cananian 1.1.2.1     static class DefaultEdgeOracle implements ToTree.EdgeOracle {
 49 cananian 1.1.2.1         DefaultEdgeOracle() { }
 50 cananian 1.1.2.1         public int defaultEdge(HCodeElement hce) { return 0; }
 51 cananian 1.1.2.9     }
 52 cananian 1.1.2.9     /** The <code>SourceSimilarEdgeOracle</code> tries to lay out code
 53 cananian 1.1.2.9      *  in the same order as in the original java file, when it has the
 54 cananian 1.1.2.9      *  information to do so.  Otherwide, it falls back on a provided
 55 cananian 1.1.2.9      *  default edge oracle. */
 56 cananian 1.1.2.9     static class SourceSimilarEdgeOracle implements ToTree.EdgeOracle {
 57 cananian 1.1.2.9         final CFGrapher cfg;
 58 cananian 1.1.2.9         final ToTree.EdgeOracle eo;
 59 cananian 1.1.2.9         SourceSimilarEdgeOracle(ToTree.EdgeOracle eo) {
 60 cananian 1.1.2.9             this(CFGrapher.DEFAULT, eo);
 61 cananian 1.1.2.9         }
 62 cananian 1.1.2.9         SourceSimilarEdgeOracle(CFGrapher cfg, ToTree.EdgeOracle eo) {
 63 cananian 1.1.2.9             this.cfg = cfg;
 64 cananian 1.1.2.9             this.eo = eo;
 65 cananian 1.1.2.9         }
 66 cananian 1.1.2.9         public int defaultEdge(HCodeElement hce) {
 67 cananian 1.5                 HCodeEdge[] succ = (HCodeEdge[]) cfg.succC(hce).toArray(new HCodeEdge[0]);
 68 cananian 1.1.2.9             int ln[] = new int[succ.length];
 69 cananian 1.1.2.9             for (int i=0; i<succ.length; i++)
 70 cananian 1.1.2.9                 ln[i] = succ[i].to().getLineNumber();
 71 cananian 1.1.2.9             if (ln.length>0) {
 72 cananian 1.1.2.9                 int min = 0;
 73 cananian 1.1.2.9                 boolean ismin = true;
 74 cananian 1.1.2.9                 for (int i=1; i<ln.length; i++) {
 75 cananian 1.1.2.9                     if (ln[i] == ln[min])
 76 cananian 1.1.2.9                         ismin=false;
 77 cananian 1.1.2.9                     else if (ln[i] < ln[min]) { min=i; ismin=true; }
 78 cananian 1.1.2.9                 }
 79 cananian 1.1.2.9                 if (ismin) return min;
 80 cananian 1.1.2.9             }
 81 cananian 1.1.2.9             // no clear guidance.  fall back.
 82 cananian 1.1.2.9             return eo.defaultEdge(hce);
 83 cananian 1.1.2.9         }
 84 cananian 1.1.2.1     }
 85 cananian 1.1.2.1 
 86 cananian 1.1.2.1     /** The <code>MinMaxEdgeOracle</code> lays code out according to the
 87 cananian 1.1.2.1      *  computed shortest paths to the footer.  That is, it computes
 88 cananian 1.1.2.1      *  an approximation to the single-source longest paths problem
 89 cananian 1.1.2.1      *  and tries to lay out code such that the longest paths in the
 90 cananian 1.1.2.1      *  method have no branches.  Since LONGEST-PATH is NP-complete,
 91 cananian 1.1.2.1      *  this class actual sets the default successor edge to be the
 92 cananian 1.1.2.1      *  one which has the longest shortest path to the footer.
 93 cananian 1.1.2.1      *  <p>
 94 cananian 1.1.2.1      *  See Karger, Motwani, and Ramkumar, "On Approximating the Longest
 95 cananian 1.1.2.1      *  Path in a Graph" in <i>Algorithmica</i> 1997 18:82-98; and
 96 cananian 1.1.2.1      *  Young, Johnson, Karger, and Smith, "Near-optimal intraprocedural
 97 cananian 1.1.2.1      *  Branch Alignment" in PLDI'97 for some tangentially-related work.
 98 cananian 1.1.2.1      *
 99 cananian 1.1.2.1      * @author C. Scott Ananian <cananian@alumni.princeton.edu>
100 cananian 1.1.2.1      */
101 cananian 1.1.2.1     static class MinMaxEdgeOracle implements ToTree.EdgeOracle {
102 cananian 1.1.2.1         final CFGrapher cfg;
103 cananian 1.1.2.1         /** Convenience constructor. */
104 cananian 1.1.2.1         MinMaxEdgeOracle(HCode hc) { this(hc, CFGrapher.DEFAULT); }
105 cananian 1.1.2.1         /** Compute single-source shortest paths on edge-reversed graph,
106 cananian 1.1.2.1          *  using the technique of CLR, 25.2 (Dijkstra's algorithm).
107 cananian 1.1.2.1          *  Uses a binomial heap, for an O(E lg V) runtime.  If we're
108 cananian 1.1.2.1          *  feeling bored, we could use a fibonacci heap instead for
109 cananian 1.1.2.1          *  an O(V lg V + E) runtime.  I don't think the difference is
110 cananian 1.1.2.1          *  enough to care about. */
111 cananian 1.1.2.1         MinMaxEdgeOracle(HCode hc, CFGrapher cfg) {
112 cananian 1.1.2.1             this.cfg = cfg;
113 cananian 1.1.2.1             // get the 'single-source', which is the unique FOOTER node.
114 cananian 1.3.2.1             assert hc.getLeafElements().length==1;
115 cananian 1.1.2.1             HCodeElement s = hc.getLeafElements()[0];
116 cananian 1.1.2.1             // we're going to need a mapping from HCodeElements to Map.Entry's
117 cananian 1.1.2.1             Map m = new HashMap();
118 cananian 1.1.2.1             // make the priority queue, and initialize it.
119 cananian 1.1.2.3             Heap Q = new BinaryHeap(); // new FibonacciHeap();
120 cananian 1.1.2.1             for (Iterator it=hc.getElementsI(); it.hasNext(); ) {
121 cananian 1.1.2.1                 HCodeElement hce = (HCodeElement) it.next();
122 cananian 1.1.2.1                 // d[v] = infinity.
123 cananian 1.1.2.1                 Map.Entry entry= Q.insert(new Integer(Integer.MAX_VALUE), hce);
124 cananian 1.1.2.1                 // don't forget entry-to-hce correlation.
125 cananian 1.1.2.1                 m.put(hce, entry);
126 cananian 1.1.2.1             }
127 cananian 1.1.2.1             // INITIALIZE-SINGLE-SOURCE by lowering source to 0.
128 cananian 1.1.2.1             set_d(s, 0, Q, m);
129 cananian 1.1.2.1             // now do Dijkstra's algorithm.
130 cananian 1.1.2.1             while (!Q.isEmpty()) {
131 cananian 1.1.2.1                 HCodeElement u = (HCodeElement) Q.extractMinimum().getValue();
132 cananian 1.1.2.1                 for (Iterator it=cfg.predC(u).iterator(); it.hasNext(); ) {
133 cananian 1.1.2.1                     HCodeElement v = ((HCodeEdge) it.next()).from();
134 cananian 1.1.2.1                     // RELAX step.
135 cananian 1.1.2.1                     if (d(v) > (d(u)+1))
136 cananian 1.1.2.1                         set_d(v, d(u)+1, Q, m);
137 cananian 1.1.2.1                 }
138 cananian 1.1.2.1             }
139 cananian 1.1.2.1             // Look, Ma!  d[v] is loaded & ready to go!
140 cananian 1.1.2.1         }
141 cananian 1.1.2.1         /** Path weights mapping. */
142 cananian 1.1.2.1         final Map d = new HashMap();
143 cananian 1.1.2.1         // convenience functions for working with the path weights.
144 cananian 1.1.2.1         private int d(HCodeElement v) {
145 cananian 1.1.2.1             if (!d.containsKey(v)) return Integer.MAX_VALUE;
146 cananian 1.1.2.1             return ((Integer)d.get(v)).intValue();
147 cananian 1.1.2.1         }
148 cananian 1.1.2.2         private void set_d(HCodeElement v, int k, Heap Q, Map m) {
149 cananian 1.3.2.1             assert d(v) > k;
150 cananian 1.3.2.1             assert ((Map.Entry)m.get(v)).getKey()
151 cananian 1.3.2.1                         .equals(new Integer(d(v)));
152 cananian 1.1.2.1             d.put(v, new Integer(k));
153 cananian 1.1.2.1             Q.decreaseKey((Map.Entry)m.get(v), new Integer(k));
154 cananian 1.3.2.1             assert ((Map.Entry)m.get(v)).getKey()
155 cananian 1.3.2.1                         .equals(new Integer(d(v)));
156 cananian 1.1.2.1         }
157 cananian 1.1.2.1         /** defaultEdge corresponds to the edge with largest shortest path */
158 cananian 1.1.2.1         public int defaultEdge(HCodeElement hce) {
159 cananian 1.5                 HCodeEdge[] succ = (HCodeEdge[]) cfg.succC(hce).toArray(new HCodeEdge[0]);
160 cananian 1.1.2.1             // hacks for SWITCH & other common cases.
161 cananian 1.1.2.1             if (succ.length!=2) return 0;
162 cananian 1.1.2.1             int maxedge=0, maxscore=d(succ[0].to());
163 cananian 1.1.2.1             for (int i=1; i<succ.length; i++) {
164 cananian 1.1.2.1                 int score = d(succ[i].to());
165 cananian 1.1.2.1                 if (score > maxscore) {
166 cananian 1.1.2.1                     maxedge=i; maxscore=score;
167 cananian 1.1.2.1                 }
168 cananian 1.1.2.1             }
169 cananian 1.1.2.1             return maxedge;
170 cananian 1.1.2.1         }
171 cananian 1.1.2.1     }
172 cananian 1.1.2.1 
173 cananian 1.1.2.1     //------------ FoldNanny IMPLEMENTATIONS ------------------
174 cananian 1.1.2.1     static class DefaultFoldNanny implements ToTree.FoldNanny {
175 cananian 1.1.2.1         DefaultFoldNanny() { }
176 cananian 1.1.2.1         public boolean canFold(HCodeElement hce, Temp t) { return false; }
177 cananian 1.1.2.3     }
178 cananian 1.1.2.6     static class SSXSimpleFoldNanny implements ToTree.FoldNanny {
179 cananian 1.1.2.8         // Set to 'false' to enable foldings which break derived types.
180 cananian 1.1.2.8         final private static boolean RESTRICT_DERIVED_TYPES = true;
181 cananian 1.1.2.3         final Set safe = new HashSet();
182 cananian 1.1.2.6         SSXSimpleFoldNanny(HCode hc) {
183 cananian 1.1.2.3             // first, find all the single-use variables
184 cananian 1.1.2.3             HashSet singleUse = new HashSet(), multiUse = new HashSet();
185 cananian 1.1.2.3             for (Iterator it = hc.getElementsI(); it.hasNext(); ) {
186 cananian 1.1.2.3                 Quad q = (Quad) it.next();
187 cananian 1.1.2.4                 // SIGMAs count multiple times... (see uses() method)
188 cananian 1.1.2.4                 for (Iterator it2 = uses(q); it2.hasNext(); ) {
189 cananian 1.1.2.3                     Temp t = (Temp) it2.next();
190 cananian 1.1.2.3                     if (multiUse.contains(t)) continue;
191 cananian 1.1.2.3                     if (singleUse.contains(t)) {
192 cananian 1.1.2.3                         multiUse.add(t); singleUse.remove(t);
193 cananian 1.1.2.3                     } else singleUse.add(t);
194 cananian 1.1.2.3                 }
195 cananian 1.1.2.3             }
196 cananian 1.1.2.3             multiUse = null;
197 cananian 1.1.2.3             // now filter the safe set.
198 cananian 1.1.2.3             // we don't want anything folded which is live over a memory
199 cananian 1.1.2.3             // store or a synchronization.  we're going to be a bit
200 cananian 1.1.2.3             // conservative here.
201 cananian 1.1.2.8             // ALSO: we need to be careful about derived types.
202 cananian 1.1.2.3             dfs((Quad)hc.getRootElement(), new HashSet(),
203 cananian 1.1.2.3                 new HashEnvironment(), singleUse);
204 cananian 1.1.2.3             // done!
205 cananian 1.1.2.3             //System.err.print("[FOLDING: "+safe+"]");
206 cananian 1.1.2.3         }
207 cananian 1.1.2.3         void dfs(Quad q, Set seen, Environment reachingDefs, Set singleUse) {
208 cananian 1.3.2.1             assert !seen.contains(q);
209 cananian 1.1.2.3             seen.add(q);
210 cananian 1.1.2.3             { // if def reached this use, add to safe set.
211 cananian 1.1.2.3                 Temp[] use = q.use();
212 cananian 1.1.2.3                 for (int i=0; i<use.length; i++)
213 cananian 1.1.2.3                     if (reachingDefs.containsKey(use[i]))
214 cananian 1.1.2.3                         safe.add(use[i]); // reaching def means its safe.
215 cananian 1.1.2.3             }
216 cananian 1.1.2.4             if (isBarrier(q)) {
217 cananian 1.1.2.4                 // stores and syncs clear the defs
218 cananian 1.1.2.4                 reachingDefs.clear();
219 cananian 1.1.2.3             }
220 cananian 1.1.2.4             if (!isUnfoldableDef(q)) {
221 cananian 1.1.2.4                 // add environment entries for all appropriate defs
222 cananian 1.1.2.3                 Temp[] def = q.def();
223 cananian 1.1.2.3                 for (int i=0; i<def.length; i++)
224 cananian 1.1.2.3                     if (singleUse.contains(def[i]))
225 cananian 1.1.2.4                         reachingDefs.put(def[i], def[i]);
226 cananian 1.1.2.3             }
227 cananian 1.1.2.3             { // recurse.
228 cananian 1.1.2.3                 Quad[] next = q.next();
229 cananian 1.1.2.3                 Environment.Mark m = reachingDefs.getMark();
230 cananian 1.1.2.3                 for (int i=0; i<next.length; i++) {
231 cananian 1.1.2.3                     if (!seen.contains(next[i])) {
232 cananian 1.1.2.3                         dfs(next[i], seen, reachingDefs, singleUse);
233 cananian 1.1.2.3                         reachingDefs.undoToMark(m);
234 cananian 1.1.2.3                     }
235 cananian 1.1.2.3                 }
236 cananian 1.1.2.3             }
237 cananian 1.1.2.3         }
238 cananian 1.1.2.8         // the folded expression BINOP<p>(+, CONST(x), PGET(y)) is untypeable
239 cananian 1.1.2.8         // in our system, because derived types must reference a base pointer
240 cananian 1.1.2.8         // IN A TEMPORARY -- we can't represent bases in memory.  So to be
241 cananian 1.1.2.8         // safe (if a bit conservative) we don't fold non-primitive fetches
242 cananian 1.1.2.8         // (PGETs).
243 cananian 1.1.2.4         public static boolean isUnfoldableDef(Quad q) {
244 cananian 1.1.2.4             if (q instanceof PCALL) return true; // CALLS CAN NOT BE FOLDED
245 cananian 1.1.2.8             if (q instanceof PGET && !((PGET)q).type().isPrimitive() &&
246 cananian 1.1.2.8                 RESTRICT_DERIVED_TYPES) return true; // MAY BE UNTYPEABLE
247 cananian 1.1.2.4             if (q instanceof PHI) return true; // multiple definitions
248 cananian 1.1.2.4             return false;
249 cananian 1.1.2.4         }
250 cananian 1.1.2.3         public static boolean isBarrier(Quad q) {
251 cananian 1.1.2.3             // ASET,CALL,SET are Quad-only.  We only deal with LowQuads here
252 cananian 1.1.2.3             if (q instanceof MONITORENTER || q instanceof MONITOREXIT ||
253 cananian 1.1.2.5                 q instanceof PCALL || q instanceof PSET) return true;
254 cananian 1.1.2.5             return false;
255 cananian 1.1.2.4         }
256 cananian 1.1.2.4         public Iterator uses(Quad q) {
257 cananian 1.1.2.4             if (q instanceof SIGMA) return uses((SIGMA)q);
258 cananian 1.1.2.4             else return q.useC().iterator();
259 cananian 1.1.2.4         }
260 cananian 1.1.2.4         public Iterator uses(SIGMA q) {
261 cananian 1.1.2.4             ArrayList al = new ArrayList(q.useC());
262 cananian 1.1.2.4             // add in multiple copies of quads used by sigma functions
263 cananian 1.1.2.4             for (int i=1; i<q.nextLength(); i++)
264 cananian 1.1.2.4                 al.addAll(Arrays.asList(q.src()));
265 cananian 1.1.2.4             return al.iterator();
266 cananian 1.1.2.3         }
267 cananian 1.1.2.3         public boolean canFold(HCodeElement hce, Temp t) {
268 cananian 1.1.2.3             return safe.contains(t);
269 cananian 1.1.2.3         }
270 cananian 1.1.2.1     }
271 cananian 1.2     }