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

Generated on Thu Jul 28 17:33:06 2005 for Sh by  doxygen 1.4.3-20050530