1 cananian 1.1.2.1 // RuntimeMethodCloner.java, created Thu Nov  1 17:45:29 2001 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.Counters;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.1 import harpoon.Analysis.Transformation.MethodMutator;
  7 cananian 1.1.2.1 import harpoon.ClassFile.HClass;
  8 cananian 1.1.2.1 import harpoon.ClassFile.HClassMutator;
  9 cananian 1.1.2.1 import harpoon.ClassFile.HCode;
 10 cananian 1.1.2.1 import harpoon.ClassFile.HCodeAndMaps;
 11 cananian 1.1.2.1 import harpoon.ClassFile.HCodeElement;
 12 cananian 1.1.2.1 import harpoon.ClassFile.HCodeFactory;
 13 cananian 1.1.2.1 import harpoon.ClassFile.HMethod;
 14 cananian 1.1.2.1 import harpoon.ClassFile.HMethodMutator;
 15 cananian 1.1.2.1 import harpoon.ClassFile.Linker;
 16 cananian 1.1.2.1 import harpoon.IR.Quads.CALL;
 17 cananian 1.1.2.3 import harpoon.IR.Quads.CJMP;
 18 cananian 1.1.2.3 import harpoon.IR.Quads.CONST;
 19 cananian 1.1.2.3 import harpoon.IR.Quads.Edge;
 20 cananian 1.1.2.3 import harpoon.IR.Quads.HEADER;
 21 cananian 1.1.2.3 import harpoon.IR.Quads.METHOD;
 22 cananian 1.1.2.3 import harpoon.IR.Quads.OPER;
 23 cananian 1.1.2.3 import harpoon.IR.Quads.PHI;
 24 cananian 1.1.2.3 import harpoon.IR.Quads.Qop;
 25 cananian 1.1.2.1 import harpoon.IR.Quads.Quad;
 26 cananian 1.1.2.3 import harpoon.IR.Quads.QuadFactory;
 27 cananian 1.1.2.3 import harpoon.IR.Quads.QuadSSI;
 28 cananian 1.1.2.3 import harpoon.Temp.Temp;
 29 cananian 1.6     import net.cscott.jutil.GenericInvertibleMultiMap;
 30 cananian 1.6     import net.cscott.jutil.InvertibleMultiMap;
 31 cananian 1.6     import net.cscott.jutil.SnapshotIterator;
 32 cananian 1.1.2.3 import harpoon.Util.Util;
 33 cananian 1.1.2.1 
 34 cananian 1.1.2.1 import java.lang.reflect.Modifier;
 35 cananian 1.1.2.1 import java.util.HashSet;
 36 cananian 1.5     import java.util.Iterator;
 37 cananian 1.1.2.1 import java.util.Map;
 38 cananian 1.1.2.1 import java.util.Set;
 39 cananian 1.1.2.1 
 40 cananian 1.1.2.1 /**
 41 cananian 1.1.2.1  * <code>RuntimeMethodCloner</code> creates 'shadow' copies of all
 42 cananian 1.1.2.1  * non-virtual methods called in <code>harpoon.Runtime.Counters</code>
 43 cananian 1.1.2.1  * and places them in the <code>Counters</code> class.  That way, we
 44 cananian 1.1.2.1  * can easily *not* add statistics to stuff in 
 45 cananian 1.1.2.1  * <code>harpoon.Runtime.Counters</code> to avoid modifying the
 46 cananian 1.1.2.1  * statistics as we're attempting to report them.
 47 cananian 1.1.2.1  * 
 48 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 49 cananian 1.6      * @version $Id: RuntimeMethodCloner.java,v 1.6 2004/02/08 01:51:01 cananian Exp $
 50 cananian 1.1.2.1  */
 51 cananian 1.5     public class RuntimeMethodCloner extends MethodMutator<Quad> {
 52 cananian 1.1.2.1     private static final String classname = "harpoon.Runtime.Counters";
 53 cananian 1.1.2.1     // map old method names to the new methods they now will be.
 54 cananian 1.5         final Map<HMethod,HMethod> old2new;
 55 cananian 1.1.2.1     // methods we'd like to relocate but can't.
 56 cananian 1.5         final Set<HMethod> badboys = new HashSet<HMethod>();
 57 cananian 1.1.2.1     // the linker.
 58 cananian 1.1.2.1     final Linker linker;
 59 cananian 1.1.2.3     // methods to add a null-check on 'this' to.
 60 cananian 1.5         final Set<HMethod> needsNullCheck = new HashSet<HMethod>();
 61 cananian 1.1.2.1     
 62 cananian 1.1.2.1     /** Creates a <code>RuntimeMethodCloner</code>. */
 63 cananian 1.1.2.1     public RuntimeMethodCloner(final HCodeFactory parent, Linker linker) {
 64 cananian 1.5             this(parent, linker, new GenericInvertibleMultiMap<HMethod,HMethod>());
 65 cananian 1.1.2.1     }
 66 cananian 1.1.2.1     private RuntimeMethodCloner(final HCodeFactory parent, Linker linker,
 67 cananian 1.1.2.1                                 // i'd like new2old to be an InvertibleMap,
 68 cananian 1.1.2.1                                 // but the type declarations aren't cooperating
 69 cananian 1.5                                     final InvertibleMultiMap<HMethod,HMethod> new2old) {
 70 cananian 1.1.2.1         super(new HCodeFactory() {
 71 cananian 1.1.2.1                 public void clear(HMethod m) { parent.clear(m); }
 72 cananian 1.1.2.1                 public String getCodeName() { return parent.getCodeName(); }
 73 cananian 1.1.2.1                 public HCode convert(HMethod m) {
 74 cananian 1.1.2.1                     if (!new2old.containsKey(m))
 75 cananian 1.1.2.1                         return parent.convert(m);
 76 cananian 1.1.2.1                     // this is a new one for us.
 77 cananian 1.5                         HMethod old = new2old.get(m);
 78 cananian 1.1.2.1                     HCode hc = convert(old);
 79 cananian 1.1.2.1                     try {
 80 cananian 1.1.2.1                         return hc.clone(m).hcode();
 81 cananian 1.1.2.1                     } catch (CloneNotSupportedException ex) {
 82 cananian 1.1.2.1                         // XXX: we don't allow this.
 83 cananian 1.1.2.1                         throw new RuntimeException(ex.toString());
 84 cananian 1.1.2.1                     }
 85 cananian 1.1.2.1                 }
 86 cananian 1.1.2.1             });
 87 cananian 1.1.2.1         this.linker = linker;
 88 cananian 1.1.2.1         this.old2new = new2old.invert();
 89 cananian 1.1.2.1     }
 90 cananian 1.5         protected HCode<Quad> mutateHCode(HCodeAndMaps<Quad> input) {
 91 cananian 1.5             HCode<Quad> hc = input.hcode();
 92 cananian 1.1.2.1         // unless this belongs to harpoon.Runtime.Counters, we ignore it.
 93 cananian 1.1.2.1         if (!hc.getMethod().getDeclaringClass().getName().equals(classname))
 94 cananian 1.1.2.1             return hc;
 95 cananian 1.1.2.1         // otherwise, we look for appropriate calls and rewrite them.
 96 cananian 1.5             for (Iterator<Quad> it=new SnapshotIterator<Quad>(hc.getElementsI());
 97 cananian 1.5                  it.hasNext(); ) {
 98 cananian 1.5                 Quad q = it.next();
 99 cananian 1.5                 if (q instanceof CALL) {
100 cananian 1.5                     CALL call = (CALL) q;
101 cananian 1.1.2.1                 // do we want to relocate calls to this method?
102 cananian 1.1.2.1                 if (!shouldRelocate(call.method()))
103 cananian 1.1.2.1                     continue;
104 cananian 1.1.2.1                 // can we?  [we will complain about virtual calls]
105 cananian 1.1.2.1                 // (note that Nonvirtualize is not invoked on methods
106 cananian 1.1.2.1                 //  in the Counters class, so we have to do our own
107 cananian 1.1.2.1                 //  final method nonvirtualization.)
108 cananian 1.1.2.1                 if (call.isVirtual() && !isFinal(call.method())) {
109 cananian 1.1.2.1                     if (!badboys.contains(call.method()))
110 cananian 1.1.2.1                         System.err.println("WARNING: can't relocate "+
111 cananian 1.1.2.1                                            call.method()+" at "+
112 cananian 1.1.2.2                                            hc.getMethod()+" "+
113 cananian 1.1.2.1                                            call.getSourceFile()+":"+
114 cananian 1.1.2.1                                            call.getLineNumber());
115 cananian 1.1.2.1                     badboys.add(call.method());
116 cananian 1.1.2.1                     continue;
117 cananian 1.1.2.1                 }
118 cananian 1.1.2.1                 // okay, relocate this one.
119 cananian 1.1.2.1                 HMethod nm = makeNewMethod(call.method());
120 cananian 1.1.2.1                 Quad.replace(call, new CALL(call.getFactory(), call, nm,
121 cananian 1.1.2.1                                             call.params(), call.retval(),
122 cananian 1.1.2.1                                             call.retex(), false/*non-virtual*/,
123 cananian 1.1.2.1                                             call.isTailCall(),
124 cananian 1.1.2.1                                             call.dst(), call.src()));
125 cananian 1.1.2.1                 // whoo-hoo! on to the next!
126 cananian 1.1.2.1             }
127 cananian 1.5             }
128 cananian 1.1.2.3         // when we relocate a method here, we make it static.  that
129 cananian 1.1.2.3         // means that analyses can't automatically determine that the
130 cananian 1.1.2.3         // 'this' pointer is non-null.  Insert an explicit test to
131 cananian 1.1.2.3         // inform these methods what's what.
132 cananian 1.1.2.3         if (needsNullCheck.contains(hc.getMethod())) {
133 cananian 1.3.2.1             assert !hc.getName().equals(QuadSSI.codename);
134 cananian 1.1.2.3             HEADER header = (HEADER) hc.getRootElement();
135 cananian 1.1.2.3             METHOD method = header.method();
136 cananian 1.1.2.3             QuadFactory qf = method.getFactory();
137 cananian 1.1.2.3             Temp nulT = new Temp(qf.tempFactory(), "nullconst");
138 cananian 1.1.2.3             Temp chkT = new Temp(qf.tempFactory(), "nullcheck");
139 cananian 1.1.2.3             Quad q0 = new CONST(qf, method, nulT, null, HClass.Void);
140 cananian 1.1.2.3             Quad q1 = new OPER(qf, method, Qop.ACMPEQ, chkT,
141 cananian 1.1.2.3                                new Temp[] { nulT, method.params(0) });
142 cananian 1.1.2.3             Quad q2 = new CJMP(qf, method, chkT, new Temp[0]);
143 cananian 1.1.2.3             Quad q3 = new PHI(qf, method, new Temp[0], 2);
144 cananian 1.1.2.3             Edge e = method.nextEdge(0);
145 cananian 1.5                 Quad.addEdge(e.from(), e.which_succ(), q0, 0);
146 cananian 1.1.2.3             Quad.addEdges(new Quad[] { q0, q1, q2 });
147 cananian 1.5                 Quad.addEdge(q2, 0, e.to(), e.which_pred());
148 cananian 1.1.2.3             Quad.addEdge(q2, 1, q3, 0);
149 cananian 1.1.2.3             Quad.addEdge(q3, 0, q3, 1);
150 cananian 1.1.2.3             // not in SSI form. (in valid SSA/RSSx/NoSSx form, though).
151 cananian 1.1.2.3         }
152 cananian 1.1.2.1         // done!
153 cananian 1.1.2.1         return hc;
154 cananian 1.1.2.1     }
155 cananian 1.1.2.1     // check whether method or class is final.
156 cananian 1.1.2.1     private static boolean isFinal(HMethod hm) {
157 cananian 1.1.2.1         return Modifier.isFinal(hm.getModifiers()) ||
158 cananian 1.1.2.1             Modifier.isFinal(hm.getDeclaringClass().getModifiers());
159 cananian 1.1.2.1     }
160 cananian 1.1.2.1     // a variety of empirical criterial for relocating a method.
161 cananian 1.1.2.1     private boolean shouldRelocate(HMethod hm) {
162 cananian 1.1.2.1         // don't relocate ourselves!
163 cananian 1.1.2.1         if (hm.getDeclaringClass().getName().equals(classname))
164 cananian 1.1.2.1             return false;
165 cananian 1.1.2.1         // native methods are safe to skip.
166 cananian 1.1.2.1         if (Modifier.isNative(hm.getModifiers())) return false;
167 cananian 1.1.2.1         // let's assume exceptions never occur.
168 cananian 1.1.2.1         if (hm.getDeclaringClass().isInstanceOf
169 cananian 1.1.2.1             (linker.forName("java.lang.Throwable"))) return false;
170 cananian 1.1.2.1         // okay, we'll relocate everything else.
171 cananian 1.1.2.1         return true;
172 cananian 1.1.2.1     }
173 cananian 1.1.2.1     // create new method of harpoon.Runtime.Counters for given method.
174 cananian 1.1.2.1     private HMethod makeNewMethod(HMethod hm) {
175 cananian 1.1.2.1         if (!old2new.containsKey(hm)) {
176 cananian 1.1.2.1             HClass hc = linker.forName(classname);
177 cananian 1.1.2.1             // come up w/ uniq name for new method.
178 cananian 1.1.2.1             String name = "XXX."+
179 cananian 1.1.2.1                 hm.getDeclaringClass().getName()+".."+hm.getName();
180 cananian 1.1.2.1             name = replace(name, "_", "_1");
181 cananian 1.1.2.1             name = replace(name, ".", "_");
182 cananian 1.1.2.1             // come up with new descriptor.
183 cananian 1.1.2.1             String desc = hm.getDescriptor();
184 cananian 1.1.2.1             if (!hm.isStatic()) {
185 cananian 1.1.2.1                 // descriptor for original declaring class.
186 cananian 1.1.2.1                 String newpart = hm.getDeclaringClass().getDescriptor();
187 cananian 1.1.2.1                 // insert it after open paren.
188 cananian 1.1.2.1                 desc = replace(desc, "(", "("+newpart);
189 cananian 1.1.2.1             }
190 cananian 1.1.2.1             // create method.
191 cananian 1.1.2.1             HMethod newm = hc.getMutator().addDeclaredMethod(name, desc);
192 cananian 1.1.2.1             // make it static (and public, just for kicks)
193 cananian 1.1.2.1             newm.getMutator().addModifiers(Modifier.STATIC|Modifier.PUBLIC);
194 cananian 1.1.2.3             // methods we've just made static need null-checks added on 'this'
195 cananian 1.1.2.3             if (!hm.isStatic()) needsNullCheck.add(newm);
196 cananian 1.1.2.1             // done! add it to the cache.
197 cananian 1.1.2.1             old2new.put(hm, newm);
198 cananian 1.1.2.1         }
199 cananian 1.5             return old2new.get(hm);
200 cananian 1.1.2.1     }
201 cananian 1.1.2.1     private static String replace(String s, String oldstr, String newstr) {
202 cananian 1.1.2.1         StringBuffer sb = new StringBuffer();
203 cananian 1.1.2.1         while (true) {
204 cananian 1.1.2.1             // find oldstr
205 cananian 1.1.2.1             int idx = s.indexOf(oldstr);
206 cananian 1.1.2.1             // if not found, then done.
207 cananian 1.1.2.1             if (idx<0) break;
208 cananian 1.1.2.1             // split at idx
209 cananian 1.1.2.1             sb.append(s.substring(0, idx));
210 cananian 1.1.2.1             s = s.substring(idx+oldstr.length());
211 cananian 1.1.2.1             // add newstr.
212 cananian 1.1.2.1             sb.append(newstr);
213 cananian 1.1.2.1         }
214 cananian 1.1.2.1         sb.append(s);
215 cananian 1.1.2.1         return sb.toString();
216 cananian 1.1.2.1     }
217 cananian 1.1.2.1 }
218 cananian 1.2