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

ShTypeConvertTransformer.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 "ShSyntax.hpp"
00031 #include "ShError.hpp"
00032 #include "ShDebug.hpp"
00033 #include "ShTypeInfo.hpp"
00034 #include "ShVariableNode.hpp"
00035 #include "ShInternals.hpp"
00036 #include "ShInstructions.hpp"
00037 #include "ShEval.hpp"
00038 #include "ShTransformer.hpp"
00039 
00040 // #define SH_DEBUG_TYPECONVERT
00041 
00042 // @file ShTypeConvertTransformer.cpp
00043 //
00044 // Currently does something naive.  Assumes all unsupported types in the backend
00045 // turn into floats, so it is often too conservative.
00046 // (i.e. in NV for fixed-point registers, may not need to do some of the
00047 // clamping of operation results, etc.)
00048 //
00049 // @todo type may want think about leaving fi and fui types for NV backend to indicate 
00050 // using _SAT _SSAT modifiers
00051 //
00052 // @todo type probably want to use a different set of available operators (or
00053 // expand on host operator set) for different backends
00054 
00055 namespace SH {
00056 
00057 // algorithm
00058 // Converts non-float types into floats (for standard Sh types)
00059 //
00060 // Algorithm: 
00061 // 1) Identify variables of the following types that are not float  
00062 //   SH_INPUT = 0,
00063 //   SH_OUTPUT = 1,
00064 //   SH_INOUT = 2,
00065 //   SH_TEMP = 3,
00066 //   SH_CONST = 4,
00067 //   
00068 //   Add a replacement to an ShVarMap
00069 //
00070 // This pass should be done by collecting the variables then using the
00071 // FloatConverter as a functor on the variables lists.
00072 //
00073 // 2) Where an input argument to an operation does not match the 
00074 //    input type expected there insert code before the current 
00075 //    statement for a conversion. 
00076 //
00077 //    This code should operate on the float versions of variables,
00078 //    not the original if they have been converted
00079 //
00080 // 3) Generate any code required for conversion of the output
00081 //    and insert code after current statement.
00082 //
00083 //    Output conversion may be required for two reasons
00084 //    a) if the operation done in floating point gives a different
00085 //       answer than the original type of the operation
00086 //    b) if the destination variable or operation's destination type
00087 //       need to be converted
00088 //
00089 //    This should also operate on the float variables.
00090 //
00091 // Conversions in 2&3 should only happen if the type being converted from
00092 // or to is a key in m_valueTypeMap 
00093 //
00094 // (If neither is the case, then assume that the hardware natively 
00095 // supports the types and does the appropriate conversions automatically)
00096 //
00097 // @todo check that this works properly.  This is probably too conservative
00098 // in some cases where a conversion may not be necessary.
00099 //
00100 // 5) Run a ShVariableReplacer to replace old non-float variables
00101 //    with float ones.
00102 //    (Run it on both the control graph, input list, and output list)
00103 //    
00104 //    
00105 // Assumed floating point representations
00106 //  int - use  
00107 //  uint -   
00108 //  frac - use float, but clamped to [-1,1]
00109 //  ufrac - use float, but clamped to [0,1]
00110 //  
00111 //    Desired Type -->    float   int   uint      frac      ufrac
00112 //    Existing Type      
00113 //    float               X       flr   max0,flr  max_1,1   max0,1 
00114 //    int                 X       X     max0      max_1,1   max0,1 
00115 //    uint                X       X     X         max1      max1 
00116 //    frac                X       flr   max0,flr  X         max0 
00117 //    ufrac               X       flr   flr       X         X
00118 //
00119 // 
00120 // @todo handle textures/streams later
00121 // since they do not need to be converted to float
00122 // and have storage formats that vary more with
00123 // the backend. 
00124 //
00125 //   SH_TEXTURE = 5,
00126 //  
00127 //   SH_STREAM = 
00128 struct FloatConverter {
00129   FloatConverter(ShTransformer::ValueTypeMap &valueTypeMap, ShVarMap &converts)
00130     : m_valueTypeMap(valueTypeMap), m_converts(converts), m_eval(ShEval::instance())
00131   {
00132   }
00133 
00134   void operator()(ShCtrlGraphNodePtr node) {
00135     if (!node) return;
00136     ShBasicBlockPtr block = node->block;
00137     if (!block) return;
00138     
00139     ShBasicBlock::ShStmtList::iterator I;
00140     for(I = block->begin(); I != block->end(); ++I) {
00141       fixStatement(block->m_statements, I);  
00142     }
00143   }
00144 
00145   // @todo might want to make these more global (perhaps even
00146   // put it into the TypeInfo for standard types?)
00147   bool isFloat(ShValueType valueType) {
00148     return shIsFloat(valueType) && shIsRegularValueType(valueType);
00149   }
00150 
00151   bool isInt(ShValueType valueType) {
00152     return shIsInteger(valueType) && shIsSigned(valueType);
00153   }
00154 
00155   // @todo not implemented yet 
00156   bool isUint(ShValueType valueType) {
00157     return shIsInteger(valueType) && !shIsSigned(valueType);
00158   }
00159 
00160   // @todo not implemented yet 
00161   bool isFrac(ShValueType valueType) {
00162     return shIsFraction(valueType) && shIsSigned(valueType);
00163   }
00164 
00165   bool isUfrac(ShValueType valueType) {
00166     return shIsFraction(valueType) && !shIsSigned(valueType);
00167   }
00168 
00169   // flags to apply following operations (in order) 
00170   static const unsigned int APPLY_MAX_1  = 0x01; // clamp >= -1
00171   static const unsigned int APPLY_MAX0   = 0x02; // clamp >= 0
00172   static const unsigned int APPLY_MIN1   = 0x04; // clamp <= 1
00173   static const unsigned int APPLY_FLR    = 0x08; // take floor
00174 
00175   // inserts conversion code before m_I in m_curList that takes the given var 
00176   // and converts type from fromType to toType and puts this in
00177   // result ShVariable             
00178   //
00179   // Instead of using var's variable node in these operations,
00180   // it uses newvarNode (this may in fact be the same as var->node()) 
00181   //
00182   // var and result should have the converted types already,
00183   // but fromType and toType are the original types requested for the statement.
00184   //
00185   void insertConversion(ShBasicBlock::ShStmtList &stmtList, const ShBasicBlock::ShStmtList::iterator &I,
00186       const ShVariable &var, ShValueType fromType, const ShVariable &result, ShValueType toType,
00187       unsigned int forced = 0)
00188   {
00189     unsigned int operations = forced;
00190     if(isFloat(toType)) {
00191     } else if(isInt(toType)) {
00192       if(!(isInt(fromType) || isUint(fromType))) {
00193         operations |= APPLY_FLR;
00194       }
00195     } else if(isUint(toType)) {
00196       if(isFloat(fromType) || isInt(fromType) || isFrac(fromType)) {
00197         operations |= APPLY_MAX0; 
00198       }
00199       operations |= APPLY_FLR;
00200     } else if(isFrac(toType)) {
00201       if (isFloat(fromType) || isInt(fromType)) {
00202         operations |= APPLY_MAX_1;
00203         operations |= APPLY_MIN1;
00204       } else if (isUint(fromType)) {
00205         operations |= APPLY_MIN1;
00206       }
00207     } else if(isUfrac(toType)) {
00208       if (isFloat(fromType) || isInt(fromType)) {
00209         operations |= APPLY_MAX0;
00210         operations |= APPLY_MIN1;
00211       } else if (isUint(fromType)) {
00212         operations |= APPLY_MIN1;
00213       }
00214     }
00215     // @todo make sure to run make another temp in case
00216     // one of var/result is an IN/OUT and hence cannot be used in computation 
00217     ShVariable temp(var.node()->clone(SH_TEMP, var.size(), SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00218 
00219     stmtList.insert(I, ShStatement(temp, SH_OP_ASN, var));
00220 
00221     if(operations & APPLY_MAX_1) {
00222       ShVariable one(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00223       one.setVariant(shVariantFactory(var.valueType())->generateOne());
00224       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MAX, one)); 
00225     }
00226 
00227     if(operations & APPLY_MAX0)  {
00228       ShVariable zero(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00229       zero.setVariant(shVariantFactory(var.valueType())->generateZero());
00230       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MAX, zero)); 
00231     }
00232 
00233     if(operations & APPLY_MIN1)  {
00234       ShVariable one(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00235       one.setVariant(shVariantFactory(var.valueType())->generateOne());
00236       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MIN, one)); 
00237     }
00238 
00239     if(operations & APPLY_FLR) stmtList.insert(I, ShStatement(temp, SH_OP_FLR, temp)); 
00240 
00241     stmtList.insert(I, ShStatement(result, SH_OP_ASN, temp));
00242 
00243     if((operations & APPLY_FLR) && (operations - APPLY_FLR)) {
00244 #ifdef SH_DEBUG_TYPECONVERT
00245       SH_DEBUG_PRINT("Unhandled conversion operations");
00246 #endif
00247     }
00248   }
00249 
00250   // Adds required conversions for statment *I
00251   void fixStatement(ShBasicBlock::ShStmtList &stmtList, const ShBasicBlock::ShStmtList::iterator &I) {
00252     ShStatement &stmt = *I;
00253 #ifdef SH_DEBUG_TYPECONVERT
00254     SH_DEBUG_PRINT("Checking a statement op=" << opInfo[stmt.op].name);
00255 #endif
00256 
00257     const ShEvalOpInfo* evalOpInfo; 
00258     ShEvalOpInfo* texInfo = 0;
00259     switch(stmt.op) {
00260       case SH_OP_TEX:
00261       case SH_OP_TEXI:
00262       case SH_OP_FETCH:
00263       case SH_OP_TEXD:
00264         // @todo type think of a cleaner solution.
00265         // - we probably shouldn't be using the host-side set of operations
00266         // anyway - maybe use a functor given by the backend that decides
00267         // which types to use for a given set of src/dest types 
00268         
00269         // temporary hack - make an evalop for this
00270         evalOpInfo = texInfo = new ShEvalOpInfo(stmt.op, 0, stmt.dest.valueType(), stmt.src[0].valueType(), stmt.src[1].valueType(), stmt.src[2].valueType());
00271         break;
00272       default:
00273         evalOpInfo = m_eval->getEvalOpInfo(
00274             stmt.op,
00275             stmt.dest.valueType(), 
00276             stmt.src[0].valueType(),
00277             stmt.src[1].valueType(),
00278             stmt.src[2].valueType());
00279         break;
00280     }
00281 
00282     if(!evalOpInfo) {
00283 #ifdef SH_DEBUG_TYPECONVERT
00284       SH_DEBUG_PRINT("Problem finding evaluator for op = " << opInfo[stmt.op].name); 
00285 #endif
00286       return;
00287     }
00288 
00289 
00290     for(int i = 0; i < 3; ++i) {
00291       ShValueType srcValueType = stmt.src[i].valueType();
00292       ShValueType opValueType = evalOpInfo->m_src[i]; 
00293       if(srcValueType == opValueType) continue;
00294       if((m_valueTypeMap.count(srcValueType) == 0) && (m_valueTypeMap.count(opValueType) == 0)) continue;
00295 
00296 #ifdef SH_DEBUG_TYPECONVERT
00297       SH_DEBUG_PRINT("  Converting src[" << i << "] from " << shTypeInfo(srcValueType)->name()
00298           << " to " << shTypeInfo(opValueType)->name());
00299 #endif
00300 
00301       // Step 2: prepend code to convert src[i], making sure all variables
00302       // involved are converted if their type is in m_valueTypeMap
00303       ShValueType tempValueType = opValueType;
00304       if(m_valueTypeMap.count(tempValueType) > 0) tempValueType = m_valueTypeMap[tempValueType];
00305       ShVariable temp(stmt.src[i].node()->clone(SH_TEMP, stmt.src[i].size(), tempValueType, SH_SEMANTICTYPE_END, false, false));
00306 
00307       ShVariableNodePtr varNode = stmt.src[i].node();
00308       if(m_converts.count(varNode) > 0) varNode = m_converts[varNode];
00309       ShVariable newsrc(varNode, stmt.src[i].swizzle(), stmt.src[i].neg());
00310 
00311       insertConversion(stmtList, I, newsrc, srcValueType, temp, opValueType); 
00312 
00313       stmt.src[i] = temp;
00314     }
00315 
00316     // Step 3: append code to convert dest 
00317     //
00318     // @todo type - for certain ops, doing them in float doesn't give the same
00319     // result as non-float, so the output needs to be fixed...
00320     //
00321     // @todo in particular, for int, DIV, POW need flooring afterwards 
00322     ShValueType destValueType = stmt.dest.valueType();
00323     ShValueType opDest = evalOpInfo->m_dest;
00324     if((destValueType != opDest) &&
00325        (m_valueTypeMap.count(destValueType) + m_valueTypeMap.count(opDest) > 0)) {
00326 
00327 #ifdef SH_DEBUG_TYPECONVERT
00328       SH_DEBUG_PRINT("  Converting dest from " << shTypeInfo(opDest)->name() << " to " << shTypeInfo(destValueType)->name() );
00329 #endif
00330 
00331       ShBasicBlock::ShStmtList::iterator afterI = I;
00332       ++afterI;
00333 
00334       ShValueType tempValueType = opDest;
00335       if(m_valueTypeMap.count(tempValueType) > 0) tempValueType = m_valueTypeMap[tempValueType];
00336       ShVariable temp(stmt.dest.node()->clone(SH_TEMP, stmt.dest.size(), tempValueType, SH_SEMANTICTYPE_END, false, false));
00337 
00338       ShVariableNodePtr varNode = stmt.dest.node();
00339       if(m_converts.count(varNode) > 0) varNode = m_converts[varNode];
00340       ShVariable newdest(varNode, stmt.dest.swizzle(), stmt.dest.neg()); 
00341 
00342       // @todo type check for other special cases
00343       unsigned int forcedOps = 0;
00344       if((stmt.op == SH_OP_DIV || stmt.op == SH_OP_POW) && 
00345          (isInt(destValueType) || isUint(destValueType))) {
00346         forcedOps |= APPLY_FLR; 
00347       }
00348 
00349       insertConversion(stmtList, afterI, temp, opDest, newdest, destValueType, forcedOps); 
00350 
00351       stmt.dest = temp;
00352     }
00353 
00354     if(texInfo) delete texInfo;
00355   }
00356 
00357   // Adds a conversion for p into the m_converts map 
00358   void operator()(const ShVariableNodePtr &p) {
00359     // check if done or conversion not necessary
00360     if(m_converts.count(p) > 0) return; 
00361     if(m_valueTypeMap.count(p->valueType()) == 0) return; 
00362     ShVariableNodePtr &converted_p = m_converts[p] 
00363       = p->clone(SH_BINDINGTYPE_END, 0, m_valueTypeMap[p->valueType()], SH_SEMANTICTYPE_END, false);
00364 
00365     if(p->hasValues()) {
00366       converted_p->setVariant(p->getVariant());
00367 
00368 #ifdef SH_DEBUG_TYPECONVERT
00369       SH_DEBUG_PRINT("Setting values on replacement = " << converted_p->getVariant()->encode() << " original = " << p->getVariant()->encode());
00370 #endif
00371     }
00372     
00373     if(p->uniform()) { // @todo set up dependent uniform
00374       ShProgram prg = SH_BEGIN_PROGRAM("uniform") {
00375         ShVariable original(p);
00376         ShVariable converted(converted_p);
00377         shASN(converted, original);
00378       } SH_END;
00379       converted_p->attach(prg.node());
00380     }
00381 
00382 #ifdef SH_DEBUG_TYPECONVERT
00383     SH_DEBUG_PRINT("Converting " << p->name() << " from " << shTypeInfo(p->valueType())->name()
00384       << " to " << shTypeInfo(m_valueTypeMap[p->valueType()])->name()); 
00385 #endif
00386   }
00387 
00388 
00389   ShTransformer::ValueTypeMap &m_valueTypeMap;
00390   ShVarMap &m_converts;
00391 
00392   private:
00393     ShEval* m_eval;
00394 };
00395 
00396 void ShTransformer::convertToFloat(ValueTypeMap &valueTypeMap)
00397 {
00398   ShVarMap converts;
00399   FloatConverter floatconv(valueTypeMap, converts);
00400 
00401   // Step 1
00402   m_program->collectVariables(); 
00403   std::for_each(m_program->inputs.begin(), m_program->inputs.end(), floatconv); 
00404   std::for_each(m_program->outputs.begin(), m_program->outputs.end(), floatconv); 
00405   std::for_each(m_program->temps.begin(), m_program->temps.end(), floatconv); 
00406   std::for_each(m_program->constants.begin(), m_program->constants.end(), floatconv); 
00407   std::for_each(m_program->uniforms.begin(), m_program->uniforms.end(), floatconv); 
00408 
00409   // Steps 2-3
00410   m_program->ctrlGraph->dfs(floatconv);
00411 
00412   // Step 4
00413   ShVariableReplacer vr(converts);
00414   m_program->ctrlGraph->dfs(vr);
00415   vr(m_program->inputs);
00416   vr(m_program->outputs);
00417 
00418   if(!converts.empty()) m_changed = true;
00419 }
00420 
00421 }
00422 

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