1 package Analysis.Locality;
4 import Analysis.CallGraph.CallGraph;
8 import IR.MethodDescriptor;
10 import IR.ClassDescriptor;
12 public class LocalityAnalysis {
15 Hashtable<LocalityBinding,LocalityBinding> discovered;
16 Hashtable<LocalityBinding, Set<LocalityBinding>> dependence;
17 Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>> temptab;
18 Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>> atomictab;
19 Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>> tempstosave;
20 Hashtable<ClassDescriptor, Set<LocalityBinding>> classtolb;
21 Hashtable<MethodDescriptor, Set<LocalityBinding>> methodtolb;
22 private LocalityBinding lbmain;
26 public static final Integer LOCAL=new Integer(0);
27 public static final Integer GLOBAL=new Integer(1);
28 public static final Integer EITHER=new Integer(2);
29 public static final Integer CONFLICT=new Integer(3);
31 public LocalityAnalysis(State state, CallGraph callgraph, TypeUtil typeutil) {
32 this.typeutil=typeutil;
34 this.discovered=new Hashtable<LocalityBinding,LocalityBinding>();
35 this.dependence=new Hashtable<LocalityBinding, Set<LocalityBinding>>();
36 this.temptab=new Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>>();
37 this.atomictab=new Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>>();
38 this.lbtovisit=new Stack();
39 this.callgraph=callgraph;
40 this.tempstosave=new Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>>();
41 this.classtolb=new Hashtable<ClassDescriptor, Set<LocalityBinding>>();
42 this.methodtolb=new Hashtable<MethodDescriptor, Set<LocalityBinding>>();
46 public LocalityBinding getMain() {
50 /** This method returns the set of LocalityBindings that a given
51 * flatcall could invoke */
53 public LocalityBinding getBinding(LocalityBinding currlb, FlatCall fc) {
54 boolean isatomic=getAtomic(currlb).get(fc).intValue()>0;
55 Hashtable<TempDescriptor, Integer> currtable=getNodeTempInfo(currlb).get(fc);
56 MethodDescriptor md=fc.getMethod();
58 boolean isnative=md.getModifiers().isNative();
60 LocalityBinding lb=new LocalityBinding(md, isatomic);
62 for(int i=0;i<fc.numArgs();i++) {
63 TempDescriptor arg=fc.getArg(i);
64 lb.setGlobal(i,currtable.get(arg));
66 if (fc.getThis()!=null) {
67 Integer thistype=currtable.get(fc.getThis());
70 lb.setGlobalThis(thistype);
72 lb.setGlobalThis(EITHER);//default value
73 if (discovered.containsKey(lb))
74 lb=discovered.get(lb);
75 else throw new Error();
80 /** This method returns a set of LocalityBindings for the parameter class. */
82 public Set<LocalityBinding> getClassBindings(ClassDescriptor cd) {
83 return classtolb.get(cd);
86 /** This method returns a set of LocalityBindings for the parameter method. */
88 public Set<LocalityBinding> getMethodBindings(MethodDescriptor md) {
89 return methodtolb.get(md);
92 /** This method returns a set of LocalityBindings. A
93 * LocalityBinding specifies a context a method can be invoked in.
94 * It specifies whether the method is in a transaction and whether
95 * its parameter objects are locals or globals. */
97 public Set<LocalityBinding> getLocalityBindings() {
98 return discovered.keySet();
101 /** This method returns a hashtable for a given LocalityBinding
102 * that tells the current local/global status of temps at the each
103 * node in the flat representation. */
105 public Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> getNodeTempInfo(LocalityBinding lb) {
106 return temptab.get(lb);
109 /** This method returns an hashtable for a given LocalitBinding
110 * that tells whether a node in the flat represenation is in a
111 * transaction or not. Integer values greater than 0 indicate
112 * that the node is in a transaction and give the nesting depth.
113 * The outermost AtomicEnterNode will have a value of 1 and the
114 * outermost AtomicExitNode will have a value of 0. */
116 public Hashtable<FlatNode, Integer> getAtomic(LocalityBinding lb) {
117 return atomictab.get(lb);
120 /** This methods returns a hashtable for a given LocalityBinding
121 * that tells which temps needs to be saved for each
122 * AtomicEnterNode. */
124 public Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> getTemps(LocalityBinding lb) {
125 return tempstosave.get(lb);
128 public Set<TempDescriptor> getTempSet(LocalityBinding lb) {
129 HashSet<TempDescriptor> set=new HashSet<TempDescriptor>();
130 Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> table=getTemps(lb);
131 for(Iterator<FlatAtomicEnterNode> faenit=table.keySet().iterator();faenit.hasNext();) {
132 FlatAtomicEnterNode faen=faenit.next();
133 set.addAll(table.get(faen));
138 private void doAnalysis() {
139 computeLocalityBindings();
140 computeTempstoSave();
143 private void computeLocalityBindings() {
144 lbmain=new LocalityBinding(typeutil.getMain(), false);
145 lbmain.setGlobal(0, LOCAL);
146 lbtovisit.add(lbmain);
147 discovered.put(lbmain, lbmain);
148 if (!classtolb.containsKey(lbmain.getMethod().getClassDesc()))
149 classtolb.put(lbmain.getMethod().getClassDesc(), new HashSet<LocalityBinding>());
150 classtolb.get(lbmain.getMethod().getClassDesc()).add(lbmain);
152 if (!methodtolb.containsKey(lbmain.getMethod()))
153 methodtolb.put(lbmain.getMethod(), new HashSet<LocalityBinding>());
154 methodtolb.get(lbmain.getMethod()).add(lbmain);
156 while(!lbtovisit.empty()) {
157 LocalityBinding lb=(LocalityBinding) lbtovisit.pop();
158 Integer returnglobal=lb.getGlobalReturn();
159 MethodDescriptor md=lb.getMethod();
160 Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>> temptable=new Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>();
161 Hashtable<FlatNode, Integer> atomictable=new Hashtable<FlatNode, Integer>();
162 computeCallsFlags(md, lb, temptable, atomictable);
163 atomictab.put(lb, atomictable);
164 temptab.put(lb, temptable);
166 if (!md.isStatic()&&!returnglobal.equals(lb.getGlobalReturn())) {
167 //return type is more precise now
168 //rerun everything that call us
169 lbtovisit.addAll(dependence.get(lb));
175 public void computeCallsFlags(MethodDescriptor md, LocalityBinding lb, Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptable, Hashtable<FlatNode, Integer> atomictable) {
176 FlatMethod fm=state.getMethodFlat(md);
177 HashSet<FlatNode> tovisit=new HashSet<FlatNode>();
178 tovisit.add(fm.getNext(0));
180 // Build table for initial node
181 Hashtable<TempDescriptor,Integer> table=new Hashtable<TempDescriptor,Integer>();
182 temptable.put(fm, table);
183 atomictable.put(fm, lb.isAtomic()?1:0);
184 int offset=md.isStatic()?0:1;
185 if (!md.isStatic()) {
186 table.put(fm.getParameter(0), lb.getGlobalThis());
188 for(int i=offset;i<fm.numParameters();i++) {
189 TempDescriptor temp=fm.getParameter(i);
190 Integer b=lb.isGlobal(i-offset);
195 while(!tovisit.isEmpty()) {
196 FlatNode fn=tovisit.iterator().next();
198 Hashtable<TempDescriptor, Integer> currtable=new Hashtable<TempDescriptor, Integer>();
200 for(int i=0;i<fn.numPrev();i++) {
201 FlatNode prevnode=fn.getPrev(i);
202 if (atomictable.containsKey(prevnode)) {
203 atomicstate=atomictable.get(prevnode).intValue();
205 if (!temptable.containsKey(prevnode))
207 Hashtable<TempDescriptor, Integer> prevtable=temptable.get(prevnode);
208 for(Iterator<TempDescriptor> tempit=prevtable.keySet().iterator();tempit.hasNext();) {
209 TempDescriptor temp=tempit.next();
210 Integer tmpint=prevtable.get(temp);
211 Integer oldint=currtable.containsKey(temp)?currtable.get(temp):EITHER;
212 Integer newint=merge(tmpint, oldint);
213 currtable.put(temp, newint);
216 atomictable.put(fn, atomicstate);
219 case FKind.FlatAtomicEnterNode:
220 processAtomicEnterNode((FlatAtomicEnterNode)fn, atomictable);
224 case FKind.FlatAtomicExitNode:
225 processAtomicExitNode((FlatAtomicExitNode)fn, atomictable);
228 processCallNode(lb, (FlatCall)fn, currtable, isAtomic(atomictable, fn));
230 case FKind.FlatFieldNode:
231 processFieldNode(lb, (FlatFieldNode)fn, isAtomic(atomictable, fn), currtable);
233 case FKind.FlatSetFieldNode:
234 processSetFieldNode(lb, (FlatSetFieldNode)fn, isAtomic(atomictable,fn), currtable);
237 processNew(lb, (FlatNew)fn, isAtomic(atomictable, fn), currtable);
239 case FKind.FlatOpNode:
240 processOpNode((FlatOpNode)fn, currtable);
242 case FKind.FlatCastNode:
243 processCastNode((FlatCastNode)fn, currtable);
245 case FKind.FlatLiteralNode:
246 processLiteralNode((FlatLiteralNode)fn, currtable);
248 case FKind.FlatReturnNode:
249 processReturnNode(lb, (FlatReturnNode)fn, currtable);
251 case FKind.FlatSetElementNode:
252 processSetElementNode(lb, (FlatSetElementNode)fn, currtable, isAtomic(atomictable, fn));
254 case FKind.FlatElementNode:
255 processElementNode(lb, (FlatElementNode)fn, currtable, isAtomic(atomictable, fn));
257 case FKind.FlatCondBranch:
258 case FKind.FlatBackEdge:
260 //No action needed for these
262 case FKind.FlatFlagActionNode:
263 case FKind.FlatCheckNode:
264 case FKind.FlatTagDeclaration:
265 throw new Error("Incompatible with tasks!");
266 case FKind.FlatMethod:
270 Hashtable<TempDescriptor,Integer> oldtable=temptable.get(fn);
271 if (oldtable==null||!oldtable.equals(currtable)) {
272 // Update table for this node
273 temptable.put(fn, currtable);
274 for(int i=0;i<fn.numNext();i++) {
275 tovisit.add(fn.getNext(i));
281 private static boolean isAtomic(Hashtable<FlatNode, Integer> atomictable, FlatNode fn) {
282 return atomictable.get(fn).intValue()>0;
285 private static Integer merge(Integer a, Integer b) {
286 if (a==null||a.equals(EITHER))
288 if (b==null||b.equals(EITHER))
295 void processCallNode(LocalityBinding currlb, FlatCall fc, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
296 MethodDescriptor nodemd=fc.getMethod();
298 if (nodemd.isStatic()||nodemd.getReturnType()==null) {
299 methodset=new HashSet();
300 methodset.add(nodemd);
302 methodset=callgraph.getMethods(nodemd, fc.getThis().getType());
304 Integer currreturnval=EITHER; //Start off with the either value
305 for(Iterator methodit=methodset.iterator();methodit.hasNext();) {
306 MethodDescriptor md=(MethodDescriptor) methodit.next();
307 boolean isnative=md.getModifiers().isNative();
309 LocalityBinding lb=new LocalityBinding(md, isatomic);
310 if (isnative&&isatomic) {
311 System.out.println("Don't call native methods in atomic blocks!");
313 for(int i=0;i<fc.numArgs();i++) {
314 TempDescriptor arg=fc.getArg(i);
315 if(isnative&&(currtable.get(arg).equals(GLOBAL)||
316 currtable.get(arg).equals(CONFLICT)))
317 throw new Error("Potential call to native method "+md+" with global parameter:\n"+currlb.getExplanation());
318 lb.setGlobal(i,currtable.get(arg));
320 if (fc.getThis()!=null) {
321 Integer thistype=currtable.get(fc.getThis());
324 if(thistype.equals(CONFLICT))
325 throw new Error("Using type that can be either local or global in context:\n"+currlb.getExplanation());
326 if(thistype.equals(GLOBAL)&&!isatomic)
327 throw new Error("Using global object outside of transaction in context:\n"+currlb.getExplanation());
328 if (isnative&&thistype.equals(GLOBAL))
329 throw new Error("Potential call to native method "+md+" on global objects:\n"+currlb.getExplanation());
330 lb.setGlobalThis(thistype);
332 lb.setGlobalThis(EITHER);//default value
335 if (!discovered.containsKey(lb)) {
337 lb.setGlobalReturn(LOCAL);
339 lb.setGlobalReturn(EITHER);
340 lb.setParent(currlb);
342 discovered.put(lb, lb);
343 if (!classtolb.containsKey(lb.getMethod().getClassDesc()))
344 classtolb.put(lb.getMethod().getClassDesc(), new HashSet<LocalityBinding>());
345 classtolb.get(lb.getMethod().getClassDesc()).add(lb);
346 if (!methodtolb.containsKey(lb.getMethod()))
347 methodtolb.put(lb.getMethod(), new HashSet<LocalityBinding>());
348 methodtolb.get(lb.getMethod()).add(lb);
350 lb=discovered.get(lb);
351 Integer returnval=lb.getGlobalReturn();
352 currreturnval=merge(returnval, currreturnval);
353 if (!dependence.containsKey(lb))
354 dependence.put(lb, new HashSet<LocalityBinding>());
355 dependence.get(lb).add(currlb);
357 if (fc.getReturnTemp()!=null) {
358 currtable.put(fc.getReturnTemp(), currreturnval);
362 void processFieldNode(LocalityBinding lb, FlatFieldNode ffn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
363 Integer type=currtable.get(ffn.getSrc());
364 TempDescriptor dst=ffn.getDst();
365 if (type.equals(LOCAL)) {
366 if (ffn.getField().isGlobal())
367 currtable.put(dst,GLOBAL);
369 currtable.put(dst,LOCAL);
370 } else if (type.equals(GLOBAL)) {
372 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
373 if (ffn.getField().getType().isPrimitive())
374 currtable.put(dst, LOCAL); // primitives are local
376 currtable.put(dst, GLOBAL);
377 } else if (type.equals(EITHER)) {
378 if (ffn.getField().getType().isPrimitive())
379 currtable.put(dst, LOCAL); // primitives are local
380 else if (ffn.getField().isGlobal())
381 currtable.put(dst, GLOBAL);
383 currtable.put(dst, EITHER);
384 } else if (type.equals(CONFLICT)) {
385 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
389 //need to handle primitives
390 void processSetFieldNode(LocalityBinding lb, FlatSetFieldNode fsfn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
391 Integer srctype=currtable.get(fsfn.getSrc());
392 Integer dsttype=currtable.get(fsfn.getDst());
394 if (dsttype.equals(LOCAL)) {
395 if (fsfn.getField().isGlobal()) {
396 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
397 throw new Error("Writing possible local reference to global field in context: \n"+lb.getExplanation());
399 if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
400 throw new Error("Writing possible global reference to local object in context: \n"+lb.getExplanation());
402 } else if (dsttype.equals(GLOBAL)) {
404 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
405 //okay to store primitives in global object
406 if (srctype.equals(LOCAL) && fsfn.getField().getType().isPrimitive())
408 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
409 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
410 } else if (dsttype.equals(EITHER)) {
411 if (srctype.equals(CONFLICT))
412 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
413 } else if (dsttype.equals(CONFLICT)) {
414 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
418 void processNew(LocalityBinding lb, FlatNew fn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
419 if (fn.isGlobal()&&!transaction) {
420 throw new Error("Allocating global object outside of transaction in context:"+lb.getExplanation());
423 currtable.put(fn.getDst(), GLOBAL);
425 currtable.put(fn.getDst(), LOCAL);
428 void processOpNode(FlatOpNode fon, Hashtable<TempDescriptor, Integer> currtable) {
429 /* Just propagate value */
430 currtable.put(fon.getDest(), currtable.get(fon.getLeft()));
433 void processCastNode(FlatCastNode fcn, Hashtable<TempDescriptor, Integer> currtable) {
434 currtable.put(fcn.getDst(), currtable.get(fcn.getSrc()));
437 void processLiteralNode(FlatLiteralNode fln, Hashtable<TempDescriptor, Integer> currtable) {
439 if (fln.getValue()==null)
440 currtable.put(fln.getDst(), EITHER);
442 currtable.put(fln.getDst(), LOCAL);
445 void processReturnNode(LocalityBinding lb, FlatReturnNode frn, Hashtable<TempDescriptor, Integer> currtable) {
446 if(frn.getReturnTemp()!=null) {
447 Integer returntype=currtable.get(frn.getReturnTemp());
448 lb.setGlobalReturn(merge(returntype, lb.getGlobalReturn()));
452 void processSetElementNode(LocalityBinding lb, FlatSetElementNode fsen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
453 Integer srctype=currtable.get(fsen.getSrc());
454 Integer dsttype=currtable.get(fsen.getDst());
456 if (dsttype.equals(LOCAL)) {
457 if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
458 throw new Error("Writing possible global reference to local object in context:\n"+lb.getExplanation());
459 } else if (dsttype.equals(GLOBAL)) {
460 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
461 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
463 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
464 } else if (dsttype.equals(EITHER)) {
465 if (srctype.equals(CONFLICT))
466 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
467 } else if (dsttype.equals(CONFLICT)) {
468 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
472 void processElementNode(LocalityBinding lb, FlatElementNode fen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
473 Integer type=currtable.get(fen.getSrc());
474 TempDescriptor dst=fen.getDst();
475 if (type.equals(LOCAL)) {
476 currtable.put(dst,LOCAL);
477 } else if (type.equals(GLOBAL)) {
479 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
480 currtable.put(dst, GLOBAL);
481 } else if (type.equals(EITHER)) {
482 currtable.put(dst, EITHER);
483 } else if (type.equals(CONFLICT)) {
484 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
488 void processAtomicEnterNode(FlatAtomicEnterNode fen, Hashtable<FlatNode, Integer> atomictable) {
489 int atomic=atomictable.get(fen).intValue();
490 atomictable.put(fen, new Integer(atomic+1));
493 void processAtomicExitNode(FlatAtomicExitNode fen, Hashtable<FlatNode, Integer> atomictable) {
494 int atomic=atomictable.get(fen).intValue();
495 atomictable.put(fen, new Integer(atomic-1));
498 private Hashtable<FlatNode, Set<TempDescriptor>> computeLiveTemps(FlatMethod fm) {
499 Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=new Hashtable<FlatNode, Set<TempDescriptor>>();
501 Set<FlatNode> toprocess=fm.getNodeSet();
503 while(!toprocess.isEmpty()) {
504 FlatNode fn=toprocess.iterator().next();
505 toprocess.remove(fn);
507 List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
508 List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
510 HashSet<TempDescriptor> tempset=new HashSet<TempDescriptor>();
511 for(int i=0;i<fn.numNext();i++) {
512 FlatNode fnnext=fn.getNext(i);
513 if (nodetotemps.containsKey(fnnext))
514 tempset.addAll(nodetotemps.get(fnnext));
516 tempset.removeAll(writes);
517 tempset.addAll(reads);
518 if (!nodetotemps.containsKey(fn)||
519 nodetotemps.get(fn).equals(tempset)) {
520 nodetotemps.put(fn, tempset);
521 for(int i=0;i<fn.numPrev();i++)
522 toprocess.add(fn.getPrev(i));
528 private void computeTempstoSave() {
529 for(Iterator<LocalityBinding> lbit=getLocalityBindings().iterator();lbit.hasNext();) {
530 LocalityBinding lb=lbit.next();
531 computeTempstoSave(lb);
535 /* Need to checkpoint all temps that could be read from along any
536 * path that are either:
537 1) Written to by any assignment inside the transaction
538 2) Read from a global temp.
540 Generate tempstosave map from
541 localitybinding->flatatomicenternode->Set<TempDescriptors>
544 private void computeTempstoSave(LocalityBinding lb) {
547 Hashtable<FlatNode, Integer> atomictab=getAtomic(lb);
548 Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptab=getNodeTempInfo(lb);
549 MethodDescriptor md=lb.getMethod();
550 FlatMethod fm=state.getMethodFlat(md);
552 Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=computeLiveTemps(fm);
553 Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> nodetosavetemps=new Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>();
554 tempstosave.put(lb, nodetosavetemps);
556 Hashtable<FlatNode, FlatAtomicEnterNode> nodemap=new Hashtable<FlatNode, FlatAtomicEnterNode>();
558 HashSet<FlatNode> toprocess=new HashSet<FlatNode>();
559 HashSet<FlatNode> discovered=new HashSet<FlatNode>();
562 while(!toprocess.isEmpty()) {
563 FlatNode fn=toprocess.iterator().next();
564 toprocess.remove(fn);
565 boolean isatomic=atomictab.get(fn).intValue()>0;
567 atomictab.get(fn.getPrev(0)).intValue()==0) {
568 assert(fn.getPrev(0).kind()==FKind.FlatAtomicEnterNode);
569 nodemap.put(fn, (FlatAtomicEnterNode)fn);
570 nodetosavetemps.put((FlatAtomicEnterNode)fn, new HashSet<TempDescriptor>());
571 } else if (isatomic) {
572 FlatAtomicEnterNode atomicnode=nodemap.get(fn);
573 Set<TempDescriptor> livetemps=nodetotemps.get(fn);
574 List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
575 List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
577 for(Iterator<TempDescriptor> tempit=livetemps.iterator();tempit.hasNext();) {
578 TempDescriptor tmp=tempit.next();
579 if (writes.contains(tmp)) {
580 nodetosavetemps.get(atomicnode).add(tmp);
581 } else if (reads.contains(tmp)&&temptab.get(fn).get(tmp)==GLOBAL) {
582 nodetosavetemps.get(atomicnode).add(tmp);
586 for(int i=0;i<fn.numNext();i++) {
587 FlatNode fnnext=fn.getNext(i);
588 if (!discovered.contains(fnnext)) {
589 discovered.add(fnnext);
590 toprocess.add(fnnext);
592 nodemap.put(fnnext, nodemap.get(fn));