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