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.
15 //TODO it appears that using the optimize flags screws with the invar naming.
17 /* An instance of this class manages all OoOJava coarse-grained runtime conflicts
18 * by generating C-code to either rule out the conflict at runtime or resolve one.
21 * 1) Instantiate singleton object
22 * 2a) Call void traverse(FlatSESEEnterNode rblock, Hashtable<Taint, Set<Effect>> effects, Hashtable<Taint, Set<Effect>> conflicts, ReachGraph rg)
23 * as many times as needed
24 * 2b) call String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) to get the name of the traverse method in C
25 * 3) Call void close()
27 public class RuntimeConflictResolver {
28 private static final boolean debug = true;
30 private PrintWriter cFile;
31 private PrintWriter headerFile;
32 private static final String hashAndQueueCFileDir = "oooJava/";
34 // initializing variables can be found in printHeader()
35 private static final String getAllocSiteInC = "->allocsite";
36 private static final String queryAndAddHashTableInC = "hashRCRInsert(";
37 private static final String addToQueueInC = "enqueueRCRQueue(";
38 private static final String dequeueFromQueueInC = "dequeueRCRQueue()";
40 private static final String clearQueue = "resetRCRQueue()";
41 // Make hashtable; hashRCRCreate(unsigned int size, double loadfactor)
42 private static final String mallocHashTable = "hashRCRCreate(1024, 0.25)";
43 private static final String deallocHashTable = "hashRCRDelete()";
44 private static final String resetHashTable = "hashRCRreset()";
46 public RuntimeConflictResolver(String buildir) throws FileNotFoundException {
47 String outputFile = buildir + "RuntimeConflictResolver";
49 cFile = new PrintWriter(new File(outputFile + ".c"));
50 headerFile = new PrintWriter(new File(outputFile + ".h"));
52 cFile.append("#include \"" + hashAndQueueCFileDir + "hashRCR.h\"\n#include \""
53 + hashAndQueueCFileDir + "Queue_RCR.h\"\n#include <stdlib.h>\n");
54 cFile.append("#include \"classdefs.h\"\n");
55 cFile.append("#include \"RuntimeConflictResolver.h\"\n");
57 headerFile.append("#ifndef __3_RCR_H_\n");
58 headerFile.append("#define __3_RCR_H_\n");
59 //TODO more closely integrate this by asking generic type from other components?
61 cFile.append("struct genericObjectStruct {int type; int oid; int allocsite; int ___cachedCode___; int ___cachedHash___;};\n");
66 * 1) Create a hashed Effects Lookup Table (hashed by allocsite then fieldname)
67 * 1a) Use effects sets to verify if we can access something (reads)
68 * 1b) Use conflicts list to mark conflicts
69 * 2) Build runtime representation of data structure
70 * 2a) Mark conflicts with 2 flags (one for self, one for references down the line)
71 * 3) Printout via traversing data structure created in previous step.
73 public void traverse(FlatSESEEnterNode rblock,
74 Hashtable<Taint, Set<Effect>> effects,
75 Hashtable<Taint, Set<Effect>> conflicts,
78 Set<TempDescriptor> inVars = rblock.getInVarSet();
80 if (inVars.size() == 0)
83 // For every non-primitive variable, generate unique method
84 // Special Note: The Criteria for executing printCMethod in this loop should match
85 // exactly the criteria in buildcode.java to invoke the generated C method(s).
86 for (TempDescriptor invar : inVars) {
87 TypeDescriptor type = invar.getType();
88 if(type == null || type.isPrimitive()) {
92 //created stores nodes with specific alloc sites that have been traversed while building
93 //internal data structure. It is later traversed sequentially to find inset variables and
95 Hashtable<AllocSite, ConcreteRuntimeObjNode> created = new Hashtable<AllocSite, ConcreteRuntimeObjNode>();
96 VariableNode varNode = rg.getVariableNodeNoMutation(invar);
97 Hashtable<AllocSite, EffectsGroup> effectsLookupTable;
99 effectsLookupTable = generateEffectsLookupTable(rblock, varNode, effects, conflicts);
100 createConcreteGraph(effectsLookupTable, created, varNode);
102 if (!created.isEmpty()) {
103 rblock.addInVarForDynamicCoarseConflictResolution(invar);
104 printCMethods(created, invar.getSafeSymbol(), rblock.getPrettyIdentifier());
109 public String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) {
110 return "traverse___" + invar.getSafeSymbol().replaceAll(" ", "") +
111 sese.getPrettyIdentifier().replaceAll(" ", "") + "___("+varString+");";
114 public void close() {
115 // Adds Extra supporting methods
116 cFile.append("void initializeStructsRCR() { " + mallocHashTable + "; " + clearQueue + "; }");
117 cFile.append("void destroyRCR() { " + deallocHashTable + "; }\n");
119 headerFile.append("void initializeStructsRCR(); \nvoid destroyRCR(); \n");
120 headerFile.append("#endif\n");
126 //TODO it appears that using the optimize flags screws with the invar naming.
127 private void createConcreteGraph(
128 Hashtable<AllocSite, EffectsGroup> table,
129 Hashtable<AllocSite, ConcreteRuntimeObjNode> created,
130 VariableNode varNode) {
132 // if table is null that means there's no conflicts, therefore we need not
133 // create a traversal
139 System.out.println("==========Table print out============");
140 System.out.print(" Key is effect Exists/Conflict\n");
141 for(AllocSite allockey: table.keySet()) {
142 EffectsGroup eg= table.get(allockey);
143 if(eg.hasPrimativeConflicts()) {
144 System.out.print("Primitive Conflicts at alloc " + allockey.hashCode() +" : ");
145 for(String field: eg.primativeConflictingFields) {
146 System.out.print(field + " ");
148 System.out.println();
150 for(String fieldKey: eg.myEffects.keySet()) {
151 CombinedObjEffects ce = eg.myEffects.get(fieldKey);
152 System.out.println("\nFor allocSite " + allockey.hashCode() + " && field " + fieldKey);
153 System.out.println("\tread " + ce.hasReadEffect + "/"+ce.hasReadConflict +
154 " write " + ce.hasWriteEffect + "/" + ce.hasWriteConflict +
155 " SU " + ce.hasStrongUpdateEffect + "/" + ce.hasStrongUpdateConflict);
156 for(Effect ef: ce.originalEffects) {
157 System.out.println("\t" + ef);
161 System.out.println("===========End print out=============");
164 Iterator<RefEdge> possibleEdges = varNode.iteratorToReferencees();
166 while (possibleEdges.hasNext()) {
167 RefEdge edge = possibleEdges.next();
170 ConcreteRuntimeObjNode singleRoot = new ConcreteRuntimeObjNode(edge.getDst(), true);
171 AllocSite rootKey = singleRoot.allocSite;
173 if (!created.containsKey(rootKey)) {
174 created.put(rootKey, singleRoot);
175 createHelper(singleRoot, edge.getDst().iteratorToReferencees(), created, table);
180 private Hashtable<AllocSite, EffectsGroup> generateEffectsLookupTable(FlatSESEEnterNode rblock,
181 VariableNode var, Hashtable<Taint, Set<Effect>> effects,
182 Hashtable<Taint, Set<Effect>> conflicts) {
183 // we search effects since conflicts is only a subset of effects
184 Taint taint = getProperTaint(rblock, var, effects);
187 System.out.println("Null FOR " +var.getTempDescriptor().getSafeSymbol() + rblock.toPrettyString());
192 Set<Effect> localEffects = effects.get(taint);
193 Set<Effect> localConflicts = conflicts.get(taint);
195 // Debug Code for manually checking effects
197 System.out.println("#### List of Effects/Conflicts ####");
198 System.out.println("For Taint " + taint);
199 System.out.println("Effects");
200 if(localEffects != null) {
201 for(Effect e: localEffects) {
202 System.out.println(e);
205 System.out.println("Conflicts");
206 if(localConflicts != null) {
207 for(Effect e: localConflicts) {
208 System.out.println(e);
213 if (localEffects == null || localEffects.isEmpty() || localConflicts == null || localConflicts.isEmpty())
216 Hashtable<AllocSite, EffectsGroup> lookupTable = new Hashtable<AllocSite, EffectsGroup>();
218 for (Effect e : localEffects) {
219 boolean conflict = localConflicts.contains(e);
220 AllocSite key = e.getAffectedAllocSite();
221 EffectsGroup myEffects = lookupTable.get(key);
223 if(myEffects == null) {
224 myEffects = new EffectsGroup();
225 lookupTable.put(key, myEffects);
228 if(e.getField().getType().isPrimitive()) {
230 myEffects.addPrimative(e);
234 myEffects.addObjEffect(e, conflict);
241 // Plan is to add stuff to the tree depth-first sort of way. That way, we can
242 // propagate up conflicts
243 private void createHelper(ConcreteRuntimeObjNode curr,
244 Iterator<RefEdge> edges,
245 Hashtable<AllocSite, ConcreteRuntimeObjNode> created,
246 Hashtable<AllocSite, EffectsGroup> table) {
247 assert table != null;
249 AllocSite parentKey = curr.allocSite;
250 EffectsGroup currEffects = table.get(parentKey);
252 if (currEffects == null || currEffects.isEmpty())
256 if(currEffects.hasObjectEffects()) {
257 while(edges.hasNext()) {
258 RefEdge edge = edges.next();
259 String field = edge.getField();
260 CombinedObjEffects effectsForGivenField = currEffects.getObjEffect(field);
261 //If there are no effects, then there's no point in traversing this edge
262 if(effectsForGivenField != null) {
263 HeapRegionNode childHRN = edge.getDst();
264 AllocSite childKey = childHRN.getAllocSite();
265 boolean isNewChild = !created.containsKey(childKey);
266 ConcreteRuntimeObjNode child;
269 child = new ConcreteRuntimeObjNode(childHRN, false);
270 created.put(childKey, child);
273 child = created.get(childKey);
276 curr.addObjChild(field, child, effectsForGivenField);
278 if (effectsForGivenField.hasConflict() || child.decendantsObjConflict) {
279 propogateObjConflictFlag(child);
282 if (effectsForGivenField.hasReadEffect && isNewChild) {
283 createHelper(child, childHRN.iteratorToReferencees(), created, table);
290 if(currEffects.hasPrimativeConflicts()) {
291 curr.conflictingPrimitiveFields = currEffects.primativeConflictingFields;
292 propogatePrimConflictFlag(curr);
296 // This will propagate the conflict up the data structure.
297 private void propogateObjConflictFlag(ConcreteRuntimeObjNode in) {
298 ConcreteRuntimeObjNode node = in;
299 while(node.lastReferencer != null) {
300 node.lastReferencer.decendantsObjConflict = true;
301 if(!node.parentsThatWillLeadToConflicts.add(node.lastReferencer) &&
302 node.lastReferencer.isInsetVar)
304 node = node.lastReferencer;
308 private void propogatePrimConflictFlag(ConcreteRuntimeObjNode in) {
309 ConcreteRuntimeObjNode node = in;
310 while(node.lastReferencer != null) {
311 node.lastReferencer.decendantsPrimConflict = true;
312 if(!node.parentsThatWillLeadToConflicts.add(node.lastReferencer) &&
313 node.lastReferencer.isInsetVar)
315 node = node.lastReferencer;
320 * This method generates a C method for every inset variable and rblock.
322 * The C method works by generating a large switch statement that will run the appropriate
323 * checking code for each object based on its allocation site. The switch statement is
324 * surrounded by a while statement which dequeues objects to be checked from a queue. An
325 * object is added to a queue only if it contains a conflict (in itself or in its referencees)
326 * and we came across it while checking through it's referencer. Because of this property,
327 * conflicts will be signaled by the referencer; the only exception is the inset variable which can
328 * signal a conflict within itself.
330 private void printCMethods(Hashtable<AllocSite, ConcreteRuntimeObjNode> created, String inVar, String rBlock) {
331 //This hash table keeps track of all the case statements generated. Although it may seem a bit much
332 //for its purpose, I think it may come in handy later down the road to do it this way.
333 //(i.e. what if we want to eliminate some cases? Or build filter for 1 case)
334 Hashtable<AllocSite, StringBuilder> cases = new Hashtable<AllocSite, StringBuilder>();
337 for (ConcreteRuntimeObjNode node : created.values()) {
338 // If we haven't seen it and it's a node with more than 1 parent
339 // Note: a node with 0 parents is a root node (i.e. inset variable)
340 if (!cases.containsKey(node.allocSite) &&
341 (node.getNumOfReachableParents() != 1 || node.isInsetVar) &&
342 (node.decendantsConflict() || node.hasPrimativeConflicts())) {
343 //resets the lastReferncer if we're dealing with an insetVar
344 node.lastReferencer = null;
345 addChecker(node, cases, null, "ptr", 0);
349 //IMPORTANT: remember to change getTraverserInvocation if you change the line below
350 String methodName = "void traverse___" + inVar.replaceAll(" ", "") + rBlock.replaceAll(" ", "") +
353 cFile.append(methodName + " {\n");
354 headerFile.append(methodName + ";\n");
357 cFile.append("printf(\"The traverser ran for " + methodName + "\\n\");\n");
360 if(cases.size() == 0) {
361 cFile.append(" return; }");
364 //Casts the ptr to a genericObjectStruct so we can get to the ptr->allocsite field.
365 cFile.append("struct genericObjectStruct * ptr = (struct genericObjectStruct *) InVar; if(InVar != NULL) { " + queryAndAddHashTableInC
368 cFile.append("switch(ptr->allocsite) { ");
370 for(AllocSite singleCase: cases.keySet())
371 cFile.append(cases.get(singleCase));
373 cFile.append(" default : break; ");
374 cFile.append("}} while( (ptr = " + dequeueFromQueueInC + ") != NULL); ");
376 //Closes the method by clearing the Queue and reseting the hashTable to prevent
377 //overhead from freeing and mallocing the structures.
378 cFile.append(clearQueue + "; " + resetHashTable + "; }}\n");
384 * addChecker creates a case statement for every object that is either an inset variable
385 * or has multiple referencers (incoming edges). Else it just prints the checker for that object
386 * so that its processing can be pushed up to the referencer node.
388 private void addChecker(ConcreteRuntimeObjNode node,
389 Hashtable<AllocSite,StringBuilder> cases,
390 StringBuilder possibleContinuingCase,
393 StringBuilder currCase = possibleContinuingCase;
394 // We don't need a case statement for things with either 1 incoming or 0 out
395 // going edges, because they can be processed when checking the parent.
396 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) &&
397 (node.decendantsConflict() || node.hasPrimativeConflicts())) {
399 assert prefix.equals("ptr") && !cases.containsKey(node.allocSite);
400 currCase = new StringBuilder();
401 cases.put(node.allocSite, currCase);
402 currCase.append("case " + node.getAllocationSite() + ":\n { ");
404 //either currCase is continuing off a parent case or is its own.
405 assert currCase !=null;
407 //Specific Primitives test for invars
408 if(node.isInsetVar && node.hasPrimativeConflicts()) {
409 handlePrimitiveConflict(currCase, prefix, node.conflictingPrimitiveFields, node.allocSite);
412 //Casts C pointer; depth is used to create unique "myPtr" name for when things are inlined
413 String currPtr = "myPtr" + depth;
414 String structType = node.original.getType().getSafeSymbol();
415 currCase.append("struct " + structType + " * "+currPtr+"= (struct "+ structType + " * ) " + prefix + "; ");
418 for (ObjRef ref : node.objectRefs) {
419 // Will only process edge if there is some sort of conflict with the Child
420 if (ref.hasConflictsDownThisPath()) {
421 String childPtr = currPtr +"->___" + ref.field + "___";
423 // Checks if the child exists and has allocsite matching the conflict
424 currCase.append("if(" + childPtr + " != NULL && " + childPtr + getAllocSiteInC + "=="
425 + ref.allocSite + ") { ");
427 // Prints out conflicts of child
428 if (ref.hasDirectObjConflict()) {
429 handleObjConflict(currCase, childPtr, node.allocSite, ref);
432 if(ref.child.hasPrimativeConflicts()) {
433 handlePrimitiveConflict(currCase, childPtr, ref.child.conflictingPrimitiveFields, ref.child.allocSite);
436 if (ref.child.decendantsConflict()) {
437 // Checks if we have visited the child before
438 currCase.append("if(" + queryAndAddHashTableInC + childPtr + ")) { ");
439 if (ref.child.getNumOfReachableParents() == 1 && !ref.child.isInsetVar) {
440 addChecker(ref.child, cases, currCase, childPtr, depth + 1);
443 currCase.append(addToQueueInC + childPtr + ");");
446 currCase.append(" } ");
448 currCase.append(" } ");
452 if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) &&
453 (node.decendantsConflict() || node.hasPrimativeConflicts()))
454 currCase.append(" } break; \n");
457 private void handleObjConflict(StringBuilder currCase, String childPtr, AllocSite allocSite, ObjRef ref) {
458 currCase.append("printf(\"Conflict detected with %p from parent with allocation site %u\\n\"," + childPtr + "," + allocSite.hashCodeSpecific() + ");");
461 private void handlePrimitiveConflict(StringBuilder currCase, String ptr, ArrayList<String> conflicts, AllocSite allocSite) {
462 currCase.append("printf(\"Primitive Conflict detected with %p with alloc site %u\\n\", "+ptr+", "+allocSite.hashCodeSpecific()+"); ");
465 private Taint getProperTaint(FlatSESEEnterNode rblock, VariableNode var,
466 Hashtable<Taint, Set<Effect>> effects) {
467 Set<Taint> taints = effects.keySet();
469 for (Taint t : taints) {
470 FlatSESEEnterNode sese = t.getSESE();
471 //Jim says that this case should never happen, but it may
473 System.out.println( "What should I do with a stall site taint? --Jim");
475 if(sese != null && sese.equals(rblock) && t.getVar().equals(var.getTempDescriptor())) {
482 private class EffectsGroup
484 Hashtable<String, CombinedObjEffects> myEffects;
485 ArrayList<String> primativeConflictingFields;
487 public EffectsGroup() {
488 myEffects = new Hashtable<String, CombinedObjEffects>();
489 primativeConflictingFields = new ArrayList<String>();
492 public void addPrimative(Effect e) {
493 primativeConflictingFields.add(e.getField().toPrettyStringBrief());
496 public void addObjEffect(Effect e, boolean conflict) {
497 CombinedObjEffects effects;
498 if((effects = myEffects.get(e.getField().getSymbol())) == null) {
499 effects = new CombinedObjEffects();
500 myEffects.put(e.getField().getSymbol(), effects);
502 effects.add(e, conflict);
505 public boolean isEmpty() {
506 return myEffects.isEmpty() && primativeConflictingFields.isEmpty();
509 public boolean hasPrimativeConflicts(){
510 return !primativeConflictingFields.isEmpty();
513 public boolean hasObjectEffects() {
514 return !myEffects.isEmpty();
517 public CombinedObjEffects getObjEffect(String field) {
518 return myEffects.get(field);
522 //Is the combined effects for all effects with the same affectedAllocSite and field
523 private class CombinedObjEffects {
524 ArrayList<Effect> originalEffects;
526 public boolean hasReadEffect;
527 public boolean hasWriteEffect;
528 public boolean hasStrongUpdateEffect;
530 public boolean hasReadConflict;
531 public boolean hasWriteConflict;
532 public boolean hasStrongUpdateConflict;
535 public CombinedObjEffects() {
536 originalEffects = new ArrayList<Effect>();
538 hasReadEffect = false;
539 hasWriteEffect = false;
540 hasStrongUpdateEffect = false;
542 hasReadConflict = false;
543 hasWriteConflict = false;
544 hasStrongUpdateConflict = false;
547 public boolean add(Effect e, boolean conflict) {
548 if(!originalEffects.add(e))
551 switch(e.getType()) {
553 hasReadEffect = true;
554 hasReadConflict = conflict;
557 hasWriteEffect = true;
558 hasWriteConflict = conflict;
560 case Effect.strongupdate:
561 hasStrongUpdateEffect = true;
562 hasStrongUpdateConflict = conflict;
565 System.out.println("RCR ERROR: An Effect Type never seen before has been encountered");
570 public boolean hasConflict() {
571 return hasReadConflict || hasWriteConflict || hasStrongUpdateConflict;
575 // private class EffectPair {
576 // Effect originalEffect;
580 // public EffectPair(Effect e, boolean conflict) {
581 // originalEffect = e;
582 // type = e.getType();
583 // this.conflict = conflict;
586 // public int hashCode() {
587 // return originalEffect.hashCode();
590 // public boolean equals(Object o) {
594 // if (!(o instanceof EffectPair))
597 // EffectPair other = (EffectPair) o;
599 // return (other.originalEffect.getAffectedAllocSite().equals(
600 // originalEffect.getAffectedAllocSite()) && other.originalEffect.getField().equals(
601 // originalEffect.getField()));
604 // public String toString()
606 // return originalEffect.toString();
610 //This will keep track of a reference
611 private class ObjRef {
614 CombinedObjEffects myEffects;
616 //This keeps track of the parent that we need to pass by inorder to get
617 //to the conflicting child (if there is one).
618 ConcreteRuntimeObjNode child;
620 public ObjRef(String fieldname,
621 ConcreteRuntimeObjNode ref,
622 CombinedObjEffects myEffects) {
624 allocSite = ref.getAllocationSite();
627 this.myEffects = myEffects;
630 public boolean hasConflictsDownThisPath() {
631 return child.decendantsObjConflict || child.decendantsPrimConflict || child.hasPrimativeConflicts() || myEffects.hasConflict();
634 public boolean hasDirectObjConflict() {
635 return myEffects.hasConflict();
639 private class ConcreteRuntimeObjNode {
640 ArrayList<ObjRef> objectRefs;
641 ArrayList<String> conflictingPrimitiveFields;
642 HashSet<ConcreteRuntimeObjNode> parentsThatWillLeadToConflicts;
643 ConcreteRuntimeObjNode lastReferencer;
644 boolean decendantsPrimConflict;
645 boolean decendantsObjConflict;
648 HeapRegionNode original;
650 public ConcreteRuntimeObjNode(HeapRegionNode me, boolean isInVar) {
651 objectRefs = new ArrayList<ObjRef>();
652 parentsThatWillLeadToConflicts = new HashSet<ConcreteRuntimeObjNode>();
653 lastReferencer = null;
654 allocSite = me.getAllocSite();
656 isInsetVar = isInVar;
657 decendantsPrimConflict = false;
658 decendantsObjConflict = false;
659 conflictingPrimitiveFields = null;
663 public int hashCode() {
664 // This gets allocsite number
665 return allocSite.hashCodeSpecific();
669 public boolean equals(Object obj) {
670 return original.equals(obj);
673 public int getAllocationSite() {
674 return allocSite.hashCodeSpecific();
677 public int getNumOfReachableParents() {
678 return parentsThatWillLeadToConflicts.size();
681 public boolean hasPrimativeConflicts() {
682 return conflictingPrimitiveFields != null;
685 public boolean decendantsConflict() {
686 return decendantsPrimConflict || decendantsObjConflict;
689 public void addObjChild(String field, ConcreteRuntimeObjNode child, CombinedObjEffects ce) {
690 child.lastReferencer = this;
691 ObjRef ref = new ObjRef(field, child, ce);
695 public String toString()
697 return "AllocSite=" + getAllocationSite() + " myConflict=" + !parentsThatWillLeadToConflicts.isEmpty() +
698 " decCon="+decendantsObjConflict+
699 " NumOfConParents=" + getNumOfReachableParents() + " ObjectChildren=" + objectRefs.size();