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

ShIntervalConverter.cpp

00001 // Sh: A GPU metaprogramming language.
00002 //
00003 // Copyright (c) 2003 University of Waterloo Computer Graphics Laboratory
00004 // Project administrator: Michael D. McCool
00005 // Authors: Zheng Qin, Stefanus Du Toit, Kevin Moule, Tiberiu S. Popa,
00006 //          Michael D. McCool
00007 // 
00008 // This software is provided 'as-is', without any express or implied
00009 // warranty. In no event will the authors be held liable for any damages
00010 // arising from the use of this software.
00011 // 
00012 // Permission is granted to anyone to use this software for any purpose,
00013 // including commercial applications, and to alter it and redistribute it
00014 // freely, subject to the following restrictions:
00015 // 
00016 // 1. The origin of this software must not be misrepresented; you must
00017 // not claim that you wrote the original software. If you use this
00018 // software in a product, an acknowledgment in the product documentation
00019 // would be appreciated but is not required.
00020 // 
00021 // 2. Altered source versions must be plainly marked as such, and must
00022 // not be misrepresented as being the original software.
00023 // 
00024 // 3. This notice may not be removed or altered from any source
00025 // distribution.
00027 #include <algorithm>
00028 #include <map>
00029 #include <list>
00030 #include "ShError.hpp"
00031 #include "ShDebug.hpp"
00032 #include "ShTypeInfo.hpp"
00033 #include "ShVariable.hpp"
00034 #include "ShVariableNode.hpp"
00035 #include "ShInternals.hpp"
00036 #include "ShSyntax.hpp"
00037 #include "ShProgram.hpp"
00038 #include "ShInstructions.hpp"
00039 #include "ShTransformer.hpp"
00040 
00041 namespace {
00042 
00043 using namespace SH;
00044 
00045 // A set of programs that does interval arithmetic operations, except 
00046 // on 2N-tuples of type T instead of a pair of lo/hi N-tuples
00047 ShProgram intervalADD(int N, ShValueType valueType) 
00048 {
00049   ShProgram result = SH_BEGIN_PROGRAM() {
00050     ShVariable a_lo(new ShVariableNode(SH_INPUT, N, valueType));
00051     ShVariable a_hi(new ShVariableNode(SH_INPUT, N, valueType));
00052     ShVariable b_lo(new ShVariableNode(SH_INPUT, N, valueType));
00053     ShVariable b_hi(new ShVariableNode(SH_INPUT, N, valueType));
00054 
00055     ShVariable r_lo(new ShVariableNode(SH_OUTPUT, N, valueType));
00056     ShVariable r_hi(new ShVariableNode(SH_OUTPUT, N, valueType));
00057 
00058     shADD(r_lo, a_lo, b_lo);
00059     shADD(r_hi, a_hi, b_hi);
00060   } SH_END;
00061   return result;
00062 }
00063 
00064 ShProgram intervalMUL(int N, ShValueType valueType) 
00065 {
00066   ShProgram result = SH_BEGIN_PROGRAM() {
00067     ShVariable a_lo(new ShVariableNode(SH_INPUT, N, valueType));
00068     ShVariable a_hi(new ShVariableNode(SH_INPUT, N, valueType));
00069     ShVariable b_lo(new ShVariableNode(SH_INPUT, N, valueType));
00070     ShVariable b_hi(new ShVariableNode(SH_INPUT, N, valueType));
00071 
00072     ShVariable r_lo(new ShVariableNode(SH_OUTPUT, N, valueType));
00073     ShVariable r_hi(new ShVariableNode(SH_OUTPUT, N, valueType));
00074 
00075     ShVariable ll(new ShVariableNode(SH_TEMP, N, valueType));
00076     ShVariable lh(new ShVariableNode(SH_TEMP, N, valueType));
00077     ShVariable hl(new ShVariableNode(SH_TEMP, N, valueType));
00078     ShVariable hh(new ShVariableNode(SH_TEMP, N, valueType));
00079     ShVariable temp(new ShVariableNode(SH_TEMP, N, valueType));
00080 
00081     shMUL(ll, a_lo, b_lo);
00082     shMUL(lh, a_lo, b_hi);
00083     shMUL(hl, a_hi, b_lo);
00084     shMUL(hh, a_hi, b_hi);
00085 
00086     shMIN(temp, ll, lh);
00087     shMIN(r_lo, hl, hh);
00088     shMIN(r_lo, r_lo, temp);
00089 
00090     shMAX(temp, ll, lh);
00091     shMAX(r_hi, hl, hh);
00092     shMAX(r_hi, r_hi, temp);
00093   } SH_END;
00094   return result;
00095 }
00096 
00097 ShProgram getProgram(ShOperation op, int N, ShValueType valueType) {
00098   switch(op) {
00099     case SH_OP_ADD:   return intervalADD(N, valueType);
00100     case SH_OP_MUL:   return intervalMUL(N, valueType);
00101     default:
00102       shError(ShTransformerException(
00103             "Cannot translate interval arithmetic"));
00104   }
00105   return ShProgram();
00106 }
00107 
00108 typedef std::map<int, int> ValueTypeMap; 
00109 
00110 // Converts operations on ShInterval<T> to operations on T 
00111 // This takes the same form as the variable splitters, but does more complicated
00112 // code splices.
00113 //
00114 // So the first step is to assign new lo/hi tuples corresponding to each
00115 // interval tuple.
00116 //
00117 // Then interval operations are converted to use lo/hi tuples by splicing in
00118 // code fragments using ShProgram objects in ShSplitIntervalProgram 
00119 struct IntervalSplitter {
00120 
00121   IntervalSplitter(ShTransformer::VarSplitMap& splits, bool &changed)
00122     : splits(splits), changed(changed) {
00123   }
00124 
00125   void operator()(ShCtrlGraphNodePtr node) {
00126     if (!node) return;
00127     ShBasicBlockPtr block = node->block;
00128     if (!block) return;
00129     for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00130       splitVars(*I);
00131     }
00132   }
00133 
00134   // this must be called BEFORE running a DFS on the program
00135   // to split temporaries (otherwise the stupid hack marked (#) does not work)
00136   void splitVarList(ShProgramNode::VarList &vars) {
00137     for(ShProgramNode::VarList::iterator I = vars.begin();
00138         I != vars.end();) {
00139       if(split(*I)) {
00140         // (#) erase the stuff that split added to the end of the var list
00141         // @todo type check if this is actually no longer needed. 
00142         //vars.resize(vars.size() - splits[*I].size());
00143 
00144         vars.insert(I, splits[*I].begin(), splits[*I].end());
00145         I = vars.erase(I); 
00146       } else ++I;
00147     }
00148   }
00149 
00150   ShVariableNodePtr cloneNode(ShVariableNodePtr node, ShValueType rangeValueType) {
00151     return node->clone(SH_BINDINGTYPE_END, 0, rangeValueType, SH_SEMANTICTYPE_END, false); 
00152   }
00153   
00154   void splitVars(ShStatement& stmt) {
00155     stmt.marked = false;
00156     if(stmt.dest.node()) stmt.marked = split(stmt.dest.node()) || stmt.marked;
00157     for(int i = 0; i < 3; ++i) if(stmt.src[i].node()) stmt.marked = split(stmt.src[i].node()) || stmt.marked; 
00158   }
00159 
00160   // returns true if variable split
00161   // does not add variable to Program's VarList, so this must be handled manually 
00162   // (since this matters only for IN/OUT/INOUT types, splitVarList handles the
00163   // insertions nicely)
00164   bool split(ShVariableNodePtr node)
00165   {
00166     ShValueType valueType = node->valueType(); 
00167 
00168     if(!shIsInterval(valueType)) return false; 
00169     else if(splits.count(node) > 0) return true;
00170 
00171     if( node->kind() == SH_TEXTURE || node->kind() == SH_STREAM ) {
00172       shError( ShTransformerException(
00173             "Interval Arithmetic support is not implemented for textures or streams"));
00174     }
00175     changed = true;
00176 
00177     ShTransformer::VarNodeVec &splitNodes = splits[node];
00178     ShValueType newValueType = shRegularValueType(valueType);
00179 
00180     splitNodes.push_back(cloneNode(node, newValueType));  // lo
00181     splitNodes.push_back(cloneNode(node, newValueType));  // hi
00182     // @todo type handle constants/uniforms
00183 
00184     return true; 
00185   }
00186 
00187   ShTransformer::VarSplitMap &splits;
00188   bool& changed;
00189 };
00190 
00191 // @todo type handle negations - remove all negations from interval ShVariables 
00192 // since negating the lo/hi doesn't give a valid interval any more 
00193 
00197 struct IntervalStatementFixer {
00198   typedef std::vector<ShVariable> VarVec;
00199 
00200   IntervalStatementFixer(ShTransformer::VarSplitMap &splits)
00201     : splits(splits), dirty(false) {}
00202 
00203   void operator()(ShCtrlGraphNodePtr node) {
00204     if (dirty) return;
00205     if (!node) return;
00206     ShBasicBlockPtr block = node->block;
00207     if (!block) return;
00208 
00209     for (ShBasicBlock::ShStmtList::iterator I = block->begin(); I != block->end(); ++I) {
00210       if(I->marked) splitStatement(I, node);
00211     }
00212   }
00213 
00214 
00215   void splitStatement(ShBasicBlock::ShStmtList::iterator stmtI, ShCtrlGraphNodePtr node)
00216   {
00217     int i;
00218     ShStatement &stmt = *stmtI;
00219 
00220     // find out the op size
00221     int opSize = 0; 
00222     for(i = 0; stmt.src[i].node(); ++i) opSize = std::max(opSize, stmt.src[i].size());
00223 
00224     switch(stmt.op) {
00225       // for the simple cases, replace with an appropriate assignment statement
00226       case SH_OP_LO: // replace interval src with the lo tuple as a src  
00227         stmt.src[0] = ShVariable(splits[stmt.src[0].node()][0], stmt.src[0].swizzle(), stmt.src[0].neg());
00228         stmt.op = SH_OP_ASN;
00229         break;
00230 
00231       case SH_OP_HI:
00232         stmt.src[0] = ShVariable(splits[stmt.src[0].node()][1], stmt.src[0].swizzle(), stmt.src[0].neg());
00233         stmt.op = SH_OP_ASN;
00234         break;
00235 
00236       case SH_OP_SETLO:
00237         stmt.dest = ShVariable(splits[stmt.dest.node()][0], stmt.dest.swizzle(), stmt.dest.neg());
00238         stmt.op = SH_OP_ASN;
00239         break;
00240 
00241       case SH_OP_SETHI:
00242         stmt.dest = ShVariable(splits[stmt.dest.node()][1], stmt.dest.swizzle(), stmt.dest.neg());
00243         stmt.op = SH_OP_ASN;
00244         break;
00245 
00246       default:
00247         // otherwise, we need to do some graph mangling
00248         dirty = true;
00249 
00250         // split up the control graph node at the current statement
00251         ShCtrlGraphNodePtr after = node->split(stmtI);
00252 
00253         // since we used list splices, this should not invalidate the iterator
00254         // stmtI or variable stmt.
00255 
00256         // @todo type handle statements that have some non-interval src/dest
00257         // for now, assume all interval, and all the same interval type
00258         ShValueType iaType = stmt.dest.valueType();
00259         ShValueType newType = shRegularValueType(iaType);
00260 
00261         ShProgram newProgram = getProgram(stmt.op, opSize, newType);
00262         
00263         // @todo type should use algebra ops to do below in fewer lines 
00264         
00265         // run a variable replacement to turn newProgram's inputs/outputs into
00266         // temps, and add them to lists for argument/result passing from the
00267         // original statement
00268         ShVarMap varMap;
00269         ShProgramNode::VarList destNodes; 
00270         ShProgramNode::VarList srcNodes; 
00271         ShProgramNodePtr pnode = newProgram.node();
00272         ShVariableNodePtr temp;
00273         ShProgramNode::VarList::const_iterator I;
00274         ShProgramNode::VarList::const_iterator O;
00275 
00276         for(I = pnode->inputs.begin(); I != pnode->inputs.end(); ++I) {
00277           varMap[*I] = temp = (*I)->clone(SH_TEMP); 
00278           srcNodes.push_back(temp);
00279         }
00280 
00281         for(O = pnode->outputs.begin(); O != pnode->outputs.end(); ++O) {
00282           varMap[*O] = temp = (*O)->clone(SH_TEMP); 
00283           destNodes.push_back(temp);
00284         }
00285         
00286         ShVariableReplacer varFix(varMap);
00287         newProgram.node()->ctrlGraph->dfs(varFix);
00288 
00289         // now do the appropriate assignments to send split up src vars to the appropriate
00290         // temps and results to the appropriate split up dest vars 
00291         // (this needs to be done incase of swizzling/write masking on the
00292         // src/dest.  we cannot just varmap those into the newProgram's in/out)
00293         ShBasicBlockPtr beforeBlock = node->block;
00294         I = srcNodes.begin();
00295         for(i = 0; stmt.src[i].node() && I != srcNodes.end(); ++i) {
00296           // assign lo and hi
00297           beforeBlock->addStatement(ShStatement(ShVariable(*I), SH_OP_ASN, 
00298                 ShVariable(splits[stmt.src[i].node()][0],stmt.src[i].swizzle(), stmt.src[i].neg())));
00299           ++I;
00300           SH_DEBUG_ASSERT(I != srcNodes.end());
00301           beforeBlock->addStatement(ShStatement(ShVariable(*I), SH_OP_ASN,
00302                 ShVariable(splits[stmt.src[i].node()][1],stmt.src[i].swizzle(), stmt.src[i].neg())));
00303           ++I;
00304         }
00305 
00306         ShBasicBlockPtr afterBlock = after->block;
00307         I = srcNodes.begin();
00308           // assign lo and hi
00309         afterBlock->addStatement(ShStatement(ShVariable(splits[stmt.dest.node()][0],stmt.dest.swizzle(), stmt.dest.neg()),
00310               SH_OP_ASN, ShVariable(*I)));
00311         ++I;
00312         SH_DEBUG_ASSERT(I != srcNodes.end());
00313         afterBlock->addStatement(ShStatement(ShVariable(splits[stmt.dest.node()][1],stmt.dest.swizzle(), stmt.dest.neg()),
00314               SH_OP_ASN, ShVariable(*I)));
00315 
00316         // join up the nodes appropriately
00317         node->append(newProgram.node()->ctrlGraph->entry());
00318         newProgram.node()->ctrlGraph->exit()->append(after);
00319 
00320         // the last thing - cleanup by removing original statement from the
00321         // block
00322         afterBlock->erase(stmtI); // get rid of the 
00323     }
00324   }
00325 
00326   void resetDirt() {
00327     dirty = false;
00328   }
00329 
00330   bool isDirty() {
00331     return dirty;
00332   }
00333 
00334   ShTransformer::VarSplitMap &splits;
00335   bool dirty;
00336 };
00337 
00338 }
00339 
00340 namespace SH {
00341 
00342 void ShTransformer::convertIntervalTypes(ShTransformer::VarSplitMap &splits) {
00343   IntervalSplitter is(splits, m_changed);
00344   is.splitVarList(m_program->inputs);
00345   is.splitVarList(m_program->outputs);
00346   m_program->ctrlGraph->dfs(is);
00347 
00348   // @todo type this thing is DARNED slow...
00349   // To make it faster, need to think about how to continue properly
00350   // traversing the control graph even if the statement fixer
00351   // starts mucking around with both the node currently being traversed
00352   // and its children...
00353   //
00354   // The default dfs might work...but until it provably does, 
00355   // the StatementFixer starts traversing anew on each pass
00356   // until it hits one statement that needs fixing,
00357   // mangles the graph around inserting the replacement
00358   // code, and quits...
00359   //
00360   // (It can fix LO, HI, SETLO, and SETHI statements in place without
00361   // graph mangling)
00362 
00363   IntervalStatementFixer isf(splits);
00364   do {
00365     isf.resetDirt();
00366     m_program->ctrlGraph->dfs(isf);
00367     m_changed = m_changed || isf.isDirty();
00368   } while(isf.isDirty());
00369 }
00370 
00371 }

Generated on Mon Jan 24 18:36:32 2005 for Sh by  doxygen 1.4.1