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

CcEmit.cpp

Go to the documentation of this file.
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 <map>
00028 #include <vector>
00029 #include <sstream>
00030 #include "Cc.hpp" 
00031 #include "ShDebug.hpp" 
00032 #include "ShStream.hpp" 
00033 #include "ShVariant.hpp"
00034 #include "ShOperation.hpp"
00035 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 #ifdef SH_CC_DEBUG
00041 #  define SH_CC_DEBUG_PRINT(x) SH_DEBUG_PRINT(x)
00042 #else
00043 #  define SH_CC_DEBUG_PRINT(x) do { } while(0)
00044 #endif
00045 
00046 namespace ShCc {
00047 
00048 using namespace SH;
00049 
00055 // @todo type use different functions depending on type
00056 // e.g. std::abs for int types, funcf versions of func for float
00057 
00058 // handles linear ops that require up to 4 src arguments (may not be independent) 
00059 struct CcOpCode 
00060 {
00061   ShOperation op;
00062   char *code;
00063 };
00064 
00065 struct CcOpCodeVecs
00066 {
00067   CcOpCodeVecs() {}
00068 
00069   // Dices up the code string into references #i or $i to 
00070   // src variables and the code fragments between references. 
00071   //
00072   // after construction, frag.size() == (index.size() + 1) 
00073   // and index.size() = scalar.size()
00074   CcOpCodeVecs(const CcOpCode &op);
00075 
00076   bool operator<(const CcOpCodeVecs &other) {
00077     return op < other.op;
00078   }
00079 
00080   std::string encode() const;
00081 
00082   ShOperation op;
00083 
00084   std::vector<int> index;
00085   std::vector<bool> scalar;
00086   std::vector<std::string> frag; 
00087 };
00088 
00089 
00090 typedef std::map<SH::ShOperation, CcOpCodeVecs> CcOpCodeMap;
00091 
00092 CcOpCodeVecs::CcOpCodeVecs(const CcOpCode &op) {
00093   std::string code = op.code; 
00094 
00095   unsigned i, j;
00096   i = j = 0;
00097   for(; (j = code.find_first_of("#$", i)) != std::string::npos;) {
00098     frag.push_back(code.substr(i, j - i));
00099     scalar.push_back(code[j] == '$');
00100     i = j + 1;
00101     j = code.find_first_not_of("012345689", i);
00102     index.push_back(atoi(code.substr(i, j - i).c_str()));
00103     i = j;
00104   }
00105   if(i == std::string::npos) {
00106     frag.push_back("");
00107   } else {
00108     frag.push_back(code.substr(i));
00109   }
00110 
00111 }
00112 
00113 std::string CcOpCodeVecs::encode() const 
00114 {
00115   std::ostringstream out;
00116   for(unsigned int i = 0; i < index.size(); ++i) {
00117     out << frag[i];
00118     out << "src[" << index[i] << "]";
00119     if(scalar[i]) out << ".scalar";
00120   }
00121   out << frag.back();
00122   return out.str();
00123 }
00124 
00125 // Table of replacement macros holding C++ code corresponding to SH op 
00126 //
00127 // This table is parsed into a static map in CcBackendCode.  Although
00128 // it may be possible to classify the ops depending on the kinds of C++
00129 // code output, this is a bit trickier than with the gl backend. Since
00130 // the end result is not assmebly, the variety of syntax makes categorizing
00131 // things more difficult, so here's the easy way out. 
00132 //
00133 // Each entry represents the following macro expansion
00134 // for j = 0 to dest.size() 
00135 //    insert code for resolve(dest, j) = code string in table with 
00136 //        #i in rhs replaced by resolve(src[i], j)
00137 //        $i in rhs replaced by resolve(src[i], src[i].size() == 1 ? 0 : j)
00138 //        where i is an non-negative integer
00139 const CcOpCode opCodeTable[] = {
00140   {SH_OP_ASN,   "#0" },
00141   {SH_OP_NEG,   "-#0" },  
00142   {SH_OP_ADD,   "$0 + $1"},
00143   {SH_OP_MUL,   "$0 * $1"},
00144   {SH_OP_DIV,   "$0 / $1"},
00145 
00146   {SH_OP_SLT,   "($0 < $1 ? 1 : 0)"},
00147   {SH_OP_SLE,   "($0 <= $1 ? 1 : 0)"},
00148   {SH_OP_SGT,   "($0 > $1 ? 1 : 0)"},
00149   {SH_OP_SGE,   "($0 >= $1 ? 1 : 0)"},
00150   {SH_OP_SEQ,   "($0 == $1 ? 1 : 0)"},
00151   {SH_OP_SNE,   "($0 != $1 ? 1 : 0)"},
00152 
00153   {SH_OP_ABS,   "fabs(#0)"}, 
00154   {SH_OP_ACOS,  "acos(#0)"},
00155   {SH_OP_ASIN,  "asin(#0)"},
00156   {SH_OP_ATAN,  "atan(#0)"},
00157   {SH_OP_ATAN2, "atan2(#0, #1)"},
00158   {SH_OP_CBRT,  "pow(#0, 1 / 3.0)"},
00159   {SH_OP_CEIL,  "ceil(#0)"},
00160   {SH_OP_COS,   "cos(#0)"},
00161   {SH_OP_EXP,   "exp(#0)"},
00162   {SH_OP_EXP2,  "exp2(#0)"},
00163   {SH_OP_EXP10, "exp10(#0)"},
00164   {SH_OP_FLR,   "floor(#0)"},
00165   {SH_OP_FRAC,  "#0 - floor(#0)"},
00166   {SH_OP_LOG,   "log(#0)"},
00167   {SH_OP_LOG2,  "log2(#0)"},
00168   {SH_OP_LOG10, "log10(#0)"},
00169   {SH_OP_LRP,   "$0 * ($1 - $2) + $2"},
00170   {SH_OP_MAD,   "$0 * $1 + $2"},
00171   {SH_OP_MAX,   "($0 > $1 ? $0 : $1)"},
00172   {SH_OP_MIN,   "($0 < $1 ? $0 : $1)"}, 
00173   {SH_OP_MOD,   "fmod($0, $1)"},
00174   {SH_OP_POW,   "pow($0, $1)"},
00175   {SH_OP_RCP,   "1 / #0"},
00176   {SH_OP_RND,   "floor(#0 + 0.5)"},
00177   {SH_OP_RSQ,   "1 / sqrt(#0)"},
00178   {SH_OP_SIN,   "sin(#0)"},
00179   {SH_OP_SGN,   "(#0 < 0 ? -1 : (#0 > 0 ? 1 : 0))"},
00180   {SH_OP_SQRT,  "sqrt(#0)"},
00181   {SH_OP_TAN,   "tan(#0)"},
00182   {SH_OP_COND,  "($0 > 0 ? $1 : $2)"},
00183   {SH_OP_FETCH, "#0"},
00184 
00185   {SH_OPERATION_END,  0} 
00186   // @todo LO, HI, SETLO, SETHI
00187 };
00188 
00189 // @todo type these are still implemented in the switch statement below
00190 // fix them later or maybe just leave them 
00191 #if 0
00192   {SH_OP_CMUL,             
00193   {SH_OP_CSUM,             
00194   {SH_OP_DOT,             
00195   {SH_OP_NORM,             
00196   {SH_OP_XPD,              
00197   {SH_OP_TEX,              
00198   {SH_OP_TEXI,             
00199   {SH_OP_TEXD,             
00200   {SH_OP_KIL,              
00201   {SH_OP_OPTBRA,           
00202 #endif
00203 
00204 // @todo type implement emit
00205 void CcBackendCode::emit(const ShStatement& stmt) {
00206   static CcOpCodeMap opcodeMap;
00207 
00208   // @todo type should really move this to the CcBackendCode constructor 
00209   // @todo type should handle other types properly
00210   
00211   // fill in opcodeMap from the above table
00212   if(opcodeMap.empty()) {
00213     SH_CC_DEBUG_PRINT("ShOperation -> C++ code mappings");
00214     for(int i = 0; opCodeTable[i].op != SH_OPERATION_END; ++i) {
00215 
00216       opcodeMap[opCodeTable[i].op] = CcOpCodeVecs(opCodeTable[i]); 
00217       SH_CC_DEBUG_PRINT(opInfo[opCodeTable[i].op].name << " -> " 
00218           << opcodeMap[opCodeTable[i].op].encode());
00219     }
00220   }
00221 
00222   // output SH intermediate m_code for reference
00223   m_code << "  // " << stmt << std::endl;
00224 
00225   // generate C m_code from statement
00226 
00227   // @todo get rid of warnings for assignment of different types 
00228   // (e.g. when float to int cast required)
00229 
00230   // handle ops in the table first 
00231   if(opcodeMap.find(stmt.op) != opcodeMap.end()) {
00232     CcOpCodeVecs codeVecs = opcodeMap[stmt.op]; 
00233     for(int i = 0; i < stmt.dest.size(); ++i) {
00234       m_code << "  " << resolve(stmt.dest, i) << " = (" 
00235         << ctype(stmt.dest.valueType()) << ")(";
00236       unsigned int j;
00237       for(j = 0; j < codeVecs.index.size(); ++j) { 
00238         const ShVariable& src = stmt.src[codeVecs.index[j]];
00239         m_code << codeVecs.frag[j];
00240         if(codeVecs.scalar[j]) {
00241           m_code << resolve(src, src.size() > 1 ? i : 0); 
00242         } else {
00243           m_code << resolve(src, i); 
00244         }
00245       }
00246       m_code << codeVecs.frag[j] << ");" << std::endl;
00247     }
00248     return;
00249   }
00250 
00251   // handle remaining ops with some custom code
00252   // @todo improve collecting ops
00253   switch(stmt.op) {
00254     case SH_OP_DOT:
00255       {
00256         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00257         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00258           << resolve(stmt.src[0], 0) 
00259           << " * "
00260           << resolve(stmt.src[1], 0)
00261           << ";" << std::endl;
00262 
00263         int inc0 = stmt.src[0].size() == 1 ? 0 : 1;
00264         int inc1 = stmt.src[1].size() == 1 ? 0 : 1;
00265         int size = std::max(stmt.src[0].size(), stmt.src[1].size());
00266 
00267         int i, s0, s1;
00268         for(i = s0 = s1 = 1; i < size; ++i, s0 += inc0, s1 += inc1) {
00269           m_code << "  " << resolve(stmt.dest, 0) << " += " 
00270             << resolve(stmt.src[0], s0) 
00271             << " * "
00272             << resolve(stmt.src[1], s1)
00273             << ";" << std::endl;
00274         }
00275         break;
00276       }
00277 
00278     case SH_OP_CSUM:
00279       {
00280         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00281         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00282           << resolve(stmt.src[0], 0) 
00283           << ";" << std::endl;
00284 
00285         int size = stmt.src[0].size();
00286         for(int i = 1; i < size; ++i) {
00287           m_code << "  " << resolve(stmt.dest, 0) << " += " 
00288             << resolve(stmt.src[0], i) 
00289             << ";" << std::endl;
00290         }
00291         break;
00292       }
00293 
00294     case SH_OP_CMUL:
00295       {
00296         SH_DEBUG_ASSERT(stmt.dest.size() == 1);
00297         m_code << "  " << resolve(stmt.dest, 0) << " = " 
00298           << resolve(stmt.src[0], 0) 
00299           << ";" << std::endl;
00300 
00301         int size = stmt.src[0].size();
00302         for(int i = 1; i < size; ++i) {
00303           m_code << "  " << resolve(stmt.dest, 0) << " *= " 
00304             << resolve(stmt.src[0], i) 
00305             << ";" << std::endl;
00306         }
00307         break;
00308       }
00309 
00310     case SH_OP_NORM:
00311       {
00312         m_code << "  {" << std::endl;
00313         m_code << "    float len = 1.0/sqrt(";
00314         for(int i = 0; i < stmt.dest.size(); i++)
00315         {
00316           if (i != 0) m_code << " + ";
00317           m_code << resolve(stmt.src[0], i)
00318            << " * "
00319            << resolve(stmt.src[0], i);
00320         }
00321         m_code << ");" << std::endl;
00322         for(int i = 0; i < stmt.dest.size(); i++)
00323         {
00324           m_code << "    "
00325            << resolve(stmt.dest, i)
00326            << " = len*"
00327            << resolve(stmt.src[0], i)
00328            << ";" << std::endl;
00329         }
00330         m_code << "  }" << std::endl;
00331 
00332         break;
00333       }
00334     case SH_OP_XPD:
00335       {
00336         for(int i = 0; i < stmt.dest.size(); i++)
00337         {
00338           int i0 = (i+1)%3;
00339           int i1 = (i+2)%3;
00340           m_code << "  "
00341            << resolve(stmt.dest, i)
00342            << " = "
00343            << resolve(stmt.src[0], i0)
00344            << " * "
00345            << resolve(stmt.src[1], i1)
00346            << " - "
00347            << resolve(stmt.src[1], i0)
00348            << " * "
00349            << resolve(stmt.src[0], i1)
00350            << ";" << std::endl;
00351         }
00352 
00353         break;
00354       }
00355     case SH_OP_TEX:
00356       emitTexLookup(stmt, "sh_cc_backend_lookup");
00357       break;
00358 
00359     case SH_OP_TEXI:
00360       emitTexLookup(stmt, "sh_cc_backend_lookupi");
00361       break;
00362 
00363     case SH_OP_KIL:
00364       {
00365       // TODO: maintain prior output values 
00366       m_code << "  if (";
00367       for(int i = 0; i < stmt.src[0].size(); i++)
00368       {
00369         if (i != 0) m_code << " || ";
00370         m_code << "("
00371          << resolve(stmt.src[0], i) 
00372          << " > 0)";
00373       }
00374       m_code << ")" << std::endl;
00375       m_code << "    return;" << std::endl;
00376       break;
00377       }
00378     case SH_OP_OPTBRA:
00379       {
00380         SH_DEBUG_ASSERT(false);
00381         break;
00382       }
00383     default:
00384       {
00385       m_code << "  // *** unhandled operation "
00386              << opInfo[stmt.op].name
00387              << " ***" << std::endl;
00388       break;
00389       }
00390   }
00391 }
00392 
00393 void CcBackendCode::emitTexLookup(const ShStatement& stmt, const char* texfunc) {
00394   ShTextureNodePtr node = shref_dynamic_cast<ShTextureNode>(stmt.src[0].node());
00395   int dims = 0; 
00396   switch(node->dims()) {
00397     case SH_TEXTURE_1D: dims = 1; break;   
00398     case SH_TEXTURE_2D: dims = 2; break; 
00399     case SH_TEXTURE_RECT: dims = 2; break;
00400     case SH_TEXTURE_3D: dims = 3; break; 
00401     case SH_TEXTURE_CUBE: 
00402       SH_DEBUG_ERROR("Cube maps not handled"); 
00403     default:
00404       SH_DEBUG_ERROR("Unhandled texture dim");
00405   }
00406 
00407   // names of the functors to use for different texture lookup types 
00408   std::string srcInterp, srcFilter, srcWrap, destClamp; 
00409 
00410   if(node->traits().interpolation() != 0) {
00411       //shError(ShBackendException("cc backend supports only nearest-neighbour texture lookup."));
00412       //SH_DEBUG_WARN("cc backend supports only nearest-neighbour texture lookup.");
00413   }
00414 
00415   if (node->traits().filtering() != ShTextureTraits::SH_FILTER_NONE) {
00416       //shError(ShBackendException("cc backend does not support texture filtering."));
00417       SH_DEBUG_WARN("cc backend does not support texture filtering.");
00418   }
00419 
00420   switch(node->traits().wrapping()) {
00421     case ShTextureTraits::SH_WRAP_CLAMP:
00422     case ShTextureTraits::SH_WRAP_CLAMP_TO_EDGE:
00423       srcWrap = "sh_gcc_backend_wrap_clamp";
00424       break;
00425     case ShTextureTraits::SH_WRAP_REPEAT:
00426       srcWrap = "sh_gcc_backend_wrap_repeat";
00427       break;
00428     default:
00429       shError(ShBackendException("cc backend does not support requested texture wrapping mode."));
00430       break;
00431   }
00432 
00433   switch(node->traits().clamping()) {
00434     case ShTextureTraits::SH_CLAMPED:
00435       destClamp = "sh_gcc_backend_clamped";
00436       break;
00437     default:
00438       destClamp = "sh_gcc_backend_unclamped";
00439   }
00440 
00441   m_code << "  {" << std::endl;
00442   std::string destvar; 
00443   std::string srcvar;
00444   bool tempdest = !stmt.dest.swizzle().identity();
00445   bool tempsrc = (!stmt.src[1].swizzle().identity()) || stmt.src[1].neg();
00446 
00447   if(tempdest) {
00448     m_code << "    " << ctype(stmt.dest.valueType()) <<  
00449       " result[" << stmt.dest.size() << "];" << std::endl;
00450     destvar = "result";
00451   } else destvar = resolve(stmt.dest);
00452 
00453   if(tempsrc) {
00454     m_code << "    " << ctype(stmt.src[1].valueType()) 
00455       << " input[" << stmt.src[1].size() << "];" << std::endl;
00456 
00457     for(int i = 0; i < stmt.src[1].size(); i++) {
00458       m_code << "    input[" << i << "] = " 
00459         << resolve(stmt.src[1], i) << ";" << std::endl;
00460     }
00461 
00462     srcvar = "input";
00463   } else srcvar = resolve(stmt.src[1]);
00464 
00465   m_code << "    " << texfunc << "<"
00466      << dims << ", "
00467      << node->size() << ", "
00468      << node->width() << ", "
00469      << node->height() << ", "
00470      << node->depth() <<  ", "
00471      << ctype(node->valueType()) << "," 
00472      << srcWrap << ", "
00473      << destClamp << ">("
00474    << resolve(stmt.src[0])
00475    << ", "
00476    << srcvar
00477    << ", "
00478    << destvar
00479    << ");" << std::endl;
00480   
00481   if(tempdest) {
00482     for(int i = 0; i < stmt.dest.size(); i++) {
00483       m_code << "    "
00484        << resolve(stmt.dest, i)
00485        << " = result[" << i << "];" << std::endl;
00486     }
00487   }
00488   m_code << "  }" << std::endl;
00489 }
00490 
00491 }
00492 

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