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 Make more efficient by only using ONE hashtable.
16 //TODO make threads be aware of each other and add another check for other rblocks or something
17 //to fix issue of sometimes one thread marking conflict and not the other.
18 //TODO it appears that using the optimize flags screws with the invar naming.
20 /* An instance of this class manages all OoOJava coarse-grained runtime conflicts
21 * by generating C-code to either rule out the conflict at runtime or resolve one.
24 * 1) Instantiate singleton object
25 * 2a) Call void traverse(FlatSESEEnterNode rblock, Hashtable<Taint, Set<Effect>> effects, Hashtable<Taint, Set<Effect>> conflicts, ReachGraph rg)
26 * as many times as needed
27 * 2b) call String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) to get the name of the traverse method in C
28 * 3) Call void close()
30 public class RuntimeConflictResolver {
31 private static boolean debug = false;
33 public static String outputFile;
34 private PrintWriter cFile;
35 private PrintWriter headerFile;
36 private static final String hashAndQueueCFileDir = "oooJava/";
38 // initializing variables can be found in printHeader()
39 private static final String getAllocSiteInC = "->allocsite";
40 private static final String queryAndAddHashTableInC = "hashRCRInsert(";
41 private static final String addToQueueInC = "enqueueRCRQueue(";
42 private static final String dequeueFromQueueInC = "dequeueRCRQueue()";
44 private static final String clearQueue = "resetRCRQueue()";
45 // Make hashtable; hashRCRCreate(unsigned int size, double loadfactor)
46 private static final String mallocHashTable = "hashRCRCreate(1024, 0.25)";
47 private static final String deallocHashTable = "hashRCRDelete()";
48 private static final String resetHashTable = "hashRCRreset()";
50 public RuntimeConflictResolver(String buildir) throws FileNotFoundException {
51 outputFile = buildir + "RuntimeConflictResolver";
53 cFile = new PrintWriter(new File(outputFile + ".c"));
54 headerFile = new PrintWriter(new File(outputFile + ".h"));
56 cFile.append("#include \"" + hashAndQueueCFileDir + "hashRCR.h\"\n#include \""
57 + hashAndQueueCFileDir + "Queue_RCR.h\"\n#include <stdlib.h>\n");
58 cFile.append("#include \"classdefs.h\"\n");
59 cFile.append("#include \"RuntimeConflictResolver.h\"\n");
61 headerFile.append("#ifndef __3_RCR_H_\n");
62 headerFile.append("#define __3_RCR_H_\n");
63 //TODO more closely integrate this by asking generic type from other components?
65 cFile.append("struct genericObjectStruct {int type; int oid; int allocsite; int ___cachedCode___; int ___cachedHash___;};\n");
70 * 1) Create pruned data structures from givens
71 * 1a) Use effects sets to verify if we can access something (reads)
72 * 1b) Mark conflicts with 2 flags (one for self, one for references down the line)
73 * 2) build code output structure
76 public void traverse(FlatSESEEnterNode rblock,
77 Hashtable<Taint, Set<Effect>> effects,
78 Hashtable<Taint, Set<Effect>> conflicts,
81 Set<TempDescriptor> inVars = rblock.getInVarSet();
83 if (inVars.size() == 0)
86 // For every non-primitive variable, generate unique method
87 // Special Note: The Criteria for executing printCMethod in this loop should match
88 // exactly the criteria in buildcode.java to invoke the generated C method(s).
89 for (TempDescriptor invar : inVars) {
90 TypeDescriptor type = invar.getType();
91 if(type == null || type.isPrimitive()) {
95 Hashtable<AllocSite, ConcreteRuntimeObjNode> created = new Hashtable<AllocSite, ConcreteRuntimeObjNode>();
97 createTree(rblock, invar, effects, conflicts, rg, created);
98 if (!created.isEmpty()) {
99 rblock.addInVarForDynamicCoarseConflictResolution(invar);
100 printCMethod(created, invar.getSafeSymbol(), rblock.getPrettyIdentifier());
105 public String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) {
106 return "traverse___" + invar.getSafeSymbol().replaceAll(" ", "") +
107 sese.getPrettyIdentifier().replaceAll(" ", "") + "___("+varString+");";
110 public void close() {
111 // Adds Extra supporting methods
112 cFile.append("void initializeStructsRCR() { " + mallocHashTable + "; " + clearQueue + "; }");
113 cFile.append("void destroyRCR() { " + deallocHashTable + "; }\n");
115 headerFile.append("void initializeStructsRCR(); \nvoid destroyRCR(); \n");
116 headerFile.append("#endif\n");
122 //TODO it appears that using the optimize flags screws with the invar naming.
123 private void createTree(FlatSESEEnterNode rblock,
124 TempDescriptor invar,
125 Hashtable<Taint, Set<Effect>> effects,
126 Hashtable<Taint, Set<Effect>> conflicts,
128 Hashtable<AllocSite, ConcreteRuntimeObjNode> created) {
130 VariableNode varNode = rg.getVariableNodeNoMutation(invar);
132 //TODO Fix bug where it recognizes multiple effects for the same string ><
133 Hashtable<AllocSite, EffectsGroup> table =
134 generateEffectsLookupTable(rblock, varNode, effects, conflicts);
136 // if table is null that means there's no conflicts, therefore we need not
137 // create a traversal
142 System.out.println("==========Table print out============");
143 for(AllocSite key: table.keySet()) {
144 EffectsGroup eg= table.get(key);
145 for(String innerKey: eg.myEffects.keySet()) {
146 EffectPair ef = eg.myEffects.get(innerKey);
147 System.out.println(key.hashCode() + "." + innerKey + " conflict="+ ef.conflict );
152 Iterator<RefEdge> possibleEdges = varNode.iteratorToReferencees();
154 while (possibleEdges.hasNext()) {
155 RefEdge edge = possibleEdges.next();
158 ConcreteRuntimeObjNode singleRoot = new ConcreteRuntimeObjNode(edge.getDst(), true);
159 AllocSite rootKey = singleRoot.allocSite;
161 if (!created.containsKey(rootKey)) {
162 created.put(rootKey, singleRoot);
163 createHelper(singleRoot, edge.getDst().iteratorToReferencees(), created, table);
168 private Hashtable<AllocSite, EffectsGroup> generateEffectsLookupTable(FlatSESEEnterNode rblock,
169 VariableNode var, Hashtable<Taint, Set<Effect>> effects,
170 Hashtable<Taint, Set<Effect>> conflicts) {
171 // we search effects since conflicts is only a subset of effects
172 Taint taint = getProperTaint(rblock, var, effects);
175 System.out.println("Null FOR " +var.getTempDescriptor().getSafeSymbol() + rblock.toPrettyString());
180 Set<Effect> localEffects = effects.get(taint);
181 Set<Effect> localConflicts = conflicts.get(taint);
183 if (localEffects == null || localEffects.isEmpty() || localConflicts == null || localConflicts.isEmpty())
186 // Debug Code for manually checking effects
188 System.out.println("#### List of Effects/Conflicts ####");
189 System.out.println("For Taint " + taint);
190 System.out.println("Effects");
191 for(Effect e: localEffects)
193 System.out.println(e);
196 System.out.println("Conflicts");
197 for(Effect e: localConflicts)
199 System.out.println(e);
203 Hashtable<AllocSite, EffectsGroup> lookupTable = new Hashtable<AllocSite, EffectsGroup>();
205 for (Effect e : localEffects) {
206 boolean conflict = localConflicts.contains(e);
207 AllocSite key = e.getAffectedAllocSite();
208 EffectsGroup myEffects = lookupTable.get(key);
210 if(myEffects == null) {
211 myEffects = new EffectsGroup();
212 lookupTable.put(key, myEffects);
215 if(e.getField().getType().isPrimitive()) {
217 myEffects.addPrimative(e);
221 myEffects.addObj(e, conflict);
228 // Plan is to add stuff to the tree depth-first sort of way. That way, we can
229 // propagate up conflicts
230 private void createHelper(ConcreteRuntimeObjNode curr,
231 Iterator<RefEdge> edges,
232 Hashtable<AllocSite, ConcreteRuntimeObjNode> created,
233 Hashtable<AllocSite, EffectsGroup> table) {
234 assert table != null;
236 AllocSite parentKey = curr.allocSite;
237 EffectsGroup currEffects = table.get(parentKey);
239 if (currEffects == null || currEffects.isEmpty())
243 if(currEffects.hasObjectConflicts()) {
244 while(edges.hasNext()) {
245 RefEdge edge = edges.next();
246 String field = edge.getField();
247 EffectPair effect = currEffects.getObjEffect(field); // TODO are you certain there is only one effect to get here?
248 //If there is no effect, then there's not point in traversing this edge
251 HeapRegionNode childHRN = edge.getDst();
252 AllocSite childKey = childHRN.getAllocSite();
253 boolean isNewChild = !created.containsKey(childKey);
254 ConcreteRuntimeObjNode child;
257 child = new ConcreteRuntimeObjNode(childHRN, false);
258 created.put(childKey, child);
261 child = created.get(childKey);
264 curr.addObjChild(field, child, effect.conflict);
266 if (effect.conflict) {
267 propogateObjConflictFlag(child);
270 if (effect.type == Effect.read && isNewChild) {
271 createHelper(child, childHRN.iteratorToReferencees(), created, table);
278 if(currEffects.hasPrimativeConflicts()) {
279 curr.primativeFields = currEffects.primativeConflictingFields;
280 propogatePrimConflictFlag(curr);
284 // This will propagate the conflict up the data structure.
285 private void propogateObjConflictFlag(ConcreteRuntimeObjNode in) {
286 ConcreteRuntimeObjNode node = in;
288 while(node.lastReferencer != null) {
289 node.lastReferencer.decendantsObjConflict = true;
290 if(!node.conflictingParents.add(node.lastReferencer))
292 node = node.lastReferencer;
296 private void propogatePrimConflictFlag(ConcreteRuntimeObjNode in) {
297 ConcreteRuntimeObjNode node = in;
299 while(node.lastReferencer != null) {
300 node.lastReferencer.decendantsPrimConflict = true;
301 if(!node.conflictingParents.add(node.lastReferencer))
303 node = node.lastReferencer;
308 * This method generates a C method for every inset variable and rblock.
310 * The C method works by generating a large switch statement that will run the appropriate
311 * checking code for each object based on its allocation site. The switch statement is
312 * surrounded by a while statement which dequeues objects to be checked from a queue. An
313 * object is added to a queue only if it contains a conflict (in itself or in its referencees)
314 * and we came across it while checking through it's referencer. Because of this property,
315 * conflicts will be signaled by the referencer; the only exception is the inset variable which can
316 * signal a conflict within itself.
318 private void printCMethod(Hashtable<AllocSite, ConcreteRuntimeObjNode> created, String inVar, String rBlock) {
319 //This hash table keeps track of all the case statements generated. Although it may seem a bit much
320 //for its purpose, I think it may come in handy later down the road to do it this way.
321 //(i.e. what if we want to eliminate some cases? Or build filter for 1 case)
322 Hashtable<AllocSite, StringBuilder> cases = new Hashtable<AllocSite, StringBuilder>();
325 for (ConcreteRuntimeObjNode node : created.values()) {
326 // If we haven't seen it and it's a node with more than 1 parent
327 // Note: a node with 0 parents is a root node (i.e. inset variable)
328 if (!cases.containsKey(node.allocSite) && (node.getNumOfReachableParents() != 1 || node.isInsetVar)
329 && node.decendantsConflict()) {
330 //resets the lastReferncer if we're dealing with an insetVar
331 node.lastReferencer = null;
332 addChecker(node, cases, null, "ptr", 0);
336 //IMPORTANT: remember to change getTraverserInvocation if you change the line below
337 String methodName = "void traverse___" + inVar.replaceAll(" ", "") + rBlock.replaceAll(" ", "") +
340 cFile.append(methodName + " {\n");
341 headerFile.append(methodName + ";\n");
343 cFile.append("printf(\"The traverser ran for " + methodName + "\\n\");\n");
345 if(cases.size() == 0) {
346 cFile.append(" return; }");
349 //Casts the ptr to a genericObjectStruct so we can get to the ptr->allocsite field.
350 cFile.append("struct genericObjectStruct * ptr = (struct genericObjectStruct *) InVar; if(InVar != NULL) { " + queryAndAddHashTableInC
353 cFile.append("switch(ptr->allocsite) { ");
355 for(AllocSite singleCase: cases.keySet())
356 cFile.append(cases.get(singleCase));
358 cFile.append(" default : break; ");
359 cFile.append("}} while( (ptr = " + dequeueFromQueueInC + ") != NULL); ");
361 //Closes the method by clearing the Queue and reseting the hashTable to prevent
362 //overhead from freeing and mallocing the structures.
363 cFile.append(clearQueue + "; " + resetHashTable + "; }}\n");
369 * addChecker creates a case statement for every object that is either an inset variable
370 * or has multiple referencers (incoming edges). Else it just prints the checker for that object
371 * so that its processing can be pushed up to the referencer node.
373 private void addChecker(ConcreteRuntimeObjNode node,
374 Hashtable<AllocSite,StringBuilder> cases,
375 StringBuilder possibleContinuingCase,
378 StringBuilder currCase = possibleContinuingCase;
379 // We don't need a case statement for things with either 1 incoming or 0 out
380 // going edges, because they can be processed when checking the parent.
381 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) && node.decendantsConflict()) {
382 assert prefix.equals("ptr") && !cases.containsKey(node.allocSite);
383 currCase = new StringBuilder();
384 cases.put(node.allocSite, currCase);
385 currCase.append("case " + node.getAllocationSite() + ":\n { ");
387 //either currCase is continuing off a parent case or is its own.
388 assert currCase !=null;
390 //Specific Primitives test for invars
391 if(node.isInsetVar && node.hasPrimativeConflicts()) {
392 handlePrimitiveConflict(currCase, prefix, node.primativeFields, node.allocSite);
395 //Casts C pointer; depth is used to create unique "myPtr" name for when things are inlined
396 String currPtr = "myPtr" + depth;
397 String structType = node.original.getType().getSafeSymbol();
398 currCase.append("struct " + structType + " * "+currPtr+"= (struct "+ structType + " * ) " + prefix + "; ");
401 for (ObjRef ref : node.objectRefs) {
402 // Will only process edge if there is some sort of conflict with the Child
403 if (ref.hasConflictsDownThisPath(node)) {
404 String childPtr = currPtr +"->___" + ref.field + "___";
406 // Checks if the child exists and has allocsite matching the conflict
407 currCase.append("if(" + childPtr + " != NULL && " + childPtr + getAllocSiteInC + "=="
408 + ref.allocSite + ") { ");
410 // Prints out conflicts of child
411 if (ref.child.hasConflicts())
412 handleObjConflict(currCase, childPtr, node.allocSite);
414 if(ref.child.hasPrimativeConflicts())
415 handlePrimitiveConflict(currCase, childPtr, ref.child.primativeFields, ref.child.allocSite);
417 if (ref.child.decendantsConflict()) {
418 // Checks if we have visited the child before
419 currCase.append("if(!" + queryAndAddHashTableInC + childPtr + ")) { ");
420 if (ref.child.getNumOfReachableParents() == 1 && !ref.child.isInsetVar) {
421 addChecker(ref.child, cases, currCase, childPtr, depth + 1);
424 currCase.append(addToQueueInC + childPtr + ");");
427 currCase.append(" } ");
429 currCase.append(" } ");
433 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) && node.decendantsConflict())
434 currCase.append(" } break; \n");
437 private void handleObjConflict(StringBuilder currCase, String childPtr, AllocSite allocSite) {
438 currCase.append("printf(\"Conflict detected with %p from parent with allocation site %u\\n\"," + childPtr + "," + allocSite.hashCodeSpecific() + ");");
441 private void handlePrimitiveConflict(StringBuilder currCase, String ptr, ArrayList<String> conflicts, AllocSite allocSite) {
442 currCase.append("printf(\"Primitive Conflict detected with %p with alloc site %u\\n\", "+ptr+", "+allocSite.hashCodeSpecific()+"); ");
445 private Taint getProperTaint(FlatSESEEnterNode rblock, VariableNode var,
446 Hashtable<Taint, Set<Effect>> effects) {
447 Set<Taint> taints = effects.keySet();
449 for (Taint t : taints) {
450 FlatSESEEnterNode sese = t.getSESE();
451 //Jim says that this case should never happen, but it may
453 System.out.println( "What should I do with a stall site taint? --Jim");
455 if(sese != null && sese.equals(rblock) && t.getVar().equals(var.getTempDescriptor())) {
462 private class EffectsGroup
464 Hashtable<String, EffectPair> myEffects;
465 ArrayList<String> primativeConflictingFields;
467 public EffectsGroup() {
468 myEffects = new Hashtable<String, EffectPair>();
469 primativeConflictingFields = new ArrayList<String>();
472 public void addPrimative(Effect e) {
473 primativeConflictingFields.add(e.getField().toPrettyStringBrief());
476 public void addObj(Effect e, boolean conflict) {
477 EffectPair effect = new EffectPair(e, conflict);
478 myEffects.put(e.getField().getSymbol(), effect);
481 public boolean isEmpty() {
482 return myEffects.isEmpty() && primativeConflictingFields.isEmpty();
485 public boolean hasPrimativeConflicts(){
486 return !primativeConflictingFields.isEmpty();
489 public boolean hasObjectConflicts() {
490 return !myEffects.isEmpty();
493 public EffectPair getObjEffect(String field) {
494 return myEffects.get(field);
498 private class EffectPair {
499 Effect originalEffect;
503 public EffectPair(Effect e, boolean conflict) {
506 this.conflict = conflict;
509 public int hashCode() {
510 return originalEffect.hashCode();
513 public boolean equals(Object o) {
517 if (!(o instanceof EffectPair))
520 EffectPair other = (EffectPair) o;
522 return (other.originalEffect.getAffectedAllocSite().equals(
523 originalEffect.getAffectedAllocSite()) && other.originalEffect.getField().equals(
524 originalEffect.getField()));
527 public String toString()
529 return originalEffect.toString();
533 private class ObjRef {
536 //This keeps track of the parent that we need to pass by inorder to get
537 //to the conflicting child (if there is one).
538 ConcreteRuntimeObjNode parentPathToMe;
539 ConcreteRuntimeObjNode child;
541 public ObjRef(String fieldname,
542 ConcreteRuntimeObjNode ref,
544 ConcreteRuntimeObjNode grandParent) {
546 allocSite = ref.getAllocationSite();
548 parentPathToMe = con ? grandParent : null;
551 public boolean hasConflictsDownThisPath(ConcreteRuntimeObjNode curr) {
552 if(!child.hasConflicts())
555 if(curr.conflictingParents.isEmpty() && curr.isInsetVar)
558 for(ConcreteRuntimeObjNode parent: curr.conflictingParents)
559 // we can do a == since it will LITTERALLY be the same object.
560 if(parent == parentPathToMe)
567 private class ConcreteRuntimeObjNode {
568 ArrayList<ObjRef> objectRefs;
569 ArrayList<String> primativeFields;
570 ArrayList<ConcreteRuntimeObjNode> parents;
571 HashSet<ConcreteRuntimeObjNode> conflictingParents;
572 ConcreteRuntimeObjNode lastReferencer;
573 boolean decendantsPrimConflict;
574 boolean decendantsObjConflict;
577 HeapRegionNode original;
579 public ConcreteRuntimeObjNode(HeapRegionNode me, boolean isInVar) {
580 objectRefs = new ArrayList<ObjRef>();
581 parents = new ArrayList<ConcreteRuntimeObjNode>();
582 conflictingParents = new HashSet<ConcreteRuntimeObjNode>();
583 lastReferencer = null;
584 allocSite = me.getAllocSite();
586 isInsetVar = isInVar;
587 decendantsPrimConflict = false;
588 decendantsObjConflict = false;
589 primativeFields = null;
593 public int hashCode() {
594 // This gets allocsite number
595 return allocSite.hashCodeSpecific();
599 public boolean equals(Object obj) {
600 return original.equals(obj);
603 public int getAllocationSite() {
604 return allocSite.hashCodeSpecific();
607 public int getNumOfReachableParents() {
608 return conflictingParents.size();
611 public boolean hasPrimativeConflicts() {
612 return primativeFields != null;
615 public boolean hasConflicts() {
616 return (primativeFields != null) || !conflictingParents.isEmpty();
619 public boolean decendantsConflict() {
620 return decendantsPrimConflict || decendantsObjConflict;
623 public void addObjChild(String field, ConcreteRuntimeObjNode child, boolean conflict) {
624 child.lastReferencer = this;
625 ObjRef ref = new ObjRef(field, child, conflict, this.lastReferencer);
627 child.parents.add(this);
630 public String toString()
632 return "AllocSite=" + getAllocationSite() + " myConflict=" + !conflictingParents.isEmpty() +
633 " decCon="+decendantsObjConflict+ " NumOfParents=" + parents.size()+
634 " NumOfConParents=" + getNumOfReachableParents() + " ObjectChildren=" + objectRefs.size();