1 cananian 1.1.2.1 // DispatchTreeTransformation.java, created Fri Oct 13 19:33:06 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.Analysis.Quads;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.1 import harpoon.Analysis.ClassHierarchy;
  7 cananian 1.1.2.5 import harpoon.Analysis.Maps.ConstMapProxy;
  8 cananian 1.1.2.1 import harpoon.Analysis.Maps.ExactTypeMap;
  9 cananian 1.1.2.5 import harpoon.Analysis.Maps.ExactTypeMapProxy;
 10 cananian 1.1.2.5 import harpoon.Analysis.Maps.ExecMapProxy;
 11 cananian 1.1.2.1 import harpoon.Analysis.Quads.SCC.SCCAnalysis;
 12 cananian 1.1.2.1 import harpoon.Analysis.Quads.SCC.SCCOptimize;
 13 cananian 1.1.2.1 import harpoon.ClassFile.HClass;
 14 cananian 1.1.2.1 import harpoon.ClassFile.HCode;
 15 cananian 1.1.2.1 import harpoon.ClassFile.HCodeAndMaps;
 16 cananian 1.1.2.4 import harpoon.ClassFile.HCodeEdge;
 17 cananian 1.1.2.4 import harpoon.ClassFile.HCodeElement;
 18 cananian 1.1.2.1 import harpoon.ClassFile.HCodeFactory;
 19 cananian 1.1.2.1 import harpoon.ClassFile.HMethod;
 20 cananian 1.1.2.1 import harpoon.IR.Quads.CALL;
 21 cananian 1.1.2.1 import harpoon.IR.Quads.Edge;
 22 cananian 1.1.2.1 import harpoon.IR.Quads.PHI;
 23 cananian 1.1.2.1 import harpoon.IR.Quads.Quad;
 24 cananian 1.1.2.4 import harpoon.IR.Quads.QuadRSSx;
 25 cananian 1.1.2.4 import harpoon.IR.Quads.QuadSSI;
 26 cananian 1.1.2.1 import harpoon.IR.Quads.QuadFactory;
 27 cananian 1.1.2.1 import harpoon.IR.Quads.TYPESWITCH;
 28 cananian 1.1.2.1 import harpoon.Temp.Temp;
 29 cananian 1.1.2.4 import harpoon.Temp.TempMap;
 30 cananian 1.1.2.1 import harpoon.Util.Util;
 31 cananian 1.1.2.1 
 32 cananian 1.1.2.1 import java.lang.reflect.Modifier;
 33 cananian 1.1.2.1 import java.util.ArrayList;
 34 cananian 1.1.2.4 import java.util.Collections;
 35 cananian 1.1.2.4 import java.util.Comparator;
 36 cananian 1.1.2.1 import java.util.Iterator;
 37 cananian 1.1.2.1 import java.util.List;
 38 cananian 1.1.2.4 import java.util.Map;
 39 cananian 1.1.2.1 import java.util.Set;
 40 cananian 1.1.2.1 /**
 41 cananian 1.1.2.1  * <code>DispatchTreeTransformation</code> replaces dynamic dispatch
 42 cananian 1.1.2.1  * call sites with TYPESWITCHes leading to static dispatch calls.
 43 cananian 1.1.2.1  * Given proper optimization of the TYPESWITCH test, this should
 44 cananian 1.1.2.1  * speed up dispatch.
 45 cananian 1.1.2.1  * 
 46 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 47 cananian 1.5      * @version $Id: DispatchTreeTransformation.java,v 1.5 2004/02/08 05:09:40 cananian Exp $
 48 cananian 1.1.2.1  */
 49 cananian 1.1.2.1 public class DispatchTreeTransformation
 50 cananian 1.1.2.1     extends harpoon.Analysis.Transformation.MethodMutator {
 51 cananian 1.1.2.1     private final static int CUTOFF = 10;
 52 cananian 1.1.2.1 
 53 cananian 1.1.2.1     final ClassHierarchy ch;
 54 cananian 1.1.2.1     
 55 cananian 1.1.2.1     /** Creates a <code>DispatchTreeTransformation</code>. */
 56 cananian 1.1.2.1     public DispatchTreeTransformation(HCodeFactory parent, ClassHierarchy ch) {
 57 cananian 1.1.2.4         // we take in SSI, and output RSSx.
 58 cananian 1.1.2.4         super(harpoon.IR.Quads.QuadSSI.codeFactory(parent));
 59 cananian 1.1.2.1         this.ch = ch;
 60 cananian 1.1.2.1     }
 61 cananian 1.1.2.1     protected HCode mutateHCode(HCodeAndMaps input) {
 62 cananian 1.1.2.4         HCode ahc = input.ancestorHCode();
 63 cananian 1.1.2.1         HCode hc = input.hcode();
 64 cananian 1.3.2.1         assert ahc.getName().equals(harpoon.IR.Quads.QuadSSI.codename);
 65 cananian 1.3.2.1         assert hc.getName().equals(harpoon.IR.Quads.QuadRSSx.codename);
 66 cananian 1.1.2.1         // do a type analysis of the method.
 67 cananian 1.1.2.5         SCCAnalysis scc = new SCCAnalysis(ahc);
 68 cananian 1.1.2.5         ExactTypeMap etm = new ExactTypeMapProxy(input, scc);
 69 cananian 1.1.2.5         new SCCOptimize(etm, 
 70 cananian 1.1.2.5                         new ConstMapProxy(input, scc),
 71 cananian 1.1.2.5                         new ExecMapProxy(input, scc)).optimize(hc);
 72 cananian 1.1.2.1         // now look for CALLs & devirtualize some of them.
 73 cananian 1.1.2.1         for (Iterator it=hc.getElementsI(); it.hasNext(); ) {
 74 cananian 1.1.2.1             Quad q = (Quad) it.next();
 75 cananian 1.1.2.1             if (q instanceof CALL && examineCALL((CALL)q, etm))
 76 cananian 1.1.2.1                 devirtualizeCALL((CALL)q, etm);
 77 cananian 1.1.2.1         }
 78 cananian 1.1.2.1         // done!
 79 cananian 1.1.2.1         return hc;
 80 cananian 1.1.2.4     }
 81 cananian 1.1.2.4     protected HCodeAndMaps cloneHCode(HCode hc, HMethod newmethod) {
 82 cananian 1.1.2.4         // make SSI into RSSx.
 83 cananian 1.3.2.1         assert hc.getName().equals(QuadSSI.codename);
 84 cananian 1.1.2.4         return MyRSSx.cloneToRSSx((harpoon.IR.Quads.Code)hc, newmethod);
 85 cananian 1.1.2.4     }
 86 cananian 1.1.2.4     private static class MyRSSx extends QuadRSSx {
 87 cananian 1.1.2.4         private MyRSSx(HMethod m) { super(m, null); }
 88 cananian 1.1.2.4         public static HCodeAndMaps cloneToRSSx(harpoon.IR.Quads.Code c,
 89 cananian 1.1.2.4                                                HMethod m) {
 90 cananian 1.1.2.4             MyRSSx r = new MyRSSx(m);
 91 cananian 1.1.2.4             return r.cloneHelper(c, r);
 92 cananian 1.1.2.4         }
 93 cananian 1.1.2.4     }
 94 cananian 1.1.2.4     protected String mutateCodeName(String codeName) {
 95 cananian 1.3.2.1         assert codeName.equals(QuadSSI.codename);
 96 cananian 1.1.2.4         return MyRSSx.codename;
 97 cananian 1.1.2.4     }
 98 cananian 1.1.2.1     private boolean examineCALL(CALL call, ExactTypeMap etm) {
 99 cananian 1.1.2.1         // skip if !isVirtual
100 cananian 1.1.2.1         if (!call.isVirtual()) return false;
101 cananian 1.1.2.1         // skip tail calls, too, just cuz they're funky.
102 cananian 1.1.2.1         if (call.isTailCall()) return false;
103 cananian 1.1.2.1         // final methods and methods in final classes are good candidates.
104 cananian 1.1.2.1         if (Modifier.isFinal(call.method().getModifiers()) ||
105 cananian 1.1.2.1             Modifier.isFinal(call.method().getDeclaringClass().getModifiers()))
106 cananian 1.1.2.1             return true;
107 cananian 1.1.2.1         // look at the receiver.
108 cananian 1.1.2.1         Temp recv = call.params(0);
109 cananian 1.1.2.1         // exact types always have just one receiver.
110 cananian 1.1.2.1         if (etm.isExactType(null, recv)) return true;
111 cananian 1.1.2.1         HClass type = etm.typeMap(null, recv);
112 cananian 1.1.2.4         // now we have to count up possible receivers (excl. abstract classes)
113 cananian 1.1.2.4         int n = numChildren(type);
114 cananian 1.1.2.4         if (!Modifier.isAbstract(type.getModifiers())) n++;
115 cananian 1.1.2.1         // okay, figure out if it's worth it.
116 cananian 1.1.2.1         return n < CUTOFF;
117 cananian 1.1.2.1     }
118 cananian 1.1.2.1     // separate method as it's recursive.
119 cananian 1.1.2.1     private int numChildren(HClass hc) {
120 cananian 1.1.2.4         // note that we don't count abstract classes in the total.
121 cananian 1.1.2.1         int n=0;
122 cananian 1.5             for (HClass hcc : ch.children(hc)) {
123 cananian 1.1.2.4             if (!Modifier.isAbstract(hcc.getModifiers())) n += 1;
124 cananian 1.1.2.4             n += numChildren(hcc);
125 cananian 1.1.2.4         }
126 cananian 1.1.2.1         return n;
127 cananian 1.1.2.1     }
128 cananian 1.1.2.1 
129 cananian 1.1.2.1     /** The meat of the transformation. */
130 cananian 1.1.2.1     void devirtualizeCALL(CALL call, ExactTypeMap etm) {
131 cananian 1.1.2.1         QuadFactory qf = call.getFactory();
132 cananian 1.1.2.1         Temp recvr = call.params(0);
133 cananian 1.1.2.1 
134 cananian 1.1.2.1         // find the set of relevant calls.
135 cananian 1.1.2.1         List methods = new ArrayList();
136 cananian 1.1.2.6         { // find the actual method which will be called.
137 cananian 1.1.2.6             HClass hc = etm.typeMap(null, recvr);
138 cananian 1.1.2.6             HMethod declarM = call.method();
139 cananian 1.1.2.6             HMethod actualM = hc.getMethod(declarM.getName(),
140 cananian 1.1.2.6                                            declarM.getDescriptor());
141 cananian 1.1.2.6             methods.add(actualM);
142 cananian 1.1.2.6             if (!etm.isExactType(null, recvr))
143 cananian 1.1.2.6                 methods.addAll(ch.overrides(hc, actualM, true));
144 cananian 1.1.2.6         }
145 cananian 1.1.2.4         // remove uncallable methods.
146 cananian 1.1.2.4         methods.retainAll(ch.callableMethods());
147 cananian 1.1.2.4         // remove interface and abstract methods.
148 cananian 1.1.2.4         for (Iterator it=methods.iterator(); it.hasNext(); ) {
149 cananian 1.1.2.4             HMethod hm = (HMethod) it.next();
150 cananian 1.1.2.4             if (Modifier.isAbstract(hm.getModifiers()) ||
151 cananian 1.1.2.4                 hm.getDeclaringClass().isInterface())
152 cananian 1.1.2.4                 it.remove();
153 cananian 1.1.2.4         }
154 cananian 1.1.2.4         // could be that this method is completely uncallable.  SKIP IT.
155 cananian 1.1.2.4         if (methods.size()==0) return;
156 cananian 1.1.2.4         // now sort methods from most-specific to least-specific
157 cananian 1.1.2.4         Collections.sort(methods, new Comparator() {
158 cananian 1.1.2.4             // ascending order.  smallest is most-specific
159 cananian 1.1.2.4             public int compare(Object o1, Object o2) { // neg if o1 more sp.
160 cananian 1.1.2.4                 HMethod hm1 = (HMethod)o1, hm2 = (HMethod)o2;
161 cananian 1.1.2.4                 HClass hc1=hm1.getDeclaringClass(),hc2=hm2.getDeclaringClass();
162 cananian 1.1.2.4                 return hc1.isInstanceOf(hc2)? -1: hc2.isInstanceOf(hc1)? 1: 0;
163 cananian 1.1.2.4             }
164 cananian 1.1.2.4         });
165 cananian 1.1.2.1 
166 cananian 1.1.2.1         // make devirtualized calls.
167 cananian 1.1.2.1         List spcalls = new ArrayList(methods.size());
168 cananian 1.5             for (Object hmO : methods) {
169 cananian 1.5                 HMethod hm = (HMethod) hmO;
170 cananian 1.1.2.1             // note that the quad will no longer be in SSI form when
171 cananian 1.1.2.1             // we're done because all CALLs have the same sigma functions.
172 cananian 1.1.2.1             CALL ncall = new CALL(qf, call, hm, call.params(),
173 cananian 1.1.2.1                                   call.retval(), call.retex(),
174 cananian 1.1.2.1                                   false /* isVirtual */,
175 cananian 1.1.2.1                                   call.isTailCall(),
176 cananian 1.1.2.1                                   call.dst(), call.src());
177 cananian 1.1.2.1             spcalls.add(ncall);
178 cananian 1.1.2.1         }
179 cananian 1.3.2.1         assert spcalls.size()>0;
180 cananian 1.1.2.1         // make PHI node for regular and exceptional returns.
181 cananian 1.1.2.1         PHI rephi = new PHI(qf, call, new Temp[0], spcalls.size());
182 cananian 1.1.2.1         PHI exphi = new PHI(qf, call, new Temp[0], spcalls.size());
183 cananian 1.1.2.1         // and TYPESWITCH node
184 cananian 1.1.2.3         HClass[] keys = new HClass[methods.size()];
185 cananian 1.1.2.1         for (int i=0; i<keys.length; i++)
186 cananian 1.1.2.1             keys[i] = ((HMethod)methods.get(i)).getDeclaringClass();
187 cananian 1.1.2.1         TYPESWITCH ts = new TYPESWITCH(qf, call, recvr, keys,
188 cananian 1.1.2.3                                        new Temp[0], false/* no default*/);
189 cananian 1.1.2.1         // link everything up.
190 cananian 1.1.2.1         for (int i=0; i<spcalls.size(); i++) {
191 cananian 1.1.2.1             CALL ncall = (CALL) spcalls.get(i);
192 cananian 1.1.2.1             Quad.addEdge(ncall, 0, rephi, i);
193 cananian 1.1.2.1             Quad.addEdge(ncall, 1, exphi, i);
194 cananian 1.1.2.1             Quad.addEdge(ts, i, ncall, 0);
195 cananian 1.1.2.1         }
196 cananian 1.1.2.1         Edge toE = call.prevEdge(0);
197 cananian 1.1.2.1         Edge reE = call.nextEdge(0);
198 cananian 1.1.2.1         Edge exE = call.nextEdge(1);
199 cananian 1.1.2.1         if (spcalls.size()==1) { // don't link TYPESWITCH and PHIs
200 cananian 1.1.2.1             CALL thecall = (CALL)spcalls.get(0);
201 cananian 1.1.2.1             Quad.addEdge((Quad)toE.from(), toE.which_succ(), thecall, 0);
202 cananian 1.1.2.1             Quad.addEdge(thecall, 0, (Quad)reE.to(), reE.which_pred());
203 cananian 1.1.2.1             Quad.addEdge(thecall, 1, (Quad)exE.to(), exE.which_pred());
204 cananian 1.1.2.1         } else { // link up TYPESWITCH and PHIs
205 cananian 1.1.2.1             Quad.addEdge((Quad)toE.from(), toE.which_succ(), ts, 0);
206 cananian 1.1.2.1             Quad.addEdge(rephi, 0, (Quad)reE.to(), reE.which_pred());
207 cananian 1.1.2.1             Quad.addEdge(exphi, 0, (Quad)exE.to(), exE.which_pred());
208 cananian 1.1.2.1         }
209 cananian 1.1.2.1     }
210 cananian 1.2     }