ShTypeConvertTransformer.cpp

00001 // Sh: A GPU metaprogramming language.
00002 //
00003 // Copyright 2003-2006 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   // assignment operator could not be generated: declaration only
00128   FloatConverter& operator=(FloatConverter const&);
00129 
00130   void operator()(const ShCtrlGraphNodePtr& node) {
00131     if (!node) return;
00132     ShBasicBlockPtr block = node->block;
00133     if (!block) return;
00134     
00135     ShBasicBlock::ShStmtList::iterator I;
00136     for(I = block->begin(); I != block->end(); ++I) {
00137       fixStatement(block->m_statements, I);  
00138     }
00139   }
00140 
00141   // @todo might want to make these more global (perhaps even
00142   // put it into the TypeInfo for standard types?)
00143   bool isFloat(ShValueType valueType) {
00144     return shIsFloat(valueType) && shIsRegularValueType(valueType);
00145   }
00146 
00147   bool isInt(ShValueType valueType) {
00148     return shIsInteger(valueType) && shIsSigned(valueType);
00149   }
00150 
00151   // @todo not implemented yet 
00152   bool isUint(ShValueType valueType) {
00153     return shIsInteger(valueType) && !shIsSigned(valueType);
00154   }
00155 
00156   // @todo not implemented yet 
00157   bool isFrac(ShValueType valueType) {
00158     return shIsFraction(valueType) && shIsSigned(valueType);
00159   }
00160 
00161   bool isUfrac(ShValueType valueType) {
00162     return shIsFraction(valueType) && !shIsSigned(valueType);
00163   }
00164 
00165   // flags to apply following operations (in order) 
00166   static const unsigned int APPLY_MAX_1  = 0x01; // clamp >= -1
00167   static const unsigned int APPLY_MAX0   = 0x02; // clamp >= 0
00168   static const unsigned int APPLY_MIN1   = 0x04; // clamp <= 1
00169   static const unsigned int APPLY_FLR    = 0x08; // take floor
00170 
00171   // inserts conversion code before m_I in m_curList that takes the given var 
00172   // and converts type from fromType to toType and puts this in
00173   // result ShVariable             
00174   //
00175   // Instead of using var's variable node in these operations,
00176   // it uses newvarNode (this may in fact be the same as var->node()) 
00177   //
00178   // var and result should have the converted types already,
00179   // but fromType and toType are the original types requested for the statement.
00180   //
00181   void insertConversion(ShBasicBlock::ShStmtList &stmtList, const ShBasicBlock::ShStmtList::iterator &I,
00182       const ShVariable &var, ShValueType fromType, const ShVariable &result, ShValueType toType,
00183       unsigned int forced = 0)
00184   {
00185     unsigned int operations = forced;
00186     if(isFloat(toType)) {
00187     } else if(isInt(toType)) {
00188       if(!(isInt(fromType) || isUint(fromType))) {
00189         operations |= APPLY_FLR;
00190       }
00191     } else if(isUint(toType)) {
00192       if(isFloat(fromType) || isInt(fromType) || isFrac(fromType)) {
00193         operations |= APPLY_MAX0; 
00194       }
00195       operations |= APPLY_FLR;
00196     } else if(isFrac(toType)) {
00197       if (isFloat(fromType) || isInt(fromType)) {
00198         operations |= APPLY_MAX_1;
00199         operations |= APPLY_MIN1;
00200       } else if (isUint(fromType)) {
00201         operations |= APPLY_MIN1;
00202       }
00203     } else if(isUfrac(toType)) {
00204       if (isFloat(fromType) || isInt(fromType)) {
00205         operations |= APPLY_MAX0;
00206         operations |= APPLY_MIN1;
00207       } else if (isUint(fromType)) {
00208         operations |= APPLY_MIN1;
00209       }
00210     }
00211     // @todo make sure to run make another temp in case
00212     // one of var/result is an IN/OUT and hence cannot be used in computation 
00213     ShVariable temp(var.node()->clone(SH_TEMP, var.size(), SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00214 
00215     stmtList.insert(I, ShStatement(temp, SH_OP_ASN, var));
00216 
00217     if(operations & APPLY_MAX_1) {
00218       ShVariable one(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00219       one.setVariant(shVariantFactory(var.valueType())->generateOne());
00220       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MAX, one)); 
00221     }
00222 
00223     if(operations & APPLY_MAX0)  {
00224       ShVariable zero(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00225       zero.setVariant(shVariantFactory(var.valueType())->generateZero());
00226       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MAX, zero)); 
00227     }
00228 
00229     if(operations & APPLY_MIN1)  {
00230       ShVariable one(var.node()->clone(SH_CONST, 1, SH_VALUETYPE_END, SH_SEMANTICTYPE_END, false, false));
00231       one.setVariant(shVariantFactory(var.valueType())->generateOne());
00232       stmtList.insert(I, ShStatement(temp, temp, SH_OP_MIN, one)); 
00233     }
00234 
00235     if(operations & APPLY_FLR) stmtList.insert(I, ShStatement(temp, SH_OP_FLR, temp)); 
00236 
00237     stmtList.insert(I, ShStatement(result, SH_OP_ASN, temp));
00238 
00239     if((operations & APPLY_FLR) && (operations - APPLY_FLR)) {
00240 #ifdef SH_DEBUG_TYPECONVERT
00241       SH_DEBUG_PRINT("Unhandled conversion operations");
00242 #endif
00243     }
00244   }
00245 
00246   // Adds required conversions for statment *I
00247   void fixStatement(ShBasicBlock::ShStmtList &stmtList, const ShBasicBlock::ShStmtList::iterator &I) {
00248     ShStatement &stmt = *I;
00249     if(stmt.dest.null()) return;
00250 #ifdef SH_DEBUG_TYPECONVERT
00251     SH_DEBUG_PRINT("Checking a statement op=" << opInfo[stmt.op].name);
00252 #endif
00253 
00254     const ShEvalOpInfo* evalOpInfo; 
00255     ShEvalOpInfo* texInfo = 0;
00256     switch(stmt.op) {
00257       case SH_OP_TEX:
00258       case SH_OP_TEXI:
00259       case SH_OP_FETCH:
00260       case SH_OP_TEXD:
00261       case SH_OP_DX:
00262       case SH_OP_DY:
00263         // @todo type think of a cleaner solution.
00264         // - we probably shouldn't be using the host-side set of operations
00265         // anyway - maybe use a functor given by the backend that decides
00266         // which types to use for a given set of src/dest types 
00267         
00268         // temporary hack - make an evalop for this
00269         evalOpInfo = texInfo = new ShEvalOpInfo(stmt.op, 0, stmt.dest.valueType(), stmt.src[0].valueType(), stmt.src[1].valueType(), stmt.src[2].valueType());
00270         break;
00271       default:
00272         evalOpInfo = m_eval->getEvalOpInfo(
00273             stmt.op,
00274             stmt.dest.valueType(), 
00275             stmt.src[0].valueType(),
00276             stmt.src[1].valueType(),
00277             stmt.src[2].valueType());
00278         break;
00279     }
00280 
00281     if(!evalOpInfo) {
00282       switch(stmt.op) {
00283         case SH_OP_TEX:
00284         case SH_OP_TEXI:
00285         case SH_OP_FETCH:
00286         case SH_OP_TEXD:
00287         case SH_OP_KIL:
00288         case SH_OP_RET:
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 Feb 16 14:51:39 2006 for Sh by  doxygen 1.4.6