Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

ShValueTracking.cpp

00001 // Sh: A GPU metaprogramming language.
00002 //
00003 // Copyright 2003-2005 Serious Hack Inc.
00004 // 
00005 // This software is provided 'as-is', without any express or implied
00006 // warranty. In no event will the authors be held liable for any damages
00007 // arising from the use of this software.
00008 // 
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it
00011 // freely, subject to the following restrictions:
00012 // 
00013 // 1. The origin of this software must not be misrepresented; you must
00014 // not claim that you wrote the original software. If you use this
00015 // software in a product, an acknowledgment in the product documentation
00016 // would be appreciated but is not required.
00017 // 
00018 // 2. Altered source versions must be plainly marked as such, and must
00019 // not be misrepresented as being the original software.
00020 // 
00021 // 3. This notice may not be removed or altered from any source
00022 // distribution.
00024 #include <map>
00025 #include <set>
00026 #include <utility>
00027 #include <iostream>
00028 #include <sstream>
00029 #include <fstream>
00030 #include "ShOptimizations.hpp"
00031 #include "ShBitSet.hpp"
00032 #include "ShCtrlGraph.hpp"
00033 #include "ShDebug.hpp"
00034 #include "ShEvaluate.hpp"
00035 #include "ShContext.hpp"
00036 #include "ShSyntax.hpp"
00037 #include "ShProgramNode.hpp"
00038 
00039 // Uncomment to enable use-def chain debugging 
00040 // #define SH_DEBUG_VALUETRACK
00041 
00042 #ifdef SH_DEBUG_OPTIMIZER
00043 #ifndef SH_DEBUG_VALUETRACK
00044 #define SH_DEBUG_VALUETRACK
00045 #endif
00046 #endif
00047 namespace {
00048 using namespace SH;
00049 
00050 
00051 // Data related to doing reaching definitions
00052 struct ReachingDefs {
00053   ReachingDefs()
00054     : defsize(0)
00055   {
00056   }
00057   
00058   struct Definition {
00059     Definition(ShStatement* stmt,
00060                const ShCtrlGraphNodePtr& node,
00061                int offset,
00062                ShBitSet disable_mask)
00063       : varnode(stmt->dest.node()), stmt(stmt), node(node), offset(offset),
00064         disable_mask(disable_mask)
00065     {}
00066 
00067     Definition(ShVariableNodePtr varnode,
00068                const ShCtrlGraphNodePtr& node,
00069                int offset,
00070                ShBitSet disable_mask)
00071       : varnode(varnode), stmt(0), node(node), offset(offset), 
00072         disable_mask(disable_mask)
00073     {}
00074 
00075     bool isInput() const
00076     { 
00077       return !stmt; 
00078     }
00079 
00080     int size() const
00081     {
00082       if(isInput()) return varnode->size();
00083       else return stmt->dest.size();
00084     }
00085     
00086     /* returns whether the i'th element is disabled */
00087     int isDisabled(int i) const
00088     {
00089       return disable_mask[index(i)];
00090     }
00091     
00092     /* returns the offset of the i'th tuple element */
00093     int off(int i) const
00094     {
00095       return offset + i;
00096     }
00097 
00098     /* returns unswizzled index for i'th tuple element */
00099     int index(int i) const
00100     {
00101       if(isInput()) return i;
00102       return stmt->dest.swizzle()[i];
00103     }
00104 
00105     /* returns Def for i'th tuple element */
00106     ValueTracking::Def toDef(int i) const
00107     {
00108       if(isInput()) return ValueTracking::Def(varnode, i); 
00109       return ValueTracking::Def(stmt, i);
00110     }
00111 
00112     friend std::ostream& operator<<(std::ostream& out, const Definition& def)
00113     {
00114       out << "{";
00115       out << " off " << def.offset;
00116       out << ", node " << def.node.object();
00117       out << ", dsbl " << def.disable_mask << ", ";
00118       if(def.isInput()) out << "input " << def.varnode->name();
00119       else out << "stmt " << *def.stmt;
00120       out << "}";
00121       return out;
00122     }
00123     
00124     ShVariableNodePtr varnode;
00125     ShStatement* stmt;
00126     ShCtrlGraphNodePtr node;
00127     int offset;
00128     
00129     // Sometimes we want to ignore some of the elements, when
00130     // constructing gen, because they are overwritten by later
00131     // statements. In that case we mark the ignored components here.
00132     // unswizzled disable mask
00133     ShBitSet disable_mask;
00134   };
00135 
00136   void addDefinition(const Definition& d)
00137   {
00138     // Otherwise, add this definition
00139     defs.push_back(d);
00140     defsize += d.size(); 
00141   }
00142 
00143   typedef std::map<ShCtrlGraphNodePtr, int> SizeMap;
00144   typedef std::map<ShCtrlGraphNodePtr, ShBitSet> ReachingMap;
00145   
00146   std::vector<Definition> defs;
00147   int defsize; 
00148 
00149   ReachingMap gen, prsv;
00150   ReachingMap rchin;
00151 };
00152 
00153 struct DefFinder {
00154   DefFinder(ReachingDefs& r, ShCtrlGraphNodePtr entry, const ShProgramNode::VarList& inputs)
00155     : entry(entry), inputs(inputs), r(r), offset(0)
00156   {
00157   }
00158 
00159   void operator()(ShCtrlGraphNodePtr node)
00160   {
00161     if (!node) return;
00162     ShBasicBlockPtr block = node->block;
00163 
00164     // Use this data structure to mark assignments to node elements,
00165     // so that we can ignore earlier assignments to them in this block.
00166     std::map<ShVariableNodePtr, ShBitSet> disable_map;
00167 
00168     if(block) {
00169       // Iterate backwards over the list of statements...
00170       ShBasicBlock::ShStmtList::iterator I = block->end();
00171       while (1) {
00172         if (I == block->begin()) break;
00173         --I;
00174         if (!I->dest.null() && I->op != SH_OP_KIL && I->op != SH_OP_OPTBRA /*&& I->dest.node()->kind() == SH_TEMP*/) {
00175           // Construct a disable map if this node has not yet been
00176           // assigned to in a later statement in this block.
00177           if (disable_map.find(I->dest.node()) == disable_map.end()) {
00178             disable_map[I->dest.node()] = ShBitSet(I->dest.node()->size());
00179           }
00180 
00181           // Construct a bitset containing all elements which this
00182           // definition affects.
00183           ShBitSet defn_map(I->dest.node()->size());
00184           for (int i = 0; i < I->dest.size(); i++) defn_map[I->dest.swizzle()[i]] = true;
00185 
00186           // Skip this definition if all its components have already
00187           // been defined in a later statement.
00188           if ((defn_map & disable_map[I->dest.node()]) == defn_map) continue;
00189 
00190           // Otherwise, add this definition
00191           r.addDefinition(ReachingDefs::Definition(&(*I), node, r.defsize,
00192                                                     disable_map[I->dest.node()]));
00193 
00194           // And update the disable map for the destination node.
00195           disable_map[I->dest.node()] |= defn_map;
00196         }
00197       }
00198     }
00199 
00200     // Add in defs for the inputs
00201     if(node != entry) return; 
00202     for(ShProgramNode::VarList::const_iterator I = inputs.begin(); 
00203         I != inputs.end(); ++I) {
00204       if(disable_map.find(*I) == disable_map.end()) {
00205         disable_map[*I] = ShBitSet((*I)->size());
00206       }
00207 
00208       // check whether input does not reach end of block
00209       if(disable_map[*I].full()) continue;
00210 
00211       r.addDefinition(ReachingDefs::Definition(*I, node, r.defsize,
00212             disable_map[*I]));
00213     }
00214   }
00215 
00216   ShCtrlGraphNodePtr entry; // @todo range - this is a touch clumsy
00217   const ShProgramNode::VarList& inputs;
00218   ReachingDefs& r;
00219   int offset;
00220 };
00221 
00222 struct InitRch {
00223   InitRch(ReachingDefs& r)
00224     : r(r)
00225   {
00226   }
00227 
00228   void operator()(ShCtrlGraphNodePtr node)
00229   {
00230     if (!node) return;
00231 
00232     r.rchin[node] = ShBitSet(r.defsize);
00233     r.gen[node] = ShBitSet(r.defsize);
00234     r.prsv[node] = ~ShBitSet(r.defsize);
00235       
00236     ShBasicBlockPtr block = node->block;
00237 
00238     // Initialize gen
00239     for (unsigned int i = 0; i < r.defs.size(); i++) {
00240       ReachingDefs::Definition &d = r.defs[i];
00241       if (d.node == node) {
00242         for (int j = 0; j < d.size(); j++) {
00243 // @todo range - I think this needs to be fixed for swizzling?
00244 //               it's been fixed above in isDisabled
00245          // if (!r.defs[i].disable_mask[j]) {
00246           if (!d.isDisabled(j)) {
00247             r.gen[node][d.off(j)] = true;
00248           }
00249         }
00250       }
00251     }
00252 
00253     if(!block) return;
00254 
00255     // Initialize prsv
00256     for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00257       if (I->dest.null() 
00258           || I->op == SH_OP_KIL
00259           || I->op == SH_OP_OPTBRA
00260           /*|| I->dest.node()->kind() != SH_TEMP*/) continue;
00261       for (unsigned int i = 0; i < r.defs.size(); i++) {
00262         ReachingDefs::Definition &d = r.defs[i];
00263         if (d.varnode != I->dest.node()) continue;
00264 
00265         for (int j = 0; j < I->dest.size(); ++j) {
00266           for (int k = 0; k < d.size(); ++k) {
00267             if (d.index(k) == I->dest.swizzle()[j]) {
00268               r.prsv[node][d.off(k)] = false;
00269             }
00270           }
00271         }
00272       }
00273     }
00274   }
00275 
00276   ReachingDefs& r;
00277 };
00278 
00279 struct IterateRch {
00280   IterateRch(ReachingDefs& r, bool& changed)
00281     : r(r), changed(changed)
00282   {
00283   }
00284 
00285   void operator()(const ShCtrlGraphNodePtr& node)
00286   {
00287     if (!node) return;
00288     SH_DEBUG_ASSERT(r.rchin.find(node) != r.rchin.end());
00289     ShBitSet newRchIn(r.defsize);
00290     
00291     for (ShCtrlGraphNode::ShPredList::iterator I = node->predecessors.begin();
00292          I != node->predecessors.end(); ++I) {
00293       SH_DEBUG_ASSERT(r.gen.find(*I) != r.gen.end());
00294       SH_DEBUG_ASSERT(r.prsv.find(*I) != r.prsv.end());
00295       SH_DEBUG_ASSERT(r.rchin.find(*I) != r.rchin.end());
00296       
00297       newRchIn |= (r.gen[*I] | (r.rchin[*I] & r.prsv[*I]));
00298     }
00299     if (newRchIn != r.rchin[node]) {
00300       r.rchin[node] = newRchIn;
00301       changed = true;
00302     }
00303   }
00304 
00305   ReachingDefs& r;
00306   bool& changed;
00307 };
00308 
00309 // Builds ud and du chains per statement and
00310 // also gathers input du and output ud chains into the program
00311 struct UdDuBuilder {
00312   UdDuBuilder(ReachingDefs& r, ShProgramNodePtr p) 
00313     : r(r), m_exit(p->ctrlGraph->exit()), p(p),
00314       intrack(new InputValueTracking()),
00315       outtrack(new OutputValueTracking())
00316   {
00317     p->destroy_info<InputValueTracking>();
00318     p->destroy_info<OutputValueTracking>();
00319     p->add_info(intrack);
00320     p->add_info(outtrack);
00321   }
00322 
00323   struct TupleElement {
00324     TupleElement(const ShVariableNodePtr& node, int index)
00325       : node(node), index(index)
00326     {
00327     }
00328 
00329     bool operator<(const TupleElement& other) const
00330     {
00331       if (node < other.node) return true;
00332       if (node == other.node) return index < other.index;
00333       return false;
00334     }
00335     
00336     ShVariableNodePtr node;
00337     int index;
00338   };
00339   
00340   void operator()(ShCtrlGraphNodePtr node) {
00341     typedef std::set<ValueTracking::Def> DefSet;
00342     typedef std::map<TupleElement, DefSet> DefMap;
00343 
00344     // defs contains all of the possible contributors to the key's
00345     // definition at the current point.
00346     DefMap defs;
00347     
00348     if (!node) return;
00349     ShBasicBlockPtr block = node->block;
00350 
00351     // TODO: Handle "non assigning" statements like KIL
00352 
00353     // initialize defs at the start of the block, using the reaching
00354     // definitions solution.
00355     for (std::size_t i = 0; i < r.defs.size(); i++) {
00356       for (int j = 0; j < r.defs[i].size(); j++) {
00357         if (r.rchin[node][r.defs[i].offset + j]) {
00358           ValueTracking::Def def(r.defs[i].toDef(j));
00359           defs[TupleElement(r.defs[i].varnode,
00360                             r.defs[i].index(j))].insert(def);
00361         }
00362       }
00363     }
00364 
00365     if(block) {
00366       // Now consider each statement in turn.
00367       for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00368         // Compute the ud chains for the statement's source variables,
00369         // and contribute to the du chains of the source variables' definitions.
00370         for (int j = 0; j < opInfo[I->op].arity; j++) {
00371           //if (I->src[j].node()->kind() == SH_TEMP) {
00372             ValueTracking* vt = I->get_info<ValueTracking>();
00373             if (!vt) {
00374               vt = new ValueTracking(&(*I));
00375               I->add_info(vt);
00376             }
00377             ShVariableNodePtr srcNode = I->src[j].node();
00378             for (int i = 0; i < I->src[j].size(); i++) {
00379               const DefSet& ds = defs[TupleElement(srcNode, I->src[j].swizzle()[i])];
00380 
00381               vt->defs[j][i] = ds;
00382               ValueTracking::Use srcUse(&(*I), j, i);
00383               for (DefSet::const_iterator J = ds.begin(); J != ds.end(); J++) {
00384                 switch(J->kind) {
00385                   case ValueTracking::Def::INPUT:
00386                     {
00387                       ValueTracking::TupleDefUseChain& inputDu = 
00388                         intrack->inputUses[srcNode];
00389                       if(inputDu.empty()) {
00390                         inputDu.resize(srcNode->size());
00391                       }
00392                       inputDu[J->index].insert(srcUse);
00393                       break;
00394                     }
00395                   case ValueTracking::Def::STMT:
00396                     {
00397                       ValueTracking* ut = J->stmt->get_info<ValueTracking>();
00398                       if (!ut) {
00399                         ut = new ValueTracking(J->stmt);
00400                         J->stmt->add_info(ut);
00401                       }
00402                       ut->uses[J->index].insert(srcUse);
00403                       break;
00404                     }
00405                 }
00406               }
00407             //} 
00408           }
00409         }
00410         // Now update the defs structure.
00411         for (int i = 0; i < I->dest.size(); ++i) {
00412           defs[TupleElement(I->dest.node(), I->dest.swizzle()[i])].clear();
00413           ValueTracking::Def def(&(*I), i);
00414           defs[TupleElement(I->dest.node(), I->dest.swizzle()[i])].insert(def);
00415         }
00416       }
00417     }
00418 
00419     // now find any output defs and add appropriate du chain to stmts 
00420     // @todo range - note that this still doesn't show inputs that are
00421     // immediately passed through to outputs, but that should be easy 
00422     // to store somewhere if needed
00423     if(node == m_exit) {
00424       for(DefMap::iterator D = defs.begin(); D != defs.end(); ++D) {
00425         ShVariableNodePtr node = D->first.node;
00426         ShBindingType kind = node->kind();
00427         int index = D->first.index;
00428 
00429         if(kind == SH_INOUT || kind == SH_OUTPUT) {
00430           DefSet& ds = D->second;
00431 
00432           ValueTracking::TupleUseDefChain& outDef = outtrack->outputDefs[node];
00433           if(outDef.empty()) {
00434             outDef.resize(node->size());
00435           }
00436           outDef[index] = ds;
00437 
00438           for(DefSet::iterator S = ds.begin(); S != ds.end(); ++S) {
00439             if(S->kind != ValueTracking::Def::STMT) continue; 
00440             ValueTracking* vt = S->stmt->get_info<ValueTracking>();
00441             if (!vt) {
00442               vt = new ValueTracking(S->stmt);
00443               S->stmt->add_info(vt);
00444             }
00445             SH_DEBUG_ASSERT(vt); // must have added it above somewhere
00446             ValueTracking::Use use(node, S->stmt->dest.swizzle()[S->index]);
00447             vt->uses[S->index].insert(use);
00448           }
00449         }
00450       }
00451     }
00452   }
00453 
00454   ReachingDefs& r;
00455   ShCtrlGraphNodePtr m_exit;
00456   ShProgramNodePtr p;
00457   InputValueTracking* intrack;
00458   OutputValueTracking* outtrack;
00459 };
00460 
00461 struct UdDuClearer {
00462   void operator()(ShCtrlGraphNodePtr node) {
00463     if (!node) return;
00464     ShBasicBlockPtr block = node->block;
00465     if (!block) return;
00466     
00467     for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00468       I->destroy_info<ValueTracking>();
00469     }
00470   }
00471 };
00472 
00473 struct UdDuDumper {
00474   void operator()(ShCtrlGraphNodePtr node) {
00475     if (!node) return;
00476     ShBasicBlockPtr block = node->block;
00477     if (!block) return;
00478 
00479     for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00480       ValueTracking* vt = I->get_info<ValueTracking>();
00481       if (!vt) {
00482         SH_DEBUG_PRINT(*I << " HAS NO VALUE TRACKING");
00483         continue;
00484       }
00485       SH_DEBUG_PRINT("Valuetracking for " << *I);
00486       for (int i = 0; i < opInfo[I->op].arity; i++) {
00487         SH_DEBUG_PRINT("  src ud" << i << "\n" << vt->defs[i]);
00488       }
00489       SH_DEBUG_PRINT("  dest du" << vt->uses);
00490     }
00491   }
00492 
00493   void operator()(ShProgramNodePtr p) {
00494     InputValueTracking* ivt = p->get_info<InputValueTracking>();
00495     SH_DEBUG_ASSERT(ivt);
00496     SH_DEBUG_PRINT("Input Valuetracking:\n" << *ivt);
00497 
00498     OutputValueTracking* ovt = p->get_info<OutputValueTracking>();
00499     SH_DEBUG_ASSERT(ovt);
00500     SH_DEBUG_PRINT("Output Valuetracking:\n" << *ovt);
00501   }
00502 };
00503 
00504 }
00505 
00506 namespace SH {
00507 
00508 ValueTracking::ValueTracking(ShStatement* stmt)
00509   : uses(stmt->dest.node() ? stmt->dest.size() : 0),
00510     defs(stmt->src.size())
00511 {
00512 #ifdef SH_DEBUG_VALUETRACK
00513   SH_DEBUG_PRINT("Adding value tracking to " << *stmt);
00514 #endif
00515   for (int i = 0; i < opInfo[stmt->op].arity; i++) {
00516     for (int j = 0; j < (stmt->src[i].node() ? stmt->src[i].size() : 0); j++) {
00517       defs[i].push_back(std::set<Def>());
00518     }
00519   }
00520 }
00521 
00522 ShInfo* ValueTracking::clone() const
00523 {
00524   return new ValueTracking(*this);
00525 }
00526 
00527 std::ostream& operator<<(std::ostream& out, const ValueTracking::Use& use) {
00528   if(use.kind == ValueTracking::Use::STMT) {
00529     out << "(" << *use.stmt << ").src" << use.source << "[" << use.index << "]";
00530   } else {
00531     out << "(OUTPUT " << use.node->name() << ")[" << use.index << "]"; 
00532   }
00533   return out;
00534 }
00535 
00536 std::ostream& operator<<(std::ostream& out, const ValueTracking::TupleUseDefChain& tud)
00537 {
00538   int e = 0;
00539   for (ValueTracking::TupleUseDefChain::const_iterator E = tud.begin();
00540        E != tud.end(); ++E, ++e) {
00541     out << "[" << e << "] <- {";
00542     for (ValueTracking::UseDefChain::const_iterator J = E->begin(); J != E->end(); ++J) {
00543       if(J != E->begin()) out << ", ";
00544       out << *J;
00545     }
00546     out << "}\n";
00547   }
00548   return out;
00549 }
00550 
00551 std::ostream& operator<<(std::ostream& out, const ValueTracking::Def& def) {
00552   if(def.kind == ValueTracking::Def::STMT) {
00553     out << "(" << *def.stmt << ").dst" << "[" << def.index << "]";
00554   } else {
00555     out << "(INPUT " << def.node->name() << ")[" << def.index << "]"; 
00556   }
00557   return out;
00558 }
00559 
00560 std::ostream& operator<<(std::ostream& out, const ValueTracking::TupleDefUseChain& tdu)
00561 {
00562   int e = 0;
00563   for (ValueTracking::TupleDefUseChain::const_iterator E = tdu.begin();
00564        E != tdu.end(); ++E, ++e) {
00565     out << "[" << e << "] -> {";
00566     for (ValueTracking::DefUseChain::const_iterator J = E->begin(); J != E->end(); ++J) {
00567       if(J != E->begin()) out << ", ";
00568       out << *J;
00569     }
00570     out << "}\n";
00571   }
00572   return out;
00573 }
00574 
00575 ShInfo* InputValueTracking::clone() const
00576 {
00577   return new InputValueTracking(*this);
00578 }
00579 
00580 
00581 std::ostream& operator<<(std::ostream& out, const InputValueTracking& ivt)
00582 {
00583   InputValueTracking::InputTupleDefUseChain::const_iterator I;
00584   for(I = ivt.inputUses.begin(); I != ivt.inputUses.end(); ++I) {
00585     ShVariableNodePtr node = I->first;
00586     const ValueTracking::TupleDefUseChain& tdu = I->second;
00587     out << node->name();
00588     out << tdu; 
00589   }
00590   return out;
00591 }
00592 
00593 
00594 ShInfo* OutputValueTracking::clone() const
00595 {
00596   return new OutputValueTracking(*this);
00597 }
00598 
00599 std::ostream& operator<<(std::ostream& out, const OutputValueTracking& ovt)
00600 {
00601   OutputValueTracking::OutputTupleUseDefChain::const_iterator O;
00602   for(O = ovt.outputDefs.begin(); O != ovt.outputDefs.end(); ++O) {
00603     ShVariableNodePtr node = O->first;
00604     const ValueTracking::TupleUseDefChain& tud = O->second;
00605     out << node->name() << tud; 
00606   }
00607   return out;
00608 }
00609 
00610 void add_value_tracking(ShProgram& p)
00611 {
00612   ReachingDefs r;
00613 
00614   ShCtrlGraphPtr graph = p.node()->ctrlGraph;
00615   
00616   DefFinder finder(r, graph->entry(), p.node()->inputs);
00617   graph->dfs(finder);
00618 
00619   InitRch init(r);
00620   graph->dfs(init);
00621 
00622   bool changed;
00623   IterateRch iter(r, changed);
00624   do {
00625     changed = false;
00626     graph->dfs(iter);
00627   } while (changed);
00628 
00629 #ifdef SH_DEBUG_VALUETRACK
00630   SH_DEBUG_PRINT("Dumping Reaching Defs");
00631   SH_DEBUG_PRINT("defsize = " << r.defsize);
00632   SH_DEBUG_PRINT("defs.size() = " << r.defs.size());
00633   for(unsigned int i = 0; i < r.defs.size(); ++i) {
00634     SH_DEBUG_PRINT("  " << i << ": " << r.defs[i]);
00635   }
00636   std::cerr << std::endl;
00637 
00638   for (ReachingDefs::ReachingMap::const_iterator I = r.rchin.begin(); I != r.rchin.end(); ++I) {
00639     ShCtrlGraphNodePtr node = I->first;
00640     SH_DEBUG_PRINT(" rchin[" << node.object() << "]: " << I->second);
00641     SH_DEBUG_PRINT("   gen[" << node.object() << "]: " << r.gen[I->first]);
00642     SH_DEBUG_PRINT("  prsv[" << node.object() << "]: " << r.prsv[I->first]);
00643     std::cerr << std::endl;
00644   }
00645 #endif
00646 
00647   UdDuClearer clearer;
00648   graph->dfs(clearer);
00649   
00650   UdDuBuilder builder(r, p.node());
00651   graph->dfs(builder);
00652 
00653 #ifdef SH_DEBUG_VALUETRACK
00654   SH_DEBUG_PRINT("Uddu Dump");
00655   UdDuDumper dumper;
00656   graph->dfs(dumper);
00657   dumper(p.node());
00658 #endif
00659 }
00660 
00661 
00662 
00663 }

Generated on Wed Jun 15 18:12:42 2005 for Sh by  doxygen 1.4.3-20050530