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 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 <map>
00025 #include <vector>
00026 #include <sstream>
00027 #include "Cc.hpp" 
00028 #include "ShDebug.hpp" 
00029 #include "ShStream.hpp" 
00030 #include "ShVariant.hpp"
00031 #include "ShOperation.hpp"
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #include "config.h"
00035 #endif
00036 
00037 #ifdef SH_CC_DEBUG
00038 #  define SH_CC_DEBUG_PRINT(x) SH_DEBUG_PRINT(x)
00039 #else
00040 #  define SH_CC_DEBUG_PRINT(x) do { } while(0)
00041 #endif
00042 
00043 namespace ShCc {
00044 
00045 using namespace SH;
00046 
00052 // @todo type use different functions depending on type
00053 // e.g. std::abs for int types, funcf versions of func for float
00054 
00055 // handles linear ops that require up to 4 src arguments (may not be independent) 
00056 struct CcOpCode 
00057 {
00058   ShOperation op;
00059   char *code;
00060 };
00061 
00062 struct CcOpCodeVecs
00063 {
00064   CcOpCodeVecs() {}
00065 
00066   // Dices up the code string into references #i or $i to 
00067   // src variables and the code fragments between references. 
00068   //
00069   // after construction, frag.size() == (index.size() + 1) 
00070   // and index.size() = scalar.size()
00071   CcOpCodeVecs(const CcOpCode &op);
00072 
00073   bool operator<(const CcOpCodeVecs &other) {
00074     return op < other.op;
00075   }
00076 
00077   std::string encode() const;
00078 
00079   ShOperation op;
00080 
00081   std::vector<int> index;
00082   std::vector<bool> scalar;
00083   std::vector<std::string> frag; 
00084 };
00085 
00086 
00087 typedef std::map<SH::ShOperation, CcOpCodeVecs> CcOpCodeMap;
00088 
00089 CcOpCodeVecs::CcOpCodeVecs(const CcOpCode &op) {
00090   std::string code = op.code; 
00091 
00092   unsigned i, j;
00093   i = j = 0;
00094   for(; (j = code.find_first_of("#$", i)) != std::string::npos;) {
00095     frag.push_back(code.substr(i, j - i));
00096     scalar.push_back(code[j] == '$');
00097     i = j + 1;
00098     j = code.find_first_not_of("012345689", i);
00099     index.push_back(atoi(code.substr(i, j - i).c_str()));
00100     i = j;
00101   }
00102   if(i == std::string::npos) {
00103     frag.push_back("");
00104   } else {
00105     frag.push_back(code.substr(i));
00106   }
00107 
00108 }
00109 
00110 std::string CcOpCodeVecs::encode() const 
00111 {
00112   std::ostringstream out;
00113   for(unsigned int i = 0; i < index.size(); ++i) {
00114     out << frag[i];
00115     out << "src[" << index[i] << "]";
00116     if(scalar[i]) out << ".scalar";
00117   }
00118   out << frag.back();
00119   return out.str();
00120 }
00121 
00122 // Table of replacement macros holding C++ code corresponding to SH op 
00123 //
00124 // This table is parsed into a static map in CcBackendCode.  Although
00125 // it may be possible to classify the ops depending on the kinds of C++
00126 // code output, this is a bit trickier than with the gl backend. Since
00127 // the end result is not assmebly, the variety of syntax makes categorizing
00128 // things more difficult, so here's the easy way out. 
00129 //
00130 // Each entry represents the following macro expansion
00131 // for j = 0 to dest.size() 
00132 //    insert code for resolve(dest, j) = code string in table with 
00133 //        #i in rhs replaced by resolve(src[i], j)
00134 //        $i in rhs replaced by resolve(src[i], src[i].size() == 1 ? 0 : j)
00135 //        where i is an non-negative integer
00136 const CcOpCode opCodeTable[] = {
00137   {SH_OP_ASN,   "#0" },
00138   {SH_OP_NEG,   "-#0" },  
00139   {SH_OP_ADD,   "$0 + $1"},
00140   {SH_OP_MUL,   "$0 * $1"},
00141   {SH_OP_DIV,   "$0 / $1"},
00142 
00143   {SH_OP_SLT,   "($0 < $1 ? 1 : 0)"},
00144   {SH_OP_SLE,   "($0 <= $1 ? 1 : 0)"},
00145   {SH_OP_SGT,   "($0 > $1 ? 1 : 0)"},
00146   {SH_OP_SGE,   "($0 >= $1 ? 1 : 0)"},
00147   {SH_OP_SEQ,   "($0 == $1 ? 1 : 0)"},
00148   {SH_OP_SNE,   "($0 != $1 ? 1 : 0)"},
00149 
00150   {SH_OP_ABS,   "fabs(#0)"}, 
00151   {SH_OP_ACOS,  "acos(#0)"},
00152   {SH_OP_ASIN,  "asin(#0)"},
00153   {SH_OP_ATAN,  "atan(#0)"},
00154   {SH_OP_ATAN2, "atan2(#0, #1)"},
00155   {SH_OP_CBRT,  "pow(#0, 1 / 3.0)"},
00156   {SH_OP_CEIL,  "ceil(#0)"},
00157   {SH_OP_COS,   "cos(#0)"},
00158   {SH_OP_COSH,  "cosh(#0)"},
00159   {SH_OP_EXP,   "exp(#0)"},
00160   {SH_OP_EXP2,  "exp2(#0)"},
00161   {SH_OP_EXP10, "exp10(#0)"},
00162   {SH_OP_FLR,   "floor(#0)"},
00163   {SH_OP_FRAC,  "#0 - floor(#0)"},
00164   {SH_OP_LOG,   "log(#0)"},
00165   {SH_OP_LOG2,  "log2(#0)"},
00166   {SH_OP_LOG10, "log10(#0)"},
00167   {SH_OP_LRP,   "$0 * ($1 - $2) + $2"},
00168   {SH_OP_MAD,   "$0 * $1 + $2"},
00169   {SH_OP_MAX,   "($0 > $1 ? $0 : $1)"},
00170   {SH_OP_MIN,   "($0 < $1 ? $0 : $1)"}, 
00171   {SH_OP_MOD,   "($0 - $1 * floor((double)$0 / $1))"},
00172   {SH_OP_POW,   "pow($0, $1)"},
00173   {SH_OP_RCP,   "1 / #0"},
00174   {SH_OP_RND,   "floor(#0 + 0.5)"},
00175   {SH_OP_RSQ,   "1 / sqrt(#0)"},
00176   {SH_OP_SIN,   "sin(#0)"},
00177   {SH_OP_SINH,  "sinh(#0)"},
00178   {SH_OP_SGN,   "(#0 < 0 ? -1 : (#0 > 0 ? 1 : 0))"},
00179   {SH_OP_SQRT,  "sqrt(#0)"},
00180   {SH_OP_TAN,   "tan(#0)"},
00181   {SH_OP_TANH,  "tanh(#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_LIT:
00311       {
00312         m_code << "  {" << std::endl;
00313         
00314         // Clamp to zero the first two arguments
00315         m_code << "    " << resolve(stmt.src[0], 0) << " = (" 
00316                << resolve(stmt.src[0], 0) << " > 0) ? " 
00317                << resolve(stmt.src[0], 0) << " : 0;" << std::endl;
00318         m_code << "    " << resolve(stmt.src[0], 1) << " = (" 
00319                << resolve(stmt.src[0], 1) << " > 0) ? " 
00320                << resolve(stmt.src[0], 1) << " : 0;" << std::endl;
00321 
00322         // Clamp to -128, 128 the third argument
00323         m_code << "    " << resolve(stmt.src[0], 2) << " = (" 
00324                << resolve(stmt.src[0], 2) << " > -128.0) ? " 
00325                << resolve(stmt.src[0], 2) << " : -128.0;" << std::endl;
00326         m_code << "    " << resolve(stmt.src[0], 2) << " = (" 
00327                << resolve(stmt.src[0], 2) << " < 128.0) ? " 
00328                << resolve(stmt.src[0], 2) << " : 128.0;" << std::endl;
00329 
00330         // Result according to OpenGL spec
00331         m_code << "    " << resolve(stmt.dest, 0) << " = 1;" << std::endl;
00332         m_code << "    " << resolve(stmt.dest, 1) << " = "
00333                << resolve(stmt.src[0], 0) << ";" << std::endl;
00334         m_code << "    " << resolve(stmt.dest, 2) << " = (" 
00335                << resolve(stmt.src[0], 0) << " > 0)" << " * pow(" 
00336                << resolve(stmt.src[0], 1) << ", " << resolve(stmt.src[0], 2)
00337                << ");" << std::endl;
00338         m_code << "    " << resolve(stmt.dest, 3) << " = 1;" << std::endl;
00339 
00340         m_code << "  }" << std::endl;
00341         break;
00342       }
00343 
00344     case SH_OP_NORM:
00345       {
00346         m_code << "  {" << std::endl;
00347         m_code << "    float len = 1.0/sqrt(";
00348         for(int i = 0; i < stmt.dest.size(); i++)
00349         {
00350           if (i != 0) m_code << " + ";
00351           m_code << resolve(stmt.src[0], i)
00352            << " * "
00353            << resolve(stmt.src[0], i);
00354         }
00355         m_code << ");" << std::endl;
00356         for(int i = 0; i < stmt.dest.size(); i++)
00357         {
00358           m_code << "    "
00359            << resolve(stmt.dest, i)
00360            << " = len*"
00361            << resolve(stmt.src[0], i)
00362            << ";" << std::endl;
00363         }
00364         m_code << "  }" << std::endl;
00365 
00366         break;
00367       }
00368     case SH_OP_XPD:
00369       {
00370         for(int i = 0; i < stmt.dest.size(); i++)
00371         {
00372           int i0 = (i+1)%3;
00373           int i1 = (i+2)%3;
00374           m_code << "  "
00375            << resolve(stmt.dest, i)
00376            << " = "
00377            << resolve(stmt.src[0], i0)
00378            << " * "
00379            << resolve(stmt.src[1], i1)
00380            << " - "
00381            << resolve(stmt.src[1], i0)
00382            << " * "
00383            << resolve(stmt.src[0], i1)
00384            << ";" << std::endl;
00385         }
00386 
00387         break;
00388       }
00389     case SH_OP_TEX:
00390       emitTexLookup(stmt, "sh_cc_backend_lookup");
00391       break;
00392 
00393     case SH_OP_TEXI:
00394       emitTexLookup(stmt, "sh_cc_backend_lookupi");
00395       break;
00396 
00397     case SH_OP_KIL:
00398       {
00399       // TODO: maintain prior output values 
00400       m_code << "  if (";
00401       for(int i = 0; i < stmt.src[0].size(); i++)
00402       {
00403         if (i != 0) m_code << " || ";
00404         m_code << "("
00405          << resolve(stmt.src[0], i) 
00406          << " > 0)";
00407       }
00408       m_code << ")" << std::endl;
00409       m_code << "    return;" << std::endl;
00410       break;
00411       }
00412     case SH_OP_OPTBRA:
00413       {
00414         SH_DEBUG_ASSERT(false);
00415         break;
00416       }
00417     default:
00418       {
00419       m_code << "  // *** unhandled operation "
00420              << opInfo[stmt.op].name
00421              << " ***" << std::endl;
00422       break;
00423       }
00424   }
00425 }
00426 
00427 void CcBackendCode::emitTexLookup(const ShStatement& stmt, const char* texfunc) {
00428   ShTextureNodePtr node = shref_dynamic_cast<ShTextureNode>(stmt.src[0].node());
00429   int dims = 0; 
00430   switch(node->dims()) {
00431     case SH_TEXTURE_1D: dims = 1; break;   
00432     case SH_TEXTURE_2D: dims = 2; break; 
00433     case SH_TEXTURE_RECT: dims = 2; break;
00434     case SH_TEXTURE_3D: dims = 3; break; 
00435     case SH_TEXTURE_CUBE: 
00436       SH_DEBUG_ERROR("Cube maps not handled"); 
00437     default:
00438       SH_DEBUG_ERROR("Unhandled texture dim");
00439   }
00440 
00441   // names of the functors to use for different texture lookup types 
00442   std::string srcInterp, srcFilter, srcWrap;
00443 
00444   if(node->traits().interpolation() != 0) {
00445       //shError(ShBackendException("cc backend supports only nearest-neighbour texture lookup."));
00446       //SH_DEBUG_WARN("cc backend supports only nearest-neighbour texture lookup.");
00447   }
00448 
00449   if (node->traits().filtering() != ShTextureTraits::SH_FILTER_NONE) {
00450       //shError(ShBackendException("cc backend does not support texture filtering."));
00451       SH_DEBUG_WARN("cc backend does not support texture filtering.");
00452   }
00453 
00454   switch(node->traits().wrapping()) {
00455     case ShTextureTraits::SH_WRAP_CLAMP:
00456     case ShTextureTraits::SH_WRAP_CLAMP_TO_EDGE:
00457       srcWrap = "sh_gcc_backend_wrap_clamp";
00458       break;
00459     case ShTextureTraits::SH_WRAP_REPEAT:
00460       srcWrap = "sh_gcc_backend_wrap_repeat";
00461       break;
00462     default:
00463       shError(ShBackendException("cc backend does not support requested texture wrapping mode."));
00464       break;
00465   }
00466 
00467   m_code << "  {" << std::endl;
00468   std::string destvar; 
00469   std::string srcvar;
00470   bool tempdest = !stmt.dest.swizzle().identity();
00471   bool tempsrc = (!stmt.src[1].swizzle().identity()) || stmt.src[1].neg();
00472 
00473   if(tempdest) {
00474     m_code << "    " << ctype(stmt.dest.valueType()) <<  
00475       " result[" << stmt.dest.size() << "];" << std::endl;
00476     destvar = "result";
00477   } else destvar = resolve(stmt.dest);
00478 
00479   if(tempsrc) {
00480     m_code << "    " << ctype(stmt.src[1].valueType()) 
00481       << " input[" << stmt.src[1].size() << "];" << std::endl;
00482 
00483     for(int i = 0; i < stmt.src[1].size(); i++) {
00484       m_code << "    input[" << i << "] = " 
00485         << resolve(stmt.src[1], i) << ";" << std::endl;
00486     }
00487 
00488     srcvar = "input";
00489   } else srcvar = resolve(stmt.src[1]);
00490 
00491   m_code << "    " << texfunc << "<"
00492      << dims << ", "
00493      << node->size() << ", "
00494      << node->width() << ", "
00495      << node->height() << ", "
00496      << node->depth() <<  ", "
00497      << ctype(node->valueType()) << "," 
00498      << srcWrap << ">("
00499    << resolve(stmt.src[0])
00500    << ", "
00501    << srcvar
00502    << ", "
00503    << destvar
00504    << ");" << std::endl;
00505   
00506   if(tempdest) {
00507     for(int i = 0; i < stmt.dest.size(); i++) {
00508       m_code << "    "
00509        << resolve(stmt.dest, i)
00510        << " = result[" << i << "];" << std::endl;
00511     }
00512   }
00513   m_code << "  }" << std::endl;
00514 }
00515 
00516 }
00517 

Generated on Wed Jun 15 18:12:37 2005 for Sh by  doxygen 1.4.3-20050530