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

Generated on Thu Apr 21 17:32:49 2005 for Sh by  doxygen 1.4.2