4 import java.io.FileNotFoundException;
5 import java.io.PrintWriter;
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.Hashtable;
9 import java.util.Iterator;
11 import Analysis.Disjoint.*;
12 import IR.TypeDescriptor;
14 //TODO fix inaccuracy problem and take advantage of the refEdges
15 //TODO make it so that methods with no conflicts get no output.
16 //TODO Make more efficient by only using ONE hashtable.
18 /* An instance of this class manages all OoOJava coarse-grained runtime conflicts
19 * by generating C-code to either rule out the conflict at runtime or resolve one.
22 * 1) Instantiate singleton object
23 * 2) Call void traverse(FlatSESEEnterNode rblock, Hashtable<Taint, Set<Effect>> effects, Hashtable<Taint, Set<Effect>> conflicts, ReachGraph rg)
24 * as many times as needed
25 * 3) Call void close()
27 public class RuntimeConflictResolver {
28 public static String outputFile;
29 private PrintWriter cFile;
30 private PrintWriter headerFile;
31 private static final String hashAndQueueCFileDir = "oooJava/";
33 // initializing variables can be found in printHeader()
34 private static final String getAllocSiteInC = "->allocsite";
35 private static final String queryAndAddHashTableInC = "hashRCRInsert(";
36 private static final String addToQueueInC = "enqueueRCRQueue(";
37 private static final String dequeueFromQueueInC = "dequeueRCRQueue()";
39 private static final String clearQueue = "resetRCRQueue()";
40 // Make hashtable; hashRCRCreate(unsigned int size, double loadfactor)
41 private static final String mallocHashTable = "hashRCRCreate(1024, 0.25)";
42 private static final String deallocHashTable = "hashRCRDelete()";
43 private static final String resetHashTable = "hashRCRreset()";
45 public RuntimeConflictResolver(String buildir) throws FileNotFoundException {
46 outputFile = buildir + "RuntimeConflictResolver";
48 cFile = new PrintWriter(new File(outputFile + ".c"));
49 headerFile = new PrintWriter(new File(outputFile + ".h"));
51 cFile.append("#include \"" + hashAndQueueCFileDir + "hashRCR.h\"\n#include \""
52 + hashAndQueueCFileDir + "Queue_RCR.h\"\n#include <stdlib.h>\n");
53 cFile.append("#include \"classdefs.h\"\n");
54 cFile.append("#include \"RuntimeConflictResolver.h\"\n");
56 headerFile.append("#ifndef __3_RCR_H_\n");
57 headerFile.append("#define __3_RCR_H_\n");
58 //TODO more closely integrate this by asking generic type from other components?
60 cFile.append("struct genericObjectStruct {int type; int oid; int allocsite; int ___cachedCode___; int ___cachedHash___;};\n");
65 * 1) Create pruned data structures from givens
66 * 1a) Use effects sets to verify if we can access something (reads)
67 * 1b) Mark conflicts with 2 flags (one for self, one for references down the line)
68 * 2) build code output structure
71 public void traverse(FlatSESEEnterNode rblock,
72 Hashtable<Taint, Set<Effect>> effects,
73 Hashtable<Taint, Set<Effect>> conflicts,
76 Set<TempDescriptor> inVars = rblock.getInVarSet();
78 if (inVars.size() == 0)
81 // For every non-primative variable, generate unique method
82 // Special Note: The Criteria for executing printCMethod in this loop should match
83 // exactly the criteria in buildcode.java to invoke the generated C method(s).
84 for (TempDescriptor invar : inVars) {
85 TypeDescriptor type = invar.getType();
86 if(type == null || type.isPrimitive()) {
90 Hashtable<AllocSite, ConcreteRuntimeObjNode> created = new Hashtable<AllocSite, ConcreteRuntimeObjNode>();
92 createTree(rblock, invar, effects, conflicts, rg, created);
93 if (!created.isEmpty()) {
94 rblock.addInVarForDynamicCoarseConflictResolution(invar);
95 printCMethod(created, invar.getSafeSymbol(), rblock.getPrettyIdentifier());
100 public void close() {
101 // Adds Extra supporting methods
102 cFile.append("void initializeStructsRCR() { " + mallocHashTable + "; " + clearQueue + "; }");
103 cFile.append("void destroyRCR() { " + deallocHashTable + "; }\n");
105 headerFile.append("void initializeStructsRCR(); \nvoid destroyRCR(); \n");
106 headerFile.append("#endif\n");
112 private void createTree(FlatSESEEnterNode rblock,
113 TempDescriptor invar,
114 Hashtable<Taint, Set<Effect>> effects,
115 Hashtable<Taint, Set<Effect>> conflicts,
117 Hashtable<AllocSite, ConcreteRuntimeObjNode> created) {
119 VariableNode varNode = rg.getVariableNodeNoMutation(invar);
120 Hashtable<AllocSite, EffectsGroup> table =
121 generateEffectsLookupTable(rblock, varNode, effects, conflicts);
123 // if table is null that means there's no conflicts, therefore we need not
124 // create a traversal
128 Iterator<RefEdge> possibleEdges = varNode.iteratorToReferencees();
130 while (possibleEdges.hasNext()) {
131 RefEdge edge = possibleEdges.next();
134 ConcreteRuntimeObjNode singleRoot = new ConcreteRuntimeObjNode(edge.getDst(), false, true);
135 AllocSite rootKey = singleRoot.allocSite;
137 if (!created.containsKey(rootKey)) {
138 created.put(rootKey, singleRoot);
139 createHelper(singleRoot, edge.getDst().iteratorToReferencees(), created, table);
144 private Hashtable<AllocSite, EffectsGroup> generateEffectsLookupTable(FlatSESEEnterNode rblock,
145 VariableNode var, Hashtable<Taint, Set<Effect>> effects,
146 Hashtable<Taint, Set<Effect>> conflicts) {
147 // we search effects since conflicts is only a subset of effects
148 Taint taint = getProperTaint(rblock, var, effects);
149 assert taint != null;
151 Set<Effect> localEffects = effects.get(taint);
152 Set<Effect> localConflicts = conflicts.get(taint);
154 if (localEffects == null || localEffects.isEmpty() || localConflicts == null || localConflicts.isEmpty())
157 // Debug Code for manually checking effects
158 // System.out.println("For Taint " + taint);
159 // System.out.println("Effects");
160 // for(Effect e: localEffects)
162 // System.out.println(e);
165 // System.out.println("Conflicts");
166 // for(Effect e: localConflicts)
168 // System.out.println(e);
171 Hashtable<AllocSite, EffectsGroup> lookupTable = new Hashtable<AllocSite, EffectsGroup>();
173 for (Effect e : localEffects) {
174 boolean conflict = localConflicts.contains(e);
175 AllocSite key = e.getAffectedAllocSite();
176 EffectsGroup myEffects = lookupTable.get(key);
178 if(myEffects == null) {
179 myEffects = new EffectsGroup();
180 lookupTable.put(key, myEffects);
183 if(e.getField().getType().isPrimitive()) {
185 myEffects.addPrimative(e);
189 myEffects.addObj(e, conflict);
196 // Plan is to add stuff to the tree depth-first sort of way. That way, we can
197 // propagate up conflicts
198 private void createHelper(ConcreteRuntimeObjNode curr, Iterator<RefEdge> edges, Hashtable<AllocSite, ConcreteRuntimeObjNode> created,
199 Hashtable<AllocSite, EffectsGroup> table) {
200 assert table != null;
202 AllocSite parentKey = curr.allocSite;
203 EffectsGroup currEffects = table.get(parentKey);
205 if (currEffects == null || currEffects.isEmpty())
209 if(currEffects.hasObjectConflicts()) {
210 while(edges.hasNext()) {
211 RefEdge edge = edges.next();
212 String field = edge.getField();
213 EffectPair effect = currEffects.getObjEffect(field); // TODO are you certain there is only one effect to get here?
214 //If there is no effect, then there's not point in traversing this edge
217 HeapRegionNode childHRN = edge.getDst();
218 AllocSite childKey = childHRN.getAllocSite();
219 boolean isNewChild = !created.containsKey(childKey);
220 ConcreteRuntimeObjNode child;
223 child = new ConcreteRuntimeObjNode(childHRN, effect.conflict, false);
224 created.put(childKey, child);
227 child = created.get(childKey);
230 curr.addObjChild(field, child, effect.conflict);
232 if (effect.conflict) {
233 propogateObjConflictFlag(child);
236 if (effect.type == Effect.read && isNewChild) {
237 createHelper(child, childHRN.iteratorToReferencees(), created, table);
244 if(currEffects.hasPrimativeConflicts()) {
245 curr.primativeFields = currEffects.primativeConflictingFields;
246 propogatePrimConflictFlag(curr);
250 // This will propagate the conflict up the data structure.
251 private void propogateObjConflictFlag(ConcreteRuntimeObjNode in) {
252 ConcreteRuntimeObjNode node = in;
254 while(node.lastReferencer != null) {
255 node.lastReferencer.decendantsObjConflict = true;
256 if(!node.conflictingParents.add(node.lastReferencer))
258 node = node.lastReferencer;
262 private void propogatePrimConflictFlag(ConcreteRuntimeObjNode in) {
263 ConcreteRuntimeObjNode node = in;
265 while(node.lastReferencer != null) {
266 node.lastReferencer.decendantsPrimConflict = true;
267 if(!node.conflictingParents.add(node.lastReferencer))
269 node = node.lastReferencer;
274 * This method generates a C method for every inset variable and rblock.
276 * The C method works by generating a large switch statement that will run the appropriate
277 * checking code for each object based on its allocation site. The switch statement is
278 * surrounded by a while statement which dequeues objects to be checked from a queue. An
279 * object is added to a queue only if it contains a conflict (in itself or in its referencees)
280 * and we came across it while checking through it's referencer. Because of this property,
281 * conflicts will be signaled by the referencer; the only exception is the inset variable which can
282 * signal a conflict within itself.
284 //TODO make empty switch statments just have a return.
285 //TODO make check for only 1 case statement (String Builder?)
286 //TODO where are all these "temp" variables coming from?
287 private void printCMethod(Hashtable<AllocSite, ConcreteRuntimeObjNode> created, String inVar, String rBlock) {
288 HashSet<AllocSite> done = new HashSet<AllocSite>();
289 // note that primitive in-set variables do not generate effects, so we can assume
290 // that inVar is an object
292 //Note: remember to change getTraverserInvocation if you change the line below
293 String methodName = "void traverse___" + inVar.replaceAll(" ", "") + rBlock.replaceAll(" ", "") +
296 cFile.append(methodName + " {\n");
297 headerFile.append(methodName + ";\n");
299 cFile.append("printf(\"The traverser ran for " + methodName + "\\n\");\n");
301 //Casts the ptr to a genericObjectSTruct so we can get to the ptr->allocsite field.
302 cFile.append("struct genericObjectStruct * ptr = (struct genericObjectStruct *) InVar; if(InVar != NULL) { " + queryAndAddHashTableInC
305 //This part of the code generates the switch statement from all objects in hash.
306 cFile.append("switch(ptr->allocsite) { ");
307 for (ConcreteRuntimeObjNode node : created.values()) {
308 // If we haven't seen it and it's a node with more than 1 parent
309 // Note: a node with 0 parents is a root node (i.e. inset variable)
310 if (!done.contains(node.allocSite) && (node.getNumOfReachableParents() != 1 || node.isInsetVar)
311 && node.decendantsConflict()) {
312 addChecker(node, done, "ptr", 0);
315 cFile.append(" default : break; ");
316 cFile.append("}} while( (ptr = " + dequeueFromQueueInC + ") != NULL); ");
318 //Closes the method by clearing the Queue and reseting the hashTable to prevent
319 //overhead from freeing and mallocing the structures.
320 cFile.append(clearQueue + "; " + resetHashTable + "; }}\n");
325 public String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) {
326 return "traverse___" + invar.getSafeSymbol().replaceAll(" ", "") +
327 sese.getPrettyIdentifier().replaceAll(" ", "") + "___("+varString+");";
331 * addChecker creates a case statement for every object that is either an inset variable
332 * or has multiple referencers (incoming edges). Else it just prints the checker for that object
333 * so that its processing can be pushed up to the referencer node.
335 private void addChecker(ConcreteRuntimeObjNode node, HashSet<AllocSite> done, String prefix, int depth) {
336 // We don't need a case statement for things with either 1 incoming or 0 out
337 // going edges, because they can be processed when checking the parent.
338 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) && node.decendantsConflict()) {
339 assert prefix.equals("ptr");
340 cFile.append("case " + node.getAllocationSite() + ":\n { ");
343 //Specific Primitives test for invars
344 if(node.isInsetVar && node.hasPrimativeConflicts())
345 handlePrimitiveConflict(prefix, node.primativeFields, node.allocSite);
348 //Casts C pointer; depth is used to create unique "myPtr" name
349 String currPtr = "myPtr" + depth;
350 String structType = node.original.getType().getSafeSymbol();
351 cFile.append("struct " + structType + " * "+currPtr+"= (struct "+ structType + " * ) " + prefix + "; ");
354 for (ObjRef ref : node.objectRefs) {
355 // Will only process edge if there is some sort of conflict with the Child
356 if (ref.child.decendantsConflict()|| ref.child.hasConflicts()) {
357 String childPtr = currPtr +"->___" + ref.field + "___";
359 // Checks if the child exists and has allocsite matching the conflict
360 cFile.append("if(" + childPtr + " != NULL && " + childPtr + getAllocSiteInC + "=="
361 + ref.allocSite + ") { ");
363 // Prints out conflicts of child
365 handleObjConflict(childPtr, node.allocSite);
367 if(ref.child.hasPrimativeConflicts())
368 handlePrimitiveConflict(childPtr, ref.child.primativeFields, ref.child.allocSite);
370 if (ref.child.decendantsConflict()) {
371 // Checks if we have visited the child before
372 cFile.append("if(!" + queryAndAddHashTableInC + childPtr + ")) { ");
373 if (ref.child.getNumOfReachableParents() == 1 && !ref.child.isInsetVar) {
374 addChecker(ref.child, done, childPtr, depth + 1);
377 cFile.append(addToQueueInC + childPtr + ");");
386 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) && node.decendantsConflict())
387 cFile.println(" } break; ");
389 done.add(node.allocSite);
392 private void handleObjConflict(String childPtr, AllocSite allocSite) {
393 cFile.append("printf(\"Conflict detected with %p from parent with allocation site %u\\n\"," + childPtr + "," + allocSite.hashCodeSpecific() + ");");
396 private void handlePrimitiveConflict(String ptr, ArrayList<String> conflicts, AllocSite allocSite) {
397 cFile.append("printf(\"Primitive Conflict detected with %p with alloc site %u\\n\", "+ptr+", "+allocSite.hashCodeSpecific()+"); ");
400 private Taint getProperTaint(FlatSESEEnterNode rblock, VariableNode var,
401 Hashtable<Taint, Set<Effect>> effects) {
402 Set<Taint> taints = effects.keySet();
404 for (Taint t : taints) {
405 FlatSESEEnterNode sese = t.getSESE();
406 //Jim says that this case should never happen, but it may
408 System.out.println( "What should I do with a stall site taint? --Jim");
410 if(sese != null && sese.equals(rblock) && t.getVar().equals(var.getTempDescriptor())) {
417 private class EffectsGroup
419 Hashtable<String, EffectPair> myEffects;
420 ArrayList<String> primativeConflictingFields;
422 public EffectsGroup() {
423 myEffects = new Hashtable<String, EffectPair>();
424 primativeConflictingFields = new ArrayList<String>();
427 public void addPrimative(Effect e) {
428 primativeConflictingFields.add(e.getField().toPrettyStringBrief());
431 public void addObj(Effect e, boolean conflict) {
432 EffectPair effect = new EffectPair(e, conflict);
433 myEffects.put(e.getField().getSymbol(), effect);
436 public boolean isEmpty() {
437 return myEffects.isEmpty() && primativeConflictingFields.isEmpty();
440 public boolean hasPrimativeConflicts(){
441 return !primativeConflictingFields.isEmpty();
444 public boolean hasObjectConflicts() {
445 return !myEffects.isEmpty();
448 public EffectPair getObjEffect(String field) {
449 return myEffects.get(field);
453 private class EffectPair {
454 Effect originalEffect;
458 public EffectPair(Effect e, boolean conflict) {
461 this.conflict = conflict;
464 public int hashCode() {
465 return originalEffect.hashCode();
468 public boolean equals(Object o) {
472 if (!(o instanceof EffectPair))
475 EffectPair other = (EffectPair) o;
477 return (other.originalEffect.getAffectedAllocSite().equals(
478 originalEffect.getAffectedAllocSite()) && other.originalEffect.getField().equals(
479 originalEffect.getField()));
482 public String toString()
484 return originalEffect.toString();
488 private class ObjRef {
492 ConcreteRuntimeObjNode child;
494 public ObjRef(String fieldname, ConcreteRuntimeObjNode ref, boolean con) {
496 allocSite = ref.getAllocationSite();
502 private class ConcreteRuntimeObjNode {
503 ArrayList<ObjRef> objectRefs;
504 ArrayList<String> primativeFields;
505 ArrayList<ConcreteRuntimeObjNode> parents;
506 HashSet<ConcreteRuntimeObjNode> conflictingParents;
507 ConcreteRuntimeObjNode lastReferencer;
508 boolean decendantsPrimConflict;
509 boolean decendantsObjConflict;
512 HeapRegionNode original;
514 public ConcreteRuntimeObjNode(HeapRegionNode me, boolean conflict, boolean isInVar) {
515 objectRefs = new ArrayList<ObjRef>();
516 parents = new ArrayList<ConcreteRuntimeObjNode>();
517 conflictingParents = new HashSet<ConcreteRuntimeObjNode>();
518 lastReferencer = null;
519 allocSite = me.getAllocSite();
521 isInsetVar = isInVar;
522 decendantsPrimConflict = false;
523 decendantsObjConflict = false;
524 primativeFields = null;
528 public int hashCode() {
529 // This gets allocsite number
530 return allocSite.hashCodeSpecific();
534 public boolean equals(Object obj) {
535 return original.equals(obj);
538 public int getAllocationSite() {
539 return allocSite.hashCodeSpecific();
542 public int getNumOfReachableParents() {
543 return conflictingParents.size();
546 public boolean hasPrimativeConflicts() {
547 return primativeFields != null;
550 public boolean hasConflicts() {
551 return (primativeFields != null) || !conflictingParents.isEmpty();
554 public boolean decendantsConflict() {
555 return decendantsPrimConflict || decendantsObjConflict;
558 public void addObjChild(String field, ConcreteRuntimeObjNode child, boolean conflict) {
559 child.lastReferencer = this;
560 ObjRef ref = new ObjRef(field, child, conflict);
562 child.parents.add(this);
565 public String toString()
567 return "AllocSite=" + getAllocationSite() + " myConflict=" + !conflictingParents.isEmpty() +
568 " decCon="+decendantsObjConflict+ " NumOfParents=" + parents.size()+
569 " NumOfConParents=" + getNumOfReachableParents() + " ObjectChildren=" + objectRefs.size();