1 kkz 1.1.2.1 // MRAFactory.java, created Sat Oct 13 19:47:53 2001 by kkz 2 kkz 1.1.2.1 // Copyright (C) 2000 Karen Zee <kkz@tmi.lcs.mit.edu> 3 kkz 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details. 4 kkz 1.1.2.1 package harpoon.Analysis.PreciseGC; 5 kkz 1.1.2.1 6 kkz 1.1.2.1 import harpoon.Analysis.BasicBlock; 7 kkz 1.1.2.1 import harpoon.Analysis.ClassHierarchy; 8 kkz 1.7 import harpoon.Analysis.Quads.CachingCallGraph; 9 kkz 1.1.2.4 import harpoon.Analysis.Quads.CallGraph; 10 kkz 1.1.2.4 import harpoon.Analysis.Quads.CallGraphImpl; 11 kkz 1.1.2.1 import harpoon.ClassFile.CachingCodeFactory; 12 kkz 1.1.2.1 import harpoon.ClassFile.HCode; 13 kkz 1.1.2.1 import harpoon.ClassFile.HCodeFactory; 14 kkz 1.1.2.1 import harpoon.ClassFile.HMethod; 15 kkz 1.1.2.2 import harpoon.ClassFile.Linker; 16 kkz 1.1.2.1 import harpoon.IR.Properties.CFGrapher; 17 kkz 1.1.2.1 import harpoon.IR.Properties.UseDefer; 18 kkz 1.1.2.1 import harpoon.IR.Quads.AGET; 19 kkz 1.1.2.1 import harpoon.IR.Quads.ANEW; 20 kkz 1.1.2.1 import harpoon.IR.Quads.ARRAYINIT; 21 kkz 1.1.2.1 import harpoon.IR.Quads.ASET; 22 kkz 1.1.2.1 import harpoon.IR.Quads.CALL; 23 kkz 1.1.2.1 import harpoon.IR.Quads.CJMP; 24 kkz 1.1.2.1 import harpoon.IR.Quads.CONST; 25 kkz 1.1.2.1 import harpoon.IR.Quads.Code; 26 kkz 1.1.2.1 import harpoon.IR.Quads.Edge; 27 kkz 1.1.2.1 import harpoon.IR.Quads.FOOTER; 28 kkz 1.1.2.1 import harpoon.IR.Quads.GET; 29 kkz 1.1.2.1 import harpoon.IR.Quads.HEADER; 30 kkz 1.1.2.1 import harpoon.IR.Quads.INSTANCEOF; 31 kkz 1.1.2.1 import harpoon.IR.Quads.METHOD; 32 kkz 1.1.2.1 import harpoon.IR.Quads.MONITORENTER; 33 kkz 1.1.2.1 import harpoon.IR.Quads.MONITOREXIT; 34 kkz 1.1.2.1 import harpoon.IR.Quads.MOVE; 35 kkz 1.1.2.1 import harpoon.IR.Quads.NEW; 36 kkz 1.1.2.1 import harpoon.IR.Quads.NOP; 37 kkz 1.1.2.1 import harpoon.IR.Quads.OPER; 38 kkz 1.1.2.1 import harpoon.IR.Quads.PHI; 39 kkz 1.1.2.1 import harpoon.IR.Quads.Qop; 40 kkz 1.1.2.1 import harpoon.IR.Quads.Quad; 41 kkz 1.1.2.1 import harpoon.IR.Quads.QuadKind; 42 kkz 1.1.2.1 import harpoon.IR.Quads.QuadVisitor; 43 kkz 1.1.2.1 import harpoon.IR.Quads.QuadFactory; 44 kkz 1.1.2.1 import harpoon.IR.Quads.RETURN; 45 kkz 1.1.2.1 import harpoon.IR.Quads.SET; 46 kkz 1.1.2.1 import harpoon.IR.Quads.SIGMA; 47 kkz 1.1.2.1 import harpoon.IR.Quads.THROW; 48 kkz 1.1.2.1 import harpoon.IR.Quads.TYPESWITCH; 49 kkz 1.1.2.1 import harpoon.Temp.Temp; 50 kkz 1.7 import harpoon.Util.Collections.WorkSet; 51 cananian 1.8 import net.cscott.jutil.Default; 52 kkz 1.1.2.2 import harpoon.Util.ParseUtil; 53 kkz 1.1.2.4 import harpoon.Util.Tuple; 54 kkz 1.1.2.1 import harpoon.Util.Util; 55 kkz 1.1.2.1 import harpoon.Util.Worklist; 56 kkz 1.1.2.1 57 kkz 1.1.2.1 import java.util.Collections; 58 kkz 1.1.2.1 import java.util.HashMap; 59 kkz 1.1.2.1 import java.util.HashSet; 60 kkz 1.1.2.1 import java.util.Iterator; 61 kkz 1.1.2.1 import java.util.List; 62 kkz 1.1.2.1 import java.util.Map; 63 kkz 1.1.2.1 import java.util.Set; 64 kkz 1.1.2.1 65 kkz 1.1.2.1 /** 66 kkz 1.1.2.1 * <code>MRAFactory</code> generates <code>MRA</code>s. 67 kkz 1.1.2.1 * 68 kkz 1.1.2.1 * @author Karen Zee <kkz@tmi.lcs.mit.edu> 69 cananian 1.9 * @version $Id: MRAFactory.java,v 1.9 2004/02/08 03:20:07 cananian Exp $ 70 kkz 1.1.2.1 */ 71 kkz 1.1.2.1 public class MRAFactory { 72 kkz 1.1.2.1 73 kkz 1.7 //private static int iterations = 0; 74 kkz 1.7 //private static long time = 0; 75 kkz 1.7 76 kkz 1.1.2.1 private final ClassHierarchy ch; 77 kkz 1.7 private CallGraph cg; 78 kkz 1.1.2.4 private final HCodeFactory hcf; 79 kkz 1.1.2.5 private final Map method2types; 80 kkz 1.1.2.5 private Set safeMethods; 81 kkz 1.1.2.1 private final CFGrapher cfger; 82 kkz 1.1.2.1 private final UseDefer ud; 83 kkz 1.1.2.1 private final Map cache; 84 kkz 1.1.2.1 85 kkz 1.7 /** Creates an <code>MRAFactory</code>. Requires a 86 kkz 1.7 * quad-ssa or quad-ssi codefactory. If the code is 87 kkz 1.1.2.1 * modified, a new <code>MRAFactory</code> is needed. 88 kkz 1.1.2.4 * For efficiency reasons, <code>hcf</code> should be 89 kkz 1.1.2.4 * a <code>CachingCodeFactory</code>. 90 kkz 1.1.2.1 */ 91 kkz 1.1.2.2 public MRAFactory(ClassHierarchy ch, HCodeFactory hcf, Linker l, 92 kkz 1.1.2.5 String rName, int optLevel) { 93 kkz 1.7 // check that the codefactory produces the right type of code 94 kkz 1.7 /* 95 kkz 1.7 assert 96 kkz 1.7 hcf.getCodeName().equals("quad-ssi") || 97 kkz 1.7 hcf.getCodeName().equals("quad-ssa") : 98 kkz 1.7 "MRAFactory requires quad-ssi or quad-ssa"; 99 kkz 1.7 */ 100 kkz 1.1.2.1 this.ch = ch; 101 kkz 1.1.2.4 this.hcf = hcf; 102 kkz 1.7 this.cg = new CachingCallGraph(new CallGraphImpl(ch, hcf)); 103 kkz 1.1.2.1 this.cfger = CFGrapher.DEFAULT; 104 kkz 1.1.2.1 this.ud = UseDefer.DEFAULT; 105 kkz 1.1.2.1 this.cache = new HashMap(); 106 kkz 1.7 /* 107 kkz 1.7 System.out.print("\nMethod->Types timing: "); 108 kkz 1.7 long start_time = System.currentTimeMillis(); 109 kkz 1.7 */ 110 kkz 1.1.2.5 this.method2types = 111 kkz 1.1.2.5 (optLevel == 2 || optLevel == 3)? dynamicDispatchM2TMap(l, rName): 112 kkz 1.5 (optLevel == 4 || optLevel == 5)? createMethod2TypesMap(l, rName): 113 kkz 1.7 Default.EMPTY_MAP; 114 kkz 1.7 /* 115 kkz 1.7 System.out.println(System.currentTimeMillis() - start_time); 116 kkz 1.7 System.out.print("Safe methods timing: "); 117 kkz 1.7 start_time = System.currentTimeMillis(); 118 kkz 1.7 */ 119 kkz 1.5 if (optLevel == 2 || optLevel == 4 || optLevel == 6) 120 kkz 1.1.2.5 findSafeMethods(); 121 kkz 1.1.2.5 else 122 kkz 1.1.2.5 safeMethods = Collections.EMPTY_SET; 123 kkz 1.7 /* 124 kkz 1.7 System.out.println(System.currentTimeMillis() - start_time + " ms"); 125 kkz 1.7 System.out.println(MRAFactory.iterations + " iterations"); 126 kkz 1.7 System.out.print("Analysis timing: "); 127 kkz 1.7 int numMethods = 0; 128 kkz 1.7 start_time = System.currentTimeMillis(); 129 kkz 1.7 */ 130 kkz 1.7 // for timing reasons, force analysis to run 131 kkz 1.7 for(Iterator it = ch.callableMethods().iterator(); it.hasNext(); ) 132 kkz 1.7 { 133 kkz 1.7 Code c = (Code) hcf.convert((HMethod)it.next()); 134 kkz 1.7 if (c != null) { 135 kkz 1.7 mra(c); 136 kkz 1.7 //numMethods++; 137 kkz 1.7 } 138 kkz 1.7 } 139 kkz 1.7 /* 140 kkz 1.7 System.out.println(System.currentTimeMillis() - start_time + " ms"); 141 kkz 1.7 System.out.println(MRAFactory.iterations + " iterations"); 142 kkz 1.7 System.out.println(numMethods + " methods"); 143 kkz 1.7 System.out.println("getPre outer loop: " + MRAFactory.time + " ms"); 144 kkz 1.7 */ 145 kkz 1.7 this.cg = null; /* allow GC */ 146 kkz 1.1.2.1 } 147 kkz 1.1.2.1 148 kkz 1.1.2.1 /** Returns an <code>MRA</code>. */ 149 kkz 1.1.2.1 public MRA mra(Code c) { 150 kkz 1.1.2.1 // check cache first 151 kkz 1.1.2.1 HMethod hm = c.getMethod(); 152 kkz 1.1.2.1 MRA mra = (MRA) cache.get(c); 153 kkz 1.1.2.1 if (mra == null) { 154 kkz 1.1.2.1 mra = new MRAImpl(c); 155 kkz 1.1.2.1 cache.put(c, mra); 156 kkz 1.1.2.1 } 157 kkz 1.1.2.1 return mra; 158 kkz 1.1.2.1 } 159 kkz 1.1.2.1 160 kkz 1.1.2.4 /** 161 kkz 1.1.2.4 * Removes representation of <code>Code</code> 162 kkz 1.1.2.4 * <code>c</code> from this factory. 163 kkz 1.1.2.4 */ 164 kkz 1.1.2.4 public void clear(Code c) { 165 kkz 1.1.2.4 Object o = cache.remove(c); 166 cananian 1.3.2.1 assert (o != null) : "Failed to remove "+c.getMethod(); 167 kkz 1.1.2.4 } 168 kkz 1.1.2.4 169 kkz 1.1.2.5 /** Checks whether a method is "safe" (i.e. whether all 170 kkz 1.1.2.5 * calls to a method occurs when the receiver object is 171 kkz 1.1.2.5 * the most recently allocated object.) 172 kkz 1.1.2.2 */ 173 kkz 1.1.2.5 public boolean isSafeMethod(HMethod hm) { 174 kkz 1.1.2.5 return safeMethods.contains(hm); 175 kkz 1.1.2.2 } 176 kkz 1.1.2.2 177 kkz 1.1.2.5 /** Checks whether the types that the <code>HMethod</code> 178 kkz 1.1.2.5 * may allocate are known. 179 kkz 1.1.2.4 */ 180 kkz 1.1.2.5 public boolean allocatedTypesKnown(HMethod hm) { 181 kkz 1.1.2.5 return method2types.containsKey(hm); 182 kkz 1.1.2.4 } 183 kkz 1.1.2.4 184 kkz 1.1.2.4 /** Returns an unmodifiable <code>Set</code> of 185 kkz 1.1.2.4 * <code>HClass</code>es. The presence of an 186 kkz 1.1.2.4 * <code>HClass</code> in the <code>Set</code> indicates 187 kkz 1.1.2.4 * that the given <code>HMethod</code> may allocate, 188 kkz 1.1.2.4 * either directly or through calls, objects of that type. 189 kkz 1.1.2.4 */ 190 kkz 1.1.2.5 public Set getAllocatedTypes(HMethod hm) { 191 kkz 1.1.2.5 return (Set) method2types.get(hm); 192 kkz 1.1.2.1 } 193 kkz 1.1.2.1 194 kkz 1.7 private Map tryDDMethod2TypesMap(Linker linker, String resourceName) { 195 kkz 1.7 // start w/ an empty map 196 kkz 1.7 Map safe = new HashMap(); 197 kkz 1.7 // add known native methods 198 kkz 1.7 for (Iterator it = parseResource(linker, resourceName).iterator(); 199 kkz 1.7 it.hasNext(); ) 200 kkz 1.7 safe.put((HMethod)it.next(), Collections.EMPTY_SET); 201 kkz 1.7 // add leaves of call graph 202 kkz 1.7 QuadVisitor qv = new QuadVisitor() { 203 kkz 1.7 Set types = new HashSet(); 204 kkz 1.7 205 kkz 1.7 public void visit(ANEW q) { types.add(q.hclass()); } 206 kkz 1.7 public void visit(NEW q) { types.add(q.hclass()); } 207 kkz 1.7 public void visit(MONITORENTER q) { assert false: 208 kkz 1.7 "should not contain " + q; } 209 kkz 1.7 public void visit(Quad q) { /* do nothing */ } 210 kkz 1.7 }; 211 kkz 1.7 return safe; 212 kkz 1.7 } 213 kkz 1.7 214 kkz 1.1.2.2 /** Creates a <code>Map</code> of <code>HMethod</code>s 215 kkz 1.1.2.2 * to the <code>Set</code> of <code>Classes</code> 216 kkz 1.1.2.2 * whose objects may be allocated by the method either 217 kkz 1.1.2.2 * directly or indirectly through calling other methods. 218 kkz 1.1.2.5 * Handles dynamically-dispatched calls by conservative 219 kkz 1.1.2.5 * approximation. 220 kkz 1.1.2.5 */ 221 kkz 1.1.2.5 private Map dynamicDispatchM2TMap(Linker linker, String resourceName) { 222 kkz 1.1.2.5 // start with an empty map 223 kkz 1.1.2.5 Map safe = new HashMap(); 224 kkz 1.1.2.5 // add methods unless we have no information about them 225 cananian 1.9 for (Object hmO : ch.callableMethods()) { 226 cananian 1.9 HMethod hm = (HMethod) hmO; 227 kkz 1.1.2.5 Code c = (Code) hcf.convert(hm); 228 kkz 1.1.2.5 if (c != null) { 229 kkz 1.1.2.5 Set cls = new HashSet(); 230 kkz 1.1.2.5 safe.put(hm, cls); 231 kkz 1.1.2.5 for (Iterator stms = c.getElementsI(); stms.hasNext(); ) { 232 kkz 1.1.2.5 Quad q = (Quad) stms.next(); 233 kkz 1.1.2.5 int kind = q.kind(); 234 kkz 1.1.2.5 if (kind == QuadKind.ANEW) { 235 kkz 1.1.2.5 cls.add(((ANEW)q).hclass()); 236 kkz 1.1.2.5 } else if (kind == QuadKind.NEW) { 237 kkz 1.1.2.5 cls.add(((NEW)q).hclass()); 238 kkz 1.5 } else if (kind == QuadKind.MONITORENTER) { 239 kkz 1.5 // for race-free multi-threaded programs 240 kkz 1.5 safe.remove(hm); 241 kkz 1.5 break; 242 kkz 1.1.2.5 } 243 kkz 1.1.2.5 } 244 kkz 1.1.2.5 } 245 kkz 1.1.2.5 } 246 kkz 1.1.2.5 // add known native methods 247 kkz 1.1.2.5 for (Iterator it = parseResource(linker, resourceName).iterator(); 248 kkz 1.1.2.5 it.hasNext(); ) { 249 kkz 1.1.2.5 safe.put((HMethod)it.next(), Collections.EMPTY_SET); 250 kkz 1.1.2.5 } 251 kkz 1.1.2.5 // save results 252 kkz 1.1.2.5 safe = Collections.unmodifiableMap(safe); 253 kkz 1.1.2.5 // start with the map so far 254 kkz 1.1.2.5 Map safer = new HashMap(safe); 255 kkz 1.1.2.5 // iterate until fixed point to remove any that 256 kkz 1.1.2.5 // are unsafe by calling unsafe methods 257 kkz 1.1.2.5 while(true) { 258 kkz 1.1.2.5 boolean changed = false; 259 kkz 1.1.2.5 boolean done = false; 260 cananian 1.9 for (Object hmO : safe.keySet()) { 261 cananian 1.9 HMethod hm = (HMethod) hmO; 262 kkz 1.1.2.5 // get all the call sites in hm 263 kkz 1.1.2.5 CALL[] calls = cg.getCallSites(hm); 264 kkz 1.1.2.5 for (int i = 0; i < calls.length; i++) { 265 kkz 1.1.2.5 // there are multiple possible callees here 266 kkz 1.1.2.5 HMethod[] callees = cg.calls(hm, calls[i]); 267 kkz 1.1.2.5 for(int j = 0; j < callees.length && !done; j++) { 268 kkz 1.1.2.5 HMethod callee = callees[j]; 269 kkz 1.1.2.5 if (!safe.containsKey(callee)) { 270 kkz 1.1.2.5 // if the method being called isn't in the 271 kkz 1.1.2.5 // safe set, then the caller is also unsafe 272 kkz 1.1.2.5 safer.remove(hm); 273 kkz 1.1.2.5 changed = true; 274 kkz 1.1.2.5 done = true; // done with this method 275 kkz 1.1.2.5 break; 276 kkz 1.1.2.5 } else { 277 kkz 1.1.2.5 // if the method being called is safe, then 278 kkz 1.1.2.5 // add its classes to the set of known classes 279 kkz 1.1.2.5 // being allocated 280 kkz 1.1.2.5 if (((Set)safer.get(hm)).addAll 281 kkz 1.1.2.5 ((Set)safe.get(callee))) 282 kkz 1.1.2.5 changed = true; 283 kkz 1.1.2.5 } 284 kkz 1.1.2.5 } 285 kkz 1.1.2.5 } 286 kkz 1.1.2.5 } 287 kkz 1.1.2.5 if (changed) { 288 kkz 1.1.2.5 // should be monotonic 289 cananian 1.3.2.1 assert safer.size() <= safe.size(); 290 kkz 1.1.2.5 safe = Collections.unmodifiableMap(safer); 291 kkz 1.1.2.5 safer = new HashMap(safe); 292 kkz 1.1.2.5 } else { 293 kkz 1.1.2.5 // reached fix point 294 cananian 1.3.2.1 assert safer.size() == safe.size(); 295 kkz 1.1.2.5 break; 296 kkz 1.1.2.5 } 297 kkz 1.1.2.5 } 298 kkz 1.1.2.5 // get a safe view of the keys 299 kkz 1.1.2.5 Set keys = Collections.unmodifiableSet(new HashSet(safer.keySet())); 300 kkz 1.1.2.5 // save results 301 cananian 1.9 for (Object hmO : keys) { 302 cananian 1.9 HMethod hm = (HMethod) hmO; 303 kkz 1.1.2.5 safer.put(hm, Collections.unmodifiableSet((Set)safer.get(hm))); 304 kkz 1.1.2.5 } 305 kkz 1.1.2.5 return Collections.unmodifiableMap(safer); 306 kkz 1.1.2.5 } 307 kkz 1.1.2.5 308 kkz 1.1.2.5 /** Creates a <code>Map</code> of <code>HMethod</code>s 309 kkz 1.1.2.5 * to the <code>Set</code> of <code>Classes</code> 310 kkz 1.1.2.5 * whose objects may be allocated by the method either 311 kkz 1.1.2.5 * directly or indirectly through calling other methods. 312 kkz 1.1.2.2 * <code>HMethods</code> that do not allocate any 313 kkz 1.1.2.2 * objects map to the empty set, while 314 kkz 1.1.2.2 * <code>HMethod</code>s that are entirely unsafe due 315 kkz 1.1.2.2 * to the presence of virtually-dispatched calls will 316 kkz 1.1.2.2 * be absent from the map (i.e. map to null). 317 kkz 1.1.2.1 */ 318 kkz 1.1.2.5 private Map createMethod2TypesMap(Linker linker, String resourceName) { 319 kkz 1.1.2.4 // start with an empty map 320 kkz 1.1.2.4 Map safe = new HashMap(); 321 kkz 1.1.2.4 // add methods unless we know they are unsafe because of 322 kkz 1.1.2.4 // dynamic dispatch or if we have no information because 323 kkz 1.1.2.4 // it is native or abstract 324 cananian 1.9 for (Object hmO : ch.callableMethods()) { 325 cananian 1.9 HMethod hm = (HMethod) hmO; 326 kkz 1.1.2.4 Code c = (Code) hcf.convert(hm); 327 kkz 1.1.2.2 if (c != null) { 328 kkz 1.1.2.4 Set cls = new HashSet(); 329 kkz 1.1.2.4 safe.put(hm, cls); 330 kkz 1.1.2.2 for (Iterator stms = c.getElementsI(); stms.hasNext(); ) { 331 kkz 1.1.2.2 Quad q = (Quad) stms.next(); 332 kkz 1.1.2.4 int kind = q.kind(); 333 kkz 1.5 if ((kind == QuadKind.CALL && ((CALL)q).isVirtual()) || 334 kkz 1.5 kind == QuadKind.MONITORENTER) { 335 kkz 1.5 // remove if unsafe due to dynamic dispatch or 336 kkz 1.5 // if unsafe due to interthread interactions 337 kkz 1.1.2.4 safe.remove(hm); 338 kkz 1.1.2.2 break; 339 kkz 1.1.2.4 } else if (kind == QuadKind.ANEW) { 340 kkz 1.1.2.4 cls.add(((ANEW)q).hclass()); 341 kkz 1.1.2.4 } else if (kind == QuadKind.NEW) { 342 kkz 1.1.2.4 cls.add(((NEW)q).hclass()); 343 kkz 1.1.2.2 } 344 kkz 1.1.2.1 } 345 kkz 1.1.2.1 } 346 kkz 1.1.2.1 } 347 kkz 1.1.2.4 // add known native methods 348 kkz 1.1.2.4 for (Iterator it = parseResource(linker, resourceName).iterator(); 349 kkz 1.1.2.4 it.hasNext(); ) { 350 kkz 1.1.2.4 safe.put((HMethod)it.next(), Collections.EMPTY_SET); 351 kkz 1.1.2.4 } 352 kkz 1.1.2.4 // save results 353 kkz 1.1.2.4 safe = Collections.unmodifiableMap(safe); 354 kkz 1.1.2.4 // start with the map so far 355 kkz 1.1.2.4 Map safer = new HashMap(safe); 356 kkz 1.1.2.4 // iterate until fixed point to remove any that 357 kkz 1.1.2.4 // are unsafe by calling unsafe methods 358 kkz 1.1.2.4 while(true) { 359 kkz 1.1.2.4 boolean changed = false; 360 cananian 1.9 for (Object hmO : safe.keySet()) { 361 cananian 1.9 HMethod hm = (HMethod) hmO; 362 kkz 1.1.2.4 CALL[] calls = cg.getCallSites(hm); 363 kkz 1.1.2.4 for (int i = 0; i < calls.length; i++) { 364 kkz 1.1.2.4 HMethod callee = calls[i].method(); 365 kkz 1.1.2.4 if (!safe.containsKey(callee)) { 366 kkz 1.1.2.4 // if the method being called isn't in the safe set, 367 kkz 1.1.2.4 // then the caller is also unsafe 368 kkz 1.1.2.4 safer.remove(hm); 369 kkz 1.1.2.4 changed = true; 370 kkz 1.1.2.4 break; 371 kkz 1.1.2.4 } else { 372 kkz 1.1.2.4 // if the method being called is safe, then add its 373 kkz 1.1.2.4 // classes to the set of known classes being allocated 374 kkz 1.1.2.4 if (((Set)safer.get(hm)).addAll((Set)safe.get(callee))) 375 kkz 1.1.2.4 changed = true; 376 kkz 1.1.2.4 } 377 kkz 1.1.2.4 } 378 kkz 1.1.2.4 } 379 kkz 1.1.2.4 if (changed) { 380 kkz 1.1.2.4 // should be monotonic 381 cananian 1.3.2.1 assert safer.size() < safe.size(); 382 kkz 1.1.2.4 safe = Collections.unmodifiableMap(safer); 383 kkz 1.1.2.4 safer = new HashMap(safe); 384 kkz 1.1.2.4 } else { 385 kkz 1.1.2.4 // reached fix point 386 cananian 1.3.2.1 assert safer.size() == safe.size(); 387 kkz 1.1.2.4 break; 388 kkz 1.1.2.4 } 389 kkz 1.1.2.4 } 390 kkz 1.1.2.4 // get a safe view of the keys 391 kkz 1.1.2.4 Set keys = Collections.unmodifiableSet(new HashSet(safer.keySet())); 392 kkz 1.1.2.4 // save results 393 cananian 1.9 for (Object hmO : keys) { 394 cananian 1.9 HMethod hm = (HMethod) hmO; 395 kkz 1.1.2.4 safer.put(hm, Collections.unmodifiableSet((Set)safer.get(hm))); 396 kkz 1.1.2.4 } 397 kkz 1.1.2.4 return Collections.unmodifiableMap(safer); 398 kkz 1.1.2.4 } 399 kkz 1.1.2.4 400 kkz 1.1.2.5 /** Calculates the <code>Set</code> of safe methods; 401 kkz 1.1.2.5 * a safe method is one where the receiver object 402 kkz 1.1.2.5 * is the most recently allocated object for all 403 kkz 1.1.2.5 * calls of that method in the program. 404 kkz 1.1.2.4 */ 405 kkz 1.1.2.5 private void findSafeMethods() { 406 kkz 1.1.2.5 // start with the set of all analyzable methods 407 kkz 1.1.2.5 Set methods = new HashSet(); 408 cananian 1.9 for (Object hmO : ch.callableMethods()) { 409 cananian 1.9 HMethod hm = (HMethod) hmO; 410 kkz 1.1.2.4 if (!hm.isStatic() && !hm.isInterfaceMethod()) { 411 kkz 1.1.2.4 Code c = (Code) hcf.convert(hm); 412 kkz 1.1.2.4 if (c != null) 413 kkz 1.1.2.5 methods.add(hm); 414 kkz 1.1.2.4 } 415 kkz 1.1.2.4 } 416 kkz 1.1.2.5 // assume that they are all safe 417 kkz 1.1.2.5 safeMethods = Collections.unmodifiableSet(methods); 418 kkz 1.1.2.4 // Start with the set of initializers and remove 419 kkz 1.1.2.4 // any that get called when the receiver is not 420 kkz 1.1.2.4 // mra. Iterate until a fixed-point is reached. 421 kkz 1.1.2.1 while (true) { 422 kkz 1.1.2.5 Set safe = new HashSet(safeMethods); 423 cananian 1.9 for (Object hmO : ch.callableMethods()) { 424 cananian 1.9 HMethod hm = (HMethod) hmO; 425 kkz 1.1.2.4 Code c = (Code) hcf.convert(hm); 426 kkz 1.1.2.4 if (c != null) { 427 kkz 1.1.2.4 MRA mra = mra(c); 428 kkz 1.1.2.4 CALL[] calls = cg.getCallSites(hm); 429 kkz 1.1.2.4 for(int i = 0; i < calls.length; i++) { 430 kkz 1.1.2.4 CALL call = calls[i]; 431 kkz 1.1.2.4 // consider only initializers that 432 kkz 1.1.2.4 // have not been eliminated 433 kkz 1.1.2.4 if (safe.contains(call.method())) { 434 kkz 1.1.2.4 Tuple mra_before = mra.mra_before(call); 435 kkz 1.1.2.4 Map m = (Map) mra_before.proj(0); 436 kkz 1.1.2.4 Set s = (Set) mra_before.proj(1); 437 kkz 1.1.2.4 if (!m.containsKey(call.params(0)) || 438 kkz 1.1.2.5 !s.isEmpty()) { 439 kkz 1.1.2.5 // if the receiver is not the MRA 440 kkz 1.1.2.5 // object, or if there are exceptions, 441 kkz 1.1.2.5 // then the called method is not safe 442 kkz 1.1.2.5 if (call.isVirtual()) { 443 kkz 1.1.2.5 // if the call is virtual, none 444 kkz 1.1.2.5 // of the other methods callable 445 kkz 1.1.2.5 // at this site are safe, either 446 kkz 1.1.2.5 HMethod[] cms = cg.calls(hm, call); 447 kkz 1.1.2.5 for(int j = 0; j < cms.length; j++) 448 kkz 1.1.2.5 safe.remove(cms[j]); 449 kkz 1.1.2.5 } else { 450 kkz 1.1.2.5 // if the call is not virtual, the 451 kkz 1.1.2.5 // exact called method is removed 452 kkz 1.1.2.5 safe.remove(call.method()); 453 kkz 1.1.2.5 } 454 kkz 1.1.2.1 } 455 kkz 1.1.2.1 } 456 kkz 1.1.2.1 } 457 kkz 1.1.2.1 } 458 kkz 1.1.2.4 } 459 kkz 1.1.2.4 // save results 460 kkz 1.1.2.4 safe = Collections.unmodifiableSet(safe); 461 kkz 1.1.2.5 // newly-removed safe initializers must be re-analyzed 462 cananian 1.9 for (Object hmO : safeMethods) { 463 cananian 1.9 HMethod hm = (HMethod) hmO; 464 kkz 1.1.2.5 if (!safe.contains(hm)) { 465 kkz 1.1.2.4 Code c = (Code) hcf.convert(hm); 466 cananian 1.3.2.1 assert c != null; 467 kkz 1.1.2.4 clear(c); 468 kkz 1.1.2.1 } 469 kkz 1.1.2.1 } 470 kkz 1.1.2.5 if (safeMethods.size() == safe.size()) { 471 kkz 1.1.2.4 // reached fix-point 472 kkz 1.1.2.5 safeMethods = safe; 473 kkz 1.1.2.4 break; 474 kkz 1.1.2.1 } else { 475 kkz 1.1.2.5 // continue, size of set must be decreasing 476 cananian 1.3.2.1 assert safeMethods.size() > safe.size(); 477 kkz 1.1.2.5 safeMethods = safe; 478 kkz 1.1.2.1 } 479 kkz 1.1.2.1 } 480 kkz 1.1.2.1 } 481 kkz 1.1.2.1 482 kkz 1.1.2.1 /** Implementation of <code>MRA</code> analysis. */ 483 kkz 1.1.2.2 private class MRAImpl extends MRA { 484 kkz 1.1.2.1 485 kkz 1.1.2.1 private boolean DEBUG = false; 486 kkz 1.1.2.1 private final Code code; 487 kkz 1.1.2.5 private final boolean isSafeMethod; 488 kkz 1.1.2.1 private final BasicBlock.Factory bbf; 489 kkz 1.1.2.1 private final Map bb2pre; 490 kkz 1.1.2.1 private Map bb2post; 491 kkz 1.1.2.1 492 kkz 1.1.2.1 /** Creates a new <code>MRA</code>. Performs analysis. 493 kkz 1.1.2.1 * If the code is modified, then a new MRA needs to be 494 kkz 1.1.2.1 * created. 495 kkz 1.1.2.1 */ 496 kkz 1.1.2.1 private MRAImpl(Code c) { 497 kkz 1.1.2.1 this.code = c; 498 kkz 1.1.2.5 this.isSafeMethod = isSafeMethod(code.getMethod()); 499 kkz 1.1.2.1 this.bbf = new BasicBlock.Factory(code, cfger); 500 kkz 1.1.2.1 this.bb2pre = new HashMap(); 501 kkz 1.1.2.1 this.bb2post = new HashMap(); 502 kkz 1.1.2.1 analyze(); 503 kkz 1.1.2.1 // after analysis, we can get rid of the post map 504 kkz 1.1.2.1 this.bb2post = null; 505 kkz 1.1.2.1 } 506 kkz 1.1.2.1 507 kkz 1.1.2.4 private Map results = new HashMap(); 508 kkz 1.1.2.4 509 kkz 1.1.2.4 /** Returns a <code>Tuple</code> that characterizes 510 kkz 1.1.2.4 * the state before the given <code>Quad</code>. 511 kkz 1.1.2.1 */ 512 kkz 1.1.2.4 public Tuple mra_before(Quad q) { 513 kkz 1.1.2.4 // first, check our cache 514 kkz 1.1.2.4 Tuple t = (Tuple) results.get(q); 515 kkz 1.1.2.4 if (t != null) return t; 516 kkz 1.1.2.4 // if not in cache, then calculate 517 kkz 1.1.2.1 BasicBlock bb = bbf.getBlock(q); 518 cananian 1.3.2.1 assert bb != null; 519 kkz 1.1.2.4 Tuple pre = (Tuple) bb2pre.get(bb); 520 cananian 1.3.2.1 assert pre != null; 521 kkz 1.1.2.4 Tuple post = new Tuple(new Object[] 522 kkz 1.1.2.4 { new HashMap((Map)pre.proj(0)), 523 kkz 1.1.2.4 new HashSet((Set)pre.proj(1)), 524 kkz 1.1.2.4 (Quad)pre.proj(2), 525 kkz 1.1.2.4 new HashSet((Set)pre.proj(3)) }); 526 cananian 1.9 for(Object currO : bb.statements()) { 527 cananian 1.9 Quad curr = (Quad) currO; 528 kkz 1.1.2.1 if (q.equals(curr)) 529 kkz 1.1.2.1 break; 530 kkz 1.1.2.1 // PHIs and LABELs (which are also PHIs) are 531 kkz 1.1.2.1 // handled specially. so are CJMPs, SWITCHs and 532 kkz 1.1.2.1 // TYPESWITCHs, which are actually SIGMAs. 533 kkz 1.1.2.1 // CALLs are SIGMAs, but the "call" part is 534 kkz 1.1.2.1 // handled separately from the "sigma" part 535 kkz 1.1.2.1 if (curr.kind() != QuadKind.PHI && 536 kkz 1.1.2.1 curr.kind() != QuadKind.LABEL && 537 kkz 1.1.2.1 curr.kind() != QuadKind.CJMP && 538 kkz 1.1.2.1 curr.kind() != QuadKind.SWITCH && 539 kkz 1.1.2.1 curr.kind() != QuadKind.TYPESWITCH) 540 kkz 1.1.2.4 post = transfer(curr, post); 541 kkz 1.1.2.1 else if (DEBUG) 542 kkz 1.1.2.1 System.out.println(q); 543 kkz 1.1.2.1 } 544 kkz 1.1.2.4 t = new Tuple 545 kkz 1.1.2.4 (new Object[] 546 kkz 1.1.2.4 { Collections.unmodifiableMap((Map)post.proj(0)), 547 kkz 1.1.2.4 Collections.unmodifiableSet((Set)post.proj(1)), 548 kkz 1.1.2.4 (Quad)post.proj(2), 549 kkz 1.1.2.4 Collections.unmodifiableSet((Set)post.proj(3)) }); 550 kkz 1.1.2.4 results.put(q, t); 551 kkz 1.1.2.4 return t; 552 kkz 1.1.2.1 } 553 kkz 1.1.2.1 554 kkz 1.1.2.1 555 kkz 1.1.2.4 /** Performs analysis. This may take a while. 556 kkz 1.1.2.4 */ 557 kkz 1.1.2.1 private void analyze() { 558 kkz 1.1.2.1 // create universe 559 kkz 1.1.2.4 Set temps = new HashSet(); 560 kkz 1.1.2.1 for(Iterator it = code.getElementsI(); it.hasNext(); ) { 561 kkz 1.1.2.1 Quad q = (Quad) it.next(); 562 kkz 1.1.2.4 temps.addAll(ud.useC(q)); 563 kkz 1.1.2.4 temps.addAll(ud.defC(q)); 564 kkz 1.1.2.4 } 565 kkz 1.7 // save results 566 kkz 1.1.2.4 temps = Collections.unmodifiableSet(temps); 567 kkz 1.1.2.4 // maps Temps to an array of 2 booleans 568 kkz 1.1.2.4 // the first boolean is true iff 569 kkz 1.1.2.4 // the method is an initializer and 570 kkz 1.1.2.4 // the given Temp points to the 571 kkz 1.1.2.4 // receiver object 572 kkz 1.1.2.4 // the second boolean is true iff 573 kkz 1.1.2.4 // the given Temp succeeded the receiver 574 kkz 1.1.2.4 // as the most-recently-allocated object 575 kkz 1.1.2.4 Map universe = new HashMap(); 576 kkz 1.1.2.4 for(Iterator it = temps.iterator(); it.hasNext(); ) { 577 kkz 1.1.2.4 universe.put((Temp)it.next(), MRA.MRAToken.TOP); 578 kkz 1.1.2.1 } 579 kkz 1.1.2.4 universe = Collections.unmodifiableMap(universe); 580 kkz 1.1.2.1 // initialize the maps 581 kkz 1.1.2.1 for(Iterator it = bbf.blocksIterator(); it.hasNext(); ) 582 kkz 1.1.2.2 bb2post.put((BasicBlock)it.next(), 583 kkz 1.1.2.4 new Tuple(new Object[] { universe, 584 kkz 1.1.2.4 Collections.EMPTY_SET, 585 kkz 1.1.2.4 null, 586 kkz 1.1.2.4 temps })); 587 kkz 1.1.2.1 // initialize worklist 588 kkz 1.1.2.1 Worklist toprocess = new WorkSet(); 589 kkz 1.1.2.1 toprocess.push((BasicBlock)bbf.getRoot()); 590 kkz 1.1.2.1 // keep going until we reached a fixed point 591 kkz 1.1.2.1 while(!toprocess.isEmpty()) { 592 kkz 1.7 //MRAFactory.iterations++; 593 kkz 1.1.2.1 BasicBlock bb = (BasicBlock)toprocess.pull(); 594 kkz 1.1.2.1 // construct the pre-Set 595 kkz 1.1.2.4 Tuple pre = getPre(bb); 596 kkz 1.1.2.1 // add the unmodifiable original to the map 597 kkz 1.1.2.1 bb2pre.put(bb, pre); 598 kkz 1.1.2.4 // make a copy to modify 599 kkz 1.1.2.4 Tuple tup = new Tuple 600 kkz 1.1.2.4 (new Object[] { new HashMap((Map)pre.proj(0)), 601 kkz 1.1.2.4 new HashSet((Set)pre.proj(1)), 602 kkz 1.1.2.4 (Quad)pre.proj(2), 603 kkz 1.1.2.4 new HashSet((Set)pre.proj(3)) }); 604 cananian 1.9 for(Object qO : bb.statements()) { 605 cananian 1.9 Quad q = (Quad) qO; 606 kkz 1.1.2.4 // PHIs (which include LABELs) are handled implicitly 607 kkz 1.1.2.4 // in the call to getPre(). SIGMAs (which include 608 kkz 1.1.2.4 // CALLs, CJMPs, SWITCHes and TYPESWITCHes) are also 609 kkz 1.1.2.4 // handled implicitly in the call to getPre(), which 610 kkz 1.1.2.4 // calls getPost(). CALLs are handled twice because 611 kkz 1.1.2.4 // they have non-SIGMA defs. 612 kkz 1.1.2.4 int kind = q.kind(); 613 kkz 1.1.2.4 if (kind != QuadKind.PHI && kind != QuadKind.LABEL && 614 kkz 1.1.2.4 kind != QuadKind.CJMP && kind != QuadKind.SWITCH && 615 kkz 1.1.2.4 kind != QuadKind.TYPESWITCH) 616 kkz 1.1.2.4 tup = transfer(q, tup); 617 kkz 1.1.2.1 else if (DEBUG) 618 kkz 1.1.2.1 System.out.println(q); 619 kkz 1.1.2.1 } 620 kkz 1.1.2.4 Tuple post = (Tuple) bb2post.get(bb); 621 kkz 1.1.2.4 // check for convergence: 622 kkz 1.1.2.4 Set except = (Set) tup.proj(1); 623 kkz 1.1.2.4 if (((Set)tup.proj(1)).size() == ((Set)post.proj(1)).size()) { 624 kkz 1.1.2.4 // exception sets converged, check next: 625 kkz 1.1.2.4 if (((Map)tup.proj(0)).equals((Map)post.proj(0))) { 626 kkz 1.1.2.4 // mra maps converged, check next: 627 kkz 1.1.2.4 Quad q1 = (Quad)tup.proj(2); 628 kkz 1.1.2.4 Quad q2 = (Quad)post.proj(2); 629 kkz 1.1.2.4 if ((q1 == null && q2 == null) || 630 kkz 1.1.2.4 (q1 != null && q1.equals(q2))) { 631 kkz 1.1.2.4 // all converged, go to next in worklist 632 kkz 1.1.2.4 continue; 633 kkz 1.1.2.4 } 634 kkz 1.1.2.4 } 635 kkz 1.1.2.4 } else { 636 cananian 1.3.2.1 assert ((Set)tup.proj(1)).size() > 637 cananian 1.3.2.1 ((Set)post.proj(1)).size(); 638 kkz 1.1.2.1 } 639 kkz 1.1.2.4 // not converged, update map with new values, 640 kkz 1.1.2.4 // and add successors to worklist 641 kkz 1.1.2.4 bb2post.put(bb, new Tuple 642 kkz 1.1.2.4 (new Object[] 643 kkz 1.1.2.4 { Collections.unmodifiableMap((Map)tup.proj(0)), 644 kkz 1.1.2.4 Collections.unmodifiableSet((Set)tup.proj(1)), 645 kkz 1.1.2.4 (Quad)tup.proj(2), 646 kkz 1.1.2.4 Collections.unmodifiableSet((Set)tup.proj(3)) })); 647 kkz 1.1.2.4 for(Iterator it = bb.nextSet().iterator(); it.hasNext(); ) 648 kkz 1.1.2.4 toprocess.push((BasicBlock)it.next()); 649 kkz 1.1.2.1 } 650 kkz 1.1.2.1 } 651 kkz 1.1.2.1 652 kkz 1.1.2.4 /** Returns a <code>Tuple</code> containing an unmodifiable 653 kkz 1.1.2.4 * <code>Map</code>, an unmodifiable <code>Set</code>, and a 654 kkz 1.1.2.4 * <code>Quad</code> characterizing the state of the 655 kkz 1.1.2.4 * analysis prior to the given <code>Quad</code>. 656 kkz 1.1.2.4 * 657 kkz 1.1.2.4 * Handles complications that arise due to 658 kkz 1.1.2.4 * <code>SIGMA</code>s and <code>PHI</code>s. 659 kkz 1.1.2.4 */ 660 kkz 1.1.2.4 private Tuple getPre(BasicBlock bb) { 661 kkz 1.1.2.4 // start from empty maps and sets 662 kkz 1.1.2.4 Map mra = new HashMap(); 663 kkz 1.1.2.2 Set except = new HashSet(); 664 kkz 1.1.2.4 Set allocs = new HashSet(); 665 kkz 1.1.2.4 Set receiver = new HashSet(); 666 kkz 1.1.2.4 // look at the first Quad of the BasicBlock 667 kkz 1.1.2.1 Quad q = (Quad) bb.statements().get(0); 668 kkz 1.1.2.1 // PHIs are special 669 kkz 1.1.2.1 if (q.kind() == QuadKind.LABEL || 670 kkz 1.1.2.1 q.kind() == QuadKind.PHI) { 671 kkz 1.1.2.1 PHI phi = (PHI) q; 672 kkz 1.1.2.1 Quad[] prev = phi.prev(); 673 kkz 1.1.2.1 // need to cycle through predecessors 674 kkz 1.1.2.1 for (int i = 0; i < phi.arity(); i++) { 675 kkz 1.1.2.4 Tuple post = getPost(bbf.getBlock(prev[i]), bb); 676 kkz 1.1.2.4 // create a new map since we need to modify it 677 kkz 1.1.2.4 Map pmra = new HashMap((Map) post.proj(0)); 678 kkz 1.1.2.4 // create a new set since we need to modify it 679 kkz 1.1.2.4 Set preceiver = new HashSet((Set) post.proj(3)); 680 kkz 1.1.2.1 for (int j = 0; j < phi.numPhis(); j++) { 681 kkz 1.1.2.1 Temp src = phi.src(j, i); 682 kkz 1.1.2.1 // substitute srcs for dsts 683 kkz 1.1.2.4 if (pmra.containsKey(src)) { 684 kkz 1.1.2.4 MRA.MRAToken tok = (MRA.MRAToken) pmra.remove(src); 685 cananian 1.3.2.1 assert tok != null; 686 kkz 1.1.2.4 pmra.put(phi.dst(j), tok); 687 kkz 1.1.2.4 } 688 kkz 1.1.2.4 if (preceiver.remove(src)) { 689 kkz 1.1.2.4 preceiver.add(phi.dst(j)); 690 kkz 1.1.2.4 } 691 kkz 1.1.2.1 } 692 kkz 1.7 //long start_time = System.currentTimeMillis(); 693 kkz 1.7 694 kkz 1.1.2.1 // perform intersection 695 kkz 1.1.2.4 if (i == 0) { 696 kkz 1.1.2.4 mra.putAll(pmra); 697 kkz 1.1.2.4 receiver.addAll(preceiver); 698 kkz 1.1.2.4 } else { 699 kkz 1.7 //long start_time = System.currentTimeMillis(); 700 kkz 1.7 701 kkz 1.1.2.4 intersect(mra, pmra); 702 kkz 1.7 703 kkz 1.7 /* 704 kkz 1.7 MRAFactory.time += 705 kkz 1.7 (System.currentTimeMillis() - start_time); 706 kkz 1.7 */ 707 kkz 1.1.2.4 receiver.retainAll(preceiver); 708 kkz 1.1.2.4 } 709 kkz 1.1.2.2 // exceptions are just unioned 710 kkz 1.1.2.4 except.addAll((Set) post.proj(1)); 711 kkz 1.1.2.4 // collect all allocations sites 712 kkz 1.1.2.4 allocs.add((Quad) post.proj(2)); 713 kkz 1.7 714 kkz 1.7 //MRAFactory.time += 715 kkz 1.7 // (System.currentTimeMillis() - start_time); 716 kkz 1.1.2.1 } 717 kkz 1.1.2.1 } else { 718 kkz 1.1.2.4 // we may have joins that are not 719 kkz 1.1.2.4 // PHIs or LABELs (FOOTERs), so 720 kkz 1.1.2.4 // perform intersection for mra, 721 kkz 1.1.2.4 // union for exceptions 722 kkz 1.1.2.1 Iterator it = bb.prevSet().iterator(); 723 kkz 1.1.2.2 if (it.hasNext()) { 724 kkz 1.1.2.4 Tuple post = getPost((BasicBlock)it.next(), bb); 725 kkz 1.1.2.4 mra.putAll((Map) post.proj(0)); 726 kkz 1.1.2.4 except.addAll((Set) post.proj(1)); 727 kkz 1.1.2.4 allocs.add((Quad) post.proj(2)); 728 kkz 1.1.2.4 receiver.addAll((Set) post.proj(3)); 729 kkz 1.1.2.2 } 730 kkz 1.1.2.2 while (it.hasNext()) { 731 kkz 1.1.2.4 Tuple post = getPost((BasicBlock)it.next(), bb); 732 kkz 1.1.2.4 Map pmra = (Map) post.proj(0); 733 kkz 1.1.2.4 intersect(mra, pmra); 734 kkz 1.1.2.4 except.addAll((Set) post.proj(1)); 735 kkz 1.1.2.4 allocs.add((Quad) post.proj(2)); 736 kkz 1.1.2.4 receiver.retainAll((Set) post.proj(3)); 737 kkz 1.1.2.4 } 738 kkz 1.1.2.4 } 739 kkz 1.1.2.4 Quad alloc = null; 740 kkz 1.1.2.4 if (allocs.size() == 1) { 741 kkz 1.1.2.4 alloc = (Quad) allocs.iterator().next(); 742 kkz 1.1.2.4 } 743 kkz 1.1.2.4 return new Tuple(new Object[] 744 kkz 1.1.2.4 { Collections.unmodifiableMap(mra), 745 kkz 1.1.2.4 Collections.unmodifiableSet(except), 746 kkz 1.1.2.4 alloc, 747 kkz 1.1.2.4 Collections.unmodifiableSet(receiver) }); 748 kkz 1.1.2.1 } 749 kkz 1.1.2.1 750 kkz 1.1.2.2 // Handles the complicated post-Sets that 751 kkz 1.1.2.2 // occur when we have SIGMAs and PHIs. 752 kkz 1.1.2.2 // The successor is needed when bb has 753 kkz 1.1.2.2 // multiple sucessors, because its post-Sets 754 kkz 1.1.2.2 // are different for each successor 755 kkz 1.1.2.2 // returns an array of unmodifiable sets 756 kkz 1.1.2.4 private Tuple getPost(BasicBlock bb, BasicBlock succ) { 757 kkz 1.1.2.1 List stms = bb.statements(); 758 kkz 1.1.2.1 Quad q = (Quad) stms.get(stms.size()-1); 759 kkz 1.1.2.1 // SIGMAs are special, but abstract 760 kkz 1.1.2.1 if (q.kind() == QuadKind.CALL || 761 kkz 1.1.2.1 q.kind() == QuadKind.CJMP || 762 kkz 1.1.2.1 q.kind() == QuadKind.SWITCH || 763 kkz 1.1.2.1 q.kind() == QuadKind.TYPESWITCH) { 764 kkz 1.1.2.1 // find our arity 765 kkz 1.1.2.1 SIGMA sigma = (SIGMA) q; 766 kkz 1.1.2.1 int arity = 0; 767 kkz 1.1.2.1 Quad match = (Quad) succ.statements().get(0); 768 kkz 1.1.2.1 Quad[] next = sigma.next(); 769 kkz 1.1.2.1 for ( ; arity < sigma.arity(); arity++) { 770 kkz 1.1.2.1 if (match.equals(next[arity])) { 771 kkz 1.1.2.1 // found arity 772 kkz 1.1.2.4 Tuple t = (Tuple) bb2post.get(bb); 773 kkz 1.1.2.4 Map mra = new HashMap((Map)t.proj(0)); 774 kkz 1.1.2.4 Set receiver = new HashSet((Set)t.proj(3)); 775 kkz 1.1.2.1 for (int i = 0; i < sigma.numSigmas(); i++) { 776 kkz 1.1.2.4 Temp src = sigma.src(i); 777 kkz 1.1.2.4 // exchange dst for src in mra 778 kkz 1.1.2.4 if (mra.containsKey(src)) { 779 kkz 1.1.2.4 MRA.MRAToken tok = (MRA.MRAToken) mra.remove(src); 780 cananian 1.3.2.1 assert tok != null; 781 kkz 1.1.2.4 mra.put(sigma.dst(i, arity), tok); 782 kkz 1.1.2.4 } 783 kkz 1.1.2.4 if (receiver.remove(src)) { 784 kkz 1.1.2.4 receiver.add(sigma.dst(i, arity)); 785 kkz 1.1.2.4 } 786 kkz 1.1.2.1 } 787 kkz 1.1.2.1 // make unmodifiable 788 kkz 1.1.2.4 return new Tuple(new Object[] 789 kkz 1.1.2.4 { Collections.unmodifiableMap(mra), 790 kkz 1.1.2.4 (Set)t.proj(1), 791 kkz 1.1.2.4 (Quad)t.proj(2), 792 kkz 1.1.2.4 Collections.unmodifiableSet(receiver) }); 793 kkz 1.1.2.1 } 794 kkz 1.1.2.1 } 795 kkz 1.1.2.1 // should never get here 796 kkz 1.1.2.1 throw new Error("Cannot find arity of "+succ+" w.r.t. "+bb); 797 kkz 1.1.2.1 } 798 kkz 1.1.2.1 // otherwise... 799 kkz 1.1.2.4 return (Tuple) bb2post.get(bb); 800 kkz 1.1.2.1 } 801 kkz 1.1.2.1 802 kkz 1.1.2.4 /** Transfer function for the analysis. Returns 803 kkz 1.1.2.4 * a <code>Tuple</code> characterizing the 804 kkz 1.1.2.4 * state after the <code>Quad</code> <code>q</code>. 805 kkz 1.1.2.4 */ 806 kkz 1.1.2.4 private Tuple transfer(Quad q, Tuple pre) { 807 kkz 1.1.2.1 if (DEBUG) { 808 kkz 1.1.2.1 System.out.println(q); 809 kkz 1.1.2.4 System.out.print(" "+pre.proj(0)); 810 kkz 1.1.2.4 System.out.print(" except for "+pre.proj(1)); 811 kkz 1.1.2.4 System.out.print(" allocated at "+pre.proj(2)); 812 kkz 1.1.2.1 } 813 kkz 1.1.2.1 int kind = q.kind(); 814 kkz 1.1.2.4 Map mra = (Map) pre.proj(0); 815 kkz 1.1.2.4 Set except = (Set) pre.proj(1); 816 kkz 1.1.2.4 Set receiver = (Set) pre.proj(3); 817 kkz 1.1.2.4 if (kind == QuadKind.ANEW || 818 kkz 1.1.2.4 kind == QuadKind.NEW) { 819 kkz 1.1.2.4 boolean succeeding = false; 820 kkz 1.1.2.5 if (isSafeMethod) { 821 kkz 1.1.2.4 // check if the Temp we def is succeeding the 822 kkz 1.1.2.4 // receiver as the most recently allocated object 823 kkz 1.1.2.4 succeeding = true; 824 kkz 1.1.2.4 for(Iterator it = mra.values().iterator(); 825 kkz 1.1.2.4 it.hasNext(); ) { 826 kkz 1.1.2.4 if (((MRA.MRAToken)it.next()) != MRA.MRAToken.RCVR) 827 kkz 1.1.2.4 succeeding = false; 828 kkz 1.1.2.4 } 829 kkz 1.1.2.4 } 830 kkz 1.1.2.4 // the Temp we def is now the mra 831 kkz 1.1.2.2 mra.clear(); 832 kkz 1.1.2.4 mra.put(q.def()[0], (succeeding ? 833 kkz 1.1.2.4 MRA.MRAToken.SUCC : MRA.MRAToken.BOTTOM)); 834 kkz 1.1.2.4 // there should only be one 835 cananian 1.3.2.1 assert ud.defC(q).size() == 1; 836 kkz 1.1.2.4 // clear exceptions after an allocation 837 kkz 1.1.2.2 except.clear(); 838 kkz 1.1.2.4 // remove dst from receiver 839 kkz 1.1.2.4 receiver.remove(q.def()[0]); 840 kkz 1.1.2.4 // make new Tuple, because we are changing the allocation site 841 kkz 1.1.2.4 pre = new Tuple(new Object[] { mra, except, q, receiver }); 842 kkz 1.1.2.1 } else if (kind == QuadKind.CALL) { 843 kkz 1.1.2.1 // after a call, we can no longer be 844 kkz 1.1.2.1 // certain of the most recently allocated 845 kkz 1.1.2.1 // object, unless that method has been 846 kkz 1.1.2.1 // pre-approved, in which case only the 847 kkz 1.1.2.1 // return value and exception are toast 848 kkz 1.1.2.1 // the "sigma" part of the call is 849 kkz 1.1.2.1 // handled as part of the control flow 850 kkz 1.1.2.1 CALL call = (CALL) q; 851 kkz 1.1.2.5 if (allocatedTypesKnown(call.method())) { 852 kkz 1.1.2.5 Set exceptions = (Set) getAllocatedTypes(call.method()); 853 cananian 1.3.2.1 assert exceptions != null; 854 kkz 1.1.2.2 // need to add exceptions 855 kkz 1.1.2.4 except.addAll(exceptions); 856 kkz 1.1.2.2 // note our defs 857 kkz 1.1.2.2 Temp retval = call.retval(); 858 kkz 1.1.2.4 if (retval != null) { 859 kkz 1.1.2.2 mra.remove(retval); 860 kkz 1.1.2.4 receiver.remove(retval); 861 kkz 1.1.2.4 } 862 kkz 1.1.2.2 Temp retex = call.retex(); 863 kkz 1.1.2.4 if (retex != null) { 864 kkz 1.1.2.2 mra.remove(retex); 865 kkz 1.1.2.4 receiver.remove(retval); 866 kkz 1.1.2.4 } 867 kkz 1.1.2.1 } else { 868 kkz 1.1.2.2 // not safe, just clear 869 kkz 1.1.2.2 mra.clear(); 870 kkz 1.1.2.1 } 871 kkz 1.1.2.1 } else if (kind == QuadKind.MOVE) { 872 kkz 1.1.2.4 // if the src is an mra, then the dst should 873 kkz 1.1.2.4 // be added to the map as a clone of the src 874 kkz 1.1.2.1 MOVE move = (MOVE)q; 875 kkz 1.1.2.4 if (mra.containsKey(move.src())) { 876 kkz 1.1.2.4 MRA.MRAToken token = (MRA.MRAToken) mra.get(move.src()); 877 cananian 1.3.2.1 assert token != null; 878 kkz 1.1.2.4 mra.put(move.dst(), token); 879 kkz 1.1.2.4 } else { 880 kkz 1.1.2.2 mra.remove(move.dst()); 881 kkz 1.1.2.4 } 882 kkz 1.1.2.4 // keep receiver set updated 883 kkz 1.1.2.4 if (receiver.contains(move.src())) { 884 kkz 1.1.2.4 receiver.add(move.dst()); 885 kkz 1.1.2.4 } else { 886 kkz 1.1.2.4 receiver.remove(move.dst()); 887 kkz 1.1.2.4 } 888 kkz 1.1.2.1 } else if (kind == QuadKind.METHOD) { 889 cananian 1.3.2.1 assert mra.isEmpty(); 890 kkz 1.1.2.5 if (isSafeMethod) { 891 kkz 1.1.2.4 Temp rcvr = ((METHOD) q).params(0); 892 kkz 1.1.2.4 mra.put(rcvr, MRA.MRAToken.RCVR); 893 kkz 1.1.2.4 receiver.add(rcvr); 894 kkz 1.1.2.4 } 895 kkz 1.1.2.1 } else if (kind == QuadKind.AGET || 896 kkz 1.1.2.1 kind == QuadKind.ALENGTH || 897 kkz 1.1.2.1 kind == QuadKind.COMPONENTOF || 898 kkz 1.1.2.1 kind == QuadKind.CONST || 899 kkz 1.1.2.1 kind == QuadKind.GET || 900 kkz 1.1.2.1 kind == QuadKind.HANDLER || 901 kkz 1.1.2.1 kind == QuadKind.INSTANCEOF || 902 kkz 1.1.2.1 kind == QuadKind.OPER) { 903 kkz 1.1.2.4 // defs of a Temp revoke its mra status 904 kkz 1.1.2.4 mra.remove(q.def()[0]); 905 kkz 1.1.2.4 receiver.remove(q.def()[0]); 906 kkz 1.1.2.4 // should only be one for these Quads 907 cananian 1.3.2.1 assert ud.defC(q).size() == 1; 908 kkz 1.5 } else if (kind == QuadKind.MONITORENTER) { 909 kkz 1.5 // for race-free multi-threaded code 910 kkz 1.5 mra.clear(); 911 kkz 1.5 except.clear(); 912 kkz 1.1.2.1 } else if (kind == QuadKind.ARRAYINIT || 913 kkz 1.1.2.1 kind == QuadKind.ASET || 914 kkz 1.1.2.1 kind == QuadKind.DEBUG || 915 kkz 1.1.2.1 kind == QuadKind.FOOTER || 916 kkz 1.1.2.1 kind == QuadKind.HEADER || 917 kkz 1.1.2.1 kind == QuadKind.MONITOREXIT || 918 kkz 1.1.2.1 kind == QuadKind.NOP || 919 kkz 1.1.2.1 kind == QuadKind.RETURN || 920 kkz 1.1.2.1 kind == QuadKind.SET || 921 kkz 1.1.2.1 kind == QuadKind.THROW || 922 kkz 1.1.2.1 kind == QuadKind.TYPECAST) { 923 kkz 1.1.2.1 // Quads with no defs have no effect 924 cananian 1.3.2.1 assert ud.defC(q).size() == 0; 925 kkz 1.1.2.1 } else { 926 kkz 1.1.2.1 // LABELs, PHIs, SIGMAs, SWITCHs, TYPESWITCHs and XIs 927 kkz 1.1.2.1 // are not handled, but there should not be any 928 kkz 1.1.2.1 throw new Error("Unknown QuadKind: "+q.kind()+" for "+q); 929 kkz 1.1.2.1 } 930 kkz 1.1.2.2 if (DEBUG) { 931 kkz 1.1.2.2 System.out.print(" -> "); 932 kkz 1.1.2.4 System.out.println(pre.proj(0)+" except for "+pre.proj(1)); 933 kkz 1.1.2.4 } 934 kkz 1.1.2.4 return pre; 935 kkz 1.1.2.4 } 936 kkz 1.1.2.4 } 937 kkz 1.1.2.4 938 kkz 1.1.2.4 /** Requires that m1 and m2 map <code>Temp</code>s to 939 kkz 1.1.2.4 * <code>MRA.MRAToken</code>s. 940 kkz 1.1.2.4 * Modifies m1 such that it is an intersection of m1 941 kkz 1.1.2.4 * and m2 according to the following rules: 942 kkz 1.1.2.4 * 943 kkz 1.1.2.4 * 1. m1 will contain a mapping for a key k iff m1 944 kkz 1.1.2.4 * originally contained a mapping for k and if m2 945 kkz 1.1.2.4 * contains a mapping for k. 946 kkz 1.1.2.4 * 2. The value to which k maps in m1 will be the 947 kkz 1.1.2.4 * join of the value to which k originally mapped 948 kkz 1.1.2.4 * in m1 and the value to which k maps in m2. 949 kkz 1.1.2.4 */ 950 kkz 1.1.2.4 private static void intersect(Map m1, Map m2) { 951 kkz 1.1.2.4 // make a copy of the keys before modifying the map 952 kkz 1.1.2.4 Set keySet = new HashSet(m1.keySet()); 953 cananian 1.9 for(Object tO : keySet) { 954 cananian 1.9 Temp t = (Temp) tO; 955 kkz 1.1.2.4 if (m2.containsKey(t)) { 956 kkz 1.1.2.4 // both m1 and m2 contain a mapping for t 957 kkz 1.1.2.4 // compute join and put new mapping in m1 958 kkz 1.1.2.4 MRA.MRAToken t1 = (MRA.MRAToken) m1.get(t); 959 kkz 1.1.2.4 MRA.MRAToken t2 = (MRA.MRAToken) m2.get(t); 960 cananian 1.3.2.1 assert t1 != null && t2 != null; 961 kkz 1.1.2.4 m1.put(t, t1.join(t2)); 962 kkz 1.1.2.4 } else { 963 kkz 1.1.2.4 // m2 does not contain a mapping for t 964 kkz 1.1.2.4 // therefore, remove mapping from m1 965 kkz 1.7 m1.remove(t); 966 kkz 1.1.2.2 } 967 kkz 1.1.2.2 } 968 kkz 1.1.2.2 } 969 kkz 1.1.2.2 970 kkz 1.1.2.2 private static Set parseResource(final Linker l, String resourceName) { 971 kkz 1.1.2.2 final Set result = new HashSet(); 972 kkz 1.1.2.2 try { 973 kkz 1.1.2.2 ParseUtil.readResource(resourceName, new ParseUtil.StringParser() { 974 kkz 1.1.2.2 public void parseString(String s) 975 kkz 1.1.2.2 throws ParseUtil.BadLineException { 976 kkz 1.1.2.2 result.add(ParseUtil.parseMethod(l, s)); 977 kkz 1.1.2.2 } 978 kkz 1.1.2.2 }); 979 kkz 1.1.2.2 } catch (java.io.IOException ex) { 980 kkz 1.1.2.2 System.err.println("ERROR READING SAFE SET, SKIPPING REST."); 981 kkz 1.1.2.2 System.err.println(ex.toString()); 982 kkz 1.1.2.1 } 983 kkz 1.1.2.2 // done. 984 kkz 1.1.2.2 return result; 985 kkz 1.1.2.1 } 986 cananian 1.2 }